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 : #pragma once 11 : 12 : #include "Executioner.h" 13 : #include "ExecutorInterface.h" 14 : 15 : #include <string> 16 : 17 : class Problem; 18 : class Executor; 19 : 20 : /// The Executor class directs the execution flow of simulations. It manages 21 : /// outputting, time stepping, mesh adaptivity, solve sequencing, multiapp 22 : /// execution, etc. Users can compose executor objects in an input file using 23 : /// the same pattern as with mesh generators. Subclass here and implement your 24 : /// own run function if you need special or fancy functionality that 25 : /// isn't supported by the default executors available. 26 : class Executor : public Executioner, public ExecutorInterface 27 : { 28 : public: 29 : /// This object tracks the success/failure state of the executor system as 30 : /// execution proceeds in a simulation. Because executors can be composed into 31 : /// trees, result objects are correspondingly composed into trees to track 32 : /// simulation progress. Result objects should generally be created by 33 : /// executors calling the Result::newResult() function rather than by using 34 : /// the Result constructor directly. 35 : struct Result 36 : { 37 110 : Result() : converged(true), _name("NO_NAME") {} 38 3 : Result(const std::string & name) : converged(true), _name(name) {} 39 40 : Result(const MooseObject * obj) : converged(true), _name(obj->name()) {} 40 : 41 : /// whether or not a executor ran its code successfully - only reports 42 : /// results from the executor itself. If a sub/internal executor of a executor 43 : /// fails, that sub-executor's result object will have converged=false, while 44 : /// the parent may still have converged=truee. Users should use convergedAll 45 : /// to recursively determine if there was any descendant executor failure. 46 : bool converged = true; 47 : 48 : /// Optional message detailing why an executor passed or failed (i.e. failed to converge). 49 : std::string reason; 50 : 51 : /// Maps a name/label of a executor's internal/sub executors to the result 52 : /// object returned by running each of those internal/sub executors. This 53 : /// member should generally not be accessed directly. It should generally 54 : /// be populated through the record function. Info contained in these 55 : /// results will be included in printouts from the str function. 56 : std::map<std::string, Result> subs; 57 : 58 : /// Prints a full recursive output of this result object - including all 59 : /// descendant's results. If success_msg is true, then all result output 60 : /// that contains a message will be printed even if it converged/passed. 61 : /// Otherwise, messages will only be printed for unconverged/failed results. 62 : std::string 63 6 : str(bool success_msg = false, const std::string & indent = "", const std::string & subname = "") 64 : { 65 6 : std::string s = indent + label(success_msg, subname) + "\n"; 66 10 : for (auto & entry : subs) 67 4 : s += entry.second.str(success_msg, indent + " ", entry.first); 68 6 : return s; 69 0 : } 70 : 71 : /// Marks the result as passing/converged with the given msg text 72 : /// describing detail about how things ran. A result object is in this 73 : /// state by default, so it is not necessary to call this function for 74 : /// converged/passing scenarios. 75 1 : void pass(const std::string & msg, bool overwrite = false) 76 : { 77 : mooseAssert(converged || overwrite, 78 : "cannot override nonconverged executioner result with a passing one"); 79 : ((void)(overwrite)); // avoid unused error due to assert 80 1 : reason = msg; 81 1 : converged = true; 82 1 : } 83 : 84 : /// Marks the result as failing/unconverged with the given msg text 85 : /// describing detail about how things ran. 86 1 : void fail(const std::string & msg) 87 : { 88 1 : reason = msg; 89 1 : converged = false; 90 1 : } 91 : 92 : /// Records results from sub/internal executors in a executor's result. When 93 : /// child-executors return a result object following their execution, this 94 : /// function should be called to add that info into the result hierarchy. 95 : /// If the child executor was identified by a label/text from the input file 96 : /// (e.g. via sub_solve1=foo_executor) - then "name" should be "sub_solve1". 97 22 : bool record(const std::string & name, const Result & r) 98 : { 99 22 : subs[name] = r; 100 22 : return r.convergedAll(); 101 : } 102 : 103 : /// Returns false if any single executor in the current hierarchy of results 104 : /// (i.e. including all child results accumulated recursively via record) 105 : /// had a failed/unconverged return state. Returns true otherwise. This 106 : /// is how convergence should generally be checked/tracked by executors - 107 : /// rather than accessing e.g. the converged member directly. 108 62 : bool convergedAll() const 109 : { 110 62 : if (!converged) 111 1 : return false; 112 81 : for (auto & entry : subs) 113 20 : if (!entry.second.convergedAll()) 114 0 : return false; 115 61 : return true; 116 : } 117 : 118 : private: 119 6 : std::string label(bool success_msg, const std::string & subname = "") 120 : { 121 : std::string state_str = 122 10 : success_msg || !converged ? (std::string("(") + (converged ? "pass" : "FAIL") + ")") : ""; 123 12 : return subname + (subname.empty() ? "" : ":") + _name + state_str + 124 24 : ((success_msg || !converged) && !reason.empty() ? ": " + reason : ""); 125 6 : } 126 : std::string _name; 127 : }; 128 : 129 : Executor(const InputParameters & parameters); 130 : 131 60 : virtual ~Executor() {} 132 : 133 : static InputParameters validParams(); 134 : 135 : /// This is the main function for executors - this is how executors should 136 : /// invoke child/sub executors - by calling their exec function. 137 : Result exec(); 138 : 139 0 : virtual void execute() override final {} 140 : 141 : /// Executors need to return a Result object describing how execution went - 142 : /// rather than constructing Result objects directly, the newResult function 143 : /// should be called to generate new objects. *DO NOT* catch the result by 144 : /// value - if you do, MOOSE cannot track result state for restart capability. 145 : /// You must catch result values from this function by reference. 146 40 : Result & newResult() 147 : { 148 40 : _result = Result(this); 149 40 : return _result; 150 : } 151 : 152 : /// Whether the executor and all its sub-executors passed / converged 153 0 : virtual bool lastSolveConverged() const override { return _result.convergedAll(); } 154 : 155 : protected: 156 : /// This function contains the primary execution implementation for a 157 : /// executor. Custom executor behavior should be localized to this function. If 158 : /// you are writing a executor - this is basically where you should put all your 159 : /// code. 160 : virtual Result run() = 0; 161 : 162 : /// The execute-on flag to associate with the beginning of this executor's execution. 163 : /// This allows the framework and users to trigger other object execution by 164 : /// associating other objects with this flag. 165 : ExecFlagType _begin_flag; 166 : /// The execute-on flag to associate with the end of this executor's execution. 167 : /// This allows the framework and users to trigger other object execution by 168 : /// associating other objects with this flag. 169 : ExecFlagType _end_flag; 170 : 171 : private: 172 : /// Stores the result representing the outcome from the run function. It is 173 : /// a local member variable here to facilitate restart capability. 174 : Result _result; 175 : };