https://mooseframework.inl.gov
PIDTransientControl.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "PIDTransientControl.h"
11 #include "Function.h"
12 #include "TransientBase.h"
13 #include "TimeStepper.h"
14 #include "FEProblemBase.h"
15 #include "MooseApp.h"
16 
18 
21 {
23  params.addClassDescription(
24  "Sets the value of a 'Real' input parameter (or postprocessor) based on a Proportional "
25  "Integral Derivative control of a postprocessor to match a target a target value.");
26  params.addRequiredParam<PostprocessorName>(
27  "postprocessor", "The postprocessor to watch for controlling the specified parameter.");
28  params.addRequiredParam<FunctionName>("target",
29  "The target value 1D time function for the postprocessor");
30  params.addRequiredParam<Real>("K_integral", "The coefficient multiplying the integral term");
31  params.addRequiredParam<Real>("K_proportional",
32  "The coefficient multiplying the difference term");
33  params.addRequiredParam<Real>("K_derivative", "The coefficient multiplying the derivative term");
34  params.addParam<std::string>(
35  "parameter",
36  "The input parameter(s) to control. Specify a single parameter name and all "
37  "parameters in all objects matching the name will be updated");
38  params.addParam<std::string>("parameter_pp",
39  "The postprocessor to control. Should be accessed by reference by "
40  "the objects depending on its value.");
41  params.addParam<Real>(
42  "start_time", -std::numeric_limits<Real>::max(), "The time to start the PID controller at");
43  params.addParam<Real>(
44  "stop_time", std::numeric_limits<Real>::max(), "The time to stop the PID controller at");
45  params.addParam<bool>(
46  "reset_every_timestep",
47  false,
48  "Reset the PID integral when changing timestep, for coupling iterations within a timestep");
49  params.addParam<bool>("reset_integral_windup",
50  true,
51  "Reset the PID integral when the error crosses zero and the integral is "
52  "larger than the error.");
53 
54  params.addParam<Real>("maximum_output_value",
56  "Can be used to limit the maximum value output by the PID controller.");
57  params.addParam<Real>("minimum_output_value",
59  "Can be used to limit the minimum value output by the PID controller.");
60  params.addRangeCheckedParam<Real>(
61  "maximum_change_rate",
63  "maximum_change_rate>0",
64  "Can be used to limit the absolute rate of change per second of value "
65  "output by the PID controller.");
66  return params;
67 }
68 
70  : Control(parameters),
71  _current(getPostprocessorValueByName(getParam<PostprocessorName>("postprocessor"))),
72  _target(getFunction("target")),
73  _Kint(getParam<Real>("K_integral")),
74  _Kpro(getParam<Real>("K_proportional")),
75  _Kder(getParam<Real>("K_derivative")),
76  _start_time(getParam<Real>("start_time")),
77  _stop_time(getParam<Real>("stop_time")),
78  _reset_every_timestep(getParam<bool>("reset_every_timestep")),
79  _reset_integral_windup(getParam<bool>("reset_integral_windup")),
80  _maximum_output_value(getParam<Real>("maximum_output_value")),
81  _minimum_output_value(getParam<Real>("minimum_output_value")),
82  _maximum_change_rate(getParam<Real>("maximum_change_rate")),
83  _integral(declareRestartableData<Real>("pid_integral", 0)),
84  _integral_old(declareRestartableData<Real>("pid_integral_old", 0)),
85  _value(declareRestartableData<Real>("pid_value", 0)),
86  _value_old(declareRestartableData<Real>("pid_value_old", 0)),
87  _t_step_old(declareRestartableData<int>("pid_tstep_old", -1)),
88  _old_delta(declareRestartableData<Real>("pid_delta_old", 0)),
89  _has_recovered(false)
90 {
91  if (!_fe_problem.isTransient())
92  mooseError("PIDTransientControl is only meant to be used when the problem is transient, for "
93  "example with a Transient Executioner. Support for Steady "
94  "Executioner can be added in the future, however certain parameters are currently "
95  "not well defined for use with Picard iterations.");
96 
97  if (isParamValid("parameter") && isParamValid("parameter_pp"))
98  paramError("parameter_pp",
99  "Either a controllable parameter or a postprocessor to control should be specified, "
100  "not both.");
101  if (!isParamValid("parameter") && !isParamValid("parameter_pp"))
102  mooseError("A parameter or a postprocessor to control should be specified.");
103  if (isParamValid("parameter") && _reset_every_timestep)
104  paramError(
105  "reset_every_timestep",
106  "Resetting the PID every time step is only supported using controlled postprocessors");
108  mooseError(
109  "The parameters maximum_output_value and minimum_output_value are inconsistent. The value "
110  "of maximum_output_value should be greater than the value of minimum_output_value.");
111 }
112 
113 void
115 {
116  // Dont execute on INITIAL again on a recover.
117  // Executing on INITIAL in a regular simulation is fine, but on a recover we will get the integral
118  // term wrong and set the wrong attributes for the time derivative too. Best to skip
120  {
121  mooseInfo("Skipping execution on recover + INITIAL.");
122  return;
123  }
124 
125  if (_t >= _start_time && _t < _stop_time)
126  {
127  // Get the current value of the controllable parameter
128  if (isParamValid("parameter"))
129  {
130  // If just recovering, we must use the restartable value as the parameter value is
131  // not restarted
132  if (_app.isRecovering() && !_has_recovered)
133  _has_recovered = true;
134  // else get the current value of the parameter
135  else
136  _value = getControllableValue<Real>("parameter");
137  }
138  else
139  _value = getPostprocessorValueByName(getParam<std::string>("parameter_pp"));
140 
141  // Compute the delta between the current value of the postprocessor and the desired value
142  Real delta = _current - _target.value(_t);
143 
144  // Save integral and controlled value at each time step
145  // if the solver fails, a smaller time step will be used but _t_step is unchanged
146  if (_t_step != _t_step_old)
147  {
148  // Reset the error integral if PID is only used within each timestep
150  _integral = 0;
151 
153  _value_old = _value;
155  _delta_prev_tstep = delta;
157  }
158 
159  // If there were coupling/Picard iterations during the transient and they failed,
160  // we need to reset the controlled value and the error integral to their initial value at the
161  // beginning of the coupling process
163  {
165  _value = _value_old;
166  delta = _delta_prev_tstep;
168  }
169 
170  // If desired, reset integral of the error if the error crosses zero
171  if (_reset_integral_windup && delta * _old_delta < 0)
172  _integral = 0;
173 
174  // Compute the three error terms and add them to the controlled value
175  _integral += delta * _dt;
176  _value += _Kint * _integral + _Kpro * delta;
177  if (_dt > 0)
178  _value += _Kder * (delta - _old_delta) / _dt;
179 
180  // If the maximum rate of change by the pid is fixed
184 
185  // Compute the value, within the bounds
187 
188  // Set the new value of the parameter or postprocessor
189  if (isParamValid("parameter"))
190  setControllableValue<Real>("parameter", _value);
191  else
192  _fe_problem.setPostprocessorValueByName(getParam<std::string>("parameter_pp"), _value);
193 
194  // Keep track of the previous delta for integral windup control
195  // and for time derivative calculation
196  _old_delta = delta;
197  }
198 }
199 
200 void
202 {
203  if (_app.isRecovering())
204  {
205  if (isParamValid("parameter"))
206  setControllableValue<Real>("parameter", _value);
207  else
208  _fe_problem.setPostprocessorValueByName(getParam<std::string>("parameter_pp"), _value);
209  }
210 }
211 
212 void
214 {
215  const auto * t_ex = dynamic_cast<const TransientBase *>(_app.getExecutioner());
216  if (t_ex && t_ex->getTimeStepper()->justFailedTimeStep() &&
218  {
219  // We need to revert to the timestep begin state
221  _value = _value_old;
223  if (isParamValid("parameter"))
224  setControllableValue<Real>("parameter", _value);
225  else
226  _fe_problem.setPostprocessorValueByName(getParam<std::string>("parameter_pp"), _value);
227  }
228 }
void mooseInfo(Args &&... args) const
Definition: MooseBase.h:344
const Real _Kint
The coefficient multiplying the integral of the error.
const PostprocessorValue & _current
The current value of the target postprocessor.
static InputParameters validParams()
Class constructor.
Definition: Control.C:16
Real & _integral
Integral of the error.
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:467
const Real _stop_time
The time to stop using the PID controller on.
static InputParameters validParams()
const bool _reset_every_timestep
Whether to reset the PID integral error when changing timestep, to limit its action to within couplin...
void setPostprocessorValueByName(const PostprocessorName &name, const PostprocessorValue &value, std::size_t t_index=0)
Set the value of a PostprocessorValue.
const ExecFlagType & getCurrentExecuteOnFlag() const
Return/set the current execution flag.
const Real _Kder
The coefficient multiplying the derivative of the error.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
Real _delta_prev_tstep
the difference with the target from the previous time step, used if a time step fails ...
Real & _dt
Time step size.
const ExecFlagType EXEC_TIMESTEP_END
Definition: Moose.C:36
const bool _reset_integral_windup
Whether to reset the PID integral error when the error crosses 0, to avoid windup.
Real & _value_old
Saved value of the controlled parameter at the beginning of a timestep, to recover from a failed solv...
unsigned int numFixedPointIts() const
Get the number of fixed point iterations performed Because this returns the number of fixed point ite...
registerMooseObject("MooseApp", PIDTransientControl)
Real & _value
Saved value of the controlled parameter at the end of a timestep.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
A time-dependent control of an input parameter or a postprocessor, which aims at making a postprocess...
auto max(const L &left, const R &right)
bool _has_recovered
whether the app has recovered once, because the logic for setting the value is different after having...
bool contains(const std::string &value) const
Methods for seeing if a value is set in the MultiMooseEnum.
int & _t_step
The number of the time step.
const Real _minimum_output_value
Limiting minimum value for the output of the PID controller.
const Real _Kpro
The coefficient multiplying the error.
virtual void initialSetup() override
Called once at the beginning of the simulation, used to initialize recovered control values...
const Real _maximum_output_value
Limiting maximum value for the output of the PID controller.
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this object.
Definition: Control.h:76
const ExecFlagEnum & getExecuteOnEnum() const
Return the execute on MultiMooseEnum for this object.
const Function & _target
The target 1D time-dependent function for the postprocessor.
virtual void execute() override
Execute the control.
Real & _old_delta
the previous value of the difference with the target, to detect changes of sign, and to compute the d...
Real _old_delta_prev_tstep
the difference with the target from the second-to-last iteration of previous time step...
virtual void timestepSetup() override
Used to reset the PID when failing a timestep and the control is executed on timestep_end.
Base class for transient executioners that use a FixedPointSolve solve object for multiapp-main app i...
Definition: TransientBase.h:27
const Real _start_time
The time to start the PID controller on.
MooseApp & _app
The MOOSE application this is associated with.
Definition: MooseBase.h:385
int & _t_step_old
the previous time step
Executioner * getExecutioner() const
Retrieve the Executioner for this App.
Definition: MooseApp.C:1815
virtual const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name) const
Retrieve the value of the Postprocessor.
Real & _integral_old
Saved value of the integral at the beginning of a timestep, to recover from a failed solve...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Base class for Control objects.
Definition: Control.h:34
PIDTransientControl(const InputParameters &parameters)
Class constructor.
FixedPointSolve & fixedPointSolve()
Definition: Executioner.h:124
const Real _maximum_change_rate
Limiting maximum value for the rate of change of output of the PID controller.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:281
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition: MooseBase.h:209
virtual bool isTransient() const override
virtual Real value(Real t, const Point &p) const
Override this to evaluate the scalar function at point (t,x,y,z), by default this returns zero...
Definition: Function.C:30
bool isRecovering() const
Whether or not this is a "recover" calculation.
Definition: MooseApp.C:1493
auto min(const L &left, const R &right)
void ErrorVector unsigned int
const ExecFlagType EXEC_INITIAL
Definition: Moose.C:30