mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 13:45:36 -04:00
Removed OpenCG dependency for distribcell paths in Pandas DataFrames
This commit is contained in:
parent
ae083cf5d4
commit
7741b525bb
3 changed files with 208 additions and 484 deletions
File diff suppressed because one or more lines are too long
189
openmc/filter.py
189
openmc/filter.py
|
|
@ -1,4 +1,4 @@
|
|||
from collections import Iterable
|
||||
from collections import Iterable, OrderedDict
|
||||
import copy
|
||||
from numbers import Real, Integral
|
||||
import sys
|
||||
|
|
@ -516,7 +516,7 @@ class Filter(object):
|
|||
|
||||
return filter_bin
|
||||
|
||||
def get_pandas_dataframe(self, data_size, summary=None):
|
||||
def get_pandas_dataframe(self, data_size, distribcell_paths=False):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
|
|
@ -531,12 +531,10 @@ class Filter(object):
|
|||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or openmc.Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
distribcell_paths : bool
|
||||
Construct columns for distribcell tally filters. The geometric
|
||||
information in the Summary object is embedded into a Multi-index
|
||||
column with a geometric "path" to each distribcell instance.
|
||||
NOTE: This option requires the OpenCG Python package.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -554,7 +552,7 @@ class Filter(object):
|
|||
|
||||
1. a single column with the cell instance IDs (without summary info)
|
||||
2. separate columns for the cell IDs, universe IDs, and lattice IDs
|
||||
and x,y,z cell indices corresponding to each (with summary info).
|
||||
and x,y,z cell indices corresponding to each (distribcell paths).
|
||||
|
||||
For 'energy' and 'energyout' filters, the DataFrame includes one
|
||||
column for the lower energy bound and one column for the upper
|
||||
|
|
@ -566,8 +564,7 @@ class Filter(object):
|
|||
Raises
|
||||
------
|
||||
ImportError
|
||||
When Pandas is not installed, or summary info is requested but
|
||||
OpenCG is not installed.
|
||||
When Pandas is not installed
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -626,106 +623,108 @@ class Filter(object):
|
|||
elif self.type == 'distribcell':
|
||||
level_df = None
|
||||
|
||||
if isinstance(summary, Summary):
|
||||
# Attempt to import the OpenCG package
|
||||
try:
|
||||
import opencg
|
||||
except ImportError:
|
||||
msg = 'The OpenCG package must be installed ' \
|
||||
'to use a Summary for distribcell dataframes'
|
||||
raise ImportError(msg)
|
||||
# Create Pandas Multi-index columns for each level in CSG tree
|
||||
if distribcell_paths:
|
||||
|
||||
# Extract the OpenCG geometry from the Summary
|
||||
opencg_geometry = summary.opencg_geometry
|
||||
openmc_geometry = summary.openmc_geometry
|
||||
# FIXME: Make assumption that each path is the same length???
|
||||
# NOTE: Just state this caveat in the docstring
|
||||
|
||||
# Use OpenCG to compute the number of regions
|
||||
opencg_geometry.initialize_cell_offsets()
|
||||
num_regions = opencg_geometry.num_regions
|
||||
|
||||
# Initialize a dictionary mapping OpenMC distribcell
|
||||
# offsets to OpenCG LocalCoords linked lists
|
||||
offsets_to_coords = {}
|
||||
|
||||
for offset, path in enumerate(self.distribcell_paths):
|
||||
region = opencg_geometry.get_region_from_path(path)
|
||||
coords = opencg_geometry.find_region(region)
|
||||
offsets_to_coords[offset] = coords
|
||||
|
||||
# Each distribcell offset is a DataFrame bin
|
||||
# Unravel the paths into DataFrame columns
|
||||
num_offsets = len(offsets_to_coords)
|
||||
|
||||
# Initialize termination condition for while loop
|
||||
distribcell_paths = copy.deepcopy(self.distribcell_paths)
|
||||
num_offsets = len(distribcell_paths)
|
||||
levels_remain = True
|
||||
counter = 0
|
||||
level_counter = 0
|
||||
|
||||
# Iterate over each level in the CSG tree hierarchy
|
||||
# FIXME: Allocate NumPy arrays for each CSG level
|
||||
while levels_remain:
|
||||
levels_remain = False
|
||||
level_counter += 1
|
||||
level_key = 'level {}'.format(level_counter)
|
||||
first_path = distribcell_paths[0]
|
||||
level_dict = OrderedDict()
|
||||
|
||||
# Initialize dictionary to build Pandas Multi-index
|
||||
# column for this level in the CSG tree hierarchy
|
||||
level_dict = {}
|
||||
next_index = first_path.index('-')
|
||||
level = first_path[:next_index]
|
||||
first_path = first_path[next_index+2:]
|
||||
|
||||
# Initialize prefix Multi-index keys
|
||||
counter += 1
|
||||
level_key = 'level {0}'.format(counter)
|
||||
univ_key = (level_key, 'univ', 'id')
|
||||
cell_key = (level_key, 'cell', 'id')
|
||||
lat_id_key = (level_key, 'lat', 'id')
|
||||
lat_x_key = (level_key, 'lat', 'x')
|
||||
lat_y_key = (level_key, 'lat', 'y')
|
||||
lat_z_key = (level_key, 'lat', 'z')
|
||||
# This level is a lattice (e.g., ID(x,y,z))
|
||||
if '(' in level:
|
||||
level_type = 'lattice'
|
||||
|
||||
# Allocate NumPy arrays for each CSG level and
|
||||
# each Multi-index column in the DataFrame
|
||||
level_dict[univ_key] = np.empty(num_offsets)
|
||||
level_dict[cell_key] = np.empty(num_offsets)
|
||||
level_dict[lat_id_key] = np.empty(num_offsets)
|
||||
level_dict[lat_x_key] = np.empty(num_offsets)
|
||||
level_dict[lat_y_key] = np.empty(num_offsets)
|
||||
level_dict[lat_z_key] = np.empty(num_offsets)
|
||||
# Initialize prefix Multi-index keys
|
||||
lat_id_key = (level_key, 'lat', 'id')
|
||||
lat_x_key = (level_key, 'lat', 'x')
|
||||
lat_y_key = (level_key, 'lat', 'y')
|
||||
lat_z_key = (level_key, 'lat', 'z')
|
||||
|
||||
# Initialize Multi-index columns to NaN - this is
|
||||
# necessary since some distribcell instances may
|
||||
# have very different LocalCoords linked lists
|
||||
level_dict[univ_key][:] = np.NAN
|
||||
level_dict[cell_key][:] = np.NAN
|
||||
level_dict[lat_id_key][:] = np.NAN
|
||||
level_dict[lat_x_key][:] = np.NAN
|
||||
level_dict[lat_y_key][:] = np.NAN
|
||||
level_dict[lat_z_key][:] = np.NAN
|
||||
# Allocate NumPy arrays for each CSG level and
|
||||
# each Multi-index column in the DataFrame
|
||||
level_dict[lat_id_key] = np.empty(num_offsets)
|
||||
level_dict[lat_x_key] = np.empty(num_offsets)
|
||||
level_dict[lat_y_key] = np.empty(num_offsets)
|
||||
level_dict[lat_z_key] = np.empty(num_offsets)
|
||||
|
||||
# Iterate over all regions (distribcell instances)
|
||||
for offset in range(num_offsets):
|
||||
coords = offsets_to_coords[offset]
|
||||
# This level is a universe / cell (e.g., ID->ID)
|
||||
else:
|
||||
level_type = 'universe'
|
||||
|
||||
# If entire LocalCoords has been unraveled into
|
||||
# Multi-index columns already, continue
|
||||
if coords is None:
|
||||
continue
|
||||
|
||||
# Assign entry to Universe Multi-index column
|
||||
if coords._type == 'universe':
|
||||
level_dict[univ_key][offset] = coords._universe._id
|
||||
level_dict[cell_key][offset] = coords._cell._id
|
||||
|
||||
# Assign entry to Lattice Multi-index column
|
||||
# Pop off the cell ID from the path
|
||||
if '-' in first_path:
|
||||
next_index = first_path.index('-')
|
||||
level = first_path[:next_index]
|
||||
first_path = first_path[next_index+2:]
|
||||
else:
|
||||
# Reverse y index per lattice ordering in OpenCG
|
||||
level_dict[lat_id_key][offset] = coords._lattice._id
|
||||
level_dict[lat_x_key][offset] = coords._lat_x
|
||||
level_dict[lat_y_key][offset] = \
|
||||
coords._lattice.dimension[1] - coords._lat_y - 1
|
||||
level_dict[lat_z_key][offset] = coords._lat_z
|
||||
levels_remain = False
|
||||
|
||||
# Initialize prefix Multi-index keys
|
||||
univ_key = (level_key, 'univ', 'id')
|
||||
cell_key = (level_key, 'cell', 'id')
|
||||
|
||||
# Allocate NumPy arrays for each CSG level and
|
||||
# each Multi-index column in the DataFrame
|
||||
level_dict[univ_key] = np.empty(num_offsets)
|
||||
level_dict[cell_key] = np.empty(num_offsets)
|
||||
|
||||
# Populate Multi-index arrays with all distribcell paths
|
||||
for i, path in enumerate(distribcell_paths):
|
||||
|
||||
if level_type == 'lattice':
|
||||
# Extract lattice ID, indices from path
|
||||
next_index = path.index('-')
|
||||
lat_id_indices = path[:next_index]
|
||||
|
||||
# Trim lattice info from distribcell path
|
||||
distribcell_paths[i] = path[next_index+2:]
|
||||
|
||||
# Extract the lattice cell indices from the path
|
||||
i1 = lat_id_indices.index('(')
|
||||
i2 = lat_id_indices.index(')')
|
||||
i3 = lat_id_indices[i1+1:i2]
|
||||
|
||||
# Assign entry to Lattice Multi-index column
|
||||
level_dict[lat_id_key][i] = path[:i1]
|
||||
level_dict[lat_x_key][i] = int(i3.split(',')[0]) - 1
|
||||
level_dict[lat_y_key][i] = int(i3.split(',')[1]) - 1
|
||||
level_dict[lat_z_key][i] = int(i3.split(',')[2]) - 1
|
||||
|
||||
# Move to next node in LocalCoords linked list
|
||||
if coords._next is None:
|
||||
offsets_to_coords[offset] = None
|
||||
else:
|
||||
offsets_to_coords[offset] = coords._next
|
||||
levels_remain = True
|
||||
# Extract universe ID from path
|
||||
next_index = path.index('-')
|
||||
universe_id = int(path[:next_index])
|
||||
|
||||
# Trim universe info from distribcell path
|
||||
path = path[next_index+2:]
|
||||
|
||||
# Extract cell ID from path
|
||||
if '-' in path:
|
||||
next_index = path.index('-')
|
||||
cell_id = int(path[:next_index])
|
||||
distribcell_paths[i] = path[next_index+2:]
|
||||
else:
|
||||
cell_id = int(path)
|
||||
distribcell_paths[i] = ''
|
||||
|
||||
# Assign entry to Universe, Cell Multi-index columns
|
||||
level_dict[univ_key][i] = universe_id
|
||||
level_dict[cell_key][i] = cell_id
|
||||
|
||||
# Tile the Multi-index columns
|
||||
for level_key, level_bins in level_dict.items():
|
||||
|
|
|
|||
|
|
@ -1538,8 +1538,8 @@ class Tally(object):
|
|||
|
||||
return data
|
||||
|
||||
def get_pandas_dataframe(self, filters=True, nuclides=True,
|
||||
scores=True, summary=None, float_format='{:.2e}'):
|
||||
def get_pandas_dataframe(self, filters=True, nuclides=True, scores=True,
|
||||
distribcell_paths=False, float_format='{:.2e}'):
|
||||
"""Build a Pandas DataFrame for the Tally data.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the Tally data
|
||||
|
|
@ -1557,12 +1557,10 @@ class Tally(object):
|
|||
Include columns with nuclide bin information (default is True).
|
||||
scores : bool
|
||||
Include columns with score bin information (default is True).
|
||||
summary : None or openmc.Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
distribcell_paths : bool
|
||||
Construct columns for distribcell tally filters. The geometric
|
||||
information in the Summary object is embedded into a Multi-index
|
||||
column with a geometric "path" to each distribcell intance.
|
||||
NOTE: This option requires the OpenCG Python package.
|
||||
column with a geometric "path" to each distribcell instance.
|
||||
float_format : str
|
||||
All floats in the DataFrame will be formatted using the given
|
||||
format string before printing.
|
||||
|
|
@ -1588,13 +1586,15 @@ class Tally(object):
|
|||
msg = 'The Tally ID="{0}" has no data to return'.format(self.id)
|
||||
raise KeyError(msg)
|
||||
|
||||
'''
|
||||
# If using Summary, ensure StatePoint.link_with_summary(...) was called
|
||||
if summary and not self.with_summary:
|
||||
if distribcell_pathssummary and not self.with_summary:
|
||||
msg = 'The Tally ID="{0}" has not been linked with the Summary. ' \
|
||||
'Call the StatePoint.link_with_summary(...) method ' \
|
||||
'before using Tally.get_pandas_dataframe(...) with ' \
|
||||
'Summary info'.format(self.id)
|
||||
raise KeyError(msg)
|
||||
'''
|
||||
|
||||
# Initialize a pandas dataframe for the tally data
|
||||
import pandas as pd
|
||||
|
|
@ -1608,7 +1608,8 @@ class Tally(object):
|
|||
|
||||
# Append each Filter's DataFrame to the overall DataFrame
|
||||
for self_filter in self.filters:
|
||||
filter_df = self_filter.get_pandas_dataframe(data_size, summary)
|
||||
filter_df = self_filter.get_pandas_dataframe(
|
||||
data_size, distribcell_paths)
|
||||
df = pd.concat([df, filter_df], axis=1)
|
||||
|
||||
# Include DataFrame column for nuclides if user requested it
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue