-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #872 from vanroekel/add_woa23_salinity_restoring
Adds WOA 23 SSS restoring file script
- Loading branch information
Showing
8 changed files
with
310 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
compass/ocean/tests/utility/create_salin_restoring/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from compass.ocean.tests.utility.create_salin_restoring.extrap_salin import ( | ||
ExtrapSalin, | ||
) | ||
from compass.ocean.tests.utility.create_salin_restoring.salinity_restoring import ( # noqa: E501 | ||
Salinity, | ||
) | ||
from compass.testcase import TestCase | ||
|
||
|
||
class CreateSalinRestoring(TestCase): | ||
""" | ||
A test case for first creating monthly sea surface salinity from WOA23 | ||
then extrapolating the WOA23 data into missing ocean regions such as | ||
ice-shelf cavities and coasts | ||
""" | ||
|
||
def __init__(self, test_group): | ||
""" | ||
Create the test case | ||
Parameters | ||
---------- | ||
test_group : compass.ocean.tests.utility.Utility | ||
The test group that this test case belongs to | ||
""" | ||
super().__init__(test_group=test_group, name='create_salin_restoring') | ||
|
||
self.add_step(Salinity(test_case=self)) | ||
self.add_step(ExtrapSalin(test_case=self)) |
122 changes: 122 additions & 0 deletions
122
compass/ocean/tests/utility/create_salin_restoring/extrap_salin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
from datetime import datetime | ||
|
||
import numpy as np | ||
import xarray as xr | ||
from scipy.signal import convolve2d | ||
|
||
from compass.step import Step | ||
|
||
|
||
class ExtrapSalin(Step): | ||
""" | ||
Extrapolate WOA 2023 monthly sea surface salinity data into missing ocean | ||
regions, including ice cavities and coasts | ||
Attributes | ||
---------- | ||
woa_filename : str | ||
The name of the output file name after extrapolation | ||
""" | ||
def __init__(self, test_case): | ||
""" | ||
Create a new test case | ||
Parameters | ||
---------- | ||
test_case : compass.ocean.tests.utility.create_salin_restoring. | ||
CreateSalinRestoring | ||
The test case this step belongs to | ||
""" | ||
super().__init__(test_case=test_case, name='extrap', cpus_per_task=64, | ||
min_cpus_per_task=1, openmp_threads=1) | ||
|
||
self.add_input_file( | ||
filename='woa_surface_salinity_monthly.nc', | ||
target='../salinity_restoring/woa_surface_salinity_monthly.nc') | ||
|
||
self.woa_filename = None | ||
|
||
def setup(self): | ||
""" | ||
Determine the output filename | ||
""" | ||
|
||
now = datetime.now() | ||
|
||
datestring = now.strftime("%Y%m%d") | ||
|
||
self.woa_filename = f'woa23_decav_0.25_sss_monthly_extrap.' \ | ||
f'{datestring}.nc' | ||
self.add_output_file(self.woa_filename) | ||
|
||
def run(self): | ||
""" | ||
Extrapolate WOA 2023 model temperature and salinity into ice-shelf | ||
cavities. | ||
""" | ||
# extrapolate horizontally using the ocean mask | ||
_extrap(self.woa_filename) | ||
|
||
|
||
def _extrap(out_filename): | ||
|
||
in_filename = 'woa_surface_salinity_monthly.nc' | ||
ds = xr.open_dataset(in_filename) | ||
|
||
field = ds.SALT.values.copy() | ||
|
||
# a small averaging kernel | ||
x = np.arange(-1, 2) | ||
x, y = np.meshgrid(x, x) | ||
kernel = np.exp(-0.5 * (x**2 + y**2)) | ||
|
||
threshold = 0.01 | ||
nlon = field.shape[-1] | ||
|
||
lon_with_halo = np.array([nlon - 2, nlon - 1] + list(range(nlon)) + [0, 1]) | ||
lon_no_halo = list(range(2, nlon + 2)) | ||
|
||
for i in range(12): | ||
valid = np.isfinite(field[i, :, :]) | ||
orig_mask = valid | ||
prev_fill_count = 0 | ||
while True: | ||
valid_weight_sum = _extrap_with_halo(valid, kernel, valid, | ||
lon_with_halo, lon_no_halo) | ||
|
||
new_valid = valid_weight_sum > threshold | ||
|
||
# don't want to overwrite original data but do want ot smooth | ||
# extrapolated data | ||
fill_mask = np.logical_and(new_valid, np.logical_not(orig_mask)) | ||
|
||
fill_count = np.count_nonzero(fill_mask) | ||
if fill_count == prev_fill_count: | ||
# no change so we're done | ||
break | ||
|
||
field_extrap = _extrap_with_halo(field[i, :, :], kernel, valid, | ||
lon_with_halo, lon_no_halo) | ||
field[i, fill_mask] = field_extrap[fill_mask] / \ | ||
valid_weight_sum[fill_mask] | ||
|
||
valid = new_valid | ||
prev_fill_count = fill_count | ||
|
||
attrs = ds.SALT.attrs | ||
dims = ds.SALT.dims | ||
ds['SALT'] = (dims, field) | ||
ds['SALT'].attrs = attrs | ||
|
||
ds.to_netcdf(out_filename) | ||
|
||
|
||
def _extrap_with_halo(field, kernel, valid, lon_with_halo, lon_no_halo): | ||
field = field.copy() | ||
field[np.logical_not(valid)] = 0. | ||
field_with_halo = field[:, lon_with_halo] | ||
field_extrap = convolve2d(field_with_halo, kernel, mode='same') | ||
field_extrap = field_extrap[:, lon_no_halo] | ||
return field_extrap |
113 changes: 113 additions & 0 deletions
113
compass/ocean/tests/utility/create_salin_restoring/salinity_restoring.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import numpy as np | ||
import xarray as xr | ||
from mpas_tools.io import write_netcdf | ||
|
||
from compass.step import Step | ||
|
||
|
||
class Salinity(Step): | ||
""" | ||
A step for combining January through December sea surface salinity | ||
data into a single file for salinity restoring in G-cases. | ||
The top level data of the monthly WOA23 is utilized. | ||
""" | ||
|
||
def __init__(self, test_case): | ||
""" | ||
Create a new step | ||
Parameters | ||
---------- | ||
test_case : compass.ocean.tests.utility.create_salin_restoring. | ||
CreateSalinRestoring | ||
The test case this step belongs to | ||
""" | ||
super().__init__(test_case, name='salinity_restoring', ntasks=1, | ||
min_tasks=1) | ||
self.add_output_file(filename='woa_surface_salinity_monthly.nc') | ||
|
||
def setup(self): | ||
""" | ||
Set up the step in the work directory, including downloading any | ||
dependencies. | ||
""" | ||
super().setup() | ||
|
||
base_url = \ | ||
'https://www.ncei.noaa.gov/thredds-ocean/fileServer/woa23/DATA' | ||
|
||
woa_dir = 'salinity/netcdf/decav91C0/0.25' | ||
|
||
woa_files = dict(jan='woa23_decav91C0_s01_04.nc', | ||
feb='woa23_decav91C0_s02_04.nc', | ||
mar='woa23_decav91C0_s03_04.nc', | ||
apr='woa23_decav91C0_s04_04.nc', | ||
may='woa23_decav91C0_s05_04.nc', | ||
jun='woa23_decav91C0_s06_04.nc', | ||
jul='woa23_decav91C0_s07_04.nc', | ||
aug='woa23_decav91C0_s08_04.nc', | ||
sep='woa23_decav91C0_s09_04.nc', | ||
octo='woa23_decav91C0_s10_04.nc', | ||
nov='woa23_decav91C0_s11_04.nc', | ||
dec='woa23_decav91C0_s12_04.nc') | ||
|
||
for month in ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', | ||
'aug', 'sep', 'octo', 'nov', 'dec']: | ||
woa_filename = woa_files[month] | ||
woa_url = f'{base_url}/{woa_dir}/{woa_filename}' | ||
|
||
self.add_input_file( | ||
filename=f'woa_salin_{month}.nc', | ||
target=woa_filename, | ||
database='initial_condition_database', | ||
url=woa_url) | ||
|
||
def run(self): | ||
""" | ||
Run this step of the test case | ||
""" | ||
ds_jan = xr.open_dataset('woa_salin_jan.nc', decode_times=False) | ||
|
||
ds_out = xr.Dataset() | ||
|
||
for var in ['lon', 'lat']: | ||
ds_out[var] = ds_jan[var] | ||
ds_out[f'{var}_bnds'] = ds_jan[f'{var}_bnds'] | ||
|
||
slices = list() | ||
for month in ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', | ||
'aug', 'sep', 'octo', 'nov', 'dec']: | ||
ds = xr.open_dataset( | ||
f'woa_salin_{month}.nc', | ||
decode_times=False).isel(depth=0). \ | ||
drop_vars('depth') | ||
slices.append(ds.s_an) | ||
|
||
ds_out['s_an'] = xr.concat(slices, dim='time') | ||
ds_out['s_an'].attrs = ds_jan['s_an'].attrs | ||
|
||
# Change names of variables and alter attributes | ||
ds_out = ds_out.rename_dims({'time': 'Time'}) | ||
ds_out = ds_out.rename_vars({'s_an': 'SALT'}) | ||
ds_out = ds_out.drop_vars('time') | ||
|
||
# Create a time index | ||
time_var = np.arange(1, 12.1, 1) | ||
ds_out = ds_out.assign(Time=xr.DataArray(time_var, dims=['Time'])) | ||
|
||
# Create a xtime array | ||
xtime_list = [] | ||
for i in range(1, 13): | ||
xtime_string = f"0000-{i:02d}-15_00:00:00" | ||
xtime_list.append(xtime_string) | ||
xtime_out = np.array(xtime_list, dtype='S64') | ||
|
||
ds_out = ds_out.assign(xtime=xr.DataArray(xtime_out, dims=['Time'])) | ||
|
||
# Change attributes to be consistent with PHC restoring file | ||
ds_out.Time.attrs['long_name'] = "Month Index" | ||
ds_out.Time.attrs['units'] = "month" | ||
ds_out.Time.attrs['axis'] = "T" | ||
|
||
# Save file and change time dimension to unlimited | ||
write_netcdf(ds_out, 'woa_surface_salinity_monthly.nc') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters