<jupyter_start><jupyter_text>Tutorial on how to use Parcels on NEMO curvilinear grids Parcels also supports [curvilinear grids](https://www.nemo-ocean.eu/doc/node108.html) such as those used in the [NEMO models](https://www.nemo-ocean.eu/).We will be using the example data in the `NemoCurvilinear_data/` directory. These fields are a purely zonal flow on an aqua-planet (so zonal-velocity is 1 m/s and meridional-velocity is 0 m/s everywhere, and no land). However, because of the curvilinear grid, the `U` and `V` fields vary north of 20N.<jupyter_code>from parcels import FieldSet, ParticleSet, JITParticle, ParticleFile, plotTrajectoriesFile
from parcels import AdvectionRK4
import numpy as np
from datetime import timedelta as delta
%matplotlib inline<jupyter_output>INFO: Compiled ParcelsRandom ==> C:\Users\GEBRUI~1\AppData\Local\Temp\parcels-tmp\libparcels_random_2dd98a77-1098-4d48-a870-a9bfe10931aa.so<jupyter_text>We can create a `FieldSet` just like we do for normal grids.Note that NEMO is discretised on a C-grid. U and V velocities are not located on the same nodes (see https://www.nemo-ocean.eu/doc/node19.html ).``` __V1__|      |U0     U1|__V0__|```To interpolate U, V velocities on the C-grid, Parcels needs to read the f-nodes, which are located on the corners of the cells (for indexing details: https://www.nemo-ocean.eu/doc/img360.png ).<jupyter_code>data_path = 'NemoCurvilinear_data/'
filenames = {'U': {'lon': data_path + 'mesh_mask.nc4',
                   'lat': data_path + 'mesh_mask.nc4',
                   'data': data_path + 'U_purely_zonal-ORCA025_grid_U.nc4'},
             'V': {'lon': data_path + 'mesh_mask.nc4',
                   'lat': data_path + 'mesh_mask.nc4',
                   'data': data_path + 'V_purely_zonal-ORCA025_grid_V.nc4'}}
variables = {'U': 'U',
             'V': 'V'}
dimensions = {'lon': 'glamf', 'lat': 'gphif', 'time': 'time_counter'}
field_set = FieldSet.from_nemo(filenames, variables, dimensions, allow_time_extrapolation=True)<jupyter_output>WARNING: Casting lon data to np.float32
WARNING: Casting lat data to np.float32
WARNING: Casting depth data to np.float32<jupyter_text>And we can plot the `U` field.<jupyter_code>field_set.U.show()<jupyter_output>WARNING: Field.show() does not always correctly determine the domain for curvilinear grids. Use plotting with caution and perhaps use domain argument as in the NEMO 3D tutorial<jupyter_text>As you see above, the `U` field indeed is 1 m/s south of 20N, but varies with longitude and latitude north of thatNow we can run particles as normal. Parcels will take care to rotate the `U` and `V` fields<jupyter_code># Start 20 particles on a meridional line at 180W
npart = 20
lonp = -180 * np.ones(npart)
latp = [i for i in np.linspace(-70, 88, npart)]

# Create a periodic boundary condition kernel
def periodicBC(particle, fieldset, time):
    if particle.lon > 180:
        particle.lon -= 360

pset = ParticleSet.from_list(field_set, JITParticle, lon=lonp, lat=latp)
pfile = ParticleFile("nemo_particles", pset, outputdt=delta(days=1))
kernels = pset.Kernel(AdvectionRK4) + periodicBC
pset.execute(kernels, runtime=delta(days=50), dt=delta(hours=6),
             output_file=pfile)<jupyter_output>INFO: Compiled JITParticleAdvectionRK4periodicBC ==> C:\Users\GEBRUI~1\AppData\Local\Temp\parcels-tmp\f2f5255b84aa2e4798dac710ca472c26_0.dll<jupyter_text>And then we can plot these trajectories. As expected, all trajectories go exactly zonal and due to the curvature of the earth, ones at higher latitude move more degrees eastward (even though the distance in km is equal for all particles)<jupyter_code>pfile.export()  # export the trajectory data to a netcdf file
plotTrajectoriesFile("nemo_particles.nc");<jupyter_output><empty_output><jupyter_text>Speeding up `ParticleSet` initialisation by efficiently finding particle start-locations on the `Grid`On a Curvilinear grid, determining the location of each `Particle` on the grid is more complicated and therefore takes longer than on a Rectilinear grid. Since Parcels version 2.2.2, a function is available on the `ParticleSet` class, that speeds up the look-up. After creating the `ParticleSet`, but before running the `ParticleSet.execute()`, simply call the function `ParticleSet.populate_indices()`. Note that this only works if you have the [`pykdtree`](https://anaconda.org/conda-forge/pykdtree) package installed, which is only included in the Parcels dependencies in version >= 2.2.2<jupyter_code>pset = ParticleSet.from_list(field_set, JITParticle, lon=lonp, lat=latp)
pset.populate_indices()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09834997828918801, 'fraction_numerical': 0.024967433782023447, 'mean_word_length': 4.959896507115136, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 5, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '490195', 'license': 'MIT', 'n_tokens_mistral': 1609, 'n_tokens_neox': 1489, 'n_words': 485, 'path': 'parcels/examples/tutorial_nemo_curvilinear.ipynb', 'repo_name': 'axnsantana/parcels', 'size': '4606'}