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>>("parameters",
27 "The input parameter(s) to control.");
28 params.addRequiredParam<std::vector<PostprocessorName>>(
29 "responses", "The responses (prostprocessors) which are used for the control.");
30 params.addParam<std::vector<Real>>(
31 "response_shift_factors",
32 "Constants which will be used to shift the response values. This is used for the "
33 "manipulation of the neural net inputs for better training efficiency.");
34 params.addParam<std::vector<Real>>(
35 "response_scaling_factors",
36 "Constants which will be used to multiply the shifted response values. This is used for "
37 "the manipulation of the neural net inputs for better training efficiency.");
38 params.addParam<std::string>("filename",
39 "Define if the neural net is supposed to be loaded from a file.");
40 params.addParam<bool>("torch_script_format",
41 false,
42 "If we want to load the neural net using the torch-script format.");
43 params.addParam<unsigned int>(
44 "input_timesteps",
45 1,
46 "Number of time steps to use in the input data, if larger than 1, "
47 "data from the previous timesteps will be used as well as inputs in the training.");
48 params.addParam<std::vector<unsigned int>>("num_neurons_per_layer",
49 "The number of neurons on each hidden layer.");
50 params.addParam<std::vector<std::string>>(
51 "activation_function",
52 std::vector<std::string>({"relu"}),
53 "The type of activation functions to use. It is either one value "
54 "or one value per hidden layer.");
55
56 params.addParam<std::vector<Real>>(
57 "action_scaling_factors",
58 "Scale factor that multiplies the NN output to obtain a physically meaningful value.");
59
60 return params;
61}
62
64 : Control(parameters),
65 _old_responses(declareRestartableData<std::vector<std::vector<Real>>>("old_responses")),
66 _control_names(getParam<std::vector<std::string>>("parameters")),
67 _current_control_signals(std::vector<Real>(_control_names.size(), 0.0)),
68 _response_names(getParam<std::vector<PostprocessorName>>("responses")),
69 _input_timesteps(getParam<unsigned int>("input_timesteps")),
70 _response_shift_factors(isParamValid("response_shift_factors")
71 ? getParam<std::vector<Real>>("response_shift_factors")
72 : std::vector<Real>(_response_names.size(), 0.0)),
73 _response_scaling_factors(isParamValid("response_scaling_factors")
74 ? getParam<std::vector<Real>>("response_scaling_factors")
75 : std::vector<Real>(_response_names.size(), 1.0)),
76 _action_scaling_factors(isParamValid("action_scaling_factors")
77 ? getParam<std::vector<Real>>("action_scaling_factors")
78 : std::vector<Real>(_control_names.size(), 1.0))
79{
80 // We first check if the input parameters make sense and throw errors if different parameter
81 // combinations are not allowed
83 {"num_neurons_per_layer", "activation_function"},
84 !getParam<bool>("torch_script_format"));
85
86 if (_response_names.size() != _response_shift_factors.size())
87 paramError("response_shift_factors",
88 "The number of shift factors is not the same as the number of responses!");
89
90 if (_response_names.size() != _response_scaling_factors.size())
92 "response_scaling_factors",
93 "The number of normalization coefficients is not the same as the number of responses!");
94
95 if (_control_names.size() != _action_scaling_factors.size())
96 paramError("action_scaling_factors",
97 "The number of normalization coefficients is not the same as the number of "
98 "controlled parameters!");
99
100 // We link to the postprocessor values so that we can fetch them any time. This also raises
101 // errors if we don't have the postprocessors requested in the input.
102 for (unsigned int resp_i = 0; resp_i < _response_names.size(); ++resp_i)
104
105 // If the user wants to read the neural net from file, we do it. We can read it from a
106 // torchscript file, or we can create a shell and read back the parameters
107 if (isParamValid("filename"))
108 {
109 std::string filename = getParam<std::string>("filename");
110 if (getParam<bool>("torch_script_format"))
111 _nn = std::make_shared<Moose::TorchScriptModule>(filename);
112 else
113 {
114 unsigned int num_inputs = _response_names.size() * _input_timesteps;
115 unsigned int num_outputs = _control_names.size();
116 std::vector<unsigned int> num_neurons_per_layer =
117 getParam<std::vector<unsigned int>>("num_neurons_per_layer");
118 std::vector<std::string> activation_functions =
119 parameters.isParamSetByUser("activation_function")
120 ? getParam<std::vector<std::string>>("activation_function")
121 : std::vector<std::string>({"relu"});
122 auto nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(
123 filename, num_inputs, num_outputs, num_neurons_per_layer, activation_functions);
124
125 try
126 {
127 torch::load(nn, filename);
128 _nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(*nn);
129 }
130 catch (const c10::Error & e)
131 {
133 "The requested pytorch parameter file could not be loaded. This can either be the"
134 "result of the file not existing or a misalignment in the generated container and"
135 "the data in the file. Make sure the dimensions of the generated neural net are the"
136 "same as the dimensions of the parameters in the input file!\n",
137 e.msg());
138 }
139 }
140 }
141}
142
143void
145{
146 if (_nn)
147 {
148 const unsigned int n_controls = _control_names.size();
149 const unsigned int num_old_timesteps = _input_timesteps - 1;
150
151 // Fetch current reporter values and populate _current_response
153
154 // If this is the first timestep, we fill up the old values with the initial value
155 if (_old_responses.empty())
156 _old_responses.assign(num_old_timesteps, _current_response);
157
158 // Organize the old an current solution into a tensor so we can evaluate the neural net
159 torch::Tensor input_tensor = prepareInputTensor();
160
161 // Evaluate the neural network to get the control values then convert it back to vectors
162 torch::Tensor action = _nn->forward(input_tensor);
163
164 _current_control_signals = {action.data_ptr<Real>(), action.data_ptr<Real>() + action.size(1)};
165 for (unsigned int control_i = 0; control_i < n_controls; ++control_i)
166 {
167 // We scale the controllable value for physically meaningful control action
168 setControllableValueByName<Real>(_control_names[control_i],
169 _current_control_signals[control_i] *
170 _action_scaling_factors[control_i]);
171 }
172
173 // We add the curent solution to the old solutions and move everything in there one step
174 // backward
175 std::rotate(_old_responses.rbegin(), _old_responses.rbegin() + 1, _old_responses.rend());
177 }
178}
179
180Real
181LibtorchNeuralNetControl::getSignal(const unsigned int signal_index) const
182{
183 mooseAssert(signal_index < _control_names.size(),
184 "The index of the requested control signal is not in the [0," +
185 std::to_string(_control_names.size()) + ") range!");
186 return _current_control_signals[signal_index];
187}
188
189void
191 const std::string & param_name,
192 const std::vector<std::string> & conditional_params,
193 bool should_be_defined)
194{
195 if (parameters().isParamSetByUser(param_name))
196 for (const auto & param : conditional_params)
197 if (parameters().isParamSetByUser(param) != should_be_defined)
198 paramError(param,
199 "This parameter should",
200 (should_be_defined ? " " : " not "),
201 "be defined when ",
202 param_name,
203 " is defined!");
204}
205
206void
208{
209 // Gather the current response values from the reporters
210 _current_response.clear();
211
212 for (const auto & resp_i : index_range(_response_names))
213 _current_response.push_back((*_response_values[resp_i] - _response_shift_factors[resp_i]) *
215}
216
217void
219{
220 _nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(input_nn);
221}
222
223torch::Tensor
225{
226 const unsigned int num_old_timesteps = _input_timesteps - 1;
227
228 // We convert the standard vectors to libtorch tensors
229 std::vector<Real> raw_input(_current_response);
230
231 for (const auto & step_i : make_range(num_old_timesteps))
232 raw_input.insert(raw_input.end(), _old_responses[step_i].begin(), _old_responses[step_i].end());
233
234 torch::Tensor input_tensor;
235 LibtorchUtils::vectorToTensor(raw_input, input_tensor);
236
237 return input_tensor.transpose(0, 1);
238}
239
242{
243 if (!hasControlNeuralNet())
244 mooseError("The neural network in the controller must exist!");
245 return *_nn;
246}
247
248#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.
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 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 netwok.
const unsigned int _input_timesteps
Number of timesteps to use as input data from the reporters (this influences how many past results ar...
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< Real > _response_scaling_factors
Scaling constants (multipliers) for the responses.
std::vector< Real > _current_response
The values of the current observed postprocessor values.
const std::vector< PostprocessorName > & _response_names
Names of the postprocessors which contain the observations of the system.
std::vector< std::vector< Real > > & _old_responses
This variable is populated if the controller needs acess to older values of the observed postprocesso...
static InputParameters validParams()
std::vector< Real > _current_control_signals
The control signals from the last evaluation of the controller.
LibtorchNeuralNetControl(const InputParameters &parameters)
Construct using input parameters.
const std::vector< Real > _response_shift_factors
Shifting constants for the responses.
const std::vector< std::string > & _control_names
The names of the controllable parameters.
torch::Tensor prepareInputTensor()
Function that prepares the input tensor for the controller neural network.
std::vector< const Real * > _response_values
Links to the current response postprocessor values.
std::shared_ptr< Moose::LibtorchNeuralNetBase > _nn
Pointer to the neural net object which is supposed to be used to control the parameter values.
void updateCurrentResponse()
Function that updates the values of the current response.
const Moose::LibtorchNeuralNetBase & controlNeuralNet() const
Return a reference to the stored neural network.
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
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
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.