From 452123b06a1abdfe9040b7c31889aafea0b3dd75 Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Tue, 22 Sep 2026 10:09:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor(nmNum):=20=E5=B0=86=20LM=20=E5=BD=A2?= =?UTF-8?q?=E7=8A=B6=E9=98=B6=E6=AE=B5=E6=94=B9=E4=B8=BA=E9=80=90=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8D=95=E8=BD=AE=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bin/Config/Lang/cn/nmNum_cn.qm | Bin 147250 -> 147243 bytes Bin/Config/Lang/cn/nmNum_cn.ts | 12 +- .../nmCalculation/nmCalculationAutoFitLM.cpp | 267 ++++++++++-------- 3 files changed, 162 insertions(+), 117 deletions(-) diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index 776d7652a03136f518578c753c99f83aeb04808f..a0b706a0031440ddad072be980c7a0d71e81241e 100644 GIT binary patch delta 14045 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 delta 14052 zcmZvi1ymH#zsG-fYI|2vuoY~v6$2AQOhg4+M5P1+C2VCyMNtt0yFm=>#>4<@5j(I^ zF|oVecjx}!JMX;nIEV9@ncbba_jiANXX{_>p1<1pWv%Uqs1MPt2Aoe{sAN^jaxR(5 zd8HxP7vJ;ad}gPTRq^6na+LGR5U?N7`7vN9v8J`b5yZUqa}GVNQeXg$B(h!~zz?21 zQpubXh@^?co;L?)5@kn$^T3Uqm+&Cl`8R_~rr!iEA-eQRB{P=dZ05$<1>cpH;`etr zPh-H+TB6fdFqN#7gPG!hnQRXPn~mk{f{{wN`RP4GGCteaRwZ*Q!`U^AGvEnl@Gs7x z|M`5hD?gu*#~J&YbLBKww-@_$oY>CKw#h0b3vX0 zb|t!9PbHIzaF(b8`V(`T01hTrY6loX?B+HQk~(Mm9VBvYFKC4*&OOBeW_sQpJWTBH zLGV2Ih)5|xtjZ+tBe81up~i(+joU=^&BTU85VeQI4nVXv79LV5h(Cxr)FPISC9?5W zCb)xGAeP9+P%MFs!^&a(Z3mIVh`LN8=3?SZhE8;^Pb?S`@UbIiUlPPj>>)B6U9ve_ zdxFrBGq{lt*7=Mth?$+4MdVu^GoAs)6Kjou+UR{8{EPGToZcU>{yk4aRGo-={w7wk zC1>LboXKCH^fn1$0DU2$a~OGF%=Yp#m8?Q%PES`TeP5#7(kfYn!iJs62O$15tyNQk!QOWdUNZMG9=;C6|7yc@laXLwxlZk!R zlXNPa1nDnHxmAc+!wN~dHiTHiRh%~ma=v#X>2?&cnIlPhQ;*n+@|+LXa(){~vUHhP zbUl(AMMGE0lI)g_wLcCX2j6pE?#TJ=jY{TF9y~*I)`{c}jmXLzveW|>@5s5}2t}3){r{;%a`IoIn~9wHB~-EscmS?5&nV7rF`P-KI6t-`dFvpek84yi|IH-tYDDyQ zKj)YFDp?cU2iMunTb!Tp`QCV+k;*PVLo6YI^OS|Egf1m^&5vr1#uBb1 zs?{`+m`8o8jR(+?5Y7kZRWj2$s{I9?^=~TG#bhYl)k^gYuZa2WCznQWtx`6*w1vI? z=|+v|I#JCNoTJ-Pv&}7_TnjjNjHEWHFv*TJ$fH{jQRi;t*#Umy`A%x*b&^=S6Vzd> zE3xr4Igh-g4zpmkcWQ8c8ABb{?jq`FeNCNe;)iEEsZ(RDNuS%CYg%v~T)=teH|Ld= z)G44M>^qqAMO~H5*o`{Hry+_)lGpM8`1okf$SdSk0Lg}?P`5wuDW{*2Pjx)czAgFe zs{#Lixwv}3ihU=agSc=ZgY!Xs&H^8mtoU%wMkIoX+WEBVl7tFfZ3;rH95$c zGK&Ue!iyf5uaZq1%bA^RBkcdi6Mo^jKm&elCOZ6_^V$~9r??TWv&#HB8&Z~^FKI*m z%ae%RUrR$4tb%Keq0liHK<&L0YIDIaUr?CcK8U^~g#|AldKRvdRTDWsyHP}GE6ia} zeToe7Mf`tAkq2rJ6Mj*Y14Q>ZgQB9Z!^}jA8odtmr!fl(iPpTOv8x!&bt+A=No3t0 z&YMjrW=;+U;72oxog`{pz-hfmGd96G_ROO>5gA0)N>jqn2SmGDP@*+#FEXGQN*uAA zC}ZaA?A(>T8t(stKY@PudR zKtm4_1Xs$~(2CfllXP+&MBhG&GMiwD{>-Fv3wslDoyOUvG+EUPsR~`vH6ZGiMVF@D zz!ErcJ`PsN9A?mEcg*b9JGzX|*@8xt8;2RJa;6)(%ZU{XqC4?=1gmv)PrrfKT_Zib z2J@{omhv4Fi8e{}Iu9W{^OZ_gu`PXAx`&u=h?Txxh9}y$o6+6r#Igo5rC)i7E|xPU zixpjfkPQMLH>X31-DeM2})`Cw0i<{wLSht{`uW*gQcJ-gzh zl2w=3mc>NuZ4BEUi0>yfV>|s~h(7ISdyUY6k9}BHsdQqy=dvpaZp1dXV>dr#5F7Q5 zJzkMuMJ3aOJ(2v0omkEC$6P}ulg;u^VWjtlv%(LB#Ojw~zgogGo*b)^l`SQ(2CrbA z(*&t)I8n+OL06<7v44{Uqiv?2J_)9#pW(n91#>9Qe~NJT{(lX%5Wyty!4z9HRG^glX@PoQCOyxNSX&o-6<17|vHEgc!S2$ z@OTG=W&HyY9)pCHMduRfB863j4T+XioLL>4ZgSSp>RYKjHUdil9elK6^`_LgXq*k zB`eooI5G%J(r3JI^bfST%W&aD@14YA?h7a5A&Fn7gsi9_qPinEC%Fo_Q-g@@%@%TV zIuf-TEL?{zQIRO&rsHxKb5R>Urvv7&aOF}7h;8HWxfzwJXCo02cGUtF`>YA{wrE2u$}+T5?-}H zunBV)z64<+4N`<(WANa0E}U;)iNaziYxs6iN%nyf7L*iqkr4IMEYaYGwS6~MG+p&3 zT7E$^|7?Zc!8Nf+{w#!LUs3ghcRs6Rmb;u$+eOvm<<(Znid7Nq5;2jiWRl}|0PQmtuF+G1j5=PZ10I%nPo&Vo`ZnK7O7Zk|}3jX=G4Uv!Sf z()5-@=U0;2*J_E$!A`5<8$;VjF$L4(Fj{wfl=5Y-Seq zS?stLc7M3F*eLL;dj-GBy;Aa1_Dp4j$k;#LLO!{#62 z?o6m|UyHc!D*WP?Szw8&kbZv$tzAR_Ax1589h(EkPBCAj&cI6P! zn+uZW3gnsdP}0>v(4Y55GWPUBNPDzdJz(EvOXk?!L`Mg!WV(}5kvn}cfH#u;>+VDi z=S$8&*QlE-prklaY>(Xt!SVmGPB zTl~%~P3qU>4&2{DX}~!>{LTnzz*T>wv@3s00|RqlBWt8U2mDZzBSkm3ODuV&6zhkX z^?oSDwL}aZH9%T&WB{=l3#DZpA+csA&MtnO0sA=v7jX_*&N=iAXT(R&@&Eb$TXG(q$C*)*^J;z0TR%AO`f?UFB)O(!^Y~whPIP7JX%Clrl+*&@mWNU#wuBvo6^=2ac~~@RWhBcw5=AD{7#5u zwH>e^oqB=2yRA}?CP+Kdk%HbjD(&tEZFZ|K?HLGVDl=Q!J991?e3PX89fIL?CQApR z&=g3$3Sz(;jM9PYW^|P*N+(9aue!eGoc~zL)XgDwAxp|MJE8=BAZ2&#NNi`8RXRJq zkf_Zr>1^g+nB8WTYy#uF?;xE$1EmT|l`b_$8q;*4bjb=c9FQmFY@JQ4O*}az@KF>w1{z!V7g}|`EAU*f5g3uT(71RqScATWwe?FtLWIZaq zdFeuwu}vkj8z&XY_ux%L>1$88rOaI_nNytfw^tlmS@E(sIg@CAQI%}M23ay!Muj+l zGjf0|ufl-dN2(O$RMFDm~%Z{N%dP6_JMg;+%3{ zZYZFO*z=C;64xH7?kl-52Eyj2$xZuDf=4{VSvXhr-1-wW^)$Id@iYYSJDkfRPZ`d(Tdo6rGSMP+%$O7vj$o}9f><(a#%G&cvUWMzABE_@@$cY&_t zbdzVbv=#SbRkHH)<%D+DY_y)&;{g27U7i~VWenRO&+~96ioY$-Z}bDY@k%9g=q@km z22r}Z$V+}+Br5eqUbYp=WPKs8YhMMicMIo}S8@u2Mjk|n&&am0zs>N%X$A{L;NA zQN;xL^~(ZcSMJGg+H8O}FOlEoZes8i;wf~VY&uO!|nB-*P{792(;*|@8+FbYevJX~2g^8p$>xk^%Y zWnvRQsvLG+mz+zdDXRxL5fwebIcTS{_8267&p}x)d%*tNmQXgF?}jL5uWWRd zh@I0Z8~bGtoAyN6^r#p7!bfGtIhfPvugb1mDBr@v%7J|ciB;MH9s*w|hmzY83mB*z zO;0AOEGijIpkxDUC>iKfu^CO36IB_}?3v0*T~lJO)+$-s5S)%p1g$z^20!Kcn<#V% z-*OIdRPvTzBlfH~=kEmN{(f66yiIu!nusMJ4tL2)~f$(_eH>(sVLsM;$8C``Znz|jf;D)U=^-^MqHLswlAB6kVsHUkO zQUcY5y{5rR{6583<5CpbzRq6bG7>tmajnL+C6xPRcTMvNEzzY+)mU4*G(?vBQsYr( z0g}%wjYm!$qOJ2ap7DrI#h!BpPttUHyO5}Cn8pi9I~}Ubc{PLck&8-J^tHxkI@Wr& zqVa8A9)+G>;~Nf%eQl)aGaXAhE>RP_1JTxNmuAT2ZzL!yt(vfEH4s)0Yr<*{gn3NX z3~hk5Zu&wq)D@!bcS(aDGveHJbeuYC!c$5kADFI* zDC>?gr@3ZKGqgvuTvf7)i#4{GK{txcEI1gk-c`Dh2hMW(sYj%Da0p-1**)^vS{n6E$J+~tfLyl_>Hok)1+%!!_ecab= zi{_Ld5e*B_WR8V|&i~eAe%OJ{fE${umFLp1sKgNPjp&=lZ0JsG5umFceedk`@8eB z&Q9T|dzZYSdA;q81MtUXl`O=A^Y##Jv%MK; zMZMK}-b38J`HA!Cc$KVjGN;!l&hAN^&-#P=k)V9icFcse+$hcYV4zB7;OA`aDs5+L z1*B4mN3@+E>?U%2#5u}N+r2kt-uJP#=Zs{SWp{1wN5~VFr*M9&r|q9-L4LnWB`bSD zJK#+se7sKUufK)l^QLy7T@+Eez*g8Ht0|e(f0Y;sFweTiOE{)*wEc*;Qr96 zTyaG^4haT*pR64h0WL5-Ms8Bv9nXO zTb{t2HeAKQP)2(- z9?Dj}l=gZSg4Ue&+8f*8FIt|{=B2{nHBZ+*>V<(=M|f)s@`n&LmpB(T(Z1XsPShw; z`+9Z+QG;adxA+@KCZo0AJ1)jf@fPim4bPAx9@hTIiUZ&4q&0{cKEHI*Dt|U()6c4_lKTJ&Mmt^I*)6awY0@>R*_X(7uC8^aC*Hwmt!tAt zo7mC1I*)N!f_~L>o>S5A@foV?R;?6r#@d__e{{Y6LK5p=>iR@hf&E`}wjB@^997B6 zJ<|1=g%a%k8Qs7VaLxB$=msU*^88d?fU=+1L3_?m<8>hy&!MlHtQ+15j%(;b-S8BA z{=JEAq&_?dV;)jZGo?lyAC?_mOD4?yuW?tszkh6KB$L-PRRT;TI&`j@{>p#U9{1 ztmyU(Yk;+`q&qkBI$GtkbmumJPQP?H|C+#K=ISp0fJfb3Pj|(dfUZDjuI`6b4sG_?Vic#5=Q9WS93>Rkfi(U zIty8JP2IPw&`Dk`rrf5fql^`+4w|#_zM&+Z9D2mKZi2DTdg0DC1ZuJ^f5hk zC_Zzs7`)We^UJdRSyid583eUm}-kW@0$J9bGy{Vr!TYzr(>F4(uOKk0O{gPH4(IHu+U-2skDOjq0O_{pe377%nTK-T8=}8dHx*X* zPk$+;3+&%uwjE#(?UX*}%vqxA^Hs9acKVz*P`ZOBR0_&H{ne~L*qSoxZyd)Ahj{Am z*?%Ug(_8;|G3@{BN&V|of9$I)=Um!JUpN$w?PH<-eGM#u{Tls8E!MoAtNv>RSE3{* z{kQd2+XoNy-^b@;UqsRW`d%Id#9sr=+)XsY*T9ye5^{KJ5I4L<&ZjqM%efPEy=Bn0 z1_$+1$@)|@7+&a6%cXN3s%@~yl~8De8Z4XJVkue}EN82s>2%0Y#DM!d-!>Epz(Cn0 zgT1xR8DfKf8%kBkf`ch(aKZ)+IYe>}*=TSsjUQAmVW>44&G|{=4RvgM8q4`J!qCti zRqPKRLzBKS*n0~yG`|Z6b;*nKrJYLV@Ws$}IQ+=WCx*657hwl_tD*C-Oyr!;4OZI$ zGk!C8Er8i16yrP|Ven}VKhS%u!RN&jqOn^IeJ4RelVc5o1`dT|YHt`E1AE?d*AQ?H zJ5o>GRIkHa^Bsm zk`;MmnEP-c-bR~bSa{t9shwm<%7ZnCbqtHTM56){4NJvqc;&@<(y%NJ5{Rv5SXs3= z5)GqaO|?_VayuB-f%K%TN>+N2Az6m~+Rf%1Y&E1c^(3~48PX;o`sKG%$sBhZ4nDqv z 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 接受灵敏度探针:形状误差=%1 - fresh sensitivity at joint shape entry - 进入联合形状调整时重建灵敏度 + fresh sensitivity at single-parameter shape entry + 进入单参数形状调整时重建灵敏度 Adaptive fitting counts: %1 shape attempts, %2 total-stage iterations, %3 total evaluations. 自适应拟合统计:形状尝试 %1 次,整体阶段迭代 %2 次,累计评估 %3 次。 - LM stage 2: joint shape search; after 2 ineffective steps, refresh sensitivity and confirm with one global step. - LM 第二阶段:联合调整全部可调形状参数;连续两次改善不足后,刷新灵敏度并用一次全局联合调整确认。 + LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections. + LM 第二阶段:逐个调整形状参数,每个参数只调一轮;改善后步长翻倍,拒绝后减半,连续拒绝三次后换下一个参数。 LM starting point error: %1; independent total-stage evaluation budget: %2 @@ -1313,8 +1313,8 @@ Reason: %1 进入整体阶段时建立完整灵敏度 - no significant shape improvement after fresh sensitivity and one global step - 刷新灵敏度并进行一次全局联合调整后,形状仍无显著改善 + all shape parameters visited once + 所有形状参数均已完成一轮调整 no valid early sensitivity model diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp index 78ab27e9..55d8c7a6 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp @@ -671,6 +671,30 @@ static bool buildTrustRegionFisherStep( return projectAndPredict(); } +// 为每个可调参数独立求解 LM 步,按边界投影后的预测下降量选择一个参数。 +static bool buildBestSingleParameterStep(const TrustRegionFisher& information, + const QVector& available, const QVector& coordinates, + double damping, double trustRadius, double minimumStep, + QVector* selected, QVector* step, double* predictedReduction) +{ + selected->clear(); + step->fill(0.0, coordinates.size()); + *predictedReduction = 0.0; + for(int i = 0; i < available.size(); ++i) { + QVector singleColumn(1, available[i]); + QVector 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(); +} + // 前期每次只调一个参数:间距锁定符号,形状 LM 提供幅度和后续验收依据。 static bool buildEarlyGapGuidedStep(const TrustRegionFisher& shapeInformation, const QVector& selected, const QVector& coordinates, double direction, @@ -691,7 +715,7 @@ static bool buildEarlyGapGuidedStep(const TrustRegionFisher& shapeInformation, } // 放大当前方向,仍受传入的最大半径和参数边界限制。 -// 普通形状步要求预测下降;前期初始探路可跨过预测上坡区,最终仍按真实形状验收。 +// 前期初始探路可跨过预测上坡区,最终仍按真实形状验收。 static bool buildExpandedTrustRegionStep(const TrustRegionFisher& information, const QVector& coordinates, const QVector& originalStep, double maximumRadius, QVector* expandedStep, double* prediction, @@ -1123,7 +1147,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile() QTextStream out(&metaFile); out << "{\n"; - out << " \"schema_version\": 36,\n"; + out << " \"schema_version\": 39,\n"; out << " \"strategy\": \"permeability_height_then_shape_then_joint_lm\",\n"; out << " \"height_acceptance\": \"reliable_vertical_loss_decrease; no_shape_or_total_loss_constraint\",\n"; out << " \"shape_priority\": \"storage_then_skin_then_shape_without_wellbore_then_optional_wellbore_recheck\",\n"; @@ -1138,11 +1162,12 @@ void nmCalculationAutoFitLM::writeTraceMetaFile() out << " \"early_wellbore_total_constraint\": false,\n"; out << " \"shape_stage_total_constraint\": false,\n"; out << " \"shape_wellbore_parameters_frozen\": true,\n"; - out << " \"shape_sensitivity_refresh\": \"non_wellbore_columns_only at joint shape entry and stagnation confirmation; full Jacobian rebuilt at total-stage entry\",\n"; - out << " \"shape_expansion_policy\": \"one expansion per accepted shape direction, up to 2x with predicted descent and parameter bounds; no_rho_gate; retain ordinary step on failure\",\n"; - out << " \"shape_global_parameter_selection\": \"all_valid_non_wellbore_columns_with_positive_response_at_every_shape_step; no_window_selection\",\n"; - out << " \"stage2_budget\": \"adaptive, no fixed iteration or evaluation quota; 2 ineffective shape attempts then one fresh-J global confirmation\",\n"; - out << " \"shape_step_improvement_threshold\": \"max(0.0001, 10% of last effective shape loss); accumulate small improvements\",\n"; + out << " \"shape_sensitivity_refresh\": \"non_wellbore_columns_once_at_sweep_entry; secant updates during sweep; full Jacobian rebuilt at total-stage entry\",\n"; + out << " \"shape_expansion_policy\": \"lock parameter and direction; after each strict shape decrease double next coordinate step from accepted point; otherwise retain point and halve step\",\n"; + out << " \"shape_radius_policy\": \"initial LM step capped at 0.24; subsequent coordinate steps capped at 0.60; no prediction-ratio gate\",\n"; + out << " \"shape_global_parameter_selection\": \"largest predicted reduction among unvisited non-wellbore parameters; finish current parameter before selecting next; each parameter visited once\",\n"; + out << " \"stage2_budget\": \"one parameter sweep; each parameter stops after 3 consecutive non-improving trials, a bound, or minimum step; existing solver failure limit retained\",\n"; + out << " \"shape_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"; @@ -1924,6 +1949,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() const double minimumCoordinateStep = 1.0e-5; const double minimumTrustRadius = 2.0e-3; const double maximumTrustRadius = 0.30; + // 第二阶段整体形状允许更大步幅;第三阶段继续使用原来的半径和预测验收规则。 + const double initialShapeTrustRadius = 0.24; + const double maximumShapeTrustRadius = 0.60; // 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。 // 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。 const double effectiveRelativeImprovement = 2.0e-3; @@ -2120,14 +2148,16 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() if(m_shouldStop) return LM_USER_STOPPED; - // 第二阶段不按迭代比例截断;连续两次改善不足后,刷新灵敏度并仅做一次全局联合确认。 + // 第二阶段先测灵敏度,再逐个调完非井筒参数;每个参数只访问一次。 // 普通形状调整冻结井储、表皮,只按形状验收;总误差达标只在第三阶段判断。 bool shapeStage = true; - bool jointShapeStarted = false; - double shapeImprovementBaseline = current.breakdown.shapeLoss; + bool singleShapeStarted = false; int shapeStepCount = 0; - int ineffectiveShapeSteps = 0; - bool shapeConfirmationRequested = false; + QVector 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; @@ -2193,7 +2223,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() restoreEvaluationState(current); }; emit logMessageGenerated(shapeStage - ? tr("LM stage 2: joint shape search; after 2 ineffective steps, refresh sensitivity and confirm with one global step.") + ? tr("LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections.") : tr("LM stage 3: original LM fitting; accept by total error only.")); if(earlyShapeStage) { emit logMessageGenerated(earlyShapeParameter == 2 @@ -2267,7 +2297,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() consecutiveSolverFailures = 0; stagnationConfirmationRequested = false; globalFallbackAttempted = false; - trustRadius = 0.12; + trustRadius = shapeStage && !earlyShapeStage ? initialShapeTrustRadius : 0.12; damping = 0.01; }; auto enterTotalStage = [&](const QString& reason) { @@ -2362,38 +2392,37 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() true, 0, "early_wellbore_to_shape", ¤t.breakdown); }; - auto completeShapeSearchStep = [&]() { - if(!shapeStage || earlyShapeStage || m_shouldStop) return; - // 改善相对上一次有效改善累计,避免单次小步被立即否定;不再等待完整窗口轮次。 - const double required = qMax(1.0e-4, 0.10 * shapeImprovementBaseline); - const double improvement = shapeImprovementBaseline - current.breakdown.shapeLoss; - const bool improved = improvement >= required; - ++shapeStepCount; - writeTraceRow(m_currentIteration, -1, "shape_step_end", current.parameters, current.fitness, - true, 0, QString(improved ? "improved_step_%1" : "ineffective_step_%1").arg(shapeStepCount), ¤t.breakdown); - emit logMessageGenerated(tr("Shape search step %1: shape error=%2, accumulated improvement=%3, required=%4.") - .arg(shapeStepCount).arg(current.breakdown.shapeLoss, 0, 'g', 6) - .arg(improvement, 0, 'g', 6).arg(required, 0, 'g', 6)); - // 每次形状调整都是全局联合步,结束后清除本次尝试标记。 - globalFallbackAttempted = false; - if(improved) { - shapeImprovementBaseline = current.breakdown.shapeLoss; - ineffectiveShapeSteps = 0; - shapeConfirmationRequested = false; - return; - } - ++ineffectiveShapeSteps; - if(shapeConfirmationRequested) { - finishShapeStage(tr("no significant shape improvement after fresh sensitivity and one global step")); - return; + 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, ¤t.breakdown); } - if(ineffectiveShapeSteps >= 2) { - jacobian.clear(); - reuseSensitivityForNextStage(); - rebuildRequested = true; - rebuildReason = QT_TR_NOOP("confirm shape stagnation after 2 ineffective steps"); - shapeConfirmationRequested = true; + activeShapeColumn = -1; + shapeCoordinateStep = 0.0; + consecutiveShapeRejections = 0; + for(int column = 0; column < dimensions; ++column) { + if(parameterAllowedInStage(column) && !shapeParameterFinished[column]) return; } + finishShapeStage(tr("all shape parameters visited once")); + }; + auto completeShapeSearchStep = [&](bool accepted) { + if(!shapeStage || earlyShapeStage || m_shouldStop) return; + // 只按本次真实下降控制实际步长;不再使用累计 10% 门槛提前结束整轮。 + ++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), ¤t.breakdown); + if(consecutiveShapeRejections >= maximumShapeRejections) + finishShapeParameter("3_consecutive_non_improving_trials"); + else if(qAbs(shapeCoordinateStep) < minimumCoordinateStep) + finishShapeParameter("minimum_coordinate_step"); }; auto registerEffectiveImprovement = [&](double fitness) -> bool { @@ -2415,11 +2444,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() return true; }; - // 普通形状按连续尝试判断停滞,并用新灵敏度下的全局联合步确认; + // 普通形状的失败试算缩步并累计当前参数的拒绝次数; // 前期井储、表皮与整体 LM 沿用各自的无效次数处理。 auto recordIneffectiveStep = [&]() -> bool { if(shapeStage && !earlyShapeStage) { - completeShapeSearchStep(); + completeShapeSearchStep(false); return false; } ++consecutiveIneffectiveSteps; @@ -2587,9 +2616,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() jacobianColumnValid[column] = true; columnBuilt = true; + // 后半段形状探针仅用于灵敏度,正式调整必须经过单参数 LM 预测选优。 // 前期同时测井储和表皮,但缓存探针不能绕过当前子阶段的选参限制。 // 前期首次正式调整必须从小步开始,测形状灵敏度的大探针不能提前成为工作点。 - if((!earlyShapeStage || earlyShapeHasImproved) && + if((!shapeStage || earlyShapeStage) && + (!earlyShapeStage || earlyShapeHasImproved) && parameterAllowedInStage(column) && acceptable(probe, base) && (!bestProbe.valid || stageError(probe) < stageError(bestProbe))) { bestProbe = probe; @@ -2609,14 +2640,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() if(parameterAllowedInStage(i)) hasActiveSensitivity = true; } } - // 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度仍由停滞逻辑处理。 + // 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度交给逐参数选步处理。 if(validColumnCount == 0 || (shapeStage && !earlyShapeStage && !hasActiveSensitivity) || m_shouldStop) { restoreEvaluationState(base); return false; } - // 灵敏度试算本身若找到更优真实解也应保留。所有列先基于同一个 base - // 建完,再用该已知割线把 Jacobian 平移到新工作点,避免边算边移动基点。 + // 前期井储/表皮和整体阶段仍可接受更优缓存探针;后半段形状只由 LM 选步。 + // 所有列先基于同一个 base 建完,再用已知割线平移 Jacobian,避免边算边移动基点。 if(bestProbe.valid && bestProbeColumn >= 0) { QVector acceptedStep(dimensions, 0.0); acceptedStep[bestProbeColumn] = bestProbeDelta; @@ -2676,14 +2707,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() if(wellboreRecheck && current.breakdown.earlyParallelLoss <= earlyWellboreBaseline + qMax(1.0e-4, 0.10 * earlyWellboreBaseline)) enterTotalStage(tr("early shape error restored within tolerance")); - if(shapeStage && !earlyShapeStage && !jointShapeStarted) { - jointShapeStarted = true; - shapeImprovementBaseline = current.breakdown.shapeLoss; + if(shapeStage && !earlyShapeStage && !singleShapeStarted) { + singleShapeStarted = true; // 初调已改变井储/表皮,普通形状必须在新的工作点重测其余可调参数的响应。 jacobian.clear(); reuseSensitivityForNextStage(); rebuildRequested = true; - rebuildReason = QT_TR_NOOP("fresh sensitivity at joint shape entry"); + rebuildReason = QT_TR_NOOP("fresh sensitivity at single-parameter shape entry"); } if(m_shouldStop) break; if(!shapeStage && (totalIterations >= m_maxIterations || m_totalEvaluations >= maximumEvaluations)) break; @@ -2741,13 +2771,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() --iteration; continue; } - // 单列探针没有改善不代表联合步无效;重建后继续真实候选评价,再确认停滞。 + // 差分探针不等于 LM 候选;重建后继续按当前阶段求步并真实评价,再确认停滞。 registerEffectiveImprovement(stageError(current)); } // 第三阶段独立计数;方向不可行也消耗一次局部尝试,不能无限缩步循环。 if(!shapeStage) ++totalIterations; - // 每次从最新 J 和当前阶段残差重算 Fisher,联合步使用全局信息。 + // 每次从最新 J 和当前阶段残差重算全局信息,供单参数或联合 LM 求步。 const QVector objectiveResidual = earlyShapeStage ? current.breakdown.earlyParallelResiduals : (shapeStage ? current.breakdown.shapeResiduals : current.breakdown.residualVector); @@ -2775,8 +2805,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() double predictedReduction = 0.0; const int selectedWindow = earlyShapeStage ? 0 : -1; - // 前期只开放当前井储或表皮;普通形状和整体 LM 都联合求解全部有效自由列。 - // 保留弱敏感、相关及边界列,让参数相互配合;窗口只保留作误差诊断。 + // 前期只开放当前井储或表皮;后半段形状从有效自由列中选一个,整体 LM 仍联合求解。 + // 保留弱敏感、相关及边界列供预测比较;窗口只保留作误差诊断。 for(int column = 0; column < dimensions; ++column) { if(stageColumnValid[column] && (earlyShapeStage || global.matrix[column][column] > 0.0)) selectedColumns.append(column); @@ -2800,8 +2830,54 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() damping, earlyStepRadius, minimumCoordinateStep, &coordinateStep, &predictedReduction)) { selectedColumns.clear(); } + } else if(shapeStage) { + // 首步由灵敏度选出尚未调过的参数;后续锁定方向,直接使用扩缩后的步长。 + if(activeShapeColumn < 0) { + QVector 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", ¤t.breakdown); + } else { + // 剩余参数均无可行预测下降步时逐项记录,不能重复选择已完成参数。 + for(int column = 0; column < dimensions; ++column) { + if(!parameterAllowedInStage(column) || shapeParameterFinished[column]) continue; + activeShapeColumn = column; + finishShapeParameter(jacobianColumnValid[column] + ? "no_feasible_descent_step" : "invalid_sensitivity"); + if(!shapeStage || earlyShapeStage) break; + } + --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; if(selectedColumns.isEmpty() || !buildTrustRegionFisherStep( global, selectedColumns, current.coordinates, damping, @@ -2818,11 +2894,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() --iteration; continue; } - if(shapeStage) { - completeShapeSearchStep(); - --iteration; - continue; - } if(trustRadius <= minimumTrustRadius * 1.01 && modelRebuiltAtMinimumRadius) { if(promoteSampling(false)) continue; @@ -2933,7 +3004,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() "solver_invalid_" + selectionName, nullptr); restoreEvaluationState(current); - if(consecutiveSolverFailures >= 2) { + if(consecutiveSolverFailures >= 2 && (!shapeStage || earlyShapeStage)) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("2 consecutive solver failures"); } @@ -2941,8 +3012,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() stopReason = LM_CONSECUTIVE_FAILURES; break; } - // 确认步求解失败不代表形状已收敛,沿用有限重试后按求解失败退出。 - if(shapeStage && !earlyShapeStage && shapeConfirmationRequested) continue; if(recordIneffectiveStep()) { if(promoteSampling(false)) continue; stopReason = LM_LOCAL_OPTIMUM; @@ -2951,37 +3020,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() continue; } - // 普通形状步真实改善后,沿同一联合方向最多扩步一次,不再用预测兑现比例拦截。 - // 扩步只按整体形状验收,优于原步才替换,否则保留原候选。 - if(shapeStage && !earlyShapeStage && acceptable(candidate, current) && processPauseAndStop()) { - QVector expandedStep; - double expandedPrediction = 0.0; - if(buildExpandedTrustRegionStep(stepInformation, current.coordinates, coordinateStep, - maximumTrustRadius, &expandedStep, &expandedPrediction)) { - writeTraceRow(m_currentIteration, -1, "shape_step_base", candidate.parameters, - candidate.fitness, true, candidate.elapsedMs, "eligible_for_expansion", &candidate.breakdown); - TrustRegionEvaluation expanded; - expanded.coordinates = current.coordinates; - for(int i = 0; i < dimensions; ++i) expanded.coordinates[i] += expandedStep[i]; - expanded.parameters = parametersFromCoordinates(expanded.coordinates); - 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; - writeTraceRow(m_currentIteration, -1, "shape_step_expanded", expanded.parameters, - expanded.fitness, expanded.valid, expanded.elapsedMs, useExpanded ? "selected_expanded" : "retain_ordinary", - expanded.valid ? &expanded.breakdown : nullptr); - if(useExpanded) { - candidate = expanded; - coordinateStep = expandedStep; - predictedReduction = expandedPrediction; - stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep)); - selectionName += "_expanded"; - } - // 两次真实调用已分别记录,下方候选行只记录最终决策,避免重复统计耗时。 - candidate.elapsedMs = -1; - restoreEvaluationState(current); - } - } if(m_shouldStop) { restoreEvaluationState(current); stopReason = LM_USER_STOPPED; @@ -3011,7 +3049,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() const bool poorPrediction = predictedReduction > predictionFloor && (!isFiniteNumber(reductionRatio) || reductionRatio < 0.25); consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0; - if(consecutivePoorPredictions >= 2) { + if(consecutivePoorPredictions >= 2 && (!shapeStage || earlyShapeStage)) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("2 consecutive steps with actual improvement below 25% of prediction"); } @@ -3032,21 +3070,24 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() ++acceptedSinceRebuild; consecutiveRejectedSteps = 0; - if(reductionRatio > 0.75) { - damping = qMax(1.0e-8, damping * 0.5); - if(stepNorm >= trustRadius * 0.8) { - trustRadius = qMin( - maximumTrustRadius, trustRadius * 1.6); + // 井筒调整及第三阶段保留原 LM 控制;逐参数粗调在步末直接扩缩实际步长。 + if(!shapeStage || earlyShapeStage) { + if(reductionRatio > 0.75) { + damping = qMax(1.0e-8, damping * 0.5); + if(stepNorm >= trustRadius * 0.8) { + trustRadius = qMin( + maximumTrustRadius, trustRadius * 1.6); + } + } else if(reductionRatio > 0.25) { + damping = qMax(1.0e-8, damping * 0.8); + } else { + damping = qMin(1.0e8, damping * 2.0); + trustRadius = qMax( + minimumTrustRadius, trustRadius * 0.75); } - } else if(reductionRatio > 0.25) { - damping = qMax(1.0e-8, damping * 0.8); - } else { - damping = qMin(1.0e8, damping * 2.0); - trustRadius = qMax( - minimumTrustRadius, trustRadius * 0.75); } - if(acceptedSinceRebuild >= 10 && !rebuildRequested) { + if(acceptedSinceRebuild >= 10 && !rebuildRequested && (!shapeStage || earlyShapeStage)) { rebuildRequested = true; rebuildReason = QT_TR_NOOP("10 accepted steps since last rebuild"); } @@ -3102,6 +3143,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting() finishEarlyShapeStage(tr("initial same-direction expansion reached the parameter bound without early shape improvement")); continue; } + if(shapeStage && !earlyShapeStage) { + completeShapeSearchStep(accepted); + continue; + } const bool effectiveImprovement = registerEffectiveImprovement(stageError(current)); if(!effectiveImprovement && recordIneffectiveStep()) stopReason = LM_LOCAL_OPTIMUM;