How to use the Rings system
To use the rings system we need to do the following:
- Define dynamical parameters (interaction parameters such as springs stiffness, activity, etc)
- Create initial condition (positions and polarizations at initial time)
- Define space configuration (geometry plus boundary behavior)
- Set numerical parameters (time step size, box size for space partitioning, etc)
let's address one item at a time.
1. Dynamical parameters
We need to configure the interaction potential between particles and the rings properties, in code this looks like this
# Using the module which contains things related
# to model configurations
using Mavi.Rings.Configs
# Creating an object which contains the parameters related
# to how particles interact. Specifically, we are
# creating configurations for a harmonic truncated
# potential.
interaction_cfg = HarmTruncCfg(
k_rep=20,
k_atr=4,
dist_eq=1,
dist_max=1 + 0.2,
)
# Creating the object which contains all
# dynamical configurations
dynamic_cfg = RingsCfg(
p0=3.5,
relax_time=1,
vo=1,
mobility=1,
rot_diff=0.05,
k_area=1,
k_spring=20,
l_spring=1,
interaction_finder=interaction_cfg,
)2. Initial Condition
We need to specify the initial particles positions and rings polarizations. These two quantities are used to create an object of type RingsState. We can do that manually or use a helper function such as rectangle_grid, which returns rings positioned in the vertices of a rectangular grid (this function also returns a geometry configuration, which we will talk about in the next section). Use example
using Mavi.Rings.States
using Mavi.Rings.InitStates
num_cols = 3
num_rows = 3
rings_pos, geometry_cfg = rectangular_grid(
num_cols=num_cols, # How many columns the grid has
num_rows=num_rows, # How many rows the grid has
num_particles=10, # How many particles each ring has
p_radius=1, # Rings particles radius
pad_x=0.1, pad_y=0.1, # Spacing between rings in the x and y direction
)
# Creating the initial state
num_rings = num_cols * num_rows
state = RingsState(
rings_pos=rings_pos,
pol=random_pol(num_rings), # creating random polarizations using the helper function `random_pol`
)
OBS: Usually, the particle radius is determined by the dynamical configuration, so one can use the function
Configs.particle_radius(which is available if we are usingMavi.Rings.Configs) to get the particle radius. Ex:pr = Configs.particle_radius(dynamic_cfg)wheredynamic_cfgis the object we created in section 1
3. Space configuration
A space configuration in the combination of a geometry configuration and boundary behavior, for instance, if we want a rectangular geometry with periodic boundaries, we would do the following
using Mavi.Rings.Configs
geometry_cfg = Configs.RectangleCfg(
length=10,
height=10,
)
wall_type = Configs.PeriodicWalls()
space_cfg = Configs.SpaceCfg(
geometry_cfg=geometry_cfg,
wall_type=wall_type,
)the helper function rectangular_grid also returns a rectangular geometry configuration which contains all rings created, so we can use that and only set the boundary behavior
rings_pos, geometry_cfg = rectangular_grid(...)
space_cfg = Configs.SpaceCfg(
geometry_cfg=geometry_cfg,
wall_type=Configs.PeriodicWalls(),
)OBS: three dots in
rectangular_gridrepresent omitted code.
4. Numerical parameters
The last thing we need to do is to set numerical parameters (parameters used to perform the numerical integration). The minimum set of parameters we need to configure is the time step
using Mavi.Rings.Configs
int_cfg = RingsIntCfg(dt=0.01)but it is very beneficial to performance the use of a chunks technique (space is partitioned into chunks and interactions are only computed between neighboring chunks). For that, we only need to specify the chunks size, which should be at least the size of the maximum interaction distance (which we can get from dynamic_cfg)
using Mavi.Rings.Configs
interaction_cfg = dynamic_cfg.interaction_finder # This is the interaction potential configuration between particles
bbox = space_cfg.geometry_cfg # bbox stands for bounding box
max_size = interaction_cfg.dist_max * 1.1 # Setting the size to be a little bit bigger than the maximum interaction distance just to be safe
# Calculating how many chunks we want in each direction
num_x_chunks = floor(Int, bbox.length / max_size)
num_y_chunks = floor(Int, bbox.height / max_size)
p_chunks_cfg = Configs.ChunksCfg(num_x_chunks, num_y_chunks)
# Creating the integration configurations object
int_cfg = RingsIntCfg(
dt=0.01,
p_chunks_cfg=p_chunks_cfg,
)Visualizing the simulation
Now we can put everything together in a RingsSystem and start the simulation with real-time rendering
# Importing everything we need
using Mavi.Rings
using Mavi.Rings.Configs
using Mavi.Rings.InitStates
using Mavi.Visualization
# =
# Dynamical Configuration
# =
interaction_cfg = HarmTruncCfg(
k_rep=20,
k_atr=4,
dist_eq=1,
dist_max=1 + 0.2,
)
dynamic_cfg = RingsCfg(
p0=3.5,
relax_time=1,
vo=1,
mobility=1,
rot_diff=0.05,
k_area=1,
k_spring=20,
l_spring=1,
interaction_finder=interaction_cfg,
)
# =
# Initial State
# =
num_cols = 3
num_rows = 3
rings_pos, geometry_cfg = rectangular_grid(
num_cols=num_cols, # How many columns the grid has
num_rows=num_rows, # How many rows the grid has
num_particles=10, # How many particles each ring has
p_radius=Configs.particle_radius(dynamic_cfg), # Rings particles radius
pad_x=0.1, pad_y=0.1, # Spacing between rings in the x and y direction
)
num_rings = num_cols * num_rows
state = RingsState(
rings_pos=rings_pos,
pol=random_pol(num_rings), # creating random polarizations using the helper function `random_pol`
)
# =
# Space Configuration
# =
space_cfg = Configs.SpaceCfg(
geometry_cfg=geometry_cfg,
wall_type=Configs.PeriodicWalls(),
)
# =
# Integration Configuration
# =
interaction_cfg = dynamic_cfg.interaction_finder # This is the interaction potential configuration between particles
bbox = space_cfg.geometry_cfg # bbox stands for bounding box
max_size = interaction_cfg.dist_max * 1.1 # Setting the size to be a little big bigger than the maximum interaction distance just to be safe
# Calculating how many chunks we want in each direction
num_x_chunks = floor(Int, bbox.length / max_size)
num_y_chunks = floor(Int, bbox.height / max_size)
p_chunks_cfg = Configs.ChunksCfg(num_x_chunks, num_y_chunks)
# Creating the integration configurations object
int_cfg = RingsIntCfg(
dt=0.01,
p_chunks_cfg=p_chunks_cfg,
)
# =
# Creating the System
# =
system = RingsSystem(
state=state,
space_cfg=space_cfg,
dynamic_cfg=dynamic_cfg,
int_cfg=Configs.RingsIntCfg(
dt=0.01,
p_chunks_cfg=Configs.ChunksCfg(num_x_chunks, num_y_chunks),
device=Configs.Threaded(),
),
)
# =
# Animating the system
# =
animate(system)