Skip to content

Commit 63fc7c9

Browse files
committed
Merge PR #357 script to check GCHP emission diags)
This merge brings PR #357 (Add example script to check GCHP emission diagnostics entries in HISTORY.rc and HEMCO_Diagn.rc, by @yantosca) into the GCPy development stream. PR #358 adds an example script (check_gchp_emission_diags.py) to cross-check GCHP emissions diagnostic entrieds in HISTORY.rc and HEMCO_Diagn.rc. The script prints entries common to both files, and those that are only in one file but not the other. Signed-off-by: Bob Yantosca <[email protected]>
2 parents a3ab913 + 774de73 commit 63fc7c9

File tree

5 files changed

+320
-3
lines changed

5 files changed

+320
-3
lines changed

CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77
## [Unreleased] - TBD
88
### Added
99
- Added `create_benchmark_sanity_check_table` routine to `gcpy/benchmark/benchmark_funcs.py` to test if variables are all zero or NaN
10+
- Added a chapter on using code profiling tools in the ReadTheDocs documentatio
11+
n
12+
- Added code profiling scripts (in `gcpy/gcpy/profile`) to read and display output from gprofng and Intel VTune profilers
13+
- Added `check_gchp_emission_diags.py` example script and documentation
1014

1115
### Changed
1216
- Updated `gcpy_environment_py313.yml` to use `esmf==8.8.1` and `esmpy==8.8.1` to fix package inconsistency issues
13-
- Added code profiling scripts (in `gcpy/gcpy/profile`) to read and display output from gprofng and Intel VTune profilers
14-
- Added a chapter on using code profiling tools in the ReadTheDocs documentation
1517

1618
## [1.6.1] - 2025-03-24
1719
### Added
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
.. _check-gchp:
2+
3+
###############################
4+
Check GCHP emission diagnostics
5+
###############################
6+
7+
Because `GCHP <https://gchp.readthedocs.io>`_ uses the MAPL library's
8+
History component for diagnostic archiving, all emission entries must
9+
be entered individually in both the `HISTORY.rc
10+
<https://gchp.readthedocs.io/en/stable/user-guide/config-files/HISTORY_rc.html>`_
11+
and `HEMCO_Diagn.rc
12+
<https://gchp.readthedocs.io/en/stable/geos-chem-shared-docs/doc/hemco-diagn.html>`_
13+
template files. These template files are located in the
14+
:file:`run/GCHP` folder of the GEOS-Chem "science codebase"
15+
repository.
16+
17+
This example demonstrates how you can cross-check the GCHP emission
18+
diagnostic entries. This is especially important if you are adding
19+
new emissions diagnostics (so that you won't forget to update one file
20+
or the other).
21+
22+
.. _check_gchp_code:
23+
24+
===========
25+
Source code
26+
===========
27+
28+
**Script location:** `gcpy/examples/diagnostics/check_gchp_emission_diags.py
29+
<https://github.com/geoschem/gcpy/blob/main/gcpy/examples/diagnostics/check_gchp_emission_diags.py>`_
30+
31+
.. _check-gchp-usage:
32+
33+
=====
34+
Usage
35+
=====
36+
37+
First, clone the GCHP source code (if you haven't done so already).
38+
Then navigate to the :file:`run/GCHP` folder, which is the top-level
39+
folder for GCHP run directory template files.
40+
41+
.. code-block:: console
42+
43+
$ git clone --recurse-submodules https://github.com/geoschem/GCHP
44+
45+
$ cd gchp/run
46+
47+
Activate your GCPy environment with mamba or conda:
48+
49+
.. code-block:: console
50+
51+
$ mamba activate gcpy_env # If using mamba, or
52+
53+
$ conda activate gcpy_env # If using conda
54+
55+
Use one of these commands to check GCHP emissions diagnostics for a
56+
given simulation type:
57+
58+
.. code-block:: console
59+
60+
$ python -m gcpy.examples.diagnostics.check_gchp_emission_diags . carbon
61+
62+
$ python -m gcpy.examples.diagnostics.check_gchp_emission_diags . fullchem
63+
64+
$ python -m gcpy.examples.diagnostics.check_gchp_emission_diags . TransportTracers
65+
66+
$ python -m gcpy.examples.diagnostics.check_gchp_emission_diags . tagO3
67+
68+
You will then see output similar to this (fullchem example shown).
69+
70+
.. code-block:: console
71+
72+
===============================================================================
73+
Common to both HISTORY.rc and HEMCO_Diagn.rc
74+
===============================================================================
75+
#EmisCH4_Anthro
76+
#EmisCH4_BioBurn
77+
#EmisCH4_Ship
78+
#EmisCH4_Total
79+
#EmisSESQ_Biogenic
80+
#InvAEIC_ACET
81+
#InvAEIC_ALD2
82+
#InvAEIC_ALK4
83+
#InvAEIC_BCPI
84+
#InvAEIC_C2H6
85+
#InvAEIC_C3H8
86+
... etc ...
87+
EmisACET_BioBurn
88+
EmisACET_Biogenic
89+
EmisACET_Ocean
90+
EmisACET_Total
91+
EmisACR_BioBurn
92+
EmisACR_Total
93+
EmisACTA_BioBurn
94+
EmisACTA_Total
95+
EmisALD2_Anthro
96+
... etc ...
97+
98+
===============================================================================
99+
In HISTORY.rc but not in HEMCO_Diagn.rc
100+
===============================================================================
101+
102+
===============================================================================
103+
In HEMCO_Diagn.rc but not in HISTORY.rc
104+
===============================================================================
105+
#EmisNO_Fert
106+
#InvCEDS_ALK6
107+
#InvGTChlorine_HCl
108+
109+
The output indicates that some diagnostics in :file:`HEMCO_Diagn.rc`
110+
are not present in :literal:`HISTORY.rc`, and should be added there
111+
for consistency. A comment before the emission entry means that the
112+
diagnostic will be disabled by default.

docs/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ For documentation on setting up and running GEOS-Chem please see our
5757
Six-Panel
5858
Compare-Diags
5959
Code-Profiling
60+
Check-GCHP-Emission-Diags
6061
KPP-Standalone
6162

6263
.. toctree::

gcpy/benchmark/modules/emission_species.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ FullChemBenchmark:
4747
MTPA: Tg
4848
MTPO: Tg
4949
NH3: Tg
50-
'NO': Tg
50+
'NO': Tg # Quotes are necessary, otherwise YAML interprets NO as False
5151
NO2: Tg
5252
O3: Tg
5353
OCPI: Tg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Compares GCHP emission diagnostic entries in HISTORY.rc
4+
and in HEMCO_Diagn.rc configuration files for consistency.
5+
"""
6+
from os.path import join, realpath
7+
from sys import argv
8+
from re import sub
9+
from gcpy.constants import ENCODING
10+
from gcpy.util import verify_variable_type
11+
12+
13+
def read_history(filename):
14+
"""
15+
Reads the HISTORY_Diagn.rc file and returns a list of
16+
diagnostic container names containing "Emis" or "Inv".
17+
18+
Args
19+
filename : str : Path to the HISTORY.rc file
20+
21+
Returns
22+
result : list : List of diagnostic entries
23+
"""
24+
verify_variable_type(filename, str)
25+
26+
with open(filename, "r", encoding=ENCODING) as ifile:
27+
result = []
28+
for line in ifile:
29+
30+
# Only take lines with diagnostic fields
31+
if "GCHPchem" not in line:
32+
continue
33+
if "Budget" in line:
34+
continue
35+
if "Emis" not in line and "Inv" not in line:
36+
continue
37+
if "Emissions.fields:" in line:
38+
line = line.replace("Emissions.fields:", "")
39+
40+
# The diagnostic container is the 1st word
41+
# (also strip quotes)
42+
43+
first_word = line.strip().split()[0]
44+
first_word = first_word.replace("'", "").replace('"', "")
45+
46+
# Replace multiple # chars with a single # char
47+
first_word = sub(r"#+", "#", first_word)
48+
49+
# Add to list of diagnosic entries
50+
result.append(first_word)
51+
52+
return result
53+
54+
55+
def read_hemco_diagn(filename):
56+
"""
57+
Reads the HEMCO_Diagn.rc file and returns a list of
58+
diagnostic container names containing "Emis" or "Inv".
59+
60+
Args
61+
filename : str : Path to the HEMCO_Diagn.rc file
62+
63+
Returns
64+
result : list : List of diagnostic entries
65+
"""
66+
verify_variable_type(filename, str)
67+
68+
with open(filename, "r", encoding=ENCODING) as ifile:
69+
result = []
70+
for line in ifile:
71+
72+
# Strip newlines and split into substrings
73+
line = line.strip().split()
74+
75+
# Skip if the line is empty
76+
if len(line) == 0:
77+
continue
78+
79+
# Skip if the 1st word doesn't contain "Emis" or "Inv"
80+
first_word = line[0]
81+
if "Emis" not in first_word and "Inv" not in first_word:
82+
continue
83+
84+
# Replace quotes
85+
first_word.replace("'", "").replace('"', "")
86+
87+
# Replace multiple "#" characters with a single "#"
88+
first_word = sub(r"#+", "#", first_word)
89+
90+
# Append to list of diagnosic entries
91+
result.append(first_word)
92+
93+
return result
94+
95+
96+
def compare_containers(history_entries, hco_diagn_entries):
97+
"""
98+
Compares the list of GCHP emission entries in HISTORY.rc
99+
and HEMCO_Diagn.rc. Returns the list of entries common to
100+
both, and in one file but not the other.
101+
102+
Args
103+
history_containers : list : Emission entries in HISTORY.rc
104+
hco_diagn_containers : list : Emission entries in HEMCO_Diagn.rc
105+
106+
Returns
107+
result : dict : Results of the comparison
108+
"""
109+
verify_variable_type(history_entries, list)
110+
verify_variable_type(hco_diagn_entries, list)
111+
112+
# Convert lists to sets
113+
set_history = set(history_entries)
114+
set_hemco = set(hco_diagn_entries)
115+
116+
# Diagnostic entries common to History and HEMCO
117+
result = {}
118+
result["common"] = sorted(list(set_history & set_hemco))
119+
120+
# Diagnostic entries found in one file but not the other
121+
result["in_history_not_hemco"] = sorted(list(set_history - set_hemco))
122+
result["in_hemco_not_history"] = sorted(list(set_hemco - set_history))
123+
124+
return result
125+
126+
127+
def print_results(result):
128+
"""
129+
Prints the results of the comparison between HISTORY.rc
130+
and HEMCO_Diagn.rc
131+
132+
Args
133+
result : dict : Dictionary with results of the comparison
134+
"""
135+
verify_variable_type(result, dict)
136+
137+
print("="*79)
138+
print("Common to both HISTORY.rc and HEMCO_Diagn.rc")
139+
print("="*79)
140+
for var in result["common"]:
141+
print(f" {var}")
142+
143+
print("")
144+
print("="*79)
145+
print("In HISTORY.rc but not in HEMCO_Diagn.rc")
146+
print("="*79)
147+
for var in result["in_history_not_hemco"]:
148+
print(f" {var}")
149+
150+
print("")
151+
print("="*79)
152+
print("In HEMCO_Diagn.rc but not in HISTORY.rc")
153+
print("="*79)
154+
for var in result["in_hemco_not_history"]:
155+
print(f" {var}")
156+
157+
158+
def main(path_to_rundir, simulation):
159+
"""
160+
Main program. Calls routines to read GCHP HISTORY.rc and
161+
HEMCO_Diagn.rc files, to compare emission diagnostics, and
162+
to print the results.
163+
164+
Args
165+
path_to_rundir : str : Path to the run/GCHP folder
166+
simulation : str : Simulation name (e.g. fullchem)
167+
"""
168+
verify_variable_type(path_to_rundir, str)
169+
verify_variable_type(simulation, str)
170+
171+
# Create paths to HISTORY.rc and HEMCO_Diagn.rc
172+
history_file = realpath(
173+
join(
174+
path_to_rundir,
175+
"HISTORY.rc.templates",
176+
f"HISTORY.rc.{simulation}"
177+
)
178+
)
179+
hco_diagn_file = realpath(
180+
join(
181+
path_to_rundir,
182+
"HEMCO_Diagn.rc.templates",
183+
f"HEMCO_Diagn.rc.{simulation}"
184+
)
185+
)
186+
187+
# Read emission entries
188+
history_entries = read_history(history_file)
189+
hco_diag_entries = read_hemco_diagn(hco_diagn_file)
190+
191+
# Compare and print results
192+
result = compare_containers(history_entries, hco_diag_entries)
193+
print_results(result)
194+
195+
196+
if __name__ == '__main__':
197+
198+
if len(argv) != 3:
199+
MSG = "Usage: python -m gcpy.examples.diagnostics.check_gchp_emission_diags /path/to/run/GCHP SIMULATION-NAME"
200+
raise ValueError(MSG)
201+
202+
main(argv[1], argv[2])

0 commit comments

Comments
 (0)