def test_randomvonmises(mode, mu, kappa, npart=10000): fieldset = zeros_fieldset() # Parameters for random.vonmisesvariate fieldset.mu = mu fieldset.kappa = kappa # Set random seed ParcelsRandom.seed(1234) class AngleParticle(ptype[mode]): angle = Variable('angle') pset = ParticleSet(fieldset=fieldset, pclass=AngleParticle, lon=np.zeros(npart), lat=np.zeros(npart), depth=np.zeros(npart)) def vonmises(particle, fieldset, time): particle.angle = ParcelsRandom.vonmisesvariate(fieldset.mu, fieldset.kappa) pset.execute(vonmises, runtime=1, dt=1) angles = np.array([p.angle for p in pset]) assert np.allclose(np.mean(angles), mu, atol=.1) scipy_mises = stats.vonmises.rvs(kappa, loc=mu, size=10000) assert np.allclose(np.mean(angles), np.mean(scipy_mises), atol=.1) assert np.allclose(np.std(angles), np.std(scipy_mises), atol=.1)
def test_recursive_errorhandling(mode, xdim=2, ydim=2): """Example script to show how recursaive error handling can work. In this example, a set of Particles is started at Longitude 0.5. These are run through a Kernel that throws an error if the Longitude is smaller than 0.7. The error Kernel then draws a new random number between 0 and 1 Importantly, the 'normal' Kernel and Error Kernel keep iterating until a particle does have a longitude larger than 0.7. This behaviour can be useful if particles need to be 'pushed out' from e.g. land. Note however that current under-the-hood implementation is not extremely efficient, so code could be slow.""" dimensions = {'lon': np.linspace(0., 1., xdim, dtype=np.float32), 'lat': np.linspace(0., 1., ydim, dtype=np.float32)} data = {'U': np.zeros((ydim, xdim), dtype=np.float32), 'V': np.zeros((ydim, xdim), dtype=np.float32)} fieldset = FieldSet.from_data(data, dimensions, mesh='flat') # Set minimum value for valid longitudes (i.e. all longitudes < minlon are 'land') fieldset.add_constant('minlon', 0.7) # create a ParticleSet with all particles starting at centre of Field pset = ParticleSet.from_line(fieldset=fieldset, pclass=ptype[mode], start=(0.5, 0.5), finish=(0.5, 0.5), size=10) def TestLon(particle, fieldset, time): """Kernel to check whether a longitude is larger than fieldset.minlon. If not, the Kernel throws an error""" if particle.lon <= fieldset.minlon: return ErrorCode.Error def Error_RandomiseLon(particle, fieldset, time): """Error handling kernel that draws a new longitude. Note that this new longitude can be smaller than fieldset.minlon""" particle.lon = ParcelsRandom.uniform(0., 1.) ParcelsRandom.seed(123456) # The .execute below is only run for one timestep. Yet the # recovery={ErrorCode.Error: Error_RandomiseLon} assures Parcels keeps # attempting to move all particles beyond 0.7 longitude pset.execute(pset.Kernel(TestLon), runtime=1, dt=1, recovery={ErrorCode.Error: Error_RandomiseLon}) assert (pset.lon > fieldset.minlon).all()
def test_brownian_example(mode, mesh, npart=3000): fieldset = FieldSet.from_data({ 'U': 0, 'V': 0 }, { 'lon': 0, 'lat': 0 }, mesh=mesh) # Set diffusion constants. kh_zonal = 100 # in m^2/s kh_meridional = 100 # in m^2/s # Create field of constant Kh_zonal and Kh_meridional fieldset.add_field(Field('Kh_zonal', kh_zonal, lon=0, lat=0, mesh=mesh)) fieldset.add_field( Field('Kh_meridional', kh_meridional, lon=0, lat=0, mesh=mesh)) # Set random seed ParcelsRandom.seed(123456) runtime = delta(days=1) ParcelsRandom.seed(1234) pset = ParticleSet(fieldset=fieldset, pclass=ptype[mode], lon=np.zeros(npart), lat=np.zeros(npart)) pset.execute(pset.Kernel(DiffusionUniformKh), runtime=runtime, dt=delta(hours=1)) expected_std_x = np.sqrt(2 * kh_zonal * runtime.total_seconds()) expected_std_y = np.sqrt(2 * kh_meridional * runtime.total_seconds()) ys = pset.lat * mesh_conversion(mesh) xs = pset.lon * mesh_conversion( mesh ) # since near equator, we do not need to care about curvature effect tol = 200 # 200m tolerance assert np.allclose(np.std(xs), expected_std_x, atol=tol) assert np.allclose(np.std(ys), expected_std_y, atol=tol) assert np.allclose(np.mean(xs), 0, atol=tol) assert np.allclose(np.mean(ys), 0, atol=tol)
def test_fieldKh_Brownian(mesh, mode, xdim=200, ydim=100, kh_zonal=100, kh_meridional=50): mesh_conversion = 1 / 1852. / 60 if mesh == 'spherical' else 1 fieldset = zeros_fieldset(mesh=mesh, xdim=xdim, ydim=ydim, mesh_conversion=mesh_conversion) vec = np.linspace(-1e5 * mesh_conversion, 1e5 * mesh_conversion, 2) grid = RectilinearZGrid(lon=vec, lat=vec, mesh=mesh) fieldset.add_field(Field('Kh_zonal', kh_zonal * np.ones((2, 2)), grid=grid)) fieldset.add_field( Field('Kh_meridional', kh_meridional * np.ones((2, 2)), grid=grid)) npart = 1000 runtime = delta(days=1) ParcelsRandom.seed(1234) pset = ParticleSet(fieldset=fieldset, pclass=ptype[mode], lon=np.zeros(npart), lat=np.zeros(npart)) pset.execute(pset.Kernel(DiffusionUniformKh), runtime=runtime, dt=delta(hours=1)) expected_std_lon = np.sqrt(2 * kh_zonal * mesh_conversion**2 * runtime.total_seconds()) expected_std_lat = np.sqrt(2 * kh_meridional * mesh_conversion**2 * runtime.total_seconds()) lats = pset.lat lons = pset.lon tol = 200 * mesh_conversion # effectively 200 m errors assert np.allclose(np.std(lats), expected_std_lat, atol=tol) assert np.allclose(np.std(lons), expected_std_lon, atol=tol) assert np.allclose(np.mean(lons), 0, atol=tol) assert np.allclose(np.mean(lats), 0, atol=tol)
def test_fieldKh_Brownian(mesh, mode, pset_mode, xdim=200, ydim=100, kh_zonal=100, kh_meridional=50): mesh_conversion = 1 / 1852. / 60 if mesh == 'spherical' else 1 fieldset = zeros_fieldset(mesh=mesh, xdim=xdim, ydim=ydim, mesh_conversion=mesh_conversion) fieldset.add_constant_field("Kh_zonal", kh_zonal, mesh=mesh) fieldset.add_constant_field("Kh_meridional", kh_meridional, mesh=mesh) npart = 1000 runtime = delta(days=1) ParcelsRandom.seed(1234) pset = pset_type[pset_mode]['pset'](fieldset=fieldset, pclass=ptype[mode], lon=np.zeros(npart), lat=np.zeros(npart)) pset.execute(pset.Kernel(DiffusionUniformKh), runtime=runtime, dt=delta(hours=1)) expected_std_lon = np.sqrt(2 * kh_zonal * mesh_conversion**2 * runtime.total_seconds()) expected_std_lat = np.sqrt(2 * kh_meridional * mesh_conversion**2 * runtime.total_seconds()) lats = pset.lat lons = pset.lon tol = 200 * mesh_conversion # effectively 200 m errors assert np.allclose(np.std(lats), expected_std_lat, atol=tol) assert np.allclose(np.std(lons), expected_std_lon, atol=tol) assert np.allclose(np.mean(lons), 0, atol=tol) assert np.allclose(np.mean(lats), 0, atol=tol)
def test_fieldKh_SpatiallyVaryingDiffusion(mesh, mode, pset_mode, kernel, xdim=200, ydim=100): """Test advection-diffusion kernels on a non-uniform diffusivity field with a linear gradient in one direction""" mesh_conversion = 1 / 1852. / 60 if mesh == 'spherical' else 1 fieldset = zeros_fieldset(mesh=mesh, xdim=xdim, ydim=ydim, mesh_conversion=mesh_conversion) Kh = np.zeros((ydim, xdim), dtype=np.float32) for x in range(xdim): Kh[:, x] = np.tanh(fieldset.U.lon[x] / fieldset.U.lon[-1] * 10.) * xdim / 2. + xdim / 2. + 100. grid = RectilinearZGrid(lon=fieldset.U.lon, lat=fieldset.U.lat, mesh=mesh) fieldset.add_field(Field('Kh_zonal', Kh, grid=grid)) fieldset.add_field(Field('Kh_meridional', Kh, grid=grid)) fieldset.add_constant('dres', fieldset.U.lon[1] - fieldset.U.lon[0]) npart = 100 runtime = delta(days=1) ParcelsRandom.seed(1636) pset = pset_type[pset_mode]['pset'](fieldset=fieldset, pclass=ptype[mode], lon=np.zeros(npart), lat=np.zeros(npart)) pset.execute(pset.Kernel(kernel), runtime=runtime, dt=delta(hours=1)) lats = pset.lat lons = pset.lon tol = 2000 * mesh_conversion # effectively 2000 m errors (because of low numbers of particles) assert np.allclose(np.mean(lons), 0, atol=tol) assert np.allclose(np.mean(lats), 0, atol=tol) assert (stats.skew(lons) > stats.skew(lats))
def test_randomexponential(mode, lambd, npart=1000): fieldset = zeros_fieldset() # Rate parameter for random.expovariate fieldset.lambd = lambd # Set random seed ParcelsRandom.seed(1234) pset = ParticleSet(fieldset=fieldset, pclass=ptype[mode], lon=np.zeros(npart), lat=np.zeros(npart), depth=np.zeros(npart)) def vertical_randomexponential(particle, fieldset, time): # Kernel for random exponential variable in depth direction particle.depth = ParcelsRandom.expovariate(fieldset.lambd) pset.execute(vertical_randomexponential, runtime=1, dt=1) depth = pset.depth expected_mean = 1. / fieldset.lambd assert np.allclose(np.mean(depth), expected_mean, rtol=.1)
# ADD THE PERIODIC BOUNDARY fieldset.add_constant('halo_west', -180.) fieldset.add_constant('halo_east', 180.) fieldset.add_periodic_halo(zonal=True) # ADD DIFFUSION if param['Kh']: fieldset.add_constant_field('Kh_zonal', param['Kh'], mesh='spherical') fieldset.add_constant_field('Kh_meridional', param['Kh'], mesh='spherical') # ADD MAXIMUM PARTICLE AGE (IF LIMITED AGE) fieldset.add_constant('max_age', param['max_age']) # SET SEED ParcelsRandom.seed(690) ############################################################################## # PARTICLE CLASS ############################################################# ############################################################################## class debris(JITParticle): ########################################################################## # TEMPORARY VARIABLES FOR TRACKING PARTICLE POSITION/STATUS ############## ########################################################################## # ISO code of current cell (>0 if in any coastal cell) iso = Variable('iso', dtype=np.int32, initial=0, to_write=False)