diff --git a/compass/ocean/cached_files.json b/compass/ocean/cached_files.json index 2ec036b847..23256adcba 100644 --- a/compass/ocean/cached_files.json +++ b/compass/ocean/cached_files.json @@ -103,5 +103,40 @@ "ocean/global_convergence/qu/cosine_bell/QU60/init/namelist.ocean": "global_convergence/qu/cosine_bell/QU60/init/namelist.220528.ocean", "ocean/global_convergence/qu/cosine_bell/QU60/init/initial_state.nc": "global_convergence/qu/cosine_bell/QU60/init/initial_state.220528.nc", "ocean/global_convergence/qu/cosine_bell/QU90/init/namelist.ocean": "global_convergence/qu/cosine_bell/QU90/init/namelist.220528.ocean", - "ocean/global_convergence/qu/cosine_bell/QU90/init/initial_state.nc": "global_convergence/qu/cosine_bell/QU90/init/initial_state.220528.nc" -} + "ocean/global_convergence/qu/cosine_bell/QU90/init/initial_state.nc": "global_convergence/qu/cosine_bell/QU90/init/initial_state.220528.nc", + "ocean/global_ocean/Icos240/mesh/base_mesh/mesh.msh": "global_ocean/Icos240/mesh/base_mesh/mesh.220814.msh", + "ocean/global_ocean/Icos240/mesh/base_mesh/base_mesh.nc": "global_ocean/Icos240/mesh/base_mesh/base_mesh.220814.nc", + "ocean/global_ocean/Icos240/mesh/base_mesh/cellWidthVsLatLon.nc": "global_ocean/Icos240/mesh/base_mesh/cellWidthVsLatLon.220814.nc", + "ocean/global_ocean/Icos240/mesh/base_mesh/graph.info": "global_ocean/Icos240/mesh/base_mesh/graph.220814.info", + "ocean/global_ocean/Icos240/mesh/cull_mesh/culled_mesh.nc": "global_ocean/Icos240/mesh/cull_mesh/culled_mesh.220814.nc", + "ocean/global_ocean/Icos240/mesh/cull_mesh/culled_graph.info": "global_ocean/Icos240/mesh/cull_mesh/culled_graph.220814.info", + "ocean/global_ocean/Icos240/mesh/cull_mesh/critical_passages_mask_final.nc": "global_ocean/Icos240/mesh/cull_mesh/critical_passages_mask_final.220814.nc", + "ocean/global_ocean/QU240/mesh/base_mesh/mesh.msh": "global_ocean/QU240/mesh/base_mesh/mesh.220814.msh", + "ocean/global_ocean/QU240/mesh/base_mesh/base_mesh.nc": "global_ocean/QU240/mesh/base_mesh/base_mesh.220814.nc", + "ocean/global_ocean/QU240/mesh/base_mesh/cellWidthVsLatLon.nc": "global_ocean/QU240/mesh/base_mesh/cellWidthVsLatLon.220814.nc", + "ocean/global_ocean/QU240/mesh/base_mesh/graph.info": "global_ocean/QU240/mesh/base_mesh/graph.220814.info", + "ocean/global_ocean/QU240/mesh/cull_mesh/culled_mesh.nc": "global_ocean/QU240/mesh/cull_mesh/culled_mesh.220814.nc", + "ocean/global_ocean/QU240/mesh/cull_mesh/culled_graph.info": "global_ocean/QU240/mesh/cull_mesh/culled_graph.220814.info", + "ocean/global_ocean/QU240/mesh/cull_mesh/critical_passages_mask_final.nc": "global_ocean/QU240/mesh/cull_mesh/critical_passages_mask_final.220814.nc", + "ocean/global_ocean/QUwISC240/mesh/base_mesh/mesh.msh": "global_ocean/QUwISC240/mesh/base_mesh/mesh.220814.msh", + "ocean/global_ocean/QUwISC240/mesh/base_mesh/base_mesh.nc": "global_ocean/QUwISC240/mesh/base_mesh/base_mesh.220814.nc", + "ocean/global_ocean/QUwISC240/mesh/base_mesh/cellWidthVsLatLon.nc": "global_ocean/QUwISC240/mesh/base_mesh/cellWidthVsLatLon.220814.nc", + "ocean/global_ocean/QUwISC240/mesh/base_mesh/graph.info": "global_ocean/QUwISC240/mesh/base_mesh/graph.220814.info", + "ocean/global_ocean/QUwISC240/mesh/cull_mesh/culled_mesh.nc": "global_ocean/QUwISC240/mesh/cull_mesh/culled_mesh.220814.nc", + "ocean/global_ocean/QUwISC240/mesh/cull_mesh/culled_graph.info": "global_ocean/QUwISC240/mesh/cull_mesh/culled_graph.220814.info", + "ocean/global_ocean/QUwISC240/mesh/cull_mesh/critical_passages_mask_final.nc": "global_ocean/QUwISC240/mesh/cull_mesh/critical_passages_mask_final.220814.nc", + "ocean/global_ocean/EC30to60/mesh/base_mesh/mesh.msh": "global_ocean/EC30to60/mesh/base_mesh/mesh.220814.msh", + "ocean/global_ocean/EC30to60/mesh/base_mesh/base_mesh.nc": "global_ocean/EC30to60/mesh/base_mesh/base_mesh.220814.nc", + "ocean/global_ocean/EC30to60/mesh/base_mesh/cellWidthVsLatLon.nc": "global_ocean/EC30to60/mesh/base_mesh/cellWidthVsLatLon.220814.nc", + "ocean/global_ocean/EC30to60/mesh/base_mesh/graph.info": "global_ocean/EC30to60/mesh/base_mesh/graph.220814.info", + "ocean/global_ocean/EC30to60/mesh/cull_mesh/culled_mesh.nc": "global_ocean/EC30to60/mesh/cull_mesh/culled_mesh.220814.nc", + "ocean/global_ocean/EC30to60/mesh/cull_mesh/culled_graph.info": "global_ocean/EC30to60/mesh/cull_mesh/culled_graph.220814.info", + "ocean/global_ocean/EC30to60/mesh/cull_mesh/critical_passages_mask_final.nc": "global_ocean/EC30to60/mesh/cull_mesh/critical_passages_mask_final.220814.nc", + "ocean/global_ocean/ECwISC30to60/mesh/base_mesh/mesh.msh": "global_ocean/ECwISC30to60/mesh/base_mesh/mesh.220814.msh", + "ocean/global_ocean/ECwISC30to60/mesh/base_mesh/base_mesh.nc": "global_ocean/ECwISC30to60/mesh/base_mesh/base_mesh.220814.nc", + "ocean/global_ocean/ECwISC30to60/mesh/base_mesh/cellWidthVsLatLon.nc": "global_ocean/ECwISC30to60/mesh/base_mesh/cellWidthVsLatLon.220814.nc", + "ocean/global_ocean/ECwISC30to60/mesh/base_mesh/graph.info": "global_ocean/ECwISC30to60/mesh/base_mesh/graph.220814.info", + "ocean/global_ocean/ECwISC30to60/mesh/cull_mesh/culled_mesh.nc": "global_ocean/ECwISC30to60/mesh/cull_mesh/culled_mesh.220814.nc", + "ocean/global_ocean/ECwISC30to60/mesh/cull_mesh/culled_graph.info": "global_ocean/ECwISC30to60/mesh/cull_mesh/culled_graph.220814.info", + "ocean/global_ocean/ECwISC30to60/mesh/cull_mesh/critical_passages_mask_final.nc": "global_ocean/ECwISC30to60/mesh/cull_mesh/critical_passages_mask_final.220814.nc" +} \ No newline at end of file diff --git a/compass/ocean/mesh/__init__.py b/compass/ocean/mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/compass/ocean/tests/global_ocean/mesh/cull.py b/compass/ocean/mesh/cull.py old mode 100755 new mode 100644 similarity index 73% rename from compass/ocean/tests/global_ocean/mesh/cull.py rename to compass/ocean/mesh/cull.py index fbe2f9f394..3faf23333d --- a/compass/ocean/tests/global_ocean/mesh/cull.py +++ b/compass/ocean/mesh/cull.py @@ -1,4 +1,5 @@ import xarray +import os from geometric_features import GeometricFeatures, FeatureCollection, \ read_feature_collection @@ -6,11 +7,127 @@ from mpas_tools.mesh.mask import compute_mpas_flood_fill_mask import mpas_tools.io from mpas_tools.io import write_netcdf +from mpas_tools.ocean import inject_bathymetry from mpas_tools.ocean.coastline_alteration import widen_transect_edge_masks, \ add_critical_land_blockages, add_land_locked_cells_to_mask from mpas_tools.viz.paraview_extractor import extract_vtk from mpas_tools.logging import LoggingContext, check_call +from compass.step import Step + + +class CullMeshStep(Step): + """ + A step for culling a global MPAS-Ocean mesh + + Attributes + ---------- + base_mesh_step : compass.mesh.spherical.SphericalBaseStep + The base mesh step containing input files to this step + + with_ice_shelf_cavities : bool + Whether the mesh includes ice-shelf cavities + + do_inject_bathymetry : bool + Whether to interpolate bathymetry from a data file so it + can be used as a culling criteria + + preserve_floodplain : bool + Whether to leave land cells in the mesh based on bathymetry + specified by do_inject_bathymetry + """ + + def __init__(self, test_case, base_mesh_step, with_ice_shelf_cavities, + name='cull_mesh', subdir=None, do_inject_bathymetry=False, + preserve_floodplain=False): + """ + Create a new step + + Parameters + ---------- + test_case : compass.ocean.tests.global_ocean.mesh.Mesh + The test case this step belongs to + + base_mesh_step : compass.mesh.spherical.SphericalBaseStep + The base mesh step containing input files to this step + + with_ice_shelf_cavities : bool + Whether the mesh includes ice-shelf cavities + + name : str, optional + the name of the step + + subdir : str, optional + the subdirectory for the step. The default is ``name`` + + do_inject_bathymetry : bool, optional + Whether to interpolate bathymetry from a data file so it + can be used as a culling criteria + + preserve_floodplain : bool, optional + Whether to leave land cells in the mesh based on bathymetry + specified by do_inject_bathymetry + """ + super().__init__(test_case, name=name, subdir=subdir, + cpus_per_task=None, min_cpus_per_task=None) + self.base_mesh_step = base_mesh_step + + for file in ['culled_mesh.nc', 'culled_graph.info', + 'critical_passages_mask_final.nc']: + self.add_output_file(filename=file) + + self.with_ice_shelf_cavities = with_ice_shelf_cavities + self.do_inject_bathymetry = do_inject_bathymetry + self.preserve_floodplain = preserve_floodplain + + def setup(self): + """ + Set up the test case in the work directory, including downloading any + dependencies. + """ + super().setup() + if self.do_inject_bathymetry: + self.add_input_file( + filename='earth_relief_15s.nc', + target='SRTM15_plus_earth_relief_15s.nc', + database='bathymetry_database') + + # get the these properties from the config options + config = self.config + # todo: move to constrain_resources() + self.cpus_per_task = config.getint('spherical_mesh', + 'cull_mesh_cpus_per_task') + self.min_cpus_per_task = config.getint('spherical_mesh', + 'cull_mesh_min_cpus_per_task') + + base_path = self.base_mesh_step.path + base_filename = self.base_mesh_step.config.get( + 'spherical_mesh', 'mpas_mesh_filename') + target = os.path.join(base_path, base_filename) + self.add_input_file(filename='base_mesh.nc', work_dir_target=target) + + def run(self): + """ + Run this step of the test case + """ + with_ice_shelf_cavities = self.with_ice_shelf_cavities + logger = self.logger + + # only use progress bars if we're not writing to a log file + use_progress_bar = self.log_filename is None + + do_inject_bathymetry = self.do_inject_bathymetry + preserve_floodplain = self.preserve_floodplain + + cull_mesh(with_critical_passages=True, logger=logger, + use_progress_bar=use_progress_bar, + preserve_floodplain=preserve_floodplain, + with_cavities=with_ice_shelf_cavities, + process_count=self.cpus_per_task) + + if do_inject_bathymetry: + inject_bathymetry(mesh_file='culled_mesh.nc') + def cull_mesh(with_cavities=False, with_critical_passages=False, custom_critical_passages=None, custom_land_blockages=None, diff --git a/compass/ocean/mesh/floodplain.py b/compass/ocean/mesh/floodplain.py new file mode 100644 index 0000000000..eef8db0098 --- /dev/null +++ b/compass/ocean/mesh/floodplain.py @@ -0,0 +1,63 @@ +from mpas_tools.ocean import inject_bathymetry, inject_preserve_floodplain + +from compass.mesh.spherical import QuasiUniformSphericalMeshStep + + +class FloodplainMeshStep(QuasiUniformSphericalMeshStep): + """ + A step for creating a global MPAS-Ocean mesh that includes variables + needed for preserving a floodplain + + preserve_floodplain : bool + Whether the mesh includes land cells + """ + + def __init__(self, test_case, name='base_mesh', subdir=None, + cell_width=None, preserve_floodplain=True): + """ + Create a new step + + Parameters + ---------- + test_case : compass.testcase.TestCase + The test case this step belongs to + + name : str + the name of the step + + subdir : {str, None} + the subdirectory for the step. The default is ``name`` + + cell_width : float, optional + The approximate cell width in km of the mesh if constant resolution + + preserve_floodplain : bool, optional + Whether the mesh includes land cells + """ + + super().__init__(test_case=test_case, name=name, subdir=subdir, + cell_width=cell_width) + + self.preserve_floodplain = preserve_floodplain + + self.add_input_file(filename='earth_relief_15s.nc', + target='SRTM15_plus_earth_relief_15s.nc', + database='bathymetry_database') + + def run(self): + """ + Run this step of the test case + """ + super().run() + + config = self.config + + mesh_filename = config.get('spherical_mesh', 'mpas_mesh_filename') + + inject_bathymetry(mesh_file=mesh_filename) + if self.preserve_floodplain: + floodplain_elevation = config.getfloat('spherical_mesh', + 'floodplain_elevation') + inject_preserve_floodplain( + mesh_file=mesh_filename, + floodplain_elevation=floodplain_elevation) diff --git a/compass/ocean/tests/global_ocean/__init__.py b/compass/ocean/tests/global_ocean/__init__.py index ede88adf45..c6f38cf31e 100644 --- a/compass/ocean/tests/global_ocean/__init__.py +++ b/compass/ocean/tests/global_ocean/__init__.py @@ -36,7 +36,7 @@ def __init__(self, mpas_core): super().__init__(mpas_core=mpas_core, name='global_ocean') # we do a lot of tests for QU240/QUwISC240 - for mesh_name in ['QU240', 'QUwISC240']: + for mesh_name in ['QU240', 'Icos240', 'QUwISC240']: mesh = Mesh(test_group=self, mesh_name=mesh_name) self.add_test_case(mesh) diff --git a/compass/ocean/tests/global_ocean/configure.py b/compass/ocean/tests/global_ocean/configure.py index 043a358c52..0d4f30002b 100644 --- a/compass/ocean/tests/global_ocean/configure.py +++ b/compass/ocean/tests/global_ocean/configure.py @@ -18,8 +18,8 @@ def configure_global_ocean(test_case, mesh, init=None): The test case that produces the initial condition for this run """ config = test_case.config - mesh_step = mesh.mesh_step - config.add_from_package(mesh_step.package, mesh_step.mesh_config_filename, + config.add_from_package('compass.mesh', 'mesh.cfg') + config.add_from_package(mesh.package, mesh.mesh_config_filename, exception=True) if mesh.with_ice_shelf_cavities: diff --git a/compass/ocean/tests/global_ocean/files_for_e3sm/ocean_graph_partition.py b/compass/ocean/tests/global_ocean/files_for_e3sm/ocean_graph_partition.py index a63de5c349..93a200fb61 100644 --- a/compass/ocean/tests/global_ocean/files_for_e3sm/ocean_graph_partition.py +++ b/compass/ocean/tests/global_ocean/files_for_e3sm/ocean_graph_partition.py @@ -38,7 +38,7 @@ def __init__(self, test_case, mesh, restart_filename): self.add_input_file(filename='restart.nc', target='../{}'.format(restart_filename)) - mesh_path = mesh.mesh_step.path + mesh_path = mesh.get_cull_mesh_path() self.add_input_file( filename='graph.info', work_dir_target='{}/culled_graph.info'.format(mesh_path)) diff --git a/compass/ocean/tests/global_ocean/forward.py b/compass/ocean/tests/global_ocean/forward.py index 8793055cb2..91313bbba8 100644 --- a/compass/ocean/tests/global_ocean/forward.py +++ b/compass/ocean/tests/global_ocean/forward.py @@ -103,7 +103,7 @@ def __init__(self, test_case, mesh, init, time_integrator, name='forward', self.add_streams_file( 'compass.ocean.tests.global_ocean', 'streams.bgc') - mesh_package = mesh.mesh_step.package + mesh_package = mesh.package mesh_package_contents = list(contents(mesh_package)) mesh_namelists = ['namelist.forward', f'namelist.{time_integrator.lower()}'] diff --git a/compass/ocean/tests/global_ocean/global_ocean.cfg b/compass/ocean/tests/global_ocean/global_ocean.cfg index 41d1223708..634954247c 100644 --- a/compass/ocean/tests/global_ocean/global_ocean.cfg +++ b/compass/ocean/tests/global_ocean/global_ocean.cfg @@ -1,15 +1,19 @@ -# options for global ocean testcases -[global_ocean] +# options for spherical meshes +[spherical_mesh] -## each mesh should replace these with appropriate values in its config file - -## config options related to the mesh step +## config options related to the step for culling land from the mesh # number of cores to use -mesh_cpus_per_task = 18 +cull_mesh_cpus_per_task = 18 # minimum of cores, below which the step fails -mesh_min_cpus_per_task = 1 +cull_mesh_min_cpus_per_task = 1 # maximum memory usage allowed (in MB) -mesh_max_memory = 1000 +cull_mesh_max_memory = 1000 + + +# options for global ocean testcases +[global_ocean] + +## each mesh should replace these with appropriate values in its config file ## config options related to the initial_state step # number of cores to use diff --git a/compass/ocean/tests/global_ocean/init/initial_state.py b/compass/ocean/tests/global_ocean/init/initial_state.py index a5a0da92f0..155039f0b4 100644 --- a/compass/ocean/tests/global_ocean/init/initial_state.py +++ b/compass/ocean/tests/global_ocean/init/initial_state.py @@ -113,7 +113,7 @@ def __init__(self, test_case, mesh, initial_condition, with_bgc): '1timeLevel.nc', database='initial_condition_database') - mesh_path = mesh.mesh_step.path + mesh_path = self.mesh.get_cull_mesh_path() self.add_input_file( filename='mesh.nc', @@ -123,7 +123,7 @@ def __init__(self, test_case, mesh, initial_condition, with_bgc): filename='graph.info', work_dir_target=f'{mesh_path}/culled_graph.info') - if mesh.with_ice_shelf_cavities: + if self.mesh.with_ice_shelf_cavities: self.add_input_file( filename='land_ice_mask.nc', work_dir_target=f'{mesh_path}/land_ice_mask.nc') diff --git a/compass/ocean/tests/global_ocean/init/ssh_adjustment.py b/compass/ocean/tests/global_ocean/init/ssh_adjustment.py index d85de6c7a3..fab3ee5731 100644 --- a/compass/ocean/tests/global_ocean/init/ssh_adjustment.py +++ b/compass/ocean/tests/global_ocean/init/ssh_adjustment.py @@ -49,7 +49,7 @@ def __init__(self, test_case, ntasks=None, min_tasks=None, self.add_streams_file('compass.ocean.tests.global_ocean.init', 'streams.ssh_adjust') - mesh_path = test_case.mesh.mesh_step.path + mesh_path = test_case.mesh.get_cull_mesh_path() init_path = test_case.steps['initial_state'].path self.add_input_file( diff --git a/compass/ocean/tests/global_ocean/mesh/__init__.py b/compass/ocean/tests/global_ocean/mesh/__init__.py index 9e24b5aa97..1655aa4d09 100644 --- a/compass/ocean/tests/global_ocean/mesh/__init__.py +++ b/compass/ocean/tests/global_ocean/mesh/__init__.py @@ -1,8 +1,10 @@ from compass.testcase import TestCase -from compass.ocean.tests.global_ocean.mesh.qu240 import QU240Mesh -from compass.ocean.tests.global_ocean.mesh.ec30to60 import EC30to60Mesh -from compass.ocean.tests.global_ocean.mesh.so12to60 import SO12to60Mesh -from compass.ocean.tests.global_ocean.mesh.wc14 import WC14Mesh +from compass.mesh.spherical import IcosahedralMeshStep, \ + QuasiUniformSphericalMeshStep +from compass.ocean.mesh.cull import CullMeshStep +from compass.ocean.tests.global_ocean.mesh.ec30to60 import EC30to60BaseMesh +from compass.ocean.tests.global_ocean.mesh.so12to60 import SO12to60BaseMesh +from compass.ocean.tests.global_ocean.mesh.wc14 import WC14BaseMesh from compass.ocean.tests.global_ocean.configure import configure_global_ocean from compass.validate import compare_variables @@ -13,8 +15,11 @@ class Mesh(TestCase): Attributes ---------- - mesh_step : compass.ocean.tests.global_ocean.mesh.mesh.MeshStep - The step for creating the mesh + package : str + The python package for the mesh + + mesh_config_filename : str + The name of the mesh config file with_ice_shelf_cavities : bool Whether the mesh includes ice-shelf cavities @@ -34,52 +39,51 @@ def __init__(self, test_group, mesh_name): name = 'mesh' subdir = '{}/{}'.format(mesh_name, name) super().__init__(test_group=test_group, name=name, subdir=subdir) - if mesh_name in 'QU240': - self.mesh_step = QU240Mesh(self, mesh_name, - with_ice_shelf_cavities=False) - elif mesh_name in 'QUwISC240': - self.mesh_step = QU240Mesh(self, mesh_name, - with_ice_shelf_cavities=True) - elif mesh_name in 'EC30to60': - self.mesh_step = EC30to60Mesh(self, mesh_name, - with_ice_shelf_cavities=False) - elif mesh_name in 'ECwISC30to60': - self.mesh_step = EC30to60Mesh(self, mesh_name, - with_ice_shelf_cavities=True) - elif mesh_name in 'SOwISC12to60': - self.mesh_step = SO12to60Mesh(self, mesh_name, - with_ice_shelf_cavities=True) + + with_ice_shelf_cavities = 'wISC' in mesh_name + mesh_lower = mesh_name.lower() + if with_ice_shelf_cavities: + mesh_lower = mesh_lower.replace('wisc', '') + if 'icos' in mesh_lower: + mesh_lower = mesh_lower.replace('icos', 'qu') + + self.package = f'compass.ocean.tests.global_ocean.mesh.{mesh_lower}' + self.mesh_config_filename = f'{mesh_lower}.cfg' + + self.mesh_name = mesh_name + self.with_ice_shelf_cavities = with_ice_shelf_cavities + + name = 'base_mesh' + subdir = None + if mesh_name in ['Icos240', 'IcoswISC240']: + base_mesh_step = IcosahedralMeshStep( + self, name=name, subdir=subdir, cell_width=240) + elif mesh_name in ['QU240', 'QUwISC240']: + base_mesh_step = QuasiUniformSphericalMeshStep( + self, name=name, subdir=subdir, cell_width=240) + elif mesh_name in ['EC30to60', 'ECwISC30to60']: + base_mesh_step = EC30to60BaseMesh(self, name=name, subdir=subdir) + elif mesh_name in ['SOwISC12to60']: + base_mesh_step = SO12to60BaseMesh(self, name=name, subdir=subdir) elif mesh_name in 'WC14': - self.mesh_step = WC14Mesh(self, mesh_name, - with_ice_shelf_cavities=False) + base_mesh_step = WC14BaseMesh(self, name=name, subdir=subdir) else: - raise ValueError('Unknown mesh name {}'.format(mesh_name)) + raise ValueError(f'Unknown mesh name {mesh_name}') - self.add_step(self.mesh_step) + self.add_step(base_mesh_step) - self.mesh_name = mesh_name - self.with_ice_shelf_cavities = self.mesh_step.with_ice_shelf_cavities + self.add_step(CullMeshStep( + test_case=self, base_mesh_step=base_mesh_step, + with_ice_shelf_cavities=self.with_ice_shelf_cavities)) def configure(self): """ Modify the configuration options for this test case """ configure_global_ocean(test_case=self, mesh=self) - - def run(self): - """ - Run each step of the testcase - """ - step = self.mesh_step config = self.config - # get the these properties from the config options - step.cpus_per_task = config.getint( - 'global_ocean', 'mesh_cpus_per_task') - step.min_cpus_per_task = config.getint( - 'global_ocean', 'mesh_min_cpus_per_task') - - # run the step - super().run() + config.set('spherical_mesh', 'add_mesh_density', 'True') + config.set('spherical_mesh', 'plot_cell_width', 'True') def validate(self): """ @@ -88,4 +92,14 @@ def validate(self): """ variables = ['xCell', 'yCell', 'zCell'] compare_variables(test_case=self, variables=variables, - filename1='mesh/culled_mesh.nc') + filename1='cull_mesh/culled_mesh.nc') + + def get_cull_mesh_path(self): + """ + Get the path of the cull mesh step (for input files) + Returns + ------- + cull_mesh_path : str + The path to the work directory of the cull mesh step. + """ + return self.steps['cull_mesh'].path diff --git a/compass/ocean/tests/global_ocean/mesh/ec30to60/__init__.py b/compass/ocean/tests/global_ocean/mesh/ec30to60/__init__.py index 1a599c7952..cb1b0e19d3 100644 --- a/compass/ocean/tests/global_ocean/mesh/ec30to60/__init__.py +++ b/compass/ocean/tests/global_ocean/mesh/ec30to60/__init__.py @@ -1,32 +1,13 @@ import numpy as np import mpas_tools.mesh.creation.mesh_definition_tools as mdt -from compass.ocean.tests.global_ocean.mesh.mesh import MeshStep +from compass.mesh import QuasiUniformSphericalMeshStep -class EC30to60Mesh(MeshStep): +class EC30to60BaseMesh(QuasiUniformSphericalMeshStep): """ A step for creating EC30to60 and ECwISC30to60 meshes """ - def __init__(self, test_case, mesh_name, with_ice_shelf_cavities): - """ - Create a new step - - Parameters - ---------- - test_case : compass.TestCase - The test case this step belongs to - - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities - """ - - super().__init__(test_case, mesh_name, with_ice_shelf_cavities, - package=self.__module__, - mesh_config_filename='ec30to60.cfg') def build_cell_width_lat_lon(self): """ diff --git a/compass/ocean/tests/global_ocean/mesh/mesh.py b/compass/ocean/tests/global_ocean/mesh/mesh.py deleted file mode 100644 index ea406d9192..0000000000 --- a/compass/ocean/tests/global_ocean/mesh/mesh.py +++ /dev/null @@ -1,138 +0,0 @@ -from mpas_tools.ocean import build_spherical_mesh -from mpas_tools.ocean import inject_bathymetry -from abc import abstractmethod - -from compass.ocean.tests.global_ocean.mesh.cull import cull_mesh -from compass.step import Step - - -class MeshStep(Step): - """ - A step for creating a global MPAS-Ocean mesh - - Attributes - ---------- - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities - - package : str - The python package for the mesh - - mesh_config_filename : str - The name of the mesh config file - """ - def __init__(self, test_case, mesh_name, with_ice_shelf_cavities, - package, mesh_config_filename, name='mesh', subdir=None, - do_inject_bathymetry=False, preserve_floodplain=False): - """ - Create a new step - - Parameters - ---------- - test_case : compass.ocean.tests.global_ocean.mesh.Mesh - The test case this step belongs to - - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities - - package : str - The python package for the mesh - - mesh_config_filename : str - The name of the mesh config file - - name : str, optional - the name of the step - - subdir : str, optional - the subdirectory for the step. The default is ``name`` - - do_inject_bathymetry : bool, optional - Whether to interpolate bathymetry from a data file so it - can be used as a culling criteria - - preserve_floodplain : bool, optional - Whether to leave land cells in the mesh based on bathymetry - specified by do_inject_bathymetry - """ - super().__init__(test_case, name=name, subdir=subdir, ntasks=1, - min_tasks=1, cpus_per_task=None, - min_cpus_per_task=None, openmp_threads=1) - for file in ['culled_mesh.nc', 'culled_graph.info', - 'critical_passages_mask_final.nc']: - self.add_output_file(filename=file) - - self.mesh_name = mesh_name - self.with_ice_shelf_cavities = with_ice_shelf_cavities - self.package = package - self.mesh_config_filename = mesh_config_filename - self.do_inject_bathymetry = do_inject_bathymetry - self.preserve_floodplain = preserve_floodplain - - def setup(self): - """ - Set up the test case in the work directory, including downloading any - dependencies. - """ - # get the these properties from the config options - config = self.config - self.cpus_per_task = config.getint('global_ocean', - 'mesh_cpus_per_task') - self.min_cpus_per_task = config.getint('global_ocean', - 'mesh_min_cpus_per_task') - - def run(self): - """ - Run this step of the test case - """ - with_ice_shelf_cavities = self.with_ice_shelf_cavities - logger = self.logger - do_inject_bathymetry = self.do_inject_bathymetry - preserve_floodplain = self.preserve_floodplain - floodplain_elevation = self.config.getfloat('global_ocean', - 'floodplain_elevation') - - # only use progress bars if we're not writing to a log file - use_progress_bar = self.log_filename is None - - # create the base mesh - cellWidth, lon, lat = self.build_cell_width_lat_lon() - build_spherical_mesh(cellWidth, lon, lat, out_filename='base_mesh.nc', - logger=logger, use_progress_bar=use_progress_bar, - do_inject_bathymetry=do_inject_bathymetry, - preserve_floodplain=preserve_floodplain, - floodplain_elevation=floodplain_elevation) - - cull_mesh(with_critical_passages=True, logger=logger, - use_progress_bar=use_progress_bar, - preserve_floodplain=preserve_floodplain, - with_cavities=with_ice_shelf_cavities) - - if do_inject_bathymetry: - inject_bathymetry(mesh_file='culled_mesh.nc') - - @abstractmethod - def build_cell_width_lat_lon(self): - """ - A function for creating cell width array for this mesh on a regular - latitude-longitude grid. Child classes need to override this function - to return the expected data - - Returns - ------- - cellWidth : numpy.array - m x n array of cell width in km - - lon : numpy.array - longitude in degrees (length n and between -180 and 180) - - lat : numpy.array - longitude in degrees (length m and between -90 and 90) - """ - pass diff --git a/compass/ocean/tests/global_ocean/mesh/qu240/__init__.py b/compass/ocean/tests/global_ocean/mesh/qu240/__init__.py index 5174e1de3a..e69de29bb2 100644 --- a/compass/ocean/tests/global_ocean/mesh/qu240/__init__.py +++ b/compass/ocean/tests/global_ocean/mesh/qu240/__init__.py @@ -1,56 +0,0 @@ -import numpy as np - -from compass.ocean.tests.global_ocean.mesh.mesh import MeshStep - - -class QU240Mesh(MeshStep): - """ - A step for creating QU240 and QUwISC240 meshes - """ - def __init__(self, test_case, mesh_name, with_ice_shelf_cavities): - """ - Create a new step - - Parameters - ---------- - test_case : compass.TestCase - The test case this step belongs to - - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities - """ - - super().__init__(test_case, mesh_name, with_ice_shelf_cavities, - package=self.__module__, - mesh_config_filename='qu240.cfg') - - def build_cell_width_lat_lon(self): - """ - Create cell width array for this mesh on a regular latitude-longitude - grid - - Returns - ------- - cellWidth : numpy.array - m x n array of cell width in km - - lon : numpy.array - longitude in degrees (length n and between -180 and 180) - - lat : numpy.array - longitude in degrees (length m and between -90 and 90) - """ - dlon = 10. - dlat = dlon - constantCellWidth = 240. - - nlat = int(180/dlat) + 1 - nlon = int(360/dlon) + 1 - lat = np.linspace(-90., 90., nlat) - lon = np.linspace(-180., 180., nlon) - - cellWidth = constantCellWidth * np.ones((lat.size, lon.size)) - return cellWidth, lon, lat diff --git a/compass/ocean/tests/global_ocean/mesh/so12to60/__init__.py b/compass/ocean/tests/global_ocean/mesh/so12to60/__init__.py index 096a5f31a3..d1df6f4c4a 100644 --- a/compass/ocean/tests/global_ocean/mesh/so12to60/__init__.py +++ b/compass/ocean/tests/global_ocean/mesh/so12to60/__init__.py @@ -6,39 +6,26 @@ from geometric_features import read_feature_collection from mpas_tools.cime.constants import constants -from compass.ocean.tests.global_ocean.mesh.mesh import MeshStep +from compass.mesh import QuasiUniformSphericalMeshStep -class SO12to60Mesh(MeshStep): +class SO12to60BaseMesh(QuasiUniformSphericalMeshStep): """ A step for creating SOwISC12to60 meshes """ - def __init__(self, test_case, mesh_name, with_ice_shelf_cavities): + def setup(self): """ - Create a new step - - Parameters - ---------- - test_case : compass.TestCase - The test case this step belongs to - - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities + Add some input files """ - super().__init__(test_case, mesh_name, with_ice_shelf_cavities, - package=self.__module__, - mesh_config_filename='so12to60.cfg') - self.add_input_file(filename='atlantic.geojson', package=self.__module__) self.add_input_file(filename='high_res_region.geojson', package=self.__module__) + super().setup() + def build_cell_width_lat_lon(self): """ Create cell width array for this mesh on a regular latitude-longitude diff --git a/compass/ocean/tests/global_ocean/mesh/wc14/__init__.py b/compass/ocean/tests/global_ocean/mesh/wc14/__init__.py index 0cd1baee2a..2583e9b941 100644 --- a/compass/ocean/tests/global_ocean/mesh/wc14/__init__.py +++ b/compass/ocean/tests/global_ocean/mesh/wc14/__init__.py @@ -10,33 +10,18 @@ from mpas_tools.cime.constants import constants from mpas_tools.viz.colormaps import register_sci_viz_colormaps -from compass.ocean.tests.global_ocean.mesh.mesh import MeshStep +from compass.mesh import QuasiUniformSphericalMeshStep -class WC14Mesh(MeshStep): +class WC14BaseMesh(QuasiUniformSphericalMeshStep): """ A step for creating SOwISC12to60 meshes """ - def __init__(self, test_case, mesh_name, with_ice_shelf_cavities): + def setup(self): """ - Create a new step - - Parameters - ---------- - test_case : compass.ocean.tests.global_ocean.Mesh - The test case this step belongs to - - mesh_name : str - The name of the mesh - - with_ice_shelf_cavities : bool - Whether the mesh includes ice-shelf cavities + Add some input files """ - super().__init__(test_case, mesh_name, with_ice_shelf_cavities, - package=self.__module__, - mesh_config_filename='wc14.cfg') - inputs = ['coastline_CUSP.geojson', 'land_mask_Kamchatka.geojson', 'land_mask_Mexico.geojson', @@ -51,6 +36,8 @@ def __init__(self, test_case, mesh_name, with_ice_shelf_cavities): self.add_input_file(filename=filename, package=self.__module__) + super().setup() + def build_cell_width_lat_lon(self): """ Create cell width array for this mesh on a regular latitude-longitude diff --git a/compass/ocean/tests/hurricane/configure.py b/compass/ocean/tests/hurricane/configure.py index c7708e937f..4f0db8eff9 100644 --- a/compass/ocean/tests/hurricane/configure.py +++ b/compass/ocean/tests/hurricane/configure.py @@ -13,13 +13,11 @@ def configure_hurricane(test_case, mesh): mesh : compass.ocean.tests.global_ocean.mesh.Mesh The test case that produces the mesh for this run - - init : compass.ocean.tests.global_ocean.init.Init, optional - The test case that produces the initial condition for this run """ config = test_case.config - mesh_step = mesh.mesh_step - config.add_from_package(mesh_step.package, mesh_step.mesh_config_filename, + + config.add_from_package('compass.mesh', 'mesh.cfg') + config.add_from_package(mesh.package, mesh.mesh_config_filename, exception=True) get_author_and_email_from_git(config) diff --git a/compass/ocean/tests/hurricane/forward/forward.py b/compass/ocean/tests/hurricane/forward/forward.py index 37a444bfea..cc2047cce0 100644 --- a/compass/ocean/tests/hurricane/forward/forward.py +++ b/compass/ocean/tests/hurricane/forward/forward.py @@ -45,7 +45,7 @@ def __init__(self, test_case, mesh, init, name='forward', subdir=None): self.add_streams_file( 'compass.ocean.tests.hurricane.forward', 'streams.ocean') - mesh_package = mesh.mesh_step.package + mesh_package = mesh.package self.add_namelist_file(mesh_package, 'namelist.ocean') initial_state_target = \ diff --git a/compass/ocean/tests/hurricane/init/create_pointstats_file.py b/compass/ocean/tests/hurricane/init/create_pointstats_file.py index e026aa378d..564f44031d 100644 --- a/compass/ocean/tests/hurricane/init/create_pointstats_file.py +++ b/compass/ocean/tests/hurricane/init/create_pointstats_file.py @@ -47,7 +47,7 @@ def __init__(self, test_case, mesh, storm): 'USGS_stations.txt'] self.pointstats_file = 'points.nc' - mesh_path = mesh.mesh_step.path + mesh_path = mesh.steps['cull_mesh'].path self.add_input_file( filename=self.mesh_file, diff --git a/compass/ocean/tests/hurricane/init/initial_state.py b/compass/ocean/tests/hurricane/init/initial_state.py index 9064b0a620..6a9f13c164 100644 --- a/compass/ocean/tests/hurricane/init/initial_state.py +++ b/compass/ocean/tests/hurricane/init/initial_state.py @@ -38,7 +38,7 @@ def __init__(self, test_case, mesh): # generate the streams file self.add_streams_file(package, 'streams.init', mode='init') - mesh_path = mesh.mesh_step.path + mesh_path = mesh.steps['cull_mesh'].path self.add_input_file( filename='mesh.nc', diff --git a/compass/ocean/tests/hurricane/init/interpolate_atm_forcing.py b/compass/ocean/tests/hurricane/init/interpolate_atm_forcing.py index 665c9f0a2b..f3b79c3601 100644 --- a/compass/ocean/tests/hurricane/init/interpolate_atm_forcing.py +++ b/compass/ocean/tests/hurricane/init/interpolate_atm_forcing.py @@ -75,7 +75,7 @@ def __init__(self, test_case, mesh, storm): target=f'{storm}_prmsl.nc', database='initial_condition_database') - mesh_path = mesh.mesh_step.path + mesh_path = mesh.steps['cull_mesh'].path self.add_input_file( filename='mesh.nc', diff --git a/compass/ocean/tests/hurricane/mesh/__init__.py b/compass/ocean/tests/hurricane/mesh/__init__.py index 6202d37fb1..7006427e60 100644 --- a/compass/ocean/tests/hurricane/mesh/__init__.py +++ b/compass/ocean/tests/hurricane/mesh/__init__.py @@ -1,16 +1,13 @@ from compass.testcase import TestCase from compass.ocean.tests.hurricane.mesh.dequ120at30cr10rr2 \ - import DEQU120at30cr10rr2Mesh + import DEQU120at30cr10rr2BaseMesh +from compass.ocean.tests.hurricane.configure import configure_hurricane +from compass.ocean.mesh.cull import CullMeshStep class Mesh(TestCase): """ A test case for creating a global MPAS-Ocean mesh - - Attributes - ---------- - mesh_step : compass.ocean.tests.global_ocean.mesh.mesh.MeshStep - The step for creating the mesh """ def __init__(self, test_group, mesh_name): """ @@ -28,39 +25,31 @@ def __init__(self, test_group, mesh_name): name = 'mesh' subdir = '{}/{}'.format(mesh_name, name) super().__init__(test_group=test_group, name=name, subdir=subdir) + + name = 'base_mesh' if mesh_name == 'DEQU120at30cr10rr2': - self.mesh_step = DEQU120at30cr10rr2Mesh( - self, mesh_name, - preserve_floodplain=False) + base_mesh_step = DEQU120at30cr10rr2BaseMesh( + self, name=name, preserve_floodplain=False) + mesh_lower = 'dequ120at30cr10rr2' elif mesh_name == 'DEQU120at30cr10rr2WD': - self.mesh_step = DEQU120at30cr10rr2Mesh( - self, mesh_name, - preserve_floodplain=True) + base_mesh_step = DEQU120at30cr10rr2BaseMesh( + self, name=name, preserve_floodplain=True) + mesh_lower = 'dequ120at30cr10rr2' else: raise ValueError(f'Unexpected mesh name {mesh_name}') - self.add_step(self.mesh_step) + self.package = f'compass.ocean.tests.hurricane.mesh.{mesh_lower}' + self.mesh_config_filename = f'{mesh_lower}.cfg' + + self.add_step(base_mesh_step) + + self.add_step(CullMeshStep( + test_case=self, base_mesh_step=base_mesh_step, + with_ice_shelf_cavities=False, do_inject_bathymetry=True, + preserve_floodplain=True)) def configure(self): """ Modify the configuration options for this test case """ - self.config.add_from_package(self.mesh_step.package, - self.mesh_step.mesh_config_filename, - exception=True) - - def run(self): - """ - Run each step of the testcase - """ - step = self.mesh_step - config = self.config - - # get the these properties from the config options - step.cpus_per_task = config.getint( - 'global_ocean', 'mesh_cpus_per_task') - step.min_cpus_per_task = config.getint( - 'global_ocean', 'mesh_min_cpus_per_task') - - # run the step - super().run() + configure_hurricane(test_case=self, mesh=self) diff --git a/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/__init__.py b/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/__init__.py index 573f6d6a21..5e5951fb02 100644 --- a/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/__init__.py +++ b/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/__init__.py @@ -1,41 +1,12 @@ import mpas_tools.ocean.coastal_tools as ct -from compass.ocean.tests.global_ocean.mesh.mesh import MeshStep +from compass.ocean.mesh.floodplain import FloodplainMeshStep -class DEQU120at30cr10rr2Mesh(MeshStep): +class DEQU120at30cr10rr2BaseMesh(FloodplainMeshStep): """ A step for creating DEQU120at30cr10rr2 meshes """ - def __init__(self, test_case, mesh_name, preserve_floodplain): - """ - Create a new step - - Parameters - ---------- - test_case : compass.TestCase - The test case this step belongs to - - mesh_name : str - The name of the mesh - - preserve_floodplain : bool - Whether the mesh includes land cells - """ - - with_ice_shelf_cavities = False - - super().__init__(test_case, mesh_name, with_ice_shelf_cavities, - package=self.__module__, - mesh_config_filename='dequ120at30cr10rr2.cfg', - do_inject_bathymetry=True, - preserve_floodplain=preserve_floodplain) - - self.add_input_file( - filename='earth_relief_15s.nc', - target='SRTM15_plus_earth_relief_15s.nc', - database='bathymetry_database') - def build_cell_width_lat_lon(self): """ Create cell width array for this mesh on a regular latitude-longitude diff --git a/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/dequ120at30cr10rr2.cfg b/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/dequ120at30cr10rr2.cfg index 0ddf050d72..6f37302e9d 100644 --- a/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/dequ120at30cr10rr2.cfg +++ b/compass/ocean/tests/hurricane/mesh/dequ120at30cr10rr2/dequ120at30cr10rr2.cfg @@ -1,19 +1,25 @@ -# options for global ocean testcases -[global_ocean] +# options for spherical meshes +[spherical_mesh] -## config options related to the mesh step +## config options related to the step for culling land from the mesh # number of cores to use -mesh_cpus_per_task = 1 +cull_mesh_cpus_per_task = 18 # minimum of cores, below which the step fails -mesh_min_cpus_per_task = 1 +cull_mesh_min_cpus_per_task = 1 +# maximum memory usage allowed (in MB) +cull_mesh_max_memory = 1000 + +# Elevation threshold to use for including land cells +floodplain_elevation = 10.0 + + +# options for global ocean testcases +[global_ocean] # The following options are detected from .gitconfig if not explicitly entered author = autodetect email = autodetect -# Elevation threshold to use for including land cells -floodplain_elevation = 10.0 - # options for hurricane testcases [hurricane] diff --git a/docs/developers_guide/ocean/api.rst b/docs/developers_guide/ocean/api.rst index d5d704910f..1796805acf 100644 --- a/docs/developers_guide/ocean/api.rst +++ b/docs/developers_guide/ocean/api.rst @@ -166,32 +166,25 @@ test cases and steps mesh.Mesh mesh.Mesh.configure mesh.Mesh.run - mesh.cull.cull_mesh - mesh.mesh.MeshStep - mesh.mesh.MeshStep.setup - mesh.mesh.MeshStep.run - mesh.mesh.MeshStep.build_cell_width_lat_lon - mesh.ec30to60.EC30to60Mesh - mesh.ec30to60.EC30to60Mesh.build_cell_width_lat_lon + mesh.ec30to60.EC30to60BaseMesh + mesh.ec30to60.EC30to60BaseMesh.build_cell_width_lat_lon mesh.ec30to60.dynamic_adjustment.EC30to60DynamicAdjustment mesh.ec30to60.dynamic_adjustment.EC30to60DynamicAdjustment.configure mesh.ec30to60.dynamic_adjustment.EC30to60DynamicAdjustment.run - mesh.qu240.QU240Mesh - mesh.qu240.QU240Mesh.build_cell_width_lat_lon mesh.qu240.dynamic_adjustment.QU240DynamicAdjustment mesh.qu240.dynamic_adjustment.QU240DynamicAdjustment.configure mesh.qu240.dynamic_adjustment.QU240DynamicAdjustment.run - mesh.so12to60.SO12to60Mesh - mesh.so12to60.SO12to60Mesh.build_cell_width_lat_lon + mesh.so12to60.SO12to60BaseMesh + mesh.so12to60.SO12to60BaseMesh.build_cell_width_lat_lon mesh.so12to60.dynamic_adjustment.SO12to60DynamicAdjustment mesh.so12to60.dynamic_adjustment.SO12to60DynamicAdjustment.configure mesh.so12to60.dynamic_adjustment.SO12to60DynamicAdjustment.run - mesh.wc14.WC14Mesh - mesh.wc14.WC14Mesh.build_cell_width_lat_lon + mesh.wc14.WC14BaseMesh + mesh.wc14.WC14BaseMesh.build_cell_width_lat_lon mesh.wc14.dynamic_adjustment.WC14DynamicAdjustment mesh.wc14.dynamic_adjustment.WC14DynamicAdjustment.configure mesh.wc14.dynamic_adjustment.WC14DynamicAdjustment.run @@ -281,8 +274,8 @@ test cases and steps mesh.Mesh.configure mesh.Mesh.run - mesh.dequ120at30cr10rr2.DEQU120at30cr10rr2Mesh - mesh.dequ120at30cr10rr2.DEQU120at30cr10rr2Mesh.build_cell_width_lat_lon + mesh.dequ120at30cr10rr2.DEQU120at30cr10rr2BaseMesh + mesh.dequ120at30cr10rr2.DEQU120at30cr10rr2BaseMesh.build_cell_width_lat_lon init.Init init.Init.configure @@ -669,6 +662,14 @@ ocean framework .. autosummary:: :toctree: generated/ + mesh.cull.CullMeshStep + mesh.cull.CullMeshStep.setup + mesh.cull.CullMeshStep.run + mesh.cull.cull_mesh + + mesh.floodplain.FloodplainMeshStep + mesh.floodplain.FloodplainMeshStep.run + haney.compute_haney_number iceshelf.compute_land_ice_pressure_and_draft diff --git a/docs/developers_guide/ocean/framework.rst b/docs/developers_guide/ocean/framework.rst index 1b44481d56..2676015b77 100644 --- a/docs/developers_guide/ocean/framework.rst +++ b/docs/developers_guide/ocean/framework.rst @@ -37,6 +37,86 @@ and ``restingThickness`` variables for :ref:`ocean_z_level` and :ref:`ocean_z_star` coordinates using the ``ssh`` and ``bottomDepth`` as well as config options from ``vertical_grid``. +.. _dev_ocean_framework_mesh: + +Mesh +---- + +The ``compass.ocean.mesh`` package includes modules for spherical ocean meshes +shared across test groups. + + +.. _dev_ocean_framework_cull_mesh: + +Culling Meshes +~~~~~~~~~~~~~~ + +The ``compass.ocean.mesh.cull`` module is for culling land cells from +global ocean meshes. + +The class :py:class:`compass.ocean.mesh.cull.CullMeshStep` culls out land +cells by calling ``cull_mesh()``. + +:py:func:`compass.ocean.mesh.cull.cull_mesh()` uses a number of +capabilities from `MPAS-Tools `_ +and `geometric_features `_ +to cull the mesh. It performs the following steps: + +1. combining Natural Earth land coverage north of 60S with Antarctic + ice coverage or grounded ice coverage from BedMachineAntarctica + +2. combining transects defining critical passages (if + ``with_critical_passages=True``) + +3. combining points used to seed a flood fill of the global ocean. + +4. create masks from land coverage + +5. add land-locked cells to land coverage mask. + +6. create masks from transects (if ``with_critical_passages=True``) + +7. cull cells based on land coverage but with transects present + +8. create flood-fill mask based on seeds + +9. cull cells based on flood-fill mask + +10. create masks from transects on the final culled mesh (if + ``with_critical_passages=True``) + + +.. _dev_ocean_framework_floodplain: + +Including a Floodplain +~~~~~~~~~~~~~~~~~~~~~~ + +The ``compass.ocean.mesh.floodplain`` module is for adding support for a +floodplain to a base global ocean mesh. + +The class :py:class:`compass.ocean.mesh.floodplain.FloodplainMeshStep` +descends from :py:class:`compass.mesh.QuasiUniformSphericalMeshStep`, adding +an attribute: + +``self.preserve_floodplain`` + A ``bool`` defining whether the mesh includes land cells + +and including topography in the base mesh from the +`SRTM15_plus_earth_relief_15s.nc` file in the `bathymetry_database`. The +``run()`` method uses the config option: + +.. code-block:: ini + + # options for spherical meshes + [spherical_mesh] + + # Elevation threshold to use for including land cells + floodplain_elevation = 10.0 + +to determine the elevation of the floodplain to maintain above sea level. +The bathymetry and the floodplain are added to the mesh using +:py:func:`mpas_tools.ocean.inject_bathymetry()` and +:py:func:`mpas_tools.ocean.inject_preserve_floodplain()`, respectively. .. _dev_ocean_framework_haney: diff --git a/docs/developers_guide/ocean/test_groups/global_ocean.rst b/docs/developers_guide/ocean/test_groups/global_ocean.rst index 6fe63cc184..5d68ca9315 100644 --- a/docs/developers_guide/ocean/test_groups/global_ocean.rst +++ b/docs/developers_guide/ocean/test_groups/global_ocean.rst @@ -268,21 +268,10 @@ to improve model efficiency. A :py:class:`compass.ocean.tests.global_ocean.mesh.Mesh` object is constructed with the ``mesh_name`` as one of its arguments. Based on this argument, it determines the appropriate child class of -:py:class:`compass.ocean.tests.global_ocean.mesh.mesh.MeshStep` to create. +:py:class:`compass.mesh.spherical.SphericalBaseStep` to create the base mesh +and adds a :py:class:`compass.ocean.mesh.cull.CullMeshStep`. -.. _dev_ocean_global_ocean_mesh_step: - -mesh step -^^^^^^^^^ - -The parent class :py:class:`compass.ocean.tests.global_ocean.mesh.mesh.MeshStep` -defines a method -:py:meth:`compass.ocean.tests.global_ocean.mesh.mesh.MeshStep.build_cell_width_lat_lon()` -that a child class for each mesh type must override to define the size of -MPAS cells as a function of longitude and latitude. - -This class also takes care of defining the output from the step and storing -attributes: +This class also stores attributes: ``self.mesh_name`` the name of the mesh @@ -297,39 +286,6 @@ attributes: ``self.mesh_config_filename`` the name of the config file with mesh-specific config options -In its ``run()`` method, ``MeshStep`` calls the ``build_cell_width_lat_lon()`` -method to get the desired mesh resolution, then creates the mesh by calling -:py:func:`mpas_tools.ocean.build_mesh.build_spherical_mesh()` culls out land -cells by calling :py:func:`compass.ocean.tests.global_ocean.mesh.cull.cull_mesh()`. - -``cull_mesh()`` uses a number of capabilities from -`MPAS-Tools `_ -and `geometric_features `_ -to cull the mesh. It performs the following steps: - -1. combining Natural Earth land coverage north of 60S with Antarctic - ice coverage or grounded ice coverage from BedMachineAntarctica - -2. combining transects defining critical passages (if - ``with_critical_passages=True``) - -3. combining points used to seed a flood fill of the global ocean. - -4. create masks from land coverage - -5. add land-locked cells to land coverage mask. - -6. create masks from transects (if ``with_critical_passages=True``) - -7. cull cells based on land coverage but with transects present - -8. create flood-fill mask based on seeds - -9. cull cells based on flood-fill mask - -10. create masks from transects on the final culled mesh (if - ``with_critical_passages=True``) - .. _dev_ocean_global_ocean_meshes: meshes @@ -344,12 +300,12 @@ QU240 and QUwISC240 The ``QU240`` mesh is a quasi-uniform mesh with 240-km resolution. The ``QUwISC240`` mesh is identical except that it includes the cavities below ice -shelves in the ocean domain. The class -:py:class:`compass.ocean.tests.global_ocean.mesh.qu240.QU240Mesh` defines the -resolution for both meshes. The ``compass.ocean.tests.global_ocean.mesh.qu240`` -module includes namelist options appropriate for forward simulations with both -RK4 and split-explicit time integration on this mesh. These set the time step -and default run duration for short runs with this mesh. +shelves in the ocean domain. The mesh is defined by +:py:class:`compass.mesh.QuasiUniformSphericalMeshStep`. The +``compass.ocean.tests.global_ocean.mesh.qu240`` module includes namelist +options appropriate for forward simulations with both RK4 and split-explicit +time integration on this mesh. These set the time step and default run +duration for short runs with this mesh. The default config options for this mesh are: @@ -379,23 +335,19 @@ The default config options for this mesh are: ## config options related to the initial_state step # number of cores to use - init_cores = 4 + init_ntasks = 4 # minimum of cores, below which the step fails - init_min_cores = 1 + init_min_tasks = 1 # maximum memory usage allowed (in MB) init_max_memory = 1000 - # maximum disk usage allowed (in MB) - init_max_disk = 1000 ## config options related to the forward steps # number of cores to use - forward_cores = 4 + forward_ntasks = 4 # minimum of cores, below which the step fails - forward_min_cores = 1 + forward_min_tasks = 1 # maximum memory usage allowed (in MB) forward_max_memory = 1000 - # maximum disk usage allowed (in MB) - forward_max_disk = 1000 ## metadata related to the mesh # the prefix (e.g. QU, EC, WC, SO) @@ -420,6 +372,17 @@ The default config options for this mesh are: The vertical grid is a ``tanh_dz`` profile (see :ref:`dev_ocean_framework_vertical`) with 16 vertical levels ranging in thickness from 3 to 500 m. +.. _dev_ocean_global_ocean_isco240: + +Icos240 and IcoswISC240 ++++++++++++++++++++++++ + +The ``Icos240`` mesh is a subdivided icosahedral mesh with 240-km resolution +using the :py:class:`compass.mesh.IcosahedralMeshStep` class. The +``IcoswISC240`` mesh is identical except that it includes the cavities below +ice shelves in the ocean domain. Aside from the base mesh, these are identical +to :ref:`dev_ocean_global_ocean_qu240`. + .. _dev_ocean_global_ocean_ec30to60: EC30to60 and ECwISC30to60 @@ -454,23 +417,19 @@ The default config options for this mesh are: ## config options related to the initial_state step # number of cores to use - init_cores = 36 + init_ntasks = 36 # minimum of cores, below which the step fails - init_min_cores = 8 + init_min_tasks = 8 # maximum memory usage allowed (in MB) init_max_memory = 1000 - # maximum disk usage allowed (in MB) - init_max_disk = 1000 ## config options related to the forward steps # number of cores to use - forward_cores = 128 + forward_ntasks = 128 # minimum of cores, below which the step fails - forward_min_cores = 36 + forward_min_tasks = 36 # maximum memory usage allowed (in MB) forward_max_memory = 1000 - # maximum disk usage allowed (in MB) - forward_max_disk = 1000 ## metadata related to the mesh # the prefix (e.g. QU, EC, WC, SO) @@ -528,23 +487,19 @@ The default config options for this mesh are: ## config options related to the initial_state step # number of cores to use - init_cores = 36 + init_ntasks = 36 # minimum of cores, below which the step fails - init_min_cores = 8 + init_min_tasks = 8 # maximum memory usage allowed (in MB) init_max_memory = 1000 - # maximum disk usage allowed (in MB) - init_max_disk = 1000 ## config options related to the forward steps # number of cores to use - forward_cores = 1296 + forward_ntasks = 1296 # minimum of cores, below which the step fails - forward_min_cores = 128 + forward_min_tasks = 128 # maximum memory usage allowed (in MB) forward_max_memory = 1000 - # maximum disk usage allowed (in MB) - forward_max_disk = 1000 ## metadata related to the mesh # the prefix (e.g. QU, EC, WC, SO) @@ -607,23 +562,19 @@ The default config options for this mesh are: ## config options related to the initial_state step # number of cores to use - init_cores = 36 + init_ntasks = 36 # minimum of cores, below which the step fails - init_min_cores = 8 + init_min_tasks = 8 # maximum memory usage allowed (in MB) init_max_memory = 1000 - # maximum disk usage allowed (in MB) - init_max_disk = 1000 ## config options related to the forward steps # number of cores to use - forward_cores = 720 + forward_ntasks = 720 # minimum of cores, below which the step fails - forward_min_cores = 144 + forward_min_tasks = 144 # maximum memory usage allowed (in MB) forward_max_memory = 1000 - # maximum disk usage allowed (in MB) - forward_max_disk = 1000 ## metadata related to the mesh # the prefix (e.g. QU, EC, WC, SO) diff --git a/docs/users_guide/ocean/test_groups/global_ocean.rst b/docs/users_guide/ocean/test_groups/global_ocean.rst index 81d16a12b6..514f818679 100644 --- a/docs/users_guide/ocean/test_groups/global_ocean.rst +++ b/docs/users_guide/ocean/test_groups/global_ocean.rst @@ -33,44 +33,42 @@ Note that meshes and test cases may modify these options, as noted below. .. code-block:: cfg - # options for global ocean testcases - [global_ocean] - - ## each mesh should replace these with appropriate values in its config file + # options for spherical meshes + [spherical_mesh] - ## config options related to the mesh step + ## config options related to the step for culling land from the mesh # number of cores to use - mesh_cores = 1 + cull_mesh_cpus_per_task = 18 # minimum of cores, below which the step fails - mesh_min_cores = 1 + cull_mesh_min_cpus_per_task = 1 # maximum memory usage allowed (in MB) - mesh_max_memory = 1000 - # maximum disk usage allowed (in MB) - mesh_max_disk = 1000 + cull_mesh_max_memory = 1000 + + + # options for global ocean testcases + [global_ocean] + + ## each mesh should replace these with appropriate values in its config file ## config options related to the initial_state step # number of cores to use - init_cores = 4 + init_ntasks = 4 # minimum of cores, below which the step fails - init_min_cores = 1 + init_min_tasks = 1 # maximum memory usage allowed (in MB) init_max_memory = 1000 - # maximum disk usage allowed (in MB) - init_max_disk = 1000 # number of threads init_threads = 1 ## config options related to the forward steps # number of cores to use - forward_cores = 4 + forward_ntasks = 4 # minimum of cores, below which the step fails - forward_min_cores = 1 + forward_min_tasks = 1 # number of threads forward_threads = 1 # maximum memory usage allowed (in MB) forward_max_memory = 1000 - # maximum disk usage allowed (in MB) - forward_max_disk = 1000 ## metadata related to the mesh # whether to add metadata to output files @@ -105,6 +103,16 @@ Note that meshes and test cases may modify these options, as noted below. # The URL of the pull request documenting the creation of the mesh pull_request = <<>> + # Elevation threshold for including land cells + floodplain_elevation = 10.0 + + + # config options related to dynamic adjustment + [dynamic_adjustment] + + # the maximum allowed value of temperatureMax in global statistics + temperature_max = 33.0 + # config options related to initial condition and diagnostics support files # for E3SM @@ -140,8 +148,8 @@ Note that meshes and test cases may modify these options, as noted below. comparisonArcticStereoWidth = 6000. comparisonArcticStereoResolution = 10. -The ``mesh_*``, ``init_*`` and ``forward:*`` config options are used to specify -the resources used in in the ``mesh`` step of the :ref:`global_ocean_mesh`, +The ``cull_mesh_*``, ``init_*`` and ``forward:*`` config options are used to +specify the resources used in in the ``mesh`` step of the :ref:`global_ocean_mesh`, the ``initial_state`` step of the :ref:`global_ocean_init` and the :ref:`global_ocean_forward`, respectively. These values will differ between test cases and meshes. @@ -378,7 +386,7 @@ options directly in ``namelist.ocean`` or modifying streams in you can change in the config file for a test case. Since some test cases like :ref:`global_ocean_restart_test` and :ref`global_ocean_dynamic_adjustment` have more than one forward run, it is convenient to change options like -``forward_cores`` once in the config file, knowing that this will change the +``forward_ntasks`` once in the config file, knowing that this will change the target number of cores of all forward model runs in the test case. The same applies to the other ``forward_*`` config options that change the minimum cores allowed, the number of threads, and (in the future) the maximum memory and disk diff --git a/docs/users_guide/ocean/test_groups/hurricane.rst b/docs/users_guide/ocean/test_groups/hurricane.rst index e0418baada..f908a2413d 100644 --- a/docs/users_guide/ocean/test_groups/hurricane.rst +++ b/docs/users_guide/ocean/test_groups/hurricane.rst @@ -26,22 +26,28 @@ Note that meshes and test cases may modify these options, as noted below. .. code-block:: cfg - # options for global ocean testcases - [global_ocean] + # options for spherical meshes + [spherical_mesh] - ## config options related to the mesh step + ## config options related to the step for culling land from the mesh # number of cores to use - mesh_ntasks = 1 + cull_mesh_cpus_per_task = 18 # minimum of cores, below which the step fails - mesh_min_tasks = 1 + cull_mesh_min_cpus_per_task = 1 + # maximum memory usage allowed (in MB) + cull_mesh_max_memory = 1000 + + # Elevation threshold to use for including land cells + floodplain_elevation = 10.0 + + + # options for global ocean testcases + [global_ocean] # The following options are detected from .gitconfig if not explicitly entered author = autodetect email = autodetect - # Elevation threshold to use for including land cells - floodplain_elevation = 10.0 - # options for hurricane testcases [hurricane]