@ -496,25 +496,6 @@ static bool solveTrustRegionLinearSystem(
return true ;
return true ;
}
}
// 半长候选只由当前值和用户边界生成;投影后去重,不把某个半长写成目标值。
static QVector < double > trustRegionShapeLengthTrials ( double value , double lower , double upper )
{
QVector < double > trials ;
if ( ! isFiniteNumber ( value ) | | ! isFiniteNumber ( lower ) | | ! isFiniteNumber ( upper ) | | upper < = lower )
return trials ;
const double raw [ ] = { value * 2.0 , value * 0.5 , value * 2.0 < upper ? upper : lower } ;
for ( int i = 0 ; i < 3 ; + + i ) {
const double trial = qBound ( lower , raw [ i ] , upper ) ;
const double tolerance = 1.0e-12 * qMax ( 1.0 , qAbs ( trial ) ) ;
if ( qAbs ( trial - value ) < = tolerance ) continue ;
bool duplicate = false ;
for ( int j = 0 ; j < trials . size ( ) ; + + j )
if ( qAbs ( trial - trials [ j ] ) < = tolerance ) duplicate = true ;
if ( ! duplicate ) trials . append ( trial ) ;
}
return trials ;
}
// Fisher 仅作为当前归一化坐标下的局部信息矩阵,不用于统计置信区间。
// Fisher 仅作为当前归一化坐标下的局部信息矩阵,不用于统计置信区间。
struct TrustRegionFisher
struct TrustRegionFisher
{
{
@ -1169,7 +1150,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
QTextStream out ( & metaFile ) ;
QTextStream out ( & metaFile ) ;
out < < " { \n " ;
out < < " { \n " ;
out < < " \" schema_version \" : 2 0 ,\n " ;
out < < " \" schema_version \" : 2 1 ,\n " ;
out < < " \" strategy \" : \" permeability_height_then_shape_then_joint_lm \" , \n " ;
out < < " \" strategy \" : \" permeability_height_then_shape_then_joint_lm \" , \n " ;
out < < " \" shape_priority \" : \" storage_then_skin_then_shape_without_wellbore_then_optional_wellbore_recheck \" , \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 < < " \" shape_metric \" : \" pressure_and_derivative_log_slopes_81_points_lag_8 \" , \n " ;
@ -1178,11 +1159,10 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
out < < " \" early_wellbore_total_constraint \" : false, \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_tolerance \" : \" max(0.02, 25% of stage entry total) \" , \n " ;
out < < " \" shape_wellbore_parameters_frozen \" : true, \n " ;
out < < " \" shape_wellbore_parameters_frozen \" : true, \n " ;
out < < " \" shape_sensitivity_refresh \" : \" full Jacobian at joint shape entry and after accepted length exploration \" , \n " ;
out < < " \" shape_sensitivity_refresh \" : \" full Jacobian at joint shape entry and stagnation confirmation \" , \n " ;
out < < " \" shape_length_exploration \" : \" each complete shape round: after 3 ordinary candidates or before round completion; bounded 2x, 0.5x and far-bound trials with optional permeability correction \" , \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 " ;
out < < " \" shape_round_extra_evaluation_limit \" : 8, \n " ;
out < < " \" shape_global_parameter_selection \" : \" all_valid_non_wellbore_columns_with_positive_response \" , \n " ;
out < < " \" shape_expansion_policy \" : \" Dfc joint step up to 2x when actual rho exceeds 0.75; retain ordinary step on failure \" , \n " ;
out < < " \" stage2_budget \" : \" adaptive, no fixed iteration or evaluation quota; full window/global rounds, 2 ineffective rounds then fresh-J confirmation \" , \n " ;
out < < " \" stage2_budget \" : \" adaptive, no fixed iteration or evaluation quota; full window/global/length rounds, 2 ineffective rounds then fresh-J confirmation \" , \n " ;
out < < " \" shape_round_improvement_threshold \" : \" max(0.0001, 1% of round entry shape loss) \" , \n " ;
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 < < " \" 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_trigger \" : \" early loss exceeds initial early-stage exit by max(0.0001, 10%) \" , \n " ;
@ -2170,13 +2150,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
int shapeRoundNumber = 0 ;
int shapeRoundNumber = 0 ;
int ineffectiveShapeRounds = 0 ;
int ineffectiveShapeRounds = 0 ;
bool shapeConfirmationRequested = false ;
bool shapeConfirmationRequested = false ;
int shapeCandidateCount = 0 ;
const int fractureLengthColumn = m_enabledParamIndices . indexOf ( 6 ) ;
const int conductivityColumn = m_enabledParamIndices . indexOf ( 5 ) ;
// 每轮探索有限个候选,但不会因全流程累计次数截断半长与渗透率组合。
const int maximumShapeExplorationEvaluations = 8 ;
int shapeExplorationEvaluations = 0 ;
bool shapeExplorationDone = fractureLengthColumn < 0 ;
const int storageColumn = m_enabledParamIndices . indexOf ( 2 ) ;
const int storageColumn = m_enabledParamIndices . indexOf ( 2 ) ;
const int skinColumn = m_enabledParamIndices . indexOf ( 1 ) ;
const int skinColumn = m_enabledParamIndices . indexOf ( 1 ) ;
bool hasRemainingShapeParameters = false ;
bool hasRemainingShapeParameters = false ;
@ -2301,79 +2274,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
trustRadius = 0.12 ;
trustRadius = 0.12 ;
damping = 0.01 ;
damping = 0.01 ;
} ;
} ;
auto exploreShapeLengths = [ & ] ( ) - > bool {
if ( shapeExplorationDone | | ! jointShapeStarted | | earlyShapeStage | | wellboreRecheck )
return false ;
const TrustRegionEvaluation base = current ;
TrustRegionEvaluation best ;
const QVector < double > trials = trustRegionShapeLengthTrials ( base . parameters [ fractureLengthColumn ] ,
m_parameterLower [ 6 ] , m_parameterUpper [ 6 ] ) ;
const int explorationLimit = qMin ( maximumShapeExplorationEvaluations , shapeExplorationEvaluations + 6 ) ;
auto canEvaluate = [ & ] ( ) - > bool {
return shapeExplorationEvaluations < explorationLimit & & processPauseAndStop ( ) ;
} ;
auto keepBetter = [ & ] ( const TrustRegionEvaluation & point ) {
if ( acceptable ( point , base ) & & ( ! best . valid | | point . breakdown . shapeLoss < best . breakdown . shapeLoss | |
( point . breakdown . shapeLoss = = best . breakdown . shapeLoss & & point . fitness < best . fitness ) ) ) best = point ;
} ;
for ( int i = 0 ; i < trials . size ( ) & & canEvaluate ( ) ; + + i ) {
TrustRegionEvaluation probe ;
probe . parameters = base . parameters ;
probe . parameters [ fractureLengthColumn ] = trials [ i ] ;
probe . coordinates = coordinatesFromParameters ( probe . parameters ) ;
+ + shapeExplorationEvaluations ;
probe . valid = evaluateTrustRegionPoint ( probe . parameters , & probe . fitness ,
& probe . breakdown , & probe . curve , & probe . elapsedMs ) ;
writeTraceRow ( m_currentIteration , fractureLengthColumn , " shape_length_probe " , probe . parameters ,
probe . fitness , probe . valid , probe . elapsedMs , probe . valid ? " probe_valid " : " solver_invalid " ,
probe . valid ? & probe . breakdown : nullptr ) ;
if ( probe . valid ) {
keepBetter ( probe ) ;
// 半长点即使暂时超出总误差上限,仍允许一次渗透率补偿后验收组合。
// 沿用高度预调的有符号偏差与初始斜率 -1, 远点修正必须真实求解验证。
if ( permeabilityColumn > = 0 & & probe . breakdown . verticalReliable & &
qAbs ( probe . breakdown . verticalCommonBias ) > 0.01 & & m_parameterLower [ 0 ] > 0.0 & &
m_parameterUpper [ 0 ] > m_parameterLower [ 0 ] & & canEvaluate ( ) ) {
TrustRegionEvaluation corrected ;
corrected . parameters = probe . parameters ;
const double logRange = qLn ( m_parameterUpper [ 0 ] ) - qLn ( m_parameterLower [ 0 ] ) ;
const double change = qBound ( - 0.30 * logRange , probe . breakdown . verticalCommonBias , 0.30 * logRange ) ;
const double k = probe . parameters [ permeabilityColumn ] ;
corrected . parameters [ permeabilityColumn ] = qBound ( m_parameterLower [ 0 ] , k * qExp ( change ) , m_parameterUpper [ 0 ] ) ;
corrected . coordinates = coordinatesFromParameters ( corrected . parameters ) ;
if ( qAbs ( qLn ( corrected . parameters [ permeabilityColumn ] / k ) ) > 1.0e-5 ) {
+ + shapeExplorationEvaluations ;
corrected . valid = evaluateTrustRegionPoint ( corrected . parameters , & corrected . fitness ,
& corrected . breakdown , & corrected . curve , & corrected . elapsedMs ) ;
writeTraceRow ( m_currentIteration , permeabilityColumn , " shape_length_height_probe " , corrected . parameters ,
corrected . fitness , corrected . valid , corrected . elapsedMs , corrected . valid ? " probe_valid " : " solver_invalid " ,
corrected . valid ? & corrected . breakdown : nullptr ) ;
if ( corrected . valid ) keepBetter ( corrected ) ;
}
}
}
restoreEvaluationState ( base ) ;
}
restoreEvaluationState ( base ) ;
// 停止时不把未完成的探索记为完成;正常情况下至多六次调用即可覆盖本轮候选。
if ( m_shouldStop ) return false ;
shapeExplorationDone = true ;
if ( ! best . valid ) return false ;
acceptPoint ( best ) ;
// 不用跨越不同半长基点的割线修补旧模型;在最终选中的新点重新测完整 J。
jacobian . clear ( ) ;
reuseSensitivityForNextStage ( ) ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " fresh sensitivity after half-length exploration " ) ;
effectiveImprovementBaseline = current . breakdown . shapeLoss ;
writeTraceRow ( m_currentIteration , fractureLengthColumn , " shape_exploration_accept " , current . parameters ,
current . fitness , true , 0 , " length_permeability_combination_accepted " , & current . breakdown ) ;
emit logMessageGenerated ( tr ( " Half-length exploration accepted: L=%1, shape error=%2, total error=%3. " )
. arg ( current . parameters [ fractureLengthColumn ] , 0 , ' g ' , 6 )
. arg ( current . breakdown . shapeLoss , 0 , ' g ' , 6 ) . arg ( current . fitness , 0 , ' g ' , 6 ) ) ;
return true ;
} ;
auto enterTotalStage = [ & ] ( const QString & reason ) {
auto enterTotalStage = [ & ] ( const QString & reason ) {
const bool finishedRecheck = wellboreRecheck ;
const bool finishedRecheck = wellboreRecheck ;
totalIterations = 0 ;
totalIterations = 0 ;
@ -2461,9 +2361,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
auto completeShapeSearchRound = [ & ] ( ) {
auto completeShapeSearchRound = [ & ] ( ) {
if ( ! shapeStage | | earlyShapeStage | | ! globalFallbackAttempted ) return ;
if ( ! shapeStage | | earlyShapeStage | | ! globalFallbackAttempted ) return ;
// 即使所有局部方向都不可行,也先完成半长组合搜索。大步接受后需在新点重走窗口。
if ( m_shouldStop ) return ;
if ( ! shapeExplorationDone & & exploreShapeLengths ( ) ) return ;
if ( m_shouldStop | | ! shapeExplorationDone ) return ;
const double required = qMax ( 1.0e-4 , 0.01 * shapeRoundBaseline ) ;
const double required = qMax ( 1.0e-4 , 0.01 * shapeRoundBaseline ) ;
const double improvement = shapeRoundBaseline - current . breakdown . shapeLoss ;
const double improvement = shapeRoundBaseline - current . breakdown . shapeLoss ;
const bool improved = improvement > = required ;
const bool improved = improvement > = required ;
@ -2481,13 +2379,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
finishShapeStage ( tr ( " no significant improvement in a complete round after fresh sensitivity confirmation " ) ) ;
finishShapeStage ( tr ( " no significant improvement in a complete round after fresh sensitivity confirmation " ) ) ;
return ;
return ;
}
}
// 下一轮重新覆盖窗口 、全局和半长;单步的小改善不会把当前一轮无限延长 。
// 下一轮重新覆盖窗口 和全部有效参数的全局步;单步小改善不重置本轮进度 。
shapeRoundBaseline = current . breakdown . shapeLoss ;
shapeRoundBaseline = current . breakdown . shapeLoss ;
attemptedWindows . fill ( false ) ;
attemptedWindows . fill ( false ) ;
globalFallbackAttempted = false ;
globalFallbackAttempted = false ;
shapeCandidateCount = 0 ;
shapeExplorationEvaluations = 0 ;
shapeExplorationDone = fractureLengthColumn < 0 ;
if ( ! improved & & ineffectiveShapeRounds > = 2 ) {
if ( ! improved & & ineffectiveShapeRounds > = 2 ) {
jacobian . clear ( ) ;
jacobian . clear ( ) ;
reuseSensitivityForNextStage ( ) ;
reuseSensitivityForNextStage ( ) ;
@ -2779,8 +2674,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
rebuildRequested = true ;
rebuildRequested = true ;
rebuildReason = QT_TR_NOOP ( " fresh sensitivity at joint shape entry " ) ;
rebuildReason = QT_TR_NOOP ( " fresh sensitivity at joint shape entry " ) ;
}
}
if ( shapeStage & & ! earlyShapeStage & & shapeCandidateCount > = 3 )
exploreShapeLengths ( ) ;
if ( m_shouldStop ) break ;
if ( m_shouldStop ) break ;
if ( ! shapeStage & & ( totalIterations > = m_maxIterations | | m_totalEvaluations > = maximumEvaluations ) ) break ;
if ( ! shapeStage & & ( totalIterations > = m_maxIterations | | m_totalEvaluations > = maximumEvaluations ) ) break ;
if ( ! shapeStage & & m_layeredSampling & & m_samplingStride > 1 ) {
if ( ! shapeStage & & m_layeredSampling & & m_samplingStride > 1 ) {
@ -2811,8 +2704,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
rebuildReason = QT_TR_NOOP ( " no valid sensitivity model " ) ;
rebuildReason = QT_TR_NOOP ( " no valid sensitivity model " ) ;
continue ;
continue ;
}
}
// 真实差分全失败不能冒充停滞收敛;独立探索若找到有效新点则重试。
// 真实差分全失败不能冒充停滞收敛。
if ( exploreShapeLengths ( ) ) continue ;
m_lastError = tr ( " Unable to build a valid shape sensitivity model. " ) ;
m_lastError = tr ( " Unable to build a valid shape sensitivity model. " ) ;
stopReason = LM_OPTIMIZATION_FAILED ;
stopReason = LM_OPTIMIZATION_FAILED ;
break ;
break ;
@ -2898,7 +2790,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
selectedColumns . clear ( ) ;
selectedColumns . clear ( ) ;
}
}
}
}
// 预调整阶段保留原窗口筛选及子集比较,整体阶段直接采用上面的 联合步。
// 形状阶段先覆盖局部窗口,再用全部有效自由列求一次全局 联合步。
while ( shapeStage & & selectedColumns . isEmpty ( ) ) {
while ( shapeStage & & selectedColumns . isEmpty ( ) ) {
selectedWindow = earlyShapeStage ? 0 : nextTrustRegionWindow (
selectedWindow = earlyShapeStage ? 0 : nextTrustRegionWindow (
objectiveWindows , attemptedWindows ) ;
objectiveWindows , attemptedWindows ) ;
@ -2911,22 +2803,30 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
? information [ selectedWindow ] : global ;
? information [ selectedWindow ] : global ;
QVector < int > proposed ;
QVector < int > proposed ;
if ( earlyShapeStage ) {
if ( earlyShapeStage ) {
// 当前只开放井储或表皮;其余阶段仍使用原 Fisher 组合筛选 。
// 前期只开放当前的井储或表皮参数 。
for ( int column = 0 ; column < dimensions ; + + column ) {
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( stageColumnValid [ column ] ) proposed . append ( column ) ;
if ( stageColumnValid [ column ] ) proposed . append ( column ) ;
}
}
} else if ( selectedWindow < 0 ) {
// 全局步保留弱敏感及相关列,避免参数长期被窗口前三名或共线筛选排除。
for ( int column = 0 ; column < dimensions ; + + column ) {
if ( stageColumnValid [ column ] & & global . matrix [ column ] [ column ] > 0.0 )
proposed . append ( column ) ;
}
} else {
} else {
proposed = selectTrustRegionFisherColumns (
proposed = selectTrustRegionFisherColumns (
local , global , stageColumnValid ,
local , global , stageColumnValid ,
current . coordinates , minimumCoordinateStep ) ;
current . coordinates , minimumCoordinateStep ) ;
}
}
// 最多三个推荐参数,比较其全部非空子集(最多七组),避免首参数必选。
// 局部窗口至多三列,比较非空子集;全局只求完整组合,不枚举全部参数子集。
// 这里只做小矩阵运算,真正的候选评价每轮仍至多一次。
const bool fullJointStep = ! earlyShapeStage & & selectedWindow < 0 ;
for ( int mask = 1 ; mask < ( 1 < < proposed . size ( ) ) ; + + mask ) {
const int combinationCount = proposed . isEmpty ( ) ? 0 : ( fullJointStep ? 1 : ( 1 < < proposed . size ( ) ) - 1 ) ;
for ( int combination = 1 ; combination < = combinationCount ; + + combination ) {
QVector < int > columns ;
QVector < int > columns ;
for ( int i = 0 ; i < proposed . size ( ) ; + + i ) {
if ( fullJointStep ) columns = proposed ;
if ( mask & ( 1 < < i ) ) {
else {
columns . append ( proposed [ i ] ) ;
for ( int i = 0 ; i < proposed . size ( ) ; + + i ) {
if ( combination & ( 1 < < i ) ) columns . append ( proposed [ i ] ) ;
}
}
}
}
QVector < double > step ;
QVector < double > step ;
@ -3041,7 +2941,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
& candidate . breakdown ,
& candidate . breakdown ,
& candidate . curve ,
& candidate . curve ,
& candidate . elapsedMs ) ;
& candidate . elapsedMs ) ;
if ( shapeStage & & ! earlyShapeStage ) + + shapeCandidateCount ;
if ( ! candidate . valid ) {
if ( ! candidate . valid ) {
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
@ -3077,11 +2976,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
continue ;
continue ;
}
}
// 普通联合步真实表现可靠时,给含导流能力的方向一次较大步试算。
// 普通形状步真实表现可靠时,沿同一联合方向最多扩步试算一次,不区分参数类型。
// 先保存原步;扩展失败或不如原步时仍保留原候选,本轮额外调用共用八次上限。
// 扩步仍须通过固定总误差上限且优于原步,否则保留原候选。
if ( shapeStage & & ! earlyShapeStage & & shapeExplorationDone & & conductivityColumn > = 0 & &
if ( shapeStage & & ! earlyShapeStage & & acceptable ( candidate , current ) & & processPauseAndStop ( ) ) {
selectedColumns . contains ( conductivityColumn ) & & acceptable ( candidate , current ) & &
shapeExplorationEvaluations < maximumShapeExplorationEvaluations & & processPauseAndStop ( ) ) {
const double ordinaryEnergy = 0.5 * trustRegionSquaredNorm ( objectiveResidual ) ;
const double ordinaryEnergy = 0.5 * trustRegionSquaredNorm ( objectiveResidual ) ;
const double ordinaryReduction = ordinaryEnergy - 0.5 * trustRegionSquaredNorm ( candidate . breakdown . shapeResiduals ) ;
const double ordinaryReduction = ordinaryEnergy - 0.5 * trustRegionSquaredNorm ( candidate . breakdown . shapeResiduals ) ;
const double predictionFloor = qMax ( 1.0e-14 , ordinaryEnergy * 1.0e-8 ) ;
const double predictionFloor = qMax ( 1.0e-14 , ordinaryEnergy * 1.0e-8 ) ;
@ -3096,7 +2993,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
expanded . coordinates = current . coordinates ;
expanded . coordinates = current . coordinates ;
for ( int i = 0 ; i < dimensions ; + + i ) expanded . coordinates [ i ] + = expandedStep [ i ] ;
for ( int i = 0 ; i < dimensions ; + + i ) expanded . coordinates [ i ] + = expandedStep [ i ] ;
expanded . parameters = parametersFromCoordinates ( expanded . coordinates ) ;
expanded . parameters = parametersFromCoordinates ( expanded . coordinates ) ;
+ + shapeExplorationEvaluations ;
expanded . valid = evaluateTrustRegionPoint ( expanded . parameters , & expanded . fitness ,
expanded . valid = evaluateTrustRegionPoint ( expanded . parameters , & expanded . fitness ,
& expanded . breakdown , & expanded . curve , & expanded . elapsedMs ) ;
& expanded . breakdown , & expanded . curve , & expanded . elapsedMs ) ;
const bool useExpanded = acceptable ( expanded , current ) & & expanded . breakdown . shapeLoss < candidate . breakdown . shapeLoss ;
const bool useExpanded = acceptable ( expanded , current ) & & expanded . breakdown . shapeLoss < candidate . breakdown . shapeLoss ;