diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm
index b6dbcf60..13ec7e77 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 302d0278..28acd643 100644
--- a/Bin/Config/Lang/cn/nmNum_cn.ts
+++ b/Bin/Config/Lang/cn/nmNum_cn.ts
@@ -1276,14 +1276,6 @@ Reason: %1
total target reached during shape fitting
形状调整期间已达到总误差目标
-
- Half-length exploration accepted: L=%1, shape error=%2, total error=%3.
- 裂缝半长探索结果已接受:半长=%1,形状误差=%2,整体误差=%3。
-
-
- fresh sensitivity after half-length exploration
- 裂缝半长探索后重建灵敏度
-
fresh sensitivity at joint shape entry
进入联合形状调整时重建灵敏度
diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
index 7ff0db33..2af9406a 100644
--- a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
+++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
@@ -496,25 +496,6 @@ static bool solveTrustRegionLinearSystem(
return true;
}
-// 半长候选只由当前值和用户边界生成;投影后去重,不把某个半长写成目标值。
-static QVector trustRegionShapeLengthTrials(double value, double lower, double upper)
-{
- QVector 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 仅作为当前归一化坐标下的局部信息矩阵,不用于统计置信区间。
struct TrustRegionFisher
{
@@ -1169,7 +1150,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
QTextStream out(&metaFile);
out << "{\n";
- out << " \"schema_version\": 20,\n";
+ out << " \"schema_version\": 21,\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_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 << " \"shape_stage_total_tolerance\": \"max(0.02, 25% of stage entry total)\",\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_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_round_extra_evaluation_limit\": 8,\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/length rounds, 2 ineffective rounds then fresh-J confirmation\",\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";
+ out << " \"shape_global_parameter_selection\": \"all_valid_non_wellbore_columns_with_positive_response\",\n";
+ out << " \"stage2_budget\": \"adaptive, no fixed iteration or evaluation quota; full window/global 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 << " \"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";
@@ -2170,13 +2150,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
int shapeRoundNumber = 0;
int ineffectiveShapeRounds = 0;
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 skinColumn = m_enabledParamIndices.indexOf(1);
bool hasRemainingShapeParameters = false;
@@ -2301,79 +2274,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
trustRadius = 0.12;
damping = 0.01;
};
- auto exploreShapeLengths = [&]() -> bool {
- if(shapeExplorationDone || !jointShapeStarted || earlyShapeStage || wellboreRecheck)
- return false;
- const TrustRegionEvaluation base = current;
- TrustRegionEvaluation best;
- const QVector 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", ¤t.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) {
const bool finishedRecheck = wellboreRecheck;
totalIterations = 0;
@@ -2461,9 +2361,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
auto completeShapeSearchRound = [&]() {
if(!shapeStage || earlyShapeStage || !globalFallbackAttempted) return;
- // 即使所有局部方向都不可行,也先完成半长组合搜索。大步接受后需在新点重走窗口。
- if(!shapeExplorationDone && exploreShapeLengths()) return;
- if(m_shouldStop || !shapeExplorationDone) return;
+ if(m_shouldStop) return;
const double required = qMax(1.0e-4, 0.01 * shapeRoundBaseline);
const double improvement = shapeRoundBaseline - current.breakdown.shapeLoss;
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"));
return;
}
- // 下一轮重新覆盖窗口、全局和半长;单步的小改善不会把当前一轮无限延长。
+ // 下一轮重新覆盖窗口和全部有效参数的全局步;单步小改善不重置本轮进度。
shapeRoundBaseline = current.breakdown.shapeLoss;
attemptedWindows.fill(false);
globalFallbackAttempted = false;
- shapeCandidateCount = 0;
- shapeExplorationEvaluations = 0;
- shapeExplorationDone = fractureLengthColumn < 0;
if(!improved && ineffectiveShapeRounds >= 2) {
jacobian.clear();
reuseSensitivityForNextStage();
@@ -2779,8 +2674,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
rebuildRequested = true;
rebuildReason = QT_TR_NOOP("fresh sensitivity at joint shape entry");
}
- if(shapeStage && !earlyShapeStage && shapeCandidateCount >= 3)
- exploreShapeLengths();
if(m_shouldStop) break;
if(!shapeStage && (totalIterations >= m_maxIterations || m_totalEvaluations >= maximumEvaluations)) break;
if(!shapeStage && m_layeredSampling && m_samplingStride > 1) {
@@ -2811,8 +2704,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
rebuildReason = QT_TR_NOOP("no valid sensitivity model");
continue;
}
- // 真实差分全失败不能冒充停滞收敛;独立探索若找到有效新点则重试。
- if(exploreShapeLengths()) continue;
+ // 真实差分全失败不能冒充停滞收敛。
m_lastError = tr("Unable to build a valid shape sensitivity model.");
stopReason = LM_OPTIMIZATION_FAILED;
break;
@@ -2898,7 +2790,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
selectedColumns.clear();
}
}
- // 预调整阶段保留原窗口筛选及子集比较,整体阶段直接采用上面的联合步。
+ // 形状阶段先覆盖局部窗口,再用全部有效自由列求一次全局联合步。
while(shapeStage && selectedColumns.isEmpty()) {
selectedWindow = earlyShapeStage ? 0 : nextTrustRegionWindow(
objectiveWindows, attemptedWindows);
@@ -2911,22 +2803,30 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
? information[selectedWindow] : global;
QVector proposed;
if(earlyShapeStage) {
- // 当前只开放井储或表皮;其余阶段仍使用原 Fisher 组合筛选。
+ // 前期只开放当前的井储或表皮参数。
for(int column = 0; column < dimensions; ++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 {
proposed = selectTrustRegionFisherColumns(
local, global, stageColumnValid,
current.coordinates, minimumCoordinateStep);
}
- // 最多三个推荐参数,比较其全部非空子集(最多七组),避免首参数必选。
- // 这里只做小矩阵运算,真正的候选评价每轮仍至多一次。
- for(int mask = 1; mask < (1 << proposed.size()); ++mask) {
+ // 局部窗口至多三列,比较非空子集;全局只求完整组合,不枚举全部参数子集。
+ const bool fullJointStep = !earlyShapeStage && selectedWindow < 0;
+ const int combinationCount = proposed.isEmpty() ? 0 : (fullJointStep ? 1 : (1 << proposed.size()) - 1);
+ for(int combination = 1; combination <= combinationCount; ++combination) {
QVector columns;
- for(int i = 0; i < proposed.size(); ++i) {
- if(mask & (1 << i)) {
- columns.append(proposed[i]);
+ if(fullJointStep) columns = proposed;
+ else {
+ for(int i = 0; i < proposed.size(); ++i) {
+ if(combination & (1 << i)) columns.append(proposed[i]);
}
}
QVector step;
@@ -3041,7 +2941,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
&candidate.breakdown,
&candidate.curve,
&candidate.elapsedMs);
- if(shapeStage && !earlyShapeStage) ++shapeCandidateCount;
if(!candidate.valid) {
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
@@ -3077,11 +2976,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
continue;
}
- // 普通联合步真实表现可靠时,给含导流能力的方向一次较大步试算。
- // 先保存原步;扩展失败或不如原步时仍保留原候选,本轮额外调用共用八次上限。
- if(shapeStage && !earlyShapeStage && shapeExplorationDone && conductivityColumn >= 0 &&
- selectedColumns.contains(conductivityColumn) && acceptable(candidate, current) &&
- shapeExplorationEvaluations < maximumShapeExplorationEvaluations && processPauseAndStop()) {
+ // 普通形状步真实表现可靠时,沿同一联合方向最多扩步试算一次,不区分参数类型。
+ // 扩步仍须通过固定总误差上限且优于原步,否则保留原候选。
+ if(shapeStage && !earlyShapeStage && acceptable(candidate, current) && processPauseAndStop()) {
const double ordinaryEnergy = 0.5 * trustRegionSquaredNorm(objectiveResidual);
const double ordinaryReduction = ordinaryEnergy - 0.5 * trustRegionSquaredNorm(candidate.breakdown.shapeResiduals);
const double predictionFloor = qMax(1.0e-14, ordinaryEnergy * 1.0e-8);
@@ -3096,7 +2993,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
expanded.coordinates = current.coordinates;
for(int i = 0; i < dimensions; ++i) expanded.coordinates[i] += expandedStep[i];
expanded.parameters = parametersFromCoordinates(expanded.coordinates);
- ++shapeExplorationEvaluations;
expanded.valid = evaluateTrustRegionPoint(expanded.parameters, &expanded.fitness,
&expanded.breakdown, &expanded.curve, &expanded.elapsedMs);
const bool useExpanded = acceptable(expanded, current) && expanded.breakdown.shapeLoss < candidate.breakdown.shapeLoss;