|
|
|
|
@ -860,8 +860,7 @@ void nmCalculationAutoFitLM::writeTraceHeader()
|
|
|
|
|
<< "late_trend_loss"
|
|
|
|
|
<< "late_slope_bias"
|
|
|
|
|
<< "late_trend_reliable"
|
|
|
|
|
<< "registration_ambiguous"
|
|
|
|
|
<< "coverage";
|
|
|
|
|
<< "registration_ambiguous";
|
|
|
|
|
|
|
|
|
|
QTextStream out(&m_traceFile);
|
|
|
|
|
out << cols.join(",") << "\n";
|
|
|
|
|
@ -978,10 +977,9 @@ void nmCalculationAutoFitLM::writeTraceRow(
|
|
|
|
|
<< traceNumber(objectiveBreakdown->lateDerivativeTrendLoss)
|
|
|
|
|
<< traceNumber(objectiveBreakdown->lateDerivativeSlopeBias)
|
|
|
|
|
<< QString::number(objectiveBreakdown->lateDerivativeTrendReliable ? 1 : 0)
|
|
|
|
|
<< QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0)
|
|
|
|
|
<< traceNumber(objectiveBreakdown->coverage);
|
|
|
|
|
<< QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0);
|
|
|
|
|
} else {
|
|
|
|
|
for(int i = 0; i < 14; ++i) {
|
|
|
|
|
for(int i = 0; i < 13; ++i) {
|
|
|
|
|
cols << QString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -3247,12 +3245,11 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
const QVector<QVector<double> >& target,
|
|
|
|
|
const QVector<QVector<double> >& result) const
|
|
|
|
|
{
|
|
|
|
|
// 主目标只比较固定网格上的压力和导数残差;上下、左右和形状只负责诊断
|
|
|
|
|
// 误差来源和选择参数,避免同一残差在 total 中被重复计算。整个计算过程均
|
|
|
|
|
// 位于 log(time)-log(value) 坐标,因此得到的是相对尺度偏差而非原始压力量纲。
|
|
|
|
|
// 主目标在目标与模拟曲线的公共时间范围内比较压力和导数残差;上下、左右
|
|
|
|
|
// 和形状只负责诊断误差来源和选择参数,避免同一残差在 total 中被重复计算。
|
|
|
|
|
// 整个计算过程均位于 log(time)-log(value) 坐标。
|
|
|
|
|
const double invalidLoss = 1.0e10;
|
|
|
|
|
const double valueFloor = 1.0e-12;
|
|
|
|
|
const double minimumCoverage = 0.95;
|
|
|
|
|
const int numPoints = 80;
|
|
|
|
|
m_lastObjectiveBreakdown = AutoFitObjectiveBreakdownLM();
|
|
|
|
|
|
|
|
|
|
@ -3369,34 +3366,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
return isFiniteNumber(*value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 覆盖率通过后若只缺少首尾少量点,用模拟曲线自身的端点斜率作短距离
|
|
|
|
|
// 双对数外推。该外推只用于主损失的固定网格,不参与水平配准搜索。
|
|
|
|
|
auto extrapolateEndpointLogValue = [valueFloor](
|
|
|
|
|
const QVector<QPointF>& curve,
|
|
|
|
|
double x,
|
|
|
|
|
double* value) -> bool {
|
|
|
|
|
if(!value || curve.size() < 2 || x <= 0.0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int left = x < curve.first().x()
|
|
|
|
|
? 0
|
|
|
|
|
: curve.size() - 2;
|
|
|
|
|
int right = left + 1;
|
|
|
|
|
double leftLogX = qLn(curve[left].x());
|
|
|
|
|
double rightLogX = qLn(curve[right].x());
|
|
|
|
|
double denominator = rightLogX - leftLogX;
|
|
|
|
|
if(qAbs(denominator) <= 1.0e-12) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
double leftLogY =
|
|
|
|
|
qLn(qMax(qAbs(curve[left].y()), valueFloor));
|
|
|
|
|
double rightLogY =
|
|
|
|
|
qLn(qMax(qAbs(curve[right].y()), valueFloor));
|
|
|
|
|
double ratio = (qLn(x) - leftLogX) / denominator;
|
|
|
|
|
*value = leftLogY + ratio * (rightLogY - leftLogY);
|
|
|
|
|
return isFiniteNumber(*value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const double targetMinX = targetPressure.first().x();
|
|
|
|
|
const double targetMaxX = targetPressure.last().x();
|
|
|
|
|
const double resultMinX = resultPressure.first().x();
|
|
|
|
|
@ -3406,25 +3375,33 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
return invalidLoss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 与 PSO 保持一致:只在目标与模拟曲线的时间交集内比较,不再设置
|
|
|
|
|
// 覆盖率门槛,也不对交集之外的首尾数据做外推。
|
|
|
|
|
const double overlapMinX = qMax(targetMinX, resultMinX);
|
|
|
|
|
const double overlapMaxX = qMin(targetMaxX, resultMaxX);
|
|
|
|
|
if(overlapMinX >= overlapMaxX) {
|
|
|
|
|
return invalidLoss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<double> commonX(numPoints);
|
|
|
|
|
QVector<double> commonLogX(numPoints);
|
|
|
|
|
QVector<double> targetLogPressure(numPoints);
|
|
|
|
|
QVector<double> targetLogDerivative(numPoints);
|
|
|
|
|
const double targetLogMinX = qLn(targetMinX);
|
|
|
|
|
const double targetLogMaxX = qLn(targetMaxX);
|
|
|
|
|
const double comparisonLogMinX = qLn(overlapMinX);
|
|
|
|
|
const double comparisonLogMaxX = qLn(overlapMaxX);
|
|
|
|
|
|
|
|
|
|
// 固定使用目标曲线的完整 log-time 网格,候选之间不会因采样点不同而失去可比性。
|
|
|
|
|
// 在公共时间范围内生成固定维度的 log-time 网格,保持 LM 残差向量为 160 维。
|
|
|
|
|
for(int i = 0; i < numPoints; ++i) {
|
|
|
|
|
double logX = targetLogMinX +
|
|
|
|
|
double logX = comparisonLogMinX +
|
|
|
|
|
static_cast<double>(i) *
|
|
|
|
|
(targetLogMaxX - targetLogMinX) /
|
|
|
|
|
(comparisonLogMaxX - comparisonLogMinX) /
|
|
|
|
|
(numPoints - 1);
|
|
|
|
|
commonLogX[i] = logX;
|
|
|
|
|
// 首尾直接使用原始端点,避免 exp(log(t)) 的舍入误差越过严格插值边界。
|
|
|
|
|
if(i == 0) {
|
|
|
|
|
commonX[i] = targetMinX;
|
|
|
|
|
commonX[i] = overlapMinX;
|
|
|
|
|
} else if(i == numPoints - 1) {
|
|
|
|
|
commonX[i] = targetMaxX;
|
|
|
|
|
commonX[i] = overlapMaxX;
|
|
|
|
|
} else {
|
|
|
|
|
commonX[i] = qExp(logX);
|
|
|
|
|
}
|
|
|
|
|
@ -3443,16 +3420,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
numPoints, std::numeric_limits<double>::quiet_NaN());
|
|
|
|
|
QVector<double> derivativeResidual(
|
|
|
|
|
numPoints, std::numeric_limits<double>::quiet_NaN());
|
|
|
|
|
int firstSupported = -1;
|
|
|
|
|
int lastSupported = -1;
|
|
|
|
|
int supportedCount = 0;
|
|
|
|
|
|
|
|
|
|
// 残差定义为“模拟减目标”:正值表示模拟曲线偏高,负值表示偏低。
|
|
|
|
|
for(int i = 0; i < numPoints; ++i) {
|
|
|
|
|
if(commonX[i] < resultMinX || commonX[i] > resultMaxX) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double resultLogPressure = 0.0;
|
|
|
|
|
double resultLogDerivative = 0.0;
|
|
|
|
|
if(!interpolateLogValue(
|
|
|
|
|
@ -3469,59 +3439,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
resultLogPressure - targetLogPressure[i];
|
|
|
|
|
derivativeResidual[i] =
|
|
|
|
|
resultLogDerivative - targetLogDerivative[i];
|
|
|
|
|
++supportedCount;
|
|
|
|
|
if(firstSupported < 0) {
|
|
|
|
|
firstSupported = i;
|
|
|
|
|
}
|
|
|
|
|
lastSupported = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 覆盖率同时约束“有效点数量”和“连续时间跨度”。取两者较小值可避免
|
|
|
|
|
// 点数很多但只集中在局部时段的候选被误认为覆盖充分。
|
|
|
|
|
AutoFitObjectiveBreakdownLM breakdown;
|
|
|
|
|
breakdown.coverage = supportedCount > 0
|
|
|
|
|
? static_cast<double>(supportedCount) /
|
|
|
|
|
numPoints
|
|
|
|
|
: 0.0;
|
|
|
|
|
if(firstSupported >= 0 && lastSupported >= firstSupported) {
|
|
|
|
|
double targetSpan =
|
|
|
|
|
qMax(1.0e-12, targetLogMaxX - targetLogMinX);
|
|
|
|
|
double coveredSpan =
|
|
|
|
|
commonLogX[lastSupported] - commonLogX[firstSupported];
|
|
|
|
|
breakdown.coverage = qMin(
|
|
|
|
|
breakdown.coverage,
|
|
|
|
|
qMax(0.0, coveredSpan / targetSpan));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 覆盖率只判断候选是否有效,不再加入固定惩罚,避免所有误差被整体抬高。
|
|
|
|
|
if(breakdown.coverage < minimumCoverage) {
|
|
|
|
|
breakdown.total = invalidLoss;
|
|
|
|
|
m_lastObjectiveBreakdown = breakdown;
|
|
|
|
|
return invalidLoss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通过门槛后最多只缺少首尾少量目标点。按模拟曲线端点趋势补齐后,
|
|
|
|
|
// 每个候选仍在固定 80 点上计算均方根误差,不能靠少算难拟合端点获益。
|
|
|
|
|
for(int i = 0; i < numPoints; ++i) {
|
|
|
|
|
if(isFiniteNumber(pressureResidual[i]) &&
|
|
|
|
|
isFiniteNumber(derivativeResidual[i])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
double resultLogPressure = 0.0;
|
|
|
|
|
double resultLogDerivative = 0.0;
|
|
|
|
|
if(!extrapolateEndpointLogValue(
|
|
|
|
|
resultPressure, commonX[i],
|
|
|
|
|
&resultLogPressure) ||
|
|
|
|
|
!extrapolateEndpointLogValue(
|
|
|
|
|
resultDerivative, commonX[i],
|
|
|
|
|
&resultLogDerivative)) {
|
|
|
|
|
return invalidLoss;
|
|
|
|
|
}
|
|
|
|
|
pressureResidual[i] =
|
|
|
|
|
resultLogPressure - targetLogPressure[i];
|
|
|
|
|
derivativeResidual[i] =
|
|
|
|
|
resultLogDerivative - targetLogDerivative[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在指定中心附近计算普通均方根误差。
|
|
|
|
|
auto rmseAround = [](
|
|
|
|
|
@ -3648,7 +3568,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const double logGridStep =
|
|
|
|
|
(targetLogMaxX - targetLogMinX) /
|
|
|
|
|
(comparisonLogMaxX - comparisonLogMinX) /
|
|
|
|
|
(numPoints - 1);
|
|
|
|
|
const double resultLogMinX = qLn(resultMinX);
|
|
|
|
|
const double resultLogMaxX = qLn(resultMaxX);
|
|
|
|
|
@ -4091,7 +4011,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
m_lastObjectiveBreakdown = breakdown;
|
|
|
|
|
|
|
|
|
|
DEBUG_OUT(
|
|
|
|
|
QString("LogLog objective: pressure=%1, derivative=%2, vertical=%3, horizontal=%4, shape=%5, ambiguous=%6, shift=%7, coverage=%8, total=%9")
|
|
|
|
|
QString("LogLog objective: pressure=%1, derivative=%2, vertical=%3, horizontal=%4, shape=%5, ambiguous=%6, shift=%7, total=%8")
|
|
|
|
|
.arg(breakdown.pressureLoss, 0, 'e', 4)
|
|
|
|
|
.arg(breakdown.derivativeLoss, 0, 'e', 4)
|
|
|
|
|
.arg(breakdown.verticalLoss, 0, 'e', 4)
|
|
|
|
|
@ -4099,7 +4019,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
|
|
|
|
|
.arg(breakdown.shapeLoss, 0, 'e', 4)
|
|
|
|
|
.arg(breakdown.registrationAmbiguous)
|
|
|
|
|
.arg(breakdown.horizontalPhysicalShift, 0, 'e', 4)
|
|
|
|
|
.arg(breakdown.coverage, 0, 'f', 4)
|
|
|
|
|
.arg(breakdown.total, 0, 'e', 4));
|
|
|
|
|
|
|
|
|
|
return breakdown.valid
|
|
|
|
|
|