LCOV - code coverage report
Current view: top level - src/outputs - JSONOutput.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 120 123 97.6 %
Date: 2025-07-17 01:28:37 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       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 : }

Generated by: LCOV version 1.14