diff --git a/Control/AthenaServices/src/AthenaOutputStream.cxx b/Control/AthenaServices/src/AthenaOutputStream.cxx index 240ac711c919db230104f2c757451ce451a65fe2..8701fa4c6513912413ec20f6bc25f79325bc93ac 100644 --- a/Control/AthenaServices/src/AthenaOutputStream.cxx +++ b/Control/AthenaServices/src/AthenaOutputStream.cxx @@ -291,7 +291,7 @@ StatusCode AthenaOutputStream::initialize() { } // Check compression settings and print some information about the configuration - // Both should be between [5, 23] and high compression should be <= low compression + // Both should be between [5, 23] and high compression should be < low compression if(m_compressionBitsHigh < 5 || m_compressionBitsHigh > 23) { ATH_MSG_INFO("Float compression mantissa bits for high compression " << "(" << m_compressionBitsHigh << ") is outside the allowed range of [5, 23]."); @@ -304,11 +304,11 @@ StatusCode AthenaOutputStream::initialize() { ATH_MSG_INFO("Setting it to the appropriate limit."); m_compressionBitsLow = m_compressionBitsLow < 5 ? 5 : 23; } - if(m_compressionBitsLow < m_compressionBitsHigh) { - ATH_MSG_INFO("Float compression mantissa bits for low compression " << - "(" << m_compressionBitsLow << ") is lower than high compression " << - "(" << m_compressionBitsHigh << ")! Setting it to the high compression value."); - m_compressionBitsLow = m_compressionBitsHigh; + if(m_compressionBitsLow <= m_compressionBitsHigh) { + ATH_MSG_ERROR("Float compression mantissa bits for low compression " << + "(" << m_compressionBitsLow << ") is lower than or equal to high compression " << + "(" << m_compressionBitsHigh << ")! Please check the configuration! "); + return StatusCode::FAILURE; } if(m_compressionListHigh.value().empty() && m_compressionListLow.value().empty()) { ATH_MSG_VERBOSE("Both high and low float compression lists are empty. Float compression will NOT be applied."); @@ -711,79 +711,20 @@ void AthenaOutputStream::addItemObjects(const SG::FolderItem& item, } } - // Here we build the list of attributes for the float compression - // CompressionList follows the same logic as the ItemList - // We find the matching keys, read the string after "Aux.", - // tokenize by "." and build an std::set of these to be - // communicated to ThinningInfo down below - std::vector<unsigned int> comp_bits{ m_compressionBitsHigh, m_compressionBitsLow }; - std::vector<std::set<std::string>> comp_attr; - comp_attr.resize(2); - if(item_key.find("Aux.") != string::npos) { - // First the high compression list - for (SG::IFolder::const_iterator iter = m_compressionDecoderHigh->begin(), iterEnd = m_compressionDecoderHigh->end(); - iter != iterEnd; iter++) { - // First match the IDs for early rejection. - if (iter->id() != item_id) { - continue; - } - // Then find the compression item key and the compression list string - size_t seppos = iter->key().find("."); - string comp_item_key{""}, comp_str{""}; - if(seppos != string::npos) { - comp_item_key = iter->key().substr(0, seppos+1); - comp_str = iter->key().substr(seppos+1); - } else { - comp_item_key = iter->key(); - } - // Proceed only if the keys match and the - // compression list string is not empty - if (!comp_str.empty() && comp_item_key == item_key) { - std::stringstream ss(comp_str); - std::string attr; - while( std::getline(ss, attr, '.') ) { - comp_attr[0].insert(attr); - } - } - } - // Then the low compression list - // Code duplication is not nice but not worth making modular - for (SG::IFolder::const_iterator iter = m_compressionDecoderLow->begin(), iterEnd = m_compressionDecoderLow->end(); - iter != iterEnd; iter++) { - // First match the IDs for early rejection. - if (iter->id() != item_id) { - continue; - } - // Then find the compression item key and the compression list string - size_t seppos = iter->key().find("."); - string comp_item_key{""}, comp_str{""}; - if(seppos != string::npos) { - comp_item_key = iter->key().substr(0, seppos+1); - comp_str = iter->key().substr(seppos+1); - } else { - comp_item_key = iter->key(); + // Here we build the list of attributes for the lossy float compression + // Note that we do not allow m_compressionBitsHigh >= m_compressionBitsLow + // Otherwise is, in any case, a logical error and they'd potentially overwrite each other + std::map< unsigned int, std::set< std::string > > comp_attr_map; + comp_attr_map[ m_compressionBitsHigh ] = buildCompressionSet( m_compressionDecoderHigh, item_id, item_key ); + comp_attr_map[ m_compressionBitsLow ] = buildCompressionSet( m_compressionDecoderLow, item_id, item_key ); + + // Print some debugging information regarding the lossy float compression configuration + for( const auto& it : comp_attr_map ) { + ATH_MSG_DEBUG(" Comp Attr " << it.second.size() << " with " << it.first << " mantissa bits."); + if ( it.second.size() > 0 ) { + for( const auto& attr : it.second ) { + ATH_MSG_DEBUG(" >> " << attr); } - // Proceed only if the keys match and the - // compression list string is not empty - if (!comp_str.empty() && comp_item_key == item_key) { - std::stringstream ss(comp_str); - std::string attr; - while( std::getline(ss, attr, '.') ) { - comp_attr[1].insert(attr); - } - } - } - } - ATH_MSG_DEBUG(" Comp Attr High: " << comp_attr[0].size() << " with " << comp_bits[0] << " mantissa bits."); - if ( comp_attr[0].size() > 0 ) { - for(auto attr : comp_attr[0]) { - ATH_MSG_DEBUG(" >> " << attr); - } - } - ATH_MSG_DEBUG(" Comp Attr Low: " << comp_attr[1].size() << " with " << comp_bits[1] << " mantissa bits."); - if ( comp_attr[1].size() > 0 ) { - for(auto attr : comp_attr[1]) { - ATH_MSG_DEBUG(" >> " << attr); } } @@ -939,13 +880,13 @@ void AthenaOutputStream::addItemObjects(const SG::FolderItem& item, // store it in the relevant map that is going to be inserted into // the ThinningCache later on by the ThinningCacheTool xAOD::AuxCompression compression; - compression.setCompressedAuxIDs( comp_attr ); - compression.setCompressionBits( comp_bits ); - - compMap[comp_bits[0]] = compression.getCompressedAuxIDs( allVars, true ); // High - compMap[comp_bits[1]] = compression.getCompressedAuxIDs( allVars, false ); // Low + compression.setCompressedAuxIDs( comp_attr_map ); + for( const auto& it : compression.getCompressedAuxIDs( allVars ) ) { + if( it.second.size() > 0 ) // insert only if the set is non-empty + compMap[ it.first ] = it.second; + } - for(auto& it : compMap) { + for( const auto& it : compMap ) { ATH_MSG_DEBUG( "Lossy float compression level " << it.first << " contains " << it.second.size() << " elements" " for container " << key ); @@ -985,6 +926,54 @@ void AthenaOutputStream::addItemObjects(const SG::FolderItem& item, } } +/// Here we build the list of attributes for the float compression +/// CompressionList follows the same logic as the ItemList +/// We find the matching keys, read the string after "Aux.", +/// tokenize by "." and build an std::set of these to be +/// communicated to ThinningInfo elsewhere in the code. +std::set<std::string> +AthenaOutputStream::buildCompressionSet (const ToolHandle<SG::IFolder>& handle, + const CLID& item_id, + const std::string& item_key) const +{ + // Create an empty result + std::set<std::string> result; + + // Check the item is indeed Aux. + if(item_key.find("Aux.") == string::npos) { + return result; + } + + // First the high compression list + for (SG::IFolder::const_iterator iter = handle->begin(), iterEnd = handle->end(); + iter != iterEnd; iter++) { + // First match the IDs for early rejection. + if (iter->id() != item_id) { + continue; + } + // Then find the compression item key and the compression list string + size_t seppos = iter->key().find("."); + string comp_item_key{""}, comp_str{""}; + if(seppos != string::npos) { + comp_item_key = iter->key().substr(0, seppos+1); + comp_str = iter->key().substr(seppos+1); + } else { + comp_item_key = iter->key(); + } + // Proceed only if the keys match and the + // compression list string is not empty + if (!comp_str.empty() && comp_item_key == item_key) { + std::stringstream ss(comp_str); + std::string attr; + while( std::getline(ss, attr, '.') ) { + result.insert(attr); + } + } + } + + // All done, return the result + return result; +} void AthenaOutputStream::handleVariableSelection (SG::IAuxStoreIO& auxio, SG::DataProxy& itemProxy, diff --git a/Control/AthenaServices/src/AthenaOutputStream.h b/Control/AthenaServices/src/AthenaOutputStream.h index ae7ad6ae5d8bb5096c6e76c63b2b2e96c6094665..3b8524587ec97495afc6569969efe6c0600dd9ec 100644 --- a/Control/AthenaServices/src/AthenaOutputStream.h +++ b/Control/AthenaServices/src/AthenaOutputStream.h @@ -224,6 +224,10 @@ private: /// Write MetaData for this stream (by default) or for a substream outputFN (in ES mode) void writeMetaData( const std::string outputFN="" ); + /// Helper function for building the compression lists + std::set<std::string> buildCompressionSet (const ToolHandle<SG::IFolder>& handle, + const CLID& item_id, + const std::string& item_key) const; }; #endif // ATHENASERVICES_OUTPUTSTREAM_H diff --git a/Event/xAOD/xAODCore/Root/AuxCompression.cxx b/Event/xAOD/xAODCore/Root/AuxCompression.cxx index 57cb5aebaab72267994c62790ed4028897f7058e..d2c5954baa5a6181e6bfe4f53f482a5cfc324046 100644 --- a/Event/xAOD/xAODCore/Root/AuxCompression.cxx +++ b/Event/xAOD/xAODCore/Root/AuxCompression.cxx @@ -4,7 +4,6 @@ // System include(s): #include <iostream> -#include <map> // EDM include(s): #include "AthContainers/AuxTypeRegistry.h" @@ -15,8 +14,7 @@ namespace xAOD { AuxCompression::AuxCompression() - : m_names{}, - m_nbits{} + : m_compression_map{} { } @@ -30,12 +28,13 @@ namespace xAOD { /// - A set of variable names, each prefixed by "-", will compress all /// variables but the ones listed. /// - /// @param attributes The attributes from CompressionList. By convention the first element holds the - /// high compression and the second element holds the low compression lists + /// @param attributes The attributes from CompressionList. + /// By convention the key is the nmantissa and the value is the + /// set of variables that should be compressed at that level. /// - void AuxCompression::setCompressedAuxIDs( const std::vector< std::set< std::string > >& attributes ) { + void AuxCompression::setCompressedAuxIDs( const std::map< unsigned int, std::set< std::string > >& attributes ) { - m_names = attributes; + m_compression_map = attributes; return; } @@ -45,118 +44,100 @@ namespace xAOD { /// be compressed. /// /// @param fullset The variables to be compressed based on the rules received - /// @param highComp Whether to retrieve the high or the low compression list - /// @returns The list of variables to be compressed + /// @returns The list of variables to be compressed per compression level /// - SG::auxid_set_t - AuxCompression::getCompressedAuxIDs( const SG::auxid_set_t& fullset, const bool& highComp ) const { - - // Find the relevant index - const bool idx = highComp ? AuxCompression::High : AuxCompression::Low; - - // Start from an empty list - SG::auxid_set_t auxids; - - // Check the simplest case, nothing to be compressed - if( m_names.size() < AuxCompression::NTotal || m_names[ idx ].empty() || ( m_names[ idx ].find("-") != m_names[ idx ].end() ) ) { - return auxids; - } - - // Check that the user only put positive or negative selections on the - // list. They can't be mixed. - bool sub = false, add = false; - std::set< std::string >::const_iterator name_itr = m_names[ idx ].begin(); - std::set< std::string >::const_iterator name_end = m_names[ idx ].end(); - for( ; name_itr != name_end; ++name_itr ) { - if( ( *name_itr )[ 0 ] == '-' ) { - sub = true; - } else { - add = true; - } - } - if( sub && add ) { - // At this level we don't have a handle to the MsgStream, - // otherwise it'd be preferred! - std::cerr << "xAOD::AuxCompression ERROR Mixing + and - options for " - << "compression attributes" << std::endl; - return auxids; // Ill-defined input, not compressing anything just in case - } - - // Loop over the full set, find all float and std::vector<float> variables, add to the list - // This is our starting point as we currently compress only floats. - // This way we don't mistakenly float compress a random type based on wrong user input. - SG::auxid_set_t fauxids; - - for ( const SG::auxid_t& auxid : fullset ) { - const std::string cType = SG::AuxTypeRegistry::instance().getTypeName( auxid ); - if( cType == "float" || cType == "std::vector<float>" ) { - fauxids.insert( auxid ); + std::map< unsigned int, SG::auxid_set_t > + AuxCompression::getCompressedAuxIDs( const SG::auxid_set_t& fullset ) const { + + // Create an empty result map + std::map< unsigned int, SG::auxid_set_t > result; + + // Loop over the internal map set by setCompressedAuxIDs + for(const auto& val : m_compression_map) { + + // Set helper variables + const unsigned int nmantissa = val.first; + const std::set< std::string > names = val.second; + + // Start from an empty list + SG::auxid_set_t auxids; + + // Check the simplest case, nothing to be compressed + if( names.empty() || ( names.find("-") != names.end() ) ) { + continue; } - } - - // Check if all floats are to be compressed, if so return the full float list at this point - if( m_names[ idx ].find("*") != m_names[ idx ].end() ) { - return fauxids; - } - - // Here comes the parsing either + or - as in AuxSelection that we follow closely - if( add ) { - // Build the list of variables to be compressed starting from the empty list - name_itr = m_names[ idx ].begin(); - name_end = m_names[ idx ].end(); + + // Check that the user only put positive or negative selections on the + // list. They can't be mixed. + bool sub = false, add = false; + std::set< std::string >::const_iterator name_itr = names.begin(); + std::set< std::string >::const_iterator name_end = names.end(); for( ; name_itr != name_end; ++name_itr ) { - // Get the ID of this name - const SG::auxid_t auxid = SG::AuxTypeRegistry::instance().findAuxID( *name_itr ); - if( auxid != SG::null_auxid ) { - // Add this variable if it exists - if( fauxids.test( auxid ) ) { - auxids.insert( auxid ); - } + if( ( *name_itr )[ 0 ] == '-' ) { + sub = true; + } else { + add = true; } } - } else { - // Build the list of variables to be compressed starting from the full float list - auxids = fauxids; - // Loop over all float variables and remove if matching - for (SG::auxid_t auxid : fauxids) { - // Construct the name of this ID - const std::string attrname = "-" + SG::AuxTypeRegistry::instance().getName( auxid ); - // Check if it is in the list to be removed - if( m_names[ idx ].find( attrname ) != m_names[ idx ].end() ) { - auxids.erase( auxid ); - } + if( sub && add ) { + // At this level we don't have a handle to the MsgStream, + // otherwise it'd be preferred! + std::cerr << "xAOD::AuxCompression ERROR Mixing + and - options for " + << "compression attributes" << std::endl; + continue; // Ill-defined input, not compressing anything just in case } - } - // Return the list of variables to be compressed - return auxids; - } + // Loop over the full set, find all float and std::vector<float> variables, add to the list + // This is our starting point as we currently compress only floats. + // This way we don't mistakenly float compress a random type based on wrong user input. + SG::auxid_set_t fauxids; - /// Set the number of bits to be used in the float compression - /// By definition first element stores the high compression - /// while the second element stores the low compression configuration - /// - /// params nbits The vector holding the mantissa bits - /// - void AuxCompression::setCompressionBits( const std::vector< unsigned int >& nbits ) { + for ( const SG::auxid_t& auxid : fullset ) { + const std::string cType = SG::AuxTypeRegistry::instance().getTypeName( auxid ); + if( cType == "float" || cType == "std::vector<float>" ) { + fauxids.insert( auxid ); + } + } - m_nbits = nbits; - return; - } + // Check if all floats are to be compressed + if( names.find("*") != names.end() ) { + auxids = fauxids; + } + // Here comes the parsing either + or - as in AuxSelection that we follow closely + else if( add ) { + // Build the list of variables to be compressed starting from the empty list + name_itr = names.begin(); + name_end = names.end(); + for( ; name_itr != name_end; ++name_itr ) { + // Get the ID of this name + const SG::auxid_t auxid = SG::AuxTypeRegistry::instance().findAuxID( *name_itr ); + if( auxid != SG::null_auxid ) { + // Add this variable if it exists + if( fauxids.test( auxid ) ) { + auxids.insert( auxid ); + } + } + } + } else { + // Build the list of variables to be compressed starting from the full float list + auxids = fauxids; + // Loop over all float variables and remove if matching + for ( SG::auxid_t auxid : fauxids ) { + // Construct the name of this ID + const std::string attrname = "-" + SG::AuxTypeRegistry::instance().getName( auxid ); + // Check if it is in the list to be removed + if( names.find( attrname ) != names.end() ) { + auxids.erase( auxid ); + } + } + } - /// Get the number of bits to be used in the float compression - /// See above for the convention - /// - /// params highComp Either high or low compression - /// - unsigned int - AuxCompression::getCompressionBits( const bool& highComp ) const { + // Finally fill the result map + result[ nmantissa ] = auxids; - // Find the relevant index - const bool idx = highComp ? AuxCompression::High : AuxCompression::Low; + } // End of loop over internal map - // Return the number of mantissa bits - return m_nbits[ idx ]; + return result; // Return the result map } } // namespace xAOD diff --git a/Event/xAOD/xAODCore/xAODCore/AuxCompression.h b/Event/xAOD/xAODCore/xAODCore/AuxCompression.h index 3d1a726ccb40c784406f29d9c0057b3efc7fd0c5..d287b89349010bf09d385fd0d190661e8b28cb9a 100644 --- a/Event/xAOD/xAODCore/xAODCore/AuxCompression.h +++ b/Event/xAOD/xAODCore/xAODCore/AuxCompression.h @@ -6,6 +6,7 @@ #define XAODCORE_AUXCOMPRESSION_H // System include(s): +#include <map> #include <set> #include <string> #include <vector> @@ -22,31 +23,17 @@ namespace xAOD { /// Default constructor AuxCompression(); - /// Set which variables should be compressed + /// Set which variables should be compressed per compression setting virtual void - setCompressedAuxIDs( const std::vector< std::set< std::string > >& attributes ); + setCompressedAuxIDs( const std::map< unsigned int, std::set< std::string > >& attributes ); - /// Return those variables that are selected to be compressed - virtual SG::auxid_set_t - getCompressedAuxIDs( const SG::auxid_set_t& fullset, const bool& highComp = true ) const; - - /// Set the number of bits that should be used in the compression - virtual void - setCompressionBits( const std::vector< unsigned int >& nbits ); - - /// Return the number of bits that should be used in the compression - virtual unsigned int - getCompressionBits( const bool& highComp = true ) const; - - /// Enum for the indices used for the high and low compression setup - enum FloatCompressionBits { High = 0, Low = 1, NTotal = 2 }; + /// Return those variables that are selected to be compressed per compression setting + virtual std::map< unsigned int, SG::auxid_set_t > + getCompressedAuxIDs( const SG::auxid_set_t& fullset ) const; protected: /// Properties following the variable selection convention - std::vector< std::set< std::string > > m_names; - - /// Vector holding the number of mantissa bits for the compression - std::vector< unsigned int > m_nbits; + std::map< unsigned int, std::set< std::string > > m_compression_map; }; // class AuxCompression