The Block Initialisation Life Cycle
The Block
class has a number of virtual functions that can be implemented by a concrete block class. These functions are intended to allow the simulation to be initialised in an orderly fashion. The initialisation is subdivided into a number of steps. This allows one class to initialise data in an earlier step and another class to access that data in a subsequent step, knowing that it has already been initialised. In the previous sections we have already come across some of these methods. In this section a complete reference is given.
class Block { protected: virtual void initParameters(BlockParameters&) {} virtual void registerData() {} virtual void preInit() {} virtual void init() {} virtual void postInit() {} };
By default all of the initialisation methods are defined as empty functions. They may be overridden but they don’t have to be. In the following a brief description of each function is given.
void initParameters(BlockParameters&)
This function is called just after the block object is created but before the contents of the block is read from setup file. Here the member variable can be added to theBlockParameters
object. This registers the member variables with the parser and allows automatic initialisation of the member variables from the setup file. For details on this function see the section on reading setup files.void registerData()
In this function data that is intended to be shared among multiple block should be registered using theaddData()
function. Once the fields have been registered other blocks are able to access them via theretrieveData()
function. See the previous section on sharing data between blocks for details.void preInit()
This function is called before the maininit()
function and allows additional initialisation that should be carried out before the main initialisation.init()
This is the main initialisation routine. This is the place where most of the set up should go.
void postInit()
This function is called after the maininit()
function and allows additional initialisation that should be carried out after the main initialisation.
When the simulation hierarchy is created by the parser through Parser::parse()
, each block instance is created the moment that it is read from the setup file. At this point the initParameters()
method is called for the newly created block. This enables the parser to read the remainder of the block’s specification from the setup file.
After the parser has created the hierarch, a call to initAll()
on the root block of the hierarchy will automatically iterate over all blocks for each of the remaining initialisation functions. However, before the registerData()
function is called on each block, initAll()
will ensure that evaluateParameters()
is called on each block in the hierarchy. After registerData()
function is called on each block, preInit()
is called on each block in the hierarchy, then init()
, and finally postInit()
. This means that, for example, during execution of the init()
function it is guaranteed that preInit()
has already been called for every block in the hierarchy.
Below is a flow diagram outlining the parsing and the initialisation sequence.
In order to clarify the flow using an example, let’s look at the following setup file.
Particle A { Nested N1 { } } Force B { } Force C { } Particle D { Nested N2 { } }
We assume here that the blocks Particle
, Force
, and Nested
have been set up with the parser according to the structure found in this file. The parsing and the initialisation sequence is then
Calling Parser::parse() Root Block initParameters Particle A initParameters Nested N1 initParameters Force B initParameters Force C initParameters Particle D initParameters Nested N2 initParameters Calling initAll() Root Block registerData Particle A registerData Nested N1 registerData Force B registerData Force C registerData Particle D registerData Nested N2 registerData Root Block preInit Particle A preInit Nested N1 preInit Force B preInit Force C preInit Particle D preInit Nested N2 preInit Root Block init Particle A init Nested N1 init Force B init Force C init Particle D init Nested N2 init Root Block postInit Particle A postInit Nested N1 postInit Force B postInit Force C postInit Particle D postInit Nested N2 postInit
This example illustrates in which order the initialisation functions are called. Notice how the each routine is called for each block in the order they appear in the setup file. This ordering is guaranteed by Schnek.
The code for this example can be found here and the setup file is located here.