diff --git a/compass/ocean/__init__.py b/compass/ocean/__init__.py index ea766c088b..605db80b34 100644 --- a/compass/ocean/__init__.py +++ b/compass/ocean/__init__.py @@ -15,6 +15,7 @@ from compass.ocean.tests.sphere_transport import SphereTransport from compass.ocean.tests.spherical_harmonic_transform import \ SphericalHarmonicTransform +from compass.ocean.tests.tides import Tides from compass.ocean.tests.ziso import Ziso @@ -44,4 +45,5 @@ def __init__(self): self.add_test_group(Soma(mpas_core=self)) self.add_test_group(SphereTransport(mpas_core=self)) self.add_test_group(SphericalHarmonicTransform(mpas_core=self)) + self.add_test_group(Tides(mpas_core=self)) self.add_test_group(Ziso(mpas_core=self)) diff --git a/compass/ocean/tests/tides/__init__.py b/compass/ocean/tests/tides/__init__.py new file mode 100644 index 0000000000..ec029d4db5 --- /dev/null +++ b/compass/ocean/tests/tides/__init__.py @@ -0,0 +1,30 @@ +from compass.testgroup import TestGroup + +from compass.ocean.tests.tides.mesh import Mesh +from compass.ocean.tests.tides.init import Init +from compass.ocean.tests.tides.forward import Forward + + +class Tides(TestGroup): + """ + A test group for tidal simulations with MPAS-Ocean + """ + def __init__(self, mpas_core): + """ + mpas_core : compass.ocean.Ocean + the MPAS core that this test group belongs to + """ + super().__init__(mpas_core=mpas_core, + name='tides') + + for mesh_name in ['Icos7']: + + mesh = Mesh(test_group=self, mesh_name=mesh_name) + self.add_test_case(mesh) + + init = Init(test_group=self, mesh=mesh) + self.add_test_case(init) + + self.add_test_case(Forward(test_group=self, + mesh=mesh, + init=init)) diff --git a/compass/ocean/tests/tides/analysis/__init__.py b/compass/ocean/tests/tides/analysis/__init__.py new file mode 100644 index 0000000000..789e85dbc7 --- /dev/null +++ b/compass/ocean/tests/tides/analysis/__init__.py @@ -0,0 +1,452 @@ +from compass.step import Step + +import netCDF4 +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import numpy as np +import cartopy.crs as ccrs +import cartopy.feature as cfeature +import os +from mpas_tools.logging import check_call + + +class Analysis(Step): + """ + A step for producing harmonic constituent errors and validation plots + + Attributes + ---------- + harmonic_analysis_file : str + File containing MPAS-O constitents + + grid_file : str + Name of file containing MPAS-O mesh information + + constituents : list + List of constituents to extract from TPXO database + + tpxo_version : str + Version of TPXO to use in validation + """ + def __init__(self, test_case): + """ + Create the step + + Parameters + ---------- + test_case : compass.ocean.tests.tides.forward.Forward + The test case this step belongs to + """ + super().__init__(test_case=test_case, name='analysis') + + self.harmonic_analysis_file = 'harmonicAnalysis.nc' + self.grid_file = 'initial_state.nc' + self.constituents = ['k1', 'k2', 'm2', 'n2', 'o1', 'p1', 'q1', 's2'] + + self.add_input_file( + filename=self.harmonic_analysis_file, + target='../forward/analysis_members/harmonicAnalysis.nc') + + self.add_input_file( + filename=self.grid_file, + target='../forward/initial_state.nc') + + def setup(self): + """ + Setup test case and download data + """ + + config = self.config + self.tpxo_version = config.get('tides', 'tpxo_version') + + os.makedirs(f'{self.work_dir}/TPXO_data', exist_ok=True) + if self.tpxo_version == 'TPXO9': + for constituent in self.constituents: + self.add_input_file( + filename=f'TPXO_data/h_{constituent}_tpxo', + target=f'TPXO9/h_{constituent}_tpxo9_atlas_30_v5', + database='tides') + self.add_input_file( + filename=f'TPXO_data/u_{constituent}_tpxo', + target=f'TPXO9/u_{constituent}_tpxo9_atlas_30_v5', + database='tides') + self.add_input_file( + filename='TPXO_data/grid_tpxo', + target='TPXO9/grid_tpxo9_atlas_30_v5', + database='tides') + elif self.tpxo_version == 'TPXO8': + for constituent in self.constituents: + self.add_input_file( + filename=f'TPXO_data/h_{constituent}_tpxo', + target=f'TPXO8/hf.{constituent}_tpxo8_atlas_30c_v1.out', + database='tides') + self.add_input_file( + filename=f'TPXO_data/u_{constituent}_tpxo', + target=f'TPXO8/uv.{constituent}_tpxo8_atlas_30c_v1.out', + database='tides') + self.add_input_file( + filename='TPXO_data/grid_tpxo', + target='TPXO8/grid_tpxo8_atlas_30_v1', + database='tides') + + def write_coordinate_file(self, idx): + """ + Write mesh coordinates for TPXO extraction + """ + + # Read in mesh + grid_nc = netCDF4.Dataset(self.grid_file, 'r') + lon_grid = np.degrees(grid_nc.variables['lonCell'][idx]) + lat_grid = np.degrees(grid_nc.variables['latCell'][idx]) + nCells = len(lon_grid) + + # Write coordinate file for OTPS2 + f = open('lat_lon', 'w') + for i in range(nCells): + f.write(str(lat_grid[i])+' '+str(lon_grid[i])+'\n') + f.close() + + def setup_otps2(self): + """ + Write input files for TPXO extraction + """ + + for con in self.constituents: + print(f'setup {con}') + + # Lines for the setup_con files + lines = [{'inp': f'inputs/Model_atlas_{con}', + 'comment': '! 1. tidal model control file'}, + {'inp': 'lat_lon', + 'comment': '! 2. latitude/longitude/