https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
24
25using namespace libMesh;
26
28
31{
33
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
56const neml2::Tensor &
57NEML2FEInterpolation::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
71const neml2::Tensor &
72NEML2FEInterpolation::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
86const neml2::Tensor &
87NEML2FEInterpolation::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
100const neml2::Tensor &
101NEML2FEInterpolation::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
114const neml2::Tensor &
115NEML2FEInterpolation::getDofMap(const std::string & var_name)
116{
117 return _neml2_dof_map[var_name];
118}
119
120const std::vector<dof_id_type> &
121NEML2FEInterpolation::getGlobalDofMap(const std::string & var_name)
122{
123 return _moose_dof_map_global[var_name];
124}
125
126int64_t
128{
129 return _local_ndof;
130}
131
133NEML2FEInterpolation::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
156void
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
169void
175
176void
181
182void
187
188void
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
202void
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
216void
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
240void
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
288void
290{
291 TIME_SECTION("finalize", 1, "Updating FEM context and interpolations for NEML2");
292
295
298}
299
300void
302{
303 TIME_SECTION("updateFEMContext", 2, "Updating FEM context for NEML2");
304
305 updateDofMap();
306 updatePhi();
308
309 // done
311}
312
313void
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))
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
338void
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
361void
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
385void
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
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
const ExecFlagType EXEC_INITIAL
Definition Moose.C:30
const ExecFlagType EXEC_LINEAR
Definition Moose.C:31
registerMooseObject("MooseApp", NEML2FEInterpolation)
virtual const std::set< SubdomainID > & blockIDs() const
Return the block subdomain ids for this object Note, if this is not block restricted,...
static InputParameters validParams()
const Elem *const & _current_elem
The current element pointer (available during execute())
const MooseArray< Point > & _q_point
A MultiMooseEnum object to hold "execute_on" flags.
T & getUserObject(const std::string &name, unsigned int tid=0) const
Get the user object by its name.
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...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void suppressParameter(const std::string &name)
This method suppresses an inherited parameter so that it isn't required or valid in the derived class...
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.
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
torch::DeviceType getLibtorchDevice() const
Get the device torch is supposed to be running on.
Definition MooseApp.h:117
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
MooseApp & _app
The MOOSE application this is associated with.
Definition MooseBase.h:375
Class for stuff related to variables.
This user object caches assembly information from MOOSE.
int64_t numQP() const
Number of quadrature points per element.
int64_t numElem() const
Number of active elements on this rank.
This user object serves as the "interface" for interpolating MOOSE variable values and gradients from...
static InputParameters validParams()
const neml2::Tensor & getPhiGradient(const std::string &var_name)
Get the shape function gradient associated with a MOOSE variable.
const neml2::Tensor & getGradient(const std::string &var_name)
Get the variable gradient of a MOOSE nonlinear variable converted to a NEML2 tensor.
const neml2::Tensor & getDofMap(const std::string &var_name)
Get the local dof map associated with a MOOSE variable.
std::unordered_map< std::string, neml2::Tensor > _vars
coupled variables (by value) requested by other objects
const NEML2Assembly & _neml2_assembly
Assembly.
virtual void syncWithMainThread()
std::unordered_map< std::string, neml2::Tensor > _neml2_dof_map
void invalidateFEMContext()
Invalidate the cached FEM context such as dof map, shape functions, etc.
void invalidateInterpolations()
Invalidate the cached interpolations.
bool _interp_up_to_date
Whether the current interpolations are up to date.
NEML2FEInterpolation(const InputParameters &parameters)
std::unordered_map< std::string, std::vector< int64_t > > _moose_dof_map
const neml2::Tensor & getValue(const std::string &var_name)
Get the variable value of a MOOSE nonlinear variable converted to a NEML2 tensor.
std::vector< dof_id_type > _dof_indices
Helper vector to store local dof indices.
std::unordered_map< FEType, std::vector< Real > > _moose_phi
std::unordered_map< FEType, const VariablePhiGradient * > _grad_phis
std::unordered_map< FEType, neml2::Tensor > _neml2_phi
const MooseVariableFE< Real > * getMOOSEVariable(const std::string &var_name) const
Helper to get the MOOSE variable and check for common restrictions.
void meshChanged() override
Called on this object when the mesh changes.
void execute() override
Execute method.
void threadJoin(const UserObject &) override
Must override.
int64_t _local_ndof
Number of local dofs (including ghost dofs)
std::unordered_map< FEType, int64_t > _ndofe
cached information on the requested function spaces
std::unordered_map< FEType, neml2::Tensor > _neml2_grad_phi
std::unordered_map< std::string, neml2::Tensor > _grad_vars
coupled variables (by gradient) requested by other objects
bool _fem_context_up_to_date
Whether the current FEM context is up to date.
virtual void updateInterpolations()
void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job.
std::unordered_map< std::string, const MooseVariableFE< Real > * > _moose_vars
moose variables that have been coupled
std::unordered_map< std::string, std::vector< dof_id_type > > _moose_dof_map_global
const PetscVector< Real > * _petsc_solution
PETSc solution vector.
std::unordered_map< FEType, const VariablePhiValue * > _phis
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)
const neml2::Tensor & getPhi(const std::string &var_name)
Get the shape function associated with a MOOSE variable.
void finalize() override
Finalize.
void initialize() override
Called before execute() is ever called so that data can be cleared.
std::unordered_map< FEType, std::vector< Real > > _moose_grad_phi
virtual const NumericVector< Number > *const & currentSolution() const =0
The solution vector that is currently being operated on.
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this user object.
const THREAD_ID _tid
Thread ID of this postprocessor.
SystemBase & _sys
Reference to the system object for this user object.
Base class for user-specific data.
Definition UserObject.h:20
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
ExecFlagEnum getDefaultExecFlagEnum()
Definition MooseUtils.C:972
@ VAR_FIELD_STANDARD
Definition MooseTypes.h:777
@ VAR_SOLVER
Definition MooseTypes.h:770
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
auto index_range(const T &sizable)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)