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 "MortarGapHeatTransferAction.h" 11 : 12 : #include "AddVariableAction.h" 13 : #include "FEProblem.h" 14 : #include "libmesh/string_to_enum.h" 15 : #include "NonlinearSystem.h" 16 : 17 : #include "ModularGapConductanceConstraint.h" 18 : #include "GapFluxModelRadiation.h" 19 : #include "GapFluxModelConduction.h" 20 : 21 : #include <algorithm> 22 : 23 : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "append_mesh_generator"); 24 : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_mortar_variable"); 25 : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_constraint"); 26 : registerMooseAction("HeatTransferApp", MortarGapHeatTransferAction, "add_user_object"); 27 : 28 : InputParameters 29 308 : MortarGapHeatTransferAction::validParams() 30 : { 31 308 : InputParameters params = Action::validParams(); 32 308 : params.addClassDescription( 33 : "Action that controls the creation of all of the necessary objects for " 34 : "calculation of heat transfer through an open/closed gap using a mortar formulation and a " 35 : "modular design approach"); 36 : 37 616 : params.addParam<Real>("thermal_lm_scaling", 38 616 : 1., 39 : "Scaling factor to apply to the thermal Lagrange multiplier variable"); 40 : 41 308 : params += ModularGapConductanceConstraint::validParams(); 42 308 : params += GapFluxModelRadiation::validParams(); 43 308 : params += GapFluxModelConduction::validParams(); 44 : 45 616 : params.addParam<bool>( 46 : "correct_edge_dropping", 47 616 : true, 48 : "Whether to enable correct edge dropping treatment for mortar constraints. When disabled " 49 : "any Lagrange Multiplier degree of freedom on a secondary element without full primary " 50 : "contributions will be set (strongly) to 0."); 51 : 52 308 : params.makeParamNotRequired<SubdomainName>("primary_subdomain"); 53 308 : params.makeParamNotRequired<SubdomainName>("secondary_subdomain"); 54 308 : params.makeParamNotRequired<Real>("gap_conductivity"); 55 : 56 616 : params.addParam<MultiMooseEnum>( 57 : "gap_flux_options", MortarGapHeatTransfer::gapFluxPhysics, "The gap flux models to build"); 58 : 59 616 : params.addParam<std::vector<UserObjectName>>( 60 : "user_created_gap_flux_models", 61 : {}, 62 : "The name of the user objects created by the user to represent gap heat transfer physics"); 63 : 64 616 : params.addParamNamesToGroup("primary_subdomain secondary_subdomain", "Gap surface definition"); 65 616 : params.addParamNamesToGroup("gap_flux_options user_created_gap_flux_models", "Gap flux models"); 66 616 : params.addParamNamesToGroup("thermal_lm_scaling correct_edge_dropping", 67 : "Thermal Lagrange multiplier"); 68 : 69 308 : return params; 70 0 : } 71 : 72 308 : MortarGapHeatTransferAction::MortarGapHeatTransferAction(const InputParameters & params) 73 : : Action(params), 74 308 : _user_provided_mortar_meshes(false), 75 308 : _user_provided_gap_flux_models( 76 308 : getParam<std::vector<UserObjectName>>("user_created_gap_flux_models").size() > 0 ? true 77 308 : : false) 78 : 79 : { 80 924 : if (getParam<MultiMooseEnum>("gap_flux_options").size() > 0 && _user_provided_gap_flux_models) 81 0 : paramError( 82 : "gap_flux_options", 83 : "Either create user objects for the action in the input file or provide the desire physics " 84 : "to the action via the gap_flux_options parameter. Mixed use is not supported"); 85 : 86 1854 : for (unsigned int i = 0; i < getParam<MultiMooseEnum>("gap_flux_options").size(); i++) 87 310 : _gap_flux_models.push_back(static_cast<MortarGapHeatTransfer::UserObjectToBuild>( 88 930 : getParam<MultiMooseEnum>("gap_flux_options").get(i))); 89 : 90 : // We do not currently support building more than one condution or more than one radiation user 91 : // object from this action. 92 : const unsigned int conduction_build_uos = 93 : cast_int<unsigned int>(std::count(_gap_flux_models.cbegin(), 94 : _gap_flux_models.cend(), 95 : MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION)); 96 : const unsigned int radiation_build_uos = 97 : cast_int<unsigned int>(std::count(_gap_flux_models.cbegin(), 98 : _gap_flux_models.cend(), 99 : MortarGapHeatTransfer::UserObjectToBuild::RADIATION)); 100 : 101 308 : if (conduction_build_uos > 1 || radiation_build_uos > 1) 102 2 : paramError("gap_flux_options", 103 : "You cannot choose to have more than one conduction or more than one radiation user " 104 : "objects when they are built by the action. If you want to superimpose multiple " 105 : "physics, you can choose to create your own user objects and pass them to this " 106 : "action via 'user_created_gap_flux_models'"); 107 : 108 612 : if (params.isParamSetByUser("primary_subdomain") && 109 382 : params.isParamSetByUser("secondary_subdomain")) 110 : { 111 76 : mooseInfo("Mortar gap heat transfer action is using the lower-dimensional domains provided by " 112 : "the user"); 113 76 : _user_provided_mortar_meshes = true; 114 : } 115 : else 116 230 : mooseInfo("Mortar gap heat transfer action is creating new lower-dimensional domains"); 117 : 118 306 : if (_user_provided_gap_flux_models) 119 154 : mooseInfo( 120 : "User decided to create user objects to model physics for the mortar gap heat transfer " 121 : "action independently, i.e. not through the mortar gap heat transfer action."); 122 : else 123 152 : mooseInfo("The mortar gap heat transfer action will add gap heat transfer physics according to " 124 : "the gap_flux_options input parameter"); 125 : 126 : const bool wrong_parameters_provided = 127 460 : _user_provided_gap_flux_models && 128 308 : (params.isParamSetByUser("gap_conductivity") || 129 612 : params.isParamSetByUser("primary_emissivity") || 130 610 : params.isParamSetByUser("secondary_emissivity") || 131 610 : params.isParamSetByUser("gap_conductivity_function") || 132 458 : params.isParamSetByUser("gap_conductivity_function_variable") || 133 458 : params.isParamSetByUser("min_gap")); 134 : 135 306 : if (wrong_parameters_provided) 136 2 : paramError( 137 : "user_created_gap_flux_models", 138 : "The mortar gap heat transfer action requires that the input file defines user objects " 139 : "with physics or adds physics parameters directly into the action. You have provided both " 140 : "user objects and physics parameters (e.g. emissivities, gap conductance, etc.)."); 141 304 : } 142 : 143 : void 144 304 : MortarGapHeatTransferAction::act() 145 : { 146 304 : if (_current_task == "append_mesh_generator") 147 76 : addMortarMesh(); 148 228 : else if (_current_task == "add_mortar_variable") 149 76 : addMortarVariable(); 150 304 : if (_current_task == "add_constraint") 151 76 : addConstraints(); 152 228 : else if (_current_task == "add_user_object") 153 76 : if (!_user_provided_gap_flux_models) 154 38 : addUserObjects(); 155 304 : } 156 : 157 : void 158 57 : MortarGapHeatTransferAction::coreMortarMesh() 159 : { 160 57 : if (!(_app.isRecovering() && _app.isUltimateMaster()) && !_app.useMasterMesh()) 161 : { 162 42 : std::string action_name = MooseUtils::shortName(name()); 163 : 164 42 : const MeshGeneratorName primary_name = action_name + "_primary_subdomain" + "_generator"; 165 42 : const MeshGeneratorName secondary_name = action_name + "_secondary_subdomain" + "_generator"; 166 : 167 42 : auto primary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator"); 168 42 : auto secondary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator"); 169 : 170 126 : primary_params.set<SubdomainName>("new_block_name") = action_name + "_primary_subdomain"; 171 126 : secondary_params.set<SubdomainName>("new_block_name") = action_name + "_secondary_subdomain"; 172 : 173 84 : primary_params.set<std::vector<BoundaryName>>("sidesets") = { 174 168 : getParam<BoundaryName>("primary_boundary")}; 175 84 : secondary_params.set<std::vector<BoundaryName>>("sidesets") = { 176 168 : getParam<BoundaryName>("secondary_boundary")}; 177 : 178 84 : _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", primary_name, primary_params); 179 84 : _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", secondary_name, secondary_params); 180 42 : } 181 183 : } 182 : 183 : void 184 912 : MortarGapHeatTransferAction::addRelationshipManagers(Moose::RelationshipManagerType input_rm_type) 185 : { 186 912 : checkForExistingSubdomains(); 187 912 : std::string action_name = MooseUtils::shortName(name()); 188 : 189 912 : auto params = MortarConstraintBase::validParams(); 190 1824 : params.set<bool>("use_displaced_mesh") = getParam<bool>("use_displaced_mesh"); 191 2736 : params.set<BoundaryName>("primary_boundary") = getParam<BoundaryName>("primary_boundary"); 192 2736 : params.set<BoundaryName>("secondary_boundary") = getParam<BoundaryName>("secondary_boundary"); 193 : 194 912 : if (_user_provided_mortar_meshes) 195 : { 196 684 : params.set<SubdomainName>("primary_subdomain") = getParam<SubdomainName>("primary_subdomain"); 197 456 : params.set<SubdomainName>("secondary_subdomain") = 198 456 : getParam<SubdomainName>("secondary_subdomain"); 199 : } 200 : else 201 : { 202 2052 : params.set<SubdomainName>("primary_subdomain") = action_name + "_primary_subdomain"; 203 2736 : params.set<SubdomainName>("secondary_subdomain") = action_name + "_secondary_subdomain"; 204 : } 205 : 206 912 : addRelationshipManagers(input_rm_type, params); 207 1824 : } 208 : 209 : void 210 76 : MortarGapHeatTransferAction::addMortarVariable() 211 : { 212 76 : checkForExistingSubdomains(); 213 : 214 76 : InputParameters params = _factory.getValidParams("MooseVariableBase"); 215 : 216 152 : const std::string & temperature = getParam<std::vector<VariableName>>("temperature")[0]; 217 76 : std::string action_name = MooseUtils::shortName(name()); 218 : 219 76 : if (!_problem->hasVariable(temperature)) 220 0 : mooseError("Temperature variable is missing"); 221 : 222 : const auto primal_type = 223 76 : _problem->getVariable(0, temperature, Moose::VarKindType::VAR_SOLVER).feType(); 224 : const int lm_order = primal_type.order.get_order(); 225 : 226 76 : if (primal_type.family != LAGRANGE) 227 0 : mooseError("The mortar thermal action can only be used with LAGRANGE finite elements"); 228 : 229 152 : params.set<MooseEnum>("family") = Utility::enum_to_string<FEFamily>(primal_type.family); 230 152 : params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(libMesh::OrderWrapper{lm_order}); 231 : 232 76 : if (_user_provided_mortar_meshes) 233 38 : params.set<std::vector<SubdomainName>>("block") = { 234 76 : getParam<SubdomainName>("secondary_subdomain")}; 235 : else 236 285 : params.set<std::vector<SubdomainName>>("block") = {action_name + "_secondary_subdomain"}; 237 : 238 228 : params.set<std::vector<Real>>("scaling") = {getParam<Real>("thermal_lm_scaling")}; 239 76 : auto fe_type = AddVariableAction::feType(params); 240 76 : auto var_type = AddVariableAction::variableType(fe_type); 241 : 242 152 : _problem->addVariable(var_type, action_name + "_thermal_lm", params); 243 114 : } 244 : 245 : void 246 76 : MortarGapHeatTransferAction::addConstraints() 247 : { 248 76 : checkForExistingSubdomains(); 249 : 250 152 : InputParameters params = _factory.getValidParams("ModularGapConductanceConstraint"); 251 76 : const std::string action_name = MooseUtils::shortName(name()); 252 : 253 76 : params.applyParameters(parameters()); 254 76 : params.set<bool>("use_displaced_mesh") = true; 255 : 256 228 : params.set<BoundaryName>("primary_boundary") = getParam<BoundaryName>("primary_boundary"); 257 228 : params.set<BoundaryName>("secondary_boundary") = getParam<BoundaryName>("secondary_boundary"); 258 : 259 76 : if (_user_provided_mortar_meshes) 260 : { 261 57 : params.set<SubdomainName>("primary_subdomain") = getParam<SubdomainName>("primary_subdomain"); 262 38 : params.set<SubdomainName>("secondary_subdomain") = 263 38 : getParam<SubdomainName>("secondary_subdomain"); 264 : } 265 : else 266 : { 267 171 : params.set<SubdomainName>("primary_subdomain") = action_name + "_primary_subdomain"; 268 228 : params.set<SubdomainName>("secondary_subdomain") = action_name + "_secondary_subdomain"; 269 : } 270 : 271 228 : params.set<NonlinearVariableName>("variable") = action_name + "_thermal_lm"; 272 : 273 152 : params.set<VariableName>("secondary_variable") = 274 76 : getParam<std::vector<VariableName>>("temperature")[0]; 275 : 276 76 : if (!_user_provided_gap_flux_models) 277 : { 278 38 : std::vector<UserObjectName> uoname_strings(0); 279 : 280 114 : for (const auto & uo_name : _gap_flux_models) 281 : { 282 76 : if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION) 283 38 : uoname_strings.push_back("gap_flux_model_conduction_object_" + 284 76 : MooseUtils::shortName(name())); 285 38 : else if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::RADIATION) 286 38 : uoname_strings.push_back("gap_flux_model_radiation_object_" + 287 76 : MooseUtils::shortName(name())); 288 : } 289 : 290 38 : params.set<std::vector<UserObjectName>>("gap_flux_models") = uoname_strings; 291 38 : } 292 : else 293 76 : params.set<std::vector<UserObjectName>>("gap_flux_models") = 294 114 : getParam<std::vector<UserObjectName>>("user_created_gap_flux_models"); 295 : 296 152 : _problem->addConstraint( 297 152 : "ModularGapConductanceConstraint", action_name + "_ModularGapConductanceConstraint", params); 298 76 : } 299 : 300 : void 301 76 : MortarGapHeatTransferAction::addMortarMesh() 302 : { 303 : // Evaluate whether we have sufficient information from the user to skip building the 304 : // lower-dimensional domains. 305 76 : checkForExistingSubdomains(); 306 : 307 : // We may have available lower-dimensional domains (e.g. from a mechanical contact action), whose 308 : // subdomains can be reused for adding mortar variables and constraints. 309 76 : if (!_user_provided_mortar_meshes) 310 57 : coreMortarMesh(); 311 76 : } 312 : 313 : void 314 38 : MortarGapHeatTransferAction::addUserObjects() 315 : { 316 : // It is risky to apply this optimization to contact problems 317 : // since the problem configuration may be changed during Jacobian 318 : // evaluation. We therefore turn it off for all contact problems so that 319 : // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3 or older. 320 : mooseAssert(_problem, "Problem pointer is null"); 321 : 322 38 : if (!_problem->isSNESMFReuseBaseSetbyUser()) 323 : _problem->setSNESMFReuseBase(false, false); 324 : 325 114 : for (const auto & uo_name : _gap_flux_models) 326 : { 327 76 : if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::CONDUCTION) 328 : { 329 38 : auto var_params = _factory.getValidParams("GapFluxModelConduction"); 330 : 331 76 : var_params.set<std::vector<VariableName>>("temperature") = 332 76 : getParam<std::vector<VariableName>>("temperature"); 333 76 : var_params.set<Real>("gap_conductivity") = getParam<Real>("gap_conductivity"); 334 : 335 76 : if (isParamValid("gap_conductivity_function")) 336 0 : var_params.set<FunctionName>("gap_conductivity_function") = 337 0 : getParam<FunctionName>("gap_conductivity_function"); 338 : 339 76 : if (isParamValid("gap_conductivity_function_variable")) 340 0 : var_params.set<std::vector<VariableName>>("gap_conductivity_function_variable") = 341 0 : getParam<std::vector<VariableName>>("gap_conductivity_function_variable"); 342 : 343 76 : var_params.set<Real>("min_gap") = getParam<Real>("min_gap"); 344 76 : var_params.set<unsigned int>("min_gap_order") = getParam<unsigned int>("min_gap_order"); 345 : 346 76 : var_params.set<std::vector<BoundaryName>>("boundary") = 347 76 : getParam<std::vector<BoundaryName>>("boundary"); 348 : 349 38 : var_params.set<bool>("use_displaced_mesh") = true; 350 : 351 114 : _problem->addUserObject("GapFluxModelConduction", 352 38 : "gap_flux_model_conduction_object_" + MooseUtils::shortName(name()), 353 : var_params); 354 38 : } 355 38 : else if (uo_name == MortarGapHeatTransfer::UserObjectToBuild::RADIATION) 356 : 357 : { 358 38 : auto var_params = _factory.getValidParams("GapFluxModelRadiation"); 359 : 360 76 : var_params.set<Real>("stefan_boltzmann") = getParam<Real>("stefan_boltzmann"); 361 76 : var_params.set<Real>("primary_emissivity") = getParam<Real>("primary_emissivity"); 362 76 : var_params.set<Real>("secondary_emissivity") = getParam<Real>("secondary_emissivity"); 363 : 364 76 : var_params.set<std::vector<BoundaryName>>("boundary") = 365 76 : getParam<std::vector<BoundaryName>>("boundary"); 366 : 367 76 : var_params.set<std::vector<VariableName>>("temperature") = 368 76 : getParam<std::vector<VariableName>>("temperature"); 369 : 370 38 : var_params.set<bool>("use_displaced_mesh") = true; 371 : 372 114 : _problem->addUserObject("GapFluxModelRadiation", 373 38 : "gap_flux_model_radiation_object_" + MooseUtils::shortName(name()), 374 : var_params); 375 38 : } 376 : } 377 38 : } 378 : 379 : void 380 1140 : MortarGapHeatTransferAction::checkForExistingSubdomains() 381 : { 382 2280 : if (parameters().isParamSetByUser("primary_subdomain") && 383 1425 : parameters().isParamSetByUser("secondary_subdomain")) 384 : { 385 285 : _user_provided_mortar_meshes = true; 386 : } 387 : else 388 855 : _user_provided_mortar_meshes = false; 389 1140 : }