Basic Callbacks

In V-HAB two basic typse of components exist with regard to the time. The first basic type are components that calculate and then register their time step with the timer. These are executed whenever their next execution time (depending on the time step and the global time at which the time step was set) is reached. They are stored in the cCallBacks property of the timer and are responsible for the actual time difference that your simulation moves forward in each tick.The timestep within V-HAB is always the current time, to the next execution time of any of the callbacks. Examples for functions that are called through this method are the phase registerUpdate, the exec functions of the vsys and solvers that registered a time step. In principle everything that sets its own time step can be found in this group. However, mostly the actual tick of V-HAB does not perform calculation but instead decides what must be updated in this tick. The majority of the calculations is handled in a specific update order during the post tick, which will be explained further down.

The way these time steps in V-HAB work also explains why you can have smaller global time steps even though the smallest time step of all components is larger. For example if your systems all run with 1 second as time step, but for some reason two of these component updates are shifted by 0.5 seconds your global time step will always be 0.5. This might become clearer if we write it down the global time and next execution times specifically:

Global Time:1 s1.5 s2 s2.5 s3 s
Component One:1.5 s2.5 s2.5 s3.5 s3.5 s
Component Two:2 s2 s3 s3 s4 s

The colors indicate in which global time which component is updated in which global time step. As you can see even though the components increase their next execution time by one second each time they are updated, the global time step is only 0.5 seconds. If you use the findSmallestTimeStep function from the tools folder you only get the smallest time steps from the components, so it is possible that your overall system has even smaller time steps than those.

Unfortunately the callback entries are a bit cryptic them self as only the function name is registered and the object to which the function belongs is not directly visible. If you want additional information on any of the entries from the cCallBacks property for debugging you can use the Matlab function functions() on any of the entries. This provides the following outputs for example:

% looking at the cCallBacks directly only provides limited information
this.cCallBacks{2}

ans =

  function_handle with value:

    @(varargin)this.exec(varargin{:})


% If you use functions() instead you can get a lot more information
A = functions(this.cCallBacks{2})

A = 

  struct with fields:

            function: '@(varargin)this.exec(varargin{:})'
                type: 'anonymous'
                file: 'C:\Users\ga56haq\V-HAB\STEPS-Cleanup\core\+systems\timed.m'
           workspace: {[1×1 struct]}
    within_file_path: ''

% Especially the workspace struct within this result tells you specifically which objects registered the call back in the timer
% So now you know that this entry is the exec function of the CCAA vsys
A.workspace{1}

ans = 

  struct with fields:

    this: [1×1 components.CCAA.CCAA]


Post Tick Callbacks

The second type of callbacks that are registered in the timer are so called post tick callbacks. They are stored in the txPostTicks property of the timer object. This property has the following structure:

txPostTicks = struct('matter', struct(...
                                'phase_massupdate', cell.empty(),...
                                'volumeManips', cell.empty(),...
                                'phase_update', cell.empty(),...
                                'solver', cell.empty(),...
                                'P2Ps', cell.empty(),...
                                'substanceManips', cell.empty(),...
                                'multibranch_solver', cell.empty(),...
                                'residual_solver', cell.empty()),...
                             ...
                             'electrical', struct(...
                                'circuits', cell.empty()),...
                             ...
                             'thermal', struct(...
                                'capacity_temperatureupdate', cell.empty(),...
                                'solver', cell.empty(),...
                                'heatsources', cell.empty(),...
                                'multibranch_solver', cell.empty(),...
                                'residual_solver', cell.empty()),...
                             ...
                             'post_physics', struct(...
                                'timestep', cell.empty()));

It is a struct which has four field matter, electrical, thermal and post_physics. These groups represent the different domains in a V-HAB calculation. These are also called post tick groups from now on. Each group then has multiple fields again, which are called post tick levels from now on. Each level represents one specific type of calculation from the current group. For example the phase_massupdate level represents the massupdate function of phases. The order of the fields also defines the update order, which means if you read the definition of txPostTicks from top to bottom that is also the update order. In this case the first execution are the massupdates of phases, which are then followed by the phase updates and so on. Each of the post tick levels then contains a cell array which contains the function handles to execute the corresponding post tick. Additional to the levels shown above there are pre_ and post_ levels for each of the levels. For example if you have a calculation and you want to ensure that it is executed before the manipulators you can registerit to the post tick level pre_manips. If you want it to be executed after the manipulators you can register it to the level post_manips.

So how can you register something to the post ticks and then bind the post tick outdated? An example of this would be the phase massupdate function, which is register with the following code

this.hBindPostTickMassUpdate  = this.oTimer.registerPostTick(@this.massupdate,   'matter',        'phase_massupdate');

The required function from the timer is registerPostTick. The function requires the function handle of the calculation you want to bind to the post tick as first input. The second input is a string specifying the post tick group to which you want to register the post tick and the second input is a string which specifies the level. The output of the registerPostTick function is a function handle which should be saved into a property of the current object. This handle allows you to set this post tick outdated by simply using the function handle directly. In this example you could set the phase massupdate post tick outdated (ensuring that it is calculated in this tick) by using this.hBindPostTickMassUpdate(). All necessary inputs are already defined when the post tick is registered! Of course you can use the same syntax to register any post tick you want, however you should bind your individual post ticks to the post_ and pre_ levels and not into the primary post tick levels directly!!

While the execution order is defined as previously mentioned it is possible that post tick is set outdated while other post ticks are executing. If the newly outdated post tick is from the level we are currently executing, it will simply be executed as well. If it from a level which will be calculated later on, it can simply be set outdated and the normal post tick calculation can handle it. However, if it is from an earlier post tick level, which was already executed, we have to iterate the post tick calculation. Overall, the post tick calculation always executes the post tick levels in the order above, but in case earlier post ticks are set outdated during the post tick execution the post tick calculation will be executed from the beginning again, calculating these post ticks. Only the post_physics group is not handled this way, as we have to ensure that all flowrates are final before the time steps are calculated, the post_physics group is calculated after all other post ticks are calculated.


Post Tick Execution Order

The current execution order is based on the following principles and thoughts:

  • Matter is executed before anything else to have the current matter properties and masses for the other calculations
  • Electrical is executed before thermal because the thermal domain might depend on results of the electrical domain (e.g. a heater changes its heat flow depending on the electrical current)
  • post_physics are executed in the end after all other calculations and contain the time step calculations (which require all flows to be known)

The post tick levels in the matter domain are as follows, with the corresponding reasoning.

Post Tick LevelContains calculations for:Reasoning for the Level
phase_massupdate
contains only the massupdate function of phase.mTo reduce the number of times massupdates are called and to ensure that they are executed before the branches they are executed in the post tick
volumeManipscontains the update of matter.manips.volumeTo calculate the volume change before the new pressure is calculated using the phase update function!
phase_update
contains only the update function of phase.mThe phase updates are executed after the massupdates to ensure that the correct mass is used for the update function. By putting them in the post tick the update order is ensured and they are calculated before the branches because the branches require the pressure values for their calculation.
solver
contains the update functions from all basic matter solversBranches must be updated in the post tick and after the massupdates because of the handling discussed in 6. Mass Balance . The correct location for the electrical domain should be further discussed
P2Ps
contains the update functions of all P2PsP2Ps can depend on the mass flows entering the phase, and are therefore calculated after the branches. The multi branch solver is special because it calculates the P2Ps flowrates directly during its own calculation and the update function of the P2Ps for the multi branch solver should be empty.
substanceManips
contains the update functions of all matter.manips.substanceAs substance manipulators can depend on the mass flows entering/exiting the phase, the branches are calculated before this, except for the residual solver and multibranch solvers. For multibranch solvers the manipulators should be calculated before to ensure that the calculated matter composition of the flow nodes is correct.
multibranch_solver
contains the update functions of all matter multibranch solversThe multi branch solver must be updated after the normal branches because they are considered as constant boundary conditions in the solver. If any branch changes its flowrate after the multi solver is calculated this can result in mass errors within the multisolvers as the condition that the mass of the gas_flow_nodes is not allowed to change could be violated.
residual_solver
contains the update functions of all residual solversThe residual and the infinite conductance solver both depend on the fact that all other flowrates (either mass or thermal) are known and finalized when they are calculated. Therefore, they are on one of the latest post tick levels.
  • Keine Stichwörter