Skip to content

Commit

Permalink
Add PID controller, refs idaholab#17271
Browse files Browse the repository at this point in the history
  • Loading branch information
GiudGiud committed Mar 23, 2021
1 parent 9b96d07 commit 2fd594c
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 0 deletions.
19 changes: 19 additions & 0 deletions framework/doc/content/source/controls/PIDControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# PIDControl

The `PIDControl` object is designed to control a "Real" parameter with a Proportional Integral Derivative
controller rather than use the constant value specified in the input file. 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/PIDControl

!syntax inputs /Controls/PIDControl

!syntax children /Controls/PIDControl
55 changes: 55 additions & 0 deletions framework/include/controls/PIDControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//* 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"

// Forward declarations
class PIDControl;

template <>
InputParameters validParams<PIDControl>();

/**
* An time-dependent control for changing an input parameter to make a target
* postprocessor match a desired value.
*/
class PIDControl : public Control
{
public:
/**
* Class constructor
* @param parameters Input parameters for this Control object
*/
static InputParameters validParams();

PIDControl(const InputParameters & parameters);

virtual void execute() override;

private:
/// The current value of the target postprocessor
const PostprocessorValue & _current;
/// The target value for the postprocessor
const Real _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;
};
70 changes: 70 additions & 0 deletions framework/src/controls/PIDControl.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//* 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 "PIDControl.h"

registerMooseObject("MooseApp", PIDControl);

defineLegacyParams(PIDControl);

InputParameters
PIDControl::validParams()
{
InputParameters params = Control::validParams();
params.addClassDescription(
"Sets the value of a 'Real' input parameters based on a Proportional Integral "
"Derivative control to make a postprocessor a target value.");
params.addRequiredParam<PostprocessorName>(
"postprocessor", "The postprocessor to use for controlling the specified parameter.");
params.addRequiredParam<Real>("target", "The target value for the postprocessor");
params.addParam<Real>("K_integral", 1, "The coefficient multiplying the integral term");
params.addParam<Real>("K_proportional", 1, "The coefficient multiplying the difference term");
params.addParam<Real>("K_derivative", 0.1, "The coefficient multiplying the derivative term");
params.addRequiredParam<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<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");

return params;
}

PIDControl::PIDControl(const InputParameters & parameters)
: Control(parameters),
_current(getPostprocessorValueByName(getParam<PostprocessorName>("postprocessor"))),
_target(getParam<Real>("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"))
{
_integral = 0;
}

void
PIDControl::execute()
{
if (_t >= _start_time && _t < _stop_time)
{
// Compute the new parameter based on the PID control of the postprocessor
Real value = getControllableValue<Real>("parameter");
_integral += (_current - _target) * _dt;
value += _Kint * _integral + _Kpro * (_current - _target);
if (_dt > 0)
value += _Kder * (_current - _target) / _dt;

// Set the new value using the Controllable system
setControllableValue<Real>("parameter", value);
}
}
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
80 changes: 80 additions & 0 deletions test/tests/controls/pid_control/pid_control.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
c = 0

[Mesh]
[square]
type = GeneratedMeshGenerator
nx = 2
ny = 2
dim = 2
[]
[]

[Variables]
active = 'u'

[u]
order = FIRST
family = LAGRANGE
[]
[]

[Kernels]
active = 'diff'

[diff]
type = Diffusion
variable = u
[]
[]

[BCs]
active = 'left right'

[./left]
type = DirichletBC
variable = u
boundary = 3
value = '${c}'
[../]

[./right]
type = DirichletBC
variable = u
boundary = 1
value = 1
[../]
[]

[Executioner]
type = Transient

solve_type = 'PJFNK'
start_time = 0.0
end_time = 20
dt = 1
[]

[Postprocessors]
[integral]
type = ElementIntegralVariablePostprocessor
variable = u
[]
[]

[Controls]
[integral_value]
type = PIDControl
postprocessor = integral
target = 1.5
parameter = 'BCs/left/value'
K_integral = -1
K_proportional = -1
K_derivative = -0.1
[]
[]

[Outputs]
file_base = out
exodus = false
csv = true
[]
10 changes: 10 additions & 0 deletions test/tests/controls/pid_control/tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Tests]
[pid]
type = 'CSVDiff'
input = 'pid_control.i'
csvdiff = 'pid_control_out.csv'
requirement = "The Control system shall be able to control a parameter to make a postprocessor match a target value, using the principle of Proportional Integral Derivative control."
issues = '#17271'
design = 'syntax/Controls/index.md source/controls/PIDControl.md'
[]
[]

0 comments on commit 2fd594c

Please sign in to comment.