diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index c31946be22..40951b49d4 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -158,7 +158,7 @@ protected: using IntensiveQuantities = GetPropType; using WellModel = GetPropType; using AquiferModel = GetPropType; - + using Toolbox = MathToolbox; using DimMatrix = Dune::FieldMatrix; @@ -1672,7 +1672,7 @@ protected: GlobalEqVector drift_; WellModel wellModel_; - AquiferModel aquiferModel_; + AquiferModel aquiferModel_; PffGridVector pffDofData_; TracerModel tracerModel_; diff --git a/opm/simulators/flow/HybridNewton.hpp b/opm/simulators/flow/HybridNewton.hpp index 7b08a0d4ac..8c79a10d65 100644 --- a/opm/simulators/flow/HybridNewton.hpp +++ b/opm/simulators/flow/HybridNewton.hpp @@ -81,6 +81,7 @@ public: : simulator_(simulator) , configsLoaded_(false) {} + void tryApplyHybridNewton() { // Check if flag activated @@ -128,7 +129,7 @@ private: { // Load cell indices from file at runtime std::vector cell_indices = loadCellIndicesFromFile(config.cell_indices_file); - const size_t n_cells = cell_indices.size(); + const std::size_t n_cells = cell_indices.size(); // Pass cells to each stage auto input = constructInputTensor(config, cell_indices, n_cells); @@ -136,8 +137,6 @@ private: updateInitialGuess(output, config, cell_indices, n_cells); } - - protected: void validateFluidSystem() @@ -319,7 +318,7 @@ protected: * correspond to cells and columns correspond to input features. */ Opm::ML::Tensor - constructInputTensor(const HybridNewtonConfig& config, const std::vector& cell_indices, size_t n_cells) + constructInputTensor(const HybridNewtonConfig& config, const std::vector& cell_indices, std::size_t n_cells) { const auto& features = config.input_features; @@ -336,11 +335,11 @@ protected: const auto& unitSyst = simulator_.vanguard().schedule().getUnits(); // Calculate total input size (feature-major) - size_t input_size = num_per_cell_features * n_cells + num_scalar_features; + std::size_t input_size = num_per_cell_features * n_cells + num_scalar_features; Opm::ML::Tensor input(input_size); // Track offset for each feature in flat tensor - size_t offset = 0; + std::size_t offset = 0; for (const auto& [name, spec] : features) { if (name == "TIMESTEP") { @@ -353,7 +352,7 @@ protected: offset += 1; // advance offset by 1 for scalar } else { // Per-cell feature: assign values for each cell - for (size_t cell_idx = 0; cell_idx < n_cells; ++cell_idx) { + for (std::size_t cell_idx = 0; cell_idx < n_cells; ++cell_idx) { const auto& intQuants = simulator_.model().intensiveQuantities(cell_indices[cell_idx], 0); const auto& fs = intQuants.fluidState(); @@ -415,7 +414,7 @@ protected: * output tensor does not match the expected feature layout. */ Opm::ML::Tensor - constructOutputTensor(const Opm::ML::Tensor& input, const HybridNewtonConfig& config, size_t n_cells) + constructOutputTensor(const Opm::ML::Tensor& input, const HybridNewtonConfig& config, std::size_t n_cells) { const auto& features = config.output_features; const int n_features = features.size(); @@ -450,7 +449,7 @@ protected: * \throws std::runtime_error if an expected output feature is missing * or if state update fails for any cell. */ - void updateInitialGuess(Opm::ML::Tensor& output, const HybridNewtonConfig& config, const std::vector& cell_indices, size_t n_cells) + void updateInitialGuess(Opm::ML::Tensor& output, const HybridNewtonConfig& config, const std::vector& cell_indices, std::size_t n_cells) { const auto& features = config.output_features; @@ -471,7 +470,7 @@ protected: const auto& unitSyst = simulator_.vanguard().schedule().getUnits(); - for (size_t i = 0; i < n_cells; ++i) { + for (std::size_t i = 0; i < n_cells; ++i) { const int cell_idx = cell_indices[i]; const auto& intQuants = simulator_.model().intensiveQuantities(cell_idx, /*timeIdx*/0); auto fs = intQuants.fluidState(); diff --git a/python/test/ml/README.md b/python/test/ml/README.md new file mode 100644 index 0000000000..76f68c2fa3 --- /dev/null +++ b/python/test/ml/README.md @@ -0,0 +1,17 @@ + + + + +requirements (same as opm-common/python/opm/ml/ml_tools/requirements.txt) + +# Python v.3.9.0 and above +python>=3.9.0<=3.12.0 + +# Numpy 1.23.0 and above +numpy>=1.23.0 + +# TensorFlow v.2.1 and above include both CPU and GPU versions. +tensorflow>=2.1.0 + +# Keras v.2.12.0 and above +keras>=2.12.0 diff --git a/python/test/ml/hybrid_newton/test_hybrid_newton.py b/python/test/ml/hybrid_newton/test_hybrid_newton.py new file mode 100644 index 0000000000..0f9c45abcc --- /dev/null +++ b/python/test/ml/hybrid_newton/test_hybrid_newton.py @@ -0,0 +1,2 @@ +from opm.io.ecl_state import EclipseState +from opm.simulators import BlackOilSimulator \ No newline at end of file diff --git a/python/test/ml/ml_tools/write_config_file.py b/python/test/ml/ml_tools/write_config_file.py new file mode 100644 index 0000000000..113eb6064f --- /dev/null +++ b/python/test/ml/ml_tools/write_config_file.py @@ -0,0 +1,111 @@ +import os, json + +def write_config(models, model_dir, multi_mode=False, case_label=None): + """ + Write one or multiple model configs to JSON. + + models: list of dicts, each dict must contain: + - input_features + - output_features + - model_path + - active_cells + - apply_time + - feature_engineering_input (optional) + - scaling_input (optional) + - feature_engineering_output (optional) + - scaling_output (optional) + - input_scaling_params (optional) + - output_scaling_params (optional) + + multi_mode: if True, write all configs into a single JSON. + case_label: used as filename for multi-mode JSON (required if multi_mode=True). + """ + + if multi_mode and not case_label: + raise ValueError("case_label must be provided for multi_mode=True") + + os.makedirs(model_dir, exist_ok=True) + all_configs = [] + + for m in models: + # Normalize input/output features + input_features = m["input_features"] + output_features = m["output_features"] + if isinstance(input_features, str): + input_features = [input_features] + if isinstance(output_features, str): + output_features = [output_features] + + n_input = len(input_features) + n_output = len(output_features) + + # Optional fields (safe defaults) + fe_input = m.get("feature_engineering_input") or [None]*n_input + fe_output = m.get("feature_engineering_output") or [None]*n_output + scaling_input = m.get("scaling_input") or [None]*n_input + scaling_output = m.get("scaling_output") or [None]*n_output + input_scaling_params = m.get("input_scaling_params") or [None]*n_input + output_scaling_params = m.get("output_scaling_params") or [None]*n_output + + # Build input block + input_block = {} + for i, fname in enumerate(input_features): + feat_eng = fe_input[i] if i < len(fe_input) else None + scale = scaling_input[i] if i < len(scaling_input) else None + feature_dict = { + "feature_engineering": feat_eng.lower() if feat_eng and feat_eng.lower() != "none" else None, + "scaling": scale.lower() if scale and scale.lower() != "none" else None, + } + feature_dict = {k: v for k, v in feature_dict.items() if v is not None} + if i < len(input_scaling_params) and input_scaling_params[i] is not None: + feature_dict["scaling_params"] = input_scaling_params[i] + input_block[fname] = feature_dict + + # Build output block + output_block = {} + for i, fname in enumerate(output_features): + feat_eng = fe_output[i] if i < len(fe_output) else None + scale = scaling_output[i] if i < len(scaling_output) else None + feature_dict = { + "feature_engineering": feat_eng.lower() if feat_eng and feat_eng.lower() != "none" else None, + "scaling": scale.lower() if scale and scale.lower() != "none" else None, + } + feature_dict = {k: v for k, v in feature_dict.items() if v is not None} + if i < len(output_scaling_params) and output_scaling_params[i] is not None: + feature_dict["scaling_params"] = output_scaling_params[i] + output_block[fname] = feature_dict + + # Save active cells + model_base = os.path.splitext(os.path.basename(m["model_path"]))[0] + cells_file = os.path.join(model_dir, model_base + "_active_cells.txt") + with open(cells_file, "w") as f: + for cell in m["active_cells"]: + f.write(f"{cell}\n") + + # Build config dict + cfg = { + "model_path": m["model_path"], + "cell_indices_file": cells_file, + "apply_times": [m["apply_time"]], + "features": { + "inputs": input_block, + "outputs": output_block + } + } + all_configs.append(cfg) + + # Determine JSON filename + if multi_mode: + if not case_label: + raise ValueError("case_label is required for multi_mode") + json_name = case_label + ".json" + else: + json_name = os.path.splitext(os.path.basename(models[0]["model_path"]))[0] + ".json" + + json_path = os.path.join(model_dir, json_name) + + # Always write a list of configs + with open(json_path, "w") as f: + json.dump(all_configs, f, indent=2) + + return json_path \ No newline at end of file