LCOV - code coverage report
Current view: top level - src/actions - SetupMeshAction.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 156 170 91.8 %
Date: 2025-07-17 01:28:37 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          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 "SetupMeshAction.h"
      11             : #include "MooseApp.h"
      12             : #include "MooseMesh.h"
      13             : #include "FileMesh.h"
      14             : #include "FEProblem.h"
      15             : #include "ActionWarehouse.h"
      16             : #include "Factory.h"
      17             : #include "AddMeshGeneratorAction.h"
      18             : 
      19             : #include <functional>
      20             : #include <algorithm>
      21             : 
      22             : registerMooseAction("MooseApp", SetupMeshAction, "setup_mesh");
      23             : registerMooseAction("MooseApp", SetupMeshAction, "set_mesh_base");
      24             : registerMooseAction("MooseApp", SetupMeshAction, "init_mesh");
      25             : 
      26             : InputParameters
      27       62412 : SetupMeshAction::validParams()
      28             : {
      29       62412 :   InputParameters params = MooseObjectAction::validParams();
      30       62412 :   params.addClassDescription("Add or create Mesh object to the simulation.");
      31             : 
      32             :   // Here we are setting the default type of the mesh to construct to "FileMesh". This is to support
      33             :   // the very long-running legacy syntax where only a file parameter is required to determine the
      34             :   // type of the "Mesh" block. We are re-adding it though so that we can detect whether or not the
      35             :   // user has explicitly set the type in an input file. We do this because we want to support
      36             :   // automatically building a "MeshGeneratorMesh" type when MeshGenerators are added to the
      37             :   // simulation.
      38       62412 :   params.addParam<std::string>(
      39             :       "type",
      40             :       "FileMesh",
      41             :       "A string representing the Moose Object that will be built by this Action");
      42             : 
      43      187236 :   params.addParam<bool>("second_order",
      44      124824 :                         false,
      45             :                         "Converts a first order mesh to a second order "
      46             :                         "mesh.  Note: This is NOT needed if you are reading "
      47             :                         "an actual first order mesh.");
      48             : 
      49       62412 :   params.addParam<std::vector<SubdomainID>>("block_id", "IDs of the block id/name pairs");
      50       62412 :   params.addParam<std::vector<SubdomainName>>(
      51             :       "block_name", "Names of the block id/name pairs (must correspond with \"block_id\"");
      52             : 
      53       62412 :   params.addParam<std::vector<BoundaryID>>("boundary_id", {}, "IDs of the boundary id/name pairs");
      54       62412 :   params.addParam<std::vector<BoundaryName>>(
      55             :       "boundary_name",
      56             :       {},
      57             :       "Names of the boundary id/name pairs (must correspond with \"boundary_id\"");
      58             : 
      59      187236 :   params.addParam<bool>("construct_side_list_from_node_list",
      60      124824 :                         false,
      61             :                         "If true, construct side lists from the nodesets in the mesh (i.e. if "
      62             :                         "every node on a give side is in a nodeset then add that side to a "
      63             :                         "sideset");
      64             : 
      65       62412 :   params.addParam<std::vector<std::string>>(
      66             :       "displacements",
      67             :       "The variables corresponding to the x y z displacements of the mesh.  If "
      68             :       "this is provided then the displacements will be taken into account during "
      69             :       "the computation. Creation of the displaced mesh can be suppressed even if "
      70             :       "this is set by setting 'use_displaced_mesh = false'.");
      71      187236 :   params.addParam<bool>(
      72             :       "use_displaced_mesh",
      73      124824 :       true,
      74             :       "Create the displaced mesh if the 'displacements' "
      75             :       "parameter is set. If this is 'false', a displaced mesh will not be created, "
      76             :       "regardless of whether 'displacements' is set.");
      77       62412 :   params.addParam<std::vector<BoundaryName>>(
      78             :       "ghosted_boundaries", {}, "Boundaries to be ghosted if using Nemesis");
      79       62412 :   params.addParam<std::vector<Real>>("ghosted_boundaries_inflation",
      80             :                                      "If you are using ghosted boundaries you will want to set "
      81             :                                      "this value to a vector of amounts to inflate the bounding "
      82             :                                      "boxes by.  ie if you are running a 3D problem you might set "
      83             :                                      "it to '0.2 0.1 0.4'");
      84             : 
      85      187236 :   params.addParam<unsigned int>(
      86      124824 :       "uniform_refine", 0, "Specify the level of uniform refinement applied to the initial mesh");
      87             : 
      88      187236 :   params.addParam<bool>("skip_deletion_repartition_after_refine",
      89      124824 :                         false,
      90             :                         "If the flag is true, uniform refinements will run more efficiently, "
      91             :                         "but at the same time, there might be extra ghosting elements. "
      92             :                         "The number of layers of additional ghosting elements depends "
      93             :                         "on the number of uniform refinement levels.  This flag "
      94             :                         "should be used only when you have a 'fine enough' coarse mesh and want "
      95             :                         "to refine the mesh by a few levels. Otherwise, it might introduce an "
      96             :                         "unbalanced workload and too large ghosting domain. ");
      97             : 
      98      187236 :   params.addParam<bool>("skip_partitioning",
      99      124824 :                         false,
     100             :                         "If true the mesh won't be partitioned. This may cause large load "
     101             :                         "imbalances.");
     102             : 
     103      187236 :   params.addParam<bool>(
     104             :       "use_split",
     105      124824 :       false,
     106             :       "Use split distributed mesh files; is overriden by the --use-split command line option");
     107       62412 :   params.addParam<std::string>("split_file",
     108             :                                "",
     109             :                                "Optional name of split mesh file(s) to write/read; is overridden "
     110             :                                "by the --split-file command line option");
     111             : 
     112             :   // groups
     113       62412 :   params.addParamNamesToGroup("displacements ghosted_boundaries ghosted_boundaries_inflation",
     114             :                               "Advanced");
     115       62412 :   params.addParamNamesToGroup("second_order construct_side_list_from_node_list skip_partitioning",
     116             :                               "Advanced");
     117       62412 :   params.addParamNamesToGroup("block_id block_name boundary_id boundary_name", "Add Names");
     118       62412 :   params.addParamNamesToGroup("use_split split_file", "Split Mesh");
     119             : 
     120       62412 :   return params;
     121           0 : }
     122             : 
     123       62174 : SetupMeshAction::SetupMeshAction(const InputParameters & params)
     124             :   : MooseObjectAction(params),
     125       62174 :     _use_split(getParam<bool>("use_split") || _app.getParam<bool>("use_split")),
     126      186211 :     _split_file(_app.isParamSetByUser("split_file") ? _app.getParam<std::string>("split_file")
     127      186211 :                                                     : getParam<std::string>("split_file"))
     128             : {
     129       62174 : }
     130             : 
     131             : void
     132       62264 : SetupMeshAction::setupMesh(MooseMesh * mesh)
     133             : {
     134       62264 :   if (isParamValid("ghosted_boundaries"))
     135       62264 :     for (const auto & bnd_name : getParam<std::vector<BoundaryName>>("ghosted_boundaries"))
     136           0 :       mesh->addGhostedBoundary(mesh->getBoundaryID(bnd_name));
     137             : 
     138       62264 :   if (isParamValid("ghosted_boundaries_inflation"))
     139             :   {
     140             :     std::vector<Real> ghosted_boundaries_inflation =
     141           0 :         getParam<std::vector<Real>>("ghosted_boundaries_inflation");
     142           0 :     mesh->setGhostedBoundaryInflation(ghosted_boundaries_inflation);
     143           0 :   }
     144             : 
     145       62264 :   mesh->ghostGhostedBoundaries();
     146             : 
     147       62264 :   if (getParam<bool>("second_order"))
     148        1151 :     mesh->getMesh().all_second_order(true);
     149             : 
     150             : #ifdef LIBMESH_ENABLE_AMR
     151       62264 :   unsigned int level = getParam<unsigned int>("uniform_refine");
     152             : 
     153             :   // Did they specify extra refinement levels on the command-line?
     154       62264 :   if (_app.isParamSetByUser("refinements"))
     155          27 :     level += _app.getParam<unsigned int>("refinements");
     156             : 
     157       62264 :   mesh->setUniformRefineLevel(level, getParam<bool>("skip_deletion_repartition_after_refine"));
     158             : #endif // LIBMESH_ENABLE_AMR
     159             : 
     160             :   // Add entity names to the mesh
     161       62264 :   if (_pars.isParamValid("block_id") && _pars.isParamValid("block_name"))
     162             :   {
     163          43 :     std::vector<SubdomainID> ids = getParam<std::vector<SubdomainID>>("block_id");
     164          43 :     std::vector<SubdomainName> names = getParam<std::vector<SubdomainName>>("block_name");
     165          43 :     std::set<SubdomainName> seen_it;
     166             : 
     167          43 :     if (ids.size() != names.size())
     168           4 :       mooseError("You must supply the same number of block ids and names parameters");
     169             : 
     170         129 :     for (unsigned int i = 0; i < ids.size(); ++i)
     171             :     {
     172          94 :       if (seen_it.find(names[i]) != seen_it.end())
     173           4 :         mooseError("The following dynamic block name is not unique: " + names[i]);
     174          90 :       seen_it.insert(names[i]);
     175          90 :       mesh->setSubdomainName(ids[i], names[i]);
     176             :     }
     177          35 :   }
     178       62256 :   if (_pars.isParamValid("boundary_id") && _pars.isParamValid("boundary_name"))
     179             :   {
     180       62256 :     std::vector<BoundaryID> ids = getParam<std::vector<BoundaryID>>("boundary_id");
     181       62256 :     std::vector<BoundaryName> names = getParam<std::vector<BoundaryName>>("boundary_name");
     182       62256 :     std::set<SubdomainName> seen_it;
     183             : 
     184       62256 :     if (ids.size() != names.size())
     185           4 :       mooseError("You must supply the same number of boundary ids and names parameters");
     186             : 
     187       62280 :     for (unsigned int i = 0; i < ids.size(); ++i)
     188             :     {
     189          32 :       if (seen_it.find(names[i]) != seen_it.end())
     190           4 :         mooseError("The following dynamic boundary name is not unique: " + names[i]);
     191          28 :       mesh->setBoundaryName(ids[i], names[i]);
     192          28 :       seen_it.insert(names[i]);
     193             :     }
     194       62248 :   }
     195             : 
     196       62248 :   if (getParam<bool>("construct_side_list_from_node_list"))
     197         198 :     mesh->getMesh().get_boundary_info().build_side_list_from_node_list();
     198             : 
     199             :   // Here we can override the partitioning for special cases
     200       62248 :   if (getParam<bool>("skip_partitioning"))
     201          28 :     mesh->getMesh().skip_partitioning(getParam<bool>("skip_partitioning"));
     202       62248 : }
     203             : 
     204             : std::string
     205         266 : SetupMeshAction::modifyParamsForUseSplit(InputParameters & moose_object_params) const
     206             : {
     207             :   // Get the split_file extension, if there is one, and use that to decide
     208             :   // between .cpr and .cpa.gz
     209         266 :   auto split_file = _split_file;
     210         266 :   std::string split_file_ext = MooseUtils::getExtension(split_file);
     211             : 
     212             :   // If split_file already has the .cpr or .cpa.gz extension, we go with
     213             :   // that, otherwise we strip off the extension and append ".cpa.gz".
     214         266 :   if (split_file != "" && split_file_ext != "cpr" && split_file_ext != "cpa.gz")
     215         149 :     split_file = MooseUtils::stripExtension(split_file) + ".cpa.gz";
     216             : 
     217         266 :   if (_type != "FileMesh")
     218             :   {
     219         266 :     if (split_file.empty())
     220           0 :       mooseError("Cannot use split mesh for a non-file mesh without specifying --split-file on "
     221             :                  "command line or the Mesh/split_file parameter");
     222             : 
     223         266 :     auto new_pars = FileMesh::validParams();
     224             : 
     225             :     // Keep existing parameters where possible
     226         266 :     new_pars.applyParameters(_moose_object_pars);
     227             : 
     228         266 :     new_pars.set<MeshFileName>("file") = split_file;
     229         266 :     new_pars.set<MooseApp *>("_moose_app") = moose_object_params.get<MooseApp *>("_moose_app");
     230         266 :     moose_object_params = new_pars;
     231         266 :   }
     232             :   else
     233             :   {
     234           0 :     if (!split_file.empty())
     235           0 :       moose_object_params.set<MeshFileName>("file") = split_file;
     236             :     else
     237           0 :       moose_object_params.set<MeshFileName>("file") =
     238           0 :           MooseUtils::stripExtension(moose_object_params.get<MeshFileName>("file")) + ".cpa.gz";
     239             :   }
     240             : 
     241         266 :   moose_object_params.set<bool>("_is_split") = true;
     242             : 
     243         532 :   return "FileMesh";
     244         266 : }
     245             : 
     246             : void
     247      183864 : SetupMeshAction::act()
     248             : {
     249             :   // Create the mesh object and tell it to build itself
     250      183864 :   if (_current_task == "setup_mesh")
     251             :   {
     252       61898 :     TIME_SECTION("SetupMeshAction::act::setup_mesh", 1, "Setting Up Mesh", true);
     253             : 
     254       61898 :     if (_app.useMasterMesh())
     255         784 :       _mesh = _app.masterMesh()->safeClone();
     256             :     else
     257             :     {
     258       61114 :       const auto & generator_actions = _awh.getActionListByName("add_mesh_generator");
     259             : 
     260             :       // If we trigger any actions that can build MeshGenerators, whether through input file
     261             :       // syntax or through custom actions, change the default type to construct. We can't yet
     262             :       // check whether there are any actual MeshGenerator objects because those are added after
     263             :       // setup_mesh
     264       61114 :       if (!generator_actions.empty())
     265             :       {
     266             :         // Check for whether type has been set or whether for the default type (FileMesh) a file has
     267             :         // been provided
     268       23550 :         if (!_pars.isParamSetByUser("type") && !_moose_object_pars.isParamValid("file"))
     269             :         {
     270       23466 :           _type = "MeshGeneratorMesh";
     271       23466 :           auto original_params = _moose_object_pars;
     272       23466 :           _moose_object_pars = _factory.getValidParams("MeshGeneratorMesh");
     273             : 
     274             :           // Since we changing the type on the fly, we'll have to manually extract parameters again
     275             :           // from the input file object.
     276       23466 :           _app.builder().extractParams(_registered_identifier, _moose_object_pars);
     277       23466 :         }
     278          84 :         else if (!_moose_object_pars.get<bool>("_mesh_generator_mesh"))
     279             :         {
     280             :           // There are cases where a custom action may register the "add_mesh_generator" task, but
     281             :           // may not actually add any mesh generators depending on user input. We don't want to risk
     282             :           // giving false warnings in this case. However, if we triggered the "add_mesh_generator"
     283             :           // task through explicit input file syntax, then it is definitely safe to warn
     284           4 :           for (auto generator_action_ptr : generator_actions)
     285           4 :             if (dynamic_cast<AddMeshGeneratorAction *>(generator_action_ptr))
     286             :             {
     287           4 :               mooseError("Mesh Generators present but the [Mesh] block is set to construct a \"",
     288           4 :                          _type,
     289             :                          "\" mesh, which does not use Mesh Generators in constructing the mesh. ");
     290             :             }
     291             :         }
     292             :       }
     293             : 
     294             :       // switch non-file meshes to be a file-mesh if using a pre-split mesh configuration.
     295       61110 :       if (_use_split)
     296         266 :         _type = modifyParamsForUseSplit(_moose_object_pars);
     297             : 
     298       61110 :       _mesh = _factory.create<MooseMesh>(_type, "mesh", _moose_object_pars);
     299             :     }
     300       61846 :   }
     301             : 
     302      121966 :   else if (_current_task == "set_mesh_base")
     303             :   {
     304       60983 :     TIME_SECTION("SetupMeshAction::act::set_mesh_base", 1, "Setting Mesh", true);
     305             : 
     306       60983 :     if (!_app.useMasterMesh() && !_mesh->hasMeshBase())
     307             :     {
     308             :       // We want to set the MeshBase object to that coming from mesh generators when the following
     309             :       // conditions are met:
     310             :       // 1. We have mesh generators
     311             :       // 2. We are not using the pre-split mesh
     312             :       // 3. We are not: recovering/restarting and we are the master application
     313       82842 :       if (!_app.getMeshGeneratorNames().empty() && !_use_split &&
     314       22643 :           !((_app.isRecovering() || _app.isRestarting()) && _app.isUltimateMaster()))
     315             :       {
     316       21760 :         auto & mesh_generator_system = _app.getMeshGeneratorSystem();
     317             :         auto mesh_base =
     318       21760 :             mesh_generator_system.getSavedMesh(mesh_generator_system.mainMeshGeneratorName());
     319       21760 :         if (_mesh->allowRemoteElementRemoval() != mesh_base->allow_remote_element_removal())
     320           0 :           mooseError("The MooseMesh and libmesh::MeshBase object coming from mesh generators are "
     321             :                      "out of sync with respect to whether remote elements can be deleted");
     322             :         mooseAssert(mesh_base, "Null mesh");
     323       21760 :         _mesh->setMeshBase(std::move(mesh_base));
     324       21760 :       }
     325             :       else
     326             :       {
     327       38439 :         const auto & mg_names = _app.getMeshGeneratorNames();
     328       38439 :         std::vector<bool> use_dm;
     329       40281 :         for (const auto & mg_name : mg_names)
     330        1842 :           if (hasMeshProperty<bool>("use_distributed_mesh", mg_name))
     331          67 :             use_dm.push_back(getMeshProperty<bool>("use_distributed_mesh", mg_name));
     332             : 
     333       38439 :         if (!use_dm.empty())
     334             :         {
     335          67 :           if (std::adjacent_find(use_dm.begin(), use_dm.end(), std::not_equal_to<bool>()) !=
     336         134 :               use_dm.end())
     337           0 :             mooseError("You cannot use mesh generators that set different values of the mesh "
     338             :                        "property 'use_distributed_mesh' within the same simulation.");
     339             : 
     340          67 :           const auto ptype = use_dm.front() ? MooseMesh::ParallelType::DISTRIBUTED
     341          67 :                                             : MooseMesh::ParallelType::REPLICATED;
     342          67 :           _mesh->setParallelType(ptype);
     343             :         }
     344             : 
     345       38439 :         _mesh->setMeshBase(_mesh->buildMeshBaseObject());
     346       38439 :       }
     347             :     }
     348       60983 :   }
     349             : 
     350       60983 :   else if (_current_task == "init_mesh")
     351             :   {
     352       60983 :     TIME_SECTION("SetupMeshAction::act::set_mesh_base", 1, "Initializing Mesh", true);
     353             : 
     354       60983 :     if (_app.useMasterMesh())
     355             :     {
     356         784 :       if (_app.masterDisplacedMesh())
     357           0 :         _displaced_mesh = _app.masterDisplacedMesh()->safeClone();
     358             :     }
     359             :     else
     360             :     {
     361       60199 :       _mesh->init();
     362             : 
     363       60191 :       if (isParamValid("displacements") && getParam<bool>("use_displaced_mesh"))
     364             :       {
     365        2073 :         _displaced_mesh = _mesh->safeClone();
     366        2073 :         _displaced_mesh->isDisplaced(true);
     367        4146 :         _displaced_mesh->getMesh().allow_remote_element_removal(
     368        2073 :             _mesh->getMesh().allow_remote_element_removal());
     369             : 
     370             :         std::vector<std::string> displacements =
     371        2073 :             getParam<std::vector<std::string>>("displacements");
     372        2073 :         if (displacements.size() < _displaced_mesh->dimension())
     373           0 :           mooseError("Number of displacements must be greater than or equal to the dimension of "
     374             :                      "the mesh!");
     375        2073 :       }
     376             : 
     377       60191 :       setupMesh(_mesh.get());
     378             : 
     379       60175 :       if (_displaced_mesh)
     380        2073 :         setupMesh(_displaced_mesh.get());
     381             :     }
     382       60959 :   }
     383      183788 : }

Generated by: LCOV version 1.14