Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added several chain controls #29161

Open
wants to merge 14 commits into
base: next
Choose a base branch
from
12 changes: 12 additions & 0 deletions framework/doc/content/source/chaincontrols/LimitChainControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# LimitChainControl

This [ChainControl](syntax/ChainControls/index.md) allows the user to apply
a minimum and maximum value to a [chain control value](/ChainControlData.md).
The resulting value is named `<control>:value`, where `<control>` is the
user-given name of the control.
joshuahansel marked this conversation as resolved.
Show resolved Hide resolved

!syntax parameters /ChainControls/LimitChainControl

!syntax inputs /ChainControls/LimitChainControl

!syntax children /ChainControls/LimitChainControl
64 changes: 64 additions & 0 deletions framework/doc/content/source/chaincontrols/PIDChainControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# PIDChainControl

This [ChainControl](syntax/ChainControls/index.md) implements the classic PID controller, which takes as its input
a value $x$ and the set point for that value, $\bar{x}$. It produces an output
signal $y$, which should be used as the input for some controllable device that
impacts the measured quantity $x$. For example, in a thermal system, $x$ may
be the temperature of a fluid at some location, and $y$ may be the power sent
to the heaters in the system, with the goal to heat the fluid such that the
temperature at some location is $\bar{x}$.

"PID" stands for its three components:

- P: Proportional
- I: Integral
- D: Derivative

The output signal $y$ at time $t_n$ is computed as follows:

!equation
y_n = K_p e_n + K_i \sum\limits_{m=1}^n e_m \Delta t_m + K_d \frac{e_n - e_{n-1}}{\Delta t_n} \,,

where $\Delta t_n \equiv t_n - t_{n-1}$ is the time step size and
$e_n$ is the error:

!equation
e_n \equiv \bar{x}_n - x_n \,.

!alert warning title=Execute only once per time step
The implementation assumes that the control will only be executed once per
time step, so you should set [!param](/ChainControls/PIDChainControl/execute_on) accordingly,
such as `execute_on = 'INITIAL TIMESTEP_END'`, which is the default.

The inputs and outputs are retrieved and named as follows, respectively:

- $x$ is set with [!param](/ChainControls/PIDChainControl/input).
- $\bar{x}$ is set with [!param](/ChainControls/PIDChainControl/set_point).
- $y$ is declared with the name `<control_name>:value`, where `<control_name>`
is the user-given name of the control object.

!alert tip title=Tuning PID coefficients
If you are unsure on how to select the PID coefficients $K_p$, $K_i$, and $K_d$,
you may try the following strategy, which involves some trial and error. First,
set $K_i$ and $K_d$ to zero. Then, set $K_p$ to some arbitrary (positive) value.
Run a simulation with $x_0 \neq \bar{x}$ and examine the transient response of $x$
to the set point value $\bar{x}$. The goal should be to maximize $K_p$ without
causing an unstable response, where $x$ oscillates indefinitely around $\bar{x}$.
Some initial overshoot and subsequent diminishing oscillations are acceptable.
After this acceptable value of $K_p$ is found, you'll likely find that $x$ appears
to be nearly constant in time, but at the wrong value. At this point, you should
start increasing $K_i$ from zero until you get an acceptable response time to
get the initial response (largely driven by $K_p$) to bridge the gap to $\bar{x}$,
without introducing oscillatory behavior. Lastly, $K_d$ can be used to fine-tune
the response, but it is not necessary, and it risks some oscillatory behavior
if the inputs have any noise, so it should only be used for relatively smooth
inputs.

!alert note
To control a controllable value directly instead of using a [ChainControlData.md], use [PIDTransientControl.md].

!syntax parameters /ChainControls/PIDChainControl

!syntax inputs /ChainControls/PIDChainControl

!syntax children /ChainControls/PIDChainControl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# RealToBoolChainControl

This [ChainControl](syntax/ChainControls/index.md) converts a `Real`-valued
[chain control data](/ChainControlData.md) to a `bool` data, with the name
`<control>:value`, where `<control>` is the user-given name of the control:

- A value of 1 converts to `true`.
- A value of 0 converts to `false`.
- Other values result in an error.

joshuahansel marked this conversation as resolved.
Show resolved Hide resolved
!syntax parameters /ChainControls/RealToBoolChainControl

!syntax inputs /ChainControls/RealToBoolChainControl

!syntax children /ChainControls/RealToBoolChainControl
35 changes: 35 additions & 0 deletions framework/doc/content/source/chaincontrols/ScaleOldChainControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ScaleOldChainControl

This [ChainControl](syntax/ChainControls/index.md) allows the user to scale
the previous time value for a [chain control data](/ChainControlData.md)
by another chain control data.
The resulting value is named `<control>:value`, where `<control>` is the
user-given name of the control.
joshuahansel marked this conversation as resolved.
Show resolved Hide resolved
The chain control data to scale may be the
same chain control data created by this `ChainControl`, or it may be another.

This `ChainControl` is useful for applying corrections to simulation quantities.
For example, suppose the objective is to tune a heat transfer coefficient such
that the resulting cooling power, which we calculate using a [Postprocessor](Postprocessors/index.md) `simulated_power`,
matches an experimentally measured power, given in a `Postprocessor` `experiment_power`.
One provides an initial guess for the heat transfer coefficient [!param](/ChainControls/ScaleOldChainControl/initial_value).
The heat transfer coefficient in the boundary condition is controlled using a
[SetValueChainControl.md]. The `Postprocessor` `simulated_power` is computed
using a numerical integral over the boundary, and then a [GetPostprocessorChainControl.md]
is used to copy each of `simulated_power` and `experiment_power`. A
[ParsedChainControl.md] is used to compute a scaling factor as `experiment_power / simulated_power`.
This value is then used in [!param](/ChainControls/ScaleOldChainControl/scale_factor).
To keep the scaled value in a physical range (for instance, the heat transfer
coefficient should be a positive value), one may use a [LimitChainControl.md].
This is important, for example, because often there is noise in experimental data,
and the response time of the controlled quantity may vary. This limited
heat transfer coefficient chain control data is the one that should be used with
the `SetValueChainControl`, so it should also be the one used in
[!param](/ChainControls/ScaleOldChainControl/control_data). If there were no
limitation step, then this parameter would be set to `<control>:value`.

!syntax parameters /ChainControls/ScaleOldChainControl

!syntax inputs /ChainControls/ScaleOldChainControl

!syntax children /ChainControls/ScaleOldChainControl
20 changes: 20 additions & 0 deletions framework/doc/content/source/chaincontrols/SmootherChainControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SmootherChainControl

This [ChainControl](syntax/ChainControls/index.md) smooths an input signal using a moving average.
The $n$ most recent values are used to compute the average $\bar{y}$ of the input
values $y$:

!equation
\bar{y}_i = \frac{1}{n} \Sigma\limits_{k=i-n+1}^i y_i

where $i$ represents the time index.

The resulting value is named `<control>:value`, where `<control>` is the
user-given name of the control.
The number of points to average, $n$, is provided with [!param](/ChainControls/SmootherChainControl/n_points).

!syntax parameters /ChainControls/SmootherChainControl

!syntax inputs /ChainControls/SmootherChainControl

!syntax children /ChainControls/SmootherChainControl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# TerminateChainControl

This [ChainControl](syntax/ChainControls/index.md) terminates the simulation
when a boolean [chain control data](/ChainControlData.md) has a given value.
The simulation may be terminated either by throwing an error or by signalling
to the [problem](/FEProblemBase.md) to terminate. This behavior is controlled
with the parameter [!param](/ChainControls/TerminateChainControl/throw_error).

!alert note title=The Terminator user object
An alternative way to terminate a simulation is to use a [Terminator.md] user object,
but `Terminator` works with [Postprocessors](Postprocessors/index.md) for the criterion
instead of a boolean chain control data.

!alert tip title=Converting to boolean control data
This object requires the input data to have type `bool`. If you have a data of
type `Real`, you can convert using [RealToBoolChainControl.md].

!syntax parameters /ChainControls/TerminateChainControl

!syntax inputs /ChainControls/TerminateChainControl

!syntax children /ChainControls/TerminateChainControl
22 changes: 22 additions & 0 deletions framework/doc/content/source/chaincontrols/UnitTripChainControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# UnitTripChainControl

This [ChainControl](syntax/ChainControls/index.md) produces a boolean trip
[chain control data](/ChainControlData.md), which is initially `false` but
upon being triggered, becomes `true` for the remainder of the simulation.
This trip value is named `<control>:tripped`, where `<control>` is the
user-given name of the control.
The trip is determined by the value of the input boolean value given by
[!param](/ChainControls/UnitTripChainControl/input) and
[!param](/ChainControls/UnitTripChainControl/trip_on_true). If `trip_on_true`
is set to `true`, then the trip occurs when the input value is `true`; else
it occurs when the input value is `false`.

!alert tip title=Converting to boolean control data
This object requires the input data to have type `bool`. If you have a data of
type `Real`, you can convert using [RealToBoolChainControl.md].

!syntax parameters /ChainControls/UnitTripChainControl

!syntax inputs /ChainControls/UnitTripChainControl

!syntax children /ChainControls/UnitTripChainControl
5 changes: 5 additions & 0 deletions framework/include/base/ChainControlDataSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ class ChainControlDataSystem
ChainControlData<T> & declareChainControlData(const std::string & data_name,
ChainControl & chain_control);

/**
* Copies current chain control data values into old values
*/
void copyValuesBack();

/**
* Gets the map of ChainControlData names to the relevant ChainControlDataBase
*/
Expand Down
25 changes: 23 additions & 2 deletions framework/include/chaincontrols/ChainControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Control.h"
#include "ChainControlData.h"
#include "ChainControlDataSystem.h"
#include "MooseUtils.h"

/**
* Control that additionally provides the capability to produce/consume data values,
Expand Down Expand Up @@ -97,6 +98,15 @@ class ChainControl : public Control
*/
void addChainControlDataDependency(const std::string & data_name);

/**
* Gets the full control data name, including object name prefix (if any)
*
* @param data_name Chain control data name
* @param apply_object_prefix If true, apply the object name as a prefix to the data name
*/
std::string fullControlDataName(const std::string & data_name,
bool apply_object_prefix = true) const;

/// List of chain control data that this control depends upon
std::vector<std::string> _control_data_depends_on;
};
Expand All @@ -105,7 +115,7 @@ template <typename T>
T &
ChainControl::declareChainControlData(const std::string & data_name, bool apply_object_prefix)
{
const std::string full_data_name = (apply_object_prefix ? name() + ":" : "") + data_name;
const std::string full_data_name = fullControlDataName(data_name, apply_object_prefix);
auto & data =
getMooseApp().getChainControlDataSystem().declareChainControlData<T>(full_data_name, *this);
return data.set();
Expand All @@ -129,7 +139,18 @@ template <typename T>
const T &
ChainControl::getChainControlDataByName(const std::string & data_name)
{
auto & data = getMooseApp().getChainControlDataSystem().getChainControlData<T>(data_name);
auto & system = getMooseApp().getChainControlDataSystem();

if (system.hasChainControlData(data_name) && !system.hasChainControlDataOfType<T>(data_name))
mooseError("The chain control data '",
data_name,
"' has the type '",
system.getChainControlDataMap().at(data_name)->type(),
"', but this chain control requires its type to be '",
MooseUtils::prettyCppType<T>(),
"'.");

auto & data = system.getChainControlData<T>(data_name);

addChainControlDataDependency(data_name);

Expand Down
3 changes: 2 additions & 1 deletion framework/include/chaincontrols/ChainControlData.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma once

#include "Restartable.h"
#include "MooseUtils.h"

class ChainControl;

Expand Down Expand Up @@ -131,7 +132,7 @@ template <typename T>
inline std::string
ChainControlData<T>::type()
{
return typeid(T).name();
return MooseUtils::prettyCppType<T>();
}

template <typename T>
Expand Down
35 changes: 35 additions & 0 deletions framework/include/chaincontrols/LimitChainControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//* 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

#include "ChainControl.h"

/**
* Limits a control value by a range.
*/
class LimitChainControl : public ChainControl
{
public:
static InputParameters validParams();

LimitChainControl(const InputParameters & parameters);

virtual void execute() override;

protected:
/// Minimum value to apply to control data
const Real _min_value;
Copy link
Contributor

@GiudGiud GiudGiud Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to have these be references to Reals from other ChainControls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would, but I'd prefer that the user not be required to add chain controls for the bounds if they just want constant values. To get the best of both approaches, I think we'd need to do some work to allow constants to be able to be supplied for control data parameters, and I'd prefer to do that work when it becomes needed.

/// Maximum value to apply to control data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Maximum value to apply to control data
/// Maximum value to restrict control data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed wording to "Lower/upper bound to apply to"

const Real _max_value;
/// Control value before limiting
const Real & _unlimited_value;
/// Control value after limiting
Real & _limited_value;
};
54 changes: 54 additions & 0 deletions framework/include/chaincontrols/PIDChainControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//* 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

#include "ChainControl.h"

/**
* Implements a proportional-integral-derivative (PID) controller.
*/
class PIDChainControl : public ChainControl
{
public:
static InputParameters validParams();

PIDChainControl(const InputParameters & parameters);

virtual void execute() override;

protected:
/// input data
const Real & _input;
/// set point
const Real & _set_point;
/// The coefficient for the proportional term
const Real & _K_p;
/// The coefficient for the integral term
const Real & _K_i;
/// The coefficient for the derivative term
const Real & _K_d;

/// The current value of the error
Real & _error;
/// The old value of the error
const Real & _error_old;

/// The proportional component
Real & _proportional;
/// The integral component
Real & _integral;
/// The old value of \c _integral
const Real & _integral_old;
/// The derivative component
Real & _derivative;

/// The output computed by the PID controller
Real & _output;
};
Loading