Time propagation

Work in progress!

In the new multisystem framework, the time propagation is handled by the routine time_dependent_run_multisystem()

The main loop

Implementation of time_dependent_run_multisystem()

The code is quite self-explanatory. The central part of the time propagation is the do ... while loop (see "! The full TD loop"). Here the function system%dt_operation() is called successively, triggering one algorithmic step of the multisystem propagator.

The propagators in general will need several calls of this to finish one time step of the propagator!

At the top level, there is only one system (in general called .), which is of type multisystem_basic_t, which then contains other systems as subsystems. These subsystems can be multisystems themselves.

Updating the system

In the multisystem framework, the highest level system is of type multisystem_basic_t, which inherits the dt_operation() routine from the abstract multisystem_t type.

Implementation of multisystem_dt_operation

This routine first calls the general system_dt_operation() subroutine from the parent class system_t and then loops over all subsystems, which are part of the multisystem, and calls their specific system%dt_operations() routine.

The general system_dt_operation() subroutine is handling all general algorithmic operations, such as starting and ending the SCF loop, or updating interaction.

Implementation of system_dt_operation()

This routine fetches the next operation from the propagator and executes it. The following operations are implemented here:

All remaining operations are passed down to the system specific routine system%do_dt_operation(), which is the place where specific systems need to implement all algorithmic steps, required by the propagators, allowed for that system.

In a multisystem, the multisystem itself as well as all its subsystems are propagating according to their own propagators. In principle, they could be different, which could lead to unexpected behaviour. If propagators of the subsystems are not specified explicitely in the input file, they inherit automatically the propagator of the parent multisystem. Therefore, they should not be specified individually. The multisystem container itself always uses an ‘empty’ propagator, which only implements the operations OP_UPDATE_INTERACTIONS and OP_FINISHED. For multisystem_t objects, the do_td_operations() routine only implements the SKIP operation.

This routine is triggering the update of the interactions, via the call to system%update_interations(), which loops over the interactions associated with the system, and all required exposed quantities.

Updating the interactions

Each of the systems (i.e. the multisystem and its subsystems) are attempting to update their interactions with system%update_interactions().

Implementation of system%update_interations()

The first part makes sure that the update_interactions_start() routine is only called when no interaction has been updated yet in this time step, iu.e. if their clocks are behind the system clock.

It is assumed that the interaction can never be ahead of time, compared to the propagator. It is therefore sufficient to ask whether the interaction time equals the propagator time to determine whether an interaction is up-to-date.

In the second part, we actually attempt to update the interactions, if needed. If an interaction is not up-to-date, it first needs to be ensured that the quantities, on which the interaction depends, are up-to-date. Once all quantities are updated, the update() routine of the interaction can be called with the current propagator time as argument.

Finally, if all interactions are updated successfully, there is a step update_interactions_finish(), which can be necessary for some systems.

Implementation of interaction_with_partner_update()

This function is to be called with for a specific requested_time, which is the time at which the interaction is needed.

The function first tries to update the exposed quantities of the partner. If this is successfull, the function calls the calculate() routine of the interaction, sets the interaction clock to the requested time and returns with the result .true., indicating a successful update. In case the function was blocked by one of the exposed quantities, the interaction is not calculated and the function returns .false..

As can be seen, it is, possible that an interaction cannot be updated at a given time. This can be the case when the interaction also depends on quantities of the other interaction partner, and that partner is not yet at the requested time.

Definition of system_update_exposed_quantities()

This routine demands some more comments:

Firstly, the routine is only allowed if the interaction is of type interaction_with_partner_t, as otherwise there is no partner to update exposed quantities. It is important to remember who called the function, and which quantities are supposed to be updated:

system_update_exposed_quantities() is called from Implementation of interaction_with_partner_update() for each interaction of a system for the interaction partner of that system with respect to a given interaction.

In the following situations, the routine is not allowd to update the exposed quantities:

If the timing conditions are fulfilled (and we are not in an internal SCF loop) the system will call the specific routines to update the exposed quantities, and copies their values to the interaction.

Deadlocks and how to avoid them