https://mooseframework.inl.gov
LAROMANCE6DInterpolation.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 
11 
12 #ifdef NEML2_ENABLED
13 
14 #include <fstream>
15 #include <initializer_list>
16 #include <torch/torch.h>
17 
18 #include "neml2/tensors/functions/sign.h"
19 #include "neml2/tensors/functions/abs.h"
20 #include "neml2/tensors/functions/pow.h"
21 #include "neml2/tensors/functions/log10.h"
22 #include "neml2/tensors/functions/clamp.h"
23 #include "neml2/tensors/functions/stack.h"
24 
25 namespace neml2
26 {
27 register_NEML2_object(LAROMANCE6DInterpolation);
28 
29 OptionSet
31 {
32  auto options = Model::expected_options();
33  options.doc() =
34  "Multilinear interpolation over six dimensions (von_mises_stress, temperature, "
35  "equivalent_plastic_strain, cell_dislocation_density, wall_dislocation_density, env_factor)";
36  // Model inputs
37  options.set<VariableName>("equivalent_plastic_strain");
38  options.set<VariableName>("von_mises_stress");
39 
40  options.set<VariableName>("cell_dislocation_density");
41  options.set<VariableName>("wall_dislocation_density");
42 
43  options.set<VariableName>("temperature");
44  options.set<VariableName>("env_factor");
45  // Model Outputs
46  options.set_output("output_rate");
47  // JSON
48  options.set<std::string>("model_file_name");
49  options.set<std::string>("model_file_variable_name");
50  // jit does not currently work with this
51  options.set<bool>("jit") = false;
52  options.set("jit").suppressed() = true;
53  return options;
54 }
55 
57  : Model(options),
58  _vm_stress(declare_input_variable<Scalar>("von_mises_stress")),
59  _temperature(declare_input_variable<Scalar>("temperature")),
60  _ep_strain(declare_input_variable<Scalar>("equivalent_plastic_strain")),
61  _cell_dd(declare_input_variable<Scalar>("cell_dislocation_density")),
62  _wall_dd(declare_input_variable<Scalar>("wall_dislocation_density")),
63  _env_fac(declare_input_variable<Scalar>("env_factor")),
64  _output_rate(declare_output_variable<Scalar>("output_rate"))
65 {
66  std::string filename = options.get<std::string>("model_file_name");
67  std::ifstream model_file(filename.c_str());
68  model_file >> _json;
69 
70  // storing grid points for indexing.
71  // these should be stored differently so that they are all read in at once. The order of this can
72  // get messed up easily
73  _stress_grid = json_vector_to_torch("in_stress");
74  _temperature_grid = json_vector_to_torch("in_temperature");
75  _plastic_strain_grid = json_vector_to_torch("in_plastic_strain");
76  _cell_grid = json_vector_to_torch("in_cell");
77  _wall_grid = json_vector_to_torch("in_wall");
78  _env_grid = json_vector_to_torch("in_env");
79 
80  // Read in grid axes transform enums
81  _stress_transform_enum = get_transform_enum(json_to_string("in_stress_transform_type"));
82  _temperature_transform_enum = get_transform_enum(json_to_string("in_temperature_transform_type"));
84  get_transform_enum(json_to_string("in_plastic_strain_transform_type"));
85  _cell_transform_enum = get_transform_enum(json_to_string("in_cell_transform_type"));
86  _wall_transform_enum = get_transform_enum(json_to_string("in_wall_transform_type"));
87  _env_transform_enum = get_transform_enum(json_to_string("in_env_transform_type"));
88 
89  // Read in grid axes transform values
90  _stress_transform_values = json_to_vector("in_stress_transform_values");
91  _temperature_transform_values = json_to_vector("in_temperature_transform_values");
92  _plastic_strain_transform_values = json_to_vector("in_plastic_strain_transform_values");
93  _cell_transform_values = json_to_vector("in_cell_transform_values");
94  _wall_transform_values = json_to_vector("in_wall_transform_values");
95  _env_transform_values = json_to_vector("in_env_transform_values");
96 
97  // Storing values for interpolation
98  _output_rate_name = options.get<std::string>("model_file_variable_name");
100 
101  // set up output transforms
102  if (_output_rate_name == "out_ep")
103  {
104  _output_transform_enum = get_transform_enum(json_to_string("out_strain_rate_transform_type"));
105  _output_transform_values = json_to_vector("out_strain_rate_transform_values");
106  }
107  else if (_output_rate_name == "out_cell")
108  {
109  _output_transform_enum = get_transform_enum(json_to_string("out_cell_rate_transform_type"));
110  _output_transform_values = json_to_vector("out_cell_rate_transform_values");
111  }
112  else if (_output_rate_name == "out_wall")
113  {
114  _output_transform_enum = get_transform_enum(json_to_string("out_wall_rate_transform_type"));
115  _output_transform_values = json_to_vector("out_wall_rate_transform_values");
116  }
117  else
118  {
119  throw NEMLException("This ouput variable is not implemented, model_file_variable_name: " +
120  std::string(_output_rate_name));
121  }
122 }
123 
124 void
126 {
127  // only using first derivatives of out_ep, not out_cell and out_wall
128  if (_output_rate_name == "out_ep")
129  {
130  std::vector<const VariableBase *> inputs = {&_vm_stress};
131  _output_rate.request_AD(inputs);
132  }
133 }
134 
135 void
136 LAROMANCE6DInterpolation::set_value(bool out, bool /*dout_din*/, bool /*d2out_din2*/)
137 {
138  if (out)
140 }
141 
144 {
145  if (name == "COMPRESS")
147  else if (name == "DECOMPRESS")
149  else if (name == "LOG10BOUNDED")
151  else if (name == "EXP10BOUNDED")
153  else if (name == "MINMAX")
154  return TransformEnum::MINMAX;
155 
156  throw NEMLException("Unrecognized transform: " + std::string(name));
157 }
158 
159 std::pair<Scalar, Scalar>
161  const Scalar & interp_points) const
162 {
163  // idx is for the left grid point.
164  // searchsorted returns the right idx so -1 makes it the left
165  auto left_idx = Scalar(torch::searchsorted(grid, interp_points) - 1);
166 
167  // this allows us to extrapolate
168  left_idx = Scalar(torch::clamp(left_idx, 0, grid.sizes()[0] - 2));
169 
170  auto left_coord = grid.batch_index({left_idx});
171  auto right_coord =
172  grid.batch_index({left_idx + torch::tensor(1, default_integer_tensor_options())});
173  auto left_fraction = (right_coord - interp_points) / (right_coord - left_coord);
174 
175  return {left_idx, neml2::batch_stack({left_fraction, 1 - left_fraction}, -1)};
176 }
177 
178 Scalar
180  const std::vector<std::pair<Scalar, Scalar>> index_and_fraction, const Scalar grid_values) const
181 {
182  Scalar result = Scalar::zeros_like(_temperature);
183  for (const auto i : {0, 1})
184  for (const auto j : {0, 1})
185  for (const auto k : {0, 1})
186  for (const auto l : {0, 1})
187  for (const auto m : {0, 1})
188  for (const auto n : {0, 1})
189  {
190  auto vertex_value =
191  grid_values.index({(index_and_fraction[0].first +
192  torch::tensor(i, default_integer_tensor_options())),
193  (index_and_fraction[1].first +
194  torch::tensor(j, default_integer_tensor_options())),
195  (index_and_fraction[2].first +
196  torch::tensor(k, default_integer_tensor_options())),
197  (index_and_fraction[3].first +
198  torch::tensor(l, default_integer_tensor_options())),
199  (index_and_fraction[4].first +
200  torch::tensor(m, default_integer_tensor_options())),
201  (index_and_fraction[5].first +
202  torch::tensor(n, default_integer_tensor_options()))});
203  auto weight = index_and_fraction[0].second.select(-1, i) *
204  index_and_fraction[1].second.select(-1, j) *
205  index_and_fraction[2].second.select(-1, k) *
206  index_and_fraction[3].second.select(-1, l) *
207  index_and_fraction[4].second.select(-1, m) *
208  index_and_fraction[5].second.select(-1, n);
209  result += vertex_value * weight;
210  }
211  return result;
212 }
213 
215 Scalar
217 {
218  // These transform constants should be given in the json file.
219  const auto cell_dd_transformed =
221  const auto wall_dd_transformed =
223  const auto vm_stress_transformed =
225  const auto ep_strain_transformed =
227  const auto temperature_transformed =
229  const auto env_fac_transformed =
231 
232  std::vector<std::pair<Scalar, Scalar>> left_index_weight;
233  left_index_weight.push_back(findLeftIndexAndFraction(_stress_grid, vm_stress_transformed));
234  left_index_weight.push_back(findLeftIndexAndFraction(_temperature_grid, temperature_transformed));
235  left_index_weight.push_back(
236  findLeftIndexAndFraction(_plastic_strain_grid, ep_strain_transformed));
237  left_index_weight.push_back(findLeftIndexAndFraction(_cell_grid, cell_dd_transformed));
238  left_index_weight.push_back(findLeftIndexAndFraction(_wall_grid, wall_dd_transformed));
239  left_index_weight.push_back(findLeftIndexAndFraction(_env_grid, env_fac_transformed));
240  Scalar interpolated_result = compute_interpolation(left_index_weight, _grid_values);
241  Scalar transformed_result =
243  return transformed_result;
244 }
245 
246 Scalar
248  const std::vector<double> & param,
249  TransformEnum transform_type) const
250 {
251  switch (transform_type)
252  {
254  return transform_compress(data, param);
255 
257  return transform_decompress(data, param);
258 
260  return transform_log10_bounded(data, param);
261 
263  return transform_exp10_bounded(data, param);
264 
266  return transform_min_max(data, param);
267 
268  default:
269  return data;
270  }
271 }
272 
273 Scalar
275  const std::vector<double> & param) const
276 {
277  double factor = param[0];
278  double compressor = param[1];
279  double original_min = param[2];
280  auto d1 = neml2::sign(data) * neml2::pow(neml2::abs(data * factor), compressor);
281  auto transformed_data = neml2::log10(1.0 + d1 - original_min);
282  return transformed_data;
283 }
284 
285 Scalar
287  const std::vector<double> & param) const
288 {
289  double factor = param[0];
290  double compressor = param[1];
291  double original_min = param[2];
292  auto d1 = neml2::pow(10.0, data) - 1.0 + original_min;
293  auto transformed_data = neml2::sign(d1) * neml2::pow(neml2::abs(d1), 1.0 / compressor) / factor;
294  return transformed_data;
295 }
296 
297 Scalar
299  const std::vector<double> & param) const
300 
301 {
302  double factor = param[0];
303  double lowerbound = param[1];
304  double upperbound = param[2];
305  double logmin = param[3];
306  double logmax = param[4];
307  double range = upperbound - lowerbound;
308  auto transformed_data =
309  range * (neml2::log10(data + factor) - logmin) / (logmax - logmin) + lowerbound;
310  return transformed_data;
311 }
312 
313 Scalar
315  const std::vector<double> & param) const
316 {
317  double factor = param[0];
318  double lowerbound = param[1];
319  double upperbound = param[2];
320  double logmin = param[3];
321  double logmax = param[4];
322  double range = upperbound - lowerbound;
323  auto transformed_data =
324  (neml2::pow(10.0, ((data - lowerbound) * (logmax - logmin) / range) + logmin) - factor);
325  return transformed_data;
326 }
327 
328 Scalar
330  const std::vector<double> & param) const
331 {
332  double data_min = param[0];
333  double data_max = param[1];
334  double scaled_min = param[2];
335  double scaled_max = param[3];
336  auto transformed_data =
337  ((data - data_min) / (data_max - data_min)) * (scaled_max - scaled_min) + scaled_min;
338  return transformed_data;
339 }
340 
341 std::string
342 LAROMANCE6DInterpolation::json_to_string(const std::string & key) const
343 {
344  if (!_json.contains(key))
345  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
346 
347  std::string name = _json[key].get<std::string>();
348  return name;
349 }
350 
351 std::vector<double>
352 LAROMANCE6DInterpolation::json_to_vector(const std::string & key) const
353 {
354  if (!_json.contains(key))
355  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
356 
357  std::vector<double> data_vec = _json[key].get<std::vector<double>>();
358  return data_vec;
359 }
360 
361 Scalar
363 {
364  if (!_json.contains(key))
365  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
366 
367  std::vector<double> in_data = _json[key].get<std::vector<double>>();
368  return Scalar::create(in_data).clone();
369 }
370 
371 Scalar
373 {
374  using std::vector;
375  if (!_json.contains(key))
376  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
377 
378  vector<vector<vector<vector<vector<vector<double>>>>>> out_data =
379  _json[key].get<vector<vector<vector<vector<vector<vector<double>>>>>>>();
380 
381  const int64_t sz_l0 = out_data.size();
382  const int64_t sz_l1 = out_data[0].size();
383  const int64_t sz_l2 = out_data[0][0].size();
384  const int64_t sz_l3 = out_data[0][0][0].size();
385  const int64_t sz_l4 = out_data[0][0][0][0].size();
386  const int64_t sz_l5 = out_data[0][0][0][0][0].size();
387 
388  auto check_level_size =
389  [](const int64_t current_vec_size, const int64_t sz_level, const std::string & key)
390  {
391  if (current_vec_size != sz_level)
392  throw NEMLException("Incorrect JSON interpolation grid size for '" + key + "'.");
393  };
394 
395  std::vector<double> linearize_values;
396  check_level_size(out_data.size(), sz_l0, key);
397  for (auto && level1 : out_data)
398  {
399  check_level_size(level1.size(), sz_l1, key);
400  for (auto && level2 : level1)
401  {
402  check_level_size(level2.size(), sz_l2, key);
403  for (auto && level3 : level2)
404  {
405  check_level_size(level3.size(), sz_l3, key);
406  for (auto && level4 : level3)
407  {
408  check_level_size(level4.size(), sz_l4, key);
409  for (auto && level5 : level4)
410  {
411  check_level_size(level5.size(), sz_l5, key);
412  for (auto && value : level5)
413  linearize_values.push_back(value);
414  }
415  }
416  }
417  }
418  }
419 
420  return Scalar::create(linearize_values)
421  .batch_reshape({sz_l0, sz_l1, sz_l2, sz_l3, sz_l4, sz_l5})
422  .clone();
423 }
424 
425 } // namespace neml2
426 
427 #endif
Scalar transform_data(const Scalar &data, const std::vector< double > &param, TransformEnum transform_type) const
transform data
TransformEnum _output_transform_enum
output transform enum
const Variable< Scalar > & _temperature
Temperature.
std::string json_to_string(const std::string &key) const
read in json axes transform name
TransformEnum get_transform_enum(const std::string &name) const
const Variable< Scalar > & _wall_dd
wall dislocation density
const Variable< Scalar > & _cell_dd
cell dislocation density
std::vector< double > _plastic_strain_transform_values
register_NEML2_object(LibtorchModel)
const Variable< Scalar > & _env_fac
environmental factor
std::string _output_rate_name
output transform rate name
Scalar transform_exp10_bounded(const Scalar &data, const std::vector< double > &params) const
std::vector< double > _stress_transform_values
input transform values
std::pair< Scalar, Scalar > findLeftIndexAndFraction(const Scalar &grid, const Scalar &interp_points) const
find index of input point
TransformEnum _stress_transform_enum
input transform enums
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
Scalar compute_interpolation(const std::vector< std::pair< Scalar, Scalar >> index_and_fraction, const Scalar grid_values) const
compute interpolated value
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
const std::string name
Definition: Setup.h:20
Scalar json_6Dvector_to_torch(const std::string &key) const
read 6D grid date from json and store in Torch tensor
Scalar _grid_values
grid values being interpolated
const Variable< Scalar > & _vm_stress
Model input for interpolation.
void set_value(bool, bool, bool) override
Scalar _stress_grid
grid for interpolation
Scalar transform_decompress(const Scalar &data, const std::vector< double > &params) const
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
Scalar transform_min_max(const Scalar &data, const std::vector< double > &params) const
LAROMANCE6DInterpolation(const OptionSet &options)
OStreamProxy out
Scalar json_vector_to_torch(const std::string &key) const
read 1D vector of grid points from json and store in Torch tensor
nlohmann::json _json
JSON object containing interpolation grid and values.
Scalar transform_compress(const Scalar &data, const std::vector< double > &params) const
LAROMANCE transforms for input axes and output axis.
std::vector< double > _output_transform_values
output transform values
std::vector< double > json_to_vector(const std::string &key) const
read in json axes transform constants
Scalar interpolate_and_transform() const
compute interpolated value and transform results
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
Scalar transform_log10_bounded(const Scalar &data, const std::vector< double > &params) const
Variable< Scalar > & _output_rate
Model output.
std::vector< double > _temperature_transform_values
const Variable< Scalar > & _ep_strain
The creep strain.
static const std::string k
Definition: NS.h:130