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> 58 template <
typename TypeTag>
69 enum { numPhases = FluidSystem::numPhases };
71 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
72 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
73 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
75 static constexpr
bool compositionSwitchEnabled =
76 Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max();
80 : simulator_(simulator)
81 , configsLoaded_(
false)
104 if (!Parameters::Get<Parameters::UseHybridNewton>())
107 validateFluidSystem();
109 if (!configsLoaded_) {
110 std::string config_file = Parameters::Get<Parameters::HybridNewtonConfigFile>();
112 for (
const auto& model_key : pt.get_child_keys()) {
115 configs_.push_back(std::move(config));
117 configsLoaded_ =
true;
120 Scalar current_time = simulator_.time();
122 for (
const auto& config : configs_) {
124 runHybridNewton(config);
152 void validateFluidSystem()
154 const auto& eclState = simulator_.vanguard().eclState();
155 const auto& phases = eclState.runspec().phases();
157 bool hasWater = phases.active(Phase::WATER);
158 bool hasGas = phases.active(Phase::GAS);
159 bool hasOil = phases.active(Phase::OIL);
160 bool hasSolvent = phases.active(Phase::SOLVENT);
163 if (!(hasWater && hasOil && hasGas && !hasSolvent)) {
164 OPM_THROW(std::runtime_error,
165 "HybridNewton: Unsupported fluid system. Only three-phase black oil is supported.");
191 if (config.apply_times.size() == 1) {
193 constexpr Scalar tolerance = 1e-6;
194 bool apply = std::abs(current_time - config.apply_times[0]) < tolerance;
198 if (config.apply_times.size() == 2) {
199 Scalar start_time = config.apply_times[0];
200 Scalar end_time = config.apply_times[1];
201 bool apply = (current_time >= start_time) && (current_time <= end_time);
234 ML::Tensor<Evaluation>
237 const auto& features = config.input_features;
240 std::size_t input_tensor_length = 0;
241 for (
const auto& feature : features) {
243 if (spec.actual_name ==
"TIMESTEP") {
244 input_tensor_length += 1;
246 input_tensor_length += config.n_cells;
250 ML::Tensor<Evaluation> input(input_tensor_length);
251 std::size_t offset = 0;
254 for (
const auto& feature: features) {
257 if (spec.actual_name ==
"TIMESTEP") {
259 input(offset++) = value;
261 for (std::size_t i = 0; i < config.n_cells; ++i) {
262 auto value =
static_cast<Evaluation
>(
265 input(offset + i) = value;
267 offset += config.n_cells;
292 if (spec.actual_name ==
"TIMESTEP") {
293 value = simulator_.timeStepSize();
295 OPM_THROW(std::runtime_error,
"Unknown scalar feature: " + spec.actual_name);
298 value = spec.transform.apply(value);
299 return spec.scaler.scale(value);
321 const auto& intQuants = simulator_.model().intensiveQuantities(cell_index, 0);
322 const auto& fs = intQuants.fluidState();
323 const auto& unitSyst = simulator_.vanguard().schedule().getUnits();
327 if (spec.actual_name ==
"PRESSURE") {
328 value = getValue(fs.pressure(oilPhaseIdx));
329 value = unitSyst.from_si(UnitSystem::measure::pressure, value);
330 }
else if (spec.actual_name ==
"SWAT") {
331 value = getValue(fs.saturation(waterPhaseIdx));
332 }
else if (spec.actual_name ==
"SGAS") {
333 value = getValue(fs.saturation(gasPhaseIdx));
334 }
else if (spec.actual_name ==
"SOIL") {
335 value = getValue(fs.saturation(oilPhaseIdx));
336 }
else if (spec.actual_name ==
"RS") {
337 value = getValue(fs.Rs());
338 value = unitSyst.from_si(UnitSystem::measure::gas_oil_ratio, value);
339 }
else if (spec.actual_name ==
"RV") {
340 value = getValue(fs.Rv());
341 value = unitSyst.from_si(UnitSystem::measure::oil_gas_ratio, value);
342 }
else if (spec.actual_name ==
"PERMX") {
343 const auto& eclState = simulator_.vanguard().eclState();
344 const auto& fp = eclState.fieldProps();
345 auto permX = fp.get_double(
"PERMX");
346 value = permX[cell_index];
347 value = unitSyst.from_si(UnitSystem::measure::permeability, value);
349 OPM_THROW(std::runtime_error,
"Unknown per-cell feature: " + spec.actual_name);
352 Scalar transformed = spec.transform.apply(value);
353 Scalar scaled = spec.scaler.scale(transformed);
376 ML::Tensor<Evaluation>
380 const auto& features = config.output_features;
381 const int n_features = features.size();
383 ML::NNModel<Evaluation> model;
384 model.loadModel(config.model_path);
386 ML::Tensor<Evaluation> output(1, config.n_cells * n_features);
387 model.apply(input, output);
422 const auto& features = config.output_features;
426 const auto& unitSyst = simulator_.vanguard().schedule().getUnits();
428 for (std::size_t i = 0; i < config.n_cells; ++i) {
429 const int cell_idx = config.cell_indices[i];
430 const auto& intQuants = simulator_.model().intensiveQuantities(cell_idx, 0);
431 auto fs = intQuants.fluidState();
436 Scalar sw_val = -1.0;
437 Scalar so_val = -1.0;
438 Scalar sg_val = -1.0;
439 Scalar po_val = -1.0;
441 for (
const auto& [name, spec] : features) {
443 auto scaled_value = getValue(output(feature_idx * config.n_cells + i));
446 Scalar raw_value = spec.scaler.unscale(scaled_value);
449 raw_value = spec.transform.applyInverse(raw_value);
452 if (spec.actual_name ==
"PRESSURE") {
453 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::pressure, getValue(fs.pressure(oilPhaseIdx)));
454 }
else if (spec.actual_name ==
"SWAT") {
455 raw_value += getValue(fs.saturation(waterPhaseIdx));
456 }
else if (spec.actual_name ==
"SOIL") {
457 raw_value += getValue(fs.saturation(oilPhaseIdx));
458 }
else if (spec.actual_name ==
"SGAS") {
459 raw_value += getValue(fs.saturation(gasPhaseIdx));
460 }
else if (spec.actual_name ==
"RS") {
461 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::gas_oil_ratio, getValue(fs.Rs()));
462 }
else if (spec.actual_name ==
"RV") {
463 raw_value = raw_value + unitSyst.from_si(UnitSystem::measure::oil_gas_ratio, getValue(fs.Rv()));
465 OPM_THROW(std::runtime_error,
"Unknown delta feature: " + name);
469 if (spec.actual_name ==
"PRESSURE") {
470 po_val = unitSyst.to_si(UnitSystem::measure::pressure, raw_value);
471 }
else if (spec.actual_name ==
"SWAT") {
473 }
else if (spec.actual_name ==
"SOIL") {
475 }
else if (spec.actual_name ==
"SGAS") {
477 }
else if (spec.actual_name ==
"RS") {
478 if constexpr (compositionSwitchEnabled) {
479 raw_value = unitSyst.to_si(UnitSystem::measure::gas_oil_ratio, raw_value);
482 }
else if (spec.actual_name ==
"RV") {
483 if constexpr (compositionSwitchEnabled) {
484 raw_value = unitSyst.to_si(UnitSystem::measure::oil_gas_ratio, raw_value);
488 OPM_THROW(std::runtime_error,
"Unknown output feature: " + name);
494 int sat_count =
static_cast<int>(flags.has_SWAT) +
495 static_cast<int>(flags.has_SOIL) +
496 static_cast<int>(flags.has_SGAS);
498 if (sat_count >= 2) {
503 if (!flags.has_SWAT) {
505 }
else if (!flags.has_SOIL) {
507 }
else if (!flags.has_SGAS) {
511 sw = max(0.0, min(sw, 1.0));
512 so = max(0.0, min(so, 1.0));
513 sg = max(0.0, min(sg, 1.0));
515 Scalar sum = sw + so + sg;
517 OPM_THROW(std::runtime_error,
"Saturation sum is zero in cell " + std::to_string(cell_idx));
520 fs.setSaturation(waterPhaseIdx, sw / sum);
521 fs.setSaturation(oilPhaseIdx, so / sum);
522 fs.setSaturation(gasPhaseIdx, sg / sum);
525 if (flags.has_PRESSURE) {
526 std::array<Evaluation, numPhases> pC;
527 const auto& materialParams = simulator_.problem().materialLawParams(cell_idx);
528 MaterialLaw::capillaryPressures(pC, materialParams, fs);
530 for (
unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
531 if (!FluidSystem::phaseIsActive(phaseIdx))
continue;
532 if (phaseIdx == oilPhaseIdx) {
533 fs.setPressure(phaseIdx, po_val);
535 fs.setPressure(phaseIdx, po_val - pC[phaseIdx]);
540 auto& primaryVars = simulator_.model().solution(0)[cell_idx];
541 primaryVars.assignNaive(fs);
544 simulator_.model().invalidateAndUpdateIntensiveQuantities(0);
549 bool has_SWAT =
false;
550 bool has_SOIL =
false;
551 bool has_SGAS =
false;
552 bool has_PRESSURE =
false;
555 FeatureFlags flagFeatures(
const std::vector<std::pair<std::string, FeatureSpec>>& features)
559 for (
const auto& [name, spec] : features) {
561 if (spec.actual_name ==
"SWAT") {
562 flags.has_SWAT =
true;
563 }
else if (spec.actual_name ==
"SOIL") {
564 flags.has_SOIL =
true;
565 }
else if (spec.actual_name ==
"SGAS") {
566 flags.has_SGAS =
true;
567 }
else if (spec.actual_name ==
"PRESSURE") {
568 flags.has_PRESSURE =
true;
576 Simulator& simulator_;
577 std::vector<HybridNewtonConfig> configs_;
583 #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:101
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:419
ML::Tensor< Evaluation > constructInputTensor(const HybridNewtonConfig &config)
Construct the input feature tensor for the Hybrid Newton model.
Definition: HybridNewton.hpp:235
Hybrid Newton solver extension for the black-oil model.
Definition: HybridNewton.hpp:59
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Scalar getScalarFeatureValue(const FeatureSpec &spec)
Retrieve and transform a scalar feature (global across the domain).
Definition: HybridNewton.hpp:288
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:377
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:189
Scalar getPerCellFeatureValue(const FeatureSpec &spec, int cell_index)
Retrieve and transform a per-cell feature value.
Definition: HybridNewton.hpp:319
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:38
Definition: HybridNewton.hpp:547
Configuration for a Hybrid Newton ML model.
Definition: HybridNewtonConfig.hpp:135