Merge remote-tracking branch 'origin/develop' into feature/refactor-numerical-model-panel-20260824

# Conflicts:
#	Bin/Config/Lang/cn/nmNum_cn.qm
#	Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp
#	Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp
feature/refactor-numerical-model-panel-20260824
lh 2 weeks ago
commit 1c4aa7c447

Binary file not shown.

@ -638,6 +638,389 @@ Reason: %1</source>
<translation>stderr%1</translation>
</message>
</context>
<context>
<name>nmCalculationAutoFitLM</name>
<message>
<source>=== User Stop Request Received ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Gracefully stopping LM automatic fitting...</source>
<translation> LM ...</translation>
</message>
<message>
<source>Waiting for current solver evaluation to stop...</source>
<translation>...</translation>
</message>
<message>
<source>LM automatic fitting stop request processed</source>
<translation>LM </translation>
</message>
<message>
<source>Stop request received but optimization is not running</source>
<translation></translation>
</message>
<message>
<source>LM fitting trace: %1</source>
<translation>LM %1</translation>
</message>
<message>
<source>LM fitting trace metadata: %1</source>
<translation>LM %1</translation>
</message>
<message>
<source>=== LM Run Summary ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>Stop reason: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Result: %1, final error=%2, iterations=%3, evaluations=%4 (successful=%5, failed=%6)</source>
<translation>%1=%2=%3=%4=%5=%6</translation>
</message>
<message>
<source>SUCCESS</source>
<translation></translation>
</message>
<message>
<source>FAILED</source>
<translation></translation>
</message>
<message>
<source>Artifacts: trace=%1</source>
<translation>=%1</translation>
</message>
<message>
<source>Artifacts: trace_meta=%1</source>
<translation>=%1</translation>
</message>
<message>
<source>ERROR: Failed to load configuration from data manager</source>
<translation></translation>
</message>
<message>
<source>Algorithm: LM</source>
<translation>LM</translation>
</message>
<message>
<source>Enabled parameters count: %1</source>
<translation>%1</translation>
</message>
<message>
<source>ERROR: No parameters enabled for optimization</source>
<translation></translation>
</message>
<message>
<source>ERROR: Target LogLog data is empty or insufficient</source>
<translation></translation>
</message>
<message>
<source>ERROR: Target LogLog data arrays have inconsistent sizes</source>
<translation></translation>
</message>
<message>
<source>ERROR: Target well name is empty</source>
<translation></translation>
</message>
<message>
<source>Target data validation passed (%1 data points)</source>
<translation>%1 </translation>
</message>
<message>
<source>=== Evaluating Initial Solution (Elite Protection) ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Initial parameters: </source>
<translation></translation>
</message>
<message>
<source>Starting initial solution evaluation...</source>
<translation>...</translation>
</message>
<message>
<source>Initial solution evaluation successful</source>
<translation></translation>
</message>
<message>
<source>Initial Error: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Initial solution evaluation failed</source>
<translation></translation>
</message>
<message>
<source>Exception during initial solution evaluation</source>
<translation></translation>
</message>
<message>
<source>Critical exception in automatic fitting: %1</source>
<translation>%1</translation>
</message>
<message>
<source>CRITICAL ERROR: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Unknown critical exception in automatic fitting</source>
<translation></translation>
</message>
<message>
<source>CRITICAL ERROR: Unknown exception in automatic fitting</source>
<translation></translation>
</message>
<message>
<source>Applying optimized parameters to model...</source>
<translation>...</translation>
</message>
<message>
<source>Final full-field calculation skipped after user stop</source>
<translation></translation>
</message>
<message>
<source>Running final full-field calculation with optimized parameters...</source>
<translation>使...</translation>
</message>
<message>
<source>Final full-field calculation completed successfully</source>
<translation></translation>
</message>
<message>
<source>Final full-field calculation stopped by user</source>
<translation></translation>
</message>
<message>
<source>Optimized parameters were found, but the final full-field calculation failed</source>
<translation></translation>
</message>
<message>
<source>ERROR: Final full-field calculation failed</source>
<translation></translation>
</message>
<message>
<source>=== Optimization Results ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Final error: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Total iterations: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Total evaluations: %1 (successful: %2)</source>
<translation>%1%2</translation>
</message>
<message>
<source>Optimized parameters: </source>
<translation></translation>
</message>
<message>
<source>Parameters and full-field results applied successfully to data manager</source>
<translation></translation>
</message>
<message>
<source>Optimized parameters applied to data manager</source>
<translation></translation>
</message>
<message>
<source>ERROR: Failed to apply final parameters: %1</source>
<translation>%1</translation>
</message>
<message>
<source>ERROR: Unknown error applying final parameters</source>
<translation></translation>
</message>
<message>
<source>Target achieved. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING SUCCESSFUL ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING CONVERGED ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>Automatic fitting reached a local optimum. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING - LOCAL OPTIMUM ===</source>
<translation>=== LM - ===</translation>
</message>
<message>
<source>Max iterations reached. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING - MAX ITERATIONS ===</source>
<translation>=== LM - ===</translation>
</message>
<message>
<source>Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING STOPPED BY USER ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING FAILED ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2</source>
<translation>%1%2</translation>
</message>
<message>
<source>=== LM AUTOMATIC FITTING - UNKNOWN END ===</source>
<translation>=== LM - ===</translation>
</message>
<message>
<source>No valid parameters are available for trust-region fitting</source>
<translation> LM </translation>
</message>
<message>
<source>The initial solution and parameter-range midpoint are both invalid</source>
<translation></translation>
</message>
<message>
<source>=== Starting LM Main Loop ===</source>
<translation>=== LM ===</translation>
</message>
<message>
<source>LM starting point error: %1; evaluation budget: %2</source>
<translation>LM %1%2</translation>
</message>
<message>
<source>No effective improvement for %1 consecutive steps; rebuilding sensitivity model for confirmation</source>
<translation> %1 </translation>
</message>
<message>
<source>Effective improvement threshold: max(%1, %2% of baseline error); %3 consecutive ineffective steps trigger convergence confirmation</source>
<translation> %1 %2% %3 </translation>
</message>
<message>
<source>Sensitivity probe accepted: error reduced to %1</source>
<translation> %1</translation>
</message>
<message>
<source>Sensitivity model rebuilt: %1/%2 parameter columns valid</source>
<translation>%1/%2 </translation>
</message>
<message>
<source>Sensitivity rebuild produced no effective improvement; local convergence detected</source>
<translation></translation>
</message>
<message>
<source>vertical deviation</source>
<translation></translation>
</message>
<message>
<source>horizontal deviation</source>
<translation></translation>
</message>
<message>
<source>shape deviation</source>
<translation></translation>
</message>
<message>
<source>total error</source>
<translation></translation>
</message>
<message>
<source>Iteration %1: focus=%2, parameters=%3, error=%4, result=%5</source>
<translation> %1=%2=%3=%4=%5</translation>
</message>
<message>
<source>accepted</source>
<translation></translation>
</message>
<message>
<source>rejected</source>
<translation></translation>
</message>
<message>
<source>No initial solution for elite protection</source>
<translation></translation>
</message>
<message>
<source>=== Final Result Validation (Elite Protection) ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Comparing results: Initial=%1, Final=%2</source>
<translation>=%1=%2</translation>
</message>
<message>
<source>Improvement: %1 (%2%)</source>
<translation>%1%2%</translation>
</message>
<message>
<source>Elite protection triggered: final result is invalid or worse than initial</source>
<translation></translation>
</message>
<message>
<source>Restoring initial solution as final result</source>
<translation></translation>
</message>
<message>
<source>Initial solution restored successfully</source>
<translation></translation>
</message>
<message>
<source>Final result validated - solution is not worse than initial</source>
<translation></translation>
</message>
<message>
<source>Target error achieved</source>
<translation></translation>
</message>
<message>
<source>Algorithm converged to stable solution</source>
<translation></translation>
</message>
<message>
<source>Local optimum detected</source>
<translation></translation>
</message>
<message>
<source>Maximum iterations reached</source>
<translation></translation>
</message>
<message>
<source>Stopped by user request</source>
<translation></translation>
</message>
<message>
<source>Too many consecutive failures</source>
<translation></translation>
</message>
<message>
<source>Optimization failed</source>
<translation></translation>
</message>
<message>
<source>Unknown reason</source>
<translation></translation>
</message>
</context>
<context>
<name>nmCalculationSolver</name>
<message>
@ -3454,6 +3837,12 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
</source>
<translation>PSO</translation>
</message>
<message>
<source>%1 Optimization completed:
</source>
<translation>%1
</translation>
</message>
<message>
<source>Best Error: %1
</source>
@ -3484,6 +3873,12 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
</source>
<translation>PSO</translation>
</message>
<message>
<source>%1 Optimization stopped by user:
</source>
<translation>%1
</translation>
</message>
<message>
<source>Current parameters have been applied to the model.</source>
<translation></translation>
@ -3686,6 +4081,10 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>GA auto fitting started</source>
<translation></translation>
</message>
<message>
<source>%1 auto fitting started</source>
<translation>%1 </translation>
</message>
<message>
<source>%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4</source>
<translation>%1 =%2=%3=%4</translation>
@ -3779,8 +4178,8 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<translation>=== %1 %2 ===</translation>
</message>
<message>
<source>Total fitting time: %1 s (%2 min %3 s)</source>
<translation>%1 s (%2 min %3 s)</translation>
<source>Total fitting time: %1 s (%2 min %3 s), total solver calls: %4</source>
<translation>%1 s (%2 min %3 s) %4 </translation>
</message>
<message>
<source>Fitting Curve</source>

@ -0,0 +1,201 @@
#ifndef NMCALCULATIONAUTOFITLM_H
#define NMCALCULATIONAUTOFITLM_H
#include <QObject>
#include <QVector>
#include <QPointF>
#include <QString>
#include <QStringList>
#include <QFile>
#include <limits>
#include "nmCalculation_global.h"
class nmDataWellBase;
// 双对数曲线误差分解。total 是 LM 候选接受和排序的唯一依据,
// 其余诊断量用于有限差分灵敏度分析和信赖域选参。
struct AutoFitObjectiveBreakdownLM {
bool valid;
double total;
double pressureLoss;
double derivativeLoss;
QVector<double> residualVector;
double verticalCommonBias;
double verticalLoss;
bool verticalReliable;
double horizontalPhysicalShift;
double horizontalLoss;
bool horizontalReliable;
bool registrationAmbiguous;
double shapeLoss;
double lateDerivativeSlopeBias;
double lateDerivativeTrendLoss;
bool lateDerivativeTrendReliable;
double coverage;
AutoFitObjectiveBreakdownLM()
: valid(false)
, total(1.0e10)
, pressureLoss(std::numeric_limits<double>::quiet_NaN())
, derivativeLoss(std::numeric_limits<double>::quiet_NaN())
, verticalCommonBias(std::numeric_limits<double>::quiet_NaN())
, verticalLoss(std::numeric_limits<double>::quiet_NaN())
, verticalReliable(false)
, horizontalPhysicalShift(std::numeric_limits<double>::quiet_NaN())
, horizontalLoss(std::numeric_limits<double>::quiet_NaN())
, horizontalReliable(false)
, registrationAmbiguous(false)
, shapeLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeSlopeBias(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendReliable(false)
, coverage(std::numeric_limits<double>::quiet_NaN())
{}
};
// 有限差分 + LM/信赖域拟合的停止原因。
enum StopReasonLM {
LM_CONTINUE_OPTIMIZATION = 0,
LM_TARGET_ACHIEVED = 1,
LM_TRUE_CONVERGENCE = 2,
LM_LOCAL_OPTIMUM = 3,
LM_MAX_ITERATIONS = 4,
LM_USER_STOPPED = 5,
LM_CONSECUTIVE_FAILURES = 6,
LM_OPTIMIZATION_FAILED = 7
};
class NMCALCULATION_EXPORT nmCalculationAutoFitLM : public QObject
{
Q_OBJECT
public:
explicit nmCalculationAutoFitLM(QObject* parent = 0);
~nmCalculationAutoFitLM();
void setTargetLogLogData(const QVector<QVector<double> >& targetData);
bool startAutoFitting();
void stopFitting();
QVector<double> getBestSolution() const;
double getBestFitness() const;
AutoFitObjectiveBreakdownLM getLastObjectiveBreakdown() const;
QString getLastError() const;
bool isRunning() const;
int getCurrentIteration() const;
int getTotalEvaluations() const;
void resetOptimizer();
void setTargetWellName(const QString& wellName);
signals:
void progressUpdated(int iteration, double bestFitness);
void fittingFinished(bool success, const QString& message);
void bestCurveUpdated(QVector<QVector<double> > targetData,
QVector<QVector<double> > bestData,
int iteration,
double fitness);
void logMessageGenerated(const QString& message);
private:
void initializeTemporaryDirectory();
void cleanupTemporaryDirectory();
bool removeDirectoryRecursively(const QString& path);
void cleanupOldTemporaryDirectories();
bool loadAllConfigFromDataManager();
void loadOptimizationConfig();
void loadParameterBounds();
void extractUserInitialValues();
// 有限差分 + LM/信赖域核心算法。
StopReasonLM runTrustRegionFitting();
bool evaluateTrustRegionPoint(const QVector<double>& parameters,
double* fitness,
AutoFitObjectiveBreakdownLM* breakdown,
QVector<QVector<double> >* curve,
int* elapsedMs);
double evaluateFitness(const QVector<double>& parameters);
void applyParametersToDataManager(const QVector<double>& parameters);
void updateReservoirParameters(const QVector<double>& parameters);
void updateWellParameters(const QVector<double>& parameters);
void updateWellToDataManager(nmDataWellBase* pWell);
QVector<QVector<double> > runSolver();
QVector<QVector<double> > runSolverDll();
bool runFinalFullSolver();
void saveOptimizationResult();
void validateAndProtectFinalResult();
QString getStopReasonDescription(StopReasonLM reason) const;
int getEnabledParameterCount() const;
// LM 运行轨迹。
void initializeTraceFile();
void closeTraceFile();
void writeTraceHeader();
void writeTraceMetaFile();
void writeTraceRow(int iteration,
int parameterIndex,
const QString& phase,
const QVector<double>& parameters,
double solverObjective,
bool solverSuccess,
int elapsedMs,
const QString& decision,
const AutoFitObjectiveBreakdownLM* objectiveBreakdown = nullptr);
QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const;
void emitRunSummary(bool success, StopReasonLM finalReason);
bool validateParameters(const QVector<double>& parameters) const;
bool validateLogLogData(const QVector<QVector<double> >& logLogData) const;
bool validateInitialValues() const;
bool validateSolverResult(const QVector<QVector<double> >& result) const;
double calculateLogLogCurveError(const QVector<QVector<double> >& target,
const QVector<QVector<double> >& result) const;
private:
bool m_isRunning;
bool m_shouldStop;
int m_currentIteration;
QString m_lastError;
QVector<double> m_initialValues;
QVector<double> m_globalBestPosition;
double m_globalBestFitness;
AutoFitObjectiveBreakdownLM m_globalBestObjectiveBreakdown;
QVector<QVector<double> > m_lastEvaluatedLogLogData;
QVector<QVector<double> > m_globalBestLogLogData;
mutable AutoFitObjectiveBreakdownLM m_lastObjectiveBreakdown;
QVector<QVector<double> > m_userInitialLogLogData;
AutoFitObjectiveBreakdownLM m_userInitialObjectiveBreakdown;
// 参数索引0 k1 skin2 wellboreC3 phi4 h5 Ct
// 6 Cf7 Swi8 Dfc9 fractureHalfLength。
QVector<bool> m_parameterSelected;
QVector<double> m_parameterLower;
QVector<double> m_parameterUpper;
QVector<int> m_enabledParamIndices;
QVector<QVector<double> > m_targetLogLogData;
QString m_targetWellName;
int m_maxIterations;
double m_targetError;
int m_totalEvaluations;
int m_successfulEvaluations;
volatile int m_evaluationInProgress;
int m_consecutiveFailures;
int m_maxConsecutiveFailures;
QVector<double> m_userInitialSolution;
double m_userInitialFitness;
bool m_hasValidUserSolution;
QString m_tempDirectory;
QString m_traceRunId;
QString m_traceFilePath;
QString m_traceMetaFilePath;
QFile m_traceFile;
};
#endif // NMCALCULATIONAUTOFITLM_H

@ -9,7 +9,6 @@
#include <QMutex>
#include <QDateTime>
#include <QFile>
#include <limits>
#include "nmCalculation_global.h"
@ -20,69 +19,6 @@ class nmDataWellBase;
class QTimer;
class QProcess;
// 双对数曲线误差分解。该结构同时保存用于候选排序的主目标,以及用于判断
// 曲线上下、左右和形状偏差的诊断量。total 是唯一的接受和排序依据,诊断量
// 只参与信赖域选参,不能再次叠加到 total否则会重复计算同一批曲线残差。
struct AutoFitObjectiveBreakdown {
// valid 表示本次曲线评价完整有效;无效评价统一保留 total=1e10。
// pressureLoss 和 derivativeLoss 均在 log(value) 空间按固定网格计算。
bool valid;
double total;
double pressureLoss;
double derivativeLoss;
// 固定目标网格上的普通对数残差。非代理搜索使用它建立完整 Jacobian
// 向量平方和与 total 的平方一致。
QVector<double> residualVector;
// 上下偏差使用压力和导数残差共享的算术平均中心。
// verticalCommonBias 为正表示模拟曲线整体偏高,为负表示整体偏低;
// verticalReliable=false 时仍保留数值,但不能据此确定参数调整方向。
double verticalCommonBias;
double verticalLoss;
bool verticalReliable;
// 水平偏差在 log(time) 坐标中计算。physicalShift 为正表示模拟曲线相对
// 目标偏右,即相同曲线特征在模拟结果中出现得更晚。
double horizontalPhysicalShift;
double horizontalLoss;
bool horizontalReliable;
// true 表示当前曲线无法可靠区分上下和左右误差;此时禁止使用两类有符号
// 诊断量选参,但去除公共中心后的 shapeLoss 仍可用于局部选参。
bool registrationAmbiguous;
// 去除公共均值中心和可信左右偏差后剩余的整体形状误差verticalReliable
// 只控制能否把公共中心解释为上下参数方向,不改变 shape 的中心化公式。
double shapeLoss;
// 兼容现有 trace 列。当前非代理搜索不再单独识别或调度晚期分量。
double lateDerivativeSlopeBias;
double lateDerivativeTrendLoss;
bool lateDerivativeTrendReliable;
// 模拟曲线对目标固定网格的有效覆盖率,取覆盖点比例与连续 log-time
// 跨度比例中的较小值。低于损失函数门槛时本次评价直接无效。
double coverage;
AutoFitObjectiveBreakdown()
: valid(false)
, total(1.0e10)
, pressureLoss(std::numeric_limits<double>::quiet_NaN())
, derivativeLoss(std::numeric_limits<double>::quiet_NaN())
, verticalCommonBias(std::numeric_limits<double>::quiet_NaN())
, verticalLoss(std::numeric_limits<double>::quiet_NaN())
, verticalReliable(false)
, horizontalPhysicalShift(std::numeric_limits<double>::quiet_NaN())
, horizontalLoss(std::numeric_limits<double>::quiet_NaN())
, horizontalReliable(false)
, registrationAmbiguous(false)
, shapeLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeSlopeBias(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendReliable(false)
, coverage(std::numeric_limits<double>::quiet_NaN())
{}
};
// PSO粒子结构
// 这里的 position / velocity / bestPosition 只保存“用户勾选参与拟合的参数”,
// 不是完整的 11 个储层/井筒参数。完整参数向量会在写 trace 或调用代理模型时
@ -97,8 +33,6 @@ struct AutoFitParticle {
QVector<double> velocity; // 速度
QVector<double> bestPosition; // 真实求解器确认的个体最优位置
QVector<double> guideBestPosition; // 仅用于速度更新的引导位置;不会参与真实 gbest/最终结果
AutoFitObjectiveBreakdown currentObjectiveBreakdown; // 当前真实评价对应的误差分解
AutoFitObjectiveBreakdown bestObjectiveBreakdown; // pbest 对应的误差分解
double fitness; // 当前适应度
double bestFitness; // 真实求解器确认的个体最优适应度
double guideBestObjective; // guideBestPosition 对应的真实或代理目标值
@ -162,10 +96,10 @@ public:
void stopFitting();
QVector<double> getBestSolution() const;
double getBestFitness() const;
AutoFitObjectiveBreakdown getLastObjectiveBreakdown() const;
QString getLastError() const;
bool isRunning() const;
int getCurrentIteration() const;
int getTotalEvaluations() const;
void resetOptimizer();
void setPSOTargetWellName(const QString& wellName);
@ -204,26 +138,20 @@ private:
void loadOptimizationConfig();
void loadParameterBounds();
// ===== 自动拟合核心算法 =====
// ===== PSO核心算法 =====
//
// 代理开启时保留原 PSO 筛选流程;代理关闭时使用真实求解器驱动的
// 诊断灵敏度信赖域搜索,不依赖 pbest/gbest 速度公式。
// 主流程:
// 1. extractUserInitialValues(): 从当前项目数据中取用户已有初始解;
// 2. initializeSwarm(): 根据初始解和上下界生成粒子群;
// 3. updateParticle(): 对单个粒子跑真实求解器并计算误差;
// 4. updateGlobalBest(): 只用真实求解器误差更新全局最优;
// 5. updateVelocityAndPosition(): 按 PSO 公式推进下一代粒子。
void extractUserInitialValues();
void initializeSwarm();
void updateVelocityAndPosition();
double evaluateFitness(const QVector<double>& parameters);
void updateGlobalBest();
void updateParticle(int particleIndex);
// 非代理拟合入口:建立有限差分灵敏度,按诊断分量选择参数,再用有界
// LM/信赖域产生候选;所有候选最终都由真实求解器总误差决定是否接受。
StopReasonPSO runTrustRegionFitting();
// 对一个信赖域候选执行完整真实评价,并一次性返回误差、诊断量、曲线和耗时。
// 返回 false 表示求解失败、损失无效或用户已请求停止。
bool evaluateTrustRegionPoint(const QVector<double>& parameters,
double* fitness,
AutoFitObjectiveBreakdown* breakdown,
QVector<QVector<double> >* curve,
int* elapsedMs);
// ===== 参数应用方法 =====
//
@ -239,7 +167,6 @@ private:
// ===== 求解器相关 =====
QVector<QVector<double> > runSolver();
QVector<QVector<double>> runSolverDll();
bool runFinalFullSolver();
QVector<QVector<double>> runSolverExe();
// ===== 数据处理 =====
@ -294,8 +221,7 @@ private:
double surrogateObjective,
const QString& screeningDecision,
const QVector<double>& pbestPosition,
double pbestObjective,
const AutoFitObjectiveBreakdown* objectiveBreakdown = nullptr);
double pbestObjective);
void writeIterationTraceRows();
QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const;
void resetRunSummary();
@ -351,34 +277,30 @@ private:
bool m_isRunning; // 当前是否有一次自动拟合正在运行。
bool m_shouldStop; // 用户停止标志;主循环和求解器等待循环会定期检查它。
bool m_isPaused; // 预留暂停标志;主循环中有暂停等待逻辑。
int m_currentIteration; // 当前自动拟合迭代序号,从 0 开始。
int m_currentIteration; // 当前 PSO 迭代序号,从 0 开始。
QString m_lastError; // 最近一次失败原因,供 UI 展示或日志排查。
// ===== 优化状态数据 =====
// ===== PSO数据 =====
QVector<double> m_initialValues; // 当前模型中提取的用户初始值,顺序与 m_enabledParamIndices 一致。
QVector<AutoFitParticle> m_swarm; // 粒子群,每个粒子只保存启用参数维度。
QVector<double> m_globalBestPosition; // 真实求解器确认的当前最优参数
QVector<double> m_globalBestPosition; // 全局最优参数,仍是启用参数向量
double m_globalBestFitness; // 全局最优真实误差,越小越好。
double m_previousBestFitness; // 上一轮全局最优误差,用于自适应参数更新。
AutoFitObjectiveBreakdown m_globalBestObjectiveBreakdown; // 真实 gbest 对应的误差分解。
QVector<QVector<double> > m_lastEvaluatedLogLogData; // 最近一次真实求解得到的 result log-log 曲线。
QVector<QVector<double> > m_globalBestLogLogData; // 当前全局最优对应的 result log-log 曲线。
mutable AutoFitObjectiveBreakdown m_lastObjectiveBreakdown; // 最近一次损失评价的误差分解。
QVector<QVector<double> > m_userInitialLogLogData; // 用户初始解对应的 result log-log 曲线,用于精英保护。
AutoFitObjectiveBreakdown m_userInitialObjectiveBreakdown; // 用户初始解对应的误差分解。
// ===== 优化配置 =====
//
// 参数索引约定:
// 0 k 渗透率1 skin 表皮系数2 wellboreC 井筒储集;
// 3 phi 孔隙度4 h 储层厚度5 Ct 综合压缩系数;
// 6 Cf 岩石压缩系数7 Swi 初始含水饱和度;
// 8 Dfc 裂缝导流能力9 fractureHalfLength 裂缝半长。
// 6 Cf 岩石压缩系数7 Swi 初始含水饱和度。
// m_enabledParamIndices 保存被用户勾选的参数索引,粒子的 position 维度与它一致。
QVector<bool> m_parameterSelected; // 完整 10 个参数是否被用户勾选参与拟合。
QVector<double> m_parameterLower; // 完整 10 个参数的搜索下界。
QVector<double> m_parameterUpper; // 完整 10 个参数的搜索上界。
QVector<int> m_enabledParamIndices; // 被勾选参数在完整 10 维体系中的索引。
QVector<bool> m_parameterSelected; // 完整 8 个参数是否被用户勾选参与拟合。
QVector<double> m_parameterLower; // 完整 8 个参数的搜索下界。
QVector<double> m_parameterUpper; // 完整 8 个参数的搜索上界。
QVector<int> m_enabledParamIndices; // 被勾选参数在完整 8 维体系中的索引。
QVector<QVector<double> > m_targetLogLogData; // 目标井 history log-log 曲线time/pressure/derivative。
QString m_targetWellName; // 目标井名称;读写井参数和读取模拟曲线都依赖它。
@ -391,7 +313,7 @@ private:
double m_socialParam; // 群体学习因子,控制粒子靠近全局 gbest 的程度。
// ===== 统计信息 =====
int m_totalEvaluations; // 真实求解器评价总,包含粒子评价和方向试算
int m_totalEvaluations; // 已调用真实求解器评价的粒子总数。
int m_successfulEvaluations; // 真实求解器成功且误差有效的评价次数。
QVector<double> m_convergenceHistory; // 每代全局最优误差历史,用于收敛判断。
@ -407,7 +329,7 @@ private:
// ===== 精英保护 =====
QVector<double> m_userInitialSolution; // 用户初始解参数,若最终改进不足会恢复它。
double m_userInitialFitness; // 用户初始解真实误差。
double m_improvementThreshold; // 仅用于日志区分显著改进和微小改进
double m_improvementThreshold; // 最终结果相对初始解至少需要达到的改进阈值
bool m_hasValidUserSolution; // 初始解是否成功跑过真实求解器。
int m_consecutiveFailedIterations; // 连续失败迭代次数
@ -436,8 +358,8 @@ private:
// 这些字段只描述代理筛选和运行复盘,不参与 PSO 数学更新。
bool m_traceEnabled; // 是否写出 trace CSV/meta 文件。
QString m_traceRunId; // 本次运行 ID作为 trace/candidate/score 文件名的一部分。
QString m_traceFilePath; // 本次自动拟合 trace CSV 的完整路径。
QString m_traceMetaFilePath; // 与 trace 匹配的 meta JSON 完整路径。
QString m_traceFilePath; // pso_baseline_trace_<run_id>.csv 完整路径。
QString m_traceMetaFilePath; // pso_baseline_trace_<run_id>.meta.json 完整路径。
QFile m_traceFile; // trace CSV 文件句柄。
bool m_surrogateScreeningEnabled; // 用户配置中的 PSO acceleration 开关。
unsigned int m_psoRandomSeed; // PSO 随机种子,也用于可复现 random audit。

@ -21,6 +21,7 @@
#include "nmDataWellBase.h"
#include "nmDataAutomaticFitting.h"
#include "nmCalculationAutoFitPSO.h"
#include "nmCalculationAutoFitLM.h"
#include "nmWxAutomaticfittingStart.h"
#include "nmSubWxs_global.h"
@ -109,6 +110,7 @@ private:
// 自动拟合相关成员
nmCalculationAutoFitPSO* m_autoFitterPSO;
nmCalculationAutoFitLM* m_autoFitterLM;
QProgressDialog* m_progressDialog;
QTimer* m_progressTimer;
bool m_autoParameterRanges;

@ -23,9 +23,11 @@
#include <QSplitter>
#include "nmCalculationAutoFitPSO.h"
#include "nmCalculationAutoFitLM.h"
// 前向声明
class nmCalculationAutoFitPSO;
class nmCalculationAutoFitLM;
class QPainter;
class QColor;
class QPaintEvent;
@ -74,8 +76,9 @@ public:
explicit nmWxAutomaticfittingStart(QWidget *parent = 0);
~nmWxAutomaticfittingStart();
// PSO算法接口
// 算法接口
void setAutoFitter(nmCalculationAutoFitPSO* autoFitter);
void setAutoFitter(nmCalculationAutoFitLM* autoFitter);
// 通用设置接口
@ -151,6 +154,8 @@ private:
// 算法实例
nmCalculationAutoFitPSO* m_autoFitterPSO;
nmCalculationAutoFitLM* m_autoFitterLM;
QString m_algorithmName;
// 拟合参数
int m_maxIterations;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -902,7 +902,6 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
vvecLogLog.append(QVector<double>());
vvecLogLog.append(QVector<double>());
vvecLogLog.append(QVector<double>());
vvecSemiLog.clear();
vvecSemiLog.append(QVector<double>());
vvecSemiLog.append(QVector<double>());

@ -1,5 +1,6 @@
#include "nmWxAutomaticFitting.h"
#include "nmCalculationAutoFitPSO.h"
#include "nmCalculationAutoFitLM.h"
#include "nmWxAutomaticfittingStart.h"
#include "nmWxParameterProperty.h"
#include "nmDataAnalyzeManager.h"
@ -593,6 +594,7 @@ bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int par
nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
: iDlgBase(parent)
, m_autoFitterPSO(nullptr)
, m_autoFitterLM(nullptr)
, m_progressDialog(nullptr)
, m_progressTimer(nullptr)
, m_progressMonitor(nullptr)
@ -675,6 +677,9 @@ nmWxAutomaticFitting::~nmWxAutomaticFitting()
if (m_autoFitterPSO) {
disconnect(m_autoFitterPSO, nullptr, this, nullptr);
}
if (m_autoFitterLM) {
disconnect(m_autoFitterLM, nullptr, this, nullptr);
}
DEBUG_UI("AutoFitting destructor - completed");
}
@ -890,6 +895,7 @@ void nmWxAutomaticFitting::setupControlPanel()
QLabel* algorithmLabel = new QLabel(tr("Algorithm:"));
m_algorithmCombo = new QComboBox();
m_algorithmCombo->addItem(tr("PSO (Particle Swarm)"));
m_algorithmCombo->addItem(tr("LM"));
m_algorithmCombo->setCurrentIndex(0);
m_algorithmCombo->setMaximumWidth(160);
m_algorithmCombo->setMinimumWidth(160);
@ -1446,10 +1452,18 @@ void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targ
// 先清理之前的实例
cleanupFitting();
DEBUG_UI("Creating PSO auto fitter");
m_autoFitterPSO = new nmCalculationAutoFitPSO(this);
m_autoFitterPSO->setTargetLogLogData(targetData);
m_autoFitterPSO->setPSOTargetWellName(targetWellName);
const bool useLM = m_algorithmCombo && m_algorithmCombo->currentIndex() == 1;
if(useLM) {
DEBUG_UI("Creating LM auto fitter");
m_autoFitterLM = new nmCalculationAutoFitLM(this);
m_autoFitterLM->setTargetLogLogData(targetData);
m_autoFitterLM->setTargetWellName(targetWellName);
} else {
DEBUG_UI("Creating PSO auto fitter");
m_autoFitterPSO = new nmCalculationAutoFitPSO(this);
m_autoFitterPSO->setTargetLogLogData(targetData);
m_autoFitterPSO->setPSOTargetWellName(targetWellName);
}
//// 特定井名时使用快速路径
//if (targetWellName == "VerticalWell1") {
@ -1485,13 +1499,22 @@ void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targ
m_progressMonitor = new nmWxAutomaticfittingStart(this);
m_progressMonitor->setAutoFitter(m_autoFitterPSO);
if(m_autoFitterLM) {
m_progressMonitor->setAutoFitter(m_autoFitterLM);
} else {
m_progressMonitor->setAutoFitter(m_autoFitterPSO);
}
m_progressMonitor->setPseudoPressureMode(
nmDataAnalyzeManager::getCurrentInstance()->getSolverModelType() == SMT_Gas_VariablePvt);
m_progressMonitor->setTargetLogLogData(targetData);
connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
if(m_autoFitterLM) {
connect(m_autoFitterLM, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
} else {
connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
}
int maxIterations = m_iterationEdit->text().toInt();
double targetError = m_errorLimitEdit->text().toDouble();
@ -1511,6 +1534,8 @@ void nmWxAutomaticFitting::runAutoFitting()
if(m_autoFitterPSO) {
m_autoFitterPSO->startAutoFitting();
} else if(m_autoFitterLM) {
m_autoFitterLM->startAutoFitting();
}
}
@ -1526,23 +1551,30 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
disconnect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
}
if (m_autoFitterLM) {
disconnect(m_autoFitterLM, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
}
if(success) {
// 只有成功拟合的结果才用于生成下一轮范围,失败结果不污染当前配置。
updateBestParametersToTable();
QString resultInfo;
if(m_autoFitterPSO) {
double bestFitness = m_autoFitterPSO->getBestFitness();
if(m_autoFitterPSO || m_autoFitterLM) {
double bestFitness = m_autoFitterLM
? m_autoFitterLM->getBestFitness()
: m_autoFitterPSO->getBestFitness();
const QString algorithmName = m_autoFitterLM ? "LM" : "PSO";
// 检查是否是用户停止的情况
if(message.contains("stopped by user", Qt::CaseInsensitive)) {
resultInfo = tr("PSO Optimization stopped by user:\n");
resultInfo = tr("%1 Optimization stopped by user:\n").arg(algorithmName);
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
resultInfo += tr("Current parameters have been applied to the model.");
QMessageBox::information(this, tr("Optimization Stopped"), resultInfo);
} else {
resultInfo = tr("PSO Optimization completed:\n");
resultInfo = tr("%1 Optimization completed:\n").arg(algorithmName);
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
resultInfo += tr("Optimized parameters have been applied to the model.");
QMessageBox::information(this, tr("Optimization Completed"), resultInfo);
@ -1558,6 +1590,8 @@ void nmWxAutomaticFitting::onStopFitting()
{
if(m_autoFitterPSO && m_autoFitterPSO->isRunning()) {
m_autoFitterPSO->stopFitting();
} else if(m_autoFitterLM && m_autoFitterLM->isRunning()) {
m_autoFitterLM->stopFitting();
}
}
@ -1601,6 +1635,24 @@ void nmWxAutomaticFitting::cleanupFitting()
DEBUG_UI("PSO fitter cleaned up");
}
if (m_autoFitterLM) {
DEBUG_UI("Stopping and disconnecting LM fitter");
disconnect(m_autoFitterLM, nullptr, nullptr, nullptr);
if (m_autoFitterLM->isRunning()) {
m_autoFitterLM->stopFitting();
int waitCount = 0;
while (m_autoFitterLM->isRunning() && waitCount < 50) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
waitCount++;
}
}
delete m_autoFitterLM;
m_autoFitterLM = nullptr;
DEBUG_UI("LM fitter cleaned up");
}
// 清理进度监控
if (m_progressMonitor) {
// 先断开进度监控的信号连接
@ -1628,6 +1680,8 @@ void nmWxAutomaticFitting::updateBestParametersToTable()
// 获取最佳解决方案
if (m_autoFitterPSO) {
bestSolution = m_autoFitterPSO->getBestSolution();
} else if(m_autoFitterLM) {
bestSolution = m_autoFitterLM->getBestSolution();
}
if (bestSolution.isEmpty()) return;

@ -383,6 +383,8 @@ nmWxAutomaticfittingStart::nmWxAutomaticfittingStart(QWidget *parent)
, chartGroup(nullptr)
, curveChart(nullptr)
, m_autoFitterPSO(nullptr)
, m_autoFitterLM(nullptr)
, m_algorithmName("PSO")
, m_maxIterations(100)
, m_targetError(0.001)
, m_wellName("")
@ -578,6 +580,8 @@ void nmWxAutomaticfittingStart::setupControlArea()
void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitter)
{
m_autoFitterPSO = autoFitter;
m_autoFitterLM = nullptr;
m_algorithmName = "PSO";
// 更新算法类型显示
algorithmTypeValue->setText("PSO");
@ -600,6 +604,30 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitte
}
}
void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitLM* autoFitter)
{
m_autoFitterPSO = nullptr;
m_autoFitterLM = autoFitter;
m_algorithmName = "LM";
algorithmTypeValue->setText(m_algorithmName);
algorithmTypeValue->setStyleSheet("QLabel { color: blue; font-weight: bold; }");
if (m_autoFitterLM) {
connect(m_autoFitterLM, SIGNAL(progressUpdated(int, double)),
this, SLOT(onFittingProgress(int, double)));
connect(m_autoFitterLM, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
connect(m_autoFitterLM, SIGNAL(logMessageGenerated(QString)),
this, SLOT(onLogMessageReceived(QString)));
connect(m_autoFitterLM, SIGNAL(bestCurveUpdated(QVector<QVector<double> >,QVector<QVector<double> >,int,double)),
this, SLOT(onBestCurveUpdated(QVector<QVector<double> >,QVector<QVector<double> >,int,double)));
stopButton->setEnabled(true);
addLogMessage(tr("%1 auto fitting started").arg(m_algorithmName));
}
}
void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double targetError, const QString& wellName)
{
m_maxIterations = maxIterations;
@ -612,7 +640,7 @@ void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double t
progressBar->setRange(0, maxIterations);
const QString algorithmName = "PSO";
const QString algorithmName = m_algorithmName;
addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4")
.arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName));
}
@ -621,7 +649,7 @@ void nmWxAutomaticfittingStart::markFittingStarted()
{
m_startTime = QDateTime::currentDateTime();
const QString algorithmName = "PSO";
const QString algorithmName = m_algorithmName;
QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss");
addLogMessage(tr("=== %1 Fitting Session Started at %2 ===")
@ -636,7 +664,7 @@ void nmWxAutomaticfittingStart::setSelectedParameters(const QStringList& paramet
// 立即更新参数表格
updateParameterTable();
const QString algorithmName = "PSO";
const QString algorithmName = m_algorithmName;
addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", ")));
}
@ -687,7 +715,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
{
m_isFinished = true;
const QString algorithmName = "PSO";
const QString algorithmName = m_algorithmName;
// 更新状态
if (success) {
@ -759,22 +787,27 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
int minutes = static_cast<int>(seconds / 60.0);
double remainSec = seconds - minutes * 60.0;
const int totalEvaluations = m_autoFitterLM
? m_autoFitterLM->getTotalEvaluations()
: (m_autoFitterPSO ? m_autoFitterPSO->getTotalEvaluations() : 0);
// 同时给总秒数和分秒
addLogMessage(tr("Total fitting time: %1 s (%2 min %3 s)")
// 同时显示总秒数、分秒和本轮真实求解器调用次数。
addLogMessage(tr("Total fitting time: %1 s (%2 min %3 s), total solver calls: %4")
.arg(seconds, 0, 'f', 2)
.arg(minutes)
.arg(remainSec, 0, 'f', 2));
.arg(remainSec, 0, 'f', 2)
.arg(totalEvaluations));
}
}
void nmWxAutomaticfittingStart::onStopButtonClicked()
{
const bool isRunning = m_autoFitterPSO && m_autoFitterPSO->isRunning();
const bool isRunning = (m_autoFitterPSO && m_autoFitterPSO->isRunning()) ||
(m_autoFitterLM && m_autoFitterLM->isRunning());
if (isRunning) {
const QString algorithmName = "PSO";
const QString algorithmName = m_algorithmName;
int ret = QMessageBox::question(this, tr("Confirm Stop"),
tr("Are you sure you want to stop the %1 fitting process?").arg(algorithmName),
QMessageBox::Yes | QMessageBox::No,
@ -783,6 +816,8 @@ void nmWxAutomaticfittingStart::onStopButtonClicked()
if (ret == QMessageBox::Yes) {
if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting();
} else if (m_autoFitterLM) {
m_autoFitterLM->stopFitting();
}
addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName));
}
@ -809,6 +844,11 @@ void nmWxAutomaticfittingStart::updateParameterTable()
if (i < bestSolution.size()) {
valueText = formatScientific(bestSolution[i]);
}
} else if (m_autoFitterLM) {
QVector<double> bestSolution = m_autoFitterLM->getBestSolution();
if (i < bestSolution.size()) {
valueText = formatScientific(bestSolution[i]);
}
}
QTableWidgetItem* valueItem = new QTableWidgetItem(valueText);
@ -842,8 +882,9 @@ QString nmWxAutomaticfittingStart::formatScientific(double value)
void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
{
const bool isRunning = m_autoFitterPSO && m_autoFitterPSO->isRunning();
const QString algorithmName = "PSO";
const bool isRunning = (m_autoFitterPSO && m_autoFitterPSO->isRunning()) ||
(m_autoFitterLM && m_autoFitterLM->isRunning());
const QString algorithmName = m_algorithmName;
if (isRunning && !m_isFinished) {
int ret = QMessageBox::question(this, tr("Confirm Close"),
@ -854,6 +895,8 @@ void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
if (ret == QMessageBox::Yes) {
if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting();
} else if (m_autoFitterLM) {
m_autoFitterLM->stopFitting();
}
event->accept();
} else {

Loading…
Cancel
Save