https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ShiftedCohesiveZoneAction.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#include "ActionWarehouse.h"
12#include "AddMaterialAction.h"
13#include "Factory.h"
14#include "FEProblem.h"
15#include "Conversion.h"
16#include "NEML2Action.h"
18
19#include <algorithm>
20#include <set>
21
22namespace
23{
24bool
25getInheritedVolumetricLockingCorrection(ActionWarehouse & awh)
26{
27 const auto quasi_static_physics = awh.getActions<QuasiStaticSolidMechanicsPhysicsBase>();
28 if (quasi_static_physics.empty())
29 return false;
30
31 for (const auto * const physics : quasi_static_physics)
32 {
33 const bool has_locking_param = physics->isParamSetByUser("volumetric_locking_correction");
34 if (!has_locking_param)
35 continue;
36
37 if (physics->getParam<bool>("volumetric_locking_correction"))
38 return true;
39 }
40
41 return false;
42}
43
44bool
45hasOverlap(const std::vector<BoundaryName> & lhs, const std::vector<BoundaryName> & rhs)
46{
47 for (const auto & left : lhs)
48 if (std::find(rhs.begin(), rhs.end(), left) != rhs.end())
49 return true;
50
51 return false;
52}
53
54bool
55producesProperty(const NEML2Action & action, const MaterialPropertyName & property_name)
56{
57 const auto & outputs = action.getParam<std::vector<std::string>>("moose_outputs");
58 if (std::find(outputs.begin(), outputs.end(), property_name) != outputs.end())
59 return true;
60
61 const auto & derivatives = action.getParam<std::vector<std::string>>("moose_derivatives");
62 return std::find(derivatives.begin(), derivatives.end(), property_name) != derivatives.end();
63}
64
65MaterialPropertyName
66resolveTransferredProperty(ActionWarehouse & awh, const MaterialPropertyName & property_name)
67{
68 for (const auto * const material_action : awh.getActions<AddMaterialAction>())
69 {
70 if (material_action->getMooseObjectType() != "ComputeLagrangianCauchyCustomStress")
71 continue;
72
73 const auto & params = material_action->getObjectParams();
74 if (property_name == "pk1_stress" || property_name == "stress")
75 return params.get<MaterialPropertyName>("custom_cauchy_stress");
76
77 if (property_name == "pk1_jacobian" || property_name == "Jacobian_mult")
78 return params.get<MaterialPropertyName>("custom_cauchy_jacobian");
79 }
80
81 return property_name;
82}
83}
84
85// Register the action for various tasks in the MOOSE execution flow
86registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_interface_kernel");
87registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_material");
88registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_function");
89registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_user_object");
90registerMooseAction("ShiftedBoundaryMethodApp",
92 "add_master_action_material");
93registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_aux_variable");
94registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_aux_kernel");
95registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_kernel");
96registerMooseAction("ShiftedBoundaryMethodApp",
98 "validate_coordinate_systems");
99
102{
104 params.addClassDescription("Action to create Shifted Cohesive Zone Method (SCZM) objects "
105 "for non-interface-fitted meshes.");
106
107 params.addParam<UserObjectName>(
108 "sbm_distance_uo",
109 "UserObject that provides signed distance and normal vector calculations.");
110 params.addParam<bool>(
111 "generate_sbm_distance",
112 false,
113 "Automatically create surface mesh builders, distance functions, and the boundary distance "
114 "user object from the supplied boundaries.");
115 params.addParam<std::vector<MeshGeneratorName>>(
116 "surface_meshes",
117 "Names of saved surface meshes corresponding one-to-one with boundary. If omitted, the "
118 "boundary names are used.");
119 params.addParam<bool>("check_surface_watertightness",
120 false,
121 "Whether generated surface mesh builders check mesh watertightness.");
122 params.addParam<MeshGeneratorName>(
123 "complete_interface_mesh",
124 "Saved surface mesh containing the complete boundary of each subdomain. Setting this "
125 "parameter automatically creates one interface manager and distance user object.");
126 params.addParam<std::vector<std::vector<SubdomainID>>>(
127 "interface_subdomain_pairs",
128 {},
129 "Ordered subdomain pair for each boundary when it cannot be inferred from boundary names.");
130 params.addParam<Real>("interface_matching_tolerance",
132 "Relative face-matching tolerance for the single-mesh interface manager.");
133
134 params.addParam<bool>("no_shifted", false, "Disable shifted terms.");
135 params.addParam<bool>("directional_correction", true, "Add the directional correction terms.");
136 params.addParam<MaterialPropertyName>(
137 "stress", "stress", "Name of the stress tensor material property for small strain SCZM.");
138 params.addParam<MaterialPropertyName>("tangent",
139 "Jacobian_mult",
140 "Name of the material Jacobian tensor property for small "
141 "strain SCZM.");
142 params.addParam<MooseEnum>(
143 "tangent_definition",
144 MooseEnum("auto stress_wrt_strain pk1_wrt_deformation_gradient", "auto"),
145 "Mathematical definition of the tangent material property. 'auto' recognizes the standard "
146 "Jacobian_mult property as d(stress)/d(strain) and pk1_jacobian as d(PK1)/d(F). Select "
147 "'stress_wrt_strain' or 'pk1_wrt_deformation_gradient' explicitly for a custom tangent "
148 "property; use the latter only when the property is known to be d(PK1)/d(F).");
149 params.addParam<bool>(
150 "volumetric_locking_correction",
151 false,
152 "Whether to apply volumetric locking correction to the directional correction Jacobian "
153 "term in SCZM. If not specified, this value is automatically inherited from the solid "
154 "mechanics physics block.");
155
156 return params;
157}
158
160 : CohesiveZoneAction(params)
161{
162 const bool generate_distance = getParam<bool>("generate_sbm_distance");
163 const bool has_distance_uo = isParamSetByUser("sbm_distance_uo");
164 const bool has_complete_interface_mesh = isParamSetByUser("complete_interface_mesh");
165 if (static_cast<unsigned int>(generate_distance) + static_cast<unsigned int>(has_distance_uo) +
166 static_cast<unsigned int>(has_complete_interface_mesh) >
167 1)
168 paramError("sbm_distance_uo",
169 "Specify only one of 'sbm_distance_uo', 'generate_sbm_distance', and "
170 "'complete_interface_mesh'.");
171
172 if (!generate_distance && isParamSetByUser("surface_meshes"))
173 paramError("surface_meshes", "This parameter is only valid with 'generate_sbm_distance=true'.");
174 if (!generate_distance && isParamSetByUser("check_surface_watertightness"))
175 paramError("check_surface_watertightness",
176 "This parameter is only valid with 'generate_sbm_distance=true'.");
177 if (!has_complete_interface_mesh && isParamSetByUser("interface_matching_tolerance"))
178 paramError("interface_matching_tolerance",
179 "This parameter is only valid with 'complete_interface_mesh'.");
180
181 if (generate_distance)
182 {
183 const auto surface_meshes = surfaceMeshNames();
184 if (surface_meshes.size() != _boundary.size())
185 paramError("surface_meshes", "The number of surface meshes must match the boundaries.");
186 if (std::set<MeshGeneratorName>(surface_meshes.begin(), surface_meshes.end()).size() !=
187 surface_meshes.size())
188 paramError("surface_meshes", "Each saved surface mesh may only be used once.");
189 }
190
191 if (!has_complete_interface_mesh && isParamSetByUser("interface_subdomain_pairs"))
192 paramError("interface_subdomain_pairs",
193 "This parameter is only valid with 'complete_interface_mesh'.");
194 if (has_complete_interface_mesh && isParamSetByUser("interface_subdomain_pairs") &&
195 getParam<std::vector<std::vector<SubdomainID>>>("interface_subdomain_pairs").size() !=
196 _boundary.size())
197 paramError("interface_subdomain_pairs", "Provide exactly one subdomain pair per boundary.");
198
199 if (_use_AD && isParamSetByUser("tangent"))
200 paramError("tangent", "This parameter applies only to the non-AD SCZM interface kernel.");
201 if (_use_AD && isParamSetByUser("tangent_definition"))
202 paramError("tangent_definition",
203 "This parameter applies only to the non-AD SCZM interface kernel. The AD kernel "
204 "differentiates the residual automatically.");
205
206 switch (_strain)
207 {
208 case Strain::Small:
209 {
211 _use_AD ? "ADSCZMInterfaceKernelSmallStrain" : "SCZMInterfaceKernelSmallStrain";
212 // For SCZM, we just need to evaulate the displacement jump correctly on the true interface.
213 // After that, the local and global traction calculations remain the same as standard CZM.
214 _disp_jump_provider_name = _use_AD ? "ADSCZMComputeDisplacementJumpSmallStrain"
215 : "SCZMComputeDisplacementJumpSmallStrain";
217 _use_AD ? "ADCZMComputeGlobalTractionSmallStrain" : "CZMComputeGlobalTractionSmallStrain";
218 break;
219 }
220 case Strain::Finite:
221 paramError("strain", "Shifted cohesive zone models currently support only small strain.");
222 default:
224 "ShiftedCohesiveZoneAction Error: Invalid kinematic parameter. Allowed values are: "
225 "SmallStrain or TotalLagrangian");
226 }
227}
228
229void
231{
232 // Consistency check: ndisp must match mesh dimension
233 if (_ndisp != _mesh->dimension())
234 paramError("displacements", "Number of displacements must match problem dimension.");
235
236 if (_current_task == "add_interface_kernel" && getParam<bool>("directional_correction"))
237 {
238 std::vector<MaterialPropertyName> stress_names;
239 std::vector<MaterialPropertyName> tangent_names;
240
241 if (isParamSetByUser("stress"))
242 stress_names.push_back(getParam<MaterialPropertyName>("stress"));
243 else if (_strain == Strain::Finite)
244 stress_names.push_back("pk1_stress");
245 else
246 {
247 // Mirror SCZMInterfaceKernelSmallStrain, which prefers stress/Jacobian_mult but falls back
248 // to pk1_stress/pk1_jacobian for Lagrangian-compatible small-strain materials.
249 stress_names.push_back("stress");
250 stress_names.push_back("pk1_stress");
251 }
252
253 if (isParamSetByUser("tangent"))
254 tangent_names.push_back(getParam<MaterialPropertyName>("tangent"));
255 else if (_strain == Strain::Finite)
256 tangent_names.push_back("pk1_jacobian");
257 else
258 {
259 tangent_names.push_back("Jacobian_mult");
260 tangent_names.push_back("pk1_jacobian");
261 }
262
263 std::vector<MaterialPropertyName> validated_neml2_properties;
264
265 const auto validate_neml2_interface =
266 [this, &validated_neml2_properties](const MaterialPropertyName & property_name)
267 {
268 const auto neml2_property_name = resolveTransferredProperty(_awh, property_name);
269
270 if (std::find(validated_neml2_properties.begin(),
271 validated_neml2_properties.end(),
272 neml2_property_name) != validated_neml2_properties.end())
273 return;
274
275 validated_neml2_properties.push_back(neml2_property_name);
276
277 for (const auto * const neml2_action : _awh.getActions<NEML2Action>())
278 {
279 if (!producesProperty(*neml2_action, neml2_property_name))
280 continue;
281
282 const bool interface_set = neml2_action->isParamSetByUser("interface");
283 const auto & interfaces = neml2_action->getParam<std::vector<BoundaryName>>("interface");
284
285 if (!interface_set || interfaces.empty())
286 neml2_action->paramError(
287 "interface",
288 "This NEML2 block provides the material property '",
289 neml2_property_name,
290 "' used by [Physics/SolidMechanics/ShiftedCohesiveZone] with "
291 "directional_correction = true. Set 'interface' to the SCZM boundary (for example, "
292 "interface = ${boundary}) or set "
293 "'directional_correction = false' in the ShiftedCohesiveZone block.");
294
295 if (!hasOverlap(_boundary, interfaces))
296 neml2_action->paramError(
297 "interface",
298 "This NEML2 block provides the material property '",
299 neml2_property_name,
300 "' used by "
301 "[Physics/SolidMechanics/ShiftedCohesiveZone] with directional_correction = true, "
302 "but "
303 "its 'interface' setting does not overlap the SCZM boundary. Make the NEML2 "
304 "'interface' include the SCZM boundary or set 'directional_correction = false'.");
305
306 return;
307 }
308 };
309
310 for (const auto & stress_name : stress_names)
311 validate_neml2_interface(stress_name);
312
313 for (const auto & tangent_name : tangent_names)
314 validate_neml2_interface(tangent_name);
315 }
316
317 // Call the base utility for parameter validation across multiple blocks
319
320 // Task Routing
321 if (_current_task == "add_function" && getParam<bool>("generate_sbm_distance"))
323 else if (_current_task == "add_user_object" &&
324 (getParam<bool>("generate_sbm_distance") || isParamSetByUser("complete_interface_mesh")))
326 else if (_current_task == "add_interface_kernel")
328 else if (_current_task == "add_master_action_material")
330
331 // optional, add required outputs
333}
334
335std::vector<MeshGeneratorName>
337{
338 if (isParamValid("surface_meshes"))
339 return getParam<std::vector<MeshGeneratorName>>("surface_meshes");
340
341 std::vector<MeshGeneratorName> surface_meshes;
342 surface_meshes.reserve(_boundary.size());
343 for (const auto & boundary : _boundary)
344 surface_meshes.push_back(boundary);
345 return surface_meshes;
346}
347
348UserObjectName
349ShiftedCohesiveZoneAction::surfaceMeshBuilderName(const BoundaryName & boundary) const
350{
351 return name() + "_" + boundary + "_builder";
352}
353
354FunctionName
356{
357 return name() + "_dist_" + boundary;
358}
359
360UserObjectName
362{
363 return name() + "_sbm_distance";
364}
365
366UserObjectName
368{
369 return name() + "_interface_manager";
370}
371
372void
374{
375 for (const auto & boundary : _boundary)
376 {
377 auto params = _factory.getValidParams("UnsignedDistanceToSurfaceMesh");
378 params.set<UserObjectName>("builder") = surfaceMeshBuilderName(boundary);
379 _problem->addFunction(
380 "UnsignedDistanceToSurfaceMesh", surfaceDistanceFunctionName(boundary), params);
381 }
382}
383
384void
386{
387 auto distance_params = _factory.getValidParams("BoundaryShortestDistanceToSurface");
388 distance_params.set<std::vector<BoundaryName>>("boundary") = _boundary;
389 distance_params.set<int>("execution_order_group") = 0;
390 distance_params.set<ExecFlagEnum>("execute_on") = EXEC_INITIAL;
391
392 if (isParamSetByUser("complete_interface_mesh"))
393 {
394 auto manager_params = _factory.getValidParams("SBMInterfaceManager");
395 manager_params.set<MeshGeneratorName>("complete_interface_mesh") =
396 getParam<MeshGeneratorName>("complete_interface_mesh");
397 manager_params.set<Real>("tolerance") = getParam<Real>("interface_matching_tolerance");
398 _problem->addUserObject("SBMInterfaceManager", interfaceManagerName(), manager_params);
399 distance_params.set<UserObjectName>("manager") = interfaceManagerName();
400 if (isParamSetByUser("interface_subdomain_pairs"))
401 distance_params.set<std::vector<std::vector<SubdomainID>>>("interface_subdomain_pairs") =
402 getParam<std::vector<std::vector<SubdomainID>>>("interface_subdomain_pairs");
403 }
404 else
405 {
406 const auto surface_meshes = surfaceMeshNames();
407 std::vector<FunctionName> distance_functions;
408 distance_functions.reserve(_boundary.size());
409 for (const auto i : index_range(_boundary))
410 {
411 auto params = _factory.getValidParams("SBMSurfaceMeshBuilder");
412 params.set<MeshGeneratorName>("interface_mesh") = surface_meshes[i];
413 params.set<bool>("check_watertightness") = getParam<bool>("check_surface_watertightness");
414 _problem->addUserObject(
415 "SBMSurfaceMeshBuilder", surfaceMeshBuilderName(_boundary[i]), params);
416 distance_functions.push_back(surfaceDistanceFunctionName(_boundary[i]));
417 }
418 distance_params.set<std::vector<FunctionName>>("surfaces") = distance_functions;
419 }
420
421 _problem->addUserObject(
422 "BoundaryShortestDistanceToSurface", sbmDistanceUserObjectName(), distance_params);
423}
424
425void
427{
428 if (getParam<bool>("generate_sbm_distance") || isParamSetByUser("complete_interface_mesh"))
429 params.set<UserObjectName>("sbm_distance_uo") = sbmDistanceUserObjectName();
430 else if (isParamSetByUser("sbm_distance_uo"))
431 params.set<UserObjectName>("sbm_distance_uo") = getParam<UserObjectName>("sbm_distance_uo");
432 if (isParamSetByUser("no_shifted"))
433 params.set<bool>("no_shifted") = getParam<bool>("no_shifted");
434 if (isParamSetByUser("directional_correction"))
435 params.set<bool>("directional_correction") = getParam<bool>("directional_correction");
436 if (isParamSetByUser("stress"))
437 params.set<MaterialPropertyName>("stress") = getParam<MaterialPropertyName>("stress");
438 if (isParamSetByUser("tangent"))
439 params.set<MaterialPropertyName>("tangent") = getParam<MaterialPropertyName>("tangent");
440 if (isParamSetByUser("tangent_definition") && params.isParamValid("tangent_definition"))
441 params.set<MooseEnum>("tangent_definition") = getParam<MooseEnum>("tangent_definition");
442
443 if (params.isParamValid("volumetric_locking_correction"))
444 {
445 const bool inherited_correction = getInheritedVolumetricLockingCorrection(_awh);
446 if (isParamSetByUser("volumetric_locking_correction"))
447 {
448 params.set<bool>("volumetric_locking_correction") =
449 getParam<bool>("volumetric_locking_correction");
450 if (getParam<bool>("volumetric_locking_correction") != inherited_correction)
452 "ShiftedCohesiveZoneAction: 'volumetric_locking_correction' value in "
453 "ShiftedCohesiveZone block is different from that in the physics block(s). Make sure "
454 "this is intended.");
455 }
456 else
457 params.set<bool>("volumetric_locking_correction") = inherited_correction;
458 }
459}
460
461void
463 InputParameters & params) const
464{
465 if (getParam<bool>("generate_sbm_distance") || isParamSetByUser("complete_interface_mesh"))
466 params.set<UserObjectName>("sbm_distance_uo") = sbmDistanceUserObjectName();
467 else if (isParamSetByUser("sbm_distance_uo"))
468 params.set<UserObjectName>("sbm_distance_uo") = getParam<UserObjectName>("sbm_distance_uo");
469 if (isParamSetByUser("no_shifted"))
470 params.set<bool>("no_shifted") = getParam<bool>("no_shifted");
471}
const ExecFlagType EXEC_INITIAL
registerMooseAction("ShiftedBoundaryMethodApp", ShiftedCohesiveZoneAction, "add_interface_kernel")
std::vector< const T * > getActions()
std::shared_ptr< MooseMesh > & _mesh
std::shared_ptr< FEProblemBase > & _problem
const std::string & _current_task
ActionWarehouse & _awh
const unsigned int _ndisp
number of displacement components
const bool _use_AD
whether to use AD kernels and materials
std::string _equilibrium_traction_calculator_name
void addRequiredCZMInterfaceMaterials()
adds the required interface materials based on the selected strain formulation
void chekMultipleActionParameters()
method checking multiple CohesiveZoneAction block inputs
static InputParameters validParams()
std::string _disp_jump_provider_name
enum CohesiveZoneAction::Strain _strain
void addRequiredCZMInterfaceKernels()
adds the required interfacekernels based on the selected strain formulation
std::string _czm_kernel_name
kernel's and materials's names
const std::vector< BoundaryName > _boundary
Base name of the material system.
InputParameters getValidParams(const std::string &name) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
bool isParamValid(const std::string &name) const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
bool isParamSetByUser(const std::string &name) const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
const T & getParam(const std::string &name) const
bool isParamValid(const std::string &name) const
Factory & _factory
Action class for the Shifted Cohesive Zone Method (SCZM).
void addSBMDistanceUserObjects()
Adds the generated surface geometry and aggregate boundary distance user objects.
FunctionName surfaceDistanceFunctionName(const BoundaryName &boundary) const
Returns the generated distance function name for a boundary.
static InputParameters validParams()
UserObjectName interfaceManagerName() const
Returns the generated single-mesh interface manager name.
virtual void act() override
Main execution point for the Action.
void customizeCZMInterfaceKernelParameters(InputParameters &params) const override
Sets SCZM-specific parameters on each generated interface kernel.
UserObjectName surfaceMeshBuilderName(const BoundaryName &boundary) const
Returns the generated builder name for a boundary.
UserObjectName sbmDistanceUserObjectName() const
Returns the generated aggregate distance user object name.
void customizeCZMDisplacementJumpMaterialParameters(InputParameters &params) const override
Sets SCZM-specific parameters on the generated displacement jump material.
ShiftedCohesiveZoneAction(const InputParameters &params)
void addSBMDistanceFunctions()
Adds the surface distance functions used by the generated distance user object.
std::vector< MeshGeneratorName > surfaceMeshNames() const
Returns the saved surface mesh names, defaulting to the boundary names.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
if(subdm)
static constexpr Real TOLERANCE