Skip to content

Commit

Permalink
Add PID controller, refs idaholab#17271
Browse files Browse the repository at this point in the history
Update tests
Add resiliency to picard iteration fails
Add integral windup reset
  • Loading branch information
GiudGiud committed Apr 13, 2021
1 parent 9d937e2 commit 0566a9c
Show file tree
Hide file tree
Showing 13 changed files with 584 additions and 1 deletion.
23 changes: 23 additions & 0 deletions framework/doc/content/source/controls/PIDTransientControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# PIDTransientControl

The `PIDTransientControl` object is designed to use the principle of Proportional Integral Derivative control to either:
- control a "Real" parameter from the input file rather than use the constant value specified in the input file.

- modify the value of a postprocessor, used in a PostprocessorDirichletBC for example, rather than use the computed/received value


This allows a simple 1D parametric optimization to make the output of a postprocessor match the `target` value.

The parameter $C$ is replaced at every time step $n$, at time $t$, by:
\begin{equation}
C_{n} = C_{n-1} + K_{integral} \int_0^{t} pp(s) - target mathrm{d}s + K_{proportional} (pp(t) - target) + K_{derivative} \dfrac{pp(t) - target}{dt}
\end{equation}

with $pp(t)$ the value at time $t$ of the postprocessor to compare to the $target$ value,
$K_i$ the coefficients for the integral error, current error, and backward derivative.

!syntax parameters /Controls/PIDTransientControl

!syntax inputs /Controls/PIDTransientControl

!syntax children /Controls/PIDTransientControl
61 changes: 61 additions & 0 deletions framework/include/controls/PIDTransientControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#pragma once

// moose includes
#include "Control.h"

/**
* A time-dependent control of an input parameter or a postprocessor, which aims at
* making a postprocessor match a desired value.
*/
class PIDTransientControl : public Control
{
public:
/**
* Class constructor
* @param parameters Input parameters for this Control object
*/
static InputParameters validParams();

PIDTransientControl(const InputParameters & parameters);

virtual void execute() override;

private:
/// The current value of the target postprocessor
const PostprocessorValue & _current;
/// The target 1D time-dependent function for the postprocessor
const Function & _target;
/// Integral of the error
Real _integral;
/// The coefficient multiplying the integral of the error
const Real _Kint;
/// The coefficient multiplying the error
const Real _Kpro;
/// The coefficient multiplying the derivative of the error
const Real _Kder;
/// The time to start the PID controller on
const Real _start_time;
/// The time to stop using the PID controller on
const Real _stop_time;
/// Whether to reset the PID integral error when changing timestep, to limit its action to within coupling iterations
const bool _reset_every_timestep;
/// Whether to reset the PID integral error when the error crosses 0, to avoid windup
const bool _reset_integral_windup;
/// Saved value of the integral at the beginning of a timestep, to recover from a failed solve
Real _integral_old;
/// Saved value of the controlled parameter at the beginning of a timestep, to recover from a failed solve
Real _value_old;
/// the previous time step
int _t_step_old;
/// the previous value of the difference with the target, to detect changes of sign
Real _old_delta;
};
138 changes: 138 additions & 0 deletions framework/src/controls/PIDTransientControl.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

// MOOSE includes
#include "PIDTransientControl.h"
#include "Function.h"
#include "Transient.h"

registerMooseObject("MooseApp", PIDTransientControl);

InputParameters
PIDTransientControl::validParams()
{
InputParameters params = Control::validParams();
params.addClassDescription(
"Sets the value of a 'Real' input parameter (or postprocessor) based on a Proportional Integral "
"Derivative control of a postprocessor to match a target a target value.");
params.addRequiredParam<PostprocessorName>(
"postprocessor", "The postprocessor to watch for controlling the specified parameter.");
params.addRequiredParam<FunctionName>("target", "The target value 1D time function for the postprocessor");
params.addRequiredParam<Real>("K_integral", "The coefficient multiplying the integral term");
params.addRequiredParam<Real>("K_proportional", "The coefficient multiplying the difference term");
params.addRequiredParam<Real>("K_derivative", "The coefficient multiplying the derivative term");
params.addParam<std::string>(
"parameter",
"The input parameter(s) to control. Specify a single parameter name and all "
"parameters in all objects matching the name will be updated");
params.addParam<std::string>("parameter_pp", "The postprocessor to control. Should be accessed by reference by "
"the objects depending on its value.");
params.addParam<Real>("start_time", -std::numeric_limits<Real>::max(), "The time to start the PID controller at");
params.addParam<Real>("stop_time", std::numeric_limits<Real>::max(), "The time to stop the PID controller at");
params.addParam<bool>("reset_every_timestep",
false,
"Reset the PID integral when changing timestep, for coupling iterations within a timestep");
params.addParam<bool>("reset_integral_windup",
true,
"Reset the PID integral when the error crosses zero and the integral is larger than the error.");

return params;
}

PIDTransientControl::PIDTransientControl(const InputParameters & parameters)
: Control(parameters),
_current(getPostprocessorValueByName(getParam<PostprocessorName>("postprocessor"))),
_target(getFunction("target")),
_Kint(getParam<Real>("K_integral")),
_Kpro(getParam<Real>("K_proportional")),
_Kder(getParam<Real>("K_derivative")),
_start_time(getParam<Real>("start_time")),
_stop_time(getParam<Real>("stop_time")),
_reset_every_timestep(getParam<bool>("reset_every_timestep")),
_reset_integral_windup(getParam<bool>("reset_integral_windup"))
{
_integral = 0;

// To keep the previous values if a multiapp coupling iteration fails
_value_old = 0;
_integral_old = 0;

if (isParamValid("parameter") && isParamValid("parameter_pp"))
paramError("parameter_pp",
"Either a controllable parameter or a postprocessor to control should be specified, not both.");
if (!isParamValid("parameter") && !isParamValid("parameter_pp"))
mooseError("A parameter or a postprocessor to control should be specified.");
if (isParamValid("parameter") && _reset_every_timestep)
paramError("reset_every_timestep",
"Resetting the PID every time step is only supported using controlled postprocessors");
}

void
PIDTransientControl::execute()
{
Point dummy;

if (_t >= _start_time && _t < _stop_time)
{
// Get the current value of the controllable parameter
Real value;
if (isParamValid("parameter"))
value = getControllableValue<Real>("parameter");
else
value = getPostprocessorValueByName(getParam<std::string>("parameter_pp"));

// Save integral and controlled value at each time step
// if the solver fails, a smaller time step will be used but _t_step is unchanged
if (_t_step != _t_step_old)
{
// Reset the error integral if PID is only used within each timestep
if (_reset_every_timestep)
_integral = 0;

_integral_old = _integral;
_value_old = value;
_t_step_old = _t_step;
_old_delta = 0;
}

// If there were coupling/Picard iterations during the transient and they failed,
// we need to reset the controlled value and the error integral to their initial value at the
// beginning of the coupling process
if (dynamic_cast<Transient *>(_app.getExecutioner())->picardSolve().numPicardIts() == 1)
{
_integral = _integral_old;
value = _value_old;
}

// Compute the delta between the current value of the postprocessor and the desired value
Real delta = _current - _target.value(_t, dummy);

// If desired, reset integral of the error if the error crosses zero
if (_reset_integral_windup && delta * _old_delta < 0)
{
_integral = 0;
}

// Compute the three error terms and add them to the controlled value
_integral += delta * _dt;
value += _Kint * _integral + _Kpro * delta;
if (_dt > 0)
value += _Kder * delta / _dt;

if (isParamValid("parameter"))
// Set the new value using the Control system
setControllableValue<Real>("parameter", value);
else
// Set the new postprocessor value using the FE Problem
_fe_problem.setPostprocessorValueByName(getParam<std::string>("parameter_pp"), value);

// Keep track of the previous delta for integral windup control
_old_delta = delta;
}
}
2 changes: 1 addition & 1 deletion test/src/timesteppers/TimeSequenceStepperFailTest.C
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ TimeSequenceStepperFailTest::step()
{
TimeStepper::step();

// The goal is to fail exactly one timestep which matches its
// The goal is to fail exactly on the timestep which matches its
// original sequence point order, other than the initial condition.
// This can only happen once, since after you fail, you are no
// longer on the original time sequence (off by one or more). Since
Expand Down
22 changes: 22 additions & 0 deletions test/tests/controls/pid_control/gold/out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
time,integral
0,0
1,1.2500000019024
2,1.5124999979718
3,1.6243749993119
4,1.6125312499924
5,1.5509359379194
6,1.4977500783125
7,1.4749414026996
8,1.4772067971694
9,1.4896228260422
10,1.5003986260823
11,1.5050484230471
12,1.5046166201441
13,1.5021139987568
14,1.4999308197787
15,1.4989829793333
16,1.4990649614658
17,1.4995693726957
18,1.5000116713998
19,1.5002048701164
20,1.5001893744816
22 changes: 22 additions & 0 deletions test/tests/controls/pid_control/gold/picard_out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
time,integral,received_bc
0,0,1.5
1,1.2499999929539,2.0250000147969
2,1.5125000086897,1.9987499965485
3,1.4993749983264,2.0000625000631
4,1.5000312500345,1.9999968749907
5,1.499998437495,2.0000001562513
6,1.5000000781256,1.9999999921874
7,1.4999999960937,2.0000000003906
8,1.5000000001953,1.9999999999805
9,1.4999999999902,2.000000000001
10,1.4999999999902,2.0000000000215
11,1.4999999999902,2.000000000042
12,1.500000000021,1.9999999999979
13,1.500000000021,1.9999999999538
14,1.4999999999769,2.0000000000023
15,1.4999999999769,2.0000000000508
16,1.5000000000254,1.9999999999975
17,1.5000000000254,1.9999999999441
18,1.4999999999721,2.0000000000028
19,1.5000000000014,1.9999999999999
20,1.5000000000014,1.9999999999969
22 changes: 22 additions & 0 deletions test/tests/controls/pid_control/gold/picard_resets_out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
time,integral,received_bc
0,0,0
1,2.0750000002998,3.15
1.5,2.3362499979628,3.6724999994903
2.5,2.0644374994832,3.1288750036185
3.5,1.6599031321169,2.3198062565912
4.5,1.3979110885044,1.7958221815498
5.5,1.3310591330534,1.6621182659777
6.5,1.3854461844374,1.7708923683484
7.5,1.4671972648003,1.9343945297591
8.5,1.5203866191641,2.0407732379705
9.5,1.5341285185424,2.0682570372176
10.5,1.5232481144013,2.0464962286063
11.5,1.5067278750744,2.0134557501487
12.5,1.4959298298578,1.9918596598765
13.5,1.4931057947564,1.9862115894847
14.5,1.4952820814815,1.9905641629481
15.5,1.4986203697672,1.9972407395323
16.5,1.5008124146255,2.0016248292351
17.5,1.5013926274845,2.0027852549683
18.5,1.5009574095391,2.0019148190721
19.5,1.5002828566841,2.0005657133767
22 changes: 22 additions & 0 deletions test/tests/controls/pid_control/gold/pp_out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
time,integral,received_bc
0,0,1.5
1,1.2499999929539,2.0250000147969
2,1.5125000086897,2.2487500035946
3,1.6243750013529,2.2250624991099
4,1.6125312496318,2.1018718718867
5,1.5509359359113,1.9955001538447
6,1.4977500772041,1.9498828031766
7,1.474941401493,1.9544135942976
8,1.4772067971532,1.9792456530392
9,1.489622826589,2.0007972528125
10,1.5003986263689,2.010096846459
11,1.5050484232342,2.0092332403195
12,1.5046166201597,2.0042279974022
13,1.5021139987112,1.9998616393671
14,1.4999308196738,1.9979659585993
15,1.4989829793008,1.998129922941
16,1.4990649614703,1.999138745426
17,1.4995693727126,2.0000233428318
18,1.5000116714148,2.0004097402504
19,1.5002048701266,2.0003787489593
20,1.5001893744798,2.0001754284
22 changes: 22 additions & 0 deletions test/tests/controls/pid_control/gold/windup_out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
time,integral
0,0
1,1.2500000019024
2,1.5124999979718
3,1.6243749993119
4,1.6125312499924
5,1.5509359379194
6,1.4977500783125
7,1.4749414026996
8,1.4772067971694
9,1.4896228260422
10,1.5003986260823
11,1.5050484230471
12,1.5046166201441
13,1.5021139987568
14,1.4999308197787
15,1.4989829793333
16,1.4990649614658
17,1.4995693726957
18,1.5000116713998
19,1.5002048701164
20,1.5001893744816

0 comments on commit 0566a9c

Please sign in to comment.