#include "nmDataSensitive.h" #include #include #include #include #include bool nmDataSensitive::BatchResult::filteredParameterBounds(int index, double& minimum, double& maximum) const { // 所有参数共用同一批误差严格小于阈值的样本,不受曲线可见性影响。 bool found = false; for(size_t i = 0; i < m_runs.size(); ++i) { const RunResult& run = m_runs[i]; 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]; if(!qIsFinite(value)) continue; minimum = found ? qMin(minimum, value) : value; maximum = found ? qMax(maximum, value) : value; found = true; } return found; } namespace { double normalCdf(double z) { // 标准正态累积分布近似,最大绝对误差约 7.5e-8,兼容 VS2010。 const double x = std::fabs(z), t = 1.0 / (1.0 + 0.2316419 * x); const double tail = 0.3989422804014327 * std::exp(-0.5 * x * x) * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429)))); return z < 0.0 ? tail : 1.0 - tail; } double normalQuantile(double probability, double low = -10.0, double high = 10.0) { // 在累积分布上二分求逆,避免引入新的数学库。 for(int i = 0; i < 60; ++i) { const double mid = (low + high) * 0.5; if(normalCdf(mid) < probability) low = mid; else high = mid; } return (low + high) * 0.5; } rapidjson::Value sensitiveNumbersToJson(const QVector& values, rapidjson::Document::AllocatorType& allocator) { // 使用数值数组保存曲线精度,不经过界面显示文本转换。 rapidjson::Value array(rapidjson::kArrayType); for(int i = 0; i < values.size(); ++i) { // 求解曲线中的无效点保存为空值,避免 NaN 或无穷值使整个项目 JSON 无法写入。 if(qIsFinite(values[i])) array.PushBack(values[i], allocator); else array.PushBack(rapidjson::Value().Move(), allocator); } return array; } rapidjson::Value sensitiveCurvesToJson(const QVector >& curves, rapidjson::Document::AllocatorType& allocator) { // 曲线列顺序与求解器返回值一致。 rapidjson::Value array(rapidjson::kArrayType); for(int i = 0; i < curves.size(); ++i) array.PushBack(sensitiveNumbersToJson(curves[i], allocator), allocator); return array; } bool sensitiveNumbersFromJson(const rapidjson::Value& array, QVector& values) { // 损坏的数值列整体丢弃,避免跳过某个元素后时间与压力错位。 values.clear(); if(!array.IsArray()) return false; for(rapidjson::SizeType i = 0; i < array.Size(); ++i) { // 空值保留原位置,绘图时过滤无效点,不改变其他时间和值的对应关系。 if(array[i].IsNull()) { values.append(std::numeric_limits::quiet_NaN()); continue; } if(!array[i].IsNumber() || !qIsFinite(array[i].GetDouble())) { values.clear(); return false; } values.append(array[i].GetDouble()); } return true; } QVector > sensitiveCurvesFromJson(const rapidjson::Value& value, const char* key) { // 兼容旧成果中不存在批量曲线的情况。 QVector > curves; if(!value.HasMember(key) || !value[key].IsArray()) return curves; const rapidjson::Value& array = value[key]; for(rapidjson::SizeType i = 0; i < array.Size(); ++i) { QVector column; if(!sensitiveNumbersFromJson(array[i], column)) return QVector >(); curves.append(column); } return curves; } QString sensitiveTextFromJson(const rapidjson::Value& value, const char* key) { // 文本统一使用 UTF-8,参数名称和失败原因可包含中文。 return value.HasMember(key) && value[key].IsString() ? QString::fromUtf8(value[key].GetString()) : QString(); } } rapidjson::Value nmDataSensitive::BatchResult::ToJsonValue( rapidjson::Document::AllocatorType& allocator) const { // 批次保存自己的配置及目标井,不引用之后可能被修改的当前模型。 rapidjson::Value value(rapidjson::kObjectType); value.AddMember("CalculationType", int(m_calcType), allocator); value.AddMember("WellCode", rapidjson::Value(m_sWellCode.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("WellName", rapidjson::Value(m_sWellName.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("AxisXName", rapidjson::Value(m_sAxisXName.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("AxisXUnit", rapidjson::Value(m_sAxisXUnit.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("AxisYName", rapidjson::Value(m_sAxisYName.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("AxisYUnit", rapidjson::Value(m_sAxisYUnit.toUtf8().constData(), allocator).Move(), allocator); value.AddMember("Completed", m_bCompleted, allocator); value.AddMember("Stopped", m_bStopped, allocator); 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); rapidjson::Value variables(rapidjson::kArrayType); for(int i = 0; i < m_variables.size(); ++i) variables.PushBack(m_variables[i].ToJsonValue(allocator), allocator); value.AddMember("Variables", variables, allocator); rapidjson::Value runs(rapidjson::kArrayType); for(size_t i = 0; i < m_runs.size(); ++i) { const RunResult& run = m_runs[i]; rapidjson::Value item(rapidjson::kObjectType); item.AddMember("RunIndex", run.m_nRunIndex, allocator); item.AddMember("Parameters", sensitiveNumbersToJson(run.m_vecParameters, allocator), 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); item.AddMember("Succeeded", run.m_bSucceeded, allocator); item.AddMember("Cancelled", run.m_bCancelled, allocator); item.AddMember("Visible", run.m_bVisible, allocator); item.AddMember("FailureReason", rapidjson::Value(run.m_sFailureReason.toUtf8().constData(), allocator).Move(), allocator); runs.PushBack(item, allocator); } value.AddMember("Runs", runs, allocator); return value; } 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; m_sWellCode = sensitiveTextFromJson(value, "WellCode"); m_sWellName = sensitiveTextFromJson(value, "WellName"); m_sAxisXName = sensitiveTextFromJson(value, "AxisXName"); m_sAxisXUnit = sensitiveTextFromJson(value, "AxisXUnit"); m_sAxisYName = sensitiveTextFromJson(value, "AxisYName"); m_sAxisYUnit = sensitiveTextFromJson(value, "AxisYUnit"); m_sFailureReason = sensitiveTextFromJson(value, "FailureReason"); m_bCompleted = value.HasMember("Completed") && value["Completed"].IsBool() && value["Completed"].GetBool(); m_bStopped = value.HasMember("Stopped") && value["Stopped"].IsBool() && value["Stopped"].GetBool(); m_vecHistoryLogLog = sensitiveCurvesFromJson(value, "HistoryLogLog"); m_vecBaseLogLog = sensitiveCurvesFromJson(value, "BaseLogLog"); if(value.HasMember("Variables") && value["Variables"].IsArray()) { const rapidjson::Value& variables = value["Variables"]; for(rapidjson::SizeType i = 0; i < variables.Size(); ++i) { if(!variables[i].IsObject()) continue; VariableSampling variable; variable.FromJsonValue(variables[i]); m_variables.append(variable); } } if(!value.HasMember("Runs") || !value["Runs"].IsArray()) return; const rapidjson::Value& runs = value["Runs"]; for(rapidjson::SizeType i = 0; i < runs.Size(); ++i) { const rapidjson::Value& item = runs[i]; if(!item.IsObject() || !item.HasMember("RunIndex") || !item["RunIndex"].IsUint64()) continue; RunResult run; run.m_nRunIndex = item["RunIndex"].GetUint64(); if(item.HasMember("Parameters")) sensitiveNumbersFromJson(item["Parameters"], run.m_vecParameters); run.m_vecPressure = sensitiveCurvesFromJson(item, "Pressure"); run.m_vecLogLog = sensitiveCurvesFromJson(item, "LogLog"); run.m_vecSemiLog = sensitiveCurvesFromJson(item, "SemiLog"); run.m_bSucceeded = item.HasMember("Succeeded") && item["Succeeded"].IsBool() && item["Succeeded"].GetBool(); run.m_bCancelled = item.HasMember("Cancelled") && item["Cancelled"].IsBool() && item["Cancelled"].GetBool(); if(item.HasMember("Visible") && item["Visible"].IsBool()) run.m_bVisible = item["Visible"].GetBool(); run.m_sFailureReason = sensitiveTextFromJson(item, "FailureReason"); m_runs.push_back(run); } } nmDataSensitive::VariableSampling::VariableSampling() : m_varGroup("") , m_varName("") , m_enabled(false) , m_mode(MODE_AUTOMATIC) , m_log(false) , m_number(0) , m_distribution(NORMAL), m_distributionInitialized(false), m_mean(0.0), m_stdDev(1.0), m_peak(0.0) , m_distributionMin(0.0), m_distributionMax(1.0) { m_modelValue = nmDataAttribute("Model value", "", ""); m_minValue = nmDataAttribute("Minimum", "", ""); m_maxValue = nmDataAttribute("Maximum", "", ""); m_values.clear(); } // 序列化 VariableSampling 为 RapidJSON Value rapidjson::Value nmDataSensitive::VariableSampling::ToJsonValue( rapidjson::Document::AllocatorType& allocator) const { rapidjson::Value obj(rapidjson::kObjectType); // 字符串字段 obj.AddMember("VarGroup", rapidjson::Value(m_varGroup.toStdString().c_str(), allocator).Move(), allocator); obj.AddMember("VarName", rapidjson::Value(m_varName.toStdString().c_str(), allocator).Move(), allocator); // 简单类型 obj.AddMember("Enabled", m_enabled, allocator); obj.AddMember("Mode", static_cast(m_mode), allocator); obj.AddMember("Log", m_log, allocator); obj.AddMember("Number", m_number, allocator); // nmDataAttribute 成员 obj.AddMember("ModelValue", m_modelValue.ToJsonValue(allocator), allocator); obj.AddMember("Minimum", m_minValue.ToJsonValue(allocator), allocator); obj.AddMember("Maximum", m_maxValue.ToJsonValue(allocator), allocator); // values 数组 rapidjson::Value arr(rapidjson::kArrayType); for (int i = 0; i < m_values.size(); ++i) { arr.PushBack(m_values.at(i), allocator); } obj.AddMember("Values", arr, allocator); return obj; } // 从 RapidJSON Value 反序列化到 VariableSampling void nmDataSensitive::VariableSampling::FromJsonValue(const rapidjson::Value& jsonValue) { if (jsonValue.HasMember("VarGroup") && jsonValue["VarGroup"].IsString()) { m_varGroup = QString::fromUtf8(jsonValue["VarGroup"].GetString()); } if (jsonValue.HasMember("VarName") && jsonValue["VarName"].IsString()) { m_varName = QString::fromUtf8(jsonValue["VarName"].GetString()); } if (jsonValue.HasMember("Enabled") && jsonValue["Enabled"].IsBool()) { m_enabled = jsonValue["Enabled"].GetBool(); } if (jsonValue.HasMember("Mode") && jsonValue["Mode"].IsInt()) { int m = jsonValue["Mode"].GetInt(); m_mode = (m == MODE_MANUAL) ? MODE_MANUAL : MODE_AUTOMATIC; } if (jsonValue.HasMember("Log") && jsonValue["Log"].IsBool()) { m_log = jsonValue["Log"].GetBool(); } if (jsonValue.HasMember("Number") && jsonValue["Number"].IsInt()) { m_number = jsonValue["Number"].GetInt(); } if (jsonValue.HasMember("ModelValue") && jsonValue["ModelValue"].IsObject()) { m_modelValue.FromJsonValue(jsonValue["ModelValue"]); } if (jsonValue.HasMember("Minimum") && jsonValue["Minimum"].IsObject()) { m_minValue.FromJsonValue(jsonValue["Minimum"]); } if (jsonValue.HasMember("Maximum") && jsonValue["Maximum"].IsObject()) { m_maxValue.FromJsonValue(jsonValue["Maximum"]); } m_values.clear(); if (jsonValue.HasMember("Values") && jsonValue["Values"].IsArray()) { const rapidjson::Value& arr = jsonValue["Values"]; for (rapidjson::SizeType i = 0; i < arr.Size(); ++i) { if (arr[i].IsNumber()) { m_values.append(arr[i].GetDouble()); } } } } // varGroup QString nmDataSensitive::VariableSampling::getVarGroup() const { return m_varGroup; } void nmDataSensitive::VariableSampling::setVarGroup(const QString& group) { m_varGroup = group; } // varName QString nmDataSensitive::VariableSampling::getVarName() const { return m_varName; } void nmDataSensitive::VariableSampling::setVarName(const QString& name) { m_varName = name; } // enabled bool nmDataSensitive::VariableSampling::getEnabled() const { return m_enabled; } void nmDataSensitive::VariableSampling::setEnabled(bool e) { m_enabled = e; } // mode nmDataSensitive::VariableSampling::Mode nmDataSensitive::VariableSampling::getMode() const { return m_mode; } void nmDataSensitive::VariableSampling::setMode(Mode m) { m_mode = m; } // log bool nmDataSensitive::VariableSampling::getLog() const { return m_log; } void nmDataSensitive::VariableSampling::setLog(bool l) { m_log = l; } // number int nmDataSensitive::VariableSampling::getNumber() const { return m_number; } void nmDataSensitive::VariableSampling::setNumber(int n) { m_number = n; } // modelValue nmDataAttribute& nmDataSensitive::VariableSampling::getModelValue() { return m_modelValue; } void nmDataSensitive::VariableSampling::setModelValue(const nmDataAttribute& attr) { m_modelValue = attr; } // minValue nmDataAttribute& nmDataSensitive::VariableSampling::getMinValue() { return m_minValue; } void nmDataSensitive::VariableSampling::setMinValue(const nmDataAttribute& attr) { m_minValue = attr; } // maxValue nmDataAttribute& nmDataSensitive::VariableSampling::getMaxValue() { return m_maxValue; } void nmDataSensitive::VariableSampling::setMaxValue(const nmDataAttribute& attr) { m_maxValue = attr; } // values QVector& nmDataSensitive::VariableSampling::getValues() { return m_values; } void nmDataSensitive::VariableSampling::setValues(const QVector& vals) { m_values = vals; } QVector nmDataSensitive::VariableSampling::samplingValues() const { // 表格显示精度不参与采样,包含零或负数的对数范围按线性处理。 if (m_mode == MODE_MANUAL) return m_values; QVector values; if (m_number <= 0) return values; const double minimum = m_minValue.getValue().toDouble(); const double maximum = m_maxValue.getValue().toDouble(); const bool logarithmic = m_log && minimum > 0.0 && maximum > 0.0; for (int i = 0; i < m_number; ++i) { const double t = m_number == 1 ? 0.0 : double(i) / (m_number - 1); double value = logarithmic ? std::exp((1.0 - t) * std::log(minimum) + t * std::log(maximum)) : (1.0 - t) * minimum + t * maximum; if (m_number == 1) value = m_modelValue.getValue().toDouble(); else if (i == 0) value = minimum; else if (i == m_number - 1) value = maximum; values.append(value); } return values; } bool nmDataSensitive::VariableSampling::distributionQuantile(double probability, double& value, bool limitToPhysicalRange) const { // 六种分布都由分位数取值;对数正态的均值、标准差仍使用参数原单位。 if(!qIsFinite(probability) || probability <= 0.0 || probability >= 1.0) return false; const bool specifiedBounds = m_distribution >= TRUNCATED_NORMAL; const bool bounded = specifiedBounds || limitToPhysicalRange; const double low = specifiedBounds ? m_distributionMin : m_modelValue.getMinValue().toDouble(); const double high = specifiedBounds ? m_distributionMax : m_modelValue.getMaxValue().toDouble(); if(bounded && (!qIsFinite(low) || !qIsFinite(high) || low >= high)) return false; if(m_distribution == UNIFORM) value = low + probability * (high - low); else if(m_distribution == TRIANGULAR) { if(!qIsFinite(m_peak) || m_peak < low || m_peak > high) return false; const double split = (m_peak - low) / (high - low); value = probability < split ? low + std::sqrt(probability * (high - low) * (m_peak - low)) : high - std::sqrt((1.0 - probability) * (high - low) * (high - m_peak)); } else { if(m_distribution < NORMAL || m_distribution > TRUNCATED_LOGNORMAL || !qIsFinite(m_mean) || !qIsFinite(m_stdDev) || m_stdDev <= 0.0) return false; const bool logarithmic = m_distribution == LOGNORMAL || m_distribution == TRUNCATED_LOGNORMAL; if(logarithmic && (m_mean <= 0.0 || (bounded && low < 0.0))) return false; double mean = m_mean, sigma = m_stdDev; if(logarithmic) { const double ratio = m_stdDev / m_mean; const double variance = std::log(1.0 + ratio * ratio); mean = std::log(m_mean) - variance * 0.5; sigma = std::sqrt(variance); } if(!qIsFinite(mean) || !qIsFinite(sigma) || sigma <= 0.0) return false; double quantile = 0.0; if(bounded) { // 在允许范围内重新分配概率后取样,保持拉丁超立方分层,不裁剪或重抽越界样本。 double zLow = logarithmic && low == 0.0 ? -std::numeric_limits::infinity() : ((logarithmic ? std::log(low) : low) - mean) / sigma; double zHigh = ((logarithmic ? std::log(high) : high) - mean) / sigma; // 右尾利用正态分布对称性转到左尾,避免两个接近 1 的累积概率相减。 const bool reflected = zLow > 0.0; if(reflected) { const double previousLow = zLow; zLow = -zHigh; zHigh = -previousLow; } const double a = normalCdf(zLow), b = normalCdf(zHigh); if(!(b > a)) return false; probability = a + (reflected ? 1.0 - probability : probability) * (b - a); // 超过 40 个标准差时双精度尾概率已下溢;缩小二分区间,同时保留实际边界。 quantile = normalQuantile(probability, qMax(zLow, -40.0), qMin(zHigh, 40.0)); if(reflected) quantile = -quantile; } else quantile = normalQuantile(probability); value = mean + sigma * quantile; if(logarithmic) value = std::exp(value); } return qIsFinite(value); } nmDataSensitive::nmDataSensitive() : m_calcType(CALC_DETERMINISTIC) , m_totalModelCount(0) , m_usesBaseUnits(false) , m_variables() , m_monteCarloCount(50) { } // 拷贝构造 nmDataSensitive::nmDataSensitive(const nmDataSensitive& other) { *this = other; } // 赋值运算符 nmDataSensitive& nmDataSensitive::operator=(const nmDataSensitive& other) { if (this != &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_monteCarloCount = other.m_monteCarloCount; m_monteCarloValues = other.m_monteCarloValues; m_results = other.m_results; } return *this; } nmDataSensitive::~nmDataSensitive() { } // 序列化 nmDataSensitive 为 RapidJSON Value rapidjson::Value nmDataSensitive::ToJsonValue( rapidjson::Document::AllocatorType& allocator) const { 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", static_cast(m_calcType), allocator); // 总模型数 root.AddMember("TotalModelCount", m_totalModelCount, allocator); // 变量数组 rapidjson::Value arr(rapidjson::kArrayType); for (int i = 0; i < m_variables.size(); ++i) { arr.PushBack(m_variables.at(i).ToJsonValue(allocator), allocator); } root.AddMember("Variables", arr, allocator); // 批量结果随现有 Sensitive 节点保存,不增加独立成果文件。 if(!m_results.isNull()) root.AddMember("Results", m_results->ToJsonValue(allocator), allocator); return root; } // 从 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()) { m_results = QSharedPointer(new BatchResult()); m_results->FromJsonValue(jsonValue["Results"]); } m_usesBaseUnits = jsonValue.HasMember("UsesBaseUnits") && 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 (jsonValue.HasMember("TotalModelCount") && jsonValue["TotalModelCount"].IsUint64()) { m_totalModelCount = jsonValue["TotalModelCount"].GetUint64(); } m_variables.clear(); if (jsonValue.HasMember("Variables") && jsonValue["Variables"].IsArray()) { const rapidjson::Value& arr = jsonValue["Variables"]; for (rapidjson::SizeType i = 0; i < arr.Size(); ++i) { if (!arr[i].IsObject()) continue; VariableSampling vs; vs.FromJsonValue(arr[i]); m_variables.append(vs); } } } // === Calculation type === nmDataSensitive::CalculationType nmDataSensitive::getCalculationType() const { return m_calcType; } void nmDataSensitive::setCalculationType(CalculationType type) { // 切换计算方法后不再沿用上一种方法的预览样本。 if(type != m_calcType) clearMonteCarloSamples(); m_calcType = type; } // === 总模型数 === quint64 nmDataSensitive::getTotalModelCount() const { return m_totalModelCount; } void nmDataSensitive::setTotalModelCount(quint64 count) { m_totalModelCount = count; } quint64 nmDataSensitive::calculateModelCount(bool* overflow) const { // 未选择变量时为零;两种确定性方法分别累计和、积。 if (overflow) *overflow = false; if(m_calcType == CALC_MONTE_CARLO) { for(int i = 0; i < m_variables.size(); ++i) if(m_variables[i].m_enabled) return qMax(0, m_monteCarloCount); return 0; } quint64 count = 0; bool selected = false; for (int i = 0; i < m_variables.size(); ++i) { const VariableSampling& variable = m_variables[i]; if (!variable.m_enabled) continue; const quint64 number = variable.m_number > 0 ? variable.m_number : 0; if (!number) return 0; const quint64 limit = (std::numeric_limits::max)(); if (m_calcType == CALC_DETERMINISTIC) { if (count > limit - number) { if (overflow) *overflow = true; return 0; } count += number; } else { if (!selected) count = 1; if (count > limit / number) { if (overflow) *overflow = true; return 0; } count *= number; } selected = true; } return count; } bool nmDataSensitive::getRunValues(quint64 runIndex, QVector& values) const { // 按编号解码组合,不预先展开笛卡尔积,也不删除重复的基准组合。 bool overflow = false; const quint64 count = calculateModelCount(&overflow); if (overflow || runIndex >= count) return false; if(m_calcType == CALC_MONTE_CARLO) { if(m_monteCarloValues.size() != int(count)) return false; values = m_monteCarloValues[int(runIndex)]; return true; } values.clear(); for (int i = 0; i < m_variables.size(); ++i) values.append(m_variables[i].m_modelValue.getValue().toDouble()); if (m_calcType == CALC_DETERMINISTIC) { for (int i = 0; i < m_variables.size(); ++i) { const VariableSampling& variable = m_variables[i]; if (!variable.m_enabled) continue; const QVector samples = variable.samplingValues(); if (samples.size() != variable.m_number) return false; if (runIndex < quint64(samples.size())) { values[i] = samples[int(runIndex)]; return true; } runIndex -= samples.size(); } } else { // Saphir 按第一个选中参数最快变化的顺序列出多变量组合。 for (int i = 0; i < m_variables.size(); ++i) { const VariableSampling& variable = m_variables[i]; if (!variable.m_enabled) continue; const QVector samples = variable.samplingValues(); if (samples.isEmpty() || samples.size() != variable.m_number) return false; values[i] = samples[int(runIndex % samples.size())]; runIndex /= samples.size(); } return true; } return false; } int nmDataSensitive::getMonteCarloCount() const { // Monte-Carlo 总次数由用户指定,不使用单参数次数的和或积。 return m_monteCarloCount; } void nmDataSensitive::setMonteCarloCount(int count) { // 次数变化后旧样本失效,下次预览或生成时重新抽样。 if(count != m_monteCarloCount) clearMonteCarloSamples(); m_monteCarloCount = count; } void nmDataSensitive::clearMonteCarloSamples() { // 只丢弃待计算样本,已完成批次的结果仍由结果窗口管理。 m_monteCarloValues.clear(); } bool nmDataSensitive::resampleMonteCarlo(QString* error) { // 各参数的累积概率均分为 N 层,每层抽一点,再独立打乱,组成 N 组参数。 if(error) error->clear(); m_monteCarloValues.clear(); const int count = int(calculateModelCount()); if(m_calcType != CALC_MONTE_CARLO || count <= 0) return false; std::mt19937 random(uint(QDateTime::currentMSecsSinceEpoch()) ^ uint(qrand())); std::uniform_real_distribution uniform(0.0, 1.0); QVector > runs(count, QVector(m_variables.size())); for(int column = 0; column < m_variables.size(); ++column) { const VariableSampling& variable = m_variables[column]; QVector samples(count, variable.m_modelValue.getValue().toDouble()); if(variable.m_enabled) { for(int row = 0; row < count; ++row) { const double jitter = qBound(1e-12, uniform(random), 1.0 - 1e-12); if(!variable.distributionQuantile((row + jitter) / count, samples[row])) { if(error) *error = tr("%1: invalid distribution parameters.").arg(variable.m_varName); return false; } } for(int row = count - 1; row > 0; --row) { std::uniform_int_distribution index(0, row); qSwap(samples[row], samples[index(random)]); } } for(int row = 0; row < count; ++row) runs[row][column] = samples[row]; } m_monteCarloValues.swap(runs); return true; } bool nmDataSensitive::usesBaseUnits() const { return m_usesBaseUnits; } QString nmDataSensitive::getModelSnapshotId() const { // 范围版本独立于敏感性批次结果。 return m_modelSnapshotId; } void nmDataSensitive::setModelSnapshotId(const QString& snapshotId) { // 仅在同步模型与范围后登记,编辑采样值不改变此标识。 if(snapshotId != m_modelSnapshotId) clearMonteCarloSamples(); m_modelSnapshotId = snapshotId; } QSharedPointer nmDataSensitive::getResults() const { // 曲线容器按共享引用交给界面,重复打开不复制整批结果。 return m_results; } void nmDataSensitive::setResults(const QSharedPointer& results) { // 结果仅在线程结束后发布,后续参数编辑不会重写冻结的批次配置。 m_results = results; } void nmDataSensitive::setUsesBaseUnits(bool enabled) { // 单位标记随配置保存,避免再次打开时重复转换。 m_usesBaseUnits = enabled; } // === 变量列表 === QList& nmDataSensitive::getVariables() { return m_variables; } const QList& nmDataSensitive::getVariables() const { // 求解任务只读取采样配置,不取得可写参数列表。 return m_variables; } void nmDataSensitive::setVariables(const QList& vars) { m_variables = vars; } int nmDataSensitive::getVariableCount() const { return m_variables.size(); } nmDataSensitive::VariableSampling& nmDataSensitive::getVariable(int index) { return m_variables[index]; }