feat(nmNum): 完善分析成果保存与参数汇总

- 保存恢复计算方法、采样配置、拟合设置和分析结果。
- 保留筛选阈值及直方图设置,汇总拟合参数范围。
- 修正中文参数保存的 UTF-8 编码。
feature/Sensitivity-20261012
lvjunjie 4 days ago
parent 7acdfa06e4
commit 3d0f343219

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -145,6 +145,19 @@
<ParaItem Name="Sensitivity_h_Higher" Alias="储层厚度 - 上限" Unit="m" Default="10" Max="10000" Min="1e-05" Digit="6" Desc="filtered h higher bound" /> <ParaItem Name="Sensitivity_h_Higher" Alias="储层厚度 - 上限" Unit="m" Default="10" Max="10000" Min="1e-05" Digit="6" Desc="filtered h higher bound" />
<ParaItem Name="Sensitivity_phi_Lower" Alias="孔隙度 - 下限" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi lower bound" /> <ParaItem Name="Sensitivity_phi_Lower" Alias="孔隙度 - 下限" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi lower bound" />
<ParaItem Name="Sensitivity_phi_Higher" Alias="孔隙度 - 上限" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi higher bound" /> <ParaItem Name="Sensitivity_phi_Higher" Alias="孔隙度 - 上限" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi higher bound" />
<!-- 第四种:按同一误差阈值筛选各组拟合最优值。 -->
<ParaItem Name="Sensitivity_Fit_K_Minimum" Alias="渗透率 - 最小值" Unit="mD" Default="25" Max="10000" Min="0" Digit="6" Desc="筛选后拟合参数渗透率最小值" />
<ParaItem Name="Sensitivity_Fit_K_Maximum" Alias="渗透率 - 最大值" Unit="mD" Default="25" Max="10000" Min="0" Digit="6" Desc="筛选后拟合参数渗透率最大值" />
<ParaItem Name="Sensitivity_Fit_Skin_Minimum" Alias="表皮系数 - 最小值" Unit="" Default="0" Max="1000" Min="-100" Digit="6" Desc="筛选后拟合参数表皮系数最小值" />
<ParaItem Name="Sensitivity_Fit_Skin_Maximum" Alias="表皮系数 - 最大值" Unit="" Default="0" Max="1000" Min="-100" Digit="6" Desc="筛选后拟合参数表皮系数最大值" />
<ParaItem Name="Sensitivity_Fit_C_Minimum" Alias="井筒储集系数 - 最小值" Unit="m^3/MPa" Default="0" Max="1000" Min="0" Digit="6" Desc="筛选后拟合参数井筒储集系数最小值" />
<ParaItem Name="Sensitivity_Fit_C_Maximum" Alias="井筒储集系数 - 最大值" Unit="m^3/MPa" Default="0" Max="1000" Min="0" Digit="6" Desc="筛选后拟合参数井筒储集系数最大值" />
<ParaItem Name="Sensitivity_Fit_phi_Minimum" Alias="孔隙度 - 最小值" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="筛选后拟合参数孔隙度最小值" />
<ParaItem Name="Sensitivity_Fit_phi_Maximum" Alias="孔隙度 - 最大值" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="筛选后拟合参数孔隙度最大值" />
<ParaItem Name="Sensitivity_Fit_Dfc_Minimum" Alias="裂缝导流能力 - 最小值" Unit="mD.m" Default="1000" Max="1e+30" Min="0" Digit="6" Desc="筛选后拟合参数裂缝导流能力最小值" />
<ParaItem Name="Sensitivity_Fit_Dfc_Maximum" Alias="裂缝导流能力 - 最大值" Unit="mD.m" Default="1000" Max="1e+30" Min="0" Digit="6" Desc="筛选后拟合参数裂缝导流能力最大值" />
<ParaItem Name="Sensitivity_Fit_FractureHalfLength_Minimum" Alias="裂缝半长 - 最小值" Unit="m" Default="50" Max="10000" Min="1e-05" Digit="6" Desc="筛选后拟合参数裂缝半长最小值" />
<ParaItem Name="Sensitivity_Fit_FractureHalfLength_Maximum" Alias="裂缝半长 - 最大值" Unit="m" Default="50" Max="10000" Min="1e-05" Digit="6" Desc="筛选后拟合参数裂缝半长最大值" />
</ParaGroup> </ParaGroup>
</Root> </Root>

@ -146,6 +146,19 @@
<ParaItem Name="Sensitivity_h_Higher" Alias="h - Higher" Unit="m" Default="10" Max="10000" Min="1e-05" Digit="6" Desc="filtered h higher bound" /> <ParaItem Name="Sensitivity_h_Higher" Alias="h - Higher" Unit="m" Default="10" Max="10000" Min="1e-05" Digit="6" Desc="filtered h higher bound" />
<ParaItem Name="Sensitivity_phi_Lower" Alias="φ - Lower" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi lower bound" /> <ParaItem Name="Sensitivity_phi_Lower" Alias="φ - Lower" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi lower bound" />
<ParaItem Name="Sensitivity_phi_Higher" Alias="φ - Higher" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi higher bound" /> <ParaItem Name="Sensitivity_phi_Higher" Alias="φ - Higher" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="filtered phi higher bound" />
<!-- 第四种:按同一误差阈值筛选各组拟合最优值。 -->
<ParaItem Name="Sensitivity_Fit_K_Minimum" Alias="Permeability - Minimum" Unit="mD" Default="25" Max="10000" Min="0" Digit="6" Desc="筛选后拟合参数渗透率最小值" />
<ParaItem Name="Sensitivity_Fit_K_Maximum" Alias="Permeability - Maximum" Unit="mD" Default="25" Max="10000" Min="0" Digit="6" Desc="筛选后拟合参数渗透率最大值" />
<ParaItem Name="Sensitivity_Fit_Skin_Minimum" Alias="Skin - Minimum" Unit="" Default="0" Max="1000" Min="-100" Digit="6" Desc="筛选后拟合参数表皮系数最小值" />
<ParaItem Name="Sensitivity_Fit_Skin_Maximum" Alias="Skin - Maximum" Unit="" Default="0" Max="1000" Min="-100" Digit="6" Desc="筛选后拟合参数表皮系数最大值" />
<ParaItem Name="Sensitivity_Fit_C_Minimum" Alias="Wellbore storage - Minimum" Unit="m^3/MPa" Default="0" Max="1000" Min="0" Digit="6" Desc="筛选后拟合参数井筒储集系数最小值" />
<ParaItem Name="Sensitivity_Fit_C_Maximum" Alias="Wellbore storage - Maximum" Unit="m^3/MPa" Default="0" Max="1000" Min="0" Digit="6" Desc="筛选后拟合参数井筒储集系数最大值" />
<ParaItem Name="Sensitivity_Fit_phi_Minimum" Alias="Porosity - Minimum" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="筛选后拟合参数孔隙度最小值" />
<ParaItem Name="Sensitivity_Fit_phi_Maximum" Alias="Porosity - Maximum" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="筛选后拟合参数孔隙度最大值" />
<ParaItem Name="Sensitivity_Fit_Dfc_Minimum" Alias="Fracture conductivity - Minimum" Unit="mD.m" Default="1000" Max="1e+30" Min="0" Digit="6" Desc="筛选后拟合参数裂缝导流能力最小值" />
<ParaItem Name="Sensitivity_Fit_Dfc_Maximum" Alias="Fracture conductivity - Maximum" Unit="mD.m" Default="1000" Max="1e+30" Min="0" Digit="6" Desc="筛选后拟合参数裂缝导流能力最大值" />
<ParaItem Name="Sensitivity_Fit_FractureHalfLength_Minimum" Alias="Fracture half length - Minimum" Unit="m" Default="50" Max="10000" Min="1e-05" Digit="6" Desc="筛选后拟合参数裂缝半长最小值" />
<ParaItem Name="Sensitivity_Fit_FractureHalfLength_Maximum" Alias="Fracture half length - Maximum" Unit="m" Default="50" Max="10000" Min="1e-05" Digit="6" Desc="筛选后拟合参数裂缝半长最大值" />
</ParaGroup> </ParaGroup>
</Root> </Root>

@ -11,6 +11,7 @@
#include "nmData_global.h" #include "nmData_global.h"
#include "nmDataBase.h" #include "nmDataBase.h"
#include "nmDataAttribute.h" #include "nmDataAttribute.h"
#include "nmDataAutomaticFitting.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
class NM_DATA_EXPORT nmDataSensitive : public nmDataBase class NM_DATA_EXPORT nmDataSensitive : public nmDataBase
@ -51,7 +52,7 @@ public:
nmDataAttribute m_maxValue; // Maximum nmDataAttribute m_maxValue; // Maximum
// 表格中的数值列表 // 表格中的数值列表
QVector<double> m_values; QVector<double> m_values;
// Monte-Carlo 分布设置与确定性取值独立,仅保存在内存中。 // Monte-Carlo 分布设置与确定性取值独立,随分析配置保存。
enum Distribution { NORMAL, LOGNORMAL, TRUNCATED_NORMAL, TRUNCATED_LOGNORMAL, UNIFORM, TRIANGULAR }; enum Distribution { NORMAL, LOGNORMAL, TRUNCATED_NORMAL, TRUNCATED_LOGNORMAL, UNIFORM, TRIANGULAR };
Distribution m_distribution; Distribution m_distribution;
bool m_distributionInitialized; bool m_distributionInitialized;
@ -106,7 +107,7 @@ public:
RunResult() : m_nRunIndex(0), m_dPressureRmse(-1.0), m_bSucceeded(false), m_bCancelled(false), m_bVisible(true) {} RunResult() : m_nRunIndex(0), m_dPressureRmse(-1.0), m_bSucceeded(false), m_bCancelled(false), m_bVisible(true) {}
quint64 m_nRunIndex; quint64 m_nRunIndex;
QVector<double> m_vecParameters; QVector<double> m_vecParameters;
QVector<double> m_vecFittedParameters; // 本组 LM 最优值,与批次拟合参数顺序一致,仅存内存。 QVector<double> m_vecFittedParameters; // 本组 LM 最优值,与批次拟合参数顺序一致。
QVector<QVector<double> > m_vecPressure; QVector<QVector<double> > m_vecPressure;
QVector<QVector<double> > m_vecLogLog; QVector<QVector<double> > m_vecLogLog;
QVector<QVector<double> > m_vecSemiLog; QVector<QVector<double> > m_vecSemiLog;
@ -119,10 +120,11 @@ public:
struct NM_DATA_EXPORT BatchResult struct NM_DATA_EXPORT BatchResult
{ {
BatchResult() : m_calcType(CALC_DETERMINISTIC), m_dFilterThreshold(-1.0), m_nHistogramVariable(-1), m_bCompleted(false), m_bStopped(false) {} BatchResult() : m_calcType(CALC_DETERMINISTIC), m_dFilterThreshold(-1.0), m_nHistogramVariable(-1), m_nHistogramBins(20), m_bCompleted(false), m_bStopped(false) {}
CalculationType m_calcType; CalculationType m_calcType;
QList<VariableSampling> m_variables; // 求解时冻结的配置,独立于之后的参数编辑。 QList<VariableSampling> m_variables; // 求解时冻结的配置,独立于之后的参数编辑。
QList<nmDataAttribute> m_fitParameters; // 名称、基准单位和本批分析开始时的模型值。 QList<nmDataAttribute> m_fitParameters; // 名称、基准单位和本批分析开始时的模型值。
QStringList m_fitParameterCodes; // 对应 XML 的参数编码,与拟合参数同序。
QString m_sWellCode; QString m_sWellCode;
QString m_sWellName; QString m_sWellName;
QString m_sAxisXName; QString m_sAxisXName;
@ -132,11 +134,12 @@ public:
QVector<QVector<double> > m_vecHistoryLogLog; QVector<QVector<double> > m_vecHistoryLogLog;
QVector<QVector<double> > m_vecBaseLogLog; // 批次启动时原模型已有的数值解。 QVector<QVector<double> > m_vecBaseLogLog; // 批次启动时原模型已有的数值解。
std::vector<RunResult> m_runs; std::vector<RunResult> m_runs;
// 散点筛选阈值只随当前批次保存在内存中,负值表示尚未筛选。 // 散点筛选阈值随当前批次保存,负值表示尚未筛选。
double m_dFilterThreshold; double m_dFilterThreshold;
int m_nHistogramVariable; // -1 为误差,其余为拟合参数下标,仅存内存。 int m_nHistogramVariable; // -1 为误差,其余为拟合参数下标。
int m_nHistogramBins; // 直方图分箱数,与统计变量一起恢复。
bool statisticValue(const RunResult& run, int variable, double& value) const; bool statisticValue(const RunResult& run, int variable, double& value) const;
bool filteredParameterBounds(int index, double& minimum, double& maximum) const; bool filteredParameterBounds(int index, double& minimum, double& maximum, bool fitted = false) const;
bool m_bCompleted; bool m_bCompleted;
bool m_bStopped; bool m_bStopped;
QString m_sFailureReason; QString m_sFailureReason;
@ -157,6 +160,11 @@ public:
// === Calculation type === // === Calculation type ===
CalculationType getCalculationType() const; CalculationType getCalculationType() const;
void setCalculationType(CalculationType type); void setCalculationType(CalculationType type);
// 第四种仍复用 Monte-Carlo 采样,独立保存改善开关与确认的拟合设置。
bool isImproveEnabled() const;
void setImproveEnabled(bool enabled);
QSharedPointer<const nmSensitivityFitSettings> getImproveSettings() const;
void setImproveSettings(const QSharedPointer<const nmSensitivityFitSettings>& settings);
// === 总模型数 === // === 总模型数 ===
quint64 getTotalModelCount() const; quint64 getTotalModelCount() const;
@ -186,6 +194,8 @@ public:
private: private:
CalculationType m_calcType; CalculationType m_calcType;
bool m_improveEnabled;
QSharedPointer<const nmSensitivityFitSettings> m_improveSettings;
quint64 m_totalModelCount; quint64 m_totalModelCount;
bool m_usesBaseUnits; bool m_usesBaseUnits;
QList<VariableSampling> m_variables; QList<VariableSampling> m_variables;

@ -27,7 +27,6 @@ class SensitivityResultsModel;
class iGuiPlot; class iGuiPlot;
class QMenu; class QMenu;
class QAction; class QAction;
struct nmSensitivityFitSettings;
class NM_SUB_WXS_EXPORT nmWxSensitivityHistogramOptions : public iDlgBase class NM_SUB_WXS_EXPORT nmWxSensitivityHistogramOptions : public iDlgBase
{ {
@ -153,7 +152,6 @@ private:
// UI 控件 // UI 控件
QComboBox *calculationTypeCombo; QComboBox *calculationTypeCombo;
QPushButton *m_improveSettingsButton; QPushButton *m_improveSettingsButton;
QSharedPointer<nmSensitivityFitSettings> m_improveSettings;
QTreeWidget *variablesTree; QTreeWidget *variablesTree;
QStackedWidget *detailStack; QStackedWidget *detailStack;
QLabel *modelCountLabel; QLabel *modelCountLabel;

@ -996,6 +996,7 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
const char* names[] = { "Permeability", "Skin", "Wellbore storage", "Porosity", const char* names[] = { "Permeability", "Skin", "Wellbore storage", "Porosity",
"Fracture conductivity", "Fracture half length" }; "Fracture conductivity", "Fracture half length" };
const char* units[] = { "mD", "", "m^3/MPa", "", "mD.m", "m" }; const char* units[] = { "mD", "", "m^3/MPa", "", "mD.m", "m" };
const char* codes[] = { "K", "Skin", "C", "phi", "Dfc", "FractureHalfLength" };
nmDataWellBase* well = m_pDataManager->findWellByName(improveSettings->targetWell); nmDataWellBase* well = m_pDataManager->findWellByName(improveSettings->targetWell);
if(well) { if(well) {
// 启动前补齐拟合目标井的历史双对数,后台各组共用这份目标数据。 // 启动前补齐拟合目标井的历史双对数,后台各组共用这份目标数据。
@ -1021,6 +1022,7 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
for(int j = 0; j < 6; ++j) if(selected[j]) { for(int j = 0; j < 6; ++j) if(selected[j]) {
task->m_pSensitivityResult->m_fitParameters.append(nmDataAttribute( task->m_pSensitivityResult->m_fitParameters.append(nmDataAttribute(
QCoreApplication::translate("nmWxAutomaticFitting", names[j]), values[j], units[j])); QCoreApplication::translate("nmWxAutomaticFitting", names[j]), values[j], units[j]));
task->m_pSensitivityResult->m_fitParameterCodes.append(codes[j]);
} }
} }
} }

@ -54,7 +54,7 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
rapidjson::Value attributeValue(rapidjson::kObjectType); rapidjson::Value attributeValue(rapidjson::kObjectType);
// 添加属性名称,QString转UTF8并移入JSON // 添加属性名称,QString转UTF8并移入JSON
attributeValue.AddMember("Name", rapidjson::Value(m_name.toStdString().c_str(), allocator).Move(), allocator); attributeValue.AddMember("Name", rapidjson::Value(m_name.toUtf8().constData(), allocator).Move(), allocator);
// 根据QVariant类型添加“值” // 根据QVariant类型添加“值”
if (m_value.type() == QVariant::Double) { if (m_value.type() == QVariant::Double) {
@ -62,11 +62,11 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
} else if (m_value.type() == QVariant::Int) { } else if (m_value.type() == QVariant::Int) {
attributeValue.AddMember("Value", m_value.toInt(), allocator); attributeValue.AddMember("Value", m_value.toInt(), allocator);
} else { // 字符串及其他类型转UTF8字符串 } else { // 字符串及其他类型转UTF8字符串
attributeValue.AddMember("Value", rapidjson::Value(m_value.toString().toStdString().c_str(), allocator).Move(), allocator); attributeValue.AddMember("Value", rapidjson::Value(m_value.toString().toUtf8().constData(), allocator).Move(), allocator);
} }
// 添加单位,QString转UTF8并移入JSON // 添加单位,QString转UTF8并移入JSON
attributeValue.AddMember("Unit", rapidjson::Value(m_unit.toStdString().c_str(), allocator).Move(), allocator); attributeValue.AddMember("Unit", rapidjson::Value(m_unit.toUtf8().constData(), allocator).Move(), allocator);
// 如果最小值有效,添加“最小值” // 如果最小值有效,添加“最小值”
if (m_minValue.isValid()) { if (m_minValue.isValid()) {
@ -75,7 +75,7 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
} else if (m_minValue.type() == QVariant::Int) { } else if (m_minValue.type() == QVariant::Int) {
attributeValue.AddMember("MinValue", m_minValue.toInt(), allocator); attributeValue.AddMember("MinValue", m_minValue.toInt(), allocator);
} else { // 字符串及其他类型转UTF8字符串 } else { // 字符串及其他类型转UTF8字符串
attributeValue.AddMember("MinValue", rapidjson::Value(m_minValue.toString().toStdString().c_str(), allocator).Move(), allocator); attributeValue.AddMember("MinValue", rapidjson::Value(m_minValue.toString().toUtf8().constData(), allocator).Move(), allocator);
} }
} }
@ -86,7 +86,7 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
} else if (m_maxValue.type() == QVariant::Int) { } else if (m_maxValue.type() == QVariant::Int) {
attributeValue.AddMember("MaxValue", m_maxValue.toInt(), allocator); attributeValue.AddMember("MaxValue", m_maxValue.toInt(), allocator);
} else { // 字符串及其他类型转UTF8字符串 } else { // 字符串及其他类型转UTF8字符串
attributeValue.AddMember("MaxValue", rapidjson::Value(m_maxValue.toString().toStdString().c_str(), allocator).Move(), allocator); attributeValue.AddMember("MaxValue", rapidjson::Value(m_maxValue.toString().toUtf8().constData(), allocator).Move(), allocator);
} }
} }
@ -94,7 +94,7 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
if (!m_listValueSelections.isEmpty()) { if (!m_listValueSelections.isEmpty()) {
rapidjson::Value selectionsArray(rapidjson::kArrayType); // 创建JSON数组 rapidjson::Value selectionsArray(rapidjson::kArrayType); // 创建JSON数组
foreach (const QString& selection , m_listValueSelections) { // 遍历列表 foreach (const QString& selection , m_listValueSelections) { // 遍历列表
selectionsArray.PushBack(rapidjson::Value(selection.toStdString().c_str(), allocator).Move(), allocator); selectionsArray.PushBack(rapidjson::Value(selection.toUtf8().constData(), allocator).Move(), allocator);
} }
attributeValue.AddMember("ValueSelections", selectionsArray, allocator); // 将数组添加到JSON对象 attributeValue.AddMember("ValueSelections", selectionsArray, allocator); // 将数组添加到JSON对象
} }
@ -103,7 +103,7 @@ rapidjson::Value nmDataAttribute::ToJsonValue(rapidjson::Document::AllocatorType
if (!m_listUnitSelections.isEmpty()) { if (!m_listUnitSelections.isEmpty()) {
rapidjson::Value unitSelectionsArray(rapidjson::kArrayType); // 创建JSON数组 rapidjson::Value unitSelectionsArray(rapidjson::kArrayType); // 创建JSON数组
foreach (const QString& unitSelection , m_listUnitSelections) { // 遍历列表 foreach (const QString& unitSelection , m_listUnitSelections) { // 遍历列表
unitSelectionsArray.PushBack(rapidjson::Value(unitSelection.toStdString().c_str(), allocator).Move(), allocator); unitSelectionsArray.PushBack(rapidjson::Value(unitSelection.toUtf8().constData(), allocator).Move(), allocator);
} }
attributeValue.AddMember("UnitSelections", unitSelectionsArray, allocator); // 将数组添加到JSON对象 attributeValue.AddMember("UnitSelections", unitSelectionsArray, allocator); // 将数组添加到JSON对象
} }

@ -18,16 +18,17 @@ bool nmDataSensitive::BatchResult::statisticValue(const RunResult& run, int vari
return qIsFinite(value); return qIsFinite(value);
} }
bool nmDataSensitive::BatchResult::filteredParameterBounds(int index, double& minimum, double& maximum) const bool nmDataSensitive::BatchResult::filteredParameterBounds(int index, double& minimum, double& maximum, bool fitted) const
{ {
// 所有参数共用同一批误差严格小于阈值的样本,不受曲线可见性影响。 // 采样和拟合参数共用误差严格小于阈值的运行,不受曲线可见性影响。
bool found = false; bool found = false;
for(size_t i = 0; i < m_runs.size(); ++i) { for(size_t i = 0; i < m_runs.size(); ++i) {
const RunResult& run = m_runs[i]; const RunResult& run = m_runs[i];
const QVector<double>& parameters = fitted ? run.m_vecFittedParameters : run.m_vecParameters;
if(!run.m_bSucceeded || run.m_bCancelled || !qIsFinite(run.m_dPressureRmse) || if(!run.m_bSucceeded || run.m_bCancelled || !qIsFinite(run.m_dPressureRmse) ||
run.m_dPressureRmse < 0.0 || !(run.m_dPressureRmse < m_dFilterThreshold) || run.m_dPressureRmse < 0.0 || !(run.m_dPressureRmse < m_dFilterThreshold) ||
index < 0 || index >= run.m_vecParameters.size()) continue; index < 0 || index >= parameters.size()) continue;
const double value = run.m_vecParameters[index]; const double value = parameters[index];
if(!qIsFinite(value)) continue; if(!qIsFinite(value)) continue;
minimum = found ? qMin(minimum, value) : value; minimum = found ? qMin(minimum, value) : value;
maximum = found ? qMax(maximum, value) : value; maximum = found ? qMax(maximum, value) : value;
@ -121,6 +122,13 @@ QString sensitiveTextFromJson(const rapidjson::Value& value, const char* key)
return value.HasMember(key) && value[key].IsString() return value.HasMember(key) && value[key].IsString()
? QString::fromUtf8(value[key].GetString()) : QString(); ? QString::fromUtf8(value[key].GetString()) : QString();
} }
double sensitiveNumberFromJson(const rapidjson::Value& value, const char* key, double fallback)
{
// 新增数值字段缺失或无效时沿用默认值,兼容之前保存的成果。
return value.HasMember(key) && value[key].IsNumber() && qIsFinite(value[key].GetDouble())
? value[key].GetDouble() : fallback;
}
} }
rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue( rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue(
@ -140,6 +148,18 @@ rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue(
value.AddMember("FailureReason", rapidjson::Value(m_sFailureReason.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("FailureReason", rapidjson::Value(m_sFailureReason.toUtf8().constData(), allocator).Move(), allocator);
value.AddMember("HistoryLogLog", sensitiveCurvesToJson(m_vecHistoryLogLog, allocator), allocator); value.AddMember("HistoryLogLog", sensitiveCurvesToJson(m_vecHistoryLogLog, allocator), allocator);
value.AddMember("BaseLogLog", sensitiveCurvesToJson(m_vecBaseLogLog, allocator), allocator); value.AddMember("BaseLogLog", sensitiveCurvesToJson(m_vecBaseLogLog, allocator), allocator);
// 统计图和结果参数页直接使用已保存的误差、拟合值及筛选设置。
value.AddMember("FilterThreshold", qIsFinite(m_dFilterThreshold) ? m_dFilterThreshold : -1.0, allocator);
value.AddMember("HistogramVariable", m_nHistogramVariable, allocator);
value.AddMember("HistogramBins", m_nHistogramBins, allocator);
rapidjson::Value fitting(rapidjson::kArrayType);
for(int i = 0; i < m_fitParameters.size(); ++i) {
rapidjson::Value parameter(rapidjson::kObjectType);
parameter.AddMember("Attribute", m_fitParameters[i].ToJsonValue(allocator), allocator);
parameter.AddMember("Code", rapidjson::Value(m_fitParameterCodes.value(i).toUtf8().constData(), allocator).Move(), allocator);
fitting.PushBack(parameter, allocator);
}
value.AddMember("FitParameters", fitting, allocator);
rapidjson::Value variables(rapidjson::kArrayType); rapidjson::Value variables(rapidjson::kArrayType);
for(int i = 0; i < m_variables.size(); ++i) for(int i = 0; i < m_variables.size(); ++i)
variables.PushBack(m_variables[i].ToJsonValue(allocator), allocator); variables.PushBack(m_variables[i].ToJsonValue(allocator), allocator);
@ -150,6 +170,8 @@ rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue(
rapidjson::Value item(rapidjson::kObjectType); rapidjson::Value item(rapidjson::kObjectType);
item.AddMember("RunIndex", run.m_nRunIndex, allocator); item.AddMember("RunIndex", run.m_nRunIndex, allocator);
item.AddMember("Parameters", sensitiveNumbersToJson(run.m_vecParameters, allocator), allocator); item.AddMember("Parameters", sensitiveNumbersToJson(run.m_vecParameters, allocator), allocator);
item.AddMember("FittedParameters", sensitiveNumbersToJson(run.m_vecFittedParameters, allocator), allocator);
item.AddMember("PressureRmse", qIsFinite(run.m_dPressureRmse) ? run.m_dPressureRmse : -1.0, allocator);
item.AddMember("Pressure", sensitiveCurvesToJson(run.m_vecPressure, allocator), allocator); item.AddMember("Pressure", sensitiveCurvesToJson(run.m_vecPressure, allocator), allocator);
item.AddMember("LogLog", sensitiveCurvesToJson(run.m_vecLogLog, allocator), allocator); item.AddMember("LogLog", sensitiveCurvesToJson(run.m_vecLogLog, allocator), allocator);
item.AddMember("SemiLog", sensitiveCurvesToJson(run.m_vecSemiLog, allocator), allocator); item.AddMember("SemiLog", sensitiveCurvesToJson(run.m_vecSemiLog, allocator), allocator);
@ -169,8 +191,8 @@ void nmDataSensitive::BatchResult::FromJsonValue(const rapidjson::Value& value)
*this = BatchResult(); *this = BatchResult();
if(!value.IsObject()) return; if(!value.IsObject()) return;
if(value.HasMember("CalculationType") && value["CalculationType"].IsInt() && if(value.HasMember("CalculationType") && value["CalculationType"].IsInt() &&
value["CalculationType"].GetInt() == CALC_DETERMINISTIC_MULTIVARIATE) value["CalculationType"].GetInt() >= CALC_DETERMINISTIC && value["CalculationType"].GetInt() <= CALC_MONTE_CARLO)
m_calcType = CALC_DETERMINISTIC_MULTIVARIATE; m_calcType = static_cast<CalculationType>(value["CalculationType"].GetInt());
m_sWellCode = sensitiveTextFromJson(value, "WellCode"); m_sWellCode = sensitiveTextFromJson(value, "WellCode");
m_sWellName = sensitiveTextFromJson(value, "WellName"); m_sWellName = sensitiveTextFromJson(value, "WellName");
m_sAxisXName = sensitiveTextFromJson(value, "AxisXName"); m_sAxisXName = sensitiveTextFromJson(value, "AxisXName");
@ -182,6 +204,28 @@ void nmDataSensitive::BatchResult::FromJsonValue(const rapidjson::Value& value)
m_bStopped = value.HasMember("Stopped") && value["Stopped"].IsBool() && value["Stopped"].GetBool(); m_bStopped = value.HasMember("Stopped") && value["Stopped"].IsBool() && value["Stopped"].GetBool();
m_vecHistoryLogLog = sensitiveCurvesFromJson(value, "HistoryLogLog"); m_vecHistoryLogLog = sensitiveCurvesFromJson(value, "HistoryLogLog");
m_vecBaseLogLog = sensitiveCurvesFromJson(value, "BaseLogLog"); m_vecBaseLogLog = sensitiveCurvesFromJson(value, "BaseLogLog");
m_dFilterThreshold = sensitiveNumberFromJson(value, "FilterThreshold", -1.0);
if(value.HasMember("FitParameters") && value["FitParameters"].IsArray()) {
const rapidjson::Value& fitting = value["FitParameters"];
for(rapidjson::SizeType i = 0; i < fitting.Size(); ++i) {
// 保留参数位置,确保每组拟合值与名称、单位和 XML 编码一一对应。
nmDataAttribute parameter;
QString code;
if(fitting[i].IsObject()) {
if(fitting[i].HasMember("Attribute") && fitting[i]["Attribute"].IsObject())
parameter.FromJsonValue(fitting[i]["Attribute"]);
code = sensitiveTextFromJson(fitting[i], "Code");
}
m_fitParameters.append(parameter);
m_fitParameterCodes.append(code);
}
}
if(value.HasMember("HistogramVariable") && value["HistogramVariable"].IsInt()) {
const int index = value["HistogramVariable"].GetInt();
if(index >= -1 && index < m_fitParameters.size()) m_nHistogramVariable = index;
}
if(value.HasMember("HistogramBins") && value["HistogramBins"].IsInt())
m_nHistogramBins = qBound(1, value["HistogramBins"].GetInt(), 1000);
if(value.HasMember("Variables") && value["Variables"].IsArray()) { if(value.HasMember("Variables") && value["Variables"].IsArray()) {
const rapidjson::Value& variables = value["Variables"]; const rapidjson::Value& variables = value["Variables"];
for(rapidjson::SizeType i = 0; i < variables.Size(); ++i) { for(rapidjson::SizeType i = 0; i < variables.Size(); ++i) {
@ -199,6 +243,8 @@ void nmDataSensitive::BatchResult::FromJsonValue(const rapidjson::Value& value)
RunResult run; RunResult run;
run.m_nRunIndex = item["RunIndex"].GetUint64(); run.m_nRunIndex = item["RunIndex"].GetUint64();
if(item.HasMember("Parameters")) sensitiveNumbersFromJson(item["Parameters"], run.m_vecParameters); if(item.HasMember("Parameters")) sensitiveNumbersFromJson(item["Parameters"], run.m_vecParameters);
if(item.HasMember("FittedParameters")) sensitiveNumbersFromJson(item["FittedParameters"], run.m_vecFittedParameters);
run.m_dPressureRmse = sensitiveNumberFromJson(item, "PressureRmse", -1.0);
run.m_vecPressure = sensitiveCurvesFromJson(item, "Pressure"); run.m_vecPressure = sensitiveCurvesFromJson(item, "Pressure");
run.m_vecLogLog = sensitiveCurvesFromJson(item, "LogLog"); run.m_vecLogLog = sensitiveCurvesFromJson(item, "LogLog");
run.m_vecSemiLog = sensitiveCurvesFromJson(item, "SemiLog"); run.m_vecSemiLog = sensitiveCurvesFromJson(item, "SemiLog");
@ -234,10 +280,10 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
// 字符串字段 // 字符串字段
obj.AddMember("VarGroup", obj.AddMember("VarGroup",
rapidjson::Value(m_varGroup.toStdString().c_str(), allocator).Move(), rapidjson::Value(m_varGroup.toUtf8().constData(), allocator).Move(),
allocator); allocator);
obj.AddMember("VarName", obj.AddMember("VarName",
rapidjson::Value(m_varName.toStdString().c_str(), allocator).Move(), rapidjson::Value(m_varName.toUtf8().constData(), allocator).Move(),
allocator); allocator);
// 简单类型 // 简单类型
@ -245,6 +291,14 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
obj.AddMember("Mode", static_cast<int>(m_mode), allocator); obj.AddMember("Mode", static_cast<int>(m_mode), allocator);
obj.AddMember("Log", m_log, allocator); obj.AddMember("Log", m_log, allocator);
obj.AddMember("Number", m_number, allocator); obj.AddMember("Number", m_number, allocator);
// 六种分布使用同一组字段,恢复时不重新生成用户设置的范围。
obj.AddMember("Distribution", int(m_distribution), allocator);
obj.AddMember("DistributionInitialized", m_distributionInitialized, allocator);
obj.AddMember("Mean", m_mean, allocator);
obj.AddMember("StdDev", m_stdDev, allocator);
obj.AddMember("Peak", m_peak, allocator);
obj.AddMember("DistributionMinimum", m_distributionMin, allocator);
obj.AddMember("DistributionMaximum", m_distributionMax, allocator);
// nmDataAttribute 成员 // nmDataAttribute 成员
obj.AddMember("ModelValue", m_modelValue.ToJsonValue(allocator), allocator); obj.AddMember("ModelValue", m_modelValue.ToJsonValue(allocator), allocator);
@ -264,6 +318,20 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
// 从 RapidJSON Value 反序列化到 VariableSampling // 从 RapidJSON Value 反序列化到 VariableSampling
void nmDataSensitive::VariableSampling::FromJsonValue(const rapidjson::Value& jsonValue) void nmDataSensitive::VariableSampling::FromJsonValue(const rapidjson::Value& jsonValue)
{ {
// 旧文件未保存分布时保留默认状态,由分析窗口按模型值初始化。
*this = VariableSampling();
if(jsonValue.HasMember("Distribution") && jsonValue["Distribution"].IsInt()) {
const int distribution = jsonValue["Distribution"].GetInt();
if(distribution >= NORMAL && distribution <= TRIANGULAR)
m_distribution = static_cast<Distribution>(distribution);
}
m_distributionInitialized = jsonValue.HasMember("DistributionInitialized") &&
jsonValue["DistributionInitialized"].IsBool() && jsonValue["DistributionInitialized"].GetBool();
m_mean = sensitiveNumberFromJson(jsonValue, "Mean", m_mean);
m_stdDev = sensitiveNumberFromJson(jsonValue, "StdDev", m_stdDev);
m_peak = sensitiveNumberFromJson(jsonValue, "Peak", m_peak);
m_distributionMin = sensitiveNumberFromJson(jsonValue, "DistributionMinimum", m_distributionMin);
m_distributionMax = sensitiveNumberFromJson(jsonValue, "DistributionMaximum", m_distributionMax);
if (jsonValue.HasMember("VarGroup") && jsonValue["VarGroup"].IsString()) { if (jsonValue.HasMember("VarGroup") && jsonValue["VarGroup"].IsString()) {
m_varGroup = QString::fromUtf8(jsonValue["VarGroup"].GetString()); m_varGroup = QString::fromUtf8(jsonValue["VarGroup"].GetString());
} }
@ -492,6 +560,7 @@ bool nmDataSensitive::VariableSampling::distributionQuantile(double probability,
nmDataSensitive::nmDataSensitive() nmDataSensitive::nmDataSensitive()
: m_calcType(CALC_DETERMINISTIC) : m_calcType(CALC_DETERMINISTIC)
, m_improveEnabled(false)
, m_totalModelCount(0) , m_totalModelCount(0)
, m_usesBaseUnits(false) , m_usesBaseUnits(false)
, m_variables() , m_variables()
@ -510,6 +579,8 @@ nmDataSensitive& nmDataSensitive::operator=(const nmDataSensitive& other)
{ {
if (this != &other) { if (this != &other) {
m_calcType = other.m_calcType; m_calcType = other.m_calcType;
m_improveEnabled = other.m_improveEnabled;
m_improveSettings = other.m_improveSettings;
m_totalModelCount = other.m_totalModelCount; m_totalModelCount = other.m_totalModelCount;
m_usesBaseUnits = other.m_usesBaseUnits; m_usesBaseUnits = other.m_usesBaseUnits;
m_variables = other.m_variables; m_variables = other.m_variables;
@ -536,6 +607,19 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
root.AddMember("CalculationType", root.AddMember("CalculationType",
static_cast<int>(m_calcType), static_cast<int>(m_calcType),
allocator); allocator);
// 与曲线一起保存采样和拟合配置,重开项目后仍可查看同一批预览。
root.AddMember("ImproveEnabled", m_improveEnabled, allocator);
root.AddMember("MonteCarloCount", m_monteCarloCount, allocator);
root.AddMember("MonteCarloValues", sensitiveCurvesToJson(m_monteCarloValues, allocator), allocator);
if(!m_improveSettings.isNull()) {
const nmSensitivityFitSettings& settings = *m_improveSettings;
rapidjson::Value fitting(rapidjson::kObjectType);
fitting.AddMember("Confirmed", settings.confirmed, allocator);
fitting.AddMember("Parameters", settings.parameters.ToJsonValue(allocator), allocator);
fitting.AddMember("TargetWell", rapidjson::Value(settings.targetWell.toUtf8().constData(), allocator).Move(), allocator);
fitting.AddMember("InitialValues", sensitiveNumbersToJson(settings.initialValues, allocator), allocator);
root.AddMember("ImproveSettings", fitting, allocator);
}
// 总模型数 // 总模型数
root.AddMember("TotalModelCount", root.AddMember("TotalModelCount",
@ -557,6 +641,14 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
// 从 RapidJSON Value 反序列化数据到 nmDataSensitive // 从 RapidJSON Value 反序列化数据到 nmDataSensitive
void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue) void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
{ {
// 先恢复默认值,旧成果缺失新增字段时不沿用上一项目的数据。
*this = nmDataSensitive();
if(!jsonValue.IsObject()) return;
m_improveEnabled = jsonValue.HasMember("ImproveEnabled") &&
jsonValue["ImproveEnabled"].IsBool() && jsonValue["ImproveEnabled"].GetBool();
if(jsonValue.HasMember("MonteCarloCount") && jsonValue["MonteCarloCount"].IsInt() &&
jsonValue["MonteCarloCount"].GetInt() > 0)
m_monteCarloCount = jsonValue["MonteCarloCount"].GetInt();
// 旧项目没有 Results 时保持空结果。 // 旧项目没有 Results 时保持空结果。
m_results.clear(); m_results.clear();
if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) { if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) {
@ -567,10 +659,8 @@ void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
jsonValue["UsesBaseUnits"].IsBool() && jsonValue["UsesBaseUnits"].GetBool(); jsonValue["UsesBaseUnits"].IsBool() && jsonValue["UsesBaseUnits"].GetBool();
if (jsonValue.HasMember("CalculationType") && jsonValue["CalculationType"].IsInt()) { if (jsonValue.HasMember("CalculationType") && jsonValue["CalculationType"].IsInt()) {
int t = jsonValue["CalculationType"].GetInt(); int t = jsonValue["CalculationType"].GetInt();
if (t == CALC_DETERMINISTIC_MULTIVARIATE) if (t >= CALC_DETERMINISTIC && t <= CALC_MONTE_CARLO)
m_calcType = CALC_DETERMINISTIC_MULTIVARIATE; m_calcType = static_cast<CalculationType>(t);
else
m_calcType = CALC_DETERMINISTIC;
} }
if (jsonValue.HasMember("TotalModelCount") && jsonValue["TotalModelCount"].IsUint64()) { if (jsonValue.HasMember("TotalModelCount") && jsonValue["TotalModelCount"].IsUint64()) {
@ -589,6 +679,30 @@ void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
m_variables.append(vs); m_variables.append(vs);
} }
} }
// 恢复完整样本,列数必须与参数表对应,避免预览或生成时错用参数。
m_monteCarloValues = sensitiveCurvesFromJson(jsonValue, "MonteCarloValues");
bool validSamples = m_monteCarloValues.size() == m_monteCarloCount;
for(int row = 0; validSamples && row < m_monteCarloValues.size(); ++row) {
const QVector<double>& values = m_monteCarloValues[row];
validSamples = values.size() == m_variables.size();
for(int column = 0; validSamples && column < values.size(); ++column)
validSamples = qIsFinite(values[column]);
}
if(!validSamples) clearMonteCarloSamples();
if(jsonValue.HasMember("ImproveSettings") && jsonValue["ImproveSettings"].IsObject()) {
const rapidjson::Value& fitting = jsonValue["ImproveSettings"];
QSharedPointer<nmSensitivityFitSettings> settings(new nmSensitivityFitSettings());
settings->targetWell = sensitiveTextFromJson(fitting, "TargetWell");
const bool hasParameters = fitting.HasMember("Parameters") && fitting["Parameters"].IsObject();
if(hasParameters) settings->parameters.FromJsonValue(fitting["Parameters"]);
if(fitting.HasMember("InitialValues")) sensitiveNumbersFromJson(fitting["InitialValues"], settings->initialValues);
bool valid = hasParameters && settings->initialValues.size() == 6 && !settings->targetWell.isEmpty();
for(int i = 0; valid && i < settings->initialValues.size(); ++i)
valid = qIsFinite(settings->initialValues[i]);
settings->confirmed = valid && fitting.HasMember("Confirmed") &&
fitting["Confirmed"].IsBool() && fitting["Confirmed"].GetBool();
if(settings->confirmed) m_improveSettings = settings;
}
} }
// === Calculation type === // === Calculation type ===
@ -604,6 +718,30 @@ void nmDataSensitive::setCalculationType(CalculationType type)
m_calcType = type; m_calcType = type;
} }
bool nmDataSensitive::isImproveEnabled() const
{
// 开关独立于拟合设置,切回第三种后仍可保留上次确认的设置。
return m_improveEnabled;
}
void nmDataSensitive::setImproveEnabled(bool enabled)
{
// 只记录是否启用改善,不改变 Monte-Carlo 的采样方式。
m_improveEnabled = enabled;
}
QSharedPointer<const nmSensitivityFitSettings> nmDataSensitive::getImproveSettings() const
{
// 设置按只读引用共享,窗口重新打开时恢复,编辑时另建副本。
return m_improveSettings;
}
void nmDataSensitive::setImproveSettings(const QSharedPointer<const nmSensitivityFitSettings>& settings)
{
// 只接收拟合窗口确认后的完整配置。
m_improveSettings = settings;
}
// === 总模型数 === // === 总模型数 ===
quint64 nmDataSensitive::getTotalModelCount() const quint64 nmDataSensitive::getTotalModelCount() const
{ {

@ -2201,7 +2201,6 @@ void nmSubWndMain::updateSensitiveHistogramPlot()
QVBoxLayout* layout = new QVBoxLayout(page); QVBoxLayout* layout = new QVBoxLayout(page);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
QHBoxLayout* options = new QHBoxLayout; QHBoxLayout* options = new QHBoxLayout;
page->setProperty("nmHistogramBins", 20);
QPushButton* scatter = new QPushButton(tr("Scatter plot"), page); QPushButton* scatter = new QPushButton(tr("Scatter plot"), page);
scatter->setObjectName("nmScatterButton"); scatter->setObjectName("nmScatterButton");
QPushButton* settings = new QPushButton(tr("Histogram options"), page); QPushButton* settings = new QPushButton(tr("Histogram options"), page);
@ -2230,7 +2229,7 @@ void nmSubWndMain::updateSensitiveHistogramPlot()
connect(settings, SIGNAL(clicked()), this, SLOT(onSensitiveHistogramOptions())); connect(settings, SIGNAL(clicked()), this, SLOT(onSensitiveHistogramOptions()));
} }
iGuiPlot* plot = window->widget()->findChild<iGuiPlot*>(); iGuiPlot* plot = window->widget()->findChild<iGuiPlot*>();
const int binCount = window->widget()->property("nmHistogramBins").toInt(); const int binCount = batch->m_nHistogramBins;
// 拟合参数与误差共用分箱绘制,轴名称和单位随本批选择同步切换。 // 拟合参数与误差共用分箱绘制,轴名称和单位随本批选择同步切换。
const int variable = batch->m_nHistogramVariable; const int variable = batch->m_nHistogramVariable;
plot->setAxisX(variable < 0 ? tr("Goodness of fit") : batch->m_fitParameters[variable].getName(), plot->setAxisX(variable < 0 ? tr("Goodness of fit") : batch->m_fitParameters[variable].getName(),
@ -2338,7 +2337,7 @@ void nmSubWndMain::onSensitiveHistogramOptions()
if(!window || batch.isNull()) return; if(!window || batch.isNull()) return;
nmWxSensitivityHistogramOptions* dialog = window->findChild<nmWxSensitivityHistogramOptions*>(); nmWxSensitivityHistogramOptions* dialog = window->findChild<nmWxSensitivityHistogramOptions*>();
if(!dialog) { if(!dialog) {
dialog = new nmWxSensitivityHistogramOptions(batch, window->widget()->property("nmHistogramBins").toInt(), window->widget()); dialog = new nmWxSensitivityHistogramOptions(batch, batch->m_nHistogramBins, window->widget());
connect(dialog, SIGNAL(settingsChanged(int,int)), this, SLOT(onSensitiveHistogramSettingsChanged(int,int))); connect(dialog, SIGNAL(settingsChanged(int,int)), this, SLOT(onSensitiveHistogramSettingsChanged(int,int)));
connect(manager, SIGNAL(sigSensitiveResultsChanged()), dialog, SLOT(reject())); connect(manager, SIGNAL(sigSensitiveResultsChanged()), dialog, SLOT(reject()));
} }
@ -2349,17 +2348,18 @@ void nmSubWndMain::onSensitiveHistogramOptions()
void nmSubWndMain::onSensitiveHistogramSettingsChanged(int bins, int variable) void nmSubWndMain::onSensitiveHistogramSettingsChanged(int bins, int variable)
{ {
// 直方图变量仅存于本批内存结果,散点图独立选择自己的纵轴。 // 分箱数与统计变量随批次保存,散点图独立选择自己的纵轴。
nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data(); nmDataAnalyzeManager* manager = m_pResultRefreshDataManager.data();
if(!manager || !manager->getSensitiveData()) return; if(!manager || !manager->getSensitiveData()) return;
const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults(); const QSharedPointer<nmDataSensitive::BatchResult> batch = manager->getSensitiveData()->getResults();
if(batch.isNull()) return; if(batch.isNull()) return;
batch->m_nHistogramVariable = variable; batch->m_nHistogramVariable = variable;
batch->m_nHistogramBins = bins;
iSubWndFitting* fitting = m_pResultRefreshFitting.data(); iSubWndFitting* fitting = m_pResultRefreshFitting.data();
if(!fitting || !fitting->m_pMdiArea) return; if(!fitting || !fitting->m_pMdiArea) return;
QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmSensitivityHistogram"); QMdiSubWindow* window = fitting->m_pMdiArea->findChild<QMdiSubWindow*>("nmSensitivityHistogram");
if(!window) return; if(!window) return;
window->widget()->setProperty("nmHistogramBins", bins); fitting->setModified(true);
updateSensitiveHistogramPlot(); updateSensitiveHistogramPlot();
} }
@ -2371,6 +2371,8 @@ void nmSubWndMain::onSensitiveFilterChanged()
QWidget* page = fitting->getFitSubRstWxOf(FSRT_NmRst, false); QWidget* page = fitting->getFitSubRstWxOf(FSRT_NmRst, false);
nmWxParaPropertyPebi* parameters = page ? page->findChild<nmWxParaPropertyPebi*>() : nullptr; nmWxParaPropertyPebi* parameters = page ? page->findChild<nmWxParaPropertyPebi*>() : nullptr;
if(parameters) parameters->refreshSensitivityResults(); if(parameters) parameters->refreshSensitivityResults();
// 只有用户调整阈值才标记修改,加载成果时的面板刷新不触发保存提示。
if(qobject_cast<nmWxSensitivityScatter*>(sender())) fitting->setModified(true);
} }
void nmSubWndMain::onSensitiveScatter() void nmSubWndMain::onSensitiveScatter()

@ -154,6 +154,14 @@ void appendSensitivityParas(nmDataAnalyzeManager* manager, QStringList& paras,
appendResultPara(paras, values, groupCode, lower, minimum); appendResultPara(paras, values, groupCode, lower, minimum);
appendResultPara(paras, values, groupCode, higher, maximum); appendResultPara(paras, values, groupCode, higher, maximum);
} }
// 第四种在采样范围后追加各组拟合最优值的范围,沿用同一阈值和 XML 分组。
for(int i = 0; i < batch->m_fitParameterCodes.size(); ++i) {
double minimum = 0.0, maximum = 0.0;
if(!batch->filteredParameterBounds(i, minimum, maximum, true)) continue;
const QString name = "Sensitivity_Fit_" + batch->m_fitParameterCodes[i];
appendResultPara(paras, values, groupCode, name + "_Minimum", minimum);
appendResultPara(paras, values, groupCode, name + "_Maximum", maximum);
}
} }
QString resultModelTitlePrefix() QString resultModelTitlePrefix()

@ -1523,7 +1523,7 @@ void nmWxSensitive::loadDataToUI(bool refreshRangesOnly)
{ {
// 加载过程中阻止联动刷新,先恢复设置,再一次生成各参数表格。 // 加载过程中阻止联动刷新,先恢复设置,再一次生成各参数表格。
if (!refreshRangesOnly) if (!refreshRangesOnly)
calculationTypeCombo->setCurrentIndex(int(m_sensitiveData.getCalculationType())); calculationTypeCombo->setCurrentIndex(m_sensitiveData.isImproveEnabled() ? 3 : int(m_sensitiveData.getCalculationType()));
m_monteCarloCount->setValue(m_sensitiveData.getMonteCarloCount()); m_monteCarloCount->setValue(m_sensitiveData.getMonteCarloCount());
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);
@ -1611,6 +1611,7 @@ void nmWxSensitive::updateModelCount()
if (m_loading) return; if (m_loading) return;
m_sensitiveData.setCalculationType( m_sensitiveData.setCalculationType(
static_cast<nmDataSensitive::CalculationType>(calculationTypeCombo->itemData(calculationTypeCombo->currentIndex()).toInt())); static_cast<nmDataSensitive::CalculationType>(calculationTypeCombo->itemData(calculationTypeCombo->currentIndex()).toInt()));
m_sensitiveData.setImproveEnabled(calculationTypeCombo->currentIndex() == 3);
for (int group = 0; group < variablesTree->topLevelItemCount(); ++group) { for (int group = 0; group < variablesTree->topLevelItemCount(); ++group) {
QTreeWidgetItem* parent = variablesTree->topLevelItem(group); QTreeWidgetItem* parent = variablesTree->topLevelItem(group);
for (int child = 0; child < parent->childCount(); ++child) { for (int child = 0; child < parent->childCount(); ++child) {
@ -1635,8 +1636,8 @@ void nmWxSensitive::updateModelCount()
m_monteCarloCount->setVisible(monteCarlo); m_monteCarloCount->setVisible(monteCarlo);
// 第四种确认设置且至少选择一个拟合参数后才开放生成,空选择直接禁用。 // 第四种确认设置且至少选择一个拟合参数后才开放生成,空选择直接禁用。
bool hasFittingParameters = false; bool hasFittingParameters = false;
if(!m_improveSettings.isNull()) { if(!m_sensitiveData.getImproveSettings().isNull()) {
const nmDataAutomaticFitting& fitting = m_improveSettings->parameters; const nmDataAutomaticFitting& fitting = m_sensitiveData.getImproveSettings()->parameters;
hasFittingParameters = fitting.getPermeabilitySelected() || fitting.getSkinSelected() || hasFittingParameters = fitting.getPermeabilitySelected() || fitting.getSkinSelected() ||
fitting.getWellboreStorageSelected() || fitting.getPorositySelected() || fitting.getWellboreStorageSelected() || fitting.getPorositySelected() ||
fitting.getFractureConductivitySelected() || fitting.getFractureHalfLengthSelected(); fitting.getFractureConductivitySelected() || fitting.getFractureHalfLengthSelected();
@ -2489,8 +2490,8 @@ void nmWxSensitive::onGenerateClicked()
} }
iAnalRun* analRun = fitting ? fitting->getAnalRun() : nullptr; iAnalRun* analRun = fitting ? fitting->getAnalRun() : nullptr;
// 第四种可选择其他井拟合,其历史曲线也要使用本次的拟压力配置。 // 第四种可选择其他井拟合,其历史曲线也要使用本次的拟压力配置。
nmDataWellBase* fitWell = (calculationTypeCombo->currentIndex() == 3) && !m_improveSettings.isNull() nmDataWellBase* fitWell = m_sensitiveData.isImproveEnabled() && !m_sensitiveData.getImproveSettings().isNull()
? m_pDataManager->findWellByName(m_improveSettings->targetWell) : nullptr; ? m_pDataManager->findWellByName(m_sensitiveData.getImproveSettings()->targetWell) : nullptr;
if(!analRun || !analRun->configPsAbouts(true, fitting->getModelOption(), if(!analRun || !analRun->configPsAbouts(true, fitting->getModelOption(),
false, fitting->getAllWxPtr()) || false, fitting->getAllWxPtr()) ||
!m_pDataManager->updateWellHistoryData(well) || !m_pDataManager->updateWellHistoryData(well) ||
@ -2516,7 +2517,7 @@ void nmWxSensitive::onGenerateClicked()
nmCalculationDllPebiSolverTask source(QString(), m_pDataManager, well->getWellName()); nmCalculationDllPebiSolverTask source(QString(), m_pDataManager, well->getWellName());
// 第四种直接交给后台任务逐组拟合,仍使用主窗口原有进度框。 // 第四种直接交给后台任务逐组拟合,仍使用主窗口原有进度框。
task.reset(source.createSensitivityBatch(m_sensitiveData, &error, task.reset(source.createSensitivityBatch(m_sensitiveData, &error,
(calculationTypeCombo->currentIndex() == 3) ? m_improveSettings.data() : nullptr)); m_sensitiveData.isImproveEnabled() ? m_sensitiveData.getImproveSettings().data() : nullptr));
} catch(const std::bad_alloc&) { } catch(const std::bad_alloc&) {
error = tr("Insufficient memory to prepare solver input."); error = tr("Insufficient memory to prepare solver input.");
} }
@ -2628,7 +2629,7 @@ void nmWxSensitive::onImproveSettingsClicked()
{ {
// 在副本上编辑,只有确定才替换设置;取消保留上次确认的内容。 // 在副本上编辑,只有确定才替换设置;取消保留上次确认的内容。
nmSensitivityFitSettings settings; nmSensitivityFitSettings settings;
if(!m_improveSettings.isNull()) settings = *m_improveSettings; if(!m_sensitiveData.getImproveSettings().isNull()) settings = *m_sensitiveData.getImproveSettings();
// 将分析窗口当前勾选的采样参数传给拟合窗口,用于禁用重叠选项。 // 将分析窗口当前勾选的采样参数传给拟合窗口,用于禁用重叠选项。
updateModelCount(); updateModelCount();
QStringList sampledParameters; QStringList sampledParameters;
@ -2636,7 +2637,7 @@ void nmWxSensitive::onImproveSettingsClicked()
if(variable.m_enabled) sampledParameters.append(variable.m_varName); if(variable.m_enabled) sampledParameters.append(variable.m_varName);
nmWxAutomaticFitting dialog(this, &settings, sampledParameters); nmWxAutomaticFitting dialog(this, &settings, sampledParameters);
if(dialog.exec() == QDialog::Accepted) { if(dialog.exec() == QDialog::Accepted) {
m_improveSettings = QSharedPointer<nmSensitivityFitSettings>(new nmSensitivityFitSettings(settings)); m_sensitiveData.setImproveSettings(QSharedPointer<const nmSensitivityFitSettings>(new nmSensitivityFitSettings(settings)));
updateModelCount(); updateModelCount();
} }
} }

Loading…
Cancel
Save