From debc085cf692cb4f2a8a9585d0acfe4a7b06bfb9 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 22 Jun 2022 22:50:07 +0200 Subject: [PATCH 001/265] add placeholder skeleton for MCPL-input --- include/openmc/source.h | 19 +++++++++++++++++++ src/source.cpp | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/openmc/source.h b/include/openmc/source.h index 18fddc6899..cadbfe93bb 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -100,6 +100,25 @@ private: vector sites_; //!< Source sites from a file }; +//============================================================================== +// MCPL-file input source +//============================================================================== +class MCPLFileSource : public Source { +public: + // Constructors, destructors + MCPLFileSource(std::string path); + ~MCPLFileSource(); + + // Defer implementation to custom source library + SourceSite sample(uint64_t* seed) const override + +private: + vector sites_; //! Date: Thu, 23 Jun 2022 11:34:53 +0200 Subject: [PATCH 002/265] include mcpl header --- src/source.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index cd8b5e5743..5e26fa0a8d 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -31,6 +31,10 @@ #include "openmc/state_point.h" #include "openmc/xml_interface.h" +#ifdef HAS_MCPL +#include +#endif + namespace openmc { //============================================================================== From 5d74af4bf766ab732d320081be47b9c4f78303fe Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 23 Jun 2022 11:35:46 +0200 Subject: [PATCH 003/265] declarations of MCPL-internals --- include/openmc/source.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index cadbfe93bb..69dd47eae9 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -109,11 +109,20 @@ public: MCPLFileSource(std::string path); ~MCPLFileSource(); - // Defer implementation to custom source library - SourceSite sample(uint64_t* seed) const override + // Properties + ParticleType particle_type() const { return particle_; } + + //! Sample from the external source distribution + //! \param[inout] seed Pseudorandom seed pointer + //! \return Site read from MCPL-file + SourceSite sample(uint64_t* seed) const override; private: + ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted vector sites_; //! Date: Thu, 23 Jun 2022 11:37:05 +0200 Subject: [PATCH 004/265] internals of sample and constructor --- src/source.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 5e26fa0a8d..231f355fc2 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -337,7 +337,7 @@ CustomSourceWrapper::~CustomSourceWrapper() } //=========================================================================== -// Read an MCPL-file +// Read particles from an MCPL-file //=========================================================================== MCPLFileSource::MCPLFileSource(std::string path) { @@ -348,12 +348,55 @@ MCPLFileSource::MCPLFileSource(std::string path) // Read the source from a binary file instead of sampling from some // assumed source distribution - write_message(6, "Reading source file from {}...", path); + write_message(6, "Reading mcpl source file from {}",path); // Open the mcpl file - id_t file_id = mcpl_open(path.c_str) - + mcpl_file = mcpl_open(path.c_str); + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + uint64_t nparticles=mcpl_hdr_nparticles(mcpl_file); + +} + +MCPLFileSource::~MCPLFilsSource(){ + mcpl_close_file(mcpl_file); +} + +SourceSite MCPLFileSource::sample(uint64_t *seed){ + SourceSite omc_particle; + mcpl_particle *mcpl_particle; + //extract particle from mcpl-file + mcpl_particle=mcpl_reazd(mcplfile); + // check if it is a neutron or a photon. otherwise skip + while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) + { + mcpl_particle(mcpl_read(mcplfile); + //check for file exhaustion + } + + if(mcpl_particle->pdgcode=2112) { + omc_particle_=ParticleType::neutron; + } else { + omc_particle_=Particletype::photon; + } + + //particle is good, convert to openmc-formalism + omc_particle.r.x=mcpl_particle.x; + omc_particle.r.y=mcpl_particle.y; + omc_particle.r.z=mcpl_particle.z; + + omc_particle.u.x=mcpl_particle->direction[0]; + omc_particle.u.y=mcpl_particle->direction[1]; + omc_particle.u.z=mcpl_particle->direction[2]; + + //mcpl stores particles in MeV + omc_particle.E=mcpl_particle->ekin*1e6; + + omc_particle.t=mcpl_particle->time*1e-3; + omc_particle.wgt=mcpl_particle->weight; + omc_particle.delayed_group=0; + return omc_particle; } From 909b96f31e90a8a8ce40e890b3f4e73ee516466b Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:25:35 +0200 Subject: [PATCH 005/265] (wip): skeleton for input only test --- .../regression_tests/source_mcpl_file/test.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/test.py diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py new file mode 100644 index 0000000000..5a6a87d878 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -0,0 +1,52 @@ +import pathlib as pl +import os +import shutil +import subprocess +import textwrap + +import openmc +import pytest + +from tests.testing_harness import TestHarness + +def model(): + subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) + subprocess.run(['./gen_dummy_mcpl.out']) + +class SourceMCPLFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + + def _test_output_created(self): + """Check that the output files were created""" + stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ + 'exist.' + assert statepoint[0].endswith('h5'), \ + 'statepoint file does not end with h5.' + +def test_mcpl_source_file(): + harness = SourceMCPLFileTestHarness('statepoint.10.h5') + harness.main() + From 3c30dbba5b643e8464eb9b45a8667e08bf6ad44b Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:26:01 +0200 Subject: [PATCH 006/265] c-prog to generate a dummy mcpl --- .../source_mcpl_file/gen_dummy_mcpl.c | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c new file mode 100644 index 0000000000..7b5a933f18 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +const int batches=10; +const int particles=10000; + +double rand01(){ + double r=rand()/((double) RAND_MAX); + return r; +} + + +int main(int argc, char **argv){ + char outfilename[128]; + snprintf(outfilename,127,"source.%d.mcpl",batches); + + /*generate an mcpl_header_of sorts*/ + mcpl_outfile_t outfile=mcpl_create_outfile(outfilename); + mcpl_particle_t *particle, Particle; + + char line[256]; + snprintf(line,255,"gen_dummy_mcpl.c"); + mcpl_hdr_set_srcname(outfile,line); + mcpl_enable_universal_pdgcode(outfile,2112);/*all particles are neutrons*/ + snprintf(line,255,"Dummy MCPL-file for testing OpenMC mcpl input"); + mcpl_hdr_add_comment(outfile,line); + + /*also add the instrument file and the command line as blobs*/ + FILE *fp; + if( (fp=fopen("gen_dummy_mcpl.c","rb"))!=NULL){ + unsigned char *buffer; + int size,status; + /*find the file size by seeking to end, "tell" the position, and then go back again*/ + fseek(fp, 0L, SEEK_END); + size = ftell(fp); // get current file pointer + fseek(fp, 0L, SEEK_SET); // seek back to beginning of file + if ( size && (buffer=malloc(size))!=NULL){ + if (size!=(fread(buffer,1,size,fp))){ + fprintf(stderr,"Warning: c source generator file not read cleanly\n"); + } + mcpl_hdr_add_data(outfile, "mcpl_point_source_file_generator", size, buffer); + free(buffer); + } + fclose(fp); + } else { + fprintf(stderr,"Warning: could not open c source generator file, hence not embedded.\n"); + } + + + /*the main particle loop*/ + particle=&Particle; + int i; + for (i=0;iposition[0]=0; + particle->position[1]=0; + particle->position[2]=0; + + /*generate a random direction on unit sphere*/ + double nrm=2.0; + double vx,vy,vz; + int iter=0; + do { + if(iter>100){ + printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + } + vx=rand01()*2.0-1.0; + vy=rand01()*2.0-1.0; + vz=rand01()*2.0-1.0; + nrm=(vx*vx + vy*vy + vz*vz); + iter++; + } while (nrm>1.0); + vx/=sqrt(nrm); + vy/=sqrt(nrm); + vz/=sqrt(nrm); + particle->direction[0]=vx; + particle->direction[1]=vy; + particle->direction[2]=vz; + + /*generate the kinetic energy (in MeV) of the particle*/ + particle->ekin=1e-3; + + /*set time=0*/ + particle->time=0; + /*set weight*/ + particle->weight=1; + + particle->userflags=0; + mcpl_add_particle(outfile,particle); + } + mcpl_closeandgzip_outfile(outfile); +} From bc797c4df83b906bebe454cd1c9d7a17bd52c49c Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:30:07 +0200 Subject: [PATCH 007/265] don't zip the output-file --- .../source_mcpl_file/gen_dummy_mcpl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c index 7b5a933f18..f35a07fc44 100644 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -54,20 +54,20 @@ int main(int argc, char **argv){ int i; for (i=0;iposition[0]=0; particle->position[1]=0; particle->position[2]=0; - + /*generate a random direction on unit sphere*/ double nrm=2.0; double vx,vy,vz; int iter=0; do { if(iter>100){ - printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + printf("Warning: Exceeded max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); } vx=rand01()*2.0-1.0; vy=rand01()*2.0-1.0; @@ -82,9 +82,9 @@ int main(int argc, char **argv){ particle->direction[1]=vy; particle->direction[2]=vz; - /*generate the kinetic energy (in MeV) of the particle*/ + /*generate the kinetic energy (in MeV) of the particle*/ particle->ekin=1e-3; - + /*set time=0*/ particle->time=0; /*set weight*/ @@ -93,5 +93,5 @@ int main(int argc, char **argv){ particle->userflags=0; mcpl_add_particle(outfile,particle); } - mcpl_closeandgzip_outfile(outfile); + mcpl_close_outfile(outfile); } From 80e69b2e0856d5af025087283964fd5a03e20339 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:53:14 +0200 Subject: [PATCH 008/265] now compiles fine if mcpl is indeed installed --- .../regression_tests/source_mcpl_file/test.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 5a6a87d878..0adf67f1aa 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -3,16 +3,13 @@ import os import shutil import subprocess import textwrap - +import glob import openmc import pytest from tests.testing_harness import TestHarness -def model(): - subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) - subprocess.run(['./gen_dummy_mcpl.out']) - + class SourceMCPLFileTestHarness(TestHarness): def execute_test(self): """Run OpenMC with the appropriate arguments and check the outputs.""" @@ -26,6 +23,10 @@ class SourceMCPLFileTestHarness(TestHarness): finally: self._cleanup() + def _create_input(self): + subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + subprocess.run(['./gen_dummy_mcpl.out']) + def update_results(self): """Update the results_true using the current version of OpenMC.""" try: @@ -40,13 +41,19 @@ class SourceMCPLFileTestHarness(TestHarness): def _test_output_created(self): """Check that the output files were created""" - stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ 'exist.' assert statepoint[0].endswith('h5'), \ 'statepoint file does not end with h5.' + def _cleanup(self): + super()._cleanup() + source_mcpl=glob.glob(os.path.join(os.getcwd(),'source*.mcpl')) + for f in source_mcpl: + if (os.path.exists(f)): + os.remove(f) + def test_mcpl_source_file(): harness = SourceMCPLFileTestHarness('statepoint.10.h5') harness.main() - From 6d352aa55c8fab8bf427bceeb165c6b870d94e53 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 22 Jun 2022 22:50:07 +0200 Subject: [PATCH 009/265] add placeholder skeleton for MCPL-input --- include/openmc/source.h | 19 +++++++++++++++++++ src/source.cpp | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/openmc/source.h b/include/openmc/source.h index 18fddc6899..cadbfe93bb 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -100,6 +100,25 @@ private: vector sites_; //!< Source sites from a file }; +//============================================================================== +// MCPL-file input source +//============================================================================== +class MCPLFileSource : public Source { +public: + // Constructors, destructors + MCPLFileSource(std::string path); + ~MCPLFileSource(); + + // Defer implementation to custom source library + SourceSite sample(uint64_t* seed) const override + +private: + vector sites_; //! Date: Thu, 23 Jun 2022 11:34:53 +0200 Subject: [PATCH 010/265] include mcpl header --- src/source.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index cd8b5e5743..5e26fa0a8d 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -31,6 +31,10 @@ #include "openmc/state_point.h" #include "openmc/xml_interface.h" +#ifdef HAS_MCPL +#include +#endif + namespace openmc { //============================================================================== From c43989af9c222b20413ee3b892feaf20919b8707 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 23 Jun 2022 11:35:46 +0200 Subject: [PATCH 011/265] declarations of MCPL-internals --- include/openmc/source.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index cadbfe93bb..69dd47eae9 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -109,11 +109,20 @@ public: MCPLFileSource(std::string path); ~MCPLFileSource(); - // Defer implementation to custom source library - SourceSite sample(uint64_t* seed) const override + // Properties + ParticleType particle_type() const { return particle_; } + + //! Sample from the external source distribution + //! \param[inout] seed Pseudorandom seed pointer + //! \return Site read from MCPL-file + SourceSite sample(uint64_t* seed) const override; private: + ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted vector sites_; //! Date: Thu, 23 Jun 2022 11:37:05 +0200 Subject: [PATCH 012/265] internals of sample and constructor --- src/source.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 5e26fa0a8d..231f355fc2 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -337,7 +337,7 @@ CustomSourceWrapper::~CustomSourceWrapper() } //=========================================================================== -// Read an MCPL-file +// Read particles from an MCPL-file //=========================================================================== MCPLFileSource::MCPLFileSource(std::string path) { @@ -348,12 +348,55 @@ MCPLFileSource::MCPLFileSource(std::string path) // Read the source from a binary file instead of sampling from some // assumed source distribution - write_message(6, "Reading source file from {}...", path); + write_message(6, "Reading mcpl source file from {}",path); // Open the mcpl file - id_t file_id = mcpl_open(path.c_str) - + mcpl_file = mcpl_open(path.c_str); + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + uint64_t nparticles=mcpl_hdr_nparticles(mcpl_file); + +} + +MCPLFileSource::~MCPLFilsSource(){ + mcpl_close_file(mcpl_file); +} + +SourceSite MCPLFileSource::sample(uint64_t *seed){ + SourceSite omc_particle; + mcpl_particle *mcpl_particle; + //extract particle from mcpl-file + mcpl_particle=mcpl_reazd(mcplfile); + // check if it is a neutron or a photon. otherwise skip + while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) + { + mcpl_particle(mcpl_read(mcplfile); + //check for file exhaustion + } + + if(mcpl_particle->pdgcode=2112) { + omc_particle_=ParticleType::neutron; + } else { + omc_particle_=Particletype::photon; + } + + //particle is good, convert to openmc-formalism + omc_particle.r.x=mcpl_particle.x; + omc_particle.r.y=mcpl_particle.y; + omc_particle.r.z=mcpl_particle.z; + + omc_particle.u.x=mcpl_particle->direction[0]; + omc_particle.u.y=mcpl_particle->direction[1]; + omc_particle.u.z=mcpl_particle->direction[2]; + + //mcpl stores particles in MeV + omc_particle.E=mcpl_particle->ekin*1e6; + + omc_particle.t=mcpl_particle->time*1e-3; + omc_particle.wgt=mcpl_particle->weight; + omc_particle.delayed_group=0; + return omc_particle; } From 103c6ffa0330ab1313489a27b2d9504cfb30f7ba Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:25:35 +0200 Subject: [PATCH 013/265] (wip): skeleton for input only test --- .../regression_tests/source_mcpl_file/test.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/test.py diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py new file mode 100644 index 0000000000..5a6a87d878 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -0,0 +1,52 @@ +import pathlib as pl +import os +import shutil +import subprocess +import textwrap + +import openmc +import pytest + +from tests.testing_harness import TestHarness + +def model(): + subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) + subprocess.run(['./gen_dummy_mcpl.out']) + +class SourceMCPLFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + + def _test_output_created(self): + """Check that the output files were created""" + stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ + 'exist.' + assert statepoint[0].endswith('h5'), \ + 'statepoint file does not end with h5.' + +def test_mcpl_source_file(): + harness = SourceMCPLFileTestHarness('statepoint.10.h5') + harness.main() + From 73fb335350bb3b6ed0199e9c3cc4ee19d73b163e Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:26:01 +0200 Subject: [PATCH 014/265] c-prog to generate a dummy mcpl --- .../source_mcpl_file/gen_dummy_mcpl.c | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c new file mode 100644 index 0000000000..7b5a933f18 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +const int batches=10; +const int particles=10000; + +double rand01(){ + double r=rand()/((double) RAND_MAX); + return r; +} + + +int main(int argc, char **argv){ + char outfilename[128]; + snprintf(outfilename,127,"source.%d.mcpl",batches); + + /*generate an mcpl_header_of sorts*/ + mcpl_outfile_t outfile=mcpl_create_outfile(outfilename); + mcpl_particle_t *particle, Particle; + + char line[256]; + snprintf(line,255,"gen_dummy_mcpl.c"); + mcpl_hdr_set_srcname(outfile,line); + mcpl_enable_universal_pdgcode(outfile,2112);/*all particles are neutrons*/ + snprintf(line,255,"Dummy MCPL-file for testing OpenMC mcpl input"); + mcpl_hdr_add_comment(outfile,line); + + /*also add the instrument file and the command line as blobs*/ + FILE *fp; + if( (fp=fopen("gen_dummy_mcpl.c","rb"))!=NULL){ + unsigned char *buffer; + int size,status; + /*find the file size by seeking to end, "tell" the position, and then go back again*/ + fseek(fp, 0L, SEEK_END); + size = ftell(fp); // get current file pointer + fseek(fp, 0L, SEEK_SET); // seek back to beginning of file + if ( size && (buffer=malloc(size))!=NULL){ + if (size!=(fread(buffer,1,size,fp))){ + fprintf(stderr,"Warning: c source generator file not read cleanly\n"); + } + mcpl_hdr_add_data(outfile, "mcpl_point_source_file_generator", size, buffer); + free(buffer); + } + fclose(fp); + } else { + fprintf(stderr,"Warning: could not open c source generator file, hence not embedded.\n"); + } + + + /*the main particle loop*/ + particle=&Particle; + int i; + for (i=0;iposition[0]=0; + particle->position[1]=0; + particle->position[2]=0; + + /*generate a random direction on unit sphere*/ + double nrm=2.0; + double vx,vy,vz; + int iter=0; + do { + if(iter>100){ + printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + } + vx=rand01()*2.0-1.0; + vy=rand01()*2.0-1.0; + vz=rand01()*2.0-1.0; + nrm=(vx*vx + vy*vy + vz*vz); + iter++; + } while (nrm>1.0); + vx/=sqrt(nrm); + vy/=sqrt(nrm); + vz/=sqrt(nrm); + particle->direction[0]=vx; + particle->direction[1]=vy; + particle->direction[2]=vz; + + /*generate the kinetic energy (in MeV) of the particle*/ + particle->ekin=1e-3; + + /*set time=0*/ + particle->time=0; + /*set weight*/ + particle->weight=1; + + particle->userflags=0; + mcpl_add_particle(outfile,particle); + } + mcpl_closeandgzip_outfile(outfile); +} From c6c876d835fe2177426ee64f4ae8dc15d5e616f2 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:30:07 +0200 Subject: [PATCH 015/265] don't zip the output-file --- .../source_mcpl_file/gen_dummy_mcpl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c index 7b5a933f18..f35a07fc44 100644 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -54,20 +54,20 @@ int main(int argc, char **argv){ int i; for (i=0;iposition[0]=0; particle->position[1]=0; particle->position[2]=0; - + /*generate a random direction on unit sphere*/ double nrm=2.0; double vx,vy,vz; int iter=0; do { if(iter>100){ - printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + printf("Warning: Exceeded max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); } vx=rand01()*2.0-1.0; vy=rand01()*2.0-1.0; @@ -82,9 +82,9 @@ int main(int argc, char **argv){ particle->direction[1]=vy; particle->direction[2]=vz; - /*generate the kinetic energy (in MeV) of the particle*/ + /*generate the kinetic energy (in MeV) of the particle*/ particle->ekin=1e-3; - + /*set time=0*/ particle->time=0; /*set weight*/ @@ -93,5 +93,5 @@ int main(int argc, char **argv){ particle->userflags=0; mcpl_add_particle(outfile,particle); } - mcpl_closeandgzip_outfile(outfile); + mcpl_close_outfile(outfile); } From 8719a539f9f8c9134ded079b2eec64c6cdf3aa82 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:53:14 +0200 Subject: [PATCH 016/265] now compiles fine if mcpl is indeed installed --- .../regression_tests/source_mcpl_file/test.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 5a6a87d878..0adf67f1aa 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -3,16 +3,13 @@ import os import shutil import subprocess import textwrap - +import glob import openmc import pytest from tests.testing_harness import TestHarness -def model(): - subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) - subprocess.run(['./gen_dummy_mcpl.out']) - + class SourceMCPLFileTestHarness(TestHarness): def execute_test(self): """Run OpenMC with the appropriate arguments and check the outputs.""" @@ -26,6 +23,10 @@ class SourceMCPLFileTestHarness(TestHarness): finally: self._cleanup() + def _create_input(self): + subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + subprocess.run(['./gen_dummy_mcpl.out']) + def update_results(self): """Update the results_true using the current version of OpenMC.""" try: @@ -40,13 +41,19 @@ class SourceMCPLFileTestHarness(TestHarness): def _test_output_created(self): """Check that the output files were created""" - stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ 'exist.' assert statepoint[0].endswith('h5'), \ 'statepoint file does not end with h5.' + def _cleanup(self): + super()._cleanup() + source_mcpl=glob.glob(os.path.join(os.getcwd(),'source*.mcpl')) + for f in source_mcpl: + if (os.path.exists(f)): + os.remove(f) + def test_mcpl_source_file(): harness = SourceMCPLFileTestHarness('statepoint.10.h5') harness.main() - From 0d1c346b5a12d2a452450b435a80cf4a2fbbe608 Mon Sep 17 00:00:00 2001 From: erkn Date: Sat, 9 Jul 2022 16:31:00 +0200 Subject: [PATCH 017/265] fix misprints --- src/source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 231f355fc2..37e580b18d 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -345,7 +345,7 @@ MCPLFileSource::MCPLFileSource(std::string path) if (!file_exists(path)) { fatal_error(fmt::format("Source file '{}' does not exist.", path)); } - + // Read the source from a binary file instead of sampling from some // assumed source distribution write_message(6, "Reading mcpl source file from {}",path); @@ -367,7 +367,7 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ SourceSite omc_particle; mcpl_particle *mcpl_particle; //extract particle from mcpl-file - mcpl_particle=mcpl_reazd(mcplfile); + mcpl_particle=mcpl_read(mcplfile); // check if it is a neutron or a photon. otherwise skip while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { @@ -375,9 +375,9 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ //check for file exhaustion } - if(mcpl_particle->pdgcode=2112) { + if(mcpl_particle->pdgcode==2112) { omc_particle_=ParticleType::neutron; - } else { + } else if (mcpl_particle->pdgcode==22){ omc_particle_=Particletype::photon; } From 7a6c69fbd5daefcca4b612affdbac6ecd46e9d32 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Mon, 11 Jul 2022 09:49:29 +0200 Subject: [PATCH 018/265] fix misprints --- src/source.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 231f355fc2..9ec1ff798c 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -367,7 +367,7 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ SourceSite omc_particle; mcpl_particle *mcpl_particle; //extract particle from mcpl-file - mcpl_particle=mcpl_reazd(mcplfile); + mcpl_particle=mcpl_read(mcplfile); // check if it is a neutron or a photon. otherwise skip while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { @@ -375,9 +375,9 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ //check for file exhaustion } - if(mcpl_particle->pdgcode=2112) { + if(mcpl_particle->pdgcode==2112) { omc_particle_=ParticleType::neutron; - } else { + } else if (mcpl_particle->pdgcode==22){ omc_particle_=Particletype::photon; } @@ -390,9 +390,9 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ omc_particle.u.y=mcpl_particle->direction[1]; omc_particle.u.z=mcpl_particle->direction[2]; - //mcpl stores particles in MeV + //mcpl stores kinetic energy in MeV omc_particle.E=mcpl_particle->ekin*1e6; - + //mcpl stores time in ms omc_particle.t=mcpl_particle->time*1e-3; omc_particle.wgt=mcpl_particle->weight; omc_particle.delayed_group=0; From 34746926d349d0e04134893aa987c49d9508e121 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:48:55 +0200 Subject: [PATCH 019/265] add necessary xmls --- tests/regression_tests/source_mcpl_file/geometry.xml | 8 ++++++++ tests/regression_tests/source_mcpl_file/materials.xml | 9 +++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/geometry.xml create mode 100644 tests/regression_tests/source_mcpl_file/materials.xml diff --git a/tests/regression_tests/source_mcpl_file/geometry.xml b/tests/regression_tests/source_mcpl_file/geometry.xml new file mode 100644 index 0000000000..bc56030e18 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/regression_tests/source_mcpl_file/materials.xml b/tests/regression_tests/source_mcpl_file/materials.xml new file mode 100644 index 0000000000..2472a74717 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + From 25cfc328d04c930a8d75ba41eb46ed7e26c64cae Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:49:56 +0200 Subject: [PATCH 020/265] set seed to make test deteministic --- tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c index f35a07fc44..61eea95c6f 100644 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -52,6 +52,7 @@ int main(int argc, char **argv){ /*the main particle loop*/ particle=&Particle; int i; + srand(1234); for (i=0;i Date: Tue, 12 Jul 2022 13:50:35 +0200 Subject: [PATCH 021/265] test target result --- tests/regression_tests/source_mcpl_file/results_true.dat | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/results_true.dat diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat new file mode 100644 index 0000000000..f2209006a1 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +3.017557E-01 3.398770E-03 From b0bb63cb3508d0dff59fd950a60dc35db2188b19 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:50:53 +0200 Subject: [PATCH 022/265] yet another necessary file --- tests/regression_tests/source_mcpl_file/settings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/settings.xml diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml new file mode 100644 index 0000000000..8b1510e0ce --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -0,0 +1,11 @@ + + + + 10 + 5 + 1000 + + + source.10.mcpl + + From e480b5c851c542e53ff6e83236994f23c2bfdba3 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 14:40:01 +0200 Subject: [PATCH 023/265] add a check that the test input got created as intended --- tests/regression_tests/source_mcpl_file/test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 0adf67f1aa..922ddddd31 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -15,6 +15,7 @@ class SourceMCPLFileTestHarness(TestHarness): """Run OpenMC with the appropriate arguments and check the outputs.""" try: self._create_input() + self._test_input_created() self._run_openmc() self._test_output_created() results = self._get_results() @@ -31,6 +32,7 @@ class SourceMCPLFileTestHarness(TestHarness): """Update the results_true using the current version of OpenMC.""" try: self._create_input() + self._test_input_created() self._run_openmc() self._test_output_created() results = self._get_results() @@ -39,6 +41,14 @@ class SourceMCPLFileTestHarness(TestHarness): finally: self._cleanup() + def _test_input_created(self): + """Check that the input mcpl.file was generated as it should""" + mcplfile=glob.glob(os.path.join(os.getcwd(),'source.10.mcpl')) + assert len(mcplfile) == 1, 'Either multiple or no mcpl files ' \ + 'exist.' + assert mcplfile[0].endswith('mcpl'), \ + 'output file does not end with mcpl.' + def _test_output_created(self): """Check that the output files were created""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) @@ -55,5 +65,5 @@ class SourceMCPLFileTestHarness(TestHarness): os.remove(f) def test_mcpl_source_file(): - harness = SourceMCPLFileTestHarness('statepoint.10.h5') + harness = SourceMCPLFileTestHarness('source.10.mcpl') harness.main() From 8c9bb761e5d3838a33822f7fa283acab106cc736 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:25:55 +0200 Subject: [PATCH 024/265] fix bugs and finalize interface to MCPL --- include/openmc/source.h | 22 +++++++------ src/source.cpp | 73 +++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 69dd47eae9..4d7ec104aa 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -12,6 +12,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { //============================================================================== @@ -100,6 +104,7 @@ private: vector sites_; //!< Source sites from a file }; +#ifdef OPENMC_MCPL //============================================================================== // MCPL-file input source //============================================================================== @@ -109,24 +114,21 @@ public: MCPLFileSource(std::string path); ~MCPLFileSource(); - // Properties - ParticleType particle_type() const { return particle_; } - + // Methods //! Sample from the external source distribution //! \param[inout] seed Pseudorandom seed pointer //! \return Site read from MCPL-file - SourceSite sample(uint64_t* seed) const override; + SourceSite sample(uint64_t* seed) const; private: - ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted + SourceSite read_single_particle() const; + void read_source_bank(vector &sites_); + vector sites_; //! #endif @@ -336,6 +336,7 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } +#ifdef OPENMC_MCPL //=========================================================================== // Read particles from an MCPL-file //=========================================================================== @@ -351,54 +352,70 @@ MCPLFileSource::MCPLFileSource(std::string path) write_message(6, "Reading mcpl source file from {}",path); // Open the mcpl file - mcpl_file = mcpl_open(path.c_str); + mcpl_file = mcpl_open_file(path.c_str()); //do checks on the mcpl_file to see if particles are many enough. // should model this on the example source shown in the docs. - uint64_t nparticles=mcpl_hdr_nparticles(mcpl_file); + n_sites=mcpl_hdr_nparticles(mcpl_file); + read_source_bank(sites_); } -MCPLFileSource::~MCPLFilsSource(){ +MCPLFileSource::~MCPLFileSource(){ mcpl_close_file(mcpl_file); } -SourceSite MCPLFileSource::sample(uint64_t *seed){ - SourceSite omc_particle; - mcpl_particle *mcpl_particle; +SourceSite MCPLFileSource::sample(uint64_t* seed) const +{ + size_t i_site = sites_.size() * prn(seed); + return sites_[i_site]; +} + +void MCPLFileSource::read_source_bank(vector &sites_) +{ + sites_.resize(n_sites); + for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) - { - mcpl_particle(mcpl_read(mcplfile); - //check for file exhaustion + mcpl_particle=mcpl_read(mcpl_file); + // check if it is a neutron or a photon. otherwise skip + while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + mcpl_particle=mcpl_read(mcpl_file); + //should check for file exhaustion This could happen if particles are other than + //neutrons or photons } if(mcpl_particle->pdgcode==2112) { - omc_particle_=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22){ - omc_particle_=Particletype::photon; + omc_particle_.particle=ParticleType::neutron; + } else if (mcpl_particle->pdgcode==22) { + omc_particle_.particle=ParticleType::photon; } //particle is good, convert to openmc-formalism - omc_particle.r.x=mcpl_particle.x; - omc_particle.r.y=mcpl_particle.y; - omc_particle.r.z=mcpl_particle.z; + omc_particle_.r.x=mcpl_particle->position[0]; + omc_particle_.r.y=mcpl_particle->position[1]; + omc_particle_.r.z=mcpl_particle->position[2]; - omc_particle.u.x=mcpl_particle->direction[0]; - omc_particle.u.y=mcpl_particle->direction[1]; - omc_particle.u.z=mcpl_particle->direction[2]; + omc_particle_.u.x=mcpl_particle->direction[0]; + omc_particle_.u.y=mcpl_particle->direction[1]; + omc_particle_.u.z=mcpl_particle->direction[2]; //mcpl stores kinetic energy in MeV - omc_particle.E=mcpl_particle->ekin*1e6; + omc_particle_.E=mcpl_particle->ekin*1e6; //mcpl stores time in ms - omc_particle.t=mcpl_particle->time*1e-3; - omc_particle.wgt=mcpl_particle->weight; - omc_particle.delayed_group=0; - return omc_particle; + omc_particle_.time=mcpl_particle->time*1e-3; + omc_particle_.wgt=mcpl_particle->weight; + omc_particle_.delayed_group=0; + return omc_particle_; } - +#endif //OPENMC_MCPL //============================================================================== // Non-member functions From 915d69fb3d217cf705158dba680b8f02a99208c7 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:26:24 +0200 Subject: [PATCH 025/265] add an option to turn on mcpl-support --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85dcd7084d..6af9643cdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) +option(OPENMC_USE_MCPL "Enable MPCPL" OFF) #=============================================================================== # Set a default build configuration if not explicitly specified @@ -154,6 +155,10 @@ if(OPENMC_ENABLE_COVERAGE) list(APPEND ldflags --coverage) endif() +if(OPENMC_USE_MCPL) + list(APPEND ldflags -lmcpl) +endif() + # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") @@ -408,6 +413,10 @@ endif() if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() +if(OPENMC_USE_MCPL) + target_compile_definitions(libopenmc PUBLIC -DOPENMC_MCPL) +endif() + # Set git SHA1 hash as a compile definition if(GIT_FOUND) From b140eeb7cb4c7a1ee76172cd21c32c2e9951a24b Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:27:18 +0200 Subject: [PATCH 026/265] add a source type to the settings interface --- src/settings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index eb0d4936c7..12e0a80509 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -430,6 +430,9 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); model::external_sources.push_back(make_unique(path)); + } else if (check_for_node(node, "mcpl")) { + auto path = get_node_value(node, "mcpl", false, true); + model::external_sources.push_back(make_unique(path)); } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); From cd163ef0b91fb4acc30630528b0728d8bab3098e Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 12:13:10 +0200 Subject: [PATCH 027/265] only add mcpl-support if OPENMC_MCPL is defined --- src/settings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index 12e0a80509..d419cab06f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -430,9 +430,11 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); model::external_sources.push_back(make_unique(path)); +#ifdef OPENMC_MCPL } else if (check_for_node(node, "mcpl")) { auto path = get_node_value(node, "mcpl", false, true); model::external_sources.push_back(make_unique(path)); +#endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); From a79233b3ec85f59647fb800da7db0973ce9365e8 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 14:13:33 +0200 Subject: [PATCH 028/265] needed to avoid name clashes with other tests --- tests/regression_tests/source_mcpl_file/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/regression_tests/source_mcpl_file/__init__.py diff --git a/tests/regression_tests/source_mcpl_file/__init__.py b/tests/regression_tests/source_mcpl_file/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From eff690fd90032abd0d4a5e68f5babcc38c476de1 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 20:55:53 +0200 Subject: [PATCH 029/265] fail without tripping the entire test-process --- tests/regression_tests/source_mcpl_file/test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 922ddddd31..f55a769047 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -1,11 +1,6 @@ -import pathlib as pl import os -import shutil import subprocess -import textwrap import glob -import openmc -import pytest from tests.testing_harness import TestHarness @@ -25,7 +20,8 @@ class SourceMCPLFileTestHarness(TestHarness): self._cleanup() def _create_input(self): - subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + compiled=subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + assert compiled==0, 'Could not compile mcpl-file generator code' subprocess.run(['./gen_dummy_mcpl.out']) def update_results(self): From 1ba696741412b17ac676cc2a0b4a78c606906d35 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 15 Jul 2022 21:35:59 +0200 Subject: [PATCH 030/265] fix typo Co-authored-by: Paul Romano --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6af9643cdb..be56f1c12f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) -option(OPENMC_USE_MCPL "Enable MPCPL" OFF) +option(OPENMC_USE_MCPL "Enable MCPL" OFF) #=============================================================================== # Set a default build configuration if not explicitly specified From 380cfc150b449ef56debb5261caa010a52c42bdd Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 9 Aug 2022 08:12:19 +0200 Subject: [PATCH 031/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- include/openmc/source.h | 30 +--------- src/settings.cpp | 2 +- src/source.cpp | 128 +++++++++++++++------------------------- 3 files changed, 52 insertions(+), 108 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 4d7ec104aa..5c2dae9311 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -96,7 +96,9 @@ class FileSource : public Source { public: // Constructors explicit FileSource(std::string path); - +#ifdef OPENMC_MCPL + explicit FileSource(mcpl_file_t mcpl_file); +#endif // Methods SourceSite sample(uint64_t* seed) const override; @@ -104,32 +106,6 @@ private: vector sites_; //!< Source sites from a file }; -#ifdef OPENMC_MCPL -//============================================================================== -// MCPL-file input source -//============================================================================== -class MCPLFileSource : public Source { -public: - // Constructors, destructors - MCPLFileSource(std::string path); - ~MCPLFileSource(); - - // Methods - //! Sample from the external source distribution - //! \param[inout] seed Pseudorandom seed pointer - //! \return Site read from MCPL-file - SourceSite sample(uint64_t* seed) const; - -private: - SourceSite read_single_particle() const; - void read_source_bank(vector &sites_); - - vector sites_; //!(path)); + model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); #endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters diff --git a/src/source.cpp b/src/source.cpp index 82978f1030..49e51c4b16 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -277,6 +277,54 @@ FileSource::FileSource(std::string path) file_close(file_id); } +#ifdef OPENMC_MCPL +FileSource::FileSource(mcpl_file_t mcpl_file) +{ + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + size_t n_sites=mcpl_hdr_nparticles(mcpl_file); + + sites_.resize(n_sites); + for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + mcpl_particle=mcpl_read(mcpl_file); + //should check for file exhaustion This could happen if particles are other than + //neutrons or photons + } + + if(mcpl_particle->pdgcode==2112) { + site_.particle=ParticleType::neutron; + } else if (mcpl_particle->pdgcode==22) { + site_.particle=ParticleType::photon; + } + + //particle is good, convert to openmc-formalism + site_.r.x=mcpl_particle->position[0]; + site_.r.y=mcpl_particle->position[1]; + site_.r.z=mcpl_particle->position[2]; + + site_.u.x=mcpl_particle->direction[0]; + site_.u.y=mcpl_particle->direction[1]; + site_.u.z=mcpl_particle->direction[2]; + + //mcpl stores kinetic energy in MeV + site_.E=mcpl_particle->ekin*1e6; + //mcpl stores time in ms + site_.time=mcpl_particle->time*1e-3; + site_.wgt=mcpl_particle->weight; + site_.delayed_group=0; + sites_[i]=site_; + } + mcpl_close_file(mcpl_file); +} +#endif //OPENMC_MCPL + SourceSite FileSource::sample(uint64_t* seed) const { size_t i_site = sites_.size() * prn(seed); @@ -336,86 +384,6 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } -#ifdef OPENMC_MCPL -//=========================================================================== -// Read particles from an MCPL-file -//=========================================================================== -MCPLFileSource::MCPLFileSource(std::string path) -{ - // Check if source file exists - if (!file_exists(path)) { - fatal_error(fmt::format("Source file '{}' does not exist.", path)); - } - - // Read the source from a binary file instead of sampling from some - // assumed source distribution - write_message(6, "Reading mcpl source file from {}",path); - - // Open the mcpl file - mcpl_file = mcpl_open_file(path.c_str()); - - //do checks on the mcpl_file to see if particles are many enough. - // should model this on the example source shown in the docs. - n_sites=mcpl_hdr_nparticles(mcpl_file); - - read_source_bank(sites_); -} - -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { - mcpl_particle=mcpl_read(mcpl_file); - //should check for file exhaustion This could happen if particles are other than - //neutrons or photons - } - - if(mcpl_particle->pdgcode==2112) { - omc_particle_.particle=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22) { - omc_particle_.particle=ParticleType::photon; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL //============================================================================== // Non-member functions From aa571c2c33b421e2524e249ca00f9584e891dd0a Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 032/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- include/openmc/state_point.h | 9 +++ src/state_point.cpp | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..2d92c935a6 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,6 +9,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { void load_state_point(); @@ -21,5 +25,10 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); +#endif + } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/state_point.cpp b/src/state_point.cpp index 9ea0459153..babd670a2d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,6 +28,10 @@ #include "openmc/timer.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -598,6 +602,45 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +void write_mcpl_point(const char *filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + // this must be rewritten: file_open is h5-specific + file_id = mcpl_create_outfile(filename_.c_str()); + //write_attribute(file_id, "filetype", "source"); + //write header stuff (oopy in xml-files as binary blobs for instance)) + if (VERSION_DEV){ + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + } else { + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + } + mcpl_hdr_set_srcname(file_id,line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + //change this - this is h5 specific + mcpl_closeandgzip_outfile(file_id); + } + +} + + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -714,6 +757,88 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if(surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + //write particles from the master node + //loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { +#ifdef OPENMC_MPI + if (i>0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + //now write the source_banke data again. + for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + //particle is now at the iterator + //write it to the mcpl-file + mcpl_particle_t p; + p.position[0]=_site->r.x; + p.position[1]=_site->r.y; + p.position[2]=_site->r.z; + + p.direction[0]=_site->u.x; + p.direction[1]=_site->u.y; + p.direction[2]=_site->u.x; + + //mcpl stores kinetic energy in MeV + p.ekin=_site->E*1e-6; + + p.time=_site->time*1e3; + + p.weight=_site->wgt; + + switch(_site->particle){ + case ParticleType::neutron: + p.pdgcode=2112; + break; + case ParticleType::photon: + p.pdgcode=22; + break; + case ParticleType::electron: + p.pdgcode=11; + break; + case ParticleType::positron: + p.pdgcode=-11; + break; + } + + mcpl_add_particle(file_id,&p); + } + } + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } + +} + + // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From be09b442fbfc41d029ed0eccef87ea25477f40aa Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 09:11:41 +0200 Subject: [PATCH 033/265] allow electrons and positrons as well --- src/source.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 82978f1030..e7c944cb58 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -383,19 +383,30 @@ SourceSite MCPLFileSource::read_single_particle() const { SourceSite omc_particle_; const mcpl_particle_t *mcpl_particle; - //extract particle from mcpl-file + // extract particle from mcpl-file mcpl_particle=mcpl_read(mcpl_file); - // check if it is a neutron or a photon. otherwise skip - while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + // check if it is a neutron, photon, electron, or positron. Otherwise skip. + int pdg=mcpl_particle->pdgcode; + while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { mcpl_particle=mcpl_read(mcpl_file); - //should check for file exhaustion This could happen if particles are other than - //neutrons or photons + pdg=mcpl_particle->pdgcode; + //should check for file exhaustion. This could happen if particles are other than + //neutrons, photons, electrons, or positrons. } - if(mcpl_particle->pdgcode==2112) { - omc_particle_.particle=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22) { - omc_particle_.particle=ParticleType::photon; + switch(pdg){ + case 2112: + omc_particle_.particle=ParticleType::neutron; + break; + case 22: + omc_particle_.particle=ParticleType::photon; + break; + case 11: + omc_particle_.particle=ParticleType::electron; + break; + case -11: + omc_particle_.particle=ParticleType::positron; + break; } //particle is good, convert to openmc-formalism From 4e5ba4b4601209f389c394cd3747891e76b43f1c Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 09:12:01 +0200 Subject: [PATCH 034/265] expose a python function to check for mcpl --- openmc/lib/__init__.py | 3 +++ src/source.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c28..1e337fc073 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -48,6 +48,9 @@ def _coord_levels(): def _libmesh_enabled(): return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value +def _mcpl_enabled(): + return c_bool.in_dll(_dll, "MCPL_ENABLED").value + from .error import * from .core import * from .nuclide import * diff --git a/src/source.cpp b/src/source.cpp index e7c944cb58..9b426c3cfa 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -41,6 +41,12 @@ namespace openmc { // Global variables //============================================================================== +#ifdef OPENMC_MCPL +const bool MCPL_ENABLED = true; +#else +const bool MCPL_ENABLED = false; +#endif + namespace model { vector> external_sources; From 32e71395b3ad44ecc7080ba39d4b920adfb88702 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 035/265] add a trigger to simulation control --- src/simulation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/simulation.cpp b/src/simulation.cpp index b70535c330..05c8c04508 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -406,6 +406,13 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } + + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcplt_source_point(filename.c_str(), true); + } + + } void initialize_generation() From ab665fb4563be28b6cfd365634049c90f133f485 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 15 Jul 2022 21:35:59 +0200 Subject: [PATCH 036/265] fix typo Co-authored-by: Paul Romano --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6af9643cdb..be56f1c12f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) -option(OPENMC_USE_MCPL "Enable MPCPL" OFF) +option(OPENMC_USE_MCPL "Enable MCPL" OFF) #=============================================================================== # Set a default build configuration if not explicitly specified From 71568d4f4bffa21a2a9c26725619cd180a9a59ff Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 9 Aug 2022 08:12:19 +0200 Subject: [PATCH 037/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- include/openmc/source.h | 30 ++--------- src/settings.cpp | 2 +- src/source.cpp | 116 +++++++++++++++++----------------------- 3 files changed, 53 insertions(+), 95 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 4d7ec104aa..5c2dae9311 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -96,7 +96,9 @@ class FileSource : public Source { public: // Constructors explicit FileSource(std::string path); - +#ifdef OPENMC_MCPL + explicit FileSource(mcpl_file_t mcpl_file); +#endif // Methods SourceSite sample(uint64_t* seed) const override; @@ -104,32 +106,6 @@ private: vector sites_; //!< Source sites from a file }; -#ifdef OPENMC_MCPL -//============================================================================== -// MCPL-file input source -//============================================================================== -class MCPLFileSource : public Source { -public: - // Constructors, destructors - MCPLFileSource(std::string path); - ~MCPLFileSource(); - - // Methods - //! Sample from the external source distribution - //! \param[inout] seed Pseudorandom seed pointer - //! \return Site read from MCPL-file - SourceSite sample(uint64_t* seed) const; - -private: - SourceSite read_single_particle() const; - void read_source_bank(vector &sites_); - - vector sites_; //!(path)); + model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); #endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters diff --git a/src/source.cpp b/src/source.cpp index 9b426c3cfa..9cdf1632bf 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -283,6 +283,54 @@ FileSource::FileSource(std::string path) file_close(file_id); } +#ifdef OPENMC_MCPL +FileSource::FileSource(mcpl_file_t mcpl_file) +{ + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + size_t n_sites=mcpl_hdr_nparticles(mcpl_file); + + sites_.resize(n_sites); + for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + mcpl_particle=mcpl_read(mcpl_file); + //should check for file exhaustion This could happen if particles are other than + //neutrons or photons + } + + if(mcpl_particle->pdgcode==2112) { + site_.particle=ParticleType::neutron; + } else if (mcpl_particle->pdgcode==22) { + site_.particle=ParticleType::photon; + } + + //particle is good, convert to openmc-formalism + site_.r.x=mcpl_particle->position[0]; + site_.r.y=mcpl_particle->position[1]; + site_.r.z=mcpl_particle->position[2]; + + site_.u.x=mcpl_particle->direction[0]; + site_.u.y=mcpl_particle->direction[1]; + site_.u.z=mcpl_particle->direction[2]; + + //mcpl stores kinetic energy in MeV + site_.E=mcpl_particle->ekin*1e6; + //mcpl stores time in ms + site_.time=mcpl_particle->time*1e-3; + site_.wgt=mcpl_particle->weight; + site_.delayed_group=0; + sites_[i]=site_; + } + mcpl_close_file(mcpl_file); +} +#endif //OPENMC_MCPL + SourceSite FileSource::sample(uint64_t* seed) const { size_t i_site = sites_.size() * prn(seed); @@ -366,73 +414,7 @@ MCPLFileSource::MCPLFileSource(std::string path) read_source_bank(sites_); } - -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. - } - - switch(pdg){ - case 2112: - omc_particle_.particle=ParticleType::neutron; - break; - case 22: - omc_particle_.particle=ParticleType::photon; - break; - case 11: - omc_particle_.particle=ParticleType::electron; - break; - case -11: - omc_particle_.particle=ParticleType::positron; - break; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL +#endif //============================================================================== // Non-member functions From 73a391daef86e3a93c0c7180d9ff7f670afff240 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 038/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- include/openmc/state_point.h | 9 +++ src/state_point.cpp | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..2d92c935a6 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,6 +9,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { void load_state_point(); @@ -21,5 +25,10 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); +#endif + } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/state_point.cpp b/src/state_point.cpp index 9ea0459153..babd670a2d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,6 +28,10 @@ #include "openmc/timer.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -598,6 +602,45 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +void write_mcpl_point(const char *filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + // this must be rewritten: file_open is h5-specific + file_id = mcpl_create_outfile(filename_.c_str()); + //write_attribute(file_id, "filetype", "source"); + //write header stuff (oopy in xml-files as binary blobs for instance)) + if (VERSION_DEV){ + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + } else { + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + } + mcpl_hdr_set_srcname(file_id,line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + //change this - this is h5 specific + mcpl_closeandgzip_outfile(file_id); + } + +} + + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -714,6 +757,88 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if(surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + //write particles from the master node + //loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { +#ifdef OPENMC_MPI + if (i>0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + //now write the source_banke data again. + for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + //particle is now at the iterator + //write it to the mcpl-file + mcpl_particle_t p; + p.position[0]=_site->r.x; + p.position[1]=_site->r.y; + p.position[2]=_site->r.z; + + p.direction[0]=_site->u.x; + p.direction[1]=_site->u.y; + p.direction[2]=_site->u.x; + + //mcpl stores kinetic energy in MeV + p.ekin=_site->E*1e-6; + + p.time=_site->time*1e3; + + p.weight=_site->wgt; + + switch(_site->particle){ + case ParticleType::neutron: + p.pdgcode=2112; + break; + case ParticleType::photon: + p.pdgcode=22; + break; + case ParticleType::electron: + p.pdgcode=11; + break; + case ParticleType::positron: + p.pdgcode=-11; + break; + } + + mcpl_add_particle(file_id,&p); + } + } + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } + +} + + // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From d07223bf681e5d6e1d7cbde0dac6f97059a12767 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 039/265] add a trigger to simulation control --- src/simulation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/simulation.cpp b/src/simulation.cpp index b70535c330..05c8c04508 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -406,6 +406,13 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } + + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcplt_source_point(filename.c_str(), true); + } + + } void initialize_generation() From 55f2be19e6c0ef51d7a7946ac15a9f2df51da6da Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 16 Sep 2022 16:07:22 +0200 Subject: [PATCH 040/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- src/source.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index 626d02f9d7..21522438cf 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -426,6 +426,7 @@ MCPLFileSource::MCPLFileSource(std::string path) read_source_bank(sites_); } +<<<<<<< HEAD MCPLFileSource::~MCPLFileSource(){ mcpl_close_file(mcpl_file); @@ -495,6 +496,9 @@ SourceSite MCPLFileSource::read_single_particle() const #endif //OPENMC_MCPL ======= >>>>>>> 380cfc150b449ef56debb5261caa010a52c42bdd +======= +#endif +>>>>>>> 71568d4f4 (refactor MCPL file read part of FileSource) //============================================================================== // Non-member functions From f0af56b181555f52d05f245240846f4ce8eadcaa Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 041/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- include/openmc/state_point.h | 9 +++ src/state_point.cpp | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..2d92c935a6 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,6 +9,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { void load_state_point(); @@ -21,5 +25,10 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); +#endif + } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/state_point.cpp b/src/state_point.cpp index 9ea0459153..babd670a2d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,6 +28,10 @@ #include "openmc/timer.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -598,6 +602,45 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +void write_mcpl_point(const char *filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + // this must be rewritten: file_open is h5-specific + file_id = mcpl_create_outfile(filename_.c_str()); + //write_attribute(file_id, "filetype", "source"); + //write header stuff (oopy in xml-files as binary blobs for instance)) + if (VERSION_DEV){ + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + } else { + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + } + mcpl_hdr_set_srcname(file_id,line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + //change this - this is h5 specific + mcpl_closeandgzip_outfile(file_id); + } + +} + + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -714,6 +757,88 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if(surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + //write particles from the master node + //loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { +#ifdef OPENMC_MPI + if (i>0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + //now write the source_banke data again. + for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + //particle is now at the iterator + //write it to the mcpl-file + mcpl_particle_t p; + p.position[0]=_site->r.x; + p.position[1]=_site->r.y; + p.position[2]=_site->r.z; + + p.direction[0]=_site->u.x; + p.direction[1]=_site->u.y; + p.direction[2]=_site->u.x; + + //mcpl stores kinetic energy in MeV + p.ekin=_site->E*1e-6; + + p.time=_site->time*1e3; + + p.weight=_site->wgt; + + switch(_site->particle){ + case ParticleType::neutron: + p.pdgcode=2112; + break; + case ParticleType::photon: + p.pdgcode=22; + break; + case ParticleType::electron: + p.pdgcode=11; + break; + case ParticleType::positron: + p.pdgcode=-11; + break; + } + + mcpl_add_particle(file_id,&p); + } + } + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } + +} + + // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From 04c8ffc81c7ed7ced0c3525b5ec13f15f8fc079c Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 042/265] add a trigger to simulation control --- src/simulation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/simulation.cpp b/src/simulation.cpp index b70535c330..05c8c04508 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -406,6 +406,13 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } + + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcplt_source_point(filename.c_str(), true); + } + + } void initialize_generation() From 314f3dbbab75104f217d1e20e2146ced28108707 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 4 Oct 2022 10:11:06 +0200 Subject: [PATCH 043/265] do not define these functions if no MCPL present --- src/state_point.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index babd670a2d..5b00f302dc 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -602,6 +602,7 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +#ifdef OPENMC_MCPL void write_mcpl_point(const char *filename, bool surf_source_bank) { std::string filename_; @@ -639,7 +640,7 @@ void write_mcpl_point(const char *filename, bool surf_source_bank) } } - +#endif void write_source_bank(hid_t group_id, bool surf_source_bank) { @@ -757,6 +758,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +#ifdef OPENMC_MCPL void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) { int64_t dims_size = settings::n_particles; @@ -837,7 +839,7 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) } } - +#endif // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) From 2de20231ad584fa7a77a5b135c82f6e995671bcc Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:56:36 +0200 Subject: [PATCH 044/265] use find_package instead - MCPL now supports this --- CMakeLists.txt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be56f1c12f..98dc4fcaf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,13 @@ if(${HDF5_VERSION} VERSION_GREATER_EQUAL 1.12.0) list(APPEND cxxflags -DH5Oget_info_by_idx_vers=1 -DH5O_info_t_vers=1) endif() +#=============================================================================== +# MCPL +#=============================================================================== +if (OPENMC_USE_MCPL) + find_package(MCPL REQUIRED) +endif() + #=============================================================================== # Set compile/link flags based on which compiler is being used #=============================================================================== @@ -155,10 +162,6 @@ if(OPENMC_ENABLE_COVERAGE) list(APPEND ldflags --coverage) endif() -if(OPENMC_USE_MCPL) - list(APPEND ldflags -lmcpl) -endif() - # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") @@ -413,10 +416,6 @@ endif() if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() -if(OPENMC_USE_MCPL) - target_compile_definitions(libopenmc PUBLIC -DOPENMC_MCPL) -endif() - # Set git SHA1 hash as a compile definition if(GIT_FOUND) @@ -460,6 +459,11 @@ if (OPENMC_USE_MPI) target_link_libraries(libopenmc MPI::MPI_CXX) endif() +if (OPENMC_USE_MCPL) + target_compile_definitions(libopenmc PUBLIC OPENMC_MCPL) + target_link_libraries(libopenmc MCPL::mcpl) +endif() + #=============================================================================== # openmc executable #=============================================================================== From 7418cb99dd7c22e80532dcc46a9d2c4c04ceb63d Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:02 +0200 Subject: [PATCH 045/265] add mcpl-output settings --- include/openmc/settings.h | 1 + src/settings.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235b..66825867d8 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -48,6 +48,7 @@ extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? extern bool surf_source_write; //!< write surface source file? +extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? extern bool temperature_multipole; //!< use multipole data? diff --git a/src/settings.cpp b/src/settings.cpp index 11dd96e025..d1762596c6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -61,6 +61,7 @@ bool source_latest {false}; bool source_separate {false}; bool source_write {true}; bool surf_source_write {false}; +bool surf_mcpl_write {false}; bool surf_source_read {false}; bool survival_biasing {false}; bool temperature_multipole {false}; @@ -682,9 +683,14 @@ void read_settings_xml() max_surface_particles = std::stoll(get_node_value(node_ssw, "max_particles")); } +#ifdef OPENMC_MCPL + if(check_for_node(node_ssw, "mcpl")){ + surf_mcpl_write=true; + } +#endif } - // If source is not seperate and is to be written out in the statepoint file, + // If source is not separate and is to be written out in the statepoint file, // make sure that the sourcepoint batch numbers are contained in the // statepoint list if (!source_separate) { From 172c518f0e161d0176385c67d3717ad9fb229038 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:28 +0200 Subject: [PATCH 046/265] fix typo --- src/simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 05c8c04508..353ef99265 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -409,7 +409,7 @@ void finalize_batch() if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ auto filename = settings::path_output + ".mcpl"; - write_mcplt_source_point(filename.c_str(), true); + write_mcpl_source_point(filename.c_str(), true); } From 83e2921034f5c448d500b93634999cdb3f350e0b Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 11:00:39 +0200 Subject: [PATCH 047/265] add cond. compilation guards for MCPL functions --- src/state_point.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index babd670a2d..7c1a454476 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -602,7 +602,8 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } -void write_mcpl_point(const char *filename, bool surf_source_bank) +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename, bool surf_source_bank) { std::string filename_; if (filename) { @@ -639,7 +640,7 @@ void write_mcpl_point(const char *filename, bool surf_source_bank) } } - +#endif void write_source_bank(hid_t group_id, bool surf_source_bank) { @@ -757,6 +758,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +#ifdef OPENMC_MCPL void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) { int64_t dims_size = settings::n_particles; @@ -837,7 +839,7 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) } } - +#endif // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) From 5e82bb2e1f1b2857375566f98669be6c93b22638 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 09:12:01 +0200 Subject: [PATCH 048/265] expose a python function to check for mcpl --- openmc/lib/__init__.py | 3 +++ src/source.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c28..1e337fc073 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -48,6 +48,9 @@ def _coord_levels(): def _libmesh_enabled(): return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value +def _mcpl_enabled(): + return c_bool.in_dll(_dll, "MCPL_ENABLED").value + from .error import * from .core import * from .nuclide import * diff --git a/src/source.cpp b/src/source.cpp index 49e51c4b16..eace931d63 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -41,6 +41,12 @@ namespace openmc { // Global variables //============================================================================== +#ifdef OPENMC_MCPL +const bool MCPL_ENABLED = true; +#else +const bool MCPL_ENABLED = false; +#endif + namespace model { vector> external_sources; From 6038555def28547a0f392f0eeded8d8d49269d51 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 049/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- include/openmc/state_point.h | 9 +++ src/state_point.cpp | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..2d92c935a6 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,6 +9,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { void load_state_point(); @@ -21,5 +25,10 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); +#endif + } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/state_point.cpp b/src/state_point.cpp index 9ea0459153..babd670a2d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,6 +28,10 @@ #include "openmc/timer.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -598,6 +602,45 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +void write_mcpl_point(const char *filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + // this must be rewritten: file_open is h5-specific + file_id = mcpl_create_outfile(filename_.c_str()); + //write_attribute(file_id, "filetype", "source"); + //write header stuff (oopy in xml-files as binary blobs for instance)) + if (VERSION_DEV){ + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + } else { + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + } + mcpl_hdr_set_srcname(file_id,line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + //change this - this is h5 specific + mcpl_closeandgzip_outfile(file_id); + } + +} + + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -714,6 +757,88 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if(surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + //write particles from the master node + //loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { +#ifdef OPENMC_MPI + if (i>0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + //now write the source_banke data again. + for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + //particle is now at the iterator + //write it to the mcpl-file + mcpl_particle_t p; + p.position[0]=_site->r.x; + p.position[1]=_site->r.y; + p.position[2]=_site->r.z; + + p.direction[0]=_site->u.x; + p.direction[1]=_site->u.y; + p.direction[2]=_site->u.x; + + //mcpl stores kinetic energy in MeV + p.ekin=_site->E*1e-6; + + p.time=_site->time*1e3; + + p.weight=_site->wgt; + + switch(_site->particle){ + case ParticleType::neutron: + p.pdgcode=2112; + break; + case ParticleType::photon: + p.pdgcode=22; + break; + case ParticleType::electron: + p.pdgcode=11; + break; + case ParticleType::positron: + p.pdgcode=-11; + break; + } + + mcpl_add_particle(file_id,&p); + } + } + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } + +} + + // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From 2ba7f8d1882cf5e2562b473e233b4ca84ab81080 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 050/265] add a trigger to simulation control --- src/simulation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/simulation.cpp b/src/simulation.cpp index b70535c330..05c8c04508 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -406,6 +406,13 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } + + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcplt_source_point(filename.c_str(), true); + } + + } void initialize_generation() From 91cd304f0e4fd32eb6df0bdb75dd3f7d169013ce Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:56:36 +0200 Subject: [PATCH 051/265] use find_package instead - MCPL now supports this --- CMakeLists.txt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be56f1c12f..98dc4fcaf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,13 @@ if(${HDF5_VERSION} VERSION_GREATER_EQUAL 1.12.0) list(APPEND cxxflags -DH5Oget_info_by_idx_vers=1 -DH5O_info_t_vers=1) endif() +#=============================================================================== +# MCPL +#=============================================================================== +if (OPENMC_USE_MCPL) + find_package(MCPL REQUIRED) +endif() + #=============================================================================== # Set compile/link flags based on which compiler is being used #=============================================================================== @@ -155,10 +162,6 @@ if(OPENMC_ENABLE_COVERAGE) list(APPEND ldflags --coverage) endif() -if(OPENMC_USE_MCPL) - list(APPEND ldflags -lmcpl) -endif() - # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") @@ -413,10 +416,6 @@ endif() if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() -if(OPENMC_USE_MCPL) - target_compile_definitions(libopenmc PUBLIC -DOPENMC_MCPL) -endif() - # Set git SHA1 hash as a compile definition if(GIT_FOUND) @@ -460,6 +459,11 @@ if (OPENMC_USE_MPI) target_link_libraries(libopenmc MPI::MPI_CXX) endif() +if (OPENMC_USE_MCPL) + target_compile_definitions(libopenmc PUBLIC OPENMC_MCPL) + target_link_libraries(libopenmc MCPL::mcpl) +endif() + #=============================================================================== # openmc executable #=============================================================================== From 9b58c2f6dc93c4c51ce6ee7a26e7ffc645434020 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:02 +0200 Subject: [PATCH 052/265] add mcpl-output settings --- include/openmc/settings.h | 1 + src/settings.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235b..66825867d8 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -48,6 +48,7 @@ extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? extern bool surf_source_write; //!< write surface source file? +extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? extern bool temperature_multipole; //!< use multipole data? diff --git a/src/settings.cpp b/src/settings.cpp index 11dd96e025..d1762596c6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -61,6 +61,7 @@ bool source_latest {false}; bool source_separate {false}; bool source_write {true}; bool surf_source_write {false}; +bool surf_mcpl_write {false}; bool surf_source_read {false}; bool survival_biasing {false}; bool temperature_multipole {false}; @@ -682,9 +683,14 @@ void read_settings_xml() max_surface_particles = std::stoll(get_node_value(node_ssw, "max_particles")); } +#ifdef OPENMC_MCPL + if(check_for_node(node_ssw, "mcpl")){ + surf_mcpl_write=true; + } +#endif } - // If source is not seperate and is to be written out in the statepoint file, + // If source is not separate and is to be written out in the statepoint file, // make sure that the sourcepoint batch numbers are contained in the // statepoint list if (!source_separate) { From 43538ac2021f36caf0d72e002a119a806aa14b71 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:28 +0200 Subject: [PATCH 053/265] fix typo --- src/simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 05c8c04508..353ef99265 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -409,7 +409,7 @@ void finalize_batch() if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ auto filename = settings::path_output + ".mcpl"; - write_mcplt_source_point(filename.c_str(), true); + write_mcpl_source_point(filename.c_str(), true); } From cba745b28b6e384311393c177456b518b60b4f55 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 11:00:39 +0200 Subject: [PATCH 054/265] add cond. compilation guards for MCPL functions --- src/state_point.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index babd670a2d..7c1a454476 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -602,7 +602,8 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } -void write_mcpl_point(const char *filename, bool surf_source_bank) +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename, bool surf_source_bank) { std::string filename_; if (filename) { @@ -639,7 +640,7 @@ void write_mcpl_point(const char *filename, bool surf_source_bank) } } - +#endif void write_source_bank(hid_t group_id, bool surf_source_bank) { @@ -757,6 +758,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +#ifdef OPENMC_MCPL void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) { int64_t dims_size = settings::n_particles; @@ -837,7 +839,7 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) } } - +#endif // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) From 179df586853b65600518a0ea62526cc2193e0206 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 16 Sep 2022 16:07:22 +0200 Subject: [PATCH 055/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- src/source.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index eace931d63..bd76969d17 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -391,6 +391,79 @@ CustomSourceWrapper::~CustomSourceWrapper() } +MCPLFileSource::~MCPLFileSource(){ + mcpl_close_file(mcpl_file); +} + +SourceSite MCPLFileSource::sample(uint64_t* seed) const +{ + size_t i_site = sites_.size() * prn(seed); + return sites_[i_site]; +} + +void MCPLFileSource::read_source_bank(vector &sites_) +{ + sites_.resize(n_sites); + for (int i=0;ipdgcode; + while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { + mcpl_particle=mcpl_read(mcpl_file); + pdg=mcpl_particle->pdgcode; + //should check for file exhaustion. This could happen if particles are other than + //neutrons, photons, electrons, or positrons. + } + + switch(pdg){ + case 2112: + omc_particle_.particle=ParticleType::neutron; + break; + case 22: + omc_particle_.particle=ParticleType::photon; + break; + case 11: + omc_particle_.particle=ParticleType::electron; + break; + case -11: + omc_particle_.particle=ParticleType::positron; + break; + } + + //particle is good, convert to openmc-formalism + omc_particle_.r.x=mcpl_particle->position[0]; + omc_particle_.r.y=mcpl_particle->position[1]; + omc_particle_.r.z=mcpl_particle->position[2]; + + omc_particle_.u.x=mcpl_particle->direction[0]; + omc_particle_.u.y=mcpl_particle->direction[1]; + omc_particle_.u.z=mcpl_particle->direction[2]; + + //mcpl stores kinetic energy in MeV + omc_particle_.E=mcpl_particle->ekin*1e6; + //mcpl stores time in ms + omc_particle_.time=mcpl_particle->time*1e-3; + omc_particle_.wgt=mcpl_particle->weight; + omc_particle_.delayed_group=0; + return omc_particle_; +} +#endif //OPENMC_MCPL +======= +>>>>>>> 380cfc150b449ef56debb5261caa010a52c42bdd +======= +#endif +>>>>>>> 71568d4f4 (refactor MCPL file read part of FileSource) +>>>>>>> 55f2be19e (refactor MCPL file read part of FileSource) + //============================================================================== // Non-member functions //============================================================================== From e3fbeac636c2e9a47599c60e04b2a1ba4d9ba46d Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 056/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- src/source.cpp | 6 ------ src/state_point.cpp | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index bd76969d17..f205a0a256 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -457,12 +457,6 @@ SourceSite MCPLFileSource::read_single_particle() const return omc_particle_; } #endif //OPENMC_MCPL -======= ->>>>>>> 380cfc150b449ef56debb5261caa010a52c42bdd -======= -#endif ->>>>>>> 71568d4f4 (refactor MCPL file read part of FileSource) ->>>>>>> 55f2be19e (refactor MCPL file read part of FileSource) //============================================================================== // Non-member functions diff --git a/src/state_point.cpp b/src/state_point.cpp index 7c1a454476..31979c0c56 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -642,6 +642,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } #endif + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); From 3b7c4f6d2f141311cba5ad9e5f773b3bc3381852 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 4 Oct 2022 10:11:06 +0200 Subject: [PATCH 057/265] do not define these functions if no MCPL present --- src/state_point.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 31979c0c56..7c1a454476 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -642,7 +642,6 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } #endif - void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); From 982377b0cdfc573469aad799852a0f2dd50b2e68 Mon Sep 17 00:00:00 2001 From: erkn Date: Wed, 5 Oct 2022 14:31:05 +0200 Subject: [PATCH 058/265] fix misnamed variable and remove accientally committed code --- src/source.cpp | 169 ++----------------------------------------------- 1 file changed, 4 insertions(+), 165 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index e1891c5e94..e35734747f 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -308,16 +308,16 @@ FileSource::FileSource(mcpl_file_t mcpl_file) switch(pdg){ case 2112: - omc_particle_.particle=ParticleType::neutron; + site_.particle=ParticleType::neutron; break; case 22: - omc_particle_.particle=ParticleType::photon; + site_.particle=ParticleType::photon; break; case 11: - omc_particle_.particle=ParticleType::electron; + site_.particle=ParticleType::electron; break; case -11: - omc_particle_.particle=ParticleType::positron; + site_.particle=ParticleType::positron; break; } @@ -401,167 +401,6 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } -<<<<<<< HEAD -<<<<<<< HEAD -#ifdef OPENMC_MCPL -//=========================================================================== -// Read particles from an MCPL-file -//=========================================================================== -MCPLFileSource::MCPLFileSource(std::string path) -{ - // Check if source file exists - if (!file_exists(path)) { - fatal_error(fmt::format("Source file '{}' does not exist.", path)); - } - - // Read the source from a binary file instead of sampling from some - // assumed source distribution - write_message(6, "Reading mcpl source file from {}",path); - - // Open the mcpl file - mcpl_file = mcpl_open_file(path.c_str()); - - //do checks on the mcpl_file to see if particles are many enough. - // should model this on the example source shown in the docs. - n_sites=mcpl_hdr_nparticles(mcpl_file); - - read_source_bank(sites_); -} -<<<<<<< HEAD - -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. - } - - switch(pdg){ - case 2112: - omc_particle_.particle=ParticleType::neutron; - break; - case 22: - omc_particle_.particle=ParticleType::photon; - break; - case 11: - omc_particle_.particle=ParticleType::electron; - break; - case -11: - omc_particle_.particle=ParticleType::positron; - break; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL - -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. - } - - switch(pdg){ - case 2112: - omc_particle_.particle=ParticleType::neutron; - break; - case 22: - omc_particle_.particle=ParticleType::photon; - break; - case 11: - omc_particle_.particle=ParticleType::electron; - break; - case -11: - omc_particle_.particle=ParticleType::positron; - break; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL //============================================================================== // Non-member functions From 12f088a5ce68c639c9772574eb69c4be5de452fe Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 6 Oct 2022 01:33:21 +0200 Subject: [PATCH 059/265] must have a full filename --- src/simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 353ef99265..2c9e7e8566 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -408,7 +408,7 @@ void finalize_batch() } if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ - auto filename = settings::path_output + ".mcpl"; + auto filename = settings::path_output + "surface_source.mcpl"; write_mcpl_source_point(filename.c_str(), true); } From 0706a0eca305c8fc4a0a5cbfb0c5b6e46099753e Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 00:46:32 +0200 Subject: [PATCH 060/265] fix malformed format string --- src/state_point.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 7c1a454476..e197ee162a 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -625,9 +625,9 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) //write_attribute(file_id, "filetype", "source"); //write header stuff (oopy in xml-files as binary blobs for instance)) if (VERSION_DEV){ - line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + line=fmt::format("OpenMC {0}.{1}.{2}-development",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); } else { - line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + line=fmt::format("OpenMC {0}.{1}.{2}",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); } mcpl_hdr_set_srcname(file_id,line.c_str()); } From aeadaf0b2f1a59fcd2c3f8cb3ad8fa7dab36e342 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 00:48:44 +0200 Subject: [PATCH 061/265] fix typo causing unit length errors --- src/state_point.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index e197ee162a..a8a4be034e 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -802,9 +802,11 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) p.position[1]=_site->r.y; p.position[2]=_site->r.z; + //mcpl requires that the direction vector is unit length + //which is also the case in openmc p.direction[0]=_site->u.x; p.direction[1]=_site->u.y; - p.direction[2]=_site->u.x; + p.direction[2]=_site->u.z; //mcpl stores kinetic energy in MeV p.ekin=_site->E*1e-6; From 72251eb30a996e6aaa9672292a1483c69d53a652 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 02:00:33 +0200 Subject: [PATCH 062/265] enable mcpl surf_source_write in the python layer --- openmc/settings.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 2d81216599..45ff0edc68 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -165,6 +165,7 @@ class Settings: banked (int) :max_particles: Maximum number of particles to be banked on surfaces per process (int) + :mcpl: Output in the form of an MCPL-file (bool) survival_biasing : bool Indicate whether survival biasing is to be used tabular_legendre : dict @@ -643,7 +644,7 @@ class Settings: cv.check_type('surface source writing options', surf_source_write, Mapping) for key, value in surf_source_write.items(): cv.check_value('surface source writing key', key, - ('surface_ids', 'max_particles')) + ('surface_ids', 'max_particles', 'mcpl')) if key == 'surface_ids': cv.check_type('surface ids for source banking', value, Iterable, Integral) @@ -655,6 +656,9 @@ class Settings: value, Integral) cv.check_greater_than('maximum particle banks on surfaces per process', value, 0) + elif key == 'mcpl': + cv.check_type('write to an MCPL-format file', value, bool) + self._surf_source_write = surf_source_write @confidence_intervals.setter @@ -1023,6 +1027,9 @@ class Settings: if 'max_particles' in self._surf_source_write: subelement = ET.SubElement(element, "max_particles") subelement.text = str(self._surf_source_write['max_particles']) + if 'mcpl' in self._surf_source_write: + subelement = ET.SubElement(element, "mcpl") + subelement.text = str(self._surf_source_write['mcpl']).lower() def _create_confidence_intervals(self, root): if self._confidence_intervals is not None: From bf0c5d7f9a8d118e4770d4ed4ca184c93aa30896 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 22 Jun 2022 22:50:07 +0200 Subject: [PATCH 063/265] add placeholder skeleton for MCPL-input --- include/openmc/source.h | 19 +++++++++++++++++++ src/source.cpp | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/openmc/source.h b/include/openmc/source.h index 04b9ff8563..0f1965cb8d 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -110,6 +110,25 @@ private: vector sites_; //!< Source sites from a file }; +//============================================================================== +// MCPL-file input source +//============================================================================== +class MCPLFileSource : public Source { +public: + // Constructors, destructors + MCPLFileSource(std::string path); + ~MCPLFileSource(); + + // Defer implementation to custom source library + SourceSite sample(uint64_t* seed) const override + +private: + vector sites_; //! Date: Thu, 23 Jun 2022 11:34:53 +0200 Subject: [PATCH 064/265] include mcpl header --- src/source.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index 1e84f8334c..7c1184602a 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -33,6 +33,10 @@ #include "openmc/state_point.h" #include "openmc/xml_interface.h" +#ifdef HAS_MCPL +#include +#endif + namespace openmc { //============================================================================== From 249a554980801a3c2cc3be336369c95c0e447436 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 23 Jun 2022 11:35:46 +0200 Subject: [PATCH 065/265] declarations of MCPL-internals --- include/openmc/source.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 0f1965cb8d..24a3d10aed 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -119,11 +119,20 @@ public: MCPLFileSource(std::string path); ~MCPLFileSource(); - // Defer implementation to custom source library - SourceSite sample(uint64_t* seed) const override + // Properties + ParticleType particle_type() const { return particle_; } + + //! Sample from the external source distribution + //! \param[inout] seed Pseudorandom seed pointer + //! \return Site read from MCPL-file + SourceSite sample(uint64_t* seed) const override; private: + ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted vector sites_; //! Date: Thu, 23 Jun 2022 11:37:05 +0200 Subject: [PATCH 066/265] internals of sample and constructor --- src/source.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 7c1184602a..3dfd007078 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -383,7 +383,7 @@ CustomSourceWrapper::~CustomSourceWrapper() } //=========================================================================== -// Read an MCPL-file +// Read particles from an MCPL-file //=========================================================================== MCPLFileSource::MCPLFileSource(std::string path) { @@ -394,12 +394,55 @@ MCPLFileSource::MCPLFileSource(std::string path) // Read the source from a binary file instead of sampling from some // assumed source distribution - write_message(6, "Reading source file from {}...", path); + write_message(6, "Reading mcpl source file from {}",path); // Open the mcpl file - id_t file_id = mcpl_open(path.c_str) - + mcpl_file = mcpl_open(path.c_str); + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + uint64_t nparticles=mcpl_hdr_nparticles(mcpl_file); + +} + +MCPLFileSource::~MCPLFilsSource(){ + mcpl_close_file(mcpl_file); +} + +SourceSite MCPLFileSource::sample(uint64_t *seed){ + SourceSite omc_particle; + mcpl_particle *mcpl_particle; + //extract particle from mcpl-file + mcpl_particle=mcpl_reazd(mcplfile); + // check if it is a neutron or a photon. otherwise skip + while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) + { + mcpl_particle(mcpl_read(mcplfile); + //check for file exhaustion + } + + if(mcpl_particle->pdgcode=2112) { + omc_particle_=ParticleType::neutron; + } else { + omc_particle_=Particletype::photon; + } + + //particle is good, convert to openmc-formalism + omc_particle.r.x=mcpl_particle.x; + omc_particle.r.y=mcpl_particle.y; + omc_particle.r.z=mcpl_particle.z; + + omc_particle.u.x=mcpl_particle->direction[0]; + omc_particle.u.y=mcpl_particle->direction[1]; + omc_particle.u.z=mcpl_particle->direction[2]; + + //mcpl stores particles in MeV + omc_particle.E=mcpl_particle->ekin*1e6; + + omc_particle.t=mcpl_particle->time*1e-3; + omc_particle.wgt=mcpl_particle->weight; + omc_particle.delayed_group=0; + return omc_particle; } From 999acbba9a9f69648e9b91198fe4ff0792838b1d Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:25:35 +0200 Subject: [PATCH 067/265] (wip): skeleton for input only test --- .../regression_tests/source_mcpl_file/test.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/test.py diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py new file mode 100644 index 0000000000..5a6a87d878 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -0,0 +1,52 @@ +import pathlib as pl +import os +import shutil +import subprocess +import textwrap + +import openmc +import pytest + +from tests.testing_harness import TestHarness + +def model(): + subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) + subprocess.run(['./gen_dummy_mcpl.out']) + +class SourceMCPLFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._create_input() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + + def _test_output_created(self): + """Check that the output files were created""" + stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ + 'exist.' + assert statepoint[0].endswith('h5'), \ + 'statepoint file does not end with h5.' + +def test_mcpl_source_file(): + harness = SourceMCPLFileTestHarness('statepoint.10.h5') + harness.main() + From 3e805cce158630e70df95abb5d20368983339031 Mon Sep 17 00:00:00 2001 From: erkn Date: Mon, 27 Jun 2022 23:26:01 +0200 Subject: [PATCH 068/265] c-prog to generate a dummy mcpl --- .../source_mcpl_file/gen_dummy_mcpl.c | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c new file mode 100644 index 0000000000..7b5a933f18 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +const int batches=10; +const int particles=10000; + +double rand01(){ + double r=rand()/((double) RAND_MAX); + return r; +} + + +int main(int argc, char **argv){ + char outfilename[128]; + snprintf(outfilename,127,"source.%d.mcpl",batches); + + /*generate an mcpl_header_of sorts*/ + mcpl_outfile_t outfile=mcpl_create_outfile(outfilename); + mcpl_particle_t *particle, Particle; + + char line[256]; + snprintf(line,255,"gen_dummy_mcpl.c"); + mcpl_hdr_set_srcname(outfile,line); + mcpl_enable_universal_pdgcode(outfile,2112);/*all particles are neutrons*/ + snprintf(line,255,"Dummy MCPL-file for testing OpenMC mcpl input"); + mcpl_hdr_add_comment(outfile,line); + + /*also add the instrument file and the command line as blobs*/ + FILE *fp; + if( (fp=fopen("gen_dummy_mcpl.c","rb"))!=NULL){ + unsigned char *buffer; + int size,status; + /*find the file size by seeking to end, "tell" the position, and then go back again*/ + fseek(fp, 0L, SEEK_END); + size = ftell(fp); // get current file pointer + fseek(fp, 0L, SEEK_SET); // seek back to beginning of file + if ( size && (buffer=malloc(size))!=NULL){ + if (size!=(fread(buffer,1,size,fp))){ + fprintf(stderr,"Warning: c source generator file not read cleanly\n"); + } + mcpl_hdr_add_data(outfile, "mcpl_point_source_file_generator", size, buffer); + free(buffer); + } + fclose(fp); + } else { + fprintf(stderr,"Warning: could not open c source generator file, hence not embedded.\n"); + } + + + /*the main particle loop*/ + particle=&Particle; + int i; + for (i=0;iposition[0]=0; + particle->position[1]=0; + particle->position[2]=0; + + /*generate a random direction on unit sphere*/ + double nrm=2.0; + double vx,vy,vz; + int iter=0; + do { + if(iter>100){ + printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + } + vx=rand01()*2.0-1.0; + vy=rand01()*2.0-1.0; + vz=rand01()*2.0-1.0; + nrm=(vx*vx + vy*vy + vz*vz); + iter++; + } while (nrm>1.0); + vx/=sqrt(nrm); + vy/=sqrt(nrm); + vz/=sqrt(nrm); + particle->direction[0]=vx; + particle->direction[1]=vy; + particle->direction[2]=vz; + + /*generate the kinetic energy (in MeV) of the particle*/ + particle->ekin=1e-3; + + /*set time=0*/ + particle->time=0; + /*set weight*/ + particle->weight=1; + + particle->userflags=0; + mcpl_add_particle(outfile,particle); + } + mcpl_closeandgzip_outfile(outfile); +} From c6b42e828cf8bf9bccf77d296976ff83902fb346 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:30:07 +0200 Subject: [PATCH 069/265] don't zip the output-file --- .../source_mcpl_file/gen_dummy_mcpl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c index 7b5a933f18..f35a07fc44 100644 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -54,20 +54,20 @@ int main(int argc, char **argv){ int i; for (i=0;iposition[0]=0; particle->position[1]=0; particle->position[2]=0; - + /*generate a random direction on unit sphere*/ double nrm=2.0; double vx,vy,vz; int iter=0; do { if(iter>100){ - printf("warning: exceeed max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); + printf("Warning: Exceeded max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); } vx=rand01()*2.0-1.0; vy=rand01()*2.0-1.0; @@ -82,9 +82,9 @@ int main(int argc, char **argv){ particle->direction[1]=vy; particle->direction[2]=vz; - /*generate the kinetic energy (in MeV) of the particle*/ + /*generate the kinetic energy (in MeV) of the particle*/ particle->ekin=1e-3; - + /*set time=0*/ particle->time=0; /*set weight*/ @@ -93,5 +93,5 @@ int main(int argc, char **argv){ particle->userflags=0; mcpl_add_particle(outfile,particle); } - mcpl_closeandgzip_outfile(outfile); + mcpl_close_outfile(outfile); } From b2f83857e4af32848570abc33985a40587e77489 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 5 Jul 2022 01:53:14 +0200 Subject: [PATCH 070/265] now compiles fine if mcpl is indeed installed --- .../regression_tests/source_mcpl_file/test.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 5a6a87d878..0adf67f1aa 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -3,16 +3,13 @@ import os import shutil import subprocess import textwrap - +import glob import openmc import pytest from tests.testing_harness import TestHarness -def model(): - subprocess.run(['gcc','-o gen_dummt_mcpl.out','-lm','-lmcpl','gen_dummy_mcpl.c'] ) - subprocess.run(['./gen_dummy_mcpl.out']) - + class SourceMCPLFileTestHarness(TestHarness): def execute_test(self): """Run OpenMC with the appropriate arguments and check the outputs.""" @@ -26,6 +23,10 @@ class SourceMCPLFileTestHarness(TestHarness): finally: self._cleanup() + def _create_input(self): + subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + subprocess.run(['./gen_dummy_mcpl.out']) + def update_results(self): """Update the results_true using the current version of OpenMC.""" try: @@ -40,13 +41,19 @@ class SourceMCPLFileTestHarness(TestHarness): def _test_output_created(self): """Check that the output files were created""" - stat epoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ 'exist.' assert statepoint[0].endswith('h5'), \ 'statepoint file does not end with h5.' + def _cleanup(self): + super()._cleanup() + source_mcpl=glob.glob(os.path.join(os.getcwd(),'source*.mcpl')) + for f in source_mcpl: + if (os.path.exists(f)): + os.remove(f) + def test_mcpl_source_file(): harness = SourceMCPLFileTestHarness('statepoint.10.h5') harness.main() - From 005189bfdfa9f956139af45c6504e22d2eb8659c Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Mon, 11 Jul 2022 09:49:29 +0200 Subject: [PATCH 071/265] fix misprints --- src/source.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 3dfd007078..3ef5ae16f0 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -413,7 +413,7 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ SourceSite omc_particle; mcpl_particle *mcpl_particle; //extract particle from mcpl-file - mcpl_particle=mcpl_reazd(mcplfile); + mcpl_particle=mcpl_read(mcplfile); // check if it is a neutron or a photon. otherwise skip while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { @@ -421,9 +421,9 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ //check for file exhaustion } - if(mcpl_particle->pdgcode=2112) { + if(mcpl_particle->pdgcode==2112) { omc_particle_=ParticleType::neutron; - } else { + } else if (mcpl_particle->pdgcode==22){ omc_particle_=Particletype::photon; } @@ -436,9 +436,9 @@ SourceSite MCPLFileSource::sample(uint64_t *seed){ omc_particle.u.y=mcpl_particle->direction[1]; omc_particle.u.z=mcpl_particle->direction[2]; - //mcpl stores particles in MeV + //mcpl stores kinetic energy in MeV omc_particle.E=mcpl_particle->ekin*1e6; - + //mcpl stores time in ms omc_particle.t=mcpl_particle->time*1e-3; omc_particle.wgt=mcpl_particle->weight; omc_particle.delayed_group=0; From 935396e40bf97a6ba4a9dcf2dcac30b5a80056ba Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:48:55 +0200 Subject: [PATCH 072/265] add necessary xmls --- tests/regression_tests/source_mcpl_file/geometry.xml | 8 ++++++++ tests/regression_tests/source_mcpl_file/materials.xml | 9 +++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/geometry.xml create mode 100644 tests/regression_tests/source_mcpl_file/materials.xml diff --git a/tests/regression_tests/source_mcpl_file/geometry.xml b/tests/regression_tests/source_mcpl_file/geometry.xml new file mode 100644 index 0000000000..bc56030e18 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/geometry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/regression_tests/source_mcpl_file/materials.xml b/tests/regression_tests/source_mcpl_file/materials.xml new file mode 100644 index 0000000000..2472a74717 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + From e87ebfebc29b07f9ca575629364165fc129ed512 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:49:56 +0200 Subject: [PATCH 073/265] set seed to make test deteministic --- tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c index f35a07fc44..61eea95c6f 100644 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c @@ -52,6 +52,7 @@ int main(int argc, char **argv){ /*the main particle loop*/ particle=&Particle; int i; + srand(1234); for (i=0;i Date: Tue, 12 Jul 2022 13:50:35 +0200 Subject: [PATCH 074/265] test target result --- tests/regression_tests/source_mcpl_file/results_true.dat | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/results_true.dat diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat new file mode 100644 index 0000000000..f2209006a1 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +3.017557E-01 3.398770E-03 From c21ff938c79bd905bb0f6e60c7abbf960b3d87f3 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 13:50:53 +0200 Subject: [PATCH 075/265] yet another necessary file --- tests/regression_tests/source_mcpl_file/settings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/regression_tests/source_mcpl_file/settings.xml diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml new file mode 100644 index 0000000000..8b1510e0ce --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -0,0 +1,11 @@ + + + + 10 + 5 + 1000 + + + source.10.mcpl + + From a123d239ddf016167cef9c99add91c5f665ac19c Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 12 Jul 2022 14:40:01 +0200 Subject: [PATCH 076/265] add a check that the test input got created as intended --- tests/regression_tests/source_mcpl_file/test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 0adf67f1aa..922ddddd31 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -15,6 +15,7 @@ class SourceMCPLFileTestHarness(TestHarness): """Run OpenMC with the appropriate arguments and check the outputs.""" try: self._create_input() + self._test_input_created() self._run_openmc() self._test_output_created() results = self._get_results() @@ -31,6 +32,7 @@ class SourceMCPLFileTestHarness(TestHarness): """Update the results_true using the current version of OpenMC.""" try: self._create_input() + self._test_input_created() self._run_openmc() self._test_output_created() results = self._get_results() @@ -39,6 +41,14 @@ class SourceMCPLFileTestHarness(TestHarness): finally: self._cleanup() + def _test_input_created(self): + """Check that the input mcpl.file was generated as it should""" + mcplfile=glob.glob(os.path.join(os.getcwd(),'source.10.mcpl')) + assert len(mcplfile) == 1, 'Either multiple or no mcpl files ' \ + 'exist.' + assert mcplfile[0].endswith('mcpl'), \ + 'output file does not end with mcpl.' + def _test_output_created(self): """Check that the output files were created""" statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) @@ -55,5 +65,5 @@ class SourceMCPLFileTestHarness(TestHarness): os.remove(f) def test_mcpl_source_file(): - harness = SourceMCPLFileTestHarness('statepoint.10.h5') + harness = SourceMCPLFileTestHarness('source.10.mcpl') harness.main() From 1509420c214275d2c27b458caefdfc943cd413de Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:25:55 +0200 Subject: [PATCH 077/265] fix bugs and finalize interface to MCPL --- include/openmc/source.h | 22 +++++++------ src/source.cpp | 73 +++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 24a3d10aed..0c944aef93 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -14,6 +14,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { //============================================================================== @@ -110,6 +114,7 @@ private: vector sites_; //!< Source sites from a file }; +#ifdef OPENMC_MCPL //============================================================================== // MCPL-file input source //============================================================================== @@ -119,24 +124,21 @@ public: MCPLFileSource(std::string path); ~MCPLFileSource(); - // Properties - ParticleType particle_type() const { return particle_; } - + // Methods //! Sample from the external source distribution //! \param[inout] seed Pseudorandom seed pointer //! \return Site read from MCPL-file - SourceSite sample(uint64_t* seed) const override; + SourceSite sample(uint64_t* seed) const; private: - ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted + SourceSite read_single_particle() const; + void read_source_bank(vector &sites_); + vector sites_; //! #endif @@ -382,6 +382,7 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } +#ifdef OPENMC_MCPL //=========================================================================== // Read particles from an MCPL-file //=========================================================================== @@ -397,54 +398,70 @@ MCPLFileSource::MCPLFileSource(std::string path) write_message(6, "Reading mcpl source file from {}",path); // Open the mcpl file - mcpl_file = mcpl_open(path.c_str); + mcpl_file = mcpl_open_file(path.c_str()); //do checks on the mcpl_file to see if particles are many enough. // should model this on the example source shown in the docs. - uint64_t nparticles=mcpl_hdr_nparticles(mcpl_file); + n_sites=mcpl_hdr_nparticles(mcpl_file); + read_source_bank(sites_); } -MCPLFileSource::~MCPLFilsSource(){ +MCPLFileSource::~MCPLFileSource(){ mcpl_close_file(mcpl_file); } -SourceSite MCPLFileSource::sample(uint64_t *seed){ - SourceSite omc_particle; - mcpl_particle *mcpl_particle; +SourceSite MCPLFileSource::sample(uint64_t* seed) const +{ + size_t i_site = sites_.size() * prn(seed); + return sites_[i_site]; +} + +void MCPLFileSource::read_source_bank(vector &sites_) +{ + sites_.resize(n_sites); + for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) - { - mcpl_particle(mcpl_read(mcplfile); - //check for file exhaustion + mcpl_particle=mcpl_read(mcpl_file); + // check if it is a neutron or a photon. otherwise skip + while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + mcpl_particle=mcpl_read(mcpl_file); + //should check for file exhaustion This could happen if particles are other than + //neutrons or photons } if(mcpl_particle->pdgcode==2112) { - omc_particle_=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22){ - omc_particle_=Particletype::photon; + omc_particle_.particle=ParticleType::neutron; + } else if (mcpl_particle->pdgcode==22) { + omc_particle_.particle=ParticleType::photon; } //particle is good, convert to openmc-formalism - omc_particle.r.x=mcpl_particle.x; - omc_particle.r.y=mcpl_particle.y; - omc_particle.r.z=mcpl_particle.z; + omc_particle_.r.x=mcpl_particle->position[0]; + omc_particle_.r.y=mcpl_particle->position[1]; + omc_particle_.r.z=mcpl_particle->position[2]; - omc_particle.u.x=mcpl_particle->direction[0]; - omc_particle.u.y=mcpl_particle->direction[1]; - omc_particle.u.z=mcpl_particle->direction[2]; + omc_particle_.u.x=mcpl_particle->direction[0]; + omc_particle_.u.y=mcpl_particle->direction[1]; + omc_particle_.u.z=mcpl_particle->direction[2]; //mcpl stores kinetic energy in MeV - omc_particle.E=mcpl_particle->ekin*1e6; + omc_particle_.E=mcpl_particle->ekin*1e6; //mcpl stores time in ms - omc_particle.t=mcpl_particle->time*1e-3; - omc_particle.wgt=mcpl_particle->weight; - omc_particle.delayed_group=0; - return omc_particle; + omc_particle_.time=mcpl_particle->time*1e-3; + omc_particle_.wgt=mcpl_particle->weight; + omc_particle_.delayed_group=0; + return omc_particle_; } - +#endif //OPENMC_MCPL //============================================================================== // Non-member functions From 2cbcd53f077b3afc7d01e0ebfe70ba1e0790fe39 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:26:24 +0200 Subject: [PATCH 078/265] add an option to turn on mcpl-support --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 521b46cfc6..1408e85afc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) +option(OPENMC_USE_MCPL "Enable MPCPL" OFF) # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") @@ -185,6 +186,10 @@ if(OPENMC_ENABLE_COVERAGE) list(APPEND ldflags --coverage) endif() +if(OPENMC_USE_MCPL) + list(APPEND ldflags -lmcpl) +endif() + # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") @@ -439,6 +444,10 @@ endif() if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() +if(OPENMC_USE_MCPL) + target_compile_definitions(libopenmc PUBLIC -DOPENMC_MCPL) +endif() + # Set git SHA1 hash as a compile definition if(GIT_FOUND) From 46e648cf383df7b7b72b4f1f5c389f3c191c69a1 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 10:27:18 +0200 Subject: [PATCH 079/265] add a source type to the settings interface --- src/settings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index eb0d4936c7..12e0a80509 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -430,6 +430,9 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); model::external_sources.push_back(make_unique(path)); + } else if (check_for_node(node, "mcpl")) { + auto path = get_node_value(node, "mcpl", false, true); + model::external_sources.push_back(make_unique(path)); } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); From 96ae7b4228798549d1bb969777a73f926ba19465 Mon Sep 17 00:00:00 2001 From: erkn Date: Sat, 9 Jul 2022 16:31:00 +0200 Subject: [PATCH 080/265] fix misprints --- src/source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index bdfe5349c8..4b27bdc7eb 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -392,7 +392,7 @@ MCPLFileSource::MCPLFileSource(std::string path) if (!file_exists(path)) { fatal_error(fmt::format("Source file '{}' does not exist.", path)); } - + // Read the source from a binary file instead of sampling from some // assumed source distribution write_message(6, "Reading mcpl source file from {}",path); From 2fa445193870a1bd6818fd82d1ea755e890c2309 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 12:13:10 +0200 Subject: [PATCH 081/265] only add mcpl-support if OPENMC_MCPL is defined --- src/settings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index 12e0a80509..d419cab06f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -430,9 +430,11 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); model::external_sources.push_back(make_unique(path)); +#ifdef OPENMC_MCPL } else if (check_for_node(node, "mcpl")) { auto path = get_node_value(node, "mcpl", false, true); model::external_sources.push_back(make_unique(path)); +#endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); From 2773add0473f28d225c1ad9efbc6fcc9e97230ed Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 14:13:33 +0200 Subject: [PATCH 082/265] needed to avoid name clashes with other tests --- tests/regression_tests/source_mcpl_file/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/regression_tests/source_mcpl_file/__init__.py diff --git a/tests/regression_tests/source_mcpl_file/__init__.py b/tests/regression_tests/source_mcpl_file/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From bb8c022d77f8e22ad3d856edeb2b1a16a6311067 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 13 Jul 2022 20:55:53 +0200 Subject: [PATCH 083/265] fail without tripping the entire test-process --- tests/regression_tests/source_mcpl_file/test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 922ddddd31..f55a769047 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -1,11 +1,6 @@ -import pathlib as pl import os -import shutil import subprocess -import textwrap import glob -import openmc -import pytest from tests.testing_harness import TestHarness @@ -25,7 +20,8 @@ class SourceMCPLFileTestHarness(TestHarness): self._cleanup() def _create_input(self): - subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + compiled=subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) + assert compiled==0, 'Could not compile mcpl-file generator code' subprocess.run(['./gen_dummy_mcpl.out']) def update_results(self): From 2730f1954f154df24ed7171e9dda39e4325c1b2c Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 09:11:41 +0200 Subject: [PATCH 084/265] allow electrons and positrons as well --- src/source.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 4b27bdc7eb..cfcd79e0a6 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -429,19 +429,30 @@ SourceSite MCPLFileSource::read_single_particle() const { SourceSite omc_particle_; const mcpl_particle_t *mcpl_particle; - //extract particle from mcpl-file + // extract particle from mcpl-file mcpl_particle=mcpl_read(mcpl_file); - // check if it is a neutron or a photon. otherwise skip - while ( mcpl_particle->pdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + // check if it is a neutron, photon, electron, or positron. Otherwise skip. + int pdg=mcpl_particle->pdgcode; + while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { mcpl_particle=mcpl_read(mcpl_file); - //should check for file exhaustion This could happen if particles are other than - //neutrons or photons + pdg=mcpl_particle->pdgcode; + //should check for file exhaustion. This could happen if particles are other than + //neutrons, photons, electrons, or positrons. } - if(mcpl_particle->pdgcode==2112) { - omc_particle_.particle=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22) { - omc_particle_.particle=ParticleType::photon; + switch(pdg){ + case 2112: + omc_particle_.particle=ParticleType::neutron; + break; + case 22: + omc_particle_.particle=ParticleType::photon; + break; + case 11: + omc_particle_.particle=ParticleType::electron; + break; + case -11: + omc_particle_.particle=ParticleType::positron; + break; } //particle is good, convert to openmc-formalism From 3b052f03cfce7605bf96641346800022e462a02a Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 09:12:01 +0200 Subject: [PATCH 085/265] expose a python function to check for mcpl --- openmc/lib/__init__.py | 3 +++ src/source.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c28..1e337fc073 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -48,6 +48,9 @@ def _coord_levels(): def _libmesh_enabled(): return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value +def _mcpl_enabled(): + return c_bool.in_dll(_dll, "MCPL_ENABLED").value + from .error import * from .core import * from .nuclide import * diff --git a/src/source.cpp b/src/source.cpp index cfcd79e0a6..dbfcd1ceba 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -43,6 +43,12 @@ namespace openmc { // Global variables //============================================================================== +#ifdef OPENMC_MCPL +const bool MCPL_ENABLED = true; +#else +const bool MCPL_ENABLED = false; +#endif + namespace model { vector> external_sources; From 5640bdb9eb25bc5675928490ef4bf925e9155ae1 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 15 Jul 2022 21:35:59 +0200 Subject: [PATCH 086/265] fix typo Co-authored-by: Paul Romano --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1408e85afc..32beb41ecd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) -option(OPENMC_USE_MCPL "Enable MPCPL" OFF) +option(OPENMC_USE_MCPL "Enable MCPL" OFF) # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") From 95338eada002270c437906cb55607945dabe39b0 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 9 Aug 2022 08:12:19 +0200 Subject: [PATCH 087/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- include/openmc/source.h | 30 +-------- src/settings.cpp | 2 +- src/source.cpp | 139 ++++++++++++++-------------------------- 3 files changed, 52 insertions(+), 119 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index 0c944aef93..bb7e2d55f7 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -106,7 +106,9 @@ class FileSource : public Source { public: // Constructors explicit FileSource(std::string path); - +#ifdef OPENMC_MCPL + explicit FileSource(mcpl_file_t mcpl_file); +#endif // Methods SourceSite sample(uint64_t* seed) const override; @@ -114,32 +116,6 @@ private: vector sites_; //!< Source sites from a file }; -#ifdef OPENMC_MCPL -//============================================================================== -// MCPL-file input source -//============================================================================== -class MCPLFileSource : public Source { -public: - // Constructors, destructors - MCPLFileSource(std::string path); - ~MCPLFileSource(); - - // Methods - //! Sample from the external source distribution - //! \param[inout] seed Pseudorandom seed pointer - //! \return Site read from MCPL-file - SourceSite sample(uint64_t* seed) const; - -private: - SourceSite read_single_particle() const; - void read_source_bank(vector &sites_); - - vector sites_; //!(path)); + model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); #endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters diff --git a/src/source.cpp b/src/source.cpp index dbfcd1ceba..e43f03929b 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -329,6 +329,54 @@ FileSource::FileSource(std::string path) file_close(file_id); } +#ifdef OPENMC_MCPL +FileSource::FileSource(mcpl_file_t mcpl_file) +{ + //do checks on the mcpl_file to see if particles are many enough. + // should model this on the example source shown in the docs. + size_t n_sites=mcpl_hdr_nparticles(mcpl_file); + + sites_.resize(n_sites); + for (int i=0;ipdgcode!=2112 && mcpl_particle->pdgcode!=22 ) { + mcpl_particle=mcpl_read(mcpl_file); + //should check for file exhaustion This could happen if particles are other than + //neutrons or photons + } + + if(mcpl_particle->pdgcode==2112) { + site_.particle=ParticleType::neutron; + } else if (mcpl_particle->pdgcode==22) { + site_.particle=ParticleType::photon; + } + + //particle is good, convert to openmc-formalism + site_.r.x=mcpl_particle->position[0]; + site_.r.y=mcpl_particle->position[1]; + site_.r.z=mcpl_particle->position[2]; + + site_.u.x=mcpl_particle->direction[0]; + site_.u.y=mcpl_particle->direction[1]; + site_.u.z=mcpl_particle->direction[2]; + + //mcpl stores kinetic energy in MeV + site_.E=mcpl_particle->ekin*1e6; + //mcpl stores time in ms + site_.time=mcpl_particle->time*1e-3; + site_.wgt=mcpl_particle->weight; + site_.delayed_group=0; + sites_[i]=site_; + } + mcpl_close_file(mcpl_file); +} +#endif //OPENMC_MCPL + SourceSite FileSource::sample(uint64_t* seed) const { size_t i_site = sites_.size() * prn(seed); @@ -388,97 +436,6 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } -#ifdef OPENMC_MCPL -//=========================================================================== -// Read particles from an MCPL-file -//=========================================================================== -MCPLFileSource::MCPLFileSource(std::string path) -{ - // Check if source file exists - if (!file_exists(path)) { - fatal_error(fmt::format("Source file '{}' does not exist.", path)); - } - - // Read the source from a binary file instead of sampling from some - // assumed source distribution - write_message(6, "Reading mcpl source file from {}",path); - - // Open the mcpl file - mcpl_file = mcpl_open_file(path.c_str()); - - //do checks on the mcpl_file to see if particles are many enough. - // should model this on the example source shown in the docs. - n_sites=mcpl_hdr_nparticles(mcpl_file); - - read_source_bank(sites_); -} - -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. - } - - switch(pdg){ - case 2112: - omc_particle_.particle=ParticleType::neutron; - break; - case 22: - omc_particle_.particle=ParticleType::photon; - break; - case 11: - omc_particle_.particle=ParticleType::electron; - break; - case -11: - omc_particle_.particle=ParticleType::positron; - break; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL //============================================================================== // Non-member functions From 5462216deba46d017db7905a312f0ed458cde6a7 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 088/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- include/openmc/state_point.h | 9 +++ src/state_point.cpp | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..2d92c935a6 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,6 +9,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { void load_state_point(); @@ -21,5 +25,10 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); +#endif + } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/state_point.cpp b/src/state_point.cpp index 470dd77182..dfbd7f65c5 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,6 +28,10 @@ #include "openmc/timer.h" #include "openmc/vector.h" +#ifdef OPENMC_MCPL +#include +#endif + namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -599,6 +603,45 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } +void write_mcpl_point(const char *filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + // this must be rewritten: file_open is h5-specific + file_id = mcpl_create_outfile(filename_.c_str()); + //write_attribute(file_id, "filetype", "source"); + //write header stuff (oopy in xml-files as binary blobs for instance)) + if (VERSION_DEV){ + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + } else { + line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + } + mcpl_hdr_set_srcname(file_id,line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + //change this - this is h5 specific + mcpl_closeandgzip_outfile(file_id); + } + +} + + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -715,6 +758,88 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if(surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + //write particles from the master node + //loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { +#ifdef OPENMC_MPI + if (i>0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + //now write the source_banke data again. + for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + //particle is now at the iterator + //write it to the mcpl-file + mcpl_particle_t p; + p.position[0]=_site->r.x; + p.position[1]=_site->r.y; + p.position[2]=_site->r.z; + + p.direction[0]=_site->u.x; + p.direction[1]=_site->u.y; + p.direction[2]=_site->u.x; + + //mcpl stores kinetic energy in MeV + p.ekin=_site->E*1e-6; + + p.time=_site->time*1e3; + + p.weight=_site->wgt; + + switch(_site->particle){ + case ParticleType::neutron: + p.pdgcode=2112; + break; + case ParticleType::photon: + p.pdgcode=22; + break; + case ParticleType::electron: + p.pdgcode=11; + break; + case ParticleType::positron: + p.pdgcode=-11; + break; + } + + mcpl_add_particle(file_id,&p); + } + } + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } + +} + + // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From 1519f4f3e6acc5d91dbbb7c952589a2fe39e5537 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 089/265] add a trigger to simulation control --- src/simulation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/simulation.cpp b/src/simulation.cpp index d7292cb9d9..9c19c2106d 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -407,6 +407,13 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } + + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcplt_source_point(filename.c_str(), true); + } + + } void initialize_generation() From f76e416ee64524ef06705db4f2fa306e4be472f1 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:56:36 +0200 Subject: [PATCH 090/265] use find_package instead - MCPL now supports this --- CMakeLists.txt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32beb41ecd..24018eb0f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,13 @@ if(${HDF5_VERSION} VERSION_GREATER_EQUAL 1.12.0) list(APPEND cxxflags -DH5Oget_info_by_idx_vers=1 -DH5O_info_t_vers=1) endif() +#=============================================================================== +# MCPL +#=============================================================================== +if (OPENMC_USE_MCPL) + find_package(MCPL REQUIRED) +endif() + #=============================================================================== # Set compile/link flags based on which compiler is being used #=============================================================================== @@ -186,10 +193,6 @@ if(OPENMC_ENABLE_COVERAGE) list(APPEND ldflags --coverage) endif() -if(OPENMC_USE_MCPL) - list(APPEND ldflags -lmcpl) -endif() - # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") @@ -444,10 +447,6 @@ endif() if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() -if(OPENMC_USE_MCPL) - target_compile_definitions(libopenmc PUBLIC -DOPENMC_MCPL) -endif() - # Set git SHA1 hash as a compile definition if(GIT_FOUND) @@ -491,6 +490,11 @@ if (OPENMC_USE_MPI) target_link_libraries(libopenmc MPI::MPI_CXX) endif() +if (OPENMC_USE_MCPL) + target_compile_definitions(libopenmc PUBLIC OPENMC_MCPL) + target_link_libraries(libopenmc MCPL::mcpl) +endif() + #=============================================================================== # openmc executable #=============================================================================== From b6aa2dc429bf7545826991c426bf0843624c7377 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:02 +0200 Subject: [PATCH 091/265] add mcpl-output settings --- include/openmc/settings.h | 1 + src/settings.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235b..66825867d8 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -48,6 +48,7 @@ extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? extern bool surf_source_write; //!< write surface source file? +extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? extern bool temperature_multipole; //!< use multipole data? diff --git a/src/settings.cpp b/src/settings.cpp index 11dd96e025..d1762596c6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -61,6 +61,7 @@ bool source_latest {false}; bool source_separate {false}; bool source_write {true}; bool surf_source_write {false}; +bool surf_mcpl_write {false}; bool surf_source_read {false}; bool survival_biasing {false}; bool temperature_multipole {false}; @@ -682,9 +683,14 @@ void read_settings_xml() max_surface_particles = std::stoll(get_node_value(node_ssw, "max_particles")); } +#ifdef OPENMC_MCPL + if(check_for_node(node_ssw, "mcpl")){ + surf_mcpl_write=true; + } +#endif } - // If source is not seperate and is to be written out in the statepoint file, + // If source is not separate and is to be written out in the statepoint file, // make sure that the sourcepoint batch numbers are contained in the // statepoint list if (!source_separate) { From 22ce618601c8dbefd4759c51d50b7a17ba09dbba Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:28 +0200 Subject: [PATCH 092/265] fix typo --- src/simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 9c19c2106d..fcb115849d 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -410,7 +410,7 @@ void finalize_batch() if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ auto filename = settings::path_output + ".mcpl"; - write_mcplt_source_point(filename.c_str(), true); + write_mcpl_source_point(filename.c_str(), true); } From 76e726b527c1b738b8b5842999e69ec4d6c4c57d Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 11:00:39 +0200 Subject: [PATCH 093/265] add cond. compilation guards for MCPL functions --- src/state_point.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index dfbd7f65c5..14a5411283 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -603,7 +603,8 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } -void write_mcpl_point(const char *filename, bool surf_source_bank) +#ifdef OPENMC_MCPL +void write_mcpl_source_point(const char *filename, bool surf_source_bank) { std::string filename_; if (filename) { @@ -640,7 +641,7 @@ void write_mcpl_point(const char *filename, bool surf_source_bank) } } - +#endif void write_source_bank(hid_t group_id, bool surf_source_bank) { @@ -758,6 +759,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } +#ifdef OPENMC_MCPL void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) { int64_t dims_size = settings::n_particles; @@ -838,7 +840,7 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) } } - +#endif // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) From bdc76d95fce06f3e94dea3afa9b8305309958179 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 16 Sep 2022 16:07:22 +0200 Subject: [PATCH 094/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- src/source.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/source.cpp b/src/source.cpp index e43f03929b..cf584e12ea 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -437,6 +437,79 @@ CustomSourceWrapper::~CustomSourceWrapper() } +MCPLFileSource::~MCPLFileSource(){ + mcpl_close_file(mcpl_file); +} + +SourceSite MCPLFileSource::sample(uint64_t* seed) const +{ + size_t i_site = sites_.size() * prn(seed); + return sites_[i_site]; +} + +void MCPLFileSource::read_source_bank(vector &sites_) +{ + sites_.resize(n_sites); + for (int i=0;ipdgcode; + while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { + mcpl_particle=mcpl_read(mcpl_file); + pdg=mcpl_particle->pdgcode; + //should check for file exhaustion. This could happen if particles are other than + //neutrons, photons, electrons, or positrons. + } + + switch(pdg){ + case 2112: + omc_particle_.particle=ParticleType::neutron; + break; + case 22: + omc_particle_.particle=ParticleType::photon; + break; + case 11: + omc_particle_.particle=ParticleType::electron; + break; + case -11: + omc_particle_.particle=ParticleType::positron; + break; + } + + //particle is good, convert to openmc-formalism + omc_particle_.r.x=mcpl_particle->position[0]; + omc_particle_.r.y=mcpl_particle->position[1]; + omc_particle_.r.z=mcpl_particle->position[2]; + + omc_particle_.u.x=mcpl_particle->direction[0]; + omc_particle_.u.y=mcpl_particle->direction[1]; + omc_particle_.u.z=mcpl_particle->direction[2]; + + //mcpl stores kinetic energy in MeV + omc_particle_.E=mcpl_particle->ekin*1e6; + //mcpl stores time in ms + omc_particle_.time=mcpl_particle->time*1e-3; + omc_particle_.wgt=mcpl_particle->weight; + omc_particle_.delayed_group=0; + return omc_particle_; +} +#endif //OPENMC_MCPL +======= +>>>>>>> 380cfc150b449ef56debb5261caa010a52c42bdd +======= +#endif +>>>>>>> 71568d4f4 (refactor MCPL file read part of FileSource) +>>>>>>> 55f2be19e (refactor MCPL file read part of FileSource) + //============================================================================== // Non-member functions //============================================================================== From 6bceb7c30c05509823a7b0a6cb2e518d3a7ff5e4 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 095/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- src/source.cpp | 6 ------ src/state_point.cpp | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index cf584e12ea..99ec3a74d1 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -503,12 +503,6 @@ SourceSite MCPLFileSource::read_single_particle() const return omc_particle_; } #endif //OPENMC_MCPL -======= ->>>>>>> 380cfc150b449ef56debb5261caa010a52c42bdd -======= -#endif ->>>>>>> 71568d4f4 (refactor MCPL file read part of FileSource) ->>>>>>> 55f2be19e (refactor MCPL file read part of FileSource) //============================================================================== // Non-member functions diff --git a/src/state_point.cpp b/src/state_point.cpp index 14a5411283..1b133a1215 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -643,6 +643,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } #endif + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); From 56cddd25e2d8540da5356fbbe8f3bbde13ce11b5 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 4 Oct 2022 10:11:06 +0200 Subject: [PATCH 096/265] do not define these functions if no MCPL present --- src/state_point.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 1b133a1215..14a5411283 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -643,7 +643,6 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } #endif - void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); From 3c33ec3b9fc513794e7e683620fbce99aedb9bcb Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 9 Aug 2022 08:12:19 +0200 Subject: [PATCH 097/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- src/source.cpp | 67 -------------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 99ec3a74d1..e43f03929b 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -437,73 +437,6 @@ CustomSourceWrapper::~CustomSourceWrapper() } -MCPLFileSource::~MCPLFileSource(){ - mcpl_close_file(mcpl_file); -} - -SourceSite MCPLFileSource::sample(uint64_t* seed) const -{ - size_t i_site = sites_.size() * prn(seed); - return sites_[i_site]; -} - -void MCPLFileSource::read_source_bank(vector &sites_) -{ - sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. - } - - switch(pdg){ - case 2112: - omc_particle_.particle=ParticleType::neutron; - break; - case 22: - omc_particle_.particle=ParticleType::photon; - break; - case 11: - omc_particle_.particle=ParticleType::electron; - break; - case -11: - omc_particle_.particle=ParticleType::positron; - break; - } - - //particle is good, convert to openmc-formalism - omc_particle_.r.x=mcpl_particle->position[0]; - omc_particle_.r.y=mcpl_particle->position[1]; - omc_particle_.r.z=mcpl_particle->position[2]; - - omc_particle_.u.x=mcpl_particle->direction[0]; - omc_particle_.u.y=mcpl_particle->direction[1]; - omc_particle_.u.z=mcpl_particle->direction[2]; - - //mcpl stores kinetic energy in MeV - omc_particle_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - omc_particle_.time=mcpl_particle->time*1e-3; - omc_particle_.wgt=mcpl_particle->weight; - omc_particle_.delayed_group=0; - return omc_particle_; -} -#endif //OPENMC_MCPL - //============================================================================== // Non-member functions //============================================================================== From 52cf6823e6c1df7dbcc30b0c860412dc124a18a3 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 098/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- src/state_point.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/state_point.cpp b/src/state_point.cpp index 14a5411283..1b133a1215 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -643,6 +643,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } #endif + void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); From 9d7105ebc7ff43d973f55d59a815d62be0a8ba5b Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 6 Sep 2022 02:22:21 +0200 Subject: [PATCH 099/265] wip add MCPL-out to openmc code for writing a source_bank/point compiles but is untested --- src/state_point.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 1b133a1215..c41c3dd53d 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -603,6 +603,7 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } + #ifdef OPENMC_MCPL void write_mcpl_source_point(const char *filename, bool surf_source_bank) { @@ -641,8 +642,8 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } } -#endif +#endif void write_source_bank(hid_t group_id, bool surf_source_bank) { From cd9eb5b1b237bbd420b7c2eef93023d3e1f1e20b Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Tue, 6 Sep 2022 23:06:19 +0200 Subject: [PATCH 100/265] add a trigger to simulation control --- src/simulation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index fcb115849d..7fda9d5239 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -408,10 +408,10 @@ void finalize_batch() write_source_point(filename.c_str(), true); } - if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ - auto filename = settings::path_output + ".mcpl"; - write_mcpl_source_point(filename.c_str(), true); - } +if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcpl_source_point(filename.c_str(), true); +} } From 8b783749341798cc9c8bc71d0ab374297824f1fc Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 10:59:28 +0200 Subject: [PATCH 101/265] fix typo --- src/simulation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 7fda9d5239..fcb115849d 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -408,10 +408,10 @@ void finalize_batch() write_source_point(filename.c_str(), true); } -if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ - auto filename = settings::path_output + ".mcpl"; - write_mcpl_source_point(filename.c_str(), true); -} + if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + auto filename = settings::path_output + ".mcpl"; + write_mcpl_source_point(filename.c_str(), true); + } } From 172fec23568ae6793c2b23c1b54e02c6b01483d7 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 5 Oct 2022 11:00:39 +0200 Subject: [PATCH 102/265] add cond. compilation guards for MCPL functions --- src/state_point.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index c41c3dd53d..14a5411283 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -603,7 +603,6 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } - #ifdef OPENMC_MCPL void write_mcpl_source_point(const char *filename, bool surf_source_bank) { @@ -642,7 +641,6 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) } } - #endif void write_source_bank(hid_t group_id, bool surf_source_bank) From 9065bdac3aeafe0d411c2d4a26ef873e39cb4c40 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 16 Sep 2022 16:07:22 +0200 Subject: [PATCH 103/265] refactor MCPL file read part of FileSource A new constructor is added to FileSource that reads from a given MCPL-file. --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index e43f03929b..3e1ddb3f38 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -436,7 +436,6 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } - //============================================================================== // Non-member functions //============================================================================== From 28cd0c5d3ae44a1ab3fe11f3ced08d6589530aa6 Mon Sep 17 00:00:00 2001 From: erkn Date: Wed, 5 Oct 2022 14:31:05 +0200 Subject: [PATCH 104/265] fix misnamed variable and remove accientally committed code --- src/source.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 3e1ddb3f38..d3037386b2 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -350,10 +350,19 @@ FileSource::FileSource(mcpl_file_t mcpl_file) //neutrons or photons } - if(mcpl_particle->pdgcode==2112) { - site_.particle=ParticleType::neutron; - } else if (mcpl_particle->pdgcode==22) { - site_.particle=ParticleType::photon; + switch(pdg){ + case 2112: + site_.particle=ParticleType::neutron; + break; + case 22: + site_.particle=ParticleType::photon; + break; + case 11: + site_.particle=ParticleType::electron; + break; + case -11: + site_.particle=ParticleType::positron; + break; } //particle is good, convert to openmc-formalism From 6e523fa1f8ca0b6eef6a5e110fc7ceb86446d8f6 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 6 Oct 2022 01:33:21 +0200 Subject: [PATCH 105/265] must have a full filename --- src/simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index fcb115849d..82fce7c221 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -409,7 +409,7 @@ void finalize_batch() } if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ - auto filename = settings::path_output + ".mcpl"; + auto filename = settings::path_output + "surface_source.mcpl"; write_mcpl_source_point(filename.c_str(), true); } From 93a9270efcf0697db410c21d184b4e0d933da875 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 00:46:32 +0200 Subject: [PATCH 106/265] fix malformed format string --- src/state_point.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 14a5411283..acae2e8d01 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -626,9 +626,9 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) //write_attribute(file_id, "filetype", "source"); //write header stuff (oopy in xml-files as binary blobs for instance)) if (VERSION_DEV){ - line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}-development"); + line=fmt::format("OpenMC {0}.{1}.{2}-development",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); } else { - line=fmt::format("OpenMC {VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_RELEASE}"); + line=fmt::format("OpenMC {0}.{1}.{2}",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); } mcpl_hdr_set_srcname(file_id,line.c_str()); } From 59a949de362f5133c43971636bcb71a557ae03e0 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 00:48:44 +0200 Subject: [PATCH 107/265] fix typo causing unit length errors --- src/state_point.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index acae2e8d01..fb915484b3 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -803,9 +803,11 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) p.position[1]=_site->r.y; p.position[2]=_site->r.z; + //mcpl requires that the direction vector is unit length + //which is also the case in openmc p.direction[0]=_site->u.x; p.direction[1]=_site->u.y; - p.direction[2]=_site->u.x; + p.direction[2]=_site->u.z; //mcpl stores kinetic energy in MeV p.ekin=_site->E*1e-6; From 7759028d803d052f220abbc00a876a1c33b00949 Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 11 Oct 2022 02:00:33 +0200 Subject: [PATCH 108/265] enable mcpl surf_source_write in the python layer --- openmc/settings.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index ad5a9b28ae..0937a3e480 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -171,6 +171,7 @@ class Settings: banked (int) :max_particles: Maximum number of particles to be banked on surfaces per process (int) + :mcpl: Output in the form of an MCPL-file (bool) survival_biasing : bool Indicate whether survival biasing is to be used tabular_legendre : dict @@ -652,7 +653,7 @@ class Settings: cv.check_type('surface source writing options', surf_source_write, Mapping) for key, value in surf_source_write.items(): cv.check_value('surface source writing key', key, - ('surface_ids', 'max_particles')) + ('surface_ids', 'max_particles', 'mcpl')) if key == 'surface_ids': cv.check_type('surface ids for source banking', value, Iterable, Integral) @@ -664,6 +665,9 @@ class Settings: value, Integral) cv.check_greater_than('maximum particle banks on surfaces per process', value, 0) + elif key == 'mcpl': + cv.check_type('write to an MCPL-format file', value, bool) + self._surf_source_write = surf_source_write @confidence_intervals.setter @@ -1032,6 +1036,9 @@ class Settings: if 'max_particles' in self._surf_source_write: subelement = ET.SubElement(element, "max_particles") subelement.text = str(self._surf_source_write['max_particles']) + if 'mcpl' in self._surf_source_write: + subelement = ET.SubElement(element, "mcpl") + subelement.text = str(self._surf_source_write['mcpl']).lower() def _create_confidence_intervals(self, root): if self._confidence_intervals is not None: From e3f1bdaca1f5144563c4ceac45f10e4b0b5b7963 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 12 Oct 2022 19:30:38 +0200 Subject: [PATCH 109/265] enable mcpl from the python layer if the source filename ends with mcpl use that --- openmc/source.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openmc/source.py b/openmc/source.py index 48e14f6f71..fdb2d5bc25 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -223,7 +223,10 @@ class Source: if self.particle != 'neutron': element.set("particle", self.particle) if self.file is not None: - element.set("file", self.file) + if (self.file.endswith('.mcpl')): + element.set("mcpl", self.file) + else: + element.set("file", self.file) if self.library is not None: element.set("library", self.library) if self.parameters is not None: From ca396268b586719df5638057b9161dbb2cdb048a Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 12 Oct 2022 19:32:09 +0200 Subject: [PATCH 110/265] protect against compilation wo mcpl --- src/simulation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 82fce7c221..d90c33ffd6 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -407,12 +407,12 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } - +#ifdef OPENMC_MCPL if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ auto filename = settings::path_output + "surface_source.mcpl"; write_mcpl_source_point(filename.c_str(), true); } - +#endif } From 94fb5f8fa6818f88c9fcf9007535a80846cb6c2d Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 12 Oct 2022 19:32:40 +0200 Subject: [PATCH 111/265] enable MPI mpi-nodes are gathered sequentially (as for h5) --- src/state_point.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index fb915484b3..983147c62e 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -786,30 +786,40 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) } if (mpi::master) { - //write particles from the master node + // Particles are writeen to disk from the master node only + + // Save source bank sites since the array is overwritten below +#ifdef OPENMC_MPI + vector temp_source {source_bank->begin(), source_bank->end()}; +#endif + //loop over the other nodes and receive data - then write those. for (int i = 0; i < mpi::n_procs; ++i) { + // number of particles for node node i + size_t count[] { + static_cast((*bank_index)[i + 1] - (*bank_index)[i])}; + #ifdef OPENMC_MPI if (i>0) MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, mpi::intracomm, MPI_STATUS_IGNORE); #endif - //now write the source_banke data again. + // now write the source_bank data again. for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ - //particle is now at the iterator - //write it to the mcpl-file + // particle is now at the iterator + // write it to the mcpl-file mcpl_particle_t p; p.position[0]=_site->r.x; p.position[1]=_site->r.y; p.position[2]=_site->r.z; - //mcpl requires that the direction vector is unit length - //which is also the case in openmc + // mcpl requires that the direction vector is unit length + // which is also the case in openmc p.direction[0]=_site->u.x; p.direction[1]=_site->u.y; p.direction[2]=_site->u.z; - //mcpl stores kinetic energy in MeV + // mcpl stores kinetic energy in MeV p.ekin=_site->E*1e-6; p.time=_site->time*1e3; @@ -834,6 +844,10 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) mcpl_add_particle(file_id,&p); } } +#ifdef OPENMC_MPI + // Restore state of source bank + std::copy(temp_source.begin(), temp_source.end(), source_bank->begin()); +#endif } else { #ifdef OPENMC_MPI MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, From 81fa3b45a35595f4913845d5cc1a4580e173d0d7 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 13 Oct 2022 23:09:12 +0200 Subject: [PATCH 112/265] mcpl-file output simimlar to regular source output file --- include/openmc/settings.h | 1 + src/settings.cpp | 1 + src/simulation.cpp | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 66825867d8..5b755784e4 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -47,6 +47,7 @@ extern "C" bool run_CE; //!< run with continuous-energy data? extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? +extern bool source_mcpl_write; //!< write source in mcpl files? extern bool surf_source_write; //!< write surface source file? extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? diff --git a/src/settings.cpp b/src/settings.cpp index d1762596c6..7aed383da1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -60,6 +60,7 @@ bool run_CE {true}; bool source_latest {false}; bool source_separate {false}; bool source_write {true}; +bool source_mcpl_write {true}; bool surf_source_write {false}; bool surf_mcpl_write {false}; bool surf_source_read {false}; diff --git a/src/simulation.cpp b/src/simulation.cpp index d90c33ffd6..70053a9a89 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -399,6 +399,20 @@ void finalize_batch() auto filename = settings::path_output + "source.h5"; write_source_point(filename.c_str()); } + +#ifdef OPENMC_MCPL + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_mcpl_write && settings::source_separate) { + write_mcpl_source_point(nullptr); + } + + // Write a continously-overwritten source point if requested. + if (settings::source_latest && setting::source_mcpl_write) { + auto filename = settings::path_output + "source.mcpl"; + write_mcpl_source_point(filename.c_str()); + } +#endif + } // Write out surface source if requested. From 5da08f6b0e1aa687b242bab32facdd5f29cf0d9d Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 14 Oct 2022 01:40:52 +0200 Subject: [PATCH 113/265] fix logic to avoid writing both kinds of source file --- src/settings.cpp | 3 +++ src/simulation.cpp | 43 +++++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 7aed383da1..9098eb6205 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -654,6 +654,9 @@ void read_settings_xml() if (check_for_node(node_sp, "write")) { source_write = get_node_value_bool(node_sp, "write"); } + if (check_for_node(node_sp, "mcpl")) { + source_write = get_node_value_bool(node_sp, "mcpl"); + } if (check_for_node(node_sp, "overwrite_latest")) { source_latest = get_node_value_bool(node_sp, "overwrite_latest"); source_separate = source_latest; diff --git a/src/simulation.cpp b/src/simulation.cpp index 70053a9a89..d7cc2237d5 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -389,30 +389,33 @@ void finalize_batch() if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch - if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_write && settings::source_separate) { - write_source_point(nullptr); - } - - // Write a continously-overwritten source point if requested. - if (settings::source_latest) { - auto filename = settings::path_output + "source.h5"; - write_source_point(filename.c_str()); - } - #ifdef OPENMC_MCPL - if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_mcpl_write && settings::source_separate) { - write_mcpl_source_point(nullptr); - } + if(! settings::source_mcpl_write) { +#endif + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_write && settings::source_separate) { + write_source_point(nullptr); + } - // Write a continously-overwritten source point if requested. - if (settings::source_latest && setting::source_mcpl_write) { - auto filename = settings::path_output + "source.mcpl"; - write_mcpl_source_point(filename.c_str()); + // Write a continously-overwritten source point if requested. + if (settings::source_latest) { + auto filename = settings::path_output + "source.h5"; + write_source_point(filename.c_str()); + } +#ifdef OPENMC_MCPL + } else { + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_mcpl_write && settings::source_separate) { + write_mcpl_source_point(nullptr); + } + + // Write a continously-overwritten source point if requested. + if (settings::source_latest && settings::source_mcpl_write) { + auto filename = settings::path_output + "source.mcpl"; + write_mcpl_source_point(filename.c_str()); + } } #endif - } // Write out surface source if requested. From 344efa8de6454998dd55d00d6f213946d266cadf Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 14 Oct 2022 01:41:21 +0200 Subject: [PATCH 114/265] don't compress by default --- src/state_point.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index 983147c62e..d0fe92bdf5 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -637,7 +637,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) if (mpi::master) { //change this - this is h5 specific - mcpl_closeandgzip_outfile(file_id); + mcpl_close_outfile(file_id); } } From 04edce7dc29543fe4e8f730f9ee516ae87c61195 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 14 Oct 2022 01:41:44 +0200 Subject: [PATCH 115/265] do read and write regression test almost dientical to source_file reg. test --- .../source_mcpl_file/results_true.dat | 2 +- .../source_mcpl_file/settings.xml | 6 +- .../regression_tests/source_mcpl_file/test.py | 146 +++++++++++------- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat index f2209006a1..19460623f5 100644 --- a/tests/regression_tests/source_mcpl_file/results_true.dat +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.017557E-01 3.398770E-03 +3.039964E-01 3.654869E-04 diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml index 8b1510e0ce..47b010bffe 100644 --- a/tests/regression_tests/source_mcpl_file/settings.xml +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -1,11 +1,15 @@ + + 10 5 1000 - source.10.mcpl + + -4 -4 -4 4 4 4 + diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index f55a769047..a005fc2d83 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -1,65 +1,101 @@ -import os -import subprocess +#!/usr/bin/env python + import glob +import os -from tests.testing_harness import TestHarness +from tests.testing_harness import * -class SourceMCPLFileTestHarness(TestHarness): - def execute_test(self): - """Run OpenMC with the appropriate arguments and check the outputs.""" - try: - self._create_input() - self._test_input_created() - self._run_openmc() - self._test_output_created() - results = self._get_results() - self._write_results(results) - self._compare_results() - finally: - self._cleanup() +settings1=""" + + + + + 10 + 5 + 1000 + + + + -4 -4 -4 4 4 4 + + + +""" - def _create_input(self): - compiled=subprocess.run(['gcc','-o','gen_dummy_mcpl.out','gen_dummy_mcpl.c','-lm','-lmcpl']) - assert compiled==0, 'Could not compile mcpl-file generator code' - subprocess.run(['./gen_dummy_mcpl.out']) +settings2 = """ + + + 10 + 5 + 1000 + + + source.10.{0} + + +""" - def update_results(self): - """Update the results_true using the current version of OpenMC.""" - try: - self._create_input() - self._test_input_created() - self._run_openmc() - self._test_output_created() - results = self._get_results() - self._write_results(results) - self._overwrite_results() - finally: - self._cleanup() - def _test_input_created(self): - """Check that the input mcpl.file was generated as it should""" - mcplfile=glob.glob(os.path.join(os.getcwd(),'source.10.mcpl')) - assert len(mcplfile) == 1, 'Either multiple or no mcpl files ' \ - 'exist.' - assert mcplfile[0].endswith('mcpl'), \ - 'output file does not end with mcpl.' +class SourceFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() - def _test_output_created(self): - """Check that the output files were created""" - statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) - assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ - 'exist.' - assert statepoint[0].endswith('h5'), \ - 'statepoint file does not end with h5.' + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() - def _cleanup(self): - super()._cleanup() - source_mcpl=glob.glob(os.path.join(os.getcwd(),'source*.mcpl')) - for f in source_mcpl: - if (os.path.exists(f)): - os.remove(f) + def _test_output_created(self): + """Make sure statepoint and source files have been created.""" + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ + 'exist.' + assert statepoint[0].endswith('h5'), \ + 'Statepoint file is not a HDF5 file.' -def test_mcpl_source_file(): - harness = SourceMCPLFileTestHarness('source.10.mcpl') - harness.main() + source = glob.glob(os.path.join(os.getcwd(), 'source.10.mcpl*')) + assert len(source) == 1, 'Either multiple or no source files exist.' + assert source[0].endswith('mcpl') or source[0].endswith('mcpl.gz'), \ + 'Source file is not a MCPL file.' + + def _run_openmc_restart(self): + # Get the name of the source file. + source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) + + # Write the new settings.xml file. + with open('settings.xml','w') as fh: + fh.write(settings2.format(source[0].split('.')[-1])) + + # Run OpenMC. + self._run_openmc() + + def _cleanup(self): + TestHarness._cleanup(self) + output = glob.glob(os.path.join(os.getcwd(), 'source.*')) + #for f in output: + # if os.path.exists(f): + # os.remove(f) + with open('settings.xml','w') as fh: + fh.write(settings1) + + +def test_source_file(): + harness = SourceFileTestHarness('statepoint.10.h5') + harness.main() From 0e0cd0a22d059159a8799906f57d12cec99c1a89 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 13 Sep 2021 01:25:36 -0300 Subject: [PATCH 116/265] Added hooks for NCrystal --- CMakeLists.txt | 16 +++++++++ include/openmc/material.h | 8 +++++ include/openmc/particle_data.h | 3 ++ include/openmc/physics.h | 27 ++++++++++++++++ include/openmc/settings.h | 3 ++ openmc/material.py | 59 ++++++++++++++++++++++++++++++++++ src/material.cpp | 36 ++++++++++++++++++++- src/physics.cpp | 21 +++++++++++- src/settings.cpp | 4 +++ 9 files changed, 175 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 521b46cfc6..7be54dc3e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) +option(OPENMC_USE_NCRYSTAL "Enable support for NCrystal scattering" OFF) # Warnings for deprecated options foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") @@ -98,6 +99,14 @@ macro(find_package_write_status pkg) endif() endmacro() +#=============================================================================== +# NCrystal Scattering Support +#=============================================================================== + +if(OPENMC_USE_NCRYSTAL) + find_package(NCrystal REQUIRED PATH_SUFFIXES ./)#fixme +endif() + #=============================================================================== # DAGMC Geometry Support - need DAGMC/MOAB #=============================================================================== @@ -482,6 +491,13 @@ if (OPENMC_USE_MPI) target_link_libraries(libopenmc MPI::MPI_CXX) endif() +if(OPENMC_USE_NCRYSTAL) + target_compile_definitions(libopenmc PRIVATE NCRYSTAL) + target_link_libraries(libopenmc NCrystal::NCrystal) +endif() + + + #=============================================================================== # openmc executable #=============================================================================== diff --git a/include/openmc/material.h b/include/openmc/material.h index b251a3ca85..55f8ae20d1 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -15,6 +15,10 @@ #include "openmc/particle.h" #include "openmc/vector.h" +#ifdef NCRYSTAL +#include "NCrystal/NCrystal.hh" +#endif + namespace openmc { //============================================================================== @@ -157,6 +161,10 @@ public: std::string name_; //!< Name of material vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector +#ifdef NCRYSTAL + std::string cfg_; //!< NCrystal configuration string + std::shared_ptr m_NCrystal_mat_; +#endif xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index d3d00571a8..06ca5990a5 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -162,6 +162,9 @@ struct MacroXS { double fission; //!< macroscopic fission xs double nu_fission; //!< macroscopic production xs double photon_prod; //!< macroscopic photon production xs +#ifdef NCRYSTAL + double NCrystal_XS; //!< macroscopic cross section of processes handled by NCrystal +#endif // Photon cross sections double coherent; //!< macroscopic coherent xs diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 262b3a8841..a1db072914 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -8,6 +8,10 @@ #include "openmc/reaction.h" #include "openmc/vector.h" +#ifdef NCRYSTAL +#include "NCrystal/NCRNG.hh" +#endif + namespace openmc { //============================================================================== @@ -101,6 +105,29 @@ void sample_secondary_photons(Particle& p, int i_nuclide); //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); +#ifdef NCRYSTAL +//============================================================================== +// NCrystal wrapper class for the OpenMC random number generator +//============================================================================== + +class NcrystalRNG_Wrapper : public NCrystal::RNGStream { + uint64_t* openmc_seed_; +public: + constexpr NcrystalRNG_Wrapper(uint64_t* s) noexcept : openmc_seed_(s) {} + //Can be cheaply created on the stack just before being used in calls to + //ProcImpl::Scatter objects, like: + // + // RNG_Wrapper rng(seed); + // +protected: + //double actualGenerate() override { return prn(openmc_seed_); } + double actualGenerate() override { + return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); + } + +}; +#endif + } // namespace openmc #endif // OPENMC_PHYSICS_H diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235b..ba1a30e307 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -120,6 +120,9 @@ extern int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette +#ifdef NCRYSTAL +extern double ncrystal_max_energy; // Energy in eV to switch between NCrystal and ENDF +#endif } // namespace settings //============================================================================== diff --git a/openmc/material.py b/openmc/material.py index e99eec52c4..1fe62961d9 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -118,6 +118,7 @@ class Material(IDManagerMixin): self._volume = None self._atoms = {} self._isotropic = [] + self._NCrystal_cfg = None # A list of tuples (nuclide, percent, percent type) self._nuclides = [] @@ -140,6 +141,8 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._NCrystal_cfg) + for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -331,6 +334,48 @@ class Material(IDManagerMixin): return material + @classmethod + def from_NCrystal(cls, cfg): + """Create material from NCrystal configuration string + Density is set from the NCrystal value, + and material temperature from the configuration string. + + Parameters + ---------- + cfg : str + NCrystal configuration string + + Returns + ------- + openmc.Material + Material instance + + """ + + try: + import NCrystal + except ImportError: + raise SystemExit("ERROR: NCrystal Python module is required.") + + NC_mat = NCrystal.createInfo(cfg) + NC_comp = NC_mat.getComposition() + + # Create the Material + material = cls() + + for frac, atom in NC_comp: + if (atom.A() == 0): + material.add_element(atom.displayLabel(), frac, 'ao') + else: + material.add_nuclide(atom.displayLabel(), frac, 'ao') + + material._NCrystal_cfg = cfg + material._density_units = "g/cm3" + material._density = NC_mat.getDensity() + material.temperature = NC_mat.getTemperature() + + return material + def add_volume_information(self, volume_calc): """Add volume information to a material. @@ -405,6 +450,9 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) + if self._NCrystal_cfg is not None: + raise ValueError("Cannot add nuclides to NCrystal material") + # If nuclide name doesn't look valid, give a warning try: Z, _, _ = openmc.data.zam(nuclide) @@ -609,6 +657,9 @@ class Material(IDManagerMixin): raise ValueError("Element name should be given by the " "element's symbol or name, e.g., 'Zr', 'zirconium'") + if self._NCrystal_cfg is not None: + raise ValueError("Cannot add elements to NCrystal material") + # Allow for element identifier to be given as a symbol or name if len(element) > 2: el = element.lower() @@ -1135,6 +1186,14 @@ class Material(IDManagerMixin): if self._volume: element.set("volume", str(self._volume)) + if self._NCrystal_cfg: + if self._sab: + raise ValueError("NCrystal materials are not compatible with S(a,b).") + if self._macroscopic is not None: + raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") + + element.set("cfg", str(self._NCrystal_cfg)) + # Create temperature XML subelement if self.temperature is not None: element.set("temperature", str(self.temperature)) diff --git a/src/material.cpp b/src/material.cpp index 30dfa5ed50..d7fae83617 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -60,6 +60,14 @@ Material::Material(pugi::xml_node node) name_ = get_node_value(node, "name"); } +#ifdef NCRYSTAL + if (check_for_node(node, "cfg")) { + cfg_ = get_node_value(node, "cfg"); + write_message(5, "NCrystal config string: >>{}<< ", cfg_); + m_NCrystal_mat_ = NCrystal::FactImpl::createScatter(cfg_); + } +#endif + if (check_for_node(node, "depletable")) { depletable_ = get_node_value_bool(node, "depletable"); } @@ -792,6 +800,17 @@ void Material::calculate_neutron_xs(Particle& p) const // Initialize position in i_sab_nuclides int j = 0; +#ifdef NCRYSTAL + double ncrystal_xs = -1; + + if (m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + // Calculate scattering XS per atom with NCrystal, only once per material + NCrystal::CachePtr dummyCache; + auto nc_energy = NCrystal::NeutronEnergy{p.E()}; + ncrystal_xs = m_NCrystal_mat_->crossSection( dummyCache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + } +#endif + // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { // ====================================================================== @@ -830,10 +849,25 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide - const auto& micro {p.neutron_xs(i_nuclide)}; +#ifndef NCRYSTAL + const +#endif + auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); +#ifdef NCRYSTAL + if (ncrystal_xs >= 0.0){ + if ( micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + data::nuclides[i_nuclide]->calculate_elastic_xs(p); + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + ncrystal_xs; + micro.elastic = ncrystal_xs; + } +#endif } // ====================================================================== diff --git a/src/physics.cpp b/src/physics.cpp index bcdcf7ecf9..9eff4aed7e 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -140,8 +140,27 @@ void sample_neutron_reaction(Particle& p) // Sample a scattering reaction and determine the secondary energy of the // exiting neutron - scatter(p, i_nuclide); +#ifdef NCRYSTAL + if (model::materials[p.material_]->m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + + NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG + //create a cache pointer for multi thread physics + NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) + auto nc_energy = NCrystal::NeutronEnergy{p.E()}; + auto outcome = model::materials[p.material_]->m_NCrystal_mat_->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + p.E_last() = p.E(); + p.E() = outcome.ekin.get(); + Direction u_old {p.u()}; + p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.mu_ = u_old.dot(p.u()); + p.event_mt_ = ELASTIC;//fixme: define a new label for NCrystal? + } else { + scatter(p, i_nuclide); + } +#else + scatter(p, i_nuclide); +#endif // Advance URR seed stream 'N' times after energy changes if (p.E() != p.E_last()) { p.stream() = STREAM_URR_PTABLE; diff --git a/src/settings.cpp b/src/settings.cpp index eb0d4936c7..3f3e970541 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -118,6 +118,10 @@ int verbosity {7}; double weight_cutoff {0.25}; double weight_survive {1.0}; +#ifdef NCRYSTAL +double ncrystal_max_energy{5.0}; +#endif + } // namespace settings //============================================================================== From 0f33e0b11c6add029f354eb3e5c89b002eec7500 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 13 Sep 2022 14:22:04 +0200 Subject: [PATCH 117/265] Add temporary requirement of natural elements in NCrystal materials --- openmc/material.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 1fe62961d9..0a293eb0f8 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -364,10 +364,9 @@ class Material(IDManagerMixin): material = cls() for frac, atom in NC_comp: - if (atom.A() == 0): - material.add_element(atom.displayLabel(), frac, 'ao') - else: - material.add_nuclide(atom.displayLabel(), frac, 'ao') + if not atom.isNaturalElement(): + raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') + material.add_element(atom.elementName(), frac, 'ao') material._NCrystal_cfg = cfg material._density_units = "g/cm3" From 9c54ac0b6b53336f834b5f868aa85e3e140239f8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 13 Sep 2022 16:07:50 +0200 Subject: [PATCH 118/265] Fix access to private attributes --- include/openmc/material.h | 6 ++++++ src/physics.cpp | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index 55f8ae20d1..eef557088c 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -155,6 +155,12 @@ public: //! \return Temperature in [K] double temperature() const; +#ifdef NCRYSTAL + //! Gwet pointer to NCrystal material object + //! \return Pointer to NCrystal material object + std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_}; +#endif + //---------------------------------------------------------------------------- // Data int32_t id_ {C_NONE}; //!< Unique ID diff --git a/src/physics.cpp b/src/physics.cpp index 9eff4aed7e..948307521e 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,19 +142,18 @@ void sample_neutron_reaction(Particle& p) // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material_]->m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ - + if (model::materials[p.material()]->m_NCrystal_mat() != nullptr && p.E() < settings::ncrystal_max_energy){ NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG //create a cache pointer for multi thread physics NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material_]->m_NCrystal_mat_->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto outcome = model::materials[p.material()]->m_NCrystal_mat()->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); - p.mu_ = u_old.dot(p.u()); - p.event_mt_ = ELASTIC;//fixme: define a new label for NCrystal? + p.mu() = u_old.dot(p.u()); + p.event_mt() = ELASTIC;//fixme: define a new label for NCrystal? } else { scatter(p, i_nuclide); } From 70d5c76ebd70cf43349423f17028c3cb1dfe1daa Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 13 Sep 2022 16:10:05 +0200 Subject: [PATCH 119/265] Fixed material.h --- include/openmc/material.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index eef557088c..10e64773e3 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -158,7 +158,7 @@ public: #ifdef NCRYSTAL //! Gwet pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_}; + std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_; }; #endif //---------------------------------------------------------------------------- From 29a10eafd3db601ffa9a7e7bf882431c150ee1b2 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 16 Sep 2022 09:03:09 -0300 Subject: [PATCH 120/265] Removed MacroNCrystalXS This was left from a previous implementation. Now it is not needed because NCrystal just takes over all neutron scattering events below a given energy threshold. --- include/openmc/particle_data.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index 06ca5990a5..d3d00571a8 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -162,9 +162,6 @@ struct MacroXS { double fission; //!< macroscopic fission xs double nu_fission; //!< macroscopic production xs double photon_prod; //!< macroscopic photon production xs -#ifdef NCRYSTAL - double NCrystal_XS; //!< macroscopic cross section of processes handled by NCrystal -#endif // Photon cross sections double coherent; //!< macroscopic coherent xs From 2e19260dea9176f03f2d5d80be19c8c819d339d3 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 11:19:54 +0200 Subject: [PATCH 121/265] Implement recommended changes --- CMakeLists.txt | 2 +- include/openmc/material.h | 8 ++++---- include/openmc/physics.h | 10 ++-------- openmc/material.py | 31 ++++++++++++++----------------- src/material.cpp | 16 +++++++++------- src/physics.cpp | 10 +++++----- 6 files changed, 35 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7be54dc3e4..7080accfd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,7 @@ endmacro() #=============================================================================== if(OPENMC_USE_NCRYSTAL) - find_package(NCrystal REQUIRED PATH_SUFFIXES ./)#fixme + find_package(NCrystal REQUIRED) endif() #=============================================================================== diff --git a/include/openmc/material.h b/include/openmc/material.h index 10e64773e3..30d8ffb7d0 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -156,9 +156,9 @@ public: double temperature() const; #ifdef NCRYSTAL - //! Gwet pointer to NCrystal material object + //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr m_NCrystal_mat() const {return m_NCrystal_mat_; }; + std::shared_ptr ncrystal_mat() const {return ncrystal_mat_; }; #endif //---------------------------------------------------------------------------- @@ -168,8 +168,8 @@ public: vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector #ifdef NCRYSTAL - std::string cfg_; //!< NCrystal configuration string - std::shared_ptr m_NCrystal_mat_; + std::string ncrystal_cfg_; //!< NCrystal configuration string + std::shared_ptr ncrystal_mat_; #endif xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] diff --git a/include/openmc/physics.h b/include/openmc/physics.h index a1db072914..109f7caffb 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -110,17 +110,11 @@ void split_particle(Particle& p); // NCrystal wrapper class for the OpenMC random number generator //============================================================================== -class NcrystalRNG_Wrapper : public NCrystal::RNGStream { +class NCrystalRNGWrapper : public NCrystal::RNGStream { uint64_t* openmc_seed_; public: - constexpr NcrystalRNG_Wrapper(uint64_t* s) noexcept : openmc_seed_(s) {} - //Can be cheaply created on the stack just before being used in calls to - //ProcImpl::Scatter objects, like: - // - // RNG_Wrapper rng(seed); - // + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} protected: - //double actualGenerate() override { return prn(openmc_seed_); } double actualGenerate() override { return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); } diff --git a/openmc/material.py b/openmc/material.py index 0a293eb0f8..15d1f8ac9a 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -118,7 +118,7 @@ class Material(IDManagerMixin): self._volume = None self._atoms = {} self._isotropic = [] - self._NCrystal_cfg = None + self._ncrystal_cfg = None # A list of tuples (nuclide, percent, percent type) self._nuclides = [] @@ -141,7 +141,7 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._NCrystal_cfg) + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -335,7 +335,7 @@ class Material(IDManagerMixin): return material @classmethod - def from_NCrystal(cls, cfg): + def from_ncrystal(cls, cfg): """Create material from NCrystal configuration string Density is set from the NCrystal value, and material temperature from the configuration string. @@ -352,26 +352,23 @@ class Material(IDManagerMixin): """ - try: - import NCrystal - except ImportError: - raise SystemExit("ERROR: NCrystal Python module is required.") + import NCrystal - NC_mat = NCrystal.createInfo(cfg) - NC_comp = NC_mat.getComposition() + nc_mat = NCrystal.createInfo(cfg) + nc_comp = nc_mat.getComposition() # Create the Material material = cls() - for frac, atom in NC_comp: + for frac, atom in nc_comp: if not atom.isNaturalElement(): raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') material.add_element(atom.elementName(), frac, 'ao') - material._NCrystal_cfg = cfg + material._ncrystal_cfg = cfg material._density_units = "g/cm3" - material._density = NC_mat.getDensity() - material.temperature = NC_mat.getTemperature() + material._density = nc_mat.getDensity() + material.temperature = nc_mat.getTemperature() return material @@ -449,7 +446,7 @@ class Material(IDManagerMixin): 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) - if self._NCrystal_cfg is not None: + if self._ncrystal_cfg is not None: raise ValueError("Cannot add nuclides to NCrystal material") # If nuclide name doesn't look valid, give a warning @@ -656,7 +653,7 @@ class Material(IDManagerMixin): raise ValueError("Element name should be given by the " "element's symbol or name, e.g., 'Zr', 'zirconium'") - if self._NCrystal_cfg is not None: + if self._ncrystal_cfg is not None: raise ValueError("Cannot add elements to NCrystal material") # Allow for element identifier to be given as a symbol or name @@ -1185,13 +1182,13 @@ class Material(IDManagerMixin): if self._volume: element.set("volume", str(self._volume)) - if self._NCrystal_cfg: + if self._ncrystal_cfg: if self._sab: raise ValueError("NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") - element.set("cfg", str(self._NCrystal_cfg)) + element.set("cfg", str(self._ncrystal_cfg)) # Create temperature XML subelement if self.temperature is not None: diff --git a/src/material.cpp b/src/material.cpp index d7fae83617..13acbba1f1 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -63,8 +63,8 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string: >>{}<< ", cfg_); - m_NCrystal_mat_ = NCrystal::FactImpl::createScatter(cfg_); + write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); + ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif @@ -803,11 +803,11 @@ void Material::calculate_neutron_xs(Particle& p) const #ifdef NCRYSTAL double ncrystal_xs = -1; - if (m_NCrystal_mat_ != nullptr && p.E() < settings::ncrystal_max_energy){ + if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy){ // Calculate scattering XS per atom with NCrystal, only once per material - NCrystal::CachePtr dummyCache; + NCrystal::CachePtr dummy_cache; auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - ncrystal_xs = m_NCrystal_mat_->crossSection( dummyCache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + ncrystal_xs = ncrystal_mat_->crossSection( dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); } #endif @@ -849,8 +849,10 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide -#ifndef NCRYSTAL - const +#ifdef NCRYSTAL + auto& micro {p.neutron_xs(i_nuclide)}; +#else + const auto& micro {p.neutron_xs(i_nuclide)}; #endif auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || diff --git a/src/physics.cpp b/src/physics.cpp index 948307521e..bad4997727 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,18 +142,18 @@ void sample_neutron_reaction(Particle& p) // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material()]->m_NCrystal_mat() != nullptr && p.E() < settings::ncrystal_max_energy){ - NcrystalRNG_Wrapper rng(p.current_seed()); // Initialize RNG + if (model::materials[p.material()]->ncrystal_mat() && p.E() < settings::ncrystal_max_energy){ + NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG //create a cache pointer for multi thread physics - NCrystal::CachePtr dummyCache;//fixme: avoid recreating here (triggers malloc) + NCrystal::CachePtr dummy_cache; auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material()]->m_NCrystal_mat()->sampleScatter( dummyCache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto outcome = model::materials[p.material()]->ncrystal_mat()->sampleScatter( dummy_cache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); p.mu() = u_old.dot(p.u()); - p.event_mt() = ELASTIC;//fixme: define a new label for NCrystal? + p.event_mt() = ELASTIC; } else { scatter(p, i_nuclide); } From 5afcc9e49b60ec1c5e2290d4725bac7a96e1692e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 11:30:39 +0200 Subject: [PATCH 122/265] Apply clang-format --- include/openmc/material.h | 13 ++++++++----- include/openmc/physics.h | 17 ++++++++++------- include/openmc/settings.h | 3 ++- src/material.cpp | 33 ++++++++++++++++++--------------- src/physics.cpp | 18 +++++++++++------- src/settings.cpp | 2 +- 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index 30d8ffb7d0..47e3e6a167 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -158,15 +158,18 @@ public: #ifdef NCRYSTAL //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr ncrystal_mat() const {return ncrystal_mat_; }; + std::shared_ptr ncrystal_mat() const + { + return ncrystal_mat_; + }; #endif //---------------------------------------------------------------------------- // Data - int32_t id_ {C_NONE}; //!< Unique ID - std::string name_; //!< Name of material - vector nuclide_; //!< Indices in nuclides vector - vector element_; //!< Indices in elements vector + int32_t id_ {C_NONE}; //!< Unique ID + std::string name_; //!< Name of material + vector nuclide_; //!< Indices in nuclides vector + vector element_; //!< Indices in elements vector #ifdef NCRYSTAL std::string ncrystal_cfg_; //!< NCrystal configuration string std::shared_ptr ncrystal_mat_; diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 109f7caffb..23995e7af9 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -111,14 +111,17 @@ void split_particle(Particle& p); //============================================================================== class NCrystalRNGWrapper : public NCrystal::RNGStream { - uint64_t* openmc_seed_; -public: - constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} -protected: - double actualGenerate() override { - return std::max( std::numeric_limits::min(), prn(openmc_seed_) ); - } + uint64_t* openmc_seed_; +public: + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} + +protected: + double actualGenerate() override + { + return std::max( + std::numeric_limits::min(), prn(openmc_seed_)); + } }; #endif diff --git a/include/openmc/settings.h b/include/openmc/settings.h index ba1a30e307..9bb447005c 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -121,7 +121,8 @@ extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette #ifdef NCRYSTAL -extern double ncrystal_max_energy; // Energy in eV to switch between NCrystal and ENDF +extern double + ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF #endif } // namespace settings diff --git a/src/material.cpp b/src/material.cpp index 13acbba1f1..e894084e29 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -64,7 +64,7 @@ Material::Material(pugi::xml_node node) if (check_for_node(node, "cfg")) { cfg_ = get_node_value(node, "cfg"); write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); - ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); + ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif @@ -375,8 +375,8 @@ void Material::finalize() this->init_thermal(); } -// Normalize density -this->normalize_density(); + // Normalize density + this->normalize_density(); } void Material::normalize_density() @@ -803,11 +803,14 @@ void Material::calculate_neutron_xs(Particle& p) const #ifdef NCRYSTAL double ncrystal_xs = -1; - if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy){ + if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { // Calculate scattering XS per atom with NCrystal, only once per material NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - ncrystal_xs = ncrystal_mat_->crossSection( dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}).get(); + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + ncrystal_xs = + ncrystal_mat_ + ->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) + .get(); } #endif @@ -859,15 +862,15 @@ void Material::calculate_neutron_xs(Particle& p) const i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); #ifdef NCRYSTAL - if (ncrystal_xs >= 0.0){ - if ( micro.thermal > 0 || micro.thermal_elastic > 0) { - fatal_error("S(a,b) treatment and NCrystal are not compatible."); - } - data::nuclides[i_nuclide]->calculate_elastic_xs(p); - // remove free atom cross section - // and replace it by scattering cross section per atom from NCrystal - micro.total = micro.total - micro.elastic + ncrystal_xs; - micro.elastic = ncrystal_xs; + if (ncrystal_xs >= 0.0) { + if (micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + data::nuclides[i_nuclide]->calculate_elastic_xs(p); + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + ncrystal_xs; + micro.elastic = ncrystal_xs; } #endif } diff --git a/src/physics.cpp b/src/physics.cpp index bad4997727..e19640314e 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -138,20 +138,24 @@ void sample_neutron_reaction(Particle& p) if (!p.alive()) return; - // Sample a scattering reaction and determine the secondary energy of the - // exiting neutron + // Sample a scattering reaction and determine the secondary energy of the + // exiting neutron #ifdef NCRYSTAL - if (model::materials[p.material()]->ncrystal_mat() && p.E() < settings::ncrystal_max_energy){ + if (model::materials[p.material()]->ncrystal_mat() && + p.E() < settings::ncrystal_max_energy) { NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG - //create a cache pointer for multi thread physics + // create a cache pointer for multi thread physics NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy{p.E()}; - auto outcome = model::materials[p.material()]->ncrystal_mat()->sampleScatter( dummy_cache, rng , nc_energy, {p.u().x, p.u().y, p.u().z}); + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + auto outcome = + model::materials[p.material()]->ncrystal_mat()->sampleScatter( + dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); p.E_last() = p.E(); p.E() = outcome.ekin.get(); Direction u_old {p.u()}; - p.u() = Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.u() = Direction( + outcome.direction[0], outcome.direction[1], outcome.direction[2]); p.mu() = u_old.dot(p.u()); p.event_mt() = ELASTIC; } else { diff --git a/src/settings.cpp b/src/settings.cpp index 3f3e970541..87c24eedd3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -119,7 +119,7 @@ double weight_cutoff {0.25}; double weight_survive {1.0}; #ifdef NCRYSTAL -double ncrystal_max_energy{5.0}; +double ncrystal_max_energy {5.0}; #endif } // namespace settings From fdf763e801b1439da96009fd5dadeadeb93e36f7 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Mon, 26 Sep 2022 12:35:12 +0200 Subject: [PATCH 123/265] Couple of fixes --- src/material.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index e894084e29..9531e9f43a 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -62,7 +62,7 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { - cfg_ = get_node_value(node, "cfg"); + ncrystal_cfg_ = get_node_value(node, "cfg"); write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } @@ -857,7 +857,6 @@ void Material::calculate_neutron_xs(Particle& p) const #else const auto& micro {p.neutron_xs(i_nuclide)}; #endif - auto& micro {p.neutron_xs(i_nuclide)}; if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); From 52ee2c37ced8fce859d2ec6a06c6909dd0d04b44 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 11:36:22 +0200 Subject: [PATCH 124/265] Print where NCrystal was found as suggested by @paulromano Co-authored-by: Paul Romano --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7080accfd0..2815230d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ endmacro() if(OPENMC_USE_NCRYSTAL) find_package(NCrystal REQUIRED) + message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") endif() #=============================================================================== From a5b811ec899470f9b0bb3e387f0f672ae8490321 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 13:25:05 +0200 Subject: [PATCH 125/265] Use new NCrystal getFlattenedComposition method to better support more complicated materials --- openmc/material.py | 63 +++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 15d1f8ac9a..de4a22cb8e 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -335,15 +335,23 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg): - """Create material from NCrystal configuration string - Density is set from the NCrystal value, - and material temperature from the configuration string. - + def from_ncrystal(cls, cfg, material_id=None, name=''): + """Create material from NCrystal configuration string. Density, + temperature, and material composition, and (ultimately) thermal neutron + scattering, will be automatically be provided by NCrystal based on this + string. The name and material_id parameters are simply passed on to the + Material constructor. + Parameters ---------- cfg : str NCrystal configuration string + material_id : int, optional + Unique identifier for the material. If not specified, an identifier will + automatically be assigned. + name : str, optional + Name of the material. If not specified, the name will be the empty + string. Returns ------- @@ -353,22 +361,43 @@ class Material(IDManagerMixin): """ import NCrystal - nc_mat = NCrystal.createInfo(cfg) - nc_comp = nc_mat.getComposition() + + def openmc_natabund( Z ): + #nc_mat.getFlattenedComposition might need natural abundancies. + #This call-back function is used so NCrystal can flatten composition + #using OpenMC's natural abundancies. In practice this function will + #only get invoked in the unlikely case where a material is specified + #by referring both to natural elements and specific isotopes of the + #same element. + elem_name = openmc.data.ATOMIC_SYMBOL.get( Z, None ) + if not elem_name: + raise ValueError( f'Element with Z={Z} is not known' ) + l = [] + for iso_name,abund in openmc.data.isotopes( elem_name ): + l.append( ( int(iso_name[ len(elem_name) : ]), abund ) ) + return l + + flat_compos = nc_mat.getFlattenedComposition( preferNaturalElements = True, + naturalAbundProvider = openmc_natabund ) # Create the Material - material = cls() + material = cls( material_id = material_id, + name = name, + temperature = nc_mat.getTemperature() ) - for frac, atom in nc_comp: - if not atom.isNaturalElement(): - raise ValueError('NCrystal-OpenMC interface only works with natural elements for now.') - material.add_element(atom.elementName(), frac, 'ao') + for Z, A_vals in flat_compos: + elemname = openmc.data.ATOMIC_SYMBOL.get(Z,None) + if not elemname: + raise ValueError(f'Element with Z={Z} is not known') + for A, frac in A_vals: + if A: + material.add_nuclide( elemname + str(A), frac, 'ao' ) + else: + material.add_element( elemname, frac, 'ao' ) - material._ncrystal_cfg = cfg - material._density_units = "g/cm3" - material._density = nc_mat.getDensity() - material.temperature = nc_mat.getTemperature() + material.set_density( 'g/cm3', nc_mat.getDensity() ) + material._ncrystal_cfg = NCrystal.normaliseCfg( cfg ) return material @@ -1186,7 +1215,7 @@ class Material(IDManagerMixin): if self._sab: raise ValueError("NCrystal materials are not compatible with S(a,b).") if self._macroscopic is not None: - raise ValueError("NCrystal materials are not compatible macroscopic cross sections.") + raise ValueError("NCrystal materials are not compatible with macroscopic cross sections.") element.set("cfg", str(self._ncrystal_cfg)) From f2e807e942ed421cd03fc87823c579e47dfcce3c Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Thu, 20 Oct 2022 13:26:45 +0200 Subject: [PATCH 126/265] Move NCrystalRNGWrapper data member into explicit private section as requested --- include/openmc/physics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 23995e7af9..806297c4ea 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -111,8 +111,6 @@ void split_particle(Particle& p); //============================================================================== class NCrystalRNGWrapper : public NCrystal::RNGStream { - uint64_t* openmc_seed_; - public: constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} @@ -122,6 +120,8 @@ protected: return std::max( std::numeric_limits::min(), prn(openmc_seed_)); } +private: + uint64_t* openmc_seed_; }; #endif From aca4fcb42bbfb45bd3ded0f9f1bbf8b7107aa632 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Fri, 21 Oct 2022 10:30:05 +0200 Subject: [PATCH 127/265] NCrystal cfgstr printout now mentions material ID --- src/material.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/material.cpp b/src/material.cpp index 9531e9f43a..32f5f2e865 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -63,7 +63,7 @@ Material::Material(pugi::xml_node node) #ifdef NCRYSTAL if (check_for_node(node, "cfg")) { ncrystal_cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string: '{}'", ncrystal_cfg_); + write_message(5, "NCrystal config string for material #{}: '{}'", this->id(), ncrystal_cfg_); ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); } #endif From a3dd55d8bc4b28323b73e9a36c52f950db6ea1fb Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Fri, 21 Oct 2022 15:04:57 +0200 Subject: [PATCH 128/265] first stab at gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100755 tools/ci/gha-install-ncrystal.sh diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh new file mode 100755 index 0000000000..c18087f235 --- /dev/null +++ b/tools/ci/gha-install-ncrystal.sh @@ -0,0 +1,62 @@ +#!/bin/bash +set -ex +cd $HOME + +#Use the NCrystal develop branch (in the near future we can move this to master): +git clone https://github.com/mctools/ncrystal --branch develop --single-branch --depth 1 ncrystal_src + +SRC_DIR="$PWD/ncrystal_src" +BLD_DIR="$PWD/ncrystal_bld" +INST_DIR="$PWD/ncrystal_inst" +PYTHON=$(which python3) + +CPU_COUNT=1 + +mkdir "$BLD_DIR" +cd ncrystal_bld + +#cmake -Dstatic=on .. && make 2>/dev/null && sudo make install + +cmake \ + "${SRC_DIR}" \ + -DBUILD_SHARED_LIBS=ON \ + -DNCRYSTAL_NOTOUCH_CMAKE_BUILD_TYPE=ON \ + -DMODIFY_RPATH=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_EXAMPLES=OFF \ + -DINSTALL_SETUPSH=OFF \ + -DEMBED_DATA=ON \ + -DINSTALL_DATA=OFF \ + -DNO_DIRECT_PYMODINST=ON \ + -DPython3_EXECUTABLE="$PYTHON" + +make -j${CPU_COUNT:-1} +make install + +#Note: There is no "make test" or "make ctest" functionality for NCrystal +# yet. If it appears in the future, we should add it here. + + +#The next stuff is not pretty, but it is needed as a temporary +#workaround until NCrystal add better support for system-wide +#installations in CMake: + +cat < ./findncrystallib.py +import pathlib +libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() +libs = list(pathlib.Path("/usr").glob("**/lib*/**/%s"%libname)) +assert len(libs)==1 +pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) +EOF + +python3 ./findncrystallib.py + +TMPNCRYSTALLIBLOCATION=$(cat ./ncrystal_liblocation.txt) + +cat < ./ncrystal_pypkg/NCrystal/_nclibpath.py +#File autogenerated for working with systemwide install of NCrystal: +import pathlib +liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) +EOF + +$PYTHON -m pip install ./ncrystal_pypkg/ -vv From e64bf6a7ffb1b33f732e804bf10ff376d2cba700 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 10 Nov 2022 12:31:45 -0300 Subject: [PATCH 129/265] Add installation instructions for NCrystal --- docs/source/usersguide/install.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 74be9ba0bc..9e5ba1ffd9 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -271,6 +271,16 @@ Prerequisites cmake -DOPENMC_USE_DAGMC=on -DCMAKE_PREFIX_PATH=/path/to/dagmc/installation .. + * NCrystal_ library for defining materials with enhanced thermal neutron transport + + Adding this option allows the creation of materials from NCrystal, which + replaces the scattering kernel treatment of ACE files with a modular, on-the-fly approach. + To use it `install `_ and + `initialize `_ + NCrystal and turn on the option in the CMake configuration step: + + cmake -DOPENMC_USE_NCRYSTAL=on .. + * libMesh_ mesh library framework for numerical simulations of partial differential equations This optional dependency enables support for unstructured mesh tally @@ -294,6 +304,7 @@ Prerequisites .. _MOAB: https://bitbucket.org/fathomteam/moab .. _libMesh: https://libmesh.github.io/ .. _libpng: http://www.libpng.org/pub/png/libpng.html +.. _NCrystal: https://github.com/mctools/ncrystal Obtaining the Source -------------------- @@ -362,6 +373,12 @@ OPENMC_USE_DAGMC should also be defined as `DAGMC_ROOT` in the CMake configuration command. (Default: off) +OPENMC_USE_NCRYSTAL + Turns on support for NCrystal materials. NCrystal must be + `installed `_ and + `initialized `_. + (Default: off) + OPENMC_USE_LIBMESH Enables the use of unstructured mesh tallies with libMesh_. (Default: off) From 1f3c5ed6bc4b29259d300a0d9b0fac0decf0eff6 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 10 Nov 2022 13:04:47 -0300 Subject: [PATCH 130/265] Add use instructions and examples for NCrystal --- docs/source/usersguide/materials.rst | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 8b43f1a2ac..ab5a8c5afa 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -101,6 +101,42 @@ you would need to add hydrogen and oxygen to a material and then assign the .. _usersguide_naming: +------------------ +Adding NCrystal materials +------------------ + +Additional support for thermal scattering can be added by using NCrystal_. +The :meth:`Material.from_ncrystal` class method generates a material object from +an `NCrystal configuration string `_. +Temperature, material composition and density are passed from the configuration string +and the `NCMAT file `_ +that define the material, e.g: + +:: + + mat = openmc.Material.from_ncrystal('Al_sg225.ncmat') + +defines a material containing polycrystalline alumnium, + +:: + + mat = openmc.Material.from_ncrystal(""""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; + dir1=@crys_hkl:5,1,1@lab:0,0,1; + dir2=@crys_hkl:0,-1,1@lab:0,1,0""") + +defines an oriented germanium single crystal with 40 arcsec mosaicity. + +NCrystal only handles low energy neutron interactions. Other interaction are +provided by standard ACE files. NCrystal_ comes with a `predefined library +https://github.com/mctools/ncrystal/wiki/Data-library`_ but more materials can +be added by creating NCMAT files or on-the-fly in the configuration string. + +.. warning:: Currently, NCrystal_ materials cannot be modified after created. + Density, temperature and composition should be defined in the + configuration string or the NCMAT file. + +.. _NCrystal: https://github.com/mctools/ncrystal + ------------------ Naming Conventions ------------------ @@ -222,3 +258,4 @@ been generated, you can tell OpenMC to use this file either by setting materials.cross_sections = '/path/to/cross_sections.xml' .. _MCNP: https://mcnp.lanl.gov/ + From e224adf4313056e8dfb8700126d7329511de0721 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 16 Nov 2022 10:31:15 +0100 Subject: [PATCH 131/265] follow source code conventions Co-authored-by: Paul Romano --- include/openmc/state_point.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 2d92c935a6..698ed2c93a 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -26,7 +26,7 @@ void restart_set_keff(); void write_unstructured_mesh_results(); #ifdef OPENMC_MCPL -void write_mcpl_source_point(const char *filename_, bool surf_source_bank = false); +void write_mcpl_source_point(const char* filename, bool surf_source_bank = false); void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); #endif From b2320ae1d477d7f3d932a406eb2989eee5cf37e3 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 16 Nov 2022 13:42:25 +0100 Subject: [PATCH 132/265] not needed since mcpl-file is created by openmc --- .../source_mcpl_file/gen_dummy_mcpl.c | 98 ------------------- 1 file changed, 98 deletions(-) delete mode 100644 tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c diff --git a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c b/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c deleted file mode 100644 index 61eea95c6f..0000000000 --- a/tests/regression_tests/source_mcpl_file/gen_dummy_mcpl.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include -#include - -const int batches=10; -const int particles=10000; - -double rand01(){ - double r=rand()/((double) RAND_MAX); - return r; -} - - -int main(int argc, char **argv){ - char outfilename[128]; - snprintf(outfilename,127,"source.%d.mcpl",batches); - - /*generate an mcpl_header_of sorts*/ - mcpl_outfile_t outfile=mcpl_create_outfile(outfilename); - mcpl_particle_t *particle, Particle; - - char line[256]; - snprintf(line,255,"gen_dummy_mcpl.c"); - mcpl_hdr_set_srcname(outfile,line); - mcpl_enable_universal_pdgcode(outfile,2112);/*all particles are neutrons*/ - snprintf(line,255,"Dummy MCPL-file for testing OpenMC mcpl input"); - mcpl_hdr_add_comment(outfile,line); - - /*also add the instrument file and the command line as blobs*/ - FILE *fp; - if( (fp=fopen("gen_dummy_mcpl.c","rb"))!=NULL){ - unsigned char *buffer; - int size,status; - /*find the file size by seeking to end, "tell" the position, and then go back again*/ - fseek(fp, 0L, SEEK_END); - size = ftell(fp); // get current file pointer - fseek(fp, 0L, SEEK_SET); // seek back to beginning of file - if ( size && (buffer=malloc(size))!=NULL){ - if (size!=(fread(buffer,1,size,fp))){ - fprintf(stderr,"Warning: c source generator file not read cleanly\n"); - } - mcpl_hdr_add_data(outfile, "mcpl_point_source_file_generator", size, buffer); - free(buffer); - } - fclose(fp); - } else { - fprintf(stderr,"Warning: could not open c source generator file, hence not embedded.\n"); - } - - - /*the main particle loop*/ - particle=&Particle; - int i; - srand(1234); - for (i=0;iposition[0]=0; - particle->position[1]=0; - particle->position[2]=0; - - /*generate a random direction on unit sphere*/ - double nrm=2.0; - double vx,vy,vz; - int iter=0; - do { - if(iter>100){ - printf("Warning: Exceeded max random vector attempts: at loop %d -> %g %d\n",i,nrm,iter); - } - vx=rand01()*2.0-1.0; - vy=rand01()*2.0-1.0; - vz=rand01()*2.0-1.0; - nrm=(vx*vx + vy*vy + vz*vz); - iter++; - } while (nrm>1.0); - vx/=sqrt(nrm); - vy/=sqrt(nrm); - vz/=sqrt(nrm); - particle->direction[0]=vx; - particle->direction[1]=vy; - particle->direction[2]=vz; - - /*generate the kinetic energy (in MeV) of the particle*/ - particle->ekin=1e-3; - - /*set time=0*/ - particle->time=0; - /*set weight*/ - particle->weight=1; - - particle->userflags=0; - mcpl_add_particle(outfile,particle); - } - mcpl_close_outfile(outfile); -} From 3e60d51f904ca413030716aa1882866bebab8c23 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 16 Nov 2022 13:54:08 +0100 Subject: [PATCH 133/265] from_xml mcpl-settings functionality --- openmc/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 0937a3e480..b2c184f780 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1341,13 +1341,15 @@ class Settings: def _surf_source_write_from_xml_element(self, root): elem = root.find('surf_source_write') if elem is not None: - for key in ('surface_ids', 'max_particles'): + for key in ('surface_ids', 'max_particles','mcpl'): value = get_text(elem, key) if value is not None: if key == 'surface_ids': value = [int(x) for x in value.split()] elif key in ('max_particles'): value = int(value) + elif key == 'mcpl': + value = True self.surf_source_write[key] = value def _confidence_intervals_from_xml_element(self, root): From cf96ddf8bbedac4d72e1730e313d5b7a9cd94284 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Thu, 17 Nov 2022 13:47:03 +0100 Subject: [PATCH 134/265] Basic introduction to NCrystal Included references to the main papers and documentation --- docs/source/methods/cross_sections.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 3396d7f251..c700b3f2d3 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -178,6 +178,24 @@ been selected. There are three methods available: section data is loaded for a single temperature and is used in the unresolved resonance and fast energy ranges. +------------------ +NCrystal materials +------------------ + +As an alternative of the standard thermal scattering treatment using :math:`S(\alpha,\beta)` +tables, OpenMC allows to create materials using NCrystal_. In addition to the regular thermal elastic, +and thermal inelastic processes, NCrystal allows the generation of models for materials that +cannot currently included in ACE files such as oriented single crystals (see the `NCrystal paper`_), +and further extend the physics `using plugins`_. Thermal scattering kernels are generated on +the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted +from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ +that does not require previous processing. A `large library` of materials is already included in +the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format` +or `combining existing files`. + +The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions +except for thermal neutron scattering are handled by continuous energy ACE libraries. + ---------------- Multi-Group Data ---------------- @@ -279,3 +297,10 @@ or even isotropic scattering. .. _ENDF/B data: https://www.nndc.bnl.gov/endf-b8.0/ .. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019 .. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package +.. _NCrystal: https://github.com/mctools/ncrystal +.. _NCrystal paper: https://doi.org/10.1016/j.cpc.2019.07.015 +.. _using plugins: https://doi.org/10.1016/j.cpc.2021.108082 +.. _rejection algorithm: https://doi.org/10.1016/j.jcp.2018.11.043 +.. _large library: https://github.com/mctools/ncrystal/wiki/Data-library +.. _NCMAT format: https://github.com/mctools/ncrystal/wiki/NCMAT-format +.. _combining existing files: https://github.com/mctools/ncrystal/wiki/Announcement-Release3.0.0#2-multiphase-materials From 24572a71f22b588eb628e855618a4a4a27a63e4c Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Thu, 17 Nov 2022 16:33:48 +0100 Subject: [PATCH 135/265] Added simple NCrystal test --- tests/regression_tests/ncrystal/__init__.py | 0 .../regression_tests/ncrystal/inputs_true.dat | 45 +++++ .../ncrystal/results_true.dat | 181 ++++++++++++++++++ tests/regression_tests/ncrystal/test.py | 70 +++++++ 4 files changed, 296 insertions(+) create mode 100644 tests/regression_tests/ncrystal/__init__.py create mode 100644 tests/regression_tests/ncrystal/inputs_true.dat create mode 100644 tests/regression_tests/ncrystal/results_true.dat create mode 100644 tests/regression_tests/ncrystal/test.py diff --git a/tests/regression_tests/ncrystal/__init__.py b/tests/regression_tests/ncrystal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/ncrystal/inputs_true.dat b/tests/regression_tests/ncrystal/inputs_true.dat new file mode 100644 index 0000000000..7a3f20eb3f --- /dev/null +++ b/tests/regression_tests/ncrystal/inputs_true.dat @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + fixed source + 100000 + 10 + + + 0 0 -20 + + + + 0.012 1.0 + + + + + + + 1 + + + 0.0 0.017453292519943295 0.03490658503988659 0.05235987755982989 0.06981317007977318 0.08726646259971647 0.10471975511965978 0.12217304763960307 0.13962634015954636 0.15707963267948966 0.17453292519943295 0.19198621771937624 0.20943951023931956 0.22689280275926285 0.24434609527920614 0.2617993877991494 0.2792526803190927 0.29670597283903605 0.3141592653589793 0.33161255787892263 0.3490658503988659 0.3665191429188092 0.3839724354387525 0.4014257279586958 0.4188790204786391 0.4363323129985824 0.4537856055185257 0.47123889803846897 0.4886921905584123 0.5061454830783556 0.5235987755982988 0.5410520681182421 0.5585053606381855 0.5759586531581288 0.5934119456780721 0.6108652381980153 0.6283185307179586 0.6457718232379019 0.6632251157578453 0.6806784082777885 0.6981317007977318 0.7155849933176751 0.7330382858376184 0.7504915783575618 0.767944870877505 0.7853981633974483 0.8028514559173916 0.8203047484373349 0.8377580409572782 0.8552113334772214 0.8726646259971648 0.8901179185171081 0.9075712110370514 0.9250245035569946 0.9424777960769379 0.9599310885968813 0.9773843811168246 0.9948376736367679 1.0122909661567112 1.0297442586766545 1.0471975511965976 1.064650843716541 1.0821041362364843 1.0995574287564276 1.117010721276371 1.1344640137963142 1.1519173063162575 1.1693705988362009 1.1868238913561442 1.2042771838760873 1.2217304763960306 1.239183768915974 1.2566370614359172 1.2740903539558606 1.2915436464758039 1.3089969389957472 1.3264502315156905 1.3439035240356338 1.361356816555577 1.3788101090755203 1.3962634015954636 1.413716694115407 1.4311699866353502 1.4486232791552935 1.4660765716752369 1.4835298641951802 1.5009831567151235 1.5184364492350666 1.53588974175501 1.5533430342749532 1.5707963267948966 1.5882496193148399 1.6057029118347832 1.6231562043547265 1.6406094968746698 1.6580627893946132 1.6755160819145565 1.6929693744344996 1.710422666954443 1.7278759594743862 1.7453292519943295 1.7627825445142729 1.7802358370342162 1.7976891295541595 1.8151424220741028 1.8325957145940461 1.8500490071139892 1.8675022996339325 1.8849555921538759 1.9024088846738192 1.9198621771937625 1.9373154697137058 1.9547687622336491 1.9722220547535925 1.9896753472735358 2.007128639793479 2.0245819323134224 2.0420352248333655 2.059488517353309 2.076941809873252 2.0943951023931953 2.111848394913139 2.129301687433082 2.1467549799530254 2.1642082724729685 2.181661564992912 2.199114857512855 2.2165681500327987 2.234021442552742 2.251474735072685 2.2689280275926285 2.2863813201125716 2.303834612632515 2.321287905152458 2.3387411976724017 2.356194490192345 2.3736477827122884 2.3911010752322315 2.4085543677521746 2.426007660272118 2.443460952792061 2.4609142453120048 2.478367537831948 2.4958208303518914 2.5132741228718345 2.530727415391778 2.548180707911721 2.5656340004316642 2.5830872929516078 2.600540585471551 2.6179938779914944 2.6354471705114375 2.652900463031381 2.670353755551324 2.6878070480712677 2.705260340591211 2.722713633111154 2.7401669256310974 2.7576202181510405 2.775073510670984 2.792526803190927 2.8099800957108707 2.827433388230814 2.8448866807507573 2.8623399732707004 2.8797932657906435 2.897246558310587 2.91469985083053 2.9321531433504737 2.949606435870417 2.9670597283903604 2.9845130209103035 3.001966313430247 3.01941960595019 3.036872898470133 3.0543261909900767 3.07177948351002 3.0892327760299634 3.1066860685499065 3.12413936106985 3.141592653589793 + + + 1 + + + 1 2 3 + current + + diff --git a/tests/regression_tests/ncrystal/results_true.dat b/tests/regression_tests/ncrystal/results_true.dat new file mode 100644 index 0000000000..3ef0306fdf --- /dev/null +++ b/tests/regression_tests/ncrystal/results_true.dat @@ -0,0 +1,181 @@ + surface polar low [rad] polar high [rad] cellfrom nuclide score mean std. dev. +0 1 0.00e+00 1.75e-02 1 total current 9.82e-01 1.43e-04 +1 1 1.75e-02 3.49e-02 1 total current 0.00e+00 0.00e+00 +2 1 3.49e-02 5.24e-02 1 total current 1.00e-06 1.00e-06 +3 1 5.24e-02 6.98e-02 1 total current 0.00e+00 0.00e+00 +4 1 6.98e-02 8.73e-02 1 total current 0.00e+00 0.00e+00 +5 1 8.73e-02 1.05e-01 1 total current 1.00e-06 1.00e-06 +6 1 1.05e-01 1.22e-01 1 total current 0.00e+00 0.00e+00 +7 1 1.22e-01 1.40e-01 1 total current 1.00e-06 1.00e-06 +8 1 1.40e-01 1.57e-01 1 total current 1.00e-06 1.00e-06 +9 1 1.57e-01 1.75e-01 1 total current 1.00e-06 1.00e-06 +10 1 1.75e-01 1.92e-01 1 total current 0.00e+00 0.00e+00 +11 1 1.92e-01 2.09e-01 1 total current 2.00e-06 1.33e-06 +12 1 2.09e-01 2.27e-01 1 total current 0.00e+00 0.00e+00 +13 1 2.27e-01 2.44e-01 1 total current 1.00e-06 1.00e-06 +14 1 2.44e-01 2.62e-01 1 total current 1.00e-06 1.00e-06 +15 1 2.62e-01 2.79e-01 1 total current 2.00e-06 1.33e-06 +16 1 2.79e-01 2.97e-01 1 total current 0.00e+00 0.00e+00 +17 1 2.97e-01 3.14e-01 1 total current 2.00e-06 2.00e-06 +18 1 3.14e-01 3.32e-01 1 total current 1.00e-06 1.00e-06 +19 1 3.32e-01 3.49e-01 1 total current 1.00e-06 1.00e-06 +20 1 3.49e-01 3.67e-01 1 total current 2.00e-06 1.33e-06 +21 1 3.67e-01 3.84e-01 1 total current 0.00e+00 0.00e+00 +22 1 3.84e-01 4.01e-01 1 total current 2.00e-06 1.33e-06 +23 1 4.01e-01 4.19e-01 1 total current 2.00e-06 1.33e-06 +24 1 4.19e-01 4.36e-01 1 total current 1.00e-06 1.00e-06 +25 1 4.36e-01 4.54e-01 1 total current 2.00e-06 2.00e-06 +26 1 4.54e-01 4.71e-01 1 total current 5.00e-06 2.24e-06 +27 1 4.71e-01 4.89e-01 1 total current 4.00e-06 1.63e-06 +28 1 4.89e-01 5.06e-01 1 total current 3.00e-06 1.53e-06 +29 1 5.06e-01 5.24e-01 1 total current 3.00e-06 1.53e-06 +30 1 5.24e-01 5.41e-01 1 total current 3.00e-06 1.53e-06 +31 1 5.41e-01 5.59e-01 1 total current 7.00e-06 2.13e-06 +32 1 5.59e-01 5.76e-01 1 total current 3.00e-06 1.53e-06 +33 1 5.76e-01 5.93e-01 1 total current 3.00e-06 1.53e-06 +34 1 5.93e-01 6.11e-01 1 total current 2.00e-06 1.33e-06 +35 1 6.11e-01 6.28e-01 1 total current 2.00e-06 1.33e-06 +36 1 6.28e-01 6.46e-01 1 total current 3.00e-06 2.13e-06 +37 1 6.46e-01 6.63e-01 1 total current 3.00e-06 1.53e-06 +38 1 6.63e-01 6.81e-01 1 total current 2.00e-06 1.33e-06 +39 1 6.81e-01 6.98e-01 1 total current 3.00e-06 1.53e-06 +40 1 6.98e-01 7.16e-01 1 total current 1.00e-06 1.00e-06 +41 1 7.16e-01 7.33e-01 1 total current 6.00e-06 2.67e-06 +42 1 7.33e-01 7.50e-01 1 total current 6.00e-06 2.21e-06 +43 1 7.50e-01 7.68e-01 1 total current 7.00e-06 3.35e-06 +44 1 7.68e-01 7.85e-01 1 total current 6.00e-06 2.67e-06 +45 1 7.85e-01 8.03e-01 1 total current 6.00e-06 2.21e-06 +46 1 8.03e-01 8.20e-01 1 total current 7.00e-06 2.13e-06 +47 1 8.20e-01 8.38e-01 1 total current 5.00e-06 2.24e-06 +48 1 8.38e-01 8.55e-01 1 total current 3.00e-06 1.53e-06 +49 1 8.55e-01 8.73e-01 1 total current 8.00e-06 3.27e-06 +50 1 8.73e-01 8.90e-01 1 total current 7.00e-06 2.13e-06 +51 1 8.90e-01 9.08e-01 1 total current 6.00e-06 2.21e-06 +52 1 9.08e-01 9.25e-01 1 total current 4.00e-06 2.21e-06 +53 1 9.25e-01 9.42e-01 1 total current 1.20e-05 3.27e-06 +54 1 9.42e-01 9.60e-01 1 total current 8.00e-06 3.89e-06 +55 1 9.60e-01 9.77e-01 1 total current 1.20e-05 4.42e-06 +56 1 9.77e-01 9.95e-01 1 total current 7.00e-06 3.67e-06 +57 1 9.95e-01 1.01e+00 1 total current 1.20e-05 3.89e-06 +58 1 1.01e+00 1.03e+00 1 total current 9.00e-06 2.33e-06 +59 1 1.03e+00 1.05e+00 1 total current 6.00e-06 2.67e-06 +60 1 1.05e+00 1.06e+00 1 total current 7.00e-06 2.13e-06 +61 1 1.06e+00 1.08e+00 1 total current 5.00e-06 2.24e-06 +62 1 1.08e+00 1.10e+00 1 total current 1.20e-05 2.91e-06 +63 1 1.10e+00 1.12e+00 1 total current 1.30e-05 3.35e-06 +64 1 1.12e+00 1.13e+00 1 total current 9.00e-06 3.14e-06 +65 1 1.13e+00 1.15e+00 1 total current 1.20e-05 3.89e-06 +66 1 1.15e+00 1.17e+00 1 total current 6.00e-06 2.21e-06 +67 1 1.17e+00 1.19e+00 1 total current 5.11e-03 4.20e-05 +68 1 1.19e+00 1.20e+00 1 total current 8.00e-06 3.27e-06 +69 1 1.20e+00 1.22e+00 1 total current 1.30e-05 4.48e-06 +70 1 1.22e+00 1.24e+00 1 total current 1.20e-05 3.89e-06 +71 1 1.24e+00 1.26e+00 1 total current 1.50e-05 4.28e-06 +72 1 1.26e+00 1.27e+00 1 total current 7.00e-06 3.00e-06 +73 1 1.27e+00 1.29e+00 1 total current 1.60e-05 3.71e-06 +74 1 1.29e+00 1.31e+00 1 total current 9.00e-06 3.79e-06 +75 1 1.31e+00 1.33e+00 1 total current 1.30e-05 2.60e-06 +76 1 1.33e+00 1.34e+00 1 total current 1.60e-05 3.06e-06 +77 1 1.34e+00 1.36e+00 1 total current 1.40e-05 2.67e-06 +78 1 1.36e+00 1.38e+00 1 total current 1.40e-05 6.86e-06 +79 1 1.38e+00 1.40e+00 1 total current 1.50e-05 4.28e-06 +80 1 1.40e+00 1.41e+00 1 total current 3.26e-03 6.93e-05 +81 1 1.41e+00 1.43e+00 1 total current 1.40e-05 3.71e-06 +82 1 1.43e+00 1.45e+00 1 total current 1.30e-05 3.00e-06 +83 1 1.45e+00 1.47e+00 1 total current 1.30e-05 3.67e-06 +84 1 1.47e+00 1.48e+00 1 total current 1.70e-05 5.59e-06 +85 1 1.48e+00 1.50e+00 1 total current 1.70e-05 3.67e-06 +86 1 1.50e+00 1.52e+00 1 total current 1.40e-05 3.40e-06 +87 1 1.52e+00 1.54e+00 1 total current 2.50e-05 4.53e-06 +88 1 1.54e+00 1.55e+00 1 total current 1.30e-05 5.39e-06 +89 1 1.55e+00 1.57e+00 1 total current 1.80e-05 4.67e-06 +90 1 1.57e+00 1.59e+00 1 total current 1.40e-05 4.52e-06 +91 1 1.59e+00 1.61e+00 1 total current 1.70e-05 4.23e-06 +92 1 1.61e+00 1.62e+00 1 total current 1.40e-05 3.71e-06 +93 1 1.62e+00 1.64e+00 1 total current 1.00e-05 2.11e-06 +94 1 1.64e+00 1.66e+00 1 total current 2.00e-05 4.22e-06 +95 1 1.66e+00 1.68e+00 1 total current 2.30e-05 5.59e-06 +96 1 1.68e+00 1.69e+00 1 total current 1.70e-05 6.51e-06 +97 1 1.69e+00 1.71e+00 1 total current 1.30e-05 3.00e-06 +98 1 1.71e+00 1.73e+00 1 total current 1.50e-05 4.01e-06 +99 1 1.73e+00 1.75e+00 1 total current 1.70e-05 3.96e-06 +100 1 1.75e+00 1.76e+00 1 total current 1.80e-05 5.12e-06 +101 1 1.76e+00 1.78e+00 1 total current 2.50e-05 6.54e-06 +102 1 1.78e+00 1.80e+00 1 total current 1.80e-05 3.59e-06 +103 1 1.80e+00 1.82e+00 1 total current 1.50e-05 4.01e-06 +104 1 1.82e+00 1.83e+00 1 total current 1.10e-05 4.07e-06 +105 1 1.83e+00 1.85e+00 1 total current 1.50e-05 4.01e-06 +106 1 1.85e+00 1.87e+00 1 total current 1.90e-05 4.82e-06 +107 1 1.87e+00 1.88e+00 1 total current 2.20e-05 3.89e-06 +108 1 1.88e+00 1.90e+00 1 total current 2.10e-05 3.79e-06 +109 1 1.90e+00 1.92e+00 1 total current 1.50e-05 3.42e-06 +110 1 1.92e+00 1.94e+00 1 total current 2.20e-05 5.12e-06 +111 1 1.94e+00 1.95e+00 1 total current 2.10e-05 5.86e-06 +112 1 1.95e+00 1.97e+00 1 total current 2.60e-05 4.27e-06 +113 1 1.97e+00 1.99e+00 1 total current 2.20e-05 4.16e-06 +114 1 1.99e+00 2.01e+00 1 total current 2.40e-05 5.42e-06 +115 1 2.01e+00 2.02e+00 1 total current 1.60e-05 4.52e-06 +116 1 2.02e+00 2.04e+00 1 total current 1.30e-05 3.35e-06 +117 1 2.04e+00 2.06e+00 1 total current 1.90e-05 4.07e-06 +118 1 2.06e+00 2.08e+00 1 total current 1.30e-05 3.00e-06 +119 1 2.08e+00 2.09e+00 1 total current 1.40e-05 4.00e-06 +120 1 2.09e+00 2.11e+00 1 total current 3.10e-05 5.47e-06 +121 1 2.11e+00 2.13e+00 1 total current 2.30e-05 5.39e-06 +122 1 2.13e+00 2.15e+00 1 total current 2.20e-05 4.67e-06 +123 1 2.15e+00 2.16e+00 1 total current 1.70e-05 4.48e-06 +124 1 2.16e+00 2.18e+00 1 total current 1.60e-05 4.00e-06 +125 1 2.18e+00 2.20e+00 1 total current 1.80e-05 4.16e-06 +126 1 2.20e+00 2.22e+00 1 total current 1.80e-05 4.42e-06 +127 1 2.22e+00 2.23e+00 1 total current 1.80e-05 5.54e-06 +128 1 2.23e+00 2.25e+00 1 total current 1.90e-05 4.33e-06 +129 1 2.25e+00 2.27e+00 1 total current 1.00e-05 3.33e-06 +130 1 2.27e+00 2.29e+00 1 total current 1.80e-05 4.42e-06 +131 1 2.29e+00 2.30e+00 1 total current 4.15e-03 5.40e-05 +132 1 2.30e+00 2.32e+00 1 total current 1.90e-05 2.33e-06 +133 1 2.32e+00 2.34e+00 1 total current 2.30e-05 3.96e-06 +134 1 2.34e+00 2.36e+00 1 total current 1.90e-05 3.48e-06 +135 1 2.36e+00 2.37e+00 1 total current 1.60e-05 3.71e-06 +136 1 2.37e+00 2.39e+00 1 total current 1.60e-05 4.27e-06 +137 1 2.39e+00 2.41e+00 1 total current 2.30e-05 4.48e-06 +138 1 2.41e+00 2.43e+00 1 total current 2.10e-05 3.48e-06 +139 1 2.43e+00 2.44e+00 1 total current 1.60e-05 2.21e-06 +140 1 2.44e+00 2.46e+00 1 total current 9.00e-06 2.77e-06 +141 1 2.46e+00 2.48e+00 1 total current 1.30e-05 3.00e-06 +142 1 2.48e+00 2.50e+00 1 total current 2.70e-05 3.67e-06 +143 1 2.50e+00 2.51e+00 1 total current 1.90e-05 4.07e-06 +144 1 2.51e+00 2.53e+00 1 total current 1.20e-05 4.16e-06 +145 1 2.53e+00 2.55e+00 1 total current 1.30e-05 2.13e-06 +146 1 2.55e+00 2.57e+00 1 total current 1.10e-05 2.33e-06 +147 1 2.57e+00 2.58e+00 1 total current 1.50e-05 3.07e-06 +148 1 2.58e+00 2.60e+00 1 total current 1.20e-05 2.49e-06 +149 1 2.60e+00 2.62e+00 1 total current 1.80e-05 5.54e-06 +150 1 2.62e+00 2.64e+00 1 total current 1.30e-05 3.67e-06 +151 1 2.64e+00 2.65e+00 1 total current 1.60e-05 3.40e-06 +152 1 2.65e+00 2.67e+00 1 total current 7.00e-06 3.35e-06 +153 1 2.67e+00 2.69e+00 1 total current 1.00e-05 2.98e-06 +154 1 2.69e+00 2.71e+00 1 total current 7.00e-06 3.35e-06 +155 1 2.71e+00 2.72e+00 1 total current 1.20e-05 2.91e-06 +156 1 2.72e+00 2.74e+00 1 total current 9.00e-06 2.33e-06 +157 1 2.74e+00 2.76e+00 1 total current 1.00e-05 3.33e-06 +158 1 2.76e+00 2.78e+00 1 total current 1.10e-05 3.14e-06 +159 1 2.78e+00 2.79e+00 1 total current 1.00e-05 3.33e-06 +160 1 2.79e+00 2.81e+00 1 total current 1.40e-05 4.76e-06 +161 1 2.81e+00 2.83e+00 1 total current 8.00e-06 2.91e-06 +162 1 2.83e+00 2.84e+00 1 total current 5.00e-06 2.69e-06 +163 1 2.84e+00 2.86e+00 1 total current 6.00e-06 2.21e-06 +164 1 2.86e+00 2.88e+00 1 total current 5.00e-06 1.67e-06 +165 1 2.88e+00 2.90e+00 1 total current 4.00e-06 2.21e-06 +166 1 2.90e+00 2.91e+00 1 total current 7.00e-06 2.13e-06 +167 1 2.91e+00 2.93e+00 1 total current 6.00e-06 2.67e-06 +168 1 2.93e+00 2.95e+00 1 total current 7.00e-06 2.13e-06 +169 1 2.95e+00 2.97e+00 1 total current 5.00e-06 1.67e-06 +170 1 2.97e+00 2.98e+00 1 total current 3.00e-06 1.53e-06 +171 1 2.98e+00 3.00e+00 1 total current 6.00e-06 2.21e-06 +172 1 3.00e+00 3.02e+00 1 total current 3.00e-06 1.53e-06 +173 1 3.02e+00 3.04e+00 1 total current 1.00e-05 2.98e-06 +174 1 3.04e+00 3.05e+00 1 total current 2.00e-06 1.33e-06 +175 1 3.05e+00 3.07e+00 1 total current 1.00e-06 1.00e-06 +176 1 3.07e+00 3.09e+00 1 total current 2.00e-06 1.33e-06 +177 1 3.09e+00 3.11e+00 1 total current 0.00e+00 0.00e+00 +178 1 3.11e+00 3.12e+00 1 total current 1.00e-06 1.00e-06 +179 1 3.12e+00 3.14e+00 1 total current 0.00e+00 0.00e+00 \ No newline at end of file diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py new file mode 100644 index 0000000000..51a0f07b72 --- /dev/null +++ b/tests/regression_tests/ncrystal/test.py @@ -0,0 +1,70 @@ +import numpy as np +import openmc +from tests.testing_harness import PyAPITestHarness + +def compute_angular_distribution(cfg, E0, N): + """Return a openmc.model.Model() object for a monoenergetic pencil + beam hitting a 1 mm sphere filled with the material defined by + the cfg string, and compute the angular distribution""" + + # Material definition + + m1 = openmc.Material.from_ncrystal(cfg) + materials = openmc.Materials([m1]) + + # Geometry definition + + sample_sphere = openmc.Sphere(r=0.1) + outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") + cell1 = openmc.Cell(region= -sample_sphere, fill=m1) + cell2_region= +sample_sphere&-outer_sphere + cell2 = openmc.Cell(region= cell2_region, fill=None) + uni1 = openmc.Universe(cells=[cell1, cell2]) + geometry = openmc.Geometry(uni1) + + # Source definition + + source = openmc.Source() + source.space = openmc.stats.Point(xyz = (0,0,-20)) + source.angle = openmc.stats.Monodirectional(reference_uvw = (0,0,1)) + source.energy = openmc.stats.Discrete([E0], [1.0]) + + # Execution settings + + settings = openmc.Settings() + settings.source = source + settings.run_mode = "fixed source" + settings.batches = 10 + settings.particles = N + + # Tally definition + + tally1 = openmc.Tally(name="angular distribution") + tally1.scores = ["current"] + filter1 = openmc.filter.SurfaceFilter(sample_sphere) + filter2 = openmc.filter.PolarFilter(np.linspace(0,np.pi,180+1)) + filter3 = openmc.filter.CellFromFilter(cell1) + tally1.filters = [filter1, filter2, filter3] + tallies = openmc.Tallies([tally1]) + + return openmc.model.Model(geometry, materials, settings, tallies) + + +class NCrystalTest(PyAPITestHarness): + def _get_results(self): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + tal = sp.get_tally(name='angular distribution') + df = tal.get_pandas_dataframe() + return df.to_string() + +def test_ncrystal(): + NParticles = 100000 + T = 293.6 # K + E0 = 0.012 # eV + cfg = 'Al_sg225.ncmat' + test = compute_angular_distribution(cfg, E0, NParticles) + harness = NCrystalTest('statepoint.10.h5', model=test) + harness.main() From 54745f507641489bc82acbfb4303a728c7bf4fca Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:11:37 -0300 Subject: [PATCH 136/265] Changed example NCrystal cfg strings --- docs/source/usersguide/materials.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index ab5a8c5afa..1886a772be 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -114,13 +114,13 @@ that define the material, e.g: :: - mat = openmc.Material.from_ncrystal('Al_sg225.ncmat') + mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') defines a material containing polycrystalline alumnium, :: - mat = openmc.Material.from_ncrystal(""""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; + mat = openmc.Material.from_ncrystal("""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; dir1=@crys_hkl:5,1,1@lab:0,0,1; dir2=@crys_hkl:0,-1,1@lab:0,1,0""") From 91f939a391032db72220e4be715b888d0f8db287 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 18:26:31 +0100 Subject: [PATCH 137/265] Add flag for NCrystal --- include/openmc/settings.h | 2 ++ openmc/lib/__init__.py | 3 +++ src/settings.cpp | 7 +++++++ tests/regression_tests/ncrystal/test.py | 7 +++++++ 4 files changed, 19 insertions(+) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 9bb447005c..b01c75d45c 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -20,6 +20,8 @@ namespace openmc { // Global variable declarations //============================================================================== +extern "C" const bool NCRYSTAL_ENABLED; + namespace settings { // Boolean flags diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c28..55e2ec11b3 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -42,6 +42,9 @@ else: def _dagmc_enabled(): return c_bool.in_dll(_dll, "DAGMC_ENABLED").value +def _ncrystal_enabled(): + return c_bool.in_dll(_dll, "NCRYSTAL_ENABLED").value + def _coord_levels(): return c_int.in_dll(_dll, "n_coord_levels").value diff --git a/src/settings.cpp b/src/settings.cpp index 87c24eedd3..be7228d681 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -36,6 +36,13 @@ namespace openmc { // Global variables //============================================================================== +#ifdef NCRYSTAL +const bool NCRYSTAL_ENABLED = true; +#else +const bool NCRYSTAL_ENABLED = false; +#endif + + namespace settings { // Default values for boolean flags diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 51a0f07b72..0a47d634df 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -1,7 +1,14 @@ import numpy as np import openmc +import openmc.lib +import pytest + from tests.testing_harness import PyAPITestHarness +pytestmark = pytest.mark.skipif( + not openmc.lib._ncrystal_enabled(), + reason="NCrystal materials are not enabled.") + def compute_angular_distribution(cfg, E0, N): """Return a openmc.model.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by From 5f8e64b463aa2e0509d2b9955dc7de6686d5497b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:47:21 -0300 Subject: [PATCH 138/265] Add NCrystal vairables to ci.yml --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e267c0d8e1..321f188744 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: mpi: [n, y] omp: [n, y] dagmc: [n] + ncrystal: [n] libmesh: [n] event: [n] vectfit: [n] @@ -47,6 +48,10 @@ jobs: python-version: '3.10' mpi: y omp: y + - ncrystal: y + python-version: '3.10' + mpi: n + omp: n - libmesh: y python-version: '3.10' mpi: y @@ -64,7 +69,7 @@ jobs: omp: n mpi: y name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }}, - mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, + mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, ncrystal=${{ matrix.ncrystal }}, libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }} vectfit=${{ matrix.vectfit }})" @@ -73,6 +78,7 @@ jobs: PHDF5: ${{ matrix.mpi }} OMP: ${{ matrix.omp }} DAGMC: ${{ matrix.dagmc }} + NCRYSTAL: ${{ matrix.ncrystal }} EVENT: ${{ matrix.event }} VECTFIT: ${{ matrix.vectfit }} LIBMESH: ${{ matrix.libmesh }} From 87ed26babee2c7b69c229529e146d335a90fd952 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:48:19 -0300 Subject: [PATCH 139/265] Add option to install NCrystal --- tools/ci/gha-install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index aa40eb90b1..e5390be1e1 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -17,6 +17,11 @@ if [[ $DAGMC = 'y' ]]; then ./tools/ci/gha-install-dagmc.sh fi +# Install NCrystal if needed +if [[ $NCRYSTAL = 'y' ]]; then + ./tools/ci/gha-install-ncrystal.sh +fi + # Install vectfit for WMP generation if needed if [[ $VECTFIT = 'y' ]]; then ./tools/ci/gha-install-vectfit.sh From 8cf9dbe320baf4ff98eb57979f6e249e69ee0424 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 22:28:04 +0100 Subject: [PATCH 140/265] Fixed in NCrystal docs --- docs/source/methods/cross_sections.rst | 4 ++-- docs/source/usersguide/materials.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index c700b3f2d3..64e4fac3de 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -190,8 +190,8 @@ and further extend the physics `using plugins`_. Thermal scattering kernels are the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ that does not require previous processing. A `large library` of materials is already included in -the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format` -or `combining existing files`. +the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ +or `combining existing files`_. The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions except for thermal neutron scattering are handled by continuous energy ACE libraries. diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 1886a772be..1eaae846c3 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -127,8 +127,8 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. NCrystal only handles low energy neutron interactions. Other interaction are -provided by standard ACE files. NCrystal_ comes with a `predefined library -https://github.com/mctools/ncrystal/wiki/Data-library`_ but more materials can +provided by standard ACE files. NCrystal_ comes with a `predefined library +`_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. .. warning:: Currently, NCrystal_ materials cannot be modified after created. From b9e8f1324e72dc4730185b5f1f350eea62ab6757 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Fri, 18 Nov 2022 22:40:46 +0100 Subject: [PATCH 141/265] Attempt to fix NCrystal installation script --- tools/ci/gha-install-ncrystal.sh | 4 ++++ tools/ci/gha-install.py | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index c18087f235..5f577b8da9 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -28,6 +28,7 @@ cmake \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ + -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" make -j${CPU_COUNT:-1} @@ -60,3 +61,6 @@ liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv + +setup ${INST_DIR}/setup.sh + diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 4c69ba70b0..f3a29273f6 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -19,7 +19,7 @@ def which(program): return None -def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrystal=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') @@ -54,6 +54,9 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) + if ncrystal: + cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') + # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') @@ -70,10 +73,11 @@ def main(): mpi = (os.environ.get('MPI') == 'y') phdf5 = (os.environ.get('PHDF5') == 'y') dagmc = (os.environ.get('DAGMC') == 'y') + ncrystal = (os.environ.get('NCRYSTAL') == 'y') libmesh = (os.environ.get('LIBMESH') == 'y') # Build and install - install(omp, mpi, phdf5, dagmc, libmesh) + install(omp, mpi, phdf5, dagmc, libmesh, ncrystal) if __name__ == '__main__': main() From 6442de68348eefe5f26ba9a0c74fd082067e95fd Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Fri, 18 Nov 2022 23:26:24 +0100 Subject: [PATCH 142/265] remove unneccessary initialization Co-authored-by: Paul Romano --- src/source.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index 1c46e4939c..67a4a511da 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -381,7 +381,6 @@ FileSource::FileSource(mcpl_file_t mcpl_file) //mcpl stores time in ms site_.time=mcpl_particle->time*1e-3; site_.wgt=mcpl_particle->weight; - site_.delayed_group=0; sites_[i]=site_; } mcpl_close_file(mcpl_file); From a299dcc56b4d79e73a996ed3c47baaa2729fd795 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 16 Nov 2022 14:02:01 +0100 Subject: [PATCH 143/265] add a documentation entry to surf_source_write --- docs/source/io_formats/settings.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 1a8f63716f..285dfa2290 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -767,6 +767,15 @@ certain surfaces and write out the source bank in a separate file called *Default*: None + :mcpl: + An optional boolean which indicates if the banked particle should + be written to a file in the MCPL-format (documented in mcpl_). + instead of the native hdf5-based format. + + *Default*: false + + .. _mcpl: https://mctools.github.io/mcpl/mcpl.pdf + ------------------------------ ```` Element ------------------------------ From a40cc14d8ebb08da619b2d9fcaa9fc58adca1efa Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Sat, 19 Nov 2022 00:33:04 +0100 Subject: [PATCH 144/265] check in the c++-layer if it's an mcpl-file --- src/settings.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 9098eb6205..0ca430de62 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -431,12 +431,13 @@ void read_settings_xml() for (pugi::xml_node node : root.children("source")) { if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); - model::external_sources.push_back(make_unique(path)); #ifdef OPENMC_MCPL - } else if (check_for_node(node, "mcpl")) { - auto path = get_node_value(node, "mcpl", false, true); - model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); + if ( (path.size() >= 5 && path.compare(path.size()-5,5,".mcpl")==0 ) || + (path.size() >= 8 && path.compare(path.size()-8,8,".mcpl.gz")==0 ) ) { + model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); + } else #endif + model::external_sources.push_back(make_unique(path)); } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); From 8bfb1af81e874a6e9b55c59a12e816f071592ab0 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Sat, 19 Nov 2022 01:41:02 +0100 Subject: [PATCH 145/265] remove unneccessary in-code comments --- src/state_point.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/state_point.cpp b/src/state_point.cpp index d0fe92bdf5..3ee5f13b39 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -621,10 +621,7 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) std::string line; if (mpi::master) { - // this must be rewritten: file_open is h5-specific file_id = mcpl_create_outfile(filename_.c_str()); - //write_attribute(file_id, "filetype", "source"); - //write header stuff (oopy in xml-files as binary blobs for instance)) if (VERSION_DEV){ line=fmt::format("OpenMC {0}.{1}.{2}-development",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); } else { From 727161f95ab8364a04499de060e25fcdc28d474a Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Sat, 19 Nov 2022 01:44:47 +0100 Subject: [PATCH 146/265] remove some in-code comments --- src/source.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 1c46e4939c..2e6335aad2 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -332,8 +332,6 @@ FileSource::FileSource(std::string path) #ifdef OPENMC_MCPL FileSource::FileSource(mcpl_file_t mcpl_file) { - //do checks on the mcpl_file to see if particles are many enough. - // should model this on the example source shown in the docs. size_t n_sites=mcpl_hdr_nparticles(mcpl_file); sites_.resize(n_sites); @@ -348,8 +346,6 @@ FileSource::FileSource(mcpl_file_t mcpl_file) while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { mcpl_particle=mcpl_read(mcpl_file); pdg=mcpl_particle->pdgcode; - //should check for file exhaustion. This could happen if particles are other than - //neutrons, photons, electrons, or positrons. } switch(pdg){ From ccbef891ef7d2585b21b7bd5dd0e76d872017210 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Sat, 19 Nov 2022 01:49:31 +0100 Subject: [PATCH 147/265] update to doc-string --- docs/source/io_formats/settings.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 285dfa2290..a162e8de6d 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -768,9 +768,10 @@ certain surfaces and write out the source bank in a separate file called *Default*: None :mcpl: - An optional boolean which indicates if the banked particle should + An optional boolean which indicates if the banked particles should be written to a file in the MCPL-format (documented in mcpl_). - instead of the native hdf5-based format. + instead of the native hdf5-based format.If activated the output + output file name is altered to ``surface_source.mcpl`` *Default*: false From fab1ddac003648e7954ad85b8d4718e7685df0e4 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Sat, 19 Nov 2022 02:19:41 +0100 Subject: [PATCH 148/265] prefer native as default Co-authored-by: Paul Romano --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index 9098eb6205..91a13289cf 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -60,7 +60,7 @@ bool run_CE {true}; bool source_latest {false}; bool source_separate {false}; bool source_write {true}; -bool source_mcpl_write {true}; +bool source_mcpl_write {false}; bool surf_source_write {false}; bool surf_mcpl_write {false}; bool surf_source_read {false}; From d7ab491f473e87f5865e365bd5f9fbcfcd24943e Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 08:57:00 +0100 Subject: [PATCH 149/265] Try fix of NCrystal install --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 5f577b8da9..78a1a44f21 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -45,7 +45,7 @@ make install cat < ./findncrystallib.py import pathlib libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() -libs = list(pathlib.Path("/usr").glob("**/lib*/**/%s"%libname)) +libs = list(pathlib.Path("${INST_DIR}").glob("**/lib*/**/%s"%libname)) assert len(libs)==1 pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) EOF From b199db9cb9c6de05117c2749c21ce5ea3d474f7d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 09:06:42 +0100 Subject: [PATCH 150/265] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 78a1a44f21..715e4da94c 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -62,5 +62,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -setup ${INST_DIR}/setup.sh +source ${INST_DIR}/setup.sh From 39d0c5cd61224b2a80b8c6ee75597588e132842b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 09:38:34 +0100 Subject: [PATCH 151/265] Try to initialize with setup.sh --- tools/ci/gha-install-ncrystal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 715e4da94c..8e75cc60af 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -24,7 +24,7 @@ cmake \ -DMODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=OFF \ + -DINSTALL_SETUPSH=ON \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ From 9e1abb328429072d9349fd5c01ae6249c438d187 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Sat, 19 Nov 2022 10:47:26 +0100 Subject: [PATCH 152/265] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 8e75cc60af..c080c16d8f 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -24,7 +24,7 @@ cmake \ -DMODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=ON \ + -DINSTALL_SETUPSH=OFF \ -DEMBED_DATA=ON \ -DINSTALL_DATA=OFF \ -DNO_DIRECT_PYMODINST=ON \ @@ -62,5 +62,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -source ${INST_DIR}/setup.sh +# source ${INST_DIR}/setup.sh From c9c74f08b2cae9c57f818c4a93abf986514fd809 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Mon, 21 Nov 2022 00:34:30 +0100 Subject: [PATCH 153/265] apply clang-format --- src/source.cpp | 78 +++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 2e6335aad2..af108f3fa9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -234,7 +234,8 @@ SourceSite IndependentSource::sample(uint64_t* seed) const auto id = (domain_type_ == DomainType::CELL) ? model::cells[coord.cell]->id_ : model::universes[coord.universe]->id_; - if (found = contains(domain_ids_, id)) break; + if (found = contains(domain_ids_, id)) + break; } } } @@ -332,57 +333,57 @@ FileSource::FileSource(std::string path) #ifdef OPENMC_MCPL FileSource::FileSource(mcpl_file_t mcpl_file) { - size_t n_sites=mcpl_hdr_nparticles(mcpl_file); + size_t n_sites = mcpl_hdr_nparticles(mcpl_file); sites_.resize(n_sites); - for (int i=0;ipdgcode; - while ( pdg!=2112 && pdg!=22 && pdg!=11 && pdg!=-11) { - mcpl_particle=mcpl_read(mcpl_file); - pdg=mcpl_particle->pdgcode; + int pdg = mcpl_particle->pdgcode; + while (pdg != 2112 && pdg != 22 && pdg != 11 && pdg != -11) { + mcpl_particle = mcpl_read(mcpl_file); + pdg = mcpl_particle->pdgcode; } - switch(pdg){ - case 2112: - site_.particle=ParticleType::neutron; - break; - case 22: - site_.particle=ParticleType::photon; - break; - case 11: - site_.particle=ParticleType::electron; - break; - case -11: - site_.particle=ParticleType::positron; - break; + switch (pdg) { + case 2112: + site_.particle = ParticleType::neutron; + break; + case 22: + site_.particle = ParticleType::photon; + break; + case 11: + site_.particle = ParticleType::electron; + break; + case -11: + site_.particle = ParticleType::positron; + break; } - //particle is good, convert to openmc-formalism - site_.r.x=mcpl_particle->position[0]; - site_.r.y=mcpl_particle->position[1]; - site_.r.z=mcpl_particle->position[2]; + // particle is good, convert to openmc-formalism + site_.r.x = mcpl_particle->position[0]; + site_.r.y = mcpl_particle->position[1]; + site_.r.z = mcpl_particle->position[2]; - site_.u.x=mcpl_particle->direction[0]; - site_.u.y=mcpl_particle->direction[1]; - site_.u.z=mcpl_particle->direction[2]; + site_.u.x = mcpl_particle->direction[0]; + site_.u.y = mcpl_particle->direction[1]; + site_.u.z = mcpl_particle->direction[2]; - //mcpl stores kinetic energy in MeV - site_.E=mcpl_particle->ekin*1e6; - //mcpl stores time in ms - site_.time=mcpl_particle->time*1e-3; - site_.wgt=mcpl_particle->weight; - site_.delayed_group=0; - sites_[i]=site_; + // mcpl stores kinetic energy in MeV + site_.E = mcpl_particle->ekin * 1e6; + // mcpl stores time in ms + site_.time = mcpl_particle->time * 1e-3; + site_.wgt = mcpl_particle->weight; + site_.delayed_group = 0; + sites_[i] = site_; } mcpl_close_file(mcpl_file); } -#endif //OPENMC_MCPL +#endif // OPENMC_MCPL SourceSite FileSource::sample(uint64_t* seed) const { @@ -443,7 +444,6 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } - //============================================================================== // Non-member functions //============================================================================== From 3e6bb9d5f669d2729fa96acdb4894ed1d338f2c5 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 08:51:23 -0300 Subject: [PATCH 154/265] Upgrade installation script to NCrystal 3.5.0 --- tools/ci/gha-install-ncrystal.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index c080c16d8f..18472dfa66 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -21,13 +21,12 @@ cmake \ "${SRC_DIR}" \ -DBUILD_SHARED_LIBS=ON \ -DNCRYSTAL_NOTOUCH_CMAKE_BUILD_TYPE=ON \ - -DMODIFY_RPATH=OFF \ + -DNCRYSTAL_MODIFY_RPATH=OFF \ -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_EXAMPLES=OFF \ - -DINSTALL_SETUPSH=OFF \ - -DEMBED_DATA=ON \ - -DINSTALL_DATA=OFF \ - -DNO_DIRECT_PYMODINST=ON \ + -DNCRYSTAL_ENABLE_EXAMPLES=OFF \ + -DNCRYSTAL_ENABLE_SETUPSH=OFF \ + -DNCRYSTAL_ENABLE_DATA=OFF \ + -DNCRYSTAL_SKIP_PYMODINST=ON \ -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" @@ -62,5 +61,5 @@ EOF $PYTHON -m pip install ./ncrystal_pypkg/ -vv -# source ${INST_DIR}/setup.sh +eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) From be330a11b6798850ddaa54635167c89030bfb511 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:15:45 -0300 Subject: [PATCH 155/265] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 18472dfa66..1bcc896275 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -25,8 +25,7 @@ cmake \ -DCMAKE_BUILD_TYPE=Release \ -DNCRYSTAL_ENABLE_EXAMPLES=OFF \ -DNCRYSTAL_ENABLE_SETUPSH=OFF \ - -DNCRYSTAL_ENABLE_DATA=OFF \ - -DNCRYSTAL_SKIP_PYMODINST=ON \ + -DNCRYSTAL_ENABLE_DATA=EMBED \ -DCMAKE_INSTALL_PREFIX="${INST_DIR}" \ -DPython3_EXECUTABLE="$PYTHON" @@ -36,30 +35,8 @@ make install #Note: There is no "make test" or "make ctest" functionality for NCrystal # yet. If it appears in the future, we should add it here. - -#The next stuff is not pretty, but it is needed as a temporary -#workaround until NCrystal add better support for system-wide -#installations in CMake: - -cat < ./findncrystallib.py -import pathlib -libname = pathlib.Path('./cfg_ncrystal_libname.txt').read_text().strip() -libs = list(pathlib.Path("${INST_DIR}").glob("**/lib*/**/%s"%libname)) -assert len(libs)==1 -pathlib.Path('ncrystal_liblocation.txt').write_text(str(libs[0])) -EOF - -python3 ./findncrystallib.py - -TMPNCRYSTALLIBLOCATION=$(cat ./ncrystal_liblocation.txt) - -cat < ./ncrystal_pypkg/NCrystal/_nclibpath.py -#File autogenerated for working with systemwide install of NCrystal: -import pathlib -liblocation = pathlib.Path('${TMPNCRYSTALLIBLOCATION}'.strip()) -EOF - -$PYTHON -m pip install ./ncrystal_pypkg/ -vv - eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) +# Check installation worked + +ncrystal-config --setup From fff545ac79d39b6aad0ee4dd055ac1da38cc344a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:27:12 -0300 Subject: [PATCH 156/265] Update gha-install-ncrystal.sh --- tools/ci/gha-install-ncrystal.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 1bcc896275..390b7dba54 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -35,8 +35,14 @@ make install #Note: There is no "make test" or "make ctest" functionality for NCrystal # yet. If it appears in the future, we should add it here. -eval $( "${INST_DIR}/bin/ncrystal-config --setup" ) +# Output the configuration to the log + +"${INST_DIR}/bin/ncrystal-config" --setup + +# Change environmental variables + +eval $( "${INST_DIR}/bin/ncrystal-config" --setup ) # Check installation worked -ncrystal-config --setup +nctool --test From 8598ff7e4311c80c3dc7993a34eb6fc40976d7ed Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:45:27 -0300 Subject: [PATCH 157/265] Pass CMAKE_PREFIX_PATH from NCrystall installation --- tools/ci/gha-install.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index f3a29273f6..95fe70abea 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,6 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') + ncrystal_path = os.environ.get('CMAKE_PREFIX_PATH') + cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_path) # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') From c98a12171010e00fb78572c72f7327b0de740718 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:56:10 -0300 Subject: [PATCH 158/265] Update gha-install.py --- tools/ci/gha-install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 95fe70abea..f5779c9351 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,8 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') - ncrystal_path = os.environ.get('CMAKE_PREFIX_PATH') - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_path) + ncrystal_cmake_path = os.environ.get('HOME')+'/ncrystal_inst/lib/cmake' + cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_cmake_path) # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') From 19bf624b34806efcffc52a8f18ad915624b8811b Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:03:41 -0300 Subject: [PATCH 159/265] Add NCrystal installation test --- tools/ci/gha-script.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index c791167e99..4c125b529a 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -14,5 +14,10 @@ if [[ $EVENT == 'y' ]]; then args="${args} --event " fi +# Check NCrystal installation +if [[ $NCRYSTAL = 'y' ]]; then + nctool --test +fi + # Run regression and unit tests pytest --cov=openmc -v $args tests From 4391d601fde4f74f91d594238e98564632c695b8 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:06:06 -0300 Subject: [PATCH 160/265] find NCrystal --- cmake/OpenMCConfig.cmake.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index d0e2beb827..162e3aad79 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -9,6 +9,11 @@ if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() +if(@OPENMC_USE_NCRYSTAL@) + find_package(NCrystal REQUIRED) + message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") +endif() + if(@OPENMC_USE_LIBMESH@) include(FindPkgConfig) list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@) From e626447a4e8dbce5ff8211b0ada661c329e3cd3d Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian <22483345+marquezj@users.noreply.github.com> Date: Mon, 28 Nov 2022 11:18:32 -0300 Subject: [PATCH 161/265] Update gha-script.sh --- tools/ci/gha-script.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index 4c125b529a..85fdef49f5 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -16,6 +16,8 @@ fi # Check NCrystal installation if [[ $NCRYSTAL = 'y' ]]; then + # Change environmental variables + eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) nctool --test fi From e7b4285bd5214d0028700c07897fe60919ef8689 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Marquez Damian Date: Tue, 29 Nov 2022 11:21:04 +0100 Subject: [PATCH 162/265] Cleanup --- docs/source/methods/cross_sections.rst | 2 +- docs/source/usersguide/materials.rst | 2 +- tools/ci/gha-install-ncrystal.sh | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 64e4fac3de..0b87913e51 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -189,7 +189,7 @@ cannot currently included in ACE files such as oriented single crystals (see the and further extend the physics `using plugins`_. Thermal scattering kernels are generated on the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ -that does not require previous processing. A `large library` of materials is already included in +that does not require previous processing. A `large library`_ of materials is already included in the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ or `combining existing files`_. diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 1eaae846c3..a3c05f4211 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -106,7 +106,7 @@ Adding NCrystal materials ------------------ Additional support for thermal scattering can be added by using NCrystal_. -The :meth:`Material.from_ncrystal` class method generates a material object from +The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from an `NCrystal configuration string `_. Temperature, material composition and density are passed from the configuration string and the `NCMAT file `_ diff --git a/tools/ci/gha-install-ncrystal.sh b/tools/ci/gha-install-ncrystal.sh index 390b7dba54..16f77e13e2 100755 --- a/tools/ci/gha-install-ncrystal.sh +++ b/tools/ci/gha-install-ncrystal.sh @@ -15,8 +15,6 @@ CPU_COUNT=1 mkdir "$BLD_DIR" cd ncrystal_bld -#cmake -Dstatic=on .. && make 2>/dev/null && sudo make install - cmake \ "${SRC_DIR}" \ -DBUILD_SHARED_LIBS=ON \ From 2555b951a4c585ab31942d4a718c2c5354d082a8 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 18:27:53 -0500 Subject: [PATCH 163/265] Factor out to_xml_element methods for XML classes --- openmc/geometry.py | 52 +++++++++++++-------- openmc/plots.py | 21 +++++---- openmc/settings.py | 109 ++++++++++++++++++++++++--------------------- openmc/tallies.py | 25 +++++++---- 4 files changed, 120 insertions(+), 87 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 3b69333625..889cb6b31c 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -103,6 +103,38 @@ class Geometry: if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) + def to_xml_element(self, remove_surfs=False): + """Creates a 'geometry' element to be written to an XML file. + + Parameters + ---------- + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting + + """ + # Find and remove redundant surfaces from the geometry + if remove_surfs: + warnings.warn("remove_surfs kwarg will be deprecated soon, please " + "set the Geometry.merge_surfaces attribute instead.") + self.merge_surfaces = True + + if self.merge_surfaces: + self.remove_redundant_surfaces() + + # Create XML representation + element = ET.Element("geometry") + self.root_universe.create_xml_subelement(element, memo=set()) + + # Sort the elements in the file + element[:] = sorted(element, key=lambda x: ( + x.tag, int(x.get('id')))) + + # Clean the indentation in the file to be user-readable + xml.clean_indentation(element) + + return element + def export_to_xml(self, path='geometry.xml', remove_surfs=False): """Export geometry to an XML file. @@ -117,25 +149,7 @@ class Geometry: .. versionadded:: 0.12 """ - # Find and remove redundant surfaces from the geometry - if remove_surfs: - warnings.warn("remove_surfs kwarg will be deprecated soon, please " - "set the Geometry.merge_surfaces attribute instead.") - self.merge_surfaces = True - - if self.merge_surfaces: - self.remove_redundant_surfaces() - - # Create XML representation - root_element = ET.Element("geometry") - self.root_universe.create_xml_subelement(root_element, memo=set()) - - # Sort the elements in the file - root_element[:] = sorted(root_element, key=lambda x: ( - x.tag, int(x.get('id')))) - - # Clean the indentation in the file to be user-readable - xml.clean_indentation(root_element) + root_element = self.to_xml_element(remove_surfs) # Check if path is a directory p = Path(path) diff --git a/openmc/plots.py b/openmc/plots.py index 2708683b1a..52dcf3fc13 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -909,14 +909,8 @@ class Plots(cv.CheckedList): self._plots_file.append(xml_element) - def export_to_xml(self, path='plots.xml'): - """Export plot specifications to an XML file. - - Parameters - ---------- - path : str - Path to file to write. Defaults to 'plots.xml'. - + def to_xml_element(self): + """Create a 'plots' element to be written to an XML file. """ # Reset xml element tree self._plots_file.clear() @@ -926,6 +920,17 @@ class Plots(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(self._plots_file) + return self._plots_file + + def export_to_xml(self, path='plots.xml'): + """Export plot specifications to an XML file. + + Parameters + ---------- + path : str + Path to file to write. Defaults to 'plots.xml'. + + """ # Check if path is a directory p = Path(path) if p.is_dir(): diff --git a/openmc/settings.py b/openmc/settings.py index 7d327ce7cf..29e19db2e1 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1539,6 +1539,63 @@ class Settings: if text is not None: self.max_tracks = int(text) + def to_xml_element(self): + """Create a 'settings' element to be written to an XML file. + """ + + # Reset xml element tree + element = ET.Element("settings") + + self._create_run_mode_subelement(element) + self._create_particles_subelement(element) + self._create_batches_subelement(element) + self._create_inactive_subelement(element) + self._create_max_lost_particles_subelement(element) + self._create_rel_max_lost_particles_subelement(element) + self._create_generations_per_batch_subelement(element) + self._create_keff_trigger_subelement(element) + self._create_source_subelement(element) + self._create_output_subelement(element) + self._create_statepoint_subelement(element) + self._create_sourcepoint_subelement(element) + self._create_surf_source_read_subelement(element) + self._create_surf_source_write_subelement(element) + self._create_confidence_intervals(element) + self._create_electron_treatment_subelement(element) + self._create_energy_mode_subelement(element) + self._create_max_order_subelement(element) + self._create_photon_transport_subelement(element) + self._create_ptables_subelement(element) + self._create_seed_subelement(element) + self._create_survival_biasing_subelement(element) + self._create_cutoff_subelement(element) + self._create_entropy_mesh_subelement(element) + self._create_trigger_subelement(element) + self._create_no_reduce_subelement(element) + self._create_verbosity_subelement(element) + self._create_tabular_legendre_subelements(element) + self._create_temperature_subelements(element) + self._create_trace_subelement(element) + self._create_track_subelement(element) + self._create_ufs_mesh_subelement(element) + self._create_resonance_scattering_subelement(element) + self._create_volume_calcs_subelement(element) + self._create_create_fission_neutrons_subelement(element) + self._create_delayed_photon_scaling_subelement(element) + self._create_event_based_subelement(element) + self._create_max_particles_in_flight_subelement(element) + self._create_material_cell_offsets_subelement(element) + self._create_log_grid_bins_subelement(element) + self._create_write_initial_source_subelement(element) + self._create_weight_windows_subelement(element) + self._create_max_splits_subelement(element) + self._create_max_tracks_subelement(element) + + # Clean the indentation in the file to be user-readable + clean_indentation(element) + + return element + def export_to_xml(self, path: PathLike = 'settings.xml'): """Export simulation settings to an XML file. @@ -1548,57 +1605,7 @@ class Settings: Path to file to write. Defaults to 'settings.xml'. """ - - # Reset xml element tree - root_element = ET.Element("settings") - - self._create_run_mode_subelement(root_element) - self._create_particles_subelement(root_element) - self._create_batches_subelement(root_element) - self._create_inactive_subelement(root_element) - self._create_max_lost_particles_subelement(root_element) - self._create_rel_max_lost_particles_subelement(root_element) - self._create_generations_per_batch_subelement(root_element) - self._create_keff_trigger_subelement(root_element) - self._create_source_subelement(root_element) - self._create_output_subelement(root_element) - self._create_statepoint_subelement(root_element) - self._create_sourcepoint_subelement(root_element) - self._create_surf_source_read_subelement(root_element) - self._create_surf_source_write_subelement(root_element) - self._create_confidence_intervals(root_element) - self._create_electron_treatment_subelement(root_element) - self._create_energy_mode_subelement(root_element) - self._create_max_order_subelement(root_element) - self._create_photon_transport_subelement(root_element) - self._create_ptables_subelement(root_element) - self._create_seed_subelement(root_element) - self._create_survival_biasing_subelement(root_element) - self._create_cutoff_subelement(root_element) - self._create_entropy_mesh_subelement(root_element) - self._create_trigger_subelement(root_element) - self._create_no_reduce_subelement(root_element) - self._create_verbosity_subelement(root_element) - self._create_tabular_legendre_subelements(root_element) - self._create_temperature_subelements(root_element) - self._create_trace_subelement(root_element) - self._create_track_subelement(root_element) - self._create_ufs_mesh_subelement(root_element) - self._create_resonance_scattering_subelement(root_element) - self._create_volume_calcs_subelement(root_element) - self._create_create_fission_neutrons_subelement(root_element) - self._create_delayed_photon_scaling_subelement(root_element) - self._create_event_based_subelement(root_element) - self._create_max_particles_in_flight_subelement(root_element) - self._create_material_cell_offsets_subelement(root_element) - self._create_log_grid_bins_subelement(root_element) - self._create_write_initial_source_subelement(root_element) - self._create_weight_windows_subelement(root_element) - self._create_max_splits_subelement(root_element) - self._create_max_tracks_subelement(root_element) - - # Clean the indentation in the file to be user-readable - clean_indentation(root_element) + root_element = self.to_xml_element() # Check if path is a directory p = Path(path) diff --git a/openmc/tallies.py b/openmc/tallies.py index d0355f14ee..dfb0c98bfd 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3155,6 +3155,21 @@ class Tallies(cv.CheckedList): for d in derivs: root_element.append(d.to_xml_element()) + def to_xml_element(self): + """Creates a 'tallies' element to be written to an XML file. + """ + element = ET.Element("tallies") + self._create_mesh_subelements(element) + self._create_filter_subelements(element) + self._create_tally_subelements(element) + self._create_derivative_subelements(element) + + # Clean the indentation in the file to be user-readable + clean_indentation(element) + + return element + + def export_to_xml(self, path='tallies.xml'): """Create a tallies.xml file that can be used for a simulation. @@ -3164,15 +3179,7 @@ class Tallies(cv.CheckedList): Path to file to write. Defaults to 'tallies.xml'. """ - - root_element = ET.Element("tallies") - self._create_mesh_subelements(root_element) - self._create_filter_subelements(root_element) - self._create_tally_subelements(root_element) - self._create_derivative_subelements(root_element) - - # Clean the indentation in the file to be user-readable - clean_indentation(root_element) + root_element = self.to_xml_element() # Check if path is a directory p = Path(path) From ad746cb3e1d57c2fec163fbb4b8f87286bf72852 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 19:09:15 -0500 Subject: [PATCH 164/265] Writing all main nodes to a single XML file --- openmc/material.py | 62 +++++++++++++++++++++++++------------------ openmc/model/model.py | 34 +++++++++++++++++------- openmc/settings.py | 2 +- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 420a085216..af42e2e249 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1449,6 +1449,41 @@ class Materials(cv.CheckedList): for material in self: material.make_isotropic_in_lab() + def _write_xml(self, file): + """Writes XML content of the materials to an open file handle. + + Parameters + ---------- + file : IOTextWrapper + Open file handle to write content into. + """ + # Write the header and the opening tag for the root element. + file.write("\n") + file.write('\n') + + # Write the element. + if self.cross_sections is not None: + element = ET.Element('cross_sections') + element.text = str(self.cross_sections) + clean_indentation(element, level=1) + element.tail = element.tail.strip(' ') + file.write(' ') + reorder_attributes(element) # TODO: Remove when support is Python 3.8+ + ET.ElementTree(element).write(file, encoding='unicode') + + # Write the elements. + for material in sorted(self, key=lambda x: x.id): + element = material.to_xml_element() + clean_indentation(element, level=1) + element.tail = element.tail.strip(' ') + file.write(' ') + reorder_attributes(element) # TODO: Remove when support is Python 3.8+ + ET.ElementTree(element).write(file, encoding='unicode') + + # Write the closing tag for the root element. + file.write('\n') + + def export_to_xml(self, path: PathLike = 'materials.xml'): """Export material collection to an XML file. @@ -1468,32 +1503,7 @@ class Materials(cv.CheckedList): # one go. with open(str(p), 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: - - # Write the header and the opening tag for the root element. - fh.write("\n") - fh.write('\n') - - # Write the element. - if self.cross_sections is not None: - element = ET.Element('cross_sections') - element.text = str(self.cross_sections) - clean_indentation(element, level=1) - element.tail = element.tail.strip(' ') - fh.write(' ') - reorder_attributes(element) # TODO: Remove when support is Python 3.8+ - ET.ElementTree(element).write(fh, encoding='unicode') - - # Write the elements. - for material in sorted(self, key=lambda x: x.id): - element = material.to_xml_element() - clean_indentation(element, level=1) - element.tail = element.tail.strip(' ') - fh.write(' ') - reorder_attributes(element) # TODO: Remove when support is Python 3.8+ - ET.ElementTree(element).write(fh, encoding='unicode') - - # Write the closing tag for the root element. - fh.write('\n') + self._write_xml(fh) @classmethod def from_xml(cls, path: PathLike = 'materials.xml'): diff --git a/openmc/model/model.py b/openmc/model/model.py index 98136b2d59..5a1b86a1ff 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -5,11 +5,16 @@ import os from pathlib import Path from numbers import Integral from tempfile import NamedTemporaryFile +<<<<<<< HEAD import warnings +======= +from xml.etree import ElementTree as ET +>>>>>>> d42935a08 (Writing all main nodes to a single XML file) import h5py import openmc +import openmc._xml as xml from openmc.dummy_comm import DummyCommunicator from openmc.executor import _process_CLI_arguments from openmc.checkvalue import check_type, check_value @@ -404,7 +409,7 @@ class Model: Parameters ---------- directory : str - Directory to write XML files to. If it doesn't exist already, it + Directory to write the model.xml file to. If it doesn't exist already, it will be created. remove_surfs : bool Whether or not to remove redundant surfaces from the geometry when @@ -416,8 +421,8 @@ class Model: d = Path(directory) if not d.is_dir(): d.mkdir(parents=True) + d /= 'model.xml' - self.settings.export_to_xml(d) if remove_surfs: warnings.warn("remove_surfs kwarg will be deprecated soon, please " "set the Geometry.merge_surfaces attribute instead.") @@ -425,22 +430,33 @@ class Model: # Can be used to modify tallies in case any surfaces are redundant redundant_surfaces = self.geometry.remove_redundant_surfaces() - self.geometry.export_to_xml(d) + settings_element = self.settings.to_xml_element() + geometry_element = self.geometry.to_xml_element() # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build # a collection. if self.materials: - self.materials.export_to_xml(d) + materials = self.materials else: materials = openmc.Materials(self.geometry.get_all_materials() .values()) - materials.export_to_xml(d) - if self.tallies: - self.tallies.export_to_xml(d) - if self.plots: - self.plots.export_to_xml(d) + with open(d, 'w', encoding='utf-8', + errors='xmlcharrefreplace') as fh: + # Write the materials collection to the open XML file first. + # This will write the XML header also + materials._write_xml(fh) + # Write remaining elements as a tree + ET.ElementTree(geometry_element).write(fh, encoding='unicode') + ET.ElementTree(settings_element).write(fh, encoding='unicode') + + if self.tallies: + tallies_element = self.tallies.to_xml_element() + ET.ElementTree(tallies_element).write(fh, encoding='unicode') + if self.plots: + plots_element = self.plots.to_xml_element() + ET.ElementTree(plots_element).write(fh, encoding='unicode') def import_properties(self, filename): """Import physical properties diff --git a/openmc/settings.py b/openmc/settings.py index 29e19db2e1..dd00413516 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1595,7 +1595,7 @@ class Settings: clean_indentation(element) return element - + def export_to_xml(self, path: PathLike = 'settings.xml'): """Export simulation settings to an XML file. From a09a61e77b10416ad52613bb578e03d60af72ebf Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 19:12:18 -0500 Subject: [PATCH 165/265] Writing all output under a single root node --- openmc/material.py | 7 +++++-- openmc/model/model.py | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index af42e2e249..9e3951ccf4 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1449,16 +1449,19 @@ class Materials(cv.CheckedList): for material in self: material.make_isotropic_in_lab() - def _write_xml(self, file): + def _write_xml(self, file, header=True): """Writes XML content of the materials to an open file handle. Parameters ---------- file : IOTextWrapper Open file handle to write content into. + header : bool + Whether or not to write the XML header """ # Write the header and the opening tag for the root element. - file.write("\n") + if header: + file.write("\n") file.write('\n') # Write the element. diff --git a/openmc/model/model.py b/openmc/model/model.py index 5a1b86a1ff..1af3bdffc9 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -444,9 +444,12 @@ class Model: with open(d, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: + # write the XML header + fh.write("\n") + fh.write("\n") # Write the materials collection to the open XML file first. # This will write the XML header also - materials._write_xml(fh) + materials._write_xml(fh, False) # Write remaining elements as a tree ET.ElementTree(geometry_element).write(fh, encoding='unicode') ET.ElementTree(settings_element).write(fh, encoding='unicode') @@ -457,6 +460,7 @@ class Model: if self.plots: plots_element = self.plots.to_xml_element() ET.ElementTree(plots_element).write(fh, encoding='unicode') + fh.write("\n") def import_properties(self, filename): """Import physical properties From 7a9d8c95afa53fca2a8d99cf48a6be13f1786107 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 19:18:39 -0500 Subject: [PATCH 166/265] Keeping old option to write separate XMLs so I can still use to tests to make sure I don't break stuff. --- openmc/model/model.py | 61 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 1af3bdffc9..e3c4c4ae19 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -403,7 +403,66 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.', remove_surfs=False): + def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True): + """Export model to separate XML files. + + Parameters + ---------- + directory : str + Directory to write XML files to. If it doesn't exist already, it + will be created. + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting. + + .. versionadded:: 0.13.1 + + separate_xmls : bool + Whether or not to write a single model.xml file or many XML files. + """ + if separate_xmls: + self.export_to_separate_xmls(directory, remove_surfs) + else: + self.export_to_single_xml(directory, remove_surfs) + + def export_to_separate_xmls(self, directory='.', remove_surfs=False): + """Export model to separate XML files. + + Parameters + ---------- + directory : str + Directory to write XML files to. If it doesn't exist already, it + will be created. + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting. + + .. versionadded:: 0.13.1 + """ + # Create directory if required + d = Path(directory) + if not d.is_dir(): + d.mkdir(parents=True) + + self.settings.export_to_xml(d) + self.geometry.export_to_xml(d, remove_surfs=remove_surfs) + + # If a materials collection was specified, export it. Otherwise, look + # for all materials in the geometry and use that to automatically build + # a collection. + if self.materials: + self.materials.export_to_xml(d) + else: + materials = openmc.Materials(self.geometry.get_all_materials() + .values()) + materials.export_to_xml(d) + + if self.tallies: + self.tallies.export_to_xml(d) + if self.plots: + self.plots.export_to_xml(d) + + def export_to_single_xml(self, directory='.', remove_surfs=False): """Export model to XML files. Parameters From 040965245dc5a7476aa005829e7cb42f75a9233c Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 20:10:22 -0500 Subject: [PATCH 167/265] Adding signatures for reading information from an XML node where necessary. --- include/openmc/cross_sections.h | 5 +++ include/openmc/geometry_aux.h | 6 ++++ include/openmc/material.h | 4 +++ include/openmc/plot.h | 4 +++ include/openmc/settings.h | 5 ++- include/openmc/tallies/tally.h | 8 +++-- src/cross_sections.cpp | 8 +++-- src/geometry_aux.cpp | 8 +++-- src/initialize.cpp | 63 ++++++++++++++++++++++++++++++--- src/material.cpp | 9 +++-- src/plot.cpp | 7 ++-- src/settings.cpp | 14 +++++--- src/tallies/tally.cpp | 8 +++-- 13 files changed, 127 insertions(+), 22 deletions(-) diff --git a/include/openmc/cross_sections.h b/include/openmc/cross_sections.h index 2b0473becd..06140a6a8c 100644 --- a/include/openmc/cross_sections.h +++ b/include/openmc/cross_sections.h @@ -62,6 +62,11 @@ extern vector libraries; //! libraries void read_cross_sections_xml(); +//! Read cross sections file (either XML or multigroup H5) and populate data +//! libraries +//! \param[in] root node of the cross_sections.xml +void read_cross_sections_xml(pugi::xml_node root); + //! Load nuclide and thermal scattering data from HDF5 files // //! \param[in] nuc_temps Temperatures for each nuclide in [K] diff --git a/include/openmc/geometry_aux.h b/include/openmc/geometry_aux.h index b248d491ac..a4c506c31a 100644 --- a/include/openmc/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -10,6 +10,7 @@ #include #include "openmc/vector.h" +#include "openmc/xml_interface.h" namespace openmc { @@ -19,8 +20,13 @@ extern std::unordered_map> extern std::unordered_map universe_level_counts; } // namespace model +//! Read geometry from XML file void read_geometry_xml(); +//! Read geometry from XML node +//! \param[in] root node of geometry XML element +void read_geometry_xml(pugi::xml_node root); + //============================================================================== //! Replace Universe, Lattice, and Material IDs with indices. //============================================================================== diff --git a/include/openmc/material.h b/include/openmc/material.h index b251a3ca85..81f4e1421c 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -221,6 +221,10 @@ double density_effect(const vector& f, const vector& e_b_sq, //! Read material data from materials.xml void read_materials_xml(); +//! Read material data XML node +//! \param[in] root node of materials XML element +void read_materials_xml(pugi::xml_node root); + void free_memory_material(); } // namespace openmc diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 650b7e16a1..a415b17473 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -279,6 +279,10 @@ void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace); //! Read plot specifications from a plots.xml file void read_plots_xml(); +//! Read plot specifications from an XML Node +//! \param[in] XML node containing plot info +void read_plots_xml(pugi::xml_node root); + //! Clear memory void free_memory_plot(); diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1e061b235b..cd0ab477c4 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -127,9 +127,12 @@ extern double weight_survive; //!< Survival weight after Russian roulette //============================================================================== //! Read settings from XML file -//! \param[in] root XML node for void read_settings_xml(); +//! Read settings from XML node +//! \param[in] root XML node for +void read_settings_xml(pugi::xml_node root); + void free_memory_settings(); } // namespace openmc diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 3cead91dc7..56a51370a8 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -48,10 +48,10 @@ public: void set_nuclides(const vector& nuclides); //! returns vector of indices corresponding to the tally this is called on - const vector& filters() const { return filters_; } + const vector& filters() const { return filters_; } //! \brief Returns the tally filter at index i - int32_t filters(int i) const { return filters_[i]; } + int32_t filters(int i) const { return filters_[i]; } void set_filters(gsl::span filters); @@ -178,6 +178,10 @@ extern double global_tally_leakage; //! Read tally specification from tallies.xml void read_tallies_xml(); +//! Read tally specification from an XML node +//! \param[in] root node of tallies XML element +void read_tallies_xml(pugi::xml_node root); + //! \brief Accumulate the sum of the contributions from each history within the //! batch to a new random variable void accumulate_tallies(); diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index afb0a1f36c..2094fd551f 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -91,8 +91,7 @@ Library::Library(pugi::xml_node node, const std::string& directory) // Non-member functions //============================================================================== -void read_cross_sections_xml() -{ +void read_cross_sections_xml() { pugi::xml_document doc; std::string filename = settings::path_input + "materials.xml"; // Check if materials.xml exists @@ -104,6 +103,11 @@ void read_cross_sections_xml() auto root = doc.document_element(); + read_cross_sections_xml(root); +} + +void read_cross_sections_xml(pugi::xml_node root) +{ // Find cross_sections.xml file -- the first place to look is the // materials.xml file. If no file is found there, then we check the // OPENMC_CROSS_SECTIONS environment variable diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 2614719840..83e8494de6 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -40,8 +40,7 @@ void update_universe_cell_count(int32_t a, int32_t b) } } -void read_geometry_xml() -{ +void read_geometry_xml() { // Display output message write_message("Reading geometry XML file...", 5); @@ -61,6 +60,11 @@ void read_geometry_xml() // Get root element pugi::xml_node root = doc.document_element(); + read_geometry_xml(root); +} + +void read_geometry_xml(pugi::xml_node root) +{ // Read surfaces, cells, lattice read_surfaces(root); read_cells(root); diff --git a/src/initialize.cpp b/src/initialize.cpp index aa353aa9cb..44f84034a7 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -14,6 +14,7 @@ #include "openmc/constants.h" #include "openmc/cross_sections.h" #include "openmc/error.h" +#include "openmc/file_utils.h" #include "openmc/geometry_aux.h" #include "openmc/hdf5_interface.h" #include "openmc/material.h" @@ -291,10 +292,53 @@ int parse_command_line(int argc, char* argv[]) void read_input_xml() { - read_settings_xml(); + + // search for a single model.xml file + // Check if settings.xml exists + std::string model_filename = settings::path_input + "model.xml"; + bool use_model_file = file_exists(model_filename); + pugi::xml_node model_root; + if (use_model_file) { + write_message("Found model.xml file."); + pugi::xml_document doc; + auto result = doc.load_file(model_filename.c_str()); + if (!result) { + fatal_error("Error processing model.xml file."); + } + auto model_root = doc.document_element(); + + auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; + for (const auto& input : other_inputs) { + if (file_exists(settings::path_input + input)) { + warning(fmt::format("A '{}' file is also present. This file will be ignored.", input)); + } + } + } else { + write_message("No model.xml found. Reading separate XML inputs..."); + } + if (use_model_file) { + if (!check_for_node(model_root, "settings")) { + fatal_error("No node present in the model.xml file."); + } + read_settings_xml(model_root.child("settings")); + } else { + read_settings_xml(); + } + read_cross_sections_xml(); - read_materials_xml(); - read_geometry_xml(); + if (use_model_file) { + if (!check_for_node(model_root, "materials")) { + fatal_error("No node present in the model.xml file."); + } + read_materials_xml(model_root.child("materials")); + if (!check_for_node(model_root, "geometry")) { + fatal_error("No node present in the model.xml_file."); + } + read_geometry_xml(model_root.child("geometry")); + } else { + read_materials_xml(); + read_geometry_xml(); + } // Final geometry setup and assign temperatures finalize_geometry(); @@ -302,14 +346,23 @@ void read_input_xml() // Finalize cross sections having assigned temperatures finalize_cross_sections(); - read_tallies_xml(); + if (use_model_file && check_for_node(model_root, "tallies")) { + read_tallies_xml(model_root.child("tallies")); + } else { + read_tallies_xml(); + } // Initialize distribcell_filters prepare_distribcell(); // Read the plots.xml regardless of plot mode in case plots are requested // via the API - read_plots_xml(); + if (use_model_file) { + if (check_for_node(model_root, "plots")) + read_plots_xml(model_root.child("plots")); + } else { + read_plots_xml(); + } if (settings::run_mode == RunMode::PLOTTING) { // Read plots.xml if it exists if (mpi::master && settings::verbosity >= 5) diff --git a/src/material.cpp b/src/material.cpp index 30dfa5ed50..f2e23aede2 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1264,8 +1264,7 @@ double density_effect(const vector& f, const vector& e_b_sq, return delta - w_sq * (1.0 - beta_sq); } -void read_materials_xml() -{ +void read_materials_xml() { write_message("Reading materials XML file...", 5); pugi::xml_document doc; @@ -1281,6 +1280,12 @@ void read_materials_xml() // Loop over XML material elements and populate the array. pugi::xml_node root = doc.document_element(); + + read_materials_xml(root); +} + +void read_materials_xml(pugi::xml_node root) +{ for (pugi::xml_node material_node : root.children("material")) { model::materials.push_back(make_unique(material_node)); } diff --git a/src/plot.cpp b/src/plot.cpp index b012ed1311..e28543cdff 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -124,8 +124,7 @@ extern "C" int openmc_plot_geometry() return 0; } -void read_plots_xml() -{ +void read_plots_xml() { // Check if plots.xml exists; this is only necessary when the plot runmode is // initiated. Otherwise, we want to read plots.xml because it may be called // later via the API. In that case, its ok for a plots.xml to not exist @@ -141,6 +140,10 @@ void read_plots_xml() doc.load_file(filename.c_str()); pugi::xml_node root = doc.document_element(); +} + +void read_plots_xml(pugi::xml_node root) +{ for (auto node : root.children("plot")) { model::plots.emplace_back(node); model::plot_map[model::plots.back().id_] = model::plots.size() - 1; diff --git a/src/settings.cpp b/src/settings.cpp index eb0d4936c7..710fb28101 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -211,13 +211,11 @@ void get_run_parameters(pugi::xml_node node_base) } } -void read_settings_xml() -{ +void read_settings_xml() { using namespace settings; using namespace pugi; - // Check if settings.xml exists - std::string filename = path_input + "settings.xml"; + std::string filename = settings::path_input + "settings.xml"; if (!file_exists(filename)) { if (run_mode != RunMode::PLOTTING) { fatal_error( @@ -245,6 +243,14 @@ void read_settings_xml() // Get root element xml_node root = doc.document_element(); + read_settings_xml(root); +} + +void read_settings_xml(pugi::xml_node root) +{ + using namespace settings; + using namespace pugi; + // Verbosity if (check_for_node(root, "verbosity")) { verbosity = std::stoi(get_node_value(root, "verbosity")); diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 51194684ae..1c1f274d79 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -705,8 +705,7 @@ std::string Tally::nuclide_name(int nuclide_idx) const // Non-member functions //============================================================================== -void read_tallies_xml() -{ +void read_tallies_xml() { // Check if tallies.xml exists. If not, just return since it is optional std::string filename = settings::path_input + "tallies.xml"; if (!file_exists(filename)) @@ -719,6 +718,11 @@ void read_tallies_xml() doc.load_file(filename.c_str()); pugi::xml_node root = doc.document_element(); + read_tallies_xml(root); +} + +void read_tallies_xml(pugi::xml_node root) +{ // Check for setting if (check_for_node(root, "assume_separate")) { settings::assume_separate = get_node_value_bool(root, "assume_separate"); From cdd2a6b1cacb54387de050e96c36bca74c114777 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 20:43:29 -0500 Subject: [PATCH 168/265] Correcting doc scope and some reads --- src/initialize.cpp | 10 ++++++---- src/settings.cpp | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 44f84034a7..cb35ebce9d 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -298,14 +298,14 @@ void read_input_xml() std::string model_filename = settings::path_input + "model.xml"; bool use_model_file = file_exists(model_filename); pugi::xml_node model_root; + pugi::xml_document doc; if (use_model_file) { - write_message("Found model.xml file."); - pugi::xml_document doc; + write_message("Reading model.xml..."); auto result = doc.load_file(model_filename.c_str()); if (!result) { fatal_error("Error processing model.xml file."); } - auto model_root = doc.document_element(); + model_root = doc.document_element(); auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; for (const auto& input : other_inputs) { @@ -320,12 +320,14 @@ void read_input_xml() if (!check_for_node(model_root, "settings")) { fatal_error("No node present in the model.xml file."); } - read_settings_xml(model_root.child("settings")); + auto settings_root = model_root.child("settings"); + read_settings_xml(); } else { read_settings_xml(); } read_cross_sections_xml(); + if (use_model_file) { if (!check_for_node(model_root, "materials")) { fatal_error("No node present in the model.xml file."); diff --git a/src/settings.cpp b/src/settings.cpp index 710fb28101..de24e200ac 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -266,6 +266,7 @@ void read_settings_xml(pugi::xml_node root) // Find if a multi-group or continuous-energy simulation is desired if (check_for_node(root, "energy_mode")) { + std::cout << "Here" << std::endl; std::string temp_str = get_node_value(root, "energy_mode", true, true); if (temp_str == "mg" || temp_str == "multi-group") { run_CE = false; @@ -273,6 +274,7 @@ void read_settings_xml(pugi::xml_node root) run_CE = true; } } + std::cout << "Here" << std::endl; // Look for deprecated cross_sections.xml file in settings.xml if (check_for_node(root, "cross_sections")) { From bec9bf6f20805e12134e798aca98b90dd8e71d23 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 21:18:04 -0500 Subject: [PATCH 169/265] Refactoring model read into it's own function. --- include/openmc/initialize.h | 1 + src/initialize.cpp | 148 +++++++++++++++++++++++------------- src/settings.cpp | 19 +++-- 3 files changed, 106 insertions(+), 62 deletions(-) diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index 869be44414..47474c986b 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -11,6 +11,7 @@ int parse_command_line(int argc, char* argv[]); #ifdef OPENMC_MPI void initialize_mpi(MPI_Comm intracomm); #endif +bool read_model_xml(); void read_input_xml(); } // namespace openmc diff --git a/src/initialize.cpp b/src/initialize.cpp index cb35ebce9d..254ad88da5 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -290,81 +290,125 @@ int parse_command_line(int argc, char* argv[]) return 0; } -void read_input_xml() -{ - - // search for a single model.xml file - // Check if settings.xml exists +bool read_model_xml() { + // get verbosity from settings node std::string model_filename = settings::path_input + "model.xml"; bool use_model_file = file_exists(model_filename); - pugi::xml_node model_root; + if (!file_exists(model_filename)) return false; + + // attempt to open the document pugi::xml_document doc; - if (use_model_file) { - write_message("Reading model.xml..."); - auto result = doc.load_file(model_filename.c_str()); - if (!result) { - fatal_error("Error processing model.xml file."); - } - model_root = doc.document_element(); - - auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; - for (const auto& input : other_inputs) { - if (file_exists(settings::path_input + input)) { - warning(fmt::format("A '{}' file is also present. This file will be ignored.", input)); - } - } - } else { - write_message("No model.xml found. Reading separate XML inputs..."); + auto result = doc.load_file(model_filename.c_str()); + if (!result) { + fatal_error("Error processing model.xml file."); } - if (use_model_file) { - if (!check_for_node(model_root, "settings")) { + pugi::xml_node root = doc.document_element(); + + // Read settings + if (!check_for_node(root, "settings")) { fatal_error("No node present in the model.xml file."); - } - auto settings_root = model_root.child("settings"); - read_settings_xml(); - } else { - read_settings_xml(); + } + auto settings_root = root.child("settings"); + + // Verbosity + if (check_for_node(settings_root, "verbosity")) { + settings::verbosity = std::stoi(get_node_value(settings_root, "verbosity")); } - read_cross_sections_xml(); - - if (use_model_file) { - if (!check_for_node(model_root, "materials")) { - fatal_error("No node present in the model.xml file."); - } - read_materials_xml(model_root.child("materials")); - if (!check_for_node(model_root, "geometry")) { - fatal_error("No node present in the model.xml_file."); - } - read_geometry_xml(model_root.child("geometry")); - } else { - read_materials_xml(); - read_geometry_xml(); + // To this point, we haven't displayed any output since we didn't know what + // the verbosity is. Now that we checked for it, show the title if necessary + if (mpi::master) { + if (settings::verbosity >= 2) + title(); } + write_message("Reading model XML file...", 5); + + read_settings_xml(settings_root); + + // If other XML files are present, display warning + // that they will be ignored + auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; + for (const auto& input : other_inputs) { + if (file_exists(settings::path_input + input)) { + warning(("Other XML file input(s) are present. These file will be ignored in favor of the model.xml file.")); + break; + } + } + + // Read materials and cross sections + if (!check_for_node(root, "materials")) { + fatal_error("No node present in the model.xml file."); + } + auto materials_node = root.child("materials"); + read_cross_sections_xml(materials_node); + read_materials_xml(root.child("materials")); + + // Read geometry + if (!check_for_node(root, "geometry")) { + fatal_error("No node present in the model.xml_file."); + } + read_geometry_xml(root.child("geometry")); + // Final geometry setup and assign temperatures finalize_geometry(); // Finalize cross sections having assigned temperatures finalize_cross_sections(); - if (use_model_file && check_for_node(model_root, "tallies")) { - read_tallies_xml(model_root.child("tallies")); + if (check_for_node(root, "tallies")) + read_tallies_xml(root.child("tallies")); + + // Initialize distribcell_filters + prepare_distribcell(); + + if (check_for_node(root, "plots")) + read_plots_xml(root.child("plots")); + + if (settings::run_mode == RunMode::PLOTTING) { + // Read plots.xml if it exists + if (mpi::master && settings::verbosity >= 5) + print_plot(); + } else { - read_tallies_xml(); + // Write summary information + if (mpi::master && settings::output_summary) + write_summary(); + + // Warn if overlap checking is on + if (mpi::master && settings::check_overlaps) { + warning("Cell overlap checking is ON."); + } } + return true; +} + +void read_input_xml() +{ + // attempt to reach the model.xml file if present + if (read_model_xml()) return; + + read_settings_xml(); + read_cross_sections_xml(); + read_materials_xml(); + read_geometry_xml(); + + // Final geometry setup and assign temperatures + finalize_geometry(); + + // Finalize cross sections having assigned temperatures + finalize_cross_sections(); + + read_tallies_xml(); + // Initialize distribcell_filters prepare_distribcell(); // Read the plots.xml regardless of plot mode in case plots are requested // via the API - if (use_model_file) { - if (check_for_node(model_root, "plots")) - read_plots_xml(model_root.child("plots")); - } else { - read_plots_xml(); - } + read_plots_xml(); + if (settings::run_mode == RunMode::PLOTTING) { // Read plots.xml if it exists if (mpi::master && settings::verbosity >= 5) diff --git a/src/settings.cpp b/src/settings.cpp index de24e200ac..3048862e3f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -243,14 +243,6 @@ void read_settings_xml() { // Get root element xml_node root = doc.document_element(); - read_settings_xml(root); -} - -void read_settings_xml(pugi::xml_node root) -{ - using namespace settings; - using namespace pugi; - // Verbosity if (check_for_node(root, "verbosity")) { verbosity = std::stoi(get_node_value(root, "verbosity")); @@ -262,11 +254,19 @@ void read_settings_xml(pugi::xml_node root) if (verbosity >= 2) title(); } + write_message("Reading settings XML file...", 5); + read_settings_xml(root); +} + +void read_settings_xml(pugi::xml_node root) +{ + using namespace settings; + using namespace pugi; + // Find if a multi-group or continuous-energy simulation is desired if (check_for_node(root, "energy_mode")) { - std::cout << "Here" << std::endl; std::string temp_str = get_node_value(root, "energy_mode", true, true); if (temp_str == "mg" || temp_str == "multi-group") { run_CE = false; @@ -274,7 +274,6 @@ void read_settings_xml(pugi::xml_node root) run_CE = true; } } - std::cout << "Here" << std::endl; // Look for deprecated cross_sections.xml file in settings.xml if (check_for_node(root, "cross_sections")) { From 81aa653d506bf3e9f9288679c8e92a6f21b6ad46 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 21:22:41 -0500 Subject: [PATCH 170/265] Factoring out some common lines between reader functions --- include/openmc/initialize.h | 1 + src/initialize.cpp | 22 ++++++---------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index 47474c986b..d3d12d45df 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -13,6 +13,7 @@ void initialize_mpi(MPI_Comm intracomm); #endif bool read_model_xml(); void read_input_xml(); +void initial_output(); } // namespace openmc diff --git a/src/initialize.cpp b/src/initialize.cpp index 254ad88da5..aa3e15efa9 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -108,6 +108,9 @@ int openmc_init(int argc, char* argv[], const void* intracomm) // Read XML input files read_input_xml(); + // Write some initial output under the header if needed + initial_output(); + // Check for particle restart run if (settings::particle_restart_run) settings::run_mode = RunMode::PARTICLE; @@ -365,22 +368,6 @@ bool read_model_xml() { if (check_for_node(root, "plots")) read_plots_xml(root.child("plots")); - if (settings::run_mode == RunMode::PLOTTING) { - // Read plots.xml if it exists - if (mpi::master && settings::verbosity >= 5) - print_plot(); - - } else { - // Write summary information - if (mpi::master && settings::output_summary) - write_summary(); - - // Warn if overlap checking is on - if (mpi::master && settings::check_overlaps) { - warning("Cell overlap checking is ON."); - } - } - return true; } @@ -408,7 +395,10 @@ void read_input_xml() // Read the plots.xml regardless of plot mode in case plots are requested // via the API read_plots_xml(); +} +void initial_output() { + // handle some final output if (settings::run_mode == RunMode::PLOTTING) { // Read plots.xml if it exists if (mpi::master && settings::verbosity >= 5) From 9391f17cff9a798c048ec62b9eed3a9188baa534 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 22:29:36 -0500 Subject: [PATCH 171/265] Adding import for single or multiple XMLs --- openmc/geometry.py | 46 +++++++++++----- openmc/material.py | 40 ++++++++++---- openmc/model/model.py | 37 ++++++++++++- openmc/plots.py | 28 ++++++++-- openmc/settings.py | 119 ++++++++++++++++++++++++------------------ openmc/tallies.py | 72 +++++++++++++++---------- 6 files changed, 232 insertions(+), 110 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 889cb6b31c..b4f8046bd2 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -162,13 +162,13 @@ class Geometry: tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml(cls, path='geometry.xml', materials=None): - """Generate geometry from XML file + def from_xml_element(cls, elem, materials=None): + """Generate geometry from an XML element Parameters ---------- - path : str, optional - Path to geometry XML file + elem : xml.etree.ElementTree.Element + XML element materials : openmc.Materials or None Materials used to assign to cells. If None, an attempt is made to generate it from the materials.xml file. @@ -187,13 +187,10 @@ class Geometry: universes[univ_id] = univ return universes[univ_id] - tree = ET.parse(path) - root = tree.getroot() - # Get surfaces surfaces = {} periodic = {} - for surface in root.findall('surface'): + for surface in elem.findall('surface'): s = openmc.Surface.from_xml_element(surface) surfaces[s.id] = s @@ -207,15 +204,15 @@ class Geometry: surfaces[s1].periodic_surface = surfaces[s2] # Add any DAGMC universes - for elem in root.findall('dagmc_universe'): + for elem in elem.findall('dagmc_universe'): dag_univ = openmc.DAGMCUniverse.from_xml_element(elem) universes[dag_univ.id] = dag_univ # Dictionary that maps each universe to a list of cells/lattices that - # contain it (needed to determine which universe is the root) + # contain it (needed to determine which universe is the elem) child_of = defaultdict(list) - for elem in root.findall('lattice'): + for elem in elem.findall('lattice'): lat = openmc.RectLattice.from_xml_element(elem, get_universe) universes[lat.id] = lat if lat.outer is not None: @@ -223,7 +220,7 @@ class Geometry: for u in lat.universes.ravel(): child_of[u].append(lat) - for elem in root.findall('hex_lattice'): + for elem in elem.findall('hex_lattice'): lat = openmc.HexLattice.from_xml_element(elem, get_universe) universes[lat.id] = lat if lat.outer is not None: @@ -245,7 +242,7 @@ class Geometry: mats = {str(m.id): m for m in materials} mats['void'] = None - for elem in root.findall('cell'): + for elem in elem.findall('cell'): c = openmc.Cell.from_xml_element(elem, surfaces, mats, get_universe) if c.fill_type in ('universe', 'lattice'): child_of[c.fill].append(c) @@ -258,6 +255,29 @@ class Geometry: else: raise ValueError('Error determining root universe.') + @classmethod + def from_xml(cls, path='geometry.xml', materials=None): + """Generate geometry from XML file + + Parameters + ---------- + path : str, optional + Path to geometry XML file + materials : openmc.Materials or None + Materials used to assign to cells. If None, an attempt is made to + generate it from the materials.xml file. + + Returns + ------- + openmc.Geometry + Geometry object + + """ + tree = ET.parse(path) + root = tree.getroot() + + return cls.from_xml_element(root, materials) + def find(self, point): """Find cells/universes/lattices which contain a given point diff --git a/openmc/material.py b/openmc/material.py index 9e3951ccf4..c335968002 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1508,6 +1508,34 @@ class Materials(cv.CheckedList): errors='xmlcharrefreplace') as fh: self._write_xml(fh) + @classmethod + def from_xml_element(cls, elem): + """Generate materials collection from XML file + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Materials + Materials collection + + """ + # Generate each material + materials = cls() + for material in elem.findall('material'): + materials.append(Material.from_xml_element(material)) + + # Check for cross sections settings + xs = elem.find('cross_sections') + if xs is not None: + materials.cross_sections = xs.text + + return materials + + @classmethod def from_xml(cls, path: PathLike = 'materials.xml'): """Generate materials collection from XML file @@ -1526,14 +1554,4 @@ class Materials(cv.CheckedList): tree = ET.parse(path) root = tree.getroot() - # Generate each material - materials = cls() - for material in root.findall('material'): - materials.append(Material.from_xml_element(material)) - - # Check for cross sections settings - xs = tree.find('cross_sections') - if xs is not None: - materials.cross_sections = xs.text - - return materials + return cls.from_xml_element(root) \ No newline at end of file diff --git a/openmc/model/model.py b/openmc/model/model.py index e3c4c4ae19..2193084c36 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -208,7 +208,42 @@ class Model: self._plots.append(plot) @classmethod - def from_xml(cls, geometry='geometry.xml', materials='materials.xml', + def from_xml(cls, separate_xmls=True, **kwargs): + if separate_xmls: + return cls.from_separate_xmls(**kwargs) + else: + return cls.from_model_xml(**kwargs) + + @classmethod + def from_model_xml(cls, path='model.xml'): + """Create model from single XML file + + Parameters + ---------- + path : str or Pathlike + Path to model.xml file + """ + tree = ET.parse(path) + root = tree.getroot() + + model = cls() + + model.settings = openmc.Settings.from_xml_element(root.find('settings')) + model.materials = openmc.Materials.from_xml_element(root.find('materials')) + model.geometry = \ + openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) + + if tally_node := root.find('tallies'): + print(tally_node) + model.tallies = openmc.Tallies.from_xml_element(tally_node) + + if plots_node := root.find('plots'): + model.plots = openmc.Plots.from_xml_element(plots_node) + + return model + + @classmethod + def from_separate_xmls(cls, geometry='geometry.xml', materials='materials.xml', settings='settings.xml', tallies='tallies.xml', plots='plots.xml'): """Create model from existing XML files diff --git a/openmc/plots.py b/openmc/plots.py index 52dcf3fc13..badea2e586 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -941,6 +941,27 @@ class Plots(cv.CheckedList): tree = ET.ElementTree(self._plots_file) tree.write(str(p), xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml_element(cls, elem): + """Generate plots collection from XML file + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Plots + Plots collection + + """ + # Generate each plot + plots = cls() + for elem in elem.findall('plot'): + plots.append(Plot.from_xml_element(elem)) + return plots + @classmethod def from_xml(cls, path='plots.xml'): """Generate plots collection from XML file @@ -958,9 +979,6 @@ class Plots(cv.CheckedList): """ tree = ET.parse(path) root = tree.getroot() + return cls.from_xml_element(root) + - # Generate each plot - plots = cls() - for elem in root.findall('plot'): - plots.append(Plot.from_xml_element(elem)) - return plots diff --git a/openmc/settings.py b/openmc/settings.py index dd00413516..c2fca98f7d 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1595,7 +1595,7 @@ class Settings: clean_indentation(element) return element - + def export_to_xml(self, path: PathLike = 'settings.xml'): """Export simulation settings to an XML file. @@ -1617,6 +1617,71 @@ class Settings: tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml_element(cls, elem): + """Generate settings from XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Settings + Settings object + + """ + settings = cls() + settings._eigenvalue_from_xml_element(elem) + settings._run_mode_from_xml_element(elem) + settings._particles_from_xml_element(elem) + settings._batches_from_xml_element(elem) + settings._inactive_from_xml_element(elem) + settings._max_lost_particles_from_xml_element(elem) + settings._rel_max_lost_particles_from_xml_element(elem) + settings._generations_per_batch_from_xml_element(elem) + settings._keff_trigger_from_xml_element(elem) + settings._source_from_xml_element(elem) + settings._volume_calcs_from_xml_element(elem) + settings._output_from_xml_element(elem) + settings._statepoint_from_xml_element(elem) + settings._sourcepoint_from_xml_element(elem) + settings._surf_source_read_from_xml_element(elem) + settings._surf_source_write_from_xml_element(elem) + settings._confidence_intervals_from_xml_element(elem) + settings._electron_treatment_from_xml_element(elem) + settings._energy_mode_from_xml_element(elem) + settings._max_order_from_xml_element(elem) + settings._photon_transport_from_xml_element(elem) + settings._ptables_from_xml_element(elem) + settings._seed_from_xml_element(elem) + settings._survival_biasing_from_xml_element(elem) + settings._cutoff_from_xml_element(elem) + settings._entropy_mesh_from_xml_element(elem) + settings._trigger_from_xml_element(elem) + settings._no_reduce_from_xml_element(elem) + settings._verbosity_from_xml_element(elem) + settings._tabular_legendre_from_xml_element(elem) + settings._temperature_from_xml_element(elem) + settings._trace_from_xml_element(elem) + settings._track_from_xml_element(elem) + settings._ufs_mesh_from_xml_element(elem) + settings._resonance_scattering_from_xml_element(elem) + settings._create_fission_neutrons_from_xml_element(elem) + settings._delayed_photon_scaling_from_xml_element(elem) + settings._event_based_from_xml_element(elem) + settings._max_particles_in_flight_from_xml_element(elem) + settings._material_cell_offsets_from_xml_element(elem) + settings._log_grid_bins_from_xml_element(elem) + settings._write_initial_source_from_xml_element(elem) + settings._weight_windows_from_xml_element(elem) + settings._max_splits_from_xml_element(elem) + settings._max_tracks_from_xml_element(elem) + + # TODO: Get volume calculations + return settings + @classmethod def from_xml(cls, path: PathLike = 'settings.xml'): """Generate settings from XML file @@ -1636,54 +1701,4 @@ class Settings: """ tree = ET.parse(path) root = tree.getroot() - - settings = cls() - settings._eigenvalue_from_xml_element(root) - settings._run_mode_from_xml_element(root) - settings._particles_from_xml_element(root) - settings._batches_from_xml_element(root) - settings._inactive_from_xml_element(root) - settings._max_lost_particles_from_xml_element(root) - settings._rel_max_lost_particles_from_xml_element(root) - settings._generations_per_batch_from_xml_element(root) - settings._keff_trigger_from_xml_element(root) - settings._source_from_xml_element(root) - settings._volume_calcs_from_xml_element(root) - settings._output_from_xml_element(root) - settings._statepoint_from_xml_element(root) - settings._sourcepoint_from_xml_element(root) - settings._surf_source_read_from_xml_element(root) - settings._surf_source_write_from_xml_element(root) - settings._confidence_intervals_from_xml_element(root) - settings._electron_treatment_from_xml_element(root) - settings._energy_mode_from_xml_element(root) - settings._max_order_from_xml_element(root) - settings._photon_transport_from_xml_element(root) - settings._ptables_from_xml_element(root) - settings._seed_from_xml_element(root) - settings._survival_biasing_from_xml_element(root) - settings._cutoff_from_xml_element(root) - settings._entropy_mesh_from_xml_element(root) - settings._trigger_from_xml_element(root) - settings._no_reduce_from_xml_element(root) - settings._verbosity_from_xml_element(root) - settings._tabular_legendre_from_xml_element(root) - settings._temperature_from_xml_element(root) - settings._trace_from_xml_element(root) - settings._track_from_xml_element(root) - settings._ufs_mesh_from_xml_element(root) - settings._resonance_scattering_from_xml_element(root) - settings._create_fission_neutrons_from_xml_element(root) - settings._delayed_photon_scaling_from_xml_element(root) - settings._event_based_from_xml_element(root) - settings._max_particles_in_flight_from_xml_element(root) - settings._material_cell_offsets_from_xml_element(root) - settings._log_grid_bins_from_xml_element(root) - settings._write_initial_source_from_xml_element(root) - settings._weight_windows_from_xml_element(root) - settings._max_splits_from_xml_element(root) - settings._max_tracks_from_xml_element(root) - - # TODO: Get volume calculations - - return settings + return cls.from_xml_element(root) diff --git a/openmc/tallies.py b/openmc/tallies.py index dfb0c98bfd..0355568ec0 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3191,6 +3191,49 @@ class Tallies(cv.CheckedList): tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml_element(cls, elem): + """Generate tallies from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Tallies + Tallies object + + """ + # Read mesh elements + meshes = {} + for elem in elem.findall('mesh'): + mesh = MeshBase.from_xml_element(elem) + meshes[mesh.id] = mesh + + # Read filter elements + filters = {} + for elem in elem.findall('filter'): + filter = openmc.Filter.from_xml_element(elem, meshes=meshes) + filters[filter.id] = filter + + # Read derivative elements + derivatives = {} + for elem in elem.findall('derivative'): + deriv = openmc.TallyDerivative.from_xml_element(elem) + derivatives[deriv.id] = deriv + + # Read tally elements + tallies = [] + for elem in elem.findall('tally'): + tally = openmc.Tally.from_xml_element( + elem, filters=filters, derivatives=derivatives + ) + tallies.append(tally) + + return cls(tallies) + @classmethod def from_xml(cls, path='tallies.xml'): """Generate tallies from XML file @@ -3208,31 +3251,4 @@ class Tallies(cv.CheckedList): """ tree = ET.parse(path) root = tree.getroot() - - # Read mesh elements - meshes = {} - for elem in root.findall('mesh'): - mesh = MeshBase.from_xml_element(elem) - meshes[mesh.id] = mesh - - # Read filter elements - filters = {} - for elem in root.findall('filter'): - filter = openmc.Filter.from_xml_element(elem, meshes=meshes) - filters[filter.id] = filter - - # Read derivative elements - derivatives = {} - for elem in root.findall('derivative'): - deriv = openmc.TallyDerivative.from_xml_element(elem) - derivatives[deriv.id] = deriv - - # Read tally elements - tallies = [] - for elem in root.findall('tally'): - tally = openmc.Tally.from_xml_element( - elem, filters=filters, derivatives=derivatives - ) - tallies.append(tally) - - return cls(tallies) + return cls.from_xml_element(root) From af6b0af1e3ab2fc84224a12e55928cc0030a27d4 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 22:41:28 -0500 Subject: [PATCH 172/265] Fix loop variable overwrite --- openmc/tallies.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openmc/tallies.py b/openmc/tallies.py index 0355568ec0..2996f1b034 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3208,27 +3208,27 @@ class Tallies(cv.CheckedList): """ # Read mesh elements meshes = {} - for elem in elem.findall('mesh'): - mesh = MeshBase.from_xml_element(elem) + for e in elem.findall('mesh'): + mesh = MeshBase.from_xml_element(e) meshes[mesh.id] = mesh # Read filter elements filters = {} - for elem in elem.findall('filter'): - filter = openmc.Filter.from_xml_element(elem, meshes=meshes) + for e in elem.findall('filter'): + filter = openmc.Filter.from_xml_element(e, meshes=meshes) filters[filter.id] = filter # Read derivative elements derivatives = {} - for elem in elem.findall('derivative'): - deriv = openmc.TallyDerivative.from_xml_element(elem) + for e in elem.findall('derivative'): + deriv = openmc.TallyDerivative.from_xml_element(e) derivatives[deriv.id] = deriv # Read tally elements tallies = [] - for elem in elem.findall('tally'): + for e in elem.findall('tally'): tally = openmc.Tally.from_xml_element( - elem, filters=filters, derivatives=derivatives + e, filters=filters, derivatives=derivatives ) tallies.append(tally) From 5ac6e87a68c0666ef41ef1c6d9b60aa19500b1a5 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 23:15:14 -0500 Subject: [PATCH 173/265] Properly call read_plots_xml --- src/plot.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plot.cpp b/src/plot.cpp index e28543cdff..1a62fe2631 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -140,6 +140,8 @@ void read_plots_xml() { doc.load_file(filename.c_str()); pugi::xml_node root = doc.document_element(); + + read_plots_xml(root); } void read_plots_xml(pugi::xml_node root) From 2e716589a14d2465ab4a494e12ddf86172af72e3 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 3 Nov 2022 23:57:22 -0500 Subject: [PATCH 174/265] Other bug fixes --- openmc/geometry.py | 32 ++++++++++++++++---------------- openmc/model/model.py | 6 +++--- openmc/plots.py | 7 ++++--- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index b4f8046bd2..d036df9c74 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -204,24 +204,24 @@ class Geometry: surfaces[s1].periodic_surface = surfaces[s2] # Add any DAGMC universes - for elem in elem.findall('dagmc_universe'): - dag_univ = openmc.DAGMCUniverse.from_xml_element(elem) + for e in elem.findall('dagmc_universe'): + dag_univ = openmc.DAGMCUniverse.from_xml_element(e) universes[dag_univ.id] = dag_univ # Dictionary that maps each universe to a list of cells/lattices that # contain it (needed to determine which universe is the elem) child_of = defaultdict(list) - for elem in elem.findall('lattice'): - lat = openmc.RectLattice.from_xml_element(elem, get_universe) + for e in elem.findall('lattice'): + lat = openmc.RectLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: child_of[lat.outer].append(lat) for u in lat.universes.ravel(): child_of[u].append(lat) - for elem in elem.findall('hex_lattice'): - lat = openmc.HexLattice.from_xml_element(elem, get_universe) + for e in elem.findall('hex_lattice'): + lat = openmc.HexLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: child_of[lat.outer].append(lat) @@ -235,15 +235,8 @@ class Geometry: for u in ring: child_of[u].append(lat) - # Create dictionary to easily look up materials - if materials is None: - filename = Path(path).parent / 'materials.xml' - materials = openmc.Materials.from_xml(str(filename)) - mats = {str(m.id): m for m in materials} - mats['void'] = None - - for elem in elem.findall('cell'): - c = openmc.Cell.from_xml_element(elem, surfaces, mats, get_universe) + for e in elem.findall('cell'): + c = openmc.Cell.from_xml_element(e, surfaces, materials, get_universe) if c.fill_type in ('universe', 'lattice'): child_of[c.fill].append(c) @@ -276,7 +269,14 @@ class Geometry: tree = ET.parse(path) root = tree.getroot() - return cls.from_xml_element(root, materials) + # Create dictionary to easily look up materials + if materials is None: + filename = Path(path).parent / 'materials.xml' + materials = openmc.Materials.from_xml(str(filename)) + mats = {str(m.id): m for m in materials} + mats['void'] = None + + return cls.from_xml_element(root, mats) def find(self, point): """Find cells/universes/lattices which contain a given point diff --git a/openmc/model/model.py b/openmc/model/model.py index 2193084c36..a839dc8f0c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -208,11 +208,11 @@ class Model: self._plots.append(plot) @classmethod - def from_xml(cls, separate_xmls=True, **kwargs): + def from_xml(cls, *args, separate_xmls=True, **kwargs): if separate_xmls: - return cls.from_separate_xmls(**kwargs) + return cls.from_separate_xmls(*args, **kwargs) else: - return cls.from_model_xml(**kwargs) + return cls.from_model_xml(*args, **kwargs) @classmethod def from_model_xml(cls, path='model.xml'): diff --git a/openmc/plots.py b/openmc/plots.py index badea2e586..b1f1927876 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -919,6 +919,7 @@ class Plots(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(self._plots_file) + reorder_attributes(self._plots_file) # TODO: Remove when support is Python 3.8+ return self._plots_file @@ -936,8 +937,8 @@ class Plots(cv.CheckedList): if p.is_dir(): p /= 'plots.xml' + self.to_xml_element() # Write the XML Tree to the plots.xml file - reorder_attributes(self._plots_file) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(self._plots_file) tree.write(str(p), xml_declaration=True, encoding='utf-8') @@ -958,8 +959,8 @@ class Plots(cv.CheckedList): """ # Generate each plot plots = cls() - for elem in elem.findall('plot'): - plots.append(Plot.from_xml_element(elem)) + for e in elem.findall('plot'): + plots.append(Plot.from_xml_element(e)) return plots @classmethod From b4dcc30f92c4b8ff29b77505cdf654fa7eff7181 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 4 Nov 2022 00:58:43 -0500 Subject: [PATCH 175/265] Removing walrus operator --- openmc/model/model.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index a839dc8f0c..bc8f0b5318 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -233,12 +233,11 @@ class Model: model.geometry = \ openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) - if tally_node := root.find('tallies'): - print(tally_node) - model.tallies = openmc.Tallies.from_xml_element(tally_node) + if root.find('tallies'): + model.tallies = openmc.Tallies.from_xml_element(root.find('tallies')) - if plots_node := root.find('plots'): - model.plots = openmc.Plots.from_xml_element(plots_node) + if root.find('plots'): + model.plots = openmc.Plots.from_xml_element(root.find('plots')) return model From 611475a835be98beee0082727e94660b3fcd3000 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 4 Nov 2022 22:43:25 -0500 Subject: [PATCH 176/265] Restructureing input function calls a little --- src/initialize.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index aa3e15efa9..3f6e69ecde 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -106,7 +106,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm) openmc::openmc_set_seed(DEFAULT_SEED); // Read XML input files - read_input_xml(); + if (!read_model_xml()) read_input_xml(); // Write some initial output under the header if needed initial_output(); @@ -373,9 +373,6 @@ bool read_model_xml() { void read_input_xml() { - // attempt to reach the model.xml file if present - if (read_model_xml()) return; - read_settings_xml(); read_cross_sections_xml(); read_materials_xml(); From 021bb280c9b4cad713ecdd516c9ef8b9ee97bd2c Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 4 Nov 2022 22:45:37 -0500 Subject: [PATCH 177/265] Adding initial test for exporting model.xmls. Correcting material maps --- openmc/model/model.py | 3 ++- tests/unit_tests/test_model.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index bc8f0b5318..6950642e0a 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -230,8 +230,9 @@ class Model: model.settings = openmc.Settings.from_xml_element(root.find('settings')) model.materials = openmc.Materials.from_xml_element(root.find('materials')) + materials = {str(m.id): m for m in model.materials} model.geometry = \ - openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) + openmc.Geometry.from_xml_element(root.find('geometry'), materials) if root.find('tallies'): model.tallies = openmc.Tallies.from_xml_element(root.find('tallies')) diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 9b05ed2a5c..ce81b00eec 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -7,6 +7,8 @@ import pytest import openmc import openmc.lib +from .. import cdtemp + @pytest.fixture(scope='function') def pin_model_attributes(): @@ -529,3 +531,22 @@ def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert openmc.lib.materials[3].volume == mats[2].volume test_model.finalize_lib() + +def test_model_xml(): + + with cdtemp(): + # load a model from examples + pwr_model = openmc.examples.pwr_core() + + # export to separate XMLs manually + pwr_model.settings.export_to_xml('settings_ref.xml') + pwr_model.materials.export_to_xml('materials_ref.xml') + pwr_model.geometry.export_to_xml('geometry_ref.xml') + + # now write and read a model.xml file + pwr_model.export_to_xml(separate_xmls=False) + new_model = openmc.Model.from_xml(separate_xmls=False) + + # make sure we can also export this again to separate + # XML files + new_model.export_to_xml(separate_xmls=True) \ No newline at end of file From abd3d515b0906363ce1975ffadd983c6c5a86f2b Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 4 Nov 2022 22:46:11 -0500 Subject: [PATCH 178/265] Correcting doc strings for material mapping sent to --- openmc/cell.py | 2 +- openmc/geometry.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 0cea73b324..d03052623d 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -650,7 +650,7 @@ class Cell(IDManagerMixin): surfaces : dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances materials : dict - Dictionary mapping material IDs to :class:`openmc.Material` + Dictionary mapping material ID strings to :class:`openmc.Material` instances (defined in :math:`openmc.Geometry.from_xml`) get_universe : function Function returning universe (defined in diff --git a/openmc/geometry.py b/openmc/geometry.py index d036df9c74..57aab18846 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -256,9 +256,9 @@ class Geometry: ---------- path : str, optional Path to geometry XML file - materials : openmc.Materials or None - Materials used to assign to cells. If None, an attempt is made to - generate it from the materials.xml file. + materials : dict + Dictionary mapping material ID strings to :class:`openmc.Material` + instances (defined in :math:`openmc.Geometry.from_xml`) Returns ------- From 662d83a7dd72240b4da8594319bab416977ec71b Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 9 Nov 2022 23:29:17 -0600 Subject: [PATCH 179/265] Running some existing regression tests using single XML file --- .../adj_cell_rotation/__init__.py | 2 + .../regression_tests/energy_laws/__init__.py | 1 + .../lattice_multiple/__init__.py | 1 + tests/regression_tests/model_xml/__init__.py | 0 .../model_xml/inputs_true.dat | 38 ++++++++++ tests/regression_tests/model_xml/test.py | 76 +++++++++++++++++++ .../photon_production/__init__.py | 1 + 7 files changed, 119 insertions(+) create mode 100644 tests/regression_tests/model_xml/__init__.py create mode 100644 tests/regression_tests/model_xml/inputs_true.dat create mode 100644 tests/regression_tests/model_xml/test.py diff --git a/tests/regression_tests/adj_cell_rotation/__init__.py b/tests/regression_tests/adj_cell_rotation/__init__.py index e69de29bb2..7efe448659 100644 --- a/tests/regression_tests/adj_cell_rotation/__init__.py +++ b/tests/regression_tests/adj_cell_rotation/__init__.py @@ -0,0 +1,2 @@ + +from .test import model \ No newline at end of file diff --git a/tests/regression_tests/energy_laws/__init__.py b/tests/regression_tests/energy_laws/__init__.py index e69de29bb2..29fbfcc0c2 100644 --- a/tests/regression_tests/energy_laws/__init__.py +++ b/tests/regression_tests/energy_laws/__init__.py @@ -0,0 +1 @@ +from .test import model \ No newline at end of file diff --git a/tests/regression_tests/lattice_multiple/__init__.py b/tests/regression_tests/lattice_multiple/__init__.py index e69de29bb2..29fbfcc0c2 100644 --- a/tests/regression_tests/lattice_multiple/__init__.py +++ b/tests/regression_tests/lattice_multiple/__init__.py @@ -0,0 +1 @@ +from .test import model \ No newline at end of file diff --git a/tests/regression_tests/model_xml/__init__.py b/tests/regression_tests/model_xml/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/model_xml/inputs_true.dat b/tests/regression_tests/model_xml/inputs_true.dat new file mode 100644 index 0000000000..5aedbfa143 --- /dev/null +++ b/tests/regression_tests/model_xml/inputs_true.dat @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py new file mode 100644 index 0000000000..448079b3f6 --- /dev/null +++ b/tests/regression_tests/model_xml/test.py @@ -0,0 +1,76 @@ +from difflib import unified_diff +import filecmp +import os + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness, colorize + +# use a few models from other tests to make sure the same results are +# produced when using a single model.xml file as input +from ..adj_cell_rotation import model as adj_cell_rotation_model +from ..lattice_multiple import model as lattice_mult_model +from ..energy_laws import model as energy_laws_model +from ..photon_production import model as photon_prod_model + + +class ModelXMLTestHarness(PyAPITestHarness): + """Accept a results file to check against and assume inputs_true is the contents of a model.xml file. + """ + def __init__(self, model=None, inputs_true=None, results_true=None): + statepoint_name = f'statepoint.{model.settings.batches}.h5' + super().__init__(statepoint_name, model, inputs_true) + + self.results_true = 'results_true.dat' if results_true is None else results_true + + def _build_inputs(self): + self._model.export_to_xml(separate_xmls=False) + + def _get_inputs(self): + return open('model.xml').read() + + def _compare_inputs(self): + """Skip input comparisons for now + """ + pass + + def _compare_results(self): + """Make sure the current results agree with the reference.""" + compare = filecmp.cmp('results_test.dat', self.results_true) + if not compare: + expected = open(self.results_true).readlines() + actual = open('results_test.dat').readlines() + diff = unified_diff(expected, actual, self.results_true, + 'results_test.dat') + print('Result differences:') + print(''.join(colorize(diff))) + os.rename('results_test.dat', 'results_error.dat') + assert compare, 'Results do not agree' + + def _cleanup(self): + super()._cleanup() + if os.path.exists('model.xml'): + os.remove('model.xml') + + +models = [ +'adj_cell_rotation_model', +'lattice_mult_model', +'energy_laws_model', +'photon_prod_model' +] +paths = [ +'../adj_cell_rotation', +'../lattice_multiple', +'../energy_laws', +'../photon_production' +] + + +@pytest.mark.parametrize("model, path", zip(models, paths)) +def test_model_xml(model, path, request): + inputs = path + "/inputs_true.dat" + results = path + "/results_true.dat" + harness = ModelXMLTestHarness(request.getfixturevalue(model), inputs, results) + harness.main() \ No newline at end of file diff --git a/tests/regression_tests/photon_production/__init__.py b/tests/regression_tests/photon_production/__init__.py index e69de29bb2..29fbfcc0c2 100644 --- a/tests/regression_tests/photon_production/__init__.py +++ b/tests/regression_tests/photon_production/__init__.py @@ -0,0 +1 @@ +from .test import model \ No newline at end of file From 1e7dbe99cf7d6c262a9900930836f5786f519f9a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 10 Nov 2022 00:01:40 -0600 Subject: [PATCH 180/265] Making sure meshes are only written once to a model.xml file --- openmc/model/model.py | 18 ++++++++++++------ openmc/settings.py | 15 ++++++++++----- openmc/tallies.py | 24 ++++++++++++------------ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 6950642e0a..cb84f2c17f 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -5,11 +5,8 @@ import os from pathlib import Path from numbers import Integral from tempfile import NamedTemporaryFile -<<<<<<< HEAD import warnings -======= from xml.etree import ElementTree as ET ->>>>>>> d42935a08 (Writing all main nodes to a single XML file) import h5py @@ -234,8 +231,16 @@ class Model: model.geometry = \ openmc.Geometry.from_xml_element(root.find('geometry'), materials) + # gather meshses from other classes before reading the tally node + meshes = {} + if model.settings.entropy_mesh is not None: + meshes[model.settings.entropy_mesh.id] = model.settings.entropy_mesh + + for ww in model.settings.weight_windows: + meshes[ww.mesh.id] = ww.mesh + if root.find('tallies'): - model.tallies = openmc.Tallies.from_xml_element(root.find('tallies')) + model.tallies = openmc.Tallies.from_xml_element(root.find('tallies'), meshes) if root.find('plots'): model.plots = openmc.Plots.from_xml_element(root.find('plots')) @@ -524,7 +529,8 @@ class Model: # Can be used to modify tallies in case any surfaces are redundant redundant_surfaces = self.geometry.remove_redundant_surfaces() - settings_element = self.settings.to_xml_element() + memo = set() + settings_element = self.settings.to_xml_element(memo) geometry_element = self.geometry.to_xml_element() # If a materials collection was specified, export it. Otherwise, look @@ -549,7 +555,7 @@ class Model: ET.ElementTree(settings_element).write(fh, encoding='unicode') if self.tallies: - tallies_element = self.tallies.to_xml_element() + tallies_element = self.tallies.to_xml_element(memo) ET.ElementTree(tallies_element).write(fh, encoding='unicode') if self.plots: plots_element = self.plots.to_xml_element() diff --git a/openmc/settings.py b/openmc/settings.py index c2fca98f7d..74ec0d54a6 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1073,7 +1073,7 @@ class Settings: subelement = ET.SubElement(element, key) subelement.text = str(value) - def _create_entropy_mesh_subelement(self, root): + def _create_entropy_mesh_subelement(self, root, memo=None): if self.entropy_mesh is not None: # use default heuristic for entropy mesh if not set by user if self.entropy_mesh.dimension is None: @@ -1207,15 +1207,20 @@ class Settings: elem = ET.SubElement(root, "write_initial_source") elem.text = str(self._write_initial_source).lower() - def _create_weight_windows_subelement(self, root): + def _create_weight_windows_subelement(self, root, memo=None): for ww in self._weight_windows: # Add weight window information root.append(ww.to_xml_element()) + # check the memo for a mesh + if memo and ww.mesh.id in memo: + continue + # See if a element already exists -- if not, add it path = f"./mesh[@id='{ww.mesh.id}']" if root.find(path) is None: root.append(ww.mesh.to_xml_element()) + if memo is not None: memo.add(ww.mesh.id) if self._weight_windows_on is not None: elem = ET.SubElement(root, "weight_windows_on") @@ -1539,7 +1544,7 @@ class Settings: if text is not None: self.max_tracks = int(text) - def to_xml_element(self): + def to_xml_element(self, memo=None): """Create a 'settings' element to be written to an XML file. """ @@ -1569,7 +1574,7 @@ class Settings: self._create_seed_subelement(element) self._create_survival_biasing_subelement(element) self._create_cutoff_subelement(element) - self._create_entropy_mesh_subelement(element) + self._create_entropy_mesh_subelement(element, memo) self._create_trigger_subelement(element) self._create_no_reduce_subelement(element) self._create_verbosity_subelement(element) @@ -1587,7 +1592,7 @@ class Settings: self._create_material_cell_offsets_subelement(element) self._create_log_grid_bins_subelement(element) self._create_write_initial_source_subelement(element) - self._create_weight_windows_subelement(element) + self._create_weight_windows_subelement(element, memo) self._create_max_splits_subelement(element) self._create_max_tracks_subelement(element) diff --git a/openmc/tallies.py b/openmc/tallies.py index 2996f1b034..e37f04e252 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3119,17 +3119,17 @@ class Tallies(cv.CheckedList): for tally in self: root_element.append(tally.to_xml_element()) - def _create_mesh_subelements(self, root_element): - already_written = set() + def _create_mesh_subelements(self, root_element, memo=None): + already_written = memo if memo else set() for tally in self: for f in tally.filters: if isinstance(f, openmc.MeshFilter): - if f.mesh.id not in already_written: - if len(f.mesh.name) > 0: - root_element.append(ET.Comment(f.mesh.name)) - - root_element.append(f.mesh.to_xml_element()) - already_written.add(f.mesh.id) + if f.mesh.id in already_written: + continue + if len(f.mesh.name) > 0: + root_element.append(ET.Comment(f.mesh.name)) + root_element.append(f.mesh.to_xml_element()) + already_written.add(f.mesh.id) def _create_filter_subelements(self, root_element): already_written = dict() @@ -3155,11 +3155,11 @@ class Tallies(cv.CheckedList): for d in derivs: root_element.append(d.to_xml_element()) - def to_xml_element(self): + def to_xml_element(self, memo=None): """Creates a 'tallies' element to be written to an XML file. """ element = ET.Element("tallies") - self._create_mesh_subelements(element) + self._create_mesh_subelements(element, memo) self._create_filter_subelements(element) self._create_tally_subelements(element) self._create_derivative_subelements(element) @@ -3192,7 +3192,7 @@ class Tallies(cv.CheckedList): tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem, meshes=None): """Generate tallies from an XML element Parameters @@ -3207,7 +3207,7 @@ class Tallies(cv.CheckedList): """ # Read mesh elements - meshes = {} + meshes = {} if meshes is None else meshes for e in elem.findall('mesh'): mesh = MeshBase.from_xml_element(e) meshes[mesh.id] = mesh From d034e1de9c8fe166f38624ee872b8bc46643c66a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 21:23:36 -0600 Subject: [PATCH 181/265] Apply suggestions from code review Incorporating suggestions from @paulromano Co-authored-by: Paul Romano --- openmc/geometry.py | 2 +- openmc/model/model.py | 6 ++---- tests/regression_tests/model_xml/test.py | 16 ++++++++-------- tests/unit_tests/test_model.py | 2 -- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 57aab18846..630106628d 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -256,7 +256,7 @@ class Geometry: ---------- path : str, optional Path to geometry XML file - materials : dict + materials : dict Dictionary mapping material ID strings to :class:`openmc.Material` instances (defined in :math:`openmc.Geometry.from_xml`) diff --git a/openmc/model/model.py b/openmc/model/model.py index cb84f2c17f..559def5702 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -228,8 +228,7 @@ class Model: model.settings = openmc.Settings.from_xml_element(root.find('settings')) model.materials = openmc.Materials.from_xml_element(root.find('materials')) materials = {str(m.id): m for m in model.materials} - model.geometry = \ - openmc.Geometry.from_xml_element(root.find('geometry'), materials) + model.geometry = .Geometry.from_xml_element(root.find('geometry'), materials) # gather meshses from other classes before reading the tally node meshes = {} @@ -542,8 +541,7 @@ class Model: materials = openmc.Materials(self.geometry.get_all_materials() .values()) - with open(d, 'w', encoding='utf-8', - errors='xmlcharrefreplace') as fh: + with open(d, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: # write the XML header fh.write("\n") fh.write("\n") diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 448079b3f6..289bd46552 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -55,16 +55,16 @@ class ModelXMLTestHarness(PyAPITestHarness): models = [ -'adj_cell_rotation_model', -'lattice_mult_model', -'energy_laws_model', -'photon_prod_model' + 'adj_cell_rotation_model', + 'lattice_mult_model', + 'energy_laws_model', + 'photon_prod_model' ] paths = [ -'../adj_cell_rotation', -'../lattice_multiple', -'../energy_laws', -'../photon_production' + '../adj_cell_rotation', + '../lattice_multiple', + '../energy_laws', + '../photon_production' ] diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index ce81b00eec..928bf0715f 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -7,8 +7,6 @@ import pytest import openmc import openmc.lib -from .. import cdtemp - @pytest.fixture(scope='function') def pin_model_attributes(): From b76825fa9f0598ca563d1c9d579968aa7bffa370 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 22:16:34 -0600 Subject: [PATCH 182/265] Addressing some comments from PR --- include/openmc/initialize.h | 6 ++++- openmc/geometry.py | 6 ++--- openmc/model/model.py | 49 ++++++++++++++++++++++++---------- openmc/plots.py | 6 +++++ src/initialize.cpp | 4 +-- tests/unit_tests/test_model.py | 27 +++++++++---------- 6 files changed, 64 insertions(+), 34 deletions(-) diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index d3d12d45df..5caccceda6 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -11,8 +11,12 @@ int parse_command_line(int argc, char* argv[]); #ifdef OPENMC_MPI void initialize_mpi(MPI_Comm intracomm); #endif + +//! Read material, geometry, settings, and tallies from a single XML file bool read_model_xml(); -void read_input_xml(); +//! Read inputs from separate XML files +void read_separate_xml_files(); +//! Write some output that occurs right after initialization void initial_output(); } // namespace openmc diff --git a/openmc/geometry.py b/openmc/geometry.py index 630106628d..d036df9c74 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -256,9 +256,9 @@ class Geometry: ---------- path : str, optional Path to geometry XML file - materials : dict - Dictionary mapping material ID strings to :class:`openmc.Material` - instances (defined in :math:`openmc.Geometry.from_xml`) + materials : openmc.Materials or None + Materials used to assign to cells. If None, an attempt is made to + generate it from the materials.xml file. Returns ------- diff --git a/openmc/model/model.py b/openmc/model/model.py index 559def5702..2ed9bf668a 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -205,8 +205,24 @@ class Model: self._plots.append(plot) @classmethod - def from_xml(cls, *args, separate_xmls=True, **kwargs): - if separate_xmls: + def from_xml(cls, *args, path='model.xml', separate_xmls=True, **kwargs): + """Generate geometry from XML file + + Parameters + ---------- + path : str, optional + Path to model XML file + separate_xmls : bool + Whether or not to read from a single or separate XML files + + Returns + ------- + openmc.Geometry + Geometry object + + """ + + if separate_xmls or not Path(path).exists(): return cls.from_separate_xmls(*args, **kwargs) else: return cls.from_model_xml(*args, **kwargs) @@ -228,7 +244,7 @@ class Model: model.settings = openmc.Settings.from_xml_element(root.find('settings')) model.materials = openmc.Materials.from_xml_element(root.find('materials')) materials = {str(m.id): m for m in model.materials} - model.geometry = .Geometry.from_xml_element(root.find('geometry'), materials) + model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), materials) # gather meshses from other classes before reading the tally node meshes = {} @@ -442,8 +458,8 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True): - """Export model to separate XML files. + def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True, filename='model.xml'): + """Export model to an XML file(s). Parameters ---------- @@ -453,6 +469,8 @@ class Model: remove_surfs : bool Whether or not to remove redundant surfaces from the geometry when exporting. + filename : str + Name of the single XML file to create (only used :math:`separate_xmls` if False) .. versionadded:: 0.13.1 @@ -460,11 +478,15 @@ class Model: Whether or not to write a single model.xml file or many XML files. """ if separate_xmls: - self.export_to_separate_xmls(directory, remove_surfs) + if filename != 'model.xml': + warnings.warn('Export filename parameter {filename} is ignored ' + 'because the model is being written to separate XML files.') + self._export_to_separate_xmls(directory, remove_surfs) else: - self.export_to_single_xml(directory, remove_surfs) + filename = Path(directory) / Path(filename) + self._export_to_single_xml(filename, remove_surfs) - def export_to_separate_xmls(self, directory='.', remove_surfs=False): + def _export_to_separate_xmls(self, directory='.', remove_surfs=False): """Export model to separate XML files. Parameters @@ -501,7 +523,7 @@ class Model: if self.plots: self.plots.export_to_xml(d) - def export_to_single_xml(self, directory='.', remove_surfs=False): + def _export_to_single_xml(self, path='model.xml', remove_surfs=False): """Export model to XML files. Parameters @@ -516,10 +538,9 @@ class Model: .. versionadded:: 0.13.1 """ # Create directory if required - d = Path(directory) - if not d.is_dir(): - d.mkdir(parents=True) - d /= 'model.xml' + xml_path = Path(path) + if not xml_path.parent.exists: + raise RuntimeError(f'The directory "{xml_path}" does not exist.') if remove_surfs: warnings.warn("remove_surfs kwarg will be deprecated soon, please " @@ -541,7 +562,7 @@ class Model: materials = openmc.Materials(self.geometry.get_all_materials() .values()) - with open(d, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: + with open(xml_path, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: # write the XML header fh.write("\n") fh.write("\n") diff --git a/openmc/plots.py b/openmc/plots.py index b1f1927876..0a04516259 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -911,6 +911,12 @@ class Plots(cv.CheckedList): def to_xml_element(self): """Create a 'plots' element to be written to an XML file. + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing all plot elements + """ # Reset xml element tree self._plots_file.clear() diff --git a/src/initialize.cpp b/src/initialize.cpp index 3f6e69ecde..dc5c0c3c11 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -106,7 +106,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm) openmc::openmc_set_seed(DEFAULT_SEED); // Read XML input files - if (!read_model_xml()) read_input_xml(); + if (!read_model_xml()) read_separate_xml_files(); // Write some initial output under the header if needed initial_output(); @@ -371,7 +371,7 @@ bool read_model_xml() { return true; } -void read_input_xml() +void read_separate_xml_files() { read_settings_xml(); read_cross_sections_xml(); diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 928bf0715f..87557ddc53 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -530,21 +530,20 @@ def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model.finalize_lib() -def test_model_xml(): +def test_model_xml(run_in_tmpdir): - with cdtemp(): - # load a model from examples - pwr_model = openmc.examples.pwr_core() + # load a model from examples + pwr_model = openmc.examples.pwr_core() - # export to separate XMLs manually - pwr_model.settings.export_to_xml('settings_ref.xml') - pwr_model.materials.export_to_xml('materials_ref.xml') - pwr_model.geometry.export_to_xml('geometry_ref.xml') + # export to separate XMLs manually + pwr_model.settings.export_to_xml('settings_ref.xml') + pwr_model.materials.export_to_xml('materials_ref.xml') + pwr_model.geometry.export_to_xml('geometry_ref.xml') - # now write and read a model.xml file - pwr_model.export_to_xml(separate_xmls=False) - new_model = openmc.Model.from_xml(separate_xmls=False) + # now write and read a model.xml file + pwr_model.export_to_xml(separate_xmls=False) + new_model = openmc.Model.from_xml(separate_xmls=False) - # make sure we can also export this again to separate - # XML files - new_model.export_to_xml(separate_xmls=True) \ No newline at end of file + # make sure we can also export this again to separate + # XML files + new_model.export_to_xml(separate_xmls=True) \ No newline at end of file From d9847163e86a4131016a1a6a5f0cd7c0e12b34cc Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 23:06:31 -0600 Subject: [PATCH 183/265] Adding ability to specify an input filename to the executable or to the python exec --- include/openmc/initialize.h | 4 +++ openmc/executor.py | 37 +++++++++++++++++++----- openmc/model/model.py | 1 - src/initialize.cpp | 24 +++++++++++---- tests/regression_tests/model_xml/test.py | 22 +++++++++++++- 5 files changed, 73 insertions(+), 15 deletions(-) diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index 5caccceda6..c37208e5e3 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -1,6 +1,8 @@ #ifndef OPENMC_INITIALIZE_H #define OPENMC_INITIALIZE_H +#include + #ifdef OPENMC_MPI #include "mpi.h" #endif @@ -19,6 +21,8 @@ void read_separate_xml_files(); //! Write some output that occurs right after initialization void initial_output(); + +std::string args_xml_filename {}; } // namespace openmc #endif // OPENMC_INITIALIZE_H diff --git a/openmc/executor.py b/openmc/executor.py index a73d896ffe..b143132139 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -9,7 +9,7 @@ from .plots import _get_plot_image def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, plot=False, restart_file=None, threads=None, tracks=False, event_based=None, - openmc_exec='openmc', mpi_args=None): + openmc_exec='openmc', mpi_args=None, input_file=None): """Converts user-readable flags in to command-line arguments to be run with the OpenMC executable via subprocess. @@ -42,6 +42,8 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, mpi_args : list of str, optional MPI execute command and any additional MPI arguments to pass, e.g. ['mpiexec', '-n', '8']. + input_file : str or Pathlike + Name of a single XML input file for the OpenMC executable to read. .. versionadded:: 0.13.0 @@ -82,6 +84,9 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, if mpi_args is not None: args = mpi_args + args + if input_file is not None: + args += ['-i', input_file] + return args @@ -118,7 +123,7 @@ def _run(args, output, cwd): raise RuntimeError(error_msg) -def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): +def plot_geometry(output=True, openmc_exec='openmc', cwd='.', input_file=None): """Run OpenMC in plotting mode Parameters @@ -129,6 +134,8 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): Path to OpenMC executable cwd : str, optional Path to working directory to run in + input_file : str + Name of a single XML input file for the OpenMC executable to read. Raises ------ @@ -136,10 +143,13 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): If the `openmc` executable returns a non-zero status """ + args = [openmc_exec, '-p'] + if input_file is not None: + args += ['-i', input_file] _run([openmc_exec, '-p'], output, cwd) -def plot_inline(plots, openmc_exec='openmc', cwd='.'): +def plot_inline(plots, openmc_exec='openmc', cwd='.', input_file=None): """Display plots inline in a Jupyter notebook. .. versionchanged:: 0.13.0 @@ -155,6 +165,8 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): Path to OpenMC executable cwd : str, optional Path to working directory to run in + input_file : str + Name of a single XML input file for the OpenMC executable to read. Raises ------ @@ -171,7 +183,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): openmc.Plots(plots).export_to_xml(cwd) # Run OpenMC in geometry plotting mode - plot_geometry(False, openmc_exec, cwd) + plot_geometry(False, openmc_exec, cwd, input_file) if plots is not None: images = [_get_plot_image(p, cwd) for p in plots] @@ -179,7 +191,8 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): def calculate_volumes(threads=None, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None): + openmc_exec='openmc', mpi_args=None, + input_file=None): """Run stochastic volume calculations in OpenMC. This function runs OpenMC in stochastic volume calculation mode. To specify @@ -210,6 +223,8 @@ def calculate_volumes(threads=None, output=True, cwd='.', cwd : str, optional Path to working directory to run in. Defaults to the current working directory. + input_file : str or Pathlike + Name of a single XML input file for the OpenMC executable to read. Raises ------ @@ -223,14 +238,16 @@ def calculate_volumes(threads=None, output=True, cwd='.', """ args = _process_CLI_arguments(volume=True, threads=threads, - openmc_exec=openmc_exec, mpi_args=mpi_args) + openmc_exec=openmc_exec, mpi_args=mpi_args, + input_file=input_file) _run(args, output, cwd) def run(particles=None, threads=None, geometry_debug=False, restart_file=None, tracks=False, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, event_based=False): + openmc_exec='openmc', mpi_args=None, event_based=False, + input_file=None): """Run an OpenMC simulation. Parameters @@ -265,6 +282,9 @@ def run(particles=None, threads=None, geometry_debug=False, .. versionadded:: 0.12 + input_file : str or Pathlike + Name of a single XML input file for the OpenMC executable to read. + Raises ------ RuntimeError @@ -275,6 +295,7 @@ def run(particles=None, threads=None, geometry_debug=False, args = _process_CLI_arguments( volume=False, geometry_debug=geometry_debug, particles=particles, restart_file=restart_file, threads=threads, tracks=tracks, - event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args) + event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args, + input_file=input_file) _run(args, output, cwd) diff --git a/openmc/model/model.py b/openmc/model/model.py index 2ed9bf668a..15effd3e4c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -221,7 +221,6 @@ class Model: Geometry object """ - if separate_xmls or not Path(path).exists(): return cls.from_separate_xmls(*args, **kwargs) else: diff --git a/src/initialize.cpp b/src/initialize.cpp index dc5c0c3c11..02a4f9ab75 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -181,10 +181,11 @@ int parse_command_line(int argc, char* argv[]) } else if (arg == "-e" || arg == "--event") { settings::event_based = true; - + } else if (arg == "-i" || arg == "--input") { + i += 1; + args_xml_filename = std::string(argv[i]); } else if (arg == "-r" || arg == "--restart") { i += 1; - // Check what type of file this is hid_t file_id = file_open(argv[i], 'r', true); std::string filetype; @@ -295,9 +296,19 @@ int parse_command_line(int argc, char* argv[]) bool read_model_xml() { // get verbosity from settings node - std::string model_filename = settings::path_input + "model.xml"; - bool use_model_file = file_exists(model_filename); - if (!file_exists(model_filename)) return false; + + std::string xml_filename = "model.xml"; + if (!args_xml_filename.empty()) xml_filename = args_xml_filename; + std::string model_filename = settings::path_input + xml_filename; + + // check that the model file exists. If it does not and a custom filename was + // supplied by the user report an error + if (!file_exists(model_filename)) { + if (!args_xml_filename.empty()) { + fatal_error(fmt::format("The input file '{}' specified on the command line does not exist", args_xml_filename)); + } + return false; + } // attempt to open the document pugi::xml_document doc; @@ -325,6 +336,9 @@ bool read_model_xml() { title(); } + if (!args_xml_filename.empty()) + write_message(fmt::format("Reaging user-specified input '{}'...", args_xml_filename), 5); + else write_message("Reading model XML file...", 5); read_settings_xml(settings_root); diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 289bd46552..342d7dca90 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -73,4 +73,24 @@ def test_model_xml(model, path, request): inputs = path + "/inputs_true.dat" results = path + "/results_true.dat" harness = ModelXMLTestHarness(request.getfixturevalue(model), inputs, results) - harness.main() \ No newline at end of file + harness.main() + +def test_input_arg(run_in_tmpdir): + + pincell = openmc.examples.pwr_pin_cell() + + pincell.settings.particles = 100 + + # export to separate XML files and run + pincell.export_to_xml(separate_xmls=True) + openmc.run() + + # now export to a single XML file with a custom name + pincell.export_to_xml(filename='pincell.xml', separate_xmls=False) + + # run by specifying that single file + openmc.run(input_file='pincell.xml') + + # now ensure we get an error for an incorrect filename + with pytest.raises(RuntimeError): + openmc.run(input_file='ex-em-ell.xml') \ No newline at end of file From 366f9e905c4f049e8971f743b6cca5d210fff04e Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 23:10:57 -0600 Subject: [PATCH 184/265] Test comment and updated error messages --- src/initialize.cpp | 8 +++++--- tests/regression_tests/model_xml/test.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 02a4f9ab75..68436f07e4 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -348,14 +348,15 @@ bool read_model_xml() { auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; for (const auto& input : other_inputs) { if (file_exists(settings::path_input + input)) { - warning(("Other XML file input(s) are present. These file will be ignored in favor of the model.xml file.")); + warning((fmt::format("Other XML file input(s) are present. These file will be ignored in favor of the {} file.", xml_filename)); break; } } // Read materials and cross sections if (!check_for_node(root, "materials")) { - fatal_error("No node present in the model.xml file."); + fatal_error( + fmt::format("No node present in the {} file.", xml_filename)); } auto materials_node = root.child("materials"); read_cross_sections_xml(materials_node); @@ -363,7 +364,8 @@ bool read_model_xml() { // Read geometry if (!check_for_node(root, "geometry")) { - fatal_error("No node present in the model.xml_file."); + fatal_error(fmt::format( + "No node present in the model.xml_file.", xml_filename)); } read_geometry_xml(root.child("geometry")); diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 342d7dca90..5bfe912a44 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -91,6 +91,7 @@ def test_input_arg(run_in_tmpdir): # run by specifying that single file openmc.run(input_file='pincell.xml') - # now ensure we get an error for an incorrect filename + # now ensure we get an error for an incorrect filename, + # even in the presence of other, valid XML files with pytest.raises(RuntimeError): openmc.run(input_file='ex-em-ell.xml') \ No newline at end of file From 1e6cb68ea420558f6f120e830602b8938f2c1319 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 23:14:30 -0600 Subject: [PATCH 185/265] Addressing a few more comments in C++ code --- src/initialize.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 68436f07e4..87e839dfdf 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -358,8 +358,12 @@ bool read_model_xml() { fatal_error( fmt::format("No node present in the {} file.", xml_filename)); } + + // find materials node this object cannot be used after being passed to the + // cross section reading function below auto materials_node = root.child("materials"); read_cross_sections_xml(materials_node); + read_materials_xml(root.child("materials")); // Read geometry @@ -411,7 +415,7 @@ void read_separate_xml_files() } void initial_output() { - // handle some final output + // write initial output if (settings::run_mode == RunMode::PLOTTING) { // Read plots.xml if it exists if (mpi::master && settings::verbosity >= 5) From 7a203fbf7266f365d3d209461687402edcb3b56b Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 23:16:57 -0600 Subject: [PATCH 186/265] Correcting parenthesis --- src/initialize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 87e839dfdf..a6a6b5a776 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -348,7 +348,7 @@ bool read_model_xml() { auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; for (const auto& input : other_inputs) { if (file_exists(settings::path_input + input)) { - warning((fmt::format("Other XML file input(s) are present. These file will be ignored in favor of the {} file.", xml_filename)); + warning((fmt::format("Other XML file input(s) are present. These file will be ignored in favor of the {} file.", xml_filename))); break; } } From fe47d565fd7fddb464e3e67907936211bc76a857 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 23 Nov 2022 23:17:11 -0600 Subject: [PATCH 187/265] Spot check in error message from failed run --- tests/regression_tests/model_xml/test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 5bfe912a44..0dc2cc2898 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -93,5 +93,6 @@ def test_input_arg(run_in_tmpdir): # now ensure we get an error for an incorrect filename, # even in the presence of other, valid XML files - with pytest.raises(RuntimeError): - openmc.run(input_file='ex-em-ell.xml') \ No newline at end of file + with pytest.raises(RuntimeError) as execinfo: + openmc.run(input_file='ex-em-ell.xml') + assert 'ex-em-ell.xml' in execinfo.value \ No newline at end of file From 6f7a6febd3fe01531979d2276f1c40fb6f8430f7 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 25 Nov 2022 08:22:57 -0600 Subject: [PATCH 188/265] Updating model XML file format w/ indentation. --- openmc/_xml.py | 28 ++++-- openmc/material.py | 27 +++-- openmc/model/model.py | 7 +- .../model_xml/inputs_true.dat | 99 ++++++++++++------- 4 files changed, 111 insertions(+), 50 deletions(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index 32679fd89e..b4e08c7519 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -1,22 +1,36 @@ -def clean_indentation(element, level=0, spaces_per_level=2): - """ - copy and paste from https://effbot.org/zone/element-lib.htm#prettyprint - it basically walks your tree and adds spaces and newlines so the tree is - printed in a nice way +def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True): + """Set indentation of XML element and its sub-elements. + Copied and pastee from https://effbot.org/zone/element-lib.htm#prettyprint. + It walks your tree and adds spaces and newlines so the tree is + printed in a nice way. + + Parameters + ---------- + level : int + Indentation level for the element passed in (default 0) + spaces_per_level : int + Number of spaces per indentation level (default 2) + trailing_indent : bool + Whether or not to include an indentation after closing the element + """ i = "\n" + level*spaces_per_level*" " + # ensure there's awlays some tail for the element passed in + if not element.tail: + element.tail = "" + if len(element): if not element.text or not element.text.strip(): element.text = i + spaces_per_level*" " - if not element.tail or not element.tail.strip(): + if trailing_indent and (not element.tail or not element.tail.strip()): element.tail = i for sub_element in element: clean_indentation(sub_element, level+1, spaces_per_level) if not sub_element.tail or not sub_element.tail.strip(): sub_element.tail = i else: - if level and (not element.tail or not element.tail.strip()): + if trailing_indent and level and (not element.tail or not element.tail.strip()): element.tail = i diff --git a/openmc/material.py b/openmc/material.py index c335968002..332f8c9659 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1449,7 +1449,7 @@ class Materials(cv.CheckedList): for material in self: material.make_isotropic_in_lab() - def _write_xml(self, file, header=True): + def _write_xml(self, file, header=True, level=0, spaces_per_level=2, trailing_indent=True): """Writes XML content of the materials to an open file handle. Parameters @@ -1458,33 +1458,46 @@ class Materials(cv.CheckedList): Open file handle to write content into. header : bool Whether or not to write the XML header + level : int + Indentation level of materials element + spaces_per_level : int + Number of spaces per indentation + trailing_indentation : bool + Whether or not to write a trailing indentation for the materials element + """ + indentation = level*spaces_per_level*' ' # Write the header and the opening tag for the root element. if header: file.write("\n") - file.write('\n') + file.write(indentation+'\n') # Write the element. if self.cross_sections is not None: element = ET.Element('cross_sections') element.text = str(self.cross_sections) - clean_indentation(element, level=1) + clean_indentation(element, level=level+1) element.tail = element.tail.strip(' ') - file.write(' ') + file.write((level+1)*spaces_per_level*' ') reorder_attributes(element) # TODO: Remove when support is Python 3.8+ ET.ElementTree(element).write(file, encoding='unicode') # Write the elements. for material in sorted(self, key=lambda x: x.id): element = material.to_xml_element() - clean_indentation(element, level=1) + clean_indentation(element, level=level+1) element.tail = element.tail.strip(' ') - file.write(' ') + file.write((level+1)*spaces_per_level*' ') reorder_attributes(element) # TODO: Remove when support is Python 3.8+ ET.ElementTree(element).write(file, encoding='unicode') # Write the closing tag for the root element. - file.write('\n') + file.write(indentation+'\n') + + # Write a trailing indentation for the next element + # at this level if needed + if trailing_indent: + file.write(indentation) def export_to_xml(self, path: PathLike = 'materials.xml'): diff --git a/openmc/model/model.py b/openmc/model/model.py index 15effd3e4c..9c38d27c32 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -552,6 +552,9 @@ class Model: settings_element = self.settings.to_xml_element(memo) geometry_element = self.geometry.to_xml_element() + xml.clean_indentation(geometry_element, level=1) + xml.clean_indentation(settings_element, level=1) + # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build # a collection. @@ -567,16 +570,18 @@ class Model: fh.write("\n") # Write the materials collection to the open XML file first. # This will write the XML header also - materials._write_xml(fh, False) + materials._write_xml(fh, False, level=1) # Write remaining elements as a tree ET.ElementTree(geometry_element).write(fh, encoding='unicode') ET.ElementTree(settings_element).write(fh, encoding='unicode') if self.tallies: tallies_element = self.tallies.to_xml_element(memo) + xml.clean_indentation(tallies_element, level=1, trailing_indent=self.plots) ET.ElementTree(tallies_element).write(fh, encoding='unicode') if self.plots: plots_element = self.plots.to_xml_element() + xml.clean_indentation(plots_element, level=1, trailing_indent=False) ET.ElementTree(plots_element).write(fh, encoding='unicode') fh.write("\n") diff --git a/tests/regression_tests/model_xml/inputs_true.dat b/tests/regression_tests/model_xml/inputs_true.dat index 5aedbfa143..c408026316 100644 --- a/tests/regression_tests/model_xml/inputs_true.dat +++ b/tests/regression_tests/model_xml/inputs_true.dat @@ -1,38 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 10000 - 10 - 5 - - - -4.0 -4.0 -4.0 4.0 4.0 4.0 - - - + + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 14000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + 16 + + + neutron photon electron positron + + + 1 2 + current + + + 2 + Al27 total + total (n,gamma) + tracklength + + + 2 + Al27 total + total heating (n,gamma) + collision + + + 2 + Al27 total + total heating (n,gamma) + analog + + From 5d1ead2e6a88affe5900067412e0464171cba8e4 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Sat, 26 Nov 2022 00:46:12 -0600 Subject: [PATCH 189/265] Addressing some more PR comments. A couple of typo fixes too. --- openmc/_xml.py | 2 +- openmc/model/model.py | 2 +- src/initialize.cpp | 6 +----- tests/regression_tests/adj_cell_rotation/__init__.py | 2 -- tests/regression_tests/energy_laws/__init__.py | 1 - tests/regression_tests/lattice_multiple/__init__.py | 1 - tests/regression_tests/model_xml/test.py | 8 ++++---- tests/regression_tests/photon_production/__init__.py | 1 - 8 files changed, 7 insertions(+), 16 deletions(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index b4e08c7519..450c8a99bf 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -11,7 +11,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True spaces_per_level : int Number of spaces per indentation level (default 2) trailing_indent : bool - Whether or not to include an indentation after closing the element + Whether or not to add indentation after closing the element """ i = "\n" + level*spaces_per_level*" " diff --git a/openmc/model/model.py b/openmc/model/model.py index 9c38d27c32..145a2151bc 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -469,7 +469,7 @@ class Model: Whether or not to remove redundant surfaces from the geometry when exporting. filename : str - Name of the single XML file to create (only used :math:`separate_xmls` if False) + Name of the single XML file to create (only used :math:`separate_xmls` is False) .. versionadded:: 0.13.1 diff --git a/src/initialize.cpp b/src/initialize.cpp index a6a6b5a776..212b2f7bdc 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -359,11 +359,7 @@ bool read_model_xml() { fmt::format("No node present in the {} file.", xml_filename)); } - // find materials node this object cannot be used after being passed to the - // cross section reading function below - auto materials_node = root.child("materials"); - read_cross_sections_xml(materials_node); - + read_cross_sections_xml(root.child("materials")); read_materials_xml(root.child("materials")); // Read geometry diff --git a/tests/regression_tests/adj_cell_rotation/__init__.py b/tests/regression_tests/adj_cell_rotation/__init__.py index 7efe448659..e69de29bb2 100644 --- a/tests/regression_tests/adj_cell_rotation/__init__.py +++ b/tests/regression_tests/adj_cell_rotation/__init__.py @@ -1,2 +0,0 @@ - -from .test import model \ No newline at end of file diff --git a/tests/regression_tests/energy_laws/__init__.py b/tests/regression_tests/energy_laws/__init__.py index 29fbfcc0c2..e69de29bb2 100644 --- a/tests/regression_tests/energy_laws/__init__.py +++ b/tests/regression_tests/energy_laws/__init__.py @@ -1 +0,0 @@ -from .test import model \ No newline at end of file diff --git a/tests/regression_tests/lattice_multiple/__init__.py b/tests/regression_tests/lattice_multiple/__init__.py index 29fbfcc0c2..e69de29bb2 100644 --- a/tests/regression_tests/lattice_multiple/__init__.py +++ b/tests/regression_tests/lattice_multiple/__init__.py @@ -1 +0,0 @@ -from .test import model \ No newline at end of file diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 0dc2cc2898..52f67f76fd 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -9,10 +9,10 @@ from tests.testing_harness import PyAPITestHarness, colorize # use a few models from other tests to make sure the same results are # produced when using a single model.xml file as input -from ..adj_cell_rotation import model as adj_cell_rotation_model -from ..lattice_multiple import model as lattice_mult_model -from ..energy_laws import model as energy_laws_model -from ..photon_production import model as photon_prod_model +from ..adj_cell_rotation.test import model as adj_cell_rotation_model +from ..lattice_multiple.test import model as lattice_mult_model +from ..energy_laws.test import model as energy_laws_model +from ..photon_production.test import model as photon_prod_model class ModelXMLTestHarness(PyAPITestHarness): diff --git a/tests/regression_tests/photon_production/__init__.py b/tests/regression_tests/photon_production/__init__.py index 29fbfcc0c2..e69de29bb2 100644 --- a/tests/regression_tests/photon_production/__init__.py +++ b/tests/regression_tests/photon_production/__init__.py @@ -1 +0,0 @@ -from .test import model \ No newline at end of file From e7f95b0c7a5fc0c1548183f38606ce158c2eb31c Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 28 Nov 2022 00:44:05 -0600 Subject: [PATCH 190/265] Docstring correction and a note about the recursion --- openmc/_xml.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index 450c8a99bf..6d298638da 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -1,6 +1,6 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True): """Set indentation of XML element and its sub-elements. - Copied and pastee from https://effbot.org/zone/element-lib.htm#prettyprint. + Copied and pasted from https://effbot.org/zone/element-lib.htm#prettyprint. It walks your tree and adds spaces and newlines so the tree is printed in a nice way. @@ -26,6 +26,8 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True if trailing_indent and (not element.tail or not element.tail.strip()): element.tail = i for sub_element in element: + # `trailing_indent` intentionally not passed to the recursive call. + # it only applies to the element for the initial of this function. clean_indentation(sub_element, level+1, spaces_per_level) if not sub_element.tail or not sub_element.tail.strip(): sub_element.tail = i From b3225be24d384ba834fab6f003ff7ba39e9d99b1 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 28 Nov 2022 20:03:42 -0600 Subject: [PATCH 191/265] Adding executor tests --- tests/unit_tests/test_model.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 87557ddc53..5d4765d657 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -1,5 +1,6 @@ from math import pi from pathlib import Path +import os import numpy as np import pytest @@ -546,4 +547,23 @@ def test_model_xml(run_in_tmpdir): # make sure we can also export this again to separate # XML files - new_model.export_to_xml(separate_xmls=True) \ No newline at end of file + new_model.export_to_xml(separate_xmls=True) + +def test_model_exec(run_in_tmpdir): + + pincell_model = openmc.examples.pwr_pin_cell() + + pincell_model.export_to_xml(filename='pwr_pincell.xml', separate_xmls=False) + + openmc.run(input_file='pwr_pincell.xml') + + with pytest.raises(RuntimeError, match='ex-em-ell.xml'): + openmc.run(input_file='ex-em-ell.xml') + + # test that a file in a different directory can be used + os.mkdir('inputs') + pincell_model.export_to_xml(directory='./inputs', filename='pincell.xml', separate_xmls=False) + openmc.run(input_file='./inputs/pincell.xml') + + with pytest.raises(RuntimeError, match='input_dir'): + openmc.run(input_file='input_dir/pincell.xml') \ No newline at end of file From 72ff942226e8fb60801328a64530ad1cd94b1da0 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 28 Nov 2022 20:04:45 -0600 Subject: [PATCH 192/265] Checking RuntimeError message correctly --- tests/regression_tests/model_xml/test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 52f67f76fd..51c25a603b 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -93,6 +93,5 @@ def test_input_arg(run_in_tmpdir): # now ensure we get an error for an incorrect filename, # even in the presence of other, valid XML files - with pytest.raises(RuntimeError) as execinfo: - openmc.run(input_file='ex-em-ell.xml') - assert 'ex-em-ell.xml' in execinfo.value \ No newline at end of file + with pytest.raises(RuntimeError, match='ex-em-ell.xml'): + openmc.run(input_file='ex-em-ell.xml') \ No newline at end of file From 7f5d2a583932206bc681a8555d658334d74ebc9d Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 28 Nov 2022 23:54:26 -0600 Subject: [PATCH 193/265] Refactoring model_xml regression tests a bit and adding input checking --- .../adj_cell_rotation_inputs_true.dat | 38 +++++++++++ .../model_xml/energy_laws_inputs_true.dat | 23 +++++++ .../lattice_multiple_inputs_true.dat | 53 +++++++++++++++ .../photon_production_inputs_true.dat | 67 +++++++++++++++++++ tests/regression_tests/model_xml/test.py | 42 ++++++------ 5 files changed, 201 insertions(+), 22 deletions(-) create mode 100644 tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat create mode 100644 tests/regression_tests/model_xml/energy_laws_inputs_true.dat create mode 100644 tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat create mode 100644 tests/regression_tests/model_xml/photon_production_inputs_true.dat diff --git a/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat new file mode 100644 index 0000000000..3b14f304ea --- /dev/null +++ b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + diff --git a/tests/regression_tests/model_xml/energy_laws_inputs_true.dat b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat new file mode 100644 index 0000000000..62485ff72a --- /dev/null +++ b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat new file mode 100644 index 0000000000..d828b25f6b --- /dev/null +++ b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + 1.2 1.2 + 1 + 2 2 + -1.2 -1.2 + +2 1 +1 1 + + + 2.4 2.4 + 2 2 + -2.4 -2.4 + +4 4 +4 4 + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/model_xml/photon_production_inputs_true.dat b/tests/regression_tests/model_xml/photon_production_inputs_true.dat new file mode 100644 index 0000000000..9e60033bec --- /dev/null +++ b/tests/regression_tests/model_xml/photon_production_inputs_true.dat @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 14000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + 1 + + + neutron photon electron positron + + + 1 2 + current + + + 2 + Al27 total + total (n,gamma) + tracklength + + + 2 + Al27 total + total heating (n,gamma) + collision + + + 2 + Al27 total + total heating (n,gamma) + analog + + + diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 51c25a603b..9da5438db6 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -10,9 +10,9 @@ from tests.testing_harness import PyAPITestHarness, colorize # use a few models from other tests to make sure the same results are # produced when using a single model.xml file as input from ..adj_cell_rotation.test import model as adj_cell_rotation_model -from ..lattice_multiple.test import model as lattice_mult_model +from ..lattice_multiple.test import model as lattice_multiple_model from ..energy_laws.test import model as energy_laws_model -from ..photon_production.test import model as photon_prod_model +from ..photon_production.test import model as photon_production_model class ModelXMLTestHarness(PyAPITestHarness): @@ -30,10 +30,10 @@ class ModelXMLTestHarness(PyAPITestHarness): def _get_inputs(self): return open('model.xml').read() - def _compare_inputs(self): - """Skip input comparisons for now - """ - pass + # def _compare_inputs(self): + # """Skip input comparisons for now + # """ + # pass def _compare_results(self): """Make sure the current results agree with the reference.""" @@ -54,25 +54,23 @@ class ModelXMLTestHarness(PyAPITestHarness): os.remove('model.xml') -models = [ - 'adj_cell_rotation_model', - 'lattice_mult_model', - 'energy_laws_model', - 'photon_prod_model' -] -paths = [ - '../adj_cell_rotation', - '../lattice_multiple', - '../energy_laws', - '../photon_production' +test_names = [ + 'adj_cell_rotation', + 'lattice_multiple', + 'energy_laws', + 'photon_production' ] -@pytest.mark.parametrize("model, path", zip(models, paths)) -def test_model_xml(model, path, request): - inputs = path + "/inputs_true.dat" - results = path + "/results_true.dat" - harness = ModelXMLTestHarness(request.getfixturevalue(model), inputs, results) +@pytest.mark.parametrize("test_name", test_names, ids=lambda test: test) +def test_model_xml(test_name, request): + openmc.reset_auto_ids() + + test_path = '../' + test_name + results = test_path + "/results_true.dat" + inputs = test_name + "_inputs_true.dat" + model_name = test_name + "_model" + harness = ModelXMLTestHarness(request.getfixturevalue(model_name), inputs, results) harness.main() def test_input_arg(run_in_tmpdir): From 3f4a9eb7a2056f3136b00b82a814e934f52803e3 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 29 Nov 2022 01:19:38 -0600 Subject: [PATCH 194/265] Reordering geometry XML element attributes --- openmc/geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index d036df9c74..afcae1d550 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -132,6 +132,7 @@ class Geometry: # Clean the indentation in the file to be user-readable xml.clean_indentation(element) + xml.reorder_attributes(element) # TODO: Remove when support is Python 3.8+ return element @@ -157,7 +158,6 @@ class Geometry: p /= 'geometry.xml' # Write the XML Tree to the geometry.xml file - xml.reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') From 83cf6848d0f0fbe0a59798b333542daf3255c7e5 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 29 Nov 2022 11:11:06 -0600 Subject: [PATCH 195/265] Updating expected inputs with reordering --- .../adj_cell_rotation_inputs_true.dat | 20 +++++++++---------- .../model_xml/energy_laws_inputs_true.dat | 2 +- .../lattice_multiple_inputs_true.dat | 16 +++++++-------- .../photon_production_inputs_true.dat | 8 ++++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat index 3b14f304ea..1c4516f42d 100644 --- a/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat +++ b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat @@ -13,16 +13,16 @@ - - - - - - - - - - + + + + + + + + + + eigenvalue diff --git a/tests/regression_tests/model_xml/energy_laws_inputs_true.dat b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat index 62485ff72a..c89ccc97ee 100644 --- a/tests/regression_tests/model_xml/energy_laws_inputs_true.dat +++ b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat @@ -12,7 +12,7 @@ - + eigenvalue diff --git a/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat index d828b25f6b..0eed3c2015 100644 --- a/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat +++ b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat @@ -18,8 +18,8 @@ - - + + 1.2 1.2 1 @@ -37,12 +37,12 @@ 4 4 4 4 - - - - - - + + + + + + eigenvalue diff --git a/tests/regression_tests/model_xml/photon_production_inputs_true.dat b/tests/regression_tests/model_xml/photon_production_inputs_true.dat index 9e60033bec..e4fe9a5858 100644 --- a/tests/regression_tests/model_xml/photon_production_inputs_true.dat +++ b/tests/regression_tests/model_xml/photon_production_inputs_true.dat @@ -10,10 +10,10 @@ - - - - + + + + fixed source From 2c27d1065a1e583a3778aa493f70a36724b46306 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 29 Nov 2022 13:45:10 -0600 Subject: [PATCH 196/265] Moving other relevant XML reorder calls and updating expected input again. --- openmc/settings.py | 2 +- openmc/tallies.py | 2 +- .../model_xml/photon_production_inputs_true.dat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 74ec0d54a6..3c1c8c7f5c 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1598,6 +1598,7 @@ class Settings: # Clean the indentation in the file to be user-readable clean_indentation(element) + reorder_attributes(element) # TODO: Remove when support is Python 3.8+ return element @@ -1618,7 +1619,6 @@ class Settings: p /= 'settings.xml' # Write the XML Tree to the settings.xml file - reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') diff --git a/openmc/tallies.py b/openmc/tallies.py index e37f04e252..e17e7c99db 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3166,6 +3166,7 @@ class Tallies(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(element) + reorder_attributes(element) # TODO: Remove when support is Python 3.8+ return element @@ -3187,7 +3188,6 @@ class Tallies(cv.CheckedList): p /= 'tallies.xml' # Write the XML Tree to the tallies.xml file - reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') diff --git a/tests/regression_tests/model_xml/photon_production_inputs_true.dat b/tests/regression_tests/model_xml/photon_production_inputs_true.dat index e4fe9a5858..0b2b434681 100644 --- a/tests/regression_tests/model_xml/photon_production_inputs_true.dat +++ b/tests/regression_tests/model_xml/photon_production_inputs_true.dat @@ -23,7 +23,7 @@ 0 0 0 - + 14000000.0 1.0 From cd7c3bf6ba62cd019c378a3a22880e24e847f8ef Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 12:58:32 -0600 Subject: [PATCH 197/265] Updates to import/export methods suggested by @paulromano --- openmc/model/model.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 145a2151bc..6ca6f72b62 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -205,15 +205,19 @@ class Model: self._plots.append(plot) @classmethod - def from_xml(cls, *args, path='model.xml', separate_xmls=True, **kwargs): + def from_xml(cls, *args, separate_xmls=True, **kwargs): """Generate geometry from XML file Parameters ---------- - path : str, optional - Path to model XML file + args : list + Positional arguments to be forwarded to + :func:`Model.from_separate_xmls` or :func:`Model.from_model_xml`. separate_xmls : bool - Whether or not to read from a single or separate XML files + Whether or not to read from a single or separate XML files. + kwargs : dict, optional + Keyword arguments to be forwarded to + :func:`Model.from_separate_xmls` or :func:`Model.from_model_xml`. Returns ------- @@ -221,7 +225,7 @@ class Model: Geometry object """ - if separate_xmls or not Path(path).exists(): + if separate_xmls: return cls.from_separate_xmls(*args, **kwargs) else: return cls.from_model_xml(*args, **kwargs) @@ -457,19 +461,19 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True, filename='model.xml'): + def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True, path='model.xml'): """Export model to an XML file(s). Parameters ---------- directory : str Directory to write XML files to. If it doesn't exist already, it - will be created. + will be created. Only used if :math:`separate_xmls` is True. remove_surfs : bool Whether or not to remove redundant surfaces from the geometry when exporting. - filename : str - Name of the single XML file to create (only used :math:`separate_xmls` is False) + path : str + Path to an output filename or directory. Only used if :math:`separate_xmls` is False. .. versionadded:: 0.13.1 @@ -477,13 +481,9 @@ class Model: Whether or not to write a single model.xml file or many XML files. """ if separate_xmls: - if filename != 'model.xml': - warnings.warn('Export filename parameter {filename} is ignored ' - 'because the model is being written to separate XML files.') self._export_to_separate_xmls(directory, remove_surfs) else: - filename = Path(directory) / Path(filename) - self._export_to_single_xml(filename, remove_surfs) + self._export_to_single_xml(path, remove_surfs) def _export_to_separate_xmls(self, directory='.', remove_surfs=False): """Export model to separate XML files. @@ -530,16 +530,25 @@ class Model: directory : str Directory to write the model.xml file to. If it doesn't exist already, it will be created. + path : str or Pathlike + Location of the XML file to write. Can be a directory or file path. remove_surfs : bool Whether or not to remove redundant surfaces from the geometry when exporting. .. versionadded:: 0.13.1 """ - # Create directory if required xml_path = Path(path) - if not xml_path.parent.exists: - raise RuntimeError(f'The directory "{xml_path}" does not exist.') + # if the provided path doesn't end with the XML extension, assume the + # input path is meant to be a directory. If the directory does not + # exist, create it and place a 'model.xml' file there. + if not str(xml_path).endswith('.xml') and not xml_path.exists(): + os.mkdir(xml_path) + xml_path /= 'model.xml' + # if this is an XML file location and the file's parent directory does + # not exist, create it before continuing + elif not xml_path.parent.exists(): + os.mkdir(xml_path.parent) if remove_surfs: warnings.warn("remove_surfs kwarg will be deprecated soon, please " From ef2f690e3d4c7bb1f700f189bdaad88689fa5b16 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 12:59:08 -0600 Subject: [PATCH 198/265] Updating handling of single XML input to leverage existing settings::path_input --- src/initialize.cpp | 42 +++++++++++++++++++----------------------- src/settings.cpp | 3 ++- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 212b2f7bdc..c0333e01c0 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -181,9 +181,6 @@ int parse_command_line(int argc, char* argv[]) } else if (arg == "-e" || arg == "--event") { settings::event_based = true; - } else if (arg == "-i" || arg == "--input") { - i += 1; - args_xml_filename = std::string(argv[i]); } else if (arg == "-r" || arg == "--restart") { i += 1; // Check what type of file this is @@ -295,27 +292,24 @@ int parse_command_line(int argc, char* argv[]) } bool read_model_xml() { - // get verbosity from settings node + std::string model_filename = settings::path_input; - std::string xml_filename = "model.xml"; - if (!args_xml_filename.empty()) xml_filename = args_xml_filename; - std::string model_filename = settings::path_input + xml_filename; - - // check that the model file exists. If it does not and a custom filename was - // supplied by the user report an error - if (!file_exists(model_filename)) { - if (!args_xml_filename.empty()) { - fatal_error(fmt::format("The input file '{}' specified on the command line does not exist", args_xml_filename)); - } - return false; + // if the path input isn't an existing file, assume its a directory + // and append the default model.xml filename + if (!file_exists(settings::path_input)) { + model_filename += "/model.xml"; + if (!file_exists(model_filename)) + return false; } - // attempt to open the document + // try to process the path input as an XML file pugi::xml_document doc; - auto result = doc.load_file(model_filename.c_str()); - if (!result) { - fatal_error("Error processing model.xml file."); + // if the input is a file, try to read it + if (!doc.load_file(model_filename.c_str())) { + fatal_error(fmt::format( + "Error reading from single XML input file '{}'", model_filename)); } + pugi::xml_node root = doc.document_element(); // Read settings @@ -348,15 +342,17 @@ bool read_model_xml() { auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; for (const auto& input : other_inputs) { if (file_exists(settings::path_input + input)) { - warning((fmt::format("Other XML file input(s) are present. These file will be ignored in favor of the {} file.", xml_filename))); + warning((fmt::format("Other XML file input(s) are present. These file " + "will be ignored in favor of the {} file.", + model_filename))); break; } } // Read materials and cross sections if (!check_for_node(root, "materials")) { - fatal_error( - fmt::format("No node present in the {} file.", xml_filename)); + fatal_error(fmt::format( + "No node present in the {} file.", model_filename)); } read_cross_sections_xml(root.child("materials")); @@ -365,7 +361,7 @@ bool read_model_xml() { // Read geometry if (!check_for_node(root, "geometry")) { fatal_error(fmt::format( - "No node present in the model.xml_file.", xml_filename)); + "No node present in the model.xml_file.", model_filename)); } read_geometry_xml(root.child("geometry")); diff --git a/src/settings.cpp b/src/settings.cpp index 3048862e3f..74d45543d8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -222,7 +222,8 @@ void read_settings_xml() { fmt::format("Settings XML file '{}' does not exist! In order " "to run OpenMC, you first need a set of input files; at a " "minimum, this " - "includes settings.xml, geometry.xml, and materials.xml. " + "includes settings.xml, geometry.xml, and materials.xml " + "or a single XML file containing all of these files. " "Please consult " "the user's guide at https://docs.openmc.org for further " "information.", From 574845b18b1de2d2fa9fa8415d7770e18d411bd6 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 13:09:04 -0600 Subject: [PATCH 199/265] Moving Model.from_separate_xmls back to Model.from_xml. --- openmc/model/model.py | 78 +++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 6ca6f72b62..34cc085b84 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -205,30 +205,40 @@ class Model: self._plots.append(plot) @classmethod - def from_xml(cls, *args, separate_xmls=True, **kwargs): - """Generate geometry from XML file + def from_xml(cls, geometry='geometry.xml', materials='materials.xml', + settings='settings.xml', tallies='tallies.xml', + plots='plots.xml'): + """Create model from existing XML files Parameters ---------- - args : list - Positional arguments to be forwarded to - :func:`Model.from_separate_xmls` or :func:`Model.from_model_xml`. - separate_xmls : bool - Whether or not to read from a single or separate XML files. - kwargs : dict, optional - Keyword arguments to be forwarded to - :func:`Model.from_separate_xmls` or :func:`Model.from_model_xml`. + geometry : str + Path to geometry.xml file + materials : str + Path to materials.xml file + settings : str + Path to settings.xml file + tallies : str + Path to tallies.xml file + + .. versionadded:: 0.13.0 + plots : str + Path to plots.xml file + + .. versionadded:: 0.13.0 Returns ------- - openmc.Geometry - Geometry object + openmc.model.Model + Model created from XML files """ - if separate_xmls: - return cls.from_separate_xmls(*args, **kwargs) - else: - return cls.from_model_xml(*args, **kwargs) + materials = openmc.Materials.from_xml(materials) + geometry = openmc.Geometry.from_xml(geometry, materials) + settings = openmc.Settings.from_xml(settings) + tallies = openmc.Tallies.from_xml(tallies) if Path(tallies).exists() else None + plots = openmc.Plots.from_xml(plots) if Path(plots).exists() else None + return cls(geometry, materials, settings, tallies, plots) @classmethod def from_model_xml(cls, path='model.xml'): @@ -265,42 +275,6 @@ class Model: return model - @classmethod - def from_separate_xmls(cls, geometry='geometry.xml', materials='materials.xml', - settings='settings.xml', tallies='tallies.xml', - plots='plots.xml'): - """Create model from existing XML files - - Parameters - ---------- - geometry : str - Path to geometry.xml file - materials : str - Path to materials.xml file - settings : str - Path to settings.xml file - tallies : str - Path to tallies.xml file - - .. versionadded:: 0.13.0 - plots : str - Path to plots.xml file - - .. versionadded:: 0.13.0 - - Returns - ------- - openmc.model.Model - Model created from XML files - - """ - materials = openmc.Materials.from_xml(materials) - geometry = openmc.Geometry.from_xml(geometry, materials) - settings = openmc.Settings.from_xml(settings) - tallies = openmc.Tallies.from_xml(tallies) if Path(tallies).exists() else None - plots = openmc.Plots.from_xml(plots) if Path(plots).exists() else None - return cls(geometry, materials, settings, tallies, plots) - def init_lib(self, threads=None, geometry_debug=False, restart_file=None, tracks=False, output=True, event_based=None, intracomm=None): """Initializes the model in memory via the C API From 270331aff956b95b1193e121c545345e64d3716c Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 14:39:44 -0600 Subject: [PATCH 200/265] Adding function to check if a path is a directory --- include/openmc/file_utils.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/openmc/file_utils.h b/include/openmc/file_utils.h index f9c23468df..ae3a26e942 100644 --- a/include/openmc/file_utils.h +++ b/include/openmc/file_utils.h @@ -3,15 +3,31 @@ #include // for ifstream #include +#include namespace openmc { +// TODO: replace with std::filesysem when switch to C++17 is made +//! Determine if a path is a directory +//! \param[in] path Path to check +//! \return Whether the path is a directory +inline bool is_dir(const std::string& path) { + struct stat s; + if (stat(path.c_str(), &s) != 0) return false; + + return s.st_mode & S_IFDIR; +} + //! Determine if a file exists //! \param[in] filename Path to file //! \return Whether file exists inline bool file_exists(const std::string& filename) { + // rule out file being a directory path + if (is_dir(filename)) return false; + std::ifstream s {filename}; + s.seekg(0, std::ios::beg); return s.good(); } From bc1791d367b307656ce31bdfdf7c4b794def44ad Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 14:40:02 -0600 Subject: [PATCH 201/265] Updates to simplify model filename checking --- include/openmc/initialize.h | 2 -- src/initialize.cpp | 30 ++++++++++++++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index c37208e5e3..a9b8b336f9 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -21,8 +21,6 @@ void read_separate_xml_files(); //! Write some output that occurs right after initialization void initial_output(); - -std::string args_xml_filename {}; } // namespace openmc #endif // OPENMC_INITIALIZE_H diff --git a/src/initialize.cpp b/src/initialize.cpp index c0333e01c0..b75f410a10 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -282,7 +282,12 @@ int parse_command_line(int argc, char* argv[]) if (argc > 1 && last_flag < argc - 1) { settings::path_input = std::string(argv[last_flag + 1]); - // Add slash at end of directory if it isn't there + // check that the path is either a valid directory or file + if (!is_dir(settings::path_input) && !file_exists(settings::path_input)) { + fatal_error(fmt::format("The specified path '{}' does not exist.", settings::path_input)); + } + + // Add slash at end of directory if it isn't the if (!ends_with(settings::path_input, "/")) { settings::path_input += "/"; } @@ -294,13 +299,17 @@ int parse_command_line(int argc, char* argv[]) bool read_model_xml() { std::string model_filename = settings::path_input; - // if the path input isn't an existing file, assume its a directory - // and append the default model.xml filename - if (!file_exists(settings::path_input)) { - model_filename += "/model.xml"; - if (!file_exists(model_filename)) - return false; - } + // some string cleanup + // a trailing "/" is applied to path_input if it's present, + // remove it for the first attempt at reading the input file + if (ends_with(model_filename, "/")) model_filename.pop_back(); + if (model_filename.length() == 0) model_filename = "."; + + // if the current filename is a directory, append the default model filename + if (is_dir(model_filename)) model_filename += "/model.xml"; + + // if this file doesn't exist, stop here + if (!file_exists(model_filename)) return false; // try to process the path input as an XML file pugi::xml_document doc; @@ -330,10 +339,7 @@ bool read_model_xml() { title(); } - if (!args_xml_filename.empty()) - write_message(fmt::format("Reaging user-specified input '{}'...", args_xml_filename), 5); - else - write_message("Reading model XML file...", 5); + write_message(fmt::format("Reading model XML file '{}' ...", model_filename), 5); read_settings_xml(settings_root); From 5634d8318ad110ee20f79d69ab277f7134a0d277 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 14:40:59 -0600 Subject: [PATCH 202/265] Changing name of input argument for Python executor --- openmc/executor.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/openmc/executor.py b/openmc/executor.py index b143132139..9267366e89 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -9,7 +9,7 @@ from .plots import _get_plot_image def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, plot=False, restart_file=None, threads=None, tracks=False, event_based=None, - openmc_exec='openmc', mpi_args=None, input_file=None): + openmc_exec='openmc', mpi_args=None, path_input=None): """Converts user-readable flags in to command-line arguments to be run with the OpenMC executable via subprocess. @@ -42,7 +42,7 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, mpi_args : list of str, optional MPI execute command and any additional MPI arguments to pass, e.g. ['mpiexec', '-n', '8']. - input_file : str or Pathlike + path_input : str or Pathlike Name of a single XML input file for the OpenMC executable to read. .. versionadded:: 0.13.0 @@ -84,8 +84,8 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, if mpi_args is not None: args = mpi_args + args - if input_file is not None: - args += ['-i', input_file] + if path_input is not None: + args += [path_input] return args @@ -95,6 +95,7 @@ def _run(args, output, cwd): p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + print(args) # Capture and re-print OpenMC output in real-time lines = [] while True: @@ -123,7 +124,7 @@ def _run(args, output, cwd): raise RuntimeError(error_msg) -def plot_geometry(output=True, openmc_exec='openmc', cwd='.', input_file=None): +def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): """Run OpenMC in plotting mode Parameters @@ -134,7 +135,7 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', input_file=None): Path to OpenMC executable cwd : str, optional Path to working directory to run in - input_file : str + path_input : str Name of a single XML input file for the OpenMC executable to read. Raises @@ -144,12 +145,12 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', input_file=None): """ args = [openmc_exec, '-p'] - if input_file is not None: - args += ['-i', input_file] + if path_input is not None: + args += ['-i', path_input] _run([openmc_exec, '-p'], output, cwd) -def plot_inline(plots, openmc_exec='openmc', cwd='.', input_file=None): +def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): """Display plots inline in a Jupyter notebook. .. versionchanged:: 0.13.0 @@ -165,7 +166,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', input_file=None): Path to OpenMC executable cwd : str, optional Path to working directory to run in - input_file : str + path_input : str Name of a single XML input file for the OpenMC executable to read. Raises @@ -183,7 +184,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', input_file=None): openmc.Plots(plots).export_to_xml(cwd) # Run OpenMC in geometry plotting mode - plot_geometry(False, openmc_exec, cwd, input_file) + plot_geometry(False, openmc_exec, cwd, path_input) if plots is not None: images = [_get_plot_image(p, cwd) for p in plots] @@ -192,7 +193,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', input_file=None): def calculate_volumes(threads=None, output=True, cwd='.', openmc_exec='openmc', mpi_args=None, - input_file=None): + path_input=None): """Run stochastic volume calculations in OpenMC. This function runs OpenMC in stochastic volume calculation mode. To specify @@ -223,7 +224,7 @@ def calculate_volumes(threads=None, output=True, cwd='.', cwd : str, optional Path to working directory to run in. Defaults to the current working directory. - input_file : str or Pathlike + path_input : str or Pathlike Name of a single XML input file for the OpenMC executable to read. Raises @@ -239,7 +240,7 @@ def calculate_volumes(threads=None, output=True, cwd='.', args = _process_CLI_arguments(volume=True, threads=threads, openmc_exec=openmc_exec, mpi_args=mpi_args, - input_file=input_file) + path_input=path_input) _run(args, output, cwd) @@ -247,7 +248,7 @@ def calculate_volumes(threads=None, output=True, cwd='.', def run(particles=None, threads=None, geometry_debug=False, restart_file=None, tracks=False, output=True, cwd='.', openmc_exec='openmc', mpi_args=None, event_based=False, - input_file=None): + path_input=None): """Run an OpenMC simulation. Parameters @@ -282,7 +283,7 @@ def run(particles=None, threads=None, geometry_debug=False, .. versionadded:: 0.12 - input_file : str or Pathlike + path_input : str or Pathlike Name of a single XML input file for the OpenMC executable to read. Raises @@ -296,6 +297,6 @@ def run(particles=None, threads=None, geometry_debug=False, volume=False, geometry_debug=geometry_debug, particles=particles, restart_file=restart_file, threads=threads, tracks=tracks, event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args, - input_file=input_file) + path_input=path_input) _run(args, output, cwd) From d639d1dc1b2796e9fae599d74283b9f125e3801a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 14:41:11 -0600 Subject: [PATCH 203/265] Docstring correction --- openmc/model/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 34cc085b84..a7a6f2d7da 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -450,7 +450,6 @@ class Model: Path to an output filename or directory. Only used if :math:`separate_xmls` is False. .. versionadded:: 0.13.1 - separate_xmls : bool Whether or not to write a single model.xml file or many XML files. """ From 939adbe70e2c73e2d503260bc5a912812d1b0087 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 14:41:26 -0600 Subject: [PATCH 204/265] Updating tests to reflect changes to API --- tests/regression_tests/model_xml/test.py | 12 +++++++++--- tests/unit_tests/test_model.py | 14 +++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 9da5438db6..8aa9e5c2e2 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -1,6 +1,8 @@ from difflib import unified_diff +import glob import filecmp import os +from pathlib import Path import openmc import pytest @@ -83,13 +85,17 @@ def test_input_arg(run_in_tmpdir): pincell.export_to_xml(separate_xmls=True) openmc.run() + # make sure the executable isn't falling back on the separate XMLs + [os.remove(f) for f in glob.glob('*.xml')] # now export to a single XML file with a custom name - pincell.export_to_xml(filename='pincell.xml', separate_xmls=False) + pincell.export_to_xml(path='pincell.xml', separate_xmls=False) + assert Path('pincell.xml').exists() # run by specifying that single file - openmc.run(input_file='pincell.xml') + openmc.run(path_input='pincell.xml') # now ensure we get an error for an incorrect filename, # even in the presence of other, valid XML files + pincell.export_to_xml(separate_xmls=True) with pytest.raises(RuntimeError, match='ex-em-ell.xml'): - openmc.run(input_file='ex-em-ell.xml') \ No newline at end of file + openmc.run(path_input='ex-em-ell.xml') \ No newline at end of file diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 5d4765d657..11a88d76ce 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -543,7 +543,7 @@ def test_model_xml(run_in_tmpdir): # now write and read a model.xml file pwr_model.export_to_xml(separate_xmls=False) - new_model = openmc.Model.from_xml(separate_xmls=False) + new_model = openmc.Model.from_model_xml() # make sure we can also export this again to separate # XML files @@ -553,17 +553,17 @@ def test_model_exec(run_in_tmpdir): pincell_model = openmc.examples.pwr_pin_cell() - pincell_model.export_to_xml(filename='pwr_pincell.xml', separate_xmls=False) + pincell_model.export_to_xml(path='pwr_pincell.xml', separate_xmls=False) - openmc.run(input_file='pwr_pincell.xml') + openmc.run(path_input='pwr_pincell.xml') with pytest.raises(RuntimeError, match='ex-em-ell.xml'): - openmc.run(input_file='ex-em-ell.xml') + openmc.run(path_input='ex-em-ell.xml') # test that a file in a different directory can be used os.mkdir('inputs') - pincell_model.export_to_xml(directory='./inputs', filename='pincell.xml', separate_xmls=False) - openmc.run(input_file='./inputs/pincell.xml') + pincell_model.export_to_xml(path='./inputs/pincell.xml', separate_xmls=False) + openmc.run(path_input='./inputs/pincell.xml') with pytest.raises(RuntimeError, match='input_dir'): - openmc.run(input_file='input_dir/pincell.xml') \ No newline at end of file + openmc.run(path_input='input_dir/pincell.xml') \ No newline at end of file From 7fdfed22f37e2c50913ba96adce55da98daf4c40 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 21:49:46 -0600 Subject: [PATCH 205/265] Some cleanup of file utils --- include/openmc/file_utils.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/openmc/file_utils.h b/include/openmc/file_utils.h index ae3a26e942..439af6ee08 100644 --- a/include/openmc/file_utils.h +++ b/include/openmc/file_utils.h @@ -7,7 +7,7 @@ namespace openmc { -// TODO: replace with std::filesysem when switch to C++17 is made +// TODO: replace with std::filesystem when switch to C++17 is made //! Determine if a path is a directory //! \param[in] path Path to check //! \return Whether the path is a directory @@ -27,7 +27,6 @@ inline bool file_exists(const std::string& filename) if (is_dir(filename)) return false; std::ifstream s {filename}; - s.seekg(0, std::ios::beg); return s.good(); } From 55d77cf06c86726d9ebaf7abcb8de232b01af505 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 21:50:08 -0600 Subject: [PATCH 206/265] Small improvements to read_model_xml --- src/initialize.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index b75f410a10..041712d5a2 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -284,7 +284,9 @@ int parse_command_line(int argc, char* argv[]) // check that the path is either a valid directory or file if (!is_dir(settings::path_input) && !file_exists(settings::path_input)) { - fatal_error(fmt::format("The specified path '{}' does not exist.", settings::path_input)); + fatal_error(fmt::format( + "The path specified to the OpenMC executable '{}' does not exist.", + settings::path_input)); } // Add slash at end of directory if it isn't the @@ -297,13 +299,14 @@ int parse_command_line(int argc, char* argv[]) } bool read_model_xml() { - std::string model_filename = settings::path_input; + std::string model_filename = + settings::path_input.empty() ? "." : settings::path_input; // some string cleanup - // a trailing "/" is applied to path_input if it's present, + // a trailing "/" is applied to path_input if it's specified, // remove it for the first attempt at reading the input file - if (ends_with(model_filename, "/")) model_filename.pop_back(); - if (model_filename.length() == 0) model_filename = "."; + if (ends_with(model_filename, "/")) + model_filename.pop_back(); // if the current filename is a directory, append the default model filename if (is_dir(model_filename)) model_filename += "/model.xml"; @@ -313,7 +316,6 @@ bool read_model_xml() { // try to process the path input as an XML file pugi::xml_document doc; - // if the input is a file, try to read it if (!doc.load_file(model_filename.c_str())) { fatal_error(fmt::format( "Error reading from single XML input file '{}'", model_filename)); From 7873b4d6c1bfb983a7ec4c87ad29b492d4bbb088 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 6 Dec 2022 21:50:49 -0600 Subject: [PATCH 207/265] Update tests/regression_tests/model_xml/test.py Co-authored-by: Paul Romano --- tests/regression_tests/model_xml/test.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index 8aa9e5c2e2..d040f4649f 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -32,11 +32,6 @@ class ModelXMLTestHarness(PyAPITestHarness): def _get_inputs(self): return open('model.xml').read() - # def _compare_inputs(self): - # """Skip input comparisons for now - # """ - # pass - def _compare_results(self): """Make sure the current results agree with the reference.""" compare = filecmp.cmp('results_test.dat', self.results_true) From f2c28706a6eaf2206da5ff73867c279946b55f4b Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 7 Dec 2022 10:12:23 +0100 Subject: [PATCH 208/265] Apply suggestions from code review Co-authored-by: Paul Romano --- CMakeLists.txt | 2 -- docs/source/usersguide/materials.rst | 14 ++++---- openmc/material.py | 43 +++++++++++++------------ tests/regression_tests/ncrystal/test.py | 29 ++++++++--------- tools/ci/gha-install.py | 4 +-- tools/ci/gha-script.sh | 6 ++-- 6 files changed, 47 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2815230d43..5c4d489e3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,8 +497,6 @@ if(OPENMC_USE_NCRYSTAL) target_link_libraries(libopenmc NCrystal::NCrystal) endif() - - #=============================================================================== # openmc executable #=============================================================================== diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index a3c05f4211..fbf9effd89 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -101,19 +101,17 @@ you would need to add hydrogen and oxygen to a material and then assign the .. _usersguide_naming: ------------------- +------------------------- Adding NCrystal materials ------------------- +------------------------- Additional support for thermal scattering can be added by using NCrystal_. The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from an `NCrystal configuration string `_. -Temperature, material composition and density are passed from the configuration string +Temperature, material composition, and density are passed from the configuration string and the `NCMAT file `_ -that define the material, e.g: +that define the material, e.g.:: -:: - mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') defines a material containing polycrystalline alumnium, @@ -126,12 +124,12 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. -NCrystal only handles low energy neutron interactions. Other interaction are +NCrystal only handles low energy neutron interactions. Other interactions are provided by standard ACE files. NCrystal_ comes with a `predefined library `_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. -.. warning:: Currently, NCrystal_ materials cannot be modified after created. +.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. Density, temperature and composition should be defined in the configuration string or the NCMAT file. diff --git a/openmc/material.py b/openmc/material.py index de4a22cb8e..298731c92c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -336,11 +336,12 @@ class Material(IDManagerMixin): @classmethod def from_ncrystal(cls, cfg, material_id=None, name=''): - """Create material from NCrystal configuration string. Density, - temperature, and material composition, and (ultimately) thermal neutron - scattering, will be automatically be provided by NCrystal based on this - string. The name and material_id parameters are simply passed on to the - Material constructor. + """Create material from NCrystal configuration string. + + Density, temperature, and material composition, and (ultimately) thermal + neutron scattering will be automatically be provided by NCrystal based + on this string. The name and material_id parameters are simply passed on + to the Material constructor. Parameters ---------- @@ -363,41 +364,41 @@ class Material(IDManagerMixin): import NCrystal nc_mat = NCrystal.createInfo(cfg) - def openmc_natabund( Z ): + def openmc_natabund(Z): #nc_mat.getFlattenedComposition might need natural abundancies. #This call-back function is used so NCrystal can flatten composition #using OpenMC's natural abundancies. In practice this function will #only get invoked in the unlikely case where a material is specified #by referring both to natural elements and specific isotopes of the #same element. - elem_name = openmc.data.ATOMIC_SYMBOL.get( Z, None ) + elem_name = openmc.data.ATOMIC_SYMBOL.get(Z) if not elem_name: raise ValueError( f'Element with Z={Z} is not known' ) - l = [] - for iso_name,abund in openmc.data.isotopes( elem_name ): - l.append( ( int(iso_name[ len(elem_name) : ]), abund ) ) - return l + return [ + (int(iso_name[len(elem_name):]), abund) + for iso_name, abund in openmc.data.isotopes(elem_name) + ] - flat_compos = nc_mat.getFlattenedComposition( preferNaturalElements = True, - naturalAbundProvider = openmc_natabund ) + flat_compos = nc_mat.getFlattenedComposition(preferNaturalElements = True, + naturalAbundProvider=openmc_natabund) # Create the Material - material = cls( material_id = material_id, - name = name, - temperature = nc_mat.getTemperature() ) + material = cls(material_id=material_id, + name=name, + temperature=nc_mat.getTemperature()) for Z, A_vals in flat_compos: - elemname = openmc.data.ATOMIC_SYMBOL.get(Z,None) + elemname = openmc.data.ATOMIC_SYMBOL.get(Z) if not elemname: raise ValueError(f'Element with Z={Z} is not known') for A, frac in A_vals: if A: - material.add_nuclide( elemname + str(A), frac, 'ao' ) + material.add_nuclide(f'{elemname}{A}', frac) else: - material.add_element( elemname, frac, 'ao' ) + material.add_element(elemname, frac) - material.set_density( 'g/cm3', nc_mat.getDensity() ) - material._ncrystal_cfg = NCrystal.normaliseCfg( cfg ) + material.set_density('g/cm3', nc_mat.getDensity()) + material._ncrystal_cfg = NCrystal.normaliseCfg(cfg) return material diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 0a47d634df..3137515d4a 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -10,7 +10,7 @@ pytestmark = pytest.mark.skipif( reason="NCrystal materials are not enabled.") def compute_angular_distribution(cfg, E0, N): - """Return a openmc.model.Model() object for a monoenergetic pencil + """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by the cfg string, and compute the angular distribution""" @@ -23,17 +23,16 @@ def compute_angular_distribution(cfg, E0, N): sample_sphere = openmc.Sphere(r=0.1) outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") - cell1 = openmc.Cell(region= -sample_sphere, fill=m1) - cell2_region= +sample_sphere&-outer_sphere - cell2 = openmc.Cell(region= cell2_region, fill=None) - uni1 = openmc.Universe(cells=[cell1, cell2]) - geometry = openmc.Geometry(uni1) + cell1 = openmc.Cell(region=-sample_sphere, fill=m1) + cell2_region= +sample_sphere & -outer_sphere + cell2 = openmc.Cell(region=cell2_region, fill=None) + geometry = openmc.Geometry([cell1, cell2]) # Source definition source = openmc.Source() - source.space = openmc.stats.Point(xyz = (0,0,-20)) - source.angle = openmc.stats.Monodirectional(reference_uvw = (0,0,1)) + source.space = openmc.stats.Point((0, 0, -20)) + source.angle = openmc.stats.Monodirectional(reference_uvw=(0, 0, 1)) source.energy = openmc.stats.Discrete([E0], [1.0]) # Execution settings @@ -48,13 +47,13 @@ def compute_angular_distribution(cfg, E0, N): tally1 = openmc.Tally(name="angular distribution") tally1.scores = ["current"] - filter1 = openmc.filter.SurfaceFilter(sample_sphere) - filter2 = openmc.filter.PolarFilter(np.linspace(0,np.pi,180+1)) - filter3 = openmc.filter.CellFromFilter(cell1) + filter1 = openmc.SurfaceFilter(sample_sphere) + filter2 = openmc.PolarFilter(np.linspace(0, np.pi, 180+1)) + filter3 = openmc.CellFromFilter(cell1) tally1.filters = [filter1, filter2, filter3] tallies = openmc.Tallies([tally1]) - return openmc.model.Model(geometry, materials, settings, tallies) + return openmc.Model(geometry, materials, settings, tallies) class NCrystalTest(PyAPITestHarness): @@ -62,9 +61,9 @@ class NCrystalTest(PyAPITestHarness): """Digest info in the statepoint and return as a string.""" # Read the statepoint file. - sp = openmc.StatePoint(self._sp_name) - tal = sp.get_tally(name='angular distribution') - df = tal.get_pandas_dataframe() + with openmc.StatePoint(self._sp_name) as sp: + tal = sp.get_tally(name='angular distribution') + df = tal.get_pandas_dataframe() return df.to_string() def test_ncrystal(): diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index f5779c9351..ac10bb3f65 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -56,8 +56,8 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys if ncrystal: cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') - ncrystal_cmake_path = os.environ.get('HOME')+'/ncrystal_inst/lib/cmake' - cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + ncrystal_cmake_path) + ncrystal_cmake_path = os.environ.get('HOME') + '/ncrystal_inst/lib/cmake' + cmake_cmd.append(f'-DCMAKE_PREFIX_PATH={ncrystal_cmake_path}') # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index 85fdef49f5..1f1c3a1ebe 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -16,9 +16,9 @@ fi # Check NCrystal installation if [[ $NCRYSTAL = 'y' ]]; then - # Change environmental variables - eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) - nctool --test + # Change environmental variables + eval $( "${HOME}/ncrystal_inst/bin/ncrystal-config" --setup ) + nctool --test fi # Run regression and unit tests From a86e74f59a27236a4705ab0b4dc8c79736cd2c56 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 7 Dec 2022 12:51:31 +0100 Subject: [PATCH 209/265] Rename compute_angular_distribution to pencil_beam_model --- tests/regression_tests/ncrystal/test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 3137515d4a..2a789dac78 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -9,7 +9,7 @@ pytestmark = pytest.mark.skipif( not openmc.lib._ncrystal_enabled(), reason="NCrystal materials are not enabled.") -def compute_angular_distribution(cfg, E0, N): +def pencil_beam_model(cfg, E0, N): """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by the cfg string, and compute the angular distribution""" @@ -71,6 +71,6 @@ def test_ncrystal(): T = 293.6 # K E0 = 0.012 # eV cfg = 'Al_sg225.ncmat' - test = compute_angular_distribution(cfg, E0, NParticles) + test = pencil_beam_model(cfg, E0, NParticles) harness = NCrystalTest('statepoint.10.h5', model=test) harness.main() From ef13643f297dfe030c644d7a776fd85be54afdaa Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 7 Dec 2022 08:28:13 -0600 Subject: [PATCH 210/265] Some housekeeping in the Python classes --- openmc/model/model.py | 8 ++------ openmc/settings.py | 38 ++++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index a7a6f2d7da..1fdd127300 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -496,13 +496,10 @@ class Model: self.plots.export_to_xml(d) def _export_to_single_xml(self, path='model.xml', remove_surfs=False): - """Export model to XML files. + """Export model to a single XML file. Parameters ---------- - directory : str - Directory to write the model.xml file to. If it doesn't exist already, it - will be created. path : str or Pathlike Location of the XML file to write. Can be a directory or file path. remove_surfs : bool @@ -530,8 +527,7 @@ class Model: # Can be used to modify tallies in case any surfaces are redundant redundant_surfaces = self.geometry.remove_redundant_surfaces() - memo = set() - settings_element = self.settings.to_xml_element(memo) + settings_element = self.settings.to_xml_element() geometry_element = self.geometry.to_xml_element() xml.clean_indentation(geometry_element, level=1) diff --git a/openmc/settings.py b/openmc/settings.py index 3c1c8c7f5c..3f56f729f3 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1073,7 +1073,7 @@ class Settings: subelement = ET.SubElement(element, key) subelement.text = str(value) - def _create_entropy_mesh_subelement(self, root, memo=None): + def _create_entropy_mesh_subelement(self, root, mesh_memo=None): if self.entropy_mesh is not None: # use default heuristic for entropy mesh if not set by user if self.entropy_mesh.dimension is None: @@ -1085,13 +1085,20 @@ class Settings: d = len(self.entropy_mesh.lower_left) self.entropy_mesh.dimension = (n,)*d + # add mesh ID to this element + subelement = ET.SubElement(root, "entropy_mesh") + subelement.text = str(self.entropy_mesh.id) + + # If this mesh has already been written outside the + # settings element, skip writing it again + if mesh_memo and self.entropy_mesh.id in mesh_memo: + return + # See if a element already exists -- if not, add it path = f"./mesh[@id='{self.entropy_mesh.id}']" if root.find(path) is None: root.append(self.entropy_mesh.to_xml_element()) - - subelement = ET.SubElement(root, "entropy_mesh") - subelement.text = str(self.entropy_mesh.id) + if mesh_memo is not None: mesh_memo.add(self.entropy_mesh.id) def _create_trigger_subelement(self, root): if self._trigger_active is not None: @@ -1207,20 +1214,21 @@ class Settings: elem = ET.SubElement(root, "write_initial_source") elem.text = str(self._write_initial_source).lower() - def _create_weight_windows_subelement(self, root, memo=None): + def _create_weight_windows_subelement(self, root, mesh_memo=None): for ww in self._weight_windows: # Add weight window information root.append(ww.to_xml_element()) - # check the memo for a mesh - if memo and ww.mesh.id in memo: + # if this mesh has already been written, + # skip writing the mesh element + if mesh_memo and ww.mesh.id in mesh_memo: continue # See if a element already exists -- if not, add it path = f"./mesh[@id='{ww.mesh.id}']" if root.find(path) is None: root.append(ww.mesh.to_xml_element()) - if memo is not None: memo.add(ww.mesh.id) + if mesh_memo is not None: mesh_memo.add(ww.mesh.id) if self._weight_windows_on is not None: elem = ET.SubElement(root, "weight_windows_on") @@ -1544,10 +1552,16 @@ class Settings: if text is not None: self.max_tracks = int(text) - def to_xml_element(self, memo=None): + def to_xml_element(self, mesh_memo=None): """Create a 'settings' element to be written to an XML file. - """ + Parameters + ---------- + mesh_memo : set of ints + A set of mesh IDs to keep track of whether a mesh has already been written. + """ + # create a memo object if one isn't passed in already + mesh_memo = mesh_memo if mesh_memo else set() # Reset xml element tree element = ET.Element("settings") @@ -1574,7 +1588,7 @@ class Settings: self._create_seed_subelement(element) self._create_survival_biasing_subelement(element) self._create_cutoff_subelement(element) - self._create_entropy_mesh_subelement(element, memo) + self._create_entropy_mesh_subelement(element, mesh_memo) self._create_trigger_subelement(element) self._create_no_reduce_subelement(element) self._create_verbosity_subelement(element) @@ -1592,7 +1606,7 @@ class Settings: self._create_material_cell_offsets_subelement(element) self._create_log_grid_bins_subelement(element) self._create_write_initial_source_subelement(element) - self._create_weight_windows_subelement(element, memo) + self._create_weight_windows_subelement(element, mesh_memo) self._create_max_splits_subelement(element) self._create_max_tracks_subelement(element) From 9c942db45eaca790dc0f7f142b801381d2574daa Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 7 Dec 2022 10:10:22 -0600 Subject: [PATCH 211/265] Correct use of mesh memo in single XML export --- openmc/model/model.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 1fdd127300..a54cd60763 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -527,7 +527,9 @@ class Model: # Can be used to modify tallies in case any surfaces are redundant redundant_surfaces = self.geometry.remove_redundant_surfaces() - settings_element = self.settings.to_xml_element() + # provide a memo to track which meshes have been written + mesh_memo = set() + settings_element = self.settings.to_xml_element(mesh_memo) geometry_element = self.geometry.to_xml_element() xml.clean_indentation(geometry_element, level=1) @@ -554,7 +556,7 @@ class Model: ET.ElementTree(settings_element).write(fh, encoding='unicode') if self.tallies: - tallies_element = self.tallies.to_xml_element(memo) + tallies_element = self.tallies.to_xml_element(mesh_memo) xml.clean_indentation(tallies_element, level=1, trailing_indent=self.plots) ET.ElementTree(tallies_element).write(fh, encoding='unicode') if self.plots: From 916863f82ae304ff6f936701cbb058c5e7c2583a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 7 Dec 2022 11:02:10 -0600 Subject: [PATCH 212/265] Renaming and docstring/comment updates --- include/openmc/file_utils.h | 8 +++++--- include/openmc/geometry_aux.h | 2 +- openmc/settings.py | 2 -- src/initialize.cpp | 6 ++++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/openmc/file_utils.h b/include/openmc/file_utils.h index 439af6ee08..cb52b33c89 100644 --- a/include/openmc/file_utils.h +++ b/include/openmc/file_utils.h @@ -11,7 +11,8 @@ namespace openmc { //! Determine if a path is a directory //! \param[in] path Path to check //! \return Whether the path is a directory -inline bool is_dir(const std::string& path) { +inline bool dir_exists(const std::string& path) +{ struct stat s; if (stat(path.c_str(), &s) != 0) return false; @@ -23,8 +24,9 @@ inline bool is_dir(const std::string& path) { //! \return Whether file exists inline bool file_exists(const std::string& filename) { - // rule out file being a directory path - if (is_dir(filename)) return false; + // rule out file being a path to a directory + if (dir_exists(filename)) + return false; std::ifstream s {filename}; return s.good(); diff --git a/include/openmc/geometry_aux.h b/include/openmc/geometry_aux.h index a4c506c31a..cf62debc04 100644 --- a/include/openmc/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -24,7 +24,7 @@ extern std::unordered_map universe_level_counts; void read_geometry_xml(); //! Read geometry from XML node -//! \param[in] root node of geometry XML element +//! \param[in] root node of geometry XML element void read_geometry_xml(pugi::xml_node root); //============================================================================== diff --git a/openmc/settings.py b/openmc/settings.py index 3f56f729f3..294aea7a3b 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1560,8 +1560,6 @@ class Settings: mesh_memo : set of ints A set of mesh IDs to keep track of whether a mesh has already been written. """ - # create a memo object if one isn't passed in already - mesh_memo = mesh_memo if mesh_memo else set() # Reset xml element tree element = ET.Element("settings") diff --git a/src/initialize.cpp b/src/initialize.cpp index 041712d5a2..3b332c3b27 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -283,7 +283,8 @@ int parse_command_line(int argc, char* argv[]) settings::path_input = std::string(argv[last_flag + 1]); // check that the path is either a valid directory or file - if (!is_dir(settings::path_input) && !file_exists(settings::path_input)) { + if (!dir_exists(settings::path_input) && + !file_exists(settings::path_input)) { fatal_error(fmt::format( "The path specified to the OpenMC executable '{}' does not exist.", settings::path_input)); @@ -309,7 +310,8 @@ bool read_model_xml() { model_filename.pop_back(); // if the current filename is a directory, append the default model filename - if (is_dir(model_filename)) model_filename += "/model.xml"; + if (dir_exists(model_filename)) + model_filename += "/model.xml"; // if this file doesn't exist, stop here if (!file_exists(model_filename)) return false; From 145594ebb8697ff2c711b6a8f57abbfedfebf8c7 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 12 Dec 2022 20:23:49 -0600 Subject: [PATCH 213/265] Apply @paulromano suggestions from code review Co-authored-by: Paul Romano --- openmc/_xml.py | 2 +- openmc/executor.py | 3 +-- openmc/material.py | 1 - openmc/model/model.py | 2 +- src/initialize.cpp | 4 ++-- tests/regression_tests/model_xml/test.py | 3 ++- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index 6d298638da..aeaeb45b52 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -16,7 +16,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True """ i = "\n" + level*spaces_per_level*" " - # ensure there's awlays some tail for the element passed in + # ensure there's always some tail for the element passed in if not element.tail: element.tail = "" diff --git a/openmc/executor.py b/openmc/executor.py index 9267366e89..38421ecc2c 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -95,7 +95,6 @@ def _run(args, output, cwd): p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) - print(args) # Capture and re-print OpenMC output in real-time lines = [] while True: @@ -146,7 +145,7 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): """ args = [openmc_exec, '-p'] if path_input is not None: - args += ['-i', path_input] + args += [path_input] _run([openmc_exec, '-p'], output, cwd) diff --git a/openmc/material.py b/openmc/material.py index 332f8c9659..aa778cab64 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1548,7 +1548,6 @@ class Materials(cv.CheckedList): return materials - @classmethod def from_xml(cls, path: PathLike = 'materials.xml'): """Generate materials collection from XML file diff --git a/openmc/model/model.py b/openmc/model/model.py index a54cd60763..9983d5c60c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -259,7 +259,7 @@ class Model: materials = {str(m.id): m for m in model.materials} model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), materials) - # gather meshses from other classes before reading the tally node + # gather meshes from other classes before reading the tally node meshes = {} if model.settings.entropy_mesh is not None: meshes[model.settings.entropy_mesh.id] = model.settings.entropy_mesh diff --git a/src/initialize.cpp b/src/initialize.cpp index 3b332c3b27..0c137795bb 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -352,7 +352,7 @@ bool read_model_xml() { auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", "tallies.xml", "plots.xml"}; for (const auto& input : other_inputs) { if (file_exists(settings::path_input + input)) { - warning((fmt::format("Other XML file input(s) are present. These file " + warning((fmt::format("Other XML file input(s) are present. These files " "will be ignored in favor of the {} file.", model_filename))); break; @@ -371,7 +371,7 @@ bool read_model_xml() { // Read geometry if (!check_for_node(root, "geometry")) { fatal_error(fmt::format( - "No node present in the model.xml_file.", model_filename)); + "No node present in the {} file.", model_filename)); } read_geometry_xml(root.child("geometry")); diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index d040f4649f..fbff02357f 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -81,7 +81,8 @@ def test_input_arg(run_in_tmpdir): openmc.run() # make sure the executable isn't falling back on the separate XMLs - [os.remove(f) for f in glob.glob('*.xml')] + for f in glob.glob('*.xml'): + os.remove(f) # now export to a single XML file with a custom name pincell.export_to_xml(path='pincell.xml', separate_xmls=False) assert Path('pincell.xml').exists() From f3bcad37b72629cfc64e857aebdbe7c38461cabb Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 12 Dec 2022 21:18:48 -0600 Subject: [PATCH 214/265] Addressing comments from @paulromano --- openmc/executor.py | 32 ++++++----- openmc/geometry.py | 19 ++++--- openmc/model/model.py | 33 ++---------- openmc/settings.py | 68 +++++++++++++----------- tests/regression_tests/model_xml/test.py | 11 ++-- tests/unit_tests/test_model.py | 10 ++-- 6 files changed, 85 insertions(+), 88 deletions(-) diff --git a/openmc/executor.py b/openmc/executor.py index 38421ecc2c..22862a7cf9 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -43,7 +43,8 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, MPI execute command and any additional MPI arguments to pass, e.g. ['mpiexec', '-n', '8']. path_input : str or Pathlike - Name of a single XML input file for the OpenMC executable to read. + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. .. versionadded:: 0.13.0 @@ -135,7 +136,8 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): cwd : str, optional Path to working directory to run in path_input : str - Name of a single XML input file for the OpenMC executable to read. + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. Raises ------ @@ -146,7 +148,7 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): args = [openmc_exec, '-p'] if path_input is not None: args += [path_input] - _run([openmc_exec, '-p'], output, cwd) + _run(args, output, cwd) def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): @@ -166,7 +168,8 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): cwd : str, optional Path to working directory to run in path_input : str - Name of a single XML input file for the OpenMC executable to read. + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. Raises ------ @@ -224,7 +227,9 @@ def calculate_volumes(threads=None, output=True, cwd='.', Path to working directory to run in. Defaults to the current working directory. path_input : str or Pathlike - Name of a single XML input file for the OpenMC executable to read. + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. + Raises ------ @@ -256,17 +261,17 @@ def run(particles=None, threads=None, geometry_debug=False, Number of particles to simulate per generation. threads : int, optional Number of OpenMP threads. If OpenMC is compiled with OpenMP threading - enabled, the default is implementation-dependent but is usually equal - to the number of hardware threads available (or a value set by the + enabled, the default is implementation-dependent but is usually equal to + the number of hardware threads available (or a value set by the :envvar:`OMP_NUM_THREADS` environment variable). geometry_debug : bool, optional Turn on geometry debugging during simulation. Defaults to False. restart_file : str, optional Path to restart file to use tracks : bool, optional - Enables the writing of particles tracks. The number of particle - tracks written to tracks.h5 is limited to 1000 unless - Settings.max_tracks is set. Defaults to False. + Enables the writing of particles tracks. The number of particle tracks + written to tracks.h5 is limited to 1000 unless Settings.max_tracks is + set. Defaults to False. output : bool Capture OpenMC output from standard out cwd : str, optional @@ -275,15 +280,16 @@ def run(particles=None, threads=None, geometry_debug=False, openmc_exec : str, optional Path to OpenMC executable. Defaults to 'openmc'. mpi_args : list of str, optional - MPI execute command and any additional MPI arguments to pass, - e.g. ['mpiexec', '-n', '8']. + MPI execute command and any additional MPI arguments to pass, e.g. + ['mpiexec', '-n', '8']. event_based : bool, optional Turns on event-based parallelism, instead of default history-based .. versionadded:: 0.12 path_input : str or Pathlike - Name of a single XML input file for the OpenMC executable to read. + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. Raises ------ diff --git a/openmc/geometry.py b/openmc/geometry.py index afcae1d550..a43899daaf 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -179,6 +179,11 @@ class Geometry: Geometry object """ + mats = dict() + if materials is not None: + mats.update({str(m.id): m for m in materials}) + mats['void'] = None + # Helper function for keeping a cache of Universe instances universes = {} def get_universe(univ_id): @@ -236,7 +241,7 @@ class Geometry: child_of[u].append(lat) for e in elem.findall('cell'): - c = openmc.Cell.from_xml_element(e, surfaces, materials, get_universe) + c = openmc.Cell.from_xml_element(e, surfaces, mats, get_universe) if c.fill_type in ('universe', 'lattice'): child_of[c.fill].append(c) @@ -266,17 +271,15 @@ class Geometry: Geometry object """ - tree = ET.parse(path) - root = tree.getroot() - - # Create dictionary to easily look up materials + # Create dictionary to easily look up materials if materials is None: filename = Path(path).parent / 'materials.xml' materials = openmc.Materials.from_xml(str(filename)) - mats = {str(m.id): m for m in materials} - mats['void'] = None - return cls.from_xml_element(root, mats) + tree = ET.parse(path) + root = tree.getroot() + + return cls.from_xml_element(root, materials) def find(self, point): """Find cells/universes/lattices which contain a given point diff --git a/openmc/model/model.py b/openmc/model/model.py index 9983d5c60c..a8dc1ce484 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -256,8 +256,7 @@ class Model: model.settings = openmc.Settings.from_xml_element(root.find('settings')) model.materials = openmc.Materials.from_xml_element(root.find('materials')) - materials = {str(m.id): m for m in model.materials} - model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), materials) + model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) # gather meshes from other classes before reading the tally node meshes = {} @@ -435,30 +434,7 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.', remove_surfs=False, separate_xmls=True, path='model.xml'): - """Export model to an XML file(s). - - Parameters - ---------- - directory : str - Directory to write XML files to. If it doesn't exist already, it - will be created. Only used if :math:`separate_xmls` is True. - remove_surfs : bool - Whether or not to remove redundant surfaces from the geometry when - exporting. - path : str - Path to an output filename or directory. Only used if :math:`separate_xmls` is False. - - .. versionadded:: 0.13.1 - separate_xmls : bool - Whether or not to write a single model.xml file or many XML files. - """ - if separate_xmls: - self._export_to_separate_xmls(directory, remove_surfs) - else: - self._export_to_single_xml(path, remove_surfs) - - def _export_to_separate_xmls(self, directory='.', remove_surfs=False): + def export_to_xml(self, directory='.', remove_surfs=False): """Export model to separate XML files. Parameters @@ -495,13 +471,14 @@ class Model: if self.plots: self.plots.export_to_xml(d) - def _export_to_single_xml(self, path='model.xml', remove_surfs=False): + def export_to_model_xml(self, path='model.xml', remove_surfs=False): """Export model to a single XML file. Parameters ---------- path : str or Pathlike - Location of the XML file to write. Can be a directory or file path. + Location of the XML file to write (default is 'model.xml'). Can be a + directory or file path. remove_surfs : bool Whether or not to remove redundant surfaces from the geometry when exporting. diff --git a/openmc/settings.py b/openmc/settings.py index 294aea7a3b..8f6d3ec078 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1074,31 +1074,33 @@ class Settings: subelement.text = str(value) def _create_entropy_mesh_subelement(self, root, mesh_memo=None): - if self.entropy_mesh is not None: - # use default heuristic for entropy mesh if not set by user - if self.entropy_mesh.dimension is None: - if self.particles is None: - raise RuntimeError("Number of particles must be set in order to " \ - "use entropy mesh dimension heuristic") - else: - n = ceil((self.particles / 20.0)**(1.0 / 3.0)) - d = len(self.entropy_mesh.lower_left) - self.entropy_mesh.dimension = (n,)*d + if self.entropy_mesh is None: + return - # add mesh ID to this element - subelement = ET.SubElement(root, "entropy_mesh") - subelement.text = str(self.entropy_mesh.id) + # use default heuristic for entropy mesh if not set by user + if self.entropy_mesh.dimension is None: + if self.particles is None: + raise RuntimeError("Number of particles must be set in order to " \ + "use entropy mesh dimension heuristic") + else: + n = ceil((self.particles / 20.0)**(1.0 / 3.0)) + d = len(self.entropy_mesh.lower_left) + self.entropy_mesh.dimension = (n,)*d - # If this mesh has already been written outside the - # settings element, skip writing it again - if mesh_memo and self.entropy_mesh.id in mesh_memo: - return + # add mesh ID to this element + subelement = ET.SubElement(root, "entropy_mesh") + subelement.text = str(self.entropy_mesh.id) - # See if a element already exists -- if not, add it - path = f"./mesh[@id='{self.entropy_mesh.id}']" - if root.find(path) is None: - root.append(self.entropy_mesh.to_xml_element()) - if mesh_memo is not None: mesh_memo.add(self.entropy_mesh.id) + # If this mesh has already been written outside the + # settings element, skip writing it again + if mesh_memo and self.entropy_mesh.id in mesh_memo: + return + + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{self.entropy_mesh.id}']" + if root.find(path) is None: + root.append(self.entropy_mesh.to_xml_element()) + if mesh_memo is not None: mesh_memo.add(self.entropy_mesh.id) def _create_trigger_subelement(self, root): if self._trigger_active is not None: @@ -1149,15 +1151,21 @@ class Settings: element = ET.SubElement(root, "track") element.text = ' '.join(map(str, itertools.chain(*self._track))) - def _create_ufs_mesh_subelement(self, root): - if self.ufs_mesh is not None: - # See if a element already exists -- if not, add it - path = f"./mesh[@id='{self.ufs_mesh.id}']" - if root.find(path) is None: - root.append(self.ufs_mesh.to_xml_element()) + def _create_ufs_mesh_subelement(self, root, mesh_memo=None): + if self.ufs_mesh is None: + return - subelement = ET.SubElement(root, "ufs_mesh") - subelement.text = str(self.ufs_mesh.id) + subelement = ET.SubElement(root, "ufs_mesh") + subelement.text = str(self.ufs_mesh.id) + + if mesh_memo and self.ufs_mesh.id in mesh_memo: + return + + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{self.ufs_mesh.id}']" + if root.find(path) is None: + root.append(self.ufs_mesh.to_xml_element()) + if mesh_memo is not None: mesh_memo.add(self.ufs_mesh.id) def _create_resonance_scattering_subelement(self, root): res = self.resonance_scattering diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py index fbff02357f..c67a72ed37 100644 --- a/tests/regression_tests/model_xml/test.py +++ b/tests/regression_tests/model_xml/test.py @@ -27,7 +27,7 @@ class ModelXMLTestHarness(PyAPITestHarness): self.results_true = 'results_true.dat' if results_true is None else results_true def _build_inputs(self): - self._model.export_to_xml(separate_xmls=False) + self._model.export_to_model_xml() def _get_inputs(self): return open('model.xml').read() @@ -77,21 +77,24 @@ def test_input_arg(run_in_tmpdir): pincell.settings.particles = 100 # export to separate XML files and run - pincell.export_to_xml(separate_xmls=True) + pincell.export_to_xml() openmc.run() # make sure the executable isn't falling back on the separate XMLs for f in glob.glob('*.xml'): os.remove(f) # now export to a single XML file with a custom name - pincell.export_to_xml(path='pincell.xml', separate_xmls=False) + pincell.export_to_model_xml('pincell.xml') assert Path('pincell.xml').exists() # run by specifying that single file openmc.run(path_input='pincell.xml') + # check that this works for plotting too + openmc.plot_geometry(path_input='pincell.xml') + # now ensure we get an error for an incorrect filename, # even in the presence of other, valid XML files - pincell.export_to_xml(separate_xmls=True) + pincell.export_to_model_xml() with pytest.raises(RuntimeError, match='ex-em-ell.xml'): openmc.run(path_input='ex-em-ell.xml') \ No newline at end of file diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 11a88d76ce..6c483aeb94 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -542,18 +542,18 @@ def test_model_xml(run_in_tmpdir): pwr_model.geometry.export_to_xml('geometry_ref.xml') # now write and read a model.xml file - pwr_model.export_to_xml(separate_xmls=False) + pwr_model.export_to_model_xml() new_model = openmc.Model.from_model_xml() # make sure we can also export this again to separate # XML files - new_model.export_to_xml(separate_xmls=True) + new_model.export_to_xml() -def test_model_exec(run_in_tmpdir): +def test_single_xml_exec(run_in_tmpdir): pincell_model = openmc.examples.pwr_pin_cell() - pincell_model.export_to_xml(path='pwr_pincell.xml', separate_xmls=False) + pincell_model.export_to_model_xml('pwr_pincell.xml') openmc.run(path_input='pwr_pincell.xml') @@ -562,7 +562,7 @@ def test_model_exec(run_in_tmpdir): # test that a file in a different directory can be used os.mkdir('inputs') - pincell_model.export_to_xml(path='./inputs/pincell.xml', separate_xmls=False) + pincell_model.export_to_model_xml('./inputs/pincell.xml') openmc.run(path_input='./inputs/pincell.xml') with pytest.raises(RuntimeError, match='input_dir'): From 43687e5194f854d6d2a9fdf6287ec0c9b45bf3a1 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 12 Dec 2022 21:40:48 -0600 Subject: [PATCH 215/265] Improve comment on recursion arguments --- openmc/_xml.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index aeaeb45b52..2a33d4b8d7 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -26,8 +26,10 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True if trailing_indent and (not element.tail or not element.tail.strip()): element.tail = i for sub_element in element: - # `trailing_indent` intentionally not passed to the recursive call. - # it only applies to the element for the initial of this function. + # `trailing_indent` is intentionally not forwarded to the recursive + # call. Any child element of the topmost clement should add + # indentation at the end to ensure its parent's indentation is + # correct. clean_indentation(sub_element, level+1, spaces_per_level) if not sub_element.tail or not sub_element.tail.strip(): sub_element.tail = i From 2eca57a299ce91b9ddb68b8d6b40313ad6ddd708 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Tue, 13 Dec 2022 23:16:57 -0600 Subject: [PATCH 216/265] Adding mesh memo to Settings.from_xml_element --- openmc/model/model.py | 4 ++-- openmc/settings.py | 25 ++++++++++++++++++------- openmc/tallies.py | 4 ++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index a8dc1ce484..7c6d9b828c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -254,12 +254,12 @@ class Model: model = cls() - model.settings = openmc.Settings.from_xml_element(root.find('settings')) + meshes = {} + model.settings = openmc.Settings.from_xml_element(root.find('settings'), meshes) model.materials = openmc.Materials.from_xml_element(root.find('materials')) model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) # gather meshes from other classes before reading the tally node - meshes = {} if model.settings.entropy_mesh is not None: meshes[model.settings.entropy_mesh.id] = model.settings.entropy_mesh diff --git a/openmc/settings.py b/openmc/settings.py index 8f6d3ec078..65ca719572 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1417,13 +1417,15 @@ class Settings: if value is not None: self.cutoff[key] = float(value) - def _entropy_mesh_from_xml_element(self, root): + def _entropy_mesh_from_xml_element(self, root, meshes=None): text = get_text(root, 'entropy_mesh') if text is not None: path = f"./mesh[@id='{int(text)}']" elem = root.find(path) if elem is not None: self.entropy_mesh = RegularMesh.from_xml_element(elem) + if meshes is not None and self.entropy_mesh is not None: + meshes[self.entropy_mesh.id] = self.entropy_mesh def _trigger_from_xml_element(self, root): elem = root.find('trigger') @@ -1483,13 +1485,15 @@ class Settings: values = [int(x) for x in text.split()] self.track = list(zip(values[::3], values[1::3], values[2::3])) - def _ufs_mesh_from_xml_element(self, root): + def _ufs_mesh_from_xml_element(self, root, meshes=None): text = get_text(root, 'ufs_mesh') if text is not None: path = f"./mesh[@id='{int(text)}']" elem = root.find(path) if elem is not None: self.ufs_mesh = RegularMesh.from_xml_element(elem) + if meshes is not None and self.ufs_mesh is not None: + meshes[self.ufs_mesh.id] = self.ufs_mesh def _resonance_scattering_from_xml_element(self, root): elem = root.find('resonance_scattering') @@ -1541,7 +1545,7 @@ class Settings: if text is not None: self.write_initial_source = text in ('true', '1') - def _weight_windows_from_xml_element(self, root): + def _weight_windows_from_xml_element(self, root, meshes=None): for elem in root.findall('weight_windows'): ww = WeightWindows.from_xml_element(elem, root) self.weight_windows.append(ww) @@ -1550,6 +1554,9 @@ class Settings: if text is not None: self.weight_windows_on = text in ('true', '1') + if meshes is not None and self.weight_windows: + meshes.update({ww.mesh.id: ww.mesh for ww in self.weight_windows}) + def _max_splits_from_xml_element(self, root): text = get_text(root, 'max_splits') if text is not None: @@ -1643,13 +1650,17 @@ class Settings: tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem, meshes=None): """Generate settings from XML element Parameters ---------- elem : xml.etree.ElementTree.Element XML element + meshes : dict or None + A dictionary with mesh IDs as keys and mesh instances as values that + have already been read from XML. Pre-existing meshes are used + and new meshes are added to when creating tally objects. Returns ------- @@ -1683,7 +1694,7 @@ class Settings: settings._seed_from_xml_element(elem) settings._survival_biasing_from_xml_element(elem) settings._cutoff_from_xml_element(elem) - settings._entropy_mesh_from_xml_element(elem) + settings._entropy_mesh_from_xml_element(elem, meshes) settings._trigger_from_xml_element(elem) settings._no_reduce_from_xml_element(elem) settings._verbosity_from_xml_element(elem) @@ -1691,7 +1702,7 @@ class Settings: settings._temperature_from_xml_element(elem) settings._trace_from_xml_element(elem) settings._track_from_xml_element(elem) - settings._ufs_mesh_from_xml_element(elem) + settings._ufs_mesh_from_xml_element(elem, meshes) settings._resonance_scattering_from_xml_element(elem) settings._create_fission_neutrons_from_xml_element(elem) settings._delayed_photon_scaling_from_xml_element(elem) @@ -1700,7 +1711,7 @@ class Settings: settings._material_cell_offsets_from_xml_element(elem) settings._log_grid_bins_from_xml_element(elem) settings._write_initial_source_from_xml_element(elem) - settings._weight_windows_from_xml_element(elem) + settings._weight_windows_from_xml_element(elem, meshes) settings._max_splits_from_xml_element(elem) settings._max_tracks_from_xml_element(elem) diff --git a/openmc/tallies.py b/openmc/tallies.py index e17e7c99db..b22cc4cf0a 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -3199,6 +3199,10 @@ class Tallies(cv.CheckedList): ---------- elem : xml.etree.ElementTree.Element XML element + meshes : dict or None + A dictionary with mesh IDs as keys and mesh instances as values that + have already been read from XML. Pre-existing meshes are used + and new meshes are added to when creating tally objects. Returns ------- From 2b7fa8fdd2dbe313e14cf2d789f9cad1260d964e Mon Sep 17 00:00:00 2001 From: erkn Date: Tue, 20 Dec 2022 23:58:22 +0100 Subject: [PATCH 217/265] Revert "enable mcpl from the python layer" This reverts commit e3f1bdaca1f5144563c4ceac45f10e4b0b5b7963. There is now a check in the cpp-layer if the file is named .mcpl --- openmc/source.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openmc/source.py b/openmc/source.py index fdb2d5bc25..48e14f6f71 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -223,10 +223,7 @@ class Source: if self.particle != 'neutron': element.set("particle", self.particle) if self.file is not None: - if (self.file.endswith('.mcpl')): - element.set("mcpl", self.file) - else: - element.set("file", self.file) + element.set("file", self.file) if self.library is not None: element.set("library", self.library) if self.parameters is not None: From 1ab9aa4b6fddf4e6b7216b7fe993d8c672c22468 Mon Sep 17 00:00:00 2001 From: erkn Date: Wed, 21 Dec 2022 00:14:43 +0100 Subject: [PATCH 218/265] script for installing mcpl as a ci-thing --- tools/ci/gha-install-mcpl.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 tools/ci/gha-install-mcpl.sh diff --git a/tools/ci/gha-install-mcpl.sh b/tools/ci/gha-install-mcpl.sh new file mode 100755 index 0000000000..9b8609398a --- /dev/null +++ b/tools/ci/gha-install-mcpl.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -ex +cd $HOME +git clone https://github.com/mctools/mcpl +cd mcpl +mkdir build && cd build +cmake .. && make 2>/dev/null && sudo make install From 9865a129c49ad2b07628d152a2d8865ad6936fd6 Mon Sep 17 00:00:00 2001 From: erkn Date: Wed, 21 Dec 2022 00:22:38 +0100 Subject: [PATCH 219/265] optionally install mcpl depending on environment var --- tools/ci/gha-install.py | 8 ++++++-- tools/ci/gha-install.sh | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 4c69ba70b0..ea317b5f0a 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -19,7 +19,7 @@ def which(program): return None -def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, mcpl=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') @@ -54,6 +54,9 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) + if mcpl: + cmake_cmd.append('-DOPENMC_USE_MCPL=ON') + # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') @@ -71,9 +74,10 @@ def main(): phdf5 = (os.environ.get('PHDF5') == 'y') dagmc = (os.environ.get('DAGMC') == 'y') libmesh = (os.environ.get('LIBMESH') == 'y') + mcpl = (os.environ.get('MCPL') == 'y') # Build and install - install(omp, mpi, phdf5, dagmc, libmesh) + install(omp, mpi, phdf5, dagmc, libmesh, mcpl) if __name__ == '__main__': main() diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index aa40eb90b1..68e825d486 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -27,6 +27,11 @@ if [[ $LIBMESH = 'y' ]]; then ./tools/ci/gha-install-libmesh.sh fi +# Install mcpl if needed +if [[ $MCPL = 'y' ]]; then + ./tools/ci/gha-install-mcpl.sh +fi + # For MPI configurations, make sure mpi4py and h5py are built against the # correct version of MPI if [[ $MPI == 'y' ]]; then From a50776219fe659be4572a918d4e19bfb9596a5f3 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Wed, 21 Dec 2022 11:33:36 +0100 Subject: [PATCH 220/265] resolve conflict w. develop --- src/source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source.cpp b/src/source.cpp index af108f3fa9..19d1c2a9db 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -234,7 +234,7 @@ SourceSite IndependentSource::sample(uint64_t* seed) const auto id = (domain_type_ == DomainType::CELL) ? model::cells[coord.cell]->id_ : model::universes[coord.universe]->id_; - if (found = contains(domain_ids_, id)) + if ((found = contains(domain_ids_, id))) break; } } From ff48f53595144aa0ecbc1f44f2d28428a7306633 Mon Sep 17 00:00:00 2001 From: erkn Date: Thu, 22 Dec 2022 12:04:29 +0100 Subject: [PATCH 221/265] skip mcpl-test if it is not enabled patterned after the dagmc-tests This required the MCPL_ENABLED symbol to be unmangled --- include/openmc/source.h | 1 + tests/regression_tests/source_mcpl_file/test.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/openmc/source.h b/include/openmc/source.h index bb7e2d55f7..051167b3f6 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -32,6 +32,7 @@ constexpr double EXTSRC_REJECT_FRACTION {0.05}; //============================================================================== // Global variables //============================================================================== +extern "C" const bool MCPL_ENABLED; class Source; diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index a005fc2d83..0b8e79104e 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -1,10 +1,13 @@ #!/usr/bin/env python - +import openmc.lib +import pytest import glob import os from tests.testing_harness import * - +pytestmark = pytest.mark.skipif( + not openmc.lib._mcpl_enabled(), + reason="MCPL is not enabled.") settings1=""" From fe19cfcdd25966c3ab5cce056b9337c50e3e7743 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 22 Dec 2022 17:16:16 +0100 Subject: [PATCH 222/265] set seed to get a deterministic test and set result --- tests/regression_tests/source_mcpl_file/results_true.dat | 2 +- tests/regression_tests/source_mcpl_file/settings.xml | 1 + tests/regression_tests/source_mcpl_file/test.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat index 19460623f5..e8f4a20a08 100644 --- a/tests/regression_tests/source_mcpl_file/results_true.dat +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.039964E-01 3.654869E-04 +3.004254E-01 8.027600E-04 diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml index 47b010bffe..2e511461f3 100644 --- a/tests/regression_tests/source_mcpl_file/settings.xml +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -1,5 +1,6 @@ + 1234 diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 0b8e79104e..e87479c5a4 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -11,6 +11,7 @@ pytestmark = pytest.mark.skipif( settings1=""" + 1234 From e9c6b03b9e2f5fb50875c64a8a33db8eda1059dd Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 22 Dec 2022 17:17:50 +0100 Subject: [PATCH 223/265] remove dead code --- tests/regression_tests/source_mcpl_file/test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index e87479c5a4..6492783197 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -93,9 +93,6 @@ class SourceFileTestHarness(TestHarness): def _cleanup(self): TestHarness._cleanup(self) output = glob.glob(os.path.join(os.getcwd(), 'source.*')) - #for f in output: - # if os.path.exists(f): - # os.remove(f) with open('settings.xml','w') as fh: fh.write(settings1) From 4d7797c25b3932aaa166b4a6b12c4ffebd48d925 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 22 Dec 2022 17:18:37 +0100 Subject: [PATCH 224/265] mistake assigning the wrong variable --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index 5e8eb0e58f..b0829c1bad 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -656,7 +656,7 @@ void read_settings_xml() source_write = get_node_value_bool(node_sp, "write"); } if (check_for_node(node_sp, "mcpl")) { - source_write = get_node_value_bool(node_sp, "mcpl"); + source_mcpl_write = get_node_value_bool(node_sp, "mcpl"); } if (check_for_node(node_sp, "overwrite_latest")) { source_latest = get_node_value_bool(node_sp, "overwrite_latest"); From 51d3a0c2ce6079f18ea479c7efc158148236f5a7 Mon Sep 17 00:00:00 2001 From: Erik B Knudsen Date: Thu, 22 Dec 2022 17:19:56 +0100 Subject: [PATCH 225/265] check for mcpl-input is in the cpp-layer now --- tests/regression_tests/source_mcpl_file/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 6492783197..60e0ad50f8 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -35,7 +35,7 @@ settings2 = """ 1000 - source.10.{0} + source.10.{0} """ From 28521c8384c6e68b6ee0f52a7a44425b4a6306bd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 10:53:40 -0600 Subject: [PATCH 226/265] Use vector::push_back for building MCPL FileSource --- src/source.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/source.cpp b/src/source.cpp index 19d1c2a9db..4ab087e125 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -335,9 +335,8 @@ FileSource::FileSource(mcpl_file_t mcpl_file) { size_t n_sites = mcpl_hdr_nparticles(mcpl_file); - sites_.resize(n_sites); for (int i = 0; i < n_sites; i++) { - SourceSite site_; + SourceSite site; const mcpl_particle_t* mcpl_particle; // extract particle from mcpl-file @@ -351,36 +350,41 @@ FileSource::FileSource(mcpl_file_t mcpl_file) switch (pdg) { case 2112: - site_.particle = ParticleType::neutron; + site.particle = ParticleType::neutron; break; case 22: - site_.particle = ParticleType::photon; + site.particle = ParticleType::photon; break; case 11: - site_.particle = ParticleType::electron; + site.particle = ParticleType::electron; break; case -11: - site_.particle = ParticleType::positron; + site.particle = ParticleType::positron; break; } - // particle is good, convert to openmc-formalism - site_.r.x = mcpl_particle->position[0]; - site_.r.y = mcpl_particle->position[1]; - site_.r.z = mcpl_particle->position[2]; - - site_.u.x = mcpl_particle->direction[0]; - site_.u.y = mcpl_particle->direction[1]; - site_.u.z = mcpl_particle->direction[2]; + // Copy position and direction + site.r.x = mcpl_particle->position[0]; + site.r.y = mcpl_particle->position[1]; + site.r.z = mcpl_particle->position[2]; + site.u.x = mcpl_particle->direction[0]; + site.u.y = mcpl_particle->direction[1]; + site.u.z = mcpl_particle->direction[2]; // mcpl stores kinetic energy in MeV - site_.E = mcpl_particle->ekin * 1e6; + site.E = mcpl_particle->ekin * 1e6; // mcpl stores time in ms - site_.time = mcpl_particle->time * 1e-3; - site_.wgt = mcpl_particle->weight; - site_.delayed_group = 0; - sites_[i] = site_; + site.time = mcpl_particle->time * 1e-3; + site.wgt = mcpl_particle->weight; + sites_.push_back(site); } + + // Check that some sites were read + if (sites_.empty()) { + fatal_error("MCPL file contained no neutron, photon, electron, or positron " + "source particles."); + } + mcpl_close_file(mcpl_file); } #endif // OPENMC_MCPL From 987293cfffb2934359b0c1f0dcd685eb8140fd63 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 10:57:11 -0600 Subject: [PATCH 227/265] Small fixes and formatting changes --- docs/source/io_formats/settings.rst | 8 ++-- include/openmc/state_point.h | 3 +- openmc/settings.py | 2 +- src/settings.cpp | 17 ++++--- src/simulation.cpp | 10 ++--- src/state_point.cpp | 70 ++++++++++++++--------------- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index a162e8de6d..7174c5317a 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -768,10 +768,10 @@ certain surfaces and write out the source bank in a separate file called *Default*: None :mcpl: - An optional boolean which indicates if the banked particles should - be written to a file in the MCPL-format (documented in mcpl_). - instead of the native hdf5-based format.If activated the output - output file name is altered to ``surface_source.mcpl`` + An optional boolean which indicates if the banked particles should be + written to a file in the MCPL-format (documented in mcpl_). instead of the + native HDF5-based format. If activated the output file name is changed to + ``surface_source.mcpl``. *Default*: false diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 698ed2c93a..7301275542 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -26,7 +26,8 @@ void restart_set_keff(); void write_unstructured_mesh_results(); #ifdef OPENMC_MCPL -void write_mcpl_source_point(const char* filename, bool surf_source_bank = false); +void write_mcpl_source_point( + const char* filename, bool surf_source_bank = false); void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); #endif diff --git a/openmc/settings.py b/openmc/settings.py index b2c184f780..fd622870c5 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1349,7 +1349,7 @@ class Settings: elif key in ('max_particles'): value = int(value) elif key == 'mcpl': - value = True + value = value in ('true', '1') self.surf_source_write[key] = value def _confidence_intervals_from_xml_element(self, root): diff --git a/src/settings.cpp b/src/settings.cpp index b0829c1bad..de298c58d1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -432,12 +432,15 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); #ifdef OPENMC_MCPL - if ( (path.size() >= 5 && path.compare(path.size()-5,5,".mcpl")==0 ) || - (path.size() >= 8 && path.compare(path.size()-8,8,".mcpl.gz")==0 ) ) { - model::external_sources.push_back(make_unique(mcpl_open_file(path.c_str()))); - } else -#endif + if (ends_with(path, ".mcpl") || ends_with(path, ".mcpl.gz")) { + model::external_sources.push_back( + make_unique(mcpl_open_file(path.c_str()))); + } else { model::external_sources.push_back(make_unique(path)); + } +#else + model::external_sources.push_back(make_unique(path)); +#endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); @@ -689,8 +692,8 @@ void read_settings_xml() std::stoll(get_node_value(node_ssw, "max_particles")); } #ifdef OPENMC_MCPL - if(check_for_node(node_ssw, "mcpl")){ - surf_mcpl_write=true; + if (check_for_node(node_ssw, "mcpl")) { + surf_mcpl_write = true; } #endif } diff --git a/src/simulation.cpp b/src/simulation.cpp index d7cc2237d5..2c1d1c988b 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -390,7 +390,7 @@ void finalize_batch() if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch #ifdef OPENMC_MCPL - if(! settings::source_mcpl_write) { + if (!settings::source_mcpl_write) { #endif if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_write && settings::source_separate) { @@ -405,8 +405,8 @@ void finalize_batch() #ifdef OPENMC_MCPL } else { if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_mcpl_write && settings::source_separate) { - write_mcpl_source_point(nullptr); + settings::source_mcpl_write && settings::source_separate) { + write_mcpl_source_point(nullptr); } // Write a continously-overwritten source point if requested. @@ -425,12 +425,12 @@ void finalize_batch() write_source_point(filename.c_str(), true); } #ifdef OPENMC_MCPL - if(settings::surf_mcpl_write && simulation::current_batch == settings::n_batches){ + if (settings::surf_mcpl_write && + simulation::current_batch == settings::n_batches) { auto filename = settings::path_output + "surface_source.mcpl"; write_mcpl_source_point(filename.c_str(), true); } #endif - } void initialize_generation() diff --git a/src/state_point.cpp b/src/state_point.cpp index 3ee5f13b39..9dd0f13747 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -604,7 +604,7 @@ void write_source_point(const char* filename, bool surf_source_bank) } #ifdef OPENMC_MCPL -void write_mcpl_source_point(const char *filename, bool surf_source_bank) +void write_mcpl_source_point(const char* filename, bool surf_source_bank) { std::string filename_; if (filename) { @@ -622,21 +622,21 @@ void write_mcpl_source_point(const char *filename, bool surf_source_bank) std::string line; if (mpi::master) { file_id = mcpl_create_outfile(filename_.c_str()); - if (VERSION_DEV){ - line=fmt::format("OpenMC {0}.{1}.{2}-development",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); + if (VERSION_DEV) { + line = fmt::format("OpenMC {0}.{1}.{2}-development", VERSION_MAJOR, + VERSION_MINOR, VERSION_RELEASE); } else { - line=fmt::format("OpenMC {0}.{1}.{2}",VERSION_MAJOR,VERSION_MINOR,VERSION_RELEASE); + line = fmt::format( + "OpenMC {0}.{1}.{2}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE); } - mcpl_hdr_set_srcname(file_id,line.c_str()); + mcpl_hdr_set_srcname(file_id, line.c_str()); } write_mcpl_source_bank(file_id, surf_source_bank); if (mpi::master) { - //change this - this is h5 specific mcpl_close_outfile(file_id); } - } #endif @@ -768,7 +768,7 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) vector surf_source_index_vector; vector surf_source_bank_vector; - if(surf_source_bank) { + if (surf_source_bank) { surf_source_index_vector = calculate_surf_source_size(); dims_size = surf_source_index_vector[mpi::n_procs]; count_size = simulation::surf_source_bank.size(); @@ -790,55 +790,56 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) vector temp_source {source_bank->begin(), source_bank->end()}; #endif - //loop over the other nodes and receive data - then write those. + // loop over the other nodes and receive data - then write those. for (int i = 0; i < mpi::n_procs; ++i) { // number of particles for node node i size_t count[] { static_cast((*bank_index)[i + 1] - (*bank_index)[i])}; #ifdef OPENMC_MPI - if (i>0) + if (i > 0) MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, mpi::intracomm, MPI_STATUS_IGNORE); #endif // now write the source_bank data again. - for (vector::iterator _site=source_bank->begin(); _site!=source_bank->end();_site++){ + for (vector::iterator _site = source_bank->begin(); + _site != source_bank->end(); _site++) { // particle is now at the iterator // write it to the mcpl-file mcpl_particle_t p; - p.position[0]=_site->r.x; - p.position[1]=_site->r.y; - p.position[2]=_site->r.z; + p.position[0] = _site->r.x; + p.position[1] = _site->r.y; + p.position[2] = _site->r.z; // mcpl requires that the direction vector is unit length // which is also the case in openmc - p.direction[0]=_site->u.x; - p.direction[1]=_site->u.y; - p.direction[2]=_site->u.z; + p.direction[0] = _site->u.x; + p.direction[1] = _site->u.y; + p.direction[2] = _site->u.z; // mcpl stores kinetic energy in MeV - p.ekin=_site->E*1e-6; + p.ekin = _site->E * 1e-6; - p.time=_site->time*1e3; + p.time = _site->time * 1e3; - p.weight=_site->wgt; + p.weight = _site->wgt; - switch(_site->particle){ - case ParticleType::neutron: - p.pdgcode=2112; - break; - case ParticleType::photon: - p.pdgcode=22; - break; - case ParticleType::electron: - p.pdgcode=11; - break; - case ParticleType::positron: - p.pdgcode=-11; - break; + switch (_site->particle) { + case ParticleType::neutron: + p.pdgcode = 2112; + break; + case ParticleType::photon: + p.pdgcode = 22; + break; + case ParticleType::electron: + p.pdgcode = 11; + break; + case ParticleType::positron: + p.pdgcode = -11; + break; } - mcpl_add_particle(file_id,&p); + mcpl_add_particle(file_id, &p); } } #ifdef OPENMC_MPI @@ -851,7 +852,6 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) mpi::intracomm); #endif } - } #endif From a3065b479c0a47caae6229fa8657da59668be1e9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 11:33:29 -0600 Subject: [PATCH 228/265] Move FileSource MCPL reading to mcpl_interface.h/cpp --- CMakeLists.txt | 1 + include/openmc/mcpl_interface.h | 25 +++++++++ include/openmc/source.h | 10 +--- src/mcpl_interface.cpp | 93 +++++++++++++++++++++++++++++++++ src/settings.cpp | 11 ++-- src/source.cpp | 69 ------------------------ 6 files changed, 126 insertions(+), 83 deletions(-) create mode 100644 include/openmc/mcpl_interface.h create mode 100644 src/mcpl_interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 24018eb0f1..af3a5b4af8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,6 +331,7 @@ list(APPEND libopenmc_SOURCES src/lattice.cpp src/material.cpp src/math_functions.cpp + src/mcpl_interface.cpp src/mesh.cpp src/message_passing.cpp src/mgxs.cpp diff --git a/include/openmc/mcpl_interface.h b/include/openmc/mcpl_interface.h new file mode 100644 index 0000000000..89dab16bcb --- /dev/null +++ b/include/openmc/mcpl_interface.h @@ -0,0 +1,25 @@ +#ifndef OPENMC_MCPL_INTERFACE_H +#define OPENMC_MCPL_INTERFACE_H + +#include "openmc/particle_data.h" + +#include +#include + +#ifdef OPENMC_MCPL +#include +#endif + +namespace openmc { + +extern "C" const bool MCPL_ENABLED; + +//! Get a vector of source sites from an MCPL file +// +//! \param[in] path Path to MCPL file +//! \return Vector of source sites +vector mcpl_source_sites(std::string path); + +} // namespace openmc + +#endif // OPENMC_MCPL_INTERFACE_H diff --git a/include/openmc/source.h b/include/openmc/source.h index 051167b3f6..96d659dcb3 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -14,10 +14,6 @@ #include "openmc/particle.h" #include "openmc/vector.h" -#ifdef OPENMC_MCPL -#include -#endif - namespace openmc { //============================================================================== @@ -32,7 +28,6 @@ constexpr double EXTSRC_REJECT_FRACTION {0.05}; //============================================================================== // Global variables //============================================================================== -extern "C" const bool MCPL_ENABLED; class Source; @@ -107,9 +102,8 @@ class FileSource : public Source { public: // Constructors explicit FileSource(std::string path); -#ifdef OPENMC_MCPL - explicit FileSource(mcpl_file_t mcpl_file); -#endif + explicit FileSource(const vector& sites) : sites_ {sites} {} + // Methods SourceSite sample(uint64_t* seed) const override; diff --git a/src/mcpl_interface.cpp b/src/mcpl_interface.cpp new file mode 100644 index 0000000000..d55cc5af7c --- /dev/null +++ b/src/mcpl_interface.cpp @@ -0,0 +1,93 @@ +#include "openmc/mcpl_interface.h" + +#include "openmc/error.h" + +#ifdef OPENMC_MCPL +#include +#endif + +namespace openmc { + +#ifdef OPENMC_MCPL +const bool MCPL_ENABLED = true; +#else +const bool MCPL_ENABLED = false; +#endif + +#ifdef OPENMC_MCPL +SourceSite mcpl_particle_to_site(const mcpl_particle* particle) +{ + SourceSite site; + + switch (particle->pdgcode) { + case 2112: + site.particle = ParticleType::neutron; + break; + case 22: + site.particle = ParticleType::photon; + break; + case 11: + site.particle = ParticleType::electron; + break; + case -11: + site.particle = ParticleType::positron; + break; + } + + // Copy position and direction + site.r.x = mcpl_particle->position[0]; + site.r.y = mcpl_particle->position[1]; + site.r.z = mcpl_particle->position[2]; + site.u.x = mcpl_particle->direction[0]; + site.u.y = mcpl_particle->direction[1]; + site.u.z = mcpl_particle->direction[2]; + + // mcpl stores kinetic energy in MeV + site.E = mcpl_particle->ekin * 1e6; + // mcpl stores time in ms + site.time = mcpl_particle->time * 1e-3; + site.wgt = mcpl_particle->weight; + + return site; +} +#endif + +vector mcpl_source_sites(std::string path) +{ + vector sites; + +#ifdef OPENMC_MCPL + size_t n_sites = mcpl_hdr_nparticles(mcpl_file); + + for (int i = 0; i < n_sites; i++) { + SourceSite site; + + // Extract particle from mcpl-file, checking if it is a neutron, photon, + // electron, or positron. Otherwise skip. + const mcpl_particle_t* particle; + int pdg = 0; + while (pdg != 2112 && pdg != 22 && pdg != 11 && pdg != -11) { + particle = mcpl_read(mcpl_file); + pdg = mcpl_particle->pdgcode; + } + + // Convert to source site and add to vector + sites.push_back(mcpl_particle_to_site(particle)); + } + + // Check that some sites were read + if (sites_.empty()) { + fatal_error("MCPL file contained no neutron, photon, electron, or positron " + "source particles."); + } + + mcpl_close_file(mcpl_file); +#else + fatal_error( + "Your build of OpenMC does not support reading MCPL source files."); +#endif + + return sites; +} + +} // namespace openmc diff --git a/src/settings.cpp b/src/settings.cpp index de298c58d1..bc31e2c4d0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,6 +18,7 @@ #include "openmc/eigenvalue.h" #include "openmc/error.h" #include "openmc/file_utils.h" +#include "openmc/mcpl_interface.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" #include "openmc/output.h" @@ -431,16 +432,14 @@ void read_settings_xml() for (pugi::xml_node node : root.children("source")) { if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); -#ifdef OPENMC_MCPL if (ends_with(path, ".mcpl") || ends_with(path, ".mcpl.gz")) { - model::external_sources.push_back( - make_unique(mcpl_open_file(path.c_str()))); +#ifdef OPENMC_MCPL + auto sites = mcpl_source_sites(path); + model::external_sources.push_back(make_unique(sites)); +#endif } else { model::external_sources.push_back(make_unique(path)); } -#else - model::external_sources.push_back(make_unique(path)); -#endif } else if (check_for_node(node, "library")) { // Get shared library path and parameters auto path = get_node_value(node, "library", false, true); diff --git a/src/source.cpp b/src/source.cpp index 4ab087e125..d201e3c033 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -33,22 +33,12 @@ #include "openmc/state_point.h" #include "openmc/xml_interface.h" -#ifdef OPENMC_MCPL -#include -#endif - namespace openmc { //============================================================================== // Global variables //============================================================================== -#ifdef OPENMC_MCPL -const bool MCPL_ENABLED = true; -#else -const bool MCPL_ENABLED = false; -#endif - namespace model { vector> external_sources; @@ -330,65 +320,6 @@ FileSource::FileSource(std::string path) file_close(file_id); } -#ifdef OPENMC_MCPL -FileSource::FileSource(mcpl_file_t mcpl_file) -{ - size_t n_sites = mcpl_hdr_nparticles(mcpl_file); - - for (int i = 0; i < n_sites; i++) { - SourceSite site; - - const mcpl_particle_t* mcpl_particle; - // extract particle from mcpl-file - mcpl_particle = mcpl_read(mcpl_file); - // check if it is a neutron, photon, electron, or positron. Otherwise skip. - int pdg = mcpl_particle->pdgcode; - while (pdg != 2112 && pdg != 22 && pdg != 11 && pdg != -11) { - mcpl_particle = mcpl_read(mcpl_file); - pdg = mcpl_particle->pdgcode; - } - - switch (pdg) { - case 2112: - site.particle = ParticleType::neutron; - break; - case 22: - site.particle = ParticleType::photon; - break; - case 11: - site.particle = ParticleType::electron; - break; - case -11: - site.particle = ParticleType::positron; - break; - } - - // Copy position and direction - site.r.x = mcpl_particle->position[0]; - site.r.y = mcpl_particle->position[1]; - site.r.z = mcpl_particle->position[2]; - site.u.x = mcpl_particle->direction[0]; - site.u.y = mcpl_particle->direction[1]; - site.u.z = mcpl_particle->direction[2]; - - // mcpl stores kinetic energy in MeV - site.E = mcpl_particle->ekin * 1e6; - // mcpl stores time in ms - site.time = mcpl_particle->time * 1e-3; - site.wgt = mcpl_particle->weight; - sites_.push_back(site); - } - - // Check that some sites were read - if (sites_.empty()) { - fatal_error("MCPL file contained no neutron, photon, electron, or positron " - "source particles."); - } - - mcpl_close_file(mcpl_file); -} -#endif // OPENMC_MCPL - SourceSite FileSource::sample(uint64_t* seed) const { size_t i_site = sites_.size() * prn(seed); From 361b3486efa4c078e87a1c29f76e6ab4a1edc58e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 12:27:56 -0600 Subject: [PATCH 229/265] Move remainder of MCPL-related code to mcpl_interface.h/cpp --- CMakeLists.txt | 1 + include/openmc/mcpl_interface.h | 19 +++- include/openmc/state_point.h | 10 -- src/mcpl_interface.cpp | 185 +++++++++++++++++++++++++++++--- src/settings.cpp | 18 +++- src/simulation.cpp | 7 +- src/state_point.cpp | 140 ------------------------ 7 files changed, 201 insertions(+), 179 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index af3a5b4af8..466816b300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,7 @@ endif() #=============================================================================== if (OPENMC_USE_MCPL) find_package(MCPL REQUIRED) + message(STATUS "Found MCPL: ${MCPL_DIR} (found version \"${MCPL_VERSION}\")") endif() #=============================================================================== diff --git a/include/openmc/mcpl_interface.h b/include/openmc/mcpl_interface.h index 89dab16bcb..c931f17743 100644 --- a/include/openmc/mcpl_interface.h +++ b/include/openmc/mcpl_interface.h @@ -6,20 +6,31 @@ #include #include -#ifdef OPENMC_MCPL -#include -#endif - namespace openmc { +//============================================================================== +// Constants +//============================================================================== + extern "C" const bool MCPL_ENABLED; +//============================================================================== +// Functions +//============================================================================== + //! Get a vector of source sites from an MCPL file // //! \param[in] path Path to MCPL file //! \return Vector of source sites vector mcpl_source_sites(std::string path); +//! Write an MCPL source file +// +//! \param[in] filename Path to MCPL file +//! \param[in] surf_source_bank Whether to use the surface source bank +void write_mcpl_source_point( + const char* filename, bool surf_source_bank = false); + } // namespace openmc #endif // OPENMC_MCPL_INTERFACE_H diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 7301275542..5ada2bf88d 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -9,10 +9,6 @@ #include "openmc/particle.h" #include "openmc/vector.h" -#ifdef OPENMC_MCPL -#include -#endif - namespace openmc { void load_state_point(); @@ -25,11 +21,5 @@ void write_tally_results_nr(hid_t file_id); void restart_set_keff(); void write_unstructured_mesh_results(); -#ifdef OPENMC_MCPL -void write_mcpl_source_point( - const char* filename, bool surf_source_bank = false); -void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank); -#endif - } // namespace openmc #endif // OPENMC_STATE_POINT_H diff --git a/src/mcpl_interface.cpp b/src/mcpl_interface.cpp index d55cc5af7c..e9bc648303 100644 --- a/src/mcpl_interface.cpp +++ b/src/mcpl_interface.cpp @@ -1,6 +1,13 @@ #include "openmc/mcpl_interface.h" +#include "openmc/bank.h" #include "openmc/error.h" +#include "openmc/message_passing.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/state_point.h" + +#include #ifdef OPENMC_MCPL #include @@ -8,14 +15,22 @@ namespace openmc { +//============================================================================== +// Constants +//============================================================================== + #ifdef OPENMC_MCPL const bool MCPL_ENABLED = true; #else const bool MCPL_ENABLED = false; #endif +//============================================================================== +// Functions +//============================================================================== + #ifdef OPENMC_MCPL -SourceSite mcpl_particle_to_site(const mcpl_particle* particle) +SourceSite mcpl_particle_to_site(const mcpl_particle_t* particle) { SourceSite site; @@ -35,40 +50,42 @@ SourceSite mcpl_particle_to_site(const mcpl_particle* particle) } // Copy position and direction - site.r.x = mcpl_particle->position[0]; - site.r.y = mcpl_particle->position[1]; - site.r.z = mcpl_particle->position[2]; - site.u.x = mcpl_particle->direction[0]; - site.u.y = mcpl_particle->direction[1]; - site.u.z = mcpl_particle->direction[2]; + site.r.x = particle->position[0]; + site.r.y = particle->position[1]; + site.r.z = particle->position[2]; + site.u.x = particle->direction[0]; + site.u.y = particle->direction[1]; + site.u.z = particle->direction[2]; // mcpl stores kinetic energy in MeV - site.E = mcpl_particle->ekin * 1e6; + site.E = particle->ekin * 1e6; // mcpl stores time in ms - site.time = mcpl_particle->time * 1e-3; - site.wgt = mcpl_particle->weight; + site.time = particle->time * 1e-3; + site.wgt = particle->weight; return site; } #endif +//============================================================================== + vector mcpl_source_sites(std::string path) { vector sites; #ifdef OPENMC_MCPL + // Open MCPL file and determine number of particles + auto mcpl_file = mcpl_open_file(path.c_str()); size_t n_sites = mcpl_hdr_nparticles(mcpl_file); for (int i = 0; i < n_sites; i++) { - SourceSite site; - // Extract particle from mcpl-file, checking if it is a neutron, photon, // electron, or positron. Otherwise skip. const mcpl_particle_t* particle; int pdg = 0; while (pdg != 2112 && pdg != 22 && pdg != 11 && pdg != -11) { particle = mcpl_read(mcpl_file); - pdg = mcpl_particle->pdgcode; + pdg = particle->pdgcode; } // Convert to source site and add to vector @@ -76,7 +93,7 @@ vector mcpl_source_sites(std::string path) } // Check that some sites were read - if (sites_.empty()) { + if (sites.empty()) { fatal_error("MCPL file contained no neutron, photon, electron, or positron " "source particles."); } @@ -90,4 +107,144 @@ vector mcpl_source_sites(std::string path) return sites; } +//============================================================================== + +#ifdef OPENMC_MCPL +void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) +{ + int64_t dims_size = settings::n_particles; + int64_t count_size = simulation::work_per_rank; + + // Set vectors for source bank and starting bank index of each process + vector* bank_index = &simulation::work_index; + vector* source_bank = &simulation::source_bank; + vector surf_source_index_vector; + vector surf_source_bank_vector; + + if (surf_source_bank) { + surf_source_index_vector = calculate_surf_source_size(); + dims_size = surf_source_index_vector[mpi::n_procs]; + count_size = simulation::surf_source_bank.size(); + + bank_index = &surf_source_index_vector; + + // Copy data in a SharedArray into a vector. + surf_source_bank_vector.resize(count_size); + surf_source_bank_vector.assign(simulation::surf_source_bank.data(), + simulation::surf_source_bank.data() + count_size); + source_bank = &surf_source_bank_vector; + } + + if (mpi::master) { + // Particles are writeen to disk from the master node only + + // Save source bank sites since the array is overwritten below +#ifdef OPENMC_MPI + vector temp_source {source_bank->begin(), source_bank->end()}; +#endif + + // loop over the other nodes and receive data - then write those. + for (int i = 0; i < mpi::n_procs; ++i) { + // number of particles for node node i + size_t count[] { + static_cast((*bank_index)[i + 1] - (*bank_index)[i])}; + +#ifdef OPENMC_MPI + if (i > 0) + MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + mpi::intracomm, MPI_STATUS_IGNORE); +#endif + // now write the source_bank data again. + for (vector::iterator _site = source_bank->begin(); + _site != source_bank->end(); _site++) { + // particle is now at the iterator + // write it to the mcpl-file + mcpl_particle_t p; + p.position[0] = _site->r.x; + p.position[1] = _site->r.y; + p.position[2] = _site->r.z; + + // mcpl requires that the direction vector is unit length + // which is also the case in openmc + p.direction[0] = _site->u.x; + p.direction[1] = _site->u.y; + p.direction[2] = _site->u.z; + + // mcpl stores kinetic energy in MeV + p.ekin = _site->E * 1e-6; + + p.time = _site->time * 1e3; + + p.weight = _site->wgt; + + switch (_site->particle) { + case ParticleType::neutron: + p.pdgcode = 2112; + break; + case ParticleType::photon: + p.pdgcode = 22; + break; + case ParticleType::electron: + p.pdgcode = 11; + break; + case ParticleType::positron: + p.pdgcode = -11; + break; + } + + mcpl_add_particle(file_id, &p); + } + } +#ifdef OPENMC_MPI + // Restore state of source bank + std::copy(temp_source.begin(), temp_source.end(), source_bank->begin()); +#endif + } else { +#ifdef OPENMC_MPI + MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + mpi::intracomm); +#endif + } +} +#endif + +//============================================================================== + +void write_mcpl_source_point(const char* filename, bool surf_source_bank) +{ + std::string filename_; + if (filename) { + filename_ = filename; + } else { + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + + filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, + simulation::current_batch, w); + } + +#ifdef OPENMC_MCPL + mcpl_outfile_t file_id; + + std::string line; + if (mpi::master) { + file_id = mcpl_create_outfile(filename_.c_str()); + if (VERSION_DEV) { + line = fmt::format("OpenMC {0}.{1}.{2}-development", VERSION_MAJOR, + VERSION_MINOR, VERSION_RELEASE); + } else { + line = fmt::format( + "OpenMC {0}.{1}.{2}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE); + } + mcpl_hdr_set_srcname(file_id, line.c_str()); + } + + write_mcpl_source_bank(file_id, surf_source_bank); + + if (mpi::master) { + mcpl_close_outfile(file_id); + } +#endif +} + } // namespace openmc diff --git a/src/settings.cpp b/src/settings.cpp index bc31e2c4d0..cb39d0c8b2 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -433,10 +433,8 @@ void read_settings_xml() if (check_for_node(node, "file")) { auto path = get_node_value(node, "file", false, true); if (ends_with(path, ".mcpl") || ends_with(path, ".mcpl.gz")) { -#ifdef OPENMC_MCPL auto sites = mcpl_source_sites(path); model::external_sources.push_back(make_unique(sites)); -#endif } else { model::external_sources.push_back(make_unique(path)); } @@ -659,6 +657,12 @@ void read_settings_xml() } if (check_for_node(node_sp, "mcpl")) { source_mcpl_write = get_node_value_bool(node_sp, "mcpl"); + + // Make sure MCPL support is enabled + if (source_mcpl_write && !MCPL_ENABLED) { + fatal_error( + "Your build of OpenMC does not support writing MCPL source files."); + } } if (check_for_node(node_sp, "overwrite_latest")) { source_latest = get_node_value_bool(node_sp, "overwrite_latest"); @@ -690,11 +694,15 @@ void read_settings_xml() max_surface_particles = std::stoll(get_node_value(node_ssw, "max_particles")); } -#ifdef OPENMC_MCPL if (check_for_node(node_ssw, "mcpl")) { - surf_mcpl_write = true; + surf_mcpl_write = get_node_value_bool(node_ssw, "mcpl"); + + // Make sure MCPL support is enabled + if (surf_mcpl_write && !MCPL_ENABLED) { + fatal_error("Your build of OpenMC does not support writing MCPL " + "surface source files."); + } } -#endif } // If source is not separate and is to be written out in the statepoint file, diff --git a/src/simulation.cpp b/src/simulation.cpp index 2c1d1c988b..497b26124b 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -8,6 +8,7 @@ #include "openmc/event.h" #include "openmc/geometry_aux.h" #include "openmc/material.h" +#include "openmc/mcpl_interface.h" #include "openmc/message_passing.h" #include "openmc/nuclide.h" #include "openmc/output.h" @@ -389,9 +390,7 @@ void finalize_batch() if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch -#ifdef OPENMC_MCPL if (!settings::source_mcpl_write) { -#endif if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_write && settings::source_separate) { write_source_point(nullptr); @@ -402,7 +401,6 @@ void finalize_batch() auto filename = settings::path_output + "source.h5"; write_source_point(filename.c_str()); } -#ifdef OPENMC_MCPL } else { if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_mcpl_write && settings::source_separate) { @@ -415,7 +413,6 @@ void finalize_batch() write_mcpl_source_point(filename.c_str()); } } -#endif } // Write out surface source if requested. @@ -424,13 +421,11 @@ void finalize_batch() auto filename = settings::path_output + "surface_source.h5"; write_source_point(filename.c_str(), true); } -#ifdef OPENMC_MCPL if (settings::surf_mcpl_write && simulation::current_batch == settings::n_batches) { auto filename = settings::path_output + "surface_source.mcpl"; write_mcpl_source_point(filename.c_str(), true); } -#endif } void initialize_generation() diff --git a/src/state_point.cpp b/src/state_point.cpp index 9dd0f13747..470dd77182 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -28,10 +28,6 @@ #include "openmc/timer.h" #include "openmc/vector.h" -#ifdef OPENMC_MCPL -#include -#endif - namespace openmc { extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) @@ -603,43 +599,6 @@ void write_source_point(const char* filename, bool surf_source_bank) file_close(file_id); } -#ifdef OPENMC_MCPL -void write_mcpl_source_point(const char* filename, bool surf_source_bank) -{ - std::string filename_; - if (filename) { - filename_ = filename; - } else { - // Determine width for zero padding - int w = std::to_string(settings::n_max_batches).size(); - - filename_ = fmt::format("{0}source.{1:0{2}}.mcpl", settings::path_output, - simulation::current_batch, w); - } - - mcpl_outfile_t file_id; - - std::string line; - if (mpi::master) { - file_id = mcpl_create_outfile(filename_.c_str()); - if (VERSION_DEV) { - line = fmt::format("OpenMC {0}.{1}.{2}-development", VERSION_MAJOR, - VERSION_MINOR, VERSION_RELEASE); - } else { - line = fmt::format( - "OpenMC {0}.{1}.{2}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE); - } - mcpl_hdr_set_srcname(file_id, line.c_str()); - } - - write_mcpl_source_bank(file_id, surf_source_bank); - - if (mpi::master) { - mcpl_close_outfile(file_id); - } -} -#endif - void write_source_bank(hid_t group_id, bool surf_source_bank) { hid_t banktype = h5banktype(); @@ -756,105 +715,6 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Tclose(banktype); } -#ifdef OPENMC_MCPL -void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) -{ - int64_t dims_size = settings::n_particles; - int64_t count_size = simulation::work_per_rank; - - // Set vectors for source bank and starting bank index of each process - vector* bank_index = &simulation::work_index; - vector* source_bank = &simulation::source_bank; - vector surf_source_index_vector; - vector surf_source_bank_vector; - - if (surf_source_bank) { - surf_source_index_vector = calculate_surf_source_size(); - dims_size = surf_source_index_vector[mpi::n_procs]; - count_size = simulation::surf_source_bank.size(); - - bank_index = &surf_source_index_vector; - - // Copy data in a SharedArray into a vector. - surf_source_bank_vector.resize(count_size); - surf_source_bank_vector.assign(simulation::surf_source_bank.data(), - simulation::surf_source_bank.data() + count_size); - source_bank = &surf_source_bank_vector; - } - - if (mpi::master) { - // Particles are writeen to disk from the master node only - - // Save source bank sites since the array is overwritten below -#ifdef OPENMC_MPI - vector temp_source {source_bank->begin(), source_bank->end()}; -#endif - - // loop over the other nodes and receive data - then write those. - for (int i = 0; i < mpi::n_procs; ++i) { - // number of particles for node node i - size_t count[] { - static_cast((*bank_index)[i + 1] - (*bank_index)[i])}; - -#ifdef OPENMC_MPI - if (i > 0) - MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, - mpi::intracomm, MPI_STATUS_IGNORE); -#endif - // now write the source_bank data again. - for (vector::iterator _site = source_bank->begin(); - _site != source_bank->end(); _site++) { - // particle is now at the iterator - // write it to the mcpl-file - mcpl_particle_t p; - p.position[0] = _site->r.x; - p.position[1] = _site->r.y; - p.position[2] = _site->r.z; - - // mcpl requires that the direction vector is unit length - // which is also the case in openmc - p.direction[0] = _site->u.x; - p.direction[1] = _site->u.y; - p.direction[2] = _site->u.z; - - // mcpl stores kinetic energy in MeV - p.ekin = _site->E * 1e-6; - - p.time = _site->time * 1e3; - - p.weight = _site->wgt; - - switch (_site->particle) { - case ParticleType::neutron: - p.pdgcode = 2112; - break; - case ParticleType::photon: - p.pdgcode = 22; - break; - case ParticleType::electron: - p.pdgcode = 11; - break; - case ParticleType::positron: - p.pdgcode = -11; - break; - } - - mcpl_add_particle(file_id, &p); - } - } -#ifdef OPENMC_MPI - // Restore state of source bank - std::copy(temp_source.begin(), temp_source.end(), source_bank->begin()); -#endif - } else { -#ifdef OPENMC_MPI - MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, - mpi::intracomm); -#endif - } -} -#endif - // Determine member names of a compound HDF5 datatype std::string dtype_member_names(hid_t dtype_id) { From 9ae44b3fd1e9c6e26c3f5089c9382a7fe97eb48e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 12:43:31 -0600 Subject: [PATCH 230/265] Rearragne MCPL source writing logic --- src/simulation.cpp | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 497b26124b..2f07f8124f 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -390,27 +390,23 @@ void finalize_batch() if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch - if (!settings::source_mcpl_write) { - if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_write && settings::source_separate) { + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_write && settings::source_separate) { + if (settings::source_mcpl_write) { + write_mcpl_source_point(nullptr); + } else { write_source_point(nullptr); } + } - // Write a continously-overwritten source point if requested. - if (settings::source_latest) { - auto filename = settings::path_output + "source.h5"; - write_source_point(filename.c_str()); - } - } else { - if (contains(settings::sourcepoint_batch, simulation::current_batch) && - settings::source_mcpl_write && settings::source_separate) { - write_mcpl_source_point(nullptr); - } - - // Write a continously-overwritten source point if requested. - if (settings::source_latest && settings::source_mcpl_write) { + // Write a continously-overwritten source point if requested. + if (settings::source_latest) { + if (settings::source_mcpl_write) { auto filename = settings::path_output + "source.mcpl"; write_mcpl_source_point(filename.c_str()); + } else { + auto filename = settings::path_output + "source.h5"; + write_source_point(filename.c_str()); } } } @@ -418,13 +414,13 @@ void finalize_batch() // Write out surface source if requested. if (settings::surf_source_write && simulation::current_batch == settings::n_batches) { - auto filename = settings::path_output + "surface_source.h5"; - write_source_point(filename.c_str(), true); - } - if (settings::surf_mcpl_write && - simulation::current_batch == settings::n_batches) { - auto filename = settings::path_output + "surface_source.mcpl"; - write_mcpl_source_point(filename.c_str(), true); + if (settings::surf_mcpl_write) { + auto filename = settings::path_output + "surface_source.mcpl"; + write_mcpl_source_point(filename.c_str(), true); + } else { + auto filename = settings::path_output + "surface_source.h5"; + write_source_point(filename.c_str(), true); + } } } From eeab41a19d8bbe1617f6abb63ff8bb2f89dd24f3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 14:03:26 -0600 Subject: [PATCH 231/265] Build against MCPL in CI by default --- tools/ci/gha-install.py | 12 ++++-------- tools/ci/gha-install.sh | 6 ++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index ea317b5f0a..83e5afac79 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -19,14 +19,14 @@ def which(program): return None -def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, mcpl=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') os.chdir('build') - # Build in debug mode by default - cmake_cmd = ['cmake', '-DCMAKE_BUILD_TYPE=Debug'] + # Build in debug mode by default with support for MCPL + cmake_cmd = ['cmake', '-DCMAKE_BUILD_TYPE=Debug', '-DOPENMC_USE_MCPL=on'] # Turn off OpenMP if specified if not omp: @@ -54,9 +54,6 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, mcpl= libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) - if mcpl: - cmake_cmd.append('-DOPENMC_USE_MCPL=ON') - # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') @@ -74,10 +71,9 @@ def main(): phdf5 = (os.environ.get('PHDF5') == 'y') dagmc = (os.environ.get('DAGMC') == 'y') libmesh = (os.environ.get('LIBMESH') == 'y') - mcpl = (os.environ.get('MCPL') == 'y') # Build and install - install(omp, mpi, phdf5, dagmc, libmesh, mcpl) + install(omp, mpi, phdf5, dagmc, libmesh) if __name__ == '__main__': main() diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index 68e825d486..75fa0ca703 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -27,10 +27,8 @@ if [[ $LIBMESH = 'y' ]]; then ./tools/ci/gha-install-libmesh.sh fi -# Install mcpl if needed -if [[ $MCPL = 'y' ]]; then - ./tools/ci/gha-install-mcpl.sh -fi +# Install MCPL +./tools/ci/gha-install-mcpl.sh # For MPI configurations, make sure mpi4py and h5py are built against the # correct version of MPI From 47848b79abe964be913095397d1d5aaf2780d28c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 14:34:37 -0600 Subject: [PATCH 232/265] Allow Settings.sourcepoint['mcpl'] to be set --- docs/source/io_formats/settings.rst | 14 +++++++++++--- openmc/settings.py | 12 ++++++++++-- tests/unit_tests/test_settings.py | 4 ++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 7174c5317a..01b1852ca2 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -732,6 +732,14 @@ attributes/sub-elements: *Default*: false + :mcpl: + If this element is set to "true", the source point file containing the + source bank will be written as an MCPL_ file name ``source.mcpl`` instead of + an HDF5 file. This option is only applicable if the ```` element + is set to true. + + *Default*: false + ------------------------------ ```` Element ------------------------------ @@ -769,13 +777,13 @@ certain surfaces and write out the source bank in a separate file called :mcpl: An optional boolean which indicates if the banked particles should be - written to a file in the MCPL-format (documented in mcpl_). instead of the - native HDF5-based format. If activated the output file name is changed to + written to a file in the MCPL_-format instead of the native HDF5-based + format. If activated the output file name is changed to ``surface_source.mcpl``. *Default*: false - .. _mcpl: https://mctools.github.io/mcpl/mcpl.pdf + .. _MCPL: https://mctools.github.io/mcpl/mcpl.pdf ------------------------------ ```` Element diff --git a/openmc/settings.py b/openmc/settings.py index fd622870c5..cc0ca29645 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -156,6 +156,7 @@ class Settings: :separate: bool indicating whether the source should be written as a separate file :write: bool indicating whether or not to write the source + :mcpl: bool indicating whether to write the source as an MCPL file statepoint : dict Options for writing state points. Acceptable keys are: @@ -620,6 +621,8 @@ class Settings: cv.check_type('sourcepoint write', value, bool) elif key == 'overwrite': cv.check_type('sourcepoint overwrite', value, bool) + elif key == 'mcpl': + cv.check_type('sourcepoint mcpl', value, bool) else: raise ValueError(f"Unknown key '{key}' encountered when " "setting sourcepoint options.") @@ -1019,6 +1022,11 @@ class Settings: subelement = ET.SubElement(element, "overwrite_latest") subelement.text = str(self._sourcepoint['overwrite']).lower() + if 'mcpl' in self._sourcepoint: + subelement = ET.SubElement(element, "mcpl") + subelement.text = str(self._sourcepoint['mcpl']).lower() + + def _create_surf_source_read_subelement(self, root): if self._surf_source_read: element = ET.SubElement(root, "surf_source_read") @@ -1319,10 +1327,10 @@ class Settings: def _sourcepoint_from_xml_element(self, root): elem = root.find('source_point') if elem is not None: - for key in ('separate', 'write', 'overwrite_latest', 'batches'): + for key in ('separate', 'write', 'overwrite_latest', 'batches', 'mcpl'): value = get_text(elem, key) if value is not None: - if key in ('separate', 'write'): + if key in ('separate', 'write', 'mcpl'): value = value in ('true', '1') elif key == 'overwrite_latest': value = value in ('true', '1') diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 7678711a43..f1ae581923 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -17,7 +17,7 @@ def test_export_to_xml(run_in_tmpdir): s.output = {'summary': True, 'tallies': False, 'path': 'here'} s.verbosity = 7 s.sourcepoint = {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True} + 'write': True, 'overwrite': True, 'mcpl': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.surf_source_read = {'path': 'surface_source_1.h5'} s.surf_source_write = {'surface_ids': [2], 'max_particles': 200} @@ -75,7 +75,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.output == {'summary': True, 'tallies': False, 'path': 'here'} assert s.verbosity == 7 assert s.sourcepoint == {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True} + 'write': True, 'overwrite': True, 'mcpl': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} assert s.surf_source_read == {'path': 'surface_source_1.h5'} assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} From 731eff8817520773670d8a56c3ee4fbf7962cca9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 14:43:58 -0600 Subject: [PATCH 233/265] Update source_mcpl_file test result --- tests/regression_tests/source_mcpl_file/results_true.dat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat index e8f4a20a08..5651efde70 100644 --- a/tests/regression_tests/source_mcpl_file/results_true.dat +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.004254E-01 8.027600E-04 +2.943088E-01 2.093028E-03 From ddbd61cc97ee2767d81f93b62542a3d367be1c85 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 14:49:47 -0600 Subject: [PATCH 234/265] Formatting fixes --- CMakeLists.txt | 1 + openmc/settings.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 466816b300..c6c2257830 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,6 +162,7 @@ endif() #=============================================================================== # MCPL #=============================================================================== + if (OPENMC_USE_MCPL) find_package(MCPL REQUIRED) message(STATUS "Found MCPL: ${MCPL_DIR} (found version \"${MCPL_VERSION}\")") diff --git a/openmc/settings.py b/openmc/settings.py index cc0ca29645..e445655ca2 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1026,7 +1026,6 @@ class Settings: subelement = ET.SubElement(element, "mcpl") subelement.text = str(self._sourcepoint['mcpl']).lower() - def _create_surf_source_read_subelement(self, root): if self._surf_source_read: element = ET.SubElement(root, "surf_source_read") From 25f2bd50f9005cf8fa05ea055ebce0384d488106 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 18:27:42 -0600 Subject: [PATCH 235/265] Several small fixes --- openmc/_xml.py | 2 +- openmc/model/model.py | 12 ++++-------- openmc/settings.py | 5 +++-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/openmc/_xml.py b/openmc/_xml.py index 2a33d4b8d7..6799a4e2d8 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -27,7 +27,7 @@ def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True element.tail = i for sub_element in element: # `trailing_indent` is intentionally not forwarded to the recursive - # call. Any child element of the topmost clement should add + # call. Any child element of the topmost element should add # indentation at the end to ensure its parent's indentation is # correct. clean_indentation(sub_element, level+1, spaces_per_level) diff --git a/openmc/model/model.py b/openmc/model/model.py index 7c6d9b828c..2f44890844 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -244,6 +244,8 @@ class Model: def from_model_xml(cls, path='model.xml'): """Create model from single XML file + .. vesionadded:: 0.13.3 + Parameters ---------- path : str or Pathlike @@ -259,13 +261,6 @@ class Model: model.materials = openmc.Materials.from_xml_element(root.find('materials')) model.geometry = openmc.Geometry.from_xml_element(root.find('geometry'), model.materials) - # gather meshes from other classes before reading the tally node - if model.settings.entropy_mesh is not None: - meshes[model.settings.entropy_mesh.id] = model.settings.entropy_mesh - - for ww in model.settings.weight_windows: - meshes[ww.mesh.id] = ww.mesh - if root.find('tallies'): model.tallies = openmc.Tallies.from_xml_element(root.find('tallies'), meshes) @@ -474,6 +469,8 @@ class Model: def export_to_model_xml(self, path='model.xml', remove_surfs=False): """Export model to a single XML file. + .. versionadded:: 0.13.3 + Parameters ---------- path : str or Pathlike @@ -483,7 +480,6 @@ class Model: Whether or not to remove redundant surfaces from the geometry when exporting. - .. versionadded:: 0.13.1 """ xml_path = Path(path) # if the provided path doesn't end with the XML extension, assume the diff --git a/openmc/settings.py b/openmc/settings.py index 65ca719572..4e6f144fee 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1100,7 +1100,8 @@ class Settings: path = f"./mesh[@id='{self.entropy_mesh.id}']" if root.find(path) is None: root.append(self.entropy_mesh.to_xml_element()) - if mesh_memo is not None: mesh_memo.add(self.entropy_mesh.id) + if mesh_memo is not None: + mesh_memo.add(self.entropy_mesh.id) def _create_trigger_subelement(self, root): if self._trigger_active is not None: @@ -1609,7 +1610,7 @@ class Settings: self._create_temperature_subelements(element) self._create_trace_subelement(element) self._create_track_subelement(element) - self._create_ufs_mesh_subelement(element) + self._create_ufs_mesh_subelement(element, mesh_memo) self._create_resonance_scattering_subelement(element) self._create_volume_calcs_subelement(element) self._create_create_fission_neutrons_subelement(element) From a0d8541a0164e19732ff3ce50b94ae9f83e0da13 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Dec 2022 22:27:03 -0600 Subject: [PATCH 236/265] Make sure OpenMCConfig.cmake.in includes MCPL --- cmake/OpenMCConfig.cmake.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index d0e2beb827..1e35083050 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -25,3 +25,7 @@ endif() if(@OPENMC_USE_MPI@) find_package(MPI REQUIRED) endif() + +if(@OPENMC_USE_MCPL@) + find_package(MCPL REQUIRED) +endif() From ecfa94e0ff0c50a83c8db61444e5cad888b16c7a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 8 Sep 2022 14:25:22 -0500 Subject: [PATCH 237/265] Rename gnd_name to gnds_name. Change GND to GNDS in comments --- docs/source/pythonapi/data.rst | 2 +- docs/source/usersguide/scripts.rst | 4 ++-- openmc/data/ace.py | 4 ++-- openmc/data/data.py | 19 +++++++++++-------- openmc/data/decay.py | 2 +- openmc/data/endf.py | 12 ++++++------ openmc/data/multipole.py | 4 ++-- openmc/data/neutron.py | 4 ++-- openmc/data/reaction.py | 2 +- openmc/data/thermal.py | 10 +++++----- openmc/deplete/chain.py | 16 ++++++++-------- openmc/deplete/nuclide.py | 2 +- openmc/nuclide.py | 2 +- scripts/openmc-update-inputs | 5 ++--- tests/unit_tests/test_data_misc.py | 12 ++++++------ 15 files changed, 51 insertions(+), 49 deletions(-) diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 960abec2fe..1eaf90c972 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -66,7 +66,7 @@ Core Functions decay_energy decay_photon_energy dose_coefficients - gnd_name + gnds_name half_life isotopes kalbach_slope diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index c433ffe2c3..78ee6f7758 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -157,9 +157,9 @@ geometry.xml added. Any 'surfaces' attributes/elements on a cell will be renamed 'region'. materials.xml - Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GND + Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GNDS names (e.g., Am242_m1). Thermal scattering table names will be changed from - ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). + ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). ---------------------- ``openmc-update-mgxs`` diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 06c581b426..91cbe41960 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -24,7 +24,7 @@ import numpy as np import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -from .data import ATOMIC_SYMBOL, gnd_name, EV_PER_MEV, K_BOLTZMANN +from .data import ATOMIC_SYMBOL, gnds_name, EV_PER_MEV, K_BOLTZMANN from .endf import ENDF_FLOAT_RE @@ -88,7 +88,7 @@ def get_metadata(zaid, metastable_scheme='nndc'): # Determine name element = ATOMIC_SYMBOL[Z] - name = gnd_name(Z, mass_number, metastable) + name = gnds_name(Z, mass_number, metastable) return (name, element, Z, mass_number, metastable) diff --git a/openmc/data/data.py b/openmc/data/data.py index 55bfb4f096..d5521d9980 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -197,8 +197,8 @@ NEUTRON_MASS = 1.00866491595 # Used in atomic_mass function as a cache _ATOMIC_MASS = {} -# Regex for GND nuclide names (used in zam function) -_GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') +# Regex for GNDS nuclide names (used in zam function) +_GNDS_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') # Used in half_life function as a cache _HALF_LIFE = {} @@ -436,8 +436,11 @@ def water_density(temperature, pressure=0.1013): return coeff / pi / gamma1_pi -def gnd_name(Z, A, m=0): - """Return nuclide name using GND convention +def gnds_name(Z, A, m=0): + """Return nuclide name using GNDS convention + + .. versionchanged:: 0.14.0 + Function name changed from ``gnd_name`` to ``gnds_name`` Parameters ---------- @@ -451,7 +454,7 @@ def gnd_name(Z, A, m=0): Returns ------- str - Nuclide name in GND convention, e.g., 'Am242_m1' + Nuclide name in GNDS convention, e.g., 'Am242_m1' """ if m > 0: @@ -502,7 +505,7 @@ def zam(name): Parameters ---------- name : str - Name of nuclide using GND convention, e.g., 'Am242_m1' + Name of nuclide using GNDS convention, e.g., 'Am242_m1' Returns ------- @@ -511,10 +514,10 @@ def zam(name): """ try: - symbol, A, state = _GND_NAME_RE.match(name).groups() + symbol, A, state = _GNDS_NAME_RE.match(name).groups() except AttributeError: raise ValueError(f"'{name}' does not appear to be a nuclide name in " - "GND format") + "GNDS format") if symbol not in ATOMIC_NUMBER: raise ValueError(f"'{symbol}' is not a recognized element symbol") diff --git a/openmc/data/decay.py b/openmc/data/decay.py index 0db8422010..d002424385 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -144,7 +144,7 @@ class FissionProductYields(EqualityMixin): # Assign basic nuclide properties self.nuclide = { - 'name': ev.gnd_name, + 'name': ev.gnds_name, 'atomic_number': ev.target['atomic_number'], 'mass_number': ev.target['mass_number'], 'isomeric_state': ev.target['isomeric_state'] diff --git a/openmc/data/endf.py b/openmc/data/endf.py index f9b9d06941..d526bc53f5 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -12,7 +12,7 @@ import re import numpy as np -from .data import gnd_name +from .data import gnds_name from .function import Tabulated1D try: from ._endf import float_endf @@ -520,10 +520,10 @@ class Evaluation: self.reaction_list.append((mf, mt, nc, mod)) @property - def gnd_name(self): - return gnd_name(self.target['atomic_number'], - self.target['mass_number'], - self.target['isomeric_state']) + def gnds_name(self): + return gnds_name(self.target['atomic_number'], + self.target['mass_number'], + self.target['isomeric_state']) class Tabulated2D: @@ -531,7 +531,7 @@ class Tabulated2D: This is a dummy class that is not really used other than to store the interpolation information for a two-dimensional function. Once we refactor - to adopt GND-like data containers, this will probably be removed or + to adopt GNDS-like data containers, this will probably be removed or extended. Parameters diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 508c76ac43..0be70cdba2 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -748,12 +748,12 @@ class WindowedMultipole(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention Attributes ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention spacing : float The width of each window in sqrt(E)-space. For example, the frst window will end at (sqrt(E_min) + spacing)**2 and the second window at diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index e0574d76d4..2d4c139635 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -44,7 +44,7 @@ class IncidentNeutron(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention atomic_number : int Number of protons in the target nucleus mass_number : int @@ -75,7 +75,7 @@ class IncidentNeutron(EqualityMixin): Metastable state of the target nucleus. A value of zero indicates ground state. name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention reactions : collections.OrderedDict Contains the cross sections, secondary angle and energy distributions, and other associated data for each reaction. The keys are the MT values diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index a2431cf1ce..ac9d7f14e9 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -555,7 +555,7 @@ def _get_activation_products(ev, rx): Z, A = divmod(items[2], 1000) excited_state = items[3] - # Get GND name for product + # Get GNDS name for product symbol = ATOMIC_SYMBOL[Z] if excited_state > 0: name = '{}{}_e{}'.format(symbol, A, excited_state) diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index e6213e5f0d..48f6bfc9b3 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -104,7 +104,7 @@ def get_thermal_name(name): Returns ------- str - GND-format thermal scattering name + GNDS-format thermal scattering name """ if name in _THERMAL_NAMES: @@ -396,7 +396,7 @@ class ThermalScattering(EqualityMixin): Parameters ---------- name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O atomic_weight_ratio : float Atomic mass ratio of the target nuclide. kTs : Iterable of float @@ -415,7 +415,7 @@ class ThermalScattering(EqualityMixin): Inelastic scattering cross section derived in the incoherent approximation name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O temperatures : Iterable of str List of string representations the temperatures of the target nuclide in the data set. The temperatures are strings of the temperature, @@ -491,7 +491,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. @@ -596,7 +596,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index f439cedc34..372ed35108 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -15,7 +15,7 @@ from numbers import Real, Integral from warnings import warn from openmc.checkvalue import check_type, check_greater_than -from openmc.data import gnd_name, zam, DataLibrary +from openmc.data import gnds_name, zam, DataLibrary from openmc.exceptions import DataError from .nuclide import FissionYieldDistribution @@ -135,14 +135,14 @@ def replace_missing(product, decay_data): Parameters ---------- product : str - Name of product in GND format, e.g. 'Y86_m1'. + Name of product in GNDS format, e.g. 'Y86_m1'. decay_data : dict Dictionary of decay data Returns ------- product : str - Replacement for missing product in GND format. + Replacement for missing product in GNDS format. """ # Determine atomic number, mass number, and metastable state @@ -213,7 +213,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): # Check if metastable state has data (e.g., Am242m) Z, A, m = zam(actinide) if m == 0: - metastable = gnd_name(Z, A, 1) + metastable = gnds_name(Z, A, 1) if metastable in fpy_data: return metastable @@ -222,7 +222,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z += 1 A += 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -231,7 +231,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z -= 1 A -= 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -357,7 +357,7 @@ class Chain: reactions = {} for f in neutron_files: evaluation = openmc.data.endf.Evaluation(f) - name = evaluation.gnd_name + name = evaluation.gnds_name reactions[name] = {} for mf, mt, nc, mod in evaluation.reaction_list: if mf == 3: @@ -904,7 +904,7 @@ class Chain: ground_target = grounds.get(parent_name) if ground_target is None: pz, pa, pm = zam(parent_name) - ground_target = gnd_name(pz, pa + 1, 0) + ground_target = gnds_name(pz, pa + 1, 0) new_ratios[ground_target] = ground_br parent.add_reaction(reaction, ground_target, rxn_Q, ground_br) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index b84b91a7ff..2e9673d5f7 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -82,7 +82,7 @@ class Nuclide: Parameters ---------- name : str, optional - GND name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` + GNDS name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` Attributes ---------- diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 196c8d7701..d5ae4bddbb 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -26,7 +26,7 @@ class Nuclide(str): if name.endswith('m'): name = name[:-1] + '_m1' - msg = ('OpenMC nuclides follow the GND naming convention. ' + msg = ('OpenMC nuclides follow the GNDS naming convention. ' f'Nuclide "{orig_name}" is being renamed as "{name}".') warnings.warn(msg) diff --git a/scripts/openmc-update-inputs b/scripts/openmc-update-inputs index c47f888c83..2d49c0626e 100755 --- a/scripts/openmc-update-inputs +++ b/scripts/openmc-update-inputs @@ -4,7 +4,6 @@ """ import argparse -from difflib import get_close_matches from itertools import chain from random import randint from shutil import move @@ -27,8 +26,8 @@ geometry.xml: Lattices containing 'outside' attributes/tags will be replaced will be renamed 'region'. materials.xml: Nuclide names will be changed from ACE aliases (e.g., Am-242m) to - HDF5/GND names (e.g., Am242_m1). Thermal scattering table names will be - changed from ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). + HDF5/GNDS names (e.g., Am242_m1). Thermal scattering table names will be + changed from ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). """ diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 37e024fe23..f88be2602d 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -101,12 +101,12 @@ def test_water_density(): assert dens(500.0, 3.0) == pytest.approx(1e-3/0.120241800e-2, 1e-6) -def test_gnd_name(): - assert openmc.data.gnd_name(1, 1) == 'H1' - assert openmc.data.gnd_name(40, 90) == ('Zr90') - assert openmc.data.gnd_name(95, 242, 0) == ('Am242') - assert openmc.data.gnd_name(95, 242, 1) == ('Am242_m1') - assert openmc.data.gnd_name(95, 242, 10) == ('Am242_m10') +def test_gnds_name(): + assert openmc.data.gnds_name(1, 1) == 'H1' + assert openmc.data.gnds_name(40, 90) == ('Zr90') + assert openmc.data.gnds_name(95, 242, 0) == ('Am242') + assert openmc.data.gnds_name(95, 242, 1) == ('Am242_m1') + assert openmc.data.gnds_name(95, 242, 10) == ('Am242_m10') def test_isotopes(): From a5bdbd3adbc18ec429647c4b0ae9cee7098d7025 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 23 Dec 2022 00:04:45 -0600 Subject: [PATCH 238/265] Address a few warnings from tests --- openmc/mgxs/mgxs.py | 4 ++-- tests/regression_tests/diff_tally/test.py | 5 ++--- tests/regression_tests/surface_tally/test.py | 5 ++--- tests/unit_tests/test_mesh_to_vtk.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 15a1128f89..c93b42b88b 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -925,7 +925,7 @@ class MGXS: # Tabulate the atomic number densities for all nuclides elif nuclides == 'all': nuclides = self.get_nuclides() - densities = np.zeros(self.num_nuclides, dtype=np.float) + densities = np.zeros(self.num_nuclides, dtype=float) for i, nuclide in enumerate(nuclides): densities[i] += self.get_nuclide_density(nuclide) @@ -3019,7 +3019,7 @@ class DiffusionCoefficient(TransportXS): new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt - p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], + p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], filter_bins=[('P1',)],squeeze=True) p1_tally._scores = ['scatter-1'] total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] diff --git a/tests/regression_tests/diff_tally/test.py b/tests/regression_tests/diff_tally/test.py index b688e6d233..18c5013722 100644 --- a/tests/regression_tests/diff_tally/test.py +++ b/tests/regression_tests/diff_tally/test.py @@ -103,9 +103,8 @@ class DiffTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(statepoint) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('d_material', 'd_nuclide', 'd_variable', 'score', 'mean', diff --git a/tests/regression_tests/surface_tally/test.py b/tests/regression_tests/surface_tally/test.py index 96199ec2a4..d21f361e64 100644 --- a/tests/regression_tests/surface_tally/test.py +++ b/tests/regression_tests/surface_tally/test.py @@ -164,9 +164,8 @@ class SurfaceTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(self._sp_name) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('mean', 'std. dev.') diff --git a/tests/unit_tests/test_mesh_to_vtk.py b/tests/unit_tests/test_mesh_to_vtk.py index d7d5907a22..f90eab134b 100644 --- a/tests/unit_tests/test_mesh_to_vtk.py +++ b/tests/unit_tests/test_mesh_to_vtk.py @@ -77,8 +77,8 @@ def test_write_data_to_vtk_size_mismatch(mesh): # by regex. These are needed to make the test string match the error message # string when using the match argument as that uses regular expression expected_error_msg = ( - f"The size of the dataset 'label' \({len(data)}\) should be equal to " - f"the number of mesh cells \({mesh.num_mesh_cells}\)" + f"The size of the dataset 'label' ({len(data)}) should be equal to " + f"the number of mesh cells ({mesh.num_mesh_cells})" ) with pytest.raises(ValueError, match=expected_error_msg): mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) From f9c6765eca0026291bf760cb8f33866fe367034b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 12 Dec 2022 21:39:39 -0600 Subject: [PATCH 239/265] Update intersphinx URLs --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f083332799..ae329dd7ed 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -247,7 +247,7 @@ napoleon_use_ivar = True intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'numpy': ('https://numpy.org/doc/stable/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), - 'matplotlib': ('https://matplotlib.org/', None) + 'matplotlib': ('https://matplotlib.org/stable/', None) } From a178f5f85a95d3eb519da91b9e462ae5d906be0c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 23 Dec 2022 10:55:58 -0600 Subject: [PATCH 240/265] Use raw f-string in test_mesh_to_vtk.py --- tests/unit_tests/test_mesh_to_vtk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_mesh_to_vtk.py b/tests/unit_tests/test_mesh_to_vtk.py index f90eab134b..bc3633c8c0 100644 --- a/tests/unit_tests/test_mesh_to_vtk.py +++ b/tests/unit_tests/test_mesh_to_vtk.py @@ -77,8 +77,8 @@ def test_write_data_to_vtk_size_mismatch(mesh): # by regex. These are needed to make the test string match the error message # string when using the match argument as that uses regular expression expected_error_msg = ( - f"The size of the dataset 'label' ({len(data)}) should be equal to " - f"the number of mesh cells ({mesh.num_mesh_cells})" + fr"The size of the dataset 'label' \({len(data)}\) should be equal to " + fr"the number of mesh cells \({mesh.num_mesh_cells}\)" ) with pytest.raises(ValueError, match=expected_error_msg): mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) From 954a2bdcdaf47cfbf778f0e07e0c4ab3fba17175 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 23 Dec 2022 11:12:22 -0600 Subject: [PATCH 241/265] Fix source definition in source_mcpl_file test --- include/openmc/mcpl_interface.h | 2 +- src/mcpl_interface.cpp | 31 +++++++++---------- .../source_mcpl_file/results_true.dat | 2 +- .../source_mcpl_file/settings.xml | 1 - .../regression_tests/source_mcpl_file/test.py | 3 +- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/include/openmc/mcpl_interface.h b/include/openmc/mcpl_interface.h index c931f17743..64f15c13ab 100644 --- a/include/openmc/mcpl_interface.h +++ b/include/openmc/mcpl_interface.h @@ -2,9 +2,9 @@ #define OPENMC_MCPL_INTERFACE_H #include "openmc/particle_data.h" +#include "openmc/vector.h" #include -#include namespace openmc { diff --git a/src/mcpl_interface.cpp b/src/mcpl_interface.cpp index e9bc648303..a388914782 100644 --- a/src/mcpl_interface.cpp +++ b/src/mcpl_interface.cpp @@ -6,6 +6,7 @@ #include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/state_point.h" +#include "openmc/vector.h" #include @@ -57,9 +58,8 @@ SourceSite mcpl_particle_to_site(const mcpl_particle_t* particle) site.u.y = particle->direction[1]; site.u.z = particle->direction[2]; - // mcpl stores kinetic energy in MeV + // MCPL stores kinetic energy in [MeV], time in [ms] site.E = particle->ekin * 1e6; - // mcpl stores time in ms site.time = particle->time * 1e-3; site.wgt = particle->weight; @@ -155,29 +155,26 @@ void write_mcpl_source_bank(mcpl_outfile_t file_id, bool surf_source_bank) mpi::intracomm, MPI_STATUS_IGNORE); #endif // now write the source_bank data again. - for (vector::iterator _site = source_bank->begin(); - _site != source_bank->end(); _site++) { + for (const auto& site : *source_bank) { // particle is now at the iterator // write it to the mcpl-file mcpl_particle_t p; - p.position[0] = _site->r.x; - p.position[1] = _site->r.y; - p.position[2] = _site->r.z; + p.position[0] = site.r.x; + p.position[1] = site.r.y; + p.position[2] = site.r.z; // mcpl requires that the direction vector is unit length // which is also the case in openmc - p.direction[0] = _site->u.x; - p.direction[1] = _site->u.y; - p.direction[2] = _site->u.z; + p.direction[0] = site.u.x; + p.direction[1] = site.u.y; + p.direction[2] = site.u.z; - // mcpl stores kinetic energy in MeV - p.ekin = _site->E * 1e-6; + // MCPL stores kinetic energy in [MeV], time in [ms] + p.ekin = site.E * 1e-6; + p.time = site.time * 1e3; + p.weight = site.wgt; - p.time = _site->time * 1e3; - - p.weight = _site->wgt; - - switch (_site->particle) { + switch (site.particle) { case ParticleType::neutron: p.pdgcode = 2112; break; diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat index 5651efde70..0a98e9d8cc 100644 --- a/tests/regression_tests/source_mcpl_file/results_true.dat +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.943088E-01 2.093028E-03 +3.009416E-01 3.229999E-03 diff --git a/tests/regression_tests/source_mcpl_file/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml index 2e511461f3..47b010bffe 100644 --- a/tests/regression_tests/source_mcpl_file/settings.xml +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -1,6 +1,5 @@ - 1234 diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 60e0ad50f8..84ec005ae1 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -11,7 +11,6 @@ pytestmark = pytest.mark.skipif( settings1=""" - 1234 @@ -35,7 +34,7 @@ settings2 = """ 1000 - source.10.{0} + source.10.{} """ From 577157b846ac6885251ade59456967e4102d6b24 Mon Sep 17 00:00:00 2001 From: shimwell Date: Sat, 24 Dec 2022 22:40:34 +0000 Subject: [PATCH 242/265] allowing xml file for materials --- openmc/geometry.py | 29 +++++++++++++++++++---------- tests/unit_tests/test_geometry.py | 19 ++++++++++++++++++- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index a43899daaf..eb7fa8200a 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,3 +1,5 @@ +import os +import typing from collections import OrderedDict, defaultdict from collections.abc import Iterable from copy import deepcopy @@ -7,7 +9,7 @@ import warnings import openmc import openmc._xml as xml -from .checkvalue import check_type, check_less_than, check_greater_than +from .checkvalue import check_type, check_less_than, check_greater_than, PathLike class Geometry: @@ -254,16 +256,20 @@ class Geometry: raise ValueError('Error determining root universe.') @classmethod - def from_xml(cls, path='geometry.xml', materials=None): + def from_xml( + cls, + path: PathLike = 'geometry.xml', + materials: typing.Optional[typing.Union[PathLike, 'openmc.Materials']] = 'materials.xml' + ): """Generate geometry from XML file Parameters ---------- - path : str, optional + path : PathLike, optional Path to geometry XML file - materials : openmc.Materials or None - Materials used to assign to cells. If None, an attempt is made to - generate it from the materials.xml file. + materials : openmc.Materials or or PathLike + Materials used to assign to cells. If PathLike, an attempt is made + to generate materials from the provided xml file. Returns ------- @@ -271,10 +277,13 @@ class Geometry: Geometry object """ - # Create dictionary to easily look up materials - if materials is None: - filename = Path(path).parent / 'materials.xml' - materials = openmc.Materials.from_xml(str(filename)) + + # Using str and os.Pathlike here to avoid error when using just the imported PathLike + # TypeError: Subscripted generics cannot be used with class and instance checks + check_type('materials', materials, (str, os.PathLike, openmc.Materials)) + + if isinstance(materials, (str, os.PathLike)): + materials = openmc.Materials.from_xml(materials) tree = ET.parse(path) root = tree.getroot() diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 9db112fd29..843c01defd 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -1,4 +1,5 @@ import xml.etree.ElementTree as ET +from pathlib import Path import numpy as np import openmc @@ -274,7 +275,23 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): # Export model mixed_lattice_model.export_to_xml() - # Import geometry + mats_from_xml = openmc.Materials.from_xml('materials.xml') + # checking string a Path are both acceptable + for path in ['geometry.xml', Path('geometry.xml')]: + for materials in [mats_from_xml, 'materials.xml']: + # Import geometry from file + geom = openmc.Geometry.from_xml(path=path, materials=materials) + assert isinstance(geom, openmc.Geometry) + ll, ur = geom.bounding_box + assert ll == pytest.approx((-6.0, -6.0, -np.inf)) + assert ur == pytest.approx((6.0, 6.0, np.inf)) + + with pytest.raises(TypeError) as excinfo: + geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) + assert 'Unable to set "materials" to "None"' in str(excinfo.value) + + + # checking that the default args also work geom = openmc.Geometry.from_xml() assert isinstance(geom, openmc.Geometry) ll, ur = geom.bounding_box From 80dad0f2403176c8fe5a501ec7a29ef0963ddf03 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 31 Dec 2022 11:32:39 +0700 Subject: [PATCH 243/265] Small fix in plot_xs when S(a,b) tables are present --- openmc/plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/plotter.py b/openmc/plotter.py index 16760868e3..e2d18d0857 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -563,7 +563,7 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., for nuclide in nuclides.items(): sabs[nuclide[0]] = None if isinstance(this, openmc.Material): - for sab_name in this._sab: + for sab_name, _ in this._sab: sab = openmc.data.ThermalScattering.from_hdf5( library.get_by_material(sab_name, data_type='thermal')['path']) for nuc in sab.nuclides: From ac2ba93b3a573353a6e2fd051a1a35581f32c257 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 03:31:07 +0000 Subject: [PATCH 244/265] remove cell deepcopying for creating a fresh instance --- openmc/cell.py | 11 ++++++++--- tests/unit_tests/test_cell.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 2aa538012f..8169ca4d24 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -1,6 +1,5 @@ from collections import OrderedDict from collections.abc import Iterable -from copy import deepcopy from math import cos, sin, pi from numbers import Real from xml.etree import ElementTree as ET @@ -519,8 +518,14 @@ class Cell(IDManagerMixin): paths = self._paths self._paths = None - clone = deepcopy(self) - clone.id = None + clone = openmc.Cell() + clone.name = self.name + clone.temperature = self.temperature + clone.volume = self.volume + if self.translation is not None: + clone.translation = self.translation + if self.rotation is not None: + clone.rotation = self.rotation clone._num_instances = None # Restore paths on original instance diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 1c2e1b70e4..eebe0f895c 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -58,24 +58,54 @@ def test_clone(): cyl = openmc.ZCylinder() c = openmc.Cell(fill=m, region=-cyl) c.temperature = 650. + c.translation = (1,2,3) + c.rotation = (4,5,6) + c.volume = 100 c2 = c.clone() assert c2.id != c.id assert c2.fill != c.fill assert c2.region != c.region assert c2.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + assert c2.volume == c.volume c3 = c.clone(clone_materials=False) assert c3.id != c.id assert c3.fill == c.fill assert c3.region != c.region assert c3.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) c4 = c.clone(clone_regions=False) assert c4.id != c.id assert c4.fill != c.fill assert c4.region == c.region assert c4.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + + c5 = c.clone(clone_materials=False, clone_regions=False) + assert c5.id != c.id + assert c5.fill == c.fill + assert c5.region == c.region + assert c5.temperature == c.temperature + assert all(c2.translation == c.translation) + assert all(c2.rotation == c.rotation) + + # Mutate the original to ensure the changes are not seen in the clones + c.fill = openmc.Material() + c.region = +openmc.ZCylinder() + c.translation = [-1,-2,-3] + c.rotation = [-4,-5,-6] + c.temperature = 1 + assert c5.fill != c.fill + assert c5.region != c.region + assert c5.temperature != c.temperature + assert all(c2.translation != c.translation) + assert all(c2.rotation != c.rotation) def test_temperature(cell_with_lattice): From affc62f09cdb66bd2fea9ffdf6b44099b1e0afde Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 22:53:55 +0000 Subject: [PATCH 245/265] only reassign temps when not None --- openmc/cell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/cell.py b/openmc/cell.py index 8169ca4d24..cea362ed8f 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -520,8 +520,9 @@ class Cell(IDManagerMixin): clone = openmc.Cell() clone.name = self.name - clone.temperature = self.temperature clone.volume = self.volume + if self.temperature is not None: + clone.temperature = self.temperature if self.translation is not None: clone.translation = self.translation if self.rotation is not None: From 60f9a4271172d61c2c36dd57ebbe8bc4a02fe587 Mon Sep 17 00:00:00 2001 From: josh Date: Wed, 4 Jan 2023 22:54:26 +0000 Subject: [PATCH 246/265] simplify cell cloning test changes --- tests/unit_tests/test_cell.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index eebe0f895c..4185e9a44c 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -57,43 +57,37 @@ def test_clone(): m = openmc.Material() cyl = openmc.ZCylinder() c = openmc.Cell(fill=m, region=-cyl) - c.temperature = 650. - c.translation = (1,2,3) - c.rotation = (4,5,6) - c.volume = 100 + # Check cloning with all optional params as the defaults c2 = c.clone() assert c2.id != c.id assert c2.fill != c.fill assert c2.region != c.region - assert c2.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) - assert c2.volume == c.volume c3 = c.clone(clone_materials=False) assert c3.id != c.id assert c3.fill == c.fill assert c3.region != c.region - assert c3.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) c4 = c.clone(clone_regions=False) assert c4.id != c.id assert c4.fill != c.fill assert c4.region == c.region - assert c4.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) + + # Add optional properties to the original cell to ensure they're cloned successfully + c.temperature = 650. + c.translation = [1,2,3] + c.rotation = [4,5,6] + c.volume = 100 c5 = c.clone(clone_materials=False, clone_regions=False) assert c5.id != c.id assert c5.fill == c.fill assert c5.region == c.region assert c5.temperature == c.temperature - assert all(c2.translation == c.translation) - assert all(c2.rotation == c.rotation) + assert c5.volume == c.volume + assert all(c5.translation == c.translation) + assert all(c5.rotation == c.rotation) # Mutate the original to ensure the changes are not seen in the clones c.fill = openmc.Material() @@ -101,11 +95,13 @@ def test_clone(): c.translation = [-1,-2,-3] c.rotation = [-4,-5,-6] c.temperature = 1 + c.volume = 1 assert c5.fill != c.fill assert c5.region != c.region assert c5.temperature != c.temperature - assert all(c2.translation != c.translation) - assert all(c2.rotation != c.rotation) + assert c5.volume != c.volume + assert all(c5.translation != c.translation) + assert all(c5.rotation != c.rotation) def test_temperature(cell_with_lattice): From 82429cbb19d4ba10aa771a5493a092cd35711b4f Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 5 Jan 2023 11:16:16 +0000 Subject: [PATCH 247/265] added test for sab xs appears in xs calc --- tests/unit_tests/test_plotter.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/unit_tests/test_plotter.py diff --git a/tests/unit_tests/test_plotter.py b/tests/unit_tests/test_plotter.py new file mode 100644 index 0000000000..a3f1ff6662 --- /dev/null +++ b/tests/unit_tests/test_plotter.py @@ -0,0 +1,27 @@ +import openmc +import numpy as np + + +def test_calculate_cexs_elem_mat_sab(): + """Checks that sab cross sections are included in the + _calculate_cexs_elem_mat method and have the correct shape""" + + mat_1 = openmc.Material() + mat_1.add_element("H", 4.0, "ao") + mat_1.add_element("O", 4.0, "ao") + mat_1.add_element("C", 4.0, "ao") + + mat_1.add_s_alpha_beta("c_C6H6") + mat_1.set_density("g/cm3", 0.865) + + energy_grid, data = openmc.plotter._calculate_cexs_elem_mat( + mat_1, + ["inelastic"], + sab_name="c_C6H6", + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 1 + assert len(data[0]) == len(energy_grid) From 9fac1bb4d25d7399d7c942e8605c7dc01cd4824a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 12:13:55 -0600 Subject: [PATCH 248/265] Updating note on photon transport limitations --- docs/source/usersguide/settings.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index 336982ac6e..760a08e90c 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -474,7 +474,6 @@ selected:: Some features related to photon transport are not currently implemented, including: - * Tallying photon energy deposition. * Generating a photon source from a neutron calculation that can be used for a later fixed source photon calculation. * Photoneutron reactions. From 9615b9f5da9a1bccfd0a693f74c9c0437ec1329a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 16:04:33 -0600 Subject: [PATCH 249/265] Adding tests for tally triggers. --- tests/unit_tests/test_triggers.py | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/unit_tests/test_triggers.py diff --git a/tests/unit_tests/test_triggers.py b/tests/unit_tests/test_triggers.py new file mode 100644 index 0000000000..4fe6e044ab --- /dev/null +++ b/tests/unit_tests/test_triggers.py @@ -0,0 +1,75 @@ + +import openmc + +def test_tally_trigger(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # create a tally with triggers applied + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['scatter'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['scatter'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 100 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + with openmc.StatePoint(sp_file) as sp: + expected_realizations = sp.n_realizations + + # adding other scores to the tally should not change the + # number of batches required to satisfy the trigger + tally.scores = ['total', 'absorption', 'scatter'] + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + realizations = sp.n_realizations + + assert realizations == expected_realizations + + +def test_tally_trigger_null_score(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # apply a tally with a score that be tallied in this model + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['pair-production'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['pair-production'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 50 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + # verify that the tally mean is zero + tally_out = sp.get_tally(id=tally.id) + assert all(tally_out.mean == 0.0) + + # we expect that this simulation will run + # up to the max allowed batches + total_batches = sp.n_realizations + sp.n_inactive + assert total_batches == pincell.settings.trigger_max_batches + From bfcbecdbaa002954c5a25edae670d108ea95369a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Thu, 5 Jan 2023 16:25:15 -0600 Subject: [PATCH 250/265] Fixing tally triggers. Refs #2342 and #2343 --- src/tallies/trigger.cpp | 87 ++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 2c155980e7..79af658793 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -37,6 +37,10 @@ std::pair get_tally_uncertainty( int n = tally->n_realizations_; auto mean = sum / n; + + // if the result has no contributions, return an invalid pair + if (mean == 0) return {-1 , -1}; + double std_dev = std::sqrt((sum_sq / n - mean * mean) / (n - 1)); double rel_err = (mean != 0.) ? std_dev / std::abs(mean) : 0.; @@ -68,42 +72,49 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) const auto& results = t.results_; for (auto filter_index = 0; filter_index < results.shape()[0]; ++filter_index) { - for (auto score_index = 0; score_index < results.shape()[1]; - ++score_index) { - // Compute the tally uncertainty metrics. - auto uncert_pair = - get_tally_uncertainty(i_tally, score_index, filter_index); - double std_dev = uncert_pair.first; - double rel_err = uncert_pair.second; + // Compute the tally uncertainty metrics. + auto uncert_pair = + get_tally_uncertainty(i_tally, trigger.score_index, filter_index); - // Pick out the relevant uncertainty metric for this trigger. - double uncertainty; - switch (trigger.metric) { - case TriggerMetric::variance: - uncertainty = std_dev * std_dev; - break; - case TriggerMetric::standard_deviation: - uncertainty = std_dev; - break; - case TriggerMetric::relative_error: - uncertainty = rel_err; - break; - case TriggerMetric::not_active: - UNREACHABLE(); - } + // if there is a score without contributions, set ratio to inf and + // exit early + if (uncert_pair.first == -1) { + ratio = INFINITY; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; + return; + } - // Compute the uncertainty / threshold ratio. - double this_ratio = uncertainty / trigger.threshold; - if (trigger.metric == TriggerMetric::variance) { - this_ratio = std::sqrt(ratio); - } + double std_dev = uncert_pair.first; + double rel_err = uncert_pair.second; - // If this is the most uncertain value, set the output variables. - if (this_ratio > ratio) { - ratio = this_ratio; - score = t.scores_[trigger.score_index]; - tally_id = t.id_; - } + // Pick out the relevant uncertainty metric for this trigger. + double uncertainty; + switch (trigger.metric) { + case TriggerMetric::variance: + uncertainty = std_dev * std_dev; + break; + case TriggerMetric::standard_deviation: + uncertainty = std_dev; + break; + case TriggerMetric::relative_error: + uncertainty = rel_err; + break; + case TriggerMetric::not_active: + UNREACHABLE(); + } + + // Compute the uncertainty / threshold ratio. + double this_ratio = uncertainty / trigger.threshold; + if (trigger.metric == TriggerMetric::variance) { + this_ratio = std::sqrt(ratio); + } + + // If this is the most uncertain value, set the output variables. + if (this_ratio > ratio) { + ratio = this_ratio; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; } } } @@ -181,9 +192,13 @@ void check_triggers() "eigenvalue", keff_ratio); } else { - msg = fmt::format( - "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", - tally_ratio, reaction_name(score), tally_id); + if (tally_ratio == INFINITY) { + msg = fmt::format("Triggers unsatisfied, no result tallied for score {} in tally {}", reaction_name(score), tally_id); + } else{ + msg = fmt::format( + "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", + tally_ratio, reaction_name(score), tally_id); + } } write_message(msg, 7); From 62af77310897b6de1ef43906a781da0c9fcfe15f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 9 Jan 2023 18:16:45 +0700 Subject: [PATCH 251/265] Refactor NCrystal interface code to ncrystal_interface.h/cpp --- CMakeLists.txt | 1 + include/openmc/material.h | 25 ++----- include/openmc/ncrystal_interface.h | 91 +++++++++++++++++++++++ include/openmc/physics.h | 28 +------- include/openmc/settings.h | 8 +-- src/material.cpp | 39 +++------- src/ncrystal_interface.cpp | 108 ++++++++++++++++++++++++++++ src/physics.cpp | 30 ++------ src/settings.cpp | 12 +--- 9 files changed, 229 insertions(+), 113 deletions(-) create mode 100644 include/openmc/ncrystal_interface.h create mode 100644 src/ncrystal_interface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dee3f90e7..4737ff47b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,6 +348,7 @@ list(APPEND libopenmc_SOURCES src/message_passing.cpp src/mgxs.cpp src/mgxs_interface.cpp + src/ncrystal_interface.cpp src/nuclide.cpp src/output.cpp src/particle.cpp diff --git a/include/openmc/material.h b/include/openmc/material.h index 5326ad6ded..8db9bb6283 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -12,13 +12,10 @@ #include "openmc/bremsstrahlung.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr +#include "openmc/ncrystal_interface.h" #include "openmc/particle.h" #include "openmc/vector.h" -#ifdef NCRYSTAL -#include "NCrystal/NCrystal.hh" -#endif - namespace openmc { //============================================================================== @@ -155,25 +152,17 @@ public: //! \return Temperature in [K] double temperature() const; -#ifdef NCRYSTAL //! Get pointer to NCrystal material object //! \return Pointer to NCrystal material object - std::shared_ptr ncrystal_mat() const - { - return ncrystal_mat_; - }; -#endif + const NCrystalMat& ncrystal_mat() const { return ncrystal_mat_; }; //---------------------------------------------------------------------------- // Data - int32_t id_ {C_NONE}; //!< Unique ID - std::string name_; //!< Name of material - vector nuclide_; //!< Indices in nuclides vector - vector element_; //!< Indices in elements vector -#ifdef NCRYSTAL - std::string ncrystal_cfg_; //!< NCrystal configuration string - std::shared_ptr ncrystal_mat_; -#endif + int32_t id_ {C_NONE}; //!< Unique ID + std::string name_; //!< Name of material + vector nuclide_; //!< Indices in nuclides vector + vector element_; //!< Indices in elements vector + NCrystalMat ncrystal_mat_; //!< NCrystal material object xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h new file mode 100644 index 0000000000..36038d50c3 --- /dev/null +++ b/include/openmc/ncrystal_interface.h @@ -0,0 +1,91 @@ +#ifndef OPENMC_NCRYSTAL_INTERFACE_H +#define OPENMC_NCRYSTAL_INTERFACE_H + +#ifdef NCRYSTAL +#include "NCrystal/NCRNG.hh" +#include "NCrystal/NCrystal.hh" +#endif + +#include "openmc/particle.h" + +#include // for uint64_t +#include // for numeric_limits +#include + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +extern "C" const bool NCRYSTAL_ENABLED; + +//============================================================================== +// Wrapper class an NCrystal material +//============================================================================== + +class NCrystalMat { +public: + //---------------------------------------------------------------------------- + // Constructors + NCrystalMat() = default; + explicit NCrystalMat(const std::string& cfg); + + //---------------------------------------------------------------------------- + // Methods + +#ifdef NCRYSTAL + //! Return configuration string + std::string cfg() const; + + //! Get cross section from NCrystal material + // + //! \param[in] p Particle object + //! \return Cross section in [b] + double xs(const Particle& p) const; + + // Process scattering event + // + //! \param[in] p Particle object + void scatter(Particle& p) const; + + //! Whether the object holds a valid NCrystal material + operator bool() const; +#else + + //---------------------------------------------------------------------------- + // Trivial methods when compiling without NCRYSTAL + std::string cfg() const + { + return ""; + } + double xs(const Particle& p) const + { + return -1.0; + } + void scatter(Particle& p) const {} + operator bool() const + { + return false; + } +#endif + +private: + //---------------------------------------------------------------------------- + // Data members (only present when compiling with NCrystal support) +#ifdef NCRYSTAL + std::string cfg_; //!< NCrystal configuration string + std::shared_ptr + ptr_; //!< Pointer to NCrystal material object +#endif +}; + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro); + +} // namespace openmc + +#endif // OPENMC_NCRYSTAL_INTERFACE_H diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 806297c4ea..382f0f0dd2 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -8,10 +8,6 @@ #include "openmc/reaction.h" #include "openmc/vector.h" -#ifdef NCRYSTAL -#include "NCrystal/NCRNG.hh" -#endif - namespace openmc { //============================================================================== @@ -100,31 +96,11 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p); void sample_secondary_photons(Particle& p, int i_nuclide); -//!Split or Roulette particles based their weight and the lower weight window -// bound. +// !Split or Roulette particles based their weight and the lower weight window +// bound. //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); -#ifdef NCRYSTAL -//============================================================================== -// NCrystal wrapper class for the OpenMC random number generator -//============================================================================== - -class NCrystalRNGWrapper : public NCrystal::RNGStream { -public: - constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} - -protected: - double actualGenerate() override - { - return std::max( - std::numeric_limits::min(), prn(openmc_seed_)); - } -private: - uint64_t* openmc_seed_; -}; -#endif - } // namespace openmc #endif // OPENMC_PHYSICS_H diff --git a/include/openmc/settings.h b/include/openmc/settings.h index fb5f8387a1..9cd430deba 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -20,8 +20,6 @@ namespace openmc { // Global variable declarations //============================================================================== -extern "C" const bool NCRYSTAL_ENABLED; - namespace settings { // Boolean flags @@ -93,6 +91,8 @@ extern int n_log_bins; //!< number of bins for logarithmic energy grid extern int n_batches; //!< number of (inactive+active) batches extern int n_max_batches; //!< Maximum number of batches extern int max_tracks; //!< Maximum number of particle tracks written to file +extern double + ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF extern ResScatMethod res_scat_method; //!< resonance upscattering method extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering @@ -124,10 +124,6 @@ extern int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette -#ifdef NCRYSTAL -extern double - ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF -#endif } // namespace settings //============================================================================== diff --git a/src/material.cpp b/src/material.cpp index 25014c448d..2d55d6a047 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -60,13 +60,12 @@ Material::Material(pugi::xml_node node) name_ = get_node_value(node, "name"); } -#ifdef NCRYSTAL if (check_for_node(node, "cfg")) { - ncrystal_cfg_ = get_node_value(node, "cfg"); - write_message(5, "NCrystal config string for material #{}: '{}'", this->id(), ncrystal_cfg_); - ncrystal_mat_ = NCrystal::FactImpl::createScatter(ncrystal_cfg_); + auto cfg = get_node_value(node, "cfg"); + write_message( + 5, "NCrystal config string for material #{}: '{}'", this->id(), cfg); + ncrystal_mat_ = NCrystalMat(cfg); } -#endif if (check_for_node(node, "depletable")) { depletable_ = get_node_value_bool(node, "depletable"); @@ -800,19 +799,11 @@ void Material::calculate_neutron_xs(Particle& p) const // Initialize position in i_sab_nuclides int j = 0; -#ifdef NCRYSTAL - double ncrystal_xs = -1; - + // Calculate NCrystal cross section + double ncrystal_xs = -1.0; if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { - // Calculate scattering XS per atom with NCrystal, only once per material - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - ncrystal_xs = - ncrystal_mat_ - ->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) - .get(); + ncrystal_xs = ncrystal_mat_.xs(p); } -#endif // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { @@ -852,26 +843,16 @@ void Material::calculate_neutron_xs(Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide -#ifdef NCRYSTAL auto& micro {p.neutron_xs(i_nuclide)}; -#else - const auto& micro {p.neutron_xs(i_nuclide)}; -#endif if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); -#ifdef NCRYSTAL + + // If NCrystal is being used, update micro cross section cache if (ncrystal_xs >= 0.0) { - if (micro.thermal > 0 || micro.thermal_elastic > 0) { - fatal_error("S(a,b) treatment and NCrystal are not compatible."); - } data::nuclides[i_nuclide]->calculate_elastic_xs(p); - // remove free atom cross section - // and replace it by scattering cross section per atom from NCrystal - micro.total = micro.total - micro.elastic + ncrystal_xs; - micro.elastic = ncrystal_xs; + ncrystal_update_micro(ncrystal_xs, micro); } -#endif } // ====================================================================== diff --git a/src/ncrystal_interface.cpp b/src/ncrystal_interface.cpp new file mode 100644 index 0000000000..b39f62d902 --- /dev/null +++ b/src/ncrystal_interface.cpp @@ -0,0 +1,108 @@ +#include "openmc/ncrystal_interface.h" + +#include "openmc/error.h" +#include "openmc/material.h" +#include "openmc/random_lcg.h" + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +#ifdef NCRYSTAL +const bool NCRYSTAL_ENABLED = true; +#else +const bool NCRYSTAL_ENABLED = false; +#endif + +//============================================================================== +// NCrystal wrapper class for the OpenMC random number generator +//============================================================================== + +#ifdef NCRYSTAL +class NCrystalRNGWrapper : public NCrystal::RNGStream { +public: + constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} + +protected: + double actualGenerate() override + { + return std::max( + std::numeric_limits::min(), prn(openmc_seed_)); + } + +private: + uint64_t* openmc_seed_; +}; +#endif + +//============================================================================== +// NCrystal implementation +//============================================================================== + +NCrystalMat::NCrystalMat(const std::string& cfg) +{ +#ifdef NCRYSTAL + cfg_ = cfg; + ptr_ = NCrystal::FactImpl::createScatter(cfg); +#else + fatal_error("Your build of OpenMC does not support NCrystal materials."); +#endif +} + +#ifdef NCRYSTAL +std::string NCrystalMat::cfg() const +{ + return cfg_; +} + +double NCrystalMat::xs(const Particle& p) const +{ + // Calculate scattering XS per atom with NCrystal, only once per material + NCrystal::CachePtr dummy_cache; + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + return ptr_->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) + .get(); +} + +void NCrystalMat::scatter(Particle& p) const +{ + NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG + // create a cache pointer for multi thread physics + NCrystal::CachePtr dummy_cache; + auto nc_energy = NCrystal::NeutronEnergy {p.E()}; + auto outcome = ptr_->sampleScatter( + dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); + + // Modify attributes of particle + p.E() = outcome.ekin.get(); + Direction u_old {p.u()}; + p.u() = + Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.mu() = u_old.dot(p.u()); + p.event_mt() = ELASTIC; +} + +NCrystalMat::operator bool() const +{ + return ptr_.get(); +} +#endif + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro) +{ + if (micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + xs; + micro.elastic = xs; +} + +} // namespace openmc diff --git a/src/physics.cpp b/src/physics.cpp index c1ef282f7a..46bf37aa9f 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -10,6 +10,7 @@ #include "openmc/material.h" #include "openmc/math_functions.h" #include "openmc/message_passing.h" +#include "openmc/ncrystal_interface.h" #include "openmc/nuclide.h" #include "openmc/photon.h" #include "openmc/physics_common.h" @@ -138,32 +139,15 @@ void sample_neutron_reaction(Particle& p) if (!p.alive()) return; - // Sample a scattering reaction and determine the secondary energy of the - // exiting neutron - -#ifdef NCRYSTAL - if (model::materials[p.material()]->ncrystal_mat() && - p.E() < settings::ncrystal_max_energy) { - NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG - // create a cache pointer for multi thread physics - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - auto outcome = - model::materials[p.material()]->ncrystal_mat()->sampleScatter( - dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); - p.E_last() = p.E(); - p.E() = outcome.ekin.get(); - Direction u_old {p.u()}; - p.u() = Direction( - outcome.direction[0], outcome.direction[1], outcome.direction[2]); - p.mu() = u_old.dot(p.u()); - p.event_mt() = ELASTIC; + // Sample a scattering reaction and determine the secondary energy of the + // exiting neutron + const auto& ncrystal_mat = model::materials[p.material()]->ncrystal_mat(); + if (ncrystal_mat && p.E() < settings::ncrystal_max_energy) { + ncrystal_mat.scatter(p); } else { scatter(p, i_nuclide); } -#else - scatter(p, i_nuclide); -#endif + // Advance URR seed stream 'N' times after energy changes if (p.E() != p.E_last()) { p.stream() = STREAM_URR_PTABLE; diff --git a/src/settings.cpp b/src/settings.cpp index 6f6e4e090a..9174d41147 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -37,13 +37,6 @@ namespace openmc { // Global variables //============================================================================== -#ifdef NCRYSTAL -const bool NCRYSTAL_ENABLED = true; -#else -const bool NCRYSTAL_ENABLED = false; -#endif - - namespace settings { // Default values for boolean flags @@ -106,6 +99,7 @@ int n_batches; int n_max_batches; int max_splits {1000}; int max_tracks {1000}; +double ncrystal_max_energy {5.0}; ResScatMethod res_scat_method {ResScatMethod::rvs}; double res_scat_energy_min {0.01}; double res_scat_energy_max {1000.0}; @@ -128,10 +122,6 @@ int verbosity {7}; double weight_cutoff {0.25}; double weight_survive {1.0}; -#ifdef NCRYSTAL -double ncrystal_max_energy {5.0}; -#endif - } // namespace settings //============================================================================== From 4eda693133a3a0f6c55eea084ca7ddeb3ab0769d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Jan 2023 10:39:22 +0700 Subject: [PATCH 252/265] Style and spacing changes --- docs/source/methods/cross_sections.rst | 27 ++++++++++++++----------- docs/source/usersguide/install.rst | 11 +++++----- docs/source/usersguide/materials.rst | 20 +++++++++--------- include/openmc/physics.h | 5 +++-- tests/regression_tests/ncrystal/test.py | 16 +++++++++------ 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 0b87913e51..2cafa86916 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -182,19 +182,22 @@ been selected. There are three methods available: NCrystal materials ------------------ -As an alternative of the standard thermal scattering treatment using :math:`S(\alpha,\beta)` -tables, OpenMC allows to create materials using NCrystal_. In addition to the regular thermal elastic, -and thermal inelastic processes, NCrystal allows the generation of models for materials that -cannot currently included in ACE files such as oriented single crystals (see the `NCrystal paper`_), -and further extend the physics `using plugins`_. Thermal scattering kernels are generated on -the fly from dynamic and structural data, or loaded from :math:`S(\alpha,\beta)` tables converted -from ENDF6 evaluations. These kernels are sampled in a direct way using a fast `rejection algorithm`_ -that does not require previous processing. A `large library`_ of materials is already included in -the NCrystal distribution, and new materials can be easily defined from scratch in the `NCMAT format`_ -or `combining existing files`_. +As an alternative of the standard thermal scattering treatment using +:math:`S(\alpha,\beta)` tables, OpenMC allows to create materials using +NCrystal_. In addition to the regular thermal elastic, and thermal inelastic +processes, NCrystal allows the generation of models for materials that cannot +currently included in ACE files such as oriented single crystals (see the +`NCrystal paper`_), and further extend the physics `using plugins`_. Thermal +scattering kernels are generated on the fly from dynamic and structural data, or +loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. +These kernels are sampled in a direct way using a fast `rejection algorithm`_ +that does not require previous processing. A `large library`_ of materials is +already included in the NCrystal distribution, and new materials can be easily +defined from scratch in the `NCMAT format`_ or `combining existing files`_. -The compositions of the materials defined in NCrystal are passed on to OpenMC all other reactions -except for thermal neutron scattering are handled by continuous energy ACE libraries. +The compositions of the materials defined in NCrystal are passed on to OpenMC +all other reactions except for thermal neutron scattering are handled by +continuous energy ACE libraries. ---------------- Multi-Group Data diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 9e5ba1ffd9..537dda8248 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -274,9 +274,10 @@ Prerequisites * NCrystal_ library for defining materials with enhanced thermal neutron transport Adding this option allows the creation of materials from NCrystal, which - replaces the scattering kernel treatment of ACE files with a modular, on-the-fly approach. - To use it `install `_ and - `initialize `_ + replaces the scattering kernel treatment of ACE files with a modular, + on-the-fly approach. To use it `install + `_ and `initialize + `_ NCrystal and turn on the option in the CMake configuration step: cmake -DOPENMC_USE_NCRYSTAL=on .. @@ -374,8 +375,8 @@ OPENMC_USE_DAGMC (Default: off) OPENMC_USE_NCRYSTAL - Turns on support for NCrystal materials. NCrystal must be - `installed `_ and + Turns on support for NCrystal materials. NCrystal must be + `installed `_ and `initialized `_. (Default: off) diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index fbf9effd89..83af558057 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -105,12 +105,14 @@ you would need to add hydrogen and oxygen to a material and then assign the Adding NCrystal materials ------------------------- -Additional support for thermal scattering can be added by using NCrystal_. -The :meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` object from -an `NCrystal configuration string `_. -Temperature, material composition, and density are passed from the configuration string -and the `NCMAT file `_ -that define the material, e.g.:: +Additional support for thermal scattering can be added by using NCrystal_. The +:meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` +object from an `NCrystal configuration string +`_. +Temperature, material composition, and density are passed from the configuration +string and the `NCMAT file +`_ that define the +material, e.g.:: mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') @@ -125,12 +127,12 @@ defines a material containing polycrystalline alumnium, defines an oriented germanium single crystal with 40 arcsec mosaicity. NCrystal only handles low energy neutron interactions. Other interactions are -provided by standard ACE files. NCrystal_ comes with a `predefined library +provided by standard ACE files. NCrystal_ comes with a `predefined library `_ but more materials can be added by creating NCMAT files or on-the-fly in the configuration string. -.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. - Density, temperature and composition should be defined in the +.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. + Density, temperature and composition should be defined in the configuration string or the NCMAT file. .. _NCrystal: https://github.com/mctools/ncrystal diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 382f0f0dd2..6e7327d381 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -96,8 +96,9 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p); void sample_secondary_photons(Particle& p, int i_nuclide); -// !Split or Roulette particles based their weight and the lower weight window -// bound. +//! Split or Roulette particles based their weight and the lower weight window +//! bound. +// //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index 2a789dac78..cb6421b034 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -1,3 +1,5 @@ +from math import pi + import numpy as np import openmc import openmc.lib @@ -9,6 +11,7 @@ pytestmark = pytest.mark.skipif( not openmc.lib._ncrystal_enabled(), reason="NCrystal materials are not enabled.") + def pencil_beam_model(cfg, E0, N): """Return an openmc.Model() object for a monoenergetic pencil beam hitting a 1 mm sphere filled with the material defined by @@ -24,7 +27,7 @@ def pencil_beam_model(cfg, E0, N): sample_sphere = openmc.Sphere(r=0.1) outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") cell1 = openmc.Cell(region=-sample_sphere, fill=m1) - cell2_region= +sample_sphere & -outer_sphere + cell2_region = +sample_sphere & -outer_sphere cell2 = openmc.Cell(region=cell2_region, fill=None) geometry = openmc.Geometry([cell1, cell2]) @@ -48,7 +51,7 @@ def pencil_beam_model(cfg, E0, N): tally1 = openmc.Tally(name="angular distribution") tally1.scores = ["current"] filter1 = openmc.SurfaceFilter(sample_sphere) - filter2 = openmc.PolarFilter(np.linspace(0, np.pi, 180+1)) + filter2 = openmc.PolarFilter(np.linspace(0, pi, 180+1)) filter3 = openmc.CellFromFilter(cell1) tally1.filters = [filter1, filter2, filter3] tallies = openmc.Tallies([tally1]) @@ -66,11 +69,12 @@ class NCrystalTest(PyAPITestHarness): df = tal.get_pandas_dataframe() return df.to_string() + def test_ncrystal(): - NParticles = 100000 - T = 293.6 # K - E0 = 0.012 # eV + n_particles = 100000 + T = 293.6 # K + E0 = 0.012 # eV cfg = 'Al_sg225.ncmat' - test = pencil_beam_model(cfg, E0, NParticles) + test = pencil_beam_model(cfg, E0, n_particles) harness = NCrystalTest('statepoint.10.h5', model=test) harness.main() From c4227f7dc1b30879dd7492730e8dbb02b14006d5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 Jan 2023 10:39:45 +0700 Subject: [PATCH 253/265] Change Material.from_ncrystal to accept kwargs --- openmc/material.py | 51 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 68ec199c3c..7844ec131c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -99,6 +99,10 @@ class Material(IDManagerMixin): [decay/sec]. .. versionadded:: 0.13.2 + ncrystal_cfg : str + NCrystal configuration string + + .. versionadded:: 0.13.3 """ @@ -141,7 +145,8 @@ class Material(IDManagerMixin): string += '{: <16}\n'.format('\tS(a,b) Tables') - string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) + if self._ncrystal_cfg: + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -222,6 +227,10 @@ class Material(IDManagerMixin): def volume(self): return self._volume + @property + def ncrystal_cfg(self): + return self._ncrystal_cfg + @name.setter def name(self, name: Optional[str]): if name is not None: @@ -335,9 +344,9 @@ class Material(IDManagerMixin): return material @classmethod - def from_ncrystal(cls, cfg, material_id=None, name=''): + def from_ncrystal(cls, cfg, **kwargs): """Create material from NCrystal configuration string. - + Density, temperature, and material composition, and (ultimately) thermal neutron scattering will be automatically be provided by NCrystal based on this string. The name and material_id parameters are simply passed on @@ -347,12 +356,8 @@ class Material(IDManagerMixin): ---------- cfg : str NCrystal configuration string - material_id : int, optional - Unique identifier for the material. If not specified, an identifier will - automatically be assigned. - name : str, optional - Name of the material. If not specified, the name will be the empty - string. + **kwargs + Keyword arguments passed to :class:`openmc.Material` Returns ------- @@ -371,26 +376,20 @@ class Material(IDManagerMixin): #only get invoked in the unlikely case where a material is specified #by referring both to natural elements and specific isotopes of the #same element. - elem_name = openmc.data.ATOMIC_SYMBOL.get(Z) - if not elem_name: - raise ValueError( f'Element with Z={Z} is not known' ) + elem_name = openmc.data.ATOMIC_SYMBOL[Z] return [ (int(iso_name[len(elem_name):]), abund) for iso_name, abund in openmc.data.isotopes(elem_name) ] - flat_compos = nc_mat.getFlattenedComposition(preferNaturalElements = True, - naturalAbundProvider=openmc_natabund) + flat_compos = nc_mat.getFlattenedComposition( + preferNaturalElements=True, naturalAbundProvider=openmc_natabund) # Create the Material - material = cls(material_id=material_id, - name=name, - temperature=nc_mat.getTemperature()) + material = cls(temperature=nc_mat.getTemperature(), **kwargs) for Z, A_vals in flat_compos: - elemname = openmc.data.ATOMIC_SYMBOL.get(Z) - if not elemname: - raise ValueError(f'Element with Z={Z} is not known') + elemname = openmc.data.ATOMIC_SYMBOL[Z] for A, frac in A_vals: if A: material.add_nuclide(f'{elemname}{A}', frac) @@ -1067,7 +1066,7 @@ class Material(IDManagerMixin): activity[nuclide] = inv_seconds * 1e24 * atoms_per_bcm * multiplier return activity if by_nuclide else sum(activity.values()) - + def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False): """Returns the decay heat of the material or for each nuclide in the material in units of [W], [W/g] or [W/cm3]. @@ -1101,16 +1100,16 @@ class Material(IDManagerMixin): multiplier = 1 elif units == 'W/g': multiplier = 1.0 / self.get_mass_density() - - decayheat = {} + + decayheat = {} for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items(): decay_erg = openmc.data.decay_energy(nuclide) inv_seconds = openmc.data.decay_constant(nuclide) decay_erg *= openmc.data.JOULE_PER_EV decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier - return decayheat if by_nuclide else sum(decayheat.values()) - + return decayheat if by_nuclide else sum(decayheat.values()) + def get_nuclide_atoms(self): """Return number of atoms of each nuclide in the material @@ -1651,4 +1650,4 @@ class Materials(cv.CheckedList): tree = ET.parse(path) root = tree.getroot() - return cls.from_xml_element(root) \ No newline at end of file + return cls.from_xml_element(root) From e83ced88dc58d0a9fcf83f04390a5284b30d06b9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 Jan 2023 01:17:53 +0700 Subject: [PATCH 254/265] Change ncrystal_max_energy to be a global constant --- include/openmc/ncrystal_interface.h | 3 +++ include/openmc/settings.h | 2 -- src/material.cpp | 2 +- src/physics.cpp | 2 +- src/settings.cpp | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h index 36038d50c3..5a3882df9c 100644 --- a/include/openmc/ncrystal_interface.h +++ b/include/openmc/ncrystal_interface.h @@ -20,6 +20,9 @@ namespace openmc { extern "C" const bool NCRYSTAL_ENABLED; +//! Energy in [eV] to switch between NCrystal and ENDF +constexpr double NCRYSTAL_MAX_ENERGY {5.0}; + //============================================================================== // Wrapper class an NCrystal material //============================================================================== diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 9cd430deba..90f1e8c297 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -91,8 +91,6 @@ extern int n_log_bins; //!< number of bins for logarithmic energy grid extern int n_batches; //!< number of (inactive+active) batches extern int n_max_batches; //!< Maximum number of batches extern int max_tracks; //!< Maximum number of particle tracks written to file -extern double - ncrystal_max_energy; //!< Energy in eV to switch between NCrystal and ENDF extern ResScatMethod res_scat_method; //!< resonance upscattering method extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering diff --git a/src/material.cpp b/src/material.cpp index 2d55d6a047..74a8d7e350 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -801,7 +801,7 @@ void Material::calculate_neutron_xs(Particle& p) const // Calculate NCrystal cross section double ncrystal_xs = -1.0; - if (ncrystal_mat_ && p.E() < settings::ncrystal_max_energy) { + if (ncrystal_mat_ && p.E() < NCRYSTAL_MAX_ENERGY) { ncrystal_xs = ncrystal_mat_.xs(p); } diff --git a/src/physics.cpp b/src/physics.cpp index 46bf37aa9f..7cb8040f63 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -142,7 +142,7 @@ void sample_neutron_reaction(Particle& p) // Sample a scattering reaction and determine the secondary energy of the // exiting neutron const auto& ncrystal_mat = model::materials[p.material()]->ncrystal_mat(); - if (ncrystal_mat && p.E() < settings::ncrystal_max_energy) { + if (ncrystal_mat && p.E() < NCRYSTAL_MAX_ENERGY) { ncrystal_mat.scatter(p); } else { scatter(p, i_nuclide); diff --git a/src/settings.cpp b/src/settings.cpp index 9174d41147..2fda352ce0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -99,7 +99,6 @@ int n_batches; int n_max_batches; int max_splits {1000}; int max_tracks {1000}; -double ncrystal_max_energy {5.0}; ResScatMethod res_scat_method {ResScatMethod::rvs}; double res_scat_energy_min {0.01}; double res_scat_energy_max {1000.0}; From a0afa5de441bbd584721bf7f6c029e966675291f Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 10 Jan 2023 19:04:12 +0000 Subject: [PATCH 255/265] added get_surfaces_by_name method --- openmc/geometry.py | 21 +++++++++++++++++++++ tests/unit_tests/test_geometry.py | 16 ++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index a43899daaf..f9aecca9c7 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -525,6 +525,27 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'cell') + def get_surfaces_by_name(self, name, case_sensitive=False, matching=False): + """Return a list of surfaces with matching names. + + Parameters + ---------- + name : str + The name to search match + case_sensitive : bool + Whether to distinguish upper and lower case letters in each + surface's name (default is False) + matching : bool + Whether the names must match completely (default is False) + + Returns + ------- + list of openmc.Surface + Surfaces matching the queried name + + """ + return self._get_domains_by_name(name, case_sensitive, matching, 'surface') + def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False): """Return a list of cells with fills with matching names. diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 9db112fd29..f8c5230714 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -159,12 +159,13 @@ def test_get_by_name(): m2 = openmc.Material(name='Zirconium') m2.add_element('Zr', 1.0) - c1 = openmc.Cell(fill=m1, name='cell1') + s1 = openmc.Sphere(name='surface1') + c1 = openmc.Cell(fill=m1, region=-s1, name='cell1') u1 = openmc.Universe(name='Zircaloy universe', cells=[c1]) - cyl = openmc.ZCylinder() - c2 = openmc.Cell(fill=u1, region=-cyl, name='cell2') - c3 = openmc.Cell(fill=m2, region=+cyl, name='Cell3') + s2 = openmc.ZCylinder(name='surface2') + c2 = openmc.Cell(fill=u1, region=-s2, name='cell2') + c3 = openmc.Cell(fill=m2, region=+s2, name='Cell3') root = openmc.Universe(name='root Universe', cells=[c2, c3]) geom = openmc.Geometry(root) @@ -177,6 +178,13 @@ def test_get_by_name(): mats = geom.get_materials_by_name('zirconium', True, True) assert not mats + surfaces = set(geom.get_surfaces_by_name('surface')) + assert not surfaces ^ {s1, s2} + surfaces = set(geom.get_surfaces_by_name('Surface2', False, True)) + assert not surfaces ^ {s2} + surfaces = geom.get_surfaces_by_name('Surface2', True, True) + assert not surfaces + cells = set(geom.get_cells_by_name('cell')) assert not cells ^ {c1, c2, c3} cells = set(geom.get_cells_by_name('cell', True)) From 2c9df1b77e5828ed410dbd00250856265ec211ca Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 11 Jan 2023 09:32:02 +0000 Subject: [PATCH 256/265] Apply typo fixes identified incode review by @paulromano Co-authored-by: Paul Romano --- openmc/geometry.py | 2 +- tests/unit_tests/test_geometry.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index eb7fa8200a..a9817fbbb8 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -267,7 +267,7 @@ class Geometry: ---------- path : PathLike, optional Path to geometry XML file - materials : openmc.Materials or or PathLike + materials : openmc.Materials or PathLike Materials used to assign to cells. If PathLike, an attempt is made to generate materials from the provided xml file. diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 843c01defd..0e952ab0f8 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -290,7 +290,6 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) assert 'Unable to set "materials" to "None"' in str(excinfo.value) - # checking that the default args also work geom = openmc.Geometry.from_xml() assert isinstance(geom, openmc.Geometry) From 50892c61b7865a92b27e289be88299ad2667343b Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 13:56:54 -0500 Subject: [PATCH 257/265] unit test and description --- tests/unit_tests/test_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 1ea66d1f6f..15e86ca619 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -49,6 +49,7 @@ def test_export_to_xml(run_in_tmpdir): domains=[openmc.Cell()], samples=1000, lower_left=(-10., -10., -10.), upper_right = (10., 10., 10.)) s.create_fission_neutrons = True + s.create_delayed_neutrons = False s.log_grid_bins = 2000 s.photon_transport = False s.electron_treatment = 'led' @@ -107,6 +108,7 @@ def test_export_to_xml(run_in_tmpdir): 'energy_min': 1.0, 'energy_max': 1000.0, 'nuclides': ['U235', 'U238', 'Pu239']} assert s.create_fission_neutrons + assert not s.create_delayed_neutrons assert s.log_grid_bins == 2000 assert not s.photon_transport assert s.electron_treatment == 'led' From 9d86274fcc790e7ba05d030234ec099ef8c09d15 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 14:17:29 -0500 Subject: [PATCH 258/265] merge conflict --- openmc/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openmc/settings.py b/openmc/settings.py index 6f3ef5ab4f..6271e05dc9 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -224,6 +224,10 @@ class Settings: weight_windows : WeightWindows iterable of WeightWindows Weight windows to use for variance reduction + .. versionadded:: 0.13.3 + create_delayed_neutrons : bool + Whether delayed neutrons are created in fission. + .. versionadded:: 0.13 weight_windows_on : bool Whether weight windows are enabled From e266bb15bfcbbdbaa77a0a30c2c9909f60612bb8 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 14:50:06 -0500 Subject: [PATCH 259/265] old changes for delayed neutron feature --- docs/source/io_formats/settings.rst | 11 +++++++++++ include/openmc/settings.h | 1 + openmc/settings.py | 23 +++++++++++++++++++++++ src/finalize.cpp | 1 + src/nuclide.cpp | 4 ++-- src/settings.cpp | 7 +++++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 71ca61d540..6dff768381 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -32,6 +32,17 @@ standard deviation. *Default*: false +------------------------------------- +```` Element +------------------------------------- + +The ```` element indicates whether delayed neutrons +are created in fission. If this element is set to "true", delayed neutrons +will be created in fission events; otherwise only prompt neutrons will be +created. + + *Default*: true + ------------------------------------- ```` Element ------------------------------------- diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 90f1e8c297..806288efe5 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -28,6 +28,7 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? +extern bool create_delayed_neutrons; //!< create delayed fission neutrons? extern "C" bool cmfd_run; //!< is a CMFD run? extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed diff --git a/openmc/settings.py b/openmc/settings.py index 6271e05dc9..3f5873c0a7 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -300,6 +300,7 @@ class Settings: VolumeCalculation, 'volume calculations') self._create_fission_neutrons = None + self._create_delayed_neutrons = None self._delayed_photon_scaling = None self._material_cell_offsets = None self._log_grid_bins = None @@ -463,6 +464,10 @@ class Settings: def create_fission_neutrons(self) -> bool: return self._create_fission_neutrons + @property + def create_delayed_neutrons(self) -> bool: + return self._create_delayed_neutrons + @property def delayed_photon_scaling(self) -> bool: return self._delayed_photon_scaling @@ -870,6 +875,12 @@ class Settings: create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons + @create_delayed_neutrons.setter + def create_delayed_neutrons(self, create_delayed_neutrons: bool): + cv.check_type('Whether create only prompt neutrons', + create_delayed_neutrons, bool) + self._create_delayed_neutrons = create_delayed_neutrons + @delayed_photon_scaling.setter def delayed_photon_scaling(self, value: bool): cv.check_type('delayed photon scaling', value, bool) @@ -1211,6 +1222,11 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() + def _create_create_delayed_neutrons_subelement(self, root): + if self._create_delayed_neutrons is not None: + elem = ET.SubElement(root, "create_delayed_neutrons") + elem.text = str(self._create_delayed_neutrons).lower() + def _create_delayed_photon_scaling_subelement(self, root): if self._delayed_photon_scaling is not None: elem = ET.SubElement(root, "delayed_photon_scaling") @@ -1536,6 +1552,11 @@ class Settings: if text is not None: self.create_fission_neutrons = text in ('true', '1') + def _create_delayed_neutrons_from_xml_element(self, root): + text = get_text(root, 'create_delayed_neutrons') + if text is not None: + self.create_delayed_neutrons = text in ('true', '1') + def _delayed_photon_scaling_from_xml_element(self, root): text = get_text(root, 'delayed_photon_scaling') if text is not None: @@ -1634,6 +1655,7 @@ class Settings: self._create_resonance_scattering_subelement(element) self._create_volume_calcs_subelement(element) self._create_create_fission_neutrons_subelement(element) + self._create_create_delayed_neutrons_subelement(root_element) self._create_delayed_photon_scaling_subelement(element) self._create_event_based_subelement(element) self._create_max_particles_in_flight_subelement(element) @@ -1726,6 +1748,7 @@ class Settings: settings._ufs_mesh_from_xml_element(elem, meshes) settings._resonance_scattering_from_xml_element(elem) settings._create_fission_neutrons_from_xml_element(elem) + settings._create_delayed_neutrons_from_xml_element(root) settings._delayed_photon_scaling_from_xml_element(elem) settings._event_based_from_xml_element(elem) settings._max_particles_in_flight_from_xml_element(elem) diff --git a/src/finalize.cpp b/src/finalize.cpp index 0c2c62310e..59294b28c9 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -74,6 +74,7 @@ int openmc_finalize() settings::check_overlaps = false; settings::confidence_intervals = false; settings::create_fission_neutrons = true; + settings::create_delayed_neutrons = true; settings::electron_treatment = ElectronTreatment::LED; settings::delayed_photon_scaling = true; settings::energy_cutoff = {0.0, 1000.0, 0.0, 0.0}; diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 134e2d6b9a..99ed44b112 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -519,7 +519,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const case EmissionMode::prompt: return (*fission_rx_[0]->products_[0].yield_)(E); case EmissionMode::delayed: - if (n_precursor_ > 0) { + if (n_precursor_ > 0 && settings::create_delayed_neutrons) { auto rx = fission_rx_[0]; if (group >= 1 && group < rx->products_.size()) { // If delayed group specified, determine yield immediately @@ -544,7 +544,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const return 0.0; } case EmissionMode::total: - if (total_nu_) { + if (total_nu_ && settings::create_delayed_neutrons) { return (*total_nu_)(E); } else { return (*fission_rx_[0]->products_[0].yield_)(E); diff --git a/src/settings.cpp b/src/settings.cpp index 6386ce52bd..1b822f3b96 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -44,6 +44,7 @@ bool assume_separate {false}; bool check_overlaps {false}; bool cmfd_run {false}; bool confidence_intervals {false}; +bool create_delayed_neutrons {true}; bool create_fission_neutrons {true}; bool delayed_photon_scaling {true}; bool entropy_on {false}; @@ -872,6 +873,12 @@ void read_settings_xml(pugi::xml_node root) } } + // Check whether create delayed neutrons in fission + if (check_for_node(root, "create_delayed_neutrons")) { + create_delayed_neutrons = + get_node_value_bool(root, "create_delayed_neutrons"); + } + // Check whether create fission sites if (run_mode == RunMode::FIXED_SOURCE) { if (check_for_node(root, "create_fission_neutrons")) { From 8b1ecaad09546901231dc0890e55fc57622b77bf Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 15:38:16 -0500 Subject: [PATCH 260/265] deleted white spaces --- openmc/settings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 3f5873c0a7..2210b23a28 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -465,7 +465,7 @@ class Settings: return self._create_fission_neutrons @property - def create_delayed_neutrons(self) -> bool: + def create_delayed_neutrons(self) -> bool: return self._create_delayed_neutrons @property @@ -875,7 +875,7 @@ class Settings: create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons - @create_delayed_neutrons.setter + @create_delayed_neutrons.setter def create_delayed_neutrons(self, create_delayed_neutrons: bool): cv.check_type('Whether create only prompt neutrons', create_delayed_neutrons, bool) @@ -1222,7 +1222,7 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() - def _create_create_delayed_neutrons_subelement(self, root): + def _create_create_delayed_neutrons_subelement(self, root): if self._create_delayed_neutrons is not None: elem = ET.SubElement(root, "create_delayed_neutrons") elem.text = str(self._create_delayed_neutrons).lower() @@ -1552,7 +1552,7 @@ class Settings: if text is not None: self.create_fission_neutrons = text in ('true', '1') - def _create_delayed_neutrons_from_xml_element(self, root): + def _create_delayed_neutrons_from_xml_element(self, root): text = get_text(root, 'create_delayed_neutrons') if text is not None: self.create_delayed_neutrons = text in ('true', '1') From 3ba117fcd95afa1f8aba745dd368db705a3a5102 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 15:41:31 -0500 Subject: [PATCH 261/265] indentation level change --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 2210b23a28..79d28c44b7 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1222,7 +1222,7 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() - def _create_create_delayed_neutrons_subelement(self, root): + def _create_create_delayed_neutrons_subelement(self, root): if self._create_delayed_neutrons is not None: elem = ET.SubElement(root, "create_delayed_neutrons") elem.text = str(self._create_delayed_neutrons).lower() From afa490f6a6c98e41062c180eb74fae20ddffdad4 Mon Sep 17 00:00:00 2001 From: Josh May Date: Wed, 11 Jan 2023 13:27:06 -0800 Subject: [PATCH 262/265] Apply suggestions from code review Co-authored-by: Paul Romano --- openmc/cell.py | 3 +-- tests/unit_tests/test_cell.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index cea362ed8f..a8e1178f41 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -518,8 +518,7 @@ class Cell(IDManagerMixin): paths = self._paths self._paths = None - clone = openmc.Cell() - clone.name = self.name + clone = openmc.Cell(name=self.name) clone.volume = self.volume if self.temperature is not None: clone.temperature = self.temperature diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 4185e9a44c..cef77f1608 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -76,8 +76,8 @@ def test_clone(): # Add optional properties to the original cell to ensure they're cloned successfully c.temperature = 650. - c.translation = [1,2,3] - c.rotation = [4,5,6] + c.translation = (1., 2., 3.) + c.rotation = (4., 5., 6.) c.volume = 100 c5 = c.clone(clone_materials=False, clone_regions=False) @@ -92,8 +92,8 @@ def test_clone(): # Mutate the original to ensure the changes are not seen in the clones c.fill = openmc.Material() c.region = +openmc.ZCylinder() - c.translation = [-1,-2,-3] - c.rotation = [-4,-5,-6] + c.translation = (-1., -2., -3.) + c.rotation = (-4., -5., -6.) c.temperature = 1 c.volume = 1 assert c5.fill != c.fill From f7c1a57b15ba1fe5bb697db077bf4cd29d595f79 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer Date: Wed, 11 Jan 2023 16:55:27 -0500 Subject: [PATCH 263/265] settings change element --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 79d28c44b7..911131387e 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1655,7 +1655,7 @@ class Settings: self._create_resonance_scattering_subelement(element) self._create_volume_calcs_subelement(element) self._create_create_fission_neutrons_subelement(element) - self._create_create_delayed_neutrons_subelement(root_element) + self._create_create_delayed_neutrons_subelement(element) self._create_delayed_photon_scaling_subelement(element) self._create_event_based_subelement(element) self._create_max_particles_in_flight_subelement(element) From 36249d3742dabfe95ee50e2aac822caf72f1e1a9 Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer <29277544+cfichtlscherer@users.noreply.github.com> Date: Thu, 12 Jan 2023 06:25:52 -0500 Subject: [PATCH 264/265] Update openmc/settings.py yes Co-authored-by: Paul Romano --- openmc/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/settings.py b/openmc/settings.py index 911131387e..9330d99b6e 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1748,7 +1748,7 @@ class Settings: settings._ufs_mesh_from_xml_element(elem, meshes) settings._resonance_scattering_from_xml_element(elem) settings._create_fission_neutrons_from_xml_element(elem) - settings._create_delayed_neutrons_from_xml_element(root) + settings._create_delayed_neutrons_from_xml_element(elem) settings._delayed_photon_scaling_from_xml_element(elem) settings._event_based_from_xml_element(elem) settings._max_particles_in_flight_from_xml_element(elem) From a66d4da53f1ce87f6a14902636ab12eb765c895b Mon Sep 17 00:00:00 2001 From: Christopher Fichtlscherer <29277544+cfichtlscherer@users.noreply.github.com> Date: Thu, 12 Jan 2023 06:26:07 -0500 Subject: [PATCH 265/265] Update openmc/settings.py version number Co-authored-by: Paul Romano --- openmc/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 9330d99b6e..22a83509f3 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -224,11 +224,11 @@ class Settings: weight_windows : WeightWindows iterable of WeightWindows Weight windows to use for variance reduction - .. versionadded:: 0.13.3 + .. versionadded:: 0.13 create_delayed_neutrons : bool Whether delayed neutrons are created in fission. - .. versionadded:: 0.13 + .. versionadded:: 0.13.3 weight_windows_on : bool Whether weight windows are enabled