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 : #include "PeriodicBCHelper.h" 11 : 12 : #include "DisplacedProblem.h" 13 : #include "FEProblemBase.h" 14 : #include "FunctionPeriodicBoundary.h" 15 : #include "MooseObjectAction.h" 16 : #include "MooseMesh.h" 17 : 18 : #include "libmesh/periodic_boundary.h" 19 : #include "libmesh/ghost_point_neighbors.h" 20 : 21 : namespace Moose 22 : { 23 : InputParameters 24 737 : PeriodicBCHelper::validParams() 25 : { 26 737 : auto params = emptyInputParameters(); 27 : 28 1474 : const MultiMooseEnum auto_direction("x=0 y=1 z=2"); 29 2948 : params.addParam<MultiMooseEnum>( 30 : "auto_direction", 31 : auto_direction, 32 : "If using a generated mesh, the dimension(s) you want to mark as periodic."); 33 : 34 2948 : params.addParam<BoundaryName>("primary", "Boundary associated with the primary boundary."); 35 2948 : params.addParam<BoundaryName>("secondary", "Boundary associated with the secondary boundary."); 36 2948 : params.addParam<RealVectorValue>("translation", 37 : "Vector that translates coordinates on the primary boundary to " 38 : "coordinates on the secondary boundary."); 39 2948 : params.addParam<std::vector<std::string>>("transform_func", 40 : "Functions that specify the transformation"); 41 2211 : params.addParam<std::vector<std::string>>("inv_transform_func", 42 : "Functions that specify the inverse transformation"); 43 1474 : return params; 44 737 : } 45 : 46 695 : PeriodicBCHelper::PeriodicBCHelper(const Action & action) : _action(action), _params(getParams()) {} 47 : 48 : void 49 695 : PeriodicBCHelper::checkPeriodicParams() const 50 : { 51 2085 : if (_params.isParamValid("auto_direction")) 52 : { 53 1821 : for (const auto & param : 54 2127 : {"primary", "secondary", "translation", "transform_func", "inv_transform_func"}) 55 4554 : if (_params.isParamValid(param)) 56 6 : _params.paramError(param, "Should not be specified along with 'auto_direction'"); 57 : } 58 1939 : else if (!_params.isParamValid("primary") || !_params.isParamValid("secondary")) 59 3 : _action.mooseError( 60 : "Either 'auto_direction' or both 'primary' and 'secondary' must be specified"); 61 689 : } 62 : 63 : void 64 662 : PeriodicBCHelper::setupPeriodicBoundaries(FEProblemBase & problem) 65 : { 66 : mooseAssert(_periodic_boundaries.empty(), "Already set"); 67 : 68 : // Setup paired sidesets to be able to call MooseMesh::getPairedBoundaryMapping() 69 662 : if (!problem.mesh().hasDetectedPairedSidesets()) 70 538 : problem.mesh().detectPairedSidesets(); 71 : 72 1986 : if (_params.isParamValid("auto_direction")) 73 285 : setupAutoPeriodicBoundaries(problem.mesh()); 74 : else 75 377 : setupManualPeriodicBoundaries(problem); 76 : 77 : mooseAssert(getPeriodicBoundaries().size(), "Shouldn't run without boundaries"); 78 : 79 727 : const auto add_ghosting = [this](auto & problem) 80 : { 81 727 : auto & mesh = problem.mesh().getMesh(); 82 727 : auto functor = std::make_shared<libMesh::GhostPointNeighbors>(mesh); 83 727 : functor->set_periodic_boundaries(&getPeriodicBoundaries()); 84 727 : mesh.add_ghosting_functor(functor); 85 1362 : }; 86 : 87 635 : add_ghosting(problem); 88 635 : if (const auto displaced_problem = problem.getDisplacedProblem()) 89 635 : add_ghosting(*displaced_problem); 90 635 : } 91 : 92 : void 93 905 : PeriodicBCHelper::addPeriodicBoundary(std::unique_ptr<libMesh::PeriodicBoundaryBase> p) 94 : { 95 905 : onSetupPeriodicBoundary(*p); 96 : 97 902 : _periodic_boundaries.emplace(p->myboundary, p->clone()); 98 902 : _periodic_boundaries.emplace(p->pairedboundary, p->clone(libMesh::PeriodicBoundaryBase::INVERSE)); 99 902 : } 100 : 101 : void 102 285 : PeriodicBCHelper::setupAutoPeriodicBoundaries(MooseMesh & mesh) 103 : { 104 : // If we are working with a parallel mesh then we're going to ghost all the boundaries 105 : // everywhere because we don't know what we need... 106 285 : if (mesh.isDistributedMesh() && !mesh.detectOrthogonalDimRanges()) 107 0 : _action.mooseError("Could not detect orthogonal dimension ranges for DistributedMesh."); 108 : 109 831 : for (const auto & dir : _params.get<MultiMooseEnum>("auto_direction")) 110 : { 111 552 : const int component = dir.id(); 112 552 : if (component > (cast_int<int>(mesh.dimension()) - 1)) 113 3 : _params.paramError("auto_direction", 114 3 : MooseUtils::toLower(dir.name()), 115 : "-dimension component not valid for ", 116 3 : mesh.dimension(), 117 : "D mesh"); 118 : 119 549 : const auto boundary_ids = mesh.getPairedBoundaryMapping(component); 120 549 : if (!boundary_ids) 121 3 : _params.paramError("auto_direction", 122 : "Couldn't auto-detect a paired boundary in the ", 123 3 : MooseUtils::toLower(dir.name()), 124 : "-direction"); 125 : 126 546 : RealVectorValue v; 127 546 : v(component) = mesh.dimensionWidth(component); 128 : 129 546 : auto p = std::make_unique<libMesh::PeriodicBoundary>(v); 130 546 : p->myboundary = boundary_ids->first; 131 546 : p->pairedboundary = boundary_ids->second; 132 : 133 546 : addPeriodicBoundary(std::move(p)); 134 546 : } 135 279 : } 136 : 137 : void 138 377 : PeriodicBCHelper::setupManualPeriodicBoundaries(FEProblemBase & problem) 139 : { 140 377 : auto & mesh = problem.mesh(); 141 377 : std::unique_ptr<libMesh::PeriodicBoundaryBase> p; 142 : 143 1131 : if (const auto translation_ptr = _params.queryParam<RealVectorValue>("translation")) 144 239 : p = std::make_unique<libMesh::PeriodicBoundary>(*translation_ptr); 145 414 : else if (const auto fn_names_ptr = _params.queryParam<std::vector<std::string>>("transform_func")) 146 : { 147 : const auto inv_fn_names_ptr = 148 270 : _params.queryParam<std::vector<std::string>>("inv_transform_func"); 149 135 : if (!inv_fn_names_ptr) 150 6 : _params.paramError("transform_func", "Must also specify 'inv_transform_func'"); 151 132 : if (fn_names_ptr->size() != mesh.dimension()) 152 6 : _params.paramError( 153 3 : "transform_func", "Must be the size of the mesh dimension ", mesh.dimension()); 154 129 : if (inv_fn_names_ptr->size() != mesh.dimension()) 155 6 : _params.paramError( 156 3 : "inv_transform_func", "Must be the size of the mesh dimension ", mesh.dimension()); 157 : 158 126 : p = std::make_unique<FunctionPeriodicBoundary>(problem, *fn_names_ptr, *inv_fn_names_ptr); 159 : } 160 : else 161 3 : _action.mooseError( 162 : "You need to specify either 'auto_direction', 'translation', or 'transform_func'"); 163 : 164 727 : const auto get_boundary = [this, &mesh](const auto & param) -> BoundaryID 165 : { 166 2181 : if (const auto name_ptr = _params.queryParam<BoundaryName>(param)) 167 : { 168 727 : if (const auto id = MooseMeshUtils::getBoundaryID(*name_ptr, mesh); 169 727 : id != Moose::INVALID_BOUNDARY_ID) 170 721 : return id; 171 12 : _params.paramError(param, "Boundary '", *name_ptr, "' does not exist in the mesh"); 172 : } 173 0 : _action.mooseError("Parameter '", param, "' is required when 'auto_direction' is not set"); 174 365 : }; 175 365 : p->myboundary = get_boundary("primary"); 176 362 : p->pairedboundary = get_boundary("secondary"); 177 : 178 359 : addPeriodicBoundary(std::move(p)); 179 356 : } 180 : 181 : const InputParameters & 182 695 : PeriodicBCHelper::getParams() const 183 : { 184 695 : if (const auto moa = dynamic_cast<const MooseObjectAction *>(&_action)) 185 0 : return moa->getObjectParams(); 186 695 : return _action.parameters(); 187 : } 188 : }