Skip to content

Add particlefile_to_v3_zarr() helper - #2811

Open
VeckoTheGecko wants to merge 10 commits into
Parcels-code:mainfrom
VeckoTheGecko:push-unusxmtrrptn
Open

Add particlefile_to_v3_zarr() helper#2811
VeckoTheGecko wants to merge 10 commits into
Parcels-code:mainfrom
VeckoTheGecko:push-unusxmtrrptn

Conversation

@VeckoTheGecko

Copy link
Copy Markdown
Contributor

Description

Adds particlefile_to_v3_zarr helper, tests, and strategies for property based testing.

Note that this function is eager. This is noted in the docstring for the function.

Checklist

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested any AI-generated content in my PR.
    • I take responsibility for any AI-generated content in my PR.
    • Describe how you used it: Familiarity with polars API.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that this would be useful for the future as well

Comment thread src/parcels/_v3.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about the name of this file (also on the name of test_v3.py)

@erikvansebille erikvansebille Aug 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about compatibility_v3.py? Or compat_v3.py? Or `regression_v3.py?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compat_v3 sounds good

Comment thread src/parcels/_v3.py
df = pl.read_parquet(from_parquet)
table = pq.read_table(from_parquet)

# TODO: Check for available memory here and fail as a safeguard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentionally left here. I think it would be good to discuss (or leave for a future PR)

Comment thread src/parcels/_v3.py Outdated
return ret


def particlefile_to_v3_zarr(from_parquet: str | os.PathLike, to_zarr: str | os.PathLike) -> None:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this API (i.e., from file-> file) is significantly more stable and robust than trying to take in/return in-memory objects like dataframes or Xarray Datasets.

Comment thread tests/test_v3.py Outdated
Comment on lines +12 to +15
for var in ["lat", "lon", "depth", "time"]:
assert var in ds.variables

assert set(ds.dims) == {"obs", "trajectory"}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's it in terms of checks that we need to do? Do you have anything else in mind @erikvansebille ?

Comment thread tests/test_v3.py Outdated
Comment on lines +19 to +20
def test_particlefile_to_v3_zarr(df):
with tempfile.TemporaryDirectory() as tmpdir:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't do

@given(df=pst.particlefile_output())
def test_particlefile_to_v3_zarr(df, tmp_zarr, tmp_parquet)

since hypothesis doesn't allow mixing of strategies and Pytest fixtures

Comment thread src/parcels/_v3.py
Comment on lines +43 to +48
Notes
-----
This is not a lazy operation — the entire parquet file is read into memory
and pivoted before writing to zarr. For large particle files this may
require significant memory. Performance improvements are welcome via PRs.
"""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes

This is not a lazy operation — the entire parquet file is read into memory
and pivoted before writing to zarr. For large particle files this may
require significant memory. Performance improvements are welcome via PRs.

I think that this sort of functionality (i.e., pivoting dataframes to zarr CF convention output) can live outside of Parcels since its also relevant to other tools (e..g, TRACMASS).

@oj-tooth have you guys done any work around pivoting (larger than in-memory) tabular data into Lagrangian Zarr datasets?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe its something that we can collab on across-teams

@erikvansebille erikvansebille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good; a few comments/questions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also remove the TODO about the conversion function higher up in the notebook?

And remove "do" from the "We do also have some tooling ..."?

And filter out the ZarrUserWarning: Consolidated metadata is currently not part in the Zarr format 3 specification. It may not be supported by other zarr implementations and may change in the future.?

Comment thread src/parcels/_strategies/_core.py Outdated


def _generate_dummy_data(particle: ParticleClass, nparticles=10, nobs=10) -> pd.DataFrame:
"""Build a pandera DataFrameSchema from a ParticleClass.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type-o? Auto-correct? Or intentional?

Suggested change
"""Build a pandera DataFrameSchema from a ParticleClass.
"""Build a pandas DataFrameSchema from a ParticleClass.

@VeckoTheGecko VeckoTheGecko Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was left over from a different implementation I was exploring (using Panderas hypothesis strategies), though it wasn't as flexible as I was needing.

Cleaned up now

Comment thread src/parcels/_strategies/_core.py Outdated

Only variables with ``to_write=True`` are included in the schema.
Each column is typed with the variable's numpy dtype and carries the
variable's ``attrs`` as pandera column-level metadata.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
variable's ``attrs`` as pandera column-level metadata.
variable's ``attrs`` as pandas column-level metadata.

Comment thread src/parcels/__init__.py
from parcels._xarray import open_raw_zarr
from parcels._core.particleset import ParticleSet
from parcels._core.particlefile import ParticleFile, read_particlefile
from ._v3 import particlefile_to_v3_zarr

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we at the risk of confusing users which 'v3' we mean here? Zarr v3? Parcels v3? Both? Should we clarify?

Comment thread src/parcels/_v3.py

Notes
-----
This is not a lazy operation — the entire parquet file is read into memory

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a relatively simple improvement be to do the loading/pivoting/writing on a per-variable basis? So that only one variable is kept in memory at a time? Or is this not worth the extra code/effort for now?

Comment thread src/parcels/_v3.py Outdated

Reads the parquet file, renames columns to v3 conventions
(``particle_id`` -> ``trajectory``, ``t`` -> ``time``, ``x`` -> ``lon``,
``y`` -> ``lat``, ``z`` -> ``depth``), and reshapes the data into a 2D

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the output was already z in v3

Suggested change
``y`` -> ``lat``, ``z`` -> ``depth``), and reshapes the data into a 2D
``y`` -> ``lat``), and reshapes the data into a 2D

Comment thread src/parcels/_v3.py Outdated
# TODO: Check for available memory here and fail as a safeguard?

# Rename columns to v3 conventions
rename_map = {"particle_id": "trajectory", "t": "time", "x": "lon", "y": "lat", "z": "depth"}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rename_map = {"particle_id": "trajectory", "t": "time", "x": "lon", "y": "lat", "z": "depth"}
rename_map = {"particle_id": "trajectory", "t": "time", "x": "lon", "y": "lat"}

Comment thread src/parcels/_v3.py
Comment thread src/parcels/_v3.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to implement the particlefile_to_v3_zarr() in #2812 (so that I could much easier compare the old v3 and the new v4 output), but ran into an issue when ow all trajectories have the same length (e.g. because some particles are deleted). This leads to a ragged pivot table and then all NaNs in the zarr file. Could you explore a fix, @VeckoTheGecko? Ragged output is quite normal in Parcels

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parquet to CF-trajectory compliant Zarr conversion tooling

2 participants