diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index 3cf81428..c3daf824 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index 6759fa7a..37b1bbbf 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -1217,16 +1217,12 @@ Reason: %1 没有其他勾选参数需要调整形状 - Early adjustment: adjust wellbore storage, then skin, to match the early pressure-derivative slope difference of the target. - 前期预调整:先调整井储,再调整表皮,使模拟压力与导数的前期斜率差接近目标曲线。 + Early adjustment: select storage or skin by predicted first-window shape reduction; halve the step after rejection and switch after 3 consecutive rejections. + 前期预调整:按第一时间窗口形状误差的预计下降量选择井储或表皮;拒绝后步长减半,连续拒绝三次换参数。 - Early adjustment: adjust skin to match the early pressure-derivative slope difference of the target. - 前期预调整:调整表皮,使模拟压力与导数的前期斜率差接近目标曲线。 - - - Wellbore storage adjustment ended: %1; now adjusting skin. - 井储调整结束:%1;开始单独调整表皮。 + all wellbore parameters visited once + 已勾选的井储、表皮参数均已完成一轮调整 Early wellbore adjustment ended: %1 diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp index e3f6579b..4b0257c8 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp @@ -381,13 +381,6 @@ static double trustRegionEarlyGapBias(const AutoFitObjectiveBreakdownLM& objecti return bias / qLn(10.0); } -// 按用户指定的形态规则定向:初始模拟间距大则增大井储/表皮,小则减小。 -static double trustRegionEarlyGapDirection(double initialGapBias) -{ - if(!isFiniteNumber(initialGapBias) || qAbs(initialGapBias) <= 1.0e-12) return 0.0; - return initialGapBias > 0.0 ? 1.0 : -1.0; -} - // 第一窗口的形状能量与局部 Fisher 使用相同的中心时间和重叠权重, // 压力、导数同时参与;不重新插值或调用求解器。 static double trustRegionEarlyShapeEnergy(const QVector& residual) @@ -695,50 +688,6 @@ static bool buildBestSingleParameterStep(const TrustRegionFisher& information, return !selected->isEmpty(); } -// 前期每次只调一个参数:间距锁定符号,形状 LM 提供幅度和后续验收依据。 -static bool buildEarlyGapGuidedStep(const TrustRegionFisher& shapeInformation, - const QVector& selected, const QVector& coordinates, double direction, - double damping, double trustRadius, double minimumStep, - QVector* step, double* predictedReduction) -{ - if(selected.size() != 1 || direction == 0.0) return false; - const int column = selected[0]; - TrustRegionFisher guided = shapeInformation; - guided.gradient[column] = -direction * qAbs(shapeInformation.gradient[column]); - if(!buildTrustRegionFisherStep(guided, selected, coordinates, damping, - trustRadius, minimumStep, step, predictedReduction)) return false; - // 改方向后的预测必须用原形状模型重算,不能把上坡伪装成预测下降。 - const double delta = (*step)[column]; - *predictedReduction = -shapeInformation.gradient[column] * delta - - 0.5 * shapeInformation.matrix[column][column] * delta * delta; - return isFiniteNumber(*predictedReduction); -} - -// 放大当前方向,仍受传入的最大半径和参数边界限制。 -// 前期初始探路可跨过预测上坡区,最终仍按真实形状验收。 -static bool buildExpandedTrustRegionStep(const TrustRegionFisher& information, - const QVector& coordinates, const QVector& originalStep, - double maximumRadius, QVector* expandedStep, double* prediction, - bool requirePredictedDescent = true) -{ - const double norm = qSqrt(trustRegionSquaredNorm(originalStep)); - if(norm <= 1.0e-12) return false; - const double scale = qMin(2.0, maximumRadius / norm); - if(scale <= 1.01) return false; - expandedStep->resize(originalStep.size()); - double difference = 0.0; - for(int i = 0; i < originalStep.size(); ++i) { - (*expandedStep)[i] = qBound(0.0, coordinates[i] + scale * originalStep[i], 1.0) - coordinates[i]; - difference += qAbs((*expandedStep)[i] - originalStep[i]); - } - if(difference <= 1.0e-10) return false; - *prediction = -trustRegionDotProduct(information.gradient, *expandedStep); - for(int i = 0; i < expandedStep->size(); ++i) - for(int j = 0; j < expandedStep->size(); ++j) - *prediction -= 0.5 * (*expandedStep)[i] * information.matrix[i][j] * (*expandedStep)[j]; - return isFiniteNumber(*prediction) && (!requirePredictedDescent || *prediction > 1.0e-14); -} - // 每次得到有效真实候选后,使用满足最新割线条件的秩一修正更新完整残差 // Jacobian。这样模型吸收了刚得到的真实变化,又不必立即逐参数重新试算。 static void updateTrustRegionJacobian( @@ -1147,31 +1096,31 @@ void nmCalculationAutoFitLM::writeTraceMetaFile() QTextStream out(&metaFile); out << "{\n"; - out << " \"schema_version\": 39,\n"; + out << " \"schema_version\": 47,\n"; out << " \"strategy\": \"permeability_height_then_shape_then_joint_lm\",\n"; out << " \"height_acceptance\": \"reliable_vertical_loss_decrease; no_shape_or_total_loss_constraint\",\n"; - out << " \"shape_priority\": \"storage_then_skin_then_shape_without_wellbore_then_optional_wellbore_recheck\",\n"; + out << " \"shape_priority\": \"wellbore_parameter_sweep_then_non_wellbore_sweep_then_optional_wellbore_recheck\",\n"; out << " \"shape_metric\": \"pressure_and_derivative_log_slopes_81_points_lag_8\",\n"; out << " \"early_parallel_metric\": \"pressure_and_derivative_log_slopes_first_window_81_points_lag_8\",\n"; out << " \"early_gap_metric\": \"weighted_rms_log10_pressure_derivative_ratio_error_first_window_81_points\",\n"; out << " \"early_gap_bias_metric\": \"weighted_mean_signed_log10_gap_simulation_minus_target_first_window\",\n"; - out << " \"early_direction_policy\": \"initial_gap_bias_positive_increase_negative_decrease; direction_shared_and_locked_for_storage_and_skin; recompute_only_at_wellbore_recheck_entry\",\n"; - out << " \"early_initial_step_policy\": \"after_gap_direction_selection; storage_ratio_cap_1.05; skin_local_scale_fraction_0.02; no_cached_probe_acceptance_before_first_shape_improvement\",\n"; - out << " \"early_initial_expansion_policy\": \"before_first_accepted_shape_improvement; if_shape_worsens_double_coordinate_step_from_unchanged_base_up_to_parameter_bound; retain_base_on_failure\",\n"; - out << " \"early_wellbore_acceptance\": \"locked_gap_direction_and_early_shape_decrease\",\n"; + out << " \"early_direction_policy\": \"first_window_shape_LM_selects_unvisited_wellbore_parameter_and_initial_direction; direction_locked_until_parameter_finished; gap_diagnostics_only\",\n"; + out << " \"early_initial_step_policy\": \"same_as_later_shape_sweep; initial_LM_coordinate_radius_0.24; no_cached_probe_acceptance\",\n"; + out << " \"early_expansion_policy\": \"accepted_double_from_new_point; rejected_halve_from_unchanged_point; coordinate_step_cap_0.60; switch_after_3_consecutive_rejections\",\n"; + out << " \"early_wellbore_acceptance\": \"first_window_shape_decrease; no_gap_direction_constraint; wellbore_recheck_retains_full_shape_guard\",\n"; out << " \"early_wellbore_total_constraint\": false,\n"; out << " \"shape_stage_total_constraint\": false,\n"; out << " \"shape_wellbore_parameters_frozen\": true,\n"; - out << " \"shape_sensitivity_refresh\": \"non_wellbore_columns_once_at_sweep_entry; secant updates during sweep; full Jacobian rebuilt at total-stage entry\",\n"; + out << " \"shape_sensitivity_refresh\": \"active_group_columns_once_at_each_sweep_entry; secant updates during sweep; full Jacobian rebuilt at total-stage entry\",\n"; out << " \"shape_expansion_policy\": \"lock parameter and direction; after each strict shape decrease double next coordinate step from accepted point; otherwise retain point and halve step\",\n"; out << " \"shape_radius_policy\": \"initial LM step capped at 0.24; subsequent coordinate steps capped at 0.60; no prediction-ratio gate\",\n"; - out << " \"shape_global_parameter_selection\": \"largest predicted reduction among unvisited non-wellbore parameters; finish current parameter before selecting next; each parameter visited once\",\n"; - out << " \"stage2_budget\": \"one parameter sweep; each parameter stops after 3 consecutive non-improving trials, a bound, or minimum step; existing solver failure limit retained\",\n"; + out << " \"shape_global_parameter_selection\": \"largest predicted reduction among unvisited parameters_in_current_group; finish current parameter before selecting next; each parameter visited once_per_sweep\",\n"; + out << " \"stage2_budget\": \"one sweep_per_parameter_group; switch_after_3_consecutive_rejections; bounds, minimum step and solver failure limit retained\",\n"; out << " \"shape_step_improvement_threshold\": \"any strict shape loss decrease; no cumulative improvement threshold\",\n"; out << " \"iteration_count_scope\": \"max_iterations applies to total-stage LM; trace iteration remains cumulative\",\n"; out << " \"wellbore_recheck_trigger\": \"early loss exceeds initial early-stage exit by max(0.0001, 10%)\",\n"; out << " \"wellbore_recheck_acceptance\": \"early decrease with fixed entry shape limit max(0.0001, 5%); no_total_loss_constraint\",\n"; - out << " \"wellbore_recheck_budget\": \"once when early shape degrades; each wellbore parameter stops after 2 ineffective steps, independent of total-stage budget\",\n"; + out << " \"wellbore_recheck_budget\": \"once_when_early_shape_degrades; single_parameter_sweep_with_3_consecutive_rejections; stop_when_baseline_restored; independent_of_total_stage_budget\",\n"; out << " \"total_stage_shape_constraint\": false,\n"; out << " \"total_parameter_selection\": \"all_valid_free_columns\",\n"; out << " \"skin_difference_policy\": \"local_scale_in_all_stages\",\n"; @@ -1949,7 +1898,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() const double minimumCoordinateStep = 1.0e-5; const double minimumTrustRadius = 2.0e-3; const double maximumTrustRadius = 0.30; - // 第二阶段整体形状允许更大步幅;第三阶段继续使用原来的半径和预测验收规则。 + // 第二阶段两组逐参数调整共用较大步幅;第三阶段继续使用原来的半径和预测验收规则。 const double initialShapeTrustRadius = 0.24; const double maximumShapeTrustRadius = 0.60; // 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。 @@ -2147,7 +2096,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() if(m_shouldStop) return LM_USER_STOPPED; - // 第二阶段先测灵敏度,再逐个调完非井筒参数;每个参数只访问一次。 + // 第二阶段先调井筒参数、再调其他参数;各组共用单参数扫描,每个参数只访问一次。 // 普通形状调整冻结井储、表皮,只按形状验收;总误差达标只在第三阶段判断。 bool shapeStage = true; bool singleShapeStarted = false; @@ -2169,31 +2118,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() bool wellboreRecheckDone = false; double earlyWellboreBaseline = current.breakdown.earlyParallelLoss; double recheckShapeLimit = 0.0; - // 先井储、再表皮;回检沿用相同顺序和前期指标,不回到形状阶段反复循环。 + // 井储、表皮在组内按预测下降量选序;回检沿用第一窗口指标和相同扫描规则。 bool earlyShapeStage = shapeStage && wellboreParameterCount > 0; - int earlyShapeParameter = storageColumn >= 0 ? 2 : 1; - double initialEarlyGapBias = trustRegionEarlyGapBias(current.breakdown); - double earlyGapDirection = trustRegionEarlyGapDirection(initialEarlyGapBias); - bool earlyShapeHasImproved = false; - auto announceEarlyGapDirection = [&]() { - // 两个参数共用本轮入口的方向;日志保留初始偏差,避免误读为探针响应方向。 - const QString directionName = earlyGapDirection > 0.0 ? "increase" : - (earlyGapDirection < 0.0 ? "decrease" : "matched"); - writeTraceRow(m_currentIteration, m_enabledParamIndices.indexOf(earlyShapeParameter), - "early_gap_direction", current.parameters, current.fitness, true, 0, - QString("%1_initial_gap_bias_%2").arg(directionName).arg(initialEarlyGapBias, 0, 'g', 12), - ¤t.breakdown); - emit logMessageGenerated(tr("Early gap guidance: %1, initial signed gap=%2, direction=%3; fixed for storage and skin.") - .arg(earlyShapeParameter == 2 ? tr("wellbore storage") : tr("skin")) - .arg(initialEarlyGapBias, 0, 'g', 6) - .arg(earlyGapDirection > 0.0 ? tr("increase") : - (earlyGapDirection < 0.0 ? tr("decrease") : tr("matched")))); - }; + trustRadius = initialShapeTrustRadius; auto parameterAllowedInStage = [&](int column) -> bool { const int parameterIndex = m_enabledParamIndices[column]; - // 候选和缓存灵敏度探针共用此限制,防止探针绕过形状阶段的冻结。 + // 各子阶段只开放对应参数组,实际候选始终只改变选中的一列。 if(!shapeStage) return true; - if(earlyShapeStage) return parameterIndex == earlyShapeParameter; + if(earlyShapeStage) return parameterIndex == 1 || parameterIndex == 2; return parameterIndex != 1 && parameterIndex != 2; }; auto stageError = [&](const TrustRegionEvaluation& point) -> double { @@ -2203,10 +2135,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() auto acceptable = [&](const TrustRegionEvaluation& point, const TrustRegionEvaluation& base) -> bool { if(!point.valid) return false; if(earlyShapeStage) { - // 缓存探针也只能沿间距确定的方向接受;停止仍看原来的前期形状改善。 - const int column = m_enabledParamIndices.indexOf(earlyShapeParameter); - return earlyGapDirection * (point.coordinates[column] - base.coordinates[column]) > 0.0 && - point.breakdown.earlyParallelLoss < base.breakdown.earlyParallelLoss && + // 第一窗口形状决定是否改善;补调保留原有的整体形状保护。 + return point.breakdown.earlyParallelLoss < base.breakdown.earlyParallelLoss && (!wellboreRecheck || point.breakdown.shapeLoss <= recheckShapeLimit); } return shapeStage @@ -2215,8 +2145,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() : point.fitness < base.fitness; }; auto acceptPoint = [&](const TrustRegionEvaluation& point) { - // 缓存探针和正式候选共用此标记;首次接受形状改善后,不再启用初始扩步探路。 - if(earlyShapeStage) earlyShapeHasImproved = true; + // 只发布通过当前阶段目标验收的真实候选。 current = point; publishAcceptedPoint(current); restoreEvaluationState(current); @@ -2225,14 +2154,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() ? tr("LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections.") : tr("LM stage 3: original LM fitting; accept by total error only.")); if(earlyShapeStage) { - emit logMessageGenerated(earlyShapeParameter == 2 - ? tr("Early adjustment: storage then skin; pressure-derivative gap selects direction, early shape error controls acceptance and stopping.") - : tr("Early adjustment: skin; pressure-derivative gap selects direction, early shape error controls acceptance and stopping.")); - announceEarlyGapDirection(); + emit logMessageGenerated(tr("Early adjustment: select storage or skin by predicted first-window shape reduction; halve the step after rejection and switch after 3 consecutive rejections.")); } - // 有效改善始终相对“上一次有效改善后的误差”累计判断,避免一连串微小 - // 下降每次都清零计数;累计达到门槛后才开始新的有效改善基准。 + // 第三阶段有效改善相对上一次有效基准累计判断;第二阶段按逐参数拒绝次数结束。 double effectiveImprovementBaseline = stageError(current); int fullDataRejections = 0; bool samplingRefreshFailed = false; @@ -2296,7 +2221,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() consecutiveSolverFailures = 0; stagnationConfirmationRequested = false; globalFallbackAttempted = false; - trustRadius = shapeStage && !earlyShapeStage ? initialShapeTrustRadius : 0.12; + trustRadius = shapeStage ? initialShapeTrustRadius : 0.12; damping = 0.01; }; auto enterTotalStage = [&](const QString& reason) { @@ -2306,6 +2231,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() shapeStage = false; earlyShapeStage = false; wellboreRecheck = false; + activeShapeColumn = -1; + shapeCoordinateStep = 0.0; + consecutiveShapeRejections = 0; effectiveImprovementBaseline = current.fitness; reuseSensitivityForNextStage(); // 第二阶段只测各子阶段需要的列;所有出口都在当前点重建完整 J,不能把缺列模型带入整体 LM。 @@ -2338,14 +2266,16 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() } wellboreRecheck = true; earlyShapeStage = true; - earlyShapeParameter = storageColumn >= 0 ? 2 : 1; - initialEarlyGapBias = trustRegionEarlyGapBias(current.breakdown); - earlyGapDirection = trustRegionEarlyGapDirection(initialEarlyGapBias); - earlyShapeHasImproved = false; // 回检只约束形状:允许总误差上升,但保护其他参数已调好的整体形状。 recheckShapeLimit = current.breakdown.shapeLoss + qMax(1.0e-4, 0.05 * current.breakdown.shapeLoss); + // 回检重新开放井储和表皮,清除上一组留下的活动步长及完成标记。 + if(storageColumn >= 0) shapeParameterFinished[storageColumn] = false; + if(skinColumn >= 0) shapeParameterFinished[skinColumn] = false; + activeShapeColumn = -1; + shapeCoordinateStep = 0.0; + consecutiveShapeRejections = 0; effectiveImprovementBaseline = current.breakdown.earlyParallelLoss; - // 其他参数已改变,回检入口重测形状灵敏度,方向由本轮初始间距重新确定。 + // 其他参数已改变,回检入口重测第一窗口形状灵敏度,由 LM 重新确定方向和步幅。 jacobian.clear(); reuseSensitivityForNextStage(); rebuildRequested = true; @@ -2355,24 +2285,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() .arg(recheckShapeLimit, 0, 'g', 6)); writeTraceRow(m_currentIteration, -1, "stage_switch", current.parameters, current.fitness, true, 0, "shape_to_wellbore_recheck", ¤t.breakdown); - announceEarlyGapDirection(); }; auto finishEarlyShapeStage = [&](const QString& reason) { - if(earlyShapeParameter == 2 && skinColumn >= 0) { - earlyShapeParameter = 1; - earlyShapeHasImproved = false; - effectiveImprovementBaseline = current.breakdown.earlyParallelLoss; - reuseSensitivityForNextStage(); - // 井储已变,重测表皮的形状灵敏度和步幅,方向仍保持本轮入口的判断。 - rebuildRequested = true; - rebuildReason = QT_TR_NOOP("refresh shape sensitivity at skin adjustment entry"); - emit logMessageGenerated(tr("Wellbore storage adjustment ended: %1; now adjusting skin.").arg(reason)); - writeTraceRow(m_currentIteration, -1, "stage_switch", current.parameters, current.fitness, - true, 0, wellboreRecheck ? "recheck_storage_to_skin" : "storage_to_skin", ¤t.breakdown); - announceEarlyGapDirection(); - return; - } + if(m_shouldStop) return; + // 井筒参数整组完成后才切换目标,不能把旧组的活动参数或步长带入下一组。 + activeShapeColumn = -1; + shapeCoordinateStep = 0.0; + consecutiveShapeRejections = 0; if(wellboreRecheck) { enterTotalStage(reason); return; @@ -2404,11 +2324,12 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() for(int column = 0; column < dimensions; ++column) { if(parameterAllowedInStage(column) && !shapeParameterFinished[column]) return; } - finishShapeStage(tr("all shape parameters visited once")); + if(earlyShapeStage) finishEarlyShapeStage(tr("all wellbore parameters visited once")); + else finishShapeStage(tr("all shape parameters visited once")); }; auto completeShapeSearchStep = [&](bool accepted) { - if(!shapeStage || earlyShapeStage || m_shouldStop) return; - // 只按本次真实下降控制实际步长;不再使用累计 10% 门槛提前结束整轮。 + if(!shapeStage || m_shouldStop) return; + // 两组参数共用扩缩步规则:改善后放大,拒绝后从已接受点缩步重试。 ++shapeStepCount; consecutiveShapeRejections = accepted ? 0 : consecutiveShapeRejections + 1; shapeCoordinateStep = qBound(-maximumShapeTrustRadius, @@ -2425,11 +2346,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() }; auto registerEffectiveImprovement = [&](double fitness) -> bool { - if(shapeStage && !earlyShapeStage) return false; + if(shapeStage) return false; const double requiredImprovement = qMax( - shapeStage ? 1.0e-4 : effectiveAbsoluteImprovement, - qAbs(effectiveImprovementBaseline) * - (shapeStage ? 0.01 : effectiveRelativeImprovement)); + effectiveAbsoluteImprovement, + qAbs(effectiveImprovementBaseline) * effectiveRelativeImprovement); const double improvement = effectiveImprovementBaseline - fitness; if(improvement < requiredImprovement) { return false; @@ -2443,20 +2363,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() return true; }; - // 普通形状的失败试算缩步并累计当前参数的拒绝次数; - // 前期井储、表皮与整体 LM 沿用各自的无效次数处理。 + // 第二阶段求解失败也进入当前参数的拒绝处理,第三阶段保留原 LM 停滞确认。 auto recordIneffectiveStep = [&]() -> bool { - if(shapeStage && !earlyShapeStage) { + if(shapeStage) { completeShapeSearchStep(false); return false; } ++consecutiveIneffectiveSteps; - if(earlyShapeStage) { - // 一次拒绝只缩步,再给当前参数一次真实尝试;连续无改善才交给下一段。 - if(consecutiveIneffectiveSteps >= 2) - finishEarlyShapeStage(tr("2 consecutive steps without effective early improvement")); - return false; - } if(!globalFallbackAttempted) { return false; } @@ -2525,9 +2438,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() processPauseAndStop(); ++column) { const int parameterIndex = m_enabledParamIndices[column]; - if(earlyShapeStage && parameterIndex != 1 && parameterIndex != 2) continue; - // 普通形状阶段跳过冻结的井储、表皮,避免无用正演;回检和整体阶段会重新差分。 - if(shapeStage && !earlyShapeStage && !parameterAllowedInStage(column)) continue; + // 每组入口只测该组开放的参数;回检和整体阶段会重新差分。 + if(shapeStage && !parameterAllowedInStage(column)) continue; const double lower = m_parameterLower[parameterIndex]; const double upper = m_parameterUpper[parameterIndex]; if(upper <= lower) continue; @@ -2615,11 +2527,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() jacobianColumnValid[column] = true; columnBuilt = true; - // 后半段形状探针仅用于灵敏度,正式调整必须经过单参数 LM 预测选优。 - // 前期同时测井储和表皮,但缓存探针不能绕过当前子阶段的选参限制。 - // 前期首次正式调整必须从小步开始,测形状灵敏度的大探针不能提前成为工作点。 - if((!shapeStage || earlyShapeStage) && - (!earlyShapeStage || earlyShapeHasImproved) && + // 第二阶段探针只用于灵敏度;只有第三阶段保留直接接受缓存探针的规则。 + if(!shapeStage && parameterAllowedInStage(column) && acceptable(probe, base) && (!bestProbe.valid || stageError(probe) < stageError(bestProbe))) { bestProbe = probe; @@ -2640,12 +2549,12 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() } } // 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度交给逐参数选步处理。 - if(validColumnCount == 0 || (shapeStage && !earlyShapeStage && !hasActiveSensitivity) || m_shouldStop) { + if(validColumnCount == 0 || (shapeStage && !hasActiveSensitivity) || m_shouldStop) { restoreEvaluationState(base); return false; } - // 前期井储/表皮和整体阶段仍可接受更优缓存探针;后半段形状只由 LM 选步。 + // 只有整体阶段可接受更优缓存探针,第二阶段两组参数都由单参数 LM 选步。 // 所有列先基于同一个 base 建完,再用已知割线平移 Jacobian,避免边算边移动基点。 if(bestProbe.valid && bestProbeColumn >= 0) { QVector acceptedStep(dimensions, 0.0); @@ -2663,14 +2572,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() current.fitness, true, 0, - wellboreRecheck ? "wellbore_recheck_accepted_cached_probe" : - (earlyShapeStage ? "early_shape_accepted_cached_probe" : - (shapeStage ? "shape_accepted_cached_probe" : "total_accepted_cached_probe")), + "total_accepted_cached_probe", ¤t.breakdown); emit logMessageGenerated( - (earlyShapeStage ? tr("Sensitivity probe accepted: early shape error=%1") - : (shapeStage ? tr("Sensitivity probe accepted: shape error=%1") - : tr("Sensitivity probe accepted: total error=%1"))) + tr("Sensitivity probe accepted: total error=%1") .arg(stageError(current), 0, 'e', 4)); } else { restoreEvaluationState(current); @@ -2764,12 +2669,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() stopReason = LM_MAX_ITERATIONS; break; } - if(wellboreRecheck && current.breakdown.earlyParallelLoss <= - earlyWellboreBaseline + qMax(1.0e-4, 0.10 * earlyWellboreBaseline)) { - enterTotalStage(tr("wellbore recheck completed during sensitivity evaluation")); - --iteration; - continue; - } // 差分探针不等于 LM 候选;重建后继续按当前阶段求步并真实评价,再确认停滞。 registerEffectiveImprovement(stageError(current)); } @@ -2792,9 +2691,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() } const QVector information = buildTrustRegionFisher( objectiveJacobian, objectiveResidual, jacobianColumnValid, objectiveCoordinates); - const TrustRegionFisher& global = information[kAutoFitTimeWindowCount]; // 前期残差已含第一窗口权重,使用全局和,不能再次乘局部窗口权重。 - const TrustRegionFisher& stepInformation = global; + const TrustRegionFisher& global = information[kAutoFitTimeWindowCount]; QVector stageColumnValid = jacobianColumnValid; for(int column = 0; column < dimensions; ++column) { if(!parameterAllowedInStage(column)) stageColumnValid[column] = false; @@ -2804,32 +2702,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() double predictedReduction = 0.0; const int selectedWindow = earlyShapeStage ? 0 : -1; - // 前期只开放当前井储或表皮;后半段形状从有效自由列中选一个,整体 LM 仍联合求解。 + // 第二阶段从当前组有效自由列中选一个,整体 LM 仍联合求解。 // 保留弱敏感、相关及边界列供预测比较;窗口只保留作误差诊断。 for(int column = 0; column < dimensions; ++column) { - if(stageColumnValid[column] && (earlyShapeStage || global.matrix[column][column] > 0.0)) + if(stageColumnValid[column] && global.matrix[column][column] > 0.0) selectedColumns.append(column); } - if(earlyShapeStage) { - double earlyStepRadius = trustRadius; - if(!earlyShapeHasImproved && selectedColumns.size() == 1) { - // 首次调整沿用小步起调,形状变差时由后面的同向扩步循环处理。 - const int column = selectedColumns[0]; - const int parameterIndex = m_enabledParamIndices[column]; - const double lower = m_parameterLower[parameterIndex]; - const double upper = m_parameterUpper[parameterIndex]; - const double smallStep = useTrustRegionLogScale(parameterIndex, lower, upper) - ? qLn(1.05) / (qLn(upper) - qLn(lower)) - : (parameterIndex == 1 ? 0.02 * qMax(0.1, qAbs(current.parameters[column])) - : 0.05 * qMax(1.0e-8, qAbs(current.parameters[column]))) / (upper - lower); - earlyStepRadius = qMin(trustRadius, smallStep); - } - if(selectedColumns.isEmpty() || !buildEarlyGapGuidedStep( - stepInformation, selectedColumns, current.coordinates, earlyGapDirection, - damping, earlyStepRadius, minimumCoordinateStep, &coordinateStep, &predictedReduction)) { - selectedColumns.clear(); - } - } else if(shapeStage) { + if(shapeStage) { // 首步由灵敏度选出尚未调过的参数;后续锁定方向,直接使用扩缩后的步长。 if(activeShapeColumn < 0) { QVector availableColumns; @@ -2848,12 +2727,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() "largest_predicted_reduction_unvisited", ¤t.breakdown); } else { // 剩余参数均无可行预测下降步时逐项记录,不能重复选择已完成参数。 + const bool finishingEarlyShape = earlyShapeStage; for(int column = 0; column < dimensions; ++column) { if(!parameterAllowedInStage(column) || shapeParameterFinished[column]) continue; activeShapeColumn = column; finishShapeParameter(jacobianColumnValid[column] ? "no_feasible_descent_step" : "invalid_sensitivity"); - if(!shapeStage || earlyShapeStage) break; + if(!shapeStage || earlyShapeStage != finishingEarlyShape) break; } --iteration; continue; @@ -2887,12 +2767,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() // 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。 if(selectedColumns.isEmpty()) { - if(earlyShapeStage) { - finishEarlyShapeStage(tr("no feasible early adjustment direction or parameter at bound")); - // 尚未求解候选,原迭代留给其余参数,不额外占用迭代或求解预算。 - --iteration; - continue; - } if(trustRadius <= minimumTrustRadius * 1.01 && modelRebuiltAtMinimumRadius) { if(promoteSampling(false)) continue; @@ -2922,14 +2796,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() selectedParameterIndices << QString::number(m_enabledParamIndices[selectedColumns[i]]); } QString selectionName = (wellboreRecheck ? QString("recheck_") : QString()) + QString(earlyShapeStage - ? (earlyShapeParameter == 2 ? "storage_parallel_" : "skin_parallel_") : (shapeStage ? "shape_" : "total_")) + (selectedWindow >= 0 + ? (m_enabledParamIndices[selectedColumns[0]] == 2 ? "storage_parallel_" : "skin_parallel_") : (shapeStage ? "shape_" : "total_")) + (selectedWindow >= 0 ? QString("window_%1").arg(selectedWindow + 1) : QString("global")) + "_params_" + selectedParameterIndices.join("_"); TrustRegionEvaluation candidate; candidate.coordinates = candidateCoordinates; candidate.parameters = parametersFromCoordinates(candidate.coordinates); - if(earlyShapeStage && earlyShapeParameter == 2) { + if(earlyShapeStage && m_enabledParamIndices[selectedColumns[0]] == 2) { emit logMessageGenerated(tr("Wellbore storage trial: early shape error=%1, C=%2 -> %3") .arg(current.breakdown.earlyParallelLoss, 0, 'g', 5) .arg(current.parameters[storageColumn], 0, 'g', 6) @@ -2942,43 +2816,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() &candidate.curve, &candidate.elapsedMs); - // 初始形状变差时先沿锁定方向跨大步探路,不立即缩步或累计无效次数。 - // 始终以未移动的 current 为基点;每次翻倍并裁到边界,差解不发布、不接受。 - bool earlyInitialExpanded = false; - bool earlyInitialExpansionAtBound = false; - while(earlyShapeStage && !earlyShapeHasImproved && candidate.valid && - candidate.breakdown.earlyParallelLoss > current.breakdown.earlyParallelLoss && - processPauseAndStop()) { - QVector expandedStep; - double expandedPrediction = 0.0; - if(!buildExpandedTrustRegionStep(stepInformation, current.coordinates, coordinateStep, - 1.0, &expandedStep, &expandedPrediction, false)) { - earlyInitialExpansionAtBound = true; - break; - } - writeTraceRow(m_currentIteration, selectedColumns[0], "early_initial_expansion", - candidate.parameters, candidate.fitness, true, candidate.elapsedMs, - "shape_worse_expand_same_direction", &candidate.breakdown); - restoreEvaluationState(current); - TrustRegionEvaluation expanded; - expanded.coordinates = current.coordinates; - for(int column = 0; column < dimensions; ++column) - expanded.coordinates[column] += expandedStep[column]; - expanded.parameters = parametersFromCoordinates(expanded.coordinates); - const int column = selectedColumns[0]; - emit logMessageGenerated(tr("Early shape initially worsened: %1, trial=%2 -> %3; expanding in the same direction.") - .arg(earlyShapeParameter == 2 ? tr("wellbore storage") : tr("skin")) - .arg(candidate.parameters[column], 0, 'g', 6) - .arg(expanded.parameters[column], 0, 'g', 6)); - expanded.valid = evaluateTrustRegionPoint(expanded.parameters, &expanded.fitness, - &expanded.breakdown, &expanded.curve, &expanded.elapsedMs); - candidate = expanded; - coordinateStep = expandedStep; - predictedReduction = expandedPrediction; - stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep)); - earlyInitialExpanded = true; - } - if(earlyInitialExpanded) selectionName += "_initial_expanded"; if(m_shouldStop) { restoreEvaluationState(current); stopReason = LM_USER_STOPPED; @@ -3003,7 +2840,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() "solver_invalid_" + selectionName, nullptr); restoreEvaluationState(current); - if(consecutiveSolverFailures >= 2 && (!shapeStage || earlyShapeStage)) { + if(consecutiveSolverFailures >= 2 && !shapeStage) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("2 consecutive solver failures"); } @@ -3048,7 +2885,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() const bool poorPrediction = predictedReduction > predictionFloor && (!isFiniteNumber(reductionRatio) || reductionRatio < 0.25); consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0; - if(consecutivePoorPredictions >= 2 && (!shapeStage || earlyShapeStage)) { + if(consecutivePoorPredictions >= 2 && !shapeStage) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("2 consecutive steps with actual improvement below 25% of prediction"); } @@ -3060,7 +2897,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() fullDataRejections = 0; } QString componentName = earlyShapeStage - ? (earlyShapeParameter == 2 ? "storage_parallel" : "skin_parallel") : (shapeStage ? "shape" : "total"); + ? (m_enabledParamIndices[selectedColumns[0]] == 2 ? "storage_parallel" : "skin_parallel") : (shapeStage ? "shape" : "total"); if(accepted) { // 当前阶段接受候选后同步发布参数、曲线和诊断。 @@ -3069,8 +2906,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() ++acceptedSinceRebuild; consecutiveRejectedSteps = 0; - // 井筒调整及第三阶段保留原 LM 控制;逐参数粗调在步末直接扩缩实际步长。 - if(!shapeStage || earlyShapeStage) { + // 第三阶段保留原 LM 控制;第二阶段两组都在步末直接扩缩实际步长。 + if(!shapeStage) { if(reductionRatio > 0.75) { damping = qMax(1.0e-8, damping * 0.5); if(stepNorm >= trustRadius * 0.8) { @@ -3086,7 +2923,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() } } - if(acceptedSinceRebuild >= 10 && !rebuildRequested && (!shapeStage || earlyShapeStage)) { + if(acceptedSinceRebuild >= 10 && !rebuildRequested && !shapeStage) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("10 accepted steps since last rebuild"); } @@ -3138,11 +2975,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() emit progressUpdated(shapeStage ? -1 : totalIterations, m_globalBestFitness); // 先记录当前试调结果,再发布阶段切换,避免日志显示为未试调就结束。 - if(earlyInitialExpansionAtBound && !accepted) { - finishEarlyShapeStage(tr("initial same-direction expansion reached the parameter bound without early shape improvement")); - continue; - } - if(shapeStage && !earlyShapeStage) { + if(shapeStage) { completeShapeSearchStep(accepted); continue; }