https://mooseframework.inl.gov
Loading...
Searching...
No Matches
MortarCZMDebondedBoundaryUpdater.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 "MooseMesh.h"
12#include "DisplacedProblem.h"
14
16
19{
21 params.addClassDescription("Promotes fully-damaged CZM interface segments into new sidesets.");
22
23 params.addRequiredCoupledVar("damage_variable",
24 "CZM damage AuxVariable (0 = intact, 1 = fully broken), valid on "
25 "the secondary lower-dimensional mortar block.");
26 params.addParam<Real>(
27 "threshold", 1.0 - 1e-10, "Damage at or above which a face considered is fully separated.");
28 params.addRequiredParam<UserObjectName>(
29 "cohesive_zone_userobject", "The mortar cohesive-zone UserObject for this interface.");
30 params.addRequiredParam<BoundaryName>("primary_boundary",
31 "Primary side of the CZM mortar interface.");
32 params.addRequiredParam<BoundaryName>("secondary_boundary",
33 "Secondary side of the CZM mortar interface.");
34 params.addRequiredParam<SubdomainName>("primary_subdomain",
35 "Primary lower-dimensional mortar block.");
36 params.addRequiredParam<SubdomainName>("secondary_subdomain",
37 "Secondary lower-dimensional mortar block.");
38 params.addRequiredParam<BoundaryName>("debonded_boundary_primary",
39 "Boundary to populate with fully-damaged primary faces.");
40 params.addRequiredParam<BoundaryName>("debonded_boundary_secondary",
41 "Boundary to populate with fully-damaged secondary faces.");
42 params.set<bool>("use_displaced_mesh") = true;
43 params.registerBase("MeshModifier");
44 return params;
45}
46
48 const InputParameters & parameters)
49 : DomainUserObject(parameters),
50 _damage(coupledValue("damage_variable")),
51 _threshold(getParam<Real>("threshold")),
52 _czm_userobject_name(getParam<UserObjectName>("cohesive_zone_userobject")),
53 _primary_boundary_id(_mesh.getBoundaryID(getParam<BoundaryName>("primary_boundary"))),
54 _secondary_boundary_id(_mesh.getBoundaryID(getParam<BoundaryName>("secondary_boundary"))),
55 _primary_subdomain_id(_mesh.getSubdomainID(getParam<SubdomainName>("primary_subdomain"))),
56 _secondary_subdomain_id(_mesh.getSubdomainID(getParam<SubdomainName>("secondary_subdomain"))),
57 _displaced_problem(_fe_problem.getDisplacedProblem().get()),
58 _debonded_name_primary(getParam<BoundaryName>("debonded_boundary_primary")),
59 _debonded_name_secondary(getParam<BoundaryName>("debonded_boundary_secondary")),
60 _debonded_bid_primary(_mesh.getBoundaryID(_debonded_name_primary)),
61 _debonded_bid_secondary(_mesh.getBoundaryID(_debonded_name_secondary)),
62 _undisplaced_mesh(_fe_problem.mesh().getMesh()),
63 _displaced_mesh(_displaced_problem ? &_displaced_problem->mesh().getMesh() : nullptr),
64 _boundary_info(_undisplaced_mesh.get_boundary_info()),
65 _displaced_boundary_info(_displaced_mesh ? &_displaced_mesh->get_boundary_info() : nullptr)
66{
67 // Mortar machinery lives on the displaced mesh, so this object must execute there.
69 mooseError("MortarCZMDebondedBoundaryUpdater requires a displaced mesh.");
70
71 // Persist boundary names on both undisplaced and displaced meshes.
76
81}
82
83void
89
90void
92{
93
98 /*on_displaced=*/false,
99 /*periodic=*/false,
100 /*debug=*/false,
101 /*correct_edge_dropping=*/true,
102 /*minimum_projection_angle=*/40.0,
103 Mortar3DSubpatchPlane::GEOMETRIC_NORMAL,
104 MortarSegmentTriangulationMode::Centroid,
105 /*triangulate_triangles=*/false);
108 if (_mesh.dimension() == 2)
109 {
113 }
114 else
116
117 // Each mortar segment is the overlap of one secondary face with one primary face, so the set of
118 // segments of a secondary face names every primary face it was bonded to.
119 const auto & seg_to_info = amg.mortarSegmentMeshElemToInfo();
121 for (const auto & [secondary_id, segments] : amg.secondariesToMortarSegments())
122 {
123 auto & faces = _secondary_to_primary_faces[secondary_id];
124 for (const Elem * const mseg : segments)
125 {
126 const Elem * const primary_lowerd = libmesh_map_find(seg_to_info, mseg).primary_elem;
127 if (!primary_lowerd)
128 continue;
129
130 const Elem * const primary_ip = primary_lowerd->interior_parent();
131 mooseAssert(primary_ip, "Lower-d mortar element has no interior parent.");
132 const std::pair<dof_id_type, unsigned int> face(primary_ip->id(),
133 primary_ip->which_side_am_i(primary_lowerd));
134 if (std::find(faces.begin(), faces.end(), face) == faces.end())
135 faces.push_back(face);
136 }
137 }
138}
139
140void
145
146bool
148{
149 for (unsigned int qp = 0; qp < qRule().n_points(); ++qp)
150 if (_damage[qp] < _threshold)
151 return false;
152 return true;
153}
154
155void
161
162void
164{
165 const auto & uo = static_cast<const MortarCZMDebondedBoundaryUpdater &>(y);
166 _separated_lowerd.insert(
167 _separated_lowerd.end(), uo._separated_lowerd.begin(), uo._separated_lowerd.end());
168}
169
170void
172{
173 // Sides not yet in their sideset, gathered on every rank so that all ranks add the same sides and
174 // the boundary info of the replicated mesh stays consistent without a parallel sync. Only sides
175 // that are actually new are collected, so that the mesh is changed only on the steps where a
176 // segment is first promoted.
177
178 std::set<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> new_sides;
179 for (const auto lowerd_id : _separated_lowerd)
180 {
181 const Elem * const lowerd = _undisplaced_mesh.elem_ptr(lowerd_id);
182 const Elem * const secondary_ip = lowerd->interior_parent();
183 mooseAssert(secondary_ip, "Lower-d mortar element has no interior parent.");
184 const auto secondary_side = secondary_ip->which_side_am_i(lowerd);
185 if (!_boundary_info.has_boundary_id(secondary_ip, secondary_side, _debonded_bid_secondary))
186 new_sides.emplace(secondary_ip->id(), secondary_side, _debonded_bid_secondary);
187
188 const auto it = _secondary_to_primary_faces.find(lowerd_id);
189 if (it != _secondary_to_primary_faces.end())
190 for (const auto & [primary_id, primary_side] : it->second)
191 if (!_boundary_info.has_boundary_id(
192 _undisplaced_mesh.elem_ptr(primary_id), primary_side, _debonded_bid_primary))
193 new_sides.emplace(primary_id, primary_side, _debonded_bid_primary);
194 }
195 _communicator.set_union(new_sides);
196
197 if (new_sides.empty())
198 return;
199
200 for (const auto & [elem_id, side, bid] : new_sides)
201 {
202 _boundary_info.add_side(_undisplaced_mesh.elem_ptr(elem_id), side, bid);
203 _displaced_boundary_info->add_side(_displaced_mesh->elem_ptr(elem_id), side, bid);
204 }
205
206 // Update both undisplaced and displaced meshes
207 for (auto * const moose_mesh : {&_fe_problem.mesh(), &_displaced_problem->mesh()})
208 {
209 moose_mesh->getMesh().get_boundary_info().build_node_list_from_side_list();
210 moose_mesh->update();
211 }
212
213 // Rebuild equation systems AND the mortar functor maps. This triggers reinit of
214 // BCs and other objects so they see the newly-populated boundary sides.
216 /*intermediate_change=*/false, /*contract_mesh=*/false, /*clean_refinement_flags=*/false);
217}
const std::vector< double > y
registerMooseObject("ContactApp", MortarCZMDebondedBoundaryUpdater)
std::vector< MortarFilterIter > secondariesToMortarSegments(const Node &node) const
const std::unordered_map< const Elem *, MortarSegmentInfo > & mortarSegmentMeshElemToInfo() const
virtual MooseMesh & mesh() override
const Elem *const & _current_elem
MooseMesh & _mesh
static InputParameters validParams()
const QBase & qRule() const
virtual MooseMesh & mesh() override
virtual void meshChanged(bool intermediate_change, bool contract_mesh, bool clean_refinement_flags)
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void registerBase(const std::string &value)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
void mooseError(Args &&... args) const
virtual unsigned int dimension() const
MooseApp & _app
Dynamically adds fully-damaged CZM interface segments to sidesets at runtime.
BoundaryID _primary_boundary_id
Primary/secondary interface boundary ids (to key the mortar interface).
virtual void threadJoin(const UserObject &y) override
std::unordered_map< dof_id_type, SideList > _secondary_to_primary_faces
Secondary lower-d element id -> every primary (elem id, side) whose face overlaps it in the reference...
const VariableValue & _damage
CZM damage (0 = intact, 1 = broken), valid on the secondary lower-d block.
MortarCZMDebondedBoundaryUpdater(const InputParameters &parameters)
bool currentElemFullySeparated() const
True if damage >= threshold at every QP of the current lower-d element.
BoundaryName _debonded_name_primary
Boundaries to which damaged faces are added.
SubdomainID _primary_subdomain_id
Primary/secondary lower-dimensional subdomain ids (to key the mortar interface).
std::vector< dof_id_type > _separated_lowerd
Thread-local fully-separated secondary lower-d element ids found this execution.
const Real _threshold
Damage at/above which a face is considered fully separated.
void buildReferencePrimaryFaceMap()
Build _secondary_to_primary_faces from a mortar projection of the undisplaced interface.
DisplacedProblem * _displaced_problem
Displaced problem.
void set_union(T &data, const unsigned int root_id) const
FEProblemBase & _fe_problem
const THREAD_ID _tid
const Parallel::Communicator & _communicator
MeshBase & mesh