/g/data/xp65/public/apps/med_conda/envs/analysis3-25.08/lib/python3.11/site-packages/distributed/node.py:187: UserWarning: Port 8787 is already in use.
Perhaps you already have a cluster running?
Hosting the HTTP server on port 38267 instead
warnings.warn(
Local directory: /jobfs/147046130.gadi-pbs/dask-scratch-space/worker-x20z9xub
Open the Intake Catalog
[3]:
catalog=intake.cat.access_nri
[4]:
depth=3000# metres
Pick shelf coordinates
[5]:
# OM2 longitude coordinates run from [-280, 80] to [-180, 180] - so we need to add 100.shelf_coord=(-62,-60+100)deep_coord=(-56.5,-59+100)
Load velocity and bathymetry data
[6]:
# Select data in the southern part of the Southern Oceanlat_slice=slice(-80,-59)# We use a time-mean from some output from the RYF runexpt='01deg_jra55v13_ryf9091'# Select the datastore for the experimentesm_datastore=catalog[expt]# We want to limit our date range to the 1950s.# (If you were a cookbook user, this would have been `start_time = '1950-01-31 00:00:00'` and `end_time = '1959-12-31 00:00:00'`)# With the catalog, we use the following regular expression to filter on date - it says "find me anything where the date starts with '1950/1951.../1959'":# See also https://docs.python.org/3/library/re.htmldate_str='^195[0-9].*'# Import bathymetry as a data arrayhu=esm_datastore.search(variable='hu',start_date=date_str).to_dask()['hu']hu=hu.sel(yu_ocean=lat_slice)# Select our lat slice
/g/data/xp65/public/apps/med_conda/envs/analysis3-25.08/lib/python3.11/site-packages/intake_esm/core.py:301: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning.
records = grouped.get_group(internal_key).to_dict(orient='records')
/g/data/xp65/public/apps/med_conda/envs/analysis3-25.08/lib/python3.11/site-packages/intake_esm/source.py:306: ConcatenationWarning: Attempting to concatenate datasets without valid dimension coordinates: retaining only first dataset. Request valid dimension coordinate to silence this warning.
warnings.warn(
[7]:
# Load density and potential temperature.rho_temp_datastore=esm_datastore.search(variable=["pot_rho_2","pot_temp"],# We can search for these together by passing a list to variable - it's faster that doing it separately.start_date=date_str,frequency="1mon",variable_cell_methods="time: mean")rho_temp_dataset=rho_temp_datastore.to_dask(xarray_open_kwargs={"chunks":{}})pot_rho_2=rho_temp_dataset["pot_rho_2"]pot_temp=rho_temp_dataset["pot_temp"]
/g/data/xp65/public/apps/med_conda/envs/analysis3-25.08/lib/python3.11/site-packages/intake_esm/core.py:301: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning.
records = grouped.get_group(internal_key).to_dict(orient='records')
[8]:
esm_datastore.search(variable=["xt_ocean","yt_ocean","xu_ocean","yu_ocean"],start_date=date_str,frequency="1mon",file_id="ocean.1mon.nv:2.xt_ocean:3600.xu_ocean:3600.yt_ocean:2700.yu_ocean:2700",# If we don't add this, we will get two datasets back.)
01deg_jra55v13_ryf9091 catalog with 1 dataset(s) from 40 asset(s):
unique
filename
1
path
40
file_id
1
frequency
1
start_date
40
end_date
40
variable
42
variable_long_name
42
variable_standard_name
23
variable_cell_methods
2
variable_units
17
realm
1
derived_variable
0
[9]:
# We're also gonna need xu_ocean, xt_ocean, yu_ocean, and yt_ocean - so we search for them all at once toogrid_datastore=esm_datastore.search(variable=["xt_ocean","yt_ocean","xu_ocean","yu_ocean"],start_date=date_str,frequency="1mon",file_id="ocean.1mon.nv:2.xt_ocean:3600.xu_ocean:3600.yt_ocean:2700.yu_ocean:2700",# If we don't add this, we will get two datasets back.)grid=grid_datastore.to_dask()
/g/data/xp65/public/apps/med_conda/envs/analysis3-25.08/lib/python3.11/site-packages/intake_esm/core.py:301: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning.
records = grouped.get_group(internal_key).to_dict(orient='records')
[10]:
# Give information on the grid: location of u (momentum) and t (tracer) points on B-gridds=xr.merge([pot_temp.sel(yt_ocean=lat_slice).sel(st_ocean=slice(0,depth)),grid.sel(yu_ocean=lat_slice).sel(yt_ocean=lat_slice)])ds.coords['xt_ocean'].attrs.update(axis='X')ds.coords['xu_ocean'].attrs.update(axis='X',c_grid_axis_shift=0.5)ds.coords['yt_ocean'].attrs.update(axis='Y')ds.coords['yu_ocean'].attrs.update(axis='Y',c_grid_axis_shift=0.5)grid_depth=xgcm.Grid(ds,periodic=['X'])grid_depth
[10]:
<xgcm.Grid>
Y Axis (not periodic, boundary=None):
* center yt_ocean --> outer
* outer yu_ocean --> center
X Axis (periodic, boundary=None):
* center xt_ocean --> right
* right xu_ocean --> center
Take time mean for density and temperature
[11]:
# Make coordinate range selections before taking mean - it'll be faster!pot_rho_2=pot_rho_2.sel(yt_ocean=lat_slice).sel(st_ocean=slice(0,depth))pot_rho_2=pot_rho_2.mean(dim='time')
Now we make the cross sections using the cross_section function of metpy
[13]:
#choosing number of steps in cross sectionstep_no=400# Create datasetds_pot_rho_2=xr.Dataset({"pot_rho_2":pot_rho_2,"lat":pot_rho_2.yt_ocean,"lon":pot_rho_2.xt_ocean})# Interpolate to xu_ocean and yu_ocean# Rename coordinate namesds_pot_rho_2=ds_pot_rho_2.rename({'xt_ocean':'x','yt_ocean':'y'})# Convert latitude from ACCESS-OM2 default range of [-280, 80] to [-180, 180] which is what metpy expects.ds_pot_rho_2['x']=ds_pot_rho_2['x']+100ds_pot_rho_2['x'].attrs=pot_rho_2['xt_ocean'].attrs# MetPy parsingpot_rho_2_parsed=ds_pot_rho_2.metpy.parse_cf('pot_rho_2',coordinates={'y':'y','x':'x'})pot_rho_2_section=cross_section(pot_rho_2_parsed,start=(shelf_coord[0],shelf_coord[1]),end=(deep_coord[0],deep_coord[1]),steps=step_no,interp_type='linear')
[14]:
# Create datasetds_pot_temp=xr.Dataset({"pot_temp":pot_temp,"lat":pot_temp.yt_ocean,"lon":pot_temp.xt_ocean})# Interpolate to xu_ocean and yu_ocean# Rename coordinate namesds_pot_temp=ds_pot_temp.rename({'xt_ocean':'x','yt_ocean':'y'})# Convert longitude from ACCESS-OM2 range of [-280, 80] to [-180, 180] which is what metpy expects.ds_pot_temp['x']=ds_pot_temp['x']+100ds_pot_temp['x'].attrs=pot_temp['xt_ocean'].attrs# MetPy parsingpot_temp_parsed=ds_pot_temp.metpy.parse_cf('pot_temp',coordinates={'y':'y','x':'x'})pot_temp_section=cross_section(pot_temp_parsed,start=(shelf_coord[0],shelf_coord[1]),end=(deep_coord[0],deep_coord[1]),steps=step_no,interp_type='linear')
Finally calculate the distance along the transect (for plotting purposes)
[15]:
# Define number of points you want to interpolatestep_no=400# Radius of the EarthRearth=6371# km# Difference between points in lat/lon spacedlon=deep_coord[1]-shelf_coord[1]dlat=deep_coord[0]-shelf_coord[0]# Calculate distance in km between the two end pointsdistance_endpoints=Rearth*np.deg2rad(np.sqrt(dlat**2+(dlon*np.cos(np.deg2rad(np.mean([shelf_coord[0],deep_coord[0]]))))**2))# Create array with length of step_nodistance_in_km=np.linspace(0,distance_endpoints,step_no)# Repeat by the number of depth levelsdistance_in_km=np.tile(distance_in_km,(len(pot_temp_section.st_ocean),1))
Plotting cross-slope section of potential temperature¶
[16]:
fig,axs=plt.subplots(figsize=(6,8),sharex=True)pot_temp_section_top=pot_temp_section.sel(st_ocean=slice(0,3000))axs.set_title('Cross-slope section of (%.1f,%.1f) to (%.1f,%.1f)'%(shelf_coord[0],shelf_coord[1]-100,deep_coord[0],deep_coord[1]-100))cmesh=axs.pcolormesh(distance_in_km,pot_temp_section_top.st_ocean.values,pot_temp_section_top.values,cmap='RdBu_r')# Colorbarcbar=plt.colorbar(cmesh,orientation='horizontal')cbar.set_label(r'$\theta$ ($^\circ$K)')cs=axs.contour(distance_in_km[0,:],pot_rho_2_section.st_ocean,pot_rho_2_section,cmap='gray')axs.clabel(cs,cs.levels,fontsize=8,colors='k',inline=True,use_clabeltext=True)# Axesplt.gca().invert_yaxis()plt.xlim([0,350])plt.xlabel('Distance northwards from shelf (km)')plt.ylabel('Depth (m)')axs.xaxis.set_minor_locator(MultipleLocator(10))axs.yaxis.set_minor_locator(MultipleLocator(50))axs.set_facecolor('darkgrey')# Since our colorbar goes through white, we'll set our mask to grey.plt.tight_layout();