22 #ifndef HYBRID_NEWTON_HPP 23 #define HYBRID_NEWTON_HPP 27 #include <opm/simulators/flow/HybridNewtonConfig.hpp> 28 #include <opm/input/eclipse/Units/UnitSystem.hpp> 30 #include <opm/ml/ml_model.hpp> 57 template <
typename TypeTag>
68 enum { numPhases = FluidSystem::numPhases };
70 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
71 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
72 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
74 static constexpr
bool compositionSwitchEnabled = Indices::compositionSwitchIdx >= 0;
78 : simulator_(simulator)
79 , configsLoaded_(
false)
102 if (!Parameters::Get<Parameters::UseHybridNewton>())
105 validateFluidSystem();
107 if (!configsLoaded_) {
108 std::string config_file = Parameters::Get<Parameters::HybridNewtonConfigFile>();
110 for (
const auto& model_key : pt.get_child_keys()) {
113 configs_.push_back(std::move(config));
115 configsLoaded_ =
true;
118 Scalar current_time = simulator_.time();
120 for (
const auto& config : configs_) {
122 runHybridNewton(config);
150 void validateFluidSystem()
152 const auto& eclState = simulator_.vanguard().eclState();
153 const auto& phases = eclState.runspec().phases();
155 bool hasWater = phases.active(Phase::WATER);
156 bool hasGas = phases.active(Phase::GAS);
157 bool hasOil = phases.active(Phase::OIL);
158 bool hasSolvent = phases.active(Phase::SOLVENT);
161 if (!(hasWater && hasOil && hasGas && !hasSolvent)) {
162 OPM_THROW(std::runtime_error,
163 "HybridNewton: Unsupported fluid system. Only three-phase black oil is supported.");
189 if (config.apply_times.size() == 1) {
191 constexpr Scalar tolerance = 1e-6;
192 bool apply = std::abs(current_time - config.apply_times[0]) < tolerance;
196 if (config.apply_times.size() == 2) {
197 Scalar start_time = config.apply_times[0];
198 Scalar end_time = config.apply_times[1];
199 bool apply = (current_time >= start_time) && (current_time <= end_time);
232 ML::Tensor<Evaluation>
235 const auto& features = config.input_features;
238 std::size_t input_tensor_length = 0;
239 for (
const auto& feature : features) {
241 if (spec.actual_name ==
"TIMESTEP") {
242 input_tensor_length += 1;
244 input_tensor_length += config.n_cells;
248 ML::Tensor<Evaluation> input(input_tensor_length);
249 std::size_t offset = 0;
252 for (
const auto& feature: features) {
255 if (spec.actual_name ==
"TIMESTEP") {
257 input(offset++) = value;
259 for (std::size_t i = 0; i < config.n_cells; ++i) {
260 auto value =
static_cast<Evaluation
>(
263 input(offset + i) = value;
265 offset += config.n_cells;
290 if (spec.actual_name ==
"TIMESTEP") {
291 value = simulator_.timeStepSize();
293 OPM_THROW(std::runtime_error,
"Unknown scalar feature: " + spec.actual_name);
296 value = spec.transform.apply(value);
297 return spec.scaler.scale(value);
319 const auto& intQuants = simulator_.model().intensiveQuantities(cell_index, 0);
320 const auto& fs = intQuants.fluidState();
321 const auto& unitSyst = simulator_.vanguard().schedule().getUnits();
325 if (spec.actual_name ==
"PRESSURE") {
326 value = getValue(fs.pressure(oilPhaseIdx));
327 value = unitSyst.from_si(UnitSystem::measure::pressure, value);
328 }
else if (spec.actual_name ==
"SWAT") {
329 value = getValue(fs.saturation(waterPhaseIdx));
330 }
else if (spec.actual_name ==
"SGAS") {
331 value = getValue(fs.saturation(gasPhaseIdx));
332 }
else if (spec.actual_name ==
"SOIL") {
333 value = getValue(fs.saturation(oilPhaseIdx));
334 }
else if (spec.actual_name ==
"RS") {
335 value = getValue(fs.Rs());
336 value = unitSyst.from_si(UnitSystem::measure::gas_oil_ratio, value);
337 }
else if (spec.actual_name ==
"RV") {
338 value = getValue(fs.Rv());
339 value = unitSyst.from_si(UnitSystem::measure::oil_gas_ratio, value);
340 }
else if (spec.actual_name ==
"PERMX") {
341 const auto& eclState = simulator_.vanguard().eclState();
342 const auto& fp = eclState.fieldProps();
343 auto permX = fp.get_double(
"PERMX");
344 value = permX[cell_index];
345 value = unitSyst.from_si(UnitSystem::measure::permeability, value);
347 OPM_THROW(std::runtime_error,
"Unknown per-cell feature: " + spec.actual_name);
350 Scalar transformed = spec.transform.apply(value);
351 Scalar scaled = spec.scaler.scale(transformed);
374 ML::Tensor<Evaluation>
378 const auto& features = config.output_features;
379 const int n_features = features.size();
381 ML::NNModel<Evaluation> model;
382 model.loadModel(config.model_path);
384 ML::Tensor<Evaluation> output(1, config.n_cells * n_features);
385 model.apply(input, output);
420 const auto& features = config.output_features;
424 const auto& unitSyst = simulator_.vanguard().schedule().getUnits();
426 for (std::size_t i = 0; i < config.n_cells; ++i) {
427 const int cell_idx = config.cell_indices[i];
428 const auto& intQuants = simulator_.model().intensiveQuantities(cell_idx, 0);
429 auto fs = intQuants.fluidState();
434 Scalar sw_val = -1.0;
435 Scalar so_val = -1.0;
436 Scalar sg_val = -1.0;
437 Scalar po_val = -1.0;
439 for (
const auto& [name, spec] : features) {
441 auto scaled_value = getValue(output(feature_idx * config.n_cells + i));
444 Scalar raw_value = spec.scaler.unscale(scaled_value);
447 raw_value = spec.transform.applyInverse(raw_value);
450 if (spec.actual_name ==
"PRESSURE") {
451 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::pressure, getValue(fs.pressure(oilPhaseIdx)));
452 }
else if (spec.actual_name ==
"SWAT") {
453 raw_value += getValue(fs.saturation(waterPhaseIdx));
454 }
else if (spec.actual_name ==
"SOIL") {
455 raw_value += getValue(fs.saturation(oilPhaseIdx));
456 }
else if (spec.actual_name ==
"SGAS") {
457 raw_value += getValue(fs.saturation(gasPhaseIdx));
458 }
else if (spec.actual_name ==
"RS") {
459 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::gas_oil_ratio, getValue(fs.Rs()));
460 }
else if (spec.actual_name ==
"RV") {
461 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::oil_gas_ratio, getValue(fs.Rv()));
463 OPM_THROW(std::runtime_error,
"Unknown delta feature: " + name);
467 if (spec.actual_name ==
"PRESSURE") {
468 po_val = unitSyst.to_si(UnitSystem::measure::pressure, raw_value);
469 }
else if (spec.actual_name ==
"SWAT") {
471 }
else if (spec.actual_name ==
"SOIL") {
473 }
else if (spec.actual_name ==
"SGAS") {
475 }
else if (spec.actual_name ==
"RS") {
476 if constexpr (compositionSwitchEnabled) {
477 raw_value = unitSyst.to_si(UnitSystem::measure::gas_oil_ratio, raw_value);
480 }
else if (spec.actual_name ==
"RV") {
481 if constexpr (compositionSwitchEnabled) {
482 raw_value = unitSyst.to_si(UnitSystem::measure::oil_gas_ratio, raw_value);
486 OPM_THROW(std::runtime_error,
"Unknown output feature: " + name);
492 int sat_count =
static_cast<int>(flags.has_SWAT) +
493 static_cast<int>(flags.has_SOIL) +
494 static_cast<int>(flags.has_SGAS);
496 if (sat_count >= 2) {
501 if (!flags.has_SWAT) {
503 }
else if (!flags.has_SOIL) {
505 }
else if (!flags.has_SGAS) {
509 sw = max(0.0, min(sw, 1.0));
510 so = max(0.0, min(so, 1.0));
511 sg = max(0.0, min(sg, 1.0));
513 Scalar sum = sw + so + sg;
515 OPM_THROW(std::runtime_error,
"Saturation sum is zero in cell " + std::to_string(cell_idx));
518 fs.setSaturation(waterPhaseIdx, sw / sum);
519 fs.setSaturation(oilPhaseIdx, so / sum);
520 fs.setSaturation(gasPhaseIdx, sg / sum);
523 if (flags.has_PRESSURE) {
524 std::array<Evaluation, numPhases> pC;
525 const auto& materialParams = simulator_.problem().materialLawParams(cell_idx);
526 MaterialLaw::capillaryPressures(pC, materialParams, fs);
528 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
529 if (!FluidSystem::phaseIsActive(phaseIdx))
continue;
530 if (phaseIdx == oilPhaseIdx) {
531 fs.setPressure(phaseIdx, po_val);
533 fs.setPressure(phaseIdx, po_val - pC[phaseIdx]);
538 auto& primaryVars = simulator_.model().solution(0)[cell_idx];
539 primaryVars.assignNaive(fs);
542 simulator_.model().invalidateAndUpdateIntensiveQuantities(0);
547 bool has_SWAT =
false;
548 bool has_SOIL =
false;
549 bool has_SGAS =
false;
550 bool has_PRESSURE =
false;
553 FeatureFlags flagFeatures(
const std::vector<std::pair<std::string, FeatureSpec>>& features)
557 for (
const auto& [name, spec] : features) {
559 if (spec.actual_name ==
"SWAT") {
560 flags.has_SWAT =
true;
561 }
else if (spec.actual_name ==
"SOIL") {
562 flags.has_SOIL =
true;
563 }
else if (spec.actual_name ==
"SGAS") {
564 flags.has_SGAS =
true;
565 }
else if (spec.actual_name ==
"PRESSURE") {
566 flags.has_PRESSURE =
true;
574 Simulator& simulator_;
575 std::vector<HybridNewtonConfig> configs_;
581 #endif // HYBRID_NEWTON_CLASS_HPP typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...))
Definition: propertysystem.hh:233
void tryApplyHybridNewton()
Attempt to apply the Hybrid Newton correction at the current timestep.
Definition: HybridNewton.hpp:99
void validateConfig(bool compositionSwitchEnabled) const
Validate feature compatibility with simulator settings.
Definition: HybridNewtonConfig.cpp:82
void updateInitialGuess(ML::Tensor< Evaluation > &output, const HybridNewtonConfig &config)
Update the nonlinear solver's initial guess using ML predictions.
Definition: HybridNewton.hpp:417
ML::Tensor< Evaluation > constructInputTensor(const HybridNewtonConfig &config)
Construct the input feature tensor for the Hybrid Newton model.
Definition: HybridNewton.hpp:233
Hybrid Newton solver extension for the black-oil model.
Definition: HybridNewton.hpp:58
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Scalar getScalarFeatureValue(const FeatureSpec &spec)
Retrieve and transform a scalar feature (global across the domain).
Definition: HybridNewton.hpp:286
Metadata for a single feature (input or output).
Definition: HybridNewtonConfig.hpp:119
ML::Tensor< Evaluation > constructOutputTensor(const ML::Tensor< Evaluation > &input, const HybridNewtonConfig &config)
Run the Hybrid Newton model to produce output predictions.
Definition: HybridNewton.hpp:375
bool shouldApplyHybridNewton(Scalar current_time, const HybridNewtonConfig &config) const
Check whether the Hybrid Newton method should be applied at the given time.
Definition: HybridNewton.hpp:187
Scalar getPerCellFeatureValue(const FeatureSpec &spec, int cell_index)
Retrieve and transform a per-cell feature value.
Definition: HybridNewton.hpp:317
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:38
Definition: HybridNewton.hpp:545
Configuration for a Hybrid Newton ML model.
Definition: HybridNewtonConfig.hpp:135