|
|
|
@ -695,6 +695,7 @@ nmCalculationAutoFitLM::nmCalculationAutoFitLM(QObject* parent)
|
|
|
|
, m_maxIterations(100)
|
|
|
|
, m_maxIterations(100)
|
|
|
|
, m_targetError(0.001)
|
|
|
|
, m_targetError(0.001)
|
|
|
|
, m_totalEvaluations(0)
|
|
|
|
, m_totalEvaluations(0)
|
|
|
|
|
|
|
|
, m_solverCallCount(0)
|
|
|
|
, m_successfulEvaluations(0)
|
|
|
|
, m_successfulEvaluations(0)
|
|
|
|
, m_evaluationInProgress(0)
|
|
|
|
, m_evaluationInProgress(0)
|
|
|
|
, m_consecutiveFailures(0)
|
|
|
|
, m_consecutiveFailures(0)
|
|
|
|
@ -791,15 +792,15 @@ void nmCalculationAutoFitLM::setTargetLogLogData(const QVector<QVector<double> >
|
|
|
|
void nmCalculationAutoFitLM::stopFitting()
|
|
|
|
void nmCalculationAutoFitLM::stopFitting()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(m_isFinalizing) {
|
|
|
|
if(m_isFinalizing) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("The final full-field calculation is already running"));
|
|
|
|
tr("The final full-field calculation is already running"));
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 用户点击停止时只设置请求标志,让 LM 主循环和求解器等待逻辑自然退出。
|
|
|
|
// 用户点击停止时只设置请求标志,让 LM 主循环和求解器等待逻辑自然退出。
|
|
|
|
if(m_isRunning) {
|
|
|
|
if(m_isRunning) {
|
|
|
|
emit logMessageGenerated(tr("=== User Stop Request Received ==="));
|
|
|
|
DEBUG_OUT(tr("=== User Stop Request Received ==="));
|
|
|
|
emit logMessageGenerated(tr("Gracefully stopping LM automatic fitting..."));
|
|
|
|
DEBUG_OUT(tr("Gracefully stopping LM automatic fitting..."));
|
|
|
|
m_shouldStop = true;
|
|
|
|
m_shouldStop = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 此槽由求解等待循环派发,必须立即返回,外层循环才能转发取消请求。
|
|
|
|
// 此槽由求解等待循环派发,必须立即返回,外层循环才能转发取消请求。
|
|
|
|
@ -807,9 +808,9 @@ void nmCalculationAutoFitLM::stopFitting()
|
|
|
|
emit logMessageGenerated(tr("Waiting for current solver evaluation to stop..."));
|
|
|
|
emit logMessageGenerated(tr("Waiting for current solver evaluation to stop..."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("LM automatic fitting stop request processed"));
|
|
|
|
DEBUG_OUT(tr("LM automatic fitting stop request processed"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
emit logMessageGenerated(tr("Stop request received but optimization is not running"));
|
|
|
|
DEBUG_OUT(tr("Stop request received but optimization is not running"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -827,10 +828,22 @@ int nmCalculationAutoFitLM::getCurrentIteration() const
|
|
|
|
|
|
|
|
|
|
|
|
int nmCalculationAutoFitLM::getTotalEvaluations() const
|
|
|
|
int nmCalculationAutoFitLM::getTotalEvaluations() const
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 返回本轮拟合实际调用真实求解器的总次数,供完成日志展示。
|
|
|
|
// 返回评价次数,用于迭代预算;真实求解器调用次数另行统计。
|
|
|
|
return m_totalEvaluations;
|
|
|
|
return m_totalEvaluations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int nmCalculationAutoFitLM::getSolverCallCount() const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 包含失败调用、重试和最终完整求解,不包含未进入求解器的评价。
|
|
|
|
|
|
|
|
return m_solverCallCount;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void nmCalculationAutoFitLM::recordSolverCall()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 信号来自求解线程,使用原子累加避免跨线程读写竞争。
|
|
|
|
|
|
|
|
m_solverCallCount.fetchAndAddOrdered(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVector<double> nmCalculationAutoFitLM::getBestSolution() const
|
|
|
|
QVector<double> nmCalculationAutoFitLM::getBestSolution() const
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 返回紧凑的“启用参数向量”,顺序与 m_enabledParamIndices 一致。
|
|
|
|
// 返回紧凑的“启用参数向量”,顺序与 m_enabledParamIndices 一致。
|
|
|
|
@ -873,6 +886,7 @@ void nmCalculationAutoFitLM::resetOptimizer()
|
|
|
|
m_comparisonTimeMax = 0.0;
|
|
|
|
m_comparisonTimeMax = 0.0;
|
|
|
|
m_currentIteration = 0;
|
|
|
|
m_currentIteration = 0;
|
|
|
|
m_totalEvaluations = 0;
|
|
|
|
m_totalEvaluations = 0;
|
|
|
|
|
|
|
|
m_solverCallCount = 0;
|
|
|
|
m_successfulEvaluations = 0;
|
|
|
|
m_successfulEvaluations = 0;
|
|
|
|
m_lastError.clear();
|
|
|
|
m_lastError.clear();
|
|
|
|
m_initialValues.clear();
|
|
|
|
m_initialValues.clear();
|
|
|
|
@ -931,9 +945,9 @@ void nmCalculationAutoFitLM::initializeTraceFile()
|
|
|
|
|
|
|
|
|
|
|
|
writeTraceHeader();
|
|
|
|
writeTraceHeader();
|
|
|
|
writeTraceMetaFile();
|
|
|
|
writeTraceMetaFile();
|
|
|
|
emit logMessageGenerated(tr("LM fitting trace: %1").arg(m_traceFilePath));
|
|
|
|
DEBUG_OUT(tr("LM fitting trace: %1").arg(m_traceFilePath));
|
|
|
|
if(!m_traceMetaFilePath.isEmpty()) {
|
|
|
|
if(!m_traceMetaFilePath.isEmpty()) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("LM fitting trace metadata: %1").arg(m_traceMetaFilePath));
|
|
|
|
tr("LM fitting trace metadata: %1").arg(m_traceMetaFilePath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1172,26 +1186,48 @@ void nmCalculationAutoFitLM::writeTraceRow(
|
|
|
|
m_traceFile.flush();
|
|
|
|
m_traceFile.flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmCalculationAutoFitLM::emitRunSummary(bool success, StopReasonLM finalReason)
|
|
|
|
// 窗口统一显示普通小数;失败评价不展示惩罚值或非有限数。
|
|
|
|
|
|
|
|
static QString lmLogError(double value)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 汇总仅描述 LM 迭代和真实求解器评价。
|
|
|
|
return isFiniteNumber(value) && value < 1.0e9
|
|
|
|
emit logMessageGenerated(tr("=== LM Run Summary ==="));
|
|
|
|
? QString::number(value, 'f', 6) : QString("--");
|
|
|
|
emit logMessageGenerated(tr("Stop reason: %1").arg(getStopReasonDescription(finalReason)));
|
|
|
|
|
|
|
|
emit logMessageGenerated(
|
|
|
|
|
|
|
|
tr("Result: %1, final error=%2, iterations=%3, evaluations=%4 (successful=%5, failed=%6)")
|
|
|
|
|
|
|
|
.arg(success ? tr("SUCCESS") : tr("FAILED"))
|
|
|
|
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
|
|
|
|
.arg(m_currentIteration + 1)
|
|
|
|
|
|
|
|
.arg(m_totalEvaluations)
|
|
|
|
|
|
|
|
.arg(m_successfulEvaluations)
|
|
|
|
|
|
|
|
.arg(m_totalEvaluations - m_successfulEvaluations));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_traceFilePath.isEmpty()) {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Artifacts: trace=%1").arg(m_traceFilePath));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!m_traceMetaFilePath.isEmpty()) {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Artifacts: trace_meta=%1").arg(m_traceMetaFilePath));
|
|
|
|
void nmCalculationAutoFitLM::emitRunSummary(bool success, StopReasonLM finalReason)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 最终只汇总总误差,耗时由进程窗口在收到结束信号后追加。
|
|
|
|
|
|
|
|
emit logMessageGenerated(success ? tr("[Fitting completed]") : tr("[Fitting ended]"));
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Stop reason: %1").arg(success
|
|
|
|
|
|
|
|
? getStopReasonDescription(finalReason)
|
|
|
|
|
|
|
|
: (m_lastError.isEmpty() ? getStopReasonDescription(finalReason) : m_lastError)));
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Total error: %1 -> %2")
|
|
|
|
|
|
|
|
.arg(lmLogError(m_userInitialFitness)).arg(lmLogError(m_globalBestFitness)));
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Solver calls: %1").arg(getSolverCallCount()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void nmCalculationAutoFitLM::emitTrialLog(const QString& title,
|
|
|
|
|
|
|
|
const AutoFitObjectiveBreakdownLM& before, const AutoFitObjectiveBreakdownLM& after,
|
|
|
|
|
|
|
|
bool valid, bool accepted, bool shapeStage)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 只显示当前阶段目标;改善率相对上次已接受的误差计算,变差时为负值。
|
|
|
|
|
|
|
|
if(!valid) {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("%1 | Solver failed\nNo valid error was returned; keep the last accepted result and reduce the step.").arg(title));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString status = accepted ? tr("accepted") : tr("rejected");
|
|
|
|
|
|
|
|
const double beforeError = shapeStage ? before.shapeLoss : before.total;
|
|
|
|
|
|
|
|
const double afterError = shapeStage ? after.shapeLoss : after.total;
|
|
|
|
|
|
|
|
const QString objective = (shapeStage ? tr("Shape error: %1 -> %2")
|
|
|
|
|
|
|
|
: tr("Total error: %1 -> %2"))
|
|
|
|
|
|
|
|
.arg(lmLogError(beforeError)).arg(lmLogError(afterError));
|
|
|
|
|
|
|
|
// 起点为零时相对改善率无定义,避免除零。
|
|
|
|
|
|
|
|
const QString improvement = beforeError > 0.0
|
|
|
|
|
|
|
|
? QString::number((beforeError - afterError) / beforeError * 100.0, 'f', 2)
|
|
|
|
|
|
|
|
: QString("--");
|
|
|
|
|
|
|
|
QString message = tr("%1 | %2 | %3 | Improvement: %4%")
|
|
|
|
|
|
|
|
.arg(title).arg(status).arg(objective).arg(improvement);
|
|
|
|
|
|
|
|
if(!accepted) message += tr("\n Objective error did not decrease; keep the last accepted result and reduce the step.");
|
|
|
|
|
|
|
|
emit logMessageGenerated(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVector<double> nmCalculationAutoFitLM::buildTraceParameterVector(const QVector<double>& selectedParameters) const
|
|
|
|
QVector<double> nmCalculationAutoFitLM::buildTraceParameterVector(const QVector<double>& selectedParameters) const
|
|
|
|
@ -1353,9 +1389,9 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Algorithm: LM"));
|
|
|
|
DEBUG_OUT(tr("Algorithm: LM"));
|
|
|
|
const int enabledParams = getEnabledParameterCount();
|
|
|
|
const int enabledParams = getEnabledParameterCount();
|
|
|
|
emit logMessageGenerated(tr("Enabled parameters count: %1").arg(enabledParams));
|
|
|
|
DEBUG_OUT(tr("Enabled parameters count: %1").arg(enabledParams));
|
|
|
|
|
|
|
|
|
|
|
|
if(enabledParams == 0) {
|
|
|
|
if(enabledParams == 0) {
|
|
|
|
m_lastError = tr("No parameters enabled for optimization");
|
|
|
|
m_lastError = tr("No parameters enabled for optimization");
|
|
|
|
@ -1382,7 +1418,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Target data validation passed (%1 data points)")
|
|
|
|
tr("Target data validation passed (%1 data points)")
|
|
|
|
.arg(m_targetLogLogData[0].size()));
|
|
|
|
.arg(m_targetLogLogData[0].size()));
|
|
|
|
|
|
|
|
|
|
|
|
@ -1403,21 +1439,29 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
m_consecutiveFailures = 0;
|
|
|
|
m_consecutiveFailures = 0;
|
|
|
|
m_initialValues = savedInitialValues;
|
|
|
|
m_initialValues = savedInitialValues;
|
|
|
|
initializeTraceFile();
|
|
|
|
initializeTraceFile();
|
|
|
|
|
|
|
|
// 开头只展示一次初始参数和总误差,不展示形状误差。
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("========== LM fitting started =========="));
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Target well: %1").arg(m_targetWellName));
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Target error: %1").arg(lmLogError(m_targetError)));
|
|
|
|
|
|
|
|
|
|
|
|
// 先用真实求解器评价用户当前模型,供最终精英保护使用。
|
|
|
|
// 先用真实求解器评价用户当前模型,供最终精英保护使用。
|
|
|
|
if(!savedInitialValues.isEmpty()) {
|
|
|
|
if(!savedInitialValues.isEmpty()) {
|
|
|
|
m_userInitialSolution = savedInitialValues;
|
|
|
|
m_userInitialSolution = savedInitialValues;
|
|
|
|
emit logMessageGenerated(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
|
|
|
|
DEBUG_OUT(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
|
|
|
|
|
|
|
|
|
|
|
|
QString paramStr = tr("Initial parameters: ");
|
|
|
|
const char* names[] = { QT_TR_NOOP("Permeability"), QT_TR_NOOP("Skin"),
|
|
|
|
|
|
|
|
QT_TR_NOOP("Wellbore storage"), QT_TR_NOOP("Porosity"), "",
|
|
|
|
|
|
|
|
QT_TR_NOOP("Fracture conductivity"), QT_TR_NOOP("Fracture half length") };
|
|
|
|
|
|
|
|
const char* units[] = { "mD", "", "m^3/MPa", "", "", "mD.m", "m" };
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("[Initial parameters]"));
|
|
|
|
for(int i = 0; i < m_userInitialSolution.size(); ++i) {
|
|
|
|
for(int i = 0; i < m_userInitialSolution.size(); ++i) {
|
|
|
|
paramStr += QString("[%1]=%2 ")
|
|
|
|
const int index = m_enabledParamIndices[i];
|
|
|
|
.arg(i).arg(m_userInitialSolution[i], 0, 'f', 6);
|
|
|
|
emit logMessageGenerated(QString("%1: %2 %3").arg(tr(names[index]))
|
|
|
|
|
|
|
|
.arg(m_userInitialSolution[i], 0, 'g', 10).arg(tr(units[index])));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit logMessageGenerated(paramStr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Starting initial solution evaluation..."));
|
|
|
|
tr("Starting initial solution evaluation..."));
|
|
|
|
QTime initialEvalTimer;
|
|
|
|
QTime initialEvalTimer;
|
|
|
|
initialEvalTimer.start();
|
|
|
|
initialEvalTimer.start();
|
|
|
|
@ -1434,16 +1478,15 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
m_globalBestLogLogData = m_userInitialLogLogData;
|
|
|
|
m_globalBestLogLogData = m_userInitialLogLogData;
|
|
|
|
m_userInitialObjectiveBreakdown = m_lastObjectiveBreakdown;
|
|
|
|
m_userInitialObjectiveBreakdown = m_lastObjectiveBreakdown;
|
|
|
|
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
|
|
|
|
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
|
|
|
|
emit logMessageGenerated(tr("Initial solution evaluation successful"));
|
|
|
|
DEBUG_OUT(tr("Initial solution evaluation successful"));
|
|
|
|
emit logMessageGenerated(
|
|
|
|
emit logMessageGenerated(tr("Initial total error: %1").arg(lmLogError(m_userInitialFitness)));
|
|
|
|
tr("Initial Error: %1").arg(m_userInitialFitness, 0, 'e', 4));
|
|
|
|
|
|
|
|
emit bestCurveUpdated(m_targetLogLogData,
|
|
|
|
emit bestCurveUpdated(m_targetLogLogData,
|
|
|
|
m_globalBestLogLogData,
|
|
|
|
m_globalBestLogLogData,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
m_globalBestFitness);
|
|
|
|
m_globalBestFitness);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
m_hasValidUserSolution = false;
|
|
|
|
m_hasValidUserSolution = false;
|
|
|
|
emit logMessageGenerated(tr("Initial solution evaluation failed"));
|
|
|
|
if(!m_shouldStop) emit logMessageGenerated(tr("Initial solution failed; trying the parameter-range midpoint."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
writeTraceRow(-1,
|
|
|
|
writeTraceRow(-1,
|
|
|
|
@ -1458,7 +1501,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
? &m_userInitialObjectiveBreakdown : nullptr);
|
|
|
|
? &m_userInitialObjectiveBreakdown : nullptr);
|
|
|
|
} catch(...) {
|
|
|
|
} catch(...) {
|
|
|
|
m_hasValidUserSolution = false;
|
|
|
|
m_hasValidUserSolution = false;
|
|
|
|
emit logMessageGenerated(tr("Exception during initial solution evaluation"));
|
|
|
|
if(!m_shouldStop) emit logMessageGenerated(tr("Initial solution failed; trying the parameter-range midpoint."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_initialValues = savedInitialValues;
|
|
|
|
m_initialValues = savedInitialValues;
|
|
|
|
@ -1491,6 +1534,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
cleanupTemporaryDirectory();
|
|
|
|
cleanupTemporaryDirectory();
|
|
|
|
m_isFinalizing = false;
|
|
|
|
m_isFinalizing = false;
|
|
|
|
m_isRunning = false;
|
|
|
|
m_isRunning = false;
|
|
|
|
|
|
|
|
emitRunSummary(false, LM_OPTIMIZATION_FAILED);
|
|
|
|
emit fittingFinished(false, m_lastError);
|
|
|
|
emit fittingFinished(false, m_lastError);
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
} catch(...) {
|
|
|
|
} catch(...) {
|
|
|
|
@ -1500,6 +1544,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
cleanupTemporaryDirectory();
|
|
|
|
cleanupTemporaryDirectory();
|
|
|
|
m_isFinalizing = false;
|
|
|
|
m_isFinalizing = false;
|
|
|
|
m_isRunning = false;
|
|
|
|
m_isRunning = false;
|
|
|
|
|
|
|
|
emitRunSummary(false, LM_OPTIMIZATION_FAILED);
|
|
|
|
emit fittingFinished(false, m_lastError);
|
|
|
|
emit fittingFinished(false, m_lastError);
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1513,7 +1558,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
// Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。
|
|
|
|
// Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。
|
|
|
|
m_isFinalizing = true;
|
|
|
|
m_isFinalizing = true;
|
|
|
|
emit finalizingStarted();
|
|
|
|
emit finalizingStarted();
|
|
|
|
emit logMessageGenerated(tr("Applying optimized parameters to model..."));
|
|
|
|
DEBUG_OUT(tr("Applying optimized parameters to model..."));
|
|
|
|
applyParametersToDataManager(m_globalBestPosition);
|
|
|
|
applyParametersToDataManager(m_globalBestPosition);
|
|
|
|
|
|
|
|
|
|
|
|
// 裂缝参数会改变网格输入;标记失效后,最终求解任务会基于新快照重建网格。
|
|
|
|
// 裂缝参数会改变网格输入;标记失效后,最终求解任务会基于新快照重建网格。
|
|
|
|
@ -1529,28 +1574,28 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
dataManager->invalidatePebiGrid();
|
|
|
|
dataManager->invalidatePebiGrid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Running final full-field calculation with optimized parameters..."));
|
|
|
|
tr("Running final full-field calculation with optimized parameters..."));
|
|
|
|
finalFullSolverExecuted = true;
|
|
|
|
finalFullSolverExecuted = true;
|
|
|
|
finalFullSolverSucceeded = runFinalFullSolver();
|
|
|
|
finalFullSolverSucceeded = runFinalFullSolver();
|
|
|
|
|
|
|
|
|
|
|
|
if(finalFullSolverSucceeded) {
|
|
|
|
if(finalFullSolverSucceeded) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Final full-field calculation completed successfully"));
|
|
|
|
tr("Final full-field calculation completed successfully"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
m_lastError =
|
|
|
|
m_lastError =
|
|
|
|
tr("Optimized parameters were found, but the final full-field calculation failed");
|
|
|
|
tr("Optimized parameters were found, but the final full-field calculation failed");
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("ERROR: Final full-field calculation failed"));
|
|
|
|
tr("ERROR: Final full-field calculation failed"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
saveOptimizationResult();
|
|
|
|
saveOptimizationResult();
|
|
|
|
emit logMessageGenerated(tr("=== Optimization Results ==="));
|
|
|
|
DEBUG_OUT(tr("=== Optimization Results ==="));
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4));
|
|
|
|
tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4));
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Total iterations: %1").arg(m_currentIteration + 1));
|
|
|
|
tr("Total iterations: %1").arg(m_currentIteration + 1));
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Total evaluations: %1 (successful: %2)")
|
|
|
|
tr("Total evaluations: %1 (successful: %2)")
|
|
|
|
.arg(m_totalEvaluations).arg(m_successfulEvaluations));
|
|
|
|
.arg(m_totalEvaluations).arg(m_successfulEvaluations));
|
|
|
|
|
|
|
|
|
|
|
|
@ -1559,24 +1604,24 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
finalParams += QString("[%1]=%2 ")
|
|
|
|
finalParams += QString("[%1]=%2 ")
|
|
|
|
.arg(i).arg(m_globalBestPosition[i], 0, 'f', 6);
|
|
|
|
.arg(i).arg(m_globalBestPosition[i], 0, 'f', 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emit logMessageGenerated(finalParams);
|
|
|
|
DEBUG_OUT(finalParams);
|
|
|
|
|
|
|
|
|
|
|
|
if(finalFullSolverExecuted && finalFullSolverSucceeded) {
|
|
|
|
if(finalFullSolverExecuted && finalFullSolverSucceeded) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Parameters and full-field results applied successfully to data manager"));
|
|
|
|
tr("Parameters and full-field results applied successfully to data manager"));
|
|
|
|
} else if(!finalFullSolverExecuted) {
|
|
|
|
} else if(!finalFullSolverExecuted) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Optimized parameters applied to data manager"));
|
|
|
|
tr("Optimized parameters applied to data manager"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch(const std::exception& e) {
|
|
|
|
} catch(const std::exception& e) {
|
|
|
|
finalFullSolverSucceeded = false;
|
|
|
|
finalFullSolverSucceeded = false;
|
|
|
|
m_lastError = tr("Failed to apply final parameters: %1").arg(e.what());
|
|
|
|
m_lastError = tr("Failed to apply final parameters: %1").arg(e.what());
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
|
|
|
|
tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
|
|
|
|
} catch(...) {
|
|
|
|
} catch(...) {
|
|
|
|
finalFullSolverSucceeded = false;
|
|
|
|
finalFullSolverSucceeded = false;
|
|
|
|
m_lastError = tr("Failed to apply final parameters due to unknown error");
|
|
|
|
m_lastError = tr("Failed to apply final parameters due to unknown error");
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("ERROR: Unknown error applying final parameters"));
|
|
|
|
tr("ERROR: Unknown error applying final parameters"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1591,45 +1636,45 @@ bool nmCalculationAutoFitLM::startAutoFitting()
|
|
|
|
message = QString(tr("Target achieved. Best error: %1, Iterations: %2"))
|
|
|
|
message = QString(tr("Target achieved. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING SUCCESSFUL ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING SUCCESSFUL ==="));
|
|
|
|
} else if(finalReason == LM_TRUE_CONVERGENCE) {
|
|
|
|
} else if(finalReason == LM_TRUE_CONVERGENCE) {
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
message = QString(
|
|
|
|
message = QString(
|
|
|
|
tr("Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2"))
|
|
|
|
tr("Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING CONVERGED ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING CONVERGED ==="));
|
|
|
|
} else if(finalReason == LM_LOCAL_OPTIMUM) {
|
|
|
|
} else if(finalReason == LM_LOCAL_OPTIMUM) {
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
message = QString(
|
|
|
|
message = QString(
|
|
|
|
tr("Automatic fitting reached a local optimum. Best error: %1, Iterations: %2"))
|
|
|
|
tr("Automatic fitting reached a local optimum. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - LOCAL OPTIMUM ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - LOCAL OPTIMUM ==="));
|
|
|
|
} else if(finalReason == LM_MAX_ITERATIONS) {
|
|
|
|
} else if(finalReason == LM_MAX_ITERATIONS) {
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
message = QString(tr("Total-stage budget reached. Best error: %1, Cumulative iterations: %2"))
|
|
|
|
message = QString(tr("Total-stage budget reached. Best error: %1, Cumulative iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - MAX ITERATIONS ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - MAX ITERATIONS ==="));
|
|
|
|
} else if(finalReason == LM_USER_STOPPED) {
|
|
|
|
} else if(finalReason == LM_USER_STOPPED) {
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
message = QString(tr("Stopped by user. Best error: %1, Iterations: %2"))
|
|
|
|
message = QString(tr("Stopped by user. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING STOPPED BY USER ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING STOPPED BY USER ==="));
|
|
|
|
} else if(finalReason == LM_CONSECUTIVE_FAILURES) {
|
|
|
|
} else if(finalReason == LM_CONSECUTIVE_FAILURES) {
|
|
|
|
message = QString(
|
|
|
|
message = QString(
|
|
|
|
tr("Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2"))
|
|
|
|
tr("Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING FAILED ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING FAILED ==="));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
message = QString(
|
|
|
|
message = QString(
|
|
|
|
tr("Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2"))
|
|
|
|
tr("Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2"))
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_globalBestFitness, 0, 'e', 4)
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
.arg(m_currentIteration + 1);
|
|
|
|
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - UNKNOWN END ==="));
|
|
|
|
DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - UNKNOWN END ==="));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!finalFullSolverSucceeded) {
|
|
|
|
if(!finalFullSolverSucceeded) {
|
|
|
|
@ -1918,8 +1963,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
|
|
|
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
emit progressUpdated(-1, current.fitness);
|
|
|
|
emit progressUpdated(-1, current.fitness);
|
|
|
|
emit logMessageGenerated(tr("=== Starting LM Main Loop ==="));
|
|
|
|
DEBUG_OUT(tr("=== Starting LM Main Loop ==="));
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("LM starting point error: %1; evaluation budget per LM phase: %2")
|
|
|
|
tr("LM starting point error: %1; evaluation budget per LM phase: %2")
|
|
|
|
.arg(current.fitness, 0, 'e', 4)
|
|
|
|
.arg(current.fitness, 0, 'e', 4)
|
|
|
|
.arg(phaseEvaluationBudget));
|
|
|
|
.arg(phaseEvaluationBudget));
|
|
|
|
@ -1931,8 +1976,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
int heightStagnation = 0;
|
|
|
|
int heightStagnation = 0;
|
|
|
|
double heightScale = 1.0;
|
|
|
|
double heightScale = 1.0;
|
|
|
|
double heightSlope = -1.0;
|
|
|
|
double heightSlope = -1.0;
|
|
|
|
|
|
|
|
int heightTrials = 0;
|
|
|
|
if(permeabilityColumn >= 0) {
|
|
|
|
if(permeabilityColumn >= 0) {
|
|
|
|
emit logMessageGenerated(tr("LM stage 1: align curve height using permeability only; no fixed evaluation limit, effective improvement threshold 10%."));
|
|
|
|
emit logMessageGenerated(tr("[Stage 1: Height alignment]\nAdjust permeability only; accept by height error."));
|
|
|
|
// 不限制试算次数,由高度对齐、改善停滞及可行步长决定何时结束。
|
|
|
|
// 不限制试算次数,由高度对齐、改善停滞及可行步长决定何时结束。
|
|
|
|
while(processPauseAndStop()) {
|
|
|
|
while(processPauseAndStop()) {
|
|
|
|
const double bias = current.breakdown.verticalCommonBias;
|
|
|
|
const double bias = current.breakdown.verticalCommonBias;
|
|
|
|
@ -1941,6 +1987,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
if(k <= 0.0 || m_parameterLower[0] <= 0.0 || m_parameterUpper[0] <= m_parameterLower[0]) break;
|
|
|
|
if(k <= 0.0 || m_parameterLower[0] <= 0.0 || m_parameterUpper[0] <= m_parameterLower[0]) break;
|
|
|
|
const double logRange = qLn(m_parameterUpper[0]) - qLn(m_parameterLower[0]);
|
|
|
|
const double logRange = qLn(m_parameterUpper[0]) - qLn(m_parameterLower[0]);
|
|
|
|
const double change = qBound(-0.30 * logRange, -bias / heightSlope * heightScale, 0.30 * logRange);
|
|
|
|
const double change = qBound(-0.30 * logRange, -bias / heightSlope * heightScale, 0.30 * logRange);
|
|
|
|
|
|
|
|
const AutoFitObjectiveBreakdownLM previous = current.breakdown;
|
|
|
|
|
|
|
|
++heightTrials;
|
|
|
|
TrustRegionEvaluation candidate;
|
|
|
|
TrustRegionEvaluation candidate;
|
|
|
|
candidate.parameters = current.parameters;
|
|
|
|
candidate.parameters = current.parameters;
|
|
|
|
candidate.parameters[permeabilityColumn] = qBound(m_parameterLower[0], k * qExp(change), m_parameterUpper[0]);
|
|
|
|
candidate.parameters[permeabilityColumn] = qBound(m_parameterLower[0], k * qExp(change), m_parameterUpper[0]);
|
|
|
|
@ -1970,10 +2018,16 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
heightBaseline = current.breakdown.verticalLoss;
|
|
|
|
heightBaseline = current.breakdown.verticalLoss;
|
|
|
|
heightStagnation = 0;
|
|
|
|
heightStagnation = 0;
|
|
|
|
} else ++heightStagnation;
|
|
|
|
} else ++heightStagnation;
|
|
|
|
emit logMessageGenerated(tr("Permeability alignment: k=%1, height error=%2, shape error=%3, result=%4")
|
|
|
|
if(candidate.valid) {
|
|
|
|
.arg(candidate.parameters[permeabilityColumn], 0, 'g', 6)
|
|
|
|
emit logMessageGenerated(tr("Trial %1 | %2 | Height error: %3 -> %4")
|
|
|
|
.arg(candidate.breakdown.verticalLoss, 0, 'e', 4).arg(candidate.breakdown.shapeLoss, 0, 'e', 4)
|
|
|
|
.arg(heightTrials).arg(accepted ? tr("accepted") : tr("rejected"))
|
|
|
|
.arg(accepted ? tr("accepted") : tr("rejected")));
|
|
|
|
.arg(lmLogError(previous.verticalLoss)).arg(lmLogError(candidate.breakdown.verticalLoss)));
|
|
|
|
|
|
|
|
if(!accepted) emit logMessageGenerated(candidate.breakdown.verticalReliable
|
|
|
|
|
|
|
|
? tr("Height error did not decrease; keep the last accepted result and reduce the step.")
|
|
|
|
|
|
|
|
: tr("Pressure and derivative height directions conflict; keep the last accepted result and reduce the step."));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Trial %1 | Solver failed; keep the last accepted result and reduce the step.").arg(heightTrials));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QString heightReason = tr("no feasible permeability step or step too small");
|
|
|
|
QString heightReason = tr("no feasible permeability step or step too small");
|
|
|
|
if(m_shouldStop) heightReason = tr("stopped by user");
|
|
|
|
if(m_shouldStop) heightReason = tr("stopped by user");
|
|
|
|
@ -1982,9 +2036,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
else if(heightStagnation >= 2) heightReason = tr("2 consecutive steps without effective height improvement");
|
|
|
|
else if(heightStagnation >= 2) heightReason = tr("2 consecutive steps without effective height improvement");
|
|
|
|
else if(current.parameters[permeabilityColumn] <= m_parameterLower[0] ||
|
|
|
|
else if(current.parameters[permeabilityColumn] <= m_parameterLower[0] ||
|
|
|
|
current.parameters[permeabilityColumn] >= m_parameterUpper[0]) heightReason = tr("permeability reached its bound");
|
|
|
|
current.parameters[permeabilityColumn] >= m_parameterUpper[0]) heightReason = tr("permeability reached its bound");
|
|
|
|
emit logMessageGenerated(tr("Permeability alignment ended: %1").arg(heightReason));
|
|
|
|
emit logMessageGenerated(tr("Height alignment ended: %1").arg(heightReason));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(permeabilityColumn < 0) emit logMessageGenerated(tr("Height alignment skipped: permeability is not selected."));
|
|
|
|
if(m_shouldStop) return LM_USER_STOPPED;
|
|
|
|
if(m_shouldStop) return LM_USER_STOPPED;
|
|
|
|
|
|
|
|
|
|
|
|
// 原逐参数第二阶段已移除;同一个联合 LM 先匹配整体形状,再匹配总误差。
|
|
|
|
// 原逐参数第二阶段已移除;同一个联合 LM 先匹配整体形状,再匹配总误差。
|
|
|
|
@ -2002,7 +2057,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
double effectiveImprovementBaseline = stageError(current);
|
|
|
|
double effectiveImprovementBaseline = stageError(current);
|
|
|
|
emit logMessageGenerated(tr("LM sampling: %1 points (%2 intervals per log-time decade).")
|
|
|
|
DEBUG_OUT(tr("LM sampling: %1 points (%2 intervals per log-time decade).")
|
|
|
|
.arg(current.breakdown.residualVector.size() / 2).arg(kAutoFitIntervalsPerDecade));
|
|
|
|
.arg(current.breakdown.residualVector.size() / 2).arg(kAutoFitIntervalsPerDecade));
|
|
|
|
|
|
|
|
|
|
|
|
auto registerEffectiveImprovement = [&](double fitness) -> bool {
|
|
|
|
auto registerEffectiveImprovement = [&](double fitness) -> bool {
|
|
|
|
@ -2048,7 +2103,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("LM effective improvement threshold: max(%1, %2% of baseline error); "
|
|
|
|
tr("LM effective improvement threshold: max(%1, %2% of baseline error); "
|
|
|
|
"%3 consecutive ineffective steps trigger convergence confirmation")
|
|
|
|
"%3 consecutive ineffective steps trigger convergence confirmation")
|
|
|
|
.arg(effectiveAbsoluteImprovement, 0, 'e', 2)
|
|
|
|
.arg(effectiveAbsoluteImprovement, 0, 'e', 2)
|
|
|
|
@ -2212,9 +2267,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
shapeStage ? "shape_accepted_cached_probe" : "total_accepted_cached_probe",
|
|
|
|
shapeStage ? "shape_accepted_cached_probe" : "total_accepted_cached_probe",
|
|
|
|
¤t.breakdown);
|
|
|
|
¤t.breakdown);
|
|
|
|
emit logMessageGenerated(
|
|
|
|
emitTrialLog(tr("Sensitivity trial"), base.breakdown, current.breakdown, true, true, shapeStage);
|
|
|
|
tr("Sensitivity probe accepted: current objective error=%1")
|
|
|
|
|
|
|
|
.arg(stageError(current), 0, 'e', 4));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2258,9 +2311,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
: QT_TR_NOOP("full sensitivity at total-stage entry");
|
|
|
|
: QT_TR_NOOP("full sensitivity at total-stage entry");
|
|
|
|
emit progressUpdated(0, current.fitness);
|
|
|
|
emit progressUpdated(0, current.fitness);
|
|
|
|
emit logMessageGenerated(shapeStage
|
|
|
|
emit logMessageGenerated(shapeStage
|
|
|
|
? tr("Joint LM shape phase: adjust all selected parameters by full shape error.")
|
|
|
|
? tr("[Stage 2: Joint LM - Shape]\nAdjust selected parameters; accept by shape error.")
|
|
|
|
: tr("Joint LM total phase: adjust all selected parameters by total error."));
|
|
|
|
: tr("[Stage 2: Joint LM - Total error]\nAdjust selected parameters; accept by total error."));
|
|
|
|
emit logMessageGenerated(tr("LM phase budget: %1 iterations, %2 evaluations.")
|
|
|
|
DEBUG_OUT(tr("LM phase budget: %1 iterations, %2 evaluations.")
|
|
|
|
.arg(m_maxIterations).arg(phaseEvaluationBudget));
|
|
|
|
.arg(m_maxIterations).arg(phaseEvaluationBudget));
|
|
|
|
writeTraceRow(completedIterations, -1, "stage_switch", current.parameters, current.fitness,
|
|
|
|
writeTraceRow(completedIterations, -1, "stage_switch", current.parameters, current.fitness,
|
|
|
|
true, 0, shapeStage ? "height_to_shape_lm" : "shape_to_total", ¤t.breakdown);
|
|
|
|
true, 0, shapeStage ? "height_to_shape_lm" : "shape_to_total", ¤t.breakdown);
|
|
|
|
@ -2276,7 +2329,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(rebuildRequested) {
|
|
|
|
if(rebuildRequested) {
|
|
|
|
emit logMessageGenerated(tr("Rebuilding sensitivity model: %1").arg(tr(rebuildReason)));
|
|
|
|
emit logMessageGenerated(tr("Calculating sensitivity..."));
|
|
|
|
writeTraceRow(m_currentIteration, -1, "sensitivity_rebuild", current.parameters,
|
|
|
|
writeTraceRow(m_currentIteration, -1, "sensitivity_rebuild", current.parameters,
|
|
|
|
current.fitness, true, 0, rebuildReason, ¤t.breakdown);
|
|
|
|
current.fitness, true, 0, rebuildReason, ¤t.breakdown);
|
|
|
|
if(!rebuildSensitivity()) {
|
|
|
|
if(!rebuildSensitivity()) {
|
|
|
|
@ -2333,6 +2386,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
|
|
|
|
|
|
|
// 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。
|
|
|
|
// 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。
|
|
|
|
if(selectedColumns.isEmpty()) {
|
|
|
|
if(selectedColumns.isEmpty()) {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Iteration %1 | No feasible descent step; keep the last accepted result.").arg(phaseIterations));
|
|
|
|
if(trustRadius <= minimumTrustRadius * 1.01 &&
|
|
|
|
if(trustRadius <= minimumTrustRadius * 1.01 &&
|
|
|
|
modelRebuiltAtMinimumRadius) {
|
|
|
|
modelRebuiltAtMinimumRadius) {
|
|
|
|
stopReason = LM_LOCAL_OPTIMUM;
|
|
|
|
stopReason = LM_LOCAL_OPTIMUM;
|
|
|
|
@ -2379,6 +2433,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!candidate.valid) {
|
|
|
|
if(!candidate.valid) {
|
|
|
|
|
|
|
|
emitTrialLog(tr("Iteration %1").arg(phaseIterations), current.breakdown, candidate.breakdown,
|
|
|
|
|
|
|
|
false, false, shapeStage);
|
|
|
|
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
|
|
|
|
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
|
|
|
|
// 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。
|
|
|
|
// 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。
|
|
|
|
++consecutiveSolverFailures;
|
|
|
|
++consecutiveSolverFailures;
|
|
|
|
@ -2493,14 +2549,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
: "rejected_" + selectionName,
|
|
|
|
: "rejected_" + selectionName,
|
|
|
|
&candidate.breakdown);
|
|
|
|
&candidate.breakdown);
|
|
|
|
|
|
|
|
|
|
|
|
QString componentDisplayName = shapeStage ? tr("shape deviation") : tr("total error");
|
|
|
|
emitTrialLog(tr("Iteration %1").arg(phaseIterations), oldBreakdown, candidate.breakdown,
|
|
|
|
emit logMessageGenerated(
|
|
|
|
true, accepted, shapeStage);
|
|
|
|
tr("Iteration %1: focus=%2, parameters=%3, error=%4, result=%5")
|
|
|
|
|
|
|
|
.arg(iteration + 1)
|
|
|
|
|
|
|
|
.arg(componentDisplayName)
|
|
|
|
|
|
|
|
.arg(selectedColumns.size())
|
|
|
|
|
|
|
|
.arg(stageError(candidate), 0, 'e', 4)
|
|
|
|
|
|
|
|
.arg(accepted ? tr("accepted") : tr("rejected")));
|
|
|
|
|
|
|
|
emit progressUpdated(phaseIterations, m_globalBestFitness);
|
|
|
|
emit progressUpdated(phaseIterations, m_globalBestFitness);
|
|
|
|
|
|
|
|
|
|
|
|
const bool effectiveImprovement = registerEffectiveImprovement(stageError(current));
|
|
|
|
const bool effectiveImprovement = registerEffectiveImprovement(stageError(current));
|
|
|
|
@ -2528,9 +2578,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
// 形状达标、确认停滞或用完额度后,保留当前参数进入总误差段。
|
|
|
|
// 形状达标、确认停滞或用完额度后,保留当前参数进入总误差段。
|
|
|
|
if(shapeStage) {
|
|
|
|
if(shapeStage) {
|
|
|
|
emit logMessageGenerated(tr("Shape LM phase ended: %1").arg(getStopReasonDescription(stopReason)));
|
|
|
|
emit logMessageGenerated(tr("Shape phase ended: %1").arg(getStopReasonDescription(stopReason)));
|
|
|
|
writeTraceRow(m_currentIteration, -1, "lm_phase_end", current.parameters, current.fitness,
|
|
|
|
writeTraceRow(m_currentIteration, -1, "lm_phase_end", current.parameters, current.fitness,
|
|
|
|
true, 0, QString("shape_stop_reason_%1").arg(static_cast<int>(stopReason)), ¤t.breakdown);
|
|
|
|
true, 0, QString("shape_stop_reason_%1").arg(static_cast<int>(stopReason)), ¤t.breakdown);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Total-error phase ended: %1").arg(getStopReasonDescription(stopReason)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -2538,7 +2590,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
m_currentIteration = completedIterations - 1;
|
|
|
|
m_currentIteration = completedIterations - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
emit logMessageGenerated(tr("Adaptive fitting counts: %1 shape LM iterations, %2 total LM iterations, %3 total evaluations.")
|
|
|
|
DEBUG_OUT(tr("Adaptive fitting counts: %1 shape LM iterations, %2 total LM iterations, %3 total evaluations.")
|
|
|
|
.arg(shapeIterations).arg(totalIterations).arg(m_totalEvaluations));
|
|
|
|
.arg(shapeIterations).arg(totalIterations).arg(m_totalEvaluations));
|
|
|
|
|
|
|
|
|
|
|
|
if(m_shouldStop) {
|
|
|
|
if(m_shouldStop) {
|
|
|
|
@ -3001,17 +3053,17 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
|
|
|
|
// 最终精英保护只阻止无效结果或真正变差的结果。任何真实误差下降都应保留,
|
|
|
|
// 最终精英保护只阻止无效结果或真正变差的结果。任何真实误差下降都应保留,
|
|
|
|
// 不能再用固定百分比门槛把已经找到的更优解恢复成初始值。
|
|
|
|
// 不能再用固定百分比门槛把已经找到的更优解恢复成初始值。
|
|
|
|
if(!m_hasValidUserSolution) {
|
|
|
|
if(!m_hasValidUserSolution) {
|
|
|
|
emit logMessageGenerated(tr("No initial solution for elite protection"));
|
|
|
|
DEBUG_OUT(tr("No initial solution for elite protection"));
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("=== Final Result Validation (Elite Protection) ==="));
|
|
|
|
DEBUG_OUT(tr("=== Final Result Validation (Elite Protection) ==="));
|
|
|
|
|
|
|
|
|
|
|
|
// 使用已有的评估结果
|
|
|
|
// 使用已有的评估结果
|
|
|
|
double finalFitness = m_globalBestFitness;
|
|
|
|
double finalFitness = m_globalBestFitness;
|
|
|
|
double initialFitness = m_userInitialFitness;
|
|
|
|
double initialFitness = m_userInitialFitness;
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Comparing results: Initial=%1, Final=%2")
|
|
|
|
DEBUG_OUT(tr("Comparing results: Initial=%1, Final=%2")
|
|
|
|
.arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4));
|
|
|
|
.arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4));
|
|
|
|
|
|
|
|
|
|
|
|
bool finalValid = isFiniteNumber(finalFitness) &&
|
|
|
|
bool finalValid = isFiniteNumber(finalFitness) &&
|
|
|
|
@ -3024,15 +3076,15 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
|
|
|
|
double improvement = initialFitness - finalFitness;
|
|
|
|
double improvement = initialFitness - finalFitness;
|
|
|
|
double relativeImprovement =
|
|
|
|
double relativeImprovement =
|
|
|
|
improvement / qMax(1.0e-10, qAbs(initialFitness));
|
|
|
|
improvement / qMax(1.0e-10, qAbs(initialFitness));
|
|
|
|
emit logMessageGenerated(tr("Improvement: %1 (%2%)")
|
|
|
|
DEBUG_OUT(tr("Improvement: %1 (%2%)")
|
|
|
|
.arg(improvement, 0, 'e', 4)
|
|
|
|
.arg(improvement, 0, 'e', 4)
|
|
|
|
.arg(relativeImprovement * 100, 0, 'f', 2));
|
|
|
|
.arg(relativeImprovement * 100, 0, 'f', 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!finalValid || finalFitness > initialFitness) {
|
|
|
|
if(!finalValid || finalFitness > initialFitness) {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Elite protection triggered: final result is invalid or worse than initial"));
|
|
|
|
tr("Elite protection triggered: final result is invalid or worse than initial"));
|
|
|
|
emit logMessageGenerated(tr("Restoring initial solution as final result"));
|
|
|
|
emit logMessageGenerated(tr("Final total error did not improve; restoring the initial result."));
|
|
|
|
|
|
|
|
|
|
|
|
m_globalBestFitness = initialFitness;
|
|
|
|
m_globalBestFitness = initialFitness;
|
|
|
|
m_globalBestPosition = m_userInitialSolution;
|
|
|
|
m_globalBestPosition = m_userInitialSolution;
|
|
|
|
@ -3040,9 +3092,9 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
|
|
|
|
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
|
|
|
|
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
|
|
|
|
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
|
|
|
|
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
|
|
|
|
|
|
|
|
|
|
|
|
emit logMessageGenerated(tr("Initial solution restored successfully"));
|
|
|
|
DEBUG_OUT(tr("Initial solution restored successfully"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
emit logMessageGenerated(
|
|
|
|
DEBUG_OUT(
|
|
|
|
tr("Final result validated - solution is not worse than initial"));
|
|
|
|
tr("Final result validated - solution is not worse than initial"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -4129,6 +4181,7 @@ QVector<QVector<double>> nmCalculationAutoFitLM::runSolverDll()
|
|
|
|
DEBUG_OUT("Starting DLL solver execution...");
|
|
|
|
DEBUG_OUT("Starting DLL solver execution...");
|
|
|
|
|
|
|
|
|
|
|
|
// 异步执行 DLL 任务。循环等待期间持续 processEvents,保证界面不会完全卡死。
|
|
|
|
// 异步执行 DLL 任务。循环等待期间持续 processEvents,保证界面不会完全卡死。
|
|
|
|
|
|
|
|
connect(dllTask, SIGNAL(solverInvoked()), this, SLOT(recordSolverCall()), Qt::DirectConnection);
|
|
|
|
dllTask->start();
|
|
|
|
dllTask->start();
|
|
|
|
|
|
|
|
|
|
|
|
// 等待完成。最大等待 1 小时;用户停止使用任务已有的协作取消接口。
|
|
|
|
// 等待完成。最大等待 1 小时;用户停止使用任务已有的协作取消接口。
|
|
|
|
@ -4296,6 +4349,7 @@ bool nmCalculationAutoFitLM::runFinalFullSolver()
|
|
|
|
|
|
|
|
|
|
|
|
++m_evaluationInProgress;
|
|
|
|
++m_evaluationInProgress;
|
|
|
|
nmCalculationDllPebiSolverTask dllTask(m_tempDirectory, dataManager);
|
|
|
|
nmCalculationDllPebiSolverTask dllTask(m_tempDirectory, dataManager);
|
|
|
|
|
|
|
|
connect(&dllTask, SIGNAL(solverInvoked()), this, SLOT(recordSolverCall()), Qt::DirectConnection);
|
|
|
|
dllTask.start();
|
|
|
|
dllTask.start();
|
|
|
|
|
|
|
|
|
|
|
|
const int maxWait = 3600000;
|
|
|
|
const int maxWait = 3600000;
|
|
|
|
@ -4347,7 +4401,7 @@ QString nmCalculationAutoFitLM::getStopReasonDescription(StopReasonLM reason) co
|
|
|
|
return tr("Local optimum detected");
|
|
|
|
return tr("Local optimum detected");
|
|
|
|
|
|
|
|
|
|
|
|
case LM_MAX_ITERATIONS:
|
|
|
|
case LM_MAX_ITERATIONS:
|
|
|
|
return tr("Total-stage iteration or evaluation budget reached");
|
|
|
|
return tr("Iteration or evaluation budget reached");
|
|
|
|
|
|
|
|
|
|
|
|
case LM_USER_STOPPED:
|
|
|
|
case LM_USER_STOPPED:
|
|
|
|
return tr("Stopped by user request");
|
|
|
|
return tr("Stopped by user request");
|
|
|
|
|