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, 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_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:

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,
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 index.
[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.
21  : m_verbosity(verbosity), m_diag_file(diag_file),
22  m_diag_enable(diag_enable), m_diag_stdout_enable(diag_stdout_enable),
23  m_diag_tagged_enable(diag_tagged_enable),
24  m_diag_regtest_line_enable(diag_regtest_line_enable),
25  m_diag_csv_enable(diag_csv_enable), m_diag_callsite_mode(diag_callsite_mode),
26  m_diag_dedup_tol(diag_dedup_tol)
27 {
28  // Constructor: nothing special needed
29  // write_header_if_needed() is called on first append()
30 }
bool m_diag_tagged_enable
Definition: ERF_RadiationDiagnostics.H:153
bool m_diag_regtest_line_enable
Definition: ERF_RadiationDiagnostics.H:154
int m_verbosity
Debug verbosity level.
Definition: ERF_RadiationDiagnostics.H:143
bool m_diag_stdout_enable
Definition: ERF_RadiationDiagnostics.H:152
std::string m_diag_file
Output CSV file path.
Definition: ERF_RadiationDiagnostics.H:144
std::string m_diag_callsite_mode
Definition: ERF_RadiationDiagnostics.H:156
amrex::Real m_diag_dedup_tol
Definition: ERF_RadiationDiagnostics.H:157
bool m_diag_csv_enable
Definition: ERF_RadiationDiagnostics.H:155
bool m_diag_enable
Definition: ERF_RadiationDiagnostics.H:151

◆ ~RadiationDiagnostics()

RadiationDiagnostics::~RadiationDiagnostics ( )

Destructor.

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

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)

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.
85 {
86  // Master enable gate
87  if (!m_diag_enable) {
88  return;
89  }
90 
91  // Call-site mode filtering
92  bool should_emit_pre = (m_diag_callsite_mode == "both" || m_diag_callsite_mode == "pre_only");
93  bool should_emit_post = (m_diag_callsite_mode == "both" || m_diag_callsite_mode == "post_only");
94 
95  bool is_pre_site = (call_site.find("pre") != std::string::npos);
96  bool is_post_site = (call_site.find("post") != std::string::npos);
97 
98  // Check if this call site should be filtered based on mode
99  if ((is_pre_site && !should_emit_pre) || (is_post_site && !should_emit_post)) {
100  return;
101  }
102 
103  // Guard against duplicate writes using 3-tuple identity:
104  // (step, call_site, time)
105  // This ensures:
106  // 1. Accidental repeated calls at same (step, call_site, time) are suppressed.
107  // 2. Legitimate pre_dycore + post_dycore entries at same step both retained
108  // (different call_site values, so identity tuple differs).
109  // 3. Mode filtering above already runs first, so pre_only/post_only modes
110  // naturally prevent unwanted entries from reaching this dedup logic.
111  //
112  // Time tolerance (m_diag_dedup_tol) accounts for floating-point rounding when
113  // multiple diagnostics functions are called at effectively the same time.
114  if (step == m_last_write_step &&
115  call_site == m_last_write_call_site &&
116  std::abs(time - m_last_write_time) < m_diag_dedup_tol) {
117  return; // Duplicate detected; skip write
118  }
119  m_last_write_step = step;
120  m_last_write_call_site = call_site;
121  m_last_write_time = time;
122 
123  // Print debug output (if verbosity >= 1, and IOProcessor only)
124  //
125  // The bracketed tag is the module-generic "[RAD]" plus the call site, so
126  // it stays accurate and grepable no matter which radiation feature is
127  // driving the call.
128  if (m_verbosity >= 1 && amrex::ParallelDescriptor::IOProcessor() &&
130  amrex::Print() << "[RAD][RadiationDiagnostics::append] step=" << step
131  << " time=" << time
132  << " call_site=" << call_site
133  << " SW_surface=" << SW_surface
134  << " SW_TOA=" << SW_TOA << " SW_up_TOA=" << SW_up_TOA
135  << " LW_net_surface=" << LW_net_surface
136  << " LW_up_TOA=" << LW_up_TOA
137  << " heating_rate_max=" << heating_rate_max << "\n";
138  }
139 
140  // Print RADIATION_DIAG line (for RegTest scripts)
141  if (amrex::ParallelDescriptor::IOProcessor() && m_diag_regtest_line_enable &&
143  amrex::Print() << "RADIATION_DIAG: step=" << step << " time=" << time
144  << " call_site=" << call_site
145  << " SW_surface=" << std::scientific << std::setprecision(6)
146  << SW_surface << " SW_TOA=" << SW_TOA << " SW_up_TOA=" << SW_up_TOA
147  << " LW_net_surface=" << LW_net_surface << " LW_up_TOA=" << LW_up_TOA
148  << " heating_rate_max=" << heating_rate_max << "\n";
149  }
150 
151  // Append CSV row
152  if (!amrex::ParallelDescriptor::IOProcessor() || !m_diag_csv_enable) {
153  return;
154  }
155 
157 
158  std::ofstream outfile(m_diag_file, std::ios::app);
159  if (!outfile.good()) {
160  amrex::Warning("RadiationDiagnostics: Could not open file " + m_diag_file +
161  " for append");
162  return;
163  }
164 
165  // Write CSV row with scientific notation for fluxes
166  outfile << step << "," << std::scientific << std::setprecision(6) << time << ","
167  << call_site << ","
168  << SW_surface << "," << SW_TOA << "," << SW_up_TOA << "," << LW_net_surface << "," << LW_up_TOA
169  << "," << heating_rate_max;
170 
171  // Append SEB residual columns (backward compatible: write NaN if not available)
172  outfile << "," << seb_residual_mean << "," << seb_residual_max;
173 
174  // Append prognostic T_s and q_s columns (backward compatible: write NaN if not available)
175  outfile << "," << t_s_mean << "," << t_s_max << "," << q_s_mean << "," << q_s_max;
176 
177  outfile << "\n";
178  outfile.close();
179 }
int m_last_write_step
Guard against duplicate writes.
Definition: ERF_RadiationDiagnostics.H:145
void write_header_if_needed()
Write CSV header if file is new or doesn't exist.
Definition: ERF_RadiationDiagnostics.cpp:38
amrex::Real m_last_write_time
Definition: ERF_RadiationDiagnostics.H:148
std::string m_last_write_call_site
Definition: ERF_RadiationDiagnostics.H:147

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.

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

Referenced by append().

Here is the caller graph for this function:

Member Data Documentation

◆ 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: