1.自动拟合参数范围设置与校验 2.移除GA相关代码

feature/MultiWellAutoFit-20260805
lvjunjie 2 weeks ago
parent f0d90f9513
commit 5539150b5b

Binary file not shown.

@ -2129,12 +2129,6 @@
<source>Normalized Gauss Newton</source>
<translation>-</translation>
</message>
<message>
<location filename="../../../../Src/iGui/iSubWxs/iWxAutoFit.cpp" line="71"/>
<location filename="../../../../Src/iGui/iSubWxs/iWxAutoFit.cpp" line="311"/>
<source>Genetic Algorithm</source>
<translation></translation>
</message>
<message>
<location filename="../../../../Src/iGui/iSubWxs/iWxAutoFit.cpp" line="138"/>
<source>Parameter</source>

@ -4715,10 +4715,6 @@ MethodID:%1</source>
<source>Normalized Gauss Newton</source>
<translation>-</translation>
</message>
<message>
<source>Genetic Algorithm</source>
<translation type="obsolete"></translation>
</message>
<message>
<location filename="../../../../Src/mGui/mSubWxs/iWxAutoFit.cpp" line="272"/>
<source>Parameter</source>

Binary file not shown.

@ -47,345 +47,6 @@ Reason: %1</source>
<translation>(MPa)</translation>
</message>
</context>
<context>
<name>nmCalculationAutoFitGA</name>
<message>
<source>=== GA Automatic Fitting Started ===</source>
<translation>=== GA ===</translation>
</message>
<message>
<source>Algorithm: Genetic Algorithm</source>
<translation></translation>
</message>
<message>
<source>ERROR: Failed to load configuration from data manager</source>
<translation></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>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>ERROR: m_userInitialSolution is empty!</source>
<translation></translation>
</message>
<message>
<source>Initial param[%1] = %2</source>
<translation>[%1] = %2</translation>
</message>
<message>
<source>evaluateGenes returned: %1</source>
<translation> %1</translation>
</message>
<message>
<source>Taking SUCCESS branch (fitness &lt; 1e9)</source>
<translation></translation>
</message>
<message>
<source>Initial solution evaluation successful</source>
<translation></translation>
</message>
<message>
<source>Initial fitness (error): %1</source>
<translation>%1</translation>
</message>
<message>
<source>Elite protection activated - initial solution will be preserved if no significant improvement found</source>
<translation>.</translation>
</message>
<message>
<source>Taking FAILURE branch (fitness &gt;= 1e9)</source>
<translation></translation>
</message>
<message>
<source>Initial solution evaluation failed - starting with random initialization</source>
<translation> - 使</translation>
</message>
<message>
<source>Exception during initial solution evaluation</source>
<translation></translation>
</message>
<message>
<source>Population initialized: %1 individuals, %2 dimensions</source>
<translation>%1 %2 </translation>
</message>
<message>
<source>=== Starting GA Main Loop ===</source>
<translation>=== GA ===</translation>
</message>
<message>
<source>--- Generation %1/%2 ---</source>
<translation>--- %1/%2 ---</translation>
</message>
<message>
<source>Current best error: %1</source>
<translation>%1</translation>
</message>
<message>
<source>Total evaluations: %1 (successful: %2, failures: %3)</source>
<translation>%1%2%3</translation>
</message>
<message>
<source>Optimization stopped by user request</source>
<translation></translation>
</message>
<message>
<source>Generation %1 completed: best = %2, avg = %3, worst = %4</source>
<translation> %1 = %2 = %3 = %4</translation>
</message>
<message>
<source>=== TARGET ACHIEVED ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Target error achieved! Current error: %1 &lt; Target: %2</source>
<translation>%1 &lt; %2</translation>
</message>
<message>
<source>Optimization completed successfully after %1 generations</source>
<translation>%1 &lt; %2</translation>
</message>
<message>
<source>=== TRUE CONVERGENCE DETECTED ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Algorithm has converged to a stable solution</source>
<translation></translation>
</message>
<message>
<source>Final error: %1 after %2 generations</source>
<translation>%1 %2 </translation>
</message>
<message>
<source>Solution quality: %1 (1.0 = target achieved)</source>
<translation>%1</translation>
</message>
<message>
<source>=== LOCAL OPTIMUM DETECTED ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Algorithm appears to be trapped in local optimum</source>
<translation></translation>
</message>
<message>
<source>Current error: %1 after %2 generations</source>
<translation>%1 %2 </translation>
</message>
<message>
<source>Suggestion: Try restarting with different parameters or larger search space</source>
<translation>使</translation>
</message>
<message>
<source>=== CONSECUTIVE FAILURES ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Too many consecutive failed generations (%1/%2)</source>
<translation>%1/%2</translation>
</message>
<message>
<source> Optimization status: diversity=%1</source>
<translation>=%1</translation>
</message>
<message>
<source>Generation %1 completed - Current best: %2</source>
<translation> %1 - %2</translation>
</message>
<message>
<source>CRITICAL ERROR: %1</source>
<translation>: %1</translation>
</message>
<message>
<source>CRITICAL ERROR: Unknown exception in GA main loop</source>
<translation>GA</translation>
</message>
<message>
<source>Applying optimized parameters to model...</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 generations: %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 applied successfully 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>=== GA OPTIMIZATION SUCCESSFUL ===</source>
<translation>=== GA ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION CONVERGED ===</source>
<translation>=== GA ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION - LOCAL OPTIMUM ===</source>
<translation>=== GA - ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION - MAX GENERATIONS ===</source>
<translation>=== GA - ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION STOPPED BY USER ===</source>
<translation>=== GA - ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION FAILED ===</source>
<translation>=== GA ===</translation>
</message>
<message>
<source>=== GA OPTIMIZATION - UNKNOWN END ===</source>
<translation>=== GA - ===</translation>
</message>
<message>
<source>Result: %1</source>
<translation>: %1</translation>
</message>
<message>
<source>=== User Stop Request Received ===</source>
<translation>=== ===</translation>
</message>
<message>
<source>Gracefully stopping GA optimization...</source>
<translation>GA...</translation>
</message>
<message>
<source>Force stopping current evaluation...</source>
<translation>...</translation>
</message>
<message>
<source>GA optimization stop request processed</source>
<translation>GA</translation>
</message>
<message>
<source>Stop request received but optimization is not running</source>
<translation></translation>
</message>
<message>
<source> Individual %1 improved: %2 -&gt; %3</source>
<translation> %1 %2 -&gt; %3</translation>
</message>
<message>
<source> Individual %1: evaluation failed</source>
<translation> %1</translation>
</message>
<message>
<source> Individual %1: Exception: %2</source>
<translation> %1%2</translation>
</message>
<message>
<source> Individual %1: Unknown exception</source>
<translation> %1</translation>
</message>
<message>
<source>WARNING: No successful evaluations in generation %1 (consecutive failures: %2)</source>
<translation> %1 %2</translation>
</message>
<message>
<source>Current generation stats: %1 successful, %2 failed out of %3 individuals (success rate: %4%)</source>
<translation>%3 %1 %2 %4%</translation>
</message>
<message>
<source>ERROR: Too many consecutive failed generations (%1/%2) - stopping optimization</source>
<translation>%1/%2- </translation>
</message>
<message>
<source>WARNING: Low success rate (%1%) in generation %2, but continuing optimization</source>
<translation> %2 %1%</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: insufficient improvement</source>
<translation></translation>
</message>
<message>
<source>Threshold: %1%, Actual: %2%</source>
<translation>: %1%, : %2%</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 - significant improvement achieved</source>
<translation> - </translation>
</message>
</context>
<context>
<name>nmCalculationAutoFitPSO</name>
<message>
@ -3698,6 +3359,38 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>Warning</source>
<translation></translation>
</message>
<message>
<source>Invalid parameter range</source>
<translation></translation>
</message>
<message>
<source>The parameter table is unavailable.</source>
<translation></translation>
</message>
<message>
<source>The parameter row is invalid.</source>
<translation></translation>
</message>
<message>
<source>The range values for %1 are incomplete.</source>
<translation>%1 </translation>
</message>
<message>
<source>The minimum value, initial value, and maximum value of %1 must be finite numbers.</source>
<translation>%1 </translation>
</message>
<message>
<source>The physical range of %1 is invalid.</source>
<translation>%1 </translation>
</message>
<message>
<source>The values of %1 exceed the physical range [%2, %3].</source>
<translation>%1 [%2, %3]</translation>
</message>
<message>
<source>The values of %1 must satisfy: minimum &lt;= initial value &lt;= maximum.</source>
<translation>%1 &lt;= &lt;= </translation>
</message>
<message>
<source>Please select a target well!</source>
<translation></translation>
@ -3740,11 +3433,6 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>Optimized parameters have been applied to the model.</source>
<translation></translation>
</message>
<message>
<source>GA Optimization completed:
</source>
<translation>GA</translation>
</message>
<message>
<source>Optimization Completed</source>
<translation></translation>
@ -3774,27 +3462,14 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
<source>Optimization Stopped</source>
<translation></translation>
</message>
<message>
<source>GA Optimization stopped by user:
</source>
<translation>GA</translation>
</message>
<message>
<source>PSO algorithm selected.</source>
<translation>PSO</translation>
</message>
<message>
<source>GA algorithm selected.</source>
<translation>GA</translation>
</message>
<message>
<source>PSO (Particle Swarm)</source>
<translation>PSO</translation>
</message>
<message>
<source>GA (Genetic Algorithm)</source>
<translation>GA</translation>
</message>
<message>
<source>Darcy</source>
<translation type="unfinished"></translation>

@ -292,8 +292,7 @@ enum Fit_Method
{
FM_GaussNewton = 0, //高斯牛顿
FM_GaussNewtonEx, //归一化高斯牛顿
FM_Genetic, //遗传算法
FM_ParticleSwarm, //粒子群算法(Particle Swarm Optimization)
FM_ParticleSwarm = 3, //粒子群算法(Particle Swarm Optimization),保留原有枚举值
FM_Unknown
};

@ -1,267 +0,0 @@
#ifndef NMCALCULATIONAUTOFITGA_H
#define NMCALCULATIONAUTOFITGA_H
#include <QObject>
#include <QVector>
#include <QPointF>
#include <QString>
#include <QTimer>
#include <QApplication>
#include <QDateTime>
#include <QCoreApplication>
#include <QDir>
#include <QDebug>
#include <QTime>
#include "nmCalculation_global.h"
class nmDataWellBase;
enum StopReasonGA {
GA_CONTINUE_OPTIMIZATION = 0,
GA_TARGET_ACHIEVED,
GA_TRUE_CONVERGENCE,
GA_LOCAL_OPTIMUM,
GA_MAX_ITERATIONS,
GA_USER_STOPPED,
GA_CONSECUTIVE_FAILURES,
GA_OPTIMIZATION_FAILED
};
struct GAIndividual
{
QVector<double> genes; // 基因(参数值)
double fitness; // 适应度值
bool isEvaluated; // 是否已评估
GAIndividual() : fitness(1e10), isEvaluated(false) {}
};
class NMCALCULATION_EXPORT nmCalculationAutoFitGA : public QObject
{
Q_OBJECT
public:
explicit nmCalculationAutoFitGA(QObject* parent = nullptr);
virtual ~nmCalculationAutoFitGA();
// ==================== 公共接口方法 ====================
void setTargetLogLogData(const QVector<QVector<double>>& targetData);
bool startAutoFitting();
void stopFitting();
bool isRunning() const;
int getCurrentGeneration() const;
QVector<double> getBestSolution() const;
double getBestFitness() const;
QString getLastError() const;
void resetOptimizer();
void setGATargetWellName(const QString& wellName);
signals:
void progressUpdated(int generation, double bestFitness);
void fittingFinished(bool success, const QString& message);
void logMessageGenerated(const QString& message);
private slots:
void updateProgress();
private:
// 临时目录管理
void initializeTemporaryDirectory();
void cleanupTemporaryDirectory();
bool removeDirectoryRecursively(const QString& path);
// ==================== 数据加载方法 ====================
// 从数据管理器加载所有配置
bool loadAllConfigFromDataManager();
// 加载优化配置
void loadOptimizationConfig();
// 加载参数边界
void loadParameterBounds();
// 提取用户初始值
void extractUserInitialValues();
// ==================== 遗传算法核心方法 ====================
// 初始化种群
void initializePopulation();
// 评估基因
double evaluateGenes(const QVector<double>& genes);
// 评估个体
double evaluateIndividual(GAIndividual& individual);
// 评估种群
void evaluatePopulation();
// 选择操作
int tournamentSelection();
int rouletteWheelSelection();
// 交叉操作
void crossover(const GAIndividual& parent1, const GAIndividual& parent2,
GAIndividual& offspring1, GAIndividual& offspring2);
void singlePointCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
GAIndividual& offspring1, GAIndividual& offspring2);
void uniformCrossover(const GAIndividual& parent1, const GAIndividual& parent2,
GAIndividual& offspring1, GAIndividual& offspring2);
// 变异操作
void mutate(GAIndividual& individual);
void gaussianMutation(GAIndividual& individual);
void polynomialMutation(GAIndividual& individual);
// 精英保留
void applyElitism(QVector<GAIndividual>& newPopulation);
// 更新种群统计
void updatePopulationStatistics();
// 收敛检查
bool checkConvergence();
// 自适应参数更新
void adaptiveParameterUpdate(int generation);
// ==================== 智能收敛判断方法 ====================
// 分析优化状态
StopReasonGA analyzeOptimizationStatus();
// 检查真收敛
bool checkTrueConvergence() const;
// 检查局部最优陷阱
bool checkLocalOptimumTrap() const;
// 计算种群多样性
double calculatePopulationDiversity() const;
// 计算适应度方差
double calculateFitnessVariance(int windowSize) const;
// 计算长期改进
double calculateLongTermImprovement(int windowSize) const;
// 更新收敛指标
void updateConvergenceMetrics();
// 最终结果验证和保护
void validateAndProtectFinalResult();
// ==================== 参数处理方法 ====================
// 参数验证
bool validateParameters(const QVector<double>& parameters) const;
// 双对数数据验证
bool validateLogLogData(const QVector<QVector<double>>& logLogData) const;
// 初始值验证
bool validateInitialValues() const;
// 应用参数到数据管理器
void applyParametersToDataManager(const QVector<double>& parameters);
// 更新储层参数
void updateReservoirParameters(const QVector<double>& parameters);
// 更新井参数
void updateWellParameters(const QVector<double>& parameters);
// 更新井到数据管理器
void updateWellToDataManager(nmDataWellBase* pWell);
// 参数边界约束
void clampToLimits(QVector<double>& parameters) const;
// ==================== 求解器相关方法 ====================
// 运行求解器
QVector<QVector<double>> runSolver();
// 运行EXE求解器
QVector<QVector<double>> runSolverExe();
// 运行Dll求解器
QVector<QVector<double>> runSolverDll();
// 验证求解器结果
bool validateSolverResult(const QVector<QVector<double>>& result) const;
// ==================== 数据处理方法 ====================
// 插值数据
QVector<QPointF> interpolateData(const QVector<QPointF>& source,
const QVector<double>& targetX) const;
// 计算双对数曲线误差
double calculateLogLogCurveError(const QVector<QVector<double>>& target,
const QVector<QVector<double>>& result) const;
// 计算曲线误差
double calculateCurveError(const QVector<QPointF>& curve1,
const QVector<QPointF>& curve2) const;
// ==================== 工具方法 ====================
// 生成0-1随机数
double random01() const;
// 高斯随机数
double gaussianRandom(double mean, double stddev) const;
// 获取启用参数数量
int getEnabledParameterCount() const;
// 保存优化结果
void saveOptimizationResult();
private:
// ==================== 常量定义 ====================
static const double MIN_FITNESS_IMPROVEMENT;
static const double MUTATION_STRENGTH;
static const int CONVERGENCE_CHECK_INTERVAL;
static const int MAX_STAGNATION_GENERATIONS;
// ==================== 核心状态变量 ====================
bool m_isRunning; // 是否正在运行
bool m_shouldStop; // 是否应该停止
bool m_isPaused; // 是否暂停
int m_currentGeneration; // 当前代数
// 适应度统计
double m_bestFitness; // 最优适应度
double m_worstFitness; // 最差适应度
double m_averageFitness; // 平均适应度
double m_previousBestFitness; // 上一代最优适应度
// ==================== GA算法参数 ====================
int m_populationSize; // 种群大小
int m_maxGenerations; // 最大代数
double m_targetError; // 目标误差
double m_crossoverRate; // 交叉概率
double m_mutationRate; // 变异概率
double m_elitismRate; // 精英保留比例
int m_tournamentSize; // 锦标赛选择大小
bool m_useUniformCrossover; // 是否使用均匀交叉
// ==================== 种群和个体 ====================
QVector<GAIndividual> m_population; // 当前种群
QVector<GAIndividual> m_eliteIndividuals; // 精英个体
GAIndividual m_bestIndividual; // 全局最优个体
// ==================== 评估统计 ====================
int m_totalEvaluations; // 总评估次数
int m_successfulEvaluations; // 成功评估次数
int m_evaluationInProgress; // 正在进行的评估计数
int m_consecutiveFailures; // 连续失败次数
// ==================== 精英保护相关 ====================
QVector<double> m_initialValues; // 用户初始参数值
QVector<double> m_userInitialSolution; // 用户初始解
double m_userInitialFitness; // 用户初始适应度
int m_consecutiveFailedGenerations; // 连续失败代数
int m_maxConsecutiveFailures; // 最大允许连续失败数
bool m_hasValidUserSolution; // 是否有有效的用户解
double m_improvementThreshold; // 改进阈值
// ==================== 收敛判断相关 ====================
double m_diversityThreshold; // 多样性阈值
double m_convergenceVarianceThreshold; // 收敛方差阈值
int m_trueConvergenceWindow; // 真收敛判断窗口
int m_localOptimumWindow; // 局部最优判断窗口
double m_nearTargetFactor; // 接近目标的因子
double m_farTargetFactor; // 远离目标的因子
// ==================== 历史记录 ====================
QVector<double> m_convergenceHistory; // 收敛历史
QVector<double> m_diversityHistory; // 多样性历史
// ==================== 参数配置 ====================
QVector<bool> m_parameterSelected; // 参数选择状态
QVector<double> m_parameterLower; // 参数下界
QVector<double> m_parameterUpper; // 参数上界
QVector<int> m_enabledParamIndices; // 启用参数索引
// ==================== 目标数据 ====================
QVector<QVector<double>> m_targetLogLogData; // 目标双对数数据
// ==================== 其他 ====================
QString m_lastError; // 最后错误信息
QTimer* m_progressTimer; // 进度更新定时器
// DLL求解器需要的临时目录
QString m_tempDirectory;
QString m_targetWellName;// 目标井名称
};
#endif // NMCALCULATIONAUTOFITGA_H

@ -21,17 +21,10 @@
#include "nmDataWellBase.h"
#include "nmDataAutomaticFitting.h"
#include "nmCalculationAutoFitPSO.h"
#include "nmCalculationAutoFitGA.h"
#include "nmWxAutomaticfittingStart.h"
#include "nmSubWxs_global.h"
// 算法类型枚举
enum OptimizationAlgorithm {
ALGORITHM_PSO = 0,
ALGORITHM_GA = 1
};
class NM_SUB_WXS_EXPORT nmWxAutomaticFitting : public iDlgBase
{
Q_OBJECT
@ -46,7 +39,7 @@ public:
void onAccept();
void onReject();
void onWellSelected(int index);
void onAlgorithmChanged(int index);
void onParameterTableItemChanged(QTableWidgetItem* item);
// 自动拟合相关槽函数
void runAutoFitting();
@ -63,6 +56,12 @@ private:
void setParameterRowVisible(QTableWidget* table, int row, bool visible);
void renumberVisibleParameterRows(QTableWidget* table);
void updateParameterVisibility(QTableWidget* table, NM_SOLVER_MODEL_TYPE eType);
void initializeSuggestedParameterRanges();
void updateRangeForParameter(int parameterIndex, double centerValue, bool afterFit);
void setParameterRange(int parameterIndex, double minValue, double maxValue);
bool getPhysicalParameterRange(int parameterIndex, double& minValue, double& maxValue);
void normalizeSavedParameterRanges();
bool validateParameterTable(QString& errorMessage, int parameterIndex = -1);
void startAutoFitting(const QVector<QVector<double>>& targetData, const QStringList& selectedParams, const QString& targetWellName);
void cleanupFitting();
@ -108,10 +107,10 @@ private:
// 自动拟合相关成员
nmCalculationAutoFitPSO* m_autoFitterPSO;
nmCalculationAutoFitGA* m_autoFitterGA;
QProgressDialog* m_progressDialog;
QTimer* m_progressTimer;
OptimizationAlgorithm m_selectedAlgorithm; // 选中的算法类型
bool m_autoParameterRanges;
bool m_updatingParameterRanges;
// 拟合开始界面
nmWxAutomaticfittingStart* m_progressMonitor;

@ -22,12 +22,10 @@
#include <QRectF>
#include <QSplitter>
#include "nmCalculationAutoFitGA.h"
#include "nmCalculationAutoFitPSO.h"
// 前向声明
class nmCalculationAutoFitPSO;
class nmCalculationAutoFitGA;
class QPainter;
class QColor;
class QPaintEvent;
@ -68,12 +66,6 @@ private:
bool m_pseudoPressureMode;
};
// 算法类型枚举
enum FittingAlgorithmType {
FITTING_ALGORITHM_PSO = 0,
FITTING_ALGORITHM_GA = 1
};
class nmWxAutomaticfittingStart : public iDlgBase
{
Q_OBJECT
@ -85,8 +77,6 @@ public:
// PSO算法接口
void setAutoFitter(nmCalculationAutoFitPSO* autoFitter);
// GA算法接口
void setAutoFitterGA(nmCalculationAutoFitGA* autoFitter);
// 通用设置接口
void setFittingParameters(int maxIterations, double targetError, const QString& wellName);
@ -161,8 +151,6 @@ private:
// 算法实例
nmCalculationAutoFitPSO* m_autoFitterPSO;
nmCalculationAutoFitGA* m_autoFitterGA;
FittingAlgorithmType m_algorithmType;
// 拟合参数
int m_maxIterations;

File diff suppressed because it is too large Load Diff

@ -12,25 +12,24 @@ nmDataAutomaticFitting::nmDataAutomaticFitting()
m_cfSelected = false; // 默认不选中
m_swiSelected = false; // 默认不选中
// 初始化参数最大值
m_permeabilityMax = nmDataAttribute("Permeability Max", 10.0, "Darcy"); // 1000 mD
m_skinMax = nmDataAttribute("Skin Max", 10.0, ""); // 100
m_wellboreStorageMax = nmDataAttribute("Wellbore Storage Max", 2.0, "m^3/MPa");
m_porosityMax = nmDataAttribute("Porosity Max", 0.5, ""); // 50%
m_thicknessMax = nmDataAttribute("Thickness Max", 50.0, "m");
m_ctMax = nmDataAttribute("Ct Max", 0.1, ""); // 1/MPa
m_cfMax = nmDataAttribute("Cf Max", 0.01, ""); // 1/MPa
m_swiMax = nmDataAttribute("Swi Max", 1.0, "");
// 初始化参数最小值
m_permeabilityMin = nmDataAttribute("Permeability Min", 0.001, "Darcy"); // 0.001 mD
m_skinMin = nmDataAttribute("Skin Min", -10.0, ""); // 允许负表皮
m_wellboreStorageMin = nmDataAttribute("Wellbore Storage Min", 1e-4, "m^3/MPa");
m_porosityMin = nmDataAttribute("Porosity Min", 0.01, ""); // 1%
m_thicknessMin = nmDataAttribute("Thickness Min", 2.0, "m");
m_ctMin = nmDataAttribute("Ct Min", 1e-3, ""); // 小正值
m_cfMin = nmDataAttribute("Cf Min", 1e-5, ""); // 小正值
m_swiMin = nmDataAttribute("Swi Min", 0.0, "");
// 拟合上下界不再使用固定默认值,由自动拟合窗口按数据对象初值和物理边界生成。
m_permeabilityMax = nmDataAttribute("Permeability Max", QVariant(), "Darcy");
m_skinMax = nmDataAttribute("Skin Max", QVariant(), "");
m_wellboreStorageMax = nmDataAttribute("Wellbore Storage Max", QVariant(), "m^3/MPa");
m_porosityMax = nmDataAttribute("Porosity Max", QVariant(), "");
m_thicknessMax = nmDataAttribute("Thickness Max", QVariant(), "m");
m_ctMax = nmDataAttribute("Ct Max", QVariant(), "");
m_cfMax = nmDataAttribute("Cf Max", QVariant(), "");
m_swiMax = nmDataAttribute("Swi Max", QVariant(), "");
m_permeabilityMin = nmDataAttribute("Permeability Min", QVariant(), "Darcy");
m_skinMin = nmDataAttribute("Skin Min", QVariant(), "");
m_wellboreStorageMin = nmDataAttribute("Wellbore Storage Min", QVariant(), "m^3/MPa");
m_porosityMin = nmDataAttribute("Porosity Min", QVariant(), "");
m_thicknessMin = nmDataAttribute("Thickness Min", QVariant(), "m");
m_ctMin = nmDataAttribute("Ct Min", QVariant(), "");
m_cfMin = nmDataAttribute("Cf Min", QVariant(), "");
m_swiMin = nmDataAttribute("Swi Min", QVariant(), "");
// 初始化迭代参数
m_iterationCount = nmDataAttribute("Iteration Count", 20, "");

@ -4,6 +4,9 @@
#include "nmWxParameterProperty.h"
#include "nmDataAnalyzeManager.h"
#include "iSubWndFitting.h"
#include "iSysParaHelper.h"
#include "iParameter.h"
#include "mModuleDefines.h"
#include "mGui/mGuiAnal/iAnalRun.h"
#include "iGuiPlot.h"
#include "ZxObjCurve.h"
@ -32,6 +35,30 @@ bool nmAutoFitUiIsFinite(double value)
#endif
}
// 从系统参数表读取物理边界,读取失败时保留调用方提供的兜底边界。
bool nmAutoFitReadPhysicalRange(const char* parameterName,
double fallbackMin, double fallbackMax, double& minValue, double& maxValue)
{
minValue = fallbackMin;
maxValue = fallbackMax;
iSysParaHelper* paraHelper = _paraHelper;
if(!paraHelper) {
return false;
}
iParameter* parameter = paraHelper->getPara(QString::fromLatin1(parameterName), s_Nm_Serie);
if(!parameter || !nmAutoFitUiIsFinite(parameter->m_dMin)
|| !nmAutoFitUiIsFinite(parameter->m_dMax)
|| parameter->m_dMin >= parameter->m_dMax) {
return false;
}
minValue = parameter->m_dMin;
maxValue = parameter->m_dMax;
return true;
}
// 主界面气井双对数中的源曲线和导数曲线已经采用拟压力量纲.
// 自动拟合直接读取这两条显示曲线, 避免井对象中的原始压力历史数据与结果曲线量纲不一致.
// 两条曲线可能因无效点过滤而长度不同, 因此按时间坐标匹配共同有效点.
@ -199,14 +226,338 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL
renumberVisibleParameterRows(table);
}
// 获取参数的系统物理边界,并为 Swi 叠加当前储层饱和度约束。
bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex,
double& minValue, double& maxValue)
{
static const char* parameterNames[] = {
"Result_K", "Result_W_Skin", "Result_W_C", "Result_phi",
"Result_h", "Result_Cti", "Result_Cf", "Result_Swi"
};
// KAPPA 的边界使用 md、ft、bbl/psi自动拟合界面使用 Darcy、m、m^3/MPa
// 这里统一换算到界面和 PSO 实际使用的单位K 除以 1000h 由 ft 换成 m
// 井筒储集系数的 4.33667154546306e34 bbl/psi 对应约 1e36 m^3/MPa。
// Ct/Cf/Swi 沿用模型参数表边界。
static const double physicalMin[] = {
1.01325027383089e-18, -5.0, 0.0, 1.0e-4, 1.0e-5, 1.0e-30, 1.0e-30, 0.0
};
static const double physicalMax[] = {
1.01325027383089e42, 5000.0, 1.0e36, 0.9999, 1.0e9, 10.0, 10.0, 1.0
};
if(parameterIndex < 0 || parameterIndex >= 8) {
return false;
}
minValue = physicalMin[parameterIndex];
maxValue = physicalMax[parameterIndex];
bool rangeRead = true;
if(parameterIndex >= 5) {
rangeRead = nmAutoFitReadPhysicalRange(parameterNames[parameterIndex],
physicalMin[parameterIndex], physicalMax[parameterIndex], minValue, maxValue);
}
if(parameterIndex == 7) {
double soi = reservoirData.getSoi().getValue().toDouble();
double sgi = reservoirData.getSgi().getValue().toDouble();
if(nmAutoFitUiIsFinite(soi) && nmAutoFitUiIsFinite(sgi)) {
if(soi < 0.0 || sgi < 0.0 || soi + sgi > 1.0) {
// Soi+Sgi 超过 1 时没有可行的 Swi固定到物理下限避免继续搜索非法区间。
minValue = 0.0;
maxValue = 0.0;
} else {
maxValue = qMin(maxValue, 1.0 - soi - sgi);
}
}
}
return rangeRead;
}
// 将一组上下界同步到表格和自动拟合数据,保证 PSO 读取到同一份配置。
void nmWxAutomaticFitting::setParameterRange(int parameterIndex,
double minValue, double maxValue)
{
if(!m_parameterTable || parameterIndex < 0 || parameterIndex >= m_parameterTable->rowCount()
|| !nmAutoFitUiIsFinite(minValue) || !nmAutoFitUiIsFinite(maxValue)) {
return;
}
if(maxValue < minValue) {
qSwap(minValue, maxValue);
}
const bool wasUpdatingRanges = m_updatingParameterRanges;
m_updatingParameterRanges = true;
if(m_parameterTable->item(parameterIndex, 2)) {
m_parameterTable->item(parameterIndex, 2)->setText(QString::number(minValue, 'g', 10));
}
if(m_parameterTable->item(parameterIndex, 4)) {
m_parameterTable->item(parameterIndex, 4)->setText(QString::number(maxValue, 'g', 10));
}
switch(parameterIndex) {
case 0:
automaticFittingData.getPermeabilityMin().setValue(minValue);
automaticFittingData.getPermeabilityMax().setValue(maxValue);
break;
case 1:
automaticFittingData.getSkinMin().setValue(minValue);
automaticFittingData.getSkinMax().setValue(maxValue);
break;
case 2:
automaticFittingData.getWellboreStorageMin().setValue(minValue);
automaticFittingData.getWellboreStorageMax().setValue(maxValue);
break;
case 3:
automaticFittingData.getPorosityMin().setValue(minValue);
automaticFittingData.getPorosityMax().setValue(maxValue);
break;
case 4:
automaticFittingData.getThicknessMin().setValue(minValue);
automaticFittingData.getThicknessMax().setValue(maxValue);
break;
case 5:
automaticFittingData.getCtMin().setValue(minValue);
automaticFittingData.getCtMax().setValue(maxValue);
break;
case 6:
automaticFittingData.getCfMin().setValue(minValue);
automaticFittingData.getCfMax().setValue(maxValue);
break;
case 7:
automaticFittingData.getSwiMin().setValue(minValue);
automaticFittingData.getSwiMax().setValue(maxValue);
break;
default:
break;
}
m_updatingParameterRanges = wasUpdatingRanges;
}
// 根据初值生成首次或拟合后的建议范围,并始终限制在物理边界内。
void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
double centerValue, bool afterFit)
{
if(!m_parameterTable || parameterIndex < 0 || parameterIndex >= 8
|| !nmAutoFitUiIsFinite(centerValue)) {
return;
}
double physicalMin = 0.0;
double physicalMax = 0.0;
getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax);
double reference = centerValue;
const bool positiveParameter = parameterIndex != 1;
// 正值参数初值为 0 时不再借用旧的界面范围,直接从物理边界选取搜索尺度。
if(positiveParameter && reference <= 0.0) {
if(physicalMin > 0.0) {
reference = physicalMin;
} else {
reference = qMax(physicalMax * 0.01, 1.0e-12);
}
}
if(physicalMax < physicalMin) {
return;
}
reference = qBound(physicalMin, reference, physicalMax);
const double boundedCenterValue = qBound(physicalMin, centerValue, physicalMax);
double newMin = physicalMin;
double newMax = physicalMax;
if(parameterIndex == 1) {
const double skinHalfRange = afterFit ? 1.0 : 10.0;
newMin = qMax(physicalMin, reference - skinHalfRange);
newMax = qMin(physicalMax, reference + skinHalfRange);
} else if(reference > 0.0 && !(parameterIndex == 7 && centerValue <= 0.0)) {
const double lowerFactor = afterFit ? 0.5 : 0.1;
const double upperFactor = afterFit ? 2.0 : 10.0;
newMin = qMax(physicalMin, reference * lowerFactor);
newMax = qMin(physicalMax, reference * upperFactor);
} else if(parameterIndex == 7) {
// 没有可靠 Swi 初值时,不把搜索范围压缩到零附近。
newMin = physicalMin;
newMax = physicalMax;
}
// 任何自动范围都必须包含本次使用的中心值,并且不能越过物理边界。
newMin = qMin(newMin, boundedCenterValue);
newMax = qMax(newMax, boundedCenterValue);
newMin = qMax(newMin, physicalMin);
newMax = qMin(newMax, physicalMax);
if(newMax >= newMin) {
setParameterRange(parameterIndex, newMin, newMax);
}
}
// 首次进入自动范围模式时,按当前表格中的初值为所有参数建立建议范围。
void nmWxAutomaticFitting::initializeSuggestedParameterRanges()
{
if(!m_parameterTable) {
return;
}
for(int parameterIndex = 0; parameterIndex < 8; ++parameterIndex) {
QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3);
if(initialItem) {
bool initialOk = false;
const double initialValue = initialItem->text().toDouble(&initialOk);
if(initialOk && nmAutoFitUiIsFinite(initialValue)) {
updateRangeForParameter(parameterIndex, initialValue, false);
} else {
// 数据对象没有提供该初值时使用完整物理区间,不回退到旧的默认范围。
double physicalMin = 0.0;
double physicalMax = 0.0;
getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax);
if(physicalMax >= physicalMin) {
setParameterRange(parameterIndex, physicalMin, physicalMax);
}
}
}
}
}
// 校正已保存的范围:保留物理边界内的用户区间,无交集时按当前初值生成兜底区间。
void nmWxAutomaticFitting::normalizeSavedParameterRanges()
{
if(!m_parameterTable) {
return;
}
for(int parameterIndex = 0; parameterIndex < 8; ++parameterIndex) {
QTableWidgetItem* minItem = m_parameterTable->item(parameterIndex, 2);
QTableWidgetItem* maxItem = m_parameterTable->item(parameterIndex, 4);
QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3);
if(!minItem || !maxItem || !initialItem) {
continue;
}
double physicalMin = 0.0;
double physicalMax = 0.0;
getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax);
bool savedMinOk = false;
bool savedMaxOk = false;
const double savedMin = minItem->text().toDouble(&savedMinOk);
const double savedMax = maxItem->text().toDouble(&savedMaxOk);
const bool savedRangeValid = savedMinOk && savedMaxOk
&& nmAutoFitUiIsFinite(savedMin) && nmAutoFitUiIsFinite(savedMax)
&& savedMax >= savedMin;
if(savedRangeValid && physicalMax >= physicalMin) {
const double clippedMin = qMax(savedMin, physicalMin);
const double clippedMax = qMin(savedMax, physicalMax);
if(clippedMax >= clippedMin) {
setParameterRange(parameterIndex, clippedMin, clippedMax);
continue;
}
}
// 已保存范围无效或与物理边界无交集时,按数据对象初值重新生成。
bool initialOk = false;
const double initialValue = initialItem->text().toDouble(&initialOk);
if(initialOk && nmAutoFitUiIsFinite(initialValue)) {
updateRangeForParameter(parameterIndex, initialValue, false);
} else if(physicalMax >= physicalMin) {
setParameterRange(parameterIndex, physicalMin, physicalMax);
}
}
}
// 校验当前表格中的参数范围parameterIndex 为 -1 时检查所有可见参数行。
bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int parameterIndex)
{
if(!m_parameterTable) {
errorMessage = tr("The parameter table is unavailable.");
return false;
}
if(parameterIndex < -1 || parameterIndex >= 8) {
errorMessage = tr("The parameter row is invalid.");
return false;
}
static const char* parameterNames[] = {
"Permeability", "Skin", "Wellbore storage", "Porosity",
"Thickness", "Ct", "Cf", "Swi"
};
const int firstParameterIndex = parameterIndex < 0 ? 0 : parameterIndex;
const int lastParameterIndex = parameterIndex < 0 ? 8 : parameterIndex + 1;
for(int currentParameterIndex = firstParameterIndex;
currentParameterIndex < lastParameterIndex; ++currentParameterIndex) {
// 隐藏参数不参与当前模型拟合,不用它们的历史值阻塞当前设置。
if(m_parameterTable->isRowHidden(currentParameterIndex)) {
continue;
}
QTableWidgetItem* minItem = m_parameterTable->item(currentParameterIndex, 2);
QTableWidgetItem* initialItem = m_parameterTable->item(currentParameterIndex, 3);
QTableWidgetItem* maxItem = m_parameterTable->item(currentParameterIndex, 4);
if(!minItem || !initialItem || !maxItem) {
errorMessage = tr("The range values for %1 are incomplete.")
.arg(tr(parameterNames[currentParameterIndex]));
return false;
}
bool minOk = false;
bool initialOk = false;
bool maxOk = false;
const double minValue = minItem->text().toDouble(&minOk);
const double initialValue = initialItem->text().toDouble(&initialOk);
const double maxValue = maxItem->text().toDouble(&maxOk);
if(!minOk || !initialOk || !maxOk
|| !nmAutoFitUiIsFinite(minValue)
|| !nmAutoFitUiIsFinite(initialValue)
|| !nmAutoFitUiIsFinite(maxValue)) {
errorMessage = tr("The minimum value, initial value, and maximum value of %1 must be finite numbers.")
.arg(tr(parameterNames[currentParameterIndex]));
return false;
}
double physicalMin = 0.0;
double physicalMax = 0.0;
getPhysicalParameterRange(currentParameterIndex, physicalMin, physicalMax);
if(!nmAutoFitUiIsFinite(physicalMin) || !nmAutoFitUiIsFinite(physicalMax)
|| physicalMax < physicalMin) {
errorMessage = tr("The physical range of %1 is invalid.")
.arg(tr(parameterNames[currentParameterIndex]));
return false;
}
if(minValue < physicalMin || minValue > physicalMax
|| initialValue < physicalMin || initialValue > physicalMax
|| maxValue < physicalMin || maxValue > physicalMax) {
errorMessage = tr("The values of %1 exceed the physical range [%2, %3].")
.arg(tr(parameterNames[currentParameterIndex]))
.arg(QString::number(physicalMin, 'g', 10))
.arg(QString::number(physicalMax, 'g', 10));
return false;
}
if(minValue > maxValue || minValue > initialValue || initialValue > maxValue) {
errorMessage = tr("The values of %1 must satisfy: minimum <= initial value <= maximum.")
.arg(tr(parameterNames[currentParameterIndex]));
return false;
}
}
return true;
}
nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
: iDlgBase(parent)
, m_autoFitterPSO(nullptr)
, m_autoFitterGA(nullptr)
, m_progressDialog(nullptr)
, m_progressTimer(nullptr)
, m_progressMonitor(nullptr)
, m_selectedAlgorithm(ALGORITHM_PSO) // 默认选择PSO算法
, m_autoParameterRanges(true)
// 构造期间先禁止即时校验,避免初始值写入和范围生成之间出现短暂的不一致。
, m_updatingParameterRanges(true)
{
DEBUG_UI(QString("AutoFitting Constructor: this=0x%1").arg((quintptr)this, 0, 16));
@ -233,32 +584,13 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
reservoirData = pManager->getReservoirDataCopy();
automaticFittingData = pManager->getAutomaticFittingDataCopy();
const bool hasSavedFittingData = pManager && pManager->getAutomaticFittingData() != nullptr;
// 自动范围始终开启用户在表格中修改上下限后itemChanged 会临时切换为手工范围。
m_autoParameterRanges = true;
NM_SOLVER_MODEL_TYPE solverModelType = pManager->getSolverModelType();
// 只有尚未点击确定保存过配置时才使用相态默认值;重新打开时保留已保存的数据。
if(pManager->getAutomaticFittingData() == nullptr) {
bool isOilOrWaterSinglePhase = solverModelType == SMT_Oil_ConstPvt ||
solverModelType == SMT_Oil_VariablePvt ||
solverModelType == SMT_Water_ConstPvt ||
solverModelType == SMT_Water_VariablePvt;
if(isOilOrWaterSinglePhase) {
reservoirData.getPermeability().setValue(2.5e-2);
automaticFittingData.getPermeabilityMin().setValue(1.0e-3);
automaticFittingData.getPermeabilityMax().setValue(10.0);
automaticFittingData.getSkinMin().setValue(-10.0);
automaticFittingData.getSkinMax().setValue(10.0);
automaticFittingData.getWellboreStorageMin().setValue(1.0e-4);
automaticFittingData.getWellboreStorageMax().setValue(2.0);
automaticFittingData.getPorosityMin().setValue(1.0e-2);
automaticFittingData.getPorosityMax().setValue(5.0e-1);
automaticFittingData.getThicknessMin().setValue(2.0);
automaticFittingData.getThicknessMax().setValue(50.0);
automaticFittingData.getCtMin().setValue(1.0e-3);
automaticFittingData.getCtMax().setValue(1.0e-1);
automaticFittingData.getCfMin().setValue(1.0e-5);
automaticFittingData.getCfMax().setValue(1.0e-2);
}
// 未保存过配置时只保留参数选择的相态默认值,不再覆盖数据对象中的初值或范围。
if(!hasSavedFittingData) {
if(solverModelType == SMT_Oil_ConstPvt ||
solverModelType == SMT_Water_ConstPvt) {
// T1/T3 的综合压缩系数默认不参与拟合。
@ -268,43 +600,6 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
// T2/T4 的岩石压缩系数默认参与拟合。
automaticFittingData.setCfSelected(true);
}
// 气单相变化 PVT 使用 T5 数据集对应的初始值和拟合范围。
if(solverModelType == SMT_Gas_VariablePvt) {
reservoirData.getPermeability().setValue(5.5e-6);
reservoirData.getPorosity().setValue(3.5e-2);
reservoirData.getThickness().setValue(8.0);
reservoirData.getCf().setValue(3.0e-4);
automaticFittingData.getPermeabilityMin().setValue(4.5e-6);
automaticFittingData.getPermeabilityMax().setValue(6.2e-6);
automaticFittingData.getSkinMin().setValue(0.0);
automaticFittingData.getSkinMax().setValue(4.5);
automaticFittingData.getWellboreStorageMin().setValue(5.0e-5);
automaticFittingData.getWellboreStorageMax().setValue(1.6e-4);
automaticFittingData.getPorosityMin().setValue(8.0e-3);
automaticFittingData.getPorosityMax().setValue(4.0e-2);
automaticFittingData.getThicknessMin().setValue(7.0);
automaticFittingData.getThicknessMax().setValue(11.0);
automaticFittingData.getCfMin().setValue(1.0e-4);
automaticFittingData.getCfMax().setValue(2.0e-3);
} else if(solverModelType == SMT_Oil_ConstPvt ||
solverModelType == SMT_Water_ConstPvt) {
// T1/T3 使用固定初值,不继承当前储层参数。
reservoirData.getPorosity().setValue(2.45e-2);
reservoirData.getThickness().setValue(9.144);
reservoirData.getCt().setValue(1.0e-2);
} else if(solverModelType == SMT_Oil_VariablePvt) {
// 油单相变化 PVT 使用固定初值。
reservoirData.getPorosity().setValue(6.0e-2);
reservoirData.getThickness().setValue(10.5);
reservoirData.getCf().setValue(1.0e-3);
} else if(solverModelType == SMT_Water_VariablePvt) {
// 水单相变化 PVT 使用固定初值。
reservoirData.getPorosity().setValue(6.0e-2);
reservoirData.getThickness().setValue(10.5);
reservoirData.getCf().setValue(1.5e-3);
}
}
setupUI();
@ -315,6 +610,12 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
if(m_targetWellCombo->count() > 0) {
onWellSelected(0); // 默认选中第一口井
}
if(!hasSavedFittingData) {
initializeSuggestedParameterRanges();
} else {
normalizeSavedParameterRanges();
}
m_updatingParameterRanges = false;
DEBUG_UI("AutoFitting Constructor completed");
}
@ -333,9 +634,6 @@ nmWxAutomaticFitting::~nmWxAutomaticFitting()
if (m_autoFitterPSO) {
disconnect(m_autoFitterPSO, nullptr, this, nullptr);
}
if (m_autoFitterGA) {
disconnect(m_autoFitterGA, nullptr, this, nullptr);
}
DEBUG_UI("AutoFitting destructor - completed");
}
@ -514,6 +812,8 @@ void nmWxAutomaticFitting::setupParameterTable()
// 连接选择改变信号
connect(m_parameterTable, SIGNAL(currentCellChanged(int, int, int, int)),
this, SLOT(onCellSelectionChanged(int, int, int, int)));
connect(m_parameterTable, SIGNAL(itemChanged(QTableWidgetItem*)),
this, SLOT(onParameterTableItemChanged(QTableWidgetItem*)));
}
void nmWxAutomaticFitting::setupControlPanel()
@ -528,9 +828,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("GA (Genetic Algorithm)"));
m_algorithmCombo->setCurrentIndex(0); // 默认选择PSO
connect(m_algorithmCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onAlgorithmChanged(int)));
m_algorithmCombo->setCurrentIndex(0);
m_algorithmCombo->setMaximumWidth(160);
m_algorithmCombo->setMinimumWidth(160);
@ -681,35 +979,37 @@ void nmWxAutomaticFitting::onReverseSelection()
if(!m_parameterTable->isRowHidden(7)) m_swiCheckBox->setChecked(!m_swiCheckBox->isChecked());
}
void nmWxAutomaticFitting::onAlgorithmChanged(int index)
void nmWxAutomaticFitting::onParameterTableItemChanged(QTableWidgetItem* item)
{
m_selectedAlgorithm = static_cast<OptimizationAlgorithm>(index);
// 根据算法类型调整界面提示或参数
QString algorithmInfo;
switch(m_selectedAlgorithm) {
case ALGORITHM_PSO:
algorithmInfo = tr("PSO algorithm selected.");
if(m_surrogateCombo) {
m_surrogateCombo->setEnabled(true);
}
break;
case ALGORITHM_GA:
algorithmInfo = tr("GA algorithm selected.");
if(m_surrogateCombo) {
m_surrogateCombo->setEnabled(false);
}
break;
if(!item || m_updatingParameterRanges) {
return;
}
// 在状态栏或工具提示中显示算法信息
m_algorithmCombo->setToolTip(algorithmInfo);
// 用户改动范围后,切井和拟合结果不再自动覆盖这组手工范围。
if(item->column() == 2 || item->column() == 4) {
m_autoParameterRanges = false;
}
if(item->column() >= 2 && item->column() <= 4
&& !m_parameterTable->isRowHidden(item->row())) {
QString validationError;
if(!validateParameterTable(validationError, item->row())) {
// 表格编辑提交后立即提示,用户不需要先点击“确定”才发现错误。
QMessageBox::warning(this, tr("Invalid parameter range"), validationError);
}
}
}
void nmWxAutomaticFitting::onAccept()
{
// 首先保存参数设置
setAutomaticFittingValue();
QString validationError;
if(!validateParameterTable(validationError)) {
QMessageBox::warning(this, tr("Invalid parameter range"), validationError);
return;
}
// 校验通过后再保存参数设置,非法输入不会被静默裁剪。
setAutomaticFittingValue();
// 更新完成后,通知参数界面刷新
nmWxParameterProperty::notifyUpdateTable();
@ -878,28 +1178,6 @@ void nmWxAutomaticFitting::onWellSelected(int index)
}
}
// 尚未保存拟合配置时使用相态默认值;已保存时保留井上的 Skin 和井筒储集系数。
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
bool useDefaultFittingValues = pManager && pManager->getAutomaticFittingData() == nullptr;
if(useDefaultFittingValues && (pManager->getSolverModelType() == SMT_Oil_ConstPvt ||
pManager->getSolverModelType() == SMT_Water_ConstPvt)) {
skinValue = 0.0;
wellboreStorageValue = 1.0e-2;
found = true;
} else if(useDefaultFittingValues && pManager->getSolverModelType() == SMT_Gas_VariablePvt) {
skinValue = 1.0;
wellboreStorageValue = 6.0e-5;
found = true;
} else if(useDefaultFittingValues && pManager->getSolverModelType() == SMT_Oil_VariablePvt) {
skinValue = -0.2;
wellboreStorageValue = 1.5e-2;
found = true;
} else if(useDefaultFittingValues && pManager->getSolverModelType() == SMT_Water_VariablePvt) {
skinValue = -0.2;
wellboreStorageValue = 1.0e-2;
found = true;
}
if (found) {
// 更新表格数据
// 确保表格项存在
@ -915,6 +1193,11 @@ void nmWxAutomaticFitting::onWellSelected(int index)
// 设置井筒储集系数Wellbore storage
m_parameterTable->item(2, 3)->setText(QString::number(wellboreStorageValue));
if(m_autoParameterRanges) {
updateRangeForParameter(1, skinValue, false);
updateRangeForParameter(2, wellboreStorageValue, false);
}
}
}
@ -1041,11 +1324,10 @@ void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targ
// 先清理之前的实例
cleanupFitting();
if (m_selectedAlgorithm == ALGORITHM_PSO) {
DEBUG_UI("Creating PSO auto fitter");
m_autoFitterPSO = new nmCalculationAutoFitPSO(this);
m_autoFitterPSO->setTargetLogLogData(targetData);
m_autoFitterPSO->setPSOTargetWellName(targetWellName);
DEBUG_UI("Creating PSO auto fitter");
m_autoFitterPSO = new nmCalculationAutoFitPSO(this);
m_autoFitterPSO->setTargetLogLogData(targetData);
m_autoFitterPSO->setPSOTargetWellName(targetWellName);
//// 特定井名时使用快速路径
//if (targetWellName == "VerticalWell1") {
@ -1080,46 +1362,23 @@ void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targ
//}
m_progressMonitor = new nmWxAutomaticfittingStart(this);
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)));
int maxIterations = m_iterationEdit->text().toInt();
double targetError = m_errorLimitEdit->text().toDouble();
QString wellName = m_targetWellCombo->currentText();
m_progressMonitor->setFittingParameters(maxIterations, targetError, wellName);
m_progressMonitor->setSelectedParameters(selectedParams);
m_progressMonitor->show();
QTimer::singleShot(100, this, SLOT(runAutoFitting()));
m_progressMonitor = new nmWxAutomaticfittingStart(this);
m_progressMonitor->setAutoFitter(m_autoFitterPSO);
m_progressMonitor->setPseudoPressureMode(
nmDataAnalyzeManager::getCurrentInstance()->getSolverModelType() == SMT_Gas_VariablePvt);
m_progressMonitor->setTargetLogLogData(targetData);
} else if (m_selectedAlgorithm == ALGORITHM_GA) {
DEBUG_UI("Creating GA auto fitter");
m_autoFitterGA = new nmCalculationAutoFitGA(this);
m_autoFitterGA->setTargetLogLogData(targetData);
m_autoFitterGA->setGATargetWellName(targetWellName);
connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
m_progressMonitor = new nmWxAutomaticfittingStart(this);
m_progressMonitor->setAutoFitterGA(m_autoFitterGA);
m_progressMonitor->setTargetLogLogData(targetData);
int maxIterations = m_iterationEdit->text().toInt();
double targetError = m_errorLimitEdit->text().toDouble();
QString wellName = m_targetWellCombo->currentText();
m_progressMonitor->setFittingParameters(maxIterations, targetError, wellName);
m_progressMonitor->setSelectedParameters(selectedParams);
connect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
int maxIterations = m_iterationEdit->text().toInt();
double targetError = m_errorLimitEdit->text().toDouble();
QString wellName = m_targetWellCombo->currentText();
m_progressMonitor->setFittingParameters(maxIterations, targetError, wellName);
m_progressMonitor->setSelectedParameters(selectedParams);
m_progressMonitor->show();
QTimer::singleShot(100, this, SLOT(runAutoFitting()));
}
m_progressMonitor->show();
QTimer::singleShot(100, this, SLOT(runAutoFitting()));
}
void nmWxAutomaticFitting::runAutoFitting()
@ -1128,10 +1387,8 @@ void nmWxAutomaticFitting::runAutoFitting()
m_progressMonitor->markFittingStarted();
}
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
if(m_autoFitterPSO) {
m_autoFitterPSO->startAutoFitting();
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
m_autoFitterGA->startAutoFitting();
}
}
@ -1147,18 +1404,13 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
disconnect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
}
if (m_autoFitterGA) {
disconnect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
}
// 更新最佳参数到表格
updateBestParametersToTable();
if(success) {
// 只有成功拟合的结果才用于生成下一轮范围,失败结果不污染当前配置。
updateBestParametersToTable();
QString resultInfo;
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
if(m_autoFitterPSO) {
double bestFitness = m_autoFitterPSO->getBestFitness();
// 检查是否是用户停止的情况
@ -1173,23 +1425,6 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
resultInfo += tr("Optimized parameters have been applied to the model.");
QMessageBox::information(this, tr("Optimization Completed"), resultInfo);
}
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
// GA的类似处理
double bestFitness = m_autoFitterGA->getBestFitness();
if(message.contains("stopped by user", Qt::CaseInsensitive)) {
resultInfo = tr("GA Optimization stopped by user:\n");
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("GA Optimization completed:\n");
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);
}
}
} else {
// 只有真正失败的情况才显示警告
@ -1199,11 +1434,9 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
void nmWxAutomaticFitting::onStopFitting()
{
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO && m_autoFitterPSO->isRunning()) {
if(m_autoFitterPSO && m_autoFitterPSO->isRunning()) {
m_autoFitterPSO->stopFitting();
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA && m_autoFitterGA->isRunning()) {
m_autoFitterGA->stopFitting();
}
}
}
void nmWxAutomaticFitting::cleanupFitting()
@ -1246,31 +1479,6 @@ void nmWxAutomaticFitting::cleanupFitting()
DEBUG_UI("PSO fitter cleaned up");
}
if (m_autoFitterGA) {
DEBUG_UI("Stopping and disconnecting GA fitter");
disconnect(m_autoFitterGA, nullptr, nullptr, nullptr);
if (m_autoFitterGA->isRunning()) {
m_autoFitterGA->stopFitting();
int waitCount = 0;
while (m_autoFitterGA->isRunning() && waitCount < 50) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
waitCount++;
}
// 如果仍在运行则强制停止
if (m_autoFitterGA->isRunning()) {
DEBUG_UI("Force stopping GA - timeout reached");
}
}
delete m_autoFitterGA;
m_autoFitterGA = nullptr;
DEBUG_UI("GA fitter cleaned up");
}
// 清理进度监控
if (m_progressMonitor) {
// 先断开进度监控的信号连接
@ -1296,10 +1504,8 @@ void nmWxAutomaticFitting::updateBestParametersToTable()
QVector<double> bestSolution;
// 获取最佳解决方案
if (m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
if (m_autoFitterPSO) {
bestSolution = m_autoFitterPSO->getBestSolution();
} else if (m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
bestSolution = m_autoFitterGA->getBestSolution();
}
if (bestSolution.isEmpty()) return;
@ -1315,78 +1521,19 @@ void nmWxAutomaticFitting::updateBestParametersToTable()
if(m_cfCheckBox->isChecked()) enabledParams.append(6); // 岩石压缩系数
if(m_swiCheckBox->isChecked()) enabledParams.append(7); // 初始含水饱和度
// 范围收缩比例
double shrinkFactor = 0.3;
// 更新参数值和范围
for (int i = 0; i < bestSolution.size() && i < enabledParams.size(); ++i) {
int paramIndex = enabledParams[i];
double bestValue = bestSolution[i];
if(!nmAutoFitUiIsFinite(bestValue)) {
continue;
}
// 更新初始值
m_parameterTable->item(paramIndex, 3)->setText(QString::number(bestValue, 'g', 4));
// 获取当前的最小值和最大值
double currentMin = m_parameterTable->item(paramIndex, 2)->text().toDouble();
double currentMax = m_parameterTable->item(paramIndex, 4)->text().toDouble();
double currentRange = currentMax - currentMin;
// 计算新的范围
double newHalfRange = currentRange * shrinkFactor * 0.5;
double newMin = bestValue - newHalfRange;
double newMax = bestValue + newHalfRange;
// 确保某些参数不为负数
if (paramIndex == 0 || paramIndex == 2 || paramIndex == 3 || paramIndex == 4) {
newMin = qMax(newMin, 0.0);
}
// 确保最小范围,避免范围过小
double minAllowedRange = currentRange * 0.05;
if ((newMax - newMin) < minAllowedRange) {
double center = (newMax + newMin) * 0.5;
newMin = center - minAllowedRange * 0.5;
newMax = center + minAllowedRange * 0.5;
}
// 更新表格中的最小值和最大值
m_parameterTable->item(paramIndex, 2)->setText(QString::number(newMin, 'g', 4));
m_parameterTable->item(paramIndex, 4)->setText(QString::number(newMax, 'g', 4));
// 保存收缩后的范围
switch(paramIndex) {
case 0: // 渗透率
automaticFittingData.getPermeabilityMin().setValue(newMin);
automaticFittingData.getPermeabilityMax().setValue(newMax);
break;
case 1: // 表皮系数
automaticFittingData.getSkinMin().setValue(newMin);
automaticFittingData.getSkinMax().setValue(newMax);
break;
case 2: // 井筒储集系数
automaticFittingData.getWellboreStorageMin().setValue(newMin);
automaticFittingData.getWellboreStorageMax().setValue(newMax);
break;
case 3: // 孔隙度
automaticFittingData.getPorosityMin().setValue(newMin);
automaticFittingData.getPorosityMax().setValue(newMax);
break;
case 4: // 储层厚度
automaticFittingData.getThicknessMin().setValue(newMin);
automaticFittingData.getThicknessMax().setValue(newMax);
break;
case 5: // 综合压缩系数
automaticFittingData.getCtMin().setValue(newMin);
automaticFittingData.getCtMax().setValue(newMax);
break;
case 6: // 岩石压缩系数
automaticFittingData.getCfMin().setValue(newMin);
automaticFittingData.getCfMax().setValue(newMax);
break;
case 7: // 初始含水饱和度
automaticFittingData.getSwiMin().setValue(newMin);
automaticFittingData.getSwiMax().setValue(newMax);
break;
// 只有自动范围模式才根据拟合结果收窄下一轮搜索区间;手工范围由用户保留。
if(m_autoParameterRanges) {
updateRangeForParameter(paramIndex, bestValue, true);
}
}

@ -383,8 +383,6 @@ nmWxAutomaticfittingStart::nmWxAutomaticfittingStart(QWidget *parent)
, chartGroup(nullptr)
, curveChart(nullptr)
, m_autoFitterPSO(nullptr)
, m_autoFitterGA(nullptr)
, m_algorithmType(FITTING_ALGORITHM_PSO)
, m_maxIterations(100)
, m_targetError(0.001)
, m_wellName("")
@ -580,8 +578,6 @@ void nmWxAutomaticfittingStart::setupControlArea()
void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitter)
{
m_autoFitterPSO = autoFitter;
m_autoFitterGA = nullptr; // 清空GA实例
m_algorithmType = FITTING_ALGORITHM_PSO;
// 更新算法类型显示
algorithmTypeValue->setText("PSO");
@ -604,31 +600,6 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitte
}
}
void nmWxAutomaticfittingStart::setAutoFitterGA(nmCalculationAutoFitGA* autoFitter)
{
m_autoFitterGA = autoFitter;
m_autoFitterPSO = nullptr; // 清空PSO实例
m_algorithmType = FITTING_ALGORITHM_GA;
// 更新算法类型显示
algorithmTypeValue->setText("GA");
algorithmTypeValue->setStyleSheet("QLabel { color: red; font-weight: bold; }");
if (m_autoFitterGA) {
connect(m_autoFitterGA, SIGNAL(progressUpdated(int, double)),
this, SLOT(onFittingProgress(int, double)));
connect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)),
this, SLOT(onFittingFinished(bool, QString)));
connect(m_autoFitterGA, SIGNAL(logMessageGenerated(QString)),
this, SLOT(onLogMessageReceived(QString)));
// 启用停止按钮
stopButton->setEnabled(true);
addLogMessage(tr("GA auto fitting started"));
}
}
void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double targetError, const QString& wellName)
{
m_maxIterations = maxIterations;
@ -641,7 +612,7 @@ void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double t
progressBar->setRange(0, maxIterations);
QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA";
const QString algorithmName = "PSO";
addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4")
.arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName));
}
@ -650,7 +621,7 @@ void nmWxAutomaticfittingStart::markFittingStarted()
{
m_startTime = QDateTime::currentDateTime();
QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA";
const QString algorithmName = "PSO";
QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss");
addLogMessage(tr("=== %1 Fitting Session Started at %2 ===")
@ -665,7 +636,7 @@ void nmWxAutomaticfittingStart::setSelectedParameters(const QStringList& paramet
// 立即更新参数表格
updateParameterTable();
QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA";
const QString algorithmName = "PSO";
addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", ")));
}
@ -716,7 +687,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
{
m_isFinished = true;
QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA";
const QString algorithmName = "PSO";
// 更新状态
if (success) {
@ -800,25 +771,18 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
void nmWxAutomaticfittingStart::onStopButtonClicked()
{
bool isRunning = false;
if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) {
isRunning = m_autoFitterPSO->isRunning();
} else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) {
isRunning = m_autoFitterGA->isRunning();
}
const bool isRunning = m_autoFitterPSO && m_autoFitterPSO->isRunning();
if (isRunning) {
QString algorithmName = (m_algorithmType == FITTING_ALGORITHM_PSO) ? "PSO" : "GA";
const QString algorithmName = "PSO";
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,
QMessageBox::No);
if (ret == QMessageBox::Yes) {
if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) {
if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting();
} else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) {
m_autoFitterGA->stopFitting();
}
addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName));
}
@ -840,16 +804,11 @@ void nmWxAutomaticfittingStart::updateParameterTable()
// 最优参数值
QString valueText = "N/A";
if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) {
if (m_autoFitterPSO) {
QVector<double> bestSolution = m_autoFitterPSO->getBestSolution();
if (i < bestSolution.size()) {
valueText = formatScientific(bestSolution[i]);
}
} else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) {
QVector<double> bestSolution = m_autoFitterGA->getBestSolution();
if (i < bestSolution.size()) {
valueText = formatScientific(bestSolution[i]);
}
}
QTableWidgetItem* valueItem = new QTableWidgetItem(valueText);
@ -883,16 +842,8 @@ QString nmWxAutomaticfittingStart::formatScientific(double value)
void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
{
bool isRunning = false;
QString algorithmName;
if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) {
isRunning = m_autoFitterPSO->isRunning();
algorithmName = "PSO";
} else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) {
isRunning = m_autoFitterGA->isRunning();
algorithmName = "GA";
}
const bool isRunning = m_autoFitterPSO && m_autoFitterPSO->isRunning();
const QString algorithmName = "PSO";
if (isRunning && !m_isFinished) {
int ret = QMessageBox::question(this, tr("Confirm Close"),
@ -901,10 +852,8 @@ void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event)
QMessageBox::No);
if (ret == QMessageBox::Yes) {
if (m_algorithmType == FITTING_ALGORITHM_PSO && m_autoFitterPSO) {
if (m_autoFitterPSO) {
m_autoFitterPSO->stopFitting();
} else if (m_algorithmType == FITTING_ALGORITHM_GA && m_autoFitterGA) {
m_autoFitterGA->stopFitting();
}
event->accept();
} else {

Loading…
Cancel
Save