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 : // Moose includes 11 : #include "JSONOutput.h" 12 : #include "FEProblem.h" 13 : #include "MooseApp.h" 14 : #include "JsonIO.h" 15 : #include "Reporter.h" 16 : #include "TheWarehouse.h" 17 : #include "CommonOutputAction.h" 18 : 19 : registerMooseObjectAliased("MooseApp", JSONOutput, "JSON"); 20 : 21 : InputParameters 22 18435 : JSONOutput::validParams() 23 : { 24 18435 : InputParameters params = AdvancedOutput::validParams(); 25 36870 : params += AdvancedOutput::enableOutputTypes("system_information reporter"); 26 36870 : params.addClassDescription("Output for Reporter values using JSON format."); 27 36870 : params.set<ExecFlagEnum>("execute_system_information_on", /*quite_mode=*/true) = EXEC_INITIAL; 28 55305 : params.addParam<bool>( 29 36870 : "use_legacy_reporter_output", false, "Use reporter output that does not group by object."); 30 55305 : params.addParam<bool>("one_file_per_timestep", 31 36870 : false, 32 : "Create a unique output file for each time step of the simulation."); 33 73740 : params.addParam<std::vector<ReporterName>>( 34 : "reporters", "Specific reporter values to output; if not set, all will be output"); 35 36870 : params.addParam<bool>( 36 36870 : "distributed", true, "Whether or not to output distributed data (data not on rank 0)"); 37 18435 : return params; 38 0 : } 39 : 40 2036 : JSONOutput::JSONOutput(const InputParameters & parameters) 41 : : AdvancedOutput(parameters), 42 4072 : _reporter_data(_problem_ptr->getReporterData()), 43 4072 : _one_file_per_timestep(getParam<bool>("one_file_per_timestep")), 44 4072 : _reporters(queryParam<std::vector<ReporterName>>("reporters")), 45 6108 : _json(declareRestartableData<nlohmann::json>("json_out_str")) 46 : { 47 2036 : } 48 : 49 : void 50 1995 : JSONOutput::initialSetup() 51 : { 52 1995 : if (_reporters && _reporters->size()) 53 95 : for (const auto & name : *_reporters) 54 56 : if (!_problem_ptr->getReporterData().hasReporterValue(name)) 55 8 : paramError("reporters", "Reporter value '", name, "' was not found"); 56 1991 : } 57 : 58 : std::string 59 5299 : JSONOutput::filename() 60 : { 61 5299 : std::ostringstream file_name; 62 5299 : file_name << _file_base; 63 : 64 5299 : if (_one_file_per_timestep) 65 49 : file_name << '_' << std::setw(_padding) << std::setprecision(0) << std::setfill('0') 66 49 : << std::right << timeStep(); 67 : 68 5299 : if (processor_id() > 0) 69 : { 70 773 : int digits = MooseUtils::numDigits(n_processors()); 71 : file_name << ".json" 72 773 : << "." << std::setw(digits) << std::setfill('0') << processor_id(); 73 : } 74 : else 75 4526 : file_name << ".json"; 76 : 77 10598 : return file_name.str(); 78 5299 : } 79 : 80 : void 81 821 : JSONOutput::outputSystemInformation() 82 : { 83 821 : nlohmann::to_json(_json, _app); 84 821 : } 85 : 86 : void 87 3022 : JSONOutput::timestepSetup() 88 : { 89 3022 : AdvancedOutput::timestepSetup(); 90 3022 : if (_one_file_per_timestep) 91 36 : _json.clear(); 92 3022 : } 93 : 94 : void 95 3081 : JSONOutput::outputReporters() 96 : { 97 : // Get the reporter values that we should skip. The common output action can 98 : // add JSON output objects for specific reporters, of which we don't want to 99 : // include within the standard json output 100 3081 : std::set<ReporterName> skip_names; 101 3081 : const auto common_actions = _app.actionWarehouse().getActions<CommonOutputAction>(); 102 3081 : if (common_actions.size()) 103 : { 104 : mooseAssert(common_actions.size() == 1, "Should not be more than one"); 105 3081 : const auto & action = *common_actions[0]; 106 3081 : const auto & common_names = action.getCommonReporterNames(); 107 3081 : skip_names.insert(common_names.begin(), common_names.end()); 108 : } 109 : 110 : // Set of ReporterNames for output 111 3081 : std::set<ReporterName> r_names; 112 11763 : for (const std::string & c_name : getReporterOutput()) 113 : { 114 8682 : const ReporterName r_name(c_name); 115 : 116 : // If specific values are requested, skip anything that isn't that value 117 8682 : if (_reporters) 118 : { 119 120 : if (std::find(_reporters->begin(), _reporters->end(), r_name) == _reporters->end()) 120 48 : continue; 121 : } 122 : // If all values are requested, skip those that should be skipped 123 8562 : else if (skip_names.count(r_name)) 124 48 : continue; 125 : 126 8586 : r_names.emplace(r_name); 127 8682 : } 128 : 129 : // Is there ANY distributed data 130 9183 : _has_distributed = getParam<bool>("distributed") && 131 3021 : std::any_of(r_names.begin(), 132 : r_names.end(), 133 6033 : [this](const ReporterName & n) { 134 6033 : return _reporter_data.hasReporterWithMode( 135 6033 : n.getObjectName(), REPORTER_MODE_DISTRIBUTED); 136 : }); 137 3081 : if (processor_id() == 0 || _has_distributed) 138 : { 139 : // Create the current output node 140 2491 : auto & current_node = _json["time_steps"].emplace_back(); 141 : 142 : // Add time/iteration information 143 2491 : current_node["time"] = _problem_ptr->time(); 144 2491 : current_node["time_step"] = _problem_ptr->timeStep(); 145 2491 : if (_execute_enum.isValueSet(EXEC_LINEAR) && !_on_nonlinear_residual) 146 0 : current_node["linear_iteration"] = _linear_iter; 147 2491 : if (_execute_enum.isValueSet(EXEC_NONLINEAR)) 148 0 : current_node["nonlinear_iteration"] = _nonlinear_iter; 149 : 150 : // Inject processor info 151 2491 : if (n_processors() > 1 && _has_distributed) 152 : { 153 456 : _json["part"] = processor_id(); 154 456 : _json["number_of_parts"] = n_processors(); 155 : } 156 : 157 : // Add Reporter values to the current node 158 2491 : auto & r_node = _json["reporters"]; // non-accidental insert 159 9493 : for (const auto & r_name : r_names) 160 : { 161 : // If this value is produced in root mode and we're not on root, don't report this value 162 7002 : const auto & context = _reporter_data.getReporterContextBase(r_name); 163 7002 : if (context.getProducerModeEnum() == REPORTER_MODE_ROOT && processor_id() != 0) 164 279 : continue; 165 : 166 : // Create/get object node 167 6723 : auto obj_node_pair = r_node.emplace(r_name.getObjectName(), nlohmann::json()); 168 6723 : auto & obj_node = *(obj_node_pair.first); 169 : 170 : // Whether or not we should store this Reporter's value or have it be null 171 6723 : bool should_store = true; 172 : 173 : // If the object node was created insert the class level information 174 6723 : if (obj_node_pair.second) 175 : { 176 : // Query the TheWarehouse for all Reporter objects with the given name. The attributes and 177 : // QueryID are used to allow the TheWarehouse::queryInto method be called with the 178 : // "show_all" option set to true. This returns all objects, regardless of "enabled" state, 179 : // which is what is needed to ensure that output always happens, even if an object is 180 : // disabled. 181 1857 : std::vector<Reporter *> objs; 182 1857 : auto attr = _problem_ptr->theWarehouse() 183 1857 : .query() 184 3714 : .condition<AttribInterfaces>(Interfaces::Reporter) 185 1857 : .condition<AttribName>(r_name.getObjectName()) 186 1857 : .attributes(); 187 1857 : auto qid = _problem_ptr->theWarehouse().queryID(attr); 188 1857 : _problem_ptr->theWarehouse().queryInto(qid, objs, true); 189 : 190 : // There can now be multiple reporter objects with the same name, but 191 : // there will only be one reporter that stores all the data. 192 1857 : if (!objs.empty()) 193 : { 194 1687 : auto & reporter = *objs.front(); 195 : 196 : // It is possible to have a Reporter value without a reporter objects (i.e., VPPs, PPs), 197 : // which is why objs can be empty. 198 1687 : reporter.store(obj_node); 199 : 200 : // GeneralReporters have the option to only store JSON data when the execute flag 201 : // matches an execute flag that is within the GeneralReporter; this captures that 202 1687 : should_store = reporter.shouldStore(); 203 : } 204 1857 : } 205 : 206 : // Create/get value node 207 6723 : auto value_node_pair = obj_node["values"].emplace(r_name.getValueName(), nlohmann::json()); 208 : // If the value node was created insert the value information 209 6723 : if (value_node_pair.second) 210 : // Store value meta data 211 4037 : context.storeInfo(*value_node_pair.first); 212 : 213 : // Insert reporter value 214 6723 : auto & node = current_node[r_name.getObjectName()][r_name.getValueName()]; 215 6723 : if (should_store) 216 6711 : context.store(node); 217 : else 218 12 : node = "null"; 219 : } 220 : } 221 3081 : } 222 : 223 : void 224 4160 : JSONOutput::output() 225 : { 226 4160 : _has_distributed = false; 227 4160 : AdvancedOutput::output(); 228 4160 : if (processor_id() == 0 || _has_distributed) 229 : { 230 3304 : std::ofstream out(filename().c_str()); 231 3304 : out << std::setw(4) << _json << std::endl; 232 3304 : } 233 4160 : } 234 : 235 : template <> 236 : void 237 461 : dataStore(std::ostream & stream, nlohmann::json & json, void * /*context*/) 238 : { 239 461 : stream << json; 240 461 : } 241 : 242 : template <> 243 : void 244 149 : dataLoad(std::istream & stream, nlohmann::json & json, void * /*context*/) 245 : { 246 149 : stream >> json; 247 149 : }