https://mooseframework.inl.gov
Loading...
Searching...
No Matches
LibtorchNeuralNetControl.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#ifdef MOOSE_LIBTORCH_ENABLED
11
13#include "TorchScriptModule.h"
14#include "LibtorchUtils.h"
15
16#include "Transient.h"
17
19
22{
24 params.addClassDescription("Controls the value of multiple controllable input parameters using a "
25 "Libtorch-based neural network.");
26 params.addRequiredParam<std::vector<std::string>>(
27 "parameters", "Controllable input parameters driven by the network.");
28 params.addRequiredParam<std::vector<PostprocessorName>>(
29 "observations", "Postprocessors used as the current observation vector.");
30 params.addParam<std::vector<Real>>(
31 "observation_shift_factors",
32 {},
33 "Optional offsets applied to the observation values before scaling.");
34 params.addParam<std::vector<Real>>(
35 "observation_scaling_factors",
36 {},
37 "Optional multipliers applied after shifting the observation values.");
38 params.addParam<std::string>("filename", "File containing the controller network.");
39 params.addParam<bool>("torch_script_format",
40 false,
41 "Whether 'filename' is a TorchScript module to evaluate as-is. Otherwise, "
42 "a network is built from 'num_neurons_per_layer' and "
43 "'activation_function', and only its parameters are read from the file.");
44 params.addRangeCheckedParam<unsigned int>(
45 "input_timesteps",
46 1,
47 "1<=input_timesteps",
48 "Number of recent timesteps to stack into each network input.");
49 params.addParam<std::vector<unsigned int>>(
50 "num_neurons_per_layer", "Hidden-layer widths used when constructing the controller.");
51 params.addParam<std::vector<std::string>>(
52 "activation_function",
53 std::vector<std::string>({"relu"}),
54 "Activation name for each hidden layer, or one shared value for all layers.");
55
56 params.addParam<std::vector<Real>>(
57 "action_scaling_factors",
58 {},
59 "Per-action scaling embedded in the controller outputs so saved checkpoints stay in "
60 "physical units.");
61
62 return params;
63}
64
66 : Control(parameters),
67 _old_observations(declareRestartableData<std::vector<std::vector<Real>>>("old_observations")),
68 _control_names(getParam<std::vector<std::string>>("parameters")),
69 _current_control_signals(declareRestartableData<std::vector<Real>>(
70 "current_control_signals", std::vector<Real>(_control_names.size(), 0.0))),
71 _observation_names(getParam<std::vector<PostprocessorName>>("observations")),
72 _input_timesteps(getParam<unsigned int>("input_timesteps")),
73 _observation_shift_factors(isParamSetByUser("observation_shift_factors")
74 ? getParam<std::vector<Real>>("observation_shift_factors")
75 : std::vector<Real>(_observation_names.size(), 0.0)),
76 _observation_scaling_factors(isParamSetByUser("observation_scaling_factors")
77 ? getParam<std::vector<Real>>("observation_scaling_factors")
78 : std::vector<Real>(_observation_names.size(), 1.0)),
79 _action_scaling_factors(isParamSetByUser("action_scaling_factors")
80 ? getParam<std::vector<Real>>("action_scaling_factors")
81 : std::vector<Real>(_control_names.size(), 1.0)),
82 _observation_history(_input_timesteps)
83{
84 // We first check if the input parameters make sense and throw errors if different parameter
85 // combinations are not allowed
87 {"num_neurons_per_layer", "activation_function"},
88 !getParam<bool>("torch_script_format"));
89
91 paramError("observation_shift_factors",
92 "The number of shift factors is not the same as the number of observations!");
93
95 paramError("observation_scaling_factors",
96 "The number of normalization coefficients is not the same as the number of "
97 "observations!");
98
99 if (_control_names.size() != _action_scaling_factors.size())
100 paramError("action_scaling_factors",
101 "The number of normalization coefficients is not the same as the number of "
102 "controlled parameters!");
103
104 // We link to the postprocessor values so that we can fetch them any time. This also raises
105 // errors if we don't have the postprocessors requested in the input.
106 for (unsigned int obs_i = 0; obs_i < _observation_names.size(); ++obs_i)
108}
109
110void
112{
113 // Defer file loading until derived construction is complete so virtual dispatch invokes the
114 // appropriate loader and constructs the correct network type directly.
115 if (isParamSetByUser("filename"))
117}
118
119void
121{
122 const auto & filename = getParam<std::string>("filename");
123 if (getParam<bool>("torch_script_format"))
124 _nn = std::make_shared<Moose::TorchScriptModule>(filename);
125 else
126 {
127 unsigned int num_inputs = _observation_names.size() * _input_timesteps;
128 unsigned int num_outputs = _control_names.size();
129 std::vector<unsigned int> num_neurons_per_layer =
130 getParam<std::vector<unsigned int>>("num_neurons_per_layer");
131 std::vector<std::string> activation_functions =
132 isParamSetByUser("activation_function")
133 ? getParam<std::vector<std::string>>("activation_function")
134 : std::vector<std::string>({"relu"});
135 const auto input_shift_factors =
137 const auto input_scaling_factors =
139 auto nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(filename,
140 num_inputs,
141 num_outputs,
142 num_neurons_per_layer,
143 activation_functions,
144 torch::kCPU,
145 torch::kDouble,
146 true,
147 input_shift_factors,
148 input_scaling_factors,
150
151 try
152 {
154 _nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(*nn);
155 }
156 catch (const c10::Error & e)
157 {
159 "The requested pytorch parameter file could not be loaded. This can either be the"
160 "result of the file not existing or a misalignment in the generated container and"
161 "the data in the file. Make sure the dimensions of the generated neural net are the"
162 "same as the dimensions of the parameters in the input file!\n",
163 e.msg());
164 }
165 }
166}
167
168void
170{
171 if (_nn)
172 {
173 const unsigned int n_controls = _control_names.size();
174
175 // Fetch current observation values from the linked postprocessors.
177
178 // If this is the first timestep, we fill up the old values with the initial value
179 if (_old_observations.empty())
181
182 // Organize the old an current solution into a tensor so we can evaluate the neural net
183 torch::Tensor input_tensor = prepareInputTensor();
184
185 // Evaluate the neural network to get the control values then convert it back to vectors
186 torch::Tensor action = _nn->forward(input_tensor);
187
188 _current_control_signals = {action.data_ptr<Real>(), action.data_ptr<Real>() + action.size(1)};
189 const bool is_torch_script =
190 dynamic_cast<const Moose::TorchScriptModule *>(_nn.get()) != nullptr;
191 for (unsigned int control_i = 0; control_i < n_controls; ++control_i)
192 {
193 // Plain networks apply their serialized output scaling in forward().
194 if (is_torch_script)
195 _current_control_signals[control_i] *= _action_scaling_factors[control_i];
196
197 setControllableValueByName<Real>(_control_names[control_i],
198 _current_control_signals[control_i]);
199 }
200
201 // We add the curent solution to the old solutions and move everything in there one step
202 // backward
203 if (_old_observations.size())
205 }
206}
207
208Real
209LibtorchNeuralNetControl::getSignal(const unsigned int signal_index) const
210{
211 mooseAssert(signal_index < _control_names.size(),
212 "The index of the requested control signal is not in the [0," +
213 std::to_string(_control_names.size()) + ") range!");
214 return _current_control_signals[signal_index];
215}
216
217void
219 const std::string & param_name,
220 const std::vector<std::string> & conditional_params,
221 bool should_be_defined)
222{
223 if (parameters().isParamSetByUser(param_name))
224 for (const auto & param : conditional_params)
225 if (parameters().isParamSetByUser(param) != should_be_defined)
226 paramError(param,
227 "This parameter should",
228 (should_be_defined ? " " : " not "),
229 "be defined when ",
230 param_name,
231 " is defined!");
232}
233
234void
236{
238 for (const auto obs_i : index_range(_observation_names))
240}
241
242void
244{
245 _nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(input_nn);
246}
247
248torch::Tensor
250{
251 auto raw_input =
253 if (dynamic_cast<const Moose::TorchScriptModule *>(_nn.get()))
254 {
255 const auto input_shift_factors =
257 const auto input_scaling_factors =
259
260 // Plain networks apply their serialized input transformation in forward().
261 for (const auto input_i : index_range(raw_input))
262 raw_input[input_i] =
263 (raw_input[input_i] - input_shift_factors[input_i]) * input_scaling_factors[input_i];
264 }
265
266 torch::Tensor input_tensor;
267 LibtorchUtils::vectorToTensor(raw_input, input_tensor);
268
269 return input_tensor.transpose(0, 1);
270}
271
274{
275 if (!hasControlNeuralNet())
276 mooseError("The neural network in the controller must exist!");
277 return *_nn;
278}
279
280#endif
registerMooseObject("MooseApp", LibtorchNeuralNetControl)
void ErrorVector unsigned int
Base class for Control objects.
Definition Control.h:44
static InputParameters validParams()
Class constructor.
Definition Control.C:16
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
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 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...
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
A time-dependent, neural network-based control of multiple input parameters.
const std::vector< Real > _action_scaling_factors
Multipliers for the actions.
virtual void execute() override
Execute the neural network to determine the controllable parameter values.
void conditionalParameterError(const std::string &param_name, const std::vector< std::string > &conditional_param, bool should_be_defined=true)
Function responsible for checking for potential user errors in the input file.
bool hasControlNeuralNet() const
Return true if the object already has a neural network.
virtual void initialSetup() override
Load any file-backed controller state after full object construction.
const unsigned int _input_timesteps
Number of timesteps to use as input data from the reporters (this influences how many past results ar...
virtual void loadControlNeuralNet(const Moose::LibtorchArtificialNeuralNet &input_nn)
Function responsible for loading the neural network for the controller.
Real getSignal(const unsigned int signal_index) const
Get the (signal_index)-th signal of the control neural net.
const std::vector< PostprocessorName > & _observation_names
Names of the postprocessors which contain the observations of the system.
const std::vector< Real > _observation_shift_factors
Shifting constants for the observations.
static InputParameters validParams()
Return the input parameters supported by this control.
const std::vector< Real > _observation_scaling_factors
Scaling constants (multipliers) for the observations.
LibtorchNeuralNetControl(const InputParameters &parameters)
Construct using input parameters.
std::vector< Real > _current_observation
The values of the current observed postprocessor values.
std::vector< Real > & _current_control_signals
The control signals from the last evaluation of the controller, saved for recover/restart.
void updateCurrentObservation()
Refresh the current observation values from the linked postprocessors.
virtual void loadControlNeuralNetFromFile()
Load the controller neural network from the configured checkpoint file.
const std::vector< std::string > & _control_names
The names of the controllable parameters.
torch::Tensor prepareInputTensor()
Prepare the input tensor for the controller neural network.
std::shared_ptr< Moose::LibtorchNeuralNetBase > _nn
Pointer to the neural net object which is supposed to be used to control the parameter values.
const LibtorchObservationHistoryHelper _observation_history
Shared observation history stacking and factor-expansion helper.
std::vector< std::vector< Real > > & _old_observations
This variable is populated if the controller needs access to older values of the observed postprocess...
std::vector< const Real * > _observation_values
Links to the current observation postprocessor values.
const Moose::LibtorchNeuralNetBase & controlNeuralNet() const
Return a reference to the stored neural network.
void initializeHistory(const std::vector< Real > &observation, std::vector< std::vector< Real > > &old_observations) const
Fill the history buffer with copies of the current observation.
std::vector< Real > expandObservationFactors(const std::vector< Real > &observation_factors) const
Repeat per-observation-entry factors across all stacked timesteps.
void advanceHistory(const std::vector< Real > &observation, std::vector< std::vector< Real > > &old_observations) const
Advance the history buffer by inserting the latest observation.
std::vector< Real > stackCurrentObservation(const std::vector< Real > &observation, const std::vector< std::vector< Real > > &old_observations) const
Flatten the current observation together with its stored history.
const InputParameters & parameters() const
Get the parameters of the object.
Definition MooseBase.h:131
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:457
bool isParamSetByUser(const std::string &name) const
Test if the supplied parameter is set by a user, as opposed to not set or set to default.
Definition MooseBase.h:205
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:271
Simple feed-forward neural net with optional affine input and output scaling.
This base class is meant to gather the functions and members common in every neural network based on ...
virtual const PostprocessorValue & getPostprocessorValueByName(const PostprocessorName &name) const
Retrieve the value of the Postprocessor.
void vectorToTensor(const std::vector< DataType > &vector, torch::Tensor &tensor, const bool detach=false)
Utility function that converts a standard vector to a torch::Tensor.
void loadLibtorchArtificialNeuralNetState(Moose::LibtorchArtificialNeuralNet &nn, const std::string &filename)
Load an artificial-neural-network checkpoint written as a native libtorch state archive.