mirror of
https://github.com/OPM/opm-simulators.git
synced 2026-10-25 15:10:09 -05:00
502 lines
19 KiB
C++
502 lines
19 KiB
C++
/*
|
|
Copyright 2014 IRIS AS
|
|
Copyright 2015 Dr. Blatt - HPC-Simulation-Software & Services
|
|
Copyright 2015 Statoil AS
|
|
|
|
This file is part of the Open Porous Media project (OPM).
|
|
|
|
OPM is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OPM is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <config.h>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <limits>
|
|
|
|
#include <opm/common/ErrorMacros.hpp>
|
|
#include <opm/common/OpmLog/OpmLog.hpp>
|
|
#include <opm/input/eclipse/Units/Units.hpp>
|
|
#include <opm/simulators/timestepping/TimeStepControl.hpp>
|
|
#include <opm/simulators/timestepping/AdaptiveSimulatorTimer.hpp>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace Opm
|
|
{
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// InterationCountTimeStepControl Implementation
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
SimpleIterationCountTimeStepControl::
|
|
SimpleIterationCountTimeStepControl( const int target_iterations,
|
|
const double decayrate,
|
|
const double growthrate,
|
|
const bool verbose)
|
|
: target_iterations_( target_iterations )
|
|
, decayrate_( decayrate )
|
|
, growthrate_( growthrate )
|
|
, verbose_( verbose )
|
|
{
|
|
if( decayrate_ > 1.0 ) {
|
|
OPM_THROW(std::runtime_error,
|
|
"SimpleIterationCountTimeStepControl: "
|
|
"decay should be <= 1 " + std::to_string(decayrate_));
|
|
}
|
|
if( growthrate_ < 1.0 ) {
|
|
OPM_THROW(std::runtime_error,
|
|
"SimpleIterationCountTimeStepControl: "
|
|
"growth should be >= 1 " + std::to_string(growthrate_));
|
|
}
|
|
}
|
|
|
|
SimpleIterationCountTimeStepControl
|
|
SimpleIterationCountTimeStepControl::serializationTestObject()
|
|
{
|
|
return {1, 1.0, 2.0, true};
|
|
}
|
|
|
|
double
|
|
SimpleIterationCountTimeStepControl::computeTimeStepSize(const double dt,
|
|
const int iterations,
|
|
const RelativeChangeInterface& /* relativeChange */,
|
|
const AdaptiveSimulatorTimer& /* substepTimer */) const
|
|
{
|
|
double dtEstimate = dt ;
|
|
|
|
// reduce the time step size if we exceed the number of target iterations
|
|
if( iterations > target_iterations_ )
|
|
{
|
|
// scale dtEstimate down with a given rate
|
|
dtEstimate *= decayrate_;
|
|
}
|
|
// increase the time step size if we are below the number of target iterations
|
|
else if ( iterations < target_iterations_ )
|
|
{
|
|
dtEstimate *= growthrate_;
|
|
}
|
|
|
|
if( verbose_ )
|
|
OpmLog::debug(fmt::format("Suggested next time step size (from controller): {} days",
|
|
unit::convert::to( dtEstimate, unit::day )));
|
|
|
|
return dtEstimate;
|
|
}
|
|
|
|
bool SimpleIterationCountTimeStepControl::
|
|
operator==(const SimpleIterationCountTimeStepControl& ctrl) const
|
|
{
|
|
return this->target_iterations_ == ctrl.target_iterations_ &&
|
|
this->decayrate_ == ctrl.decayrate_ &&
|
|
this->growthrate_ == ctrl.growthrate_ &&
|
|
this->verbose_ == ctrl.verbose_;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// HardcodedTimeStepControl Implementation
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
HardcodedTimeStepControl::
|
|
HardcodedTimeStepControl(const std::string& filename)
|
|
{
|
|
std::ifstream infile (filename);
|
|
if (!infile.is_open()) {
|
|
OPM_THROW(std::runtime_error,"Incorrect or no filename is provided to the hardcodedTimeStep. "
|
|
"Use timestep.control.filename=your_file_name");
|
|
}
|
|
std::string::size_type sz;
|
|
std::string line;
|
|
while ( std::getline(infile, line)) {
|
|
if( line[0] != '-') { // ignore lines starting with '-'
|
|
const double time = std::stod(line,&sz); // read the first number i.e. the actual substep time
|
|
subStepTime_.push_back( time * unit::day );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
HardcodedTimeStepControl HardcodedTimeStepControl::serializationTestObject()
|
|
{
|
|
HardcodedTimeStepControl result;
|
|
result.subStepTime_ = {1.0, 2.0};
|
|
|
|
return result;
|
|
}
|
|
|
|
double
|
|
HardcodedTimeStepControl::computeTimeStepSize(const double /*dt */,
|
|
const int /*iterations */,
|
|
const RelativeChangeInterface& /* relativeChange */,
|
|
const AdaptiveSimulatorTimer& substepTimer) const
|
|
{
|
|
auto nextTime
|
|
= std::upper_bound(subStepTime_.begin(), subStepTime_.end(), substepTimer.simulationTimeElapsed());
|
|
return (*nextTime - substepTimer.simulationTimeElapsed());
|
|
}
|
|
|
|
bool HardcodedTimeStepControl::operator==(const HardcodedTimeStepControl& ctrl) const
|
|
{
|
|
return this->subStepTime_ == ctrl.subStepTime_;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
//
|
|
// PIDTimeStepControl Implementation
|
|
//
|
|
////////////////////////////////////////////////////////
|
|
|
|
PIDTimeStepControl::PIDTimeStepControl( const double tol,
|
|
const bool verbose )
|
|
: tol_( tol )
|
|
, errors_( 3, tol_ )
|
|
, verbose_( verbose )
|
|
{}
|
|
|
|
PIDTimeStepControl
|
|
PIDTimeStepControl::serializationTestObject()
|
|
{
|
|
PIDTimeStepControl result(1.0, true);
|
|
result.errors_ = {2.0, 3.0};
|
|
|
|
return result;;
|
|
}
|
|
|
|
double
|
|
PIDTimeStepControl::computeTimeStepSize(const double dt,
|
|
const int /* iterations */,
|
|
const RelativeChangeInterface& relChange,
|
|
const AdaptiveSimulatorTimer& /* substepTimer */) const
|
|
{
|
|
// shift errors
|
|
for( int i=0; i<2; ++i ) {
|
|
errors_[ i ] = errors_[i+1];
|
|
}
|
|
|
|
// store new error
|
|
const double error = relChange.relativeChange();
|
|
errors_[ 2 ] = error;
|
|
for( int i=0; i<2; ++i ) {
|
|
assert(std::isfinite(errors_[i]));
|
|
}
|
|
|
|
if( errors_[2] > tol_ )
|
|
{
|
|
// adjust dt by given tolerance
|
|
const double newDt = dt * tol_ / error;
|
|
if ( verbose_ )
|
|
OpmLog::debug(fmt::format("Suggested next time step size (from PID controller): {} days",
|
|
unit::convert::to( newDt, unit::day )));
|
|
return newDt;
|
|
}
|
|
else if (errors_[0] == 0 || errors_[1] == 0 || errors_[2] == 0.)
|
|
{
|
|
if ( verbose_ )
|
|
OpmLog::debug("The solution between time steps does not change, "
|
|
"there is no time step constraint from the PID time step control ");
|
|
return std::numeric_limits<double>::max();
|
|
}
|
|
else
|
|
{
|
|
// values taking from turek time stepping paper
|
|
const double kP = 0.075 ;
|
|
const double kI = 0.175 ;
|
|
const double kD = 0.01 ;
|
|
const double newDt = (dt * std::pow( errors_[ 1 ] / errors_[ 2 ], kP ) *
|
|
std::pow( tol_ / errors_[ 2 ], kI ) *
|
|
std::pow( errors_[1]*errors_[1]/errors_[ 0 ]/errors_[ 2 ], kD ));
|
|
if( verbose_ )
|
|
OpmLog::debug(fmt::format("Suggested next time step size (from PID controller): {} days",
|
|
unit::convert::to( newDt, unit::day )));
|
|
return newDt;
|
|
}
|
|
}
|
|
|
|
bool PIDTimeStepControl::operator==(const PIDTimeStepControl& ctrl) const
|
|
{
|
|
return this->tol_ == ctrl.tol_ &&
|
|
this->errors_ == ctrl.errors_ &&
|
|
this->verbose_ == ctrl.verbose_;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//
|
|
// PIDAndIterationCountTimeStepControl Implementation
|
|
//
|
|
////////////////////////////////////////////////////////////
|
|
|
|
PIDAndIterationCountTimeStepControl::
|
|
PIDAndIterationCountTimeStepControl( const int target_iterations,
|
|
const double decayDampingFactor,
|
|
const double growthDampingFactor,
|
|
const double tol,
|
|
const double minTimeStepBasedOnIterations,
|
|
const bool verbose)
|
|
: PIDTimeStepControl( tol, verbose )
|
|
, target_iterations_( target_iterations )
|
|
, decayDampingFactor_( decayDampingFactor )
|
|
, growthDampingFactor_( growthDampingFactor )
|
|
, minTimeStepBasedOnIterations_(minTimeStepBasedOnIterations)
|
|
, verbose_(verbose)
|
|
{}
|
|
|
|
PIDAndIterationCountTimeStepControl
|
|
PIDAndIterationCountTimeStepControl::serializationTestObject()
|
|
{
|
|
return PIDAndIterationCountTimeStepControl{1, 2.0, 3.0, 4.0, 5.0, true};
|
|
}
|
|
|
|
double
|
|
PIDAndIterationCountTimeStepControl::computeTimeStepSize(const double dt,
|
|
const int iterations,
|
|
const RelativeChangeInterface& relChange,
|
|
const AdaptiveSimulatorTimer& substepTimer) const
|
|
{
|
|
double dtEstimatePID = PIDTimeStepControl :: computeTimeStepSize( dt, iterations, relChange, substepTimer);
|
|
|
|
// adjust timesteps based on target iteration
|
|
double dtEstimateIter;
|
|
if (iterations > target_iterations_) {
|
|
double off_target_fraction = double(iterations - target_iterations_) / target_iterations_;
|
|
dtEstimateIter = dt / (1.0 + off_target_fraction * decayDampingFactor_);
|
|
if (dtEstimateIter < minTimeStepBasedOnIterations_) {
|
|
dtEstimateIter = minTimeStepBasedOnIterations_;
|
|
}
|
|
} else {
|
|
double off_target_fraction = double(target_iterations_ - iterations) / target_iterations_;
|
|
// Be a bit more careful when increasing.
|
|
dtEstimateIter = dt * (1.0 + off_target_fraction * growthDampingFactor_);
|
|
}
|
|
|
|
if( verbose_ )
|
|
OpmLog::debug(fmt::format("Suggested next time step size (from iteration target): {} days",
|
|
unit::convert::to( dtEstimateIter, unit::day )));
|
|
|
|
return std::min(dtEstimatePID, dtEstimateIter);
|
|
}
|
|
|
|
bool PIDAndIterationCountTimeStepControl::operator==(const PIDAndIterationCountTimeStepControl& ctrl) const
|
|
{
|
|
return static_cast<const PIDTimeStepControl&>(*this) == ctrl &&
|
|
this->target_iterations_ == ctrl.target_iterations_ &&
|
|
this->decayDampingFactor_ == ctrl.decayDampingFactor_ &&
|
|
this->growthDampingFactor_ == ctrl.growthDampingFactor_ &&
|
|
this->minTimeStepBasedOnIterations_ == ctrl.minTimeStepBasedOnIterations_ &&
|
|
this->verbose_ == ctrl.verbose_;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//
|
|
// General3rdOrderController Implementation
|
|
//
|
|
////////////////////////////////////////////////////////////
|
|
|
|
General3rdOrderController::General3rdOrderController( const double tolerance,
|
|
const double safetyFactor,
|
|
const bool rejectCompletedStep,
|
|
const std::string& toleranceTestVersion,
|
|
const double maxReductionTimeStep,
|
|
const std::string& parameters,
|
|
const bool verbose)
|
|
: tolerance_( tolerance )
|
|
, safetyFactor_( safetyFactor )
|
|
, rejectCompletedStep_( rejectCompletedStep )
|
|
, maxReductionTimeStep_( maxReductionTimeStep )
|
|
, verbose_( verbose )
|
|
{
|
|
std::istringstream parameters_stream(parameters);
|
|
std::string value;
|
|
int counter = 0;
|
|
try {
|
|
while (std::getline(parameters_stream, value, ';'))
|
|
{
|
|
if (counter < 3) {
|
|
beta_[counter] = std::stod(value);
|
|
}
|
|
else {
|
|
alpha_[counter - 3] = std::stod(value);
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
catch (const std::exception& e) {
|
|
OPM_THROW(std::runtime_error, "The parameter string for the time step controller is invalid: " + parameters);
|
|
}
|
|
|
|
if (toleranceTestVersion == "standard") {
|
|
toleranceTestVersion_ = ToleranceTestVersions::Standard;
|
|
}
|
|
else if (toleranceTestVersion == "control-error-filtering") {
|
|
toleranceTestVersion_ = ToleranceTestVersions::ControlErrorFiltering;
|
|
}
|
|
else {
|
|
OPM_THROW(std::runtime_error, "Unsupported tolerance test version given: " + toleranceTestVersion);
|
|
}
|
|
}
|
|
|
|
General3rdOrderController
|
|
General3rdOrderController::serializationTestObject()
|
|
{
|
|
General3rdOrderController result(1.0, 2.0, false, "standard", 3.0, "0.125;0.25;0.125;0.75;0.25", false);
|
|
result.errors_ = {2.0, 3.0};
|
|
|
|
return result;
|
|
}
|
|
|
|
double
|
|
General3rdOrderController::computeTimeStepSize(const double dt,
|
|
const int /* iterations */,
|
|
const RelativeChangeInterface& /* relChange */,
|
|
const AdaptiveSimulatorTimer& substepTimer) const
|
|
{
|
|
for( int i = 0; i < 2; ++i )
|
|
{
|
|
assert(std::isfinite(errors_[i]));
|
|
}
|
|
|
|
if (substepTimer.currentStepNum() < 3) // Use an I controller after report time steps
|
|
{
|
|
controllerVersion_ = InternalControlVersions::IController;
|
|
}
|
|
else // Use the general third order controller for all other time steps
|
|
{
|
|
controllerVersion_ = InternalControlVersions::General3rdOrder;
|
|
}
|
|
|
|
bool noTimeStepConstraint = false;
|
|
|
|
if (controllerVersion_ == InternalControlVersions::IController)
|
|
{
|
|
if (errors_[2] == 0)
|
|
{
|
|
noTimeStepConstraint = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors_[0] == 0 || errors_[1] == 0 || errors_[2] == 0)
|
|
{
|
|
noTimeStepConstraint = true;
|
|
}
|
|
}
|
|
|
|
if (noTimeStepConstraint)
|
|
{
|
|
if ( verbose_ )
|
|
{
|
|
OpmLog::debug("The solution between time steps does not change, "
|
|
"there is no time step constraint from the controller.");
|
|
}
|
|
return std::numeric_limits<double>::max();
|
|
}
|
|
|
|
const double newDt = dt * timeStepFactor(errors_, timeSteps_);
|
|
|
|
if (verbose_)
|
|
{
|
|
OpmLog::debug(fmt::format("Suggested next time step size (from controller): {} days",
|
|
unit::convert::to( newDt, unit::day )));
|
|
OpmLog::debug(fmt::format("Most recent relative change: {}", errors_[2]));
|
|
}
|
|
return newDt;
|
|
}
|
|
|
|
double General3rdOrderController::
|
|
timeStepFactor(const std::array<double, 3>& errors, const std::array<double, 3>& timeSteps) const
|
|
{
|
|
if (controllerVersion_ == InternalControlVersions::General3rdOrder)
|
|
{
|
|
return std::pow(safetyFactor_ * tolerance_ / errors[2], beta_[0]) *
|
|
std::pow(safetyFactor_ * tolerance_ / errors[1], beta_[1]) *
|
|
std::pow(safetyFactor_ * tolerance_ / errors[0], beta_[2]) *
|
|
std::pow(timeSteps[2] / timeSteps[1], -alpha_[0]) *
|
|
std::pow(timeSteps[1] / timeSteps[0], -alpha_[1]);
|
|
}
|
|
// controllerVersion_ == InternalControlVersions::IController
|
|
return std::pow(safetyFactor_ * tolerance_ / errors[2], 0.35);
|
|
}
|
|
|
|
bool General3rdOrderController::
|
|
timeStepAccepted(const double error, const double timeStepJustCompleted) const
|
|
{
|
|
bool acceptTimeStep = true;
|
|
|
|
// Reject time step if chosen tolerance test version fails
|
|
if (toleranceTestVersion_ == ToleranceTestVersions::Standard)
|
|
{
|
|
if (rejectCompletedStep_ && error > tolerance_)
|
|
{
|
|
acceptTimeStep = false;
|
|
}
|
|
}
|
|
else if (toleranceTestVersion_ == ToleranceTestVersions::ControlErrorFiltering)
|
|
{
|
|
const std::array<double, 3> tempErrors{errors_[1], errors_[2], error};
|
|
const std::array<double, 3> tempTimeSteps{timeSteps_[1], timeSteps_[2], timeStepJustCompleted};
|
|
const double stepFactor = timeStepFactor(tempErrors, tempTimeSteps);
|
|
if (rejectCompletedStep_ && stepFactor < maxReductionTimeStep_)
|
|
{
|
|
acceptTimeStep = false;
|
|
}
|
|
}
|
|
|
|
if (acceptTimeStep)
|
|
{
|
|
// Shift errors and time steps
|
|
for( int i = 0; i < 2; ++i )
|
|
{
|
|
errors_[i] = errors_[i+1];
|
|
timeSteps_[i] = timeSteps_[i+1];
|
|
}
|
|
|
|
// Store new error and time step
|
|
errors_[2] = error;
|
|
timeSteps_[2] = timeStepJustCompleted;
|
|
}
|
|
|
|
return acceptTimeStep;
|
|
}
|
|
|
|
bool General3rdOrderController::operator==(const General3rdOrderController& ctrl) const
|
|
{
|
|
return this->tolerance_ == ctrl.tolerance_ &&
|
|
this->safetyFactor_ == ctrl.safetyFactor_ &&
|
|
this->rejectCompletedStep_ == ctrl.rejectCompletedStep_ &&
|
|
this->errors_ == ctrl.errors_ &&
|
|
this->timeSteps_ == ctrl.timeSteps_ &&
|
|
this->beta_ == ctrl.beta_ &&
|
|
this->alpha_ == ctrl.alpha_ &&
|
|
this->controllerVersion_ == ctrl.controllerVersion_ &&
|
|
this->toleranceTestVersion_ == ctrl.toleranceTestVersion_ &&
|
|
this->maxReductionTimeStep_ == ctrl.maxReductionTimeStep_ &&
|
|
this->verbose_ == ctrl.verbose_;
|
|
}
|
|
|
|
} // end namespace Opm
|