diff --git a/.gitignore b/.gitignore index deb0167c..3af1956f 100644 --- a/.gitignore +++ b/.gitignore @@ -179,3 +179,7 @@ src/virtualship/_version_setup.py .vscode/ .DS_Store + + +# some files created during paper (JOSS) writing +docs/paper/**/*.txt diff --git a/docs/paper/figure1.png b/docs/paper/figure1.png new file mode 100644 index 00000000..7316fcce Binary files /dev/null and b/docs/paper/figure1.png differ diff --git a/docs/paper/figure1.py b/docs/paper/figure1.py new file mode 100644 index 00000000..72030144 --- /dev/null +++ b/docs/paper/figure1.py @@ -0,0 +1,125 @@ +# %% + +import cartopy.crs as ccrs +import matplotlib.gridspec as gridspec +import matplotlib.image as mpimg +import matplotlib.pyplot as plt +import numpy as np +import xarray as xr +from plotting_functions import ( + _ctd_distance_along_expedition, + plot_adcp, + plot_ctd, + plot_drifters, +) + +SAMPLE_DIR = "sample_expedition/" +CONFIG = "expedition.yaml" +EXPEDITION = "MY_EXPEDITION" + +# %% + +# VirtualShip output +ctd_ds = xr.open_dataset(f"{SAMPLE_DIR}{EXPEDITION}/results/ctd.zarr") +ctd_bgc_ds = xr.open_dataset(f"{SAMPLE_DIR}{EXPEDITION}/results/ctd_bgc.zarr") +drifter_ds = xr.open_dataset(f"{SAMPLE_DIR}{EXPEDITION}/results/drifter.zarr") +adcp_ds = xr.open_dataset(f"{SAMPLE_DIR}{EXPEDITION}/results/adcp.zarr") + + +# %% + +# plot + +PROJ = ccrs.PlateCarree() + +waypoint_distances = np.unique(_ctd_distance_along_expedition(ctd_ds)["distance"]) + + +def add_waypoint_markers(ax, distances, offset=15, marker_size=70): + ax.scatter( + (distances / 1000), + np.zeros_like(distances) - offset, + marker="v", + color="black", + edgecolors="white", + s=marker_size, + clip_on=False, + ) + + +def add_title(ax, title, fontsize=13, y0=1.03): + """Add title.""" + ax.text( + 0, + y0, + title, + ha="left", + va="bottom", + transform=ax.transAxes, + fontsize=fontsize, + ) + + +# fig +fig = plt.figure(figsize=(9, 13), dpi=300) + +# custom layout +gs = gridspec.GridSpec(3, 2, height_ratios=[1.5, 1, 1]) +ax0 = fig.add_subplot(gs[0, :]) +ax1 = fig.add_subplot(gs[1, 0]) +ax2 = fig.add_subplot(gs[1, 1], projection=PROJ) +ax3 = fig.add_subplot(gs[2, 0]) +ax4 = fig.add_subplot(gs[2, 1]) + +# overview image +add_title(ax0, r"$\bf{a}$" + ") MFP expedition overview", y0=1.01) +img = mpimg.imread(f"{SAMPLE_DIR}expedition_overview.png") +ax0.imshow(img) +ax0.axis("off") + +# adcp +add_title(ax1, r"$\bf{b}$" + ") ADCP (flow speed)") +ax1.set_ylabel("Depth (m)") +ax1.set_xlabel("Distance (km)") +plot_adcp(adcp_ds, ax1) +add_waypoint_markers(ax1, waypoint_distances) + +# drifters +add_title(ax2, r"$\bf{c}$" + ") Surface drifters") +plot_drifters( + drifter_ds, + ax2, + vmin=drifter_ds.temperature.min(), + vmax=drifter_ds.temperature.max(), +) + +# CTD (temperature) +add_title(ax3, r"$\bf{d}$" + ") CTD (temperature)") +ax3.set_ylabel("Depth (m)") +ax3.set_xlabel("Distance (km)") +_, _distances_regular, _var_masked = plot_ctd( + ctd_ds, + ax3, + plot_variable="temperature", + vmin=ctd_ds.temperature.min(), + vmax=ctd_ds.temperature.max(), +) + +ctd_wp_distances = _distances_regular[np.nansum(_var_masked, axis=1) > 0] +add_waypoint_markers(ax3, ctd_wp_distances, offset=45, marker_size=60) + +# CTD (oxygen) +add_title(ax4, r"$\bf{e}$" + ") CTD (oxygen)") +ax4.set_xlabel("Distance (km)") +plot_ctd( + ctd_bgc_ds, ax4, plot_variable="oxygen", vmin=0, vmax=ctd_bgc_ds.o2.max() +) # vmin tailored to mark red as approximate oxygen minimum zone (~ 45 mmol/m-3) +add_waypoint_markers(ax4, ctd_wp_distances, offset=45, marker_size=60) + + +plt.tight_layout() +plt.show() + +fig.savefig("figure1.png", dpi=300, bbox_inches="tight") + +# %% diff --git a/docs/paper/jats/figure1.png b/docs/paper/jats/figure1.png new file mode 100644 index 00000000..7316fcce Binary files /dev/null and b/docs/paper/jats/figure1.png differ diff --git a/docs/paper/jats/paper.jats b/docs/paper/jats/paper.jats new file mode 100644 index 00000000..049f013f --- /dev/null +++ b/docs/paper/jats/paper.jats @@ -0,0 +1,671 @@ + + +
+ + + + +Journal of Open Source Software +JOSS + +2475-9066 + +Open Journals + + + +0 +N/A + +VirtualShip for simulating oceanographic fieldwork +anywhere in the global ocean + + + +https://orcid.org/0000-0002-5735-3312 + +Atkins +Jamie R. C. + + + +* + + +https://orcid.org/0009-0005-9805-5257 + +Daniels +Emma E. + + + + + +Hodgskin +Nick + + + + + +Stuurman +Aart C. + + + + +https://orcid.org/0000-0002-2484-510X + +Simoes-Sousa +Iury + + + + +https://orcid.org/0000-0003-2041-0704 + +van Sebille +Erik + + + + + + +Freudenthal Institute, Utrecht University, the +Netherlands + + + + +Institute for Marine and Atmospheric Research, Utrecht +University, the Netherlands + + + + +Woods Hole Oceanographic Institution, Falmouth, MA, +USA + + + + +* E-mail: + + +4 +12 +2025 + +¿VOL? +¿ISSUE? +¿PAGE? + +Authors of papers retain copyright and release the +work under a Creative Commons Attribution 4.0 International License (CC +BY 4.0) +1970 +The article authors + +Authors of papers retain copyright and release the work under +a Creative Commons Attribution 4.0 International License (CC BY +4.0) + + + +Python +oceanography +fieldwork simulation +(under)graduate training +Lagrangian modelling +instrument and sampling design + + + + + + Summary +

VirtualShip is a Python-based package for + simulating measurements as if they were coming from real-life + oceanographic instruments, facilitating student training, expedition + planning, and design of instrument/sampling strategies. The software + exploits the customisability of the open-source + Parcels Lagrangian simulation framework + (Delandmeter + & van Sebille, 2019; + Lange + & van Sebille, 2017) and builds a virtual ocean by + streaming data from the + Copernicus + Marine Data Store on-the-fly, enabling expeditions anywhere + on the globe.

+
+ + Statement of need +

Marine science relies on fieldwork for data collection, yet + sea-going opportunities are limited due to financial costs, logistical + constraints, and environmental burdens. We present an alternative + means, namely VirtualShip, for training + scientists to conduct oceanographic fieldwork in an authentic manner, + to plan future expeditions and deployments, and to directly compare + observational and instrumentational strategies with model data.

+

VirtualShip goes beyond simply extracting + grid-cell values from model output. Instead, it uses programmable + behaviours and sophisticated interpolation techniques (with + Parcels underpinnings) to access data in exact + locations and timings, as if they were being collected by real-world + instruments. VirtualShip shares some + functionality with existing tools, such as + OceanSpy + (Almansi + et al., 2019) and VirtualFleet + (Maze + & Balem, 2023), but extends capabilities to mesh many + different instrument deployments into a unified expedition simulation + framework. Moreover, VirtualShip exploits + readily available, streamable data via the Copernicus Marine Data + Store, removing the need for users to download and manage large + datasets locally and/or arrange for access to remote servers. + VirtualShip can also integrate coordinate files + exported from the + Marine + Facilities Planning (MFP) tool, giving users the option to + define expedition waypoints via an intuitive web-based mapping + interface.

+
+ + Functionality +

VirtualShip simulates the deployment of + virtual instruments commonly used in oceanographic fieldwork, with + emphasis on realism in how users plan and execute expeditions. For + example, users must consider ship speed and instrument + deployment/recovery times to ensure their expedition is feasible + within given time constraints. Possible instrument selections include + surface Drifter + (Lumpkin + et al., 2017), CTD + (Conductivity-Temperature-Depth; Johnson et al. + (2007)), + Argo float + (Jayne + et al., 2017), XBT (Expendable + Bathythermograph; Goni et al. + (2019)), + underway ADCP (Acoustic Doppler Current + Profiler; Kostaschuk et al. + (2005)), + and underway Underwater_temperature/salinity + (Gordon + et al., 2014) probes. More detail on each instrument is + available in the + documentation.

+

The software can simulate complex multidisciplinary expeditions. + One example is a virtual expedition across the Agulhas Current and the + South Eastern Atlantic that deploys a suite of instruments to sample + physical and biogeochemical properties + ([fig:fig1]). Key + circulation features appear early in the expedition track, with + enhanced ADCP speeds marking the strong Agulhas Current + ([fig:fig1]b) and + drifters that turn back toward the Indian Ocean indicating the Agulhas + Retroflection + ([fig:fig1]c). The + CTD profiles capture the vertical structure of temperature and oxygen + along the route, including the warmer surface waters of the Agulhas + region ([fig:fig1]d, + early waypoints) and the Oxygen Minimum Zone in the South Eastern + Atlantic + ([fig:fig1]e, final + waypoints).

+ +

Example VirtualShip expedition simulated in July/August + 2023. Expedition waypoints displayed via the MFP tool (a), Underway + ADCP measurements (b), Surface drifter releases (c; 90-day lifetime + per drifter), and CTD vertical profiles for temperature (d) and + oxygen (e). Black triangles in b), d) and e) mark waypoint locations + across the expedition route, corresponding to the purple markers in + a).

+ +
+

The software is designed to be highly intuitive to the user. It is + wrapped into three high-level command line interface commands (using + Click):

+ + +

virtualship init: Initialises the + expedition directory structure and an + expedition.yaml configuration file, which + controls the expedition route, instrument choices and deployment + timings. A common workflow is for users to import pre-determined + waypoint coordinates using the --from-mfp + flag in combination with a coordinates .csv + or .xlsx file (e.g. exported from the + MFP + tool).

+
+ +

virtualship plan: Launches a + user-friendly Terminal-based expedition planning User Interface + (UI), built using + Textual. + This allows users to intuitively set their waypoint timings and + instrument selections, and also modify their waypoint + locations.

+
+ +

virtualship run: Executes the virtual + expedition according to the planned configuration. This includes + streaming data via the + Copernicus + Marine Data Store, simulating the instrument beahviours + and sampling, and saving the output in + Zarr + format.

+
+
+

A full example workflow is outlined in the + Quickstart + Guide documentation.

+
+ + Implementation +

Under the hood, VirtualShip is modular and + extensible. The workflows are designed around + Instrument base classes and instrument-specific + subclasses and methods. This means the platform can be easily extended + to add new instrument types. Instrument behaviours are coded as + Parcels kernels, which allows for extensive + customisability. For example, a Drifter advects + passively with ocean currents, a CTD performs + vertical profiling in the water column and an + ArgoFloat cycles between ascent, descent and + drift phases, all whilst sampling physical and/or biogeochemical + fields at their respective locations and times.

+

Moreover, the data ingestion system relies on Analysis-Ready and + Cloud-Optimized data (ARCO; Stern et al. + (2022), + Abernathey et al. + (2021)) + streamed directly from the Copernicus Marine Data Store, via the + copernicusmarine + Python toolbox. This means users can simulate expeditions anywhere in + the global ocean without downloading large datasets by default. + Leveraging the suite of + physics + and biogeochemical products available on the Copernicus + plaform, expeditions are possible from 1993 to present and forecasted + two weeks into the future. There is also an + option + for the user to specify local NetCDF files for + data ingestion, if preferred.

+
+ + Applications and future outlook +

VirtualShip has already been extensvely + applied in Master’s teaching settings at Utrecht University as part of + the + VirtualShip + Classroom initiative. Educational assignments and tutorials + have been developed alongside to integrate the tool into coursework, + including projects where students design their own research + question(s) and execute their fieldwork and analysis using + VirtualShip. Its application has been shown to + be successful, with students reporting increased self-efficacy and + knowledge in executing oceanographic fieldwork + (Daniels + et al., 2025).

+

The package opens space for many other research applications. It + can support real-life expedition planning by letting users test + sampling routes before going to sea. It also provides tooling to + explore real-time adaptive strategies in which sampling plans shift as + forecasts or observations update. The same workflow can also be used + to investigate sampling efficiency, for example, examining how + waypoint number or spacing shapes the ability to capture features of + interest. Moreover, the software is well-suited for developing + Observation System Simulation Experiments (OSSEs; e.g. Errico et al. + (2013)) + to test and optimise observational strategies in a cost- and + time-efficient manner.

+

Both the customisability of the VirtualShip + platform and the exciting potential for new ARCO-based data hosting + services in domains beyond oceanography (e.g., + atmospheric + science) means there is potential to extend VirtualShip (or + “VirtualShip-like” tools) to other domains in the future. Furthermore, + as the Parcels underpinnings themselves + continue to evolve, with a future (at time of writing) + v4.0 + release focusing on alignment with + Pangeo + standards and Xarray data structures + (Hoyer + & Hamman, 2017), VirtualShip will + also benefit from these improvements, further enhancing its + capabilities, extensibility and compatability with modern cloud-based + data pipelines.

+
+ + Acknowledgements +

The VirtualShip project is funded through the Utrecht + University-NIOZ (Royal Netherlands Institute for Sea Research) + collaboration.

+
+ + + + + + + + LangeMichael + van SebilleErik + + Parcels v0.9: prototyping a Lagrangian ocean analysis framework for the petascale age + Geoscientific Model Development + Copernicus GmbH + 2017 + 10 + 11 + 1991-9603 + http://dx.doi.org/10.5194/gmd-10-4175-2017 + 10.5194/gmd-10-4175-2017 + 4175 + 4186 + + + + + + DelandmeterPhilippe + van SebilleErik + + The Parcels v2.0 Lagrangian framework: new field interpolation schemes + Geoscientific Model Development + Copernicus GmbH + 2019 + 12 + 8 + 1991-9603 + http://dx.doi.org/10.5194/gmd-12-3571-2019 + 10.5194/gmd-12-3571-2019 + 3571 + 3584 + + + + + + DanielsEmma + ChytasChristos + SebilleErik van + + The virtual ship classroom: Developing virtual fieldwork as an authentic learning environment for physical oceanography + Current: The Journal of Marine Education + 202509 + 10.5334/cjme.121 + + + + + + AlmansiMattia + GelderloosRenske + HaineThomas W. n. + SaberiAtousa + SiddiquiAli H. + + OceanSpy: A python package to facilitate ocean model data analysis and visualization + Journal of Open Source Software + The Open Journal + 2019 + 4 + 39 + https://doi.org/10.21105/joss.01506 + 10.21105/joss.01506 + 1506 + + + + + + + AbernatheyRyan P. + AugspurgerTom + BanihirweAnderson + Blackmon-LucaCharles C. + CroneTimothy J. + GentemannChelle L. + HammanJoseph J. + HendersonNaomi + LeporeChiara + McCaieTheo A. + RobinsonNiall H. + SignellRichard P. + + Cloud-native repositories for big scientific data + Computing in Science & Engineering + 2021 + 23 + 2 + 10.1109/MCSE.2021.3059437 + 26 + 35 + + + + + + MazeGuillaume + BalemKevin + + Virtual fleet - recovery + Zenodo + 202301 + https://doi.org/10.5281/zenodo.7520147 + 10.5281/zenodo.7520147 + + + + + + SternCharles + AbernatheyRyan + HammanJoseph + WegenerRachel + LeporeChiara + HarkinsSean + MeroseAlexander + + Pangeo forge: Crowdsourcing analysis-ready, cloud optimized data production + Frontiers in Climate + 2022 + Volume 3 - 2021 + 2624-9553 + https://www.frontiersin.org/journals/climate/articles/10.3389/fclim.2021.782909 + 10.3389/fclim.2021.782909 + + + + + + HoyerS. + HammanJ. + + Xarray: N-D labeled arrays and datasets in Python + Journal of Open Research Software + Ubiquity Press + 2017 + 5 + 1 + https://doi.org/10.5334/jors.148 + 10.5334/jors.148 + + + + + + ErricoRonald M. + YangRunhua + PrivéNikki C. + TaiKing-Sheng + TodlingRicardo + SienkiewiczMeta E. + GuoJing + + Development and validation of observing-system simulation experiments at NASA’s global modeling and assimilation office + Quarterly Journal of the Royal Meteorological Society + 2013 + 139 + 674 + https://rmets.onlinelibrary.wiley.com/doi/abs/10.1002/qj.2027 + https://doi.org/10.1002/qj.2027 + 1162 + 1178 + + + + + + LumpkinRick + ÖzgökmenTamay + CenturioniLuca + + Advances in the application of surface drifters + Annual Review of Marine Science + Annual Reviews + 2017 + 9 + Volume 9, 2017 + 1941-0611 + https://www.annualreviews.org/content/journals/10.1146/annurev-marine-010816-060641 + https://doi.org/10.1146/annurev-marine-010816-060641 + 59 + 81 + + + + + + JayneSteven R. + RoemmichDean + ZilbermanNathalie + RiserStephen C. + JohnsonKenneth S. + JohnsonGregory C. + PiotrowiczStephen R. + + The argo program: Present and future + Oceanography + Oceanography Society + 2017 + 20251217 + 30 + 2 + http://www.jstor.org/stable/26201840 + 18 + 28 + + + + + + GoniGustavo J. + SprintallJanet + BringasFrancis + ChengLijing + CiranoMauro + DongShenfu + DominguesRicardo + GoesMarlos + LopezHosmay + MorrowRosemary + RiveroUlises + RossbyThomas + ToddRobert E. + TrinanesJoaquin + ZilbermanNathalie + BaringerMolly + BoyerTim + CowleyRebecca + DominguesCatia M. + HutchinsonKatherine + KrampMartin + MataMauricio M. + ReseghettiFranco + SunCharles + Bhaskar TVSUdaya + VolkovDenis + + More than 50 years of successful continuous temperature section measurements by the global expendable bathythermograph network, its integrability, societal benefits, and future + Frontiers in Marine Science + 2019 + Volume 6 - 2019 + 2296-7745 + https://www.frontiersin.org/journals/marine-science/articles/10.3389/fmars.2019.00452 + 10.3389/fmars.2019.00452 + + + + + + KostaschukRay + BestJim + VillardPaul + PeakallJeff + FranklinMark + + Measuring flow velocity and sediment transport with an acoustic doppler current profiler + Geomorphology + 2005 + 68 + 1 + 0169-555X + https://www.sciencedirect.com/science/article/pii/S0169555X04002879 + https://doi.org/10.1016/j.geomorph.2004.07.012 + 25 + 37 + + + + + + GordonArnold L. + FlamentPierre + VillanoyCesar + CenturioniLuca + + The nascent kuroshio of lamon bay + Journal of Geophysical Research: Oceans + 2014 + 119 + 7 + https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1002/2014JC009882 + https://doi.org/10.1002/2014JC009882 + 4251 + 4263 + + + + + + JohnsonGregory C. + TooleJohn M. + LarsonNordeen G. + + Sensor corrections for sea-bird SBE-41CP and SBE-41 CTDs + Journal of Atmospheric and Oceanic Technology + American Meteorological Society + Boston MA, USA + 2007 + 24 + 6 + https://journals.ametsoc.org/view/journals/atot/24/6/jtech2016_1.xml + 10.1175/JTECH2016.1 + 1117 + 1130 + + + + +
diff --git a/docs/paper/paper.bib b/docs/paper/paper.bib new file mode 100644 index 00000000..84cae273 --- /dev/null +++ b/docs/paper/paper.bib @@ -0,0 +1,218 @@ +@article{Lange2017, + author = {Lange, Michael and {van Sebille}, Erik}, + doi = {10.5194/gmd-10-4175-2017}, + issn = {1991-9603}, + journal = {Geoscientific Model Development}, + month = {November}, + number = {11}, + pages = {4175–4186}, + publisher = {Copernicus GmbH}, + title = {{Parcels v0.9: prototyping a Lagrangian ocean analysis framework for the petascale age}}, + url = {http://dx.doi.org/10.5194/gmd-10-4175-2017}, + volume = {10}, + year = {2017} +} + +@article{Delandmeter2019, + author = {Delandmeter, Philippe and {van Sebille}, Erik}, + doi = {10.5194/gmd-12-3571-2019}, + issn = {1991-9603}, + journal = {Geoscientific Model Development}, + month = {August}, + number = {8}, + pages = {3571–3584}, + publisher = {Copernicus GmbH}, + title = {{The Parcels v2.0 Lagrangian framework: new field interpolation schemes}}, + url = {http://dx.doi.org/10.5194/gmd-12-3571-2019}, + volume = {12}, + year = {2019} +} + + +@article{Daniels2025, + abstract = {Typical physical oceanography fieldwork involves boarding a research vessel and traveling to the open ocean for periods of up to several weeks. Such scientific research expeditions are expensive, time-consuming, logistically challenging, and therefore are not very accessible. We present design-based research about an alternative: a virtual fieldwork experience using the new VirtualShip Python package and accompanying lesson materials. Data are collected in two graduate courses using qualitative and quantitative methods, and include interviews, surveys, and grading rubrics to investigate students\’ learning outcomes and learning experience. We find that the virtual fieldwork was highly engaging, and students report on enhanced self-efficacy and knowledge. We conclude that student involvement and learning were boosted by using the Virtual Ship Classroom as an authentic learning environment.}, + author = {Daniels, Emma and Chytas, Christos and van Sebille, Erik}, + doi = {10.5334/cjme.121}, + journal = {Current: The Journal of Marine Education}, + keyword = {en_US}, + month = {Sep}, + title = {The Virtual Ship Classroom: Developing Virtual Fieldwork as an Authentic Learning Environment for Physical Oceanography}, + year = {2025} +} + +@article{Almansi2019, doi = {10.21105/joss.01506}, url = {https://doi.org/10.21105/joss.01506}, year = {2019}, publisher = {The Open Journal}, volume = {4}, number = {39}, pages = {1506}, author = {Almansi, Mattia and Gelderloos, Renske and Haine, Thomas W. n. and Saberi, Atousa and Siddiqui, Ali H.}, title = {OceanSpy: A Python package to facilitate ocean model data analysis and visualization}, journal = {Journal of Open Source Software} } + + +@ARTICLE{Abernathey2021, + author={Abernathey, Ryan P. and Augspurger, Tom and Banihirwe, Anderson and Blackmon-Luca, Charles C. and Crone, Timothy J. and Gentemann, Chelle L. and Hamman, Joseph J. and Henderson, Naomi and Lepore, Chiara and McCaie, Theo A. and Robinson, Niall H. and Signell, Richard P.}, + journal={Computing in Science & Engineering}, + title={Cloud-Native Repositories for Big Scientific Data}, + year={2021}, + volume={23}, + number={2}, + pages={26-35}, + keywords={Cloud computing;Training data;Computational modeling;Reproducibility of results;Collaboration;Reliability;Distributed databases}, + doi={10.1109/MCSE.2021.3059437}} + +@software{Maze2023, + author = {Guillaume Maze and + Kevin Balem}, + title = {Virtual Fleet - Recovery}, + month = jan, + year = 2023, + publisher = {Zenodo}, + version = {v0.1}, + doi = {10.5281/zenodo.7520147}, + url = {https://doi.org/10.5281/zenodo.7520147}, +} + +@ARTICLE{Stern2022, + +AUTHOR={Stern, Charles and Abernathey, Ryan and Hamman, Joseph and Wegener, Rachel and Lepore, Chiara and Harkins, Sean and Merose, Alexander }, + +TITLE={Pangeo Forge: Crowdsourcing Analysis-Ready, Cloud Optimized Data Production}, + +JOURNAL={Frontiers in Climate}, + +VOLUME={Volume 3 - 2021}, + +YEAR={2022}, + +URL={https://www.frontiersin.org/journals/climate/articles/10.3389/fclim.2021.782909}, + +DOI={10.3389/fclim.2021.782909}, + +ISSN={2624-9553}, + +ABSTRACT={Pangeo Forge is a new community-driven platform that accelerates science by providing high-level recipe frameworks alongside cloud compute infrastructure for extracting data from provider archives, transforming it into analysis-ready, cloud-optimized (ARCO) data stores, and providing a human- and machine-readable catalog for browsing and loading. In abstracting the scientific domain logic of data recipes from cloud infrastructure concerns, Pangeo Forge aims to open a door for a broader community of scientists to participate in ARCO data production. A wholly open-source platform composed of multiple modular components, Pangeo Forge presents a foundation for the practice of reproducible, cloud-native, big-data ocean, weather, and climate science without relying on proprietary or cloud-vendor-specific tooling.}} + + +@article{Hoyer2017, + title = {xarray: {N-D} labeled arrays and datasets in {Python}}, + author = {Hoyer, S. and J. Hamman}, + journal = {Journal of Open Research Software}, + volume = {5}, + number = {1}, + year = {2017}, + publisher = {Ubiquity Press}, + doi = {10.5334/jors.148}, + url = {https://doi.org/10.5334/jors.148} +} + +@article{Errico2013, +author = {Errico, Ronald M. and Yang, Runhua and Privé, Nikki C. and Tai, King-Sheng and Todling, Ricardo and Sienkiewicz, Meta E. and Guo, Jing}, +title = {Development and validation of observing-system simulation experiments at NASA's Global Modeling and Assimilation Office}, +journal = {Quarterly Journal of the Royal Meteorological Society}, +volume = {139}, +number = {674}, +pages = {1162-1178}, +keywords = {OSSE, data assimilation, atmospheric observations}, +doi = {https://doi.org/10.1002/qj.2027}, +url = {https://rmets.onlinelibrary.wiley.com/doi/abs/10.1002/qj.2027}, +eprint = {https://rmets.onlinelibrary.wiley.com/doi/pdf/10.1002/qj.2027}, +abstract = {Abstract Initial design and validation of baseline Observing System Simulation Experiments (OSSEs) at NASA's Global Modeling and Assimilation Office (GMAO) are described. The OSSEs mimic the procedures used to analyze global observations for specifying states of the atmosphere. As simulations, however, OSSEs are not only confined to already existing observations and they provide a perfect description of the true state being analyzed. These two properties of the simulations can be exploited to improve both existing and envisioned observing systems and the algorithms to analyze them. Preliminary to any applications, however, the OSSE framework must be adequately validated. This first version of the simulated observations is drawn from a 13 month simulation of nature produced by the European Center for Medium-Range Weather Forecasts. These observations include simulated errors of both instruments and representativeness. Since the statistics of analysis and forecast errors are partially determined by these observational errors, their appropriate modelling can be crucial for validating the realism of the OSSE. That validation is performed by comparing the statistics of the results of assimilating these simulated observations for one summer month compared with the corresponding statistics obtained from assimilating real observations during the same time of year. The assimilation system is the three-dimensional variational analysis (GSI) scheme used at both the National Centers for Environmental Prediction and GMAO. Here, only statistics concerning observation innovations or analysis increments within the troposphere are considered for the validation. In terms of the examined statistics, the OSSE is validated remarkably well, even with some simplifications currently employed. In order to obtain this degree of success, it was necessary to employ horizontally correlated observation errors for both atmospheric motion vectors and some satellite observed radiances. The simulated observations with added observation errors appear suitable for some initial OSSE applications.}, +year = {2013} +} + +@article{Lumpkin2017, + author = "Lumpkin, Rick and Özgökmen, Tamay and Centurioni, Luca", + title = "Advances in the Application of Surface Drifters", + journal= "Annual Review of Marine Science", + year = "2017", + volume = "9", + number = "Volume 9, 2017", + pages = "59-81", + doi = "https://doi.org/10.1146/annurev-marine-010816-060641", + url = "https://www.annualreviews.org/content/journals/10.1146/annurev-marine-010816-060641", + publisher = "Annual Reviews", + issn = "1941-0611", + type = "Journal Article", + keywords = "drifters", + keywords = "dispersion", + keywords = "transport", + keywords = "Lagrangian observations", + keywords = "surface currents", + abstract = "Surface drifting buoys, or drifters, are used in oceanographic and climate research, oil spill tracking, weather forecasting, search and rescue operations, calibration and validation of velocities from high-frequency radar and from altimeters, iceberg tracking, and support of offshore drilling operations. In this review, we present a brief history of drifters, from the message in a bottle to the latest satellite-tracked, multisensor drifters. We discuss the different types of drifters currently used for research and operations as well as drifter designs in development. We conclude with a discussion of the various properties that can be observed with drifters, with heavy emphasis on a critical process that cannot adequately be observed by any other instrument: dispersion in the upper ocean, driven by turbulence at scales from waves through the submesoscale to the large-scale geostrophic eddies.", + } + +@article{Jayne2017, + ISSN = {10428275, 2377617X}, + URL = {http://www.jstor.org/stable/26201840}, + abstract = {ABSTRACT. The Argo Program has revolutionized large-scale physical oceanography through its contributions to basic research, national and international climate assessment, education, and ocean state estimation and forecasting. This article discusses the present status of Argo and enhancements that are underway. Extensions of the array into seasonally ice-covered regions and marginal seas as well as increased numbers of floats along the equator and around western boundary current extensions have been proposed. In addition, conventional Argo floats, with their 2,000 m sampling limit, currently observe only the upper half of the open ocean volume. Recent advances in profiling float technology and in the accuracy and stability of float-mounted conductivity-temperature-depth sensors make it practical to obtain measurements to 6,000 m. The Deep Argo array will help observe and constrain the global budgets of heat content, freshwater, and steric sea level, as well as the full-depth ocean circulation. Finally, another extension to the Argo Program is the addition of a diverse set of chemical sensors to profiling floats in order to build a Biogeochemical-Argo array to understand the carbon cycle, the biological pump, and ocean acidification.}, + author = {Steven R. Jayne and Dean Roemmich and Nathalie Zilberman and Stephen C. Riser and Kenneth S. Johnson and Gregory C. Johnson and Stephen R. Piotrowicz}, + journal = {Oceanography}, + number = {2}, + pages = {18--28}, + publisher = {Oceanography Society}, + title = {The Argo Program: Present and Future}, + urldate = {2025-12-17}, + volume = {30}, + year = {2017} +} + +@ARTICLE{Goni2019, + +AUTHOR={Goni, Gustavo J. and Sprintall, Janet and Bringas, Francis and Cheng, Lijing and Cirano, Mauro and Dong, Shenfu and Domingues, Ricardo and Goes, Marlos and Lopez, Hosmay and Morrow, Rosemary and Rivero, Ulises and Rossby, Thomas and Todd, Robert E. and Trinanes, Joaquin and Zilberman, Nathalie and Baringer, Molly and Boyer, Tim and Cowley, Rebecca and Domingues, Catia M. and Hutchinson, Katherine and Kramp, Martin and Mata, Mauricio M. and Reseghetti, Franco and Sun, Charles and Bhaskar TVS, Udaya and Volkov, Denis }, + +TITLE={More Than 50 Years of Successful Continuous Temperature Section Measurements by the Global Expendable Bathythermograph Network, Its Integrability, Societal Benefits, and Future}, + +JOURNAL={Frontiers in Marine Science}, + +VOLUME={Volume 6 - 2019}, + +YEAR={2019}, + +URL={https://www.frontiersin.org/journals/marine-science/articles/10.3389/fmars.2019.00452}, + +DOI={10.3389/fmars.2019.00452}, + +ISSN={2296-7745}, + +ABSTRACT={The first eXpendable BathyThermographs (XBTs) were deployed in the 1960s in the North Atlantic Ocean. In 1967 XBTs were deployed in operational mode to provide a continuous record of temperature profile data along repeated transects, now known as the Global XBT Network. The current network is designed to monitor ocean circulation and boundary current variability, basin-wide and trans-basin ocean heat transport, and global and regional heat content. The ability of the XBT Network to systematically map the upper ocean thermal field in multiple basins with repeated trans-basin sections at eddy-resolving scales remains unmatched today and cannot be reproduced at present by any other observing platform. Some repeated XBT transects have now been continuously occupied for more than 30 years, providing an unprecedented long-term climate record of temperature and geostrophic velocity profiles that are used to understand variability in ocean heat content, sea level change, and meridional ocean heat transport. Here, we present key scientific advances in understanding the changing ocean and climate system supported by XBT observations. Improvement in XBT data quality and its impact on computations, particularly of ocean heat content, are presented. Technology development for probes, launchers, and transmission techniques are also discussed. Finally, we offer new perspectives for the future of the Global XBT Network. + +}} + +@article{Kostaschuk2005, +title = {Measuring flow velocity and sediment transport with an acoustic Doppler current profiler}, +journal = {Geomorphology}, +volume = {68}, +number = {1}, +pages = {25-37}, +year = {2005}, +note = {Fluid Flow and Sediment Transport Process in Geomorphology}, +issn = {0169-555X}, +doi = {https://doi.org/10.1016/j.geomorph.2004.07.012}, +url = {https://www.sciencedirect.com/science/article/pii/S0169555X04002879}, +author = {Ray Kostaschuk and Jim Best and Paul Villard and Jeff Peakall and Mark Franklin}, +keywords = {Acoustic Doppler current profiler, Flow velocity, Bed load velocity, Sediment transport}, +abstract = {An acoustic Doppler current profiler (aDcp) measures three-dimensional velocity profiles within the water column using the Doppler shift principle, whilst the bottom tracking function and acoustic backscatter can be used to measure bed load velocity and estimate suspended sediment concentration. The aDcp offers many advantages over traditional single-point current meters and sediment samplers, including deployment from a moving launch, a single instrument for both velocity and sediment transport measurements, profiles of three-dimensional velocity and suspended sediment and the ability to map an entire flow field. Limitations of aDcps include a large sampling diameter close to the bed, coarse measurement of vertical velocity, fragmented bottom track records, a poor understanding of the relation between bottom tracking and the mechanisms of sediment transport, and sensitivity of the acoustic backscatter to particle size.} +} + +@article{Gordon2014, +author = {Gordon, Arnold L. and Flament, Pierre and Villanoy, Cesar and Centurioni, Luca}, +title = {The nascent Kuroshio of Lamon Bay}, +journal = {Journal of Geophysical Research: Oceans}, +volume = {119}, +number = {7}, +pages = {4251-4263}, +keywords = {Kuroshio, NEC bifurcation, Lamon Bay, Philippines}, +doi = {https://doi.org/10.1002/2014JC009882}, +url = {https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1002/2014JC009882}, +eprint = {https://agupubs.onlinelibrary.wiley.com/doi/pdf/10.1002/2014JC009882}, +abstract = {Abstract A northward flowing current, emanating from the North Equatorial Current (NEC) bifurcation at the Philippine margin, enters Lamon Bay along Luzon's eastern coast. There the NEC tropical water masses merge with subtropical water of the western North Pacific to form the Kuroshio. A northward flowing western boundary current is first observed near 16.5°N, marking the initiation of the Kuroshio. The current feeding into the nascent Kuroshio of Lamon Bay is bracketed by an anticyclonic dipole to its northeast and a cyclonic dipole to its southwest. Ship-based observational programs in the spring seasons of 2011 and 2012 detect a shift of the Lamon Bay thermohaline stratification with marked enrichment of NEC tropical thermocline water in 2012 relative to a dominant western North Pacific subtropical stratification of 2011. Temperature-salinity time series from moorings spanning the two ship-based observations identify the timing of the transition as December 2011. The NEC bifurcation was further south in May 2012 than in May 2011. We suggest that the more southern bifurcation in May 2012 induced increased NEC thermocline water injection into Lamon Bay and nascent Kuroshio, increasing the linkage of the western North Pacific subtropical and tropical thermoclines. This connection was reduced in May 2011 as the NEC bifurcation shifted into a more northerly position and western North Pacific subtropical thermocline dominated Lamon Bay stratification.}, +year = {2014} +} + +@article {Johnson2007, + author = "Gregory C. Johnson and John M. Toole and Nordeen G. Larson", + title = "Sensor Corrections for Sea-Bird SBE-41CP and SBE-41 CTDs", + journal = "Journal of Atmospheric and Oceanic Technology", + year = "2007", + publisher = "American Meteorological Society", + address = "Boston MA, USA", + volume = "24", + number = "6", + doi = "10.1175/JTECH2016.1", + pages= "1117 - 1130", + url = "https://journals.ametsoc.org/view/journals/atot/24/6/jtech2016_1.xml" +} diff --git a/docs/paper/paper.md b/docs/paper/paper.md new file mode 100644 index 00000000..3eebf5a9 --- /dev/null +++ b/docs/paper/paper.md @@ -0,0 +1,84 @@ +--- +title: "VirtualShip for simulating oceanographic fieldwork anywhere in the global ocean" +tags: + - Python + - oceanography + - fieldwork simulation + - (under)graduate training + - Lagrangian modelling + - instrument and sampling design +authors: + - name: Jamie R. C. Atkins + orcid: 0000-0002-5735-3312 + corresponding: true + affiliation: "1, 2" + - name: Emma E. Daniels + orcid: 0009-0005-9805-5257 + affiliation: 1 + - name: Nick Hodgskin + affiliation: 2 + - name: Aart C. Stuurman + affiliation: 1 + - name: Iury Simoes-Sousa + orcid: 0000-0002-2484-510X + affiliation: 3 + - name: Erik van Sebille + orcid: 0000-0003-2041-0704 + affiliation: "1, 2" + +affiliations: + - name: Freudenthal Institute, Utrecht University, the Netherlands + index: 1 + - name: Institute for Marine and Atmospheric Research, Utrecht University, the Netherlands + index: 2 + - name: Woods Hole Oceanographic Institution, Falmouth, MA, USA + index: 3 +date: 4 December 2025 +bibliography: paper.bib +--- + +# Summary + +`VirtualShip` is a Python-based package for simulating measurements as if they were coming from real-life oceanographic instruments, facilitating student training, expedition planning, and design of instrument/sampling strategies. The software exploits the customisability of the open-source `Parcels` Lagrangian simulation framework [@Lange2017; @Delandmeter2019] and builds a virtual ocean by streaming data from the [Copernicus Marine Data Store](https://marine.copernicus.eu/) on-the-fly, enabling expeditions anywhere on the globe. + +# Statement of need + +Marine science relies on fieldwork for data collection, yet sea-going opportunities are limited due to financial costs, logistical constraints, and environmental burdens. We present an alternative means, namely `VirtualShip`, for training scientists to conduct oceanographic fieldwork in an authentic manner, to plan future expeditions and deployments, and to directly compare observational and instrumentational strategies with model data. + +`VirtualShip` goes beyond simply extracting grid-cell values from model output. Instead, it uses programmable behaviours and sophisticated interpolation techniques (with `Parcels` underpinnings) to access data in exact locations and timings, as if they were being collected by real-world instruments. `VirtualShip` shares some functionality with existing tools, such as `OceanSpy` [@Almansi2019] and `VirtualFleet` [@Maze2023], but extends capabilities to mesh many different instrument deployments into a unified expedition simulation framework. Moreover, `VirtualShip` exploits readily available, streamable data via the Copernicus Marine Data Store, removing the need for users to download and manage large datasets locally and/or arrange for access to remote servers. `VirtualShip` can also integrate coordinate files exported from the [Marine Facilities Planning](https://www.marinefacilitiesplanning.com/cruiselocationplanning#) (MFP) tool, giving users the option to define expedition waypoints via an intuitive web-based mapping interface. + +# Functionality + +`VirtualShip` simulates the deployment of virtual instruments commonly used in oceanographic fieldwork, with emphasis on realism in how users plan and execute expeditions. For example, users must consider ship speed and instrument deployment/recovery times to ensure their expedition is feasible within given time constraints. Possible instrument selections include surface `Drifter` [@Lumpkin2017], `CTD` (Conductivity-Temperature-Depth; @Johnson2007), `Argo float` [@Jayne2017], `XBT` (Expendable Bathythermograph; @Goni2019), underway `ADCP` (Acoustic Doppler Current Profiler; @Kostaschuk2005), and underway `Underwater_temperature/salinity` [@Gordon2014] probes. More detail on each instrument is available in the [documentation](https://virtualship.readthedocs.io/en/latest/user-guide/assignments/Research_proposal_intro.html#Measurement-Options). + +The software can simulate complex multidisciplinary expeditions. One example is a virtual expedition across the Agulhas Current and the South Eastern Atlantic that deploys a suite of instruments to sample physical and biogeochemical properties (\autoref{fig:fig1}). Key circulation features appear early in the expedition track, with enhanced ADCP speeds marking the strong Agulhas Current (\autoref{fig:fig1}b) and drifters that turn back toward the Indian Ocean indicating the Agulhas Retroflection (\autoref{fig:fig1}c). The CTD profiles capture the vertical structure of temperature and oxygen along the route, including the warmer surface waters of the Agulhas region (\autoref{fig:fig1}d, early waypoints) and the Oxygen Minimum Zone in the South Eastern Atlantic (\autoref{fig:fig1}e, final waypoints). + +![Example VirtualShip expedition simulated in July/August 2023. Expedition waypoints displayed via the MFP tool (a), Underway ADCP measurements (b), Surface drifter releases (c; 90-day lifetime per drifter), and CTD vertical profiles for temperature (d) and oxygen (e). Black triangles in b), d) and e) mark waypoint locations across the expedition route, corresponding to the purple markers in a).\label{fig:fig1}](figure1.png) + +The software is designed to be highly intuitive to the user. It is wrapped into three high-level command line interface commands (using [Click](https://click.palletsprojects.com/en/stable/)): + +1. `virtualship init`: Initialises the expedition directory structure and an `expedition.yaml` configuration file, which controls the expedition route, instrument choices and deployment timings. A common workflow is for users to import pre-determined waypoint coordinates using the `--from-mfp` flag in combination with a coordinates `.csv` or `.xlsx` file (e.g. exported from the [MFP](https://www.marinefacilitiesplanning.com/cruiselocationplanning#) tool). +2. `virtualship plan`: Launches a user-friendly Terminal-based expedition planning User Interface (UI), built using [`Textual`](https://textual.textualize.io/). This allows users to intuitively set their waypoint timings and instrument selections, and also modify their waypoint locations. +3. `virtualship run`: Executes the virtual expedition according to the planned configuration. This includes streaming data via the [Copernicus Marine Data Store](https://marine.copernicus.eu/), simulating the instrument beahviours and sampling, and saving the output in [`Zarr`](https://zarr.dev/) format. + +A full example workflow is outlined in the [Quickstart Guide](https://virtualship.readthedocs.io/en/latest/user-guide/quickstart.html) documentation. + +# Implementation + +Under the hood, `VirtualShip` is modular and extensible. The workflows are designed around `Instrument` base classes and instrument-specific subclasses and methods. This means the platform can be easily extended to add new instrument types. Instrument behaviours are coded as `Parcels` kernels, which allows for extensive customisability. For example, a `Drifter` advects passively with ocean currents, a `CTD` performs vertical profiling in the water column and an `ArgoFloat` cycles between ascent, descent and drift phases, all whilst sampling physical and/or biogeochemical fields at their respective locations and times. + +Moreover, the data ingestion system relies on Analysis-Ready and Cloud-Optimized data (ARCO; @Stern2022, @Abernathey2021) streamed directly from the Copernicus Marine Data Store, via the [`copernicusmarine`](https://github.com/mercator-ocean/copernicus-marine-toolbox) Python toolbox. This means users can simulate expeditions anywhere in the global ocean without downloading large datasets by default. Leveraging the suite of [physics and biogeochemical products](https://virtualship.readthedocs.io/en/latest/user-guide/documentation/copernicus_products.html) available on the Copernicus plaform, expeditions are possible from 1993 to present and forecasted two weeks into the future. There is also an [option](https://virtualship.readthedocs.io/en/latest/user-guide/documentation/pre_download_data.html) for the user to specify local `NetCDF` files for data ingestion, if preferred. + +# Applications and future outlook + +`VirtualShip` has already been extensvely applied in Master's teaching settings at Utrecht University as part of the [VirtualShip Classroom](https://www.uu.nl/en/research/sustainability/sustainable-ocean/education/virtual-ship) initiative. Educational assignments and tutorials have been developed alongside to integrate the tool into coursework, including projects where students design their own research question(s) and execute their fieldwork and analysis using `VirtualShip`. Its application has been shown to be successful, with students reporting increased self-efficacy and knowledge in executing oceanographic fieldwork [@Daniels2025]. + +The package opens space for many other research applications. It can support real-life expedition planning by letting users test sampling routes before going to sea. It also provides tooling to explore real-time adaptive strategies in which sampling plans shift as forecasts or observations update. The same workflow can also be used to investigate sampling efficiency, for example, examining how waypoint number or spacing shapes the ability to capture features of interest. Moreover, the software is well-suited for developing Observation System Simulation Experiments (OSSEs; e.g. @Errico2013) to test and optimise observational strategies in a cost- and time-efficient manner. + +Both the customisability of the `VirtualShip` platform and the exciting potential for new ARCO-based data hosting services in domains beyond oceanography (e.g., [atmospheric science](https://climate.copernicus.eu/work-progress-our-data-stores-turn-arco)) means there is potential to extend VirtualShip (or "VirtualShip-like" tools) to other domains in the future. Furthermore, as the `Parcels` underpinnings themselves continue to evolve, with a future (at time of writing) [v4.0 release](https://docs.oceanparcels.org/en/v4-dev/v4/) focusing on alignment with [Pangeo](https://pangeo.io/) standards and `Xarray` data structures [@Hoyer2017], `VirtualShip` will also benefit from these improvements, further enhancing its capabilities, extensibility and compatability with modern cloud-based data pipelines. + +# Acknowledgements + +The VirtualShip project is funded through the Utrecht University-NIOZ (Royal Netherlands Institute for Sea Research) collaboration. + +# References diff --git a/docs/paper/paper.pdf b/docs/paper/paper.pdf new file mode 100644 index 00000000..de1197cf Binary files /dev/null and b/docs/paper/paper.pdf differ diff --git a/docs/paper/plotting_functions.py b/docs/paper/plotting_functions.py new file mode 100644 index 00000000..a11ea798 --- /dev/null +++ b/docs/paper/plotting_functions.py @@ -0,0 +1,334 @@ +import cartopy.crs as ccrs +import cmocean.cm as cmo +import matplotlib.colors as mcolors +import matplotlib.patches as mpatches +import matplotlib.pyplot as plt +import numpy as np +import xarray as xr +from cartopy import feature as cfeature +from matplotlib.collections import LineCollection + +# ===================================================== +# plotting +# ===================================================== + + +# Drifters +def plot_drifters(drifter_ds, ax, vmin, vmax, PLOT_VARIABLE="temperature"): + """Plot drifter trajectories; cmap by temperature.""" + MARKERSIZE = 45.0 # for release location marking + PROJ = ccrs.PlateCarree() + LATLON_BUFFER = 1.0 # degrees (adjust this to 'zoom' in/out in the plot) + + for i, traj in enumerate(drifter_ds["trajectory"]): + # extract trajectory data + lons = drifter_ds["lon"][:].sel(trajectory=traj).squeeze().values + lats = drifter_ds["lat"][:].sel(trajectory=traj).squeeze().values + var = drifter_ds[PLOT_VARIABLE][:].sel(trajectory=traj).squeeze().values + + # segments for LineCollection + points = np.array([lons, lats]).T.reshape(-1, 1, 2) + segments = np.concatenate([points[:-1], points[1:]], axis=1) + + # coloured by temperature + lc = LineCollection( + segments, + cmap=cmo.thermal, + norm=mcolors.Normalize(vmin=vmin, vmax=vmax), + array=var[:-1], + linewidth=1.5, + zorder=3, + transform=PROJ, + ) + ax.add_collection(lc) + + # add release location + ax.scatter( + lons[0], + lats[0], + marker="o", + s=MARKERSIZE, + color="white", + edgecolor="black", + zorder=4, + transform=PROJ, + label="Waypoint" if i == 0 else None, # only label first for legend + ) + + # additional map features + ax.set_extent( + [ + drifter_ds.lon.min() - LATLON_BUFFER, + drifter_ds.lon.max() + LATLON_BUFFER, + drifter_ds.lat.min() - LATLON_BUFFER, + drifter_ds.lat.max() + LATLON_BUFFER, + ], + crs=PROJ, + ) + ax.coastlines(linewidth=0.5, color="black") + ax.add_feature(cfeature.LAND, facecolor="tan") + + gl = ax.gridlines( + draw_labels=True, + linewidth=0.5, + color="gainsboro", + alpha=1.0, + linestyle="-", + zorder=0, + ) + gl.top_labels = False + gl.right_labels = False + + # add colorbar + _add_cbar( + drifter_ds[PLOT_VARIABLE], cmo.thermal, ax, "Temperature (°C)", vmin, vmax + ) + + # legend + ax.legend(loc="best", fontsize=10) + + +# CTDs +def plot_ctd(ds, ax, plot_variable, vmin, vmax, axes_labels=False): + MAP_VARNAMES = {"temperature": "temperature", "oxygen": "o2"} + + MAP_CMAPS = { + "temperature": cmo.thermal, + "oxygen": cmo.oxy, + } + MAP_LABELS = { + "temperature": "Temperature (°C)", + "oxygen": "Oxygen (mmol m$^{-3}$)", + } + + ctd_distance = _ctd_distance_along_expedition(ds) + + # exract descent-only data + z_down = _ctd_descent_only(ctd_distance, "z") + d_down = _ctd_descent_only(ctd_distance, "distance") + var_down = _ctd_descent_only(ctd_distance, MAP_VARNAMES[plot_variable]) + + # 1d array of depth dimension (from deepest trajectory) + traj_idx, obs_idx = np.where(z_down == np.nanmin(z_down)) + z1d = z_down.values[traj_idx[0], :] + + # distance as 1d array + distance_1d = d_down.isel(obs=0) + + # regularised transect + profile_indices, distance_regular = _get_profile_indices(distance_1d) + var_masked = _build_masked_array(var_down, profile_indices, len(distance_regular)) + + # plot regularised transect + ax.grid( + True, which="both", color="lightgrey", linestyle="-", linewidth=0.7, alpha=0.5 + ) + + pm = ax.pcolormesh( + distance_regular / 1000, # distance in km + z1d, + var_masked.T, + cmap=MAP_CMAPS[plot_variable], + vmin=vmin, + vmax=vmax, + ) + + _add_cbar( + ds[MAP_VARNAMES[plot_variable]], + MAP_CMAPS[plot_variable], + ax, + MAP_LABELS[plot_variable], + shrink=1.00, + vmin=vmin, + vmax=vmax, + ) + + if axes_labels: + ax.set_ylabel("Depth (m)") + ax.set_xlabel("Distance from start (km)") + + return pm, distance_regular, var_masked + + +# ADCP +def plot_adcp(ds, ax, axes_labels=False): + """Absolute velocity plot.""" + CMAP = cmo.tempo + + distance_1d = _adcp_distance_along_expedition(ds.isel(trajectory=0)) + vel, _, _, _ = calc_velocities(ds) + landmask = xr.where(((ds["U"] == 0) & (ds["V"] == 0)), 1, np.nan) + + # adcp data + ax.pcolormesh( + distance_1d / 1000, + ds["z"], + vel, + cmap=CMAP, + ) + + # seabed + ax.pcolormesh( + distance_1d / 1000, # distance in km + ds["z"], + landmask, + cmap=mcolors.ListedColormap([mcolors.to_rgba("tan"), mcolors.to_rgba("white")]), + ) + + ax.set_xlim(0, distance_1d.max() / 1000) + + # legend for sea bed + tan_patch = mpatches.Patch(color=mcolors.to_rgba("tan"), label="Seabed") + ax.legend(handles=[tan_patch], loc="lower right") + + _add_cbar( + vel, + CMAP, + ax, + "Speed (m s$^{-1}$)", + vel.min(), + vel.max(), + shrink=1.00, + ) + + # axis labels + if axes_labels: + ax.set_ylabel("Depth (m)") + ax.set_xlabel("Distance from start (km)") + + +# ===================================================== +# utility +# ===================================================== + + +def _add_cbar( + da, + cmap, + ax, + label, + vmin, + vmax, + orientation="horizontal", + shrink=0.90, +): + sm = plt.cm.ScalarMappable(cmap=cmap, norm=mcolors.Normalize(vmin, vmax)) + sm._A = [] + plt.colorbar( + sm, + ax=ax, + orientation=orientation, + label=label, + shrink=shrink, + ) + + +def _haversine(lon1, lat1, lon2, lat2): + """Great-circle distance (meters) between two points.""" + lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) + dlon, dlat = lon2 - lon1, lat2 - lat1 + a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2 + c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) + return 6371000 * c + + +def _ctd_distance_along_expedition(ds): + """Add 'distance' variable: cumulative meters travelled.""" + # cumulative distance travelled along waypoints + + d = np.zeros_like(ds["lon"], dtype=float) + for ob in range(1, len(ds["lon"])): + d[ob] = d[ob - 1] + _haversine( + ds["lon"][ob - 1], ds["lat"][ob - 1], ds["lon"][ob], ds["lat"][ob] + ) + ds["distance"] = xr.DataArray( + d, + dims=ds["lon"].dims, + attrs={"long_name": "cumulative distance travelled", "units": "m"}, + ) + return ds + + +def _adcp_distance_along_expedition(ds): + """Array of cumulative meters travelled along ADCP waypoints.""" + d = np.zeros_like(ds["lon"], dtype=float) + for ob in range(1, len(ds["lon"])): + d[ob] = d[ob - 1] + _haversine( + ds["lon"][ob - 1], ds["lat"][ob - 1], ds["lon"][ob], ds["lat"][ob] + ) + return d + + +def _ctd_descent_only(ds, variable): + """Extract descending CTD data (downcast), pad with NaNs for alignment.""" + min_z_idx = ds["z"].argmin("obs") + da_clean = [] + for i, traj in enumerate(ds["trajectory"].values): + idx = min_z_idx.sel(trajectory=traj).item() + descent_vals = ds[variable][ + i, : idx + 1 + ] # take values from surface to min_z_idx (inclusive) + da_clean.append(descent_vals) + max_len = max(len(arr[~np.isnan(arr)]) for arr in da_clean) + da_padded = np.full((ds["trajectory"].size, max_len), np.nan) + for i, arr in enumerate(da_clean): + da_dropna = arr[~np.isnan(arr)] + da_padded[i, : len(da_dropna)] = da_dropna + return xr.DataArray( + da_padded, + dims=["trajectory", "obs"], + coords={"trajectory": ds["trajectory"], "obs": np.arange(max_len)}, + ) + + +def _build_masked_array(data_up, profile_indices, n_profiles): + arr = np.full((n_profiles, data_up.shape[1]), np.nan) + for i, idx in enumerate(profile_indices): + if idx is not None: + arr[i, :] = data_up.values[idx, :] + return arr + + +def _get_profile_indices(distance_1d): + """ + Returns regular distance bins and profile indices for CTD transect plotting. + + Bin size is set to one order of magnitude lower than max distance. + """ + dist_min, dist_max = float(distance_1d.min()), float(distance_1d.max()) + if dist_max > 1e6: + dist_step = 1.5e5 + elif dist_max > 1e5: + dist_step = 1.5e4 + elif dist_max > 1e4: + dist_step = 1.5e3 + else: + dist_step = 1.5e2 # fallback for very short transects + + distance_regular = np.arange(dist_min, dist_max + dist_step, dist_step) + threshold = dist_step / 2 + profile_indices = [ + np.argmin(np.abs(distance_1d.values - d)) + if np.min(np.abs(distance_1d.values - d)) < threshold + else None + for d in distance_regular + ] + return profile_indices, distance_regular + + +def calc_velocities(ds): + """From U and V, calculate absolute, parallel and perpendicular (to the ship trajectory) velocities, as well as (compass) direction of flow.""" + Uabs = np.sqrt(ds["U"] ** 2 + ds["V"] ** 2) + ds_surface = ds.isel(trajectory=0) + dlon = np.deg2rad(ds_surface["lon"].differentiate("obs")) + dlat = np.deg2rad(ds_surface["lat"].differentiate("obs")) + lat = np.deg2rad(ds_surface["lat"]) + alpha = np.arctan(dlat / (dlon * np.cos(lat))).mean("obs") # cruise direction angle + Uparallel = np.cos(alpha) * ds["U"] + np.sin(alpha) * ds["V"] + Uperp = -np.sin(alpha) * ds["U"] + np.cos(alpha) * ds["V"] + direction_rad = np.arctan2( + ds["U"], ds["V"] + ) # direction of flow [degrees from north] + direction_deg = (np.degrees(direction_rad) + 360) % 360 + + return Uabs, Uparallel, Uperp, direction_deg diff --git a/docs/paper/sample_expedition/CoordinatesExport-sample.xlsx b/docs/paper/sample_expedition/CoordinatesExport-sample.xlsx new file mode 100644 index 00000000..9196f07d Binary files /dev/null and b/docs/paper/sample_expedition/CoordinatesExport-sample.xlsx differ diff --git a/docs/paper/sample_expedition/MY_EXPEDITION/expedition.yaml b/docs/paper/sample_expedition/MY_EXPEDITION/expedition.yaml new file mode 100644 index 00000000..0e9e2270 --- /dev/null +++ b/docs/paper/sample_expedition/MY_EXPEDITION/expedition.yaml @@ -0,0 +1,148 @@ +instruments_config: + adcp_config: + max_depth_meter: -1000.0 + num_bins: 40 + period_minutes: 5.0 + argo_float_config: + cycle_days: 10.0 + drift_days: 9.0 + drift_depth_meter: -1000.0 + lifetime_minutes: 90720.0 + max_depth_meter: -2000.0 + min_depth_meter: 0.0 + stationkeeping_time_minutes: 20.0 + vertical_speed_meter_per_second: -0.1 + ctd_bgc_config: + max_depth_meter: -2000.0 + min_depth_meter: -11.0 + stationkeeping_time_minutes: 50.0 + ctd_config: + max_depth_meter: -2000.0 + min_depth_meter: -11.0 + stationkeeping_time_minutes: 50.0 + drifter_config: + depth_meter: -1.0 + lifetime_minutes: 129600.0 + stationkeeping_time_minutes: 20.0 + ship_underwater_st_config: null + xbt_config: + deceleration_coefficient: 0.00225 + fall_speed_meter_per_second: 6.7 + max_depth_meter: -285.0 + min_depth_meter: -2.0 +schedule: + waypoints: + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -30.007831 + longitude: 31.456983 + time: 2023-07-30 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -31.638883 + longitude: 30.870622 + time: 2023-07-31 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -33.318683 + longitude: 29.505758 + time: 2023-08-01 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -34.895537 + longitude: 27.046444 + time: 2023-08-02 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -35.940604 + longitude: 23.644859 + time: 2023-08-03 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -35.524463 + longitude: 19.905766 + time: 2023-08-04 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -34.2714 + longitude: 17.33693 + time: 2023-08-05 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -32.553781 + longitude: 14.83723 + time: 2023-08-06 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -30.469595 + longitude: 12.853821 + time: 2023-08-07 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -27.293171 + longitude: 11.269671 + time: 2023-08-08 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -24.474346 + longitude: 10.365769 + time: 2023-08-09 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -21.280331 + longitude: 9.349475 + time: 2023-08-10 08:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -17.493437 + longitude: 8.281086 + time: 2023-08-11 10:00:00 + - instrument: + - CTD + - CTD_BGC + - DRIFTER + location: + latitude: -14.646522 + longitude: 8.102 + time: 2023-08-12 10:00:00 +ship_config: + ship_speed_knots: 10.0 diff --git a/docs/paper/sample_expedition/expedition_overview.png b/docs/paper/sample_expedition/expedition_overview.png new file mode 100644 index 00000000..4fc49155 Binary files /dev/null and b/docs/paper/sample_expedition/expedition_overview.png differ diff --git a/src/virtualship/utils.py b/src/virtualship/utils.py index c8493d7f..c51646f4 100644 --- a/src/virtualship/utils.py +++ b/src/virtualship/utils.py @@ -420,8 +420,14 @@ def _get_bathy_data( ) else: # stream via Copernicus Marine Service + buffer = 0.1 # degrees buffer, always to 0.1 to ensure coverage in edge cases (bathy data grid resolution ~0.8 deg) + ds_bathymetry = copernicusmarine.open_dataset( dataset_id=BATHYMETRY_ID, + minimum_longitude=min_lon - buffer, + maximum_longitude=max_lon + buffer, + minimum_latitude=min_lat - buffer, + maximum_latitude=max_lat + buffer, variables=["deptho"], coordinates_selection_method="outside", )