66
77from parcels ._datasets .structured .generic import simple_UV_dataset
88from parcels .application_kernels import AdvectionDiffusionEM , AdvectionDiffusionM1 , DiffusionUniformKh
9+ from parcels .application_kernels .interpolation import XBiLinear
910from parcels .field import Field , VectorField
1011from parcels .fieldset import FieldSet
12+ from parcels .particle import Particle , Variable
1113from parcels .particleset import ParticleSet
12- from parcels .xgrid import _XGRID_AXES , XGrid
13-
14-
15- def BiLinear ( # TODO move to interpolation file
16- field : Field ,
17- ti : int ,
18- position : dict [_XGRID_AXES , tuple [int , float | np .ndarray ]],
19- tau : np .float32 | np .float64 ,
20- t : np .float32 | np .float64 ,
21- z : np .float32 | np .float64 ,
22- y : np .float32 | np .float64 ,
23- x : np .float32 | np .float64 ,
24- ):
25- """Bilinear interpolation on a regular grid."""
26- xi , xsi = position ["X" ]
27- yi , eta = position ["Y" ]
28- zi , zeta = position ["Z" ]
29-
30- data = field .data .data [:, zi , yi : yi + 2 , xi : xi + 2 ]
31- data = (1 - tau ) * data [ti , :, :] + tau * data [ti + 1 , :, :]
32-
33- return (
34- (1 - xsi ) * (1 - eta ) * data [0 , 0 ]
35- + xsi * (1 - eta ) * data [0 , 1 ]
36- + xsi * eta * data [1 , 1 ]
37- + (1 - xsi ) * eta * data [1 , 0 ]
38- )
14+ from parcels .xgrid import XGrid
15+ from tests .utils import create_fieldset_zeros_conversion
3916
4017
4118@pytest .mark .parametrize ("mesh_type" , ["spherical" , "flat" ])
@@ -48,12 +25,12 @@ def test_fieldKh_Brownian(mesh_type):
4825 ds ["lon" ].data = np .array ([- 1e6 , 1e6 ])
4926 ds ["lat" ].data = np .array ([- 1e6 , 1e6 ])
5027 grid = XGrid .from_dataset (ds )
51- U = Field ("U" , ds ["U" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
52- V = Field ("V" , ds ["V" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
28+ U = Field ("U" , ds ["U" ], grid , mesh_type = mesh_type , interp_method = XBiLinear )
29+ V = Field ("V" , ds ["V" ], grid , mesh_type = mesh_type , interp_method = XBiLinear )
5330 ds ["Kh_zonal" ] = (["time" , "depth" , "YG" , "XG" ], np .full ((2 , 1 , 2 , 2 ), kh_zonal ))
5431 ds ["Kh_meridional" ] = (["time" , "depth" , "YG" , "XG" ], np .full ((2 , 1 , 2 , 2 ), kh_meridional ))
55- Kh_zonal = Field ("Kh_zonal" , ds ["Kh_zonal" ], grid = grid , mesh_type = mesh_type , interp_method = BiLinear )
56- Kh_meridional = Field ("Kh_meridional" , ds ["Kh_meridional" ], grid = grid , mesh_type = mesh_type , interp_method = BiLinear )
32+ Kh_zonal = Field ("Kh_zonal" , ds ["Kh_zonal" ], grid = grid , mesh_type = mesh_type , interp_method = XBiLinear )
33+ Kh_meridional = Field ("Kh_meridional" , ds ["Kh_meridional" ], grid = grid , mesh_type = mesh_type , interp_method = XBiLinear )
5734 UV = VectorField ("UV" , U , V )
5835 fieldset = FieldSet ([U , V , UV , Kh_zonal , Kh_meridional ])
5936
@@ -85,17 +62,17 @@ def test_fieldKh_SpatiallyVaryingDiffusion(mesh_type, kernel):
8562 ds ["lon" ].data = np .linspace (- 1e6 , 1e6 , xdim )
8663 ds ["lat" ].data = np .linspace (- 1e6 , 1e6 , ydim )
8764 grid = XGrid .from_dataset (ds )
88- U = Field ("U" , ds ["U" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
89- V = Field ("V" , ds ["V" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
65+ U = Field ("U" , ds ["U" ], grid , mesh_type = mesh_type , interp_method = XBiLinear )
66+ V = Field ("V" , ds ["V" ], grid , mesh_type = mesh_type , interp_method = XBiLinear )
9067
9168 Kh = np .zeros ((ydim , xdim ), dtype = np .float32 )
9269 for x in range (xdim ):
9370 Kh [:, x ] = np .tanh (ds ["lon" ][x ] / ds ["lon" ][- 1 ] * 10.0 ) * xdim / 2.0 + xdim / 2.0 + 100.0
9471
9572 ds ["Kh_zonal" ] = (["time" , "depth" , "YG" , "XG" ], np .full ((2 , 1 , ydim , xdim ), Kh ))
9673 ds ["Kh_meridional" ] = (["time" , "depth" , "YG" , "XG" ], np .full ((2 , 1 , ydim , xdim ), Kh ))
97- Kh_zonal = Field ("Kh_zonal" , ds ["Kh_zonal" ], grid = grid , mesh_type = mesh_type , interp_method = BiLinear )
98- Kh_meridional = Field ("Kh_meridional" , ds ["Kh_meridional" ], grid = grid , mesh_type = mesh_type , interp_method = BiLinear )
74+ Kh_zonal = Field ("Kh_zonal" , ds ["Kh_zonal" ], grid = grid , mesh_type = mesh_type , interp_method = XBiLinear )
75+ Kh_meridional = Field ("Kh_meridional" , ds ["Kh_meridional" ], grid = grid , mesh_type = mesh_type , interp_method = XBiLinear )
9976 UV = VectorField ("UV" , U , V )
10077 fieldset = FieldSet ([U , V , UV , Kh_zonal , Kh_meridional ])
10178 fieldset .add_constant ("dres" , ds ["lon" ][1 ] - ds ["lon" ][0 ])
@@ -110,3 +87,58 @@ def test_fieldKh_SpatiallyVaryingDiffusion(mesh_type, kernel):
11087 assert np .allclose (np .mean (pset .lon ), 0 , atol = tol )
11188 assert np .allclose (np .mean (pset .lat ), 0 , atol = tol )
11289 assert stats .skew (pset .lon ) > stats .skew (pset .lat )
90+
91+
92+ @pytest .mark .parametrize ("lambd" , [1 , 5 ])
93+ def test_randomexponential (lambd ):
94+ fieldset = create_fieldset_zeros_conversion ()
95+ npart = 1000
96+
97+ # Rate parameter for random.expovariate
98+ fieldset .lambd = lambd
99+
100+ # Set random seed
101+ random .seed (1234 )
102+
103+ pset = ParticleSet (fieldset = fieldset , lon = np .zeros (npart ), lat = np .zeros (npart ), depth = np .zeros (npart ))
104+
105+ def vertical_randomexponential (particle , fieldset , time ): # pragma: no cover
106+ # Kernel for random exponential variable in depth direction
107+ particle .depth = random .expovariate (fieldset .lambd )
108+
109+ pset .execute (vertical_randomexponential , runtime = np .timedelta64 (1 , "s" ), dt = np .timedelta64 (1 , "s" ))
110+
111+ expected_mean = 1.0 / fieldset .lambd
112+ assert np .allclose (np .mean (pset .depth ), expected_mean , rtol = 0.1 )
113+
114+
115+ @pytest .mark .parametrize ("mu" , [0.8 * np .pi , np .pi ])
116+ @pytest .mark .parametrize ("kappa" , [2 , 4 ])
117+ def test_randomvonmises (mu , kappa ):
118+ npart = 10000
119+ fieldset = create_fieldset_zeros_conversion ()
120+
121+ # Parameters for random.vonmisesvariate
122+ fieldset .mu = mu
123+ fieldset .kappa = kappa
124+
125+ # Set random seed
126+ random .seed (1234 )
127+
128+ AngleParticle = Particle .add_variable (Variable ("angle" ))
129+ pset = ParticleSet (
130+ fieldset = fieldset , pclass = AngleParticle , lon = np .zeros (npart ), lat = np .zeros (npart ), depth = np .zeros (npart )
131+ )
132+
133+ def vonmises (particle , fieldset , time ): # pragma: no cover
134+ particle .angle = random .vonmisesvariate (fieldset .mu , fieldset .kappa )
135+
136+ pset .execute (vonmises , runtime = np .timedelta64 (1 , "s" ), dt = np .timedelta64 (1 , "s" ))
137+
138+ angles = np .array ([p .angle for p in pset ])
139+
140+ assert np .allclose (np .mean (angles ), mu , atol = 0.1 )
141+ vonmises_mean = stats .vonmises .mean (kappa = kappa , loc = mu )
142+ assert np .allclose (np .mean (angles ), vonmises_mean , atol = 0.1 )
143+ vonmises_var = stats .vonmises .var (kappa = kappa , loc = mu )
144+ assert np .allclose (np .var (angles ), vonmises_var , atol = 0.1 )
0 commit comments