The basics

If you haven't installed Flory yet, follow the installation guide.

All examples have the same orbit controls: drag to orbit, scroll to zoom and right click to pan. Make sure you can see the entire example for it to run.

Setting up

The general structure of a Flory simulation is simple:

  1. Instantiate a descendant of Flory.Environment
  2. Add a bunch of Flory.Entity objects to your environment with Flory.Environment.add(entity).
  3. Call Flory.Environment.advance in a loop to advance the time step and simulate according to the Flory.Environment.update function.

To visualize a simulation you need to call Flory.Environment.enableVisualization(canvas,data) with a canvas id and data object containing rendering settings. Similarly, to stop the visualization call Flory.Environment.disableVisualization.

Custom Environments

Flory provides a range of intuitive interfaces for creating simulations.

There are two ways to set up custom simulations:

  1. Create your own descendant of Flory.Environment.
  2. Make use of handlers in the built-in Flory.Newtonian environment to define you own single-entity-wise update functions.



//Simulate a single particle random walk var env = new Flory.RandomWalk() var monomer = new Flory.Monomer({ radius: 1, charge: 0, mass: 1, position: [0, 0, 0], name: "atom" }) env.add(monomer) env.enableVisualization("basic-example-canvas") setInterval(function(){ //This will update the positions of the particles //and render any thing that is renderable that was called with //env.add env.advance() },100)

Fields

Fields are exclusive to the Flory.Newtonian environment. They can be used to define pair-wise potentials, global potentials or discrete global potentials.

All fields implement the properties below.

Value Description Type
data.field the function or list of {point,force} objects that define this field. function or array
getForce(entity) Calculates and returns the force on the given Flory.Particle based on its properties. This is implemented differently depending on the type of field. function
scale(num) Globally multiplies the magnitude of the field by num function
clone Returns a clone of the field. function

Continuous Field

Using Flory.Newtonian you can abstract global continuous fields. To declare a Flory.ContinuousField, simply provide it with a function that takes a Flory.Particle object and returns the force applied on the object.



//Simulate a central 1/r^2 potential var env = new Flory.Newtonian(); var field = new Flory.ContinuousField(function(entity) { return entity.position.clone().negate().normalize().mult(250000./entity.position.lengthSq()); }) env.add(field); var monomer = new Flory.Monomer({ radius: 1, charge: 0, mass: 1, position: [0, 40, 0], velocity: [100, 0, 0], name: "atom" }); env.add(monomer); env.enableVisualization("basic-continuous-field-canvas"); setInterval(function(){ env.advance(); }, 10)

Pair-wise fields

Using Flory.Newtonian you can abstract pair wise fields. To declare a Flory.PairWiseField, simply provide it with a function that takes 2 Flory.Particle objects and returns the force applied on the first particle.



// Simulate gravity for a 4 body system with initial velocities var env = new Flory.Newtonian(); var field = new Flory.PairWiseField(function(entity_1,entity_2) { var r = entity_1.position.clone().sub(entity_2.position).negate(); return r.clone().normalize().mult((entity_1.mass*entity_2.mass)/r.lengthSq()) }) env.add(field); var monomer_info = { radius: 1, charge: 0, mass: 10000, position: [0, 40, 0], velocity: [23, 0, 0], name: "atom1" } env.add(new Flory.Monomer(monomer_info)); monomer_info.position = [0, -40, 0]; monomer_info.velocity = [-23, 0, 0]; monomer_info.name = "atom2"; env.add( new Flory.Monomer(monomer_info)); monomer_info.position = [-40, 0, 0]; monomer_info.velocity = [0, 0, -23]; monomer_info.name = "atom3"; env.add(new Flory.Monomer(monomer_info)); monomer_info.position = [40, 0, 0]; monomer_info.velocity = [0, 0, 23]; monomer_info.name = "atom4"; env.add( new Flory.Monomer(monomer_info)); env.enableVisualization("basic-pairwise-field-canvas"); setInterval(function(){ env.advance(); }, 10)

Field 2-D

Using Flory.Newtonian you can abstract a discrete 2-D field. To declare a Flory.Field2D, simply provide it with an list of objects with the properties position and vector. Where vector is the force associated at position position

Flory.Field2D.getForce(entity) returns the force associated with the closest position to the entity.position.



//Simulate a discrete toroidal field var range = function(start, stop, step) { var a = [start]; while (start < stop) { start += step || 1; a.push(start); } return a; }; var env = new Flory.Newtonian(); var x_coords = range(-100,100,1); // returns list of [0,100] in steps of 1 var y_coords = range(-100,100,1); var field_info_cross = []; var field_info_central = []; //Generate 2 potentials: //field_cross that is the cross product of the position vector and [0,0,1]. //field_center which is points opposite to the position vector. for (var i = 0; i < x_coords.length; i++) { for (var j = 0; j < y_coords.length; j++) { var position = new Flory.Vector2(y_coords[i],x_coords[j]); var force_cross = position.clone().cross([0,0,1]).normalize().mult(10); field_info_cross.push({ position: position, vector : force_cross }) var force_central = position.clone().negate().mult(1); field_info_central.push({ position: position, vector : force_central }) }; }; var field_cross = new Flory.Field2D(field_info_cross) var field_central = new Flory.Field2D(field_info_central) env.add(field_cross); env.add(field_central); var monomer = new Flory.Monomer({ radius: 1, charge: 0, mass: 1, position: [0, 40], name: "atom" }); env.add(monomer); env.enableVisualization("basic-field-2d-canvas"); env.advance()

Built in environments

Flory comes with built-in environments for you to explore.

Random Walk

The random walk allows you define a random walk experiment.

To start the random walk experiment, instantiate a Flory.RandomWalk(data) object and add entities. The valid values for data object are below.

Value Description Default Type
seed The seed for the Mersenne Twister random generator. current time Float
step_size The size of the step for each call to Flory.RandomWalk.advance 1 Float


//Simulate 30 particles undergoing non-interacting random walks var env = new Flory.RandomWalk(); var monomer_info = { radius: 1, charge: 0, mass: 1, position: [0, 0, 0] } for (var i = 0; i < 30; i++) { env.add(new Flory.Monomer(monomer_info)); }; env.enableVisualization("basic-random-walk-canvas"); setInterval(function(){ env.advance(); }, 10)

Lennard Jones

This defines an optimized Lennard-Jones potential.

To start a Lennard-Jones experiment, instantiate a Flory.LennardJones(data) object and add entities. The valid values for data object are below.

Value Description Default Type
epsilon The value of epsilon in the Lennard Jones potential. 1 Float
sigma The value of sigma in the Lennard Jones potential. 1 Float
far_cutoff_distance The cutoff distance at which the potential between two entities will not be calculated. 100 Float


// Simulate a cube of 9 particles with the lennard jones potential var our_canvas = "basic-lennard-jones-canvas"; var epsilon = 1; var sigma = 2; var lennard = new Flory.LennardJones(epsilon, sigma); var distance_apart = 4; var side_len = 3; var monomer_info = { radius : 1, charge : 0, mass : 1, position : [ 0, 0, 0 ] } var i,j,k; for (i = 0; i < side_len; i += 1) { for (j = 0; j < side_len; j += 1) { for (k = 0; k < side_len; k += 1) { monomer_info.position = [i * distance_apart,j * distance_apart,k * distance_apart]; lennard.add(new Flory.Monomer(monomer_info)); } } } lennard.enableVisualization(our_canvas); setInterval( function(){ lennard.advance() },1)

Newtonian

The Newtonian environment simulates an environment where forces applied to objects are used to calculate the change in velocity and position.

It is compatible with the Flory.baseField descendants so you can define custom global potentials, or pair wise potentials.

If we want a simple potential, we need not define a field, we can simply pass an anonymous function handler when we instantiate the Flory.Newtonian object which will be called for all entities during the main loop.

To start a Newtonian experiment, instantiate a Flory.Newtonian(handler) object and add entities. The handler object should be a valid function, however it can be left undefined.



// Simulate an r^2 central potential with a continuous field and handler function var env = new Flory.Newtonian({ update: function(entity) { return entity.position.clone().negate().normalize().mult(250000./entity.position.lengthSq()); } }); var field2 = new Flory.ContinuousField(function(entity) { return Flory.getRandomVector(100,100,100); }) env.add(field); env.add(field2); var monomer = new Flory.Monomer({ radius: 1, charge: 0, mass: 1, position: [0, 40, 0], velocity: [100, 0, 0], name: "atom" }); env.add(monomer); env.enableVisualization("basic-newtonian-canvas"); setInterval(function(){ env.advance(); }, 10)

Vector

Flory provides a range of vector objects that are optimized for ease of use and performance.

Depending on the specific class of vector used, different properties are available. However, all vector classes are guaranteed to contain the properties below.

The arguments provided for any function properties need not be of the same dimensional vector class.

Unless otherwise stated, if a function returns a vector, then it will be of the same class as the callee.

Value Description Type
components An array containing the components of the vector. Array
dimension Returns the dimension of the vector. function
add(a) Adds the vector a to the current calling object. function
sub(a) Subtracts the vector a from the current calling object. function
scale(num) Multiplies all elements of the vector by num. function
mult(num) Returns a new vector that is a copy of object.scale(num). function
dot(a) Returns the dot product of the vector with vector a. function
length Returns the magnitude of the vector. function
lengthSq Returns the magnitude squared of the vector. function
distanceTo(a) Calculates the distance from the vector to vector a function
distanceToSq Calculates the squared distance from vector to vector a function
zero Zeros all the components of the vector. function
negate Same as scale(-1). function
normalize Normalizes the vector. function
clone Returns a clone of the vector. function

Vector 2-D

Flory.Vector2 is optimized for 2-D vector calculations. As such it optimizes the basic vector operations above for 2-d. It additionally provides access to the below properties:

Value Description Type
x The x components of the vector Float
y The y components of the vector Float
cross(vec) Returns the cross product of the vector with vector vec and assumes that the object.z = 0 function

Vector 3-D

Flory.Vector3 is optimized for 3-D vector calculations. The properties are below.

Value Description Type
x The x components of the vector Float
y The y components of the vector Float
z The z components of the vector Float
cross(vec) Returns the cross product of the vector with vector vec. function

Vector N-D

Flory.Vector is optimized for general N-D vector calculations. It implements all the properties from Flory.baseVector in addition to:

Value Description Type
cross(vec) Returns the cross product of the vector with vector vec. function

Rendering settings

When calling Flory.Environment.enableVisualization(canvas,data) , the valid properties for data are below.

Value Description Default
fov The field of view for the camera. 60
near_clip The near clip cutoff for the rendering camera. 0.1
far_clip The far clip cutoff for the rendering camera. 10000
size A 2 element array representing width and height. size of canvas
camera_position The 3-D position of the camera. [80,80,70]
auto_resize Automatically change canvas size on browserresize event. true
clearColor The background color of the visualization. 0xFFFFFF
axis Show world position axes. true
axisSize The size of the axes. 10
grid Show a grid. true
gridPlane The plane of the grid. Valid values are 2-character string permutations of ("x","y","z"). "xy"
gridSize The size of the grid. 10

Environment

For more flexibility, you can define your own environment. To do this, you create a class that inherits from Flory.Environment and then you can override the methods below. Keep in mind, that you are only required to override Flory.Environment.update, the rest are optional. For an example for how to inherit a class, checkout the example for inheritance.

Value Description
update(data) Called before rendering during the Flory.Environment.advance(data) call. In order to have results rendered to the screen, this function should update the position of the entities in Flory.Environment.entities.
removedEntity(entity,id,index) Called after an entity has been removed from the environment. entity is the removed entity and id is its id ,with index being the index of entity in Flory.Environment.entities before removal.
addedEntity(entity) Called after an entity has been added from the environment. entity is the removed entity
setUpVisualization(data) Called at the end of Flory.Environment.enableVisualization(data)
disabledVisualization Called at the of Flory.Environment.disabledVisualization()
resetEnvironment This can be directly called to reset the environment.

Build Options

Flory uses the grunt build system in a different way. While it does offer the simplicity of just typing npm install;grunt; into the terminal, it also makes use command line parameters similar to the three.js build system. The options for the build system are shown below.

For example:

  1. grunt --minify --output custom_folder/flory.js to minify the library into custom_folder/flory.js.min
  2. grunt --minify --include my_light_weight/include.json to minify a list of includes into the default build/flory.js.min file
Value Description Default
--minify If this flag is set, then the library will be minified into the --output+.min . false
--include The relative or absolute path to a json file containing an array of file paths where order indicates how the source files for the build will be included. Use this as an example for building your own. utils/build/includes/source.json
--externs The relative or absolute path to a js file defining the externs for the closure compiler during the minification step. ./externs/common.js
--output The path of output file for this build.If the sourcemaps flag is selected, then the sourcemap is added as --output+.map. Similarly, if the --minify flag is set, then the minified file is added as --output+.min build/flory.js
--sourcemaps A flag that when set indicates that source maps should be generated in the same directory as the output. false