LCOV - code coverage report
Current view: top level - src/outputs - JSONOutput.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 118 123 95.9 %
Date: 2026-05-29 20:35:17 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14