fix(nmNum): 修复参数范围刷新时机

- 模型值与默认采样范围以最后一次成功求解结果为准,修改参数但未求解时保持原值。
- 求解成功后刷新已打开窗口的模型值、范围及自动采样表,保留采样数量、模式和手动输入值。
- 保存采样范围对应的求解快照标识,支持重新打开窗口和项目时识别范围是否需要更新。
feature/Sensitivity-20261012
lvjunjie 1 week ago
parent 6c6ba205ba
commit a66c024f40

@ -151,6 +151,9 @@ public:
bool getRunValues(quint64 runIndex, QVector<double>& values) const; bool getRunValues(quint64 runIndex, QVector<double>& values) const;
bool usesBaseUnits() const; bool usesBaseUnits() const;
void setUsesBaseUnits(bool enabled); void setUsesBaseUnits(bool enabled);
// 记录采样范围对应的求解快照,成功重新求解后再初始化范围。
QString getModelSnapshotId() const;
void setModelSnapshotId(const QString& snapshotId);
// 后台运行时由任务独占,线程结束后交给分析数据管理并由界面修改可见性。 // 后台运行时由任务独占,线程结束后交给分析数据管理并由界面修改可见性。
QSharedPointer<BatchResult> getResults() const; QSharedPointer<BatchResult> getResults() const;
void setResults(const QSharedPointer<BatchResult>& results); void setResults(const QSharedPointer<BatchResult>& results);
@ -167,6 +170,7 @@ private:
CalculationType m_calcType; CalculationType m_calcType;
quint64 m_totalModelCount; quint64 m_totalModelCount;
bool m_usesBaseUnits; bool m_usesBaseUnits;
QString m_modelSnapshotId;
QList<VariableSampling> m_variables; QList<VariableSampling> m_variables;
QSharedPointer<BatchResult> m_results; QSharedPointer<BatchResult> m_results;
}; };

@ -60,6 +60,7 @@ protected:
void onSensitivityFinished(); void onSensitivityFinished();
void onResultsClicked(); void onResultsClicked();
void onResultsChanged(); void onResultsChanged();
void onModelResultChanged();
void onApplyResults(); void onApplyResults();
void onAcceptResults(); void onAcceptResults();
void onShowAllResults(); void onShowAllResults();
@ -74,7 +75,7 @@ private:
void updateValuesForPage(int idx); void updateValuesForPage(int idx);
QIcon createTriangleIcon(); // 创建三角箭头图标 QIcon createTriangleIcon(); // 创建三角箭头图标
void loadDataToUI(); // 从数据加载到界面 void loadDataToUI(bool refreshRangesOnly = false); // 从数据加载到界面
bool saveDataFromUI(); // 校验并从界面保存到数据 bool saveDataFromUI(); // 校验并从界面保存到数据
void refreshModelValues(); void refreshModelValues();
void updateModelCount(); void updateModelCount();

@ -405,6 +405,7 @@ nmDataSensitive& nmDataSensitive::operator=(const nmDataSensitive& other)
m_calcType = other.m_calcType; m_calcType = other.m_calcType;
m_totalModelCount = other.m_totalModelCount; m_totalModelCount = other.m_totalModelCount;
m_usesBaseUnits = other.m_usesBaseUnits; m_usesBaseUnits = other.m_usesBaseUnits;
m_modelSnapshotId = other.m_modelSnapshotId;
m_variables = other.m_variables; m_variables = other.m_variables;
m_results = other.m_results; m_results = other.m_results;
} }
@ -422,6 +423,8 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
rapidjson::Value root(rapidjson::kObjectType); rapidjson::Value root(rapidjson::kObjectType);
// 旧配置中的采样值是显示单位,新配置统一保存业务基准单位。 // 旧配置中的采样值是显示单位,新配置统一保存业务基准单位。
root.AddMember("UsesBaseUnits", m_usesBaseUnits, allocator); root.AddMember("UsesBaseUnits", m_usesBaseUnits, allocator);
// 随配置保存范围所属的快照,项目重开后仍能识别尚未刷新的范围。
root.AddMember("ModelSnapshotId", rapidjson::Value(m_modelSnapshotId.toUtf8().constData(), allocator).Move(), allocator);
// 顶部 Calculation type // 顶部 Calculation type
root.AddMember("CalculationType", root.AddMember("CalculationType",
@ -448,6 +451,8 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
// 从 RapidJSON Value 反序列化数据到 nmDataSensitive // 从 RapidJSON Value 反序列化数据到 nmDataSensitive
void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue) void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
{ {
// 旧项目没有快照标识,首次打开时按已有求解结果初始化范围。
m_modelSnapshotId = sensitiveTextFromJson(jsonValue, "ModelSnapshotId");
// 旧项目没有 Results 时保持空结果。 // 旧项目没有 Results 时保持空结果。
m_results.clear(); m_results.clear();
if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) { if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) {
@ -570,6 +575,18 @@ bool nmDataSensitive::usesBaseUnits() const
return m_usesBaseUnits; return m_usesBaseUnits;
} }
QString nmDataSensitive::getModelSnapshotId() const
{
// 范围版本独立于敏感性批次结果。
return m_modelSnapshotId;
}
void nmDataSensitive::setModelSnapshotId(const QString& snapshotId)
{
// 仅在同步模型与范围后登记,编辑采样值不改变此标识。
m_modelSnapshotId = snapshotId;
}
QSharedPointer<nmDataSensitive::BatchResult> nmDataSensitive::getResults() const QSharedPointer<nmDataSensitive::BatchResult> nmDataSensitive::getResults() const
{ {
// 曲线容器按共享引用交给界面,重复打开不复制整批结果。 // 曲线容器按共享引用交给界面,重复打开不复制整批结果。

@ -5,6 +5,7 @@
#include "nmDataReservoir.h" #include "nmDataReservoir.h"
#include "nmDataWellBase.h" #include "nmDataWellBase.h"
#include "nmDataPerforation.h" #include "nmDataPerforation.h"
#include "nmPebiResultSnapshot.h"
#include "nmCalculationDllPebiSolverTask.h" #include "nmCalculationDllPebiSolverTask.h"
#include "iSubWndFitting.h" #include "iSubWndFitting.h"
#include "mGui/mGuiAnal/iAnalRun.h" #include "mGui/mGuiAnal/iAnalRun.h"
@ -613,8 +614,10 @@ nmWxSensitive::nmWxSensitive(QWidget *parent, nmDataAnalyzeManager* manager)
m_loading = false; m_loading = false;
onTreeItemChanged(nullptr, 0); onTreeItemChanged(nullptr, 0);
// 已有批次可直接重新打开,结果发布后同步入口与列表。 // 已有批次可直接重新打开,结果发布后同步入口与列表。
if(m_pDataManager) if(m_pDataManager) {
connect(m_pDataManager, SIGNAL(sigSensitiveResultsChanged()), this, SLOT(onResultsChanged())); connect(m_pDataManager, SIGNAL(sigSensitiveResultsChanged()), this, SLOT(onResultsChanged()));
connect(m_pDataManager, SIGNAL(sigPebiResultSnapshotChanged()), this, SLOT(onModelResultChanged()));
}
onResultsChanged(); onResultsChanged();
} }
@ -648,10 +651,16 @@ void nmWxSensitive::closeEvent(QCloseEvent* event)
void nmWxSensitive::refreshModelValues() void nmWxSensitive::refreshModelValues()
{ {
// 仅将本期六个参数绑定到主井和储层,旧采样范围按显示单位迁移一次。 // 六个参数以最后成功求解的快照为准,编辑中的模型只提供属性类型和显示单位。
const QSharedPointer<const nmPebiResultSnapshot> snapshot = m_pDataManager
? m_pDataManager->getPebiResultSnapshot() : QSharedPointer<const nmPebiResultSnapshot>();
const bool resetRanges = !snapshot.isNull() &&
snapshot->getSnapshotId() != m_sensitiveData.getModelSnapshotId();
nmDataWellBase* well = m_pDataManager nmDataWellBase* well = m_pDataManager
? m_pDataManager->findWellByCode(m_pDataManager->getPrimaryWellCode()) : nullptr; ? m_pDataManager->findWellByCode(m_pDataManager->getPrimaryWellCode()) : nullptr;
nmDataReservoir* reservoir = m_pDataManager ? m_pDataManager->getReservoirData() : 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)); const QStringList names = QStringList() << "Skin" << "C" << "Pi" << "k" << "h" << QString(QChar(0x03c6));
for (int n = 0; n < names.size(); ++n) { for (int n = 0; n < names.size(); ++n) {
nmDataAttribute* attribute = nullptr; nmDataAttribute* attribute = nullptr;
@ -677,6 +686,20 @@ void nmWxSensitive::refreshModelValues()
index = m_sensitiveData.getVariableCount() - 1; index = m_sensitiveData.getVariableCount() - 1;
} }
nmDataSensitive::VariableSampling& variable = m_sensitiveData.getVariable(index); 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; double physicalMin = 0.0, physicalMax = 0.0;
if (!attribute || !samplingPhysicalRange(name, physicalMin, physicalMax)) { if (!attribute || !samplingPhysicalRange(name, physicalMin, physicalMax)) {
variable.setEnabled(false); variable.setEnabled(false);
@ -715,7 +738,8 @@ void nmWxSensitive::refreshModelValues()
variable.getModelValue().getMaxValue().isValid() && variable.getModelValue().getMaxValue().isValid() &&
nearlyEqualSamplingValue(minimum, variable.getModelValue().getMinValue().toDouble()) && nearlyEqualSamplingValue(minimum, variable.getModelValue().getMinValue().toDouble()) &&
nearlyEqualSamplingValue(maximum, variable.getModelValue().getMaxValue().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(); const double model = attribute->getValue().toDouble();
minimum = qMin(model / 2.0, model * 2.0); minimum = qMin(model / 2.0, model * 2.0);
maximum = qMax(model / 2.0, model * 2.0); maximum = qMax(model / 2.0, model * 2.0);
@ -750,11 +774,25 @@ void nmWxSensitive::refreshModelValues()
if (!isSupportedSamplingVariable(m_sensitiveData.getVariable(i).getVarName())) if (!isSupportedSamplingVariable(m_sensitiveData.getVariable(i).getVarName()))
m_sensitiveData.getVariable(i).setEnabled(false); m_sensitiveData.getVariable(i).setEnabled(false);
m_sensitiveData.setUsesBaseUnits(true); m_sensitiveData.setUsesBaseUnits(true);
if (!snapshot.isNull()) m_sensitiveData.setModelSnapshotId(snapshot->getSnapshotId());
} }
void nmWxSensitive::loadDataToUI() void nmWxSensitive::onModelResultChanged()
{
// 查看井切换也会发送快照通知;只有新求解快照才刷新已打开窗口。
const QSharedPointer<const nmPebiResultSnapshot> 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)
{ {
// 加载过程中阻止联动刷新,先恢复设置,再一次生成各参数表格。 // 加载过程中阻止联动刷新,先恢复设置,再一次生成各参数表格。
if (!refreshRangesOnly)
calculationTypeCombo->setCurrentIndex(int(m_sensitiveData.getCalculationType())); calculationTypeCombo->setCurrentIndex(int(m_sensitiveData.getCalculationType()));
for (int i = 0; i < m_sensitiveData.getVariableCount(); ++i) { for (int i = 0; i < m_sensitiveData.getVariableCount(); ++i) {
nmDataSensitive::VariableSampling& variable = m_sensitiveData.getVariable(i); nmDataSensitive::VariableSampling& variable = m_sensitiveData.getVariable(i);
@ -769,14 +807,20 @@ void nmWxSensitive::loadDataToUI()
QTreeWidgetItem* item = parent->child(child); QTreeWidgetItem* item = parent->child(child);
if (item->text(0) != name) continue; if (item->text(0) != name) continue;
item->setDisabled(!available); item->setDisabled(!available);
if (!refreshRangesOnly)
item->setCheckState(0, available && variable.getEnabled() ? Qt::Checked : Qt::Unchecked); item->setCheckState(0, available && variable.getEnabled() ? Qt::Checked : Qt::Unchecked);
} }
} }
detailStack->widget(idx + 1)->setEnabled(available); detailStack->widget(idx + 1)->setEnabled(available);
// 求解后的刷新只更新模型值和范围,不覆盖窗口中尚未提交的采样设置。
if (!refreshRangesOnly) {
m_modeCombos[idx]->setCurrentIndex(int(variable.getMode())); m_modeCombos[idx]->setCurrentIndex(int(variable.getMode()));
m_logChecks[idx]->setChecked(variable.getLog()); m_logChecks[idx]->setChecked(variable.getLog());
m_numSpins[idx]->setValue(qMax(1, variable.getNumber())); m_numSpins[idx]->setValue(qMax(1, variable.getNumber()));
const QString unit = variable.getModelValue().getUnit(); }
// 已打开窗口沿用原显示单位,避免手动表格的数值被换成另一种单位解释。
const QString unit = refreshRangesOnly ? m_unitLabels[idx]->text() : variable.getModelValue().getUnit();
variable.getModelValue().setUnit(unit);
m_unitLabels[idx]->setText(unit); m_unitLabels[idx]->setText(unit);
double model = variable.getModelValue().getValue().toDouble(); double model = variable.getModelValue().getValue().toDouble();
double minimum = variable.getMinValue().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); .arg(formatSamplingValue(displayMin, 6)).arg(formatSamplingValue(displayMax, 6)).arg(unit);
m_minSpins[idx]->setToolTip(rangeHint); m_minSpins[idx]->setToolTip(rangeHint);
m_maxSpins[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]; QTableWidget* table = m_valueTables[idx];
table->setRowCount(variable.getNumber()); table->setRowCount(variable.getNumber());
const QVector<double>& values = variable.getValues(); const QVector<double>& values = variable.getValues();

Loading…
Cancel
Save