@ -632,30 +632,6 @@ static bool buildTrustRegionFisherStep(
return projectAndPredict ( ) ;
return projectAndPredict ( ) ;
}
}
// 为每个可调参数独立求解 LM 步,按边界投影后的预测下降量选择一个参数。
static bool buildBestSingleParameterStep ( const TrustRegionFisher & information ,
const QVector < int > & available , const QVector < double > & coordinates ,
double damping , double trustRadius , double minimumStep ,
QVector < int > * selected , QVector < double > * step , double * predictedReduction )
{
selected - > clear ( ) ;
step - > fill ( 0.0 , coordinates . size ( ) ) ;
* predictedReduction = 0.0 ;
for ( int i = 0 ; i < available . size ( ) ; + + i ) {
QVector < int > singleColumn ( 1 , available [ i ] ) ;
QVector < double > trialStep ;
double trialPrediction = 0.0 ;
if ( buildTrustRegionFisherStep ( information , singleColumn , coordinates ,
damping , trustRadius , minimumStep , & trialStep , & trialPrediction ) & &
trialPrediction > * predictedReduction ) {
* selected = singleColumn ;
* step = trialStep ;
* predictedReduction = trialPrediction ;
}
}
return ! selected - > isEmpty ( ) ;
}
// 每次得到有效真实候选后,使用满足最新割线条件的秩一修正更新完整残差
// 每次得到有效真实候选后,使用满足最新割线条件的秩一修正更新完整残差
// Jacobian。这样模型吸收了刚得到的真实变化, 又不必立即逐参数重新试算。
// Jacobian。这样模型吸收了刚得到的真实变化, 又不必立即逐参数重新试算。
static void updateTrustRegionJacobian (
static void updateTrustRegionJacobian (
@ -1052,35 +1028,21 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
QTextStream out ( & metaFile ) ;
QTextStream out ( & metaFile ) ;
out < < " { \n " ;
out < < " { \n " ;
out < < " \" schema_version \" : 5 0 ,\n " ;
out < < " \" schema_version \" : 5 2 ,\n " ;
out < < " \" strategy \" : \" permeability_height_then_ shape_then_joint_lm \" , \n " ;
out < < " \" strategy \" : \" permeability_height_then_ joint_lm_shape_then_total \" , \n " ;
out < < " \" height_acceptance \" : \" reliable_vertical_loss_decrease; no_shape_or_total_loss_constraint \" , \n " ;
out < < " \" height_acceptance \" : \" reliable_vertical_loss_decrease; no_shape_or_total_loss_constraint \" , \n " ;
out < < " \" shape_priority \" : \" wellbore_parameter_sweep_then_non_wellbore_sweep_then_optional_wellbore_recheck \" , \n " ;
out < < " \" shape_metric \" : \" pressure_and_derivative_log_slopes_shared_sampling_grid_lag_round_10_percent_min_1 \" , \n " ;
out < < " \" shape_metric \" : \" pressure_and_derivative_log_slopes_shared_sampling_grid_lag_round_10_percent_min_1 \" , \n " ;
out < < " \" early_parallel_metric \" : \" pressure_and_derivative_log_slopes_first_window_shared_sampling_grid_lag_round_10_percent_min_1 \" , \n " ;
out < < " \" shape_parameter_selection \" : \" all_valid_free_columns_joint_LM; no_single_parameter_sweep_or_wellbore_recheck \" , \n " ;
out < < " \" early_gap_metric \" : \" weighted_rms_log10_pressure_derivative_ratio_error_first_window_shared_sampling_grid \" , \n " ;
out < < " \" shape_acceptance \" : \" strict_full_shape_loss_decrease; no_total_loss_constraint \" , \n " ;
out < < " \" early_gap_bias_metric \" : \" weighted_mean_signed_log10_gap_simulation_minus_target_first_window \" , \n " ;
out < < " \" lm_step_policy \" : \" same_joint_LM_damping_trust_radius_secant_updates_and_sensitivity_rebuilds_in_both_phases \" , \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 < < " \" lm_phase_switch \" : \" shape_target_reached_or_confirmed_stagnation_or_phase_budget_exhausted_then_total; user_stop_and_consecutive_solver_failure_abort \" , \n " ;
out < < " \" early_initial_step_policy \" : \" same_as_later_shape_sweep; initial_LM_coordinate_radius_0.24; no_cached_probe_acceptance \" , \n " ;
out < < " \" lm_phase_budget \" : \" each_phase_has_max_iterations_and_max(dimensions+2,20,3*max_iterations)_evaluations; independent_of_height_prealignment \" , \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 < < " \" lm_effective_improvement \" : \" max(0.00001,0.002*baseline_stage_error); 3_ineffective_steps_trigger_stagnation_confirmation \" , \n " ;
out < < " \" early_wellbore_acceptance \" : \" first_window_shape_decrease; no_gap_direction_constraint; wellbore_recheck_retains_full_shape_guard \" , \n " ;
out < < " \" iteration_count_scope \" : \" max_iterations_applies_independently_to_each_LM_phase; trace_iteration_is_cumulative \" , \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 \" : \" 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 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; 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_stage_shape_constraint \" : false, \n " ;
out < < " \" total_parameter_selection \" : \" all_valid_free_columns \" , \n " ;
out < < " \" total_parameter_selection \" : \" all_valid_free_columns \" , \n " ;
out < < " \" skin_difference_policy \" : \" local_scale_ in_all_stag es\" , \n " ;
out < < " \" skin_difference_policy \" : \" local_scale_fraction_0.02_in_both_LM_phases \" , \n " ;
out < < " \" stage2_ difference_policy\" : \" coordinate_step_cap_0. 10_with_full_trust_radius; log_parameter_ratio_cap_1.50; skin_local_scale_fraction_0.10; total_stage_unchanged \" , \n " ;
out < < " \" difference_policy \" : \" coordinate_step_cap_0.04_with_half_trust_radius_in_both_LM_phases \" , \n " ;
out < < " \" difference_failure_policy \" : \" halve_before_opposite_direction \" , \n " ;
out < < " \" difference_failure_policy \" : \" halve_before_opposite_direction \" , \n " ;
out < < " \" trace_type \" : \" finite_difference_lm_trust_region \" , \n " ;
out < < " \" trace_type \" : \" finite_difference_lm_trust_region \" , \n " ;
out < < " \" run_id \" : " < < jsonEscape ( m_traceRunId ) < < " , \n " ;
out < < " \" run_id \" : " < < jsonEscape ( m_traceRunId ) < < " , \n " ;
@ -1818,10 +1780,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return LM_OPTIMIZATION_FAILED ;
return LM_OPTIMIZATION_FAILED ;
}
}
// 用户迭代设置只约束第三阶段;真实评价额度也在进入该阶段时独立起算。
// 形状与总误差共用原联合 LM, 每段独立使用原迭代和求解额度。
// 前期调整按改善情况结束,不能提前消耗掉最终联合 LM 的迭代和求解机会。
const int phaseEvaluationBudget = qMax ( dimensions + 2 , qMax ( 20 , m_maxIterations * 3 ) ) ;
const int totalEvaluationBudget = qMax ( dimensions + 2 , qMax ( 20 , m_maxIterations * 3 ) ) ;
int maximumEvaluations = 0 ;
int maximumEvaluations = m_totalEvaluations + totalEvaluationBudget ;
int phaseIterations = 0 ;
int shapeIterations = 0 ;
int totalIterations = 0 ;
int totalIterations = 0 ;
// 下列步长均位于归一化内部坐标: 0.04 表示参数范围的 4%,信赖半径
// 下列步长均位于归一化内部坐标: 0.04 表示参数范围的 4%,信赖半径
// 限制一次联合移动的二范数,相关性门槛用于排除响应近乎共线的参数。
// 限制一次联合移动的二范数,相关性门槛用于排除响应近乎共线的参数。
@ -1829,9 +1792,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
const double minimumCoordinateStep = 1.0e-5 ;
const double minimumCoordinateStep = 1.0e-5 ;
const double minimumTrustRadius = 2.0e-3 ;
const double minimumTrustRadius = 2.0e-3 ;
const double maximumTrustRadius = 0.30 ;
const double maximumTrustRadius = 0.30 ;
// 第二阶段两组逐参数调整共用较大步幅;第三阶段继续使用原来的半径和预测验收规则。
const double initialShapeTrustRadius = 0.24 ;
const double maximumShapeTrustRadius = 0.60 ;
// 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。
// 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。
// 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。
// 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。
const double effectiveRelativeImprovement = 2.0e-3 ;
const double effectiveRelativeImprovement = 2.0e-3 ;
@ -1856,7 +1816,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
StopReasonLM stopReason = LM_MAX_ITERATIONS ;
StopReasonLM stopReason = LM_MAX_ITERATIONS ;
// jacobian 的行对应固定采样的残差,列对应用户勾选的参数。
// jacobian 的行对应固定采样的残差,列对应用户勾选的参数。
// Fisher 按当前 阶段取数值或形状残差行;冻结列不参与形状差分,整体阶段重建全部列 。
// Fisher 按当前 目标取数值或形状残差行,两段均使用所有勾选参数 。
QVector < QVector < double > > jacobian ;
QVector < QVector < double > > jacobian ;
QVector < bool > jacobianColumnValid ( dimensions , false ) ;
QVector < bool > jacobianColumnValid ( dimensions , false ) ;
@ -1960,9 +1920,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
emit progressUpdated ( - 1 , current . fitness ) ;
emit progressUpdated ( - 1 , current . fitness ) ;
emit logMessageGenerated ( tr ( " === Starting LM Main Loop === " ) ) ;
emit logMessageGenerated ( tr ( " === Starting LM Main Loop === " ) ) ;
emit logMessageGenerated (
emit logMessageGenerated (
tr ( " LM starting point error: %1; independent total-stage evaluation budget: %2" )
tr ( " LM starting point error: %1; evaluation budget per LM phase : %2" )
. arg ( current . fitness , 0 , ' e ' , 4 )
. arg ( current . fitness , 0 , ' e ' , 4 )
. arg ( total EvaluationBudget) ) ;
. arg ( phase EvaluationBudget) ) ;
// 高度预调整只改变用户勾选的渗透率。ln(k) 的初始变化由有符号高度差
// 高度预调整只改变用户勾选的渗透率。ln(k) 的初始变化由有符号高度差
// 给出,真实求解后用割线估计修正;拒绝时缩步,不让其他参数补偿高度。
// 给出,真实求解后用割线估计修正;拒绝时缩步,不让其他参数补偿高度。
@ -2027,211 +1987,25 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if ( m_shouldStop ) return LM_USER_STOPPED ;
if ( m_shouldStop ) return LM_USER_STOPPED ;
// 第二阶段先调井筒参数、再调其他参数;各组共用单参数扫描,每个参数只访问一次。
// 原逐参数第二阶段已移除;同一个联合 LM 先匹配整体形状,再匹配总误差。
// 普通形状调整冻结井储、表皮,只按形状验收;总误差达标只在第三阶段判断。
bool shapeStage = true ;
bool shapeStage = true ;
bool singleShapeStarted = false ;
int shapeStepCount = 0 ;
QVector < bool > shapeParameterFinished ( dimensions , false ) ;
int activeShapeColumn = - 1 ;
double shapeCoordinateStep = 0.0 ;
int consecutiveShapeRejections = 0 ;
const int maximumShapeRejections = 3 ;
const int storageColumn = m_enabledParamIndices . indexOf ( 2 ) ;
const int skinColumn = m_enabledParamIndices . indexOf ( 1 ) ;
bool hasRemainingShapeParameters = false ;
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( m_enabledParamIndices [ column ] ! = 1 & & m_enabledParamIndices [ column ] ! = 2 )
hasRemainingShapeParameters = true ;
}
const int wellboreParameterCount = ( storageColumn > = 0 ? 1 : 0 ) + ( skinColumn > = 0 ? 1 : 0 ) ;
bool wellboreRecheck = false ;
bool wellboreRecheckDone = false ;
double earlyWellboreBaseline = current . breakdown . earlyParallelLoss ;
double recheckShapeLimit = 0.0 ;
// 井储、表皮在组内按预测下降量选序;回检沿用第一窗口指标和相同扫描规则。
bool earlyShapeStage = shapeStage & & wellboreParameterCount > 0 ;
trustRadius = initialShapeTrustRadius ;
auto parameterAllowedInStage = [ & ] ( int column ) - > bool {
const int parameterIndex = m_enabledParamIndices [ column ] ;
// 各子阶段只开放对应参数组,实际候选始终只改变选中的一列。
if ( ! shapeStage ) return true ;
if ( earlyShapeStage ) return parameterIndex = = 1 | | parameterIndex = = 2 ;
return parameterIndex ! = 1 & & parameterIndex ! = 2 ;
} ;
auto stageError = [ & ] ( const TrustRegionEvaluation & point ) - > double {
auto stageError = [ & ] ( const TrustRegionEvaluation & point ) - > double {
if ( earlyShapeStage ) return point . breakdown . earlyParallelLoss ;
return shapeStage ? point . breakdown . shapeLoss : point . fitness ;
return shapeStage ? point . breakdown . shapeLoss : point . fitness ;
} ;
} ;
auto acceptable = [ & ] ( const TrustRegionEvaluation & point , const TrustRegionEvaluation & base ) - > bool {
auto acceptable = [ & ] ( const TrustRegionEvaluation & point , const TrustRegionEvaluation & base ) - > bool {
if ( ! point . valid ) return false ;
return point . valid & & stageError ( point ) < stageError ( base ) ;
if ( earlyShapeStage ) {
// 第一窗口形状决定是否改善;补调保留原有的整体形状保护。
return point . breakdown . earlyParallelLoss < base . breakdown . earlyParallelLoss & &
( ! wellboreRecheck | | point . breakdown . shapeLoss < = recheckShapeLimit ) ;
}
return shapeStage
? point . breakdown . shapeLoss < base . breakdown . shapeLoss
// 预调整结束后恢复原 LM: 有效候选只按整体误差下降接受。
: point . fitness < base . fitness ;
} ;
} ;
auto acceptPoint = [ & ] ( const TrustRegionEvaluation & point ) {
auto acceptPoint = [ & ] ( const TrustRegionEvaluation & point ) {
// 只发布通过当前阶段目标验收的真实候选 。
// 参数、曲线和诊断同步发布,两个目标仅改变候选验收指标。
current = point ;
current = point ;
publishAcceptedPoint ( current ) ;
publishAcceptedPoint ( current ) ;
restoreEvaluationState ( current ) ;
restoreEvaluationState ( current ) ;
} ;
} ;
emit logMessageGenerated ( shapeStage
? 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 ( 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 ) ;
double effectiveImprovementBaseline = stageError ( current ) ;
emit logMessageGenerated ( tr ( " LM sampling: %1 points (%2 intervals per log-time decade). " )
emit logMessageGenerated ( tr ( " LM sampling: %1 points (%2 intervals per log-time decade). " )
. arg ( current . breakdown . residualVector . size ( ) / 2 ) . arg ( kAutoFitIntervalsPerDecade ) ) ;
. arg ( current . breakdown . residualVector . size ( ) / 2 ) . arg ( kAutoFitIntervalsPerDecade ) ) ;
auto reuseSensitivityForNextStage = [ & ] ( ) {
// 更换目标时复用完整 J, 清除旧目标下的拒绝和预测失准计数。
rebuildRequested = jacobian . isEmpty ( ) | | consecutiveSolverFailures > 0 | | acceptedSinceRebuild > = 10 ;
if ( jacobian . isEmpty ( ) ) rebuildReason = QT_TR_NOOP ( " no valid sensitivity model " ) ;
else if ( consecutiveSolverFailures > 0 ) rebuildReason = QT_TR_NOOP ( " solver failure before stage switch " ) ;
else if ( acceptedSinceRebuild > = 10 ) rebuildReason = QT_TR_NOOP ( " 10 accepted steps since last rebuild " ) ;
consecutivePoorPredictions = 0 ;
modelRebuiltAtMinimumRadius = false ;
consecutiveIneffectiveSteps = 0 ;
consecutiveRejectedSteps = 0 ;
consecutiveSolverFailures = 0 ;
stagnationConfirmationRequested = false ;
globalFallbackAttempted = false ;
trustRadius = shapeStage ? initialShapeTrustRadius : 0.12 ;
damping = 0.01 ;
} ;
auto enterTotalStage = [ & ] ( const QString & reason ) {
const bool finishedRecheck = wellboreRecheck ;
totalIterations = 0 ;
maximumEvaluations = m_totalEvaluations + totalEvaluationBudget ;
shapeStage = false ;
earlyShapeStage = false ;
wellboreRecheck = false ;
activeShapeColumn = - 1 ;
shapeCoordinateStep = 0.0 ;
consecutiveShapeRejections = 0 ;
effectiveImprovementBaseline = current . fitness ;
reuseSensitivityForNextStage ( ) ;
// 第二阶段只测各子阶段需要的列;所有出口都在当前点重建完整 J, 不能把缺列模型带入整体 LM。
jacobian . clear ( ) ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " full sensitivity at total-stage entry " ) ;
emit progressUpdated ( 0 , current . fitness ) ;
emit logMessageGenerated ( tr ( " Total-stage budget starts now: %1 iterations, %2 evaluations; pre-adjustment is counted separately. " )
. arg ( m_maxIterations ) . arg ( totalEvaluationBudget ) ) ;
emit logMessageGenerated ( tr ( " Shape stage ended: %1 " ) . arg ( reason ) ) ;
emit logMessageGenerated ( tr ( " LM stage 3: original LM fitting; accept by total error only. " ) ) ;
writeTraceRow ( m_currentIteration , - 1 , " stage_switch " , current . parameters , current . fitness ,
true , 0 , finishedRecheck ? " wellbore_recheck_to_total " : " shape_to_total " , & current . breakdown ) ;
} ;
auto finishShapeStage = [ & ] ( const QString & reason ) {
if ( m_shouldStop ) return ;
// 只检查一次;用绝对加相对容差区分有意义的退化和接近零时的比例放大。
if ( wellboreRecheckDone | | wellboreParameterCount = = 0 | | ! hasRemainingShapeParameters ) {
enterTotalStage ( reason ) ;
return ;
}
wellboreRecheckDone = true ;
const double earlyTolerance = qMax ( 1.0e-4 , 0.10 * earlyWellboreBaseline ) ;
if ( current . breakdown . earlyParallelLoss < = earlyWellboreBaseline + earlyTolerance ) {
writeTraceRow ( m_currentIteration , - 1 , " stage_switch " , current . parameters , current . fitness ,
true , 0 , " wellbore_recheck_skipped_no_degradation " , & current . breakdown ) ;
enterTotalStage ( reason ) ;
return ;
}
wellboreRecheck = true ;
earlyShapeStage = true ;
// 回检只约束形状:允许总误差上升,但保护其他参数已调好的整体形状。
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 ;
rebuildReason = QT_TR_NOOP ( " new sensitivity for wellbore recheck " ) ;
emit logMessageGenerated ( tr ( " Wellbore recheck: early shape error=%1, shape limit=%2. " )
. arg ( current . breakdown . earlyParallelLoss , 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 ) ;
} ;
auto finishEarlyShapeStage = [ & ] ( const QString & reason ) {
if ( m_shouldStop ) return ;
// 井筒参数整组完成后才切换目标,不能把旧组的活动参数或步长带入下一组。
activeShapeColumn = - 1 ;
shapeCoordinateStep = 0.0 ;
consecutiveShapeRejections = 0 ;
if ( wellboreRecheck ) {
enterTotalStage ( reason ) ;
return ;
}
earlyShapeStage = false ;
earlyWellboreBaseline = current . breakdown . earlyParallelLoss ;
emit logMessageGenerated ( tr ( " Early wellbore adjustment ended: %1 " ) . arg ( reason ) ) ;
if ( ! hasRemainingShapeParameters ) {
enterTotalStage ( tr ( " no remaining shape parameters " ) ) ;
return ;
}
effectiveImprovementBaseline = current . breakdown . shapeLoss ;
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 ,
true , 0 , " early_wellbore_to_shape " , & current . breakdown ) ;
} ;
auto finishShapeParameter = [ & ] ( const QString & reason ) {
// 已完成的参数不再参与后续选优,保证所有可调参数只扫一遍。
if ( activeShapeColumn > = 0 ) {
shapeParameterFinished [ activeShapeColumn ] = true ;
writeTraceRow ( m_currentIteration , activeShapeColumn , " shape_parameter_end " ,
current . parameters , current . fitness , true , 0 , reason , & current . breakdown ) ;
}
activeShapeColumn = - 1 ;
shapeCoordinateStep = 0.0 ;
consecutiveShapeRejections = 0 ;
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( parameterAllowedInStage ( column ) & & ! shapeParameterFinished [ column ] ) return ;
}
if ( earlyShapeStage ) finishEarlyShapeStage ( tr ( " all wellbore parameters visited once " ) ) ;
else finishShapeStage ( tr ( " all shape parameters visited once " ) ) ;
} ;
auto completeShapeSearchStep = [ & ] ( bool accepted ) {
if ( ! shapeStage | | m_shouldStop ) return ;
// 两组参数共用扩缩步规则:改善后放大,拒绝后从已接受点缩步重试。
+ + shapeStepCount ;
consecutiveShapeRejections = accepted ? 0 : consecutiveShapeRejections + 1 ;
shapeCoordinateStep = qBound ( - maximumShapeTrustRadius ,
shapeCoordinateStep * ( accepted ? 2.0 : 0.5 ) , maximumShapeTrustRadius ) ;
writeTraceRow ( m_currentIteration , activeShapeColumn , " shape_step_end " , current . parameters ,
current . fitness , true , 0 ,
QString ( " %1_step_%2_rejections_%3_next_delta_%4 " )
. arg ( accepted ? " improved " : " rejected " ) . arg ( shapeStepCount )
. arg ( consecutiveShapeRejections ) . arg ( shapeCoordinateStep , 0 , ' g ' , 12 ) , & current . breakdown ) ;
if ( consecutiveShapeRejections > = maximumShapeRejections )
finishShapeParameter ( " 3_consecutive_non_improving_trials " ) ;
else if ( qAbs ( shapeCoordinateStep ) < minimumCoordinateStep )
finishShapeParameter ( " minimum_coordinate_step " ) ;
} ;
auto registerEffectiveImprovement = [ & ] ( double fitness ) - > bool {
auto registerEffectiveImprovement = [ & ] ( double fitness ) - > bool {
if ( shapeStage ) return false ;
const double requiredImprovement = qMax (
const double requiredImprovement = qMax (
effectiveAbsoluteImprovement ,
effectiveAbsoluteImprovement ,
qAbs ( effectiveImprovementBaseline ) * effectiveRelativeImprovement ) ;
qAbs ( effectiveImprovementBaseline ) * effectiveRelativeImprovement ) ;
@ -2248,12 +2022,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return true ;
return true ;
} ;
} ;
// 第二阶段求解失败也进入当前参数的拒绝处理,第三阶段保留原 LM 停滞确认。
// 两个目标均沿用原 LM 的有效改善门槛及重建后 停滞确认。
auto recordIneffectiveStep = [ & ] ( ) - > bool {
auto recordIneffectiveStep = [ & ] ( ) - > bool {
if ( shapeStage ) {
completeShapeSearchStep ( false ) ;
return false ;
}
+ + consecutiveIneffectiveSteps ;
+ + consecutiveIneffectiveSteps ;
if ( ! globalFallbackAttempted ) {
if ( ! globalFallbackAttempted ) {
return false ;
return false ;
@ -2279,23 +2049,17 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
} ;
} ;
emit logMessageGenerated (
emit logMessageGenerated (
tr ( " Total-stage effective improvement threshold: max(%1, %2% of baseline error); "
tr ( " LM effective improvement threshold: max(%1, %2% of baseline error); "
" %3 consecutive ineffective steps trigger convergence confirmation " )
" %3 consecutive ineffective steps trigger convergence confirmation " )
. arg ( effectiveAbsoluteImprovement , 0 , ' e ' , 2 )
. arg ( effectiveAbsoluteImprovement , 0 , ' e ' , 2 )
. arg ( effectiveRelativeImprovement * 100.0 , 0 , ' f ' , 2 )
. arg ( effectiveRelativeImprovement * 100.0 , 0 , ' f ' , 2 )
. arg ( maximumIneffectiveSteps ) ) ;
. arg ( maximumIneffectiveSteps ) ) ;
if ( ! shapeStage & & current . fitness < m_targetError ) {
return LM_TARGET_ACHIEVED ;
}
// 在同一个真实工作点逐参数做单边差分。失败先在原方向缩步,再反向尝试;
// 在同一个真实工作点逐参数做单边差分。失败先在原方向缩步,再反向尝试;
// 正常情况下每列仍只需一次真实求解,重试也计入总评价预算。
// 正常情况下每列仍只需一次真实求解,重试也计入总评价预算。
auto rebuildSensitivity = [ & ] ( ) - > bool {
auto rebuildSensitivity = [ & ] ( ) - > bool {
// 第二阶段差分不受第三阶段额度限制;每列仍只有有限的缩步和反向重试。
// 形状段也受原 LM 求解额度约束,差分范围及重试规则与总误差段相同。
const int evaluationLimit = shapeStage ? ( std : : numeric_limits < int > : : max ) ( ) : maximumEvaluations ;
const int evaluationLimit = maximumEvaluations ;
const int sensitivityColumnCount = shapeStage
? ( earlyShapeStage ? wellboreParameterCount : dimensions - wellboreParameterCount ) : dimensions ;
const TrustRegionEvaluation base = current ;
const TrustRegionEvaluation base = current ;
const QVector < double > baseResidual = trustRegionFullResidual ( base . breakdown ) ;
const QVector < double > baseResidual = trustRegionFullResidual ( base . breakdown ) ;
const int residualCount = baseResidual . size ( ) ;
const int residualCount = baseResidual . size ( ) ;
@ -2310,11 +2074,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
TrustRegionEvaluation bestProbe ;
TrustRegionEvaluation bestProbe ;
int bestProbeColumn = - 1 ;
int bestProbeColumn = - 1 ;
double bestProbeDelta = 0.0 ;
double bestProbeDelta = 0.0 ;
// 第二阶段允许内部坐标范围的 10% 及完整信赖半径,避免较大比例试探被旧上限截小。
// 两段共用原 LM 的局部差分尺度,避免目标切换同时改变灵敏度算法。
// 第三阶段仍为 4% 及半个信赖半径;均保留 0.5% 下限以减少数值噪声影响。
const double finiteDifferenceStep = qMin (
const double finiteDifferenceStep = qMin (
shapeStage ? 0.10 : sensitivityStep ,
sensitivityStep , qMax ( 5.0e-3 , trustRadius * 0.5 ) ) ;
qMax ( 5.0e-3 , trustRadius * ( shapeStage ? 1.0 : 0.5 ) ) ) ;
for ( int column = 0 ;
for ( int column = 0 ;
column < dimensions & &
column < dimensions & &
@ -2322,19 +2084,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
processPauseAndStop ( ) ;
processPauseAndStop ( ) ;
+ + column ) {
+ + column ) {
const int parameterIndex = m_enabledParamIndices [ column ] ;
const int parameterIndex = m_enabledParamIndices [ column ] ;
// 每组入口只测该组开放的参数;回检和整体阶段会重新差分。
if ( shapeStage & & ! parameterAllowedInStage ( column ) ) continue ;
const double lower = m_parameterLower [ parameterIndex ] ;
const double lower = m_parameterLower [ parameterIndex ] ;
const double upper = m_parameterUpper [ parameterIndex ] ;
const double upper = m_parameterUpper [ parameterIndex ] ;
if ( upper < = lower ) continue ;
if ( upper < = lower ) continue ;
// 第二阶段扩大形状试探范围;第三阶段仍沿用原来的局部差分尺度。
// 表皮包含零和负值,沿用原 LM 按局部物理尺度扰动的方式。
// 表皮包含零和负值,按物理尺度扰动;正值参数按对数比例限制幅度。
double localStep = finiteDifferenceStep ;
double localStep = finiteDifferenceStep ;
if ( parameterIndex = = 1 ) {
if ( parameterIndex = = 1 ) {
const double skinStepFraction = shapeStage ? 0.10 : 0.02 ;
localStep = qMin ( localStep , 0.02 * qMax ( 0.1 , qAbs ( base . parameters [ column ] ) ) / ( upper - lower ) ) ;
localStep = qMin ( localStep , skinStepFraction * qMax ( 0.1 , qAbs ( base . parameters [ column ] ) ) / ( upper - lower ) ) ;
} else if ( shapeStage & & useTrustRegionLogScale ( parameterIndex , lower , upper ) ) {
localStep = qMin ( localStep , qLn ( 1.50 ) / ( qLn ( upper ) - qLn ( lower ) ) ) ;
}
}
double positiveRoom = 1.0 - base . coordinates [ column ] ;
double positiveRoom = 1.0 - base . coordinates [ column ] ;
double negativeRoom = base . coordinates [ column ] ;
double negativeRoom = base . coordinates [ column ] ;
@ -2382,7 +2139,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
probe . fitness ,
probe . fitness ,
probe . valid ,
probe . valid ,
probe . elapsedMs ,
probe . elapsedMs ,
( wellboreRecheck ? " wellbore_recheck_ " : ( shapeStage ? " shape_ " : " total_ " ) ) + decision ,
( shapeStage ? " shape_ " : " total_ " ) + decision ,
probe . valid ? & probe . breakdown : nullptr ) ;
probe . valid ? & probe . breakdown : nullptr ) ;
if ( ! probe . valid ) {
if ( ! probe . valid ) {
@ -2411,9 +2168,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
jacobianColumnValid [ column ] = true ;
jacobianColumnValid [ column ] = true ;
columnBuilt = true ;
columnBuilt = true ;
// 第二阶段探针只用于灵敏度;只有第三阶段保留直接接受缓存探针的规则。
// 沿用原 LM 的缓存探针验收,仅按当前段的目标选择更优探针。
if ( ! shapeStage & &
if ( acceptable ( probe , base ) & &
parameterAllowedInStage ( column ) & & acceptable ( probe , base ) & &
( ! bestProbe . valid | | stageError ( probe ) < stageError ( bestProbe ) ) ) {
( ! bestProbe . valid | | stageError ( probe ) < stageError ( bestProbe ) ) ) {
bestProbe = probe ;
bestProbe = probe ;
bestProbeColumn = column ;
bestProbeColumn = column ;
@ -2425,20 +2181,18 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
}
}
int validColumnCount = 0 ;
int validColumnCount = 0 ;
bool hasActiveSensitivity = false ;
for ( int i = 0 ; i < jacobianColumnValid . size ( ) ; + + i ) {
for ( int i = 0 ; i < jacobianColumnValid . size ( ) ; + + i ) {
if ( jacobianColumnValid [ i ] ) {
if ( jacobianColumnValid [ i ] ) {
+ + validColumnCount ;
+ + validColumnCount ;
if ( parameterAllowedInStage ( i ) ) hasActiveSensitivity = true ;
}
}
}
}
// 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度交给逐参数选步处理 。
// 两段都要求至少一列有效灵敏度,零梯度仍交给原 LM 停滞判断 。
if ( validColumnCount = = 0 | | ( shapeStage & & ! hasActiveSensitivity ) | | m_shouldStop ) {
if ( validColumnCount = = 0 | | m_shouldStop ) {
restoreEvaluationState ( base ) ;
restoreEvaluationState ( base ) ;
return false ;
return false ;
}
}
// 只有整体阶段可接受更优缓存探针,第二阶段两组参数都由单参数 LM 选步 。
// 两段均保留原 LM 接受更优缓存探针的行为 。
// 所有列先基于同一个 base 建完,再用已知割线平移 Jacobian, 避免边算边移动基点。
// 所有列先基于同一个 base 建完,再用已知割线平移 Jacobian, 避免边算边移动基点。
if ( bestProbe . valid & & bestProbeColumn > = 0 ) {
if ( bestProbe . valid & & bestProbeColumn > = 0 ) {
QVector < double > acceptedStep ( dimensions , 0.0 ) ;
QVector < double > acceptedStep ( dimensions , 0.0 ) ;
@ -2456,10 +2210,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
current . fitness ,
current . fitness ,
true ,
true ,
0 ,
0 ,
" total_accepted_cached_probe " ,
shapeStage ? " shape_accepted_cached_probe " : " total_accepted_cached_probe " ,
& current . breakdown ) ;
& current . breakdown ) ;
emit logMessageGenerated (
emit logMessageGenerated (
tr ( " Sensitivity probe accepted: total error=%1" )
tr ( " Sensitivity probe accepted: current objective error=%1" )
. arg ( stageError ( current ) , 0 , ' e ' , 4 ) ) ;
. arg ( stageError ( current ) , 0 , ' e ' , 4 ) ) ;
} else {
} else {
restoreEvaluationState ( current ) ;
restoreEvaluationState ( current ) ;
@ -2477,61 +2231,65 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
emit logMessageGenerated (
emit logMessageGenerated (
tr ( " Sensitivity model rebuilt: %1/%2 parameter columns valid " )
tr ( " Sensitivity model rebuilt: %1/%2 parameter columns valid " )
. arg ( validColumnCount )
. arg ( validColumnCount )
. arg ( sensitivityColumnCount ) ) ;
. arg ( dimensions ) ) ;
return true ;
return true ;
} ;
} ;
int completedIterations = 0 ;
int completedIterations = 0 ;
for ( int iteration = 0 ;
for ( int phase = 0 ; phase < 2 & & ! m_shouldStop ; + + phase ) {
! m_shouldStop & & ( shapeStage | |
shapeStage = phase = = 0 ;
( totalIterations < m_maxIterations & & m_totalEvaluations < maximumEvaluations ) ) ;
phaseIterations = 0 ;
maximumEvaluations = m_totalEvaluations + phaseEvaluationBudget ;
stopReason = LM_MAX_ITERATIONS ;
effectiveImprovementBaseline = stageError ( current ) ;
trustRadius = 0.12 ;
damping = 0.01 ;
consecutiveRejectedSteps = 0 ;
consecutiveSolverFailures = 0 ;
acceptedSinceRebuild = 0 ;
consecutiveIneffectiveSteps = 0 ;
consecutivePoorPredictions = 0 ;
modelRebuiltAtMinimumRadius = false ;
stagnationConfirmationRequested = false ;
globalFallbackAttempted = false ;
jacobian . clear ( ) ;
rebuildRequested = true ;
rebuildReason = shapeStage ? QT_TR_NOOP ( " full sensitivity at shape LM entry " )
: QT_TR_NOOP ( " full sensitivity at total-stage entry " ) ;
emit progressUpdated ( 0 , current . fitness ) ;
emit logMessageGenerated ( shapeStage
? tr ( " Joint LM shape phase: adjust all selected parameters by full shape error. " )
: tr ( " Joint LM total phase: adjust all selected parameters by total error. " ) ) ;
emit logMessageGenerated ( tr ( " LM phase budget: %1 iterations, %2 evaluations. " )
. arg ( m_maxIterations ) . arg ( phaseEvaluationBudget ) ) ;
writeTraceRow ( completedIterations , - 1 , " stage_switch " , current . parameters , current . fitness ,
true , 0 , shapeStage ? " height_to_shape_lm " : " shape_to_total " , & current . breakdown ) ;
for ( int iteration = completedIterations ;
! m_shouldStop & & phaseIterations < m_maxIterations & & m_totalEvaluations < maximumEvaluations ;
+ + iteration ) {
+ + iteration ) {
m_currentIteration = iteration ;
m_currentIteration = iteration ;
completedIterations = iteration + 1 ;
completedIterations = iteration + 1 ;
if ( ! processPauseAndStop ( ) ) break ;
if ( ! processPauseAndStop ( ) ) {
if ( stageError ( current ) < m_targetError ) {
stopReason = LM_TARGET_ACHIEVED ;
break ;
break ;
}
}
if ( wellboreRecheck & & current . breakdown . earlyParallelLoss < =
earlyWellboreBaseline + qMax ( 1.0e-4 , 0.10 * earlyWellboreBaseline ) )
enterTotalStage ( tr ( " early shape error restored within tolerance " ) ) ;
if ( shapeStage & & ! earlyShapeStage & & ! singleShapeStarted ) {
singleShapeStarted = true ;
// 初调已改变井储/表皮,普通形状必须在新的工作点重测其余可调参数的响应。
jacobian . clear ( ) ;
reuseSensitivityForNextStage ( ) ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " fresh sensitivity at single-parameter shape entry " ) ;
}
if ( m_shouldStop ) break ;
if ( ! shapeStage & & ( totalIterations > = m_maxIterations | | m_totalEvaluations > = maximumEvaluations ) ) break ;
if ( rebuildRequested ) {
if ( rebuildRequested ) {
emit logMessageGenerated ( tr ( " Rebuilding sensitivity model: %1 " ) . arg ( tr ( rebuildReason ) ) ) ;
emit logMessageGenerated ( tr ( " Rebuilding sensitivity model: %1 " ) . arg ( tr ( rebuildReason ) ) ) ;
writeTraceRow ( m_currentIteration , - 1 , " sensitivity_rebuild " , current . parameters ,
writeTraceRow ( m_currentIteration , - 1 , " sensitivity_rebuild " , current . parameters ,
current . fitness , true , 0 , rebuildReason , & current . breakdown ) ;
current . fitness , true , 0 , rebuildReason , & current . breakdown ) ;
if ( ! rebuildSensitivity ( ) ) {
if ( ! rebuildSensitivity ( ) ) {
if ( shapeStage & & ! m_shouldStop ) {
if ( earlyShapeStage ) {
finishEarlyShapeStage ( tr ( " no valid early sensitivity model " ) ) ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " no valid sensitivity model " ) ;
continue ;
}
// 真实差分全失败不能冒充停滞收敛。
m_lastError = tr ( " Unable to build a valid shape sensitivity model. " ) ;
stopReason = LM_OPTIMIZATION_FAILED ;
break ;
}
stopReason = m_shouldStop
stopReason = m_shouldStop
? LM_USER_STOPPED
? LM_USER_STOPPED
: LM_LOCAL_OPTIMUM ;
: LM_LOCAL_OPTIMUM ;
break ;
break ;
}
}
if ( ! shapeStage & & current . fitness < m_targetError ) {
if ( stageError ( current ) < m_targetError ) {
stopReason = LM_TARGET_ACHIEVED ;
stopReason = LM_TARGET_ACHIEVED ;
break ;
break ;
}
}
if ( ! shapeStage & & m_totalEvaluations > = maximumEvaluations ) {
if ( m_totalEvaluations > = maximumEvaluations ) {
stopReason = LM_MAX_ITERATIONS ;
stopReason = LM_MAX_ITERATIONS ;
break ;
break ;
}
}
@ -2539,16 +2297,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
registerEffectiveImprovement ( stageError ( current ) ) ;
registerEffectiveImprovement ( stageError ( current ) ) ;
}
}
// 第三阶段独立计数;方向不可行也消耗一次局部尝试,不能无限缩步循环。
// 两段分别计数;无可行方向也消耗一次尝试,避免无限缩步重建。
if ( ! shapeStage ) + + totalIterations ;
+ + phaseIterations ;
// 每次从最新 J 和当前阶段残差重算全局信息,供单参数或联合 LM 求步。
const QVector < double > objectiveResidual = shapeStage
const QVector < double > objectiveResidual = earlyShapeStage
? current . breakdown . shapeResiduals : current . breakdown . residualVector ;
? current . breakdown . earlyParallelResiduals
const int rowOffset = shapeStage ? current . breakdown . residualVector . size ( ) : 0 ;
: ( shapeStage ? current . breakdown . shapeResiduals : current . breakdown . residualVector ) ;
const int rowOffset = earlyShapeStage
? current . breakdown . residualVector . size ( ) + current . breakdown . shapeResiduals . size ( ) +
current . breakdown . earlyValueResiduals . size ( )
: ( shapeStage ? current . breakdown . residualVector . size ( ) : 0 ) ;
const QVector < QVector < double > > objectiveJacobian = jacobian . mid ( rowOffset , objectiveResidual . size ( ) ) ;
const QVector < QVector < double > > objectiveJacobian = jacobian . mid ( rowOffset , objectiveResidual . size ( ) ) ;
QVector < double > objectiveCoordinates ;
QVector < double > objectiveCoordinates ;
if ( shapeStage ) {
if ( shapeStage ) {
@ -2560,79 +2313,23 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
}
}
const QVector < TrustRegionFisher > information = buildTrustRegionFisher (
const QVector < TrustRegionFisher > information = buildTrustRegionFisher (
objectiveJacobian , objectiveResidual , jacobianColumnValid , objectiveCoordinates ) ;
objectiveJacobian , objectiveResidual , jacobianColumnValid , objectiveCoordinates ) ;
// 前期残差已含第一窗口权重,使用全局和,不能再次乘局部窗口权重 。
// 两段都使用当前目标的全局信息,不按时间窗口另行选参 。
const TrustRegionFisher & global = information [ kAutoFitTimeWindowCount ] ;
const TrustRegionFisher & global = information [ kAutoFitTimeWindowCount ] ;
QVector < bool > stageColumnValid = jacobianColumnValid ;
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( ! parameterAllowedInStage ( column ) ) stageColumnValid [ column ] = false ;
}
QVector < int > selectedColumns ;
QVector < int > selectedColumns ;
QVector < double > coordinateStep ;
QVector < double > coordinateStep ;
double predictedReduction = 0.0 ;
double predictedReduction = 0.0 ;
const int selectedWindow = earlyShapeStage ? 0 : - 1 ;
// 第二阶段从当前组有效自由列中选一个,整体 LM 仍联合求解。
// 两段都联合调整全部有效自由参数,只切换残差及其对应的 Jacobian 行。
// 保留弱敏感、相关及边界列供预测比较;窗口只保留作误差诊断。
for ( int column = 0 ; column < dimensions ; + + column ) {
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( stage ColumnValid[ column ] & & global . matrix [ column ] [ column ] > 0.0 )
if ( jacobian ColumnValid[ column ] & & global . matrix [ column ] [ column ] > 0.0 )
selectedColumns . append ( column ) ;
selectedColumns . append ( column ) ;
}
}
if ( shapeStage ) {
// 首步由灵敏度选出尚未调过的参数;后续锁定方向,直接使用扩缩后的步长。
if ( activeShapeColumn < 0 ) {
QVector < int > availableColumns ;
for ( int i = 0 ; i < selectedColumns . size ( ) ; + + i ) {
if ( ! shapeParameterFinished [ selectedColumns [ i ] ] ) availableColumns . append ( selectedColumns [ i ] ) ;
}
buildBestSingleParameterStep ( global , availableColumns , current . coordinates ,
0.01 , initialShapeTrustRadius , minimumCoordinateStep ,
& selectedColumns , & coordinateStep , & predictedReduction ) ;
if ( ! selectedColumns . isEmpty ( ) ) {
activeShapeColumn = selectedColumns [ 0 ] ;
shapeCoordinateStep = coordinateStep [ activeShapeColumn ] ;
consecutiveShapeRejections = 0 ;
writeTraceRow ( m_currentIteration , activeShapeColumn , " shape_parameter_start " ,
current . parameters , current . fitness , true , 0 ,
" largest_predicted_reduction_unvisited " , & current . 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 ! = finishingEarlyShape ) break ;
}
- - iteration ;
continue ;
}
} else {
selectedColumns . clear ( ) ;
selectedColumns . append ( activeShapeColumn ) ;
coordinateStep . fill ( 0.0 , dimensions ) ;
shapeCoordinateStep = qBound ( 0.0 ,
current . coordinates [ activeShapeColumn ] + shapeCoordinateStep , 1.0 )
- current . coordinates [ activeShapeColumn ] ;
if ( qAbs ( shapeCoordinateStep ) < minimumCoordinateStep ) {
finishShapeParameter ( " parameter_bound_or_minimum_step " ) ;
- - iteration ;
continue ;
}
coordinateStep [ activeShapeColumn ] = shapeCoordinateStep ;
predictedReduction = - global . gradient [ activeShapeColumn ] * shapeCoordinateStep
- 0.5 * global . matrix [ activeShapeColumn ] [ activeShapeColumn ]
* shapeCoordinateStep * shapeCoordinateStep ;
}
} else {
// 第三阶段保持全部有效自由参数的联合 LM 调整。
globalFallbackAttempted = true ;
globalFallbackAttempted = true ;
if ( selectedColumns . isEmpty ( ) | | ! buildTrustRegionFisherStep (
if ( selectedColumns . isEmpty ( ) | | ! buildTrustRegionFisherStep (
global , selectedColumns , current . coordinates , damping ,
global , selectedColumns , current . coordinates , damping ,
trustRadius , minimumCoordinateStep , & coordinateStep , & predictedReduction ) ) {
trustRadius , minimumCoordinateStep , & coordinateStep , & predictedReduction ) ) {
selectedColumns . clear ( ) ;
selectedColumns . clear ( ) ;
}
}
}
// 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。
// 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。
if ( selectedColumns . isEmpty ( ) ) {
if ( selectedColumns . isEmpty ( ) ) {
@ -2662,20 +2359,12 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
for ( int i = 0 ; i < selectedColumns . size ( ) ; + + i ) {
for ( int i = 0 ; i < selectedColumns . size ( ) ; + + i ) {
selectedParameterIndices < < QString : : number ( m_enabledParamIndices [ selectedColumns [ i ] ] ) ;
selectedParameterIndices < < QString : : number ( m_enabledParamIndices [ selectedColumns [ i ] ] ) ;
}
}
QString selectionName = ( wellboreRecheck ? QString ( " recheck_ " ) : QString ( ) ) + QString ( earlyShapeStage
QString selectionName = QString ( shapeStage ? " shape_global_params_ " : " total_global_params_ " ) +
? ( m_enabledParamIndices [ selectedColumns [ 0 ] ] = = 2 ? " storage_parallel_ " : " skin_parallel_ " ) : ( shapeStage ? " shape_ " : " total_ " ) ) + ( selectedWindow > = 0
selectedParameterIndices . join ( " _ " ) ;
? QString ( " window_%1 " ) . arg ( selectedWindow + 1 ) : QString ( " global " ) ) +
" _params_ " + selectedParameterIndices . join ( " _ " ) ;
TrustRegionEvaluation candidate ;
TrustRegionEvaluation candidate ;
candidate . coordinates = candidateCoordinates ;
candidate . coordinates = candidateCoordinates ;
candidate . parameters = parametersFromCoordinates ( candidate . coordinates ) ;
candidate . parameters = parametersFromCoordinates ( candidate . coordinates ) ;
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 )
. arg ( candidate . parameters [ storageColumn ] , 0 , ' g ' , 6 ) ) ;
}
candidate . valid = evaluateTrustRegionPoint (
candidate . valid = evaluateTrustRegionPoint (
candidate . parameters ,
candidate . parameters ,
& candidate . fitness ,
& candidate . fitness ,
@ -2707,7 +2396,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
" solver_invalid_ " + selectionName ,
" solver_invalid_ " + selectionName ,
nullptr ) ;
nullptr ) ;
restoreEvaluationState ( current ) ;
restoreEvaluationState ( current ) ;
if ( consecutiveSolverFailures > = 2 & & ! shapeStage ) {
if ( consecutiveSolverFailures > = 2 ) {
rebuildRequested = true ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " 2 consecutive solver failures " ) ;
rebuildReason = QT_TR_NOOP ( " 2 consecutive solver failures " ) ;
}
}
@ -2739,9 +2428,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
// reductionRatio 衡量局部线性模型的可信度:接近 1 表示预测准确;
// reductionRatio 衡量局部线性模型的可信度:接近 1 表示预测准确;
// 值较小表示虽然可能下降,但模型低估了非线性,需要收紧下一步。
// 值较小表示虽然可能下降,但模型低估了非线性,需要收紧下一步。
const double objectiveEnergy = 0.5 * trustRegionSquaredNorm ( objectiveResidual ) ;
const double objectiveEnergy = 0.5 * trustRegionSquaredNorm ( objectiveResidual ) ;
const QVector < double > candidateResidual = earlyShapeStage
const QVector < double > candidateResidual = shapeStage
? candidate . breakdown . earlyParallelResiduals
? candidate . breakdown . shapeResiduals : candidate . breakdown . residualVector ;
: ( shapeStage ? candidate . breakdown . shapeResiduals : candidate . breakdown . residualVector ) ;
const double candidateEnergy = 0.5 * trustRegionSquaredNorm ( candidateResidual ) ;
const double candidateEnergy = 0.5 * trustRegionSquaredNorm ( candidateResidual ) ;
double actualReduction = objectiveEnergy - candidateEnergy ;
double actualReduction = objectiveEnergy - candidateEnergy ;
double reductionRatio = predictedReduction > 1.0e-14 ? actualReduction / predictedReduction : 0.0 ;
double reductionRatio = predictedReduction > 1.0e-14 ? actualReduction / predictedReduction : 0.0 ;
@ -2751,13 +2439,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
const bool poorPrediction = predictedReduction > predictionFloor & &
const bool poorPrediction = predictedReduction > predictionFloor & &
( ! isFiniteNumber ( reductionRatio ) | | reductionRatio < 0.25 ) ;
( ! isFiniteNumber ( reductionRatio ) | | reductionRatio < 0.25 ) ;
consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0 ;
consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0 ;
if ( consecutivePoorPredictions > = 2 & & ! shapeStage ) {
if ( consecutivePoorPredictions > = 2 ) {
rebuildRequested = true ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " 2 consecutive steps with actual improvement below 25% of prediction " ) ;
rebuildReason = QT_TR_NOOP ( " 2 consecutive steps with actual improvement below 25% of prediction " ) ;
}
}
bool accepted = acceptable ( candidate , current ) ;
bool accepted = acceptable ( candidate , current ) ;
QString componentName = earlyShapeStage
? ( m_enabledParamIndices [ selectedColumns [ 0 ] ] = = 2 ? " storage_parallel " : " skin_parallel " ) : ( shapeStage ? " shape " : " total " ) ;
if ( accepted ) {
if ( accepted ) {
// 当前阶段接受候选后同步发布参数、曲线和诊断。
// 当前阶段接受候选后同步发布参数、曲线和诊断。
@ -2766,8 +2452,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
+ + acceptedSinceRebuild ;
+ + acceptedSinceRebuild ;
consecutiveRejectedSteps = 0 ;
consecutiveRejectedSteps = 0 ;
// 第三阶段保留原 LM 控制;第二阶段两组都在步末直接扩缩实际步长。
// 两段共用原 LM 阻尼与信赖半径更新,预测比不替代目标误差验收。
if ( ! shapeStage ) {
if ( reductionRatio > 0.75 ) {
if ( reductionRatio > 0.75 ) {
damping = qMax ( 1.0e-8 , damping * 0.5 ) ;
damping = qMax ( 1.0e-8 , damping * 0.5 ) ;
if ( stepNorm > = trustRadius * 0.8 ) {
if ( stepNorm > = trustRadius * 0.8 ) {
@ -2781,17 +2466,15 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
trustRadius = qMax (
trustRadius = qMax (
minimumTrustRadius , trustRadius * 0.75 ) ;
minimumTrustRadius , trustRadius * 0.75 ) ;
}
}
}
if ( acceptedSinceRebuild > = 10 & & ! rebuildRequested & & ! shapeStage ) {
if ( acceptedSinceRebuild > = 10 & & ! rebuildRequested ) {
rebuildRequested = true ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " 10 accepted steps since last rebuild " ) ;
rebuildReason = QT_TR_NOOP ( " 10 accepted steps since last rebuild " ) ;
}
}
modelRebuiltAtMinimumRadius = false ;
modelRebuiltAtMinimumRadius = false ;
} else {
} else {
// 拒绝时 candidate 只保留在 trace 中, DataManager 和内存状态都恢复
// 拒绝时参数恢复到 current; 沿用原 LM 的阻尼和半径收缩规则,
// 到 current。形状上限或全目标点验收也可能拒绝候选, 不能仅凭拒绝
// 模型是否重建仍由预测质量判断,不因更换目标而改变。
// 次数认定模型失准;重建由上面的预测质量判断,约束冲突先缩步。
+ + consecutiveRejectedSteps ;
+ + consecutiveRejectedSteps ;
damping = qMin ( 1.0e8 , damping * 4.0 ) ;
damping = qMin ( 1.0e8 , damping * 4.0 ) ;
trustRadius = qMax ( minimumTrustRadius , trustRadius * 0.5 ) ;
trustRadius = qMax ( minimumTrustRadius , trustRadius * 0.5 ) ;
@ -2810,21 +2493,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
: " rejected_ " + selectionName ,
: " rejected_ " + selectionName ,
& candidate . breakdown ) ;
& candidate . breakdown ) ;
QString componentDisplayName = componentName ;
QString componentDisplayName = shapeStage ? tr ( " shape deviation " ) : tr ( " total error " ) ;
if ( componentName = = " vertical " ) {
componentDisplayName = tr ( " vertical deviation " ) ;
} else if ( componentName = = " horizontal " ) {
componentDisplayName = tr ( " horizontal deviation " ) ;
} else if ( componentName = = " storage_parallel " ) {
componentDisplayName = tr ( " wellbore storage early shape " ) ;
} else if ( componentName = = " skin_parallel " ) {
componentDisplayName = tr ( " skin early shape " ) ;
} else if ( componentName = = " shape " ) {
componentDisplayName = tr ( " shape deviation " ) ;
} else if ( componentName = = " total " ) {
componentDisplayName = tr ( " total error " ) ;
}
emit logMessageGenerated (
emit logMessageGenerated (
tr ( " Iteration %1: focus=%2, parameters=%3, error=%4, result=%5 " )
tr ( " Iteration %1: focus=%2, parameters=%3, error=%4, result=%5 " )
. arg ( iteration + 1 )
. arg ( iteration + 1 )
@ -2832,23 +2501,18 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
. arg ( selectedColumns . size ( ) )
. arg ( selectedColumns . size ( ) )
. arg ( stageError ( candidate ) , 0 , ' e ' , 4 )
. arg ( stageError ( candidate ) , 0 , ' e ' , 4 )
. arg ( accepted ? tr ( " accepted " ) : tr ( " rejected " ) ) ) ;
. arg ( accepted ? tr ( " accepted " ) : tr ( " rejected " ) ) ) ;
emit progressUpdated ( shapeStage ? - 1 : total Iterations, m_globalBestFitness ) ;
emit progressUpdated ( phase Iterations, m_globalBestFitness ) ;
// 先记录当前试调结果,再发布阶段切换,避免日志显示为未试调就结束。
if ( shapeStage ) {
completeShapeSearchStep ( accepted ) ;
continue ;
}
const bool effectiveImprovement = registerEffectiveImprovement ( stageError ( current ) ) ;
const bool effectiveImprovement = registerEffectiveImprovement ( stageError ( current ) ) ;
if ( ! effectiveImprovement & & recordIneffectiveStep ( ) ) stopReason = LM_LOCAL_OPTIMUM ;
if ( ! effectiveImprovement & & recordIneffectiveStep ( ) ) stopReason = LM_LOCAL_OPTIMUM ;
if ( stopReason = = LM_LOCAL_OPTIMUM ) break ;
if ( stopReason = = LM_LOCAL_OPTIMUM ) break ;
if ( ! shapeStage & & current . fitness < m_targetError ) {
if ( stageError ( current ) < m_targetError ) {
stopReason = LM_TARGET_ACHIEVED ;
stopReason = LM_TARGET_ACHIEVED ;
break ;
break ;
}
}
if ( ! shapeStage & & selectedWindow < 0 & & trustRadius < = minimumTrustRadius * 1.01 & &
if ( trustRadius < = minimumTrustRadius * 1.01 & &
consecutiveRejectedSteps > = 2 & & consecutivePoorPredictions > = 2 ) {
consecutiveRejectedSteps > = 2 & & consecutivePoorPredictions > = 2 ) {
if ( modelRebuiltAtMinimumRadius ) {
if ( modelRebuiltAtMinimumRadius ) {
stopReason = LM_LOCAL_OPTIMUM ;
stopReason = LM_LOCAL_OPTIMUM ;
@ -2858,13 +2522,24 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
rebuildReason = QT_TR_NOOP ( " inaccurate model at minimum trust radius " ) ;
rebuildReason = QT_TR_NOOP ( " inaccurate model at minimum trust radius " ) ;
}
}
}
}
if ( shapeStage ) shapeIterations = phaseIterations ;
else totalIterations = phaseIterations ;
if ( m_shouldStop | | stopReason = = LM_CONSECUTIVE_FAILURES | | stopReason = = LM_OPTIMIZATION_FAILED )
break ;
// 形状达标、确认停滞或用完额度后,保留当前参数进入总误差段。
if ( shapeStage ) {
emit logMessageGenerated ( tr ( " Shape LM phase ended: %1 " ) . arg ( getStopReasonDescription ( stopReason ) ) ) ;
writeTraceRow ( m_currentIteration , - 1 , " lm_phase_end " , current . parameters , current . fitness ,
true , 0 , QString ( " shape_stop_reason_%1 " ) . arg ( static_cast < int > ( stopReason ) ) , & current . breakdown ) ;
}
}
if ( completedIterations > 0 ) {
if ( completedIterations > 0 ) {
m_currentIteration = completedIterations - 1 ;
m_currentIteration = completedIterations - 1 ;
}
}
restoreEvaluationState ( current ) ;
restoreEvaluationState ( current ) ;
emit logMessageGenerated ( tr ( " Adaptive fitting counts: %1 shape attempts, %2 total-stage iterations, %3 total evaluations. " )
emit logMessageGenerated ( tr ( " Adaptive fitting counts: %1 shape LM iterations, %2 total LM iterations, %3 total evaluations." )
. arg ( shapeStepCount ) . arg ( totalIterations ) . arg ( m_totalEvaluations ) ) ;
. arg ( shape Iterations ) . arg ( totalIterations ) . arg ( m_totalEvaluations ) ) ;
if ( m_shouldStop ) {
if ( m_shouldStop ) {
return LM_USER_STOPPED ;
return LM_USER_STOPPED ;