Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/codetools.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: ci

on: [push, pull_request]

env:
PACKAGE: virtual_ship

jobs:
codetools:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5.1.0
with:
python-version: ${{ matrix.python-version }}
- name: install
run: pip install ".[dev]"
- name: flake8
run: flake8 ./$PACKAGE
- name: pydocstyle
run: pydocstyle ./$PACKAGE
- name: sort-all
run: |
find ./$PACKAGE -type f -name '__init__.py' -print0 | xargs -0 sort-all
[[ -z $(git status -s) ]]
git checkout -- .
- name: black
run: black --diff --check ./$PACKAGE
- name: isort
run: isort --check-only --diff ./$PACKAGE
38 changes: 38 additions & 0 deletions codetools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh

# Runs all codetools and attempts to apply fixes wherever possible.
# Not suitable for the CI as that should not make any changes.

set -e

# Set working directory to the directory of this script.
cd "$(dirname "$0")"

PACKAGE=virtual_ship

echo "--------------"
echo "flake8"
echo "--------------"
flake8 ./$PACKAGE
# darglint is ran as a plugin for flake8.

echo "--------------"
echo "pydocstyle"
echo "--------------"
pydocstyle ./$PACKAGE

echo "--------------"
echo "sort-all"
echo "--------------"
find ./$PACKAGE -type f -name '__init__.py' -print0 | xargs -0 sort-all

echo "--------------"
echo "black"
echo "--------------"
black ./$PACKAGE

echo "--------------"
echo "isort"
echo "--------------"
isort ./$PACKAGE

17 changes: 15 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ dependencies = [
"pyproj >= 3, < 4",
"parcels >= 3, < 4",
"scipy >= 1, < 2",
"xarray >= 2023, < 2024"
"xarray >= 2023, < 2024",
]

[project.urls]
Expand All @@ -44,8 +44,21 @@ packages = ["virtual_ship"]
write_to = "virtual_ship/_version_setup.py"
local_scheme = "no-local-version"

[project.optional-dependencies]
dev = [
"black == 24.4.0",
"darglint == 1.8.1",
"flake8 == 7.0.0",
"Flake8-pyproject == 1.2.3",
"isort == 5.13.2",
"pydocstyle == 6.3.0",
"sort-all == 1.2.0",
]

[tool.isort]
profile = "black"
skip_gitignore = true

# Other tools will be added soon.
[tool.flake8]
extend-ignore = "E501" # Don't check line length.
docstring_style = "sphinx" # Use sphinx docstring style for darglint plugin.
1 change: 1 addition & 0 deletions virtual_ship/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Code for the Virtual Ship Classroom, where Marine Scientists can combine Copernicus Marine Data with an OceanParcels ship to go on a virtual expedition."""
119 changes: 93 additions & 26 deletions virtual_ship/argo_deployments.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import os
import math
import numpy as np
"""argo_deployments function."""

import datetime
import math
import os
from datetime import timedelta
from parcels import FieldSet, JITParticle, Variable, ParticleSet, AdvectionRK4, StatusCode

import numpy as np
from parcels import (
AdvectionRK4,
FieldSet,
JITParticle,
ParticleSet,
StatusCode,
Variable,
)


def argo_deployments(config, argo_time):
'''Deploys argo floats, returns results folder'''
"""
Deploy argo floats.

:param config: The cruise configuration.
:param argo_time: TODO
"""
# particle_* such as particle_ddepth are local variables defined by parcels.
# See https://docs.oceanparcels.org/en/latest/examples/tutorial_kernelloop.html#Background

if len(config.argo_deploylocations) > 0:

Expand All @@ -17,7 +35,9 @@ def ArgoVerticalMovement(particle, fieldset, time):

if particle.cycle_phase == 0:
# Phase 0: Sinking with vertical_speed until depth is driftdepth
particle_ddepth += fieldset.vertical_speed * particle.dt
particle_ddepth += ( # noqa See comment above about particle_* variables.
fieldset.vertical_speed * particle.dt
)
if particle.depth + particle_ddepth <= fieldset.driftdepth:
particle_ddepth = fieldset.driftdepth - particle.depth
particle.cycle_phase = 1
Expand All @@ -39,15 +59,23 @@ def ArgoVerticalMovement(particle, fieldset, time):
elif particle.cycle_phase == 3:
# Phase 3: Rising with vertical_speed until at surface
particle_ddepth -= fieldset.vertical_speed * particle.dt
particle.cycle_age += particle.dt # solve issue of not updating cycle_age during ascent
particle.cycle_age += (
particle.dt
) # solve issue of not updating cycle_age during ascent
if particle.depth + particle_ddepth >= fieldset.mindepth:
particle_ddepth = fieldset.mindepth - particle.depth
particle.temperature = math.nan # reset temperature to NaN at end of sampling cycle
particle.temperature = (
math.nan
) # reset temperature to NaN at end of sampling cycle
particle.salinity = math.nan # idem
particle.cycle_phase = 4
else:
particle.temperature = fieldset.T[time, particle.depth, particle.lat, particle.lon]
particle.salinity = fieldset.S[time, particle.depth, particle.lat, particle.lon]
particle.temperature = fieldset.T[
time, particle.depth, particle.lat, particle.lon
]
particle.salinity = fieldset.S[
time, particle.depth, particle.lat, particle.lon
]

elif particle.cycle_phase == 4:
# Phase 4: Transmitting at surface until cycletime is reached
Expand Down Expand Up @@ -84,39 +112,78 @@ class ArgoParticle(JITParticle):
time = argo_time

# Create and execute argo particles
argoset = ParticleSet(fieldset=fieldset, pclass=ArgoParticle, lon=lon, lat=lat, depth=np.repeat(fieldset.mindepth,len(time)), time=time)
argo_output_file = argoset.ParticleFile(name=os.path.join("results","Argos.zarr"), outputdt=timedelta(minutes=5), chunks=(1,500))
argoset = ParticleSet(
fieldset=fieldset,
pclass=ArgoParticle,
lon=lon,
lat=lat,
depth=np.repeat(fieldset.mindepth, len(time)),
time=time,
)
argo_output_file = argoset.ParticleFile(
name=os.path.join("results", "Argos.zarr"),
outputdt=timedelta(minutes=5),
chunks=(1, 500),
)
fieldset_endtime = fieldset.time_origin.fulltime(fieldset.U.grid.time_full[-1])
argo_endtime = np.array((datetime.datetime.strptime(config.requested_ship_time["start"],"%Y-%m-%dT%H:%M:%S") + timedelta(weeks=6))).astype('datetime64[ms]')
argo_endtime = np.array(
(
datetime.datetime.strptime(
config.requested_ship_time["start"], "%Y-%m-%dT%H:%M:%S"
)
+ timedelta(weeks=6)
)
).astype("datetime64[ms]")

argoset.execute(
[ArgoVerticalMovement, AdvectionRK4, KeepAtSurface, CheckError], # list of kernels to be executed
endtime=min(fieldset_endtime, argo_endtime), dt=timedelta(minutes=5),
output_file=argo_output_file
[
ArgoVerticalMovement,
AdvectionRK4,
KeepAtSurface,
CheckError,
], # list of kernels to be executed
endtime=min(fieldset_endtime, argo_endtime),
dt=timedelta(minutes=5),
output_file=argo_output_file,
)


def create_argo_fieldset(config):
'''Creates fieldset from netcdf files for argo floats, returns fieldset with negative depth values'''
"""
Create a fieldset from netcdf files for argo floats, return fieldset with negative depth values.

:param config: The cruise configuration.
:returns: The fieldset.
"""
datadirname = os.path.dirname(__file__)
filenames = {
"U": os.path.join(datadirname, "argodata_UV.nc"),
"V": os.path.join(datadirname, "argodata_UV.nc"),
"S": os.path.join(datadirname, "argodata_S.nc"),
"T": os.path.join(datadirname, "argodata_T.nc")}
variables = {'U': 'uo', 'V': 'vo', 'S': 'so', 'T': 'thetao'}
dimensions = {'lon': 'longitude', 'lat': 'latitude', 'time': 'time', 'depth': 'depth'}
"T": os.path.join(datadirname, "argodata_T.nc"),
}
variables = {"U": "uo", "V": "vo", "S": "so", "T": "thetao"}
dimensions = {
"lon": "longitude",
"lat": "latitude",
"time": "time",
"depth": "depth",
}

# create the fieldset and set interpolation methods
fieldset = FieldSet.from_netcdf(filenames, variables, dimensions, allow_time_extrapolation=False)
fieldset = FieldSet.from_netcdf(
filenames, variables, dimensions, allow_time_extrapolation=False
)
fieldset.T.interp_method = "linear_invdist_land_tracer"
for g in fieldset.gridset.grids:
if max(g.depth) > 0:
g.depth = -g.depth # make depth negative
fieldset.mindepth = -fieldset.U.depth[0] # uppermost layer in the hydrodynamic data
fieldset.add_constant('driftdepth', config.argo_characteristics["driftdepth"])
fieldset.add_constant('maxdepth', config.argo_characteristics["maxdepth"])
fieldset.add_constant('vertical_speed', config.argo_characteristics["vertical_speed"])
fieldset.add_constant('cycle_days', config.argo_characteristics["cycle_days"])
fieldset.add_constant('drift_days', config.argo_characteristics["drift_days"])
fieldset.add_constant("driftdepth", config.argo_characteristics["driftdepth"])
fieldset.add_constant("maxdepth", config.argo_characteristics["maxdepth"])
fieldset.add_constant(
"vertical_speed", config.argo_characteristics["vertical_speed"]
)
fieldset.add_constant("cycle_days", config.argo_characteristics["cycle_days"])
fieldset.add_constant("drift_days", config.argo_characteristics["drift_days"])
return fieldset
14 changes: 11 additions & 3 deletions virtual_ship/costs.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
"""costs function."""


def costs(config, total_time):
'''Calculates cost of the virtual ship (in US$)'''
"""
Calculate the cost of the virtual ship (in US$).

:param config: The cruise configuration.
:param total_time: Time cruised in seconds.
:returns: The calculated cost of the cruise.
"""
ship_cost_per_day = 30000
drifter_deploy_cost = 2500
argo_deploy_cost = 15000

ship_cost = ship_cost_per_day/24 * total_time//3600
ship_cost = ship_cost_per_day / 24 * total_time // 3600
argo_cost = len(config.argo_deploylocations) * argo_deploy_cost
drifter_cost = len(config.drifter_deploylocations) * drifter_deploy_cost

cost = ship_cost + argo_cost + drifter_cost
return cost
Loading