Exercise 3, analyzing the Baltic Sea#

import matplotlib.pyplot as plt
import xarray as xr
ds = xr.open_dataset("data/ocean_day3d.nc")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/file_manager.py:199, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
    198 try:
--> 199     file = self._cache[self._key]
    200 except KeyError:

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/lru_cache.py:53, in LRUCache.__getitem__(self, key)
     52 with self._lock:
---> 53     value = self._cache[key]
     54     self._cache.move_to_end(key)

KeyError: [<class 'netCDF4._netCDF4.Dataset'>, ('/Users/boergel/Documents/work/climateoftheocean/data/ocean_day3d.nc',), 'r', (('clobber', True), ('diskless', False), ('format', 'NETCDF4'), ('persist', False))]

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
/Users/boergel/Documents/work/climateoftheocean/2022-01-20-climate-of-the-ocean-h3.ipynb Zelle 3 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/boergel/Documents/work/climateoftheocean/2022-01-20-climate-of-the-ocean-h3.ipynb#W2sZmlsZQ%3D%3D?line=0'>1</a> ds = xr.open_dataset("data/ocean_day3d.nc")

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/api.py:495, in open_dataset(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, backend_kwargs, *args, **kwargs)
    483 decoders = _resolve_decoders_kwargs(
    484     decode_cf,
    485     open_backend_dataset_parameters=backend.open_dataset_parameters,
   (...)
    491     decode_coords=decode_coords,
    492 )
    494 overwrite_encoded_chunks = kwargs.pop("overwrite_encoded_chunks", None)
--> 495 backend_ds = backend.open_dataset(
    496     filename_or_obj,
    497     drop_variables=drop_variables,
    498     **decoders,
    499     **kwargs,
    500 )
    501 ds = _dataset_from_backend_dataset(
    502     backend_ds,
    503     filename_or_obj,
   (...)
    510     **kwargs,
    511 )
    512 return ds

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/netCDF4_.py:550, in NetCDF4BackendEntrypoint.open_dataset(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, mode, format, clobber, diskless, persist, lock, autoclose)
    529 def open_dataset(
    530     self,
    531     filename_or_obj,
   (...)
    546     autoclose=False,
    547 ):
    549     filename_or_obj = _normalize_path(filename_or_obj)
--> 550     store = NetCDF4DataStore.open(
    551         filename_or_obj,
    552         mode=mode,
    553         format=format,
    554         group=group,
    555         clobber=clobber,
    556         diskless=diskless,
    557         persist=persist,
    558         lock=lock,
    559         autoclose=autoclose,
    560     )
    562     store_entrypoint = StoreBackendEntrypoint()
    563     with close_on_error(store):

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/netCDF4_.py:379, in NetCDF4DataStore.open(cls, filename, mode, format, group, clobber, diskless, persist, lock, lock_maker, autoclose)
    373 kwargs = dict(
    374     clobber=clobber, diskless=diskless, persist=persist, format=format
    375 )
    376 manager = CachingFileManager(
    377     netCDF4.Dataset, filename, mode=mode, kwargs=kwargs
    378 )
--> 379 return cls(manager, group=group, mode=mode, lock=lock, autoclose=autoclose)

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/netCDF4_.py:327, in NetCDF4DataStore.__init__(self, manager, group, mode, lock, autoclose)
    325 self._group = group
    326 self._mode = mode
--> 327 self.format = self.ds.data_model
    328 self._filename = self.ds.filepath()
    329 self.is_remote = is_remote_uri(self._filename)

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/netCDF4_.py:388, in NetCDF4DataStore.ds(self)
    386 @property
    387 def ds(self):
--> 388     return self._acquire()

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/netCDF4_.py:382, in NetCDF4DataStore._acquire(self, needs_lock)
    381 def _acquire(self, needs_lock=True):
--> 382     with self._manager.acquire_context(needs_lock) as root:
    383         ds = _nc4_require_group(root, self._group, self._mode)
    384     return ds

File ~/miniconda3/envs/xarray/lib/python3.10/contextlib.py:135, in _GeneratorContextManager.__enter__(self)
    133 del self.args, self.kwds, self.func
    134 try:
--> 135     return next(self.gen)
    136 except StopIteration:
    137     raise RuntimeError("generator didn't yield") from None

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/file_manager.py:187, in CachingFileManager.acquire_context(self, needs_lock)
    184 @contextlib.contextmanager
    185 def acquire_context(self, needs_lock=True):
    186     """Context manager for acquiring a file."""
--> 187     file, cached = self._acquire_with_cache_info(needs_lock)
    188     try:
    189         yield file

File ~/miniconda3/envs/xarray/lib/python3.10/site-packages/xarray/backends/file_manager.py:205, in CachingFileManager._acquire_with_cache_info(self, needs_lock)
    203     kwargs = kwargs.copy()
    204     kwargs["mode"] = self._mode
--> 205 file = self._opener(*self._args, **kwargs)
    206 if self._mode == "w":
    207     # ensure file doesn't get overriden when opened again
    208     self._mode = "a"

File src/netCDF4/_netCDF4.pyx:2353, in netCDF4._netCDF4.Dataset.__init__()

File src/netCDF4/_netCDF4.pyx:1963, in netCDF4._netCDF4._ensure_nc_success()

FileNotFoundError: [Errno 2] No such file or directory: b'/Users/boergel/Documents/work/climateoftheocean/data/ocean_day3d.nc'

When you start your work at IOW you will start by reading literature about the dynamics of the Baltic Sea. Soon you will notice that nearly every article starts with a paragraph similar to:

“The hydrography of the Baltic Sea depends on the water exchange with the world ocean which is restricted by the narrows and sills of the Danish Straits and on river runoff into the Baltic [Meier and Kauker, 2003].”

Look at this figure (Meier and Kauker, 2003) and zoom on the connection between the world ocean and the Baltic Sea.

The coordinates are : lon=8-17, lat=53-59

Check you data first. In MOM we use different names for lon, lat and depth:

lon = xt_ocean lat = yt_ocean depth = st_ocean

xarray allows you to select areas using


ds.sel(xt_ocean=slice(lon1, lon2), yt_ocean=slice(lat1, lat2))
ds.dims
Frozen({'xt_ocean': 91, 'yt_ocean': 102, 'time': 11, 'nv': 2, 'xu_ocean': 91, 'yu_ocean': 102, 'st_ocean': 100, 'st_edges_ocean': 101, 'sw_ocean': 100, 'sw_edges_ocean': 101})
danish_straits = ds.sel(xt_ocean=slice(,), yt_ocean=slice(,))
# Instead of selecting coordinate, we can also use 
# index selction using .isel
# we are selecting the surface and the first timestep of 
# the variable salt and plot it

danish_straits.salt.isel(st_ocean=0, time = 0).plot()
<matplotlib.collections.QuadMesh at 0x7fafbf7ec110>
_images/da321ddee684898ca0bfffef3d4bbf94b0d83f55dc7474ec368f25043e6adb1b.png

Question 1: What is the first thing you notice, when you compare to the realistic bathymetry above?

Answer:

Question 2: Look at the colobar. What role play the Danish Straits for the salinity of the Baltic Sea?

Answer:

Following on common Baltic Sea introductions, you will find something similar to:

The inflow of freshwater by river runoff and a positive net precipitation cause a positive water balance with respect to the North Sea. The positive water balance leads to strong gradients in salinity and ecosystem variables (Reckermann et al., 2008).

So let’s look at the mean surface salinity!

level = [0,2,4,6,8,10,15,20,30,35]

f, ax = plt.subplots(1)

ds.salt.isel(st_ocean=0).mean("time").plot(levels=level, cmap=plt.cm.jet)
<matplotlib.collections.QuadMesh at 0x7faf43e89090>
_images/17a4ec713c93925934e0de19df75a18c1d83a1fd74b0b9ba2ae5838f63e5be20.png

Following Markus second sentence of his paper,

In the long-term mean, high-saline water from the Kattegat enters the Baltic proper and low-saline water leaves the Baltic because of the freshwater surplus.

Think about the exchange flow of the Baltic Sea. How would the profile of a transect a 16°E look like?

# We first select the latitude range 53-57, then longitude at 16°E
# note that by using `method="nearest"` we will search for the nearest lon coordinate to 13

transect = ds.sel(yt_ocean=slice(53, 57)).sel(xt_ocean=16, method="nearest")

This leaves us with the dimensions: time, depth and latitude

transect.salt.dims
('time', 'st_ocean', 'yt_ocean')

Markus talks about the long-term mean in his paper. So start by averaging over the time dimension.

using

.mean("time")
transect_mean_time = transect.mean("time")

Now we can average over the latitude, to give us a depth profile.

transect_mean_time_latitude = transect_mean_time.mean("yt_ocean")
f, ax = plt.subplots(1)
transect_mean_time_latitude.salt.plot(ax=ax, y="st_ocean")
ax.set_ylabel("Depth [m]")
ax.set_xlabel("Salinity [g/kg]")
ax.invert_yaxis()
_images/860f5295945f3995399c066fa78d0477c2e3b19f6fa96595e74f308a3685292e.png

Following along with Markus Paper:

The bottom water in the deep subbasins is ventilated mainly by large perturbations, so-called major Baltic saltwater inflows [Matthäus and Franck, 1992; Fischer and Matthäus, 1996].

In this year we have no strong inflow. However, we can notice inflows of high saline water analyzing the station located in the Arkona Basin.

by2 = ds.sel(xt_ocean = 16.2, yt_ocean = 55.5, method="nearest")
g = (by2.salt - by2.salt.mean("time")).plot(col="time", col_wrap = 3, y="st_ocean")

for ax in g.axes[0]:
    ax.invert_yaxis()
    ax.set_xlabel("Salinity[g/kg]")
    ax.set_ylabel("Depth [m]")

g.fig.tight_layout()
_images/48e481e635fb90597e3a9dea43f0e6d383d2504804aca247c4f2aa72552d2832.png

Question 3: Focus on the month of February. What is happening?

These saline inflows are important for the oxygen supply of the deeper layers, since due to the strong stratification in the Baltic Sea only layers above the permanent halocline are directly influenced by the atmosphere and therefore supplied with oxygen (Mohrholz et al., 2015).

Seasonal cycle of the temperature#

We will now look at the depth-averaged seasonal cycle of the Baltic Sea.

ds_temp_season = ds.temp.resample(time="1M").mean("time").mean("st_ocean")
(ds_temp_season-ds_temp_season.mean("time")).plot(col="time", col_wrap =3)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/Users/boergel/Documents/work/climateoftheocean/2022-01-20-climate-of-the-ocean-h3.ipynb Zelle 33 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/boergel/Documents/work/climateoftheocean/2022-01-20-climate-of-the-ocean-h3.ipynb#X44sZmlsZQ%3D%3D?line=0'>1</a> (ds_temp_season-ds_temp_season.mean("time")).plot(col="time", col_wrap =3)

NameError: name 'ds_temp_season' is not defined

Question 4: Above you see the deviation from the mean temperature of the Baltic Sea for every single month. Try to discuss the differences for every month.

Sea Ice#

During winter time parts of the Baltic Sea are covered with sea ice. The sea ice influences the air-sea interaction. Sea ice directly influences temperature, salinity, but also the transfer of momentum into the ocean. The annual ice cover varies from only being present in the Bothnian Bay to a nearly fully covered Baltic Sea. Therefore, the Baltic Sea is exposed to great variation in sea ice cover.

ice = xr.open_dataset("data/ice_day.nc")

Let’s start by analyzing the seasonal sea ice cover.

ice
<xarray.Dataset>
Dimensions:      (xt: 91, xb: 92, yt: 102, yb: 103, time: 11, nv: 2, ct: 5, xv: 91, yv: 102)
Coordinates:
  * xt           (xt) float64 8.12 8.36 8.6 8.84 9.08 ... 29.0 29.24 29.48 29.72
  * xb           (xb) float64 8.0 8.24 8.48 8.72 8.96 ... 29.12 29.36 29.6 29.84
  * yt           (yt) float64 53.64 53.76 53.88 54.0 ... 65.4 65.52 65.64 65.76
  * yb           (yb) float64 53.58 53.7 53.82 53.94 ... 65.46 65.58 65.7 65.82
  * time         (time) object 1961-02-15 00:00:00 ... 1961-12-16 12:00:00
  * nv           (nv) float64 1.0 2.0
  * ct           (ct) float64 0.0 0.1 0.3 0.7 1.1
  * xv           (xv) float64 8.24 8.48 8.72 8.96 9.2 ... 29.12 29.36 29.6 29.84
  * yv           (yv) float64 53.7 53.82 53.94 54.06 ... 65.46 65.58 65.7 65.82
Data variables: (12/26)
    FRAZIL       (time, yt, xt) float32 ...
    CN           (time, ct, yt, xt) float32 ...
    MI           (time, yt, xt) float32 ...
    HI           (time, yt, xt) float32 ...
    HS           (time, yt, xt) float32 ...
    TS           (time, yt, xt) float32 ...
    ...           ...
    EVAP         (time, yt, xt) float32 ...
    RUNOFF       (time, yt, xt) float32 ...
    average_T1   (time) datetime64[ns] 1961-02-01 1961-03-01 ... 1961-12-01
    average_T2   (time) datetime64[ns] 1961-03-01 1961-04-01 ... 1962-01-01
    average_DT   (time) timedelta64[ns] 28 days 31 days ... 30 days 31 days
    time_bounds  (time, nv) timedelta64[ns] 0 days 28 days ... 303 days 334 days
Attributes:
    filename:   ice_day.nc
    title:      ERGOM-MOM510 8 n.m. Baltic Hiresaff 1850-2009
    grid_type:  regular
    grid_tile:  N/A
ice.MI.resample(time="1M").mean().plot(col="time", col_wrap=4)
<xarray.plot.facetgrid.FacetGrid at 0x7faf3e711f10>
_images/f4c9a0c4b35864a2e31665d911f78c7081717da287e3250082caacf80ac38fc5.png

Question 5: Why do we find sea ice in the North but not in the central Baltic Sea?