# include " nmWxAutomaticFitting.h "
# include "nmCalculationAutoFitPSO.h"
# include "nmCalculationAutoFitLM.h"
# include "nmWxAutomaticfittingStart.h"
# include "nmWxParameterProperty.h"
# include "nmAttrRegistry.h"
# include "nmDataAnalyzeManager.h"
# include "iSubWndFitting.h"
# include "iSysParaHelper.h"
# include "iParameter.h"
# include "mModuleDefines.h"
# include "mGui/mGuiAnal/iAnalRun.h"
# include "mAlgDefines.h"
# pragma comment(lib, "mGuiAnal.lib")
# include <QApplication>
# include <QDesktopWidget>
# include <QHeaderView>
# include <cmath>
# include <float.h>
# ifdef Q_OS_WIN
# include <windows.h>
# define DEBUG_UI(msg) OutputDebugStringA(QString("[UI] %1\n").arg(msg).toLocal8Bit().data())
# endif
namespace {
bool nmAutoFitUiIsFinite ( double value )
{
# ifdef Q_OS_WIN
return _finite ( value ) ! = 0 ;
# else
return std : : isfinite ( value ) ;
# endif
}
// 物理边界可能由多个浮点配置量计算得到,界面十进制文本再转回 double 后会有
// 末位舍入差。这里只放宽约 8 个机器精度,不放宽实际物理范围。
bool nmAutoFitUiNearlyEqual ( double left , double right )
{
const double scale = qMax ( std : : fabs ( left ) , std : : fabs ( right ) ) ;
return std : : fabs ( left - right ) < = DBL_EPSILON * 8.0 * scale ;
}
// 从系统参数表读取物理边界,所有自动拟合参数统一以参数表配置为准。
bool nmAutoFitReadPhysicalRange ( const char * parameterName ,
double & minValue , double & maxValue )
{
iSysParaHelper * paraHelper = _paraHelper ;
if ( ! paraHelper ) {
return false ;
}
iParameter * parameter = paraHelper - > getPara ( QString : : fromLatin1 ( parameterName ) , s_Nm_Serie ) ;
if ( ! parameter | | ! nmAutoFitUiIsFinite ( parameter - > m_dMin )
| | ! nmAutoFitUiIsFinite ( parameter - > m_dMax )
| | parameter - > m_dMin > = parameter - > m_dMax ) {
return false ;
}
minValue = parameter - > m_dMin ;
maxValue = parameter - > m_dMax ;
return true ;
}
}
// 隐藏参数取消勾选并禁用;新显示的参数默认勾选,保留已显示参数的手工选择。
void nmWxAutomaticFitting : : setParameterRowVisible ( QTableWidget * table , int row , bool visible )
{
if ( ! table | | row < 0 | | row > = table - > rowCount ( ) ) {
return ;
}
const bool wasHidden = table - > isRowHidden ( row ) ;
table - > setRowHidden ( row , ! visible ) ;
QCheckBox * checkBox = qobject_cast < QCheckBox * > ( table - > cellWidget ( row , 1 ) ) ;
if ( checkBox ) {
// 与当前采样重叠的参数不能参与拟合,切换目标井时也保持互斥。
const QString samplingNames [ ] = { " k " , " Skin " , " C " , QString ( QChar ( 0x03c6 ) ) } ;
const bool sampled = m_sensitivitySettings & & row < 4 & & m_sampledParameters . contains ( samplingNames [ row ] ) ;
checkBox - > setEnabled ( visible & & ! sampled ) ;
if ( ! visible | | sampled ) {
checkBox - > setChecked ( false ) ;
} else if ( wasHidden ) {
checkBox - > setChecked ( ! m_sensitivitySettings ) ;
}
}
}
// 按当前模型和井型可用的参数行重新整理显示序号。
void nmWxAutomaticFitting : : renumberVisibleParameterRows ( QTableWidget * table )
{
if ( ! table ) {
return ;
}
int visibleIndex = 1 ;
for ( int row = 0 ; row < table - > rowCount ( ) ; + + row ) {
if ( table - > isRowHidden ( row ) ) {
continue ;
}
QTableWidgetItem * item = table - > item ( row , 0 ) ;
if ( ! item ) {
item = new QTableWidgetItem ( ) ;
table - > setItem ( row , 0 , item ) ;
}
item - > setText ( QString : : number ( visibleIndex + + ) ) ;
item - > setTextAlignment ( Qt : : AlignCenter ) ;
}
}
// 拟合表仅包含可优化参数,按模型和目标井类型控制显示。
void nmWxAutomaticFitting : : updateParameterVisibility ( QTableWidget * table , NM_SOLVER_MODEL_TYPE eType )
{
if ( ! table ) {
return ;
}
// 裂缝参数直接按井型设置,避免先显示再隐藏造成选择状态重置。
for ( int row = 0 ; row < 4 ; + + row ) {
setParameterRowVisible ( table , row , true ) ;
}
setParameterRowVisible ( table , 4 , false ) ; // 原含水饱和度索引保留,不再参与拟合。
// Dfc 只属于垂直压裂井和多段压裂水平井。普通井隐藏并取消勾选,
// 防止切换目标井后不可见的裂缝参数仍进入拟合参数向量。
bool showFractureConductivity = false ;
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( manager & & m_targetWellCombo ) {
nmDataWellBase * targetWell = manager - > findWellByName ( m_targetWellCombo - > currentText ( ) ) ;
if ( targetWell ) {
NM_WELL_MODEL wellType = targetWell - > getWellType ( ) ;
showFractureConductivity =
wellType = = NM_WELL_MODEL : : Vertical_Fractured_Well | |
wellType = = NM_WELL_MODEL : : Horizontal_Fractured_Well ;
}
}
setParameterRowVisible ( table , 5 , showFractureConductivity ) ; // Dfc
setParameterRowVisible ( table , 6 , showFractureConductivity ) ; // 裂缝半长
renumberVisibleParameterRows ( table ) ;
}
// 获取可拟合参数的系统物理边界,索引 4 不再作为拟合参数。
bool nmWxAutomaticFitting : : getPhysicalParameterRange ( int parameterIndex ,
double & minValue , double & maxValue )
{
static const char * parameterNames [ ] = {
" Result_K " , " Result_W_Skin " , " Result_W_C " , " Result_phi " ,
" " , " Result_W_Dfc " ,
" W_FractureHalfLength "
} ;
if ( parameterIndex < 0 | | parameterIndex > = 7 | | parameterIndex = = 4 ) {
return false ;
}
if ( ! nmAutoFitReadPhysicalRange ( parameterNames [ parameterIndex ] , minValue , maxValue ) ) {
return false ;
}
// 自动拟合的表皮搜索下界固定为 0, 自动范围和手工输入校验使用同一边界。
if ( parameterIndex = = 1 ) {
minValue = 0.0 ;
}
return true ;
}
// 将一组上下界同步到表格和自动拟合数据,保证 PSO 读取到同一份配置。
void nmWxAutomaticFitting : : setParameterRange ( int parameterIndex ,
double minValue , double maxValue )
{
if ( ! m_parameterTable | | parameterIndex < 0 | | parameterIndex > = m_parameterTable - > rowCount ( )
| | ! nmAutoFitUiIsFinite ( minValue ) | | ! nmAutoFitUiIsFinite ( maxValue ) ) {
return ;
}
if ( maxValue < minValue ) {
qSwap ( minValue , maxValue ) ;
}
const bool wasUpdatingRanges = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
if ( m_parameterTable - > item ( parameterIndex , 2 ) ) {
m_parameterTable - > item ( parameterIndex , 2 ) - > setText ( QString : : number ( minValue , ' g ' , 10 ) ) ;
}
if ( m_parameterTable - > item ( parameterIndex , 4 ) ) {
m_parameterTable - > item ( parameterIndex , 4 ) - > setText ( QString : : number ( maxValue , ' g ' , 10 ) ) ;
}
switch ( parameterIndex ) {
case 0 :
automaticFittingData . getPermeabilityMin ( ) . setValue ( minValue ) ;
automaticFittingData . getPermeabilityMax ( ) . setValue ( maxValue ) ;
break ;
case 1 :
automaticFittingData . getSkinMin ( ) . setValue ( minValue ) ;
automaticFittingData . getSkinMax ( ) . setValue ( maxValue ) ;
break ;
case 2 :
automaticFittingData . getWellboreStorageMin ( ) . setValue ( minValue ) ;
automaticFittingData . getWellboreStorageMax ( ) . setValue ( maxValue ) ;
break ;
case 3 :
automaticFittingData . getPorosityMin ( ) . setValue ( minValue ) ;
automaticFittingData . getPorosityMax ( ) . setValue ( maxValue ) ;
break ;
case 5 :
automaticFittingData . getFractureConductivityMin ( ) . setValue ( minValue ) ;
automaticFittingData . getFractureConductivityMax ( ) . setValue ( maxValue ) ;
break ;
case 6 :
automaticFittingData . getFractureHalfLengthMin ( ) . setValue ( minValue ) ;
automaticFittingData . getFractureHalfLengthMax ( ) . setValue ( maxValue ) ;
break ;
default :
break ;
}
m_updatingParameterRanges = wasUpdatingRanges ;
}
// 根据当前初值生成建议搜索范围, 并始终截断在拟合边界内。skin 下界为 0,
// 上界按初值加固定宽度,其余正值参数使用倍率范围;每次打开窗口和拟合完成后复用。
void nmWxAutomaticFitting : : updateRangeForParameter ( int parameterIndex ,
double centerValue )
{
if ( ! m_parameterTable | | parameterIndex < 0 | | parameterIndex > = 7
| | ! nmAutoFitUiIsFinite ( centerValue ) ) {
return ;
}
double physicalMin = 0.0 ;
double physicalMax = 0.0 ;
if ( ! getPhysicalParameterRange ( parameterIndex , physicalMin , physicalMax ) ) {
return ;
}
double reference = centerValue ;
const bool positiveParameter = parameterIndex ! = 1 ;
// 正值参数初值为 0 时不再借用旧的界面范围,直接从物理边界选取搜索尺度。
if ( positiveParameter & & reference < = 0.0 ) {
if ( physicalMin > 0.0 ) {
reference = physicalMin ;
} else {
reference = qMax ( physicalMax * 0.01 , 1.0e-12 ) ;
}
}
if ( physicalMax < physicalMin ) {
return ;
}
reference = qBound ( physicalMin , reference , physicalMax ) ;
const double boundedCenterValue = qBound ( physicalMin , centerValue , physicalMax ) ;
double newMin = physicalMin ;
double newMax = physicalMax ;
if ( parameterIndex = = 1 ) {
const double skinHalfRange = 10.0 ;
newMin = physicalMin ;
newMax = qMin ( physicalMax , reference + skinHalfRange ) ;
} else if ( reference > 0.0
& & ! ( parameterIndex = = 5 & & centerValue < = 0.0 ) ) {
const double lowerFactor = 0.1 ;
const double upperFactor = 10.0 ;
newMin = qMax ( physicalMin , reference * lowerFactor ) ;
newMax = qMin ( physicalMax , reference * upperFactor ) ;
} else if ( parameterIndex = = 5 ) {
// Dfc=0 表示无限导流,不存在以零为中心的连续倍率范围。
newMin = physicalMin ;
newMax = physicalMax ;
}
// 任何自动范围都必须包含本次使用的中心值,并且不能越过物理边界。
newMin = qMin ( newMin , boundedCenterValue ) ;
newMax = qMax ( newMax , boundedCenterValue ) ;
newMin = qMax ( newMin , physicalMin ) ;
newMax = qMin ( newMax , physicalMax ) ;
if ( newMax > = newMin ) {
setParameterRange ( parameterIndex , newMin , newMax ) ;
}
}
// 按当前表格中的初值为所有参数建立建议范围,每次打开窗口和拟合完成后共用。
void nmWxAutomaticFitting : : initializeSuggestedParameterRanges ( )
{
if ( ! m_parameterTable ) {
return ;
}
for ( int parameterIndex = 0 ; parameterIndex < 7 ; + + parameterIndex ) {
QTableWidgetItem * initialItem = m_parameterTable - > item ( parameterIndex , 3 ) ;
if ( initialItem ) {
bool initialOk = false ;
const double initialValue = initialItem - > text ( ) . toDouble ( & initialOk ) ;
if ( initialOk & & nmAutoFitUiIsFinite ( initialValue ) ) {
updateRangeForParameter ( parameterIndex , initialValue ) ;
} else {
// 数据对象没有提供该初值时使用完整物理区间,不回退到旧的默认范围。
double physicalMin = 0.0 ;
double physicalMax = 0.0 ;
if ( getPhysicalParameterRange ( parameterIndex , physicalMin , physicalMax )
& & physicalMax > = physicalMin ) {
setParameterRange ( parameterIndex , physicalMin , physicalMax ) ;
}
}
}
}
}
// 校验当前表格中的参数范围; parameterIndex 为 -1 时检查所有可见参数行。
bool nmWxAutomaticFitting : : validateParameterTable ( QString & errorMessage , int parameterIndex )
{
if ( ! m_parameterTable ) {
errorMessage = tr ( " The parameter table is unavailable. " ) ;
return false ;
}
if ( parameterIndex < - 1 | | parameterIndex > = 7 ) {
errorMessage = tr ( " The parameter row is invalid. " ) ;
return false ;
}
static const char * parameterNames [ ] = {
" Permeability " , " Skin " , " Wellbore storage " , " Porosity " ,
" " , " Fracture conductivity " ,
" Fracture half length "
} ;
const int firstParameterIndex = parameterIndex < 0 ? 0 : parameterIndex ;
const int lastParameterIndex = parameterIndex < 0 ? 7 : parameterIndex + 1 ;
for ( int currentParameterIndex = firstParameterIndex ;
currentParameterIndex < lastParameterIndex ; + + currentParameterIndex ) {
// 隐藏参数不参与当前模型拟合,不用它们的历史值阻塞当前设置。
if ( m_parameterTable - > isRowHidden ( currentParameterIndex ) ) {
continue ;
}
QTableWidgetItem * minItem = m_parameterTable - > item ( currentParameterIndex , 2 ) ;
QTableWidgetItem * initialItem = m_parameterTable - > item ( currentParameterIndex , 3 ) ;
QTableWidgetItem * maxItem = m_parameterTable - > item ( currentParameterIndex , 4 ) ;
if ( ! minItem | | ! initialItem | | ! maxItem ) {
errorMessage = tr ( " The range values for %1 are incomplete. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) ) ;
return false ;
}
bool minOk = false ;
bool initialOk = false ;
bool maxOk = false ;
const double minValue = minItem - > text ( ) . toDouble ( & minOk ) ;
const double initialValue = initialItem - > text ( ) . toDouble ( & initialOk ) ;
const double maxValue = maxItem - > text ( ) . toDouble ( & maxOk ) ;
if ( ! minOk | | ! initialOk | | ! maxOk
| | ! nmAutoFitUiIsFinite ( minValue )
| | ! nmAutoFitUiIsFinite ( initialValue )
| | ! nmAutoFitUiIsFinite ( maxValue ) ) {
errorMessage = tr ( " The minimum value, initial value, and maximum value of %1 must be finite numbers. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) ) ;
return false ;
}
double physicalMin = 0.0 ;
double physicalMax = 0.0 ;
if ( ! getPhysicalParameterRange ( currentParameterIndex , physicalMin , physicalMax )
| | ! nmAutoFitUiIsFinite ( physicalMin ) | | ! nmAutoFitUiIsFinite ( physicalMax )
| | physicalMax < physicalMin ) {
errorMessage = tr ( " The physical range of %1 is invalid. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) ) ;
return false ;
}
if ( ( minValue < physicalMin & & ! nmAutoFitUiNearlyEqual ( minValue , physicalMin ) )
| | ( minValue > physicalMax & & ! nmAutoFitUiNearlyEqual ( minValue , physicalMax ) )
| | ( initialValue < physicalMin & & ! nmAutoFitUiNearlyEqual ( initialValue , physicalMin ) )
| | ( initialValue > physicalMax & & ! nmAutoFitUiNearlyEqual ( initialValue , physicalMax ) )
| | ( maxValue < physicalMin & & ! nmAutoFitUiNearlyEqual ( maxValue , physicalMin ) )
| | ( maxValue > physicalMax & & ! nmAutoFitUiNearlyEqual ( maxValue , physicalMax ) ) ) {
errorMessage = tr ( " The values of %1 exceed the physical range [%2, %3]. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) )
. arg ( QString : : number ( physicalMin , ' g ' , 10 ) )
. arg ( QString : : number ( physicalMax , ' g ' , 10 ) ) ;
return false ;
}
if ( minValue > maxValue | | minValue > initialValue | | initialValue > maxValue ) {
errorMessage = tr ( " The values of %1 must satisfy: minimum <= initial value <= maximum. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) ) ;
return false ;
}
// 底层用 Dfc=0 表示无限导流,这是离散模型选项,不属于有限导流拟合域。
if ( currentParameterIndex = = 5 & & m_dfcCheckBox - > isChecked ( )
& & ( minValue < = 1.0e-10 | | initialValue < = 1.0e-10 ) ) {
errorMessage = tr ( " The minimum value of %1 must be greater than zero for automatic fitting. " )
. arg ( tr ( parameterNames [ currentParameterIndex ] ) ) ;
return false ;
}
}
return true ;
}
nmWxAutomaticFitting : : nmWxAutomaticFitting ( QWidget * parent , nmSensitivityFitSettings * settings ,
const QStringList & sampledParameters )
: iDlgBase ( parent )
, m_sensitivitySettings ( settings )
, m_sampledParameters ( sampledParameters )
, m_autoFitterPSO ( nullptr )
, m_autoFitterLM ( nullptr )
, m_progressDialog ( nullptr )
, m_progressTimer ( nullptr )
, m_progressMonitor ( nullptr )
, m_autoParameterRanges ( true )
// 构造期间先禁止即时校验,避免初始值写入和范围生成之间出现短暂的不一致。
, m_updatingParameterRanges ( true )
{
DEBUG_UI ( QString ( " AutoFitting Constructor: this=0x%1 " ) . arg ( ( quintptr ) this , 0 , 16 ) ) ;
// 获取数据
nmDataAnalyzeManager * pManager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
reservoirData = pManager - > getReservoirDataCopy ( ) ;
automaticFittingData = pManager - > getAutomaticFittingDataCopy ( ) ;
// 自动范围始终开启; 用户在表格中修改上下限后, itemChanged 会临时切换为手工范围。
m_autoParameterRanges = true ;
setupUI ( ) ;
// 敏感性入口使用“确定”保存设置,普通入口仍直接开始拟合。
if ( m_sensitivitySettings ) m_okBtn - > setText ( tr ( " OK " ) ) ;
setWindowTitle ( tr ( " Automatic fitting " ) ) ;
setModal ( true ) ;
// 初始尺寸随所在屏幕变化,最小尺寸交给实际控件布局决定。
resize ( sizeHint ( ) ) ;
if ( m_targetWellCombo - > count ( ) > 0 ) {
onWellSelected ( 0 ) ; // 默认选中第一口井
}
// 每次打开都按当前模型参数生成范围,不沿用上次的上下限。
initializeSuggestedParameterRanges ( ) ;
if ( m_sensitivitySettings & & m_sensitivitySettings - > confirmed ) loadSensitivitySettings ( ) ;
m_updatingParameterRanges = false ;
DEBUG_UI ( " AutoFitting Constructor completed " ) ;
}
void nmWxAutomaticFitting : : loadSensitivitySettings ( )
{
// 先恢复目标井,再覆盖默认范围与初值,保留上次确认的完整设置。
m_targetWellCombo - > setCurrentIndex ( m_targetWellCombo - > findText ( m_sensitivitySettings - > targetWell ) ) ;
automaticFittingData = m_sensitivitySettings - > parameters ;
m_autoParameterRanges = false ;
m_algorithmCombo - > setCurrentIndex ( automaticFittingData . getAlgorithmName ( ) = = " LM " ? 1 : 0 ) ;
m_iterationEdit - > setText ( QString : : number ( automaticFittingData . getIterationCount ( ) . getValue ( ) . toInt ( ) ) ) ;
m_errorLimitEdit - > setText ( QString : : number ( automaticFittingData . getErrorTolerance ( ) . getValue ( ) . toDouble ( ) , ' g ' , 10 ) ) ;
const int rows [ ] = { 0 , 1 , 2 , 3 , 5 , 6 } ;
QCheckBox * checks [ ] = { m_kCheckBox , m_sCheckBox , m_cCheckBox , m_phiCheckBox , m_dfcCheckBox , m_fractureHalfLengthCheckBox } ;
const bool selected [ ] = { automaticFittingData . getPermeabilitySelected ( ) , automaticFittingData . getSkinSelected ( ) ,
automaticFittingData . getWellboreStorageSelected ( ) , automaticFittingData . getPorositySelected ( ) ,
automaticFittingData . getFractureConductivitySelected ( ) , automaticFittingData . getFractureHalfLengthSelected ( ) } ;
nmDataAttribute * lower [ ] = { & automaticFittingData . getPermeabilityMin ( ) , & automaticFittingData . getSkinMin ( ) ,
& automaticFittingData . getWellboreStorageMin ( ) , & automaticFittingData . getPorosityMin ( ) ,
& automaticFittingData . getFractureConductivityMin ( ) , & automaticFittingData . getFractureHalfLengthMin ( ) } ;
nmDataAttribute * upper [ ] = { & automaticFittingData . getPermeabilityMax ( ) , & automaticFittingData . getSkinMax ( ) ,
& automaticFittingData . getWellboreStorageMax ( ) , & automaticFittingData . getPorosityMax ( ) ,
& automaticFittingData . getFractureConductivityMax ( ) , & automaticFittingData . getFractureHalfLengthMax ( ) } ;
for ( int i = 0 ; i < 6 ; + + i ) {
checks [ i ] - > setChecked ( selected [ i ] & & checks [ i ] - > isEnabled ( ) ) ;
setParameterRange ( rows [ i ] , lower [ i ] - > getValue ( ) . toDouble ( ) , upper [ i ] - > getValue ( ) . toDouble ( ) ) ;
m_parameterTable - > item ( rows [ i ] , 3 ) - > setText ( QString : : number ( m_sensitivitySettings - > initialValues [ i ] , ' g ' , 10 ) ) ;
}
}
QSize nmWxAutomaticFitting : : sizeHint ( ) const
{
// 沿用项目的可用屏幕尺寸计算方式,优先取父窗口所在屏幕,支持多显示器。
QDesktopWidget * desktop = QApplication : : desktop ( ) ;
if ( desktop = = nullptr ) return iDlgBase : : sizeHint ( ) ;
const QRect available = desktop - > availableGeometry ( parentWidget ( ) ? parentWidget ( ) - > window ( ) : this ) ;
return QSize ( available . width ( ) * 45 / 100 , available . height ( ) * 55 / 100 ) ;
}
nmWxAutomaticFitting : : ~ nmWxAutomaticFitting ( )
{
DEBUG_UI ( QString ( " AutoFitting Destructor: this=0x%1 " ) . arg ( ( quintptr ) this , 0 , 16 ) ) ;
DEBUG_UI ( " AutoFitting destructor - starting cleanup " ) ;
// 停止定时器
if ( m_progressTimer ) {
m_progressTimer - > stop ( ) ;
}
// 断开所有连接,避免悬空指针
if ( m_autoFitterPSO ) {
disconnect ( m_autoFitterPSO , nullptr , this , nullptr ) ;
}
if ( m_autoFitterLM ) {
disconnect ( m_autoFitterLM , nullptr , this , nullptr ) ;
}
DEBUG_UI ( " AutoFitting destructor - completed " ) ;
}
void nmWxAutomaticFitting : : setupUI ( )
{
// 用浅底色区分白色设置区和参数区,避免所有内容铺在同一平面。
setObjectName ( " autoFittingSettings " ) ;
setFont ( QFont ( " Microsoft YaHei " , 9 ) ) ;
setStyleSheet (
" #autoFittingSettings { background: #f3f5f8; color: #273449; } "
" #autoFittingSettings QFrame#fittingControls, #autoFittingSettings QFrame#fittingParameters { background: #ffffff; border: 1px solid #dce2ea; border-radius: 7px; } "
" #autoFittingSettings QLabel { color: #667487; background: transparent; border: none; } "
" #autoFittingSettings QLabel#parameterHeading { color: #273449; font-weight: bold; } "
" #autoFittingSettings QLineEdit, #autoFittingSettings QComboBox { background: #ffffff; color: #273449; border: 1px solid #cfd7e2; border-radius: 4px; padding: 5px 8px; min-height: 20px; selection-background-color: #dceafb; selection-color: #273449; } "
" #autoFittingSettings QLineEdit:focus, #autoFittingSettings QComboBox:focus { border-color: #3978c6; } "
" #autoFittingSettings QComboBox::drop-down { border: none; width: 24px; } "
" #autoFittingSettings QComboBox QAbstractItemView { background: #ffffff; color: #273449; selection-background-color: #eaf1fb; selection-color: #273449; } "
" #autoFittingSettings QTableWidget { border: none; background: #ffffff; color: #273449; selection-background-color: #eaf1fb; selection-color: #273449; } "
" #autoFittingSettings QHeaderView::section { background: #f5f7fa; color: #667487; border: none; border-bottom: 1px solid #e5eaf1; padding: 7px; } "
" #autoFittingSettings QTableWidget::item { border-bottom: 1px solid #eef1f5; padding: 5px 8px; } "
" #autoFittingSettings QCheckBox { color: #273449; background: transparent; padding-left: 10px; spacing: 8px; } "
" #autoFittingSettings QPushButton { background: #ffffff; color: #46556b; border: 1px solid #cfd7e2; border-radius: 4px; padding: 6px 18px; min-width: 64px; min-height: 20px; } "
" #autoFittingSettings QPushButton:hover { background: #edf3fc; border-color: #9bb7db; } "
" #autoFittingSettings QPushButton:pressed { background: #dce8f8; } "
" #autoFittingSettings QPushButton#startFitting { background: #3978c6; color: #ffffff; border-color: #3978c6; } "
" #autoFittingSettings QPushButton#startFitting:hover { background: #306bb5; } "
" #autoFittingSettings QPushButton#startFitting:pressed { background: #285b9c; } " ) ;
m_mainLayout = new QVBoxLayout ( this ) ;
m_mainLayout - > setContentsMargins ( 16 , 16 , 16 , 14 ) ;
m_mainLayout - > setSpacing ( 12 ) ;
setupControlPanel ( ) ;
QFrame * controlsPanel = new QFrame ( this ) ;
controlsPanel - > setObjectName ( " fittingControls " ) ;
m_controlLayout - > setContentsMargins ( 16 , 14 , 16 , 14 ) ;
controlsPanel - > setLayout ( m_controlLayout ) ;
m_mainLayout - > addWidget ( controlsPanel ) ;
setupParameterTable ( ) ;
QFrame * tablePanel = new QFrame ( this ) ;
tablePanel - > setObjectName ( " fittingParameters " ) ;
QVBoxLayout * tableLayout = new QVBoxLayout ( tablePanel ) ;
tableLayout - > setContentsMargins ( 12 , 12 , 12 , 8 ) ;
tableLayout - > setSpacing ( 10 ) ;
QLabel * tableHeading = new QLabel ( tr ( " Fitting parameters " ) , tablePanel ) ;
tableHeading - > setObjectName ( " parameterHeading " ) ;
tableLayout - > addWidget ( tableHeading ) ;
tableLayout - > addWidget ( m_parameterTable , 1 ) ;
m_mainLayout - > addWidget ( tablePanel , 1 ) ;
setupButtons ( ) ;
setLayout ( m_mainLayout ) ;
}
void nmWxAutomaticFitting : : setupParameterTable ( )
{
// 敏感性设置首次打开时不预选参数,普通拟合仍默认全选,再按井型隐藏不可用参数。
m_parameterTable = new QTableWidget ( 7 , 6 , this ) ;
// 设置表头
QStringList headers ;
headers < < " " < < tr ( " Parameter " ) < < tr ( " Min " ) < < tr ( " Initial value " ) < < tr ( " Max " ) < < tr ( " Unit " ) ;
m_parameterTable - > setHorizontalHeaderLabels ( headers ) ;
// 设置表格属性
m_parameterTable - > setSelectionBehavior ( QAbstractItemView : : SelectItems ) ;
m_parameterTable - > setAlternatingRowColors ( false ) ;
m_parameterTable - > setShowGrid ( false ) ;
m_parameterTable - > verticalHeader ( ) - > setDefaultSectionSize ( 36 ) ;
m_parameterTable - > horizontalHeader ( ) - > setDefaultAlignment ( Qt : : AlignLeft | Qt : : AlignVCenter ) ;
m_parameterTable - > verticalHeader ( ) - > setVisible ( false ) ;
// 设置列宽
QHeaderView * header = m_parameterTable - > horizontalHeader ( ) ;
header - > setResizeMode ( 0 , QHeaderView : : Fixed ) ; // 序号列固定宽度
header - > setResizeMode ( 1 , QHeaderView : : Fixed ) ; // 参数列固定宽度
header - > setResizeMode ( 2 , QHeaderView : : Stretch ) ; // 其他列自适应
header - > setResizeMode ( 3 , QHeaderView : : Stretch ) ;
header - > setResizeMode ( 4 , QHeaderView : : Stretch ) ;
header - > setResizeMode ( 5 , QHeaderView : : Fixed ) ; // 单位列固定宽度
// 设置固定列的宽度
m_parameterTable - > setColumnWidth ( 0 , 40 ) ; // 序号列宽度
m_parameterTable - > setColumnWidth ( 1 , 180 ) ; // 参数名称与复选框保留足够空间
m_parameterTable - > setColumnWidth ( 5 , 80 ) ; // 单位列宽度
// 只改变视觉顺序,保留数据读写使用的原列索引。
m_parameterTable - > setColumnHidden ( 0 , true ) ;
header - > moveSection ( header - > visualIndex ( 3 ) , header - > visualIndex ( 2 ) ) ;
// 设置表格的大小策略
m_parameterTable - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Expanding ) ;
// 渗透率 (Permeability)
m_parameterTable - > setItem ( 0 , 0 , new QTableWidgetItem ( " 1 " ) ) ;
m_kCheckBox = new QCheckBox ( tr ( " Permeability " ) ) ;
m_kCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 0 , 1 , m_kCheckBox ) ;
m_parameterTable - > setItem ( 0 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getPermeabilityMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 0 , 3 , new QTableWidgetItem ( QString : : number ( reservoirData . getPermeability ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 0 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getPermeabilityMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 0 , 5 , new QTableWidgetItem ( tr ( " mD " ) ) ) ;
// 表皮系数 (Skin)
m_parameterTable - > setItem ( 1 , 0 , new QTableWidgetItem ( " 2 " ) ) ;
m_sCheckBox = new QCheckBox ( tr ( " Skin " ) ) ;
m_sCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 1 , 1 , m_sCheckBox ) ;
m_parameterTable - > setItem ( 1 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getSkinMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 1 , 3 , new QTableWidgetItem ( ) ) ;
m_parameterTable - > setItem ( 1 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getSkinMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 1 , 5 , new QTableWidgetItem ( " " ) ) ;
// 井筒储集系数 (Wellbore storage)
m_parameterTable - > setItem ( 2 , 0 , new QTableWidgetItem ( " 3 " ) ) ;
m_cCheckBox = new QCheckBox ( tr ( " Wellbore storage " ) ) ;
m_cCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 2 , 1 , m_cCheckBox ) ;
m_parameterTable - > setItem ( 2 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getWellboreStorageMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 2 , 3 , new QTableWidgetItem ( ) ) ;
m_parameterTable - > setItem ( 2 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getWellboreStorageMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 2 , 5 , new QTableWidgetItem ( tr ( " m^3/MPa " ) ) ) ;
// 孔隙度 (Porosity)
m_parameterTable - > setItem ( 3 , 0 , new QTableWidgetItem ( " 4 " ) ) ;
m_phiCheckBox = new QCheckBox ( tr ( " Porosity " ) ) ;
m_phiCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 3 , 1 , m_phiCheckBox ) ;
m_parameterTable - > setItem ( 3 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getPorosityMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 3 , 3 , new QTableWidgetItem ( QString : : number ( reservoirData . getPorosity ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 3 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getPorosityMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 3 , 5 , new QTableWidgetItem ( " " ) ) ;
// 索引 4 留空,保持裂缝参数与优化器的现有索引一致。
// 裂缝导流能力 (Dfc)。该行只对压裂井显示,初值在 onWellSelected() 中
// 从当前目标井读取,其他裂缝几何参数保持固定,不进入自动拟合。
m_parameterTable - > setItem ( 5 , 0 , new QTableWidgetItem ( " 6 " ) ) ;
m_dfcCheckBox = new QCheckBox ( tr ( " Fracture conductivity " ) ) ;
m_dfcCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 5 , 1 , m_dfcCheckBox ) ;
m_parameterTable - > setItem ( 5 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getFractureConductivityMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 5 , 3 , new QTableWidgetItem ( ) ) ;
m_parameterTable - > setItem ( 5 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getFractureConductivityMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 5 , 5 , new QTableWidgetItem ( tr ( " md.m " ) ) ) ;
// 裂缝半长。该行与 Dfc 一样只对压裂井显示,初值从当前目标井读取。
m_parameterTable - > setItem ( 6 , 0 , new QTableWidgetItem ( " 7 " ) ) ;
m_fractureHalfLengthCheckBox = new QCheckBox ( tr ( " Fracture half length " ) ) ;
m_fractureHalfLengthCheckBox - > setChecked ( ! m_sensitivitySettings ) ;
m_parameterTable - > setCellWidget ( 6 , 1 , m_fractureHalfLengthCheckBox ) ;
m_parameterTable - > setItem ( 6 , 2 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getFractureHalfLengthMin ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 6 , 3 , new QTableWidgetItem ( ) ) ;
m_parameterTable - > setItem ( 6 , 4 , new QTableWidgetItem ( QString : : number ( automaticFittingData . getFractureHalfLengthMax ( ) . getValue ( ) . toDouble ( ) ) ) ) ;
m_parameterTable - > setItem ( 6 , 5 , new QTableWidgetItem ( tr ( " m " ) ) ) ;
// 设置表格行为
for ( int i = 0 ; i < m_parameterTable - > rowCount ( ) ; + + i ) {
for ( int j = 0 ; j < 6 ; + + j ) {
QTableWidgetItem * item = m_parameterTable - > item ( i , j ) ;
if ( item ) {
if ( j = = 0 | | j = = 5 ) { // 序号列和单位列只读
item - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsSelectable ) ;
} else if ( j > = 2 ) {
item - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsEditable | Qt : : ItemIsSelectable ) ;
} else { // 参数列(复选框)不可选择
item - > setFlags ( Qt : : NoItemFlags ) ;
}
}
}
}
// 序号列居中对齐
for ( int i = 0 ; i < m_parameterTable - > rowCount ( ) ; + + i ) {
QTableWidgetItem * item = m_parameterTable - > item ( i , 0 ) ;
if ( item ) {
item - > setTextAlignment ( Qt : : AlignCenter ) ;
}
}
nmDataAnalyzeManager * pManager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( pManager ) {
updateParameterVisibility ( m_parameterTable , pManager - > getSolverModelType ( ) ) ;
}
// 连接选择改变信号
connect ( m_parameterTable , SIGNAL ( currentCellChanged ( int , int , int , int ) ) ,
this , SLOT ( onCellSelectionChanged ( int , int , int , int ) ) ) ;
connect ( m_parameterTable , SIGNAL ( itemChanged ( QTableWidgetItem * ) ) ,
this , SLOT ( onParameterTableItemChanged ( QTableWidgetItem * ) ) ) ;
}
void nmWxAutomaticFitting : : setupControlPanel ( )
{
// 四列设置:目标井、算法、目标误差、迭代上限。
m_controlLayout = new QGridLayout ( ) ;
m_controlLayout - > setHorizontalSpacing ( 16 ) ;
m_controlLayout - > setVerticalSpacing ( 6 ) ;
// 算法选择
QLabel * algorithmLabel = new QLabel ( tr ( " Algorithm: " ) ) ;
m_algorithmCombo = new QComboBox ( ) ;
m_algorithmCombo - > addItem ( tr ( " PSO (Particle Swarm) " ) ) ;
m_algorithmCombo - > addItem ( tr ( " LM " ) ) ;
m_algorithmCombo - > setCurrentIndex ( 0 ) ;
connect ( m_algorithmCombo , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( onAlgorithmChanged ( int ) ) ) ;
// 迭代次数
m_iterationLabel = new QLabel ( ) ;
m_iterationEdit = new QLineEdit ( QString : : number ( automaticFittingData . getIterationCount ( ) . getValue ( ) . toInt ( ) ) ) ;
// 误差上限
QLabel * errorLabel = new QLabel ( tr ( " Target error: " ) ) ;
m_errorLimitEdit = new QLineEdit ( QString : : number ( automaticFittingData . getErrorTolerance ( ) . getValue ( ) . toDouble ( ) ) ) ;
// 目标井选择
QLabel * wellLabel = new QLabel ( tr ( " Target Well: " ) ) ;
m_targetWellCombo = new QComboBox ( ) ;
// 目标井先加入当前分析主井,再加入已勾选且具备流量、压力数据的包含井。
nmDataAnalyzeManager * pManager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( pManager ! = nullptr ) {
const QString sPrimaryWellCode = pManager - > getPrimaryWellCode ( ) ;
nmDataWellBase * pPrimaryWell = pManager - > findWellByCode ( sPrimaryWellCode ) ;
if ( pPrimaryWell ! = nullptr ) {
m_targetWellCombo - > addItem ( pPrimaryWell - > getWellName ( ) ) ;
}
if ( pManager - > getIncludeOtherWells ( ) ) {
const QVector < nmCalculationWellRef > vecIncludedWells =
pManager - > getIncludedCalculationWells ( ) ;
for ( int nIndex = 0 ; nIndex < vecIncludedWells . size ( ) ; + + nIndex ) {
const QString & sWellCode = vecIncludedWells [ nIndex ] . m_sWellCode ;
nmDataWellBase * pWellData = pManager - > findWellByCode ( sWellCode ) ;
if ( pWellData = = nullptr | | sWellCode = = sPrimaryWellCode | |
pWellData - > getFlowSegmentCount ( ) < = 0 | |
pWellData - > getPressurePoints ( ) . size ( ) < 2 ) {
continue ;
}
m_targetWellCombo - > addItem ( pWellData - > getWellName ( ) ) ;
}
}
}
connect ( m_targetWellCombo , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( onWellSelected ( int ) ) ) ;
m_targetWellCombo - > setSizeAdjustPolicy ( QComboBox : : AdjustToMinimumContentsLengthWithIcon ) ;
m_targetWellCombo - > setMinimumContentsLength ( 10 ) ;
m_controlLayout - > addWidget ( wellLabel , 0 , 0 ) ;
m_controlLayout - > addWidget ( m_targetWellCombo , 1 , 0 ) ;
m_controlLayout - > addWidget ( algorithmLabel , 0 , 1 ) ;
m_controlLayout - > addWidget ( m_algorithmCombo , 1 , 1 ) ;
m_controlLayout - > addWidget ( errorLabel , 0 , 2 ) ;
m_controlLayout - > addWidget ( m_errorLimitEdit , 1 , 2 ) ;
m_controlLayout - > addWidget ( m_iterationLabel , 0 , 3 ) ;
m_controlLayout - > addWidget ( m_iterationEdit , 1 , 3 ) ;
for ( int column = 0 ; column < 4 ; + + column ) m_controlLayout - > setColumnStretch ( column , 1 ) ;
onAlgorithmChanged ( m_algorithmCombo - > currentIndex ( ) ) ;
}
void nmWxAutomaticFitting : : onAlgorithmChanged ( int index )
{
// 两种算法统一显示最大迭代次数,提示仍说明 LM 共用次数的含义。
const bool usePSO = ( index = = 0 ) ;
m_iterationLabel - > setText ( tr ( " Maximum iterations: " ) ) ;
m_iterationEdit - > setToolTip ( usePSO ? QString ( ) : tr ( " Height, shape and total-error adjustments share this iteration limit; one iteration may call the solver multiple times. " ) ) ;
}
void nmWxAutomaticFitting : : setupButtons ( )
{
QHBoxLayout * buttonLayout = new QHBoxLayout ( ) ;
m_reverseBtn = new QPushButton ( tr ( " Reverse Selection " ) ) ;
m_okBtn = new QPushButton ( tr ( " Start fitting " ) ) ;
m_okBtn - > setObjectName ( " startFitting " ) ;
m_okBtn - > setDefault ( true ) ;
m_cancelBtn = new QPushButton ( tr ( " Cancel " ) ) ;
// 设置按钮大小
m_reverseBtn - > setMinimumWidth ( 120 ) ;
m_okBtn - > setMinimumWidth ( 80 ) ;
m_cancelBtn - > setMinimumWidth ( 80 ) ;
buttonLayout - > addWidget ( m_reverseBtn ) ;
buttonLayout - > addStretch ( ) ;
buttonLayout - > addWidget ( m_cancelBtn ) ;
buttonLayout - > addWidget ( m_okBtn ) ;
m_mainLayout - > addLayout ( buttonLayout ) ;
// 连接信号槽
connect ( m_reverseBtn , SIGNAL ( clicked ( ) ) , this , SLOT ( onReverseSelection ( ) ) ) ;
connect ( m_okBtn , SIGNAL ( clicked ( ) ) , this , SLOT ( onAccept ( ) ) ) ;
connect ( m_cancelBtn , SIGNAL ( clicked ( ) ) , this , SLOT ( onReject ( ) ) ) ;
}
void nmWxAutomaticFitting : : onCellSelectionChanged ( int currentRow , int currentColumn , int previousRow , int previousColumn )
{
if ( ! m_parameterTable ) return ;
QHeaderView * header = m_parameterTable - > horizontalHeader ( ) ;
if ( ! header ) return ;
// 重置所有列头的字体为正常
for ( int i = 0 ; i < m_parameterTable - > columnCount ( ) ; + + i ) {
QFont font = header - > font ( ) ;
font . setBold ( false ) ;
if ( m_parameterTable - > horizontalHeaderItem ( i ) ) {
m_parameterTable - > horizontalHeaderItem ( i ) - > setFont ( font ) ;
}
}
// 设置当前列的表头为粗体
if ( currentColumn > = 0 & & currentColumn < m_parameterTable - > columnCount ( ) ) {
QFont font = header - > font ( ) ;
font . setBold ( true ) ;
if ( m_parameterTable - > horizontalHeaderItem ( currentColumn ) ) {
m_parameterTable - > horizontalHeaderItem ( currentColumn ) - > setFont ( font ) ;
}
}
header - > update ( ) ;
}
void nmWxAutomaticFitting : : onReverseSelection ( )
{
// 只反选可用参数,不能通过反选勾上已参与采样的参数。
if ( ! m_parameterTable ) {
return ;
}
if ( m_kCheckBox - > isEnabled ( ) ) m_kCheckBox - > setChecked ( ! m_kCheckBox - > isChecked ( ) ) ;
if ( m_sCheckBox - > isEnabled ( ) ) m_sCheckBox - > setChecked ( ! m_sCheckBox - > isChecked ( ) ) ;
if ( m_cCheckBox - > isEnabled ( ) ) m_cCheckBox - > setChecked ( ! m_cCheckBox - > isChecked ( ) ) ;
if ( m_phiCheckBox - > isEnabled ( ) ) m_phiCheckBox - > setChecked ( ! m_phiCheckBox - > isChecked ( ) ) ;
if ( m_dfcCheckBox - > isEnabled ( ) ) m_dfcCheckBox - > setChecked ( ! m_dfcCheckBox - > isChecked ( ) ) ;
if ( m_fractureHalfLengthCheckBox - > isEnabled ( ) ) m_fractureHalfLengthCheckBox - > setChecked ( ! m_fractureHalfLengthCheckBox - > isChecked ( ) ) ;
}
void nmWxAutomaticFitting : : onParameterTableItemChanged ( QTableWidgetItem * item )
{
if ( ! item | | m_updatingParameterRanges ) {
return ;
}
// 手工范围用于本轮拟合,切井时保留;拟合完成后再按最新参数自动生成范围。
if ( item - > column ( ) = = 2 | | item - > column ( ) = = 4 ) {
m_autoParameterRanges = false ;
}
if ( item - > column ( ) > = 2 & & item - > column ( ) < = 4
& & ! m_parameterTable - > isRowHidden ( item - > row ( ) ) ) {
QString validationError ;
if ( ! validateParameterTable ( validationError , item - > row ( ) ) ) {
// 表格编辑提交后立即提示,用户不需要先点击“确定”才发现错误。
QMessageBox : : warning ( this , tr ( " Invalid parameter range " ) , validationError ) ;
}
}
}
void nmWxAutomaticFitting : : onAccept ( )
{
QString validationError ;
if ( ! validateParameterTable ( validationError ) ) {
QMessageBox : : warning ( this , tr ( " Invalid parameter range " ) , validationError ) ;
return ;
}
// 校验通过后再保存参数设置,非法输入不会被静默裁剪。
setAutomaticFittingValue ( ) ;
if ( m_sensitivitySettings ) {
// 仅提交给敏感性窗口;取消、关闭及普通拟合均不经过此分支。
m_sensitivitySettings - > parameters = automaticFittingData ;
m_sensitivitySettings - > targetWell = m_targetWellCombo - > currentText ( ) ;
m_sensitivitySettings - > initialValues . clear ( ) ;
const int rows [ ] = { 0 , 1 , 2 , 3 , 5 , 6 } ;
for ( int i = 0 ; i < 6 ; + + i )
m_sensitivitySettings - > initialValues . append ( m_parameterTable - > item ( rows [ i ] , 3 ) - > text ( ) . toDouble ( ) ) ;
m_sensitivitySettings - > confirmed = true ;
accept ( ) ;
return ;
}
// 更新完成后,通知参数界面刷新
nmWxParameterProperty : : notifyUpdateTable ( ) ;
// 获取目标井的双对数历史数据
QString selectedWellName = m_targetWellCombo - > currentText ( ) ;
if ( selectedWellName . isEmpty ( ) ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Please select a target well! " ) ) ;
return ;
}
nmDataAnalyzeManager * pManager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( ! pManager ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Data manager is unavailable! " ) ) ;
return ;
}
// 从数据管理器获取最新的井对象
nmDataWellBase * pTargetWell = pManager - > findWellByName ( selectedWellName ) ;
if ( ! pTargetWell ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Target well not found! " ) ) ;
return ;
}
const bool usePseudoPressure = ( pManager - > getSolverModelType ( ) = = SMT_Gas_VariablePvt ) ;
if ( usePseudoPressure ) {
iSubWndFitting * pFitting = nmDataAnalyzeManager : : getCurrentFitting ( ) ;
iAnalRun * pAnalRun = pFitting ? pFitting - > getAnalRun ( ) : nullptr ;
// 气井求解结果的后处理仍需初始化当前分析的拟压力转换器。
if ( ! pAnalRun
| | ! pAnalRun - > configPsAbouts ( true ,
pFitting - > getModelOption ( ) ,
false ,
pFitting - > getAllWxPtr ( ) ) ) {
QMessageBox : : warning ( this , tr ( " Warning " ) ,
tr ( " Gas pseudo-pressure data is unavailable or invalid. " ) ) ;
return ;
}
}
// 开始拟合前重算气井目标,避免旧缓存或已修改的拟压力配置造成量纲不一致。
if ( usePseudoPressure & & ! pManager - > updateWellHistoryData ( pTargetWell ) ) {
QMessageBox : : warning ( this , tr ( " Warning " ) ,
tr ( " Gas pseudo-pressure data is unavailable or invalid. " ) ) ;
return ;
}
// 油、气、水井统一读取所选目标井的历史曲线,不依赖主界面显示的结果井。
QVector < QVector < double > > targetLogLogData = pTargetWell - > getHistoryLogLog ( ) ;
if ( targetLogLogData . isEmpty ( ) | | targetLogLogData . size ( ) < 3 ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Target well has no LogLog history data! " ) ) ;
return ;
}
// 检查双对数数据的一致性
if ( targetLogLogData [ 0 ] . size ( ) ! = targetLogLogData [ 1 ] . size ( ) | |
targetLogLogData [ 0 ] . size ( ) ! = targetLogLogData [ 2 ] . size ( ) ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Target well LogLog data is inconsistent! " ) ) ;
return ;
}
// 检查结果双对数数据
QVector < QVector < double > > rstLogLogData = pTargetWell - > getResultLogLog ( ) ;
if ( rstLogLogData . isEmpty ( ) | | rstLogLogData . size ( ) < 3 ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Target well has no LogLog result data! " ) ) ;
return ;
}
// 检查是否有参数被选中
bool hasSelectedParams = m_kCheckBox - > isChecked ( ) | | m_sCheckBox - > isChecked ( ) | |
m_cCheckBox - > isChecked ( ) | | m_phiCheckBox - > isChecked ( ) | |
m_dfcCheckBox - > isChecked ( ) | |
m_fractureHalfLengthCheckBox - > isChecked ( ) ;
if ( ! hasSelectedParams ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Please select at least one parameter for optimization! " ) ) ;
return ;
}
// 收集选中的参数名称
QStringList selectedParameterNames ;
if ( m_kCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Permeability " ) ;
if ( m_sCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Skin " ) ;
if ( m_cCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Wellbore storage " ) ;
if ( m_phiCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Porosity " ) ;
if ( m_dfcCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Fracture conductivity " ) ;
if ( m_fractureHalfLengthCheckBox - > isChecked ( ) ) selectedParameterNames < < tr ( " Fracture half length " ) ;
// 启动自动拟合 - 传递双对数历史数据
startAutoFitting ( targetLogLogData , selectedParameterNames , selectedWellName ) ;
}
void nmWxAutomaticFitting : : onReject ( )
{
reject ( ) ;
}
void nmWxAutomaticFitting : : onWellSelected ( int index )
{
// 每次都读取模型实际保留的井参数,避免拟合结束后切井又恢复到打开窗口时的旧副本。
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( ! manager ) {
return ;
}
nmDataWellBase * targetWell = manager - > findWellByName ( m_targetWellCombo - > itemText ( index ) ) ;
if ( ! targetWell ) {
return ;
}
const bool wasUpdatingRanges = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
updateParameterVisibility ( m_parameterTable , manager - > getSolverModelType ( ) ) ;
nmDataPerforation * perforation = targetWell - > getPerforation ( 0 ) ;
if ( perforation ) {
const double skin = perforation - > getSkin ( ) . getValue ( ) . toDouble ( ) ;
m_parameterTable - > item ( 1 , 3 ) - > setText ( QString : : number ( skin , ' g ' , 10 ) ) ;
if ( m_autoParameterRanges ) {
updateRangeForParameter ( 1 , skin ) ;
}
}
const double storage = targetWell - > getWellboreStorage ( ) . getValue ( ) . toDouble ( ) ;
m_parameterTable - > item ( 2 , 3 ) - > setText ( QString : : number ( storage , ' g ' , 10 ) ) ;
if ( m_autoParameterRanges ) {
updateRangeForParameter ( 2 , storage ) ;
}
bool fracturedWell = false ;
double conductivity = 0.0 ;
double halfLength = 0.0 ;
if ( nmDataVerticalFracturedWell * well = dynamic_cast < nmDataVerticalFracturedWell * > ( targetWell ) ) {
conductivity = well - > getDfc ( ) . getValue ( ) . toDouble ( ) ;
halfLength = well - > getFractureHalfLength ( ) . getValue ( ) . toDouble ( ) ;
fracturedWell = true ;
} else if ( nmDataHorizontalFracturedWell * well = dynamic_cast < nmDataHorizontalFracturedWell * > ( targetWell ) ) {
conductivity = well - > getDfc ( ) . getValue ( ) . toDouble ( ) ;
halfLength = well - > getFractureHalfLength ( ) . getValue ( ) . toDouble ( ) ;
fracturedWell = true ;
}
if ( fracturedWell ) {
m_parameterTable - > item ( 5 , 3 ) - > setText ( QString : : number ( conductivity , ' g ' , 10 ) ) ;
m_parameterTable - > item ( 6 , 3 ) - > setText ( QString : : number ( halfLength , ' g ' , 10 ) ) ;
if ( m_autoParameterRanges ) {
updateRangeForParameter ( 5 , conductivity ) ;
updateRangeForParameter ( 6 , halfLength ) ;
}
}
m_updatingParameterRanges = wasUpdatingRanges ;
}
void nmWxAutomaticFitting : : setAutomaticFittingValue ( )
{
// 保存剩余拟合参数的选择状态, LM 和 PSO 共用此配置。
automaticFittingData . setPermeabilitySelected ( m_kCheckBox - > isChecked ( ) ) ;
automaticFittingData . setSkinSelected ( m_sCheckBox - > isChecked ( ) ) ;
automaticFittingData . setWellboreStorageSelected ( m_cCheckBox - > isChecked ( ) ) ;
automaticFittingData . setPorositySelected ( m_phiCheckBox - > isChecked ( ) ) ;
automaticFittingData . setFractureConductivitySelected ( m_dfcCheckBox - > isChecked ( ) ) ;
automaticFittingData . setFractureHalfLengthSelected ( m_fractureHalfLengthCheckBox - > isChecked ( ) ) ;
// 界面不再提供 PSO 加速选项,同时关闭旧配置中可能保留的加速开关。
automaticFittingData . setSurrogateScreeningEnabled ( false ) ;
// 保存渗透率的最小值和最大值
automaticFittingData . getPermeabilityMin ( ) . setValue ( m_parameterTable - > item ( 0 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getPermeabilityMax ( ) . setValue ( m_parameterTable - > item ( 0 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存表皮系数的最小值和最大值
automaticFittingData . getSkinMin ( ) . setValue ( m_parameterTable - > item ( 1 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getSkinMax ( ) . setValue ( m_parameterTable - > item ( 1 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存井筒储集系数的最小值和最大值
automaticFittingData . getWellboreStorageMin ( ) . setValue ( m_parameterTable - > item ( 2 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getWellboreStorageMax ( ) . setValue ( m_parameterTable - > item ( 2 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存孔隙度的最小值和最大值
automaticFittingData . getPorosityMin ( ) . setValue ( m_parameterTable - > item ( 3 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getPorosityMax ( ) . setValue ( m_parameterTable - > item ( 3 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存裂缝导流能力的最小值和最大值
automaticFittingData . getFractureConductivityMin ( ) . setValue ( m_parameterTable - > item ( 5 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getFractureConductivityMax ( ) . setValue ( m_parameterTable - > item ( 5 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存裂缝半长的最小值和最大值
automaticFittingData . getFractureHalfLengthMin ( ) . setValue ( m_parameterTable - > item ( 6 , 2 ) - > text ( ) . toDouble ( ) ) ;
automaticFittingData . getFractureHalfLengthMax ( ) . setValue ( m_parameterTable - > item ( 6 , 4 ) - > text ( ) . toDouble ( ) ) ;
// 保存迭代参数
automaticFittingData . getIterationCount ( ) . setValue ( m_iterationEdit - > text ( ) . toInt ( ) ) ;
automaticFittingData . getErrorTolerance ( ) . setValue ( m_errorLimitEdit - > text ( ) . toDouble ( ) ) ;
// 敏感性只收集设置,初值由调用方保存,不写回井和储层参数。
if ( m_sensitivitySettings ) {
automaticFittingData . setAlgorithmName ( m_algorithmCombo - > currentIndex ( ) = = 1 ? " LM " : " PSO " ) ;
return ;
}
// 只写回可拟合的渗透率、孔隙度,含水饱和度沿用模型当前值。
reservoirData . getPermeability ( ) . setValue ( m_parameterTable - > item ( 0 , 3 ) - > text ( ) . toDouble ( ) ) ; // 渗透率
reservoirData . getPorosity ( ) . setValue ( m_parameterTable - > item ( 3 , 3 ) - > text ( ) . toDouble ( ) ) ; // 孔隙度
// 更新储层数据(全局)
nmDataAnalyzeManager : : getCurrentInstance ( ) - > updateReservoirData ( reservoirData ) ;
// 保存自动拟合数据
nmDataAnalyzeManager : : getCurrentInstance ( ) - > updateAutomaticFittingData ( automaticFittingData ) ;
// 只更新目标井的参数
QString selectedWellName = m_targetWellCombo - > currentText ( ) ;
if ( ! selectedWellName . isEmpty ( ) ) {
double newSkinValue = m_parameterTable - > item ( 1 , 3 ) - > text ( ) . toDouble ( ) ;
double newWellboreStorageValue = m_parameterTable - > item ( 2 , 3 ) - > text ( ) . toDouble ( ) ;
double newFractureConductivityValue = m_parameterTable - > item ( 5 , 3 ) - > text ( ) . toDouble ( ) ;
double newFractureHalfLengthValue = m_parameterTable - > item ( 6 , 3 ) - > text ( ) . toDouble ( ) ;
// 直接从数据管理器获取目标井
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
nmDataWellBase * pTargetWell = manager - > findWellByName ( selectedWellName ) ;
if ( pTargetWell ) {
// 先修改井参数副本,最后写回原对象中的现有属性,保持射孔对象地址不变。
nmDataPerforation * perf = pTargetWell - > getPerforation ( 0 ) ;
if ( perf ) {
nmDataAttribute skinAttr = perf - > getSkin ( ) ;
skinAttr . setValue ( newSkinValue ) ;
perf - > getSkin ( ) . setValue ( skinAttr . getValue ( ) ) ;
}
// 更新井筒储集系数
nmDataAttribute wellboreAttr = pTargetWell - > getWellboreStorage ( ) ;
wellboreAttr . setValue ( newWellboreStorageValue ) ;
pTargetWell - > getWellboreStorage ( ) . setValue ( wellboreAttr . getValue ( ) ) ;
NM_WELL_MODEL wellType = pTargetWell - > getWellType ( ) ;
// Dfc 属于压裂井对象;只修改这一项,裂缝位置、长度和段数保持原值。
if ( wellType = = NM_WELL_MODEL : : Vertical_Fractured_Well ) {
nmDataVerticalFracturedWell * fracturedWell = dynamic_cast < nmDataVerticalFracturedWell * > ( pTargetWell ) ;
if ( fracturedWell ) {
nmDataAttribute dfc = fracturedWell - > getDfc ( ) ;
dfc . setValue ( newFractureConductivityValue ) ;
fracturedWell - > getDfc ( ) . setValue ( dfc . getValue ( ) ) ;
nmDataAttribute halfLength = fracturedWell - > getFractureHalfLength ( ) ;
halfLength . setValue ( newFractureHalfLengthValue ) ;
fracturedWell - > getFractureHalfLength ( ) . setValue ( halfLength . getValue ( ) ) ;
}
} else if ( wellType = = NM_WELL_MODEL : : Horizontal_Fractured_Well ) {
nmDataHorizontalFracturedWell * fracturedWell = dynamic_cast < nmDataHorizontalFracturedWell * > ( pTargetWell ) ;
if ( fracturedWell ) {
nmDataAttribute dfc = fracturedWell - > getDfc ( ) ;
dfc . setValue ( newFractureConductivityValue ) ;
fracturedWell - > getDfc ( ) . setValue ( dfc . getValue ( ) ) ;
nmDataAttribute halfLength = fracturedWell - > getFractureHalfLength ( ) ;
halfLength . setValue ( newFractureHalfLengthValue ) ;
fracturedWell - > getFractureHalfLength ( ) . setValue ( halfLength . getValue ( ) ) ;
}
}
// 全部井参数落地后统一刷新一次右侧参数面板。
if ( manager - > getAttrRegistry ( ) ) {
manager - > getAttrRegistry ( ) - > refreshAll ( ) ;
}
}
}
}
void nmWxAutomaticFitting : : startAutoFitting ( const QVector < QVector < double > > & targetData , const QStringList & selectedParams , const QString & targetWellName )
{
DEBUG_UI ( " === START AUTO FITTING === " ) ;
// 先清理之前的实例
cleanupFitting ( ) ;
const bool useLM = m_algorithmCombo & & m_algorithmCombo - > currentIndex ( ) = = 1 ;
if ( useLM ) {
DEBUG_UI ( " Creating LM auto fitter " ) ;
m_autoFitterLM = new nmCalculationAutoFitLM ( this ) ;
m_autoFitterLM - > setTargetLogLogData ( targetData ) ;
m_autoFitterLM - > setTargetWellName ( targetWellName ) ;
} else {
DEBUG_UI ( " Creating PSO auto fitter " ) ;
m_autoFitterPSO = new nmCalculationAutoFitPSO ( this ) ;
m_autoFitterPSO - > setTargetLogLogData ( targetData ) ;
m_autoFitterPSO - > setPSOTargetWellName ( targetWellName ) ;
}
//// 特定井名时使用快速路径
//if (targetWellName == "VerticalWell1") {
// m_autoFitterPSO->setSimulationMode(true);
// // 构建目标参数向量(按启用参数的顺序)
// QVector<double> targetParams;
// if(m_kCheckBox->isChecked()) targetParams.append(0.0033); // 渗透率
// if(m_sCheckBox->isChecked()) targetParams.append(1.75); // 表皮系数
// if(m_cCheckBox->isChecked()) targetParams.append(0.23); // 井筒储集系数
// if(m_phiCheckBox->isChecked()) targetParams.append(0.189); // 孔隙度
// double targetError = 0.02291;
// m_autoFitterPSO->setSimulationTargetParams(targetParams, targetError);
//}
//else if (targetWellName == "W0009_1") {
// m_autoFitterPSO->setSimulationMode(true);
// // 构建目标参数向量(按启用参数的顺序)
// QVector<double> targetParams;
// if(m_kCheckBox->isChecked()) targetParams.append(0.23); // 渗透率
// if(m_sCheckBox->isChecked()) targetParams.append(0.75); // 表皮系数
// if(m_cCheckBox->isChecked()) targetParams.append(1.28); // 井筒储集系数
// if(m_phiCheckBox->isChecked()) targetParams.append(0.1954); // 孔隙度
// double targetError = 0.02036;
// m_autoFitterPSO->setSimulationTargetParams(targetParams, targetError);
//}
m_progressMonitor = new nmWxAutomaticfittingStart ( this ) ;
if ( m_autoFitterLM ) {
m_progressMonitor - > setAutoFitter ( m_autoFitterLM ) ;
} else {
m_progressMonitor - > setAutoFitter ( m_autoFitterPSO ) ;
}
m_progressMonitor - > setPseudoPressureMode (
nmDataAnalyzeManager : : getCurrentInstance ( ) - > getSolverModelType ( ) = = SMT_Gas_VariablePvt ) ;
m_progressMonitor - > setTargetLogLogData ( targetData ) ;
if ( m_autoFitterLM ) {
connect ( m_autoFitterLM , SIGNAL ( fittingFinished ( bool , QString ) ) ,
this , SLOT ( onFittingFinished ( bool , QString ) ) ) ;
} else {
connect ( m_autoFitterPSO , SIGNAL ( fittingFinished ( bool , QString ) ) ,
this , SLOT ( onFittingFinished ( bool , QString ) ) ) ;
}
int maxIterations = m_iterationEdit - > text ( ) . toInt ( ) ;
double targetError = m_errorLimitEdit - > text ( ) . toDouble ( ) ;
QString wellName = m_targetWellCombo - > currentText ( ) ;
m_progressMonitor - > setFittingParameters ( maxIterations , targetError , wellName ) ;
// 单位和勾选参数保持同一顺序,供横排参数区显示。
QStringList selectedUnits ;
if ( m_kCheckBox - > isChecked ( ) ) selectedUnits < < tr ( " mD " ) ;
if ( m_sCheckBox - > isChecked ( ) ) selectedUnits < < QString ( ) ;
if ( m_cCheckBox - > isChecked ( ) ) selectedUnits < < tr ( " m^3/MPa " ) ;
if ( m_phiCheckBox - > isChecked ( ) ) selectedUnits < < QString ( ) ;
if ( m_dfcCheckBox - > isChecked ( ) ) selectedUnits < < tr ( " md.m " ) ;
if ( m_fractureHalfLengthCheckBox - > isChecked ( ) ) selectedUnits < < tr ( " m " ) ;
m_progressMonitor - > setSelectedParameters ( selectedParams , selectedUnits ) ;
m_progressMonitor - > show ( ) ;
QTimer : : singleShot ( 100 , this , SLOT ( runAutoFitting ( ) ) ) ;
}
void nmWxAutomaticFitting : : runAutoFitting ( )
{
if ( m_progressMonitor ) {
m_progressMonitor - > markFittingStarted ( ) ;
}
if ( m_autoFitterPSO ) {
m_autoFitterPSO - > startAutoFitting ( ) ;
} else if ( m_autoFitterLM ) {
m_autoFitterLM - > startAutoFitting ( ) ;
}
}
void nmWxAutomaticFitting : : onFittingProgress ( int iteration , double fitness )
{
}
void nmWxAutomaticFitting : : onFittingFinished ( bool success , const QString & message )
{
// 立即断开信号连接,防止重复调用
if ( m_autoFitterPSO ) {
disconnect ( m_autoFitterPSO , SIGNAL ( fittingFinished ( bool , QString ) ) ,
this , SLOT ( onFittingFinished ( bool , QString ) ) ) ;
}
if ( m_autoFitterLM ) {
disconnect ( m_autoFitterLM , SIGNAL ( fittingFinished ( bool , QString ) ) ,
this , SLOT ( onFittingFinished ( bool , QString ) ) ) ;
}
// 拟合候选值通过参数副本写回,结束时统一把最终值同步到右侧参数面板。
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( manager & & manager - > getAttrRegistry ( ) ) {
manager - > getAttrRegistry ( ) - > refreshAll ( ) ;
}
// 失败状态不代表参数未写回;初值与范围始终以模型实际保留的数据为准。
refreshParametersFromModel ( ) ;
if ( success ) {
QString resultInfo ;
if ( m_autoFitterPSO | | m_autoFitterLM ) {
double bestFitness = m_autoFitterLM
? m_autoFitterLM - > getBestFitness ( )
: m_autoFitterPSO - > getBestFitness ( ) ;
const QString algorithmName = m_autoFitterLM ? " LM " : " PSO " ;
// 检查是否是用户停止的情况
if ( message . contains ( " stopped by user " , Qt : : CaseInsensitive ) ) {
resultInfo = tr ( " %1 Optimization stopped by user: \n " ) . arg ( algorithmName ) ;
resultInfo + = tr ( " Best Error: %1 \n " ) . arg ( bestFitness , 0 , ' e ' , 4 ) ;
resultInfo + = tr ( " Current parameters have been applied to the model. " ) ;
QMessageBox : : information ( this , tr ( " Optimization Stopped " ) , resultInfo ) ;
} else {
resultInfo = tr ( " %1 Optimization completed: \n " ) . arg ( algorithmName ) ;
resultInfo + = tr ( " Best Error: %1 \n " ) . arg ( bestFitness , 0 , ' e ' , 4 ) ;
resultInfo + = tr ( " Optimized parameters have been applied to the model. " ) ;
QMessageBox : : information ( this , tr ( " Optimization Completed " ) , resultInfo ) ;
}
}
} else {
// 只有真正失败的情况才显示警告
QMessageBox : : warning ( this , tr ( " Optimization Failed " ) , message ) ;
}
}
void nmWxAutomaticFitting : : onStopFitting ( )
{
if ( m_autoFitterPSO & & m_autoFitterPSO - > isRunning ( ) ) {
m_autoFitterPSO - > stopFitting ( ) ;
} else if ( m_autoFitterLM & & m_autoFitterLM - > isRunning ( ) ) {
m_autoFitterLM - > stopFitting ( ) ;
}
}
void nmWxAutomaticFitting : : cleanupFitting ( )
{
DEBUG_UI ( " === CLEANUP FITTING START === " ) ;
if ( m_progressTimer ) {
m_progressTimer - > stop ( ) ;
delete m_progressTimer ;
m_progressTimer = nullptr ;
DEBUG_UI ( " Progress timer cleaned up " ) ;
}
// 断开所有信号连接,防止后续回调
if ( m_autoFitterPSO ) {
DEBUG_UI ( " Stopping and disconnecting PSO fitter " ) ;
// 断开所有信号连接
disconnect ( m_autoFitterPSO , nullptr , nullptr , nullptr ) ;
// 如果还在运行,停止它
if ( m_autoFitterPSO - > isRunning ( ) ) {
m_autoFitterPSO - > stopFitting ( ) ;
// 等待停止完成
int waitCount = 0 ;
while ( m_autoFitterPSO - > isRunning ( ) & & waitCount < 50 ) {
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 100 ) ;
waitCount + + ;
}
// 如果仍在运行则强制停止
if ( m_autoFitterPSO - > isRunning ( ) ) {
DEBUG_UI ( " Force stopping PSO - timeout reached " ) ;
}
}
delete m_autoFitterPSO ;
m_autoFitterPSO = nullptr ;
DEBUG_UI ( " PSO fitter cleaned up " ) ;
}
if ( m_autoFitterLM ) {
DEBUG_UI ( " Stopping and disconnecting LM fitter " ) ;
disconnect ( m_autoFitterLM , nullptr , nullptr , nullptr ) ;
if ( m_autoFitterLM - > isRunning ( ) ) {
m_autoFitterLM - > stopFitting ( ) ;
int waitCount = 0 ;
while ( m_autoFitterLM - > isRunning ( ) & & waitCount < 50 ) {
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 100 ) ;
waitCount + + ;
}
}
delete m_autoFitterLM ;
m_autoFitterLM = nullptr ;
DEBUG_UI ( " LM fitter cleaned up " ) ;
}
// 清理进度监控
if ( m_progressMonitor ) {
// 先断开进度监控的信号连接
disconnect ( m_progressMonitor , nullptr , nullptr , nullptr ) ;
m_progressMonitor - > hide ( ) ;
delete m_progressMonitor ;
m_progressMonitor = nullptr ;
DEBUG_UI ( " Progress monitor cleaned up " ) ;
}
if ( m_progressDialog ) {
m_progressDialog - > hide ( ) ;
delete m_progressDialog ;
m_progressDialog = nullptr ;
DEBUG_UI ( " Progress dialog cleaned up " ) ;
}
DEBUG_UI ( " === CLEANUP FITTING END === " ) ;
}
void nmWxAutomaticFitting : : refreshParametersFromModel ( )
{
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
if ( ! manager ) {
return ;
}
// 同步储层副本,避免下一次保存设置时把未拟合的储层属性覆盖成旧值。
reservoirData = manager - > getReservoirDataCopy ( ) ;
const bool wasUpdatingRanges = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
m_parameterTable - > item ( 0 , 3 ) - > setText ( QString : : number (
reservoirData . getPermeability ( ) . getValue ( ) . toDouble ( ) , ' g ' , 10 ) ) ;
m_parameterTable - > item ( 3 , 3 ) - > setText ( QString : : number (
reservoirData . getPorosity ( ) . getValue ( ) . toDouble ( ) , ' g ' , 10 ) ) ;
// 成功或失败都从当前目标井读取已落地的参数,不采用优化器尚未写回的最佳候选。
// 先刷新全部初值,再统一重建范围,避免即时校验读到新旧值混合的表格。
m_autoParameterRanges = false ;
onWellSelected ( m_targetWellCombo - > currentIndex ( ) ) ;
m_autoParameterRanges = true ;
initializeSuggestedParameterRanges ( ) ;
m_updatingParameterRanges = wasUpdatingRanges ;
manager - > updateAutomaticFittingData ( automaticFittingData ) ;
nmWxParameterProperty : : notifyUpdateTable ( ) ;
}