Skip to content

Commit 4195fc9

Browse files
Implementing correct depth-to-sigma calculation in scipy mode
1 parent d233947 commit 4195fc9

File tree

5 files changed

+93
-48
lines changed

5 files changed

+93
-48
lines changed

docs/examples/tutorial_croco_3D.ipynb

Lines changed: 28 additions & 22 deletions
Large diffs are not rendered by default.

parcels/field.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ def _deal_with_errors(error, key, vector_type: VectorType):
7676
return 0
7777

7878

79+
def _croco_from_z_to_sigma_scipy(fieldset, time, z, y, x, particle):
80+
"""Calculate local sigma level of the particle, by linearly interpolating the
81+
scaling function that maps sigma to depth (using local ocean depth H,
82+
sea-surface Zeta and stretching parameters Cs_w and hc).
83+
See also https://croco-ocean.gitlabpages.inria.fr/croco_doc/model/model.grid.html#vertical-grid-parameters
84+
"""
85+
h = fieldset.H.eval(time, 0, y, x, particle=particle, applyConversion=False)
86+
zeta = fieldset.Zeta.eval(time, 0, y, x, particle=particle, applyConversion=False)
87+
sigma_levels = fieldset.U.grid.depth
88+
z0 = fieldset.hc * sigma_levels + (h - fieldset.hc) * fieldset.Cs_w.data[0, :, 0, 0]
89+
zvec = z0 + zeta * (1 + (z0 / h))
90+
zinds = zvec <= z
91+
if z >= zvec[-1]:
92+
zi = len(zvec) - 2
93+
else:
94+
zi = zinds.argmin() - 1 if z >= zvec[0] else 0
95+
96+
return sigma_levels[zi] + (z - zvec[zi]) * (sigma_levels[zi + 1] - sigma_levels[zi]) / (zvec[zi + 1] - zvec[zi])
97+
98+
7999
class Field:
80100
"""Class that encapsulates access to field data.
81101
@@ -617,18 +637,23 @@ def from_netcdf(
617637

618638
_grid_fb_class = NetcdfFileBuffer
619639

620-
with _grid_fb_class(
621-
lonlat_filename,
622-
dimensions,
623-
indices,
624-
netcdf_engine,
625-
gridindexingtype=gridindexingtype,
626-
) as filebuffer:
627-
lon, lat = filebuffer.lonlat
628-
indices = filebuffer.indices
629-
# Check if parcels_mesh has been explicitly set in file
630-
if "parcels_mesh" in filebuffer.dataset.attrs:
631-
mesh = filebuffer.dataset.attrs["parcels_mesh"]
640+
if "lon" in dimensions and "lat" in dimensions:
641+
with _grid_fb_class(
642+
lonlat_filename,
643+
dimensions,
644+
indices,
645+
netcdf_engine,
646+
gridindexingtype=gridindexingtype,
647+
) as filebuffer:
648+
lon, lat = filebuffer.lonlat
649+
indices = filebuffer.indices
650+
# Check if parcels_mesh has been explicitly set in file
651+
if "parcels_mesh" in filebuffer.dataset.attrs:
652+
mesh = filebuffer.dataset.attrs["parcels_mesh"]
653+
else:
654+
lon = 0
655+
lat = 0
656+
mesh = "flat"
632657

633658
if "depth" in dimensions:
634659
with _grid_fb_class(
@@ -1537,8 +1562,8 @@ def eval(self, time, z, y, x, particle=None, applyConversion=True):
15371562
"""
15381563
(ti, periods) = self._time_index(time)
15391564
time -= periods * (self.grid.time_full[-1] - self.grid.time_full[0])
1540-
if self.gridindexingtype == "croco" and self is not self.fieldset.H:
1541-
z = z / self.fieldset.H.eval(time, 0, y, x, particle=particle, applyConversion=False)
1565+
if self.gridindexingtype == "croco" and self not in [self.fieldset.H, self.fieldset.Zeta]:
1566+
z = _croco_from_z_to_sigma_scipy(self.fieldset, time, z, y, x, particle=particle)
15421567
if ti < self.grid.tdim - 1 and time > self.grid.time[ti]:
15431568
f0 = self._spatial_interpolation(ti, z, y, x, time, particle=particle)
15441569
f1 = self._spatial_interpolation(ti + 1, z, y, x, time, particle=particle)
@@ -2252,7 +2277,7 @@ def spatial_c_grid_interpolation3D(self, ti, z, y, x, time, particle=None, apply
22522277
(u, v, w) = self.spatial_c_grid_interpolation3D_full(ti, z, y, x, time, particle=particle)
22532278
else:
22542279
if self.gridindexingtype == "croco":
2255-
z = z / self.fieldset.H.eval(time, 0, y, x, particle=particle, applyConversion=False)
2280+
z = _croco_from_z_to_sigma_scipy(self.fieldset, time, z, y, x, particle=particle)
22562281
(u, v) = self.spatial_c_grid_interpolation2D(ti, z, y, x, time, particle=particle)
22572282
w = self.W.eval(time, z, y, x, particle=particle, applyConversion=False)
22582283
if applyConversion:

parcels/fieldfilebuffer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ def _check_extend_depth(self, data, di):
188188
)
189189

190190
def _apply_indices(self, data, ti):
191-
if len(data.shape) == 2:
191+
if len(data.shape) == 1:
192+
if self.indices["depth"] is not None:
193+
data = data[self.indices["depth"]]
194+
elif len(data.shape) == 2:
192195
if self.nolonlatindices:
193196
pass
194197
else:

parcels/fieldset.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ def from_croco(
713713
filenames,
714714
variables,
715715
dimensions,
716+
hc: float,
716717
indices=None,
717718
mesh="spherical",
718719
allow_time_extrapolation=None,
@@ -723,11 +724,12 @@ def from_croco(
723724
):
724725
"""Initialises FieldSet object from NetCDF files of CROCO fields.
725726
All parameters and keywords are exactly the same as for FieldSet.from_nemo(), except that
726-
the vertical coordinate is scaled by the bathymetry (``h``) field from CROCO, in order to
727-
account for the sigma-grid. The horizontal interpolation uses the MITgcm grid indexing
728-
as described in FieldSet.from_mitgcm().
727+
the vertical coordinate is scaled by the bathymetry (``h``) and sea-surface height (``zeta``)
728+
fields from CROCO, in order to account for the sigma-grid.
729+
The horizontal interpolation uses the MITgcm grid indexing as described in FieldSet.from_mitgcm().
729730
730-
The sigma grid scaling means that FieldSet.from_croco() requires a variable ``H: h`` to work.
731+
The sigma grid scaling means that FieldSet.from_croco() requires variables
732+
``H: h`` and ``Zeta: zeta``, as well as he ``hc`` parameter to work.
731733
732734
See `the CROCO 3D tutorial <../examples/tutorial_croco_3D.ipynb>`__ for more infomation.
733735
"""
@@ -740,13 +742,16 @@ def from_croco(
740742

741743
dimsU = dimensions["U"] if "U" in dimensions else dimensions
742744
if "depth" in dimsU:
743-
warnings.warn(
744-
"Note that it is unclear which vertical velocity ('w' or 'omega') to use in 3D CROCO fields.\nSee https://docs.oceanparcels.org/en/latest/examples/tutorial_croco_3D.html for more information",
745-
FieldSetWarning,
746-
stacklevel=2,
747-
)
745+
if "W" in variables and variables["W"] == "omega":
746+
warnings.warn(
747+
"Note that Parcels expects 'w' for vertical velicites in 3D CROCO fields.\nSee https://docs.oceanparcels.org/en/latest/examples/tutorial_croco_3D.html for more information",
748+
FieldSetWarning,
749+
stacklevel=2,
750+
)
748751
if "H" not in variables:
749752
raise ValueError("FieldSet.from_croco() requires a field 'H' for the bathymetry")
753+
if "Zeta" not in variables:
754+
raise ValueError("FieldSet.from_croco() requires a field 'Zeta' for the free_surface")
750755

751756
interp_method = {}
752757
for v in variables:
@@ -776,6 +781,7 @@ def from_croco(
776781
gridindexingtype="croco",
777782
**kwargs,
778783
)
784+
fieldset.add_constant("hc", hc)
779785
return fieldset
780786

781787
@classmethod
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import os
22

3+
import xarray as xr
4+
35
import parcels
46

57

68
def create_fieldset(indices=None):
79
example_dataset_folder = parcels.download_example_dataset("CROCOidealized_data")
810
file = os.path.join(example_dataset_folder, "CROCO_idealized.nc")
911

10-
variables = {"U": "u", "V": "v", "W": "w", "H": "h"}
12+
variables = {"U": "u", "V": "v", "W": "w", "H": "h", "Zeta": "zeta", "Cs_w": "Cs_w"}
1113
dimensions = {
1214
"U": {"lon": "x_rho", "lat": "y_rho", "depth": "s_w", "time": "time"},
1315
"V": {"lon": "x_rho", "lat": "y_rho", "depth": "s_w", "time": "time"},
1416
"W": {"lon": "x_rho", "lat": "y_rho", "depth": "s_w", "time": "time"},
1517
"H": {"lon": "x_rho", "lat": "y_rho"},
18+
"Zeta": {"lon": "x_rho", "lat": "y_rho", "time": "time"},
19+
"Cs_w": {"depth": "s_w"},
1620
}
1721
fieldset = parcels.FieldSet.from_croco(
1822
file,
@@ -21,6 +25,7 @@ def create_fieldset(indices=None):
2125
allow_time_extrapolation=True,
2226
mesh="flat",
2327
indices=indices,
28+
hc=xr.open_dataset(file).hc.values,
2429
)
2530

2631
return fieldset

0 commit comments

Comments
 (0)