From 3314b62433ee6dfac1fc69f13564027811d0eef1 Mon Sep 17 00:00:00 2001 From: Wim Haeck Date: Wed, 8 May 2024 19:54:12 -0600 Subject: [PATCH] Adding outgoing group structures --- python/src/MultigroupTable.python.cpp | 10 +++++ .../EnergyGroupStructure.python.cpp | 12 ++++- python/src/read.hpp | 45 ++++++++++++++++--- python/test/Test_NDItk_MultigroupTable.py | 22 ++++++++- ...t_NDItk_multigroup_EnergyGroupStructure.py | 34 ++++++++++++++ src/NDItk/MultigroupTable.hpp | 32 +++++++++++-- src/NDItk/MultigroupTable/src/ctor.hpp | 3 +- .../src/generateOutgoingStructureMetadata.hpp | 16 +++++++ src/NDItk/MultigroupTable/src/read.hpp | 11 ++--- .../src/readOutgoingStructure.hpp | 32 +++++++++++++ .../src/readPrimaryStructure.hpp | 15 +++++++ .../test/MultigroupTable.test.cpp | 42 ++++++++++++++++- .../EnergyGroupStructure/src/ctor.hpp | 4 +- 13 files changed, 254 insertions(+), 24 deletions(-) create mode 100644 src/NDItk/MultigroupTable/src/generateOutgoingStructureMetadata.hpp create mode 100644 src/NDItk/MultigroupTable/src/readOutgoingStructure.hpp create mode 100644 src/NDItk/MultigroupTable/src/readPrimaryStructure.hpp diff --git a/python/src/MultigroupTable.python.cpp b/python/src/MultigroupTable.python.cpp index 90fe53f..8db2419 100644 --- a/python/src/MultigroupTable.python.cpp +++ b/python/src/MultigroupTable.python.cpp @@ -77,6 +77,16 @@ void wrapMultigroupTable( python::module& module, python::module& ) { &Table::structure, "The primary group structure record" ) + .def( + + "outgoing_structure", + &Table::outgoingStructure, + python::arg( "particle" ), + "The group structure record for an outgoing particle\n\n" + "Arguments:\n" + " self the metadata\n" + " particle the outgoing particle identifier" + ) .def_property_readonly( "flux", diff --git a/python/src/multigroup/EnergyGroupStructure.python.cpp b/python/src/multigroup/EnergyGroupStructure.python.cpp index a8d4ed6..814b80f 100644 --- a/python/src/multigroup/EnergyGroupStructure.python.cpp +++ b/python/src/multigroup/EnergyGroupStructure.python.cpp @@ -39,6 +39,16 @@ void wrapEnergyGroupStructure( python::module& module, python::module& ) { " self the table\n" " boundaries the group structure boundaries" ) + .def( + + python::init< unsigned int, std::vector< double > >(), + python::arg( "particle" ), python::arg( "boundaries" ), + "Initialise the record\n\n" + "Arguments:\n" + " self the table\n" + " particle the secondary particle identifier\n" + " boundaries the group structure boundaries" + ) .def_property_readonly( "boundaries", @@ -56,7 +66,7 @@ void wrapEnergyGroupStructure( python::module& module, python::module& ) { "from_string", [] ( const std::string& string, std::size_t number ) -> Record - { return read< Record >( string, number ); }, + { return readWithSubtype< Record >( string, number ); }, python::arg( "string" ), python::arg( "number" ), "Read the record from a string\n\n" "An exception is raised if something goes wrong while reading the\n" diff --git a/python/src/read.hpp b/python/src/read.hpp index 87af9dd..2b54ada 100644 --- a/python/src/read.hpp +++ b/python/src/read.hpp @@ -9,7 +9,7 @@ #include "tools/disco/FreeFormatCharacter.hpp" /** - * @brief Read a record from a string + * @brief Read a record from a string (no subtype record) * * @param[in] string the string to read from */ @@ -17,15 +17,15 @@ template < typename Record, typename... Arguments > Record read( const std::string& string, Arguments... arguments ) { using namespace njoy::tools; + using namespace njoy::NDItk; Record record; auto iter = string.begin(); auto end = string.end(); - std::string key = disco::FreeFormatCharacter::read< std::string >( iter, end ); - if ( record.keyword() == key ) { + base::Keyword key( disco::FreeFormatCharacter::read< std::string >( iter, end ) ); + if ( record.keyword() == key.keyword() ) { - //! @todo take into account _0 style keys record.read( iter, end, arguments... ); //! @todo verify the string is now empty @@ -35,9 +35,44 @@ Record read( const std::string& string, Arguments... arguments ) { Log::error( "The record keyword is not the one expected" ); Log::info( "Expected: \'{}\'", record.keyword() ); - Log::info( "Found: \'{}\'", key ); + Log::info( "Found: \'{}\'", key.keyword() ); throw std::exception(); } } +/** + * @brief Read a record from a string (subtype record) + * + * @param[in] string the string to read from + */ +template < typename Record, typename... Arguments > +Record readWithSubtype( const std::string& string, Arguments... arguments ) { + + using namespace njoy::tools; + using namespace njoy::NDItk; + + Record record; + auto iter = string.begin(); + auto end = string.end(); + + base::Keyword key( disco::FreeFormatCharacter::read< std::string >( iter, end ) ); + if ( key.keyword().find( record.keyword() ) == 0 ) { + + if ( key.particle().has_value() ) { + + record = Record( key.particle().value() ); + } + record.read( iter, end, arguments... ); + + //! @todo verify the string is now empty + return record; + } + else { + + Log::error( "The record keyword is not the one expected" ); + Log::info( "Expected: \'{}\'", record.keyword() ); + Log::info( "Found: \'{}\'", key.keyword() ); + throw std::exception(); + } +} #endif diff --git a/python/test/Test_NDItk_MultigroupTable.py b/python/test/Test_NDItk_MultigroupTable.py index 7a9c6ad..b54e031 100644 --- a/python/test/Test_NDItk_MultigroupTable.py +++ b/python/test/Test_NDItk_MultigroupTable.py @@ -29,9 +29,11 @@ def verify_chunk( self, chunk ) : self.assertAlmostEqual( 2.53e-8, metadata.temperature ) self.assertAlmostEqual( 1e+10, metadata.dilution ) self.assertEqual( 7, metadata.number_groups ) + self.assertEqual( 3, metadata.number_outgoing_groups( 0 ) ) + self.assertEqual( 2, metadata.number_outgoing_groups( 1001 ) ) self.assertEqual( 2, metadata.number_reactions ) - # verify content - energy boundaries + # verify content - primary energy boundaries structure = chunk.structure self.assertEqual( 7, structure.number_groups ) self.assertAlmostEqual( 20, structure.boundaries[0] ) @@ -43,6 +45,21 @@ def verify_chunk( self, chunk ) : self.assertAlmostEqual( 1, structure.boundaries[6] ) self.assertAlmostEqual( 1e-11, structure.boundaries[7] ) + # verify content - outgoing energy boundaries: 0 + structure = chunk.outgoing_structure( 0 ) + self.assertEqual( 3, structure.number_groups ) + self.assertAlmostEqual( 20, structure.boundaries[0] ) + self.assertAlmostEqual( 10, structure.boundaries[1] ) + self.assertAlmostEqual( 5, structure.boundaries[2] ) + self.assertAlmostEqual( 1e-11, structure.boundaries[3] ) + + # verify content - outgoing energy boundaries: 1001 + structure = chunk.outgoing_structure( 1001 ) + self.assertEqual( 2, structure.number_groups ) + self.assertAlmostEqual( 20, structure.boundaries[0] ) + self.assertAlmostEqual( 10, structure.boundaries[1] ) + self.assertAlmostEqual( 1e-11, structure.boundaries[2] ) + # verify content - flux weights flux = chunk.flux self.assertEqual( 7, flux.number_groups ) @@ -117,7 +134,8 @@ def verify_chunk( self, chunk ) : process = '08/07/2013', awr = 233.0248, weight = 235.043937521619, temperature = 2.53e-8, dilution = 1e+10, structure = EnergyGroupStructure( [ 20., 18., 16., 14., 10., 5, 1, 1e-11 ] ), - outgoing = [], + outgoing = [ EnergyGroupStructure( 0, [ 20., 10., 5, 1e-11 ] ), + EnergyGroupStructure( 1001, [ 20., 10., 1e-11 ] )], flux = FluxWeights( [ 0.1, 0.2, 0.25, 0.05, 0.15, 0.04, 0.06 ] ), xs = ReactionCrossSections( xs = [ CrossSection( 2, 0., [ 10., 20., 30., 40., 50., 60., 70. ] ), diff --git a/python/test/multigroup/Test_NDItk_multigroup_EnergyGroupStructure.py b/python/test/multigroup/Test_NDItk_multigroup_EnergyGroupStructure.py index e6cd203..b2b532b 100644 --- a/python/test/multigroup/Test_NDItk_multigroup_EnergyGroupStructure.py +++ b/python/test/multigroup/Test_NDItk_multigroup_EnergyGroupStructure.py @@ -14,6 +14,10 @@ class Test_NDItk_multigroup_EnergyGroupStructure( unittest.TestCase ) : ' 20 18 16 14 10\n' ' 5 1 1e-11\n' ) + chunk_outgoing_values = [ 20, 10, 1e-11 ] + chunk_outgoing_string = ( 'e_bounds_1001\n' + ' 20 10 1e-11\n' ) + def test_component( self ) : def verify_chunk( self, chunk ) : @@ -41,6 +45,26 @@ def verify_chunk( self, chunk ) : self.assertAlmostEqual( self.chunk_values[index], values[index] ) + def verify_outgoing_chunk( self, chunk ) : + + # verify content + self.assertEqual( 2, chunk.number_groups ) + self.assertAlmostEqual( 20, chunk.boundaries[0] ) + self.assertAlmostEqual( 10, chunk.boundaries[1] ) + self.assertAlmostEqual( 1e-11, chunk.boundaries[2] ) + + self.assertEqual( self.chunk_outgoing_string, chunk.to_string() ) + + # verify the record + self.assertEqual( 'e_bounds_1001', chunk.keyword ) + self.assertEqual( False, chunk.empty ) + self.assertEqual( 3, chunk.size ) + + values = chunk.values + for index in range( chunk.size ) : + + self.assertAlmostEqual( self.chunk_outgoing_values[index], values[index] ) + # the data is given explicitly chunk = EnergyGroupStructure( boundaries = [ 20., 18., 16., 14., 10., 5, 1, 1e-11 ] ) @@ -51,6 +75,16 @@ def verify_chunk( self, chunk ) : verify_chunk( self, chunk ) + # the data is given explicitly for an outgoing particle + chunk = EnergyGroupStructure( particle = 1001, boundaries = [ 20., 10., 1e-11 ] ) + + verify_outgoing_chunk( self, chunk ) + + # the data is read from a string for an outgoing particle + chunk = EnergyGroupStructure.from_string( self.chunk_outgoing_string, 3 ) + + verify_outgoing_chunk( self, chunk ) + if __name__ == '__main__' : unittest.main() diff --git a/src/NDItk/MultigroupTable.hpp b/src/NDItk/MultigroupTable.hpp index 9976611..8371e93 100644 --- a/src/NDItk/MultigroupTable.hpp +++ b/src/NDItk/MultigroupTable.hpp @@ -30,7 +30,10 @@ class MultigroupTable { /* auxiliary functions */ + #include "NDItk/MultigroupTable/src/generateOutgoingStructureMetadata.hpp" #include "NDItk/MultigroupTable/src/readRecord.hpp" + #include "NDItk/MultigroupTable/src/readPrimaryStructure.hpp" + #include "NDItk/MultigroupTable/src/readOutgoingStructure.hpp" #include "NDItk/MultigroupTable/src/verify.hpp" public: @@ -51,6 +54,29 @@ class MultigroupTable { */ const multigroup::EnergyGroupStructure& structure() const { return this->primary_structure_; } + /** + * @brief Return the group structure record for an outgoing particle + */ + const multigroup::EnergyGroupStructure& outgoingStructure( unsigned int particle ) const { + + auto pos = std::lower_bound( this->outgoing_structure_.begin(), + this->outgoing_structure_.end(), + particle, + [] ( auto&& left, auto&& right ) { + + return left.particle() < right; + } ); + if ( pos != this->outgoing_structure_.end() ) { + + if ( pos->particle() == particle ) { + + return *pos; + } + } + Log::error( "The requested outgoing particle \'{}\' has no outgoing group structure", particle ); + throw std::exception(); + } + /** * @brief Return the flux weight record */ @@ -85,13 +111,11 @@ class MultigroupTable { this->metadata_.print( iter ); this->primary_structure_.print( iter ); + for ( const auto& entry : this->outgoing_structure_ ) { entry.print( iter ); } this->weights_.print( iter ); this->xs_.print( iter ); this->release_.print( iter ); - *iter++ = 'e'; - *iter++ = 'n'; - *iter++ = 'd'; - *iter++ = '\n'; + base::Keyword( "end" ).print( iter ); }; }; diff --git a/src/NDItk/MultigroupTable/src/ctor.hpp b/src/NDItk/MultigroupTable/src/ctor.hpp index 2e7499b..7461559 100644 --- a/src/NDItk/MultigroupTable/src/ctor.hpp +++ b/src/NDItk/MultigroupTable/src/ctor.hpp @@ -33,7 +33,8 @@ MultigroupTable( std::string zaid, std::string libname, std::string source, std::optional< multigroup::AverageFissionEnergyRelease > release = std::nullopt ) : metadata_( std::move( zaid ), std::move( libname ), std::move( source ), std::move( process ), awr, weight, temperature, dilution, - xs.numberGroups(), {}, xs.numberReactions() ), + structure.numberGroups(), generateOutgoingStructureMetadata( outgoing ), + xs.numberReactions() ), primary_structure_( std::move( structure ) ), outgoing_structure_( std::move( outgoing ) ), weights_( std::move( weigths ) ), diff --git a/src/NDItk/MultigroupTable/src/generateOutgoingStructureMetadata.hpp b/src/NDItk/MultigroupTable/src/generateOutgoingStructureMetadata.hpp new file mode 100644 index 0000000..64003c1 --- /dev/null +++ b/src/NDItk/MultigroupTable/src/generateOutgoingStructureMetadata.hpp @@ -0,0 +1,16 @@ +static std::map< unsigned int, unsigned int > +generateOutgoingStructureMetadata( const std::vector< multigroup::EnergyGroupStructure >& outgoing ) { + + std::map< unsigned int, unsigned int > metadata; + for ( const auto& entry : outgoing ) { + + unsigned int particle = entry.particle().value(); + if ( metadata.find( particle ) != metadata.end() ) { + + Log::error( "Found duplicate outgoing group structure for particle \'{}\'", particle ); + throw std::exception(); + } + metadata[ particle ] = entry.numberGroups(); + } + return metadata; +} \ No newline at end of file diff --git a/src/NDItk/MultigroupTable/src/read.hpp b/src/NDItk/MultigroupTable/src/read.hpp index 6809890..54b3f7b 100644 --- a/src/NDItk/MultigroupTable/src/read.hpp +++ b/src/NDItk/MultigroupTable/src/read.hpp @@ -15,18 +15,15 @@ void read( Iterator& iter, const Iterator& end ) { this->metadata_.read( keyword, iter, end ); } - else if ( keyword == this->primary_structure_.keyword() ) { + else if ( keyword.find( this->primary_structure_.keyword() ) == 0 ) { - if ( this->metadata_.numberGroups().has_value() ) { + if ( keyword == this->primary_structure_.keyword() ) { - readRecord( this->primary_structure_, iter, end, - this->metadata_.numberGroups().value() + 1 ); + readPrimaryStructure( iter, end ); } else { - Log::error( "Metadata required for the \'\' record was not found", keyword ); - Log::info( "Required metadata is missing: number of groups in the primary group structure" ); - throw std::exception(); + readOutgoingStructure( keyword, iter, end ); } } else if ( keyword == this->weights_.keyword() ) { diff --git a/src/NDItk/MultigroupTable/src/readOutgoingStructure.hpp b/src/NDItk/MultigroupTable/src/readOutgoingStructure.hpp new file mode 100644 index 0000000..9dbe4c0 --- /dev/null +++ b/src/NDItk/MultigroupTable/src/readOutgoingStructure.hpp @@ -0,0 +1,32 @@ +template< typename Iterator > +void readOutgoingStructure( const std::string& key, Iterator& iter, const Iterator& end ) { + + base::Keyword secondary( key ); + unsigned int particle = secondary.particle().value(); + if ( this->metadata_.numberOutgoingGroups( particle ).has_value() ) { + + auto pos = std::lower_bound( this->outgoing_structure_.begin(), + this->outgoing_structure_.end(), + particle, + [] ( auto&& left, auto&& right ) { + + return left.particle() < right; + } ); + if ( pos != this->outgoing_structure_.end() ) { + + if ( pos->particle() == particle ) { + + Log::error( "Duplicate keyword found: \'{}\'", secondary.keyword() ); + throw std::exception(); + } + } + pos = this->outgoing_structure_.insert( pos, multigroup::EnergyGroupStructure( particle ) ); + readRecord( *pos, iter, end, this->metadata_.numberOutgoingGroups( particle ).value() + 1 ); + } + else { + + Log::error( "Metadata required for the \'\' record was not found", secondary.keyword() ); + Log::info( "Required metadata is missing: number of groups in the outgoing group structure" ); + throw std::exception(); + } +} \ No newline at end of file diff --git a/src/NDItk/MultigroupTable/src/readPrimaryStructure.hpp b/src/NDItk/MultigroupTable/src/readPrimaryStructure.hpp new file mode 100644 index 0000000..9188994 --- /dev/null +++ b/src/NDItk/MultigroupTable/src/readPrimaryStructure.hpp @@ -0,0 +1,15 @@ +template< typename Iterator > +void readPrimaryStructure( Iterator& iter, const Iterator& end ) { + + if ( this->metadata_.numberGroups().has_value() ) { + + readRecord( this->primary_structure_, iter, end, + this->metadata_.numberGroups().value() + 1 ); + } + else { + + Log::error( "Metadata required for the \'\' record was not found", this->primary_structure_.keyword() ); + Log::info( "Required metadata is missing: number of groups in the primary group structure" ); + throw std::exception(); + } +} \ No newline at end of file diff --git a/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp b/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp index 8b498c8..f81d0b5 100644 --- a/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp +++ b/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp @@ -32,6 +32,10 @@ SCENARIO( "MultigroupTable" ) { double dilution = 1e+10; multigroup::EnergyGroupStructure structure( { 20., 18.123456789, 16.0000000000001, 14., 10., 5, 1, 1e-11 } ); + std::vector< multigroup::EnergyGroupStructure > outgoing = { + { 0, { 20., 10., 5, 1e-11 } }, + { 1001, { 20., 10., 1e-11 } } + }; multigroup::FluxWeights weights( { 0.1, 0.2, 0.25, 0.05, 0.15, 0.04, 0.06 } ); multigroup::ReactionCrossSections xs( { { 2, 0.0, { 10., 20., 30., 40., 50., 60., 70. } }, { 16, 1.1234567, { 1., 2., 3., 4., 5., 6., 7. } } } ); @@ -40,8 +44,8 @@ SCENARIO( "MultigroupTable" ) { MultigroupTable chunk( std::move( zaid ), std::move( name ), std::move( source ), std::move( process ), awr, weight, temperature, dilution, - std::move( structure ), {}, std::move( weights ), std::move( xs ), - std::move( release ) ); + std::move( structure ), std::move( outgoing ), std::move( weights ), + std::move( xs ), std::move( release ) ); THEN( "a MultigroupTable can be constructed and members can " "be tested" ) { @@ -131,11 +135,19 @@ std::string chunk() { " 10000000000\n" "num_grps\n" " 7\n" + "num_grps_0\n" + " 3\n" + "num_grps_1001\n" + " 2\n" "num_reac\n" " 2\n" "e_bounds\n" " 20 18.123456789 16.0000000000001 14 10\n" " 5 1 1e-11\n" + "e_bounds_0\n" + " 20 10 5 1e-11\n" + "e_bounds_1001\n" + " 20 10 1e-11\n" "wgts\n" " 0.1 0.2 0.25 0.05 0.15\n" " 0.04 0.06\n" @@ -168,6 +180,7 @@ void verifyChunk( const MultigroupTable& chunk ) { // principal group structure CHECK( "e_bounds" == chunk.structure().keyword() ); + CHECK( std::nullopt == chunk.structure().particle() ); CHECK( false == chunk.structure().empty() ); CHECK( 8 == chunk.structure().size() ); CHECK( 8 == chunk.structure().boundaries().size() ); @@ -181,6 +194,31 @@ void verifyChunk( const MultigroupTable& chunk ) { CHECK_THAT( 1, WithinRel( chunk.structure().boundaries()[6] ) ); CHECK_THAT( 1e-11, WithinRel( chunk.structure().boundaries()[7] ) ); + // outgoing group structure: 0 + auto structure = chunk.outgoingStructure( 0 ); + CHECK( "e_bounds_0" == structure.keyword() ); + CHECK( 0 == structure.particle() ); + CHECK( false == structure.empty() ); + CHECK( 4 == structure.size() ); + CHECK( 4 == structure.boundaries().size() ); + CHECK( 3 == structure.numberGroups() ); + CHECK_THAT( 20, WithinRel( structure.boundaries()[0] ) ); + CHECK_THAT( 10, WithinRel( structure.boundaries()[1] ) ); + CHECK_THAT( 5, WithinRel( structure.boundaries()[2] ) ); + CHECK_THAT( 1e-11, WithinRel( structure.boundaries()[3] ) ); + + // outgoing group structure: 1001 + structure = chunk.outgoingStructure( 1001 ); + CHECK( "e_bounds_1001" == structure.keyword() ); + CHECK( 1001 == structure.particle() ); + CHECK( false == structure.empty() ); + CHECK( 3 == structure.size() ); + CHECK( 3 == structure.boundaries().size() ); + CHECK( 2 == structure.numberGroups() ); + CHECK_THAT( 20, WithinRel( structure.boundaries()[0] ) ); + CHECK_THAT( 10, WithinRel( structure.boundaries()[1] ) ); + CHECK_THAT( 1e-11, WithinRel( structure.boundaries()[2] ) ); + // flux weights CHECK( "wgts" == chunk.flux().keyword() ); CHECK( false == chunk.flux().empty() ); diff --git a/src/NDItk/multigroup/EnergyGroupStructure/src/ctor.hpp b/src/NDItk/multigroup/EnergyGroupStructure/src/ctor.hpp index 0398e49..342f8cc 100644 --- a/src/NDItk/multigroup/EnergyGroupStructure/src/ctor.hpp +++ b/src/NDItk/multigroup/EnergyGroupStructure/src/ctor.hpp @@ -6,7 +6,7 @@ EnergyGroupStructure() : RealListRecord( base::Keyword( "e_bounds" ) ) {} /** * @brief Default constructor for a secondary particle group structure * - * @param[in] particle the secondary particle ID + * @param[in] particle the secondary particle identifier */ EnergyGroupStructure( unsigned int particle ) : RealListRecord( base::Keyword( "e_bounds", particle ) ) {} @@ -25,7 +25,7 @@ EnergyGroupStructure( std::vector< double > boundaries ) : /** * @brief Constructor for a secondary particle group structure * - * @param[in] particle the secondary particle ID + * @param[in] particle the secondary particle identifier * @param[in] boundaries the group structure boundaries */ EnergyGroupStructure( unsigned int particle, std::vector< double > boundaries ) :