Skip to content

Commit 0f142bd

Browse files
committed
ACCESS-OM3: use per-variable checksums from mom restart files
1 parent fe4f2e7 commit 0f142bd

11 files changed

+110
-60
lines changed

src/model_config_tests/exp_test_helper.py

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def __init__(self, control_path: Path, lab_path: Path, disable_payu_run=False):
2727
self.work_path = lab_path / "work" / self.exp_name
2828
self.output000 = self.archive_path / "output000"
2929
self.output001 = self.archive_path / "output001"
30+
self.restart000 = self.archive_path / "restart000"
31+
self.restart001 = self.archive_path / "restart001"
3032

3133
with open(self.config_path) as f:
3234
self.config = yaml.safe_load(f)

src/model_config_tests/models/accessesm1p5.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(self, experiment):
1919
# Override model default runtime
2020
self.default_runtime_seconds = DEFAULT_RUNTIME_SECONDS
2121

22-
self.output_file = self.experiment.output000 / "access.out"
22+
self.output_file = self.output_0 / "access.out"
2323

2424
def set_model_runtime(
2525
self, years: int = 0, months: int = 0, seconds: int = DEFAULT_RUNTIME_SECONDS

src/model_config_tests/models/accessom2.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
class AccessOm2(Model):
1717
def __init__(self, experiment):
1818
super().__init__(experiment)
19-
self.output_file = self.experiment.output000 / "access-om2.out"
19+
self.output_file = self.output_0 / "access-om2.out"
2020

2121
self.accessom2_config = experiment.control_path / "accessom2.nml"
2222
self.ocean_config = experiment.control_path / "ocean" / "input.nml"

src/model_config_tests/models/accessom3.py

+23-35
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
"""Specific Access-OM3 Model setup and post-processing"""
22

3-
import re
43
from collections import defaultdict
54
from pathlib import Path
65
from typing import Any
76

7+
from netCDF4 import Dataset
88
from payu.models.cesm_cmeps import Runconfig
99

1010
from model_config_tests.models.model import (
1111
DEFAULT_RUNTIME_SECONDS,
1212
SCHEMA_VERSION_1_0_0,
1313
Model,
1414
)
15-
from model_config_tests.util import DAY_IN_SECONDS
1615

1716

1817
class AccessOm3(Model):
1918
def __init__(self, experiment):
2019
super().__init__(experiment)
21-
self.output_file = self.experiment.output000 / "ocean.stats"
2220

21+
# ACCESS-OM3 uses restarts for repro testing
22+
self.output_0 = self.experiment.restart000
23+
self.output_1 = self.experiment.restart001
24+
25+
self.mom_restart_pointer = self.output_0 / "rpointer.ocn"
2326
self.runconfig = experiment.control_path / "nuopc.runconfig"
24-
self.mom_override = experiment.control_path / "MOM_override"
25-
self.ocean_config = experiment.control_path / "input.nml"
2627

2728
def set_model_runtime(
2829
self, years: int = 0, months: int = 0, seconds: int = DEFAULT_RUNTIME_SECONDS
@@ -35,15 +36,6 @@ def set_model_runtime(
3536
freq = "nseconds"
3637
n = str(seconds)
3738

38-
# Ensure that ocean.stats are written at the end of the run
39-
if seconds < DAY_IN_SECONDS:
40-
with open(self.mom_override, "a") as f:
41-
f.writelines(
42-
[
43-
f"\n#override TIMEUNIT = {n}",
44-
"\n#override ENERGYSAVEDAYS = 1.0",
45-
]
46-
)
4739
elif seconds == 0:
4840
freq = "nmonths"
4941
n = str(12 * years + months)
@@ -61,36 +53,32 @@ def set_model_runtime(
6153

6254
def output_exists(self) -> bool:
6355
"""Check for existing output file"""
64-
return self.output_file.exists()
56+
return self.mom_restart_pointer.exists()
6557

6658
def extract_checksums(
6759
self, output_directory: Path = None, schema_version: str = None
6860
) -> dict[str, Any]:
6961
"""Parse output file and create checksum using defined schema"""
7062
if output_directory:
71-
output_filename = output_directory / "ocean.stats"
63+
mom_restart_pointer = output_directory / "rpointer.ocn"
7264
else:
73-
output_filename = self.output_file
74-
75-
# ocean.stats is used for regression testing in MOM6's own test suite
76-
# See https://github.com/mom-ocean/MOM6/blob/2ab885eddfc47fc0c8c0bae46bc61531104428d5/.testing/Makefile#L495-L501
77-
# Rows in ocean.stats look like:
78-
# 0, 693135.000, 0, En 3.0745627134675957E-23, CFL 0.00000, ...
79-
# where the first three columns are Step, Day, Truncs and the remaining
80-
# columns include a label for what they are (e.g. En = Energy/Mass)
81-
# Header info is only included for new runs so can't be relied on
65+
mom_restart_pointer = self.mom_restart_pointer
66+
67+
# MOM6 saves checksums for each variable in its restart files. Extract these
68+
# attributes for each restart
8269
output_checksums: dict[str, list[any]] = defaultdict(list)
8370

84-
with open(output_filename) as f:
85-
lines = f.readlines()
86-
# Skip header if it exists (for new runs)
87-
istart = 2 if "Step" in lines[0] else 0
88-
for line in lines[istart:]:
89-
for col in line.split(","):
90-
# Only keep columns with labels (ie not Step, Day, Truncs)
91-
col = re.split(" +", col.strip().rstrip("\n"))
92-
if len(col) > 1:
93-
output_checksums[col[0]].append(col[-1])
71+
mom_restarts = []
72+
with open(mom_restart_pointer) as f:
73+
for restart in f.readlines():
74+
mom_restarts.append(mom_restart_pointer.parent / restart.rstrip())
75+
76+
for mom_restart in mom_restarts:
77+
rootgrp = Dataset(mom_restart, "r")
78+
for v in rootgrp.variables:
79+
var = rootgrp[v]
80+
if "checksum" in var.ncattrs():
81+
output_checksums[var.long_name.strip()].append(var.checksum.strip())
9482

9583
if schema_version is None:
9684
schema_version = self.default_schema_version

src/model_config_tests/models/model.py

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ def __init__(self, experiment):
2323

2424
self.default_runtime_seconds = DEFAULT_RUNTIME_SECONDS
2525

26+
self.output_0 = self.experiment.output000
27+
self.output_1 = self.experiment.output001
28+
2629
def extract_checksums(self, output_directory: Path, schema_version: str):
2730
"""Extract checksums from output directory"""
2831
raise NotImplementedError

src/model_config_tests/test_bit_reproducibility.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def test_restart_repro(self, output_path: Path, control_path: Path):
179179

180180
# Now compare the output between our two short and one long run.
181181
checksums_1d_0 = exp_2x1day.extract_checksums()
182-
checksums_1d_1 = exp_2x1day.extract_checksums(exp_2x1day.output001)
182+
checksums_1d_1 = exp_2x1day.extract_checksums(exp_2x1day.model.output_1)
183183

184184
checksums_2d = exp_2day.extract_checksums()
185185

Original file line numberDiff line numberDiff line change
@@ -1,32 +1,89 @@
11
{
22
"schema_version": "1-0-0",
33
"output": {
4-
"En": [
5-
"3.0745627134675957E-23"
4+
"Potential Temperature": [
5+
"E441AC22DEDE8930"
66
],
7-
"CFL": [
8-
"0.00000"
7+
"Salinity": [
8+
"8E3D02158AE4EAED"
99
],
10-
"SL": [
11-
"1.5112E-10"
10+
"Layer Thickness": [
11+
"9138701970F7E8A"
1212
],
13-
"M": [
14-
"1.36404E+21"
13+
"Zonal velocity": [
14+
"803BF5B7E9C239C1"
1515
],
16-
"S": [
17-
"34.7263"
16+
"Meridional velocity": [
17+
"7D9693C22E4D52B5"
1818
],
19-
"T": [
20-
"3.6362"
19+
"Frazil heat flux into ocean": [
20+
"5FE9C9F905239FC4"
2121
],
22-
"Me": [
23-
"0.00E+00"
22+
"Ocean surface pressure used in EoS": [
23+
"161DADB852A006F1"
2424
],
25-
"Se": [
26-
"0.00E+00"
25+
"Time average sea surface height": [
26+
"40E5A21D6F0D1F87"
2727
],
28-
"Te": [
29-
"0.00E+00"
28+
"Indicator of the first direction in split calculations.": [
29+
"0"
30+
],
31+
"Free surface Height": [
32+
"68FCD5E9DE45D1DF"
33+
],
34+
"Auxiliary Zonal velocity": [
35+
"32A58A90C668C32D"
36+
],
37+
"Auxiliary Meridional velocity": [
38+
"DA212F15199DC960"
39+
],
40+
"Zonal Coriolis and advactive acceleration": [
41+
"A4C51EC2A34D8EF1"
42+
],
43+
"Meridional Coriolis and advactive acceleration": [
44+
"DA7F07794C0B7279"
45+
],
46+
"Zonal horizontal viscous acceleration": [
47+
"749F723BA63A013C"
48+
],
49+
"Meridional horizontal viscous acceleration": [
50+
"CD7614674E679DA8"
51+
],
52+
"Time mean barotropic zonal velocity": [
53+
"2E16365E105FFB9"
54+
],
55+
"Time mean barotropic meridional velocity": [
56+
"A6344B9A6D8CB3A9"
57+
],
58+
"Barotropic timestep": [
59+
"4055968059DD63AF"
60+
],
61+
"Ideal Age Tracer": [
62+
"B64AD181B58D3F4B"
63+
],
64+
"Mesoscale Eddy Kinetic Energy": [
65+
"A205FF94D0736FD0"
66+
],
67+
"Lateral diffusivity from Mesoscale Eddy Kinetic Energy": [
68+
"C5E23A24466765D4"
69+
],
70+
"Lateral viscosity from Mesoscale Eddy Kinetic Energy": [
71+
"A9959752C4D3DE41"
72+
],
73+
"Copy of thickness diffusivity for diffusing MEKE": [
74+
"C62261CF4678ACEE"
75+
],
76+
"Shear-driven turbulent diffusivity at interfaces": [
77+
"8A1CB3488BF7FAC7"
78+
],
79+
"Shear-driven turbulent viscosity at interfaces": [
80+
"55036CBF887185D4"
81+
],
82+
"Instantaneous active mixing layer thickness": [
83+
"A815BF54039C2D82"
84+
],
85+
"Time-filtered MLD for use in MLE": [
86+
"7463C5D823501487"
3087
]
3188
}
32-
}
89+
}

tests/resources/access-om3/output000/ocean.stats

-3
This file was deleted.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
access-om3.mom6.r.1900-01-01-10800.nc

tests/test_model_extract_checksums.py

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def test_extract_checksums(model_name):
1919
# Mock ExpTestHelper
2020
mock_experiment = Mock()
2121
mock_experiment.output000 = resources_dir / "output000"
22+
mock_experiment.restart000 = resources_dir / "restart000"
2223
mock_experiment.control_path = Path("test/tmp")
2324

2425
# Create Model instance
@@ -53,6 +54,7 @@ def test_extract_checksums_unsupported_version(model_name):
5354
# Mock ExpTestHelper
5455
mock_experiment = Mock()
5556
mock_experiment.output000 = resources_dir / "output000"
57+
mock_experiment.restart000 = resources_dir / "restart000"
5658
mock_experiment.control_path = Path("test/tmp")
5759

5860
# Create Model instance

0 commit comments

Comments
 (0)