Movers¶
Processes that change the position of the particles are termed “movers” in PyGNOME. These can include advection of the particles due to winds and currents, diffusive movement of particles due to unresolved (sub-grid scale) turbulent flow fields, and prescribed behavior of the particles (e.g. rise velocity of oil droplets). Many of the movers derive their data from Environment Objects, which represent the environment in which the model is running. The environment objects are queried for information (winds, currents) at the particle location in order to move the particle appropriately.
Some examples and common use cases are shown here. For comprehensive documentation see gnome.movers
in the API Reference section.
Point Wind Mover¶
A gnome.movers.PointWindMover
will act uniformly on elements anywhere in the domain (i.e. they have no spatial variability). These movers are tied to a Wind Object in the Environment Class.
For example, in that section, we saw how to create a simple spatially and temporally constant gnome.environment.Wind
using a helper function in the gnome scripting module:
import gnome.scripting as gs
model = gs.Model(start_time="2023-03-03",
duration=gs.days(1),
time_step=gs.minutes(15)
)
wind = gs.constant_wind(10, 0, 'knots')
Now we create a gnome.movers.PointWindMover
by passing the Wind Object to the Mover Class and adding it to the model:
w_mover = gs.PointWindMover(wind)
model.movers += w_mover
Even though we didn’t explicitly add the wind to the model environment, when the mover is added to the model, the wind object will also be added. Any weatherers subsequently added to the model will use that wind by default (see next section).
To set uncertainty parameters if you are running with model uncertainty on:
w_mover = gs.PointWindMover(wind, uncertain_duration=3.0 * 3600, uncertain_time_delay=0,
uncertain_speed_scale=2.0, uncertain_angle_scale=0.4)
The uncertain_time_delay is in seconds from the model start_time. Only parameters that are set different from the defaults need to be included.
Some helper functions are available in gnome.scripting
for creating wind movers.
Many of these helper functions automatically create and add environment objects to the model.
For example, to create a wind mover from a single point time series in a text file:
w_mover = gs.point_wind_mover_from_file('wind_file.txt')
model.movers += w_mover
The format of the text file is described in the GNOME File Formats section. Briefly, it has 3 header lines, followed by comma separated data. An example is given here with annotations in brackets at the end of the lines:
22NM W Forks WA *(Location name, can be blank)*
47.904000, -124.936000 *(Latitude, longitude, can be blank)*
knots *(Units, eg: knots,mph,kph,mps)*
3, 3, 2023, 12, 0, 14.00, 200 *(day, month, year, hour, minute, speed, direction)*
3, 3, 2023, 13, 0, 16.00, 190
3, 3, 2023, 14, 0, 16.00, 190
Gridded movers¶
An example of implementing a simple current mover with a uniform current was described in the scripting Overview.
More commonly, currents used to move elements in GNOME originate
from models on regular, curvilinear, or unstructured (triangular) grids, as output from oceanographic or meteorological models.
Regardless of grid type, we use the gnome.movers.CurrentMover
class.
Similarly, winds can be derived from gridded meteorological models using the gnome.movers.WindMover
class.
These movers are tied to objects in the gnome.environment.Environment
which were described
more fully in the previous section. The primary supported format for gridded winds and currents is NetCDF. See the Gridded NetCDF formats section for more information.
Here’s an example of first building an environment object from a gridded wind:
fn = 'gridded_wind.nc'
wind = gs.GridWind.from_netCDF(filename=fn)
wind_mover = gs.WindMover(wind)
model.movers += wind_mover
The work flow is identical for adding a current. Alternatively, we could skip explicitly creating the environment object as the mover classes also have the “from_netCDF” method. For example:
fn = 'gridded_current.nc'
current_mover = gs.CurrentMover.from_netCDF(filename=fn)
model.movers += current_mover
In both cases, the corresponding environment object is also added to the model.
To set uncertainty parameters if you are running with model uncertainty on:
wind_mover = gs.WindMover(wind, uncertain_duration=3.0 * 3600, uncertain_time_delay=0,
uncertain_speed_scale=2.0, uncertain_angle_scale=0.4)
current_mover = gs.CurrentMover(current, uncertain_duration=3.0 * 3600, uncertain_time_delay=0,
uncertain_along=0.25, uncertain_cross=0.1)
The uncertain_time_delay is in seconds from the model start_time. Only parameters that are set different from the defaults need to be included.
To use a current or wind in multiple files pass in a Python list with the file names:
file_list = ['day1.nc',
'day2.nc',
'day3.nc',
'day4.nc',
'day5.nc',
'day6.nc',
]
current = gs.GridCurrent.from_netCDF(file_list)
current_mover = gs.CurrentMover(current=current)
model.movers += current_mover
The files must be in order and in netcdf3 format.
The default numerical method for the gridded movers is a 2nd-order Runge-Kutta. Other options are available by specifying the “default_num_method” when creating the mover object. For more information, see the gnome.movers.CurrentMover
api documentation.
A note on 3D simulations
If a netCDF file contains currents at multiple depth levels along with 3-d grid information, the corresponding GridCurrent object will be built to include that information and full 3D simulations can be run. If only one depth level is included, it will be assumed to be the surface and used accordingly. Wind files should ideally only contain surface (assumed 10 m) winds.
Random movers¶
Randoms movers can be added to simulate both horizontal and vertical turbulent motions (for 3d simulations). Diffusion coefficients can be explicitly specified or default values will be used. For example:
import gnome.scripting as gs
random_mover = gs.RandomMover(diffusion_coef=10000) #in cm/s
model.movers += random_mover
# Or, for a 3D simulation
random_mover_3d = gs.RandomMover3D(vertical_diffusion_coef_above_ml=10,vertical_diffusion_coef_below_ml=0.2,\
mixed_layer_depth=10, horizontal_diffusion_coef_above_ml=10000,\
horizontal_diffusion_coef_below_ml=100) #diffusion coefficients in cm/s, MLD in meters
model.movers += random_mover_3d
# With model uncertainty turned on, change the diffusion uncertainty, default = 2
random_mover = gs.RandomMover(diffusion_coef=10000, uncertain_factor = 3) #diffusion coefficients in cm/s
model.movers += random_mover
Rise velocity movers¶
The rise velocity mover depends on parameters specified when setting up a subsurface spill (see Spills). For example, the rise velocities can be calculated based on the droplet size for each element and the density of the specified oil.
This information is associated with the spill object, hence creating a RiseVelocityMover
is relatively simple.:
import gnome.scripting as gs
rise_vel_mover = gs.RiseVelocityMover()
model.movers += rise_vel_mover
A distribution of rise velocities can also be explicitly specified – again this is done when initializing the subsurface release. To make all elements have the same rise velocity, we specify a uniform distribution with the same value for high and low parameters. Various distributions are available in gnome.utilities.distributions
.
Here’s a complete example where all elements will have a 1 m/s rise velocity:
import gnome.scripting as gs
from gnome.utilities.distributions import UniformDistribution
start_time = gs.asdatetime("2023-03-03")
model = gs.Model(start_time=start_time,
duration=gs.days(3),
time_step=60 * 15, #seconds
)
ud = UniformDistribution(1,1)
spill = gs.subsurface_spill(num_elements=1000,
start_position=(-144,48.5,-1000.0),
release_time=start_time,
distribution=ud,
distribution_type='rise_velocity',
end_release_time = start_time + gs.days(1),
amount=5000,
units='bbl',
name='My spill')
model.spills += spill
rise_vel_mover = gs.RiseVelocityMover()
model.movers += rise_vel_mover
model.full_run()
Ice modified movers¶
The presence of ice modifies the movement of the oil on the water surface. For example, in high ice concentrations, the oil may be encapsulated in the ice, and move with the ice drift velocity. To incorporate the presence of ice requires the creation of environment objects that include the relevant information (e.g., ice concentration and ice velocity along with currents and winds). We term these “IceAware” environment objects (see previous section for more detail). Once the environment objects have been created, movers can be created based on them using the same approach described above. For example:
ice_aware_current = gs.IceAwareCurrent.from_netCDF('file_with_currents_ice.nc')
ice_current_mover = gs.CurrentMover(ice_aware_current)
Here’s a more complete example with ice modified movers:
import gnome.scripting as gs
start_time = "1985-01-01T13:31"
model = gs.Model(start_time=start_time,
duration=gs.days(2),
time_step=60 * 15, #seconds
)
spill = gs.surface_point_line_spill(num_elements=1000,
start_position=(-163.75,69.75,0),
release_time=start_time)
model.spills += spill
fn = [gs.get_datafile(data_dir / 'arctic_avg2_0001_gnome.nc'),
gs.get_datafile(data_dir / 'arctic_avg2_0002_gnome.nc'),
]
gt = {'node_lon': 'lon',
'node_lat': 'lat'}
ice_aware_curr = gs.IceAwareCurrent.from_netCDF(filename=fn,
grid_topology=gt)
ice_aware_wind = gs.IceAwareWind.from_netCDF(filename=fn,
grid=ice_aware_curr.grid,)
i_c_mover = gs.CurrentMover(current=ice_aware_curr)
i_w_mover = gs.WindMover(wind=ice_aware_wind)
model.movers += i_c_mover
model.movers += i_w_mover
model.movers += gs.IceAwareRandomMover(ice_concentration=ice_aware_curr.ice_concentration,
diffusion_coef=50000)
model.full_run()
You can find a complete script using ice modified movers in:
pygnome/py_gnome/scripts/example_scripts/ice_example.py
Or download it here:
ice_example.py
CATS Movers¶
CATS is a NOAA/ORR hydrodynamic model that is unlikley to be used by others. Documentation forthcoming. To see some examples for how to use CATS movers in a script see CATS currents.