refactor(nmNum): 分离 LM 三阶段拟合验收目标

- 渗透率预调整仅按上下误差验收,不再因总误差达标跳过预调整
- 移除形状阶段总误差上限、预测缩步及总误差达标提前退出
- 井储表皮回检仅按前期形状改善与整体形状容差验收
- 保留第三阶段按整体误差联合微调,同步阶段误差日志、轨迹元数据及中文翻译
feature/AutoFit-Optimize-20260914
lvjunjie 2 weeks ago
parent 68c79378bb
commit 08de98335a

Binary file not shown.

@ -1257,8 +1257,8 @@ Reason: %1</source>
<translation>井储试调:相对斜率匹配误差=%1,井储=%2 → %3</translation>
</message>
<message>
<source>Wellbore recheck: early error=%1, total limit=%2, shape limit=%3.</source>
<translation>井储表皮回检:前期相对斜率匹配误差=%1,整体误差上限=%2,形状误差上限=%3。</translation>
<source>Wellbore recheck: early shape error=%1, shape limit=%2.</source>
<translation>井储表皮回检:前期形状误差=%1,整体形状误差上限=%2。</translation>
</message>
<message>
<source>early relative-slope error restored within tolerance</source>
@ -1273,8 +1273,8 @@ Reason: %1</source>
<translation>井储表皮回检在灵敏度评价期间完成</translation>
</message>
<message>
<source>total target reached during shape fitting</source>
<translation>形状调整期间已达到总误差目标</translation>
<source>Sensitivity probe accepted: shape error=%1</source>
<translation>接受灵敏度探针:形状误差=%1</translation>
</message>
<message>
<source>fresh sensitivity at joint shape entry</source>

@ -1212,9 +1212,9 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
QTextStream out(&metaFile);
out << "{\n";
out << " \"schema_version\": 29,\n";
out << " \"schema_version\": 30,\n";
out << " \"strategy\": \"permeability_height_then_shape_then_joint_lm\",\n";
out << " \"height_acceptance\": \"reliable_vertical_loss_decrease; no_shape_loss_constraint\",\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_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";
@ -1225,7 +1225,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
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_wellbore_total_constraint\": false,\n";
out << " \"shape_stage_total_tolerance\": \"max(0.02, 25% of stage entry total)\",\n";
out << " \"shape_stage_total_constraint\": false,\n";
out << " \"shape_wellbore_parameters_frozen\": true,\n";
out << " \"shape_sensitivity_refresh\": \"full Jacobian at joint shape entry and stagnation confirmation\",\n";
out << " \"shape_expansion_policy\": \"one expansion per reliable shape direction, up to 2x when actual rho exceeds 0.75; retain ordinary step on failure\",\n";
@ -1234,7 +1234,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
out << " \"shape_round_improvement_threshold\": \"max(0.0001, 1% of round entry shape loss)\",\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 total and shape limits, each max(0.0001, 5%)\",\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 << " \"total_stage_shape_constraint\": false,\n";
out << " \"total_parameter_selection\": \"all_valid_free_columns\",\n";
@ -2154,7 +2154,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
int heightStagnation = 0;
double heightScale = 1.0;
double heightSlope = -1.0;
if(permeabilityColumn >= 0 && current.fitness >= m_targetError) {
if(permeabilityColumn >= 0) {
emit logMessageGenerated(tr("LM stage 1: align curve height using permeability only (up to %1 evaluations).")
.arg(heightBudget));
for(int trial = 0; trial < heightBudget && processPauseAndStop(); ++trial) {
@ -2211,9 +2211,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(m_shouldStop) return LM_USER_STOPPED;
// 第二阶段不按迭代比例截断;普通形状按完整搜索轮次判断是否仍有显著改善。
// 井储、表皮保持冻结,总误差上限仍固定于普通形状阶段入口。
bool shapeStage = current.fitness >= m_targetError;
double shapeValueLimit = current.fitness + qMax(0.02, 0.25 * current.fitness);
// 普通形状调整冻结井储、表皮,只按形状验收;总误差达标只在第三阶段判断。
bool shapeStage = true;
bool jointShapeStarted = false;
double shapeRoundBaseline = current.breakdown.shapeLoss;
int shapeRoundNumber = 0;
@ -2230,7 +2229,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
bool wellboreRecheck = false;
bool wellboreRecheckDone = false;
double earlyWellboreBaseline = current.breakdown.earlyParallelLoss;
double recheckTotalLimit = 0.0;
double recheckShapeLimit = 0.0;
// 先井储、再表皮;回检沿用相同顺序和前期指标,不回到形状阶段反复循环。
bool earlyShapeStage = shapeStage && wellboreParameterCount > 0;
@ -2270,11 +2268,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
const int column = m_enabledParamIndices.indexOf(earlyShapeParameter);
return earlyGapDirection * (point.coordinates[column] - base.coordinates[column]) > 0.0 &&
point.breakdown.earlyParallelLoss < base.breakdown.earlyParallelLoss &&
(!wellboreRecheck || (point.fitness <= recheckTotalLimit &&
point.breakdown.shapeLoss <= recheckShapeLimit));
(!wellboreRecheck || point.breakdown.shapeLoss <= recheckShapeLimit);
}
return shapeStage
? point.breakdown.shapeLoss < base.breakdown.shapeLoss && point.fitness <= shapeValueLimit
? point.breakdown.shapeLoss < base.breakdown.shapeLoss
// 预调整结束后恢复原 LM:有效候选只按整体误差下降接受。
: point.fitness < base.fitness;
};
@ -2408,7 +2405,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
initialEarlyGapBias = trustRegionEarlyGapBias(current.breakdown);
earlyGapDirection = trustRegionEarlyGapDirection(initialEarlyGapBias);
earlyShapeHasImproved = false;
recheckTotalLimit = current.fitness + qMax(1.0e-4, 0.05 * current.fitness);
// 回检只约束形状:允许总误差上升,但保护其他参数已调好的整体形状。
recheckShapeLimit = current.breakdown.shapeLoss + qMax(1.0e-4, 0.05 * current.breakdown.shapeLoss);
effectiveImprovementBaseline = current.breakdown.earlyParallelLoss;
// 其他参数已改变,回检入口重测形状灵敏度,方向由本轮初始间距重新确定。
@ -2416,9 +2413,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
reuseSensitivityForNextStage();
rebuildRequested = true;
rebuildReason = QT_TR_NOOP("new sensitivity for wellbore recheck");
emit logMessageGenerated(tr("Wellbore recheck: early error=%1, total limit=%2, shape limit=%3.")
emit logMessageGenerated(tr("Wellbore recheck: early shape error=%1, shape limit=%2.")
.arg(current.breakdown.earlyParallelLoss, 0, 'g', 6)
.arg(recheckTotalLimit, 0, 'g', 6).arg(recheckShapeLimit, 0, 'g', 6));
.arg(recheckShapeLimit, 0, 'g', 6));
writeTraceRow(m_currentIteration, -1, "stage_switch", current.parameters, current.fitness,
true, 0, "shape_to_wellbore_recheck", &current.breakdown);
announceEarlyGapDirection();
@ -2451,8 +2448,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return;
}
effectiveImprovementBaseline = current.breakdown.shapeLoss;
// 井储、表皮可使总误差上升;联合形状阶段从当前工作点建立固定上限。
shapeValueLimit = current.fitness + qMax(0.02, 0.25 * current.fitness);
reuseSensitivityForNextStage();
emit logMessageGenerated(tr("Shape fitting continues with storage and skin fixed; a conditional wellbore recheck follows."));
writeTraceRow(m_currentIteration, -1, "stage_switch", current.parameters, current.fitness,
@ -2733,8 +2728,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
&current.breakdown);
emit logMessageGenerated(
(earlyShapeStage ? tr("Sensitivity probe accepted: early shape error=%1")
: tr("Sensitivity probe accepted: total error=%1"))
.arg(earlyShapeStage ? current.breakdown.earlyParallelLoss : current.fitness, 0, 'e', 4));
: (shapeStage ? tr("Sensitivity probe accepted: shape error=%1")
: tr("Sensitivity probe accepted: total error=%1")))
.arg(stageError(current), 0, 'e', 4));
} else {
restoreEvaluationState(current);
}
@ -2819,11 +2815,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
: LM_LOCAL_OPTIMUM;
break;
}
if(shapeStage && !earlyShapeStage && current.fitness < m_targetError) {
finishShapeStage(tr("total target reached during shape fitting"));
--iteration;
continue;
}
if(!shapeStage && current.fitness < m_targetError) {
promoteSampling(true);
stopReason = LM_TARGET_ACHIEVED;
@ -2956,37 +2947,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
: buildTrustRegionFisherStep(stepInformation, columns, current.coordinates,
damping, trustRadius, minimumCoordinateStep, &step, &reduction);
if(!stepBuilt) continue;
// 仅联合形状阶段预测总误差上限;井储、表皮步长不受总误差裁剪。
if(shapeStage && !earlyShapeStage) {
auto predictedGuard = [&](double scale) -> double {
double energy = 0.0;
for(int row = 0; row < current.breakdown.residualVector.size(); ++row) {
double value = current.breakdown.residualVector[row];
for(int column = 0; column < dimensions; ++column)
value += scale * jacobian[row][column] * step[column];
energy += value * value;
}
return qSqrt(energy);
};
const double currentGuard = predictedGuard(0.0);
const double predictedLimit = currentGuard + 0.9 * qMax(0.0, shapeValueLimit - currentGuard);
if(predictedGuard(1.0) > predictedLimit) {
double low = 0.0, high = 1.0;
for(int search = 0; search < 32; ++search) {
const double middle = 0.5 * (low + high);
if(predictedGuard(middle) <= predictedLimit) low = middle;
else high = middle;
}
const double stepScale = low;
for(int column = 0; column < dimensions; ++column) step[column] *= stepScale;
if(qSqrt(trustRegionSquaredNorm(step)) < minimumCoordinateStep) continue;
reduction = -trustRegionDotProduct(stepInformation.gradient, step);
for(int a = 0; a < dimensions; ++a)
for(int b = 0; b < dimensions; ++b)
reduction -= 0.5 * step[a] * stepInformation.matrix[a][b] * step[b];
if(!isFiniteNumber(reduction) || reduction <= 1.0e-14) continue;
}
}
const double tolerance = 1.0e-12 * qMax(predictedReduction, reduction);
if(selectedColumns.isEmpty() || reduction > predictedReduction + tolerance ||
(qAbs(reduction - predictedReduction) <= tolerance &&
@ -3141,7 +3101,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
}
// 普通形状步真实表现可靠时,沿同一联合方向最多扩步试算一次,不区分参数类型。
// 扩步仍须通过固定总误差上限且优于原步,否则保留原候选。
// 扩步只按整体形状验收,优于原步才替换,否则保留原候选。
if(shapeStage && !earlyShapeStage && acceptable(candidate, current) && processPauseAndStop()) {
const double ordinaryEnergy = 0.5 * trustRegionSquaredNorm(objectiveResidual);
const double ordinaryReduction = ordinaryEnergy - 0.5 * trustRegionSquaredNorm(candidate.breakdown.shapeResiduals);
@ -3286,7 +3246,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
.arg(iteration + 1)
.arg(componentDisplayName)
.arg(selectedColumns.size())
.arg(earlyShapeStage ? candidate.breakdown.earlyParallelLoss : candidate.fitness, 0, 'e', 4)
.arg(stageError(candidate), 0, 'e', 4)
.arg(accepted ? tr("accepted") : tr("rejected")));
emit progressUpdated(shapeStage ? -1 : totalIterations, m_globalBestFitness);
@ -3306,9 +3266,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(stopReason == LM_LOCAL_OPTIMUM || samplingRefreshFailed) break;
}
if(shapeStage && !earlyShapeStage && current.fitness < m_targetError) {
finishShapeStage(tr("total target reached during shape fitting"));
}
if(!shapeStage && current.fitness < m_targetError) {
promoteSampling(true);
stopReason = LM_TARGET_ACHIEVED;
@ -3337,7 +3294,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return LM_USER_STOPPED;
}
if(samplingRefreshFailed) return LM_OPTIMIZATION_FAILED;
if(current.fitness < m_targetError) {
if(!shapeStage && current.fitness < m_targetError) {
promoteSampling(true);
return samplingRefreshFailed ? LM_OPTIMIZATION_FAILED : LM_TARGET_ACHIEVED;
}

Loading…
Cancel
Save