@@ -21,11 +21,11 @@ def AdvectionRK4(particle, fieldset, time): # pragma: no cover
2121 dt = particle .dt / np .timedelta64 (1 , "s" ) # TODO: improve API for converting dt to seconds
2222 (u1 , v1 ) = fieldset .UV [particle ]
2323 lon1 , lat1 = (particle .lon + u1 * 0.5 * dt , particle .lat + v1 * 0.5 * dt )
24- (u2 , v2 ) = fieldset .UV [time + 0.5 * particle .dt , particle .depth , lat1 , lon1 , particle ]
24+ (u2 , v2 ) = fieldset .UV [particle . time + 0.5 * particle .dt , particle .depth , lat1 , lon1 , particle ]
2525 lon2 , lat2 = (particle .lon + u2 * 0.5 * dt , particle .lat + v2 * 0.5 * dt )
26- (u3 , v3 ) = fieldset .UV [time + 0.5 * particle .dt , particle .depth , lat2 , lon2 , particle ]
26+ (u3 , v3 ) = fieldset .UV [particle . time + 0.5 * particle .dt , particle .depth , lat2 , lon2 , particle ]
2727 lon3 , lat3 = (particle .lon + u3 * dt , particle .lat + v3 * dt )
28- (u4 , v4 ) = fieldset .UV [time + particle .dt , particle .depth , lat3 , lon3 , particle ]
28+ (u4 , v4 ) = fieldset .UV [particle . time + particle .dt , particle .depth , lat3 , lon3 , particle ]
2929 particle .dlon += (u1 + 2 * u2 + 2 * u3 + u4 ) / 6.0 * dt
3030 particle .dlat += (v1 + 2 * v2 + 2 * v3 + v4 ) / 6.0 * dt
3131
@@ -37,15 +37,15 @@ def AdvectionRK4_3D(particle, fieldset, time): # pragma: no cover
3737 lon1 = particle .lon + u1 * 0.5 * dt
3838 lat1 = particle .lat + v1 * 0.5 * dt
3939 dep1 = particle .depth + w1 * 0.5 * dt
40- (u2 , v2 , w2 ) = fieldset .UVW [time + 0.5 * particle .dt , dep1 , lat1 , lon1 , particle ]
40+ (u2 , v2 , w2 ) = fieldset .UVW [particle . time + 0.5 * particle .dt , dep1 , lat1 , lon1 , particle ]
4141 lon2 = particle .lon + u2 * 0.5 * dt
4242 lat2 = particle .lat + v2 * 0.5 * dt
4343 dep2 = particle .depth + w2 * 0.5 * dt
44- (u3 , v3 , w3 ) = fieldset .UVW [time + 0.5 * particle .dt , dep2 , lat2 , lon2 , particle ]
44+ (u3 , v3 , w3 ) = fieldset .UVW [particle . time + 0.5 * particle .dt , dep2 , lat2 , lon2 , particle ]
4545 lon3 = particle .lon + u3 * dt
4646 lat3 = particle .lat + v3 * dt
4747 dep3 = particle .depth + w3 * dt
48- (u4 , v4 , w4 ) = fieldset .UVW [time + particle .dt , dep3 , lat3 , lon3 , particle ]
48+ (u4 , v4 , w4 ) = fieldset .UVW [particle . time + particle .dt , dep3 , lat3 , lon3 , particle ]
4949 particle .dlon += (u1 + 2 * u2 + 2 * u3 + u4 ) / 6 * dt
5050 particle .dlat += (v1 + 2 * v2 + 2 * v3 + v4 ) / 6 * dt
5151 particle .ddepth += (w1 + 2 * w2 + 2 * w3 + w4 ) / 6 * dt
@@ -56,35 +56,35 @@ def AdvectionRK4_3D_CROCO(particle, fieldset, time): # pragma: no cover
5656 This kernel assumes the vertical velocity is the 'w' field from CROCO output and works on sigma-layers.
5757 """
5858 dt = particle .dt / np .timedelta64 (1 , "s" ) # TODO: improve API for converting dt to seconds
59- sig_dep = particle .depth / fieldset .H [time , 0 , particle .lat , particle .lon ]
59+ sig_dep = particle .depth / fieldset .H [particle . time , 0 , particle .lat , particle .lon ]
6060
61- (u1 , v1 , w1 ) = fieldset .UVW [time , particle .depth , particle .lat , particle .lon , particle ]
62- w1 *= sig_dep / fieldset .H [time , 0 , particle .lat , particle .lon ]
61+ (u1 , v1 , w1 ) = fieldset .UVW [particle . time , particle .depth , particle .lat , particle .lon , particle ]
62+ w1 *= sig_dep / fieldset .H [particle . time , 0 , particle .lat , particle .lon ]
6363 lon1 = particle .lon + u1 * 0.5 * dt
6464 lat1 = particle .lat + v1 * 0.5 * dt
6565 sig_dep1 = sig_dep + w1 * 0.5 * dt
66- dep1 = sig_dep1 * fieldset .H [time , 0 , lat1 , lon1 ]
66+ dep1 = sig_dep1 * fieldset .H [particle . time , 0 , lat1 , lon1 ]
6767
68- (u2 , v2 , w2 ) = fieldset .UVW [time + 0.5 * particle .dt , dep1 , lat1 , lon1 , particle ]
69- w2 *= sig_dep1 / fieldset .H [time , 0 , lat1 , lon1 ]
68+ (u2 , v2 , w2 ) = fieldset .UVW [particle . time + 0.5 * particle .dt , dep1 , lat1 , lon1 , particle ]
69+ w2 *= sig_dep1 / fieldset .H [particle . time , 0 , lat1 , lon1 ]
7070 lon2 = particle .lon + u2 * 0.5 * dt
7171 lat2 = particle .lat + v2 * 0.5 * dt
7272 sig_dep2 = sig_dep + w2 * 0.5 * dt
73- dep2 = sig_dep2 * fieldset .H [time , 0 , lat2 , lon2 ]
73+ dep2 = sig_dep2 * fieldset .H [particle . time , 0 , lat2 , lon2 ]
7474
75- (u3 , v3 , w3 ) = fieldset .UVW [time + 0.5 * particle .dt , dep2 , lat2 , lon2 , particle ]
76- w3 *= sig_dep2 / fieldset .H [time , 0 , lat2 , lon2 ]
75+ (u3 , v3 , w3 ) = fieldset .UVW [particle . time + 0.5 * particle .dt , dep2 , lat2 , lon2 , particle ]
76+ w3 *= sig_dep2 / fieldset .H [particle . time , 0 , lat2 , lon2 ]
7777 lon3 = particle .lon + u3 * dt
7878 lat3 = particle .lat + v3 * dt
7979 sig_dep3 = sig_dep + w3 * dt
80- dep3 = sig_dep3 * fieldset .H [time , 0 , lat3 , lon3 ]
80+ dep3 = sig_dep3 * fieldset .H [particle . time , 0 , lat3 , lon3 ]
8181
82- (u4 , v4 , w4 ) = fieldset .UVW [time + particle .dt , dep3 , lat3 , lon3 , particle ]
83- w4 *= sig_dep3 / fieldset .H [time , 0 , lat3 , lon3 ]
82+ (u4 , v4 , w4 ) = fieldset .UVW [particle . time + particle .dt , dep3 , lat3 , lon3 , particle ]
83+ w4 *= sig_dep3 / fieldset .H [particle . time , 0 , lat3 , lon3 ]
8484 lon4 = particle .lon + u4 * dt
8585 lat4 = particle .lat + v4 * dt
8686 sig_dep4 = sig_dep + w4 * dt
87- dep4 = sig_dep4 * fieldset .H [time , 0 , lat4 , lon4 ]
87+ dep4 = sig_dep4 * fieldset .H [particle . time , 0 , lat4 , lon4 ]
8888
8989 particle .dlon += (u1 + 2 * u2 + 2 * u3 + u4 ) / 6 * dt
9090 particle .dlat += (v1 + 2 * v2 + 2 * v3 + v4 ) / 6 * dt
@@ -115,14 +115,7 @@ def AdvectionRK45(particle, fieldset, time): # pragma: no cover
115115 Time-step dt is halved if error is larger than fieldset.RK45_tol,
116116 and doubled if error is smaller than 1/10th of tolerance.
117117 """
118- dt = particle .next_dt / np .timedelta64 (1 , "s" ) # TODO: improve API for converting dt to seconds
119- if dt > fieldset .RK45_max_dt :
120- dt = fieldset .RK45_max_dt
121- particle .next_dt = fieldset .RK45_max_dt * np .timedelta64 (1 , "s" )
122- if dt < fieldset .RK45_min_dt :
123- particle .next_dt = fieldset .RK45_min_dt * np .timedelta64 (1 , "s" )
124- return StatusCode .Repeat
125- particle .dt = particle .next_dt
118+ dt = particle .dt / np .timedelta64 (1 , "s" ) # TODO: improve API for converting dt to seconds
126119
127120 c = [1.0 / 4.0 , 3.0 / 8.0 , 12.0 / 13.0 , 1.0 , 1.0 / 2.0 ]
128121 A = [
@@ -137,42 +130,58 @@ def AdvectionRK45(particle, fieldset, time): # pragma: no cover
137130
138131 (u1 , v1 ) = fieldset .UV [particle ]
139132 lon1 , lat1 = (particle .lon + u1 * A [0 ][0 ] * dt , particle .lat + v1 * A [0 ][0 ] * dt )
140- (u2 , v2 ) = fieldset .UV [time + c [0 ] * particle .dt , particle .depth , lat1 , lon1 , particle ]
133+ (u2 , v2 ) = fieldset .UV [particle . time + c [0 ] * particle .dt , particle .depth , lat1 , lon1 , particle ]
141134 lon2 , lat2 = (
142135 particle .lon + (u1 * A [1 ][0 ] + u2 * A [1 ][1 ]) * dt ,
143136 particle .lat + (v1 * A [1 ][0 ] + v2 * A [1 ][1 ]) * dt ,
144137 )
145- (u3 , v3 ) = fieldset .UV [time + c [1 ] * particle .dt , particle .depth , lat2 , lon2 , particle ]
138+ (u3 , v3 ) = fieldset .UV [particle . time + c [1 ] * particle .dt , particle .depth , lat2 , lon2 , particle ]
146139 lon3 , lat3 = (
147140 particle .lon + (u1 * A [2 ][0 ] + u2 * A [2 ][1 ] + u3 * A [2 ][2 ]) * dt ,
148141 particle .lat + (v1 * A [2 ][0 ] + v2 * A [2 ][1 ] + v3 * A [2 ][2 ]) * dt ,
149142 )
150- (u4 , v4 ) = fieldset .UV [time + c [2 ] * particle .dt , particle .depth , lat3 , lon3 , particle ]
143+ (u4 , v4 ) = fieldset .UV [particle . time + c [2 ] * particle .dt , particle .depth , lat3 , lon3 , particle ]
151144 lon4 , lat4 = (
152145 particle .lon + (u1 * A [3 ][0 ] + u2 * A [3 ][1 ] + u3 * A [3 ][2 ] + u4 * A [3 ][3 ]) * dt ,
153146 particle .lat + (v1 * A [3 ][0 ] + v2 * A [3 ][1 ] + v3 * A [3 ][2 ] + v4 * A [3 ][3 ]) * dt ,
154147 )
155- (u5 , v5 ) = fieldset .UV [time + c [3 ] * particle .dt , particle .depth , lat4 , lon4 , particle ]
148+ (u5 , v5 ) = fieldset .UV [particle . time + c [3 ] * particle .dt , particle .depth , lat4 , lon4 , particle ]
156149 lon5 , lat5 = (
157150 particle .lon + (u1 * A [4 ][0 ] + u2 * A [4 ][1 ] + u3 * A [4 ][2 ] + u4 * A [4 ][3 ] + u5 * A [4 ][4 ]) * dt ,
158151 particle .lat + (v1 * A [4 ][0 ] + v2 * A [4 ][1 ] + v3 * A [4 ][2 ] + v4 * A [4 ][3 ] + v5 * A [4 ][4 ]) * dt ,
159152 )
160- (u6 , v6 ) = fieldset .UV [time + c [4 ] * particle .dt , particle .depth , lat5 , lon5 , particle ]
153+ (u6 , v6 ) = fieldset .UV [particle . time + c [4 ] * particle .dt , particle .depth , lat5 , lon5 , particle ]
161154
162155 lon_4th = (u1 * b4 [0 ] + u2 * b4 [1 ] + u3 * b4 [2 ] + u4 * b4 [3 ] + u5 * b4 [4 ]) * dt
163156 lat_4th = (v1 * b4 [0 ] + v2 * b4 [1 ] + v3 * b4 [2 ] + v4 * b4 [3 ] + v5 * b4 [4 ]) * dt
164157 lon_5th = (u1 * b5 [0 ] + u2 * b5 [1 ] + u3 * b5 [2 ] + u4 * b5 [3 ] + u5 * b5 [4 ] + u6 * b5 [5 ]) * dt
165158 lat_5th = (v1 * b5 [0 ] + v2 * b5 [1 ] + v3 * b5 [2 ] + v4 * b5 [3 ] + v5 * b5 [4 ] + v6 * b5 [5 ]) * dt
166159
167- kappa = math .sqrt (math .pow (lon_5th - lon_4th , 2 ) + math .pow (lat_5th - lat_4th , 2 ))
168- if (kappa <= fieldset .RK45_tol ) or (math .fabs (dt ) < math .fabs (fieldset .RK45_min_dt )):
169- particle .dlon += lon_4th
170- particle .dlat += lat_4th
171- if (kappa <= fieldset .RK45_tol / 10 ) and (math .fabs (dt * 2 ) <= math .fabs (fieldset .RK45_max_dt )):
172- particle .next_dt *= 2
173- else :
174- particle .next_dt /= 2
175- return StatusCode .Repeat
160+ kappa = np .sqrt (np .pow (lon_5th - lon_4th , 2 ) + np .pow (lat_5th - lat_4th , 2 ))
161+
162+ good_particles = (kappa <= fieldset .RK45_tol ) | (np .fabs (dt ) <= np .fabs (fieldset .RK45_min_dt ))
163+ particle .dlon += np .where (good_particles , lon_5th , 0 )
164+ particle .dlat += np .where (good_particles , lat_5th , 0 )
165+
166+ increase_dt_particles = (
167+ good_particles & (kappa <= fieldset .RK45_tol / 10 ) & (np .fabs (dt * 2 ) <= np .fabs (fieldset .RK45_max_dt ))
168+ )
169+ particle .dt = np .where (increase_dt_particles , particle .dt * 2 , particle .dt )
170+ particle .dt = np .where (
171+ particle .dt > fieldset .RK45_max_dt * np .timedelta64 (1 , "s" ),
172+ fieldset .RK45_max_dt * np .timedelta64 (1 , "s" ),
173+ particle .dt ,
174+ )
175+ particle .state = np .where (good_particles , StatusCode .Success , particle .state )
176+
177+ repeat_particles = np .invert (good_particles )
178+ particle .dt = np .where (repeat_particles , particle .dt / 2 , particle .dt )
179+ particle .dt = np .where (
180+ particle .dt < fieldset .RK45_min_dt * np .timedelta64 (1 , "s" ),
181+ fieldset .RK45_min_dt * np .timedelta64 (1 , "s" ),
182+ particle .dt ,
183+ )
184+ particle .state = np .where (repeat_particles , StatusCode .Repeat , particle .state )
176185
177186
178187def AdvectionAnalytical (particle , fieldset , time ): # pragma: no cover
0 commit comments