整理自动拟合部分PSO和LM两种算法

feature/AutoFit-Optimize-20260821
lvjunjie 3 weeks ago
parent c210cd7b39
commit d4756e0ba6

@ -0,0 +1,200 @@
#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;
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 <QMutex>
#include <QDateTime> #include <QDateTime>
#include <QFile> #include <QFile>
#include <limits>
#include "nmCalculation_global.h" #include "nmCalculation_global.h"
@ -20,69 +19,6 @@ class nmDataWellBase;
class QTimer; class QTimer;
class QProcess; 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粒子结构 // PSO粒子结构
// 这里的 position / velocity / bestPosition 只保存“用户勾选参与拟合的参数”, // 这里的 position / velocity / bestPosition 只保存“用户勾选参与拟合的参数”,
// 不是完整的 11 个储层/井筒参数。完整参数向量会在写 trace 或调用代理模型时 // 不是完整的 11 个储层/井筒参数。完整参数向量会在写 trace 或调用代理模型时
@ -97,8 +33,6 @@ struct AutoFitParticle {
QVector<double> velocity; // 速度 QVector<double> velocity; // 速度
QVector<double> bestPosition; // 真实求解器确认的个体最优位置 QVector<double> bestPosition; // 真实求解器确认的个体最优位置
QVector<double> guideBestPosition; // 仅用于速度更新的引导位置;不会参与真实 gbest/最终结果 QVector<double> guideBestPosition; // 仅用于速度更新的引导位置;不会参与真实 gbest/最终结果
AutoFitObjectiveBreakdown currentObjectiveBreakdown; // 当前真实评价对应的误差分解
AutoFitObjectiveBreakdown bestObjectiveBreakdown; // pbest 对应的误差分解
double fitness; // 当前适应度 double fitness; // 当前适应度
double bestFitness; // 真实求解器确认的个体最优适应度 double bestFitness; // 真实求解器确认的个体最优适应度
double guideBestObjective; // guideBestPosition 对应的真实或代理目标值 double guideBestObjective; // guideBestPosition 对应的真实或代理目标值
@ -162,7 +96,6 @@ public:
void stopFitting(); void stopFitting();
QVector<double> getBestSolution() const; QVector<double> getBestSolution() const;
double getBestFitness() const; double getBestFitness() const;
AutoFitObjectiveBreakdown getLastObjectiveBreakdown() const;
QString getLastError() const; QString getLastError() const;
bool isRunning() const; bool isRunning() const;
int getCurrentIteration() const; int getCurrentIteration() const;
@ -204,26 +137,20 @@ private:
void loadOptimizationConfig(); void loadOptimizationConfig();
void loadParameterBounds(); void loadParameterBounds();
// ===== 自动拟合核心算法 ===== // ===== PSO核心算法 =====
// //
// 代理开启时保留原 PSO 筛选流程;代理关闭时使用真实求解器驱动的 // 主流程:
// 诊断灵敏度信赖域搜索,不依赖 pbest/gbest 速度公式。 // 1. extractUserInitialValues(): 从当前项目数据中取用户已有初始解;
// 2. initializeSwarm(): 根据初始解和上下界生成粒子群;
// 3. updateParticle(): 对单个粒子跑真实求解器并计算误差;
// 4. updateGlobalBest(): 只用真实求解器误差更新全局最优;
// 5. updateVelocityAndPosition(): 按 PSO 公式推进下一代粒子。
void extractUserInitialValues(); void extractUserInitialValues();
void initializeSwarm(); void initializeSwarm();
void updateVelocityAndPosition(); void updateVelocityAndPosition();
double evaluateFitness(const QVector<double>& parameters); double evaluateFitness(const QVector<double>& parameters);
void updateGlobalBest(); void updateGlobalBest();
void updateParticle(int particleIndex); 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 +166,6 @@ private:
// ===== 求解器相关 ===== // ===== 求解器相关 =====
QVector<QVector<double> > runSolver(); QVector<QVector<double> > runSolver();
QVector<QVector<double>> runSolverDll(); QVector<QVector<double>> runSolverDll();
bool runFinalFullSolver();
QVector<QVector<double>> runSolverExe(); QVector<QVector<double>> runSolverExe();
// ===== 数据处理 ===== // ===== 数据处理 =====
@ -294,8 +220,7 @@ private:
double surrogateObjective, double surrogateObjective,
const QString& screeningDecision, const QString& screeningDecision,
const QVector<double>& pbestPosition, const QVector<double>& pbestPosition,
double pbestObjective, double pbestObjective);
const AutoFitObjectiveBreakdown* objectiveBreakdown = nullptr);
void writeIterationTraceRows(); void writeIterationTraceRows();
QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const; QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const;
void resetRunSummary(); void resetRunSummary();
@ -351,34 +276,30 @@ private:
bool m_isRunning; // 当前是否有一次自动拟合正在运行。 bool m_isRunning; // 当前是否有一次自动拟合正在运行。
bool m_shouldStop; // 用户停止标志;主循环和求解器等待循环会定期检查它。 bool m_shouldStop; // 用户停止标志;主循环和求解器等待循环会定期检查它。
bool m_isPaused; // 预留暂停标志;主循环中有暂停等待逻辑。 bool m_isPaused; // 预留暂停标志;主循环中有暂停等待逻辑。
int m_currentIteration; // 当前自动拟合迭代序号,从 0 开始。 int m_currentIteration; // 当前 PSO 迭代序号,从 0 开始。
QString m_lastError; // 最近一次失败原因,供 UI 展示或日志排查。 QString m_lastError; // 最近一次失败原因,供 UI 展示或日志排查。
// ===== 优化状态数据 ===== // ===== PSO数据 =====
QVector<double> m_initialValues; // 当前模型中提取的用户初始值,顺序与 m_enabledParamIndices 一致。 QVector<double> m_initialValues; // 当前模型中提取的用户初始值,顺序与 m_enabledParamIndices 一致。
QVector<AutoFitParticle> m_swarm; // 粒子群,每个粒子只保存启用参数维度。 QVector<AutoFitParticle> m_swarm; // 粒子群,每个粒子只保存启用参数维度。
QVector<double> m_globalBestPosition; // 真实求解器确认的当前最优参数 QVector<double> m_globalBestPosition; // 全局最优参数,仍是启用参数向量
double m_globalBestFitness; // 全局最优真实误差,越小越好。 double m_globalBestFitness; // 全局最优真实误差,越小越好。
double m_previousBestFitness; // 上一轮全局最优误差,用于自适应参数更新。 double m_previousBestFitness; // 上一轮全局最优误差,用于自适应参数更新。
AutoFitObjectiveBreakdown m_globalBestObjectiveBreakdown; // 真实 gbest 对应的误差分解。
QVector<QVector<double> > m_lastEvaluatedLogLogData; // 最近一次真实求解得到的 result log-log 曲线。 QVector<QVector<double> > m_lastEvaluatedLogLogData; // 最近一次真实求解得到的 result log-log 曲线。
QVector<QVector<double> > m_globalBestLogLogData; // 当前全局最优对应的 result log-log 曲线。 QVector<QVector<double> > m_globalBestLogLogData; // 当前全局最优对应的 result log-log 曲线。
mutable AutoFitObjectiveBreakdown m_lastObjectiveBreakdown; // 最近一次损失评价的误差分解。
QVector<QVector<double> > m_userInitialLogLogData; // 用户初始解对应的 result log-log 曲线,用于精英保护。 QVector<QVector<double> > m_userInitialLogLogData; // 用户初始解对应的 result log-log 曲线,用于精英保护。
AutoFitObjectiveBreakdown m_userInitialObjectiveBreakdown; // 用户初始解对应的误差分解。
// ===== 优化配置 ===== // ===== 优化配置 =====
// //
// 参数索引约定: // 参数索引约定:
// 0 k 渗透率1 skin 表皮系数2 wellboreC 井筒储集; // 0 k 渗透率1 skin 表皮系数2 wellboreC 井筒储集;
// 3 phi 孔隙度4 h 储层厚度5 Ct 综合压缩系数; // 3 phi 孔隙度4 h 储层厚度5 Ct 综合压缩系数;
// 6 Cf 岩石压缩系数7 Swi 初始含水饱和度; // 6 Cf 岩石压缩系数7 Swi 初始含水饱和度。
// 8 Dfc 裂缝导流能力9 fractureHalfLength 裂缝半长。
// m_enabledParamIndices 保存被用户勾选的参数索引,粒子的 position 维度与它一致。 // m_enabledParamIndices 保存被用户勾选的参数索引,粒子的 position 维度与它一致。
QVector<bool> m_parameterSelected; // 完整 10 个参数是否被用户勾选参与拟合。 QVector<bool> m_parameterSelected; // 完整 8 个参数是否被用户勾选参与拟合。
QVector<double> m_parameterLower; // 完整 10 个参数的搜索下界。 QVector<double> m_parameterLower; // 完整 8 个参数的搜索下界。
QVector<double> m_parameterUpper; // 完整 10 个参数的搜索上界。 QVector<double> m_parameterUpper; // 完整 8 个参数的搜索上界。
QVector<int> m_enabledParamIndices; // 被勾选参数在完整 10 维体系中的索引。 QVector<int> m_enabledParamIndices; // 被勾选参数在完整 8 维体系中的索引。
QVector<QVector<double> > m_targetLogLogData; // 目标井 history log-log 曲线time/pressure/derivative。 QVector<QVector<double> > m_targetLogLogData; // 目标井 history log-log 曲线time/pressure/derivative。
QString m_targetWellName; // 目标井名称;读写井参数和读取模拟曲线都依赖它。 QString m_targetWellName; // 目标井名称;读写井参数和读取模拟曲线都依赖它。
@ -391,7 +312,7 @@ private:
double m_socialParam; // 群体学习因子,控制粒子靠近全局 gbest 的程度。 double m_socialParam; // 群体学习因子,控制粒子靠近全局 gbest 的程度。
// ===== 统计信息 ===== // ===== 统计信息 =====
int m_totalEvaluations; // 真实求解器评价总,包含粒子评价和方向试算 int m_totalEvaluations; // 已调用真实求解器评价的粒子总数。
int m_successfulEvaluations; // 真实求解器成功且误差有效的评价次数。 int m_successfulEvaluations; // 真实求解器成功且误差有效的评价次数。
QVector<double> m_convergenceHistory; // 每代全局最优误差历史,用于收敛判断。 QVector<double> m_convergenceHistory; // 每代全局最优误差历史,用于收敛判断。
@ -407,7 +328,7 @@ private:
// ===== 精英保护 ===== // ===== 精英保护 =====
QVector<double> m_userInitialSolution; // 用户初始解参数,若最终改进不足会恢复它。 QVector<double> m_userInitialSolution; // 用户初始解参数,若最终改进不足会恢复它。
double m_userInitialFitness; // 用户初始解真实误差。 double m_userInitialFitness; // 用户初始解真实误差。
double m_improvementThreshold; // 仅用于日志区分显著改进和微小改进 double m_improvementThreshold; // 最终结果相对初始解至少需要达到的改进阈值
bool m_hasValidUserSolution; // 初始解是否成功跑过真实求解器。 bool m_hasValidUserSolution; // 初始解是否成功跑过真实求解器。
int m_consecutiveFailedIterations; // 连续失败迭代次数 int m_consecutiveFailedIterations; // 连续失败迭代次数
@ -436,8 +357,8 @@ private:
// 这些字段只描述代理筛选和运行复盘,不参与 PSO 数学更新。 // 这些字段只描述代理筛选和运行复盘,不参与 PSO 数学更新。
bool m_traceEnabled; // 是否写出 trace CSV/meta 文件。 bool m_traceEnabled; // 是否写出 trace CSV/meta 文件。
QString m_traceRunId; // 本次运行 ID作为 trace/candidate/score 文件名的一部分。 QString m_traceRunId; // 本次运行 ID作为 trace/candidate/score 文件名的一部分。
QString m_traceFilePath; // 本次自动拟合 trace CSV 的完整路径。 QString m_traceFilePath; // pso_baseline_trace_<run_id>.csv 完整路径。
QString m_traceMetaFilePath; // 与 trace 匹配的 meta JSON 完整路径。 QString m_traceMetaFilePath; // pso_baseline_trace_<run_id>.meta.json 完整路径。
QFile m_traceFile; // trace CSV 文件句柄。 QFile m_traceFile; // trace CSV 文件句柄。
bool m_surrogateScreeningEnabled; // 用户配置中的 PSO acceleration 开关。 bool m_surrogateScreeningEnabled; // 用户配置中的 PSO acceleration 开关。
unsigned int m_psoRandomSeed; // PSO 随机种子,也用于可复现 random audit。 unsigned int m_psoRandomSeed; // PSO 随机种子,也用于可复现 random audit。

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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

@ -383,6 +383,8 @@ nmWxAutomaticfittingStart::nmWxAutomaticfittingStart(QWidget *parent)
, chartGroup(nullptr) , chartGroup(nullptr)
, curveChart(nullptr) , curveChart(nullptr)
, m_autoFitterPSO(nullptr) , m_autoFitterPSO(nullptr)
, m_autoFitterLM(nullptr)
, m_algorithmName("PSO")
, m_maxIterations(100) , m_maxIterations(100)
, m_targetError(0.001) , m_targetError(0.001)
, m_wellName("") , m_wellName("")
@ -578,6 +580,8 @@ void nmWxAutomaticfittingStart::setupControlArea()
void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitter) void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitter)
{ {
m_autoFitterPSO = autoFitter; m_autoFitterPSO = autoFitter;
m_autoFitterLM = nullptr;
m_algorithmName = "PSO";
// 更新算法类型显示 // 更新算法类型显示
algorithmTypeValue->setText("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 = "Finite Difference + 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) void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double targetError, const QString& wellName)
{ {
m_maxIterations = maxIterations; m_maxIterations = maxIterations;
@ -612,7 +640,7 @@ void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double t
progressBar->setRange(0, maxIterations); 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") addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4")
.arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName)); .arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName));
} }
@ -621,7 +649,7 @@ void nmWxAutomaticfittingStart::markFittingStarted()
{ {
m_startTime = QDateTime::currentDateTime(); m_startTime = QDateTime::currentDateTime();
const QString algorithmName = "PSO"; const QString algorithmName = m_algorithmName;
QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss"); QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss");
addLogMessage(tr("=== %1 Fitting Session Started at %2 ===") addLogMessage(tr("=== %1 Fitting Session Started at %2 ===")
@ -636,7 +664,7 @@ void nmWxAutomaticfittingStart::setSelectedParameters(const QStringList& paramet
// 立即更新参数表格 // 立即更新参数表格
updateParameterTable(); updateParameterTable();
const QString algorithmName = "PSO"; const QString algorithmName = m_algorithmName;
addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", "))); 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; m_isFinished = true;
const QString algorithmName = "PSO"; const QString algorithmName = m_algorithmName;
// 更新状态 // 更新状态
if (success) { if (success) {
@ -771,10 +799,11 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
void nmWxAutomaticfittingStart::onStopButtonClicked() 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) { if (isRunning) {
const QString algorithmName = "PSO"; const QString algorithmName = m_algorithmName;
int ret = QMessageBox::question(this, tr("Confirm Stop"), int ret = QMessageBox::question(this, tr("Confirm Stop"),
tr("Are you sure you want to stop the %1 fitting process?").arg(algorithmName), tr("Are you sure you want to stop the %1 fitting process?").arg(algorithmName),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes | QMessageBox::No,
@ -783,6 +812,8 @@ void nmWxAutomaticfittingStart::onStopButtonClicked()
if (ret == QMessageBox::Yes) { if (ret == QMessageBox::Yes) {
if (m_autoFitterPSO) { if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting(); m_autoFitterPSO->stopFitting();
} else if (m_autoFitterLM) {
m_autoFitterLM->stopFitting();
} }
addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName)); addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName));
} }
@ -809,6 +840,11 @@ void nmWxAutomaticfittingStart::updateParameterTable()
if (i < bestSolution.size()) { if (i < bestSolution.size()) {
valueText = formatScientific(bestSolution[i]); 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); QTableWidgetItem* valueItem = new QTableWidgetItem(valueText);
@ -842,8 +878,9 @@ QString nmWxAutomaticfittingStart::formatScientific(double value)
void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event) void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
{ {
const bool isRunning = m_autoFitterPSO && m_autoFitterPSO->isRunning(); const bool isRunning = (m_autoFitterPSO && m_autoFitterPSO->isRunning()) ||
const QString algorithmName = "PSO"; (m_autoFitterLM && m_autoFitterLM->isRunning());
const QString algorithmName = m_algorithmName;
if (isRunning && !m_isFinished) { if (isRunning && !m_isFinished) {
int ret = QMessageBox::question(this, tr("Confirm Close"), int ret = QMessageBox::question(this, tr("Confirm Close"),
@ -854,6 +891,8 @@ void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
if (ret == QMessageBox::Yes) { if (ret == QMessageBox::Yes) {
if (m_autoFitterPSO) { if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting(); m_autoFitterPSO->stopFitting();
} else if (m_autoFitterLM) {
m_autoFitterLM->stopFitting();
} }
event->accept(); event->accept();
} else { } else {

Loading…
Cancel
Save