#ifndef NMCALCULATIONDLLPEBISOLVERTASK_H #define NMCALCULATIONDLLPEBISOLVERTASK_H #include #include #include #include #include #include #include #include #include #include #include "nmCalculationDefine.h" #include "nmCalculation_global.h" #include "nmDataSensitive.h" class nmDataAnalyzeManager; class nmPebiResultSnapshot; class vtkDoubleArray; struct nmPebiSolverInputSnapshot; struct nmSensitivityFitSettings; struct nmPebiManualCaptureState; struct nmPebiStaticPropertyValues; // DLL 类型只用于私有函数的引用参数,界面模块无需包含求解器内部头文件。 struct HX_NWTM_MODEL_INPUT; struct HX_NWTM_MODEL_OUTPUT; // 主窗口现在直接创建DLL求解线程任务,因此类需要导出,供nmSubWnd模块跨DLL使用。 class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { Q_OBJECT public: /** * @brief 创建绑定到指定分析窗口的 PEBI 求解任务。 * @param sPostprocessingDir 后处理文件输出目录。 * @param pDataManager 任务所属数据管理器,不转移所有权。 * @param sAutoFitTargetWellName 自动拟合目标井名;空值表示完整成果计算。 * @param parent Qt 父对象。 * @param bDeferManualSnapshot 手工求解是否由界面事件循环分批捕获输入。 * @param bUseSolvedInput 敏感性分析只恢复最后成功模型的输入,不捕获当前编辑值。 */ nmCalculationDllPebiSolverTask(QString sPostprocessingDir, nmDataAnalyzeManager* pDataManager = nullptr, const QString& sAutoFitTargetWellName = QString(), QObject *parent = nullptr, bool bDeferManualSnapshot = false, bool bUseSolvedInput = false); ~nmCalculationDllPebiSolverTask(); /** @brief QThread 入口,执行单次或敏感性批量求解并发送一次完成信号。 */ void run(); // PSO 等待线程结束后通过该接口判断本次结果是否可以继续参与误差计算. // 返回 false 时调用方会丢弃本次结果, 防止复用上一粒子留下的旧曲线. bool wasSuccessful() const; /** @brief 获取本次DLL内部求解耗时,单位为毫秒。 */ int getSolveTimeMs() const; /** @brief 获取本次求解的PEBI网格数量。 */ int getPebiCount() const; /** @brief 返回当前代码已识别的失败原因;成功或主动停止时为空。 */ QString getFailureReason() const; /** @brief 请求协作式停止;DLL 正在执行时会在返回后丢弃结果。 */ void requestCancel(); /** @brief 返回本次任务是否因手工停止而结束。 */ bool wasCancelled() const; /** @brief 返回任务启动后网格或求解输入是否已经变化。 */ bool hasInputChanged(nmDataAnalyzeManager* pDataManager) const; /** * @brief 在 DataManager 所属线程分批捕获手工求解输入。 * @param nMaxWellCount 本次最多捕获的真实井数量。 * @param bFinished 全部值快照完成时置为 true,随后才允许 start()。 */ bool captureManualInputStep(int nMaxWellCount, bool& bFinished); /** * @brief 在主线程一次性提交已经完整校验的求解结果。 * @param pDataManager 启动任务时绑定的数据管理器,必须仍然存活且版本一致。 * @return 全部结果提交成功返回 true;自动拟合任务和过期任务返回 false。 */ bool commitResult(nmDataAnalyzeManager* pDataManager); /** @brief 返回自动拟合目标井的原始压力结果副本。 */ QVector > getAutoFitResultPressure() const; /** @brief 返回自动拟合目标井的双对数结果副本。 */ QVector > getAutoFitResultLogLog() const; /** @brief 返回自动拟合目标井的半对数结果副本。 */ QVector > getAutoFitResultSemiLog() const; /** * @brief 从目标井值快照创建可重复使用的独立采样任务,不修改管理器或正式成果。 * @param sampling 使用业务基准单位的采样配置。 * @param runIndex 与 getRunValues() 对应的运行编号,从零开始。 * @param errorMessage 创建失败的原因。 * @return 调用方持有的任务;输入或基础网格无效时返回空指针。 * @note 源任务需指定目标井且未启动;返回任务通过 run()/start() 求解, * 完成后保存 getAutoFitResultPressure/LogLog/SemiLog() 返回的结果, * 后续各组通过 updateSensitivityRun() 更新同一个任务。 */ nmCalculationDllPebiSolverTask* createSensitivityRun( const nmDataSensitive& sampling, quint64 runIndex, QString* errorMessage = nullptr) const; /** * @brief 恢复冻结的基准参数,再填入下一组采样值并清空上次状态及曲线。 * @note 仅用于 createSensitivityRun() 返回的任务。调用方必须串行调用; * start() 后需 wait() 完成,直接 run() 时也需返回后才可更新。 * 更新失败后输入无效,需再次成功更新后才能求解。 */ bool updateSensitivityRun(const nmDataSensitive& sampling, quint64 runIndex, QString* errorMessage = nullptr); /** @brief 一组采样的临时返回值,参数顺序与采样配置的变量列表一致。 */ typedef nmDataSensitive::RunResult SensitivityRunResult; /** * @brief 从源快照创建一份副本,按确定性组合或 Monte-Carlo 预览样本顺序串行求解。 * @param results 本次批量返回值;进入时清空,每组结果在更新下一组前独立保留。 * @return 全部组合遍历完成返回 true,单组成功与否读取对应 m_bSucceeded。 * 配置、基础网格或内存不足时返回 false;已完成的结果仍保留。 * @note 源任务要求与 createSensitivityRun() 相同。此接口同步执行,调用方 * 负责安排执行线程和在 DataManager 所属线程提前捕获源输入。 */ bool calculateSensitivityBatch(const nmDataSensitive& sampling, std::vector& results, QString* errorMessage = nullptr) const; /** @brief 创建后台分析任务;传入拟合设置时对每组样本执行 LM 拟合。 */ nmCalculationDllPebiSolverTask* createSensitivityBatch( const nmDataSensitive& sampling, QString* errorMessage = nullptr, const nmSensitivityFitSettings* improveSettings = nullptr) const; /** @brief 线程结束后读取临时批量结果,引用在任务析构或重新运行后失效。 */ const std::vector& getSensitivityResults() const; /** @brief 仅在启动前或线程结束后获取批次数据,供主线程保存及显示。 */ QSharedPointer getSensitivityBatchResult() const; /** @brief 线程结束后判断全部组合是否处理完成,单组失败不影响此标记。 */ bool wasSensitivityBatchCompleted() const; // 基准模型与采样模型共用压力误差口径,返回 -1 表示无有效误差。 static double calculatePressureRmse(const QVector >& measured, const QVector >& simulated); // 从最后成功输入的裂缝端点读取半长,不访问当前编辑中的井对象。 static bool getSolvedFractureHalfLength(const QSharedPointer& input, const QString& wellCode, double& halfLength); private: /** @brief 仅复制目标井求解的值快照,不登记或访问实时管理器。 */ nmCalculationDllPebiSolverTask(const nmCalculationDllPebiSolverTask& source, QObject* parent); /** @brief 在当前线程执行一组输入,更新状态但不发送整次任务完成信号。 */ void calculateSingleRun(); /** @brief 复用当前独立副本串行计算,保留进度、停止状态及每组失败原因。 */ bool executeSensitivityBatch(); /** @brief 固定本组采样参数,在独立输入上拟合并生成最终曲线。 */ bool improveSensitivityRun(QVector& fittedParameters); /** @brief 执行当前唯一的 PEBI 求解模式。 */ bool execute(); /** @brief 成对释放构造时登记的 DataManager 后台使用权。 */ void releaseDataManagerUse(); /** @brief 丢弃正式结果候选及任务侧全部 VTK 可写别名,不影响旧快照。 */ void discardPendingFullResult(); /** @brief 同步路径在任务创建线程一次性捕获全部求解输入。 */ bool captureInputSnapshot(const QString& sAutoFitTargetWellName); /** @brief 从所属分析的最后成功结果恢复完整输入。 */ bool restoreSolvedInput(); /** @brief 手工求解在线程内完成场景、缓存和 VTK 基础网格准备。 */ bool prepareManualInput(); /** @brief 无锁读取原子停止标记。 */ bool isCancelRequested() const; /** @brief 仅为手工求解报告真实执行阶段。 */ void reportStage(const QString& sStage, int nCurrent, int nTotal); /** @brief 记录首个已识别的失败原因并统一返回 false。 */ bool fail(const QString& sFailureReason); /** @brief 构造 DLL 输入、执行求解并生成仍然有效的局部结果快照。 */ bool execPebiMode(); /** @brief 按求解器井顺序组装压力、曲线和场结果快照,不修改 DataManager。 */ bool buildPebiModeResult(HX_NWTM_MODEL_OUTPUT& p1, int nModelType, nmPebiStaticPropertyValues& oStaticProperties); /** @brief 用任务局部输出构造并封闭完整结果候选,不访问实时数据对象。 */ bool buildPebiResultSnapshotCandidate( nmPebiStaticPropertyValues& oStaticProperties); /** @brief 输出精简版 DLL 输入日志,用于定位求解异常。 */ void logHX_NWTM_MODEL_INPUT_Simplified(const HX_NWTM_MODEL_INPUT& p0); /** @brief 将完整 DLL 输入保存到文本文件,成功时返回 true。 */ bool saveHX_NWTM_MODEL_INPUT_ToTxt(const HX_NWTM_MODEL_INPUT& p0, const QString& filePath); private: /** @brief 一口真实井在本次求解中完整生成的待提交结果。 */ struct nmPebiWellResultSnapshot { QString m_sWellCode; ///< 稳定井编码。 QVector > m_vecPressure; ///< 时间和井底压力。 QVector > m_vecLogLog; ///< 时间、压差及导数。 QVector > m_vecSemiLog; ///< 时间和半对数压力。 }; QString m_sPostprocessingDir; ///< 本次求解使用的后处理输出目录。 /** @brief 创建任务时捕获所属数据中心,工作线程不得读取全局当前窗口。 */ nmDataAnalyzeManager* m_pDataManager; /** @brief 任务独占的完整值快照;析构时释放,后台禁止访问 DataManager。 */ nmPebiSolverInputSnapshot* m_pInputSnapshot; /** @brief 仅在界面线程分批捕获期间持有井对象,线程启动前必须释放。 */ nmPebiManualCaptureState* m_pManualCaptureState; bool m_bManagerUseActive; ///< 是否仍持有 DataManager 后台使用权。 bool m_bInputSnapshotValid; ///< 构造阶段是否已完成全部输入校验。 /** @brief 创建任务时捕获网格输入版本,防止回填过期网格上的结果。 */ quint64 m_nGridInputRevision; /** @brief 创建任务时捕获求解输入版本,防止角色或参数变化后回填旧结果。 */ quint64 m_nResultInputRevision; bool m_lastRunSucceeded; ///< run() 保存的执行状态,供等待线程安全读取。 /** @brief 本次DLL内部求解耗时,单位为毫秒。 */ int m_nSolveTimeMs; /** @brief 本次求解的PEBI网格数量。 */ int m_nPebiCount; QString m_sFailureReason; ///< 当前代码已识别的首个失败原因。 QString m_sAutoFitTargetWellCode; ///< 自动拟合目标井 WellCode;空值表示保存完整结果。 QVector > m_autoFitResultPressure; ///< 目标井原始压力结果。 QVector > m_autoFitResultLogLog; ///< 目标井双对数结果。 QVector > m_autoFitResultSemiLog; ///< 目标井半对数结果。 QVector m_vecPendingWellResults; ///< 完整求解的井结果快照。 QMap > m_mapPendingTimeSteps; ///< 场压力时间步快照。 /** @brief 工作线程已经完整校验、等待主线程发布的可写候选引用。 */ QSharedPointer m_pPendingResultSnapshot; double m_dPendingScalarMin; ///< 全部场压力的最小值。 double m_dPendingScalarMax; ///< 全部场压力的最大值。 bool m_bPendingFullResultReady; ///< 是否已有可由主线程一次提交的完整结果。 QAtomicInt m_nCancelRequested; ///< 跨线程协作式停止标记,1 表示请求停止。 QAtomicInt m_nWasCancelled; ///< run() 已按停止请求丢弃本次局部结果。 QSharedPointer m_pSensitivityFitSettings; ///< 第四种批量拟合的确认设置。 QSharedPointer m_pSensitivitySampling; ///< 批量启动前冻结的配置。 QSharedPointer m_pSensitivityResult; ///< 后台独占的批次数据。 QString m_sSensitivityFailureReason; ///< 整次批量无法继续执行的原因。 bool m_bSensitivityBatchCompleted; ///< 全部组合是否已处理,停止时为 false。 private slots: //void slotTaskUpdateProgress(); signals: /** @brief 即将实际调用数值求解器,供 LM 统计调用次数。 */ void solverInvoked(); void sig_calculateDone(bool isSuccessed); /** @brief 报告当前真实阶段;nTotal<=0 表示 DLL 等不可测阶段。 */ void sigStageChanged(QString sStage, int nCurrent, int nTotal); /** @brief 敏感性已处理组数、总组数和失败组数,按时间节流发送。 */ void sigSensitivityProgress(quint64 nProcessed, quint64 nTotal, quint64 nFailed); //void sigTaskProgressUpdated(int progress); //void sigResSolverProgressUpdated(int progress); }; #endif // NMCALCULATIONDLLPEBISOLVERTASK_H