NEML2 1.4.0
Loading...
Searching...
No Matches
TransientDriver.cxx
1// Copyright 2023, UChicago Argonne, LLC
2// All Rights Reserved
3// Software Name: NEML2 -- the New Engineering material Model Library, version 2
4// By: Argonne National Laboratory
5// OPEN SOURCE LICENSE (MIT)
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in
15// all copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23// THE SOFTWARE.
24
25#include "neml2/drivers/TransientDriver.h"
26#include "neml2/models/ComposedModel.h"
27#include "neml2/models/ImplicitUpdate.h"
28
29namespace fs = std::filesystem;
30
31namespace neml2
32{
33OptionSet
35{
37
38 options.set<std::string>("model");
39 options.set("model").doc() = "The material model to be updated by the driver";
40
41 options.set<CrossRef<torch::Tensor>>("times");
42 options.set("times").doc() =
43 "Time steps to perform the material update. The times tensor must have exactly 2 dimensions. "
44 "The first dimension represents time steps, and the second dimension represents batches "
45 "(i.e., how many material models to update simultaneously).";
46
47 options.set<VariableName>("time") = VariableName("forces", "t");
48 options.set("time").doc() = "Time";
49
50 options.set<std::string>("predictor") = "PREVIOUS_STATE";
51 options.set("predictor").doc() =
52 "Predictor used to set the initial guess for each time step. Options are PREVIOUS_STATE, "
53 "LINEAR_EXTRAPOLATION, CP_PREVIOUS_STATE, and CP_LINEAR_EXTRAPOLATION. The options prefixed "
54 "with 'CP_' are specifically designed for crystal plasticity models.";
55
56 options.set<Real>("cp_elastic_scale") = 1.0;
57 options.set("cp_elastic_scale").doc() = "Elastic step scale factor used in the 'CP_' predictors";
58
59 options.set<std::string>("save_as");
60 options.set("save_as").doc() =
61 "File path (absolute or relative to the working directory) to store the results";
62
63 options.set<bool>("show_parameters") = false;
64 options.set("show_parameters").doc() = "Whether to show model parameters at the beginning";
65
66 options.set<bool>("show_input_axis") = false;
67 options.set("show_input_axis").doc() = "Whether to show model input axis at the beginning";
68
69 options.set<bool>("show_output_axis") = false;
70 options.set("show_output_axis").doc() = "Whether to show model output axis at the beginning";
71
72 options.set<std::string>("device") = "cpu";
73 options.set("device").doc() =
74 "Device on which to evaluate the material model. The string supplied must follow the "
75 "following schema: (cpu|cuda)[:<device-index>] where cpu or cuda specifies the device type, "
76 "and :<device-index> optionally specifies a device index. For example, device='cpu' sets the "
77 "target compute device to be CPU, and device='cuda:1' sets the target compute device to be "
78 "CUDA with device ID 1.";
79
80 options.set<std::vector<VariableName>>("ic_scalar_names");
81 options.set("ic_scalar_names").doc() = "Apply initial conditions to these Scalar variables";
82
83 options.set<std::vector<CrossRef<Scalar>>>("ic_scalar_values");
84 options.set("ic_scalar_values").doc() = "Initial condition values for the Scalar variables";
85
86 options.set<std::vector<VariableName>>("ic_rot_names");
87 options.set("ic_rot_names").doc() = "Apply initial conditions to these Rot variables";
88
89 options.set<std::vector<CrossRef<Rot>>>("ic_rot_values");
90 options.set("ic_rot_values").doc() = "Initial condition values for the Rot variables";
91
92 options.set<std::vector<VariableName>>("ic_sr2_names");
93 options.set("ic_sr2_names").doc() = "Apply initial conditions to these SR2 variables";
94
95 options.set<std::vector<CrossRef<SR2>>>("ic_sr2_values");
96 options.set("ic_sr2_values").doc() = "Initial condition values for the SR2 variables";
97
98 return options;
99}
100
102 : Driver(options),
103 _model(Factory::get_object<Model>("Models", options.get<std::string>("model"))),
104 _device(options.get<std::string>("device")),
105 _time(options.get<CrossRef<torch::Tensor>>("times"), 2),
106 _step_count(0),
107 _time_name(options.get<VariableName>("time")),
108 _nsteps(_time.batch_sizes()[0]),
109 _nbatch(_time.batch_sizes()[1]),
110 _in(_model.input_storage()),
111 _out(_model.output_storage()),
112 _predictor(options.get<std::string>("predictor")),
113 _save_as(options.get<std::string>("save_as")),
114 _show_params(options.get<bool>("show_parameters")),
115 _show_input(options.get<bool>("show_input_axis")),
116 _show_output(options.get<bool>("show_output_axis")),
117 _result_in(LabeledVector::zeros({_nsteps, _nbatch}, {&_model.input_axis()})),
118 _result_out(LabeledVector::zeros({_nsteps, _nbatch}, {&_model.output_axis()})),
119 _ic_scalar_names(options.get<std::vector<VariableName>>("ic_scalar_names")),
120 _ic_scalar_values(options.get<std::vector<CrossRef<Scalar>>>("ic_scalar_values")),
121 _ic_rot_names(options.get<std::vector<VariableName>>("ic_rot_names")),
122 _ic_rot_values(options.get<std::vector<CrossRef<Rot>>>("ic_rot_values")),
123 _ic_sr2_names(options.get<std::vector<VariableName>>("ic_sr2_names")),
124 _ic_sr2_values(options.get<std::vector<CrossRef<SR2>>>("ic_sr2_values")),
125 _cp_elastic_scale(options.get<Real>("cp_elastic_scale"))
126{
127 _model.reinit({_nbatch}, 0, _device);
128
129 _time = _time.to(_device);
130 _result_in = _result_in.to(_device);
131 _result_out = _result_out.to(_device);
132}
133
134void
136{
138
139 auto errors = _model.preflight();
140 // For now, let's throw one error at a time...
141 if (!errors.empty())
142 throw errors[0];
143
144 neml_assert(_time.dim() == 2,
145 "Input time should have dimension 2 but instead has dimension ",
146 _time.dim());
147}
148
149bool
151{
152 // LCOV_EXCL_START
153 if (_show_params)
154 for (auto && [pname, pval] : _model.named_parameters())
155 std::cout << pname << std::endl;
156
157 if (_show_input)
158 std::cout << _model.name() << "'s input axis:\n" << _model.input_axis() << std::endl;
159
160 if (_show_output)
161 std::cout << _model.name() << "'s output axis:\n" << _model.output_axis() << std::endl;
162 // LCOV_EXCL_STOP
163
164 auto status = solve();
165
166 if (!save_as_path().empty())
167 output();
168
169 return status;
170}
171
172bool
174{
176 {
177 if (_verbose)
178 // LCOV_EXCL_START
179 std::cout << "Step " << _step_count << std::endl;
180 // LCOV_EXCL_STOP
181
182 if (_step_count > 0)
183 advance_step();
185 if (_step_count == 0)
186 {
187 store_input();
188 apply_ic();
189 }
190 else
191 {
193 store_input();
194 solve_step();
195 }
196 store_output();
197
198 if (_verbose)
199 // LCOV_EXCL_START
200 std::cout << std::endl;
201 // LCOV_EXCL_STOP
202 }
203
204 return true;
205}
206
207void
209{
210 if (_in.axis(0).has_subaxis("old_state") && _out.axis(0).has_subaxis("state"))
211 _in.slice("old_state").fill(_out.slice("state"));
212
213 if (_in.axis(0).has_subaxis("old_forces") && _in.axis(0).has_subaxis("forces"))
214 _in.slice("old_forces").fill(_in.slice("forces"));
215}
216
217void
223
224void
231
232void
234{
235 std::string predictor = _predictor;
236 bool cp = false;
237 if (predictor.substr(0, 3) == "CP_")
238 {
239 predictor = predictor.substr(3);
240 cp = true;
241 }
242
243 if (_in.axis(0).has_subaxis("state") && _in.axis(0).has_subaxis("old_state"))
244 {
245 if (predictor == "PREVIOUS_STATE")
246 _in.slice("state").fill(_in.slice("old_state"));
247 else if (predictor == "LINEAR_EXTRAPOLATION")
248 {
249 // Fall back to PREVIOUS_STATE predictor at the 1st time step
250 if (_step_count == 1)
251 _in.slice("state").fill(_in.slice("old_state"));
252 // Otherwise linearly extrapolate in time
253 else
254 {
255 auto t = _in.get<Scalar>(_time_name);
256 auto t_n = _result_in.get<Scalar>(_time_name).batch_index({_step_count - 1});
257 auto t_nm1 = _result_in.get<Scalar>(_time_name).batch_index({_step_count - 2});
258 auto dt = t - t_n;
259 auto dt_n = t_n - t_nm1;
260
261 auto states = _result_out.slice("state");
262 auto state_n = states.tensor().batch_index({_step_count - 1});
263 auto state_nm1 = states.tensor().batch_index({_step_count - 2});
265 _in.slice("state").fill(state);
266 }
267 }
268 else
269 throw NEMLException("Unrecognized predictor type: " + _predictor);
270 }
271
272 if (cp && (_step_count == 1))
273 {
274 SR2 D = _in.get<SR2>(std::vector<std::string>{"forces", "deformation_rate"});
275 auto t = _in.get<Scalar>(_time_name);
276 auto t_n = _result_in.get<Scalar>(_time_name).batch_index({_step_count - 1});
277 _in.set(D * (t - t_n) * _cp_elastic_scale, std::vector<std::string>{"state", "elastic_strain"});
278 }
279}
280
281void
286
287void
292
293void
298
299std::string
301{
302 return _save_as;
303}
304
305torch::nn::ModuleDict
307{
308 auto result_in_cpu = _result_in.to(torch::kCPU);
309 auto result_out_cpu = _result_out.to(torch::kCPU);
310
311 // Dump input variables into a Module
312 auto res_in = std::make_shared<torch::nn::Module>();
313 for (auto var : result_in_cpu.axis(0).variable_accessors(/*recursive=*/true))
314 res_in->register_buffer(utils::stringify(var), result_in_cpu(var).clone());
315
316 // Dump output variables into a Module
317 auto res_out = std::make_shared<torch::nn::Module>();
318 for (auto var : result_out_cpu.axis(0).variable_accessors(/*recursive=*/true))
319 res_out->register_buffer(utils::stringify(var), result_out_cpu(var).clone());
320
321 // Combine input and output
322 torch::nn::ModuleDict res;
323 res->update({{"input", res_in}, {"output", res_out}});
324 return res;
325}
326
327void
329{
330 if (_verbose)
331 // LCOV_EXCL_START
332 std::cout << "Saving results..." << std::endl;
333 // LCOV_EXCL_STOP
334
335 auto cwd = fs::current_path();
336 auto out = cwd / save_as_path();
337
338 if (out.extension() == ".pt")
339 output_pt(out);
340 else
341 // LCOV_EXCL_START
342 neml_assert(false, "Unsupported output format: ", out.extension());
343 // LCOV_EXCL_STOP
344
345 if (_verbose)
346 // LCOV_EXCL_START
347 std::cout << "Results saved to " << save_as_path() << std::endl;
348 // LCOV_EXCL_STOP
349}
350
351void
352TransientDriver::output_pt(const std::filesystem::path & out) const
353{
354 torch::save(result(), out);
355}
356} // namespace neml2
Derived batch_index(TorchSlice indices) const
Get a batch.
Definition BatchTensorBase.cxx:184
The wrapper (decorator) for cross-referencing unresolved values at parse time.
Definition CrossRef.h:52
The Driver drives the execution of a NEML2 Model.
Definition Driver.h:40
bool _verbose
Whether to print out additional (debugging) information during the execution.
Definition Driver.h:59
virtual void check_integrity() const
Check the integrity of the set up.
Definition Driver.h:56
static OptionSet expected_options()
Definition Driver.cxx:30
Definition Factory.h:39
The accessor containing all the information needed to access an item in a LabeledAxis.
Definition LabeledAxisAccessor.h:44
const LabeledAxis & axis(TorchSize i=0) const
Get a specific labeled axis.
Definition LabeledTensor.h:130
static LabeledVector zeros(TorchShapeRef batch_shape, const std::vector< const LabeledAxis * > &axes, const torch::TensorOptions &options=default_tensor_options())
Setup new storage with zeros.
Definition LabeledTensor.cxx:109
Derived to(const torch::TensorOptions &options) const
Change tensor options.
Definition LabeledTensor.cxx:242
void set(const BatchTensorBase< T > &value, S &&... names)
Set and interpret the input as an object.
Definition LabeledTensor.h:193
void batch_index_put(TorchSlice indices, const torch::Tensor &other)
Set a index sliced on the batch dimensions to a value.
Definition LabeledTensor.cxx:214
variable_type< T >::type get(S &&... names) const
Get and interpret the view as an object.
Definition LabeledTensor.h:176
A single-batched, logically 1D LabeledTensor.
Definition LabeledVector.h:38
void fill(const LabeledVector &other, bool recursive=true)
Definition LabeledVector.cxx:45
LabeledVector slice(const std::string &name) const
Slice the logically 1D tensor by a single sub-axis.
Definition LabeledVector.cxx:31
The base class for all constitutive models.
Definition Model.h:53
virtual LabeledVector value(const LabeledVector &in)
Convenient shortcut to construct and return the model value.
Definition Model.cxx:348
virtual std::vector< Diagnosis > preflight() const
Check for common problems.
Definition Model.cxx:70
const std::string & name() const
A readonly reference to the object's name.
Definition NEML2Object.h:65
Definition error.h:33
A custom map-like data structure. The keys are strings, and the values can be nonhomogeneously typed.
Definition OptionSet.h:59
T & set(const std::string &)
Definition OptionSet.h:436
const Storage< std::string, TensorValueBase > & named_parameters() const
Definition ParameterStore.h:44
The (logical) symmetric second order tensor.
Definition SR2.h:46
The (logical) scalar.
Definition Scalar.h:38
virtual bool solve()
Solve the initial value problem.
Definition TransientDriver.cxx:173
virtual void advance_step()
Advance in time: the state becomes old state, and forces become old forces.
Definition TransientDriver.cxx:208
LabeledVector _result_in
Inputs from all time steps.
Definition TransientDriver.h:123
const bool _show_input
Set to true to show model's input axis at the beginning.
Definition TransientDriver.h:118
Model & _model
The model which the driver uses to perform constitutive updates.
Definition TransientDriver.h:92
const bool _show_output
Set to true to show model's output axis at the beginning.
Definition TransientDriver.h:120
std::vector< VariableName > _ic_rot_names
Names for the Rot initial conditions.
Definition TransientDriver.h:132
TransientDriver(const OptionSet &options)
Construct a new TransientDriver object.
Definition TransientDriver.cxx:101
virtual void update_forces()
Update the driving forces for the current time step.
Definition TransientDriver.cxx:218
LabeledVector _result_out
Outputs from all time steps.
Definition TransientDriver.h:125
bool run() override
Let the driver run, return true upon successful completion, and return false otherwise.
Definition TransientDriver.cxx:150
const bool _show_params
Set to true to list all the model parameters at the beginning.
Definition TransientDriver.h:116
Scalar _time
The current time.
Definition TransientDriver.h:97
virtual std::string save_as_path() const
The destination file/path to save the results.
Definition TransientDriver.cxx:300
virtual void apply_predictor()
Apply the predictor to calculate the initial guess for the current time step.
Definition TransientDriver.cxx:233
virtual void store_input()
Save the input of the current time step.
Definition TransientDriver.cxx:288
std::vector< CrossRef< SR2 > > _ic_sr2_values
Values for the SR2 initial conditions.
Definition TransientDriver.h:138
virtual torch::nn::ModuleDict result() const
The results (input and output) from all time steps.
Definition TransientDriver.cxx:306
std::vector< CrossRef< Scalar > > _ic_scalar_values
Values for the scalar initial conditions.
Definition TransientDriver.h:130
std::vector< CrossRef< Rot > > _ic_rot_values
Values for the Rot initial conditions.
Definition TransientDriver.h:134
virtual void check_integrity() const override
Check the integrity of the set up.
Definition TransientDriver.cxx:135
std::vector< VariableName > _ic_sr2_names
Names for the SR2 initial conditions.
Definition TransientDriver.h:136
std::string _save_as
The destination file name or file path.
Definition TransientDriver.h:114
TorchSize _nbatch
The batch size.
Definition TransientDriver.h:105
LabeledVector & _in
The input to the constitutive model.
Definition TransientDriver.h:107
std::vector< VariableName > _ic_scalar_names
Names for scalar initial conditions.
Definition TransientDriver.h:128
LabeledVector & _out
The output of the constitutive model.
Definition TransientDriver.h:109
TorchSize _nsteps
Total number of steps.
Definition TransientDriver.h:103
virtual void output() const
Save the results into the destination file/path.
Definition TransientDriver.cxx:328
VariableName _time_name
VariableName for the time.
Definition TransientDriver.h:101
static OptionSet expected_options()
Definition TransientDriver.cxx:34
TorchSize _step_count
The current step count.
Definition TransientDriver.h:99
std::string _predictor
The predictor used to set the initial guess.
Definition TransientDriver.h:112
virtual void store_output()
Save the output of the current time step.
Definition TransientDriver.cxx:294
virtual void apply_ic()
Apply the initial conditions.
Definition TransientDriver.cxx:225
Real _cp_elastic_scale
Scale value for initial cp predictor.
Definition TransientDriver.h:141
virtual void solve_step()
Perform the constitutive update for the current time step.
Definition TransientDriver.cxx:282
LabeledAxis & output_axis()
Definition VariableStore.h:97
LabeledAxis & input_axis()
Definition VariableStore.h:91
std::string stringify(const T &t)
Definition utils.h:302
Definition CrossRef.cxx:32
double Real
Definition types.h:33
LabeledAxisAccessor VariableName
Definition Variable.h:35
void neml_assert(bool assertion, Args &&... args)
Definition error.h:73