feat(nmNum): 接入敏感性分析两种方法的批量求解

- 按预览顺序遍历单参数和多参数组合,复用同一计算副本串行求解。
- 独立保留每组参数、压力及双对数和半对数曲线,记录计算状态与失败原因。
- 校验采样配置和运行次数溢出,单组失败后继续计算并在内存不足时保留已完成结果。
feature/Sensitivity-20261012
lvjunjie 1 week ago
parent 486e847cd7
commit 4e52a6f4d7

@ -103,6 +103,31 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
bool updateSensitivityRun(const nmDataSensitive& sampling, quint64 runIndex, bool updateSensitivityRun(const nmDataSensitive& sampling, quint64 runIndex,
QString* errorMessage = nullptr); QString* errorMessage = nullptr);
/** @brief 一组采样的临时返回值,参数顺序与采样配置的变量列表一致。 */
struct SensitivityRunResult
{
SensitivityRunResult() : m_nRunIndex(0), m_bSucceeded(false) {}
quint64 m_nRunIndex; ///< 与预览一致的零基运行编号。
QVector<double> m_vecParameters; ///< 本组参数的业务基准单位值。
QVector<QVector<double> > m_vecPressure;
QVector<QVector<double> > m_vecLogLog;
QVector<QVector<double> > m_vecSemiLog;
bool m_bSucceeded;
QString m_sFailureReason;
};
/**
* @brief 从源快照创建一份副本,按两种确定性方法的预览顺序串行求解全部组合。
* @param results 本次批量返回值;进入时清空,每组结果在更新下一组前独立保留。
* @return 全部组合遍历完成返回 true,单组成功与否读取对应 m_bSucceeded。
* 配置、基础网格或内存不足时返回 false;已完成的结果仍保留。
* @note 源任务要求与 createSensitivityRun() 相同。此接口同步执行,调用方
* 负责安排执行线程和在 DataManager 所属线程提前捕获源输入。
*/
bool calculateSensitivityBatch(const nmDataSensitive& sampling,
std::vector<SensitivityRunResult>& results,
QString* errorMessage = nullptr) const;
private: private:
/** @brief 仅复制目标井求解的值快照,不登记或访问实时管理器。 */ /** @brief 仅复制目标井求解的值快照,不登记或访问实时管理器。 */
nmCalculationDllPebiSolverTask(const nmCalculationDllPebiSolverTask& source, nmCalculationDllPebiSolverTask(const nmCalculationDllPebiSolverTask& source,

@ -857,6 +857,71 @@ bool nmCalculationDllPebiSolverTask::updateSensitivityRun(
return true; return true;
} }
bool nmCalculationDllPebiSolverTask::calculateSensitivityBatch(
const nmDataSensitive& sampling, std::vector<SensitivityRunResult>& results,
QString* errorMessage) const
{
results.clear();
if(errorMessage) errorMessage->clear();
try {
// 冻结整次批量的配置,次数、运行顺序和返回参数都使用同一份采样数据。
const nmDataSensitive frozenSampling(sampling);
bool overflow = false;
const quint64 count = frozenSampling.calculateModelCount(&overflow);
const nmDataSensitive::CalculationType type = frozenSampling.getCalculationType();
if(overflow || count == 0 || !frozenSampling.usesBaseUnits() ||
(type != nmDataSensitive::CALC_DETERMINISTIC &&
type != nmDataSensitive::CALC_DETERMINISTIC_MULTIVARIATE)) {
if(errorMessage) *errorMessage = tr("Invalid sensitivity sampling configuration.");
return false;
}
// 只在整次批量开始时复制模型与网格;首组参数由创建接口填入。
QString creationError;
QScopedPointer<nmCalculationDllPebiSolverTask> task(
createSensitivityRun(frozenSampling, 0, &creationError));
if(!task) {
if(errorMessage) *errorMessage = creationError;
return false;
}
for(quint64 runIndex = 0; runIndex < count; ++runIndex) {
SensitivityRunResult result;
result.m_nRunIndex = runIndex;
// 单参数法保持其余参数的模型值,多参数法按预览组合顺序逐组取值。
bool inputReady = frozenSampling.getRunValues(runIndex, result.m_vecParameters);
if(!inputReady) {
result.m_sFailureReason = tr("Invalid sensitivity sampling configuration.");
} else if(runIndex > 0) {
// 本组更新失败仍保留运行编号,下一组继续从冻结基准更新。
inputReady = task->updateSensitivityRun(frozenSampling, runIndex,
&result.m_sFailureReason);
}
if(inputReady) {
task->run();
result.m_bSucceeded = task->wasSuccessful();
if(result.m_bSucceeded) {
result.m_vecPressure = task->getAutoFitResultPressure();
result.m_vecLogLog = task->getAutoFitResultLogLog();
result.m_vecSemiLog = task->getAutoFitResultSemiLog();
} else {
result.m_sFailureReason = task->getFailureReason();
}
}
// Qt 曲线容器按值保留;下一组清空任务结果不会清掉这里的曲线。
// 不预先展开全部组合或预分配总次数,也不合并重复的基准组合。
results.push_back(result);
}
return true;
} catch(const std::bad_alloc&) {
if(errorMessage) *errorMessage = tr("Insufficient memory to process solver results.");
return false;
} catch(const std::length_error&) {
if(errorMessage) *errorMessage = tr("Insufficient memory to process solver results.");
return false;
}
}
void nmCalculationDllPebiSolverTask::run() void nmCalculationDllPebiSolverTask::run()
{ {
m_nSolveTimeMs = -1; m_nSolveTimeMs = -1;

Loading…
Cancel
Save