From e75e91dcb5bc5db1564d0148a076996a8ebb69f1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 8 Jun 2017 20:17:07 -0500 Subject: [PATCH 01/66] Make several subroutines callable in shared library --- CMakeLists.txt | 6 ++++-- src/finalize.F90 | 4 +++- src/initialize.F90 | 18 +++++++++++++----- src/main.F90 | 16 ++++++++++------ src/plot.F90 | 12 +++++++----- src/simulation.F90 | 11 ++++++----- src/volume_calc.F90 | 10 ++++++---- 7 files changed, 49 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19d2d3886b..4e444dc0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,8 @@ endif() # endif() #endif() +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) # Make sure version is sufficient execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion @@ -107,7 +109,7 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) endif() # GCC compiler options - list(APPEND f90flags -cpp -std=f2008 -fbacktrace -O2) + list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2) list(APPEND cflags -cpp -std=c99 -O2) if(debug) list(REMOVE_ITEM f90flags -O2) @@ -344,7 +346,7 @@ set(LIBOPENMC_FORTRAN_SRC src/volume_calc.F90 src/volume_header.F90 src/xml_interface.F90) -add_library(libopenmc STATIC ${LIBOPENMC_FORTRAN_SRC}) +add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC}) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) add_executable(${program} src/main.F90) set_property(TARGET ${program} libopenmc pugixml_fortran diff --git a/src/finalize.F90 b/src/finalize.F90 index 0f1af23560..69a2d0466d 100644 --- a/src/finalize.F90 +++ b/src/finalize.F90 @@ -1,5 +1,7 @@ module finalize + use, intrinsic :: ISO_C_BINDING + use hdf5, only: h5tclose_f, h5close_f use global @@ -15,7 +17,7 @@ contains ! statistics and writing out tallies !=============================================================================== - subroutine openmc_finalize() + subroutine openmc_finalize() bind(C) integer :: hdf5_err diff --git a/src/initialize.F90 b/src/initialize.F90 index e9718fee10..329455e817 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -46,11 +46,19 @@ contains ! setting up timers, etc. !=============================================================================== - subroutine openmc_init(intracomm) -#ifdef MPIF08 - type(MPI_Comm), intent(in) :: intracomm ! MPI intracommunicator -#else + subroutine openmc_init(intracomm) bind(C) integer, intent(in), optional :: intracomm ! MPI intracommunicator + + ! Copy the communicator to a new variable. This is done to avoid changing + ! the signature of this subroutine. +#ifdef MPI +#ifdef MPIF08 + type(MPI_Comm), intent(in) :: comm ! MPI intracommunicator + comm % MPI_VAL = intracomm +#else + integer :: comm + comm = intracomm +#endif #endif ! Start total and initialization timer @@ -59,7 +67,7 @@ contains #ifdef MPI ! Setup MPI - call initialize_mpi(intracomm) + call initialize_mpi(comm) #endif ! Initialize HDF5 interface diff --git a/src/main.F90 b/src/main.F90 index 1cd3a9e547..70a2898e20 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -6,15 +6,19 @@ program main use initialize, only: openmc_init use message_passing use particle_restart, only: run_particle_restart - use plot, only: run_plot - use simulation, only: run_simulation - use volume_calc, only: run_volume_calculations + use plot, only: openmc_plot_geometry + use simulation, only: openmc_run + use volume_calc, only: openmc_calculate_volumes implicit none ! Initialize run -- when run with MPI, pass communicator #ifdef MPI +#ifdef MPIF08 + call openmc_init(MPI_COMM_WORLD % MPI_VAL) +#else call openmc_init(MPI_COMM_WORLD) +#endif #else call openmc_init() #endif @@ -22,13 +26,13 @@ program main ! start problem based on mode select case (run_mode) case (MODE_FIXEDSOURCE, MODE_EIGENVALUE) - call run_simulation() + call openmc_run() case (MODE_PLOTTING) - call run_plot() + call openmc_plot_geometry() case (MODE_PARTICLE) if (master) call run_particle_restart() case (MODE_VOLUME) - call run_volume_calculations() + call openmc_calculate_volumes() end select ! finalize run diff --git a/src/plot.F90 b/src/plot.F90 index 6a3a191703..067d0c56fc 100644 --- a/src/plot.F90 +++ b/src/plot.F90 @@ -1,5 +1,9 @@ module plot + use, intrinsic :: ISO_C_BINDING + + use hdf5 + use constants use error, only: fatal_error use geometry, only: find_cell, check_cell_overlap @@ -14,12 +18,10 @@ module plot use progress_header, only: ProgressBar use string, only: to_str - use hdf5 - implicit none private - public :: run_plot + public :: openmc_plot_geometry integer, parameter :: RED = 1 integer, parameter :: GREEN = 2 @@ -31,7 +33,7 @@ contains ! RUN_PLOT controls the logic for making one or many plots !=============================================================================== - subroutine run_plot() + subroutine openmc_plot_geometry() bind(C) integer :: i ! loop index for plots @@ -51,7 +53,7 @@ contains end associate end do - end subroutine run_plot + end subroutine openmc_plot_geometry !=============================================================================== ! POSITION_RGB computes the red/green/blue values for a given plot with the diff --git a/src/simulation.F90 b/src/simulation.F90 index 0cdb4c66d0..b8aebccc24 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -1,5 +1,7 @@ module simulation + use, intrinsic :: ISO_C_BINDING + use cmfd_execute, only: cmfd_init_batch, execute_cmfd use constants, only: ZERO use eigenvalue, only: count_source_for_ufs, calculate_average_keff, & @@ -22,21 +24,20 @@ module simulation tally_statistics use trigger, only: check_triggers use tracking, only: transport - use volume_calc, only: run_volume_calculations implicit none private - public :: run_simulation + public :: openmc_run contains !=============================================================================== -! RUN_SIMULATION encompasses all the main logic where iterations are performed +! OPENMC_RUN encompasses all the main logic where iterations are performed ! over the batches, generations, and histories in a fixed source or k-eigenvalue ! calculation. !=============================================================================== - subroutine run_simulation() + subroutine openmc_run() bind(C) type(Particle) :: p integer(8) :: i_work @@ -115,7 +116,7 @@ contains ! Clear particle call p % clear() - end subroutine run_simulation + end subroutine openmc_run !=============================================================================== ! INITIALIZE_HISTORY diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index d63955017b..3659293b37 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -1,5 +1,7 @@ module volume_calc + use, intrinsic :: ISO_C_BINDING + use hdf5, only: HID_T #ifdef _OPENMP use omp_lib @@ -21,16 +23,16 @@ module volume_calc implicit none private - public :: run_volume_calculations + public :: openmc_calculate_volumes contains !=============================================================================== -! RUN_VOLUME_CALCULATIONS runs each of the stochastic volume calculations that +! OPENMC_CALCULATE_VOLUMES runs each of the stochastic volume calculations that ! the user has specified and writes results to HDF5 files !=============================================================================== - subroutine run_volume_calculations() + subroutine openmc_calculate_volumes() bind(C) integer :: i, j integer :: n real(8), allocatable :: volume(:,:) ! volume mean/stdev in each domain @@ -93,7 +95,7 @@ contains call write_message("Elapsed time: " // trim(to_str(time_volume % & get_value())) // " s", 6) end if - end subroutine run_volume_calculations + end subroutine openmc_calculate_volumes !=============================================================================== ! GET_VOLUME stochastically determines the volume of a set of domains along with From cd9a17e64f5fb91a9c738944f9b9a6406736982b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 9 Jun 2017 11:58:14 -0500 Subject: [PATCH 02/66] Add openmc.lib object which can directly call the OpenMC library --- CMakeLists.txt | 10 ++++++ openmc/__init__.py | 1 + openmc/libopenmc.py | 80 +++++++++++++++++++++++++++++++++++++++++++++ setup.py | 17 +++++++--- 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 openmc/libopenmc.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e444dc0ef..e3d27b3db7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -385,6 +385,16 @@ endforeach() target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml_fortran faddeeva) target_link_libraries(${program} ${ldflags} libopenmc) +#=============================================================================== +# Python package +#=============================================================================== + +add_custom_command(TARGET libopenmc POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + ${CMAKE_CURRENT_SOURCE_DIR}/openmc/_$ + COMMENT "Copying libopenmc to Python module directory") + #=============================================================================== # Install executable, scripts, manpage, license #=============================================================================== diff --git a/openmc/__init__.py b/openmc/__init__.py index d692ebbae2..2c4829409f 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,5 +27,6 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * +from openmc.libopenmc import lib __version__ = '0.9.0' diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py new file mode 100644 index 0000000000..77841848fc --- /dev/null +++ b/openmc/libopenmc.py @@ -0,0 +1,80 @@ +from ctypes import CDLL, c_int, POINTER, byref +import sys +from warnings import warn + +import pkg_resources + + +class _OpenMCLibrary(object): + def __init__(self, filename): + self._dll = CDLL(filename) + + # Set argument/return types + self._dll.openmc_init.argtypes = [POINTER(c_int)] + self._dll.openmc_init.restype = None + self._dll.openmc_run.restype = None + self._dll.openmc_plot_geometry.restype = None + self._dll.openmc_calculate_volumes.restype = None + self._dll.openmc_finalize.restype = None + + def init(self, intracomm=None): + """Initialize OpenMC + + Parameters + ---------- + intracomm : int or None + MPI intracommunicator + + """ + if intracomm is not None: + # If an mpi4py communicator was passed, convert it to an integer to + # be passed to openmc_init + try: + intracomm = intracomm.py2f() + except AttributeError: + pass + return self._dll.openmc_init(byref(c_int(intracomm))) + else: + return self._dll.openmc_init(None) + + def run(self): + """Run simulation""" + return self._dll.openmc_run() + + def plot_geometry(self): + """Plot geometry""" + return self._dll.openmc_plot_geometry() + + def calculate_volumes(self): + """Run stochastic volume calculation""" + return self._dll.openmc_calculate_volumes() + + def finalize(self): + """Finalize simulation and free memory""" + return self._dll.openmc_finalize() + + def __getattr__(self, key): + # Fall-back for other functions that may be available from library + try: + return getattr(self._dll, 'openmc_{}'.format(key)) + except AttributeError: + raise AttributeError("OpenMC library doesn't have a '{}' function" + .format(key)) + + +# Determine shared-library suffix +if sys.platform == 'darwin': + suffix = 'dylib' +else: + suffix = 'so' + +# Open shared library +filename = pkg_resources.resource_filename( + __name__, '_libopenmc.{}'.format(suffix)) +try: + lib = _OpenMCLibrary(filename) +except OSError: + warn("OpenMC shared library is not available from the Python API. This " + "means you will not be able to use openmc.lib to make in-memory " + "calls to OpenMC.") + lib = None diff --git a/setup.py b/setup.py index e7eb9281ca..67bc734035 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import glob +import sys import numpy as np try: from setuptools import setup @@ -16,6 +17,12 @@ except ImportError: have_cython = False +# Determine shared library suffix +if sys.platform == 'darwin': + suffix = 'dylib' +else: + suffix = 'so' + # Get version information from __init__.py. This is ugly, but more reliable than # using an import. with open('openmc/__init__.py', 'r') as f: @@ -27,6 +34,12 @@ kwargs = {'name': 'openmc', 'openmc.stats'], 'scripts': glob.glob('scripts/openmc-*'), + # Data files and librarries + 'package_data': { + 'openmc': ['_libopenmc.{}'.format(suffix)], + 'openmc.data': ['mass.mas12', 'fission_Q_data_endfb71.h5'] + }, + # Metadata 'author': 'Will Boyd', 'author_email': 'wbinventor@gmail.com', @@ -55,10 +68,6 @@ if have_setuptools: 'validate': ['lxml'] }, - # Data files - 'package_data': { - 'openmc.data': ['mass.mas12', 'fission_Q_data_endfb71.h5'] - }, }) # If Cython is present, add resonance reconstruction capability From 8af7c78b670e18a028c5bf861175a394533a8228 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 9 Jun 2017 14:22:39 -0500 Subject: [PATCH 03/66] Finalize MPI communicator from main() --- src/finalize.F90 | 3 --- src/main.F90 | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/finalize.F90 b/src/finalize.F90 index 69a2d0466d..6164371a0b 100644 --- a/src/finalize.F90 +++ b/src/finalize.F90 @@ -33,9 +33,6 @@ contains #ifdef MPI ! Free all MPI types call MPI_TYPE_FREE(MPI_BANK, mpi_err) - - ! If MPI is in use and enabled, terminate it - call MPI_FINALIZE(mpi_err) #endif end subroutine openmc_finalize diff --git a/src/main.F90 b/src/main.F90 index 70a2898e20..723c87df9f 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -38,4 +38,10 @@ program main ! finalize run call openmc_finalize() +#ifdef MPI + ! If MPI is in use and enabled, terminate it + call MPI_FINALIZE(mpi_err) +#endif + + end program main From 996375608b12c3e0f15b9aaeb318b3400f647d92 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 10 Jun 2017 15:37:31 -0500 Subject: [PATCH 04/66] Don't modify tally results in place when writing tallies.out --- src/output.F90 | 221 +++++++++++++++++++++++---------------------- src/simulation.F90 | 14 +-- src/tally.F90 | 39 -------- 3 files changed, 119 insertions(+), 155 deletions(-) diff --git a/src/output.F90 b/src/output.F90 index 10d4f0511c..93245f1320 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -590,50 +590,53 @@ contains subroutine print_results() + integer :: n ! number of realizations real(8) :: alpha ! significance level for CI - real(8) :: t_value ! t-value for confidence intervals + real(8) :: t_n1 ! t-value with N-1 degrees of freedom + real(8) :: t_n3 ! t-value with N-3 degrees of freedom + real(8) :: x(2) ! mean and standard deviation ! display header block for results call header("Results", 4) + n = n_realizations + if (confidence_intervals) then ! Calculate t-value for confidence intervals alpha = ONE - CONFIDENCE_LEVEL - t_value = t_percentile(ONE - alpha/TWO, n_realizations - 1) - - ! Adjust sum_sq - global_tallies(RESULT_SUM_SQ,:) = t_value * global_tallies(RESULT_SUM_SQ,:) - - ! Adjust combined estimator - if (n_realizations > 3) then - t_value = t_percentile(ONE - alpha/TWO, n_realizations - 3) - k_combined(2) = t_value * k_combined(2) - end if + t_n1 = t_percentile(ONE - alpha/TWO, n - 1) + t_n3 = t_percentile(ONE - alpha/TWO, n - 3) + else + t_n1 = ONE + t_n3 = ONE end if ! write global tallies - if (n_realizations > 1) then - if (run_mode == MODE_EIGENVALUE) then - write(ou,102) "k-effective (Collision)", global_tallies(RESULT_SUM, & - K_COLLISION), global_tallies(RESULT_SUM_SQ, K_COLLISION) - write(ou,102) "k-effective (Track-length)", global_tallies(RESULT_SUM, & - K_TRACKLENGTH), global_tallies(RESULT_SUM_SQ, K_TRACKLENGTH) - write(ou,102) "k-effective (Absorption)", global_tallies(RESULT_SUM, & - K_ABSORPTION), global_tallies(RESULT_SUM_SQ, K_ABSORPTION) - if (n_realizations > 3) write(ou,102) "Combined k-effective", k_combined - end if - write(ou,102) "Leakage Fraction", global_tallies(RESULT_SUM, LEAKAGE), & - global_tallies(RESULT_SUM_SQ, LEAKAGE) + if (n > 1) then + associate (r => global_tallies(RESULT_SUM:RESULT_SUM_SQ, :)) + if (run_mode == MODE_EIGENVALUE) then + x(:) = mean_stdev(r(:, K_COLLISION), n) + write(ou,102) "k-effective (Collision)", x(1), t_n1 * x(2) + x(:) = mean_stdev(r(:, K_TRACKLENGTH), n) + write(ou,102) "k-effective (Track-length)", x(1), t_n1 * x(2) + x(:) = mean_stdev(r(:, K_ABSORPTION), n) + write(ou,102) "k-effective (Absorption)", x(1), t_n1 * x(2) + if (n > 3) write(ou,102) "Combined k-effective", k_combined(1), & + t_n3 * k_combined(2) + end if + x(:) = mean_stdev(global_tallies(:, LEAKAGE), n) + write(ou,102) "Leakage Fraction", x(1), t_n1 * x(2) + end associate else if (master) call warning("Could not compute uncertainties -- only one & &active batch simulated!") if (run_mode == MODE_EIGENVALUE) then - write(ou,103) "k-effective (Collision)", global_tallies(RESULT_SUM, K_COLLISION) - write(ou,103) "k-effective (Track-length)", global_tallies(RESULT_SUM, K_TRACKLENGTH) - write(ou,103) "k-effective (Absorption)", global_tallies(RESULT_SUM, K_ABSORPTION) + write(ou,103) "k-effective (Collision)", global_tallies(RESULT_SUM, K_COLLISION) / n + write(ou,103) "k-effective (Track-length)", global_tallies(RESULT_SUM, K_TRACKLENGTH) / n + write(ou,103) "k-effective (Absorption)", global_tallies(RESULT_SUM, K_ABSORPTION) / n end if - write(ou,103) "Leakage Fraction", global_tallies(RESULT_SUM, LEAKAGE) + write(ou,103) "Leakage Fraction", global_tallies(RESULT_SUM, LEAKAGE) / n end if write(ou,*) @@ -698,8 +701,10 @@ contains integer :: n_order ! loop index for moment orders integer :: nm_order ! loop index for Ynm moment orders integer :: unit_tally ! tallies.out file unit + integer :: nr ! number of realizations real(8) :: t_value ! t-values for confidence intervals real(8) :: alpha ! significance level for CI + real(8) :: x(2) ! mean and standard deviation character(MAX_FILE_LEN) :: filename ! name of output file character(36) :: score_names(N_SCORE_TYPES) ! names of scoring function character(36) :: score_name ! names of scoring function @@ -744,20 +749,20 @@ contains if (confidence_intervals) then alpha = ONE - CONFIDENCE_LEVEL t_value = t_percentile(ONE - alpha/TWO, n_realizations - 1) + else + t_value = ONE end if TALLY_LOOP: do i = 1, n_tallies t => tallies(i) + nr = t % n_realizations if (confidence_intervals) then ! Calculate t-value for confidence intervals - if (confidence_intervals) then - alpha = ONE - CONFIDENCE_LEVEL - t_value = t_percentile(ONE - alpha/TWO, t % n_realizations - 1) - end if - - ! Multiply uncertainty by t-value - t % results(RESULT_SUM_SQ,:,:) = t_value * t % results(RESULT_SUM_SQ,:,:) + alpha = ONE - CONFIDENCE_LEVEL + t_value = t_percentile(ONE - alpha/TWO, nr - 1) + else + t_value = ONE end if ! Write header block @@ -889,24 +894,27 @@ contains do l = 1, t % n_user_score_bins k = k + 1 score_index = score_index + 1 + + associate(r => t % results(RESULT_SUM:RESULT_SUM_SQ, :, :)) + select case(t % score_bins(k)) case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N) score_name = 'P' // trim(to_str(t % moment_order(k))) // " " // & score_names(abs(t % score_bins(k))) + x(:) = mean_stdev(r(:, score_index, filter_index), nr) write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & - repeat(" ", indent), score_name, & - to_str(t % results(RESULT_SUM,score_index,filter_index)), & - trim(to_str(t % results(RESULT_SUM_SQ,score_index,filter_index))) + repeat(" ", indent), score_name, to_str(x(1)), & + trim(to_str(t_value * x(2))) case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) score_index = score_index - 1 do n_order = 0, t % moment_order(k) score_index = score_index + 1 score_name = 'P' // trim(to_str(n_order)) // " " //& score_names(abs(t % score_bins(k))) + x(:) = mean_stdev(r(:, score_index, filter_index), nr) write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & - to_str(t % results(RESULT_SUM,score_index,filter_index)), & - trim(to_str(t % results(RESULT_SUM_SQ,score_index,filter_index))) + to_str(x(1)), trim(to_str(t_value * x(2))) end do k = k + t % moment_order(k) case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, & @@ -918,11 +926,10 @@ contains score_name = 'Y' // trim(to_str(n_order)) // ',' // & trim(to_str(nm_order)) // " " & // score_names(abs(t % score_bins(k))) + x(:) = mean_stdev(r(:, score_index, filter_index), nr) write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & - to_str(t % results(RESULT_SUM,score_index,filter_index)), & - trim(to_str(t % results(RESULT_SUM_SQ,score_index,& - filter_index))) + to_str(x(1)), trim(to_str(t_value * x(2))) end do end do k = k + (t % moment_order(k) + 1)**2 - 1 @@ -932,11 +939,12 @@ contains else score_name = score_names(abs(t % score_bins(k))) end if + x(:) = mean_stdev(r(:, score_index, filter_index), nr) write(UNIT=unit_tally, FMT='(1X,2A,1X,A,"+/- ",A)') & repeat(" ", indent), score_name, & - to_str(t % results(RESULT_SUM,score_index,filter_index)), & - trim(to_str(t % results(RESULT_SUM_SQ,score_index,filter_index))) + to_str(x(1)), trim(to_str(t_value * x(2))) end select + end associate end do indent = indent - 2 @@ -974,11 +982,15 @@ contains integer :: stride_surf ! stride for surface filter integer :: n ! number of incoming energy bins integer :: filter_index ! index in results array for filters + integer :: nr ! number of realizations + real(8) :: x(2) ! mean and standard deviation logical :: print_ebin ! should incoming energy bin be displayed? logical :: energy_filters ! energy filters present character(MAX_LINE_LEN) :: string type(RegularMesh), pointer :: m + nr = t % n_realizations + ! Get pointer to mesh i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) select type(filt => filters(i_filter_mesh) % obj) @@ -1049,104 +1061,101 @@ contains % bins % data(1) - 1) * t % stride(j) end do - ! Left Surface - write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Left", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_LEFT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_LEFT - 1) * stride_surf))) + associate(r => t % results(RESULT_SUM:RESULT_SUM_SQ, :, :)) + ! Left Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_LEFT - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Left", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_LEFT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_LEFT - 1) * stride_surf))) + "Outgoing Current on Left", to_str(x(1)), trim(to_str(x(2))) + + x(:) = mean_stdev(r(:, 1, filter_index + (IN_LEFT - 1) * & + stride_surf), nr) + write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & + "Incoming Current on Left", to_str(x(1)), trim(to_str(x(2))) ! Right Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_RIGHT - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Right", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_RIGHT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_RIGHT - 1) * stride_surf))) + "Outgoing Current on Right", to_str(x(1)), trim(to_str(x(2))) + x(:) = mean_stdev(r(:, 1, filter_index + (IN_RIGHT - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Right", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_RIGHT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_RIGHT - 1) * stride_surf))) + "Incoming Current on Right", to_str(x(1)), trim(to_str(x(2))) if (n_dim >= 2) then ! Back Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_BACK - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Back", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_BACK - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_BACK - 1) * stride_surf))) + "Outgoing Current on Back", to_str(x(1)), trim(to_str(x(2))) + x(:) = mean_stdev(r(:, 1, filter_index + (IN_BACK - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Back", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_BACK - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_BACK - 1) * stride_surf))) + "Incoming Current on Back", to_str(x(1)), trim(to_str(x(2))) ! Front Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_FRONT - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Front", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_FRONT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_FRONT - 1) * stride_surf))) + "Outgoing Current on Front", to_str(x(1)), trim(to_str(x(2))) + x(:) = mean_stdev(r(:, 1, filter_index + (IN_FRONT - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Front", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_FRONT - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_FRONT - 1) * stride_surf))) + "Incoming Current on Front", to_str(x(1)), trim(to_str(x(2))) end if if (n_dim == 3) then ! Bottom Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_BOTTOM - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Bottom", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_BOTTOM - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_BOTTOM - 1) * stride_surf))) + "Outgoing Current on Bottom", to_str(x(1)), trim(to_str(x(2))) + x(:) = mean_stdev(r(:, 1, filter_index + (IN_BOTTOM - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Bottom", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_BOTTOM - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_BOTTOM - 1) * stride_surf))) + "Incoming Current on Bottom", to_str(x(1)), trim(to_str(x(2))) ! Top Surface + x(:) = mean_stdev(r(:, 1, filter_index + (OUT_TOP - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Outgoing Current on Top", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (OUT_TOP - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (OUT_TOP - 1) * stride_surf))) + "Outgoing Current on Top", to_str(x(1)), trim(to_str(x(2))) + x(:) = mean_stdev(r(:, 1, filter_index + (IN_TOP - 1) * & + stride_surf), nr) write(UNIT=unit_tally, FMT='(5X,A,T35,A,"+/- ",A)') & - "Incoming Current on Top", & - to_str(t % results(RESULT_SUM,1,filter_index + & - (IN_TOP - 1) * stride_surf)), & - trim(to_str(t % results(RESULT_SUM_SQ,1,filter_index + & - (IN_TOP - 1) * stride_surf))) + "Incoming Current on Top", to_str(x(1)), trim(to_str(x(2))) end if + end associate end do end do end subroutine write_surface_current +!=============================================================================== +! MEAN_STDEV computes the sample mean and standard deviation of the mean of a +! single tally score +!=============================================================================== + + pure function mean_stdev(result_, n) result(x) + real(8), intent(in) :: result_(2) ! sum and sum-of-squares + integer, intent(in) :: n ! number of realizations + real(8) :: x(2) ! mean and standard deviation + + x(1) = result_(1) / n + if (n > 1) then + x(2) = sqrt((result_(2) / n - x(1)*x(1))/(n - 1)) + else + x(2) = ZERO + end if + end function mean_stdev + end module output diff --git a/src/simulation.F90 b/src/simulation.F90 index b8aebccc24..cb19f918f2 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -20,8 +20,7 @@ module simulation use source, only: initialize_source, sample_external_source use state_point, only: write_state_point, write_source_point use string, only: to_str - use tally, only: synchronize_tallies, setup_active_usertallies, & - tally_statistics + use tally, only: synchronize_tallies, setup_active_usertallies use trigger, only: check_triggers use tracking, only: transport @@ -388,15 +387,10 @@ contains subroutine finalize_simulation ! Start finalization timer - call time_finalize%start() + call time_finalize % start() - ! Calculate statistics for tallies and write to tallies.out - if (master) then - if (n_realizations > 1) call tally_statistics() - end if - if (output_tallies) then - if (master) call write_tallies() - end if + ! Write tally results to tallies.out + if (output_tallies .and. master) call write_tallies() if (check_overlaps) call reduce_overlap_count() ! Stop timers and show timing statistics diff --git a/src/tally.F90 b/src/tally.F90 index 0ee041cbe4..007b65d315 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4366,45 +4366,6 @@ contains end subroutine accumulate_tally -!=============================================================================== -! TALLY_STATISTICS computes the mean and standard deviation of the mean of each -! tally and stores them in the RESULT_SUM and RESULT_SUM_SQ positions -!=============================================================================== - - subroutine tally_statistics() - integer :: i ! index in tallies array - integer :: j, k ! score/filter indices - integer :: n ! number of realizations - - ! Calculate sample mean and standard deviation of the mean -- note that we - ! have used Bessel's correction so that the estimator of the variance of the - ! sample mean is unbiased. - - do i = 1, n_tallies - n = tallies(i) % n_realizations - - associate (r => tallies(i) % results) - do k = 1, size(r, 3) - do j = 1, size(r, 2) - r(RESULT_SUM, j, k) = r(RESULT_SUM, j, k) / n - r(RESULT_SUM_SQ, j, k) = sqrt((r(RESULT_SUM_SQ, j, k)/n - & - r(RESULT_SUM, j, k) * r(RESULT_SUM, j, k))/(n - 1)) - end do - end do - end associate - end do - - ! Calculate statistics for global tallies - n = n_realizations - associate (r => global_tallies) - do j = 1, size(r, 2) - r(RESULT_SUM, j) = r(RESULT_SUM, j) / n - r(RESULT_SUM_SQ, j) = sqrt((r(RESULT_SUM_SQ, j)/n - & - r(RESULT_SUM, j) * r(RESULT_SUM, j))/(n - 1)) - end do - end associate - end subroutine tally_statistics - !=============================================================================== ! SETUP_ACTIVE_USERTALLIES !=============================================================================== From 5cd9bc6065f228330e8f973a971fbd3a11ee1c4d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 10 Jun 2017 16:41:57 -0500 Subject: [PATCH 05/66] Add openmc_reset subroutine --- openmc/libopenmc.py | 5 +++++ src/simulation.F90 | 30 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py index 77841848fc..c5dfc1f2fd 100644 --- a/openmc/libopenmc.py +++ b/openmc/libopenmc.py @@ -16,6 +16,7 @@ class _OpenMCLibrary(object): self._dll.openmc_plot_geometry.restype = None self._dll.openmc_calculate_volumes.restype = None self._dll.openmc_finalize.restype = None + self._dll.openmc_reset.restype = None def init(self, intracomm=None): """Initialize OpenMC @@ -53,6 +54,10 @@ class _OpenMCLibrary(object): """Finalize simulation and free memory""" return self._dll.openmc_finalize() + def reset(self): + """Reset tallies""" + return self._dll.openmc_reset() + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: diff --git a/src/simulation.F90 b/src/simulation.F90 index cb19f918f2..5423037508 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -6,7 +6,8 @@ module simulation use constants, only: ZERO use eigenvalue, only: count_source_for_ufs, calculate_average_keff, & calculate_combined_keff, calculate_generation_keff, & - shannon_entropy, synchronize_bank, keff_generation + shannon_entropy, synchronize_bank, keff_generation, & + k_sum #ifdef _OPENMP use eigenvalue, only: join_bank_from_threads #endif @@ -26,7 +27,7 @@ module simulation implicit none private - public :: openmc_run + public :: openmc_run, openmc_reset contains @@ -422,4 +423,29 @@ contains end subroutine reduce_overlap_count +!=============================================================================== +! OPENMC_RESET resets all tallies +!=============================================================================== + + subroutine openmc_reset() bind(C) + integer :: i + + do i = 1, size(tallies) + tallies(i) % n_realizations = 0 + if (allocated(tallies(i) % results)) then + tallies(i) % results(:, :, :) = ZERO + end if + end do + + n_realizations = 0 + if (allocated(global_tallies)) then + global_tallies(:, :) = ZERO + end if + k_col_abs = ZERO + k_col_tra = ZERO + k_abs_tra = ZERO + k_sum(:) = ZERO + + end subroutine openmc_reset + end module simulation From 71b1c88f5934305be9875ac8474888de36cc1fa8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 10 Jun 2017 16:43:33 -0500 Subject: [PATCH 06/66] Get rid of position variable in tally module --- src/tally.F90 | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/tally.F90 b/src/tally.F90 index 007b65d315..14940551b7 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -22,10 +22,6 @@ module tally implicit none - integer :: position(N_FILTER_TYPES - 3) = 0 ! Tally map positioning array - -!$omp threadprivate(position) - procedure(score_general_), pointer :: score_general => null() procedure(score_analog_tally_), pointer :: score_analog_tally => null() @@ -2410,9 +2406,6 @@ contains ! Reset filter matches flag filter_matches(:) % bins_present = .false. - ! Reset tally map positioning - position = 0 - end subroutine score_analog_tally_ce subroutine score_analog_tally_mg(p) @@ -2558,9 +2551,6 @@ contains ! Reset filter matches flag filter_matches(:) % bins_present = .false. - ! Reset tally map positioning - position = 0 - end subroutine score_analog_tally_mg !=============================================================================== @@ -2956,9 +2946,6 @@ contains ! Reset filter matches flag filter_matches(:) % bins_present = .false. - ! Reset tally map positioning - position = 0 - end subroutine score_tracklength_tally !=============================================================================== @@ -3132,9 +3119,6 @@ contains ! Reset filter matches flag filter_matches(:) % bins_present = .false. - ! Reset tally map positioning - position = 0 - end subroutine score_collision_tally !=============================================================================== From 491309177a950e035fa9fd38187876b1ab01831b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 12 Jun 2017 15:44:06 -0500 Subject: [PATCH 07/66] Expose tally results array via numpy array --- openmc/libopenmc.py | 31 ++++++++++++++++++++++++++++++- src/tally.F90 | 16 ++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py index c5dfc1f2fd..f95f72aac6 100644 --- a/openmc/libopenmc.py +++ b/openmc/libopenmc.py @@ -1,7 +1,10 @@ -from ctypes import CDLL, c_int, POINTER, byref +from ctypes import CDLL, c_int, POINTER, byref, c_double import sys from warnings import warn +import numpy as np +from numpy.ctypeslib import ndpointer, as_array + import pkg_resources @@ -17,6 +20,10 @@ class _OpenMCLibrary(object): self._dll.openmc_calculate_volumes.restype = None self._dll.openmc_finalize.restype = None self._dll.openmc_reset.restype = None + self._dll.openmc_tally_results.argtypes = [ + c_int, POINTER(POINTER(c_double)), ndpointer( + np.intc, shape=(3,))] + self._dll.openmc_tally_results.restype = None def init(self, intracomm=None): """Initialize OpenMC @@ -58,6 +65,28 @@ class _OpenMCLibrary(object): """Reset tallies""" return self._dll.openmc_reset() + def tally_results(self, tally_id): + """Get tally results array + + Parameters + ---------- + tally_id : int + ID of tally + + Returns + ------- + numpy.ndarray + Array that exposes the internal tally results array + + """ + r_p = POINTER(c_double)() + r_shape = np.zeros(3, np.intc) + self._dll.openmc_tally_results(tally_id, byref(r_p), r_shape) + if r_p: + return as_array(r_p, tuple(r_shape[::-1])) + else: + return None + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: diff --git a/src/tally.F90 b/src/tally.F90 index 14940551b7..f2dd3b1079 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4430,4 +4430,20 @@ contains end subroutine setup_active_cmfdtallies + subroutine openmc_tally_results(i, ptr, shape_) bind(C) + integer(C_INT), intent(in), value :: i + type(C_PTR), intent(out) :: ptr + integer(C_INT), intent(out) :: shape_(3) + + ptr = C_NULL_PTR + if (allocated(tallies)) then + if (i >= 1 .and. i <= size(tallies)) then + if (allocated(tallies(i) % results)) then + ptr = C_LOC(tallies(i) % results(1,1,1)) + shape_(:) = shape(tallies(i) % results) + end if + end if + end if + end subroutine openmc_tally_results + end module tally From 8ee65e2aac8728c6dcd4b3800e3736dc543bb172 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 13 Jun 2017 07:31:21 -0500 Subject: [PATCH 08/66] Separate out compile flags for C and check for Clang --- CMakeLists.txt | 56 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3d27b3db7..b7cc1dde3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,59 +110,46 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU) # GCC compiler options list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2) - list(APPEND cflags -cpp -std=c99 -O2) if(debug) list(REMOVE_ITEM f90flags -O2) - list(REMOVE_ITEM cflags -O2) list(APPEND f90flags -g -Wall -pedantic -fbounds-check -ffpe-trap=invalid,overflow,underflow) - list(APPEND cflags -g -Wall -pedantic -fbounds-check) list(APPEND ldflags -g) endif() if(profile) list(APPEND f90flags -pg) - list(APPEND cflags -pg) list(APPEND ldflags -pg) endif() if(optimize) list(REMOVE_ITEM f90flags -O2) - list(REMOVE_ITEM cflags -O2) list(APPEND f90flags -O3) - list(APPEND cflags -O3) endif() if(openmp) list(APPEND f90flags -fopenmp) - list(APPEND cflags -fopenmp) list(APPEND ldflags -fopenmp) endif() if(coverage) list(APPEND f90flags -coverage) - list(APPEND cflags -coverage) list(APPEND ldflags -coverage) endif() elseif(CMAKE_Fortran_COMPILER_ID STREQUAL Intel) # Intel compiler options list(APPEND f90flags -fpp -std08 -assume byterecl -traceback) - list(APPEND cflags -std=c99) if(debug) list(APPEND f90flags -g -warn -ftrapuv -fp-stack-check "-check all" -fpe0 -O0) - list(APPEND cflags -g -w3 -ftrapuv -fp-stack-check -O0) list(APPEND ldflags -g) endif() if(profile) list(APPEND f90flags -pg) - list(APPEND cflags -pg) list(APPEND ldflags -pg) endif() if(optimize) list(APPEND f90flags -O3) - list(APPEND cflags -O3) endif() if(openmp) list(APPEND f90flags -qopenmp) - list(APPEND cflags -qopenmp) list(APPEND ldflags -qopenmp) endif() @@ -214,6 +201,49 @@ elseif(CMAKE_Fortran_COMPILER_ID STREQUAL Cray) endif() +if(CMAKE_C_COMPILER_ID STREQUAL GNU) + # GCC compiler options + list(APPEND cflags -std=c99 -O2) + if(debug) + list(REMOVE_ITEM cflags -O2) + list(APPEND cflags -g -Wall -pedantic -fbounds-check) + endif() + if(profile) + list(APPEND cflags -pg) + endif() + if(optimize) + list(REMOVE_ITEM cflags -O2) + list(APPEND cflags -O3) + endif() + if(coverage) + list(APPEND cflags -coverage) + endif() + +elseif(CMAKE_C_COMPILER_ID STREQUAL Intel) + # Intel compiler options + list(APPEND cflags -std=c99) + if(debug) + list(APPEND cflags -g -w3 -ftrapuv -fp-stack-check -O0) + endif() + if(profile) + list(APPEND cflags -pg) + endif() + if(optimize) + list(APPEND cflags -O3) + endif() + +elseif(CMAKE_C_COMPILER_ID MATCHES Clang) + # Clang options + list(APPEND cflags -std=c99) + if(debug) + list(APPEND cflags -g -O0 -ftrapv) + endif() + if(optimize) + list(APPEND cflags -O3) + endif() + +endif() + # Show flags being used message(STATUS "Fortran flags: ${f90flags}") message(STATUS "C flags: ${cflags}") From bb6adfca2ada46d820f31674c6372df9a5247ec0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 13 Jun 2017 07:36:29 -0500 Subject: [PATCH 09/66] Update required cmake to 2.8.12. Set MACOSX_RPATH. --- CMakeLists.txt | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b7cc1dde3c..b0743bdf1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) project(openmc Fortran C CXX) # Setup output directories @@ -21,6 +21,11 @@ if (${UNIX}) add_definitions(-DUNIX) endif() +# Set MACOSX_RPATH +if(POLICY CMP0042) + cmake_policy(SET CMP0042 NEW) +endif() + #=============================================================================== # Command line options #=============================================================================== @@ -382,28 +387,12 @@ add_executable(${program} src/main.F90) set_property(TARGET ${program} libopenmc pugixml_fortran PROPERTY LINKER_LANGUAGE Fortran) -# target_include_directories was added in CMake 2.8.11 and is the recommended -# way to set include directories. For lesser versions, we revert to set_property -if(CMAKE_VERSION VERSION_LESS 2.8.11) - include_directories(${HDF5_INCLUDE_DIRS}) -else() - target_include_directories(libopenmc PUBLIC ${HDF5_INCLUDE_DIRS}) -endif() +target_include_directories(libopenmc PUBLIC ${HDF5_INCLUDE_DIRS}) -# target_compile_options was added in CMake 2.8.12 and is the recommended way to -# set compile flags. Note that this sets the COMPILE_OPTIONS property (also -# available only in 2.8.12+) rather than the COMPILE_FLAGS property, which is -# deprecated. The former can handle lists whereas the latter cannot. -if (CMAKE_VERSION VERSION_LESS 2.8.12) - string(REPLACE ";" " " f90flags "${f90flags}") - string(REPLACE ";" " " cflags "${cflags}") - set_property(TARGET ${program} PROPERTY COMPILE_FLAGS "${f90flags}") - set_property(TARGET faddeeva PROPERTY COMPILE_FLAGS "${cflags}") -else() - target_compile_options(${program} PUBLIC ${f90flags}) - target_compile_options(libopenmc PUBLIC ${f90flags}) - target_compile_options(faddeeva PRIVATE ${cflags}) -endif() +# set compile flags via target_compile_options +target_compile_options(${program} PUBLIC ${f90flags}) +target_compile_options(libopenmc PUBLIC ${f90flags}) +target_compile_options(faddeeva PRIVATE ${cflags}) # Add HDF5 library directories to link line with -L foreach(LIBDIR ${HDF5_LIBRARY_DIRS}) From 157a45bf5a5877fe1bbcc2ef3f88c4370ac158c5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 12:21:00 -0500 Subject: [PATCH 10/66] Add openmc_cell_set_temperature --- CMakeLists.txt | 1 + openmc/libopenmc.py | 7 +++++ src/capi.F90 | 65 +++++++++++++++++++++++++++++++++++++++++++++ src/simulation.F90 | 27 +------------------ 4 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 src/capi.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index b0743bdf1b..614d4524a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,6 +299,7 @@ set(LIBOPENMC_FORTRAN_SRC src/angle_distribution.F90 src/angleenergy_header.F90 src/bank_header.F90 + src/capi.F90 src/cmfd_data.F90 src/cmfd_execute.F90 src/cmfd_header.F90 diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py index f95f72aac6..031c54564b 100644 --- a/openmc/libopenmc.py +++ b/openmc/libopenmc.py @@ -24,6 +24,9 @@ class _OpenMCLibrary(object): c_int, POINTER(POINTER(c_double)), ndpointer( np.intc, shape=(3,))] self._dll.openmc_tally_results.restype = None + self._dll.openmc_cell_set_temperature.argtypes = [ + c_int, c_double] + self._dll.openmc_cell_set_temperature.restype = c_int def init(self, intracomm=None): """Initialize OpenMC @@ -87,6 +90,10 @@ class _OpenMCLibrary(object): else: return None + def cell_set_temperature(self, cell_id, temperature): + """Set the temperature of a cell""" + return self._dll.openmc_cell_set_temperature(cell_id, temperature) + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: diff --git a/src/capi.F90 b/src/capi.F90 new file mode 100644 index 0000000000..0c7a54c5bf --- /dev/null +++ b/src/capi.F90 @@ -0,0 +1,65 @@ +module openmc_capi + + use, intrinsic :: ISO_C_BINDING + + use constants, only: K_BOLTZMANN + use eigenvalue, only: k_sum + use global + + private + public :: openmc_cell_set_temperature + public :: openmc_reset + +contains + +!=============================================================================== +! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell +!=============================================================================== + + function openmc_cell_set_temperature(id, T) result(err) bind(C) + integer(C_INT), value, intent(in) :: id ! id of cell + real(C_DOUBLE), value, intent(in) :: T + integer(C_INT) :: err + + integer :: i + + err = -1 + if (allocated(cells)) then + if (cell_dict % has_key(id)) then + i = cell_dict % get_key(id) + associate (c => cells(i)) + if (allocated(c % sqrtkT)) then + c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + err = 0 + end if + end associate + end if + end if + end function openmc_cell_set_temperature + +!=============================================================================== +! OPENMC_RESET resets all tallies +!=============================================================================== + + subroutine openmc_reset() bind(C) + integer :: i + + do i = 1, size(tallies) + tallies(i) % n_realizations = 0 + if (allocated(tallies(i) % results)) then + tallies(i) % results(:, :, :) = ZERO + end if + end do + + n_realizations = 0 + if (allocated(global_tallies)) then + global_tallies(:, :) = ZERO + end if + k_col_abs = ZERO + k_col_tra = ZERO + k_abs_tra = ZERO + k_sum(:) = ZERO + + end subroutine openmc_reset + +end module openmc_capi diff --git a/src/simulation.F90 b/src/simulation.F90 index 5423037508..8c4016f967 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -27,7 +27,7 @@ module simulation implicit none private - public :: openmc_run, openmc_reset + public :: openmc_run contains @@ -423,29 +423,4 @@ contains end subroutine reduce_overlap_count -!=============================================================================== -! OPENMC_RESET resets all tallies -!=============================================================================== - - subroutine openmc_reset() bind(C) - integer :: i - - do i = 1, size(tallies) - tallies(i) % n_realizations = 0 - if (allocated(tallies(i) % results)) then - tallies(i) % results(:, :, :) = ZERO - end if - end do - - n_realizations = 0 - if (allocated(global_tallies)) then - global_tallies(:, :) = ZERO - end if - k_col_abs = ZERO - k_col_tra = ZERO - k_abs_tra = ZERO - k_sum(:) = ZERO - - end subroutine openmc_reset - end module simulation From 93af8871d5df716877f3bf22641485bc61239749 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 15:25:56 -0500 Subject: [PATCH 11/66] Add openmc_find function, move openmc_tally_results --- openmc/libopenmc.py | 41 ++++++++++++++++++++++---- src/capi.F90 | 71 +++++++++++++++++++++++++++++++++++++++++++-- src/tally.F90 | 16 ---------- 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py index 031c54564b..c20f015aaf 100644 --- a/openmc/libopenmc.py +++ b/openmc/libopenmc.py @@ -7,26 +7,30 @@ from numpy.ctypeslib import ndpointer, as_array import pkg_resources +_double3 = c_double*3 + class _OpenMCLibrary(object): def __init__(self, filename): self._dll = CDLL(filename) # Set argument/return types + self._dll.openmc_calculate_volumes.restype = None + self._dll.openmc_cell_set_temperature.argtypes = [ + c_int, c_double] + self._dll.openmc_cell_set_temperature.restype = c_int + self._dll.openmc_finalize.restype = None + self._dll.openmc_find.argtypes = [POINTER(_double3), c_int] + self._dll.openmc_find.restype = c_int self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None - self._dll.openmc_run.restype = None self._dll.openmc_plot_geometry.restype = None - self._dll.openmc_calculate_volumes.restype = None - self._dll.openmc_finalize.restype = None + self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None self._dll.openmc_tally_results.argtypes = [ c_int, POINTER(POINTER(c_double)), ndpointer( np.intc, shape=(3,))] self._dll.openmc_tally_results.restype = None - self._dll.openmc_cell_set_temperature.argtypes = [ - c_int, c_double] - self._dll.openmc_cell_set_temperature.restype = c_int def init(self, intracomm=None): """Initialize OpenMC @@ -94,6 +98,31 @@ class _OpenMCLibrary(object): """Set the temperature of a cell""" return self._dll.openmc_cell_set_temperature(cell_id, temperature) + def find(self, xyz, rtype='cell'): + """Find the cell or material at a given point + + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates of position + rtype : {'cell', 'material'} + Whether to return the cell or material ID + + Returns + ------- + int or None + ID of the cell or material. If 'material' is requested and no + material exists at the given coordinate, None is returned. + + """ + if rtype == 'cell': + return self._dll.openmc_find(_double3(*xyz), 1) + elif rtype == 'material': + uid = self._dll.openmc_find(_double3(*xyz), 2) + return uid if uid != 0 else None + else: + raise ValueError('Unknown return type: {}'.format(rtype)) + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: diff --git a/src/capi.F90 b/src/capi.F90 index 0c7a54c5bf..66fbd97bcd 100644 --- a/src/capi.F90 +++ b/src/capi.F90 @@ -2,13 +2,27 @@ module openmc_capi use, intrinsic :: ISO_C_BINDING - use constants, only: K_BOLTZMANN - use eigenvalue, only: k_sum + use constants, only: K_BOLTZMANN + use eigenvalue, only: k_sum + use finalize, only: openmc_finalize + use geometry, only: find_cell use global + use initialize, only: openmc_init + use particle_header, only: Particle + use plot, only: openmc_plot_geometry + use simulation, only: openmc_run + use volume_calc, only: openmc_calculate_volumes private + public :: openmc_calculate_volumes public :: openmc_cell_set_temperature + public :: openmc_finalize + public :: openmc_find + public :: openmc_init + public :: openmc_plot_geometry public :: openmc_reset + public :: openmc_run + public :: openmc_tally_results contains @@ -37,6 +51,37 @@ contains end if end function openmc_cell_set_temperature +!=============================================================================== +! OPENMC_FIND determines the ID or a cell or material at a given point in space +!=============================================================================== + + function openmc_find(xyz, rtype) result(id) bind(C) + real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point + integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material + integer(C_INT) :: id + + logical :: found + type(Particle) :: p + + call p % initialize() + p % coord(1) % xyz(:) = xyz + p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] + call find_cell(p, found) + + id = -1 + if (found) then + if (rtype == 1) then + id = cells(p % coord(p % n_coord) % cell) % id + elseif (rtype == 2) then + if (p % material == MATERIAL_VOID) then + id = 0 + else + id = materials(p % material) % id + end if + end if + end if + end function openmc_find + !=============================================================================== ! OPENMC_RESET resets all tallies !=============================================================================== @@ -62,4 +107,26 @@ contains end subroutine openmc_reset +!=============================================================================== +! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its +! shape. This allows a user to obtain in-memory tally results from Python +! directly. +!=============================================================================== + + subroutine openmc_tally_results(i, ptr, shape_) bind(C) + integer(C_INT), intent(in), value :: i + type(C_PTR), intent(out) :: ptr + integer(C_INT), intent(out) :: shape_(3) + + ptr = C_NULL_PTR + if (allocated(tallies)) then + if (i >= 1 .and. i <= size(tallies)) then + if (allocated(tallies(i) % results)) then + ptr = C_LOC(tallies(i) % results(1,1,1)) + shape_(:) = shape(tallies(i) % results) + end if + end if + end if + end subroutine openmc_tally_results + end module openmc_capi diff --git a/src/tally.F90 b/src/tally.F90 index f2dd3b1079..14940551b7 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4430,20 +4430,4 @@ contains end subroutine setup_active_cmfdtallies - subroutine openmc_tally_results(i, ptr, shape_) bind(C) - integer(C_INT), intent(in), value :: i - type(C_PTR), intent(out) :: ptr - integer(C_INT), intent(out) :: shape_(3) - - ptr = C_NULL_PTR - if (allocated(tallies)) then - if (i >= 1 .and. i <= size(tallies)) then - if (allocated(tallies(i) % results)) then - ptr = C_LOC(tallies(i) % results(1,1,1)) - shape_(:) = shape(tallies(i) % results) - end if - end if - end if - end subroutine openmc_tally_results - end module tally From 7d9c6215b4420e640789d8f5bbb76c84904aaf11 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 15:28:07 -0500 Subject: [PATCH 12/66] Move capi.F90 to api.F90 --- CMakeLists.txt | 2 +- src/{capi.F90 => api.F90} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/{capi.F90 => api.F90} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 614d4524a1..3f8ce98e43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,7 +299,7 @@ set(LIBOPENMC_FORTRAN_SRC src/angle_distribution.F90 src/angleenergy_header.F90 src/bank_header.F90 - src/capi.F90 + src/api.F90 src/cmfd_data.F90 src/cmfd_execute.F90 src/cmfd_header.F90 diff --git a/src/capi.F90 b/src/api.F90 similarity index 98% rename from src/capi.F90 rename to src/api.F90 index 66fbd97bcd..926ba77348 100644 --- a/src/capi.F90 +++ b/src/api.F90 @@ -1,4 +1,4 @@ -module openmc_capi +module openmc_api use, intrinsic :: ISO_C_BINDING @@ -129,4 +129,4 @@ contains end if end subroutine openmc_tally_results -end module openmc_capi +end module openmc_api From fdd9dadf2a9331c39231dad23bfdc256696d4ec4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 15:31:43 -0500 Subject: [PATCH 13/66] Use c_int directly for tally_results shape --- openmc/libopenmc.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openmc/libopenmc.py b/openmc/libopenmc.py index c20f015aaf..d7705b6272 100644 --- a/openmc/libopenmc.py +++ b/openmc/libopenmc.py @@ -3,10 +3,11 @@ import sys from warnings import warn import numpy as np -from numpy.ctypeslib import ndpointer, as_array +from numpy.ctypeslib import as_array import pkg_resources +_int3 = c_int*3 _double3 = c_double*3 @@ -28,8 +29,7 @@ class _OpenMCLibrary(object): self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None self._dll.openmc_tally_results.argtypes = [ - c_int, POINTER(POINTER(c_double)), ndpointer( - np.intc, shape=(3,))] + c_int, POINTER(POINTER(c_double)), POINTER(_int3)] self._dll.openmc_tally_results.restype = None def init(self, intracomm=None): @@ -86,11 +86,11 @@ class _OpenMCLibrary(object): Array that exposes the internal tally results array """ - r_p = POINTER(c_double)() - r_shape = np.zeros(3, np.intc) - self._dll.openmc_tally_results(tally_id, byref(r_p), r_shape) - if r_p: - return as_array(r_p, tuple(r_shape[::-1])) + data = POINTER(c_double)() + shape = _int3() + self._dll.openmc_tally_results(tally_id, byref(data), byref(shape)) + if data: + return as_array(data, tuple(shape[::-1])) else: return None From 000de9ff89287ec637b468a3806feb7b4d6d54e8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 15:32:27 -0500 Subject: [PATCH 14/66] Rename libopenmc.py capi.py --- openmc/__init__.py | 2 +- openmc/{libopenmc.py => capi.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename openmc/{libopenmc.py => capi.py} (100%) diff --git a/openmc/__init__.py b/openmc/__init__.py index 2c4829409f..96bcaf6d23 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,6 +27,6 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * -from openmc.libopenmc import lib +from openmc.capi import lib __version__ = '0.9.0' diff --git a/openmc/libopenmc.py b/openmc/capi.py similarity index 100% rename from openmc/libopenmc.py rename to openmc/capi.py From b9b2c3653f4da9c44e3251f17618619a709ebeb4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 20:46:48 -0500 Subject: [PATCH 15/66] Continue adding C API functions --- openmc/capi.py | 54 ++++++++++++++++++++++++++++++++++- src/api.F90 | 62 +++++++++++++++++++++++++++++++++++++++++ src/material_header.F90 | 40 ++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) diff --git a/openmc/capi.py b/openmc/capi.py index d7705b6272..526ac3ae25 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -9,6 +9,7 @@ import pkg_resources _int3 = c_int*3 _double3 = c_double*3 +_double_array = POINTER(POINTER(c_double)) class _OpenMCLibrary(object): @@ -25,11 +26,19 @@ class _OpenMCLibrary(object): self._dll.openmc_find.restype = c_int self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None + self._dll.openmc_material_get_densities.argtypes = [ + c_int, _double_array] + self._dll.openmc_material_get_densities.restype = c_int + self._dll.openmc_material_set_density.argtypes = [c_int, c_double] + self._dll.openmc_material_set_density.restype = c_int self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None + self._dll.openmc_set_temperature.argtypes = [ + POINTER(_double3), c_double] + self._dll.openmc_set_temperature.restype = c_int self._dll.openmc_tally_results.argtypes = [ - c_int, POINTER(POINTER(c_double)), POINTER(_int3)] + c_int, _double_array, POINTER(_int3)] self._dll.openmc_tally_results.restype = None def init(self, intracomm=None): @@ -123,6 +132,49 @@ class _OpenMCLibrary(object): else: raise ValueError('Unknown return type: {}'.format(rtype)) + def material_get_densities(self, mat_id): + """Get atom densities in a material. + + Parameters + ---------- + mat_id : int + ID of the material + + Returns + ------- + numpy.ndarray + Array of densities in atom/b-cm + + """ + data = POINTER(c_double)() + n = self._dll.openmc_material_get_densities(mat_id, byref(data)) + if data: + return as_array(data, (n,)) + else: + return None + + def material_set_density(self, mat_id, density): + """Set density of a material. + + Parameters + ---------- + mat_id : int + ID of the material + density : float + Density in atom/b-cm + + Returns + ------- + int + Return status (negative if an error occurs). + + """ + return self._dll.openmc_material_set_density(mat_id, density) + + def set_temperature(self, xyz, T): + """Set temperature.""" + return self._dll.openmc_set_temperature(_double3(*xyz), T) + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: diff --git a/src/api.F90 b/src/api.F90 index 926ba77348..ce733e5182 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -19,6 +19,8 @@ module openmc_api public :: openmc_finalize public :: openmc_find public :: openmc_init + public :: openmc_material_get_densities + public :: openmc_material_set_density public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run @@ -26,6 +28,44 @@ module openmc_api contains + function openmc_material_set_density(id, density) result(err) bind(C) + integer(C_INT), value, intent(in) :: id + real(C_DOUBLE), value, intent(in) :: density + integer(C_INT) :: err + + integer :: i + + err = -1 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + err = m % set_density(density, nuclides) + end associate + end if + end if + end function openmc_material_set_density + + function openmc_material_get_densities(id, ptr) result(n) bind(C) + integer(C_INT), intent(in), value :: id + type(C_PTR), intent(out) :: ptr + integer(C_INT) :: n + + ptr = C_NULL_PTR + n = 0 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + if (allocated(m % atom_density)) then + ptr = C_LOC(m % atom_density(1)) + n = size(m % atom_density) + end if + end associate + end if + end if + end function openmc_material_get_densities + !=============================================================================== ! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell !=============================================================================== @@ -107,6 +147,28 @@ contains end subroutine openmc_reset + function openmc_set_temperature(xyz, T) result(err) bind(C) + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in), value :: T + integer(C_INT) :: err + + logical :: found + type(Particle) :: p + + call p % initialize() + p % coord(1) % xyz(:) = xyz + p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] + call find_cell(p, found) + + err = -1 + if (found) then + associate (c => cells(p % coord(p % n_coord) % cell)) + c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + err = 0 + end associate + end if + end function openmc_set_temperature + !=============================================================================== ! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its ! shape. This allows a user to obtain in-memory tally results from Python diff --git a/src/material_header.F90 b/src/material_header.F90 index fdc36548f6..dc03734841 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -1,5 +1,8 @@ module material_header + use constants + use nuclide_header, only: Nuclide + implicit none !=============================================================================== @@ -38,6 +41,43 @@ module material_header ! enforce isotropic scattering in lab logical, allocatable :: p0(:) + contains + procedure :: set_density => material_set_density end type Material +contains + + function material_set_density(m, density, nuclides) result(err) + class(Material), intent(inout) :: m + real(8), intent(in) :: density + type(Nuclide), intent(in) :: nuclides(:) + integer :: err + + integer :: i + real(8) :: sum_percent + real(8) :: awr + + err = -1 + if (allocated(m % atom_density)) then + ! Set total density based on value provided + m % density = density + + ! Determine normalized atom percents + sum_percent = sum(m % atom_density) + m % atom_density(:) = m % atom_density / sum_percent + + ! Recalculate nuclide atom densities based on given density + m % atom_density(:) = density * m % atom_density + + ! Calculate density in g/cm^3. + m % density_gpcc = ZERO + do i = 1, m % n_nuclides + awr = nuclides(m % nuclide(i)) % awr + m % density_gpcc = m % density_gpcc & + + m % atom_density(i) * awr * MASS_NEUTRON / N_AVOGADRO + end do + err = 0 + end if + end function material_set_density + end module material_header From cb6b5bea4a7c342bd1edbc3ecdf7dbf31fd4a4ed Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 20:49:24 -0500 Subject: [PATCH 16/66] Reorder methods in _OpenMCLibrary --- openmc/capi.py | 116 ++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 526ac3ae25..5209cca68e 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -41,72 +41,18 @@ class _OpenMCLibrary(object): c_int, _double_array, POINTER(_int3)] self._dll.openmc_tally_results.restype = None - def init(self, intracomm=None): - """Initialize OpenMC - - Parameters - ---------- - intracomm : int or None - MPI intracommunicator - - """ - if intracomm is not None: - # If an mpi4py communicator was passed, convert it to an integer to - # be passed to openmc_init - try: - intracomm = intracomm.py2f() - except AttributeError: - pass - return self._dll.openmc_init(byref(c_int(intracomm))) - else: - return self._dll.openmc_init(None) - - def run(self): - """Run simulation""" - return self._dll.openmc_run() - - def plot_geometry(self): - """Plot geometry""" - return self._dll.openmc_plot_geometry() - def calculate_volumes(self): """Run stochastic volume calculation""" return self._dll.openmc_calculate_volumes() - def finalize(self): - """Finalize simulation and free memory""" - return self._dll.openmc_finalize() - - def reset(self): - """Reset tallies""" - return self._dll.openmc_reset() - - def tally_results(self, tally_id): - """Get tally results array - - Parameters - ---------- - tally_id : int - ID of tally - - Returns - ------- - numpy.ndarray - Array that exposes the internal tally results array - - """ - data = POINTER(c_double)() - shape = _int3() - self._dll.openmc_tally_results(tally_id, byref(data), byref(shape)) - if data: - return as_array(data, tuple(shape[::-1])) - else: - return None - def cell_set_temperature(self, cell_id, temperature): """Set the temperature of a cell""" return self._dll.openmc_cell_set_temperature(cell_id, temperature) + def finalize(self): + """Finalize simulation and free memory""" + return self._dll.openmc_finalize() + def find(self, xyz, rtype='cell'): """Find the cell or material at a given point @@ -132,6 +78,26 @@ class _OpenMCLibrary(object): else: raise ValueError('Unknown return type: {}'.format(rtype)) + def init(self, intracomm=None): + """Initialize OpenMC + + Parameters + ---------- + intracomm : int or None + MPI intracommunicator + + """ + if intracomm is not None: + # If an mpi4py communicator was passed, convert it to an integer to + # be passed to openmc_init + try: + intracomm = intracomm.py2f() + except AttributeError: + pass + return self._dll.openmc_init(byref(c_int(intracomm))) + else: + return self._dll.openmc_init(None) + def material_get_densities(self, mat_id): """Get atom densities in a material. @@ -171,10 +137,44 @@ class _OpenMCLibrary(object): """ return self._dll.openmc_material_set_density(mat_id, density) + def plot_geometry(self): + """Plot geometry""" + return self._dll.openmc_plot_geometry() + + def reset(self): + """Reset tallies""" + return self._dll.openmc_reset() + + def run(self): + """Run simulation""" + return self._dll.openmc_run() + def set_temperature(self, xyz, T): """Set temperature.""" return self._dll.openmc_set_temperature(_double3(*xyz), T) + def tally_results(self, tally_id): + """Get tally results array + + Parameters + ---------- + tally_id : int + ID of tally + + Returns + ------- + numpy.ndarray + Array that exposes the internal tally results array + + """ + data = POINTER(c_double)() + shape = _int3() + self._dll.openmc_tally_results(tally_id, byref(data), byref(shape)) + if data: + return as_array(data, tuple(shape[::-1])) + else: + return None + def __getattr__(self, key): # Fall-back for other functions that may be available from library try: From e2b8a6c2641fe9126924dff6d449ccb9c8cc28db Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Jun 2017 20:58:04 -0500 Subject: [PATCH 17/66] Start adding C API documentation --- docs/source/capi/index.rst | 34 ++++++++++++++++++++++++++++++++++ docs/source/conf.py | 5 +++-- docs/source/index.rst | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 docs/source/capi/index.rst diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst new file mode 100644 index 0000000000..f3cdbb8402 --- /dev/null +++ b/docs/source/capi/index.rst @@ -0,0 +1,34 @@ +.. _capi: + +===== +C API +===== + +.. c:function:: int openmc_find(double* xyz, int rtype) + + Return the ID of the cell/material containing a given point + + :param xyz: Cartesian coordinates + :type xyz: double[3] + :param rtype: Which ID to return (1=cell, 2=material) + :type rtype: int + :rtype: int + +.. c:function:: void openmc_init(int intracomm) + + Initialize OpenMC + + :param intracomm: MPI intracommunicator + :type intracomm: int + +.. c:function:: void openmc_finalize() + + Finalize a simulation + +.. c:function:: void openmc_reset() + + Resets all tally scores + +.. c:function:: void openmc_run() + + Run a simulation diff --git a/docs/source/conf.py b/docs/source/conf.py index c3434914b3..95930fedba 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,8 +25,9 @@ except ImportError: MOCK_MODULES = ['numpy', 'numpy.polynomial', 'numpy.polynomial.polynomial', - 'h5py', 'pandas', 'uncertainties', 'openmoc', - 'openmc.data.reconstruct'] + 'numpy.ctypeslib', 'scipy', 'scipy.sparse', 'scipy.interpolate', + 'scipy.integrate', 'scipy.optimize', 'scipy.special', 'h5py', + 'pandas', 'uncertainties', 'openmoc', 'openmc.data.reconstruct'] sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES) import numpy as np diff --git a/docs/source/index.rst b/docs/source/index.rst index ce4a5f6f86..00b09db9e7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -36,6 +36,7 @@ free to send a message to the User's Group `mailing list`_. usersguide/index devguide/index pythonapi/index + capi/index io_formats/index publications license From 5e88119325a05c5800a9c4c39d0884532ab8711a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Jun 2017 07:02:06 -0500 Subject: [PATCH 18/66] Add set_density function --- openmc/capi.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- src/api.F90 | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 5209cca68e..92df0c7ae8 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -34,6 +34,8 @@ class _OpenMCLibrary(object): self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None + self._dll.openmc_set_density.argtypes = [POINTER(_double3), c_double] + self._dll.openmc_set_density.restype = c_int self._dll.openmc_set_temperature.argtypes = [ POINTER(_double3), c_double] self._dll.openmc_set_temperature.restype = c_int @@ -45,9 +47,17 @@ class _OpenMCLibrary(object): """Run stochastic volume calculation""" return self._dll.openmc_calculate_volumes() - def cell_set_temperature(self, cell_id, temperature): - """Set the temperature of a cell""" - return self._dll.openmc_cell_set_temperature(cell_id, temperature) + def cell_set_temperature(self, cell_id, T): + """Set the temperature of a cell + + Parameters + ---------- + cell_id : int + ID of the cell + T : float + Temperature in K + """ + return self._dll.openmc_cell_set_temperature(cell_id, T) def finalize(self): """Finalize simulation and free memory""" @@ -149,8 +159,40 @@ class _OpenMCLibrary(object): """Run simulation""" return self._dll.openmc_run() + def set_density(self, xyz, density): + """Set density at a given point. + + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates at position + density : float + Density in atom/b-cm + + Returns + ------- + int + Return status (negative if an error occurs) + + """ + return self._dll.openmc_set_density(_double3(*xyz), density) + def set_temperature(self, xyz, T): - """Set temperature.""" + """Set temperature at a given point. + + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates at position + T : float + Temperature in K + + Returns + ------- + int + Return status (negative if an error occurs) + + """ return self._dll.openmc_set_temperature(_double3(*xyz), T) def tally_results(self, tally_id): diff --git a/src/api.F90 b/src/api.F90 index ce733e5182..901763500e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -24,6 +24,8 @@ module openmc_api public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run + public :: openmc_set_density + public :: openmc_set_temperature public :: openmc_tally_results contains @@ -147,6 +149,37 @@ contains end subroutine openmc_reset +!=============================================================================== +! OPENMC_SET_DENSITY sets the density of a material at a given point +!=============================================================================== + + function openmc_set_density(xyz, density) result(err) bind(C) + real(C_DOUBLE), intent(in) :: xyz(3) + real(C_DOUBLE), intent(in), value :: density + integer(C_INT) :: err + + logical :: found + type(Particle) :: p + + call p % initialize() + p % coord(1) % xyz(:) = xyz + p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] + call find_cell(p, found) + + err = -1 + if (found) then + if (p % material /= MATERIAL_VOID) then + associate (m => materials(p % material)) + err = m % set_density(density, nuclides) + end associate + end if + end if + end function openmc_set_density + +!=============================================================================== +! OPENMC_SET_TEMPERATURE sets the temperature of a cell at a given point +!=============================================================================== + function openmc_set_temperature(xyz, T) result(err) bind(C) real(C_DOUBLE), intent(in) :: xyz(3) real(C_DOUBLE), intent(in), value :: T From c7e4733d290d80d5d7d4e81aebc2b2681b709ee0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Jun 2017 17:38:42 -0500 Subject: [PATCH 19/66] Get rid of overall_gen global variable --- src/eigenvalue.F90 | 25 ++++++++++++++++--------- src/global.F90 | 10 +++++++++- src/output.F90 | 27 ++++++++++++++++++++------- src/simulation.F90 | 36 ++++++++++++++++++------------------ 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 86169d98d4..7d9671e94e 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -371,20 +371,25 @@ contains subroutine calculate_generation_keff() + integer :: i ! overall generation + ! Get keff for this generation by subtracting off the starting value keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - keff_generation + ! Determine overall generation + i = overall_generation() + #ifdef MPI ! Combine values across all processors - call MPI_ALLREDUCE(keff_generation, k_generation(overall_gen), 1, & - MPI_REAL8, MPI_SUM, mpi_intracomm, mpi_err) + call MPI_ALLREDUCE(keff_generation, k_generation(i), 1, MPI_REAL8, & + MPI_SUM, mpi_intracomm, mpi_err) #else - k_generation(overall_gen) = keff_generation + k_generation(i) = keff_generation #endif ! Normalize single batch estimate of k ! TODO: This should be normalized by total_weight, not by n_particles - k_generation(overall_gen) = k_generation(overall_gen) / n_particles + k_generation(i) = k_generation(i) / n_particles end subroutine calculate_generation_keff @@ -396,22 +401,24 @@ contains subroutine calculate_average_keff() + integer :: i ! overall generation within simulation integer :: n ! number of active generations real(8) :: alpha ! significance level for CI real(8) :: t_value ! t-value for confidence intervals - ! Determine number of active generations - n = overall_gen - n_inactive*gen_per_batch + ! Determine overall generation and number of active generations + i = overall_generation() + n = i - n_inactive*gen_per_batch if (n <= 0) then ! For inactive generations, use current generation k as estimate for next ! generation - keff = k_generation(overall_gen) + keff = k_generation(i) else ! Sample mean of keff - k_sum(1) = k_sum(1) + k_generation(overall_gen) - k_sum(2) = k_sum(2) + k_generation(overall_gen)**2 + k_sum(1) = k_sum(1) + k_generation(i) + k_sum(2) = k_sum(2) + k_generation(i)**2 ! Determine mean keff = k_sum(1) / n diff --git a/src/global.F90 b/src/global.F90 index 1d45bcbe8d..b33ca2957d 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -215,7 +215,6 @@ module global integer :: gen_per_batch = 1 ! # of generations per batch integer :: current_batch = 0 ! current batch integer :: current_gen = 0 ! current generation within a batch - integer :: overall_gen = 0 ! overall generation in the run ! ============================================================================ ! TALLY PRECISION TRIGGER VARIABLES @@ -561,4 +560,13 @@ contains end subroutine free_memory +!=============================================================================== +! OVERALL_GENERATION determines the overall generation number +!=============================================================================== + + pure function overall_generation() result(gen) + integer :: gen + gen = gen_per_batch*(current_batch - 1) + current_gen + end function overall_generation + end module global diff --git a/src/output.F90 b/src/output.F90 index 93245f1320..828af717d8 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -366,17 +366,23 @@ contains subroutine print_generation() + integer :: i ! overall generation + integer :: n ! number of active generations + + ! Determine overall generation and number of active generations + i = overall_generation() + n = i - n_inactive*gen_per_batch + ! write out information about batch and generation write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') & trim(to_str(current_batch)) // "/" // trim(to_str(current_gen)) - write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') & - k_generation(overall_gen) + write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') k_generation(i) ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - entropy(overall_gen) + entropy(i) - if (overall_gen - n_inactive*gen_per_batch > 1) then + if (n > 1) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') & keff, keff_std end if @@ -393,18 +399,25 @@ contains subroutine print_batch_keff() + integer :: i ! overall generation + integer :: n ! number of active generations + + ! Determine overall generation and number of active generations + i = overall_generation() + n = i - n_inactive*gen_per_batch + ! write out information batch and option independent output write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') & trim(to_str(current_batch)) // "/" // trim(to_str(gen_per_batch)) write(UNIT=OUTPUT_UNIT, FMT='(3X,F8.5)', ADVANCE='NO') & - k_generation(overall_gen) + k_generation(i) ! write out entropy info if (entropy_on) write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - entropy(current_batch*gen_per_batch) + entropy(i) ! write out accumulated k-effective if after first active batch - if (overall_gen - n_inactive*gen_per_batch > 1) then + if (n > 1) then write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5," +/-",F8.5)', ADVANCE='NO') & keff, keff_std else diff --git a/src/simulation.F90 b/src/simulation.F90 index 8c4016f967..34efaca1e1 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -138,7 +138,7 @@ contains p % id = work_index(rank) + index_source ! set random number seed - particle_seed = (overall_gen - 1)*n_particles + p % id + particle_seed = (overall_generation() - 1)*n_particles + p % id call set_particle_seed(particle_seed) ! set particle trace @@ -203,9 +203,6 @@ contains subroutine initialize_generation() - ! set overall generation number - overall_gen = gen_per_batch*(current_batch - 1) + current_gen - if (run_mode == MODE_EIGENVALUE) then ! Reset number of fission bank sites n_bank = 0 @@ -270,13 +267,20 @@ contains call calculate_average_keff() ! Write generation output - if (master .and. current_gen /= gen_per_batch .and. verbosity >= 7) & - call print_generation() + if (master .and. verbosity >= 7) then + if (current_gen /= gen_per_batch) then + call print_generation() + else + call print_batch_keff() + end if + end if + elseif (run_mode == MODE_FIXEDSOURCE) then ! For fixed-source mode, we need to sample the external source if (path_source == '') then do i = 1, work - call set_particle_seed(overall_gen*n_particles + work_index(rank) + i) + call set_particle_seed(overall_generation()*n_particles + & + work_index(rank) + i) call sample_external_source(source_bank(i)) end do end if @@ -307,9 +311,6 @@ contains ! Perform CMFD calculation if on if (cmfd_on) call execute_cmfd() - ! Display output - if (master .and. verbosity >= 7) call print_batch_keff() - ! Calculate combined estimate of k-effective if (master) call calculate_combined_keff() end if @@ -359,7 +360,6 @@ contains if (run_mode == MODE_EIGENVALUE) then do current_gen = 1, gen_per_batch - overall_gen = overall_gen + 1 call calculate_average_keff() ! print out batch keff @@ -412,13 +412,13 @@ contains subroutine reduce_overlap_count() #ifdef MPI - if (master) then - call MPI_REDUCE(MPI_IN_PLACE, overlap_check_cnt, n_cells, & - MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) - else - call MPI_REDUCE(overlap_check_cnt, overlap_check_cnt, n_cells, & - MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) - end if + if (master) then + call MPI_REDUCE(MPI_IN_PLACE, overlap_check_cnt, n_cells, & + MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) + else + call MPI_REDUCE(overlap_check_cnt, overlap_check_cnt, n_cells, & + MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) + end if #endif end subroutine reduce_overlap_count From 69263f130ff0c1bf48325a411449327d9d93cf7f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Jun 2017 17:56:32 -0500 Subject: [PATCH 20/66] Introduce total_gen global variable to keep track of total number of generations --- src/eigenvalue.F90 | 3 +-- src/global.F90 | 1 + src/particle_restart.F90 | 3 +-- src/simulation.F90 | 9 ++++++--- src/source.F90 | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 7d9671e94e..f370ab38eb 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -95,8 +95,7 @@ contains ! skip ahead in the sequence using the starting index in the 'global' ! fission bank for each processor. - call set_particle_seed(int((current_batch - 1)*gen_per_batch + & - current_gen,8)) + call set_particle_seed(int(total_gen + overall_generation(), 8)) call advance_prn_seed(start) ! Determine how many fission sites we need to sample from the source bank diff --git a/src/global.F90 b/src/global.F90 index b33ca2957d..96df26cf6c 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -215,6 +215,7 @@ module global integer :: gen_per_batch = 1 ! # of generations per batch integer :: current_batch = 0 ! current batch integer :: current_gen = 0 ! current generation within a batch + integer :: total_gen = 0 ! total number of generations simulated ! ============================================================================ ! TALLY PRECISION TRIGGER VARIABLES diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index 262cef8021..ccf546dd9c 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -44,8 +44,7 @@ contains ! Compute random number seed select case (previous_run_mode) case (MODE_EIGENVALUE) - particle_seed = ((current_batch - 1)*gen_per_batch + & - current_gen - 1)*n_particles + p % id + particle_seed = (total_gen + overall_generation() - 1)*n_particles + p % id case (MODE_FIXEDSOURCE) particle_seed = p % id end select diff --git a/src/simulation.F90 b/src/simulation.F90 index 34efaca1e1..1283752f6c 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -138,7 +138,7 @@ contains p % id = work_index(rank) + index_source ! set random number seed - particle_seed = (overall_generation() - 1)*n_particles + p % id + particle_seed = (total_gen + overall_generation() - 1)*n_particles + p % id call set_particle_seed(particle_seed) ! set particle trace @@ -279,8 +279,8 @@ contains ! For fixed-source mode, we need to sample the external source if (path_source == '') then do i = 1, work - call set_particle_seed(overall_generation()*n_particles + & - work_index(rank) + i) + call set_particle_seed((total_gen + overall_generation()) * & + n_particles + work_index(rank) + i) call sample_external_source(source_bank(i)) end do end if @@ -387,6 +387,9 @@ contains subroutine finalize_simulation + ! Increment total number of generations + total_gen = total_gen + n_batches*gen_per_batch + ! Start finalization timer call time_finalize % start() diff --git a/src/source.F90 b/src/source.F90 index 81931245a6..517715b563 100644 --- a/src/source.F90 +++ b/src/source.F90 @@ -73,7 +73,7 @@ contains src => source_bank(i) ! initialize random number seed - id = work_index(rank) + i + id = total_gen*n_particles + work_index(rank) + i call set_particle_seed(id) ! sample external source distribution From 2084be5b3822c768b560c184771f79e9c22f9902 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Jun 2017 22:11:05 -0500 Subject: [PATCH 21/66] Save distributed cell instance in find_cell --- src/api.F90 | 6 +++++- src/geometry.F90 | 3 +++ src/particle_header.F90 | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api.F90 b/src/api.F90 index 901763500e..287aa63c2e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -196,7 +196,11 @@ contains err = -1 if (found) then associate (c => cells(p % coord(p % n_coord) % cell)) - c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + if (size(c % sqrtkT) > 1) then + c % sqrtkT(p % cell_instance) = sqrt(K_BOLTZMANN * T) + else + c % sqrtkT(1) = sqrt(K_BOLTZMANN * T) + end if err = 0 end associate end if diff --git a/src/geometry.F90 b/src/geometry.F90 index f80bd7eb70..0074a053c0 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -277,6 +277,9 @@ contains end if end if end do + + ! Keep track of which instance of the cell the particle is in + p % cell_instance = offset + 1 end if ! Save the material diff --git a/src/particle_header.F90 b/src/particle_header.F90 index a7309393b2..f28e549761 100644 --- a/src/particle_header.F90 +++ b/src/particle_header.F90 @@ -45,6 +45,7 @@ module particle_header ! Particle coordinates integer :: n_coord ! number of current coordinates + integer :: cell_instance ! offset for distributed properties type(LocalCoord) :: coord(MAX_COORD) ! coordinates for all levels ! Energy Data From ebd25603bde7fd1f7aa84708ef24b5253327e665 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 16 Jun 2017 17:23:01 -0500 Subject: [PATCH 22/66] Simplify find_cell logic --- src/geometry.F90 | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index 0074a053c0..d46f7ba207 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -219,6 +219,7 @@ contains n = size(univ % cells) end if + found = .false. CELL_LOOP: do i = 1, n ! select cells based on whether we are searching a universe or a provided ! list of cells (this would be for lists of neighbor cells) @@ -234,16 +235,21 @@ contains c => cells(index_cell) ! Move on to the next cell if the particle is not inside this cell - if (.not. cell_contains(c, p)) cycle + if (cell_contains(c, p)) then + ! Set cell on this level + p % coord(j) % cell = index_cell - ! Set cell on this level - p % coord(j) % cell = index_cell + ! Show cell information on trace + if (verbosity >= 10 .or. trace) then + call write_message(" Entering cell " // trim(to_str(c % id))) + end if - ! Show cell information on trace - if (verbosity >= 10 .or. trace) then - call write_message(" Entering cell " // trim(to_str(c % id))) + found = .true. + exit end if + end do CELL_LOOP + if (found) then CELL_TYPE: if (c % type == FILL_MATERIAL) then ! ====================================================================== ! AT LOWEST UNIVERSE, TERMINATE SEARCH @@ -323,7 +329,6 @@ contains call find_cell(p, found) j = p % n_coord - if (.not. found) exit elseif (c % type == FILL_LATTICE) then CELL_TYPE ! ====================================================================== @@ -369,16 +374,9 @@ contains call find_cell(p, found) j = p % n_coord - if (.not. found) exit end if CELL_TYPE - - ! Found cell so we can return - found = .true. - return - end do CELL_LOOP - - found = .false. + end if end subroutine find_cell From 48b255b559513eb513dce9194c3974c9a9e52027 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 19 Jun 2017 06:54:49 -0500 Subject: [PATCH 23/66] Get rid of pointers in find_cell --- src/geometry.F90 | 261 +++++++++++++++++++++++------------------------ 1 file changed, 129 insertions(+), 132 deletions(-) diff --git a/src/geometry.F90 b/src/geometry.F90 index d46f7ba207..fbbe75a183 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -198,11 +198,9 @@ contains integer :: distribcell_index integer :: i_xyz(3) ! indices in lattice integer :: n ! number of cells to search - integer :: index_cell ! index in cells array + integer :: i_cell ! index in cells array + integer :: i_universe ! index in universes array logical :: use_search_cells ! use cells provided as argument - type(Cell), pointer :: c ! pointer to cell - class(Lattice), pointer :: lat ! pointer to lattice - type(Universe), pointer :: univ ! universe to search in do j = p % n_coord + 1, MAX_COORD call p % coord(j) % reset() @@ -210,13 +208,13 @@ contains j = p % n_coord ! set size of list to search + i_universe = p % coord(j) % universe if (present(search_cells)) then use_search_cells = .true. n = size(search_cells) else use_search_cells = .false. - univ => universes(p % coord(j) % universe) - n = size(univ % cells) + n = size(universes(i_universe) % cells) end if found = .false. @@ -224,24 +222,22 @@ contains ! select cells based on whether we are searching a universe or a provided ! list of cells (this would be for lists of neighbor cells) if (use_search_cells) then - index_cell = search_cells(i) + i_cell = search_cells(i) ! check to make sure search cell is in same universe - if (cells(index_cell) % universe /= p % coord(j) % universe) cycle + if (cells(i_cell) % universe /= i_universe) cycle else - index_cell = univ % cells(i) + i_cell = universes(i_universe) % cells(i) end if - ! get pointer to cell - c => cells(index_cell) - ! Move on to the next cell if the particle is not inside this cell - if (cell_contains(c, p)) then + if (cell_contains(cells(i_cell), p)) then ! Set cell on this level - p % coord(j) % cell = index_cell + p % coord(j) % cell = i_cell ! Show cell information on trace if (verbosity >= 10 .or. trace) then - call write_message(" Entering cell " // trim(to_str(c % id))) + call write_message(" Entering cell " // trim(to_str(& + cells(i_cell) % id))) end if found = .true. @@ -250,132 +246,133 @@ contains end do CELL_LOOP if (found) then - CELL_TYPE: if (c % type == FILL_MATERIAL) then - ! ====================================================================== - ! AT LOWEST UNIVERSE, TERMINATE SEARCH + associate(c => cells(i_cell)) + CELL_TYPE: if (c % type == FILL_MATERIAL) then + ! ====================================================================== + ! AT LOWEST UNIVERSE, TERMINATE SEARCH - ! Save previous material and temperature - p % last_material = p % material - p % last_sqrtkT = p % sqrtkT + ! Save previous material and temperature + p % last_material = p % material + p % last_sqrtkT = p % sqrtkT - ! Get distributed offset - if (size(c % material) > 1 .or. size(c % sqrtkT) > 1) then - ! Distributed instances of this cell have different - ! materials/temperatures. Determine which instance this is for - ! assigning the matching material/temperature. - distribcell_index = c % distribcell_index - offset = 0 - do k = 1, p % n_coord - if (cells(p % coord(k) % cell) % type == FILL_UNIVERSE) then - offset = offset + cells(p % coord(k) % cell) % & - offset(distribcell_index) - elseif (cells(p % coord(k) % cell) % type == FILL_LATTICE) then - if (lattices(p % coord(k + 1) % lattice) % obj & - % are_valid_indices([& - p % coord(k + 1) % lattice_x, & - p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z])) then - offset = offset + lattices(p % coord(k + 1) % lattice) % obj % & - offset(distribcell_index, & + ! Get distributed offset + if (size(c % material) > 1 .or. size(c % sqrtkT) > 1) then + ! Distributed instances of this cell have different + ! materials/temperatures. Determine which instance this is for + ! assigning the matching material/temperature. + distribcell_index = c % distribcell_index + offset = 0 + do k = 1, p % n_coord + if (cells(p % coord(k) % cell) % type == FILL_UNIVERSE) then + offset = offset + cells(p % coord(k) % cell) % & + offset(distribcell_index) + elseif (cells(p % coord(k) % cell) % type == FILL_LATTICE) then + if (lattices(p % coord(k + 1) % lattice) % obj & + % are_valid_indices([& p % coord(k + 1) % lattice_x, & p % coord(k + 1) % lattice_y, & - p % coord(k + 1) % lattice_z) + p % coord(k + 1) % lattice_z])) then + offset = offset + lattices(p % coord(k + 1) % lattice) % obj % & + offset(distribcell_index, & + p % coord(k + 1) % lattice_x, & + p % coord(k + 1) % lattice_y, & + p % coord(k + 1) % lattice_z) + end if + end if + end do + + ! Keep track of which instance of the cell the particle is in + p % cell_instance = offset + 1 + end if + + ! Save the material + if (size(c % material) > 1) then + p % material = c % material(offset + 1) + else + p % material = c % material(1) + end if + + ! Save the temperature + if (size(c % sqrtkT) > 1) then + p % sqrtkT = c % sqrtkT(offset + 1) + else + p % sqrtkT = c % sqrtkT(1) + end if + + elseif (c % type == FILL_UNIVERSE) then CELL_TYPE + ! ====================================================================== + ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL + + ! Store lower level coordinates + p % coord(j + 1) % xyz = p % coord(j) % xyz + p % coord(j + 1) % uvw = p % coord(j) % uvw + + ! Move particle to next level and set universe + j = j + 1 + p % n_coord = j + p % coord(j) % universe = c % fill + + ! Apply translation + if (allocated(c % translation)) then + p % coord(j) % xyz = p % coord(j) % xyz - c % translation + end if + + ! Apply rotation + if (allocated(c % rotation_matrix)) then + p % coord(j) % xyz = matmul(c % rotation_matrix, p % coord(j) % xyz) + p % coord(j) % uvw = matmul(c % rotation_matrix, p % coord(j) % uvw) + p % coord(j) % rotated = .true. + end if + + call find_cell(p, found) + j = p % n_coord + + elseif (c % type == FILL_LATTICE) then CELL_TYPE + ! ====================================================================== + ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL + + associate (lat => lattices(c % fill) % obj) + ! Determine lattice indices + i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw) + + ! Store lower level coordinates + p % coord(j + 1) % xyz = lat % get_local_xyz(p % coord(j) % xyz, i_xyz) + p % coord(j + 1) % uvw = p % coord(j) % uvw + + ! set particle lattice indices + p % coord(j + 1) % lattice = c % fill + p % coord(j + 1) % lattice_x = i_xyz(1) + p % coord(j + 1) % lattice_y = i_xyz(2) + p % coord(j + 1) % lattice_z = i_xyz(3) + + ! Set the next lowest coordinate level. + if (lat % are_valid_indices(i_xyz)) then + ! Particle is inside the lattice. + p % coord(j + 1) % universe = & + lat % universes(i_xyz(1), i_xyz(2), i_xyz(3)) + + else + ! Particle is outside the lattice. + if (lat % outer == NO_OUTER_UNIVERSE) then + call handle_lost_particle(p, "Particle " // trim(to_str(p %id)) & + // " is outside lattice " // trim(to_str(lat % id)) & + // " but the lattice has no defined outer universe.") + return + else + p % coord(j + 1) % universe = lat % outer end if end if - end do + end associate - ! Keep track of which instance of the cell the particle is in - p % cell_instance = offset + 1 - end if + ! Move particle to next level and search for the lower cells. + j = j + 1 + p % n_coord = j - ! Save the material - if (size(c % material) > 1) then - p % material = c % material(offset + 1) - else - p % material = c % material(1) - end if + call find_cell(p, found) + j = p % n_coord - ! Save the temperature - if (size(c % sqrtkT) > 1) then - p % sqrtkT = c % sqrtkT(offset + 1) - else - p % sqrtkT = c % sqrtkT(1) - end if - - elseif (c % type == FILL_UNIVERSE) then CELL_TYPE - ! ====================================================================== - ! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL - - ! Store lower level coordinates - p % coord(j + 1) % xyz = p % coord(j) % xyz - p % coord(j + 1) % uvw = p % coord(j) % uvw - - ! Move particle to next level and set universe - j = j + 1 - p % n_coord = j - p % coord(j) % universe = c % fill - - ! Apply translation - if (allocated(c % translation)) then - p % coord(j) % xyz = p % coord(j) % xyz - c % translation - end if - - ! Apply rotation - if (allocated(c % rotation_matrix)) then - p % coord(j) % xyz = matmul(c % rotation_matrix, p % coord(j) % xyz) - p % coord(j) % uvw = matmul(c % rotation_matrix, p % coord(j) % uvw) - p % coord(j) % rotated = .true. - end if - - call find_cell(p, found) - j = p % n_coord - - elseif (c % type == FILL_LATTICE) then CELL_TYPE - ! ====================================================================== - ! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL - - ! Set current lattice - lat => lattices(c % fill) % obj - - ! Determine lattice indices - i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw) - - ! Store lower level coordinates - p % coord(j + 1) % xyz = lat % get_local_xyz(p % coord(j) % xyz, i_xyz) - p % coord(j + 1) % uvw = p % coord(j) % uvw - - ! set particle lattice indices - p % coord(j + 1) % lattice = c % fill - p % coord(j + 1) % lattice_x = i_xyz(1) - p % coord(j + 1) % lattice_y = i_xyz(2) - p % coord(j + 1) % lattice_z = i_xyz(3) - - ! Set the next lowest coordinate level. - if (lat % are_valid_indices(i_xyz)) then - ! Particle is inside the lattice. - p % coord(j + 1) % universe = & - lat % universes(i_xyz(1), i_xyz(2), i_xyz(3)) - - else - ! Particle is outside the lattice. - if (lat % outer == NO_OUTER_UNIVERSE) then - call handle_lost_particle(p, "Particle " // trim(to_str(p %id)) & - // " is outside lattice " // trim(to_str(lat % id)) & - // " but the lattice has no defined outer universe.") - return - else - p % coord(j + 1) % universe = lat % outer - end if - end if - - ! Move particle to next level and search for the lower cells. - j = j + 1 - p % n_coord = j - - call find_cell(p, found) - j = p % n_coord - - end if CELL_TYPE + end if CELL_TYPE + end associate end if end subroutine find_cell From efe1457589218a4ec97bb1943e66470d7d0e2133 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Jun 2017 10:49:59 -0500 Subject: [PATCH 24/66] Clear active tally lists --- src/api.F90 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/api.F90 b/src/api.F90 index 287aa63c2e..33bb5230d7 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -138,6 +138,7 @@ contains end if end do + ! Reset global tallies n_realizations = 0 if (allocated(global_tallies)) then global_tallies(:, :) = ZERO @@ -147,6 +148,17 @@ contains k_abs_tra = ZERO k_sum(:) = ZERO + ! Turn off tally flags + tallies_on = .false. + active_batches = .false. + + ! Clear active tally lists + call active_analog_tallies % clear() + call active_tracklength_tallies % clear() + call active_current_tallies % clear() + call active_collision_tallies % clear() + call active_tallies % clear() + end subroutine openmc_reset !=============================================================================== From 6fec79d9c2a4b166aa9c5099a630a0d2c4cf4a50 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 22 Jun 2017 14:11:33 -0500 Subject: [PATCH 25/66] Add temperature range user option to load cross sections within a range of temperatures. --- docs/source/io_formats/settings.rst | 11 +++++++++++ openmc/settings.py | 23 +++++++++++++++++------ src/global.F90 | 1 + src/input_xml.F90 | 8 ++++++-- src/nuclide_header.F90 | 18 +++++++++++++++--- src/relaxng/settings.rnc | 2 ++ src/relaxng/settings.rng | 8 ++++++++ src/sab_header.F90 | 16 +++++++++++++++- 8 files changed, 75 insertions(+), 12 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index e9bb5ef535..0aa058cdba 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -661,6 +661,17 @@ methods like "nearest" and "interpolation" in the resolved resonance range. *Default*: False +------------------------------- +```` Element +------------------------------- + +The ```` element specifies a minimum and maximum temperature +in Kelvin above and below which cross sections should be loaded for all nuclides +and thermal scattering tables. This can be used for multi-physics simulations +where the temperatures might change from one iteration to the next. + + *Default*: None + .. _temperature_tolerance: ----------------------------------- diff --git a/openmc/settings.py b/openmc/settings.py index b1ff4c0c65..3efdf1ab7a 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -125,13 +125,16 @@ class Settings(object): temperature : dict Defines a default temperature and method for treating intermediate temperatures at which nuclear data doesn't exist. Accepted keys are - 'default', 'method', 'tolerance', and 'multipole'. The value for - 'default' should be a float representing the default temperature in + 'default', 'method', 'range', 'tolerance', and 'multipole'. The value + for 'default' should be a float representing the default temperature in Kelvin. The value for 'method' should be 'nearest' or 'interpolation'. If the method is 'nearest', 'tolerance' indicates a range of temperature - within which cross sections may be used. 'multipole' is a boolean - indicating whether or not the windowed multipole method should be used - to evaluate resolved resonance cross sections. + within which cross sections may be used. The value for 'range' should be + a pair a minimum and maximum temperatures which are used to indicate + that cross sections be loaded at all temperatures within the + range. 'multipole' is a boolean indicating whether or not the windowed + multipole method should be used to evaluate resolved resonance cross + sections. threads : int Number of OpenMP threads trace : tuple or list @@ -639,7 +642,8 @@ class Settings(object): cv.check_type('temperature settings', temperature, Mapping) for key, value in temperature.items(): cv.check_value('temperature key', key, - ['default', 'method', 'tolerance', 'multipole']) + ['default', 'method', 'tolerance', 'multipole', + 'range']) if key == 'default': cv.check_type('default temperature', value, Real) elif key == 'method': @@ -649,6 +653,11 @@ class Settings(object): cv.check_type('temperature tolerance', value, Real) elif key == 'multipole': cv.check_type('temperature multipole', value, bool) + elif key == 'range': + cv.check_length('temperature range', value, 2) + for T in value: + cv.check_type('temperature', T, Real) + self._temperature = temperature @threads.setter @@ -999,6 +1008,8 @@ class Settings(object): "temperature_{}".format(key)) if isinstance(value, bool): element.text = str(value).lower() + elif key == 'range': + element.text = ' '.join(str(T) for T in value) else: element.text = str(value) diff --git a/src/global.F90 b/src/global.F90 index 96df26cf6c..1574f1f438 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -106,6 +106,7 @@ module global logical :: temperature_multipole = .false. real(8) :: temperature_tolerance = 10.0_8 real(8) :: temperature_default = 293.6_8 + real(8) :: temperature_range(2) = [ZERO, ZERO] ! ============================================================================ ! MULTI-GROUP CROSS SECTION RELATED VARIABLES diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 5ff4d119e6..cc06e3f81c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -951,6 +951,9 @@ contains if (check_for_node(root, "temperature_multipole")) then call get_node_value(root, "temperature_multipole", temperature_multipole) end if + if (check_for_node(root, "temperature_range")) then + call get_node_array(root, "temperature_range", temperature_range) + end if ! Check for tabular_legendre options if (check_for_node(root, "tabular_legendre")) then @@ -5273,7 +5276,8 @@ contains ! Read nuclide data from HDF5 group_id = open_group(file_id, name) call nuclides(i_nuclide) % from_hdf5(group_id, nuc_temps(i_nuclide), & - temperature_method, temperature_tolerance, master) + temperature_method, temperature_tolerance, temperature_range, & + master) call close_group(group_id) call file_close(file_id) @@ -5325,7 +5329,7 @@ contains ! Read S(a,b) data from HDF5 group_id = open_group(file_id, name) call sab_tables(i_sab) % from_hdf5(group_id, sab_temps(i_sab), & - temperature_method, temperature_tolerance) + temperature_method, temperature_tolerance, temperature_range) call close_group(group_id) call file_close(file_id) diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index ea6eab328f..a55e9c8411 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -168,12 +168,13 @@ module nuclide_header end subroutine nuclide_clear subroutine nuclide_from_hdf5(this, group_id, temperature, method, tolerance, & - master) + minmax, master) class(Nuclide), intent(inout) :: this integer(HID_T), intent(in) :: group_id - type(VectorReal), intent(in) :: temperature ! list of desired temperatures + type(VectorReal), intent(in) :: temperature ! list of desired temperatures integer, intent(inout) :: method real(8), intent(in) :: tolerance + real(8), intent(in) :: minmax(2) ! range of temperatures logical, intent(in) :: master ! if this is the master proc integer :: i @@ -233,7 +234,18 @@ module nuclide_header method = TEMPERATURE_NEAREST end if - ! Determine actual temperatures to read + ! Determine actual temperatures to read -- start by checking whether a + ! temperature range was given, in which case all temperatures in the range + ! are loaded irrespective of what temperatures actually appear in the model + if (minmax(2) > ZERO) then + do i = 1, size(temps_available) + temp_actual = temps_available(i) + if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then + call temps_to_read % push_back(nint(temp_actual)) + end if + end do + end if + select case (method) case (TEMPERATURE_NEAREST) ! Find nearest temperatures diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index a0f100cd85..23a0cfd4b7 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -116,6 +116,8 @@ element settings { element temperature_multipole { xsd:boolean }? & + element temperature_range { list { xsd:double, xsd:double } }? & + element temperature_tolerance { xsd:double }? & element threads { xsd:positiveInteger }? & diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng index 2a9a170f9a..e75dc7c15e 100644 --- a/src/relaxng/settings.rng +++ b/src/relaxng/settings.rng @@ -517,6 +517,14 @@ + + + + + + + + diff --git a/src/sab_header.F90 b/src/sab_header.F90 index 1d64711b8e..e0b79f2485 100644 --- a/src/sab_header.F90 +++ b/src/sab_header.F90 @@ -80,12 +80,14 @@ module sab_header contains - subroutine salphabeta_from_hdf5(this, group_id, temperature, method, tolerance) + subroutine salphabeta_from_hdf5(this, group_id, temperature, method, & + tolerance, minmax) class(SAlphaBeta), intent(inout) :: this integer(HID_T), intent(in) :: group_id type(VectorReal), intent(in) :: temperature ! list of temperatures integer, intent(in) :: method real(8), intent(in) :: tolerance + real(8), intent(in) :: minmax(2) integer :: i, j integer :: t @@ -143,6 +145,18 @@ contains end do call sort(temps_available) + ! Determine actual temperatures to read -- start by checking whether a + ! temperature range was given, in which case all temperatures in the range + ! are loaded irrespective of what temperatures actually appear in the model + if (minmax(2) > ZERO) then + do i = 1, size(temps_available) + temp_actual = temps_available(i) + if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then + call temps_to_read % push_back(nint(temp_actual)) + end if + end do + end if + select case (method) case (TEMPERATURE_NEAREST) ! Determine actual temperatures to read From 75af32e01997ed39393b7e9111e06721bd37965b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 28 Jun 2017 22:55:55 -0500 Subject: [PATCH 26/66] Add function to load nuclide cross section at arbitrary point. --- openmc/capi.py | 20 +++++++++++++- src/api.F90 | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/global.F90 | 4 +++ src/input_xml.F90 | 29 ++++++--------------- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 92df0c7ae8..790fa1e20a 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -1,4 +1,4 @@ -from ctypes import CDLL, c_int, POINTER, byref, c_double +from ctypes import CDLL, c_int, POINTER, byref, c_double, c_char_p import sys from warnings import warn @@ -26,6 +26,8 @@ class _OpenMCLibrary(object): self._dll.openmc_find.restype = c_int self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None + self._dll.openmc_load_nuclide.argtypes = [c_char_p] + self._dll.openmc_load_nuclide.restype = c_int self._dll.openmc_material_get_densities.argtypes = [ c_int, _double_array] self._dll.openmc_material_get_densities.restype = c_int @@ -108,6 +110,22 @@ class _OpenMCLibrary(object): else: return self._dll.openmc_init(None) + def load_nuclide(self, name): + """Load cross section data for a nuclide. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + + Returns + ------- + int + Return status (negative if an error occurs). + + """ + return self._dll.openmc_load_nuclide(name.encode()) + def material_get_densities(self, mat_id): """Get atom densities in a material. diff --git a/src/api.F90 b/src/api.F90 index 33bb5230d7..cba92da82b 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -2,12 +2,17 @@ module openmc_api use, intrinsic :: ISO_C_BINDING + use hdf5, only: HID_T + use constants, only: K_BOLTZMANN use eigenvalue, only: k_sum use finalize, only: openmc_finalize use geometry, only: find_cell use global + use hdf5_interface + use message_passing, only: master use initialize, only: openmc_init + use input_xml, only: assign_0K_elastic_scattering, check_data_version use particle_header, only: Particle use plot, only: openmc_plot_geometry use simulation, only: openmc_run @@ -21,6 +26,7 @@ module openmc_api public :: openmc_init public :: openmc_material_get_densities public :: openmc_material_set_density + public :: openmc_load_nuclide public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run @@ -124,6 +130,66 @@ contains end if end function openmc_find +!=============================================================================== +! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library +!=============================================================================== + + function openmc_load_nuclide(name) result(err) bind(C) + character(kind=C_CHAR) :: name(*) + integer(C_INT) :: err + + integer :: i, n + integer(HID_T) :: file_id, group_id + character(10) :: name_ + real(8) :: minmax(2) = [ZERO, INFINITY] + type(VectorReal) :: temperature + type(Nuclide), allocatable :: new_nuclides(:) + + ! Copy array of C_CHARs to normal Fortran string + name_ = '' + i = 1 + do while (name(i) /= C_NULL_CHAR) + name_(i:i) = name(i) + i = i + 1 + end do + + err = -1 + if (.not. nuclide_dict % has_key(to_lower(name_))) then + if (library_dict % has_key(to_lower(name_))) then + ! allocate extra space in nuclides array + n = n_nuclides_total + allocate(new_nuclides(n + 1)) + new_nuclides(1:n) = nuclides(:) + call move_alloc(FROM=new_nuclides, TO=nuclides) + n = n + 1 + + i_library = library_dict % get_key(to_lower(name_)) + + ! Open file and make sure version is sufficient + file_id = file_open(libraries(i_library) % path, 'r') + call check_data_version(file_id) + + ! Read nuclide data from HDF5 + group_id = open_group(file_id, name_) + call nuclides(n) % from_hdf5(group_id, temperature, & + temperature_method, temperature_tolerance, minmax, & + master) + call close_group(group_id) + call file_close(file_id) + + ! Add entry to nuclide dictionary + call nuclide_dict % add_key(to_lower(name_), n) + n_nuclides_total = n + + ! Assign resonant scattering data + if (res_scat_on) call assign_0K_elastic_scattering(nuclides(n)) + + err = 0 + end if + end if + + end function openmc_load_nuclide + !=============================================================================== ! OPENMC_RESET resets all tallies !=============================================================================== diff --git a/src/global.F90 b/src/global.F90 index 1574f1f438..72628f6d02 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -81,6 +81,10 @@ module global ! Dictionaries to look up cross sections and listings type(DictCharInt) :: nuclide_dict + type(DictCharInt) :: library_dict + + ! Cross section libraries + type(Library), allocatable :: libraries(:) ! ============================================================================ ! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES diff --git a/src/input_xml.F90 b/src/input_xml.F90 index cc06e3f81c..09a93d118e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2071,8 +2071,6 @@ contains subroutine read_materials() integer :: i, j - type(DictCharInt) :: library_dict - type(Library), allocatable :: libraries(:) type(VectorReal), allocatable :: nuc_temps(:) ! List of T to read for each nuclide type(VectorReal), allocatable :: sab_temps(:) ! List of T to read for each S(a,b) real(8), allocatable :: material_temps(:) @@ -2161,9 +2159,9 @@ contains ! Now that the cross_sections.xml or mgxs.h5 has been located, read it in if (run_CE) then - call read_ce_cross_sections_xml(libraries) + call read_ce_cross_sections_xml() else - call read_mg_cross_sections_header(libraries) + call read_mg_cross_sections_header() end if ! Creating dictionary that maps the name of the material to the entry @@ -2184,7 +2182,7 @@ contains end if ! Parse data from materials.xml - call read_materials_xml(libraries, library_dict, material_temps) + call read_materials_xml(material_temps) ! Assign temperatures to cells that don't have temperatures already assigned call assign_temperatures(material_temps) @@ -2197,17 +2195,12 @@ contains ! Read continuous-energy cross sections if (run_CE .and. run_mode /= MODE_PLOTTING) then call time_read_xs % start() - call read_ce_cross_sections(libraries, library_dict, nuc_temps, sab_temps) + call read_ce_cross_sections(nuc_temps, sab_temps) call time_read_xs % stop() end if - - ! Clear dictionary - call library_dict % clear() end subroutine read_materials - subroutine read_materials_xml(libraries, library_dict, material_temps) - type(Library), intent(in) :: libraries(:) - type(DictCharInt), intent(inout) :: library_dict + subroutine read_materials_xml(material_temps) real(8), allocatable, intent(out) :: material_temps(:) integer :: i ! loop index for materials @@ -4793,9 +4786,7 @@ contains ! file contains a listing of the CE and MG cross sections that may be used. !=============================================================================== - subroutine read_ce_cross_sections_xml(libraries) - type(Library), allocatable, intent(out) :: libraries(:) - + subroutine read_ce_cross_sections_xml() integer :: i ! loop index integer :: n integer :: n_libraries @@ -4898,9 +4889,7 @@ contains end subroutine read_ce_cross_sections_xml - subroutine read_mg_cross_sections_header(libraries) - type(Library), allocatable, intent(out) :: libraries(:) - + subroutine read_mg_cross_sections_header() integer :: i ! loop index integer :: n_libraries logical :: file_exists ! does mgxs.h5 exist? @@ -5235,9 +5224,7 @@ contains end do end subroutine assign_sab_tables - subroutine read_ce_cross_sections(libraries, library_dict, nuc_temps, sab_temps) - type(Library), intent(in) :: libraries(:) - type(DictCharInt), intent(inout) :: library_dict + subroutine read_ce_cross_sections(nuc_temps, sab_temps) type(VectorReal), intent(in) :: nuc_temps(:) type(VectorReal), intent(in) :: sab_temps(:) From 76f99c2a006fc8f7a674875ed69097c041610929 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 29 Jun 2017 09:58:15 -0500 Subject: [PATCH 27/66] Add material_add_nuclide function to C API (required a little restructuring) --- CMakeLists.txt | 1 - openmc/capi.py | 24 +++++ src/api.F90 | 196 +++++++++++++++++++++++++++++++---------- src/cross_section.F90 | 1 - src/energy_grid.F90 | 67 -------------- src/global.F90 | 3 + src/initialize.F90 | 7 -- src/input_xml.F90 | 11 ++- src/nuclide_header.F90 | 39 ++++++++ src/simulation.F90 | 42 ++++++--- 10 files changed, 252 insertions(+), 139 deletions(-) delete mode 100644 src/energy_grid.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f8ce98e43..6dad4851c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -317,7 +317,6 @@ set(LIBOPENMC_FORTRAN_SRC src/endf.F90 src/endf_header.F90 src/energy_distribution.F90 - src/energy_grid.F90 src/error.F90 src/finalize.F90 src/geometry.F90 diff --git a/openmc/capi.py b/openmc/capi.py index 790fa1e20a..a4ac38c32e 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -28,6 +28,9 @@ class _OpenMCLibrary(object): self._dll.openmc_init.restype = None self._dll.openmc_load_nuclide.argtypes = [c_char_p] self._dll.openmc_load_nuclide.restype = c_int + self._dll.openmc_material_add_nuclide.argtypes = [ + c_int, c_char_p, c_double] + self._dll.openmc_material_add_nuclide.restype = c_int self._dll.openmc_material_get_densities.argtypes = [ c_int, _double_array] self._dll.openmc_material_get_densities.restype = c_int @@ -126,6 +129,27 @@ class _OpenMCLibrary(object): """ return self._dll.openmc_load_nuclide(name.encode()) + def material_add_nuclide(self, mat_id, name, density): + """Add a nuclide to a material. + + Parameters + ---------- + mat_id : int + ID of the material + name : str + Name of nuclide, e.g. 'U235' + density : float + Density in atom/b-cm + + Returns + ------- + int + Return status (negative if an error occurs). + + """ + return self._dll.openmc_material_add_nuclide( + mat_id, name.encode(), density) + def material_get_densities(self, mat_id): """Get atom densities in a material. diff --git a/src/api.F90 b/src/api.F90 index cba92da82b..a7a7e3ab45 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -24,6 +24,7 @@ module openmc_api public :: openmc_finalize public :: openmc_find public :: openmc_init + public :: openmc_material_add_nuclide public :: openmc_material_get_densities public :: openmc_material_set_density public :: openmc_load_nuclide @@ -36,44 +37,6 @@ module openmc_api contains - function openmc_material_set_density(id, density) result(err) bind(C) - integer(C_INT), value, intent(in) :: id - real(C_DOUBLE), value, intent(in) :: density - integer(C_INT) :: err - - integer :: i - - err = -1 - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - err = m % set_density(density, nuclides) - end associate - end if - end if - end function openmc_material_set_density - - function openmc_material_get_densities(id, ptr) result(n) bind(C) - integer(C_INT), intent(in), value :: id - type(C_PTR), intent(out) :: ptr - integer(C_INT) :: n - - ptr = C_NULL_PTR - n = 0 - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - if (allocated(m % atom_density)) then - ptr = C_LOC(m % atom_density(1)) - n = size(m % atom_density) - end if - end associate - end if - end if - end function openmc_material_get_densities - !=============================================================================== ! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell !=============================================================================== @@ -138,20 +101,16 @@ contains character(kind=C_CHAR) :: name(*) integer(C_INT) :: err - integer :: i, n - integer(HID_T) :: file_id, group_id - character(10) :: name_ + integer :: n + integer(HID_T) :: file_id + integer(HID_T) :: group_id + character(:), allocatable :: name_ real(8) :: minmax(2) = [ZERO, INFINITY] type(VectorReal) :: temperature type(Nuclide), allocatable :: new_nuclides(:) ! Copy array of C_CHARs to normal Fortran string - name_ = '' - i = 1 - do while (name(i) /= C_NULL_CHAR) - name_(i:i) = name(i) - i = i + 1 - end do + name_ = to_f_string(name) err = -1 if (.not. nuclide_dict % has_key(to_lower(name_))) then @@ -184,12 +143,136 @@ contains ! Assign resonant scattering data if (res_scat_on) call assign_0K_elastic_scattering(nuclides(n)) + ! Initialize nuclide grid + call nuclides(n) % init_grid(energy_min_neutron, & + energy_max_neutron, n_log_bins) + err = 0 + else + err = -2 end if end if end function openmc_load_nuclide +!=============================================================================== +! OPENMC_MATERIAL_ADD_NUCLIDE +!=============================================================================== + + function openmc_material_add_nuclide(id, name, density) result(err) bind(C) + integer(C_INT), value, intent(in) :: id + character(kind=C_CHAR) :: name(*) + real(C_DOUBLE), value, intent(in) :: density + integer(C_INT) :: err + + integer :: i, j, k, n + integer :: err2 + real(8) :: awr + integer, allocatable :: new_nuclide(:) + real(8), allocatable :: new_density(:) + character(:), allocatable :: name_ + + name_ = to_f_string(name) + + err = -1 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + ! Check if nuclide is already in material + do j = 1, size(m % nuclide) + k = m % nuclide(j) + if (nuclides(k) % name == name_) then + awr = nuclides(k) % awr + m % density = m % density + density - m % atom_density(j) + m % density_gpcc = m % density_gpcc + (density - & + m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO + m % atom_density(j) = density + err = 0 + end if + end do + + ! If nuclide wasn't found, extend nuclide/density arrays + if (err /= 0) then + ! If nuclide hasn't been loaded, load it now + err2 = openmc_load_nuclide(name) + + if (err2 /= -2) then + ! Extend arrays + n = size(m % nuclide) + allocate(new_nuclide(n + 1)) + new_nuclide(1:n) = m % nuclide + call move_alloc(FROM=new_nuclide, TO=m % nuclide) + + allocate(new_density(n + 1)) + new_density(1:n) = m % atom_density + call move_alloc(FROM=new_density, TO=m % atom_density) + + ! Append new nuclide/density + k = nuclide_dict % get_key(to_lower(name_)) + m % nuclide(n + 1) = k + m % atom_density(n + 1) = density + m % density = m % density + density + m % density_gpcc = m % density_gpcc + & + density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO + m % n_nuclides = n + 1 + + err = 0 + end if + end if + end associate + end if + end if + + end function openmc_material_add_nuclide + +!=============================================================================== +! OPENMC_MATERIAL_GET_DENSITIES returns an array of nuclide densities in a +! material +!=============================================================================== + + function openmc_material_get_densities(id, ptr) result(n) bind(C) + integer(C_INT), intent(in), value :: id + type(C_PTR), intent(out) :: ptr + integer(C_INT) :: n + + ptr = C_NULL_PTR + n = 0 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + if (allocated(m % atom_density)) then + ptr = C_LOC(m % atom_density(1)) + n = size(m % atom_density) + end if + end associate + end if + end if + end function openmc_material_get_densities + +!=============================================================================== +! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm +!=============================================================================== + + function openmc_material_set_density(id, density) result(err) bind(C) + integer(C_INT), value, intent(in) :: id + real(C_DOUBLE), value, intent(in) :: density + integer(C_INT) :: err + + integer :: i + + err = -1 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + err = m % set_density(density, nuclides) + end associate + end if + end if + end function openmc_material_set_density + !=============================================================================== ! OPENMC_RESET resets all tallies !=============================================================================== @@ -306,4 +389,23 @@ contains end if end subroutine openmc_tally_results + function to_f_string(c_string) result(f_string) + character(kind=C_CHAR), intent(in) :: c_string(*) + character(:), allocatable :: f_string + + integer :: i, n + + ! Determine length of original string + n = 0 + do while (c_string(n + 1) /= C_NULL_CHAR) + n = n + 1 + end do + + ! Copy C string character by character + allocate(character(len=n) :: f_string) + do i = 1, n + f_string(i:i) = c_string(i) + end do + end function to_f_string + end module openmc_api diff --git a/src/cross_section.F90 b/src/cross_section.F90 index ce7d971333..28014f773a 100644 --- a/src/cross_section.F90 +++ b/src/cross_section.F90 @@ -2,7 +2,6 @@ module cross_section use algorithm, only: binary_search use constants - use energy_grid, only: log_spacing use error, only: fatal_error use global use list_header, only: ListElemInt diff --git a/src/energy_grid.F90 b/src/energy_grid.F90 deleted file mode 100644 index 47408943e3..0000000000 --- a/src/energy_grid.F90 +++ /dev/null @@ -1,67 +0,0 @@ -module energy_grid - - use global - use list_header, only: ListReal - use output, only: write_message - - - implicit none - - integer :: grid_method ! how to treat the energy grid - integer :: n_log_bins ! number of bins for logarithmic grid - real(8) :: log_spacing ! spacing on logarithmic grid - -contains - -!=============================================================================== -! LOGARITHMIC_GRID determines a logarithmic mapping for energies to bounding -! indices on a nuclide energy grid -!=============================================================================== - - subroutine logarithmic_grid() - integer :: i, j, k ! Loop indices - integer :: t ! temperature index - integer :: M ! Number of equally log-spaced bins - real(8) :: E_max ! Maximum energy in MeV - real(8) :: E_min ! Minimum energy in MeV - real(8), allocatable :: umesh(:) ! Equally log-spaced energy grid - - ! Set minimum/maximum energies - E_max = energy_max_neutron - E_min = energy_min_neutron - - ! Determine equal-logarithmic energy spacing - M = n_log_bins - log_spacing = log(E_max/E_min)/M - - ! Create equally log-spaced energy grid - allocate(umesh(0:M)) - umesh(:) = [(i*log_spacing, i=0, M)] - - do i = 1, n_nuclides_total - associate (nuc => nuclides(i)) - do t = 1, size(nuc % grid) - ! Allocate logarithmic mapping for nuclide - allocate(nuc % grid(t) % grid_index(0:M)) - - ! Determine corresponding indices in nuclide grid to energies on - ! equal-logarithmic grid - j = 1 - do k = 0, M - do while (log(nuc % grid(t) % energy(j + 1)/E_min) <= umesh(k)) - ! Ensure that for isotopes where maxval(nuc % energy) << E_max - ! that there are no out-of-bounds issues. - if (j + 1 == size(nuc % grid(t) % energy)) exit - j = j + 1 - end do - nuc % grid(t) % grid_index(k) = j - end do - end do - end associate - end do - - deallocate(umesh) - - end subroutine logarithmic_grid - -end module energy_grid diff --git a/src/global.F90 b/src/global.F90 index 72628f6d02..2690ed1518 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -112,6 +112,9 @@ module global real(8) :: temperature_default = 293.6_8 real(8) :: temperature_range(2) = [ZERO, ZERO] + integer :: n_log_bins ! number of bins for logarithmic grid + real(8) :: log_spacing ! spacing on logarithmic grid + ! ============================================================================ ! MULTI-GROUP CROSS SECTION RELATED VARIABLES diff --git a/src/initialize.F90 b/src/initialize.F90 index 329455e817..98f02863e2 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -11,7 +11,6 @@ module initialize use constants use dict_header, only: DictIntInt, ElemKeyValueII use set_header, only: SetInt - use energy_grid, only: logarithmic_grid, grid_method use error, only: fatal_error, warning use geometry, only: neighbor_lists, count_instance, calc_offsets, & maximum_levels @@ -109,12 +108,6 @@ contains end if if (run_mode /= MODE_PLOTTING) then - ! Construct information needed for nuclear data - if (run_CE) then - ! Construct log energy grid for cross-sections - call logarithmic_grid() - end if - ! Allocate and setup tally stride, filter_matches, and tally maps call configure_tallies() diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 09a93d118e..597b4f4c0e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -7,7 +7,6 @@ module input_xml use distribution_multivariate use distribution_univariate use endf, only: reaction_name - use energy_grid, only: grid_method, n_log_bins use error, only: fatal_error, warning use geometry_header, only: Cell, Lattice, RectLattice, HexLattice, & get_temperatures, root_universe @@ -5240,9 +5239,6 @@ contains allocate(nuclides(n_nuclides_total)) allocate(sab_tables(n_sab_tables)) -!$omp parallel - allocate(micro_xs(n_nuclides_total)) -!$omp end parallel ! Read cross sections do i = 1, size(materials) @@ -5294,6 +5290,13 @@ contains end do end do + ! Set up logarithmic grid for nuclides + do i = 1, size(nuclides) + call nuclides(i) % init_grid(energy_min_neutron, & + energy_max_neutron, n_log_bins) + end do + log_spacing = log(energy_max_neutron/energy_min_neutron) / n_log_bins + do i = 1, size(materials) ! Skip materials with no S(a,b) tables if (.not. allocated(materials(i) % sab_names)) cycle diff --git a/src/nuclide_header.F90 b/src/nuclide_header.F90 index a55e9c8411..c03ebee1a7 100644 --- a/src/nuclide_header.F90 +++ b/src/nuclide_header.F90 @@ -97,6 +97,7 @@ module nuclide_header contains procedure :: clear => nuclide_clear procedure :: from_hdf5 => nuclide_from_hdf5 + procedure :: init_grid => nuclide_init_grid procedure :: nu => nuclide_nu procedure, private :: create_derived => nuclide_create_derived end type Nuclide @@ -683,4 +684,42 @@ module nuclide_header end function nuclide_nu + subroutine nuclide_init_grid(this, E_min, E_max, M) + class(Nuclide), intent(inout) :: this + real(8), intent(in) :: E_min ! Minimum energy in MeV + real(8), intent(in) :: E_max ! Maximum energy in MeV + integer, intent(in) :: M ! Number of equally log-spaced bins + + integer :: i, j, k ! Loop indices + integer :: t ! temperature index + real(8) :: spacing + real(8), allocatable :: umesh(:) ! Equally log-spaced energy grid + + ! Determine equal-logarithmic energy spacing + spacing = log(E_max/E_min)/M + + ! Create equally log-spaced energy grid + allocate(umesh(0:M)) + umesh(:) = [(i*spacing, i=0, M)] + + do t = 1, size(this % grid) + ! Allocate logarithmic mapping for nuclide + allocate(this % grid(t) % grid_index(0:M)) + + ! Determine corresponding indices in nuclide grid to energies on + ! equal-logarithmic grid + j = 1 + do k = 0, M + do while (log(this % grid(t) % energy(j + 1)/E_min) <= umesh(k)) + ! Ensure that for isotopes where maxval(this % energy) << E_max + ! that there are no out-of-bounds issues. + if (j + 1 == size(this % grid(t) % energy)) exit + j = j + 1 + end do + this % grid(t) % grid_index(k) = j + end do + end do + + end subroutine nuclide_init_grid + end module nuclide_header diff --git a/src/simulation.F90 b/src/simulation.F90 index 1283752f6c..87a17452d9 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -42,17 +42,7 @@ contains type(Particle) :: p integer(8) :: i_work - if (.not. restart_run) call initialize_source() - - ! Display header - if (master) then - if (run_mode == MODE_FIXEDSOURCE) then - call header("FIXED SOURCE TRANSPORT SIMULATION", 3) - elseif (run_mode == MODE_EIGENVALUE) then - call header("K EIGENVALUE SIMULATION", 3) - if (verbosity >= 7) call print_columns() - end if - end if + call initialize_simulation() ! Turn on inactive timer call time_inactive % start() @@ -380,12 +370,40 @@ contains end subroutine replay_batch_history +!=============================================================================== +! INITIALIZE_SIMULATION +!=============================================================================== + + subroutine initialize_simulation() + +!$omp parallel + allocate(micro_xs(n_nuclides_total)) +!$omp end parallel + + if (.not. restart_run) call initialize_source() + + ! Display header + if (master) then + if (run_mode == MODE_FIXEDSOURCE) then + call header("FIXED SOURCE TRANSPORT SIMULATION", 3) + elseif (run_mode == MODE_EIGENVALUE) then + call header("K EIGENVALUE SIMULATION", 3) + if (verbosity >= 7) call print_columns() + end if + end if + + end subroutine initialize_simulation + !=============================================================================== ! FINALIZE_SIMULATION calculates tally statistics, writes tallies, and displays ! execution time and results !=============================================================================== - subroutine finalize_simulation + subroutine finalize_simulation() + +!$omp parallel + deallocate(micro_xs) +!$omp end parallel ! Increment total number of generations total_gen = total_gen + n_batches*gen_per_batch From 68070b12ca3f6a0e5ec3354d1600388afcf5ea10 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Jul 2017 15:13:27 -0500 Subject: [PATCH 28/66] Fix allocation of micro_xs in a few places --- src/mgxs_data.F90 | 3 --- src/particle_restart.F90 | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mgxs_data.F90 b/src/mgxs_data.F90 index a43d45c0e1..ad20aca83b 100644 --- a/src/mgxs_data.F90 +++ b/src/mgxs_data.F90 @@ -73,9 +73,6 @@ contains ! allocate arrays for MGXS storage and cross section cache allocate(nuclides_MG(n_nuclides_total)) -!$omp parallel - allocate(micro_xs(n_nuclides_total)) -!$omp end parallel ! ========================================================================== ! READ ALL MGXS CROSS SECTION TABLES diff --git a/src/particle_restart.F90 b/src/particle_restart.F90 index ccf546dd9c..4438c48c00 100644 --- a/src/particle_restart.F90 +++ b/src/particle_restart.F90 @@ -32,6 +32,8 @@ contains ! Set verbosity high verbosity = 10 + allocate(micro_xs(n_nuclides_total)) + ! Initialize the particle to be tracked call p % initialize() @@ -57,6 +59,8 @@ contains ! Write output if particle made it call print_particle(p) + deallocate(micro_xs) + end subroutine run_particle_restart !=============================================================================== From 9d456c3ff1f4b72f0c4b1dabf6d5cbf4eaa2b4b7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Jul 2017 14:33:18 -0500 Subject: [PATCH 29/66] Modify openmc_find to return instance as well. Add C API docs --- docs/source/capi/index.rst | 112 ++++++++++++++++++++++++++++++-- docs/source/pythonapi/capi.rst | 10 +++ docs/source/pythonapi/index.rst | 1 + openmc/__init__.py | 2 +- openmc/capi.py | 38 +++++++++-- src/api.F90 | 11 ++-- 6 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 docs/source/pythonapi/capi.rst diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index f3cdbb8402..ae072448df 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -4,15 +4,39 @@ C API ===== -.. c:function:: int openmc_find(double* xyz, int rtype) +.. c:function:: void openmc_calculate_voumes() - Return the ID of the cell/material containing a given point + Run a stochastic volume calculation + +.. c:function:: int openmc_cell_set_temperature(int id, double T) + + Set the temperature of a cell. + + :param id: ID of the cell + :type id: int + :param T: Temperature in Kelvin + :type T: double + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: void openmc_finalize() + + Finalize a simulation + +.. c:function:: void openmc_find(double* xyz, int rtype, int* id, int* instance) + + Determine the ID of the cell/material containing a given point :param xyz: Cartesian coordinates :type xyz: double[3] :param rtype: Which ID to return (1=cell, 2=material) :type rtype: int - :rtype: int + :param id: ID of the cell/material found. If a material is requested and the + point is in a void, the ID is 0. If an error occurs, the ID is -1. + :type id: int + :param instance: If a cell is repetaed in the geometry, the instance of the + cell that was found and zero otherwise. + :type instance: int .. c:function:: void openmc_init(int intracomm) @@ -21,9 +45,54 @@ C API :param intracomm: MPI intracommunicator :type intracomm: int -.. c:function:: void openmc_finalize() +.. c:function:: int openmc_load_nuclide(char name[]) - Finalize a simulation + Load data for a nuclide from the HDF5 data library. + + :param name: Name of the nuclide. + :type name: char[] + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_material_add_nuclide(int id, char name[], double density) + + Add a nuclide to an existing material. If the nuclide already exists, the + density is overwritten. + + :param id: ID of the material + :type id: int + :param name: Name of the nuclide + :type name: char[] + :param density: Density in atom/b-cm + :type density: double + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_material_get_densities(int id, double* ptr) + + Get an array of nuclide densities for a material. + + :param id: ID of the material + :type id: int + :param ptr: Pointer to the array of densities + :type ptr: double* + :return: Length of the array + :rtype: int + +.. c:function:: int openmc_material_set_density(int id, double density) + + Set the density of a material. + + :param id: ID of the material + :type id: int + :param density: Density of the material in atom/b-cm + :type density: double + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: void openmc_plot_geometry() + + Run plotting mode. .. c:function:: void openmc_reset() @@ -32,3 +101,36 @@ C API .. c:function:: void openmc_run() Run a simulation + +.. c:function:: int openmc_set_density(double xyz[3], double density) + + Set the density of a material at a given point. + + :param xyz: Cartesian coordinates + :type xyz: double[3] + :param density: Density of the material to set in atom/b-cm + :type density: double + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_set_temperature(double xyz[3], double T) + + Set the density of a cell at a given point. + + :param xyz: Cartesian coordinates + :type xyz: double[3] + :param T: Temperature of the cell to set in Kelvin + :type T: double + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: void openmc_tally_results(int i, double** ptr, int shape_[3]) + + Get a pointer to tally results array. + + :param i: Index in the tallies array + :type i: int + :param ptr: Pointer to the results array + :type ptr: double** + :param shape_: Shape of the results array + :type shape_: int[3] diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst new file mode 100644 index 0000000000..c78c43fd34 --- /dev/null +++ b/docs/source/pythonapi/capi.rst @@ -0,0 +1,10 @@ +--------------------------------------------------- +:data:`openmc.capi` -- Python bindings to the C API +--------------------------------------------------- + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.capi.OpenMCLibrary diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 5492f362df..bb99dd470f 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -47,5 +47,6 @@ Modules mgxs model data + capi examples openmoc diff --git a/openmc/__init__.py b/openmc/__init__.py index 96bcaf6d23..74c991fda4 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,6 +27,6 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * -from openmc.capi import lib +from openmc.capi import lib, OpenMCLibrary __version__ = '0.9.0' diff --git a/openmc/capi.py b/openmc/capi.py index a4ac38c32e..2df55abce3 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -12,7 +12,20 @@ _double3 = c_double*3 _double_array = POINTER(POINTER(c_double)) -class _OpenMCLibrary(object): +class OpenMCLibrary(object): + """Provides bindings to C functions defined by OpenMC shared library. + + This class is normally not directly instantiated. Instead, when the + :mod:`openmc` package is imported, an instance is automatically created with + the name :data:`openmc.lib`. Calls to the OpenMC can then be made using that + instance, for example: + + .. code-block:: python + + openmc.lib.init() + openmc.lib.run() + + """ def __init__(self, filename): self._dll = CDLL(filename) @@ -22,8 +35,9 @@ class _OpenMCLibrary(object): c_int, c_double] self._dll.openmc_cell_set_temperature.restype = c_int self._dll.openmc_finalize.restype = None - self._dll.openmc_find.argtypes = [POINTER(_double3), c_int] - self._dll.openmc_find.restype = c_int + self._dll.openmc_find.argtypes = [ + POINTER(_double3), c_int, POINTER(c_int), POINTER(c_int)] + self._dll.openmc_find.restype = None self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None self._dll.openmc_load_nuclide.argtypes = [c_char_p] @@ -61,6 +75,7 @@ class _OpenMCLibrary(object): ID of the cell T : float Temperature in K + """ return self._dll.openmc_cell_set_temperature(cell_id, T) @@ -83,16 +98,25 @@ class _OpenMCLibrary(object): int or None ID of the cell or material. If 'material' is requested and no material exists at the given coordinate, None is returned. + int + If the cell at the given point is repeated in the geometry, this + indicates which instance it is, i.e., 0 would be the first instance. """ + # Set second argument to openmc_find if rtype == 'cell': - return self._dll.openmc_find(_double3(*xyz), 1) + r_int = 1 elif rtype == 'material': - uid = self._dll.openmc_find(_double3(*xyz), 2) - return uid if uid != 0 else None + r_int = 2 else: raise ValueError('Unknown return type: {}'.format(rtype)) + # Call openmc_find + uid = c_int() + instance = c_int() + self._dll.openmc_find(_double3(*xyz), r_int, byref(uid), byref(instance)) + return (uid.value if uid != 0 else None), instance.value + def init(self, intracomm=None): """Initialize OpenMC @@ -278,7 +302,7 @@ else: filename = pkg_resources.resource_filename( __name__, '_libopenmc.{}'.format(suffix)) try: - lib = _OpenMCLibrary(filename) + lib = OpenMCLibrary(filename) except OSError: warn("OpenMC shared library is not available from the Python API. This " "means you will not be able to use openmc.lib to make in-memory " diff --git a/src/api.F90 b/src/api.F90 index a7a7e3ab45..c0ee84594a 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -24,10 +24,10 @@ module openmc_api public :: openmc_finalize public :: openmc_find public :: openmc_init + public :: openmc_load_nuclide public :: openmc_material_add_nuclide public :: openmc_material_get_densities public :: openmc_material_set_density - public :: openmc_load_nuclide public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run @@ -66,10 +66,11 @@ contains ! OPENMC_FIND determines the ID or a cell or material at a given point in space !=============================================================================== - function openmc_find(xyz, rtype) result(id) bind(C) + subroutine openmc_find(xyz, rtype, id, instance) bind(C) real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material - integer(C_INT) :: id + integer(C_INT), intent(out) :: id + integer(C_INT), intent(out) :: instance logical :: found type(Particle) :: p @@ -80,6 +81,7 @@ contains call find_cell(p, found) id = -1 + instance = -1 if (found) then if (rtype == 1) then id = cells(p % coord(p % n_coord) % cell) % id @@ -90,8 +92,9 @@ contains id = materials(p % material) % id end if end if + instance = p % cell_instance end if - end function openmc_find + end subroutine openmc_find !=============================================================================== ! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library From 14226862f33dbc1e3829b762df35e9ee3f556906 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Jul 2017 14:44:21 -0500 Subject: [PATCH 30/66] Have openmc_tally_results accept ID instead of array index --- docs/source/capi/index.rst | 6 +++--- src/api.F90 | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index ae072448df..c932bcde93 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -124,12 +124,12 @@ C API :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: void openmc_tally_results(int i, double** ptr, int shape_[3]) +.. c:function:: void openmc_tally_results(int id, double** ptr, int shape_[3]) Get a pointer to tally results array. - :param i: Index in the tallies array - :type i: int + :param id: ID of the tally + :type id: int :param ptr: Pointer to the results array :type ptr: double** :param shape_: Shape of the results array diff --git a/src/api.F90 b/src/api.F90 index c0ee84594a..72879f41f4 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -376,14 +376,17 @@ contains ! directly. !=============================================================================== - subroutine openmc_tally_results(i, ptr, shape_) bind(C) - integer(C_INT), intent(in), value :: i + subroutine openmc_tally_results(id, ptr, shape_) bind(C) + integer(C_INT), intent(in), value :: id type(C_PTR), intent(out) :: ptr integer(C_INT), intent(out) :: shape_(3) + integer :: i + ptr = C_NULL_PTR if (allocated(tallies)) then - if (i >= 1 .and. i <= size(tallies)) then + if (tally_dict % has_key(id)) then + i = tally_dict % get_key(id) if (allocated(tallies(i) % results)) then ptr = C_LOC(tallies(i) % results(1,1,1)) shape_(:) = shape(tallies(i) % results) From 712352c8e1bc9cf427c223838f84b331aa0d140e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Jul 2017 15:06:33 -0500 Subject: [PATCH 31/66] Add optional instance argument for openmc_cell_set_temperature --- docs/source/capi/index.rst | 5 ++++- openmc/capi.py | 11 +++++++++-- src/api.F90 | 19 ++++++++++++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index c932bcde93..4cc195ef20 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -8,7 +8,7 @@ C API Run a stochastic volume calculation -.. c:function:: int openmc_cell_set_temperature(int id, double T) +.. c:function:: int openmc_cell_set_temperature(int id, double T, int* instance) Set the temperature of a cell. @@ -16,6 +16,9 @@ C API :type id: int :param T: Temperature in Kelvin :type T: double + :param instance: Which instance of the cell. To set the temperature for all + instances, pass a null pointer. + :type instance: int* :return: Return status (negative if an error occurred) :rtype: int diff --git a/openmc/capi.py b/openmc/capi.py index 2df55abce3..ea6f1e7c90 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -66,7 +66,7 @@ class OpenMCLibrary(object): """Run stochastic volume calculation""" return self._dll.openmc_calculate_volumes() - def cell_set_temperature(self, cell_id, T): + def cell_set_temperature(self, cell_id, T, instance=None): """Set the temperature of a cell Parameters @@ -75,9 +75,16 @@ class OpenMCLibrary(object): ID of the cell T : float Temperature in K + instance : int or None + Which instance of the cell """ - return self._dll.openmc_cell_set_temperature(cell_id, T) + if instance is not None: + return self._dll.openmc_cell_set_temperature( + cell_id, T, byref(c_int(instance))) + else: + return self._dll.openmc_cell_set_temperature(cell_id, T, None) + def finalize(self): """Finalize simulation and free memory""" diff --git a/src/api.F90 b/src/api.F90 index 72879f41f4..444dc71f9e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -41,12 +41,13 @@ contains ! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell !=============================================================================== - function openmc_cell_set_temperature(id, T) result(err) bind(C) + function openmc_cell_set_temperature(id, T, instance) result(err) bind(C) integer(C_INT), value, intent(in) :: id ! id of cell real(C_DOUBLE), value, intent(in) :: T + integer(C_INT), optional, intent(in) :: instance integer(C_INT) :: err - integer :: i + integer :: i, n err = -1 if (allocated(cells)) then @@ -54,8 +55,16 @@ contains i = cell_dict % get_key(id) associate (c => cells(i)) if (allocated(c % sqrtkT)) then - c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) - err = 0 + n = size(c % sqrtkT) + if (present(instance) .and. n > 1) then + if (instance >= 0 .and. instance < n) then + c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * T) + err = 0 + end if + else + c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + err = 0 + end if end if end associate end if @@ -92,7 +101,7 @@ contains id = materials(p % material) % id end if end if - instance = p % cell_instance + instance = p % cell_instance - 1 end if end subroutine openmc_find From e52d5dc0a2d65fcd6f5496f15a7da402e0cc80d0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Jul 2017 15:52:37 -0500 Subject: [PATCH 32/66] Remove use of byref (ctypes is smart enough to pass by reference) --- openmc/capi.py | 12 ++++++------ src/geometry.F90 | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index ea6f1e7c90..74af8c0034 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -1,4 +1,4 @@ -from ctypes import CDLL, c_int, POINTER, byref, c_double, c_char_p +from ctypes import CDLL, c_int, POINTER, c_double, c_char_p import sys from warnings import warn @@ -81,7 +81,7 @@ class OpenMCLibrary(object): """ if instance is not None: return self._dll.openmc_cell_set_temperature( - cell_id, T, byref(c_int(instance))) + cell_id, T, c_int(instance)) else: return self._dll.openmc_cell_set_temperature(cell_id, T, None) @@ -121,7 +121,7 @@ class OpenMCLibrary(object): # Call openmc_find uid = c_int() instance = c_int() - self._dll.openmc_find(_double3(*xyz), r_int, byref(uid), byref(instance)) + self._dll.openmc_find(_double3(*xyz), r_int, uid, instance) return (uid.value if uid != 0 else None), instance.value def init(self, intracomm=None): @@ -140,7 +140,7 @@ class OpenMCLibrary(object): intracomm = intracomm.py2f() except AttributeError: pass - return self._dll.openmc_init(byref(c_int(intracomm))) + return self._dll.openmc_init(c_int(intracomm)) else: return self._dll.openmc_init(None) @@ -196,7 +196,7 @@ class OpenMCLibrary(object): """ data = POINTER(c_double)() - n = self._dll.openmc_material_get_densities(mat_id, byref(data)) + n = self._dll.openmc_material_get_densities(mat_id, data) if data: return as_array(data, (n,)) else: @@ -284,7 +284,7 @@ class OpenMCLibrary(object): """ data = POINTER(c_double)() shape = _int3() - self._dll.openmc_tally_results(tally_id, byref(data), byref(shape)) + self._dll.openmc_tally_results(tally_id, data, shape) if data: return as_array(data, tuple(shape[::-1])) else: diff --git a/src/geometry.F90 b/src/geometry.F90 index fbbe75a183..4444562321 100644 --- a/src/geometry.F90 +++ b/src/geometry.F90 @@ -283,6 +283,8 @@ contains ! Keep track of which instance of the cell the particle is in p % cell_instance = offset + 1 + else + p % cell_instance = 1 end if ! Save the material From 5fda1abcca740b64ed8c6187554ea76cad99d6f4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 13 Jul 2017 15:35:36 -0500 Subject: [PATCH 33/66] Use int32_t for IDs to ensure they are 4 bytes --- openmc/capi.py | 20 ++++++++++---------- src/api.F90 | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 74af8c0034..c04aea4574 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -1,4 +1,4 @@ -from ctypes import CDLL, c_int, POINTER, c_double, c_char_p +from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys from warnings import warn @@ -32,23 +32,23 @@ class OpenMCLibrary(object): # Set argument/return types self._dll.openmc_calculate_volumes.restype = None self._dll.openmc_cell_set_temperature.argtypes = [ - c_int, c_double] + c_int32, c_double, c_int32] self._dll.openmc_cell_set_temperature.restype = c_int self._dll.openmc_finalize.restype = None self._dll.openmc_find.argtypes = [ - POINTER(_double3), c_int, POINTER(c_int), POINTER(c_int)] + POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] self._dll.openmc_find.restype = None self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None self._dll.openmc_load_nuclide.argtypes = [c_char_p] self._dll.openmc_load_nuclide.restype = c_int self._dll.openmc_material_add_nuclide.argtypes = [ - c_int, c_char_p, c_double] + c_int32, c_char_p, c_double] self._dll.openmc_material_add_nuclide.restype = c_int self._dll.openmc_material_get_densities.argtypes = [ - c_int, _double_array] + c_int32, _double_array] self._dll.openmc_material_get_densities.restype = c_int - self._dll.openmc_material_set_density.argtypes = [c_int, c_double] + self._dll.openmc_material_set_density.argtypes = [c_int32, c_double] self._dll.openmc_material_set_density.restype = c_int self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None @@ -59,7 +59,7 @@ class OpenMCLibrary(object): POINTER(_double3), c_double] self._dll.openmc_set_temperature.restype = c_int self._dll.openmc_tally_results.argtypes = [ - c_int, _double_array, POINTER(_int3)] + c_int32, _double_array, POINTER(_int3)] self._dll.openmc_tally_results.restype = None def calculate_volumes(self): @@ -81,7 +81,7 @@ class OpenMCLibrary(object): """ if instance is not None: return self._dll.openmc_cell_set_temperature( - cell_id, T, c_int(instance)) + cell_id, T, instance) else: return self._dll.openmc_cell_set_temperature(cell_id, T, None) @@ -119,8 +119,8 @@ class OpenMCLibrary(object): raise ValueError('Unknown return type: {}'.format(rtype)) # Call openmc_find - uid = c_int() - instance = c_int() + uid = c_int32() + instance = c_int32() self._dll.openmc_find(_double3(*xyz), r_int, uid, instance) return (uid.value if uid != 0 else None), instance.value diff --git a/src/api.F90 b/src/api.F90 index 444dc71f9e..1317ebb42d 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -42,9 +42,9 @@ contains !=============================================================================== function openmc_cell_set_temperature(id, T, instance) result(err) bind(C) - integer(C_INT), value, intent(in) :: id ! id of cell + integer(C_INT32_T), value, intent(in) :: id ! id of cell real(C_DOUBLE), value, intent(in) :: T - integer(C_INT), optional, intent(in) :: instance + integer(C_INT32_T), optional, intent(in) :: instance integer(C_INT) :: err integer :: i, n @@ -78,8 +78,8 @@ contains subroutine openmc_find(xyz, rtype, id, instance) bind(C) real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material - integer(C_INT), intent(out) :: id - integer(C_INT), intent(out) :: instance + integer(C_INT32_T), intent(out) :: id + integer(C_INT32_T), intent(out) :: instance logical :: found type(Particle) :: p @@ -172,7 +172,7 @@ contains !=============================================================================== function openmc_material_add_nuclide(id, name, density) result(err) bind(C) - integer(C_INT), value, intent(in) :: id + integer(C_INT32_T), value, intent(in) :: id character(kind=C_CHAR) :: name(*) real(C_DOUBLE), value, intent(in) :: density integer(C_INT) :: err @@ -244,8 +244,8 @@ contains !=============================================================================== function openmc_material_get_densities(id, ptr) result(n) bind(C) - integer(C_INT), intent(in), value :: id - type(C_PTR), intent(out) :: ptr + integer(C_INT32_T), intent(in), value :: id + type(C_PTR), intent(out) :: ptr integer(C_INT) :: n ptr = C_NULL_PTR @@ -268,7 +268,7 @@ contains !=============================================================================== function openmc_material_set_density(id, density) result(err) bind(C) - integer(C_INT), value, intent(in) :: id + integer(C_INT32_T), value, intent(in) :: id real(C_DOUBLE), value, intent(in) :: density integer(C_INT) :: err @@ -386,9 +386,9 @@ contains !=============================================================================== subroutine openmc_tally_results(id, ptr, shape_) bind(C) - integer(C_INT), intent(in), value :: id - type(C_PTR), intent(out) :: ptr - integer(C_INT), intent(out) :: shape_(3) + integer(C_INT32_T), intent(in), value :: id + type(C_PTR), intent(out) :: ptr + integer(C_INT), intent(out) :: shape_(3) integer :: i From 23cd067bdaa4af26855d52c5e49579ecc2347855 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Jul 2017 06:42:28 -0500 Subject: [PATCH 34/66] Remove openmc_set_density and openmc_set_temperature --- docs/source/capi/index.rst | 22 -------------- openmc/capi.py | 41 -------------------------- src/api.F90 | 59 -------------------------------------- 3 files changed, 122 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index 4cc195ef20..9bb1b2c662 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -105,28 +105,6 @@ C API Run a simulation -.. c:function:: int openmc_set_density(double xyz[3], double density) - - Set the density of a material at a given point. - - :param xyz: Cartesian coordinates - :type xyz: double[3] - :param density: Density of the material to set in atom/b-cm - :type density: double - :return: Return status (negative if an error occurs) - :rtype: int - -.. c:function:: int openmc_set_temperature(double xyz[3], double T) - - Set the density of a cell at a given point. - - :param xyz: Cartesian coordinates - :type xyz: double[3] - :param T: Temperature of the cell to set in Kelvin - :type T: double - :return: Return status (negative if an error occurs) - :rtype: int - .. c:function:: void openmc_tally_results(int id, double** ptr, int shape_[3]) Get a pointer to tally results array. diff --git a/openmc/capi.py b/openmc/capi.py index c04aea4574..a194e2d26a 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -53,11 +53,6 @@ class OpenMCLibrary(object): self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None - self._dll.openmc_set_density.argtypes = [POINTER(_double3), c_double] - self._dll.openmc_set_density.restype = c_int - self._dll.openmc_set_temperature.argtypes = [ - POINTER(_double3), c_double] - self._dll.openmc_set_temperature.restype = c_int self._dll.openmc_tally_results.argtypes = [ c_int32, _double_array, POINTER(_int3)] self._dll.openmc_tally_results.restype = None @@ -232,42 +227,6 @@ class OpenMCLibrary(object): """Run simulation""" return self._dll.openmc_run() - def set_density(self, xyz, density): - """Set density at a given point. - - Parameters - ---------- - xyz : iterable of float - Cartesian coordinates at position - density : float - Density in atom/b-cm - - Returns - ------- - int - Return status (negative if an error occurs) - - """ - return self._dll.openmc_set_density(_double3(*xyz), density) - - def set_temperature(self, xyz, T): - """Set temperature at a given point. - - Parameters - ---------- - xyz : iterable of float - Cartesian coordinates at position - T : float - Temperature in K - - Returns - ------- - int - Return status (negative if an error occurs) - - """ - return self._dll.openmc_set_temperature(_double3(*xyz), T) - def tally_results(self, tally_id): """Get tally results array diff --git a/src/api.F90 b/src/api.F90 index 1317ebb42d..b00193fcff 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -31,8 +31,6 @@ module openmc_api public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run - public :: openmc_set_density - public :: openmc_set_temperature public :: openmc_tally_results contains @@ -322,63 +320,6 @@ contains end subroutine openmc_reset -!=============================================================================== -! OPENMC_SET_DENSITY sets the density of a material at a given point -!=============================================================================== - - function openmc_set_density(xyz, density) result(err) bind(C) - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in), value :: density - integer(C_INT) :: err - - logical :: found - type(Particle) :: p - - call p % initialize() - p % coord(1) % xyz(:) = xyz - p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] - call find_cell(p, found) - - err = -1 - if (found) then - if (p % material /= MATERIAL_VOID) then - associate (m => materials(p % material)) - err = m % set_density(density, nuclides) - end associate - end if - end if - end function openmc_set_density - -!=============================================================================== -! OPENMC_SET_TEMPERATURE sets the temperature of a cell at a given point -!=============================================================================== - - function openmc_set_temperature(xyz, T) result(err) bind(C) - real(C_DOUBLE), intent(in) :: xyz(3) - real(C_DOUBLE), intent(in), value :: T - integer(C_INT) :: err - - logical :: found - type(Particle) :: p - - call p % initialize() - p % coord(1) % xyz(:) = xyz - p % coord(1) % uvw(:) = [ZERO, ZERO, ONE] - call find_cell(p, found) - - err = -1 - if (found) then - associate (c => cells(p % coord(p % n_coord) % cell)) - if (size(c % sqrtkT) > 1) then - c % sqrtkT(p % cell_instance) = sqrt(K_BOLTZMANN * T) - else - c % sqrtkT(1) = sqrt(K_BOLTZMANN * T) - end if - err = 0 - end associate - end if - end function openmc_set_temperature - !=============================================================================== ! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its ! shape. This allows a user to obtain in-memory tally results from Python From ca28d20a016509a88d476aaf65c05ca6a03e12e0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Jul 2017 07:09:21 -0500 Subject: [PATCH 35/66] Make sure valid communicator is passed to initialize_mpi --- src/initialize.F90 | 15 ++++++++++++--- src/material_header.F90 | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/initialize.F90 b/src/initialize.F90 index 98f02863e2..69479907e8 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -49,14 +49,23 @@ contains integer, intent(in), optional :: intracomm ! MPI intracommunicator ! Copy the communicator to a new variable. This is done to avoid changing - ! the signature of this subroutine. + ! the signature of this subroutine. If MPI is being used but no communicator + ! was passed, assume MPI_COMM_WORLD. #ifdef MPI #ifdef MPIF08 type(MPI_Comm), intent(in) :: comm ! MPI intracommunicator - comm % MPI_VAL = intracomm + if (present(intracomm)) then + comm % MPI_VAL = intracomm + else + comm = MPI_COMM_WORLD + end if #else integer :: comm - comm = intracomm + if (present(intracomm)) then + comm = intracomm + else + comm = MPI_COMM_WORLD + end if #endif #endif diff --git a/src/material_header.F90 b/src/material_header.F90 index da2faf4a69..4415ad0867 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -48,6 +48,10 @@ module material_header contains +!=============================================================================== +! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm. +!=============================================================================== + function material_set_density(m, density, nuclides) result(err) class(Material), intent(inout) :: m real(8), intent(in) :: density From 35657d5e1b3acafde1723c07c03ed0946a8471a0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 17 Jul 2017 07:58:35 -0500 Subject: [PATCH 36/66] Add context manager for shared library calls --- docs/source/pythonapi/capi.rst | 7 +++++++ openmc/__init__.py | 2 +- openmc/capi.py | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index c78c43fd34..af00363cc6 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -2,6 +2,13 @@ :data:`openmc.capi` -- Python bindings to the C API --------------------------------------------------- +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.capi.lib_context + .. autosummary:: :toctree: generated :nosignatures: diff --git a/openmc/__init__.py b/openmc/__init__.py index 74c991fda4..a0fe264911 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,6 +27,6 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * -from openmc.capi import lib, OpenMCLibrary +from openmc.capi import * __version__ = '0.9.0' diff --git a/openmc/capi.py b/openmc/capi.py index a194e2d26a..bb9241d7d3 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -1,3 +1,4 @@ +from contextlib import contextmanager from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys from warnings import warn @@ -7,6 +8,8 @@ from numpy.ctypeslib import as_array import pkg_resources +__all__ = ['OpenMCLibrary', 'lib', 'lib_context'] + _int3 = c_int*3 _double3 = c_double*3 _double_array = POINTER(POINTER(c_double)) @@ -80,7 +83,6 @@ class OpenMCLibrary(object): else: return self._dll.openmc_cell_set_temperature(cell_id, T, None) - def finalize(self): """Finalize simulation and free memory""" return self._dll.openmc_finalize() @@ -124,7 +126,7 @@ class OpenMCLibrary(object): Parameters ---------- - intracomm : int or None + intracomm : mpi4py.MPI.Intracomm or None MPI intracommunicator """ @@ -258,6 +260,32 @@ class OpenMCLibrary(object): .format(key)) +@contextmanager +def lib_context(intracomm=None): + """Provides context manager for calling OpenMC shared library functions. + + This function is intended to be used in a 'with' statement and ensures that + OpenMC is properly initialized/finalized. At the completion of the 'with' + block, all memory that was allocated during the block is freed. For + example:: + + with openmc.lib_context() as lib: + for i in range(n_iters): + lib.reset() + do_stuff() + lib.run() + + Parameters + ---------- + intracomm : mpi4py.MPI.Intracomm or None + MPI intracommunicator + + """ + lib.init(comm) + yield lib + lib.finalize() + + # Determine shared-library suffix if sys.platform == 'darwin': suffix = 'dylib' From 7bc61c1f41931a4afcc0efdf284b7ede8a8d46a6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 25 Jul 2017 22:13:41 -0500 Subject: [PATCH 37/66] Make k-effective accessible from C API --- openmc/capi.py | 16 ++++++++++++++++ src/api.F90 | 3 ++- src/eigenvalue.F90 | 20 ++++++++++++++------ src/global.F90 | 1 - src/output.F90 | 11 +++++++++-- src/simulation.F90 | 15 ++------------- src/state_point.F90 | 8 +++++--- src/trigger.F90 | 6 ++++++ 8 files changed, 54 insertions(+), 26 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index bb9241d7d3..f4c9a7f3ce 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -43,6 +43,8 @@ class OpenMCLibrary(object): self._dll.openmc_find.restype = None self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None + self._dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] + self._dll.openmc_get_keff.restype = c_int self._dll.openmc_load_nuclide.argtypes = [c_char_p] self._dll.openmc_load_nuclide.restype = c_int self._dll.openmc_material_add_nuclide.argtypes = [ @@ -141,7 +143,21 @@ class OpenMCLibrary(object): else: return self._dll.openmc_init(None) + def keff(self): + """Return the calculated k-eigenvalue and its standard deviation. + + Returns + ------- + tuple + Mean k-eigenvalue and standard deviation of the mean + + """ + k = (c_double*2)() + err = self._dll.openmc_get_keff(k) + return tuple(k) + def load_nuclide(self, name): + """Load cross section data for a nuclide. Parameters diff --git a/src/api.F90 b/src/api.F90 index b00193fcff..45f511f9cc 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -5,7 +5,7 @@ module openmc_api use hdf5, only: HID_T use constants, only: K_BOLTZMANN - use eigenvalue, only: k_sum + use eigenvalue, only: k_sum, openmc_get_keff use finalize, only: openmc_finalize use geometry, only: find_cell use global @@ -23,6 +23,7 @@ module openmc_api public :: openmc_cell_set_temperature public :: openmc_finalize public :: openmc_find + public :: openmc_get_keff public :: openmc_init public :: openmc_load_nuclide public :: openmc_material_add_nuclide diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index f370ab38eb..9f4da7c82f 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -1,5 +1,6 @@ module eigenvalue + use, intrinsic :: ISO_C_BINDING use algorithm, only: binary_search use constants, only: ZERO @@ -439,8 +440,8 @@ contains end subroutine calculate_average_keff !=============================================================================== -! CALCULATE_COMBINED_KEFF calculates a minimum variance estimate of k-effective -! based on a linear combination of the collision, absorption, and tracklength +! OPENMC_GET_KEFF calculates a minimum variance estimate of k-effective based on +! a linear combination of the collision, absorption, and tracklength ! estimates. The theory behind this can be found in M. Halperin, "Almost ! linearly-optimum combination of unbiased estimates," J. Am. Stat. Assoc., 56, ! 36-43 (1961), doi:10.1080/01621459.1961.10482088. The implementation here @@ -448,7 +449,9 @@ contains ! of keff confidence intervals in MCNP," Nucl. Technol., 111, 169-182 (1995). !=============================================================================== - subroutine calculate_combined_keff() + function openmc_get_keff(k_combined) result(err) bind(C) + real(C_DOUBLE), intent(out) :: k_combined(2) + integer(C_INT) :: err integer :: l ! loop index integer :: i, j, k ! indices referring to collision, absorption, or track @@ -459,9 +462,14 @@ contains real(8) :: g ! sum of weighting factors real(8) :: S(3) ! sums used for variance calculation + k_combined = ZERO + ! Make sure we have at least four realizations. Notice that at the end, ! there is a N-3 term in a denominator. - if (n_realizations <= 3) return + if (n_realizations <= 3) then + err = -1 + return + end if ! Initialize variables n = real(n_realizations, 8) @@ -523,7 +531,6 @@ contains ! Initialize variables g = ZERO S = ZERO - k_combined = ZERO do l = 1, 3 ! Permutations of estimates @@ -590,8 +597,9 @@ contains k_combined(2) = sqrt(k_combined(2)) end if + err = 0 - end subroutine calculate_combined_keff + end function openmc_get_keff !=============================================================================== ! COUNT_SOURCE_FOR_UFS determines the source fraction in each UFS mesh cell and diff --git a/src/global.F90 b/src/global.F90 index 2690ed1518..41f8654694 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -256,7 +256,6 @@ module global real(8) :: k_col_abs = ZERO ! sum over batches of k_collision * k_absorption real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength - real(8) :: k_combined(2) ! combined best estimate of k-effective ! Shannon entropy logical :: entropy_on = .false. diff --git a/src/output.F90 b/src/output.F90 index 828af717d8..9835225e06 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -1,8 +1,10 @@ module output + use, intrinsic :: ISO_C_BINDING use, intrinsic :: ISO_FORTRAN_ENV use constants + use eigenvalue, only: openmc_get_keff use endf, only: reaction_name use error, only: fatal_error, warning use geometry_header, only: Cell, Universe, Lattice, RectLattice, & @@ -608,6 +610,8 @@ contains real(8) :: t_n1 ! t-value with N-1 degrees of freedom real(8) :: t_n3 ! t-value with N-3 degrees of freedom real(8) :: x(2) ! mean and standard deviation + real(C_DOUBLE) :: k_combined(2) + integer(C_INT) :: err ! display header block for results call header("Results", 4) @@ -634,8 +638,11 @@ contains write(ou,102) "k-effective (Track-length)", x(1), t_n1 * x(2) x(:) = mean_stdev(r(:, K_ABSORPTION), n) write(ou,102) "k-effective (Absorption)", x(1), t_n1 * x(2) - if (n > 3) write(ou,102) "Combined k-effective", k_combined(1), & - t_n3 * k_combined(2) + if (n > 3) then + err = openmc_get_keff(k_combined) + write(ou,102) "Combined k-effective", k_combined(1), & + t_n3 * k_combined(2) + end if end if x(:) = mean_stdev(global_tallies(:, LEAKAGE), n) write(ou,102) "Leakage Fraction", x(1), t_n1 * x(2) diff --git a/src/simulation.F90 b/src/simulation.F90 index 87a17452d9..505d5b528f 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -5,9 +5,8 @@ module simulation use cmfd_execute, only: cmfd_init_batch, execute_cmfd use constants, only: ZERO use eigenvalue, only: count_source_for_ufs, calculate_average_keff, & - calculate_combined_keff, calculate_generation_keff, & - shannon_entropy, synchronize_bank, keff_generation, & - k_sum + calculate_generation_keff, shannon_entropy, & + synchronize_bank, keff_generation, k_sum #ifdef _OPENMP use eigenvalue, only: join_bank_from_threads #endif @@ -300,9 +299,6 @@ contains if (run_mode == MODE_EIGENVALUE) then ! Perform CMFD calculation if on if (cmfd_on) call execute_cmfd() - - ! Calculate combined estimate of k-effective - if (master) call calculate_combined_keff() end if ! Check_triggers @@ -327,13 +323,6 @@ contains call write_source_point() end if - if (master .and. current_batch == n_max_batches .and. & - run_mode == MODE_EIGENVALUE) then - ! Make sure combined estimate of k-effective is calculated at the last - ! batch in case no state point is written - call calculate_combined_keff() - end if - end subroutine finalize_batch !=============================================================================== diff --git a/src/state_point.F90 b/src/state_point.F90 index e4c9a43ae7..f48b5638b4 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -11,11 +11,12 @@ module state_point ! intervals, using the tag. !=============================================================================== - use, intrinsic :: ISO_C_BINDING, only: c_loc, c_ptr + use, intrinsic :: ISO_C_BINDING use hdf5 use constants + use eigenvalue, only: openmc_get_keff use endf, only: reaction_name use error, only: fatal_error, warning use global @@ -46,6 +47,8 @@ contains integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, & mesh_group, filters_group, filter_group, derivs_group, & deriv_group, runtime_group + integer(C_INT) :: err + real(C_DOUBLE) :: k_combined(2) character(MAX_WORD_LEN), allocatable :: str_array(:) character(MAX_FILE_LEN) :: filename type(TallyObject), pointer :: tally @@ -117,6 +120,7 @@ contains call write_dataset(file_id, "k_col_abs", k_col_abs) call write_dataset(file_id, "k_col_tra", k_col_tra) call write_dataset(file_id, "k_abs_tra", k_abs_tra) + err = openmc_get_keff(k_combined) call write_dataset(file_id, "k_combined", k_combined) ! Write out CMFD info @@ -647,7 +651,6 @@ contains integer(HID_T) :: cmfd_group integer(HID_T) :: tallies_group integer(HID_T) :: tally_group - real(8) :: real_array(3) logical :: source_present character(MAX_WORD_LEN) :: word @@ -727,7 +730,6 @@ contains call read_dataset(k_col_abs, file_id, "k_col_abs") call read_dataset(k_col_tra, file_id, "k_col_tra") call read_dataset(k_abs_tra, file_id, "k_abs_tra") - call read_dataset(real_array(1:2), file_id, "k_combined") ! Take maximum of statepoint n_inactive and input n_inactive n_inactive = max(n_inactive, int_array(1)) diff --git a/src/trigger.F90 b/src/trigger.F90 index 9eb39e9cc2..dc0a10b153 100644 --- a/src/trigger.F90 +++ b/src/trigger.F90 @@ -1,10 +1,13 @@ module trigger + use, intrinsic :: ISO_C_BINDING + #ifdef MPI use message_passing #endif use constants + use eigenvalue, only: openmc_get_keff use global use string, only: to_str use output, only: warning, write_message @@ -101,10 +104,12 @@ contains integer :: score_index ! scoring bin index integer :: n_order ! loop index for moment orders integer :: nm_order ! loop index for Ynm moment orders + integer(C_INT) :: err real(8) :: uncertainty ! trigger uncertainty real(8) :: std_dev = ZERO ! trigger standard deviation real(8) :: rel_err = ZERO ! trigger relative error real(8) :: ratio ! ratio of the uncertainty/trigger threshold + real(C_DOUBLE) :: k_combined(2) type(TallyObject), pointer :: t ! tally pointer type(TriggerObject), pointer :: trigger ! tally trigger @@ -119,6 +124,7 @@ contains ! Check eigenvalue trigger if (run_mode == MODE_EIGENVALUE) then if (keff_trigger % trigger_type /= 0) then + err = openmc_get_keff(k_combined) select case (keff_trigger % trigger_type) case(VARIANCE) uncertainty = k_combined(2) ** 2 From 09d1dd7f3c53aaf1c80e4510e5f75175ec960e5a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 11:05:07 -0500 Subject: [PATCH 38/66] Reset global variables on openmc_finalize --- CMakeLists.txt | 1 - src/api.F90 | 104 +++++++++++++++++++++++++++++++++++++++++++---- src/finalize.F90 | 40 ------------------ src/global.F90 | 13 ++++-- src/main.F90 | 9 ++-- 5 files changed, 107 insertions(+), 60 deletions(-) delete mode 100644 src/finalize.F90 diff --git a/CMakeLists.txt b/CMakeLists.txt index f83d95625b..386c07100c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,7 +320,6 @@ set(LIBOPENMC_FORTRAN_SRC src/endf_header.F90 src/energy_distribution.F90 src/error.F90 - src/finalize.F90 src/geometry.F90 src/geometry_header.F90 src/global.F90 diff --git a/src/api.F90 b/src/api.F90 index 45f511f9cc..efe772a8cf 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -2,19 +2,20 @@ module openmc_api use, intrinsic :: ISO_C_BINDING - use hdf5, only: HID_T + use hdf5, only: HID_T, h5tclose_f, h5close_f use constants, only: K_BOLTZMANN use eigenvalue, only: k_sum, openmc_get_keff - use finalize, only: openmc_finalize use geometry, only: find_cell + use geometry_header, only: root_universe use global use hdf5_interface - use message_passing, only: master + use message_passing use initialize, only: openmc_init use input_xml, only: assign_0K_elastic_scattering, check_data_version use particle_header, only: Particle use plot, only: openmc_plot_geometry + use random_lcg, only: seed use simulation, only: openmc_run use volume_calc, only: openmc_calculate_volumes @@ -70,6 +71,89 @@ contains end if end function openmc_cell_set_temperature +!=============================================================================== +! OPENMC_FINALIZE frees up memory by deallocating arrays and resetting global +! variables +!=============================================================================== + + subroutine openmc_finalize() bind(C) + + integer :: hdf5_err + + ! Clear results + call openmc_reset() + + ! Reset global variables + assume_separate = .false. + check_overlaps = .false. + confidence_intervals = .false. + create_fission_neutrons = .true. + current_batch = 0 + current_gen = 0 + energy_cutoff = ZERO + energy_max_neutron = INFINITY + energy_min_neutron = ZERO + entropy_on = .false. + gen_per_batch = 1 + i_user_tallies = -1 + i_cmfd_tallies = -1 + keff = ONE + legendre_to_tabular = .true. + legendre_to_tabular_points = 33 + n_batch_interval = 1 + n_particles = 0 + n_source_points = 0 + n_state_points = 0 + output_summary = .true. + output_tallies = .true. + particle_restart_run = .false. + pred_batches = .false. + reduce_tallies = .true. + res_scat_on = .false. + res_scat_method = RES_SCAT_ARES + res_scat_energy_min = 0.01_8 + res_scat_energy_max = 1000.0_8 + restart_run = .false. + root_universe = -1 + run_CE = .true. + run_mode = NONE + satisfy_triggers = .false. + seed = 1_8 + source_latest = .false. + source_separate = .false. + source_write = .true. + survival_biasing = .false. + temperature_default = 293.6_8 + temperature_method = TEMPERATURE_NEAREST + temperature_multipole = .false. + temperature_range = [ZERO, ZERO] + temperature_tolerance = 10.0_8 + total_gen = 0 + trigger_on = .false. + ufs = .false. + urr_ptables_on = .true. + verbosity = 7 + weight_cutoff = 0.25_8 + weight_survive = ONE + write_all_tracks = .false. + write_initial_source = .false. + + ! Deallocate arrays + call free_memory() + + ! Release compound datatypes + call h5tclose_f(hdf5_bank_t, hdf5_err) + + ! Close FORTRAN interface. + call h5close_f(hdf5_err) + +#ifdef MPI + ! Free all MPI types + call MPI_TYPE_FREE(MPI_BANK, mpi_err) +#endif + + end subroutine openmc_finalize + !=============================================================================== ! OPENMC_FIND determines the ID or a cell or material at a given point in space !=============================================================================== @@ -291,12 +375,14 @@ contains subroutine openmc_reset() bind(C) integer :: i - do i = 1, size(tallies) - tallies(i) % n_realizations = 0 - if (allocated(tallies(i) % results)) then - tallies(i) % results(:, :, :) = ZERO - end if - end do + if (allocated(tallies)) then + do i = 1, size(tallies) + tallies(i) % n_realizations = 0 + if (allocated(tallies(i) % results)) then + tallies(i) % results(:, :, :) = ZERO + end if + end do + end if ! Reset global tallies n_realizations = 0 diff --git a/src/finalize.F90 b/src/finalize.F90 deleted file mode 100644 index 6164371a0b..0000000000 --- a/src/finalize.F90 +++ /dev/null @@ -1,40 +0,0 @@ -module finalize - - use, intrinsic :: ISO_C_BINDING - - use hdf5, only: h5tclose_f, h5close_f - - use global - use hdf5_interface, only: hdf5_bank_t - use message_passing - - implicit none - -contains - -!=============================================================================== -! FINALIZE_RUN does all post-simulation tasks such as calculating tally -! statistics and writing out tallies -!=============================================================================== - - subroutine openmc_finalize() bind(C) - - integer :: hdf5_err - - ! Deallocate arrays - call free_memory() - - ! Release compound datatypes - call h5tclose_f(hdf5_bank_t, hdf5_err) - - ! Close FORTRAN interface. - call h5close_f(hdf5_err) - -#ifdef MPI - ! Free all MPI types - call MPI_TYPE_FREE(MPI_BANK, mpi_err) -#endif - - end subroutine openmc_finalize - -end module finalize diff --git a/src/global.F90 b/src/global.F90 index 41f8654694..9d9002e9c5 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -140,7 +140,7 @@ module global integer :: max_order ! Whether or not to convert Legendres to tabulars - logical :: legendre_to_tabular = .True. + logical :: legendre_to_tabular = .true. ! Number of points to use in the Legendre to tabular conversion integer :: legendre_to_tabular_points = 33 @@ -466,6 +466,7 @@ contains if (allocated(surfaces)) deallocate(surfaces) if (allocated(materials)) deallocate(materials) if (allocated(plots)) deallocate(plots) + if (allocated(volume_calcs)) deallocate(volume_calcs) ! Deallocate geometry debugging information if (allocated(overlap_check_cnt)) deallocate(overlap_check_cnt) @@ -478,6 +479,7 @@ contains end do deallocate(nuclides) end if + if (allocated(libraries)) deallocate(libraries) if (allocated(res_scat_nuclides)) deallocate(res_scat_nuclides) @@ -486,7 +488,6 @@ contains if (allocated(macro_xs)) deallocate(macro_xs) if (allocated(sab_tables)) deallocate(sab_tables) - if (allocated(micro_xs)) deallocate(micro_xs) ! Deallocate external source if (allocated(external_source)) deallocate(external_source) @@ -501,21 +502,24 @@ contains if (allocated(meshes)) deallocate(meshes) if (allocated(filters)) deallocate(filters) if (allocated(tallies)) deallocate(tallies) - if (allocated(filter_matches)) deallocate(filter_matches) ! Deallocate fission and source bank and entropy !$omp parallel if (allocated(fission_bank)) deallocate(fission_bank) + if (allocated(filter_matches)) deallocate(filter_matches) + if (allocated(tally_derivs)) deallocate(tally_derivs) !$omp end parallel #ifdef _OPENMP if (allocated(master_fission_bank)) deallocate(master_fission_bank) #endif if (allocated(source_bank)) deallocate(source_bank) - if (allocated(entropy_p)) deallocate(entropy_p) ! Deallocate array of work indices if (allocated(work_index)) deallocate(work_index) + if (allocated(energy_bins)) deallocate(energy_bins) + if (allocated(energy_bin_avg)) deallocate(energy_bin_avg) + ! Deallocate CMFD call deallocate_cmfd(cmfd) @@ -541,6 +545,7 @@ contains call plot_dict % clear() call nuclide_dict % clear() call sab_dict % clear() + call library_dict % clear() ! Clear statepoint and sourcepoint batch set call statepoint_batch % clear() diff --git a/src/main.F90 b/src/main.F90 index 723c87df9f..b8c96927ea 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -1,14 +1,11 @@ program main use constants - use finalize, only: openmc_finalize use global - use initialize, only: openmc_init use message_passing - use particle_restart, only: run_particle_restart - use plot, only: openmc_plot_geometry - use simulation, only: openmc_run - use volume_calc, only: openmc_calculate_volumes + use openmc_api, only: openmc_init, openmc_finalize, openmc_run, & + openmc_plot_geometry, openmc_calculate_volumes + use particle_restart, only: run_particle_restart implicit none From 6cada92c50c8e2bdec93b9610ec54ab86c3d151a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 13:18:49 -0500 Subject: [PATCH 39/66] Update tallies.xml in examples to separate --- examples/xml/basic/tallies.xml | 21 +++++++++++++++------ examples/xml/lattice/nested/tallies.xml | 6 +++++- examples/xml/lattice/simple/tallies.xml | 6 +++++- examples/xml/pincell/tallies.xml | 11 +++++++++-- examples/xml/pincell_multigroup/tallies.xml | 9 +++++++-- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/examples/xml/basic/tallies.xml b/examples/xml/basic/tallies.xml index 26e61995da..a125e9ca2a 100644 --- a/examples/xml/basic/tallies.xml +++ b/examples/xml/basic/tallies.xml @@ -1,21 +1,30 @@ + + 100 + + + + 0 20.0e6 + + + + 0 20.0e6 + + - + 1 total scatter nu-scatter absorption fission nu-fission - - + 1 2 total scatter nu-scatter absorption fission nu-fission - - - + 1 2 3 scatter nu-scatter nu-fission diff --git a/examples/xml/lattice/nested/tallies.xml b/examples/xml/lattice/nested/tallies.xml index 89c0774f15..d342174a99 100644 --- a/examples/xml/lattice/nested/tallies.xml +++ b/examples/xml/lattice/nested/tallies.xml @@ -8,8 +8,12 @@ 1.0 1.0 + + 1 + + - + 1 total diff --git a/examples/xml/lattice/simple/tallies.xml b/examples/xml/lattice/simple/tallies.xml index 89c0774f15..d342174a99 100644 --- a/examples/xml/lattice/simple/tallies.xml +++ b/examples/xml/lattice/simple/tallies.xml @@ -8,8 +8,12 @@ 1.0 1.0 + + 1 + + - + 1 total diff --git a/examples/xml/pincell/tallies.xml b/examples/xml/pincell/tallies.xml index f740530cf7..642bdd7cd3 100644 --- a/examples/xml/pincell/tallies.xml +++ b/examples/xml/pincell/tallies.xml @@ -7,9 +7,16 @@ 0.62992 0.62992 1.e50 + + 1 + + + + 0. 4. 20.0e6 + + - - + 1 2 flux fission nu-fission diff --git a/examples/xml/pincell_multigroup/tallies.xml b/examples/xml/pincell_multigroup/tallies.xml index 1591a556b4..d84e129f2f 100644 --- a/examples/xml/pincell_multigroup/tallies.xml +++ b/examples/xml/pincell_multigroup/tallies.xml @@ -5,9 +5,14 @@ -0.63 -0.63 -1e+50 0.63 0.63 1e+50 + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 + - - + 1 2 flux fission nu-fission From 306c96c74b20e9813d1a444605e6ca6079480c78 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 14:00:49 -0500 Subject: [PATCH 40/66] Broadcast tally results at end of simulation --- src/simulation.F90 | 33 ++++++++++++++++++++++++++++++--- src/tally.F90 | 6 +++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/simulation.F90 b/src/simulation.F90 index 505d5b528f..7edaf8be22 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -20,7 +20,7 @@ module simulation use source, only: initialize_source, sample_external_source use state_point, only: write_state_point, write_source_point use string, only: to_str - use tally, only: synchronize_tallies, setup_active_usertallies + use tally, only: accumulate_tallies, setup_active_usertallies use trigger, only: check_triggers use tracking, only: transport @@ -285,9 +285,9 @@ contains subroutine finalize_batch() - ! Collect tallies + ! Reduce tallies onto master process and accumulate call time_tallies % start() - call synchronize_tallies() + call accumulate_tallies() call time_tallies % stop() ! Reset global tally results @@ -390,6 +390,10 @@ contains subroutine finalize_simulation() + integer :: i ! loop index for tallies + integer :: n ! size of arrays + real(8) :: temp(3) ! temporary array for communication + !$omp parallel deallocate(micro_xs) !$omp end parallel @@ -400,6 +404,29 @@ contains ! Start finalization timer call time_finalize % start() +#ifdef MPI + ! Broadcast tally results so that each process has access to results + do i = 1, size(tallies) + n = size(tallies(i) % results) + call MPI_BCAST(tallies(i) % results, n, MPI_DOUBLE, 0, & + mpi_intracomm, mpi_err) + end do + + ! Also broadcast global tally results + n = size(global_tallies) + call MPI_BCAST(global_tallies, n, MPI_DOUBLE, 0, mpi_intracomm, mpi_err) + + ! These guys are needed so that non-master processes can calculate the + ! combined estimate of k-effective + temp(1) = k_col_abs + temp(2) = k_col_tra + temp(3) = k_abs_tra + call MPI_BCAST(temp, 3, MPI_REAL8, 0, mpi_intracomm, mpi_err) + k_col_abs = temp(1) + k_col_tra = temp(2) + k_abs_tra = temp(3) +#endif + ! Write tally results to tallies.out if (output_tallies .and. master) call write_tallies() if (check_overlaps) call reduce_overlap_count() diff --git a/src/tally.F90 b/src/tally.F90 index 14940551b7..b8e46678ca 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4182,11 +4182,11 @@ contains end subroutine zero_flux_derivs !=============================================================================== -! SYNCHRONIZE_TALLIES accumulates the sum of the contributions from each history +! ACCUMULATE_TALLIES accumulates the sum of the contributions from each history ! within the batch to a new random variable !=============================================================================== - subroutine synchronize_tallies() + subroutine accumulate_tallies() integer :: i real(C_DOUBLE) :: k_col ! Copy of batch collision estimate of keff @@ -4236,7 +4236,7 @@ contains end do end if - end subroutine synchronize_tallies + end subroutine accumulate_tallies !=============================================================================== ! REDUCE_TALLY_RESULTS collects all the results from tallies onto one processor From 0fba66b8fe847d0b6081e262d88ea5f5a3b315ef Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 14:16:00 -0500 Subject: [PATCH 41/66] Reset a few more global variables, ugh --- src/api.F90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/api.F90 b/src/api.F90 index efe772a8cf..dc9548f107 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -101,9 +101,15 @@ contains legendre_to_tabular = .true. legendre_to_tabular_points = 33 n_batch_interval = 1 + n_filters = 0 + n_meshes = 0 n_particles = 0 n_source_points = 0 n_state_points = 0 + n_tallies = 0 + n_user_filters = 0 + n_user_meshes = 0 + n_user_tallies = 0 output_summary = .true. output_tallies = .true. particle_restart_run = .false. From c124a4e8e3c43fccb6bff981ea13027c602f84fd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 14:53:35 -0500 Subject: [PATCH 42/66] Get rid of almost all MPI_IN_PLACE specifiers --- src/mesh.F90 | 36 ++++++++++++------------------------ src/simulation.F90 | 12 +++++------- src/state_point.F90 | 19 +++++-------------- src/tally.F90 | 38 +++++++++++++------------------------- 4 files changed, 35 insertions(+), 70 deletions(-) diff --git a/src/mesh.F90 b/src/mesh.F90 index b778cbdf06..393df5dda4 100644 --- a/src/mesh.F90 +++ b/src/mesh.F90 @@ -137,21 +137,19 @@ contains real(8), intent(in), optional :: energies(:) ! energy grid to search integer(8), intent(in), optional :: size_bank ! # of bank sites (on each proc) logical, intent(inout), optional :: sites_outside ! were there sites outside mesh? + real(8), allocatable :: cnt_(:,:,:,:) integer :: i ! loop index for local fission sites integer :: n_sites ! size of bank array integer :: ijk(3) ! indices on mesh - integer :: n_groups ! number of groups in energies + integer :: n ! number of energy groups / size integer :: e_bin ! energy_bin logical :: in_mesh ! was single site outside mesh? logical :: outside ! was any site outside mesh? -#ifdef MPI - integer :: n ! total size of count variable - real(8) :: dummy ! temporary receive buffer for non-root reductions -#endif ! initialize variables - cnt = ZERO + allocate(cnt_(size(cnt,1), size(cnt,2), size(cnt,3), size(cnt,4))) + cnt_ = ZERO outside = .false. ! Set size of bank @@ -163,9 +161,9 @@ contains ! Determine number of energies in group structure if (present(energies)) then - n_groups = size(energies) - 1 + n = size(energies) - 1 else - n_groups = 1 + n = 1 end if ! loop over fission sites and count how many are in each mesh box @@ -183,40 +181,30 @@ contains if (present(energies)) then if (bank_array(i) % E < energies(1)) then e_bin = 1 - elseif (bank_array(i) % E > energies(n_groups+1)) then - e_bin = n_groups + elseif (bank_array(i) % E > energies(n + 1)) then + e_bin = n else - e_bin = binary_search(energies, n_groups + 1, bank_array(i) % E) + e_bin = binary_search(energies, n + 1, bank_array(i) % E) end if else e_bin = 1 end if ! add to appropriate mesh box - cnt(e_bin,ijk(1),ijk(2),ijk(3)) = cnt(e_bin,ijk(1),ijk(2),ijk(3)) + & + cnt_(e_bin,ijk(1),ijk(2),ijk(3)) = cnt_(e_bin,ijk(1),ijk(2),ijk(3)) + & bank_array(i) % wgt end do FISSION_SITES #ifdef MPI - ! determine total number of mesh cells - n = n_groups * size(cnt,2) * size(cnt,3) * size(cnt,4) - ! collect values from all processors - if (master) then - call MPI_REDUCE(MPI_IN_PLACE, cnt, n, MPI_REAL8, MPI_SUM, 0, & - mpi_intracomm, mpi_err) - else - ! Receive buffer not significant at other processors - call MPI_REDUCE(cnt, dummy, n, MPI_REAL8, MPI_SUM, 0, & - mpi_intracomm, mpi_err) - end if + n = size(cnt_) + call MPI_REDUCE(cnt_, cnt, n, MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err) ! Check if there were sites outside the mesh for any processor if (present(sites_outside)) then call MPI_REDUCE(outside, sites_outside, 1, MPI_LOGICAL, MPI_LOR, 0, & mpi_intracomm, mpi_err) end if - #else sites_outside = outside #endif diff --git a/src/simulation.F90 b/src/simulation.F90 index 7edaf8be22..ced5a74f1f 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -448,14 +448,12 @@ contains subroutine reduce_overlap_count() + integer(8) :: temp + #ifdef MPI - if (master) then - call MPI_REDUCE(MPI_IN_PLACE, overlap_check_cnt, n_cells, & - MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) - else - call MPI_REDUCE(overlap_check_cnt, overlap_check_cnt, n_cells, & - MPI_INTEGER8, MPI_SUM, 0, mpi_intracomm, mpi_err) - end if + call MPI_REDUCE(overlap_check_cnt, temp, n_cells, MPI_INTEGER8, & + MPI_SUM, 0, mpi_intracomm, mpi_err) + overlap_check_cnt = temp #endif end subroutine reduce_overlap_count diff --git a/src/state_point.F90 b/src/state_point.F90 index f48b5638b4..301a4efdd4 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -539,18 +539,15 @@ contains tallies_group = open_group(file_id, "tallies") end if - ! Copy global tallies into temporary array for reducing - n_bins = 3 * N_GLOBAL_TALLIES - global_temp(:,:) = global_tallies(:,:) - if (master) then - ! The MPI_IN_PLACE specifier allows the master to copy values into a - ! receive buffer without having a temporary variable #ifdef MPI - call MPI_REDUCE(MPI_IN_PLACE, global_temp, n_bins, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) + ! Reduce global tallies + n_bins = size(global_tallies) + call MPI_REDUCE(global_tallies, global_temp, n_bins, MPI_REAL8, MPI_SUM, & + 0, mpi_intracomm, mpi_err) #endif + if (master) then ! Transfer values to value on master if (current_batch == n_max_batches .or. satisfy_triggers) then global_tallies(:,:) = global_temp(:,:) @@ -558,12 +555,6 @@ contains ! Write out global tallies sum and sum_sq call write_dataset(file_id, "global_tallies", global_temp) - else - ! Receive buffer not significant at other processors -#ifdef MPI - call MPI_REDUCE(global_temp, dummy, n_bins, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) -#endif end if if (tallies_on) then diff --git a/src/tally.F90 b/src/tally.F90 index b8e46678ca..8f871e39be 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4249,13 +4249,12 @@ contains integer :: n ! number of filter bins integer :: m ! number of score bins integer :: n_bins ! total number of bins - real(8), allocatable :: tally_temp(:,:) ! contiguous array of results - real(8) :: global_temp(N_GLOBAL_TALLIES) + real(C_DOUBLE), allocatable :: tally_temp(:,:) ! contiguous array of results + real(C_DOUBLE) :: temp(N_GLOBAL_TALLIES), temp2(N_GLOBAL_TALLIES) real(8) :: dummy ! temporary receive buffer for non-root reduces - type(TallyObject), pointer :: t do i = 1, active_tallies % size() - t => tallies(active_tallies % data(i)) + associate (t => tallies(active_tallies % data(i))) m = t % total_score_bins n = t % total_filter_bins @@ -4282,37 +4281,26 @@ contains t % results(RESULT_VALUE,:,:) = ZERO end if + end associate + deallocate(tally_temp) end do - ! Copy global tallies into array to be reduced - global_temp = global_tallies(RESULT_VALUE, :) - + ! Reduce global tallies onto master + temp = global_tallies(RESULT_VALUE, :) + call MPI_REDUCE(temp, temp2, N_GLOBAL_TALLIES, MPI_DOUBLE, MPI_SUM, & + 0, mpi_intracomm, mpi_err) if (master) then - call MPI_REDUCE(MPI_IN_PLACE, global_temp, N_GLOBAL_TALLIES, & - MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err) - - ! Transfer values back to global_tallies on master - global_tallies(RESULT_VALUE, :) = global_temp + global_tallies(RESULT_VALUE, :) = temp2 else - ! Receive buffer not significant at other processors - call MPI_REDUCE(global_temp, dummy, N_GLOBAL_TALLIES, & - MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err) - - ! Reset value on other processors global_tallies(RESULT_VALUE, :) = ZERO end if ! We also need to determine the total starting weight of particles from the ! last realization - if (master) then - call MPI_REDUCE(MPI_IN_PLACE, total_weight, 1, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) - else - ! Receive buffer not significant at other processors - call MPI_REDUCE(total_weight, dummy, 1, MPI_REAL8, MPI_SUM, & - 0, mpi_intracomm, mpi_err) - end if + temp(1) = total_weight + call MPI_REDUCE(temp, total_weight, 1, MPI_REAL8, MPI_SUM, & + 0, mpi_intracomm, mpi_err) end subroutine reduce_tally_results #endif From ba318df3c42d8706bb0a6ea2b2fe9d4ec68e512d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 15:25:06 -0500 Subject: [PATCH 43/66] Don't use MPI_IN_PLACE for reducing tally results --- src/tally.F90 | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/tally.F90 b/src/tally.F90 index 8f871e39be..8b52ad0322 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4249,41 +4249,34 @@ contains integer :: n ! number of filter bins integer :: m ! number of score bins integer :: n_bins ! total number of bins - real(C_DOUBLE), allocatable :: tally_temp(:,:) ! contiguous array of results + real(C_DOUBLE), allocatable :: tally_temp(:,:) ! contiguous array of results + real(C_DOUBLE), allocatable :: tally_temp2(:,:) ! reduced contiguous results real(C_DOUBLE) :: temp(N_GLOBAL_TALLIES), temp2(N_GLOBAL_TALLIES) - real(8) :: dummy ! temporary receive buffer for non-root reduces do i = 1, active_tallies % size() associate (t => tallies(active_tallies % data(i))) - m = t % total_score_bins - n = t % total_filter_bins - n_bins = m*n + m = size(t % results, 2) + n = size(t % results, 3) + n_bins = m*n - allocate(tally_temp(m,n)) + allocate(tally_temp(m,n), tally_temp2(m,n)) - tally_temp = t % results(RESULT_VALUE,:,:) - - if (master) then - ! The MPI_IN_PLACE specifier allows the master to copy values into - ! a receive buffer without having a temporary variable - call MPI_REDUCE(MPI_IN_PLACE, tally_temp, n_bins, MPI_REAL8, & + ! Reduce contiguous set of tally results + tally_temp = t % results(RESULT_VALUE,:,:) + call MPI_REDUCE(tally_temp, tally_temp2, n_bins, MPI_DOUBLE, & MPI_SUM, 0, mpi_intracomm, mpi_err) - ! Transfer values to value on master - t % results(RESULT_VALUE,:,:) = tally_temp - else - ! Receive buffer not significant at other processors - call MPI_REDUCE(tally_temp, dummy, n_bins, MPI_REAL8, & - MPI_SUM, 0, mpi_intracomm, mpi_err) - - ! Reset value on other processors - t % results(RESULT_VALUE,:,:) = ZERO - end if + if (master) then + ! Transfer values to value on master + t % results(RESULT_VALUE,:,:) = tally_temp2 + else + ! Reset value on other processors + t % results(RESULT_VALUE,:,:) = ZERO + end if + deallocate(tally_temp, tally_temp2) end associate - - deallocate(tally_temp) end do ! Reduce global tallies onto master From b4b7e3f7c62b478cd4df0daeee24ce00cd495051 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 27 Jul 2017 16:02:02 -0500 Subject: [PATCH 44/66] Fix two bugs --- src/mesh.F90 | 1 + src/simulation.F90 | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mesh.F90 b/src/mesh.F90 index 393df5dda4..427710790a 100644 --- a/src/mesh.F90 +++ b/src/mesh.F90 @@ -207,6 +207,7 @@ contains end if #else sites_outside = outside + cnt = cnt_ #endif end subroutine count_bank_sites diff --git a/src/simulation.F90 b/src/simulation.F90 index ced5a74f1f..8a32493cac 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -406,11 +406,13 @@ contains #ifdef MPI ! Broadcast tally results so that each process has access to results - do i = 1, size(tallies) - n = size(tallies(i) % results) - call MPI_BCAST(tallies(i) % results, n, MPI_DOUBLE, 0, & - mpi_intracomm, mpi_err) - end do + if (allocated(tallies)) then + do i = 1, size(tallies) + n = size(tallies(i) % results) + call MPI_BCAST(tallies(i) % results, n, MPI_DOUBLE, 0, & + mpi_intracomm, mpi_err) + end do + end if ! Also broadcast global tally results n = size(global_tallies) From 3a6ee672c61609e62ca0d4623fabe7b28f5b1e94 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 28 Jul 2017 10:31:28 -0500 Subject: [PATCH 45/66] Add three more functions for getting/setting nuclide densities in materials --- openmc/capi.py | 88 ++++++++++++++++++++++++++++++++++++++++ src/api.F90 | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/openmc/capi.py b/openmc/capi.py index f4c9a7f3ce..2164a9d812 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -12,6 +12,7 @@ __all__ = ['OpenMCLibrary', 'lib', 'lib_context'] _int3 = c_int*3 _double3 = c_double*3 +_int_array = POINTER(POINTER(c_int)) _double_array = POINTER(POINTER(c_double)) @@ -47,14 +48,25 @@ class OpenMCLibrary(object): self._dll.openmc_get_keff.restype = c_int self._dll.openmc_load_nuclide.argtypes = [c_char_p] self._dll.openmc_load_nuclide.restype = c_int + + # Material interface self._dll.openmc_material_add_nuclide.argtypes = [ c_int32, c_char_p, c_double] self._dll.openmc_material_add_nuclide.restype = c_int self._dll.openmc_material_get_densities.argtypes = [ c_int32, _double_array] self._dll.openmc_material_get_densities.restype = c_int + self._dll.openmc_material_get_nuclides.argtypes = [ + c_int32, _int_array] + self._dll.openmc_material_get_nuclides.restype = c_int self._dll.openmc_material_set_density.argtypes = [c_int32, c_double] self._dll.openmc_material_set_density.restype = c_int + self._dll.openmc_material_set_densities.argtypes = [ + c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] + self._dll.openmc_material_set_densities.restype = c_int + + self._dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] + self._dll.openmc_nuclide_name.restype = c_int self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None @@ -215,6 +227,27 @@ class OpenMCLibrary(object): else: return None + def material_get_nuclides(self, mat_id): + """Get list of nuclides in a material. + + Parameters + ---------- + mat_id : int + ID of the material + + Returns + ------- + list of str + Nuclides in specified material + + """ + data = POINTER(c_int)() + n = self._dll.openmc_material_get_nuclides(mat_id, data) + if data: + return [self.nuclide_name(data[i]) for i in range(n)] + else: + return None + def material_set_density(self, mat_id, density): """Set density of a material. @@ -233,6 +266,61 @@ class OpenMCLibrary(object): """ return self._dll.openmc_material_set_density(mat_id, density) + def material_set_densities(self, mat_id, nuclides, densities): + """Set the densities of a list of nuclides in a material + + Parameters + ---------- + mat_id : int + ID of the material + nuclides : iterable of str + Nuclide names + densities : iterable of float + Corresponding densities in atom/b-cm + + Returns + ------- + int + Return status (negative if an error occurs). + + """ + # Convert strings to an array of char* + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + + # Get numpy array as a double* + d = np.asarray(densities) + dp = d.ctypes.data_as(POINTER(c_double)) + + return self._dll.openmc_material_set_densities( + mat_id, len(nuclides), nucs, dp) + + def nuclide_name(self, index): + """Name of nuclide with given index + + Parameter + --------- + index : int + Index in internal nuclides array + + Returns + ------- + str + Name of nuclide + + """ + name = c_char_p() + err = self._dll.openmc_nuclide_name(index, name) + + # Find blank in name + if err == 0: + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() + else: + return None + def plot_geometry(self): """Plot geometry""" return self._dll.openmc_plot_geometry() diff --git a/src/api.F90 b/src/api.F90 index dc9548f107..50beddd5db 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -19,6 +19,8 @@ module openmc_api use simulation, only: openmc_run use volume_calc, only: openmc_calculate_volumes + implicit none + private public :: openmc_calculate_volumes public :: openmc_cell_set_temperature @@ -29,7 +31,10 @@ module openmc_api public :: openmc_load_nuclide public :: openmc_material_add_nuclide public :: openmc_material_get_densities + public :: openmc_material_get_nuclides public :: openmc_material_set_density + public :: openmc_material_set_densities + public :: openmc_nuclide_name public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run @@ -202,6 +207,7 @@ contains character(kind=C_CHAR) :: name(*) integer(C_INT) :: err + integer :: i_library integer :: n integer(HID_T) :: file_id integer(HID_T) :: group_id @@ -337,6 +343,8 @@ contains type(C_PTR), intent(out) :: ptr integer(C_INT) :: n + integer :: i + ptr = C_NULL_PTR n = 0 if (allocated(materials)) then @@ -352,6 +360,55 @@ contains end if end function openmc_material_get_densities +!=============================================================================== +! OPENMC_MATERIAL_GET_NUCLIDES returns an array that indicates for each nuclide +! in a material its index in the global nuclides array +!=============================================================================== + + function openmc_material_get_nuclides(id, ptr) result(n) bind(C) + integer(C_INT32_T), intent(in), value :: id + type(C_PTR), intent(out) :: ptr + integer(C_INT) :: n + + integer :: i + + ptr = C_NULL_PTR + n = 0 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + if (allocated(m % nuclide)) then + ptr = C_LOC(m % nuclide(1)) + n = size(m % nuclide) + end if + end associate + end if + end if + end function openmc_material_get_nuclides + +!=============================================================================== +! OPENMC_NUCLIDE_NAME returns the name of a nuclide with a given index +!=============================================================================== + + function openmc_nuclide_name(index, name) result(err) bind(C) + integer(C_INT), value, intent(in) :: index + type(c_ptr), intent(out) :: name + integer(C_INT) :: err + + character(C_CHAR), pointer :: name_ + + err = -1 + name = C_NULL_PTR + if (allocated(nuclides)) then + if (index >= 1 .and. index <= size(nuclides)) then + name_ => nuclides(index) % name(1:1) + name = C_LOC(name_) + err = 0 + end if + end if + end function openmc_nuclide_name + !=============================================================================== ! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm !=============================================================================== @@ -374,6 +431,57 @@ contains end if end function openmc_material_set_density +!=============================================================================== +! OPENMC_MATERIAL_SET_DENSITIES sets the densities for a list of nuclides in a +! material. If the nuclides don't already exist in the material, they will be +! added +!=============================================================================== + + function openmc_material_set_densities(id, n, name, density) result(err) bind(C) + integer(C_INT32_T), value, intent(in) :: id + integer(C_INT), value, intent(in) :: n + type(C_PTR), intent(in) :: name(n) + real(C_DOUBLE), intent(in) :: density(n) + integer(C_INT) :: err + + integer :: i, j, k + logical :: has_nuclide + character(C_CHAR), pointer :: string(:) + character(len=:, kind=C_CHAR), allocatable :: name_ + + err = -1 + if (allocated(materials)) then + if (material_dict % has_key(id)) then + i = material_dict % get_key(id) + associate (m => materials(i)) + do i = 1, n + ! Convert C string to Fortran string + call c_f_pointer(name(i), string, [10]) + name_ = to_f_string(string) + + ! Find corresponding nuclide and set density + has_nuclide = .false. + do j = 1, size(m % nuclide) + k = m % nuclide(j) + if (nuclides(k) % name == name_) then + m % atom_density(j) = density(i) + has_nuclide = .true. + end if + end do + + ! If nuclide wasn't found, try to load it + err = openmc_material_add_nuclide(id, string, density(i)) + if (err /= 0) return + end do + + ! Set total density to the sum of the vector + err = m % set_density(sum(m % atom_density), nuclides) + end associate + end if + end if + + end function openmc_material_set_densities + !=============================================================================== ! OPENMC_RESET resets all tallies !=============================================================================== From 851e50afbcdff9385efa8f089626a99d57dca1a3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 28 Jul 2017 14:59:29 -0500 Subject: [PATCH 46/66] Awesome error handling thanks to ctypes --- openmc/capi.py | 150 ++++++++++++++++++++++++++++--------------------- src/api.F90 | 112 +++++++++++++++++++++++++++--------- 2 files changed, 171 insertions(+), 91 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 2164a9d812..a2225b571d 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -16,6 +16,53 @@ _int_array = POINTER(POINTER(c_int)) _double_array = POINTER(POINTER(c_double)) +class GeometryError(Exception): + pass + + +def _error_code(s): + """Get error code corresponding to global constant.""" + return c_int.in_dll(lib._dll, s).value + + +def _error_handler(err, func, args): + """Raise exception according to error code.""" + if err == _error_code('e_out_of_bounds'): + raise IndexError('Array index out of bounds.') + + elif err == _error_code('e_cell_not_allocated'): + raise MemoryError("Memory has not been allocated for cells.") + + elif err == _error_code('e_cell_invalid_id'): + raise KeyError("No cell exists with ID={}.".format(args[0])) + + elif err == _error_code('e_cell_not_found'): + raise GeometryError("Could not find cell at position ({}, {}, {})" + .format(*args[0])) + + elif err == _error_code('e_nuclide_not_allocated'): + raise MemoryError("Memory has not been allocated for nuclides.") + + elif err == _error_code('e_nuclide_not_in_library'): + raise KeyError("Specified nuclide doesn't exist in the cross " + "section library.") + + elif err == _error_code('e_material_not_allocated'): + raise MemoryError("Memory has not been allocated for materials.") + + elif err == _error_code('e_material_invalid_id'): + raise KeyError("No material exists with ID={}.".format(args[0])) + + elif err == _error_code('e_tally_not_allocated'): + raise MemoryError("Memory has not been allocated for tallies.") + + elif err == _error_code('e_tally_invalid_id'): + raise KeyError("No tally exists with ID={}.".format(args[0])) + + elif err < 0: + raise Exception("Unknown error encountered (code {}).".format(err)) + + class OpenMCLibrary(object): """Provides bindings to C functions defined by OpenMC shared library. @@ -36,47 +83,58 @@ class OpenMCLibrary(object): # Set argument/return types self._dll.openmc_calculate_volumes.restype = None self._dll.openmc_cell_set_temperature.argtypes = [ - c_int32, c_double, c_int32] + c_int32, c_double, POINTER(c_int32)] self._dll.openmc_cell_set_temperature.restype = c_int + self._dll.openmc_cell_set_temperature.errcheck = _error_handler self._dll.openmc_finalize.restype = None self._dll.openmc_find.argtypes = [ POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] - self._dll.openmc_find.restype = None + self._dll.openmc_find.restype = c_int + self._dll.openmc_find.errcheck = _error_handler self._dll.openmc_init.argtypes = [POINTER(c_int)] self._dll.openmc_init.restype = None self._dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] self._dll.openmc_get_keff.restype = c_int + self._dll.openmc_get_keff.errcheck = _error_handler self._dll.openmc_load_nuclide.argtypes = [c_char_p] self._dll.openmc_load_nuclide.restype = c_int + self._dll.openmc_load_nuclide.errcheck = _error_handler # Material interface self._dll.openmc_material_add_nuclide.argtypes = [ c_int32, c_char_p, c_double] self._dll.openmc_material_add_nuclide.restype = c_int + self._dll.openmc_material_add_nuclide.errcheck = _error_handler self._dll.openmc_material_get_densities.argtypes = [ - c_int32, _double_array] + c_int32, _double_array, POINTER(c_int)] self._dll.openmc_material_get_densities.restype = c_int + self._dll.openmc_material_get_densities.errcheck = _error_handler self._dll.openmc_material_get_nuclides.argtypes = [ - c_int32, _int_array] + c_int32, _int_array, POINTER(c_int)] self._dll.openmc_material_get_nuclides.restype = c_int + self._dll.openmc_material_get_nuclides.errcheck = _error_handler self._dll.openmc_material_set_density.argtypes = [c_int32, c_double] self._dll.openmc_material_set_density.restype = c_int + self._dll.openmc_material_set_density.errcheck = _error_handler self._dll.openmc_material_set_densities.argtypes = [ c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] self._dll.openmc_material_set_densities.restype = c_int + self._dll.openmc_material_set_densities.errcheck = _error_handler self._dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] self._dll.openmc_nuclide_name.restype = c_int + self._dll.openmc_nuclide_name.errcheck = _error_handler self._dll.openmc_plot_geometry.restype = None self._dll.openmc_run.restype = None self._dll.openmc_reset.restype = None self._dll.openmc_tally_results.argtypes = [ c_int32, _double_array, POINTER(_int3)] - self._dll.openmc_tally_results.restype = None + self._dll.openmc_tally_results.restype = c_int + self._dll.openmc_tally_results.errcheck = _error_handler def calculate_volumes(self): """Run stochastic volume calculation""" - return self._dll.openmc_calculate_volumes() + self._dll.openmc_calculate_volumes() def cell_set_temperature(self, cell_id, T, instance=None): """Set the temperature of a cell @@ -91,15 +149,11 @@ class OpenMCLibrary(object): Which instance of the cell """ - if instance is not None: - return self._dll.openmc_cell_set_temperature( - cell_id, T, instance) - else: - return self._dll.openmc_cell_set_temperature(cell_id, T, None) + self._dll.openmc_cell_set_temperature(cell_id, T, instance) def finalize(self): """Finalize simulation and free memory""" - return self._dll.openmc_finalize() + self._dll.openmc_finalize() def find(self, xyz, rtype='cell'): """Find the cell or material at a given point @@ -151,9 +205,9 @@ class OpenMCLibrary(object): intracomm = intracomm.py2f() except AttributeError: pass - return self._dll.openmc_init(c_int(intracomm)) + self._dll.openmc_init(c_int(intracomm)) else: - return self._dll.openmc_init(None) + self._dll.openmc_init(None) def keff(self): """Return the calculated k-eigenvalue and its standard deviation. @@ -165,7 +219,7 @@ class OpenMCLibrary(object): """ k = (c_double*2)() - err = self._dll.openmc_get_keff(k) + self._dll.openmc_get_keff(k) return tuple(k) def load_nuclide(self, name): @@ -177,13 +231,8 @@ class OpenMCLibrary(object): name : str Name of nuclide, e.g. 'U235' - Returns - ------- - int - Return status (negative if an error occurs). - """ - return self._dll.openmc_load_nuclide(name.encode()) + self._dll.openmc_load_nuclide(name.encode()) def material_add_nuclide(self, mat_id, name, density): """Add a nuclide to a material. @@ -197,14 +246,8 @@ class OpenMCLibrary(object): density : float Density in atom/b-cm - Returns - ------- - int - Return status (negative if an error occurs). - """ - return self._dll.openmc_material_add_nuclide( - mat_id, name.encode(), density) + self._dll.openmc_material_add_nuclide(mat_id, name.encode(), density) def material_get_densities(self, mat_id): """Get atom densities in a material. @@ -221,11 +264,9 @@ class OpenMCLibrary(object): """ data = POINTER(c_double)() - n = self._dll.openmc_material_get_densities(mat_id, data) - if data: - return as_array(data, (n,)) - else: - return None + n = c_int() + self._dll.openmc_material_get_densities(mat_id, data, n) + return as_array(data, (n.value,)) def material_get_nuclides(self, mat_id): """Get list of nuclides in a material. @@ -242,11 +283,9 @@ class OpenMCLibrary(object): """ data = POINTER(c_int)() - n = self._dll.openmc_material_get_nuclides(mat_id, data) - if data: - return [self.nuclide_name(data[i]) for i in range(n)] - else: - return None + n = c_int() + self._dll.openmc_material_get_nuclides(mat_id, data, n) + return [self.nuclide_name(data[i]) for i in range(n.value)] def material_set_density(self, mat_id, density): """Set density of a material. @@ -258,13 +297,8 @@ class OpenMCLibrary(object): density : float Density in atom/b-cm - Returns - ------- - int - Return status (negative if an error occurs). - """ - return self._dll.openmc_material_set_density(mat_id, density) + self._dll.openmc_material_set_density(mat_id, density) def material_set_densities(self, mat_id, nuclides, densities): """Set the densities of a list of nuclides in a material @@ -278,11 +312,6 @@ class OpenMCLibrary(object): densities : iterable of float Corresponding densities in atom/b-cm - Returns - ------- - int - Return status (negative if an error occurs). - """ # Convert strings to an array of char* nucs = (c_char_p * len(nuclides))() @@ -292,8 +321,7 @@ class OpenMCLibrary(object): d = np.asarray(densities) dp = d.ctypes.data_as(POINTER(c_double)) - return self._dll.openmc_material_set_densities( - mat_id, len(nuclides), nucs, dp) + self._dll.openmc_material_set_densities(mat_id, len(nuclides), nucs, dp) def nuclide_name(self, index): """Name of nuclide with given index @@ -310,28 +338,25 @@ class OpenMCLibrary(object): """ name = c_char_p() - err = self._dll.openmc_nuclide_name(index, name) + self._dll.openmc_nuclide_name(index, name) # Find blank in name - if err == 0: - i = 0 - while name.value[i:i+1] != b' ': - i += 1 - return name.value[:i].decode() - else: - return None + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() def plot_geometry(self): """Plot geometry""" - return self._dll.openmc_plot_geometry() + self._dll.openmc_plot_geometry() def reset(self): """Reset tallies""" - return self._dll.openmc_reset() + self._dll.openmc_reset() def run(self): """Run simulation""" - return self._dll.openmc_run() + self._dll.openmc_run() def tally_results(self, tally_id): """Get tally results array @@ -363,7 +388,6 @@ class OpenMCLibrary(object): raise AttributeError("OpenMC library doesn't have a '{}' function" .format(key)) - @contextmanager def lib_context(intracomm=None): """Provides context manager for calling OpenMC shared library functions. diff --git a/src/api.F90 b/src/api.F90 index 50beddd5db..3bea4af99d 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -40,6 +40,19 @@ module openmc_api public :: openmc_run public :: openmc_tally_results + ! Error codes + integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1 + integer(C_INT), public, bind(C) :: E_OUT_OF_BOUNDS = -2 + integer(C_INT), public, bind(C) :: E_CELL_NOT_ALLOCATED = -3 + integer(C_INT), public, bind(C) :: E_CELL_INVALID_ID = -4 + integer(C_INT), public, bind(C) :: E_CELL_NOT_FOUND = -5 + integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_ALLOCATED = -6 + integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_IN_LIBRARY = -7 + integer(C_INT), public, bind(C) :: E_MATERIAL_NOT_ALLOCATED = -8 + integer(C_INT), public, bind(C) :: E_MATERIAL_INVALID_ID = -9 + integer(C_INT), public, bind(C) :: E_TALLY_NOT_ALLOCATED = -10 + integer(C_INT), public, bind(C) :: E_TALLY_INVALID_ID = -11 + contains !=============================================================================== @@ -54,7 +67,7 @@ contains integer :: i, n - err = -1 + err = E_UNASSIGNED if (allocated(cells)) then if (cell_dict % has_key(id)) then i = cell_dict % get_key(id) @@ -72,7 +85,11 @@ contains end if end if end associate + else + err = E_CELL_INVALID_ID end if + else + err = E_CELL_NOT_ALLOCATED end if end function openmc_cell_set_temperature @@ -169,11 +186,12 @@ contains ! OPENMC_FIND determines the ID or a cell or material at a given point in space !=============================================================================== - subroutine openmc_find(xyz, rtype, id, instance) bind(C) + function openmc_find(xyz, rtype, id, instance) result(err) bind(C) real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material integer(C_INT32_T), intent(out) :: id integer(C_INT32_T), intent(out) :: instance + integer(C_INT) :: err logical :: found type(Particle) :: p @@ -185,6 +203,8 @@ contains id = -1 instance = -1 + err = E_UNASSIGNED + if (found) then if (rtype == 1) then id = cells(p % coord(p % n_coord) % cell) % id @@ -196,8 +216,12 @@ contains end if end if instance = p % cell_instance - 1 + err = 0 + else + err = E_CELL_NOT_FOUND end if - end subroutine openmc_find + + end function openmc_find !=============================================================================== ! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library @@ -219,7 +243,7 @@ contains ! Copy array of C_CHARs to normal Fortran string name_ = to_f_string(name) - err = -1 + err = 0 if (.not. nuclide_dict % has_key(to_lower(name_))) then if (library_dict % has_key(to_lower(name_))) then ! allocate extra space in nuclides array @@ -253,10 +277,8 @@ contains ! Initialize nuclide grid call nuclides(n) % init_grid(energy_min_neutron, & energy_max_neutron, n_log_bins) - - err = 0 else - err = -2 + err = E_NUCLIDE_NOT_IN_LIBRARY end if end if @@ -273,7 +295,6 @@ contains integer(C_INT) :: err integer :: i, j, k, n - integer :: err2 real(8) :: awr integer, allocatable :: new_nuclide(:) real(8), allocatable :: new_density(:) @@ -281,7 +302,7 @@ contains name_ = to_f_string(name) - err = -1 + err = E_UNASSIGNED if (allocated(materials)) then if (material_dict % has_key(id)) then i = material_dict % get_key(id) @@ -302,9 +323,9 @@ contains ! If nuclide wasn't found, extend nuclide/density arrays if (err /= 0) then ! If nuclide hasn't been loaded, load it now - err2 = openmc_load_nuclide(name) + err = openmc_load_nuclide(name) - if (err2 /= -2) then + if (err == 0) then ! Extend arrays n = size(m % nuclide) allocate(new_nuclide(n + 1)) @@ -323,12 +344,14 @@ contains m % density_gpcc = m % density_gpcc + & density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO m % n_nuclides = n + 1 - - err = 0 end if end if end associate + else + err = E_MATERIAL_INVALID_ID end if + else + err = E_MATERIAL_NOT_ALLOCATED end if end function openmc_material_add_nuclide @@ -338,15 +361,17 @@ contains ! material !=============================================================================== - function openmc_material_get_densities(id, ptr) result(n) bind(C) + function openmc_material_get_densities(id, ptr, n) result(err) bind(C) integer(C_INT32_T), intent(in), value :: id type(C_PTR), intent(out) :: ptr - integer(C_INT) :: n + integer(C_INT), intent(out) :: n + integer(C_INT) :: err integer :: i ptr = C_NULL_PTR - n = 0 + n = -1 + err = E_UNASSIGNED if (allocated(materials)) then if (material_dict % has_key(id)) then i = material_dict % get_key(id) @@ -354,9 +379,14 @@ contains if (allocated(m % atom_density)) then ptr = C_LOC(m % atom_density(1)) n = size(m % atom_density) + err = 0 end if end associate + else + err = E_MATERIAL_INVALID_ID end if + else + err = E_MATERIAL_NOT_ALLOCATED end if end function openmc_material_get_densities @@ -365,15 +395,17 @@ contains ! in a material its index in the global nuclides array !=============================================================================== - function openmc_material_get_nuclides(id, ptr) result(n) bind(C) + function openmc_material_get_nuclides(id, ptr, n) result(err) bind(C) integer(C_INT32_T), intent(in), value :: id type(C_PTR), intent(out) :: ptr - integer(C_INT) :: n + integer(C_INT), intent(out) :: n + integer(C_INT) :: err integer :: i ptr = C_NULL_PTR - n = 0 + n = -1 + err = E_UNASSIGNED if (allocated(materials)) then if (material_dict % has_key(id)) then i = material_dict % get_key(id) @@ -381,9 +413,14 @@ contains if (allocated(m % nuclide)) then ptr = C_LOC(m % nuclide(1)) n = size(m % nuclide) + err = 0 end if end associate + else + err = E_MATERIAL_INVALID_ID end if + else + err = E_MATERIAL_NOT_ALLOCATED end if end function openmc_material_get_nuclides @@ -398,14 +435,18 @@ contains character(C_CHAR), pointer :: name_ - err = -1 + err = E_UNASSIGNED name = C_NULL_PTR if (allocated(nuclides)) then if (index >= 1 .and. index <= size(nuclides)) then name_ => nuclides(index) % name(1:1) name = C_LOC(name_) err = 0 + else + err = E_OUT_OF_BOUNDS end if + else + err = E_NUCLIDE_NOT_ALLOCATED end if end function openmc_nuclide_name @@ -427,7 +468,11 @@ contains associate (m => materials(i)) err = m % set_density(density, nuclides) end associate + else + err = E_MATERIAL_INVALID_ID end if + else + err = E_MATERIAL_NOT_ALLOCATED end if end function openmc_material_set_density @@ -445,11 +490,10 @@ contains integer(C_INT) :: err integer :: i, j, k - logical :: has_nuclide character(C_CHAR), pointer :: string(:) character(len=:, kind=C_CHAR), allocatable :: name_ - err = -1 + err = E_UNASSIGNED if (allocated(materials)) then if (material_dict % has_key(id)) then i = material_dict % get_key(id) @@ -460,24 +504,29 @@ contains name_ = to_f_string(string) ! Find corresponding nuclide and set density - has_nuclide = .false. do j = 1, size(m % nuclide) k = m % nuclide(j) if (nuclides(k) % name == name_) then m % atom_density(j) = density(i) - has_nuclide = .true. + err = 0 end if end do ! If nuclide wasn't found, try to load it - err = openmc_material_add_nuclide(id, string, density(i)) - if (err /= 0) return + if (err /= 0) then + err = openmc_material_add_nuclide(id, string, density(i)) + if (err /= 0) return + end if end do ! Set total density to the sum of the vector err = m % set_density(sum(m % atom_density), nuclides) end associate + else + err = E_MATERIAL_INVALID_ID end if + else + err = E_MATERIAL_NOT_ALLOCATED end if end function openmc_material_set_densities @@ -527,24 +576,31 @@ contains ! directly. !=============================================================================== - subroutine openmc_tally_results(id, ptr, shape_) bind(C) + function openmc_tally_results(id, ptr, shape_) result(err) bind(C) integer(C_INT32_T), intent(in), value :: id type(C_PTR), intent(out) :: ptr integer(C_INT), intent(out) :: shape_(3) + integer(C_INT) :: err integer :: i ptr = C_NULL_PTR + err = E_UNASSIGNED if (allocated(tallies)) then if (tally_dict % has_key(id)) then i = tally_dict % get_key(id) if (allocated(tallies(i) % results)) then ptr = C_LOC(tallies(i) % results(1,1,1)) shape_(:) = shape(tallies(i) % results) + err = 0 end if + else + err = E_TALLY_INVALID_ID end if + else + err = E_TALLY_NOT_ALLOCATED end if - end subroutine openmc_tally_results + end function openmc_tally_results function to_f_string(c_string) result(f_string) character(kind=C_CHAR), intent(in) :: c_string(*) From 25e585881f8de46c37154f5b2bfc4841b9044003 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 28 Jul 2017 15:17:57 -0500 Subject: [PATCH 47/66] Combine material_get_densities and material_get_nuclides --- openmc/capi.py | 37 ++++++++++++------------------------- src/api.F90 | 47 ++++++++--------------------------------------- 2 files changed, 20 insertions(+), 64 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index a2225b571d..7712e0e3e3 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -106,13 +106,9 @@ class OpenMCLibrary(object): self._dll.openmc_material_add_nuclide.restype = c_int self._dll.openmc_material_add_nuclide.errcheck = _error_handler self._dll.openmc_material_get_densities.argtypes = [ - c_int32, _double_array, POINTER(c_int)] + c_int32, _int_array, _double_array, POINTER(c_int)] self._dll.openmc_material_get_densities.restype = c_int self._dll.openmc_material_get_densities.errcheck = _error_handler - self._dll.openmc_material_get_nuclides.argtypes = [ - c_int32, _int_array, POINTER(c_int)] - self._dll.openmc_material_get_nuclides.restype = c_int - self._dll.openmc_material_get_nuclides.errcheck = _error_handler self._dll.openmc_material_set_density.argtypes = [c_int32, c_double] self._dll.openmc_material_set_density.restype = c_int self._dll.openmc_material_set_density.errcheck = _error_handler @@ -259,33 +255,24 @@ class OpenMCLibrary(object): Returns ------- + list of string + List of nuclide names numpy.ndarray Array of densities in atom/b-cm """ - data = POINTER(c_double)() + # Allocate memory for arguments that are written to + nuclides = POINTER(c_int)() + densities = POINTER(c_double)() n = c_int() - self._dll.openmc_material_get_densities(mat_id, data, n) - return as_array(data, (n.value,)) - def material_get_nuclides(self, mat_id): - """Get list of nuclides in a material. + # Get nuclide names and densities + self._dll.openmc_material_get_densities(mat_id, nuclides, densities, n) - Parameters - ---------- - mat_id : int - ID of the material - - Returns - ------- - list of str - Nuclides in specified material - - """ - data = POINTER(c_int)() - n = c_int() - self._dll.openmc_material_get_nuclides(mat_id, data, n) - return [self.nuclide_name(data[i]) for i in range(n.value)] + # Convert to appropriate types and return + nuclide_list = [self.nuclide_name(nuclides[i]) for i in range(n.value)] + density_array = as_array(densities, (n.value,)) + return nuclide_list, density_array def material_set_density(self, mat_id, density): """Set density of a material. diff --git a/src/api.F90 b/src/api.F90 index 3bea4af99d..6b71bf2fc6 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -31,7 +31,6 @@ module openmc_api public :: openmc_load_nuclide public :: openmc_material_add_nuclide public :: openmc_material_get_densities - public :: openmc_material_get_nuclides public :: openmc_material_set_density public :: openmc_material_set_densities public :: openmc_nuclide_name @@ -361,15 +360,18 @@ contains ! material !=============================================================================== - function openmc_material_get_densities(id, ptr, n) result(err) bind(C) + function openmc_material_get_densities(id, nuclides, densities, n) & + result(err) bind(C) integer(C_INT32_T), intent(in), value :: id - type(C_PTR), intent(out) :: ptr + type(C_PTR), intent(out) :: nuclides + type(C_PTR), intent(out) :: densities integer(C_INT), intent(out) :: n integer(C_INT) :: err integer :: i - ptr = C_NULL_PTR + nuclides = C_NULL_PTR + densities = C_NULL_PTR n = -1 err = E_UNASSIGNED if (allocated(materials)) then @@ -377,7 +379,8 @@ contains i = material_dict % get_key(id) associate (m => materials(i)) if (allocated(m % atom_density)) then - ptr = C_LOC(m % atom_density(1)) + nuclides = C_LOC(m % nuclide(1)) + densities = C_LOC(m % atom_density(1)) n = size(m % atom_density) err = 0 end if @@ -390,40 +393,6 @@ contains end if end function openmc_material_get_densities -!=============================================================================== -! OPENMC_MATERIAL_GET_NUCLIDES returns an array that indicates for each nuclide -! in a material its index in the global nuclides array -!=============================================================================== - - function openmc_material_get_nuclides(id, ptr, n) result(err) bind(C) - integer(C_INT32_T), intent(in), value :: id - type(C_PTR), intent(out) :: ptr - integer(C_INT), intent(out) :: n - integer(C_INT) :: err - - integer :: i - - ptr = C_NULL_PTR - n = -1 - err = E_UNASSIGNED - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - if (allocated(m % nuclide)) then - ptr = C_LOC(m % nuclide(1)) - n = size(m % nuclide) - err = 0 - end if - end associate - else - err = E_MATERIAL_INVALID_ID - end if - else - err = E_MATERIAL_NOT_ALLOCATED - end if - end function openmc_material_get_nuclides - !=============================================================================== ! OPENMC_NUCLIDE_NAME returns the name of a nuclide with a given index !=============================================================================== From a33502e8fa1f482bceda48ffd55a583279c03956 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 28 Jul 2017 15:46:35 -0500 Subject: [PATCH 48/66] Get rid of OpenMCLibrary object and make all methods regular functions --- openmc/__init__.py | 2 +- openmc/capi.py | 647 ++++++++++++++++++++++----------------------- 2 files changed, 324 insertions(+), 325 deletions(-) diff --git a/openmc/__init__.py b/openmc/__init__.py index a0fe264911..571b5a93a4 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,6 +27,6 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * -from openmc.capi import * +import openmc.capi __version__ = '0.9.0' diff --git a/openmc/capi.py b/openmc/capi.py index 7712e0e3e3..2e6b17d432 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -1,3 +1,15 @@ +"""Provides bindings to C functions defined by OpenMC shared library. + +When the :mod:`openmc` package is imported, the OpenMC shared library is +automatically loaded. Calls to the OpenMC library can then be made, for example: + +.. code-block:: python + + openmc.capi.init() + openmc.capi.run() + +""" + from contextlib import contextmanager from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys @@ -5,11 +17,8 @@ from warnings import warn import numpy as np from numpy.ctypeslib import as_array - import pkg_resources -__all__ = ['OpenMCLibrary', 'lib', 'lib_context'] - _int3 = c_int*3 _double3 = c_double*3 _int_array = POINTER(POINTER(c_int)) @@ -22,7 +31,7 @@ class GeometryError(Exception): def _error_code(s): """Get error code corresponding to global constant.""" - return c_int.in_dll(lib._dll, s).value + return c_int.in_dll(_dll, s).value def _error_handler(err, func, args): @@ -63,332 +72,70 @@ def _error_handler(err, func, args): raise Exception("Unknown error encountered (code {}).".format(err)) -class OpenMCLibrary(object): - """Provides bindings to C functions defined by OpenMC shared library. +def calculate_volumes(): + """Run stochastic volume calculation""" + _dll.openmc_calculate_volumes() - This class is normally not directly instantiated. Instead, when the - :mod:`openmc` package is imported, an instance is automatically created with - the name :data:`openmc.lib`. Calls to the OpenMC can then be made using that - instance, for example: - .. code-block:: python +def cell_set_temperature(cell_id, T, instance=None): + """Set the temperature of a cell - openmc.lib.init() - openmc.lib.run() + Parameters + ---------- + cell_id : int + ID of the cell + T : float + Temperature in K + instance : int or None + Which instance of the cell """ - def __init__(self, filename): - self._dll = CDLL(filename) + _dll.openmc_cell_set_temperature(cell_id, T, instance) - # Set argument/return types - self._dll.openmc_calculate_volumes.restype = None - self._dll.openmc_cell_set_temperature.argtypes = [ - c_int32, c_double, POINTER(c_int32)] - self._dll.openmc_cell_set_temperature.restype = c_int - self._dll.openmc_cell_set_temperature.errcheck = _error_handler - self._dll.openmc_finalize.restype = None - self._dll.openmc_find.argtypes = [ - POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] - self._dll.openmc_find.restype = c_int - self._dll.openmc_find.errcheck = _error_handler - self._dll.openmc_init.argtypes = [POINTER(c_int)] - self._dll.openmc_init.restype = None - self._dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] - self._dll.openmc_get_keff.restype = c_int - self._dll.openmc_get_keff.errcheck = _error_handler - self._dll.openmc_load_nuclide.argtypes = [c_char_p] - self._dll.openmc_load_nuclide.restype = c_int - self._dll.openmc_load_nuclide.errcheck = _error_handler - # Material interface - self._dll.openmc_material_add_nuclide.argtypes = [ - c_int32, c_char_p, c_double] - self._dll.openmc_material_add_nuclide.restype = c_int - self._dll.openmc_material_add_nuclide.errcheck = _error_handler - self._dll.openmc_material_get_densities.argtypes = [ - c_int32, _int_array, _double_array, POINTER(c_int)] - self._dll.openmc_material_get_densities.restype = c_int - self._dll.openmc_material_get_densities.errcheck = _error_handler - self._dll.openmc_material_set_density.argtypes = [c_int32, c_double] - self._dll.openmc_material_set_density.restype = c_int - self._dll.openmc_material_set_density.errcheck = _error_handler - self._dll.openmc_material_set_densities.argtypes = [ - c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] - self._dll.openmc_material_set_densities.restype = c_int - self._dll.openmc_material_set_densities.errcheck = _error_handler - self._dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] - self._dll.openmc_nuclide_name.restype = c_int - self._dll.openmc_nuclide_name.errcheck = _error_handler - self._dll.openmc_plot_geometry.restype = None - self._dll.openmc_run.restype = None - self._dll.openmc_reset.restype = None - self._dll.openmc_tally_results.argtypes = [ - c_int32, _double_array, POINTER(_int3)] - self._dll.openmc_tally_results.restype = c_int - self._dll.openmc_tally_results.errcheck = _error_handler +def finalize(): + """Finalize simulation and free memory""" + _dll.openmc_finalize() - def calculate_volumes(self): - """Run stochastic volume calculation""" - self._dll.openmc_calculate_volumes() - def cell_set_temperature(self, cell_id, T, instance=None): - """Set the temperature of a cell +def find(xyz, rtype='cell'): + """Find the cell or material at a given point - Parameters - ---------- - cell_id : int - ID of the cell - T : float - Temperature in K - instance : int or None - Which instance of the cell + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates of position + rtype : {'cell', 'material'} + Whether to return the cell or material ID - """ - self._dll.openmc_cell_set_temperature(cell_id, T, instance) + Returns + ------- + int or None + ID of the cell or material. If 'material' is requested and no + material exists at the given coordinate, None is returned. + int + If the cell at the given point is repeated in the geometry, this + indicates which instance it is, i.e., 0 would be the first instance. - def finalize(self): - """Finalize simulation and free memory""" - self._dll.openmc_finalize() + """ + # Set second argument to openmc_find + if rtype == 'cell': + r_int = 1 + elif rtype == 'material': + r_int = 2 + else: + raise ValueError('Unknown return type: {}'.format(rtype)) - def find(self, xyz, rtype='cell'): - """Find the cell or material at a given point + # Call openmc_find + uid = c_int32() + instance = c_int32() + _dll.openmc_find(_double3(*xyz), r_int, uid, instance) + return (uid.value if uid != 0 else None), instance.value - Parameters - ---------- - xyz : iterable of float - Cartesian coordinates of position - rtype : {'cell', 'material'} - Whether to return the cell or material ID - Returns - ------- - int or None - ID of the cell or material. If 'material' is requested and no - material exists at the given coordinate, None is returned. - int - If the cell at the given point is repeated in the geometry, this - indicates which instance it is, i.e., 0 would be the first instance. - - """ - # Set second argument to openmc_find - if rtype == 'cell': - r_int = 1 - elif rtype == 'material': - r_int = 2 - else: - raise ValueError('Unknown return type: {}'.format(rtype)) - - # Call openmc_find - uid = c_int32() - instance = c_int32() - self._dll.openmc_find(_double3(*xyz), r_int, uid, instance) - return (uid.value if uid != 0 else None), instance.value - - def init(self, intracomm=None): - """Initialize OpenMC - - Parameters - ---------- - intracomm : mpi4py.MPI.Intracomm or None - MPI intracommunicator - - """ - if intracomm is not None: - # If an mpi4py communicator was passed, convert it to an integer to - # be passed to openmc_init - try: - intracomm = intracomm.py2f() - except AttributeError: - pass - self._dll.openmc_init(c_int(intracomm)) - else: - self._dll.openmc_init(None) - - def keff(self): - """Return the calculated k-eigenvalue and its standard deviation. - - Returns - ------- - tuple - Mean k-eigenvalue and standard deviation of the mean - - """ - k = (c_double*2)() - self._dll.openmc_get_keff(k) - return tuple(k) - - def load_nuclide(self, name): - - """Load cross section data for a nuclide. - - Parameters - ---------- - name : str - Name of nuclide, e.g. 'U235' - - """ - self._dll.openmc_load_nuclide(name.encode()) - - def material_add_nuclide(self, mat_id, name, density): - """Add a nuclide to a material. - - Parameters - ---------- - mat_id : int - ID of the material - name : str - Name of nuclide, e.g. 'U235' - density : float - Density in atom/b-cm - - """ - self._dll.openmc_material_add_nuclide(mat_id, name.encode(), density) - - def material_get_densities(self, mat_id): - """Get atom densities in a material. - - Parameters - ---------- - mat_id : int - ID of the material - - Returns - ------- - list of string - List of nuclide names - numpy.ndarray - Array of densities in atom/b-cm - - """ - # Allocate memory for arguments that are written to - nuclides = POINTER(c_int)() - densities = POINTER(c_double)() - n = c_int() - - # Get nuclide names and densities - self._dll.openmc_material_get_densities(mat_id, nuclides, densities, n) - - # Convert to appropriate types and return - nuclide_list = [self.nuclide_name(nuclides[i]) for i in range(n.value)] - density_array = as_array(densities, (n.value,)) - return nuclide_list, density_array - - def material_set_density(self, mat_id, density): - """Set density of a material. - - Parameters - ---------- - mat_id : int - ID of the material - density : float - Density in atom/b-cm - - """ - self._dll.openmc_material_set_density(mat_id, density) - - def material_set_densities(self, mat_id, nuclides, densities): - """Set the densities of a list of nuclides in a material - - Parameters - ---------- - mat_id : int - ID of the material - nuclides : iterable of str - Nuclide names - densities : iterable of float - Corresponding densities in atom/b-cm - - """ - # Convert strings to an array of char* - nucs = (c_char_p * len(nuclides))() - nucs[:] = [x.encode() for x in nuclides] - - # Get numpy array as a double* - d = np.asarray(densities) - dp = d.ctypes.data_as(POINTER(c_double)) - - self._dll.openmc_material_set_densities(mat_id, len(nuclides), nucs, dp) - - def nuclide_name(self, index): - """Name of nuclide with given index - - Parameter - --------- - index : int - Index in internal nuclides array - - Returns - ------- - str - Name of nuclide - - """ - name = c_char_p() - self._dll.openmc_nuclide_name(index, name) - - # Find blank in name - i = 0 - while name.value[i:i+1] != b' ': - i += 1 - return name.value[:i].decode() - - def plot_geometry(self): - """Plot geometry""" - self._dll.openmc_plot_geometry() - - def reset(self): - """Reset tallies""" - self._dll.openmc_reset() - - def run(self): - """Run simulation""" - self._dll.openmc_run() - - def tally_results(self, tally_id): - """Get tally results array - - Parameters - ---------- - tally_id : int - ID of tally - - Returns - ------- - numpy.ndarray - Array that exposes the internal tally results array - - """ - data = POINTER(c_double)() - shape = _int3() - self._dll.openmc_tally_results(tally_id, data, shape) - if data: - return as_array(data, tuple(shape[::-1])) - else: - return None - - def __getattr__(self, key): - # Fall-back for other functions that may be available from library - try: - return getattr(self._dll, 'openmc_{}'.format(key)) - except AttributeError: - raise AttributeError("OpenMC library doesn't have a '{}' function" - .format(key)) - -@contextmanager -def lib_context(intracomm=None): - """Provides context manager for calling OpenMC shared library functions. - - This function is intended to be used in a 'with' statement and ensures that - OpenMC is properly initialized/finalized. At the completion of the 'with' - block, all memory that was allocated during the block is freed. For - example:: - - with openmc.lib_context() as lib: - for i in range(n_iters): - lib.reset() - do_stuff() - lib.run() +def init(intracomm=None): + """Initialize OpenMC Parameters ---------- @@ -396,24 +143,276 @@ def lib_context(intracomm=None): MPI intracommunicator """ - lib.init(comm) - yield lib - lib.finalize() + if intracomm is not None: + # If an mpi4py communicator was passed, convert it to an integer to + # be passed to openmc_init + try: + intracomm = intracomm.py2f() + except AttributeError: + pass + _dll.openmc_init(c_int(intracomm)) + else: + _dll.openmc_init(None) + + +def keff(): + """Return the calculated k-eigenvalue and its standard deviation. + + Returns + ------- + tuple + Mean k-eigenvalue and standard deviation of the mean + + """ + k = (c_double*2)() + _dll.openmc_get_keff(k) + return tuple(k) + + +def load_nuclide(name): + """Load cross section data for a nuclide. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + + """ + _dll.openmc_load_nuclide(name.encode()) + + +def material_add_nuclide(mat_id, name, density): + """Add a nuclide to a material. + + Parameters + ---------- + mat_id : int + ID of the material + name : str + Name of nuclide, e.g. 'U235' + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_add_nuclide(mat_id, name.encode(), density) + + +def material_get_densities(mat_id): + """Get atom densities in a material. + + Parameters + ---------- + mat_id : int + ID of the material + + Returns + ------- + list of string + List of nuclide names + numpy.ndarray + Array of densities in atom/b-cm + + """ + # Allocate memory for arguments that are written to + nuclides = POINTER(c_int)() + densities = POINTER(c_double)() + n = c_int() + + # Get nuclide names and densities + _dll.openmc_material_get_densities(mat_id, nuclides, densities, n) + + # Convert to appropriate types and return + nuclide_list = [nuclide_name(nuclides[i]) for i in range(n.value)] + density_array = as_array(densities, (n.value,)) + return nuclide_list, density_array + + +def material_set_density(mat_id, density): + """Set density of a material. + + Parameters + ---------- + mat_id : int + ID of the material + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_set_density(mat_id, density) + + +def material_set_densities(mat_id, nuclides, densities): + """Set the densities of a list of nuclides in a material + + Parameters + ---------- + mat_id : int + ID of the material + nuclides : iterable of str + Nuclide names + densities : iterable of float + Corresponding densities in atom/b-cm + + """ + # Convert strings to an array of char* + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + + # Get numpy array as a double* + d = np.asarray(densities) + dp = d.ctypes.data_as(POINTER(c_double)) + + _dll.openmc_material_set_densities(mat_id, len(nuclides), nucs, dp) + + +def nuclide_name(index): + """Name of nuclide with given index + + Parameter + --------- + index : int + Index in internal nuclides array + + Returns + ------- + str + Name of nuclide + + """ + name = c_char_p() + _dll.openmc_nuclide_name(index, name) + + # Find blank in name + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() + + +def plot_geometry(): + """Plot geometry""" + _dll.openmc_plot_geometry() + + +def reset(): + """Reset tallies""" + _dll.openmc_reset() + + +def run(): + """Run simulation""" + _dll.openmc_run() + + +def tally_results(tally_id): + """Get tally results array + + Parameters + ---------- + tally_id : int + ID of tally + + Returns + ------- + numpy.ndarray + Array that exposes the internal tally results array + + """ + data = POINTER(c_double)() + shape = _int3() + _dll.openmc_tally_results(tally_id, data, shape) + return as_array(data, tuple(shape[::-1])) + + +@contextmanager +def run_in_memory(intracomm=None): + """Provides context manager for calling OpenMC shared library functions. + + This function is intended to be used in a 'with' statement and ensures that + OpenMC is properly initialized/finalized. At the completion of the 'with' + block, all memory that was allocated during the block is freed. For + example:: + + with openmc.capi.run_in_memory(): + for i in range(n_iters): + openmc.capi.reset() + do_stuff() + openmc.capi.run() + + Parameters + ---------- + intracomm : mpi4py.MPI.Intracomm or None + MPI intracommunicator + + """ + init(intracomm) + yield + finalize() # Determine shared-library suffix if sys.platform == 'darwin': - suffix = 'dylib' + _suffix = 'dylib' else: - suffix = 'so' + _suffix = 'so' # Open shared library -filename = pkg_resources.resource_filename( - __name__, '_libopenmc.{}'.format(suffix)) +_filename = pkg_resources.resource_filename( + __name__, '_libopenmc.{}'.format(_suffix)) try: - lib = OpenMCLibrary(filename) + _dll = CDLL(_filename) + _available = True except OSError: warn("OpenMC shared library is not available from the Python API. This " - "means you will not be able to use openmc.lib to make in-memory " + "means you will not be able to use openmc.capi to make in-memory " "calls to OpenMC.") - lib = None + _available = False + +if _available: + # Set argument/return types + _dll.openmc_calculate_volumes.restype = None + _dll.openmc_cell_set_temperature.argtypes = [ + c_int32, c_double, POINTER(c_int32)] + _dll.openmc_cell_set_temperature.restype = c_int + _dll.openmc_cell_set_temperature.errcheck = _error_handler + _dll.openmc_finalize.restype = None + _dll.openmc_find.argtypes = [ + POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] + _dll.openmc_find.restype = c_int + _dll.openmc_find.errcheck = _error_handler + _dll.openmc_init.argtypes = [POINTER(c_int)] + _dll.openmc_init.restype = None + _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] + _dll.openmc_get_keff.restype = c_int + _dll.openmc_get_keff.errcheck = _error_handler + _dll.openmc_load_nuclide.argtypes = [c_char_p] + _dll.openmc_load_nuclide.restype = c_int + _dll.openmc_load_nuclide.errcheck = _error_handler + + # Material interface + _dll.openmc_material_add_nuclide.argtypes = [ + c_int32, c_char_p, c_double] + _dll.openmc_material_add_nuclide.restype = c_int + _dll.openmc_material_add_nuclide.errcheck = _error_handler + _dll.openmc_material_get_densities.argtypes = [ + c_int32, _int_array, _double_array, POINTER(c_int)] + _dll.openmc_material_get_densities.restype = c_int + _dll.openmc_material_get_densities.errcheck = _error_handler + _dll.openmc_material_set_density.argtypes = [c_int32, c_double] + _dll.openmc_material_set_density.restype = c_int + _dll.openmc_material_set_density.errcheck = _error_handler + _dll.openmc_material_set_densities.argtypes = [ + c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] + _dll.openmc_material_set_densities.restype = c_int + _dll.openmc_material_set_densities.errcheck = _error_handler + + _dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] + _dll.openmc_nuclide_name.restype = c_int + _dll.openmc_nuclide_name.errcheck = _error_handler + _dll.openmc_plot_geometry.restype = None + _dll.openmc_run.restype = None + _dll.openmc_reset.restype = None + _dll.openmc_tally_results.argtypes = [ + c_int32, _double_array, POINTER(_int3)] + _dll.openmc_tally_results.restype = c_int + _dll.openmc_tally_results.errcheck = _error_handler From 21aa92e4ad92e153b60bf3b622adbab2e26aabb7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 08:23:46 -0500 Subject: [PATCH 49/66] Refactor C API bindings into Mapping and View classes --- openmc/capi.py | 310 +++++++++++++++++++++++++---------------- src/api.F90 | 364 +++++++++++++++++++++++++++---------------------- src/global.F90 | 9 +- 3 files changed, 403 insertions(+), 280 deletions(-) diff --git a/openmc/capi.py b/openmc/capi.py index 2e6b17d432..0706d4c774 100644 --- a/openmc/capi.py +++ b/openmc/capi.py @@ -10,10 +10,12 @@ automatically loaded. Calls to the OpenMC library can then be made, for example: """ +from collections import Mapping from contextlib import contextmanager from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys from warnings import warn +from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array @@ -52,6 +54,9 @@ def _error_handler(err, func, args): elif err == _error_code('e_nuclide_not_allocated'): raise MemoryError("Memory has not been allocated for nuclides.") + elif err == _error_code('e_nuclide_not_loaded'): + raise KeyError("No nuclide named '{}' has been loaded.") + elif err == _error_code('e_nuclide_not_in_library'): raise KeyError("Specified nuclide doesn't exist in the cross " "section library.") @@ -72,6 +77,172 @@ def _error_handler(err, func, args): raise Exception("Unknown error encountered (code {}).".format(err)) + + +class MaterialView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def id(self): + mat_id = c_int32() + _dll.openmc_material_id(self._index, mat_id) + return mat_id.value + + @property + def nuclides(self): + return self._get_densities()[0] + return nuclides + + @property + def densities(self): + return self._get_densities()[1] + + def _get_densities(self): + """Get atom densities in a material. + + Returns + ------- + list of string + List of nuclide names + numpy.ndarray + Array of densities in atom/b-cm + + """ + # Allocate memory for arguments that are written to + nuclides = POINTER(c_int)() + densities = POINTER(c_double)() + n = c_int() + + # Get nuclide names and densities + _dll.openmc_material_get_densities(self._index, nuclides, densities, n) + + # Convert to appropriate types and return + nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] + density_array = as_array(densities, (n.value,)) + return nuclide_list, density_array + + def add_nuclide(name, density): + """Add a nuclide to a material. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_add_nuclide(self._index, name.encode(), density) + + def set_density(self, density): + """Set density of a material. + + Parameters + ---------- + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_set_density(self._index, density) + + def set_densities(self, nuclides, densities): + """Set the densities of a list of nuclides in a material + + Parameters + ---------- + nuclides : iterable of str + Nuclide names + densities : iterable of float + Corresponding densities in atom/b-cm + + """ + # Convert strings to an array of char* + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + + # Get numpy array as a double* + d = np.asarray(densities) + dp = d.ctypes.data_as(POINTER(c_double)) + + _dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp) + + +class _MaterialMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + _dll.openmc_get_material(key, index) + return MaterialView(index.value) + + def __iter__(self): + for i in range(len(self)): + yield MaterialView(i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_materials').value + +materials = _MaterialMapping() + + +class NuclideView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def name(self): + """Name of nuclide with given index + + Parameter + --------- + index : int + Index in internal nuclides array + + Returns + ------- + str + Name of nuclide + + """ + name = c_char_p() + _dll.openmc_nuclide_name(self._index, name) + + # Find blank in name + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() + + +class _NuclideMapping(Mapping): + def __getitem__(self, key): + index = c_int() + _dll.openmc_get_nuclide(key.encode(), index) + return NuclideView(index) + + def __iter__(self): + for i in range(len(self)): + yield NuclideView(i + 1).name + + def __len__(self): + return c_int.in_dll(_dll, 'n_nuclides').value + +nuclides = _NuclideMapping() + + def calculate_volumes(): """Run stochastic volume calculation""" _dll.openmc_calculate_volumes() @@ -181,112 +352,6 @@ def load_nuclide(name): _dll.openmc_load_nuclide(name.encode()) -def material_add_nuclide(mat_id, name, density): - """Add a nuclide to a material. - - Parameters - ---------- - mat_id : int - ID of the material - name : str - Name of nuclide, e.g. 'U235' - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_add_nuclide(mat_id, name.encode(), density) - - -def material_get_densities(mat_id): - """Get atom densities in a material. - - Parameters - ---------- - mat_id : int - ID of the material - - Returns - ------- - list of string - List of nuclide names - numpy.ndarray - Array of densities in atom/b-cm - - """ - # Allocate memory for arguments that are written to - nuclides = POINTER(c_int)() - densities = POINTER(c_double)() - n = c_int() - - # Get nuclide names and densities - _dll.openmc_material_get_densities(mat_id, nuclides, densities, n) - - # Convert to appropriate types and return - nuclide_list = [nuclide_name(nuclides[i]) for i in range(n.value)] - density_array = as_array(densities, (n.value,)) - return nuclide_list, density_array - - -def material_set_density(mat_id, density): - """Set density of a material. - - Parameters - ---------- - mat_id : int - ID of the material - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_set_density(mat_id, density) - - -def material_set_densities(mat_id, nuclides, densities): - """Set the densities of a list of nuclides in a material - - Parameters - ---------- - mat_id : int - ID of the material - nuclides : iterable of str - Nuclide names - densities : iterable of float - Corresponding densities in atom/b-cm - - """ - # Convert strings to an array of char* - nucs = (c_char_p * len(nuclides))() - nucs[:] = [x.encode() for x in nuclides] - - # Get numpy array as a double* - d = np.asarray(densities) - dp = d.ctypes.data_as(POINTER(c_double)) - - _dll.openmc_material_set_densities(mat_id, len(nuclides), nucs, dp) - - -def nuclide_name(index): - """Name of nuclide with given index - - Parameter - --------- - index : int - Index in internal nuclides array - - Returns - ------- - str - Name of nuclide - - """ - name = c_char_p() - _dll.openmc_nuclide_name(index, name) - - # Find blank in name - i = 0 - while name.value[i:i+1] != b' ': - i += 1 - return name.value[:i].decode() def plot_geometry(): @@ -368,13 +433,9 @@ except OSError: "calls to OpenMC.") _available = False +# Set argument/return types if _available: - # Set argument/return types _dll.openmc_calculate_volumes.restype = None - _dll.openmc_cell_set_temperature.argtypes = [ - c_int32, c_double, POINTER(c_int32)] - _dll.openmc_cell_set_temperature.restype = c_int - _dll.openmc_cell_set_temperature.errcheck = _error_handler _dll.openmc_finalize.restype = None _dll.openmc_find.argtypes = [ POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] @@ -385,15 +446,24 @@ if _available: _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler - _dll.openmc_load_nuclide.argtypes = [c_char_p] - _dll.openmc_load_nuclide.restype = c_int - _dll.openmc_load_nuclide.errcheck = _error_handler - # Material interface + # Cell functions + _dll.openmc_cell_set_temperature.argtypes = [ + c_int32, c_double, POINTER(c_int32)] + _dll.openmc_cell_set_temperature.restype = c_int + _dll.openmc_cell_set_temperature.errcheck = _error_handler + + # Material functions + _dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] + _dll.openmc_get_material.restype = c_int + _dll.openmc_get_material.errcheck = _error_handler _dll.openmc_material_add_nuclide.argtypes = [ c_int32, c_char_p, c_double] _dll.openmc_material_add_nuclide.restype = c_int _dll.openmc_material_add_nuclide.errcheck = _error_handler + _dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] + _dll.openmc_material_id.restype = c_int + _dll.openmc_material_id.errcheck = _error_handler _dll.openmc_material_get_densities.argtypes = [ c_int32, _int_array, _double_array, POINTER(c_int)] _dll.openmc_material_get_densities.restype = c_int @@ -406,12 +476,22 @@ if _available: _dll.openmc_material_set_densities.restype = c_int _dll.openmc_material_set_densities.errcheck = _error_handler + # Nuclide functions + _dll.openmc_get_nuclide.argtypes = [c_char_p, POINTER(c_int)] + _dll.openmc_get_nuclide.restype = c_int + _dll.openmc_get_nuclide.errcheck = _error_handler + _dll.openmc_load_nuclide.argtypes = [c_char_p] + _dll.openmc_load_nuclide.restype = c_int + _dll.openmc_load_nuclide.errcheck = _error_handler _dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] _dll.openmc_nuclide_name.restype = c_int _dll.openmc_nuclide_name.errcheck = _error_handler + _dll.openmc_plot_geometry.restype = None _dll.openmc_run.restype = None _dll.openmc_reset.restype = None + + # Tally functions _dll.openmc_tally_results.argtypes = [ c_int32, _double_array, POINTER(_int3)] _dll.openmc_tally_results.restype = c_int diff --git a/src/api.F90 b/src/api.F90 index 6b71bf2fc6..0d4c50f418 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -27,8 +27,11 @@ module openmc_api public :: openmc_finalize public :: openmc_find public :: openmc_get_keff + public :: openmc_get_material + public :: openmc_get_nuclide public :: openmc_init public :: openmc_load_nuclide + public :: openmc_material_id public :: openmc_material_add_nuclide public :: openmc_material_get_densities public :: openmc_material_set_density @@ -46,11 +49,12 @@ module openmc_api integer(C_INT), public, bind(C) :: E_CELL_INVALID_ID = -4 integer(C_INT), public, bind(C) :: E_CELL_NOT_FOUND = -5 integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_ALLOCATED = -6 - integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_IN_LIBRARY = -7 - integer(C_INT), public, bind(C) :: E_MATERIAL_NOT_ALLOCATED = -8 - integer(C_INT), public, bind(C) :: E_MATERIAL_INVALID_ID = -9 - integer(C_INT), public, bind(C) :: E_TALLY_NOT_ALLOCATED = -10 - integer(C_INT), public, bind(C) :: E_TALLY_INVALID_ID = -11 + integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_LOADED = -7 + integer(C_INT), public, bind(C) :: E_NUCLIDE_NOT_IN_LIBRARY = -8 + integer(C_INT), public, bind(C) :: E_MATERIAL_NOT_ALLOCATED = -9 + integer(C_INT), public, bind(C) :: E_MATERIAL_INVALID_ID = -10 + integer(C_INT), public, bind(C) :: E_TALLY_NOT_ALLOCATED = -11 + integer(C_INT), public, bind(C) :: E_TALLY_INVALID_ID = -12 contains @@ -223,11 +227,60 @@ contains end function openmc_find !=============================================================================== -! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library +! OPENMC_GET_MATERIAL returns the index in the materials array of a material +! with a given ID +!=============================================================================== + + function openmc_get_material(id, index) result(err) bind(C) + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + + if (allocated(materials)) then + if (material_dict % has_key(id)) then + index = material_dict % get_key(id) + err = 0 + else + err = E_MATERIAL_INVALID_ID + end if + else + err = E_MATERIAL_NOT_ALLOCATED + end if + end function openmc_get_material + +!=============================================================================== +! OPENMC_GET_NUCLIDE returns the index in the nuclides array of a nuclide +! with a given name +!=============================================================================== + + function openmc_get_nuclide(name, index) result(err) bind(C) + character(kind=C_CHAR), intent(in) :: name(*) + integer(C_INT), intent(out) :: index + integer(C_INT) :: err + + character(:), allocatable :: name_ + + ! Copy array of C_CHARs to normal Fortran string + name_ = to_f_string(name) + + if (allocated(nuclides)) then + if (nuclide_dict % has_key(to_lower(name_))) then + index = nuclide_dict % get_key(to_lower(name_)) + err = 0 + else + err = E_NUCLIDE_NOT_LOADED + end if + else + err = E_NUCLIDE_NOT_ALLOCATED + end if + end function openmc_get_nuclide + +!=============================================================================== +! OPENMC_LOAD_LOAD loads a nuclide from the cross section library !=============================================================================== function openmc_load_nuclide(name) result(err) bind(C) - character(kind=C_CHAR) :: name(*) + character(kind=C_CHAR), intent(in) :: name(*) integer(C_INT) :: err integer :: i_library @@ -287,13 +340,13 @@ contains ! OPENMC_MATERIAL_ADD_NUCLIDE !=============================================================================== - function openmc_material_add_nuclide(id, name, density) result(err) bind(C) - integer(C_INT32_T), value, intent(in) :: id + function openmc_material_add_nuclide(index, name, density) result(err) bind(C) + integer(C_INT32_T), value, intent(in) :: index character(kind=C_CHAR) :: name(*) real(C_DOUBLE), value, intent(in) :: density integer(C_INT) :: err - integer :: i, j, k, n + integer :: j, k, n real(8) :: awr integer, allocatable :: new_nuclide(:) real(8), allocatable :: new_density(:) @@ -302,55 +355,50 @@ contains name_ = to_f_string(name) err = E_UNASSIGNED - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - ! Check if nuclide is already in material - do j = 1, size(m % nuclide) - k = m % nuclide(j) - if (nuclides(k) % name == name_) then - awr = nuclides(k) % awr - m % density = m % density + density - m % atom_density(j) - m % density_gpcc = m % density_gpcc + (density - & - m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO - m % atom_density(j) = density - err = 0 - end if - end do - - ! If nuclide wasn't found, extend nuclide/density arrays - if (err /= 0) then - ! If nuclide hasn't been loaded, load it now - err = openmc_load_nuclide(name) - - if (err == 0) then - ! Extend arrays - n = size(m % nuclide) - allocate(new_nuclide(n + 1)) - new_nuclide(1:n) = m % nuclide - call move_alloc(FROM=new_nuclide, TO=m % nuclide) - - allocate(new_density(n + 1)) - new_density(1:n) = m % atom_density - call move_alloc(FROM=new_density, TO=m % atom_density) - - ! Append new nuclide/density - k = nuclide_dict % get_key(to_lower(name_)) - m % nuclide(n + 1) = k - m % atom_density(n + 1) = density - m % density = m % density + density - m % density_gpcc = m % density_gpcc + & - density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO - m % n_nuclides = n + 1 - end if + if (index >= 1 .and. index <= size(materials)) then + associate (m => materials(index)) + ! Check if nuclide is already in material + do j = 1, size(m % nuclide) + k = m % nuclide(j) + if (nuclides(k) % name == name_) then + awr = nuclides(k) % awr + m % density = m % density + density - m % atom_density(j) + m % density_gpcc = m % density_gpcc + (density - & + m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO + m % atom_density(j) = density + err = 0 end if - end associate - else - err = E_MATERIAL_INVALID_ID - end if + end do + + ! If nuclide wasn't found, extend nuclide/density arrays + if (err /= 0) then + ! If nuclide hasn't been loaded, load it now + err = openmc_load_nuclide(name) + + if (err == 0) then + ! Extend arrays + n = size(m % nuclide) + allocate(new_nuclide(n + 1)) + new_nuclide(1:n) = m % nuclide + call move_alloc(FROM=new_nuclide, TO=m % nuclide) + + allocate(new_density(n + 1)) + new_density(1:n) = m % atom_density + call move_alloc(FROM=new_density, TO=m % atom_density) + + ! Append new nuclide/density + k = nuclide_dict % get_key(to_lower(name_)) + m % nuclide(n + 1) = k + m % atom_density(n + 1) = density + m % density = m % density + density + m % density_gpcc = m % density_gpcc + & + density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO + m % n_nuclides = n + 1 + end if + end if + end associate else - err = E_MATERIAL_NOT_ALLOCATED + err = E_OUT_OF_BOUNDS end if end function openmc_material_add_nuclide @@ -360,39 +408,115 @@ contains ! material !=============================================================================== - function openmc_material_get_densities(id, nuclides, densities, n) & + function openmc_material_get_densities(index, nuclides, densities, n) & result(err) bind(C) - integer(C_INT32_T), intent(in), value :: id + integer(C_INT32_T), value :: index type(C_PTR), intent(out) :: nuclides type(C_PTR), intent(out) :: densities integer(C_INT), intent(out) :: n integer(C_INT) :: err - integer :: i - - nuclides = C_NULL_PTR - densities = C_NULL_PTR - n = -1 err = E_UNASSIGNED - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - if (allocated(m % atom_density)) then - nuclides = C_LOC(m % nuclide(1)) - densities = C_LOC(m % atom_density(1)) - n = size(m % atom_density) - err = 0 - end if - end associate - else - err = E_MATERIAL_INVALID_ID - end if + if (index >= 1 .and. index <= size(materials)) then + associate (m => materials(index)) + if (allocated(m % atom_density)) then + nuclides = C_LOC(m % nuclide(1)) + densities = C_LOC(m % atom_density(1)) + n = size(m % atom_density) + err = 0 + end if + end associate else - err = E_MATERIAL_NOT_ALLOCATED + err = E_OUT_OF_BOUNDS end if end function openmc_material_get_densities +!=============================================================================== +! OPENMC_MATERIAL_ID returns the ID of a material +!=============================================================================== + + function openmc_material_id(index, id) result(err) bind(C) + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(materials)) then + id = materials(index) % id + err = 0 + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_material_id + +!=============================================================================== +! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm +!=============================================================================== + + function openmc_material_set_density(index, density) result(err) bind(C) + integer(C_INT32_T), value, intent(in) :: index + real(C_DOUBLE), value, intent(in) :: density + integer(C_INT) :: err + + err = E_UNASSIGNED + if (index >= 1 .and. index <= size(materials)) then + associate (m => materials(index)) + err = m % set_density(density, nuclides) + end associate + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_material_set_density + +!=============================================================================== +! OPENMC_MATERIAL_SET_DENSITIES sets the densities for a list of nuclides in a +! material. If the nuclides don't already exist in the material, they will be +! added +!=============================================================================== + + function openmc_material_set_densities(index, n, name, density) result(err) bind(C) + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + type(C_PTR), intent(in) :: name(n) + real(C_DOUBLE), intent(in) :: density(n) + integer(C_INT) :: err + + integer :: i, j, k + character(C_CHAR), pointer :: string(:) + character(len=:, kind=C_CHAR), allocatable :: name_ + + if (index >= 1 .and. index <= size(materials)) then + associate (m => materials(index)) + do i = 1, n + ! Convert C string to Fortran string + call c_f_pointer(name(i), string, [10]) + name_ = to_f_string(string) + + ! Find corresponding nuclide and set density + err = E_UNASSIGNED + do j = 1, size(m % nuclide) + k = m % nuclide(j) + if (nuclides(k) % name == name_) then + m % atom_density(j) = density(i) + err = 0 + end if + end do + + ! If nuclide wasn't found, try to load it + if (err /= 0) then + err = openmc_material_add_nuclide(index, string, density(i)) + if (err /= 0) return + end if + end do + + ! Set total density to the sum of the vector + err = m % set_density(sum(m % atom_density), nuclides) + end associate + else + err = E_OUT_OF_BOUNDS + end if + + end function openmc_material_set_densities + !=============================================================================== ! OPENMC_NUCLIDE_NAME returns the name of a nuclide with a given index !=============================================================================== @@ -405,7 +529,6 @@ contains character(C_CHAR), pointer :: name_ err = E_UNASSIGNED - name = C_NULL_PTR if (allocated(nuclides)) then if (index >= 1 .and. index <= size(nuclides)) then name_ => nuclides(index) % name(1:1) @@ -419,87 +542,6 @@ contains end if end function openmc_nuclide_name -!=============================================================================== -! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm -!=============================================================================== - - function openmc_material_set_density(id, density) result(err) bind(C) - integer(C_INT32_T), value, intent(in) :: id - real(C_DOUBLE), value, intent(in) :: density - integer(C_INT) :: err - - integer :: i - - err = -1 - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - err = m % set_density(density, nuclides) - end associate - else - err = E_MATERIAL_INVALID_ID - end if - else - err = E_MATERIAL_NOT_ALLOCATED - end if - end function openmc_material_set_density - -!=============================================================================== -! OPENMC_MATERIAL_SET_DENSITIES sets the densities for a list of nuclides in a -! material. If the nuclides don't already exist in the material, they will be -! added -!=============================================================================== - - function openmc_material_set_densities(id, n, name, density) result(err) bind(C) - integer(C_INT32_T), value, intent(in) :: id - integer(C_INT), value, intent(in) :: n - type(C_PTR), intent(in) :: name(n) - real(C_DOUBLE), intent(in) :: density(n) - integer(C_INT) :: err - - integer :: i, j, k - character(C_CHAR), pointer :: string(:) - character(len=:, kind=C_CHAR), allocatable :: name_ - - err = E_UNASSIGNED - if (allocated(materials)) then - if (material_dict % has_key(id)) then - i = material_dict % get_key(id) - associate (m => materials(i)) - do i = 1, n - ! Convert C string to Fortran string - call c_f_pointer(name(i), string, [10]) - name_ = to_f_string(string) - - ! Find corresponding nuclide and set density - do j = 1, size(m % nuclide) - k = m % nuclide(j) - if (nuclides(k) % name == name_) then - m % atom_density(j) = density(i) - err = 0 - end if - end do - - ! If nuclide wasn't found, try to load it - if (err /= 0) then - err = openmc_material_add_nuclide(id, string, density(i)) - if (err /= 0) return - end if - end do - - ! Set total density to the sum of the vector - err = m % set_density(sum(m % atom_density), nuclides) - end associate - else - err = E_MATERIAL_INVALID_ID - end if - else - err = E_MATERIAL_NOT_ALLOCATED - end if - - end function openmc_material_set_densities - !=============================================================================== ! OPENMC_RESET resets all tallies !=============================================================================== diff --git a/src/global.F90 b/src/global.F90 index 9d9002e9c5..5661cf2af3 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -43,11 +43,11 @@ module global type(VolumeCalculation), allocatable :: volume_calcs(:) ! Size of main arrays - integer :: n_cells ! # of cells + integer(C_INT32_T), bind(C) :: n_cells ! # of cells integer :: n_universes ! # of universes integer :: n_lattices ! # of lattices integer :: n_surfaces ! # of surfaces - integer :: n_materials ! # of materials + integer(C_INT32_T), bind(C) :: n_materials ! # of materials integer :: n_plots ! # of plots ! These dictionaries provide a fast lookup mechanism -- the key is the @@ -73,7 +73,8 @@ module global ! ============================================================================ ! CROSS SECTION RELATED VARIABLES NEEDED REGARDLESS OF CE OR MG - integer :: n_nuclides_total ! Number of nuclide cross section tables + ! Number of nuclide cross section tables + integer(C_INT), bind(C, name='n_nuclides') :: n_nuclides_total ! Cross section caches type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide @@ -192,7 +193,7 @@ module global integer :: n_user_meshes = 0 ! # of structured user meshes integer :: n_filters = 0 ! # of filters integer :: n_user_filters = 0 ! # of user filters - integer :: n_tallies = 0 ! # of tallies + integer(C_INT32_T), bind(C) :: n_tallies = 0 ! # of tallies integer :: n_user_tallies = 0 ! # of user tallies ! Tally derivatives From d9e1d0d4f6725623cde361f51b083a5cad9fa118 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 08:29:56 -0500 Subject: [PATCH 50/66] Move Python bindins to C API into subpackage --- CMakeLists.txt | 2 +- openmc/{capi.py => capi/__init__.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename openmc/{capi.py => capi/__init__.py} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 386c07100c..c21b44cc16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -412,7 +412,7 @@ target_link_libraries(${program} ${ldflags} libopenmc) add_custom_command(TARGET libopenmc POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ - ${CMAKE_CURRENT_SOURCE_DIR}/openmc/_$ + ${CMAKE_CURRENT_SOURCE_DIR}/openmc/capi/_$ COMMENT "Copying libopenmc to Python module directory") #=============================================================================== diff --git a/openmc/capi.py b/openmc/capi/__init__.py similarity index 100% rename from openmc/capi.py rename to openmc/capi/__init__.py From 692765ef4ac641276a16a4db153a33e6e9240423 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 08:43:48 -0500 Subject: [PATCH 51/66] Move material/nuclide related C API bindings into separate files --- openmc/capi/__init__.py | 261 ++++------------------------------------ openmc/capi/material.py | 147 ++++++++++++++++++++++ openmc/capi/nuclide.py | 86 +++++++++++++ 3 files changed, 256 insertions(+), 238 deletions(-) create mode 100644 openmc/capi/material.py create mode 100644 openmc/capi/nuclide.py diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 0706d4c774..f8beb2fd7f 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -10,21 +10,36 @@ automatically loaded. Calls to the OpenMC library can then be made, for example: """ -from collections import Mapping from contextlib import contextmanager from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys from warnings import warn -from weakref import WeakValueDictionary -import numpy as np from numpy.ctypeslib import as_array import pkg_resources + +# Determine shared-library suffix +if sys.platform == 'darwin': + _suffix = 'dylib' +else: + _suffix = 'so' + +# Open shared library +_filename = pkg_resources.resource_filename( + __name__, '_libopenmc.{}'.format(_suffix)) +try: + _dll = CDLL(_filename) + _available = True +except OSError: + warn("OpenMC shared library is not available from the Python API. This " + "means you will not be able to use openmc.capi to make in-memory " + "calls to OpenMC.") + _available = False + + _int3 = c_int*3 _double3 = c_double*3 -_int_array = POINTER(POINTER(c_int)) -_double_array = POINTER(POINTER(c_double)) class GeometryError(Exception): @@ -77,172 +92,6 @@ def _error_handler(err, func, args): raise Exception("Unknown error encountered (code {}).".format(err)) - - -class MaterialView(object): - __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: - instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] - - def __init__(self, index): - self._index = index - - @property - def id(self): - mat_id = c_int32() - _dll.openmc_material_id(self._index, mat_id) - return mat_id.value - - @property - def nuclides(self): - return self._get_densities()[0] - return nuclides - - @property - def densities(self): - return self._get_densities()[1] - - def _get_densities(self): - """Get atom densities in a material. - - Returns - ------- - list of string - List of nuclide names - numpy.ndarray - Array of densities in atom/b-cm - - """ - # Allocate memory for arguments that are written to - nuclides = POINTER(c_int)() - densities = POINTER(c_double)() - n = c_int() - - # Get nuclide names and densities - _dll.openmc_material_get_densities(self._index, nuclides, densities, n) - - # Convert to appropriate types and return - nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] - density_array = as_array(densities, (n.value,)) - return nuclide_list, density_array - - def add_nuclide(name, density): - """Add a nuclide to a material. - - Parameters - ---------- - name : str - Name of nuclide, e.g. 'U235' - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_add_nuclide(self._index, name.encode(), density) - - def set_density(self, density): - """Set density of a material. - - Parameters - ---------- - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_set_density(self._index, density) - - def set_densities(self, nuclides, densities): - """Set the densities of a list of nuclides in a material - - Parameters - ---------- - nuclides : iterable of str - Nuclide names - densities : iterable of float - Corresponding densities in atom/b-cm - - """ - # Convert strings to an array of char* - nucs = (c_char_p * len(nuclides))() - nucs[:] = [x.encode() for x in nuclides] - - # Get numpy array as a double* - d = np.asarray(densities) - dp = d.ctypes.data_as(POINTER(c_double)) - - _dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp) - - -class _MaterialMapping(Mapping): - def __getitem__(self, key): - index = c_int32() - _dll.openmc_get_material(key, index) - return MaterialView(index.value) - - def __iter__(self): - for i in range(len(self)): - yield MaterialView(i + 1).id - - def __len__(self): - return c_int32.in_dll(_dll, 'n_materials').value - -materials = _MaterialMapping() - - -class NuclideView(object): - __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: - instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] - - def __init__(self, index): - self._index = index - - @property - def name(self): - """Name of nuclide with given index - - Parameter - --------- - index : int - Index in internal nuclides array - - Returns - ------- - str - Name of nuclide - - """ - name = c_char_p() - _dll.openmc_nuclide_name(self._index, name) - - # Find blank in name - i = 0 - while name.value[i:i+1] != b' ': - i += 1 - return name.value[:i].decode() - - -class _NuclideMapping(Mapping): - def __getitem__(self, key): - index = c_int() - _dll.openmc_get_nuclide(key.encode(), index) - return NuclideView(index) - - def __iter__(self): - for i in range(len(self)): - yield NuclideView(i + 1).name - - def __len__(self): - return c_int.in_dll(_dll, 'n_nuclides').value - -nuclides = _NuclideMapping() - - def calculate_volumes(): """Run stochastic volume calculation""" _dll.openmc_calculate_volumes() @@ -264,7 +113,6 @@ def cell_set_temperature(cell_id, T, instance=None): _dll.openmc_cell_set_temperature(cell_id, T, instance) - def finalize(): """Finalize simulation and free memory""" _dll.openmc_finalize() @@ -340,20 +188,6 @@ def keff(): return tuple(k) -def load_nuclide(name): - """Load cross section data for a nuclide. - - Parameters - ---------- - name : str - Name of nuclide, e.g. 'U235' - - """ - _dll.openmc_load_nuclide(name.encode()) - - - - def plot_geometry(): """Plot geometry""" _dll.openmc_plot_geometry() @@ -415,24 +249,6 @@ def run_in_memory(intracomm=None): finalize() -# Determine shared-library suffix -if sys.platform == 'darwin': - _suffix = 'dylib' -else: - _suffix = 'so' - -# Open shared library -_filename = pkg_resources.resource_filename( - __name__, '_libopenmc.{}'.format(_suffix)) -try: - _dll = CDLL(_filename) - _available = True -except OSError: - warn("OpenMC shared library is not available from the Python API. This " - "means you will not be able to use openmc.capi to make in-memory " - "calls to OpenMC.") - _available = False - # Set argument/return types if _available: _dll.openmc_calculate_volumes.restype = None @@ -453,40 +269,6 @@ if _available: _dll.openmc_cell_set_temperature.restype = c_int _dll.openmc_cell_set_temperature.errcheck = _error_handler - # Material functions - _dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] - _dll.openmc_get_material.restype = c_int - _dll.openmc_get_material.errcheck = _error_handler - _dll.openmc_material_add_nuclide.argtypes = [ - c_int32, c_char_p, c_double] - _dll.openmc_material_add_nuclide.restype = c_int - _dll.openmc_material_add_nuclide.errcheck = _error_handler - _dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] - _dll.openmc_material_id.restype = c_int - _dll.openmc_material_id.errcheck = _error_handler - _dll.openmc_material_get_densities.argtypes = [ - c_int32, _int_array, _double_array, POINTER(c_int)] - _dll.openmc_material_get_densities.restype = c_int - _dll.openmc_material_get_densities.errcheck = _error_handler - _dll.openmc_material_set_density.argtypes = [c_int32, c_double] - _dll.openmc_material_set_density.restype = c_int - _dll.openmc_material_set_density.errcheck = _error_handler - _dll.openmc_material_set_densities.argtypes = [ - c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] - _dll.openmc_material_set_densities.restype = c_int - _dll.openmc_material_set_densities.errcheck = _error_handler - - # Nuclide functions - _dll.openmc_get_nuclide.argtypes = [c_char_p, POINTER(c_int)] - _dll.openmc_get_nuclide.restype = c_int - _dll.openmc_get_nuclide.errcheck = _error_handler - _dll.openmc_load_nuclide.argtypes = [c_char_p] - _dll.openmc_load_nuclide.restype = c_int - _dll.openmc_load_nuclide.errcheck = _error_handler - _dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] - _dll.openmc_nuclide_name.restype = c_int - _dll.openmc_nuclide_name.errcheck = _error_handler - _dll.openmc_plot_geometry.restype = None _dll.openmc_run.restype = None _dll.openmc_reset.restype = None @@ -496,3 +278,6 @@ if _available: c_int32, _double_array, POINTER(_int3)] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler + + from .nuclide import * + from .material import * diff --git a/openmc/capi/material.py b/openmc/capi/material.py new file mode 100644 index 0000000000..1863957882 --- /dev/null +++ b/openmc/capi/material.py @@ -0,0 +1,147 @@ +from collections import Mapping +from ctypes import c_int, c_int32, c_double, c_char_p, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc.capi import _dll, _error_handler, NuclideView + +__all__ = ['MaterialView', 'materials'] + + +# Material functions +_dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_material.restype = c_int +_dll.openmc_get_material.errcheck = _error_handler +_dll.openmc_material_add_nuclide.argtypes = [ + c_int32, c_char_p, c_double] +_dll.openmc_material_add_nuclide.restype = c_int +_dll.openmc_material_add_nuclide.errcheck = _error_handler +_dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_material_id.restype = c_int +_dll.openmc_material_id.errcheck = _error_handler +_dll.openmc_material_get_densities.argtypes = [ + c_int32, POINTER(POINTER(c_int)), POINTER(POINTER(c_double)), + POINTER(c_int)] +_dll.openmc_material_get_densities.restype = c_int +_dll.openmc_material_get_densities.errcheck = _error_handler +_dll.openmc_material_set_density.argtypes = [c_int32, c_double] +_dll.openmc_material_set_density.restype = c_int +_dll.openmc_material_set_density.errcheck = _error_handler +_dll.openmc_material_set_densities.argtypes = [ + c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] +_dll.openmc_material_set_densities.restype = c_int +_dll.openmc_material_set_densities.errcheck = _error_handler + + +class MaterialView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def id(self): + mat_id = c_int32() + _dll.openmc_material_id(self._index, mat_id) + return mat_id.value + + @property + def nuclides(self): + return self._get_densities()[0] + return nuclides + + @property + def densities(self): + return self._get_densities()[1] + + def _get_densities(self): + """Get atom densities in a material. + + Returns + ------- + list of string + List of nuclide names + numpy.ndarray + Array of densities in atom/b-cm + + """ + # Allocate memory for arguments that are written to + nuclides = POINTER(c_int)() + densities = POINTER(c_double)() + n = c_int() + + # Get nuclide names and densities + _dll.openmc_material_get_densities(self._index, nuclides, densities, n) + + # Convert to appropriate types and return + nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] + density_array = as_array(densities, (n.value,)) + return nuclide_list, density_array + + def add_nuclide(name, density): + """Add a nuclide to a material. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_add_nuclide(self._index, name.encode(), density) + + def set_density(self, density): + """Set density of a material. + + Parameters + ---------- + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_set_density(self._index, density) + + def set_densities(self, nuclides, densities): + """Set the densities of a list of nuclides in a material + + Parameters + ---------- + nuclides : iterable of str + Nuclide names + densities : iterable of float + Corresponding densities in atom/b-cm + + """ + # Convert strings to an array of char* + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + + # Get numpy array as a double* + d = np.asarray(densities) + dp = d.ctypes.data_as(POINTER(c_double)) + + _dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp) + + +class _MaterialMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + _dll.openmc_get_material(key, index) + return MaterialView(index.value) + + def __iter__(self): + for i in range(len(self)): + yield MaterialView(i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_materials').value + +materials = _MaterialMapping() diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py new file mode 100644 index 0000000000..7a2f7a37b4 --- /dev/null +++ b/openmc/capi/nuclide.py @@ -0,0 +1,86 @@ +from collections import Mapping +from ctypes import c_int, c_char_p, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc.capi import _dll, _error_handler + + +__all__ = ['NuclideView', 'nuclides', 'load_nuclide'] + +# Nuclide functions +_dll.openmc_get_nuclide.argtypes = [c_char_p, POINTER(c_int)] +_dll.openmc_get_nuclide.restype = c_int +_dll.openmc_get_nuclide.errcheck = _error_handler +_dll.openmc_load_nuclide.argtypes = [c_char_p] +_dll.openmc_load_nuclide.restype = c_int +_dll.openmc_load_nuclide.errcheck = _error_handler +_dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] +_dll.openmc_nuclide_name.restype = c_int +_dll.openmc_nuclide_name.errcheck = _error_handler + + +def load_nuclide(name): + """Load cross section data for a nuclide. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + + """ + _dll.openmc_load_nuclide(name.encode()) + + +class NuclideView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def name(self): + """Name of nuclide with given index + + Parameter + --------- + index : int + Index in internal nuclides array + + Returns + ------- + str + Name of nuclide + + """ + name = c_char_p() + _dll.openmc_nuclide_name(self._index, name) + + # Find blank in name + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() + + +class _NuclideMapping(Mapping): + def __getitem__(self, key): + index = c_int() + _dll.openmc_get_nuclide(key.encode(), index) + return NuclideView(index) + + def __iter__(self): + for i in range(len(self)): + yield NuclideView(i + 1).name + + def __len__(self): + return c_int.in_dll(_dll, 'n_nuclides').value + +nuclides = _NuclideMapping() From 050cb6e016ff6ddb29db07170a2d19a9eac9a259 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 09:04:41 -0500 Subject: [PATCH 52/66] Move cell/tally functionality into View/Mapping classes --- openmc/capi/__init__.py | 56 ++-------------- openmc/capi/cell.py | 68 +++++++++++++++++++ openmc/capi/tally.py | 71 ++++++++++++++++++++ src/api.F90 | 140 +++++++++++++++++++++++++++++----------- 4 files changed, 247 insertions(+), 88 deletions(-) create mode 100644 openmc/capi/cell.py create mode 100644 openmc/capi/tally.py diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index f8beb2fd7f..b3b5731c1a 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -38,8 +38,6 @@ except OSError: _available = False -_int3 = c_int*3 -_double3 = c_double*3 class GeometryError(Exception): @@ -97,22 +95,6 @@ def calculate_volumes(): _dll.openmc_calculate_volumes() -def cell_set_temperature(cell_id, T, instance=None): - """Set the temperature of a cell - - Parameters - ---------- - cell_id : int - ID of the cell - T : float - Temperature in K - instance : int or None - Which instance of the cell - - """ - _dll.openmc_cell_set_temperature(cell_id, T, instance) - - def finalize(): """Finalize simulation and free memory""" _dll.openmc_finalize() @@ -149,7 +131,7 @@ def find(xyz, rtype='cell'): # Call openmc_find uid = c_int32() instance = c_int32() - _dll.openmc_find(_double3(*xyz), r_int, uid, instance) + _dll.openmc_find((c_double*3)(*xyz), r_int, uid, instance) return (uid.value if uid != 0 else None), instance.value @@ -203,25 +185,6 @@ def run(): _dll.openmc_run() -def tally_results(tally_id): - """Get tally results array - - Parameters - ---------- - tally_id : int - ID of tally - - Returns - ------- - numpy.ndarray - Array that exposes the internal tally results array - - """ - data = POINTER(c_double)() - shape = _int3() - _dll.openmc_tally_results(tally_id, data, shape) - return as_array(data, tuple(shape[::-1])) - @contextmanager def run_in_memory(intracomm=None): @@ -254,7 +217,7 @@ if _available: _dll.openmc_calculate_volumes.restype = None _dll.openmc_finalize.restype = None _dll.openmc_find.argtypes = [ - POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)] + POINTER(c_double*3), c_int, POINTER(c_int32), POINTER(c_int32)] _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler _dll.openmc_init.argtypes = [POINTER(c_int)] @@ -262,22 +225,11 @@ if _available: _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler - - # Cell functions - _dll.openmc_cell_set_temperature.argtypes = [ - c_int32, c_double, POINTER(c_int32)] - _dll.openmc_cell_set_temperature.restype = c_int - _dll.openmc_cell_set_temperature.errcheck = _error_handler - _dll.openmc_plot_geometry.restype = None _dll.openmc_run.restype = None _dll.openmc_reset.restype = None - # Tally functions - _dll.openmc_tally_results.argtypes = [ - c_int32, _double_array, POINTER(_int3)] - _dll.openmc_tally_results.restype = c_int - _dll.openmc_tally_results.errcheck = _error_handler - from .nuclide import * from .material import * + from .cell import * + from .tally import * diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py new file mode 100644 index 0000000000..fba34db856 --- /dev/null +++ b/openmc/capi/cell.py @@ -0,0 +1,68 @@ +from collections import Mapping +from ctypes import c_int, c_int32, c_double, c_char_p, POINTER +from weakref import WeakValueDictionary + +import numpy as np + +from openmc.capi import _dll, _error_handler + +__all__ = ['CellView', 'cells'] + +# Cell functions +_dll.openmc_cell_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_cell_id.restype = c_int +_dll.openmc_cell_id.errcheck = _error_handler +_dll.openmc_cell_set_temperature.argtypes = [ + c_int32, c_double, POINTER(c_int32)] +_dll.openmc_cell_set_temperature.restype = c_int +_dll.openmc_cell_set_temperature.errcheck = _error_handler +_dll.openmc_get_cell.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_cell.restype = c_int +_dll.openmc_get_cell.errcheck = _error_handler + + +class CellView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def id(self): + cell_id = c_int32() + _dll.openmc_cell_id(self._index, cell_id) + return cell_id.value + + def set_temperature(self, T, instance=None): + """Set the temperature of a cell + + Parameters + ---------- + T : float + Temperature in K + instance : int or None + Which instance of the cell + + """ + _dll.openmc_cell_set_temperature(self._index, T, instance) + + +class _CellMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + _dll.openmc_get_cell(key, index) + return CellView(index.value) + + def __iter__(self): + for i in range(len(self)): + yield CellView(i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_cells').value + +cells = _CellMapping() diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py new file mode 100644 index 0000000000..f60d94e922 --- /dev/null +++ b/openmc/capi/tally.py @@ -0,0 +1,71 @@ +from collections import Mapping +from ctypes import c_int, c_int32, c_double, c_char_p, POINTER +from weakref import WeakValueDictionary + +from numpy.ctypeslib import as_array + +from openmc.capi import _dll, _error_handler, NuclideView + + +__all__ = ['TallyView', 'tallies'] + +# Tally functions +_dll.openmc_get_tally.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_tally.restype = c_int +_dll.openmc_get_tally.errcheck = _error_handler +_dll.openmc_tally_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_id.restype = c_int +_dll.openmc_tally_id.errcheck = _error_handler +_dll.openmc_tally_results.argtypes = [ + c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] +_dll.openmc_tally_results.restype = c_int +_dll.openmc_tally_results.errcheck = _error_handler + + +class TallyView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def id(self): + tally_id = c_int32() + _dll.openmc_tally_id(self._index, tally_id) + return tally_id.value + + @property + def results(self): + """Get tally results array + + Returns + ------- + numpy.ndarray + Array that exposes the internal tally results array + + """ + data = POINTER(c_double)() + shape = (c_int*3)() + _dll.openmc_tally_results(self._index, data, shape) + return as_array(data, tuple(shape[::-1])) + + +class _TallyMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + _dll.openmc_get_tally(key, index) + return TallyView(index.value) + + def __iter__(self): + for i in range(len(self)): + yield TallyView(i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_tallies').value + +tallies = _TallyMapping() diff --git a/src/api.F90 b/src/api.F90 index 0d4c50f418..6d50ca51eb 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -23,12 +23,15 @@ module openmc_api private public :: openmc_calculate_volumes + public :: openmc_cell_id public :: openmc_cell_set_temperature public :: openmc_finalize public :: openmc_find + public :: openmc_get_cell public :: openmc_get_keff public :: openmc_get_material public :: openmc_get_nuclide + public :: openmc_get_tally public :: openmc_init public :: openmc_load_nuclide public :: openmc_material_id @@ -40,6 +43,7 @@ module openmc_api public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run + public :: openmc_tally_id public :: openmc_tally_results ! Error codes @@ -58,12 +62,29 @@ module openmc_api contains +!=============================================================================== +! OPENMC_CELL_ID returns the ID of a cell +!=============================================================================== + + function openmc_cell_id(index, id) result(err) bind(C) + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(cells)) then + id = cells(index) % id + err = 0 + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_cell_id + !=============================================================================== ! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell !=============================================================================== - function openmc_cell_set_temperature(id, T, instance) result(err) bind(C) - integer(C_INT32_T), value, intent(in) :: id ! id of cell + function openmc_cell_set_temperature(index, T, instance) result(err) bind(C) + integer(C_INT32_T), value, intent(in) :: index real(C_DOUBLE), value, intent(in) :: T integer(C_INT32_T), optional, intent(in) :: instance integer(C_INT) :: err @@ -71,28 +92,23 @@ contains integer :: i, n err = E_UNASSIGNED - if (allocated(cells)) then - if (cell_dict % has_key(id)) then - i = cell_dict % get_key(id) - associate (c => cells(i)) - if (allocated(c % sqrtkT)) then - n = size(c % sqrtkT) - if (present(instance) .and. n > 1) then - if (instance >= 0 .and. instance < n) then - c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * T) - err = 0 - end if - else - c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + if (index >= 1 .and. index <= size(cells)) then + associate (c => cells(i)) + if (allocated(c % sqrtkT)) then + n = size(c % sqrtkT) + if (present(instance) .and. n > 1) then + if (instance >= 0 .and. instance < n) then + c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * T) err = 0 end if + else + c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) + err = 0 end if - end associate - else - err = E_CELL_INVALID_ID - end if + end if + end associate else - err = E_CELL_NOT_ALLOCATED + err = E_OUT_OF_BOUNDS end if end function openmc_cell_set_temperature @@ -226,6 +242,27 @@ contains end function openmc_find +!=============================================================================== +! OPENMC_GET_CELL returns the index in the cells array of a cell with a given ID +!=============================================================================== + + function openmc_get_cell(id, index) result(err) bind(C) + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + + if (allocated(cells)) then + if (cell_dict % has_key(id)) then + index = cell_dict % get_key(id) + err = 0 + else + err = E_CELL_INVALID_ID + end if + else + err = E_CELL_NOT_ALLOCATED + end if + end function openmc_get_cell + !=============================================================================== ! OPENMC_GET_MATERIAL returns the index in the materials array of a material ! with a given ID @@ -275,6 +312,28 @@ contains end if end function openmc_get_nuclide +!=============================================================================== +! OPENMC_GET_TALLY returns the index in the tallies array of a tally +! with a given ID +!=============================================================================== + + function openmc_get_tally(id, index) result(err) bind(C) + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + + if (allocated(tallies)) then + if (tally_dict % has_key(id)) then + index = tally_dict % get_key(id) + err = 0 + else + err = E_TALLY_INVALID_ID + end if + else + err = E_TALLY_NOT_ALLOCATED + end if + end function openmc_get_tally + !=============================================================================== ! OPENMC_LOAD_LOAD loads a nuclide from the cross section library !=============================================================================== @@ -581,35 +640,44 @@ contains end subroutine openmc_reset +!=============================================================================== +! OPENMC_TALLY_ID returns the ID of a tally +!=============================================================================== + + function openmc_tally_id(index, id) result(err) bind(C) + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + id = tallies(index) % id + err = 0 + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_tally_id + !=============================================================================== ! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its ! shape. This allows a user to obtain in-memory tally results from Python ! directly. !=============================================================================== - function openmc_tally_results(id, ptr, shape_) result(err) bind(C) - integer(C_INT32_T), intent(in), value :: id + function openmc_tally_results(index, ptr, shape_) result(err) bind(C) + integer(C_INT32_T), intent(in), value :: index type(C_PTR), intent(out) :: ptr integer(C_INT), intent(out) :: shape_(3) integer(C_INT) :: err - integer :: i - - ptr = C_NULL_PTR err = E_UNASSIGNED - if (allocated(tallies)) then - if (tally_dict % has_key(id)) then - i = tally_dict % get_key(id) - if (allocated(tallies(i) % results)) then - ptr = C_LOC(tallies(i) % results(1,1,1)) - shape_(:) = shape(tallies(i) % results) - err = 0 - end if - else - err = E_TALLY_INVALID_ID + if (index >= 1 .and. index <= size(tallies)) then + if (allocated(tallies(index) % results)) then + ptr = C_LOC(tallies(index) % results(1,1,1)) + shape_(:) = shape(tallies(index) % results) + err = 0 end if else - err = E_TALLY_NOT_ALLOCATED + err = E_OUT_OF_BOUNDS end if end function openmc_tally_results From 20331519beb2e940fff582f46423e4635bc61684 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 15:15:39 -0500 Subject: [PATCH 53/66] Add TallyView.nuclides --- openmc/capi/tally.py | 12 ++++++++++++ src/api.F90 | 27 ++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index f60d94e922..b7139bcb66 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -16,6 +16,10 @@ _dll.openmc_get_tally.errcheck = _error_handler _dll.openmc_tally_id.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_id.restype = c_int _dll.openmc_tally_id.errcheck = _error_handler +_dll.openmc_tally_nuclides.argtypes = [ + c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] +_dll.openmc_tally_nuclides.restype = c_int +_dll.openmc_tally_nuclides.errcheck = _error_handler _dll.openmc_tally_results.argtypes = [ c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] _dll.openmc_tally_results.restype = c_int @@ -39,6 +43,14 @@ class TallyView(object): _dll.openmc_tally_id(self._index, tally_id) return tally_id.value + @property + def nuclides(self): + nucs = POINTER(c_int)() + n = c_int() + _dll.openmc_tally_nuclides(self._index, nucs, n) + return [NuclideView(nucs[i]).name if nucs[i] > 0 else 'total' + for i in range(n.value)] + @property def results(self): """Get tally results array diff --git a/src/api.F90 b/src/api.F90 index 6d50ca51eb..cd8a8a0643 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -44,6 +44,7 @@ module openmc_api public :: openmc_reset public :: openmc_run public :: openmc_tally_id + public :: openmc_tally_nuclides public :: openmc_tally_results ! Error codes @@ -335,7 +336,7 @@ contains end function openmc_get_tally !=============================================================================== -! OPENMC_LOAD_LOAD loads a nuclide from the cross section library +! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library !=============================================================================== function openmc_load_nuclide(name) result(err) bind(C) @@ -657,6 +658,30 @@ contains end if end function openmc_tally_id +!=============================================================================== +! OPENMC_TALLY_NUCLIDES returns the list of nuclides assigned to a tally +!=============================================================================== + + function openmc_tally_nuclides(index, ptr, n) result(err) bind(C) + integer(C_INT32_T), value :: index + type(C_PTR), intent(out) :: ptr + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + + err = E_UNASSIGNED + if (index >= 1 .and. index <= size(tallies)) then + associate (t => tallies(index)) + if (allocated(t % nuclide_bins)) then + ptr = C_LOC(t % nuclide_bins(1)) + n = size(t % nuclide_bins) + err = 0 + end if + end associate + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_tally_nuclides + !=============================================================================== ! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its ! shape. This allows a user to obtain in-memory tally results from Python From a1a29aeb02911fa12b10823615d0e73c79c9e2c1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 15:49:36 -0500 Subject: [PATCH 54/66] Add TallyView.nuclides setter --- openmc/capi/cell.py | 8 ++--- openmc/capi/material.py | 8 ++--- openmc/capi/tally.py | 25 ++++++++----- src/api.F90 | 79 +++++++++++++++++++++++++++++++++-------- 4 files changed, 90 insertions(+), 30 deletions(-) diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index fba34db856..1dbe50d518 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -9,9 +9,9 @@ from openmc.capi import _dll, _error_handler __all__ = ['CellView', 'cells'] # Cell functions -_dll.openmc_cell_id.argtypes = [c_int32, POINTER(c_int32)] -_dll.openmc_cell_id.restype = c_int -_dll.openmc_cell_id.errcheck = _error_handler +_dll.openmc_cell_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_cell_get_id.restype = c_int +_dll.openmc_cell_get_id.errcheck = _error_handler _dll.openmc_cell_set_temperature.argtypes = [ c_int32, c_double, POINTER(c_int32)] _dll.openmc_cell_set_temperature.restype = c_int @@ -35,7 +35,7 @@ class CellView(object): @property def id(self): cell_id = c_int32() - _dll.openmc_cell_id(self._index, cell_id) + _dll.openmc_cell_get_id(self._index, cell_id) return cell_id.value def set_temperature(self, T, instance=None): diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 1863957882..c917d33317 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -18,9 +18,9 @@ _dll.openmc_material_add_nuclide.argtypes = [ c_int32, c_char_p, c_double] _dll.openmc_material_add_nuclide.restype = c_int _dll.openmc_material_add_nuclide.errcheck = _error_handler -_dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] -_dll.openmc_material_id.restype = c_int -_dll.openmc_material_id.errcheck = _error_handler +_dll.openmc_material_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_material_get_id.restype = c_int +_dll.openmc_material_get_id.errcheck = _error_handler _dll.openmc_material_get_densities.argtypes = [ c_int32, POINTER(POINTER(c_int)), POINTER(POINTER(c_double)), POINTER(c_int)] @@ -49,7 +49,7 @@ class MaterialView(object): @property def id(self): mat_id = c_int32() - _dll.openmc_material_id(self._index, mat_id) + _dll.openmc_material_get_id(self._index, mat_id) return mat_id.value @property diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index b7139bcb66..b1fc49d048 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -13,17 +13,20 @@ __all__ = ['TallyView', 'tallies'] _dll.openmc_get_tally.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_tally.restype = c_int _dll.openmc_get_tally.errcheck = _error_handler -_dll.openmc_tally_id.argtypes = [c_int32, POINTER(c_int32)] -_dll.openmc_tally_id.restype = c_int -_dll.openmc_tally_id.errcheck = _error_handler -_dll.openmc_tally_nuclides.argtypes = [ +_dll.openmc_tally_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_get_id.restype = c_int +_dll.openmc_tally_get_id.errcheck = _error_handler +_dll.openmc_tally_get_nuclides.argtypes = [ c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] -_dll.openmc_tally_nuclides.restype = c_int -_dll.openmc_tally_nuclides.errcheck = _error_handler +_dll.openmc_tally_get_nuclides.restype = c_int +_dll.openmc_tally_get_nuclides.errcheck = _error_handler _dll.openmc_tally_results.argtypes = [ c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler +_dll.openmc_tally_set_nuclides.argtypes = [c_int32, c_int, POINTER(c_char_p)] +_dll.openmc_tally_set_nuclides.restype = c_int +_dll.openmc_tally_set_nuclides.errcheck = _error_handler class TallyView(object): @@ -40,14 +43,14 @@ class TallyView(object): @property def id(self): tally_id = c_int32() - _dll.openmc_tally_id(self._index, tally_id) + _dll.openmc_tally_get_id(self._index, tally_id) return tally_id.value @property def nuclides(self): nucs = POINTER(c_int)() n = c_int() - _dll.openmc_tally_nuclides(self._index, nucs, n) + _dll.openmc_tally_get_nuclides(self._index, nucs, n) return [NuclideView(nucs[i]).name if nucs[i] > 0 else 'total' for i in range(n.value)] @@ -66,6 +69,12 @@ class TallyView(object): _dll.openmc_tally_results(self._index, data, shape) return as_array(data, tuple(shape[::-1])) + @nuclides.setter + def nuclides(self, nuclides): + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + _dll.openmc_tally_set_nuclides(self._index, len(nuclides), nucs) + class _TallyMapping(Mapping): def __getitem__(self, key): diff --git a/src/api.F90 b/src/api.F90 index cd8a8a0643..a06866425e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -23,7 +23,7 @@ module openmc_api private public :: openmc_calculate_volumes - public :: openmc_cell_id + public :: openmc_cell_get_id public :: openmc_cell_set_temperature public :: openmc_finalize public :: openmc_find @@ -34,8 +34,8 @@ module openmc_api public :: openmc_get_tally public :: openmc_init public :: openmc_load_nuclide - public :: openmc_material_id public :: openmc_material_add_nuclide + public :: openmc_material_get_id public :: openmc_material_get_densities public :: openmc_material_set_density public :: openmc_material_set_densities @@ -43,9 +43,10 @@ module openmc_api public :: openmc_plot_geometry public :: openmc_reset public :: openmc_run - public :: openmc_tally_id - public :: openmc_tally_nuclides + public :: openmc_tally_get_id + public :: openmc_tally_get_nuclides public :: openmc_tally_results + public :: openmc_tally_set_nuclides ! Error codes integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1 @@ -67,7 +68,7 @@ contains ! OPENMC_CELL_ID returns the ID of a cell !=============================================================================== - function openmc_cell_id(index, id) result(err) bind(C) + function openmc_cell_get_id(index, id) result(err) bind(C) integer(C_INT32_T), value :: index integer(C_INT32_T), intent(out) :: id integer(C_INT) :: err @@ -78,7 +79,7 @@ contains else err = E_OUT_OF_BOUNDS end if - end function openmc_cell_id + end function openmc_cell_get_id !=============================================================================== ! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell @@ -492,10 +493,10 @@ contains end function openmc_material_get_densities !=============================================================================== -! OPENMC_MATERIAL_ID returns the ID of a material +! OPENMC_MATERIAL_GET_ID returns the ID of a material !=============================================================================== - function openmc_material_id(index, id) result(err) bind(C) + function openmc_material_get_id(index, id) result(err) bind(C) integer(C_INT32_T), value :: index integer(C_INT32_T), intent(out) :: id integer(C_INT) :: err @@ -506,7 +507,7 @@ contains else err = E_OUT_OF_BOUNDS end if - end function openmc_material_id + end function openmc_material_get_id !=============================================================================== ! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm @@ -642,10 +643,10 @@ contains end subroutine openmc_reset !=============================================================================== -! OPENMC_TALLY_ID returns the ID of a tally +! OPENMC_TALLY_GET_ID returns the ID of a tally !=============================================================================== - function openmc_tally_id(index, id) result(err) bind(C) + function openmc_tally_get_id(index, id) result(err) bind(C) integer(C_INT32_T), value :: index integer(C_INT32_T), intent(out) :: id integer(C_INT) :: err @@ -656,13 +657,13 @@ contains else err = E_OUT_OF_BOUNDS end if - end function openmc_tally_id + end function openmc_tally_get_id !=============================================================================== ! OPENMC_TALLY_NUCLIDES returns the list of nuclides assigned to a tally !=============================================================================== - function openmc_tally_nuclides(index, ptr, n) result(err) bind(C) + function openmc_tally_get_nuclides(index, ptr, n) result(err) bind(C) integer(C_INT32_T), value :: index type(C_PTR), intent(out) :: ptr integer(C_INT), intent(out) :: n @@ -680,7 +681,7 @@ contains else err = E_OUT_OF_BOUNDS end if - end function openmc_tally_nuclides + end function openmc_tally_get_nuclides !=============================================================================== ! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its @@ -706,6 +707,56 @@ contains end if end function openmc_tally_results + function openmc_tally_set_nuclides(index, n, nuclides) result(err) bind(C) + integer(C_INT32_T), value :: index + integer(C_INT), value :: n + type(C_PTR), intent(in) :: nuclides(n) + integer(C_INT) :: err + + integer :: i + character(C_CHAR), pointer :: string(:) + character(len=:, kind=C_CHAR), allocatable :: nuclide_ + + err = E_UNASSIGNED + if (index >= 1 .and. index <= size(tallies)) then + associate (t => tallies(index)) + if (allocated(t % nuclide_bins)) deallocate(t % nuclide_bins) + allocate(t % nuclide_bins(n)) + t % n_nuclide_bins = n + + do i = 1, n + ! Convert C string to Fortran string + call c_f_pointer(nuclides(i), string, [10]) + nuclide_ = to_lower(to_f_string(string)) + + select case (nuclide_) + case ('total') + t % nuclide_bins(i) = -1 + case default + if (nuclide_dict % has_key(nuclide_)) then + t % nuclide_bins(i) = nuclide_dict % get_key(nuclide_) + else + err = E_NUCLIDE_NOT_LOADED + return + end if + end select + end do + + ! Recalculate total number of scoring bins + t % total_score_bins = t % n_score_bins * t % n_nuclide_bins + + ! (Re)allocate results array + if (allocated(t % results)) deallocate(t % results) + allocate(t % results(3, t % total_score_bins, t % total_filter_bins)) + t % results(:,:,:) = ZERO + + err = 0 + end associate + else + err = E_OUT_OF_BOUNDS + end if + end function openmc_tally_set_nuclides + function to_f_string(c_string) result(f_string) character(kind=C_CHAR), intent(in) :: c_string(*) character(:), allocatable :: f_string From 894e95d9018678a453bd1164daeb764633ce29c9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 30 Jul 2017 08:13:45 -0500 Subject: [PATCH 55/66] Reset timers in openmc_reset --- src/api.F90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/api.F90 b/src/api.F90 index a06866425e..b31bf387a9 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -640,6 +640,21 @@ contains call active_collision_tallies % clear() call active_tallies % clear() + ! Reset timers + call time_total % reset() + call time_total % reset() + call time_initialize % reset() + call time_read_xs % reset() + call time_unionize % reset() + call time_bank % reset() + call time_bank_sample % reset() + call time_bank_sendrecv % reset() + call time_tallies % reset() + call time_inactive % reset() + call time_active % reset() + call time_transport % reset() + call time_finalize % reset() + end subroutine openmc_reset !=============================================================================== From 1066489c66221bd6f100995d5275edd602f55d53 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 30 Jul 2017 08:28:05 -0500 Subject: [PATCH 56/66] Get rid of defaults for current_batch and current_gen --- src/api.F90 | 8 +++----- src/global.F90 | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index b31bf387a9..77b3dd576e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -131,8 +131,6 @@ contains check_overlaps = .false. confidence_intervals = .false. create_fission_neutrons = .true. - current_batch = 0 - current_gen = 0 energy_cutoff = ZERO energy_max_neutron = INFINITY energy_min_neutron = ZERO @@ -144,12 +142,12 @@ contains legendre_to_tabular = .true. legendre_to_tabular_points = 33 n_batch_interval = 1 - n_filters = 0 - n_meshes = 0 + n_filters = 0 + n_meshes = 0 n_particles = 0 n_source_points = 0 n_state_points = 0 - n_tallies = 0 + n_tallies = 0 n_user_filters = 0 n_user_meshes = 0 n_user_tallies = 0 diff --git a/src/global.F90 b/src/global.F90 index 5661cf2af3..2f47a9dce0 100644 --- a/src/global.F90 +++ b/src/global.F90 @@ -222,8 +222,8 @@ module global integer :: n_inactive ! # of inactive batches integer :: n_active ! # of active batches integer :: gen_per_batch = 1 ! # of generations per batch - integer :: current_batch = 0 ! current batch - integer :: current_gen = 0 ! current generation within a batch + integer :: current_batch ! current batch + integer :: current_gen ! current generation within a batch integer :: total_gen = 0 ! total number of generations simulated ! ============================================================================ From 9edad4876f7c2c2dd6f1961aa437ccf69b0004da Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sun, 30 Jul 2017 14:07:06 -0500 Subject: [PATCH 57/66] Add hard reset function --- openmc/capi/__init__.py | 14 +++++++++----- src/api.F90 | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index b3b5731c1a..6ae49c4ff8 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -1,17 +1,19 @@ """Provides bindings to C functions defined by OpenMC shared library. When the :mod:`openmc` package is imported, the OpenMC shared library is -automatically loaded. Calls to the OpenMC library can then be made, for example: +automatically loaded. Calls to the OpenMC library can then be via functions or +objects in the :mod:`openmc.capi` subpackage, for example: .. code-block:: python openmc.capi.init() openmc.capi.run() + openmc.capi.finalize() """ from contextlib import contextmanager -from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER +from ctypes import CDLL, c_int, c_int32, c_double, POINTER import sys from warnings import warn @@ -38,8 +40,6 @@ except OSError: _available = False - - class GeometryError(Exception): pass @@ -135,6 +135,10 @@ def find(xyz, rtype='cell'): return (uid.value if uid != 0 else None), instance.value +def hard_reset(): + _dll.openmc_hard_reset() + + def init(intracomm=None): """Initialize OpenMC @@ -185,7 +189,6 @@ def run(): _dll.openmc_run() - @contextmanager def run_in_memory(intracomm=None): """Provides context manager for calling OpenMC shared library functions. @@ -220,6 +223,7 @@ if _available: POINTER(c_double*3), c_int, POINTER(c_int32), POINTER(c_int32)] _dll.openmc_find.restype = c_int _dll.openmc_find.errcheck = _error_handler + _dll.openmc_hard_reset.restype = None _dll.openmc_init.argtypes = [POINTER(c_int)] _dll.openmc_init.restype = None _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] diff --git a/src/api.F90 b/src/api.F90 index 77b3dd576e..2c1fc968bc 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -15,7 +15,7 @@ module openmc_api use input_xml, only: assign_0K_elastic_scattering, check_data_version use particle_header, only: Particle use plot, only: openmc_plot_geometry - use random_lcg, only: seed + use random_lcg, only: seed, initialize_prng use simulation, only: openmc_run use volume_calc, only: openmc_calculate_volumes @@ -32,6 +32,7 @@ module openmc_api public :: openmc_get_material public :: openmc_get_nuclide public :: openmc_get_tally + public :: openmc_hard_reset public :: openmc_init public :: openmc_load_nuclide public :: openmc_material_add_nuclide @@ -334,6 +335,24 @@ contains end if end function openmc_get_tally +!=============================================================================== +! OPENMC_HARD_RESET reset tallies and timers as well as the pseudorandom +! generator state +!=============================================================================== + + subroutine openmc_hard_reset() bind(C) + ! Reset all tallies and timers + call openmc_reset() + + ! Reset total generations and keff guess + keff = ONE + total_gen = 0 + + ! Reset the random number generator state + seed = 1_8 + call initialize_prng() + end subroutine openmc_hard_reset + !=============================================================================== ! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library !=============================================================================== @@ -602,7 +621,7 @@ contains end function openmc_nuclide_name !=============================================================================== -! OPENMC_RESET resets all tallies +! OPENMC_RESET resets tallies and timers !=============================================================================== subroutine openmc_reset() bind(C) From dae2a169e6daa1b6d58e513b15f6831983eaefe5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 31 Jul 2017 06:57:09 -0500 Subject: [PATCH 58/66] Get rid of mpi_err global variable --- src/api.F90 | 12 +++++------ src/cmfd_execute.F90 | 6 ++++++ src/eigenvalue.F90 | 5 +++++ src/initialize.F90 | 1 + src/main.F90 | 4 ++++ src/mesh.F90 | 3 +++ src/message_passing.F90 | 1 - src/simulation.F90 | 47 +++++++++++++++++++---------------------- src/state_point.F90 | 4 +++- src/tally.F90 | 7 +++--- src/volume_calc.F90 | 1 + 11 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index 2c1fc968bc..b767d1ab4a 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -92,11 +92,11 @@ contains integer(C_INT32_T), optional, intent(in) :: instance integer(C_INT) :: err - integer :: i, n + integer :: n err = E_UNASSIGNED if (index >= 1 .and. index <= size(cells)) then - associate (c => cells(i)) + associate (c => cells(index)) if (allocated(c % sqrtkT)) then n = size(c % sqrtkT) if (present(instance) .and. n > 1) then @@ -122,7 +122,7 @@ contains subroutine openmc_finalize() bind(C) - integer :: hdf5_err + integer :: err ! Clear results call openmc_reset() @@ -190,14 +190,14 @@ contains call free_memory() ! Release compound datatypes - call h5tclose_f(hdf5_bank_t, hdf5_err) + call h5tclose_f(hdf5_bank_t, err) ! Close FORTRAN interface. - call h5close_f(hdf5_err) + call h5close_f(err) #ifdef MPI ! Free all MPI types - call MPI_TYPE_FREE(MPI_BANK, mpi_err) + call MPI_TYPE_FREE(MPI_BANK, err) #endif end subroutine openmc_finalize diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 index 0d4a9f4d35..e87bff930c 100644 --- a/src/cmfd_execute.F90 +++ b/src/cmfd_execute.F90 @@ -108,6 +108,9 @@ contains real(8) :: hxyz(3) ! cell dimensions of current ijk cell real(8) :: vol ! volume of cell real(8),allocatable :: source(:,:,:,:) ! tmp source array for entropy +#ifdef MPI + integer :: mpi_err ! MPI error code +#endif ! Get maximum of spatial and group indices nx = cmfd % indices(1) @@ -232,6 +235,9 @@ contains logical :: outside ! any source sites outside mesh logical :: in_mesh ! source site is inside mesh type(RegularMesh), pointer :: m ! point to mesh +#ifdef MPI + integer :: mpi_err +#endif ! Associate pointer m => meshes(n_user_meshes + 1) diff --git a/src/eigenvalue.F90 b/src/eigenvalue.F90 index 9f4da7c82f..accf80b310 100644 --- a/src/eigenvalue.F90 +++ b/src/eigenvalue.F90 @@ -40,6 +40,7 @@ contains & temp_sites(:) ! local array of extra sites on each node #ifdef MPI + integer :: mpi_err ! MPI error code integer(8) :: n ! number of sites to send/recv integer :: neighbor ! processor to send/recv data from #ifdef MPIF08 @@ -372,6 +373,9 @@ contains subroutine calculate_generation_keff() integer :: i ! overall generation +#ifdef MPI + integer :: mpi_err ! MPI error code +#endif ! Get keff for this generation by subtracting off the starting value keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - keff_generation @@ -614,6 +618,7 @@ contains logical :: sites_outside ! were there sites outside the ufs mesh? #ifdef MPI integer :: n ! total number of ufs mesh cells + integer :: mpi_err ! MPI error code #endif if (current_batch == 1 .and. current_gen == 1) then diff --git a/src/initialize.F90 b/src/initialize.F90 index 69479907e8..1e29c638c8 100644 --- a/src/initialize.F90 +++ b/src/initialize.F90 @@ -172,6 +172,7 @@ contains integer, intent(in) :: intracomm ! MPI intracommunicator #endif + integer :: mpi_err ! MPI error code integer :: bank_blocks(5) ! Count for each datatype #ifdef MPIF08 type(MPI_Datatype) :: bank_types(5) diff --git a/src/main.F90 b/src/main.F90 index b8c96927ea..fadf9f3bbd 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -9,6 +9,10 @@ program main implicit none +#ifdef MPI + integer :: mpi_err ! MPI error code +#endif + ! Initialize run -- when run with MPI, pass communicator #ifdef MPI #ifdef MPIF08 diff --git a/src/mesh.F90 b/src/mesh.F90 index 427710790a..c25e3f7a6a 100644 --- a/src/mesh.F90 +++ b/src/mesh.F90 @@ -144,6 +144,9 @@ contains integer :: ijk(3) ! indices on mesh integer :: n ! number of energy groups / size integer :: e_bin ! energy_bin +#ifdef MPI + integer :: mpi_err ! MPI error code +#endif logical :: in_mesh ! was single site outside mesh? logical :: outside ! was any site outside mesh? diff --git a/src/message_passing.F90 b/src/message_passing.F90 index 6c13b00e88..0f2b94d1af 100644 --- a/src/message_passing.F90 +++ b/src/message_passing.F90 @@ -16,7 +16,6 @@ module message_passing integer :: rank = 0 ! rank of process logical :: master = .true. ! master process? logical :: mpi_enabled = .false. ! is MPI in use and initialized? - integer :: mpi_err ! MPI error code #ifdef MPIF08 type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank type(MPI_Comm) :: mpi_intracomm ! MPI intra-communicator diff --git a/src/simulation.F90 b/src/simulation.F90 index 8a32493cac..b406679ceb 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -285,6 +285,10 @@ contains subroutine finalize_batch() +#ifdef MPI + integer :: mpi_err ! MPI error code +#endif + ! Reduce tallies onto master process and accumulate call time_tallies % start() call accumulate_tallies() @@ -392,7 +396,11 @@ contains integer :: i ! loop index for tallies integer :: n ! size of arrays - real(8) :: temp(3) ! temporary array for communication +#ifdef MPI + integer :: mpi_err ! MPI error code + integer(8) :: temp + real(8) :: tempr(3) ! temporary array for communication +#endif !$omp parallel deallocate(micro_xs) @@ -420,18 +428,23 @@ contains ! These guys are needed so that non-master processes can calculate the ! combined estimate of k-effective - temp(1) = k_col_abs - temp(2) = k_col_tra - temp(3) = k_abs_tra - call MPI_BCAST(temp, 3, MPI_REAL8, 0, mpi_intracomm, mpi_err) - k_col_abs = temp(1) - k_col_tra = temp(2) - k_abs_tra = temp(3) + tempr(1) = k_col_abs + tempr(2) = k_col_tra + tempr(3) = k_abs_tra + call MPI_BCAST(tempr, 3, MPI_REAL8, 0, mpi_intracomm, mpi_err) + k_col_abs = tempr(1) + k_col_tra = tempr(2) + k_abs_tra = tempr(3) + + if (check_overlaps) then + call MPI_REDUCE(overlap_check_cnt, temp, n_cells, MPI_INTEGER8, & + MPI_SUM, 0, mpi_intracomm, mpi_err) + overlap_check_cnt = temp + end if #endif ! Write tally results to tallies.out if (output_tallies .and. master) call write_tallies() - if (check_overlaps) call reduce_overlap_count() ! Stop timers and show timing statistics call time_finalize%stop() @@ -444,20 +457,4 @@ contains end subroutine finalize_simulation -!=============================================================================== -! REDUCE_OVERLAP_COUNT accumulates cell overlap check counts to master -!=============================================================================== - - subroutine reduce_overlap_count() - - integer(8) :: temp - -#ifdef MPI - call MPI_REDUCE(overlap_check_cnt, temp, n_cells, MPI_INTEGER8, & - MPI_SUM, 0, mpi_intracomm, mpi_err) - overlap_check_cnt = temp -#endif - - end subroutine reduce_overlap_count - end module simulation diff --git a/src/state_point.F90 b/src/state_point.F90 index 301a4efdd4..e2a476e7e9 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -522,7 +522,8 @@ contains real(8), allocatable :: tally_temp(:,:,:) ! contiguous array of results real(8), target :: global_temp(3,N_GLOBAL_TALLIES) #ifdef MPI - real(8) :: dummy ! temporary receive buffer for non-root reduces + integer :: mpi_err ! MPI error code + real(8) :: dummy ! temporary receive buffer for non-root reduces #endif type(TallyObject) :: dummy_tally @@ -841,6 +842,7 @@ contains #else integer :: i #ifdef MPI + integer :: mpi_err ! MPI error code type(Bank), allocatable, target :: temp_source(:) #endif #endif diff --git a/src/tally.F90 b/src/tally.F90 index 8b52ad0322..cf62412f03 100644 --- a/src/tally.F90 +++ b/src/tally.F90 @@ -4246,9 +4246,10 @@ contains subroutine reduce_tally_results() integer :: i - integer :: n ! number of filter bins - integer :: m ! number of score bins - integer :: n_bins ! total number of bins + integer :: n ! number of filter bins + integer :: m ! number of score bins + integer :: n_bins ! total number of bins + integer :: mpi_err ! MPI error code real(C_DOUBLE), allocatable :: tally_temp(:,:) ! contiguous array of results real(C_DOUBLE), allocatable :: tally_temp2(:,:) ! reduced contiguous results real(C_DOUBLE) :: temp(N_GLOBAL_TALLIES), temp2(N_GLOBAL_TALLIES) diff --git a/src/volume_calc.F90 b/src/volume_calc.F90 index 3659293b37..f8cf4085a5 100644 --- a/src/volume_calc.F90 +++ b/src/volume_calc.F90 @@ -132,6 +132,7 @@ contains integer :: min_samples ! minimum number of samples per process integer :: remainder ! leftover samples from uneven divide #ifdef MPI + integer :: mpi_err ! MPI error code integer :: m ! index over materials integer :: n ! number of materials integer, allocatable :: data(:) ! array used to send number of hits From d0faebe11d00239bc012c16dc09f8b2a03aa55fc Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 31 Jul 2017 08:18:55 -0500 Subject: [PATCH 59/66] Update documentation --- docs/source/capi/index.rst | 190 +++++++++++++++++++++++++++++---- docs/source/pythonapi/capi.rst | 25 ++++- openmc/capi/__init__.py | 7 +- openmc/capi/cell.py | 17 +++ openmc/capi/material.py | 23 +++- openmc/capi/nuclide.py | 33 +++--- openmc/capi/tally.py | 29 +++-- openmc/nuclide.py | 4 +- src/api.F90 | 13 ++- 9 files changed, 284 insertions(+), 57 deletions(-) diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index 9bb1b2c662..1e30eacf18 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -8,17 +8,28 @@ C API Run a stochastic volume calculation -.. c:function:: int openmc_cell_set_temperature(int id, double T, int* instance) +.. c:function:: int openmc_cell_get_id(int32_t index, int32_t* id) + + Get the ID of a cell + + :param index: Index in the cells array + :type index: int32_t + :param id: ID of the cell + :type id: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_cell_set_temperature(index index, double T, int32_t* instance) Set the temperature of a cell. - :param id: ID of the cell - :type id: int + :param index: Index in the cells array + :type index: int32_t :param T: Temperature in Kelvin :type T: double :param instance: Which instance of the cell. To set the temperature for all instances, pass a null pointer. - :type instance: int* + :type instance: int32_t* :return: Return status (negative if an error occurred) :rtype: int @@ -26,7 +37,7 @@ C API Finalize a simulation -.. c:function:: void openmc_find(double* xyz, int rtype, int* id, int* instance) +.. c:function:: void openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance) Determine the ID of the cell/material containing a given point @@ -36,10 +47,65 @@ C API :type rtype: int :param id: ID of the cell/material found. If a material is requested and the point is in a void, the ID is 0. If an error occurs, the ID is -1. - :type id: int + :type id: int32_t* :param instance: If a cell is repetaed in the geometry, the instance of the cell that was found and zero otherwise. - :type instance: int + :type instance: int32_t* + +.. c:function:: int openmc_get_cell(int32_t id, int32_t* index) + + Get the index in the cells array for a cell with a given ID + + :param id: ID of the cell + :type id: int32_t + :param index: Index in the cells array + :type index: int32_t* + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_get_keff(double k_combined[]) + + :param k_combined: Combined estimate of k-effective + :type k_combined: double[2] + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_get_nuclide(char name[], int* index) + + Get the index in the nuclides array for a nuclide with a given name + + :param name: Name of the nuclide + :type name: char[] + :param index: Index in the nuclides array + :type index: int* + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_get_tally(int32_t id, int32_t* index) + + Get the index in the tallies array for a tally with a given ID + + :param id: ID of the tally + :type id: int32_t + :param index: Index in the tallies array + :type index: int32_t* + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_get_material(int32_t id, int32_t* index) + + Get the index in the materials array for a material with a given ID + + :param id: ID of the material + :type id: int32_t + :param index: Index in the materials array + :type index: int32_t* + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: void openmc_hard_reset() + + Reset tallies, timers, and pseudo-random number generator state .. c:function:: void openmc_init(int intracomm) @@ -57,13 +123,13 @@ C API :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: int openmc_material_add_nuclide(int id, char name[], double density) +.. c:function:: int openmc_material_add_nuclide(int32_t index, char name[], double density) Add a nuclide to an existing material. If the nuclide already exists, the density is overwritten. - :param id: ID of the material - :type id: int + :param index: Index in the materials array + :type index: int32_t :param name: Name of the nuclide :type name: char[] :param density: Density in atom/b-cm @@ -71,28 +137,67 @@ C API :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: int openmc_material_get_densities(int id, double* ptr) +.. c:function:: int openmc_material_get_densities(int32_t index, int* nuclides[], double* densities[]) - Get an array of nuclide densities for a material. + Get density for each nuclide in a material. - :param id: ID of the material - :type id: int - :param ptr: Pointer to the array of densities - :type ptr: double* - :return: Length of the array + :param index: Index in the materials array + :type index: int32_t + :param nuclides: Pointer to array of nuclide indices + :type nuclides: int** + :param densities: Pointer to the array of densities + :type densities: double** + :param n: Length of the array + :type n: int + :return: Return status (negative if an error occurs) :rtype: int -.. c:function:: int openmc_material_set_density(int id, double density) +.. c:function:: int openmc_material_get_id(int32_t index, int32_t* id) + + Get the ID of a material + + :param index: Index in the materials array + :type index: int32_t + :param id: ID of the material + :type id: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_material_set_density(int32_t index, double density) Set the density of a material. - :param id: ID of the material - :type id: int + :param index: Index in the materials array + :type index: int32_t :param density: Density of the material in atom/b-cm :type density: double :return: Return status (negative if an error occurs) :rtype: int +.. c:function:: int openmc_material_set_densities(int32_t, n, char* name[], double density[]) + + :param index: Index in the materials array + :type index: int32_t + :param n: Length of name/density + :type n: int + :param name: Array of nuclide names + :type name: char** + :param density: Array of densities + :type density: double[] + :return: Return status (negative if an error occurs) + :rtype: int + +.. c:function:: int openmc_nuclide_name(int index, char* name[]) + + Get name of a nuclide + + :param index: Index in the nuclides array + :type index: int + :param name: Name of the nuclide + :type name: char** + :return: Return status (negative if an error occurs) + :rtype: int + .. c:function:: void openmc_plot_geometry() Run plotting mode. @@ -105,13 +210,52 @@ C API Run a simulation -.. c:function:: void openmc_tally_results(int id, double** ptr, int shape_[3]) +.. c:function:: int openmc_tally_get_id(int32_t index, int32_t* id) + + Get the ID of a tally + + :param index: Index in the tallies array + :type index: int32_t + :param id: ID of the tally + :type id: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_tally_get_nuclides(int32_t index, int* nuclides[], int* n) + + Get nuclides specified in a tally + + :param index: Index in the tallies array + :type index: int32_t + :param nuclides: Array of nuclide indices + :type nuclides: int** + :param n: Number of nuclides + :type n: int* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_tally_results(int32_t index, double** ptr, int shape_[3]) Get a pointer to tally results array. - :param id: ID of the tally - :type id: int + :param index: Index in the tallies array + :type index: int32_t :param ptr: Pointer to the results array :type ptr: double** :param shape_: Shape of the results array :type shape_: int[3] + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_tally_set_nuclides(int32_t index, int n, char* nuclides[]) + + Set the nuclides for a tally + + :param index: Index in the tallies array + :type index: int32_t + :param n: Number of nuclides + :type n: int + :param nuclides: Array of nuclide names + :type nuclides: char** + :return: Return status (negative if an error occurred) + :rtype: int diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index af00363cc6..a586cb4add 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -2,16 +2,37 @@ :data:`openmc.capi` -- Python bindings to the C API --------------------------------------------------- +.. automodule:: openmc.capi + +Functions +--------- + .. autosummary:: :toctree: generated :nosignatures: :template: myfunction.rst - openmc.capi.lib_context + openmc.capi.calculate_volumes + openmc.capi.finalize + openmc.capi.find + openmc.capi.hard_reset + openmc.capi.init + openmc.capi.keff + openmc.capi.load_nuclide + openmc.capi.plot_geometry + openmc.capi.reset + openmc.capi.run + openmc.capi.run_in_memory + +Classes +------- .. autosummary:: :toctree: generated :nosignatures: :template: myclass.rst - openmc.capi.OpenMCLibrary + openmc.capi.CellView + openmc.capi.MaterialView + openmc.capi.NuclideView + openmc.capi.TallyView diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 6ae49c4ff8..a4f16ab71c 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -1,5 +1,5 @@ -"""Provides bindings to C functions defined by OpenMC shared library. - +""" +This module provides bindings to C functions defined by OpenMC shared library. When the :mod:`openmc` package is imported, the OpenMC shared library is automatically loaded. Calls to the OpenMC library can then be via functions or objects in the :mod:`openmc.capi` subpackage, for example: @@ -136,6 +136,7 @@ def find(xyz, rtype='cell'): def hard_reset(): + """Reset tallies, timers, and pseudo-random number generator state.""" _dll.openmc_hard_reset() @@ -180,7 +181,7 @@ def plot_geometry(): def reset(): - """Reset tallies""" + """Reset tallies and timers.""" _dll.openmc_reset() diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 1dbe50d518..9abada00c8 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -22,6 +22,23 @@ _dll.openmc_get_cell.errcheck = _error_handler class CellView(object): + """View of a cell. + + This class exposes a cell that is stored internally in the OpenMC solver. To + obtain a view of a cell with a given ID, use the + :data:`openmc.capi.nuclides` mapping. + + Parameters + ---------- + index : int + Index in the `cells` array. + + Attributes + ---------- + id : int + ID of the cell + + """ __instances = WeakValueDictionary() def __new__(cls, *args): if args not in cls.__instances: diff --git a/openmc/capi/material.py b/openmc/capi/material.py index c917d33317..564ecaa053 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -7,8 +7,8 @@ from numpy.ctypeslib import as_array from openmc.capi import _dll, _error_handler, NuclideView -__all__ = ['MaterialView', 'materials'] +__all__ = ['MaterialView', 'materials'] # Material functions _dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] @@ -36,6 +36,27 @@ _dll.openmc_material_set_densities.errcheck = _error_handler class MaterialView(object): + """View of a material. + + This class exposes a material that is stored internally in the OpenMC + solver. To obtain a view of a material with a given ID, use the + :data:`openmc.capi.materials` mapping. + + Parameters + ---------- + index : int + Index in the `materials` array. + + Attributes + ---------- + id : int + ID of the material + nuclides : list of str + List of nuclides in the material + densities : numpy.ndarray + Array of densities in atom/b-cm + + """ __instances = WeakValueDictionary() def __new__(cls, *args): if args not in cls.__instances: diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index 7a2f7a37b4..384d0ae917 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -28,13 +28,30 @@ def load_nuclide(name): Parameters ---------- name : str - Name of nuclide, e.g. 'U235' + Name of the nuclide, e.g. 'U235' """ _dll.openmc_load_nuclide(name.encode()) class NuclideView(object): + """View of a nuclide. + + This class exposes a nuclide that is stored internally in the OpenMC + solver. To obtain a view of a nuclide with a given name, use the + :data:`openmc.capi.nuclides` mapping. + + Parameters + ---------- + index : int + Index in the `nuclides` array. + + Attributes + ---------- + name : str + Name of the nuclide, e.g. 'U235' + + """ __instances = WeakValueDictionary() def __new__(cls, *args): if args not in cls.__instances: @@ -47,19 +64,6 @@ class NuclideView(object): @property def name(self): - """Name of nuclide with given index - - Parameter - --------- - index : int - Index in internal nuclides array - - Returns - ------- - str - Name of nuclide - - """ name = c_char_p() _dll.openmc_nuclide_name(self._index, name) @@ -71,6 +75,7 @@ class NuclideView(object): class _NuclideMapping(Mapping): + """Provide mapping from nuclide name to index in nuclides array.""" def __getitem__(self, key): index = c_int() _dll.openmc_get_nuclide(key.encode(), index) diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index b1fc49d048..cc8c13a5a7 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -30,6 +30,27 @@ _dll.openmc_tally_set_nuclides.errcheck = _error_handler class TallyView(object): + """View of a tally. + + This class exposes a tally that is stored internally in the OpenMC + solver. To obtain a view of a tally with a given ID, use the + :data:`openmc.capi.tallies` mapping. + + Parameters + ---------- + index : int + Index in the `tallys` array. + + Attributes + ---------- + id : int + ID of the tally + nuclides : list of str + List of nuclides to score results for + results : numpy.ndarray + Array of tally results + + """ __instances = WeakValueDictionary() def __new__(cls, *args): if args not in cls.__instances: @@ -56,14 +77,6 @@ class TallyView(object): @property def results(self): - """Get tally results array - - Returns - ------- - numpy.ndarray - Array that exposes the internal tally results array - - """ data = POINTER(c_double)() shape = (c_int*3)() _dll.openmc_tally_results(self._index, data, shape) diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 03062ee0e2..fc0d7ef07f 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -11,12 +11,12 @@ class Nuclide(object): Parameters ---------- name : str - Name of the nuclide, e.g. U235 + Name of the nuclide, e.g. 'U235' Attributes ---------- name : str - Name of the nuclide, e.g. U235 + Name of the nuclide, e.g. 'U235' scattering : 'data' or 'iso-in-lab' or None The type of angular scattering distribution to use diff --git a/src/api.F90 b/src/api.F90 index b767d1ab4a..40ac0f01f1 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -66,7 +66,7 @@ module openmc_api contains !=============================================================================== -! OPENMC_CELL_ID returns the ID of a cell +! OPENMC_CELL_GET_ID returns the ID of a cell !=============================================================================== function openmc_cell_get_id(index, id) result(err) bind(C) @@ -695,9 +695,9 @@ contains ! OPENMC_TALLY_NUCLIDES returns the list of nuclides assigned to a tally !=============================================================================== - function openmc_tally_get_nuclides(index, ptr, n) result(err) bind(C) + function openmc_tally_get_nuclides(index, nuclides, n) result(err) bind(C) integer(C_INT32_T), value :: index - type(C_PTR), intent(out) :: ptr + type(C_PTR), intent(out) :: nuclides integer(C_INT), intent(out) :: n integer(C_INT) :: err @@ -705,7 +705,7 @@ contains if (index >= 1 .and. index <= size(tallies)) then associate (t => tallies(index)) if (allocated(t % nuclide_bins)) then - ptr = C_LOC(t % nuclide_bins(1)) + nuclides = C_LOC(t % nuclide_bins(1)) n = size(t % nuclide_bins) err = 0 end if @@ -739,6 +739,11 @@ contains end if end function openmc_tally_results +!=============================================================================== +! OPENMC_TALLY_SET_NUCLIDES sets the nuclides in the tally which results should +! be scored for +!=============================================================================== + function openmc_tally_set_nuclides(index, n, nuclides) result(err) bind(C) integer(C_INT32_T), value :: index integer(C_INT), value :: n From f155dfad29e40eb2a99482909923bca391e80c2d Mon Sep 17 00:00:00 2001 From: April Novak Date: Mon, 31 Jul 2017 16:13:12 -0500 Subject: [PATCH 60/66] added check to make sure that temperature is not set to a value outside data bounds and that cell cannot be filled with a universe. Added several more error codes. Refs #7 --- src/api.F90 | 99 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index 40ac0f01f1..d17d57ef99 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -62,6 +62,12 @@ module openmc_api integer(C_INT), public, bind(C) :: E_MATERIAL_INVALID_ID = -10 integer(C_INT), public, bind(C) :: E_TALLY_NOT_ALLOCATED = -11 integer(C_INT), public, bind(C) :: E_TALLY_INVALID_ID = -12 + integer(C_INT), public, bind(C) :: E_INVALID_SIZE = -13 + integer(C_INT), public, bind(C) :: E_CELL_NO_MATERIAL = -14 + + ! Warning codes + integer(C_INT), public, bind(C) :: W_BELOW_MIN_BOUND = 1 + integer(C_INT), public, bind(C) :: W_ABOVE_MAX_BOUND = 2 contains @@ -87,29 +93,92 @@ contains !=============================================================================== function openmc_cell_set_temperature(index, T, instance) result(err) bind(C) - integer(C_INT32_T), value, intent(in) :: index - real(C_DOUBLE), value, intent(in) :: T - integer(C_INT32_T), optional, intent(in) :: instance - integer(C_INT) :: err + integer(C_INT32_T), value, intent(in) :: index ! cell index in cells + real(C_DOUBLE), value, intent(in) :: T ! temperature + integer(C_INT32_T), optional, intent(in) :: instance ! cell instance - integer :: n + integer(C_INT) :: err ! error code + integer :: j ! looping variable + integer :: n ! number of cell instances + integer :: material_ID ! material associated with cell + integer :: material_index ! material index in materials array + integer :: num_nuclides ! num nuclides in material + integer :: nuclide_index ! index of nuclide in nuclides array + real(8) :: min_temp ! min common-denominator avail temp + real(8) :: max_temp ! max common-denominator avail temp + real(8) :: temp ! actual temp we'll assign + logical :: outside_low ! lower than available data + logical :: outside_high ! higher than available data + + outside_low = .false. + outside_high = .false. err = E_UNASSIGNED + if (index >= 1 .and. index <= size(cells)) then - associate (c => cells(index)) - if (allocated(c % sqrtkT)) then - n = size(c % sqrtkT) - if (present(instance) .and. n > 1) then - if (instance >= 0 .and. instance < n) then - c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * T) + + ! error if the cell is filled with another universe + if (cells(index) % fill /= NONE) then + err = E_CELL_NO_MATERIAL + else + ! find which material is associated with this cell + if (present(instance)) then + material_ID = cells(index) % material(instance) + else + material_ID = cells(index) % material(1) + end if + + ! index of that material into the materials array + material_index = material_dict % get_key(material_ID) + + ! number of nuclides associated with this material + num_nuclides = size(materials(material_index) % nuclide) + + min_temp = ZERO + max_temp = INFINITY + + do j = 1, num_nuclides + nuclide_index = materials(material_index) % nuclide(j) + min_temp = max(min_temp, minval(nuclides(nuclide_index) % kTs)) + max_temp = min(max_temp, maxval(nuclides(nuclide_index) % kTs)) + end do + + ! adjust the temperature to be within bounds if necessary + if (K_BOLTZMANN * T < min_temp) then + outside_low = .true. + temp = min_temp / K_BOLTZMANN + else if (K_BOLTZMANN * T > max_temp) then + outside_high = .true. + temp = max_temp / K_BOLTZMANN + else + temp = T + end if + + associate (c => cells(index)) + if (allocated(c % sqrtkT)) then + n = size(c % sqrtkT) + if (present(instance) .and. n > 1) then + if (instance >= 0 .and. instance < n) then + c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * temp) + err = 0 + end if + else + c % sqrtkT(:) = sqrt(K_BOLTZMANN * temp) err = 0 end if - else - c % sqrtkT(:) = sqrt(K_BOLTZMANN * T) - err = 0 end if + end associate + + ! assign error codes for outside of temperature bounds provided the + ! temperature was changed correctly. This needs to be done after + ! changing the temperature based on the logical structure above. + if (err == 0) then + if (outside_low) err = W_BELOW_MIN_BOUND + if (outside_high) err = W_ABOVE_MAX_BOUND end if - end associate + + end if + else err = E_OUT_OF_BOUNDS end if From 064e1aef596ebd0e780d433d192ae0cc8224e252 Mon Sep 17 00:00:00 2001 From: April Novak Date: Mon, 31 Jul 2017 17:29:35 -0500 Subject: [PATCH 61/66] added error and warning code handling in Python API for new codes. Refs #7 --- openmc/capi/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index a4f16ab71c..fabeac947c 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -86,6 +86,19 @@ def _error_handler(err, func, args): elif err == _error_code('e_tally_invalid_id'): raise KeyError("No tally exists with ID={}.".format(args[0])) + elif err == _error_code('e_invalid_size'): + raise MemoryError("Array size mismatch with memory allocated.") + + elif err == _error_code('e_cell_no_material'): + raise GeometryError("Operation on cell requires that it be filled" + " with a material.") + + elif err == _error_code('w_below_min_bound'): + warn("Data has not been loaded beyond lower bound of {}.".format(args[0])) + + elif err == _error_code('w_above_max_bound'): + warn("Data has not been loaded beyond upper bound of {}.".format(args[0])) + elif err < 0: raise Exception("Unknown error encountered (code {}).".format(err)) From a49b4246f455c9ce270469b2a325c6c220c00155 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 31 Jul 2017 23:00:37 -0500 Subject: [PATCH 62/66] Reimplement openmc_material_set_densities. Move assign_sab_tables to material_header. --- src/api.F90 | 37 ++++++------ src/input_xml.F90 | 123 +--------------------------------------- src/material_header.F90 | 120 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 136 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index d17d57ef99..0941e60fee 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -633,30 +633,35 @@ contains if (index >= 1 .and. index <= size(materials)) then associate (m => materials(index)) + ! If nuclide/density arrays are not correct size, reallocate + if (n /= size(m % nuclide)) then + deallocate(m % nuclide, m % atom_density, m % p0) + allocate(m % nuclide(n), m % atom_density(n), m % p0(n)) + end if + do i = 1, n ! Convert C string to Fortran string call c_f_pointer(name(i), string, [10]) - name_ = to_f_string(string) + name_ = to_lower(to_f_string(string)) - ! Find corresponding nuclide and set density - err = E_UNASSIGNED - do j = 1, size(m % nuclide) - k = m % nuclide(j) - if (nuclides(k) % name == name_) then - m % atom_density(j) = density(i) - err = 0 - end if - end do - - ! If nuclide wasn't found, try to load it - if (err /= 0) then - err = openmc_material_add_nuclide(index, string, density(i)) - if (err /= 0) return + if (.not. nuclide_dict % has_key(name_)) then + err = openmc_load_nuclide(string) + if (err < 0) return end if + + m % nuclide(i) = nuclide_dict % get_key(name_) + m % atom_density(i) = density(i) end do + m % n_nuclides = n + + ! Set isotropic flags to flags + m % p0(:) = .false. ! Set total density to the sum of the vector - err = m % set_density(sum(m % atom_density), nuclides) + err = m % set_density(sum(density), nuclides) + + ! Assign S(a,b) tables + call m % assign_sab_tables(nuclides, sab_tables) end associate else err = E_OUT_OF_BOUNDS diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 322ca8a76f..2fc6ef9188 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -5146,123 +5146,6 @@ contains end subroutine normalize_ao -!=============================================================================== -! ASSIGN_SAB_TABLES assigns S(alpha,beta) tables to specific nuclides within -! materials so the code knows when to apply bound thermal scattering data -!=============================================================================== - - subroutine assign_sab_tables() - integer :: i ! index in materials array - integer :: j ! index over nuclides in material - integer :: k ! index over S(a,b) tables in material - integer :: m ! position for sorting - integer :: temp_nuclide ! temporary value for sorting - integer :: temp_table ! temporary value for sorting - real(8) :: temp_frac ! temporary value for sorting - logical :: found - type(VectorInt) :: i_sab_tables - type(VectorInt) :: i_sab_nuclides - type(VectorReal) :: sab_fracs - - do i = 1, size(materials) - ! Skip materials with no S(a,b) tables - if (.not. allocated(materials(i) % i_sab_tables)) cycle - - associate (mat => materials(i)) - - ASSIGN_SAB: do k = 1, size(mat % i_sab_tables) - ! In order to know which nuclide the S(a,b) table applies to, we need - ! to search through the list of nuclides for one which has a matching - ! name - found = .false. - associate (sab => sab_tables(mat % i_sab_tables(k))) - FIND_NUCLIDE: do j = 1, size(mat % nuclide) - if (any(sab % nuclides == nuclides(mat % nuclide(j)) % name)) then - call i_sab_tables % push_back(mat % i_sab_tables(k)) - call i_sab_nuclides % push_back(j) - call sab_fracs % push_back(mat % sab_fracs(k)) - found = .true. - end if - end do FIND_NUCLIDE - end associate - - ! Check to make sure S(a,b) table matched a nuclide - if (.not. found) then - call fatal_error("S(a,b) table " // trim(mat % & - sab_names(k)) // " did not match any nuclide on material " & - // trim(to_str(mat % id))) - end if - end do ASSIGN_SAB - - ! Make sure each nuclide only appears in one table. - do j = 1, i_sab_nuclides % size() - do k = j+1, i_sab_nuclides % size() - if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then - call fatal_error(trim( & - nuclides(mat % nuclide(i_sab_nuclides % data(j))) % name) & - // " in material " // trim(to_str(mat % id)) // " was found & - &in multiple S(a,b) tables. Each nuclide can only appear in & - &one S(a,b) table per material.") - end if - end do - end do - - ! Update i_sab_tables and i_sab_nuclides - deallocate(mat % i_sab_tables) - deallocate(mat % sab_fracs) - m = i_sab_tables % size() - allocate(mat % i_sab_tables(m)) - allocate(mat % i_sab_nuclides(m)) - allocate(mat % sab_fracs(m)) - mat % i_sab_tables(:) = i_sab_tables % data(1:m) - mat % i_sab_nuclides(:) = i_sab_nuclides % data(1:m) - mat % sab_fracs(:) = sab_fracs % data(1:m) - - ! Clear entries in vectors for next material - call i_sab_tables % clear() - call i_sab_nuclides % clear() - call sab_fracs % clear() - - ! If there are multiple S(a,b) tables, we need to make sure that the - ! entries in i_sab_nuclides are sorted or else they won't be applied - ! correctly in the cross_section module. The algorithm here is a simple - ! insertion sort -- don't need anything fancy! - - if (size(mat % i_sab_tables) > 1) then - SORT_SAB: do k = 2, size(mat % i_sab_tables) - ! Save value to move - m = k - temp_nuclide = mat % i_sab_nuclides(k) - temp_table = mat % i_sab_tables(k) - temp_frac = mat % i_sab_tables(k) - - MOVE_OVER: do - ! Check if insertion value is greater than (m-1)th value - if (temp_nuclide >= mat % i_sab_nuclides(m-1)) exit - - ! Move values over until hitting one that's not larger - mat % i_sab_nuclides(m) = mat % i_sab_nuclides(m-1) - mat % i_sab_tables(m) = mat % i_sab_tables(m-1) - mat % sab_fracs(m) = mat % sab_fracs(m-1) - m = m - 1 - - ! Exit if we've reached the beginning of the list - if (m == 1) exit - end do MOVE_OVER - - ! Put the original value into its new position - mat % i_sab_nuclides(m) = temp_nuclide - mat % i_sab_tables(m) = temp_table - mat % sab_fracs(m) = temp_frac - end do SORT_SAB - end if - - ! Deallocate temporary arrays for names of nuclides and S(a,b) tables - if (allocated(mat % names)) deallocate(mat % names) - end associate - end do - end subroutine assign_sab_tables - subroutine read_ce_cross_sections(nuc_temps, sab_temps) type(VectorReal), intent(in) :: nuc_temps(:) type(VectorReal), intent(in) :: sab_temps(:) @@ -5367,10 +5250,10 @@ contains call already_read % add(name) end if end do - end do - ! Associate S(a,b) tables with specific nuclides - call assign_sab_tables() + ! Associate S(a,b) tables with specific nuclides + call materials(i) % assign_sab_tables(nuclides, sab_tables) + end do ! Show which nuclide results in lowest energy for neutron transport do i = 1, size(nuclides) diff --git a/src/material_header.F90 b/src/material_header.F90 index 4415ad0867..5d45c40d60 100644 --- a/src/material_header.F90 +++ b/src/material_header.F90 @@ -1,7 +1,11 @@ module material_header use constants + use error, only: fatal_error use nuclide_header, only: Nuclide + use sab_header, only: SAlphaBeta + use stl_vector, only: VectorReal, VectorInt + use string, only: to_str implicit none @@ -44,6 +48,7 @@ module material_header contains procedure :: set_density => material_set_density + procedure :: assign_sab_tables => material_assign_sab_tables end type Material contains @@ -85,4 +90,119 @@ contains end if end function material_set_density +!=============================================================================== +! ASSIGN_SAB_TABLES assigns S(alpha,beta) tables to specific nuclides within +! materials so the code knows when to apply bound thermal scattering data +!=============================================================================== + + subroutine material_assign_sab_tables(this, nuclides, sab_tables) + class(Material), intent(inout) :: this + type(Nuclide), intent(in) :: nuclides(:) + type(SAlphaBeta), intent(in) :: sab_tables(:) + + integer :: j ! index over nuclides in material + integer :: k ! index over S(a,b) tables in material + integer :: m ! position for sorting + integer :: temp_nuclide ! temporary value for sorting + integer :: temp_table ! temporary value for sorting + real(8) :: temp_frac ! temporary value for sorting + logical :: found + type(VectorInt) :: i_sab_tables + type(VectorInt) :: i_sab_nuclides + type(VectorReal) :: sab_fracs + + if (.not. allocated(this % i_sab_tables)) return + + ASSIGN_SAB: do k = 1, size(this % i_sab_tables) + ! In order to know which nuclide the S(a,b) table applies to, we need + ! to search through the list of nuclides for one which has a matching + ! name + found = .false. + associate (sab => sab_tables(this % i_sab_tables(k))) + FIND_NUCLIDE: do j = 1, size(this % nuclide) + if (any(sab % nuclides == nuclides(this % nuclide(j)) % name)) then + call i_sab_tables % push_back(this % i_sab_tables(k)) + call i_sab_nuclides % push_back(j) + call sab_fracs % push_back(this % sab_fracs(k)) + found = .true. + end if + end do FIND_NUCLIDE + end associate + + ! Check to make sure S(a,b) table matched a nuclide + if (.not. found) then + call fatal_error("S(a,b) table " // trim(this % & + sab_names(k)) // " did not match any nuclide on material " & + // trim(to_str(this % id))) + end if + end do ASSIGN_SAB + + ! Make sure each nuclide only appears in one table. + do j = 1, i_sab_nuclides % size() + do k = j+1, i_sab_nuclides % size() + if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then + call fatal_error(trim( & + nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) & + // " in material " // trim(to_str(this % id)) // " was found & + &in multiple S(a,b) tables. Each nuclide can only appear in & + &one S(a,b) table per material.") + end if + end do + end do + + ! Update i_sab_tables and i_sab_nuclides + deallocate(this % i_sab_tables) + deallocate(this % sab_fracs) + if (allocated(this % i_sab_nuclides)) deallocate(this % i_sab_nuclides) + m = i_sab_tables % size() + allocate(this % i_sab_tables(m)) + allocate(this % i_sab_nuclides(m)) + allocate(this % sab_fracs(m)) + this % i_sab_tables(:) = i_sab_tables % data(1:m) + this % i_sab_nuclides(:) = i_sab_nuclides % data(1:m) + this % sab_fracs(:) = sab_fracs % data(1:m) + + ! Clear entries in vectors for next material + call i_sab_tables % clear() + call i_sab_nuclides % clear() + call sab_fracs % clear() + + ! If there are multiple S(a,b) tables, we need to make sure that the + ! entries in i_sab_nuclides are sorted or else they won't be applied + ! correctly in the cross_section module. The algorithm here is a simple + ! insertion sort -- don't need anything fancy! + + if (size(this % i_sab_tables) > 1) then + SORT_SAB: do k = 2, size(this % i_sab_tables) + ! Save value to move + m = k + temp_nuclide = this % i_sab_nuclides(k) + temp_table = this % i_sab_tables(k) + temp_frac = this % i_sab_tables(k) + + MOVE_OVER: do + ! Check if insertion value is greater than (m-1)th value + if (temp_nuclide >= this % i_sab_nuclides(m-1)) exit + + ! Move values over until hitting one that's not larger + this % i_sab_nuclides(m) = this % i_sab_nuclides(m-1) + this % i_sab_tables(m) = this % i_sab_tables(m-1) + this % sab_fracs(m) = this % sab_fracs(m-1) + m = m - 1 + + ! Exit if we've reached the beginning of the list + if (m == 1) exit + end do MOVE_OVER + + ! Put the original value into its new position + this % i_sab_nuclides(m) = temp_nuclide + this % i_sab_tables(m) = temp_table + this % sab_fracs(m) = temp_frac + end do SORT_SAB + end if + + ! Deallocate temporary arrays for names of nuclides and S(a,b) tables + if (allocated(this % names)) deallocate(this % names) + end subroutine material_assign_sab_tables + end module material_header From d0676c7a857488028a9a912d8ceae90b7dd55b9d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 3 Aug 2017 06:31:09 -0500 Subject: [PATCH 63/66] Break up capi/__init__ into __init__, core, and error --- openmc/__init__.py | 1 - openmc/capi/__init__.py | 218 +--------------------------------------- openmc/capi/cell.py | 4 +- openmc/capi/core.py | 149 +++++++++++++++++++++++++++ openmc/capi/error.py | 66 ++++++++++++ openmc/capi/material.py | 4 +- openmc/capi/nuclide.py | 4 +- openmc/capi/tally.py | 4 +- 8 files changed, 231 insertions(+), 219 deletions(-) create mode 100644 openmc/capi/core.py create mode 100644 openmc/capi/error.py diff --git a/openmc/__init__.py b/openmc/__init__.py index 571b5a93a4..d692ebbae2 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -27,6 +27,5 @@ from openmc.particle_restart import * from openmc.mixin import * from openmc.plotter import * from openmc.search import * -import openmc.capi __version__ = '0.9.0' diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index fabeac947c..2452da7b11 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -12,12 +12,10 @@ objects in the :mod:`openmc.capi` subpackage, for example: """ -from contextlib import contextmanager -from ctypes import CDLL, c_int, c_int32, c_double, POINTER +from ctypes import CDLL import sys from warnings import warn -from numpy.ctypeslib import as_array import pkg_resources @@ -32,221 +30,13 @@ _filename = pkg_resources.resource_filename( __name__, '_libopenmc.{}'.format(_suffix)) try: _dll = CDLL(_filename) - _available = True except OSError: warn("OpenMC shared library is not available from the Python API. This " "means you will not be able to use openmc.capi to make in-memory " "calls to OpenMC.") - _available = False - - -class GeometryError(Exception): - pass - - -def _error_code(s): - """Get error code corresponding to global constant.""" - return c_int.in_dll(_dll, s).value - - -def _error_handler(err, func, args): - """Raise exception according to error code.""" - if err == _error_code('e_out_of_bounds'): - raise IndexError('Array index out of bounds.') - - elif err == _error_code('e_cell_not_allocated'): - raise MemoryError("Memory has not been allocated for cells.") - - elif err == _error_code('e_cell_invalid_id'): - raise KeyError("No cell exists with ID={}.".format(args[0])) - - elif err == _error_code('e_cell_not_found'): - raise GeometryError("Could not find cell at position ({}, {}, {})" - .format(*args[0])) - - elif err == _error_code('e_nuclide_not_allocated'): - raise MemoryError("Memory has not been allocated for nuclides.") - - elif err == _error_code('e_nuclide_not_loaded'): - raise KeyError("No nuclide named '{}' has been loaded.") - - elif err == _error_code('e_nuclide_not_in_library'): - raise KeyError("Specified nuclide doesn't exist in the cross " - "section library.") - - elif err == _error_code('e_material_not_allocated'): - raise MemoryError("Memory has not been allocated for materials.") - - elif err == _error_code('e_material_invalid_id'): - raise KeyError("No material exists with ID={}.".format(args[0])) - - elif err == _error_code('e_tally_not_allocated'): - raise MemoryError("Memory has not been allocated for tallies.") - - elif err == _error_code('e_tally_invalid_id'): - raise KeyError("No tally exists with ID={}.".format(args[0])) - - elif err == _error_code('e_invalid_size'): - raise MemoryError("Array size mismatch with memory allocated.") - - elif err == _error_code('e_cell_no_material'): - raise GeometryError("Operation on cell requires that it be filled" - " with a material.") - - elif err == _error_code('w_below_min_bound'): - warn("Data has not been loaded beyond lower bound of {}.".format(args[0])) - - elif err == _error_code('w_above_max_bound'): - warn("Data has not been loaded beyond upper bound of {}.".format(args[0])) - - elif err < 0: - raise Exception("Unknown error encountered (code {}).".format(err)) - - -def calculate_volumes(): - """Run stochastic volume calculation""" - _dll.openmc_calculate_volumes() - - -def finalize(): - """Finalize simulation and free memory""" - _dll.openmc_finalize() - - -def find(xyz, rtype='cell'): - """Find the cell or material at a given point - - Parameters - ---------- - xyz : iterable of float - Cartesian coordinates of position - rtype : {'cell', 'material'} - Whether to return the cell or material ID - - Returns - ------- - int or None - ID of the cell or material. If 'material' is requested and no - material exists at the given coordinate, None is returned. - int - If the cell at the given point is repeated in the geometry, this - indicates which instance it is, i.e., 0 would be the first instance. - - """ - # Set second argument to openmc_find - if rtype == 'cell': - r_int = 1 - elif rtype == 'material': - r_int = 2 - else: - raise ValueError('Unknown return type: {}'.format(rtype)) - - # Call openmc_find - uid = c_int32() - instance = c_int32() - _dll.openmc_find((c_double*3)(*xyz), r_int, uid, instance) - return (uid.value if uid != 0 else None), instance.value - - -def hard_reset(): - """Reset tallies, timers, and pseudo-random number generator state.""" - _dll.openmc_hard_reset() - - -def init(intracomm=None): - """Initialize OpenMC - - Parameters - ---------- - intracomm : mpi4py.MPI.Intracomm or None - MPI intracommunicator - - """ - if intracomm is not None: - # If an mpi4py communicator was passed, convert it to an integer to - # be passed to openmc_init - try: - intracomm = intracomm.py2f() - except AttributeError: - pass - _dll.openmc_init(c_int(intracomm)) - else: - _dll.openmc_init(None) - - -def keff(): - """Return the calculated k-eigenvalue and its standard deviation. - - Returns - ------- - tuple - Mean k-eigenvalue and standard deviation of the mean - - """ - k = (c_double*2)() - _dll.openmc_get_keff(k) - return tuple(k) - - -def plot_geometry(): - """Plot geometry""" - _dll.openmc_plot_geometry() - - -def reset(): - """Reset tallies and timers.""" - _dll.openmc_reset() - - -def run(): - """Run simulation""" - _dll.openmc_run() - - -@contextmanager -def run_in_memory(intracomm=None): - """Provides context manager for calling OpenMC shared library functions. - - This function is intended to be used in a 'with' statement and ensures that - OpenMC is properly initialized/finalized. At the completion of the 'with' - block, all memory that was allocated during the block is freed. For - example:: - - with openmc.capi.run_in_memory(): - for i in range(n_iters): - openmc.capi.reset() - do_stuff() - openmc.capi.run() - - Parameters - ---------- - intracomm : mpi4py.MPI.Intracomm or None - MPI intracommunicator - - """ - init(intracomm) - yield - finalize() - - -# Set argument/return types -if _available: - _dll.openmc_calculate_volumes.restype = None - _dll.openmc_finalize.restype = None - _dll.openmc_find.argtypes = [ - POINTER(c_double*3), c_int, POINTER(c_int32), POINTER(c_int32)] - _dll.openmc_find.restype = c_int - _dll.openmc_find.errcheck = _error_handler - _dll.openmc_hard_reset.restype = None - _dll.openmc_init.argtypes = [POINTER(c_int)] - _dll.openmc_init.restype = None - _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] - _dll.openmc_get_keff.restype = c_int - _dll.openmc_get_keff.errcheck = _error_handler - _dll.openmc_plot_geometry.restype = None - _dll.openmc_run.restype = None - _dll.openmc_reset.restype = None - +else: + from .error import * + from .core import * from .nuclide import * from .material import * from .cell import * diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 9abada00c8..f519d2f6dc 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -4,7 +4,8 @@ from weakref import WeakValueDictionary import numpy as np -from openmc.capi import _dll, _error_handler +from . import _dll +from .error import _error_handler __all__ = ['CellView', 'cells'] @@ -40,6 +41,7 @@ class CellView(object): """ __instances = WeakValueDictionary() + def __new__(cls, *args): if args not in cls.__instances: instance = super().__new__(cls) diff --git a/openmc/capi/core.py b/openmc/capi/core.py new file mode 100644 index 0000000000..74669540b8 --- /dev/null +++ b/openmc/capi/core.py @@ -0,0 +1,149 @@ +from contextlib import contextmanager +from ctypes import CDLL, c_int, c_int32, c_double, POINTER +from warnings import warn + +from . import _dll +from .error import _error_handler + + +_dll.openmc_calculate_volumes.restype = None +_dll.openmc_finalize.restype = None +_dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32), + POINTER(c_int32)] +_dll.openmc_find.restype = c_int +_dll.openmc_find.errcheck = _error_handler +_dll.openmc_hard_reset.restype = None +_dll.openmc_init.argtypes = [POINTER(c_int)] +_dll.openmc_init.restype = None +_dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] +_dll.openmc_get_keff.restype = c_int +_dll.openmc_get_keff.errcheck = _error_handler +_dll.openmc_plot_geometry.restype = None +_dll.openmc_run.restype = None +_dll.openmc_reset.restype = None + + +def calculate_volumes(): + """Run stochastic volume calculation""" + _dll.openmc_calculate_volumes() + + +def finalize(): + """Finalize simulation and free memory""" + _dll.openmc_finalize() + + +def find(xyz, rtype='cell'): + """Find the cell or material at a given point + + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates of position + rtype : {'cell', 'material'} + Whether to return the cell or material ID + + Returns + ------- + int or None + ID of the cell or material. If 'material' is requested and no + material exists at the given coordinate, None is returned. + int + If the cell at the given point is repeated in the geometry, this + indicates which instance it is, i.e., 0 would be the first instance. + + """ + # Set second argument to openmc_find + if rtype == 'cell': + r_int = 1 + elif rtype == 'material': + r_int = 2 + else: + raise ValueError('Unknown return type: {}'.format(rtype)) + + # Call openmc_find + uid = c_int32() + instance = c_int32() + _dll.openmc_find((c_double*3)(*xyz), r_int, uid, instance) + return (uid.value if uid != 0 else None), instance.value + + +def hard_reset(): + """Reset tallies, timers, and pseudo-random number generator state.""" + _dll.openmc_hard_reset() + + +def init(intracomm=None): + """Initialize OpenMC + + Parameters + ---------- + intracomm : mpi4py.MPI.Intracomm or None + MPI intracommunicator + + """ + if intracomm is not None: + # If an mpi4py communicator was passed, convert it to an integer to + # be passed to openmc_init + try: + intracomm = intracomm.py2f() + except AttributeError: + pass + _dll.openmc_init(c_int(intracomm)) + else: + _dll.openmc_init(None) + + +def keff(): + """Return the calculated k-eigenvalue and its standard deviation. + + Returns + ------- + tuple + Mean k-eigenvalue and standard deviation of the mean + + """ + k = (c_double*2)() + _dll.openmc_get_keff(k) + return tuple(k) + + +def plot_geometry(): + """Plot geometry""" + _dll.openmc_plot_geometry() + + +def reset(): + """Reset tallies and timers.""" + _dll.openmc_reset() + + +def run(): + """Run simulation""" + _dll.openmc_run() + + +@contextmanager +def run_in_memory(intracomm=None): + """Provides context manager for calling OpenMC shared library functions. + + This function is intended to be used in a 'with' statement and ensures that + OpenMC is properly initialized/finalized. At the completion of the 'with' + block, all memory that was allocated during the block is freed. For + example:: + + with openmc.capi.run_in_memory(): + for i in range(n_iters): + openmc.capi.reset() + do_stuff() + openmc.capi.run() + + Parameters + ---------- + intracomm : mpi4py.MPI.Intracomm or None + MPI intracommunicator + + """ + init(intracomm) + yield + finalize() diff --git a/openmc/capi/error.py b/openmc/capi/error.py new file mode 100644 index 0000000000..2058ceb204 --- /dev/null +++ b/openmc/capi/error.py @@ -0,0 +1,66 @@ +from ctypes import c_int + +from . import _dll + + +class GeometryError(Exception): + pass + + +def _error_handler(err, func, args): + """Raise exception according to error code.""" + + # Get error code corresponding to global constant. + def errcode(s): + return c_int.in_dll(_dll, s).value + + if err == errcode('e_out_of_bounds'): + raise IndexError('Array index out of bounds.') + + elif err == errcode('e_cell_not_allocated'): + raise MemoryError("Memory has not been allocated for cells.") + + elif err == errcode('e_cell_invalid_id'): + raise KeyError("No cell exists with ID={}.".format(args[0])) + + elif err == errcode('e_cell_not_found'): + raise GeometryError("Could not find cell at position ({}, {}, {})" + .format(*args[0])) + + elif err == errcode('e_nuclide_not_allocated'): + raise MemoryError("Memory has not been allocated for nuclides.") + + elif err == errcode('e_nuclide_not_loaded'): + raise KeyError("No nuclide named '{}' has been loaded.") + + elif err == errcode('e_nuclide_not_in_library'): + raise KeyError("Specified nuclide doesn't exist in the cross " + "section library.") + + elif err == errcode('e_material_not_allocated'): + raise MemoryError("Memory has not been allocated for materials.") + + elif err == errcode('e_material_invalid_id'): + raise KeyError("No material exists with ID={}.".format(args[0])) + + elif err == errcode('e_tally_not_allocated'): + raise MemoryError("Memory has not been allocated for tallies.") + + elif err == errcode('e_tally_invalid_id'): + raise KeyError("No tally exists with ID={}.".format(args[0])) + + elif err == errcode('e_invalid_size'): + raise MemoryError("Array size mismatch with memory allocated.") + + elif err == errcode('e_cell_no_material'): + raise GeometryError("Operation on cell requires that it be filled" + " with a material.") + + elif err == errcode('w_below_min_bound'): + warn("Data has not been loaded beyond lower bound of {}.".format(args[0])) + + elif err == errcode('w_above_max_bound'): + warn("Data has not been loaded beyond upper bound of {}.".format(args[0])) + + elif err < 0: + raise Exception("Unknown error encountered (code {}).".format(err)) diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 564ecaa053..d07d7aa8a9 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -5,7 +5,8 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array -from openmc.capi import _dll, _error_handler, NuclideView +from . import _dll, NuclideView +from .error import _error_handler __all__ = ['MaterialView', 'materials'] @@ -58,6 +59,7 @@ class MaterialView(object): """ __instances = WeakValueDictionary() + def __new__(cls, *args): if args not in cls.__instances: instance = super().__new__(cls) diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index 384d0ae917..4c6248d6be 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -5,7 +5,8 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array -from openmc.capi import _dll, _error_handler +from . import _dll +from .error import _error_handler __all__ = ['NuclideView', 'nuclides', 'load_nuclide'] @@ -53,6 +54,7 @@ class NuclideView(object): """ __instances = WeakValueDictionary() + def __new__(cls, *args): if args not in cls.__instances: instance = super().__new__(cls) diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index cc8c13a5a7..611f1ce879 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -4,7 +4,8 @@ from weakref import WeakValueDictionary from numpy.ctypeslib import as_array -from openmc.capi import _dll, _error_handler, NuclideView +from . import _dll, NuclideView +from .error import _error_handler __all__ = ['TallyView', 'tallies'] @@ -52,6 +53,7 @@ class TallyView(object): """ __instances = WeakValueDictionary() + def __new__(cls, *args): if args not in cls.__instances: instance = super().__new__(cls) From 745b0b2d83cdbd28fcb4f565b77b31ef6f888350 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 3 Aug 2017 06:33:46 -0500 Subject: [PATCH 64/66] Remove try/except block around CDLL --- openmc/capi/__init__.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 2452da7b11..355304a310 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -28,16 +28,11 @@ else: # Open shared library _filename = pkg_resources.resource_filename( __name__, '_libopenmc.{}'.format(_suffix)) -try: - _dll = CDLL(_filename) -except OSError: - warn("OpenMC shared library is not available from the Python API. This " - "means you will not be able to use openmc.capi to make in-memory " - "calls to OpenMC.") -else: - from .error import * - from .core import * - from .nuclide import * - from .material import * - from .cell import * - from .tally import * +_dll = CDLL(_filename) + +from .error import * +from .core import * +from .nuclide import * +from .material import * +from .cell import * +from .tally import * From cd8a297353c37cfeb4fdf4ef5600baa592b5e7d9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 3 Aug 2017 07:12:37 -0500 Subject: [PATCH 65/66] Break up capi.find into find_cell and find_material --- docs/source/pythonapi/capi.rst | 3 ++- openmc/capi/core.py | 44 ++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index a586cb4add..6e4e860fad 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -14,7 +14,8 @@ Functions openmc.capi.calculate_volumes openmc.capi.finalize - openmc.capi.find + openmc.capi.find_cell + openmc.capi.find_material openmc.capi.hard_reset openmc.capi.init openmc.capi.keff diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 74669540b8..fe5416dd61 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -33,39 +33,47 @@ def finalize(): _dll.openmc_finalize() -def find(xyz, rtype='cell'): - """Find the cell or material at a given point +def find_cell(xyz): + """Find the cell at a given point Parameters ---------- xyz : iterable of float Cartesian coordinates of position - rtype : {'cell', 'material'} - Whether to return the cell or material ID Returns ------- - int or None - ID of the cell or material. If 'material' is requested and no - material exists at the given coordinate, None is returned. + int + ID of the cell. int If the cell at the given point is repeated in the geometry, this indicates which instance it is, i.e., 0 would be the first instance. """ - # Set second argument to openmc_find - if rtype == 'cell': - r_int = 1 - elif rtype == 'material': - r_int = 2 - else: - raise ValueError('Unknown return type: {}'.format(rtype)) - - # Call openmc_find uid = c_int32() instance = c_int32() - _dll.openmc_find((c_double*3)(*xyz), r_int, uid, instance) - return (uid.value if uid != 0 else None), instance.value + _dll.openmc_find((c_double*3)(*xyz), 1, uid, instance) + return uid.value, instance.value + + +def find_material(xyz): + """Find the material at a given point + + Parameters + ---------- + xyz : iterable of float + Cartesian coordinates of position + + Returns + ------- + int or None + ID of the material or None is no material is found + + """ + uid = c_int32() + instance = c_int32() + _dll.openmc_find((c_double*3)(*xyz), 2, uid, instance) + return uid.value if uid != 0 else None def hard_reset(): From 242d633185828d48e522350a48fae96ffbe0c8f9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 3 Aug 2017 10:35:23 -0500 Subject: [PATCH 66/66] Address more @smharper comments --- src/api.F90 | 9 +++++++-- src/simulation.F90 | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index 0941e60fee..d45360d284 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -169,7 +169,7 @@ contains end if end associate - ! assign error codes for outside of temperature bounds provided the + ! Assign error codes for outside of temperature bounds provided the ! temperature was changed correctly. This needs to be done after ! changing the temperature based on the logical structure above. if (err == 0) then @@ -627,7 +627,7 @@ contains real(C_DOUBLE), intent(in) :: density(n) integer(C_INT) :: err - integer :: i, j, k + integer :: i character(C_CHAR), pointer :: string(:) character(len=:, kind=C_CHAR), allocatable :: name_ @@ -868,6 +868,11 @@ contains end if end function openmc_tally_set_nuclides +!=============================================================================== +! TO_F_STRING takes a null-terminated array of C chars and turns it into a +! deferred-length character string. Yay Fortran 2003! +!=============================================================================== + function to_f_string(c_string) result(f_string) character(kind=C_CHAR), intent(in) :: c_string(*) character(:), allocatable :: f_string diff --git a/src/simulation.F90 b/src/simulation.F90 index b406679ceb..7427443bbe 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -394,9 +394,9 @@ contains subroutine finalize_simulation() - integer :: i ! loop index for tallies - integer :: n ! size of arrays #ifdef MPI + integer :: i ! loop index for tallies + integer :: n ! size of arrays integer :: mpi_err ! MPI error code integer(8) :: temp real(8) :: tempr(3) ! temporary array for communication