Log local switching

The log is used for output and checking for oscillations

only count the swithcing after NUPCOL
This commit is contained in:
Tor Harald Sandve committed 2025-04-30 11:55:57 +02:00
1 parent 357c60c2ba
commit 82401f7417
1 file changed
+51 -31
+51 -31
View File
@@ -209,21 +209,20 @@ namespace Opm
} else {
from = WellProducerCMode2String(ws.production_cmode);
}
bool oscillating = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) >= this->param_.max_number_of_well_switches_;
const int episodeIdx = simulator.episodeIndex();
const int iterationIdx = simulator.model().newtonMethod().numIterations();
const int nupcol = schedule[episodeIdx].nupcol();
if (oscillating && iterationIdx > nupcol) {
// only output frist time
bool output = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) == this->param_.max_number_of_well_switches_;
const bool oscillating = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) >= this->param_.max_number_of_well_switches_;
if (oscillating) {
// only output first time
const bool output = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) == this->param_.max_number_of_well_switches_;
if (output) {
std::ostringstream ss;
ss << " The control mode for well " << this->name()
<< " is oscillating\n"
<< " We don't allow for more than "
<< this->param_.max_number_of_well_switches_
<< " switches. The control is kept at " << from;
deferred_logger.info(ss.str());
const auto msg = fmt::format(" The control mode for well {} is oscillating. \n"
"We don't allow for more than {} switches after NUPCOL iterations. (NUPCOL = {}) \n"
"The control is kept at {}.",
this->name(), this->param_.max_number_of_well_switches_, nupcol, from);
deferred_logger.info(msg);
// add one more to avoid outputting the same info again
this->well_control_log_.push_back(from);
}
@@ -256,7 +255,12 @@ namespace Opm
}
deferred_logger.debug(ss.str());
this->well_control_log_.push_back(from);
// We always store the current control as it is used for output
// and only after iteration >= nupcol
// we log all switches to check if the well controls oscillates
if (iterationIdx >= nupcol || this->well_control_log_.empty()) {
this->well_control_log_.push_back(from);
}
updateWellStateWithTarget(simulator, group_state, well_state, deferred_logger);
updatePrimaryVariables(simulator, well_state, deferred_logger);
}
@@ -288,7 +292,6 @@ namespace Opm
from = WellProducerCMode2String(ws.production_cmode);
}
const bool oscillating = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) >= this->param_.max_number_of_well_switches_;
if (oscillating || this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) {
return false;
}
@@ -508,6 +511,9 @@ namespace Opm
const auto& summary_state = simulator.vanguard().summaryState();
const auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0);
const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0);
const auto& ws = well_state.well(this->indexOfWell());
const auto pmode_orig = ws.production_cmode;
const auto imode_orig = ws.injection_cmode;
bool converged = false;
try {
// TODO: the following two functions will be refactored to be one to reduce the code duplication
@@ -526,6 +532,32 @@ namespace Opm
deferred_logger.warning("INNER_ITERATION_FAILED", msg);
converged = false;
}
if (converged) {
// Add debug info for switched controls
if (ws.production_cmode != pmode_orig || ws.injection_cmode != imode_orig) {
std::string from,to;
if (this->isInjector()) {
from = WellInjectorCMode2String(imode_orig);
to = WellInjectorCMode2String(ws.injection_cmode);
} else {
from = WellProducerCMode2String(pmode_orig);
to = WellProducerCMode2String(ws.production_cmode);
}
const auto msg = fmt::format(" Well {} switched from {} to {} during local solve", this->name(), from, to);
deferred_logger.debug(msg);
const int episodeIdx = simulator.episodeIndex();
const int iterationIdx = simulator.model().newtonMethod().numIterations();
const auto& schedule = simulator.vanguard().schedule();
const int nupcol = schedule[episodeIdx].nupcol();
// We always store the current control as it is used for output
// and only after iteration >= nupcol
// we log all switches to check if the well controls oscillates
if (iterationIdx >= nupcol || this->well_control_log_.empty()) {
this->well_control_log_.push_back(from);
}
}
}
return converged;
}
@@ -762,13 +794,17 @@ namespace Opm
thp_control = ws.injection_cmode == Well::InjectorCMode::THP;
if (thp_control) {
ws.injection_cmode = Well::InjectorCMode::BHP;
this->well_control_log_.push_back(WellInjectorCMode2String(Well::InjectorCMode::THP));
if (this->well_control_log_.empty()) { // only log the first control
this->well_control_log_.push_back(WellInjectorCMode2String(Well::InjectorCMode::THP));
}
}
} else {
thp_control = ws.production_cmode == Well::ProducerCMode::THP;
if (thp_control) {
ws.production_cmode = Well::ProducerCMode::BHP;
this->well_control_log_.push_back(WellProducerCMode2String(Well::ProducerCMode::THP));
if (this->well_control_log_.empty()) { // only log the first control
this->well_control_log_.push_back(WellProducerCMode2String(Well::ProducerCMode::THP));
}
}
}
if (thp_control) {
@@ -844,9 +880,6 @@ namespace Opm
const int iteration_idx = simulator.model().newtonMethod().numIterations();
if (iteration_idx < this->param_.max_niter_inner_well_iter_ || this->well_ecl_.isMultiSegment()) {
const auto& ws = well_state.well(this->indexOfWell());
const auto pmode_orig = ws.production_cmode;
const auto imode_orig = ws.injection_cmode;
const bool nonzero_rate_original =
std::any_of(ws.surface_rates.begin(),
ws.surface_rates.begin() + well_state.numPhases(),
@@ -865,19 +898,6 @@ namespace Opm
this->openWell();
deferred_logger.debug(" " + this->name() + " is re-opened after being stopped during local solve");
}
// Add debug info for switched controls
if (ws.production_cmode != pmode_orig || ws.injection_cmode != imode_orig) {
std::string from,to;
if (this->isInjector()) {
from = WellInjectorCMode2String(imode_orig);
to = WellInjectorCMode2String(ws.injection_cmode);
} else {
from = WellProducerCMode2String(pmode_orig);
to = WellProducerCMode2String(ws.production_cmode);
}
deferred_logger.debug(" " + this->name() + " switched from " + from + " to " + to + " during local solve");
}
} else {
// unsolvable wells are treated as not operable and will not be solved for in this iteration.
if (this->param_.shut_unsolvable_wells_)