Skip to content

Commit a7eceee

Browse files
committed
"forgot github fix"
Merge branch 'education' of https://github.com/OceanParcels/virtualship into education
2 parents 19fc09c + 29bfc2c commit a7eceee

File tree

11 files changed

+336
-13
lines changed

11 files changed

+336
-13
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ""
5+
labels: bug
6+
assignees: ""
7+
---
8+
9+
**Describe the bug**
10+
A clear and concise description of what the bug is.
11+
12+
**To Reproduce**
13+
Steps to reproduce the behavior:
14+
15+
1. Go to '...'
16+
2. Click on '....'
17+
3. Scroll down to '....'
18+
4. See error
19+
20+
**Expected behavior**
21+
A clear and concise description of what you expected to happen.
22+
23+
**Screenshots**
24+
If applicable, add screenshots to help explain your problem.
25+
26+
**Please complete the following information:**
27+
28+
- OS: [e.g. iOS]
29+
- Browser [e.g. chrome, safari]
30+
- Version [e.g. 22]
31+
32+
**Additional context**
33+
Add any other context about the problem here.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ""
5+
labels: enhancement
6+
assignees: ""
7+
---
8+
9+
**Is your feature request related to a problem? Please describe.**
10+
A clear and concise description of what the problem is.
11+
12+
**Describe the solution you'd like**
13+
A clear and concise description of what you want to happen.
14+
15+
**Describe alternatives you've considered**
16+
A clear and concise description of any alternative solutions or features you've considered.
17+
18+
**Additional context**
19+
Add any other context or screenshots about the feature request here.

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
[![Anaconda-release](https://anaconda.org/conda-forge/virtualship/badges/version.svg)](https://anaconda.org/conda-forge/virtualship/)
1111
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/virtualship)
12+
[![DOI](https://zenodo.org/badge/682478059.svg)](https://doi.org/10.5281/zenodo.14013931)
1213
[![unit-tests](https://github.com/OceanParcels/virtualship/actions/workflows/ci.yml/badge.svg)](https://github.com/OceanParcels/virtualship/actions/workflows/ci.yml)
1314
[![codecov](https://codecov.io/gh/OceanParcels/virtualship/graph/badge.svg?token=SLGLN8QBLW)](https://codecov.io/gh/OceanParcels/virtualship)
1415

@@ -33,8 +34,9 @@
3334

3435
VirtualShipParcels is a command line simulator allowing students to plan and conduct a virtual research expedition, receiving measurements as if they were coming from actual oceanographic instruments including:
3536

36-
- ADCP (for currents)
37-
- CTD (for conductivity, and temperature)
37+
- ADCP (currents)
38+
- CTD (conductivity and temperature)
39+
- XBT (temperature)
3840
- underwater measurements (salinity and temperature)
3941
- surface drifters
4042
- argo float deployments

docs/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ The running of these commands is useful for local development and quick iteratio
5151
When adding a dependency, make sure to modify the following files where relevant:
5252

5353
- `environment.yml` for core and development dependencies (important for the development environment, and CI)
54-
- `pyproject.toml` for core dependencies (important for the pypi package, this should propogate through automatically to `recipe/meta.yml` in the conda-forge feedstock)
54+
- `pyproject.toml` for core dependencies (important for the pypi package, this should propagate through automatically to `recipe/meta.yml` in the conda-forge feedstock)

docs/tutorials/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,3 @@ caption: Tutorials
1616
ADCP_data_tutorial.ipynb
1717
CTD_data_tutorial.ipynb
1818
```
19-

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ channels:
33
- conda-forge
44
dependencies:
55
- click
6-
- parcels >= 3, < 4
6+
- parcels >3, <3.1.0
77
- pyproj >= 3, < 4
88
- sortedcontainers == 2.4.0
99
- opensimplex == 0.4.5

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
dependencies = [
2828
"click",
29-
"parcels >= 3, < 4",
29+
"parcels >3, <3.1.0",
3030
"pyproj >= 3, < 4",
3131
"sortedcontainers == 2.4.0",
3232
"opensimplex == 0.4.5",

src/virtualship/expedition/input_data.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ def _load_default_fieldset(cls, directory: str | Path) -> FieldSet:
9797

9898
# make depth negative
9999
for g in fieldset.gridset.grids:
100-
g._depth = (
101-
-g._depth
102-
) # TODO maybe add a grid.negate_depth() method in Parcels?
100+
g.depth = -g.depth
103101

104102
# add bathymetry data
105103
bathymetry_file = directory.joinpath("bathymetry.nc")
@@ -139,7 +137,7 @@ def _load_drifter_fieldset(cls, directory: str | Path) -> FieldSet:
139137

140138
# make depth negative
141139
for g in fieldset.gridset.grids:
142-
g._depth = -g._depth
140+
g.depth = -g.depth
143141

144142
# read in data already
145143
fieldset.computeTimeChunk(0, 1)
@@ -171,7 +169,7 @@ def _load_argo_float_fieldset(cls, directory: str | Path) -> FieldSet:
171169
# make depth negative
172170
for g in fieldset.gridset.grids:
173171
if max(g.depth) > 0:
174-
g._depth = -g._depth
172+
g.depth = -g.depth
175173

176174
# read in data already
177175
fieldset.computeTimeChunk(0, 1)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Measurement instrument that can be used with Parcels."""
22

3-
from . import adcp, argo_float, ctd, drifter, ship_underwater_st
3+
from . import adcp, argo_float, ctd, drifter, ship_underwater_st, xbt
44

5-
__all__ = ["adcp", "argo_float", "ctd", "drifter", "ship_underwater_st"]
5+
__all__ = ["adcp", "argo_float", "ctd", "drifter", "ship_underwater_st", "xbt"]

src/virtualship/instruments/xbt.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""XBT instrument."""
2+
3+
from dataclasses import dataclass
4+
from datetime import timedelta
5+
from pathlib import Path
6+
7+
import numpy as np
8+
from parcels import FieldSet, JITParticle, ParticleSet, Variable
9+
10+
from ..spacetime import Spacetime
11+
12+
13+
@dataclass
14+
class XBT:
15+
"""Configuration for a single XBT."""
16+
17+
spacetime: Spacetime
18+
min_depth: float
19+
max_depth: float
20+
fall_speed: float
21+
deceleration_coefficient: float
22+
23+
24+
_XBTParticle = JITParticle.add_variables(
25+
[
26+
Variable("temperature", dtype=np.float32, initial=np.nan),
27+
Variable("max_depth", dtype=np.float32),
28+
Variable("min_depth", dtype=np.float32),
29+
Variable("fall_speed", dtype=np.float32),
30+
Variable("deceleration_coefficient", dtype=np.float32),
31+
]
32+
)
33+
34+
35+
def _sample_temperature(particle, fieldset, time):
36+
particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon]
37+
38+
39+
def _xbt_cast(particle, fieldset, time):
40+
particle_ddepth = -particle.fall_speed * particle.dt
41+
42+
# update the fall speed from the quadractic fall-rate equation
43+
# check https://doi.org/10.5194/os-7-231-2011
44+
particle.fall_speed = (
45+
particle.fall_speed - 2 * particle.deceleration_coefficient * particle.dt
46+
)
47+
48+
# delete particle if depth is exactly max_depth
49+
if particle.depth == particle.max_depth:
50+
particle.delete()
51+
52+
# set particle depth to max depth if it's too deep
53+
if particle.depth + particle_ddepth < particle.max_depth:
54+
particle_ddepth = particle.max_depth - particle.depth
55+
56+
57+
def simulate_xbt(
58+
fieldset: FieldSet,
59+
out_path: str | Path,
60+
xbts: list[XBT],
61+
outputdt: timedelta,
62+
) -> None:
63+
"""
64+
Use Parcels to simulate a set of XBTs in a fieldset.
65+
66+
:param fieldset: The fieldset to simulate the XBTs in.
67+
:param out_path: The path to write the results to.
68+
:param xbts: A list of XBTs to simulate.
69+
:param outputdt: Interval which dictates the update frequency of file output during simulation
70+
:raises ValueError: Whenever provided XBTs, fieldset, are not compatible with this function.
71+
"""
72+
DT = 10.0 # dt of XBT simulation integrator
73+
74+
if len(xbts) == 0:
75+
print(
76+
"No XBTs provided. Parcels currently crashes when providing an empty particle set, so no XBT simulation will be done and no files will be created."
77+
)
78+
# TODO when Parcels supports it this check can be removed.
79+
return
80+
81+
fieldset_starttime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[0])
82+
fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1])
83+
84+
# deploy time for all xbts should be later than fieldset start time
85+
if not all(
86+
[np.datetime64(xbt.spacetime.time) >= fieldset_starttime for xbt in xbts]
87+
):
88+
raise ValueError("XBT deployed before fieldset starts.")
89+
90+
# depth the xbt will go to. shallowest between xbt max depth and bathymetry.
91+
max_depths = [
92+
max(
93+
xbt.max_depth,
94+
fieldset.bathymetry.eval(
95+
z=0, y=xbt.spacetime.location.lat, x=xbt.spacetime.location.lon, time=0
96+
),
97+
)
98+
for xbt in xbts
99+
]
100+
101+
# initial fall speeds
102+
initial_fall_speeds = [xbt.fall_speed for xbt in xbts]
103+
104+
# XBT depth can not be too shallow, because kernel would break.
105+
# This shallow is not useful anyway, no need to support.
106+
for max_depth, fall_speed in zip(max_depths, initial_fall_speeds, strict=False):
107+
if not max_depth <= -DT * fall_speed:
108+
raise ValueError(
109+
f"XBT max_depth or bathymetry shallower than maximum {-DT * fall_speed}"
110+
)
111+
112+
# define xbt particles
113+
xbt_particleset = ParticleSet(
114+
fieldset=fieldset,
115+
pclass=_XBTParticle,
116+
lon=[xbt.spacetime.location.lon for xbt in xbts],
117+
lat=[xbt.spacetime.location.lat for xbt in xbts],
118+
depth=[xbt.min_depth for xbt in xbts],
119+
time=[xbt.spacetime.time for xbt in xbts],
120+
max_depth=max_depths,
121+
min_depth=[xbt.min_depth for xbt in xbts],
122+
fall_speed=[xbt.fall_speed for xbt in xbts],
123+
)
124+
125+
# define output file for the simulation
126+
out_file = xbt_particleset.ParticleFile(name=out_path, outputdt=outputdt)
127+
128+
# execute simulation
129+
xbt_particleset.execute(
130+
[_sample_temperature, _xbt_cast],
131+
endtime=fieldset_endtime,
132+
dt=DT,
133+
verbose_progress=False,
134+
output_file=out_file,
135+
)
136+
137+
# there should be no particles left, as they delete themselves when they finish profiling
138+
if len(xbt_particleset.particledata) != 0:
139+
raise ValueError(
140+
"Simulation ended before XBT finished profiling. This most likely means the field time dimension did not match the simulation time span."
141+
)

0 commit comments

Comments
 (0)