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;
}