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