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 17993 : JSONOutput::validParams() 23 : { 24 17993 : InputParameters params = AdvancedOutput::validParams(); 25 17993 : params += AdvancedOutput::enableOutputTypes("system_information reporter"); 26 17993 : params.addClassDescription("Output for Reporter values using JSON format."); 27 17993 : params.set<ExecFlagEnum>("execute_system_information_on", /*quite_mode=*/true) = EXEC_INITIAL; 28 53979 : params.addParam<bool>( 29 35986 : "use_legacy_reporter_output", false, "Use reporter output that does not group by object."); 30 53979 : params.addParam<bool>("one_file_per_timestep", 31 35986 : false, 32 : "Create a unique output file for each time step of the simulation."); 33 17993 : params.addParam<std::vector<ReporterName>>( 34 : "reporters", "Specific reporter values to output; if not set, all will be output"); 35 53979 : params.addParam<bool>( 36 35986 : "distributed", true, "Whether or not to output distributed data (data not on rank 0)"); 37 17993 : return params; 38 0 : } 39 : 40 1816 : JSONOutput::JSONOutput(const InputParameters & parameters) 41 : : AdvancedOutput(parameters), 42 3632 : _reporter_data(_problem_ptr->getReporterData()), 43 1816 : _one_file_per_timestep(getParam<bool>("one_file_per_timestep")), 44 1816 : _reporters(queryParam<std::vector<ReporterName>>("reporters")), 45 3632 : _json(declareRestartableData<nlohmann::json>("json_out_str")) 46 : { 47 1816 : } 48 : 49 : void 50 1779 : JSONOutput::initialSetup() 51 : { 52 1779 : if (_reporters && _reporters->size()) 53 88 : for (const auto & name : *_reporters) 54 52 : if (!_problem_ptr->getReporterData().hasReporterValue(name)) 55 4 : paramError("reporters", "Reporter value '", name, "' was not found"); 56 1775 : } 57 : 58 : std::string 59 4592 : JSONOutput::filename() 60 : { 61 4592 : std::ostringstream file_name; 62 4592 : file_name << _file_base; 63 : 64 4592 : if (_one_file_per_timestep) 65 44 : file_name << '_' << std::setw(_padding) << std::setprecision(0) << std::setfill('0') 66 44 : << std::right << timeStep(); 67 : 68 4592 : if (processor_id() > 0) 69 : { 70 719 : int digits = MooseUtils::numDigits(n_processors()); 71 : file_name << ".json" 72 719 : << "." << std::setw(digits) << std::setfill('0') << processor_id(); 73 : } 74 : else 75 3873 : file_name << ".json"; 76 : 77 9184 : return file_name.str(); 78 4592 : } 79 : 80 : void 81 726 : JSONOutput::outputSystemInformation() 82 : { 83 726 : nlohmann::to_json(_json, _app); 84 726 : } 85 : 86 : void 87 2674 : JSONOutput::timestepSetup() 88 : { 89 2674 : AdvancedOutput::timestepSetup(); 90 2674 : if (_one_file_per_timestep) 91 33 : _json.clear(); 92 2674 : } 93 : 94 : void 95 2638 : 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 2638 : std::set<ReporterName> skip_names; 101 2638 : const auto common_actions = _app.actionWarehouse().getActions<CommonOutputAction>(); 102 2638 : if (common_actions.size()) 103 : { 104 : mooseAssert(common_actions.size() == 1, "Should not be more than one"); 105 2638 : const auto & action = *common_actions[0]; 106 2638 : const auto & common_names = action.getCommonReporterNames(); 107 2638 : skip_names.insert(common_names.begin(), common_names.end()); 108 : } 109 : 110 : // Set of ReporterNames for output 111 2638 : std::set<ReporterName> r_names; 112 10165 : for (const std::string & c_name : getReporterOutput()) 113 : { 114 7527 : const ReporterName r_name(c_name); 115 : 116 : // If specific values are requested, skip anything that isn't that value 117 7527 : if (_reporters) 118 : { 119 110 : if (std::find(_reporters->begin(), _reporters->end(), r_name) == _reporters->end()) 120 44 : continue; 121 : } 122 : // If all values are requested, skip those that should be skipped 123 7417 : else if (skip_names.count(r_name)) 124 44 : continue; 125 : 126 7439 : r_names.emplace(r_name); 127 7527 : } 128 : 129 : // Is there ANY distributed data 130 5222 : _has_distributed = getParam<bool>("distributed") && 131 2584 : std::any_of(r_names.begin(), 132 : r_names.end(), 133 5169 : [this](const ReporterName & n) { 134 5169 : return _reporter_data.hasReporterWithMode( 135 5169 : n.getObjectName(), REPORTER_MODE_DISTRIBUTED); 136 : }); 137 2638 : if (processor_id() == 0 || _has_distributed) 138 : { 139 : // Create the current output node 140 2098 : auto & current_node = _json["time_steps"].emplace_back(); 141 : 142 : // Add time/iteration information 143 2098 : current_node["time"] = _problem_ptr->time(); 144 2098 : current_node["time_step"] = _problem_ptr->timeStep(); 145 2098 : if (_execute_enum.isValueSet(EXEC_LINEAR) && !_on_nonlinear_residual) 146 0 : current_node["linear_iteration"] = _linear_iter; 147 2098 : if (_execute_enum.isValueSet(EXEC_NONLINEAR)) 148 0 : current_node["nonlinear_iteration"] = _nonlinear_iter; 149 : 150 : // Inject processor info 151 2098 : if (n_processors() > 1 && _has_distributed) 152 : { 153 418 : _json["part"] = processor_id(); 154 418 : _json["number_of_parts"] = n_processors(); 155 : } 156 : 157 : // Add Reporter values to the current node 158 2098 : auto & r_node = _json["reporters"]; // non-accidental insert 159 8087 : 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 5989 : const auto & context = _reporter_data.getReporterContextBase(r_name); 163 5989 : if (context.getProducerModeEnum() == REPORTER_MODE_ROOT && processor_id() != 0) 164 248 : continue; 165 : 166 : // Create/get object node 167 5741 : auto obj_node_pair = r_node.emplace(r_name.getObjectName(), nlohmann::json()); 168 5741 : 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 5741 : bool should_store = true; 172 : 173 : // If the object node was created insert the class level information 174 5741 : 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 1604 : std::vector<Reporter *> objs; 182 1604 : auto attr = _problem_ptr->theWarehouse() 183 1604 : .query() 184 3208 : .condition<AttribInterfaces>(Interfaces::Reporter) 185 1604 : .condition<AttribName>(r_name.getObjectName()) 186 1604 : .attributes(); 187 1604 : auto qid = _problem_ptr->theWarehouse().queryID(attr); 188 1604 : _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 1604 : if (!objs.empty()) 193 : { 194 1460 : 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 1460 : 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 1460 : should_store = reporter.shouldStore(); 203 : } 204 1604 : } 205 : 206 : // Create/get value node 207 5741 : 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 5741 : if (value_node_pair.second) 210 : // Store value meta data 211 3490 : context.storeInfo(*value_node_pair.first); 212 : 213 : // Insert reporter value 214 5741 : auto & node = current_node[r_name.getObjectName()][r_name.getValueName()]; 215 5741 : if (should_store) 216 5730 : context.store(node); 217 : else 218 11 : node = "null"; 219 : } 220 : } 221 2638 : } 222 : 223 : void 224 3603 : JSONOutput::output() 225 : { 226 3603 : _has_distributed = false; 227 3603 : AdvancedOutput::output(); 228 3603 : if (processor_id() == 0 || _has_distributed) 229 : { 230 2813 : std::ofstream out(filename().c_str()); 231 2813 : out << std::setw(4) << _json << std::endl; 232 2813 : } 233 3603 : } 234 : 235 : template <> 236 : void 237 426 : dataStore(std::ostream & stream, nlohmann::json & json, void * /*context*/) 238 : { 239 426 : stream << json; 240 426 : } 241 : 242 : template <> 243 : void 244 145 : dataLoad(std::istream & stream, nlohmann::json & json, void * /*context*/) 245 : { 246 145 : stream >> json; 247 145 : }