@@ -37,6 +37,39 @@ def BiLinear( # TODO move to interpolation file
3737 )
3838
3939
40+ def BiLinearPeriodic ( # TODO move to interpolation file
41+ field : Field ,
42+ ti : int ,
43+ position : dict [_XGRID_AXES , tuple [int , float | np .ndarray ]],
44+ tau : np .float32 | np .float64 ,
45+ t : np .float32 | np .float64 ,
46+ z : np .float32 | np .float64 ,
47+ y : np .float32 | np .float64 ,
48+ x : np .float32 | np .float64 ,
49+ ):
50+ """Bilinear interpolation on a regular grid with periodic boundary conditions in horizontal directions."""
51+ xi , xsi = position ["X" ]
52+ yi , eta = position ["Y" ]
53+ zi , zeta = position ["Z" ]
54+
55+ if xi < 0 :
56+ xi = 0
57+ xsi = (x - field .grid .lon [xi ]) / (field .grid .lon [xi + 1 ] - field .grid .lon [xi ])
58+ if yi < 0 :
59+ yi = 0
60+ eta = (y - field .grid .lat [yi ]) / (field .grid .lat [yi + 1 ] - field .grid .lat [yi ])
61+
62+ data = field .data .data [:, zi , yi : yi + 2 , xi : xi + 2 ]
63+ data = (1 - tau ) * data [ti , :, :] + tau * data [ti + 1 , :, :]
64+
65+ return (
66+ (1 - xsi ) * (1 - eta ) * data [0 , 0 ]
67+ + xsi * (1 - eta ) * data [0 , 1 ]
68+ + xsi * eta * data [1 , 1 ]
69+ + (1 - xsi ) * eta * data [1 , 0 ]
70+ )
71+
72+
4073def TriLinear ( # TODO move to interpolation file
4174 field : Field ,
4275 ti : int ,
@@ -84,15 +117,40 @@ def test_advection_zonal(mesh_type, npart=10):
84117 U = Field ("U" , ds ["U" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
85118 V = Field ("V" , ds ["V" ], grid , mesh_type = mesh_type , interp_method = BiLinear )
86119 UV = VectorField ("UV" , U , V )
87- fieldset2D = FieldSet ([U , V , UV ])
120+ fieldset = FieldSet ([U , V , UV ])
88121
89- pset2D = ParticleSet (fieldset2D , lon = np .zeros (npart ) + 20.0 , lat = np .linspace (0 , 80 , npart ))
90- pset2D .execute (AdvectionRK4 , runtime = np .timedelta64 (2 , "h" ), dt = np .timedelta64 (15 , "m" ))
122+ pset = ParticleSet (fieldset , lon = np .zeros (npart ) + 20.0 , lat = np .linspace (0 , 80 , npart ))
123+ pset .execute (AdvectionRK4 , runtime = np .timedelta64 (2 , "h" ), dt = np .timedelta64 (15 , "m" ))
91124
92125 if mesh_type == "spherical" :
93- assert (np .diff (pset2D .lon ) > 1.0e-4 ).all ()
126+ assert (np .diff (pset .lon ) > 1.0e-4 ).all ()
94127 else :
95- assert (np .diff (pset2D .lon ) < 1.0e-4 ).all ()
128+ assert (np .diff (pset .lon ) < 1.0e-4 ).all ()
129+
130+
131+ def periodicBC (particle , fieldset , time ):
132+ particle .total_dlon += particle_dlon # noqa
133+ particle .lon = np .fmod (particle .lon , fieldset .U .grid .lon [- 1 ])
134+ particle .lat = np .fmod (particle .lat , fieldset .U .grid .lat [- 1 ])
135+
136+
137+ def test_advection_zonal_periodic ():
138+ ds = simple_UV_dataset (dims = (2 , 2 , 2 , 2 ), mesh_type = "flat" )
139+ ds ["U" ].data [:] = 0.1
140+ ds ["lon" ].data = np .array ([0 , 2 ])
141+ ds ["lat" ].data = np .array ([0 , 2 ])
142+
143+ grid = XGrid .from_dataset (ds )
144+ U = Field ("U" , ds ["U" ], grid , interp_method = BiLinearPeriodic )
145+ V = Field ("V" , ds ["V" ], grid , interp_method = BiLinearPeriodic )
146+ UV = VectorField ("UV" , U , V )
147+ fieldset = FieldSet ([U , V , UV ])
148+
149+ PeriodicParticle = Particle .add_variable (Variable ("total_dlon" , initial = 0 ))
150+ pset = ParticleSet (fieldset , pclass = PeriodicParticle , lon = [0.5 ], lat = [0.5 ])
151+ pset .execute ([AdvectionEE , periodicBC ], runtime = np .timedelta64 (40 , "s" ), dt = np .timedelta64 (1 , "s" ))
152+ assert np .isclose (pset .total_dlon [0 ], 4 , atol = 1e-5 )
153+ assert np .isclose (pset .lon_nextloop [0 ], 0.5 , atol = 1e-5 )
96154
97155
98156def test_horizontal_advection_in_3D_flow (npart = 10 ):
0 commit comments