// netgen-2010.cpp : 定义控制台应用程序的入口点。 // #include #include "../include/singlePhaseSolver.h" #include "windows.h" #include #include #include #include #include #include #include #include #include using namespace std; // 添加全局变量用于进度跟踪 int g_timeStep = 0; int g_totalSteps = 100; volatile bool g_calculationRunning = true; UINT_PTR g_timerID = 0; // 添加一个进度条显示函数 void ShowProgressBar(int timeStep, int totalSteps, int barWidth = 50) { float progress = (float)timeStep / totalSteps; int pos = barWidth * progress; std::cout << "["; for (int i = 0; i < barWidth; ++i) { if (i < pos) { std::cout << "="; } else if (i == pos) { std::cout << ">"; } else { std::cout << " "; } } std::cout << "] " << int(progress * 100.0) << "% (" << timeStep << "/" << totalSteps << ")\r"; std::cout.flush(); } // 添加定时器回调函数 VOID CALLBACK ProgressTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { if (g_calculationRunning && g_totalSteps > 0) { ShowProgressBar(g_timeStep, g_totalSteps); } } // 添加油藏类型枚举 enum RESERVOIR_TYPE { OIL_RESERVOIR = 0, // 油藏 GAS_RESERVOIR = 1 // 气藏 }; void Ng2VTKWithData(Ng_Mesh *mesh, std::vector vecField, std::string filename) { std::cout << "Convert Ng_Mesh to vtk legacy file version of 4.2" << std::endl; int i, nseg, ne, np, matnum; int nodes[3]; double point[2]; // int *EleNodeNum; // double *NodeCoors; np = Ng_GetNP_2D(mesh); ne = Ng_GetNE_2D(mesh); nseg = Ng_GetNSeg_2D(mesh); std::ofstream vtkFile; vtkFile.open(filename); if (!vtkFile.is_open()) { std::cerr << "Error opening file: " << "mesh.vtk" << std::endl; return; } // Write VTK file header vtkFile << "# vtk DataFile Version 4.1" << std::endl; vtkFile << "Mesh data" << std::endl; vtkFile << "ASCII" << std::endl; vtkFile << "DATASET UNSTRUCTURED_GRID" << std::endl; // Write points vtkFile << "POINTS " << np << " float" << std::endl; for (i = 1; i <= np; i++) { Ng_GetPoint_2D(mesh, i, point); vtkFile << point[0] << " " << point[1] << " " << 0 << std::endl; } // Write cells vtkFile << "CELLS " << ne << " " << ne * 4 << std::endl; std::vector vecMater; for (int i = 1; i <= ne; i++) { Ng_GetElement_2D(mesh, i, nodes, &matnum); vecMater.emplace_back(matnum); vtkFile << 3 << " " << nodes[0] - 1 << " " << nodes[1] - 1 << " " << nodes[2] - 1 << std::endl; } // Write cell types vtkFile << "CELL_TYPES " << ne << std::endl; for (int i = 1; i <= ne; i++) { vtkFile << 5 << std::endl; // Assuming getCellType is a method that returns the VTK cell type } // Write cell data vtkFile << "CELL_DATA " << ne << std::endl; vtkFile << "SCALARS Material int 1" << std::endl; vtkFile << "LOOKUP_TABLE default" << std::endl; for (int i = 0; i < vecMater.size(); i++) { vtkFile << vecMater[i] << std::endl; } // Write point data vtkFile << "POINT_DATA " << np << std::endl; vtkFile << "SCALARS Pressure double 1" << std::endl; vtkFile << "LOOKUP_TABLE default" << std::endl; for (int i = 0; i < vecField.size(); i++) { vtkFile << vecField[i] << std::endl; } vtkFile.close(); } // 辅助函数 string trim(const string &s) { size_t start = s.find_first_not_of(" \t"); size_t end = s.find_last_not_of(" \t"); return (start == string::npos) ? "" : s.substr(start, end - start + 1); } vector split(const string &s, char delimiter) { vector tokens; string token; stringstream iss(s); while (getline(iss, token, delimiter)) { if (!token.empty()) { tokens.push_back(token); } } return tokens; } // 边界类型字符串转枚举值的辅助函数 BOUND_TYPE StringToBoundType(const string &s) { string str = trim(s); transform(str.begin(), str.end(), str.begin(), ::toupper); if (str == "CONST_PRESSURE") { return CONST_PRESSURE; } if (str == "CONST_RATE") { return CONST_RATE; } if (str == "CLOSED") { return CLOSED; } throw runtime_error("未知边界条件类型: " + str); } // 井类型字符串转枚举值的辅助函数 WELL_TYPE StringToWellType(const string &s) { string str = trim(s); transform(str.begin(), str.end(), str.begin(), ::toupper); if (str == "VERTICAL") { return VERTICAL; } if (str == "VERTICAL_FRACTURED") { return VERTICAL_FRACTURED; } if (str == "MULTI_FRACED_HORIZONTAL") { return MULTI_FRACED_HORIZONTAL; } throw runtime_error("未知井类型: " + str); } void ReadParameters(const string &filename, CBoundShape &shape, vector& wells, vector& limits, vector& faults, vector& fracs, double pBaseData[8], TimeDis &timeDis, RESERVOIR_TYPE &reservoirType, double& reservoirTemp, double gasComponents[32]) { ifstream fin(filename.c_str()); if (!fin) { throw runtime_error("无法打开文件: " + filename); } string line, section; int currentWell = -1; vector tempWells; vector tempLimits; CBoundPolygon *currentLimit = nullptr; vector tempFaults, tempFracs; // 设置默认值 reservoirType = OIL_RESERVOIR; // 默认为油藏 reservoirTemp = 43; // 默认43℃ for (int i = 0; i < 32; i++) { gasComponents[i] = 0.0; // 默认所有组分为0 } // 设置TimeDis默认值 timeDis.nLogNum = 20; // 默认日志数量 timeDis.dTB = 1.0; // 默认时间基准值 while (getline(fin, line)) { line = trim(line); if (line.empty() || line[0] == '/') { // 跳过空行和注释行 continue; } if (line[0] == '[') { section = line.substr(1, line.find(']') - 1); transform(section.begin(), section.end(), section.begin(), ::toupper); currentWell = -1; currentLimit = nullptr; continue; } vector tokens = split(line, ' '); if (tokens.empty()) { continue; } try { if (section == "RESERVOIRBASEDATA") { if (tokens.size() == 9) { // 基础参数 + 油藏类型 for (int i = 0; i < 8; ++i) { pBaseData[i] = atof(tokens[i].c_str()); } // 读取油藏类型 reservoirType = (RESERVOIR_TYPE)atoi(tokens[8].c_str()); } else if (tokens.size() == 8) { // 仅基础参数,默认为油藏 for (int i = 0; i < 8; ++i) { pBaseData[i] = atof(tokens[i].c_str()); } reservoirType = OIL_RESERVOIR; // 默认为油藏 } else { throw runtime_error("油藏基础参数个数不正确"); } } else if (section == "GASRESERVOIRDATA" && reservoirType == GAS_RESERVOIR) { if (tokens[0] == "Temperature" && tokens.size() >= 2) { reservoirTemp = atof(tokens[1].c_str()); } else if (tokens[0] == "Components") { // 等待下一行读取组分数据 } else if (tokens.size() >= 32) { // 气体组分数据,应有32个值 // 确保有足够的数字用于32个组分 int compCount = min(32, (int)tokens.size()); for (int i = 0; i < compCount; ++i) { gasComponents[i] = atof(tokens[i].c_str()); } } } else if (section == "BOUNDSHAPE") { if (tokens[0] == "type") { if (tokens.size() < 2) { throw runtime_error("缺少边界类型"); } if (tokens[1] == "CIRCLE") { shape.boundShape = CIRCLE; } else if (tokens[1] == "POLYGON") { shape.boundShape = POLYGON; } else { throw runtime_error("未知边界类型: " + tokens[1]); } } else if (shape.boundShape == CIRCLE) { // 处理圆形边界参数 if (tokens[0] == "center" && tokens.size() >= 3) { shape.cCenter.x = atof(tokens[1].c_str()); shape.cCenter.y = atof(tokens[2].c_str()); } else if (tokens[0] == "radius" && tokens.size() >= 2) { shape.dRadius = atof(tokens[1].c_str()); } else if (tokens[0] == "boundType" && tokens.size() >= 2) { shape.boundType = StringToBoundType(tokens[1]); } else if (tokens[0] == "dConstPressure" && tokens.size() >= 2) { shape.dConstPressure = atof(tokens[1].c_str()); } } else if (shape.boundShape == POLYGON) { // 处理多边形边界参数 if (tokens[0] == "segments" && tokens.size() >= 2) { shape.nNumSegs = atoi(tokens[1].c_str()); } else if (tokens.size() >= 5) { // 至少需要4个坐标值和1个边界类型 Segment seg; seg.p1 = Point(atof(tokens[0].c_str()), atof(tokens[1].c_str())); seg.p2 = Point(atof(tokens[2].c_str()), atof(tokens[3].c_str())); seg.boundType = StringToBoundType(tokens[4]); // 如果是定压力边界,还需要读取压力值 if (tokens.size() >= 6 && seg.boundType == CONST_PRESSURE) { seg.dConstPressure = atof(tokens[5].c_str()); } shape.vecSegments.push_back(seg); } } } else if (section == "WELLS") { // 开始读取一个新井 if (tokens.size() >= 4 && currentWell == -1) { CBoundWell well; well.wellType = StringToWellType(tokens[0]); // 根据井类型读取不同参数 if (well.wellType == VERTICAL) { if (tokens.size() < 4) { throw runtime_error("垂直井参数不足"); } well.cCenter.x = atof(tokens[1].c_str()); well.cCenter.y = atof(tokens[2].c_str()); well.dRadius = atof(tokens[3].c_str()); tempWells.push_back(well); currentWell = tempWells.size() - 1; } else if (well.wellType == VERTICAL_FRACTURED) { // 垂直压裂井参数 if (tokens.size() < 6) { throw runtime_error("垂直压裂井参数不足"); } well.cCenter.x = atof(tokens[1].c_str()); well.cCenter.y = atof(tokens[2].c_str()); well.dRadius = atof(tokens[3].c_str()); well.dBeta = atof(tokens[4].c_str()); well.dHf = atof(tokens[5].c_str()); tempWells.push_back(well); currentWell = tempWells.size() - 1; } else if (well.wellType == MULTI_FRACED_HORIZONTAL) { // 多段压裂水平井参数,只需要读取cCenter, dRadius, dBeta, dLength, nFracNum, dHf if (tokens.size() < 7) { throw runtime_error("多段压裂水平井参数不足"); } well.cCenter.x = atof(tokens[1].c_str()); well.cCenter.y = atof(tokens[2].c_str()); well.dRadius = atof(tokens[3].c_str()); well.dBeta = atof(tokens[4].c_str()); well.dLength = atof(tokens[5].c_str()); well.nFracNum = atoi(tokens[6].c_str()); if (tokens.size() >= 8) { well.dHf = atof(tokens[7].c_str()); } else { well.dHf = 100.0; // 默认裂缝半长 } // 初始化裂缝数组 if (well.nFracNum > 0) { well.vecFracs.resize(2 * well.nFracNum); // 裂缝的具体位置会在模型中自动计算,这里不需要读取 } tempWells.push_back(well); currentWell = tempWells.size() - 1; } } else if (currentWell >= 0) { CBoundWell &w = tempWells[currentWell]; if (tokens[0] == "bisMultFlow" && tokens.size() >= 2) { w.bisMultFlow = (atoi(tokens[1].c_str()) != 0); } else if (tokens[0] == "dSkin" && tokens.size() >= 2) { w.dSkin = atof(tokens[1].c_str()); } else if (tokens[0] == "dC" && tokens.size() >= 2) { w.dC = atof(tokens[1].c_str()); } else if (tokens[0] == "TimeQ" && tokens.size() >= 2) { int numTimes = atoi(tokens[1].c_str()); w.nTimeNumQ = numTimes; // w.pdTimeQ = new double[numTimes]; // w.pdQ = new double[numTimes]; } else if (tokens.size() == 2 && w.pdTimeQ != nullptr && w.pdQ != nullptr) { // 这里假设TimeQ后的每行数据都是时间和流量对 static int timeIdx = 0; if (timeIdx < w.nTimeNumQ) { w.pdTimeQ[timeIdx] = atof(tokens[0].c_str()); w.pdQ[timeIdx] = atof(tokens[1].c_str()); timeIdx++; // 如果读完了所有时间流量对,重置计数器 if (timeIdx == w.nTimeNumQ) { timeIdx = 0; } } } else if (tokens[0] == "FracFc" && w.wellType == VERTICAL_FRACTURED) { if (tokens.size() - 1 != 2) { throw runtime_error("导流能力数量不匹配"); } for (int i = 0; i < 2; ++i) { w.wFrac[i].dFc = atof(tokens[i + 1].c_str()); } } else if (tokens[0] == "FracFc" && w.wellType == MULTI_FRACED_HORIZONTAL) { if (tokens.size() - 1 != w.nFracNum) { throw runtime_error("导流能力数量不匹配"); } for (int i = 0; i < w.nFracNum; ++i) { w.vecFracs[2 * i].dFc = atof(tokens[i + 1].c_str()); w.vecFracs[2 * i + 1].dFc = atof(tokens[i + 1].c_str()); } } } } else if (section == "LIMITS") { // 修改LIMITS部分,支持更多参数 if (tokens[0] == "segments" && tokens.size() >= 2) { tempLimits.push_back(CBoundPolygon()); currentLimit = &tempLimits.back(); currentLimit->nNumSegs = atoi(tokens[1].c_str()); } else if (currentLimit && tokens[0] == "bAnchor" && tokens.size() >= 2) { currentLimit->bAnchor = (atoi(tokens[1].c_str()) != 0); } else if (currentLimit && tokens[0] == "dComKr" && tokens.size() >= 2) { currentLimit->dComKr = atof(tokens[1].c_str()); } else if (currentLimit && tokens[0] == "dComW" && tokens.size() >= 2) { currentLimit->dComW = atof(tokens[1].c_str()); } else if (currentLimit && tokens.size() == 4) { // 复合区的线段只需要4个坐标值 Segment seg; seg.p1 = Point(atof(tokens[0].c_str()), atof(tokens[1].c_str())); seg.p2 = Point(atof(tokens[2].c_str()), atof(tokens[3].c_str())); // 复合区不需要指定边界类型 currentLimit->vecSegments.push_back(seg); } // 向后兼容旧格式(前两个数字分别表示nNumSegs和bAnchor) else if (tokens.size() == 2 && isdigit(tokens[0][0]) && isdigit(tokens[1][0])) { tempLimits.push_back(CBoundPolygon()); currentLimit = &tempLimits.back(); currentLimit->nNumSegs = atoi(tokens[0].c_str()); currentLimit->bAnchor = (atoi(tokens[1].c_str()) != 0); } } else if (section == "FAULTS") { if (tokens.size() == 4) { Frac f; f.p1 = Point(atof(tokens[0].c_str()), atof(tokens[1].c_str())); f.p2 = Point(atof(tokens[2].c_str()), atof(tokens[3].c_str())); tempFaults.push_back(f); } } else if (section == "FRACS") { if (tokens.size() >= 4) { Frac f; f.p1 = Point(atof(tokens[0].c_str()), atof(tokens[1].c_str())); f.p2 = Point(atof(tokens[2].c_str()), atof(tokens[3].c_str())); // 读取储能比和导流能力 if (tokens.size() >= 5) { f.dW = atof(tokens[4].c_str()); } else { f.dW = 1.0; // 默认储能比为1.0 } if (tokens.size() >= 6) { f.dFc = atof(tokens[5].c_str()); } else { f.dFc = 100.0; // 默认导流能力为100.0 } tempFracs.push_back(f); } } else if (section == "TIMEDIS") { if (tokens[0] == "nLogNum" && tokens.size() >= 2) { timeDis.nLogNum = atoi(tokens[1].c_str()); } else if (tokens[0] == "dTB" && tokens.size() >= 2) { timeDis.dTB = atof(tokens[1].c_str()); } } } catch (const exception &e) { throw runtime_error("解析错误[" + section + "]: " + e.what() + ", 行: " + line); } } // 最终数据校验 if (shape.boundShape == CIRCLE) { if (shape.dRadius <= 0) { throw runtime_error("圆形边界半径必须大于0"); } } else if (shape.boundShape == POLYGON) { if (shape.vecSegments.size() != shape.nNumSegs) { throw runtime_error("多边形边界线段数量不匹配"); } } // 给每个复合区设置默认值(如果未指定),并验证线段数量 for (int i = 0; i < tempLimits.size(); ++i) { if (tempLimits[i].dComKr <= 0) { tempLimits[i].dComKr = 1.0; // 默认渗透率比为1.0 } if (tempLimits[i].dComW <= 0) { tempLimits[i].dComW = 1.0; // 默认孔隙度比为1.0 } // 验证线段数量 if (tempLimits[i].vecSegments.size() != tempLimits[i].nNumSegs) { throw runtime_error("复合区线段数量与指定数量不匹配"); } } // 对气藏参数进行验证 if (reservoirType == GAS_RESERVOIR) { // 检查气体组分总和是否接近1.0 double sum = 0.0; for (int i = 0; i < 32; i++) { sum += gasComponents[i]; } if (sum < (100.0 - 1e-5) || sum > (100.0 + 1e-5)) { // 使用stringstream替代to_string,以兼容VS2010 std::stringstream ss; ss << sum; throw runtime_error("气体组分总和应接近100.0%,当前值为: " + ss.str() + "%"); } } wells = tempWells; limits = tempLimits; faults = tempFaults; fracs = tempFracs; } void PrintWell(const CBoundWell &w) { cout << "井类型: "; switch (w.wellType) { case VERTICAL: cout << "垂直井"; break; case VERTICAL_FRACTURED: cout << "垂直压裂井"; break; case MULTI_FRACED_HORIZONTAL: cout << "多段压裂水平井"; break; } cout << "\n位置: (" << w.cCenter.x << ", " << w.cCenter.y << ")" << "\n半径: " << w.dRadius << "\n流动模式: " << (w.bisMultFlow ? "多级" : "单级") << "\n时间流量数据(" << w.nTimeNumQ << "个):\n"; for (int i = 0; i < w.nTimeNumQ; ++i) { cout << " " << w.pdTimeQ[i] << "hr: " << w.pdQ[i] << " m^3/d\n"; } } // 气体组分名称数组(包含英文名称和化学式) const char* gasComponentNames[32] = { "甲烷(Methane, CH4)", "乙烷(Ethane, C2H6)", "丙烷(Propane, C3H8)", "异丁烷(Isobutane, i-C4H10)", "正丁烷(n-Butane, n-C4H10)", "异戊烷(Isopentane, i-C5H12)", "正戊烷(n-Pentane, n-C5H12)", "己烷(Hexane, C6H14)", "庚烷(Heptane, C7H16)", "辛烷(Octane, C8H18)", "壬烷(Nonane, C9H20)", "葵烷(Decane, C10H22)", "十一烷(Undecane, C11H24)", "十二烷(Dodecane, C12H26)", "十三烷(Tridecane, C13H28)", "十四烷(Tetradecane, C14H30)", "十五烷(Pentadecane, C15H32)", "十六烷(Hexadecane, C16H34)", "十七烷(Heptadecane, C17H36)", "十八烷(Octadecane, C18H38)", "十九烷(Nonadecane, C19H40)", "廿烷(Icosane, C20H42)", "廿一烷(Henicosane, C21H44)", "廿一以上烷(C21+)", "硫化氢(Hydrogen Sulfide, H2S)", "二氧化碳(Carbon Dioxide, CO2)", "氮气(Nitrogen, N2)", "水(Water, H2O)", "氢气(Hydrogen, H2)", "氦气(Helium, He)", "氧气(Oxygen, O2)", "一氧化碳(Carbon Monoxide, CO)" }; // 输出油藏基本信息及类型信息 void OutputReservoirInfo(const double pBaseData[8], RESERVOIR_TYPE reservoirType, double reservoirTemp, const double gasComponents[32], const std::string reservoirPara[8], std::ostream &out = std::cout) { out << "====================== 油藏基本信息 ======================" << std::endl; // 使用预定义的参数名称 for (int i = 0; i < 8; i++) { out << reservoirPara[i] << ": " << pBaseData[i]; // 根据参数类型添加适当的单位 if (i == 0) { out << " MPa"; // 初始压力 } else if (i == 1) { out << " mD"; // 渗透率 } else if (i == 2) { out << " m"; // 厚度 } else if (i == 3) { out << " mPa·s"; // 油粘度 } else if (i == 5) { out << " (分数)"; // 孔隙度 } else if (i == 6) { out << " MPa^(-1)"; // 岩石压缩系数 } out << std::endl; } // 输出油藏类型 out << "油藏类型: "; if (reservoirType == OIL_RESERVOIR) { out << "油藏" << std::endl; } else if (reservoirType == GAS_RESERVOIR) { out << "气藏" << std::endl; out << "气藏温度: " << reservoirTemp << " K" << std::endl; // 输出气体组分 out << "气体组分:" << std::endl; double sum = 0.0; for (int i = 0; i < 32; i++) { if (gasComponents[i] > 1.0e-5) { // 只输出非零组分 // 使用名称数组,显示组分名称 out << " " << gasComponentNames[i] << ": " << gasComponents[i] << "%" << std::endl; sum += gasComponents[i]; } } out << "组分总和: " << sum << "%" << std::endl; } out << "==========================================================" << std::endl; } DWORD WINAPI MessagePumpThread(LPVOID lpParam) { MSG msg; while (g_calculationRunning) { // 处理所有消息 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 显示进度条 ShowProgressBar(g_timeStep, g_totalSteps); // 短暂休眠,减少CPU使用率 Sleep(100); } return 0; } int main() { std::string reservoirPara[8] = {"初始压力", "渗透率", "厚度", "黏度", "体积系数", "孔隙度", "综合压缩系数", "渗透率各向异性系数"}; try { CBoundShape shape; vector wells; vector limits; vector faults, fracs; double pBaseData[8] = {0}; TimeDis timeDis; RESERVOIR_TYPE reservoirType; double reservoirTemp; double gasComponents[32]; ReadParameters("input/case_oil_faults.txt", shape, wells, limits, faults, fracs, pBaseData, timeDis, reservoirType, reservoirTemp, gasComponents); // 详细油藏信息输出 OutputReservoirInfo(pBaseData, reservoirType, reservoirTemp, gasComponents, reservoirPara); cout << "\n===== 井数据 =====" << endl; for (size_t i = 0; i < wells.size(); ++i) { cout << "\n井 " << i + 1 << ":\n"; PrintWell(wells[i]); } /************************************************************************/ /* load netgen mesh generation library and generate mesh */ /************************************************************************/ HMODULE hMod = LoadLibrary("meshgene.dll"); if (NULL == hMod) { std::cout << "meshgene.dll加载失败!\n"; return 1; } typedef Ng_Mesh *(*NgMeshGen2DByIn2d)(std::vector& wells, const std::vector limits, const std::vector& faults, const std::vector& fracs, const CBoundShape & shape); typedef Ng_Mesh *(*NgMeshGen2D)(std::string strFileIn2D); typedef void *(*Ng2VTK)(Ng_Mesh * mesh); // Get the address of the Ng_Mesh function NgMeshGen2DByIn2d meshgene1 = (NgMeshGen2DByIn2d)GetProcAddress(hMod, "NgMeshGen2DByIn2d"); NgMeshGen2D meshgene2 = (NgMeshGen2D)GetProcAddress(hMod, "NgMeshGen2D"); if (NULL == meshgene1) { FreeLibrary(hMod); std::cout << "meshgene文件加载函数地址获取失败!\n"; return 1; } Ng_Mesh *mesh = meshgene1(wells, limits, faults, fracs, shape); // Ng_Mesh *mesh=meshgene2("netgen-1.in2d"); Ng2VTK ng = (Ng2VTK)GetProcAddress(hMod, "Ng2VTK"); ng(mesh); // return 0; HMODULE hMod_solver = LoadLibrary("singlePhaseSolverDll.dll"); if (NULL == hMod_solver) { std::cout << "singlePhaseSolverDll.dll加载失败!\n"; return 1; } // TimeDis timeDis; // timeDis.dTB = 1.0e-5; // // 初始化全局进度变量 g_timeStep = 0; g_totalSteps = 100; g_calculationRunning = true; // 创建消息处理线程 std::cout << "开始计算,请稍候...\n"; HANDLE hThread = CreateThread(NULL, 0, MessagePumpThread, NULL, 0, NULL); std::vector>> vecFieldPre; int timeStep, totalSteps; if (reservoirType == OIL_RESERVOIR) { typedef int (*Solver)(const TimeDis time, double* m_pBaseData, nglib::Ng_Mesh * mesh, std::vector& m_wWell, const std::vector& comPolygon, const std::vector& faultPolygon, std::vector& fracs, CBoundShape & outBound, std::vector>>& vecNodePre, int& timeStep, int& totalSteps); Solver solverFun = (Solver)GetProcAddress(hMod_solver, "singlePhaseSolverNgmesh"); if (NULL == solverFun) { FreeLibrary(hMod_solver); std::cout << "singlePhaseSolverNgmesh failed!\n"; return 1; } // std::vector> vecBP; // std::vector>> vecFieldPre; solverFun(timeDis, pBaseData, mesh, wells, limits, faults, fracs, shape, vecFieldPre, g_timeStep, g_totalSteps); // 更新全局变量,确保显示100%进度 } else if (reservoirType == GAS_RESERVOIR) { typedef int (*Solver)(double* component, double temperature, const TimeDis time, double* m_pBaseData, nglib::Ng_Mesh * mesh, std::vector& m_wWell, const std::vector& comPolygon, const std::vector& faultPolygon, std::vector& fracs, CBoundShape & outBound, std::vector>>& vecNodePre, int& timeStep, int& totalSteps); Solver solverFun = (Solver)GetProcAddress(hMod_solver, "singlePhaseGasSolverNgmesh"); if (NULL == solverFun) { FreeLibrary(hMod_solver); std::cout << "singlePhaseGasSolverNgmesh failed!\n"; return 1; } // std::vector> vecBP; solverFun(gasComponents, reservoirTemp, timeDis, pBaseData, mesh, wells, limits, faults, fracs, shape, vecFieldPre, g_timeStep, g_totalSteps); } // 停止消息处理线程 g_calculationRunning = false; if (hThread != NULL) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); } // 显示100%完成的进度条 ShowProgressBar(g_totalSteps, g_totalSteps); std::cout << std::endl << "计算完成!" << std::endl; // export bottom hole pressure history std::cout << "Export log-log plot and history plot data to files\n"; std::ofstream historyFile; historyFile.precision(12); historyFile.open("output/history.txt"); if (!historyFile.is_open()) { std::cerr << "Error opening file: " << "history.txt" << std::endl; return 1; } int nWell = 0; for (int i = 0; i < wells[nWell].m_PressureData.size(); i++) { historyFile << wells[nWell].m_PressureData[i].x << "\t" << wells[nWell].m_PressureData[i].y << "\t" << std::endl; } historyFile.close(); // calculate the pressure derivative typedef bool (*PreLog)(const std::vector& pressure, const int& nSection, double* TimeQ, double* dQ, int nTimeQ, std::vector& logPre); PreLog preLogFun = (PreLog)GetProcAddress(hMod_solver, "logLogPre"); if (NULL == preLogFun) { FreeLibrary(hMod_solver); std::cout << "preLogFun failed!\n"; return 1; } int nSection = 3; std::vector logPre; preLogFun(wells[nWell].m_PressureData, nSection, wells[nWell].pdTimeQ, wells[nWell].pdQ, wells[nWell].nTimeNumQ, logPre); // export the pressure derivative std::ofstream logFile; logFile.precision(8); logFile.open("output/logPre.txt"); if (!logFile.is_open()) { std::cerr << "Error opening file: " << "logPre.txt" << std::endl; return 1; } for (int i = 0; i < logPre.size(); i++) { logFile << logPre[i].x << "\t" << logPre[i].y << "\t" << logPre[i].z << "\t" << logPre[i].pointData[1] << std::endl; } logFile.close(); // export the semi-log data std::ofstream semiLogFile; semiLogFile.precision(8); semiLogFile.open("output/semiLogPre.txt"); if (!semiLogFile.is_open()) { std::cerr << "Error opening file: " << "semiLogPre.txt" << std::endl; return 1; } for (int i = 0; i < logPre.size(); i++) { semiLogFile << logPre[i].x << "\t" << logPre[i].pointData[0] << "\t" << std::endl; } semiLogFile.close(); // export the rate of selected well std::ofstream rateFile; rateFile.precision(8); rateFile.open("output/rate.txt"); if (!rateFile.is_open()) { std::cerr << "Error opening file: " << "rate.txt" << std::endl; return 1; } for (int i = 0; i < wells[nWell].nTimeNumQ; i++) { rateFile << wells[nWell].pdTimeQ[i] << "\t" << wells[nWell].pdQ[i] << std::endl; } rateFile.close(); // export with pressure field with vtk format (version 4.2) for (int i = 0; i < vecFieldPre.size(); ++i) { if (i % 10 == 0) { Ng2VTKWithData(mesh, vecFieldPre[i].second, "output/" + std::to_string(long double(i)) + ".vtk"); } } FreeLibrary(hMod); system("run_python.bat"); } catch (const exception &e) { cerr << "错误: " << e.what() << endl; return 1; } return 0; }