#include "nmDataDiagnostic.h" #include "nmDataWellBase.h" #include "nmDataReservoir.h" #include #include "qmath.h" #include #include #ifdef Q_OS_WIN #include #define DEBUG_OUT(msg) OutputDebugStringA(QString("%1\n").arg(msg).toLocal8Bit().data()) #endif nmDataDiagnostic::nmDataDiagnostic() { m_diagnosticSkin = nmDataAttribute("Skin", 0.0, ""); m_diagnosticWellboreStorage = nmDataAttribute("Wellbore storage", 0.01, "m^3/MPa", UNIT_TYPE_COMPRESSIBILITY, QStringList(), QStringList() << "bbl/psi" << "m^3/bar" << "m^3/kPa" << "m^3/Pa" << "m^3.cm^2/kg" << "m^2" << "m^3/MPa"); m_diagnosticPermeability = nmDataAttribute("Permeability", 0.025, "md", UNIT_TYPE_PERMEABILITY, QStringList(), QStringList() << "md" << "Darcy" << "m^2" << "cm^2" << "um^2"); m_diagnosticTransmissibility = nmDataAttribute("Transmissibility", 1000.0, "md.m", UNIT_TYPE_CONDUCTIVITY, QStringList(), QStringList()<< "md.ft" << "md.m" << "m^3"); } nmDataDiagnostic::~nmDataDiagnostic() { } // 序列化 nmDataDiagnostic 为 RapidJSON Value rapidjson::Value nmDataDiagnostic::ToJsonValue(rapidjson::Document::AllocatorType& allocator) const { // 创建一个 RapidJSON 对象类型的值 rapidjson::Value diagnosticObject(rapidjson::kObjectType); // 序列化 nmDataAttribute 类型的成员 // 调用 nmDataAttribute 自身的 ToJsonValue 方法进行递归序列化 diagnosticObject.AddMember("DiagnosticSkin", m_diagnosticSkin.ToJsonValue(allocator), allocator); diagnosticObject.AddMember("DiagnosticWellboreStorage", m_diagnosticWellboreStorage.ToJsonValue(allocator), allocator); diagnosticObject.AddMember("DiagnosticPermeability", m_diagnosticPermeability.ToJsonValue(allocator), allocator); diagnosticObject.AddMember("DiagnosticTransmissibility", m_diagnosticTransmissibility.ToJsonValue(allocator), allocator); return diagnosticObject; // 返回序列化后的 RapidJSON Value } // 从 RapidJSON Value 反序列化数据到 nmDataDiagnostic void nmDataDiagnostic::FromJsonValue(const rapidjson::Value& jsonValue) { // 反序列化 nmDataAttribute 类型的成员 // 调用 nmDataAttribute 自身的 FromJsonValue 方法进行递归反序列化 if (jsonValue.HasMember("DiagnosticSkin") && jsonValue["DiagnosticSkin"].IsObject()) { m_diagnosticSkin.FromJsonValue(jsonValue["DiagnosticSkin"]); } if (jsonValue.HasMember("DiagnosticWellboreStorage") && jsonValue["DiagnosticWellboreStorage"].IsObject()) { m_diagnosticWellboreStorage.FromJsonValue(jsonValue["DiagnosticWellboreStorage"]); } if (jsonValue.HasMember("DiagnosticPermeability") && jsonValue["DiagnosticPermeability"].IsObject()) { m_diagnosticPermeability.FromJsonValue(jsonValue["DiagnosticPermeability"]); } if (jsonValue.HasMember("DiagnosticTransmissibility") && jsonValue["DiagnosticTransmissibility"].IsObject()) { m_diagnosticTransmissibility.FromJsonValue(jsonValue["DiagnosticTransmissibility"]); } } // 获取诊断结果参数 nmDataAttribute& nmDataDiagnostic::getDiagnosticPermeability() { return m_diagnosticPermeability; } nmDataAttribute& nmDataDiagnostic::getDiagnosticSkin() { return m_diagnosticSkin; } nmDataAttribute& nmDataDiagnostic::getDiagnosticWellboreStorage() { return m_diagnosticWellboreStorage; } nmDataAttribute& nmDataDiagnostic::getDiagnosticTransmissibility() { return m_diagnosticTransmissibility; } void nmDataDiagnostic::resetFromDiagnostic(const QVector>& rawData, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { if (!pWell || !pReservoir) { DEBUG_OUT("Invalid well or reservoir data"); return; } if (rawData.size() < 2 || rawData[0].isEmpty() || rawData[1].isEmpty()) { DEBUG_OUT("Invalid rawData format"); return; } DEBUG_OUT("=== Starting Intelligent Diagnostic Analysis ==="); // 调用各个参数的诊断计算方法 calculateWellboreStorage(rawData, pWell, pReservoir); calculateTransmissibility(rawData, pWell, pReservoir); // 先计算 kh calculatePermeability(pWell, pReservoir); // 再基于 kh 计算 k //calculateSkin(rawData, pWell, pReservoir); DEBUG_OUT("=== Diagnostic Analysis Complete ==="); } void nmDataDiagnostic::calculateWellboreStorage(const QVector>& rawData, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Intelligent Wellbore Storage Analysis ==="); const QVector& timeData = rawData[0]; // 时间数据 (hr) const QVector& pressureDropData = rawData[1]; // 压力差数据 (MPa) // 确保有足够的数据点 int availablePoints = qMin(timeData.size(), pressureDropData.size()); if (availablePoints < 3) { DEBUG_OUT("Not enough data points for wellbore storage analysis (need at least 3 points)"); return; } // 只分析早期数据点(前20%或前30个点,取较小值) int earlyPointsToUse = qMin(qMax(5, static_cast(availablePoints * 0.2)), 30); earlyPointsToUse = qMin(earlyPointsToUse, availablePoints); DEBUG_OUT(QString("Total points: %1, analyzing early %2 points (%3%) for flow regime identification") .arg(availablePoints).arg(earlyPointsToUse).arg(100.0 * earlyPointsToUse / availablePoints, 0, 'f', 1)); // 获取储层和井参数 double viscosity = pReservoir->getMiuo().getValue().toDouble(); // 粘度 double bo = pReservoir->getBo().getValue().toDouble(); // 体积系数 DEBUG_OUT(QString("Reservoir parameters: viscosity=%1, Bo=%2").arg(viscosity).arg(bo)); // 智能识别流动阶段:分析wellbore storage和径向流两种机制 FlowRegimeAnalysis wellboreStorageAnalysis = analyzeFlowRegime(timeData, pressureDropData, earlyPointsToUse, 1.0, "Wellbore Storage", pWell, pReservoir); FlowRegimeAnalysis radialFlowAnalysis = analyzeFlowRegime(timeData, pressureDropData, earlyPointsToUse, 0.5, "Radial Flow", pWell, pReservoir); // 智能选择最佳结果 - 改进的选择逻辑 FlowRegimeAnalysis* selectedAnalysis = nullptr; QString selectionReason = ""; if (wellboreStorageAnalysis.isValid && radialFlowAnalysis.isValid) { // 检查每个斜率更接近哪个理论值 double wbSlope = wellboreStorageAnalysis.slope; double rfSlope = radialFlowAnalysis.slope; // 计算斜率到两个理论值的距离 double wbDistanceTo1 = qAbs(wbSlope - 1.0); double wbDistanceTo05 = qAbs(wbSlope - 0.5); double rfDistanceTo1 = qAbs(rfSlope - 1.0); double rfDistanceTo05 = qAbs(rfSlope - 0.5); // 判断wellbore storage斜率更接近哪个理论值 bool wbCloserTo1 = wbDistanceTo1 < wbDistanceTo05; // 判断radial flow斜率更接近哪个理论值 bool rfCloserTo05 = rfDistanceTo05 < rfDistanceTo1; DEBUG_OUT(QString("=== Selection Analysis ===")); DEBUG_OUT(QString("WB slope %1: distance to 1.0=%2, to 0.5=%3, closer to %4") .arg(wbSlope, 0, 'f', 4).arg(wbDistanceTo1, 0, 'f', 4).arg(wbDistanceTo05, 0, 'f', 4) .arg(wbCloserTo1 ? "1.0" : "0.5")); DEBUG_OUT(QString("RF slope %1: distance to 1.0=%2, to 0.5=%3, closer to %4") .arg(rfSlope, 0, 'f', 4).arg(rfDistanceTo1, 0, 'f', 4).arg(rfDistanceTo05, 0, 'f', 4) .arg(rfCloserTo05 ? "0.5" : "1.0")); // 决策逻辑: if (wbCloserTo1 && wellboreStorageAnalysis.avgTime < 0.05) { // 如果wellbore storage斜率确实更接近1.0且时间很早,优先选择wellbore storage selectedAnalysis = &wellboreStorageAnalysis; selectionReason = QString("Wellbore storage slope (%1) closer to 1.0 and early time (%2hr)") .arg(wbSlope, 0, 'f', 4).arg(wellboreStorageAnalysis.avgTime, 0, 'f', 6); } else if (rfCloserTo05) { // 如果radial flow斜率确实更接近0.5,选择radial flow selectedAnalysis = &radialFlowAnalysis; selectionReason = QString("Radial flow slope (%1) closer to 0.5, difference=%2") .arg(rfSlope, 0, 'f', 4).arg(radialFlowAnalysis.slopeDifference, 0, 'f', 4); } else if (wbCloserTo1) { // 如果wellbore storage斜率更接近1.0,选择wellbore storage selectedAnalysis = &wellboreStorageAnalysis; selectionReason = QString("Wellbore storage slope (%1) closer to 1.0, difference=%2") .arg(wbSlope, 0, 'f', 4).arg(wellboreStorageAnalysis.slopeDifference, 0, 'f', 4); } else { // 兜底:选择斜率差异更小的 if (wellboreStorageAnalysis.slopeDifference < radialFlowAnalysis.slopeDifference) { selectedAnalysis = &wellboreStorageAnalysis; selectionReason = QString("Fallback: WB slope difference (%1) < RF difference (%2)") .arg(wellboreStorageAnalysis.slopeDifference, 0, 'f', 4) .arg(radialFlowAnalysis.slopeDifference, 0, 'f', 4); } else { selectedAnalysis = &radialFlowAnalysis; selectionReason = QString("Fallback: RF slope difference (%1) < WB difference (%2)") .arg(radialFlowAnalysis.slopeDifference, 0, 'f', 4) .arg(wellboreStorageAnalysis.slopeDifference, 0, 'f', 4); } } } else if (wellboreStorageAnalysis.isValid) { selectedAnalysis = &wellboreStorageAnalysis; selectionReason = "Only wellbore storage analysis is valid"; } else if (radialFlowAnalysis.isValid) { selectedAnalysis = &radialFlowAnalysis; selectionReason = "Only radial flow analysis is valid"; } // 设置诊断结果 if (selectedAnalysis && selectedAnalysis->result > 0) { m_diagnosticWellboreStorage.setValue(selectedAnalysis->result); DEBUG_OUT(QString("=== Final Result ===")); DEBUG_OUT(QString("Selected method: %1").arg(selectedAnalysis->description)); DEBUG_OUT(QString("Selection reason: %1").arg(selectionReason)); DEBUG_OUT(QString("Final wellbore storage: %1").arg(selectedAnalysis->result)); DEBUG_OUT(QString("Based on points [%1,%2] at times %3hr and %4hr") .arg(selectedAnalysis->pointPair.first).arg(selectedAnalysis->pointPair.second) .arg(timeData[selectedAnalysis->pointPair.first]) .arg(timeData[selectedAnalysis->pointPair.second])); } else { DEBUG_OUT("Unable to calculate wellbore storage - no valid flow regime identified"); } } void nmDataDiagnostic::calculateTransmissibility(const QVector>& rawData, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Transmissibility (kh) Analysis ==="); // 检查数据格式:需要3个向量 [时间, 压力, 导数] if (rawData.size() < 3 || rawData[0].isEmpty() || rawData[1].isEmpty() || rawData[2].isEmpty()) { DEBUG_OUT("Invalid rawData format: need [time, pressure, derivative] vectors"); return; } const QVector& timeData = rawData[0]; // 时间数据 (hr) const QVector& derivativeData = rawData[2]; // 压力导数数据 (MPa) // 确保有足够的数据点 int availablePoints = timeData.size(); if (availablePoints < 10) { DEBUG_OUT("Not enough data points for transmissibility analysis (need at least 10 points)"); return; } DEBUG_OUT("Trying multiple analysis methods to find best transmissibility estimate..."); // 方法1:标准径向流平台分析 FlowRegimeAnalysis standardRadialFlow = analyzeStandardRadialFlow( timeData, derivativeData, availablePoints, pWell, pReservoir); // 方法2:晚期分析(使用极小导数值) FlowRegimeAnalysis saphirStyleAnalysis = analyzeSaphirStyleLateTime( timeData, derivativeData, availablePoints, pWell, pReservoir); // 方法3:早中期稳定段分析 FlowRegimeAnalysis earlyStableAnalysis = analyzeEarlyStableFlow( timeData, derivativeData, availablePoints, pWell, pReservoir); // 智能选择最佳方法 FlowRegimeAnalysis* selectedAnalysis = selectBestTransmissibilityAnalysis( standardRadialFlow, saphirStyleAnalysis, earlyStableAnalysis); if (selectedAnalysis && selectedAnalysis->isValid && selectedAnalysis->result > 0) { double khValue = selectedAnalysis->result; m_diagnosticTransmissibility.setValue(khValue); DEBUG_OUT(QString("=== Transmissibility Analysis Result ===")); DEBUG_OUT(QString("Selected method: %1").arg(selectedAnalysis->description)); DEBUG_OUT(QString("Transmissibility (kh): %1 md*m").arg(khValue, 0, 'f', 6)); DEBUG_OUT(QString("Analysis time: %1 hr").arg(selectedAnalysis->avgTime, 0, 'f', 4)); DEBUG_OUT(QString("Derivative value used: %1 MPa").arg(selectedAnalysis->avgPressure, 0, 'f', 6)); } else { DEBUG_OUT("All transmissibility analysis methods failed"); } } void nmDataDiagnostic::calculatePermeability(nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Permeability (k) Analysis ==="); // 获取已计算的 kh 值 double khValue = m_diagnosticTransmissibility.getValue().toDouble(); if (khValue <= 0) { DEBUG_OUT("Invalid transmissibility value - cannot calculate permeability"); DEBUG_OUT("Please ensure transmissibility is calculated first"); return; } // 获取地层厚度 double thickness = pReservoir->getThickness().getValue().toDouble(); // 地层厚度 (m) if (thickness <= 0) { DEBUG_OUT("Invalid reservoir thickness for permeability calculation"); return; } // 计算渗透率 k = kh / h double kValue = khValue / thickness; m_diagnosticPermeability.setValue(kValue); DEBUG_OUT(QString("=== Permeability Analysis Result ===")); DEBUG_OUT(QString("Transmissibility (kh): %1 md*m").arg(khValue, 0, 'f', 6)); DEBUG_OUT(QString("Reservoir thickness (h): %1 m").arg(thickness, 0, 'f', 2)); DEBUG_OUT(QString("Permeability (k): %1 md").arg(kValue, 0, 'f', 6)); } //void nmDataDiagnostic::calculateSkin(const QVector>& rawData, // nmDataWellBase* pWell, nmDataReservoir* pReservoir) //{ // //} nmDataDiagnostic::FlowRegimeAnalysis nmDataDiagnostic::analyzeFlowRegime( const QVector& timeData, const QVector& pressureDropData, int pointCount, double targetSlope, const QString& regimeName, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { FlowRegimeAnalysis analysis; analysis.pointPair = findClosestSlopeToValue(timeData, pressureDropData, pointCount, targetSlope); if (analysis.pointPair.first >= 0 && analysis.pointPair.second >= 0) { int i = analysis.pointPair.first; int j = analysis.pointPair.second; double t1 = timeData[i]; double t2 = timeData[j]; double deltaP1 = pressureDropData[i]; double deltaP2 = pressureDropData[j]; analysis.slope = calculateSlopeBetweenPoints(t1, deltaP1, t2, deltaP2); analysis.slopeDifference = qAbs(analysis.slope - targetSlope); analysis.avgTime = (t1 + t2) / 2.0; analysis.avgPressure = (deltaP1 + deltaP2) / 2.0; // 获取流量 double flowRate = pWell->getFlowRateAtTime(analysis.avgTime); if (flowRate > 0) { // 获取储层参数 double viscosity = pReservoir->getMiuo().getValue().toDouble(); double bo = pReservoir->getBo().getValue().toDouble(); // 使用公式计算wellbore storage: c = qb*μo*Bo*t/(24*ΔP) analysis.result = (flowRate * viscosity * bo * analysis.avgTime) / (24.0 * analysis.avgPressure); analysis.isValid = true; analysis.description = QString("%1 (slope=%2, target=%3, diff=%4)") .arg(regimeName).arg(analysis.slope, 0, 'f', 4) .arg(targetSlope, 0, 'f', 1).arg(analysis.slopeDifference, 0, 'f', 4); DEBUG_OUT(QString("=== %1 Analysis ===").arg(regimeName)); DEBUG_OUT(QString("Best points: [%1,%2] at times %3hr - %4hr") .arg(i).arg(j).arg(t1, 0, 'f', 6).arg(t2, 0, 'f', 6)); DEBUG_OUT(QString("Slope: %1 (target: %2, difference: %3)") .arg(analysis.slope, 0, 'f', 4).arg(targetSlope, 0, 'f', 1) .arg(analysis.slopeDifference, 0, 'f', 4)); DEBUG_OUT(QString("Average time: %1hr, Average derTaP: %2").arg(analysis.avgTime, 0, 'f', 6).arg(analysis.avgPressure, 0, 'f', 6)); DEBUG_OUT(QString("Flow rate: %1, Result: %2").arg(flowRate).arg(analysis.result, 0, 'f', 6)); } } return analysis; } QPair nmDataDiagnostic::findClosestSlopeToValue(const QVector& timeData, const QVector& pressureDropData, int pointCount, double targetSlope) { double bestSlope = -1.0; double minDifference = 999999.0; QPair bestPair(-1, -1); // 遍历所有可能的点对,寻找斜率最接近目标值的 for (int i = 0; i < pointCount - 1; ++i) { for (int j = i + 1; j < pointCount; ++j) { double t1 = timeData[i]; double t2 = timeData[j]; double deltaP1 = pressureDropData[i]; double deltaP2 = pressureDropData[j]; // 确保数据有效 if (t1 <= 0 || t2 <= 0 || deltaP1 <= 0 || deltaP2 <= 0 || t2 <= t1) { continue; } // 计算斜率 double slope = calculateSlopeBetweenPoints(t1, deltaP1, t2, deltaP2); // 计算与目标值的差异 double difference = qAbs(slope - targetSlope); // 如果这个斜率更接近目标值,更新最佳结果 if (difference < minDifference) { minDifference = difference; bestSlope = slope; bestPair.first = i; bestPair.second = j; } } } return bestPair; } double nmDataDiagnostic::calculateSlopeBetweenPoints(double t1, double deltaP1, double t2, double deltaP2) { if (t1 <= 0 || t2 <= 0 || deltaP1 <= 0 || deltaP2 <= 0 || t2 <= t1) { return 0.0; } // 计算双对数斜率: slope = log(ΔP2/ΔP1) / log(t2/t1) double logTimeRatio = log10(t2 / t1); double logPressureRatio = log10(deltaP2 / deltaP1); if (qAbs(logTimeRatio) < 1e-10) { // 避免除以零 return 0.0; } double slope = logPressureRatio / logTimeRatio; return slope; } double nmDataDiagnostic::interpolatePressureAtTime(const QVector& timeData, const QVector& pressureDropData, double targetTime) { for (int i = 0; i < timeData.size() - 1; ++i) { if (targetTime >= timeData[i] && targetTime <= timeData[i + 1]) { // 线性插值 double ratio = (targetTime - timeData[i]) / (timeData[i + 1] - timeData[i]); return pressureDropData[i] + ratio * (pressureDropData[i + 1] - pressureDropData[i]); } } return -1.0; // 未找到合适的区间 } nmDataDiagnostic::FlowRegimeAnalysis nmDataDiagnostic::analyzeRadialFlowForKh( const QVector& timeData, const QVector& derivativeData, int startPoint, int pointCount, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { FlowRegimeAnalysis analysis; analysis.isValid = false; DEBUG_OUT(QString("Starting radial flow analysis from point %1, analyzing %2 points") .arg(startPoint).arg(pointCount)); // 寻找导数平台段 QPair bestPlatform = findDerivativePlatform(timeData, derivativeData, startPoint, pointCount); if (bestPlatform.first >= 0 && bestPlatform.second >= 0) { int platformLength = bestPlatform.second - bestPlatform.first + 1; // 计算整个平台段的平均值,而不是只使用两个端点 double totalDeriv = 0.0; double totalTime = 0.0; for (int i = bestPlatform.first; i <= bestPlatform.second; ++i) { totalDeriv += derivativeData[i]; totalTime += timeData[i]; } analysis.avgPressure = totalDeriv / platformLength; // 平台段平均值 analysis.avgTime = totalTime / platformLength; // 平台段时间平均值 analysis.pointPair = bestPlatform; // 获取对应时间的流量 double flowRate = pWell->getFlowRateAtTime(analysis.avgTime); double bo = pReservoir->getBo().getValue().toDouble(); double miuo = pReservoir->getMiuo().getValue().toDouble(); DEBUG_OUT(QString("Radial flow platform found: points [%1-%2], length=%3 points") .arg(bestPlatform.first).arg(bestPlatform.second).arg(platformLength)); DEBUG_OUT(QString("Time range: %1hr to %2hr") .arg(timeData[bestPlatform.first], 0, 'f', 4) .arg(timeData[bestPlatform.second], 0, 'f', 4)); DEBUG_OUT(QString("Platform average derivative: %1 (from %2 points)") .arg(analysis.avgPressure, 0, 'f', 6).arg(platformLength)); DEBUG_OUT(QString("Analysis time: %1 hr, flow rate: %2") .arg(analysis.avgTime, 0, 'f', 4).arg(flowRate, 0, 'f', 4)); if (flowRate > 0 && analysis.avgPressure > 0) { // 公式: kh = kh = (70.6 * q * B * μ) / (ΔP'_{platform}) double kh = flowRate * bo * miuo * 70.6 / analysis.avgPressure; analysis.result = kh; analysis.isValid = true; // 计算平台段的整体斜率(使用首尾点) double t1 = timeData[bestPlatform.first]; double t2 = timeData[bestPlatform.second]; double deriv1 = derivativeData[bestPlatform.first]; double deriv2 = derivativeData[bestPlatform.second]; analysis.slope = calculateSlopeBetweenPoints(t1, deriv1, t2, deriv2); analysis.slopeDifference = qAbs(analysis.slope - 0.0); analysis.description = QString("Radial Flow Platform (length=%1, DertaP'=%2, kh=%3)") .arg(platformLength) .arg(analysis.avgPressure, 0, 'f', 6) .arg(kh, 0, 'f', 6); DEBUG_OUT(QString("Platform overall slope: %1 (over %2 hours)") .arg(analysis.slope, 0, 'f', 4) .arg(t2 - t1, 0, 'f', 2)); DEBUG_OUT(QString("Calculated kh: %1").arg(kh, 0, 'f', 6)); } } return analysis; } QPair nmDataDiagnostic::findDerivativePlatform( const QVector& timeData, const QVector& derivativeData, int startPoint, int pointCount) { QPair bestSegment(-1, -1); if (startPoint < 0 || pointCount <= 0) { DEBUG_OUT("Not enough points for platform identification"); return bestSegment; } int endPoint = qMin(startPoint + pointCount, qMin(timeData.size(), derivativeData.size())); if (endPoint - startPoint < 3) { DEBUG_OUT("Not enough points for platform identification"); return bestSegment; } DEBUG_OUT(QString("Searching for longest stable platform in points [%1-%2]").arg(startPoint).arg(endPoint-1)); // 首先计算整个分析区间的导数平均值,作为平台参考值 double totalDeriv = 0.0; int count = 0; for (int i = startPoint; i < endPoint; ++i) { totalDeriv += derivativeData[i]; count++; } if (count <= 0) { return bestSegment; } double averageDeriv = totalDeriv / count; DEBUG_OUT(QString("Overall derivative average in analysis range: %1").arg(averageDeriv, 0, 'f', 6)); // 定义平台稳定性阈值(相对于平均值的允许偏差) const double stabilityThreshold = 0.01; // 1%的偏差 double maxAllowedDeviation = averageDeriv * stabilityThreshold; DEBUG_OUT(QString("Stability threshold: +-%1").arg(maxAllowedDeviation, 0, 'f', 6)); // 寻找最长的连续稳定段 int maxLength = 0; int currentStart = -1; int currentLength = 0; for (int i = startPoint; i < endPoint; ++i) { double deviation = qAbs(derivativeData[i] - averageDeriv); if (deviation <= maxAllowedDeviation) { // 点在稳定范围内 if (currentStart == -1) { currentStart = i; } currentLength++; } else { // 点超出稳定范围,结束当前段 if (currentLength > maxLength) { maxLength = currentLength; bestSegment = qMakePair(currentStart, currentStart + currentLength - 1); } currentStart = -1; currentLength = 0; } } // 检查最后一段 if (currentLength > maxLength) { maxLength = currentLength; bestSegment = qMakePair(currentStart, currentStart + currentLength - 1); } if (bestSegment.first >= 0) { DEBUG_OUT(QString("Longest stable platform found: points [%1-%2], length=%3 points") .arg(bestSegment.first).arg(bestSegment.second).arg(maxLength)); DEBUG_OUT(QString("Time range: %1hr to %2hr") .arg(timeData[bestSegment.first], 0, 'f', 4) .arg(timeData[bestSegment.second], 0, 'f', 4)); // 计算该平台段的统计信息 double platformAvg = 0.0; for (int i = bestSegment.first; i <= bestSegment.second; ++i) { platformAvg += derivativeData[i]; } platformAvg /= maxLength; DEBUG_OUT(QString("Platform average derivative: %1, overall average: %2") .arg(platformAvg, 0, 'f', 6).arg(averageDeriv, 0, 'f', 6)); } else { DEBUG_OUT("No suitable platform segment found using stability criteria"); } return bestSegment; } // 方法1:标准径向流平台分析(保持原有逻辑) nmDataDiagnostic::FlowRegimeAnalysis nmDataDiagnostic::analyzeStandardRadialFlow( const QVector& timeData, const QVector& derivativeData, int availablePoints, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Method 1: Standard Radial Flow Platform Analysis ==="); // 使用中后期数据寻找平台 int startPoint = qMin(static_cast(availablePoints * 0.3), availablePoints - 20); int pointsToAnalyze = availablePoints - startPoint; if (pointsToAnalyze < 5) { FlowRegimeAnalysis failedAnalysis; failedAnalysis.isValid = false; failedAnalysis.description = "Standard Radial Flow (failed - insufficient points)"; return failedAnalysis; } return analyzeRadialFlowForKh(timeData, derivativeData, startPoint, pointsToAnalyze, pWell, pReservoir); } // 方法2:晚期分析 nmDataDiagnostic::FlowRegimeAnalysis nmDataDiagnostic::analyzeSaphirStyleLateTime( const QVector& timeData, const QVector& derivativeData, int availablePoints, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Method 2: Late-Time Analysis ==="); FlowRegimeAnalysis analysis; analysis.isValid = false; analysis.description = "Late-Time Analysis"; // 使用后50%的数据寻找极小导数值 int startPoint = qMax(static_cast(availablePoints * 0.5), availablePoints - 50); int endPoint = availablePoints - 1; if (endPoint - startPoint < 10) { DEBUG_OUT("Not enough late-time data for analysis"); return analysis; } DEBUG_OUT(QString("Analyzing late-time points [%1-%2] for minimum derivative") .arg(startPoint).arg(endPoint)); // 寻找晚期导数的最小值段 QPair minDerivativeSegment = findMinimumDerivativeSegment( timeData, derivativeData, startPoint, endPoint); if (minDerivativeSegment.first >= 0) { // 计算该段的平均值 int segmentStart = minDerivativeSegment.first; int segmentEnd = minDerivativeSegment.second; int segmentLength = segmentEnd - segmentStart + 1; double totalDeriv = 0.0; double totalTime = 0.0; for (int i = segmentStart; i <= segmentEnd; ++i) { totalDeriv += derivativeData[i]; totalTime += timeData[i]; } analysis.avgPressure = totalDeriv / segmentLength; // 最小导数段平均值 analysis.avgTime = totalTime / segmentLength; // 对应时间平均值 analysis.pointPair = minDerivativeSegment; // 获取流量和储层参数 double flowRate = pWell->getFlowRateAtTime(analysis.avgTime); double bo = pReservoir->getBo().getValue().toDouble(); double miuo = pReservoir->getMiuo().getValue().toDouble(); DEBUG_OUT(QString("Minimum derivative segment found: points [%1-%2], length=%3") .arg(segmentStart).arg(segmentEnd).arg(segmentLength)); DEBUG_OUT(QString("Time range: %1hr to %2hr") .arg(timeData[segmentStart], 0, 'f', 2).arg(timeData[segmentEnd], 0, 'f', 2)); DEBUG_OUT(QString("Minimum derivative average: %1 MPa") .arg(analysis.avgPressure, 0, 'f', 6)); DEBUG_OUT(QString("Flow rate at analysis time: %1") .arg(flowRate, 0, 'f', 4)); if (flowRate > 0 && analysis.avgPressure > 0) { // kh = (70.6 * q * B * μ) / (DertaP'_min) analysis.result = (70.6 * flowRate * bo * miuo) / analysis.avgPressure; analysis.isValid = true; // 计算质量指标 analysis.slope = 0.0; // 晚期分析不关注斜率 analysis.slopeDifference = 0.0; analysis.description = QString("Late-Time (segment=%1pts, DertaP'=%2, kh=%3)") .arg(segmentLength) .arg(analysis.avgPressure, 0, 'f', 6) .arg(analysis.result, 0, 'f', 0); DEBUG_OUT(QString("Calculation successful:")); DEBUG_OUT(QString("Formula: kh = (70.6 * %1 * %2 * %3) / %4") .arg(flowRate, 0, 'f', 1).arg(bo, 0, 'f', 2) .arg(miuo, 0, 'f', 2).arg(analysis.avgPressure, 0, 'f', 6)); DEBUG_OUT(QString("Result: kh = %1 md*m").arg(analysis.result, 0, 'f', 0)); } else { DEBUG_OUT("Invalid flow rate or derivative value for analysis"); } } else { DEBUG_OUT("Failed to find suitable minimum derivative segment"); } return analysis; } // 方法3:早中期稳定段分析 nmDataDiagnostic::FlowRegimeAnalysis nmDataDiagnostic::analyzeEarlyStableFlow( const QVector& timeData, const QVector& derivativeData, int availablePoints, nmDataWellBase* pWell, nmDataReservoir* pReservoir) { DEBUG_OUT("=== Method 3: Early-Middle Stable Flow Analysis ==="); FlowRegimeAnalysis analysis; analysis.isValid = false; analysis.description = "Early-Middle Stable Flow"; // 使用10%-60%的数据寻找相对稳定段 int startPoint = qMax(static_cast(availablePoints * 0.1), 5); int endPoint = qMin(static_cast(availablePoints * 0.6), availablePoints - 5); if (endPoint - startPoint < 10) { DEBUG_OUT("Not enough data for early-middle stable flow analysis"); return analysis; } DEBUG_OUT(QString("Searching for stable flow in points [%1-%2]") .arg(startPoint).arg(endPoint)); // 寻找变异系数最小的连续段 QPair mostStableSegment = findMostStableDerivativeSegment( derivativeData, startPoint, endPoint); if (mostStableSegment.first >= 0) { int segmentStart = mostStableSegment.first; int segmentEnd = mostStableSegment.second; int segmentLength = segmentEnd - segmentStart + 1; // 计算该段的平均值 double totalDeriv = 0.0; double totalTime = 0.0; for (int i = segmentStart; i <= segmentEnd; ++i) { totalDeriv += derivativeData[i]; totalTime += timeData[i]; } analysis.avgPressure = totalDeriv / segmentLength; analysis.avgTime = totalTime / segmentLength; analysis.pointPair = mostStableSegment; // 获取流量和储层参数 double flowRate = pWell->getFlowRateAtTime(analysis.avgTime); double bo = pReservoir->getBo().getValue().toDouble(); double miuo = pReservoir->getMiuo().getValue().toDouble(); if (flowRate > 0 && analysis.avgPressure > 0) { analysis.result = (70.6 * flowRate * bo * miuo) / analysis.avgPressure; analysis.isValid = true; analysis.description = QString("Early-Middle Stable (segment=%1pts, DertaP'=%2, kh=%3)") .arg(segmentLength) .arg(analysis.avgPressure, 0, 'f', 6) .arg(analysis.result, 0, 'f', 0); DEBUG_OUT(QString("Early-middle stable flow found: points [%1-%2], kh=%3") .arg(segmentStart).arg(segmentEnd).arg(analysis.result, 0, 'f', 0)); } } return analysis; } // 寻找最小导数段 QPair nmDataDiagnostic::findMinimumDerivativeSegment( const QVector& timeData, const QVector& derivativeData, int startPoint, int endPoint) { QPair bestSegment(-1, -1); // 寻找连续的低值段(至少5个点) const int minSegmentLength = 5; double minAvgDerivative = 999999.0; for (int i = startPoint; i <= endPoint - minSegmentLength; ++i) { for (int len = minSegmentLength; len <= qMin(20, endPoint - i + 1); ++len) { int j = i + len - 1; // 计算这个段的平均导数 double totalDeriv = 0.0; for (int k = i; k <= j; ++k) { totalDeriv += derivativeData[k]; } double avgDeriv = totalDeriv / len; // 如果这个段的平均导数更小,更新最佳结果 if (avgDeriv < minAvgDerivative) { minAvgDerivative = avgDeriv; bestSegment = qMakePair(i, j); } } } if (bestSegment.first >= 0) { DEBUG_OUT(QString("Found minimum derivative segment [%1-%2] with average DertaP'=%3") .arg(bestSegment.first).arg(bestSegment.second) .arg(minAvgDerivative, 0, 'f', 6)); } return bestSegment; } // 寻找最稳定的导数段 QPair nmDataDiagnostic::findMostStableDerivativeSegment( const QVector& derivativeData, int startPoint, int endPoint) { QPair bestSegment(-1, -1); const int minSegmentLength = 8; double minVariationCoeff = 999999.0; for (int i = startPoint; i <= endPoint - minSegmentLength; ++i) { for (int len = minSegmentLength; len <= qMin(25, endPoint - i + 1); ++len) { int j = i + len - 1; // 计算变异系数 (标准差/均值) double variationCoeff = calculateVariationCoefficient(derivativeData, i, j); if (variationCoeff < minVariationCoeff) { minVariationCoeff = variationCoeff; bestSegment = qMakePair(i, j); } } } return bestSegment; } // 辅助函数:计算变异系数 double nmDataDiagnostic::calculateVariationCoefficient( const QVector& data, int start, int end) { if (end <= start) return 999999.0; int count = end - start + 1; // 计算均值 double mean = 0.0; for (int i = start; i <= end; ++i) { mean += data[i]; } mean /= count; if (mean <= 0) return 999999.0; // 计算标准差 double variance = 0.0; for (int i = start; i <= end; ++i) { double diff = data[i] - mean; variance += diff * diff; } double stdDev = qSqrt(variance / count); return stdDev / mean; // 变异系数 } // 智能选择最佳分析方法 nmDataDiagnostic::FlowRegimeAnalysis* nmDataDiagnostic::selectBestTransmissibilityAnalysis( FlowRegimeAnalysis& standardRadial, FlowRegimeAnalysis& saphirStyle, FlowRegimeAnalysis& earlyStable) { QVector validMethods; if (standardRadial.isValid) validMethods.append(&standardRadial); if (saphirStyle.isValid) validMethods.append(&saphirStyle); if (earlyStable.isValid) validMethods.append(&earlyStable); if (validMethods.isEmpty()) { DEBUG_OUT("No valid transmissibility calculation method found"); return NULL; } DEBUG_OUT("=== Method Selection Analysis ==="); for (int i = 0; i < validMethods.size(); ++i) { FlowRegimeAnalysis* method = validMethods[i]; DEBUG_OUT(QString("Valid method: %1, kh=%2") .arg(method->description).arg(method->result, 0, 'f', 0)); } // 选择策略: // 1. 优先选择标准径向流(如果存在且质量好) // 2. 如果没有标准径向流,选择模拟商业软件行为 // 3. 最后选择早期稳定流 if (standardRadial.isValid && standardRadial.slopeDifference < 0.3) { DEBUG_OUT("Selected: Standard radial flow (good platform quality)"); return &standardRadial; } if (saphirStyle.isValid) { DEBUG_OUT("Selected: Saphir-style late-time analysis (following commercial software approach)"); return &saphirStyle; } if (earlyStable.isValid) { DEBUG_OUT("Selected: Early stable flow (fallback method)"); return &earlyStable; } // 兜底:返回第一个有效方法 DEBUG_OUT(QString("Selected: First available method (%1)").arg(validMethods[0]->description)); return validMethods[0]; } double nmDataDiagnostic::calculateSlopeVariation( const QVector& timeData, const QVector& derivativeData, int start, int end) { if (end <= start + 1) return 999999.0; double totalSlope = 0.0; int slopeCount = 0; // 计算段内所有相邻点之间的斜率绝对值 for (int i = start; i < end; ++i) { if (i + 1 >= timeData.size()) break; double slope = calculateSlopeBetweenPoints(timeData[i], derivativeData[i], timeData[i+1], derivativeData[i+1]); totalSlope += qAbs(slope); // 理想平台斜率应该接近0 slopeCount++; } return slopeCount > 0 ? totalSlope / slopeCount : 999999.0; }