# 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 <QStyledItemDelegate>
# 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 {
// 树节点和名称、单位列只读,仅允许编辑参数的三个数值列。
class nmFitParameterDelegate : public QStyledItemDelegate
{
public :
explicit nmFitParameterDelegate ( QObject * parent ) : QStyledItemDelegate ( parent ) { }
QSize sizeHint ( const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
// 分组和参数统一增高,字号变化时也保留上下留白。
QSize size = QStyledItemDelegate : : sizeHint ( option , index ) ;
size . setHeight ( qMax ( size . height ( ) , qMax ( 32 , option . fontMetrics . height ( ) + 12 ) ) ) ;
return size ;
}
QWidget * createEditor ( QWidget * parent , const QStyleOptionViewItem & option ,
const QModelIndex & index ) const
{
if ( index . column ( ) < 1 | | index . column ( ) > 3
| | ! index . sibling ( index . row ( ) , 0 ) . data ( Qt : : UserRole ) . isValid ( ) )
return nullptr ;
return QStyledItemDelegate : : createEditor ( parent , option , index ) ;
}
} ;
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 ;
}
}
// 获取可拟合参数的系统物理边界,索引 4 不再作为拟合参数。
bool nmWxAutomaticFitting : : getPhysicalParameterRange ( int parameterIndex ,
double & minValue , double & maxValue )
{
// 参数行按实际类型查询边界,不依赖它位于哪口井的分组。
static const char * names [ ] = { " Result_K " , " Result_W_Skin " , " Result_W_C " ,
" Result_phi " , " Result_W_Dfc " , " W_FractureHalfLength " } ;
const int type = automaticFittingData . getModelParameters ( ) [ parameterIndex ] . type ;
if ( ! nmAutoFitReadPhysicalRange ( names [ type ] , minValue , maxValue ) ) return false ;
if ( type = = 1 ) minValue = 0.0 ;
return true ;
}
// 将一组上下界同步到表格和自动拟合数据,保证 PSO 读取到同一份配置。
void nmWxAutomaticFitting : : setParameterRange ( int parameterIndex , double minValue , double maxValue )
{
// 自动范围写入时不触发尚未完成的行校验。
const bool updating = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
m_parameterItems [ parameterIndex ] - > setText ( 2 , QString : : number ( minValue , ' g ' , 10 ) ) ;
m_parameterItems [ parameterIndex ] - > setText ( 3 , QString : : number ( maxValue , ' g ' , 10 ) ) ;
m_updatingParameterRanges = updating ;
}
// 根据当前初值生成建议搜索范围, 并始终截断在拟合边界内。skin 下界为 0,
// 上界按初值加固定宽度,其余正值参数使用倍率范围;每次打开窗口和拟合完成后复用。
void nmWxAutomaticFitting : : updateRangeForParameter ( int parameterIndex ,
double centerValue )
{
if ( ! m_parameterTable | | parameterIndex < 0 | | parameterIndex > = m_parameterItems . size ( )
| | ! nmAutoFitUiIsFinite ( centerValue ) ) {
return ;
}
double physicalMin = 0.0 ;
double physicalMax = 0.0 ;
if ( ! getPhysicalParameterRange ( parameterIndex , physicalMin , physicalMax ) ) {
return ;
}
double reference = centerValue ;
const int type = automaticFittingData . getModelParameters ( ) [ parameterIndex ] . type ;
const bool positiveParameter = type ! = 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 ( type = = 1 ) {
const double skinHalfRange = 10.0 ;
newMin = physicalMin ;
newMax = qMin ( physicalMax , reference + skinHalfRange ) ;
} else if ( reference > 0.0
& & ! ( type = = 4 & & 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 ( type = = 4 ) {
// 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 ) ;
}
}
// 校验参数范围; 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 > = m_parameterItems . size ( ) ) {
errorMessage = tr ( " The parameter row is invalid. " ) ;
return false ;
}
const int firstParameterIndex = parameterIndex < 0 ? 0 : parameterIndex ;
const int lastParameterIndex = parameterIndex < 0 ? m_parameterItems . size ( ) : parameterIndex + 1 ;
for ( int currentParameterIndex = firstParameterIndex ;
currentParameterIndex < lastParameterIndex ; + + currentParameterIndex ) {
QTreeWidgetItem * item = m_parameterItems [ currentParameterIndex ] ;
// 已参与采样或当前算法不可用的参数不阻塞设置保存;折叠的分组仍须校验。
if ( item - > isDisabled ( ) ) continue ;
const nmFitParameter & parameter = automaticFittingData . getModelParameters ( ) [ currentParameterIndex ] ;
bool minOk = false ;
bool initialOk = false ;
bool maxOk = false ;
const double minValue = item - > text ( 2 ) . toDouble ( & minOk ) ;
const double initialValue = item - > text ( 1 ) . toDouble ( & initialOk ) ;
const double maxValue = item - > text ( 3 ) . 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 ( parameter . name ( ) ) ;
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 ( parameter . name ( ) ) ;
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 ( parameter . name ( ) )
. 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 ( parameter . name ( ) ) ;
return false ;
}
// 底层用 Dfc=0 表示无限导流,这是离散模型选项,不属于有限导流拟合域。
if ( parameter . type = = 4 & & item - > checkState ( 0 ) = = Qt : : Checked
& & ( minValue < = 1.0e-10 | | initialValue < = 1.0e-10 ) ) {
errorMessage = tr ( " The minimum value of %1 must be greater than zero for automatic fitting. " )
. arg ( parameter . name ( ) ) ;
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_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 ( ) ;
if ( m_sensitivitySettings ) {
if ( m_sensitivitySettings - > confirmed ) automaticFittingData = m_sensitivitySettings - > parameters ;
else automaticFittingData . getModelParameters ( ) . clear ( ) ;
}
nmDataWellBase * primary = pManager - > findWellByCode ( pManager - > getPrimaryWellCode ( ) ) ;
m_targetWellName = primary ? primary - > getWellName ( ) : QString ( ) ;
setupUI ( ) ;
// 敏感性入口使用“确定”保存设置,普通入口仍直接开始拟合。
if ( m_sensitivitySettings ) m_okBtn - > setText ( tr ( " OK " ) ) ;
setWindowTitle ( tr ( " Automatic fitting " ) ) ;
setModal ( true ) ;
// 初始尺寸随所在屏幕变化,最小尺寸交给实际控件布局决定。
resize ( sizeHint ( ) ) ;
initializeModelParameters ( ) ;
onAlgorithmChanged ( m_algorithmCombo - > currentIndex ( ) ) ;
m_updatingParameterRanges = false ;
DEBUG_UI ( " AutoFitting Constructor completed " ) ;
}
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 QTreeWidget { 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 QTreeWidget::item { border-bottom: 1px solid #eef1f5; padding: 5px 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 QTreeWidget ( this ) ;
m_parameterTable - > setFont ( QFont ( " Microsoft YaHei " , 10 ) ) ;
m_parameterTable - > setColumnCount ( 5 ) ;
m_parameterTable - > setHeaderLabels ( QStringList ( ) < < tr ( " Parameter " ) < < tr ( " Initial value " )
< < tr ( " Min " ) < < tr ( " Max " ) < < tr ( " Unit " ) ) ;
m_parameterTable - > setItemDelegate ( new nmFitParameterDelegate ( m_parameterTable ) ) ;
m_parameterTable - > setSelectionBehavior ( QAbstractItemView : : SelectItems ) ;
m_parameterTable - > setUniformRowHeights ( true ) ;
m_parameterTable - > setIndentation ( 18 ) ;
QHeaderView * header = m_parameterTable - > header ( ) ;
header - > setDefaultAlignment ( Qt : : AlignLeft | Qt : : AlignVCenter ) ;
header - > setStretchLastSection ( false ) ;
header - > setResizeMode ( 0 , QHeaderView : : Interactive ) ;
m_parameterTable - > setColumnWidth ( 0 , 230 ) ;
for ( int column = 1 ; column < = 3 ; + + column ) header - > setResizeMode ( column , QHeaderView : : Stretch ) ;
header - > setResizeMode ( 4 , QHeaderView : : Fixed ) ;
m_parameterTable - > setColumnWidth ( 4 , 120 ) ;
connect ( m_parameterTable , SIGNAL ( itemChanged ( QTreeWidgetItem * , int ) ) ,
this , SLOT ( onParameterTableItemChanged ( QTreeWidgetItem * , int ) ) ) ;
}
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 " ) ) ;
// 从敏感性分析打开时仅使用 LM, 普通自动拟合仍可选择算法。
m_algorithmCombo - > setCurrentIndex ( m_sensitivitySettings
| | nmDataAnalyzeManager : : getCurrentInstance ( ) - > getEffectiveCalculationWells ( ) . size ( ) > 1 ? 1 : 0 ) ;
m_algorithmCombo - > setEnabled ( ! m_sensitivitySettings ) ;
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 ( ) ) ) ;
m_controlLayout - > addWidget ( algorithmLabel , 0 , 0 ) ;
m_controlLayout - > addWidget ( m_algorithmCombo , 1 , 0 ) ;
m_controlLayout - > addWidget ( errorLabel , 0 , 1 ) ;
m_controlLayout - > addWidget ( m_errorLimitEdit , 1 , 1 ) ;
m_controlLayout - > addWidget ( m_iterationLabel , 0 , 2 ) ;
m_controlLayout - > addWidget ( m_iterationEdit , 1 , 2 ) ;
// 右侧预留一列空白,三个输入框不再铺满整行。
for ( int column = 0 ; column < 4 ; + + column ) m_controlLayout - > setColumnStretch ( column , 1 ) ;
}
void nmWxAutomaticFitting : : onAlgorithmChanged ( int index )
{
// 多井联合拟合使用 LM; PSO 仍只开放主井和共享储层参数。
const bool usePSO = index = = 0 ;
const QString primaryCode = nmDataAnalyzeManager : : getCurrentInstance ( ) - > getPrimaryWellCode ( ) ;
const bool updating = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
const QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
for ( int i = 0 ; i < parameters . size ( ) ; + + i ) {
const nmFitParameter & p = parameters [ i ] ;
nmDataWellBase * well = p . wellCode . isEmpty ( ) ? nullptr
: nmDataAnalyzeManager : : getCurrentInstance ( ) - > findWellByCode ( p . wellCode ) ;
const bool unavailable = ( m_sensitivitySettings & & m_sampledParameters . contains ( p . samplingKey ( ) ) )
| | ( usePSO & & ! p . wellCode . isEmpty ( ) & & p . wellCode ! = primaryCode )
| | ( p . type = = 1 & & ( ! well | | well - > getPerforationCount ( ) = = 0 ) ) ;
m_parameterItems [ i ] - > setDisabled ( unavailable ) ;
if ( unavailable ) m_parameterItems [ i ] - > setCheckState ( 0 , Qt : : Unchecked ) ;
}
m_updatingParameterRanges = updating ;
updateSelectAllState ( ) ;
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_selectAllCheckBox = new QCheckBox ( tr ( " Select All " ) ) ;
m_okBtn = new QPushButton ( tr ( " Start fitting " ) ) ;
m_okBtn - > setObjectName ( " startFitting " ) ;
m_okBtn - > setDefault ( true ) ;
m_cancelBtn = new QPushButton ( tr ( " Cancel " ) ) ;
// 设置按钮大小
m_okBtn - > setMinimumWidth ( 80 ) ;
m_cancelBtn - > setMinimumWidth ( 80 ) ;
buttonLayout - > addWidget ( m_selectAllCheckBox ) ;
buttonLayout - > addStretch ( ) ;
buttonLayout - > addWidget ( m_cancelBtn ) ;
buttonLayout - > addWidget ( m_okBtn ) ;
m_mainLayout - > addLayout ( buttonLayout ) ;
// 连接信号槽
connect ( m_selectAllCheckBox , SIGNAL ( clicked ( bool ) ) , this , SLOT ( onSelectAll ( bool ) ) ) ;
connect ( m_okBtn , SIGNAL ( clicked ( ) ) , this , SLOT ( onAccept ( ) ) ) ;
connect ( m_cancelBtn , SIGNAL ( clicked ( ) ) , this , SLOT ( onReject ( ) ) ) ;
}
void nmWxAutomaticFitting : : onSelectAll ( bool checked )
{
// 批量切换可用参数,完成后统一同步全选框,避免逐项更新时改变点击状态。
const bool updating = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
for ( int i = 0 ; i < m_parameterItems . size ( ) ; + + i ) {
QTreeWidgetItem * item = m_parameterItems [ i ] ;
if ( ! item - > isDisabled ( ) )
item - > setCheckState ( 0 , checked ? Qt : : Checked : Qt : : Unchecked ) ;
}
m_updatingParameterRanges = updating ;
updateSelectAllState ( ) ;
}
void nmWxAutomaticFitting : : updateSelectAllState ( )
{
// 仅统计可用参数;单项勾选和算法切换后,全选框都与当前列表保持一致。
int available = 0 ;
int selected = 0 ;
for ( int i = 0 ; i < m_parameterItems . size ( ) ; + + i ) {
if ( m_parameterItems [ i ] - > isDisabled ( ) ) continue ;
+ + available ;
if ( m_parameterItems [ i ] - > checkState ( 0 ) = = Qt : : Checked ) + + selected ;
}
m_selectAllCheckBox - > setEnabled ( available > 0 ) ;
m_selectAllCheckBox - > setChecked ( available > 0 & & selected = = available ) ;
}
void nmWxAutomaticFitting : : onParameterTableItemChanged ( QTreeWidgetItem * item , int column )
{
// 勾选时同步全选框,数值编辑仍沿用原来的范围校验。
if ( ! m_updatingParameterRanges & & column = = 0 ) updateSelectAllState ( ) ;
if ( m_updatingParameterRanges | | column < 1 | | column > 3
| | ! item - > data ( 0 , Qt : : UserRole ) . isValid ( ) ) return ;
QString error ;
if ( ! validateParameterTable ( error , item - > data ( 0 , Qt : : UserRole ) . toInt ( ) ) )
QMessageBox : : warning ( this , tr ( " Invalid parameter range " ) , error ) ;
}
void nmWxAutomaticFitting : : onAccept ( )
{
QString validationError ;
if ( ! validateParameterTable ( validationError ) ) {
QMessageBox : : warning ( this , tr ( " Invalid parameter range " ) , validationError ) ;
return ;
}
// 校验通过后再保存参数设置,非法输入不会被静默裁剪。
saveModelParameters ( ) ;
setAutomaticFittingValue ( ) ;
if ( m_sensitivitySettings ) {
// 仅提交给敏感性窗口;取消、关闭及普通拟合均不经过此分支。
m_sensitivitySettings - > parameters = automaticFittingData ;
m_sensitivitySettings - > targetWell = m_targetWellName ;
m_sensitivitySettings - > initialValues = primaryInitialValues ( ) ;
m_sensitivitySettings - > confirmed = true ;
accept ( ) ;
return ;
}
// 更新完成后,通知参数界面刷新
nmWxParameterProperty : : notifyUpdateTable ( ) ;
// 获取目标井的双对数历史数据
QString selectedWellName = m_targetWellName ;
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 = automaticFittingData . hasSelectedParameters ( ) ;
if ( ! hasSelectedParams ) {
QMessageBox : : warning ( this , tr ( " Warning " ) , tr ( " Please select at least one parameter for optimization! " ) ) ;
return ;
}
const QStringList selectedParameterNames = selectedParameterLabels ( false ) ;
// 启动自动拟合 - 传递双对数历史数据
startAutoFitting ( targetLogLogData , selectedParameterNames , selectedWellName ) ;
}
void nmWxAutomaticFitting : : onReject ( )
{
reject ( ) ;
}
void nmWxAutomaticFitting : : setAutomaticFittingValue ( )
{
// 单井旧字段继续供 PSO 和旧配置读取, LM 直接使用完整分组参数列表。
automaticFittingData . setPermeabilitySelected ( false ) ;
automaticFittingData . setSkinSelected ( false ) ;
automaticFittingData . setWellboreStorageSelected ( false ) ;
automaticFittingData . setPorositySelected ( false ) ;
automaticFittingData . setFractureConductivitySelected ( false ) ;
automaticFittingData . setFractureHalfLengthSelected ( false ) ;
const QString primaryCode = nmDataAnalyzeManager : : getCurrentInstance ( ) - > getPrimaryWellCode ( ) ;
const QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
for ( int i = 0 ; i < parameters . size ( ) ; + + i ) {
const nmFitParameter & p = parameters [ i ] ;
if ( ! p . wellCode . isEmpty ( ) & & p . wellCode ! = primaryCode ) continue ;
switch ( p . type ) {
case 0 :
automaticFittingData . setPermeabilitySelected ( p . selected ) ;
automaticFittingData . getPermeabilityMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getPermeabilityMax ( ) . setValue ( p . upper ) ;
break ;
case 1 :
automaticFittingData . setSkinSelected ( p . selected ) ;
automaticFittingData . getSkinMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getSkinMax ( ) . setValue ( p . upper ) ;
break ;
case 2 :
automaticFittingData . setWellboreStorageSelected ( p . selected ) ;
automaticFittingData . getWellboreStorageMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getWellboreStorageMax ( ) . setValue ( p . upper ) ;
break ;
case 3 :
automaticFittingData . setPorositySelected ( p . selected ) ;
automaticFittingData . getPorosityMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getPorosityMax ( ) . setValue ( p . upper ) ;
break ;
case 4 :
automaticFittingData . setFractureConductivitySelected ( p . selected ) ;
automaticFittingData . getFractureConductivityMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getFractureConductivityMax ( ) . setValue ( p . upper ) ;
break ;
case 5 :
automaticFittingData . setFractureHalfLengthSelected ( p . selected ) ;
automaticFittingData . getFractureHalfLengthMin ( ) . setValue ( p . lower ) ;
automaticFittingData . getFractureHalfLengthMax ( ) . setValue ( p . upper ) ;
break ;
}
}
automaticFittingData . setSurrogateScreeningEnabled ( false ) ;
automaticFittingData . getIterationCount ( ) . setValue ( m_iterationEdit - > text ( ) . toInt ( ) ) ;
automaticFittingData . getErrorTolerance ( ) . setValue ( m_errorLimitEdit - > text ( ) . toDouble ( ) ) ;
automaticFittingData . setAlgorithmName ( m_algorithmCombo - > currentIndex ( ) = = 1 ? " LM " : " PSO " ) ;
// 敏感性入口只保存设置;普通 LM 在启动时按井应用初值。
if ( m_sensitivitySettings ) return ;
if ( m_algorithmCombo - > currentIndex ( ) = = 1 ) {
nmDataAnalyzeManager : : getCurrentInstance ( ) - > updateAutomaticFittingData ( automaticFittingData ) ;
return ;
}
const QVector < double > initial = primaryInitialValues ( ) ;
// 只写回可拟合的渗透率、孔隙度,含水饱和度沿用模型当前值。
reservoirData . getPermeability ( ) . setValue ( initial [ 0 ] ) ; // 渗透率
reservoirData . getPorosity ( ) . setValue ( initial [ 3 ] ) ; // 孔隙度
// 更新储层数据(全局)
nmDataAnalyzeManager : : getCurrentInstance ( ) - > updateReservoirData ( reservoirData ) ;
// 保存自动拟合数据
nmDataAutomaticFitting psoSettings = automaticFittingData ;
psoSettings . getModelParameters ( ) . clear ( ) ;
nmDataAnalyzeManager : : getCurrentInstance ( ) - > updateAutomaticFittingData ( psoSettings ) ;
// 只更新目标井的参数
QString selectedWellName = m_targetWellName ;
if ( ! selectedWellName . isEmpty ( ) ) {
double newSkinValue = initial [ 1 ] ;
double newWellboreStorageValue = initial [ 2 ] ;
double newFractureConductivityValue = initial [ 4 ] ;
double newFractureHalfLengthValue = initial [ 5 ] ;
// 直接从数据管理器获取目标井
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 ) ;
}
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_targetWellName ;
m_progressMonitor - > setFittingParameters ( maxIterations , targetError , wellName ) ;
// 名称和单位均按实际优化向量的顺序展示。
m_progressMonitor - > setSelectedParameters ( selectedParams , selectedParameterLabels ( true ) ) ;
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 ( ) ;
saveModelParameters ( ) ;
reservoirData = manager - > getReservoirDataCopy ( ) ;
initializeModelParameters ( false ) ;
onAlgorithmChanged ( m_algorithmCombo - > currentIndex ( ) ) ;
saveModelParameters ( ) ;
setAutomaticFittingValue ( ) ;
nmWxParameterProperty : : notifyUpdateTable ( ) ;
}
void nmWxAutomaticFitting : : saveModelParameters ( )
{
// 每个叶节点对应一个带井编码的参数,直接收集所有分组。
QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
for ( int i = 0 ; i < parameters . size ( ) ; + + i ) {
nmFitParameter & p = parameters [ i ] ;
QTreeWidgetItem * item = m_parameterItems [ i ] ;
p . selected = ! item - > isDisabled ( ) & & item - > checkState ( 0 ) = = Qt : : Checked ;
p . initial = item - > text ( 1 ) . toDouble ( ) ;
p . lower = item - > text ( 2 ) . toDouble ( ) ;
p . upper = item - > text ( 3 ) . toDouble ( ) ;
}
}
void nmWxAutomaticFitting : : initializeModelParameters ( bool restoreRanges )
{
// 储层只建一组,各井按井型加入原有参数;不再通过切换页面收集数据。
const bool updating = m_updatingParameterRanges ;
m_updatingParameterRanges = true ;
const QVector < nmFitParameter > saved = automaticFittingData . getModelParameters ( ) ;
automaticFittingData . getModelParameters ( ) . clear ( ) ;
m_parameterItems . clear ( ) ;
m_parameterTable - > clear ( ) ;
nmDataAnalyzeManager * manager = nmDataAnalyzeManager : : getCurrentInstance ( ) ;
QTreeWidgetItem * reservoir = new QTreeWidgetItem ( m_parameterTable , QStringList ( tr ( " Reservoir parameters " ) ) ) ;
addParameter ( reservoir , 0 , nullptr , reservoirData . getPermeability ( ) . getValue ( ) . toDouble ( ) ) ;
addParameter ( reservoir , 3 , nullptr , reservoirData . getPorosity ( ) . getValue ( ) . toDouble ( ) ) ;
QTreeWidgetItem * wellsGroup = new QTreeWidgetItem ( m_parameterTable , QStringList ( tr ( " Well parameters " ) ) ) ;
const QVector < nmCalculationWellRef > wells = manager - > getEffectiveCalculationWells ( ) ;
for ( int i = 0 ; i < wells . size ( ) ; + + i ) {
nmDataWellBase * well = manager - > findWellByCode ( wells [ i ] . m_sWellCode ) ;
if ( ! well ) continue ;
QTreeWidgetItem * group = new QTreeWidgetItem ( wellsGroup , QStringList ( well - > getWellName ( ) ) ) ;
const double skin = well - > getPerforationCount ( ) > 0
? well - > getPerforation ( 0 ) - > getSkin ( ) . getValue ( ) . toDouble ( ) : 0.0 ;
addParameter ( group , 1 , well , skin ) ;
addParameter ( group , 2 , well , well - > getWellboreStorage ( ) . getValue ( ) . toDouble ( ) ) ;
if ( nmDataVerticalFracturedWell * fractured = dynamic_cast < nmDataVerticalFracturedWell * > ( well ) ) {
addParameter ( group , 4 , well , fractured - > getDfc ( ) . getValue ( ) . toDouble ( ) ) ;
addParameter ( group , 5 , well , fractured - > getFractureHalfLength ( ) . getValue ( ) . toDouble ( ) ) ;
} else if ( nmDataHorizontalFracturedWell * fractured = dynamic_cast < nmDataHorizontalFracturedWell * > ( well ) ) {
addParameter ( group , 4 , well , fractured - > getDfc ( ) . getValue ( ) . toDouble ( ) ) ;
addParameter ( group , 5 , well , fractured - > getFractureHalfLength ( ) . getValue ( ) . toDouble ( ) ) ;
}
}
const bool legacySelected [ ] = { automaticFittingData . getPermeabilitySelected ( ) ,
automaticFittingData . getSkinSelected ( ) , automaticFittingData . getWellboreStorageSelected ( ) ,
automaticFittingData . getPorositySelected ( ) , automaticFittingData . getFractureConductivitySelected ( ) ,
automaticFittingData . getFractureHalfLengthSelected ( ) } ;
nmDataAttribute * legacyMin [ ] = { & automaticFittingData . getPermeabilityMin ( ) , & automaticFittingData . getSkinMin ( ) ,
& automaticFittingData . getWellboreStorageMin ( ) , & automaticFittingData . getPorosityMin ( ) ,
& automaticFittingData . getFractureConductivityMin ( ) , & automaticFittingData . getFractureHalfLengthMin ( ) } ;
nmDataAttribute * legacyMax [ ] = { & automaticFittingData . getPermeabilityMax ( ) , & automaticFittingData . getSkinMax ( ) ,
& automaticFittingData . getWellboreStorageMax ( ) , & automaticFittingData . getPorosityMax ( ) ,
& automaticFittingData . getFractureConductivityMax ( ) , & automaticFittingData . getFractureHalfLengthMax ( ) } ;
const QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
for ( int i = 0 ; i < parameters . size ( ) ; + + i ) {
const nmFitParameter & p = parameters [ i ] ;
updateRangeForParameter ( i , p . initial ) ;
for ( int j = 0 ; j < saved . size ( ) ; + + j ) {
if ( saved [ j ] . type ! = p . type | | saved [ j ] . wellCode ! = p . wellCode ) continue ;
m_parameterItems [ i ] - > setCheckState ( 0 , saved [ j ] . selected ? Qt : : Checked : Qt : : Unchecked ) ;
if ( restoreRanges & & p . initial > = saved [ j ] . lower & & p . initial < = saved [ j ] . upper )
setParameterRange ( i , saved [ j ] . lower , saved [ j ] . upper ) ;
break ;
}
// 兼容尚无多井列表的敏感性设置,旧井参数仍对应原来记录的目标井。
if ( saved . isEmpty ( ) & & m_sensitivitySettings & & m_sensitivitySettings - > confirmed
& & ( p . wellCode . isEmpty ( ) | | p . wellName = = m_sensitivitySettings - > targetWell ) ) {
m_parameterItems [ i ] - > setCheckState ( 0 , legacySelected [ p . type ] ? Qt : : Checked : Qt : : Unchecked ) ;
const double lower = legacyMin [ p . type ] - > getValue ( ) . toDouble ( ) ;
const double upper = legacyMax [ p . type ] - > getValue ( ) . toDouble ( ) ;
if ( restoreRanges & & p . initial > = lower & & p . initial < = upper ) setParameterRange ( i , lower , upper ) ;
}
}
// 分组标题加粗并横跨数值列,叶节点仍保留独立勾选和编辑。
for ( QTreeWidgetItemIterator it ( m_parameterTable ) ; * it ; + + it ) {
if ( ( * it ) - > data ( 0 , Qt : : UserRole ) . isValid ( ) ) continue ;
QFont font = ( * it ) - > font ( 0 ) ;
font . setBold ( true ) ;
( * it ) - > setFont ( 0 , font ) ;
( * it ) - > setFirstColumnSpanned ( true ) ;
( * it ) - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsSelectable ) ;
}
m_parameterTable - > expandAll ( ) ;
m_updatingParameterRanges = updating ;
}
void nmWxAutomaticFitting : : addParameter ( QTreeWidgetItem * group , int type ,
nmDataWellBase * well , double initial )
{
// 名称在井分组内只显示参数本身,数据仍保留所属井编码供计算使用。
nmFitParameter parameter ;
parameter . type = type ;
parameter . initial = initial ;
const QString name = parameter . name ( ) ;
if ( well ) {
parameter . wellCode = well - > getWellCode ( ) ;
parameter . wellName = well - > getWellName ( ) ;
}
const int index = automaticFittingData . getModelParameters ( ) . size ( ) ;
automaticFittingData . getModelParameters ( ) . append ( parameter ) ;
QTreeWidgetItem * item = new QTreeWidgetItem ( group ) ;
item - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsSelectable | Qt : : ItemIsUserCheckable | Qt : : ItemIsEditable ) ;
item - > setData ( 0 , Qt : : UserRole , index ) ;
item - > setText ( 0 , name ) ;
item - > setText ( 1 , QString : : number ( initial , ' g ' , 10 ) ) ;
item - > setText ( 4 , parameter . unit ( ) ) ;
item - > setCheckState ( 0 , ! m_sensitivitySettings
& & ( ! well | | well - > getWellCode ( ) = = nmDataAnalyzeManager : : getCurrentInstance ( ) - > getPrimaryWellCode ( ) )
? Qt : : Checked : Qt : : Unchecked ) ;
m_parameterItems . append ( item ) ;
}
QVector < double > nmWxAutomaticFitting : : primaryInitialValues ( ) const
{
// 旧六参数接口仅提取主井与共享储层,其他井不会覆盖主井初值。
QVector < double > values ( 6 , 0.0 ) ;
const QString primaryCode = nmDataAnalyzeManager : : getCurrentInstance ( ) - > getPrimaryWellCode ( ) ;
const QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
for ( int i = 0 ; i < parameters . size ( ) ; + + i )
if ( parameters [ i ] . wellCode . isEmpty ( ) | | parameters [ i ] . wellCode = = primaryCode )
values [ parameters [ i ] . type ] = parameters [ i ] . initial ;
return values ;
}
QStringList nmWxAutomaticFitting : : selectedParameterLabels ( bool units ) const
{
// LM 按完整向量顺序显示, PSO 沿用旧六参数类型顺序。
QStringList labels ;
const QVector < nmFitParameter > & parameters = automaticFittingData . getModelParameters ( ) ;
if ( m_algorithmCombo - > currentIndex ( ) = = 1 ) {
for ( int i = 0 ; i < parameters . size ( ) ; + + i )
if ( parameters [ i ] . selected ) labels < < ( units ? parameters [ i ] . unit ( ) : parameters [ i ] . name ( ) ) ;
} else {
for ( int type = 0 ; type < 6 ; + + type )
for ( int i = 0 ; i < parameters . size ( ) ; + + i )
if ( parameters [ i ] . selected & & parameters [ i ] . type = = type )
labels < < ( units ? parameters [ i ] . unit ( ) : parameters [ i ] . name ( ) ) ;
}
return labels ;
}