https://mooseframework.inl.gov
NEML2FEInterpolation.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 NEML2_ENABLED
11 
12 // libmesh includes
13 #include "libmesh/petsc_vector.h"
14 
15 // Torch includes
16 #include <ATen/ops/from_blob.h>
17 
18 // NEML2 includes
19 #include "neml2/tensors/functions/discretization/scatter.h"
20 #include "neml2/tensors/functions/discretization/interpolate.h"
21 
22 // MOOSE includes
23 #include "NEML2FEInterpolation.h"
24 
25 using namespace libMesh;
26 
28 
31 {
33 
34  params.addClassDescription(
35  "This user object provides an interface to NEML2 for finite element "
36  "interpolation of variables and their gradients. It gathers the shape "
37  "functions and DOF maps for each variable in the assembly and provides "
38  "them as NEML2 tensors for use in NEML2 models.");
39 
40  params.addRequiredParam<UserObjectName>(
41  "assembly", "The NEML2Assembly object to use to provide assembly information");
42 
44  execute_options = {EXEC_INITIAL, EXEC_LINEAR};
45  params.set<ExecFlagEnum>("execute_on") = execute_options;
46  params.suppressParameter<ExecFlagEnum>("execute_on");
47 
48  return params;
49 }
50 
52  : ElementUserObject(parameters), _neml2_assembly(getUserObject<NEML2Assembly>("assembly"))
53 {
54 }
55 
56 const neml2::Tensor &
57 NEML2FEInterpolation::getValue(const std::string & var_name)
58 {
59  auto [it, success] = _vars.emplace(var_name, neml2::Tensor());
60 
61  if (success)
62  {
63  const auto * var = getMOOSEVariable(var_name);
64  _phis.emplace(var->feType(), &var->phi());
65  _moose_vars.emplace(var_name, var);
66  }
67 
68  return it->second;
69 }
70 
71 const neml2::Tensor &
72 NEML2FEInterpolation::getGradient(const std::string & var_name)
73 {
74  auto [it, success] = _grad_vars.emplace(var_name, neml2::Tensor());
75 
76  if (success)
77  {
78  const auto * var = getMOOSEVariable(var_name);
79  _grad_phis.emplace(var->feType(), &var->gradPhi());
80  _moose_vars.emplace(var_name, var);
81  }
82 
83  return it->second;
84 }
85 
86 const neml2::Tensor &
87 NEML2FEInterpolation::getPhi(const std::string & var_name)
88 {
89  const auto * var = getMOOSEVariable(var_name);
90 
91  const auto it = _neml2_phi.find(var->feType());
92  if (it != _neml2_phi.end())
93  return it->second;
94 
95  _phis.emplace(var->feType(), &var->phi());
96  auto [it2, success] = _neml2_phi.emplace(var->feType(), neml2::Tensor());
97  return it2->second;
98 }
99 
100 const neml2::Tensor &
101 NEML2FEInterpolation::getPhiGradient(const std::string & var_name)
102 {
103  const auto * var = getMOOSEVariable(var_name);
104 
105  const auto it = _neml2_grad_phi.find(var->feType());
106  if (it != _neml2_grad_phi.end())
107  return it->second;
108 
109  _grad_phis.emplace(var->feType(), &var->gradPhi());
110  auto [it2, success] = _neml2_grad_phi.emplace(var->feType(), neml2::Tensor());
111  return it2->second;
112 }
113 
114 const neml2::Tensor &
115 NEML2FEInterpolation::getDofMap(const std::string & var_name)
116 {
117  return _neml2_dof_map[var_name];
118 }
119 
120 const std::vector<dof_id_type> &
121 NEML2FEInterpolation::getGlobalDofMap(const std::string & var_name)
122 {
123  return _moose_dof_map_global[var_name];
124 }
125 
126 int64_t
128 {
129  return _local_ndof;
130 }
131 
132 const MooseVariableFE<Real> *
133 NEML2FEInterpolation::getMOOSEVariable(const std::string & var_name) const
134 {
135  const auto * var = &_fe_problem.getVariable(
137  const auto * var_fe = dynamic_cast<const MooseVariableFE<Real> *>(var);
138 
139  if (!var_fe)
140  mooseError("NEML2FEInterpolation only supports variables of type MooseVariableFE<Real>");
141 
142  if (var_fe->scalingFactor() != 1)
143  mooseError("Scaling factors other than unity are not yet supported");
144 
145  // check domain restrictions for compatibility
146  if (!var_fe->hasBlocks(blockIDs()))
147  mooseError("The variable '",
148  var_fe->name(),
149  "' must be defined on all blocks '",
150  name(),
151  "' is defined on.");
152 
153  return var_fe;
154 }
155 
156 void
158 {
159  _petsc_solution = dynamic_cast<const PetscVector<Real> *>(_sys.currentSolution());
160 
161  // check if the solution vector is of a supported type
162  if (!_petsc_solution)
163  mooseError("Only solution vectors of type PetscVector are currently supported");
164 
165  if (_tid != 0)
167 }
168 
169 void
171 {
172  _fem_context_up_to_date = false;
174 }
175 
176 void
178 {
179  _interp_up_to_date = false;
180 }
181 
182 void
184 {
186 }
187 
188 void
190 {
192  return;
193 
194  _ndofe.clear();
195  _moose_dof_map.clear();
196  _moose_dof_map_global.clear();
197  _moose_phi.clear();
198  _moose_grad_phi.clear();
199  _local_ndof = 0;
200 }
201 
202 void
204 {
205  auto & main_uo = _fe_problem.getUserObject<NEML2FEInterpolation>(name(), /*tid=*/0);
206  for (const auto & [var_name, var] : main_uo._moose_vars)
207  {
208  _moose_vars.emplace(var_name, getMOOSEVariable(var_name));
209  if (main_uo._phis.count(var->feType()))
210  getPhi(var_name);
211  if (main_uo._grad_phis.count(var->feType()))
212  getPhiGradient(var_name);
213  }
214 }
215 
216 void
218 {
219  const auto & other = static_cast<const NEML2FEInterpolation &>(y);
220  mooseAssert(_fem_context_up_to_date == other._fem_context_up_to_date,
221  "NEML2FEInterpolation becomes out of sync with other thread");
222 
224  return;
225 
226  auto merge_map_vecs = [](auto & map1, const auto & map2)
227  {
228  for (const auto & [key, map2_val] : map2)
229  {
230  auto & map1_val = map1[key];
231  map1_val.insert(map1_val.end(), map2_val.begin(), map2_val.end());
232  }
233  };
234 
235  merge_map_vecs(_moose_dof_map, other._moose_dof_map);
236  merge_map_vecs(_moose_phi, other._moose_phi);
237  merge_map_vecs(_moose_grad_phi, other._moose_grad_phi);
238 }
239 
240 void
242 {
244  return;
245 
246  // DOF indices
247  const auto & nl_dof_map = _sys.dofMap();
248  for (const auto & [var_name, var] : _moose_vars)
249  {
250  nl_dof_map.dof_indices(_current_elem, _dof_indices, var->number());
251  auto [it, success] = _ndofe.emplace(var->feType(), _dof_indices.size());
252  if (!success && std::size_t(it->second) != _dof_indices.size())
253  mooseError("DOF map size mismatch for variable ",
254  var_name,
255  ", got ",
256  it->second,
257  " and ",
258  _dof_indices.size());
259  auto & moose_dof_map = _moose_dof_map[var_name];
260  auto & moose_dof_map_global = _moose_dof_map_global[var_name];
261  for (auto dof : _dof_indices)
262  {
263  moose_dof_map.push_back(_petsc_solution->map_global_to_local_index(dof));
264  moose_dof_map_global.push_back(dof);
265  }
266  }
267 
268  // shape function values
269  for (const auto & [fetype, phi] : _phis)
270  {
271  auto & moose_phi = _moose_phi[fetype];
272  for (auto i : index_range(*phi))
273  for (auto qp : index_range(_q_point))
274  moose_phi.push_back((*phi)[i][qp]);
275  }
276 
277  // shape function gradients
278  for (const auto & [fetype, grad_phi] : _grad_phis)
279  {
280  auto & moose_grad_phi = _moose_grad_phi[fetype];
281  for (auto i : index_range(*grad_phi))
282  for (auto qp : index_range(_q_point))
283  for (auto j : make_range(3))
284  moose_grad_phi.push_back((*grad_phi)[i][qp](j));
285  }
286 }
287 
288 void
290 {
291  TIME_SECTION("finalize", 1, "Updating FEM context and interpolations for NEML2");
292 
295 
296  if (!_interp_up_to_date)
298 }
299 
300 void
302 {
303  TIME_SECTION("updateFEMContext", 2, "Updating FEM context for NEML2");
304 
305  updateDofMap();
306  updatePhi();
307  updateGradPhi();
308 
309  // done
311 }
312 
313 void
315 {
316  auto device = _app.getLibtorchDevice();
317  auto nelem = _neml2_assembly.numElem();
318 
319  for (auto & [var_name, moose_dof_map] : _moose_dof_map)
320  {
321  auto var = _moose_vars.at(var_name);
322  auto ndofe = _ndofe.at(var->feType());
323 
324  // sanity check on sizes
325  if (moose_dof_map.size() != std::size_t(nelem * ndofe))
326  mooseError(
327  "dof map size mismatch, expected ", nelem * ndofe, " but got ", moose_dof_map.size());
328 
329  // convert to neml2 tensor
330  _neml2_dof_map[var_name] =
331  neml2::Tensor(at::from_blob(moose_dof_map.data(), {nelem, ndofe}, torch::kInt64), 2)
332  .to(device);
333 
334  _local_ndof = std::max(_local_ndof, _neml2_dof_map[var_name].max().item<int64_t>() + 1);
335  }
336 }
337 
338 void
340 {
341  auto device = _app.getLibtorchDevice();
342  auto nelem = _neml2_assembly.numElem();
343  auto nqp = _neml2_assembly.numQP();
344 
345  for (auto & [fetype, moose_phi] : _moose_phi)
346  {
347  auto ndofe = _ndofe.at(fetype);
348 
349  // sanity check on sizes
350  if (moose_phi.size() != std::size_t(nelem * ndofe * nqp))
351  mooseError("shape function size mismatch, expected ",
352  nelem * ndofe * nqp,
353  " but got ",
354  moose_phi.size());
355  _neml2_phi[fetype] =
356  neml2::Tensor(at::from_blob(moose_phi.data(), {nelem, ndofe, nqp}, torch::kFloat64), 3)
357  .to(device);
358  }
359 }
360 
361 void
363 {
364  auto device = _app.getLibtorchDevice();
365  auto nelem = _neml2_assembly.numElem();
366  auto nqp = _neml2_assembly.numQP();
367 
368  for (auto & [fetype, moose_grad_phi] : _moose_grad_phi)
369  {
370  auto ndofe = _ndofe.at(fetype);
371 
372  // sanity check on sizes
373  if (moose_grad_phi.size() != std::size_t(nelem * ndofe * nqp * 3))
374  mooseError("shape function gradient size mismatch, expected ",
375  nelem * ndofe * nqp * 3,
376  " but got ",
377  moose_grad_phi.size());
378  _neml2_grad_phi[fetype] =
379  neml2::Tensor(at::from_blob(moose_grad_phi.data(), {nelem, ndofe, nqp, 3}, torch::kFloat64),
380  3)
381  .to(device);
382  }
383 }
384 
385 void
387 {
388  TIME_SECTION("updateInterpolations", 2, "Updating FEM interpolations for NEML2");
389 
390  // convert the local solution vector to neml2 tensor
391  auto sol = at::from_blob(const_cast<Real *>(_petsc_solution->get_array_read()),
392  {local_ndof()},
393  torch::kFloat64)
394  .to(_app.getLibtorchDevice());
395 
396  // interpolate variable values
397  for (auto & [var_name, val] : _vars)
398  {
399  const auto & dof_map = _neml2_dof_map[var_name];
400  const auto fetype = _moose_vars[var_name]->feType();
401  const auto & phi = _neml2_phi[fetype];
402  auto sol_scattered = neml2::discretization::scatter(sol, dof_map);
403  val = neml2::discretization::interpolate(sol_scattered, phi);
404  }
405 
406  // interpolate variable gradients
407  for (auto & [var_name, val] : _grad_vars)
408  {
409  const auto & dof_map = _neml2_dof_map[var_name];
410  const auto fetype = _moose_vars[var_name]->feType();
411  const auto & grad_phi = _neml2_grad_phi[fetype];
412  auto sol_scattered = neml2::discretization::scatter(sol, dof_map);
413  val = neml2::discretization::interpolate(sol_scattered, grad_phi);
414  }
415 
416  // close solution and residual vector access
417  const_cast<PetscVector<Real> *>(_petsc_solution)->restore_array();
418 
419  // done
420  _interp_up_to_date = true;
421 }
422 
423 #endif
This user object serves as the "interface" for interpolating MOOSE variable values and gradients from...
const THREAD_ID _tid
Thread ID of this postprocessor.
const neml2::Tensor & getPhiGradient(const std::string &var_name)
Get the shape function gradient associated with a MOOSE variable.
const std::vector< dof_id_type > & getGlobalDofMap(const std::string &var_name)
Similar to getDofMap, but returns the global dof map (as a flattened vector of dof_id_type) ...
virtual void updateInterpolations()
virtual const NumericVector< Number > *const & currentSolution() const =0
The solution vector that is currently being operated on.
const neml2::Tensor & getGradient(const std::string &var_name)
Get the variable gradient of a MOOSE nonlinear variable converted to a NEML2 tensor.
A MultiMooseEnum object to hold "execute_on" flags.
Definition: ExecFlagEnum.h:21
std::unordered_map< FEType, const VariablePhiGradient * > _grad_phis
void initialize() override
Called before execute() is ever called so that data can be cleared.
const MooseArray< Point > & _q_point
T & getUserObject(const std::string &name, unsigned int tid=0) const
Get the user object by its name.
bool _fem_context_up_to_date
Whether the current FEM context is up to date.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
bool _interp_up_to_date
Whether the current interpolations are up to date.
int64_t numQP() const
Number of quadrature points per element.
Definition: NEML2Assembly.h:31
static InputParameters validParams()
const NEML2Assembly & _neml2_assembly
Assembly.
const neml2::Tensor & getValue(const std::string &var_name)
Get the variable value of a MOOSE nonlinear variable converted to a NEML2 tensor. ...
std::unordered_map< std::string, neml2::Tensor > _vars
coupled variables (by value) requested by other objects
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
NEML2FEInterpolation(const InputParameters &parameters)
virtual const std::set< SubdomainID > & blockIDs() const
Return the block subdomain ids for this object Note, if this is not block restricted, this function returns all mesh subdomain ids.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const PetscVector< Real > * _petsc_solution
PETSc solution vector.
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...
virtual const MooseVariableFieldBase & getVariable(const THREAD_ID tid, const std::string &var_name, Moose::VarKindType expected_var_type=Moose::VarKindType::VAR_ANY, Moose::VarFieldType expected_var_field_type=Moose::VarFieldType::VAR_FIELD_ANY) const override
Returns the variable reference for requested variable which must be of the expected_var_type (Nonline...
void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
registerMooseObject("MooseApp", NEML2FEInterpolation)
auto max(const L &left, const R &right)
void suppressParameter(const std::string &name)
This method suppresses an inherited parameter so that it isn&#39;t required or valid in the derived class...
ExecFlagEnum getDefaultExecFlagEnum()
Definition: MooseUtils.C:961
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this user object.
void threadJoin(const UserObject &) override
Must override.
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
Definition: SystemBase.C:1164
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
virtual void syncWithMainThread()
int64_t _local_ndof
Number of local dofs (including ghost dofs)
virtual void updateFEMContext()
std::unordered_map< std::string, neml2::Tensor > _neml2_dof_map
std::unordered_map< std::string, neml2::Tensor > _grad_vars
coupled variables (by gradient) requested by other objects
void invalidateInterpolations()
Invalidate the cached interpolations.
void execute() override
Execute method.
std::unordered_map< FEType, neml2::Tensor > _neml2_phi
torch::DeviceType getLibtorchDevice() const
Get the device torch is supposed to be running on.
Definition: MooseApp.h:116
MooseApp & _app
The MOOSE application this is associated with.
Definition: MooseBase.h:385
const ExecFlagType EXEC_LINEAR
Definition: Moose.C:31
static InputParameters validParams()
const neml2::Tensor & getPhi(const std::string &var_name)
Get the shape function associated with a MOOSE variable.
std::unordered_map< FEType, int64_t > _ndofe
cached information on the requested function spaces
int64_t numElem() const
Number of active elements on this rank.
Definition: NEML2Assembly.h:28
void finalize() override
Finalize.
This user object caches assembly information from MOOSE.
Definition: NEML2Assembly.h:20
std::unordered_map< FEType, std::vector< Real > > _moose_phi
std::unordered_map< FEType, neml2::Tensor > _neml2_grad_phi
SystemBase & _sys
Reference to the system object for this user object.
const neml2::Tensor & getDofMap(const std::string &var_name)
Get the local dof map associated with a MOOSE variable.
std::unordered_map< FEType, std::vector< Real > > _moose_grad_phi
const MooseVariableFE< Real > * getMOOSEVariable(const std::string &var_name) const
Helper to get the MOOSE variable and check for common restrictions.
const Elem *const & _current_elem
The current element pointer (available during execute())
std::unordered_map< FEType, const VariablePhiValue * > _phis
void meshChanged() override
Called on this object when the mesh changes.
std::unordered_map< std::string, std::vector< dof_id_type > > _moose_dof_map_global
IntRange< T > make_range(T beg, T end)
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:281
std::unordered_map< std::string, const MooseVariableFE< Real > * > _moose_vars
moose variables that have been coupled
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 invalidateFEMContext()
Invalidate the cached FEM context such as dof map, shape functions, etc.
void interpolate(InterpMethod m, T &result, const T2 &value1, const T3 &value2, const FaceInfo &fi, const bool one_is_elem)
Provides interpolation of face values for non-advection-specific purposes (although it can/will still...
Definition: MathFVUtils.h:285
auto index_range(const T &sizable)
Base class for user-specific data.
Definition: UserObject.h:19
std::vector< dof_id_type > _dof_indices
Helper vector to store local dof indices.
std::unordered_map< std::string, std::vector< int64_t > > _moose_dof_map
const ExecFlagType EXEC_INITIAL
Definition: Moose.C:30