|
|
|
|
@ -18,16 +18,17 @@ bool nmDataSensitive::BatchResult::statisticValue(const RunResult& run, int vari
|
|
|
|
|
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;
|
|
|
|
|
for(size_t i = 0; i < m_runs.size(); ++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) ||
|
|
|
|
|
run.m_dPressureRmse < 0.0 || !(run.m_dPressureRmse < m_dFilterThreshold) ||
|
|
|
|
|
index < 0 || index >= run.m_vecParameters.size()) continue;
|
|
|
|
|
const double value = run.m_vecParameters[index];
|
|
|
|
|
index < 0 || index >= parameters.size()) continue;
|
|
|
|
|
const double value = parameters[index];
|
|
|
|
|
if(!qIsFinite(value)) continue;
|
|
|
|
|
minimum = found ? qMin(minimum, 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()
|
|
|
|
|
? 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(
|
|
|
|
|
@ -140,6 +148,18 @@ rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue(
|
|
|
|
|
value.AddMember("FailureReason", rapidjson::Value(m_sFailureReason.toUtf8().constData(), allocator).Move(), allocator);
|
|
|
|
|
value.AddMember("HistoryLogLog", sensitiveCurvesToJson(m_vecHistoryLogLog, 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);
|
|
|
|
|
for(int i = 0; i < m_variables.size(); ++i)
|
|
|
|
|
variables.PushBack(m_variables[i].ToJsonValue(allocator), allocator);
|
|
|
|
|
@ -150,6 +170,8 @@ rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue(
|
|
|
|
|
rapidjson::Value item(rapidjson::kObjectType);
|
|
|
|
|
item.AddMember("RunIndex", run.m_nRunIndex, 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("LogLog", sensitiveCurvesToJson(run.m_vecLogLog, 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();
|
|
|
|
|
if(!value.IsObject()) return;
|
|
|
|
|
if(value.HasMember("CalculationType") && value["CalculationType"].IsInt() &&
|
|
|
|
|
value["CalculationType"].GetInt() == CALC_DETERMINISTIC_MULTIVARIATE)
|
|
|
|
|
m_calcType = CALC_DETERMINISTIC_MULTIVARIATE;
|
|
|
|
|
value["CalculationType"].GetInt() >= CALC_DETERMINISTIC && value["CalculationType"].GetInt() <= CALC_MONTE_CARLO)
|
|
|
|
|
m_calcType = static_cast<CalculationType>(value["CalculationType"].GetInt());
|
|
|
|
|
m_sWellCode = sensitiveTextFromJson(value, "WellCode");
|
|
|
|
|
m_sWellName = sensitiveTextFromJson(value, "WellName");
|
|
|
|
|
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_vecHistoryLogLog = sensitiveCurvesFromJson(value, "HistoryLogLog");
|
|
|
|
|
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()) {
|
|
|
|
|
const rapidjson::Value& variables = value["Variables"];
|
|
|
|
|
for(rapidjson::SizeType i = 0; i < variables.Size(); ++i) {
|
|
|
|
|
@ -199,6 +243,8 @@ void nmDataSensitive::BatchResult::FromJsonValue(const rapidjson::Value& value)
|
|
|
|
|
RunResult run;
|
|
|
|
|
run.m_nRunIndex = item["RunIndex"].GetUint64();
|
|
|
|
|
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_vecLogLog = sensitiveCurvesFromJson(item, "LogLog");
|
|
|
|
|
run.m_vecSemiLog = sensitiveCurvesFromJson(item, "SemiLog");
|
|
|
|
|
@ -234,10 +280,10 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
|
|
|
|
|
|
|
|
|
|
// 字符串字段
|
|
|
|
|
obj.AddMember("VarGroup",
|
|
|
|
|
rapidjson::Value(m_varGroup.toStdString().c_str(), allocator).Move(),
|
|
|
|
|
rapidjson::Value(m_varGroup.toUtf8().constData(), allocator).Move(),
|
|
|
|
|
allocator);
|
|
|
|
|
obj.AddMember("VarName",
|
|
|
|
|
rapidjson::Value(m_varName.toStdString().c_str(), allocator).Move(),
|
|
|
|
|
rapidjson::Value(m_varName.toUtf8().constData(), allocator).Move(),
|
|
|
|
|
allocator);
|
|
|
|
|
|
|
|
|
|
// 简单类型
|
|
|
|
|
@ -245,6 +291,14 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
|
|
|
|
|
obj.AddMember("Mode", static_cast<int>(m_mode), allocator);
|
|
|
|
|
obj.AddMember("Log", m_log, 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 成员
|
|
|
|
|
obj.AddMember("ModelValue", m_modelValue.ToJsonValue(allocator), allocator);
|
|
|
|
|
@ -264,6 +318,20 @@ rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue(
|
|
|
|
|
// 从 RapidJSON Value 反序列化到 VariableSampling
|
|
|
|
|
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()) {
|
|
|
|
|
m_varGroup = QString::fromUtf8(jsonValue["VarGroup"].GetString());
|
|
|
|
|
}
|
|
|
|
|
@ -492,6 +560,7 @@ bool nmDataSensitive::VariableSampling::distributionQuantile(double probability,
|
|
|
|
|
|
|
|
|
|
nmDataSensitive::nmDataSensitive()
|
|
|
|
|
: m_calcType(CALC_DETERMINISTIC)
|
|
|
|
|
, m_improveEnabled(false)
|
|
|
|
|
, m_totalModelCount(0)
|
|
|
|
|
, m_usesBaseUnits(false)
|
|
|
|
|
, m_variables()
|
|
|
|
|
@ -510,6 +579,8 @@ nmDataSensitive& nmDataSensitive::operator=(const nmDataSensitive& other)
|
|
|
|
|
{
|
|
|
|
|
if (this != &other) {
|
|
|
|
|
m_calcType = other.m_calcType;
|
|
|
|
|
m_improveEnabled = other.m_improveEnabled;
|
|
|
|
|
m_improveSettings = other.m_improveSettings;
|
|
|
|
|
m_totalModelCount = other.m_totalModelCount;
|
|
|
|
|
m_usesBaseUnits = other.m_usesBaseUnits;
|
|
|
|
|
m_variables = other.m_variables;
|
|
|
|
|
@ -536,6 +607,19 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
|
|
|
|
|
root.AddMember("CalculationType",
|
|
|
|
|
static_cast<int>(m_calcType),
|
|
|
|
|
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",
|
|
|
|
|
@ -557,6 +641,14 @@ rapidjson::Value nmDataSensitive::ToJsonValue(
|
|
|
|
|
// 从 RapidJSON Value 反序列化数据到 nmDataSensitive
|
|
|
|
|
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 时保持空结果。
|
|
|
|
|
m_results.clear();
|
|
|
|
|
if(jsonValue.HasMember("Results") && jsonValue["Results"].IsObject()) {
|
|
|
|
|
@ -567,10 +659,8 @@ void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
|
|
|
|
|
jsonValue["UsesBaseUnits"].IsBool() && jsonValue["UsesBaseUnits"].GetBool();
|
|
|
|
|
if (jsonValue.HasMember("CalculationType") && jsonValue["CalculationType"].IsInt()) {
|
|
|
|
|
int t = jsonValue["CalculationType"].GetInt();
|
|
|
|
|
if (t == CALC_DETERMINISTIC_MULTIVARIATE)
|
|
|
|
|
m_calcType = CALC_DETERMINISTIC_MULTIVARIATE;
|
|
|
|
|
else
|
|
|
|
|
m_calcType = CALC_DETERMINISTIC;
|
|
|
|
|
if (t >= CALC_DETERMINISTIC && t <= CALC_MONTE_CARLO)
|
|
|
|
|
m_calcType = static_cast<CalculationType>(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (jsonValue.HasMember("TotalModelCount") && jsonValue["TotalModelCount"].IsUint64()) {
|
|
|
|
|
@ -589,6 +679,30 @@ void nmDataSensitive::FromJsonValue(const rapidjson::Value& jsonValue)
|
|
|
|
|
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 ===
|
|
|
|
|
@ -604,6 +718,30 @@ void nmDataSensitive::setCalculationType(CalculationType 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
|
|
|
|
|
{
|
|
|
|
|
|