From a66c024f40112c39e741f167c9016957c0d76cf8 Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Tue, 13 Oct 2026 17:35:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(nmNum):=20=E4=BF=AE=E5=A4=8D=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=8C=83=E5=9B=B4=E5=88=B7=E6=96=B0=E6=97=B6=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 模型值与默认采样范围以最后一次成功求解结果为准,修改参数但未求解时保持原值。 - 求解成功后刷新已打开窗口的模型值、范围及自动采样表,保留采样数量、模式和手动输入值。 - 保存采样范围对应的求解快照标识,支持重新打开窗口和项目时识别范围是否需要更新。 --- Include/nmNum/nmData/nmDataSensitive.h | 4 ++ Include/nmNum/nmSubWxs/nmWxSensitive.h | 3 +- Src/nmNum/nmData/nmDataSensitive.cpp | 17 +++++++ Src/nmNum/nmSubWxs/nmWxSensitive.cpp | 66 +++++++++++++++++++++----- 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/Include/nmNum/nmData/nmDataSensitive.h b/Include/nmNum/nmData/nmDataSensitive.h index dd1b195b..492c8002 100644 --- a/Include/nmNum/nmData/nmDataSensitive.h +++ b/Include/nmNum/nmData/nmDataSensitive.h @@ -151,6 +151,9 @@ public: bool getRunValues(quint64 runIndex, QVector& values) const; bool usesBaseUnits() const; void setUsesBaseUnits(bool enabled); + // 记录采样范围对应的求解快照,成功重新求解后再初始化范围。 + QString getModelSnapshotId() const; + void setModelSnapshotId(const QString& snapshotId); // 后台运行时由任务独占,线程结束后交给分析数据管理并由界面修改可见性。 QSharedPointer getResults() const; void setResults(const QSharedPointer& results); @@ -167,6 +170,7 @@ private: CalculationType m_calcType; quint64 m_totalModelCount; bool m_usesBaseUnits; + QString m_modelSnapshotId; QList m_variables; QSharedPointer m_results; }; diff --git a/Include/nmNum/nmSubWxs/nmWxSensitive.h b/Include/nmNum/nmSubWxs/nmWxSensitive.h index 31722655..ae551f91 100644 --- a/Include/nmNum/nmSubWxs/nmWxSensitive.h +++ b/Include/nmNum/nmSubWxs/nmWxSensitive.h @@ -60,6 +60,7 @@ protected: void onSensitivityFinished(); void onResultsClicked(); void onResultsChanged(); + void onModelResultChanged(); void onApplyResults(); void onAcceptResults(); void onShowAllResults(); @@ -74,7 +75,7 @@ private: void updateValuesForPage(int idx); QIcon createTriangleIcon(); // 创建三角箭头图标 - void loadDataToUI(); // 从数据加载到界面 + void loadDataToUI(bool refreshRangesOnly = false); // 从数据加载到界面 bool saveDataFromUI(); // 校验并从界面保存到数据 void refreshModelValues(); void updateModelCount(); diff --git a/Src/nmNum/nmData/nmDataSensitive.cpp b/Src/nmNum/nmData/nmDataSensitive.cpp index b9b4fdaa..290556a0 100644 --- a/Src/nmNum/nmData/nmDataSensitive.cpp +++ b/Src/nmNum/nmData/nmDataSensitive.cpp @@ -405,6 +405,7 @@ nmDataSensitive& nmDataSensitive::operator=(const nmDataSensitive& other) m_calcType = other.m_calcType; m_totalModelCount = other.m_totalModelCount; m_usesBaseUnits = other.m_usesBaseUnits; + m_modelSnapshotId = other.m_modelSnapshotId; m_variables = other.m_variables; m_results = other.m_results; } @@ -422,6 +423,8 @@ rapidjson::Value nmDataSensitive::ToJsonValue( rapidjson::Value root(rapidjson::kObjectType); // 旧配置中的采样值是显示单位,新配置统一保存业务基准单位。 root.AddMember("UsesBaseUnits", m_usesBaseUnits, allocator); + // 随配置保存范围所属的快照,项目重开后仍能识别尚未刷新的范围。 + root.AddMember("ModelSnapshotId", rapidjson::Value(m_modelSnapshotId.toUtf8().constData(), allocator).Move(), allocator); // 顶部 Calculation type root.AddMember("CalculationType", @@ -448,6 +451,8 @@ rapidjson::Value nmDataSensitive::ToJsonValue( // 从 RapidJSON Value 反序列化数据到 nmDataSensitive void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue) { + // 旧项目没有快照标识,首次打开时按已有求解结果初始化范围。 + m_modelSnapshotId = sensitiveTextFromJson(jsonValue, "ModelSnapshotId"); // 旧项目没有 Results 时保持空结果。 m_results.clear(); if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) { @@ -570,6 +575,18 @@ bool nmDataSensitive::usesBaseUnits() const return m_usesBaseUnits; } +QString nmDataSensitive::getModelSnapshotId() const +{ + // 范围版本独立于敏感性批次结果。 + return m_modelSnapshotId; +} + +void nmDataSensitive::setModelSnapshotId(const QString& snapshotId) +{ + // 仅在同步模型与范围后登记,编辑采样值不改变此标识。 + m_modelSnapshotId = snapshotId; +} + QSharedPointer nmDataSensitive::getResults() const { // 曲线容器按共享引用交给界面,重复打开不复制整批结果。 diff --git a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp index 3c787243..938c92bb 100644 --- a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp +++ b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp @@ -5,6 +5,7 @@ #include "nmDataReservoir.h" #include "nmDataWellBase.h" #include "nmDataPerforation.h" +#include "nmPebiResultSnapshot.h" #include "nmCalculationDllPebiSolverTask.h" #include "iSubWndFitting.h" #include "mGui/mGuiAnal/iAnalRun.h" @@ -613,8 +614,10 @@ nmWxSensitive::nmWxSensitive(QWidget *parent, nmDataAnalyzeManager* manager) m_loading = false; onTreeItemChanged(nullptr, 0); // 已有批次可直接重新打开,结果发布后同步入口与列表。 - if(m_pDataManager) + if(m_pDataManager) { connect(m_pDataManager, SIGNAL(sigSensitiveResultsChanged()), this, SLOT(onResultsChanged())); + connect(m_pDataManager, SIGNAL(sigPebiResultSnapshotChanged()), this, SLOT(onModelResultChanged())); + } onResultsChanged(); } @@ -648,10 +651,16 @@ void nmWxSensitive::closeEvent(QCloseEvent* event) void nmWxSensitive::refreshModelValues() { - // 仅将本期六个参数绑定到主井和储层,旧采样范围按显示单位迁移一次。 + // 六个参数以最后成功求解的快照为准,编辑中的模型只提供属性类型和显示单位。 + const QSharedPointer snapshot = m_pDataManager + ? m_pDataManager->getPebiResultSnapshot() : QSharedPointer(); + const bool resetRanges = !snapshot.isNull() && + snapshot->getSnapshotId() != m_sensitiveData.getModelSnapshotId(); nmDataWellBase* well = m_pDataManager ? m_pDataManager->findWellByCode(m_pDataManager->getPrimaryWellCode()) : nullptr; nmDataReservoir* reservoir = m_pDataManager ? m_pDataManager->getReservoirData() : nullptr; + const nmPebiResultWellSnapshot* solvedWell = !snapshot.isNull() && well + ? snapshot->findWell(well->getWellInstanceId()) : nullptr; const QStringList names = QStringList() << "Skin" << "C" << "Pi" << "k" << "h" << QString(QChar(0x03c6)); for (int n = 0; n < names.size(); ++n) { nmDataAttribute* attribute = nullptr; @@ -677,6 +686,20 @@ void nmWxSensitive::refreshModelValues() index = m_sensitiveData.getVariableCount() - 1; } nmDataSensitive::VariableSampling& variable = m_sensitiveData.getVariable(index); + // 在局部属性上替换数值,不能把未求解的编辑值带入 Model value 或默认范围。 + nmDataAttribute solvedAttribute; + if (attribute && !snapshot.isNull()) { + solvedAttribute = *attribute; + const nmPebiResultReservoirParameters& solvedReservoir = snapshot->getReservoirParameters(); + if (name == "Skin" || name == "C") { + if (!solvedWell || (name == "Skin" && !solvedWell->m_bHasSkin)) attribute = nullptr; + else solvedAttribute.setValue(name == "Skin" ? solvedWell->m_dSkin : solvedWell->m_dWellboreStorage); + } else if (name == "Pi") solvedAttribute.setValue(solvedReservoir.m_dInitialPressure); + else if (name == "k") solvedAttribute.setValue(solvedReservoir.m_dPermeability); + else if (name == "h") solvedAttribute.setValue(solvedReservoir.m_dThickness); + else if (name == QString(QChar(0x03c6))) solvedAttribute.setValue(solvedReservoir.m_dPorosity); + if (attribute) attribute = &solvedAttribute; + } double physicalMin = 0.0, physicalMax = 0.0; if (!attribute || !samplingPhysicalRange(name, physicalMin, physicalMax)) { variable.setEnabled(false); @@ -715,7 +738,8 @@ void nmWxSensitive::refreshModelValues() variable.getModelValue().getMaxValue().isValid() && nearlyEqualSamplingValue(minimum, variable.getModelValue().getMinValue().toDouble()) && nearlyEqualSamplingValue(maximum, variable.getModelValue().getMaxValue().toDouble()))); - if (!migrated || ((name == "Skin" || name == "Pi") && previousDefault)) { + // 新的正式求解结果重新建立范围,采样数量、模式及手动值保持原设置。 + if (resetRanges || !migrated || ((name == "Skin" || name == "Pi") && previousDefault)) { const double model = attribute->getValue().toDouble(); minimum = qMin(model / 2.0, model * 2.0); maximum = qMax(model / 2.0, model * 2.0); @@ -750,12 +774,26 @@ void nmWxSensitive::refreshModelValues() if (!isSupportedSamplingVariable(m_sensitiveData.getVariable(i).getVarName())) m_sensitiveData.getVariable(i).setEnabled(false); m_sensitiveData.setUsesBaseUnits(true); + if (!snapshot.isNull()) m_sensitiveData.setModelSnapshotId(snapshot->getSnapshotId()); } -void nmWxSensitive::loadDataToUI() +void nmWxSensitive::onModelResultChanged() +{ + // 查看井切换也会发送快照通知;只有新求解快照才刷新已打开窗口。 + const QSharedPointer snapshot = m_pDataManager->getPebiResultSnapshot(); + if (snapshot.isNull() || snapshot->getSnapshotId() == m_sensitiveData.getModelSnapshotId()) return; + m_loading = true; + refreshModelValues(); + loadDataToUI(true); + m_loading = false; + updateModelCount(); +} + +void nmWxSensitive::loadDataToUI(bool refreshRangesOnly) { // 加载过程中阻止联动刷新,先恢复设置,再一次生成各参数表格。 - calculationTypeCombo->setCurrentIndex(int(m_sensitiveData.getCalculationType())); + if (!refreshRangesOnly) + calculationTypeCombo->setCurrentIndex(int(m_sensitiveData.getCalculationType())); for (int i = 0; i < m_sensitiveData.getVariableCount(); ++i) { nmDataSensitive::VariableSampling& variable = m_sensitiveData.getVariable(i); const QString name = variable.getVarName(); @@ -769,14 +807,20 @@ void nmWxSensitive::loadDataToUI() QTreeWidgetItem* item = parent->child(child); if (item->text(0) != name) continue; item->setDisabled(!available); - item->setCheckState(0, available && variable.getEnabled() ? Qt::Checked : Qt::Unchecked); + if (!refreshRangesOnly) + item->setCheckState(0, available && variable.getEnabled() ? Qt::Checked : Qt::Unchecked); } } detailStack->widget(idx + 1)->setEnabled(available); - m_modeCombos[idx]->setCurrentIndex(int(variable.getMode())); - m_logChecks[idx]->setChecked(variable.getLog()); - m_numSpins[idx]->setValue(qMax(1, variable.getNumber())); - const QString unit = variable.getModelValue().getUnit(); + // 求解后的刷新只更新模型值和范围,不覆盖窗口中尚未提交的采样设置。 + if (!refreshRangesOnly) { + m_modeCombos[idx]->setCurrentIndex(int(variable.getMode())); + m_logChecks[idx]->setChecked(variable.getLog()); + m_numSpins[idx]->setValue(qMax(1, variable.getNumber())); + } + // 已打开窗口沿用原显示单位,避免手动表格的数值被换成另一种单位解释。 + const QString unit = refreshRangesOnly ? m_unitLabels[idx]->text() : variable.getModelValue().getUnit(); + variable.getModelValue().setUnit(unit); m_unitLabels[idx]->setText(unit); double model = variable.getModelValue().getValue().toDouble(); double minimum = variable.getMinValue().getValue().toDouble(); @@ -795,7 +839,7 @@ void nmWxSensitive::loadDataToUI() .arg(formatSamplingValue(displayMin, 6)).arg(formatSamplingValue(displayMax, 6)).arg(unit); m_minSpins[idx]->setToolTip(rangeHint); m_maxSpins[idx]->setToolTip(rangeHint); - if (variable.getMode() == nmDataSensitive::VariableSampling::MODE_MANUAL) { + if (!refreshRangesOnly && variable.getMode() == nmDataSensitive::VariableSampling::MODE_MANUAL) { QTableWidget* table = m_valueTables[idx]; table->setRowCount(variable.getNumber()); const QVector& values = variable.getValues();