Line data Source code
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 : // MOOSE includes
11 : #include "ComponentJunction.h"
12 : #include "MooseUtils.h"
13 :
14 : registerMooseAction("MooseApp", ComponentJunction, "add_mesh_generator");
15 : // ComponentJunction is an example of ComponentPhysicsInterface
16 : registerMooseAction("MooseApp", ComponentJunction, "init_component_physics");
17 : // ComponentJunction is an example of ComponentMaterialPropertyInterface
18 : registerMooseAction("MooseApp", ComponentJunction, "add_material");
19 : // ComponentJunction is an example of ComponentInitialConditionInterface
20 : registerMooseAction("MooseApp", ComponentJunction, "check_integrity");
21 : registerActionComponent("MooseApp", ComponentJunction);
22 :
23 : InputParameters
24 150 : ComponentJunction::validParams()
25 : {
26 150 : InputParameters params = ActionComponent::validParams();
27 150 : params += ComponentPhysicsInterface::validParams();
28 150 : params += ComponentMaterialPropertyInterface::validParams();
29 150 : params += ComponentInitialConditionInterface::validParams();
30 150 : params += ComponentBoundaryConditionInterface::validParams();
31 :
32 300 : params.addClassDescription("Component to join two other components.");
33 :
34 600 : params.addRequiredParam<ComponentName>("first_component", "First component to join");
35 600 : params.addRequiredParam<BoundaryName>("first_boundary", "First boundary to connect to.");
36 600 : params.addRequiredParam<ComponentName>("second_component", "Second component to join");
37 600 : params.addRequiredParam<BoundaryName>("second_boundary", "Second boundary to connect to.");
38 :
39 600 : MooseEnum junction_type("stitch_meshes extrude_boundary", "extrude_boundary");
40 600 : params.addParam<MooseEnum>("junction_method", junction_type, "How to join the two components");
41 :
42 : /* Stitching parameters */
43 450 : params.addParam<bool>("enforce_all_nodes_match_on_boundaries",
44 300 : true,
45 : "Only stitch if all nodes match on the boundary. Defaults to true because "
46 : "there is a search algorithm that forces nodes to match assuming there are "
47 : "equal number of nodes on each target boundary.");
48 :
49 : /* Meshing the gap parameters */
50 : // Parameters for the region between meshes
51 600 : params.addParam<unsigned int>("n_elem_normal",
52 : "Number of elements in the normal direction of the junction");
53 600 : params.addParam<SubdomainName>("block", "Block name for the junction, if a block is created.");
54 :
55 : // Parameters for changing radius -- final radius will be calculated under the hood
56 600 : MooseEnum radial_growth_methods("LINEAR CUBIC", "CUBIC");
57 600 : params.addParam<MooseEnum>("radial_growth_method",
58 : radial_growth_methods,
59 : "Functional form to change radius while extruding along curve.");
60 600 : params.addParam<Real>("start_radial_growth_rate", 0, "Starting rate of radial expansion.");
61 600 : params.addParam<Real>("end_radial_growth_rate", 0, "Ending rate of radial expansion.");
62 :
63 : // Parameters for the 1D spline joining the two components (serving as an extrusion guide for
64 : // 2,3D)
65 900 : params.addRangeCheckedParam<libMesh::Real>("sharpness",
66 : "sharpness>0 & sharpness<=1",
67 : "Sharpness of curve bend. See BSplineCurveGenerator "
68 : "for explanation of the meaning given to sharpness");
69 450 : params.addParam<unsigned int>(
70 : "num_cps",
71 300 : 6,
72 : "Number of control points used to draw the curve. Miniumum of degree+1 points are required.");
73 600 : MooseEnum edge_elem_type("EDGE2 EDGE3 EDGE4", "EDGE2");
74 600 : params.addParam<MooseEnum>(
75 : "edge_element_type", edge_elem_type, "Type of the EDGE elements to be generated.");
76 :
77 600 : params.addParamNamesToGroup("sharpness num_cps edge_element_type", "1D mesh junction");
78 450 : params.addParamNamesToGroup(
79 : "radial_growth_method start_radial_growth_rate end_radial_growth_rate",
80 : "Radial expansion in 2D and 3D junction");
81 :
82 300 : return params;
83 150 : }
84 :
85 126 : ComponentJunction::ComponentJunction(const InputParameters & params)
86 : : ActionComponent(params),
87 : ComponentPhysicsInterface(params),
88 : ComponentMaterialPropertyInterface(params),
89 : ComponentInitialConditionInterface(params),
90 : ComponentBoundaryConditionInterface(params),
91 126 : _junction_method(getParam<MooseEnum>("junction_method")),
92 378 : _enforce_all_nodes_match_on_boundaries(getParam<bool>("enforce_all_nodes_match_on_boundaries"))
93 : {
94 252 : addRequiredTask("add_mesh_generator");
95 :
96 : // Check parameters
97 126 : if (_junction_method != "extrude_boundary")
98 490 : errorDependentParameter("junction_method",
99 : "extrude_boundary",
100 : {"n_elem_normal",
101 : "block",
102 : "radial_growth_method",
103 : "start_radial_growth_rate",
104 : "end_radial_growth_rate",
105 : "sharpness",
106 : "num_cps",
107 : "edge_element_type"});
108 : else
109 : {
110 168 : if (!isParamValid("n_elem_normal"))
111 0 : paramError("n_elem_normal", "Should be specified if junction_method = 'extrude_boundary'");
112 : }
113 : // The 1D and 2D meshing parameters will be re-checked later, once we know the dimension
114 126 : }
115 :
116 : void
117 126 : ComponentJunction::addMeshGenerators()
118 : {
119 : auto & first_component =
120 252 : _awh.getAction<ActionComponent>(getParam<ComponentName>("first_component"));
121 : auto & second_component =
122 252 : _awh.getAction<ActionComponent>(getParam<ComponentName>("second_component"));
123 252 : const auto first_boundary = getParam<BoundaryName>("first_boundary");
124 252 : const auto second_boundary = getParam<BoundaryName>("second_boundary");
125 :
126 : // Get the dimension of the components
127 126 : const auto dimension_first = first_component.dimension();
128 126 : const auto dimension_second = second_component.dimension();
129 :
130 126 : if (dimension_first == 0 || dimension_second == 0)
131 0 : mooseError("Connecting 0 dimension meshes not implemented!");
132 :
133 : // Perform junction
134 126 : if (_junction_method == "stitch_meshes")
135 : {
136 : // Fairly easy to stitch this
137 70 : if (dimension_first == dimension_second)
138 : {
139 70 : if (first_component.getCurrentTopLevelMeshGeneratorName() !=
140 140 : second_component.getCurrentTopLevelMeshGeneratorName())
141 : { // Stitch the two meshes
142 120 : InputParameters params = _factory.getValidParams("StitchMeshGenerator");
143 120 : params.set<std::vector<MeshGeneratorName>>("inputs") = {
144 : first_component.getCurrentTopLevelMeshGeneratorName(),
145 240 : second_component.getCurrentTopLevelMeshGeneratorName()};
146 120 : params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") = {
147 300 : {first_boundary, second_boundary}};
148 60 : params.set<bool>("verbose_stitching") = _verbose;
149 60 : params.set<bool>("enforce_all_nodes_match_on_boundaries") =
150 60 : _enforce_all_nodes_match_on_boundaries;
151 120 : _app.getMeshGeneratorSystem().addMeshGenerator(
152 120 : "StitchMeshGenerator", name() + "_base", params);
153 60 : _mg_names.push_back(name() + "_base");
154 60 : }
155 : else
156 : {
157 : // Handles case of needing to close a mesh. Using StitchBoundaryMeshGenerator prevents
158 : // issues with element overlap.
159 20 : InputParameters params = _factory.getValidParams("StitchBoundaryMeshGenerator");
160 20 : params.set<MeshGeneratorName>("input") =
161 30 : first_component.getCurrentTopLevelMeshGeneratorName();
162 20 : params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") = {
163 50 : {first_boundary, second_boundary}};
164 10 : params.set<bool>("show_info") = _verbose;
165 20 : _app.getMeshGeneratorSystem().addMeshGenerator(
166 20 : "StitchBoundaryMeshGenerator", name() + "_close", params);
167 10 : _mg_names.push_back(name() + "_close");
168 10 : }
169 : }
170 : else
171 0 : mooseError("Stiching meshes of different dimensions is not implemented");
172 : }
173 :
174 56 : else if (_junction_method == "extrude_boundary")
175 : {
176 : //
177 : // This junction method is set to use a B-Spline to draw a 1D curve between, then extrude along
178 : // that spline
179 : //
180 :
181 : // find start and end directions (may need to take the negative of the end direction).
182 : // obey user parameters if set
183 : // TODO: modify the spline curve generator to figure out the direction so we can support other
184 : // component types
185 : // TODO: this really should be the surface direction, not the component direction. Maybe all
186 : // this should be deleted and replaced by logic in the Spline curve generator instead.
187 112 : auto get_direction = [this](const auto & component, const auto & param_name) -> RealVectorValue
188 : {
189 112 : const auto & cname = component.name();
190 112 : if (dynamic_cast<const ComponentMeshTransformHelper *>(
191 112 : &_awh.getAction<ActionComponent>(cname)))
192 112 : return _awh.getAction<ComponentMeshTransformHelper>(cname).direction();
193 0 : else if (component.isParamValid("direction"))
194 0 : return component.template getParam<RealVectorValue>("direction");
195 : else
196 0 : paramError(param_name,
197 : "Only components inheriting from 'ComponentMeshTransformerHelper' or with a "
198 : "'direction' parameter are supported at this time");
199 56 : };
200 :
201 56 : RealVectorValue start_direction = get_direction(first_component, "first_component");
202 56 : RealVectorValue end_direction = get_direction(second_component, "second_component");
203 :
204 168 : InputParameters bspline_params = _factory.getValidParams("BSplineCurveGenerator");
205 56 : bspline_params.set<RealVectorValue>("start_direction") = start_direction;
206 112 : bspline_params.set<RealVectorValue>("end_direction") = -end_direction;
207 224 : bspline_params.set<unsigned int>("num_elements") = getParam<unsigned int>("n_elem_normal");
208 168 : if (isParamValid("sharpness"))
209 0 : bspline_params.set<Real>("sharpness") = getParam<Real>("sharpness");
210 224 : bspline_params.set<unsigned int>("num_cps") = getParam<unsigned int>("num_cps");
211 112 : bspline_params.set<MeshGeneratorName>("start_mesh") =
212 112 : first_component.meshGeneratorNames().back();
213 112 : bspline_params.set<MeshGeneratorName>("end_mesh") =
214 112 : second_component.meshGeneratorNames().back();
215 112 : bspline_params.set<BoundaryName>("boundary_providing_start_point") =
216 224 : getParam<BoundaryName>("first_boundary");
217 112 : bspline_params.set<BoundaryName>("boundary_providing_end_point") =
218 224 : getParam<BoundaryName>("second_boundary");
219 :
220 56 : if (dimension_first == 1)
221 : {
222 120 : if (isParamValid("block"))
223 40 : bspline_params.set<SubdomainName>("new_subdomain_name") = getParam<SubdomainName>("block");
224 80 : bspline_params.set<std::vector<BoundaryName>>("edge_nodesets") = {
225 240 : name() + "_bspline_start_node", name() + "_bspline_end_node"};
226 120 : bspline_params.set<bool>("output") = _verbose;
227 : }
228 :
229 112 : _app.getMeshGeneratorSystem().addMeshGenerator(
230 112 : "BSplineCurveGenerator", name() + "_curve", bspline_params);
231 56 : _mg_names.push_back(name() + "_curve");
232 :
233 : // Extrude boundary from first component
234 56 : if (dimension_first > 1)
235 : {
236 : // create lower dimension mesh on the boundary
237 32 : InputParameters ld_source_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
238 32 : ld_source_params.set<MeshGeneratorName>("input") =
239 32 : first_component.meshGeneratorNames().back();
240 32 : ld_source_params.set<std::vector<BoundaryName>>("sidesets") =
241 80 : std::vector{getParam<BoundaryName>("first_boundary")};
242 32 : ld_source_params.set<SubdomainName>("new_block_name") =
243 48 : (SubdomainName)(name() + "_LowerDBlockSource");
244 32 : _app.getMeshGeneratorSystem().addMeshGenerator(
245 32 : "LowerDBlockFromSidesetGenerator", name() + "_lowerDGenerationSource", ld_source_params);
246 16 : _mg_names.push_back(name() + "_lowerDGenerationSource");
247 :
248 32 : InputParameters _bmc_source_params = _factory.getValidParams("BlockToMeshConverterGenerator");
249 48 : _bmc_source_params.set<MeshGeneratorName>("input") = name() + "_lowerDGenerationSource";
250 32 : _bmc_source_params.set<std::vector<SubdomainName>>("target_blocks") = {
251 64 : (SubdomainName)(name() + "_LowerDBlockSource")};
252 32 : _app.getMeshGeneratorSystem().addMeshGenerator(
253 32 : "BlockToMeshConverterGenerator", name() + "_blockToMeshSource", _bmc_source_params);
254 16 : _mg_names.push_back(name() + "_blockToMeshSource");
255 :
256 : // set up AdvancedExtruderGenerator
257 32 : InputParameters aeg_params = _factory.getValidParams("AdvancedExtruderGenerator");
258 48 : aeg_params.set<MeshGeneratorName>("extrusion_curve") = (MeshGeneratorName)(name() + "_curve");
259 32 : aeg_params.set<MeshGeneratorName>("input") =
260 48 : (MeshGeneratorName)(name() + "_blockToMeshSource");
261 :
262 32 : aeg_params.set<RealVectorValue>("start_extrusion_direction") = start_direction;
263 16 : aeg_params.set<RealVectorValue>("end_extrusion_direction") = end_direction;
264 :
265 64 : aeg_params.set<Real>("start_radial_growth_rate") = getParam<Real>("start_radial_growth_rate");
266 64 : aeg_params.set<Real>("end_radial_growth_rate") = getParam<Real>("end_radial_growth_rate");
267 32 : aeg_params.set<MooseEnum>("radial_growth_method") =
268 64 : getParam<MooseEnum>("radial_growth_method");
269 :
270 48 : aeg_params.set<BoundaryName>("bottom_boundary") = name() + "_aeg_bottom_boundary";
271 48 : aeg_params.set<BoundaryName>("top_boundary") = name() + "_aeg_top_boundary";
272 48 : if (isParamValid("block"))
273 0 : paramError("block", "Not yet implemented for 2D or 3D junction");
274 32 : aeg_params.set<bool>("output") = _verbose;
275 :
276 32 : _app.getMeshGeneratorSystem().addMeshGenerator(
277 32 : "AdvancedExtruderGenerator", name() + "_aeg", aeg_params);
278 16 : _mg_names.push_back(name() + "_aeg");
279 16 : }
280 :
281 : // Stitch the extrusion / curve (in 1D) to the components
282 56 : if (first_component.getCurrentTopLevelMeshGeneratorName() !=
283 112 : second_component.getCurrentTopLevelMeshGeneratorName())
284 : {
285 96 : InputParameters stitcher_params = _factory.getValidParams("StitchMeshGenerator");
286 96 : stitcher_params.set<std::vector<MeshGeneratorName>>("inputs") =
287 288 : std::vector<MeshGeneratorName>{first_component.getCurrentTopLevelMeshGeneratorName(),
288 48 : _mg_names.back(),
289 240 : second_component.getCurrentTopLevelMeshGeneratorName()};
290 48 : if (dimension_first > 1)
291 32 : stitcher_params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") = {
292 16 : {first_boundary, name() + "_aeg_bottom_boundary"},
293 128 : {name() + "_aeg_top_boundary", second_boundary}};
294 : else
295 : {
296 32 : stitcher_params.set<bool>("clear_stitched_boundary_ids") = false;
297 64 : stitcher_params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") = {
298 32 : {first_boundary, name() + "_bspline_start_node"},
299 256 : {name() + "_bspline_end_node", second_boundary}};
300 : }
301 :
302 96 : stitcher_params.set<bool>("verbose_stitching") = _verbose;
303 96 : stitcher_params.set<bool>("output") = _verbose;
304 48 : stitcher_params.set<bool>("enforce_all_nodes_match_on_boundaries") =
305 48 : _enforce_all_nodes_match_on_boundaries;
306 96 : _app.getMeshGeneratorSystem().addMeshGenerator(
307 96 : "StitchMeshGenerator", name() + "_stitcher", stitcher_params);
308 48 : _mg_names.push_back(name() + "_stitcher");
309 48 : }
310 : else
311 : {
312 : // Handles case of needing to close a mesh. Using StitchBoundaryMeshGenerator prevents
313 : // issues with element overlap.
314 16 : InputParameters mesh_stitcher_params = _factory.getValidParams("StitchMeshGenerator");
315 16 : mesh_stitcher_params.set<std::vector<MeshGeneratorName>>("inputs") =
316 40 : std::vector<MeshGeneratorName>{first_component.getCurrentTopLevelMeshGeneratorName(),
317 32 : _mg_names.back()};
318 8 : if (dimension_first > 1)
319 0 : mesh_stitcher_params.set<std::vector<std::vector<std::string>>>(
320 0 : "stitch_boundaries_pairs") = {{first_boundary, name() + "_aeg_bottom_boundary"}};
321 : else
322 16 : mesh_stitcher_params.set<std::vector<std::vector<std::string>>>(
323 40 : "stitch_boundaries_pairs") = {{first_boundary, name() + "_bspline_start_node"}};
324 24 : mesh_stitcher_params.set<bool>("verbose_stitching") = _verbose;
325 : // TODO: remove this once we understand why it errors without
326 8 : mesh_stitcher_params.set<bool>("clear_stitched_boundary_ids") = false;
327 8 : mesh_stitcher_params.set<bool>("enforce_all_nodes_match_on_boundaries") =
328 8 : _enforce_all_nodes_match_on_boundaries;
329 16 : _app.getMeshGeneratorSystem().addMeshGenerator(
330 16 : "StitchMeshGenerator", name() + "_mesh_stitcher", mesh_stitcher_params);
331 8 : _mg_names.push_back(name() + "_stitcher");
332 :
333 : InputParameters boundary_stitcher_params =
334 16 : _factory.getValidParams("StitchBoundaryMeshGenerator");
335 24 : boundary_stitcher_params.set<MeshGeneratorName>("input") = name() + "_mesh_stitcher";
336 8 : if (dimension_first > 1)
337 0 : boundary_stitcher_params.set<std::vector<std::vector<std::string>>>(
338 0 : "stitch_boundaries_pairs") = {{name() + "_aeg_top_boundary", second_boundary}};
339 : else
340 16 : boundary_stitcher_params.set<std::vector<std::vector<std::string>>>(
341 40 : "stitch_boundaries_pairs") = {{name() + "_bspline_end_node", second_boundary}};
342 16 : boundary_stitcher_params.set<bool>("show_info") = _verbose;
343 16 : _app.getMeshGeneratorSystem().addMeshGenerator(
344 16 : "StitchBoundaryMeshGenerator", name() + "_closed", boundary_stitcher_params);
345 8 : _mg_names.push_back(name() + "_closed");
346 8 : }
347 56 : }
348 : else
349 0 : mooseError("junction_method specified is invalid!");
350 :
351 126 : _top_mg_name = _mg_names.back();
352 126 : first_component.addConnectedComponent(second_component);
353 : // Sets it for all connected components
354 : // Connected might not be the right abstraction here. It's more like "included in a common mesh"
355 468 : for (auto * component : first_component.getConnectedComponents())
356 342 : component->setCurrentTopLevelMeshGeneratorName(_top_mg_name);
357 :
358 : // For now this is a safe choice. We might want to decide otherwise once we
359 : // do mixed-dimensions. Build the junction with the dimension of the first component?
360 : mooseAssert(dimension_first == dimension_second, "Should be the same");
361 126 : _dimension = std::max(dimension_first, dimension_second);
362 1224 : }
363 :
364 : void
365 0 : ComponentJunction::checkIntegrity()
366 : {
367 0 : ComponentInitialConditionInterface::checkIntegrity();
368 0 : ComponentBoundaryConditionInterface::checkIntegrity();
369 0 : }
|