@@ -39,10 +39,6 @@ def StokesDrift(particle, fieldset, time):
3939 [1] Breivik (2016) - https://doi.org/10.1016/j.ocemod.2016.01.005
4040
4141 """
42- # Sample the U / V components of Stokes drift
43- stokes_U = fieldset .Stokes_U [time , particle .depth , particle .lat , particle .lon ]
44- stokes_V = fieldset .Stokes_V [time , particle .depth , particle .lat , particle .lon ]
45-
4642 # Sample the peak wave period
4743 T_p = fieldset .wave_Tp [time , particle .depth , particle .lat , particle .lon ]
4844
@@ -51,6 +47,10 @@ def StokesDrift(particle, fieldset, time):
5147
5248 # Only compute displacements if the peak wave period is large enough and the particle is in the water
5349 if T_p > 1E-14 and particle .depth < local_bathymetry :
50+ # Sample the U / V components of Stokes drift
51+ stokes_U = fieldset .Stokes_U [time , particle .depth , particle .lat , particle .lon ]
52+ stokes_V = fieldset .Stokes_V [time , particle .depth , particle .lat , particle .lon ]
53+
5454 # Peak wave frequency
5555 omega_p = 2. * math .pi / T_p
5656
@@ -102,13 +102,13 @@ def WindageDrift(particle, fieldset, time):
102102 (ocean_U , ocean_V ) = fieldset .UV [particle ]
103103 ocean_speed = math .sqrt (ocean_U ** 2 + ocean_V ** 2 )
104104
105- # Sample the U / V components of wind
106- wind_U = fieldset .Wind_U [time , particle .depth , particle .lat , particle .lon ]
107- wind_V = fieldset .Wind_V [time , particle .depth , particle .lat , particle .lon ]
108-
109105 # Apply windage to particles that have some exposed surface above the ocean surface
110106 # Use a basic approach to only apply windage to particle in the ocean
111- if particle .depth < 0.5 * particle .plastic_diameter and ocean_speed > 1E-12 :
107+ if particle .depth < 0.5 * particle .plastic_diameter and ocean_speed > 1E-14 :
108+ # Sample the U / V components of wind
109+ wind_U = fieldset .Wind_U [time , particle .depth , particle .lat , particle .lon ]
110+ wind_V = fieldset .Wind_V [time , particle .depth , particle .lat , particle .lon ]
111+
112112 # Compute particle displacement
113113 particle_dlon += particle .wind_coefficient * (wind_U - ocean_U ) * particle .dt # noqa
114114 particle_dlat += particle .wind_coefficient * (wind_V - ocean_V ) * particle .dt # noqa
@@ -161,15 +161,15 @@ def SettlingVelocity(particle, fieldset, time):
161161 g = fieldset .G # gravitational acceleration [m s-2]
162162 seawater_density = particle .seawater_density # [kg m-3]
163163 temperature = fieldset .conservative_temperature [time , particle .depth , particle .lat , particle .lon ]
164- seawater_salinity = fieldset .absolute_salinity [time , particle .depth , particle .lat , particle .lon ] / 1000
164+ seawater_salinity = fieldset .absolute_salinity [time , particle .depth , particle .lat , particle .lon ] / 1000.
165165 particle_diameter = particle .plastic_diameter
166166 particle_density = particle .plastic_density
167167
168168 # Compute the kinematic viscosity of seawater
169169 water_dynamic_viscosity = 4.2844E-5 + (1. / ((0.156 * (temperature + 64.993 ) ** 2 ) - 91.296 )) # Eq. (26) from [2]
170- A = 1.541 + 1.998E-2 * temperature - 9.52E-5 * temperature ** 2 # Eq. (28) from [2]
171- B = 7.974 - 7.561E-2 * temperature + 4.724E-4 * temperature ** 2 # Eq. (29) from [2]
172- seawater_dynamic_viscosity = water_dynamic_viscosity * (1. + A * seawater_salinity + B * seawater_salinity ** 2 ) # Eq. (27) from [2]
170+ A = 1.541 + 1.998E-2 * temperature - 9.52E-5 * temperature ** 2. # Eq. (28) from [2]
171+ B = 7.974 - 7.561E-2 * temperature + 4.724E-4 * temperature ** 2. # Eq. (29) from [2]
172+ seawater_dynamic_viscosity = water_dynamic_viscosity * (1. + A * seawater_salinity + B * seawater_salinity ** 2. ) # Eq. (27) from [2]
173173 seawater_kinematic_viscosity = seawater_dynamic_viscosity / seawater_density # Eq. (25) from [2]
174174
175175 # Compute the density difference of the particle
@@ -179,7 +179,7 @@ def SettlingVelocity(particle, fieldset, time):
179179 dimensionless_diameter = (math .fabs (particle_density - seawater_density ) * g * particle_diameter ** 3. ) / (seawater_density * seawater_kinematic_viscosity ** 2. ) # [-]
180180
181181 # Compute the dimensionless settling velocity w_*
182- if dimensionless_diameter > 5E9 : # "The boundary layer around the sphere becomes fully turbulent, causing a reduction in drag and an increase in settling velocity" - [1]
182+ if dimensionless_diameter > 5.0E9 : # "The boundary layer around the sphere becomes fully turbulent, causing a reduction in drag and an increase in settling velocity" - [1]
183183 dimensionless_velocity = 265000. # Set a maximum dimensionless settling velocity
184184 elif dimensionless_diameter < 0.05 : # "At values of D_* less than 0.05, (9) deviates signficantly ... from Stokes' law and (8) should be used." - [1]
185185 dimensionless_velocity = (dimensionless_diameter ** 2. ) / 5832. # Using Eq. (8) in [1]
@@ -271,9 +271,9 @@ def Biofouling(particle, fieldset, time):
271271 initial_settling_velocity = particle .settling_velocity # settling velocity [m s-1]
272272
273273 # Compute the seawater dynamic viscosity and kinematic viscosity
274- water_dynamic_viscosity = 4.2844E-5 + (1. / ((0.156 * (temperature + 64.993 ) ** 2 ) - 91.296 )) # Eq. (26) from [2]
275- A = 1.541 + 1.998E-2 * temperature - 9.52E-5 * temperature ** 2 # Eq. (28) from [2]
276- B = 7.974 - 7.561E-2 * temperature + 4.724E-4 * temperature ** 2 # Eq. (29) from [2]
274+ water_dynamic_viscosity = 4.2844E-5 + (1. / ((0.156 * (temperature + 64.993 ) ** 2. ) - 91.296 )) # Eq. (26) from [2]
275+ A = 1.541 + 1.998E-2 * temperature - 9.52E-5 * temperature ** 2. # Eq. (28) from [2]
276+ B = 7.974 - 7.561E-2 * temperature + 4.724E-4 * temperature ** 2. # Eq. (29) from [2]
277277 seawater_dynamic_viscosity = water_dynamic_viscosity * (1. + A * seawater_salinity + B * seawater_salinity ** 2 ) # Eq. (27) from [2]
278278 seawater_kinematic_viscosity = seawater_dynamic_viscosity / particle .seawater_density # Eq. (25) from [2]
279279
@@ -282,7 +282,7 @@ def Biofouling(particle, fieldset, time):
282282 mol_concentration_diatoms = fieldset .bio_diatom [time , particle .depth , particle .lat , particle .lon ] # Mole concentration of Diatoms expressed as carbon in sea water
283283 mol_concentration_nanophytoplankton = fieldset .bio_nanophy [time , particle .depth , particle .lat , particle .lon ] # Mole concentration of Nanophytoplankton expressed as carbon in seawater
284284 total_primary_production_of_phyto = fieldset .pp_phyto [time , particle .depth , particle .lat , particle .lon ] # mg C /m3/day #pp_phyto_
285- median_mg_carbon_per_cell = 2726e -9 # Median mg of Carbon per cell selected from [3]. From [2] pg7966 - "The conversion from carbon to algae cells is highly variable, ranging between 35339 to 47.76 pg per carbon cell [3]. We choose the median value, 2726 x 10^-9 mg per carbon cell."
285+ median_mg_carbon_per_cell = 2726.0E -9 # Median mg of Carbon per cell selected from [3]. From [2] pg7966 - "The conversion from carbon to algae cells is highly variable, ranging between 35339 to 47.76 pg per carbon cell [3]. We choose the median value, 2726 x 10^-9 mg per carbon cell."
286286 # carbon_molecular_weight = fieldset.carbon_molecular_weight # grams C per mol of C #Wt_C
287287
288288 # Compute concentration numbers
@@ -350,7 +350,7 @@ def Biofouling(particle, fieldset, time):
350350 dimensionless_diameter = (math .fabs (total_density - particle .seawater_density ) * fieldset .G * particle_diameter ** 3. ) / (particle .seawater_density * seawater_kinematic_viscosity ** 2. ) # [-]
351351
352352 # Compute the dimensionless settling velocity w_*
353- if dimensionless_diameter > 5E9 : # "The boundary layer around the sphere becomes fully turbulent, causing a reduction in drag and an increase in settling velocity" - [1]
353+ if dimensionless_diameter > 5.0E9 : # "The boundary layer around the sphere becomes fully turbulent, causing a reduction in drag and an increase in settling velocity" - [1]
354354 dimensionless_velocity = 265000. # Set a maximum dimensionless settling velocity
355355 elif dimensionless_diameter < 0.05 : # "At values of D_* less than 0.05, (9) deviates signficantly ... from Stokes' law and (8) should be used." - [1]
356356 dimensionless_velocity = (dimensionless_diameter ** 2. ) / 5832. # Using Eq. (8) in [1]
@@ -489,7 +489,7 @@ def VerticalMixing(particle, fieldset, time):
489489
490490 Description
491491 ----------
492- A simple verticle mixing kernel that uses a markov-0 process to determine the vertical
492+ A simple vertical mixing kernel that uses a markov-0 process to determine the vertical
493493 displacement of a particle [1]. The deterministic component is determined
494494 using forward-difference with a given `delta_z`.
495495
@@ -524,7 +524,6 @@ def VerticalMixing(particle, fieldset, time):
524524
525525 # Compute the random walk component of Eq. (1)
526526 dz_random = ParcelsRandom .uniform (- 1. , 1. ) * math .sqrt (math .fabs (particle .dt ) * 3 ) * math .sqrt (2 * kz )
527- # TODO - implement the reflective boundary condition
528527
529528 # Compute rise velocity component of Eq. (1) - Already accounted for in other kernels
530529 dz_wb = 0 # particle.settling_velocity * particle.dt
@@ -535,7 +534,32 @@ def VerticalMixing(particle, fieldset, time):
535534 # Update particle position
536535 particle_ddepth += ddepth # noqa
537536
538- # Biofouling related kernels
537+
538+ def reflectAtSurface (particle , fieldset , time ):
539+ """A reflecting boundary condition kernel at the ocean surface.
540+
541+ Description
542+ ----------
543+ A simple kernel to reflect particles at the ocean surface if they go through the surface.
544+
545+ Parameter Requirements
546+ ----------
547+ None
548+
549+ Kernel Requirements
550+ ----------
551+ Order of Operations:
552+ This kernel should be performed after all vertical displacement kernels have been applied.
553+
554+ References
555+ ----------
556+ None
557+
558+ """
559+ potential_depth = particle .depth + particle_ddepth # noqa
560+ if potential_depth < 0. :# Particle is above the surface
561+ particle .depth = - potential_depth
562+ particle_ddepth = 0. # noqa
539563
540564
541565def unbeaching (particle , fieldset , time ):
@@ -560,7 +584,7 @@ def unbeaching(particle, fieldset, time):
560584 unbeaching field using the updated particle position.
561585 """
562586 # Measure the velocity field at the final particle location
563- (vel_u , vel_v , vel_w ) = fieldset .UVW [time , particle .depth + particle_ddepth , particle .lat + particle_dlat , particle .lon + particle_dlon ] # noqa
587+ (vel_u , vel_v ) = fieldset .UV [time , particle .depth + particle_ddepth , particle .lat + particle_dlat , particle .lon + particle_dlon ] # noqa
564588
565589 if math .fabs (vel_u ) < 1e-14 and math .fabs (vel_v ) < 1e-14 :
566590 U_ub = fieldset .unbeach_U [time , particle .depth + particle_ddepth , particle .lat + particle_dlat , particle .lon + particle_dlon ] # noqa
@@ -573,6 +597,104 @@ def unbeaching(particle, fieldset, time):
573597 particle_dlat += dlat # noqa
574598
575599
600+ def unbeachingBySamplingAfterwards (particle , fieldset , time ):
601+ """Alternative unbeaching kernel.
602+
603+ Description
604+ ----------
605+ A kernel to 'unbeach' particles that have been advected onto non-ocean grid cells.
606+ This kernel samples the velocity field in the four cardinal directions around the particle,
607+ and moves the particle in the direction of the highest velocity magnitude, assuming that
608+ this direction is the ocean direction. This kernel only acts if the particle displacement
609+ in both zonal and meridional directions is (near) zero, indicating that the particle is beached.
610+
611+ Parameter Requirements
612+ ----------
613+ Fieldset:
614+ None
615+
616+ Kernel Requirements
617+ ----------
618+ Order of Operations:
619+ This kernel should be performed after all other movement kernels.
620+ """
621+ # In the case of being beached, these displacement will be zero
622+ new_lon = particle .lon + particle_dlon # noqa
623+ new_lat = particle .lat + particle_dlat # noqa
624+ new_depth = particle .depth + particle_ddepth # noqa
625+
626+ if math .fabs (particle_dlon ) + math .fabs (particle_dlat ) < 1e-14 : # noqa
627+ displacement = 1. / 8. # Degree displacement to sample the velocity field
628+
629+ # Convert 1m/s to degrees/s at the particle latitude in zonal and meridional directions
630+ unbeach_U = 1. / (1852. * 60. * math .cos (particle .lat * math .pi / 180. ))
631+ unbeach_V = 1. / (1852. * 60. )
632+
633+ # Sample the velocity field in the four cardinal directions
634+ (U_left , V_left ) = fieldset .UV [time , new_depth , new_lat , new_lon - displacement ]
635+ (U_right , V_right ) = fieldset .UV [time , new_depth , new_lat , new_lon + displacement ]
636+ (U_up , V_up ) = fieldset .UV [time , new_depth , new_lat + displacement , new_lon ]
637+ (U_down , V_down ) = fieldset .UV [time , new_depth , new_lat - displacement , new_lon ]
638+
639+ # Find the direction of the highest velocity
640+ left = math .sqrt (U_left ** 2 + V_left ** 2 )
641+ right = math .sqrt (U_right ** 2 + V_right ** 2 )
642+ up = math .sqrt (U_up ** 2 + V_up ** 2 )
643+ down = math .sqrt (U_down ** 2 + V_down ** 2 )
644+
645+ max_vel = 0.
646+ U_dir = 0.
647+ V_dir = 0.
648+
649+ if left + right + up + down > 1e-14 :
650+ max_vel = left
651+ U_dir = - 1.
652+ V_dir = 0.
653+ if max_vel < right :
654+ max_vel = right
655+ U_dir = 1.
656+ V_dir = 0.
657+ if max_vel < up :
658+ max_vel = up
659+ U_dir = 0.
660+ V_dir = 1.
661+ if max_vel < down :
662+ max_vel = down
663+ U_dir = 0.
664+ V_dir = - 1.
665+
666+ # If all four cardinal directions are zero, check diagonal directions
667+ else :
668+ (U_left_up , V_left_up ) = fieldset .UV [time , new_depth , new_lat + displacement , new_lon - displacement ]
669+ (U_left_down , V_left_down ) = fieldset .UV [time , new_depth , new_lat - displacement , new_lon - displacement ]
670+ (U_right_up , V_right_up ) = fieldset .UV [time , new_depth , new_lat + displacement , new_lon + displacement ]
671+ (U_right_down , V_right_down ) = fieldset .UV [time , new_depth , new_lat - displacement , new_lon + displacement ]
672+
673+ left_up = math .sqrt (U_left_up ** 2 + V_left_up ** 2 )
674+ left_down = math .sqrt (U_left_down ** 2 + V_left_down ** 2 )
675+ right_up = math .sqrt (U_right_up ** 2 + V_right_up ** 2 )
676+ right_down = math .sqrt (U_right_down ** 2 + V_right_down ** 2 )
677+
678+ max_vel = left_up
679+ U_dir = - 1.
680+ V_dir = 1.
681+ if max_vel < left_down :
682+ max_vel = left_down
683+ U_dir = 1.
684+ V_dir = - 1.
685+ if max_vel < right_up :
686+ max_vel = right_up
687+ U_dir = 1.
688+ V_dir = 1.
689+ if max_vel < right_down :
690+ max_vel = right_down
691+ U_dir = 1.
692+ V_dir = - 1.
693+
694+ particle_dlon += U_dir * unbeach_U * particle .dt # noqa
695+ particle_dlat += V_dir * unbeach_V * particle .dt # noqa
696+
697+
576698def checkThroughBathymetry (particle , fieldset , time ):
577699 """Bathymetry error kernel.
578700
@@ -607,6 +729,35 @@ def checkThroughBathymetry(particle, fieldset, time):
607729 particle_ddepth = 3900. - particle .depth # noqa
608730
609731
732+ def reflectAtBathymetry (particle , fieldset , time ):
733+ """Reflect at bathymetry kernel.
734+
735+ Description
736+ ----------
737+ A simple kernel to reflect particles at the ocean bathymetry, if they go through the bathymetry.
738+
739+ Parameter Requirements
740+ ----------
741+ Fieldset:
742+ - `fieldset.bathymetry` - A 2D field containing the ocean bathymetry. Units [m].
743+
744+ Kernel Requirements
745+ ----------
746+ Order of Operations:
747+ This kernel should be performed after all other movement kernels, as it samples the
748+ bathymetry field using the updated particle position.
749+ """
750+ local_bathymetry = fieldset .bathymetry [time , particle .depth , particle .lat , particle .lon ]
751+ potential_depth = particle .depth + particle_ddepth # noqa
752+
753+ if potential_depth > 100 :
754+ local_bathymetry = 0.99 * local_bathymetry # Handle linear interpolation issues for deep particles
755+
756+ if potential_depth > local_bathymetry :
757+ beyond_depth = potential_depth - local_bathymetry
758+ particle .depth = local_bathymetry - beyond_depth # Reflect the particle back above the bathymetry
759+ particle_ddepth = 0. # noqa
760+
610761
611762def periodicBC (particle , fieldset , time ):
612763 """A periodic boundary condition kernel.
0 commit comments