Event Views

Last update: 05 Jul 2023 [History] [Edit]


EventViews are a new component in AthenaMT for the ATLAS HLT. They are used to provide a subset of event data to an algorithm in a transparent way, using DataHandles, to allow sharing of algorithmic code between online and offline environments. In an offline workflow, an algorithm would run once per event, guaranteed, reading all relevant data. By contrast, the ATLAS HLT expects to subdivide event data into multiple regions of interest and run algorithms separately for each one - this is accomplished using EventViews. To emphasise the previous point: in the HLT, a single algorithm might be expected to run many times in a single event, loading different data inputs from different EventViews (or, correspondingly, it might not run at all if there are no EventViews).

EventViews are described formally in the HLT software upgrade design document.

Algorithms within an EventView are completely isolated from the rest of the event, and so any data that they require from outside the view must be declared explicitly.

Do I need to worry about views?

If you are trying to add an algorithm to an existing HLT workflow that uses EventViews, then you probably don’t need to do anything. Your algorithm should just run correctly, inside a view. Consult debugging views if not.

If you want to create a new HLT workflow using EventViews then take a look at the simple example below.

For HLT core software developers there’s an expert section.

Simple example

The simplest available example of the use of EventViews is found in this job options file: AthViews/SimpleViewExample.py

Essentially there are two components

  • An HLT-specific algorithm to create EventViews
  • A generic algorithm to run within each EventView (in-view algorithm)

Typically the algorithm run within the EventViews is a complex reconstruction algorithm shared between online and offline environments, while the algorithm that creates the EventViews is specifically developed for the HLT. This pattern is often augmented by a third algorithm that consumes (merges) the data produced in each EventView - again, this would be an HLT-specific algorithm. Note that the ordering of EventView creation, execution, and consumption cannot be guaranteed by data dependencies since there may be no relevent EventViews and hence no data. Consequently, the ordering must be guaranteed using Control Flow.

Job configuration

Ideally the manipulation of EventViews will be handled by existing, configurable algorithms, and so you can just add these to your Job Option files. Besides the algorithms and Control Flow mentioned in the simple example above, the one other requirement for configuration is a named Control Flow node to attach your in-view algorithm(s) to. The EventView creation algorithm must then be configured with the name of this control flow node: in the simple example, it is allViewAlgorithms. This structure is explained in more detail in this presentation. The configuration should look like this:

Sequential CF node
  EventView creator algorithm
  EventView CF node             (sequential or parallel)
    ViewDataVerifier algorithm  (optional)
    Reconstruction algorithm

Some standard algorithms used to handle EventViews are:

  • EventViewCreatorAlgorithm creates and schedules a set of EventViews.
  • ViewTestAlg prints a debug message naming the EventView it is running in. It also prints data objects available inside of the view. This algorithm can be useful to debug scheduler stalls in views.
  • ViewDataVerifier: An EventView can inherit its contents from previous views. Typically a child view will inherit data produced in a parent from an earlier step of the same HLT chain. The ViewDataVerifier algorithm ensures that any data to be accessed in this way is actually available, and known to the scheduler. It needs to be configured with every data object that should be loaded:
    verifier = ViewDataVerifier("electronViewDataVerifier")
    verifier.DataObjects = [('xAOD::TrigEMClusterContainer','StoreGateSvc+'+ CaloMenuDefs.L2CaloClusters)]

    This particular instance of the ViewVerifier makes the object of type xAOD::TrigEMClusterContainer and name L2CaloClusters available in the view in which it is executed. Any event-level data that is required inside a view but created by an algorithm running at the event level (e.g. an RoI collection) must also be added to a ViewDataVerifier.

Accessing data from views

The data in views should be accessed using data handles. For algorithms running inside of the views the redirection of the handles to views is transparent as stated above. Some algorithms running outside of the view still need access to view data: for example, the view creator algorithm or a HypoAlg using view data to make a decision. While constructing their data handles they need to point them to the correct view. In the case of HypoAlg the view objects can be obtained from each of the input decision objects.

auto viewELInfo = TrigCompositeUtils::findLink< ViewContainer >( previousDecision, "view" );

Each ReadHandleKey that is supposed to point to the input data has to be hidden from scheduler (renounced), because the scheduler will not allow data dependencies across a view boundary.

// in header, an usual handle declaration
SG::ReadHandleKey< InputContainerType > m_inputKey {this, "MyInputContainer", "DefaultName", "Input"};
// in ::initialize()
ATH_CHECK( m_inputKey.initialize() )
if ( m_inputKey  ) renounce( m_inputKey );

// in ::execute(const EventContsxt& context)
auto inputObjectsHandle = ViewHelper::makeHandle( *viewELInfo.link, m_objectKey, context );

The *viewELInfo.link is effectively the pointer to a view object. An example can be found in: TrigL2ElectronHypoAlgMT.

To construct an ElementLink pointing to a container fetched from a view, the following helper function can be used:

  decisionObject->setObjectLink( "feature", ViewHelper::makeLink<InputContainerType>( *viewELInfo.link, inputObjectsHandle, objectsCounter ) );

This code constructs an ElementLink using the index of the object in the in-view collection.

For an example of writing data into a view, see the above-mentioned EventViewCreatorAlgorithm.

Debugging views

When configuring a Control Flow sequence to use EventViews it’s easy to lose track of what data objects should be available within the view. If an algorithm stalls within a view you can use a special scheduler configuration flag to diagnose the problem:

from AthenaCommon.AlgScheduler import AlgScheduler
AlgScheduler.EnableVerboseViews( True )

The debugging output will allow you to see the algorithm states in each view listed separately. This information will only appear in the event of a stall, but you can make the scheduler produce the same information after every scheduling decision if you really need:

from AthenaCommon.AlgScheduler import AlgScheduler
AlgScheduler.EnableVerboseViews( True )

This output is incredibly verbose, so it is recommended you start simply with the verbose stall output.

Another easy option is to print out a debug message from the execute() method of an algorithm stating which view it is running in. If you are happy to just add another algorithm to your Job Options then you can use the existing ViewTestAlg as shown in the simple example. Alternatively, you can add the following C++ code to your own algorithm:

ATH_MSG_DEBUG( name() << " running with store " << Atlas::getExtendedEventContext( getContext() ).proxy()->name() );

Please note that in a re-entrant algorithm or tool, getContext() should be replaced by the input context method argument, which is called ctx by convention.

There is a version of the view code which will provide additional debugging information, but it has a performance impact and so is disabled by default. To compile using this version instead, use:


Splitting containers between views

A logical pattern for the use of views is to split a single data container between multiple views, have the views process an element or elements separately, and then merge the results into a new container. However, in the most straightforward implementation this involves copying data in and out of the views, and probably creating extra containers, which may be inefficient. IdentifiableContainers are intended to be used where this overhead would not be acceptable. An IdentifiableContainer (within an EventView) presents the relevant data for that view. In the background it is connected to an IdentifiableCache (in the whole-event context) that actually stores the data, and prevents duplication.

The IdentifiableCache in the whole event context must be accessible for an algorithm running within a view using the corresponding IdentifiableContainer. To make the cache available, add it to a ViewDataVerifier.

For more information on this topic, see the dedicated page for IdentifiableContainers

Expert topics

The following is only likely to be useful for HLT Core Software developers, and involves manipulating views directly.

View data inheritance

Once a set of views have been processed, and the results inspected, it might make sense to perform additional processing on some of these views. Rather than create new views containing the same data, there are two other possibilities. Firstly, the original views can simply be re-used, and additional data added if necessary. Secondly, a new view can be created with an old view as its “parent.”

childView->linkParent( parentView );

This will mean that any data objects that the parent view contained will be accessible in the child view, without any overhead from copying and pasting. However, the scheduler will need to be informed that these objects should be available, as it cannot track data dependencies between views. This is the intended use of the ViewDataVerifier algorithm: you should configure it to run in the child view, so that it verifies that data is inherited from the parent view.

In the AthViews package there is an example of view data inheritance and the use of ViewDataVerifiers included for the sake of documentation. Take a look at the job options file: AthViews/ViewInheritance.py

Views as data objects

Although it may seem counterintuitive, EventViews can and should be stored as (a collection of) data objects in StoreGate. This does not create overhead, as EventViews are lightweight objects, and do not actually own any of the data they “contain.” When an algorithm creates EventViews they should be stored in an iterable container, so that downstream algorithms can retrieve and consume them.

See the standard implementation in the EventViewCreatorAlgorithm execute() method.

The typical pattern would be that a collection of views would be created by one algorithm, and then retrieved by the algorithm that merges the results of view processing.

View helper functions

Because a lot of EventView manipulation follows common patterns, you may be able to use existing helper functions. These are found in the file AthViews/ViewHelper.h. Common ones include the functions to create (MakeAndPopulate()) views, schedule (ScheduleViews()) them for execution, and merge (class ViewMerger) the results.

The merging step is particularly important, as this adds standardised bookkeeping information about the origin of different data objects. This is used when building the HLT result, so it is important that this be included. Therefore it is recommended that you use the ViewMerger class provided: an example can be found here in the mergeHelperTest section.

Element links to objects within views should be remapped to point to the corresponding entries in merged collections. This is only really relevant when preparing to create an HLT output. Re-mapping is established automatically in the ViewMerger helper, but is only applied when objects are made persistent. The StoreGateSvc::remap() method is used - see StoreGate/StoreGateSvc.h.

TrigComposite objects contain their own set of typeless element links, which must also be re-mapped. There is an example of this in the HLTEDMCreator class, in the fixLinks() method, but this is likely to be superceded by current development.