diff --git a/cmake/unit_testing.cmake b/cmake/unit_testing.cmake index bbb3267..0129499 100644 --- a/cmake/unit_testing.cmake +++ b/cmake/unit_testing.cmake @@ -33,6 +33,7 @@ add_subdirectory( src/NDItk/base/SingleStringRecord/test ) add_subdirectory( src/NDItk/base/IntegerListRecord/test ) add_subdirectory( src/NDItk/base/RealListRecord/test ) add_subdirectory( src/NDItk/base/StringListRecord/test ) +add_subdirectory( src/NDItk/base/InformationRecord/test ) add_subdirectory( src/NDItk/multigroup/Metadata/test ) add_subdirectory( src/NDItk/multigroup/EnergyGroupStructure/test ) diff --git a/python/src/MultigroupTable.python.cpp b/python/src/MultigroupTable.python.cpp index 71bced3..18cd4d3 100644 --- a/python/src/MultigroupTable.python.cpp +++ b/python/src/MultigroupTable.python.cpp @@ -50,6 +50,7 @@ void wrapMultigroupTable( python::module& module, python::module& ) { ReactionCrossSections, ScatteringMatrix, std::optional< std::string >, + std::optional< std::string >, std::optional< double >, std::optional< TotalCrossSection >, std::optional< AverageFissionEnergyRelease >, @@ -66,6 +67,7 @@ void wrapMultigroupTable( python::module& module, python::module& ) { python::arg( "structure" ), python::arg( "outgoing" ), python::arg( "velocities" ), python::arg( "flux" ), python::arg( "xs" ), python::arg( "scattering" ), + python::arg( "information" ) = std::nullopt, python::arg( "source" ) = std::nullopt, python::arg( "weight" ) = std::nullopt, python::arg( "total" ) = std::nullopt, @@ -93,6 +95,7 @@ void wrapMultigroupTable( python::module& module, python::module& ) { " flux the flux weights\n" " xs the reaction cross section data\n" " scattering the scattering matrix\n" + " information the table information line (optional)\n" " source the source date (optional)\n" " weight the atomic weight of the target (optional)\n" " total the total cross section (optional)\n" diff --git a/python/src/definitions.hpp b/python/src/definitions.hpp index 1d29430..918e323 100644 --- a/python/src/definitions.hpp +++ b/python/src/definitions.hpp @@ -9,6 +9,7 @@ #include #include "tools/views/views-python.hpp" #include "NDItk/fromFile.hpp" +#include "NDItk/toFile.hpp" namespace python = pybind11; @@ -142,8 +143,20 @@ void addStandardTableDefinitions( PythonClass& table ) { "table\n\n" "Arguments:\n" " filename the file name and path" + ) + .def( + + "to_file", + [] ( const Table& self, const std::string& filename ) { + + return njoy::NDItk::toFile( self, filename ); + }, + "Read an ACE table from a file\n\n" + "An exception is raised if something goes wrong while reading the\n" + "table\n\n" + "Arguments:\n" + " filename the file name and path" ); } #endif - diff --git a/python/src/multigroup/Metadata.python.cpp b/python/src/multigroup/Metadata.python.cpp index 10b07bc..09141dc 100644 --- a/python/src/multigroup/Metadata.python.cpp +++ b/python/src/multigroup/Metadata.python.cpp @@ -37,6 +37,7 @@ void wrapMetadata( python::module& module, python::module& ) { unsigned int, std::map< unsigned int, unsigned int >, unsigned int, unsigned int, std::optional< std::string >, + std::optional< std::string >, std::optional< double >, std::optional< int >, std::optional< int > >(), @@ -44,6 +45,7 @@ void wrapMetadata( python::module& module, python::module& ) { python::arg( "process" ), python::arg( "awr" ), python::arg( "temperature" ), python::arg( "dilution" ), python::arg( "groups" ), python::arg( "outgoing" ), python::arg( "reactions" ), python::arg( "legendre" ), + python::arg( "information" ) = std::nullopt, python::arg( "source" ) = std::nullopt, python::arg( "weight" ) = std::nullopt, python::arg( "upscatter" ) = std::nullopt, @@ -62,6 +64,7 @@ void wrapMetadata( python::module& module, python::module& ) { " outgoing the number of groups in the outgoing group structures\n" " reactions the number of reactions defined in the table\n" " legendre the number of Legendre moments in the table\n" + " information the table information line (optional)\n" " source the source date (optional)\n" " weight the atomic weight of the target (optional)\n" " upscatter the number of upscatter groups (optional)\n" @@ -73,6 +76,12 @@ void wrapMetadata( python::module& module, python::module& ) { &Record::zaid, "The zaid of the table" ) + .def_property_readonly( + + "information", + &Record::information, + "The table information line" + ) .def_property_readonly( "library_name", diff --git a/python/test/Test_NDItk_MultigroupTable.py b/python/test/Test_NDItk_MultigroupTable.py index e21ee2b..c5e109d 100644 --- a/python/test/Test_NDItk_MultigroupTable.py +++ b/python/test/Test_NDItk_MultigroupTable.py @@ -1,5 +1,6 @@ # standard imports import unittest +import os # third party imports @@ -29,6 +30,7 @@ def verify_chunk( self, chunk ) : # verify content - metadata metadata = chunk.metadata self.assertEqual( '92235.711nm', metadata.zaid ) + self.assertEqual( 'this is some information for the table', metadata.information ) self.assertEqual( 'mendf71x', metadata.library_name ) self.assertEqual( '12/22/2011', metadata.source_date ) self.assertEqual( '08/07/2013', metadata.process_date ) @@ -429,7 +431,8 @@ def verify_chunk( self, chunk ) : self.assertAlmostEqual( 150, kerma.values[1] ) # the data is given explicitly - chunk = MultigroupTable( zaid = '92235.711nm', libname = 'mendf71x', source = '12/22/2011', + chunk = MultigroupTable( zaid = '92235.711nm', libname = 'mendf71x', + information = 'this is some information for the table', source = '12/22/2011', process = '08/07/2013', awr = 233.0248, weight = 235.043937521619, temperature = 2.53e-8, dilution = 1e+10, structure = EnergyGroupStructure( [ 20., 18.123456789, 16.0000000000001, 14., 10., 5, 1, 1e-11 ] ), @@ -501,6 +504,12 @@ def verify_chunk( self, chunk ) : verify_chunk( self, chunk ) + # the data is read from a file + chunk.to_file( 'test.txt' ) + chunk = MultigroupTable.from_file( 'test.txt' ) + verify_chunk( self, chunk ) + os.remove( 'test.txt' ) + if __name__ == '__main__' : unittest.main() diff --git a/src/NDItk/MultigroupTable/src/ctor.hpp b/src/NDItk/MultigroupTable/src/ctor.hpp index 32ccdfc..e46eace 100644 --- a/src/NDItk/MultigroupTable/src/ctor.hpp +++ b/src/NDItk/MultigroupTable/src/ctor.hpp @@ -25,6 +25,7 @@ MultigroupTable() : * @param[in] total the total cross section * @param[in] xs the reaction cross section data * @param[in] scattering the scattering matrix + * @param[in] information the table information line (optional) * @param[in] source the source date (optional) * @param[in] weight the atomic weight of the target (optional) * @param[in] release the average fission energy release data (optional) @@ -45,6 +46,7 @@ MultigroupTable( std::string zaid, std::string libname, multigroup::FluxWeights weigths, multigroup::ReactionCrossSections xs, multigroup::ScatteringMatrix scattering, + std::optional< std::string > information = std::nullopt, std::optional< std::string > source = std::nullopt, std::optional< double > weight = std::nullopt, std::optional< multigroup::TotalCrossSection > total = std::nullopt, @@ -60,7 +62,7 @@ MultigroupTable( std::string zaid, std::string libname, std::move( process ), awr, temperature, dilution, structure.numberGroups(), generateOutgoingStructureMetadata( outgoing ), xs.numberReactions(), scattering.numberLegendreMoments(), - std::move( source ), std::move( weight ), + std::move( information ), std::move( source ), std::move( weight ), std::nullopt, std::nullopt ), primary_structure_( std::move( structure ) ), velocities_( std::move( velocities ) ), diff --git a/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp b/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp index 29cd831..4120460 100644 --- a/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp +++ b/src/NDItk/MultigroupTable/test/MultigroupTable.test.cpp @@ -24,6 +24,7 @@ SCENARIO( "MultigroupTable" ) { WHEN( "the data is given explicitly" ) { std::string zaid = "92235.711nm"; + std::string information = "this is some information for the table"; std::string name = "mendf71x"; std::string source = "12/22/2011"; std::string process = "08/07/2013"; @@ -107,7 +108,7 @@ SCENARIO( "MultigroupTable" ) { std::move( structure ), std::move( outgoing ), std::move( velocities ), std::move( weights ), std::move( xs ), std::move( scattering ), - std::move( source ), weight, + std::move( information ), std::move( source ), weight, std::move( total ), std::move( release ), std::move( types ), std::move( transport ), std::move( production ), @@ -174,6 +175,7 @@ SCENARIO( "MultigroupTable" ) { WHEN( "the number of groups is inconsistent" ) { std::string zaid = "92235.711nm"; + std::string information = "this is some information for the table"; std::string name = "mendf71x"; std::string source = "12/22/2011"; std::string process = "08/07/2013"; @@ -254,7 +256,7 @@ SCENARIO( "MultigroupTable" ) { std::move( structure ), {}, std::move( velocities ), std::move( weights ), std::move( xs ), std::move( scattering ), - std::move( source ), weight, + std::move( information ), std::move( source ), weight, std::move( total ), std::move( release ), std::move( types ), std::move( transport ), std::move( production ), @@ -269,6 +271,8 @@ std::string chunk() { return "zaid\n" " 92235.711nm\n" + "info\n" + " this is some information for the table\n" "library_name\n" " mendf71x\n" "date_source\n" @@ -391,6 +395,7 @@ void verifyChunk( const MultigroupTable& chunk ) { // metadata CHECK( "92235.711nm" == chunk.metadata().zaid() ); + CHECK( "this is some information for the table" == chunk.metadata().information() ); CHECK( "mendf71x" == chunk.metadata().libraryName() ); CHECK( "12/22/2011" == chunk.metadata().sourceDate() ); CHECK( "08/07/2013" == chunk.metadata().processDate() ); diff --git a/src/NDItk/base/InformationRecord.hpp b/src/NDItk/base/InformationRecord.hpp new file mode 100644 index 0000000..93c9b9c --- /dev/null +++ b/src/NDItk/base/InformationRecord.hpp @@ -0,0 +1,115 @@ +#ifndef NJOY_NDITK_BASE_INFORMATIONRECORD +#define NJOY_NDITK_BASE_INFORMATIONRECORD + +// system includes +#include +#include +#include + +// other includes +#include "NDItk/base/SingleValueRecord.hpp" + +namespace njoy { +namespace NDItk { +namespace base { + +/** + * @brief An NDI record with a line of information + */ +class InformationRecord : protected SingleValueRecord< InformationRecord, std::string > { + + friend class SingleValueRecord< InformationRecord, std::string >; + using Parent = SingleValueRecord< InformationRecord, std::string >; + +protected: + + /** + * @brief Write the record data + * + * This assumes that the record is not empty. + * + * @param[in] iter the current position in the output + */ + template< typename OutputIterator > + void write( OutputIterator& iter ) const { + + unsigned int indent = 4; + while ( indent-- ) { *iter++ = ' '; } + for ( auto c : this->data().value() ) { *iter++ = c; } + *iter++ = '\n'; + }; + + using Parent::key; + +public: + + /* constructor */ + + /** + * @brief Constructor + * + * @param[in] keyword the keyword of the record + */ + InformationRecord() : + SingleValueRecord( Keyword( "info" ) ) {} + + /** + * @brief Constructor + * + * @param[in] keyword the keyword of the record + * @param[in] value the value of the record + */ + InformationRecord( std::string value ) : + SingleValueRecord( Keyword( "info" ), value ) {} + + /* methods */ + + using Parent::keyword; + using Parent::data; + using Parent::empty; + using Parent::print; + + /** + * @brief Read the record data + * + * @param[in] iter the current position in the input + */ + template< typename Iterator > + void read( Iterator& iter, const Iterator& end ) { + + // move over the first endline + while ( ( iter != end ) && std::isspace( *iter ) && ( *iter != '\n' ) ) { + + ++iter; + } + if ( *iter != '\n' ) { + + Log::error( "The table information line should start on a new line" ); + throw std::exception(); + } + ++iter; + // move over initial white space + while ( ( iter != end ) && std::isspace( *iter ) && ( *iter != '\n' ) ) { + + ++iter; + } + auto pos = std::find_if( iter, end, [] ( auto&& character ) + { return character == '\n'; } ); + if ( pos == iter ) { + + Log::error( "There appears to be no table information line" ); + throw std::exception(); + } + this->data() = std::string( iter, pos ); + if ( pos != end ) { + + iter = pos + 1; + } + }; +}; + +} // base namespace +} // NDItk namespace +} // njoy namespace + +#endif diff --git a/src/NDItk/base/InformationRecord/test/CMakeLists.txt b/src/NDItk/base/InformationRecord/test/CMakeLists.txt new file mode 100644 index 0000000..579432a --- /dev/null +++ b/src/NDItk/base/InformationRecord/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( base.InformationRecord InformationRecord.test.cpp ) \ No newline at end of file diff --git a/src/NDItk/base/InformationRecord/test/InformationRecord.test.cpp b/src/NDItk/base/InformationRecord/test/InformationRecord.test.cpp new file mode 100644 index 0000000..b574d80 --- /dev/null +++ b/src/NDItk/base/InformationRecord/test/InformationRecord.test.cpp @@ -0,0 +1,78 @@ +// include Catch2 +#include + +// what we are testing +#include "NDItk/base/InformationRecord.hpp" + +// other includes + +// convenience typedefs +using namespace njoy::NDItk; +using InformationRecord = base::InformationRecord; + +std::string chunk(); +void verifyChunk( const InformationRecord& ); + +SCENARIO( "InformationRecord" ) { + + GIVEN( "valid data for a InformationRecord instance" ) { + + std::string record = chunk(); + + WHEN( "the data is given explicitly" ) { + + InformationRecord chunk( "this is some information for the table" ); + + THEN( "a InformationRecord can be constructed and members can " + "be tested" ) { + + verifyChunk( chunk ); + } // THEN + + THEN( "the record can be printed" ) { + + std::string buffer; + auto output = std::back_inserter( buffer ); + chunk.print( output ); + + CHECK( buffer == record ); + } // THEN + } // WHEN + + WHEN( "the data is defined by iterators" ) { + + auto iter = record.begin() + 4; + auto end = record.end(); + InformationRecord chunk; + chunk.read( iter, end ); + + THEN( "a InformationRecord can be constructed and members can " + "be tested" ) { + + verifyChunk( chunk ); + } // THEN + + THEN( "the record can be printed" ) { + + std::string buffer; + auto output = std::back_inserter( buffer ); + chunk.print( output ); + + CHECK( buffer == record ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO + +std::string chunk() { + + return "info\n" + " this is some information for the table\n"; +} + +void verifyChunk( const InformationRecord& chunk ) { + + CHECK( "info" == chunk.keyword() ); + CHECK( false == chunk.empty() ); + CHECK( "this is some information for the table" == chunk.data() ); +} diff --git a/src/NDItk/multigroup/Metadata.hpp b/src/NDItk/multigroup/Metadata.hpp index c9c1aba..8ca4502 100644 --- a/src/NDItk/multigroup/Metadata.hpp +++ b/src/NDItk/multigroup/Metadata.hpp @@ -6,6 +6,7 @@ // other includes #include "tools/Log.hpp" +#include "NDItk/base/InformationRecord.hpp" #include "NDItk/base/SingleIntegerRecord.hpp" #include "NDItk/base/SingleRealRecord.hpp" #include "NDItk/base/SingleStringRecord.hpp" @@ -22,6 +23,7 @@ class Metadata { /* fields */ base::SingleStringRecord zaid_; + base::InformationRecord information_; base::SingleStringRecord library_name_; base::SingleStringRecord source_date_; base::SingleStringRecord process_date_; @@ -55,7 +57,8 @@ class Metadata { */ bool isMetadataKey( const std::string& keyword ) const { - return ( keyword == this->zaid_.keyword() ) || + return ( keyword == this->information_.keyword() ) || + ( keyword == this->zaid_.keyword() ) || ( keyword == this->library_name_.keyword() ) || ( keyword == this->source_date_.keyword() ) || ( keyword == this->process_date_.keyword() ) || @@ -76,6 +79,11 @@ class Metadata { */ decltype(auto) zaid() const { return this->zaid_.data(); } + /** + * @brief Return the table information line + */ + decltype(auto) information() const { return this->information_.data(); } + /** * @brief Return the library name */ @@ -186,6 +194,7 @@ class Metadata { void read( const std::string& keyword, Iterator& iter, const Iterator& end ) { if ( keyword == this->zaid_.keyword() ) { readRecord( this->zaid_, iter, end ); } + else if ( keyword == this->information_.keyword() ) { readRecord( this->information_, iter, end ); } else if ( keyword == this->library_name_.keyword() ) { readRecord( this->library_name_, iter, end ); } else if ( keyword == this->source_date_.keyword() ) { readRecord( this->source_date_, iter, end ); } else if ( keyword == this->process_date_.keyword() ) { readRecord( this->process_date_, iter, end ); } @@ -243,6 +252,7 @@ class Metadata { void print( OutputIterator& iter ) const { this->zaid_.print( iter ); + this->information_.print( iter ); this->library_name_.print( iter ); this->source_date_.print( iter ); this->process_date_.print( iter ); diff --git a/src/NDItk/multigroup/Metadata/src/ctor.hpp b/src/NDItk/multigroup/Metadata/src/ctor.hpp index cddd00f..691424c 100644 --- a/src/NDItk/multigroup/Metadata/src/ctor.hpp +++ b/src/NDItk/multigroup/Metadata/src/ctor.hpp @@ -2,6 +2,7 @@ * @brief Default constructor */ Metadata() : zaid_( base::Keyword( "zaid" ) ), + information_(), library_name_( base::Keyword( "library_name" ) ), source_date_( base::Keyword( "date_source" ) ), process_date_( base::Keyword( "date_processed" ) ), @@ -40,11 +41,15 @@ Metadata( std::string zaid, std::string libname, std::string process, unsigned int groups, const std::map< unsigned int, unsigned int >& outgoing, unsigned int reactions, unsigned int legendre, + std::optional< std::string > information = std::nullopt, std::optional< std::string > source = std::nullopt, std::optional< double > weight = std::nullopt, std::optional< int > upscatter = std::nullopt, std::optional< int > downscatter = std::nullopt ) : zaid_( base::Keyword( "zaid" ), std::move( zaid ) ), + information_( information.has_value() + ? base::InformationRecord( std::move( information.value() ) ) + : base::InformationRecord() ), library_name_( base::Keyword( "library_name" ), std::move( libname ) ), source_date_( source.has_value() ? base::SingleStringRecord( base::Keyword( "date_source" ), std::move( source.value() ) ) diff --git a/src/NDItk/multigroup/Metadata/test/Metadata.test.cpp b/src/NDItk/multigroup/Metadata/test/Metadata.test.cpp index 5caa160..305c297 100644 --- a/src/NDItk/multigroup/Metadata/test/Metadata.test.cpp +++ b/src/NDItk/multigroup/Metadata/test/Metadata.test.cpp @@ -25,6 +25,7 @@ SCENARIO( "Metadata" ) { std::string zaid = "92235.711nm"; std::string name = "mendf71x"; + std::optional< std::string > information = "this is some information for the table"; std::optional< std::string > source = "12/22/2011"; std::string process = "08/07/2013"; double awr = 233.0248; @@ -42,6 +43,7 @@ SCENARIO( "Metadata" ) { std::move( process ), awr, temperature, dilution, groups, outgoing, reactions, legendre, + std::move( information ), std::move( source ), weight, upscatter, downscatter ); @@ -89,6 +91,7 @@ SCENARIO( "Metadata" ) { chunk.read( readKey( iter, end ), iter, end ); chunk.read( readKey( iter, end ), iter, end ); chunk.read( readKey( iter, end ), iter, end ); + chunk.read( readKey( iter, end ), iter, end ); THEN( "a Metadata can be constructed and members can " "be tested" ) { @@ -112,6 +115,8 @@ std::string chunk() { return "zaid\n" " 92235.711nm\n" + "info\n" + " this is some information for the table\n" "library_name\n" " mendf71x\n" "date_source\n" @@ -147,6 +152,7 @@ std::string chunk() { void verifyChunk( const Metadata& chunk ) { CHECK( "92235.711nm" == chunk.zaid() ); + CHECK( "this is some information for the table" == chunk.information() ); CHECK( "mendf71x" == chunk.libraryName() ); CHECK( "12/22/2011" == chunk.sourceDate() ); CHECK( "08/07/2013" == chunk.processDate() ); diff --git a/src/NDItk/toFile.hpp b/src/NDItk/toFile.hpp new file mode 100644 index 0000000..bf9cf15 --- /dev/null +++ b/src/NDItk/toFile.hpp @@ -0,0 +1,35 @@ +#ifndef NJOY_NDITK_TOFILE +#define NJOY_NDITK_TOFILE + +// system includes +#include +#include + +// other includes +#include "tools/Log.hpp" + +namespace njoy { +namespace NDItk { + + /** + * @brief Function to write an NDI table to a file + * + * @param[in] filename the file name + */ + template < typename Table > + void toFile( const Table& table, const std::string& filename ) { + + std::string content; + auto output = std::back_inserter( content ); + table.print( output ); + + std::ofstream out( filename ); + out << "ndi: multigroup version=2.0" << std::endl; + out << content; + out.close(); + } + +} // NDItk namespace +} // njoy namespace + +#endif