From 8dcbf9af3069147c7d0e520ffcb72e9ad0a5c1f7 Mon Sep 17 00:00:00 2001 From: Erik van Sebille Date: Tue, 18 Aug 2026 11:21:53 +0200 Subject: [PATCH 1/4] Implement british-english spell checking of markdown and notebooks --- .github/workflows/ci.yml | 23 +++++++++++ .pre-commit-config.yaml | 7 ++++ .vale.ini | 5 +++ .vale/styles/Parcels/BritishEnglish.yml | 39 +++++++++++++++++++ docs/development/docsguide.md | 2 + .../examples/tutorial_Argofloats.ipynb | 4 +- .../examples/tutorial_delaystart.ipynb | 4 +- .../examples/tutorial_dt_integrators.ipynb | 2 +- .../examples/tutorial_interpolation.ipynb | 10 ++--- .../examples/tutorial_sampling.ipynb | 2 +- .../examples/tutorial_stuck_particles.ipynb | 12 +++--- .../examples/tutorial_unstuck_Agrid.ipynb | 12 +++--- .../getting_started/tutorial_output.ipynb | 4 +- .../getting_started/tutorial_quickstart.md | 4 +- pixi.toml | 2 + tools/lint_british_english.py | 38 ++++++++++++++++++ 16 files changed, 143 insertions(+), 27 deletions(-) create mode 100644 .vale.ini create mode 100644 .vale/styles/Parcels/BritishEnglish.yml create mode 100644 tools/lint_british_english.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccddf66fef..2817349718 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -332,6 +332,29 @@ jobs: with: name: Mypy report path: mypy-report + spell-check-tutorial-prose: + name: "Documentation: pixi run docs-spelling" + runs-on: ubuntu-latest + needs: [cache-pixi-lock] + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Restore cached pixi lockfile + uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0 + with: + cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }} + - uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6 + with: + pixi-version: ${{ needs.cache-pixi-lock.outputs.pixi-version }} + locked: false # TODO: Remove once v7 of the lock file is removed, or once we stop having external source dependencies https://github.com/Parcels-code/parcels/pull/2550#issuecomment-4088660238 + cache: true + cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }} + - name: Check tutorial prose + run: pixi run docs-spelling build-and-upload-nightly-parcels: # for alpha testing needs: [cache-pixi-lock] permissions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d180c2a8cd..1ea8869c67 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,3 +59,10 @@ repos: "--option", "align_comments=false", ] + - repo: local + hooks: + - id: vale-british-english + name: Vale British-English tutorial prose + entry: pixi run -e docs python tools/lint_british_english.py + language: system + files: ^docs/user_guide/(examples|getting_started)/tutorial.*\.(md|ipynb)$ diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000000..3b0671fe18 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,5 @@ +StylesPath = .vale/styles +MinAlertLevel = error + +[*] +BasedOnStyles = Parcels diff --git a/.vale/styles/Parcels/BritishEnglish.yml b/.vale/styles/Parcels/BritishEnglish.yml new file mode 100644 index 0000000000..257c3a21fb --- /dev/null +++ b/.vale/styles/Parcels/BritishEnglish.yml @@ -0,0 +1,39 @@ +extends: substitution +message: "Use British English: '%s' -> '%s'." +level: error +scope: text +ignorecase: true +swap: + analyze: analyse + analyzed: analysed + analyzing: analysing + behavior: behaviour + behaviors: behaviours + behavioral: behavioural + center: centre + centers: centres + centered: centred + centering: centring + color: colour + colors: colours + colored: coloured + coloring: colouring + customize: customise + customized: customised + customizing: customising + customization: customisation + labeled: labelled + labeling: labelling + modeler: modeller + modelers: modellers + modeling: modelling + normalize: normalise + normalized: normalised + normalizing: normalising + normalization: normalisation + optimize: optimise + optimized: optimised + optimizing: optimising + optimization: optimisation + organization: organisation + organizations: organisations diff --git a/docs/development/docsguide.md b/docs/development/docsguide.md index b48ad28444..ecbd029554 100644 --- a/docs/development/docsguide.md +++ b/docs/development/docsguide.md @@ -41,4 +41,6 @@ a table showing the latest notebook execution: by any developer or user, so the documentation teaches all of us how to do something with Parcels. Sometimes it can be more natural to take on the tone of a teacher, writing to a student/learner, in which case it is okay to use "you". Please refrain from using impersonal subjects such as "the user". +- **Use British English in tutorial prose.** The Vale check covers Markdown files and Markdown cells in tutorial + notebooks; preserve the spelling required by code, APIs, and quoted external text. - We recommend hard wrapping prose in markdown so that reading it becomes easier in any editor. diff --git a/docs/user_guide/examples/tutorial_Argofloats.ipynb b/docs/user_guide/examples/tutorial_Argofloats.ipynb index dfd4f6a268..728b377076 100644 --- a/docs/user_guide/examples/tutorial_Argofloats.ipynb +++ b/docs/user_guide/examples/tutorial_Argofloats.ipynb @@ -166,7 +166,7 @@ "source": [ "Now we can plot the trajectory of the Argo float with some simple calls to netCDF4 and matplotlib.\n", "\n", - "First plot the depth as a function of time, with the temperature as color (only on the upcast)." + "First plot the depth as a function of time, with the temperature as colour (only on the upcast)." ] }, { @@ -194,7 +194,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can also make a 3D plot of the trajectory colored by temperature." + "We can also make a 3D plot of the trajectory coloured by temperature." ] }, { diff --git a/docs/user_guide/examples/tutorial_delaystart.ipynb b/docs/user_guide/examples/tutorial_delaystart.ipynb index c404f425d5..e714a8305c 100644 --- a/docs/user_guide/examples/tutorial_delaystart.ipynb +++ b/docs/user_guide/examples/tutorial_delaystart.ipynb @@ -79,7 +79,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Defining the initial times of particles is done when the `ParticleSet` is defined. Although `time` and `z` are optional arguments (with FieldSet t=0 and z=0 as defaults), it is good practice to define them explicitly to ensure expected behavior. The simplest way to delay the start of a particle is to use the `time` argument for each particle.\n" + "Defining the initial times of particles is done when the `ParticleSet` is defined. Although `time` and `z` are optional arguments (with FieldSet t=0 and z=0 as defaults), it is good practice to define them explicitly to ensure expected behaviour. The simplest way to delay the start of a particle is to use the `time` argument for each particle." ] }, { @@ -363,7 +363,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, for some applications, this behavior may be undesirable; for example when particles need to be analyzed at a same age (instead of at a same time). In that case, we recommend either changing `outputdt` so that it is a common divisor of all start times; or doing multiple Parcels runs with subsets of the original `ParticleSet` (e.g., in the example above, one run with the Particles that start at `t=[0, 2]` and one with the Particle at `t=[1]`). In that case, you will get two files:\n" + "Now, for some applications, this behaviour may be undesirable; for example when particles need to be analysed at a same age (instead of at a same time). In that case, we recommend either changing `outputdt` so that it is a common divisor of all start times; or doing multiple Parcels runs with subsets of the original `ParticleSet` (e.g., in the example above, one run with the Particles that start at `t=[0, 2]` and one with the Particle at `t=[1]`). In that case, you will get two files:" ] }, { diff --git a/docs/user_guide/examples/tutorial_dt_integrators.ipynb b/docs/user_guide/examples/tutorial_dt_integrators.ipynb index 8dba6b1f9b..0bfdb9f03e 100644 --- a/docs/user_guide/examples/tutorial_dt_integrators.ipynb +++ b/docs/user_guide/examples/tutorial_dt_integrators.ipynb @@ -468,7 +468,7 @@ "id": "20", "metadata": {}, "source": [ - "We can see that in our simulation advecting particles for 7 days, the effect of `dt` on the precision of our simulation is approximately linear. The precision for a simulation with a timestep of 20 minutes is order of magnitude ~100 m. The effect on the time it takes to run a simulation is not linear in our case however; it increases sharply as we decrease our timestep. This may be optimized using more efficient chunking." + "We can see that in our simulation advecting particles for 7 days, the effect of `dt` on the precision of our simulation is approximately linear. The precision for a simulation with a timestep of 20 minutes is order of magnitude ~100 m. The effect on the time it takes to run a simulation is not linear in our case however; it increases sharply as we decrease our timestep. This may be optimised using more efficient chunking." ] }, { diff --git a/docs/user_guide/examples/tutorial_interpolation.ipynb b/docs/user_guide/examples/tutorial_interpolation.ipynb index d706a5109e..a5ac9088a9 100644 --- a/docs/user_guide/examples/tutorial_interpolation.ipynb +++ b/docs/user_guide/examples/tutorial_interpolation.ipynb @@ -148,7 +148,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "And then we can show each of the four interpolation methods, by plotting the interpolated values on the `Particles` locations (circles) on top of the `Field` values (background colors)\n" + "And then we can show each of the four interpolation methods, by plotting the interpolated values on the `Particles` locations (circles) on top of the `Field` values (background colours)" ] }, { @@ -255,10 +255,10 @@ "source": [ "## Interpolators on unstructured grids\n", "Parcels v4 supports the use of general circulation model output that is defined on unstructured grids. We include basic interpolators to help you get started, including\n", - "- `UxConstantFaceConstantZC` - this interpolator implements piecewise constant interpolation in both the lateral and vertical directions. It is appropriate for data that is registered to the face centers of the unstructured grid and centered on vertical layers.\n", - "- `UxLinearNodeConstantZC` - this interpolator implements barycentric interpolation in the lateral direction and piecewise constant in the vertical direction. It is appropriate for data that is registered to the corner vertices of the unstructured grid faces and centered on vertical layers.\n", + "- `UxConstantFaceConstantZC` - this interpolator implements piecewise constant interpolation in both the lateral and vertical directions. It is appropriate for data that is registered to the face centres of the unstructured grid and centred on vertical layers.\n", + "- `UxLinearNodeConstantZC` - this interpolator implements barycentric interpolation in the lateral direction and piecewise constant in the vertical direction. It is appropriate for data that is registered to the corner vertices of the unstructured grid faces and centred on vertical layers.\n", "- `UxLinearNodeLinearZF` - this interpolator implements barycentric interpolation in the lateral direction and piecewise linear interpolation in the vertical direction. It is appropriate for data that is registered to the corner vertices of the unstructured grid faces and on vertical layer interfaces.\n", - "- `UxConstantFaceLinearZF` - this interpolator implements piecewise constant interpolation in the lateral direction and piecewise linear interpolation in the vertical direction. It is appropriate for data that is registered to the face centers of the unstructured grid and centered on vertical layers\n", + "- `UxConstantFaceLinearZF` - this interpolator implements piecewise constant interpolation in the lateral direction and piecewise linear interpolation in the vertical direction. It is appropriate for data that is registered to the face centres of the unstructured grid and centred on vertical layers\n", "\n", "To get started, we use a very simple generated `UxArray.UxDataset` that is included with Parcels." ] @@ -466,7 +466,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In both plots the black lines show the edges that define the boundaries between the faces in the unstructured grid. For the node registered field, the background coloring is done using smooth shading based on the value of `T_node` at the corner nodes. For the face registered fields, each face is colored according to the value of `T_face` at the face center. The color of the particles is the value of the function that the particles take on via the corresponding interpolation method." + "In both plots the black lines show the edges that define the boundaries between the faces in the unstructured grid. For the node registered field, the background colouring is done using smooth shading based on the value of `T_node` at the corner nodes. For the face registered fields, each face is coloured according to the value of `T_face` at the face centre. The colour of the particles is the value of the function that the particles take on via the corresponding interpolation method." ] }, { diff --git a/docs/user_guide/examples/tutorial_sampling.ipynb b/docs/user_guide/examples/tutorial_sampling.ipynb index 53bbc82a56..a5f490f6a2 100644 --- a/docs/user_guide/examples/tutorial_sampling.ipynb +++ b/docs/user_guide/examples/tutorial_sampling.ipynb @@ -74,7 +74,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - " Ten particles are initialized at the surface in the center of our domain, at the initial time step." + " Ten particles are initialized at the surface in the centre of our domain, at the initial time step." ] }, { diff --git a/docs/user_guide/examples/tutorial_stuck_particles.ipynb b/docs/user_guide/examples/tutorial_stuck_particles.ipynb index 10711cdb8a..e6dd1717ec 100644 --- a/docs/user_guide/examples/tutorial_stuck_particles.ipynb +++ b/docs/user_guide/examples/tutorial_stuck_particles.ipynb @@ -88,11 +88,11 @@ "(a-grids)=\n", "## 1. A grids\n", "\n", - "Arakawa A grids are unstaggered grids where the velocities $u$, $v$ (and $w$), pressure and other tracers are defined at the same position (on so-called nodes). In numerical models, these nodes can be interpreted to be located **at the corner _or_ at the center of the grid cells**. This means that the cell boundaries, and therefore the solid-fluid boundaries can either be located at the nodes (**figure 1A**) or at 0.5 dx distance from the nodes (**figure 1B**) respectively.\n", + "Arakawa A grids are unstaggered grids where the velocities $u$, $v$ (and $w$), pressure and other tracers are defined at the same position (on so-called nodes). In numerical models, these nodes can be interpreted to be located **at the corner _or_ at the centre of the grid cells**. This means that the cell boundaries, and therefore the solid-fluid boundaries can either be located at the nodes (**figure 1A**) or at 0.5 dx distance from the nodes (**figure 1B**) respectively.\n", "\n", "Many ocean models natively run on a C grid, because boundary conditions are easier to implement there (see [C grid](#c-grids)). Sometimes, the C-grid output of these models is interpolated onto an A grid. This is the case for all(?) the data available from [Copernicus Marine Data Store](https://data.marine.copernicus.eu/products), which provide the data on a rectilinear A grid velocity field.\n", "\n", - "To visualize this, in **figure 1** we show the nodes and cells of a coastal region. The ocean cells and nodes are in red and the land cells and nodes are in white.\n" + "To visualize this, in **figure 1** we show the nodes and cells of a coastal region. The ocean cells and nodes are in red and the land cells and nodes are in white." ] }, { @@ -256,7 +256,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Figure 1** shows how _land_ grid points and boundaries can be interpreted, depending on whether you assume a grid node is at the center or corner of a cell. When they are at the corner of a cell, boundaries are interpreted as the edges between two nodes. This is especially visible in the upper center of the figures, where the white nodes with surrounding ocean can be assumed to only have a line of _land_ between them (**figure 1A**), or as entire _land_ cells (**figure 1B**).\n" + "**Figure 1** shows how _land_ grid points and boundaries can be interpreted, depending on whether you assume a grid node is at the centre or corner of a cell. When they are at the corner of a cell, boundaries are interpreted as the edges between two nodes. This is especially visible in the upper centre of the figures, where the white nodes with surrounding ocean can be assumed to only have a line of _land_ between them (**figure 1A**), or as entire _land_ cells (**figure 1B**)." ] }, { @@ -416,7 +416,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In **figure 2A** you can see how particles released in a 3x3 grid keep moving toward the boundary between two _land_ nodes. The ratio of the $u$ and $v$ components stays at a similar value due to both being linearly interpolated to zero at the boundary. Note that the interpretation of nodes at the center of grid cells (**figure 2B**) is clearly incompatible with Parcels interpolation.\n" + "In **figure 2A** you can see how particles released in a 3x3 grid keep moving toward the boundary between two _land_ nodes. The ratio of the $u$ and $v$ components stays at a similar value due to both being linearly interpolated to zero at the boundary. Note that the interpretation of nodes at the centre of grid cells (**figure 2B**) is clearly incompatible with Parcels interpolation." ] }, { @@ -449,9 +449,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "On staggered grids, different types of boundary conditions can be satisfied simultaneously. On a C-grid, the velocities are defined on the cell-edges normal to the velocity-component and pressure, temperature and tracers are defined at the cell centers. This way, a [Dirichlet boundary condition](https://en.wikipedia.org/wiki/Dirichlet_boundary_condition) can be used for the velocities, while a [Neumann boundary condition](https://en.wikipedia.org/wiki/Neumann_boundary_condition) can be satisfied for the gradient of pressure.\n", + "On staggered grids, different types of boundary conditions can be satisfied simultaneously. On a C-grid, the velocities are defined on the cell-edges normal to the velocity-component and pressure, temperature and tracers are defined at the cell centres. This way, a [Dirichlet boundary condition](https://en.wikipedia.org/wiki/Dirichlet_boundary_condition) can be used for the velocities, while a [Neumann boundary condition](https://en.wikipedia.org/wiki/Neumann_boundary_condition) can be satisfied for the gradient of pressure.\n", "\n", - "Here we investigate how Parcels interprets the the boundaries on a C grid. For background information, see [Delanmeter & Van Sebille (2019)](https://gmd.copernicus.org/articles/12/3571/2019/). First we show how the velocities are staggered and how the velocity input necessary to create a `FieldSet` results in the definition of boundaries in Parcels. This example uses a NEMO dataset but many of the relevant C grid assumptions are similar in other models, such as MITgcm.\n" + "Here we investigate how Parcels interprets the the boundaries on a C grid. For background information, see [Delanmeter & Van Sebille (2019)](https://gmd.copernicus.org/articles/12/3571/2019/). First we show how the velocities are staggered and how the velocity input necessary to create a `FieldSet` results in the definition of boundaries in Parcels. This example uses a NEMO dataset but many of the relevant C grid assumptions are similar in other models, such as MITgcm." ] }, { diff --git a/docs/user_guide/examples/tutorial_unstuck_Agrid.ipynb b/docs/user_guide/examples/tutorial_unstuck_Agrid.ipynb index 7b439f9b99..3a513b3623 100644 --- a/docs/user_guide/examples/tutorial_unstuck_Agrid.ipynb +++ b/docs/user_guide/examples/tutorial_unstuck_Agrid.ipynb @@ -398,7 +398,7 @@ "\n", "For the shore nodes that only have a diagonal component, we need to take into account the diagonal nodes also and project the vectors only onto the inside corners that border the ocean diagonally.\n", "\n", - "Then to make the vectors unitary, we normalize them by their magnitude.\n" + "Then to make the vectors unitary, we normalise them by their magnitude." ] }, { @@ -927,12 +927,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Consider a grid cell with a solid boundary to the right and vectors $(U0, V0)$ = $(1, 1)$ on the left-hand nodes, as in **figure 6**. \n", - "Parcels bilinear interpolation will interpolate in the $x$ and $y$ directions. This cell is invariant in the $y$-direction, we will only consider the effect in the direction normal to the boundary. In the x-direction, both u and v will be interpolated along $\\xi$, the normalized $x$-coordinate within the cell. This is plotted with the blue and orange dashed lines in **figure 6A**.\n", + "Consider a grid cell with a solid boundary to the right and vectors $(U0, V0)$ = $(1, 1)$ on the left-hand nodes, as in **figure 6**.\n", + "Parcels bilinear interpolation will interpolate in the $x$ and $y$ directions. This cell is invariant in the $y$-direction, we will only consider the effect in the direction normal to the boundary. In the x-direction, both u and v will be interpolated along $\\xi$, the normalised $x$-coordinate within the cell. This is plotted with the blue and orange dashed lines in **figure 6A**.\n", "\n", "A free slip boundary condition is defined with $\\frac{\\delta v}{\\delta \\xi}=0$. This means that the tangential velocity is constant in the direction normal to the boundary. This can be achieved in a kernel after interpolation by dividing by $(1-\\xi)$. The resulting velocity profiles are shown in **figure 6B**.\n", "\n", - "A partial slip boundary condition is defined with a tangential velocity profile that decreases toward the boundary, but not to zero. This can be achieved by multiplying the interpolated velocity by $\\frac{1-1/2\\xi}{1-\\xi}$. This is shown in **figure 6C**.\n" + "A partial slip boundary condition is defined with a tangential velocity profile that decreases toward the boundary, but not to zero. This can be achieved by multiplying the interpolated velocity by $\\frac{1-1/2\\xi}{1-\\xi}$. This is shown in **figure 6C**." ] }, { @@ -940,7 +940,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For each direction and boundary condition a different factor must be used (where $\\xi$ and $\\eta$ are the normalized x- and y-coordinates within the cell, respectively):\n", + "For each direction and boundary condition a different factor must be used (where $\\xi$ and $\\eta$ are the normalised x- and y-coordinates within the cell, respectively):\n", "\n", "- Free slip\n", "\n", @@ -960,7 +960,7 @@ "\n", " 4: $f_v = \\frac{1/2+1/2\\xi}{\\xi}$\n", "\n", - " 8: $f_v = \\frac{1-1/2\\xi}{1-\\xi}$\n" + " 8: $f_v = \\frac{1-1/2\\xi}{1-\\xi}$" ] }, { diff --git a/docs/user_guide/getting_started/tutorial_output.ipynb b/docs/user_guide/getting_started/tutorial_output.ipynb index 6bcb0ebf74..90511ae619 100644 --- a/docs/user_guide/getting_started/tutorial_output.ipynb +++ b/docs/user_guide/getting_started/tutorial_output.ipynb @@ -45,7 +45,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "First we need to create some Parcels output to analyze. We simulate a set of particles using the setup described in the [Delay start tutorial](https://docs.oceanparcels.org/en/latest/examples/tutorial_delaystart.html). We will also add some user defined metadata to the output file." + "First we need to create some Parcels output to analyse. We simulate a set of particles using the setup described in the [Delay start tutorial](https://docs.oceanparcels.org/en/latest/examples/tutorial_delaystart.html). We will also add some user defined metadata to the output file." ] }, { @@ -516,7 +516,7 @@ "metadata": { "celltoolbar": "Metagegevens bewerken", "kernelspec": { - "display_name": "Python 3", + "display_name": "Parcels:test (3.14.6)", "language": "python", "name": "python3" }, diff --git a/docs/user_guide/getting_started/tutorial_quickstart.md b/docs/user_guide/getting_started/tutorial_quickstart.md index 467063c5ac..6e5f7069e7 100644 --- a/docs/user_guide/getting_started/tutorial_quickstart.md +++ b/docs/user_guide/getting_started/tutorial_quickstart.md @@ -164,7 +164,7 @@ pset.execute( ## Read output -To start analyzing the trajectories computed by **Parcels**, we can open the `ParticleFile` using the `read_particlefile()` utility, which itself uses `polars`: +To start analysing the trajectories computed by **Parcels**, we can open the `ParticleFile` using the `read_particlefile()` utility, which itself uses `polars`: ```{code-cell} df = parcels.read_particlefile("output-quickstart.parquet") @@ -179,7 +179,7 @@ Let's verify that Parcels has computed the advection of the virtual particles! ```{code-cell} import matplotlib.pyplot as plt -# plot positions and color particles by time +# plot positions and colour particles by time scatter = plt.scatter(df['x'], df['y'], c=df['t']) plt.scatter(df['x'][:npart], df['y'][:npart], facecolors="none", edgecolors='r') # starting positions plt.scatter(lon, lat, facecolors="none", edgecolors='r') # starting positions diff --git a/pixi.toml b/pixi.toml index 4b699435b1..cb4211ae17 100644 --- a/pixi.toml +++ b/pixi.toml @@ -130,6 +130,7 @@ sphinx-autobuild = "*" sphinxcontrib-mermaid = "*" sphinx-design = "*" sphinx-autoapi = "*" +vale = "*" [feature.docs.tasks] docs-only-clean = { cmd = "make clean", cwd = "docs", description = "Cleans the documentation folder of build artifacts." } @@ -142,6 +143,7 @@ docs-watch = { cmd = "make livehtml", cwd = "docs", description = "Build and aut "docs-only-clean", ] } docs-linkcheck = { cmd = "make linkcheck", cwd = "docs", description = "Verify all links in documentation don't 404." } +docs-spelling = { cmd = "python tools/lint_british_english.py docs/user_guide/examples/tutorial*.ipynb docs/user_guide/examples/tutorial*.md docs/user_guide/getting_started/tutorial*.ipynb docs/user_guide/getting_started/tutorial*.md", description = "Check tutorial prose uses British English." } [feature.pre-commit.dependencies] pre_commit = "*" diff --git a/tools/lint_british_english.py b/tools/lint_british_english.py new file mode 100644 index 0000000000..ab13071673 --- /dev/null +++ b/tools/lint_british_english.py @@ -0,0 +1,38 @@ +"""Lint tutorial Markdown and notebook prose with Vale.""" + +import argparse +import json +import subprocess +import tempfile +from pathlib import Path + + +def extract_markdown(notebook_path: Path, output_path: Path) -> None: + notebook = json.loads(notebook_path.read_text()) + markdown_cells = ("".join(cell["source"]) for cell in notebook["cells"] if cell["cell_type"] == "markdown") + output_path.write_text("\n\n".join(markdown_cells)) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("paths", nargs="+", type=Path) + arguments = parser.parse_args() + + with tempfile.TemporaryDirectory() as temporary_directory: + temporary_path = Path(temporary_directory) + vale_paths = [] + for path in arguments.paths: + if path.suffix == ".ipynb": + extracted_path = temporary_path / path.with_suffix(".md").name + extract_markdown(path, extracted_path) + vale_paths.append(extracted_path) + else: + vale_paths.append(path) + + result = subprocess.run(["vale", *map(str, vale_paths)], check=False) + + return result.returncode + + +if __name__ == "__main__": + raise SystemExit(main()) From 82edccbc2a729ba32e49103ecdac611bc2b6464d Mon Sep 17 00:00:00 2001 From: Erik van Sebille Date: Tue, 18 Aug 2026 11:27:51 +0200 Subject: [PATCH 2/4] Restaging pre-commit spell check --- .pre-commit-config.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1587a0ec89..1ea8869c67 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,10 +59,10 @@ repos: "--option", "align_comments=false", ] - # - repo: local - # hooks: - # - id: vale-british-english - # name: Vale British-English tutorial prose - # entry: pixi run -e docs python tools/lint_british_english.py - # language: system - # files: ^docs/user_guide/(examples|getting_started)/tutorial.*\.(md|ipynb)$ + - repo: local + hooks: + - id: vale-british-english + name: Vale British-English tutorial prose + entry: pixi run -e docs python tools/lint_british_english.py + language: system + files: ^docs/user_guide/(examples|getting_started)/tutorial.*\.(md|ipynb)$ From 1a36a9a9cb3899d45dd39e347e8d7c8070fbb103 Mon Sep 17 00:00:00 2001 From: Erik van Sebille Date: Tue, 18 Aug 2026 11:29:26 +0200 Subject: [PATCH 3/4] Fixing spelling --- docs/user_guide/examples/tutorial_delaystart.ipynb | 4 ++-- docs/user_guide/getting_started/tutorial_output.ipynb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/user_guide/examples/tutorial_delaystart.ipynb b/docs/user_guide/examples/tutorial_delaystart.ipynb index 5568bc3613..130751dc5f 100644 --- a/docs/user_guide/examples/tutorial_delaystart.ipynb +++ b/docs/user_guide/examples/tutorial_delaystart.ipynb @@ -79,7 +79,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Defining the initial times of particles is done when the {py:obj}`parcels.ParticleSet` is defined. Although `t` and `z` are optional arguments (with FieldSet t=0 and z=0 as defaults), it is good practice to define them explicitly to ensure expected behavior. The simplest way to delay the start of a particle is to use the `t` argument for each particle." + "Defining the initial times of particles is done when the {py:obj}`parcels.ParticleSet` is defined. Although `t` and `z` are optional arguments (with FieldSet t=0 and z=0 as defaults), it is good practice to define them explicitly to ensure expected behaviour. The simplest way to delay the start of a particle is to use the `t` argument for each particle." ] }, { @@ -363,7 +363,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, for some applications, this behavior may be undesirable; for example when particles need to be analyzed at a same age (instead of at a same time). In that case, we recommend either changing `outputdt` so that it is a common divisor of all start times; or by [writing directly in the kernel](./tutorial_write_in_kernel.ipynb); or by doing multiple Parcels runs with subsets of the original `ParticleSet` (e.g., in the example above, one run with the Particles that start at `t=[0, 2]` and one with the Particle at `t=[1]`). In that case, you will get two files:\n" + "Now, for some applications, this behaviour may be undesirable; for example when particles need to be analysed at a same age (instead of at a same time). In that case, we recommend either changing `outputdt` so that it is a common divisor of all start times; or by [writing directly in the kernel](./tutorial_write_in_kernel.ipynb); or by doing multiple Parcels runs with subsets of the original `ParticleSet` (e.g., in the example above, one run with the Particles that start at `t=[0, 2]` and one with the Particle at `t=[1]`). In that case, you will get two files:\n" ] }, { diff --git a/docs/user_guide/getting_started/tutorial_output.ipynb b/docs/user_guide/getting_started/tutorial_output.ipynb index 7c4775f05b..4dfffb0825 100644 --- a/docs/user_guide/getting_started/tutorial_output.ipynb +++ b/docs/user_guide/getting_started/tutorial_output.ipynb @@ -45,7 +45,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "First we need to create some Parcels output to analyze. We simulate a set of particles using the setup described in the [Delay start tutorial](../examples/tutorial_delaystart.ipynb). We will also add some user defined metadata to the output file." + "First we need to create some Parcels output to analyse. We simulate a set of particles using the setup described in the [Delay start tutorial](../examples/tutorial_delaystart.ipynb). We will also add some user defined metadata to the output file." ] }, { From 01ae993416b3d1540188417148909b86e86810d3 Mon Sep 17 00:00:00 2001 From: Erik van Sebille Date: Tue, 18 Aug 2026 11:38:16 +0200 Subject: [PATCH 4/4] Fixing pre-commit hook to not use pixi --- .pre-commit-config.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ea8869c67..831606f69f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,10 +59,9 @@ repos: "--option", "align_comments=false", ] - - repo: local + - repo: https://github.com/vale-cli/vale + rev: v3.17.1 hooks: - - id: vale-british-english + - id: vale name: Vale British-English tutorial prose - entry: pixi run -e docs python tools/lint_british_english.py - language: system - files: ^docs/user_guide/(examples|getting_started)/tutorial.*\.(md|ipynb)$ + files: ^docs/user_guide/(examples|getting_started)/tutorial.*\.md$