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

Generated by: LCOV version 1.14