Plotting agents in a domain#
Summary
On this page, you will see how to
use
.plot.abm
to plot snapshots and animations of agents in a domainplot data variables onto the agent marker size, orientation, and color
add fading trails to the agents moving around the domain
adjust the plot for periodic boundary conditions
adjust the plot domain
Complete example: Agents in a domain (SimpleFlocking model)
agents:
based_on:
- .creator.universe
- .plot.abm
- .animation.ffmpeg
- .animation.fps30
# Prepare data
select:
x:
path: agent/x
transform: [ .data ]
allow_failure: silent
fallback: !dag_tag _skip_plot
y:
path: agent/y
transform: [ .data ]
orientation:
path: agent/orientation
transform: [ .data ]
space_extent:
path: agent/x # meta data is written to every other dataset as well
transform:
- getattr: [!dag_prev , attrs]
- getitem: [!dag_prev , space_extent]
transform:
# Combine data into one dataset
- xr.Dataset:
- x: !dag_tag x
y: !dag_tag y
orientation: !dag_tag orientation
tag: agents
# Data encoding
x: x
y: y
frames: time
orientation: orientation
hue: orientation
# Define what to plot
to_plot:
agents:
marker: wedge
# Pass on space extent (used for setting limits)
domain:
extent: !dag_result space_extent
pad: 0
# Set tail lengths, style, and decay factor
tail_length: 24
tail_decay: 0.12
tail_max_segment_length: 1. # to avoid problems when crossing a boundary
tail_kwargs:
color: black
linewidth: 0.5
alpha: .6
zorder: -10
# Colorbar settings
vmin: -3.14159
vmax: +3.14159
cbar_labels:
+3.14159: $+π$
+1.570795: $+π/2$
0: $0$
-1.570795: $+π/2$
-3.14159: $-π$
Agent-based models (ABMs) are a common type of model to simulate and analyze the behaviour of individual agents
moving around in space, interacting with each other, and adapting their behaviour or even learning in the process.
Utopia uses utopya’s .plot.abm
plot (implemented in utopya as abmplot()
) to visualize the behavior of such agents.
We will use the SimpleFlocking ABM to illustrate its functionality.
See also
The implementation of this plot is in utopya.eval
, which is where you can find details about the available arguments and their behavior:
Implementation of
.plot.abm
:utopya.eval.plots.abm.abmplot()
Helper function that draws individual agents:
utopya.eval.plots.abm.draw_agents()
Custom matplotlib collection for individual agents:
utopya.eval.plots.abm.AgentCollection
Animated agents in a domain#
Below is an example animation of flocking dynamics in a square domain with periodic boundary conditions:
The agents’ color represents their orientation angle, and the fading trails their trajectory over a number of past steps.
Such animations can be easily generated using the .plot.abm
base plot.
The plot requires a single xarray.Dataset
or an xarray.DataArray
to be passed containing all the plotting data.
Base your plot on the universe creator (.creator.universe
), .plot.abm
, as well as an animation base configuration (see the
animations page for more details on these, including on adjusting the animation resolution):
agents_in_domain:
based_on:
- .creator.universe
- .plot.abm
- .animations.ffmpeg
select:
agents: path/to/agent/data
Next, specify what to plot, and where to plot the variables:
agents_in_domain:
# Everything from above ...
to_plot:
agents:
marker: wedge
x: x
y: y
frames: time
orientation: orientation
hue: orientation
This plots the variables onto the corresponding dimensions.
The to_plot
argument is required; it can also contain information on the agent markers, colors, and size scales – see below.
Naturally, this requires the data we selected to contain variables called x
, y
, time
, and orientation
(which can be mapped to other variables using the corresponding keyword arguments).
Hint
Arguments on the top-level of the plot config are used as defaults for all entries within to_plot
.
If the individual to_plot
entries have different encodings, you can also specify those arguments there:
agents_in_domain:
# ...
to_plot:
agents:
# custom parameters for this layer
x: y
y: x
If you have saved these datasets separately, use the transformation DAG to combine them into a single xarray.Dataset
:
agents_in_domain:
based_on:
# As before ...
select:
x:
path: path/to/x/coordinates
transform: [.data] # Transform into a xr.DataArray
y:
path: path/to/y/coordinates
transform: [.data]
# analogously for other variables ...
# Combine data into one dataset
transform:
- xr.Dataset:
- x: !dag_tag x
y: !dag_tag y
# other variables ...
tag: agents
to_plot:
agents:
marker: wedge
Hint
You can turn off the time stamp by setting
suptitle_fstr: False
Plotting frames and snapshots#
You can of course choose specific time steps to use for the animation. To do this, pass the frames_isel
keyword:
agents_in_domain:
# Everything else as above ...
frames_isel: !range [10]
This will plot the first ten time steps.
Alternatively, you can pass a list of frames.
If you base the plot on animation.frames
and do not pass frames_isel
, all frames will be plotted as individual images.
You can also plot snapshots instead of an animation.
To do this, add the .plot.abm.snapshot
base configuration; then, select the time frames to plot with frames_isel
:
agents_in_domain:
based_on:
- .creator.universe
- .plot.abm
- .plot.abm.snapshot
# Snapshot of the final state (default)
frames_isel: -1
Agent hue, orientation, size, and markers#
As we saw above, the orientation
and hue
of the agents can be used to visualize data variables. You can then specify
a colormap using the ColorManager:
agents_in_domain:
# Everything as above
cmap:
continuous: true
from_values:
0.0: darkgreen
0.333: yellow
0.666: brickred
1.0: darkgreen
Colors can also be specified e.g. as hex or rgb values. You can also simply pass the name of a colormap, use a norm, and include limits: take a look at the style section for more details.
Additionally, you can plot data dimensions onto the agent size
. The size scale and marker style of the agents is determined via the size_scale
key in the to_plot
entry, which roughly corresponds to the area of the marker in relation to the whole domain:
agents_in_domain:
# Everything as above ...
size: some_dimension
to_plot:
agents:
size_scale: 0.0002
marker: fish2
The marker
argument can be any one of wedge
, fish
(a basic fish shape), and fish2
(a more complex fish shape); see MARKERS
for more information and other available marker paths:
Hint
See AgentCollection
for more possibilities on how to set up the marker
.
Adding trails#
Trails can be useful to visualize the trajectories of the agents, especially in flocking or chemotaxis models.
You can control the length of the tail with the tail_length
entry. The aesthetics of the tail are controlled via the tail_kwargs
dict:
agents_in_domain:
tail_length: 12
tail_kwargs:
color: black
linewidth: 0.5
alpha: .6
zorder: -10
This sets the tail_length
to 12 frames of the animation.
The alpha
value is applied uniformly along the length of the tail.
To get a fading effect, add the following key:
agents_in_domain:
tail_decay: 0.12
This will set the alpha value of each tail segment to 1 - tail_decay
times the alpha value of the previous segment, giving an exponential fade.
Periodic boundary conditions#
The tail_max_segment_length
parameter is useful if you plan on drawing tails of agents that move in a periodic space.
In such a case, agent positions may jump aprubtly when crossing a boundary.
Ordinarily, this would lead to the tail segment going across the whole domain.
To avoid this, set the tail_max_segment_length
parameter to half the domain size; this typically suffices to detect jumps in x- or y- position and leads to these segments not being drawn.
(To be precise, the length refers not to that of the segment but to the differences in x- and/or y-position.)
Adjusting the plot domain#
Instead of using the PlotHelper
to adjust the x- and y-extent, we recommend using the domain/extent
key; this way,
marker size will be kept constant. This key also allows setting the padding to the border of the domain, the height, and
the aspect of the domain:
agents_in_domain:
domain:
extent: [0, 4, 1, 10]
pad: 0.5
height: 3 # height in data units
aspect: 2 # can also be 'auto' if height is not given
The extent
entry should be a tuple of the form (left, right, bottom, top)
.
Alternatively, a 2-tuple will be interpreted as (0, right, 0, top)
.
Finally, you can also add a mode
key to the domain
dictionary to control automatic deduction of boundaries.
The domain mode can be auto
, manual
, fixed
, and follow
.
In fixed
mode, all available data is inspected to derive domain bounds.
In follow
mode, boundaries are adjusted from frame to frame.
In auto
mode, will use a fixed
domain if no extent
was given, otherwise manual
mode is used.