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 "libmesh/libmesh_config.h" 11 : 12 : #ifdef LIBMESH_HAVE_FPARSER 13 : 14 : #include "Terminator.h" 15 : #include "MooseApp.h" 16 : #include "MooseEnum.h" 17 : #include "Executioner.h" 18 : 19 : registerMooseObject("MooseApp", Terminator); 20 : 21 : InputParameters 22 14559 : Terminator::validParams() 23 : { 24 14559 : InputParameters params = GeneralUserObject::validParams(); 25 14559 : params.addClassDescription("Requests termination of the current solve based on the evaluation of" 26 : " a parsed logical expression of the Postprocessor value(s)."); 27 14559 : params.addRequiredCustomTypeParam<std::string>( 28 : "expression", 29 : "FunctionExpression", 30 : "FParser expression to process Postprocessor values into a boolean value. " 31 : "Termination of the simulation occurs when this returns true."); 32 14559 : MooseEnum failModeOption("HARD SOFT NONE", "HARD"); 33 14559 : params.addParam<MooseEnum>( 34 : "fail_mode", 35 : failModeOption, 36 : "Abort entire simulation (HARD), just the current time step (SOFT), or not at all (NONE)."); 37 14559 : params.addParam<std::string>("message", 38 : "An optional message to be output instead of the default message " 39 : "when the termination condition is triggered"); 40 : 41 14559 : MooseEnum errorLevel("INFO WARNING ERROR NONE", "INFO"); 42 14559 : errorLevel.addDocumentation("INFO", "Output an information message once."); 43 14559 : errorLevel.addDocumentation("WARNING", "Output a warning message once."); 44 14559 : errorLevel.addDocumentation("ERROR", 45 : "Throw a MOOSE error, resulting in the termination of the run."); 46 14559 : errorLevel.addDocumentation("NONE", "No message will be printed."); 47 14559 : params.addParam<MooseEnum>( 48 : "error_level", 49 : errorLevel, 50 : "The error level for the message. A level of ERROR will always lead to a hard " 51 : "termination of the entire simulation."); 52 29118 : return params; 53 14559 : } 54 : 55 150 : Terminator::Terminator(const InputParameters & parameters) 56 : : GeneralUserObject(parameters), 57 150 : _fail_mode(getParam<MooseEnum>("fail_mode").getEnum<FailMode>()), 58 150 : _msg_type(getParam<MooseEnum>("error_level").getEnum<MessageType>()), 59 150 : _pp_names(), 60 150 : _pp_values(), 61 150 : _expression(getParam<std::string>("expression")), 62 300 : _fp() 63 : { 64 : // sanity check the parameters 65 150 : if (_msg_type == MessageType::ERROR && _fail_mode != FailMode::HARD) 66 4 : paramError("error_level", 67 : "Setting the error level to ERROR always causes a hard failure, which is " 68 : "incompatible with `fail_mode=SOFT or NONE`."); 69 146 : if (_msg_type == MessageType::NONE && isParamValid("message")) 70 4 : paramError("error_level", 71 : "Cannot specify `error_level=NONE` together with the `message` parameter."); 72 142 : if (_msg_type == MessageType::NONE && _fail_mode == FailMode::NONE) 73 0 : paramWarning("error_level", 74 : "With the current error level and fail mode settings, the terminator will not " 75 : "error or output."); 76 : 77 : // build the expression object 78 142 : if (_fp.ParseAndDeduceVariables(_expression, _pp_names) >= 0) 79 0 : mooseError(std::string("Invalid function\n" + _expression + "\nin Terminator.\n") + 80 : _fp.ErrorMsg()); 81 : 82 142 : _pp_num = _pp_names.size(); 83 142 : _pp_values.resize(_pp_num); 84 : 85 : // get all necessary postprocessors 86 284 : for (unsigned int i = 0; i < _pp_num; ++i) 87 142 : _pp_values[i] = &getPostprocessorValueByName(_pp_names[i]); 88 : 89 142 : _params.resize(_pp_num); 90 142 : } 91 : 92 : void 93 142 : Terminator::initialSetup() 94 : { 95 : // Check execution schedule of the postprocessors 96 142 : if (_fail_mode == FailMode::SOFT || _fail_mode == FailMode::NONE) 97 92 : for (const auto i : make_range(_pp_num)) 98 : // Make sure the postprocessor is executed at least as often 99 : { 100 48 : const auto & pp_exec = _fe_problem.getUserObjectBase(_pp_names[i], _tid).getExecuteOnEnum(); 101 92 : for (const auto & flag : getExecuteOnEnum()) 102 48 : if (!pp_exec.isValueSet(flag) && flag != EXEC_FINAL) 103 4 : paramWarning("expression", 104 4 : "Postprocessor '" + _pp_names[i] + "' is not executed on " + flag.name() + 105 : ", which it really should be to serve in the criterion " 106 : "expression for throwing."); 107 : } 108 138 : } 109 : 110 : void 111 275 : Terminator::handleMessage() 112 : { 113 275 : std::string message; 114 275 : if (!isParamValid("message")) 115 : { 116 253 : message = "Terminator '" + name() + "' is "; 117 253 : if (_fail_mode == FailMode::HARD) 118 11 : message += "causing the execution to terminate.\n"; 119 242 : else if (_fail_mode == FailMode::SOFT) 120 176 : message += "causing a time step cutback by marking the current step as failed.\n"; 121 : else 122 66 : message += "outputting a message due to the criterion being met"; 123 : } 124 : else 125 22 : message = getParam<std::string>("message"); 126 : 127 275 : switch (_msg_type) 128 : { 129 267 : case MessageType::INFO: 130 267 : mooseInfoRepeated(message); 131 267 : break; 132 : 133 4 : case MessageType::WARNING: 134 4 : mooseWarning(message); 135 4 : break; 136 : 137 4 : case MessageType::ERROR: 138 4 : mooseError(message); 139 : break; 140 : 141 0 : default: 142 0 : break; 143 : } 144 271 : } 145 : 146 : void 147 2419 : Terminator::execute() 148 : { 149 : // copy current Postprocessor values into the FParser parameter buffer 150 4838 : for (unsigned int i = 0; i < _pp_num; ++i) 151 2419 : _params[i] = *(_pp_values[i]); 152 : 153 : // request termination of the run or timestep in case the expression evaluates to true 154 2419 : if (_fp.Eval(_params.data()) != 0) 155 : { 156 275 : handleMessage(); 157 271 : if (_fail_mode == FailMode::HARD) 158 21 : _fe_problem.terminateSolve(); 159 250 : else if (_fail_mode == FailMode::SOFT) 160 : { 161 : // Within a nonlinear solve, trigger a solve fail 162 280 : if (_fe_problem.getCurrentExecuteOnFlag() == EXEC_LINEAR || 163 96 : _fe_problem.getCurrentExecuteOnFlag() == EXEC_NONLINEAR) 164 88 : _fe_problem.setFailNextNonlinearConvergenceCheck(); 165 : // Outside of a solve, trigger a time step fail 166 : else 167 96 : getMooseApp().getExecutioner()->fixedPointSolve().failStep(); 168 : } 169 : } 170 2415 : } 171 : 172 : #endif