#include "singlePhaseSolver.h" #include #include #include #include #include #include #include #include "pch.h" #include "DatasetIO.h" #include "RunnerIO.h" // HX_NWTM typedef void (*HX_NWTM_MODEL_Func)(HX_NWTM_MODEL_OUTPUT&, const HX_NWTM_MODEL_INPUT&, std::string); // log-log pre typedef bool (*PreLogFun)(const std::vector&, const int&, double*, double*, int, std::vector&); static bool fileExistsA(const std::string& p) { DWORD attr = GetFileAttributesA(p.c_str()); return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY); } static std::string getExeDir() { char buf[MAX_PATH] = { 0 }; DWORD len = GetModuleFileNameA(NULL, buf, MAX_PATH); if (len == 0) return "."; std::string path(buf, len); size_t pos = path.find_last_of("\\/"); if (pos != std::string::npos) path.resize(pos); return path; } // =============== 组装模型输入 =============== static HX_NWTM_MODEL_INPUT buildModelInputFromDataset(const PebiScene& scene, const HX_NWTM_GRID_OUTPUT2& gridOutput2) { HX_NWTM_MODEL_INPUT input(gridOutput2); input.T = scene.solverType; input.Rate.t = scene.Rate.t; input.Rate.qo = scene.Rate.qo; input.Rate.qg = scene.Rate.qg; input.Rate.qw = scene.Rate.qw; input.CS.C = scene.CS.C; input.CS.S = scene.CS.S; input.PVT.p = scene.PVT.p; input.PVT.pb = scene.PVT.pb; input.PVT.Rso = scene.PVT.Rso; input.PVT.Bo = scene.PVT.Bo; input.PVT.Co = scene.PVT.Co; input.PVT.miuo = scene.PVT.miuo; input.PVT.rouo = scene.PVT.rouo; input.PVT.Rv = scene.PVT.Rv; input.PVT.Bg = scene.PVT.Bg; input.PVT.Cg = scene.PVT.Cg; input.PVT.miug = scene.PVT.miug; input.PVT.roug = scene.PVT.roug; input.PVT.Z = scene.PVT.Z; input.PVT.Rsw = scene.PVT.Rsw; input.PVT.Bw = scene.PVT.Bw; input.PVT.Cw = scene.PVT.Cw; input.PVT.miuw = scene.PVT.miuw; input.PVT.rouw = scene.PVT.rouw; input.PVT.V = scene.PVT.V; input.PVT.k_kinitial = scene.PVT.k_kinitial; input.PVT.Cf_Cfinitial = scene.PVT.Cf_Cfinitial; input.PVT.So = scene.PVT.So; input.PVT.Kro = scene.PVT.Kro; input.PVT.Sg = scene.PVT.Sg; input.PVT.Krg = scene.PVT.Krg; input.PVT.Sw = scene.PVT.Sw; input.PVT.Krw = scene.PVT.Krw; input.Base.Pi = scene.Base.Pi; input.Base.Cti = scene.Base.Cti; input.Base.Cf = scene.Base.Cf; input.Base.Soi = scene.Base.Soi; input.Base.Sgi = scene.Base.Sgi; input.Base.Swi = scene.Base.Swi; input.Base.d = scene.Base.d; input.Base.dt_Min = scene.Base.dt_Min; input.Base.dt_Max = scene.Base.dt_Max; size_t nCells = gridOutput2.Trinodexy.size(); input.Base.k = dVec1(nCells, scene.Base.k_ref); input.Base.phi = dVec1(nCells, scene.Base.phi_ref); input.Base.h = dVec1(nCells, scene.Base.h_ref); return input; } static inline size_t inferWellCount(const HX_NWTM_MODEL_INPUT& in) { size_t n = 0; n = (std::max)(n, in.Rate.t.size()); n = (std::max)(n, in.CS.C.size()); n = (std::max)(n, in.CS.S.size()); if (n == 0) n = 1; return n; } // =============== 应用采样参数 =============== static void applySampledParamsAndMaybeOverrideRate(HX_NWTM_MODEL_INPUT& in, const RunnerParams& p) { const size_t nWells = inferWellCount(in); // Ensure well vector sizes (不会清空 capacity) if (in.Rate.t.size() < nWells) in.Rate.t.resize(nWells); if (in.Rate.qo.size() < nWells) in.Rate.qo.resize(nWells); if (in.Rate.qg.size() < nWells) in.Rate.qg.resize(nWells); if (in.Rate.qw.size() < nWells) in.Rate.qw.resize(nWells); if (in.CS.S.size() < nWells) in.CS.S.resize(nWells, 0.0); if (in.CS.C.size() < nWells) in.CS.C.resize(nWells, 0.0); // 覆盖 skin / wellboreC(fill 更快) std::fill(in.CS.S.begin(), in.CS.S.end(), p.skin); std::fill(in.CS.C.begin(), in.CS.C.end(), p.wellboreC); // 覆盖 k/phi/h/Cf(resize + fill,复用 capacity) const size_t nCells = in.GRID.Trinodexy.size(); in.Base.k.resize(nCells); in.Base.phi.resize(nCells); in.Base.h.resize(nCells); std::fill(in.Base.k.begin(), in.Base.k.end(), p.k); std::fill(in.Base.phi.begin(), in.Base.phi.end(), p.phi); std::fill(in.Base.h.begin(), in.Base.h.end(), p.h); in.Base.Cf = p.Cf; // 制度覆盖:params.bin 带 schedule 才覆盖 if (!p.timeQ.empty() && !p.q.empty() && p.timeQ.size() == p.q.size()) { const size_t nQ = p.timeQ.size(); for (size_t w = 0; w < nWells; ++w) { dVec1& tt = in.Rate.t[w]; dVec1& qo = in.Rate.qo[w]; dVec1& qg = in.Rate.qg[w]; dVec1& qw = in.Rate.qw[w]; tt.resize(nQ); qo.resize(nQ); qg.resize(nQ); qw.resize(nQ); std::copy(p.timeQ.begin(), p.timeQ.end(), tt.begin()); std::copy(p.q.begin(), p.q.end(), qo.begin()); std::fill(qg.begin(), qg.end(), 0.0); std::fill(qw.begin(), qw.end(), 0.0); } } } // =============== 转换求解结果=============== static void toRunnerResult(const HX_NWTM_MODEL_OUTPUT& out, RunnerResult& rr) { rr.nSteps = (unsigned int)out.t.size(); rr.t = out.t; rr.nWells = (unsigned int)out.pw.size(); rr.pw.resize(rr.nWells); for (unsigned int w = 0; w < rr.nWells; ++w) { rr.pw[w] = out.pw[w]; if (rr.pw[w].size() < rr.nSteps) rr.pw[w].resize(rr.nSteps, 0.0); } } // =============== 计算双对数曲线(传入已加载的 preLogFun) =============== static bool computeLogLogCurves(RunnerResult& rr, const PebiScene& scene, const RunnerParams& params, PreLogFun preLogFun) { // 无论有没有 DLL,都先保证结构完整,避免 writeResultBin 越界/串数据 rr.loglog_t.assign(rr.nWells, std::vector()); rr.loglog_p.assign(rr.nWells, std::vector()); rr.loglog_deriv.assign(rr.nWells, std::vector()); if (!preLogFun) { return true; // 没 dll:loglog 为空,但结构正确 } for (unsigned int w = 0; w < rr.nWells; ++w) { std::vector wellPressureData; wellPressureData.resize(rr.nSteps); for (unsigned int i = 0; i < rr.nSteps; ++i) { wellPressureData[i].x = rr.t[i]; wellPressureData[i].y = rr.pw[w][i]; wellPressureData[i].z = 0.0; } int sectionIndex = 0; std::vector timeQ; std::vector q; // 1) params schedule 优先 if (!params.timeQ.empty() && !params.q.empty() && params.timeQ.size() == params.q.size()) { sectionIndex = (int)params.sectionIndex; timeQ = params.timeQ; q = params.q; } else { // 2) scene 默认 if (!scene.wellFlowSectionIndex.empty()) { size_t idx = (w < (unsigned int)scene.wellFlowSectionIndex.size()) ? w : 0; sectionIndex = scene.wellFlowSectionIndex[idx]; } if (w < scene.Rate.t.size()) timeQ = scene.Rate.t[w]; if (w < scene.Rate.qo.size()) q = scene.Rate.qo[w]; } if (timeQ.size() < 2 || q.size() != timeQ.size()) { return false; } std::vector logPreResult; bool success = preLogFun( wellPressureData, sectionIndex, timeQ.empty() ? NULL : &timeQ[0], // 安全处理空vector q.empty() ? NULL : &q[0], (int)timeQ.size(), logPreResult ); if (!success || logPreResult.size() <= 2) continue; std::vector& lt = rr.loglog_t[w]; std::vector& lp = rr.loglog_p[w]; std::vector& ld = rr.loglog_deriv[w]; lt.reserve(logPreResult.size() - 2); lp.reserve(logPreResult.size() - 2); ld.reserve(logPreResult.size() - 2); for (size_t i = 1; i + 1 < logPreResult.size(); ++i) { const Point& pt = logPreResult[i]; lt.push_back(pt.x); lp.push_back(pt.y); ld.push_back(pt.z); } } return true; } // =============== server 模式:循环处理 paramsPath/resultPath =============== static int runServer(const std::string& datasetPath, const std::string& dllPath, const std::string& licPath) { if (!fileExistsA(datasetPath)) { std::cerr << "ERROR: dataset not found\n"; return 10; } if (!fileExistsA(dllPath)) { std::cerr << "ERROR: dll not found\n"; return 12; } if (!fileExistsA(licPath)) { std::cerr << "ERROR: license not found\n"; return 13; } // 1) load dataset once PebiScene scene; HX_NWTM_GRID_OUTPUT2 gridOutput2; if (!DatasetIO::loadDataset(datasetPath, scene, gridOutput2, true)) { std::cerr << "ERROR: loadDataset failed\n"; return 30; } // 2) prepare base input once HX_NWTM_MODEL_INPUT modelInput = buildModelInputFromDataset(scene, gridOutput2); // 3) load HX solver once HMODULE hx = LoadLibraryA(dllPath.c_str()); if (!hx) { std::cerr << "ERROR: LoadLibrary(HX) failed\n"; return 40; } HX_NWTM_MODEL_Func HX_NWTM_MODEL = (HX_NWTM_MODEL_Func)GetProcAddress(hx, "HX_NWTM_MODEL"); if (!HX_NWTM_MODEL) { std::cerr << "ERROR: GetProcAddress(HX_NWTM_MODEL) failed\n"; FreeLibrary(hx); return 41; } // 4) load loglog dll once PreLogFun preLogFun = NULL; HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll"); if (hMod_solver) { preLogFun = (PreLogFun)GetProcAddress(hMod_solver, "logLogPre"); if (!preLogFun) { FreeLibrary(hMod_solver); hMod_solver = NULL; } } std::string line; RunnerResult rr; // 输入格式:每行 "paramsPath resultPath" while (std::getline(std::cin, line)) { if (line.empty()) continue; std::istringstream iss(line); std::string paramsPath, resultPath; if (!(iss >> paramsPath >> resultPath)) { std::cerr << "ERROR: invalid input line, expected: paramsPath resultPath\n"; continue; } RunnerParams params; if (!RunnerIO::readParamsBin(paramsPath, params)) { std::cerr << "ERROR: read params failed: " << paramsPath << "\n"; continue; } // 覆盖参数(复用内存) applySampledParamsAndMaybeOverrideRate(modelInput, params); HX_NWTM_MODEL_OUTPUT modelOutput; try { HX_NWTM_MODEL(modelOutput, modelInput, licPath); } catch (...) { std::cerr << "ERROR: solver exception\n"; continue; } toRunnerResult(modelOutput, rr); if (!computeLogLogCurves(rr, scene, params, preLogFun)) { std::cerr << "ERROR: computeLogLogCurves failed\n"; continue; } if (!RunnerIO::writeResultBin(resultPath, rr)) { std::cerr << "ERROR: write result failed: " << resultPath << "\n"; continue; } // 方便 Python 端读取:成功就输出一行 OK std::cout << "OK " << resultPath << "\n"; std::cout.flush(); } if (hMod_solver) FreeLibrary(hMod_solver); FreeLibrary(hx); return 0; } // =============== 单次模式 =============== int main(int argc, char** argv) { std::string exeDir = getExeDir(); // server: runner.exe --server dataset.bin HX_NWTM.dll lic.dat if (argc >= 2 && std::string(argv[1]) == "--server") { if (argc < 5) { std::cerr << "Usage: runner.exe --server \n"; return 2; } return runServer(argv[2], argv[3], argv[4]); } // Single-run mode requires explicit paths. Do not fall back to exeDir\\HX_NWTM.dll, // because Runner and HX_NWTM.dll must be built against the same PEBI header version. if (argc < 6) { std::cerr << "Usage: runner.exe \n"; return 2; } std::string datasetPath = argv[1]; std::string paramsPath = argv[2]; std::string resultPath = argv[3]; std::string dllPath = argv[4]; std::string licPath = argv[5]; if (!fileExistsA(datasetPath)) { std::cerr << "ERROR: dataset not found\n"; return 10; } if (!fileExistsA(paramsPath)) { std::cerr << "ERROR: params not found\n"; return 11; } if (!fileExistsA(dllPath)) { std::cerr << "ERROR: dll not found\n"; return 12; } if (!fileExistsA(licPath)) { std::cerr << "ERROR: license not found\n"; return 13; } RunnerParams params; if (!RunnerIO::readParamsBin(paramsPath, params)) { std::cerr << "ERROR: read params failed\n"; return 20; } PebiScene scene; HX_NWTM_GRID_OUTPUT2 gridOutput2; if (!DatasetIO::loadDataset(datasetPath, scene, gridOutput2, true)) { std::cerr << "ERROR: loadDataset failed\n"; return 30; } HX_NWTM_MODEL_INPUT modelInput = buildModelInputFromDataset(scene, gridOutput2); applySampledParamsAndMaybeOverrideRate(modelInput, params); HMODULE hx = LoadLibraryA(dllPath.c_str()); if (!hx) { std::cerr << "ERROR: LoadLibrary failed\n"; return 40; } HX_NWTM_MODEL_Func HX_NWTM_MODEL = (HX_NWTM_MODEL_Func)GetProcAddress(hx, "HX_NWTM_MODEL"); if (!HX_NWTM_MODEL) { std::cerr << "ERROR: GetProcAddress failed\n"; FreeLibrary(hx); return 41; } // loglog dll PreLogFun preLogFun = NULL; HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll"); if (hMod_solver) { preLogFun = (PreLogFun)GetProcAddress(hMod_solver, "logLogPre"); if (!preLogFun) { FreeLibrary(hMod_solver); hMod_solver = NULL; } } HX_NWTM_MODEL_OUTPUT modelOutput; try { HX_NWTM_MODEL(modelOutput, modelInput, licPath); } catch (...) { std::cerr << "ERROR: solver exception\n"; if (hMod_solver) FreeLibrary(hMod_solver); FreeLibrary(hx); return 50; } RunnerResult rr; toRunnerResult(modelOutput, rr); if (!computeLogLogCurves(rr, scene, params, preLogFun)) { std::cerr << "ERROR: computeLogLogCurves failed\n"; if (hMod_solver) FreeLibrary(hMod_solver); FreeLibrary(hx); return 55; } if (!RunnerIO::writeResultBin(resultPath, rr)) { std::cerr << "ERROR: write result failed\n"; if (hMod_solver) FreeLibrary(hMod_solver); FreeLibrary(hx); return 60; } if (hMod_solver) FreeLibrary(hMod_solver); FreeLibrary(hx); return 0; }