diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index 8af4f2398a..c3d078eba4 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -4,7 +4,7 @@ Summary File Format =================== -The current revision of the summary file format is 1. +The current revision of the summary file format is 4. **/filetype** (*char[]*) @@ -129,7 +129,16 @@ The current revision of the summary file format is 1. **/geometry/cells/cell /distribcell_index** (*int*) - Index of this cell in distribcell filter arrays. + Index of this cell in distribcell arrays. Only present if this cell is + listed in a distribcell filter or if it uses distributed materials. + +**/geometry/cells/cell /distribcell_index** (*char[][]*) + + The paths traversed through the CSG tree to reach each distribcell + instance. This consists of the integer IDs for each universe, cell and + lattice delimited by '->'. Each lattice cell is specified by its (x,y) or + (x,y,z) indices. Only present if this cell is listed in a distribcell filter + or if it uses distributed materials. **/geometry/surfaces/surface /index** (*int*) @@ -244,90 +253,6 @@ The current revision of the summary file format is 1. Names of S(:math:`\alpha`,:math:`\beta`) tables assigned to the material. -**/tallies/n_tallies** (*int*) - - Number of tallies in the problem. - -**/tallies/n_meshes** (*int*) - - Number of meshes in the problem. - -**/tallies/mesh /index** (*int*) - - Index in the meshes array used internally in OpenMC. - -**/tallies/mesh /type** (*char[]*) - - Type of the mesh. The only valid option is currently 'regular'. - -**/tallies/mesh /dimension** (*int[]*) - - Number of mesh cells in each direction. - -**/tallies/mesh /lower_left** (*double[]*) - - Coordinates of the lower-left corner of the mesh. - -**/tallies/mesh /upper_right** (*double[]*) - - Coordinates of the upper-right corner of the mesh. - -**/tallies/mesh /width** (*double[]*) - - Width of a single mesh cell in each direction. - -**/tallies/tally /index** (*int*) - - Index in tallies array used internally in OpenMC. - **/tallies/tally /name** (*char[]*) Name of the tally. - -**/tallies/tally /n_filters** (*int*) - - Number of filters applied to the tally. - -**/tallies/tally /filter /type** (*char[]*) - - Type of the j-th filter. Can be 'universe', 'material', 'cell', 'cellborn', - 'surface', 'mesh', 'energy', 'energyout', or 'distribcell'. - -**/tallies/tally /filter /offset** (*int*) - - Filter offset (used for distribcell filter). - -**/tallies/tally /filter /paths** (*char[][]*) - - The paths traversed through the CSG tree to reach each distribcell - instance (for 'distribcell' filters only). This consists of the integer - IDs for each universe, cell and lattice delimited by '->'. Each lattice - cell is specified by its (x,y) or (x,y,z) indices. - -**/tallies/tally /filter /n_bins** (*int*) - - Number of bins for the j-th filter. - -**/tallies/tally /filter /bins** (*int[]* or *double[]*) - - Value for each filter bin of this type. - -**/tallies/tally /nuclides** (*char[][]*) - - Array of nuclides to tally. Note that if no nuclide is specified in the user - input, a single 'total' nuclide appears here. - -**/tallies/tally /n_score_bins** (*int*) - - Number of scoring bins for a single nuclide. In general, this can be greater - than the number of user-specified scores since each score might have - multiple scoring bins, e.g., scatter-PN. - -**/tallies/tally /moment_orders** (*char[][]*) - - Tallying moment orders for Legendre and spherical harmonic tally expansions - (*e.g.*, 'P2', 'Y1,2', etc.). - -**/tallies/tally /score_bins** (*char[][]*) - - Scoring bins for the tally. diff --git a/openmc/cell.py b/openmc/cell.py index fe8b4a52cd..fad6d2d7e2 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -85,6 +85,9 @@ class Cell(object): Array of offsets used for distributed cell searches distribcell_index : int Index of this cell in distribcell arrays + distribcell_paths : list of str + The paths traversed through the CSG tree to reach each distribcell + instance volume_information : dict Estimate of the volume and total number of atoms of each nuclide from a stochastic volume calculation. This information is set with the @@ -104,6 +107,7 @@ class Cell(object): self._translation = None self._offsets = None self._distribcell_index = None + self._distribcell_paths = None self._volume_information = None def __contains__(self, point): @@ -217,6 +221,10 @@ class Cell(object): def distribcell_index(self): return self._distribcell_index + @property + def distribcell_paths(self): + return self._distribcell_paths + @property def volume_information(self): return self._volume_information @@ -316,6 +324,12 @@ class Cell(object): cv.check_type('distribcell index', ind, Integral) self._distribcell_index = ind + @distribcell_paths.setter + def distribcell_paths(self, distribcell_paths): + cv.check_iterable_type('distribcell_paths', distribcell_paths, + basestring) + self._distribcell_paths = distribcell_paths + def add_surface(self, surface, halfspace): """Add a half-space to the list of half-spaces whose intersection defines the cell. diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 01154b33d9..8f18580501 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -691,19 +691,10 @@ class StatePoint(object): raise ValueError(msg) for tally_id, tally in self.tallies.items(): - summary_tally = summary.tallies[tally_id] - tally.name = summary_tally.name + tally.name = summary.tally_names[tally_id] tally.with_summary = True for tally_filter in tally.filters: - summary_filter = summary_tally.find_filter(tally_filter.type) - - if tally_filter.type == 'surface': - surface_ids = [] - for bin in tally_filter.bins: - surface_ids.append(bin) - tally_filter.bins = surface_ids - if tally_filter.type in ['cell', 'distribcell']: distribcell_ids = [] for bin in tally_filter.bins: @@ -711,8 +702,9 @@ class StatePoint(object): tally_filter.bins = distribcell_ids if tally_filter.type == 'distribcell': - tally_filter.distribcell_paths = \ - summary_filter.distribcell_paths + cell_id = tally_filter.bins[0] + cell = summary.get_cell_by_id(cell_id) + tally_filter.distribcell_paths = cell.distribcell_paths if tally_filter.type == 'universe': universe_ids = [] diff --git a/openmc/summary.py b/openmc/summary.py index 38c0e335c7..fd1689eb28 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -297,10 +297,13 @@ class Summary(object): cell.region = Region.from_expression( region, {s.id: s for s in self.surfaces.values()}) - # Get the distribcell index - ind = self._f['geometry/cells'][key]['distribcell_index'].value - if ind != 0: + # Get the distribcell data + if 'distribcell_index' in self._f['geometry/cells'][key]: + ind = self._f['geometry/cells'][key]['distribcell_index'].value cell.distribcell_index = ind + paths = self._f['geometry/cells'][key]['paths'][...] + paths = [str(path.decode()) for path in paths] + cell.distribcell_paths = paths # Add the Cell to the global dictionary of all Cells self.cells[index] = cell @@ -520,18 +523,15 @@ class Summary(object): self.openmc_geometry.root_universe = root_universe def _read_tallies(self): - # Initialize dictionaries for the Tallies + # Initialize a dictionary for the tally names # Keys - Tally IDs - # Values - Tally objects - self.tallies = {} + # Values - Tally names + self.tally_names = {} # Read the number of tallies if 'tallies' not in self._f: - self.n_tallies = 0 return - self.n_tallies = self._f['tallies/n_tallies'].value - # OpenMC Tally keys all_keys = self._f['tallies/'].keys() tally_keys = [key for key in all_keys if 'tally' in key] @@ -545,52 +545,7 @@ class Summary(object): # Read Tally name metadata tally_name = self._f['{0}/name'.format(subbase)].value.decode() - - # Create Tally object and assign basic properties - tally = openmc.Tally(tally_id, tally_name) - - # Read scattering moment order strings (e.g., P3, Y1,2, etc.) - moments = self._f['{0}/moment_orders'.format(subbase)].value - - # Read score metadata - scores = self._f['{0}/score_bins'.format(subbase)].value - for j, score in enumerate(scores): - score = score.decode() - - # If this is a moment, use generic moment order - pattern = r'-n$|-pn$|-yn$' - score = re.sub(pattern, '-' + moments[j].decode(), score) - tally.scores.append(score) - - # Read filter metadata - num_filters = self._f['{0}/n_filters'.format(subbase)].value - - # Initialize all Filters - for j in range(1, num_filters+1): - subsubbase = '{0}/filter {1}'.format(subbase, j) - - # Read filter type (e.g., "cell", "energy", etc.) - filter_type = self._f['{0}/type'.format(subsubbase)].value.decode() - - # Read the filter bins - num_bins = self._f['{0}/n_bins'.format(subsubbase)].value - bins = self._f['{0}/bins'.format(subsubbase)][...] - - # Create Filter object - new_filter = openmc.Filter(filter_type, bins) - new_filter.num_bins = num_bins - - # Read in distribcell paths - if filter_type == 'distribcell': - paths = self._f['{0}/paths'.format(subsubbase)][...] - paths = [str(path.decode()) for path in paths] - new_filter.distribcell_paths = paths - - # Add Filter to the Tally - tally.filters.append(new_filter) - - # Add Tally to the global dictionary of all Tallies - self.tallies[tally_id] = tally + self.tally_names[tally_id] = tally_name def add_volume_information(self, volume_calc): """Add volume information to the geometry within the summary file diff --git a/src/constants.F90 b/src/constants.F90 index c877472c21..cc0d663ea3 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -14,7 +14,7 @@ module constants integer, parameter :: REVISION_STATEPOINT = 15 integer, parameter :: REVISION_PARTICLE_RESTART = 1 integer, parameter :: REVISION_TRACK = 1 - integer, parameter :: REVISION_SUMMARY = 3 + integer, parameter :: REVISION_SUMMARY = 4 character(10), parameter :: MULTIPOLE_VERSION = "v0.2" ! ============================================================================ diff --git a/src/summary.F90 b/src/summary.F90 index cc0c517588..97abeb2063 100644 --- a/src/summary.F90 +++ b/src/summary.F90 @@ -2,8 +2,8 @@ module summary use constants use endf, only: reaction_name - use geometry_header, only: Cell, Universe, Lattice, RectLattice, & - &HexLattice + use geometry_header, only: BASE_UNIVERSE, Cell, Universe, Lattice, & + RectLattice, HexLattice use global use hdf5_interface use material_header, only: Material @@ -13,6 +13,7 @@ module summary use surface_header use string, only: to_str use tally_header, only: TallyObject + use tally_filter, only: find_offset use hdf5 @@ -150,7 +151,7 @@ contains subroutine write_geometry(file_id) integer(HID_T), intent(in) :: file_id - integer :: i, j, k, m + integer :: i, j, k, m, offset integer, allocatable :: lattice_universes(:,:,:) integer, allocatable :: cell_materials(:) real(8), allocatable :: cell_temperatures(:) @@ -161,6 +162,8 @@ contains integer(HID_T) :: lattices_group, lattice_group real(8), allocatable :: coeffs(:) character(REGION_SPEC_LEN) :: region_spec + character(MAX_LINE_LEN), allocatable :: paths(:) + character(MAX_LINE_LEN) :: path type(Cell), pointer :: c class(Surface), pointer :: s type(Universe), pointer :: u @@ -266,7 +269,21 @@ contains end do call write_dataset(cell_group, "region", adjustl(region_spec)) - call write_dataset(cell_group, "distribcell_index", c % distribcell_index) + ! Write distribcell data + if (c % distribcell_index /= NONE) then + call write_dataset(cell_group, "distribcell_index", & + c % distribcell_index) + + allocate(paths(c % instances)) + do k = 1, c % instances + path = '' + offset = 1 + call find_offset(i, universes(BASE_UNIVERSE), k, offset, path) + paths(k) = path + end do + call write_dataset(cell_group, "paths", paths) + deallocate(paths) + end if call close_group(cell_group) end do CELL_LOOP @@ -566,119 +583,21 @@ contains subroutine write_tallies(file_id) integer(HID_T), intent(in) :: file_id - integer :: i, j, k - integer :: n_order ! loop index for moment orders - integer :: nm_order ! loop index for Ynm moment orders + integer :: i integer(HID_T) :: tallies_group - integer(HID_T) :: mesh_group integer(HID_T) :: tally_group - integer(HID_T) :: filter_group - character(20), allocatable :: str_array(:) - type(RegularMesh), pointer :: m type(TallyObject), pointer :: t tallies_group = create_group(file_id, "tallies") - ! Write total number of meshes - call write_dataset(tallies_group, "n_meshes", n_meshes) - - ! Write information for meshes - MESH_LOOP: do i = 1, n_meshes - m => meshes(i) - mesh_group = create_group(tallies_group, "mesh " // trim(to_str(m%id))) - - ! Write internal OpenMC index for this mesh - call write_dataset(mesh_group, "index", i) - - ! Write type and number of dimensions - call write_dataset(mesh_group, "type", "regular") - - ! Write mesh information - call write_dataset(mesh_group, "dimension", m%dimension) - call write_dataset(mesh_group, "lower_left", m%lower_left) - call write_dataset(mesh_group, "upper_right", m%upper_right) - call write_dataset(mesh_group, "width", m%width) - - call close_group(mesh_group) - end do MESH_LOOP - - ! Write number of tallies - call write_dataset(tallies_group, "n_tallies", n_tallies) - TALLY_METADATA: do i = 1, n_tallies ! Get pointer to tally t => tallies(i) - tally_group = create_group(tallies_group, "tally " // trim(to_str(t%id))) - - ! Write internal OpenMC index for this tally - call write_dataset(tally_group, "index", i) + tally_group = create_group(tallies_group, "tally " & + // trim(to_str(t % id))) ! Write the name for this tally - call write_dataset(tally_group, "name", t%name) - - ! Write number of filters - call write_dataset(tally_group, "n_filters", size(t % filters)) - - FILTER_LOOP: do j = 1, size(t % filters) - filter_group = create_group(tally_group, "filter " // trim(to_str(j))) - call t % filters(j) % obj % to_summary(filter_group) - call close_group(filter_group) - end do FILTER_LOOP - - ! Create temporary array for nuclide bins - allocate(str_array(t%n_nuclide_bins)) - NUCLIDE_LOOP: do j = 1, t%n_nuclide_bins - if (t%nuclide_bins(j) > 0) then - str_array(j) = nuclides(t % nuclide_bins(j)) % name - else - str_array(j) = 'total' - end if - end do NUCLIDE_LOOP - - ! Write and deallocate nuclide bins - call write_dataset(tally_group, "nuclides", str_array) - deallocate(str_array) - - ! Write number of score bins - call write_dataset(tally_group, "n_score_bins", t%n_score_bins) - allocate(str_array(size(t%score_bins))) - do j = 1, size(t%score_bins) - str_array(j) = reaction_name(t%score_bins(j)) - end do - call write_dataset(tally_group, "score_bins", str_array) - - deallocate(str_array) - - ! Write explicit moment order strings for each score bin - k = 1 - allocate(str_array(t%n_score_bins)) - MOMENT_LOOP: do j = 1, t%n_user_score_bins - select case(t%score_bins(k)) - case (SCORE_SCATTER_N, SCORE_NU_SCATTER_N) - str_array(k) = 'P' // trim(to_str(t%moment_order(k))) - k = k + 1 - case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN) - do n_order = 0, t%moment_order(k) - str_array(k) = 'P' // trim(to_str(n_order)) - k = k + 1 - end do - case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, & - SCORE_TOTAL_YN) - do n_order = 0, t%moment_order(k) - do nm_order = -n_order, n_order - str_array(k) = 'Y' // trim(to_str(n_order)) // ',' // & - trim(to_str(nm_order)) - k = k + 1 - end do - end do - case default - str_array(k) = '' - k = k + 1 - end select - end do MOMENT_LOOP - - call write_dataset(tally_group, "moment_orders", str_array) - deallocate(str_array) + call write_dataset(tally_group, "name", t % name) call close_group(tally_group) end do TALLY_METADATA diff --git a/src/tally_filter.F90 b/src/tally_filter.F90 index 51b93ac4cf..f151203468 100644 --- a/src/tally_filter.F90 +++ b/src/tally_filter.F90 @@ -80,7 +80,6 @@ module tally_filter contains procedure :: get_next_bin => get_next_bin_distribcell procedure :: to_statepoint => to_statepoint_distribcell - procedure :: to_summary => to_summary_distribcell procedure :: text_label => text_label_distribcell procedure :: initialize => initialize_distribcell end type DistribcellFilter @@ -714,36 +713,6 @@ contains call write_dataset(filter_group, "bins", this % cell ) end subroutine to_statepoint_distribcell - subroutine to_summary_distribcell(this, filter_group) - class(DistribcellFilter), intent(in) :: this - integer(HID_T), intent(in) :: filter_group - - integer :: offset, k - character(MAX_LINE_LEN), allocatable :: paths(:) - character(MAX_LINE_LEN) :: path - - call write_dataset(filter_group, "type", "distribcell") - call write_dataset(filter_group, "n_bins", this % n_bins) - call write_dataset(filter_group, "bins", this % cell ) - - ! Write paths to reach each distribcell instance - - ! Allocate array of strings for each distribcell path - allocate(paths(this % n_bins)) - - ! Store path for each distribcell instance - do k = 1, this % n_bins - path = '' - offset = 1 - call find_offset(this % cell, universes(BASE_UNIVERSE), k, offset, path) - paths(k) = path - end do - - ! Write array of distribcell paths to summary file - call write_dataset(filter_group, "paths", paths) - deallocate(paths) - end subroutine to_summary_distribcell - subroutine initialize_distribcell(this) class(DistribcellFilter), intent(inout) :: this diff --git a/src/tally_filter_header.F90 b/src/tally_filter_header.F90 index 43ef846e3e..8541cd3340 100644 --- a/src/tally_filter_header.F90 +++ b/src/tally_filter_header.F90 @@ -18,7 +18,6 @@ module tally_filter_header contains procedure(get_next_bin_), deferred :: get_next_bin procedure(to_statepoint_), deferred :: to_statepoint - procedure :: to_summary => filter_to_summary procedure(text_label_), deferred :: text_label procedure :: initialize => filter_initialize end type TallyFilter @@ -82,18 +81,6 @@ module tally_filter_header contains -!=============================================================================== -! TO_SUMMARY writes all the information needed to reconstruct the filter to the -! given filter_group. If this procedure is not overridden by the derived class, -! then it will call to_statepoint by default. - - subroutine filter_to_summary(this, filter_group) - class(TallyFilter), intent(in) :: this - integer(HID_T), intent(in) :: filter_group - - call this % to_statepoint(filter_group) - end subroutine filter_to_summary - !=============================================================================== ! INITIALIZE sets up any internal data, as necessary. If this procedure is not ! overriden by the derived class, then it will do nothing by default.