ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
RadiationDiagnostics Class Reference

CSV logger and debug-print manager for radiation module. More...

#include <ERF_RadiationDiagnostics.H>

Collaboration diagram for RadiationDiagnostics:

Public Member Functions

 RadiationDiagnostics (int verbosity, const std::string &diag_file, int amr_level, bool diag_enable=true, bool diag_stdout_enable=true, bool diag_tagged_enable=true, bool diag_regtest_line_enable=true, bool diag_csv_enable=true, const std::string &diag_callsite_mode="both", amrex::Real diag_dedup_tol=1.0e-12)
 Constructor. More...
 
 ~RadiationDiagnostics ()
 Destructor. More...
 
void append (int step, amrex::Real time, std::string const &call_site, amrex::Real sw_surface, amrex::Real sw_toa, amrex::Real sw_up_toa, amrex::Real lw_net_surface, amrex::Real lw_up_toa, amrex::Real heating_rate_max, amrex::Real seb_residual_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real seb_residual_max=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real t_s_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real t_s_max=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real q_s_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real q_s_max=std::numeric_limits< amrex::Real >::quiet_NaN())
 Append a diagnostic row to the CSV file. More...
 

Private Member Functions

void write_header_if_needed ()
 Write CSV header if file is new or doesn't exist. More...
 

Private Attributes

int m_verbosity
 Debug verbosity level. More...
 
std::string m_diag_file
 Output CSV file path. More...
 
int m_amr_level = 0
 AMR level this instance reports for. More...
 
int m_last_write_step = -1
 Guard against duplicate writes. More...
 
bool m_header_written = false
 Track whether CSV header was written. More...
 
std::string m_last_write_call_site = ""
 
amrex::Real m_last_write_time = -1.0
 
bool m_diag_enable = true
 
bool m_diag_stdout_enable = true
 
bool m_diag_tagged_enable = true
 
bool m_diag_regtest_line_enable = true
 
bool m_diag_csv_enable = true
 
std::string m_diag_callsite_mode = "both"
 
amrex::Real m_diag_dedup_tol = 1.0e-12
 

Detailed Description

CSV logger and debug-print manager for radiation module.

Manages output of radiation diagnostics including:

  • Tagged debug prints (when verbosity >= 1)
  • CSV rows for flux/heating diagnostics
  • RADIATION_DIAG: lines for RegTest parsing

Dedup Contract:

Each AMR level owns its own RadiationDiagnostics, so the identity below is implicitly per level: two levels reporting the same step, call_site and time are distinct instances and neither suppresses the other. All instances append to the same CSV, which carries a level column to tell the rows apart.

Duplicate detection uses a 3-tuple identity (step, call_site, time):

  • step: timestep number (int)
  • call_site: diagnostic call-site label (e.g., "pre_dycore", "post_dycore")
  • time: simulation time with tolerance m_diag_dedup_tol

This ensures:

  1. Accidental repeated calls at the same (step, call_site, time) are suppressed.
  2. Legitimate pre_dycore + post_dycore entries at the same step are both retained (they have different call_site values, so dedup identity differs).
  3. Mode filtering (pre_only, post_only, both) preserves this contract by checking whether to emit BEFORE the dedup guard runs.

Follows the duplicate-write-guard pattern from UCMDiagnostics.

Constructor & Destructor Documentation

◆ RadiationDiagnostics()

RadiationDiagnostics::RadiationDiagnostics ( int  verbosity,
const std::string &  diag_file,
int  amr_level,
bool  diag_enable = true,
bool  diag_stdout_enable = true,
bool  diag_tagged_enable = true,
bool  diag_regtest_line_enable = true,
bool  diag_csv_enable = true,
const std::string &  diag_callsite_mode = "both",
amrex::Real  diag_dedup_tol = 1.0e-12 
)

Constructor.

Parameters
[in]verbosityDebug verbosity level (0=off, 1+=print).
[in]diag_fileOutput CSV file path (relative to working directory).
[in]amr_levelAMR level this instance reports for. Written as the level CSV column and tagged onto the stdout lines. One instance per level, so the duplicate guard below is per level and a fine level's row is never mistaken for a coarse one.
[in]diag_enableMaster switch for all diagnostics.
[in]diag_stdout_enableEnable human-readable stdout block.
[in]diag_tagged_enableEnable tagged debug lines.
[in]diag_regtest_line_enableEnable RADIATION_DIAG: lines.
[in]diag_csv_enableEnable CSV file writes.
[in]diag_callsite_modeCall-site filter: "both", "pre_only", "post_only".
[in]diag_dedup_tolTolerance for time equality in duplicate guard.
22  : m_verbosity(verbosity), m_diag_file(diag_file), m_amr_level(amr_level),
23  m_diag_enable(diag_enable), m_diag_stdout_enable(diag_stdout_enable),
24  m_diag_tagged_enable(diag_tagged_enable),
25  m_diag_regtest_line_enable(diag_regtest_line_enable),
26  m_diag_csv_enable(diag_csv_enable), m_diag_callsite_mode(diag_callsite_mode),
27  m_diag_dedup_tol(diag_dedup_tol)
28 {
29  // Constructor: nothing special needed
30  // write_header_if_needed() is called on first append()
31 }
bool m_diag_tagged_enable
Definition: ERF_RadiationDiagnostics.H:165
bool m_diag_regtest_line_enable
Definition: ERF_RadiationDiagnostics.H:166
int m_verbosity
Debug verbosity level.
Definition: ERF_RadiationDiagnostics.H:154
bool m_diag_stdout_enable
Definition: ERF_RadiationDiagnostics.H:164
std::string m_diag_file
Output CSV file path.
Definition: ERF_RadiationDiagnostics.H:155
int m_amr_level
AMR level this instance reports for.
Definition: ERF_RadiationDiagnostics.H:156
std::string m_diag_callsite_mode
Definition: ERF_RadiationDiagnostics.H:168
amrex::Real m_diag_dedup_tol
Definition: ERF_RadiationDiagnostics.H:169
bool m_diag_csv_enable
Definition: ERF_RadiationDiagnostics.H:167
bool m_diag_enable
Definition: ERF_RadiationDiagnostics.H:163

◆ ~RadiationDiagnostics()

RadiationDiagnostics::~RadiationDiagnostics ( )

Destructor.

34 {
35  // Destructor: nothing special needed
36  // File handles are closed by ofstream RAII
37 }

Member Function Documentation

◆ append()

void RadiationDiagnostics::append ( int  step,
amrex::Real  time,
std::string const &  call_site,
amrex::Real  sw_surface,
amrex::Real  sw_toa,
amrex::Real  sw_up_toa,
amrex::Real  lw_net_surface,
amrex::Real  lw_up_toa,
amrex::Real  heating_rate_max,
amrex::Real  seb_residual_mean = std::numeric_limits<amrex::Real>::quiet_NaN(),
amrex::Real  seb_residual_max = std::numeric_limits<amrex::Real>::quiet_NaN(),
amrex::Real  t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN(),
amrex::Real  t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN(),
amrex::Real  q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN(),
amrex::Real  q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN() 
)

Append a diagnostic row to the CSV file.

Records a row of radiation diagnostics for the current timestep:

CSV columns (order): step, time, call_site, SW_surface, SW_TOA, SW_up_TOA, LW_net_surface, LW_up_TOA, heating_rate_max [optional]: SEB_residual_mean, SEB_residual_max (only if seb_diagnostic_enable=true) [optional]: T_s_mean, T_s_max, q_s_mean, q_s_max level – appended last, following this file's rule that new columns go at the end so existing header-name parsers keep working.

Also prints tagged debug output and RADIATION_DIAG: line for RegTest parsing.

Guards against duplicate writes (uses m_last_write_step).

Parameters
[in]stepCurrent timestep number.
[in]timeCurrent simulation time [s].
[in]call_siteDiagnostic call-site label (e.g., "pre_dycore", "post_dycore").
[in]SW_surfaceSW absorbed by the surface, (1 - albedo) * incident [W/m^2].
[in]SW_TOAIncident SW at the top of the atmosphere [W/m^2].
[in]SW_up_TOAReflected SW leaving the top of the atmosphere [W/m^2].
[in]LW_net_surfaceNet LW (up - down) at the surface [W/m^2].
[in]LW_up_TOAOutgoing LW at the top of the atmosphere [W/m^2].
[in]heating_rate_maxMaximum heating rate magnitude [K/s].
[in]seb_residual_mean[optional] Mean SEB residual [W/m^2]; if non-finite, not written.
[in]seb_residual_max[optional] Max SEB residual [W/m^2]; if non-finite, not written.
[in]t_s_mean[optional] Mean surface temperature [K]; if non-finite, written as NaN.
[in]t_s_max[optional] Max surface temperature [K]; if non-finite, written as NaN.
[in]q_s_mean[optional] Mean surface moisture [kg/kg]; if non-finite, written as NaN.
[in]q_s_max[optional] Max surface moisture [kg/kg]; if non-finite, written as NaN.
89 {
90  // Master enable gate
91  if (!m_diag_enable) {
92  return;
93  }
94 
95  // Call-site mode filtering
96  bool should_emit_pre = (m_diag_callsite_mode == "both" || m_diag_callsite_mode == "pre_only");
97  bool should_emit_post = (m_diag_callsite_mode == "both" || m_diag_callsite_mode == "post_only");
98 
99  bool is_pre_site = (call_site.find("pre") != std::string::npos);
100  bool is_post_site = (call_site.find("post") != std::string::npos);
101 
102  // Check if this call site should be filtered based on mode
103  if ((is_pre_site && !should_emit_pre) || (is_post_site && !should_emit_post)) {
104  return;
105  }
106 
107  // Guard against duplicate writes using 3-tuple identity:
108  // (step, call_site, time)
109  // One instance per AMR level, so this state is per level and a fine level
110  // never suppresses a coarse level's row at the same step and time.
111  // This ensures:
112  // 1. Accidental repeated calls at same (step, call_site, time) are suppressed.
113  // 2. Legitimate pre_dycore + post_dycore entries at same step both retained
114  // (different call_site values, so identity tuple differs).
115  // 3. Mode filtering above already runs first, so pre_only/post_only modes
116  // naturally prevent unwanted entries from reaching this dedup logic.
117  //
118  // Time tolerance (m_diag_dedup_tol) accounts for floating-point rounding when
119  // multiple diagnostics functions are called at effectively the same time.
120  if (step == m_last_write_step &&
121  call_site == m_last_write_call_site &&
122  std::abs(time - m_last_write_time) < m_diag_dedup_tol) {
123  return; // Duplicate detected; skip write
124  }
125  m_last_write_step = step;
126  m_last_write_call_site = call_site;
127  m_last_write_time = time;
128 
129  // Print debug output (if verbosity >= 1, and IOProcessor only)
130  //
131  // The bracketed tag is the module-generic "[RAD]" plus the call site, so
132  // it stays accurate and grepable no matter which radiation feature is
133  // driving the call.
134  if (m_verbosity >= 1 && amrex::ParallelDescriptor::IOProcessor() &&
136  amrex::Print() << "[RAD][RadiationDiagnostics::append] level=" << m_amr_level
137  << " step=" << step
138  << " time=" << time
139  << " call_site=" << call_site
140  << " SW_surface=" << SW_surface
141  << " SW_TOA=" << SW_TOA << " SW_up_TOA=" << SW_up_TOA
142  << " LW_net_surface=" << LW_net_surface
143  << " LW_up_TOA=" << LW_up_TOA
144  << " heating_rate_max=" << heating_rate_max << "\n";
145  }
146 
147  // Print RADIATION_DIAG line (for RegTest scripts)
148  if (amrex::ParallelDescriptor::IOProcessor() && m_diag_regtest_line_enable &&
150  amrex::Print() << "RADIATION_DIAG: level=" << m_amr_level
151  << " step=" << step << " time=" << time
152  << " call_site=" << call_site
153  << " SW_surface=" << std::scientific << std::setprecision(6)
154  << SW_surface << " SW_TOA=" << SW_TOA << " SW_up_TOA=" << SW_up_TOA
155  << " LW_net_surface=" << LW_net_surface << " LW_up_TOA=" << LW_up_TOA
156  << " heating_rate_max=" << heating_rate_max << "\n";
157  }
158 
159  // Append CSV row
160  if (!amrex::ParallelDescriptor::IOProcessor() || !m_diag_csv_enable) {
161  return;
162  }
163 
165 
166  std::ofstream outfile(m_diag_file, std::ios::app);
167  if (!outfile.good()) {
168  amrex::Warning("RadiationDiagnostics: Could not open file " + m_diag_file +
169  " for append");
170  return;
171  }
172 
173  // Write CSV row with scientific notation for fluxes
174  outfile << step << "," << std::scientific << std::setprecision(6) << time << ","
175  << call_site << ","
176  << SW_surface << "," << SW_TOA << "," << SW_up_TOA << "," << LW_net_surface << "," << LW_up_TOA
177  << "," << heating_rate_max;
178 
179  // Append SEB residual columns (backward compatible: write NaN if not available)
180  outfile << "," << seb_residual_mean << "," << seb_residual_max;
181 
182  // Append prognostic T_s and q_s columns (backward compatible: write NaN if not available)
183  outfile << "," << t_s_mean << "," << t_s_max << "," << q_s_mean << "," << q_s_max;
184 
185  // AMR level (see write_header_if_needed)
186  outfile << "," << m_amr_level;
187 
188  outfile << "\n";
189  outfile.close();
190 }
int m_last_write_step
Guard against duplicate writes.
Definition: ERF_RadiationDiagnostics.H:157
void write_header_if_needed()
Write CSV header if file is new or doesn't exist.
Definition: ERF_RadiationDiagnostics.cpp:39
amrex::Real m_last_write_time
Definition: ERF_RadiationDiagnostics.H:160
std::string m_last_write_call_site
Definition: ERF_RadiationDiagnostics.H:159

Referenced by TwoStreamRadiation::advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_header_if_needed()

void RadiationDiagnostics::write_header_if_needed ( )
private

Write CSV header if file is new or doesn't exist.

40 {
42  return;
43  }
44 
45  if (!amrex::ParallelDescriptor::IOProcessor()) {
46  m_header_written = true;
47  return;
48  }
49 
50  // Check if file already exists (append vs. new)
51  std::ifstream infile(m_diag_file);
52  bool file_exists = infile.good();
53  infile.close();
54 
55  std::ofstream outfile(m_diag_file, std::ios::app);
56  if (!outfile.good()) {
57  amrex::Warning("RadiationDiagnostics: Could not open file " + m_diag_file);
58  m_header_written = true;
59  return;
60  }
61 
62  // Only write header if file is new
63  if (!file_exists) {
64  // Base columns: always present
65  outfile << "step,time,call_site,SW_surface,SW_TOA,SW_up_TOA,LW_net_surface,LW_up_TOA,heating_rate_max";
66  // SEB diagnostic columns (added at end for backward compatibility)
67  outfile << ",SEB_residual_mean,SEB_residual_max";
68  // SEB prognostic surface temperature and moisture columns
69  outfile << ",T_s_mean,T_s_max,q_s_mean,q_s_max";
70  // AMR level, appended last for the same backward-compatibility reason as
71  // the columns above: consumers index by header name, not position.
72  outfile << ",level";
73  outfile << "\n";
74  }
75  outfile.close();
76  m_header_written = true;
77 }
bool m_header_written
Track whether CSV header was written.
Definition: ERF_RadiationDiagnostics.H:158

Referenced by append().

Here is the caller graph for this function:

Member Data Documentation

◆ m_amr_level

int RadiationDiagnostics::m_amr_level = 0
private

AMR level this instance reports for.

Referenced by append().

◆ m_diag_callsite_mode

std::string RadiationDiagnostics::m_diag_callsite_mode = "both"
private

Referenced by append().

◆ m_diag_csv_enable

bool RadiationDiagnostics::m_diag_csv_enable = true
private

Referenced by append(), and write_header_if_needed().

◆ m_diag_dedup_tol

amrex::Real RadiationDiagnostics::m_diag_dedup_tol = 1.0e-12
private

Referenced by append().

◆ m_diag_enable

bool RadiationDiagnostics::m_diag_enable = true
private

Referenced by append(), and write_header_if_needed().

◆ m_diag_file

std::string RadiationDiagnostics::m_diag_file
private

Output CSV file path.

Referenced by append(), and write_header_if_needed().

◆ m_diag_regtest_line_enable

bool RadiationDiagnostics::m_diag_regtest_line_enable = true
private

Referenced by append().

◆ m_diag_stdout_enable

bool RadiationDiagnostics::m_diag_stdout_enable = true
private

Referenced by append().

◆ m_diag_tagged_enable

bool RadiationDiagnostics::m_diag_tagged_enable = true
private

Referenced by append().

◆ m_header_written

bool RadiationDiagnostics::m_header_written = false
private

Track whether CSV header was written.

Referenced by write_header_if_needed().

◆ m_last_write_call_site

std::string RadiationDiagnostics::m_last_write_call_site = ""
private

Referenced by append().

◆ m_last_write_step

int RadiationDiagnostics::m_last_write_step = -1
private

Guard against duplicate writes.

Referenced by append().

◆ m_last_write_time

amrex::Real RadiationDiagnostics::m_last_write_time = -1.0
private

Referenced by append().

◆ m_verbosity

int RadiationDiagnostics::m_verbosity
private

Debug verbosity level.

Referenced by append().


The documentation for this class was generated from the following files: