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 
37  // Model inputs
38  options.add_input("equivalent_plastic_strain", "The equivalent plastic strain");
39  options.add_input("von_mises_stress", "The von Mises stress");
40  options.add_input("cell_dislocation_density", "The cell dislocation density");
41  options.add_input("wall_dislocation_density", "The wall dislocation density");
42  options.add_input("temperature", "The temperature");
43  options.add_input("env_factor", "The environment factor");
44 
45  // Model Outputs
46  options.add_output("output_rate", "The output rate");
47 
48  // JSON
49  options.add<std::string>("model_file_name", "The name of the model file");
50  options.add<std::string>("model_file_variable_name",
51  "The name of the variable in the model file");
52 
53  // jit does not currently work with this
54  options.set<bool>("jit", false);
55  options.suppress("jit");
56 
57  return options;
58 }
59 
61  : Model(options),
62  _vm_stress(declare_input_variable<Scalar>("von_mises_stress")),
63  _temperature(declare_input_variable<Scalar>("temperature")),
64  _ep_strain(declare_input_variable<Scalar>("equivalent_plastic_strain")),
65  _cell_dd(declare_input_variable<Scalar>("cell_dislocation_density")),
66  _wall_dd(declare_input_variable<Scalar>("wall_dislocation_density")),
67  _env_fac(declare_input_variable<Scalar>("env_factor")),
68  _output_rate(declare_output_variable<Scalar>("output_rate"))
69 {
70  std::string filename = options.get<std::string>("model_file_name");
71  std::ifstream model_file(filename.c_str());
72  model_file >> _json;
73 
74  // storing grid points for indexing.
75  // these should be stored differently so that they are all read in at once. The order of this can
76  // get messed up easily
77  _stress_grid = json_vector_to_torch("in_stress");
78  _temperature_grid = json_vector_to_torch("in_temperature");
79  _plastic_strain_grid = json_vector_to_torch("in_plastic_strain");
80  _cell_grid = json_vector_to_torch("in_cell");
81  _wall_grid = json_vector_to_torch("in_wall");
82  _env_grid = json_vector_to_torch("in_env");
83 
84  // Read in grid axes transform enums
85  _stress_transform_enum = get_transform_enum(json_to_string("in_stress_transform_type"));
86  _temperature_transform_enum = get_transform_enum(json_to_string("in_temperature_transform_type"));
88  get_transform_enum(json_to_string("in_plastic_strain_transform_type"));
89  _cell_transform_enum = get_transform_enum(json_to_string("in_cell_transform_type"));
90  _wall_transform_enum = get_transform_enum(json_to_string("in_wall_transform_type"));
91  _env_transform_enum = get_transform_enum(json_to_string("in_env_transform_type"));
92 
93  // Read in grid axes transform values
94  _stress_transform_values = json_to_vector("in_stress_transform_values");
95  _temperature_transform_values = json_to_vector("in_temperature_transform_values");
96  _plastic_strain_transform_values = json_to_vector("in_plastic_strain_transform_values");
97  _cell_transform_values = json_to_vector("in_cell_transform_values");
98  _wall_transform_values = json_to_vector("in_wall_transform_values");
99  _env_transform_values = json_to_vector("in_env_transform_values");
100 
101  // Storing values for interpolation
102  _output_rate_name = options.get<std::string>("model_file_variable_name");
104 
105  // set up output transforms
106  if (_output_rate_name == "out_ep")
107  {
108  _output_transform_enum = get_transform_enum(json_to_string("out_strain_rate_transform_type"));
109  _output_transform_values = json_to_vector("out_strain_rate_transform_values");
110  }
111  else if (_output_rate_name == "out_cell")
112  {
113  _output_transform_enum = get_transform_enum(json_to_string("out_cell_rate_transform_type"));
114  _output_transform_values = json_to_vector("out_cell_rate_transform_values");
115  }
116  else if (_output_rate_name == "out_wall")
117  {
118  _output_transform_enum = get_transform_enum(json_to_string("out_wall_rate_transform_type"));
119  _output_transform_values = json_to_vector("out_wall_rate_transform_values");
120  }
121  else
122  {
123  throw NEMLException("This ouput variable is not implemented, model_file_variable_name: " +
124  std::string(_output_rate_name));
125  }
126 }
127 
128 void
130 {
131  // only using first derivatives of out_ep, not out_cell and out_wall
132  if (_output_rate_name == "out_ep")
133  {
134  std::vector<const VariableBase *> inputs = {&_vm_stress};
135  _output_rate.request_AD(inputs);
136  }
137 }
138 
139 void
140 LAROMANCE6DInterpolation::set_value(bool out, bool /*dout_din*/, bool /*d2out_din2*/)
141 {
142  if (out)
144 }
145 
148 {
149  if (name == "COMPRESS")
151  else if (name == "DECOMPRESS")
153  else if (name == "LOG10BOUNDED")
155  else if (name == "EXP10BOUNDED")
157  else if (name == "MINMAX")
158  return TransformEnum::MINMAX;
159 
160  throw NEMLException("Unrecognized transform: " + std::string(name));
161 }
162 
163 std::pair<Scalar, Scalar>
165  const Scalar & interp_points) const
166 {
167  // idx is for the left grid point.
168  // searchsorted returns the right idx so -1 makes it the left
169  auto left_idx = Scalar(torch::searchsorted(grid, interp_points) - 1, 0);
170 
171  // this allows us to extrapolate
172  left_idx = Scalar(torch::clamp(left_idx, 0, grid.sizes()[0] - 2), 0);
173 
174  auto left_coord = grid.dynamic_index({left_idx});
175  auto right_coord =
176  grid.dynamic_index({left_idx + torch::tensor(1, default_integer_tensor_options())});
177  auto left_fraction = (right_coord - interp_points) / (right_coord - left_coord);
178 
179  return {left_idx, neml2::dynamic_stack({left_fraction, 1 - left_fraction}, -1)};
180 }
181 
182 Scalar
184  const std::vector<std::pair<Scalar, Scalar>> index_and_fraction, const Scalar grid_values) const
185 {
186  Scalar result = Scalar::zeros_like(_temperature());
187  for (const auto i : {0, 1})
188  for (const auto j : {0, 1})
189  for (const auto k : {0, 1})
190  for (const auto l : {0, 1})
191  for (const auto m : {0, 1})
192  for (const auto n : {0, 1})
193  {
194  auto vertex_value =
195  grid_values.index({(index_and_fraction[0].first +
196  torch::tensor(i, default_integer_tensor_options())),
197  (index_and_fraction[1].first +
198  torch::tensor(j, default_integer_tensor_options())),
199  (index_and_fraction[2].first +
200  torch::tensor(k, default_integer_tensor_options())),
201  (index_and_fraction[3].first +
202  torch::tensor(l, default_integer_tensor_options())),
203  (index_and_fraction[4].first +
204  torch::tensor(m, default_integer_tensor_options())),
205  (index_and_fraction[5].first +
206  torch::tensor(n, default_integer_tensor_options()))});
207  auto weight = index_and_fraction[0].second.select(-1, i) *
208  index_and_fraction[1].second.select(-1, j) *
209  index_and_fraction[2].second.select(-1, k) *
210  index_and_fraction[3].second.select(-1, l) *
211  index_and_fraction[4].second.select(-1, m) *
212  index_and_fraction[5].second.select(-1, n);
213  result += vertex_value * weight;
214  }
215  return result;
216 }
217 
219 Scalar
221 {
222  // These transform constants should be given in the json file.
223  const auto cell_dd_transformed =
225  const auto wall_dd_transformed =
227  const auto vm_stress_transformed =
229  const auto ep_strain_transformed = transform_data(
231  const auto temperature_transformed =
233  const auto env_fac_transformed =
235 
236  std::vector<std::pair<Scalar, Scalar>> left_index_weight;
237  left_index_weight.push_back(findLeftIndexAndFraction(_stress_grid, vm_stress_transformed));
238  left_index_weight.push_back(findLeftIndexAndFraction(_temperature_grid, temperature_transformed));
239  left_index_weight.push_back(
240  findLeftIndexAndFraction(_plastic_strain_grid, ep_strain_transformed));
241  left_index_weight.push_back(findLeftIndexAndFraction(_cell_grid, cell_dd_transformed));
242  left_index_weight.push_back(findLeftIndexAndFraction(_wall_grid, wall_dd_transformed));
243  left_index_weight.push_back(findLeftIndexAndFraction(_env_grid, env_fac_transformed));
244  Scalar interpolated_result = compute_interpolation(left_index_weight, _grid_values);
245  Scalar transformed_result =
247  return transformed_result;
248 }
249 
250 Scalar
252  const std::vector<double> & param,
253  TransformEnum transform_type) const
254 {
255  switch (transform_type)
256  {
258  return transform_compress(data, param);
259 
261  return transform_decompress(data, param);
262 
264  return transform_log10_bounded(data, param);
265 
267  return transform_exp10_bounded(data, param);
268 
270  return transform_min_max(data, param);
271 
272  default:
273  return data;
274  }
275 }
276 
277 Scalar
279  const std::vector<double> & param) const
280 {
281  double factor = param[0];
282  double compressor = param[1];
283  double original_min = param[2];
284  auto d1 = neml2::sign(data) * neml2::pow(neml2::abs(data * factor), compressor);
285  auto transformed_data = neml2::log10(1.0 + d1 - original_min);
286  return transformed_data;
287 }
288 
289 Scalar
291  const std::vector<double> & param) const
292 {
293  double factor = param[0];
294  double compressor = param[1];
295  double original_min = param[2];
296  auto d1 = neml2::pow(10.0, data) - 1.0 + original_min;
297  auto transformed_data = neml2::sign(d1) * neml2::pow(neml2::abs(d1), 1.0 / compressor) / factor;
298  return transformed_data;
299 }
300 
301 Scalar
303  const std::vector<double> & param) const
304 
305 {
306  double factor = param[0];
307  double lowerbound = param[1];
308  double upperbound = param[2];
309  double logmin = param[3];
310  double logmax = param[4];
311  double range = upperbound - lowerbound;
312  auto transformed_data =
313  range * (neml2::log10(data + factor) - logmin) / (logmax - logmin) + lowerbound;
314  return transformed_data;
315 }
316 
317 Scalar
319  const std::vector<double> & param) const
320 {
321  double factor = param[0];
322  double lowerbound = param[1];
323  double upperbound = param[2];
324  double logmin = param[3];
325  double logmax = param[4];
326  double range = upperbound - lowerbound;
327  auto transformed_data =
328  (neml2::pow(10.0, ((data - lowerbound) * (logmax - logmin) / range) + logmin) - factor);
329  return transformed_data;
330 }
331 
332 Scalar
334  const std::vector<double> & param) const
335 {
336  double data_min = param[0];
337  double data_max = param[1];
338  double scaled_min = param[2];
339  double scaled_max = param[3];
340  auto transformed_data =
341  ((data - data_min) / (data_max - data_min)) * (scaled_max - scaled_min) + scaled_min;
342  return transformed_data;
343 }
344 
345 std::string
346 LAROMANCE6DInterpolation::json_to_string(const std::string & key) const
347 {
348  if (!_json.contains(key))
349  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
350 
351  std::string name = _json[key].get<std::string>();
352  return name;
353 }
354 
355 std::vector<double>
356 LAROMANCE6DInterpolation::json_to_vector(const std::string & key) const
357 {
358  if (!_json.contains(key))
359  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
360 
361  std::vector<double> data_vec = _json[key].get<std::vector<double>>();
362  return data_vec;
363 }
364 
365 Scalar
367 {
368  if (!_json.contains(key))
369  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
370 
371  std::vector<double> in_data = _json[key].get<std::vector<double>>();
372  return Scalar::create(in_data).clone();
373 }
374 
375 Scalar
377 {
378  using std::vector;
379  if (!_json.contains(key))
380  throw NEMLException("The key '" + std::string(key) + "' is missing from the JSON data file.");
381 
382  vector<vector<vector<vector<vector<vector<double>>>>>> out_data =
383  _json[key].get<vector<vector<vector<vector<vector<vector<double>>>>>>>();
384 
385  const int64_t sz_l0 = out_data.size();
386  const int64_t sz_l1 = out_data[0].size();
387  const int64_t sz_l2 = out_data[0][0].size();
388  const int64_t sz_l3 = out_data[0][0][0].size();
389  const int64_t sz_l4 = out_data[0][0][0][0].size();
390  const int64_t sz_l5 = out_data[0][0][0][0][0].size();
391 
392  auto check_level_size =
393  [](const int64_t current_vec_size, const int64_t sz_level, const std::string & key)
394  {
395  if (current_vec_size != sz_level)
396  throw NEMLException("Incorrect JSON interpolation grid size for '" + key + "'.");
397  };
398 
399  std::vector<double> linearize_values;
400  check_level_size(out_data.size(), sz_l0, key);
401  for (auto && level1 : out_data)
402  {
403  check_level_size(level1.size(), sz_l1, key);
404  for (auto && level2 : level1)
405  {
406  check_level_size(level2.size(), sz_l2, key);
407  for (auto && level3 : level2)
408  {
409  check_level_size(level3.size(), sz_l3, key);
410  for (auto && level4 : level3)
411  {
412  check_level_size(level4.size(), sz_l4, key);
413  for (auto && level5 : level4)
414  {
415  check_level_size(level5.size(), sz_l5, key);
416  for (auto && value : level5)
417  linearize_values.push_back(value);
418  }
419  }
420  }
421  }
422  }
423 
424  return Scalar::create(linearize_values)
425  .dynamic_reshape({sz_l0, sz_l1, sz_l2, sz_l3, sz_l4, sz_l5})
426  .clone();
427 }
428 
429 } // namespace neml2
430 
431 #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:21
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:134