From e73b5ab45335b00ef7d1961b228ca7f2ec3bf434 Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Fri, 14 Aug 2026 11:23:23 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E5=8E=8B=E8=A3=82=E4=BA=95?= =?UTF-8?q?=E8=A3=82=E7=BC=9D=E5=8D=8A=E9=95=BF=E6=8B=9F=E5=90=88=E5=8F=82?= =?UTF-8?q?=E6=95=B0=20=202.=E5=8F=82=E6=95=B0=E7=89=A9=E7=90=86=E8=BE=B9?= =?UTF-8?q?=E7=95=8C=E7=BB=9F=E4=B8=80=E7=94=A8=E6=A1=86=E6=9E=B6=E4=B8=AD?= =?UTF-8?q?=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bin/Config/Lang/cn/nmNum_cn.qm | Bin 93895 -> 93972 bytes Bin/Config/Lang/cn/nmNum_cn.ts | 4 + .../nmCalculation/nmCalculationAutoFitPSO.h | 11 +- Include/nmNum/nmData/nmDataAutomaticFitting.h | 13 +++ Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h | 1 + .../nmCalculation/nmCalculationAutoFitPSO.cpp | 105 +++++++++++++----- Src/nmNum/nmData/nmDataAutomaticFitting.cpp | 27 +++++ Src/nmNum/nmSubWxs/nmWxAutomaticFitting.cpp | 100 ++++++++++------- 8 files changed, 191 insertions(+), 70 deletions(-) diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index 14a0e5f0d7b69500fcb31946cf2f7bbf3d0a7285..c9e8ed0c65c5f7422395fd6f64d2faf6de240c85 100644 GIT binary patch delta 5605 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 TV(K=%2VLK2Q`~f|WyAjgmn?nV delta 5700 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 Zo#kU9JYD1COVpzUKHf)Fracture conductivity 裂缝导流能力 + + Fracture half length + 裂缝半长 + The minimum value of %1 must be greater than zero for automatic fitting. 自动拟合时,%1 的最小值和初始值必须大于零。 diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h index 748831d..9731259 100644 --- a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h +++ b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h @@ -372,12 +372,13 @@ private: // 参数索引约定: // 0 k 渗透率;1 skin 表皮系数;2 wellboreC 井筒储集; // 3 phi 孔隙度;4 h 储层厚度;5 Ct 综合压缩系数; - // 6 Cf 岩石压缩系数;7 Swi 初始含水饱和度。 + // 6 Cf 岩石压缩系数;7 Swi 初始含水饱和度; + // 8 Dfc 裂缝导流能力;9 fractureHalfLength 裂缝半长。 // m_enabledParamIndices 保存被用户勾选的参数索引,粒子的 position 维度与它一致。 - QVector m_parameterSelected; // 完整 9 个参数是否被用户勾选参与拟合。 - QVector m_parameterLower; // 完整 9 个参数的搜索下界。 - QVector m_parameterUpper; // 完整 9 个参数的搜索上界。 - QVector m_enabledParamIndices; // 被勾选参数在完整 9 维体系中的索引。 + QVector m_parameterSelected; // 完整 10 个参数是否被用户勾选参与拟合。 + QVector m_parameterLower; // 完整 10 个参数的搜索下界。 + QVector m_parameterUpper; // 完整 10 个参数的搜索上界。 + QVector m_enabledParamIndices; // 被勾选参数在完整 10 维体系中的索引。 QVector > m_targetLogLogData; // 目标井 history log-log 曲线:time/pressure/derivative。 QString m_targetWellName; // 目标井名称;读写井参数和读取模拟曲线都依赖它。 diff --git a/Include/nmNum/nmData/nmDataAutomaticFitting.h b/Include/nmNum/nmData/nmDataAutomaticFitting.h index 18bc851..98829eb 100644 --- a/Include/nmNum/nmData/nmDataAutomaticFitting.h +++ b/Include/nmNum/nmData/nmDataAutomaticFitting.h @@ -82,6 +82,13 @@ public: nmDataAttribute& getFractureConductivityMin(); void setFractureConductivityMin(const nmDataAttribute& fractureConductivityMin); + // Getter and Setter for fractureHalfLengthMax + nmDataAttribute& getFractureHalfLengthMax(); + void setFractureHalfLengthMax(const nmDataAttribute& fractureHalfLengthMax); + // Getter and Setter for fractureHalfLengthMin + nmDataAttribute& getFractureHalfLengthMin(); + void setFractureHalfLengthMin(const nmDataAttribute& fractureHalfLengthMin); + // Getter and Setter for iteration count nmDataAttribute& getIterationCount(); void setIterationCount(const nmDataAttribute& iterationCount); @@ -124,6 +131,9 @@ public: bool getFractureConductivitySelected() const; void setFractureConductivitySelected(bool selected); + bool getFractureHalfLengthSelected() const; + void setFractureHalfLengthSelected(bool selected); + private: // 参数最大值 nmDataAttribute m_permeabilityMax; @@ -135,6 +145,7 @@ private: nmDataAttribute m_cfMax; nmDataAttribute m_swiMax; nmDataAttribute m_fractureConductivityMax; + nmDataAttribute m_fractureHalfLengthMax; // 参数最小值 nmDataAttribute m_permeabilityMin; @@ -146,6 +157,7 @@ private: nmDataAttribute m_cfMin; nmDataAttribute m_swiMin; nmDataAttribute m_fractureConductivityMin; + nmDataAttribute m_fractureHalfLengthMin; // 迭代参数 nmDataAttribute m_iterationCount; // 迭代步数 @@ -163,6 +175,7 @@ private: bool m_cfSelected; // 是否选择岩石压缩系数进行拟合 bool m_swiSelected; // 是否选择初始含水饱和度进行拟合 bool m_fractureConductivitySelected; // 是否选择裂缝导流能力进行拟合 + bool m_fractureHalfLengthSelected; // 是否选择裂缝半长进行拟合 }; #endif // NMDATAAUTOMATICFITTING_H diff --git a/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h b/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h index ada66d0..a8c4f76 100644 --- a/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h +++ b/Include/nmNum/nmSubWxs/nmWxAutomaticFitting.h @@ -92,6 +92,7 @@ private: QCheckBox* m_cfCheckBox; // 岩石压缩系数 QCheckBox* m_swiCheckBox; // 初始含水饱和度 QCheckBox* m_dfcCheckBox; // 裂缝导流能力 + QCheckBox* m_fractureHalfLengthCheckBox; // 裂缝半长 // 按钮 QPushButton* m_reverseBtn; diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp index 7378599..0f37602 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp @@ -432,7 +432,7 @@ static QString findExecutableInPath(const QString& executableName) static QStringList traceParameterNames() { // trace 和 trace meta 使用的完整参数名顺序。 - // 这个顺序必须与 buildTraceParameterVector() 和 m_parameterSelected 的 0-8 索引一致。 + // 这个顺序必须与 buildTraceParameterVector() 和 m_parameterSelected 的 0-9 索引一致。 QStringList names; names << "k" << "skin" @@ -442,7 +442,8 @@ static QStringList traceParameterNames() << "Ct" << "Cf" << "Swi" - << "Dfc"; + << "Dfc" + << "fractureHalfLength"; return names; } @@ -1013,7 +1014,7 @@ void nmCalculationAutoFitPSO::writeTraceHeader() { // trace CSV 字段说明: // - 代理模式保持原 k/skin/wellboreC/phi/h/Ct/Cf 契约; - // - 非代理模式额外记录 Swi/Dfc,便于复盘信赖域对两项参数的调整; + // - 非代理模式额外记录 Swi/Dfc/裂缝半长,便于复盘信赖域调整; // - solver_objective 是真实求解器误差; // - surrogate_objective 是 Python 代理评分; // - screening_decision 说明该粒子为什么跑/不跑真实求解器; @@ -1037,7 +1038,8 @@ void nmCalculationAutoFitPSO::writeTraceHeader() << "Cf"; if(!isSurrogateScreeningEnabled()) { cols << "Swi" - << "Dfc"; + << "Dfc" + << "fractureHalfLength"; } cols << "solver_objective" << "solver_success" @@ -1054,7 +1056,8 @@ void nmCalculationAutoFitPSO::writeTraceHeader() << "pbest_Cf"; if(!isSurrogateScreeningEnabled()) { cols << "pbest_Swi" - << "pbest_Dfc"; + << "pbest_Dfc" + << "pbest_fractureHalfLength"; } cols << "gbest_objective" << "gbest_k" @@ -1066,7 +1069,8 @@ void nmCalculationAutoFitPSO::writeTraceHeader() << "gbest_Cf"; if(!isSurrogateScreeningEnabled()) { cols << "gbest_Swi" - << "gbest_Dfc"; + << "gbest_Dfc" + << "gbest_fractureHalfLength"; } cols << "enabled_param_indices" << "pressure_loss" @@ -1132,7 +1136,7 @@ void nmCalculationAutoFitPSO::writeTraceMetaFile() // 非代理 v5 增加带符号诊断列;代理 PSO 保留原 v3 字段和目标,避免改变 // 已有模型的训练和回放契约。 out << " \"schema_version\": " - << (isSurrogateScreeningEnabled() ? 3 : 6) << ",\n"; + << (isSurrogateScreeningEnabled() ? 3 : 7) << ",\n"; out << " \"trace_type\": " << jsonEscape(isSurrogateScreeningEnabled() ? "pso_baseline_replay_meta" @@ -1193,10 +1197,10 @@ void nmCalculationAutoFitPSO::writeTraceMetaFile() QVector nmCalculationAutoFitPSO::buildTraceParameterVector(const QVector& selectedParameters) const { - // 将粒子内部使用的“启用参数向量”还原成完整 9 维参数向量。 + // 将粒子内部使用的“启用参数向量”还原成完整 10 维参数向量。 // 未启用的参数从当前 DataManager 读取,启用的参数用 selectedParameters 覆盖。 // trace CSV、候选 CSV、代理训练域检查都需要这个完整向量。 - QVector fullParams(9, 0.0); + QVector fullParams(10, 0.0); nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance(); @@ -1224,12 +1228,14 @@ QVector nmCalculationAutoFitPSO::buildTraceParameterVector(const QVector dynamic_cast(pTargetWell); if(fracturedWell) { fullParams[8] = fracturedWell->getDfc().getValue().toDouble(); + fullParams[9] = fracturedWell->getFractureHalfLength().getValue().toDouble(); } } else if(pTargetWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) { nmDataHorizontalFracturedWell* fracturedWell = dynamic_cast(pTargetWell); if(fracturedWell) { fullParams[8] = fracturedWell->getDfc().getValue().toDouble(); + fullParams[9] = fracturedWell->getFractureHalfLength().getValue().toDouble(); } } } @@ -1290,7 +1296,8 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation, << traceParamAt(currentParams, 6); if(!isSurrogateScreeningEnabled()) { cols << traceParamAt(currentParams, 7) - << traceParamAt(currentParams, 8); + << traceParamAt(currentParams, 8) + << traceParamAt(currentParams, 9); } cols << traceNumber(solverObjective) << QString::number(solverSuccess ? 1 : 0) @@ -1307,7 +1314,8 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation, << traceParamAt(pbestParams, 6); if(!isSurrogateScreeningEnabled()) { cols << traceParamAt(pbestParams, 7) - << traceParamAt(pbestParams, 8); + << traceParamAt(pbestParams, 8) + << traceParamAt(pbestParams, 9); } cols << traceNumber(m_globalBestFitness) << traceParamAt(gbestParams, 0) @@ -1319,7 +1327,8 @@ void nmCalculationAutoFitPSO::writeTraceRow(int generation, << traceParamAt(gbestParams, 6); if(!isSurrogateScreeningEnabled()) { cols << traceParamAt(gbestParams, 7) - << traceParamAt(gbestParams, 8); + << traceParamAt(gbestParams, 8) + << traceParamAt(gbestParams, 9); } cols << csvEscape(enabledIndices.join(";")); @@ -2917,14 +2926,14 @@ void nmCalculationAutoFitPSO::loadParameterBounds() // 读取用户勾选的拟合参数及上下界。 // // 这里构建三个核心数组: - // - m_parameterSelected[9]:完整参数体系中每个参数是否参与拟合; - // - m_parameterLower/Upper[9]:完整参数体系的搜索上下界; + // - m_parameterSelected[10]:完整参数体系中每个参数是否参与拟合; + // - m_parameterLower/Upper[10]:完整参数体系的搜索上下界; // - m_enabledParamIndices:把粒子内部紧凑向量映射回完整参数索引。 nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance(); nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy(); // 获取参数选择状态 - m_parameterSelected.resize(9); + m_parameterSelected.resize(10); m_parameterSelected[0] = fittingData.getPermeabilitySelected(); m_parameterSelected[1] = fittingData.getSkinSelected(); m_parameterSelected[2] = fittingData.getWellboreStorageSelected(); @@ -2934,10 +2943,11 @@ void nmCalculationAutoFitPSO::loadParameterBounds() m_parameterSelected[6] = fittingData.getCfSelected(); m_parameterSelected[7] = fittingData.getSwiSelected(); m_parameterSelected[8] = fittingData.getFractureConductivitySelected(); + m_parameterSelected[9] = fittingData.getFractureHalfLengthSelected(); // 获取参数边界 - m_parameterLower.resize(9); - m_parameterUpper.resize(9); + m_parameterLower.resize(10); + m_parameterUpper.resize(10); m_parameterLower[0] = fittingData.getPermeabilityMin().getValue().toDouble(); m_parameterUpper[0] = fittingData.getPermeabilityMax().getValue().toDouble(); @@ -2966,6 +2976,9 @@ void nmCalculationAutoFitPSO::loadParameterBounds() m_parameterLower[8] = fittingData.getFractureConductivityMin().getValue().toDouble(); m_parameterUpper[8] = fittingData.getFractureConductivityMax().getValue().toDouble(); + m_parameterLower[9] = fittingData.getFractureHalfLengthMin().getValue().toDouble(); + m_parameterUpper[9] = fittingData.getFractureHalfLengthMax().getValue().toDouble(); + // 更新启用参数索引 m_enabledParamIndices.clear(); @@ -3474,11 +3487,14 @@ bool nmCalculationAutoFitPSO::startAutoFitting() applyParametersToDataManager(m_globalBestPosition); // 即使用户此时停止、不再执行最终完整计算,也要把 PEBI 缓存恢复为 - // 最终已接受的 Dfc,避免缓存仍停留在最后一个被拒绝的候选值。 - if(m_parameterSelected.size() > 8 && m_parameterSelected[8]) { + // 最终已接受的裂缝参数,避免缓存仍停留在最后一个被拒绝的候选值。 + const bool fractureGridParameterSelected = + (m_parameterSelected.size() > 8 && m_parameterSelected[8]) || + (m_parameterSelected.size() > 9 && m_parameterSelected[9]); + if(fractureGridParameterSelected) { nmCalculationPebiGrid* pebiGrid = nmCalculationPebiGrid::getInstance(); if(!pebiGrid || !pebiGrid->generateOutputPara()) { - throw std::runtime_error("Failed to refresh final fracture conductivity"); + throw std::runtime_error("Failed to refresh final fracture parameters"); } } @@ -3711,6 +3727,22 @@ void nmCalculationAutoFitPSO::extractUserInitialValues() } } break; + + case 9: // 裂缝半长 + if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) { + nmDataVerticalFracturedWell* fracturedWell = + dynamic_cast(pTargetWell); + if(fracturedWell) { + initialValue = fracturedWell->getFractureHalfLength().getValue().toDouble(); + } + } else if(pTargetWell && pTargetWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) { + nmDataHorizontalFracturedWell* fracturedWell = + dynamic_cast(pTargetWell); + if(fracturedWell) { + initialValue = fracturedWell->getFractureHalfLength().getValue().toDouble(); + } + } + break; } m_initialValues.append(initialValue); @@ -5534,12 +5566,15 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter return 1e10; } - // Dfc 位于 PEBI 裂缝数组 crack[5],不是每次求解都会重新组装的 Base/CS 参数。 - // 因此只有勾选 Dfc 时才刷新一次网格输出参数,保证本次真实试算使用刚写入井对象的值。 - if(m_parameterSelected.size() > 8 && m_parameterSelected[8]) { + // Dfc 和裂缝半长都通过 PEBI 裂缝数组传入,不属于每次求解都会重新组装的 Base/CS 参数。 + // 勾选任一裂缝参数时刷新网格输出,保证本次真实试算使用新的导流能力和端点坐标。 + const bool fractureGridParameterSelected = + (m_parameterSelected.size() > 8 && m_parameterSelected[8]) || + (m_parameterSelected.size() > 9 && m_parameterSelected[9]); + if(fractureGridParameterSelected) { nmCalculationPebiGrid* pebiGrid = nmCalculationPebiGrid::getInstance(); if(!pebiGrid || !pebiGrid->generateOutputPara()) { - DEBUG_OUT(QString("%1: Call #%2 - Failed to refresh PEBI fracture conductivity") + DEBUG_OUT(QString("%1: Call #%2 - Failed to refresh PEBI fracture parameters") .arg(funcName).arg(callCount)); return 1e10; } @@ -5707,7 +5742,7 @@ void nmCalculationAutoFitPSO::updateReservoirParameters(const QVector& p nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance(); nmDataReservoir reservoirData = dataManager->getReservoirDataCopy(); - // paramIndex 是粒子 position 中的索引;i 是完整 9 个参数体系中的索引。 + // paramIndex 是粒子 position 中的索引;i 是完整 10 个参数体系中的索引。 // 只有 m_parameterSelected[i] 为 true 时,才从 parameters 中消费一个值。 int paramIndex = 0; @@ -5754,7 +5789,7 @@ void nmCalculationAutoFitPSO::updateWellParameters(const QVector& parame // 更新目标井上的拟合参数。目前井级可拟合参数主要是: // - skin:写入第一个 perforation; // - wellboreC:写入井筒储集系数; - // - Dfc:只写入垂直压裂井或多段压裂水平井的裂缝导流能力。 + // - Dfc/裂缝半长:只写入垂直压裂井或多段压裂水平井。 // 如果目标井不存在或没有射孔数据,这里只记录 debug,不抛异常。 nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance(); @@ -5811,6 +5846,24 @@ void nmCalculationAutoFitPSO::updateWellParameters(const QVector& parame } } break; + + case 9: { // 裂缝半长 + // 直接修改井对象中的属性,复用已有信号重算裂缝端点。 + if(pWell->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) { + nmDataVerticalFracturedWell* fracturedWell = + dynamic_cast(pWell); + if(fracturedWell) { + fracturedWell->getFractureHalfLength().setValue(value); + } + } else if(pWell->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) { + nmDataHorizontalFracturedWell* fracturedWell = + dynamic_cast(pWell); + if(fracturedWell) { + fracturedWell->getFractureHalfLength().setValue(value); + } + } + } + break; } paramIndex++; diff --git a/Src/nmNum/nmData/nmDataAutomaticFitting.cpp b/Src/nmNum/nmData/nmDataAutomaticFitting.cpp index a7a3462..af01ce0 100644 --- a/Src/nmNum/nmData/nmDataAutomaticFitting.cpp +++ b/Src/nmNum/nmData/nmDataAutomaticFitting.cpp @@ -12,6 +12,7 @@ nmDataAutomaticFitting::nmDataAutomaticFitting() m_cfSelected = false; // 默认不选中 m_swiSelected = false; // 默认不选中 m_fractureConductivitySelected = false; // 仅压裂井可用,默认不选中 + m_fractureHalfLengthSelected = false; // 仅压裂井可用,默认不选中 // 拟合上下界不再使用固定默认值,由自动拟合窗口按数据对象初值和物理边界生成。 m_permeabilityMax = nmDataAttribute("Permeability Max", QVariant(), "Darcy"); @@ -23,6 +24,7 @@ nmDataAutomaticFitting::nmDataAutomaticFitting() m_cfMax = nmDataAttribute("Cf Max", QVariant(), ""); m_swiMax = nmDataAttribute("Swi Max", QVariant(), ""); m_fractureConductivityMax = nmDataAttribute("Fracture Conductivity Max", QVariant(), "md.m"); + m_fractureHalfLengthMax = nmDataAttribute("Fracture Half Length Max", QVariant(), "m"); m_permeabilityMin = nmDataAttribute("Permeability Min", QVariant(), "Darcy"); m_skinMin = nmDataAttribute("Skin Min", QVariant(), ""); @@ -33,6 +35,7 @@ nmDataAutomaticFitting::nmDataAutomaticFitting() m_cfMin = nmDataAttribute("Cf Min", QVariant(), ""); m_swiMin = nmDataAttribute("Swi Min", QVariant(), ""); m_fractureConductivityMin = nmDataAttribute("Fracture Conductivity Min", QVariant(), "md.m"); + m_fractureHalfLengthMin = nmDataAttribute("Fracture Half Length Min", QVariant(), "m"); // 初始化迭代参数 m_iterationCount = nmDataAttribute("Iteration Count", 20, ""); @@ -63,6 +66,7 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF m_cfSelected = other.m_cfSelected; m_swiSelected = other.m_swiSelected; m_fractureConductivitySelected = other.m_fractureConductivitySelected; + m_fractureHalfLengthSelected = other.m_fractureHalfLengthSelected; // 复制参数最大值 m_permeabilityMax = other.m_permeabilityMax; @@ -74,6 +78,7 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF m_cfMax = other.m_cfMax; m_swiMax = other.m_swiMax; m_fractureConductivityMax = other.m_fractureConductivityMax; + m_fractureHalfLengthMax = other.m_fractureHalfLengthMax; // 复制参数最小值 m_permeabilityMin = other.m_permeabilityMin; @@ -85,6 +90,7 @@ nmDataAutomaticFitting& nmDataAutomaticFitting::operator=(const nmDataAutomaticF m_cfMin = other.m_cfMin; m_swiMin = other.m_swiMin; m_fractureConductivityMin = other.m_fractureConductivityMin; + m_fractureHalfLengthMin = other.m_fractureHalfLengthMin; // 复制迭代参数 m_iterationCount = other.m_iterationCount; @@ -109,6 +115,7 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca fittingObject.AddMember("CfSelected", m_cfSelected, allocator); fittingObject.AddMember("SwiSelected", m_swiSelected, allocator); fittingObject.AddMember("FractureConductivitySelected", m_fractureConductivitySelected, allocator); + fittingObject.AddMember("FractureHalfLengthSelected", m_fractureHalfLengthSelected, allocator); // 序列化参数最大值 fittingObject.AddMember("PermeabilityMax", m_permeabilityMax.ToJsonValue(allocator), allocator); @@ -120,6 +127,7 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca fittingObject.AddMember("CfMax", m_cfMax.ToJsonValue(allocator), allocator); fittingObject.AddMember("SwiMax", m_swiMax.ToJsonValue(allocator), allocator); fittingObject.AddMember("FractureConductivityMax", m_fractureConductivityMax.ToJsonValue(allocator), allocator); + fittingObject.AddMember("FractureHalfLengthMax", m_fractureHalfLengthMax.ToJsonValue(allocator), allocator); // 序列化参数最小值 fittingObject.AddMember("PermeabilityMin", m_permeabilityMin.ToJsonValue(allocator), allocator); @@ -131,6 +139,7 @@ rapidjson::Value nmDataAutomaticFitting::ToJsonValue(rapidjson::Document::Alloca fittingObject.AddMember("CfMin", m_cfMin.ToJsonValue(allocator), allocator); fittingObject.AddMember("SwiMin", m_swiMin.ToJsonValue(allocator), allocator); fittingObject.AddMember("FractureConductivityMin", m_fractureConductivityMin.ToJsonValue(allocator), allocator); + fittingObject.AddMember("FractureHalfLengthMin", m_fractureHalfLengthMin.ToJsonValue(allocator), allocator); // 序列化迭代参数 fittingObject.AddMember("IterationCount", m_iterationCount.ToJsonValue(allocator), allocator); @@ -171,6 +180,9 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue) if (jsonValue.HasMember("FractureConductivitySelected") && jsonValue["FractureConductivitySelected"].IsBool()) { m_fractureConductivitySelected = jsonValue["FractureConductivitySelected"].GetBool(); } + if (jsonValue.HasMember("FractureHalfLengthSelected") && jsonValue["FractureHalfLengthSelected"].IsBool()) { + m_fractureHalfLengthSelected = jsonValue["FractureHalfLengthSelected"].GetBool(); + } // 反序列化参数最大值 if (jsonValue.HasMember("PermeabilityMax") && jsonValue["PermeabilityMax"].IsObject()) { @@ -200,6 +212,9 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue) if (jsonValue.HasMember("FractureConductivityMax") && jsonValue["FractureConductivityMax"].IsObject()) { m_fractureConductivityMax.FromJsonValue(jsonValue["FractureConductivityMax"]); } + if (jsonValue.HasMember("FractureHalfLengthMax") && jsonValue["FractureHalfLengthMax"].IsObject()) { + m_fractureHalfLengthMax.FromJsonValue(jsonValue["FractureHalfLengthMax"]); + } // 反序列化参数最小值 if (jsonValue.HasMember("PermeabilityMin") && jsonValue["PermeabilityMin"].IsObject()) { @@ -229,6 +244,9 @@ void nmDataAutomaticFitting::FromJsonValue(const rapidjson::Value& jsonValue) if (jsonValue.HasMember("FractureConductivityMin") && jsonValue["FractureConductivityMin"].IsObject()) { m_fractureConductivityMin.FromJsonValue(jsonValue["FractureConductivityMin"]); } + if (jsonValue.HasMember("FractureHalfLengthMin") && jsonValue["FractureHalfLengthMin"].IsObject()) { + m_fractureHalfLengthMin.FromJsonValue(jsonValue["FractureHalfLengthMin"]); + } // 反序列化迭代参数 if (jsonValue.HasMember("IterationCount") && jsonValue["IterationCount"].IsObject()) { @@ -274,6 +292,9 @@ void nmDataAutomaticFitting::setSwiSelected(bool selected) { m_swiSelected = sel bool nmDataAutomaticFitting::getFractureConductivitySelected() const { return m_fractureConductivitySelected; } void nmDataAutomaticFitting::setFractureConductivitySelected(bool selected) { m_fractureConductivitySelected = selected; } +bool nmDataAutomaticFitting::getFractureHalfLengthSelected() const { return m_fractureHalfLengthSelected; } +void nmDataAutomaticFitting::setFractureHalfLengthSelected(bool selected) { m_fractureHalfLengthSelected = selected; } + // Getter and Setter implementations for Max values nmDataAttribute& nmDataAutomaticFitting::getPermeabilityMax() { return m_permeabilityMax; } @@ -303,6 +324,9 @@ void nmDataAutomaticFitting::setSwiMax(const nmDataAttribute& swiMax) { m_swiMax nmDataAttribute& nmDataAutomaticFitting::getFractureConductivityMax() { return m_fractureConductivityMax; } void nmDataAutomaticFitting::setFractureConductivityMax(const nmDataAttribute& fractureConductivityMax) { m_fractureConductivityMax = fractureConductivityMax; } +nmDataAttribute& nmDataAutomaticFitting::getFractureHalfLengthMax() { return m_fractureHalfLengthMax; } +void nmDataAutomaticFitting::setFractureHalfLengthMax(const nmDataAttribute& fractureHalfLengthMax) { m_fractureHalfLengthMax = fractureHalfLengthMax; } + // Getter and Setter implementations for Min values nmDataAttribute& nmDataAutomaticFitting::getPermeabilityMin() { return m_permeabilityMin; } void nmDataAutomaticFitting::setPermeabilityMin(const nmDataAttribute& permeabilityMin) { m_permeabilityMin = permeabilityMin; } @@ -331,6 +355,9 @@ void nmDataAutomaticFitting::setSwiMin(const nmDataAttribute& swiMin) { m_swiMin nmDataAttribute& nmDataAutomaticFitting::getFractureConductivityMin() { return m_fractureConductivityMin; } void nmDataAutomaticFitting::setFractureConductivityMin(const nmDataAttribute& fractureConductivityMin) { m_fractureConductivityMin = fractureConductivityMin; } +nmDataAttribute& nmDataAutomaticFitting::getFractureHalfLengthMin() { return m_fractureHalfLengthMin; } +void nmDataAutomaticFitting::setFractureHalfLengthMin(const nmDataAttribute& fractureHalfLengthMin) { m_fractureHalfLengthMin = fractureHalfLengthMin; } + // Getter and Setter implementations for iteration parameters nmDataAttribute& nmDataAutomaticFitting::getIterationCount() { return m_iterationCount; } void nmDataAutomaticFitting::setIterationCount(const nmDataAttribute& iterationCount) { m_iterationCount = iterationCount; } diff --git a/Src/nmNum/nmSubWxs/nmWxAutomaticFitting.cpp b/Src/nmNum/nmSubWxs/nmWxAutomaticFitting.cpp index 34415d0..6bd7312 100644 --- a/Src/nmNum/nmSubWxs/nmWxAutomaticFitting.cpp +++ b/Src/nmNum/nmSubWxs/nmWxAutomaticFitting.cpp @@ -43,13 +43,10 @@ bool nmAutoFitUiNearlyEqual(double left, double right) return std::fabs(left - right) <= DBL_EPSILON * 8.0 * scale; } -// 从系统参数表读取物理边界,读取失败时保留调用方提供的兜底边界。 +// 从系统参数表读取物理边界,所有自动拟合参数统一以参数表配置为准。 bool nmAutoFitReadPhysicalRange(const char* parameterName, - double fallbackMin, double fallbackMax, double& minValue, double& maxValue) + double& minValue, double& maxValue) { - minValue = fallbackMin; - maxValue = fallbackMax; - iSysParaHelper* paraHelper = _paraHelper; if(!paraHelper) { return false; @@ -245,6 +242,7 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL } } setParameterRowVisible(table, 8, showFractureConductivity); // Dfc + setParameterRowVisible(table, 9, showFractureConductivity); // 裂缝半长 renumberVisibleParameterRows(table); } @@ -255,29 +253,15 @@ bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex, { static const char* parameterNames[] = { "Result_K", "Result_W_Skin", "Result_W_C", "Result_phi", - "Result_h", "Result_Cti", "Result_Cf", "Result_Swi", "Result_W_Dfc" - }; - // KAPPA 的边界使用 md、ft、bbl/psi;自动拟合界面使用 Darcy、m、m^3/MPa, - // 这里统一换算到界面和 PSO 实际使用的单位:K 除以 1000,h 由 ft 换成 m, - // 井筒储集系数的 4.33667154546306e34 bbl/psi 对应约 1e36 m^3/MPa。 - // Ct/Cf/Swi/Dfc 沿用模型参数表边界。 - static const double physicalMin[] = { - 1.01325027383089e-18, -5.0, 0.0, 1.0e-4, 1.0e-5, 1.0e-30, 1.0e-30, 0.0, 0.0 - }; - static const double physicalMax[] = { - 1.01325027383089e42, 5000.0, 1.0e36, 0.9999, 1.0e9, 10.0, 10.0, 1.0, 1.0e30 + "Result_h", "Result_Cti", "Result_Cf", "Result_Swi", "Result_W_Dfc", + "W_FractureHalfLength" }; - - if(parameterIndex < 0 || parameterIndex >= 9) { + if(parameterIndex < 0 || parameterIndex >= 10) { return false; } - minValue = physicalMin[parameterIndex]; - maxValue = physicalMax[parameterIndex]; - bool rangeRead = true; - if(parameterIndex >= 5) { - rangeRead = nmAutoFitReadPhysicalRange(parameterNames[parameterIndex], - physicalMin[parameterIndex], physicalMax[parameterIndex], minValue, maxValue); + if(!nmAutoFitReadPhysicalRange(parameterNames[parameterIndex], minValue, maxValue)) { + return false; } if(parameterIndex == 7) { @@ -294,7 +278,7 @@ bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex, } } - return rangeRead; + return true; } // 将一组上下界同步到表格和自动拟合数据,保证 PSO 读取到同一份配置。 @@ -357,6 +341,10 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex, automaticFittingData.getFractureConductivityMin().setValue(minValue); automaticFittingData.getFractureConductivityMax().setValue(maxValue); break; + case 9: + automaticFittingData.getFractureHalfLengthMin().setValue(minValue); + automaticFittingData.getFractureHalfLengthMax().setValue(maxValue); + break; default: break; } @@ -369,14 +357,16 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex, void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex, double centerValue) { - if(!m_parameterTable || parameterIndex < 0 || parameterIndex >= 9 + if(!m_parameterTable || parameterIndex < 0 || parameterIndex >= 10 || !nmAutoFitUiIsFinite(centerValue)) { return; } double physicalMin = 0.0; double physicalMax = 0.0; - getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax); + if(!getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax)) { + return; + } double reference = centerValue; const bool positiveParameter = parameterIndex != 1; @@ -437,7 +427,7 @@ void nmWxAutomaticFitting::initializeSuggestedParameterRanges() return; } - for(int parameterIndex = 0; parameterIndex < 9; ++parameterIndex) { + for(int parameterIndex = 0; parameterIndex < 10; ++parameterIndex) { QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3); if(initialItem) { bool initialOk = false; @@ -448,8 +438,8 @@ void nmWxAutomaticFitting::initializeSuggestedParameterRanges() // 数据对象没有提供该初值时使用完整物理区间,不回退到旧的默认范围。 double physicalMin = 0.0; double physicalMax = 0.0; - getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax); - if(physicalMax >= physicalMin) { + if(getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax) + && physicalMax >= physicalMin) { setParameterRange(parameterIndex, physicalMin, physicalMax); } } @@ -464,7 +454,7 @@ void nmWxAutomaticFitting::normalizeSavedParameterRanges() return; } - for(int parameterIndex = 0; parameterIndex < 9; ++parameterIndex) { + for(int parameterIndex = 0; parameterIndex < 10; ++parameterIndex) { QTableWidgetItem* minItem = m_parameterTable->item(parameterIndex, 2); QTableWidgetItem* maxItem = m_parameterTable->item(parameterIndex, 4); QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3); @@ -474,7 +464,9 @@ void nmWxAutomaticFitting::normalizeSavedParameterRanges() double physicalMin = 0.0; double physicalMax = 0.0; - getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax); + if(!getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax)) { + continue; + } bool savedMinOk = false; bool savedMaxOk = false; const double savedMin = minItem->text().toDouble(&savedMinOk); @@ -513,18 +505,19 @@ bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int par errorMessage = tr("The parameter table is unavailable."); return false; } - if(parameterIndex < -1 || parameterIndex >= 9) { + if(parameterIndex < -1 || parameterIndex >= 10) { errorMessage = tr("The parameter row is invalid."); return false; } static const char* parameterNames[] = { "Permeability", "Skin", "Wellbore storage", "Porosity", - "Thickness", "Ct", "Cf", "Swi", "Fracture conductivity" + "Thickness", "Ct", "Cf", "Swi", "Fracture conductivity", + "Fracture half length" }; const int firstParameterIndex = parameterIndex < 0 ? 0 : parameterIndex; - const int lastParameterIndex = parameterIndex < 0 ? 9 : parameterIndex + 1; + const int lastParameterIndex = parameterIndex < 0 ? 10 : parameterIndex + 1; for(int currentParameterIndex = firstParameterIndex; currentParameterIndex < lastParameterIndex; ++currentParameterIndex) { // 隐藏参数不参与当前模型拟合,不用它们的历史值阻塞当前设置。 @@ -558,8 +551,8 @@ bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int par double physicalMin = 0.0; double physicalMax = 0.0; - getPhysicalParameterRange(currentParameterIndex, physicalMin, physicalMax); - if(!nmAutoFitUiIsFinite(physicalMin) || !nmAutoFitUiIsFinite(physicalMax) + if(!getPhysicalParameterRange(currentParameterIndex, physicalMin, physicalMax) + || !nmAutoFitUiIsFinite(physicalMin) || !nmAutoFitUiIsFinite(physicalMax) || physicalMax < physicalMin) { errorMessage = tr("The physical range of %1 is invalid.") .arg(tr(parameterNames[currentParameterIndex])); @@ -717,7 +710,7 @@ void nmWxAutomaticFitting::setupUI() void nmWxAutomaticFitting::setupParameterTable() { // 创建表格 - m_parameterTable = new QTableWidget(9, 6, this); + m_parameterTable = new QTableWidget(10, 6, this); // 设置表头 QStringList headers; @@ -837,6 +830,16 @@ void nmWxAutomaticFitting::setupParameterTable() m_parameterTable->setItem(8, 4, new QTableWidgetItem(QString::number(automaticFittingData.getFractureConductivityMax().getValue().toDouble()))); m_parameterTable->setItem(8, 5, new QTableWidgetItem(tr("md.m"))); + // 裂缝半长。该行与 Dfc 一样只对压裂井显示,初值从当前目标井读取。 + m_parameterTable->setItem(9, 0, new QTableWidgetItem("10")); + m_fractureHalfLengthCheckBox = new QCheckBox(tr("Fracture half length")); + m_fractureHalfLengthCheckBox->setChecked(automaticFittingData.getFractureHalfLengthSelected()); + m_parameterTable->setCellWidget(9, 1, m_fractureHalfLengthCheckBox); + m_parameterTable->setItem(9, 2, new QTableWidgetItem(QString::number(automaticFittingData.getFractureHalfLengthMin().getValue().toDouble()))); + m_parameterTable->setItem(9, 3, new QTableWidgetItem()); + m_parameterTable->setItem(9, 4, new QTableWidgetItem(QString::number(automaticFittingData.getFractureHalfLengthMax().getValue().toDouble()))); + m_parameterTable->setItem(9, 5, new QTableWidgetItem(tr("m"))); + // 设置表格行为 for(int i = 0; i < m_parameterTable->rowCount(); ++i) { for(int j = 0; j < 6; ++j) { @@ -1037,6 +1040,7 @@ void nmWxAutomaticFitting::onReverseSelection() if(!m_parameterTable->isRowHidden(6)) m_cfCheckBox->setChecked(!m_cfCheckBox->isChecked()); if(!m_parameterTable->isRowHidden(7)) m_swiCheckBox->setChecked(!m_swiCheckBox->isChecked()); if(!m_parameterTable->isRowHidden(8)) m_dfcCheckBox->setChecked(!m_dfcCheckBox->isChecked()); + if(!m_parameterTable->isRowHidden(9)) m_fractureHalfLengthCheckBox->setChecked(!m_fractureHalfLengthCheckBox->isChecked()); } void nmWxAutomaticFitting::onParameterTableItemChanged(QTableWidgetItem* item) @@ -1155,7 +1159,8 @@ void nmWxAutomaticFitting::onAccept() m_cCheckBox->isChecked() || m_phiCheckBox->isChecked() || m_hCheckBox->isChecked() || m_ctCheckBox->isChecked() || m_cfCheckBox->isChecked() || - m_swiCheckBox->isChecked() || m_dfcCheckBox->isChecked(); + m_swiCheckBox->isChecked() || m_dfcCheckBox->isChecked() || + m_fractureHalfLengthCheckBox->isChecked(); if(!hasSelectedParams) { QMessageBox::warning(this, tr("Warning"), tr("Please select at least one parameter for optimization!")); @@ -1174,6 +1179,7 @@ void nmWxAutomaticFitting::onAccept() if(m_cfCheckBox->isChecked()) selectedParameterNames << tr("Cf"); if(m_swiCheckBox->isChecked()) selectedParameterNames << tr("Swi"); if(m_dfcCheckBox->isChecked()) selectedParameterNames << tr("Fracture conductivity"); + if(m_fractureHalfLengthCheckBox->isChecked()) selectedParameterNames << tr("Fracture half length"); // 启动自动拟合 - 传递双对数历史数据 startAutoFitting(targetLogLogData, selectedParameterNames, selectedWellName); @@ -1194,6 +1200,7 @@ void nmWxAutomaticFitting::onWellSelected(int index) double skinValue = 0.0; double wellboreStorageValue = 0.0; double fractureConductivityValue = 0.0; + double fractureHalfLengthValue = 0.0; // 查找垂直井 for(int i = 0; i < m_verticalWells.size(); ++i) { @@ -1224,6 +1231,7 @@ void nmWxAutomaticFitting::onWellSelected(int index) skinValue = m_verticalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble(); wellboreStorageValue = m_verticalFracturedWells[i].getWellboreStorage().getValue().toDouble(); fractureConductivityValue = m_verticalFracturedWells[i].getDfc().getValue().toDouble(); + fractureHalfLengthValue = m_verticalFracturedWells[i].getFractureHalfLength().getValue().toDouble(); fracturedWell = true; found = true; break; @@ -1238,6 +1246,7 @@ void nmWxAutomaticFitting::onWellSelected(int index) skinValue = m_horizontalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble(); wellboreStorageValue = m_horizontalFracturedWells[i].getWellboreStorage().getValue().toDouble(); fractureConductivityValue = m_horizontalFracturedWells[i].getDfc().getValue().toDouble(); + fractureHalfLengthValue = m_horizontalFracturedWells[i].getFractureHalfLength().getValue().toDouble(); fracturedWell = true; found = true; break; @@ -1268,12 +1277,16 @@ void nmWxAutomaticFitting::onWellSelected(int index) if(fracturedWell && m_parameterTable->item(8, 3)) { m_parameterTable->item(8, 3)->setText(QString::number(fractureConductivityValue)); } + if(fracturedWell && m_parameterTable->item(9, 3)) { + m_parameterTable->item(9, 3)->setText(QString::number(fractureHalfLengthValue)); + } if(m_autoParameterRanges) { updateRangeForParameter(1, skinValue); updateRangeForParameter(2, wellboreStorageValue); if(fracturedWell) { updateRangeForParameter(8, fractureConductivityValue); + updateRangeForParameter(9, fractureHalfLengthValue); } } } @@ -1291,6 +1304,7 @@ void nmWxAutomaticFitting::setAutomaticFittingValue() automaticFittingData.setCfSelected(m_cfCheckBox->isChecked()); automaticFittingData.setSwiSelected(m_swiCheckBox->isChecked()); automaticFittingData.setFractureConductivitySelected(m_dfcCheckBox->isChecked()); + automaticFittingData.setFractureHalfLengthSelected(m_fractureHalfLengthCheckBox->isChecked()); automaticFittingData.setSurrogateScreeningEnabled(m_surrogateCombo && m_surrogateCombo->currentIndex() == 1); // 保存渗透率的最小值和最大值 @@ -1329,6 +1343,10 @@ void nmWxAutomaticFitting::setAutomaticFittingValue() automaticFittingData.getFractureConductivityMin().setValue(m_parameterTable->item(8, 2)->text().toDouble()); automaticFittingData.getFractureConductivityMax().setValue(m_parameterTable->item(8, 4)->text().toDouble()); + // 保存裂缝半长的最小值和最大值 + automaticFittingData.getFractureHalfLengthMin().setValue(m_parameterTable->item(9, 2)->text().toDouble()); + automaticFittingData.getFractureHalfLengthMax().setValue(m_parameterTable->item(9, 4)->text().toDouble()); + // 保存迭代参数 automaticFittingData.getIterationCount().setValue(m_iterationEdit->text().toInt()); automaticFittingData.getErrorTolerance().setValue(m_errorLimitEdit->text().toDouble()); @@ -1353,6 +1371,7 @@ void nmWxAutomaticFitting::setAutomaticFittingValue() double newSkinValue = m_parameterTable->item(1, 3)->text().toDouble(); double newWellboreStorageValue = m_parameterTable->item(2, 3)->text().toDouble(); double newFractureConductivityValue = m_parameterTable->item(8, 3)->text().toDouble(); + double newFractureHalfLengthValue = m_parameterTable->item(9, 3)->text().toDouble(); // 直接从数据管理器获取目标井 nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance(); @@ -1382,6 +1401,7 @@ void nmWxAutomaticFitting::setAutomaticFittingValue() nmDataAttribute dfc = fracturedWell->getDfc(); dfc.setValue(newFractureConductivityValue); fracturedWell->setDfc(dfc); + fracturedWell->getFractureHalfLength().setValue(newFractureHalfLengthValue); } } else if(wellType == NM_WELL_MODEL::Horizontal_Fractured_Well) { nmDataHorizontalFracturedWell* fracturedWell = dynamic_cast(pTargetWell); @@ -1389,6 +1409,7 @@ void nmWxAutomaticFitting::setAutomaticFittingValue() nmDataAttribute dfc = fracturedWell->getDfc(); dfc.setValue(newFractureConductivityValue); fracturedWell->setDfc(dfc); + fracturedWell->getFractureHalfLength().setValue(newFractureHalfLengthValue); } } @@ -1622,6 +1643,7 @@ void nmWxAutomaticFitting::updateBestParametersToTable() if(m_cfCheckBox->isChecked()) enabledParams.append(6); // 岩石压缩系数 if(m_swiCheckBox->isChecked()) enabledParams.append(7); // 初始含水饱和度 if(m_dfcCheckBox->isChecked()) enabledParams.append(8); // 裂缝导流能力 + if(m_fractureHalfLengthCheckBox->isChecked()) enabledParams.append(9); // 裂缝半长 // 更新参数值和范围 for (int i = 0; i < bestSolution.size() && i < enabledParams.size(); ++i) {