According to Wikipedia, the Verlet algorithm is defined as:
Calculate x(t+Δt)=x(t)+v(t)Δt+21a(t)Δt2.
Derive a(t+Δt) from the interaction potential using x(t+Δt).
Calculate v(t+Δt)=v(t)+21(a(t)+a(t+Δt))Δt.
The Verlet operator is represented by the type propagator_verlet_t which extends propagator_t:
Definition of propagator_verlet_t
type,extends(propagator_t)::propagator_verlet_tprivate
end type propagator_verlet_t
Note, that the class definition does not add anything to the propagator_t class. The only differences are the definition of the operations, and the overloaded constructor.
For the Verlet propagator, we need to define the following operations:
These are used to define the algorithm, which is done in the constructor of the propagator:
function propagator_verlet_constructor(dt)result(this)FLOAT,intent(in)::dttype(propagator_verlet_t),pointer::thisPUSH_SUB(propagator_verlet_constructor)allocate(this)this%start_operation=OP_VERLET_STARTthis%final_operation=OP_VERLET_FINISHcall this%add_operation(OP_VERLET_UPDATE_POS)call this%add_operation(OP_UPDATE_COUPLINGS)call this%add_operation(OP_UPDATE_INTERACTIONS)call this%add_operation(OP_VERLET_COMPUTE_ACC)call this%add_operation(OP_VERLET_COMPUTE_VEL)call this%add_operation(OP_ITERATION_DONE)call this%add_operation(OP_REWIND_ALGORITHM)! Verlet has only one algorithmic step
this%algo_steps=1this%dt=dtPOP_SUB(propagator_verlet_constructor)end function propagator_verlet_constructor
The algorithm also uses steps, which are not specific to the Verlet algorithm, and are defined in the propagator_oct_m module.
Note, the difference between OP_VERLET_FINISH and OP_FINISHED. The former denotes a specific step, to be taken at the end of one time step,
while the latter generally denotes the end of a time step.
As can be seen in this example, the definition of the propagator in terms of the algorithmic operations is a direct translation of the algorithm, shown above.
Implementation of the steps
So far, the propagator is only defined in an abstract way. It is down to the individual systems (or better, their programmers) to implement the individual steps of the algorithm, in terms of the dynamic variables for that system. An easy examample to demonstrate this is the classical particle, as implemented in classical_particles_t
definition of classical_particles_t
type,extends(system_t),abstract::classical_particles_tprivate
type(space_t),public::space!< Dimensions of physical space
integer,public::np!< Number of particles in the system
FLOAT,allocatable,public::mass(:)!< Mass of the particles
FLOAT,allocatable,public::pos(:,:)!< Position of the particles
FLOAT,allocatable,public::vel(:,:)!< Velocity of the particles
FLOAT,allocatable,public::tot_force(:,:)!< Total force acting on each particle
FLOAT,allocatable,public::lj_epsilon(:)!< Lennard-Jones epsilon
FLOAT,allocatable,public::lj_sigma(:)!< Lennard-Jones sigma
logical,allocatable,public::fixed(:)!< True if a giving particle is to be kept fixed during a propagation. The default is to let the particles move.
type(propagator_data_t),public::prop_datacontains
procedure::do_algorithmic_operation=>classical_particles_do_algorithmic_operationprocedure::is_tolerance_reached=>classical_particles_is_tolerance_reachedprocedure::copy_quantities_to_interaction=>classical_particles_copy_quantities_to_interactionprocedure::update_interactions_start=>classical_particles_update_interactions_startprocedure::update_interactions_finish=>classical_particles_update_interactions_finishprocedure::restart_write_data=>classical_particles_restart_write_dataprocedure::restart_read_data=>classical_particles_restart_read_dataprocedure::update_kinetic_energy=>classical_particles_update_kinetic_energyprocedure::center_of_mass=>classical_particles_center_of_massprocedure::center_of_mass_vel=>classical_particles_center_of_mass_velprocedure::center=>classical_particles_centerprocedure::axis_large=>classical_particles_axis_largeprocedure::axis_inertia=>classical_particles_axis_inertiaend type classical_particles_t
This describes an extremely simple system, consisting of a set of classical, neutral particles. The dynamic variables (i.e. the state) of the system are the positions, velocities and accelerations.
One ‘‘tick’’ of the propagator is defined in the function classical_particle_do_td. As can be seen in the code below, this function implements all possible algorithmic steps for all propagators, allowed for that system.
Example implementation for classical particles
The timeline explained
The Verlet algorithm is a good (because simple) example to illustrate how the systems and the interaction are updated as the code progresses through the main loop.
0: multisystem_propagation_start
System: root
Clocks:
In
Out
system:
0.00000E+00
0.00000E+00
prop:
0.00000E+00
0.00000E+00
This graph illustrates how the state machine is stepping through the algorithm. Each system is picking the next algorithmic step from the propagator. For the containers (i.e. ‘‘root’’ and ‘‘earth’'), the only steps are ‘‘Updating interactions’’ and ‘‘Finished’’. The real systems, on the other hand, are progressing orderly through the operations, defined in the propagator.
Example with different time steps
This example shows the propagation of the solar system, where different time steps are used for the three systems.
In contrast to the previous example we can see here that the slowest system (i.e. ‘‘sun’') is waiting in ‘‘Updating interaction’’ for many computational steps, as it needs to wait for the other systems to complete their time steps. These waiting steps are computationally very cheap, as no grid operations are performed.