Creating Particles and Particle Types
The particle is the most basic physical object of Tissue Forge. A particle occupies a point in space and necessarily has a mass. Forces acts on a particle and cause a corresponding change in its trajectory according to the dynamics of a simulation. A particle has a specified radius for various modeling and visualization purposes, but has no intrinsic volume. Like the radius property, a particle has other properties that can be assigned to it that have no meaning except within the context of a model. Depending on the application, a particle can represent an atom, a molecule, a point on a surface or in a lattice, a part of a cell, a cell, a parcel of material, or something else with a dynamic position. Particles in Tissue Forge can be created and destroyed, interact with each other and other objects, and even carry cargo that can be transported between particles.
Particle Types
The only meaning that Tissue Forge assigns to particles is that each particle is an instance of a particle type. A particle type defines a template definition from which particles can be created, and a particle type is an identifier of a corresponding group of particles in a simulation such that all particles can always be grouped according to a set of particle types. A particle can change type, and particle processes, interactions and properties in Tissue Forge can be specified on the basis of individual particles, but each particle always has a type, and the particle type defines the initial properties of the particle that the particle type can create.
As such, the basic specification of particle properties in Tissue Forge
occurs through the specification of particle types.
Every particle type is a subclass of the Tissue Forge class
ParticleType
. Each particle type in a simulation is an
object in memory that is instantiated and can be used for various tasks
(e.g., to create particles). However, unlike a particle,
an instance of a particle type is not a model object, but rather
a dynamic model definition and simulation entry point for specifying
particle properties on the basis of type (e.g.,
particle dynamics)
and doing type-related operations (e.g.,
finding all particles of a type).
Furthermore, Tissue Forge only recognizes particle types that have
been registered with Tissue Forge for a simulation, and there is exactly
one instance of each registered particle type that Tissue Forge works
with during a simulation.
ParticleType
and each subclass of it has a special method
get
that retrieves the instance of the
particle type that Tissue Forge is using in a simulation (assuming the type
has been registered), which is not necessarily an instance of a particle
type that might be created during simulation.
This way, the working instance of a registered particle type can be
retrieved from Tissue Forge and interacted with by referring to the
corresponding class definition of the particle type.
Tissue Forge provides a few ways to create and register particle types.
The recommend method of creating a particle type in Python is to create a class
definition that is a subclass of ParticleTypeSpec
, and likewise in C
to particularize a struct tfParticleTypeSpec
. In C++, a class can be directly
derived from a ParticleType
.
The properties of a particle type can be changed at anytime during a simulation
without issue, with the exception of its name. For many reasons related to
computational performance and model accessibility, the name of a
particle type can be set during a simulation. However, doing so after
registering a particle type will result in undefined behavior and
likely cause a simulation to fail.
In Python, particle types can be specified using a class definition
that derives from the class ParticleTypeSpec
, and properties
can be specified as class attributes,
import tissue_forge as tf
class MyParticleType(tf.ParticleTypeSpec):
mass = 1.0
radius = 1.0
dynamics = tf.Overdamped
A new particle type can be simultaneously instantiated and registered
with Tissue Forge in Python with the class method ParticleTypeSpec.get
,
my_particle_type = MyParticleType.get()
Note
In Python, ParticleTypeSpec
is not the same as
ParticleType
. Rather, it is a convenience class that
automates the process of creating, registering and retrieving a
ParticleType
instance with Tissue Forge using the class method
get
, which always returns the actual registered
ParticleType
instance without ambiguity. A
ParticleTypeSpec
instance can be instantiated in the typical
way and operated on without any need for the Tissue Forge engine, so
long as get
is not called on the instance.
Furthermore, additional specifications can be made on a ParticleTypeSpec
class definition. However, self
in a ParticleTypeSpec
method
does not refer to the corresponding ParticleType
instance
registered with Tissue Forge.
Particle type definitions can then be changed on-the-fly in Python for particles created later in simulation,
# Changing back to default dynamics!
my_particle_type.dynamics = tf.Newtonian
In C++, particle types can be specified using a class definition
that derives from the class ParticleType
, and properties
can be specified as members during instantiation,
#include <TissueForge.h>
using namespace TissueForge;
struct MyParticleType : ParticleType {
MyParticleType() : ParticleType(true) {
mass = 1.0;
radius = 1.0;
dynamics = PARTICLE_OVERDAMPED;
registerType();
}
};
Note that registerType
is how particle types are
registered with Tissue Forge. The call to registerType
in the
constructor is optional, and can instead be called after
instantiation of the particle type (i.e., subsequent attempts to
register the type are ignored).
A registered particle type can be retrieved from Tissue Forge in C++
with the class method ParticleType::get
, the returned
pointer of which is of type ParticleType
that can be
safely cast to the new particle type (assuming no conflicting
additional specifications on the class definition),
MyParticleType *myParticleType = new MyParticleType();
myParticleType = (MyParticleType*)myParticleType->get();
Particle type definitions can then be changed on-the-fly in C++ for particles created later in simulation,
// Changing back to default dynamics!
myParticleType->dynamics = PARTICLE_NEWTONIAN
A particle type can also be created on the fly using a unique name, and the unique name can be used to retrieve the registered particle type instance from Tissue Forge. In Python,
another_particle_type = tf.ParticleType.newType('AnotherParticleType')
another_particle_type.registerType()
another_particle_type = tf.ParticleType_FindFromName('AnotherParticleType')
Particles
Particle type instances function like factories of particles. Each particle type instance can be called like a function to create exactly one new particle. Such a call returns a handle to the newly created particle. Referring to the previous examples in Python,
particle_handle = my_particle_type(position=[1.0, 2.0, 3.0], velocity=[0.0, 0.0, 1.0])
# Change the mass of this particle
particle_handle.mass = 0.5
When an initial position or velocity is not specified while creating
a particle, it is randomly selected. Initial position, when randomly
selected, is always within the universe. Initial velocity, when
randomly selected, has a random direction but speed such that the
initial kinetic energy of the particle is equal to the particle type
property target_temperature
(in C++, target_energy
).
Properties of individual particles can be set at any time during simulation, including
mass
, velocity
and
radius
. While most vector quantities like
velocity
can be set both by vector and vector component,
the particle position
requires a special exception
where it can only be set using a full vector value, which helps Tissue Forge maintain
critically important aspects of its computational performance.
particle_handle.radius *= 2 # Double the particle radius
particle_handle.velocity[0] = 0 # Stop particle motion along the x-direction
# Set the y-component of the particle position
position = particle_handle.position
position[1] = 2
particle_handle.position = position
Note
When the radius of a particle exceeds the global cutoff distance, the particle is classified as a large particle. While some scenarios may warrant the usage of large particles, their support in Tissue Forge is limited and, in general, their usage is discouraged in favor of adjusting the size, scale and cutoff distance of the simulation such that all particles are smaller than the cutoff distance. For more information, see Numerical Details.
Clusters
A cluster is a special kind of particle that contains other particles, including other clusters, with a corresponding base cluster type. The cluster and cluster type are extensions of the particle and particle types, respectively, and so the same properties and methods are available to each along with the additional descriptions supporting this idea of a cluster.
In general, operations concerning particles and particle types are
not concerned with the distinction between a particle and a cluster.
As such, operations can return a cluster or cluster type instance
as a particle or particle type instance. To handle any ambiguity
about whether a particle is actually a cluster, each particle type
has a method isCluster
that
returns true
if the particle type is a cluster type. In such cases,
corresponding particles can be safely cast to a cluster type in appropriate
languages. In Python, this cast can be accomplished with the particle method
to_cluster
, which returns a cluster instance
of the same underlying particle. Likewise the get
method of cluster types in Python correctly returns instances of the cluster type.
Clusters also have unique properties (e.g., center of mass, particle inventory) that are derived from their constituent particles, and unique processes that involve their constituent particles (e.g., cleavage). Clusters can also interact with other particles on the basis of particle types and cluster ownership.
Defining Clusters
The class ClusterParticleType
corresponds
to ParticleType
for clusters, and the class Cluster
corresponds to Particle
. New cluster types can be specified in
the same way as particle types, with the additional specification of
other particle and cluster types that constitute it with the
cluster type property types
.
import tissue_forge as tf
class ConstitutiveType(tf.ParticleTypeSpec):
radius = 0.1
constitutive_type = ConstitutiveType.get()
class MyClusterType(tf.ClusterParticleTypeSpec):
radius = 1.0
types = [constitutive_type]
In C++, cluster particle types can be created by deriving a new class definition
from ClusterParticleType
and adding type ids to the member types
.
Similar procedures can be accomplished in C using operations defined for
struct tfClusterParticleTypeHandle
(see tfCCluster.h).
Creating with Clusters
Clusters have the unique ability to function like particle types in that they, like particle types, can create new constituent particles, and in a similar way. A particle of a constituent particle type of a cluster can be created using the cluster in the same way as when creating with a particle type, but while passing the constituent particle type to be created. Constituent particles created by a particular cluster belong to that cluster,
my_cluster_type = MyClusterType.get()
cluster_particle = my_cluster_type(position=[1.0, 2.0, 3.0])
const_part = cluster_particle(particle_type=my_cluster_type, position=[2.0, 2.0, 3.0])