• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payu-org / payu / 6489602581

12 Oct 2023 12:25AM UTC coverage: 45.573% (+2.8%) from 42.772%
6489602581

push

github

web-flow
Merge pull request #363 from jo-basevi/358-date-based-frequency

Add support for date-based restart frequency

111 of 147 new or added lines in 10 files covered. (75.51%)

2 existing lines in 1 file now uncovered.

1580 of 3467 relevant lines covered (45.57%)

1.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.04
/payu/models/mom.py
1
"""Driver interface to the MOM ocean model.
2

3
:copyright: Copyright 2011 Marshall Ward, see AUTHORS for details
4
:license: Apache License, Version 2.0, see LICENSE for details
5
"""
6
import os
3✔
7
import shlex
3✔
8
import shutil
3✔
9
import subprocess
3✔
10

11
import f90nml
3✔
12

13
from payu.models.fms import Fms
3✔
14
from payu.fsops import mkdir_p, make_symlink
3✔
15

16

17
class Mom(Fms):
3✔
18

19
    def __init__(self, expt, name, config):
3✔
20

21
        # FMS initialisation
22
        super(Mom, self).__init__(expt, name, config)
3✔
23

24
        # Model-specific configuration
25
        self.model_type = 'mom'
3✔
26
        self.default_exec = 'fms_MOM_SIS.x'
3✔
27

28
        # Default repo and build details.
29
        self.repo_url = 'git://github.com/BreakawayLabs/mom.git'
3✔
30
        self.repo_tag = 'master'
3✔
31
        self.build_command = './MOM_compile.csh --platform nci --type MOM_SIS'
3✔
32

33
        self.config_files = [
3✔
34
            'data_table',
35
            'diag_table',
36
            'field_table',
37
            'input.nml'
38
        ]
39

40
        self.optional_config_files = [
3✔
41
            'blob_diag_table',
42
            'mask_table',
43
            'ocean_mask_table'
44
        ]
45

46
    def set_model_pathnames(self):
3✔
47
        super(Mom, self).set_model_pathnames()
3✔
48

49
        self.build_exec_path = os.path.join(self.codebase_path, 'exec', 'nci',
3✔
50
                                            'MOM_SIS')
51
        self.build_path = os.path.join(self.codebase_path, 'exp')
3✔
52

53
    def build_model(self):
3✔
54
        super(Mom, self).build_model()
×
55

56
        # Model is built, now copy over mppnccombine.
57
        mppnc_exec = 'mppnccombine.nci'
×
58

59
        mppnc_src = os.path.join(self.codebase_path, 'bin', mppnc_exec)
×
60
        mppnc_dest = os.path.join(self.expt.lab.bin_path, 'mppnccombine')
×
61
        shutil.copy(mppnc_src, mppnc_dest)
×
62

63
    def setup(self):
3✔
64
        # FMS initialisation
65
        super(Mom, self).setup()
×
66

67
        if not self.top_level_model:
×
68
            # Make log dir
69
            mkdir_p(os.path.join(self.work_path, 'log'))
×
70

71
        input_nml_path = os.path.join(self.work_path, 'input.nml')
×
72
        input_nml = f90nml.read(input_nml_path)
×
73

74
        # Set the runtime
75
        if self.expt.runtime:
×
76
            ocean_solo_nml = input_nml['ocean_solo_nml']
×
77

78
            ocean_solo_nml['years'] = self.expt.runtime['years']
×
79
            ocean_solo_nml['months'] = self.expt.runtime['months']
×
80
            ocean_solo_nml['days'] = self.expt.runtime['days']
×
81
            ocean_solo_nml['seconds'] = self.expt.runtime.get('seconds', 0)
×
82

83
            input_nml.write(input_nml_path, force=True)
×
84

85
        # Construct the land CPU mask
86
        if self.expt.config.get('mask_table', False):
×
87
            # NOTE: This function actually creates a mask table using the
88
            #       `check_mask` command line tool.  But it is not very usable
89
            #       since you need to know the number of masked CPUs to submit
90
            #       the job.  It needs a rethink of the submission process.
91
            self.create_mask_table(input_nml)
×
92

93
        # NOTE: Don't expect this to be here forever...
94
        # Attempt to set a mask table from the input
95
        if self.config.get('mask', False):
×
96
            mask_path = os.path.join(self.work_input_path, 'ocean_mask_table')
×
97

98
            # Remove any existing mask
99
            # (If no reference mask is available, then we will not use one)
100
            if os.path.isfile(mask_path):
×
101
                os.remove(mask_path)
×
102

103
            # Reference mask table
NEW
104
            assert ('layout' in input_nml['ocean_model_nml'])
×
105
            nx, ny = input_nml['ocean_model_nml'].get('layout')
×
106
            n_masked_cpus = nx * ny - self.config.get('ncpus')
×
107

108
            mask_table_fname = 'mask_table.{nmask}.{nx}x{ny}'.format(
×
109
                nmask=n_masked_cpus,
110
                nx=nx,
111
                ny=ny
112
            )
113

114
            ref_mask_path = os.path.join(self.work_input_path,
×
115
                                         mask_table_fname)
116

117
            # Set (or replace) mask table if reference is available
118
            if os.path.isfile(ref_mask_path):
×
119
                make_symlink(ref_mask_path, mask_path)
×
120

121
    def set_timestep(self, timestep):
3✔
122

123
        input_nml_path = os.path.join(self.work_path, 'input.nml')
×
124
        input_nml = f90nml.read(input_nml_path)
×
125

126
        input_nml['ocean_model_nml']['dt_ocean'] = timestep
×
127

128
        input_nml.write(input_nml_path, force=True)
×
129

130
    def create_mask_table(self, input_nml):
3✔
131
        import netCDF4
×
132

133
        # Disable E1136 which is tripped below when accessing grid_vars
134
        # pylint: disable=unsubscriptable-object
135

136
        # Get the grid spec path
137
        grid_spec_fname = 'grid_spec.nc'
×
138
        for input_dir in self.input_paths:
×
139
            if grid_spec_fname in os.listdir(input_dir):
×
140
                grid_spec_path = os.path.join(input_dir, grid_spec_fname)
×
141
                break
×
142
        assert grid_spec_path
×
143

144
        grid_spec_nc = netCDF4.Dataset(grid_spec_path)
×
145
        grid_vars = grid_spec_nc.variables
×
146

147
        # Get the ocean mosaic file
148
        # TODO: Do not assume mosaic format
149
        ocn_mosaic_fname = ''.join(grid_vars['ocn_mosaic_file'][:].data)
×
150
        for input_dir in self.input_paths:
×
151
            if ocn_mosaic_fname in os.listdir(input_dir):
×
152
                ocn_mosaic_path = os.path.join(input_dir, ocn_mosaic_fname)
×
153
                break
×
154

155
        # Get the topography file
156
        ocn_topog_fname = ''.join(grid_vars['ocn_topog_file'][:].data)
×
157
        for input_dir in self.input_paths:
×
158
            if ocn_topog_fname in os.listdir(input_dir):
×
159
                ocn_topog_path = os.path.join(input_dir, ocn_topog_fname)
×
160
                break
×
161

162
        # pylint: enable=unsubscriptable-object
163

164
        grid_spec_nc.close()
×
165

166
        check_mask = os.path.join(self.expt.lab.bin_path, 'check_mask')
×
167
        f_null = open(os.devnull, 'w')
×
168

169
        # Generate ocean mask_table
170
        ocn_layout = input_nml['ocean_model_nml']['layout']
×
171

172
        cmd = (
×
173
            '{check_mask} --grid_file {grid_file} '
174
            '--ocean_topog {ocean_topog} --layout {layout}'.format(
175
                check_mask=check_mask,
176
                grid_file=ocn_mosaic_path,
177
                ocean_topog=ocn_topog_path,
178
                layout=','.join([str(s) for s in ocn_layout])
179
            )
180
        )
181
        subprocess.call(shlex.split(cmd), stdout=f_null)
×
182
        ocn_mask_fname = [f for f in os.listdir(os.curdir)
×
183
                          if f.startswith('mask_table')][0]
184

185
        ocn_mask_path = os.path.join(self.work_input_path,
×
186
                                     'ocean_mask_table')
187
        shutil.copy(ocn_mask_fname, ocn_mask_path)
×
188

189
        # Generate the ice mask_table
190
        ice_layout = input_nml['ice_model_nml']['layout']
×
191

192
        if ice_layout == ocn_layout:
×
193
            ice_mask_fname = ocn_mask_fname
×
194
        else:
195
            cmd = (
×
196
                '{check_mask} --grid_file {grid_file} '
197
                '--ocean_topog {ocean_topog} --layout {layout}'.format(
198
                    check_mask=check_mask,
199
                    grid_file=ocn_mosaic_path,
200
                    ocean_topog=ocn_topog_path,
201
                    layout=','.join([str(s) for s in ice_layout])
202
                )
203
            )
204
            subprocess.call(shlex.split(cmd), stdout=f_null)
×
205
            ice_mask_fname = [f for f in os.listdir(os.curdir)
×
206
                              if f.startswith('mask_table')][0]
207

208
        ice_mask_path = os.path.join(self.work_input_path,
×
209
                                     'ice_mask_table')
210

211
        shutil.copy(ice_mask_fname, ice_mask_path)
×
212

213
        try:
×
214
            os.remove(ocn_mask_fname)
×
215
            os.remove(ice_mask_fname)
×
216
        except EnvironmentError:
×
217
            # TODO: Check this a little bit better
218
            pass
×
219

220
        f_null.close()
×
221

222
        # Read and return the number of land cells
223
        with open(ocn_mask_path) as fmask:
×
224
            land_cells = int(fmask.readline())
×
225

226
        return land_cells
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc