Programming new state updaters: Steepest descent

In this tutorial, we will show how to create a simple state updater on an example of the steepest descent minimization algorithm. State updaters are used to update the state of a set of atoms.

State updater is a part of the simulation engine in SAMSON. They can be used to implement integrators (e.g. Verlet) or optimizers (e.g. the steepest descent algorithm).

Relations between the elements inside a simulator

Inside a simulator, the state updater and the interaction model share the same dynamical model. Each time the simulator requires an update through the updateState method, the state updater moves the atoms in the dynamical model according to the forces given by the interaction model.

The state updater allows you to retrieve and modify position of particles (via SBParticleSystem::getPosition and SBParticleSystem::setPosition functions) and momentum of particles (via SBParticleSystem::getMomentum and SBParticleSystem::setMomentum functions); this is done via the associated dynamical model.

The source code for an Element created in this tutorial can be found at https://github.com/1A-OneAngstrom/SAMSON-Developer-Tutorials.

# Generating an Element

We will start by creating a new SAMSON Element called SteepestDescent thanks to the SAMSON Element generator (please, refer to the SAMSON Element generator tutorial for a reminder on how to use it):

The SAMSON Element generator generates a state updater class (derived from the SBSStateUpdaterParticleSystem class) and a property widget class for the state updater.

# Setting the description of the Element

SAMSON provides the steepest descent state updater with its State Updater pack, to distinguish we add "DevTutorial" in the description of the Element.

Open the header file of your state updater descriptor (SESteepestDescentStateUpdaterDescriptor.hpp) and modify the class description:

SB_CLASS_DESCRIPTION("DevTutorial: Steepest descent");

Do the same for the property window descriptor (SESteepestDescentStateUpdaterPropertiesDescriptor.hpp). Modify the getName function of the property window (SESteepestDescentStateUpdaterProperties.cpp) to have a user-friendly description of your interaction model inside SAMSON:

QString SESteepestDescentStateUpdaterProperties::getName() const {
return "DevTutorial: Steepest Descent";
}

And modify the constructor for the state updater class (class: SESteepestDescentStateUpdater, file: SESteepestDescentStateUpdater.cpp) by modifying an argument for the setName function as follows:

setName("DevTutorial: Steepest Descent");

Now, try building the SAMSON Element.

Start SAMSON and open any file containing atoms or add atoms in the data graph, then create a new simulator (Simulation > Add simulator or ctrl/ Cmd⌘ + shift + M): your new Element should be in the list of the available state updaters.

# Moving atoms with state updater

Let's first try to move atoms using the state updater by displacing their position. Note: In this example we will not be taking the forces obtained with an interaction model into account, we will be changing positions of atoms by a constant value.

Implement the method called updateState in the state updater (file SESteepestDescentStateUpdater.cpp). SAMSON Element generator provides some commented code that allows you to implement a steepest descent if you uncomment it. Leave this code commented for the moment.

In order to move atoms we need to modify their positions. For that, we first get the number of atoms that are inside of the dynamical model (associated with the state updater).

SBPointerIndexer<SBStructuralParticle> const* particleIndexer = (*dynamicalModel)->getStructuralParticleIndexer();
unsigned int nParticles = particleIndexer->size(); // number of particles in the particle system

We get the number of iterations per an interactive simulation step:

unsigned int nSteps = getNumberOfSteps(); // the number of iterations per interactive simulation step

Note: The state updater Element generated with the SAMSON Element generator should already has a standard property window implemented that provides the user the possibility to modify the timestep and the number of interactive simulation steps.

Now we can iterate over the steps and atoms in the dynamical model. For each particle, we retrieve its position, add a displacement to it, and set the new position.

// A state updater that just displaces atoms in the x-direction
for (unsigned int k = 0; k < nSteps; k++) {
for (unsigned int i = 0; i < nParticles; i++) {
// get the current position of particle i
SBPosition3 currentPosition = (*dynamicalModel)->getPosition(i);
// change the current position
SBPosition3 newPosition = currentPosition + displacement;
// set the new position of particle i
(*dynamicalModel)->setPosition(i, newPosition);
}
}

Now, build the Element and launch SAMSON. Load or create any molecule and add a new simulator (Simulation > Add simulator or ctrl/ Cmd⌘ + shift + M) with any interaction model and with this state updater. Run the simulation (Simulation > Start simulation or Space or click on the Play icon) and the atoms will be shifted on the x-axis. Try modifying the number of steps (iterations per an interactive simulation step).

Displacing atoms with the state updater

# Steepest descent

Let's now implement the steepest descent minimization algorithm. For that, in the updateState function of the state updater we change the loop from the previous section to the following code:

// The basic implementation of the steepest descent minimization algorithm
for (unsigned int k = 0; k < nSteps; k++) {
// compute the new energy and forces based on the updated positions
(*interactionModel)->updateInteractions();
// tell the dynamical model that we have used the list of updated positions
(*dynamicalModel)->flushPositionBuffer();
for (unsigned int i = 0; i < nParticles; i++) {
// get the current position of particle i
SBPosition3 currentPosition = (*dynamicalModel)->getPosition(i);
// get the force applied to particle i
SBForce3 force = (*interactionModel)->getForce(i);
// compute the displacement (take care of dimensions)
SBPosition3 deltaPosition = 1.0 / SBQuantity::mass(1) * getStepSize() * getStepSize() * force;
// change the current position
SBPosition3 newPosition = currentPosition + deltaPosition;
// set the new position of particle i
(*dynamicalModel)->setPosition(i, newPosition);
}
// notify the interaction model that we have used the list of updated forces
// (actually, we used all forces in this non-adaptive algorithm)
(*interactionModel)->flushForceBuffer();
}
// update the structural model based on the new positions in the dynamical model
(*dynamicalModel)->updateStructuralNodes();

Here, we first ask the interaction model to update the interactions (compute forces and energy), then we update positions of particles according to the computed forces.

Now you should have the basic implementation of the steepest descent minimization algorithm. Build the Element and launch SAMSON. Load or create any molecule and add a new simulator with this state updater and with, for example, the springs interaction model. Run the simulation (Simulation > Start simulation or Space or click on the Play icon) and try slighlty pulling an atom.

Moving atoms with the steepest descent state updater