LCOV - code coverage report
Current view: top level - src/outputs - JSONOutput.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 120 123 97.6 %
Date: 2025-08-08 20:01:16 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       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 : }

Generated by: LCOV version 1.14