Compare commits

..

4 Commits

Binary file not shown.

@ -2440,8 +2440,10 @@ Reason: %1</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Solve time: %1 ms</source> <source>Grid count: %1
<translation>%1 ms</translation> Solve time: %2 ms</source>
<translation>%1
%2 ms</translation>
</message> </message>
</context> </context>
<context> <context>

@ -52,6 +52,8 @@ public:
// 获取输出结果 // 获取输出结果
HX_NWTM_GRID_OUTPUT1 getGridOutput1(); HX_NWTM_GRID_OUTPUT1 getGridOutput1();
HX_NWTM_GRID_OUTPUT2 getGridOutput2(); HX_NWTM_GRID_OUTPUT2 getGridOutput2();
// 获取最近一次网格划分生成的PEBI网格数量
int getPebiCount() const;
private: private:
// 静态对象 // 静态对象
static nmCalculationPebiGrid* m_instance; static nmCalculationPebiGrid* m_instance;
@ -62,6 +64,7 @@ private:
HX_NWTM_GRID_OUTPUT2 p2; HX_NWTM_GRID_OUTPUT2 p2;
// 对应HX_NWTM_GRID_INPUT::GridControl // 对应HX_NWTM_GRID_INPUT::GridControl
double m_dGridControl; double m_dGridControl;
int m_nPebiCount;
}; };

@ -0,0 +1,16 @@
#pragma once
class QWidget;
// 使用PEBI自带算例生成网格、计算压力并导出结果。
class nmPebiExampleCalculator
{
public:
explicit nmPebiExampleCalculator(QWidget* pParent = 0);
// 显示模型选择对话框并执行所选算例。
void run();
private:
QWidget* m_pParent;
};

@ -399,12 +399,11 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
SetIntValueFunc setSolverType = (SetIntValueFunc)GetProcAddress(dll, "set_solvetype"); SetIntValueFunc setSolverType = (SetIntValueFunc)GetProcAddress(dll, "set_solvetype");
SetIntValueFunc setOmpThreads = (SetIntValueFunc)GetProcAddress(dll, "set_omp_threads"); SetIntValueFunc setOmpThreads = (SetIntValueFunc)GetProcAddress(dll, "set_omp_threads");
SetIntValueFunc setIluReuseSteps = (SetIntValueFunc)GetProcAddress(dll, "set_ilu_reuse_steps"); SetIntValueFunc setIluReuseSteps = (SetIntValueFunc)GetProcAddress(dll, "set_ilu_reuse_steps");
GetIntValueFunc getPebiCount = (GetIntValueFunc)GetProcAddress(dll, "getPEBInum");
GetIntValueFunc getSolveTime = (GetIntValueFunc)GetProcAddress(dll, "getsolvetime"); GetIntValueFunc getSolveTime = (GetIntValueFunc)GetProcAddress(dll, "getsolvetime");
if(HX_NWTM_MODEL == nullptr || setSolverType == nullptr if(HX_NWTM_MODEL == nullptr || setSolverType == nullptr
|| setOmpThreads == nullptr || setIluReuseSteps == nullptr || setOmpThreads == nullptr || setIluReuseSteps == nullptr
|| getPebiCount == nullptr || getSolveTime == nullptr) { || getSolveTime == nullptr) {
QString sLogMessage = "Failed to resolve PEBI solver configuration interface."; QString sLogMessage = "Failed to resolve PEBI solver configuration interface.";
qWarning() << sLogMessage; qWarning() << sLogMessage;
zxLogInstance::getInstance()->writeLogF(sLogMessage); zxLogInstance::getInstance()->writeLogF(sLogMessage);
@ -625,6 +624,14 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
} }
} }
// 油水两相算例按定产油量处理导入流量传给qoqg和qw使用等长零数组。
if(modelType == static_cast<int>(SMT_Oil_Water_TwoPhase)) {
for(size_t i = 0; i < p0.Rate.t.size(); ++i) {
p0.Rate.qg[i].assign(p0.Rate.t[i].size(), 0.0);
p0.Rate.qw[i].assign(p0.Rate.t[i].size(), 0.0);
}
}
// PVT数据根据求解器模型类型填充PVT输入数据 // PVT数据根据求解器模型类型填充PVT输入数据
nmDataPvtParaForPebi* pebiPvtPara = pDataInstance->getPebiPvtPara(); nmDataPvtParaForPebi* pebiPvtPara = pDataInstance->getPebiPvtPara();
fillPvtInputByModel(p0, static_cast<NM_SOLVER_MODEL_TYPE>(p0.T), pebiPvtPara, pReservoirData); fillPvtInputByModel(p0, static_cast<NM_SOLVER_MODEL_TYPE>(p0.T), pebiPvtPara, pReservoirData);
@ -684,7 +691,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
} }
HX_NWTM_MODEL(p1, p0, licensePathStd); HX_NWTM_MODEL(p1, p0, licensePathStd);
m_nPebiCount = getPebiCount(); m_nPebiCount = pGridInstance->getPebiCount();
m_nSolveTimeMs = getSolveTime(); m_nSolveTimeMs = getSolveTime();
if(m_autoFitTargetWellName.isEmpty()) { if(m_autoFitTargetWellName.isEmpty()) {
QString sSolveLog = QString("PEBI count: %1, solve time: %2 ms") QString sSolveLog = QString("PEBI count: %1, solve time: %2 ms")

@ -171,6 +171,7 @@ nmCalculationPebiGrid* nmCalculationPebiGrid::getInstance()
} }
nmCalculationPebiGrid::nmCalculationPebiGrid() nmCalculationPebiGrid::nmCalculationPebiGrid()
: m_nPebiCount(-1)
{ {
// 默认值来自HX_NWTM_GRID_INPUT构造函数 // 默认值来自HX_NWTM_GRID_INPUT构造函数
m_dGridControl = p0.GridControl; m_dGridControl = p0.GridControl;
@ -223,6 +224,7 @@ void nmCalculationPebiGrid::clearGridData()
p1 = HX_NWTM_GRID_OUTPUT1(); p1 = HX_NWTM_GRID_OUTPUT1();
p2 = HX_NWTM_GRID_OUTPUT2(); p2 = HX_NWTM_GRID_OUTPUT2();
m_dGridControl = p0.GridControl; m_dGridControl = p0.GridControl;
m_nPebiCount = -1;
} }
void nmCalculationPebiGrid::logInputParameters(const HX_NWTM_GRID_INPUT& input) void nmCalculationPebiGrid::logInputParameters(const HX_NWTM_GRID_INPUT& input)
@ -413,7 +415,8 @@ bool nmCalculationPebiGrid::meshGenPebiWells(HX_NWTM_GRID_INPUT& inputObj)
crack[1] = vFracPoints[0].y(); crack[1] = vFracPoints[0].y();
crack[2] = vFracPoints[1].x(); crack[2] = vFracPoints[1].x();
crack[3] = vFracPoints[1].y(); crack[3] = vFracPoints[1].y();
crack[4] = 1; // 裂缝宽度 // 裂缝宽度直接取井对象中保存的面板参数。
crack[4] = pVerticalFracturedWell->getWidth().getValue().toDouble();
// FC直接取井对象中保存的裂缝导流能力0表示无限导流。 // FC直接取井对象中保存的裂缝导流能力0表示无限导流。
crack[5] = pVerticalFracturedWell->getDfc().getValue().toDouble(); crack[5] = pVerticalFracturedWell->getDfc().getValue().toDouble();
inputObj.FractureVerticalWell.push_back(crack); inputObj.FractureVerticalWell.push_back(crack);
@ -438,7 +441,8 @@ bool nmCalculationPebiGrid::meshGenPebiWells(HX_NWTM_GRID_INPUT& inputObj)
crack[1] = vStartPoint.y(); crack[1] = vStartPoint.y();
crack[2] = vEndPoint.x(); crack[2] = vEndPoint.x();
crack[3] = vEndPoint.y(); crack[3] = vEndPoint.y();
crack[4] = 2; // 裂缝宽度 // 裂缝宽度直接取井对象中保存的面板参数。
crack[4] = pHorizontalFracturedWell->getWidth().getValue().toDouble();
// FC直接取井对象中保存的裂缝导流能力0表示无限导流。 // FC直接取井对象中保存的裂缝导流能力0表示无限导流。
crack[5] = pHorizontalFracturedWell->getDfc().getValue().toDouble(); crack[5] = pHorizontalFracturedWell->getDfc().getValue().toDouble();
@ -717,6 +721,11 @@ HX_NWTM_GRID_OUTPUT2 nmCalculationPebiGrid::getGridOutput2()
return p2; return p2;
} }
int nmCalculationPebiGrid::getPebiCount() const
{
return m_nPebiCount;
}
vtkSmartPointer<vtkUnstructuredGrid> nmCalculationPebiGrid::createPebiUnstructuredGrid(const HX_NWTM_GRID_OUTPUT1& P1) vtkSmartPointer<vtkUnstructuredGrid> nmCalculationPebiGrid::createPebiUnstructuredGrid(const HX_NWTM_GRID_OUTPUT1& P1)
{ {
vtkSmartPointer<vtkUnstructuredGrid> pUnstructuredGrid = vtkSmartPointer<vtkUnstructuredGrid>::New(); vtkSmartPointer<vtkUnstructuredGrid> pUnstructuredGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
@ -893,12 +902,13 @@ bool nmCalculationPebiGrid::generateOutputPara()
// 定义函数指针类型 // 定义函数指针类型
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string); typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
typedef int (*GetIntValueFunc)();
// ===== 调用网格生成器 ===== // ===== 调用网格生成器 =====
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
HX_NWTM_GRID_Func HX_NWTM_GRID = (HX_NWTM_GRID_Func)GetProcAddress(dll, "HX_NWTM_GRID"); HX_NWTM_GRID_Func HX_NWTM_GRID = (HX_NWTM_GRID_Func)GetProcAddress(dll, "HX_NWTM_GRID");
GetIntValueFunc getPebiCount = (GetIntValueFunc)GetProcAddress(dll, "getPEBInum");
if(!HX_NWTM_GRID) { if(!HX_NWTM_GRID || !getPebiCount) {
DWORD error = GetLastError(); DWORD error = GetLastError();
printf("Failed to resolve function. Error code: %lu\n", error); printf("Failed to resolve function. Error code: %lu\n", error);
FreeLibrary(dll); FreeLibrary(dll);
@ -910,6 +920,7 @@ bool nmCalculationPebiGrid::generateOutputPara()
p0 = HX_NWTM_GRID_INPUT(); p0 = HX_NWTM_GRID_INPUT();
p1 = HX_NWTM_GRID_OUTPUT1(); p1 = HX_NWTM_GRID_OUTPUT1();
p2 = HX_NWTM_GRID_OUTPUT2(); p2 = HX_NWTM_GRID_OUTPUT2();
m_nPebiCount = -1;
// PEBI网格划分输入参数GridControl来自界面 // PEBI网格划分输入参数GridControl来自界面
p0.GridControl = m_dGridControl; p0.GridControl = m_dGridControl;
@ -1005,6 +1016,14 @@ bool nmCalculationPebiGrid::generateOutputPara()
} }
} }
// 油水两相算例按定产油量处理导入流量传给qoqg和qw使用等长零数组。
if(solverModelType == SMT_Oil_Water_TwoPhase) {
for(size_t wellIdx = 0; wellIdx < scene.Rate.t.size(); ++wellIdx) {
scene.Rate.qg[wellIdx].assign(scene.Rate.t[wellIdx].size(), 0.0);
scene.Rate.qw[wellIdx].assign(scene.Rate.t[wellIdx].size(), 0.0);
}
}
// 3.3 CS井筒参数 // 3.3 CS井筒参数
scene.CS.C.resize(order.size()); scene.CS.C.resize(order.size());
scene.CS.S.resize(order.size()); scene.CS.S.resize(order.size());
@ -1167,6 +1186,9 @@ bool nmCalculationPebiGrid::generateOutputPara()
HX_NWTM_GRID(p1, p2, p0, licensePathStd); HX_NWTM_GRID(p1, p2, p0, licensePathStd);
// getPEBInum的计数在HX_NWTM_GRID阶段生成必须在卸载DLL前保存。
m_nPebiCount = getPebiCount();
} catch(const std::exception& e) { } catch(const std::exception& e) {
zxLogInstance::getInstance()->writeLogF(QString("C++ Exception: %1").arg(e.what())); zxLogInstance::getInstance()->writeLogF(QString("C++ Exception: %1").arg(e.what()));
logCurrentState(); logCurrentState();

@ -0,0 +1,700 @@
#include "nmPebiExampleCalculator.h"
#include "pch.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataPvtParaForPebi.h"
#include <QApplication>
#include <QCoreApplication>
#include <QDateTime>
#include <QDesktopServices>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QInputDialog>
#include <QMessageBox>
#include <QStringList>
#include <QTextStream>
#include <exception>
namespace
{
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&,
HX_NWTM_GRID_OUTPUT2&,
const HX_NWTM_GRID_INPUT&,
std::string);
typedef void (*HX_NWTM_MODEL_Func)(HX_NWTM_MODEL_OUTPUT&,
const HX_NWTM_MODEL_INPUT&,
std::string);
QStringList exampleNames()
{
QStringList names;
names << QString::fromLocal8Bit("1. 油单相常数PVT一口多段压裂水平井")
<< QString::fromLocal8Bit("2. 油单相常数PVT五口混合井")
<< QString::fromLocal8Bit("3. 油单相常数PVT五十口直井")
<< QString::fromLocal8Bit("4. 油单相变化PVT一口多段压裂水平井")
<< QString::fromLocal8Bit("5. 水单相常数PVT一口多段压裂水平井")
<< QString::fromLocal8Bit("6. 水单相变化PVT一口多段压裂水平井")
<< QString::fromLocal8Bit("7. 气单相变化PVT一口多段压裂水平井")
<< QString::fromLocal8Bit("8. 气单相拟压力(一口多段压裂水平井)")
<< QString::fromLocal8Bit("9. 油水两相(一口多段压裂水平井)");
return names;
}
dVec1 makePoint(double x, double y, double value)
{
dVec1 point(3);
point[0] = x;
point[1] = y;
point[2] = value;
return point;
}
dVec1 makeLine(double x0, double y0, double x1, double y1)
{
dVec1 line(4);
line[0] = x0;
line[1] = y0;
line[2] = x1;
line[3] = y1;
return line;
}
dVec1 makeFracture(double x0, double y0, double x1, double y1,
double halfWidth, double conductivity)
{
dVec1 fracture(6);
fracture[0] = x0;
fracture[1] = y0;
fracture[2] = x1;
fracture[3] = y1;
fracture[4] = halfWidth;
fracture[5] = conductivity;
return fracture;
}
void initializeGridInput(HX_NWTM_GRID_INPUT& input, double halfSize)
{
// 构造函数带有默认井数据,算例计算前必须全部清空后重新组装。
input.Boundary.clear();
input.VerticalWell.clear();
input.HorizontalWell.clear();
input.FractureVerticalWell.clear();
input.MultistageFracturedHorizontalWell.clear();
input.InclinedWell.clear();
input.Fault.clear();
input.Boundary.push_back(makeLine(-halfSize, -halfSize, -halfSize, halfSize));
input.Boundary.push_back(makeLine(-halfSize, halfSize, halfSize, halfSize));
input.Boundary.push_back(makeLine(halfSize, halfSize, halfSize, -halfSize));
input.Boundary.push_back(makeLine(halfSize, -halfSize, -halfSize, -halfSize));
input.GridControl = 150.0;
input.D = 2;
}
void setupSingleWellGrid(HX_NWTM_GRID_INPUT& input)
{
initializeGridInput(input, 1500.0);
// 使用main.cpp中单口多段压裂水平井的五条裂缝参数。
dVec2 fractures;
fractures.push_back(makeFracture(-400.0, -200.0, -400.0, 200.0, 0.05, 100.0));
fractures.push_back(makeFracture(-200.0, -200.0, -200.0, 200.0, 0.05, 100.0));
fractures.push_back(makeFracture(0.0, -200.0, 0.0, 200.0, 0.05, 100.0));
fractures.push_back(makeFracture(200.0, -200.0, 200.0, 200.0, 0.05, 100.0));
fractures.push_back(makeFracture(400.0, -200.0, 400.0, 200.0, 0.05, 100.0));
input.MultistageFracturedHorizontalWell.push_back(fractures);
}
void setupFiveWellGrid(HX_NWTM_GRID_INPUT& input)
{
initializeGridInput(input, 1500.0);
input.VerticalWell.push_back(makePoint(0.0, 0.0, 0.1));
input.VerticalWell.push_back(makePoint(1000.0, 1000.0, 0.1));
input.VerticalWell.push_back(makePoint(-1000.0, -1000.0, 0.1));
input.FractureVerticalWell.push_back(
makeFracture(-200.0, -200.0, 200.0, -200.0, 0.05, 0.0));
dVec2 fractures;
fractures.push_back(makeFracture(-600.0, 600.0, -400.0, 600.0, 0.1, 0.0));
fractures.push_back(makeFracture(-600.0, 400.0, -400.0, 400.0, 0.1, 0.0));
fractures.push_back(makeFracture(-600.0, 200.0, -400.0, 200.0, 0.1, 0.0));
input.MultistageFracturedHorizontalWell.push_back(fractures);
input.Fault.push_back(makeLine(-500.0, 1000.0, 500.0, 500.0));
}
void setupFiftyWellGrid(HX_NWTM_GRID_INPUT& input)
{
initializeGridInput(input, 1000.0);
static const double wells[50][2] = {
{766.32207856101854, -408.4253100821785},
{-359.11086885711029, -242.17185988435722},
{-396.28113134064483, 618.18974488936828},
{-844.38929572547772, 859.25724767620932},
{-631.69279373636357, 620.26791301684102},
{-858.84439780240791, 333.48071142559911},
{-844.38929572547772, 609.87707237947734},
{-639.95285206603785, 867.56992018610026},
{764.25706397860017, -190.2176566975379},
{-410.73623341757479, 867.56992018610026},
{-121.63419187897284, 98.647713021176514},
{-152.6094106152517, 364.65323333769061},
{-152.6094106152517, 620.26791301684102},
{-361.17588343952877, 102.80404927612199},
{-844.38929572547772, -231.78101924699342},
{-156.73943978008879, 875.88259269599121},
{138.55764550576896, 890.42976958830059},
{446.24481828613784, -439.59783199426988},
{-846.45431030789632, 61.240686726666581},
{-142.28433770315883, -223.46834673710214},
{-375.63098551645885, -472.84852203383423},
{-602.78258958250342, 354.2623927003267},
{431.78971620920788, 624.42424927178672},
{-834.06422281338484, -474.92669016130685},
{-836.12923739580344, -747.16671486023938},
{813.81741395664631, -693.13434354594744},
{458.63490578064966, -718.07236107562062},
{409.07455580260353, 873.80442456851847},
{124.10254342883854, -454.14500888657915},
{138.55764550576896, 96.569544893703778},
{101.38738302223419, 611.95524050694985},
{-373.56597093404037, 360.49689708274514},
{-604.8476041649219, 79.944199873921661},
{-611.0426479121777, -219.31201048215667},
{448.30983286855667, -202.68666546237455},
{735.34685982474002, 880.03892895093713},
{107.58242676948976, -724.30686545803894},
{150.94773300028032, -196.45216107995623},
{762.19204939618135, 113.19488991348589},
{-125.76422104381015, -470.77035390636138},
{452.43986203339387, 366.73140146516357},
{-350.85081052743578, -751.32305111518485},
{122.03752884641995, 354.2623927003267},
{-608.9776333297591, -447.91050450416094},
{407.0095412201847, 113.19488991348589},
{731.21683065990283, 636.89325803662314},
{-121.63419187897284, -724.30686545803894},
{289.3037100223255, -925.88917382289742},
{737.41187440715862, 366.73140146516357},
{-611.0426479121777, -730.54136984045726}
};
for(int i = 0; i < 50; ++i) {
input.VerticalWell.push_back(makePoint(wells[i][0], wells[i][1], 0.108));
}
}
void setupGridForExample(int exampleIndex, HX_NWTM_GRID_INPUT& input)
{
if(exampleIndex == 2) {
setupFiveWellGrid(input);
} else if(exampleIndex == 3) {
setupFiftyWellGrid(input);
} else {
setupSingleWellGrid(input);
}
}
void resetWellData(HX_NWTM_MODEL_INPUT& input, int wellCount)
{
// 所有相态统一按实际井数重建外层数组,避免沿用接口构造函数中的五井默认值。
input.Rate.t.assign(wellCount, dVec1());
input.Rate.qo.assign(wellCount, dVec1());
input.Rate.qg.assign(wellCount, dVec1());
input.Rate.qw.assign(wellCount, dVec1());
input.Pressure.t.clear();
input.Pressure.p.clear();
input.CS.C.assign(wellCount, 0.0);
input.CS.S.assign(wellCount, 0.0);
}
void resizeRateStep(HX_NWTM_MODEL_INPUT& input, int wellIndex, int stepCount)
{
input.Rate.t[wellIndex].assign(stepCount, 0.0);
input.Rate.qo[wellIndex].assign(stepCount, 0.0);
input.Rate.qg[wellIndex].assign(stepCount, 0.0);
input.Rate.qw[wellIndex].assign(stepCount, 0.0);
}
void setPressureAxis(HX_NWTM_MODEL_INPUT& input)
{
input.PVT.p.assign(200, 0.0);
for(int i = 0; i < 200; ++i) {
input.PVT.p[i] = i + 1.0;
}
}
void setBase(HX_NWTM_MODEL_INPUT& input, int cellCount,
double permeability, double porosity, double thickness)
{
input.Base.k.assign(cellCount, permeability);
input.Base.phi.assign(cellCount, porosity);
input.Base.h.assign(cellCount, thickness);
input.Base.d = 1.05;
input.Base.dt_Min = 0.0025;
input.Base.dt_Max = 12.5;
}
void setupExample1(HX_NWTM_MODEL_INPUT& input, int cellCount)
{
input.T = 1;
resetWellData(input, 1);
resizeRateStep(input, 0, 2);
input.Rate.t[0][0] = 2000.0;
input.Rate.t[0][1] = 500.0;
input.Rate.qo[0][0] = 20.0;
input.PVT.Bo.assign(200, 1.2);
input.PVT.miuo.assign(200, 0.5);
setPressureAxis(input);
input.Base.Pi = 40.0;
input.Base.Cti = 1e-3;
setBase(input, cellCount, 0.001, 0.1, 10.0);
}
void setupExample2(HX_NWTM_MODEL_INPUT& input, int cellCount)
{
input.T = 1;
resetWellData(input, 5);
resizeRateStep(input, 0, 2);
input.Rate.t[0][0] = 2000.0;
input.Rate.t[0][1] = 500.0;
input.Rate.qo[0][0] = 10.0;
resizeRateStep(input, 1, 0);
resizeRateStep(input, 2, 3);
input.Rate.t[2][0] = 1000.0;
input.Rate.t[2][1] = 1000.0;
input.Rate.t[2][2] = 500.0;
input.Rate.qo[2][0] = 30.0;
input.Rate.qo[2][1] = 40.0;
input.Rate.qo[2][2] = 20.0;
resizeRateStep(input, 3, 2);
input.Rate.t[3][0] = 1500.0;
input.Rate.t[3][1] = 1000.0;
input.Rate.qo[3][0] = 30.0;
input.Rate.qo[3][1] = 20.0;
resizeRateStep(input, 4, 2);
input.Rate.t[4][0] = 1000.0;
input.Rate.t[4][1] = 1500.0;
input.Rate.qo[4][0] = -50.0;
input.Rate.qo[4][1] = -60.0;
input.CS.C.assign(5, 0.1);
input.CS.S.assign(5, 0.1);
setPressureAxis(input);
input.PVT.Bo.assign(200, 1.2);
input.PVT.miuo.assign(200, 0.5);
input.Base.Pi = 40.0;
input.Base.Cti = 1e-3;
setBase(input, cellCount, 0.001, 0.1, 10.0);
}
void setupExample3(HX_NWTM_MODEL_INPUT& input, int cellCount)
{
input.T = 1;
resetWellData(input, 50);
for(int wellIndex = 0; wellIndex < 50; ++wellIndex) {
resizeRateStep(input, wellIndex, 5);
input.Rate.t[wellIndex][0] = 12.0;
input.Rate.t[wellIndex][1] = 12.0;
input.Rate.t[wellIndex][2] = 12.0;
input.Rate.t[wellIndex][3] = 48.0;
input.Rate.t[wellIndex][4] = 72.0;
input.Rate.qo[wellIndex][0] = 158.98699999999999;
input.Rate.qo[wellIndex][1] = 190.785;
input.Rate.qo[wellIndex][2] = 222.58199999999999;
input.Rate.qo[wellIndex][3] = 238.48099999999999;
}
input.CS.C.assign(50, 0.01);
input.CS.S.assign(50, 0.0);
setPressureAxis(input);
input.PVT.Bo.assign(200, 1.07);
input.PVT.miuo.assign(200, 0.79);
input.Base.Pi = 40.0;
input.Base.Cti = 0.43e-3;
setBase(input, cellCount, 0.025, 0.1, 10.0);
}
void setupSinglePhaseExample(HX_NWTM_MODEL_INPUT& input, int exampleIndex,
int cellCount)
{
resetWellData(input, 1);
resizeRateStep(input, 0, 2);
input.Rate.t[0][0] = 2000.0;
input.Rate.t[0][1] = 500.0;
input.CS.C[0] = 0.1;
input.CS.S[0] = 0.1;
if(exampleIndex == 4) {
input.T = 2;
input.Rate.qo[0][0] = 10.0;
} else if(exampleIndex == 5 || exampleIndex == 6) {
input.T = exampleIndex == 5 ? 3 : 4;
input.Rate.qw[0][0] = -10.0;
if(exampleIndex == 5) {
setPressureAxis(input);
input.PVT.Bw.assign(200, 1.05);
input.PVT.miuw.assign(200, 0.8);
}
} else {
input.T = exampleIndex == 7 ? 5 : 6;
input.Rate.qg[0][0] = 50000.0;
}
input.Base.Pi = 40.0;
input.Base.Cf = 1e-3;
if(exampleIndex == 5) {
input.Base.Cti = 1e-3;
}
setBase(input, cellCount, 0.001, 0.1, 10.0);
}
void setupExample9(HX_NWTM_MODEL_INPUT& input, int cellCount)
{
input.T = 8;
resetWellData(input, 1);
resizeRateStep(input, 0, 2);
input.Rate.t[0][0] = 2000.0;
input.Rate.t[0][1] = 500.0;
input.Rate.qo[0][0] = 20.0;
input.CS.C[0] = 0.1;
input.CS.S[0] = 0.1;
input.Base.Pi = 40.0;
input.Base.Cf = 1e-4;
input.Base.Swi = 0.2;
setBase(input, cellCount, 0.001, 0.1, 10.0);
}
void setupModelForExample(int exampleIndex, const HX_NWTM_GRID_OUTPUT2& grid,
HX_NWTM_MODEL_INPUT& input)
{
const int cellCount = static_cast<int>(grid.Trinodexy.size());
if(exampleIndex == 1) {
setupExample1(input, cellCount);
} else if(exampleIndex == 2) {
setupExample2(input, cellCount);
} else if(exampleIndex == 3) {
setupExample3(input, cellCount);
} else if(exampleIndex >= 4 && exampleIndex <= 8) {
setupSinglePhaseExample(input, exampleIndex, cellCount);
} else {
setupExample9(input, cellCount);
}
}
bool hasPhasePvt(const QVector<double>& pressure,
const QVector<double>& volumeFactor,
const QVector<double>& compressibility,
const QVector<double>& viscosity)
{
return !pressure.isEmpty()
&& pressure.size() == volumeFactor.size()
&& pressure.size() == compressibility.size()
&& pressure.size() == viscosity.size();
}
bool loadFrameworkPvt(int exampleIndex,
HX_NWTM_MODEL_INPUT& input,
QString& error)
{
// 常数PVT算例继续使用main.cpp中的固定数据。
if(exampleIndex == 1 || exampleIndex == 2
|| exampleIndex == 3 || exampleIndex == 5) {
return true;
}
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataPvtParaForPebi* pvt = dataManager == NULL
? NULL : dataManager->getPebiPvtPara();
if(pvt == NULL) {
error = QString::fromLocal8Bit("当前分析中没有可用的PVT数据。");
return false;
}
const QVector<double>& pressure = pvt->getPressure();
if(exampleIndex == 4) {
if(!hasPhasePvt(pressure, pvt->getBo(), pvt->getCo(), pvt->getMiuo())) {
error = QString::fromLocal8Bit("当前分析中的油相变化PVT数据不完整。");
return false;
}
input.PVT.p = pressure.toStdVector();
input.PVT.Bo = pvt->getBo().toStdVector();
input.PVT.Co = pvt->getCo().toStdVector();
input.PVT.miuo = pvt->getMiuo().toStdVector();
} else if(exampleIndex == 6) {
if(!hasPhasePvt(pressure, pvt->getBw(), pvt->getCw(), pvt->getMiuw())) {
error = QString::fromLocal8Bit("当前分析中的水相变化PVT数据不完整。");
return false;
}
input.PVT.p = pressure.toStdVector();
input.PVT.Bw = pvt->getBw().toStdVector();
input.PVT.Cw = pvt->getCw().toStdVector();
input.PVT.miuw = pvt->getMiuw().toStdVector();
} else if(exampleIndex == 7 || exampleIndex == 8) {
if(!hasPhasePvt(pressure, pvt->getBg(), pvt->getCg(), pvt->getMiug())) {
error = QString::fromLocal8Bit("当前分析中的气相变化PVT数据不完整。");
return false;
}
input.PVT.p = pressure.toStdVector();
input.PVT.Bg = pvt->getBg().toStdVector();
input.PVT.Cg = pvt->getCg().toStdVector();
input.PVT.miug = pvt->getMiug().toStdVector();
} else if(exampleIndex == 9) {
const bool oilWaterPvtValid = !pressure.isEmpty()
&& pressure.size() == pvt->getBo().size()
&& pressure.size() == pvt->getMiuo().size()
&& pressure.size() == pvt->getBw().size()
&& pressure.size() == pvt->getMiuw().size();
const bool relativePermeabilityValid = !pvt->getSo().isEmpty()
&& pvt->getSo().size() == pvt->getKro().size()
&& pvt->getSo().size() == pvt->getKrw().size();
if(!oilWaterPvtValid || !relativePermeabilityValid) {
error = QString::fromLocal8Bit("当前分析中的油水两相PVT或相渗数据不完整。");
return false;
}
input.PVT.p = pressure.toStdVector();
input.PVT.Bo = pvt->getBo().toStdVector();
input.PVT.miuo = pvt->getMiuo().toStdVector();
input.PVT.Bw = pvt->getBw().toStdVector();
input.PVT.miuw = pvt->getMiuw().toStdVector();
input.PVT.So = pvt->getSo().toStdVector();
input.PVT.Kro = pvt->getKro().toStdVector();
input.PVT.Krw = pvt->getKrw().toStdVector();
}
return true;
}
QString numberText(double value)
{
return QString::number(value, 'g', 16);
}
bool writePressureFile(const QString& filePath,
const HX_NWTM_MODEL_OUTPUT& output,
size_t wellIndex,
QString& error)
{
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
error = QString::fromLocal8Bit("无法创建压力文件:%1").arg(filePath);
return false;
}
QTextStream stream(&file);
stream.setCodec("UTF-8");
for(size_t timeIndex = 0; timeIndex < output.t.size(); ++timeIndex) {
stream << numberText(output.t[timeIndex]) << " ";
if(timeIndex < output.pw[wellIndex].size()) {
stream << numberText(output.pw[wellIndex][timeIndex]);
}
stream << "\n";
}
return true;
}
bool writeFlowFile(const QString& filePath,
const HX_NWTM_MODEL_INPUT& input,
int exampleIndex,
size_t wellIndex,
QString& error)
{
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
error = QString::fromLocal8Bit("无法创建流量文件:%1").arg(filePath);
return false;
}
QTextStream stream(&file);
stream.setCodec("UTF-8");
stream << "0 0\n";
// 按算例相态选择实际使用的油、气或水流量数组。
const dVec1* rates = &input.Rate.qo[wellIndex];
if(exampleIndex == 5 || exampleIndex == 6) {
rates = &input.Rate.qw[wellIndex];
} else if(exampleIndex == 7 || exampleIndex == 8) {
rates = &input.Rate.qg[wellIndex];
}
for(size_t stepIndex = 0; stepIndex < input.Rate.t[wellIndex].size(); ++stepIndex) {
stream << numberText(input.Rate.t[wellIndex][stepIndex]) << " ";
if(stepIndex < rates->size()) {
stream << numberText((*rates)[stepIndex]);
}
stream << "\n";
}
return true;
}
bool saveResults(int exampleIndex,
const HX_NWTM_MODEL_INPUT& input,
const HX_NWTM_MODEL_OUTPUT& output,
int& savedWellCount,
QString& error)
{
QString desktopPath = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
if(desktopPath.isEmpty()) {
desktopPath = QDir::home().filePath("Desktop");
}
if(!QDir(desktopPath).exists()) {
error = QString::fromLocal8Bit("桌面目录不存在:%1").arg(desktopPath);
return false;
}
if(output.pw.size() != input.Rate.t.size()) {
error = QString::fromLocal8Bit("求解器返回的压力井数与输入井数不一致。");
return false;
}
const QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
QStringList createdFiles;
savedWellCount = static_cast<int>(input.Rate.t.size());
for(int wellIndex = 0; wellIndex < savedWellCount; ++wellIndex) {
const QString pressurePath = QDir(desktopPath).filePath(
QString("PEBI_Model_%1_Well_%2_Pressure_%3.txt")
.arg(exampleIndex).arg(wellIndex + 1).arg(timestamp));
const QString flowPath = QDir(desktopPath).filePath(
QString("PEBI_Model_%1_Well_%2_Flow_%3.txt")
.arg(exampleIndex).arg(wellIndex + 1).arg(timestamp));
if(!writePressureFile(pressurePath, output, wellIndex, error)) {
break;
}
createdFiles.append(pressurePath);
if(!writeFlowFile(flowPath, input, exampleIndex, wellIndex, error)) {
break;
}
createdFiles.append(flowPath);
}
if(!error.isEmpty()) {
for(int fileIndex = 0; fileIndex < createdFiles.size(); ++fileIndex) {
QFile::remove(createdFiles[fileIndex]);
}
savedWellCount = 0;
return false;
}
return true;
}
bool calculateExample(int exampleIndex,
int& savedWellCount,
QString& error)
{
const QString dllPath = QDir(QCoreApplication::applicationDirPath()).filePath("HX_NWTM.dll");
if(!QFileInfo(dllPath).exists()) {
error = QString::fromLocal8Bit("未找到PEBI求解器%1").arg(dllPath);
return false;
}
HMODULE dll = LoadLibraryW(reinterpret_cast<const wchar_t*>(dllPath.utf16()));
if(dll == NULL) {
error = QString::fromLocal8Bit("加载PEBI求解器失败错误码%1").arg(GetLastError());
return false;
}
HX_NWTM_GRID_Func gridFunction = reinterpret_cast<HX_NWTM_GRID_Func>(
GetProcAddress(dll, "HX_NWTM_GRID"));
HX_NWTM_MODEL_Func modelFunction = reinterpret_cast<HX_NWTM_MODEL_Func>(
GetProcAddress(dll, "HX_NWTM_MODEL"));
if(gridFunction == NULL || modelFunction == NULL) {
error = QString::fromLocal8Bit("PEBI求解器缺少网格或模型计算接口。");
FreeLibrary(dll);
return false;
}
const QString licensePath = QDir::cleanPath(
QDir(QCoreApplication::applicationDirPath()).filePath("../Res/license/HXNWTM_license.dat"));
if(!QFileInfo(licensePath).exists()) {
error = QString::fromLocal8Bit("未找到PEBI许可证%1").arg(licensePath);
FreeLibrary(dll);
return false;
}
try {
// 必须先由所选算例的井和边界生成网格,再用网格输出构造模型输入。
HX_NWTM_GRID_INPUT gridInput;
HX_NWTM_GRID_OUTPUT1 gridOutput1;
HX_NWTM_GRID_OUTPUT2 gridOutput2;
setupGridForExample(exampleIndex, gridInput);
gridFunction(gridOutput1, gridOutput2, gridInput, licensePath.toStdString());
if(gridOutput2.Trinodexy.empty()) {
error = QString::fromLocal8Bit("PEBI网格生成失败未返回有效网格单元。");
FreeLibrary(dll);
return false;
}
HX_NWTM_MODEL_INPUT modelInput(gridOutput2);
HX_NWTM_MODEL_OUTPUT modelOutput;
setupModelForExample(exampleIndex, gridOutput2, modelInput);
if(!loadFrameworkPvt(exampleIndex, modelInput, error)) {
FreeLibrary(dll);
return false;
}
modelFunction(modelOutput, modelInput, licensePath.toStdString());
if(modelOutput.t.empty() || modelOutput.pw.empty()) {
error = QString::fromLocal8Bit("PEBI模型计算失败未返回压力数据。");
FreeLibrary(dll);
return false;
}
const bool saved = saveResults(exampleIndex, modelInput, modelOutput,
savedWellCount, error);
FreeLibrary(dll);
return saved;
} catch(const std::exception& exception) {
error = QString::fromLocal8Bit("PEBI算例计算异常%1")
.arg(QString::fromLocal8Bit(exception.what()));
} catch(...) {
error = QString::fromLocal8Bit("PEBI算例计算发生未知异常。");
}
FreeLibrary(dll);
return false;
}
}
nmPebiExampleCalculator::nmPebiExampleCalculator(QWidget* pParent)
: m_pParent(pParent)
{
}
void nmPebiExampleCalculator::run()
{
bool accepted = false;
const QString selected = QInputDialog::getItem(
m_pParent,
QString::fromLocal8Bit("PEBI算例计算"),
QString::fromLocal8Bit("请选择计算模型:"),
exampleNames(), 0, false, &accepted);
if(!accepted || selected.isEmpty()) {
return;
}
const int exampleIndex = exampleNames().indexOf(selected) + 1;
if(exampleIndex < 1 || exampleIndex > 9) {
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
int savedWellCount = 0;
QString error;
const bool success = calculateExample(exampleIndex, savedWellCount, error);
QApplication::restoreOverrideCursor();
if(!success) {
QMessageBox::warning(m_pParent, QString::fromLocal8Bit("PEBI算例计算"), error);
return;
}
QMessageBox::information(
m_pParent,
QString::fromLocal8Bit("PEBI算例计算"),
QString::fromLocal8Bit("计算完成,已按井保存到桌面,共%1口井、%2个文件。")
.arg(savedWellCount).arg(savedWellCount * 2));
}

@ -44,6 +44,7 @@
#include "nmWxParameterProperty.h" #include "nmWxParameterProperty.h"
#include "nmCalculationDllPebiSolverTask.h" #include "nmCalculationDllPebiSolverTask.h"
#include "nmCalculationUtils.h" #include "nmCalculationUtils.h"
#include "nmPebiExampleCalculator.h"
#include "nmObjPointWell.h" #include "nmObjPointWell.h"
@ -493,6 +494,14 @@ bool nmSubWndMain::runCmdBy(QString sName, QString sID)
this->displaySetting(); this->displaySetting();
return true; return true;
case 5125:
{
// 临时复用打印按钮运行PEBI固定算例不读取当前工区模型参数。
nmPebiExampleCalculator calculator(this);
calculator.run();
return true;
}
/* /*
case 5117: case 5117:
this->initData(); this->initData();
@ -2058,6 +2067,7 @@ void nmSubWndMain::on_solverTaskFinished(bool isSuccessed)
{ {
// 线程完成后清空两个持有点:当前窗口指针和全局运行中指针。 // 线程完成后清空两个持有点:当前窗口指针和全局运行中指针。
nmCalculationDllPebiSolverTask* pTask = qobject_cast<nmCalculationDllPebiSolverTask*>(sender()); nmCalculationDllPebiSolverTask* pTask = qobject_cast<nmCalculationDllPebiSolverTask*>(sender());
int nPebiCount = pTask != nullptr ? pTask->getPebiCount() : -1;
int nSolveTimeMs = pTask != nullptr ? pTask->getSolveTimeMs() : -1; int nSolveTimeMs = pTask != nullptr ? pTask->getSolveTimeMs() : -1;
if(pTask == m_pSolverTask) { if(pTask == m_pSolverTask) {
m_pSolverTask = nullptr; m_pSolverTask = nullptr;
@ -2070,9 +2080,11 @@ void nmSubWndMain::on_solverTaskFinished(bool isSuccessed)
NM_Calculation_Result result = isSuccessed ? NM_Calculation_Result_Success : NM_Calculation_Result_Fail; NM_Calculation_Result result = isSuccessed ? NM_Calculation_Result_Success : NM_Calculation_Result_Fail;
this->on_calculationFinished(result); this->on_calculationFinished(result);
if(isSuccessed && nSolveTimeMs >= 0) { if(isSuccessed && nPebiCount >= 0 && nSolveTimeMs >= 0) {
QMessageBox::information(this, tr("Solver completed"), QMessageBox::information(this, tr("Solver completed"),
tr("Solve time: %1 ms").arg(nSolveTimeMs)); tr("Grid count: %1\nSolve time: %2 ms")
.arg(nPebiCount)
.arg(nSolveTimeMs));
} }
} }

Loading…
Cancel
Save