11"""Specific Access-OM3 Model setup and post-processing""" 
22
3- import  re 
43from  collections  import  defaultdict 
54from  pathlib  import  Path 
65from  typing  import  Any 
76
7+ import  f90nml 
8+ from  netCDF4  import  Dataset 
89from  payu .models .cesm_cmeps  import  Runconfig 
910
1011from  model_config_tests .models .model  import  (
1112    DEFAULT_RUNTIME_SECONDS ,
1213    SCHEMA_VERSION_1_0_0 ,
1314    Model ,
1415)
15- from  model_config_tests .util  import  DAY_IN_SECONDS 
1616
1717
1818class  AccessOm3 (Model ):
1919    def  __init__ (self , experiment ):
2020        super ().__init__ (experiment )
21-         self .output_file  =  self .experiment .output000  /  "ocean.stats" 
2221
22+         # ACCESS-OM3 uses restarts for repro testing 
23+         self .output_0  =  self .experiment .restart000 
24+         self .output_1  =  self .experiment .restart001 
25+ 
26+         self .mom_restart_pointer  =  self .output_0  /  "rpointer.ocn" 
2327        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" 
28+         self .wav_in  =  experiment .control_path  /  "wav_in" 
2629
2730    def  set_model_runtime (
2831        self , years : int  =  0 , months : int  =  0 , seconds : int  =  DEFAULT_RUNTIME_SECONDS 
@@ -31,19 +34,20 @@ def set_model_runtime(
3134        Default is 3 hours""" 
3235        runconfig  =  Runconfig (self .runconfig )
3336
37+         # Check that ocean model component is MOM since checksums are obtained from 
38+         # MOM6 restarts. Fail early if not 
39+         ocn_model  =  runconfig .get ("ALLCOMP_attributes" , "OCN_model" )
40+         if  ocn_model  !=  "mom" :
41+             raise  ValueError (
42+                 "ACCESS-OM3 reproducibility checks utilize checksums written in MOM6 " 
43+                 "restarts and hence can only be used with ACCESS-OM3 configurations that " 
44+                 f"use MOM6. This configuration uses OCN_model = { ocn_model }  ." 
45+             )
46+ 
3447        if  years  ==  months  ==  0 :
3548            freq  =  "nseconds" 
3649            n  =  str (seconds )
3750
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-                     )
4751        elif  seconds  ==  0 :
4852            freq  =  "nmonths" 
4953            n  =  str (12  *  years  +  months )
@@ -59,38 +63,42 @@ def set_model_runtime(
5963
6064        runconfig .write ()
6165
66+         # Unfortunately WW3 doesn't (yet) obey the nuopc.runconfig. This should change in a 
67+         # future release, but for now we have to set WW3 runtime in wav_in. See 
68+         # https://github.com/COSIMA/access-om3/issues/239 
69+         if  self .wav_in .exists ():
70+             with  open (self .wav_in ) as  f :
71+                 nml  =  f90nml .read (f )
72+ 
73+             nml ["output_date_nml" ]["date" ]["restart" ]["stride" ] =  int (n )
74+             nml .write (self .wav_in , force = True )
75+ 
6276    def  output_exists (self ) ->  bool :
6377        """Check for existing output file""" 
64-         return  self .output_file .exists ()
78+         return  self .mom_restart_pointer .exists ()
6579
6680    def  extract_checksums (
6781        self , output_directory : Path  =  None , schema_version : str  =  None 
6882    ) ->  dict [str , Any ]:
6983        """Parse output file and create checksum using defined schema""" 
7084        if  output_directory :
71-             output_filename  =  output_directory  /  "ocean.stats " 
85+             mom_restart_pointer  =  output_directory  /  "rpointer.ocn " 
7286        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 
87+             mom_restart_pointer  =  self .mom_restart_pointer 
88+ 
89+         # MOM6 saves checksums for each variable in its restart files. Extract these 
90+         # attributes for each restart 
8291        output_checksums : dict [str , list [any ]] =  defaultdict (list )
8392
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 ])
93+         with  open (mom_restart_pointer ) as  f :
94+             for  restart_file  in  f .readlines ():
95+                 restart  =  mom_restart_pointer .parent  /  restart_file .rstrip ()
96+                 rootgrp  =  Dataset (restart , "r" )
97+                 for  variable  in  sorted (rootgrp .variables ):
98+                     var  =  rootgrp [variable ]
99+                     if  "checksum"  in  var .ncattrs ():
100+                         output_checksums [variable .strip ()].append (var .checksum .strip ())
101+                 rootgrp .close ()
94102
95103        if  schema_version  is  None :
96104            schema_version  =  self .default_schema_version 
0 commit comments