예제 #1
0
def test_translate_hit_positions():
    mcHits = [
        MCHit(pos=(0., 0., 0.), E=1., t=0. * units.mus, l='test'),
        MCHit(pos=(0., 0., 0.), E=1., t=10. * units.mus, l='test'),
        MCHit(pos=(0., 0., 0.), E=1., t=20. * units.mus, l='test')
    ]
    drift_velocity = 1. * units.mm / units.mus
    new_positions = [(0., 0., 0.), (0., 0., 10.), (0., 0., 20.)]
    assert new_positions == translate_hit_positions(mcHits, drift_velocity)
예제 #2
0
            if not event_smE_filter:
                extend_events_reco_data(events_dict,
                                        event_number,
                                        evt_num_MChits=num_hits,
                                        evt_mcE=event_mcE,
                                        evt_smE=event_smE,
                                        evt_smE_filter=event_smE_filter)

            # Only for those events passing the smE filter:
            else:
                # Smearing hit energies
                mcE_to_smE_factor = event_smE / event_mcE
                hits_smE = smear_hit_energies(active_mcHits, mcE_to_smE_factor)

                # Translating hit positions
                hits_transPositions = translate_hit_positions(
                    active_mcHits, DRIFT_VELOCITY)

                # Creating the smHits with the smeared energies and translated positions
                active_smHits = []
                for i in range(num_hits):
                    smHit = MCHit(hits_transPositions[i],
                                  active_mcHits[i].time, hits_smE[i], 'ACTIVE')
                    active_smHits.append(smHit)

                # Filtering hits outside the ACTIVE region (due to translation)
                active_smHits = [
                    hit for hit in active_smHits if hit.Z < ACTIVE_ZMAX
                ]

                # Voxelizing using the active_smHits ...
                event_voxels = voxelize_hits(active_smHits,
예제 #3
0
def test_translate_hit_positions():
    # Check of assymetric detector
    mcHits = [{
        'label': 'test',
        'time': 0 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': 0.,
        'E': 0.01
    }, {
        'label': 'test',
        'time': 10 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': 0.,
        'E': 0.01
    }, {
        'label': 'test',
        'time': 20 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': 0.,
        'E': 0.01
    }]
    mcHits = pd.DataFrame(mcHits,
                          columns=('label', 'time', 'x', 'y', 'z', 'E'))

    drift_velocity = 1. * units.mm / units.mus
    translate_hit_positions(DetName.next100, mcHits, drift_velocity)

    OK_shifted_z = [0., 10., 20.]

    assert_array_equal(OK_shifted_z, mcHits.shifted_z)

    # Check of ssymetric detectors
    mcHits = [{
        'label': 'test',
        'time': 0 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': 0.,
        'E': 0.01
    }, {
        'label': 'test',
        'time': 40 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': 500.,
        'E': 0.01
    }, {
        'label': 'test',
        'time': 20 * units.mus,
        'x': 0.,
        'y': 0.,
        'z': -500.,
        'E': 0.01
    }]
    mcHits = pd.DataFrame(mcHits,
                          columns=('label', 'time', 'x', 'y', 'z', 'E'))

    drift_velocity = 1. * units.mm / units.mus
    translate_hit_positions(DetName.next500, mcHits, drift_velocity)

    OK_shifted_z = [0., 460., -480.]

    assert_array_equal(OK_shifted_z, mcHits.shifted_z)
예제 #4
0
def fanal_reco(det_name,    # Detector name: 'new', 'next100', 'next500'
               event_type,  # Event type: 'bb0nu', 'Tl208', 'Bi214'
               fwhm,        # FWHM at Qbb
               e_min,       # Minimum smeared energy for energy filtering
               e_max,       # Maximum smeared energy for energy filtering
               voxel_size,  # Voxel size (x, y, z)
               voxel_Eth,   # Voxel energy threshold
               veto_width,  # Veto width for fiducial filtering
               min_veto_e,  # Minimum energy in veto for fiducial filtering
               files_in,    # Input files
               event_range, # Range of events to analyze: all, ... ??
               file_out,    # Output file
               compression, # Compression of output file: 'ZLIB1', 'ZLIB4',
                            # 'ZLIB5', 'ZLIB9', 'BLOSC5', 'BLZ4HC5'
               verbosity_level):


    ### LOGGER
    logger = get_logger('FanalReco', verbosity_level)


    ### DETECTOR NAME & its ACTIVE dimensions
    det_name = getattr(DetName, det_name)
    ACTIVE_dimensions = get_active_size(det_name)
    fid_dimensions    = get_fiducial_size(det_name, veto_width)


    ### RECONSTRUCTION DATA
    # Smearing energy settings
    fwhm_Qbb  = fwhm * Qbb
    sigma_Qbb = fwhm_Qbb / 2.355
    assert e_max > e_min, 'SmE_filter settings not valid. e_max must be higher than e_min.'


    ### PRINTING GENERAL INFO
    print('\n***********************************************************************************')
    print('***** Detector: {}'.format(det_name.name))
    print('***** Reconstructing {} events'.format(event_type))
    print('***** Energy Resolution: {:.2f}% fwhm at Qbb'.format(fwhm / units.perCent))
    print('***** Voxel Size: ({}, {}, {}) mm'.format(voxel_size[0] / units.mm,
                                                     voxel_size[1] / units.mm,
                                                     voxel_size[2] / units.mm))
    print('***********************************************************************************\n')

    print('* Sigma at Qbb: {:.3f} keV.\n'.format(sigma_Qbb / units.keV))

    print('* Voxel_size: ({}, {}, {}) mm'.format(voxel_size[0] / units.mm,
                                                 voxel_size[1] / units.mm,
                                                 voxel_size[2] / units.mm))
    print('  Voxel Eth:  {:4.1f} keV\n'.format(voxel_Eth/units.keV))

    print('* Detector-Active dimensions [mm]:  Zmin: {:7.1f}   Zmax: {:7.1f}   Rmax: {:7.1f}'
          .format(ACTIVE_dimensions.z_min, ACTIVE_dimensions.z_max,
                  ACTIVE_dimensions.rad))
    print('         ... fiducial limits [mm]:  Zmin: {:7.1f}   Zmax: {:7.1f}   Rmax: {:7.1f}\n'
          .format(fid_dimensions.z_min, fid_dimensions.z_max, fid_dimensions.rad))

    print('* {0} {1} input files:'.format(len(files_in), event_type))
    for iFileName in files_in:
        print(' ', iFileName)


    ### OUTPUT FILE, ITS GROUPS & ATTRIBUTES
    # Output file
    oFile = tb.open_file(file_out, 'w', filters = tbl_filters(compression))

    # Reco group Name
    reco_group_name = get_reco_group_name(fwhm/units.perCent, voxel_size)
    oFile.create_group('/', 'FANALIC')
    oFile.create_group('/FANALIC', reco_group_name[9:])

    print('\n* Output file name:', file_out)
    print('  Reco group name:  {}\n'.format(reco_group_name))

    # Attributes
    oFile.set_node_attr(reco_group_name, 'input_sim_files',           files_in)
    oFile.set_node_attr(reco_group_name, 'event_type',                event_type)
    oFile.set_node_attr(reco_group_name, 'energy_resolution',         fwhm/units.perCent)
    oFile.set_node_attr(reco_group_name, 'voxel_sizeX',               voxel_size[0])
    oFile.set_node_attr(reco_group_name, 'voxel_sizeY',               voxel_size[1])
    oFile.set_node_attr(reco_group_name, 'voxel_sizeZ',               voxel_size[2])
    oFile.set_node_attr(reco_group_name, 'voxel_Eth',                 voxel_Eth)
    oFile.set_node_attr(reco_group_name, 'smE_filter_Emin',           e_min)
    oFile.set_node_attr(reco_group_name, 'smE_filter_Emax',           e_max)
    oFile.set_node_attr(reco_group_name, 'fiducial_filter_VetoWidth', veto_width)
    oFile.set_node_attr(reco_group_name, 'fiducial_filter_MinVetoE',  min_veto_e)


    ### DATA TO STORE
    # Event counters
    simulated_events = 0
    stored_events    = 0
    analyzed_events  = 0
    toUpdate_events  = 1

    # Dictionaries for events & voxels data
    events_dict = get_events_reco_dict()
    voxels_dict = get_voxels_reco_dict()


    ### RECONSTRUCTION PROCEDURE
    # Looping through all the input files
    for iFileName in files_in:
        # Updating simulated and stored event counters
        configuration_df  = pd.read_hdf(iFileName, '/MC/configuration', mode='r')
        simulated_events += int(configuration_df[configuration_df.param_key == 'num_events'].param_value)
        stored_events    += int(configuration_df[configuration_df.param_key == 'saved_events'].param_value)

        # Getting event numbers
        file_extents = pd.read_hdf(iFileName, '/MC/extents', mode='r')
        file_event_numbers = file_extents.evt_number

        print('* Processing {0}  ({1} events) ...'.format(iFileName, len(file_event_numbers)))

        # Getting mc hits
        file_mcHits = load_mc_hits(iFileName)

        # Looping through all the events in iFile
        for event_number in file_event_numbers:

            # Updating counter of analyzed events
            analyzed_events += 1
            logger.info('Reconstructing event Id: {0} ...'.format(event_number))

            # Getting event data
            event_data = get_event_reco_data()
            event_data['event_id'] = event_number
            
            event_mcHits  = file_mcHits.loc[event_number, :]
            active_mcHits = event_mcHits[event_mcHits.label == 'ACTIVE'].copy()

            event_data['num_MCparts'] = get_num_mc_particles(file_extents, event_number)
            event_data['num_MChits']  = len(active_mcHits)
            
            # The event mc energy is the sum of the energy of all the hits except
            # for Bi214 events, in which the number of S1 in the event is considered
            if (event_type == 'Bi214'):
                event_data['mcE'] = get_mc_energy(active_mcHits)
            else:
                event_data['mcE'] = active_mcHits.E.sum()
                
            # Smearing the event energy
            event_data['smE'] = smear_evt_energy(event_data['mcE'], sigma_Qbb, Qbb)

            # Applying the smE filter
            event_data['smE_filter'] = (e_min <= event_data['smE'] <= e_max)

            # Verbosing
            logger.info('  Num mcHits: {0:3}   mcE: {1:.1f} keV   smE: {2:.1f} keV   smE_filter: {3}' \
                        .format(event_data['num_MChits'], event_data['mcE']/units.keV,
                                event_data['smE']/units.keV, event_data['smE_filter']))
                
            # For those events passing the smE filter:
            if event_data['smE_filter']:

                # Smearing hit energies
                smearing_factor = event_data['smE'] / event_data['mcE']
                active_mcHits['smE'] = active_mcHits['E'] * smearing_factor

                # Translating hit Z positions from delayed hits
                translate_hit_positions(det_name, active_mcHits, DRIFT_VELOCITY)

                # Creating the IChits with the smeared energies and translated Z positions
                # to be passed to paolina functions
                #IChits = []
                #for i, hit in active_mcHits[active_mcHits.shifted_z < ACTIVE_dimensions.z_max].iterrows():
                #    IChit = MCHit((hit.x, hit.y, hit.shifted_z), hit.time, hit.smE, 'ACTIVE')
                #    IChits.append(IChit)
                IChits = active_mcHits[(active_mcHits.shifted_z < ACTIVE_dimensions.z_max) &
                                       (active_mcHits.shifted_z > ACTIVE_dimensions.z_min)] \
                    .apply(lambda hit: MCHit((hit.x, hit.y, hit.shifted_z),
                                             hit.time, hit.smE, 'ACTIVE'), axis=1).tolist()

                # Voxelizing using the IChits ...
                event_voxels = voxelize_hits(IChits, voxel_size, strict_voxel_size=False)
                event_data['num_voxels'] = len(event_voxels)

                eff_voxel_size = event_voxels[0].size
                event_data['voxel_sizeX'] = eff_voxel_size[0]
                event_data['voxel_sizeY'] = eff_voxel_size[1]
                event_data['voxel_sizeZ'] = eff_voxel_size[2]
    
                # Storing voxels info
                for voxel_id in range(len(event_voxels)):
                    extend_voxels_reco_dict(voxels_dict, event_number, voxel_id,
                                            event_voxels[voxel_id], voxel_Eth)
                    
                # Check fiduciality
                event_data['voxels_minZ'], event_data['voxels_maxZ'], \
                event_data['voxels_maxRad'], event_data['veto_energy'], \
                event_data['fid_filter'] = \
                check_event_fiduciality(det_name, veto_width, min_veto_e, event_voxels)
                   
                # Verbosing
                logger.info('  NumVoxels: {:3}   minZ: {:.1f} mm   maxZ: {:.1f} mm   maxR: {:.1f} mm   veto_E: {:.1f} keV   fid_filter: {}' \
                            .format(event_data['num_voxels'], event_data['voxels_minZ'],
                                    event_data['voxels_maxZ'], event_data['voxels_maxRad'],
                                    event_data['veto_energy'] / units.keV,
                                    event_data['fid_filter']))
                
                for voxel in event_voxels:
                    logger.debug('    Voxel pos: ({:5.1f}, {:5.1f}, {:5.1f}) mm   E: {:5.1f} keV'\
                                 .format(voxel.X/units.mm, voxel.Y/units.mm,
                                         voxel.Z/units.mm, voxel.E/units.keV))

            # Storing event_data
            extend_events_reco_dict(events_dict, event_data)

            # Verbosing
            if (not(analyzed_events % toUpdate_events)):
                print('* Num analyzed events: {}'.format(analyzed_events))
            if (analyzed_events == (10 * toUpdate_events)): toUpdate_events *= 10
            

    ### STORING RECONSTRUCTION DATA
    # Storing events and voxels dataframes
    print('\n* Storing data in the output file: {}'.format(file_out))
    store_events_reco_dict(file_out, reco_group_name, events_dict)
    store_voxels_reco_dict(file_out, reco_group_name, voxels_dict)

    # Storing event counters as attributes
    smE_filter_events = sum(events_dict['smE_filter'])
    fid_filter_events = sum(events_dict['fid_filter'])
    store_events_reco_counters(oFile, reco_group_name, simulated_events,
                               stored_events, smE_filter_events, fid_filter_events)

    oFile.close()
    print('* Reconstruction done !!\n')

    # Printing reconstruction numbers
    print('* Event counters ...')
    print('''  Simulated events:  {0:9}
  Stored events:     {1:9}
  smE_filter events: {2:9}
  fid_filter events: {3:9}\n'''
        .format(simulated_events, stored_events, smE_filter_events, fid_filter_events))
예제 #5
0
def fanal_reco(
    det_name,  # Detector name: 'new', 'next100', 'next500'
    event_type,  # Event type: 'bb0nu', 'Tl208', 'Bi214'
    fwhm,  # FWHM at Qbb
    e_min,  # Minimum smeared energy for energy filtering
    e_max,  # Maximum smeared energy for energy filtering
    spatial_def,  # Spatial definition: 'low', 'high'
    veto_width,  # Veto width for fiducial filtering
    min_veto_e,  # Minimum energy in veto for fiducial filtering
    files_in,  # Input files
    event_range,  # Range of events to analyze: all, ... ??
    file_out,  # Output file
    compression,  # Compression of output file: 'ZLIB1', 'ZLIB4',
    # 'ZLIB5', 'ZLIB9', 'BLOSC5', 'BLZ4HC5'
    verbosity_level):

    ### LOGGER
    logger = get_logger('FanalReco', verbosity_level)

    ### DETECTOR NAME & its ACTIVE dimensions
    det_name = getattr(DetName, det_name)
    ACTIVE_dimensions = get_active_size(det_name)

    ### RECONSTRUCTION DATA
    # Smearing energy settings
    fwhm_Qbb = fwhm * Qbb
    sigma_Qbb = fwhm_Qbb / 2.355
    assert e_max > e_min, 'SmE_filter settings not valid. e_max must be higher than e_min.'

    # Spatial definition
    spatial_def = getattr(SpatialDef, spatial_def)

    # Voxel size
    voxel_size = get_voxel_size(spatial_def)

    # Fiducial limits
    fid_dimensions = get_fiducial_size(ACTIVE_dimensions, veto_width)

    ### PRINTING GENERAL INFO
    print(
        '\n***********************************************************************************'
    )
    print('***** Detector: {}'.format(det_name.name))
    print('***** Reconstructing {} events'.format(event_type))
    print('***** Energy Resolution: {:.2f}% FWFM at Qbb'.format(fwhm /
                                                                units.perCent))
    print('***** Spatial definition: {}'.format(spatial_def.name))
    print(
        '***********************************************************************************\n'
    )

    print(
        '* Detector-Active dimensions [mm]:  Zmin: {:7.1f}   Zmax: {:7.1f}   Rmax: {:7.1f}'
        .format(ACTIVE_dimensions.z_min, ACTIVE_dimensions.z_max,
                ACTIVE_dimensions.rad))
    print(
        '         ... fiducial limits [mm]:  Zmin: {:7.1f}   Zmax: {:7.1f}   Rmax: {:7.1f}\n'
        .format(fid_dimensions.z_min, fid_dimensions.z_max,
                fid_dimensions.rad))
    print('* Sigma at Qbb: {:.3f} keV.\n'.format(sigma_Qbb / units.keV))
    print('* Voxel_size: {} mm.\n'.format(voxel_size))

    print('* {0} {1} input files:'.format(len(files_in), event_type))
    for iFileName in files_in:
        print(' ', iFileName)

    ### OUTPUT FILE, ITS GROUPS & ATTRIBUTES
    # Output file
    oFile = tb.open_file(file_out, 'w', filters=tbl_filters(compression))

    # Reco group Name
    reco_group_name = get_reco_group_name(fwhm / units.perCent, spatial_def)
    oFile.create_group('/', 'FANALIC')
    oFile.create_group('/FANALIC', reco_group_name[9:])

    print('\n* Output file name:', file_out)
    print('  Reco group name:  {}\n'.format(reco_group_name))

    # Attributes
    oFile.set_node_attr(reco_group_name, 'input_sim_files', files_in)
    oFile.set_node_attr(reco_group_name, 'event_type', event_type)
    oFile.set_node_attr(reco_group_name, 'energy_resolution',
                        fwhm / units.perCent)
    oFile.set_node_attr(reco_group_name, 'smE_filter_Emin', e_min)
    oFile.set_node_attr(reco_group_name, 'smE_filter_Emax', e_max)
    oFile.set_node_attr(reco_group_name, 'fiducial_filter_VetoWidth',
                        veto_width)
    oFile.set_node_attr(reco_group_name, 'fiducial_filter_MinVetoE',
                        min_veto_e)

    ### DATA TO STORE
    # Event counters
    simulated_events = 0
    stored_events = 0
    analyzed_events = 0

    # Dictionaries for events & voxels data
    events_dict = get_events_reco_dict()
    voxels_dict = get_voxels_reco_dict()

    ### RECONSTRUCTION PROCEDURE
    # Looping through all the input files
    for iFileName in files_in:
        # Updating simulated and stored event counters
        configuration_df = pd.read_hdf(iFileName,
                                       '/MC/configuration',
                                       mode='r')
        simulated_events += int(configuration_df[configuration_df.param_key ==
                                                 'num_events'].param_value)
        stored_events += int(configuration_df[configuration_df.param_key ==
                                              'saved_events'].param_value)

        with tb.open_file(iFileName, mode='r') as iFile:
            file_event_numbers = iFile.root.MC.extents.cols.evt_number
            print('* Processing {0}  ({1} events) ...'.format(
                iFileName, len(file_event_numbers)))

            # Loading into memory all the particles & hits in the file
            file_mcParts = load_mcparticles(iFileName)
            file_mcHits = load_mchits(iFileName)

            # Looping through all the events in the file
            for event_number in file_event_numbers:

                # Updating counter of analyzed events
                analyzed_events += 1
                #if not int(str(analyzed_events)[-int(math.log10(analyzed_events)):]):
                #    print('* Num analyzed events: {}'.format(analyzed_events))

                # Verbosing
                logger.info(
                    'Reconstructing event Id: {0} ...'.format(event_number))

                # Getting mcParts of the event, using the event_number as the key
                event_mcParts = file_mcParts[event_number]
                num_parts = len(event_mcParts)

                # Getting mcHits of the event, using the event_number as the key
                event_mcHits = file_mcHits[event_number]
                active_mcHits = [
                    hit for hit in event_mcHits if hit.label == 'ACTIVE'
                ]
                num_hits = len(active_mcHits)

                # The event mc energy is the sum of the energy of all the hits
                event_mcE = sum([hit.E for hit in active_mcHits])

                # Smearing the event energy
                event_smE = smear_evt_energy(event_mcE, sigma_Qbb, Qbb)

                # Applying the smE filter
                event_smE_filter = (e_min <= event_smE <= e_max)

                # Verbosing
                logger.info(
                    '  Num mcHits: {0:3}   mcE: {1:.1f} keV   smE: {2:.1f} keV   smE_filter: {3}'
                    .format(num_hits, event_mcE / units.keV,
                            event_smE / units.keV, event_smE_filter))

                # For those events NOT passing the smE filter:
                # Storing data of NON smE_filter vents
                if not event_smE_filter:
                    extend_events_reco_data(events_dict,
                                            event_number,
                                            evt_num_MCparts=num_parts,
                                            evt_num_MChits=num_hits,
                                            evt_mcE=event_mcE,
                                            evt_smE=event_smE,
                                            evt_smE_filter=event_smE_filter)

                # Only for those events passing the smE filter:
                else:
                    # Smearing hit energies
                    hits_smE = smear_hit_energies(active_mcHits,
                                                  event_smE / event_mcE)

                    # Translating hit positions
                    hits_transPositions = translate_hit_positions(
                        active_mcHits, DRIFT_VELOCITY)

                    # Creating the smHits with the smeared energies and translated positions
                    active_smHits = []
                    for i in range(num_hits):
                        smHit = MCHit(hits_transPositions[i],
                                      active_mcHits[i].time, hits_smE[i],
                                      'ACTIVE')
                        active_smHits.append(smHit)

                    # Filtering hits outside the ACTIVE region (due to translation)
                    active_smHits = [hit for hit in active_smHits \
                                     if hit.Z < ACTIVE_dimensions.z_max]

                    # Voxelizing using the active_smHits ...
                    event_voxels = voxelize_hits(active_smHits,
                                                 voxel_size,
                                                 strict_voxel_size=True)
                    eff_voxel_size = event_voxels[0].size

                    # Storing voxels info
                    for voxel in event_voxels:
                        extend_voxels_reco_data(voxels_dict, event_number,
                                                voxel)

                    # Check fiduciality
                    voxels_minZ, voxels_maxZ, voxels_maxRad, veto_energy, fiducial_filter = \
                        check_event_fiduciality(event_voxels, fid_dimensions, min_veto_e)

                    # Storing data of NON smE_filter vents
                    extend_events_reco_data(events_dict,
                                            event_number,
                                            evt_num_MCparts=num_parts,
                                            evt_num_MChits=num_hits,
                                            evt_mcE=event_mcE,
                                            evt_smE=event_smE,
                                            evt_smE_filter=event_smE_filter,
                                            evt_num_voxels=len(event_voxels),
                                            evt_voxel_sizeX=eff_voxel_size[0],
                                            evt_voxel_sizeY=eff_voxel_size[1],
                                            evt_voxel_sizeZ=eff_voxel_size[2],
                                            evt_voxels_minZ=voxels_minZ,
                                            evt_voxels_maxZ=voxels_maxZ,
                                            evt_voxels_maxRad=voxels_maxRad,
                                            evt_veto_energy=veto_energy,
                                            evt_fid_filter=fiducial_filter)

                    # Verbosing
                    logger.info(
                        '  NumVoxels: {:3}   minZ: {:.1f} mm   maxZ: {:.1f} mm   maxR: {:.1f} mm   veto_E: {:.1f} keV   fid_filter: {}'
                        .format(len(event_voxels), voxels_minZ, voxels_maxZ,
                                voxels_maxRad, veto_energy / units.keV,
                                fiducial_filter))
                    for voxel in event_voxels:
                        logger.debug(
                            '    Voxel pos: ({:5.1f}, {:5.1f}, {:5.1f}) mm   E: {:5.1f} keV'
                            .format(voxel.X / units.mm, voxel.Y / units.mm,
                                    voxel.Z / units.mm, voxel.E / units.keV))

    ### STORING DATA
    # Storing events and voxels dataframes
    print('\n* Storing data in the output file ...\n  {}\n'.format(file_out))
    store_events_reco_data(file_out, reco_group_name, events_dict)
    store_voxels_reco_data(file_out, reco_group_name, voxels_dict)

    # Storing event counters as attributes
    smE_filter_events = sum(events_dict['smE_filter'])
    fid_filter_events = sum(events_dict['fid_filter'])
    store_events_reco_counters(oFile, reco_group_name, simulated_events,
                               stored_events, smE_filter_events,
                               fid_filter_events)

    oFile.close()
    print('* Reconstruction done !!\n')

    # Printing reconstruction numbers
    print('* Event counters ...')
    print('''  Simulated events:  {0:9}
  Stored events:     {1:9}
  smE_filter events: {2:9}
  fid_filter events: {3:9}\n'''.format(simulated_events, stored_events,
                                       smE_filter_events, fid_filter_events))