From 55761214fa24e2e44897e5dd14914b0fc1155247 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 31 Jul 2018 02:00:47 -0400 Subject: [PATCH 01/58] First pass at implementing CMFD through CAPI, for ANL meeting; most of cmfd_input.F90 replicated --- examples/cmfd_testing/run_openmc_cmfd.py | 21 + openmc/cmfd.py | 486 +++++++++++++++++++++++ 2 files changed, 507 insertions(+) create mode 100644 examples/cmfd_testing/run_openmc_cmfd.py diff --git a/examples/cmfd_testing/run_openmc_cmfd.py b/examples/cmfd_testing/run_openmc_cmfd.py new file mode 100644 index 0000000000..d377c0cd14 --- /dev/null +++ b/examples/cmfd_testing/run_openmc_cmfd.py @@ -0,0 +1,21 @@ +import openmc +import numpy as np + +# Initialize CMFD Mesh +cmfd_mesh = openmc.CMFDMesh() +cmfd_mesh.lower_left = [-10, -1, -1] +cmfd_mesh.upper_right = [10, 1, 1] +cmfd_mesh.dimension = [10, 1, 1] +cmfd_mesh.albedo = [0., 0., 1., 1., 1., 1.] +cmfd_mesh.energy = [0.001, 0.01, 0.1] + +# Initialize CMFDRun object +cmfd_run = openmc.CMFDRun() + +# Set all runtime parameters (cmfd_mesh, tolerances, tally_resets, etc) + +# All error checking done under the hood when setter function called +cmfd_run.cmfd_mesh = cmfd_mesh + +# Run CMFD +cmfd_run.run() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 5444334b20..161c618c30 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -14,11 +14,17 @@ from collections.abc import Iterable from numbers import Real, Integral from xml.etree import ElementTree as ET import sys +import numpy as np +import openmc.capi +from openmc.exceptions import AllocationError from openmc.clean_xml import clean_xml_indentation from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) +# Maximum/minimum neutron energies, from src/api.F90 +ENERGY_MAX_NEUTRON = np.inf +ENERGY_MIN_NEUTRON = 0. class CMFDMesh(object): """A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD) @@ -119,12 +125,16 @@ class CMFDMesh(object): def dimension(self, dimension): check_type('CMFD mesh dimension', dimension, Iterable, Integral) check_length('CMFD mesh dimension', dimension, 2, 3) + for d in dimension: + check_greater_than('CMFD mesh dimension', d, 0) self._dimension = dimension @width.setter def width(self, width): check_type('CMFD mesh width', width, Iterable, Real) check_length('CMFD mesh width', width, 2, 3) + for w in width: + check_greater_than('CMFD mesh width', w, 0) self._width = width @energy.setter @@ -518,3 +528,479 @@ class CMFD(object): tree = ET.ElementTree(self._cmfd_file) tree.write("cmfd.xml", xml_declaration=True, encoding='utf-8', method="xml") + +class CMFDRun(object): + r"""Class to run openmc with CMFD acceleration through the C API. Running + openmc through this manner obviates the need of defining CMFD parameters + through a cmfd.xml file. Instead, all input parameters should be passed through + the CMFDRun initializer. + + Self notes: + Have it so that only required parameters part of __init__, all other parameters + need to be set through setter functions + Require CMFD parameters if this routine called? + cmfd_run needs to be false in settings.xml, implication is either openmc called through cmfd.xml or through CMFDRun + does order of how things are being called matter? + (read_input_xml in input_xml.F90) + (openmc_init_f in initialize.F90) + Tell user anything when turning configure cmfd called? How to print to buffer? + When do things get initialized in beginning of cmfd_header? ok in __init__, or should all parameters be set to None? + Define external variables like flux, xs, matrices in init or later? + Make sure all input variables are used / have coveragage somewhere in code + Store indices of meshes, tallies relevant to CMFD? + raise AllocationError or use openmc.checkvalue? + All error checking for CMFDMesh attributes done in CMFDMesh setter function instead of create_cmfd_tally + Some variables stored as numpy array, some as list, based on how user defines them, is that ok? + Setting tally macro bins correctly? + Lines unable to replicate in create_cmfd_tally + 281 - Set mesh type to rectangular + 291 - Set mesh n_dimension + 369 - Set volume fraction + 441 - Set reset variable for each tally + 453 - Set number of nuclide bins for each tally + + + Input parameters: Fill out, specifying which ones are required for CMFDRun + Required: cmfd_mesh, cmfd_mesh.dimension, cmfd_mesh.lower_left + + Ignore: + 1) MG mode - read_cmfd_xml + 2) configure_cmfd + call time_cmfd % reset() + call time_cmfdbuild % reset() + call time_cmfdsolve % reset() + 3) cmfd_adjoint_type input parameter, either set to 'physical' or 'math' - see cmfd_solver.F90 + + + + Attributes + ---------- + To add: cmfd_coremap: Flag for active core map + indices: Stores spatial and group dimensions as [nx, ny, nz, ng] + egrid: energy grid used for CMFD acceleration + albedo: Albedo for global boundary conditions, taken from CMFD mesh. Set to [1,1,1,1,1,1] if not specified by user + cmfd_coremap: Optional acceleration map to overlay on coarse mesh spatial grid, taken from CMFD mesh + n_cmfd_resets: Number of elements in tally_reset, list that stores batches where CMFD tallies should be reset + cmfd_atoli: Absolute GS tolerance, set by gauss_seidel_tolerance + cmfd_rtoli: Relative GS tolerance, set by gauss_seidel_tolerance + cmfd_mesh_id: Mesh id of openmc.capi.Mesh object that corresponds to the CMFD mesh + energy_filters: Boolean that stores whether energy filters should be created or not. + Set to true if user specifies energy grid in CMFDMesh, false otherwise + + TODO: Put descriptions for all methods in CMFDRun + + """ + + def __init__(self): + ''' + self._begin = None + self._dhat_reset = None + self._display = None + self._downscatter = None + self._feedback = None + self._gauss_seidel_tolerance = None + self._ktol = None + self._cmfd_mesh = None + self._norm = None + self._power_monitor = None + self._run_adjoint = None + self._shift = None + self._spectral = None + self._stol = None + self._tally_reset = None + self._write_matrices = None + ''' + + # Set CMFD default parameters based on cmfd_header.F90 + # Input parameters that user can define + self._cmfd_begin = 1 + self._dhat_reset = False + self._cmfd_display = 'balance' + self._cmfd_downscatter = False + self._cmfd_feedback = False + self._gauss_seidel_tolerance = [1.e-10, 1.e-5] + self._cmfd_ktol = 1.e-8 + self._cmfd_mesh = None + self._norm = 1. + self._cmfd_power_monitor = False + self._cmfd_run_adjoint = False + self._cmfd_shift = 1.e-6 + self._cmfd_spectral = 0. + self._cmfd_stol = 1.e-8 + self._cmfd_reset = None + self._cmfd_write_matrices = False + + # External variables used during runtime but users don't have control over + self._cmfd_coremap = False + self._indices = np.zeros(4, dtype=int) + self._egrid = None + self._albedo = None + self._coremap = None + self._n_cmfd_resets = 0 + self._cmfd_atoli = None + self._cmfd_rtoli = None + self._cmfd_mesh_id = None + self._energy_filters = None + + # All timing variables + + + + @property + def cmfd_begin(self): + return self._cmfd_begin + + @property + def dhat_reset(self): + return self._dhat_reset + + @property + def display(self): + return self._display + + @property + def cmfd_downscatter(self): + return self._cmfd_downscatter + + @property + def cmfd_feedback(self): + return self._cmfd_feedback + + @property + def gauss_seidel_tolerance(self): + return self._gauss_seidel_tolerance + + @property + def cmfd_ktol(self): + return self._cmfd_ktol + + @property + def cmfd_mesh(self): + return self._cmfd_mesh + + @property + def norm(self): + return self._norm + + @property + def cmfd_power_monitor(self): + return self._cmfd_power_monitor + + @property + def cmfd_run_adjoint(self): + return self._cmfd_run_adjoint + + @property + def cmfd_shift(self): + return self._cmfd_shift + + @property + def cmfd_spectral(self): + return self._cmfd_spectral + + @property + def cmfd_stol(self): + return self._cmfd_stol + + @property + def cmfd_reset(self): + return self._cmfd_reset + + @property + def cmfd_write_matrices(self): + return self._cmfd_write_matrices + + @cmfd_begin.setter + def cmfd_begin(self, cmfd_begin): + check_type('CMFD begin batch', cmfd_begin, Integral) + check_greater_than('CMFD begin batch', cmfd_begin, 0) + self._cmfd_begin = begin + + @dhat_reset.setter + def dhat_reset(self, dhat_reset): + check_type('CMFD Dhat reset', dhat_reset, bool) + self._dhat_reset = dhat_reset + + @display.setter + def display(self, display): + check_type('CMFD display', display, str) + check_value('CMFD display', display, + ['balance', 'dominance', 'entropy', 'source']) + self._display = display + + @cmfd_downscatter.setter + def cmfd_downscatter(self, cmfd_downscatter): + check_type('CMFD downscatter', cmfd_downscatter, bool) + self._cmfd_downscatter = cmfd_downscatter + + @cmfd_feedback.setter + def cmfd_feedback(self, cmfd_feedback): + check_type('CMFD feedback', cmfd_feedback, bool) + self._cmfd_feedback = cmfd_feedback + + @gauss_seidel_tolerance.setter + def gauss_seidel_tolerance(self, gauss_seidel_tolerance): + check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance, + Iterable, Real) + check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) + self._gauss_seidel_tolerance = gauss_seidel_tolerance + + @cmfd_ktol.setter + def cmfd_ktol(self, cmfd_ktol): + check_type('CMFD eigenvalue tolerance', cmfd_ktol, Real) + self._cmfd_ktol = cmfd_ktol + + @cmfd_mesh.setter + def cmfd_mesh(self, mesh): + check_type('CMFD mesh', mesh, CMFDMesh) + + # Check dimension defined + if mesh.dimension is None: + raise AllocationError('CMFD mesh requires spatial ' + 'dimensions to be specified') + + # Check lower left defined + if mesh.lower_left is None: + raise AllocationError('CMFD mesh requires lower left coordinates ' + 'to be specified') + + # Check that both upper right and width both not defined + if mesh.upper_right is not None and mesh.width is not None: + raise AllocationError('Both upper right coordinates and width ' + 'cannot be specified for CMFD mesh') + + # Check that at least one of width or upper right is defined + if mesh.upper_right is None and mesh.width is None: + raise AllocationError('CMFD mesh requires either upper right ' + 'coordinates or width to be specified') + + # Check width and lower length are same dimension and define upper_right + if mesh.width is not None: + check_length('CMFD mesh width', mesh.width, len(mesh.lower_left)) + mesh.upper_right = np.array(mesh.lower_left) + \ + np.array(mesh.width) * np.array(mesh.dimension) + + # Check upper_right and lower length are same dimension and define width + elif mesh.upper_right is not None: + check_length('CMFD mesh upper right', mesh.upper_right, \ + len(mesh.lower_left)) + # Check upper right coordinates are greater than lower left + if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): + raise AllocationError('CMFD mesh requires upper right ' + 'coordinates to be greater than lower ' + 'left coordinates') + mesh.width = np.true_divide( + (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ + np.array(mesh.dimension)) + self._cmfd_mesh = mesh + + @norm.setter + def norm(self, norm): + check_type('CMFD norm', norm, Real) + self._norm = norm + + @cmfd_power_monitor.setter + def cmfd_power_monitor(self, cmfd_power_monitor): + check_type('CMFD power monitor', cmfd_power_monitor, bool) + self._cmfd_power_monitor = cmfd_power_monitor + + @cmfd_run_adjoint.setter + def cmfd_run_adjoint(self, cmfd_run_adjoint): + check_type('CMFD run adjoint', cmfd_run_adjoint, bool) + self._cmfd_run_adjoint = cmfd_run_adjoint + + @cmfd_shift.setter + def cmfd_shift(self, cmfd_shift): + check_type('CMFD Wielandt shift', cmfd_shift, Real) + self._cmfd_shift = cmfd_shift + + @cmfd_spectral.setter + def cmfd_spectral(self, cmfd_spectral): + check_type('CMFD spectral radius', cmfd_spectral, Real) + self._cmfd_spectral = cmfd_spectral + + @cmfd_stol.setter + def cmfd_stol(self, cmfd_stol): + check_type('CMFD fission source tolerance', cmfd_stol, Real) + self._cmfd_stol = cmfd_stol + + @cmfd_reset.setter + def cmfd_reset(self, cmfd_reset): + check_type('tally reset batches', cmfd_reset, Iterable, Integral) + self._cmfd_reset = cmfd_reset + + @cmfd_write_matrices.setter + def cmfd_write_matrices(self, cmfd_write_matrices): + check_type('CMFD write matrices', cmfd_write_matrices, bool) + self._cmfd_write_matrices = cmfd_write_matrices + + def run(self): + openmc.capi.init() + self._configure_cmfd() + openmc.capi.simulation_init() + while True: + sys.exit() + # Look at how openmc.capi.run is defined + openmc.capi.next_batch(status) + #self._solve_cmfd() + + openmc.capi.simulation_finalize() + openmc.capi.finalize() + + def _configure_cmfd(self): + # TODO Tell user + # print('Configuring CMFD parameters for simulation') + + # Check if CMFD mesh is defined + if self._cmfd_mesh is None: + raise AllocationError('No CMFD mesh has been specified for ' + 'simulation') + + # Set spatial dimensions of CMFD object + # Iterate through each element of self._cmfd_mesh.dimension as could be + # length 2 or 3 + for i, n in enumerate(self._cmfd_mesh.dimension): + self._indices[i] = n + + # Set number of energy groups + if self._cmfd_mesh.energy is not None: + ng = len(self._cmfd_mesh.energy) + self._egrid = np.array(self._cmfd_mesh.energy) + self._indices[3] = ng - 1 + self._energy_filters = True + # TODO: MG mode check + else: + self._egrid = np.array([ENERGY_MIN_NEUTRON, ENERGY_MAX_NEUTRON]) + self._indices[3] = 1 + self._energy_filters = False + + # Set global albedo + if self._cmfd_mesh.albedo is not None: + self._albedo = np.array(self._cmfd_mesh.albedo) + else: + self._albedo = np.array([1.,1.,1.,1.,1.,1.]) + + # Get acceleration map + if self._cmfd_mesh.map is not None: + check_length('CMFD coremap', self._cmfd_mesh.map, + np.product(self._indices[0:3])) + self._coremap = np.array(self._cmfd_mesh.map).reshape(( \ + self._indices[0], self._indices[1], \ + self._indices[2])) + self._cmfd_coremap = True + + # Set number of batches where cmfd tallies should be reset + if self._cmfd_reset is not None: + self._n_cmfd_resets = len(self._cmfd_reset) + + # Set Gauss Sidel tolerances + self._cmfd_atoli = self._gauss_seidel_tolerance[0] + self._cmfd_rtoli = self._gauss_seidel_tolerance[1] + + # Create tally objects + self._create_cmfd_tally() + + def _create_cmfd_tally(self): + # Create Mesh object based on CMFDMesh, stored internally + cmfd_mesh = openmc.capi.Mesh() + # Store id of Mesh object + self._cmfd_mesh_id = cmfd_mesh.id + # TODO Set mesh type to rectangular + # TODO Set n_dimension of Mesh object + # TODO Set volume fraction + # Set dimension and parameters of Mesh object + cmfd_mesh.dimension = self._cmfd_mesh.dimension + cmfd_mesh.set_parameters(lower_left=self._cmfd_mesh.lower_left, + upper_right=self._cmfd_mesh.upper_right, + width=self._cmfd_mesh.width) + + # Create Mesh Filter object, stored internally + mesh_filter = openmc.capi.MeshFilter() + # Set mesh for Mesh Filter + mesh_filter.mesh = cmfd_mesh + + # Set up energy filters, if applicable + if self._energy_filters: + # Create Energy Filter object, stored internally + energy_filter = openmc.capi.EnergyFilter() + # Set bins for Energy Filter + energy_filter.bins = self._egrid + + # Create Energy Out Filter object, stored internally + energyout_filter = openmc.capi.EnergyoutFilter() + # Set bins for Energy Filter + energyout_filter.bins = self._egrid + + # Create Mesh Surface Filter object, stored internally + meshsurface_filter = openmc.capi.MeshSurfaceFilter() + # Set mesh for Mesh Surface Filter + meshsurface_filter.mesh = cmfd_mesh + + # Create Legendre Filter object, stored internally + legendre_filter = openmc.capi.LegendreFilter() + # Set order for Legendre Filter + legendre_filter.order = 1 + + # Create CMFD tallies, stored internally + n_tallies = 4 + for i in range(n_tallies): + tally = openmc.capi.Tally() + # TODO: set tally reset variable + # TODO: set nuclide bins + + # Set attributes of CMFD flux, total tally + if i == 0: + # TODO: Set name to "CMFD flux, total" + # TODO: Set tally estimator to ESTIMATOR_ANALOG + # TODO: Set tally type to TALLY_VOLUME + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, energy_filter] + else: + tally.filters = [mesh_filter] + # Set scores for tally + tally.scores = ['flux', 'total'] + + # Set attributes of CMFD neutron production tally + if i == 1: + # TODO: Set name to "CMFD neutron production" + # TODO: Set tally estimator to ESTIMATOR_ANALOG + # TODO: Set tally type to TALLY_VOLUME + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, energy_filter, energyout_filter] + else: + tally.filters = [mesh_filter] + # Set scores for tally + tally.scores = ['nu-scatter', 'nu-fission'] + + # Set attributes of CMFD surface current tally + if i == 2: + # TODO: Set name to "CMFD surface currents" + # TODO: Set tally estimator to ESTIMATOR_ANALOG + # TODO: Set tally type to TALLY_MESH_SURFACE + # Set filters for tally + if self._energy_filters: + tally.filters = [meshsurface_filter, energy_filter] + else: + tally.filters = [meshsurface_filter] + # Set scores for tally + tally.scores = ['current'] + + # Set attributes of CMFD P1 scatter tally + if i == 3: + # TODO: Set name to "CMFD P1 scatter" + # TODO: Set tally estimator to ESTIMATOR_ANALOG + # TODO: Set tally type to TALLY_VOLUME + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, legendre_filter, energy_filter] + else: + tally.filters = [mesh_filter, legendre_filter] + # Set scores for tally + tally.scores = ['scatter'] + + # Set all tallies to be active from beginning + tally.active = True + + # TODO: Stores id's of filters and tallies \ No newline at end of file From 651fb9c8f5db7f99f6f718a1f008713df11d6205 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 31 Jul 2018 23:38:10 -0400 Subject: [PATCH 02/58] Get rid of unnecessary comments, have CMFDRun.run() run openmc completely without actually running CMFD --- openmc/cmfd.py | 68 +++----------------------------------------------- 1 file changed, 4 insertions(+), 64 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 161c618c30..a6dcd9bfd1 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -538,26 +538,7 @@ class CMFDRun(object): Self notes: Have it so that only required parameters part of __init__, all other parameters need to be set through setter functions - Require CMFD parameters if this routine called? - cmfd_run needs to be false in settings.xml, implication is either openmc called through cmfd.xml or through CMFDRun - does order of how things are being called matter? - (read_input_xml in input_xml.F90) - (openmc_init_f in initialize.F90) - Tell user anything when turning configure cmfd called? How to print to buffer? - When do things get initialized in beginning of cmfd_header? ok in __init__, or should all parameters be set to None? - Define external variables like flux, xs, matrices in init or later? Make sure all input variables are used / have coveragage somewhere in code - Store indices of meshes, tallies relevant to CMFD? - raise AllocationError or use openmc.checkvalue? - All error checking for CMFDMesh attributes done in CMFDMesh setter function instead of create_cmfd_tally - Some variables stored as numpy array, some as list, based on how user defines them, is that ok? - Setting tally macro bins correctly? - Lines unable to replicate in create_cmfd_tally - 281 - Set mesh type to rectangular - 291 - Set mesh n_dimension - 369 - Set volume fraction - 441 - Set reset variable for each tally - 453 - Set number of nuclide bins for each tally Input parameters: Fill out, specifying which ones are required for CMFDRun @@ -592,24 +573,6 @@ class CMFDRun(object): """ def __init__(self): - ''' - self._begin = None - self._dhat_reset = None - self._display = None - self._downscatter = None - self._feedback = None - self._gauss_seidel_tolerance = None - self._ktol = None - self._cmfd_mesh = None - self._norm = None - self._power_monitor = None - self._run_adjoint = None - self._shift = None - self._spectral = None - self._stol = None - self._tally_reset = None - self._write_matrices = None - ''' # Set CMFD default parameters based on cmfd_header.F90 # Input parameters that user can define @@ -642,7 +605,7 @@ class CMFDRun(object): self._cmfd_mesh_id = None self._energy_filters = None - # All timing variables + # TODO All timing variables @@ -838,12 +801,7 @@ class CMFDRun(object): openmc.capi.init() self._configure_cmfd() openmc.capi.simulation_init() - while True: - sys.exit() - # Look at how openmc.capi.run is defined - openmc.capi.next_batch(status) - #self._solve_cmfd() - + openmc.capi.run() openmc.capi.simulation_finalize() openmc.capi.finalize() @@ -905,9 +863,6 @@ class CMFDRun(object): cmfd_mesh = openmc.capi.Mesh() # Store id of Mesh object self._cmfd_mesh_id = cmfd_mesh.id - # TODO Set mesh type to rectangular - # TODO Set n_dimension of Mesh object - # TODO Set volume fraction # Set dimension and parameters of Mesh object cmfd_mesh.dimension = self._cmfd_mesh.dimension cmfd_mesh.set_parameters(lower_left=self._cmfd_mesh.lower_left, @@ -945,14 +900,10 @@ class CMFDRun(object): n_tallies = 4 for i in range(n_tallies): tally = openmc.capi.Tally() - # TODO: set tally reset variable - # TODO: set nuclide bins + # Set nuclide bins # Set attributes of CMFD flux, total tally if i == 0: - # TODO: Set name to "CMFD flux, total" - # TODO: Set tally estimator to ESTIMATOR_ANALOG - # TODO: Set tally type to TALLY_VOLUME # Set filters for tally if self._energy_filters: tally.filters = [mesh_filter, energy_filter] @@ -963,9 +914,6 @@ class CMFDRun(object): # Set attributes of CMFD neutron production tally if i == 1: - # TODO: Set name to "CMFD neutron production" - # TODO: Set tally estimator to ESTIMATOR_ANALOG - # TODO: Set tally type to TALLY_VOLUME # Set filters for tally if self._energy_filters: tally.filters = [mesh_filter, energy_filter, energyout_filter] @@ -976,9 +924,6 @@ class CMFDRun(object): # Set attributes of CMFD surface current tally if i == 2: - # TODO: Set name to "CMFD surface currents" - # TODO: Set tally estimator to ESTIMATOR_ANALOG - # TODO: Set tally type to TALLY_MESH_SURFACE # Set filters for tally if self._energy_filters: tally.filters = [meshsurface_filter, energy_filter] @@ -989,9 +934,6 @@ class CMFDRun(object): # Set attributes of CMFD P1 scatter tally if i == 3: - # TODO: Set name to "CMFD P1 scatter" - # TODO: Set tally estimator to ESTIMATOR_ANALOG - # TODO: Set tally type to TALLY_VOLUME # Set filters for tally if self._energy_filters: tally.filters = [mesh_filter, legendre_filter, energy_filter] @@ -1001,6 +943,4 @@ class CMFDRun(object): tally.scores = ['scatter'] # Set all tallies to be active from beginning - tally.active = True - - # TODO: Stores id's of filters and tallies \ No newline at end of file + tally.active = True \ No newline at end of file From f43131f325a1c69ad914e7361daa9cdab4285912 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 31 Jul 2018 23:40:01 -0400 Subject: [PATCH 03/58] Add xml files for CMFD example, taken from test suite --- examples/cmfd_testing/cmfd_ref.xml | 15 ++++++++++ examples/cmfd_testing/geometry.xml | 43 +++++++++++++++++++++++++++++ examples/cmfd_testing/materials.xml | 12 ++++++++ examples/cmfd_testing/settings.xml | 28 +++++++++++++++++++ examples/cmfd_testing/tallies.xml | 21 ++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 examples/cmfd_testing/cmfd_ref.xml create mode 100644 examples/cmfd_testing/geometry.xml create mode 100644 examples/cmfd_testing/materials.xml create mode 100644 examples/cmfd_testing/settings.xml create mode 100644 examples/cmfd_testing/tallies.xml diff --git a/examples/cmfd_testing/cmfd_ref.xml b/examples/cmfd_testing/cmfd_ref.xml new file mode 100644 index 0000000000..ae50243c03 --- /dev/null +++ b/examples/cmfd_testing/cmfd_ref.xml @@ -0,0 +1,15 @@ + + + + + -10 -1 -1 + 10 1 1 + 10 1 1 + 0.0 0.0 1.0 1.0 1.0 1.0 + + + 5 + dominance + true + 1.e-15 1.e-20 + diff --git a/examples/cmfd_testing/geometry.xml b/examples/cmfd_testing/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/examples/cmfd_testing/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/examples/cmfd_testing/materials.xml b/examples/cmfd_testing/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/examples/cmfd_testing/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/examples/cmfd_testing/settings.xml b/examples/cmfd_testing/settings.xml new file mode 100644 index 0000000000..0646677bca --- /dev/null +++ b/examples/cmfd_testing/settings.xml @@ -0,0 +1,28 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + + + diff --git a/examples/cmfd_testing/tallies.xml b/examples/cmfd_testing/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/examples/cmfd_testing/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + From cfbf0af96c744104c78c660c594ade505ef9d117 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 8 Aug 2018 23:15:27 -0400 Subject: [PATCH 04/58] Incorporate comments from ANL meeting, remove reset property from tally_header.F90 --- openmc/cmfd.py | 135 +++++++++++++++++++++++++---------- src/cmfd_input.F90 | 5 -- src/tallies/tally_header.F90 | 3 - 3 files changed, 98 insertions(+), 45 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index a6dcd9bfd1..b6a7c4dafc 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -16,7 +16,6 @@ from xml.etree import ElementTree as ET import sys import numpy as np import openmc.capi -from openmc.exceptions import AllocationError from openmc.clean_xml import clean_xml_indentation from openmc.checkvalue import (check_type, check_length, check_value, @@ -376,6 +375,45 @@ class CMFD(object): @cmfd_mesh.setter def cmfd_mesh(self, mesh): check_type('CMFD mesh', mesh, CMFDMesh) + + # Check dimension defined + if mesh.dimension is None: + raise ValueError('CMFD mesh requires spatial ' + 'dimensions to be specified') + + # Check lower left defined + if mesh.lower_left is None: + raise ValueError('CMFD mesh requires lower left coordinates ' + 'to be specified') + + # Check that both upper right and width both not defined + if mesh.upper_right is not None and mesh.width is not None: + raise ValueError('Both upper right coordinates and width ' + 'cannot be specified for CMFD mesh') + + # Check that at least one of width or upper right is defined + if mesh.upper_right is None and mesh.width is None: + raise ValueError('CMFD mesh requires either upper right ' + 'coordinates or width to be specified') + + # Check width and lower length are same dimension and define upper_right + if mesh.width is not None: + check_length('CMFD mesh width', mesh.width, len(mesh.lower_left)) + mesh.upper_right = np.array(mesh.lower_left) + \ + np.array(mesh.width) * np.array(mesh.dimension) + + # Check upper_right and lower length are same dimension and define width + elif mesh.upper_right is not None: + check_length('CMFD mesh upper right', mesh.upper_right, \ + len(mesh.lower_left)) + # Check upper right coordinates are greater than lower left + if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): + raise ValueError('CMFD mesh requires upper right ' + 'coordinates to be greater than lower ' + 'left coordinates') + mesh.width = np.true_divide( + (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ + np.array(mesh.dimension)) self._cmfd_mesh = mesh @norm.setter @@ -418,86 +456,87 @@ class CMFD(object): check_type('CMFD write matrices', write_matrices, bool) self._write_matrices = write_matrices + # TODO: Remove def _create_begin_subelement(self): if self._begin is not None: element = ET.SubElement(self._cmfd_file, "begin") element.text = str(self._begin) - + # TODO: Remove def _create_dhat_reset_subelement(self): if self._dhat_reset is not None: element = ET.SubElement(self._cmfd_file, "dhat_reset") element.text = str(self._dhat_reset).lower() - + # TODO: Remove def _create_display_subelement(self): if self._display is not None: element = ET.SubElement(self._cmfd_file, "display") element.text = str(self._display) - + # TODO: Remove def _create_downscatter_subelement(self): if self._downscatter is not None: element = ET.SubElement(self._cmfd_file, "downscatter") element.text = str(self._downscatter).lower() - + # TODO: Remove def _create_feedback_subelement(self): if self._feedback is not None: element = ET.SubElement(self._cmfd_file, "feeback") element.text = str(self._feedback).lower() - + # TODO: Remove def _create_gauss_seidel_tolerance_subelement(self): if self._gauss_seidel_tolerance is not None: element = ET.SubElement(self._cmfd_file, "gauss_seidel_tolerance") element.text = ' '.join(map(str, self._gauss_seidel_tolerance)) - + # TODO: Remove def _create_ktol_subelement(self): if self._ktol is not None: element = ET.SubElement(self._ktol, "ktol") element.text = str(self._ktol) - + # TODO: Remove def _create_mesh_subelement(self): if self._cmfd_mesh is not None: xml_element = self._cmfd_mesh._get_xml_element() self._cmfd_file.append(xml_element) - + # TODO: Remove def _create_norm_subelement(self): if self._norm is not None: element = ET.SubElement(self._cmfd_file, "norm") element.text = str(self._norm) - + # TODO: Remove def _create_power_monitor_subelement(self): if self._power_monitor is not None: element = ET.SubElement(self._cmfd_file, "power_monitor") element.text = str(self._power_monitor).lower() - + # TODO: Remove def _create_run_adjoint_subelement(self): if self._run_adjoint is not None: element = ET.SubElement(self._cmfd_file, "run_adjoint") element.text = str(self._run_adjoint).lower() - + # TODO: Remove def _create_shift_subelement(self): if self._shift is not None: element = ET.SubElement(self._shift, "shift") element.text = str(self._shift) - + # TODO: Remove def _create_spectral_subelement(self): if self._spectral is not None: element = ET.SubElement(self._spectral, "spectral") element.text = str(self._spectral) - + # TODO: Remove def _create_stol_subelement(self): if self._stol is not None: element = ET.SubElement(self._stol, "stol") element.text = str(self._stol) - + # TODO: Remove def _create_tally_reset_subelement(self): if self._tally_reset is not None: element = ET.SubElement(self._tally_reset, "tally_reset") element.text = ' '.join(map(str, self._tally_reset)) - + # TODO: Remove def _create_write_matrices_subelement(self): if self._write_matrices is not None: element = ET.SubElement(self._cmfd_file, "write_matrices") element.text = str(self._write_matrices).lower() - + # TODO: Remove def export_to_xml(self): """Create a cmfd.xml file using the class data that can be used for an OpenMC simulation. @@ -552,6 +591,18 @@ class CMFDRun(object): call time_cmfdsolve % reset() 3) cmfd_adjoint_type input parameter, either set to 'physical' or 'math' - see cmfd_solver.F90 + Notes: + -cmfd_run needs to be false if run from this class, ow call using cmfd.xml and set cmfd_run to true in settings.xml + -things run out of order, does not change results but will fix this in future PR when more funcs avail through Capillarity + -combine CMFD and CMFDRun class? + -Did not replicate: m % type = MESH_REGULAR (all meshes are regular for now) + -Did not replicate: m % n_dimension = n (Keep track through CMFD mesh dimension) + -Did not replicate: m % volume_frac = ONE/real(product(m % dimension),8) (can calculate if needed) + -Did not replicate: Set reset property (Never used in simulation) + -Import methods from CMFD into CMFDRun for error checking - will remove CMFD class altogether, left for now if xml files needed + + + Attributes @@ -565,10 +616,14 @@ class CMFDRun(object): cmfd_atoli: Absolute GS tolerance, set by gauss_seidel_tolerance cmfd_rtoli: Relative GS tolerance, set by gauss_seidel_tolerance cmfd_mesh_id: Mesh id of openmc.capi.Mesh object that corresponds to the CMFD mesh + cmfd_filter_ids: list of ids corresponding to CMFD filters (details:) + cmfd_tally_ids: list of ids corresponding to CMFD tallies (details:) energy_filters: Boolean that stores whether energy filters should be created or not. Set to true if user specifies energy grid in CMFDMesh, false otherwise TODO: Put descriptions for all methods in CMFDRun + TODO All timing variables + TODO: add tally type and tally estimator to CAPI """ @@ -603,12 +658,10 @@ class CMFDRun(object): self._cmfd_atoli = None self._cmfd_rtoli = None self._cmfd_mesh_id = None + self._cmfd_filter_ids = None + self._cmfd_tally_ids = None self._energy_filters = None - # TODO All timing variables - - - @property def cmfd_begin(self): return self._cmfd_begin @@ -719,23 +772,23 @@ class CMFDRun(object): # Check dimension defined if mesh.dimension is None: - raise AllocationError('CMFD mesh requires spatial ' - 'dimensions to be specified') + raise ValueError('CMFD mesh requires spatial ' + 'dimensions to be specified') # Check lower left defined if mesh.lower_left is None: - raise AllocationError('CMFD mesh requires lower left coordinates ' - 'to be specified') + raise ValueError('CMFD mesh requires lower left coordinates ' + 'to be specified') # Check that both upper right and width both not defined if mesh.upper_right is not None and mesh.width is not None: - raise AllocationError('Both upper right coordinates and width ' - 'cannot be specified for CMFD mesh') + raise ValueError('Both upper right coordinates and width ' + 'cannot be specified for CMFD mesh') # Check that at least one of width or upper right is defined if mesh.upper_right is None and mesh.width is None: - raise AllocationError('CMFD mesh requires either upper right ' - 'coordinates or width to be specified') + raise ValueError('CMFD mesh requires either upper right ' + 'coordinates or width to be specified') # Check width and lower length are same dimension and define upper_right if mesh.width is not None: @@ -749,9 +802,9 @@ class CMFDRun(object): len(mesh.lower_left)) # Check upper right coordinates are greater than lower left if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): - raise AllocationError('CMFD mesh requires upper right ' - 'coordinates to be greater than lower ' - 'left coordinates') + raise ValueError('CMFD mesh requires upper right ' + 'coordinates to be greater than lower ' + 'left coordinates') mesh.width = np.true_divide( (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ np.array(mesh.dimension)) @@ -806,17 +859,16 @@ class CMFDRun(object): openmc.capi.finalize() def _configure_cmfd(self): - # TODO Tell user - # print('Configuring CMFD parameters for simulation') + print('Configuring CMFD parameters for simulation') # Check if CMFD mesh is defined if self._cmfd_mesh is None: - raise AllocationError('No CMFD mesh has been specified for ' + raise ValueError('No CMFD mesh has been specified for ' 'simulation') # Set spatial dimensions of CMFD object - # Iterate through each element of self._cmfd_mesh.dimension as could be - # length 2 or 3 + # Iterate through each element of self._cmfd_mesh.dimension + # as could be length 2 or 3 for i, n in enumerate(self._cmfd_mesh.dimension): self._indices[i] = n @@ -869,10 +921,12 @@ class CMFDRun(object): upper_right=self._cmfd_mesh.upper_right, width=self._cmfd_mesh.width) + self._cmfd_filter_ids = [] # Create Mesh Filter object, stored internally mesh_filter = openmc.capi.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh + self._cmfd_filter_ids.append(mesh_filter.id) # Set up energy filters, if applicable if self._energy_filters: @@ -880,27 +934,34 @@ class CMFDRun(object): energy_filter = openmc.capi.EnergyFilter() # Set bins for Energy Filter energy_filter.bins = self._egrid + self._cmfd_filter_ids.append(energy_filter.id) # Create Energy Out Filter object, stored internally energyout_filter = openmc.capi.EnergyoutFilter() # Set bins for Energy Filter energyout_filter.bins = self._egrid + self._cmfd_filter_ids.append(energyout_filter.id) # Create Mesh Surface Filter object, stored internally meshsurface_filter = openmc.capi.MeshSurfaceFilter() # Set mesh for Mesh Surface Filter meshsurface_filter.mesh = cmfd_mesh + self._cmfd_filter_ids.append(meshsurface_filter.id) # Create Legendre Filter object, stored internally legendre_filter = openmc.capi.LegendreFilter() # Set order for Legendre Filter legendre_filter.order = 1 + self._cmfd_filter_ids.append(legendre_filter.id) # Create CMFD tallies, stored internally n_tallies = 4 + self._cmfd_tally_ids = [] for i in range(n_tallies): tally = openmc.capi.Tally() # Set nuclide bins + tally.nuclides = ['total'] + self._cmfd_tally_ids.append(tally.id) # Set attributes of CMFD flux, total tally if i == 0: diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 1e4b8f3742..e559debf70 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -438,11 +438,6 @@ contains ! Point t to tally variable associate (t => cmfd_tallies(i) % obj) - ! Set reset property - if (check_for_node(root, "reset")) then - call get_node_value(root, "reset", t % reset) - end if - ! Set the incoming energy mesh filter index in the tally find_filter ! array n_filter = 1 diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index ee44685924..a51b8921e7 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -85,9 +85,6 @@ module tally_header integer :: total_score_bins real(C_DOUBLE), allocatable :: results(:,:,:) - ! reset property - allows a tally to be reset after every batch - logical :: reset = .false. - ! Number of realizations of tally random variables integer :: n_realizations = 0 From 6e56f892456052e1a0066cab091f257f2de852e9 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 9 Aug 2018 03:37:47 -0400 Subject: [PATCH 05/58] Create getter and setter functions for tally estimator and tally type through C API --- include/openmc.h | 4 ++ openmc/capi/tally.py | 47 +++++++++++++++++++ src/tallies/tally_header.F90 | 91 +++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/include/openmc.h b/include/openmc.h index 1f919d7c61..991efb2c71 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -93,19 +93,23 @@ extern "C" { int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); int openmc_statepoint_write(const char filename[]); int openmc_tally_get_active(int32_t index, bool* active); + int openmc_tally_get_estimator(int32_t index, int32_t* estimator); int openmc_tally_get_id(int32_t index, int32_t* id); int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n); int openmc_tally_get_n_realizations(int32_t index, int32_t* n); int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n); int openmc_tally_get_scores(int32_t index, int** scores, int* n); + int openmc_tally_get_type(int32_t index, int32_t* type); int openmc_tally_reset(int32_t index); int openmc_tally_results(int32_t index, double** ptr, int shape_[3]); int openmc_tally_set_active(int32_t index, bool active); + int openmc_tally_set_estimator(int32_t index, const char* estimator); int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices); int openmc_tally_set_id(int32_t index, int32_t id); int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); + int openmc_tally_update_type(int32_t index, const char* type); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index d4d70af718..cc23aa3f97 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -29,6 +29,9 @@ _dll.openmc_global_tallies.errcheck = _error_handler _dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)] _dll.openmc_tally_get_active.restype = c_int _dll.openmc_tally_get_active.errcheck = _error_handler +_dll.openmc_tally_get_estimator.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_get_estimator.restype = c_int +_dll.openmc_tally_get_estimator.errcheck = _error_handler _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 @@ -47,6 +50,9 @@ _dll.openmc_tally_get_scores.argtypes = [ c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] _dll.openmc_tally_get_scores.restype = c_int _dll.openmc_tally_get_scores.errcheck = _error_handler +_dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_tally_get_type.restype = c_int +_dll.openmc_tally_get_type.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 @@ -57,6 +63,9 @@ _dll.openmc_tally_set_active.errcheck = _error_handler _dll.openmc_tally_set_filters.argtypes = [c_int32, c_int, POINTER(c_int32)] _dll.openmc_tally_set_filters.restype = c_int _dll.openmc_tally_set_filters.errcheck = _error_handler +_dll.openmc_tally_set_estimator.argtypes = [c_int32, c_char_p] +_dll.openmc_tally_set_estimator.restype = c_int +_dll.openmc_tally_set_estimator.errcheck = _error_handler _dll.openmc_tally_set_id.argtypes = [c_int32, c_int32] _dll.openmc_tally_set_id.restype = c_int _dll.openmc_tally_set_id.errcheck = _error_handler @@ -69,6 +78,9 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler _dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p] _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler +_dll.openmc_tally_update_type.argtypes = [c_int32, c_char_p] +_dll.openmc_tally_update_type.restype = c_int +_dll.openmc_tally_update_type.errcheck = _error_handler _SCORES = { @@ -78,6 +90,13 @@ _SCORES = { -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', -15: 'fission-q-recoverable', -16: 'decay-rate' } +_ESTIMATORS = { + 1: 'analog', 2: 'tracklength', 3: 'collision' +} +_TALLY_TYPES = { + 1: 'volume', 2: 'mesh-surface', 3: 'surface' +} + def global_tallies(): @@ -140,6 +159,8 @@ class Tally(_FortranObjectWithID): ---------- id : int ID of the tally + estimator: str + Estimator type of tally (analog, tracklength, collision) filters : list List of tally filters mean : numpy.ndarray @@ -152,6 +173,8 @@ class Tally(_FortranObjectWithID): Array of tally results std_dev : numpy.ndarray An array containing the sample standard deviation for each bin + type : str + Type of tally (volume, mesh_surface, surface) """ __instances = WeakValueDictionary() @@ -190,6 +213,30 @@ class Tally(_FortranObjectWithID): _dll.openmc_tally_get_active(self._index, active) return active.value + @property + def type(self): + type = c_int32() + _dll.openmc_tally_get_type(self._index, type) + return _TALLY_TYPES[type.value] + + @type.setter + def type(self, type): + _dll.openmc_tally_update_type(self._index, type.encode()) + + @property + def estimator(self): + estimator = c_int32() + try: + _dll.openmc_tally_get_estimator(self._index, estimator) + except AllocationError: + return "" + else: + return _ESTIMATORS[estimator.value] + + @estimator.setter + def estimator(self, estimator): + _dll.openmc_tally_set_estimator(self._index, estimator.encode()) + @active.setter def active(self, active): _dll.openmc_tally_set_active(self._index, active) diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 6442d58009..6b20fe1255 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -24,11 +24,13 @@ module tally_header public :: openmc_get_tally_index public :: openmc_global_tallies public :: openmc_tally_get_active + public :: openmc_tally_get_estimator public :: openmc_tally_get_id public :: openmc_tally_get_filters public :: openmc_tally_get_n_realizations public :: openmc_tally_get_nuclides public :: openmc_tally_get_scores + public :: openmc_tally_get_type public :: openmc_tally_reset public :: openmc_tally_results public :: openmc_tally_set_active @@ -501,6 +503,20 @@ contains end if end function openmc_tally_get_active + function openmc_tally_get_estimator(index, estimator) result(err) bind(C) + ! Return the type of estimator of a tally + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: estimator + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + estimator = tallies(index) % obj % estimator + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg('Index in tallies array is out of bounds.') + end if + end function openmc_tally_get_estimator function openmc_tally_get_id(index, id) result(err) bind(C) ! Return the ID of a tally @@ -608,6 +624,21 @@ contains end if end function openmc_tally_get_scores + function openmc_tally_get_type(index, type) result(err) bind(C) + ! Return the type of a tally + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: type + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(tallies)) then + type = tallies(index) % obj % type + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg('Index in tallies array is out of bounds.') + end if + end function openmc_tally_get_type + function openmc_tally_reset(index) result(err) bind(C) ! Reset tally results and number of realizations @@ -652,6 +683,35 @@ contains end if end function openmc_tally_results + function openmc_tally_set_estimator(index, estimator) result(err) bind(C) + ! Set the type of estimator a tally + integer(C_INT32_T), value, intent(in) :: index + character(kind=C_CHAR), intent(in) :: estimator(*) + integer(C_INT) :: err + + character(:), allocatable :: estimator_ + + ! Convert C string to Fortran string + estimator_ = to_f_string(estimator) + + err = 0 + if (index >= 1 .and. index <= size(tallies)) then + select case (estimator_) + case ('analog') + tallies(index) % obj % estimator = ESTIMATOR_ANALOG + case ('tracklength') + tallies(index) % obj % estimator = ESTIMATOR_TRACKLENGTH + case ('collision') + tallies(index) % obj % estimator = ESTIMATOR_COLLISION + case default + err = E_UNASSIGNED + call set_errmsg("Unknown tally estimator: " // trim(estimator_)) + end select + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in tally array is out of bounds.") + end if + end function openmc_tally_set_estimator function openmc_tally_set_filters(index, n, filter_indices) result(err) bind(C) ! Set the list of filters for a tally @@ -765,7 +825,6 @@ contains end if end function openmc_tally_set_nuclides - function openmc_tally_set_scores(index, n, scores) result(err) bind(C) ! Sets the scores in the tally integer(C_INT32_T), value :: index @@ -939,4 +998,34 @@ contains end if end function openmc_tally_set_scores + function openmc_tally_update_type(index, type) result(err) bind(C) + ! Update the type of a tally that is already allocated + integer(C_INT32_T), value, intent(in) :: index + character(kind=C_CHAR), intent(in) :: type(*) + integer(C_INT) :: err + + character(:), allocatable :: type_ + + ! Convert C string to Fortran string + type_ = to_f_string(type) + + err = 0 + if (index >= 1 .and. index <= size(tallies)) then + select case (type_) + case ('volume') + tallies(index) % obj % type = TALLY_VOLUME + case ('mesh-surface') + tallies(index) % obj % type = TALLY_MESH_SURFACE + case ('surface') + tallies(index) % obj % type = TALLY_SURFACE + case default + err = E_UNASSIGNED + call set_errmsg("Unknown tally type: " // trim(type_)) + end select + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in tally array is out of bounds.") + end if + end function openmc_tally_update_type + end module tally_header From 977dec7a5aa6213bcee9bfe635adbac8df92bd84 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 14 Aug 2018 23:33:59 -0400 Subject: [PATCH 06/58] Remove cmfd.py notes to separate file --- openmc/cmfd.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index b6a7c4dafc..cf532e28f9 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -574,36 +574,6 @@ class CMFDRun(object): through a cmfd.xml file. Instead, all input parameters should be passed through the CMFDRun initializer. - Self notes: - Have it so that only required parameters part of __init__, all other parameters - need to be set through setter functions - Make sure all input variables are used / have coveragage somewhere in code - - - Input parameters: Fill out, specifying which ones are required for CMFDRun - Required: cmfd_mesh, cmfd_mesh.dimension, cmfd_mesh.lower_left - - Ignore: - 1) MG mode - read_cmfd_xml - 2) configure_cmfd - call time_cmfd % reset() - call time_cmfdbuild % reset() - call time_cmfdsolve % reset() - 3) cmfd_adjoint_type input parameter, either set to 'physical' or 'math' - see cmfd_solver.F90 - - Notes: - -cmfd_run needs to be false if run from this class, ow call using cmfd.xml and set cmfd_run to true in settings.xml - -things run out of order, does not change results but will fix this in future PR when more funcs avail through Capillarity - -combine CMFD and CMFDRun class? - -Did not replicate: m % type = MESH_REGULAR (all meshes are regular for now) - -Did not replicate: m % n_dimension = n (Keep track through CMFD mesh dimension) - -Did not replicate: m % volume_frac = ONE/real(product(m % dimension),8) (can calculate if needed) - -Did not replicate: Set reset property (Never used in simulation) - -Import methods from CMFD into CMFDRun for error checking - will remove CMFD class altogether, left for now if xml files needed - - - - Attributes ---------- From 6080c3a9114e35b6a7a772d8bb57b34e5a42411e Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 15 Aug 2018 12:23:48 -0400 Subject: [PATCH 07/58] Create skeleton for cmfd execute through CAPI --- openmc/capi/core.py | 45 +++++++++++++ openmc/cmfd.py | 22 ++++++- src/simulation.F90 | 150 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 211 insertions(+), 6 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index a1f908c1c5..a47bbf287a 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -39,6 +39,15 @@ _dll.openmc_get_keff.errcheck = _error_handler _dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int _dll.openmc_next_batch.errcheck = _error_handler +_dll.openmc_next_batch_before_cmfd_init.argtypes = [] +_dll.openmc_next_batch_before_cmfd_init.restype = c_int +_dll.openmc_next_batch_before_cmfd_init.errcheck = _error_handler +_dll.openmc_next_batch_between_cmfd_init_execute.argtypes = [POINTER(c_int)] +_dll.openmc_next_batch_between_cmfd_init_execute.restype = c_int +_dll.openmc_next_batch_between_cmfd_init_execute.errcheck = _error_handler +_dll.openmc_next_batch_after_cmfd_execute.argtypes = [POINTER(c_int)] +_dll.openmc_next_batch_after_cmfd_execute.restype = c_int +_dll.openmc_next_batch_after_cmfd_execute.errcheck = _error_handler _dll.openmc_plot_geometry.restype = c_int _dll.openmc_plot_geometry.restype = _error_handler _dll.openmc_run.restype = c_int @@ -225,6 +234,42 @@ def next_batch(): return status.value +def next_batch_before_cmfd_init(): + """Run everything in batch that occurs before CMFD initialized.""" + _dll.openmc_next_batch_before_cmfd_init() + + +def next_batch_between_cmfd_init_execute(): + """Run everything in batch that occurs between CMFD + initialization and execution. + + Returns + ------- + int + Status after running block (0=batch is part of restart batch so don't + run CMFD, 3=normal execution, continue with CMFD solver) + + """ + status = c_int() + _dll.openmc_next_batch_between_cmfd_init_execute(status) + return status.value + + +def next_batch_after_cmfd_execute(): + """Run next batch. + + Returns + ------- + int + Status after running a batch (0=normal, 1=reached maximum number of + batches, 2=tally triggers reached) + + """ + status = c_int() + _dll.openmc_next_batch_after_cmfd_execute(status) + return status.value + + def plot_geometry(): """Plot geometry""" _dll.openmc_plot_geometry() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index cf532e28f9..84cb121e3e 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -824,7 +824,21 @@ class CMFDRun(object): openmc.capi.init() self._configure_cmfd() openmc.capi.simulation_init() - openmc.capi.run() + + while(True): + openmc.capi.next_batch_before_cmfd_init() + self._cmfd_init() + # Status determines whether batch should continue with a + # CMFD update or skip it entirely if it is a restart run + status = openmc.capi.next_batch_between_cmfd_init_execute() + if status != 0: + self._cmfd_execute() + # Status now determines whether another batch should be run + # or simulation should be terminated. + status = openmc.capi.next_batch_after_cmfd_execute() + if status != 0: + break + openmc.capi.simulation_finalize() openmc.capi.finalize() @@ -880,6 +894,12 @@ class CMFDRun(object): # Create tally objects self._create_cmfd_tally() + def _cmfd_init(self): + pass + + def _cmfd_execute(self): + pass + def _create_cmfd_tally(self): # Create Mesh object based on CMFDMesh, stored internally cmfd_mesh = openmc.capi.Mesh() diff --git a/src/simulation.F90 b/src/simulation.F90 index c8c9dfcf8e..fa1036fc5f 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -47,10 +47,14 @@ module simulation public :: openmc_next_batch public :: openmc_simulation_init public :: openmc_simulation_finalize + public :: openmc_next_batch_before_cmfd_init + public :: openmc_next_batch_between_cmfd_init_execute + public :: openmc_next_batch_after_cmfd_execute integer(C_INT), parameter :: STATUS_EXIT_NORMAL = 0 integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1 integer(C_INT), parameter :: STATUS_EXIT_ON_TRIGGER = 2 + integer(C_INT), parameter :: STATUS_CONTINUE_BATCH = 3 contains @@ -129,6 +133,147 @@ contains end function openmc_next_batch +!=============================================================================== +! OPENMC_NEXT_BATCH_BEFORE_CMFD_INIT +!=============================================================================== + + function openmc_next_batch_before_cmfd_init() result(err) bind(C) + integer(C_INT) :: err + + err = 0 + + ! Make sure simulation has been initialized + if (.not. simulation_initialized) then + err = E_ALLOCATE + call set_errmsg("Simulation has not been initialized yet.") + return + end if + + call initialize_batch() + + end function openmc_next_batch_before_cmfd_init + +!=============================================================================== +! OPENMC_NEXT_BATCH_BETWEEN_CMFD_INIT_EXECUTE +!=============================================================================== + + function openmc_next_batch_between_cmfd_init_execute(status) result(err) bind(C) + integer(C_INT), intent(out), optional :: status + integer(C_INT) :: err + + type(Particle) :: p + integer(8) :: i_work + + err = 0 + + ! Handle restart runs + if (restart_run .and. current_batch <= restart_batch) then + call replay_batch_history() + status = STATUS_EXIT_NORMAL + return + end if + + ! ======================================================================= + ! LOOP OVER GENERATIONS + GENERATION_LOOP: do current_gen = 1, gen_per_batch + + call initialize_generation() + + ! Start timer for transport + call time_transport % start() + + ! ==================================================================== + ! LOOP OVER PARTICLES +!$omp parallel do schedule(runtime) firstprivate(p) copyin(tally_derivs) + PARTICLE_LOOP: do i_work = 1, work + current_work = i_work + + ! grab source particle from bank + call initialize_history(p, current_work) + + ! transport particle + call transport(p) + + end do PARTICLE_LOOP +!$omp end parallel do + + ! Accumulate time for transport + call time_transport % stop() + + call finalize_generation() + + end do GENERATION_LOOP + + ! Reduce tallies onto master process and accumulate + call time_tallies % start() + call accumulate_tallies() + call time_tallies % stop() + + ! Reset global tally results + if (current_batch <= n_inactive) then + global_tallies(:,:) = ZERO + n_realizations = 0 + end if + + if (present(status)) then + status = STATUS_CONTINUE_BATCH + end if + + end function openmc_next_batch_between_cmfd_init_execute + +!=============================================================================== +! OPENMC_NEXT_BATCH_AFTER_CMFD_EXECUTE +!=============================================================================== + + function openmc_next_batch_after_cmfd_execute(status) result(err) bind(C) + integer(C_INT), intent(out), optional :: status + integer(C_INT) :: err +#ifdef OPENMC_MPI + integer :: mpi_err ! MPI error code +#endif + + err = 0 + + if (run_mode == MODE_EIGENVALUE) then + ! Write batch output + if (master .and. verbosity >= 7) call print_batch_keff() + end if + + ! Check_triggers + if (master) call check_triggers() +#ifdef OPENMC_MPI + call MPI_BCAST(satisfy_triggers, 1, MPI_LOGICAL, 0, & + mpi_intracomm, mpi_err) +#endif + if (satisfy_triggers .or. & + (trigger_on .and. current_batch == n_max_batches)) then + call statepoint_batch % add(current_batch) + end if + + ! Write out state point if it's been specified for this batch + if (statepoint_batch % contains(current_batch)) then + err = openmc_statepoint_write() + end if + + ! Write out source point if it's been specified for this batch + if ((sourcepoint_batch % contains(current_batch) .or. source_latest) .and. & + source_write) then + call write_source_point() + end if + + ! Check simulation ending criteria + if (present(status)) then + if (current_batch == n_max_batches) then + status = STATUS_EXIT_MAX_BATCH + elseif (satisfy_triggers) then + status = STATUS_EXIT_ON_TRIGGER + else + status = STATUS_EXIT_NORMAL + end if + end if + + end function openmc_next_batch_after_cmfd_execute + !=============================================================================== ! INITIALIZE_HISTORY !=============================================================================== @@ -206,11 +351,6 @@ contains end do end if - ! check CMFD initialize batch - if (run_mode == MODE_EIGENVALUE) then - if (cmfd_run) call cmfd_init_batch() - end if - ! Add user tallies to active tallies list call setup_active_tallies() From 3a73450ed9aacf69174702c20e8571bb7209e341 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 16 Aug 2018 02:23:20 -0400 Subject: [PATCH 08/58] Replicate all of cmfd_header.F90 and cmfd_input.F90 through CAPI --- examples/cmfd_testing/cmfd_ref.xml | 2 +- openmc/capi/tally.py | 6 ++ openmc/cmfd.py | 135 +++++++++++++++++++++++++++-- src/simulation.F90 | 5 ++ 4 files changed, 141 insertions(+), 7 deletions(-) diff --git a/examples/cmfd_testing/cmfd_ref.xml b/examples/cmfd_testing/cmfd_ref.xml index ae50243c03..1f6cdac198 100644 --- a/examples/cmfd_testing/cmfd_ref.xml +++ b/examples/cmfd_testing/cmfd_ref.xml @@ -7,7 +7,7 @@ 10 1 1 0.0 0.0 1.0 1.0 1.0 1.0 - + 5 dominance true diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index cc23aa3f97..50ebeccabd 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -53,6 +53,9 @@ _dll.openmc_tally_get_scores.errcheck = _error_handler _dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_type.restype = c_int _dll.openmc_tally_get_type.errcheck = _error_handler +_dll.openmc_tally_reset.argtypes = [c_int32] +_dll.openmc_tally_reset.restype = c_int +_dll.openmc_tally_reset.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 @@ -349,6 +352,9 @@ class Tally(_FortranObjectWithID): return std_dev + def reset(self): + _dll.openmc_tally_reset(self._index) + def ci_width(self, alpha=0.05): """Confidence interval half-width based on a Student t distribution diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 84cb121e3e..0a0adb6780 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -590,10 +590,32 @@ class CMFDRun(object): cmfd_tally_ids: list of ids corresponding to CMFD tallies (details:) energy_filters: Boolean that stores whether energy filters should be created or not. Set to true if user specifies energy grid in CMFDMesh, false otherwise + cmfd_on: Boolean to tell if cmfd solver should be initiated, based on whether current batch has reached variable + cmfd_begin + batch_num: Current batch + # Look at cmfd_header.F90 for description + flux + totalxs + p1scattxs + scattxs + nfissxs + diffcof + dtilde + dhat + hxyz + current + cmfd_src + openmc_src + sourcecounts + weightfactors + entropy + balance + src_cmp + dom + k_cmfd TODO: Put descriptions for all methods in CMFDRun TODO All timing variables - TODO: add tally type and tally estimator to CAPI """ @@ -615,7 +637,7 @@ class CMFDRun(object): self._cmfd_shift = 1.e-6 self._cmfd_spectral = 0. self._cmfd_stol = 1.e-8 - self._cmfd_reset = None + self._cmfd_reset = [5,10] self._cmfd_write_matrices = False # External variables used during runtime but users don't have control over @@ -631,6 +653,29 @@ class CMFDRun(object): self._cmfd_filter_ids = None self._cmfd_tally_ids = None self._energy_filters = None + self._cmfd_on = False + self._batch_num = 1 + + # Numpy arrays used to build CMFD matrices + self._flux = None + self._totalxs = None + self._p1scattxs = None + self._scattxs = None + self._nfissxs = None + self._diffcof = None + self._dtilde = None + self._dhat = None + self._hxyz = None + self._current = None + self._cmfd_src = None + self._openmc_src = None + self._sourcecounts = None + self._weightfactors = None + self._entropy = None + self._balance = None + self._src_cmp = None + self._dom = None + self._k_cmfd = None @property def cmfd_begin(self): @@ -827,7 +872,7 @@ class CMFDRun(object): while(True): openmc.capi.next_batch_before_cmfd_init() - self._cmfd_init() + self._cmfd_init_batch() # Status determines whether batch should continue with a # CMFD update or skip it entirely if it is a restart run status = openmc.capi.next_batch_between_cmfd_init_execute() @@ -838,12 +883,26 @@ class CMFDRun(object): status = openmc.capi.next_batch_after_cmfd_execute() if status != 0: break + self._batch_num += 1 openmc.capi.simulation_finalize() openmc.capi.finalize() def _configure_cmfd(self): - print('Configuring CMFD parameters for simulation') + # Read in cmfd input from python + self._read_cmfd_input() + + # TODO + # Initialize timers + #call time_cmfd % reset() + #call time_cmfdbuild % reset() + #call time_cmfdsolve % reset() + + # Initialize all numpy arrays used for cmfd solver + self._allocate_cmfd(n_batches) + + def _read_cmfd_input(self): + print(' Configuring CMFD parameters for simulation') # Check if CMFD mesh is defined if self._cmfd_mesh is None: @@ -894,12 +953,76 @@ class CMFDRun(object): # Create tally objects self._create_cmfd_tally() - def _cmfd_init(self): - pass + def _allocate_cmfd(self): + # Determine number of batches through C API + n_batches = openmc.capi.settings.batches + + # Extract spatial and energy indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + + # Allocate flux, cross sections and diffusion coefficient + self._flux = np.zeros((ng, nx, ny, nz)) + self._totalxs = np.zeros((ng, nx, ny, nz)) + self._p1scattxs = np.zeros((ng, nx, ny, nz)) + self._scattxs = np.zeros((ng, ng, nx, ny, nz)) + self._nfissxs = np.zeros((ng, ng, nx, ny, nz)) + self._diffcof = np.zeros((ng, nx, ny, nz)) + + # Allocate dtilde and dhat + self._dtilde = np.zeros((6, ng, nx, ny, nz)) + self._dhat = np.zeros((6, ng, nx, ny, nz)) + + # Allocate dimensions for each box (here for general case) + self._hxyz = np.zeros((3, nx, ny, nz)) + + # Allocate surface currents + self._current = np.zeros((12, ng, nx, ny, nz)) + + # Allocate source distributions + self._cmfd_src = np.zeros((ng, nx, ny, nz)) + self._openmc_src = np.zeros((ng, nx, ny, nz)) + + # Allocate source weight modification variables + self._sourcecounts = np.zeros((ng, nx*ny*nz)) + self._weightfactors = np.ones((ng, nx, ny, nz)) + + # Allocate batchwise parameters + self._entropy = np.zeros((n_batches, )) + self._balance = np.zeros((n_batches, )) + self._src_cmp = np.zeros((n_batches, )) + self._dom = np.zeros((n_batches, )) + self._k_cmfd = np.zeros((n_batches, )) + + def _cmfd_init_batch(self): + if self._cmfd_begin == self._batch_num: + self._cmfd_on = True + + # TODO: Figure out how to tell if part of restart run + # if restart_run: + # return + # If this is a restart run and we are just replaying batches leave + # if (restart_run .and. current_batch <= restart_batch) return + + # Check to reset tallies + if self._n_cmfd_resets > 0 and self._batch_num in self._cmfd_reset: + self._cmfd_tally_reset() def _cmfd_execute(self): pass + def _cmfd_tally_reset(self): + # TODO: How does write_message in error.F90 work? + # Print message + print(' CMFD tallies reset') + + # Reset CMFD tallies + tallies = openmc.capi.tallies + for tally_id in self._cmfd_tally_ids: + tallies[tally_id].reset() + def _create_cmfd_tally(self): # Create Mesh object based on CMFDMesh, stored internally cmfd_mesh = openmc.capi.Mesh() diff --git a/src/simulation.F90 b/src/simulation.F90 index fa1036fc5f..799baa7b0f 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -351,6 +351,11 @@ contains end do end if + ! check CMFD initialize batch + if (run_mode == MODE_EIGENVALUE) then + if (cmfd_run) call cmfd_init_batch() + end if + ! Add user tallies to active tallies list call setup_active_tallies() From e5e1202d8fdd0770e35cf5dd98fea0659b578fc7 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 16 Aug 2018 13:15:03 -0400 Subject: [PATCH 09/58] Get cmfd tallies to display properly through C API --- .../{ => basic/capi}/cmfd_ref.xml | 0 .../{ => basic/capi}/geometry.xml | 0 .../{ => basic/capi}/materials.xml | 0 .../{ => basic/capi}/run_openmc_cmfd.py | 0 .../{ => basic/capi}/settings.xml | 0 .../cmfd_testing/{ => basic/capi}/tallies.xml | 0 examples/cmfd_testing/basic/results_true.dat | 508 ++++++++++++++++++ examples/cmfd_testing/basic/trad/cmfd.xml | 15 + examples/cmfd_testing/basic/trad/geometry.xml | 43 ++ .../cmfd_testing/basic/trad/materials.xml | 12 + examples/cmfd_testing/basic/trad/settings.xml | 28 + examples/cmfd_testing/basic/trad/tallies.xml | 21 + src/cmfd_input.F90 | 3 + src/input_xml.F90 | 37 +- src/tallies/tally_header.F90 | 15 + 15 files changed, 664 insertions(+), 18 deletions(-) rename examples/cmfd_testing/{ => basic/capi}/cmfd_ref.xml (100%) rename examples/cmfd_testing/{ => basic/capi}/geometry.xml (100%) rename examples/cmfd_testing/{ => basic/capi}/materials.xml (100%) rename examples/cmfd_testing/{ => basic/capi}/run_openmc_cmfd.py (100%) rename examples/cmfd_testing/{ => basic/capi}/settings.xml (100%) rename examples/cmfd_testing/{ => basic/capi}/tallies.xml (100%) create mode 100644 examples/cmfd_testing/basic/results_true.dat create mode 100644 examples/cmfd_testing/basic/trad/cmfd.xml create mode 100644 examples/cmfd_testing/basic/trad/geometry.xml create mode 100644 examples/cmfd_testing/basic/trad/materials.xml create mode 100644 examples/cmfd_testing/basic/trad/settings.xml create mode 100644 examples/cmfd_testing/basic/trad/tallies.xml diff --git a/examples/cmfd_testing/cmfd_ref.xml b/examples/cmfd_testing/basic/capi/cmfd_ref.xml similarity index 100% rename from examples/cmfd_testing/cmfd_ref.xml rename to examples/cmfd_testing/basic/capi/cmfd_ref.xml diff --git a/examples/cmfd_testing/geometry.xml b/examples/cmfd_testing/basic/capi/geometry.xml similarity index 100% rename from examples/cmfd_testing/geometry.xml rename to examples/cmfd_testing/basic/capi/geometry.xml diff --git a/examples/cmfd_testing/materials.xml b/examples/cmfd_testing/basic/capi/materials.xml similarity index 100% rename from examples/cmfd_testing/materials.xml rename to examples/cmfd_testing/basic/capi/materials.xml diff --git a/examples/cmfd_testing/run_openmc_cmfd.py b/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py similarity index 100% rename from examples/cmfd_testing/run_openmc_cmfd.py rename to examples/cmfd_testing/basic/capi/run_openmc_cmfd.py diff --git a/examples/cmfd_testing/settings.xml b/examples/cmfd_testing/basic/capi/settings.xml similarity index 100% rename from examples/cmfd_testing/settings.xml rename to examples/cmfd_testing/basic/capi/settings.xml diff --git a/examples/cmfd_testing/tallies.xml b/examples/cmfd_testing/basic/capi/tallies.xml similarity index 100% rename from examples/cmfd_testing/tallies.xml rename to examples/cmfd_testing/basic/capi/tallies.xml diff --git a/examples/cmfd_testing/basic/results_true.dat b/examples/cmfd_testing/basic/results_true.dat new file mode 100644 index 0000000000..aba219ba83 --- /dev/null +++ b/examples/cmfd_testing/basic/results_true.dat @@ -0,0 +1,508 @@ +k-combined: +1.169891E+00 6.289489E-03 +tally 1: +1.173922E+01 +1.385461E+01 +2.164076E+01 +4.699368E+01 +2.906462E+01 +8.464937E+01 +3.382312E+01 +1.147095E+02 +3.632006E+01 +1.323878E+02 +3.655413E+01 +1.341064E+02 +3.347757E+01 +1.124264E+02 +2.931336E+01 +8.607239E+01 +2.182947E+01 +4.789565E+01 +1.147668E+01 +1.325716E+01 +tally 2: +2.298190E+01 +2.667071E+01 +1.600292E+01 +1.293670E+01 +4.268506E+01 +9.161216E+01 +3.022909E+01 +4.598915E+01 +5.680399E+01 +1.623879E+02 +4.033805E+01 +8.196263E+01 +6.814742E+01 +2.331778E+02 +4.851618E+01 +1.182330E+02 +7.392923E+01 +2.740255E+02 +5.253586E+01 +1.384152E+02 +7.332860E+01 +2.698608E+02 +5.227405E+01 +1.371810E+02 +6.830172E+01 +2.340687E+02 +4.867159E+01 +1.188724E+02 +5.885634E+01 +1.736180E+02 +4.170434E+01 +8.719622E+01 +4.371848E+01 +9.592893E+01 +3.106403E+01 +4.844308E+01 +2.338413E+01 +2.752467E+01 +1.636713E+01 +1.347770E+01 +tally 3: +1.538752E+01 +1.196478E+01 +1.079685E+00 +6.010786E-02 +2.911906E+01 +4.269070E+01 +1.822657E+00 +1.671850E-01 +3.885421E+01 +7.608218E+01 +2.541516E+00 +3.262451E-01 +4.673300E+01 +1.097036E+02 +2.885307E+00 +4.214444E-01 +5.059247E+01 +1.283984E+02 +3.222796E+00 +5.237329E-01 +5.034856E+01 +1.272538E+02 +3.230225E+00 +5.273424E-01 +4.688476E+01 +1.103152E+02 +2.941287E+00 +4.363749E-01 +4.013746E+01 +8.077506E+01 +2.634234E+00 +3.520270E-01 +2.996887E+01 +4.509953E+01 +1.946504E+00 +1.919104E-01 +1.575260E+01 +1.248707E+01 +1.020705E+00 +5.413569E-02 +tally 4: +3.049469E+00 +4.677325E-01 +0.000000E+00 +0.000000E+00 +2.770358E+00 +3.879191E-01 +5.514939E+00 +1.528899E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.514939E+00 +1.528899E+00 +2.770358E+00 +3.879191E-01 +5.032131E+00 +1.275040E+00 +7.294002E+00 +2.675589E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.294002E+00 +2.675589E+00 +5.032131E+00 +1.275040E+00 +7.036008E+00 +2.490718E+00 +8.668860E+00 +3.776102E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.668860E+00 +3.776102E+00 +7.036008E+00 +2.490718E+00 +8.352414E+00 +3.501945E+00 +9.345868E+00 +4.380719E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.345868E+00 +4.380719E+00 +8.352414E+00 +3.501945E+00 +9.093766E+00 +4.158282E+00 +9.223771E+00 +4.270120E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.223771E+00 +4.270120E+00 +9.093766E+00 +4.158282E+00 +9.219150E+00 +4.264346E+00 +8.530966E+00 +3.651778E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.530966E+00 +3.651778E+00 +9.219150E+00 +4.264346E+00 +8.690373E+00 +3.785262E+00 +7.204424E+00 +2.604203E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.204424E+00 +2.604203E+00 +8.690373E+00 +3.785262E+00 +7.513640E+00 +2.833028E+00 +5.326721E+00 +1.426975E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.326721E+00 +1.426975E+00 +7.513640E+00 +2.833028E+00 +5.662215E+00 +1.607757E+00 +2.848381E+00 +4.093396E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.848381E+00 +4.093396E-01 +5.662215E+00 +1.607757E+00 +3.025812E+00 +4.597241E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +1.538652E+01 +1.196332E+01 +2.252427E+00 +2.605738E-01 +2.911344E+01 +4.267319E+01 +3.873926E+00 +7.615035E-01 +3.884516E+01 +7.604619E+01 +5.280610E+00 +1.414008E+00 +4.672391E+01 +1.096625E+02 +6.261805E+00 +1.983205E+00 +5.058447E+01 +1.283588E+02 +6.733810E+00 +2.278242E+00 +5.033589E+01 +1.271898E+02 +6.714658E+00 +2.273652E+00 +4.687563E+01 +1.102719E+02 +6.215002E+00 +1.956978E+00 +4.013134E+01 +8.075062E+01 +5.253064E+00 +1.396224E+00 +2.996497E+01 +4.508840E+01 +3.818076E+00 +7.509442E-01 +1.574994E+01 +1.248291E+01 +2.219928E+00 +2.515492E-01 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.170416E+00 +1.172966E+00 +1.165537E+00 +1.170979E+00 +1.161922E+00 +1.157523E+00 +1.158873E+00 +1.162877E+00 +1.167101E+00 +1.168130E+00 +1.170570E+00 +1.168115E+00 +1.174081E+00 +1.169458E+00 +1.167848E+00 +1.165116E+00 +cmfd entropy +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.203643E+00 +3.207943E+00 +3.213367E+00 +3.214360E+00 +3.219634E+00 +3.222232E+00 +3.221744E+00 +3.224544E+00 +3.225990E+00 +3.227769E+00 +3.227417E+00 +3.230728E+00 +3.231662E+00 +3.233316E+00 +3.233193E+00 +3.232564E+00 +cmfd balance +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.009062E-03 +4.431773E-03 +3.152666E-03 +3.510383E-03 +2.052089E-03 +2.068651E-03 +1.502427E-03 +1.589825E-03 +1.566020E-03 +1.219160E-03 +1.017888E-03 +9.771622E-04 +1.010120E-03 +1.073382E-03 +1.172758E-03 +9.827332E-04 +cmfd dominance ratio + 0.000E+00 + 0.000E+00 + 0.000E+00 + 0.000E+00 + 5.397E-01 + 5.425E-01 + 5.481E-01 + 5.473E-01 + 5.503E-01 + 5.502E-01 + 5.483E-01 + 5.520E-01 + 5.505E-01 + 3.216E-01 + 5.373E-01 + 5.517E-01 + 5.508E-01 + 5.524E-01 + 5.524E-01 + 5.523E-01 +cmfd openmc source comparison +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.959834E-03 +5.655657E-03 +3.886185E-03 +4.035116E-03 +3.043277E-03 +5.455475E-03 +4.515311E-03 +2.439840E-03 +2.114032E-03 +2.673132E-03 +2.431749E-03 +4.330928E-03 +3.404647E-03 +3.680298E-03 +3.309620E-03 +3.705541E-03 +cmfd source +4.697085E-02 +7.920706E-02 +1.107968E-01 +1.250932E-01 +1.383930E-01 +1.380648E-01 +1.246874E-01 +1.113705E-01 +8.203754E-02 +4.337882E-02 diff --git a/examples/cmfd_testing/basic/trad/cmfd.xml b/examples/cmfd_testing/basic/trad/cmfd.xml new file mode 100644 index 0000000000..b1c623ef2a --- /dev/null +++ b/examples/cmfd_testing/basic/trad/cmfd.xml @@ -0,0 +1,15 @@ + + + + + -10 -1 -1 + 10 1 1 + 10 1 1 + 0.0 0.0 1.0 1.0 1.0 1.0 + + 5 10 + 5 + dominance + true + 1.e-15 1.e-20 + diff --git a/examples/cmfd_testing/basic/trad/geometry.xml b/examples/cmfd_testing/basic/trad/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/examples/cmfd_testing/basic/trad/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/examples/cmfd_testing/basic/trad/materials.xml b/examples/cmfd_testing/basic/trad/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/examples/cmfd_testing/basic/trad/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/examples/cmfd_testing/basic/trad/settings.xml b/examples/cmfd_testing/basic/trad/settings.xml new file mode 100644 index 0000000000..6548ac1e05 --- /dev/null +++ b/examples/cmfd_testing/basic/trad/settings.xml @@ -0,0 +1,28 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + + true + diff --git a/examples/cmfd_testing/basic/trad/tallies.xml b/examples/cmfd_testing/basic/trad/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/examples/cmfd_testing/basic/trad/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 6a6e40eeae..ab26a36357 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -262,6 +262,7 @@ contains integer(C_INT) :: err integer :: i_filt ! index in filters array integer :: filt_id + integer :: tally_id integer :: iarray3(3) ! temp integer array real(8) :: rarray3(3) ! temp double array real(C_DOUBLE), allocatable :: energies(:) @@ -434,6 +435,8 @@ contains do i = 1, size(cmfd_tallies) ! Allocate tally err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR) + call openmc_get_tally_next_id(tally_id) + err = openmc_tally_set_id(i_start + i - 1, tally_id) ! Point t to tally variable associate (t => cmfd_tallies(i) % obj) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index e81bb92f7b..a97cc268da 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1960,6 +1960,7 @@ contains integer :: k ! another loop index integer :: l ! loop over bins integer :: filter_id ! user-specified identifier for filter + integer :: tally_id ! user-specified identifier for filter integer :: i_filt ! index in filters array integer :: i_elem ! index of entry in dictionary integer :: n ! size of arrays in mesh specification @@ -2169,15 +2170,15 @@ contains ! Copy tally id if (check_for_node(node_tal, "id")) then - call get_node_value(node_tal, "id", t % id) + call get_node_value(node_tal, "id", tally_id) else call fatal_error("Must specify id for tally in tally XML file.") end if ! Check to make sure 'id' hasn't been used - if (tally_dict % has(t % id)) then + if (tally_dict % has(tally_id)) then call fatal_error("Two or more tallies use the same unique ID: " & - // to_str(t % id)) + // to_str(tally_id)) end if ! Copy tally name @@ -2216,7 +2217,7 @@ contains else call fatal_error("Could not find filter " & // trim(to_str(temp_filter(j))) // " specified on tally " & - // trim(to_str(t % id))) + // trim(to_str(tally_id))) end if ! Store the index of the filter @@ -2270,7 +2271,7 @@ contains if (.not. nuclide_dict % has(word)) then call fatal_error("Could not find the nuclide " & // trim(word) // " specified in tally " & - // trim(to_str(t % id)) // " in any material.") + // trim(to_str(tally_id)) // " in any material.") end if ! Set bin to index in nuclides array @@ -2580,7 +2581,7 @@ contains if (t % score_bins(j) == t % score_bins(k)) then call fatal_error("Duplicate score of type '" // trim(& reaction_name(t % score_bins(j))) // "' found in tally " & - // trim(to_str(t % id))) + // trim(to_str(tally_id))) end if end do end do @@ -2627,7 +2628,7 @@ contains end if else call fatal_error("No specified on tally " & - // trim(to_str(t % id)) // ".") + // trim(to_str(tally_id)) // ".") end if ! Check for a tally derivative. @@ -2650,7 +2651,7 @@ contains if (j == size(tally_derivs)) then call fatal_error("Could not find derivative " & // trim(to_str(t % deriv)) // " specified on tally " & - // trim(to_str(t % id))) + // trim(to_str(tally_id))) end if end do @@ -2658,7 +2659,7 @@ contains .or. tally_derivs(t % deriv) % variable == DIFF_TEMPERATURE) then if (any(t % nuclide_bins == -1)) then if (t % find_filter(FILTER_ENERGYOUT) > 0) then - call fatal_error("Error on tally " // trim(to_str(t % id)) & + call fatal_error("Error on tally " // trim(to_str(tally_id)) & // ": Cannot use a 'nuclide_density' or 'temperature' & &derivative on a tally with an outgoing energy filter and & &'total' nuclide rate. Instead, tally each nuclide in the & @@ -2735,7 +2736,7 @@ contains temp_str = to_lower(temp_str) else call fatal_error("Must specify trigger type for tally " // & - trim(to_str(t % id)) // " in tally XML file.") + trim(to_str(tally_id)) // " in tally XML file.") end if ! Get the convergence threshold for the trigger @@ -2743,7 +2744,7 @@ contains call get_node_value(node_trigger, "threshold", threshold) else call fatal_error("Must specify trigger threshold for tally " // & - trim(to_str(t % id)) // " in tally XML file.") + trim(to_str(tally_id)) // " in tally XML file.") end if ! Get list scores for this trigger @@ -2787,7 +2788,7 @@ contains t % triggers(trig_ind) % type = RELATIVE_ERROR case default call fatal_error("Unknown trigger type " // & - trim(temp_str) // " in tally " // trim(to_str(t % id))) + trim(temp_str) // " in tally " // trim(to_str(tally_id))) end select ! Store the trigger convergence threshold @@ -2808,7 +2809,7 @@ contains ! Check if an invalid score was set for the trigger if (t % triggers(trig_ind) % score_index == 0) then call fatal_error("The trigger score " // trim(score_name) // & - " is not set for tally " // trim(to_str(t % id))) + " is not set for tally " // trim(to_str(tally_id))) end if ! Store the trigger convergence threshold @@ -2824,7 +2825,7 @@ contains t % triggers(trig_ind) % type = RELATIVE_ERROR case default call fatal_error("Unknown trigger type " // trim(temp_str) // & - " in tally " // trim(to_str(t % id))) + " in tally " // trim(to_str(tally_id))) end select ! Increment the overall trigger index @@ -2856,7 +2857,7 @@ contains ! tally needs post-collision information if (t % estimator == ESTIMATOR_ANALOG) then call fatal_error("Cannot use track-length estimator for tally " & - // to_str(t % id)) + // to_str(tally_id)) end if ! Set estimator to track-length estimator @@ -2867,7 +2868,7 @@ contains ! tally needs post-collision information if (t % estimator == ESTIMATOR_ANALOG) then call fatal_error("Cannot use collision estimator for tally " & - // to_str(t % id)) + // to_str(tally_id)) end if ! Set estimator to collision estimator @@ -2879,8 +2880,8 @@ contains end select end if - ! Add tally to dictionary - call tally_dict % set(t % id, i) + ! Set tally id + err = openmc_tally_set_id(i_start + i - 1, tally_id) end associate end do READ_TALLIES diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 95fdacf398..d106e76b8e 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -38,6 +38,7 @@ module tally_header public :: openmc_tally_set_id public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores + public :: openmc_get_tally_next_id !=============================================================================== ! TALLYOBJECT describes a user-specified tally. The region of phase space to @@ -117,6 +118,10 @@ module tally_header ! Dictionary that maps user IDs to indices in 'tallies' type(DictIntInt), public :: tally_dict + ! The largest tally ID that has been specified in the system. This is useful + ! in case the code needs to find an ID for a new tally. + integer :: largest_tally_id + ! Global tallies ! 1) collision estimate of k-eff ! 2) absorption estimate of k-eff @@ -399,6 +404,7 @@ contains n_tallies = 0 if (allocated(tallies)) deallocate(tallies) call tally_dict % clear() + largest_tally_id = 0 if (allocated(global_tallies)) deallocate(global_tallies) @@ -763,6 +769,7 @@ contains if (allocated(tallies(index) % obj)) then tallies(index) % obj % id = id call tally_dict % set(id, index) + if (id > largest_tally_id) largest_tally_id = id err = 0 else @@ -1025,4 +1032,12 @@ contains end if end function openmc_tally_update_type + subroutine openmc_get_tally_next_id(id) bind(C) + ! Returns an ID number that has not been used by any other tallies. + integer(C_INT32_T), intent(out) :: id + + id = largest_tally_id + 1 + print *, id + end subroutine openmc_get_tally_next_id + end module tally_header From d80501ea1721b8b9d3774d9ad563b5aae9ad093d Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 16 Aug 2018 15:17:25 -0400 Subject: [PATCH 10/58] Clean up openmc.h and api.F90 --- include/openmc.h | 4 ++++ src/api.F90 | 8 ++++++++ src/tallies/tally_header.F90 | 5 +++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 991efb2c71..07f53cd632 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -47,6 +47,7 @@ extern "C" { int openmc_get_nuclide_index(const char name[], int* index); int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); + void openmc_get_tally_next_id(int32_t* id); int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); int openmc_init_f(const int* intracomm); @@ -72,6 +73,9 @@ extern "C" { int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(int* status); + int openmc_next_batch_before_cmfd_init(); + int openmc_next_batch_between_cmfd_init_execute(int* status); + int openmc_next_batch_after_cmfd_execute(int* status); int openmc_nuclide_name(int index, char** name); int openmc_particle_restart(); int openmc_plot_geometry(); diff --git a/src/api.F90 b/src/api.F90 index cc9120c7b9..66a0b8d83e 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -62,6 +62,7 @@ module openmc_api public :: openmc_get_nuclide_index public :: openmc_get_seed public :: openmc_get_tally_index + public :: openmc_get_tally_next_id public :: openmc_global_tallies public :: openmc_hard_reset public :: openmc_init_f @@ -77,6 +78,9 @@ module openmc_api public :: openmc_mesh_filter_set_mesh public :: openmc_meshsurface_filter_set_mesh public :: openmc_next_batch + public :: openmc_next_batch_before_cmfd_init + public :: openmc_next_batch_between_cmfd_init_execute + public :: openmc_next_batch_after_cmfd_execute public :: openmc_nuclide_name public :: openmc_plot_geometry public :: openmc_reset @@ -85,17 +89,21 @@ module openmc_api public :: openmc_simulation_init public :: openmc_source_bank public :: openmc_source_set_strength + public :: openmc_tally_get_estimator public :: openmc_tally_get_id public :: openmc_tally_get_filters public :: openmc_tally_get_n_realizations public :: openmc_tally_get_nuclides public :: openmc_tally_get_scores + public :: openmc_tally_get_type public :: openmc_tally_results + public :: openmc_tally_set_estimator public :: openmc_tally_set_filters public :: openmc_tally_set_id public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores public :: openmc_tally_set_type + public :: openmc_tally_update_type contains diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index d106e76b8e..c0d5a04894 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -22,6 +22,7 @@ module tally_header public :: free_memory_tally public :: openmc_extend_tallies public :: openmc_get_tally_index + public :: openmc_get_tally_next_id public :: openmc_global_tallies public :: openmc_tally_get_active public :: openmc_tally_get_estimator @@ -34,11 +35,12 @@ module tally_header public :: openmc_tally_reset public :: openmc_tally_results public :: openmc_tally_set_active + public :: openmc_tally_set_estimator public :: openmc_tally_set_filters public :: openmc_tally_set_id public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores - public :: openmc_get_tally_next_id + public :: openmc_tally_update_type !=============================================================================== ! TALLYOBJECT describes a user-specified tally. The region of phase space to @@ -1037,7 +1039,6 @@ contains integer(C_INT32_T), intent(out) :: id id = largest_tally_id + 1 - print *, id end subroutine openmc_get_tally_next_id end module tally_header From 2f43fa3960d81a41b25426c2e9188493c887c54f Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 16 Aug 2018 20:12:04 -0400 Subject: [PATCH 11/58] Set cmfd tally type and estimator through C API --- examples/cmfd_testing/basic/capi/run_openmc_cmfd.py | 3 +-- openmc/cmfd.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py b/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py index d377c0cd14..5db5394ac0 100644 --- a/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py +++ b/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py @@ -7,15 +7,14 @@ cmfd_mesh.lower_left = [-10, -1, -1] cmfd_mesh.upper_right = [10, 1, 1] cmfd_mesh.dimension = [10, 1, 1] cmfd_mesh.albedo = [0., 0., 1., 1., 1., 1.] -cmfd_mesh.energy = [0.001, 0.01, 0.1] # Initialize CMFDRun object cmfd_run = openmc.CMFDRun() # Set all runtime parameters (cmfd_mesh, tolerances, tally_resets, etc) - # All error checking done under the hood when setter function called cmfd_run.cmfd_mesh = cmfd_mesh +cmfd_run.cmfd_reset = [5,10] # Run CMFD cmfd_run.run() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 0a0adb6780..3cf5e6cbec 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -637,7 +637,7 @@ class CMFDRun(object): self._cmfd_shift = 1.e-6 self._cmfd_spectral = 0. self._cmfd_stol = 1.e-8 - self._cmfd_reset = [5,10] + self._cmfd_reset = [] self._cmfd_write_matrices = False # External variables used during runtime but users don't have control over @@ -899,7 +899,7 @@ class CMFDRun(object): #call time_cmfdsolve % reset() # Initialize all numpy arrays used for cmfd solver - self._allocate_cmfd(n_batches) + self._allocate_cmfd() def _read_cmfd_input(self): print(' Configuring CMFD parameters for simulation') @@ -1085,6 +1085,8 @@ class CMFDRun(object): tally.filters = [mesh_filter] # Set scores for tally tally.scores = ['flux', 'total'] + tally.type = 'volume' + tally.estimator = 'analog' # Set attributes of CMFD neutron production tally if i == 1: @@ -1095,6 +1097,8 @@ class CMFDRun(object): tally.filters = [mesh_filter] # Set scores for tally tally.scores = ['nu-scatter', 'nu-fission'] + tally.type = 'volume' + tally.estimator = 'analog' # Set attributes of CMFD surface current tally if i == 2: @@ -1105,6 +1109,8 @@ class CMFDRun(object): tally.filters = [meshsurface_filter] # Set scores for tally tally.scores = ['current'] + tally.type = 'mesh-surface' + tally.estimator = 'analog' # Set attributes of CMFD P1 scatter tally if i == 3: @@ -1115,6 +1121,8 @@ class CMFDRun(object): tally.filters = [mesh_filter, legendre_filter] # Set scores for tally tally.scores = ['scatter'] + tally.type = 'volume' + tally.estimator = 'analog' # Set all tallies to be active from beginning tally.active = True \ No newline at end of file From 465a9ad83809d1c3409bc92b3fa373c8db4eca18 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 16 Aug 2018 20:43:48 -0400 Subject: [PATCH 12/58] Use C API functions in cmfd_input.F90 --- src/cmfd_input.F90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index ab26a36357..33cd01a9e9 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -462,10 +462,10 @@ contains t % name = "CMFD flux, total" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Allocate and set filters allocate(filter_indices(n_filter)) @@ -490,10 +490,10 @@ contains t % name = "CMFD neutron production" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Set the incoming energy mesh filter index in the tally find_filter ! array @@ -525,7 +525,7 @@ contains t % name = "CMFD surface currents" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Allocate and set filters allocate(filter_indices(n_filter)) @@ -542,17 +542,17 @@ contains ! Set macro bins t % score_bins(1) = SCORE_CURRENT - t % type = TALLY_MESH_SURFACE + err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR) else if (i == 4) then ! Set name t % name = "CMFD P1 scatter" ! Set tally estimator to analog - t % estimator = ESTIMATOR_ANALOG + err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) ! Set tally type to volume - t % type = TALLY_VOLUME + err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) ! Allocate and set filters n_filter = 2 From 6798cafde17c4c09fcc649c9af77b11b8a442ec0 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 20 Aug 2018 17:56:22 -0400 Subject: [PATCH 13/58] Finish implementing compute_xs --- examples/cmfd_testing/basic/results_true.dat | 508 ------------------ .../{basic => cmfd-feed}/capi/cmfd_ref.xml | 0 .../{basic => cmfd-feed}/capi/geometry.xml | 0 .../{basic => cmfd-feed}/capi/materials.xml | 0 .../capi/run_openmc_cmfd.py | 7 +- .../{basic => cmfd-feed}/capi/settings.xml | 0 .../{basic => cmfd-feed}/capi/tallies.xml | 0 .../{basic => cmfd-feed}/trad/cmfd.xml | 5 +- .../{basic => cmfd-feed}/trad/geometry.xml | 0 .../{basic => cmfd-feed}/trad/materials.xml | 0 .../{basic => cmfd-feed}/trad/settings.xml | 0 .../{basic => cmfd-feed}/trad/tallies.xml | 0 openmc/capi/settings.py | 7 +- openmc/cmfd.py | 333 ++++++++++-- src/cmfd_data.F90 | 2 +- src/simulation_header.F90 | 2 +- 16 files changed, 293 insertions(+), 571 deletions(-) delete mode 100644 examples/cmfd_testing/basic/results_true.dat rename examples/cmfd_testing/{basic => cmfd-feed}/capi/cmfd_ref.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/capi/geometry.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/capi/materials.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/capi/run_openmc_cmfd.py (68%) rename examples/cmfd_testing/{basic => cmfd-feed}/capi/settings.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/capi/tallies.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/trad/cmfd.xml (75%) rename examples/cmfd_testing/{basic => cmfd-feed}/trad/geometry.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/trad/materials.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/trad/settings.xml (100%) rename examples/cmfd_testing/{basic => cmfd-feed}/trad/tallies.xml (100%) diff --git a/examples/cmfd_testing/basic/results_true.dat b/examples/cmfd_testing/basic/results_true.dat deleted file mode 100644 index aba219ba83..0000000000 --- a/examples/cmfd_testing/basic/results_true.dat +++ /dev/null @@ -1,508 +0,0 @@ -k-combined: -1.169891E+00 6.289489E-03 -tally 1: -1.173922E+01 -1.385461E+01 -2.164076E+01 -4.699368E+01 -2.906462E+01 -8.464937E+01 -3.382312E+01 -1.147095E+02 -3.632006E+01 -1.323878E+02 -3.655413E+01 -1.341064E+02 -3.347757E+01 -1.124264E+02 -2.931336E+01 -8.607239E+01 -2.182947E+01 -4.789565E+01 -1.147668E+01 -1.325716E+01 -tally 2: -2.298190E+01 -2.667071E+01 -1.600292E+01 -1.293670E+01 -4.268506E+01 -9.161216E+01 -3.022909E+01 -4.598915E+01 -5.680399E+01 -1.623879E+02 -4.033805E+01 -8.196263E+01 -6.814742E+01 -2.331778E+02 -4.851618E+01 -1.182330E+02 -7.392923E+01 -2.740255E+02 -5.253586E+01 -1.384152E+02 -7.332860E+01 -2.698608E+02 -5.227405E+01 -1.371810E+02 -6.830172E+01 -2.340687E+02 -4.867159E+01 -1.188724E+02 -5.885634E+01 -1.736180E+02 -4.170434E+01 -8.719622E+01 -4.371848E+01 -9.592893E+01 -3.106403E+01 -4.844308E+01 -2.338413E+01 -2.752467E+01 -1.636713E+01 -1.347770E+01 -tally 3: -1.538752E+01 -1.196478E+01 -1.079685E+00 -6.010786E-02 -2.911906E+01 -4.269070E+01 -1.822657E+00 -1.671850E-01 -3.885421E+01 -7.608218E+01 -2.541516E+00 -3.262451E-01 -4.673300E+01 -1.097036E+02 -2.885307E+00 -4.214444E-01 -5.059247E+01 -1.283984E+02 -3.222796E+00 -5.237329E-01 -5.034856E+01 -1.272538E+02 -3.230225E+00 -5.273424E-01 -4.688476E+01 -1.103152E+02 -2.941287E+00 -4.363749E-01 -4.013746E+01 -8.077506E+01 -2.634234E+00 -3.520270E-01 -2.996887E+01 -4.509953E+01 -1.946504E+00 -1.919104E-01 -1.575260E+01 -1.248707E+01 -1.020705E+00 -5.413569E-02 -tally 4: -3.049469E+00 -4.677325E-01 -0.000000E+00 -0.000000E+00 -2.770358E+00 -3.879191E-01 -5.514939E+00 -1.528899E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.514939E+00 -1.528899E+00 -2.770358E+00 -3.879191E-01 -5.032131E+00 -1.275040E+00 -7.294002E+00 -2.675589E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.294002E+00 -2.675589E+00 -5.032131E+00 -1.275040E+00 -7.036008E+00 -2.490718E+00 -8.668860E+00 -3.776102E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.668860E+00 -3.776102E+00 -7.036008E+00 -2.490718E+00 -8.352414E+00 -3.501945E+00 -9.345868E+00 -4.380719E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -9.345868E+00 -4.380719E+00 -8.352414E+00 -3.501945E+00 -9.093766E+00 -4.158282E+00 -9.223771E+00 -4.270120E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -9.223771E+00 -4.270120E+00 -9.093766E+00 -4.158282E+00 -9.219150E+00 -4.264346E+00 -8.530966E+00 -3.651778E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.530966E+00 -3.651778E+00 -9.219150E+00 -4.264346E+00 -8.690373E+00 -3.785262E+00 -7.204424E+00 -2.604203E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.204424E+00 -2.604203E+00 -8.690373E+00 -3.785262E+00 -7.513640E+00 -2.833028E+00 -5.326721E+00 -1.426975E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.326721E+00 -1.426975E+00 -7.513640E+00 -2.833028E+00 -5.662215E+00 -1.607757E+00 -2.848381E+00 -4.093396E-01 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.848381E+00 -4.093396E-01 -5.662215E+00 -1.607757E+00 -3.025812E+00 -4.597241E-01 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -tally 5: -1.538652E+01 -1.196332E+01 -2.252427E+00 -2.605738E-01 -2.911344E+01 -4.267319E+01 -3.873926E+00 -7.615035E-01 -3.884516E+01 -7.604619E+01 -5.280610E+00 -1.414008E+00 -4.672391E+01 -1.096625E+02 -6.261805E+00 -1.983205E+00 -5.058447E+01 -1.283588E+02 -6.733810E+00 -2.278242E+00 -5.033589E+01 -1.271898E+02 -6.714658E+00 -2.273652E+00 -4.687563E+01 -1.102719E+02 -6.215002E+00 -1.956978E+00 -4.013134E+01 -8.075062E+01 -5.253064E+00 -1.396224E+00 -2.996497E+01 -4.508840E+01 -3.818076E+00 -7.509442E-01 -1.574994E+01 -1.248291E+01 -2.219928E+00 -2.515492E-01 -cmfd indices -1.000000E+01 -1.000000E+00 -1.000000E+00 -1.000000E+00 -k cmfd -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.170416E+00 -1.172966E+00 -1.165537E+00 -1.170979E+00 -1.161922E+00 -1.157523E+00 -1.158873E+00 -1.162877E+00 -1.167101E+00 -1.168130E+00 -1.170570E+00 -1.168115E+00 -1.174081E+00 -1.169458E+00 -1.167848E+00 -1.165116E+00 -cmfd entropy -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.203643E+00 -3.207943E+00 -3.213367E+00 -3.214360E+00 -3.219634E+00 -3.222232E+00 -3.221744E+00 -3.224544E+00 -3.225990E+00 -3.227769E+00 -3.227417E+00 -3.230728E+00 -3.231662E+00 -3.233316E+00 -3.233193E+00 -3.232564E+00 -cmfd balance -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -4.009062E-03 -4.431773E-03 -3.152666E-03 -3.510383E-03 -2.052089E-03 -2.068651E-03 -1.502427E-03 -1.589825E-03 -1.566020E-03 -1.219160E-03 -1.017888E-03 -9.771622E-04 -1.010120E-03 -1.073382E-03 -1.172758E-03 -9.827332E-04 -cmfd dominance ratio - 0.000E+00 - 0.000E+00 - 0.000E+00 - 0.000E+00 - 5.397E-01 - 5.425E-01 - 5.481E-01 - 5.473E-01 - 5.503E-01 - 5.502E-01 - 5.483E-01 - 5.520E-01 - 5.505E-01 - 3.216E-01 - 5.373E-01 - 5.517E-01 - 5.508E-01 - 5.524E-01 - 5.524E-01 - 5.523E-01 -cmfd openmc source comparison -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.959834E-03 -5.655657E-03 -3.886185E-03 -4.035116E-03 -3.043277E-03 -5.455475E-03 -4.515311E-03 -2.439840E-03 -2.114032E-03 -2.673132E-03 -2.431749E-03 -4.330928E-03 -3.404647E-03 -3.680298E-03 -3.309620E-03 -3.705541E-03 -cmfd source -4.697085E-02 -7.920706E-02 -1.107968E-01 -1.250932E-01 -1.383930E-01 -1.380648E-01 -1.246874E-01 -1.113705E-01 -8.203754E-02 -4.337882E-02 diff --git a/examples/cmfd_testing/basic/capi/cmfd_ref.xml b/examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml similarity index 100% rename from examples/cmfd_testing/basic/capi/cmfd_ref.xml rename to examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml diff --git a/examples/cmfd_testing/basic/capi/geometry.xml b/examples/cmfd_testing/cmfd-feed/capi/geometry.xml similarity index 100% rename from examples/cmfd_testing/basic/capi/geometry.xml rename to examples/cmfd_testing/cmfd-feed/capi/geometry.xml diff --git a/examples/cmfd_testing/basic/capi/materials.xml b/examples/cmfd_testing/cmfd-feed/capi/materials.xml similarity index 100% rename from examples/cmfd_testing/basic/capi/materials.xml rename to examples/cmfd_testing/cmfd-feed/capi/materials.xml diff --git a/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py similarity index 68% rename from examples/cmfd_testing/basic/capi/run_openmc_cmfd.py rename to examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py index 5db5394ac0..73d8e9a598 100644 --- a/examples/cmfd_testing/basic/capi/run_openmc_cmfd.py +++ b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py @@ -7,6 +7,8 @@ cmfd_mesh.lower_left = [-10, -1, -1] cmfd_mesh.upper_right = [10, 1, 1] cmfd_mesh.dimension = [10, 1, 1] cmfd_mesh.albedo = [0., 0., 1., 1., 1., 1.] +cmfd_mesh.energy = [0., 10000., 10000000.] +cmfd_mesh.map = [0,1,1,1,1,1,1,1,1,0] # Initialize CMFDRun object cmfd_run = openmc.CMFDRun() @@ -14,7 +16,10 @@ cmfd_run = openmc.CMFDRun() # Set all runtime parameters (cmfd_mesh, tolerances, tally_resets, etc) # All error checking done under the hood when setter function called cmfd_run.cmfd_mesh = cmfd_mesh -cmfd_run.cmfd_reset = [5,10] +cmfd_run.cmfd_begin = 5 +cmfd_run.cmfd_display = 'dominance' +cmfd_run.cmfd_feedback = True +cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] # Run CMFD cmfd_run.run() diff --git a/examples/cmfd_testing/basic/capi/settings.xml b/examples/cmfd_testing/cmfd-feed/capi/settings.xml similarity index 100% rename from examples/cmfd_testing/basic/capi/settings.xml rename to examples/cmfd_testing/cmfd-feed/capi/settings.xml diff --git a/examples/cmfd_testing/basic/capi/tallies.xml b/examples/cmfd_testing/cmfd-feed/capi/tallies.xml similarity index 100% rename from examples/cmfd_testing/basic/capi/tallies.xml rename to examples/cmfd_testing/cmfd-feed/capi/tallies.xml diff --git a/examples/cmfd_testing/basic/trad/cmfd.xml b/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml similarity index 75% rename from examples/cmfd_testing/basic/trad/cmfd.xml rename to examples/cmfd_testing/cmfd-feed/trad/cmfd.xml index b1c623ef2a..adda403407 100644 --- a/examples/cmfd_testing/basic/trad/cmfd.xml +++ b/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml @@ -6,10 +6,11 @@ 10 1 1 10 1 1 0.0 0.0 1.0 1.0 1.0 1.0 + 0.0 10000.0 10000000.0 + 1 2 2 2 2 2 2 2 2 1 - 5 10 5 dominance - true + false 1.e-15 1.e-20 diff --git a/examples/cmfd_testing/basic/trad/geometry.xml b/examples/cmfd_testing/cmfd-feed/trad/geometry.xml similarity index 100% rename from examples/cmfd_testing/basic/trad/geometry.xml rename to examples/cmfd_testing/cmfd-feed/trad/geometry.xml diff --git a/examples/cmfd_testing/basic/trad/materials.xml b/examples/cmfd_testing/cmfd-feed/trad/materials.xml similarity index 100% rename from examples/cmfd_testing/basic/trad/materials.xml rename to examples/cmfd_testing/cmfd-feed/trad/materials.xml diff --git a/examples/cmfd_testing/basic/trad/settings.xml b/examples/cmfd_testing/cmfd-feed/trad/settings.xml similarity index 100% rename from examples/cmfd_testing/basic/trad/settings.xml rename to examples/cmfd_testing/cmfd-feed/trad/settings.xml diff --git a/examples/cmfd_testing/basic/trad/tallies.xml b/examples/cmfd_testing/cmfd-feed/trad/tallies.xml similarity index 100% rename from examples/cmfd_testing/basic/trad/tallies.xml rename to examples/cmfd_testing/cmfd-feed/trad/tallies.xml diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index d706112c41..61f0ee2214 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -1,4 +1,5 @@ -from ctypes import c_int, c_int32, c_int64, c_double, c_char_p, POINTER +from ctypes import (c_int, c_int32, c_int64, c_double, c_char_p, c_bool, + POINTER) from . import _dll from .core import _DLLGlobal @@ -17,9 +18,13 @@ _dll.openmc_get_seed.restype = c_int64 class _Settings(object): # Attributes that are accessed through a descriptor batches = _DLLGlobal(c_int32, 'n_batches') + current_batch = _DLLGlobal(c_int, 'openmc_current_batch') generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') + master = _DLLGlobal(c_bool, 'openmc_master') particles = _DLLGlobal(c_int64, 'n_particles') + restart_batch = _DLLGlobal(c_int, 'openmc_restart_batch') + restart_run = _DLLGlobal(c_bool, 'openmc_restart_run') verbosity = _DLLGlobal(c_int, 'openmc_verbosity') @property diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 3cf5e6cbec..51af386343 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -10,6 +10,7 @@ References """ +# TODO: Check to make sure no redundant import statements from collections.abc import Iterable from numbers import Real, Integral from xml.etree import ElementTree as ET @@ -21,9 +22,28 @@ from openmc.clean_xml import clean_xml_indentation from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) -# Maximum/minimum neutron energies, from src/api.F90 -ENERGY_MAX_NEUTRON = np.inf -ENERGY_MIN_NEUTRON = 0. + +""" +-------------- +CMFD CONSTANTS +-------------- +""" +# Maximum/minimum neutron energies +_ENERGY_MAX_NEUTRON = np.inf +_ENERGY_MIN_NEUTRON = 0. + +# Tolerance for detecting zero flux values +_TINY_BIT = 1.e-8 + +# For non-accelerated regions on coarse mesh overlay +_CMFD_NOACCEL = 99999 + +# Constant to represent a zero flux "albedo" +_ZERO_FLUX = 999.0 + +# Constant for writing out no residual +_CMFD_NORES = 99999.0 + class CMFDMesh(object): """A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD) @@ -156,7 +176,7 @@ class CMFDMesh(object): def map(self, meshmap): check_type('CMFD mesh map', meshmap, Iterable, Integral) for m in meshmap: - check_value('CMFD mesh map', m, [1, 2]) + check_value('CMFD mesh map', m, [0, 1]) self._map = meshmap def _get_xml_element(self): @@ -186,7 +206,7 @@ class CMFDMesh(object): if self.map is not None: subelement = ET.SubElement(element, "map") - subelement.text = ' '.join(map(str, self.map)) + subelement.text = ' '.join(map(str, [self.map[i]+1 for i in range(len(self.map))])) return element @@ -409,8 +429,8 @@ class CMFD(object): # Check upper right coordinates are greater than lower left if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): raise ValueError('CMFD mesh requires upper right ' - 'coordinates to be greater than lower ' - 'left coordinates') + 'coordinates to be greater than lower ' + 'left coordinates') mesh.width = np.true_divide( (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ np.array(mesh.dimension)) @@ -577,11 +597,9 @@ class CMFDRun(object): Attributes ---------- - To add: cmfd_coremap: Flag for active core map - indices: Stores spatial and group dimensions as [nx, ny, nz, ng] + To add: indices: Stores spatial and group dimensions as [nx, ny, nz, ng] egrid: energy grid used for CMFD acceleration albedo: Albedo for global boundary conditions, taken from CMFD mesh. Set to [1,1,1,1,1,1] if not specified by user - cmfd_coremap: Optional acceleration map to overlay on coarse mesh spatial grid, taken from CMFD mesh n_cmfd_resets: Number of elements in tally_reset, list that stores batches where CMFD tallies should be reset cmfd_atoli: Absolute GS tolerance, set by gauss_seidel_tolerance cmfd_rtoli: Relative GS tolerance, set by gauss_seidel_tolerance @@ -592,7 +610,6 @@ class CMFDRun(object): Set to true if user specifies energy grid in CMFDMesh, false otherwise cmfd_on: Boolean to tell if cmfd solver should be initiated, based on whether current batch has reached variable cmfd_begin - batch_num: Current batch # Look at cmfd_header.F90 for description flux totalxs @@ -613,9 +630,12 @@ class CMFDRun(object): src_cmp dom k_cmfd + keff_bal + mat_dim TODO: Put descriptions for all methods in CMFDRun TODO All timing variables + TODO Get rid of CMFD constants """ @@ -641,7 +661,6 @@ class CMFDRun(object): self._cmfd_write_matrices = False # External variables used during runtime but users don't have control over - self._cmfd_coremap = False self._indices = np.zeros(4, dtype=int) self._egrid = None self._albedo = None @@ -654,7 +673,8 @@ class CMFDRun(object): self._cmfd_tally_ids = None self._energy_filters = None self._cmfd_on = False - self._batch_num = 1 + self._mat_dim = _CMFD_NOACCEL + self._keff_bal = None # Numpy arrays used to build CMFD matrices self._flux = None @@ -745,7 +765,7 @@ class CMFDRun(object): def cmfd_begin(self, cmfd_begin): check_type('CMFD begin batch', cmfd_begin, Integral) check_greater_than('CMFD begin batch', cmfd_begin, 0) - self._cmfd_begin = begin + self._cmfd_begin = cmfd_begin @dhat_reset.setter def dhat_reset(self, dhat_reset): @@ -793,12 +813,12 @@ class CMFDRun(object): # Check lower left defined if mesh.lower_left is None: raise ValueError('CMFD mesh requires lower left coordinates ' - 'to be specified') + 'to be specified') # Check that both upper right and width both not defined if mesh.upper_right is not None and mesh.width is not None: raise ValueError('Both upper right coordinates and width ' - 'cannot be specified for CMFD mesh') + 'cannot be specified for CMFD mesh') # Check that at least one of width or upper right is defined if mesh.upper_right is None and mesh.width is None: @@ -865,7 +885,8 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self): + def run(self, mpi_procs=None, omp_threads=None): + # TODO: Add logic for mpi_procs, omp_threads as args openmc.capi.init() self._configure_cmfd() openmc.capi.simulation_init() @@ -877,13 +898,13 @@ class CMFDRun(object): # CMFD update or skip it entirely if it is a restart run status = openmc.capi.next_batch_between_cmfd_init_execute() if status != 0: - self._cmfd_execute() + if self._cmfd_on: + self._execute_cmfd() # Status now determines whether another batch should be run # or simulation should be terminated. status = openmc.capi.next_batch_after_cmfd_execute() if status != 0: break - self._batch_num += 1 openmc.capi.simulation_finalize() openmc.capi.finalize() @@ -902,12 +923,14 @@ class CMFDRun(object): self._allocate_cmfd() def _read_cmfd_input(self): + # TODO: Print message with verbosity + # Print message print(' Configuring CMFD parameters for simulation') # Check if CMFD mesh is defined if self._cmfd_mesh is None: raise ValueError('No CMFD mesh has been specified for ' - 'simulation') + 'simulation') # Set spatial dimensions of CMFD object # Iterate through each element of self._cmfd_mesh.dimension @@ -923,7 +946,7 @@ class CMFDRun(object): self._energy_filters = True # TODO: MG mode check else: - self._egrid = np.array([ENERGY_MIN_NEUTRON, ENERGY_MAX_NEUTRON]) + self._egrid = np.array([_ENERGY_MIN_NEUTRON, _ENERGY_MAX_NEUTRON]) self._indices[3] = 1 self._energy_filters = False @@ -933,14 +956,16 @@ class CMFDRun(object): else: self._albedo = np.array([1.,1.,1.,1.,1.,1.]) - # Get acceleration map + # Get acceleration map, otherwise set all regions to be accelerated if self._cmfd_mesh.map is not None: check_length('CMFD coremap', self._cmfd_mesh.map, np.product(self._indices[0:3])) self._coremap = np.array(self._cmfd_mesh.map).reshape(( \ self._indices[0], self._indices[1], \ self._indices[2])) - self._cmfd_coremap = True + else: + self._coremap = np.ones((self._indices[0], self._indices[1], \ + self._indices[2])) # Set number of batches where cmfd tallies should be reset if self._cmfd_reset is not None: @@ -954,9 +979,6 @@ class CMFDRun(object): self._create_cmfd_tally() def _allocate_cmfd(self): - # Determine number of batches through C API - n_batches = openmc.capi.settings.batches - # Extract spatial and energy indices nx = self._indices[0] ny = self._indices[1] @@ -964,57 +986,92 @@ class CMFDRun(object): ng = self._indices[3] # Allocate flux, cross sections and diffusion coefficient - self._flux = np.zeros((ng, nx, ny, nz)) - self._totalxs = np.zeros((ng, nx, ny, nz)) - self._p1scattxs = np.zeros((ng, nx, ny, nz)) - self._scattxs = np.zeros((ng, ng, nx, ny, nz)) - self._nfissxs = np.zeros((ng, ng, nx, ny, nz)) - self._diffcof = np.zeros((ng, nx, ny, nz)) + self._flux = np.zeros((nx, ny, nz, ng)) + self._totalxs = np.zeros((nx, ny, nz, ng)) + self._p1scattxs = np.zeros((nx, ny, nz, ng)) + self._scattxs = np.zeros((nx, ny, nz, ng, ng)) # Outgoing, incoming + self._nfissxs = np.zeros((nx, ny, nz, ng, ng)) # Outgoing, incoming + self._diffcof = np.zeros((nx, ny, nz, ng)) # Allocate dtilde and dhat - self._dtilde = np.zeros((6, ng, nx, ny, nz)) - self._dhat = np.zeros((6, ng, nx, ny, nz)) + self._dtilde = np.zeros((6, nx, ny, nz, ng)) + self._dhat = np.zeros((6, nx, ny, nz, ng)) # Allocate dimensions for each box (here for general case) self._hxyz = np.zeros((3, nx, ny, nz)) # Allocate surface currents - self._current = np.zeros((12, ng, nx, ny, nz)) + self._current = np.zeros((nx, ny, nz, ng, 12)) # Allocate source distributions - self._cmfd_src = np.zeros((ng, nx, ny, nz)) - self._openmc_src = np.zeros((ng, nx, ny, nz)) + self._cmfd_src = np.zeros((nx, ny, nz, ng)) + self._openmc_src = np.zeros((nx, ny, nz, ng)) # Allocate source weight modification variables - self._sourcecounts = np.zeros((ng, nx*ny*nz)) - self._weightfactors = np.ones((ng, nx, ny, nz)) + self._sourcecounts = np.zeros((nx*ny*nz, ng)) + self._weightfactors = np.ones((nx, ny, nz, ng)) # Allocate batchwise parameters - self._entropy = np.zeros((n_batches, )) - self._balance = np.zeros((n_batches, )) - self._src_cmp = np.zeros((n_batches, )) - self._dom = np.zeros((n_batches, )) - self._k_cmfd = np.zeros((n_batches, )) + self._entropy = [] + self._balance = [] + self._src_cmp = [] + self._dom = [] + self._k_cmfd = [] def _cmfd_init_batch(self): - if self._cmfd_begin == self._batch_num: + current_batch = openmc.capi.settings.current_batch + restart_run = openmc.capi.settings.restart_run + restart_batch = openmc.capi.settings.restart_batch + + if self._cmfd_begin == current_batch: self._cmfd_on = True - # TODO: Figure out how to tell if part of restart run - # if restart_run: - # return - # If this is a restart run and we are just replaying batches leave - # if (restart_run .and. current_batch <= restart_batch) return + # TODO: Test restart_batch + if restart_run and current_batch <= restart_batch: + return # Check to reset tallies - if self._n_cmfd_resets > 0 and self._batch_num in self._cmfd_reset: + if (self._n_cmfd_resets > 0 + and current_batch in self._cmfd_reset): self._cmfd_tally_reset() - def _cmfd_execute(self): - pass + def _execute_cmfd(self): + # CMFD single processor on master + if openmc.capi.settings.master: + + # TODO + #! Start cmfd timer + #call time_cmfd % start() + + # Create cmfd data from OpenMC tallies + self._set_up_cmfd() + + ''' + ! Call solver + call cmfd_solver_execute() + + ! Save k-effective + cmfd % k_cmfd(current_batch) = cmfd % keff + + ! check to perform adjoint on last batch + if (current_batch == n_batches .and. cmfd_run_adjoint) then + call cmfd_solver_execute(adjoint=.true.) + end if + + end if + + ! calculate fission source + call calc_fission_source() + + ! calculate weight factors + call cmfd_reweight(.true.) + + ! stop cmfd timer + if (master) call time_cmfd % stop() + ''' def _cmfd_tally_reset(self): - # TODO: How does write_message in error.F90 work? + # TODO: Print message with verbosity # Print message print(' CMFD tallies reset') @@ -1023,6 +1080,168 @@ class CMFDRun(object): for tally_id in self._cmfd_tally_ids: tallies[tally_id].reset() + def _set_up_cmfd(self): + # Check for core map and set it up + if (self._mat_dim == _CMFD_NOACCEL): + self._set_coremap() + + # Calculate all cross sections based on reaction rates from last batch + self._compute_xs() + ''' + ! Compute effective downscatter cross section + if (cmfd_downscatter) call compute_effective_downscatter() + + ! Check neutron balance + call neutron_balance() + + ! Calculate dtilde + call compute_dtilde() + + ! Calculate dhat + call compute_dhat() + ''' + + def _set_coremap(self): + self._mat_dim = np.sum(self._coremap) + self._coremap = np.where(self._coremap == 0, + _CMFD_NOACCEL, self._coremap) + + def _compute_xs(self): + # Extract energy indices + ng = self._indices[3] + + # Set flux object and source distribution all to zeros + self._flux.fill(0.) + self._openmc_src.fill(0.) + + # Reset keff_bal to zero + self._keff_bal = 0. + + # Get tallies in-memory + tallies = openmc.capi.tallies + + # Set conditional numpy array as boolean vector based on coremap + # Repeat each value for number of groups in problem + is_cmfd_accel = np.repeat(self._coremap.ravel() != _CMFD_NOACCEL, ng) + + # Get flux from CMFD tally 0 + tally_id = self._cmfd_tally_ids[0] + tally_results = tallies[tally_id].results[:,0,1] + flux = np.where(is_cmfd_accel, tally_results, 0.) + + # Detect zero flux, abort if located + if np.any(flux[is_cmfd_accel] < _TINY_BIT): + # Get index of zero flux in flux array + idx = np.argmax(np.where(is_cmfd_accel, flux, 1) < _TINY_BIT) + + # Convert scalar idx to index in flux matrix + mat_idx = np.unravel_index(idx, self._flux.shape) + + # Throw error message (one-based indexing) + # Index of group is flipped + err_message = 'Detected zero flux without coremap overlay' + \ + ' at mesh: (' + \ + ', '.join(str(i+1) for i in mat_idx[:-1]) + \ + ') in group ' + str(ng-mat_idx[-1]) + raise ValueError(err_message) + + # Store flux and reshape + # Flux is flipped in energy axis as tally results are given in reverse + # order of energy group + self._flux = np.flip(flux.reshape(self._flux.shape), axis=3) + + # Get total rr and convert to total xs from CMFD tally 0 + tally_results = tallies[tally_id].results[:,1,1] + totalxs = np.divide(tally_results, flux, \ + where=flux>0, out=np.zeros_like(tally_results)) + + # Store total xs and reshape + # Total xs is flipped in energy axis as tally results are given in + # reverse order of energy group + self._totalxs = np.flip(totalxs.reshape(self._totalxs.shape), axis=3) + + # Get scattering xs from CMFD tally 1 + # flux is repeated to account for extra dimensionality of scattering xs + tally_id = self._cmfd_tally_ids[1] + tally_results = tallies[tally_id].results[:,0,1] + scattxs = np.divide(tally_results, \ + np.repeat(flux, ng), \ + where=np.repeat(flux>0, ng), \ + out=np.zeros_like(tally_results)) + + # Store scattxs and reshape + # Scattering xs is flipped in both incoming and outgoing energy axes + # as tally results are given in reverse order of energy group + self._scattxs = np.flip(scattxs.reshape(self._scattxs.shape), axis=3) + self._scattxs = np.flip(self._scattxs.reshape(self._scattxs.shape), \ + axis=4) + + # Get nu-fission xs from CMFD tally 1 + # flux is repeated to account for extra dimensionality of nu-fission xs + tally_results = tallies[tally_id].results[:,1,1] + num_realizations = tallies[tally_id].num_realizations + nfissxs = np.divide(tally_results, \ + np.repeat(flux, ng), \ + where=np.repeat(flux>0, ng), \ + out=np.zeros_like(tally_results)) + + # Store nfissxs and reshape + # Nu-fission xs is flipped in both incoming and outgoing energy axes + # as tally results are given in reverse order of energy group + self._nfissxs = np.flip(nfissxs.reshape(self._nfissxs.shape), axis=3) + self._nfissxs = np.flip(self._nfissxs.reshape(self._nfissxs.shape), \ + axis=4) + + # Filter nu-fission tally results to compute openmc source distribution + tally_results = np.where(np.repeat(flux>0, ng), tally_results, \ + 0.) + + # Openmc source distribution is sum of nu-fission rr in outgoing energies + openmc_src = np.sum(tally_results.reshape(self._nfissxs.shape), + axis=3) + + # Store openmc_src + # Openmc source is flipped in energy axis as tally results are given + # in reverse order of energy group + self._openmc_src = np.flip(openmc_src, axis=3) + + # Compute k_eff from source distribution + self._keff_bal = np.sum(self._openmc_src) / num_realizations + + # Normalize openmc source distribution + self._openmc_src /= np.sum(self._openmc_src) * self._norm + + # Get surface currents from CMFD tally 2 + tally_id = self._cmfd_tally_ids[2] + tally_results = tallies[tally_id].results[:,0,1] + + # Filter tally results to include only accelerated regions + tally_results = np.where(np.repeat(flux>0, 12), tally_results, 0.) + + # Reshape and store current + # Current is flipped in energy axis as tally results are given in + # reverse order of energy group + self._current = np.flip(tally_results.reshape(self._current.shape), \ + axis=3) + + # Get p1 scatter xs from CMFD tally 3 + tally_id = self._cmfd_tally_ids[3] + tally_results = tallies[tally_id].results[:,0,1] + + # Reshape and extract only p1 data from tally results (no need for p0 data) + p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,1] + + # Store p1 scatter xs + # p1 scatter xs is flipped in energy axis as tally results are given in + # reverse order of energy group + self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, \ + where=self._flux>0, \ + out=np.zeros_like(p1scattrr)) + + # Calculate and store diffusion coefficient + self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * \ + (self._totalxs - self._p1scattxs)), 0.) + def _create_cmfd_tally(self): # Create Mesh object based on CMFDMesh, stored internally cmfd_mesh = openmc.capi.Mesh() @@ -1083,7 +1302,7 @@ class CMFDRun(object): tally.filters = [mesh_filter, energy_filter] else: tally.filters = [mesh_filter] - # Set scores for tally + # Set scores, type, and estimator for tally tally.scores = ['flux', 'total'] tally.type = 'volume' tally.estimator = 'analog' @@ -1095,7 +1314,7 @@ class CMFDRun(object): tally.filters = [mesh_filter, energy_filter, energyout_filter] else: tally.filters = [mesh_filter] - # Set scores for tally + # Set scores, type, and estimator for tally tally.scores = ['nu-scatter', 'nu-fission'] tally.type = 'volume' tally.estimator = 'analog' @@ -1107,7 +1326,7 @@ class CMFDRun(object): tally.filters = [meshsurface_filter, energy_filter] else: tally.filters = [meshsurface_filter] - # Set scores for tally + # Set scores, type, and estimator for tally tally.scores = ['current'] tally.type = 'mesh-surface' tally.estimator = 'analog' diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 index ce4825426b..38ad014288 100644 --- a/src/cmfd_data.F90 +++ b/src/cmfd_data.F90 @@ -254,7 +254,7 @@ contains ! Set the energy bin if needed if (energy_filters) then - filter_matches(i_filter_ein) % bins % data(1) = ng - h + 1 + filter_matches(i_filter_ein) % bins % data(1) = 12*(ng - h) + 1 end if score_index = 0 diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index 60bed6425a..d2d8eef023 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -65,7 +65,7 @@ module simulation_header ! ============================================================================ ! MISCELLANEOUS VARIABLES - integer :: restart_batch + integer(C_INT), bind(C, name='openmc_restart_batch') :: restart_batch ! Flag for enabling cell overlap checking during transport integer(8), allocatable :: overlap_check_cnt(:) From ec1199f689d69e009483d93d642b602ee7f9577c Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 20 Aug 2018 18:33:04 -0400 Subject: [PATCH 14/58] Fix merge conflicts --- src/api.F90 | 1 - src/tallies/tally_header.F90 | 62 +----------------------------------- 2 files changed, 1 insertion(+), 62 deletions(-) diff --git a/src/api.F90 b/src/api.F90 index 923b271762..df1719c711 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -102,7 +102,6 @@ module openmc_api public :: openmc_tally_set_nuclides public :: openmc_tally_set_scores public :: openmc_tally_set_type - public :: openmc_tally_update_type contains diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index f740b9689f..967d00f575 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -511,20 +511,6 @@ contains end if end function openmc_tally_get_active - function openmc_tally_get_estimator(index, estimator) result(err) bind(C) - ! Return the type of estimator of a tally - integer(C_INT32_T), value :: index - integer(C_INT32_T), intent(out) :: estimator - integer(C_INT) :: err - - if (index >= 1 .and. index <= size(tallies)) then - estimator = tallies(index) % obj % estimator - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg('Index in tallies array is out of bounds.') - end if - end function openmc_tally_get_estimator function openmc_tally_get_estimator(index, estimator) result(err) bind(C) ! Return the type of estimator of a tally @@ -664,22 +650,6 @@ contains end function openmc_tally_get_type - function openmc_tally_get_type(index, type) result(err) bind(C) - ! Return the type of a tally - integer(C_INT32_T), value :: index - integer(C_INT32_T), intent(out) :: type - integer(C_INT) :: err - - if (index >= 1 .and. index <= size(tallies)) then - type = tallies(index) % obj % type - err = 0 - else - err = E_OUT_OF_BOUNDS - call set_errmsg('Index in tallies array is out of bounds.') - end if - end function openmc_tally_get_type - - function openmc_tally_reset(index) result(err) bind(C) ! Reset tally results and number of realizations integer(C_INT32_T), intent(in), value :: index @@ -723,6 +693,7 @@ contains end if end function openmc_tally_results + function openmc_tally_set_estimator(index, estimator) result(err) bind(C) ! Set the type of estimator a tally integer(C_INT32_T), value, intent(in) :: index @@ -753,37 +724,6 @@ contains end if end function openmc_tally_set_estimator - function openmc_tally_set_estimator(index, estimator) result(err) bind(C) - ! Set the type of estimator a tally - integer(C_INT32_T), value, intent(in) :: index - character(kind=C_CHAR), intent(in) :: estimator(*) - integer(C_INT) :: err - - character(:), allocatable :: estimator_ - - ! Convert C string to Fortran string - estimator_ = to_f_string(estimator) - - err = 0 - if (index >= 1 .and. index <= size(tallies)) then - select case (estimator_) - case ('analog') - tallies(index) % obj % estimator = ESTIMATOR_ANALOG - case ('tracklength') - tallies(index) % obj % estimator = ESTIMATOR_TRACKLENGTH - case ('collision') - tallies(index) % obj % estimator = ESTIMATOR_COLLISION - case default - err = E_INVALID_ARGUMENT - call set_errmsg("Unknown tally estimator: " // trim(estimator_)) - end select - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in tally array is out of bounds.") - end if - end function openmc_tally_set_estimator - - function openmc_tally_set_filters(index, n, filter_indices) result(err) bind(C) ! Set the list of filters for a tally integer(C_INT32_T), value, intent(in) :: index From 7fa99816ef6b5b99d4d8db7f2c16b0ac5f73888c Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 20 Aug 2018 18:39:53 -0400 Subject: [PATCH 15/58] Some more merge conflict fixes --- include/openmc.h | 1 - openmc/capi/tally.py | 3 --- src/input_xml.F90 | 26 +++++++++++++------------- src/tallies/tally_header.F90 | 4 +++- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 406da2c175..a193c517ea 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -116,7 +116,6 @@ extern "C" { int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); - int openmc_tally_update_type(int32_t index, const char* type); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index eaf8caf202..44bf89415b 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -84,9 +84,6 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler _dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p] _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler -_dll.openmc_tally_update_type.argtypes = [c_int32, c_char_p] -_dll.openmc_tally_update_type.restype = c_int -_dll.openmc_tally_update_type.errcheck = _error_handler _SCORES = { diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d85ae853d4..a2b1a1fd93 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2171,7 +2171,7 @@ contains else call fatal_error("Could not find filter " & // trim(to_str(temp_filter(j))) // " specified on tally " & - // trim(to_str(tally_id))) + // trim(to_str(t % id))) end if ! Store the index of the filter @@ -2225,7 +2225,7 @@ contains if (.not. nuclide_dict % has(word)) then call fatal_error("Could not find the nuclide " & // trim(word) // " specified in tally " & - // trim(to_str(tally_id)) // " in any material.") + // trim(to_str(t % id)) // " in any material.") end if ! Set bin to index in nuclides array @@ -2535,7 +2535,7 @@ contains if (t % score_bins(j) == t % score_bins(k)) then call fatal_error("Duplicate score of type '" // trim(& reaction_name(t % score_bins(j))) // "' found in tally " & - // trim(to_str(tally_id))) + // trim(to_str(t % id))) end if end do end do @@ -2582,7 +2582,7 @@ contains end if else call fatal_error("No specified on tally " & - // trim(to_str(tally_id)) // ".") + // trim(to_str(t % id)) // ".") end if ! Check for a tally derivative. @@ -2605,7 +2605,7 @@ contains if (j == size(tally_derivs)) then call fatal_error("Could not find derivative " & // trim(to_str(t % deriv)) // " specified on tally " & - // trim(to_str(tally_id))) + // trim(to_str(t % id))) end if end do @@ -2613,7 +2613,7 @@ contains .or. tally_derivs(t % deriv) % variable == DIFF_TEMPERATURE) then if (any(t % nuclide_bins == -1)) then if (t % find_filter(FILTER_ENERGYOUT) > 0) then - call fatal_error("Error on tally " // trim(to_str(tally_id)) & + call fatal_error("Error on tally " // trim(to_str(t % id)) & // ": Cannot use a 'nuclide_density' or 'temperature' & &derivative on a tally with an outgoing energy filter and & &'total' nuclide rate. Instead, tally each nuclide in the & @@ -2690,7 +2690,7 @@ contains temp_str = to_lower(temp_str) else call fatal_error("Must specify trigger type for tally " // & - trim(to_str(tally_id)) // " in tally XML file.") + trim(to_str(t % id)) // " in tally XML file.") end if ! Get the convergence threshold for the trigger @@ -2698,7 +2698,7 @@ contains call get_node_value(node_trigger, "threshold", threshold) else call fatal_error("Must specify trigger threshold for tally " // & - trim(to_str(tally_id)) // " in tally XML file.") + trim(to_str(t % id)) // " in tally XML file.") end if ! Get list scores for this trigger @@ -2742,7 +2742,7 @@ contains t % triggers(trig_ind) % type = RELATIVE_ERROR case default call fatal_error("Unknown trigger type " // & - trim(temp_str) // " in tally " // trim(to_str(tally_id))) + trim(temp_str) // " in tally " // trim(to_str(t % id))) end select ! Store the trigger convergence threshold @@ -2763,7 +2763,7 @@ contains ! Check if an invalid score was set for the trigger if (t % triggers(trig_ind) % score_index == 0) then call fatal_error("The trigger score " // trim(score_name) // & - " is not set for tally " // trim(to_str(tally_id))) + " is not set for tally " // trim(to_str(t % id))) end if ! Store the trigger convergence threshold @@ -2779,7 +2779,7 @@ contains t % triggers(trig_ind) % type = RELATIVE_ERROR case default call fatal_error("Unknown trigger type " // trim(temp_str) // & - " in tally " // trim(to_str(tally_id))) + " in tally " // trim(to_str(t % id))) end select ! Increment the overall trigger index @@ -2811,7 +2811,7 @@ contains ! tally needs post-collision information if (t % estimator == ESTIMATOR_ANALOG) then call fatal_error("Cannot use track-length estimator for tally " & - // to_str(tally_id)) + // to_str(t % id)) end if ! Set estimator to track-length estimator @@ -2822,7 +2822,7 @@ contains ! tally needs post-collision information if (t % estimator == ESTIMATOR_ANALOG) then call fatal_error("Cannot use collision estimator for tally " & - // to_str(tally_id)) + // to_str(t % id)) end if ! Set estimator to collision estimator diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index 967d00f575..b2e7f7ce53 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -274,6 +274,7 @@ contains end subroutine tally_allocate_results + function tally_set_filters(this, filter_indices) result(err) class(TallyObject), intent(inout) :: this integer(C_INT32_T), intent(in) :: filter_indices(:) @@ -634,6 +635,7 @@ contains end if end function openmc_tally_get_scores + function openmc_tally_get_type(index, type) result(err) bind(C) ! Return the type of a tally integer(C_INT32_T), value :: index @@ -715,7 +717,7 @@ contains case ('collision') tallies(index) % obj % estimator = ESTIMATOR_COLLISION case default - err = E_UNASSIGNED + err = E_INVALID_ARGUMENT call set_errmsg("Unknown tally estimator: " // trim(estimator_)) end select else From d55fc1e9ce29f71dc30ae1cc2ac4a2e30764e658 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 22 Aug 2018 02:05:41 -0400 Subject: [PATCH 16/58] Replicate cmfd_data.F90 with Python, still need to vectorize compute_dhat and compute_dtilde --- openmc/cmfd.py | 392 ++++++++++++++++++++++++++++++++--- src/tallies/tally_header.F90 | 1 + 2 files changed, 364 insertions(+), 29 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 51af386343..22b24fecb2 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -41,9 +41,12 @@ _CMFD_NOACCEL = 99999 # Constant to represent a zero flux "albedo" _ZERO_FLUX = 999.0 -# Constant for writing out no residual -_CMFD_NORES = 99999.0 - +# Map that returns index of current direction in current matrix +_CURRENTS = { + 'out_left' : 0, 'in_left' : 1, 'out_right': 2, 'in_right': 3, + 'out_back' : 4, 'in_back' : 5, 'out_front': 6, 'in_front': 7, + 'out_bottom': 8, 'in_bottom': 9, 'out_top' : 10, 'in_top' : 11 +} class CMFDMesh(object): """A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD) @@ -619,7 +622,7 @@ class CMFDRun(object): diffcof dtilde dhat - hxyz + hxyz: mesh width current cmfd_src openmc_src @@ -636,6 +639,8 @@ class CMFDRun(object): TODO: Put descriptions for all methods in CMFDRun TODO All timing variables TODO Get rid of CMFD constants + TODO Get rid of unused variables defined in init + TODO Make sure all self variables defined in init """ @@ -675,6 +680,7 @@ class CMFDRun(object): self._cmfd_on = False self._mat_dim = _CMFD_NOACCEL self._keff_bal = None + self._cmfd_adjoint_type = None # Numpy arrays used to build CMFD matrices self._flux = None @@ -696,6 +702,7 @@ class CMFDRun(object): self._src_cmp = None self._dom = None self._k_cmfd = None + self._resnb = None @property def cmfd_begin(self): @@ -989,16 +996,16 @@ class CMFDRun(object): self._flux = np.zeros((nx, ny, nz, ng)) self._totalxs = np.zeros((nx, ny, nz, ng)) self._p1scattxs = np.zeros((nx, ny, nz, ng)) - self._scattxs = np.zeros((nx, ny, nz, ng, ng)) # Outgoing, incoming - self._nfissxs = np.zeros((nx, ny, nz, ng, ng)) # Outgoing, incoming + self._scattxs = np.zeros((nx, ny, nz, ng, ng)) # Incoming, outgoing + self._nfissxs = np.zeros((nx, ny, nz, ng, ng)) # Incoming, outgoing self._diffcof = np.zeros((nx, ny, nz, ng)) # Allocate dtilde and dhat - self._dtilde = np.zeros((6, nx, ny, nz, ng)) - self._dhat = np.zeros((6, nx, ny, nz, ng)) + self._dtilde = np.zeros((nx, ny, nz, ng, 6)) + self._dhat = np.zeros((nx, ny, nz, ng, 6)) - # Allocate dimensions for each box (here for general case) - self._hxyz = np.zeros((3, nx, ny, nz)) + # Allocate dimensions for each box (assume fixed mesh dimensions) + self._hxyz = np.zeros((3)) # Allocate surface currents self._current = np.zeros((nx, ny, nz, ng, 12)) @@ -1046,10 +1053,9 @@ class CMFDRun(object): # Create cmfd data from OpenMC tallies self._set_up_cmfd() - ''' - ! Call solver - call cmfd_solver_execute() - + # Call solver + self._cmfd_solver_execute() + ''' ! Save k-effective cmfd % k_cmfd(current_batch) = cmfd % keff @@ -1083,28 +1089,117 @@ class CMFDRun(object): def _set_up_cmfd(self): # Check for core map and set it up if (self._mat_dim == _CMFD_NOACCEL): + # TODO: Don't reshape coremap before this point, reshape in set_coremap self._set_coremap() # Calculate all cross sections based on reaction rates from last batch self._compute_xs() + + # Compute effective downscatter cross section + if (self._cmfd_downscatter): self._compute_effective_downscatter() + + # Check neutron balance + self._neutron_balance() + + # Calculate dtilde + self._compute_dtilde() + + # Calculate dhat + self._compute_dhat() + + def _cmfd_solver_execute(self, adjoint=False): + # TODO Check for physical adjoint + physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' + + # TODO Start timer for build + # call time_cmfdbuild % start() + + # Initialize matrices and vectors + self._init_data(physical_adjoint) + + # TODO Check for mathematical adjoint calculation + #if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & + # call compute_adjoint() + + # TODO Stop timer for build + # call time_cmfdbuild % stop() + + # TODO Begin power iteration + # call time_cmfdsolve % start() + self._execute_power_iter() + # call time_cmfdsolve % stop() + + # Extract results + self._extract_results() + + def _init_data(self, adjoint): + # Set up matrices + + call init_loss_matrix(loss) + call init_prod_matrix(prod) ''' - ! Compute effective downscatter cross section - if (cmfd_downscatter) call compute_effective_downscatter() + # Get problem size + n = loss % n - ! Check neutron balance - call neutron_balance() + # Set up flux vectors + call phi_n % create(n) + call phi_o % create(n) - ! Calculate dtilde - call compute_dtilde() + # Set up source vectors + call s_n % create(n) + call s_o % create(n) + call serr_v % create(n) - ! Calculate dhat - call compute_dhat() + # Set initial guess + guess = ONE + phi_n % val = guess + phi_o % val = guess + k_n = keff + k_o = k_n + dw = cmfd_shift + k_s = k_o + dw + k_ln = ONE/(ONE/k_n - ONE/k_s) + k_lo = k_ln + + # Fill in loss matrix + call build_loss_matrix(loss, adjoint=adjoint) + + # Fill in production matrix + call build_prod_matrix(prod, adjoint=adjoint) + + # Finalize setup of CSR matrices + call loss % assemble() + call prod % assemble() + if (cmfd_write_matrices) then + call loss % write('loss.dat') + call prod % write('prod.dat') + end if + + # Set norms to 0 + norm_n = ZERO + norm_o = ZERO + + # Set tolerances + ktol = cmfd_ktol + stol = cmfd_stol ''' + def _execute_power_iter(self): + pass + + def _extract_results(self): + pass + def _set_coremap(self): self._mat_dim = np.sum(self._coremap) - self._coremap = np.where(self._coremap == 0, - _CMFD_NOACCEL, self._coremap) + + # Create a temporary array that unravels coremap and aggregates + # cumulative sum over accelerated regions + temp = np.where(self._coremap.ravel()==0, _CMFD_NOACCEL, + np.cumsum(self._coremap.ravel())-1) + + # Reshape coremap back to correct shape + self._coremap = temp.reshape(self._coremap.shape) def _compute_xs(self): # Extract energy indices @@ -1114,6 +1209,9 @@ class CMFDRun(object): self._flux.fill(0.) self._openmc_src.fill(0.) + # Set mesh widths + self._hxyz = openmc.capi.meshes[self._cmfd_mesh_id].width + # Reset keff_bal to zero self._keff_bal = 0. @@ -1196,7 +1294,7 @@ class CMFDRun(object): tally_results = np.where(np.repeat(flux>0, ng), tally_results, \ 0.) - # Openmc source distribution is sum of nu-fission rr in outgoing energies + # Openmc source distribution is sum of nu-fission rr in incoming energies openmc_src = np.sum(tally_results.reshape(self._nfissxs.shape), axis=3) @@ -1239,9 +1337,245 @@ class CMFDRun(object): out=np.zeros_like(p1scattrr)) # Calculate and store diffusion coefficient - self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * \ + self._diffcof = np.where(self._flux>0, 1.0 / (3.0 * \ (self._totalxs - self._p1scattxs)), 0.) + def _compute_effective_downscatter(self): + # Extract energy index + ng = self._indices[3] + + # Return if not two groups + if ng != 2: + return + + # Extract cross sections and flux for each group + flux1 = self._flux[:,:,:,0] + flux2 = self._flux[:,:,:,1] + sigt1 = self._totalxs[:,:,:,0] + sigt2 = self._totalxs[:,:,:,1] + # First energy index is incoming, second is outgoing + sigs11 = self._scattxs[:,:,:,0,0] + sigs21 = self._scattxs[:,:,:,1,0] + sigs12 = self._scattxs[:,:,:,0,1] + sigs22 = self._scattxs[:,:,:,1,1] + + # Compute absorption xs + siga1 = sigt1 - sigs11 - sigs12 + siga2 = sigt2 - sigs22 - sigs21 + + # Compute effective downscatter XS + sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, where=flux1>0, + out=np.zeros_like(flux2)) + + # Recompute total cross sections and record + self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff + self._totalxs[:,:,:,1] = siga2 + sigs22 + + # Record effective dowmscatter xs + self._scattxs[:,:,:,0,1] = sigs12_eff + + # Zero out upscatter cross section + self._scattxs[:,:,:,1,0] = 0.0 + + def _neutron_balance(self): + # Extract energy indices + ng = self._indices[3] + + # Get openmc k-effective + keff = openmc.capi.keff()[0] + + leakage = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ + self._current[:,:,:,:,_CURRENTS['in_right']]) - \ + (self._current[:,:,:,:,_CURRENTS['in_left']] - \ + self._current[:,:,:,:,_CURRENTS['out_left']])) + \ + ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ + self._current[:,:,:,:,_CURRENTS['in_front']]) - \ + (self._current[:,:,:,:,_CURRENTS['in_back']] - \ + self._current[:,:,:,:,_CURRENTS['out_back']])) + \ + ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ + self._current[:,:,:,:,_CURRENTS['in_top']]) - \ + (self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ + self._current[:,:,:,:,_CURRENTS['out_bottom']])) + + # Compute total rr + interactions = self._totalxs * self._flux + + # Compute scattering rr by broadcasting flux in outgoing energy and + # summing over incoming energy + scattering = np.sum(self._scattxs * \ + np.repeat(self._flux[:,:,:,:,np.newaxis], ng, axis=4), axis=3) + + # Compute fission rr by broadcasting flux in outgoing energy and + # summing over incoming energy + fission = np.sum(self._nfissxs * \ + np.repeat(self._flux[:,:,:,:,np.newaxis], ng, axis=4), axis=3) + + # Compute residual + res = leakage + interactions - scattering - (1.0 / keff) * fission + + # Normalize res by flux and bank res + self._resnb = np.divide(res, self._flux, where=self._flux>0) + + # Calculate RMS and record for this batch + self._balance.append(np.sqrt( + np.sum(np.multiply(self._resnb, self._resnb)) / \ + np.count_nonzero(self._resnb))) + + def _compute_dtilde(self): + # Get maximum of spatial and group indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + + # Create single vector of these indices for boundary calculation + nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) + + # Get boundary condition information + albedo = self._albedo + + # Loop over group and spatial indices + for k in range(nz): + for j in range(ny): + for i in range(nx): + for g in range(ng): + # Cycle if non-accelration region + if self._coremap[i,j,k] == _CMFD_NOACCEL: + continue + + # Get cell data + cell_dc = self._diffcof[i,j,k,g] + cell_hxyz = self._hxyz + + # Setup of vector to identify boundary conditions + bound = np.repeat([i,j,k], 2) + + # Begin loop around sides of cell for leakage + for l in range(6): + xyz_idx = int(l/2) # x=0, y=1, z=2 + dir_idx = l % 2 # -=0, +=1 + + # Check if at boundary + if bound[l] == nxyz[xyz_idx, dir_idx]: + # Compute dtilde with albedo boundary condition + dtilde = (2*cell_dc*(1-albedo[l]))/ \ + (4*cell_dc*(1+albedo[l]) + \ + (1-albedo[l])*cell_hxyz[xyz_idx]) + + # Check for zero flux albedo + if abs(albedo[l] - _ZERO_FLUX) < _TINY_BIT: + dtilde = 2*cell_dc / cell_hxyz[xyz_idx] + + else: # Not at a boundary + shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 + + # Compute neighboring cell indices + neig_idx = [i,j,k] # Begin with i,j,k + neig_idx[xyz_idx] += shift_idx + + # Get neighbor cell data + neig_dc = self._diffcof[tuple(neig_idx) + (g,)] + + # Check for fuel-reflector interface + if (self._coremap[tuple(neig_idx)] == + _CMFD_NOACCEL): + # Get albedo + ref_albedo = self._get_reflector_albedo(l,g,i,j,k) + dtilde = (2*cell_dc*(1-ref_albedo))/(4*cell_dc*(1+ \ + ref_albedo)+(1-ref_albedo)*cell_hxyz[xyz_idx]) + + else: # Not next to a reflector + # Compute dtilde to neighbor cell + dtilde = (2*cell_dc*neig_dc)/(cell_hxyz[xyz_idx]*cell_dc + \ + cell_hxyz[xyz_idx]*neig_dc) + + # Record dtilde + self._dtilde[i, j, k, g, l] = dtilde + + def _compute_dhat(self): + #TODO compute dhat and dtilde for general case with hxyz (just define as repeated but use in formulas) + # Get maximum of spatial and group indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + + # Create single vector of these indices for boundary calculation + nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) + + # Loop over group and spatial indices + for k in range(nz): + for j in range(ny): + for i in range(nx): + for g in range(ng): + # Cycle if non-accelration region + if self._coremap[i,j,k] == _CMFD_NOACCEL: + continue + + # Get cell data + cell_dtilde = self._dtilde[i,j,k,g,:] + cell_flux = self._flux[i,j,k,g]/np.product(self._hxyz) + current = self._current[i,j,k,g,:] + + # Setup of vector to identify boundary conditions + bound = np.repeat([i,j,k], 2) + + # Begin loop around sides of cell for leakage + for l in range(6): + xyz_idx = int(l/2) # x=0, y=1, z=2 + dir_idx = l % 2 # -=0, +=1 + shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 + + # Calculate net current on l face (divided by surf area) + net_current = shift_idx*(current[2*l] - current[2*l+1]) / \ + np.product(self._hxyz) * self._hxyz[xyz_idx] + + # Check if at boundary + if bound[l] == nxyz[xyz_idx, dir_idx]: + # Compute dhat + dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ + cell_flux + #print(dhat, i, j, k, g, l) + else: # Not at a boundary + # Compute neighboring cell indices + neig_idx = [i,j,k] # Begin with i,j,k + neig_idx[xyz_idx] += shift_idx + + # Get neigbor flux + neig_flux = self._flux[tuple(neig_idx)+(g,)] / \ + np.product(self._hxyz) + + # Check for fuel-reflector interface + if (self._coremap[tuple(neig_idx)] == + _CMFD_NOACCEL): + # Compute dhat + dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ + cell_flux + else: # not a fuel-reflector interface + # Compute dhat + dhat = (net_current + shift_idx*cell_dtilde[l]* \ + (neig_flux - cell_flux))/(neig_flux + cell_flux) + + # Record dhat + self._dhat[i, j, k, g, l] = dhat + + # check for dhat reset + if self._dhat_reset: + self._dhat[i, j, k, g, l] = 0.0 + + sys.exit() + + + def _get_reflector_albedo(self, l, g, i, j, k): + # Get partial currents from object + current = self._current[i,j,k,g,:] + + # Calculate albedo + if current[2*l] < 1.0e-10: + return 1.0 + else: + return current[2*l+1]/current[2*l] + def _create_cmfd_tally(self): # Create Mesh object based on CMFDMesh, stored internally cmfd_mesh = openmc.capi.Mesh() @@ -1308,7 +1642,7 @@ class CMFDRun(object): tally.estimator = 'analog' # Set attributes of CMFD neutron production tally - if i == 1: + elif i == 1: # Set filters for tally if self._energy_filters: tally.filters = [mesh_filter, energy_filter, energyout_filter] @@ -1320,7 +1654,7 @@ class CMFDRun(object): tally.estimator = 'analog' # Set attributes of CMFD surface current tally - if i == 2: + elif i == 2: # Set filters for tally if self._energy_filters: tally.filters = [meshsurface_filter, energy_filter] @@ -1332,7 +1666,7 @@ class CMFDRun(object): tally.estimator = 'analog' # Set attributes of CMFD P1 scatter tally - if i == 3: + elif i == 3: # Set filters for tally if self._energy_filters: tally.filters = [mesh_filter, legendre_filter, energy_filter] diff --git a/src/tallies/tally_header.F90 b/src/tallies/tally_header.F90 index b2e7f7ce53..36efbe3bef 100644 --- a/src/tallies/tally_header.F90 +++ b/src/tallies/tally_header.F90 @@ -726,6 +726,7 @@ contains end if end function openmc_tally_set_estimator + function openmc_tally_set_filters(index, n, filter_indices) result(err) bind(C) ! Set the list of filters for a tally integer(C_INT32_T), value, intent(in) :: index From e14826f7ffc9bb393153760aecb1e089e9da0401 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 22 Aug 2018 23:42:53 -0400 Subject: [PATCH 17/58] Build production and loss matrices (not vectorized), print matrices at each batch in Fortran --- openmc/cmfd.py | 262 ++++++++++++++++++++++++++++++++++++-------- src/cmfd_solver.F90 | 6 +- 2 files changed, 217 insertions(+), 51 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 22b24fecb2..e29a759350 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -74,6 +74,7 @@ class CMFDMesh(object): boundary conditions. They are listed in the following order: -x +x -y +y -z +z. map : Iterable of int + TODO: EDIT THIS DESCRIPTION WITH CORRECT VALUES An optional acceleration map can be specified to overlay on the coarse mesh spatial grid. If this option is used, a ``1`` is used for a non-accelerated region and a ``2`` is used for an accelerated region. @@ -182,6 +183,7 @@ class CMFDMesh(object): check_value('CMFD mesh map', m, [0, 1]) self._map = meshmap + # REMOVE def _get_xml_element(self): element = ET.Element("mesh") @@ -479,87 +481,87 @@ class CMFD(object): check_type('CMFD write matrices', write_matrices, bool) self._write_matrices = write_matrices - # TODO: Remove + def _create_begin_subelement(self): if self._begin is not None: element = ET.SubElement(self._cmfd_file, "begin") element.text = str(self._begin) - # TODO: Remove + def _create_dhat_reset_subelement(self): if self._dhat_reset is not None: element = ET.SubElement(self._cmfd_file, "dhat_reset") element.text = str(self._dhat_reset).lower() - # TODO: Remove + def _create_display_subelement(self): if self._display is not None: element = ET.SubElement(self._cmfd_file, "display") element.text = str(self._display) - # TODO: Remove + def _create_downscatter_subelement(self): if self._downscatter is not None: element = ET.SubElement(self._cmfd_file, "downscatter") element.text = str(self._downscatter).lower() - # TODO: Remove + def _create_feedback_subelement(self): if self._feedback is not None: element = ET.SubElement(self._cmfd_file, "feeback") element.text = str(self._feedback).lower() - # TODO: Remove + def _create_gauss_seidel_tolerance_subelement(self): if self._gauss_seidel_tolerance is not None: element = ET.SubElement(self._cmfd_file, "gauss_seidel_tolerance") element.text = ' '.join(map(str, self._gauss_seidel_tolerance)) - # TODO: Remove + def _create_ktol_subelement(self): if self._ktol is not None: element = ET.SubElement(self._ktol, "ktol") element.text = str(self._ktol) - # TODO: Remove + def _create_mesh_subelement(self): if self._cmfd_mesh is not None: xml_element = self._cmfd_mesh._get_xml_element() self._cmfd_file.append(xml_element) - # TODO: Remove + def _create_norm_subelement(self): if self._norm is not None: element = ET.SubElement(self._cmfd_file, "norm") element.text = str(self._norm) - # TODO: Remove + def _create_power_monitor_subelement(self): if self._power_monitor is not None: element = ET.SubElement(self._cmfd_file, "power_monitor") element.text = str(self._power_monitor).lower() - # TODO: Remove + def _create_run_adjoint_subelement(self): if self._run_adjoint is not None: element = ET.SubElement(self._cmfd_file, "run_adjoint") element.text = str(self._run_adjoint).lower() - # TODO: Remove + def _create_shift_subelement(self): if self._shift is not None: element = ET.SubElement(self._shift, "shift") element.text = str(self._shift) - # TODO: Remove + def _create_spectral_subelement(self): if self._spectral is not None: element = ET.SubElement(self._spectral, "spectral") element.text = str(self._spectral) - # TODO: Remove + def _create_stol_subelement(self): if self._stol is not None: element = ET.SubElement(self._stol, "stol") element.text = str(self._stol) - # TODO: Remove + def _create_tally_reset_subelement(self): if self._tally_reset is not None: element = ET.SubElement(self._tally_reset, "tally_reset") element.text = ' '.join(map(str, self._tally_reset)) - # TODO: Remove + def _create_write_matrices_subelement(self): if self._write_matrices is not None: element = ET.SubElement(self._cmfd_file, "write_matrices") element.text = str(self._write_matrices).lower() - # TODO: Remove + def export_to_xml(self): """Create a cmfd.xml file using the class data that can be used for an OpenMC simulation. @@ -641,6 +643,8 @@ class CMFDRun(object): TODO Get rid of CMFD constants TODO Get rid of unused variables defined in init TODO Make sure all self variables defined in init + TODO Clean up logic for adjoint, understand what different adjoint types are doing + TODO Check to make sure no compatibility issues with numpy arrays for input variables """ @@ -1115,28 +1119,206 @@ class CMFDRun(object): # call time_cmfdbuild % start() # Initialize matrices and vectors - self._init_data(physical_adjoint) + loss, prod = self._build_matrices(physical_adjoint) # TODO Check for mathematical adjoint calculation - #if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & - # call compute_adjoint() + #if adjoint_calc and self._cmfd_adjoint_type == 'math': + # self._compute_adjoint() # TODO Stop timer for build # call time_cmfdbuild % stop() # TODO Begin power iteration # call time_cmfdsolve % start() - self._execute_power_iter() + phi, keff, dom = self._execute_power_iter(loss, prod) # call time_cmfdsolve % stop() - # Extract results - self._extract_results() + # TODO Save results + #if self._ - def _init_data(self, adjoint): + def _build_matrices(self, adjoint): # Set up matrices - - call init_loss_matrix(loss) - call init_prod_matrix(prod) + + loss = self._build_loss_matrix(adjoint) + prod = self._build_prod_matrix(adjoint) + ''' + # TODO Write matrices + if (cmfd_write_matrices) then + call loss % write('loss.dat') + call prod % write('prod.dat') + end if + ''' + + return loss, prod + + def _build_loss_matrix(self, adjoint): + # Extract spatial and energy indices and define matrix dimension + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + n = self._mat_dim*ng + + # Allocate matrix + loss = np.zeros((n, n)) + + # Create single vector of these indices for boundary calculation + nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) + + # Allocate leakage coefficients in front of cell flux + jo = np.zeros((6,)) + + for irow in range(n): + i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) + + # Retrieve cell data + totxs = self._totalxs[i,j,k,g] + scattxsgg = self._scattxs[i,j,k,g,g] + dtilde = self._dtilde[i,j,k,g,:] + hxyz = self._hxyz + dhat = self._dhat[i,j,k,g,:] + + # Create boundary vector + bound = np.repeat([i,j,k], 2) + + # Begin loop over leakages + for l in range(6): + # Define (x,y,z) and (-,+) indices + xyz_idx = int(l/2) # x=0, y=1, z=2 + dir_idx = l % 2 # -=0, +=1 + + # Calculate spatial indices of neighbor + neig_idx = [i,j,k] # Begin with i,j,k + shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 + neig_idx[xyz_idx] += shift_idx + + # Check for global boundary + if bound[l] != nxyz[xyz_idx, dir_idx]: + + # Check that neighbor is not reflector + if self._coremap[tuple(neig_idx)] != _CMFD_NOACCEL: + # Compute leakage coefficient for neighbor + jn = -1.0 * dtilde[l] + shift_idx*dhat[l] + + # Get neighbor matrix index + neig_mat_idx = self._indices_to_matrix(neig_idx[0], \ + neig_idx[1], neig_idx[2], g, ng) + # Compute value and record to bank + val = jn/hxyz[xyz_idx] + loss[irow, neig_mat_idx] = val + + # Compute leakage coefficient for target + jo[l] = shift_idx*dtilde[l] + dhat[l] + + # Calculate net leakage coefficient for target + jnet = (jo[1] - jo[0])/hxyz[0] + (jo[3] - jo[2])/hxyz[1] + \ + (jo[5] - jo[4])/hxyz[2] + + # Calculate loss of neutrons + val = jnet + totxs - scattxsgg + loss[irow, irow] = val + + for h in range(ng): + # Cycle though if h=g, value already banked in removal xs + if h == g: + continue + + # Get neighbor matrix index + scatt_mat_idx = self._indices_to_matrix(i,j,k, h, ng) + + # TODO Check for adjoint + #if (adjoint_calc) then + #! Get scattering macro xs, transposed! + #scattxshg = cmfd%scattxs(g, h, i, j, k) + #else + # Get scattering macro xs + scattxshg = self._scattxs[i, j, k, h, g] + #end if + + # Negate the scattering xs + val = -1.0*scattxshg + + # Record value in matrix + loss[irow, scatt_mat_idx] = val + + ''' + print("Loss matrix:") + for i in range(loss.shape[0]): + print_str = "" + for j in range(loss.shape[1]): + if loss[i,j] != 0.0: + print_str += "%.3g\t" % loss[i, j] + else: + print_str += "%.3f\t" % loss[i, j] + print(print_str) + ''' + + return loss + + def _build_prod_matrix(self, adjoint): + # Extract spatial and energy indices and define matrix dimension + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + n = self._mat_dim*ng + + # Allocate matrix + prod = np.zeros((n, n)) + + for irow in range(n): + i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) + + # Check if at a reflector + if self._coremap[i,j,k] == _CMFD_NOACCEL: + continue + + # loop around all other groups + for h in range(ng): + hmat_idx = self._indices_to_matrix(i,j,k, h, ng) + # TODO check for adjoint and bank val + #if (adjoint_calc) then + # ! get nu-fission cross section from cell + # nfissxs = cmfd%nfissxs(g,h,i,j,k) + #else + # get nu-fission cross section from cell + nfissxs = self._nfissxs[i, j, k, h, g] + + # set as value to be recorded + val = nfissxs + + # record value in matrix + prod[irow, hmat_idx] = val + + ''' + print("Prod matrix:") + for i in range(prod.shape[0]): + print_str = "" + for j in range(prod.shape[1]): + if prod[i,j] != 0.0: + print_str += "%.3g\t" % prod[i, j] + else: + print_str += "%.3f\t" % prod[i, j] + print(print_str) + return prod + ''' + sys.exit() + + def _matrix_to_indices(self, irow, nx, ny, nz, ng): + # Get indices from coremap + g = irow % ng + spatial_idx = np.where(self._coremap == int(irow/ng)) + i = spatial_idx[0][0] + j = spatial_idx[1][0] + k = spatial_idx[2][0] + return i, j, k, g + + def _indices_to_matrix(self, i, j, k, g, ng): + matidx = ng*(self._coremap[i,j,k]) + g + return matidx + + def _execute_power_iter(self): + pass ''' # Get problem size n = loss % n @@ -1161,20 +1343,6 @@ class CMFDRun(object): k_ln = ONE/(ONE/k_n - ONE/k_s) k_lo = k_ln - # Fill in loss matrix - call build_loss_matrix(loss, adjoint=adjoint) - - # Fill in production matrix - call build_prod_matrix(prod, adjoint=adjoint) - - # Finalize setup of CSR matrices - call loss % assemble() - call prod % assemble() - if (cmfd_write_matrices) then - call loss % write('loss.dat') - call prod % write('prod.dat') - end if - # Set norms to 0 norm_n = ZERO norm_o = ZERO @@ -1184,12 +1352,6 @@ class CMFDRun(object): stol = cmfd_stol ''' - def _execute_power_iter(self): - pass - - def _extract_results(self): - pass - def _set_coremap(self): self._mat_dim = np.sum(self._coremap) @@ -1563,7 +1725,11 @@ class CMFDRun(object): if self._dhat_reset: self._dhat[i, j, k, g, l] = 0.0 - sys.exit() + # Write that dhats are zero + if self._dhat_reset: + # TODO: Print message with verbosity 8 + print(' Dhats reset to zero') + def _get_reflector_albedo(self, l, g, i, j, k): diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index 99d482480a..270ddfab42 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -101,7 +101,7 @@ contains use constants, only: ONE, ZERO use cmfd_header, only: cmfd_shift, cmfd_ktol, cmfd_stol, cmfd_write_matrices - use simulation_header, only: keff + use simulation_header, only: keff, current_batch logical, intent(in) :: adjoint @@ -146,8 +146,8 @@ contains call loss % assemble() call prod % assemble() if (cmfd_write_matrices) then - call loss % write('loss.dat') - call prod % write('prod.dat') + call loss % write('loss' // trim(to_str(current_batch)) // '.dat') + call prod % write('prod' // trim(to_str(current_batch)) // '.dat') end if ! Set norms to 0 From 07f2b3ac3d0abb3c4c1ae20b6a1aaf5ac2822bac Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 23 Aug 2018 23:35:29 -0400 Subject: [PATCH 18/58] Implement CMFD solver, omp_num_threads now command line arg for CMFDRun.run() --- .../cmfd-feed/capi/run_openmc_cmfd.py | 4 +- openmc/cmfd.py | 236 +++++++++++------- 2 files changed, 146 insertions(+), 94 deletions(-) diff --git a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py index 73d8e9a598..d9c3953eda 100644 --- a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py +++ b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py @@ -1,5 +1,8 @@ import openmc import numpy as np +from mpi4py import MPI + +comm = MPI.COMM_WORLD # Initialize CMFD Mesh cmfd_mesh = openmc.CMFDMesh() @@ -19,7 +22,6 @@ cmfd_run.cmfd_mesh = cmfd_mesh cmfd_run.cmfd_begin = 5 cmfd_run.cmfd_display = 'dominance' cmfd_run.cmfd_feedback = True -cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] # Run CMFD cmfd_run.run() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index e29a759350..6a136a90a5 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -10,17 +10,19 @@ References """ -# TODO: Check to make sure no redundant import statements from collections.abc import Iterable from numbers import Real, Integral -from xml.etree import ElementTree as ET -import sys +from xml.etree import ElementTree as ET # TODO Remove +import sys # TODO Remove import numpy as np -import openmc.capi +from scipy import sparse -from openmc.clean_xml import clean_xml_indentation +import openmc.capi +from openmc.clean_xml import clean_xml_indentation # TODO Remove from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) +from openmc.exceptions import OpenMCError + """ @@ -606,8 +608,6 @@ class CMFDRun(object): egrid: energy grid used for CMFD acceleration albedo: Albedo for global boundary conditions, taken from CMFD mesh. Set to [1,1,1,1,1,1] if not specified by user n_cmfd_resets: Number of elements in tally_reset, list that stores batches where CMFD tallies should be reset - cmfd_atoli: Absolute GS tolerance, set by gauss_seidel_tolerance - cmfd_rtoli: Relative GS tolerance, set by gauss_seidel_tolerance cmfd_mesh_id: Mesh id of openmc.capi.Mesh object that corresponds to the CMFD mesh cmfd_filter_ids: list of ids corresponding to CMFD filters (details:) cmfd_tally_ids: list of ids corresponding to CMFD tallies (details:) @@ -645,6 +645,7 @@ class CMFDRun(object): TODO Make sure all self variables defined in init TODO Clean up logic for adjoint, understand what different adjoint types are doing TODO Check to make sure no compatibility issues with numpy arrays for input variables + TODO Create write_vector function in cmfd_solver.F90 """ @@ -657,14 +658,12 @@ class CMFDRun(object): self._cmfd_display = 'balance' self._cmfd_downscatter = False self._cmfd_feedback = False - self._gauss_seidel_tolerance = [1.e-10, 1.e-5] self._cmfd_ktol = 1.e-8 self._cmfd_mesh = None self._norm = 1. self._cmfd_power_monitor = False self._cmfd_run_adjoint = False - self._cmfd_shift = 1.e-6 - self._cmfd_spectral = 0. + self._cmfd_shift = 1.e6 self._cmfd_stol = 1.e-8 self._cmfd_reset = [] self._cmfd_write_matrices = False @@ -675,8 +674,6 @@ class CMFDRun(object): self._albedo = None self._coremap = None self._n_cmfd_resets = 0 - self._cmfd_atoli = None - self._cmfd_rtoli = None self._cmfd_mesh_id = None self._cmfd_filter_ids = None self._cmfd_tally_ids = None @@ -685,6 +682,10 @@ class CMFDRun(object): self._mat_dim = _CMFD_NOACCEL self._keff_bal = None self._cmfd_adjoint_type = None + self._keff = None + self._adj_keff = None + self._phi = None + self._adj_phi = None # Numpy arrays used to build CMFD matrices self._flux = None @@ -728,10 +729,6 @@ class CMFDRun(object): def cmfd_feedback(self): return self._cmfd_feedback - @property - def gauss_seidel_tolerance(self): - return self._gauss_seidel_tolerance - @property def cmfd_ktol(self): return self._cmfd_ktol @@ -756,10 +753,6 @@ class CMFDRun(object): def cmfd_shift(self): return self._cmfd_shift - @property - def cmfd_spectral(self): - return self._cmfd_spectral - @property def cmfd_stol(self): return self._cmfd_stol @@ -800,13 +793,6 @@ class CMFDRun(object): check_type('CMFD feedback', cmfd_feedback, bool) self._cmfd_feedback = cmfd_feedback - @gauss_seidel_tolerance.setter - def gauss_seidel_tolerance(self, gauss_seidel_tolerance): - check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance, - Iterable, Real) - check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) - self._gauss_seidel_tolerance = gauss_seidel_tolerance - @cmfd_ktol.setter def cmfd_ktol(self, cmfd_ktol): check_type('CMFD eigenvalue tolerance', cmfd_ktol, Real) @@ -876,11 +862,6 @@ class CMFDRun(object): check_type('CMFD Wielandt shift', cmfd_shift, Real) self._cmfd_shift = cmfd_shift - @cmfd_spectral.setter - def cmfd_spectral(self, cmfd_spectral): - check_type('CMFD spectral radius', cmfd_spectral, Real) - self._cmfd_spectral = cmfd_spectral - @cmfd_stol.setter def cmfd_stol(self, cmfd_stol): check_type('CMFD fission source tolerance', cmfd_stol, Real) @@ -896,9 +877,12 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, mpi_procs=None, omp_threads=None): - # TODO: Add logic for mpi_procs, omp_threads as args - openmc.capi.init() + def run(self, mpi_procs=None, omp_num_threads=None): + if omp_num_threads is not None: + check_type('OpenMP num threads', omp_num_threads, Integral) + openmc.capi.init(args=['-s',str(omp_num_threads)]) + else: + openmc.capi.init() self._configure_cmfd() openmc.capi.simulation_init() @@ -971,21 +955,14 @@ class CMFDRun(object): if self._cmfd_mesh.map is not None: check_length('CMFD coremap', self._cmfd_mesh.map, np.product(self._indices[0:3])) - self._coremap = np.array(self._cmfd_mesh.map).reshape(( \ - self._indices[0], self._indices[1], \ - self._indices[2])) + self._coremap = np.array(self._cmfd_mesh.map) else: - self._coremap = np.ones((self._indices[0], self._indices[1], \ - self._indices[2])) + self._coremap = np.ones((np.product(self._indices[0:3]))) # Set number of batches where cmfd tallies should be reset if self._cmfd_reset is not None: self._n_cmfd_resets = len(self._cmfd_reset) - # Set Gauss Sidel tolerances - self._cmfd_atoli = self._gauss_seidel_tolerance[0] - self._cmfd_rtoli = self._gauss_seidel_tolerance[1] - # Create tally objects self._create_cmfd_tally() @@ -1059,24 +1036,24 @@ class CMFDRun(object): # Call solver self._cmfd_solver_execute() - ''' - ! Save k-effective - cmfd % k_cmfd(current_batch) = cmfd % keff - ! check to perform adjoint on last batch + # Save k-effective + self._k_cmfd.append(self._keff) + ''' + ! TODO check to perform adjoint on last batch if (current_batch == n_batches .and. cmfd_run_adjoint) then call cmfd_solver_execute(adjoint=.true.) end if end if - ! calculate fission source + ! TODO calculate fission source call calc_fission_source() - ! calculate weight factors + ! TODO calculate weight factors call cmfd_reweight(.true.) - ! stop cmfd timer + ! TODO stop cmfd timer if (master) call time_cmfd % stop() ''' @@ -1128,13 +1105,32 @@ class CMFDRun(object): # TODO Stop timer for build # call time_cmfdbuild % stop() - # TODO Begin power iteration - # call time_cmfdsolve % start() + # Begin power iteration + # TODO call time_cmfdsolve % start() phi, keff, dom = self._execute_power_iter(loss, prod) - # call time_cmfdsolve % stop() + # TODO call time_cmfdsolve % stop() - # TODO Save results - #if self._ + # Save results, normalizing phi to sum to 1 + if adjoint: + self._adj_keff = keff + self._adj_phi = phi/np.sqrt(np.sum(phi*phi)) + else: + self._keff = keff + self._phi = phi/np.sqrt(np.sum(phi*phi)) + + self._dom.append(dom) + + # TODO Write out flux vector + ''' + if (cmfd_write_matrices) then + if (adjoint_calc) then + filename = 'adj_fluxvec.dat' + else + filename = 'fluxvec.dat' + end if + ! TODO: call phi_n % write(filename) + end if + ''' def _build_matrices(self, adjoint): # Set up matrices @@ -1300,9 +1296,9 @@ class CMFDRun(object): else: print_str += "%.3f\t" % prod[i, j] print(print_str) - return prod ''' - sys.exit() + return prod + def _matrix_to_indices(self, irow, nx, ny, nz, ng): # Get indices from coremap @@ -1317,51 +1313,107 @@ class CMFDRun(object): matidx = ng*(self._coremap[i,j,k]) + g return matidx - def _execute_power_iter(self): - pass - ''' + def _execute_power_iter(self, loss, prod): # Get problem size - n = loss % n + n = loss.shape[0] - # Set up flux vectors - call phi_n % create(n) - call phi_o % create(n) + # Set up flux vectors, intital guess set to 1 + phi_n = np.ones((n,)) + phi_o = np.ones((n,)) # Set up source vectors - call s_n % create(n) - call s_o % create(n) - call serr_v % create(n) + s_n = np.zeros((n,)) + s_o = np.zeros((n,)) + serr_v = np.zeros((n,)) # Set initial guess - guess = ONE - phi_n % val = guess - phi_o % val = guess - k_n = keff + k_n = openmc.capi.keff()[0] k_o = k_n - dw = cmfd_shift + dw = self._cmfd_shift k_s = k_o + dw - k_ln = ONE/(ONE/k_n - ONE/k_s) + k_ln = 1.0/(1.0/k_n - 1.0/k_s) k_lo = k_ln # Set norms to 0 - norm_n = ZERO - norm_o = ZERO + norm_n = 0.0 + norm_o = 0.0 - # Set tolerances - ktol = cmfd_ktol - stol = cmfd_stol - ''' + # Maximum number of power iterations + maxits = 10000 + + # Perform Wielandt shift + loss -= 1.0/k_s*prod + + # Convert matrices to csr matrix in order to use scipy sparse solver + prod = sparse.csr_matrix(prod) + loss = sparse.csr_matrix(loss) + + for i in range(maxits): + if i == maxits - 1: + raise OpenMCError('Reached maximum iterations in CMFD power ' + 'iteration solver.') + print("iter", i) + # Compute source vector + s_o = prod.dot(phi_o) + + # Normalize source vector + s_o /= k_lo + + # Compute new flux vector with scipy sparse solver + phi_n = sparse.linalg.spsolve(loss, s_o) + + # Compute new source vector + s_n = prod.dot(phi_n) + + # Compute new shifted eigenvalue + k_ln = np.sum(s_n) / np.sum(s_o) + + # Compute new eigenvalue + k_n = 1.0/(1.0/k_ln + 1.0/k_s) + + # Renormalize the old source + s_o *= k_lo + + # Check convergence + iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o) + + # If converged, calculate dominance ratio and break from loop + if iconv: + dom = norm_n / norm_o + return phi_n, k_n, dom + + # Record old values if not converged + phi_o = phi_n + k_o = k_n + k_lo = k_ln + norm_o = norm_n + + def _check_convergence(self, s_n, s_o, k_n, k_o): + # Calculate error in keff + kerr = abs(k_o - k_n)/k_n + + # Calculate max error in source + serr = np.sqrt(np.sum(np.where(s_n>0, ((s_n-s_o)/s_n)**2,0))/len(s_n)) + + # Check for convergence + iconv = kerr < self._cmfd_ktol and serr < self._cmfd_stol + + # TODO Print out to user + #if (cmfd_power_monitor .and. master) then + # write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & + # &1PE12.5,T55, "src-error: ",1PE12.5,T80,I0)') iter, k_n, kerr, & + # serr, innerits + + # Return source error and convergence logical back to solver + return iconv, serr def _set_coremap(self): self._mat_dim = np.sum(self._coremap) - # Create a temporary array that unravels coremap and aggregates - # cumulative sum over accelerated regions - temp = np.where(self._coremap.ravel()==0, _CMFD_NOACCEL, - np.cumsum(self._coremap.ravel())-1) - - # Reshape coremap back to correct shape - self._coremap = temp.reshape(self._coremap.shape) + # Create a temporary array that aggregates cumulative sum over + # accelerated regions + self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, + np.cumsum(self._coremap)-1) def _compute_xs(self): # Extract energy indices @@ -1382,7 +1434,7 @@ class CMFDRun(object): # Set conditional numpy array as boolean vector based on coremap # Repeat each value for number of groups in problem - is_cmfd_accel = np.repeat(self._coremap.ravel() != _CMFD_NOACCEL, ng) + is_cmfd_accel = np.repeat(self._coremap != _CMFD_NOACCEL, ng) # Get flux from CMFD tally 0 tally_id = self._cmfd_tally_ids[0] @@ -1403,7 +1455,7 @@ class CMFDRun(object): ' at mesh: (' + \ ', '.join(str(i+1) for i in mat_idx[:-1]) + \ ') in group ' + str(ng-mat_idx[-1]) - raise ValueError(err_message) + raise OpenMCError(err_message) # Store flux and reshape # Flux is flipped in energy axis as tally results are given in reverse @@ -1433,8 +1485,7 @@ class CMFDRun(object): # Scattering xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group self._scattxs = np.flip(scattxs.reshape(self._scattxs.shape), axis=3) - self._scattxs = np.flip(self._scattxs.reshape(self._scattxs.shape), \ - axis=4) + self._scattxs = np.flip(self._scattxs, axis=4) # Get nu-fission xs from CMFD tally 1 # flux is repeated to account for extra dimensionality of nu-fission xs @@ -1564,6 +1615,7 @@ class CMFDRun(object): # Compute scattering rr by broadcasting flux in outgoing energy and # summing over incoming energy + # TODO Improve this with knowledge of numpy bradcasting scattering = np.sum(self._scattxs * \ np.repeat(self._flux[:,:,:,:,np.newaxis], ng, axis=4), axis=3) @@ -1730,8 +1782,6 @@ class CMFDRun(object): # TODO: Print message with verbosity 8 print(' Dhats reset to zero') - - def _get_reflector_albedo(self, l, g, i, j, k): # Get partial currents from object current = self._current[i,j,k,g,:] From 1fde7f85991a2d678646a7a913cd44db7aacb434 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 24 Aug 2018 15:38:49 -0400 Subject: [PATCH 19/58] Add vectorized version of compute_dhat --- openmc/cmfd.py | 245 ++++++++++++++++++++++++++++++++------------ src/cmfd_solver.F90 | 1 + 2 files changed, 182 insertions(+), 64 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 6a136a90a5..123b83d766 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -76,19 +76,18 @@ class CMFDMesh(object): boundary conditions. They are listed in the following order: -x +x -y +y -z +z. map : Iterable of int - TODO: EDIT THIS DESCRIPTION WITH CORRECT VALUES An optional acceleration map can be specified to overlay on the coarse - mesh spatial grid. If this option is used, a ``1`` is used for a - non-accelerated region and a ``2`` is used for an accelerated region. + mesh spatial grid. If this option is used, a ``0`` is used for a + non-accelerated region and a ``1`` is used for an accelerated region. For a simple 4x4 coarse mesh with a 2x2 fuel lattice surrounded by reflector, the map is: :: - [1, 1, 1, 1, - 1, 2, 2, 1, - 1, 2, 2, 1, - 1, 1, 1, 1] + [0, 0, 0, 0, + 0, 1, 1, 0, + 0, 1, 1, 0, + 0, 0, 0, 0] Therefore a 2x2 system of equations is solved rather than a 4x4. This is extremely important to use in reflectors as neutrons will not contribute @@ -597,7 +596,7 @@ class CMFD(object): class CMFDRun(object): r"""Class to run openmc with CMFD acceleration through the C API. Running - openmc through this manner obviates the need of defining CMFD parameters + openmc through this manner obviates the need for defining CMFD parameters through a cmfd.xml file. Instead, all input parameters should be passed through the CMFDRun initializer. @@ -609,7 +608,6 @@ class CMFDRun(object): albedo: Albedo for global boundary conditions, taken from CMFD mesh. Set to [1,1,1,1,1,1] if not specified by user n_cmfd_resets: Number of elements in tally_reset, list that stores batches where CMFD tallies should be reset cmfd_mesh_id: Mesh id of openmc.capi.Mesh object that corresponds to the CMFD mesh - cmfd_filter_ids: list of ids corresponding to CMFD filters (details:) cmfd_tally_ids: list of ids corresponding to CMFD tallies (details:) energy_filters: Boolean that stores whether energy filters should be created or not. Set to true if user specifies energy grid in CMFDMesh, false otherwise @@ -698,6 +696,7 @@ class CMFDRun(object): self._dhat = None self._hxyz = None self._current = None + self._cmfd_src = None self._openmc_src = None self._sourcecounts = None @@ -918,9 +917,9 @@ class CMFDRun(object): self._allocate_cmfd() def _read_cmfd_input(self): - # TODO: Print message with verbosity # Print message - print(' Configuring CMFD parameters for simulation') + if openmc.capi.settings.verbosity >= 7 and openmc.capi.settings.master: + print(' Configuring CMFD parameters for simulation') # Check if CMFD mesh is defined if self._cmfd_mesh is None: @@ -1026,41 +1025,38 @@ class CMFDRun(object): def _execute_cmfd(self): # CMFD single processor on master if openmc.capi.settings.master: + # TODO + #! Start cmfd timer + #call time_cmfd % start() - # TODO - #! Start cmfd timer - #call time_cmfd % start() + # Create cmfd data from OpenMC tallies + self._set_up_cmfd() - # Create cmfd data from OpenMC tallies - self._set_up_cmfd() + # Call solver + self._cmfd_solver_execute() - # Call solver - self._cmfd_solver_execute() - - # Save k-effective - self._k_cmfd.append(self._keff) - ''' - ! TODO check to perform adjoint on last batch - if (current_batch == n_batches .and. cmfd_run_adjoint) then + # Save k-effective + self._k_cmfd.append(self._keff) + ''' + ! TODO check to perform adjoint on last batch + if (current_batch == n_batches .and. cmfd_run_adjoint) then call cmfd_solver_execute(adjoint=.true.) - end if + end if - end if + ! TODO calculate fission source + call calc_fission_source() - ! TODO calculate fission source - call calc_fission_source() + ! TODO calculate weight factors + call cmfd_reweight(.true.) - ! TODO calculate weight factors - call cmfd_reweight(.true.) - - ! TODO stop cmfd timer - if (master) call time_cmfd % stop() - ''' + ! TODO stop cmfd timer + if (master) call time_cmfd % stop() + ''' def _cmfd_tally_reset(self): - # TODO: Print message with verbosity # Print message - print(' CMFD tallies reset') + if openmc.capi.settings.verbosity >= 6 and openmc.capi.settings.master: + print(' CMFD tallies reset') # Reset CMFD tallies tallies = openmc.capi.tallies @@ -1088,6 +1084,9 @@ class CMFDRun(object): # Calculate dhat self._compute_dhat() + # Calculate dhat + self._compute_dhat2() + def _cmfd_solver_execute(self, adjoint=False): # TODO Check for physical adjoint physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' @@ -1119,6 +1118,7 @@ class CMFDRun(object): self._phi = phi/np.sqrt(np.sum(phi*phi)) self._dom.append(dom) + #print(phi, keff, dom) # TODO Write out flux vector ''' @@ -1131,6 +1131,7 @@ class CMFDRun(object): ! TODO: call phi_n % write(filename) end if ''' + sys.exit() def _build_matrices(self, adjoint): # Set up matrices @@ -1352,7 +1353,7 @@ class CMFDRun(object): if i == maxits - 1: raise OpenMCError('Reached maximum iterations in CMFD power ' 'iteration solver.') - print("iter", i) + # Compute source vector s_o = prod.dot(phi_o) @@ -1425,6 +1426,7 @@ class CMFDRun(object): # Set mesh widths self._hxyz = openmc.capi.meshes[self._cmfd_mesh_id].width + # self._hxyz[:,:,:,] = openmc.capi.meshes[self._cmfd_mesh_id].width # Reset keff_bal to zero self._keff_bal = 0. @@ -1441,6 +1443,9 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] flux = np.where(is_cmfd_accel, tally_results, 0.) + print(flux) + print(self._coremap) + # Detect zero flux, abort if located if np.any(flux[is_cmfd_accel] < _TINY_BIT): # Get index of zero flux in flux array @@ -1500,21 +1505,11 @@ class CMFDRun(object): # Nu-fission xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group self._nfissxs = np.flip(nfissxs.reshape(self._nfissxs.shape), axis=3) - self._nfissxs = np.flip(self._nfissxs.reshape(self._nfissxs.shape), \ - axis=4) - - # Filter nu-fission tally results to compute openmc source distribution - tally_results = np.where(np.repeat(flux>0, ng), tally_results, \ - 0.) + self._nfissxs = np.flip(self._nfissxs, axis=4) # Openmc source distribution is sum of nu-fission rr in incoming energies - openmc_src = np.sum(tally_results.reshape(self._nfissxs.shape), - axis=3) - - # Store openmc_src - # Openmc source is flipped in energy axis as tally results are given - # in reverse order of energy group - self._openmc_src = np.flip(openmc_src, axis=3) + self._openmc_src = np.sum(self._nfissxs*self._flux[:,:,:,:,np.newaxis], + axis=3) # Compute k_eff from source distribution self._keff_bal = np.sum(self._openmc_src) / num_realizations @@ -1553,6 +1548,10 @@ class CMFDRun(object): self._diffcof = np.where(self._flux>0, 1.0 / (3.0 * \ (self._totalxs - self._p1scattxs)), 0.) + # Reshape coremap to three dimensional array as all cross section data + # has been reshaped + self._coremap = self._coremap.reshape(self._indices[0:3]) + def _compute_effective_downscatter(self): # Extract energy index ng = self._indices[3] @@ -1615,14 +1614,13 @@ class CMFDRun(object): # Compute scattering rr by broadcasting flux in outgoing energy and # summing over incoming energy - # TODO Improve this with knowledge of numpy bradcasting - scattering = np.sum(self._scattxs * \ - np.repeat(self._flux[:,:,:,:,np.newaxis], ng, axis=4), axis=3) + scattering = np.sum(self._scattxs * self._flux[:,:,:,:,np.newaxis], + axis=3) # Compute fission rr by broadcasting flux in outgoing energy and # summing over incoming energy - fission = np.sum(self._nfissxs * \ - np.repeat(self._flux[:,:,:,:,np.newaxis], ng, axis=4), axis=3) + fission = np.sum(self._nfissxs * self._flux[:,:,:,:,np.newaxis], + axis=3) # Compute residual res = leakage + interactions - scattering - (1.0 / keff) * fission @@ -1706,6 +1704,124 @@ class CMFDRun(object): # Record dtilde self._dtilde[i, j, k, g, l] = dtilde + def _compute_dhat2(self): + print("Before dhat:") + print(self._dhat) + print() + + dhat2 = np.zeros(self._dhat.shape) + + net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ + self._current[:,:,:,:,_CURRENTS['out_left']]) / \ + np.prod(self._hxyz)*self._hxyz[0]) + net_current_plusx = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ + self._current[:,:,:,:,_CURRENTS['in_right']]) / \ + np.prod(self._hxyz)*self._hxyz[0]) + net_current_minusy = ((self._current[:,:,:,:,_CURRENTS['in_back']] - \ + self._current[:,:,:,:,_CURRENTS['out_back']]) / \ + np.prod(self._hxyz)*self._hxyz[1]) + net_current_plusy = ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ + self._current[:,:,:,:,_CURRENTS['in_front']]) / \ + np.prod(self._hxyz)*self._hxyz[1]) + net_current_minusz = ((self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ + self._current[:,:,:,:,_CURRENTS['out_bottom']]) / \ + np.prod(self._hxyz)*self._hxyz[2]) + net_current_plusz = ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ + self._current[:,:,:,:,_CURRENTS['in_top']]) / \ + np.prod(self._hxyz)*self._hxyz[2]) + + cell_flux = self._flux / np.prod(self._hxyz) + is_accel = self._coremap != _CMFD_NOACCEL + + dhat2[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], + (net_current_minusx[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ + cell_flux[0,:,:,:]) / cell_flux[0,:,:,:], 0) + dhat2[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], + (net_current_plusx[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ + cell_flux[-1,:,:,:]) / cell_flux[-1,:,:,:], 0) + dhat2[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], + (net_current_minusy[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ + cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) + dhat2[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], + (net_current_plusy[:,-1,:,:] + self._dtilde[:,-1,:,:,3] * \ + cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) + dhat2[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], + (net_current_minusz[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ + cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) + dhat2[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], + (net_current_minusz[:,:,-1,:] + self._dtilde[:,:,-1,:,5] * \ + cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) + + # Minus x direction + adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, 1, axis=0) / np.prod(self._hxyz) + dhat2[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ + np.where(adj_reflector[1:,:,:,np.newaxis], + (net_current_minusx[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ + cell_flux[1:,:,:,:]) / cell_flux[1:,:,:,:], + (net_current_minusx[1:,:,:,:] - self._dtilde[1:,:,:,:,0] * \ + (neig_flux[1:,:,:,:] - cell_flux[1:,:,:,:])) / \ + (neig_flux[1:,:,:,:] + cell_flux[1:,:,:,:])), 0.0) + + # Plus x direction + adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, -1, axis=0) / np.prod(self._hxyz) + dhat2[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ + np.where(adj_reflector[:-1,:,:,np.newaxis], + (net_current_plusx[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ + cell_flux[:-1,:,:,:]) / cell_flux[:-1,:,:,:], + (net_current_plusx[:-1,:,:,:] + self._dtilde[:-1,:,:,:,1] * \ + (neig_flux[:-1,:,:,:] - cell_flux[:-1,:,:,:])) / \ + (neig_flux[:-1,:,:,:] + cell_flux[:-1,:,:,:])), 0.0) + + # Minus y direction + adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, 1, axis=1) / np.prod(self._hxyz) + dhat2[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ + np.where(adj_reflector[:,1:,:,np.newaxis], + (net_current_minusy[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ + cell_flux[:,1:,:,:]) / cell_flux[:,1:,:,:], + (net_current_minusy[:,1:,:,:] - self._dtilde[:,1:,:,:,2] * \ + (neig_flux[:,1:,:,:] - cell_flux[:,1:,:,:])) / \ + (neig_flux[:,1:,:,:] + cell_flux[:,1:,:,:])), 0.0) + + # Plus y direction + adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, -1, axis=1) / np.prod(self._hxyz) + dhat2[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ + np.where(adj_reflector[:,:-1,:,np.newaxis], + (net_current_plusy[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ + cell_flux[:,:-1,:,:]) / cell_flux[:,:-1,:,:], + (net_current_plusy[:,:-1,:,:] + self._dtilde[:,:-1,:,:,3] * \ + (neig_flux[:,:-1,:,:] - cell_flux[:,:-1,:,:])) / \ + (neig_flux[:,:-1,:,:] + cell_flux[:,:-1,:,:])), 0.0) + + # Minus z direction + adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, 1, axis=2) / np.prod(self._hxyz) + dhat2[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ + np.where(adj_reflector[:,:,1:,np.newaxis], + (net_current_minusz[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ + cell_flux[:,:,1:,:]) / cell_flux[:,:,1:,:], + (net_current_minusz[:,:,1:,:] - self._dtilde[:,:,1:,:,4] * \ + (neig_flux[:,:,1:,:] - cell_flux[:,:,1:,:])) / \ + (neig_flux[:,:,1:,:] + cell_flux[:,:,1:,:])), 0.0) + + # Plus z direction + adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + neig_flux = np.roll(self._flux, -1, axis=2) / np.prod(self._hxyz) + dhat2[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ + np.where(adj_reflector[:,:,:-1,np.newaxis], + (net_current_plusz[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ + cell_flux[:,:,:-1,:]) / cell_flux[:,:,:-1,:], + (net_current_plusz[:,:,:-1,:] + self._dtilde[:,:,:-1,:,5] * \ + (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ + (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) + + print("After dhat") + print(dhat2) + sys.exit() + def _compute_dhat(self): #TODO compute dhat and dtilde for general case with hxyz (just define as repeated but use in formulas) # Get maximum of spatial and group indices @@ -1749,7 +1865,11 @@ class CMFDRun(object): # Compute dhat dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ cell_flux - #print(dhat, i, j, k, g, l) + if l == 1: + print(dhat, i, j, k, g, l) + print(net_current, cell_dtilde, cell_flux) + print("yo") + else: # Not at a boundary # Compute neighboring cell indices neig_idx = [i,j,k] # Begin with i,j,k @@ -1765,6 +1885,9 @@ class CMFDRun(object): # Compute dhat dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ cell_flux + #if l==0: + # print("hit") + # print(dhat, net_current, cell_dtilde[l], cell_flux) else: # not a fuel-reflector interface # Compute dhat dhat = (net_current + shift_idx*cell_dtilde[l]* \ @@ -1778,8 +1901,8 @@ class CMFDRun(object): self._dhat[i, j, k, g, l] = 0.0 # Write that dhats are zero - if self._dhat_reset: - # TODO: Print message with verbosity 8 + if self._dhat_reset and openmc.capi.settings.verbosity >= 8 and \ + openmc.capi.settings.master: print(' Dhats reset to zero') def _get_reflector_albedo(self, l, g, i, j, k): @@ -1803,12 +1926,10 @@ class CMFDRun(object): upper_right=self._cmfd_mesh.upper_right, width=self._cmfd_mesh.width) - self._cmfd_filter_ids = [] # Create Mesh Filter object, stored internally mesh_filter = openmc.capi.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh - self._cmfd_filter_ids.append(mesh_filter.id) # Set up energy filters, if applicable if self._energy_filters: @@ -1816,25 +1937,21 @@ class CMFDRun(object): energy_filter = openmc.capi.EnergyFilter() # Set bins for Energy Filter energy_filter.bins = self._egrid - self._cmfd_filter_ids.append(energy_filter.id) # Create Energy Out Filter object, stored internally energyout_filter = openmc.capi.EnergyoutFilter() # Set bins for Energy Filter energyout_filter.bins = self._egrid - self._cmfd_filter_ids.append(energyout_filter.id) # Create Mesh Surface Filter object, stored internally meshsurface_filter = openmc.capi.MeshSurfaceFilter() # Set mesh for Mesh Surface Filter meshsurface_filter.mesh = cmfd_mesh - self._cmfd_filter_ids.append(meshsurface_filter.id) # Create Legendre Filter object, stored internally legendre_filter = openmc.capi.LegendreFilter() # Set order for Legendre Filter legendre_filter.order = 1 - self._cmfd_filter_ids.append(legendre_filter.id) # Create CMFD tallies, stored internally n_tallies = 4 diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index 270ddfab42..f10a609028 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -102,6 +102,7 @@ contains use constants, only: ONE, ZERO use cmfd_header, only: cmfd_shift, cmfd_ktol, cmfd_stol, cmfd_write_matrices use simulation_header, only: keff, current_batch + use string, only: to_str logical, intent(in) :: adjoint From 961178ed80913e110d143d3fa985957ee1022ef0 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 24 Aug 2018 23:20:03 -0400 Subject: [PATCH 20/58] Vectorized version of compute_dtilde --- openmc/cmfd.py | 215 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 22 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 123b83d766..56b2ab488c 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1042,16 +1042,17 @@ class CMFDRun(object): if (current_batch == n_batches .and. cmfd_run_adjoint) then call cmfd_solver_execute(adjoint=.true.) end if - - ! TODO calculate fission source - call calc_fission_source() - - ! TODO calculate weight factors - call cmfd_reweight(.true.) - - ! TODO stop cmfd timer - if (master) call time_cmfd % stop() ''' + # calculate fission source + self._calc_fission_source() + + ''' + ! TODO calculate weight factors + call cmfd_reweight(.true.) + + ! TODO stop cmfd timer + if (master) call time_cmfd % stop() + ''' def _cmfd_tally_reset(self): # Print message @@ -1081,6 +1082,8 @@ class CMFDRun(object): # Calculate dtilde self._compute_dtilde() + self._compute_dtilde2() + # Calculate dhat self._compute_dhat() @@ -1118,7 +1121,6 @@ class CMFDRun(object): self._phi = phi/np.sqrt(np.sum(phi*phi)) self._dom.append(dom) - #print(phi, keff, dom) # TODO Write out flux vector ''' @@ -1131,7 +1133,9 @@ class CMFDRun(object): ! TODO: call phi_n % write(filename) end if ''' - sys.exit() + + def _calc_fission_source(self): + pass def _build_matrices(self, adjoint): # Set up matrices @@ -1443,9 +1447,6 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] flux = np.where(is_cmfd_accel, tally_results, 0.) - print(flux) - print(self._coremap) - # Detect zero flux, abort if located if np.any(flux[is_cmfd_accel] < _TINY_BIT): # Get index of zero flux in flux array @@ -1633,6 +1634,184 @@ class CMFDRun(object): np.sum(np.multiply(self._resnb, self._resnb)) / \ np.count_nonzero(self._resnb))) + def _compute_dtilde2(self): + dtilde2 = np.zeros(self._dtilde.shape) + + is_accel = self._coremap != _CMFD_NOACCEL + is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + + dtilde2[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], + np.where(is_zero_flux_alb[0], 2.0 * self._diffcof[0,:,:,:] / \ + self._hxyz[0], + (2.0 * self._diffcof[0,:,:,:] * \ + (1.0 - self._albedo[0])) / \ + (4.0 * self._diffcof[0,:,:,:] * \ + (1.0 + self._albedo[0]) + \ + (1.0 - self._albedo[0]) * self._hxyz[0])), 0) + + dtilde2[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], + np.where(is_zero_flux_alb[1], 2.0 * self._diffcof[-1,:,:,:] / \ + self._hxyz[0], + (2.0 * self._diffcof[-1,:,:,:] * \ + (1.0 - self._albedo[1])) / \ + (4.0 * self._diffcof[-1,:,:,:] * \ + (1.0 + self._albedo[1]) + \ + (1.0 - self._albedo[1]) * self._hxyz[0])), 0) + + dtilde2[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], + np.where(is_zero_flux_alb[2], 2.0 * self._diffcof[:,0,:,:] / \ + self._hxyz[1], + (2.0 * self._diffcof[:,0,:,:] * \ + (1.0 - self._albedo[2])) / \ + (4.0 * self._diffcof[:,0,:,:] * \ + (1.0 + self._albedo[2]) + \ + (1.0 - self._albedo[2]) * self._hxyz[1])), 0) + + dtilde2[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], + np.where(is_zero_flux_alb[3], 2.0 * self._diffcof[:,-1,:,:] / \ + self._hxyz[1], + (2.0 * self._diffcof[:,-1,:,:] * \ + (1.0 - self._albedo[3])) / \ + (4.0 * self._diffcof[:,-1,:,:] * \ + (1.0 + self._albedo[3]) + \ + (1.0 - self._albedo[3]) * self._hxyz[1])), 0) + + dtilde2[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], + np.where(is_zero_flux_alb[4], 2.0 * self._diffcof[:,:,0,:] / \ + self._hxyz[2], + (2.0 * self._diffcof[:,:,0,:] * \ + (1.0 - self._albedo[4])) / \ + (4.0 * self._diffcof[:,:,0,:] * \ + (1.0 + self._albedo[4]) + \ + (1.0 - self._albedo[4]) * self._hxyz[2])), 0) + + dtilde2[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], + np.where(is_zero_flux_alb[5], 2.0 * self._diffcof[:,:,-1,:] / \ + self._hxyz[2], + (2.0 * self._diffcof[:,:,-1,:] * \ + (1.0 - self._albedo[5])) / \ + (4.0 * self._diffcof[:,:,-1,:] * \ + (1.0 + self._albedo[5]) + \ + (1.0 - self._albedo[5]) * self._hxyz[2])), 0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_left']], + self._current[:,:,:,:,_CURRENTS['out_left']], + where=self._current[:,:,:,:,_CURRENTS['out_left']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_left']])) + adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, 1, axis=0) + # Define neg_hxyz + + dtilde2[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ + np.where(adj_reflector[1:,:,:,np.newaxis], + (2.0 * self._diffcof[1:,:,:,:] * \ + (1.0 - ref_albedo[1:,:,:,:])) / \ + (4.0 * self._diffcof[1:,:,:,:] * \ + (1.0 + ref_albedo[1:,:,:,:]) + \ + (1.0 - ref_albedo[1:,:,:,:]) * self._hxyz[0]), + (2.0 * self._diffcof[1:,:,:,:] * neig_dc[1:,:,:,:]) / \ + (self._hxyz[0] * self._diffcof[1:,:,:,:] + \ + self._hxyz[0] * neig_dc[1:,:,:,:])), 0.0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_right']], + self._current[:,:,:,:,_CURRENTS['out_right']], + where=self._current[:,:,:,:,_CURRENTS['out_right']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_right']])) + adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, -1, axis=0) + # Define neg_hxyz + + dtilde2[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ + np.where(adj_reflector[:-1,:,:,np.newaxis], + (2.0 * self._diffcof[:-1,:,:,:] * \ + (1.0 - ref_albedo[:-1,:,:,:])) / \ + (4.0 * self._diffcof[:-1,:,:,:] * \ + (1.0 + ref_albedo[:-1,:,:,:]) + \ + (1.0 - ref_albedo[:-1,:,:,:]) * self._hxyz[0]), + (2.0 * self._diffcof[:-1,:,:,:] * neig_dc[:-1,:,:,:]) / \ + (self._hxyz[0] * self._diffcof[:-1,:,:,:] + \ + self._hxyz[0] * neig_dc[:-1,:,:,:])), 0.0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_back']], + self._current[:,:,:,:,_CURRENTS['out_back']], + where=self._current[:,:,:,:,_CURRENTS['out_back']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_back']])) + adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, 1, axis=1) + # Define neg_hxyz + + dtilde2[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ + np.where(adj_reflector[:,1:,:,np.newaxis], + (2.0 * self._diffcof[:,1:,:,:] * \ + (1.0 - ref_albedo[:,1:,:,:])) / \ + (4.0 * self._diffcof[:,1:,:,:] * \ + (1.0 + ref_albedo[:,1:,:,:]) + \ + (1.0 - ref_albedo[:,1:,:,:]) * self._hxyz[1]), + (2.0 * self._diffcof[:,1:,:,:] * neig_dc[:,1:,:,:]) / \ + (self._hxyz[1] * self._diffcof[:,1:,:,:] + \ + self._hxyz[1] * neig_dc[:,1:,:,:])), 0.0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_front']], + self._current[:,:,:,:,_CURRENTS['out_front']], + where=self._current[:,:,:,:,_CURRENTS['out_front']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_front']])) + adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, -1, axis=1) + # Define neg_hxyz + + dtilde2[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ + np.where(adj_reflector[:,:-1,:,np.newaxis], + (2.0 * self._diffcof[:,:-1,:,:] * \ + (1.0 - ref_albedo[:,:-1,:,:])) / \ + (4.0 * self._diffcof[:,:-1,:,:] * \ + (1.0 + ref_albedo[:,:-1,:,:]) + \ + (1.0 - ref_albedo[:,:-1,:,:]) * self._hxyz[1]), + (2.0 * self._diffcof[:,:-1,:,:] * neig_dc[:,:-1,:,:]) / \ + (self._hxyz[1] * self._diffcof[:,:-1,:,:] + \ + self._hxyz[1] * neig_dc[:,:-1,:,:])), 0.0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_bottom']], + self._current[:,:,:,:,_CURRENTS['out_bottom']], + where=self._current[:,:,:,:,_CURRENTS['out_bottom']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_bottom']])) + adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, 1, axis=2) + # Define neg_hxyz + + dtilde2[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ + np.where(adj_reflector[:,:,1:,np.newaxis], + (2.0 * self._diffcof[:,:,1:,:] * \ + (1.0 - ref_albedo[:,:,1:,:])) / \ + (4.0 * self._diffcof[:,:,1:,:] * \ + (1.0 + ref_albedo[:,:,1:,:]) + \ + (1.0 - ref_albedo[:,:,1:,:]) * self._hxyz[2]), + (2.0 * self._diffcof[:,:,1:,:] * neig_dc[:,:,1:,:]) / \ + (self._hxyz[2] * self._diffcof[:,:,1:,:] + \ + self._hxyz[2] * neig_dc[:,:,1:,:])), 0.0) + + ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_top']], + self._current[:,:,:,:,_CURRENTS['out_top']], + where=self._current[:,:,:,:,_CURRENTS['out_top']] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_top']])) + adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + neig_dc = np.roll(self._diffcof, -1, axis=2) + # Define neg_hxyz + + dtilde2[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ + np.where(adj_reflector[:,:,:-1,np.newaxis], + (2.0 * self._diffcof[:,:,:-1,:] * \ + (1.0 - ref_albedo[:,:,:-1,:])) / \ + (4.0 * self._diffcof[:,:,:-1,:] * \ + (1.0 + ref_albedo[:,:,:-1,:]) + \ + (1.0 - ref_albedo[:,:,:-1,:]) * self._hxyz[2]), + (2.0 * self._diffcof[:,:,:-1,:] * neig_dc[:,:,:-1,:]) / \ + (self._hxyz[2] * self._diffcof[:,:,:-1,:] + \ + self._hxyz[2] * neig_dc[:,:,:-1,:])), 0.0) + + print("After dtilde") + print(dtilde2) + sys.exit() + def _compute_dtilde(self): # Get maximum of spatial and group indices nx = self._indices[0] @@ -1705,10 +1884,6 @@ class CMFDRun(object): self._dtilde[i, j, k, g, l] = dtilde def _compute_dhat2(self): - print("Before dhat:") - print(self._dhat) - print() - dhat2 = np.zeros(self._dhat.shape) net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ @@ -1818,10 +1993,6 @@ class CMFDRun(object): (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) - print("After dhat") - print(dhat2) - sys.exit() - def _compute_dhat(self): #TODO compute dhat and dtilde for general case with hxyz (just define as repeated but use in formulas) # Get maximum of spatial and group indices From c2858bc94119d9430698feaf6f211a766a5e924c Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Sun, 26 Aug 2018 22:45:58 -0400 Subject: [PATCH 21/58] Clean up comments, add vector_write function in Fortran, create vectorized calc_fission_source --- openmc/cmfd.py | 232 ++++++++++++++++++++++++++++-------------- src/cmfd_solver.F90 | 21 ++-- src/vector_header.F90 | 25 ++++- 3 files changed, 194 insertions(+), 84 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 56b2ab488c..4a5ebc580d 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -16,6 +16,7 @@ from xml.etree import ElementTree as ET # TODO Remove import sys # TODO Remove import numpy as np from scipy import sparse +import time import openmc.capi from openmc.clean_xml import clean_xml_indentation # TODO Remove @@ -637,11 +638,9 @@ class CMFDRun(object): mat_dim TODO: Put descriptions for all methods in CMFDRun - TODO All timing variables TODO Get rid of CMFD constants TODO Get rid of unused variables defined in init TODO Make sure all self variables defined in init - TODO Clean up logic for adjoint, understand what different adjoint types are doing TODO Check to make sure no compatibility issues with numpy arrays for input variables TODO Create write_vector function in cmfd_solver.F90 @@ -679,7 +678,7 @@ class CMFDRun(object): self._cmfd_on = False self._mat_dim = _CMFD_NOACCEL self._keff_bal = None - self._cmfd_adjoint_type = None + self._cmfd_adjoint_type = "physical" self._keff = None self._adj_keff = None self._phi = None @@ -708,6 +707,10 @@ class CMFDRun(object): self._k_cmfd = None self._resnb = None + self._time_cmfd = None + self._time_cmfdbuild = None + self._time_cmfdsolve = None + @property def cmfd_begin(self): return self._cmfd_begin @@ -740,6 +743,10 @@ class CMFDRun(object): def norm(self): return self._norm + @property + def cmfd_adjoint_type(self): + return self._cmfd_adjoint_type + @property def cmfd_power_monitor(self): return self._cmfd_power_monitor @@ -846,6 +853,13 @@ class CMFDRun(object): check_type('CMFD norm', norm, Real) self._norm = norm + @cmfd_adjoint_type.setter + def cmfd_adjoint_type(self, adjoint_type): + check_type('CMFD adjoint type', adjoint_type, str) + check_value('CMFD adjoint type', adjoint_type, + ['math', 'phyical']) + self._cmfd_adjoint_type = adjoint_type + @cmfd_power_monitor.setter def cmfd_power_monitor(self, cmfd_power_monitor): check_type('CMFD power monitor', cmfd_power_monitor, bool) @@ -877,47 +891,65 @@ class CMFDRun(object): self._cmfd_write_matrices = cmfd_write_matrices def run(self, mpi_procs=None, omp_num_threads=None): + # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) openmc.capi.init(args=['-s',str(omp_num_threads)]) else: openmc.capi.init() + + # Configure cmfd parameters and tallies self._configure_cmfd() + + # Initialize simulation openmc.capi.simulation_init() while(True): + # Run everything in next batch before initializing cmfd openmc.capi.next_batch_before_cmfd_init() + + # Initialize CMFD batch self._cmfd_init_batch() + + # Run everything in next batch in between initializing and + # executing CMFD + status = openmc.capi.next_batch_between_cmfd_init_execute() + # Status determines whether batch should continue with a # CMFD update or skip it entirely if it is a restart run - status = openmc.capi.next_batch_between_cmfd_init_execute() if status != 0: + + # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd() - # Status now determines whether another batch should be run - # or simulation should be terminated. + + # Run everything in next batch after executing CMFD. Status + # now determines whether another batch should be run or + # simulation should be terminated. status = openmc.capi.next_batch_after_cmfd_execute() if status != 0: break + # Finalize simuation openmc.capi.simulation_finalize() + + # Finalize and free memory openmc.capi.finalize() def _configure_cmfd(self): - # Read in cmfd input from python + # Read in cmfd input defined in Python self._read_cmfd_input() - # TODO # Initialize timers - #call time_cmfd % reset() - #call time_cmfdbuild % reset() - #call time_cmfdsolve % reset() + self._time_cmfd = 0.0 + self._time_cmfdbuild = 0.0 + self._time_cmfdsolve = 0.0 # Initialize all numpy arrays used for cmfd solver self._allocate_cmfd() def _read_cmfd_input(self): - # Print message + # Print message to user if openmc.capi.settings.verbosity >= 7 and openmc.capi.settings.master: print(' Configuring CMFD parameters for simulation') @@ -985,6 +1017,7 @@ class CMFDRun(object): self._dhat = np.zeros((nx, ny, nz, ng, 6)) # Allocate dimensions for each box (assume fixed mesh dimensions) + # TODO Update this self._hxyz = np.zeros((3)) # Allocate surface currents @@ -1006,14 +1039,18 @@ class CMFDRun(object): self._k_cmfd = [] def _cmfd_init_batch(self): + # Get simulation parameters through C API current_batch = openmc.capi.settings.current_batch restart_run = openmc.capi.settings.restart_run restart_batch = openmc.capi.settings.restart_batch + # Check to activate CMFD diffusion and possible feedback if self._cmfd_begin == current_batch: self._cmfd_on = True # TODO: Test restart_batch + # If this is a restart run we are just replaying batches so don't + # execute anything if restart_run and current_batch <= restart_batch: return @@ -1023,11 +1060,10 @@ class CMFDRun(object): self._cmfd_tally_reset() def _execute_cmfd(self): - # CMFD single processor on master + # Run CMFD on single processor on master if openmc.capi.settings.master: - # TODO #! Start cmfd timer - #call time_cmfd % start() + time_start_cmfd = time.time() # Create cmfd data from OpenMC tallies self._set_up_cmfd() @@ -1037,22 +1073,22 @@ class CMFDRun(object): # Save k-effective self._k_cmfd.append(self._keff) - ''' - ! TODO check to perform adjoint on last batch - if (current_batch == n_batches .and. cmfd_run_adjoint) then - call cmfd_solver_execute(adjoint=.true.) - end if - ''' - # calculate fission source + + # Check to perform adjoint on last batch + if (openmc.capi.settings.current_batch == \ + openmc.capi.settings.batches and self._cmfd_run_adjoint): + self._cmfd_solver_execute(adjoint=True) + + # Calculate fission source self._calc_fission_source() - ''' - ! TODO calculate weight factors + ! TODO Calculate weight factors call cmfd_reweight(.true.) - - ! TODO stop cmfd timer - if (master) call time_cmfd % stop() ''' + # Stop cmfd timer + if openmc.capi.settings.master: + time_stop_cmfd = time.time() + self._time_cmfd += time_stop_cmfd - time_start_cmfd def _cmfd_tally_reset(self): # Print message @@ -1065,9 +1101,8 @@ class CMFDRun(object): tallies[tally_id].reset() def _set_up_cmfd(self): - # Check for core map and set it up + # Set up CMFD coremap if (self._mat_dim == _CMFD_NOACCEL): - # TODO: Don't reshape coremap before this point, reshape in set_coremap self._set_coremap() # Calculate all cross sections based on reaction rates from last batch @@ -1087,30 +1122,31 @@ class CMFDRun(object): # Calculate dhat self._compute_dhat() - # Calculate dhat self._compute_dhat2() def _cmfd_solver_execute(self, adjoint=False): - # TODO Check for physical adjoint + # Check for physical adjoint physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' - # TODO Start timer for build - # call time_cmfdbuild % start() + # Start timer for build + time_start_buildcmfd = time.time() - # Initialize matrices and vectors + # Build loss and production matrices loss, prod = self._build_matrices(physical_adjoint) - # TODO Check for mathematical adjoint calculation - #if adjoint_calc and self._cmfd_adjoint_type == 'math': - # self._compute_adjoint() + # Check for mathematical adjoint calculation + if adjoint and self._cmfd_adjoint_type == 'math': + loss, prod = self._compute_adjoint(loss, prod) - # TODO Stop timer for build - # call time_cmfdbuild % stop() + # Stop timer for build + time_stop_buildcmfd = time.time() + self._time_cmfdbuild += time_stop_buildcmfd - time_start_buildcmfd # Begin power iteration - # TODO call time_cmfdsolve % start() + time_start_solvecmfd = time.time() phi, keff, dom = self._execute_power_iter(loss, prod) - # TODO call time_cmfdsolve % stop() + time_stop_solvecmfd = time.time() + self._time_cmfdsolve += time_stop_solvecmfd - time_start_solvecmfd # Save results, normalizing phi to sum to 1 if adjoint: @@ -1135,20 +1171,61 @@ class CMFDRun(object): ''' def _calc_fission_source(self): - pass + # Extract number of groups and number of accelerated regions + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + n = self._mat_dim + + # Reset cmfd source to 0 + self._cmfd_src.fill(0.) + + # Calculate volume + vol = np.product(self._hxyz) + + # Reshape phi by number of groups + phi = self._phi.reshape((n, ng)) + + # Extract indices of coremap that are accelerated + idx = np.where(self._coremap != _CMFD_NOACCEL) + + # Initialize CMFD flux map that maps phi to actualy spatial and group + # indices of problem + cmfd_flux = np.zeros((nx, ny, nz, ng)) + + # Loop over all groups and set CMFD flux based on indices of coremap + # and values of phi + for g in range(ng): + flux[idx + (np.full((n,),g),)] = phi[:,g] + + # Compute fission source + self._cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ + cmfd_flux[:,:,:,:,np.newaxis], axis=3) * vol def _build_matrices(self, adjoint): - # Set up matrices - + # Build loss matrix loss = self._build_loss_matrix(adjoint) + + # Build production matrix prod = self._build_prod_matrix(adjoint) - ''' - # TODO Write matrices - if (cmfd_write_matrices) then - call loss % write('loss.dat') - call prod % write('prod.dat') - end if - ''' + + # TODO Write out matrices + #if self._cmfd_write_matrices: + # self._write_matrix(loss, 'loss.dat') + # self._write_matrix(prod, 'prod.dat') + + return loss, prod + + def _compute_adjoint(self, loss, prod): + # Transpose matrices + loss = np.transpose(loss) + prod = np.transpose(prod) + + # TODO Write out matrices + #if self._cmfd_write_matrices: + # self._write_matrix(loss, 'adj_loss.dat') + # self._write_matrix(prod, 'adj_prod.dat') return loss, prod @@ -1170,6 +1247,7 @@ class CMFDRun(object): jo = np.zeros((6,)) for irow in range(n): + # Get indices for row in matrix i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) # Retrieve cell data @@ -1219,6 +1297,7 @@ class CMFDRun(object): val = jnet + totxs - scattxsgg loss[irow, irow] = val + # Begin loop over off diagonal in-scattering for h in range(ng): # Cycle though if h=g, value already banked in removal xs if h == g: @@ -1227,14 +1306,13 @@ class CMFDRun(object): # Get neighbor matrix index scatt_mat_idx = self._indices_to_matrix(i,j,k, h, ng) - # TODO Check for adjoint - #if (adjoint_calc) then - #! Get scattering macro xs, transposed! - #scattxshg = cmfd%scattxs(g, h, i, j, k) - #else - # Get scattering macro xs - scattxshg = self._scattxs[i, j, k, h, g] - #end if + # Check for adjoint + if adjoint: + # Get scattering macro xs, transposed! + scattxshg = self._scattxs[i, j, k, g, h] + else: + # Get scattering macro xs + scattxshg = self._scattxs[i, j, k, h, g] # Negate the scattering xs val = -1.0*scattxshg @@ -1268,24 +1346,26 @@ class CMFDRun(object): prod = np.zeros((n, n)) for irow in range(n): + # Get indices for row in matrix i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) # Check if at a reflector if self._coremap[i,j,k] == _CMFD_NOACCEL: continue - # loop around all other groups + # Loop around all other groups for h in range(ng): + # Get matrix column location hmat_idx = self._indices_to_matrix(i,j,k, h, ng) - # TODO check for adjoint and bank val - #if (adjoint_calc) then - # ! get nu-fission cross section from cell - # nfissxs = cmfd%nfissxs(g,h,i,j,k) - #else - # get nu-fission cross section from cell - nfissxs = self._nfissxs[i, j, k, h, g] + # Check for adjoint and bank val + if adjoint: + # Get nu-fission cross section from cell, transposed! + nfissxs = self._nfissxs[i, j, k, g, h] + else: + # Get nu-fission cross section from cell + nfissxs = self._nfissxs[i, j, k, h, g] - # set as value to be recorded + # Set as value to be recorded val = nfissxs # record value in matrix @@ -1312,9 +1392,11 @@ class CMFDRun(object): i = spatial_idx[0][0] j = spatial_idx[1][0] k = spatial_idx[2][0] + return i, j, k, g def _indices_to_matrix(self, i, j, k, g, ng): + # Get matrix index from coremap matidx = ng*(self._coremap[i,j,k]) + g return matidx @@ -1353,7 +1435,9 @@ class CMFDRun(object): prod = sparse.csr_matrix(prod) loss = sparse.csr_matrix(loss) + # Begin power iteration for i in range(maxits): + # Check if reach max number of iterations if i == maxits - 1: raise OpenMCError('Reached maximum iterations in CMFD power ' 'iteration solver.') @@ -1415,8 +1499,9 @@ class CMFDRun(object): def _set_coremap(self): self._mat_dim = np.sum(self._coremap) - # Create a temporary array that aggregates cumulative sum over - # accelerated regions + # Define coremap as cumulative sum over accelerated regions, + # otherwise set value to _CMFD_NOACCEL + # TODO, does algorithm work if CMFD_ACCEL is fixed number self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, np.cumsum(self._coremap)-1) @@ -1597,6 +1682,7 @@ class CMFDRun(object): # Get openmc k-effective keff = openmc.capi.keff()[0] + # Define leakage in each mesh cell and energy group leakage = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ self._current[:,:,:,:,_CURRENTS['in_right']]) - \ (self._current[:,:,:,:,_CURRENTS['in_left']] - \ @@ -1635,6 +1721,7 @@ class CMFDRun(object): np.count_nonzero(self._resnb))) def _compute_dtilde2(self): + #TODO add coments for this method dtilde2 = np.zeros(self._dtilde.shape) is_accel = self._coremap != _CMFD_NOACCEL @@ -1808,10 +1895,6 @@ class CMFDRun(object): (self._hxyz[2] * self._diffcof[:,:,:-1,:] + \ self._hxyz[2] * neig_dc[:,:,:-1,:])), 0.0) - print("After dtilde") - print(dtilde2) - sys.exit() - def _compute_dtilde(self): # Get maximum of spatial and group indices nx = self._indices[0] @@ -1884,6 +1967,7 @@ class CMFDRun(object): self._dtilde[i, j, k, g, l] = dtilde def _compute_dhat2(self): + # TODO Write comments for this function dhat2 = np.zeros(self._dhat.shape) net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index f10a609028..960ad11a7a 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -7,6 +7,8 @@ module cmfd_solver use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix use matrix_header, only: Matrix use vector_header, only: Vector + use simulation_header, only: current_batch + use string, only: to_str implicit none private @@ -101,8 +103,7 @@ contains use constants, only: ONE, ZERO use cmfd_header, only: cmfd_shift, cmfd_ktol, cmfd_stol, cmfd_write_matrices - use simulation_header, only: keff, current_batch - use string, only: to_str + use simulation_header, only: keff logical, intent(in) :: adjoint @@ -147,8 +148,8 @@ contains call loss % assemble() call prod % assemble() if (cmfd_write_matrices) then - call loss % write('loss' // trim(to_str(current_batch)) // '.dat') - call prod % write('prod' // trim(to_str(current_batch)) // '.dat') + call loss % write('loss_gen' // trim(to_str(current_batch)) // '.dat') + call prod % write('prod_gen' // trim(to_str(current_batch)) // '.dat') end if ! Set norms to 0 @@ -176,8 +177,8 @@ contains ! Write out matrix in binary file (debugging) if (cmfd_write_matrices) then - call loss % write('adj_loss.dat') - call prod % write('adj_prod.dat') + call loss % write('adj_loss_gen' // trim(to_str(current_batch)) // '.dat') + call prod % write('adj_prod_gen' // trim(to_str(current_batch)) // '.dat') end if end subroutine compute_adjoint @@ -731,17 +732,19 @@ contains cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) end if + print *, cmfd % phi + ! Save dominance ratio cmfd % dom(current_batch) = norm_n/norm_o ! Write out results if (cmfd_write_matrices) then if (adjoint_calc) then - filename = 'adj_fluxvec.dat' + filename = 'adj_fluxvec_gen' // trim(to_str(current_batch)) // '.dat' else - filename = 'fluxvec.dat' + filename = 'fluxvec_gen' // trim(to_str(current_batch)) // '.dat' end if - ! TODO: call phi_n % write(filename) + call phi_n % write(filename) end if end subroutine extract_results diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 1ada0fba74..5bcf0fddcb 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -14,7 +14,7 @@ module vector_header procedure :: destroy => vector_destroy procedure :: add_value => vector_add_value procedure :: copy => vector_copy - ! TODO: procedure :: write => vector_write + procedure :: write => vector_write end type Vector contains @@ -88,4 +88,27 @@ contains end subroutine vector_copy +!=============================================================================== +! VECTOR_WRITE write a vector to file +!=============================================================================== + + subroutine vector_write(self, filename) + + class(Vector), target, intent(inout) :: self ! vector instance + character(*), intent(in) :: filename ! filename to output to + + integer :: unit_ + integer :: i + + open(newunit=unit_, file=filename) + + do i = 1, self % n + write(unit_,*) i, self % data(i) + print *, self % data(i) + end do + + close(unit_) + + end subroutine vector_write + end module vector_header From 5d31649e6dd1f3bacaf6a2389b3003597e1277aa Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 27 Aug 2018 03:42:43 -0400 Subject: [PATCH 22/58] Include vectorized versions of build matrices --- openmc/cmfd.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 4a5ebc580d..e9da4095df 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -15,6 +15,8 @@ from numbers import Real, Integral from xml.etree import ElementTree as ET # TODO Remove import sys # TODO Remove import numpy as np +# TODO Include comment for this +np.seterr(divide='ignore', invalid='ignore') from scipy import sparse import time @@ -1197,7 +1199,7 @@ class CMFDRun(object): # Loop over all groups and set CMFD flux based on indices of coremap # and values of phi for g in range(ng): - flux[idx + (np.full((n,),g),)] = phi[:,g] + cmfd_flux[idx + (np.full((n,),g),)] = phi[:,g] # Compute fission source self._cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ @@ -1206,9 +1208,11 @@ class CMFDRun(object): def _build_matrices(self, adjoint): # Build loss matrix loss = self._build_loss_matrix(adjoint) + self._build_loss_matrix2(adjoint) # Build production matrix prod = self._build_prod_matrix(adjoint) + self._build_prod_matrix2(adjoint) # TODO Write out matrices #if self._cmfd_write_matrices: @@ -1334,6 +1338,118 @@ class CMFDRun(object): return loss + def _build_loss_matrix2(self, adjoint): + # TODO build as csr matrix + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Allocate matrix + loss2 = np.zeros((n, n)) + + # Define net leakage coefficient for each matrix element + jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ + (-1.0 * self._dtilde[:,:,:,:,0] + self._dhat[:,:,:,:,0])) / \ + self._hxyz[0] + \ + ((1.0 * self._dtilde[:,:,:,:,3] + self._dhat[:,:,:,:,3]) - \ + (-1.0 * self._dtilde[:,:,:,:,2] + self._dhat[:,:,:,:,2])) / \ + self._hxyz[1] + \ + ((1.0 * self._dtilde[:,:,:,:,5] + self._dhat[:,:,:,:,5]) - \ + (-1.0 * self._dtilde[:,:,:,:,4] + self._dhat[:,:,:,:,4])) / \ + self._hxyz[2] + + # Shift coremap in all directions to determine whether leakage term + # should be defined for particular cell in matrix + coremap_minusx = np.pad(self._coremap, ((1,0),(0,0),(0,0)), mode='constant', + constant_values=_CMFD_NOACCEL)[:-1,:,:] + + coremap_plusx = np.pad(self._coremap, ((0,1),(0,0),(0,0)), mode='constant', + constant_values=_CMFD_NOACCEL)[1:,:,:] + + coremap_minusy = np.pad(self._coremap, ((0,0),(1,0),(0,0)), mode='constant', + constant_values=_CMFD_NOACCEL)[:,:-1,:] + + coremap_plusy = np.pad(self._coremap, ((0,0),(0,1),(0,0)), mode='constant', + constant_values=_CMFD_NOACCEL)[:,1:,:] + + coremap_minusz = np.pad(self._coremap, ((0,0),(0,0),(1,0)), mode='constant', + constant_values=_CMFD_NOACCEL)[:,:,:-1] + + coremap_plusz = np.pad(self._coremap, ((0,0),(0,0),(0,1)), mode='constant', + constant_values=_CMFD_NOACCEL)[:,:,1:] + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_minusy != _CMFD_NOACCEL) + + for g in range(ng): + # Leakage terms + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_minusx != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_minusx[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,0] - + self._dhat[:,:,:,g,0])[condition] / self._hxyz[0] + loss2[idx_x, idx_y] = vals + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_plusx != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_plusx[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,1] + + self._dhat[:,:,:,g,1])[condition] / self._hxyz[0] + loss2[idx_x, idx_y] = vals + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_minusy != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_minusy[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,2] - + self._dhat[:,:,:,g,2])[condition] / self._hxyz[1] + loss2[idx_x, idx_y] = vals + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_plusy != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_plusy[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,3] + + self._dhat[:,:,:,g,3])[condition] / self._hxyz[1] + loss2[idx_x, idx_y] = vals + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_minusz != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_minusz[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,4] - + self._dhat[:,:,:,g,4])[condition] / self._hxyz[1] + loss2[idx_x, idx_y] = vals + + condition = np.logical_and(self._coremap != _CMFD_NOACCEL, + coremap_plusz != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (coremap_plusz[condition]) + g + vals = (-1.0 * self._dtilde[:,:,:,g,5] + + self._dhat[:,:,:,g,5])[condition] / self._hxyz[1] + loss2[idx_x, idx_y] = vals + + # Loss of neutrons + condition = self._coremap != _CMFD_NOACCEL + idx_x = ng * (self._coremap[condition]) + g + idx_y = idx_x + vals = (jnet[:,:,:,g] + self._totalxs[:,:,:,g] - \ + self._scattxs[:,:,:,g,g])[condition] + loss2[idx_x, idx_y] = vals + + # Off diagonal in-scattering + for h in range(ng): + #TODO check for adjoint (see cmfd_loss_operator.F90) + if h != g: + condition = self._coremap != _CMFD_NOACCEL + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (self._coremap[condition]) + h + vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] + loss2[idx_x, idx_y] = vals + + def _build_prod_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension nx = self._indices[0] @@ -1384,6 +1500,24 @@ class CMFDRun(object): ''' return prod + def _build_prod_matrix2(self, adjoint): + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Allocate matrix + prod2 = np.zeros((n, n)) + + for g in range(ng): + for h in range(ng): + #TODO check for adjoint (see cmfd_prod_operator.F90) + condition = self._coremap != _CMFD_NOACCEL + idx_x = ng * (self._coremap[condition]) + g + idx_y = ng * (self._coremap[condition]) + h + vals = (self._nfissxs[:, :, :, h, g])[condition] + prod2[idx_x, idx_y] = vals + + prod = prod2 def _matrix_to_indices(self, irow, nx, ny, nz, ng): # Get indices from coremap From 3631d15f5108aabf9a2f445fe792577cb938c353 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 28 Aug 2018 14:11:34 -0400 Subject: [PATCH 23/58] Say hello to a new CMFD solver (Finish particle reweight) --- .../cmfd-feed/capi/run_openmc_cmfd.py | 2 +- examples/cmfd_testing/cmfd-feed/trad/cmfd.xml | 4 +- openmc/cmfd.py | 121 ++++++++++++++++-- src/cmfd_solver.F90 | 2 - src/vector_header.F90 | 1 - 5 files changed, 116 insertions(+), 14 deletions(-) diff --git a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py index d9c3953eda..1193fde7f3 100644 --- a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py +++ b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py @@ -10,7 +10,7 @@ cmfd_mesh.lower_left = [-10, -1, -1] cmfd_mesh.upper_right = [10, 1, 1] cmfd_mesh.dimension = [10, 1, 1] cmfd_mesh.albedo = [0., 0., 1., 1., 1., 1.] -cmfd_mesh.energy = [0., 10000., 10000000.] +cmfd_mesh.energy = [0., 1000000., 10000000000.] cmfd_mesh.map = [0,1,1,1,1,1,1,1,1,0] # Initialize CMFDRun object diff --git a/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml b/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml index adda403407..0d033ad119 100644 --- a/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml +++ b/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml @@ -6,11 +6,11 @@ 10 1 1 10 1 1 0.0 0.0 1.0 1.0 1.0 1.0 - 0.0 10000.0 10000000.0 + 0.0 1000000.0 10000000000.0 1 2 2 2 2 2 2 2 2 1 5 dominance - false + true 1.e-15 1.e-20 diff --git a/openmc/cmfd.py b/openmc/cmfd.py index e9da4095df..caa8deafce 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -19,6 +19,7 @@ import numpy as np np.seterr(divide='ignore', invalid='ignore') from scipy import sparse import time +from mpi4py import MPI import openmc.capi from openmc.clean_xml import clean_xml_indentation # TODO Remove @@ -898,7 +899,8 @@ class CMFDRun(object): check_type('OpenMP num threads', omp_num_threads, Integral) openmc.capi.init(args=['-s',str(omp_num_threads)]) else: - openmc.capi.init() + comm = MPI.COMM_WORLD + openmc.capi.init(intracomm=comm) # Configure cmfd parameters and tallies self._configure_cmfd() @@ -1083,10 +1085,10 @@ class CMFDRun(object): # Calculate fission source self._calc_fission_source() - ''' - ! TODO Calculate weight factors - call cmfd_reweight(.true.) - ''' + + # Calculate weight factors + self._cmfd_reweight(True) + # Stop cmfd timer if openmc.capi.settings.master: time_stop_cmfd = time.time() @@ -1119,12 +1121,12 @@ class CMFDRun(object): # Calculate dtilde self._compute_dtilde() - self._compute_dtilde2() + #self._compute_dtilde2() # Calculate dhat self._compute_dhat() - self._compute_dhat2() + #self._compute_dhat2() def _cmfd_solver_execute(self, adjoint=False): # Check for physical adjoint @@ -1205,6 +1207,110 @@ class CMFDRun(object): self._cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ cmfd_flux[:,:,:,:,np.newaxis], axis=3) * vol + def _cmfd_reweight(self, new_weights): + # Compute new weight factors + if new_weights: + + # Set weight factors to default 1.0 + self._weightfactors.fill(1.0) + + # Count bank site in mesh and reverse due to egrid structured + outside = self._count_bank_sites() + + if openmc.capi.settings.master and outside: + raise OpenMCError('Source sites outside of the CMFD mesh!') + + if openmc.capi.settings.master: + norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) + sourcecounts = np.flip( + self._sourcecounts.reshape(self._cmfd_src.shape), \ + axis=3) + divide_condition = np.logical_and(sourcecounts > 0, + self._cmfd_src > 0) + self._weightfactors = np.divide(self._cmfd_src * norm, \ + sourcecounts, where=divide_condition, \ + out=np.ones_like(self._cmfd_src)) + + if not self._cmfd_feedback: + return + + comm = MPI.COMM_WORLD + self._weightfactors = comm.bcast(self._weightfactors, root=0) + + m = openmc.capi.meshes[self._cmfd_mesh_id] + bank = openmc.capi.source_bank() + energy = self._egrid + ng = self._indices[3] + + for i in range(bank.size): + # Get xyz location of source point + xyz = bank[i][1] + # Get energy of source point + source_energy = bank[i][3] + + # Determine which mesh location of source point + ijk = np.floor((xyz - m.lower_left) / m.width).astype(int) + + # Determine energy bin of source point + e_bin = np.where(source_energy < energy[0], 0, + np.where(source_energy > energy[-1], ng - 1, \ + np.digitize(source_energy, energy) - 1)) + + # Issue warnings if energy below or above defined grid + if openmc.capi.settings.master and source_energy < energy[0]: + print(' WARNING: Source pt below energy grid') + if openmc.capi.settings.master and source_energy > energy[-1]: + print(' WARNING: Source pt above energy grid') + + # Reverse index of bin due to egrid structure + e_bin = ng - e_bin - 1 + + # Reweight particle + bank[i][0] *= self._weightfactors[ijk[0], ijk[1], ijk[2], e_bin] + + def _count_bank_sites(self): + comm = MPI.COMM_WORLD + + m = openmc.capi.meshes[self._cmfd_mesh_id] + bank = openmc.capi.source_bank() + energy = self._egrid + sites_outside = np.zeros(1, dtype=bool) + ng = self._indices[3] + + # Initiate variables + outside = np.zeros(1, dtype=bool) + count = np.zeros(self._sourcecounts.shape) + + source_xyz = np.array([bank[i][1] for i in range(bank.size)]) + source_energies = np.array([bank[i][3] for i in range(bank.size)]) + + mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) + mesh_bins = mesh_locations[:,2] * m.dimension[2] + \ + mesh_locations[:,1] * m.dimension[1] + mesh_locations[:,0] + + if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): + outside[0] = True + + energy_bins = np.where(source_energies < energy[0], 0, + np.where(source_energies > energy[-1], ng - 1, \ + np.digitize(source_energies, energy) - 1)) + + idx, counts = np.unique(np.array([mesh_bins, energy_bins]), axis=1, + return_counts=True) + + count[idx[0].astype(int), idx[1].astype(int)] = counts + + comm.Reduce(count, self._sourcecounts, MPI.SUM, 0) + comm.Reduce(outside, sites_outside, MPI.LOR, 0) + + # TODO ifdef mpi? + # how to define comm? + # issue with output out of order? + return sites_outside[0] + + + + def _build_matrices(self, adjoint): # Build loss matrix loss = self._build_loss_matrix(adjoint) @@ -1635,7 +1741,6 @@ class CMFDRun(object): # Define coremap as cumulative sum over accelerated regions, # otherwise set value to _CMFD_NOACCEL - # TODO, does algorithm work if CMFD_ACCEL is fixed number self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, np.cumsum(self._coremap)-1) diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index 960ad11a7a..bd9a974d20 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -732,8 +732,6 @@ contains cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) end if - print *, cmfd % phi - ! Save dominance ratio cmfd % dom(current_batch) = norm_n/norm_o diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 5bcf0fddcb..475e4295e5 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -104,7 +104,6 @@ contains do i = 1, self % n write(unit_,*) i, self % data(i) - print *, self % data(i) end do close(unit_) From 269e71f9d77ddb31c5c663c59df40744a75aea84 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 28 Aug 2018 16:19:51 -0400 Subject: [PATCH 24/58] Fix travis issue, mpi4py not defined in some test cases --- openmc/cmfd.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index caa8deafce..1149a5dabf 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -19,7 +19,12 @@ import numpy as np np.seterr(divide='ignore', invalid='ignore') from scipy import sparse import time -from mpi4py import MPI +try: + from mpi4py import MPI + comm = MPI.COMM_WORLD + have_mpi = True +except ImportError: + have_mpi = False import openmc.capi from openmc.clean_xml import clean_xml_indentation # TODO Remove @@ -899,8 +904,7 @@ class CMFDRun(object): check_type('OpenMP num threads', omp_num_threads, Integral) openmc.capi.init(args=['-s',str(omp_num_threads)]) else: - comm = MPI.COMM_WORLD - openmc.capi.init(intracomm=comm) + openmc.capi.init() # Configure cmfd parameters and tallies self._configure_cmfd() @@ -1234,8 +1238,9 @@ class CMFDRun(object): if not self._cmfd_feedback: return - comm = MPI.COMM_WORLD - self._weightfactors = comm.bcast(self._weightfactors, root=0) + if have_mpi: + comm = MPI.COMM_WORLD + self._weightfactors = comm.bcast(self._weightfactors, root=0) m = openmc.capi.meshes[self._cmfd_mesh_id] bank = openmc.capi.source_bank() @@ -1269,8 +1274,6 @@ class CMFDRun(object): bank[i][0] *= self._weightfactors[ijk[0], ijk[1], ijk[2], e_bin] def _count_bank_sites(self): - comm = MPI.COMM_WORLD - m = openmc.capi.meshes[self._cmfd_mesh_id] bank = openmc.capi.source_bank() energy = self._egrid @@ -1300,12 +1303,18 @@ class CMFDRun(object): count[idx[0].astype(int), idx[1].astype(int)] = counts - comm.Reduce(count, self._sourcecounts, MPI.SUM, 0) - comm.Reduce(outside, sites_outside, MPI.LOR, 0) + if have_mpi: + comm = MPI.COMM_WORLD + comm.Reduce(count, self._sourcecounts, MPI.SUM, 0) + comm.Reduce(outside, sites_outside, MPI.LOR, 0) + else: + sites_outside = outside + self._sourcecounts = count # TODO ifdef mpi? # how to define comm? # issue with output out of order? + # travis mpi4py error return sites_outside[0] From 84dad25c3d8b3161c1e173bba3b0c0395605dc12 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 29 Aug 2018 00:21:38 -0400 Subject: [PATCH 25/58] Vecotrize source reweighting, merge vectorized and non-vectorized functions (compute_dhat, dtilde, build_matrix) --- openmc/cmfd.py | 215 ++++++++++++++++++++++++------------------------- 1 file changed, 106 insertions(+), 109 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 1149a5dabf..09ddf3f5ab 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -13,7 +13,7 @@ References from collections.abc import Iterable from numbers import Real, Integral from xml.etree import ElementTree as ET # TODO Remove -import sys # TODO Remove +import sys import numpy as np # TODO Include comment for this np.seterr(divide='ignore', invalid='ignore') @@ -21,7 +21,6 @@ from scipy import sparse import time try: from mpi4py import MPI - comm = MPI.COMM_WORLD have_mpi = True except ImportError: have_mpi = False @@ -719,6 +718,8 @@ class CMFDRun(object): self._time_cmfdbuild = None self._time_cmfdsolve = None + self._intracomm = None + @property def cmfd_begin(self): return self._cmfd_begin @@ -898,7 +899,14 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, mpi_procs=None, omp_num_threads=None): + def run(self, mpi_procs=None, omp_num_threads=None, intracomm=None): + # Store intracomm for part of CMFD routine where MPI reduce and + # broadcast calls are made + if intracomm is not None: + self._intracomm = intracomm + elif intracomm is None and have_mpi: + self._intracomm = MPI.COMM_WORLD + # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) @@ -960,6 +968,7 @@ class CMFDRun(object): # Print message to user if openmc.capi.settings.verbosity >= 7 and openmc.capi.settings.master: print(' Configuring CMFD parameters for simulation') + sys.stdout.flush() # Check if CMFD mesh is defined if self._cmfd_mesh is None: @@ -996,7 +1005,7 @@ class CMFDRun(object): np.product(self._indices[0:3])) self._coremap = np.array(self._cmfd_mesh.map) else: - self._coremap = np.ones((np.product(self._indices[0:3]))) + self._coremap = np.ones((np.product(self._indices[0:3])), dtype=int) # Set number of batches where cmfd tallies should be reset if self._cmfd_reset is not None: @@ -1102,13 +1111,14 @@ class CMFDRun(object): # Print message if openmc.capi.settings.verbosity >= 6 and openmc.capi.settings.master: print(' CMFD tallies reset') + sys.stdout.flush() # Reset CMFD tallies tallies = openmc.capi.tallies for tally_id in self._cmfd_tally_ids: tallies[tally_id].reset() - def _set_up_cmfd(self): + def _set_up_cmfd(self, vectorized=True): # Set up CMFD coremap if (self._mat_dim == _CMFD_NOACCEL): self._set_coremap() @@ -1123,16 +1133,18 @@ class CMFDRun(object): self._neutron_balance() # Calculate dtilde - self._compute_dtilde() - - #self._compute_dtilde2() + if vectorized: + self._compute_dtilde_vectorized() + else: + self._compute_dtilde() # Calculate dhat - self._compute_dhat() + if vectorized: + self._compute_dhat_vectorized() + else: + self._compute_dhat() - #self._compute_dhat2() - - def _cmfd_solver_execute(self, adjoint=False): + def _cmfd_solver_execute(self, adjoint=False, vectorized=True): # Check for physical adjoint physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' @@ -1140,7 +1152,7 @@ class CMFDRun(object): time_start_buildcmfd = time.time() # Build loss and production matrices - loss, prod = self._build_matrices(physical_adjoint) + loss, prod = self._build_matrices(physical_adjoint, vectorized) # Check for mathematical adjoint calculation if adjoint and self._cmfd_adjoint_type == 'math': @@ -1213,6 +1225,7 @@ class CMFDRun(object): def _cmfd_reweight(self, new_weights): # Compute new weight factors + # TODO Comments for this section if new_weights: # Set weight factors to default 1.0 @@ -1239,41 +1252,35 @@ class CMFDRun(object): return if have_mpi: - comm = MPI.COMM_WORLD - self._weightfactors = comm.bcast(self._weightfactors, root=0) + self._weightfactors = self._intracomm.bcast( + self._weightfactors, root=0) m = openmc.capi.meshes[self._cmfd_mesh_id] - bank = openmc.capi.source_bank() energy = self._egrid ng = self._indices[3] - for i in range(bank.size): - # Get xyz location of source point - xyz = bank[i][1] - # Get energy of source point - source_energy = bank[i][3] + source_xyz = openmc.capi.source_bank()['xyz'] + source_energies = openmc.capi.source_bank()['E'] - # Determine which mesh location of source point - ijk = np.floor((xyz - m.lower_left) / m.width).astype(int) + mesh_ijk = np.floor((source_xyz - m.lower_left) / m.width).astype(int) - # Determine energy bin of source point - e_bin = np.where(source_energy < energy[0], 0, - np.where(source_energy > energy[-1], ng - 1, \ - np.digitize(source_energy, energy) - 1)) + # Flip energy + energy_bins = np.where(source_energies < energy[0], ng - 1, + np.where(source_energies > energy[-1], 0, \ + ng - np.digitize(source_energies, energy))) - # Issue warnings if energy below or above defined grid - if openmc.capi.settings.master and source_energy < energy[0]: - print(' WARNING: Source pt below energy grid') - if openmc.capi.settings.master and source_energy > energy[-1]: - print(' WARNING: Source pt above energy grid') + openmc.capi.source_bank()['wgt'] *= self._weightfactors[ \ + mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] - # Reverse index of bin due to egrid structure - e_bin = ng - e_bin - 1 - - # Reweight particle - bank[i][0] *= self._weightfactors[ijk[0], ijk[1], ijk[2], e_bin] + if openmc.capi.settings.master and \ + np.any(source_energies < energy[0]): + print(' WARNING: Source pt below energy grid') + if openmc.capi.settings.master and \ + np.any(source_energies > energy[-1]): + print(' WARNING: Source pt above energy grid') def _count_bank_sites(self): + # TODO Comments for this section m = openmc.capi.meshes[self._cmfd_mesh_id] bank = openmc.capi.source_bank() energy = self._egrid @@ -1284,8 +1291,8 @@ class CMFDRun(object): outside = np.zeros(1, dtype=bool) count = np.zeros(self._sourcecounts.shape) - source_xyz = np.array([bank[i][1] for i in range(bank.size)]) - source_energies = np.array([bank[i][3] for i in range(bank.size)]) + source_xyz = openmc.capi.source_bank()['xyz'] + source_energies = openmc.capi.source_bank()['E'] mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) mesh_bins = mesh_locations[:,2] * m.dimension[2] + \ @@ -1304,30 +1311,22 @@ class CMFDRun(object): count[idx[0].astype(int), idx[1].astype(int)] = counts if have_mpi: - comm = MPI.COMM_WORLD - comm.Reduce(count, self._sourcecounts, MPI.SUM, 0) - comm.Reduce(outside, sites_outside, MPI.LOR, 0) + self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM, 0) + self._intracomm.Reduce(outside, sites_outside, MPI.LOR, 0) else: sites_outside = outside self._sourcecounts = count - # TODO ifdef mpi? - # how to define comm? - # issue with output out of order? - # travis mpi4py error return sites_outside[0] - - - - def _build_matrices(self, adjoint): - # Build loss matrix - loss = self._build_loss_matrix(adjoint) - self._build_loss_matrix2(adjoint) - - # Build production matrix - prod = self._build_prod_matrix(adjoint) - self._build_prod_matrix2(adjoint) + def _build_matrices(self, adjoint, vectorized): + # Build loss and production matrices + if vectorized: + loss = self._build_loss_matrix_vectorized(adjoint) + prod = self._build_prod_matrix_vectorized(adjoint) + else: + loss = self._build_loss_matrix(adjoint) + prod = self._build_prod_matrix(adjoint) # TODO Write out matrices #if self._cmfd_write_matrices: @@ -1453,14 +1452,15 @@ class CMFDRun(object): return loss - def _build_loss_matrix2(self, adjoint): + def _build_loss_matrix_vectorized(self, adjoint): # TODO build as csr matrix + # TODO comments for this section # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng # Allocate matrix - loss2 = np.zeros((n, n)) + loss = np.zeros((n, n)) # Define net leakage coefficient for each matrix element jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ @@ -1504,7 +1504,7 @@ class CMFDRun(object): idx_y = ng * (coremap_minusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,0] - self._dhat[:,:,:,g,0])[condition] / self._hxyz[0] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusx != _CMFD_NOACCEL) @@ -1512,7 +1512,7 @@ class CMFDRun(object): idx_y = ng * (coremap_plusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,1] + self._dhat[:,:,:,g,1])[condition] / self._hxyz[0] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_minusy != _CMFD_NOACCEL) @@ -1520,7 +1520,7 @@ class CMFDRun(object): idx_y = ng * (coremap_minusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,2] - self._dhat[:,:,:,g,2])[condition] / self._hxyz[1] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusy != _CMFD_NOACCEL) @@ -1528,7 +1528,7 @@ class CMFDRun(object): idx_y = ng * (coremap_plusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,3] + self._dhat[:,:,:,g,3])[condition] / self._hxyz[1] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_minusz != _CMFD_NOACCEL) @@ -1536,7 +1536,7 @@ class CMFDRun(object): idx_y = ng * (coremap_minusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,4] - self._dhat[:,:,:,g,4])[condition] / self._hxyz[1] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusz != _CMFD_NOACCEL) @@ -1544,7 +1544,7 @@ class CMFDRun(object): idx_y = ng * (coremap_plusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,5] + self._dhat[:,:,:,g,5])[condition] / self._hxyz[1] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals # Loss of neutrons condition = self._coremap != _CMFD_NOACCEL @@ -1552,7 +1552,7 @@ class CMFDRun(object): idx_y = idx_x vals = (jnet[:,:,:,g] + self._totalxs[:,:,:,g] - \ self._scattxs[:,:,:,g,g])[condition] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals # Off diagonal in-scattering for h in range(ng): @@ -1562,7 +1562,9 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] - loss2[idx_x, idx_y] = vals + loss[idx_x, idx_y] = vals + + return loss def _build_prod_matrix(self, adjoint): @@ -1615,13 +1617,15 @@ class CMFDRun(object): ''' return prod - def _build_prod_matrix2(self, adjoint): + def _build_prod_matrix_vectorized(self, adjoint): + # TODO Comments for this section + # TODO Build as csr matrix # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng # Allocate matrix - prod2 = np.zeros((n, n)) + prod = np.zeros((n, n)) for g in range(ng): for h in range(ng): @@ -1630,9 +1634,9 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h vals = (self._nfissxs[:, :, :, h, g])[condition] - prod2[idx_x, idx_y] = vals + prod[idx_x, idx_y] = vals - prod = prod2 + return prod def _matrix_to_indices(self, irow, nx, ny, nz, ng): # Get indices from coremap @@ -1746,6 +1750,8 @@ class CMFDRun(object): return iconv, serr def _set_coremap(self): + # Set number of accelerated regions in problem. This will be related to + # the dimension of CMFD matrices self._mat_dim = np.sum(self._coremap) # Define coremap as cumulative sum over accelerated regions, @@ -1869,7 +1875,7 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] # Reshape and extract only p1 data from tally results (no need for p0 data) - p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,1] + p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,:,1] # Store p1 scatter xs # p1 scatter xs is flipped in energy axis as tally results are given in @@ -1968,14 +1974,12 @@ class CMFDRun(object): np.sum(np.multiply(self._resnb, self._resnb)) / \ np.count_nonzero(self._resnb))) - def _compute_dtilde2(self): + def _compute_dtilde_vectorized(self): #TODO add coments for this method - dtilde2 = np.zeros(self._dtilde.shape) - is_accel = self._coremap != _CMFD_NOACCEL is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT - dtilde2[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], + self._dtilde[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], np.where(is_zero_flux_alb[0], 2.0 * self._diffcof[0,:,:,:] / \ self._hxyz[0], (2.0 * self._diffcof[0,:,:,:] * \ @@ -1984,7 +1988,7 @@ class CMFDRun(object): (1.0 + self._albedo[0]) + \ (1.0 - self._albedo[0]) * self._hxyz[0])), 0) - dtilde2[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], + self._dtilde[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], np.where(is_zero_flux_alb[1], 2.0 * self._diffcof[-1,:,:,:] / \ self._hxyz[0], (2.0 * self._diffcof[-1,:,:,:] * \ @@ -1993,7 +1997,7 @@ class CMFDRun(object): (1.0 + self._albedo[1]) + \ (1.0 - self._albedo[1]) * self._hxyz[0])), 0) - dtilde2[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], + self._dtilde[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], np.where(is_zero_flux_alb[2], 2.0 * self._diffcof[:,0,:,:] / \ self._hxyz[1], (2.0 * self._diffcof[:,0,:,:] * \ @@ -2002,7 +2006,7 @@ class CMFDRun(object): (1.0 + self._albedo[2]) + \ (1.0 - self._albedo[2]) * self._hxyz[1])), 0) - dtilde2[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], + self._dtilde[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], np.where(is_zero_flux_alb[3], 2.0 * self._diffcof[:,-1,:,:] / \ self._hxyz[1], (2.0 * self._diffcof[:,-1,:,:] * \ @@ -2011,7 +2015,7 @@ class CMFDRun(object): (1.0 + self._albedo[3]) + \ (1.0 - self._albedo[3]) * self._hxyz[1])), 0) - dtilde2[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], + self._dtilde[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], np.where(is_zero_flux_alb[4], 2.0 * self._diffcof[:,:,0,:] / \ self._hxyz[2], (2.0 * self._diffcof[:,:,0,:] * \ @@ -2020,7 +2024,7 @@ class CMFDRun(object): (1.0 + self._albedo[4]) + \ (1.0 - self._albedo[4]) * self._hxyz[2])), 0) - dtilde2[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], + self._dtilde[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], np.where(is_zero_flux_alb[5], 2.0 * self._diffcof[:,:,-1,:] / \ self._hxyz[2], (2.0 * self._diffcof[:,:,-1,:] * \ @@ -2037,7 +2041,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, 1, axis=0) # Define neg_hxyz - dtilde2[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ + self._dtilde[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], (2.0 * self._diffcof[1:,:,:,:] * \ (1.0 - ref_albedo[1:,:,:,:])) / \ @@ -2056,7 +2060,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, -1, axis=0) # Define neg_hxyz - dtilde2[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ + self._dtilde[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], (2.0 * self._diffcof[:-1,:,:,:] * \ (1.0 - ref_albedo[:-1,:,:,:])) / \ @@ -2075,7 +2079,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, 1, axis=1) # Define neg_hxyz - dtilde2[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ + self._dtilde[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], (2.0 * self._diffcof[:,1:,:,:] * \ (1.0 - ref_albedo[:,1:,:,:])) / \ @@ -2094,7 +2098,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, -1, axis=1) # Define neg_hxyz - dtilde2[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ + self._dtilde[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], (2.0 * self._diffcof[:,:-1,:,:] * \ (1.0 - ref_albedo[:,:-1,:,:])) / \ @@ -2113,7 +2117,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, 1, axis=2) # Define neg_hxyz - dtilde2[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ + self._dtilde[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], (2.0 * self._diffcof[:,:,1:,:] * \ (1.0 - ref_albedo[:,:,1:,:])) / \ @@ -2132,7 +2136,7 @@ class CMFDRun(object): neig_dc = np.roll(self._diffcof, -1, axis=2) # Define neg_hxyz - dtilde2[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ + self._dtilde[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], (2.0 * self._diffcof[:,:,:-1,:] * \ (1.0 - ref_albedo[:,:,:-1,:])) / \ @@ -2214,10 +2218,8 @@ class CMFDRun(object): # Record dtilde self._dtilde[i, j, k, g, l] = dtilde - def _compute_dhat2(self): + def _compute_dhat_vectorized(self): # TODO Write comments for this function - dhat2 = np.zeros(self._dhat.shape) - net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ self._current[:,:,:,:,_CURRENTS['out_left']]) / \ np.prod(self._hxyz)*self._hxyz[0]) @@ -2240,29 +2242,29 @@ class CMFDRun(object): cell_flux = self._flux / np.prod(self._hxyz) is_accel = self._coremap != _CMFD_NOACCEL - dhat2[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], + self._dhat[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], (net_current_minusx[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ cell_flux[0,:,:,:]) / cell_flux[0,:,:,:], 0) - dhat2[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], + self._dhat[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], (net_current_plusx[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ cell_flux[-1,:,:,:]) / cell_flux[-1,:,:,:], 0) - dhat2[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], + self._dhat[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], (net_current_minusy[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) - dhat2[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], + self._dhat[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], (net_current_plusy[:,-1,:,:] + self._dtilde[:,-1,:,:,3] * \ cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) - dhat2[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], + self._dhat[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], (net_current_minusz[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) - dhat2[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], + self._dhat[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], (net_current_minusz[:,:,-1,:] + self._dtilde[:,:,-1,:,5] * \ cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) # Minus x direction adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, 1, axis=0) / np.prod(self._hxyz) - dhat2[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ + self._dhat[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], (net_current_minusx[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ cell_flux[1:,:,:,:]) / cell_flux[1:,:,:,:], @@ -2273,7 +2275,7 @@ class CMFDRun(object): # Plus x direction adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, -1, axis=0) / np.prod(self._hxyz) - dhat2[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ + self._dhat[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], (net_current_plusx[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ cell_flux[:-1,:,:,:]) / cell_flux[:-1,:,:,:], @@ -2284,7 +2286,7 @@ class CMFDRun(object): # Minus y direction adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, 1, axis=1) / np.prod(self._hxyz) - dhat2[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ + self._dhat[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], (net_current_minusy[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ cell_flux[:,1:,:,:]) / cell_flux[:,1:,:,:], @@ -2295,7 +2297,7 @@ class CMFDRun(object): # Plus y direction adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, -1, axis=1) / np.prod(self._hxyz) - dhat2[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ + self._dhat[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], (net_current_plusy[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ cell_flux[:,:-1,:,:]) / cell_flux[:,:-1,:,:], @@ -2306,7 +2308,7 @@ class CMFDRun(object): # Minus z direction adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, 1, axis=2) / np.prod(self._hxyz) - dhat2[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ + self._dhat[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], (net_current_minusz[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ cell_flux[:,:,1:,:]) / cell_flux[:,:,1:,:], @@ -2317,7 +2319,7 @@ class CMFDRun(object): # Plus z direction adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL neig_flux = np.roll(self._flux, -1, axis=2) / np.prod(self._hxyz) - dhat2[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ + self._dhat[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], (net_current_plusz[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ cell_flux[:,:,:-1,:]) / cell_flux[:,:,:-1,:], @@ -2368,10 +2370,6 @@ class CMFDRun(object): # Compute dhat dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ cell_flux - if l == 1: - print(dhat, i, j, k, g, l) - print(net_current, cell_dtilde, cell_flux) - print("yo") else: # Not at a boundary # Compute neighboring cell indices @@ -2388,9 +2386,7 @@ class CMFDRun(object): # Compute dhat dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ cell_flux - #if l==0: - # print("hit") - # print(dhat, net_current, cell_dtilde[l], cell_flux) + else: # not a fuel-reflector interface # Compute dhat dhat = (net_current + shift_idx*cell_dtilde[l]* \ @@ -2407,6 +2403,7 @@ class CMFDRun(object): if self._dhat_reset and openmc.capi.settings.verbosity >= 8 and \ openmc.capi.settings.master: print(' Dhats reset to zero') + sys.stdout.flush() def _get_reflector_albedo(self, l, g, i, j, k): # Get partial currents from object From c5477e66f604ee26ccee23ab2df98ae98d78ef01 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 30 Aug 2018 02:30:51 -0400 Subject: [PATCH 26/58] Cleanup code, write matrices/vector to file from Python, write cmfd output, populate matrices directly as csr format --- include/openmc.h | 1 + openmc/capi/core.py | 24 +++ openmc/capi/settings.py | 3 +- openmc/cmfd.py | 325 +++++++++++++++++++++++++--------------- src/cmfd_solver.F90 | 20 ++- src/settings.F90 | 2 +- 6 files changed, 243 insertions(+), 132 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index a193c517ea..92c55f79b1 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -156,6 +156,7 @@ extern "C" { extern int32_t n_surfaces; extern int32_t n_tallies; extern int32_t n_universes; + extern bool openmc_entropy_on; extern int openmc_run_mode; extern bool openmc_simulation_initialized; extern int openmc_verbosity; diff --git a/openmc/capi/core.py b/openmc/capi/core.py index aff3645319..73a3454af1 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -71,6 +71,18 @@ def calculate_volumes(): _dll.openmc_calculate_volumes() +def current_batch(): + """Return the current batch of the simulation. + + Returns + ------- + int + Current batch of the simulation + + """ + return c_int.in_dll(_dll, 'openmc_current_batch').value + + def finalize(): """Finalize simulation and free memory""" _dll.openmc_finalize() @@ -219,6 +231,18 @@ def keff(): return (mean, std_dev) +def master(): + """Return whether processor is master processor or not. + + Returns + ------- + bool + Whether is master processor or not + + """ + return c_bool.in_dll(_dll, 'openmc_master').value + + def next_batch(): """Run next batch. diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index 61f0ee2214..55b2080227 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -18,10 +18,9 @@ _dll.openmc_get_seed.restype = c_int64 class _Settings(object): # Attributes that are accessed through a descriptor batches = _DLLGlobal(c_int32, 'n_batches') - current_batch = _DLLGlobal(c_int, 'openmc_current_batch') + entropy_on = _DLLGlobal(c_bool, 'openmc_entropy_on') generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') - master = _DLLGlobal(c_bool, 'openmc_master') particles = _DLLGlobal(c_int64, 'n_particles') restart_batch = _DLLGlobal(c_int, 'openmc_restart_batch') restart_run = _DLLGlobal(c_bool, 'openmc_restart_run') diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 09ddf3f5ab..d19b511cd6 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -646,11 +646,6 @@ class CMFDRun(object): TODO: Put descriptions for all methods in CMFDRun TODO Get rid of CMFD constants - TODO Get rid of unused variables defined in init - TODO Make sure all self variables defined in init - TODO Check to make sure no compatibility issues with numpy arrays for input variables - TODO Create write_vector function in cmfd_solver.F90 - """ def __init__(self): @@ -685,7 +680,7 @@ class CMFDRun(object): self._cmfd_on = False self._mat_dim = _CMFD_NOACCEL self._keff_bal = None - self._cmfd_adjoint_type = "physical" + self._cmfd_adjoint_type = 'physical' self._keff = None self._adj_keff = None self._phi = None @@ -729,8 +724,8 @@ class CMFDRun(object): return self._dhat_reset @property - def display(self): - return self._display + def cmfd_display(self): + return self._cmfd_display @property def cmfd_downscatter(self): @@ -791,12 +786,12 @@ class CMFDRun(object): check_type('CMFD Dhat reset', dhat_reset, bool) self._dhat_reset = dhat_reset - @display.setter - def display(self, display): + @cmfd_display.setter + def cmfd_display(self, display): check_type('CMFD display', display, str) check_value('CMFD display', display, ['balance', 'dominance', 'entropy', 'source']) - self._display = display + self._cmfd_display = display @cmfd_downscatter.setter def cmfd_downscatter(self, cmfd_downscatter): @@ -917,6 +912,9 @@ class CMFDRun(object): # Configure cmfd parameters and tallies self._configure_cmfd() + # TODO comment + sys.stdout.flush() + # Initialize simulation openmc.capi.simulation_init() @@ -938,20 +936,43 @@ class CMFDRun(object): # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd() + # TODO comment # Run everything in next batch after executing CMFD. Status # now determines whether another batch should be run or # simulation should be terminated. status = openmc.capi.next_batch_after_cmfd_execute() + + # TODO Comment + if self._cmfd_on: + self._write_cmfd_output() + if status != 0: break # Finalize simuation openmc.capi.simulation_finalize() + # TODO print out timing statistics + #print("yo") # Finalize and free memory openmc.capi.finalize() + def _write_cmfd_output(self): + # TODO Comment + str1 = 'CMFD k: {:0.5f}'.format(self._k_cmfd[-1]) + if self._cmfd_display == 'dominance': + str2 = 'Dom Rat: {:0.5f}'.format(self._dom[-1]) + elif self._cmfd_display == 'entropy': + str2 = 'CMFD Ent: {:0.5f}'.format(self._entropy[-1]) + elif self._cmfd_display == 'source': + str2 = 'RMS Src: {:0.5f}'.format(self._src_cmp[-1]) + else: + str2 = 'RMS Bal: {:0.5f}'.format(self._balance[-1]) + + print('{0:>76s}\n{1:>76s}'.format(str1, str2)) + sys.stdout.flush() + def _configure_cmfd(self): # Read in cmfd input defined in Python self._read_cmfd_input() @@ -966,9 +987,8 @@ class CMFDRun(object): def _read_cmfd_input(self): # Print message to user - if openmc.capi.settings.verbosity >= 7 and openmc.capi.settings.master: + if openmc.capi.settings.verbosity >= 7 and openmc.capi.master(): print(' Configuring CMFD parameters for simulation') - sys.stdout.flush() # Check if CMFD mesh is defined if self._cmfd_mesh is None: @@ -1057,7 +1077,7 @@ class CMFDRun(object): def _cmfd_init_batch(self): # Get simulation parameters through C API - current_batch = openmc.capi.settings.current_batch + current_batch = openmc.capi.current_batch() restart_run = openmc.capi.settings.restart_run restart_batch = openmc.capi.settings.restart_batch @@ -1065,7 +1085,6 @@ class CMFDRun(object): if self._cmfd_begin == current_batch: self._cmfd_on = True - # TODO: Test restart_batch # If this is a restart run we are just replaying batches so don't # execute anything if restart_run and current_batch <= restart_batch: @@ -1078,7 +1097,7 @@ class CMFDRun(object): def _execute_cmfd(self): # Run CMFD on single processor on master - if openmc.capi.settings.master: + if openmc.capi.master(): #! Start cmfd timer time_start_cmfd = time.time() @@ -1088,12 +1107,12 @@ class CMFDRun(object): # Call solver self._cmfd_solver_execute() - # Save k-effective + # Store k-effective self._k_cmfd.append(self._keff) # Check to perform adjoint on last batch - if (openmc.capi.settings.current_batch == \ - openmc.capi.settings.batches and self._cmfd_run_adjoint): + if (openmc.capi.current_batch() == openmc.capi.settings.batches + and self._cmfd_run_adjoint): self._cmfd_solver_execute(adjoint=True) # Calculate fission source @@ -1103,15 +1122,14 @@ class CMFDRun(object): self._cmfd_reweight(True) # Stop cmfd timer - if openmc.capi.settings.master: + if openmc.capi.master(): time_stop_cmfd = time.time() self._time_cmfd += time_stop_cmfd - time_start_cmfd def _cmfd_tally_reset(self): # Print message - if openmc.capi.settings.verbosity >= 6 and openmc.capi.settings.master: + if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): print(' CMFD tallies reset') - sys.stdout.flush() # Reset CMFD tallies tallies = openmc.capi.tallies @@ -1178,19 +1196,34 @@ class CMFDRun(object): self._dom.append(dom) - # TODO Write out flux vector - ''' - if (cmfd_write_matrices) then - if (adjoint_calc) then - filename = 'adj_fluxvec.dat' - else - filename = 'fluxvec.dat' - end if - ! TODO: call phi_n % write(filename) - end if - ''' + # Write out flux vector + if self._cmfd_write_matrices: + if adjoint: + self._write_vector(self._adj_phi, 'adj_fluxvec') + else: + self._write_vector(self._phi, 'fluxvec') - def _calc_fission_source(self): + def _write_vector(self, vector, base_filename): + # TODO write comments + with open(base_filename+'.dat', 'w') as fh: + for val in vector: + fh.write('{:0.8f}\n'.format(val)) + + np.save(base_filename, vector) + + def _write_matrix(self, matrix, base_filename): + # TODO write comments, mention zero-based indexing + with open(base_filename+'.dat', 'w') as fh: + for row in range(matrix.shape[0]): + cols = matrix.indices[matrix.indptr[row]:matrix.indptr[row+1]] + data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] + for i in range(len(cols)): + fh.write('({0:3d}, {1:3d}): {2:0.8f}\n'.format( + row, cols[i], data[i])) + + sparse.save_npz(base_filename, matrix) + + def _calc_fission_source(self, vectorized=True): # Extract number of groups and number of accelerated regions nx = self._indices[0] ny = self._indices[1] @@ -1201,27 +1234,69 @@ class CMFDRun(object): # Reset cmfd source to 0 self._cmfd_src.fill(0.) - # Calculate volume - vol = np.product(self._hxyz) + # TODO Comment for vectorized vs. not-vectorized + if vectorized: + # Calculate volume + vol = np.product(self._hxyz) - # Reshape phi by number of groups - phi = self._phi.reshape((n, ng)) + # Reshape phi by number of groups + phi = self._phi.reshape((n, ng)) - # Extract indices of coremap that are accelerated - idx = np.where(self._coremap != _CMFD_NOACCEL) + # Extract indices of coremap that are accelerated + idx = np.where(self._coremap != _CMFD_NOACCEL) - # Initialize CMFD flux map that maps phi to actualy spatial and group - # indices of problem - cmfd_flux = np.zeros((nx, ny, nz, ng)) + # Initialize CMFD flux map that maps phi to actualy spatial and + # group indices of problem + cmfd_flux = np.zeros((nx, ny, nz, ng)) - # Loop over all groups and set CMFD flux based on indices of coremap - # and values of phi - for g in range(ng): - cmfd_flux[idx + (np.full((n,),g),)] = phi[:,g] + # Loop over all groups and set CMFD flux based on indices of + # coremap and values of phi + for g in range(ng): + cmfd_flux[idx + (np.full((n,),g),)] = phi[:,g] - # Compute fission source - self._cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ - cmfd_flux[:,:,:,:,np.newaxis], axis=3) * vol + # Compute fission source + cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ + cmfd_flux[:,:,:,:,np.newaxis], axis=3) * vol + + else: + cmfd_src = np.zeros((nx, ny, nz, ng)) + for k in range(nz): + for j in range(ny): + for i in range(nx): + for g in range(ng): + # Cycle through if non-accelerated region + if self._coremap[i,j,k] == _CMFD_NOACCEL: + continue + + # Calculate volume + vol = np.product(self._hxyz) + + # Get index in matrix + idx = self._indices_to_matrix(i, j, k, 0, ng) + + # Compute fission source + cmfd_src2[i,j,k,g] = np.sum( + self._nfissxs[i,j,k,:,g] \ + * self._phi[idx:idx+ng]) * vol + + # Normalize source such that it sums to 1.0 + self._cmfd_src = cmfd_src / np.sum(cmfd_src) + + # Compute entropy + if openmc.capi.settings.entropy_on: + # Compute source times log_2(source) + source = self._cmfd_src[self._cmfd_src > 0] \ + * np.log(self._cmfd_src[self._cmfd_src > 0])/np.log(2) + + # Sum source and store + self._entropy.append(-1.0 * np.sum(source)) + + # Normalize source so average is 1.0 + self._cmfd_src = self._cmfd_src/np.sum(self._cmfd_src) * self._norm + + # Calculate differences between normalized sources + self._src_cmp.append(np.sqrt(1.0 / self._norm \ + * np.sum((self._cmfd_src - self._openmc_src)**2))) def _cmfd_reweight(self, new_weights): # Compute new weight factors @@ -1234,10 +1309,10 @@ class CMFDRun(object): # Count bank site in mesh and reverse due to egrid structured outside = self._count_bank_sites() - if openmc.capi.settings.master and outside: + if openmc.capi.master() and outside: raise OpenMCError('Source sites outside of the CMFD mesh!') - if openmc.capi.settings.master: + if openmc.capi.master(): norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) sourcecounts = np.flip( self._sourcecounts.reshape(self._cmfd_src.shape), \ @@ -1272,11 +1347,9 @@ class CMFDRun(object): openmc.capi.source_bank()['wgt'] *= self._weightfactors[ \ mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] - if openmc.capi.settings.master and \ - np.any(source_energies < energy[0]): + if openmc.capi.master() and np.any(source_energies < energy[0]): print(' WARNING: Source pt below energy grid') - if openmc.capi.settings.master and \ - np.any(source_energies > energy[-1]): + if openmc.capi.master() and np.any(source_energies > energy[-1]): print(' WARNING: Source pt above energy grid') def _count_bank_sites(self): @@ -1328,10 +1401,14 @@ class CMFDRun(object): loss = self._build_loss_matrix(adjoint) prod = self._build_prod_matrix(adjoint) - # TODO Write out matrices - #if self._cmfd_write_matrices: - # self._write_matrix(loss, 'loss.dat') - # self._write_matrix(prod, 'prod.dat') + # Write out matrices + if self._cmfd_write_matrices: + if adjoint: + self._write_matrix(loss, 'adj_loss') + self._write_matrix(prod, 'adj_prod') + else: + self._write_matrix(loss, 'loss') + self._write_matrix(prod, 'prod') return loss, prod @@ -1340,10 +1417,10 @@ class CMFDRun(object): loss = np.transpose(loss) prod = np.transpose(prod) - # TODO Write out matrices - #if self._cmfd_write_matrices: - # self._write_matrix(loss, 'adj_loss.dat') - # self._write_matrix(prod, 'adj_prod.dat') + # Write out matrices + if self._cmfd_write_matrices: + self._write_matrix(loss, 'adj_loss') + self._write_matrix(prod, 'adj_prod') return loss, prod @@ -1438,29 +1515,20 @@ class CMFDRun(object): # Record value in matrix loss[irow, scatt_mat_idx] = val - ''' - print("Loss matrix:") - for i in range(loss.shape[0]): - print_str = "" - for j in range(loss.shape[1]): - if loss[i,j] != 0.0: - print_str += "%.3g\t" % loss[i, j] - else: - print_str += "%.3f\t" % loss[i, j] - print(print_str) - ''' - + # Convert matrix to csr matrix in order to use scipy sparse solver + loss = sparse.csr_matrix(loss) return loss def _build_loss_matrix_vectorized(self, adjoint): - # TODO build as csr matrix # TODO comments for this section # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng - # Allocate matrix - loss = np.zeros((n, n)) + # Define rows, columns, and data used to build csr matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + data = np.array([]) # Define net leakage coefficient for each matrix element jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ @@ -1504,7 +1572,9 @@ class CMFDRun(object): idx_y = ng * (coremap_minusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,0] - self._dhat[:,:,:,g,0])[condition] / self._hxyz[0] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusx != _CMFD_NOACCEL) @@ -1512,7 +1582,9 @@ class CMFDRun(object): idx_y = ng * (coremap_plusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,1] + self._dhat[:,:,:,g,1])[condition] / self._hxyz[0] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_minusy != _CMFD_NOACCEL) @@ -1520,7 +1592,9 @@ class CMFDRun(object): idx_y = ng * (coremap_minusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,2] - self._dhat[:,:,:,g,2])[condition] / self._hxyz[1] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusy != _CMFD_NOACCEL) @@ -1528,7 +1602,9 @@ class CMFDRun(object): idx_y = ng * (coremap_plusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,3] + self._dhat[:,:,:,g,3])[condition] / self._hxyz[1] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_minusz != _CMFD_NOACCEL) @@ -1536,7 +1612,9 @@ class CMFDRun(object): idx_y = ng * (coremap_minusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,4] - self._dhat[:,:,:,g,4])[condition] / self._hxyz[1] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) condition = np.logical_and(self._coremap != _CMFD_NOACCEL, coremap_plusz != _CMFD_NOACCEL) @@ -1544,7 +1622,9 @@ class CMFDRun(object): idx_y = ng * (coremap_plusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,5] + self._dhat[:,:,:,g,5])[condition] / self._hxyz[1] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) # Loss of neutrons condition = self._coremap != _CMFD_NOACCEL @@ -1552,18 +1632,26 @@ class CMFDRun(object): idx_y = idx_x vals = (jnet[:,:,:,g] + self._totalxs[:,:,:,g] - \ self._scattxs[:,:,:,g,g])[condition] - loss[idx_x, idx_y] = vals + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) # Off diagonal in-scattering for h in range(ng): - #TODO check for adjoint (see cmfd_loss_operator.F90) if h != g: condition = self._coremap != _CMFD_NOACCEL idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h - vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] - loss[idx_x, idx_y] = vals + if adjoint: + vals = (-1.0 * self._scattxs[:, :, :, g, h])[condition] + else: + vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + # Create csr matrix + loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return loss @@ -1604,38 +1692,37 @@ class CMFDRun(object): # record value in matrix prod[irow, hmat_idx] = val - ''' - print("Prod matrix:") - for i in range(prod.shape[0]): - print_str = "" - for j in range(prod.shape[1]): - if prod[i,j] != 0.0: - print_str += "%.3g\t" % prod[i, j] - else: - print_str += "%.3f\t" % prod[i, j] - print(print_str) - ''' + # Convert matrix to csr matrix in order to use scipy sparse solver + prod = sparse.csr_matrix(prod) + return prod def _build_prod_matrix_vectorized(self, adjoint): # TODO Comments for this section - # TODO Build as csr matrix # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng - # Allocate matrix - prod = np.zeros((n, n)) + # Define rows, columns, and data used to build csr matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + data = np.array([]) for g in range(ng): for h in range(ng): - #TODO check for adjoint (see cmfd_prod_operator.F90) condition = self._coremap != _CMFD_NOACCEL idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h - vals = (self._nfissxs[:, :, :, h, g])[condition] - prod[idx_x, idx_y] = vals + if adjoint: + vals = (self._nfissxs[:, :, :, g, h])[condition] + else: + vals = (self._nfissxs[:, :, :, h, g])[condition] + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + # Create csr matrix + prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return prod def _matrix_to_indices(self, irow, nx, ny, nz, ng): @@ -1684,10 +1771,6 @@ class CMFDRun(object): # Perform Wielandt shift loss -= 1.0/k_s*prod - # Convert matrices to csr matrix in order to use scipy sparse solver - prod = sparse.csr_matrix(prod) - loss = sparse.csr_matrix(loss) - # Begin power iteration for i in range(maxits): # Check if reach max number of iterations @@ -1717,7 +1800,7 @@ class CMFDRun(object): s_o *= k_lo # Check convergence - iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o) + iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1) # If converged, calculate dominance ratio and break from loop if iconv: @@ -1730,7 +1813,7 @@ class CMFDRun(object): k_lo = k_ln norm_o = norm_n - def _check_convergence(self, s_n, s_o, k_n, k_o): + def _check_convergence(self, s_n, s_o, k_n, k_o, iter): # Calculate error in keff kerr = abs(k_o - k_n)/k_n @@ -1740,13 +1823,14 @@ class CMFDRun(object): # Check for convergence iconv = kerr < self._cmfd_ktol and serr < self._cmfd_stol - # TODO Print out to user - #if (cmfd_power_monitor .and. master) then - # write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & - # &1PE12.5,T55, "src-error: ",1PE12.5,T80,I0)') iter, k_n, kerr, & - # serr, innerits + # Print out to user + if self._cmfd_power_monitor and openmc.capi.master(): + str1 = ' {:d}:'.format(iter) + str2 = 'k-eff: {:0.8f}'.format(k_n) + str3 = 'k-error: {0:.5E}'.format(kerr) + str4 = 'src-error: {0:.5E}'.format(serr) + print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) - # Return source error and convergence logical back to solver return iconv, serr def _set_coremap(self): @@ -1875,7 +1959,7 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] # Reshape and extract only p1 data from tally results (no need for p0 data) - p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,:,1] + p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,1] # Store p1 scatter xs # p1 scatter xs is flipped in energy axis as tally results are given in @@ -2401,9 +2485,8 @@ class CMFDRun(object): # Write that dhats are zero if self._dhat_reset and openmc.capi.settings.verbosity >= 8 and \ - openmc.capi.settings.master: + openmc.capi.master(): print(' Dhats reset to zero') - sys.stdout.flush() def _get_reflector_albedo(self, l, g, i, j, k): # Get partial currents from object diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index bd9a974d20..12e83f0e2f 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -7,8 +7,6 @@ module cmfd_solver use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix use matrix_header, only: Matrix use vector_header, only: Vector - use simulation_header, only: current_batch - use string, only: to_str implicit none private @@ -148,8 +146,13 @@ contains call loss % assemble() call prod % assemble() if (cmfd_write_matrices) then - call loss % write('loss_gen' // trim(to_str(current_batch)) // '.dat') - call prod % write('prod_gen' // trim(to_str(current_batch)) // '.dat') + if (adjoint) then + call loss % write('adj_loss.dat') + call prod % write('adj_prod.dat') + else + call loss % write('adj.dat') + call prod % write('adj.dat') + end if end if ! Set norms to 0 @@ -177,8 +180,8 @@ contains ! Write out matrix in binary file (debugging) if (cmfd_write_matrices) then - call loss % write('adj_loss_gen' // trim(to_str(current_batch)) // '.dat') - call prod % write('adj_prod_gen' // trim(to_str(current_batch)) // '.dat') + call loss % write('adj_loss.dat') + call prod % write('adj_prod.dat') end if end subroutine compute_adjoint @@ -193,6 +196,7 @@ contains use constants, only: ONE use error, only: fatal_error use cmfd_header, only: cmfd, cmfd_atoli, cmfd_rtoli + use simulation_header, only: current_batch integer :: i ! iteration counter integer :: innerits ! # of inner iterations @@ -738,9 +742,9 @@ contains ! Write out results if (cmfd_write_matrices) then if (adjoint_calc) then - filename = 'adj_fluxvec_gen' // trim(to_str(current_batch)) // '.dat' + filename = 'adj_fluxvec.dat' else - filename = 'fluxvec_gen' // trim(to_str(current_batch)) // '.dat' + filename = 'fluxvec.dat' end if call phi_n % write(filename) end if diff --git a/src/settings.F90 b/src/settings.F90 index 972eba1d58..0bd44c7364 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -60,7 +60,7 @@ module settings logical :: pred_batches = .false. ! predict batches for triggers logical :: trigger_on = .false. ! flag for turning triggers on/off - logical :: entropy_on = .false. + logical(C_BOOL), bind(C, name='openmc_entropy_on') :: entropy_on = .false. integer :: index_entropy_mesh = -1 logical :: ufs = .false. From 72462666938cde83aa19eccac277350f06c24618 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 30 Aug 2018 14:08:00 -0400 Subject: [PATCH 27/58] Solve for general hxyz, output cmfd timing stats --- openmc/cmfd.py | 193 +++++++++++++++++++++++++----------------- src/cmfd_solver.F90 | 1 - src/vector_header.F90 | 2 +- 3 files changed, 117 insertions(+), 79 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index d19b511cd6..eb5c4739de 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -936,7 +936,6 @@ class CMFDRun(object): # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd() - # TODO comment # Run everything in next batch after executing CMFD. Status # now determines whether another batch should be run or @@ -952,8 +951,8 @@ class CMFDRun(object): # Finalize simuation openmc.capi.simulation_finalize() - # TODO print out timing statistics - #print("yo") + # Print out timing statistics + self._write_cmfd_timing_stats() # Finalize and free memory openmc.capi.finalize() @@ -973,6 +972,17 @@ class CMFDRun(object): print('{0:>76s}\n{1:>76s}'.format(str1, str2)) sys.stdout.flush() + def _write_cmfd_timing_stats(self): + # TODO Comment + print( +"""=====================> CMFD TIMING STATISTICS <==================== + + Time in CMFD = {0:.5E} seconds + Building matrices = {1:.5E} seconds + Solving matrices = {2:.5E} seconds +""".format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve)) + sys.stdout.flush() + def _configure_cmfd(self): # Read in cmfd input defined in Python self._read_cmfd_input() @@ -1053,9 +1063,8 @@ class CMFDRun(object): self._dtilde = np.zeros((nx, ny, nz, ng, 6)) self._dhat = np.zeros((nx, ny, nz, ng, 6)) - # Allocate dimensions for each box (assume fixed mesh dimensions) - # TODO Update this - self._hxyz = np.zeros((3)) + # Allocate dimensions for each mesh cell + self._hxyz = np.zeros((nx, ny, nz, 3)) # Allocate surface currents self._current = np.zeros((nx, ny, nz, ng, 12)) @@ -1237,7 +1246,7 @@ class CMFDRun(object): # TODO Comment for vectorized vs. not-vectorized if vectorized: # Calculate volume - vol = np.product(self._hxyz) + vol = np.product(self._hxyz, axis=3) # Reshape phi by number of groups phi = self._phi.reshape((n, ng)) @@ -1256,7 +1265,8 @@ class CMFDRun(object): # Compute fission source cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ - cmfd_flux[:,:,:,:,np.newaxis], axis=3) * vol + cmfd_flux[:,:,:,:,np.newaxis], axis=3) * \ + vol[:,:,:,np.newaxis] else: cmfd_src = np.zeros((nx, ny, nz, ng)) @@ -1269,13 +1279,13 @@ class CMFDRun(object): continue # Calculate volume - vol = np.product(self._hxyz) + vol = np.product(self._hxyz[i,j,k]) # Get index in matrix idx = self._indices_to_matrix(i, j, k, 0, ng) # Compute fission source - cmfd_src2[i,j,k,g] = np.sum( + cmfd_src[i,j,k,g] = np.sum( self._nfissxs[i,j,k,:,g] \ * self._phi[idx:idx+ng]) * vol @@ -1449,7 +1459,7 @@ class CMFDRun(object): totxs = self._totalxs[i,j,k,g] scattxsgg = self._scattxs[i,j,k,g,g] dtilde = self._dtilde[i,j,k,g,:] - hxyz = self._hxyz + hxyz = self._hxyz[i,j,k,:] dhat = self._dhat[i,j,k,g,:] # Create boundary vector @@ -1533,13 +1543,13 @@ class CMFDRun(object): # Define net leakage coefficient for each matrix element jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ (-1.0 * self._dtilde[:,:,:,:,0] + self._dhat[:,:,:,:,0])) / \ - self._hxyz[0] + \ + self._hxyz[:,:,:,np.newaxis,0] + \ ((1.0 * self._dtilde[:,:,:,:,3] + self._dhat[:,:,:,:,3]) - \ (-1.0 * self._dtilde[:,:,:,:,2] + self._dhat[:,:,:,:,2])) / \ - self._hxyz[1] + \ + self._hxyz[:,:,:,np.newaxis,1] + \ ((1.0 * self._dtilde[:,:,:,:,5] + self._dhat[:,:,:,:,5]) - \ (-1.0 * self._dtilde[:,:,:,:,4] + self._dhat[:,:,:,:,4])) / \ - self._hxyz[2] + self._hxyz[:,:,:,np.newaxis,2] # Shift coremap in all directions to determine whether leakage term # should be defined for particular cell in matrix @@ -1571,7 +1581,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_minusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,0] - - self._dhat[:,:,:,g,0])[condition] / self._hxyz[0] + self._dhat[:,:,:,g,0])[condition] / \ + self._hxyz[:,:,:,0][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1581,7 +1592,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_plusx[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,1] + - self._dhat[:,:,:,g,1])[condition] / self._hxyz[0] + self._dhat[:,:,:,g,1])[condition] / \ + self._hxyz[:,:,:,0][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1591,7 +1603,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_minusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,2] - - self._dhat[:,:,:,g,2])[condition] / self._hxyz[1] + self._dhat[:,:,:,g,2])[condition] / \ + self._hxyz[:,:,:,1][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1601,7 +1614,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_plusy[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,3] + - self._dhat[:,:,:,g,3])[condition] / self._hxyz[1] + self._dhat[:,:,:,g,3])[condition] / \ + self._hxyz[:,:,:,1][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1611,7 +1625,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_minusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,4] - - self._dhat[:,:,:,g,4])[condition] / self._hxyz[1] + self._dhat[:,:,:,g,4])[condition] / \ + self._hxyz[:,:,:,2][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1621,7 +1636,8 @@ class CMFDRun(object): idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (coremap_plusz[condition]) + g vals = (-1.0 * self._dtilde[:,:,:,g,5] + - self._dhat[:,:,:,g,5])[condition] / self._hxyz[1] + self._dhat[:,:,:,g,5])[condition] / \ + self._hxyz[:,:,:,2][condition] row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1852,8 +1868,7 @@ class CMFDRun(object): self._openmc_src.fill(0.) # Set mesh widths - self._hxyz = openmc.capi.meshes[self._cmfd_mesh_id].width - # self._hxyz[:,:,:,] = openmc.capi.meshes[self._cmfd_mesh_id].width + self._hxyz[:,:,:,:] = openmc.capi.meshes[self._cmfd_mesh_id].width # Reset keff_bal to zero self._keff_bal = 0. @@ -2065,57 +2080,63 @@ class CMFDRun(object): self._dtilde[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], np.where(is_zero_flux_alb[0], 2.0 * self._diffcof[0,:,:,:] / \ - self._hxyz[0], + self._hxyz[0,:,:,np.newaxis,0], (2.0 * self._diffcof[0,:,:,:] * \ (1.0 - self._albedo[0])) / \ (4.0 * self._diffcof[0,:,:,:] * \ (1.0 + self._albedo[0]) + \ - (1.0 - self._albedo[0]) * self._hxyz[0])), 0) + (1.0 - self._albedo[0]) * \ + self._hxyz[0,:,:,np.newaxis,0])), 0) self._dtilde[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], np.where(is_zero_flux_alb[1], 2.0 * self._diffcof[-1,:,:,:] / \ - self._hxyz[0], + self._hxyz[-1,:,:,np.newaxis,0], (2.0 * self._diffcof[-1,:,:,:] * \ (1.0 - self._albedo[1])) / \ (4.0 * self._diffcof[-1,:,:,:] * \ (1.0 + self._albedo[1]) + \ - (1.0 - self._albedo[1]) * self._hxyz[0])), 0) + (1.0 - self._albedo[1]) * \ + self._hxyz[-1,:,:,np.newaxis,0])), 0) self._dtilde[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], np.where(is_zero_flux_alb[2], 2.0 * self._diffcof[:,0,:,:] / \ - self._hxyz[1], + self._hxyz[:,0,:,np.newaxis,1], (2.0 * self._diffcof[:,0,:,:] * \ (1.0 - self._albedo[2])) / \ (4.0 * self._diffcof[:,0,:,:] * \ (1.0 + self._albedo[2]) + \ - (1.0 - self._albedo[2]) * self._hxyz[1])), 0) + (1.0 - self._albedo[2]) * \ + self._hxyz[:,0,:,np.newaxis,1])), 0) self._dtilde[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], np.where(is_zero_flux_alb[3], 2.0 * self._diffcof[:,-1,:,:] / \ - self._hxyz[1], + self._hxyz[:,-1,:,np.newaxis,1], (2.0 * self._diffcof[:,-1,:,:] * \ (1.0 - self._albedo[3])) / \ (4.0 * self._diffcof[:,-1,:,:] * \ (1.0 + self._albedo[3]) + \ - (1.0 - self._albedo[3]) * self._hxyz[1])), 0) + (1.0 - self._albedo[3]) * \ + self._hxyz[:,-1,:,np.newaxis,1])), 0) self._dtilde[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], np.where(is_zero_flux_alb[4], 2.0 * self._diffcof[:,:,0,:] / \ - self._hxyz[2], + self._hxyz[:,:,0,np.newaxis,2], (2.0 * self._diffcof[:,:,0,:] * \ (1.0 - self._albedo[4])) / \ (4.0 * self._diffcof[:,:,0,:] * \ (1.0 + self._albedo[4]) + \ - (1.0 - self._albedo[4]) * self._hxyz[2])), 0) + (1.0 - self._albedo[4]) * \ + self._hxyz[:,:,0,np.newaxis,2])), 0) self._dtilde[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], np.where(is_zero_flux_alb[5], 2.0 * self._diffcof[:,:,-1,:] / \ - self._hxyz[2], + self._hxyz[:,:,-1,np.newaxis,2], (2.0 * self._diffcof[:,:,-1,:] * \ (1.0 - self._albedo[5])) / \ (4.0 * self._diffcof[:,:,-1,:] * \ (1.0 + self._albedo[5]) + \ - (1.0 - self._albedo[5]) * self._hxyz[2])), 0) + (1.0 - self._albedo[5]) * \ + self._hxyz[:,:,-1,np.newaxis,2])), 0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_left']], self._current[:,:,:,:,_CURRENTS['out_left']], @@ -2123,7 +2144,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_left']])) adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, 1, axis=0) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, 1, axis=0) self._dtilde[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], @@ -2131,10 +2152,11 @@ class CMFDRun(object): (1.0 - ref_albedo[1:,:,:,:])) / \ (4.0 * self._diffcof[1:,:,:,:] * \ (1.0 + ref_albedo[1:,:,:,:]) + \ - (1.0 - ref_albedo[1:,:,:,:]) * self._hxyz[0]), + (1.0 - ref_albedo[1:,:,:,:]) * \ + self._hxyz[1:,:,:,np.newaxis,0]), \ (2.0 * self._diffcof[1:,:,:,:] * neig_dc[1:,:,:,:]) / \ - (self._hxyz[0] * self._diffcof[1:,:,:,:] + \ - self._hxyz[0] * neig_dc[1:,:,:,:])), 0.0) + (neig_hxyz[1:,:,:,np.newaxis,0] * self._diffcof[1:,:,:,:] + \ + self._hxyz[1:,:,:,np.newaxis,0] * neig_dc[1:,:,:,:])), 0.0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_right']], self._current[:,:,:,:,_CURRENTS['out_right']], @@ -2142,7 +2164,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_right']])) adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, -1, axis=0) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, -1, axis=0) self._dtilde[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], @@ -2150,10 +2172,11 @@ class CMFDRun(object): (1.0 - ref_albedo[:-1,:,:,:])) / \ (4.0 * self._diffcof[:-1,:,:,:] * \ (1.0 + ref_albedo[:-1,:,:,:]) + \ - (1.0 - ref_albedo[:-1,:,:,:]) * self._hxyz[0]), + (1.0 - ref_albedo[:-1,:,:,:]) * \ + self._hxyz[:-1,:,:,np.newaxis,0]), (2.0 * self._diffcof[:-1,:,:,:] * neig_dc[:-1,:,:,:]) / \ - (self._hxyz[0] * self._diffcof[:-1,:,:,:] + \ - self._hxyz[0] * neig_dc[:-1,:,:,:])), 0.0) + (neig_hxyz[:-1,:,:,np.newaxis,0] * self._diffcof[:-1,:,:,:] + \ + self._hxyz[:-1,:,:,np.newaxis,0] * neig_dc[:-1,:,:,:])), 0.0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_back']], self._current[:,:,:,:,_CURRENTS['out_back']], @@ -2161,7 +2184,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_back']])) adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, 1, axis=1) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, 1, axis=1) self._dtilde[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], @@ -2169,10 +2192,11 @@ class CMFDRun(object): (1.0 - ref_albedo[:,1:,:,:])) / \ (4.0 * self._diffcof[:,1:,:,:] * \ (1.0 + ref_albedo[:,1:,:,:]) + \ - (1.0 - ref_albedo[:,1:,:,:]) * self._hxyz[1]), + (1.0 - ref_albedo[:,1:,:,:]) * \ + self._hxyz[:,1:,:,np.newaxis,1]), (2.0 * self._diffcof[:,1:,:,:] * neig_dc[:,1:,:,:]) / \ - (self._hxyz[1] * self._diffcof[:,1:,:,:] + \ - self._hxyz[1] * neig_dc[:,1:,:,:])), 0.0) + (neig_hxyz[:,1:,:,np.newaxis,1] * self._diffcof[:,1:,:,:] + \ + self._hxyz[:,1:,:,np.newaxis,1] * neig_dc[:,1:,:,:])), 0.0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_front']], self._current[:,:,:,:,_CURRENTS['out_front']], @@ -2180,7 +2204,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_front']])) adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, -1, axis=1) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, -1, axis=1) self._dtilde[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], @@ -2188,10 +2212,11 @@ class CMFDRun(object): (1.0 - ref_albedo[:,:-1,:,:])) / \ (4.0 * self._diffcof[:,:-1,:,:] * \ (1.0 + ref_albedo[:,:-1,:,:]) + \ - (1.0 - ref_albedo[:,:-1,:,:]) * self._hxyz[1]), + (1.0 - ref_albedo[:,:-1,:,:]) * \ + self._hxyz[:,:-1,:,np.newaxis,1]), (2.0 * self._diffcof[:,:-1,:,:] * neig_dc[:,:-1,:,:]) / \ - (self._hxyz[1] * self._diffcof[:,:-1,:,:] + \ - self._hxyz[1] * neig_dc[:,:-1,:,:])), 0.0) + (neig_hxyz[:,:-1,:,np.newaxis,1] * self._diffcof[:,:-1,:,:] + \ + self._hxyz[:,:-1,:,np.newaxis,1] * neig_dc[:,:-1,:,:])), 0.0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_bottom']], self._current[:,:,:,:,_CURRENTS['out_bottom']], @@ -2199,7 +2224,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_bottom']])) adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, 1, axis=2) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, 1, axis=2) self._dtilde[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], @@ -2207,10 +2232,11 @@ class CMFDRun(object): (1.0 - ref_albedo[:,:,1:,:])) / \ (4.0 * self._diffcof[:,:,1:,:] * \ (1.0 + ref_albedo[:,:,1:,:]) + \ - (1.0 - ref_albedo[:,:,1:,:]) * self._hxyz[2]), + (1.0 - ref_albedo[:,:,1:,:]) * \ + self._hxyz[:,:,1:,np.newaxis,2]), (2.0 * self._diffcof[:,:,1:,:] * neig_dc[:,:,1:,:]) / \ - (self._hxyz[2] * self._diffcof[:,:,1:,:] + \ - self._hxyz[2] * neig_dc[:,:,1:,:])), 0.0) + (neig_hxyz[:,:,1:,np.newaxis,2] * self._diffcof[:,:,1:,:] + \ + self._hxyz[:,:,1:,np.newaxis,2] * neig_dc[:,:,1:,:])), 0.0) ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_top']], self._current[:,:,:,:,_CURRENTS['out_top']], @@ -2218,7 +2244,7 @@ class CMFDRun(object): out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_top']])) adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL neig_dc = np.roll(self._diffcof, -1, axis=2) - # Define neg_hxyz + neig_hxyz = np.roll(self._hxyz, -1, axis=2) self._dtilde[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], @@ -2226,10 +2252,11 @@ class CMFDRun(object): (1.0 - ref_albedo[:,:,:-1,:])) / \ (4.0 * self._diffcof[:,:,:-1,:] * \ (1.0 + ref_albedo[:,:,:-1,:]) + \ - (1.0 - ref_albedo[:,:,:-1,:]) * self._hxyz[2]), + (1.0 - ref_albedo[:,:,:-1,:]) * \ + self._hxyz[:,:,:-1,np.newaxis,2]), (2.0 * self._diffcof[:,:,:-1,:] * neig_dc[:,:,:-1,:]) / \ - (self._hxyz[2] * self._diffcof[:,:,:-1,:] + \ - self._hxyz[2] * neig_dc[:,:,:-1,:])), 0.0) + (neig_hxyz[:,:,:-1,np.newaxis,2] * self._diffcof[:,:,:-1,:] + \ + self._hxyz[:,:,:-1,np.newaxis,2] * neig_dc[:,:,:-1,:])), 0.0) def _compute_dtilde(self): # Get maximum of spatial and group indices @@ -2255,7 +2282,7 @@ class CMFDRun(object): # Get cell data cell_dc = self._diffcof[i,j,k,g] - cell_hxyz = self._hxyz + cell_hxyz = self._hxyz[i,j,k,:] # Setup of vector to identify boundary conditions bound = np.repeat([i,j,k], 2) @@ -2285,6 +2312,7 @@ class CMFDRun(object): # Get neighbor cell data neig_dc = self._diffcof[tuple(neig_idx) + (g,)] + neig_hxyz = self._hxyz[tuple(neig_idx)] # Check for fuel-reflector interface if (self._coremap[tuple(neig_idx)] == @@ -2296,7 +2324,7 @@ class CMFDRun(object): else: # Not next to a reflector # Compute dtilde to neighbor cell - dtilde = (2*cell_dc*neig_dc)/(cell_hxyz[xyz_idx]*cell_dc + \ + dtilde = (2*cell_dc*neig_dc)/(neig_hxyz[xyz_idx]*cell_dc + \ cell_hxyz[xyz_idx]*neig_dc) # Record dtilde @@ -2306,24 +2334,30 @@ class CMFDRun(object): # TODO Write comments for this function net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ self._current[:,:,:,:,_CURRENTS['out_left']]) / \ - np.prod(self._hxyz)*self._hxyz[0]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,0]) net_current_plusx = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ self._current[:,:,:,:,_CURRENTS['in_right']]) / \ - np.prod(self._hxyz)*self._hxyz[0]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,0]) net_current_minusy = ((self._current[:,:,:,:,_CURRENTS['in_back']] - \ self._current[:,:,:,:,_CURRENTS['out_back']]) / \ - np.prod(self._hxyz)*self._hxyz[1]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,1]) net_current_plusy = ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ self._current[:,:,:,:,_CURRENTS['in_front']]) / \ - np.prod(self._hxyz)*self._hxyz[1]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,1]) net_current_minusz = ((self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ self._current[:,:,:,:,_CURRENTS['out_bottom']]) / \ - np.prod(self._hxyz)*self._hxyz[2]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,2]) net_current_plusz = ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ self._current[:,:,:,:,_CURRENTS['in_top']]) / \ - np.prod(self._hxyz)*self._hxyz[2]) + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ + self._hxyz[:,:,:,np.newaxis,2]) - cell_flux = self._flux / np.prod(self._hxyz) + cell_flux = self._flux / np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] is_accel = self._coremap != _CMFD_NOACCEL self._dhat[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], @@ -2347,7 +2381,8 @@ class CMFDRun(object): # Minus x direction adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, 1, axis=0) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, 1, axis=0) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], (net_current_minusx[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ @@ -2358,7 +2393,8 @@ class CMFDRun(object): # Plus x direction adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, -1, axis=0) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, -1, axis=0) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], (net_current_plusx[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ @@ -2369,7 +2405,8 @@ class CMFDRun(object): # Minus y direction adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, 1, axis=1) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, 1, axis=1) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], (net_current_minusy[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ @@ -2380,7 +2417,8 @@ class CMFDRun(object): # Plus y direction adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, -1, axis=1) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, -1, axis=1) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], (net_current_plusy[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ @@ -2391,7 +2429,8 @@ class CMFDRun(object): # Minus z direction adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, 1, axis=2) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, 1, axis=2) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], (net_current_minusz[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ @@ -2402,7 +2441,8 @@ class CMFDRun(object): # Plus z direction adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - neig_flux = np.roll(self._flux, -1, axis=2) / np.prod(self._hxyz) + neig_flux = np.roll(self._flux, -1, axis=2) / \ + np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] self._dhat[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], (net_current_plusz[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ @@ -2412,7 +2452,6 @@ class CMFDRun(object): (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) def _compute_dhat(self): - #TODO compute dhat and dtilde for general case with hxyz (just define as repeated but use in formulas) # Get maximum of spatial and group indices nx = self._indices[0] ny = self._indices[1] @@ -2433,7 +2472,7 @@ class CMFDRun(object): # Get cell data cell_dtilde = self._dtilde[i,j,k,g,:] - cell_flux = self._flux[i,j,k,g]/np.product(self._hxyz) + cell_flux = self._flux[i,j,k,g]/np.product(self._hxyz[i,j,k,:]) current = self._current[i,j,k,g,:] # Setup of vector to identify boundary conditions @@ -2447,7 +2486,7 @@ class CMFDRun(object): # Calculate net current on l face (divided by surf area) net_current = shift_idx*(current[2*l] - current[2*l+1]) / \ - np.product(self._hxyz) * self._hxyz[xyz_idx] + np.product(self._hxyz[i,j,k,:]) * self._hxyz[i,j,k,xyz_idx] # Check if at boundary if bound[l] == nxyz[xyz_idx, dir_idx]: @@ -2462,7 +2501,7 @@ class CMFDRun(object): # Get neigbor flux neig_flux = self._flux[tuple(neig_idx)+(g,)] / \ - np.product(self._hxyz) + np.product(self._hxyz[tuple(neig_idx)]) # Check for fuel-reflector interface if (self._coremap[tuple(neig_idx)] == diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index 12e83f0e2f..7f0eb965a2 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -196,7 +196,6 @@ contains use constants, only: ONE use error, only: fatal_error use cmfd_header, only: cmfd, cmfd_atoli, cmfd_rtoli - use simulation_header, only: current_batch integer :: i ! iteration counter integer :: innerits ! # of inner iterations diff --git a/src/vector_header.F90 b/src/vector_header.F90 index 475e4295e5..4054863d80 100644 --- a/src/vector_header.F90 +++ b/src/vector_header.F90 @@ -89,7 +89,7 @@ contains end subroutine vector_copy !=============================================================================== -! VECTOR_WRITE write a vector to file +! VECTOR_WRITE writes a vector to file !=============================================================================== subroutine vector_write(self, filename) From e8176efa36bfddc453927f616a4b90de1384ce3d Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 6 Sep 2018 19:42:02 -0400 Subject: [PATCH 28/58] Remove functionality to handle restart batches, add comments for all CMFD instance variables and methods --- openmc/capi/core.py | 29 +- openmc/capi/settings.py | 2 - openmc/cmfd.py | 944 +++++++++++++++++++++++++++++++------- src/simulation.F90 | 15 +- src/simulation_header.F90 | 2 +- 5 files changed, 800 insertions(+), 192 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 73a3454af1..d09c93963c 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -42,7 +42,7 @@ _dll.openmc_next_batch.errcheck = _error_handler _dll.openmc_next_batch_before_cmfd_init.argtypes = [] _dll.openmc_next_batch_before_cmfd_init.restype = c_int _dll.openmc_next_batch_before_cmfd_init.errcheck = _error_handler -_dll.openmc_next_batch_between_cmfd_init_execute.argtypes = [POINTER(c_int)] +_dll.openmc_next_batch_between_cmfd_init_execute.argtypes = [] _dll.openmc_next_batch_between_cmfd_init_execute.restype = c_int _dll.openmc_next_batch_between_cmfd_init_execute.errcheck = _error_handler _dll.openmc_next_batch_after_cmfd_execute.argtypes = [POINTER(c_int)] @@ -231,6 +231,23 @@ def keff(): return (mean, std_dev) +# TODO Remove +def keff_temp(): + """Return the calculated tracklength k-eigenvalue and its standard deviation. + + Returns + ------- + tuple + Mean k-eigenvalue and standard deviation of the mean + + """ + n = openmc.capi.num_realizations() + mean = c_double.in_dll(_dll, 'openmc_keff').value + std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \ + if n > 1 else np.inf + return (mean, std_dev) + + def master(): """Return whether processor is master processor or not. @@ -267,16 +284,8 @@ def next_batch_between_cmfd_init_execute(): """Run everything in batch that occurs between CMFD initialization and execution. - Returns - ------- - int - Status after running block (0=batch is part of restart batch so don't - run CMFD, 3=normal execution, continue with CMFD solver) - """ - status = c_int() - _dll.openmc_next_batch_between_cmfd_init_execute(status) - return status.value + _dll.openmc_next_batch_between_cmfd_init_execute() def next_batch_after_cmfd_execute(): diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index 55b2080227..9e95935fb8 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -22,8 +22,6 @@ class _Settings(object): generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') particles = _DLLGlobal(c_int64, 'n_particles') - restart_batch = _DLLGlobal(c_int, 'openmc_restart_batch') - restart_run = _DLLGlobal(c_bool, 'openmc_restart_run') verbosity = _DLLGlobal(c_int, 'openmc_verbosity') @property diff --git a/openmc/cmfd.py b/openmc/cmfd.py index eb5c4739de..b5147db8af 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -15,10 +15,12 @@ from numbers import Real, Integral from xml.etree import ElementTree as ET # TODO Remove import sys import numpy as np -# TODO Include comment for this +# Line below is added to suppress warnings when using numpy.divide to +# divide by numpy arrays that contain entries with zero np.seterr(divide='ignore', invalid='ignore') from scipy import sparse import time +# See if mpi4py module can be imported, define have_mpi global variable try: from mpi4py import MPI have_mpi = True @@ -32,7 +34,6 @@ from openmc.checkvalue import (check_type, check_length, check_value, from openmc.exceptions import OpenMCError - """ -------------- CMFD CONSTANTS @@ -51,7 +52,7 @@ _CMFD_NOACCEL = 99999 # Constant to represent a zero flux "albedo" _ZERO_FLUX = 999.0 -# Map that returns index of current direction in current matrix +# Map that returns index of current direction in numpy current matrix _CURRENTS = { 'out_left' : 0, 'in_left' : 1, 'out_right': 2, 'in_right': 3, 'out_back' : 4, 'in_back' : 5, 'out_front': 6, 'in_front': 7, @@ -99,7 +100,7 @@ class CMFDMesh(object): Therefore a 2x2 system of equations is solved rather than a 4x4. This is extremely important to use in reflectors as neutrons will not contribute - to any tallies far away from fission source neutron regions. A ``2`` + to any tallies far away from fission source neutron regions. A ``1`` must be used to identify any fission source region. """ @@ -192,7 +193,7 @@ class CMFDMesh(object): check_value('CMFD mesh map', m, [0, 1]) self._map = meshmap - # REMOVE + # REMOVE this method def _get_xml_element(self): element = ET.Element("mesh") @@ -225,6 +226,7 @@ class CMFDMesh(object): return element +# REMOVE this entire class class CMFD(object): r"""Parameters that control the use of coarse-mesh finite difference acceleration in OpenMC. This corresponds directly to the cmfd.xml input file. @@ -604,54 +606,184 @@ class CMFD(object): class CMFDRun(object): r"""Class to run openmc with CMFD acceleration through the C API. Running - openmc through this manner obviates the need for defining CMFD parameters - through a cmfd.xml file. Instead, all input parameters should be passed through - the CMFDRun initializer. - + openmc in this manner obviates the need for defining CMFD parameters + through a cmfd.xml file. Instead, all input parameters should be passed + through the CMFDRun instance. Attributes ---------- - To add: indices: Stores spatial and group dimensions as [nx, ny, nz, ng] - egrid: energy grid used for CMFD acceleration - albedo: Albedo for global boundary conditions, taken from CMFD mesh. Set to [1,1,1,1,1,1] if not specified by user - n_cmfd_resets: Number of elements in tally_reset, list that stores batches where CMFD tallies should be reset - cmfd_mesh_id: Mesh id of openmc.capi.Mesh object that corresponds to the CMFD mesh - cmfd_tally_ids: list of ids corresponding to CMFD tallies (details:) - energy_filters: Boolean that stores whether energy filters should be created or not. - Set to true if user specifies energy grid in CMFDMesh, false otherwise - cmfd_on: Boolean to tell if cmfd solver should be initiated, based on whether current batch has reached variable - cmfd_begin - # Look at cmfd_header.F90 for description - flux - totalxs - p1scattxs - scattxs - nfissxs - diffcof - dtilde - dhat - hxyz: mesh width - current - cmfd_src - openmc_src - sourcecounts - weightfactors - entropy - balance - src_cmp - dom - k_cmfd - keff_bal - mat_dim + cmfd_begin : int + Batch number at which CMFD calculations should begin + dhat_reset : bool + Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should be + reset to zero before solving CMFD eigenproblem. + cmfd_display : {'balance', 'dominance', 'entropy', 'source'} + Set one additional CMFD output column. Options are: + + * "balance" - prints the RMS [%] of the resdiual from the neutron balance + equation on CMFD tallies. + * "dominance" - prints the estimated dominance ratio from the CMFD + iterations. + * "entropy" - prints the *entropy* of the CMFD predicted fission source. + * "source" - prints the RMS [%] between the OpenMC fission source and + CMFD fission source. + cmfd_downscatter : bool + Indicate whether an effective downscatter cross section should be used + when using 2-group CMFD. + cmfd_feedback : bool + Indicate or not the CMFD diffusion result is used to adjust the weight + of fission source neutrons on the next OpenMC batch. Defaults to False. + cmfd_ktol : float + Tolerance on the eigenvalue when performing CMFD power iteration + cmfd_mesh : openmc.CMFDMesh + Structured mesh to be used for acceleration + norm : float + Normalization factor applied to the CMFD fission source distribution + cmfd_power_monitor : bool + View convergence of power iteration during CMFD acceleration + cmfd_run_adjoint : bool + Perform adjoint calculation on the last batch + cmfd_shift : float + Optional Wielandt shift parameter for accelerating power iterations. By + default, it is very large so there is effectively no impact. + cmfd_stol : float + Tolerance on the fission source when performing CMFD power iteration + cmfd_reset : list of int + List of batch numbers at which CMFD tallies should be reset + cmfd_write_matrices : bool + # TODO update this to read "resultant normalized flux vector" + Write sparse matrices that are used during CMFD acceleration (loss, + production) and resultant flux vector phi to file + indices : numpy.ndarray + Stores spatial and group dimensions as [nx, ny, nz, ng] + egrid : numpy.ndarray + Energy grid used for CMFD acceleration + albedo : numpy.ndarray + Albedo for global boundary conditions, taken from CMFD mesh. It is + listed in the following order: [-x +x -y +y -z +z]. Set to + [1, 1, 1, 1, 1, 1] if not specified by user. + coremap : numpy.ndarray + Coremap for coarse mesh overlay, defining acceleration regions in CMFD + problem, created from map variable in CMFD class. Each accelerated + region is given a unique index to map each spatial mesh to a + corresponding row within the CMFD matrices. Non-accelerated regions are + set with value _CMFD_NOACCEL. If a user does not specify a CMFD map, + coremap is set to treat each spatial mesh as an accelerated region. + For non-accelerated regions on coarse mesh overlay + n_cmfd_resets : int + Number of elements in cmfd_reset to store number of times cmfd tallies + will be reset. + cmfd_mesh_id : int + Mesh id of CMFD mesh, stored to access CMFD mesh object in memory + cmfd_tally_ids : list of ints + List that stores the id's of all CMFD tallies. The tallies that each + id corresponds to are: + + * cmfd_tally_id[0] - CMFD flux, total tally + * cmfd_tally_id[1] - CMFD neutron production tally + * cmfd_tally_id[2] - CMFD surface current tally + * cmfd_tally_id[3] - CMFD P1 scatter tally + energy_filters : bool + Stores whether energy filters should be created or not, based on whether + a user specifies an energy grid + cmfd_on : bool + Stores whether cmfd solver should be invoked, based on whether current + batch has reached variable ``cmfd_begin`` + mat_dim : int + Number of accelerated regions exist in problem. Value of ``mat_dim`` is + tied to size of CMFD matrices + keff_bal : float + Balance k-effective computed from OpenMC source + cmfd_adjoint_type : {'physical', 'math'} + Stores type of adjoint calculation that should be performed. + ``cmfd_run_adjoint`` must be true for an adjoint calculation to be + perfomed. Options are: + + * "physical" - Create adjoint matrices from physical parameters of + CMFD problem + * "math" - Create adjoint matrices mathematically as the transpose of + loss and production CMFD matrices + keff : float + K-effective from solving CMFD matrix equations + adj_keff : float + K-effective from solving adjoint CMFD matrix equations + phi : numpy.ndarray + Final flux vector from solving CMFD matrix equations + adj_phi : numpy.ndarray + Final flux vector from solving adjoint CMFD matrix equations + flux : numpy.ndarray + Flux computed from tally data + totalxs : numpy.ndarray + Total cross section computed from tally data + p1scattxs : numpy.ndarray + P1 scattering cross section computed from tally data + scattxs : numpy.ndarray + Scattering cross section computed from tally data + nfissxs : numpy.ndarray + Nu-fission cross section computed from tally data + diffcof : numpy.ndarray + Diffusion coefficients computed from tally data + dtilde : numpy.ndarray + Array of diffusion coupling coefficients + dhat : numpy.ndarray + Array of nonlinear coupling coefficients + hxyz : numpy.ndarray + Dimensions of mesh cells, stored as (xloc,yloc,zloc,[hu,hv,hw]) + current : numpy.ndarray + Surface currents for each mesh cell in each incoming and outgoing + direction on each mesh surface, computed from tally data + cmfd_src : numpy.ndarray + CMFD source distribution calculated from solving CMFD equations + openmc_src : numpy.ndarray + OpenMC source distribution computed from tally data + sourcecounts : numpy.ndarray + Number of neutrons in each spatial and energy group, used to calculate + normalizing factors and weight factors when reweighting each neutron + weightfactors : numpy.ndarray + Weight factors for each spatial and energy group, used to reweight + neutrons at the end of each batch + entropy : list of floats + "Shannon entropy" from cmfd fission source, stored for each generation + that CMFD is invoked + balance : list of floats + RMS of neutron balance equations, stored for each generation that CMFD is + invoked + src_cmp : list of floats + RMS deviation of OpenMC and CMFD normalized source, stored for each + generation that CMFD is invoked + dom : list of floats + Dominance ratio from solving CMFD matrix equations, stored for each + generation that CMFD is invoked + k_cmfd : list of floats + List of CMFD k-effectives, stored for each generation that CMFD is + invoked + resnb : numpy.ndarray + Residual from solving neutron balance equations + time_cmfd : float + Time for entire CMFD calculation, in seconds + time_cmfdbuild : float + Time for building CMFD matrices, in seconds + time_cmfdsolve : float + Time for solving CMFD matrix equations, in seconds + intracomm : mpi4py.MPI.Intracomm or None + MPI intercommunicator for running MPI commands + + TODO Remove CMFD constants, timing variables in Fortran + TODO Remove cmfd_data.F90, cmfd_execute.F90, cmfd_header.F90, + cmfd_input.F90, cmfd_loss_operator.F90, cmfd_prod_operator.F90, + cmfd_solver.F90 + TODO Remove instances of cmfd in sourcepoint.F90, output.F90, + simulation.F90, api.F90, settings.F90, input_xml.F90, plots.F90 - TODO: Put descriptions for all methods in CMFDRun - TODO Get rid of CMFD constants """ def __init__(self): + """Constructor for CMFDRun class. Default values for instance variables + set in this method. - # Set CMFD default parameters based on cmfd_header.F90 - # Input parameters that user can define + """ + + # Variables that users can modify self._cmfd_begin = 1 self._dhat_reset = False self._cmfd_display = 'balance' @@ -667,14 +799,13 @@ class CMFDRun(object): self._cmfd_reset = [] self._cmfd_write_matrices = False - # External variables used during runtime but users don't have control over + # External variables used during runtime but users cannot control self._indices = np.zeros(4, dtype=int) self._egrid = None self._albedo = None self._coremap = None self._n_cmfd_resets = 0 self._cmfd_mesh_id = None - self._cmfd_filter_ids = None self._cmfd_tally_ids = None self._energy_filters = None self._cmfd_on = False @@ -685,8 +816,6 @@ class CMFDRun(object): self._adj_keff = None self._phi = None self._adj_phi = None - - # Numpy arrays used to build CMFD matrices self._flux = None self._totalxs = None self._p1scattxs = None @@ -697,7 +826,6 @@ class CMFDRun(object): self._dhat = None self._hxyz = None self._current = None - self._cmfd_src = None self._openmc_src = None self._sourcecounts = None @@ -708,11 +836,9 @@ class CMFDRun(object): self._dom = None self._k_cmfd = None self._resnb = None - self._time_cmfd = None self._time_cmfdbuild = None self._time_cmfdsolve = None - self._intracomm = None @property @@ -894,7 +1020,21 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, mpi_procs=None, omp_num_threads=None, intracomm=None): + def run(self, omp_num_threads=None, intracomm=None): + """Public method to run OpenMC with CMFD + + This method is called by user to run CMFD once instance variables of + CMFDRun class are set + + Parameters + ---------- + omp_num_threads : int + Number of OpenMP threads to use for OpenMC simulation + intracomm : mpi4py.MPI.Intracomm or None + MPI intercommunicator to pass through C API. Set to MPI.COMM_WORLD + by default + + """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made if intracomm is not None: @@ -909,17 +1049,14 @@ class CMFDRun(object): else: openmc.capi.init() - # Configure cmfd parameters and tallies + # Configure CMFD parameters and tallies self._configure_cmfd() - # TODO comment - sys.stdout.flush() - # Initialize simulation openmc.capi.simulation_init() while(True): - # Run everything in next batch before initializing cmfd + # Run everything in next batch before initializing CMFD openmc.capi.next_batch_before_cmfd_init() # Initialize CMFD batch @@ -927,39 +1064,37 @@ class CMFDRun(object): # Run everything in next batch in between initializing and # executing CMFD - status = openmc.capi.next_batch_between_cmfd_init_execute() + openmc.capi.next_batch_between_cmfd_init_execute() - # Status determines whether batch should continue with a - # CMFD update or skip it entirely if it is a restart run - if status != 0: + # Perform CMFD calculation if on + if self._cmfd_on: + self._execute_cmfd() - # Perform CMFD calculation if on - if self._cmfd_on: - self._execute_cmfd() + # Run everything in next batch after executing CMFD. Status + # determines whether another batch should be run or + # simulation should be terminated. + status = openmc.capi.next_batch_after_cmfd_execute() - # Run everything in next batch after executing CMFD. Status - # now determines whether another batch should be run or - # simulation should be terminated. - status = openmc.capi.next_batch_after_cmfd_execute() - - # TODO Comment - if self._cmfd_on: - self._write_cmfd_output() + # Write CMFD output if CMFD on for current batch + if self._cmfd_on and openmc.capi.master(): + self._write_cmfd_output() if status != 0: break # Finalize simuation openmc.capi.simulation_finalize() - # Print out timing statistics + # Print out CMFD timing statistics self._write_cmfd_timing_stats() # Finalize and free memory openmc.capi.finalize() def _write_cmfd_output(self): - # TODO Comment + """Write CMFD output to buffer at the end of each batch""" + # Display CMFD k-effective str1 = 'CMFD k: {:0.5f}'.format(self._k_cmfd[-1]) + # Display value of additional field based on value of cmfd_display if self._cmfd_display == 'dominance': str2 = 'Dom Rat: {:0.5f}'.format(self._dom[-1]) elif self._cmfd_display == 'entropy': @@ -973,7 +1108,7 @@ class CMFDRun(object): sys.stdout.flush() def _write_cmfd_timing_stats(self): - # TODO Comment + """Write CMFD timing statistics to buffer after finalizing simulation""" print( """=====================> CMFD TIMING STATISTICS <==================== @@ -984,6 +1119,7 @@ class CMFDRun(object): sys.stdout.flush() def _configure_cmfd(self): + """Initialize CMFD parameters and set CMFD input variables""" # Read in cmfd input defined in Python self._read_cmfd_input() @@ -992,13 +1128,15 @@ class CMFDRun(object): self._time_cmfdbuild = 0.0 self._time_cmfdsolve = 0.0 - # Initialize all numpy arrays used for cmfd solver + # Initialize all numpy arrays used for CMFD solver self._allocate_cmfd() def _read_cmfd_input(self): - # Print message to user + """Sets values of additional instance variables based on user input""" + # Print message to user and flush output to stdout if openmc.capi.settings.verbosity >= 7 and openmc.capi.master(): print(' Configuring CMFD parameters for simulation') + sys.stdout.flush() # Check if CMFD mesh is defined if self._cmfd_mesh is None: @@ -1045,6 +1183,7 @@ class CMFDRun(object): self._create_cmfd_tally() def _allocate_cmfd(self): + """Allocates all numpy arrays and lists used in CMFD algorithm""" # Extract spatial and energy indices nx = self._indices[0] ny = self._indices[1] @@ -1085,32 +1224,27 @@ class CMFDRun(object): self._k_cmfd = [] def _cmfd_init_batch(self): + """Handles CMFD options at the beginning of each batch""" # Get simulation parameters through C API current_batch = openmc.capi.current_batch() - restart_run = openmc.capi.settings.restart_run - restart_batch = openmc.capi.settings.restart_batch # Check to activate CMFD diffusion and possible feedback if self._cmfd_begin == current_batch: self._cmfd_on = True - # If this is a restart run we are just replaying batches so don't - # execute anything - if restart_run and current_batch <= restart_batch: - return - # Check to reset tallies if (self._n_cmfd_resets > 0 and current_batch in self._cmfd_reset): self._cmfd_tally_reset() def _execute_cmfd(self): + """Runs CMFD calculation on master node""" # Run CMFD on single processor on master if openmc.capi.master(): - #! Start cmfd timer + #! Start CMFD timer time_start_cmfd = time.time() - # Create cmfd data from OpenMC tallies + # Create CMFD data from OpenMC tallies self._set_up_cmfd() # Call solver @@ -1130,12 +1264,13 @@ class CMFDRun(object): # Calculate weight factors self._cmfd_reweight(True) - # Stop cmfd timer + # Stop CMFD timer if openmc.capi.master(): time_stop_cmfd = time.time() self._time_cmfd += time_stop_cmfd - time_start_cmfd def _cmfd_tally_reset(self): + """Resets all CMFD tallies in memory""" # Print message if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): print(' CMFD tallies reset') @@ -1146,6 +1281,15 @@ class CMFDRun(object): tallies[tally_id].reset() def _set_up_cmfd(self, vectorized=True): + """Configures CMFD object for a CMFD eigenvalue calculation + + Parameters + ---------- + vectorized : bool + Whether to compute dhat and dtilde using a vectorized numpy approach + or with traditional for loops + + """ # Set up CMFD coremap if (self._mat_dim == _CMFD_NOACCEL): self._set_coremap() @@ -1172,6 +1316,17 @@ class CMFDRun(object): self._compute_dhat() def _cmfd_solver_execute(self, adjoint=False, vectorized=True): + """Sets up and runs power iteration solver for CMFD + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + vectorized : bool + Whether to build CMFD matrices using a vectorized numpy approach + or with traditional for loops + + """ # Check for physical adjoint physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' @@ -1213,26 +1368,79 @@ class CMFDRun(object): self._write_vector(self._phi, 'fluxvec') def _write_vector(self, vector, base_filename): - # TODO write comments + """Write a 1-D numpy array to file and also save it in .npy format. This + particular format allows users to load the variable directly in a Python + session with np.load() + + Parameters + ---------- + vector : numpy.ndarray + Vector that will be saved + base_filename : string + Filename to save vector as, without any file extension at the end. + Vector will be saved to file [base_filename].dat and in numpy format + as [base_filename].npy + + """ + # Write each element in vector to file with open(base_filename+'.dat', 'w') as fh: for val in vector: fh.write('{:0.8f}\n'.format(val)) + # Save as numpy format np.save(base_filename, vector) def _write_matrix(self, matrix, base_filename): - # TODO write comments, mention zero-based indexing + """Write a numpy matrix to file and also save it in .npz format. This + particular format allows users to load the variable directly in a Python + session with scipy.sparse.load_npz() + + Parameters + ---------- + matrix : scipy.sparse.spmatrix + Sparse matrix that will be saved + base_filename : string + Filename to save matrix entries, without any file extension at the + end. Matrix entries will be saved to file [base_filename].dat and in + scipy format as [base_filename].npz + + """ + # Write row, col, and data of each entry in sparse matrix. This ignores + # all zero-entries, and indices are written with zero-based indexing with open(base_filename+'.dat', 'w') as fh: for row in range(matrix.shape[0]): + # Get all cols for particular row in matrix cols = matrix.indices[matrix.indptr[row]:matrix.indptr[row+1]] + # Get all data entries for particular row in matrix data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] for i in range(len(cols)): fh.write('({0:3d}, {1:3d}): {2:0.8f}\n'.format( row, cols[i], data[i])) + # Save matrix in scipy format sparse.save_npz(base_filename, matrix) def _calc_fission_source(self, vectorized=True): + """Calculates CMFD fission source from CMFD flux. If a coremap is + defined, there will be a discrepancy between the spatial indices in the + variables ``phi`` and ``nfissxs``, so ``phi`` needs to be mapped to the + spatial indices of the cross sections. This can be done in a vectorized + numpy manner or with for loops + + Parameters + ---------- + vectorized : bool + Whether to compute CMFD fission source in a vectorized manner or by + looping over all spatial regions and energy groups. This distinction + is made because ``phi`` does not correspond to the same spatial + domain as ``nfissxs`` + + Returns + ------- + bool + Whether or not the other filter is a subset of this filter + + """ # Extract number of groups and number of accelerated regions nx = self._indices[0] ny = self._indices[1] @@ -1240,10 +1448,11 @@ class CMFDRun(object): ng = self._indices[3] n = self._mat_dim - # Reset cmfd source to 0 + # Reset CMFD source to 0 self._cmfd_src.fill(0.) - # TODO Comment for vectorized vs. not-vectorized + # Compute cmfd_src in a vecotorized manner by phi to the spatial indices + # of the actual problem so that cmfd_flux can be multiplied by nfissxs if vectorized: # Calculate volume vol = np.product(self._hxyz, axis=3) @@ -1254,7 +1463,7 @@ class CMFDRun(object): # Extract indices of coremap that are accelerated idx = np.where(self._coremap != _CMFD_NOACCEL) - # Initialize CMFD flux map that maps phi to actualy spatial and + # Initialize CMFD flux map that maps phi to actual spatial and # group indices of problem cmfd_flux = np.zeros((nx, ny, nz, ng)) @@ -1268,6 +1477,8 @@ class CMFDRun(object): cmfd_flux[:,:,:,:,np.newaxis], axis=3) * \ vol[:,:,:,np.newaxis] + # Otherwise compute cmfd_src by looping over all groups and spatial + # regions else: cmfd_src = np.zeros((nx, ny, nz, ng)) for k in range(nz): @@ -1309,8 +1520,15 @@ class CMFDRun(object): * np.sum((self._cmfd_src - self._openmc_src)**2))) def _cmfd_reweight(self, new_weights): + """Performs weighting of particles in source bank + + Parameters + ---------- + new_weights : bool + Whether to reweight particles or not + + """ # Compute new weight factors - # TODO Comments for this section if new_weights: # Set weight factors to default 1.0 @@ -1319,11 +1537,16 @@ class CMFDRun(object): # Count bank site in mesh and reverse due to egrid structured outside = self._count_bank_sites() + # Check and raise error if source sites exist outside of CMFD mesh if openmc.capi.master() and outside: raise OpenMCError('Source sites outside of the CMFD mesh!') + # Have master compute weight factors, ignore any zeros in + # sourcecounts or cmfd_src if openmc.capi.master(): + # Compute normalization factor norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) + # Flip index of energy dimension sourcecounts = np.flip( self._sourcecounts.reshape(self._cmfd_src.shape), \ axis=3) @@ -1336,6 +1559,7 @@ class CMFDRun(object): if not self._cmfd_feedback: return + # Broadcast weight factors to all procs if have_mpi: self._weightfactors = self._intracomm.bcast( self._weightfactors, root=0) @@ -1344,16 +1568,20 @@ class CMFDRun(object): energy = self._egrid ng = self._indices[3] + # Get xyz locations and energies of all particles in source bank source_xyz = openmc.capi.source_bank()['xyz'] source_energies = openmc.capi.source_bank()['E'] + # Convert xyz location to the CMFD mesh index mesh_ijk = np.floor((source_xyz - m.lower_left) / m.width).astype(int) - # Flip energy + # Determine which energy bin each particle's energy belongs to energy_bins = np.where(source_energies < energy[0], ng - 1, np.where(source_energies > energy[-1], 0, \ ng - np.digitize(source_energies, energy))) + # Determine weight factor of each particle based on its mesh index + # and energy bin and updates its weight openmc.capi.source_bank()['wgt'] *= self._weightfactors[ \ mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] @@ -1363,39 +1591,56 @@ class CMFDRun(object): print(' WARNING: Source pt above energy grid') def _count_bank_sites(self): - # TODO Comments for this section + """Determines the number of fission bank sites in each cell of a given + mesh and energy group structure. + Returns + ------- + bool + Wheter any source sites outside of CMFD mesh were found + + """ + # Initialize variables m = openmc.capi.meshes[self._cmfd_mesh_id] bank = openmc.capi.source_bank() energy = self._egrid sites_outside = np.zeros(1, dtype=bool) ng = self._indices[3] - # Initiate variables outside = np.zeros(1, dtype=bool) count = np.zeros(self._sourcecounts.shape) + # Get xyz locations and energies of each particle in source bank source_xyz = openmc.capi.source_bank()['xyz'] source_energies = openmc.capi.source_bank()['E'] + # Convert xyz location to mesh index and ravel index to scalar mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) mesh_bins = mesh_locations[:,2] * m.dimension[2] + \ mesh_locations[:,1] * m.dimension[1] + mesh_locations[:,0] + # Check if any source locations lie outside of defined CMFD mesh if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): outside[0] = True + # Determine which energy bin each particle's energy corresponds to energy_bins = np.where(source_energies < energy[0], 0, np.where(source_energies > energy[-1], ng - 1, \ np.digitize(source_energies, energy) - 1)) + # Determine all unique combinations of mesh bin and energy bin, and + # count number of particles that belong to these combinations idx, counts = np.unique(np.array([mesh_bins, energy_bins]), axis=1, return_counts=True) + # Store counts to appropriate mesh-energy combination count[idx[0].astype(int), idx[1].astype(int)] = counts if have_mpi: + # Collect values of count from all processors self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM, 0) + # Check if there were sites outside the mesh for any processor self._intracomm.Reduce(outside, sites_outside, MPI.LOR, 0) + # Deal with case if MPI not defined (only one proc) else: sites_outside = outside self._sourcecounts = count @@ -1403,6 +1648,24 @@ class CMFDRun(object): return sites_outside[0] def _build_matrices(self, adjoint, vectorized): + """Build loss and production matrices and write these matrices + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + vectorized : bool + Whether to build CMFD matrices using a vectorized numpy approach + or with traditional for loops + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + """ # Build loss and production matrices if vectorized: loss = self._build_loss_matrix_vectorized(adjoint) @@ -1423,6 +1686,24 @@ class CMFDRun(object): return loss, prod def _compute_adjoint(self, loss, prod): + """Computes a mathematical adjoint of the CMFD problem by transposing + production and loss matrices passed in as arguments + + Parameters + ---------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of adjoint CMFD loss matrix + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of adjoint CMFD production matrix + + """ # Transpose matrices loss = np.transpose(loss) prod = np.transpose(prod) @@ -1435,6 +1716,22 @@ class CMFDRun(object): return loss, prod def _build_loss_matrix(self, adjoint): + """Creates matrix representing loss of neutrons. This method uses for + loops to loop over all spatial indices and energy groups for easier + readability. Matrix is returned in CSR format by populating all entries + with a numpy array and later converting to CSR format + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + + """ # Extract spatial and energy indices and define matrix dimension nx = self._indices[0] ny = self._indices[1] @@ -1530,7 +1827,22 @@ class CMFDRun(object): return loss def _build_loss_matrix_vectorized(self, adjoint): - # TODO comments for this section + """Creates matrix representing loss of neutrons. Since the end shape + of the loss matrix is known a priori, this method uses a + vectorized numpy approach to define all rows, columns, and data, which + is then used to initialize the loss matrix in CSR format. + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + + """ # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -1540,7 +1852,7 @@ class CMFDRun(object): col = np.array([], dtype=int) data = np.array([]) - # Define net leakage coefficient for each matrix element + # Define net leakage coefficient for each surface in each matrix element jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ (-1.0 * self._dtilde[:,:,:,:,0] + self._dhat[:,:,:,:,0])) / \ self._hxyz[:,:,:,np.newaxis,0] + \ @@ -1553,115 +1865,164 @@ class CMFDRun(object): # Shift coremap in all directions to determine whether leakage term # should be defined for particular cell in matrix - coremap_minusx = np.pad(self._coremap, ((1,0),(0,0),(0,0)), mode='constant', - constant_values=_CMFD_NOACCEL)[:-1,:,:] + coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] - coremap_plusx = np.pad(self._coremap, ((0,1),(0,0),(0,0)), mode='constant', - constant_values=_CMFD_NOACCEL)[1:,:,:] + coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] - coremap_minusy = np.pad(self._coremap, ((0,0),(1,0),(0,0)), mode='constant', - constant_values=_CMFD_NOACCEL)[:,:-1,:] + coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] - coremap_plusy = np.pad(self._coremap, ((0,0),(0,1),(0,0)), mode='constant', - constant_values=_CMFD_NOACCEL)[:,1:,:] + coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] - coremap_minusz = np.pad(self._coremap, ((0,0),(0,0),(1,0)), mode='constant', - constant_values=_CMFD_NOACCEL)[:,:,:-1] + coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] - coremap_plusz = np.pad(self._coremap, ((0,0),(0,0),(0,1)), mode='constant', - constant_values=_CMFD_NOACCEL)[:,:,1:] - - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_minusy != _CMFD_NOACCEL) + coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] for g in range(ng): - # Leakage terms + # Define leakage terms that relate terms to their neighbors to the + # left + + # Extract all regions where a cell and its neighbor to the left + # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_minusx != _CMFD_NOACCEL) + coremap_shift_left != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_minusx[condition]) + g + idx_y = ng * (coremap_shift_left[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,0] - self._dhat[:,:,:,g,0])[condition] / \ self._hxyz[:,:,:,0][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) + # Define leakage terms that relate terms to their neighbors to the + # right + + # Extract all regions where a cell and its neighbor to the right + # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_plusx != _CMFD_NOACCEL) + coremap_shift_left != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_plusx[condition]) + g + idx_y = ng * (coremap_shift_left[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,1] + self._dhat[:,:,:,g,1])[condition] / \ self._hxyz[:,:,:,0][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors in the + # back + + # Extract all regions where a cell and its neighbor in the back + # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_minusy != _CMFD_NOACCEL) + coremap_shift_back != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_minusy[condition]) + g + idx_y = ng * (coremap_shift_back[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,2] - self._dhat[:,:,:,g,2])[condition] / \ self._hxyz[:,:,:,1][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) + # Define leakage terms that relate terms to their neighbors in the + # front + + # Extract all regions where a cell and its neighbor in the front + # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_plusy != _CMFD_NOACCEL) + coremap_shift_front != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_plusy[condition]) + g + idx_y = ng * (coremap_shift_front[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,3] + self._dhat[:,:,:,g,3])[condition] / \ self._hxyz[:,:,:,1][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) + # Define leakage terms that relate terms to their neighbors to the + # bottom + + # Extract all regions where a cell and its neighbor to the bottom + # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_minusz != _CMFD_NOACCEL) + coremap_shift_bottom != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_minusz[condition]) + g + idx_y = ng * (coremap_shift_bottom[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,4] - self._dhat[:,:,:,g,4])[condition] / \ self._hxyz[:,:,:,2][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) + # Define leakage terms that relate terms to their neighbors to the + # top + + # Extract all regions where a cell and its neighbor to the + # top are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_plusz != _CMFD_NOACCEL) + coremap_shift_top != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_plusz[condition]) + g + idx_y = ng * (coremap_shift_top[condition]) + g + # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,5] + self._dhat[:,:,:,g,5])[condition] / \ self._hxyz[:,:,:,2][condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) - # Loss of neutrons + # Define terms that relate to loss of neutrons in a cell. These + # correspond to all the diagonal entries of the loss matrix + + # Extract all regions where a cell is a fuel region condition = self._coremap != _CMFD_NOACCEL idx_x = ng * (self._coremap[condition]) + g idx_y = idx_x vals = (jnet[:,:,:,g] + self._totalxs[:,:,:,g] - \ self._scattxs[:,:,:,g,g])[condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) - # Off diagonal in-scattering + # Define terms that relate to in-scattering from group to group. + # These terms relate a mesh index to all mesh indices with the same + # spatial dimensions but belong to a different energy group for h in range(ng): if h != g: + # Extract all regions where a cell is a fuel region condition = self._coremap != _CMFD_NOACCEL idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h + # Get scattering macro xs, transposed if adjoint: vals = (-1.0 * self._scattxs[:, :, :, g, h])[condition] + # Get scattering macro xs else: vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1672,6 +2033,22 @@ class CMFDRun(object): def _build_prod_matrix(self, adjoint): + """Creates matrix representing production of neutrons. This method uses for + loops to loop over all spatial indices and energy groups for easier + readability. Matrix is returned in CSR format by populating all entries + with a numpy array and later converting to CSR format + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + """ # Extract spatial and energy indices and define matrix dimension nx = self._indices[0] ny = self._indices[1] @@ -1714,7 +2091,22 @@ class CMFDRun(object): return prod def _build_prod_matrix_vectorized(self, adjoint): - # TODO Comments for this section + """Creates matrix representing production of neutrons. Since the end shape + of the production matrix is known a priori, this method uses a + vectorized numpy approach to define all rows, columns, and data, which + is then used to initialize the loss matrix in CSR format. + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + + Returns + ------- + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + """ # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -1724,15 +2116,20 @@ class CMFDRun(object): col = np.array([], dtype=int) data = np.array([]) + # Define terms that relate to fission production from group to group. for g in range(ng): for h in range(ng): + # Extract all regions where a cell is a fuel region condition = self._coremap != _CMFD_NOACCEL idx_x = ng * (self._coremap[condition]) + g idx_y = ng * (self._coremap[condition]) + h + # Get nu-fission macro xs, transposed if adjoint: vals = (self._nfissxs[:, :, :, g, h])[condition] + # Get nu-fission macro xs else: vals = (self._nfissxs[:, :, :, h, g])[condition] + # Store rows, cols, and data to add to CSR matrix row = np.append(row, idx_x) col = np.append(col, idx_y) data = np.append(data, vals) @@ -1742,8 +2139,36 @@ class CMFDRun(object): return prod def _matrix_to_indices(self, irow, nx, ny, nz, ng): - # Get indices from coremap + """Converts matrix index in CMFD matrices to spatial and group indices + of actual problem, based on values from coremap + + Parameters + ---------- + irow : int + Row in CMFD matrix + nx : int + Total number of mesh cells in problem in x direction + ny : int + Total number of mesh cells in problem in y direction + nz : int + Total number of mesh cells in problem in z direction + ng : int + Total number of energy groups in problem + + Returns + ------- + i : int + Corresponding x-index in CMFD problem + j : int + Corresponding y-index in CMFD problem + k : int + Corresponding z-index in CMFD problem + g : int + Corresponding group in CMFD problem + + """ g = irow % ng + # Get indices from coremap spatial_idx = np.where(self._coremap == int(irow/ng)) i = spatial_idx[0][0] j = spatial_idx[1][0] @@ -1752,11 +2177,51 @@ class CMFDRun(object): return i, j, k, g def _indices_to_matrix(self, i, j, k, g, ng): + """Takes (i,j,k,g) indices and computes location in CMFD matrix + + Parameters + ---------- + i : int + Corresponding x-index in CMFD problem + j : int + Corresponding y-index in CMFD problem + k : int + Corresponding z-index in CMFD problem + g : int + Corresponding group in CMFD problem + ng : int + Total number of energy groups in problem + + Returns + ------- + matidx : int + Row in CMFD matrix + + """ # Get matrix index from coremap matidx = ng*(self._coremap[i,j,k]) + g return matidx def _execute_power_iter(self, loss, prod): + """Main power iteration routine for the CMFD calculation + + Parameters + ---------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + Returns + ------- + phi_n : numpy.ndarray + Flux vector of CMFD problem + k_n : float + Eigenvalue of CMFD problem + dom : float + Dominance ratio of CMFD problem + + """ # Get problem size n = loss.shape[0] @@ -1770,7 +2235,7 @@ class CMFDRun(object): serr_v = np.zeros((n,)) # Set initial guess - k_n = openmc.capi.keff()[0] + k_n = openmc.capi.keff_temp()[0] k_o = k_n dw = self._cmfd_shift k_s = k_o + dw @@ -1830,6 +2295,30 @@ class CMFDRun(object): norm_o = norm_n def _check_convergence(self, s_n, s_o, k_n, k_o, iter): + """Checks the convergence of the CMFD problem + + Parameters + ---------- + s_n : numpy.ndarray + Source vector from current iteration + s_o : numpy.ndarray + Source vector from previous iteration + k_n : float + K-effective from current iteration + k_o : float + K-effective from previous iteration + iter: int + Iteration number + + Returns + ------- + iconv : bool + Whether the power iteration has reached convergence + serr : float + Error in source from previous iteration to current iteration, used + for dominance ratio calculations + + """ # Calculate error in keff kerr = abs(k_o - k_n)/k_n @@ -1850,6 +2339,12 @@ class CMFDRun(object): return iconv, serr def _set_coremap(self): + """Sets the core mapping information. All regions marked with zero + are set to CMFD_NO_ACCEL, while all regions marked with 1 are set to a + unique index that maps each fuel region to a row number when building + CMFD matrices + + """ # Set number of accelerated regions in problem. This will be related to # the dimension of CMFD matrices self._mat_dim = np.sum(self._coremap) @@ -1860,6 +2355,10 @@ class CMFDRun(object): np.cumsum(self._coremap)-1) def _compute_xs(self): + """Takes CMFD tallies from OpenMC and computes macroscopic cross + sections, flux, and diffusion coefficients for each mesh cell + + """ # Extract energy indices ng = self._indices[3] @@ -1992,6 +2491,7 @@ class CMFDRun(object): self._coremap = self._coremap.reshape(self._indices[0:3]) def _compute_effective_downscatter(self): + """Changes downscatter rate for zero upscatter""" # Extract energy index ng = self._indices[3] @@ -2004,7 +2504,8 @@ class CMFDRun(object): flux2 = self._flux[:,:,:,1] sigt1 = self._totalxs[:,:,:,0] sigt2 = self._totalxs[:,:,:,1] - # First energy index is incoming, second is outgoing + + # First energy index is incoming energy, second is outgoing energy sigs11 = self._scattxs[:,:,:,0,0] sigs21 = self._scattxs[:,:,:,1,0] sigs12 = self._scattxs[:,:,:,0,1] @@ -2029,6 +2530,7 @@ class CMFDRun(object): self._scattxs[:,:,:,1,0] = 0.0 def _neutron_balance(self): + """Computes the RMS neutron balance over the CMFD mesh""" # Extract energy indices ng = self._indices[3] @@ -2074,10 +2576,21 @@ class CMFDRun(object): np.count_nonzero(self._resnb))) def _compute_dtilde_vectorized(self): - #TODO add coments for this method + """Computes the diffusion coupling coefficient using a vectorized numpy + approach. Aggregate values for the dtilde multidimensional array are + populated by first defining values on the problem boundary, and then for + all other regions. For indices not lying on a boundary, dtilde values + are distinguished between regions that neighbor a reflector region and + regions that don't neighbor a reflector + + """ + # Logical for determining whether region of interest is accelerated region is_accel = self._coremap != _CMFD_NOACCEL + # Logical for determining whether a zero flux "albedo" b.c. should be + # applied is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + # Define dtilde at left surface for all mesh cells on left boundary self._dtilde[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], np.where(is_zero_flux_alb[0], 2.0 * self._diffcof[0,:,:,:] / \ self._hxyz[0,:,:,np.newaxis,0], @@ -2088,6 +2601,7 @@ class CMFDRun(object): (1.0 - self._albedo[0]) * \ self._hxyz[0,:,:,np.newaxis,0])), 0) + # Define dtilde at right surface for all mesh cells on right boundary self._dtilde[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], np.where(is_zero_flux_alb[1], 2.0 * self._diffcof[-1,:,:,:] / \ self._hxyz[-1,:,:,np.newaxis,0], @@ -2098,6 +2612,7 @@ class CMFDRun(object): (1.0 - self._albedo[1]) * \ self._hxyz[-1,:,:,np.newaxis,0])), 0) + # Define dtilde at back surface for all mesh cells on back boundary self._dtilde[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], np.where(is_zero_flux_alb[2], 2.0 * self._diffcof[:,0,:,:] / \ self._hxyz[:,0,:,np.newaxis,1], @@ -2108,6 +2623,7 @@ class CMFDRun(object): (1.0 - self._albedo[2]) * \ self._hxyz[:,0,:,np.newaxis,1])), 0) + # Define dtilde at front surface for all mesh cells on front boundary self._dtilde[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], np.where(is_zero_flux_alb[3], 2.0 * self._diffcof[:,-1,:,:] / \ self._hxyz[:,-1,:,np.newaxis,1], @@ -2118,6 +2634,7 @@ class CMFDRun(object): (1.0 - self._albedo[3]) * \ self._hxyz[:,-1,:,np.newaxis,1])), 0) + # Define dtilde at bottom surface for all mesh cells on bottom boundary self._dtilde[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], np.where(is_zero_flux_alb[4], 2.0 * self._diffcof[:,:,0,:] / \ self._hxyz[:,:,0,np.newaxis,2], @@ -2128,6 +2645,7 @@ class CMFDRun(object): (1.0 - self._albedo[4]) * \ self._hxyz[:,:,0,np.newaxis,2])), 0) + # Define dtilde at top surface for all mesh cells on top boundary self._dtilde[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], np.where(is_zero_flux_alb[5], 2.0 * self._diffcof[:,:,-1,:] / \ self._hxyz[:,:,-1,np.newaxis,2], @@ -2138,14 +2656,20 @@ class CMFDRun(object): (1.0 - self._albedo[5]) * \ self._hxyz[:,:,-1,np.newaxis,2])), 0) + # Define reflector albedo for all cells on the left surface, in case + # a cell borders a reflector region on the left ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_left']], self._current[:,:,:,:,_CURRENTS['out_left']], where=self._current[:,:,:,:,_CURRENTS['out_left']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_left']])) + # Logical for whether neighboring cell to the left is reflector region adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to left neig_dc = np.roll(self._diffcof, 1, axis=0) + # Cell dimensions of neighbor to left neig_hxyz = np.roll(self._hxyz, 1, axis=0) + # Define dtilde at left surface for all mesh cells not on left boundary self._dtilde[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], (2.0 * self._diffcof[1:,:,:,:] * \ @@ -2158,14 +2682,20 @@ class CMFDRun(object): (neig_hxyz[1:,:,:,np.newaxis,0] * self._diffcof[1:,:,:,:] + \ self._hxyz[1:,:,:,np.newaxis,0] * neig_dc[1:,:,:,:])), 0.0) + # Define reflector albedo for all cells on the right surface, in case + # a cell borders a reflector region on the right ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_right']], self._current[:,:,:,:,_CURRENTS['out_right']], where=self._current[:,:,:,:,_CURRENTS['out_right']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_right']])) + # Logical for whether neighboring cell to the right is reflector region adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to right neig_dc = np.roll(self._diffcof, -1, axis=0) + # Cell dimensions of neighbor to right neig_hxyz = np.roll(self._hxyz, -1, axis=0) + # Define dtilde at right surface for all mesh cells not on right boundary self._dtilde[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], (2.0 * self._diffcof[:-1,:,:,:] * \ @@ -2178,14 +2708,20 @@ class CMFDRun(object): (neig_hxyz[:-1,:,:,np.newaxis,0] * self._diffcof[:-1,:,:,:] + \ self._hxyz[:-1,:,:,np.newaxis,0] * neig_dc[:-1,:,:,:])), 0.0) + # Define reflector albedo for all cells on the back surface, in case + # a cell borders a reflector region on the back ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_back']], self._current[:,:,:,:,_CURRENTS['out_back']], where=self._current[:,:,:,:,_CURRENTS['out_back']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_back']])) + # Logical for whether neighboring cell to the back is reflector region adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to back neig_dc = np.roll(self._diffcof, 1, axis=1) + # Cell dimensions of neighbor to back neig_hxyz = np.roll(self._hxyz, 1, axis=1) + # Define dtilde at back surface for all mesh cells not on back boundary self._dtilde[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], (2.0 * self._diffcof[:,1:,:,:] * \ @@ -2198,14 +2734,20 @@ class CMFDRun(object): (neig_hxyz[:,1:,:,np.newaxis,1] * self._diffcof[:,1:,:,:] + \ self._hxyz[:,1:,:,np.newaxis,1] * neig_dc[:,1:,:,:])), 0.0) + # Define reflector albedo for all cells on the front surface, in case + # a cell borders a reflector region in the front ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_front']], self._current[:,:,:,:,_CURRENTS['out_front']], where=self._current[:,:,:,:,_CURRENTS['out_front']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_front']])) + # Logical for whether neighboring cell to the front is reflector region adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to front neig_dc = np.roll(self._diffcof, -1, axis=1) + # Cell dimensions of neighbor to front neig_hxyz = np.roll(self._hxyz, -1, axis=1) + # Define dtilde at front surface for all mesh cells not on front boundary self._dtilde[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], (2.0 * self._diffcof[:,:-1,:,:] * \ @@ -2218,14 +2760,20 @@ class CMFDRun(object): (neig_hxyz[:,:-1,:,np.newaxis,1] * self._diffcof[:,:-1,:,:] + \ self._hxyz[:,:-1,:,np.newaxis,1] * neig_dc[:,:-1,:,:])), 0.0) + # Define reflector albedo for all cells on the bottom surface, in case + # a cell borders a reflector region on the bottom ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_bottom']], self._current[:,:,:,:,_CURRENTS['out_bottom']], where=self._current[:,:,:,:,_CURRENTS['out_bottom']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_bottom']])) + # Logical for whether neighboring cell to the bottom is reflector region adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to bottom neig_dc = np.roll(self._diffcof, 1, axis=2) + # Cell dimensions of neighbor to bottom neig_hxyz = np.roll(self._hxyz, 1, axis=2) + # Define dtilde at bottom surface for all mesh cells not on bottom boundary self._dtilde[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], (2.0 * self._diffcof[:,:,1:,:] * \ @@ -2238,14 +2786,20 @@ class CMFDRun(object): (neig_hxyz[:,:,1:,np.newaxis,2] * self._diffcof[:,:,1:,:] + \ self._hxyz[:,:,1:,np.newaxis,2] * neig_dc[:,:,1:,:])), 0.0) + # Define reflector albedo for all cells on the top surface, in case + # a cell borders a reflector region on the top ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_top']], self._current[:,:,:,:,_CURRENTS['out_top']], where=self._current[:,:,:,:,_CURRENTS['out_top']] > 1.0e-10, out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_top']])) + # Logical for whether neighboring cell to the top is reflector region adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to top neig_dc = np.roll(self._diffcof, -1, axis=2) + # Cell dimensions of neighbor to top neig_hxyz = np.roll(self._hxyz, -1, axis=2) + # Define dtilde at top surface for all mesh cells not on top boundary self._dtilde[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], (2.0 * self._diffcof[:,:,:-1,:] * \ @@ -2259,6 +2813,10 @@ class CMFDRun(object): self._hxyz[:,:,:-1,np.newaxis,2] * neig_dc[:,:,:-1,:])), 0.0) def _compute_dtilde(self): + """Computes the diffusion coupling coefficient by looping over all + spatial regions and energy groups + + """ # Get maximum of spatial and group indices nx = self._indices[0] ny = self._indices[1] @@ -2331,127 +2889,159 @@ class CMFDRun(object): self._dtilde[i, j, k, g, l] = dtilde def _compute_dhat_vectorized(self): - # TODO Write comments for this function - net_current_minusx = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ + """Computes the nonlinear coupling coefficient using a vectorized numpy + approach. Aggregate values for the dhat multidimensional array are + populated by first defining values on the problem boundary, and then for + all other regions. For indices not lying by a boundary, dhat values + are distinguished between regions that neighbor a reflector region and + regions that don't neighbor a reflector + + """ + # Define net current on each face, divided by surface area + net_current_left = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ self._current[:,:,:,:,_CURRENTS['out_left']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,0]) - net_current_plusx = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ + net_current_right = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ self._current[:,:,:,:,_CURRENTS['in_right']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,0]) - net_current_minusy = ((self._current[:,:,:,:,_CURRENTS['in_back']] - \ + net_current_back = ((self._current[:,:,:,:,_CURRENTS['in_back']] - \ self._current[:,:,:,:,_CURRENTS['out_back']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,1]) - net_current_plusy = ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ + net_current_front = ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ self._current[:,:,:,:,_CURRENTS['in_front']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,1]) - net_current_minusz = ((self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ + net_current_bottom = ((self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ self._current[:,:,:,:,_CURRENTS['out_bottom']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,2]) - net_current_plusz = ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ + net_current_top = ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ self._current[:,:,:,:,_CURRENTS['in_top']]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,2]) + # Define flux in each cell cell_flux = self._flux / np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Extract indices of coremap that are accelerated is_accel = self._coremap != _CMFD_NOACCEL + # Define dtilde at left surface for all mesh cells on left boundary self._dhat[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], - (net_current_minusx[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ + (net_current_left[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ cell_flux[0,:,:,:]) / cell_flux[0,:,:,:], 0) + # Define dtilde at right surface for all mesh cells on right boundary self._dhat[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], - (net_current_plusx[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ + (net_current_right[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ cell_flux[-1,:,:,:]) / cell_flux[-1,:,:,:], 0) + # Define dtilde at back surface for all mesh cells on back boundary self._dhat[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], - (net_current_minusy[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ + (net_current_back[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) + # Define dtilde at front surface for all mesh cells on front boundary self._dhat[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], - (net_current_plusy[:,-1,:,:] + self._dtilde[:,-1,:,:,3] * \ + (net_current_front[:,-1,:,:] + self._dtilde[:,-1,:,:,3] * \ cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) + # Define dtilde at bottom surface for all mesh cells on bottom boundary self._dhat[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], - (net_current_minusz[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ + (net_current_bottom[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) + # Define dtilde at top surface for all mesh cells on top boundary self._dhat[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], - (net_current_minusz[:,:,-1,:] + self._dtilde[:,:,-1,:,5] * \ + (net_current_top[:,:,-1,:] + self._dtilde[:,:,-1,:,5] * \ cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) - # Minus x direction + # Logical for whether neighboring cell to the left is reflector region adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + # Cell flux of neighbor to left neig_flux = np.roll(self._flux, 1, axis=0) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at left surface for all mesh cells not on left boundary self._dhat[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], - (net_current_minusx[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ + (net_current_left[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ cell_flux[1:,:,:,:]) / cell_flux[1:,:,:,:], - (net_current_minusx[1:,:,:,:] - self._dtilde[1:,:,:,:,0] * \ + (net_current_left[1:,:,:,:] - self._dtilde[1:,:,:,:,0] * \ (neig_flux[1:,:,:,:] - cell_flux[1:,:,:,:])) / \ (neig_flux[1:,:,:,:] + cell_flux[1:,:,:,:])), 0.0) - # Plus x direction + # Logical for whether neighboring cell to the right is reflector region adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + # Cell flux of neighbor to right neig_flux = np.roll(self._flux, -1, axis=0) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at right surface for all mesh cells not on right boundary self._dhat[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], - (net_current_plusx[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ + (net_current_right[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ cell_flux[:-1,:,:,:]) / cell_flux[:-1,:,:,:], - (net_current_plusx[:-1,:,:,:] + self._dtilde[:-1,:,:,:,1] * \ + (net_current_right[:-1,:,:,:] + self._dtilde[:-1,:,:,:,1] * \ (neig_flux[:-1,:,:,:] - cell_flux[:-1,:,:,:])) / \ (neig_flux[:-1,:,:,:] + cell_flux[:-1,:,:,:])), 0.0) - # Minus y direction + # Logical for whether neighboring cell to the back is reflector region adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + # Cell flux of neighbor to back neig_flux = np.roll(self._flux, 1, axis=1) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at back surface for all mesh cells not on back boundary self._dhat[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], - (net_current_minusy[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ + (net_current_back[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ cell_flux[:,1:,:,:]) / cell_flux[:,1:,:,:], - (net_current_minusy[:,1:,:,:] - self._dtilde[:,1:,:,:,2] * \ + (net_current_back[:,1:,:,:] - self._dtilde[:,1:,:,:,2] * \ (neig_flux[:,1:,:,:] - cell_flux[:,1:,:,:])) / \ (neig_flux[:,1:,:,:] + cell_flux[:,1:,:,:])), 0.0) - # Plus y direction + # Logical for whether neighboring cell to the front is reflector region adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + # Cell flux of neighbor to front neig_flux = np.roll(self._flux, -1, axis=1) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at front surface for all mesh cells not on front boundary self._dhat[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], - (net_current_plusy[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ + (net_current_front[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ cell_flux[:,:-1,:,:]) / cell_flux[:,:-1,:,:], - (net_current_plusy[:,:-1,:,:] + self._dtilde[:,:-1,:,:,3] * \ + (net_current_front[:,:-1,:,:] + self._dtilde[:,:-1,:,:,3] * \ (neig_flux[:,:-1,:,:] - cell_flux[:,:-1,:,:])) / \ (neig_flux[:,:-1,:,:] + cell_flux[:,:-1,:,:])), 0.0) - # Minus z direction + # Logical for whether neighboring cell to the bottom is reflector region adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + # Cell flux of neighbor to bottom neig_flux = np.roll(self._flux, 1, axis=2) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at bottom surface for all mesh cells not on bottom boundary self._dhat[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], - (net_current_minusz[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ + (net_current_bottom[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ cell_flux[:,:,1:,:]) / cell_flux[:,:,1:,:], - (net_current_minusz[:,:,1:,:] - self._dtilde[:,:,1:,:,4] * \ + (net_current_bottom[:,:,1:,:] - self._dtilde[:,:,1:,:,4] * \ (neig_flux[:,:,1:,:] - cell_flux[:,:,1:,:])) / \ (neig_flux[:,:,1:,:] + cell_flux[:,:,1:,:])), 0.0) - # Plus z direction + # Logical for whether neighboring cell to the top is reflector region adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + # Cell flux of neighbor to top neig_flux = np.roll(self._flux, -1, axis=2) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + # Define dtilde at top surface for all mesh cells not on top boundary self._dhat[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], - (net_current_plusz[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ + (net_current_top[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ cell_flux[:,:,:-1,:]) / cell_flux[:,:,:-1,:], - (net_current_plusz[:,:,:-1,:] + self._dtilde[:,:,:-1,:,5] * \ + (net_current_top[:,:,:-1,:] + self._dtilde[:,:,:-1,:,5] * \ (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) def _compute_dhat(self): + """Computes the nonlinear coupling coefficient by looping over all + spatial regions and energy groups + + """ # Get maximum of spatial and group indices nx = self._indices[0] ny = self._indices[1] @@ -2528,6 +3118,29 @@ class CMFDRun(object): print(' Dhats reset to zero') def _get_reflector_albedo(self, l, g, i, j, k): + """Calculates the albedo to the reflector by returning ratio of + incoming to outgoing ratio + + Parameters + ---------- + l : int + leakage index, see _CURRENTS for map between leakage index and leakage + direction + g : int + index of energy group + i : int + index of mesh location in x-direction + j : int + index of mesh location in y-direction + k : int + index of mesh location in z-direction + + Returns + ------- + float + reflector albedo + + """ # Get partial currents from object current = self._current[i,j,k,g,:] @@ -2538,6 +3151,7 @@ class CMFDRun(object): return current[2*l+1]/current[2*l] def _create_cmfd_tally(self): + """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh, stored internally cmfd_mesh = openmc.capi.Mesh() # Store id of Mesh object diff --git a/src/simulation.F90 b/src/simulation.F90 index a454ada936..12af1a8924 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -54,7 +54,6 @@ module simulation integer(C_INT), parameter :: STATUS_EXIT_NORMAL = 0 integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1 integer(C_INT), parameter :: STATUS_EXIT_ON_TRIGGER = 2 - integer(C_INT), parameter :: STATUS_CONTINUE_BATCH = 3 contains @@ -157,8 +156,7 @@ contains ! OPENMC_NEXT_BATCH_BETWEEN_CMFD_INIT_EXECUTE !=============================================================================== - function openmc_next_batch_between_cmfd_init_execute(status) result(err) bind(C) - integer(C_INT), intent(out), optional :: status + function openmc_next_batch_between_cmfd_init_execute() result(err) bind(C) integer(C_INT) :: err type(Particle) :: p @@ -166,13 +164,6 @@ contains err = 0 - ! Handle restart runs - if (restart_run .and. current_batch <= restart_batch) then - call replay_batch_history() - status = STATUS_EXIT_NORMAL - return - end if - ! ======================================================================= ! LOOP OVER GENERATIONS GENERATION_LOOP: do current_gen = 1, gen_per_batch @@ -215,10 +206,6 @@ contains n_realizations = 0 end if - if (present(status)) then - status = STATUS_CONTINUE_BATCH - end if - end function openmc_next_batch_between_cmfd_init_execute !=============================================================================== diff --git a/src/simulation_header.F90 b/src/simulation_header.F90 index d2d8eef023..60bed6425a 100644 --- a/src/simulation_header.F90 +++ b/src/simulation_header.F90 @@ -65,7 +65,7 @@ module simulation_header ! ============================================================================ ! MISCELLANEOUS VARIABLES - integer(C_INT), bind(C, name='openmc_restart_batch') :: restart_batch + integer :: restart_batch ! Flag for enabling cell overlap checking during transport integer(8), allocatable :: overlap_check_cnt(:) From 1633d5a74990f5f0776c74a56aeb4249622670d9 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 12 Sep 2018 14:37:08 -0400 Subject: [PATCH 29/58] Bug fixes for 1grp-{1d,2d,3d} cases --- .../cmfd_testing/cmfd-feed/capi/cmfd_ref.xml | 15 --- .../cmfd_testing/cmfd-feed/capi/geometry.xml | 43 ------ .../cmfd_testing/cmfd-feed/capi/materials.xml | 12 -- .../cmfd-feed/capi/run_openmc_cmfd.py | 27 ---- .../cmfd_testing/cmfd-feed/capi/settings.xml | 28 ---- .../cmfd_testing/cmfd-feed/capi/tallies.xml | 21 --- examples/cmfd_testing/cmfd-feed/trad/cmfd.xml | 16 --- .../cmfd_testing/cmfd-feed/trad/geometry.xml | 43 ------ .../cmfd_testing/cmfd-feed/trad/materials.xml | 12 -- .../cmfd_testing/cmfd-feed/trad/settings.xml | 28 ---- .../cmfd_testing/cmfd-feed/trad/tallies.xml | 21 --- openmc/cmfd.py | 122 +++++++++++++----- src/cmfd_solver.F90 | 4 +- 13 files changed, 89 insertions(+), 303 deletions(-) delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/geometry.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/materials.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/settings.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/capi/tallies.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/trad/cmfd.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/trad/geometry.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/trad/materials.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/trad/settings.xml delete mode 100644 examples/cmfd_testing/cmfd-feed/trad/tallies.xml diff --git a/examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml b/examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml deleted file mode 100644 index 1f6cdac198..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/cmfd_ref.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - -10 -1 -1 - 10 1 1 - 10 1 1 - 0.0 0.0 1.0 1.0 1.0 1.0 - - - 5 - dominance - true - 1.e-15 1.e-20 - diff --git a/examples/cmfd_testing/cmfd-feed/capi/geometry.xml b/examples/cmfd_testing/cmfd-feed/capi/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/examples/cmfd_testing/cmfd-feed/capi/materials.xml b/examples/cmfd_testing/cmfd-feed/capi/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py b/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py deleted file mode 100644 index 1193fde7f3..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/run_openmc_cmfd.py +++ /dev/null @@ -1,27 +0,0 @@ -import openmc -import numpy as np -from mpi4py import MPI - -comm = MPI.COMM_WORLD - -# Initialize CMFD Mesh -cmfd_mesh = openmc.CMFDMesh() -cmfd_mesh.lower_left = [-10, -1, -1] -cmfd_mesh.upper_right = [10, 1, 1] -cmfd_mesh.dimension = [10, 1, 1] -cmfd_mesh.albedo = [0., 0., 1., 1., 1., 1.] -cmfd_mesh.energy = [0., 1000000., 10000000000.] -cmfd_mesh.map = [0,1,1,1,1,1,1,1,1,0] - -# Initialize CMFDRun object -cmfd_run = openmc.CMFDRun() - -# Set all runtime parameters (cmfd_mesh, tolerances, tally_resets, etc) -# All error checking done under the hood when setter function called -cmfd_run.cmfd_mesh = cmfd_mesh -cmfd_run.cmfd_begin = 5 -cmfd_run.cmfd_display = 'dominance' -cmfd_run.cmfd_feedback = True - -# Run CMFD -cmfd_run.run() diff --git a/examples/cmfd_testing/cmfd-feed/capi/settings.xml b/examples/cmfd_testing/cmfd-feed/capi/settings.xml deleted file mode 100644 index 0646677bca..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/settings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - - - diff --git a/examples/cmfd_testing/cmfd-feed/capi/tallies.xml b/examples/cmfd_testing/cmfd-feed/capi/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/examples/cmfd_testing/cmfd-feed/capi/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml b/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml deleted file mode 100644 index 0d033ad119..0000000000 --- a/examples/cmfd_testing/cmfd-feed/trad/cmfd.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -10 -1 -1 - 10 1 1 - 10 1 1 - 0.0 0.0 1.0 1.0 1.0 1.0 - 0.0 1000000.0 10000000000.0 - 1 2 2 2 2 2 2 2 2 1 - - 5 - dominance - true - 1.e-15 1.e-20 - diff --git a/examples/cmfd_testing/cmfd-feed/trad/geometry.xml b/examples/cmfd_testing/cmfd-feed/trad/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/examples/cmfd_testing/cmfd-feed/trad/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/examples/cmfd_testing/cmfd-feed/trad/materials.xml b/examples/cmfd_testing/cmfd-feed/trad/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/examples/cmfd_testing/cmfd-feed/trad/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/examples/cmfd_testing/cmfd-feed/trad/settings.xml b/examples/cmfd_testing/cmfd-feed/trad/settings.xml deleted file mode 100644 index 6548ac1e05..0000000000 --- a/examples/cmfd_testing/cmfd-feed/trad/settings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - - true - diff --git a/examples/cmfd_testing/cmfd-feed/trad/tallies.xml b/examples/cmfd_testing/cmfd-feed/trad/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/examples/cmfd_testing/cmfd-feed/trad/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/openmc/cmfd.py b/openmc/cmfd.py index b5147db8af..d5002279c5 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1020,7 +1020,7 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, omp_num_threads=None, intracomm=None): + def run(self, omp_num_threads=None, intracomm=None, vectorized=True): """Public method to run OpenMC with CMFD This method is called by user to run CMFD once instance variables of @@ -1068,7 +1068,7 @@ class CMFDRun(object): # Perform CMFD calculation if on if self._cmfd_on: - self._execute_cmfd() + self._execute_cmfd(vectorized) # Run everything in next batch after executing CMFD. Status # determines whether another batch should be run or @@ -1237,18 +1237,18 @@ class CMFDRun(object): and current_batch in self._cmfd_reset): self._cmfd_tally_reset() - def _execute_cmfd(self): + def _execute_cmfd(self, vectorized): """Runs CMFD calculation on master node""" # Run CMFD on single processor on master if openmc.capi.master(): - #! Start CMFD timer + # Start CMFD timer time_start_cmfd = time.time() # Create CMFD data from OpenMC tallies - self._set_up_cmfd() + self._set_up_cmfd(vectorized=vectorized) # Call solver - self._cmfd_solver_execute() + self._cmfd_solver_execute(vectorized=vectorized) # Store k-effective self._k_cmfd.append(self._keff) @@ -1259,7 +1259,7 @@ class CMFDRun(object): self._cmfd_solver_execute(adjoint=True) # Calculate fission source - self._calc_fission_source() + self._calc_fission_source(vectorized=vectorized) # Calculate weight factors self._cmfd_reweight(True) @@ -1363,9 +1363,11 @@ class CMFDRun(object): # Write out flux vector if self._cmfd_write_matrices: if adjoint: - self._write_vector(self._adj_phi, 'adj_fluxvec') + # TODO Change to self._adj_phi + self._write_vector(phi, 'adj_fluxvec') else: - self._write_vector(self._phi, 'fluxvec') + # TODO Change to self._phi + self._write_vector(phi, 'fluxvec') def _write_vector(self, vector, base_filename): """Write a 1-D numpy array to file and also save it in .npy format. This @@ -1470,7 +1472,8 @@ class CMFDRun(object): # Loop over all groups and set CMFD flux based on indices of # coremap and values of phi for g in range(ng): - cmfd_flux[idx + (np.full((n,),g),)] = phi[:,g] + phi_g = phi[:,g] + cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] # Compute fission source cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ @@ -1531,6 +1534,12 @@ class CMFDRun(object): # Compute new weight factors if new_weights: + # Get spatial dimensions and energy groups + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + # Set weight factors to default 1.0 self._weightfactors.fill(1.0) @@ -1546,10 +1555,20 @@ class CMFDRun(object): if openmc.capi.master(): # Compute normalization factor norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) + + # Define target reshape dimensions for sourcecounts. This defines + # how self._sourcecounts is ordered by dimension + target_shape = [nz, ny, nx, ng] + + # Reshape sourcecounts to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng] + sourcecounts = np.swapaxes( + self._sourcecounts.reshape(target_shape), 0, 2) + # Flip index of energy dimension - sourcecounts = np.flip( - self._sourcecounts.reshape(self._cmfd_src.shape), \ - axis=3) + sourcecounts = np.flip(sourcecounts, axis=3) + + # Compute weight factors divide_condition = np.logical_and(sourcecounts > 0, self._cmfd_src > 0) self._weightfactors = np.divide(self._cmfd_src * norm, \ @@ -1615,8 +1634,8 @@ class CMFDRun(object): # Convert xyz location to mesh index and ravel index to scalar mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) - mesh_bins = mesh_locations[:,2] * m.dimension[2] + \ - mesh_locations[:,1] * m.dimension[1] + mesh_locations[:,0] + mesh_bins = mesh_locations[:,2] * m.dimension[1] * m.dimension[0] + \ + mesh_locations[:,1] * m.dimension[0] + mesh_locations[:,0] # Check if any source locations lie outside of defined CMFD mesh if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): @@ -1908,9 +1927,9 @@ class CMFDRun(object): # Extract all regions where a cell and its neighbor to the right # are both fuel regions condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_left != _CMFD_NOACCEL) + coremap_shift_right != _CMFD_NOACCEL) idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_left[condition]) + g + idx_y = ng * (coremap_shift_right[condition]) + g # Compute leakage term associated with these regions vals = (-1.0 * self._dtilde[:,:,:,g,1] + self._dhat[:,:,:,g,1])[condition] / \ @@ -2360,6 +2379,9 @@ class CMFDRun(object): """ # Extract energy indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] ng = self._indices[3] # Set flux object and source distribution all to zeros @@ -2400,20 +2422,30 @@ class CMFDRun(object): ') in group ' + str(ng-mat_idx[-1]) raise OpenMCError(err_message) - # Store flux and reshape - # Flux is flipped in energy axis as tally results are given in reverse - # order of energy group - self._flux = np.flip(flux.reshape(self._flux.shape), axis=3) + # Define target tally reshape dimensions. This defines how openmc + # tallies are ordered by dimension + target_tally_shape = [nz, ny, nx, ng] + + # Reshape flux array to target shape. Swap x and z axes so that + # flux shape is now [nx, ny, nz, ng] + reshape_flux = np.swapaxes(flux.reshape(target_tally_shape), 0, 2) + + # Flip energy axis as tally results are given in reverse order of + # energy group + self._flux = np.flip(reshape_flux, axis=3) # Get total rr and convert to total xs from CMFD tally 0 tally_results = tallies[tally_id].results[:,1,1] totalxs = np.divide(tally_results, flux, \ where=flux>0, out=np.zeros_like(tally_results)) - # Store total xs and reshape + # Reshape totalxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng] + reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), 0, 2) + # Total xs is flipped in energy axis as tally results are given in # reverse order of energy group - self._totalxs = np.flip(totalxs.reshape(self._totalxs.shape), axis=3) + self._totalxs = np.flip(reshape_totalxs, axis=3) # Get scattering xs from CMFD tally 1 # flux is repeated to account for extra dimensionality of scattering xs @@ -2424,10 +2456,17 @@ class CMFDRun(object): where=np.repeat(flux>0, ng), \ out=np.zeros_like(tally_results)) - # Store scattxs and reshape + # Define target tally reshape dimensions for xs with incoming + # and outgoing energies + target_tally_shape = [nz, ny, nx, ng, ng] + + # Reshape scattxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng] + reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), 0, 2) + # Scattering xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group - self._scattxs = np.flip(scattxs.reshape(self._scattxs.shape), axis=3) + self._scattxs = np.flip(reshape_scattxs, axis=3) self._scattxs = np.flip(self._scattxs, axis=4) # Get nu-fission xs from CMFD tally 1 @@ -2439,10 +2478,13 @@ class CMFDRun(object): where=np.repeat(flux>0, ng), \ out=np.zeros_like(tally_results)) - # Store nfissxs and reshape + # Reshape nfissxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng] + reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), 0, 2) + # Nu-fission xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group - self._nfissxs = np.flip(nfissxs.reshape(self._nfissxs.shape), axis=3) + self._nfissxs = np.flip(reshape_nfissxs, axis=3) self._nfissxs = np.flip(self._nfissxs, axis=4) # Openmc source distribution is sum of nu-fission rr in incoming energies @@ -2460,20 +2502,29 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] # Filter tally results to include only accelerated regions - tally_results = np.where(np.repeat(flux>0, 12), tally_results, 0.) + current = np.where(np.repeat(flux>0, 12), tally_results, 0.) + + # Define target tally reshape dimensions for current + target_tally_shape = [nz, ny, nx, ng, 12] + + # Reshape current array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, 12] + reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) - # Reshape and store current # Current is flipped in energy axis as tally results are given in # reverse order of energy group - self._current = np.flip(tally_results.reshape(self._current.shape), \ - axis=3) + self._current = np.flip(reshape_current, axis=3) # Get p1 scatter xs from CMFD tally 3 tally_id = self._cmfd_tally_ids[3] tally_results = tallies[tally_id].results[:,0,1] + # Define target tally reshape dimensions for p1 scatter tally + target_tally_shape = [nz, ny, nx, 2, ng] + # Reshape and extract only p1 data from tally results (no need for p0 data) - p1scattrr = tally_results.reshape(self._p1scattxs.shape+(2,))[:,:,:,1] + p1scattrr = np.swapaxes(tally_results.reshape(target_tally_shape), + 0, 2)[:,:,:,1,:] # Store p1 scatter xs # p1 scatter xs is flipped in energy axis as tally results are given in @@ -2488,7 +2539,8 @@ class CMFDRun(object): # Reshape coremap to three dimensional array as all cross section data # has been reshaped - self._coremap = self._coremap.reshape(self._indices[0:3]) + self._coremap = self._coremap.reshape(nz,ny,nx) + self._coremap = np.swapaxes(self._coremap, 0, 2) def _compute_effective_downscatter(self): """Changes downscatter rate for zero upscatter""" @@ -2942,7 +2994,7 @@ class CMFDRun(object): cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) # Define dtilde at front surface for all mesh cells on front boundary self._dhat[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], - (net_current_front[:,-1,:,:] + self._dtilde[:,-1,:,:,3] * \ + (net_current_front[:,-1,:,:] - self._dtilde[:,-1,:,:,3] * \ cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) # Define dtilde at bottom surface for all mesh cells on bottom boundary self._dhat[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], @@ -2950,7 +3002,7 @@ class CMFDRun(object): cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) # Define dtilde at top surface for all mesh cells on top boundary self._dhat[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], - (net_current_top[:,:,-1,:] + self._dtilde[:,:,-1,:,5] * \ + (net_current_top[:,:,-1,:] - self._dtilde[:,:,-1,:,5] * \ cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) # Logical for whether neighboring cell to the left is reflector region diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 index 7f0eb965a2..01404d9b0e 100644 --- a/src/cmfd_solver.F90 +++ b/src/cmfd_solver.F90 @@ -150,8 +150,8 @@ contains call loss % write('adj_loss.dat') call prod % write('adj_prod.dat') else - call loss % write('adj.dat') - call prod % write('adj.dat') + call loss % write('loss.dat') + call prod % write('prod.dat') end if end if From 91180f08a747c57f5c9dc437111ac06d3fa703b7 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 19 Sep 2018 20:20:31 -0400 Subject: [PATCH 30/58] minor changes that should also be cherry-picked to shikhar413::cmfd-capi --- openmc/cmfd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index d5002279c5..cbec331999 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1548,7 +1548,7 @@ class CMFDRun(object): # Check and raise error if source sites exist outside of CMFD mesh if openmc.capi.master() and outside: - raise OpenMCError('Source sites outside of the CMFD mesh!') + raise OpenMCError('Source sites outside of the CMFD mesh') # Have master compute weight factors, ignore any zeros in # sourcecounts or cmfd_src @@ -1656,9 +1656,9 @@ class CMFDRun(object): if have_mpi: # Collect values of count from all processors - self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM, 0) + self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM, root=0) # Check if there were sites outside the mesh for any processor - self._intracomm.Reduce(outside, sites_outside, MPI.LOR, 0) + self._intracomm.Reduce(outside, sites_outside, MPI.LOR, root=0) # Deal with case if MPI not defined (only one proc) else: sites_outside = outside From 2a709a0a563651e9f289e8ad98f504298c9ff13f Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 20 Sep 2018 10:03:08 -0400 Subject: [PATCH 31/58] Remove subdivision of next_batch, CAPI CMFD no longer preserves order of calls from Fortran side --- include/openmc.h | 3 - openmc/capi/core.py | 37 ------------- openmc/cmfd.py | 28 ++++------ src/api.F90 | 3 - src/simulation.F90 | 132 -------------------------------------------- 5 files changed, 10 insertions(+), 193 deletions(-) diff --git a/include/openmc.h b/include/openmc.h index 92c55f79b1..0e367079da 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -75,9 +75,6 @@ extern "C" { int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_next_batch(int* status); - int openmc_next_batch_before_cmfd_init(); - int openmc_next_batch_between_cmfd_init_execute(int* status); - int openmc_next_batch_after_cmfd_execute(int* status); int openmc_nuclide_name(int index, char** name); int openmc_particle_restart(); int openmc_plot_geometry(); diff --git a/openmc/capi/core.py b/openmc/capi/core.py index d09c93963c..258220a934 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -39,15 +39,6 @@ _dll.openmc_get_keff.errcheck = _error_handler _dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int _dll.openmc_next_batch.errcheck = _error_handler -_dll.openmc_next_batch_before_cmfd_init.argtypes = [] -_dll.openmc_next_batch_before_cmfd_init.restype = c_int -_dll.openmc_next_batch_before_cmfd_init.errcheck = _error_handler -_dll.openmc_next_batch_between_cmfd_init_execute.argtypes = [] -_dll.openmc_next_batch_between_cmfd_init_execute.restype = c_int -_dll.openmc_next_batch_between_cmfd_init_execute.errcheck = _error_handler -_dll.openmc_next_batch_after_cmfd_execute.argtypes = [POINTER(c_int)] -_dll.openmc_next_batch_after_cmfd_execute.restype = c_int -_dll.openmc_next_batch_after_cmfd_execute.errcheck = _error_handler _dll.openmc_plot_geometry.restype = c_int _dll.openmc_plot_geometry.restype = _error_handler _dll.openmc_run.restype = c_int @@ -275,34 +266,6 @@ def next_batch(): return status.value -def next_batch_before_cmfd_init(): - """Run everything in batch that occurs before CMFD initialized.""" - _dll.openmc_next_batch_before_cmfd_init() - - -def next_batch_between_cmfd_init_execute(): - """Run everything in batch that occurs between CMFD - initialization and execution. - - """ - _dll.openmc_next_batch_between_cmfd_init_execute() - - -def next_batch_after_cmfd_execute(): - """Run next batch. - - Returns - ------- - int - Status after running a batch (0=normal, 1=reached maximum number of - batches, 2=tally triggers reached) - - """ - status = c_int() - _dll.openmc_next_batch_after_cmfd_execute(status) - return status.value - - def plot_geometry(): """Plot geometry""" _dll.openmc_plot_geometry() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index cbec331999..95bc4c0fd4 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1056,34 +1056,26 @@ class CMFDRun(object): openmc.capi.simulation_init() while(True): - # Run everything in next batch before initializing CMFD - openmc.capi.next_batch_before_cmfd_init() - # Initialize CMFD batch self._cmfd_init_batch() - # Run everything in next batch in between initializing and - # executing CMFD - openmc.capi.next_batch_between_cmfd_init_execute() + # Run next batch + status = openmc.capi.next_batch() # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd(vectorized) - # Run everything in next batch after executing CMFD. Status - # determines whether another batch should be run or - # simulation should be terminated. - status = openmc.capi.next_batch_after_cmfd_execute() - - # Write CMFD output if CMFD on for current batch - if self._cmfd_on and openmc.capi.master(): - self._write_cmfd_output() + # Write CMFD output if CMFD on for current batch + if openmc.capi.master(): + self._write_cmfd_output() if status != 0: break # Finalize simuation openmc.capi.simulation_finalize() + # Print out CMFD timing statistics self._write_cmfd_timing_stats() @@ -1225,16 +1217,16 @@ class CMFDRun(object): def _cmfd_init_batch(self): """Handles CMFD options at the beginning of each batch""" - # Get simulation parameters through C API - current_batch = openmc.capi.current_batch() + # Get current batch through C API + # Add 1 as next_batch has not been called yet + current_batch = openmc.capi.current_batch() + 1 # Check to activate CMFD diffusion and possible feedback if self._cmfd_begin == current_batch: self._cmfd_on = True # Check to reset tallies - if (self._n_cmfd_resets > 0 - and current_batch in self._cmfd_reset): + if self._n_cmfd_resets > 0 and current_batch in self._cmfd_reset: self._cmfd_tally_reset() def _execute_cmfd(self, vectorized): diff --git a/src/api.F90 b/src/api.F90 index df1719c711..51cc14766c 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -76,9 +76,6 @@ module openmc_api public :: openmc_mesh_filter_set_mesh public :: openmc_meshsurface_filter_set_mesh public :: openmc_next_batch - public :: openmc_next_batch_before_cmfd_init - public :: openmc_next_batch_between_cmfd_init_execute - public :: openmc_next_batch_after_cmfd_execute public :: openmc_nuclide_name public :: openmc_plot_geometry public :: openmc_reset diff --git a/src/simulation.F90 b/src/simulation.F90 index 12af1a8924..df6bf13bae 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -47,9 +47,6 @@ module simulation public :: openmc_next_batch public :: openmc_simulation_init public :: openmc_simulation_finalize - public :: openmc_next_batch_before_cmfd_init - public :: openmc_next_batch_between_cmfd_init_execute - public :: openmc_next_batch_after_cmfd_execute integer(C_INT), parameter :: STATUS_EXIT_NORMAL = 0 integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1 @@ -132,135 +129,6 @@ contains end function openmc_next_batch -!=============================================================================== -! OPENMC_NEXT_BATCH_BEFORE_CMFD_INIT -!=============================================================================== - - function openmc_next_batch_before_cmfd_init() result(err) bind(C) - integer(C_INT) :: err - - err = 0 - - ! Make sure simulation has been initialized - if (.not. simulation_initialized) then - err = E_ALLOCATE - call set_errmsg("Simulation has not been initialized yet.") - return - end if - - call initialize_batch() - - end function openmc_next_batch_before_cmfd_init - -!=============================================================================== -! OPENMC_NEXT_BATCH_BETWEEN_CMFD_INIT_EXECUTE -!=============================================================================== - - function openmc_next_batch_between_cmfd_init_execute() result(err) bind(C) - integer(C_INT) :: err - - type(Particle) :: p - integer(8) :: i_work - - err = 0 - - ! ======================================================================= - ! LOOP OVER GENERATIONS - GENERATION_LOOP: do current_gen = 1, gen_per_batch - - call initialize_generation() - - ! Start timer for transport - call time_transport % start() - - ! ==================================================================== - ! LOOP OVER PARTICLES -!$omp parallel do schedule(runtime) firstprivate(p) copyin(tally_derivs) - PARTICLE_LOOP: do i_work = 1, work - current_work = i_work - - ! grab source particle from bank - call initialize_history(p, current_work) - - ! transport particle - call transport(p) - - end do PARTICLE_LOOP -!$omp end parallel do - - ! Accumulate time for transport - call time_transport % stop() - - call finalize_generation() - - end do GENERATION_LOOP - - ! Reduce tallies onto master process and accumulate - call time_tallies % start() - call accumulate_tallies() - call time_tallies % stop() - - ! Reset global tally results - if (current_batch <= n_inactive) then - global_tallies(:,:) = ZERO - n_realizations = 0 - end if - - end function openmc_next_batch_between_cmfd_init_execute - -!=============================================================================== -! OPENMC_NEXT_BATCH_AFTER_CMFD_EXECUTE -!=============================================================================== - - function openmc_next_batch_after_cmfd_execute(status) result(err) bind(C) - integer(C_INT), intent(out), optional :: status - integer(C_INT) :: err -#ifdef OPENMC_MPI - integer :: mpi_err ! MPI error code -#endif - - err = 0 - - if (run_mode == MODE_EIGENVALUE) then - ! Write batch output - if (master .and. verbosity >= 7) call print_batch_keff() - end if - - ! Check_triggers - if (master) call check_triggers() -#ifdef OPENMC_MPI - call MPI_BCAST(satisfy_triggers, 1, MPI_LOGICAL, 0, & - mpi_intracomm, mpi_err) -#endif - if (satisfy_triggers .or. & - (trigger_on .and. current_batch == n_max_batches)) then - call statepoint_batch % add(current_batch) - end if - - ! Write out state point if it's been specified for this batch - if (statepoint_batch % contains(current_batch)) then - err = openmc_statepoint_write() - end if - - ! Write out source point if it's been specified for this batch - if ((sourcepoint_batch % contains(current_batch) .or. source_latest) .and. & - source_write) then - call write_source_point() - end if - - ! Check simulation ending criteria - if (present(status)) then - if (current_batch == n_max_batches) then - status = STATUS_EXIT_MAX_BATCH - elseif (satisfy_triggers) then - status = STATUS_EXIT_ON_TRIGGER - else - status = STATUS_EXIT_NORMAL - end if - end if - - end function openmc_next_batch_after_cmfd_execute - !=============================================================================== ! INITIALIZE_HISTORY !=============================================================================== From e0363898b7cff259aa29d288b397937d5c5090d3 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 11 Oct 2018 02:41:01 -0400 Subject: [PATCH 32/58] Correct surface currents tally on Python side, throw error if not in CE mode --- openmc/cmfd.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index aed5508dac..7120558b78 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1101,12 +1101,14 @@ class CMFDRun(object): def _write_cmfd_timing_stats(self): """Write CMFD timing statistics to buffer after finalizing simulation""" - print( + if openmc.capi.master(): + print( """=====================> CMFD TIMING STATISTICS <==================== Time in CMFD = {0:.5E} seconds Building matrices = {1:.5E} seconds Solving matrices = {2:.5E} seconds +<<<<<<< HEAD """.format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve)) sys.stdout.flush() @@ -1136,18 +1138,19 @@ class CMFDRun(object): 'simulation') # Set spatial dimensions of CMFD object - # Iterate through each element of self._cmfd_mesh.dimension - # as could be length 2 or 3 for i, n in enumerate(self._cmfd_mesh.dimension): self._indices[i] = n + # Check if in continuous energy mode + if not openmc.capi.settings.run_CE: + raise OpenMCError('CMFD must be run in continuous energy mode') + # Set number of energy groups if self._cmfd_mesh.energy is not None: ng = len(self._cmfd_mesh.energy) self._egrid = np.array(self._cmfd_mesh.energy) self._indices[3] = ng - 1 self._energy_filters = True - # TODO: MG mode check else: self._egrid = np.array([_ENERGY_MIN_NEUTRON, _ENERGY_MAX_NEUTRON]) self._indices[3] = 1 @@ -1157,7 +1160,7 @@ class CMFDRun(object): if self._cmfd_mesh.albedo is not None: self._albedo = np.array(self._cmfd_mesh.albedo) else: - self._albedo = np.array([1.,1.,1.,1.,1.,1.]) + self._albedo = np.array([1., 1., 1., 1., 1., 1.]) # Get acceleration map, otherwise set all regions to be accelerated if self._cmfd_mesh.map is not None: @@ -2497,11 +2500,12 @@ class CMFDRun(object): current = np.where(np.repeat(flux>0, 12), tally_results, 0.) # Define target tally reshape dimensions for current - target_tally_shape = [nz, ny, nx, ng, 12] + target_tally_shape = [nz, ny, nx, 12, ng] # Reshape current array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, 12] reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) + reshape_current = np.swapaxes(reshape_current, 3, 4) # Current is flipped in energy axis as tally results are given in # reverse order of energy group @@ -2578,8 +2582,11 @@ class CMFDRun(object): # Extract energy indices ng = self._indices[3] + # Get number of accelerated regions + num_accel = np.sum(self._coremap != _CMFD_NOACCEL) + # Get openmc k-effective - keff = openmc.capi.keff()[0] + keff = openmc.capi.keff_temp()[0] # Define leakage in each mesh cell and energy group leakage = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ @@ -2617,7 +2624,7 @@ class CMFDRun(object): # Calculate RMS and record for this batch self._balance.append(np.sqrt( np.sum(np.multiply(self._resnb, self._resnb)) / \ - np.count_nonzero(self._resnb))) + (ng * num_accel))) def _compute_dtilde_vectorized(self): """Computes the diffusion coupling coefficient using a vectorized numpy From 4a03fc9a03593d784be1832223bb7056503d7129 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 17 Oct 2018 11:09:01 -0400 Subject: [PATCH 33/58] Add more flushes for print statements --- openmc/cmfd.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 7120558b78..3ce48c2ac3 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -33,7 +33,6 @@ from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) from openmc.exceptions import OpenMCError - """ -------------- CMFD CONSTANTS @@ -59,6 +58,7 @@ _CURRENTS = { 'out_bottom': 8, 'in_bottom': 9, 'out_top' : 10, 'in_top' : 11 } + class CMFDMesh(object): """A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD) acceleration. @@ -604,6 +604,7 @@ class CMFD(object): tree.write("cmfd.xml", xml_declaration=True, encoding='utf-8', method="xml") + class CMFDRun(object): r"""Class to run openmc with CMFD acceleration through the C API. Running openmc in this manner obviates the need for defining CMFD parameters @@ -782,7 +783,6 @@ class CMFDRun(object): set in this method. """ - # Variables that users can modify self._cmfd_begin = 1 self._dhat_reset = False @@ -1110,7 +1110,7 @@ class CMFDRun(object): Solving matrices = {2:.5E} seconds <<<<<<< HEAD """.format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve)) - sys.stdout.flush() + sys.stdout.flush() def _configure_cmfd(self): """Initialize CMFD parameters and set CMFD input variables""" @@ -1269,6 +1269,7 @@ class CMFDRun(object): # Print message if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): print(' CMFD tallies reset') + sys.stdout.flush() # Reset CMFD tallies tallies = openmc.capi.tallies @@ -1601,8 +1602,10 @@ class CMFDRun(object): if openmc.capi.master() and np.any(source_energies < energy[0]): print(' WARNING: Source pt below energy grid') + sys.stdout.flush() if openmc.capi.master() and np.any(source_energies > energy[-1]): print(' WARNING: Source pt above energy grid') + sys.stdout.flush() def _count_bank_sites(self): """Determines the number of fission bank sites in each cell of a given @@ -2349,6 +2352,7 @@ class CMFDRun(object): str3 = 'k-error: {0:.5E}'.format(kerr) str4 = 'src-error: {0:.5E}'.format(serr) print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) + sys.stdout.flush() return iconv, serr @@ -3167,6 +3171,7 @@ class CMFDRun(object): if self._dhat_reset and openmc.capi.settings.verbosity >= 8 and \ openmc.capi.master(): print(' Dhats reset to zero') + sys.stdout.flush() def _get_reflector_albedo(self, l, g, i, j, k): """Calculates the albedo to the reflector by returning ratio of From b4d7ddbd483ff5fd46d463a0430ed29113d69712 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 18 Oct 2018 20:09:44 -0400 Subject: [PATCH 34/58] Switch index of current tally direction and num groups to be consistent with openmc tallies --- openmc/cmfd.py | 105 ++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 3ce48c2ac3..38f67ab5a0 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1201,7 +1201,7 @@ class CMFDRun(object): self._hxyz = np.zeros((nx, ny, nz, 3)) # Allocate surface currents - self._current = np.zeros((nx, ny, nz, ng, 12)) + self._current = np.zeros((nx, ny, nz, 12, ng)) # Allocate source distributions self._cmfd_src = np.zeros((nx, ny, nz, ng)) @@ -2509,11 +2509,10 @@ class CMFDRun(object): # Reshape current array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, 12] reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) - reshape_current = np.swapaxes(reshape_current, 3, 4) # Current is flipped in energy axis as tally results are given in # reverse order of energy group - self._current = np.flip(reshape_current, axis=3) + self._current = np.flip(reshape_current, axis=4) # Get p1 scatter xs from CMFD tally 3 tally_id = self._cmfd_tally_ids[3] @@ -2593,18 +2592,18 @@ class CMFDRun(object): keff = openmc.capi.keff_temp()[0] # Define leakage in each mesh cell and energy group - leakage = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ - self._current[:,:,:,:,_CURRENTS['in_right']]) - \ - (self._current[:,:,:,:,_CURRENTS['in_left']] - \ - self._current[:,:,:,:,_CURRENTS['out_left']])) + \ - ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ - self._current[:,:,:,:,_CURRENTS['in_front']]) - \ - (self._current[:,:,:,:,_CURRENTS['in_back']] - \ - self._current[:,:,:,:,_CURRENTS['out_back']])) + \ - ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ - self._current[:,:,:,:,_CURRENTS['in_top']]) - \ - (self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ - self._current[:,:,:,:,_CURRENTS['out_bottom']])) + leakage = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ + self._current[:,:,:,_CURRENTS['in_right'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_left'],:] - \ + self._current[:,:,:,_CURRENTS['out_left'],:])) + \ + ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ + self._current[:,:,:,_CURRENTS['in_front'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_back'],:] - \ + self._current[:,:,:,_CURRENTS['out_back'],:])) + \ + ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ + self._current[:,:,:,_CURRENTS['in_top'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ + self._current[:,:,:,_CURRENTS['out_bottom'],:])) # Compute total rr interactions = self._totalxs * self._flux @@ -2713,10 +2712,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the left surface, in case # a cell borders a reflector region on the left - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_left']], - self._current[:,:,:,:,_CURRENTS['out_left']], - where=self._current[:,:,:,:,_CURRENTS['out_left']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_left']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_left'],:], + self._current[:,:,:,_CURRENTS['out_left'],:], + where=self._current[:,:,:,_CURRENTS['out_left'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_left'],:])) # Logical for whether neighboring cell to the left is reflector region adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to left @@ -2739,10 +2738,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the right surface, in case # a cell borders a reflector region on the right - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_right']], - self._current[:,:,:,:,_CURRENTS['out_right']], - where=self._current[:,:,:,:,_CURRENTS['out_right']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_right']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_right'],:], + self._current[:,:,:,_CURRENTS['out_right'],:], + where=self._current[:,:,:,_CURRENTS['out_right'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_right'],:])) # Logical for whether neighboring cell to the right is reflector region adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to right @@ -2765,10 +2764,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the back surface, in case # a cell borders a reflector region on the back - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_back']], - self._current[:,:,:,:,_CURRENTS['out_back']], - where=self._current[:,:,:,:,_CURRENTS['out_back']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_back']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_back'],:], + self._current[:,:,:,_CURRENTS['out_back'],:], + where=self._current[:,:,:,_CURRENTS['out_back'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_back'],:])) # Logical for whether neighboring cell to the back is reflector region adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to back @@ -2791,10 +2790,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the front surface, in case # a cell borders a reflector region in the front - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_front']], - self._current[:,:,:,:,_CURRENTS['out_front']], - where=self._current[:,:,:,:,_CURRENTS['out_front']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_front']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_front'],:], + self._current[:,:,:,_CURRENTS['out_front'],:], + where=self._current[:,:,:,_CURRENTS['out_front'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_front'],:])) # Logical for whether neighboring cell to the front is reflector region adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to front @@ -2817,10 +2816,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the bottom surface, in case # a cell borders a reflector region on the bottom - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_bottom']], - self._current[:,:,:,:,_CURRENTS['out_bottom']], - where=self._current[:,:,:,:,_CURRENTS['out_bottom']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_bottom']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_bottom'],:], + self._current[:,:,:,_CURRENTS['out_bottom'],:], + where=self._current[:,:,:,_CURRENTS['out_bottom'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_bottom'],:])) # Logical for whether neighboring cell to the bottom is reflector region adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to bottom @@ -2843,10 +2842,10 @@ class CMFDRun(object): # Define reflector albedo for all cells on the top surface, in case # a cell borders a reflector region on the top - ref_albedo = np.divide(self._current[:,:,:,:,_CURRENTS['in_top']], - self._current[:,:,:,:,_CURRENTS['out_top']], - where=self._current[:,:,:,:,_CURRENTS['out_top']] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,:,_CURRENTS['out_top']])) + ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_top'],:], + self._current[:,:,:,_CURRENTS['out_top'],:], + where=self._current[:,:,:,_CURRENTS['out_top'],:] > 1.0e-10, + out=np.ones_like(self._current[:,:,:,_CURRENTS['out_top'],:])) # Logical for whether neighboring cell to the top is reflector region adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL # Diffusion coefficient of neighbor to top @@ -2953,28 +2952,28 @@ class CMFDRun(object): """ # Define net current on each face, divided by surface area - net_current_left = ((self._current[:,:,:,:,_CURRENTS['in_left']] - \ - self._current[:,:,:,:,_CURRENTS['out_left']]) / \ + net_current_left = ((self._current[:,:,:,_CURRENTS['in_left'],:] - \ + self._current[:,:,:,_CURRENTS['out_left'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,0]) - net_current_right = ((self._current[:,:,:,:,_CURRENTS['out_right']] - \ - self._current[:,:,:,:,_CURRENTS['in_right']]) / \ + net_current_right = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ + self._current[:,:,:,_CURRENTS['in_right'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,0]) - net_current_back = ((self._current[:,:,:,:,_CURRENTS['in_back']] - \ - self._current[:,:,:,:,_CURRENTS['out_back']]) / \ + net_current_back = ((self._current[:,:,:,_CURRENTS['in_back'],:] - \ + self._current[:,:,:,_CURRENTS['out_back'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,1]) - net_current_front = ((self._current[:,:,:,:,_CURRENTS['out_front']] - \ - self._current[:,:,:,:,_CURRENTS['in_front']]) / \ + net_current_front = ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ + self._current[:,:,:,_CURRENTS['in_front'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,1]) - net_current_bottom = ((self._current[:,:,:,:,_CURRENTS['in_bottom']] - \ - self._current[:,:,:,:,_CURRENTS['out_bottom']]) / \ + net_current_bottom = ((self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ + self._current[:,:,:,_CURRENTS['out_bottom'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,2]) - net_current_top = ((self._current[:,:,:,:,_CURRENTS['out_top']] - \ - self._current[:,:,:,:,_CURRENTS['in_top']]) / \ + net_current_top = ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ + self._current[:,:,:,_CURRENTS['in_top'],:]) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ self._hxyz[:,:,:,np.newaxis,2]) @@ -3118,7 +3117,7 @@ class CMFDRun(object): # Get cell data cell_dtilde = self._dtilde[i,j,k,g,:] cell_flux = self._flux[i,j,k,g]/np.product(self._hxyz[i,j,k,:]) - current = self._current[i,j,k,g,:] + current = self._current[i,j,k,:,g] # Setup of vector to identify boundary conditions bound = np.repeat([i,j,k], 2) @@ -3198,7 +3197,7 @@ class CMFDRun(object): """ # Get partial currents from object - current = self._current[i,j,k,g,:] + current = self._current[i,j,k,:,g] # Calculate albedo if current[2*l] < 1.0e-10: From 530e1e0733c576077b5b8662978f7c637023b029 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 23 Oct 2018 13:35:39 -0400 Subject: [PATCH 35/58] Rename openmc_keff to keff --- openmc/capi/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 6ac7b10153..f429f23b2d 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -237,8 +237,8 @@ def keff_temp(): """ n = openmc.capi.num_realizations() - mean = c_double.in_dll(_dll, 'openmc_keff').value - std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \ + mean = c_double.in_dll(_dll, 'keff').value + std_dev = c_double.in_dll(_dll, 'keff_std').value \ if n > 1 else np.inf return (mean, std_dev) From d57592a0314ce910f832a02e0e9d47cacf47f87f Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 31 Oct 2018 01:18:24 -0400 Subject: [PATCH 36/58] Initial attempt at making vectorized functions more readable --- openmc/cmfd.py | 865 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 853 insertions(+), 12 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 38f67ab5a0..9a82d0fd83 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1302,12 +1302,14 @@ class CMFDRun(object): # Calculate dtilde if vectorized: self._compute_dtilde_vectorized() + self._compute_dtilde_vectorized_updated() else: self._compute_dtilde() # Calculate dhat if vectorized: self._compute_dhat_vectorized() + self._compute_dhat_vectorized_updated() else: self._compute_dhat() @@ -1464,12 +1466,15 @@ class CMFDRun(object): # Initialize CMFD flux map that maps phi to actual spatial and # group indices of problem cmfd_flux = np.zeros((nx, ny, nz, ng)) + cmfd_flux2 = np.zeros((nx, ny, nz, ng)) # Loop over all groups and set CMFD flux based on indices of # coremap and values of phi for g in range(ng): phi_g = phi[:,g] cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] + cmfd_flux2[idx + (g,)] = phi_g[self._coremap[idx]] + print(np.where(cmfd_flux != cmfd_flux2)) # Compute fission source cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ @@ -1594,6 +1599,16 @@ class CMFDRun(object): energy_bins = np.where(source_energies < energy[0], ng - 1, np.where(source_energies > energy[-1], 0, \ ng - np.digitize(source_energies, energy))) + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins2 = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins2[idx] = ng - 1 + idx = np.where(source_energies > energy[-1]) + energy_bins2[idx] = 0 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins2[idx] = ng - np.digitize(source_energies, energy) + print(np.where(energy_bins != energy_bins2)) # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight @@ -1644,6 +1659,17 @@ class CMFDRun(object): np.where(source_energies > energy[-1], ng - 1, \ np.digitize(source_energies, energy) - 1)) + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins2 = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins2[idx] = 0 + idx = np.where(source_energies > energy[-1]) + energy_bins2[idx] = ng - 1 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins2[idx] = np.digitize(source_energies, energy) - 1 + print(np.where(energy_bins != energy_bins2)) + # Determine all unique combinations of mesh bin and energy bin, and # count number of particles that belong to these combinations idx, counts = np.unique(np.array([mesh_bins, energy_bins]), axis=1, @@ -1686,7 +1712,9 @@ class CMFDRun(object): # Build loss and production matrices if vectorized: loss = self._build_loss_matrix_vectorized(adjoint) + loss2 = self._build_loss_matrix_vectorized_updated(adjoint,loss) prod = self._build_prod_matrix_vectorized(adjoint) + prod2 = self._build_prod_matrix_vectorized_updated(adjoint,prod) else: loss = self._build_loss_matrix(adjoint) prod = self._build_prod_matrix(adjoint) @@ -2048,6 +2076,215 @@ class CMFDRun(object): loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return loss + def _build_loss_matrix_vectorized_updated(self, adjoint, org_loss): + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Define rows, columns, and data used to build csr matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + data = np.array([]) + + dtilde_left = self._dtilde[:,:,:,:,0] + dtilde_right = self._dtilde[:,:,:,:,1] + dtilde_back = self._dtilde[:,:,:,:,2] + dtilde_front = self._dtilde[:,:,:,:,3] + dtilde_bottom = self._dtilde[:,:,:,:,4] + dtilde_top = self._dtilde[:,:,:,:,5] + dhat_left = self._dhat[:,:,:,:,0] + dhat_right = self._dhat[:,:,:,:,1] + dhat_back = self._dhat[:,:,:,:,2] + dhat_front = self._dhat[:,:,:,:,3] + dhat_bottom = self._dhat[:,:,:,:,4] + dhat_top = self._dhat[:,:,:,:,5] + + dx = self._hxyz[:,:,:,np.newaxis,0] + dy = self._hxyz[:,:,:,np.newaxis,1] + dz = self._hxyz[:,:,:,np.newaxis,2] + + # Define net leakage coefficient for each surface in each matrix element + jnet = (((dtilde_right + dhat_right) - (-1.0 * dtilde_left + dhat_left)) / dx + + ((dtilde_front + dhat_front) - (-1.0 * dtilde_back + dhat_back)) / dy + + ((dtilde_top + dhat_top) - (-1.0 * dtilde_bottom + dhat_bottom)) / dz) + + # Shift coremap in all directions to determine whether leakage term + # should be defined for particular cell in matrix + coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] + + coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] + + coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] + + coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] + + coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] + + coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] + + for g in range(ng): + # Define leakage terms that relate terms to their neighbors to the + # left + + # Extract all indices where a cell and its neighbor to the left + # are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_left != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_left[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,0][indices] + dhat = self._dhat[:,:,:,g,0][indices] + dx = self._hxyz[:,:,:,0][indices] + vals = (-1.0 * dtilde - dhat) / dx + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # right + + # Extract all regions where a cell and its neighbor to the right + # are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_right != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_right[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,1][indices] + dhat = self._dhat[:,:,:,g,1][indices] + dx = self._hxyz[:,:,:,0][indices] + vals = (-1.0 * dtilde + dhat) / dx + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors in the + # back + + # Extract all regions where a cell and its neighbor in the back + # are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_back != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_back[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,2][indices] + dhat = self._dhat[:,:,:,g,2][indices] + dy = self._hxyz[:,:,:,1][indices] + vals = (-1.0 * dtilde - dhat) / dy + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors in the + # front + + # Extract all regions where a cell and its neighbor in the front + # are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_front != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_front[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,3][indices] + dhat = self._dhat[:,:,:,g,3][indices] + dy = self._hxyz[:,:,:,1][indices] + vals = (-1.0 * dtilde + dhat) / dy + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # bottom + + # Extract all regions where a cell and its neighbor to the bottom + # are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_bottom != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_bottom[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,4][indices] + dhat = self._dhat[:,:,:,g,4][indices] + dz = self._hxyz[:,:,:,2][indices] + vals = (-1.0 * dtilde - dhat) / dz + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # top + + # Extract all regions where a cell and its neighbor to the + # top are both fuel regions + indices = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_top != _CMFD_NOACCEL)) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (coremap_shift_top[indices]) + g + # Compute leakage term associated with these regions + dtilde = self._dtilde[:,:,:,g,5][indices] + dhat = self._dhat[:,:,:,g,5][indices] + dz = self._hxyz[:,:,:,2][indices] + vals = (-1.0 * dtilde + dhat) / dz + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define terms that relate to loss of neutrons in a cell. These + # correspond to all the diagonal entries of the loss matrix + + # Extract all regions where a cell is a fuel region + indices = np.where(self._coremap != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[indices]) + g + idx_y = idx_x + jnet_g = jnet[:,:,:,g][indices] + total_xs = self._totalxs[:,:,:,g][indices] + scatt_xs = self._scattxs[:,:,:,g,g][indices] + vals = jnet_g + total_xs - scatt_xs + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Define terms that relate to in-scattering from group to group. + # These terms relate a mesh index to all mesh indices with the same + # spatial dimensions but belong to a different energy group + for h in range(ng): + if h != g: + # Extract all regions where a cell is a fuel region + indices = np.where(self._coremap != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (self._coremap[indices]) + h + # Get scattering macro xs, transposed + if adjoint: + scatt_xs = self._scattxs[:,:,:,g,h][indices] + # Get scattering macro xs + else: + scatt_xs = self._scattxs[:,:,:,h,g][indices] + vals = -1.0 * scatt_xs + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Create csr matrix + loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) + print(np.where(loss.toarray() != org_loss.toarray())) + return loss + def _build_prod_matrix(self, adjoint): """Creates matrix representing production of neutrons. This method uses for @@ -2155,6 +2392,39 @@ class CMFDRun(object): prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return prod + def _build_prod_matrix_vectorized_updated(self, adjoint, org_prod): + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Define rows, columns, and data used to build csr matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + data = np.array([]) + + # Define terms that relate to fission production from group to group. + for g in range(ng): + for h in range(ng): + # Extract all regions where a cell is a fuel region + indices = np.where(self._coremap != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (self._coremap[indices]) + h + # Get nu-fission macro xs, transposed + if adjoint: + vals = (self._nfissxs[:, :, :, g, h])[indices] + # Get nu-fission macro xs + else: + vals = (self._nfissxs[:, :, :, h, g])[indices] + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + data = np.append(data, vals) + + # Create csr matrix + prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) + print(np.where(prod.toarray() != org_prod.toarray())) + return prod + def _matrix_to_indices(self, irow, nx, ny, nz, ng): """Converts matrix index in CMFD matrices to spatial and group indices of actual problem, based on values from coremap @@ -2866,6 +3136,328 @@ class CMFDRun(object): (neig_hxyz[:,:,:-1,np.newaxis,2] * self._diffcof[:,:,:-1,:] + \ self._hxyz[:,:,:-1,np.newaxis,2] * neig_dc[:,:,:-1,:])), 0.0) + ################################################################################# + def _compute_dtilde_vectorized_updated(self): + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + self._dtilde2 = np.zeros((nx, ny, nz, ng, 6)) + + # Logical for determining whether region of interest is accelerated region + is_accel = self._coremap != _CMFD_NOACCEL + # Logical for determining whether a zero flux "albedo" b.c. should be + # applied + is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + + # Define dtilde at left surface for all mesh cells on left boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[0,:,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + if is_zero_flux_alb[0]: + self._dtilde2[boundary_grps + (0,)] = 2.0 * D / dx + else: + alb = self._albedo[0] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + # Define dtilde at right surface for all mesh cells on right boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[-1,:,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + if is_zero_flux_alb[1]: + self._dtilde2[boundary_grps + (1,)] = 2.0 * D / dx + else: + alb = self._albedo[1] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + # Define dtilde at back surface for all mesh cells on back boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,0,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + if is_zero_flux_alb[2]: + self._dtilde2[boundary_grps + (2,)] = 2.0 * D / dy + else: + alb = self._albedo[2] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + # Define dtilde at front surface for all mesh cells on front boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,-1,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + if is_zero_flux_alb[3]: + self._dtilde2[boundary_grps + (3,)] = 2.0 * D / dy + else: + alb = self._albedo[3] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + # Define dtilde at bottom surface for all mesh cells on bottom boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,0] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + if is_zero_flux_alb[4]: + self._dtilde2[boundary_grps + (4,)] = 2.0 * D / dz + else: + alb = self._albedo[4] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + # Define dtilde at top surface for all mesh cells on top boundary + # Separate between zero flux b.c. and alebdo b.c. + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,-1] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + if is_zero_flux_alb[5]: + self._dtilde2[boundary_grps + (5,)] = 2.0 * D / dz + else: + alb = self._albedo[5] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + # Define reflector albedo for all cells on the left surface, in case + # a cell borders a reflector region on the left + current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] + current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] + ref_albedo = np.divide(current_in_left, current_out_left, + where=current_out_left > 1.0e-10, + out=np.ones_like(current_out_left)) + + # Logical for whether neighboring cell to the left is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to left + neig_dc = np.roll(self._diffcof, 1, axis=0) + # Cell dimensions of neighbor to left + neig_hxyz = np.roll(self._hxyz, 1, axis=0) + + # Define dtilde at left surface for all mesh cells not on left boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[1:,:,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + neig_D = neig_dc[boundary_grps] + neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * neig_D) + / (neig_dx * D + dx * neig_D)) + + # Define reflector albedo for all cells on the right surface, in case + # a cell borders a reflector region on the right + current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] + current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] + ref_albedo = np.divide(current_in_right, current_out_right, + where=current_out_right > 1.0e-10, + out=np.ones_like(current_out_right)) + + # Logical for whether neighboring cell to the right is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to right + neig_dc = np.roll(self._diffcof, -1, axis=0) + # Cell dimensions of neighbor to right + neig_hxyz = np.roll(self._hxyz, -1, axis=0) + + # Define dtilde at right surface for all mesh cells not on right boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:-1,:,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + neig_D = neig_dc[boundary_grps] + neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * neig_D) + / (neig_dx * D + dx * neig_D)) + + # Define reflector albedo for all cells on the back surface, in case + # a cell borders a reflector region on the back + current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] + current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] + ref_albedo = np.divide(current_in_back, current_out_back, + where=current_out_back > 1.0e-10, + out=np.ones_like(current_out_back)) + + # Logical for whether neighboring cell to the back is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to back + neig_dc = np.roll(self._diffcof, 1, axis=1) + # Cell dimensions of neighbor to back + neig_hxyz = np.roll(self._hxyz, 1, axis=1) + + # Define dtilde at back surface for all mesh cells not on back boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,1:,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + neig_D = neig_dc[boundary_grps] + neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * neig_D) + / (neig_dy * D + dy * neig_D)) + + # Define reflector albedo for all cells on the front surface, in case + # a cell borders a reflector region in the front + current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] + current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] + ref_albedo = np.divide(current_in_front, current_out_front, + where=current_out_front > 1.0e-10, + out=np.ones_like(current_out_front)) + + # Logical for whether neighboring cell to the front is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to front + neig_dc = np.roll(self._diffcof, -1, axis=1) + # Cell dimensions of neighbor to front + neig_hxyz = np.roll(self._hxyz, -1, axis=1) + + # Define dtilde at front surface for all mesh cells not on front boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:-1,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + neig_D = neig_dc[boundary_grps] + neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * neig_D) + / (neig_dy * D + dy * neig_D)) + + # Define reflector albedo for all cells on the bottom surface, in case + # a cell borders a reflector region on the bottom + current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] + current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] + ref_albedo = np.divide(current_in_bottom, current_out_bottom, + where=current_out_bottom > 1.0e-10, + out=np.ones_like(current_out_bottom)) + + # Logical for whether neighboring cell to the bottom is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to bottom + neig_dc = np.roll(self._diffcof, 1, axis=2) + # Cell dimensions of neighbor to bottom + neig_hxyz = np.roll(self._hxyz, 1, axis=2) + + # Define dtilde at bottom surface for all mesh cells not on bottom boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,1:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + neig_D = neig_dc[boundary_grps] + neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * neig_D) + / (neig_dz * D + dz * neig_D)) + + # Define reflector albedo for all cells on the top surface, in case + # a cell borders a reflector region on the top + current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] + current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] + ref_albedo = np.divide(current_in_top, current_out_top, + where=current_out_top > 1.0e-10, + out=np.ones_like(current_out_top)) + + # Logical for whether neighboring cell to the top is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + # Diffusion coefficient of neighbor to top + neig_dc = np.roll(self._diffcof, -1, axis=2) + # Cell dimensions of neighbor to top + neig_hxyz = np.roll(self._hxyz, -1, axis=2) + + # Define dtilde at top surface for all mesh cells not on top boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,:-1] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + neig_D = neig_dc[boundary_grps] + neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * neig_D) + / (neig_dz * D + dz * neig_D)) + print(np.where(self._dtilde2 != self._dtilde)) + def _compute_dtilde(self): """Computes the diffusion coupling coefficient by looping over all spatial regions and energy groups @@ -2982,27 +3574,27 @@ class CMFDRun(object): # Extract indices of coremap that are accelerated is_accel = self._coremap != _CMFD_NOACCEL - # Define dtilde at left surface for all mesh cells on left boundary + # Define dhat at left surface for all mesh cells on left boundary self._dhat[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], (net_current_left[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ cell_flux[0,:,:,:]) / cell_flux[0,:,:,:], 0) - # Define dtilde at right surface for all mesh cells on right boundary + # Define dhat at right surface for all mesh cells on right boundary self._dhat[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], (net_current_right[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ cell_flux[-1,:,:,:]) / cell_flux[-1,:,:,:], 0) - # Define dtilde at back surface for all mesh cells on back boundary + # Define dhat at back surface for all mesh cells on back boundary self._dhat[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], (net_current_back[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) - # Define dtilde at front surface for all mesh cells on front boundary + # Define dhat at front surface for all mesh cells on front boundary self._dhat[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], (net_current_front[:,-1,:,:] - self._dtilde[:,-1,:,:,3] * \ cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) - # Define dtilde at bottom surface for all mesh cells on bottom boundary + # Define dhat at bottom surface for all mesh cells on bottom boundary self._dhat[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], (net_current_bottom[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) - # Define dtilde at top surface for all mesh cells on top boundary + # Define dhat at top surface for all mesh cells on top boundary self._dhat[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], (net_current_top[:,:,-1,:] - self._dtilde[:,:,-1,:,5] * \ cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) @@ -3012,7 +3604,7 @@ class CMFDRun(object): # Cell flux of neighbor to left neig_flux = np.roll(self._flux, 1, axis=0) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at left surface for all mesh cells not on left boundary + # Define dhat at left surface for all mesh cells not on left boundary self._dhat[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ np.where(adj_reflector[1:,:,:,np.newaxis], (net_current_left[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ @@ -3026,7 +3618,7 @@ class CMFDRun(object): # Cell flux of neighbor to right neig_flux = np.roll(self._flux, -1, axis=0) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at right surface for all mesh cells not on right boundary + # Define dhat at right surface for all mesh cells not on right boundary self._dhat[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ np.where(adj_reflector[:-1,:,:,np.newaxis], (net_current_right[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ @@ -3040,7 +3632,7 @@ class CMFDRun(object): # Cell flux of neighbor to back neig_flux = np.roll(self._flux, 1, axis=1) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at back surface for all mesh cells not on back boundary + # Define dhat at back surface for all mesh cells not on back boundary self._dhat[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ np.where(adj_reflector[:,1:,:,np.newaxis], (net_current_back[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ @@ -3054,7 +3646,7 @@ class CMFDRun(object): # Cell flux of neighbor to front neig_flux = np.roll(self._flux, -1, axis=1) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at front surface for all mesh cells not on front boundary + # Define dhat at front surface for all mesh cells not on front boundary self._dhat[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ np.where(adj_reflector[:,:-1,:,np.newaxis], (net_current_front[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ @@ -3068,7 +3660,7 @@ class CMFDRun(object): # Cell flux of neighbor to bottom neig_flux = np.roll(self._flux, 1, axis=2) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at bottom surface for all mesh cells not on bottom boundary + # Define dhat at bottom surface for all mesh cells not on bottom boundary self._dhat[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ np.where(adj_reflector[:,:,1:,np.newaxis], (net_current_bottom[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ @@ -3082,7 +3674,7 @@ class CMFDRun(object): # Cell flux of neighbor to top neig_flux = np.roll(self._flux, -1, axis=2) / \ np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dtilde at top surface for all mesh cells not on top boundary + # Define dhat at top surface for all mesh cells not on top boundary self._dhat[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ np.where(adj_reflector[:,:,:-1,np.newaxis], (net_current_top[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ @@ -3091,6 +3683,255 @@ class CMFDRun(object): (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) + ################################################################################# + def _compute_dhat_vectorized_updated(self): + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + self._dhat2 = np.zeros((nx, ny, nz, ng, 6)) + + # Define current in each direction + current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] + current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] + current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] + current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] + current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] + current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] + current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] + current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] + current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] + current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] + current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] + current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] + + dx = self._hxyz[:,:,:,np.newaxis,0] + dy = self._hxyz[:,:,:,np.newaxis,1] + dz = self._hxyz[:,:,:,np.newaxis,2] + dxdydz = np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + + # Define net current on each face + net_current_left = (current_in_left - current_out_left) / dxdydz * dx + net_current_right = (current_out_right - current_in_right) / dxdydz * dx + net_current_back = (current_in_back - current_out_back) / dxdydz * dy + net_current_front = (current_out_front - current_in_front) / dxdydz * dy + net_current_bottom = (current_in_bottom - current_out_bottom) / dxdydz * dz + net_current_top = (current_out_top - current_in_top) / dxdydz * dz + + # Define flux in each cell + cell_flux = self._flux / dxdydz + # Extract indices of coremap that are accelerated + is_accel = self._coremap != _CMFD_NOACCEL + + # Define dhat at left surface for all mesh cells on left boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[0,:,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux + + # Define dhat at right surface for all mesh cells on right boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[-1,:,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux + + # Define dhat at back surface for all mesh cells on back boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,0,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux + + # Define dhat at front surface for all mesh cells on front boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,-1,:] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux + + # Define dhat at bottom surface for all mesh cells on bottom boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,0] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux + + # Define dhat at top surface for all mesh cells on top boundary + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,-1] = True + boundary = np.where(is_accel & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux + + # Logical for whether neighboring cell to the left is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + # Cell flux of neighbor to left + neig_flux = np.roll(self._flux, 1, axis=0) / dxdydz + + # Define dhat at left surface for all mesh cells not on left boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[1:,:,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + flux_left = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = ((net_current - dtilde * (flux_left - flux)) + / (flux_left + flux)) + + # Logical for whether neighboring cell to the right is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + # Cell flux of neighbor to right + neig_flux = np.roll(self._flux, -1, axis=0) / dxdydz + # Define dhat at right surface for all mesh cells not on right boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = np.where(is_accel[:-1,:,:] & adj_reflector[:-1,:,:]) + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux + + boundary = np.where(is_accel[:-1,:,:] & ~adj_reflector[:-1,:,:]) + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + flux_right = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = ((net_current + dtilde * (flux_right - flux)) + / (flux_right + flux)) + + # Logical for whether neighboring cell to the back is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + # Cell flux of neighbor to back + neig_flux = np.roll(self._flux, 1, axis=1) / dxdydz + + # Define dhat at back surface for all mesh cells not on back boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,1:,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + flux_back = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = ((net_current - dtilde * (flux_back - flux)) + / (flux_back + flux)) + + # Logical for whether neighboring cell to the front is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + # Cell flux of neighbor to front + neig_flux = np.roll(self._flux, -1, axis=1) / dxdydz + + # Define dhat at front surface for all mesh cells not on front boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:-1,:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + flux_front = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = ((net_current + dtilde * (flux_front - flux)) + / (flux_front + flux)) + + # Logical for whether neighboring cell to the bottom is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + # Cell flux of neighbor to bottom + neig_flux = np.roll(self._flux, 1, axis=2) / dxdydz + + # Define dhat at bottom surface for all mesh cells not on bottom boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,1:] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + flux_bottom = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = ((net_current - dtilde * (flux_bottom - flux)) + / (flux_bottom + flux)) + + # Logical for whether neighboring cell to the top is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + # Cell flux of neighbor to top + neig_flux = np.roll(self._flux, -1, axis=2) / dxdydz + + # Define dhat at top surface for all mesh cells not on top boundary + # Separate between cases where regions do and don't neighbor reflector regions + mask = np.full(self._coremap.shape, False, dtype=bool) + mask[:,:,:-1] = True + boundary = np.where(is_accel & adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux + + boundary = np.where(is_accel & ~adj_reflector & mask) + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + flux_top = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = ((net_current + dtilde * (flux_top - flux)) + / (flux_top + flux)) + print(np.where(self._dhat2 != self._dhat)) + + def _compute_dhat(self): """Computes the nonlinear coupling coefficient by looping over all spatial regions and energy groups From 3f0878dd367b556989ea2ab184bf2bbe519995f4 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 1 Nov 2018 02:54:21 -0400 Subject: [PATCH 37/58] Precompute array indices, clean up readability more, fix bug with coremap axis swapping --- openmc/cmfd.py | 2878 +++++++++++++++++++++++++----------------------- 1 file changed, 1495 insertions(+), 1383 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 9a82d0fd83..3e97f6564d 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -840,6 +840,7 @@ class CMFDRun(object): self._time_cmfdbuild = None self._time_cmfdsolve = None self._intracomm = None + # TODO Add all index related variables @property def cmfd_begin(self): @@ -1052,6 +1053,12 @@ class CMFDRun(object): # Configure CMFD parameters and tallies self._configure_cmfd() + # Set up CMFD coremap + self._set_coremap() + + # TEMP + self._precompute_array_indices() + # Initialize simulation openmc.capi.simulation_init() @@ -1286,10 +1293,6 @@ class CMFDRun(object): or with traditional for loops """ - # Set up CMFD coremap - if (self._mat_dim == _CMFD_NOACCEL): - self._set_coremap() - # Calculate all cross sections based on reaction rates from last batch self._compute_xs() @@ -1474,6 +1477,7 @@ class CMFDRun(object): phi_g = phi[:,g] cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] cmfd_flux2[idx + (g,)] = phi_g[self._coremap[idx]] + print("CMFD Flux") print(np.where(cmfd_flux != cmfd_flux2)) # Compute fission source @@ -1608,6 +1612,7 @@ class CMFDRun(object): energy_bins2[idx] = 0 idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) energy_bins2[idx] = ng - np.digitize(source_energies, energy) + print("Energy Bins Reweight") print(np.where(energy_bins != energy_bins2)) # Determine weight factor of each particle based on its mesh index @@ -1668,6 +1673,7 @@ class CMFDRun(object): energy_bins2[idx] = ng - 1 idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) energy_bins2[idx] = np.digitize(source_energies, energy) - 1 + print("Energy bins bank sites") print(np.where(energy_bins != energy_bins2)) # Determine all unique combinations of mesh bin and energy bin, and @@ -1760,6 +1766,1433 @@ class CMFDRun(object): return loss, prod + def _precompute_array_indices(self): + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + + # Logical for determining whether region of interest is accelerated region + is_accel = self._coremap != _CMFD_NOACCEL + # Logical for determining whether a zero flux "albedo" b.c. should be + # applied + is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) + + # Define slice equivalent to _accel[0,:,:] + slice_x = x_inds[:1,:,:] + slice_y = y_inds[:1,:,:] + slice_z = z_inds[:1,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[-1,:,:] + slice_x = x_inds[-1:,:,:] + slice_y = y_inds[-1:,:,:] + slice_z = z_inds[-1:,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,0,:] + slice_x = x_inds[:,:1,:] + slice_y = y_inds[:,:1,:] + slice_z = z_inds[:,:1,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,-1,:] + slice_x = x_inds[:,-1:,:] + slice_y = y_inds[:,-1:,:] + slice_z = z_inds[:,-1:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,0] + slice_x = x_inds[:,:,:1] + slice_y = y_inds[:,:,:1] + slice_z = z_inds[:,:,:1] + bndry_accel = is_accel[(x_inds, y_inds, 0)] + self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,-1] + slice_x = x_inds[:,:,-1:] + slice_y = y_inds[:,:,-1:] + slice_z = z_inds[:,:,-1:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the left is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[1:,:,:] & adj_reflector[1:,:,:] + slice_x = x_inds[1:,:,:] + slice_y = y_inds[1:,:,:] + slice_z = z_inds[1:,:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_x_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[1:,:,:] & ~adj_reflector[1:,:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_x_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the right is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[:-1,:,:] & adj_reflector[:-1,:,:] + slice_x = x_inds[:-1,:,:] + slice_y = y_inds[:-1,:,:] + slice_z = z_inds[:-1,:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_x_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:-1,:,:] & ~adj_reflector[:-1,:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_x_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the back is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[:,1:,:] & adj_reflector[:,1:,:] + slice_x = x_inds[:,1:,:] + slice_y = y_inds[:,1:,:] + slice_z = z_inds[:,1:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_y_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,1:,:] & ~adj_reflector[:,1:,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_y_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the front is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[:,:-1,:] & adj_reflector[:,:-1,:] + slice_x = x_inds[:,:-1,:] + slice_y = y_inds[:,:-1,:] + slice_z = z_inds[:,:-1,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_y_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:-1,:] & ~adj_reflector[:,:-1,:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_y_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the bottom is reflector region + adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[:,:,1:] & adj_reflector[:,:,1:] + slice_x = x_inds[:,:,1:] + slice_y = y_inds[:,:,1:] + slice_z = z_inds[:,:,1:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_z_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,1:] & ~adj_reflector[:,:,1:] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notfirst_z_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Logical for whether neighboring cell to the top is reflector region + adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + + # Define slice equivalent to is_accel[:,:,:-1] & adj_reflector[:,:,:-1] + slice_x = x_inds[:,:,:-1] + slice_y = y_inds[:,:,:-1] + slice_z = z_inds[:,:,:-1] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_z_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,:-1] & ~adj_reflector[:,:,:-1] + bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & + ~adj_reflector[(slice_x, slice_y, slice_z)]) + self._notlast_z_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # TEMP ALL ARRAY INDICES RELATED TO BUILDING MATRIX + # Shift coremap in all directions to determine whether leakage term + # should be defined for particular cell in matrix + coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] + + coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] + + coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] + + coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] + + coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] + + coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] + + # Create empty row and column vectors to store for loss matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + + self._is_accel = np.where(self._coremap != _CMFD_NOACCEL) + self._is_accel_neig_left = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_left != _CMFD_NOACCEL)) + self._is_accel_neig_right = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_right != _CMFD_NOACCEL)) + self._is_accel_neig_back = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_back != _CMFD_NOACCEL)) + self._is_accel_neig_front = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_front != _CMFD_NOACCEL)) + self._is_accel_neig_bottom = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_bottom != _CMFD_NOACCEL)) + self._is_accel_neig_top = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_top != _CMFD_NOACCEL)) + + for g in range(ng): + # Extract row and column data of regions where a cell and its + # neighbor to the left are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_left]) + g + idx_y = ng * (coremap_shift_left[self._is_accel_neig_left]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the right are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_right]) + g + idx_y = ng * (coremap_shift_right[self._is_accel_neig_right]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the back are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_back]) + g + idx_y = ng * (coremap_shift_back[self._is_accel_neig_back]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the front are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_front]) + g + idx_y = ng * (coremap_shift_front[self._is_accel_neig_front]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the bottom are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_bottom]) + g + idx_y = ng * (coremap_shift_bottom[self._is_accel_neig_bottom]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the top are both fuel regions + idx_x = ng * (self._coremap[self._is_accel_neig_top]) + g + idx_y = ng * (coremap_shift_top[self._is_accel_neig_top]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._is_accel]) + g + idx_y = idx_x + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + for h in range(ng): + if h != g: + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._is_accel]) + g + idx_y = ng * (self._coremap[self._is_accel]) + h + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Store row and col as rows and columns of production matrix + self._loss_row = row + self._loss_col = col + + # Create empty row and column vectors to store for production matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + + for g in range(ng): + for h in range(ng): + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._is_accel]) + g + idx_y = ng * (self._coremap[self._is_accel]) + h + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Store row and col as rows and columns of production matrix + self._prod_row = row + self._prod_col = col + + + def _build_loss_matrix_vectorized_updated(self, adjoint, org_loss): + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Define data entries used to build csr matrix + data = np.array([]) + + dtilde_left = self._dtilde[:,:,:,:,0] + dtilde_right = self._dtilde[:,:,:,:,1] + dtilde_back = self._dtilde[:,:,:,:,2] + dtilde_front = self._dtilde[:,:,:,:,3] + dtilde_bottom = self._dtilde[:,:,:,:,4] + dtilde_top = self._dtilde[:,:,:,:,5] + dhat_left = self._dhat[:,:,:,:,0] + dhat_right = self._dhat[:,:,:,:,1] + dhat_back = self._dhat[:,:,:,:,2] + dhat_front = self._dhat[:,:,:,:,3] + dhat_bottom = self._dhat[:,:,:,:,4] + dhat_top = self._dhat[:,:,:,:,5] + + dx = self._hxyz[:,:,:,np.newaxis,0] + dy = self._hxyz[:,:,:,np.newaxis,1] + dz = self._hxyz[:,:,:,np.newaxis,2] + + # Define net leakage coefficient for each surface in each matrix element + jnet = (((dtilde_right + dhat_right) - (-1.0 * dtilde_left + dhat_left)) / dx + + ((dtilde_front + dhat_front) - (-1.0 * dtilde_back + dhat_back)) / dy + + ((dtilde_top + dhat_top) - (-1.0 * dtilde_bottom + dhat_bottom)) / dz) + + for g in range(ng): + # Define leakage terms that relate terms to their neighbors to the + # left + dtilde = self._dtilde[:,:,:,g,0][self._is_accel_neig_left] + dhat = self._dhat[:,:,:,g,0][self._is_accel_neig_left] + dx = self._hxyz[:,:,:,0][self._is_accel_neig_left] + vals = (-1.0 * dtilde - dhat) / dx + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # right + dtilde = self._dtilde[:,:,:,g,1][self._is_accel_neig_right] + dhat = self._dhat[:,:,:,g,1][self._is_accel_neig_right] + dx = self._hxyz[:,:,:,0][self._is_accel_neig_right] + vals = (-1.0 * dtilde + dhat) / dx + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors in the + # back + dtilde = self._dtilde[:,:,:,g,2][self._is_accel_neig_back] + dhat = self._dhat[:,:,:,g,2][self._is_accel_neig_back] + dy = self._hxyz[:,:,:,1][self._is_accel_neig_back] + vals = (-1.0 * dtilde - dhat) / dy + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors in the + # front + dtilde = self._dtilde[:,:,:,g,3][self._is_accel_neig_front] + dhat = self._dhat[:,:,:,g,3][self._is_accel_neig_front] + dy = self._hxyz[:,:,:,1][self._is_accel_neig_front] + vals = (-1.0 * dtilde + dhat) / dy + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # bottom + dtilde = self._dtilde[:,:,:,g,4][self._is_accel_neig_bottom] + dhat = self._dhat[:,:,:,g,4][self._is_accel_neig_bottom] + dz = self._hxyz[:,:,:,2][self._is_accel_neig_bottom] + vals = (-1.0 * dtilde - dhat) / dz + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define leakage terms that relate terms to their neighbors to the + # top + dtilde = self._dtilde[:,:,:,g,5][self._is_accel_neig_top] + dhat = self._dhat[:,:,:,g,5][self._is_accel_neig_top] + dz = self._hxyz[:,:,:,2][self._is_accel_neig_top] + vals = (-1.0 * dtilde + dhat) / dz + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define terms that relate to loss of neutrons in a cell. These + # correspond to all the diagonal entries of the loss matrix + jnet_g = jnet[:,:,:,g][self._is_accel] + total_xs = self._totalxs[:,:,:,g][self._is_accel] + scatt_xs = self._scattxs[:,:,:,g,g][self._is_accel] + vals = jnet_g + total_xs - scatt_xs + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Define terms that relate to in-scattering from group to group. + # These terms relate a mesh index to all mesh indices with the same + # spatial dimensions but belong to a different energy group + for h in range(ng): + if h != g: + # Get scattering macro xs, transposed + if adjoint: + scatt_xs = self._scattxs[:,:,:,g,h][self._is_accel] + # Get scattering macro xs + else: + scatt_xs = self._scattxs[:,:,:,h,g][self._is_accel] + vals = -1.0 * scatt_xs + # Store data to add to CSR matrix + data = np.append(data, vals) + + # Create csr matrix + loss = sparse.csr_matrix((data, (self._loss_row, self._loss_col)), shape=(n, n)) + print("Loss Mat") + print(np.where(loss.toarray() != org_loss.toarray())) + return loss + + def _build_prod_matrix_vectorized_updated(self, adjoint, org_prod): + # Extract spatial and energy indices and define matrix dimension + ng = self._indices[3] + n = self._mat_dim*ng + + # Define rows, columns, and data used to build csr matrix + data = np.array([]) + + # Define terms that relate to fission production from group to group. + for g in range(ng): + for h in range(ng): + # Extract all regions where a cell is a fuel region + indices = np.where(self._coremap != _CMFD_NOACCEL) + idx_x = ng * (self._coremap[indices]) + g + idx_y = ng * (self._coremap[indices]) + h + # Get nu-fission macro xs, transposed + if adjoint: + vals = (self._nfissxs[:, :, :, g, h])[self._is_accel] + # Get nu-fission macro xs + else: + vals = (self._nfissxs[:, :, :, h, g])[self._is_accel] + # Store rows, cols, and data to add to CSR matrix + data = np.append(data, vals) + + # Create csr matrix + prod = sparse.csr_matrix((data, (self._prod_row, self._prod_col)), shape=(n, n)) + print("Prod Mat") + print(np.where(prod.toarray() != org_prod.toarray())) + return prod + + def _execute_power_iter(self, loss, prod): + """Main power iteration routine for the CMFD calculation + + Parameters + ---------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD loss matrix + prod : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + Returns + ------- + phi_n : numpy.ndarray + Flux vector of CMFD problem + k_n : float + Eigenvalue of CMFD problem + dom : float + Dominance ratio of CMFD problem + + """ + # Get problem size + n = loss.shape[0] + + # Set up flux vectors, intital guess set to 1 + phi_n = np.ones((n,)) + phi_o = np.ones((n,)) + + # Set up source vectors + s_n = np.zeros((n,)) + s_o = np.zeros((n,)) + serr_v = np.zeros((n,)) + + # Set initial guess + k_n = openmc.capi.keff_temp()[0] + k_o = k_n + dw = self._cmfd_shift + k_s = k_o + dw + k_ln = 1.0/(1.0/k_n - 1.0/k_s) + k_lo = k_ln + + # Set norms to 0 + norm_n = 0.0 + norm_o = 0.0 + + # Maximum number of power iterations + maxits = 10000 + + # Perform Wielandt shift + loss -= 1.0/k_s*prod + + # Begin power iteration + for i in range(maxits): + # Check if reach max number of iterations + if i == maxits - 1: + raise OpenMCError('Reached maximum iterations in CMFD power ' + 'iteration solver.') + + # Compute source vector + s_o = prod.dot(phi_o) + + # Normalize source vector + s_o /= k_lo + + # Compute new flux vector with scipy sparse solver + phi_n = sparse.linalg.spsolve(loss, s_o) + + # Compute new source vector + s_n = prod.dot(phi_n) + + # Compute new shifted eigenvalue + k_ln = np.sum(s_n) / np.sum(s_o) + + # Compute new eigenvalue + k_n = 1.0/(1.0/k_ln + 1.0/k_s) + + # Renormalize the old source + s_o *= k_lo + + # Check convergence + iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1) + + # If converged, calculate dominance ratio and break from loop + if iconv: + dom = norm_n / norm_o + return phi_n, k_n, dom + + # Record old values if not converged + phi_o = phi_n + k_o = k_n + k_lo = k_ln + norm_o = norm_n + + def _check_convergence(self, s_n, s_o, k_n, k_o, iter): + """Checks the convergence of the CMFD problem + + Parameters + ---------- + s_n : numpy.ndarray + Source vector from current iteration + s_o : numpy.ndarray + Source vector from previous iteration + k_n : float + K-effective from current iteration + k_o : float + K-effective from previous iteration + iter: int + Iteration number + + Returns + ------- + iconv : bool + Whether the power iteration has reached convergence + serr : float + Error in source from previous iteration to current iteration, used + for dominance ratio calculations + + """ + # Calculate error in keff + kerr = abs(k_o - k_n)/k_n + + # Calculate max error in source + serr = np.sqrt(np.sum(np.where(s_n>0, ((s_n-s_o)/s_n)**2,0))/len(s_n)) + + # Check for convergence + iconv = kerr < self._cmfd_ktol and serr < self._cmfd_stol + + # Print out to user + if self._cmfd_power_monitor and openmc.capi.master(): + str1 = ' {:d}:'.format(iter) + str2 = 'k-eff: {:0.8f}'.format(k_n) + str3 = 'k-error: {0:.5E}'.format(kerr) + str4 = 'src-error: {0:.5E}'.format(serr) + print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) + sys.stdout.flush() + + return iconv, serr + + def _set_coremap(self): + """Sets the core mapping information. All regions marked with zero + are set to CMFD_NO_ACCEL, while all regions marked with 1 are set to a + unique index that maps each fuel region to a row number when building + CMFD matrices + + """ + # Set number of accelerated regions in problem. This will be related to + # the dimension of CMFD matrices + self._mat_dim = np.sum(self._coremap) + + # Define coremap as cumulative sum over accelerated regions, + # otherwise set value to _CMFD_NOACCEL + self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, + np.cumsum(self._coremap)-1) + + # Reshape coremap to three dimensional array + # Indices of coremap in user input switched in x and z axes + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + self._coremap = self._coremap.reshape(nz, ny, nx) + self._coremap = np.swapaxes(self._coremap, 0, 2) + + def _compute_xs(self): + """Takes CMFD tallies from OpenMC and computes macroscopic cross + sections, flux, and diffusion coefficients for each mesh cell + + """ + # Extract energy indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + + # Set flux object and source distribution all to zeros + self._flux.fill(0.) + self._openmc_src.fill(0.) + + # Set mesh widths + self._hxyz[:,:,:,:] = openmc.capi.meshes[self._cmfd_mesh_id].width + + # Reset keff_bal to zero + self._keff_bal = 0. + + # Get tallies in-memory + tallies = openmc.capi.tallies + + # Ravel coremap as 1d array similar to how tally data is arranged + coremap = np.ravel(self._coremap.swapaxes(0, 2)) + + # Set conditional numpy array as boolean vector based on coremap + # Repeat each value for number of groups in problem + is_cmfd_accel = np.repeat(coremap != _CMFD_NOACCEL, ng) + + # Get flux from CMFD tally 0 + tally_id = self._cmfd_tally_ids[0] + tally_results = tallies[tally_id].results[:,0,1] + flux = np.where(is_cmfd_accel, tally_results, 0.) + + # Detect zero flux, abort if located + if np.any(flux[is_cmfd_accel] < _TINY_BIT): + # Get index of zero flux in flux array + idx = np.argmax(np.where(is_cmfd_accel, flux, 1) < _TINY_BIT) + + # Convert scalar idx to index in flux matrix + mat_idx = np.unravel_index(idx, self._flux.shape) + + # Throw error message (one-based indexing) + # Index of group is flipped + err_message = 'Detected zero flux without coremap overlay' + \ + ' at mesh: (' + \ + ', '.join(str(i+1) for i in mat_idx[:-1]) + \ + ') in group ' + str(ng-mat_idx[-1]) + raise OpenMCError(err_message) + + # Define target tally reshape dimensions. This defines how openmc + # tallies are ordered by dimension + target_tally_shape = [nz, ny, nx, ng] + + # Reshape flux array to target shape. Swap x and z axes so that + # flux shape is now [nx, ny, nz, ng] + reshape_flux = np.swapaxes(flux.reshape(target_tally_shape), 0, 2) + + # Flip energy axis as tally results are given in reverse order of + # energy group + self._flux = np.flip(reshape_flux, axis=3) + + # Get total rr and convert to total xs from CMFD tally 0 + tally_results = tallies[tally_id].results[:,1,1] + totalxs = np.divide(tally_results, flux, \ + where=flux>0, out=np.zeros_like(tally_results)) + + # Reshape totalxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng] + reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), 0, 2) + + # Total xs is flipped in energy axis as tally results are given in + # reverse order of energy group + self._totalxs = np.flip(reshape_totalxs, axis=3) + + # Get scattering xs from CMFD tally 1 + # flux is repeated to account for extra dimensionality of scattering xs + tally_id = self._cmfd_tally_ids[1] + tally_results = tallies[tally_id].results[:,0,1] + scattxs = np.divide(tally_results, \ + np.repeat(flux, ng), \ + where=np.repeat(flux>0, ng), \ + out=np.zeros_like(tally_results)) + + # Define target tally reshape dimensions for xs with incoming + # and outgoing energies + target_tally_shape = [nz, ny, nx, ng, ng] + + # Reshape scattxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng] + reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), 0, 2) + + # Scattering xs is flipped in both incoming and outgoing energy axes + # as tally results are given in reverse order of energy group + self._scattxs = np.flip(reshape_scattxs, axis=3) + self._scattxs = np.flip(self._scattxs, axis=4) + + # Get nu-fission xs from CMFD tally 1 + # flux is repeated to account for extra dimensionality of nu-fission xs + tally_results = tallies[tally_id].results[:,1,1] + num_realizations = tallies[tally_id].num_realizations + nfissxs = np.divide(tally_results, \ + np.repeat(flux, ng), \ + where=np.repeat(flux>0, ng), \ + out=np.zeros_like(tally_results)) + + # Reshape nfissxs array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng] + reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), 0, 2) + + # Nu-fission xs is flipped in both incoming and outgoing energy axes + # as tally results are given in reverse order of energy group + self._nfissxs = np.flip(reshape_nfissxs, axis=3) + self._nfissxs = np.flip(self._nfissxs, axis=4) + + # Openmc source distribution is sum of nu-fission rr in incoming energies + self._openmc_src = np.sum(self._nfissxs*self._flux[:,:,:,:,np.newaxis], + axis=3) + + # Compute k_eff from source distribution + self._keff_bal = np.sum(self._openmc_src) / num_realizations + + # Normalize openmc source distribution + self._openmc_src /= np.sum(self._openmc_src) * self._norm + + # Get surface currents from CMFD tally 2 + tally_id = self._cmfd_tally_ids[2] + tally_results = tallies[tally_id].results[:,0,1] + + # Filter tally results to include only accelerated regions + current = np.where(np.repeat(flux>0, 12), tally_results, 0.) + + # Define target tally reshape dimensions for current + target_tally_shape = [nz, ny, nx, 12, ng] + + # Reshape current array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, 12] + reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) + + # Current is flipped in energy axis as tally results are given in + # reverse order of energy group + self._current = np.flip(reshape_current, axis=4) + + # Get p1 scatter xs from CMFD tally 3 + tally_id = self._cmfd_tally_ids[3] + tally_results = tallies[tally_id].results[:,0,1] + + # Define target tally reshape dimensions for p1 scatter tally + target_tally_shape = [nz, ny, nx, 2, ng] + + # Reshape and extract only p1 data from tally results (no need for p0 data) + p1scattrr = np.swapaxes(tally_results.reshape(target_tally_shape), + 0, 2)[:,:,:,1,:] + + # Store p1 scatter xs + # p1 scatter xs is flipped in energy axis as tally results are given in + # reverse order of energy group + self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, \ + where=self._flux>0, \ + out=np.zeros_like(p1scattrr)) + + # Calculate and store diffusion coefficient + self._diffcof = np.where(self._flux>0, 1.0 / (3.0 * \ + (self._totalxs - self._p1scattxs)), 0.) + + def _compute_effective_downscatter(self): + """Changes downscatter rate for zero upscatter""" + # Extract energy index + ng = self._indices[3] + + # Return if not two groups + if ng != 2: + return + + # Extract cross sections and flux for each group + flux1 = self._flux[:,:,:,0] + flux2 = self._flux[:,:,:,1] + sigt1 = self._totalxs[:,:,:,0] + sigt2 = self._totalxs[:,:,:,1] + + # First energy index is incoming energy, second is outgoing energy + sigs11 = self._scattxs[:,:,:,0,0] + sigs21 = self._scattxs[:,:,:,1,0] + sigs12 = self._scattxs[:,:,:,0,1] + sigs22 = self._scattxs[:,:,:,1,1] + + # Compute absorption xs + siga1 = sigt1 - sigs11 - sigs12 + siga2 = sigt2 - sigs22 - sigs21 + + # Compute effective downscatter XS + sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, where=flux1>0, + out=np.zeros_like(flux2)) + + # Recompute total cross sections and record + self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff + self._totalxs[:,:,:,1] = siga2 + sigs22 + + # Record effective dowmscatter xs + self._scattxs[:,:,:,0,1] = sigs12_eff + + # Zero out upscatter cross section + self._scattxs[:,:,:,1,0] = 0.0 + + def _neutron_balance(self): + """Computes the RMS neutron balance over the CMFD mesh""" + # Extract energy indices + ng = self._indices[3] + + # Get number of accelerated regions + num_accel = np.sum(self._coremap != _CMFD_NOACCEL) + + # Get openmc k-effective + keff = openmc.capi.keff_temp()[0] + + # Define leakage in each mesh cell and energy group + leakage = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ + self._current[:,:,:,_CURRENTS['in_right'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_left'],:] - \ + self._current[:,:,:,_CURRENTS['out_left'],:])) + \ + ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ + self._current[:,:,:,_CURRENTS['in_front'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_back'],:] - \ + self._current[:,:,:,_CURRENTS['out_back'],:])) + \ + ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ + self._current[:,:,:,_CURRENTS['in_top'],:]) - \ + (self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ + self._current[:,:,:,_CURRENTS['out_bottom'],:])) + + # Compute total rr + interactions = self._totalxs * self._flux + + # Compute scattering rr by broadcasting flux in outgoing energy and + # summing over incoming energy + scattering = np.sum(self._scattxs * self._flux[:,:,:,:,np.newaxis], + axis=3) + + # Compute fission rr by broadcasting flux in outgoing energy and + # summing over incoming energy + fission = np.sum(self._nfissxs * self._flux[:,:,:,:,np.newaxis], + axis=3) + + # Compute residual + res = leakage + interactions - scattering - (1.0 / keff) * fission + + # Normalize res by flux and bank res + self._resnb = np.divide(res, self._flux, where=self._flux>0) + + # Calculate RMS and record for this batch + self._balance.append(np.sqrt( + np.sum(np.multiply(self._resnb, self._resnb)) / \ + (ng * num_accel))) + + def _compute_dtilde_vectorized_updated(self): + # Extract spatial and energy indices + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + self._dtilde2 = np.zeros((nx, ny, nz, ng, 6)) + + # Logical for determining whether a zero flux "albedo" b.c. should be + # applied + is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + + # Define dtilde at left surface for all mesh cells on left boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._first_x_accel + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + if is_zero_flux_alb[0]: + self._dtilde2[boundary_grps + (0,)] = 2.0 * D / dx + else: + alb = self._albedo[0] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + # Define dtilde at right surface for all mesh cells on right boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._last_x_accel + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + if is_zero_flux_alb[1]: + self._dtilde2[boundary_grps + (1,)] = 2.0 * D / dx + else: + alb = self._albedo[1] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + # Define dtilde at back surface for all mesh cells on back boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._first_y_accel + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + if is_zero_flux_alb[2]: + self._dtilde2[boundary_grps + (2,)] = 2.0 * D / dy + else: + alb = self._albedo[2] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + # Define dtilde at front surface for all mesh cells on front boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._last_y_accel + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + if is_zero_flux_alb[3]: + self._dtilde2[boundary_grps + (3,)] = 2.0 * D / dy + else: + alb = self._albedo[3] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + # Define dtilde at bottom surface for all mesh cells on bottom boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._first_z_accel + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + if is_zero_flux_alb[4]: + self._dtilde2[boundary_grps + (4,)] = 2.0 * D / dz + else: + alb = self._albedo[4] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + # Define dtilde at top surface for all mesh cells on top boundary + # Separate between zero flux b.c. and alebdo b.c. + boundary = self._last_z_accel + boundary_grps = boundary + (slice(None),) + + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + if is_zero_flux_alb[5]: + self._dtilde2[boundary_grps + (5,)] = 2.0 * D / dz + else: + alb = self._albedo[5] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + # Define reflector albedo for all cells on the left surface, in case + # a cell borders a reflector region on the left + current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] + current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] + ref_albedo = np.divide(current_in_left, current_out_left, + where=current_out_left > 1.0e-10, + out=np.ones_like(current_out_left)) + + # Diffusion coefficient of neighbor to left + neig_dc = np.roll(self._diffcof, 1, axis=0) + # Cell dimensions of neighbor to left + neig_hxyz = np.roll(self._hxyz, 1, axis=0) + + # Define dtilde at left surface for all mesh cells not on left boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_x_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + boundary = self._notfirst_x_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + neig_D = neig_dc[boundary_grps] + neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] + self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * neig_D) + / (neig_dx * D + dx * neig_D)) + + # Define reflector albedo for all cells on the right surface, in case + # a cell borders a reflector region on the right + current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] + current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] + ref_albedo = np.divide(current_in_right, current_out_right, + where=current_out_right > 1.0e-10, + out=np.ones_like(current_out_right)) + + # Diffusion coefficient of neighbor to right + neig_dc = np.roll(self._diffcof, -1, axis=0) + # Cell dimensions of neighbor to right + neig_hxyz = np.roll(self._hxyz, -1, axis=0) + + # Define dtilde at right surface for all mesh cells not on right boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_x_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + + boundary = self._notlast_x_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dx = self._hxyz[boundary + (np.newaxis, 0)] + neig_D = neig_dc[boundary_grps] + neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] + self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * neig_D) + / (neig_dx * D + dx * neig_D)) + + # Define reflector albedo for all cells on the back surface, in case + # a cell borders a reflector region on the back + current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] + current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] + ref_albedo = np.divide(current_in_back, current_out_back, + where=current_out_back > 1.0e-10, + out=np.ones_like(current_out_back)) + + # Diffusion coefficient of neighbor to back + neig_dc = np.roll(self._diffcof, 1, axis=1) + # Cell dimensions of neighbor to back + neig_hxyz = np.roll(self._hxyz, 1, axis=1) + + # Define dtilde at back surface for all mesh cells not on back boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_y_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + boundary = self._notfirst_y_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + neig_D = neig_dc[boundary_grps] + neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] + self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * neig_D) + / (neig_dy * D + dy * neig_D)) + + # Define reflector albedo for all cells on the front surface, in case + # a cell borders a reflector region in the front + current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] + current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] + ref_albedo = np.divide(current_in_front, current_out_front, + where=current_out_front > 1.0e-10, + out=np.ones_like(current_out_front)) + + # Diffusion coefficient of neighbor to front + neig_dc = np.roll(self._diffcof, -1, axis=1) + # Cell dimensions of neighbor to front + neig_hxyz = np.roll(self._hxyz, -1, axis=1) + + # Define dtilde at front surface for all mesh cells not on front boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_y_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + + boundary = self._notlast_y_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dy = self._hxyz[boundary + (np.newaxis, 1)] + neig_D = neig_dc[boundary_grps] + neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] + self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * neig_D) + / (neig_dy * D + dy * neig_D)) + + # Define reflector albedo for all cells on the bottom surface, in case + # a cell borders a reflector region on the bottom + current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] + current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] + ref_albedo = np.divide(current_in_bottom, current_out_bottom, + where=current_out_bottom > 1.0e-10, + out=np.ones_like(current_out_bottom)) + + # Diffusion coefficient of neighbor to bottom + neig_dc = np.roll(self._diffcof, 1, axis=2) + # Cell dimensions of neighbor to bottom + neig_hxyz = np.roll(self._hxyz, 1, axis=2) + + # Define dtilde at bottom surface for all mesh cells not on bottom boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_z_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + boundary = self._notfirst_z_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + neig_D = neig_dc[boundary_grps] + neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] + self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * neig_D) + / (neig_dz * D + dz * neig_D)) + + # Define reflector albedo for all cells on the top surface, in case + # a cell borders a reflector region on the top + current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] + current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] + ref_albedo = np.divide(current_in_top, current_out_top, + where=current_out_top > 1.0e-10, + out=np.ones_like(current_out_top)) + + # Diffusion coefficient of neighbor to top + neig_dc = np.roll(self._diffcof, -1, axis=2) + # Cell dimensions of neighbor to top + neig_hxyz = np.roll(self._hxyz, -1, axis=2) + + # Define dtilde at top surface for all mesh cells not on top boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_z_accel_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + alb = ref_albedo[boundary_grps] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1.0 - alb)) + / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + + boundary = self._notlast_z_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + D = self._diffcof[boundary_grps] + dz = self._hxyz[boundary + (np.newaxis, 2)] + neig_D = neig_dc[boundary_grps] + neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] + self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * neig_D) + / (neig_dz * D + dz * neig_D)) + + def _compute_dhat_vectorized_updated(self): + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + self._dhat2 = np.zeros((nx, ny, nz, ng, 6)) + + # Define current in each direction + current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] + current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] + current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] + current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] + current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] + current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] + current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] + current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] + current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] + current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] + current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] + current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] + + dx = self._hxyz[:,:,:,np.newaxis,0] + dy = self._hxyz[:,:,:,np.newaxis,1] + dz = self._hxyz[:,:,:,np.newaxis,2] + dxdydz = np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] + + # Define net current on each face + net_current_left = (current_in_left - current_out_left) / dxdydz * dx + net_current_right = (current_out_right - current_in_right) / dxdydz * dx + net_current_back = (current_in_back - current_out_back) / dxdydz * dy + net_current_front = (current_out_front - current_in_front) / dxdydz * dy + net_current_bottom = (current_in_bottom - current_out_bottom) / dxdydz * dz + net_current_top = (current_out_top - current_in_top) / dxdydz * dz + + # Define flux in each cell + cell_flux = self._flux / dxdydz + # Extract indices of coremap that are accelerated + is_accel = self._coremap != _CMFD_NOACCEL + + # Define dhat at left surface for all mesh cells on left boundary + boundary = self._first_x_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux + + # Define dhat at right surface for all mesh cells on right boundary + boundary = self._last_x_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux + + # Define dhat at back surface for all mesh cells on back boundary + boundary = self._first_y_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux + + # Define dhat at front surface for all mesh cells on front boundary + boundary = self._last_y_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux + + # Define dhat at bottom surface for all mesh cells on bottom boundary + boundary = self._first_z_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux + + # Define dhat at top surface for all mesh cells on top boundary + boundary = self._last_z_accel + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux + + # Cell flux of neighbor to left + neig_flux = np.roll(self._flux, 1, axis=0) / dxdydz + + # Define dhat at left surface for all mesh cells not on left boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_x_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux + + boundary = self._notfirst_x_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_left[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 0)] + flux = cell_flux[boundary_grps] + flux_left = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (0,)] = ((net_current - dtilde * (flux_left - flux)) + / (flux_left + flux)) + + # Cell flux of neighbor to right + neig_flux = np.roll(self._flux, -1, axis=0) / dxdydz + + # Define dhat at right surface for all mesh cells not on right boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_x_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux + + boundary = self._notlast_x_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_right[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 1)] + flux = cell_flux[boundary_grps] + flux_right = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (1,)] = ((net_current + dtilde * (flux_right - flux)) + / (flux_right + flux)) + + # Cell flux of neighbor to back + neig_flux = np.roll(self._flux, 1, axis=1) / dxdydz + + # Define dhat at back surface for all mesh cells not on back boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_y_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux + + boundary = self._notfirst_y_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_back[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 2)] + flux = cell_flux[boundary_grps] + flux_back = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (2,)] = ((net_current - dtilde * (flux_back - flux)) + / (flux_back + flux)) + + # Cell flux of neighbor to front + neig_flux = np.roll(self._flux, -1, axis=1) / dxdydz + + # Define dhat at front surface for all mesh cells not on front boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_y_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux + + boundary = self._notlast_y_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_front[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 3)] + flux = cell_flux[boundary_grps] + flux_front = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (3,)] = ((net_current + dtilde * (flux_front - flux)) + / (flux_front + flux)) + + # Cell flux of neighbor to bottom + neig_flux = np.roll(self._flux, 1, axis=2) / dxdydz + + # Define dhat at bottom surface for all mesh cells not on bottom boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notfirst_z_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux + + boundary = self._notfirst_z_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_bottom[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 4)] + flux = cell_flux[boundary_grps] + flux_bottom = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (4,)] = ((net_current - dtilde * (flux_bottom - flux)) + / (flux_bottom + flux)) + + # Cell flux of neighbor to top + neig_flux = np.roll(self._flux, -1, axis=2) / dxdydz + + # Define dhat at top surface for all mesh cells not on top boundary + # Separate between cases where regions do and don't neighbor reflector regions + boundary = self._notlast_z_accel_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux + + boundary = self._notlast_z_accel_not_adj_ref + boundary_grps = boundary + (slice(None),) + net_current = net_current_top[boundary_grps] + dtilde = self._dtilde[boundary + (slice(None), 5)] + flux = cell_flux[boundary_grps] + flux_top = neig_flux[boundary_grps] + self._dhat2[boundary_grps + (5,)] = ((net_current + dtilde * (flux_top - flux)) + / (flux_top + flux)) + print("Dhat") + print(np.where(self._dhat2 != self._dhat)) + + def _create_cmfd_tally(self): + """Creates all tallies in-memory that are used to solve CMFD problem""" + # Create Mesh object based on CMFDMesh, stored internally + cmfd_mesh = openmc.capi.Mesh() + # Store id of Mesh object + self._cmfd_mesh_id = cmfd_mesh.id + # Set dimension and parameters of Mesh object + cmfd_mesh.dimension = self._cmfd_mesh.dimension + cmfd_mesh.set_parameters(lower_left=self._cmfd_mesh.lower_left, + upper_right=self._cmfd_mesh.upper_right, + width=self._cmfd_mesh.width) + + # Create Mesh Filter object, stored internally + mesh_filter = openmc.capi.MeshFilter() + # Set mesh for Mesh Filter + mesh_filter.mesh = cmfd_mesh + + # Set up energy filters, if applicable + if self._energy_filters: + # Create Energy Filter object, stored internally + energy_filter = openmc.capi.EnergyFilter() + # Set bins for Energy Filter + energy_filter.bins = self._egrid + + # Create Energy Out Filter object, stored internally + energyout_filter = openmc.capi.EnergyoutFilter() + # Set bins for Energy Filter + energyout_filter.bins = self._egrid + + # Create Mesh Surface Filter object, stored internally + meshsurface_filter = openmc.capi.MeshSurfaceFilter() + # Set mesh for Mesh Surface Filter + meshsurface_filter.mesh = cmfd_mesh + + # Create Legendre Filter object, stored internally + legendre_filter = openmc.capi.LegendreFilter() + # Set order for Legendre Filter + legendre_filter.order = 1 + + # Create CMFD tallies, stored internally + n_tallies = 4 + self._cmfd_tally_ids = [] + for i in range(n_tallies): + tally = openmc.capi.Tally() + # Set nuclide bins + tally.nuclides = ['total'] + self._cmfd_tally_ids.append(tally.id) + + # Set attributes of CMFD flux, total tally + if i == 0: + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, energy_filter] + else: + tally.filters = [mesh_filter] + # Set scores, type, and estimator for tally + tally.scores = ['flux', 'total'] + tally.type = 'volume' + tally.estimator = 'analog' + + # Set attributes of CMFD neutron production tally + elif i == 1: + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, energy_filter, energyout_filter] + else: + tally.filters = [mesh_filter] + # Set scores, type, and estimator for tally + tally.scores = ['nu-scatter', 'nu-fission'] + tally.type = 'volume' + tally.estimator = 'analog' + + # Set attributes of CMFD surface current tally + elif i == 2: + # Set filters for tally + if self._energy_filters: + tally.filters = [meshsurface_filter, energy_filter] + else: + tally.filters = [meshsurface_filter] + # Set scores, type, and estimator for tally + tally.scores = ['current'] + tally.type = 'mesh-surface' + tally.estimator = 'analog' + + # Set attributes of CMFD P1 scatter tally + elif i == 3: + # Set filters for tally + if self._energy_filters: + tally.filters = [mesh_filter, legendre_filter, energy_filter] + else: + tally.filters = [mesh_filter, legendre_filter] + # Set scores for tally + tally.scores = ['scatter'] + tally.type = 'volume' + tally.estimator = 'analog' + + # Set all tallies to be active from beginning + tally.active = True + def _build_loss_matrix(self, adjoint): """Creates matrix representing loss of neutrons. This method uses for loops to loop over all spatial indices and energy groups for easier @@ -1871,6 +3304,64 @@ class CMFDRun(object): loss = sparse.csr_matrix(loss) return loss + def _build_prod_matrix(self, adjoint): + """Creates matrix representing production of neutrons. This method uses for + loops to loop over all spatial indices and energy groups for easier + readability. Matrix is returned in CSR format by populating all entries + with a numpy array and later converting to CSR format + + Parameters + ---------- + adjoint : bool + Whether or not to run an adjoint calculation + + Returns + ------- + loss : scipy.sparse.spmatrix + Sparse matrix storing elements of CMFD production matrix + + """ + # Extract spatial and energy indices and define matrix dimension + nx = self._indices[0] + ny = self._indices[1] + nz = self._indices[2] + ng = self._indices[3] + n = self._mat_dim*ng + + # Allocate matrix + prod = np.zeros((n, n)) + + for irow in range(n): + # Get indices for row in matrix + i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) + + # Check if at a reflector + if self._coremap[i,j,k] == _CMFD_NOACCEL: + continue + + # Loop around all other groups + for h in range(ng): + # Get matrix column location + hmat_idx = self._indices_to_matrix(i,j,k, h, ng) + # Check for adjoint and bank val + if adjoint: + # Get nu-fission cross section from cell, transposed! + nfissxs = self._nfissxs[i, j, k, g, h] + else: + # Get nu-fission cross section from cell + nfissxs = self._nfissxs[i, j, k, h, g] + + # Set as value to be recorded + val = nfissxs + + # record value in matrix + prod[irow, hmat_idx] = val + + # Convert matrix to csr matrix in order to use scipy sparse solver + prod = sparse.csr_matrix(prod) + + return prod + def _build_loss_matrix_vectorized(self, adjoint): """Creates matrix representing loss of neutrons. Since the end shape of the loss matrix is known a priori, this method uses a @@ -2076,274 +3567,6 @@ class CMFDRun(object): loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return loss - def _build_loss_matrix_vectorized_updated(self, adjoint, org_loss): - # Extract spatial and energy indices and define matrix dimension - ng = self._indices[3] - n = self._mat_dim*ng - - # Define rows, columns, and data used to build csr matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - data = np.array([]) - - dtilde_left = self._dtilde[:,:,:,:,0] - dtilde_right = self._dtilde[:,:,:,:,1] - dtilde_back = self._dtilde[:,:,:,:,2] - dtilde_front = self._dtilde[:,:,:,:,3] - dtilde_bottom = self._dtilde[:,:,:,:,4] - dtilde_top = self._dtilde[:,:,:,:,5] - dhat_left = self._dhat[:,:,:,:,0] - dhat_right = self._dhat[:,:,:,:,1] - dhat_back = self._dhat[:,:,:,:,2] - dhat_front = self._dhat[:,:,:,:,3] - dhat_bottom = self._dhat[:,:,:,:,4] - dhat_top = self._dhat[:,:,:,:,5] - - dx = self._hxyz[:,:,:,np.newaxis,0] - dy = self._hxyz[:,:,:,np.newaxis,1] - dz = self._hxyz[:,:,:,np.newaxis,2] - - # Define net leakage coefficient for each surface in each matrix element - jnet = (((dtilde_right + dhat_right) - (-1.0 * dtilde_left + dhat_left)) / dx - + ((dtilde_front + dhat_front) - (-1.0 * dtilde_back + dhat_back)) / dy - + ((dtilde_top + dhat_top) - (-1.0 * dtilde_bottom + dhat_bottom)) / dz) - - # Shift coremap in all directions to determine whether leakage term - # should be defined for particular cell in matrix - coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] - - coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] - - coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] - - coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] - - coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] - - coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] - - for g in range(ng): - # Define leakage terms that relate terms to their neighbors to the - # left - - # Extract all indices where a cell and its neighbor to the left - # are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_left != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_left[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,0][indices] - dhat = self._dhat[:,:,:,g,0][indices] - dx = self._hxyz[:,:,:,0][indices] - vals = (-1.0 * dtilde - dhat) / dx - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # right - - # Extract all regions where a cell and its neighbor to the right - # are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_right != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_right[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,1][indices] - dhat = self._dhat[:,:,:,g,1][indices] - dx = self._hxyz[:,:,:,0][indices] - vals = (-1.0 * dtilde + dhat) / dx - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors in the - # back - - # Extract all regions where a cell and its neighbor in the back - # are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_back != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_back[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,2][indices] - dhat = self._dhat[:,:,:,g,2][indices] - dy = self._hxyz[:,:,:,1][indices] - vals = (-1.0 * dtilde - dhat) / dy - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors in the - # front - - # Extract all regions where a cell and its neighbor in the front - # are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_front != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_front[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,3][indices] - dhat = self._dhat[:,:,:,g,3][indices] - dy = self._hxyz[:,:,:,1][indices] - vals = (-1.0 * dtilde + dhat) / dy - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # bottom - - # Extract all regions where a cell and its neighbor to the bottom - # are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_bottom != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_bottom[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,4][indices] - dhat = self._dhat[:,:,:,g,4][indices] - dz = self._hxyz[:,:,:,2][indices] - vals = (-1.0 * dtilde - dhat) / dz - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # top - - # Extract all regions where a cell and its neighbor to the - # top are both fuel regions - indices = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_top != _CMFD_NOACCEL)) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (coremap_shift_top[indices]) + g - # Compute leakage term associated with these regions - dtilde = self._dtilde[:,:,:,g,5][indices] - dhat = self._dhat[:,:,:,g,5][indices] - dz = self._hxyz[:,:,:,2][indices] - vals = (-1.0 * dtilde + dhat) / dz - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define terms that relate to loss of neutrons in a cell. These - # correspond to all the diagonal entries of the loss matrix - - # Extract all regions where a cell is a fuel region - indices = np.where(self._coremap != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[indices]) + g - idx_y = idx_x - jnet_g = jnet[:,:,:,g][indices] - total_xs = self._totalxs[:,:,:,g][indices] - scatt_xs = self._scattxs[:,:,:,g,g][indices] - vals = jnet_g + total_xs - scatt_xs - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define terms that relate to in-scattering from group to group. - # These terms relate a mesh index to all mesh indices with the same - # spatial dimensions but belong to a different energy group - for h in range(ng): - if h != g: - # Extract all regions where a cell is a fuel region - indices = np.where(self._coremap != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (self._coremap[indices]) + h - # Get scattering macro xs, transposed - if adjoint: - scatt_xs = self._scattxs[:,:,:,g,h][indices] - # Get scattering macro xs - else: - scatt_xs = self._scattxs[:,:,:,h,g][indices] - vals = -1.0 * scatt_xs - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Create csr matrix - loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) - print(np.where(loss.toarray() != org_loss.toarray())) - return loss - - - def _build_prod_matrix(self, adjoint): - """Creates matrix representing production of neutrons. This method uses for - loops to loop over all spatial indices and energy groups for easier - readability. Matrix is returned in CSR format by populating all entries - with a numpy array and later converting to CSR format - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - """ - # Extract spatial and energy indices and define matrix dimension - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - n = self._mat_dim*ng - - # Allocate matrix - prod = np.zeros((n, n)) - - for irow in range(n): - # Get indices for row in matrix - i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) - - # Check if at a reflector - if self._coremap[i,j,k] == _CMFD_NOACCEL: - continue - - # Loop around all other groups - for h in range(ng): - # Get matrix column location - hmat_idx = self._indices_to_matrix(i,j,k, h, ng) - # Check for adjoint and bank val - if adjoint: - # Get nu-fission cross section from cell, transposed! - nfissxs = self._nfissxs[i, j, k, g, h] - else: - # Get nu-fission cross section from cell - nfissxs = self._nfissxs[i, j, k, h, g] - - # Set as value to be recorded - val = nfissxs - - # record value in matrix - prod[irow, hmat_idx] = val - - # Convert matrix to csr matrix in order to use scipy sparse solver - prod = sparse.csr_matrix(prod) - - return prod - def _build_prod_matrix_vectorized(self, adjoint): """Creates matrix representing production of neutrons. Since the end shape of the production matrix is known a priori, this method uses a @@ -2392,39 +3615,6 @@ class CMFDRun(object): prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) return prod - def _build_prod_matrix_vectorized_updated(self, adjoint, org_prod): - # Extract spatial and energy indices and define matrix dimension - ng = self._indices[3] - n = self._mat_dim*ng - - # Define rows, columns, and data used to build csr matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - data = np.array([]) - - # Define terms that relate to fission production from group to group. - for g in range(ng): - for h in range(ng): - # Extract all regions where a cell is a fuel region - indices = np.where(self._coremap != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (self._coremap[indices]) + h - # Get nu-fission macro xs, transposed - if adjoint: - vals = (self._nfissxs[:, :, :, g, h])[indices] - # Get nu-fission macro xs - else: - vals = (self._nfissxs[:, :, :, h, g])[indices] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Create csr matrix - prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) - print(np.where(prod.toarray() != org_prod.toarray())) - return prod - def _matrix_to_indices(self, irow, nx, ny, nz, ng): """Converts matrix index in CMFD matrices to spatial and group indices of actual problem, based on values from coremap @@ -2489,416 +3679,6 @@ class CMFDRun(object): matidx = ng*(self._coremap[i,j,k]) + g return matidx - def _execute_power_iter(self, loss, prod): - """Main power iteration routine for the CMFD calculation - - Parameters - ---------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD loss matrix - prod : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - Returns - ------- - phi_n : numpy.ndarray - Flux vector of CMFD problem - k_n : float - Eigenvalue of CMFD problem - dom : float - Dominance ratio of CMFD problem - - """ - # Get problem size - n = loss.shape[0] - - # Set up flux vectors, intital guess set to 1 - phi_n = np.ones((n,)) - phi_o = np.ones((n,)) - - # Set up source vectors - s_n = np.zeros((n,)) - s_o = np.zeros((n,)) - serr_v = np.zeros((n,)) - - # Set initial guess - k_n = openmc.capi.keff_temp()[0] - k_o = k_n - dw = self._cmfd_shift - k_s = k_o + dw - k_ln = 1.0/(1.0/k_n - 1.0/k_s) - k_lo = k_ln - - # Set norms to 0 - norm_n = 0.0 - norm_o = 0.0 - - # Maximum number of power iterations - maxits = 10000 - - # Perform Wielandt shift - loss -= 1.0/k_s*prod - - # Begin power iteration - for i in range(maxits): - # Check if reach max number of iterations - if i == maxits - 1: - raise OpenMCError('Reached maximum iterations in CMFD power ' - 'iteration solver.') - - # Compute source vector - s_o = prod.dot(phi_o) - - # Normalize source vector - s_o /= k_lo - - # Compute new flux vector with scipy sparse solver - phi_n = sparse.linalg.spsolve(loss, s_o) - - # Compute new source vector - s_n = prod.dot(phi_n) - - # Compute new shifted eigenvalue - k_ln = np.sum(s_n) / np.sum(s_o) - - # Compute new eigenvalue - k_n = 1.0/(1.0/k_ln + 1.0/k_s) - - # Renormalize the old source - s_o *= k_lo - - # Check convergence - iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1) - - # If converged, calculate dominance ratio and break from loop - if iconv: - dom = norm_n / norm_o - return phi_n, k_n, dom - - # Record old values if not converged - phi_o = phi_n - k_o = k_n - k_lo = k_ln - norm_o = norm_n - - def _check_convergence(self, s_n, s_o, k_n, k_o, iter): - """Checks the convergence of the CMFD problem - - Parameters - ---------- - s_n : numpy.ndarray - Source vector from current iteration - s_o : numpy.ndarray - Source vector from previous iteration - k_n : float - K-effective from current iteration - k_o : float - K-effective from previous iteration - iter: int - Iteration number - - Returns - ------- - iconv : bool - Whether the power iteration has reached convergence - serr : float - Error in source from previous iteration to current iteration, used - for dominance ratio calculations - - """ - # Calculate error in keff - kerr = abs(k_o - k_n)/k_n - - # Calculate max error in source - serr = np.sqrt(np.sum(np.where(s_n>0, ((s_n-s_o)/s_n)**2,0))/len(s_n)) - - # Check for convergence - iconv = kerr < self._cmfd_ktol and serr < self._cmfd_stol - - # Print out to user - if self._cmfd_power_monitor and openmc.capi.master(): - str1 = ' {:d}:'.format(iter) - str2 = 'k-eff: {:0.8f}'.format(k_n) - str3 = 'k-error: {0:.5E}'.format(kerr) - str4 = 'src-error: {0:.5E}'.format(serr) - print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) - sys.stdout.flush() - - return iconv, serr - - def _set_coremap(self): - """Sets the core mapping information. All regions marked with zero - are set to CMFD_NO_ACCEL, while all regions marked with 1 are set to a - unique index that maps each fuel region to a row number when building - CMFD matrices - - """ - # Set number of accelerated regions in problem. This will be related to - # the dimension of CMFD matrices - self._mat_dim = np.sum(self._coremap) - - # Define coremap as cumulative sum over accelerated regions, - # otherwise set value to _CMFD_NOACCEL - self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, - np.cumsum(self._coremap)-1) - - def _compute_xs(self): - """Takes CMFD tallies from OpenMC and computes macroscopic cross - sections, flux, and diffusion coefficients for each mesh cell - - """ - # Extract energy indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Set flux object and source distribution all to zeros - self._flux.fill(0.) - self._openmc_src.fill(0.) - - # Set mesh widths - self._hxyz[:,:,:,:] = openmc.capi.meshes[self._cmfd_mesh_id].width - - # Reset keff_bal to zero - self._keff_bal = 0. - - # Get tallies in-memory - tallies = openmc.capi.tallies - - # Set conditional numpy array as boolean vector based on coremap - # Repeat each value for number of groups in problem - is_cmfd_accel = np.repeat(self._coremap != _CMFD_NOACCEL, ng) - - # Get flux from CMFD tally 0 - tally_id = self._cmfd_tally_ids[0] - tally_results = tallies[tally_id].results[:,0,1] - flux = np.where(is_cmfd_accel, tally_results, 0.) - - # Detect zero flux, abort if located - if np.any(flux[is_cmfd_accel] < _TINY_BIT): - # Get index of zero flux in flux array - idx = np.argmax(np.where(is_cmfd_accel, flux, 1) < _TINY_BIT) - - # Convert scalar idx to index in flux matrix - mat_idx = np.unravel_index(idx, self._flux.shape) - - # Throw error message (one-based indexing) - # Index of group is flipped - err_message = 'Detected zero flux without coremap overlay' + \ - ' at mesh: (' + \ - ', '.join(str(i+1) for i in mat_idx[:-1]) + \ - ') in group ' + str(ng-mat_idx[-1]) - raise OpenMCError(err_message) - - # Define target tally reshape dimensions. This defines how openmc - # tallies are ordered by dimension - target_tally_shape = [nz, ny, nx, ng] - - # Reshape flux array to target shape. Swap x and z axes so that - # flux shape is now [nx, ny, nz, ng] - reshape_flux = np.swapaxes(flux.reshape(target_tally_shape), 0, 2) - - # Flip energy axis as tally results are given in reverse order of - # energy group - self._flux = np.flip(reshape_flux, axis=3) - - # Get total rr and convert to total xs from CMFD tally 0 - tally_results = tallies[tally_id].results[:,1,1] - totalxs = np.divide(tally_results, flux, \ - where=flux>0, out=np.zeros_like(tally_results)) - - # Reshape totalxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng] - reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), 0, 2) - - # Total xs is flipped in energy axis as tally results are given in - # reverse order of energy group - self._totalxs = np.flip(reshape_totalxs, axis=3) - - # Get scattering xs from CMFD tally 1 - # flux is repeated to account for extra dimensionality of scattering xs - tally_id = self._cmfd_tally_ids[1] - tally_results = tallies[tally_id].results[:,0,1] - scattxs = np.divide(tally_results, \ - np.repeat(flux, ng), \ - where=np.repeat(flux>0, ng), \ - out=np.zeros_like(tally_results)) - - # Define target tally reshape dimensions for xs with incoming - # and outgoing energies - target_tally_shape = [nz, ny, nx, ng, ng] - - # Reshape scattxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, ng] - reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), 0, 2) - - # Scattering xs is flipped in both incoming and outgoing energy axes - # as tally results are given in reverse order of energy group - self._scattxs = np.flip(reshape_scattxs, axis=3) - self._scattxs = np.flip(self._scattxs, axis=4) - - # Get nu-fission xs from CMFD tally 1 - # flux is repeated to account for extra dimensionality of nu-fission xs - tally_results = tallies[tally_id].results[:,1,1] - num_realizations = tallies[tally_id].num_realizations - nfissxs = np.divide(tally_results, \ - np.repeat(flux, ng), \ - where=np.repeat(flux>0, ng), \ - out=np.zeros_like(tally_results)) - - # Reshape nfissxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, ng] - reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), 0, 2) - - # Nu-fission xs is flipped in both incoming and outgoing energy axes - # as tally results are given in reverse order of energy group - self._nfissxs = np.flip(reshape_nfissxs, axis=3) - self._nfissxs = np.flip(self._nfissxs, axis=4) - - # Openmc source distribution is sum of nu-fission rr in incoming energies - self._openmc_src = np.sum(self._nfissxs*self._flux[:,:,:,:,np.newaxis], - axis=3) - - # Compute k_eff from source distribution - self._keff_bal = np.sum(self._openmc_src) / num_realizations - - # Normalize openmc source distribution - self._openmc_src /= np.sum(self._openmc_src) * self._norm - - # Get surface currents from CMFD tally 2 - tally_id = self._cmfd_tally_ids[2] - tally_results = tallies[tally_id].results[:,0,1] - - # Filter tally results to include only accelerated regions - current = np.where(np.repeat(flux>0, 12), tally_results, 0.) - - # Define target tally reshape dimensions for current - target_tally_shape = [nz, ny, nx, 12, ng] - - # Reshape current array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, 12] - reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) - - # Current is flipped in energy axis as tally results are given in - # reverse order of energy group - self._current = np.flip(reshape_current, axis=4) - - # Get p1 scatter xs from CMFD tally 3 - tally_id = self._cmfd_tally_ids[3] - tally_results = tallies[tally_id].results[:,0,1] - - # Define target tally reshape dimensions for p1 scatter tally - target_tally_shape = [nz, ny, nx, 2, ng] - - # Reshape and extract only p1 data from tally results (no need for p0 data) - p1scattrr = np.swapaxes(tally_results.reshape(target_tally_shape), - 0, 2)[:,:,:,1,:] - - # Store p1 scatter xs - # p1 scatter xs is flipped in energy axis as tally results are given in - # reverse order of energy group - self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, \ - where=self._flux>0, \ - out=np.zeros_like(p1scattrr)) - - # Calculate and store diffusion coefficient - self._diffcof = np.where(self._flux>0, 1.0 / (3.0 * \ - (self._totalxs - self._p1scattxs)), 0.) - - # Reshape coremap to three dimensional array as all cross section data - # has been reshaped - self._coremap = self._coremap.reshape(nz,ny,nx) - self._coremap = np.swapaxes(self._coremap, 0, 2) - - def _compute_effective_downscatter(self): - """Changes downscatter rate for zero upscatter""" - # Extract energy index - ng = self._indices[3] - - # Return if not two groups - if ng != 2: - return - - # Extract cross sections and flux for each group - flux1 = self._flux[:,:,:,0] - flux2 = self._flux[:,:,:,1] - sigt1 = self._totalxs[:,:,:,0] - sigt2 = self._totalxs[:,:,:,1] - - # First energy index is incoming energy, second is outgoing energy - sigs11 = self._scattxs[:,:,:,0,0] - sigs21 = self._scattxs[:,:,:,1,0] - sigs12 = self._scattxs[:,:,:,0,1] - sigs22 = self._scattxs[:,:,:,1,1] - - # Compute absorption xs - siga1 = sigt1 - sigs11 - sigs12 - siga2 = sigt2 - sigs22 - sigs21 - - # Compute effective downscatter XS - sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, where=flux1>0, - out=np.zeros_like(flux2)) - - # Recompute total cross sections and record - self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff - self._totalxs[:,:,:,1] = siga2 + sigs22 - - # Record effective dowmscatter xs - self._scattxs[:,:,:,0,1] = sigs12_eff - - # Zero out upscatter cross section - self._scattxs[:,:,:,1,0] = 0.0 - - def _neutron_balance(self): - """Computes the RMS neutron balance over the CMFD mesh""" - # Extract energy indices - ng = self._indices[3] - - # Get number of accelerated regions - num_accel = np.sum(self._coremap != _CMFD_NOACCEL) - - # Get openmc k-effective - keff = openmc.capi.keff_temp()[0] - - # Define leakage in each mesh cell and energy group - leakage = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ - self._current[:,:,:,_CURRENTS['in_right'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_left'],:] - \ - self._current[:,:,:,_CURRENTS['out_left'],:])) + \ - ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ - self._current[:,:,:,_CURRENTS['in_front'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_back'],:] - \ - self._current[:,:,:,_CURRENTS['out_back'],:])) + \ - ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ - self._current[:,:,:,_CURRENTS['in_top'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ - self._current[:,:,:,_CURRENTS['out_bottom'],:])) - - # Compute total rr - interactions = self._totalxs * self._flux - - # Compute scattering rr by broadcasting flux in outgoing energy and - # summing over incoming energy - scattering = np.sum(self._scattxs * self._flux[:,:,:,:,np.newaxis], - axis=3) - - # Compute fission rr by broadcasting flux in outgoing energy and - # summing over incoming energy - fission = np.sum(self._nfissxs * self._flux[:,:,:,:,np.newaxis], - axis=3) - - # Compute residual - res = leakage + interactions - scattering - (1.0 / keff) * fission - - # Normalize res by flux and bank res - self._resnb = np.divide(res, self._flux, where=self._flux>0) - - # Calculate RMS and record for this batch - self._balance.append(np.sqrt( - np.sum(np.multiply(self._resnb, self._resnb)) / \ - (ng * num_accel))) - def _compute_dtilde_vectorized(self): """Computes the diffusion coupling coefficient using a vectorized numpy approach. Aggregate values for the dtilde multidimensional array are @@ -3136,328 +3916,6 @@ class CMFDRun(object): (neig_hxyz[:,:,:-1,np.newaxis,2] * self._diffcof[:,:,:-1,:] + \ self._hxyz[:,:,:-1,np.newaxis,2] * neig_dc[:,:,:-1,:])), 0.0) - ################################################################################# - def _compute_dtilde_vectorized_updated(self): - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - self._dtilde2 = np.zeros((nx, ny, nz, ng, 6)) - - # Logical for determining whether region of interest is accelerated region - is_accel = self._coremap != _CMFD_NOACCEL - # Logical for determining whether a zero flux "albedo" b.c. should be - # applied - is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT - - # Define dtilde at left surface for all mesh cells on left boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[0,:,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - if is_zero_flux_alb[0]: - self._dtilde2[boundary_grps + (0,)] = 2.0 * D / dx - else: - alb = self._albedo[0] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - # Define dtilde at right surface for all mesh cells on right boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[-1,:,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - if is_zero_flux_alb[1]: - self._dtilde2[boundary_grps + (1,)] = 2.0 * D / dx - else: - alb = self._albedo[1] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - # Define dtilde at back surface for all mesh cells on back boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,0,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - if is_zero_flux_alb[2]: - self._dtilde2[boundary_grps + (2,)] = 2.0 * D / dy - else: - alb = self._albedo[2] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - # Define dtilde at front surface for all mesh cells on front boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,-1,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - if is_zero_flux_alb[3]: - self._dtilde2[boundary_grps + (3,)] = 2.0 * D / dy - else: - alb = self._albedo[3] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - # Define dtilde at bottom surface for all mesh cells on bottom boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,0] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - if is_zero_flux_alb[4]: - self._dtilde2[boundary_grps + (4,)] = 2.0 * D / dz - else: - alb = self._albedo[4] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - # Define dtilde at top surface for all mesh cells on top boundary - # Separate between zero flux b.c. and alebdo b.c. - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,-1] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - if is_zero_flux_alb[5]: - self._dtilde2[boundary_grps + (5,)] = 2.0 * D / dz - else: - alb = self._albedo[5] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - # Define reflector albedo for all cells on the left surface, in case - # a cell borders a reflector region on the left - current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] - current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - ref_albedo = np.divide(current_in_left, current_out_left, - where=current_out_left > 1.0e-10, - out=np.ones_like(current_out_left)) - - # Logical for whether neighboring cell to the left is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to left - neig_dc = np.roll(self._diffcof, 1, axis=0) - # Cell dimensions of neighbor to left - neig_hxyz = np.roll(self._hxyz, 1, axis=0) - - # Define dtilde at left surface for all mesh cells not on left boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[1:,:,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - neig_D = neig_dc[boundary_grps] - neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * neig_D) - / (neig_dx * D + dx * neig_D)) - - # Define reflector albedo for all cells on the right surface, in case - # a cell borders a reflector region on the right - current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] - current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - ref_albedo = np.divide(current_in_right, current_out_right, - where=current_out_right > 1.0e-10, - out=np.ones_like(current_out_right)) - - # Logical for whether neighboring cell to the right is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to right - neig_dc = np.roll(self._diffcof, -1, axis=0) - # Cell dimensions of neighbor to right - neig_hxyz = np.roll(self._hxyz, -1, axis=0) - - # Define dtilde at right surface for all mesh cells not on right boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:-1,:,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - neig_D = neig_dc[boundary_grps] - neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * neig_D) - / (neig_dx * D + dx * neig_D)) - - # Define reflector albedo for all cells on the back surface, in case - # a cell borders a reflector region on the back - current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] - current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - ref_albedo = np.divide(current_in_back, current_out_back, - where=current_out_back > 1.0e-10, - out=np.ones_like(current_out_back)) - - # Logical for whether neighboring cell to the back is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to back - neig_dc = np.roll(self._diffcof, 1, axis=1) - # Cell dimensions of neighbor to back - neig_hxyz = np.roll(self._hxyz, 1, axis=1) - - # Define dtilde at back surface for all mesh cells not on back boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,1:,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - neig_D = neig_dc[boundary_grps] - neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * neig_D) - / (neig_dy * D + dy * neig_D)) - - # Define reflector albedo for all cells on the front surface, in case - # a cell borders a reflector region in the front - current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] - current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - ref_albedo = np.divide(current_in_front, current_out_front, - where=current_out_front > 1.0e-10, - out=np.ones_like(current_out_front)) - - # Logical for whether neighboring cell to the front is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to front - neig_dc = np.roll(self._diffcof, -1, axis=1) - # Cell dimensions of neighbor to front - neig_hxyz = np.roll(self._hxyz, -1, axis=1) - - # Define dtilde at front surface for all mesh cells not on front boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:-1,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - neig_D = neig_dc[boundary_grps] - neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * neig_D) - / (neig_dy * D + dy * neig_D)) - - # Define reflector albedo for all cells on the bottom surface, in case - # a cell borders a reflector region on the bottom - current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] - current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - ref_albedo = np.divide(current_in_bottom, current_out_bottom, - where=current_out_bottom > 1.0e-10, - out=np.ones_like(current_out_bottom)) - - # Logical for whether neighboring cell to the bottom is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to bottom - neig_dc = np.roll(self._diffcof, 1, axis=2) - # Cell dimensions of neighbor to bottom - neig_hxyz = np.roll(self._hxyz, 1, axis=2) - - # Define dtilde at bottom surface for all mesh cells not on bottom boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,1:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - neig_D = neig_dc[boundary_grps] - neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * neig_D) - / (neig_dz * D + dz * neig_D)) - - # Define reflector albedo for all cells on the top surface, in case - # a cell borders a reflector region on the top - current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] - current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - ref_albedo = np.divide(current_in_top, current_out_top, - where=current_out_top > 1.0e-10, - out=np.ones_like(current_out_top)) - - # Logical for whether neighboring cell to the top is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to top - neig_dc = np.roll(self._diffcof, -1, axis=2) - # Cell dimensions of neighbor to top - neig_hxyz = np.roll(self._hxyz, -1, axis=2) - - # Define dtilde at top surface for all mesh cells not on top boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,:-1] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - neig_D = neig_dc[boundary_grps] - neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * neig_D) - / (neig_dz * D + dz * neig_D)) - print(np.where(self._dtilde2 != self._dtilde)) - def _compute_dtilde(self): """Computes the diffusion coupling coefficient by looping over all spatial regions and energy groups @@ -3683,255 +4141,6 @@ class CMFDRun(object): (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) - ################################################################################# - def _compute_dhat_vectorized_updated(self): - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - self._dhat2 = np.zeros((nx, ny, nz, ng, 6)) - - # Define current in each direction - current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] - current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] - current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] - current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] - current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] - current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] - current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - - dx = self._hxyz[:,:,:,np.newaxis,0] - dy = self._hxyz[:,:,:,np.newaxis,1] - dz = self._hxyz[:,:,:,np.newaxis,2] - dxdydz = np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - - # Define net current on each face - net_current_left = (current_in_left - current_out_left) / dxdydz * dx - net_current_right = (current_out_right - current_in_right) / dxdydz * dx - net_current_back = (current_in_back - current_out_back) / dxdydz * dy - net_current_front = (current_out_front - current_in_front) / dxdydz * dy - net_current_bottom = (current_in_bottom - current_out_bottom) / dxdydz * dz - net_current_top = (current_out_top - current_in_top) / dxdydz * dz - - # Define flux in each cell - cell_flux = self._flux / dxdydz - # Extract indices of coremap that are accelerated - is_accel = self._coremap != _CMFD_NOACCEL - - # Define dhat at left surface for all mesh cells on left boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[0,:,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_left[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 0)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux - - # Define dhat at right surface for all mesh cells on right boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[-1,:,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_right[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 1)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux - - # Define dhat at back surface for all mesh cells on back boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,0,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_back[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 2)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux - - # Define dhat at front surface for all mesh cells on front boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,-1,:] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_front[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 3)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux - - # Define dhat at bottom surface for all mesh cells on bottom boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,0] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_bottom[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 4)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux - - # Define dhat at top surface for all mesh cells on top boundary - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,-1] = True - boundary = np.where(is_accel & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_top[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 5)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux - - # Logical for whether neighboring cell to the left is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - # Cell flux of neighbor to left - neig_flux = np.roll(self._flux, 1, axis=0) / dxdydz - - # Define dhat at left surface for all mesh cells not on left boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[1:,:,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_left[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 0)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_left[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 0)] - flux = cell_flux[boundary_grps] - flux_left = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = ((net_current - dtilde * (flux_left - flux)) - / (flux_left + flux)) - - # Logical for whether neighboring cell to the right is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - # Cell flux of neighbor to right - neig_flux = np.roll(self._flux, -1, axis=0) / dxdydz - # Define dhat at right surface for all mesh cells not on right boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = np.where(is_accel[:-1,:,:] & adj_reflector[:-1,:,:]) - boundary_grps = boundary + (slice(None),) - net_current = net_current_right[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 1)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux - - boundary = np.where(is_accel[:-1,:,:] & ~adj_reflector[:-1,:,:]) - boundary_grps = boundary + (slice(None),) - net_current = net_current_right[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 1)] - flux = cell_flux[boundary_grps] - flux_right = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = ((net_current + dtilde * (flux_right - flux)) - / (flux_right + flux)) - - # Logical for whether neighboring cell to the back is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - # Cell flux of neighbor to back - neig_flux = np.roll(self._flux, 1, axis=1) / dxdydz - - # Define dhat at back surface for all mesh cells not on back boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,1:,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_back[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 2)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_back[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 2)] - flux = cell_flux[boundary_grps] - flux_back = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = ((net_current - dtilde * (flux_back - flux)) - / (flux_back + flux)) - - # Logical for whether neighboring cell to the front is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - # Cell flux of neighbor to front - neig_flux = np.roll(self._flux, -1, axis=1) / dxdydz - - # Define dhat at front surface for all mesh cells not on front boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:-1,:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_front[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 3)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_front[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 3)] - flux = cell_flux[boundary_grps] - flux_front = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = ((net_current + dtilde * (flux_front - flux)) - / (flux_front + flux)) - - # Logical for whether neighboring cell to the bottom is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - # Cell flux of neighbor to bottom - neig_flux = np.roll(self._flux, 1, axis=2) / dxdydz - - # Define dhat at bottom surface for all mesh cells not on bottom boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,1:] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_bottom[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 4)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_bottom[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 4)] - flux = cell_flux[boundary_grps] - flux_bottom = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = ((net_current - dtilde * (flux_bottom - flux)) - / (flux_bottom + flux)) - - # Logical for whether neighboring cell to the top is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - # Cell flux of neighbor to top - neig_flux = np.roll(self._flux, -1, axis=2) / dxdydz - - # Define dhat at top surface for all mesh cells not on top boundary - # Separate between cases where regions do and don't neighbor reflector regions - mask = np.full(self._coremap.shape, False, dtype=bool) - mask[:,:,:-1] = True - boundary = np.where(is_accel & adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_top[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 5)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux - - boundary = np.where(is_accel & ~adj_reflector & mask) - boundary_grps = boundary + (slice(None),) - net_current = net_current_top[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 5)] - flux = cell_flux[boundary_grps] - flux_top = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = ((net_current + dtilde * (flux_top - flux)) - / (flux_top + flux)) - print(np.where(self._dhat2 != self._dhat)) - - def _compute_dhat(self): """Computes the nonlinear coupling coefficient by looping over all spatial regions and energy groups @@ -4046,101 +4255,4 @@ class CMFDRun(object): else: return current[2*l+1]/current[2*l] - def _create_cmfd_tally(self): - """Creates all tallies in-memory that are used to solve CMFD problem""" - # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = openmc.capi.Mesh() - # Store id of Mesh object - self._cmfd_mesh_id = cmfd_mesh.id - # Set dimension and parameters of Mesh object - cmfd_mesh.dimension = self._cmfd_mesh.dimension - cmfd_mesh.set_parameters(lower_left=self._cmfd_mesh.lower_left, - upper_right=self._cmfd_mesh.upper_right, - width=self._cmfd_mesh.width) - # Create Mesh Filter object, stored internally - mesh_filter = openmc.capi.MeshFilter() - # Set mesh for Mesh Filter - mesh_filter.mesh = cmfd_mesh - - # Set up energy filters, if applicable - if self._energy_filters: - # Create Energy Filter object, stored internally - energy_filter = openmc.capi.EnergyFilter() - # Set bins for Energy Filter - energy_filter.bins = self._egrid - - # Create Energy Out Filter object, stored internally - energyout_filter = openmc.capi.EnergyoutFilter() - # Set bins for Energy Filter - energyout_filter.bins = self._egrid - - # Create Mesh Surface Filter object, stored internally - meshsurface_filter = openmc.capi.MeshSurfaceFilter() - # Set mesh for Mesh Surface Filter - meshsurface_filter.mesh = cmfd_mesh - - # Create Legendre Filter object, stored internally - legendre_filter = openmc.capi.LegendreFilter() - # Set order for Legendre Filter - legendre_filter.order = 1 - - # Create CMFD tallies, stored internally - n_tallies = 4 - self._cmfd_tally_ids = [] - for i in range(n_tallies): - tally = openmc.capi.Tally() - # Set nuclide bins - tally.nuclides = ['total'] - self._cmfd_tally_ids.append(tally.id) - - # Set attributes of CMFD flux, total tally - if i == 0: - # Set filters for tally - if self._energy_filters: - tally.filters = [mesh_filter, energy_filter] - else: - tally.filters = [mesh_filter] - # Set scores, type, and estimator for tally - tally.scores = ['flux', 'total'] - tally.type = 'volume' - tally.estimator = 'analog' - - # Set attributes of CMFD neutron production tally - elif i == 1: - # Set filters for tally - if self._energy_filters: - tally.filters = [mesh_filter, energy_filter, energyout_filter] - else: - tally.filters = [mesh_filter] - # Set scores, type, and estimator for tally - tally.scores = ['nu-scatter', 'nu-fission'] - tally.type = 'volume' - tally.estimator = 'analog' - - # Set attributes of CMFD surface current tally - elif i == 2: - # Set filters for tally - if self._energy_filters: - tally.filters = [meshsurface_filter, energy_filter] - else: - tally.filters = [meshsurface_filter] - # Set scores, type, and estimator for tally - tally.scores = ['current'] - tally.type = 'mesh-surface' - tally.estimator = 'analog' - - # Set attributes of CMFD P1 scatter tally - elif i == 3: - # Set filters for tally - if self._energy_filters: - tally.filters = [mesh_filter, legendre_filter, energy_filter] - else: - tally.filters = [mesh_filter, legendre_filter] - # Set scores for tally - tally.scores = ['scatter'] - tally.type = 'volume' - tally.estimator = 'analog' - - # Set all tallies to be active from beginning - tally.active = True From 37030fed2f22462e37133acdbbde3daf8fb3b01b Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 1 Nov 2018 22:44:25 -0400 Subject: [PATCH 38/58] Split between vectorized and updated-vectorized versions of CMFD --- openmc/cmfd.py | 1005 +++++++++++++++++++++++------------------------- 1 file changed, 477 insertions(+), 528 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 3e97f6564d..fd0a7f55fd 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -839,8 +839,36 @@ class CMFDRun(object): self._time_cmfd = None self._time_cmfdbuild = None self._time_cmfdsolve = None + + # Add all index-related variables, for numpy vectorization + self._last_x_accel = None + self._first_y_accel = None + self._last_y_accel = None + self._first_z_accel = None + self._last_z_accel = None + self._notfirst_x_accel = None + self._notlast_x_accel = None + self._notfirst_y_accel = None + self._notlast_y_accel = None + self._notfirst_z_accel = None + self._notlast_z_accel = None + self._adj_reflector_left = None + self._adj_reflector_right = None + self._adj_reflector_back = None + self._adj_reflector_front = None + self._adj_reflector_bottom = None + self._adj_reflector_top = None + self._accel_idxs = None + self._accel_neig_left_idxs = None + self._accel_neig_right_idxs = None + self._accel_neig_back_idxs = None + self._accel_neig_front_idxs = None + self._accel_neig_bottom_idxs = None + self._accel_neig_top_idxs = None + self._loss_row = None + self._loss_col = None self._intracomm = None - # TODO Add all index related variables + @property def cmfd_begin(self): @@ -1021,7 +1049,8 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, omp_num_threads=None, intracomm=None, vectorized=True): + # TEMP + def run(self, omp_num_threads=None, intracomm=None, vectorized=True, updated=False): """Public method to run OpenMC with CMFD This method is called by user to run CMFD once instance variables of @@ -1043,6 +1072,9 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD + # TEMP + self._updated = updated + # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) @@ -1056,9 +1088,12 @@ class CMFDRun(object): # Set up CMFD coremap self._set_coremap() - # TEMP + # Compute and store array indices used to build cross section arrays self._precompute_array_indices() + # Compute and store row and column indices used to build CMFD matrices + self._precompute_matrix_indices() + # Initialize simulation openmc.capi.simulation_init() @@ -1115,7 +1150,6 @@ class CMFDRun(object): Time in CMFD = {0:.5E} seconds Building matrices = {1:.5E} seconds Solving matrices = {2:.5E} seconds -<<<<<<< HEAD """.format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve)) sys.stdout.flush() @@ -1304,15 +1338,20 @@ class CMFDRun(object): # Calculate dtilde if vectorized: - self._compute_dtilde_vectorized() - self._compute_dtilde_vectorized_updated() + # TEMP + if self._updated: + self._compute_dtilde_vectorized_updated() + else: + self._compute_dtilde_vectorized() else: self._compute_dtilde() # Calculate dhat if vectorized: - self._compute_dhat_vectorized() - self._compute_dhat_vectorized_updated() + if self._updated: + self._compute_dhat_vectorized_updated() + else: + self._compute_dhat_vectorized() else: self._compute_dhat() @@ -1464,21 +1503,21 @@ class CMFDRun(object): phi = self._phi.reshape((n, ng)) # Extract indices of coremap that are accelerated - idx = np.where(self._coremap != _CMFD_NOACCEL) + idx = self._accel_idxs # Initialize CMFD flux map that maps phi to actual spatial and # group indices of problem cmfd_flux = np.zeros((nx, ny, nz, ng)) - cmfd_flux2 = np.zeros((nx, ny, nz, ng)) # Loop over all groups and set CMFD flux based on indices of # coremap and values of phi for g in range(ng): phi_g = phi[:,g] - cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] - cmfd_flux2[idx + (g,)] = phi_g[self._coremap[idx]] - print("CMFD Flux") - print(np.where(cmfd_flux != cmfd_flux2)) + # TEMP + if self._updated: + cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] + else: + cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] # Compute fission source cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ @@ -1599,21 +1638,22 @@ class CMFDRun(object): # Convert xyz location to the CMFD mesh index mesh_ijk = np.floor((source_xyz - m.lower_left) / m.width).astype(int) - # Determine which energy bin each particle's energy belongs to - energy_bins = np.where(source_energies < energy[0], ng - 1, - np.where(source_energies > energy[-1], 0, \ - ng - np.digitize(source_energies, energy))) - # Determine which energy bin each particle's energy belongs to - # Separate into cases bases on where source energies lies on egrid - energy_bins2 = np.zeros(len(source_energies), dtype=int) - idx = np.where(source_energies < energy[0]) - energy_bins2[idx] = ng - 1 - idx = np.where(source_energies > energy[-1]) - energy_bins2[idx] = 0 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) - energy_bins2[idx] = ng - np.digitize(source_energies, energy) - print("Energy Bins Reweight") - print(np.where(energy_bins != energy_bins2)) + # TEMP + if self._updated: + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins[idx] = ng - 1 + idx = np.where(source_energies > energy[-1]) + energy_bins[idx] = 0 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins[idx] = ng - np.digitize(source_energies, energy) + else: + # Determine which energy bin each particle's energy belongs to + energy_bins = np.where(source_energies < energy[0], ng - 1, + np.where(source_energies > energy[-1], 0, \ + ng - np.digitize(source_energies, energy))) # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight @@ -1659,22 +1699,22 @@ class CMFDRun(object): if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): outside[0] = True - # Determine which energy bin each particle's energy corresponds to - energy_bins = np.where(source_energies < energy[0], 0, - np.where(source_energies > energy[-1], ng - 1, \ - np.digitize(source_energies, energy) - 1)) - - # Determine which energy bin each particle's energy belongs to - # Separate into cases bases on where source energies lies on egrid - energy_bins2 = np.zeros(len(source_energies), dtype=int) - idx = np.where(source_energies < energy[0]) - energy_bins2[idx] = 0 - idx = np.where(source_energies > energy[-1]) - energy_bins2[idx] = ng - 1 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) - energy_bins2[idx] = np.digitize(source_energies, energy) - 1 - print("Energy bins bank sites") - print(np.where(energy_bins != energy_bins2)) + # TEMP + if self._updated: + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins[idx] = 0 + idx = np.where(source_energies > energy[-1]) + energy_bins[idx] = ng - 1 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins[idx] = np.digitize(source_energies, energy) - 1 + else: + # Determine which energy bin each particle's energy corresponds to + energy_bins = np.where(source_energies < energy[0], 0, + np.where(source_energies > energy[-1], ng - 1, \ + np.digitize(source_energies, energy) - 1)) # Determine all unique combinations of mesh bin and energy bin, and # count number of particles that belong to these combinations @@ -1717,10 +1757,14 @@ class CMFDRun(object): """ # Build loss and production matrices if vectorized: - loss = self._build_loss_matrix_vectorized(adjoint) - loss2 = self._build_loss_matrix_vectorized_updated(adjoint,loss) - prod = self._build_prod_matrix_vectorized(adjoint) - prod2 = self._build_prod_matrix_vectorized_updated(adjoint,prod) + if self._updated: + loss = self._build_loss_matrix_vectorized_updated(adjoint) + else: + loss = self._build_loss_matrix_vectorized(adjoint) + if self._updated: + prod = self._build_prod_matrix_vectorized_updated(adjoint) + else: + prod = self._build_prod_matrix_vectorized(adjoint) else: loss = self._build_loss_matrix(adjoint) prod = self._build_prod_matrix(adjoint) @@ -1766,276 +1810,7 @@ class CMFDRun(object): return loss, prod - def _precompute_array_indices(self): - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Logical for determining whether region of interest is accelerated region - is_accel = self._coremap != _CMFD_NOACCEL - # Logical for determining whether a zero flux "albedo" b.c. should be - # applied - is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT - x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) - - # Define slice equivalent to _accel[0,:,:] - slice_x = x_inds[:1,:,:] - slice_y = y_inds[:1,:,:] - slice_z = z_inds[:1,:,:] - bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[-1,:,:] - slice_x = x_inds[-1:,:,:] - slice_y = y_inds[-1:,:,:] - slice_z = z_inds[-1:,:,:] - bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,0,:] - slice_x = x_inds[:,:1,:] - slice_y = y_inds[:,:1,:] - slice_z = z_inds[:,:1,:] - bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,-1,:] - slice_x = x_inds[:,-1:,:] - slice_y = y_inds[:,-1:,:] - slice_z = z_inds[:,-1:,:] - bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,:,0] - slice_x = x_inds[:,:,:1] - slice_y = y_inds[:,:,:1] - slice_z = z_inds[:,:,:1] - bndry_accel = is_accel[(x_inds, y_inds, 0)] - self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,:,-1] - slice_x = x_inds[:,:,-1:] - slice_y = y_inds[:,:,-1:] - slice_z = z_inds[:,:,-1:] - bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the left is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[1:,:,:] & adj_reflector[1:,:,:] - slice_x = x_inds[1:,:,:] - slice_y = y_inds[1:,:,:] - slice_z = z_inds[1:,:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_x_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[1:,:,:] & ~adj_reflector[1:,:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_x_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the right is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[:-1,:,:] & adj_reflector[:-1,:,:] - slice_x = x_inds[:-1,:,:] - slice_y = y_inds[:-1,:,:] - slice_z = z_inds[:-1,:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_x_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:-1,:,:] & ~adj_reflector[:-1,:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_x_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the back is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[:,1:,:] & adj_reflector[:,1:,:] - slice_x = x_inds[:,1:,:] - slice_y = y_inds[:,1:,:] - slice_z = z_inds[:,1:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_y_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,1:,:] & ~adj_reflector[:,1:,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_y_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the front is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[:,:-1,:] & adj_reflector[:,:-1,:] - slice_x = x_inds[:,:-1,:] - slice_y = y_inds[:,:-1,:] - slice_z = z_inds[:,:-1,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_y_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,:-1,:] & ~adj_reflector[:,:-1,:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_y_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the bottom is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[:,:,1:] & adj_reflector[:,:,1:] - slice_x = x_inds[:,:,1:] - slice_y = y_inds[:,:,1:] - slice_z = z_inds[:,:,1:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_z_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,:,1:] & ~adj_reflector[:,:,1:] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notfirst_z_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Logical for whether neighboring cell to the top is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - - # Define slice equivalent to is_accel[:,:,:-1] & adj_reflector[:,:,:-1] - slice_x = x_inds[:,:,:-1] - slice_y = y_inds[:,:,:-1] - slice_z = z_inds[:,:,:-1] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_z_accel_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # Define slice equivalent to is_accel[:,:,:-1] & ~adj_reflector[:,:,:-1] - bndry_accel = (is_accel[(slice_x, slice_y, slice_z)] & - ~adj_reflector[(slice_x, slice_y, slice_z)]) - self._notlast_z_accel_not_adj_ref = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) - - # TEMP ALL ARRAY INDICES RELATED TO BUILDING MATRIX - # Shift coremap in all directions to determine whether leakage term - # should be defined for particular cell in matrix - coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] - - coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] - - coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] - - coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] - - coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] - - coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] - - # Create empty row and column vectors to store for loss matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - - self._is_accel = np.where(self._coremap != _CMFD_NOACCEL) - self._is_accel_neig_left = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_left != _CMFD_NOACCEL)) - self._is_accel_neig_right = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_right != _CMFD_NOACCEL)) - self._is_accel_neig_back = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_back != _CMFD_NOACCEL)) - self._is_accel_neig_front = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_front != _CMFD_NOACCEL)) - self._is_accel_neig_bottom = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_bottom != _CMFD_NOACCEL)) - self._is_accel_neig_top = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_top != _CMFD_NOACCEL)) - - for g in range(ng): - # Extract row and column data of regions where a cell and its - # neighbor to the left are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_left]) + g - idx_y = ng * (coremap_shift_left[self._is_accel_neig_left]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract row and column data of regions where a cell and its - # neighbor to the right are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_right]) + g - idx_y = ng * (coremap_shift_right[self._is_accel_neig_right]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract row and column data of regions where a cell and its - # neighbor to the back are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_back]) + g - idx_y = ng * (coremap_shift_back[self._is_accel_neig_back]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract row and column data of regions where a cell and its - # neighbor to the front are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_front]) + g - idx_y = ng * (coremap_shift_front[self._is_accel_neig_front]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract row and column data of regions where a cell and its - # neighbor to the bottom are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_bottom]) + g - idx_y = ng * (coremap_shift_bottom[self._is_accel_neig_bottom]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract row and column data of regions where a cell and its - # neighbor to the top are both fuel regions - idx_x = ng * (self._coremap[self._is_accel_neig_top]) + g - idx_y = ng * (coremap_shift_top[self._is_accel_neig_top]) + g - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Extract all regions where a cell is a fuel region - idx_x = ng * (self._coremap[self._is_accel]) + g - idx_y = idx_x - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - for h in range(ng): - if h != g: - # Extract all regions where a cell is a fuel region - idx_x = ng * (self._coremap[self._is_accel]) + g - idx_y = ng * (self._coremap[self._is_accel]) + h - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Store row and col as rows and columns of production matrix - self._loss_row = row - self._loss_col = col - - # Create empty row and column vectors to store for production matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - - for g in range(ng): - for h in range(ng): - # Extract all regions where a cell is a fuel region - idx_x = ng * (self._coremap[self._is_accel]) + g - idx_y = ng * (self._coremap[self._is_accel]) + h - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - - # Store row and col as rows and columns of production matrix - self._prod_row = row - self._prod_col = col - - - def _build_loss_matrix_vectorized_updated(self, adjoint, org_loss): + def _build_loss_matrix_vectorized_updated(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -2068,63 +1843,63 @@ class CMFDRun(object): for g in range(ng): # Define leakage terms that relate terms to their neighbors to the # left - dtilde = self._dtilde[:,:,:,g,0][self._is_accel_neig_left] - dhat = self._dhat[:,:,:,g,0][self._is_accel_neig_left] - dx = self._hxyz[:,:,:,0][self._is_accel_neig_left] + dtilde = self._dtilde[:,:,:,g,0][self._accel_neig_left_idxs] + dhat = self._dhat[:,:,:,g,0][self._accel_neig_left_idxs] + dx = self._hxyz[:,:,:,0][self._accel_neig_left_idxs] vals = (-1.0 * dtilde - dhat) / dx # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # right - dtilde = self._dtilde[:,:,:,g,1][self._is_accel_neig_right] - dhat = self._dhat[:,:,:,g,1][self._is_accel_neig_right] - dx = self._hxyz[:,:,:,0][self._is_accel_neig_right] + dtilde = self._dtilde[:,:,:,g,1][self._accel_neig_right_idxs] + dhat = self._dhat[:,:,:,g,1][self._accel_neig_right_idxs] + dx = self._hxyz[:,:,:,0][self._accel_neig_right_idxs] vals = (-1.0 * dtilde + dhat) / dx # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors in the # back - dtilde = self._dtilde[:,:,:,g,2][self._is_accel_neig_back] - dhat = self._dhat[:,:,:,g,2][self._is_accel_neig_back] - dy = self._hxyz[:,:,:,1][self._is_accel_neig_back] + dtilde = self._dtilde[:,:,:,g,2][self._accel_neig_back_idxs] + dhat = self._dhat[:,:,:,g,2][self._accel_neig_back_idxs] + dy = self._hxyz[:,:,:,1][self._accel_neig_back_idxs] vals = (-1.0 * dtilde - dhat) / dy # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors in the # front - dtilde = self._dtilde[:,:,:,g,3][self._is_accel_neig_front] - dhat = self._dhat[:,:,:,g,3][self._is_accel_neig_front] - dy = self._hxyz[:,:,:,1][self._is_accel_neig_front] + dtilde = self._dtilde[:,:,:,g,3][self._accel_neig_front_idxs] + dhat = self._dhat[:,:,:,g,3][self._accel_neig_front_idxs] + dy = self._hxyz[:,:,:,1][self._accel_neig_front_idxs] vals = (-1.0 * dtilde + dhat) / dy # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # bottom - dtilde = self._dtilde[:,:,:,g,4][self._is_accel_neig_bottom] - dhat = self._dhat[:,:,:,g,4][self._is_accel_neig_bottom] - dz = self._hxyz[:,:,:,2][self._is_accel_neig_bottom] + dtilde = self._dtilde[:,:,:,g,4][self._accel_neig_bottom_idxs] + dhat = self._dhat[:,:,:,g,4][self._accel_neig_bottom_idxs] + dz = self._hxyz[:,:,:,2][self._accel_neig_bottom_idxs] vals = (-1.0 * dtilde - dhat) / dz # Store data to add to CSR matrix data = np.append(data, vals) # Define leakage terms that relate terms to their neighbors to the # top - dtilde = self._dtilde[:,:,:,g,5][self._is_accel_neig_top] - dhat = self._dhat[:,:,:,g,5][self._is_accel_neig_top] - dz = self._hxyz[:,:,:,2][self._is_accel_neig_top] + dtilde = self._dtilde[:,:,:,g,5][self._accel_neig_top_idxs] + dhat = self._dhat[:,:,:,g,5][self._accel_neig_top_idxs] + dz = self._hxyz[:,:,:,2][self._accel_neig_top_idxs] vals = (-1.0 * dtilde + dhat) / dz # Store data to add to CSR matrix data = np.append(data, vals) # Define terms that relate to loss of neutrons in a cell. These # correspond to all the diagonal entries of the loss matrix - jnet_g = jnet[:,:,:,g][self._is_accel] - total_xs = self._totalxs[:,:,:,g][self._is_accel] - scatt_xs = self._scattxs[:,:,:,g,g][self._is_accel] + jnet_g = jnet[:,:,:,g][self._accel_idxs] + total_xs = self._totalxs[:,:,:,g][self._accel_idxs] + scatt_xs = self._scattxs[:,:,:,g,g][self._accel_idxs] vals = jnet_g + total_xs - scatt_xs # Store data to add to CSR matrix data = np.append(data, vals) @@ -2136,21 +1911,19 @@ class CMFDRun(object): if h != g: # Get scattering macro xs, transposed if adjoint: - scatt_xs = self._scattxs[:,:,:,g,h][self._is_accel] + scatt_xs = self._scattxs[:,:,:,g,h][self._accel_idxs] # Get scattering macro xs else: - scatt_xs = self._scattxs[:,:,:,h,g][self._is_accel] + scatt_xs = self._scattxs[:,:,:,h,g][self._accel_idxs] vals = -1.0 * scatt_xs # Store data to add to CSR matrix data = np.append(data, vals) # Create csr matrix loss = sparse.csr_matrix((data, (self._loss_row, self._loss_col)), shape=(n, n)) - print("Loss Mat") - print(np.where(loss.toarray() != org_loss.toarray())) return loss - def _build_prod_matrix_vectorized_updated(self, adjoint, org_prod): + def _build_prod_matrix_vectorized_updated(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -2161,23 +1934,17 @@ class CMFDRun(object): # Define terms that relate to fission production from group to group. for g in range(ng): for h in range(ng): - # Extract all regions where a cell is a fuel region - indices = np.where(self._coremap != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[indices]) + g - idx_y = ng * (self._coremap[indices]) + h # Get nu-fission macro xs, transposed if adjoint: - vals = (self._nfissxs[:, :, :, g, h])[self._is_accel] + vals = (self._nfissxs[:, :, :, g, h])[self._accel_idxs] # Get nu-fission macro xs else: - vals = (self._nfissxs[:, :, :, h, g])[self._is_accel] + vals = (self._nfissxs[:, :, :, h, g])[self._accel_idxs] # Store rows, cols, and data to add to CSR matrix data = np.append(data, vals) # Create csr matrix prod = sparse.csr_matrix((data, (self._prod_row, self._prod_col)), shape=(n, n)) - print("Prod Mat") - print(np.where(prod.toarray() != org_prod.toarray())) return prod def _execute_power_iter(self, loss, prod): @@ -2346,7 +2113,7 @@ class CMFDRun(object): sections, flux, and diffusion coefficients for each mesh cell """ - # Extract energy indices + # Extract spatial and energy indices nx = self._indices[0] ny = self._indices[1] nz = self._indices[2] @@ -2553,7 +2320,7 @@ class CMFDRun(object): ng = self._indices[3] # Get number of accelerated regions - num_accel = np.sum(self._coremap != _CMFD_NOACCEL) + num_accel = self._mat_dim # Get openmc k-effective keff = openmc.capi.keff_temp()[0] @@ -2596,13 +2363,250 @@ class CMFDRun(object): np.sum(np.multiply(self._resnb, self._resnb)) / \ (ng * num_accel))) - def _compute_dtilde_vectorized_updated(self): - # Extract spatial and energy indices + def _precompute_array_indices(self): + """Computes the indices used to populate certain cross section arrays. + These indices are used in _compute_dtilde and _compute_dhat + + """ + # Extract spatial indices nx = self._indices[0] ny = self._indices[1] nz = self._indices[2] + + + # Logical for determining whether region of interest is accelerated region + is_accel = self._coremap != _CMFD_NOACCEL + # Logical for determining whether a zero flux "albedo" b.c. should be + # applied + is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT + x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) + + # Define slice equivalent to _accel[0,:,:] + slice_x = x_inds[:1,:,:] + slice_y = y_inds[:1,:,:] + slice_z = z_inds[:1,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[-1,:,:] + slice_x = x_inds[-1:,:,:] + slice_y = y_inds[-1:,:,:] + slice_z = z_inds[-1:,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,0,:] + slice_x = x_inds[:,:1,:] + slice_y = y_inds[:,:1,:] + slice_z = z_inds[:,:1,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,-1,:] + slice_x = x_inds[:,-1:,:] + slice_y = y_inds[:,-1:,:] + slice_z = z_inds[:,-1:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,0] + slice_x = x_inds[:,:,:1] + slice_y = y_inds[:,:,:1] + slice_z = z_inds[:,:,:1] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,-1] + slice_x = x_inds[:,:,-1:] + slice_y = y_inds[:,:,-1:] + slice_z = z_inds[:,:,-1:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[1:,:,:] + slice_x = x_inds[1:,:,:] + slice_y = y_inds[1:,:,:] + slice_z = z_inds[1:,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notfirst_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:-1,:,:] + slice_x = x_inds[:-1,:,:] + slice_y = y_inds[:-1,:,:] + slice_z = z_inds[:-1,:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notlast_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,1:,:] + slice_x = x_inds[:,1:,:] + slice_y = y_inds[:,1:,:] + slice_z = z_inds[:,1:,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notfirst_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:-1,:] + slice_x = x_inds[:,:-1,:] + slice_y = y_inds[:,:-1,:] + slice_z = z_inds[:,:-1,:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notlast_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,1:] + slice_x = x_inds[:,:,1:] + slice_y = y_inds[:,:,1:] + slice_z = z_inds[:,:,1:] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notfirst_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Define slice equivalent to is_accel[:,:,:-1] + slice_x = x_inds[:,:,:-1] + slice_y = y_inds[:,:,:-1] + slice_z = z_inds[:,:,:-1] + bndry_accel = is_accel[(slice_x, slice_y, slice_z)] + self._notlast_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) + + # Store logical for whether neighboring cell is reflector region + # in all directions + self._adj_reflector_left = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + self._adj_reflector_right = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + self._adj_reflector_back = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + self._adj_reflector_front = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + self._adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + self._adj_reflector_top = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + + def _precompute_matrix_indices(self): + """Computes the indices and row/column data used to populate CMFD CSR matrices. + These indices are used in _build_loss_matrix and _build_prod_matrix + + """ + # Extract energy group indices ng = self._indices[3] - self._dtilde2 = np.zeros((nx, ny, nz, ng, 6)) + + # Shift coremap in all directions to determine whether leakage term + # should be defined for particular cell in matrix + coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] + + coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] + + coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] + + coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] + + coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] + + coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), + mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] + + # Create empty row and column vectors to store for loss matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + + # Store all indices used to populate production and loss matrix + self._accel_idxs = np.where(self._coremap != _CMFD_NOACCEL) + self._accel_neig_left_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_left != _CMFD_NOACCEL)) + self._accel_neig_right_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_right != _CMFD_NOACCEL)) + self._accel_neig_back_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_back != _CMFD_NOACCEL)) + self._accel_neig_front_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_front != _CMFD_NOACCEL)) + self._accel_neig_bottom_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_bottom != _CMFD_NOACCEL)) + self._accel_neig_top_idxs = np.where((self._coremap != _CMFD_NOACCEL) & + (coremap_shift_top != _CMFD_NOACCEL)) + + for g in range(ng): + # Extract row and column data of regions where a cell and its + # neighbor to the left are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_left_idxs]) + g + idx_y = ng * (coremap_shift_left[self._accel_neig_left_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the right are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_right_idxs]) + g + idx_y = ng * (coremap_shift_right[self._accel_neig_right_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the back are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_back_idxs]) + g + idx_y = ng * (coremap_shift_back[self._accel_neig_back_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the front are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_front_idxs]) + g + idx_y = ng * (coremap_shift_front[self._accel_neig_front_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the bottom are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_bottom_idxs]) + g + idx_y = ng * (coremap_shift_bottom[self._accel_neig_bottom_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract row and column data of regions where a cell and its + # neighbor to the top are both fuel regions + idx_x = ng * (self._coremap[self._accel_neig_top_idxs]) + g + idx_y = ng * (coremap_shift_top[self._accel_neig_top_idxs]) + g + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._accel_idxs]) + g + idx_y = idx_x + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + for h in range(ng): + if h != g: + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._accel_idxs]) + g + idx_y = ng * (self._coremap[self._accel_idxs]) + h + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Store row and col as rows and columns of production matrix + self._loss_row = row + self._loss_col = col + + # Create empty row and column vectors to store for production matrix + row = np.array([], dtype=int) + col = np.array([], dtype=int) + + for g in range(ng): + for h in range(ng): + # Extract all regions where a cell is a fuel region + idx_x = ng * (self._coremap[self._accel_idxs]) + g + idx_y = ng * (self._coremap[self._accel_idxs]) + h + # Store rows, cols, and data to add to CSR matrix + row = np.append(row, idx_x) + col = np.append(col, idx_y) + + # Store row and col as rows and columns of production matrix + self._prod_row = row + self._prod_col = col + + def _compute_dtilde_vectorized_updated(self): + # TODO: Update comment # Logical for determining whether a zero flux "albedo" b.c. should be # applied @@ -2615,10 +2619,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dx = self._hxyz[boundary + (np.newaxis, 0)] if is_zero_flux_alb[0]: - self._dtilde2[boundary_grps + (0,)] = 2.0 * D / dx + self._dtilde[boundary_grps + (0,)] = 2.0 * D / dx else: alb = self._albedo[0] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) + self._dtilde[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) # Define dtilde at right surface for all mesh cells on right boundary @@ -2628,10 +2632,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dx = self._hxyz[boundary + (np.newaxis, 0)] if is_zero_flux_alb[1]: - self._dtilde2[boundary_grps + (1,)] = 2.0 * D / dx + self._dtilde[boundary_grps + (1,)] = 2.0 * D / dx else: alb = self._albedo[1] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) + self._dtilde[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) # Define dtilde at back surface for all mesh cells on back boundary @@ -2641,10 +2645,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dy = self._hxyz[boundary + (np.newaxis, 1)] if is_zero_flux_alb[2]: - self._dtilde2[boundary_grps + (2,)] = 2.0 * D / dy + self._dtilde[boundary_grps + (2,)] = 2.0 * D / dy else: alb = self._albedo[2] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) + self._dtilde[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) # Define dtilde at front surface for all mesh cells on front boundary @@ -2654,10 +2658,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dy = self._hxyz[boundary + (np.newaxis, 1)] if is_zero_flux_alb[3]: - self._dtilde2[boundary_grps + (3,)] = 2.0 * D / dy + self._dtilde[boundary_grps + (3,)] = 2.0 * D / dy else: alb = self._albedo[3] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) + self._dtilde[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) # Define dtilde at bottom surface for all mesh cells on bottom boundary @@ -2667,10 +2671,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dz = self._hxyz[boundary + (np.newaxis, 2)] if is_zero_flux_alb[4]: - self._dtilde2[boundary_grps + (4,)] = 2.0 * D / dz + self._dtilde[boundary_grps + (4,)] = 2.0 * D / dz else: alb = self._albedo[4] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) + self._dtilde[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) # Define dtilde at top surface for all mesh cells on top boundary @@ -2681,10 +2685,10 @@ class CMFDRun(object): D = self._diffcof[boundary_grps] dz = self._hxyz[boundary + (np.newaxis, 2)] if is_zero_flux_alb[5]: - self._dtilde2[boundary_grps + (5,)] = 2.0 * D / dz + self._dtilde[boundary_grps + (5,)] = 2.0 * D / dz else: alb = self._albedo[5] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) + self._dtilde[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) # Define reflector albedo for all cells on the left surface, in case @@ -2701,23 +2705,19 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, 1, axis=0) # Define dtilde at left surface for all mesh cells not on left boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_x_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - boundary = self._notfirst_x_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_x_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dx = self._hxyz[boundary + (np.newaxis, 0)] neig_D = neig_dc[boundary_grps] neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] - self._dtilde2[boundary_grps + (0,)] = ((2.0 * D * neig_D) - / (neig_dx * D + dx * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_left = self._adj_reflector_left[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (0,)] = np.where(is_adj_ref_left, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) # Define reflector albedo for all cells on the right surface, in case # a cell borders a reflector region on the right @@ -2733,23 +2733,19 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, -1, axis=0) # Define dtilde at right surface for all mesh cells not on right boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_x_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dx = self._hxyz[boundary + (np.newaxis, 0)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) - - boundary = self._notlast_x_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_x_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dx = self._hxyz[boundary + (np.newaxis, 0)] neig_D = neig_dc[boundary_grps] neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] - self._dtilde2[boundary_grps + (1,)] = ((2.0 * D * neig_D) - / (neig_dx * D + dx * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_right = self._adj_reflector_right[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (1,)] = np.where(is_adj_ref_right, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) # Define reflector albedo for all cells on the back surface, in case # a cell borders a reflector region on the back @@ -2765,23 +2761,19 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, 1, axis=1) # Define dtilde at back surface for all mesh cells not on back boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_y_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - boundary = self._notfirst_y_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_y_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dy = self._hxyz[boundary + (np.newaxis, 1)] neig_D = neig_dc[boundary_grps] neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] - self._dtilde2[boundary_grps + (2,)] = ((2.0 * D * neig_D) - / (neig_dy * D + dy * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_back = self._adj_reflector_back[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (2,)] = np.where(is_adj_ref_back, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) # Define reflector albedo for all cells on the front surface, in case # a cell borders a reflector region in the front @@ -2797,23 +2789,19 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, -1, axis=1) # Define dtilde at front surface for all mesh cells not on front boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_y_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dy = self._hxyz[boundary + (np.newaxis, 1)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) - - boundary = self._notlast_y_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_y_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dy = self._hxyz[boundary + (np.newaxis, 1)] neig_D = neig_dc[boundary_grps] neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] - self._dtilde2[boundary_grps + (3,)] = ((2.0 * D * neig_D) - / (neig_dy * D + dy * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_front = self._adj_reflector_front[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (3,)] = np.where(is_adj_ref_front, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) # Define reflector albedo for all cells on the bottom surface, in case # a cell borders a reflector region on the bottom @@ -2829,23 +2817,19 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, 1, axis=2) # Define dtilde at bottom surface for all mesh cells not on bottom boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_z_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - boundary = self._notfirst_z_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_z_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dz = self._hxyz[boundary + (np.newaxis, 2)] neig_D = neig_dc[boundary_grps] neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] - self._dtilde2[boundary_grps + (4,)] = ((2.0 * D * neig_D) - / (neig_dz * D + dz * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_bottom = self._adj_reflector_bottom[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (4,)] = np.where(is_adj_ref_bottom, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) # Define reflector albedo for all cells on the top surface, in case # a cell borders a reflector region on the top @@ -2861,31 +2845,22 @@ class CMFDRun(object): neig_hxyz = np.roll(self._hxyz, -1, axis=2) # Define dtilde at top surface for all mesh cells not on top boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_z_accel_adj_ref - boundary_grps = boundary + (slice(None),) - D = self._diffcof[boundary_grps] - dz = self._hxyz[boundary + (np.newaxis, 2)] - alb = ref_albedo[boundary_grps] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) - - boundary = self._notlast_z_accel_not_adj_ref + # Dtilde is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_z_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] dz = self._hxyz[boundary + (np.newaxis, 2)] neig_D = neig_dc[boundary_grps] neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] - self._dtilde2[boundary_grps + (5,)] = ((2.0 * D * neig_D) - / (neig_dz * D + dz * neig_D)) + alb = ref_albedo[boundary_grps] + is_adj_ref_top = self._adj_reflector_top[boundary + (np.newaxis,)] + self._dtilde[boundary_grps + (5,)] = np.where(is_adj_ref_top, + (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) def _compute_dhat_vectorized_updated(self): - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - self._dhat2 = np.zeros((nx, ny, nz, ng, 6)) - + #TODO update comment # Define current in each direction current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] @@ -2924,7 +2899,7 @@ class CMFDRun(object): net_current = net_current_left[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 0)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux + self._dhat[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux # Define dhat at right surface for all mesh cells on right boundary boundary = self._last_x_accel @@ -2932,7 +2907,7 @@ class CMFDRun(object): net_current = net_current_right[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 1)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux + self._dhat[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux # Define dhat at back surface for all mesh cells on back boundary boundary = self._first_y_accel @@ -2940,7 +2915,7 @@ class CMFDRun(object): net_current = net_current_back[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 2)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux + self._dhat[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux # Define dhat at front surface for all mesh cells on front boundary boundary = self._last_y_accel @@ -2948,7 +2923,7 @@ class CMFDRun(object): net_current = net_current_front[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 3)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux + self._dhat[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux # Define dhat at bottom surface for all mesh cells on bottom boundary boundary = self._first_z_accel @@ -2956,7 +2931,7 @@ class CMFDRun(object): net_current = net_current_bottom[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 4)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux + self._dhat[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux # Define dhat at top surface for all mesh cells on top boundary boundary = self._last_z_accel @@ -2964,135 +2939,109 @@ class CMFDRun(object): net_current = net_current_top[boundary_grps] dtilde = self._dtilde[boundary + (slice(None), 5)] flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux + self._dhat[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux # Cell flux of neighbor to left neig_flux = np.roll(self._flux, 1, axis=0) / dxdydz # Define dhat at left surface for all mesh cells not on left boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_x_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_x_accel boundary_grps = boundary + (slice(None),) net_current = net_current_left[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 0)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = (net_current + dtilde * flux) / flux - - boundary = self._notfirst_x_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_left[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 0)] + dtilde = self._dtilde[boundary_grps + (0,)] flux = cell_flux[boundary_grps] flux_left = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (0,)] = ((net_current - dtilde * (flux_left - flux)) - / (flux_left + flux)) + is_adj_ref_left = self._adj_reflector_left[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (0,)] = np.where(is_adj_ref_left, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_left - flux)) / (flux_left + flux)) # Cell flux of neighbor to right neig_flux = np.roll(self._flux, -1, axis=0) / dxdydz # Define dhat at right surface for all mesh cells not on right boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_x_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_x_accel boundary_grps = boundary + (slice(None),) net_current = net_current_right[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 1)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = (net_current - dtilde * flux) / flux - - boundary = self._notlast_x_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_right[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 1)] + dtilde = self._dtilde[boundary_grps + (1,)] flux = cell_flux[boundary_grps] flux_right = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (1,)] = ((net_current + dtilde * (flux_right - flux)) - / (flux_right + flux)) + is_adj_ref_right = self._adj_reflector_right[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (1,)] = np.where(is_adj_ref_right, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_right - flux)) / (flux_right + flux)) # Cell flux of neighbor to back neig_flux = np.roll(self._flux, 1, axis=1) / dxdydz # Define dhat at back surface for all mesh cells not on back boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_y_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_y_accel boundary_grps = boundary + (slice(None),) net_current = net_current_back[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 2)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = (net_current + dtilde * flux) / flux - - boundary = self._notfirst_y_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_back[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 2)] + dtilde = self._dtilde[boundary_grps + (2,)] flux = cell_flux[boundary_grps] flux_back = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (2,)] = ((net_current - dtilde * (flux_back - flux)) - / (flux_back + flux)) + is_adj_ref_back = self._adj_reflector_back[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (2,)] = np.where(is_adj_ref_back, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_back - flux)) / (flux_back + flux)) # Cell flux of neighbor to front neig_flux = np.roll(self._flux, -1, axis=1) / dxdydz # Define dhat at front surface for all mesh cells not on front boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_y_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_y_accel boundary_grps = boundary + (slice(None),) net_current = net_current_front[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 3)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = (net_current - dtilde * flux) / flux - - boundary = self._notlast_y_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_front[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 3)] + dtilde = self._dtilde[boundary_grps + (3,)] flux = cell_flux[boundary_grps] flux_front = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (3,)] = ((net_current + dtilde * (flux_front - flux)) - / (flux_front + flux)) + is_adj_ref_front = self._adj_reflector_front[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (3,)] = np.where(is_adj_ref_front, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_front - flux)) / (flux_front + flux)) # Cell flux of neighbor to bottom neig_flux = np.roll(self._flux, 1, axis=2) / dxdydz # Define dhat at bottom surface for all mesh cells not on bottom boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notfirst_z_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notfirst_z_accel boundary_grps = boundary + (slice(None),) net_current = net_current_bottom[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 4)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = (net_current + dtilde * flux) / flux - - boundary = self._notfirst_z_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_bottom[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 4)] + dtilde = self._dtilde[boundary_grps + (4,)] flux = cell_flux[boundary_grps] flux_bottom = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (4,)] = ((net_current - dtilde * (flux_bottom - flux)) - / (flux_bottom + flux)) + is_adj_ref_bottom = self._adj_reflector_bottom[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (4,)] = np.where(is_adj_ref_bottom, + (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_bottom - flux)) / (flux_bottom + flux)) # Cell flux of neighbor to top neig_flux = np.roll(self._flux, -1, axis=2) / dxdydz # Define dhat at top surface for all mesh cells not on top boundary - # Separate between cases where regions do and don't neighbor reflector regions - boundary = self._notlast_z_accel_adj_ref + # Dhat is defined differently for regions that do and don't neighbor + # reflector regions + boundary = self._notlast_z_accel boundary_grps = boundary + (slice(None),) net_current = net_current_top[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 5)] - flux = cell_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = (net_current - dtilde * flux) / flux - - boundary = self._notlast_z_accel_not_adj_ref - boundary_grps = boundary + (slice(None),) - net_current = net_current_top[boundary_grps] - dtilde = self._dtilde[boundary + (slice(None), 5)] + dtilde = self._dtilde[boundary_grps + (5,)] flux = cell_flux[boundary_grps] flux_top = neig_flux[boundary_grps] - self._dhat2[boundary_grps + (5,)] = ((net_current + dtilde * (flux_top - flux)) - / (flux_top + flux)) - print("Dhat") - print(np.where(self._dhat2 != self._dhat)) + is_adj_ref_top = self._adj_reflector_top[boundary + (np.newaxis,)] + self._dhat[boundary_grps + (5,)] = np.where(is_adj_ref_top, + (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_top - flux)) / (flux_top + flux)) def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" From 58fdec12a1ba9f4b680fc5a4d51d8b14335e40d3 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 2 Nov 2018 00:53:57 -0400 Subject: [PATCH 39/58] Get rid of old vectorized functions, use updated versions --- openmc/cmfd.py | 734 ++----------------------------------------------- 1 file changed, 29 insertions(+), 705 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index fd0a7f55fd..8eee2a6d32 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1049,8 +1049,7 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - # TEMP - def run(self, omp_num_threads=None, intracomm=None, vectorized=True, updated=False): + def run(self, omp_num_threads=None, intracomm=None, vectorized=True): """Public method to run OpenMC with CMFD This method is called by user to run CMFD once instance variables of @@ -1072,9 +1071,6 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD - # TEMP - self._updated = updated - # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) @@ -1338,20 +1334,13 @@ class CMFDRun(object): # Calculate dtilde if vectorized: - # TEMP - if self._updated: - self._compute_dtilde_vectorized_updated() - else: - self._compute_dtilde_vectorized() + self._compute_dtilde_vectorized() else: self._compute_dtilde() # Calculate dhat if vectorized: - if self._updated: - self._compute_dhat_vectorized_updated() - else: - self._compute_dhat_vectorized() + self._compute_dhat_vectorized() else: self._compute_dhat() @@ -1513,11 +1502,7 @@ class CMFDRun(object): # coremap and values of phi for g in range(ng): phi_g = phi[:,g] - # TEMP - if self._updated: - cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] - else: - cmfd_flux[idx + (np.full((n,),g),)] = phi_g[self._coremap[idx]] + cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] # Compute fission source cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ @@ -1638,22 +1623,15 @@ class CMFDRun(object): # Convert xyz location to the CMFD mesh index mesh_ijk = np.floor((source_xyz - m.lower_left) / m.width).astype(int) - # TEMP - if self._updated: - # Determine which energy bin each particle's energy belongs to - # Separate into cases bases on where source energies lies on egrid - energy_bins = np.zeros(len(source_energies), dtype=int) - idx = np.where(source_energies < energy[0]) - energy_bins[idx] = ng - 1 - idx = np.where(source_energies > energy[-1]) - energy_bins[idx] = 0 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) - energy_bins[idx] = ng - np.digitize(source_energies, energy) - else: - # Determine which energy bin each particle's energy belongs to - energy_bins = np.where(source_energies < energy[0], ng - 1, - np.where(source_energies > energy[-1], 0, \ - ng - np.digitize(source_energies, energy))) + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins[idx] = ng - 1 + idx = np.where(source_energies > energy[-1]) + energy_bins[idx] = 0 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins[idx] = ng - np.digitize(source_energies, energy) # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight @@ -1699,22 +1677,15 @@ class CMFDRun(object): if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): outside[0] = True - # TEMP - if self._updated: - # Determine which energy bin each particle's energy belongs to - # Separate into cases bases on where source energies lies on egrid - energy_bins = np.zeros(len(source_energies), dtype=int) - idx = np.where(source_energies < energy[0]) - energy_bins[idx] = 0 - idx = np.where(source_energies > energy[-1]) - energy_bins[idx] = ng - 1 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) - energy_bins[idx] = np.digitize(source_energies, energy) - 1 - else: - # Determine which energy bin each particle's energy corresponds to - energy_bins = np.where(source_energies < energy[0], 0, - np.where(source_energies > energy[-1], ng - 1, \ - np.digitize(source_energies, energy) - 1)) + # Determine which energy bin each particle's energy belongs to + # Separate into cases bases on where source energies lies on egrid + energy_bins = np.zeros(len(source_energies), dtype=int) + idx = np.where(source_energies < energy[0]) + energy_bins[idx] = 0 + idx = np.where(source_energies > energy[-1]) + energy_bins[idx] = ng - 1 + idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + energy_bins[idx] = np.digitize(source_energies, energy) - 1 # Determine all unique combinations of mesh bin and energy bin, and # count number of particles that belong to these combinations @@ -1757,14 +1728,8 @@ class CMFDRun(object): """ # Build loss and production matrices if vectorized: - if self._updated: - loss = self._build_loss_matrix_vectorized_updated(adjoint) - else: - loss = self._build_loss_matrix_vectorized(adjoint) - if self._updated: - prod = self._build_prod_matrix_vectorized_updated(adjoint) - else: - prod = self._build_prod_matrix_vectorized(adjoint) + loss = self._build_loss_matrix_vectorized(adjoint) + prod = self._build_prod_matrix_vectorized(adjoint) else: loss = self._build_loss_matrix(adjoint) prod = self._build_prod_matrix(adjoint) @@ -1810,7 +1775,7 @@ class CMFDRun(object): return loss, prod - def _build_loss_matrix_vectorized_updated(self, adjoint): + def _build_loss_matrix_vectorized(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -1923,7 +1888,7 @@ class CMFDRun(object): loss = sparse.csr_matrix((data, (self._loss_row, self._loss_col)), shape=(n, n)) return loss - def _build_prod_matrix_vectorized_updated(self, adjoint): + def _build_prod_matrix_vectorized(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -2605,7 +2570,7 @@ class CMFDRun(object): self._prod_row = row self._prod_col = col - def _compute_dtilde_vectorized_updated(self): + def _compute_dtilde_vectorized(self): # TODO: Update comment # Logical for determining whether a zero flux "albedo" b.c. should be @@ -2859,7 +2824,7 @@ class CMFDRun(object): (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) - def _compute_dhat_vectorized_updated(self): + def _compute_dhat_vectorized(self): #TODO update comment # Define current in each direction current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] @@ -3311,259 +3276,6 @@ class CMFDRun(object): return prod - def _build_loss_matrix_vectorized(self, adjoint): - """Creates matrix representing loss of neutrons. Since the end shape - of the loss matrix is known a priori, this method uses a - vectorized numpy approach to define all rows, columns, and data, which - is then used to initialize the loss matrix in CSR format. - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD loss matrix - - """ - # Extract spatial and energy indices and define matrix dimension - ng = self._indices[3] - n = self._mat_dim*ng - - # Define rows, columns, and data used to build csr matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - data = np.array([]) - - # Define net leakage coefficient for each surface in each matrix element - jnet = ((1.0 * self._dtilde[:,:,:,:,1] + self._dhat[:,:,:,:,1]) - \ - (-1.0 * self._dtilde[:,:,:,:,0] + self._dhat[:,:,:,:,0])) / \ - self._hxyz[:,:,:,np.newaxis,0] + \ - ((1.0 * self._dtilde[:,:,:,:,3] + self._dhat[:,:,:,:,3]) - \ - (-1.0 * self._dtilde[:,:,:,:,2] + self._dhat[:,:,:,:,2])) / \ - self._hxyz[:,:,:,np.newaxis,1] + \ - ((1.0 * self._dtilde[:,:,:,:,5] + self._dhat[:,:,:,:,5]) - \ - (-1.0 * self._dtilde[:,:,:,:,4] + self._dhat[:,:,:,:,4])) / \ - self._hxyz[:,:,:,np.newaxis,2] - - # Shift coremap in all directions to determine whether leakage term - # should be defined for particular cell in matrix - coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] - - coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] - - coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] - - coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] - - coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] - - coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] - - for g in range(ng): - # Define leakage terms that relate terms to their neighbors to the - # left - - # Extract all regions where a cell and its neighbor to the left - # are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_left != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_left[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,0] - - self._dhat[:,:,:,g,0])[condition] / \ - self._hxyz[:,:,:,0][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # right - - # Extract all regions where a cell and its neighbor to the right - # are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_right != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_right[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,1] + - self._dhat[:,:,:,g,1])[condition] / \ - self._hxyz[:,:,:,0][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - - # Define leakage terms that relate terms to their neighbors in the - # back - - # Extract all regions where a cell and its neighbor in the back - # are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_back != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_back[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,2] - - self._dhat[:,:,:,g,2])[condition] / \ - self._hxyz[:,:,:,1][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors in the - # front - - # Extract all regions where a cell and its neighbor in the front - # are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_front != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_front[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,3] + - self._dhat[:,:,:,g,3])[condition] / \ - self._hxyz[:,:,:,1][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # bottom - - # Extract all regions where a cell and its neighbor to the bottom - # are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_bottom != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_bottom[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,4] - - self._dhat[:,:,:,g,4])[condition] / \ - self._hxyz[:,:,:,2][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define leakage terms that relate terms to their neighbors to the - # top - - # Extract all regions where a cell and its neighbor to the - # top are both fuel regions - condition = np.logical_and(self._coremap != _CMFD_NOACCEL, - coremap_shift_top != _CMFD_NOACCEL) - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (coremap_shift_top[condition]) + g - # Compute leakage term associated with these regions - vals = (-1.0 * self._dtilde[:,:,:,g,5] + - self._dhat[:,:,:,g,5])[condition] / \ - self._hxyz[:,:,:,2][condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define terms that relate to loss of neutrons in a cell. These - # correspond to all the diagonal entries of the loss matrix - - # Extract all regions where a cell is a fuel region - condition = self._coremap != _CMFD_NOACCEL - idx_x = ng * (self._coremap[condition]) + g - idx_y = idx_x - vals = (jnet[:,:,:,g] + self._totalxs[:,:,:,g] - \ - self._scattxs[:,:,:,g,g])[condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Define terms that relate to in-scattering from group to group. - # These terms relate a mesh index to all mesh indices with the same - # spatial dimensions but belong to a different energy group - for h in range(ng): - if h != g: - # Extract all regions where a cell is a fuel region - condition = self._coremap != _CMFD_NOACCEL - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (self._coremap[condition]) + h - # Get scattering macro xs, transposed - if adjoint: - vals = (-1.0 * self._scattxs[:, :, :, g, h])[condition] - # Get scattering macro xs - else: - vals = (-1.0 * self._scattxs[:, :, :, h, g])[condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Create csr matrix - loss = sparse.csr_matrix((data, (row, col)), shape=(n, n)) - return loss - - def _build_prod_matrix_vectorized(self, adjoint): - """Creates matrix representing production of neutrons. Since the end shape - of the production matrix is known a priori, this method uses a - vectorized numpy approach to define all rows, columns, and data, which - is then used to initialize the loss matrix in CSR format. - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - prod : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - """ - # Extract spatial and energy indices and define matrix dimension - ng = self._indices[3] - n = self._mat_dim*ng - - # Define rows, columns, and data used to build csr matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) - data = np.array([]) - - # Define terms that relate to fission production from group to group. - for g in range(ng): - for h in range(ng): - # Extract all regions where a cell is a fuel region - condition = self._coremap != _CMFD_NOACCEL - idx_x = ng * (self._coremap[condition]) + g - idx_y = ng * (self._coremap[condition]) + h - # Get nu-fission macro xs, transposed - if adjoint: - vals = (self._nfissxs[:, :, :, g, h])[condition] - # Get nu-fission macro xs - else: - vals = (self._nfissxs[:, :, :, h, g])[condition] - # Store rows, cols, and data to add to CSR matrix - row = np.append(row, idx_x) - col = np.append(col, idx_y) - data = np.append(data, vals) - - # Create csr matrix - prod = sparse.csr_matrix((data, (row, col)), shape=(n, n)) - return prod - def _matrix_to_indices(self, irow, nx, ny, nz, ng): """Converts matrix index in CMFD matrices to spatial and group indices of actual problem, based on values from coremap @@ -3628,243 +3340,6 @@ class CMFDRun(object): matidx = ng*(self._coremap[i,j,k]) + g return matidx - def _compute_dtilde_vectorized(self): - """Computes the diffusion coupling coefficient using a vectorized numpy - approach. Aggregate values for the dtilde multidimensional array are - populated by first defining values on the problem boundary, and then for - all other regions. For indices not lying on a boundary, dtilde values - are distinguished between regions that neighbor a reflector region and - regions that don't neighbor a reflector - - """ - # Logical for determining whether region of interest is accelerated region - is_accel = self._coremap != _CMFD_NOACCEL - # Logical for determining whether a zero flux "albedo" b.c. should be - # applied - is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT - - # Define dtilde at left surface for all mesh cells on left boundary - self._dtilde[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], - np.where(is_zero_flux_alb[0], 2.0 * self._diffcof[0,:,:,:] / \ - self._hxyz[0,:,:,np.newaxis,0], - (2.0 * self._diffcof[0,:,:,:] * \ - (1.0 - self._albedo[0])) / \ - (4.0 * self._diffcof[0,:,:,:] * \ - (1.0 + self._albedo[0]) + \ - (1.0 - self._albedo[0]) * \ - self._hxyz[0,:,:,np.newaxis,0])), 0) - - # Define dtilde at right surface for all mesh cells on right boundary - self._dtilde[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], - np.where(is_zero_flux_alb[1], 2.0 * self._diffcof[-1,:,:,:] / \ - self._hxyz[-1,:,:,np.newaxis,0], - (2.0 * self._diffcof[-1,:,:,:] * \ - (1.0 - self._albedo[1])) / \ - (4.0 * self._diffcof[-1,:,:,:] * \ - (1.0 + self._albedo[1]) + \ - (1.0 - self._albedo[1]) * \ - self._hxyz[-1,:,:,np.newaxis,0])), 0) - - # Define dtilde at back surface for all mesh cells on back boundary - self._dtilde[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], - np.where(is_zero_flux_alb[2], 2.0 * self._diffcof[:,0,:,:] / \ - self._hxyz[:,0,:,np.newaxis,1], - (2.0 * self._diffcof[:,0,:,:] * \ - (1.0 - self._albedo[2])) / \ - (4.0 * self._diffcof[:,0,:,:] * \ - (1.0 + self._albedo[2]) + \ - (1.0 - self._albedo[2]) * \ - self._hxyz[:,0,:,np.newaxis,1])), 0) - - # Define dtilde at front surface for all mesh cells on front boundary - self._dtilde[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], - np.where(is_zero_flux_alb[3], 2.0 * self._diffcof[:,-1,:,:] / \ - self._hxyz[:,-1,:,np.newaxis,1], - (2.0 * self._diffcof[:,-1,:,:] * \ - (1.0 - self._albedo[3])) / \ - (4.0 * self._diffcof[:,-1,:,:] * \ - (1.0 + self._albedo[3]) + \ - (1.0 - self._albedo[3]) * \ - self._hxyz[:,-1,:,np.newaxis,1])), 0) - - # Define dtilde at bottom surface for all mesh cells on bottom boundary - self._dtilde[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], - np.where(is_zero_flux_alb[4], 2.0 * self._diffcof[:,:,0,:] / \ - self._hxyz[:,:,0,np.newaxis,2], - (2.0 * self._diffcof[:,:,0,:] * \ - (1.0 - self._albedo[4])) / \ - (4.0 * self._diffcof[:,:,0,:] * \ - (1.0 + self._albedo[4]) + \ - (1.0 - self._albedo[4]) * \ - self._hxyz[:,:,0,np.newaxis,2])), 0) - - # Define dtilde at top surface for all mesh cells on top boundary - self._dtilde[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], - np.where(is_zero_flux_alb[5], 2.0 * self._diffcof[:,:,-1,:] / \ - self._hxyz[:,:,-1,np.newaxis,2], - (2.0 * self._diffcof[:,:,-1,:] * \ - (1.0 - self._albedo[5])) / \ - (4.0 * self._diffcof[:,:,-1,:] * \ - (1.0 + self._albedo[5]) + \ - (1.0 - self._albedo[5]) * \ - self._hxyz[:,:,-1,np.newaxis,2])), 0) - - # Define reflector albedo for all cells on the left surface, in case - # a cell borders a reflector region on the left - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_left'],:], - self._current[:,:,:,_CURRENTS['out_left'],:], - where=self._current[:,:,:,_CURRENTS['out_left'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_left'],:])) - # Logical for whether neighboring cell to the left is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to left - neig_dc = np.roll(self._diffcof, 1, axis=0) - # Cell dimensions of neighbor to left - neig_hxyz = np.roll(self._hxyz, 1, axis=0) - - # Define dtilde at left surface for all mesh cells not on left boundary - self._dtilde[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ - np.where(adj_reflector[1:,:,:,np.newaxis], - (2.0 * self._diffcof[1:,:,:,:] * \ - (1.0 - ref_albedo[1:,:,:,:])) / \ - (4.0 * self._diffcof[1:,:,:,:] * \ - (1.0 + ref_albedo[1:,:,:,:]) + \ - (1.0 - ref_albedo[1:,:,:,:]) * \ - self._hxyz[1:,:,:,np.newaxis,0]), \ - (2.0 * self._diffcof[1:,:,:,:] * neig_dc[1:,:,:,:]) / \ - (neig_hxyz[1:,:,:,np.newaxis,0] * self._diffcof[1:,:,:,:] + \ - self._hxyz[1:,:,:,np.newaxis,0] * neig_dc[1:,:,:,:])), 0.0) - - # Define reflector albedo for all cells on the right surface, in case - # a cell borders a reflector region on the right - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_right'],:], - self._current[:,:,:,_CURRENTS['out_right'],:], - where=self._current[:,:,:,_CURRENTS['out_right'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_right'],:])) - # Logical for whether neighboring cell to the right is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to right - neig_dc = np.roll(self._diffcof, -1, axis=0) - # Cell dimensions of neighbor to right - neig_hxyz = np.roll(self._hxyz, -1, axis=0) - - # Define dtilde at right surface for all mesh cells not on right boundary - self._dtilde[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ - np.where(adj_reflector[:-1,:,:,np.newaxis], - (2.0 * self._diffcof[:-1,:,:,:] * \ - (1.0 - ref_albedo[:-1,:,:,:])) / \ - (4.0 * self._diffcof[:-1,:,:,:] * \ - (1.0 + ref_albedo[:-1,:,:,:]) + \ - (1.0 - ref_albedo[:-1,:,:,:]) * \ - self._hxyz[:-1,:,:,np.newaxis,0]), - (2.0 * self._diffcof[:-1,:,:,:] * neig_dc[:-1,:,:,:]) / \ - (neig_hxyz[:-1,:,:,np.newaxis,0] * self._diffcof[:-1,:,:,:] + \ - self._hxyz[:-1,:,:,np.newaxis,0] * neig_dc[:-1,:,:,:])), 0.0) - - # Define reflector albedo for all cells on the back surface, in case - # a cell borders a reflector region on the back - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_back'],:], - self._current[:,:,:,_CURRENTS['out_back'],:], - where=self._current[:,:,:,_CURRENTS['out_back'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_back'],:])) - # Logical for whether neighboring cell to the back is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to back - neig_dc = np.roll(self._diffcof, 1, axis=1) - # Cell dimensions of neighbor to back - neig_hxyz = np.roll(self._hxyz, 1, axis=1) - - # Define dtilde at back surface for all mesh cells not on back boundary - self._dtilde[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ - np.where(adj_reflector[:,1:,:,np.newaxis], - (2.0 * self._diffcof[:,1:,:,:] * \ - (1.0 - ref_albedo[:,1:,:,:])) / \ - (4.0 * self._diffcof[:,1:,:,:] * \ - (1.0 + ref_albedo[:,1:,:,:]) + \ - (1.0 - ref_albedo[:,1:,:,:]) * \ - self._hxyz[:,1:,:,np.newaxis,1]), - (2.0 * self._diffcof[:,1:,:,:] * neig_dc[:,1:,:,:]) / \ - (neig_hxyz[:,1:,:,np.newaxis,1] * self._diffcof[:,1:,:,:] + \ - self._hxyz[:,1:,:,np.newaxis,1] * neig_dc[:,1:,:,:])), 0.0) - - # Define reflector albedo for all cells on the front surface, in case - # a cell borders a reflector region in the front - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_front'],:], - self._current[:,:,:,_CURRENTS['out_front'],:], - where=self._current[:,:,:,_CURRENTS['out_front'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_front'],:])) - # Logical for whether neighboring cell to the front is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to front - neig_dc = np.roll(self._diffcof, -1, axis=1) - # Cell dimensions of neighbor to front - neig_hxyz = np.roll(self._hxyz, -1, axis=1) - - # Define dtilde at front surface for all mesh cells not on front boundary - self._dtilde[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ - np.where(adj_reflector[:,:-1,:,np.newaxis], - (2.0 * self._diffcof[:,:-1,:,:] * \ - (1.0 - ref_albedo[:,:-1,:,:])) / \ - (4.0 * self._diffcof[:,:-1,:,:] * \ - (1.0 + ref_albedo[:,:-1,:,:]) + \ - (1.0 - ref_albedo[:,:-1,:,:]) * \ - self._hxyz[:,:-1,:,np.newaxis,1]), - (2.0 * self._diffcof[:,:-1,:,:] * neig_dc[:,:-1,:,:]) / \ - (neig_hxyz[:,:-1,:,np.newaxis,1] * self._diffcof[:,:-1,:,:] + \ - self._hxyz[:,:-1,:,np.newaxis,1] * neig_dc[:,:-1,:,:])), 0.0) - - # Define reflector albedo for all cells on the bottom surface, in case - # a cell borders a reflector region on the bottom - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_bottom'],:], - self._current[:,:,:,_CURRENTS['out_bottom'],:], - where=self._current[:,:,:,_CURRENTS['out_bottom'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_bottom'],:])) - # Logical for whether neighboring cell to the bottom is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to bottom - neig_dc = np.roll(self._diffcof, 1, axis=2) - # Cell dimensions of neighbor to bottom - neig_hxyz = np.roll(self._hxyz, 1, axis=2) - - # Define dtilde at bottom surface for all mesh cells not on bottom boundary - self._dtilde[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ - np.where(adj_reflector[:,:,1:,np.newaxis], - (2.0 * self._diffcof[:,:,1:,:] * \ - (1.0 - ref_albedo[:,:,1:,:])) / \ - (4.0 * self._diffcof[:,:,1:,:] * \ - (1.0 + ref_albedo[:,:,1:,:]) + \ - (1.0 - ref_albedo[:,:,1:,:]) * \ - self._hxyz[:,:,1:,np.newaxis,2]), - (2.0 * self._diffcof[:,:,1:,:] * neig_dc[:,:,1:,:]) / \ - (neig_hxyz[:,:,1:,np.newaxis,2] * self._diffcof[:,:,1:,:] + \ - self._hxyz[:,:,1:,np.newaxis,2] * neig_dc[:,:,1:,:])), 0.0) - - # Define reflector albedo for all cells on the top surface, in case - # a cell borders a reflector region on the top - ref_albedo = np.divide(self._current[:,:,:,_CURRENTS['in_top'],:], - self._current[:,:,:,_CURRENTS['out_top'],:], - where=self._current[:,:,:,_CURRENTS['out_top'],:] > 1.0e-10, - out=np.ones_like(self._current[:,:,:,_CURRENTS['out_top'],:])) - # Logical for whether neighboring cell to the top is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - # Diffusion coefficient of neighbor to top - neig_dc = np.roll(self._diffcof, -1, axis=2) - # Cell dimensions of neighbor to top - neig_hxyz = np.roll(self._hxyz, -1, axis=2) - - # Define dtilde at top surface for all mesh cells not on top boundary - self._dtilde[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ - np.where(adj_reflector[:,:,:-1,np.newaxis], - (2.0 * self._diffcof[:,:,:-1,:] * \ - (1.0 - ref_albedo[:,:,:-1,:])) / \ - (4.0 * self._diffcof[:,:,:-1,:] * \ - (1.0 + ref_albedo[:,:,:-1,:]) + \ - (1.0 - ref_albedo[:,:,:-1,:]) * \ - self._hxyz[:,:,:-1,np.newaxis,2]), - (2.0 * self._diffcof[:,:,:-1,:] * neig_dc[:,:,:-1,:]) / \ - (neig_hxyz[:,:,:-1,np.newaxis,2] * self._diffcof[:,:,:-1,:] + \ - self._hxyz[:,:,:-1,np.newaxis,2] * neig_dc[:,:,:-1,:])), 0.0) - def _compute_dtilde(self): """Computes the diffusion coupling coefficient by looping over all spatial regions and energy groups @@ -3941,155 +3416,6 @@ class CMFDRun(object): # Record dtilde self._dtilde[i, j, k, g, l] = dtilde - def _compute_dhat_vectorized(self): - """Computes the nonlinear coupling coefficient using a vectorized numpy - approach. Aggregate values for the dhat multidimensional array are - populated by first defining values on the problem boundary, and then for - all other regions. For indices not lying by a boundary, dhat values - are distinguished between regions that neighbor a reflector region and - regions that don't neighbor a reflector - - """ - # Define net current on each face, divided by surface area - net_current_left = ((self._current[:,:,:,_CURRENTS['in_left'],:] - \ - self._current[:,:,:,_CURRENTS['out_left'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,0]) - net_current_right = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ - self._current[:,:,:,_CURRENTS['in_right'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,0]) - net_current_back = ((self._current[:,:,:,_CURRENTS['in_back'],:] - \ - self._current[:,:,:,_CURRENTS['out_back'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,1]) - net_current_front = ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ - self._current[:,:,:,_CURRENTS['in_front'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,1]) - net_current_bottom = ((self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ - self._current[:,:,:,_CURRENTS['out_bottom'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,2]) - net_current_top = ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ - self._current[:,:,:,_CURRENTS['in_top'],:]) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] * \ - self._hxyz[:,:,:,np.newaxis,2]) - - # Define flux in each cell - cell_flux = self._flux / np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Extract indices of coremap that are accelerated - is_accel = self._coremap != _CMFD_NOACCEL - - # Define dhat at left surface for all mesh cells on left boundary - self._dhat[0,:,:,:,0] = np.where(is_accel[0,:,:,np.newaxis], - (net_current_left[0,:,:,:] + self._dtilde[0,:,:,:,0] * \ - cell_flux[0,:,:,:]) / cell_flux[0,:,:,:], 0) - # Define dhat at right surface for all mesh cells on right boundary - self._dhat[-1,:,:,:,1] = np.where(is_accel[-1,:,:,np.newaxis], - (net_current_right[-1,:,:,:] - self._dtilde[-1,:,:,:,1] * \ - cell_flux[-1,:,:,:]) / cell_flux[-1,:,:,:], 0) - # Define dhat at back surface for all mesh cells on back boundary - self._dhat[:,0,:,:,2] = np.where(is_accel[:,0,:,np.newaxis], - (net_current_back[:,0,:,:] + self._dtilde[:,0,:,:,2] * \ - cell_flux[:,0,:,:]) / cell_flux[:,0,:,:], 0) - # Define dhat at front surface for all mesh cells on front boundary - self._dhat[:,-1,:,:,3] = np.where(is_accel[:,-1,:,np.newaxis], - (net_current_front[:,-1,:,:] - self._dtilde[:,-1,:,:,3] * \ - cell_flux[:,-1,:,:]) / cell_flux[:,-1,:,:], 0) - # Define dhat at bottom surface for all mesh cells on bottom boundary - self._dhat[:,:,0,:,4] = np.where(is_accel[:,:,0,np.newaxis], - (net_current_bottom[:,:,0,:] + self._dtilde[:,:,0,:,4] * \ - cell_flux[:,:,0,:]) / cell_flux[:,:,0,:], 0) - # Define dhat at top surface for all mesh cells on top boundary - self._dhat[:,:,-1,:,5] = np.where(is_accel[:,:,-1,np.newaxis], - (net_current_top[:,:,-1,:] - self._dtilde[:,:,-1,:,5] * \ - cell_flux[:,:,-1,:]) / cell_flux[:,:,-1,:], 0) - - # Logical for whether neighboring cell to the left is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - # Cell flux of neighbor to left - neig_flux = np.roll(self._flux, 1, axis=0) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at left surface for all mesh cells not on left boundary - self._dhat[1:,:,:,:,0] = np.where(is_accel[1:,:,:,np.newaxis], \ - np.where(adj_reflector[1:,:,:,np.newaxis], - (net_current_left[1:,:,:,:] + self._dtilde[1:,:,:,:,0] * \ - cell_flux[1:,:,:,:]) / cell_flux[1:,:,:,:], - (net_current_left[1:,:,:,:] - self._dtilde[1:,:,:,:,0] * \ - (neig_flux[1:,:,:,:] - cell_flux[1:,:,:,:])) / \ - (neig_flux[1:,:,:,:] + cell_flux[1:,:,:,:])), 0.0) - - # Logical for whether neighboring cell to the right is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - # Cell flux of neighbor to right - neig_flux = np.roll(self._flux, -1, axis=0) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at right surface for all mesh cells not on right boundary - self._dhat[:-1,:,:,:,1] = np.where(is_accel[:-1,:,:,np.newaxis], \ - np.where(adj_reflector[:-1,:,:,np.newaxis], - (net_current_right[:-1,:,:,:] - self._dtilde[:-1,:,:,:,1] * \ - cell_flux[:-1,:,:,:]) / cell_flux[:-1,:,:,:], - (net_current_right[:-1,:,:,:] + self._dtilde[:-1,:,:,:,1] * \ - (neig_flux[:-1,:,:,:] - cell_flux[:-1,:,:,:])) / \ - (neig_flux[:-1,:,:,:] + cell_flux[:-1,:,:,:])), 0.0) - - # Logical for whether neighboring cell to the back is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - # Cell flux of neighbor to back - neig_flux = np.roll(self._flux, 1, axis=1) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at back surface for all mesh cells not on back boundary - self._dhat[:,1:,:,:,2] = np.where(is_accel[:,1:,:,np.newaxis], \ - np.where(adj_reflector[:,1:,:,np.newaxis], - (net_current_back[:,1:,:,:] + self._dtilde[:,1:,:,:,2] * \ - cell_flux[:,1:,:,:]) / cell_flux[:,1:,:,:], - (net_current_back[:,1:,:,:] - self._dtilde[:,1:,:,:,2] * \ - (neig_flux[:,1:,:,:] - cell_flux[:,1:,:,:])) / \ - (neig_flux[:,1:,:,:] + cell_flux[:,1:,:,:])), 0.0) - - # Logical for whether neighboring cell to the front is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - # Cell flux of neighbor to front - neig_flux = np.roll(self._flux, -1, axis=1) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at front surface for all mesh cells not on front boundary - self._dhat[:,:-1,:,:,3] = np.where(is_accel[:,:-1,:,np.newaxis], \ - np.where(adj_reflector[:,:-1,:,np.newaxis], - (net_current_front[:,:-1,:,:] - self._dtilde[:,:-1,:,:,3] * \ - cell_flux[:,:-1,:,:]) / cell_flux[:,:-1,:,:], - (net_current_front[:,:-1,:,:] + self._dtilde[:,:-1,:,:,3] * \ - (neig_flux[:,:-1,:,:] - cell_flux[:,:-1,:,:])) / \ - (neig_flux[:,:-1,:,:] + cell_flux[:,:-1,:,:])), 0.0) - - # Logical for whether neighboring cell to the bottom is reflector region - adj_reflector = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - # Cell flux of neighbor to bottom - neig_flux = np.roll(self._flux, 1, axis=2) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at bottom surface for all mesh cells not on bottom boundary - self._dhat[:,:,1:,:,4] = np.where(is_accel[:,:,1:,np.newaxis], \ - np.where(adj_reflector[:,:,1:,np.newaxis], - (net_current_bottom[:,:,1:,:] + self._dtilde[:,:,1:,:,4] * \ - cell_flux[:,:,1:,:]) / cell_flux[:,:,1:,:], - (net_current_bottom[:,:,1:,:] - self._dtilde[:,:,1:,:,4] * \ - (neig_flux[:,:,1:,:] - cell_flux[:,:,1:,:])) / \ - (neig_flux[:,:,1:,:] + cell_flux[:,:,1:,:])), 0.0) - - # Logical for whether neighboring cell to the top is reflector region - adj_reflector = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - # Cell flux of neighbor to top - neig_flux = np.roll(self._flux, -1, axis=2) / \ - np.prod(self._hxyz, axis=3)[:,:,:,np.newaxis] - # Define dhat at top surface for all mesh cells not on top boundary - self._dhat[:,:,:-1,:,5] = np.where(is_accel[:,:,:-1,np.newaxis], \ - np.where(adj_reflector[:,:,:-1,np.newaxis], - (net_current_top[:,:,:-1,:] - self._dtilde[:,:,:-1,:,5] * \ - cell_flux[:,:,:-1,:]) / cell_flux[:,:,:-1,:], - (net_current_top[:,:,:-1,:] + self._dtilde[:,:,:-1,:,5] * \ - (neig_flux[:,:,:-1,:] - cell_flux[:,:,:-1,:])) / \ - (neig_flux[:,:,:-1,:] + cell_flux[:,:,:-1,:])), 0.0) - def _compute_dhat(self): """Computes the nonlinear coupling coefficient by looping over all spatial regions and energy groups @@ -4202,6 +3528,4 @@ class CMFDRun(object): if current[2*l] < 1.0e-10: return 1.0 else: - return current[2*l+1]/current[2*l] - - + return current[2*l+1]/current[2*l] \ No newline at end of file From cb932673c513950c4051e565b212f3e98da31184 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 2 Nov 2018 05:56:12 -0400 Subject: [PATCH 40/58] Minor changes to storing boolean array of is_adj_ref directly for each direction --- openmc/cmfd.py | 82 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 8eee2a6d32..c3bc47431c 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -2438,12 +2438,23 @@ class CMFDRun(object): # Store logical for whether neighboring cell is reflector region # in all directions - self._adj_reflector_left = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - self._adj_reflector_right = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - self._adj_reflector_back = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - self._adj_reflector_front = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - self._adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - self._adj_reflector_top = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + adj_reflector_left = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL + self._is_adj_ref_left = adj_reflector_left[self._notfirst_x_accel + (np.newaxis,)] + + adj_reflector_right = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL + self._is_adj_ref_right = adj_reflector_right[self._notlast_x_accel + (np.newaxis,)] + + adj_reflector_back = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL + self._is_adj_ref_back = adj_reflector_back[self._notfirst_y_accel + (np.newaxis,)] + + adj_reflector_front = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL + self._is_adj_ref_front = adj_reflector_front[self._notlast_y_accel + (np.newaxis,)] + + adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL + self._is_adj_ref_bottom = adj_reflector_bottom[self._notfirst_z_accel + (np.newaxis,)] + + adj_reflector_top = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL + self._is_adj_ref_top = adj_reflector_top[self._notlast_z_accel + (np.newaxis,)] def _precompute_matrix_indices(self): """Computes the indices and row/column data used to populate CMFD CSR matrices. @@ -2571,8 +2582,14 @@ class CMFDRun(object): self._prod_col = col def _compute_dtilde_vectorized(self): - # TODO: Update comment + """Computes the diffusion coupling coefficient using a vectorized numpy + approach. Aggregate values for the dtilde multidimensional array are + populated by first defining values on the problem boundary, and then for + all other regions. For indices not lying on a boundary, dtilde values + are distinguished between regions that neighbor a reflector region and + regions that don't neighbor a reflector + """ # Logical for determining whether a zero flux "albedo" b.c. should be # applied is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT @@ -2679,7 +2696,7 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] - is_adj_ref_left = self._adj_reflector_left[boundary + (np.newaxis,)] + is_adj_ref = self._is_adj_ref_left self._dtilde[boundary_grps + (0,)] = np.where(is_adj_ref_left, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) @@ -2707,8 +2724,8 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] - is_adj_ref_right = self._adj_reflector_right[boundary + (np.newaxis,)] - self._dtilde[boundary_grps + (1,)] = np.where(is_adj_ref_right, + is_adj_ref = self._is_adj_ref_right + self._dtilde[boundary_grps + (1,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) @@ -2735,8 +2752,8 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] - is_adj_ref_back = self._adj_reflector_back[boundary + (np.newaxis,)] - self._dtilde[boundary_grps + (2,)] = np.where(is_adj_ref_back, + is_adj_ref = self._is_adj_ref_back + self._dtilde[boundary_grps + (2,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) @@ -2763,8 +2780,8 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] - is_adj_ref_front = self._adj_reflector_front[boundary + (np.newaxis,)] - self._dtilde[boundary_grps + (3,)] = np.where(is_adj_ref_front, + is_adj_ref = self._is_adj_ref_front + self._dtilde[boundary_grps + (3,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) @@ -2791,7 +2808,7 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] - is_adj_ref_bottom = self._adj_reflector_bottom[boundary + (np.newaxis,)] + is_adj_ref = self._is_adj_ref_bottom self._dtilde[boundary_grps + (4,)] = np.where(is_adj_ref_bottom, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) @@ -2819,13 +2836,20 @@ class CMFDRun(object): neig_D = neig_dc[boundary_grps] neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] - is_adj_ref_top = self._adj_reflector_top[boundary + (np.newaxis,)] + is_adj_ref = self._is_adj_ref_top self._dtilde[boundary_grps + (5,)] = np.where(is_adj_ref_top, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) def _compute_dhat_vectorized(self): - #TODO update comment + """Computes the nonlinear coupling coefficient using a vectorized numpy + approach. Aggregate values for the dhat multidimensional array are + populated by first defining values on the problem boundary, and then for + all other regions. For indices not lying by a boundary, dhat values + are distinguished between regions that neighbor a reflector region and + regions that don't neighbor a reflector + + """ # Define current in each direction current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] @@ -2918,8 +2942,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (0,)] flux = cell_flux[boundary_grps] flux_left = neig_flux[boundary_grps] - is_adj_ref_left = self._adj_reflector_left[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (0,)] = np.where(is_adj_ref_left, + is_adj_ref = self._is_adj_ref_left + self._dhat[boundary_grps + (0,)] = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, (net_current - dtilde * (flux_left - flux)) / (flux_left + flux)) @@ -2935,8 +2959,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (1,)] flux = cell_flux[boundary_grps] flux_right = neig_flux[boundary_grps] - is_adj_ref_right = self._adj_reflector_right[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (1,)] = np.where(is_adj_ref_right, + is_adj_ref = self._is_adj_ref_right + self._dhat[boundary_grps + (1,)] = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, (net_current + dtilde * (flux_right - flux)) / (flux_right + flux)) @@ -2952,8 +2976,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (2,)] flux = cell_flux[boundary_grps] flux_back = neig_flux[boundary_grps] - is_adj_ref_back = self._adj_reflector_back[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (2,)] = np.where(is_adj_ref_back, + is_adj_ref = self._is_adj_ref_back + self._dhat[boundary_grps + (2,)] = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, (net_current - dtilde * (flux_back - flux)) / (flux_back + flux)) @@ -2969,8 +2993,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (3,)] flux = cell_flux[boundary_grps] flux_front = neig_flux[boundary_grps] - is_adj_ref_front = self._adj_reflector_front[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (3,)] = np.where(is_adj_ref_front, + is_adj_ref = self._is_adj_ref_front + self._dhat[boundary_grps + (3,)] = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, (net_current + dtilde * (flux_front - flux)) / (flux_front + flux)) @@ -2986,8 +3010,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (4,)] flux = cell_flux[boundary_grps] flux_bottom = neig_flux[boundary_grps] - is_adj_ref_bottom = self._adj_reflector_bottom[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (4,)] = np.where(is_adj_ref_bottom, + is_adj_ref = self._is_adj_ref_bottom + self._dhat[boundary_grps + (4,)] = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, (net_current - dtilde * (flux_bottom - flux)) / (flux_bottom + flux)) @@ -3003,8 +3027,8 @@ class CMFDRun(object): dtilde = self._dtilde[boundary_grps + (5,)] flux = cell_flux[boundary_grps] flux_top = neig_flux[boundary_grps] - is_adj_ref_top = self._adj_reflector_top[boundary + (np.newaxis,)] - self._dhat[boundary_grps + (5,)] = np.where(is_adj_ref_top, + is_adj_ref = self._is_adj_ref_top + self._dhat[boundary_grps + (5,)] = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, (net_current + dtilde * (flux_top - flux)) / (flux_top + flux)) From 70cced45bb0443cb376f1fa66d6485643d958995 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 2 Nov 2018 06:27:33 -0400 Subject: [PATCH 41/58] Some more minor fixes --- openmc/cmfd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index c3bc47431c..62f28ff6c9 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -2697,7 +2697,7 @@ class CMFDRun(object): neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_left - self._dtilde[boundary_grps + (0,)] = np.where(is_adj_ref_left, + self._dtilde[boundary_grps + (0,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) @@ -2809,7 +2809,7 @@ class CMFDRun(object): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_bottom - self._dtilde[boundary_grps + (4,)] = np.where(is_adj_ref_bottom, + self._dtilde[boundary_grps + (4,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) @@ -2837,7 +2837,7 @@ class CMFDRun(object): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_top - self._dtilde[boundary_grps + (5,)] = np.where(is_adj_ref_top, + self._dtilde[boundary_grps + (5,)] = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) From e2c2a0322095df20355e27c99cd3db46ec6e80e2 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Sat, 10 Nov 2018 12:11:42 -0500 Subject: [PATCH 42/58] Move solver to C++ --- CMakeLists.txt | 1 + include/openmc/cmfd_solver.h | 113 ++++++++++++ include/openmc/constants.h | 6 + openmc/cmfd.py | 122 +++++++++++-- src/cmfd_solver.cpp | 339 +++++++++++++++++++++++++++++++++++ 5 files changed, 563 insertions(+), 18 deletions(-) create mode 100644 include/openmc/cmfd_solver.h create mode 100644 src/cmfd_solver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fded5876c3..b5d1946d98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,6 +384,7 @@ add_library(libopenmc SHARED src/dagmc.cpp src/cell.cpp src/cmfd_execute.cpp + src/cmfd_solver.cpp src/distribution.cpp src/distribution_angle.cpp src/distribution_energy.cpp diff --git a/include/openmc/cmfd_solver.h b/include/openmc/cmfd_solver.h new file mode 100644 index 0000000000..47400b2a83 --- /dev/null +++ b/include/openmc/cmfd_solver.h @@ -0,0 +1,113 @@ +#ifndef OPENMC_CMFD_SOLVER_H +#define OPENMC_CMFD_SOLVER_H + +#include + +#include "xtensor/xtensor.hpp" + +namespace openmc { + +//=============================================================================== +// Global variables +//=============================================================================== + +// CSR format index pointer array of loss matrix +extern std::vector indptr; + +// CSR format index array of loss matrix +extern std::vector indices; + +// Dimension n of nxn CMFD loss matrix +extern int dim; + +// Spectral radius of CMFD matrices and tolerances +extern double spectral; + +// Maximum dimension in x, y, and z directions +extern int nx; +extern int ny; +extern int nz; + +// Number of energy groups +extern int ng; + +// Indexmap storing all x, y, z positions of accelerated regions +extern xt::xtensor indexmap; + +//=============================================================================== +// Non-member functions +//=============================================================================== + +//! returns the index in CSR index array corresponding to the diagonal element +//! of a specified row +//! \param[in] row of interest +//! \return index in CSR index array corresponding to diagonal element +int get_diagonal_index(int row); + +//! sets the elements of indexmap based on input coremap +//! \param[in] user-defined coremap +void set_indexmap(int* coremap); + +//! solves a one group CMFD linear system +//! \param[in] CSR format data array of coefficient matrix +//! \param[in] right hand side vector +//! \param[out] unknown vector +//! \param[in] tolerance on final error +//! \return number of inner iterations required to reach convergence +int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol); + +//! solves a two group CMFD linear system +//! \param[in] CSR format data array of coefficient matrix +//! \param[in] right hand side vector +//! \param[out] unknown vector +//! \param[in] tolerance on final error +//! \return number of inner iterations required to reach convergence +int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol); + +//! solves a general CMFD linear system +//! \param[in] CSR format data array of coefficient matrix +//! \param[in] right hand side vector +//! \param[out] unknown vector +//! \param[in] tolerance on final error +//! \return number of inner iterations required to reach convergence +int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol); + +//! converts a matrix index to spatial and group indices +//! \param[in] iteration counter over row +//! \param[out] iteration counter for groups +//! \param[out] iteration counter for x +//! \param[out] iteration counter for y +//! \param[out] iteration counter for z +void matrix_to_indices(int irow, int& g, int& i, int& j, int& k); + +//=============================================================================== +// External functions +//=============================================================================== + +//! sets the fixed variables that are used for the linear solver +//! \param[in] CSR format index pointer array of loss matrix +//! \param[in] length of indptr +//! \param[in] CSR format index array of loss matrix +//! \param[in] number of non-zero elements in CMFD loss matrix +//! \param[in] dimension n of nxn CMFD loss matrix +//! \param[in] spectral radius of CMFD matrices and tolerances +//! \param[in] indices storing spatial and energy dimensions of CMFD problem +//! \param[in] coremap for problem, storing accelerated regions +extern "C" void openmc_initialize_linsolver(int* indptr, int len_indptr, + int* indices, int n_elements, + int dim, double spectral, + int* cmfd_indices, int* map); + +//! runs a Gauss Seidel linear solver to solve CMFD matrix equations +//! linear solver +//! \param[in] CSR format data array of coefficient matrix +//! \param[in] right hand side vector +//! \param[out] unknown vector +//! \param[in] tolerance on final error +//! \return number of inner iterations required to reach convergence +extern "C" int openmc_run_linsolver(double* A_data, double* b, double* x, + double tol); + + +} // namespace openmc +#endif // OPENMC_CMFD_SOLVER_H \ No newline at end of file diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 75da02c0a6..888978c594 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -434,6 +434,12 @@ constexpr int RUN_MODE_PLOTTING {3}; constexpr int RUN_MODE_PARTICLE {4}; constexpr int RUN_MODE_VOLUME {5}; +// ============================================================================ +// CMFD CONSTANTS + +// For non-accelerated regions on coarse mesh overlay +constexpr int CMFD_NOACCEL {99999}; + } // namespace openmc #endif // OPENMC_CONSTANTS_H diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 62f28ff6c9..e3bdd6d4b6 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -18,8 +18,10 @@ import numpy as np # Line below is added to suppress warnings when using numpy.divide to # divide by numpy arrays that contain entries with zero np.seterr(divide='ignore', invalid='ignore') +import numpy.ctypeslib as npct from scipy import sparse import time +from ctypes import c_int, c_double # See if mpi4py module can be imported, define have_mpi global variable try: from mpi4py import MPI @@ -33,6 +35,20 @@ from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) from openmc.exceptions import OpenMCError + +# Define input type for numpy arrays that will be passed into C++ functions +# Must be an int or double array, with single dimension that is contiguous +array_1d_int = npct.ndpointer(dtype=np.int32, ndim=1, flags='CONTIGUOUS') +array_1d_dble = npct.ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS') + +# Setup the return types and argument types for C++ functions +openmc.capi._dll.openmc_initialize_linsolver.restype = None +openmc.capi._dll.openmc_initialize_linsolver.argtypes = [array_1d_int, c_int, + array_1d_int, c_int, c_int, c_double, array_1d_int, array_1d_int] +openmc.capi._dll.openmc_run_linsolver.restype = c_int +openmc.capi._dll.openmc_run_linsolver.argtypes = [array_1d_dble, array_1d_dble, + array_1d_dble, c_double] + """ -------------- CMFD CONSTANTS @@ -758,6 +774,9 @@ class CMFDRun(object): k_cmfd : list of floats List of CMFD k-effectives, stored for each generation that CMFD is invoked + spectral : float + Optional spectral radius that can be used to accelerate the convergence + of Gauss-Seidel iterations during CMFD power iteration. resnb : numpy.ndarray Residual from solving neutron balance equations time_cmfd : float @@ -798,9 +817,11 @@ class CMFDRun(object): self._cmfd_stol = 1.e-8 self._cmfd_reset = [] self._cmfd_write_matrices = False + self._cmfd_spectral = 0.0 + self._gauss_seidel_tolerance = [1.e-10, 1.e-5] # External variables used during runtime but users cannot control - self._indices = np.zeros(4, dtype=int) + self._indices = np.zeros(4, dtype=np.int32) self._egrid = None self._albedo = None self._coremap = None @@ -852,12 +873,12 @@ class CMFDRun(object): self._notlast_y_accel = None self._notfirst_z_accel = None self._notlast_z_accel = None - self._adj_reflector_left = None - self._adj_reflector_right = None - self._adj_reflector_back = None - self._adj_reflector_front = None - self._adj_reflector_bottom = None - self._adj_reflector_top = None + self._is_adj_ref_left = None + self._is_adj_ref_right = None + self._is_adj_ref_back = None + self._is_adj_ref_front = None + self._is_adj_ref_bottom = None + self._is_adj_ref_top = None self._accel_idxs = None self._accel_neig_left_idxs = None self._accel_neig_right_idxs = None @@ -867,6 +888,8 @@ class CMFDRun(object): self._accel_neig_top_idxs = None self._loss_row = None self._loss_col = None + self._prod_row = None + self._prod_col = None self._intracomm = None @@ -922,6 +945,10 @@ class CMFDRun(object): def cmfd_stol(self): return self._cmfd_stol + @property + def cmfd_spectral(self): + return self._cmfd_spectral + @property def cmfd_reset(self): return self._cmfd_reset @@ -930,6 +957,10 @@ class CMFDRun(object): def cmfd_write_matrices(self): return self._cmfd_write_matrices + @property + def gauss_seidel_tolerance(self): + return self._gauss_seidel_tolerance + @cmfd_begin.setter def cmfd_begin(self, cmfd_begin): check_type('CMFD begin batch', cmfd_begin, Integral) @@ -1039,6 +1070,11 @@ class CMFDRun(object): check_type('CMFD fission source tolerance', cmfd_stol, Real) self._cmfd_stol = cmfd_stol + @cmfd_spectral.setter + def cmfd_spectral(self, spectral): + check_type('CMFD spectral radius', spectral, Real) + self._cmfd_spectral = spectral + @cmfd_reset.setter def cmfd_reset(self, cmfd_reset): check_type('tally reset batches', cmfd_reset, Iterable, Integral) @@ -1049,7 +1085,14 @@ class CMFDRun(object): check_type('CMFD write matrices', cmfd_write_matrices, bool) self._cmfd_write_matrices = cmfd_write_matrices - def run(self, omp_num_threads=None, intracomm=None, vectorized=True): + @gauss_seidel_tolerance.setter + def gauss_seidel_tolerance(self, gauss_seidel_tolerance): + check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance, + Iterable, Real) + check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) + self._gauss_seidel_tolerance = gauss_seidel_tolerance + + def run(self, omp_num_threads=None, intracomm=None, vectorized=True, cpp_solver=False): """Public method to run OpenMC with CMFD This method is called by user to run CMFD once instance variables of @@ -1071,6 +1114,8 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD + self._cpp_solver = cpp_solver + # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) @@ -1090,6 +1135,10 @@ class CMFDRun(object): # Compute and store row and column indices used to build CMFD matrices self._precompute_matrix_indices() + # Initialize all variables used for linear solver in C++ + if self._cpp_solver: + self._initialize_linsolver() + # Initialize simulation openmc.capi.simulation_init() @@ -1120,6 +1169,23 @@ class CMFDRun(object): # Finalize and free memory openmc.capi.finalize() + def _initialize_linsolver(self): + # Determine number of rows in CMFD matrix + ng = self._indices[3] + n = self._mat_dim*ng + + # Create temp loss matrix to pass row/col indices to C++ linear solver + temp_data = np.ones(len(self._loss_row)) + temp_loss = sparse.csr_matrix((temp_data, (self._loss_row, self._loss_col)), + shape=(n, n)) + + # Pass coremap as 1-d array of 32-bit integers + coremap = np.swapaxes(self._coremap, 0, 2).flatten().astype(np.int32) + + return openmc.capi._dll.openmc_initialize_linsolver(temp_loss.indptr, + len(temp_loss.indptr), temp_loss.indices, len(temp_loss.indices), n, + self._cmfd_spectral, self._indices, coremap) + def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" # Display CMFD k-effective @@ -1935,6 +2001,12 @@ class CMFDRun(object): # Get problem size n = loss.shape[0] + # Set up tolerances for C++ solver + if self._cpp_solver: + atoli = self._gauss_seidel_tolerance[0] + rtoli = self._gauss_seidel_tolerance[1] + toli = rtoli * 100 + # Set up flux vectors, intital guess set to 1 phi_n = np.ones((n,)) phi_o = np.ones((n,)) @@ -1942,7 +2014,6 @@ class CMFDRun(object): # Set up source vectors s_n = np.zeros((n,)) s_o = np.zeros((n,)) - serr_v = np.zeros((n,)) # Set initial guess k_n = openmc.capi.keff_temp()[0] @@ -1975,8 +2046,13 @@ class CMFDRun(object): # Normalize source vector s_o /= k_lo - # Compute new flux vector with scipy sparse solver - phi_n = sparse.linalg.spsolve(loss, s_o) + # Compute new flux with either C++ solver or scipy sparse solver + if self._cpp_solver: + innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, + s_o, phi_n, toli) + else: + phi_n = sparse.linalg.spsolve(loss, s_o) + innerits = 0 # Compute new source vector s_n = prod.dot(phi_n) @@ -1991,7 +2067,8 @@ class CMFDRun(object): s_o *= k_lo # Check convergence - iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1) + iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1, + innerits=innerits) # If converged, calculate dominance ratio and break from loop if iconv: @@ -2004,7 +2081,11 @@ class CMFDRun(object): k_lo = k_ln norm_o = norm_n - def _check_convergence(self, s_n, s_o, k_n, k_o, iter): + # Update tolerance for inner iterations + if self._cpp_solver: + toli = max(atoli, rtoli*norm_n) + + def _check_convergence(self, s_n, s_o, k_n, k_o, iter, innerits=0): """Checks the convergence of the CMFD problem Parameters @@ -2044,14 +2125,19 @@ class CMFDRun(object): str2 = 'k-eff: {:0.8f}'.format(k_n) str3 = 'k-error: {0:.5E}'.format(kerr) str4 = 'src-error: {0:.5E}'.format(serr) - print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) + if innerits: + str5 = ' {:d}'.format(innerits) + print('{0:8s}{1:20s}{2:25s}{3:s}{4:s}'.format(str1, str2, str3, + str4, str5)) + else: + print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) sys.stdout.flush() return iconv, serr def _set_coremap(self): """Sets the core mapping information. All regions marked with zero - are set to CMFD_NO_ACCEL, while all regions marked with 1 are set to a + are set to CMFD_NOACCEL, while all regions marked with 1 are set to a unique index that maps each fuel region to a row number when building CMFD matrices @@ -2485,8 +2571,8 @@ class CMFDRun(object): mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] # Create empty row and column vectors to store for loss matrix - row = np.array([], dtype=int) - col = np.array([], dtype=int) + row = np.array([]) + col = np.array([]) # Store all indices used to populate production and loss matrix self._accel_idxs = np.where(self._coremap != _CMFD_NOACCEL) @@ -3552,4 +3638,4 @@ class CMFDRun(object): if current[2*l] < 1.0e-10: return 1.0 else: - return current[2*l+1]/current[2*l] \ No newline at end of file + return current[2*l+1]/current[2*l] diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp new file mode 100644 index 0000000000..07fdd3c3a5 --- /dev/null +++ b/src/cmfd_solver.cpp @@ -0,0 +1,339 @@ +//TODO remove +#include +#include + +#include "openmc/cmfd_solver.h" +#include "openmc/error.h" +#include "openmc/constants.h" + +namespace openmc { + +//============================================================================== +// Global variables +//============================================================================== + +// TODO check which variables actually necessary + +std::vector indptr; + +std::vector indices; + +int dim; + +double spectral; + +int nx, ny, nz, ng; + +xt::xtensor indexmap; + +//============================================================================== +// GET_DIAGONAL_INDEX returns the index in CSR index array corresponding to +// the diagonal element of a specified row +//============================================================================== + +int get_diagonal_index(int row) { + for (int j = indptr[row]; j < indptr[row+1]; j++) { + if (indices[j] == row) + return j; + } + + // Return -1 if not found + return -1; +} + +//============================================================================== +// SET_INDEXMAP sets the elements of indexmap based on input coremap +//============================================================================== + +void set_indexmap(int* coremap) { + for (int z = 0; z < nz; z++) { + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + if (coremap[(z*ny*nx) + (y*nx) + x] != CMFD_NOACCEL) { + int counter = coremap[(z*ny*nx) + (y*nx) + x]; + indexmap(counter, 0) = x; + indexmap(counter, 1) = y; + indexmap(counter, 2) = z; + } + } + } + } +} + +//============================================================================== +// CMFD_LINSOLVER_1G solves a one group CMFD linear system +//============================================================================== + +int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol) { + // Set overrelaxation parameter + double w = 1.0; + + // Perform Gauss-Seidel iterations + for (int igs = 1; igs <= 10000; igs++) { + double tmpx[dim]; + double err = 0.0; + + // Copy over x vector + std::copy(x, x+dim, tmpx); + + // Perform red/black Gauss-Seidel iterations + for (int irb = 0; irb < 2; irb++) { + + // Loop around matrix rows + for (int irow = 0; irow < dim; irow++) { + int g, i, j, k; + matrix_to_indices(irow, g, i, j, k); + + // Filter out black cells + if ((i+j+k) % 2 != irb) continue; + + // Get index of diagonal for current row + int didx = get_diagonal_index(irow); + + // Perform temporary sums, first do left of diag, then right of diag + double tmp1 = 0.0; + for (int icol = indptr[irow]; icol < didx; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = didx + 1; icol < indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + + // Solve for new x + double x1 = (b[irow] - tmp1) / A_data[didx]; + + // Perform overrelaxation + x[irow] = (1.0 - w) * x[irow] + w * x1; + + // Compute residual and update error + double res = (tmpx[irow] - x[irow]) / tmpx[irow]; + err += res * res; + } + } + + // Check convergence + err = std::sqrt(err / dim); + if (err < tol) + return igs; + + // Calculate new overrelaxation parameter + w = 1.0/(1.0 - 0.25 * spectral * w); + } + + // Throw error, as max iterations met + fatal_error("Maximum Gauss-Seidel iterations encountered."); + + // Return -1 by default, although error thrown before reaching this point + return -1; +} + +//============================================================================== +// CMFD_LINSOLVER_2G solves a two group CMFD linear system +//============================================================================== + +int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { + // Set overrelaxation parameter + double w = 1.0; + + // Perform Gauss-Seidel iterations + for (int igs = 1; igs <= 10000; igs++) { + double tmpx[dim]; + double err = 0.0; + + // Copy over x vector + std::copy(x, x+dim, tmpx); + + // Perform red/black Gauss-Seidel iterations + for (int irb = 0; irb < 2; irb++) { + + // Loop around matrix rows + for (int irow = 0; irow < dim; irow+=2) { + int g, i, j, k; + matrix_to_indices(irow, g, i, j, k); + + // Filter out black cells + if ((i+j+k) % 2 != irb) continue; + + // Get index of diagonals for current row and next row + int d1idx = get_diagonal_index(irow); + int d2idx = get_diagonal_index(irow+1); + + // Get block diagonal + double m11 = A_data[d1idx]; // group 1 diagonal + double m12 = A_data[d1idx + 1]; // group 1 right of diagonal (sorted by col) + double m21 = A_data[d2idx - 1]; // group 2 left of diagonal (sorted by col) + double m22 = A_data[d2idx]; // group 2 diagonal + + // Analytically invert the diagonal + double dm = m11*m22 - m12*m21; + double d11 = m22/dm; + double d12 = -m12/dm; + double d21 = -m21/dm; + double d22 = m11/dm; + + // Perform temporary sums, first do left of diag, then right of diag + double tmp1 = 0.0; + double tmp2 = 0.0; + for (int icol = indptr[irow]; icol < d1idx; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = indptr[irow+1]; icol < d2idx-1; icol++) + tmp2 += A_data[icol] * x[indices[icol]]; + for (int icol = d1idx + 2; icol < indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = d2idx + 1; icol < indptr[irow + 2]; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + + // Adjust with RHS vector + tmp1 = b[irow] - tmp1; + tmp2 = b[irow + 1] - tmp2; + + // Solve for new x + double x1 = d11*tmp1 + d12*tmp2; + double x2 = d21*tmp1 + d22*tmp2; + + // Perform overrelaxation + x[irow] = (1.0 - w) * x[irow] + w * x1; + x[irow + 1] = (1.0 - w) * x[irow + 1] + w * x2; + + // Compute residual and update error + double res = (tmpx[irow] - x[irow]) / tmpx[irow]; + err += res * res; + } + } + + // Check convergence + err = std::sqrt(err / dim); + if (err < tol) + return igs; + + // Calculate new overrelaxation parameter + w = 1.0/(1.0 - 0.25 * spectral * w); + } + + // Throw error, as max iterations met + fatal_error("Maximum Gauss-Seidel iterations encountered."); + + // Return -1 by default, although error thrown before reaching this point + return -1; +} + +//============================================================================== +// CMFD_LINSOLVER_NG solves a general CMFD linear system +//============================================================================== + +int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol) { + // Set overrelaxation parameter + double w = 1.0; + + // Perform Gauss-Seidel iterations + for (int igs = 1; igs <= 10000; igs++) { + double tmpx[dim]; + double err = 0.0; + + // Copy over x vector + std::copy(x, x+dim, tmpx); + + // Loop around matrix rows + for (int irow = 0; irow < dim; irow++) { + // Get index of diagonal for current row + int didx = get_diagonal_index(irow); + + // Perform temporary sums, first do left of diag, then right of diag + double tmp1 = 0.0; + for (int icol = indptr[irow]; icol < didx; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = didx + 1; icol < indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[indices[icol]]; + + // Solve for new x + double x1 = (b[irow] - tmp1) / A_data[didx]; + + // Perform overrelaxation + x[irow] = (1.0 - w) * x[irow] + w * x1; + + // Compute residual and update error + double res = (tmpx[irow] - x[irow]) / tmpx[irow]; + err += res * res; + } + + // Check convergence + err = std::sqrt(err / dim); + if (err < tol) + return igs; + + // Calculate new overrelaxation parameter + w = 1.0/(1.0 - 0.25 * spectral * w); + } + + // Throw error, as max iterations met + fatal_error("Maximum Gauss-Seidel iterations encountered."); + + // Return -1 by default, although error thrown before reaching this point + return -1; +} + +//============================================================================== +// MATRIX_TO_INDICES converts a matrix index to spatial and group +// indices +//============================================================================== + +void matrix_to_indices(int irow, int& g, int& i, int& j, int& k) { + g = irow % ng; + i = indexmap(irow/ng, 0); + j = indexmap(irow/ng, 1); + k = indexmap(irow/ng, 2); +} + +//============================================================================== +// OPENMC_INITIALIZE_LINSOLVER sets the fixed variables that are used for the +// linear solver +//============================================================================== + +extern "C" +void openmc_initialize_linsolver(int* indptr, int len_indptr, int* indices, + int n_elements, int dim, double spectral, + int* cmfd_indices, int* map) { + // Store elements of indptr + for (int i = 0; i < len_indptr; i++) + openmc::indptr.push_back(indptr[i]); + + // Store elements of indices + for (int i = 0; i < n_elements; i++) + openmc::indices.push_back(indices[i]); + + // Set dimenion of CMFD problem and specral radius + openmc::dim = dim; + openmc::spectral = spectral; + + // Set number of groups + openmc::ng = cmfd_indices[3]; + + // Set problem dimensions and indexmap if 1 or 2 group problem + if (openmc::ng == 1 || openmc::ng == 2) { + openmc::nx = cmfd_indices[0]; + openmc::ny = cmfd_indices[1]; + openmc::nz = cmfd_indices[2]; + + // Resize indexmap and set its elements + openmc::indexmap.resize({static_cast(dim), 3}); + set_indexmap(map); + } +} + +//============================================================================== +// OPENMC_RUN_LINSOLVER runs a Gauss Seidel linear solver to solve CMFD matrix +// equations +//============================================================================== + +extern "C" +int openmc_run_linsolver(double* A_data, double* b, double* x, double tol) { + switch (ng) { + case 1: + return cmfd_linsolver_1g(A_data, b, x, tol); + case 2: + return cmfd_linsolver_2g(A_data, b, x, tol); + default: + return cmfd_linsolver_ng(A_data, b, x, tol); + } +} + + +} // namespace openmc \ No newline at end of file From dbf2cb3e1a87152d01b07a88f58e570e315d3720 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Sat, 10 Nov 2018 13:58:23 -0500 Subject: [PATCH 43/58] Minor fixes to 2group solver --- src/cmfd_solver.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 07fdd3c3a5..1a2d87091e 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -1,5 +1,3 @@ -//TODO remove -#include #include #include "openmc/cmfd_solver.h" @@ -12,8 +10,6 @@ namespace openmc { // Global variables //============================================================================== -// TODO check which variables actually necessary - std::vector indptr; std::vector indices; @@ -179,7 +175,7 @@ int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { for (int icol = d1idx + 2; icol < indptr[irow + 1]; icol++) tmp1 += A_data[icol] * x[indices[icol]]; for (int icol = d2idx + 1; icol < indptr[irow + 2]; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; + tmp2 += A_data[icol] * x[indices[icol]]; // Adjust with RHS vector tmp1 = b[irow] - tmp1; From e149c68a0eb6ccbe51056603a4839bf58e9f86f9 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 12 Nov 2018 14:08:15 -0500 Subject: [PATCH 44/58] Remove unused solvers, rely solely on vectorized numpy and c++ linear solver --- openmc/cmfd.py | 592 +++++-------------------------------------------- 1 file changed, 51 insertions(+), 541 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index e3bdd6d4b6..ddb0d81bef 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1092,7 +1092,7 @@ class CMFDRun(object): check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) self._gauss_seidel_tolerance = gauss_seidel_tolerance - def run(self, omp_num_threads=None, intracomm=None, vectorized=True, cpp_solver=False): + def run(self, omp_num_threads=None, intracomm=None): """Public method to run OpenMC with CMFD This method is called by user to run CMFD once instance variables of @@ -1114,8 +1114,6 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD - self._cpp_solver = cpp_solver - # Check number of OpenMP threads is valid input and initialize C API if omp_num_threads is not None: check_type('OpenMP num threads', omp_num_threads, Integral) @@ -1136,8 +1134,7 @@ class CMFDRun(object): self._precompute_matrix_indices() # Initialize all variables used for linear solver in C++ - if self._cpp_solver: - self._initialize_linsolver() + self._initialize_linsolver() # Initialize simulation openmc.capi.simulation_init() @@ -1151,7 +1148,7 @@ class CMFDRun(object): # Perform CMFD calculation if on if self._cmfd_on: - self._execute_cmfd(vectorized) + self._execute_cmfd() # Write CMFD output if CMFD on for current batch if openmc.capi.master(): @@ -1335,7 +1332,7 @@ class CMFDRun(object): if self._n_cmfd_resets > 0 and current_batch in self._cmfd_reset: self._cmfd_tally_reset() - def _execute_cmfd(self, vectorized): + def _execute_cmfd(self): """Runs CMFD calculation on master node""" # Run CMFD on single processor on master if openmc.capi.master(): @@ -1343,10 +1340,10 @@ class CMFDRun(object): time_start_cmfd = time.time() # Create CMFD data from OpenMC tallies - self._set_up_cmfd(vectorized=vectorized) + self._set_up_cmfd() # Call solver - self._cmfd_solver_execute(vectorized=vectorized) + self._cmfd_solver_execute() # Store k-effective self._k_cmfd.append(self._keff) @@ -1357,7 +1354,7 @@ class CMFDRun(object): self._cmfd_solver_execute(adjoint=True) # Calculate fission source - self._calc_fission_source(vectorized=vectorized) + self._calc_fission_source() # Calculate weight factors self._cmfd_reweight(True) @@ -1379,15 +1376,9 @@ class CMFDRun(object): for tally_id in self._cmfd_tally_ids: tallies[tally_id].reset() - def _set_up_cmfd(self, vectorized=True): + def _set_up_cmfd(self): """Configures CMFD object for a CMFD eigenvalue calculation - Parameters - ---------- - vectorized : bool - Whether to compute dhat and dtilde using a vectorized numpy approach - or with traditional for loops - """ # Calculate all cross sections based on reaction rates from last batch self._compute_xs() @@ -1399,27 +1390,18 @@ class CMFDRun(object): self._neutron_balance() # Calculate dtilde - if vectorized: - self._compute_dtilde_vectorized() - else: - self._compute_dtilde() + self._compute_dtilde() # Calculate dhat - if vectorized: - self._compute_dhat_vectorized() - else: - self._compute_dhat() + self._compute_dhat() - def _cmfd_solver_execute(self, adjoint=False, vectorized=True): + def _cmfd_solver_execute(self, adjoint=False): """Sets up and runs power iteration solver for CMFD Parameters ---------- adjoint : bool Whether or not to run an adjoint calculation - vectorized : bool - Whether to build CMFD matrices using a vectorized numpy approach - or with traditional for loops """ # Check for physical adjoint @@ -1429,7 +1411,7 @@ class CMFDRun(object): time_start_buildcmfd = time.time() # Build loss and production matrices - loss, prod = self._build_matrices(physical_adjoint, vectorized) + loss, prod = self._build_matrices(physical_adjoint) # Check for mathematical adjoint calculation if adjoint and self._cmfd_adjoint_type == 'math': @@ -1517,26 +1499,13 @@ class CMFDRun(object): # Save matrix in scipy format sparse.save_npz(base_filename, matrix) - def _calc_fission_source(self, vectorized=True): + def _calc_fission_source(self): """Calculates CMFD fission source from CMFD flux. If a coremap is defined, there will be a discrepancy between the spatial indices in the variables ``phi`` and ``nfissxs``, so ``phi`` needs to be mapped to the spatial indices of the cross sections. This can be done in a vectorized numpy manner or with for loops - Parameters - ---------- - vectorized : bool - Whether to compute CMFD fission source in a vectorized manner or by - looping over all spatial regions and energy groups. This distinction - is made because ``phi`` does not correspond to the same spatial - domain as ``nfissxs`` - - Returns - ------- - bool - Whether or not the other filter is a subset of this filter - """ # Extract number of groups and number of accelerated regions nx = self._indices[0] @@ -1550,53 +1519,30 @@ class CMFDRun(object): # Compute cmfd_src in a vecotorized manner by phi to the spatial indices # of the actual problem so that cmfd_flux can be multiplied by nfissxs - if vectorized: - # Calculate volume - vol = np.product(self._hxyz, axis=3) - # Reshape phi by number of groups - phi = self._phi.reshape((n, ng)) + # Calculate volume + vol = np.product(self._hxyz, axis=3) - # Extract indices of coremap that are accelerated - idx = self._accel_idxs + # Reshape phi by number of groups + phi = self._phi.reshape((n, ng)) - # Initialize CMFD flux map that maps phi to actual spatial and - # group indices of problem - cmfd_flux = np.zeros((nx, ny, nz, ng)) + # Extract indices of coremap that are accelerated + idx = self._accel_idxs - # Loop over all groups and set CMFD flux based on indices of - # coremap and values of phi - for g in range(ng): - phi_g = phi[:,g] - cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] + # Initialize CMFD flux map that maps phi to actual spatial and + # group indices of problem + cmfd_flux = np.zeros((nx, ny, nz, ng)) - # Compute fission source - cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ - cmfd_flux[:,:,:,:,np.newaxis], axis=3) * \ - vol[:,:,:,np.newaxis] + # Loop over all groups and set CMFD flux based on indices of + # coremap and values of phi + for g in range(ng): + phi_g = phi[:,g] + cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] - # Otherwise compute cmfd_src by looping over all groups and spatial - # regions - else: - cmfd_src = np.zeros((nx, ny, nz, ng)) - for k in range(nz): - for j in range(ny): - for i in range(nx): - for g in range(ng): - # Cycle through if non-accelerated region - if self._coremap[i,j,k] == _CMFD_NOACCEL: - continue - - # Calculate volume - vol = np.product(self._hxyz[i,j,k]) - - # Get index in matrix - idx = self._indices_to_matrix(i, j, k, 0, ng) - - # Compute fission source - cmfd_src[i,j,k,g] = np.sum( - self._nfissxs[i,j,k,:,g] \ - * self._phi[idx:idx+ng]) * vol + # Compute fission source + cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ + cmfd_flux[:,:,:,:,np.newaxis], axis=3) * \ + vol[:,:,:,np.newaxis] # Normalize source such that it sums to 1.0 self._cmfd_src = cmfd_src / np.sum(cmfd_src) @@ -1773,16 +1719,13 @@ class CMFDRun(object): return sites_outside[0] - def _build_matrices(self, adjoint, vectorized): + def _build_matrices(self, adjoint): """Build loss and production matrices and write these matrices Parameters ---------- adjoint : bool Whether or not to run an adjoint calculation - vectorized : bool - Whether to build CMFD matrices using a vectorized numpy approach - or with traditional for loops Returns ------- @@ -1793,12 +1736,8 @@ class CMFDRun(object): """ # Build loss and production matrices - if vectorized: - loss = self._build_loss_matrix_vectorized(adjoint) - prod = self._build_prod_matrix_vectorized(adjoint) - else: - loss = self._build_loss_matrix(adjoint) - prod = self._build_prod_matrix(adjoint) + loss = self._build_loss_matrix(adjoint) + prod = self._build_prod_matrix(adjoint) # Write out matrices if self._cmfd_write_matrices: @@ -1841,7 +1780,7 @@ class CMFDRun(object): return loss, prod - def _build_loss_matrix_vectorized(self, adjoint): + def _build_loss_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -1954,7 +1893,7 @@ class CMFDRun(object): loss = sparse.csr_matrix((data, (self._loss_row, self._loss_col)), shape=(n, n)) return loss - def _build_prod_matrix_vectorized(self, adjoint): + def _build_prod_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] n = self._mat_dim*ng @@ -2002,10 +1941,9 @@ class CMFDRun(object): n = loss.shape[0] # Set up tolerances for C++ solver - if self._cpp_solver: - atoli = self._gauss_seidel_tolerance[0] - rtoli = self._gauss_seidel_tolerance[1] - toli = rtoli * 100 + atoli = self._gauss_seidel_tolerance[0] + rtoli = self._gauss_seidel_tolerance[1] + toli = rtoli * 100 # Set up flux vectors, intital guess set to 1 phi_n = np.ones((n,)) @@ -2047,12 +1985,8 @@ class CMFDRun(object): s_o /= k_lo # Compute new flux with either C++ solver or scipy sparse solver - if self._cpp_solver: - innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, - s_o, phi_n, toli) - else: - phi_n = sparse.linalg.spsolve(loss, s_o) - innerits = 0 + innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, + s_o, phi_n, toli) # Compute new source vector s_n = prod.dot(phi_n) @@ -2068,7 +2002,7 @@ class CMFDRun(object): # Check convergence iconv, norm_n = self._check_convergence(s_n, s_o, k_n, k_o, i+1, - innerits=innerits) + innerits) # If converged, calculate dominance ratio and break from loop if iconv: @@ -2082,10 +2016,9 @@ class CMFDRun(object): norm_o = norm_n # Update tolerance for inner iterations - if self._cpp_solver: - toli = max(atoli, rtoli*norm_n) + toli = max(atoli, rtoli*norm_n) - def _check_convergence(self, s_n, s_o, k_n, k_o, iter, innerits=0): + def _check_convergence(self, s_n, s_o, k_n, k_o, iter, innerits): """Checks the convergence of the CMFD problem Parameters @@ -2100,6 +2033,8 @@ class CMFDRun(object): K-effective from previous iteration iter: int Iteration number + innerits: int + Number of iterations required for convergence in inner GS loop Returns ------- @@ -2125,12 +2060,9 @@ class CMFDRun(object): str2 = 'k-eff: {:0.8f}'.format(k_n) str3 = 'k-error: {0:.5E}'.format(kerr) str4 = 'src-error: {0:.5E}'.format(serr) - if innerits: - str5 = ' {:d}'.format(innerits) - print('{0:8s}{1:20s}{2:25s}{3:s}{4:s}'.format(str1, str2, str3, - str4, str5)) - else: - print('{0:8s}{1:20s}{2:25s}{3:s}'.format(str1, str2, str3, str4)) + str5 = ' {:d}'.format(innerits) + print('{0:8s}{1:20s}{2:25s}{3:s}{4:s}'.format(str1, str2, str3, + str4, str5)) sys.stdout.flush() return iconv, serr @@ -2667,7 +2599,7 @@ class CMFDRun(object): self._prod_row = row self._prod_col = col - def _compute_dtilde_vectorized(self): + def _compute_dtilde(self): """Computes the diffusion coupling coefficient using a vectorized numpy approach. Aggregate values for the dtilde multidimensional array are populated by first defining values on the problem boundary, and then for @@ -2927,7 +2859,7 @@ class CMFDRun(object): (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) - def _compute_dhat_vectorized(self): + def _compute_dhat(self): """Computes the nonlinear coupling coefficient using a vectorized numpy approach. Aggregate values for the dhat multidimensional array are populated by first defining values on the problem boundary, and then for @@ -3217,425 +3149,3 @@ class CMFDRun(object): # Set all tallies to be active from beginning tally.active = True - def _build_loss_matrix(self, adjoint): - """Creates matrix representing loss of neutrons. This method uses for - loops to loop over all spatial indices and energy groups for easier - readability. Matrix is returned in CSR format by populating all entries - with a numpy array and later converting to CSR format - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD loss matrix - - """ - # Extract spatial and energy indices and define matrix dimension - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - n = self._mat_dim*ng - - # Allocate matrix - loss = np.zeros((n, n)) - - # Create single vector of these indices for boundary calculation - nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) - - # Allocate leakage coefficients in front of cell flux - jo = np.zeros((6,)) - - for irow in range(n): - # Get indices for row in matrix - i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) - - # Retrieve cell data - totxs = self._totalxs[i,j,k,g] - scattxsgg = self._scattxs[i,j,k,g,g] - dtilde = self._dtilde[i,j,k,g,:] - hxyz = self._hxyz[i,j,k,:] - dhat = self._dhat[i,j,k,g,:] - - # Create boundary vector - bound = np.repeat([i,j,k], 2) - - # Begin loop over leakages - for l in range(6): - # Define (x,y,z) and (-,+) indices - xyz_idx = int(l/2) # x=0, y=1, z=2 - dir_idx = l % 2 # -=0, +=1 - - # Calculate spatial indices of neighbor - neig_idx = [i,j,k] # Begin with i,j,k - shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 - neig_idx[xyz_idx] += shift_idx - - # Check for global boundary - if bound[l] != nxyz[xyz_idx, dir_idx]: - - # Check that neighbor is not reflector - if self._coremap[tuple(neig_idx)] != _CMFD_NOACCEL: - # Compute leakage coefficient for neighbor - jn = -1.0 * dtilde[l] + shift_idx*dhat[l] - - # Get neighbor matrix index - neig_mat_idx = self._indices_to_matrix(neig_idx[0], \ - neig_idx[1], neig_idx[2], g, ng) - # Compute value and record to bank - val = jn/hxyz[xyz_idx] - loss[irow, neig_mat_idx] = val - - # Compute leakage coefficient for target - jo[l] = shift_idx*dtilde[l] + dhat[l] - - # Calculate net leakage coefficient for target - jnet = (jo[1] - jo[0])/hxyz[0] + (jo[3] - jo[2])/hxyz[1] + \ - (jo[5] - jo[4])/hxyz[2] - - # Calculate loss of neutrons - val = jnet + totxs - scattxsgg - loss[irow, irow] = val - - # Begin loop over off diagonal in-scattering - for h in range(ng): - # Cycle though if h=g, value already banked in removal xs - if h == g: - continue - - # Get neighbor matrix index - scatt_mat_idx = self._indices_to_matrix(i,j,k, h, ng) - - # Check for adjoint - if adjoint: - # Get scattering macro xs, transposed! - scattxshg = self._scattxs[i, j, k, g, h] - else: - # Get scattering macro xs - scattxshg = self._scattxs[i, j, k, h, g] - - # Negate the scattering xs - val = -1.0*scattxshg - - # Record value in matrix - loss[irow, scatt_mat_idx] = val - - # Convert matrix to csr matrix in order to use scipy sparse solver - loss = sparse.csr_matrix(loss) - return loss - - def _build_prod_matrix(self, adjoint): - """Creates matrix representing production of neutrons. This method uses for - loops to loop over all spatial indices and energy groups for easier - readability. Matrix is returned in CSR format by populating all entries - with a numpy array and later converting to CSR format - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - """ - # Extract spatial and energy indices and define matrix dimension - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - n = self._mat_dim*ng - - # Allocate matrix - prod = np.zeros((n, n)) - - for irow in range(n): - # Get indices for row in matrix - i,j,k,g = self._matrix_to_indices(irow, nx, ny, nz, ng) - - # Check if at a reflector - if self._coremap[i,j,k] == _CMFD_NOACCEL: - continue - - # Loop around all other groups - for h in range(ng): - # Get matrix column location - hmat_idx = self._indices_to_matrix(i,j,k, h, ng) - # Check for adjoint and bank val - if adjoint: - # Get nu-fission cross section from cell, transposed! - nfissxs = self._nfissxs[i, j, k, g, h] - else: - # Get nu-fission cross section from cell - nfissxs = self._nfissxs[i, j, k, h, g] - - # Set as value to be recorded - val = nfissxs - - # record value in matrix - prod[irow, hmat_idx] = val - - # Convert matrix to csr matrix in order to use scipy sparse solver - prod = sparse.csr_matrix(prod) - - return prod - - def _matrix_to_indices(self, irow, nx, ny, nz, ng): - """Converts matrix index in CMFD matrices to spatial and group indices - of actual problem, based on values from coremap - - Parameters - ---------- - irow : int - Row in CMFD matrix - nx : int - Total number of mesh cells in problem in x direction - ny : int - Total number of mesh cells in problem in y direction - nz : int - Total number of mesh cells in problem in z direction - ng : int - Total number of energy groups in problem - - Returns - ------- - i : int - Corresponding x-index in CMFD problem - j : int - Corresponding y-index in CMFD problem - k : int - Corresponding z-index in CMFD problem - g : int - Corresponding group in CMFD problem - - """ - g = irow % ng - # Get indices from coremap - spatial_idx = np.where(self._coremap == int(irow/ng)) - i = spatial_idx[0][0] - j = spatial_idx[1][0] - k = spatial_idx[2][0] - - return i, j, k, g - - def _indices_to_matrix(self, i, j, k, g, ng): - """Takes (i,j,k,g) indices and computes location in CMFD matrix - - Parameters - ---------- - i : int - Corresponding x-index in CMFD problem - j : int - Corresponding y-index in CMFD problem - k : int - Corresponding z-index in CMFD problem - g : int - Corresponding group in CMFD problem - ng : int - Total number of energy groups in problem - - Returns - ------- - matidx : int - Row in CMFD matrix - - """ - # Get matrix index from coremap - matidx = ng*(self._coremap[i,j,k]) + g - return matidx - - def _compute_dtilde(self): - """Computes the diffusion coupling coefficient by looping over all - spatial regions and energy groups - - """ - # Get maximum of spatial and group indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Create single vector of these indices for boundary calculation - nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) - - # Get boundary condition information - albedo = self._albedo - - # Loop over group and spatial indices - for k in range(nz): - for j in range(ny): - for i in range(nx): - for g in range(ng): - # Cycle if non-accelration region - if self._coremap[i,j,k] == _CMFD_NOACCEL: - continue - - # Get cell data - cell_dc = self._diffcof[i,j,k,g] - cell_hxyz = self._hxyz[i,j,k,:] - - # Setup of vector to identify boundary conditions - bound = np.repeat([i,j,k], 2) - - # Begin loop around sides of cell for leakage - for l in range(6): - xyz_idx = int(l/2) # x=0, y=1, z=2 - dir_idx = l % 2 # -=0, +=1 - - # Check if at boundary - if bound[l] == nxyz[xyz_idx, dir_idx]: - # Compute dtilde with albedo boundary condition - dtilde = (2*cell_dc*(1-albedo[l]))/ \ - (4*cell_dc*(1+albedo[l]) + \ - (1-albedo[l])*cell_hxyz[xyz_idx]) - - # Check for zero flux albedo - if abs(albedo[l] - _ZERO_FLUX) < _TINY_BIT: - dtilde = 2*cell_dc / cell_hxyz[xyz_idx] - - else: # Not at a boundary - shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 - - # Compute neighboring cell indices - neig_idx = [i,j,k] # Begin with i,j,k - neig_idx[xyz_idx] += shift_idx - - # Get neighbor cell data - neig_dc = self._diffcof[tuple(neig_idx) + (g,)] - neig_hxyz = self._hxyz[tuple(neig_idx)] - - # Check for fuel-reflector interface - if (self._coremap[tuple(neig_idx)] == - _CMFD_NOACCEL): - # Get albedo - ref_albedo = self._get_reflector_albedo(l,g,i,j,k) - dtilde = (2*cell_dc*(1-ref_albedo))/(4*cell_dc*(1+ \ - ref_albedo)+(1-ref_albedo)*cell_hxyz[xyz_idx]) - - else: # Not next to a reflector - # Compute dtilde to neighbor cell - dtilde = (2*cell_dc*neig_dc)/(neig_hxyz[xyz_idx]*cell_dc + \ - cell_hxyz[xyz_idx]*neig_dc) - - # Record dtilde - self._dtilde[i, j, k, g, l] = dtilde - - def _compute_dhat(self): - """Computes the nonlinear coupling coefficient by looping over all - spatial regions and energy groups - - """ - # Get maximum of spatial and group indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Create single vector of these indices for boundary calculation - nxyz = np.array([[0,nx-1], [0,ny-1], [0,nz-1]]) - - # Loop over group and spatial indices - for k in range(nz): - for j in range(ny): - for i in range(nx): - for g in range(ng): - # Cycle if non-accelration region - if self._coremap[i,j,k] == _CMFD_NOACCEL: - continue - - # Get cell data - cell_dtilde = self._dtilde[i,j,k,g,:] - cell_flux = self._flux[i,j,k,g]/np.product(self._hxyz[i,j,k,:]) - current = self._current[i,j,k,:,g] - - # Setup of vector to identify boundary conditions - bound = np.repeat([i,j,k], 2) - - # Begin loop around sides of cell for leakage - for l in range(6): - xyz_idx = int(l/2) # x=0, y=1, z=2 - dir_idx = l % 2 # -=0, +=1 - shift_idx = 2*(l % 2) - 1 # shift neig by -1 or +1 - - # Calculate net current on l face (divided by surf area) - net_current = shift_idx*(current[2*l] - current[2*l+1]) / \ - np.product(self._hxyz[i,j,k,:]) * self._hxyz[i,j,k,xyz_idx] - - # Check if at boundary - if bound[l] == nxyz[xyz_idx, dir_idx]: - # Compute dhat - dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ - cell_flux - - else: # Not at a boundary - # Compute neighboring cell indices - neig_idx = [i,j,k] # Begin with i,j,k - neig_idx[xyz_idx] += shift_idx - - # Get neigbor flux - neig_flux = self._flux[tuple(neig_idx)+(g,)] / \ - np.product(self._hxyz[tuple(neig_idx)]) - - # Check for fuel-reflector interface - if (self._coremap[tuple(neig_idx)] == - _CMFD_NOACCEL): - # Compute dhat - dhat = (net_current - shift_idx*cell_dtilde[l]*cell_flux) / \ - cell_flux - - else: # not a fuel-reflector interface - # Compute dhat - dhat = (net_current + shift_idx*cell_dtilde[l]* \ - (neig_flux - cell_flux))/(neig_flux + cell_flux) - - # Record dhat - self._dhat[i, j, k, g, l] = dhat - - # check for dhat reset - if self._dhat_reset: - self._dhat[i, j, k, g, l] = 0.0 - - # Write that dhats are zero - if self._dhat_reset and openmc.capi.settings.verbosity >= 8 and \ - openmc.capi.master(): - print(' Dhats reset to zero') - sys.stdout.flush() - - def _get_reflector_albedo(self, l, g, i, j, k): - """Calculates the albedo to the reflector by returning ratio of - incoming to outgoing ratio - - Parameters - ---------- - l : int - leakage index, see _CURRENTS for map between leakage index and leakage - direction - g : int - index of energy group - i : int - index of mesh location in x-direction - j : int - index of mesh location in y-direction - k : int - index of mesh location in z-direction - - Returns - ------- - float - reflector albedo - - """ - # Get partial currents from object - current = self._current[i,j,k,:,g] - - # Calculate albedo - if current[2*l] < 1.0e-10: - return 1.0 - else: - return current[2*l+1]/current[2*l] From ab2f3bfc75c65a969f9941616521cfa40558f6ed Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 12 Nov 2018 17:44:57 -0500 Subject: [PATCH 45/58] Remove keff_temp definition --- openmc/capi/core.py | 17 ----------------- openmc/cmfd.py | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index f429f23b2d..5be31ce865 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -226,23 +226,6 @@ def keff(): return (mean, std_dev) -# TODO Remove -def keff_temp(): - """Return the calculated tracklength k-eigenvalue and its standard deviation. - - Returns - ------- - tuple - Mean k-eigenvalue and standard deviation of the mean - - """ - n = openmc.capi.num_realizations() - mean = c_double.in_dll(_dll, 'keff').value - std_dev = c_double.in_dll(_dll, 'keff_std').value \ - if n > 1 else np.inf - return (mean, std_dev) - - def master(): """Return whether processor is master processor or not. diff --git a/openmc/cmfd.py b/openmc/cmfd.py index ddb0d81bef..2c0a3eca2b 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1954,7 +1954,7 @@ class CMFDRun(object): s_o = np.zeros((n,)) # Set initial guess - k_n = openmc.capi.keff_temp()[0] + k_n = openmc.capi.keff()[0] k_o = k_n dw = self._cmfd_shift k_s = k_o + dw @@ -2306,7 +2306,7 @@ class CMFDRun(object): num_accel = self._mat_dim # Get openmc k-effective - keff = openmc.capi.keff_temp()[0] + keff = openmc.capi.keff()[0] # Define leakage in each mesh cell and energy group leakage = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ From ee9a6ab065faff63150fca6d9aa3078ae8e57b62 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Sat, 24 Nov 2018 00:13:37 -0500 Subject: [PATCH 46/58] So begins the CMFD Fortran purge :D (~3.6K lines) --- CMakeLists.txt | 8 - docs/source/io_formats/cmfd.rst | 221 ------ docs/source/io_formats/index.rst | 1 - docs/source/io_formats/settings.rst | 10 - docs/source/io_formats/statepoint.rst | 20 - docs/source/pythonapi/base.rst | 63 +- docs/source/usersguide/basics.rst | 4 - openmc/cmfd.py | 515 +++----------- openmc/model/model.py | 18 +- openmc/settings.py | 18 - schemas.xml | 1 - src/api.F90 | 4 - src/cmfd_data.F90 | 943 -------------------------- src/cmfd_execute.F90 | 408 ----------- src/cmfd_execute.cpp | 43 -- src/cmfd_header.F90 | 272 -------- src/cmfd_input.F90 | 495 -------------- src/cmfd_loss_operator.F90 | 435 ------------ src/cmfd_prod_operator.F90 | 199 ------ src/cmfd_solver.F90 | 809 ---------------------- src/constants.F90 | 12 - src/input_xml.F90 | 4 - src/output.F90 | 44 -- src/relaxng/cmfd.rnc | 53 -- src/relaxng/cmfd.rng | 215 ------ src/relaxng/settings.rng | 5 - src/settings.F90 | 3 - src/simulation.cpp | 15 - src/state_point.F90 | 48 +- 29 files changed, 148 insertions(+), 4738 deletions(-) delete mode 100644 docs/source/io_formats/cmfd.rst delete mode 100644 src/cmfd_data.F90 delete mode 100644 src/cmfd_execute.F90 delete mode 100644 src/cmfd_execute.cpp delete mode 100644 src/cmfd_header.F90 delete mode 100644 src/cmfd_input.F90 delete mode 100644 src/cmfd_loss_operator.F90 delete mode 100644 src/cmfd_prod_operator.F90 delete mode 100644 src/cmfd_solver.F90 delete mode 100644 src/relaxng/cmfd.rnc delete mode 100644 src/relaxng/cmfd.rng diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fc6fcec64..c9bb9f3c80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,13 +309,6 @@ add_library(libopenmc SHARED src/bank_header.F90 src/api.F90 src/dagmc_header.F90 - src/cmfd_data.F90 - src/cmfd_execute.F90 - src/cmfd_header.F90 - src/cmfd_input.F90 - src/cmfd_loss_operator.F90 - src/cmfd_prod_operator.F90 - src/cmfd_solver.F90 src/constants.F90 src/dict_header.F90 src/eigenvalue.F90 @@ -383,7 +376,6 @@ add_library(libopenmc SHARED src/tallies/trigger_header.F90 src/dagmc.cpp src/cell.cpp - src/cmfd_execute.cpp src/cmfd_solver.cpp src/cross_sections.cpp src/distribution.cpp diff --git a/docs/source/io_formats/cmfd.rst b/docs/source/io_formats/cmfd.rst deleted file mode 100644 index 80e8365c79..0000000000 --- a/docs/source/io_formats/cmfd.rst +++ /dev/null @@ -1,221 +0,0 @@ -.. _io_cmfd: - -============================== -CMFD Specification -- cmfd.xml -============================== - -Coarse mesh finite difference acceleration method has been implemented in -OpenMC. Currently, it allows users to accelerate fission source convergence -during inactive neutron batches. To run CMFD, the ```` element in -``settings.xml`` should be set to "true". - -------------------- -```` Element -------------------- - -The ```` element controls what batch CMFD calculations should begin. - - *Default*: 1 - ------------------------- -```` Element ------------------------- - -The ```` element controls whether :math:`\widehat{D}` nonlinear -CMFD parameters should be reset to zero before solving CMFD eigenproblem. -It can be turned on with "true" and off with "false". - - *Default*: false - ---------------------- -```` Element ---------------------- - -The ```` element sets one additional CMFD output column. Options are: - -* "balance" - prints the RMS [%] of the resdiual from the neutron balance - equation on CMFD tallies. -* "dominance" - prints the estimated dominance ratio from the CMFD iterations. - **This will only work for power iteration eigensolver**. -* "entropy" - prints the *entropy* of the CMFD predicted fission source. - **Can only be used if OpenMC entropy is active as well**. -* "source" - prints the RMS [%] between the OpenMC fission source and CMFD - fission source. - - *Default*: balance - -------------------------- -```` Element -------------------------- - -The ```` element controls whether an effective downscatter cross -section should be used when using 2-group CMFD. It can be turned on with "true" -and off with "false". - - *Default*: false - ----------------------- -```` Element ----------------------- - -The ```` element controls whether or not the CMFD diffusion result is -used to adjust the weight of fission source neutrons on the next OpenMC batch. -It can be turned on with "true" and off with "false". - - *Default*: false - ------------------------------------- -```` Element ------------------------------------- - -The ```` element specifies two parameters. The first is -the absolute inner tolerance for Gauss-Seidel iterations when performing CMFD -and the second is the relative inner tolerance for Gauss-Seidel iterations -for CMFD calculations. - - *Default*: 1.e-10 1.e-5 - --------------------- -```` Element --------------------- - -The ```` element specifies the tolerance on the eigenvalue when performing -CMFD power iteration. - - *Default*: 1.e-8 - ------------------- -```` Element ------------------- - -The CMFD mesh is a structured Cartesian mesh. This element has the following -attributes/sub-elements: - - :lower_left: - The lower-left corner of the structured mesh. If only two coordinates are - given, it is assumed that the mesh is an x-y mesh. - - :upper_right: - The upper-right corner of the structrued mesh. If only two coordinates are - given, it is assumed that the mesh is an x-y mesh. - - :dimension: - The number of mesh cells in each direction. - - :width: - The width of mesh cells in each direction. - - :energy: - Energy bins [in eV], listed in ascending order (e.g. 0.0 0.625 20.0e6) - for CMFD tallies and acceleration. If no energy bins are listed, OpenMC - automatically assumes a one energy group calculation over the entire - energy range. - - :albedo: - Surface ratio of incoming to outgoing partial currents on global boundary - conditions. They are listed in the following order: -x +x -y +y -z +z. - - *Default*: 1.0 1.0 1.0 1.0 1.0 1.0 - - :map: - An optional acceleration map can be specified to overlay on the coarse - mesh spatial grid. If this option is used, a ``1`` is used for a - non-accelerated region and a ``2`` is used for an accelerated region. - For a simple 4x4 coarse mesh with a 2x2 fuel lattice surrounded by - reflector, the map is: - - ``1 1 1 1`` - - ``1 2 2 1`` - - ``1 2 2 1`` - - ``1 1 1 1`` - - Therefore a 2x2 system of equations is solved rather than a 4x4. This - is extremely important to use in reflectors as neutrons will not - contribute to any tallies far away from fission source neutron regions. - A ``2`` must be used to identify any fission source region. - - .. note:: Only two of the following three sub-elements are needed: - ``lower_left``, ``upper_right`` and ``width``. Any combination - of two of these will yield the third. - ------------------- -```` Element ------------------- - -The ```` element is used to normalize the CMFD fission source distribution -to a particular value. For example, if a fission source is calculated for a -17 x 17 lattice of pins, the fission source may be normalized to the number of -fission source regions, in this case 289. This is useful when visualizing this -distribution as the average peaking factor will be unity. This parameter will -not impact the calculation. - - *Default*: 1.0 - ---------------------------- -```` Element ---------------------------- - -The ```` element is used to view the convergence of power -iteration. This option can be turned on with "true" and turned off with "false". - - *Default*: false - -------------------------- -```` Element -------------------------- - -The ```` element can be turned on with "true" to have an adjoint -calculation be performed on the last batch when CMFD is active. - - *Default*: false - --------------------- -```` Element --------------------- - -The ```` element specifies an optional Wielandt shift parameter for -accelerating power iterations. It is by default very large so the impact of the -shift is effectively zero. - - *Default*: 1e6 - ----------------------- -```` Element ----------------------- - -The ```` element specifies an optional spectral radius that can be set to -accelerate the convergence of Gauss-Seidel iterations during CMFD power iteration -solve. - - *Default*: 0.0 - ------------------- -```` Element ------------------- - -The ```` element specifies the tolerance on the fission source when performing -CMFD power iteration. - - *Default*: 1.e-8 - -------------------------- -```` Element -------------------------- - -The ```` element contains a list of batch numbers in which CMFD tallies -should be reset. - - *Default*: None - ----------------------------- -```` Element ----------------------------- - -The ```` element is used to write the sparse matrices created -when solving CMFD equations. This option can be turned on with "true" and off -with "false". - - *Default*: false diff --git a/docs/source/io_formats/index.rst b/docs/source/io_formats/index.rst index ef83bd23da..7ae22c0a33 100644 --- a/docs/source/io_formats/index.rst +++ b/docs/source/io_formats/index.rst @@ -19,7 +19,6 @@ Input Files settings tallies plots - cmfd ---------- Data Files diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index f19a9e902d..178c440801 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -361,16 +361,6 @@ or sub-elements: .. note:: This element is not used in the multi-group :ref:`energy_mode`. ----------------------- -```` Element ----------------------- - -The ```` element indicates whether or not CMFD acceleration should be -turned on or off. This element has no attributes or sub-elements and can be set -to either "false" or "true". - - *Default*: false - ---------------------- ```` Element ---------------------- diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 38dae7dd7a..9c5b8f5260 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -17,8 +17,6 @@ The current version of the statepoint file format is 17.0. - **date_and_time** (*char[]*) -- Date and time the summary was written. - **path** (*char[]*) -- Path to directory containing input files. - - **cmfd_on** (*int*) -- Flag indicating whether CMFD is on (1) or - off (0). - **tallies_present** (*int*) -- Flag indicating whether tallies are present (1) or not (0). - **source_present** (*int*) -- Flag indicating whether the source @@ -59,18 +57,6 @@ The current version of the statepoint file format is 17.0. source particle, respectively. Only present when `run_mode` is 'eigenvalue'. -**/cmfd/** - -:Datasets: - **indices** (*int[4]*) -- Indices for cmfd mesh (i,j,k,g) - - **k_cmfd** (*double[]*) -- CMFD eigenvalues - - **cmfd_src** (*double[][][][]*) -- CMFD fission source - - **cmfd_entropy** (*double[]*) -- CMFD estimate of Shannon entropy - - **cmfd_balance** (*double[]*) -- RMS of the residual neutron - balance equation on CMFD mesh - - **cmfd_dominance** (*double[]*) -- CMFD estimate of dominance ratio - - **cmfd_srccmp** (*double[]*) -- RMS comparison of difference - between OpenMC and CMFD fission source - **/tallies/** :Attributes: - **n_tallies** (*int*) -- Number of user-defined tallies. @@ -166,9 +152,3 @@ All values are given in seconds and are measured on the master process. source sites between processes for load balancing. - **accumulating tallies** (*double*) -- Time spent communicating tally results and evaluating their statistics. - - **CMFD** (*double*) -- Time spent evaluating CMFD. - - **CMFD building matrices** (*double*) -- Time spent buliding CMFD - matrices. - - **CMFD solving matrices** (*double*) -- Time spent solving CMFD - matrices. - - **total** (*double*) -- Total time spent in the program. diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 84bb3cc54d..5202ea591d 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -130,17 +130,6 @@ Constructing Tallies openmc.Tally openmc.Tallies -Coarse Mesh Finite Difference Acceleration ------------------------------------------- - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: myclass.rst - - openmc.CMFDMesh - openmc.CMFD - Geometry Plotting ----------------- @@ -183,7 +172,7 @@ The following classes and functions are used for functional expansion reconstruc .. autosummary:: :toctree: generated :nosignatures: - :template: myclass.rst + :template: myclass.rst openmc.ZernikeRadial @@ -208,3 +197,53 @@ Various classes may be created when performing tally slicing and/or arithmetic: openmc.arithmetic.AggregateScore openmc.arithmetic.AggregateNuclide openmc.arithmetic.AggregateFilter + +Coarse Mesh Finite Difference Acceleration +------------------------------------------ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + openmc.CMFDMesh + openmc.CMFDRun + +CMFD is implemented in OpenMC and allows users to accelerate fission source +convergence during inactive neutron batches. To run CMFD, the CMFDRun class should +be used. The following properties can be set through the CMFDRun class: + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.CMFDRun.cmfd_begin + openmc.CMFDRun.dhat_reset + openmc.CMFDRun.cmfd_display + openmc.CMFDRun.cmfd_downscatter + openmc.CMFDRun.cmfd_feedback + openmc.CMFDRun.cmfd_ktol + openmc.CMFDRun.cmfd_mesh + openmc.CMFDRun.norm + openmc.CMFDRun.cmfd_adjoint_type + openmc.CMFDRun.cmfd_power_monitor + openmc.CMFDRun.cmfd_run_adjoint + openmc.CMFDRun.cmfd_shift + openmc.CMFDRun.cmfd_stol + openmc.CMFDRun.cmfd_spectral + openmc.CMFDRun.cmfd_reset + openmc.CMFDRun.cmfd_write_matrices + openmc.CMFDRun.gauss_seidel_tolerance + +At the minimum, a CMFD mesh needs to be specified in order to run CMFD. Once +these properties are set, an OpenMC simulation can be run with CMFD turned on +with the function: + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.CMFDRun.run + diff --git a/docs/source/usersguide/basics.rst b/docs/source/usersguide/basics.rst index 74e65f771c..2dd91ee0fa 100644 --- a/docs/source/usersguide/basics.rst +++ b/docs/source/usersguide/basics.rst @@ -43,10 +43,6 @@ described below. This file gives specifications for producing slice or voxel plots of the geometry. - :ref:`io_cmfd` - This file specifies execution parameters for coarse mesh finite difference - (CMFD) acceleration. - eXtensible Markup Language (XML) -------------------------------- diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 2c0a3eca2b..ee9a8a1b80 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -12,7 +12,6 @@ References from collections.abc import Iterable from numbers import Real, Integral -from xml.etree import ElementTree as ET # TODO Remove import sys import numpy as np # Line below is added to suppress warnings when using numpy.divide to @@ -30,7 +29,6 @@ except ImportError: have_mpi = False import openmc.capi -from openmc._xml import clean_indentation #TODO Remove from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) from openmc.exceptions import OpenMCError @@ -242,385 +240,6 @@ class CMFDMesh(object): return element -# REMOVE this entire class -class CMFD(object): - r"""Parameters that control the use of coarse-mesh finite difference acceleration - in OpenMC. This corresponds directly to the cmfd.xml input file. - - Attributes - ---------- - begin : int - Batch number at which CMFD calculations should begin - dhat_reset : bool - Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should be - reset to zero before solving CMFD eigenproblem. - display : {'balance', 'dominance', 'entropy', 'source'} - Set one additional CMFD output column. Options are: - - * "balance" - prints the RMS [%] of the resdiual from the neutron balance - equation on CMFD tallies. - * "dominance" - prints the estimated dominance ratio from the CMFD - iterations. - * "entropy" - prints the *entropy* of the CMFD predicted fission source. - * "source" - prints the RMS [%] between the OpenMC fission source and - CMFD fission source. - downscatter : bool - Indicate whether an effective downscatter cross section should be used - when using 2-group CMFD. - feedback : bool - Indicate or not the CMFD diffusion result is used to adjust the weight - of fission source neutrons on the next OpenMC batch. Defaults to False. - gauss_seidel_tolerance : Iterable of float - Two parameters specifying the absolute inner tolerance and the relative - inner tolerance for Gauss-Seidel iterations when performing CMFD. - ktol : float - Tolerance on the eigenvalue when performing CMFD power iteration - cmfd_mesh : openmc.CMFDMesh - Structured mesh to be used for acceleration - norm : float - Normalization factor applied to the CMFD fission source distribution - power_monitor : bool - View convergence of power iteration during CMFD acceleration - run_adjoint : bool - Perform adjoint calculation on the last batch - shift : float - Optional Wielandt shift parameter for accelerating power iterations. By - default, it is very large so there is effectively no impact. - spectral : float - Optional spectral radius that can be used to accelerate the convergence - of Gauss-Seidel iterations during CMFD power iteration. - stol : float - Tolerance on the fission source when performing CMFD power iteration - tally_reset : list of int - List of batch numbers at which CMFD tallies should be reset - write_matrices : bool - Write sparse matrices that are used during CMFD acceleration (loss, - production) to file - - """ - - def __init__(self): - self._begin = None - self._dhat_reset = None - self._display = None - self._downscatter = None - self._feedback = None - self._gauss_seidel_tolerance = None - self._ktol = None - self._cmfd_mesh = None - self._norm = None - self._power_monitor = None - self._run_adjoint = None - self._shift = None - self._spectral = None - self._stol = None - self._tally_reset = None - self._write_matrices = None - - self._cmfd_file = ET.Element("cmfd") - self._cmfd_mesh_element = None - - @property - def begin(self): - return self._begin - - @property - def dhat_reset(self): - return self._dhat_reset - - @property - def display(self): - return self._display - - @property - def downscatter(self): - return self._downscatter - - @property - def feedback(self): - return self._feedback - - @property - def gauss_seidel_tolerance(self): - return self._gauss_seidel_tolerance - - @property - def ktol(self): - return self._ktol - - @property - def cmfd_mesh(self): - return self._cmfd_mesh - - @property - def norm(self): - return self._norm - - @property - def power_monitor(self): - return self._power_monitor - - @property - def run_adjoint(self): - return self._run_adjoint - - @property - def shift(self): - return self._shift - - @property - def spectral(self): - return self._spectral - - @property - def stol(self): - return self._stol - - @property - def tally_reset(self): - return self._tally_reset - - @property - def write_matrices(self): - return self._write_matrices - - @begin.setter - def begin(self, begin): - check_type('CMFD begin batch', begin, Integral) - check_greater_than('CMFD begin batch', begin, 0) - self._begin = begin - - @dhat_reset.setter - def dhat_reset(self, dhat_reset): - check_type('CMFD Dhat reset', dhat_reset, bool) - self._dhat_reset = dhat_reset - - @display.setter - def display(self, display): - check_type('CMFD display', display, str) - check_value('CMFD display', display, - ['balance', 'dominance', 'entropy', 'source']) - self._display = display - - @downscatter.setter - def downscatter(self, downscatter): - check_type('CMFD downscatter', downscatter, bool) - self._downscatter = downscatter - - @feedback.setter - def feedback(self, feedback): - check_type('CMFD feedback', feedback, bool) - self._feedback = feedback - - @gauss_seidel_tolerance.setter - def gauss_seidel_tolerance(self, gauss_seidel_tolerance): - check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance, - Iterable, Real) - check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) - self._gauss_seidel_tolerance = gauss_seidel_tolerance - - @ktol.setter - def ktol(self, ktol): - check_type('CMFD eigenvalue tolerance', ktol, Real) - self._ktol = ktol - - @cmfd_mesh.setter - def cmfd_mesh(self, mesh): - check_type('CMFD mesh', mesh, CMFDMesh) - - # Check dimension defined - if mesh.dimension is None: - raise ValueError('CMFD mesh requires spatial ' - 'dimensions to be specified') - - # Check lower left defined - if mesh.lower_left is None: - raise ValueError('CMFD mesh requires lower left coordinates ' - 'to be specified') - - # Check that both upper right and width both not defined - if mesh.upper_right is not None and mesh.width is not None: - raise ValueError('Both upper right coordinates and width ' - 'cannot be specified for CMFD mesh') - - # Check that at least one of width or upper right is defined - if mesh.upper_right is None and mesh.width is None: - raise ValueError('CMFD mesh requires either upper right ' - 'coordinates or width to be specified') - - # Check width and lower length are same dimension and define upper_right - if mesh.width is not None: - check_length('CMFD mesh width', mesh.width, len(mesh.lower_left)) - mesh.upper_right = np.array(mesh.lower_left) + \ - np.array(mesh.width) * np.array(mesh.dimension) - - # Check upper_right and lower length are same dimension and define width - elif mesh.upper_right is not None: - check_length('CMFD mesh upper right', mesh.upper_right, \ - len(mesh.lower_left)) - # Check upper right coordinates are greater than lower left - if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): - raise ValueError('CMFD mesh requires upper right ' - 'coordinates to be greater than lower ' - 'left coordinates') - mesh.width = np.true_divide( - (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ - np.array(mesh.dimension)) - self._cmfd_mesh = mesh - - @norm.setter - def norm(self, norm): - check_type('CMFD norm', norm, Real) - self._norm = norm - - @power_monitor.setter - def power_monitor(self, power_monitor): - check_type('CMFD power monitor', power_monitor, bool) - self._power_monitor = power_monitor - - @run_adjoint.setter - def run_adjoint(self, run_adjoint): - check_type('CMFD run adjoint', run_adjoint, bool) - self._run_adjoint = run_adjoint - - @shift.setter - def shift(self, shift): - check_type('CMFD Wielandt shift', shift, Real) - self._shift = shift - - @spectral.setter - def spectral(self, spectral): - check_type('CMFD spectral radius', spectral, Real) - self._spectral = spectral - - @stol.setter - def stol(self, stol): - check_type('CMFD fission source tolerance', stol, Real) - self._stol = stol - - @tally_reset.setter - def tally_reset(self, tally_reset): - check_type('tally reset batches', tally_reset, Iterable, Integral) - self._tally_reset = tally_reset - - @write_matrices.setter - def write_matrices(self, write_matrices): - check_type('CMFD write matrices', write_matrices, bool) - self._write_matrices = write_matrices - - - def _create_begin_subelement(self): - if self._begin is not None: - element = ET.SubElement(self._cmfd_file, "begin") - element.text = str(self._begin) - - def _create_dhat_reset_subelement(self): - if self._dhat_reset is not None: - element = ET.SubElement(self._cmfd_file, "dhat_reset") - element.text = str(self._dhat_reset).lower() - - def _create_display_subelement(self): - if self._display is not None: - element = ET.SubElement(self._cmfd_file, "display") - element.text = str(self._display) - - def _create_downscatter_subelement(self): - if self._downscatter is not None: - element = ET.SubElement(self._cmfd_file, "downscatter") - element.text = str(self._downscatter).lower() - - def _create_feedback_subelement(self): - if self._feedback is not None: - element = ET.SubElement(self._cmfd_file, "feeback") - element.text = str(self._feedback).lower() - - def _create_gauss_seidel_tolerance_subelement(self): - if self._gauss_seidel_tolerance is not None: - element = ET.SubElement(self._cmfd_file, "gauss_seidel_tolerance") - element.text = ' '.join(map(str, self._gauss_seidel_tolerance)) - - def _create_ktol_subelement(self): - if self._ktol is not None: - element = ET.SubElement(self._ktol, "ktol") - element.text = str(self._ktol) - - def _create_mesh_subelement(self): - if self._cmfd_mesh is not None: - xml_element = self._cmfd_mesh._get_xml_element() - self._cmfd_file.append(xml_element) - - def _create_norm_subelement(self): - if self._norm is not None: - element = ET.SubElement(self._cmfd_file, "norm") - element.text = str(self._norm) - - def _create_power_monitor_subelement(self): - if self._power_monitor is not None: - element = ET.SubElement(self._cmfd_file, "power_monitor") - element.text = str(self._power_monitor).lower() - - def _create_run_adjoint_subelement(self): - if self._run_adjoint is not None: - element = ET.SubElement(self._cmfd_file, "run_adjoint") - element.text = str(self._run_adjoint).lower() - - def _create_shift_subelement(self): - if self._shift is not None: - element = ET.SubElement(self._shift, "shift") - element.text = str(self._shift) - - def _create_spectral_subelement(self): - if self._spectral is not None: - element = ET.SubElement(self._spectral, "spectral") - element.text = str(self._spectral) - - def _create_stol_subelement(self): - if self._stol is not None: - element = ET.SubElement(self._stol, "stol") - element.text = str(self._stol) - - def _create_tally_reset_subelement(self): - if self._tally_reset is not None: - element = ET.SubElement(self._tally_reset, "tally_reset") - element.text = ' '.join(map(str, self._tally_reset)) - - def _create_write_matrices_subelement(self): - if self._write_matrices is not None: - element = ET.SubElement(self._cmfd_file, "write_matrices") - element.text = str(self._write_matrices).lower() - - def export_to_xml(self): - """Create a cmfd.xml file using the class data that can be used for an OpenMC - simulation. - - """ - - self._create_begin_subelement() - self._create_dhat_reset_subelement() - self._create_display_subelement() - self._create_downscatter_subelement() - self._create_feedback_subelement() - self._create_gauss_seidel_tolerance_subelement() - self._create_ktol_subelement() - self._create_mesh_subelement() - self._create_norm_subelement() - self._create_power_monitor_subelement() - self._create_run_adjoint_subelement() - self._create_shift_subelement() - self._create_spectral_subelement() - self._create_stol_subelement() - self._create_tally_reset_subelement() - self._create_write_matrices_subelement() - - # Clean the indentation in the file to be user-readable - clean_indentation(self._cmfd_file) - - # Write the XML Tree to the cmfd.xml file - tree = ET.ElementTree(self._cmfd_file) - tree.write("cmfd.xml", xml_declaration=True, - encoding='utf-8', method="xml") - - class CMFDRun(object): r"""Class to run openmc with CMFD acceleration through the C API. Running openmc in this manner obviates the need for defining CMFD parameters @@ -629,27 +248,6 @@ class CMFDRun(object): Attributes ---------- - cmfd_begin : int - Batch number at which CMFD calculations should begin - dhat_reset : bool - Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should be - reset to zero before solving CMFD eigenproblem. - cmfd_display : {'balance', 'dominance', 'entropy', 'source'} - Set one additional CMFD output column. Options are: - - * "balance" - prints the RMS [%] of the resdiual from the neutron balance - equation on CMFD tallies. - * "dominance" - prints the estimated dominance ratio from the CMFD - iterations. - * "entropy" - prints the *entropy* of the CMFD predicted fission source. - * "source" - prints the RMS [%] between the OpenMC fission source and - CMFD fission source. - cmfd_downscatter : bool - Indicate whether an effective downscatter cross section should be used - when using 2-group CMFD. - cmfd_feedback : bool - Indicate or not the CMFD diffusion result is used to adjust the weight - of fission source neutrons on the next OpenMC batch. Defaults to False. cmfd_ktol : float Tolerance on the eigenvalue when performing CMFD power iteration cmfd_mesh : openmc.CMFDMesh @@ -668,9 +266,14 @@ class CMFDRun(object): cmfd_reset : list of int List of batch numbers at which CMFD tallies should be reset cmfd_write_matrices : bool - # TODO update this to read "resultant normalized flux vector" Write sparse matrices that are used during CMFD acceleration (loss, - production) and resultant flux vector phi to file + production) and resultant normalized flux vector phi to file + cmfd_spectral : float + Optional spectral radius that can be used to accelerate the convergence + of Gauss-Seidel iterations during CMFD power iteration. + gauss_seidel_tolerance : Iterable of float + Two parameters specifying the absolute inner tolerance and the relative + inner tolerance for Gauss-Seidel iterations when performing CMFD. indices : numpy.ndarray Stores spatial and group dimensions as [nx, ny, nz, ng] egrid : numpy.ndarray @@ -774,9 +377,6 @@ class CMFDRun(object): k_cmfd : list of floats List of CMFD k-effectives, stored for each generation that CMFD is invoked - spectral : float - Optional spectral radius that can be used to accelerate the convergence - of Gauss-Seidel iterations during CMFD power iteration. resnb : numpy.ndarray Residual from solving neutron balance equations time_cmfd : float @@ -787,13 +387,89 @@ class CMFDRun(object): Time for solving CMFD matrix equations, in seconds intracomm : mpi4py.MPI.Intracomm or None MPI intercommunicator for running MPI commands - - TODO Remove CMFD constants, timing variables in Fortran - TODO Remove cmfd_data.F90, cmfd_execute.F90, cmfd_header.F90, - cmfd_input.F90, cmfd_loss_operator.F90, cmfd_prod_operator.F90, - cmfd_solver.F90 - TODO Remove instances of cmfd in sourcepoint.F90, output.F90, - simulation.F90, api.F90, settings.F90, input_xml.F90, plots.F90 + first_x_accel : tuple + Indices in CMFD problem where first x element is an accelerated region + Precomputed and stored for updating CMFD arrays + last_x_accel : tuple + Indices in CMFD problem where last x element is an accelerated region + Precomputed and stored for updating CMFD arrays + first_y_accel : tuple + Indices in CMFD problem where first y element is an accelerated region + Precomputed and stored for updating CMFD arrays + last_y_accel : tuple + Indices in CMFD problem where last y element is an accelerated region + Precomputed and stored for updating CMFD arrays + first_z_accel : tuple + Indices in CMFD problem where first z element is an accelerated region + Precomputed and stored for updating CMFD arrays + last_z_accel : tuple + Indices in CMFD problem where last z element is an accelerated region + Precomputed and stored for updating CMFD arrays + notfirst_x_accel : tuple + Indices in CMFD problem where all x element excluding first are + accelerated regions. Precomputed and stored for updating CMFD arrays + notlast_x_accel : tuple + Indices in CMFD problem where all x element excluding last are + accelerated regions. Precomputed and stored for updating CMFD arrays + notfirst_y_accel : tuple + Indices in CMFD problem where all y element excluding first are + accelerated regions. Precomputed and stored for updating CMFD arrays + notlast_y_accel : tuple + Indices in CMFD problem where all y element excluding last are + accelerated regions. Precomputed and stored for updating CMFD arrays + notfirst_z_accel : tuple + Indices in CMFD problem where all z element excluding first are + accelerated regions. Precomputed and stored for updating CMFD arrays + notlast_z_accel : tuple + Indices in CMFD problem where all z element excluding last are + accelerated regions. Precomputed and stored for updating CMFD arrays + is_adj_ref_left : numpy.ndarray + Boolean array of all indices in notfirst_x_accel that neighbor a reflector + region to the left. Precomputed and stored for updating CMFD arrays + is_adj_ref_right : numpy.ndarray + Boolean array of all indices in notlast_x_accel that neighbor a reflector + region to the right. Precomputed and stored for updating CMFD arrays + is_adj_ref_back : numpy.ndarray + Boolean array of all indices in notfirst_y_accel that neighbor a reflector + region to the back. Precomputed and stored for updating CMFD arrays + is_adj_ref_front : numpy.ndarray + Boolean array of all indices in notlast_y_accel that neighbor a reflector + region to the front. Precomputed and stored for updating CMFD arrays + is_adj_ref_bottom : numpy.ndarray + Boolean array of all indices in notfirst_z_accel that neighbor a reflector + region to the bottom. Precomputed and stored for updating CMFD arrays + is_adj_ref_top : numpy.ndarray + Boolean array of all indices in notlast_z_accel that neighbor a reflector + region to the top. Precomputed and stored for updating CMFD arrays + accel_idxs : tuple + All indices in CMFD problem that are accelerated. Precomputed and + stored for updating CMFD matrixes + accel_neig_left_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the left + accel_neig_right_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the right + accel_neig_back_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the back + accel_neig_front_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the front + accel_neig_bottom_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the bottom + accel_neig_top_idxs : tuple + All indices in CMFD problem that are accelerated and have a neighbor to + the top + loss_row : numpy.ndarray + All row indices in loss matrix that have nonzero elements + loss_col : numpy.ndarray + All column indices in loss matrix that have nonzero elements + prod_row : numpy.ndarray + All row indices in production matrix that have nonzero elements + prod_col : numpy.ndarray + All column indices in production matrix that have nonzero elements """ @@ -860,8 +536,10 @@ class CMFDRun(object): self._time_cmfd = None self._time_cmfdbuild = None self._time_cmfdsolve = None + self._intracomm = None # Add all index-related variables, for numpy vectorization + self._first_x_accel = None self._last_x_accel = None self._first_y_accel = None self._last_y_accel = None @@ -890,7 +568,6 @@ class CMFDRun(object): self._loss_col = None self._prod_row = None self._prod_col = None - self._intracomm = None @property @@ -1440,11 +1117,9 @@ class CMFDRun(object): # Write out flux vector if self._cmfd_write_matrices: if adjoint: - # TODO Change to self._adj_phi - self._write_vector(phi, 'adj_fluxvec') + self._write_vector(self._adj_phi, 'adj_fluxvec') else: - # TODO Change to self._phi - self._write_vector(phi, 'fluxvec') + self._write_vector(self._phi, 'fluxvec') def _write_vector(self, vector, base_filename): """Write a 1-D numpy array to file and also save it in .npy format. This @@ -2364,7 +2039,7 @@ class CMFDRun(object): is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) - # Define slice equivalent to _accel[0,:,:] + # Define slice equivalent to is_accel[0,:,:] slice_x = x_inds[:1,:,:] slice_y = y_inds[:1,:,:] slice_z = z_inds[:1,:,:] diff --git a/openmc/model/model.py b/openmc/model/model.py index 8583da27a7..28d19713c9 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -25,8 +25,6 @@ class Model(object): Settings information tallies : openmc.Tallies, optional Tallies information - cmfd : openmc.CMFD, optional - CMFD information plots : openmc.Plots, optional Plot information @@ -40,19 +38,16 @@ class Model(object): Settings information tallies : openmc.Tallies Tallies information - cmfd : openmc.CMFD - CMFD information plots : openmc.Plots Plot information """ def __init__(self, geometry=None, materials=None, settings=None, - tallies=None, cmfd=None, plots=None): + tallies=None, plots=None): self.geometry = openmc.Geometry() self.materials = openmc.Materials() self.settings = openmc.Settings() - self.cmfd = cmfd self.tallies = openmc.Tallies() self.plots = openmc.Plots() @@ -83,10 +78,6 @@ class Model(object): def tallies(self): return self._tallies - @property - def cmfd(self): - return self._cmfd - @property def plots(self): return self._plots @@ -121,11 +112,6 @@ class Model(object): for tally in tallies: self._tallies.append(tally) - @cmfd.setter - def cmfd(self, cmfd): - check_type('cmfd', cmfd, (openmc.CMFD, type(None))) - self._cmfd = cmfd - @plots.setter def plots(self, plots): check_type('plots', plots, Iterable, openmc.Plot) @@ -196,8 +182,6 @@ class Model(object): if self.tallies: self.tallies.export_to_xml() - if self.cmfd is not None: - self.cmfd.export_to_xml() if self.plots: self.plots.export_to_xml() diff --git a/openmc/settings.py b/openmc/settings.py index 2bbe0864e5..1d97250ede 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -90,8 +90,6 @@ class Settings(object): indicates what nuclides the method should be applied to. In its absence, the method will be applied to all nuclides with 0 K elastic scattering data present. - run_cmfd : bool - Indicate if coarse mesh finite difference acceleration is to be used run_mode : {'eigenvalue', 'fixed source', 'plot', 'volume', 'particle restart'} The type of calculation to perform (default is 'eigenvalue') seed : int @@ -182,7 +180,6 @@ class Settings(object): self._electron_treatment = None self._photon_transport = None self._ptables = None - self._run_cmfd = None self._seed = None self._survival_biasing = None @@ -279,10 +276,6 @@ class Settings(object): def photon_transport(self): return self._photon_transport - @property - def run_cmfd(self): - return self._run_cmfd - @property def seed(self): return self._seed @@ -528,11 +521,6 @@ class Settings(object): cv.check_type('probability tables', ptables, bool) self._ptables = ptables - @run_cmfd.setter - def run_cmfd(self, run_cmfd): - cv.check_type('run_cmfd', run_cmfd, bool) - self._run_cmfd = run_cmfd - @seed.setter def seed(self, seed): cv.check_type('random number generator seed', seed, Integral) @@ -830,11 +818,6 @@ class Settings(object): element = ET.SubElement(root, "ptables") element.text = str(self._ptables).lower() - def _create_run_cmfd_subelement(self, root): - if self._run_cmfd is not None: - element = ET.SubElement(root, "run_cmfd") - element.text = str(self._run_cmfd).lower() - def _create_seed_subelement(self, root): if self._seed is not None: element = ET.SubElement(root, "seed") @@ -991,7 +974,6 @@ class Settings(object): self._create_max_order_subelement(root_element) self._create_photon_transport_subelement(root_element) self._create_ptables_subelement(root_element) - self._create_run_cmfd_subelement(root_element) self._create_seed_subelement(root_element) self._create_survival_biasing_subelement(root_element) self._create_cutoff_subelement(root_element) diff --git a/schemas.xml b/schemas.xml index 465b9b3980..3e586ec6ac 100644 --- a/schemas.xml +++ b/schemas.xml @@ -5,6 +5,5 @@ - diff --git a/src/api.F90 b/src/api.F90 index efcfa16a6a..30310e5fce 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -59,7 +59,6 @@ contains subroutine free_memory() bind(C) use bank_header - use cmfd_header use geometry_header use material_header use photon_header @@ -103,9 +102,6 @@ contains call free_memory_dagmc() #endif - ! Deallocate CMFD - call deallocate_cmfd(cmfd) - end subroutine free_memory end module openmc_api diff --git a/src/cmfd_data.F90 b/src/cmfd_data.F90 deleted file mode 100644 index f2fb4dc26b..0000000000 --- a/src/cmfd_data.F90 +++ /dev/null @@ -1,943 +0,0 @@ -module cmfd_data - -!============================================================================== -! CMFD_DATA -- This module processes the cmfd tally object to generate -! parameters for CMFD calculation. -!============================================================================== - - use cmfd_header, only: allocate_cmfd, cmfd, cmfd_coremap, & - cmfd_downscatter, cmfd_tallies, dhat_reset - use constants - use tally_filter_mesh, only: MeshFilter - - implicit none - private - public :: set_up_cmfd, neutron_balance - -contains - -!============================================================================== -! SET_UP_CMFD configures cmfd object for a CMFD eigenvalue calculation -!============================================================================== - - subroutine set_up_cmfd() - - use constants, only: CMFD_NOACCEL - - ! Check for core map and set it up - if ((cmfd_coremap) .and. (cmfd%mat_dim == CMFD_NOACCEL)) call set_coremap() - - ! Calculate all cross sections based on reaction rates from last batch - call compute_xs() - - ! Compute effective downscatter cross section - if (cmfd_downscatter) call compute_effective_downscatter() - - ! Check neutron balance - call neutron_balance() - - ! Calculate dtilde - call compute_dtilde() - - ! Calculate dhat - call compute_dhat() - - end subroutine set_up_cmfd - -!=============================================================================== -! COMPUTE_XS takes tallies and computes macroscopic cross sections -!=============================================================================== - - subroutine compute_xs() - - use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, & - FILTER_SURFACE, OUT_LEFT, OUT_RIGHT, OUT_BACK, & - OUT_FRONT, OUT_BOTTOM, OUT_TOP, IN_LEFT, IN_RIGHT, & - IN_BACK, IN_FRONT, IN_BOTTOM, IN_TOP, CMFD_NOACCEL, & - ZERO, ONE, TINY_BIT - use error, only: fatal_error - use mesh_header, only: RegularMesh, meshes - use string, only: to_str - use tally_filter_header, only: filters, filter_matches - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for g - integer :: h ! iteration counter for outgoing groups - integer :: l ! iteration counter for tally filters - integer :: ital ! tally object index - integer :: ijk(3) ! indices for mesh cell - integer :: score_index ! index to pull from tally object - integer :: i_filter_mesh ! index for mesh filter - integer :: i_filter_ein ! index for incoming energy filter - integer :: i_filter_eout ! index for outgoing energy filter - integer :: i_filter_legendre ! index for Legendre filter - integer :: i_mesh ! flattend index for mesh - logical :: energy_filters! energy filters present - real(8) :: flux ! temp variable for flux - type(RegularMesh) :: m ! pointer for mesh object - - ! Extract spatial and energy indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! Set flux object and source distribution to all zeros - cmfd % flux = ZERO - cmfd % openmc_src = ZERO - - ! Associate tallies and mesh - associate (t => cmfd_tallies(1) % obj) - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - end associate - - select type(filt => filters(i_filter_mesh) % obj) - type is (MeshFilter) - m = meshes(filt % mesh()) - end select - - ! Set mesh widths - cmfd % hxyz(1,:,:,:) = m % width(1) ! set x width - cmfd % hxyz(2,:,:,:) = m % width(2) ! set y width - cmfd % hxyz(3,:,:,:) = m % width(3) ! set z width - - cmfd % keff_bal = ZERO - - ! Begin loop around tallies - TAL: do ital = 1, size(cmfd_tallies) - - ! Associate tallies and mesh - associate (t => cmfd_tallies(ital) % obj) - - if (ital < 3) then - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - else if (ital == 3) then - i_filter_mesh = t % filter(t % find_filter(FILTER_MESHSURFACE)) - else if (ital == 4) then - i_filter_mesh = t % filter(t % find_filter(FILTER_MESH)) - i_filter_legendre = t % filter(t % find_filter(FILTER_LEGENDRE)) - end if - - ! Check for energy filters - energy_filters = (t % find_filter(FILTER_ENERGYIN) > 0) - - if (energy_filters) then - i_filter_ein = t % filter(t % find_filter(FILTER_ENERGYIN)) - i_filter_eout = t % filter(t % find_filter(FILTER_ENERGYOUT)) - end if - - ! Begin loop around space - ZLOOP: do k = 1,nz - - YLOOP: do j = 1,ny - - XLOOP: do i = 1,nx - - ! Check for active mesh cell - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then - cycle - end if - end if - - ! Loop around energy groups - OUTGROUP: do h = 1,ng - - ! Start tally 1 - TALLY: if (ital == 1) then - - ! Reset all bins to 1 - do l = 1, size(t % filter) - call filter_matches(t % filter(l)) % bins_clear() - call filter_matches(t % filter(l)) % bins_push_back(1) - end do - - ! Set ijk as mesh indices - ijk = (/ i, j, k /) - - ! Get bin number for mesh indices - call filter_matches(i_filter_mesh) & - % bins_set_data(1, m % get_bin_from_indices(ijk)) - - ! Apply energy in filter - if (energy_filters) then - call filter_matches(i_filter_ein) % bins_set_data(1, ng - h + 1) - end if - - ! Calculate score index from bins - score_index = 1 - do l = 1, size(t % filter) - score_index = score_index + (filter_matches(t % filter(l)) & - % bins_data(1) - 1) * t % stride(l) - end do - - ! Get flux - flux = t % results(RESULT_SUM,1,score_index) - cmfd % flux(h,i,j,k) = flux - - ! Detect zero flux, abort if located - if ((flux - ZERO) < TINY_BIT) then - call fatal_error('Detected zero flux without coremap overlay & - &at: (' // to_str(i) // ',' // to_str(j) // ',' // & - &to_str(k) // ') in group ' // to_str(h)) - end if - - ! Get total rr and convert to total xs - cmfd % totalxs(h,i,j,k) = t % results(RESULT_SUM,2,score_index) / flux - - else if (ital == 2) then - - ! Begin loop to get energy out tallies - INGROUP: do g = 1, ng - - ! Reset all bins to 1 - do l = 1, size(t % filter) - call filter_matches(t % filter(l)) % bins_clear() - call filter_matches(t % filter(l)) % bins_push_back(1) - end do - - ! Set ijk as mesh indices - ijk = (/ i, j, k /) - - ! Get bin number for mesh indices - call filter_matches(i_filter_mesh) & - % bins_set_data(1, m % get_bin_from_indices(ijk)) - - if (energy_filters) then - ! Apply energy in filter - call filter_matches(i_filter_ein) % bins_set_data(1, ng - h + 1) - - ! Set energy out bin - call filter_matches(i_filter_eout) % bins_set_data(1, ng - g + 1) - end if - - ! Calculate score index from bins - score_index = 1 - do l = 1, size(t % filter) - score_index = score_index + (filter_matches(t % filter(l)) & - % bins_data(1) - 1) * t % stride(l) - end do - - ! Get scattering - cmfd % scattxs(h,g,i,j,k) = t % results(RESULT_SUM,1,score_index) /& - cmfd % flux(h,i,j,k) - - ! Get nu-fission - cmfd % nfissxs(h,g,i,j,k) = t % results(RESULT_SUM,2,score_index) /& - cmfd % flux(h,i,j,k) - - ! Bank source - cmfd % openmc_src(g,i,j,k) = cmfd % openmc_src(g,i,j,k) + & - t % results(RESULT_SUM,2,score_index) - cmfd % keff_bal = cmfd % keff_bal + & - t % results(RESULT_SUM,2,score_index) / t % n_realizations - - end do INGROUP - - else if (ital == 3) then - - ! Initialize and filter for energy - do l = 1, size(t % filter) - call filter_matches(t % filter(l)) % bins_clear() - call filter_matches(t % filter(l)) % bins_push_back(1) - end do - - ! Set the bin for this mesh cell - i_mesh = m % get_bin_from_indices([ i, j, k ]) - call filter_matches(i_filter_mesh) % bins_set_data(1, 12*(i_mesh - 1) + 1) - - ! Set the energy bin if needed - if (energy_filters) then - call filter_matches(i_filter_ein) % bins_set_data(1, ng - h + 1) - end if - - score_index = 0 - do l = 1, size(t % filter) - score_index = score_index + (filter_matches(t % filter(l)) & - % bins_data(1) - 1) * t % stride(l) - end do - - ! Left surface - cmfd % current(1,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_LEFT - 1)) - cmfd % current(2,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_LEFT - 1)) - - ! Right surface - cmfd % current(3,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_RIGHT - 1)) - cmfd % current(4,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_RIGHT - 1)) - - ! Back surface - cmfd % current(5,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_BACK - 1)) - cmfd % current(6,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_BACK - 1)) - - ! Front surface - cmfd % current(7,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_FRONT - 1)) - cmfd % current(8,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_FRONT - 1)) - - ! Left surface - cmfd % current(9,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_BOTTOM - 1)) - cmfd % current(10,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_BOTTOM - 1)) - - ! Right surface - cmfd % current(11,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(IN_TOP - 1)) - cmfd % current(12,h,i,j,k) = t % results(RESULT_SUM, 1, & - score_index + 1 + ng*(OUT_TOP - 1)) - - else if (ital == 4) then - - ! Reset all bins to 1 - do l = 1, size(t % filter) - call filter_matches(t % filter(l)) % bins_clear() - call filter_matches(t % filter(l)) % bins_push_back(1) - end do - - ! Set ijk as mesh indices - ijk = (/ i, j, k /) - - ! Get bin number for mesh indices - call filter_matches(i_filter_mesh) & - % bins_set_data(1, m % get_bin_from_indices(ijk)) - - ! Apply energy in filter - if (energy_filters) then - call filter_matches(i_filter_ein) % bins_set_data(1, ng - h + 1) - end if - - ! Apply Legendre filter - call filter_matches(i_filter_legendre) % bins_set_data(1, 2) - - ! Calculate score index from bins - score_index = 1 - do l = 1, size(t % filter) - score_index = score_index + (filter_matches(t % filter(l)) & - % bins_data(1) - 1) * t % stride(l) - end do - - ! Get p1 scatter rr and convert to p1 scatter xs - cmfd % p1scattxs(h,i,j,k) = & - t % results(RESULT_SUM,1,score_index) / & - cmfd % flux(h,i,j,k) - - ! Calculate diffusion coefficient - cmfd % diffcof(h,i,j,k) = & - ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - & - cmfd % p1scattxs(h,i,j,k))) - end if TALLY - - end do OUTGROUP - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - end associate - end do TAL - - ! Normalize openmc source distribution - cmfd % openmc_src = cmfd % openmc_src/sum(cmfd % openmc_src)*cmfd%norm - - end subroutine compute_xs - -!=============================================================================== -! SET_COREMAP is a routine that sets the core mapping information -!=============================================================================== - - subroutine set_coremap() - - use constants, only: CMFD_NOACCEL - - integer :: counter=1 ! counter for unique fuel assemblies - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - - ! Extract spatial indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - - ! Count how many fuel assemblies exist - cmfd % mat_dim = sum(cmfd % coremap - 1) - - ! Allocate indexmap - if (.not. allocated(cmfd % indexmap)) & - allocate(cmfd % indexmap(cmfd % mat_dim,3)) - - ! Begin loops over spatial indices - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - ! Check for reflector - if (cmfd % coremap(i,j,k) == 1) then - - ! reset value to CMFD no acceleration constant - cmfd % coremap(i,j,k) = CMFD_NOACCEL - - else - - ! Must be a fuel --> give unique id number - cmfd % coremap(i,j,k) = counter - cmfd % indexmap(counter,1) = i - cmfd % indexmap(counter,2) = j - cmfd % indexmap(counter,3) = k - counter = counter + 1 - - end if - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - end subroutine set_coremap - -!=============================================================================== -! NEUTRON_BALANCE computes the RMS neutron balance over the CMFD mesh -!=============================================================================== - - subroutine neutron_balance() - - use constants, only: ONE, ZERO, CMFD_NOACCEL, CMFD_NORES - use simulation_header, only: keff, current_batch - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for g - integer :: h ! iteration counter for outgoing groups - integer :: l ! iteration counter for leakage - integer :: cnt ! number of locations to count neutron balance - real(8) :: leakage ! leakage term in neutron balance - real(8) :: interactions ! total number of interactions in balance - real(8) :: scattering ! scattering term in neutron balance - real(8) :: fission ! fission term in neutron balance - real(8) :: res ! residual of neutron balance - real(8) :: rms ! RMS of the residual - - ! Extract spatial and energy indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! Allocate res dataspace - if (.not. allocated(cmfd%resnb)) allocate(cmfd%resnb(ng,nx,ny,nz)) - - ! Reset rms and cnt - rms = ZERO - cnt = 0 - - ! Begin loop around space and energy groups - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - GROUPG: do g = 1, ng - - ! Check for active mesh - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then - cmfd%resnb(g,i,j,k) = CMFD_NORES - cycle - end if - end if - - ! Get leakage - leakage = ZERO - LEAK: do l = 1, 3 - leakage = leakage + ((cmfd % current(4*l,g,i,j,k) - & - cmfd % current(4*l-1,g,i,j,k))) - & - ((cmfd % current(4*l-2,g,i,j,k) - & - cmfd % current(4*l-3,g,i,j,k))) - - end do LEAK - - ! Interactions - interactions = cmfd % totalxs(g,i,j,k) * cmfd % flux(g,i,j,k) - - ! Get scattering and fission - scattering = ZERO - fission = ZERO - GROUPH: do h = 1, ng - - scattering = scattering + cmfd % scattxs(h,g,i,j,k) * & - cmfd % flux(h,i,j,k) - - fission = fission + cmfd % nfissxs(h,g,i,j,k) * & - cmfd % flux(h,i,j,k) - - end do GROUPH - - ! Compute residual - res = leakage + interactions - scattering - (ONE/keff)*fission - - ! Normalize by flux - res = res/cmfd%flux(g,i,j,k) - - ! Bank res in cmfd object - cmfd%resnb(g,i,j,k) = res - - ! Take square for RMS calculation - rms = rms + res**2 - cnt = cnt + 1 - - end do GROUPG - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - ! Calculate RMS and record in vector for this batch - cmfd % balance(current_batch) = sqrt(ONE/dble(cnt)*rms) - - end subroutine neutron_balance - -!=============================================================================== -! COMPUTE_DTILDE precomputes the diffusion coupling coefficient -!=============================================================================== - - subroutine compute_dtilde() - - use constants, only: CMFD_NOACCEL, ZERO_FLUX, TINY_BIT - - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: nxyz(3,2) ! single vector containing boundary locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: bound(6) ! vector containing indices for boudary check - real(8) :: albedo(6) ! albedo vector with global boundaries - real(8) :: cell_dc ! diffusion coef of current cell - real(8) :: cell_hxyz(3) ! cell dimensions of current ijk cell - real(8) :: neig_dc ! diffusion coefficient of neighbor cell - real(8) :: neig_hxyz(3) ! cell dimensions of neighbor cell - real(8) :: dtilde ! finite difference coupling parameter - real(8) :: ref_albedo ! albedo to reflector - - ! Get maximum of spatial and group indices - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! Create single vector of these indices for boundary calculation - nxyz(1,:) = (/1,nx/) - nxyz(2,:) = (/1,ny/) - nxyz(3,:) = (/1,nz/) - - ! Get boundary condition information - albedo = cmfd%albedo - - ! Loop over group and spatial indices - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - GROUP: do g = 1, ng - - ! Check for active mesh cell - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle - end if - - ! Get cell data - cell_dc = cmfd%diffcof(g,i,j,k) - cell_hxyz = cmfd%hxyz(:,i,j,k) - - ! Setup of vector to identify boundary conditions - bound = (/i,i,j,j,k,k/) - - ! Begin loop around sides of cell for leakage - LEAK: do l = 1, 6 - - ! Define xyz and +/- indices - xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 - dir_idx = 2 - mod(l,2) ! -=1, +=2 - shift_idx = -2*mod(l,2) + 1 ! shift neig by -1 or +1 - - ! Check if at a boundary - if (bound(l) == nxyz(xyz_idx,dir_idx)) then - - ! Compute dtilde with albedo boundary condition - dtilde = (2*cell_dc*(1-albedo(l)))/(4*cell_dc*(1+albedo(l)) + & - (1-albedo(l))*cell_hxyz(xyz_idx)) - - ! Check for zero flux - if (abs(albedo(l) - ZERO_FLUX) < TINY_BIT) dtilde = 2*cell_dc / & - cell_hxyz(xyz_idx) - - else ! not a boundary - - ! Compute neighboring cell indices - neig_idx = (/i,j,k/) ! begin with i,j,k - neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - - ! Get neigbor cell data - neig_dc = cmfd%diffcof(g,neig_idx(1),neig_idx(2),neig_idx(3)) - neig_hxyz = cmfd%hxyz(:,neig_idx(1),neig_idx(2),neig_idx(3)) - - ! Check for fuel-reflector interface - if (cmfd_coremap) then - - if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) == & - CMFD_NOACCEL .and. cmfd % coremap(i,j,k) /= CMFD_NOACCEL) then - - ! Get albedo - ref_albedo = get_reflector_albedo(l,g,i,j,k) - - ! Compute dtilde - dtilde = (2*cell_dc*(1-ref_albedo))/(4*cell_dc*(1+ & - ref_albedo)+(1-ref_albedo)*cell_hxyz(xyz_idx)) - - else ! Not next to a reflector or no core map - - ! Compute dtilde to neighbor cell - dtilde = (2*cell_dc*neig_dc)/(neig_hxyz(xyz_idx)*cell_dc + & - cell_hxyz(xyz_idx)*neig_dc) - - end if - - else ! no core map - - ! Compute dtilde to neighbor cell - dtilde = (2*cell_dc*neig_dc)/(neig_hxyz(xyz_idx)*cell_dc + & - cell_hxyz(xyz_idx)*neig_dc) - - end if - - end if - - ! Record dtilde in cmfd object - cmfd%dtilde(l,g,i,j,k) = dtilde - - end do LEAK - - end do GROUP - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - end subroutine compute_dtilde - -!=============================================================================== -! COMPUTE_DHAT computes the nonlinear coupling coefficient -!=============================================================================== - - subroutine compute_dhat() - - use constants, only: CMFD_NOACCEL, ZERO - use error, only: write_message - use string, only: to_str - - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: nxyz(3,2) ! single vector containing boundary locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: bound(6) ! vector containing indices for boudary check - real(8) :: cell_dtilde(6) ! cell dtilde for each face - real(8) :: cell_flux ! flux in current cell - real(8) :: current(12) ! area integrated cell current at each face - real(8) :: net_current ! net current on a face - real(8) :: neig_flux ! flux in neighbor cell - real(8) :: dhat ! dhat equivalence parameter - - ! Get maximum of spatial and group indices - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! Create single vector of these indices for boundary calculation - nxyz(1,:) = (/1,nx/) - nxyz(2,:) = (/1,ny/) - nxyz(3,:) = (/1,nz/) - - ! Geting loop over group and spatial indices - ZLOOP: do k = 1,nz - - YLOOP: do j = 1,ny - - XLOOP: do i = 1,nx - - GROUP: do g = 1,ng - - ! Check for active mesh cell - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) then - cycle - end if - end if - - ! Get cell data - cell_dtilde = cmfd%dtilde(:,g,i,j,k) - cell_flux = cmfd%flux(g,i,j,k)/product(cmfd%hxyz(:,i,j,k)) - current = cmfd%current(:,g,i,j,k) - - ! Setup of vector to identify boundary conditions - bound = (/i,i,j,j,k,k/) - - ! Begin loop around sides of cell for leakage - LEAK: do l = 1,6 - - ! Define xyz and +/- indices - xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 - dir_idx = 2 - mod(l,2) ! -=1, +=2 - shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 - - ! Calculate net current on l face (divided by surf area) - net_current = (current(2*l) - current(2*l-1)) / & - product(cmfd%hxyz(:,i,j,k)) * cmfd%hxyz(xyz_idx,i,j,k) - - ! Check if at a boundary - if (bound(l) == nxyz(xyz_idx,dir_idx)) then - - ! Compute dhat - dhat = (net_current - shift_idx*cell_dtilde(l)*cell_flux) / & - cell_flux - - else ! not a boundary - - ! Compute neighboring cell indices - neig_idx = (/i,j,k/) ! begin with i,j,k - neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - - ! Get neigbor flux - neig_flux = cmfd%flux(g,neig_idx(1),neig_idx(2),neig_idx(3)) / & - product(cmfd%hxyz(:,neig_idx(1),neig_idx(2),neig_idx(3))) - - ! Check for fuel-reflector interface - if (cmfd_coremap) then - - if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) == & - CMFD_NOACCEL .and. cmfd % coremap(i,j,k) /= CMFD_NOACCEL) then - - ! compute dhat - dhat = (net_current - shift_idx*cell_dtilde(l)*cell_flux) /& - cell_flux - - else ! not a fuel-reflector interface - - ! Compute dhat - dhat = (net_current + shift_idx*cell_dtilde(l)* & - (neig_flux - cell_flux))/(neig_flux + cell_flux) - - end if - - else ! not for fuel-reflector case - - ! Compute dhat - dhat = (net_current + shift_idx*cell_dtilde(l)* & - (neig_flux - cell_flux))/(neig_flux + cell_flux) - - end if - - end if - - ! record dhat in cmfd object - cmfd%dhat(l,g,i,j,k) = dhat - - ! check for dhat reset - if (dhat_reset) then - cmfd%dhat(l,g,i,j,k) = ZERO - end if - - end do LEAK - - end do GROUP - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - ! write that dhats are zero - if (dhat_reset) then - call write_message('Dhats reset to zero.', 8) - end if - - end subroutine compute_dhat - -!=============================================================================== -! GET_REFLECTOR_ALBEDO is a function that calculates the albedo to the reflector -!=============================================================================== - - function get_reflector_albedo(l, g, i, j, k) - - use constants, only: ONE - - real(8) :: get_reflector_albedo ! reflector albedo - integer, intent(in) :: i ! iteration counter for x - integer, intent(in) :: j ! iteration counter for y - integer, intent(in) :: k ! iteration counter for z - integer, intent(in) :: g ! iteration counter for groups - integer, intent(in) :: l ! iteration counter for leakages - - integer :: shift_idx ! parameter to shift index by +1 or -1 - real(8) :: current(12) ! partial currents for all faces of mesh cell - real(8) :: albedo ! the albedo - - ! Get partial currents from object - current = cmfd%current(:,g,i,j,k) - - ! Define xyz and +/- indices - shift_idx = -2*mod(l,2) + 1 ! shift neig by -1 or +1 - - ! Calculate albedo - if ((shift_idx == 1 .and. current(2*l ) < 1.0e-10_8) .or. & - (shift_idx == -1 .and. current(2*l-1) < 1.0e-10_8)) then - albedo = ONE - else - albedo = (current(2*l-1)/current(2*l))**(shift_idx) - end if - - ! Assign to function variable - get_reflector_albedo = albedo - - end function get_reflector_albedo - -!=============================================================================== -! COMPUTE_EFFECTIVE_DOWNSCATTER changes downscatter rate for zero upscatter -!=============================================================================== - - subroutine compute_effective_downscatter() - - use constants, only: ZERO, CMFD_NOACCEL - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - real(8) :: flux1 ! group 1 volume int flux - real(8) :: flux2 ! group 2 volume int flux - real(8) :: sigt1 ! group 1 total xs - real(8) :: sigt2 ! group 2 total xs - real(8) :: sigs11 ! scattering transfer 1 --> 1 - real(8) :: sigs21 ! scattering transfer 2 --> 1 - real(8) :: sigs12 ! scattering transfer 1 --> 2 - real(8) :: sigs22 ! scattering transfer 2 --> 2 - real(8) :: siga1 ! group 1 abs xs - real(8) :: siga2 ! group 2 abs xs - real(8) :: sigs12_eff ! effective downscatter xs - - ! Extract spatial and energy indices from object - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! Return if not two groups - if (ng /= 2) return - - ! Begin loop around space and energy groups - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - ! Check for active mesh - if (allocated(cmfd%coremap)) then - if (cmfd%coremap(i,j,k) == CMFD_NOACCEL) cycle - end if - - ! Extract cross sections and flux from object - flux1 = cmfd % flux(1,i,j,k) - flux2 = cmfd % flux(2,i,j,k) - sigt1 = cmfd % totalxs(1,i,j,k) - sigt2 = cmfd % totalxs(2,i,j,k) - sigs11 = cmfd % scattxs(1,1,i,j,k) - sigs21 = cmfd % scattxs(2,1,i,j,k) - sigs12 = cmfd % scattxs(1,2,i,j,k) - sigs22 = cmfd % scattxs(2,2,i,j,k) - - ! Compute absorption xs - siga1 = sigt1 - sigs11 - sigs12 - siga2 = sigt2 - sigs22 - sigs21 - - ! Compute effective downscatter xs - sigs12_eff = sigs12 - sigs21*flux2/flux1 - - ! Recompute total cross sections (use effective and no upscattering) - sigt1 = siga1 + sigs11 + sigs12_eff - sigt2 = siga2 + sigs22 - - ! Record total xs - cmfd % totalxs(1,i,j,k) = sigt1 - cmfd % totalxs(2,i,j,k) = sigt2 - - ! Record effective downscatter xs - cmfd % scattxs(1,2,i,j,k) = sigs12_eff - - ! Zero out upscatter cross section - cmfd % scattxs(2,1,i,j,k) = ZERO - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - end subroutine compute_effective_downscatter - -end module cmfd_data diff --git a/src/cmfd_execute.F90 b/src/cmfd_execute.F90 deleted file mode 100644 index 681d08c5c1..0000000000 --- a/src/cmfd_execute.F90 +++ /dev/null @@ -1,408 +0,0 @@ -module cmfd_execute - -!============================================================================== -! CMFD_EXECUTE -- This module is the highest level cmfd module that controls the -! cross section generation, diffusion calculation, and source re-weighting -!============================================================================== - - use cmfd_header - use settings - use simulation_header - - implicit none - private - public :: execute_cmfd, cmfd_init_batch, cmfd_tally_init - -#ifdef OPENMC_MPI - interface - subroutine cmfd_broadcast(n, buffer) bind(C) - import C_DOUBLE, C_INT - integer(C_INT), value :: n - real(C_DOUBLE), intent(out) :: buffer - end subroutine - end interface -#endif - -contains - -!============================================================================== -! EXECUTE_CMFD runs the CMFD calculation -!============================================================================== - - subroutine execute_cmfd() bind(C) - - use cmfd_data, only: set_up_cmfd - use cmfd_solver, only: cmfd_solver_execute - use error, only: warning, fatal_error - use message_passing, only: master - - ! CMFD single processor on master - if (master) then - - ! Start cmfd timer - call time_cmfd % start() - - ! Create cmfd data from OpenMC tallies - call set_up_cmfd() - - ! Call solver - call cmfd_solver_execute() - - ! Save k-effective - cmfd % k_cmfd(current_batch) = cmfd % keff - - ! check to perform adjoint on last batch - if (current_batch == n_batches .and. cmfd_run_adjoint) then - call cmfd_solver_execute(adjoint=.true.) - end if - - end if - - ! calculate fission source - call calc_fission_source() - - ! calculate weight factors - call cmfd_reweight(.true.) - - ! stop cmfd timer - if (master) call time_cmfd % stop() - - end subroutine execute_cmfd - -!============================================================================== -! CMFD_INIT_BATCH handles cmfd options at the start of every batch -!============================================================================== - - subroutine cmfd_init_batch() bind(C) - - ! Check to activate CMFD diffusion and possible feedback - ! this guarantees that when cmfd begins at least one batch of tallies are - ! accumulated - if (cmfd_run .and. cmfd_begin == current_batch) then - cmfd_on = .true. - end if - - ! If this is a restart run and we are just replaying batches leave - if (restart_run .and. current_batch <= restart_batch) return - - ! Check to reset tallies - if (cmfd_run .and. cmfd_reset % contains(current_batch)) then - call cmfd_tally_reset() - end if - - end subroutine cmfd_init_batch - -!=============================================================================== -! CALC_FISSION_SOURCE calculates the cmfd fission source -!=============================================================================== - - subroutine calc_fission_source() - - use constants, only: CMFD_NOACCEL, ZERO, TWO - use message_passing - use string, only: to_str - - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer :: ng ! maximum number of energy groups - integer :: n ! total size - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: idx ! index in vector - 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 - - ! Get maximum of spatial and group indices - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - n = ng*nx*ny*nz - - ! Allocate cmfd source if not already allocated and allocate buffer - if (.not. allocated(cmfd % cmfd_src)) & - allocate(cmfd % cmfd_src(ng,nx,ny,nz)) - - ! Reset cmfd source to 0 - cmfd % cmfd_src = ZERO - - ! Only perform for master - if (master) then - - ! Loop around indices to map to cmfd object - ZLOOP: do k = 1, nz - - YLOOP: do j = 1, ny - - XLOOP: do i = 1, nx - - GROUP: do g = 1, ng - - ! Check for core map - if (cmfd_coremap) then - if (cmfd % coremap(i,j,k) == CMFD_NOACCEL) then - cycle - end if - end if - - ! Get dimensions of cell - hxyz = cmfd % hxyz(:,i,j,k) - - ! Calculate volume - vol = hxyz(1)*hxyz(2)*hxyz(3) - - ! Get first index - idx = get_matrix_idx(1,i,j,k,ng,nx,ny) - - ! Compute fission source - cmfd % cmfd_src(g,i,j,k) = sum(cmfd % nfissxs(:,g,i,j,k) * & - cmfd % phi(idx:idx + (ng - 1)))*vol - - end do GROUP - - end do XLOOP - - end do YLOOP - - end do ZLOOP - - ! Normalize source such that it sums to 1.0 - cmfd % cmfd_src = cmfd % cmfd_src/sum(cmfd % cmfd_src) - - ! Compute entropy - if (entropy_on) then - - ! Allocate tmp array - if (.not.allocated(source)) allocate(source(ng,nx,ny,nz)) - - ! Initialize the source - source = ZERO - - ! Compute log - where (cmfd % cmfd_src > ZERO) - source = cmfd % cmfd_src*log(cmfd % cmfd_src)/log(TWO) - end where - - ! Sum that source - cmfd % entropy(current_batch) = -sum(source) - - ! Deallocate tmp array - if (allocated(source)) deallocate(source) - - end if - - ! Normalize source so average is 1.0 - cmfd % cmfd_src = cmfd % cmfd_src/sum(cmfd % cmfd_src)*cmfd % norm - - ! Calculate differences between normalized sources - cmfd % src_cmp(current_batch) = sqrt(ONE/cmfd % norm * & - sum((cmfd % cmfd_src - cmfd % openmc_src)**2)) - - end if - -#ifdef OPENMC_MPI - ! Broadcast full source to all procs - call cmfd_broadcast(n, cmfd % cmfd_src(1,1,1,1)) -#endif - - end subroutine calc_fission_source - -!=============================================================================== -! CMFD_REWEIGHT performs weighting of particles in the source bank -!=============================================================================== - - subroutine cmfd_reweight(new_weights) - - use algorithm, only: binary_search - use bank_header, only: source_bank - use constants, only: ZERO, ONE - use error, only: warning, fatal_error - use message_passing - use string, only: to_str - - logical, intent(in) :: new_weights ! calcualte new weights - - integer :: nx ! maximum number of cells in x direction - integer :: ny ! maximum number of cells in y direction - integer :: nz ! maximum number of cells in z direction - integer(C_INT) :: ng ! maximum number of energy groups - integer :: i ! iteration counter - integer :: g ! index for group - integer :: ijk(3) ! spatial bin location - integer :: e_bin ! energy bin of source particle - integer :: mesh_bin ! mesh bin of soruce particle - integer :: n_groups ! number of energy groups - real(8) :: norm ! normalization factor - logical(C_BOOL) :: outside ! any source sites outside mesh - logical :: in_mesh ! source site is inside mesh - - interface - subroutine cmfd_populate_sourcecounts(ng, energies, source_counts, outside) bind(C) - import C_INT, C_DOUBLE, C_BOOL - integer(C_INT), value :: ng - real(C_DOUBLE), intent(in) :: energies - real(C_DOUBLE), intent(out) :: source_counts - logical(C_BOOL), intent(out) :: outside - end subroutine - end interface - - ! Get maximum of spatial and group indices - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - ng = cmfd % indices(4) - - ! allocate arrays in cmfd object (can take out later extend to multigroup) - if (.not.allocated(cmfd%sourcecounts)) then - allocate(cmfd%sourcecounts(ng, nx*ny*nz)) - cmfd % sourcecounts = 0 - end if - if (.not.allocated(cmfd % weightfactors)) then - allocate(cmfd % weightfactors(ng,nx,ny,nz)) - cmfd % weightfactors = ONE - end if - - ! Compute new weight factors - if (new_weights) then - - ! Set weight factors to a default 1.0 - cmfd%weightfactors = ONE - - ! Count bank sites in mesh and reverse due to egrid structure - call cmfd_populate_sourcecounts(ng + 1, cmfd % egrid(1), & - cmfd % sourcecounts(1,1), outside) - - ! Check for sites outside of the mesh - if (master .and. outside) then - call fatal_error("Source sites outside of the CMFD mesh!") - end if - - ! Have master compute weight factors (watch for 0s) - if (master) then - ! Calculate normalization factor - norm = sum(cmfd % sourcecounts) / sum(cmfd % cmfd_src) - - do mesh_bin = 1, nx*ny*nz - call cmfd_mesh % get_indices_from_bin(mesh_bin, ijk) - do g = 1, ng - if (cmfd % sourcecounts(ng - g + 1, mesh_bin) > ZERO) then - if (cmfd % cmfd_src(g,ijk(1),ijk(2),ijk(3)) > ZERO) then - cmfd % weightfactors(g,ijk(1),ijk(2),ijk(3)) = & - cmfd % cmfd_src(g,ijk(1),ijk(2),ijk(3)) * norm & - / cmfd % sourcecounts(ng - g + 1, mesh_bin) - end if - end if - end do - end do - end if - - if (.not. cmfd_feedback) return - - ! Broadcast weight factors to all procs -#ifdef OPENMC_MPI - call cmfd_broadcast(ng*nx*ny*nz, cmfd % weightfactors(1,1,1,1)) -#endif - end if - - ! begin loop over source bank - do i = 1, int(work,4) - - ! Determine spatial bin - call cmfd_mesh % get_indices(source_bank(i) % xyz, ijk, in_mesh) - - ! Determine energy bin - n_groups = size(cmfd % egrid) - 1 - if (source_bank(i) % E < cmfd % egrid(1)) then - e_bin = 1 - if (master) call warning('Source pt below energy grid') - elseif (source_bank(i) % E > cmfd % egrid(n_groups + 1)) then - e_bin = n_groups - if (master) call warning('Source pt above energy grid') - else - e_bin = binary_search(cmfd % egrid, n_groups + 1, source_bank(i) % E) - end if - - ! Reverese energy bin (lowest grp is highest energy bin) - e_bin = n_groups - e_bin + 1 - - ! Check for outside of mesh - if (.not. in_mesh) then - call fatal_error('Source site found outside of CMFD mesh') - end if - - ! Reweight particle - source_bank(i) % wgt = source_bank(i) % wgt * & - cmfd % weightfactors(e_bin, ijk(1), ijk(2), ijk(3)) - end do - - end subroutine cmfd_reweight - -!=============================================================================== -! GET_MATRIX_IDX takes (x,y,z,g) indices and computes location in matrix -!=============================================================================== - - function get_matrix_idx(g, i, j, k, ng, nx, ny) result (matidx) - - integer :: matidx ! the index location in matrix - integer, intent(in) :: i ! current x index - integer, intent(in) :: j ! current y index - integer, intent(in) :: k ! current z index - integer, intent(in) :: g ! current group index - integer, intent(in) :: nx ! maximum number of cells in x direction - integer, intent(in) :: ny ! maximum number of cells in y direction - integer, intent(in) :: ng ! maximum number of energy groups - - ! Check if coremap is used - if (cmfd_coremap) then - - ! Get idx from core map - matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) - - else - - ! Compute index - matidx = g + ng*(i - 1) + ng*nx*(j - 1) + ng*nx*ny*(k - 1) - - end if - - end function get_matrix_idx - -!=============================================================================== -! CMFD_TALLY_INIT -!=============================================================================== - - subroutine cmfd_tally_init() bind(C) - integer :: i - if (cmfd_run) then - do i = 1, size(cmfd_tallies) - cmfd_tallies(i) % obj % active = .true. - end do - end if - end subroutine cmfd_tally_init - -!=============================================================================== -! CMFD_TALLY_RESET resets all cmfd tallies -!=============================================================================== - - subroutine cmfd_tally_reset() - - use error, only: write_message - - integer :: i ! loop counter - - ! Print message - call write_message("CMFD tallies reset", 6) - - ! Reset CMFD tallies - do i = 1, size(cmfd_tallies) - cmfd_tallies(i) % obj % n_realizations = 0 - cmfd_tallies(i) % obj % results(:,:,:) = ZERO - end do - - end subroutine cmfd_tally_reset - -end module cmfd_execute diff --git a/src/cmfd_execute.cpp b/src/cmfd_execute.cpp deleted file mode 100644 index 0af584c1b0..0000000000 --- a/src/cmfd_execute.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include // for copy -#include -#include - -#include "xtensor/xarray.hpp" -#include "xtensor/xio.hpp" - -#include "openmc/capi.h" -#include "openmc/mesh.h" -#include "openmc/message_passing.h" -#include "openmc/simulation.h" -#include "openmc/settings.h" - -namespace openmc { - - -extern "C" void -cmfd_populate_sourcecounts(int n_energy, const double* energies, - double* source_counts, bool* outside) -{ - // Get pointer to source bank - Bank* source_bank; - int64_t n; - openmc_source_bank(&source_bank, &n); - - // Get source counts in each mesh bin / energy bin - auto& m = model::meshes.at(settings::index_cmfd_mesh); - xt::xarray counts = m->count_sites(simulation::work, source_bank, n_energy, energies, outside); - - // Copy data from the xarray into the source counts array - std::copy(counts.begin(), counts.end(), source_counts); -} - -#ifdef OPENMC_MPI -extern "C" void -cmfd_broadcast(int n, double* buffer) -{ - MPI_Bcast(buffer, n, MPI_DOUBLE, 0, mpi::intracomm); -} -#endif - - -} // namespace openmc diff --git a/src/cmfd_header.F90 b/src/cmfd_header.F90 deleted file mode 100644 index f4b14b6f2b..0000000000 --- a/src/cmfd_header.F90 +++ /dev/null @@ -1,272 +0,0 @@ -module cmfd_header - - use, intrinsic :: ISO_C_BINDING - - use constants, only: CMFD_NOACCEL, ZERO, ONE - use mesh_header, only: RegularMesh - use set_header, only: SetInt - use tally_header, only: TallyContainer - use timer_header, only: Timer - - implicit none - private - public :: allocate_cmfd, deallocate_cmfd - - type, public :: cmfd_type - - ! Indices for problem - integer :: indices(4) - - ! Albedo boundary condition - real(8) :: albedo(6) - - ! Core overlay map - integer, allocatable :: coremap(:,:,:) - integer, allocatable :: indexmap(:,:) - integer :: mat_dim = CMFD_NOACCEL - - ! Energy grid - real(C_DOUBLE), allocatable :: egrid(:) - - ! Cross sections - real(8), allocatable :: totalxs(:,:,:,:) - real(8), allocatable :: p1scattxs(:,:,:,:) - real(8), allocatable :: scattxs(:,:,:,:,:) - real(8), allocatable :: nfissxs(:,:,:,:,:) - - ! Diffusion coefficient - real(8), allocatable :: diffcof(:,:,:,:) - - ! Current - real(8), allocatable :: current(:,:,:,:,:) - - ! Flux - real(8), allocatable :: flux(:,:,:,:) - - ! Coupling coefficients and equivalence parameters - real(8), allocatable :: dtilde(:,:,:,:,:) - real(8), allocatable :: dhat(:,:,:,:,:) - - ! Dimensions of mesh cells ([hu,hv,hw],xloc,yloc,zloc) - real(8), allocatable :: hxyz(:,:,:,:) - - ! Source distributions - real(C_DOUBLE), allocatable :: cmfd_src(:,:,:,:) - real(C_DOUBLE), allocatable :: openmc_src(:,:,:,:) - - ! Source sites in each mesh box - real(C_DOUBLE), allocatable :: sourcecounts(:,:) - - ! Weight adjustment factors - real(8), allocatable :: weightfactors(:,:,:,:) - - ! Eigenvector/eigenvalue from cmfd run - real(8), allocatable :: phi(:) - real(8) :: keff = ZERO - - ! Eigenvector/eigenvalue from adjoint run - real(8), allocatable :: adj_phi(:) - real(8) :: adj_keff = ZERO - - ! Residual for neutron balance - real(8), allocatable :: resnb(:,:,:,:) - - ! Openmc source normalization factor - real(8) :: norm = ONE - - ! "Shannon entropy" from cmfd fission source - real(8), allocatable :: entropy(:) - - ! RMS of neutron balance equations - real(8), allocatable :: balance(:) - - ! RMS deviation of OpenMC and CMFD normalized source - real(8), allocatable :: src_cmp(:) - - ! Dominance ratio - real(8), allocatable :: dom(:) - - ! List of CMFD k - real(8), allocatable :: k_cmfd(:) - - ! Balance keff - real(8) :: keff_bal - - end type cmfd_type - - ! Main object - type(cmfd_type), public :: cmfd - - integer(C_INT), public, bind(C) :: index_cmfd_mesh - type(RegularMesh), public :: cmfd_mesh - - ! Pointers for different tallies - type(TallyContainer), public, pointer :: cmfd_tallies(:) => null() - - ! Timing objects - type(Timer), public :: time_cmfd ! timer for whole cmfd calculation - type(Timer), public :: time_cmfdbuild ! timer for matrix build - type(Timer), public :: time_cmfdsolve ! timer for solver - - ! Flag for active core map - logical, public :: cmfd_coremap = .false. - - ! Flag to reset dhats to zero - logical, public :: dhat_reset = .false. - - ! Flag to activate neutronic feedback via source weights - logical, public :: cmfd_feedback = .false. - - ! Adjoint method type - character(len=10), public :: cmfd_adjoint_type = 'physical' - - ! Number of incomplete ilu factorization levels - integer, public :: cmfd_ilu_levels = 1 - - ! Batch to begin cmfd - integer, public :: cmfd_begin = 1 - - ! Tally reset list - integer, public :: n_cmfd_resets - type(SetInt), public :: cmfd_reset - - ! Compute effective downscatter cross section - logical, public :: cmfd_downscatter = .false. - - ! Convergence monitoring - logical, public :: cmfd_power_monitor = .false. - - ! Cmfd output - logical, public :: cmfd_write_matrices = .false. - - ! Run an adjoint calculation (last batch only) - logical, public :: cmfd_run_adjoint = .false. - - ! CMFD run logicals - logical(C_BOOL), public, bind(C) :: cmfd_on = .false. - - ! CMFD display info - character(len=25), public :: cmfd_display = 'balance' - - ! Estimate of spectral radius of CMFD matrices and tolerances - real(8), public :: cmfd_spectral = ZERO - real(8), public :: cmfd_shift = 1.e6 - real(8), public :: cmfd_ktol = 1.e-8_8 - real(8), public :: cmfd_stol = 1.e-8_8 - real(8), public :: cmfd_atoli = 1.e-10_8 - real(8), public :: cmfd_rtoli = 1.e-5_8 - -contains - -!============================================================================== -! ALLOCATE_CMFD allocates all data in of cmfd type -!============================================================================== - - subroutine allocate_cmfd(this, n_batches) - - integer, intent(in) :: n_batches ! number of batches in calc - type(cmfd_type), intent(inout) :: this ! cmfd instance - - integer :: nx ! number of mesh cells in x direction - integer :: ny ! number of mesh cells in y direction - integer :: nz ! number of mesh cells in z direction - integer :: ng ! number of energy groups - - ! Extract spatial and energy indices from object - nx = this % indices(1) - ny = this % indices(2) - nz = this % indices(3) - ng = this % indices(4) - - ! Allocate flux, cross sections and diffusion coefficient - if (.not. allocated(this % flux)) allocate(this % flux(ng,nx,ny,nz)) - if (.not. allocated(this % totalxs)) allocate(this % totalxs(ng,nx,ny,nz)) - if (.not. allocated(this % p1scattxs)) allocate(this % p1scattxs(ng,nx,ny,nz)) - if (.not. allocated(this % scattxs)) allocate(this % scattxs(ng,ng,nx,ny,nz)) - if (.not. allocated(this % nfissxs)) allocate(this % nfissxs(ng,ng,nx,ny,nz)) - if (.not. allocated(this % diffcof)) allocate(this % diffcof(ng,nx,ny,nz)) - - ! Allocate dtilde and dhat - if (.not. allocated(this % dtilde)) allocate(this % dtilde(6,ng,nx,ny,nz)) - if (.not. allocated(this % dhat)) allocate(this % dhat(6,ng,nx,ny,nz)) - - ! Allocate dimensions for each box (here for general case) - if (.not. allocated(this % hxyz)) allocate(this % hxyz(3,nx,ny,nz)) - - ! Allocate surface currents - if (.not. allocated(this % current)) allocate(this % current(12,ng,nx,ny,nz)) - - ! Allocate source distributions - if (.not. allocated(this % cmfd_src)) allocate(this % cmfd_src(ng,nx,ny,nz)) - if (.not. allocated(this % openmc_src)) allocate(this % openmc_src(ng,nx,ny,nz)) - - ! Allocate source weight modification vars - if (.not. allocated(this % sourcecounts)) allocate(this % sourcecounts(ng,nx*ny*nz)) - if (.not. allocated(this % weightfactors)) allocate(this % weightfactors(ng,nx,ny,nz)) - - ! Allocate batchwise parameters - if (.not. allocated(this % entropy)) allocate(this % entropy(n_batches)) - if (.not. allocated(this % balance)) allocate(this % balance(n_batches)) - if (.not. allocated(this % src_cmp)) allocate(this % src_cmp(n_batches)) - if (.not. allocated(this % dom)) allocate(this % dom(n_batches)) - if (.not. allocated(this % k_cmfd)) allocate(this % k_cmfd(n_batches)) - - ! Set everthing to 0 except weight multiply factors if feedback isnt on - this % flux = ZERO - this % totalxs = ZERO - this % p1scattxs = ZERO - this % scattxs = ZERO - this % nfissxs = ZERO - this % diffcof = ZERO - this % dtilde = ZERO - this % dhat = ZERO - this % hxyz = ZERO - this % current = ZERO - this % cmfd_src = ZERO - this % openmc_src = ZERO - this % sourcecounts = ZERO - this % weightfactors = ONE - this % balance = ZERO - this % src_cmp = ZERO - this % dom = ZERO - this % k_cmfd = ZERO - this % entropy = ZERO - - end subroutine allocate_cmfd - -!=============================================================================== -! DEALLOCATE_CMFD frees all memory of cmfd type -!=============================================================================== - - subroutine deallocate_cmfd(this) - - type(cmfd_type), intent(inout) :: this ! cmfd instance - - if (allocated(this % egrid)) deallocate(this % egrid) - if (allocated(this % totalxs)) deallocate(this % totalxs) - if (allocated(this % p1scattxs)) deallocate(this % p1scattxs) - if (allocated(this % scattxs)) deallocate(this % scattxs) - if (allocated(this % nfissxs)) deallocate(this % nfissxs) - if (allocated(this % diffcof)) deallocate(this % diffcof) - if (allocated(this % current)) deallocate(this % current) - if (allocated(this % flux)) deallocate(this % flux) - if (allocated(this % dtilde)) deallocate(this % dtilde) - if (allocated(this % dhat)) deallocate(this % dhat) - if (allocated(this % hxyz)) deallocate(this % hxyz) - if (allocated(this % coremap)) deallocate(this % coremap) - if (allocated(this % indexmap)) deallocate(this % indexmap) - if (allocated(this % phi)) deallocate(this % phi) - if (allocated(this % sourcecounts)) deallocate(this % sourcecounts) - if (allocated(this % weightfactors)) deallocate(this % weightfactors) - if (allocated(this % cmfd_src)) deallocate(this % cmfd_src) - if (allocated(this % openmc_src)) deallocate(this % openmc_src) - if (allocated(this % balance)) deallocate(this % balance) - if (allocated(this % src_cmp)) deallocate(this % src_cmp) - if (allocated(this % dom)) deallocate(this % dom) - if (allocated(this % k_cmfd)) deallocate(this % k_cmfd) - if (allocated(this % entropy)) deallocate(this % entropy) - if (allocated(this % resnb)) deallocate(this % resnb) - - end subroutine deallocate_cmfd - -end module cmfd_header diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 deleted file mode 100644 index d1e0f6a9a7..0000000000 --- a/src/cmfd_input.F90 +++ /dev/null @@ -1,495 +0,0 @@ -module cmfd_input - - use, intrinsic :: ISO_C_BINDING - - use cmfd_header - use mesh_header - use mgxs_interface, only: energy_bins, num_energy_groups - use tally - use tally_header - use timer_header - - implicit none - private - public :: configure_cmfd - -contains - -!=============================================================================== -! CONFIGURE_CMFD initializes CMFD parameters -!=============================================================================== - - subroutine configure_cmfd() - - ! Read in cmfd input file - call read_cmfd_xml() - - ! Initialize timers - call time_cmfd % reset() - call time_cmfdbuild % reset() - call time_cmfdsolve % reset() - - ! Allocate cmfd object - call allocate_cmfd(cmfd, n_batches) - - end subroutine configure_cmfd - -!=============================================================================== -! READ_INPUT reads the CMFD input file and organizes it into a data structure -!=============================================================================== - - subroutine read_cmfd_xml() - - use constants, only: ZERO, ONE - use error, only: fatal_error, warning, write_message - use string, only: to_lower - use xml_interface - use, intrinsic :: ISO_FORTRAN_ENV - - integer :: i, g - integer :: ng - integer :: n_params - integer, allocatable :: iarray(:) - integer, allocatable :: int_array(:) - logical :: file_exists ! does cmfd.xml exist? - logical :: found - character(MAX_LINE_LEN) :: filename - real(8) :: gs_tol(2) - type(XMLDocument) :: doc - type(XMLNode) :: root - type(XMLNode) :: node_mesh - - ! Read cmfd input file - filename = trim(path_input) // "cmfd.xml" - inquire(FILE=filename, EXIST=file_exists) - if (.not. file_exists) then - ! CMFD is optional unless it is in on from settings - if (cmfd_run) then - call fatal_error("No CMFD XML file, '" // trim(filename) // "' does not& - & exist!") - end if - return - else - - ! Tell user - call write_message("Reading CMFD XML file...", 5) - - end if - - ! Parse cmfd.xml file - call doc % load_file(filename) - root = doc % document_element() - - ! Get pointer to mesh XML node - node_mesh = root % child("mesh") - - ! Check if mesh is there - if (.not. node_mesh % associated()) then - call fatal_error("No CMFD mesh specified in CMFD XML file.") - end if - - ! Set spatial dimensions in cmfd object - call get_node_array(node_mesh, "dimension", cmfd % indices(1:3)) - - ! Get number of energy groups - if (check_for_node(node_mesh, "energy")) then - ng = node_word_count(node_mesh, "energy") - if(.not. allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) - call get_node_array(node_mesh, "energy", cmfd%egrid) - cmfd % indices(4) = ng - 1 ! sets energy group dimension - ! If using MG mode, check to see if these egrid points at least match - ! the MG Data breakpoints - if (.not. run_CE) then - do i = 1, ng - found = .false. - do g = 1, num_energy_groups + 1 - if (cmfd % egrid(i) == energy_bins(g)) then - found = .true. - exit - end if - end do - if (.not. found) then - call fatal_error("CMFD energy mesh boundaries must align with& - & boundaries of multi-group data!") - end if - end do - end if - else - if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2)) - cmfd % egrid = [ ZERO, energy_max(NEUTRON) ] - cmfd % indices(4) = 1 ! one energy group - end if - - ! Set global albedo - if (check_for_node(node_mesh, "albedo")) then - call get_node_array(node_mesh, "albedo", cmfd % albedo) - else - cmfd % albedo = [ ONE, ONE, ONE, ONE, ONE, ONE ] - end if - - ! Get acceleration map - if (check_for_node(node_mesh, "map")) then - allocate(cmfd % coremap(cmfd % indices(1), cmfd % indices(2), & - cmfd % indices(3))) - if (node_word_count(node_mesh, "map") /= & - product(cmfd % indices(1:3))) then - call fatal_error('CMFD coremap not to correct dimensions') - end if - allocate(iarray(node_word_count(node_mesh, "map"))) - call get_node_array(node_mesh, "map", iarray) - cmfd % coremap = reshape(iarray,(cmfd % indices(1:3))) - cmfd_coremap = .true. - deallocate(iarray) - end if - - ! Check for normalization constant - if (check_for_node(root, "norm")) then - call get_node_value(root, "norm", cmfd % norm) - end if - - ! Set feedback logical - if (check_for_node(root, "feedback")) then - call get_node_value(root, "feedback", cmfd_feedback) - end if - - ! Set downscatter logical - if (check_for_node(root, "downscatter")) then - call get_node_value(root, "downscatter", cmfd_downscatter) - end if - - ! Reset dhat parameters - if (check_for_node(root, "dhat_reset")) then - call get_node_value(root, "dhat_reset", dhat_reset) - end if - - ! Set monitoring - if (check_for_node(root, "power_monitor")) then - call get_node_value(root, "power_monitor", cmfd_power_monitor) - end if - - ! Output logicals - if (check_for_node(root, "write_matrices")) then - call get_node_value(root, "write_matrices", cmfd_write_matrices) - end if - - ! Run an adjoint calc - if (check_for_node(root, "run_adjoint")) then - call get_node_value(root, "run_adjoint", cmfd_run_adjoint) - end if - - ! Batch to begin cmfd - if (check_for_node(root, "begin")) & - call get_node_value(root, "begin", cmfd_begin) - - ! Check for cmfd tally resets - if (check_for_node(root, "tally_reset")) then - n_cmfd_resets = node_word_count(root, "tally_reset") - else - n_cmfd_resets = 0 - end if - if (n_cmfd_resets > 0) then - allocate(int_array(n_cmfd_resets)) - call get_node_array(root, "tally_reset", int_array) - do i = 1, n_cmfd_resets - call cmfd_reset % add(int_array(i)) - end do - deallocate(int_array) - end if - - ! Get display - if (check_for_node(root, "display")) & - call get_node_value(root, "display", cmfd_display) - - ! Read in spectral radius estimate and tolerances - if (check_for_node(root, "spectral")) & - call get_node_value(root, "spectral", cmfd_spectral) - if (check_for_node(root, "shift")) & - call get_node_value(root, "shift", cmfd_shift) - if (check_for_node(root, "ktol")) & - call get_node_value(root, "ktol", cmfd_ktol) - if (check_for_node(root, "stol")) & - call get_node_value(root, "stol", cmfd_stol) - if (check_for_node(root, "gauss_seidel_tolerance")) then - n_params = node_word_count(root, "gauss_seidel_tolerance") - if (n_params /= 2) then - call fatal_error('Gauss Seidel tolerance is not 2 parameters & - &(absolute, relative).') - end if - call get_node_array(root, "gauss_seidel_tolerance", gs_tol) - cmfd_atoli = gs_tol(1) - cmfd_rtoli = gs_tol(2) - end if - - ! Create tally objects - call create_cmfd_tally(root) - - ! Close CMFD XML file - call doc % clear() - - end subroutine read_cmfd_xml - -!=============================================================================== -! CREATE_CMFD_TALLY creates the tally object for OpenMC to process for CMFD -! accleration. -! There are 3 tally types: -! 1: Only an energy in filter-> flux,total,p1 scatter -! 2: Energy in and energy out filter-> nu-scatter,nu-fission -! 3: Mesh current -!=============================================================================== - - subroutine create_cmfd_tally(root) - - use constants, only: MAX_LINE_LEN - use error, only: fatal_error, warning - use mesh_header - use string - use tally, only: openmc_tally_allocate - use tally_header, only: openmc_extend_tallies - use tally_filter_header - use tally_filter - use xml_interface - - type(XMLNode), intent(in) :: root ! XML root element - - logical :: energy_filters - integer :: i ! loop counter - integer :: n ! size of arrays in mesh specification - integer(C_INT32_T) :: ng ! number of energy groups (default 1) - integer :: n_filter ! number of filters - integer :: i_start, i_end - integer :: i_filt_start, i_filt_end - integer(C_INT32_T), allocatable :: filter_indices(:) - integer(C_INT) :: err - integer :: i_filt ! index in filters array - integer :: filt_id - integer :: tally_id - real(C_DOUBLE), allocatable :: energies(:) - type(XMLNode) :: node_mesh - - ! Read CMFD mesh - call read_meshes(root % ptr) - - ! Get index of cmfd mesh and set ID - i_start = n_meshes() - 1 - err = openmc_mesh_set_id(i_start, i_start) - - ! Save reference to CMFD mesh - index_cmfd_mesh = i_start - cmfd_mesh = meshes(i_start) - - ! Get pointer to mesh XML node - node_mesh = root % child("mesh") - - ! Determine number of filters - energy_filters = check_for_node(node_mesh, "energy") - n = merge(5, 3, energy_filters) - - ! Extend filters array so we can add CMFD filters - err = openmc_extend_filters(n, i_filt_start, i_filt_end) - - ! Set up mesh filter - i_filt = i_filt_start - err = openmc_filter_set_type(i_filt, C_CHAR_'mesh' // C_NULL_CHAR) - call openmc_get_filter_next_id(filt_id) - err = openmc_filter_set_id(i_filt, filt_id) - err = openmc_mesh_filter_set_mesh(i_filt, i_start) - - if (energy_filters) then - ! Read and set incoming energy mesh filter - i_filt = i_filt + 1 - err = openmc_filter_set_type(i_filt, C_CHAR_'energy' // C_NULL_CHAR) - call openmc_get_filter_next_id(filt_id) - err = openmc_filter_set_id(i_filt, filt_id) - - ! Get energies and set bins - ng = node_word_count(node_mesh, "energy") - allocate(energies(ng)) - call get_node_array(node_mesh, "energy", energies) - err = openmc_energy_filter_set_bins(i_filt, ng, energies) - - ! Read and set outgoing energy mesh filter - i_filt = i_filt + 1 - err = openmc_filter_set_type(i_filt, C_CHAR_'energyout' // C_NULL_CHAR) - call openmc_get_filter_next_id(filt_id) - err = openmc_filter_set_id(i_filt, filt_id) - err = openmc_energy_filter_set_bins(i_filt, ng, energies) - end if - - ! Duplicate the mesh filter for the mesh current tally since other - ! tallies use this filter and we need to change the dimension - i_filt = i_filt + 1 - err = openmc_filter_set_type(i_filt, C_CHAR_'meshsurface' // C_NULL_CHAR) - call openmc_get_filter_next_id(filt_id) - err = openmc_filter_set_id(i_filt, filt_id) - err = openmc_meshsurface_filter_set_mesh(i_filt, i_start) - - ! Add in legendre filter for the P1 tally - i_filt = i_filt + 1 - err = openmc_filter_set_type(i_filt, C_CHAR_'legendre' // C_NULL_CHAR) - call openmc_get_filter_next_id(filt_id) - err = openmc_filter_set_id(i_filt, filt_id) - err = openmc_legendre_filter_set_order(i_filt, 1) - - ! Initialize filters - do i = i_filt_start, i_filt_end - call filters(i) % obj % initialize() - end do - - ! Allocate tallies - err = openmc_extend_tallies(4, i_start, i_end) - cmfd_tallies => tallies(i_start:i_end) - - ! Begin loop around tallies - do i = 1, size(cmfd_tallies) - ! Allocate tally - err = openmc_tally_allocate(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR) - call openmc_get_tally_next_id(tally_id) - err = openmc_tally_set_id(i_start + i - 1, tally_id) - - ! Point t to tally variable - associate (t => cmfd_tallies(i) % obj) - - ! Set the incoming energy mesh filter index in the tally find_filter - ! array - n_filter = 1 - if (energy_filters) then - n_filter = n_filter + 1 - end if - - ! Set number of nucilde bins - allocate(t % nuclide_bins(1)) - t % nuclide_bins(1) = -1 - t % n_nuclide_bins = 1 - - ! Record tally id which is equivalent to loop number - t % id = i_start + i - 1 - - if (i == 1) then - - ! Set name - t % name = "CMFD flux, total" - - ! Set tally estimator to analog - err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) - - ! Set tally type to volume - err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) - - ! Allocate and set filters - allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_start - if (energy_filters) then - filter_indices(2) = i_filt_start + 1 - end if - err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices) - deallocate(filter_indices) - - ! Allocate scoring bins - allocate(t % score_bins(2)) - t % n_score_bins = 2 - - ! Set macro_bins - t % score_bins(1) = SCORE_FLUX - t % score_bins(2) = SCORE_TOTAL - - else if (i == 2) then - - ! Set name - t % name = "CMFD neutron production" - - ! Set tally estimator to analog - err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) - - ! Set tally type to volume - err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) - - ! Set the incoming energy mesh filter index in the tally find_filter - ! array - if (energy_filters) then - n_filter = n_filter + 1 - end if - - ! Allocate and set indices in filters array - allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_start - if (energy_filters) then - filter_indices(2) = i_filt_start + 1 - filter_indices(3) = i_filt_start + 2 - end if - err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices) - deallocate(filter_indices) - - ! Allocate macro reactions - allocate(t % score_bins(2)) - t % n_score_bins = 2 - - ! Set macro_bins - t % score_bins(1) = SCORE_NU_SCATTER - t % score_bins(2) = SCORE_NU_FISSION - - else if (i == 3) then - - ! Set name - t % name = "CMFD surface currents" - - ! Set tally estimator to analog - err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) - - ! Allocate and set filters - allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_end - 1 - if (energy_filters) then - filter_indices(2) = i_filt_start + 1 - end if - err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices) - deallocate(filter_indices) - - ! Allocate macro reactions - allocate(t % score_bins(1)) - t % n_score_bins = 1 - - ! Set macro bins - t % score_bins(1) = SCORE_CURRENT - err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR) - - else if (i == 4) then - ! Set name - t % name = "CMFD P1 scatter" - - ! Set tally estimator to analog - err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR) - - ! Set tally type to volume - err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR) - - ! Allocate and set filters - n_filter = 2 - if (energy_filters) then - n_filter = n_filter + 1 - end if - allocate(filter_indices(n_filter)) - filter_indices(1) = i_filt_start - filter_indices(2) = i_filt_end - if (energy_filters) then - filter_indices(3) = i_filt_start + 1 - end if - err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices) - deallocate(filter_indices) - - ! Allocate scoring bins - allocate(t % score_bins(1)) - t % n_score_bins = 1 - - ! Set macro_bins - t % score_bins(1) = SCORE_SCATTER - end if - - ! Make CMFD tallies active from the start - t % active = .true. - - end associate - end do - - end subroutine create_cmfd_tally - -end module cmfd_input diff --git a/src/cmfd_loss_operator.F90 b/src/cmfd_loss_operator.F90 deleted file mode 100644 index 50a75e52d5..0000000000 --- a/src/cmfd_loss_operator.F90 +++ /dev/null @@ -1,435 +0,0 @@ -module cmfd_loss_operator - - use constants, only: CMFD_NOACCEL, ZERO - use cmfd_header, only: cmfd, cmfd_coremap - use matrix_header, only: Matrix - - implicit none - private - public :: init_loss_matrix, build_loss_matrix - -contains - -!=============================================================================== -! INIT_LOSS_MATRIX preallocates loss matrix and initializes it -!=============================================================================== - - subroutine init_loss_matrix(loss_matrix) - - type(Matrix), intent(inout) :: loss_matrix ! cmfd loss matrix - - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: n ! total length of matrix - integer :: nnz ! number of nonzeros in matrix - integer :: n_i ! number of interior cells - integer :: n_c ! number of corner cells - integer :: n_s ! number of side cells - integer :: n_e ! number of edge cells - integer :: nz_c ! number of non-zero corner cells - integer :: nz_e ! number of non-zero edge cells - integer :: nz_s ! number of non-zero side cells - integer :: nz_i ! number of non-zero interior cells - - ! Get maximum number of cells in each direction - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! Calculate dimensions of matrix - if (cmfd_coremap) then - n = cmfd % mat_dim * ng - else - n = nx*ny*nz*ng - end if - - ! Calculate number of nonzeros, if core map -> need to determine manually - if (cmfd_coremap) then - nnz = preallocate_loss_matrix(nx, ny, nz, ng, n) - else ! structured Cartesian grid - n_c = 8 ! define # of corners - n_e = 4*(nx - 2) + 4*(ny - 2) + 4*(nz - 2) ! define # of edges - n_s = 2*(nx - 2)*(ny - 2) + 2*(nx - 2)*(nz - 2) & - + 2*(ny - 2)*(nz - 2) ! define # of sides - n_i = nx*ny*nz - (n_c + n_e + n_s) ! define # of interiors - nz_c = ng*n_c*(4 + ng - 1) ! define # nonzero corners - nz_e = ng*n_e*(5 + ng - 1) ! define # nonzero edges - nz_s = ng*n_s*(6 + ng - 1) ! define # nonzero sides - nz_i = ng*n_i*(7 + ng - 1) ! define # nonzero interiors - nnz = nz_c + nz_e + nz_s + nz_i - end if - - ! Configure loss matrix - call loss_matrix % create(n, nnz) - - end subroutine init_loss_matrix - -!=============================================================================== -! PREALLOCATE_LOSS_MATRIX manually preallocates the loss matrix -!=============================================================================== - - function preallocate_loss_matrix(nx, ny, nz, ng, n) result(nnz) - - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: nz ! maximum number of z cells - integer, intent(in) :: ng ! maximum number of groups - integer, intent(in) :: n ! total length of matrix - integer :: nnz ! number of nonzeros - - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: h ! energy group when doing scattering - integer :: irow ! row counter - integer :: bound(6) ! vector for comparing when looking for bound - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: nxyz(3,2) ! single vector containing bound. locations - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: neig_mat_idx ! matrix index of neighbor cell - integer :: scatt_mat_idx ! matrix index for h-->g scattering terms - - ! Reset number of nonzeros to 0 - nnz = 0 - - ! Create single vector of these indices for boundary calculation - nxyz(1,:) = (/1,nx/) - nxyz(2,:) = (/1,ny/) - nxyz(3,:) = (/1,nz/) - - ! Begin loop around local rows - ROWS: do irow = 1, n - - ! Set a nonzero for diagonal - nnz = nnz + 1 - - ! Get location indices - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! Create boundary vector - bound = (/i,i,j,j,k,k/) - - ! Begin loop over leakages - LEAK: do l = 1,6 - - ! Define (x,y,z) and (-,+) indices - xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 - dir_idx = 2 - mod(l,2) ! -=1, +=2 - - ! Calculate spatial indices of neighbor - neig_idx = (/i,j,k/) ! begin with i,j,k - shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 - neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - - ! Check for global boundary - if (bound(l) /= nxyz(xyz_idx,dir_idx)) then - - ! Check for coremap - if (cmfd_coremap) then - - ! Check for neighbor that is non-acceleartred - if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) /= & - CMFD_NOACCEL) then - - ! Get neighbor matrix index - call indices_to_matrix(g,neig_idx(1), neig_idx(2), & - neig_idx(3), neig_mat_idx, ng, nx, ny) - - ! Record nonzero - nnz = nnz + 1 - - end if - - else - - ! Get neighbor matrix index - call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx, ng, nx, ny) - - ! Record nonzero - nnz = nnz + 1 - - end if - - end if - - end do LEAK - - ! Begin loop over off diagonal in-scattering - SCATTR: do h = 1, ng - - ! Cycle though if h=g, it was already banked in removal xs - if (h == g) cycle - - ! Get neighbor matrix index - call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) - - ! Record nonzero - nnz = nnz + 1 - - end do SCATTR - - end do ROWS - - end function preallocate_loss_matrix - -!=============================================================================== -! BUILD_LOSS_MATRIX creates the matrix representing loss of neutrons -!=============================================================================== - - subroutine build_loss_matrix(loss_matrix, adjoint) - - type(Matrix), intent(inout) :: loss_matrix ! cmfd loss matrix - logical, intent(in), optional :: adjoint ! set up the adjoint - - integer :: nxyz(3,2) ! single vector containing bound. locations - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: l ! iteration counter for leakages - integer :: h ! energy group when doing scattering - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: neig_mat_idx ! matrix index of neighbor cell - integer :: scatt_mat_idx ! matrix index for h-->g scattering terms - integer :: bound(6) ! vector for comparing when looking for bound - integer :: xyz_idx ! index for determining if x,y or z leakage - integer :: dir_idx ! index for determining - or + face of cell - integer :: neig_idx(3) ! spatial indices of neighbour - integer :: shift_idx ! parameter to shift index by +1 or -1 - integer :: irow ! iteration counter over row - logical :: adjoint_calc ! is this a physical adjoint calculation? - real(8) :: totxs ! total macro cross section - real(8) :: scattxsgg ! scattering macro cross section g-->g - real(8) :: scattxshg ! scattering macro cross section h-->g - real(8) :: dtilde(6) ! finite difference coupling parameter - real(8) :: dhat(6) ! nonlinear coupling parameter - real(8) :: hxyz(3) ! cell lengths in each direction - real(8) :: jn ! direction dependent leakage coeff to neig - real(8) :: jo(6) ! leakage coeff in front of cell flux - real(8) :: jnet ! net leakage from jo - real(8) :: val ! temporary variable before saving to - - ! Check for adjoint - adjoint_calc = .false. - if (present(adjoint)) adjoint_calc = adjoint - - ! Get maximum number of cells in each direction - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! Create single vector of these indices for boundary calculation - nxyz(1,:) = (/1,nx/) - nxyz(2,:) = (/1,ny/) - nxyz(3,:) = (/1,nz/) - - ! Begin iteration loops - ROWS: do irow = 1, loss_matrix % n - - ! Set up a new row in matrix - call loss_matrix % new_row() - - ! Get indices for that row - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! Retrieve cell data - totxs = cmfd%totalxs(g,i,j,k) - scattxsgg = cmfd%scattxs(g,g,i,j,k) - dtilde = cmfd%dtilde(:,g,i,j,k) - hxyz = cmfd%hxyz(:,i,j,k) - - ! Check and get dhat - if (allocated(cmfd%dhat)) then - dhat = cmfd%dhat(:,g,i,j,k) - else - dhat = ZERO - end if - - ! Create boundary vector - bound = (/i,i,j,j,k,k/) - - ! Begin loop over leakages - ! 1=-x, 2=+x, 3=-y, 4=+y, 5=-z, 6=+z - LEAK: do l = 1,6 - - ! Define (x,y,z) and (-,+) indices - xyz_idx = int(ceiling(real(l)/real(2))) ! x=1, y=2, z=3 - dir_idx = 2 - mod(l,2) ! -=1, +=2 - - ! Calculate spatial indices of neighbor - neig_idx = (/i,j,k/) ! begin with i,j,k - shift_idx = -2*mod(l,2) +1 ! shift neig by -1 or +1 - neig_idx(xyz_idx) = shift_idx + neig_idx(xyz_idx) - - ! Check for global boundary - if (bound(l) /= nxyz(xyz_idx,dir_idx)) then - - ! Check for core map - if (cmfd_coremap) then - - ! Check that neighbor is not reflector - if (cmfd % coremap(neig_idx(1),neig_idx(2),neig_idx(3)) /= & - CMFD_NOACCEL) then - - ! Compute leakage coefficient for neighbor - jn = -dtilde(l) + shift_idx*dhat(l) - - ! Get neighbor matrix index - call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx, ng, nx, ny) - - ! Compute value and record to bank - val = jn/hxyz(xyz_idx) - - ! Record value in matrix - call loss_matrix % add_value(neig_mat_idx, val) - - end if - - else - - ! Compute leakage coefficient for neighbor - jn = -dtilde(l) + shift_idx*dhat(l) - - ! Get neighbor matrix index - call indices_to_matrix(g, neig_idx(1), neig_idx(2), neig_idx(3), & - neig_mat_idx, ng, nx, ny) - - ! Compute value and record to bank - val = jn/hxyz(xyz_idx) - - ! Record value in matrix - call loss_matrix % add_value(neig_mat_idx, val) - - end if - - end if - - ! Compute leakage coefficient for target - jo(l) = shift_idx*dtilde(l) + dhat(l) - - end do LEAK - - ! Calculate net leakage coefficient for target - jnet = (jo(2) - jo(1))/hxyz(1) + (jo(4) - jo(3))/hxyz(2) + & - (jo(6) - jo(5))/hxyz(3) - - ! Calculate loss of neutrons - val = jnet + totxs - scattxsgg - - ! Record diagonal term - call loss_matrix % add_value(irow, val) - - ! Begin loop over off diagonal in-scattering - SCATTR: do h = 1, ng - - ! Cycle though if h=g, value already banked in removal xs - if (h == g) cycle - - ! Get neighbor matrix index - call indices_to_matrix(h, i, j, k, scatt_mat_idx, ng, nx, ny) - - ! Check for adjoint - if (adjoint_calc) then - ! Get scattering macro xs, transposed! - scattxshg = cmfd%scattxs(g, h, i, j, k) - else - ! Get scattering macro xs - scattxshg = cmfd%scattxs(h, g, i, j, k) - end if - - ! Negate the scattering xs - val = -scattxshg - - ! Record value in matrix - call loss_matrix % add_value(scatt_mat_idx, val) - - end do SCATTR - - end do ROWS - - ! CSR requires n+1 row - call loss_matrix % new_row() - - end subroutine build_loss_matrix - -!=============================================================================== -! INDICES_TO_MATRIX takes (x,y,z,g) indices and computes location in matrix -!=============================================================================== - - subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) - - integer, intent(out) :: matidx ! the index location in matrix - integer, intent(in) :: i ! current x index - integer, intent(in) :: j ! current y index - integer, intent(in) :: k ! current z index - integer, intent(in) :: g ! current group index - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: ng ! maximum number of groups - - ! Check if coremap is used - if (cmfd_coremap) then - - ! Get idx from core map - matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) - - else - - ! Compute index - matidx = g + ng*(i - 1) + ng*nx*(j - 1) + ng*nx*ny*(k - 1) - - end if - - end subroutine indices_to_matrix - -!=============================================================================== -! MATRIX_TO_INDICES converts a matrix index to spatial and group indices -!=============================================================================== - - subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - integer, intent(out) :: i ! iteration counter for x - integer, intent(out) :: j ! iteration counter for y - integer, intent(out) :: k ! iteration counter for z - integer, intent(out) :: g ! iteration counter for groups - integer, intent(in) :: irow ! iteration counter over row (0 reference) - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: nz ! maximum number of z cells - integer, intent(in) :: ng ! maximum number of groups - - ! Check for core map - if (cmfd_coremap) then - - ! Get indices from indexmap - g = mod(irow-1, ng) + 1 - i = cmfd % indexmap((irow-1)/ng+1,1) - j = cmfd % indexmap((irow-1)/ng+1,2) - k = cmfd % indexmap((irow-1)/ng+1,3) - - else - - ! Compute indices - g = mod(irow-1, ng) + 1 - i = mod(irow-1, ng*nx)/ng + 1 - j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 - k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 - - end if - - end subroutine matrix_to_indices - -end module cmfd_loss_operator diff --git a/src/cmfd_prod_operator.F90 b/src/cmfd_prod_operator.F90 deleted file mode 100644 index 91ce43b359..0000000000 --- a/src/cmfd_prod_operator.F90 +++ /dev/null @@ -1,199 +0,0 @@ -module cmfd_prod_operator - - use constants, only: CMFD_NOACCEL - use cmfd_header, only: cmfd, cmfd_coremap - use matrix_header, only: Matrix - - implicit none - private - public :: init_prod_matrix, build_prod_matrix - -contains - -!============================================================================== -! INIT_PROD_MATRIX preallocates prod matrix and initializes it -!============================================================================== - - subroutine init_prod_matrix(prod_matrix) - - type(Matrix), intent(inout) :: prod_matrix ! production matrix - - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: n ! total length of matrix - integer :: nnz ! number of nonzeros in matrix - - ! Get maximum number of cells in each direction - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! Calculate dimensions and number of nonzeros in matrix - if (cmfd_coremap) then - n = cmfd % mat_dim * ng - else - n = nx*ny*nz*ng - end if - nnz = n * ng - - ! Configure prod matrix - call prod_matrix % create(n, nnz) - - end subroutine init_prod_matrix - -!=============================================================================== -! BUILD_PROD_MATRIX creates the matrix representing production of neutrons -!=============================================================================== - - subroutine build_prod_matrix(prod_matrix, adjoint) - - type(Matrix), intent(inout) :: prod_matrix ! production matrix - logical, intent(in), optional :: adjoint ! adjoint calculation logical - - integer :: i ! iteration counter for x - integer :: j ! iteration counter for y - integer :: k ! iteration counter for z - integer :: g ! iteration counter for groups - integer :: h ! energy group when doing scattering - integer :: nx ! maximum number of x cells - integer :: ny ! maximum number of y cells - integer :: nz ! maximum number of z cells - integer :: ng ! maximum number of groups - integer :: hmat_idx ! index in matrix for energy group h - integer :: irow ! iteration counter over row - logical :: adjoint_calc ! is this a physical adjoint? - real(8) :: nfissxs ! nufission cross section h-->g - real(8) :: val ! temporary variable for nfissxs - - ! get maximum number of cells in each direction - nx = cmfd%indices(1) - ny = cmfd%indices(2) - nz = cmfd%indices(3) - ng = cmfd%indices(4) - - ! check for adjoint - adjoint_calc = .false. - if (present(adjoint)) adjoint_calc = adjoint - - ! begin iteration loops - ROWS: do irow = 1, prod_matrix % n - - ! add a new row to matrix - call prod_matrix % new_row() - - ! get indices for that row - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! check if not including reflector - if (cmfd_coremap) then - - ! check if at a reflector - if (cmfd % coremap(i,j,k) == CMFD_NOACCEL) then - cycle - end if - - end if - - ! loop around all other groups - - NFISS: do h = 1, ng - - ! get matrix column location - call indices_to_matrix(h, i, j, k, hmat_idx, ng, nx, ny) - - ! check for adjoint and bank val - if (adjoint_calc) then - ! get nu-fission cross section from cell - nfissxs = cmfd%nfissxs(g,h,i,j,k) - else - ! get nu-fission cross section from cell - nfissxs = cmfd%nfissxs(h,g,i,j,k) - end if - - ! set as value to be recorded - val = nfissxs - - ! record value in matrix - - call prod_matrix % add_value(hmat_idx, val) - - end do NFISS - - end do ROWS - - ! CSR requires n+1 row - call prod_matrix % new_row() - - end subroutine build_prod_matrix - -!=============================================================================== -! INDICES_TO_MATRIX takes (x,y,z,g) indices and computes location in matrix -!=============================================================================== - - subroutine indices_to_matrix(g, i, j, k, matidx, ng, nx, ny) - - integer, intent(out) :: matidx ! the index location in matrix - integer, intent(in) :: i ! current x index - integer, intent(in) :: j ! current y index - integer, intent(in) :: k ! current z index - integer, intent(in) :: g ! current group index - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: ng ! maximum number of groups - - ! check if coremap is used - if (cmfd_coremap) then - - ! get idx from core map - matidx = ng*(cmfd % coremap(i,j,k)) - (ng - g) - - else - - ! compute index - matidx = g + ng*(i - 1) + ng*nx*(j - 1) + ng*nx*ny*(k - 1) - - end if - - end subroutine indices_to_matrix - -!=============================================================================== -! MATRIX_TO_INDICES converts matrix index to spatial and group indices -!=============================================================================== - - subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - integer, intent(out) :: i ! iteration counter for x - integer, intent(out) :: j ! iteration counter for y - integer, intent(out) :: k ! iteration counter for z - integer, intent(out) :: g ! iteration counter for groups - integer, intent(in) :: irow ! iteration counter over row (0 reference) - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: nz ! maximum number of z cells - integer, intent(in) :: ng ! maximum number of groups - - ! check for core map - if (cmfd_coremap) then - - ! get indices from indexmap - g = mod(irow-1, ng) + 1 - i = cmfd % indexmap((irow-1)/ng+1,1) - j = cmfd % indexmap((irow-1)/ng+1,2) - k = cmfd % indexmap((irow-1)/ng+1,3) - - else - - ! compute indices - g = mod(irow-1, ng) + 1 - i = mod(irow-1, ng*nx)/ng + 1 - j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 - k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 - - end if - - end subroutine matrix_to_indices - -end module cmfd_prod_operator diff --git a/src/cmfd_solver.F90 b/src/cmfd_solver.F90 deleted file mode 100644 index 01404d9b0e..0000000000 --- a/src/cmfd_solver.F90 +++ /dev/null @@ -1,809 +0,0 @@ -module cmfd_solver - -! This module contains routines to execute the power iteration solver - - use constants, only: MAX_LINE_LEN - use cmfd_loss_operator, only: init_loss_matrix, build_loss_matrix - use cmfd_prod_operator, only: init_prod_matrix, build_prod_matrix - use matrix_header, only: Matrix - use vector_header, only: Vector - - implicit none - private - public :: cmfd_solver_execute - - real(8) :: k_n ! New k-eigenvalue - real(8) :: k_o ! Old k-eigenvalue - real(8) :: k_s ! Shift of eigenvalue - real(8) :: k_ln ! New shifted eigenvalue - real(8) :: k_lo ! Old shifted eigenvalue - real(8) :: norm_n ! Current norm of source vector - real(8) :: norm_o ! Old norm of source vector - real(8) :: kerr ! Error in keff - real(8) :: serr ! Error in source - real(8) :: ktol ! Tolerance on keff - real(8) :: stol ! Tolerance on source - logical :: adjoint_calc ! Run an adjoint calculation - type(Matrix) :: loss ! Cmfd loss matrix - type(Matrix) :: prod ! Cmfd prod matrix - type(Vector) :: phi_n ! New flux vector - type(Vector) :: phi_o ! Old flux vector - type(Vector) :: s_n ! New source vector - type(Vector) :: s_o ! Old flux vector - type(Vector) :: serr_v ! Error in source - - ! CMFD linear solver interface - abstract interface - subroutine linsolve(A, b, x, tol, i) - import :: Matrix - import :: Vector - type(Matrix), intent(inout) :: A - type(Vector), intent(inout) :: b - type(Vector), intent(inout) :: x - real(8), intent(in) :: tol - integer, intent(out) :: i - end subroutine linsolve - end interface - -contains - -!=============================================================================== -! CMFD_SOLVER_EXECUTE sets up and runs power iteration solver for CMFD -!=============================================================================== - - subroutine cmfd_solver_execute(adjoint) - - use cmfd_header, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve - - logical, optional, intent(in) :: adjoint ! adjoint calc - - logical :: physical_adjoint = .false. - - ! Check for adjoint execution - adjoint_calc = .false. - if (present(adjoint)) adjoint_calc = adjoint - - ! Check for physical adjoint - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'physical') & - physical_adjoint = .true. - - ! Start timer for build - call time_cmfdbuild % start() - - ! Initialize matrices and vectors - call init_data(physical_adjoint) - - ! Check for mathematical adjoint calculation - if (adjoint_calc .and. trim(cmfd_adjoint_type) == 'math') & - call compute_adjoint() - - ! Stop timer for build - call time_cmfdbuild % stop() - - ! Begin power iteration - call time_cmfdsolve % start() - call execute_power_iter() - call time_cmfdsolve % stop() - - ! Extract results - call extract_results() - - ! Deallocate data - call finalize() - - end subroutine cmfd_solver_execute - -!=============================================================================== -! INIT_DATA allocates matrices and vectors for CMFD solution -!=============================================================================== - - subroutine init_data(adjoint) - - use constants, only: ONE, ZERO - use cmfd_header, only: cmfd_shift, cmfd_ktol, cmfd_stol, cmfd_write_matrices - use simulation_header, only: keff - - logical, intent(in) :: adjoint - - integer :: n ! problem size - real(8) :: guess ! initial guess - real(8) :: dw ! eigenvalue shift - - ! Set up matrices - call init_loss_matrix(loss) - call init_prod_matrix(prod) - - ! Get problem size - n = loss % n - - ! Set up flux vectors - call phi_n % create(n) - call phi_o % create(n) - - ! Set up source vectors - call s_n % create(n) - call s_o % create(n) - call serr_v % create(n) - - ! Set initial guess - guess = ONE - phi_n % val = guess - phi_o % val = guess - k_n = keff - k_o = k_n - dw = cmfd_shift - k_s = k_o + dw - k_ln = ONE/(ONE/k_n - ONE/k_s) - k_lo = k_ln - - ! Fill in loss matrix - call build_loss_matrix(loss, adjoint=adjoint) - - ! Fill in production matrix - call build_prod_matrix(prod, adjoint=adjoint) - - ! Finalize setup of CSR matrices - call loss % assemble() - call prod % assemble() - if (cmfd_write_matrices) then - if (adjoint) then - call loss % write('adj_loss.dat') - call prod % write('adj_prod.dat') - else - call loss % write('loss.dat') - call prod % write('prod.dat') - end if - end if - - ! Set norms to 0 - norm_n = ZERO - norm_o = ZERO - - ! Set tolerances - ktol = cmfd_ktol - stol = cmfd_stol - - end subroutine init_data - -!=============================================================================== -! COMPUTE_ADJOINT computes a mathematical adjoint of CMFD problem -!=============================================================================== - - subroutine compute_adjoint() - - use error, only: fatal_error - use cmfd_header, only: cmfd_write_matrices - - ! Transpose matrices - loss = loss % transpose() - prod = prod % transpose() - - ! Write out matrix in binary file (debugging) - if (cmfd_write_matrices) then - call loss % write('adj_loss.dat') - call prod % write('adj_prod.dat') - end if - - end subroutine compute_adjoint - -!=============================================================================== -! EXECUTE_POWER_ITER is the main power iteration routine -! for the cmfd calculation -!=============================================================================== - - subroutine execute_power_iter() - - use constants, only: ONE - use error, only: fatal_error - use cmfd_header, only: cmfd, cmfd_atoli, cmfd_rtoli - - integer :: i ! iteration counter - integer :: innerits ! # of inner iterations - integer :: totalits ! total number of inners - logical :: iconv ! did the problem converged - real(8) :: atoli ! absolute minimum tolerance - real(8) :: rtoli ! relative tolerance based on source conv - real(8) :: toli ! the current tolerance of inners - - ! Reset convergence flag - iconv = .false. - - ! Set up tolerances - atoli = cmfd_atoli - rtoli = cmfd_rtoli - toli = rtoli*100._8 - - ! Perform shift - call wielandt_shift() - totalits = 0 - - ! Begin power iteration - do i = 1, 10000 - - ! Check if reached iteration 10000 - if (i == 10000) then - call fatal_error('Reached maximum iterations in CMFD power iteration & - &solver.') - end if - - ! Compute source vector - call prod % vector_multiply(phi_o, s_o) - - ! Normalize source vector - s_o % val = s_o % val / k_lo - - ! Compute new flux vector - select case(cmfd % indices(4)) - case(1) - call cmfd_linsolver_1g(loss, s_o, phi_n, toli, innerits) - case(2) - call cmfd_linsolver_2g(loss, s_o, phi_n, toli, innerits) - case default - call cmfd_linsolver_ng(loss, s_o, phi_n, toli, innerits) - end select - - ! Compute new source vector - call prod % vector_multiply(phi_n, s_n) - - ! Compute new shifted eigenvalue - k_ln = sum(s_n % val) / sum(s_o % val) - - ! Compute new eigenvalue - k_n = ONE/(ONE/k_ln + ONE/k_s) - - ! Renormalize the old source - s_o % val = s_o % val * k_lo - - ! Check convergence - call convergence(i, innerits, iconv) - totalits = totalits + innerits - - ! Break loop if converged - if (iconv) exit - - ! Record old values - phi_o % val = phi_n % val - k_o = k_n - k_lo = k_ln - norm_o = norm_n - - ! Get new tolerance for inners - toli = max(atoli, rtoli*serr) - - end do - - end subroutine execute_power_iter - -!=============================================================================== -! WIELANDT SHIFT -!=============================================================================== - - subroutine wielandt_shift() - - use constants, only: ONE - - integer :: irow ! row counter - integer :: icol ! col counter - integer :: jcol ! current col index in prod matrix - - ! perform subtraction - jcol = 1 - ROWS: do irow = 1, loss % n - COLS: do icol = loss % get_row(irow), loss % get_row(irow + 1) - 1 - if (loss % get_col(icol) == prod % get_col(jcol) .and. & - jcol < prod % get_row(irow + 1)) then - loss % val(icol) = loss % val(icol) - ONE/k_s*prod % val(jcol) - jcol = jcol + 1 - end if - end do COLS - end do ROWS - - end subroutine wielandt_shift - -!=============================================================================== -! CONVERGENCE checks the convergence of the CMFD problem -!=============================================================================== - - subroutine convergence(iter, innerits, iconv) - - use, intrinsic :: ISO_FORTRAN_ENV - - use constants, only: ONE, ZERO - use cmfd_header, only: cmfd_power_monitor - use message_passing, only: master - - integer, intent(in) :: iter ! outer iteration number - integer, intent(in) :: innerits ! inner iteration nubmer - logical, intent(out) :: iconv ! convergence logical - - ! Reset convergence flag - iconv = .false. - - ! Calculate error in keff - kerr = abs(k_o - k_n)/k_n - - ! Calculate max error in source - where (s_n % val > ZERO) - serr_v % val = ((s_n % val - s_o % val)/s_n % val)**2 - end where - serr = sqrt(ONE/dble(s_n % n) * sum(serr_v % val)) - - ! Check for convergence - if(kerr < ktol .and. serr < stol) iconv = .true. - - ! Save the L2 norm of the source - norm_n = serr - - ! Print out to user - if (cmfd_power_monitor .and. master) then - write(OUTPUT_UNIT,FMT='(I0,":",T10,"k-eff: ",F0.8,T30,"k-error: ", & - &1PE12.5,T55, "src-error: ",1PE12.5,T80,I0)') iter, k_n, kerr, & - serr, innerits - end if - - end subroutine convergence - -!=============================================================================== -! CMFD_LINSOLVER_1g solves the CMFD linear system -!=============================================================================== - - subroutine cmfd_linsolver_1g(A, b, x, tol, its) - - use constants, only: ONE, ZERO - use error, only: fatal_error - use cmfd_header, only: cmfd, cmfd_spectral - - type(Matrix), intent(inout) :: A ! coefficient matrix - type(Vector), intent(inout) :: b ! right hand side vector - type(Vector), intent(inout) :: x ! unknown vector - real(8), intent(in) :: tol ! tolerance on final error - integer, intent(out) :: its ! number of inner iterations - - integer :: g ! group index - integer :: i ! loop counter for x - integer :: j ! loop counter for y - integer :: k ! loop counter for z - integer :: n ! total size of vector - integer :: nx ! maximum dimension in x direction - integer :: ny ! maximum dimension in y direction - integer :: nz ! maximum dimension in z direction - integer :: ng ! number of energy groups - integer :: igs ! Gauss-Seidel iteration counter - integer :: irb ! Red/Black iteration switch - integer :: irow ! row iteration - integer :: icol ! iteration counter over columns - integer :: didx ! index for diagonal component - logical :: found ! did we find col - real(8) :: tmp1 ! temporary sum g1 - real(8) :: x1 ! new g1 value of x - real(8) :: err ! error in convergence of solution - real(8) :: w ! overrelaxation parameter - type(Vector) :: tmpx ! temporary solution vector - - ! Set overrelaxation parameter - w = ONE - - ! Dimensions - ng = 1 - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - n = A % n - - ! Perform Gauss Seidel iterations - GS: do igs = 1, 10000 - - ! Check for max iterations met - if (igs == 10000) then - call fatal_error('Maximum Gauss-Seidel iterations encountered.') - endif - - ! Copy over x vector - call tmpx % copy(x) - - ! Perform red/black gs iterations - REDBLACK: do irb = 0,1 - - ! Begin loop around matrix rows - ROWS: do irow = 1, n - - ! Get spatial location - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! Filter out black cells (even) - if (mod(i+j+k,2) == irb) cycle - - ! Get the index of the diagonals for both rows - call A % search_indices(irow, irow, didx, found) - - ! Perform temporary sums, first do left of diag block, then right of diag block - tmp1 = ZERO - do icol = A % get_row(irow), didx - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - do icol = didx + 1, A % get_row(irow + 1) - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - - ! Solve for new x - x1 = (b % val(irow) - tmp1)/A % val(didx) - - ! Perform overrelaxation - x % val(irow) = (ONE - w)*x % val(irow) + w*x1 - - end do ROWS - - end do REDBLACK - - ! Check convergence - err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) - its = igs - if (err < tol) exit - - ! Calculation new overrelaxation parameter - w = ONE/(ONE - 0.25_8*cmfd_spectral*w) - - end do GS - - call tmpx % destroy() - - end subroutine cmfd_linsolver_1g - -!=============================================================================== -! CMFD_LINSOLVER_2G solves the CMFD linear system -!=============================================================================== - - subroutine cmfd_linsolver_2g(A, b, x, tol, its) - - use constants, only: ONE, ZERO - use error, only: fatal_error - use cmfd_header, only: cmfd, cmfd_spectral - - type(Matrix), intent(inout) :: A ! coefficient matrix - type(Vector), intent(inout) :: b ! right hand side vector - type(Vector), intent(inout) :: x ! unknown vector - real(8), intent(in) :: tol ! tolerance on final error - integer, intent(out) :: its ! number of inner iterations - - integer :: g ! group index - integer :: i ! loop counter for x - integer :: j ! loop counter for y - integer :: k ! loop counter for z - integer :: n ! total size of vector - integer :: nx ! maximum dimension in x direction - integer :: ny ! maximum dimension in y direction - integer :: nz ! maximum dimension in z direction - integer :: ng ! number of energy groups - integer :: d1idx ! index of row "1" diagonal - integer :: d2idx ! index of row "2" diagonal - integer :: igs ! Gauss-Seidel iteration counter - integer :: irb ! Red/Black iteration switch - integer :: irow ! row iteration - integer :: icol ! iteration counter over columns - logical :: found ! did we find col - real(8) :: m11 ! block diagonal component 1,1 - real(8) :: m12 ! block diagonal component 1,2 - real(8) :: m21 ! block diagonal component 2,1 - real(8) :: m22 ! block diagonal component 2,2 - real(8) :: dm ! determinant of block diagonal - real(8) :: d11 ! inverse component 1,1 - real(8) :: d12 ! inverse component 1,2 - real(8) :: d21 ! inverse component 2,1 - real(8) :: d22 ! inverse component 2,2 - real(8) :: tmp1 ! temporary sum g1 - real(8) :: tmp2 ! temporary sum g2 - real(8) :: x1 ! new g1 value of x - real(8) :: x2 ! new g2 value of x - real(8) :: err ! error in convergence of solution - real(8) :: w ! overrelaxation parameter - type(Vector) :: tmpx ! temporary solution vector - - ! Set tolerance and overrelaxation parameter - w = ONE - - ! Dimensions - ng = 2 - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - n = A % n - - ! Perform Gauss Seidel iterations - GS: do igs = 1, 10000 - - ! Check for max iterations met - if (igs == 10000) then - call fatal_error('Maximum Gauss-Seidel iterations encountered.') - endif - - ! Copy over x vector - call tmpx % copy(x) - - ! Perform red/black gs iterations - REDBLACK: do irb = 0,1 - - ! Begin loop around matrix rows - ROWS: do irow = 1, n, 2 - - ! Get spatial location - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! Filter out black cells (even) - if (mod(i+j+k,2) == irb) cycle - - ! Get the index of the diagonals for both rows - call A % search_indices(irow, irow, d1idx, found) - call A % search_indices(irow + 1, irow + 1, d2idx, found) - - ! Get block diagonal - m11 = A % val(d1idx) ! group 1 diagonal - m12 = A % val(d1idx + 1) ! group 1 right of diagonal (sorted by col) - m21 = A % val(d2idx - 1) ! group 2 left of diagonal (sorted by col) - m22 = A % val(d2idx) ! group 2 diagonal - - ! Analytically invert the diagonal - dm = m11*m22 - m12*m21 - d11 = m22/dm - d12 = -m12/dm - d21 = -m21/dm - d22 = m11/dm - - ! Perform temporary sums, first do left of diag block, then right of diag block - tmp1 = ZERO - tmp2 = ZERO - do icol = A % get_row(irow), d1idx - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - do icol = A % get_row(irow + 1), d2idx - 2 - tmp2 = tmp2 + A % val(icol)*x % val(A % get_col(icol)) - end do - do icol = d1idx + 2, A % get_row(irow + 1) - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - do icol = d2idx + 1, A % get_row(irow + 2) - 1 - tmp2 = tmp2 + A % val(icol)*x % val(A % get_col(icol)) - end do - - ! Adjust with RHS vector - tmp1 = b % val(irow) - tmp1 - tmp2 = b % val(irow + 1) - tmp2 - - ! Solve for new x - x1 = d11*tmp1 + d12*tmp2 - x2 = d21*tmp1 + d22*tmp2 - - ! Perform overrelaxation - x % val(irow) = (ONE - w)*x % val(irow) + w*x1 - x % val(irow + 1) = (ONE - w)*x % val(irow + 1) + w*x2 - - end do ROWS - - end do REDBLACK - - ! Check convergence - err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) - its = igs - if (err < tol) exit - - ! Calculation new overrelaxation parameter - w = ONE/(ONE - 0.25_8*cmfd_spectral*w) - - end do GS - - call tmpx % destroy() - - end subroutine cmfd_linsolver_2g - -!=============================================================================== -! CMFD_LINSOLVER_ng solves the CMFD linear system -!=============================================================================== - - subroutine cmfd_linsolver_ng(A, b, x, tol, its) - - use constants, only: ONE, ZERO - use error, only: fatal_error - use cmfd_header, only: cmfd, cmfd_spectral - - type(Matrix), intent(inout) :: A ! coefficient matrix - type(Vector), intent(inout) :: b ! right hand side vector - type(Vector), intent(inout) :: x ! unknown vector - real(8), intent(in) :: tol ! tolerance on final error - integer, intent(out) :: its ! number of inner iterations - - integer :: g ! group index - integer :: i ! loop counter for x - integer :: j ! loop counter for y - integer :: k ! loop counter for z - integer :: n ! total size of vector - integer :: nx ! maximum dimension in x direction - integer :: ny ! maximum dimension in y direction - integer :: nz ! maximum dimension in z direction - integer :: ng ! number of energy groups - integer :: igs ! Gauss-Seidel iteration counter - integer :: irow ! row iteration - integer :: icol ! iteration counter over columns - integer :: didx ! index for diagonal component - logical :: found ! did we find col - real(8) :: tmp1 ! temporary sum g1 - real(8) :: x1 ! new g1 value of x - real(8) :: err ! error in convergence of solution - real(8) :: w ! overrelaxation parameter - type(Vector) :: tmpx ! temporary solution vector - - ! Set overrelaxation parameter - w = ONE - - ! Dimensions - ng = 1 - nx = cmfd % indices(1) - ny = cmfd % indices(2) - nz = cmfd % indices(3) - n = A % n - - ! Perform Gauss Seidel iterations - GS: do igs = 1, 10000 - - ! Check for max iterations met - if (igs == 10000) then - call fatal_error('Maximum Gauss-Seidel iterations encountered.') - endif - - ! Copy over x vector - call tmpx % copy(x) - - ! Begin loop around matrix rows - ROWS: do irow = 1, n - - ! Get spatial location - call matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - ! Get the index of the diagonals for both rows - call A % search_indices(irow, irow, didx, found) - - ! Perform temporary sums, first do left of diag block, then right of diag block - tmp1 = ZERO - do icol = A % get_row(irow), didx - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - do icol = didx + 1, A % get_row(irow + 1) - 1 - tmp1 = tmp1 + A % val(icol)*x % val(A % get_col(icol)) - end do - - ! Solve for new x - x1 = (b % val(irow) - tmp1)/A % val(didx) - - ! Perform overrelaxation - x % val(irow) = (ONE - w)*x % val(irow) + w*x1 - - end do ROWS - - ! Check convergence - err = sqrt(sum(((tmpx % val - x % val)/tmpx % val)**2)/n) - its = igs - - if (err < tol) exit - - ! Calculation new overrelaxation parameter - w = ONE/(ONE - 0.25_8*cmfd_spectral*w) - - end do GS - - call tmpx % destroy() - - end subroutine cmfd_linsolver_ng - -!=============================================================================== -! EXTRACT_RESULTS takes results and puts them in CMFD global data object -!=============================================================================== - - subroutine extract_results() - - use cmfd_header, only: cmfd, cmfd_write_matrices - use simulation_header, only: current_batch - - character(len=25) :: filename ! name of file to write data - integer :: n ! problem size - - ! Get problem size - n = loss % n - - ! Allocate in cmfd object if not already allocated - if (adjoint_calc) then - if (.not. allocated(cmfd%adj_phi)) allocate(cmfd%adj_phi(n)) - else - if (.not. allocated(cmfd%phi)) allocate(cmfd%phi(n)) - end if - - ! Save values - if (adjoint_calc) then - cmfd % adj_phi = phi_n % val - else - cmfd % phi = phi_n % val - end if - - ! Save eigenvalue - if(adjoint_calc) then - cmfd%adj_keff = k_n - else - cmfd%keff = k_n - end if - - ! Normalize phi to 1 - if (adjoint_calc) then - cmfd%adj_phi = cmfd%adj_phi/sqrt(sum(cmfd%adj_phi*cmfd%adj_phi)) - else - cmfd%phi = cmfd%phi/sqrt(sum(cmfd%phi*cmfd%phi)) - end if - - ! Save dominance ratio - cmfd % dom(current_batch) = norm_n/norm_o - - ! Write out results - if (cmfd_write_matrices) then - if (adjoint_calc) then - filename = 'adj_fluxvec.dat' - else - filename = 'fluxvec.dat' - end if - call phi_n % write(filename) - end if - - end subroutine extract_results - -!=============================================================================== -! MATRIX_TO_INDICES converts a matrix index to spatial and group indicies -!=============================================================================== - - subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz) - - use cmfd_header, only: cmfd, cmfd_coremap - - integer, intent(out) :: i ! iteration counter for x - integer, intent(out) :: j ! iteration counter for y - integer, intent(out) :: k ! iteration counter for z - integer, intent(out) :: g ! iteration counter for groups - integer, intent(in) :: irow ! iteration counter over row (0 reference) - integer, intent(in) :: nx ! maximum number of x cells - integer, intent(in) :: ny ! maximum number of y cells - integer, intent(in) :: nz ! maximum number of z cells - integer, intent(in) :: ng ! maximum number of groups - - ! Check for core map - if (cmfd_coremap) then - - ! Get indices from indexmap - g = mod(irow-1, ng) + 1 - i = cmfd % indexmap((irow-1)/ng+1,1) - j = cmfd % indexmap((irow-1)/ng+1,2) - k = cmfd % indexmap((irow-1)/ng+1,3) - - else - - ! Compute indices - g = mod(irow-1, ng) + 1 - i = mod(irow-1, ng*nx)/ng + 1 - j = mod(irow-1, ng*nx*ny)/(ng*nx)+ 1 - k = mod(irow-1, ng*nx*ny*nz)/(ng*nx*ny) + 1 - - end if - - end subroutine matrix_to_indices - -!=============================================================================== -! FINALIZE frees all memory associated with power iteration -!=============================================================================== - - subroutine finalize() - - ! Destroy all objects - call loss % destroy() - call prod % destroy() - call phi_n % destroy() - call phi_o % destroy() - call s_n % destroy() - call s_o % destroy() - call serr_v % destroy - - end subroutine finalize - -end module cmfd_solver diff --git a/src/constants.F90 b/src/constants.F90 index a9812e4688..0e667c32a3 100644 --- a/src/constants.F90 +++ b/src/constants.F90 @@ -439,18 +439,6 @@ module constants ELECTRON_LED = 1, & ! Local Energy Deposition ELECTRON_TTB = 2 ! Thick Target Bremsstrahlung - !============================================================================= - ! CMFD CONSTANTS - - ! for non-accelerated regions on coarse mesh overlay - integer, parameter :: CMFD_NOACCEL = 99999 - - ! constant to represent a zero flux "albedo" - real(8), parameter :: ZERO_FLUX = 999.0_8 - - ! constant for writing out no residual - real(8), parameter :: CMFD_NORES = 99999.0_8 - !============================================================================= ! DELAYED NEUTRON PRECURSOR CONSTANTS diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 1da1bf9df6..32fd9f2a14 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -3,8 +3,6 @@ module input_xml use, intrinsic :: ISO_C_BINDING use algorithm, only: find - use cmfd_input, only: configure_cmfd - use cmfd_header, only: index_cmfd_mesh use constants use dict_header, only: DictIntInt, DictCharInt, DictEntryCI use endf, only: reaction_name @@ -153,8 +151,6 @@ contains ! Initialize distribcell_filters call prepare_distribcell() - if (cmfd_run) call configure_cmfd() - if (run_mode == MODE_PLOTTING) then ! Read plots.xml if it exists call read_plots_xml() diff --git a/src/output.F90 b/src/output.F90 index 485d957a6e..d2130a87a9 100644 --- a/src/output.F90 +++ b/src/output.F90 @@ -3,7 +3,6 @@ module output use, intrinsic :: ISO_C_BINDING use, intrinsic :: ISO_FORTRAN_ENV - use cmfd_header use constants use eigenvalue, only: openmc_get_keff use endf, only: reaction_name @@ -290,30 +289,12 @@ contains write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') " k " if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "Entropy " write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') " Average k " - if (cmfd_run) then - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') " CMFD k " - select case(trim(cmfd_display)) - case('entropy') - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "CMFD Ent" - case('balance') - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "RMS Bal " - case('source') - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "RMS Src " - case('dominance') - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "Dom Rat " - end select - end if write(UNIT=ou, FMT=*) write(UNIT=ou, FMT='(2X,A9,3X)', ADVANCE='NO') "=========" write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" if (entropy_on) write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" write(UNIT=ou, FMT='(A20,3X)', ADVANCE='NO') "====================" - if (cmfd_run) then - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" - if (cmfd_display /= '') & - write(UNIT=ou, FMT='(A8,3X)', ADVANCE='NO') "========" - end if write(UNIT=ou, FMT=*) end subroutine print_columns @@ -386,26 +367,6 @@ contains write(UNIT=OUTPUT_UNIT, FMT='(23X)', ADVANCE='NO') end if - ! write out cmfd keff if it is active and other display info - if (cmfd_on) then - write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % k_cmfd(current_batch) - select case(trim(cmfd_display)) - case('entropy') - write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % entropy(current_batch) - case('balance') - write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % balance(current_batch) - case('source') - write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % src_cmp(current_batch) - case('dominance') - write(UNIT=OUTPUT_UNIT, FMT='(3X, F8.5)', ADVANCE='NO') & - cmfd % dom(current_batch) - end select - end if - ! next line write(UNIT=OUTPUT_UNIT, FMT=*) @@ -442,11 +403,6 @@ contains write(ou,100) " SEND/RECV source sites", time_bank_sendrecv_elapsed() end if write(ou,100) " Time accumulating tallies", time_tallies_elapsed() - if (cmfd_run) write(ou,100) " Time in CMFD", time_cmfd % elapsed - if (cmfd_run) write(ou,100) " Building matrices", & - time_cmfdbuild % elapsed - if (cmfd_run) write(ou,100) " Solving matrices", & - time_cmfdsolve % elapsed write(ou,100) "Total time for finalization", time_finalize_elapsed() write(ou,100) "Total time elapsed", time_total_elapsed() diff --git a/src/relaxng/cmfd.rnc b/src/relaxng/cmfd.rnc deleted file mode 100644 index dde97f971e..0000000000 --- a/src/relaxng/cmfd.rnc +++ /dev/null @@ -1,53 +0,0 @@ -element cmfd { - element mesh { - (element dimension { list { xsd:int+ } } | - attribute dimension { list { xsd:int+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) & - (element albedo { list { xsd:double+ } } | - attribute albedo { list { xsd:double+ } }) & - (element map { list { xsd:int+ } } | - attribute map { list { xsd:int+ } })? & - (element energy { list { xsd:double+ } } | - attribute energy { list { xsd:double+ } })? - } & - - element norm { xsd:double }? & - - element feedback { xsd:boolean }? & - - element downscatter { xsd:boolean }? & - - element dhat_reset { xsd:boolean }? & - - element power_monitor { xsd:boolean }? & - - element write_matrices { xsd:boolean }? & - - element run_adjoint { xsd:boolean }? & - - element write_hdf5 { xsd:boolean }? & - - element begin { xsd:int }? & - - element tally_reset { list { xsd:int+ } }? & - - element display { xsd:string }? & - - element spectral { xsd:double }? & - - element shift { xsd:double }? & - - element ktol { xsd:double }? & - - element stol { xsd:double }? & - - element gauss_seidel_tolerance { list { xsd:double+ } }? - -} diff --git a/src/relaxng/cmfd.rng b/src/relaxng/cmfd.rng deleted file mode 100644 index dc096ab87e..0000000000 --- a/src/relaxng/cmfd.rng +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng index 92a1d748ab..3ed15c3ed4 100644 --- a/src/relaxng/settings.rng +++ b/src/relaxng/settings.rng @@ -259,11 +259,6 @@ - - - - - diff --git a/src/settings.F90 b/src/settings.F90 index 6c8a610361..16f35341c7 100644 --- a/src/settings.F90 +++ b/src/settings.F90 @@ -115,9 +115,6 @@ module settings real(C_DOUBLE), bind(C) :: res_scat_energy_min real(C_DOUBLE), bind(C) :: res_scat_energy_max - ! Is CMFD active - logical(C_BOOL), bind(C) :: cmfd_run - ! No reduction at end of batch logical(C_BOOL), bind(C) :: reduce_tallies diff --git a/src/simulation.cpp b/src/simulation.cpp index 50bf12521c..4a4f632ebf 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -21,15 +21,10 @@ namespace openmc { // data/functions from Fortran side -extern "C" bool cmfd_on; - extern "C" void accumulate_tallies(); extern "C" void allocate_banks(); extern "C" void allocate_tally_results(); extern "C" void check_triggers(); -extern "C" void cmfd_init_batch(); -extern "C" void cmfd_tally_init(); -extern "C" void execute_cmfd(); extern "C" void init_tally_routines(); extern "C" void join_bank_from_threads(); extern "C" void load_state_point(); @@ -94,9 +89,6 @@ int openmc_simulation_init() // Allocate tally results arrays if they're not allocated yet allocate_tally_results(); - // Activate the CMFD tallies - cmfd_tally_init(); - // Call Fortran initialization simulation_init_f(); @@ -316,11 +308,6 @@ void initialize_batch() } } - // check CMFD initialize batch - if (settings::run_mode == RUN_MODE_EIGENVALUE) { - if (settings::cmfd_run) cmfd_init_batch(); - } - // Add user tallies to active tallies list setup_active_tallies(); } @@ -340,8 +327,6 @@ void finalize_batch() } if (settings::run_mode == RUN_MODE_EIGENVALUE) { - // Perform CMFD calculation if on - if (cmfd_on) execute_cmfd(); // Write batch output if (mpi::master && settings::verbosity >= 7) print_batch_keff(); } diff --git a/src/state_point.F90 b/src/state_point.F90 index 2692856dc0..0d33d52d1c 100644 --- a/src/state_point.F90 +++ b/src/state_point.F90 @@ -14,7 +14,6 @@ module state_point use, intrinsic :: ISO_C_BINDING use bank_header, only: Bank - use cmfd_header use constants use endf, only: reaction_name use error, only: fatal_error, warning, write_message @@ -64,7 +63,7 @@ contains integer :: i_xs integer, allocatable :: id_array(:) integer(HID_T) :: file_id - integer(HID_T) :: cmfd_group, tallies_group, tally_group, & + integer(HID_T) :: tallies_group, tally_group, & filters_group, filter_group, derivs_group, & deriv_group, runtime_group character(MAX_WORD_LEN), allocatable :: str_array(:) @@ -169,23 +168,6 @@ contains ! Write out information for eigenvalue run if (run_mode == MODE_EIGENVALUE) then call write_eigenvalue_hdf5(file_id) - - ! Write out CMFD info - if (cmfd_on) then - call write_attribute(file_id, "cmfd_on", 1) - - cmfd_group = create_group(file_id, "cmfd") - call write_dataset(cmfd_group, "indices", cmfd % indices) - call write_dataset(cmfd_group, "k_cmfd", cmfd % k_cmfd) - call write_dataset(cmfd_group, "cmfd_src", cmfd % cmfd_src) - call write_dataset(cmfd_group, "cmfd_entropy", cmfd % entropy) - call write_dataset(cmfd_group, "cmfd_balance", cmfd % balance) - call write_dataset(cmfd_group, "cmfd_dominance", cmfd % dom) - call write_dataset(cmfd_group, "cmfd_srccmp", cmfd % src_cmp) - call close_group(cmfd_group) - else - call write_attribute(file_id, "cmfd_on", 0) - end if end if tallies_group = create_group(file_id, "tallies") @@ -413,13 +395,6 @@ contains end if call write_dataset(runtime_group, "accumulating tallies", & time_tallies_elapsed()) - if (cmfd_run) then - call write_dataset(runtime_group, "CMFD", time_cmfd % get_value()) - call write_dataset(runtime_group, "CMFD building matrices", & - time_cmfdbuild % get_value()) - call write_dataset(runtime_group, "CMFD solving matrices", & - time_cmfdsolve % get_value()) - end if call write_dataset(runtime_group, "total", time_total_elapsed()) call close_group(runtime_group) @@ -453,7 +428,6 @@ contains integer, allocatable :: array(:) integer(C_INT64_T) :: seed integer(HID_T) :: file_id - integer(HID_T) :: cmfd_group integer(HID_T) :: tallies_group integer(HID_T) :: tally_group logical :: source_present @@ -548,26 +522,6 @@ contains ! Take maximum of statepoint n_inactive and input n_inactive n_inactive = max(n_inactive, int_array(1)) - - ! Read in to see if CMFD was on - call read_attribute(int_array(1), file_id, "cmfd_on") - - ! Read in CMFD info - if (int_array(1) == 1) then - cmfd_group = open_group(file_id, "cmfd") - call read_dataset(cmfd % indices, cmfd_group, "indices") - call read_dataset(cmfd % k_cmfd(1:restart_batch), cmfd_group, "k_cmfd") - call read_dataset(cmfd % cmfd_src, cmfd_group, "cmfd_src") - call read_dataset(cmfd % entropy(1:restart_batch), cmfd_group, & - "cmfd_entropy") - call read_dataset(cmfd % balance(1:restart_batch), cmfd_group, & - "cmfd_balance") - call read_dataset(cmfd % dom(1:restart_batch), cmfd_group, & - "cmfd_dominance") - call read_dataset(cmfd % src_cmp(1:restart_batch), cmfd_group, & - "cmfd_srccmp") - call close_group(cmfd_group) - end if end if ! Read number of realizations for global tallies From b4a2687af95526a0decb8518505935fa30b06dcb Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Sat, 24 Nov 2018 02:09:42 -0500 Subject: [PATCH 47/58] Update CMFD test suite --- tests/regression_tests/cmfd_feed/cmfd.xml | 15 ------ .../cmfd_feed/results_true.dat | 34 +++--------- tests/regression_tests/cmfd_feed/test.py | 41 +++++++++++++- tests/regression_tests/cmfd_nofeed/cmfd.xml | 16 ------ .../cmfd_nofeed/results_true.dat | 34 +++--------- tests/regression_tests/cmfd_nofeed/test.py | 41 +++++++++++++- tests/testing_harness.py | 54 ++++++++++--------- tests/unit_tests/test_settings.py | 1 - 8 files changed, 120 insertions(+), 116 deletions(-) delete mode 100644 tests/regression_tests/cmfd_feed/cmfd.xml delete mode 100644 tests/regression_tests/cmfd_nofeed/cmfd.xml diff --git a/tests/regression_tests/cmfd_feed/cmfd.xml b/tests/regression_tests/cmfd_feed/cmfd.xml deleted file mode 100644 index ae50243c03..0000000000 --- a/tests/regression_tests/cmfd_feed/cmfd.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - -10 -1 -1 - 10 1 1 - 10 1 1 - 0.0 0.0 1.0 1.0 1.0 1.0 - - - 5 - dominance - true - 1.e-15 1.e-20 - diff --git a/tests/regression_tests/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index aba219ba83..c175dd7db9 100644 --- a/tests/regression_tests/cmfd_feed/results_true.dat +++ b/tests/regression_tests/cmfd_feed/results_true.dat @@ -391,10 +391,6 @@ cmfd indices 1.000000E+00 1.000000E+00 k cmfd -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.170416E+00 1.172966E+00 1.165537E+00 @@ -412,10 +408,6 @@ k cmfd 1.167848E+00 1.165116E+00 cmfd entropy -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.203643E+00 3.207943E+00 3.213367E+00 @@ -433,10 +425,6 @@ cmfd entropy 3.233193E+00 3.232564E+00 cmfd balance -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.009062E-03 4.431773E-03 3.152666E-03 @@ -446,18 +434,14 @@ cmfd balance 1.502427E-03 1.589825E-03 1.566020E-03 -1.219160E-03 -1.017888E-03 -9.771622E-04 -1.010120E-03 -1.073382E-03 -1.172758E-03 -9.827332E-04 +1.170009E-03 +9.507594E-04 +9.072590E-04 +1.009003E-03 +1.064703E-03 +1.163610E-03 +9.756308E-04 cmfd dominance ratio - 0.000E+00 - 0.000E+00 - 0.000E+00 - 0.000E+00 5.397E-01 5.425E-01 5.481E-01 @@ -475,10 +459,6 @@ cmfd dominance ratio 5.524E-01 5.523E-01 cmfd openmc source comparison -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.959834E-03 5.655657E-03 3.886185E-03 diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 4fc39d96ad..368e81aee2 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -1,6 +1,43 @@ from tests.testing_harness import CMFDTestHarness - +import openmc +import numpy as np def test_cmfd_feed(): - harness = CMFDTestHarness('statepoint.20.h5') + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -10.0, -1.0, -1.0 + cmfd_mesh.upper_right = 10.0, 1.0, 1.0 + cmfd_mesh.dimension = 10, 1, 1 + cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.cmfd_mesh = cmfd_mesh + cmfd_run.cmfd_begin = 5 + cmfd_run.cmfd_display = 'dominance' + cmfd_run.cmfd_feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Create output string of all CMFD results to pass into testing harness + outstr = 'cmfd indices\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\nk cmfd\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\ncmfd entropy\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\ncmfd balance\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\ncmfd dominance ratio\n' + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\ncmfd openmc source comparison\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\ncmfd source\n' + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + order='F') + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) + outstr += '\n' + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/cmfd.xml b/tests/regression_tests/cmfd_nofeed/cmfd.xml deleted file mode 100644 index 2e13cff6b8..0000000000 --- a/tests/regression_tests/cmfd_nofeed/cmfd.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -10 -1 -1 - 10 1 1 - 10 1 1 - 0.0 0.0 1.0 1.0 1.0 1.0 - - - 5 - dominance - false - 1.e-15 1.e-20 - - diff --git a/tests/regression_tests/cmfd_nofeed/results_true.dat b/tests/regression_tests/cmfd_nofeed/results_true.dat index 23368457d0..33dff8811f 100644 --- a/tests/regression_tests/cmfd_nofeed/results_true.dat +++ b/tests/regression_tests/cmfd_nofeed/results_true.dat @@ -391,10 +391,6 @@ cmfd indices 1.000000E+00 1.000000E+00 k cmfd -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 1.170416E+00 1.172572E+00 1.171159E+00 @@ -412,10 +408,6 @@ k cmfd 1.153714E+00 1.159485E+00 cmfd entropy -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 3.203643E+00 3.204555E+00 3.210935E+00 @@ -433,10 +425,6 @@ cmfd entropy 3.221580E+00 3.220523E+00 cmfd balance -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 4.009062E-03 4.869661E-03 2.997290E-03 @@ -446,18 +434,14 @@ cmfd balance 1.403979E-03 1.398429E-03 1.818398E-03 -1.761250E-03 -1.646649E-03 -1.480119E-03 -1.399559E-03 -1.400162E-03 -1.178363E-03 -1.292280E-03 +1.458254E-03 +1.437876E-03 +1.276844E-03 +1.289036E-03 +1.330300E-03 +1.139999E-03 +1.233647E-03 cmfd dominance ratio - 0.000E+00 - 0.000E+00 - 0.000E+00 - 0.000E+00 5.397E-01 5.405E-01 5.412E-01 @@ -475,10 +459,6 @@ cmfd dominance ratio 5.461E-01 5.443E-01 cmfd openmc source comparison -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 6.959834E-03 5.494667E-03 4.076255E-03 diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index a3c7301738..4e34029a9e 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -1,6 +1,43 @@ from tests.testing_harness import CMFDTestHarness - +import openmc +import numpy as np def test_cmfd_nofeed(): - harness = CMFDTestHarness('statepoint.20.h5') + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -10.0, -1.0, -1.0 + cmfd_mesh.upper_right = 10.0, 1.0, 1.0 + cmfd_mesh.dimension = 10, 1, 1 + cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.cmfd_mesh = cmfd_mesh + cmfd_run.cmfd_begin = 5 + cmfd_run.cmfd_display = 'dominance' + cmfd_run.cmfd_feedback = False + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Create output string of all CMFD results to pass into testing harness + outstr = 'cmfd indices\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\nk cmfd\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\ncmfd entropy\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\ncmfd balance\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\ncmfd dominance ratio\n' + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\ncmfd openmc source comparison\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\ncmfd source\n' + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + order='F') + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) + outstr += '\n' + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index fb07575ce7..50f02ee63c 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -133,35 +133,37 @@ class HashedTestHarness(TestHarness): class CMFDTestHarness(TestHarness): """Specialized TestHarness for running OpenMC CMFD tests.""" - def _get_results(self): - """Digest info in the statepoint and return as a string.""" + def __init__(self, statepoint_name, cmfd_results=None): + self._sp_name = statepoint_name + self._cmfd_results = cmfd_results - # Write out the eigenvalue and tallies. - outstr = super()._get_results() + def execute_test(self): + """Don't call _run_openmc as OpenMC will be called through C API for + CMFD tests, and write CMFD results that were passsed as argument - # Read the statepoint file. - statepoint = glob.glob(self._sp_name)[0] - with openmc.StatePoint(statepoint) as sp: - # Write out CMFD data. - outstr += 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in sp.cmfd_indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in sp.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in sp.cmfd_entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in sp.cmfd_balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in sp.cmfd_dominance]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in sp.cmfd_srccmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(sp.cmfd_src, np.product(sp.cmfd_indices), - order='F') - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' + """ + try: + self._test_output_created() + results = self._get_results() + results += self._cmfd_results + self._write_results(results) + self._compare_results() + finally: + self._cleanup() - return outstr + def update_results(self): + """Don't call _run_openmc as OpenMC will be called through C API for + CMFD tests, and write CMFD results that were passsed as argument + + """ + try: + self._test_output_created() + results = self._get_results() + results += self._cmfd_results + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() class ParticleRestartTestHarness(TestHarness): diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 30ec0e683c..a78f074c99 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -21,7 +21,6 @@ def test_export_to_xml(run_in_tmpdir): s.confidence_intervals = True s.cross_sections = '/path/to/cross_sections.xml' s.ptables = True - s.run_cmfd = False s.seed = 17 s.survival_biasing = True s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy': 1.0e-5} From 2874b4a2f9fd4e982489963091c6b77d4aff0e90 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 11 Dec 2018 18:00:06 -0500 Subject: [PATCH 48/58] Address @paulromano and @smharper comments --- docs/source/pythonapi/base.rst | 31 +- include/openmc/capi.h | 25 + include/openmc/cmfd_solver.h | 113 -- openmc/capi/core.py | 15 + openmc/cmfd.py | 1379 ++++++++--------- src/cmfd_solver.cpp | 165 +- src/relaxng/settings.rnc | 2 - src/settings.cpp | 5 - tests/regression_tests/cmfd_feed/settings.xml | 3 - .../regression_tests/cmfd_nofeed/settings.xml | 3 - 10 files changed, 782 insertions(+), 959 deletions(-) delete mode 100644 include/openmc/cmfd_solver.h diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 5202ea591d..d3c53d1738 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -201,6 +201,10 @@ Various classes may be created when performing tally slicing and/or arithmetic: Coarse Mesh Finite Difference Acceleration ------------------------------------------ +CMFD is implemented in OpenMC and allows users to accelerate fission source +convergence during inactive neutron batches. To run CMFD, the CMFDRun class +should be used. + .. autosummary:: :toctree: generated :nosignatures: @@ -209,33 +213,6 @@ Coarse Mesh Finite Difference Acceleration openmc.CMFDMesh openmc.CMFDRun -CMFD is implemented in OpenMC and allows users to accelerate fission source -convergence during inactive neutron batches. To run CMFD, the CMFDRun class should -be used. The following properties can be set through the CMFDRun class: - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: myfunction.rst - - openmc.CMFDRun.cmfd_begin - openmc.CMFDRun.dhat_reset - openmc.CMFDRun.cmfd_display - openmc.CMFDRun.cmfd_downscatter - openmc.CMFDRun.cmfd_feedback - openmc.CMFDRun.cmfd_ktol - openmc.CMFDRun.cmfd_mesh - openmc.CMFDRun.norm - openmc.CMFDRun.cmfd_adjoint_type - openmc.CMFDRun.cmfd_power_monitor - openmc.CMFDRun.cmfd_run_adjoint - openmc.CMFDRun.cmfd_shift - openmc.CMFDRun.cmfd_stol - openmc.CMFDRun.cmfd_spectral - openmc.CMFDRun.cmfd_reset - openmc.CMFDRun.cmfd_write_matrices - openmc.CMFDRun.gauss_seidel_tolerance - At the minimum, a CMFD mesh needs to be specified in order to run CMFD. Once these properties are set, an OpenMC simulation can be run with CMFD turned on with the function: diff --git a/include/openmc/capi.h b/include/openmc/capi.h index fdea48e9e1..ca2b0f3ab9 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -120,6 +120,31 @@ extern "C" { int openmc_zernike_filter_set_params(int32_t index, const double* x, const double* y, const double* r); + //! Sets the fixed variables that are used for CMFD linear solver + //! \param[in] CSR format index pointer array of loss matrix + //! \param[in] length of indptr + //! \param[in] CSR format index array of loss matrix + //! \param[in] number of non-zero elements in CMFD loss matrix + //! \param[in] dimension n of nxn CMFD loss matrix + //! \param[in] spectral radius of CMFD matrices and tolerances + //! \param[in] indices storing spatial and energy dimensions of CMFD problem + //! \param[in] coremap for problem, storing accelerated regions + extern "C" void openmc_initialize_linsolver(const int* indptr, int len_indptr, + const int* indices, int n_elements, + int dim, double spectral, + const int* cmfd_indices, + const int* map); + + //! Runs a Gauss Seidel linear solver to solve CMFD matrix equations + //! linear solver + //! \param[in] CSR format data array of coefficient matrix + //! \param[in] right hand side vector + //! \param[out] unknown vector + //! \param[in] tolerance on final error + //! \return number of inner iterations required to reach convergence + extern "C" int openmc_run_linsolver(const double* A_data, const double* b, + double* x, double tol); + // Error codes extern int OPENMC_E_UNASSIGNED; extern int OPENMC_E_ALLOCATE; diff --git a/include/openmc/cmfd_solver.h b/include/openmc/cmfd_solver.h deleted file mode 100644 index 47400b2a83..0000000000 --- a/include/openmc/cmfd_solver.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef OPENMC_CMFD_SOLVER_H -#define OPENMC_CMFD_SOLVER_H - -#include - -#include "xtensor/xtensor.hpp" - -namespace openmc { - -//=============================================================================== -// Global variables -//=============================================================================== - -// CSR format index pointer array of loss matrix -extern std::vector indptr; - -// CSR format index array of loss matrix -extern std::vector indices; - -// Dimension n of nxn CMFD loss matrix -extern int dim; - -// Spectral radius of CMFD matrices and tolerances -extern double spectral; - -// Maximum dimension in x, y, and z directions -extern int nx; -extern int ny; -extern int nz; - -// Number of energy groups -extern int ng; - -// Indexmap storing all x, y, z positions of accelerated regions -extern xt::xtensor indexmap; - -//=============================================================================== -// Non-member functions -//=============================================================================== - -//! returns the index in CSR index array corresponding to the diagonal element -//! of a specified row -//! \param[in] row of interest -//! \return index in CSR index array corresponding to diagonal element -int get_diagonal_index(int row); - -//! sets the elements of indexmap based on input coremap -//! \param[in] user-defined coremap -void set_indexmap(int* coremap); - -//! solves a one group CMFD linear system -//! \param[in] CSR format data array of coefficient matrix -//! \param[in] right hand side vector -//! \param[out] unknown vector -//! \param[in] tolerance on final error -//! \return number of inner iterations required to reach convergence -int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol); - -//! solves a two group CMFD linear system -//! \param[in] CSR format data array of coefficient matrix -//! \param[in] right hand side vector -//! \param[out] unknown vector -//! \param[in] tolerance on final error -//! \return number of inner iterations required to reach convergence -int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol); - -//! solves a general CMFD linear system -//! \param[in] CSR format data array of coefficient matrix -//! \param[in] right hand side vector -//! \param[out] unknown vector -//! \param[in] tolerance on final error -//! \return number of inner iterations required to reach convergence -int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol); - -//! converts a matrix index to spatial and group indices -//! \param[in] iteration counter over row -//! \param[out] iteration counter for groups -//! \param[out] iteration counter for x -//! \param[out] iteration counter for y -//! \param[out] iteration counter for z -void matrix_to_indices(int irow, int& g, int& i, int& j, int& k); - -//=============================================================================== -// External functions -//=============================================================================== - -//! sets the fixed variables that are used for the linear solver -//! \param[in] CSR format index pointer array of loss matrix -//! \param[in] length of indptr -//! \param[in] CSR format index array of loss matrix -//! \param[in] number of non-zero elements in CMFD loss matrix -//! \param[in] dimension n of nxn CMFD loss matrix -//! \param[in] spectral radius of CMFD matrices and tolerances -//! \param[in] indices storing spatial and energy dimensions of CMFD problem -//! \param[in] coremap for problem, storing accelerated regions -extern "C" void openmc_initialize_linsolver(int* indptr, int len_indptr, - int* indices, int n_elements, - int dim, double spectral, - int* cmfd_indices, int* map); - -//! runs a Gauss Seidel linear solver to solve CMFD matrix equations -//! linear solver -//! \param[in] CSR format data array of coefficient matrix -//! \param[in] right hand side vector -//! \param[out] unknown vector -//! \param[in] tolerance on final error -//! \return number of inner iterations required to reach convergence -extern "C" int openmc_run_linsolver(double* A_data, double* b, double* x, - double tol); - - -} // namespace openmc -#endif // OPENMC_CMFD_SOLVER_H \ No newline at end of file diff --git a/openmc/capi/core.py b/openmc/capi/core.py index bedde6e162..d5acded941 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -20,6 +20,13 @@ class _Bank(Structure): ('delayed_group', c_int)] +# Define input type for numpy arrays that will be passed into C++ functions +# Must be an int or double array, with single dimension that is contiguous +_array_1d_int = np.ctypeslib.ndpointer(dtype=np.int32, ndim=1, + flags='CONTIGUOUS') +_array_1d_dble = np.ctypeslib.ndpointer(dtype=np.double, ndim=1, + flags='CONTIGUOUS') + _dll.openmc_calculate_volumes.restype = c_int _dll.openmc_calculate_volumes.errcheck = _error_handler _dll.openmc_finalize.restype = c_int @@ -36,6 +43,10 @@ _dll.openmc_init.errcheck = _error_handler _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler +_init_linsolver_argtypes = [_array_1d_int, c_int, _array_1d_int, c_int, c_int, + c_double, _array_1d_int, _array_1d_int] +_dll.openmc_initialize_linsolver.argtypes = _init_linsolver_argtypes +_dll.openmc_initialize_linsolver.restype = None _dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int _dll.openmc_next_batch.errcheck = _error_handler @@ -45,6 +56,10 @@ _dll.openmc_run.restype = c_int _dll.openmc_run.errcheck = _error_handler _dll.openmc_reset.restype = c_int _dll.openmc_reset.errcheck = _error_handler +_run_linsolver_argtypes = [_array_1d_dble, _array_1d_dble, _array_1d_dble, + c_double] +_dll.openmc_run_linsolver.argtypes = _run_linsolver_argtypes +_dll.openmc_run_linsolver.restype = c_int _dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)] _dll.openmc_source_bank.restype = c_int _dll.openmc_source_bank.errcheck = _error_handler diff --git a/openmc/cmfd.py b/openmc/cmfd.py index ee9a8a1b80..bb40da618e 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -10,17 +10,20 @@ References """ -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from numbers import Real, Integral import sys -import numpy as np -# Line below is added to suppress warnings when using numpy.divide to -# divide by numpy arrays that contain entries with zero -np.seterr(divide='ignore', invalid='ignore') -import numpy.ctypeslib as npct -from scipy import sparse import time from ctypes import c_int, c_double + +import numpy as np +from scipy import sparse + +from openmc.capi import _dll, core, settings, filter, mesh, tally +from openmc.checkvalue import (check_type, check_length, check_value, + check_greater_than, check_less_than) +from openmc.exceptions import OpenMCError + # See if mpi4py module can be imported, define have_mpi global variable try: from mpi4py import MPI @@ -28,30 +31,6 @@ try: except ImportError: have_mpi = False -import openmc.capi -from openmc.checkvalue import (check_type, check_length, check_value, - check_greater_than, check_less_than) -from openmc.exceptions import OpenMCError - - -# Define input type for numpy arrays that will be passed into C++ functions -# Must be an int or double array, with single dimension that is contiguous -array_1d_int = npct.ndpointer(dtype=np.int32, ndim=1, flags='CONTIGUOUS') -array_1d_dble = npct.ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS') - -# Setup the return types and argument types for C++ functions -openmc.capi._dll.openmc_initialize_linsolver.restype = None -openmc.capi._dll.openmc_initialize_linsolver.argtypes = [array_1d_int, c_int, - array_1d_int, c_int, c_int, c_double, array_1d_int, array_1d_int] -openmc.capi._dll.openmc_run_linsolver.restype = c_int -openmc.capi._dll.openmc_run_linsolver.argtypes = [array_1d_dble, array_1d_dble, - array_1d_dble, c_double] - -""" --------------- -CMFD CONSTANTS --------------- -""" # Maximum/minimum neutron energies _ENERGY_MAX_NEUTRON = np.inf _ENERGY_MIN_NEUTRON = 0. @@ -60,28 +39,28 @@ _ENERGY_MIN_NEUTRON = 0. _TINY_BIT = 1.e-8 # For non-accelerated regions on coarse mesh overlay -_CMFD_NOACCEL = 99999 +_CMFD_NOACCEL = -1 # Constant to represent a zero flux "albedo" _ZERO_FLUX = 999.0 # Map that returns index of current direction in numpy current matrix _CURRENTS = { - 'out_left' : 0, 'in_left' : 1, 'out_right': 2, 'in_right': 3, - 'out_back' : 4, 'in_back' : 5, 'out_front': 6, 'in_front': 7, - 'out_bottom': 8, 'in_bottom': 9, 'out_top' : 10, 'in_top' : 11 + 'out_left': 0, 'in_left': 1, 'out_right': 2, 'in_right': 3, + 'out_back': 4, 'in_back': 5, 'out_front': 6, 'in_front': 7, + 'out_bottom': 8, 'in_bottom': 9, 'out_top': 10, 'in_top': 11 } class CMFDMesh(object): - """A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD) - acceleration. + """A structured Cartesian mesh used for Coarse Mesh Finite Difference + (CMFD) acceleration. Attributes ---------- lower_left : Iterable of float - The lower-left corner of the structured mesh. If only two coordinates are - given, it is assumed that the mesh is an x-y mesh. + The lower-left corner of the structured mesh. If only two coordinates + are given, it is assumed that the mesh is an x-y mesh. upper_right : Iterable of float The upper-right corner of the structrued mesh. If only two coordinates are given, it is assumed that the mesh is an x-y mesh. @@ -91,13 +70,13 @@ class CMFDMesh(object): The width of mesh cells in each direction. energy : Iterable of float Energy bins in eV, listed in ascending order (e.g. [0.0, 0.625e-1, - 20.0e6]) for CMFD tallies and acceleration. If no energy bins are listed, - OpenMC automatically assumes a one energy group calculation over the - entire energy range. + 20.0e6]) for CMFD tallies and acceleration. If no energy bins are + listed, OpenMC automatically assumes a one energy group calculation + over the entire energy range. albedo : Iterable of float Surface ratio of incoming to outgoing partial currents on global - boundary conditions. They are listed in the following order: -x +x -y +y - -z +z. + boundary conditions. They are listed in the following order: -x +x -y + +y -z +z. map : Iterable of int An optional acceleration map can be specified to overlay on the coarse mesh spatial grid. If this option is used, a ``0`` is used for a @@ -112,10 +91,10 @@ class CMFDMesh(object): 0, 1, 1, 0, 0, 0, 0, 0] - Therefore a 2x2 system of equations is solved rather than a 4x4. This is - extremely important to use in reflectors as neutrons will not contribute - to any tallies far away from fission source neutron regions. A ``1`` - must be used to identify any fission source region. + Therefore a 2x2 system of equations is solved rather than a 4x4. This + is extremely important to use in reflectors as neutrons will not + contribute to any tallies far away from fission source neutron regions. + A ``1`` must be used to identify any fission source region. """ @@ -207,38 +186,6 @@ class CMFDMesh(object): check_value('CMFD mesh map', m, [0, 1]) self._map = meshmap - # REMOVE this method - def _get_xml_element(self): - element = ET.Element("mesh") - - subelement = ET.SubElement(element, "lower_left") - subelement.text = ' '.join(map(str, self._lower_left)) - - if self.upper_right is not None: - subelement = ET.SubElement(element, "upper_right") - subelement.text = ' '.join(map(str, self.upper_right)) - - subelement = ET.SubElement(element, "dimension") - subelement.text = ' '.join(map(str, self.dimension)) - - if self.width is not None: - subelement = ET.SubElement(element, "width") - subelement.text = ' '.join(map(str, self.width)) - - if self.energy is not None: - subelement = ET.SubElement(element, "energy") - subelement.text = ' '.join(map(str, self.energy)) - - if self.albedo is not None: - subelement = ET.SubElement(element, "albedo") - subelement.text = ' '.join(map(str, self.albedo)) - - if self.map is not None: - subelement = ET.SubElement(element, "map") - subelement.text = ' '.join(map(str, [self.map[i]+1 for i in range(len(self.map))])) - - return element - class CMFDRun(object): r"""Class to run openmc with CMFD acceleration through the C API. Running @@ -248,126 +195,69 @@ class CMFDRun(object): Attributes ---------- + begin : int + Batch number at which CMFD calculations should begin + dhat_reset : bool + Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should + be reset to zero before solving CMFD eigenproblem. + display : dict + Dictionary indicating which CMFD results to output. Note that CMFD + k-effective will always be outputted. Acceptable keys are: + * "balance" - Whether to output RMS [%] of the resdiual from the + neutron balance equation on CMFD tallies (bool) + * "dominance" - Whether to output the estimated dominance ratio from + the CMFD iterations (bool) + * "entropy" - Whether to output the *entropy* of the CMFD predicted + fission source (bool) + * "source" - Whether to ouput the RMS [%] between the OpenMC fission + source and CMFD fission source (bool) + downscatter : bool + Indicate whether an effective downscatter cross section should be used + when using 2-group CMFD. + feedback : bool + Indicate or not the CMFD diffusion result is used to adjust the weight + of fission source neutrons on the next OpenMC batch. Defaults to False. cmfd_ktol : float Tolerance on the eigenvalue when performing CMFD power iteration - cmfd_mesh : openmc.CMFDMesh + mesh : openmc.CMFDMesh Structured mesh to be used for acceleration norm : float Normalization factor applied to the CMFD fission source distribution - cmfd_power_monitor : bool + power_monitor : bool View convergence of power iteration during CMFD acceleration - cmfd_run_adjoint : bool + run_adjoint : bool Perform adjoint calculation on the last batch - cmfd_shift : float + w_shift : float Optional Wielandt shift parameter for accelerating power iterations. By default, it is very large so there is effectively no impact. - cmfd_stol : float + stol : float Tolerance on the fission source when performing CMFD power iteration - cmfd_reset : list of int + reset : list of int List of batch numbers at which CMFD tallies should be reset - cmfd_write_matrices : bool + write_matrices : bool Write sparse matrices that are used during CMFD acceleration (loss, production) and resultant normalized flux vector phi to file - cmfd_spectral : float + spectral : float Optional spectral radius that can be used to accelerate the convergence of Gauss-Seidel iterations during CMFD power iteration. gauss_seidel_tolerance : Iterable of float Two parameters specifying the absolute inner tolerance and the relative inner tolerance for Gauss-Seidel iterations when performing CMFD. - indices : numpy.ndarray - Stores spatial and group dimensions as [nx, ny, nz, ng] - egrid : numpy.ndarray - Energy grid used for CMFD acceleration - albedo : numpy.ndarray - Albedo for global boundary conditions, taken from CMFD mesh. It is - listed in the following order: [-x +x -y +y -z +z]. Set to - [1, 1, 1, 1, 1, 1] if not specified by user. - coremap : numpy.ndarray - Coremap for coarse mesh overlay, defining acceleration regions in CMFD - problem, created from map variable in CMFD class. Each accelerated - region is given a unique index to map each spatial mesh to a - corresponding row within the CMFD matrices. Non-accelerated regions are - set with value _CMFD_NOACCEL. If a user does not specify a CMFD map, - coremap is set to treat each spatial mesh as an accelerated region. - For non-accelerated regions on coarse mesh overlay - n_cmfd_resets : int - Number of elements in cmfd_reset to store number of times cmfd tallies - will be reset. - cmfd_mesh_id : int - Mesh id of CMFD mesh, stored to access CMFD mesh object in memory - cmfd_tally_ids : list of ints - List that stores the id's of all CMFD tallies. The tallies that each - id corresponds to are: - - * cmfd_tally_id[0] - CMFD flux, total tally - * cmfd_tally_id[1] - CMFD neutron production tally - * cmfd_tally_id[2] - CMFD surface current tally - * cmfd_tally_id[3] - CMFD P1 scatter tally - energy_filters : bool - Stores whether energy filters should be created or not, based on whether - a user specifies an energy grid - cmfd_on : bool - Stores whether cmfd solver should be invoked, based on whether current - batch has reached variable ``cmfd_begin`` - mat_dim : int - Number of accelerated regions exist in problem. Value of ``mat_dim`` is - tied to size of CMFD matrices - keff_bal : float - Balance k-effective computed from OpenMC source - cmfd_adjoint_type : {'physical', 'math'} + adjoint_type : {'physical', 'math'} Stores type of adjoint calculation that should be performed. - ``cmfd_run_adjoint`` must be true for an adjoint calculation to be + ``run_adjoint`` must be true for an adjoint calculation to be perfomed. Options are: * "physical" - Create adjoint matrices from physical parameters of CMFD problem * "math" - Create adjoint matrices mathematically as the transpose of loss and production CMFD matrices - keff : float - K-effective from solving CMFD matrix equations - adj_keff : float - K-effective from solving adjoint CMFD matrix equations - phi : numpy.ndarray - Final flux vector from solving CMFD matrix equations - adj_phi : numpy.ndarray - Final flux vector from solving adjoint CMFD matrix equations - flux : numpy.ndarray - Flux computed from tally data - totalxs : numpy.ndarray - Total cross section computed from tally data - p1scattxs : numpy.ndarray - P1 scattering cross section computed from tally data - scattxs : numpy.ndarray - Scattering cross section computed from tally data - nfissxs : numpy.ndarray - Nu-fission cross section computed from tally data - diffcof : numpy.ndarray - Diffusion coefficients computed from tally data - dtilde : numpy.ndarray - Array of diffusion coupling coefficients - dhat : numpy.ndarray - Array of nonlinear coupling coefficients - hxyz : numpy.ndarray - Dimensions of mesh cells, stored as (xloc,yloc,zloc,[hu,hv,hw]) - current : numpy.ndarray - Surface currents for each mesh cell in each incoming and outgoing - direction on each mesh surface, computed from tally data - cmfd_src : numpy.ndarray - CMFD source distribution calculated from solving CMFD equations - openmc_src : numpy.ndarray - OpenMC source distribution computed from tally data - sourcecounts : numpy.ndarray - Number of neutrons in each spatial and energy group, used to calculate - normalizing factors and weight factors when reweighting each neutron - weightfactors : numpy.ndarray - Weight factors for each spatial and energy group, used to reweight - neutrons at the end of each batch entropy : list of floats "Shannon entropy" from cmfd fission source, stored for each generation that CMFD is invoked balance : list of floats - RMS of neutron balance equations, stored for each generation that CMFD is - invoked + RMS of neutron balance equations, stored for each generation that CMFD + is invoked src_cmp : list of floats RMS deviation of OpenMC and CMFD normalized source, stored for each generation that CMFD is invoked @@ -377,8 +267,6 @@ class CMFDRun(object): k_cmfd : list of floats List of CMFD k-effectives, stored for each generation that CMFD is invoked - resnb : numpy.ndarray - Residual from solving neutron balance equations time_cmfd : float Time for entire CMFD calculation, in seconds time_cmfdbuild : float @@ -387,89 +275,6 @@ class CMFDRun(object): Time for solving CMFD matrix equations, in seconds intracomm : mpi4py.MPI.Intracomm or None MPI intercommunicator for running MPI commands - first_x_accel : tuple - Indices in CMFD problem where first x element is an accelerated region - Precomputed and stored for updating CMFD arrays - last_x_accel : tuple - Indices in CMFD problem where last x element is an accelerated region - Precomputed and stored for updating CMFD arrays - first_y_accel : tuple - Indices in CMFD problem where first y element is an accelerated region - Precomputed and stored for updating CMFD arrays - last_y_accel : tuple - Indices in CMFD problem where last y element is an accelerated region - Precomputed and stored for updating CMFD arrays - first_z_accel : tuple - Indices in CMFD problem where first z element is an accelerated region - Precomputed and stored for updating CMFD arrays - last_z_accel : tuple - Indices in CMFD problem where last z element is an accelerated region - Precomputed and stored for updating CMFD arrays - notfirst_x_accel : tuple - Indices in CMFD problem where all x element excluding first are - accelerated regions. Precomputed and stored for updating CMFD arrays - notlast_x_accel : tuple - Indices in CMFD problem where all x element excluding last are - accelerated regions. Precomputed and stored for updating CMFD arrays - notfirst_y_accel : tuple - Indices in CMFD problem where all y element excluding first are - accelerated regions. Precomputed and stored for updating CMFD arrays - notlast_y_accel : tuple - Indices in CMFD problem where all y element excluding last are - accelerated regions. Precomputed and stored for updating CMFD arrays - notfirst_z_accel : tuple - Indices in CMFD problem where all z element excluding first are - accelerated regions. Precomputed and stored for updating CMFD arrays - notlast_z_accel : tuple - Indices in CMFD problem where all z element excluding last are - accelerated regions. Precomputed and stored for updating CMFD arrays - is_adj_ref_left : numpy.ndarray - Boolean array of all indices in notfirst_x_accel that neighbor a reflector - region to the left. Precomputed and stored for updating CMFD arrays - is_adj_ref_right : numpy.ndarray - Boolean array of all indices in notlast_x_accel that neighbor a reflector - region to the right. Precomputed and stored for updating CMFD arrays - is_adj_ref_back : numpy.ndarray - Boolean array of all indices in notfirst_y_accel that neighbor a reflector - region to the back. Precomputed and stored for updating CMFD arrays - is_adj_ref_front : numpy.ndarray - Boolean array of all indices in notlast_y_accel that neighbor a reflector - region to the front. Precomputed and stored for updating CMFD arrays - is_adj_ref_bottom : numpy.ndarray - Boolean array of all indices in notfirst_z_accel that neighbor a reflector - region to the bottom. Precomputed and stored for updating CMFD arrays - is_adj_ref_top : numpy.ndarray - Boolean array of all indices in notlast_z_accel that neighbor a reflector - region to the top. Precomputed and stored for updating CMFD arrays - accel_idxs : tuple - All indices in CMFD problem that are accelerated. Precomputed and - stored for updating CMFD matrixes - accel_neig_left_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the left - accel_neig_right_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the right - accel_neig_back_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the back - accel_neig_front_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the front - accel_neig_bottom_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the bottom - accel_neig_top_idxs : tuple - All indices in CMFD problem that are accelerated and have a neighbor to - the top - loss_row : numpy.ndarray - All row indices in loss matrix that have nonzero elements - loss_col : numpy.ndarray - All column indices in loss matrix that have nonzero elements - prod_row : numpy.ndarray - All row indices in production matrix that have nonzero elements - prod_col : numpy.ndarray - All column indices in production matrix that have nonzero elements """ @@ -479,36 +284,38 @@ class CMFDRun(object): """ # Variables that users can modify - self._cmfd_begin = 1 + self._begin = 1 self._dhat_reset = False - self._cmfd_display = 'balance' - self._cmfd_downscatter = False - self._cmfd_feedback = False + self._display = {'balance': False, 'dominance': False, + 'entropy': False, 'source': False} + self._downscatter = False + self._feedback = False self._cmfd_ktol = 1.e-8 - self._cmfd_mesh = None + self._mesh = None self._norm = 1. - self._cmfd_power_monitor = False - self._cmfd_run_adjoint = False - self._cmfd_shift = 1.e6 - self._cmfd_stol = 1.e-8 - self._cmfd_reset = [] - self._cmfd_write_matrices = False - self._cmfd_spectral = 0.0 + self._power_monitor = False + self._run_adjoint = False + self._w_shift = 1.e6 + self._stol = 1.e-8 + self._reset = [] + self._write_matrices = False + self._spectral = 0.0 self._gauss_seidel_tolerance = [1.e-10, 1.e-5] + self._adjoint_type = 'physical' + self._intracomm = None # External variables used during runtime but users cannot control self._indices = np.zeros(4, dtype=np.int32) self._egrid = None self._albedo = None self._coremap = None - self._n_cmfd_resets = 0 - self._cmfd_mesh_id = None - self._cmfd_tally_ids = None + self._n_resets = 0 + self._mesh_id = None + self._tally_ids = None self._energy_filters = None self._cmfd_on = False self._mat_dim = _CMFD_NOACCEL self._keff_bal = None - self._cmfd_adjoint_type = 'physical' self._keff = None self._adj_keff = None self._phi = None @@ -536,9 +343,8 @@ class CMFDRun(object): self._time_cmfd = None self._time_cmfdbuild = None self._time_cmfdsolve = None - self._intracomm = None - # Add all index-related variables, for numpy vectorization + # All index-related variables, for numpy vectorization self._first_x_accel = None self._last_x_accel = None self._first_y_accel = None @@ -562,205 +368,230 @@ class CMFDRun(object): self._accel_neig_right_idxs = None self._accel_neig_back_idxs = None self._accel_neig_front_idxs = None - self._accel_neig_bottom_idxs = None + self._accel_neig_bot_idxs = None self._accel_neig_top_idxs = None self._loss_row = None self._loss_col = None self._prod_row = None self._prod_col = None - @property - def cmfd_begin(self): - return self._cmfd_begin + def begin(self): + return self._begin @property def dhat_reset(self): return self._dhat_reset @property - def cmfd_display(self): - return self._cmfd_display + def display(self): + return self._display @property - def cmfd_downscatter(self): - return self._cmfd_downscatter + def downscatter(self): + return self._downscatter @property - def cmfd_feedback(self): - return self._cmfd_feedback + def feedback(self): + return self._feedback @property def cmfd_ktol(self): return self._cmfd_ktol @property - def cmfd_mesh(self): - return self._cmfd_mesh + def mesh(self): + return self._mesh @property def norm(self): return self._norm @property - def cmfd_adjoint_type(self): - return self._cmfd_adjoint_type + def adjoint_type(self): + return self._adjoint_type @property - def cmfd_power_monitor(self): - return self._cmfd_power_monitor + def power_monitor(self): + return self._power_monitor @property - def cmfd_run_adjoint(self): - return self._cmfd_run_adjoint + def run_adjoint(self): + return self._run_adjoint @property - def cmfd_shift(self): - return self._cmfd_shift + def w_shift(self): + return self._w_shift @property - def cmfd_stol(self): - return self._cmfd_stol + def stol(self): + return self._stol @property - def cmfd_spectral(self): - return self._cmfd_spectral + def spectral(self): + return self._spectral @property - def cmfd_reset(self): - return self._cmfd_reset + def reset(self): + return self._reset @property - def cmfd_write_matrices(self): - return self._cmfd_write_matrices + def write_matrices(self): + return self._write_matrices @property def gauss_seidel_tolerance(self): return self._gauss_seidel_tolerance - @cmfd_begin.setter - def cmfd_begin(self, cmfd_begin): - check_type('CMFD begin batch', cmfd_begin, Integral) - check_greater_than('CMFD begin batch', cmfd_begin, 0) - self._cmfd_begin = cmfd_begin + @property + def dom(self): + return self._dom + + @property + def src_cmp(self): + return self._src_cmp + + @property + def balance(self): + return self._balance + + @property + def entropy(self): + return self._entropy + + @property + def k_cmfd(self): + return self._k_cmfd + + @begin.setter + def begin(self, begin): + check_type('CMFD begin batch', begin, Integral) + check_greater_than('CMFD begin batch', begin, 0) + self._begin = begin @dhat_reset.setter def dhat_reset(self, dhat_reset): check_type('CMFD Dhat reset', dhat_reset, bool) self._dhat_reset = dhat_reset - @cmfd_display.setter - def cmfd_display(self, display): - check_type('CMFD display', display, str) - check_value('CMFD display', display, - ['balance', 'dominance', 'entropy', 'source']) - self._cmfd_display = display + @display.setter + def display(self, display): + check_type('display', display, Mapping) + for key, value in display.items(): + check_value('display key', key, + ('balance', 'entropy', 'dominance', 'source')) + check_type("display['{}']".format(key), value, bool) + self._display[key] = value - @cmfd_downscatter.setter - def cmfd_downscatter(self, cmfd_downscatter): - check_type('CMFD downscatter', cmfd_downscatter, bool) - self._cmfd_downscatter = cmfd_downscatter + @downscatter.setter + def downscatter(self, downscatter): + check_type('CMFD downscatter', downscatter, bool) + self._downscatter = downscatter - @cmfd_feedback.setter - def cmfd_feedback(self, cmfd_feedback): - check_type('CMFD feedback', cmfd_feedback, bool) - self._cmfd_feedback = cmfd_feedback + @feedback.setter + def feedback(self, feedback): + check_type('CMFD feedback', feedback, bool) + self._feedback = feedback @cmfd_ktol.setter def cmfd_ktol(self, cmfd_ktol): check_type('CMFD eigenvalue tolerance', cmfd_ktol, Real) self._cmfd_ktol = cmfd_ktol - @cmfd_mesh.setter - def cmfd_mesh(self, mesh): - check_type('CMFD mesh', mesh, CMFDMesh) + @mesh.setter + def mesh(self, cmfd_mesh): + check_type('CMFD mesh', cmfd_mesh, CMFDMesh) # Check dimension defined - if mesh.dimension is None: + if cmfd_mesh.dimension is None: raise ValueError('CMFD mesh requires spatial ' 'dimensions to be specified') # Check lower left defined - if mesh.lower_left is None: + if cmfd_mesh.lower_left is None: raise ValueError('CMFD mesh requires lower left coordinates ' 'to be specified') # Check that both upper right and width both not defined - if mesh.upper_right is not None and mesh.width is not None: + if cmfd_mesh.upper_right is not None and cmfd_mesh.width is not None: raise ValueError('Both upper right coordinates and width ' 'cannot be specified for CMFD mesh') # Check that at least one of width or upper right is defined - if mesh.upper_right is None and mesh.width is None: + if cmfd_mesh.upper_right is None and cmfd_mesh.width is None: raise ValueError('CMFD mesh requires either upper right ' 'coordinates or width to be specified') - # Check width and lower length are same dimension and define upper_right - if mesh.width is not None: - check_length('CMFD mesh width', mesh.width, len(mesh.lower_left)) - mesh.upper_right = np.array(mesh.lower_left) + \ - np.array(mesh.width) * np.array(mesh.dimension) + # Check width and lower length are same dimension and define + # upper_right + if cmfd_mesh.width is not None: + check_length('CMFD mesh width', cmfd_mesh.width, + len(cmfd_mesh.lower_left)) + cmfd_mesh.upper_right = np.array(cmfd_mesh.lower_left) + \ + np.array(cmfd_mesh.width) * np.array(cmfd_mesh.dimension) - # Check upper_right and lower length are same dimension and define width - elif mesh.upper_right is not None: - check_length('CMFD mesh upper right', mesh.upper_right, \ - len(mesh.lower_left)) + # Check upper_right and lower length are same dimension and define + # width + elif cmfd_mesh.upper_right is not None: + check_length('CMFD mesh upper right', cmfd_mesh.upper_right, + len(cmfd_mesh.lower_left)) # Check upper right coordinates are greater than lower left - if np.any(np.array(mesh.upper_right) <= np.array(mesh.lower_left)): + if np.any(np.array(cmfd_mesh.upper_right) <= + np.array(cmfd_mesh.lower_left)): raise ValueError('CMFD mesh requires upper right ' 'coordinates to be greater than lower ' 'left coordinates') - mesh.width = np.true_divide( - (np.array(mesh.upper_right) - np.array(mesh.lower_left)), \ - np.array(mesh.dimension)) - self._cmfd_mesh = mesh + cmfd_mesh.width = np.true_divide((np.array(cmfd_mesh.upper_right) - + np.array(cmfd_mesh.lower_left)), + np.array(cmfd_mesh.dimension)) + self._mesh = cmfd_mesh @norm.setter def norm(self, norm): check_type('CMFD norm', norm, Real) self._norm = norm - @cmfd_adjoint_type.setter - def cmfd_adjoint_type(self, adjoint_type): + @adjoint_type.setter + def adjoint_type(self, adjoint_type): check_type('CMFD adjoint type', adjoint_type, str) check_value('CMFD adjoint type', adjoint_type, ['math', 'phyical']) - self._cmfd_adjoint_type = adjoint_type + self._adjoint_type = adjoint_type - @cmfd_power_monitor.setter - def cmfd_power_monitor(self, cmfd_power_monitor): - check_type('CMFD power monitor', cmfd_power_monitor, bool) - self._cmfd_power_monitor = cmfd_power_monitor + @power_monitor.setter + def power_monitor(self, power_monitor): + check_type('CMFD power monitor', power_monitor, bool) + self._power_monitor = power_monitor - @cmfd_run_adjoint.setter - def cmfd_run_adjoint(self, cmfd_run_adjoint): - check_type('CMFD run adjoint', cmfd_run_adjoint, bool) - self._cmfd_run_adjoint = cmfd_run_adjoint + @run_adjoint.setter + def run_adjoint(self, run_adjoint): + check_type('CMFD run adjoint', run_adjoint, bool) + self._run_adjoint = run_adjoint - @cmfd_shift.setter - def cmfd_shift(self, cmfd_shift): - check_type('CMFD Wielandt shift', cmfd_shift, Real) - self._cmfd_shift = cmfd_shift + @w_shift.setter + def w_shift(self, w_shift): + check_type('CMFD Wielandt shift', w_shift, Real) + self._w_shift = w_shift - @cmfd_stol.setter - def cmfd_stol(self, cmfd_stol): - check_type('CMFD fission source tolerance', cmfd_stol, Real) - self._cmfd_stol = cmfd_stol + @stol.setter + def stol(self, stol): + check_type('CMFD fission source tolerance', stol, Real) + self._stol = stol - @cmfd_spectral.setter - def cmfd_spectral(self, spectral): + @spectral.setter + def spectral(self, spectral): check_type('CMFD spectral radius', spectral, Real) - self._cmfd_spectral = spectral + self._spectral = spectral - @cmfd_reset.setter - def cmfd_reset(self, cmfd_reset): - check_type('tally reset batches', cmfd_reset, Iterable, Integral) - self._cmfd_reset = cmfd_reset + @reset.setter + def reset(self, reset): + check_type('tally reset batches', reset, Iterable, Integral) + self._reset = reset - @cmfd_write_matrices.setter - def cmfd_write_matrices(self, cmfd_write_matrices): - check_type('CMFD write matrices', cmfd_write_matrices, bool) - self._cmfd_write_matrices = cmfd_write_matrices + @write_matrices.setter + def write_matrices(self, write_matrices): + check_type('CMFD write matrices', write_matrices, bool) + self._write_matrices = write_matrices @gauss_seidel_tolerance.setter def gauss_seidel_tolerance(self, gauss_seidel_tolerance): @@ -769,20 +600,23 @@ class CMFDRun(object): check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) self._gauss_seidel_tolerance = gauss_seidel_tolerance - def run(self, omp_num_threads=None, intracomm=None): - """Public method to run OpenMC with CMFD + def run(self, intracomm=None, **kwargs): + """Run OpenMC with coarse mesh finite difference acceleration This method is called by user to run CMFD once instance variables of CMFDRun class are set Parameters ---------- - omp_num_threads : int - Number of OpenMP threads to use for OpenMC simulation intracomm : mpi4py.MPI.Intracomm or None MPI intercommunicator to pass through C API. Set to MPI.COMM_WORLD by default + Keyword arguments + ----------------- + openmc_args : list of str + Arguments to pass to ``openmc.capi.init()``, as a list + """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made @@ -791,12 +625,11 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD - # Check number of OpenMP threads is valid input and initialize C API - if omp_num_threads is not None: - check_type('OpenMP num threads', omp_num_threads, Integral) - openmc.capi.init(args=['-s',str(omp_num_threads)]) + # Check keyword arguments and pass to C API init function + if 'openmc_args' in kwargs: + core.init(args=kwargs['openmc_args'], intracomm=self._intracomm) else: - openmc.capi.init() + core.init(intracomm=self._intracomm) # Configure CMFD parameters and tallies self._configure_cmfd() @@ -814,34 +647,32 @@ class CMFDRun(object): self._initialize_linsolver() # Initialize simulation - openmc.capi.simulation_init() + core.simulation_init() - while(True): + status = 0 + while status == 0: # Initialize CMFD batch self._cmfd_init_batch() # Run next batch - status = openmc.capi.next_batch() + status = core.next_batch() # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd() # Write CMFD output if CMFD on for current batch - if openmc.capi.master(): + if core.master(): self._write_cmfd_output() - if status != 0: - break - # Finalize simuation - openmc.capi.simulation_finalize() + core.simulation_finalize() # Print out CMFD timing statistics self._write_cmfd_timing_stats() # Finalize and free memory - openmc.capi.finalize() + core.finalize() def _initialize_linsolver(self): # Determine number of rows in CMFD matrix @@ -849,45 +680,49 @@ class CMFDRun(object): n = self._mat_dim*ng # Create temp loss matrix to pass row/col indices to C++ linear solver - temp_data = np.ones(len(self._loss_row)) - temp_loss = sparse.csr_matrix((temp_data, (self._loss_row, self._loss_col)), + loss_row = self._loss_row + loss_col = self._loss_col + temp_data = np.ones(len(loss_row)) + temp_loss = sparse.csr_matrix((temp_data, (loss_row, loss_col)), shape=(n, n)) # Pass coremap as 1-d array of 32-bit integers coremap = np.swapaxes(self._coremap, 0, 2).flatten().astype(np.int32) - return openmc.capi._dll.openmc_initialize_linsolver(temp_loss.indptr, - len(temp_loss.indptr), temp_loss.indices, len(temp_loss.indices), n, - self._cmfd_spectral, self._indices, coremap) + args = temp_loss.indptr, len(temp_loss.indptr), \ + temp_loss.indices, len(temp_loss.indices), n, \ + self._spectral, self._indices, coremap + return _dll.openmc_initialize_linsolver(*args) def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" # Display CMFD k-effective - str1 = 'CMFD k: {:0.5f}'.format(self._k_cmfd[-1]) - # Display value of additional field based on value of cmfd_display - if self._cmfd_display == 'dominance': - str2 = 'Dom Rat: {:0.5f}'.format(self._dom[-1]) - elif self._cmfd_display == 'entropy': - str2 = 'CMFD Ent: {:0.5f}'.format(self._entropy[-1]) - elif self._cmfd_display == 'source': - str2 = 'RMS Src: {:0.5f}'.format(self._src_cmp[-1]) - else: - str2 = 'RMS Bal: {:0.5f}'.format(self._balance[-1]) + str1 = '{:>11s}CMFD k: {:0.5f}'.format('', self._k_cmfd[-1]) + # Display value of additional fields based on display dict + str2 = '\n' + if self._display['dominance']: + str2 += '{:>11s}Dom Rat: {:0.5f}\n'.format('', self._dom[-1]) + if self._display['entropy']: + str2 += '{:>11s}CMFD Ent: {:0.5f}\n'.format('', self._entropy[-1]) + if self._display['source']: + str2 += '{:>11s}RMS Src: {:0.5f}\n'.format('', self._src_cmp[-1]) + if self._display['balance']: + str2 += '{:>11s}RMS Bal: {:0.5f}\n'.format('', self._balance[-1]) - print('{0:>76s}\n{1:>76s}'.format(str1, str2)) + print('{:s}{:s}'.format(str1, str2), end='') sys.stdout.flush() def _write_cmfd_timing_stats(self): - """Write CMFD timing statistics to buffer after finalizing simulation""" - if openmc.capi.master(): - print( -"""=====================> CMFD TIMING STATISTICS <==================== - - Time in CMFD = {0:.5E} seconds - Building matrices = {1:.5E} seconds - Solving matrices = {2:.5E} seconds -""".format(self._time_cmfd, self._time_cmfdbuild, self._time_cmfdsolve)) - sys.stdout.flush() + """Write CMFD timing stats to buffer after finalizing simulation""" + if core.master(): + outstr = ("=====================> " + "CMFD TIMING STATISTICS <====================\n\n" + " Time in CMFD = {:.5E} seconds\n" + " Building matrices = {:.5E} seconds\n" + " Solving matrices = {:.5E} seconds\n") + print(outstr.format(self._time_cmfd, self._time_cmfdbuild, + self._time_cmfdsolve)) + sys.stdout.flush() def _configure_cmfd(self): """Initialize CMFD parameters and set CMFD input variables""" @@ -905,27 +740,27 @@ class CMFDRun(object): def _read_cmfd_input(self): """Sets values of additional instance variables based on user input""" # Print message to user and flush output to stdout - if openmc.capi.settings.verbosity >= 7 and openmc.capi.master(): + if settings.verbosity >= 7 and core.master(): print(' Configuring CMFD parameters for simulation') sys.stdout.flush() # Check if CMFD mesh is defined - if self._cmfd_mesh is None: + if self._mesh is None: raise ValueError('No CMFD mesh has been specified for ' 'simulation') # Set spatial dimensions of CMFD object - for i, n in enumerate(self._cmfd_mesh.dimension): + for i, n in enumerate(self._mesh.dimension): self._indices[i] = n # Check if in continuous energy mode - if not openmc.capi.settings.run_CE: + if not settings.run_CE: raise OpenMCError('CMFD must be run in continuous energy mode') # Set number of energy groups - if self._cmfd_mesh.energy is not None: - ng = len(self._cmfd_mesh.energy) - self._egrid = np.array(self._cmfd_mesh.energy) + if self._mesh.energy is not None: + ng = len(self._mesh.energy) + self._egrid = np.array(self._mesh.energy) self._indices[3] = ng - 1 self._energy_filters = True else: @@ -934,22 +769,23 @@ class CMFDRun(object): self._energy_filters = False # Set global albedo - if self._cmfd_mesh.albedo is not None: - self._albedo = np.array(self._cmfd_mesh.albedo) + if self._mesh.albedo is not None: + self._albedo = np.array(self._mesh.albedo) else: self._albedo = np.array([1., 1., 1., 1., 1., 1.]) # Get acceleration map, otherwise set all regions to be accelerated - if self._cmfd_mesh.map is not None: - check_length('CMFD coremap', self._cmfd_mesh.map, + if self._mesh.map is not None: + check_length('CMFD coremap', self._mesh.map, np.product(self._indices[0:3])) - self._coremap = np.array(self._cmfd_mesh.map) + self._coremap = np.array(self._mesh.map) else: - self._coremap = np.ones((np.product(self._indices[0:3])), dtype=int) + self._coremap = np.ones((np.product(self._indices[0:3])), + dtype=int) # Set number of batches where cmfd tallies should be reset - if self._cmfd_reset is not None: - self._n_cmfd_resets = len(self._cmfd_reset) + if self._reset is not None: + self._n_resets = len(self._reset) # Create tally objects self._create_cmfd_tally() @@ -999,20 +835,20 @@ class CMFDRun(object): """Handles CMFD options at the beginning of each batch""" # Get current batch through C API # Add 1 as next_batch has not been called yet - current_batch = openmc.capi.current_batch() + 1 + current_batch = core.current_batch() + 1 # Check to activate CMFD diffusion and possible feedback - if self._cmfd_begin == current_batch: + if self._begin == current_batch: self._cmfd_on = True # Check to reset tallies - if self._n_cmfd_resets > 0 and current_batch in self._cmfd_reset: + if self._n_resets > 0 and current_batch in self._reset: self._cmfd_tally_reset() def _execute_cmfd(self): """Runs CMFD calculation on master node""" # Run CMFD on single processor on master - if openmc.capi.master(): + if core.master(): # Start CMFD timer time_start_cmfd = time.time() @@ -1026,8 +862,8 @@ class CMFDRun(object): self._k_cmfd.append(self._keff) # Check to perform adjoint on last batch - if (openmc.capi.current_batch() == openmc.capi.settings.batches - and self._cmfd_run_adjoint): + if (core.current_batch() == settings.batches + and self._run_adjoint): self._cmfd_solver_execute(adjoint=True) # Calculate fission source @@ -1037,20 +873,20 @@ class CMFDRun(object): self._cmfd_reweight(True) # Stop CMFD timer - if openmc.capi.master(): + if core.master(): time_stop_cmfd = time.time() self._time_cmfd += time_stop_cmfd - time_start_cmfd def _cmfd_tally_reset(self): """Resets all CMFD tallies in memory""" # Print message - if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): + if settings.verbosity >= 6 and core.master(): print(' CMFD tallies reset') sys.stdout.flush() # Reset CMFD tallies - tallies = openmc.capi.tallies - for tally_id in self._cmfd_tally_ids: + tallies = tally.tallies + for tally_id in self._tally_ids: tallies[tally_id].reset() def _set_up_cmfd(self): @@ -1061,7 +897,8 @@ class CMFDRun(object): self._compute_xs() # Compute effective downscatter cross section - if (self._cmfd_downscatter): self._compute_effective_downscatter() + if self._downscatter: + self._compute_effective_downscatter() # Check neutron balance self._neutron_balance() @@ -1082,7 +919,7 @@ class CMFDRun(object): """ # Check for physical adjoint - physical_adjoint = adjoint and self._cmfd_adjoint_type == 'physical' + physical_adjoint = adjoint and self._adjoint_type == 'physical' # Start timer for build time_start_buildcmfd = time.time() @@ -1091,7 +928,7 @@ class CMFDRun(object): loss, prod = self._build_matrices(physical_adjoint) # Check for mathematical adjoint calculation - if adjoint and self._cmfd_adjoint_type == 'math': + if adjoint and self._adjoint_type == 'math': loss, prod = self._compute_adjoint(loss, prod) # Stop timer for build @@ -1115,25 +952,25 @@ class CMFDRun(object): self._dom.append(dom) # Write out flux vector - if self._cmfd_write_matrices: + if self._write_matrices: if adjoint: self._write_vector(self._adj_phi, 'adj_fluxvec') else: self._write_vector(self._phi, 'fluxvec') def _write_vector(self, vector, base_filename): - """Write a 1-D numpy array to file and also save it in .npy format. This - particular format allows users to load the variable directly in a Python - session with np.load() + """Write a 1-D numpy array to file and also save it in .npy format. + This particular format allows users to load the variable directly in a + Python session with np.load() Parameters ---------- vector : numpy.ndarray Vector that will be saved - base_filename : string + base_filename : str Filename to save vector as, without any file extension at the end. - Vector will be saved to file [base_filename].dat and in numpy format - as [base_filename].npy + Vector will be saved to file [base_filename].dat and in numpy + format as [base_filename].npy """ # Write each element in vector to file @@ -1146,17 +983,17 @@ class CMFDRun(object): def _write_matrix(self, matrix, base_filename): """Write a numpy matrix to file and also save it in .npz format. This - particular format allows users to load the variable directly in a Python - session with scipy.sparse.load_npz() + particular format allows users to load the variable directly in a + Python session with scipy.sparse.load_npz() Parameters ---------- matrix : scipy.sparse.spmatrix Sparse matrix that will be saved - base_filename : string + base_filename : str Filename to save matrix entries, without any file extension at the - end. Matrix entries will be saved to file [base_filename].dat and in - scipy format as [base_filename].npz + end. Matrix entries will be saved to file [base_filename].dat and + in scipy format as [base_filename].npz """ # Write row, col, and data of each entry in sparse matrix. This ignores @@ -1168,7 +1005,7 @@ class CMFDRun(object): # Get all data entries for particular row in matrix data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] for i in range(len(cols)): - fh.write('({0:3d}, {1:3d}): {2:0.8f}\n'.format( + fh.write('({:3d}, {:3d}): {:0.8f}\n'.format( row, cols[i], data[i])) # Save matrix in scipy format @@ -1192,8 +1029,9 @@ class CMFDRun(object): # Reset CMFD source to 0 self._cmfd_src.fill(0.) - # Compute cmfd_src in a vecotorized manner by phi to the spatial indices - # of the actual problem so that cmfd_flux can be multiplied by nfissxs + # Compute cmfd_src in a vecotorized manner by phi to the spatial + # indices of the actual problem so that cmfd_flux can be multiplied by + # nfissxs # Calculate volume vol = np.product(self._hxyz, axis=3) @@ -1212,18 +1050,18 @@ class CMFDRun(object): # coremap and values of phi for g in range(ng): phi_g = phi[:,g] - cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] + cmfd_flux[idx + (g,)] = phi_g[self._coremap[idx]] # Compute fission source - cmfd_src = np.sum(self._nfissxs[:,:,:,:,:] * \ - cmfd_flux[:,:,:,:,np.newaxis], axis=3) * \ - vol[:,:,:,np.newaxis] + cmfd_src = (np.sum(self._nfissxs[:,:,:,:,:] * + cmfd_flux[:,:,:,:,np.newaxis], axis=3) * + vol[:,:,:,np.newaxis]) # Normalize source such that it sums to 1.0 self._cmfd_src = cmfd_src / np.sum(cmfd_src) # Compute entropy - if openmc.capi.settings.entropy_on: + if settings.entropy_on: # Compute source times log_2(source) source = self._cmfd_src[self._cmfd_src > 0] \ * np.log(self._cmfd_src[self._cmfd_src > 0])/np.log(2) @@ -1235,8 +1073,8 @@ class CMFDRun(object): self._cmfd_src = self._cmfd_src/np.sum(self._cmfd_src) * self._norm # Calculate differences between normalized sources - self._src_cmp.append(np.sqrt(1.0 / self._norm \ - * np.sum((self._cmfd_src - self._openmc_src)**2))) + self._src_cmp.append(np.sqrt(1.0 / self._norm + * np.sum((self._cmfd_src - self._openmc_src)**2))) def _cmfd_reweight(self, new_weights): """Performs weighting of particles in source bank @@ -1263,21 +1101,21 @@ class CMFDRun(object): outside = self._count_bank_sites() # Check and raise error if source sites exist outside of CMFD mesh - if openmc.capi.master() and outside: + if core.master() and outside: raise OpenMCError('Source sites outside of the CMFD mesh') # Have master compute weight factors, ignore any zeros in # sourcecounts or cmfd_src - if openmc.capi.master(): + if core.master(): # Compute normalization factor norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) - # Define target reshape dimensions for sourcecounts. This defines - # how self._sourcecounts is ordered by dimension + # Define target reshape dimensions for sourcecounts. This + # defines how self._sourcecounts is ordered by dimension target_shape = [nz, ny, nx, ng] - # Reshape sourcecounts to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng] + # Reshape sourcecounts to target shape. Swap x and z axes so + # that the shape is now [nx, ny, nz, ng] sourcecounts = np.swapaxes( self._sourcecounts.reshape(target_shape), 0, 2) @@ -1285,30 +1123,31 @@ class CMFDRun(object): sourcecounts = np.flip(sourcecounts, axis=3) # Compute weight factors - divide_condition = np.logical_and(sourcecounts > 0, - self._cmfd_src > 0) - self._weightfactors = np.divide(self._cmfd_src * norm, \ - sourcecounts, where=divide_condition, \ - out=np.ones_like(self._cmfd_src)) + div_condition = np.logical_and(sourcecounts > 0, + self._cmfd_src > 0) + with np.errstate(divide='ignore', invalid='ignore'): + self._weightfactors = (np.divide(self._cmfd_src * norm, + sourcecounts, where=div_condition, + out=np.ones_like(self._cmfd_src))) - if not self._cmfd_feedback: + if not self._feedback: return # Broadcast weight factors to all procs if have_mpi: self._weightfactors = self._intracomm.bcast( - self._weightfactors, root=0) + self._weightfactors) - m = openmc.capi.meshes[self._cmfd_mesh_id] + m = mesh.meshes[self._mesh_id] energy = self._egrid ng = self._indices[3] # Get xyz locations and energies of all particles in source bank - source_xyz = openmc.capi.source_bank()['xyz'] - source_energies = openmc.capi.source_bank()['E'] + source_xyz = core.source_bank()['xyz'] + source_energies = core.source_bank()['E'] # Convert xyz location to the CMFD mesh index - mesh_ijk = np.floor((source_xyz - m.lower_left) / m.width).astype(int) + mesh_ijk = np.floor((source_xyz-m.lower_left)/m.width).astype(int) # Determine which energy bin each particle's energy belongs to # Separate into cases bases on where source energies lies on egrid @@ -1317,18 +1156,19 @@ class CMFDRun(object): energy_bins[idx] = ng - 1 idx = np.where(source_energies > energy[-1]) energy_bins[idx] = 0 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + idx = np.where((source_energies >= energy[0]) & + (source_energies <= energy[-1])) energy_bins[idx] = ng - np.digitize(source_energies, energy) # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight - openmc.capi.source_bank()['wgt'] *= self._weightfactors[ \ + core.source_bank()['wgt'] *= self._weightfactors[ mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] - if openmc.capi.master() and np.any(source_energies < energy[0]): + if core.master() and np.any(source_energies < energy[0]): print(' WARNING: Source pt below energy grid') sys.stdout.flush() - if openmc.capi.master() and np.any(source_energies > energy[-1]): + if core.master() and np.any(source_energies > energy[-1]): print(' WARNING: Source pt above energy grid') sys.stdout.flush() @@ -1342,8 +1182,8 @@ class CMFDRun(object): """ # Initialize variables - m = openmc.capi.meshes[self._cmfd_mesh_id] - bank = openmc.capi.source_bank() + m = mesh.meshes[self._mesh_id] + bank = core.source_bank() energy = self._egrid sites_outside = np.zeros(1, dtype=bool) ng = self._indices[3] @@ -1352,13 +1192,13 @@ class CMFDRun(object): count = np.zeros(self._sourcecounts.shape) # Get xyz locations and energies of each particle in source bank - source_xyz = openmc.capi.source_bank()['xyz'] - source_energies = openmc.capi.source_bank()['E'] + source_xyz = core.source_bank()['xyz'] + source_energies = core.source_bank()['E'] # Convert xyz location to mesh index and ravel index to scalar mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) mesh_bins = mesh_locations[:,2] * m.dimension[1] * m.dimension[0] + \ - mesh_locations[:,1] * m.dimension[0] + mesh_locations[:,0] + mesh_locations[:,1] * m.dimension[0] + mesh_locations[:,0] # Check if any source locations lie outside of defined CMFD mesh if np.any(mesh_bins < 0) or np.any(mesh_bins >= np.prod(m.dimension)): @@ -1371,7 +1211,8 @@ class CMFDRun(object): energy_bins[idx] = 0 idx = np.where(source_energies > energy[-1]) energy_bins[idx] = ng - 1 - idx = np.where((source_energies >= energy[0]) & (source_energies <= energy[-1])) + idx = np.where((source_energies >= energy[0]) & + (source_energies <= energy[-1])) energy_bins[idx] = np.digitize(source_energies, energy) - 1 # Determine all unique combinations of mesh bin and energy bin, and @@ -1384,9 +1225,9 @@ class CMFDRun(object): if have_mpi: # Collect values of count from all processors - self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM, root=0) + self._intracomm.Reduce(count, self._sourcecounts, MPI.SUM) # Check if there were sites outside the mesh for any processor - self._intracomm.Reduce(outside, sites_outside, MPI.LOR, root=0) + self._intracomm.Reduce(outside, sites_outside, MPI.LOR) # Deal with case if MPI not defined (only one proc) else: sites_outside = outside @@ -1415,7 +1256,7 @@ class CMFDRun(object): prod = self._build_prod_matrix(adjoint) # Write out matrices - if self._cmfd_write_matrices: + if self._write_matrices: if adjoint: self._write_matrix(loss, 'adj_loss') self._write_matrix(prod, 'adj_prod') @@ -1449,7 +1290,7 @@ class CMFDRun(object): prod = np.transpose(prod) # Write out matrices - if self._cmfd_write_matrices: + if self._write_matrices: self._write_matrix(loss, 'adj_loss') self._write_matrix(prod, 'adj_prod') @@ -1480,10 +1321,14 @@ class CMFDRun(object): dy = self._hxyz[:,:,:,np.newaxis,1] dz = self._hxyz[:,:,:,np.newaxis,2] - # Define net leakage coefficient for each surface in each matrix element - jnet = (((dtilde_right + dhat_right) - (-1.0 * dtilde_left + dhat_left)) / dx - + ((dtilde_front + dhat_front) - (-1.0 * dtilde_back + dhat_back)) / dy - + ((dtilde_top + dhat_top) - (-1.0 * dtilde_bottom + dhat_bottom)) / dz) + # Define net leakage coefficient for each surface in each matrix + # element + jnet = (((dtilde_right + dhat_right)-(-1.0 * dtilde_left + dhat_left)) + / dx + + ((dtilde_front + dhat_front)-(-1.0 * dtilde_back + dhat_back)) + / dy + + ((dtilde_top + dhat_top)-(-1.0 * dtilde_bottom + dhat_bottom)) + / dz) for g in range(ng): # Define leakage terms that relate terms to their neighbors to the @@ -1524,9 +1369,9 @@ class CMFDRun(object): # Define leakage terms that relate terms to their neighbors to the # bottom - dtilde = self._dtilde[:,:,:,g,4][self._accel_neig_bottom_idxs] - dhat = self._dhat[:,:,:,g,4][self._accel_neig_bottom_idxs] - dz = self._hxyz[:,:,:,2][self._accel_neig_bottom_idxs] + dtilde = self._dtilde[:,:,:,g,4][self._accel_neig_bot_idxs] + dhat = self._dhat[:,:,:,g,4][self._accel_neig_bot_idxs] + dz = self._hxyz[:,:,:,2][self._accel_neig_bot_idxs] vals = (-1.0 * dtilde - dhat) / dz # Store data to add to CSR matrix data = np.append(data, vals) @@ -1565,7 +1410,9 @@ class CMFDRun(object): data = np.append(data, vals) # Create csr matrix - loss = sparse.csr_matrix((data, (self._loss_row, self._loss_col)), shape=(n, n)) + loss_row = self._loss_row + loss_col = self._loss_col + loss = sparse.csr_matrix((data, (loss_row, loss_col)), shape=(n, n)) return loss def _build_prod_matrix(self, adjoint): @@ -1589,7 +1436,9 @@ class CMFDRun(object): data = np.append(data, vals) # Create csr matrix - prod = sparse.csr_matrix((data, (self._prod_row, self._prod_col)), shape=(n, n)) + prod_row = self._prod_row + prod_col = self._prod_col + prod = sparse.csr_matrix((data, (prod_row, prod_col)), shape=(n, n)) return prod def _execute_power_iter(self, loss, prod): @@ -1629,9 +1478,9 @@ class CMFDRun(object): s_o = np.zeros((n,)) # Set initial guess - k_n = openmc.capi.keff()[0] + k_n = core.keff()[0] k_o = k_n - dw = self._cmfd_shift + dw = self._w_shift k_s = k_o + dw k_ln = 1.0/(1.0/k_n - 1.0/k_s) k_lo = k_ln @@ -1659,9 +1508,8 @@ class CMFDRun(object): # Normalize source vector s_o /= k_lo - # Compute new flux with either C++ solver or scipy sparse solver - innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, - s_o, phi_n, toli) + # Compute new flux with C++ solver + innerits = _dll.openmc_run_linsolver(loss.data, s_o, phi_n, toli) # Compute new source vector s_n = prod.dot(phi_n) @@ -1721,23 +1569,24 @@ class CMFDRun(object): """ # Calculate error in keff - kerr = abs(k_o - k_n)/k_n + kerr = abs(k_o - k_n) / k_n # Calculate max error in source - serr = np.sqrt(np.sum(np.where(s_n>0, ((s_n-s_o)/s_n)**2,0))/len(s_n)) + serr = np.sqrt(np.sum(np.where(s_n > 0, ((s_n-s_o) / s_n)**2, 0)) + / len(s_n)) # Check for convergence - iconv = kerr < self._cmfd_ktol and serr < self._cmfd_stol + iconv = kerr < self._cmfd_ktol and serr < self._stol # Print out to user - if self._cmfd_power_monitor and openmc.capi.master(): + if self._power_monitor and core.master(): str1 = ' {:d}:'.format(iter) str2 = 'k-eff: {:0.8f}'.format(k_n) - str3 = 'k-error: {0:.5E}'.format(kerr) - str4 = 'src-error: {0:.5E}'.format(serr) + str3 = 'k-error: {:.5E}'.format(kerr) + str4 = 'src-error: {:.5E}'.format(serr) str5 = ' {:d}'.format(innerits) - print('{0:8s}{1:20s}{2:25s}{3:s}{4:s}'.format(str1, str2, str3, - str4, str5)) + print('{:8s}{:20s}{:25s}{:s}{:s}'.format(str1, str2, str3, str4, + str5)) sys.stdout.flush() return iconv, serr @@ -1755,8 +1604,8 @@ class CMFDRun(object): # Define coremap as cumulative sum over accelerated regions, # otherwise set value to _CMFD_NOACCEL - self._coremap = np.where(self._coremap==0, _CMFD_NOACCEL, - np.cumsum(self._coremap)-1) + self._coremap = np.where(self._coremap == 0, _CMFD_NOACCEL, + np.cumsum(self._coremap)-1) # Reshape coremap to three dimensional array # Indices of coremap in user input switched in x and z axes @@ -1782,13 +1631,13 @@ class CMFDRun(object): self._openmc_src.fill(0.) # Set mesh widths - self._hxyz[:,:,:,:] = openmc.capi.meshes[self._cmfd_mesh_id].width + self._hxyz[:,:,:,:] = mesh.meshes[self._mesh_id].width # Reset keff_bal to zero self._keff_bal = 0. # Get tallies in-memory - tallies = openmc.capi.tallies + tallies = tally.tallies # Ravel coremap as 1d array similar to how tally data is arranged coremap = np.ravel(self._coremap.swapaxes(0, 2)) @@ -1798,7 +1647,7 @@ class CMFDRun(object): is_cmfd_accel = np.repeat(coremap != _CMFD_NOACCEL, ng) # Get flux from CMFD tally 0 - tally_id = self._cmfd_tally_ids[0] + tally_id = self._tally_ids[0] tally_results = tallies[tally_id].results[:,0,1] flux = np.where(is_cmfd_accel, tally_results, 0.) @@ -1832,12 +1681,15 @@ class CMFDRun(object): # Get total rr and convert to total xs from CMFD tally 0 tally_results = tallies[tally_id].results[:,1,1] - totalxs = np.divide(tally_results, flux, \ - where=flux>0, out=np.zeros_like(tally_results)) + with np.errstate(divide='ignore', invalid='ignore'): + totalxs = np.divide(tally_results, flux, + where=flux > 0, + out=np.zeros_like(tally_results)) # Reshape totalxs array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng] - reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), 0, 2) + reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), + 0, 2) # Total xs is flipped in energy axis as tally results are given in # reverse order of energy group @@ -1845,12 +1697,12 @@ class CMFDRun(object): # Get scattering xs from CMFD tally 1 # flux is repeated to account for extra dimensionality of scattering xs - tally_id = self._cmfd_tally_ids[1] + tally_id = self._tally_ids[1] tally_results = tallies[tally_id].results[:,0,1] - scattxs = np.divide(tally_results, \ - np.repeat(flux, ng), \ - where=np.repeat(flux>0, ng), \ - out=np.zeros_like(tally_results)) + with np.errstate(divide='ignore', invalid='ignore'): + scattxs = np.divide(tally_results, np.repeat(flux, ng), + where=np.repeat(flux > 0, ng), + out=np.zeros_like(tally_results)) # Define target tally reshape dimensions for xs with incoming # and outgoing energies @@ -1858,7 +1710,8 @@ class CMFDRun(object): # Reshape scattxs array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, ng] - reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), 0, 2) + reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), + 0, 2) # Scattering xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group @@ -1869,21 +1722,23 @@ class CMFDRun(object): # flux is repeated to account for extra dimensionality of nu-fission xs tally_results = tallies[tally_id].results[:,1,1] num_realizations = tallies[tally_id].num_realizations - nfissxs = np.divide(tally_results, \ - np.repeat(flux, ng), \ - where=np.repeat(flux>0, ng), \ - out=np.zeros_like(tally_results)) + with np.errstate(divide='ignore', invalid='ignore'): + nfissxs = np.divide(tally_results, np.repeat(flux, ng), + where=np.repeat(flux > 0, ng), + out=np.zeros_like(tally_results)) # Reshape nfissxs array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, ng] - reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), 0, 2) + reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), + 0, 2) # Nu-fission xs is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group self._nfissxs = np.flip(reshape_nfissxs, axis=3) self._nfissxs = np.flip(self._nfissxs, axis=4) - # Openmc source distribution is sum of nu-fission rr in incoming energies + # Openmc source distribution is sum of nu-fission rr in incoming + # energies self._openmc_src = np.sum(self._nfissxs*self._flux[:,:,:,:,np.newaxis], axis=3) @@ -1894,44 +1749,48 @@ class CMFDRun(object): self._openmc_src /= np.sum(self._openmc_src) * self._norm # Get surface currents from CMFD tally 2 - tally_id = self._cmfd_tally_ids[2] + tally_id = self._tally_ids[2] tally_results = tallies[tally_id].results[:,0,1] # Filter tally results to include only accelerated regions - current = np.where(np.repeat(flux>0, 12), tally_results, 0.) + current = np.where(np.repeat(flux > 0, 12), tally_results, 0.) # Define target tally reshape dimensions for current target_tally_shape = [nz, ny, nx, 12, ng] # Reshape current array to target shape. Swap x and z axes so that # shape is now [nx, ny, nz, ng, 12] - reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) + reshape_current = np.swapaxes(current.reshape(target_tally_shape), + 0, 2) # Current is flipped in energy axis as tally results are given in # reverse order of energy group self._current = np.flip(reshape_current, axis=4) # Get p1 scatter xs from CMFD tally 3 - tally_id = self._cmfd_tally_ids[3] + tally_id = self._tally_ids[3] tally_results = tallies[tally_id].results[:,0,1] # Define target tally reshape dimensions for p1 scatter tally target_tally_shape = [nz, ny, nx, 2, ng] - # Reshape and extract only p1 data from tally results (no need for p0 data) + # Reshape and extract only p1 data from tally results as there is + # no need for p0 data p1scattrr = np.swapaxes(tally_results.reshape(target_tally_shape), 0, 2)[:,:,:,1,:] # Store p1 scatter xs # p1 scatter xs is flipped in energy axis as tally results are given in # reverse order of energy group - self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, \ - where=self._flux>0, \ - out=np.zeros_like(p1scattrr)) + with np.errstate(divide='ignore', invalid='ignore'): + self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, + where=self._flux > 0, + out=np.zeros_like(p1scattrr)) # Calculate and store diffusion coefficient - self._diffcof = np.where(self._flux>0, 1.0 / (3.0 * \ - (self._totalxs - self._p1scattxs)), 0.) + with np.errstate(divide='ignore', invalid='ignore'): + self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * + (self._totalxs - self._p1scattxs)), 0.) def _compute_effective_downscatter(self): """Changes downscatter rate for zero upscatter""" @@ -1959,8 +1818,10 @@ class CMFDRun(object): siga2 = sigt2 - sigs22 - sigs21 # Compute effective downscatter XS - sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, where=flux1>0, - out=np.zeros_like(flux2)) + with np.errstate(divide='ignore', invalid='ignore'): + sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, + where=flux1 > 0, + out=np.zeros_like(flux2)) # Recompute total cross sections and record self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff @@ -1981,21 +1842,21 @@ class CMFDRun(object): num_accel = self._mat_dim # Get openmc k-effective - keff = openmc.capi.keff()[0] + keff = core.keff()[0] # Define leakage in each mesh cell and energy group - leakage = ((self._current[:,:,:,_CURRENTS['out_right'],:] - \ - self._current[:,:,:,_CURRENTS['in_right'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_left'],:] - \ - self._current[:,:,:,_CURRENTS['out_left'],:])) + \ - ((self._current[:,:,:,_CURRENTS['out_front'],:] - \ - self._current[:,:,:,_CURRENTS['in_front'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_back'],:] - \ - self._current[:,:,:,_CURRENTS['out_back'],:])) + \ - ((self._current[:,:,:,_CURRENTS['out_top'],:] - \ - self._current[:,:,:,_CURRENTS['in_top'],:]) - \ - (self._current[:,:,:,_CURRENTS['in_bottom'],:] - \ - self._current[:,:,:,_CURRENTS['out_bottom'],:])) + leakage = (((self._current[:,:,:,_CURRENTS['out_right'],:] - + self._current[:,:,:,_CURRENTS['in_right'],:]) - + (self._current[:,:,:,_CURRENTS['in_left'],:] - + self._current[:,:,:,_CURRENTS['out_left'],:])) + + ((self._current[:,:,:,_CURRENTS['out_front'],:] - + self._current[:,:,:,_CURRENTS['in_front'],:]) - + (self._current[:,:,:,_CURRENTS['in_back'],:] - + self._current[:,:,:,_CURRENTS['out_back'],:])) + + ((self._current[:,:,:,_CURRENTS['out_top'],:] - + self._current[:,:,:,_CURRENTS['in_top'],:]) - + (self._current[:,:,:,_CURRENTS['in_bottom'],:] - + self._current[:,:,:,_CURRENTS['out_bottom'],:]))) # Compute total rr interactions = self._totalxs * self._flux @@ -2014,11 +1875,11 @@ class CMFDRun(object): res = leakage + interactions - scattering - (1.0 / keff) * fission # Normalize res by flux and bank res - self._resnb = np.divide(res, self._flux, where=self._flux>0) + self._resnb = np.divide(res, self._flux, where=self._flux > 0) # Calculate RMS and record for this batch self._balance.append(np.sqrt( - np.sum(np.multiply(self._resnb, self._resnb)) / \ + np.sum(np.multiply(self._resnb, self._resnb)) / (ng * num_accel))) def _precompute_array_indices(self): @@ -2031,8 +1892,8 @@ class CMFDRun(object): ny = self._indices[1] nz = self._indices[2] - - # Logical for determining whether region of interest is accelerated region + # Logical for determining whether region of interest is accelerated + # region is_accel = self._coremap != _CMFD_NOACCEL # Logical for determining whether a zero flux "albedo" b.c. should be # applied @@ -2044,42 +1905,48 @@ class CMFDRun(object): slice_y = y_inds[:1,:,:] slice_z = z_inds[:1,:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._first_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[-1,:,:] slice_x = x_inds[-1:,:,:] slice_y = y_inds[-1:,:,:] slice_z = z_inds[-1:,:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._last_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,0,:] slice_x = x_inds[:,:1,:] slice_y = y_inds[:,:1,:] slice_z = z_inds[:,:1,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._first_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,-1,:] slice_x = x_inds[:,-1:,:] slice_y = y_inds[:,-1:,:] slice_z = z_inds[:,-1:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._last_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,:,0] slice_x = x_inds[:,:,:1] slice_y = y_inds[:,:,:1] slice_z = z_inds[:,:,:1] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._first_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,:,-1] slice_x = x_inds[:,:,-1:] slice_y = y_inds[:,:,-1:] slice_z = z_inds[:,:,-1:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] - self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], slice_z[bndry_accel]) + self._last_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], + slice_z[bndry_accel]) # Define slice equivalent to is_accel[1:,:,:] slice_x = x_inds[1:,:,:] @@ -2087,7 +1954,7 @@ class CMFDRun(object): slice_z = z_inds[1:,:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notfirst_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:-1,:,:] slice_x = x_inds[:-1,:,:] @@ -2095,7 +1962,7 @@ class CMFDRun(object): slice_z = z_inds[:-1,:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notlast_x_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,1:,:] slice_x = x_inds[:,1:,:] @@ -2103,7 +1970,7 @@ class CMFDRun(object): slice_z = z_inds[:,1:,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notfirst_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,:-1,:] slice_x = x_inds[:,:-1,:] @@ -2111,7 +1978,7 @@ class CMFDRun(object): slice_z = z_inds[:,:-1,:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notlast_y_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,:,1:] slice_x = x_inds[:,:,1:] @@ -2119,7 +1986,7 @@ class CMFDRun(object): slice_z = z_inds[:,:,1:] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notfirst_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Define slice equivalent to is_accel[:,:,:-1] slice_x = x_inds[:,:,:-1] @@ -2127,31 +1994,43 @@ class CMFDRun(object): slice_z = z_inds[:,:,:-1] bndry_accel = is_accel[(slice_x, slice_y, slice_z)] self._notlast_z_accel = (slice_x[bndry_accel], slice_y[bndry_accel], - slice_z[bndry_accel]) + slice_z[bndry_accel]) # Store logical for whether neighboring cell is reflector region # in all directions adj_reflector_left = np.roll(self._coremap, 1, axis=0) == _CMFD_NOACCEL - self._is_adj_ref_left = adj_reflector_left[self._notfirst_x_accel + (np.newaxis,)] + self._is_adj_ref_left = adj_reflector_left[ + self._notfirst_x_accel + (np.newaxis,)] - adj_reflector_right = np.roll(self._coremap, -1, axis=0) == _CMFD_NOACCEL - self._is_adj_ref_right = adj_reflector_right[self._notlast_x_accel + (np.newaxis,)] + adj_reflector_right = np.roll(self._coremap, -1, axis=0) == \ + _CMFD_NOACCEL + self._is_adj_ref_right = adj_reflector_right[ + self._notlast_x_accel + (np.newaxis,)] - adj_reflector_back = np.roll(self._coremap, 1, axis=1) == _CMFD_NOACCEL - self._is_adj_ref_back = adj_reflector_back[self._notfirst_y_accel + (np.newaxis,)] + adj_reflector_back = np.roll(self._coremap, 1, axis=1) == \ + _CMFD_NOACCEL + self._is_adj_ref_back = adj_reflector_back[ + self._notfirst_y_accel + (np.newaxis,)] - adj_reflector_front = np.roll(self._coremap, -1, axis=1) == _CMFD_NOACCEL - self._is_adj_ref_front = adj_reflector_front[self._notlast_y_accel + (np.newaxis,)] + adj_reflector_front = np.roll(self._coremap, -1, axis=1) == \ + _CMFD_NOACCEL + self._is_adj_ref_front = adj_reflector_front[ + self._notlast_y_accel + (np.newaxis,)] - adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == _CMFD_NOACCEL - self._is_adj_ref_bottom = adj_reflector_bottom[self._notfirst_z_accel + (np.newaxis,)] + adj_reflector_bottom = np.roll(self._coremap, 1, axis=2) == \ + _CMFD_NOACCEL + self._is_adj_ref_bottom = adj_reflector_bottom[ + self._notfirst_z_accel + (np.newaxis,)] - adj_reflector_top = np.roll(self._coremap, -1, axis=2) == _CMFD_NOACCEL - self._is_adj_ref_top = adj_reflector_top[self._notlast_z_accel + (np.newaxis,)] + adj_reflector_top = np.roll(self._coremap, -1, axis=2) == \ + _CMFD_NOACCEL + self._is_adj_ref_top = adj_reflector_top[ + self._notlast_z_accel + (np.newaxis,)] def _precompute_matrix_indices(self): - """Computes the indices and row/column data used to populate CMFD CSR matrices. - These indices are used in _build_loss_matrix and _build_prod_matrix + """Computes the indices and row/column data used to populate CMFD CSR + matrices. These indices are used in _build_loss_matrix and + _build_prod_matrix """ # Extract energy group indices @@ -2160,41 +2039,48 @@ class CMFDRun(object): # Shift coremap in all directions to determine whether leakage term # should be defined for particular cell in matrix coremap_shift_left = np.pad(self._coremap, ((1,0),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:-1,:,:] + mode='constant', + constant_values=_CMFD_NOACCEL)[:-1,:,:] coremap_shift_right = np.pad(self._coremap, ((0,1),(0,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[1:,:,:] + mode='constant', + constant_values=_CMFD_NOACCEL)[1:,:,:] coremap_shift_back = np.pad(self._coremap, ((0,0),(1,0),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:-1,:] + mode='constant', + constant_values=_CMFD_NOACCEL)[:,:-1,:] coremap_shift_front = np.pad(self._coremap, ((0,0),(0,1),(0,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,1:,:] + mode='constant', + constant_values=_CMFD_NOACCEL)[:,1:,:] coremap_shift_bottom = np.pad(self._coremap, ((0,0),(0,0),(1,0)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,:-1] + mode='constant', + constant_values=_CMFD_NOACCEL)[:,:,:-1] coremap_shift_top = np.pad(self._coremap, ((0,0),(0,0),(0,1)), - mode='constant', constant_values=_CMFD_NOACCEL)[:,:,1:] + mode='constant', + constant_values=_CMFD_NOACCEL)[:,:,1:] # Create empty row and column vectors to store for loss matrix row = np.array([]) col = np.array([]) # Store all indices used to populate production and loss matrix - self._accel_idxs = np.where(self._coremap != _CMFD_NOACCEL) - self._accel_neig_left_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_left != _CMFD_NOACCEL)) - self._accel_neig_right_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_right != _CMFD_NOACCEL)) - self._accel_neig_back_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_back != _CMFD_NOACCEL)) - self._accel_neig_front_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_front != _CMFD_NOACCEL)) - self._accel_neig_bottom_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_bottom != _CMFD_NOACCEL)) - self._accel_neig_top_idxs = np.where((self._coremap != _CMFD_NOACCEL) & - (coremap_shift_top != _CMFD_NOACCEL)) + is_accel = self._coremap != _CMFD_NOACCEL + self._accel_idxs = np.where(is_accel) + self._accel_neig_left_idxs = (np.where(is_accel & + (coremap_shift_left != _CMFD_NOACCEL))) + self._accel_neig_right_idxs = (np.where(is_accel & + (coremap_shift_right != _CMFD_NOACCEL))) + self._accel_neig_back_idxs = (np.where(is_accel & + (coremap_shift_back != _CMFD_NOACCEL))) + self._accel_neig_front_idxs = (np.where(is_accel & + (coremap_shift_front != _CMFD_NOACCEL))) + self._accel_neig_bot_idxs = (np.where(is_accel & + (coremap_shift_bottom != _CMFD_NOACCEL))) + self._accel_neig_top_idxs = (np.where(is_accel & + (coremap_shift_top != _CMFD_NOACCEL))) for g in range(ng): # Extract row and column data of regions where a cell and its @@ -2227,8 +2113,9 @@ class CMFDRun(object): # Extract row and column data of regions where a cell and its # neighbor to the bottom are both fuel regions - idx_x = ng * (self._coremap[self._accel_neig_bottom_idxs]) + g - idx_y = ng * (coremap_shift_bottom[self._accel_neig_bottom_idxs]) + g + idx_x = ng * (self._coremap[self._accel_neig_bot_idxs]) + g + idx_y = ng * (coremap_shift_bottom[self._accel_neig_bot_idxs]) \ + + g row = np.append(row, idx_x) col = np.append(col, idx_y) @@ -2277,10 +2164,10 @@ class CMFDRun(object): def _compute_dtilde(self): """Computes the diffusion coupling coefficient using a vectorized numpy approach. Aggregate values for the dtilde multidimensional array are - populated by first defining values on the problem boundary, and then for - all other regions. For indices not lying on a boundary, dtilde values - are distinguished between regions that neighbor a reflector region and - regions that don't neighbor a reflector + populated by first defining values on the problem boundary, and then + for all other regions. For indices not lying on a boundary, dtilde + values are distinguished between regions that neighbor a reflector + region and regions that don't neighbor a reflector """ # Logical for determining whether a zero flux "albedo" b.c. should be @@ -2298,7 +2185,8 @@ class CMFDRun(object): else: alb = self._albedo[0] self._dtilde[boundary_grps + (0,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dx)) # Define dtilde at right surface for all mesh cells on right boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2311,7 +2199,8 @@ class CMFDRun(object): else: alb = self._albedo[1] self._dtilde[boundary_grps + (1,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dx)) # Define dtilde at back surface for all mesh cells on back boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2324,7 +2213,8 @@ class CMFDRun(object): else: alb = self._albedo[2] self._dtilde[boundary_grps + (2,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dy)) # Define dtilde at front surface for all mesh cells on front boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2337,7 +2227,8 @@ class CMFDRun(object): else: alb = self._albedo[3] self._dtilde[boundary_grps + (3,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dy)) # Define dtilde at bottom surface for all mesh cells on bottom boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2350,7 +2241,8 @@ class CMFDRun(object): else: alb = self._albedo[4] self._dtilde[boundary_grps + (4,)] = ((2.0 * D * (1.0 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dz)) # Define dtilde at top surface for all mesh cells on top boundary # Separate between zero flux b.c. and alebdo b.c. @@ -2364,15 +2256,17 @@ class CMFDRun(object): else: alb = self._albedo[5] self._dtilde[boundary_grps + (5,)] = ((2.0 * D * (1 - alb)) - / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz)) + / (4.0 * D * (1.0 + alb) + + (1.0 - alb) * dz)) # Define reflector albedo for all cells on the left surface, in case # a cell borders a reflector region on the left current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - ref_albedo = np.divide(current_in_left, current_out_left, - where=current_out_left > 1.0e-10, - out=np.ones_like(current_out_left)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_left, current_out_left, + where=current_out_left > 1.0e-10, + out=np.ones_like(current_out_left)) # Diffusion coefficient of neighbor to left neig_dc = np.roll(self._diffcof, 1, axis=0) @@ -2390,26 +2284,28 @@ class CMFDRun(object): neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_left - self._dtilde[boundary_grps + (0,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), - (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + self._dtilde[boundary_grps + (0,)] = dtilde # Define reflector albedo for all cells on the right surface, in case # a cell borders a reflector region on the right current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - ref_albedo = np.divide(current_in_right, current_out_right, - where=current_out_right > 1.0e-10, - out=np.ones_like(current_out_right)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_right, current_out_right, + where=current_out_right > 1.0e-10, + out=np.ones_like(current_out_right)) # Diffusion coefficient of neighbor to right neig_dc = np.roll(self._diffcof, -1, axis=0) # Cell dimensions of neighbor to right neig_hxyz = np.roll(self._hxyz, -1, axis=0) - # Define dtilde at right surface for all mesh cells not on right boundary - # Dtilde is defined differently for regions that do and don't neighbor - # reflector regions + # Define dtilde at right surface for all mesh cells not on right + # boundary. Dtilde is defined differently for regions that do and don't + # neighbor reflector regions boundary = self._notlast_x_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] @@ -2418,17 +2314,19 @@ class CMFDRun(object): neig_dx = neig_hxyz[boundary + (np.newaxis, 0)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_right - self._dtilde[boundary_grps + (1,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), - (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dx), + (2.0 * D * neig_D) / (neig_dx * D + dx * neig_D)) + self._dtilde[boundary_grps + (1,)] = dtilde # Define reflector albedo for all cells on the back surface, in case # a cell borders a reflector region on the back current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - ref_albedo = np.divide(current_in_back, current_out_back, - where=current_out_back > 1.0e-10, - out=np.ones_like(current_out_back)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_back, current_out_back, + where=current_out_back > 1.0e-10, + out=np.ones_like(current_out_back)) # Diffusion coefficient of neighbor to back neig_dc = np.roll(self._diffcof, 1, axis=1) @@ -2446,26 +2344,28 @@ class CMFDRun(object): neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_back - self._dtilde[boundary_grps + (2,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), - (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + self._dtilde[boundary_grps + (2,)] = dtilde # Define reflector albedo for all cells on the front surface, in case # a cell borders a reflector region in the front current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - ref_albedo = np.divide(current_in_front, current_out_front, - where=current_out_front > 1.0e-10, - out=np.ones_like(current_out_front)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_front, current_out_front, + where=current_out_front > 1.0e-10, + out=np.ones_like(current_out_front)) # Diffusion coefficient of neighbor to front neig_dc = np.roll(self._diffcof, -1, axis=1) # Cell dimensions of neighbor to front neig_hxyz = np.roll(self._hxyz, -1, axis=1) - # Define dtilde at front surface for all mesh cells not on front boundary - # Dtilde is defined differently for regions that do and don't neighbor - # reflector regions + # Define dtilde at front surface for all mesh cells not on front + # boundary. Dtilde is defined differently for regions that do and don't + # neighbor reflector regions boundary = self._notlast_y_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] @@ -2474,26 +2374,28 @@ class CMFDRun(object): neig_dy = neig_hxyz[boundary + (np.newaxis, 1)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_front - self._dtilde[boundary_grps + (3,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), - (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dy), + (2.0 * D * neig_D) / (neig_dy * D + dy * neig_D)) + self._dtilde[boundary_grps + (3,)] = dtilde # Define reflector albedo for all cells on the bottom surface, in case # a cell borders a reflector region on the bottom current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - ref_albedo = np.divide(current_in_bottom, current_out_bottom, - where=current_out_bottom > 1.0e-10, - out=np.ones_like(current_out_bottom)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_bottom, current_out_bottom, + where=current_out_bottom > 1.0e-10, + out=np.ones_like(current_out_bottom)) # Diffusion coefficient of neighbor to bottom neig_dc = np.roll(self._diffcof, 1, axis=2) # Cell dimensions of neighbor to bottom neig_hxyz = np.roll(self._hxyz, 1, axis=2) - # Define dtilde at bottom surface for all mesh cells not on bottom boundary - # Dtilde is defined differently for regions that do and don't neighbor - # reflector regions + # Define dtilde at bottom surface for all mesh cells not on bottom + # boundary. Dtilde is defined differently for regions that do and don't + # neighbor reflector regions boundary = self._notfirst_z_accel boundary_grps = boundary + (slice(None),) D = self._diffcof[boundary_grps] @@ -2502,17 +2404,19 @@ class CMFDRun(object): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_bottom - self._dtilde[boundary_grps + (4,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), - (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + self._dtilde[boundary_grps + (4,)] = dtilde # Define reflector albedo for all cells on the top surface, in case # a cell borders a reflector region on the top current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - ref_albedo = np.divide(current_in_top, current_out_top, - where=current_out_top > 1.0e-10, - out=np.ones_like(current_out_top)) + with np.errstate(divide='ignore', invalid='ignore'): + ref_albedo = np.divide(current_in_top, current_out_top, + where=current_out_top > 1.0e-10, + out=np.ones_like(current_out_top)) # Diffusion coefficient of neighbor to top neig_dc = np.roll(self._diffcof, -1, axis=2) @@ -2530,15 +2434,16 @@ class CMFDRun(object): neig_dz = neig_hxyz[boundary + (np.newaxis, 2)] alb = ref_albedo[boundary_grps] is_adj_ref = self._is_adj_ref_top - self._dtilde[boundary_grps + (5,)] = np.where(is_adj_ref, - (2.0 * D * (1.0 - alb)) / (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), - (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + dtilde = np.where(is_adj_ref, (2.0 * D * (1.0 - alb)) / + (4.0 * D * (1.0 + alb) + (1.0 - alb) * dz), + (2.0 * D * neig_D) / (neig_dz * D + dz * neig_D)) + self._dtilde[boundary_grps + (5,)] = dtilde def _compute_dhat(self): """Computes the nonlinear coupling coefficient using a vectorized numpy approach. Aggregate values for the dhat multidimensional array are - populated by first defining values on the problem boundary, and then for - all other regions. For indices not lying by a boundary, dhat values + populated by first defining values on the problem boundary, and then + for all other regions. For indices not lying by a boundary, dhat values are distinguished between regions that neighbor a reflector region and regions that don't neighbor a reflector @@ -2564,10 +2469,13 @@ class CMFDRun(object): # Define net current on each face net_current_left = (current_in_left - current_out_left) / dxdydz * dx - net_current_right = (current_out_right - current_in_right) / dxdydz * dx + net_current_right = (current_out_right - current_in_right) / dxdydz * \ + dx net_current_back = (current_in_back - current_out_back) / dxdydz * dy - net_current_front = (current_out_front - current_in_front) / dxdydz * dy - net_current_bottom = (current_in_bottom - current_out_bottom) / dxdydz * dz + net_current_front = (current_out_front - current_in_front) / dxdydz * \ + dy + net_current_bottom = (current_in_bottom - current_out_bottom) / \ + dxdydz * dz net_current_top = (current_out_top - current_in_top) / dxdydz * dz # Define flux in each cell @@ -2636,9 +2544,10 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_left = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_left - self._dhat[boundary_grps + (0,)] = np.where(is_adj_ref, - (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_left - flux)) / (flux_left + flux)) + dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_left - flux)) / + (flux_left + flux)) + self._dhat[boundary_grps + (0,)] = dhat # Cell flux of neighbor to right neig_flux = np.roll(self._flux, -1, axis=0) / dxdydz @@ -2653,9 +2562,10 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_right = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_right - self._dhat[boundary_grps + (1,)] = np.where(is_adj_ref, - (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_right - flux)) / (flux_right + flux)) + dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_right - flux)) / + (flux_right + flux)) + self._dhat[boundary_grps + (1,)] = dhat # Cell flux of neighbor to back neig_flux = np.roll(self._flux, 1, axis=1) / dxdydz @@ -2670,9 +2580,10 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_back = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_back - self._dhat[boundary_grps + (2,)] = np.where(is_adj_ref, - (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_back - flux)) / (flux_back + flux)) + dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_back - flux)) / + (flux_back + flux)) + self._dhat[boundary_grps + (2,)] = dhat # Cell flux of neighbor to front neig_flux = np.roll(self._flux, -1, axis=1) / dxdydz @@ -2687,16 +2598,17 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_front = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_front - self._dhat[boundary_grps + (3,)] = np.where(is_adj_ref, - (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_front - flux)) / (flux_front + flux)) + dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_front - flux)) / + (flux_front + flux)) + self._dhat[boundary_grps + (3,)] = dhat # Cell flux of neighbor to bottom neig_flux = np.roll(self._flux, 1, axis=2) / dxdydz - # Define dhat at bottom surface for all mesh cells not on bottom boundary - # Dhat is defined differently for regions that do and don't neighbor - # reflector regions + # Define dhat at bottom surface for all mesh cells not on bottom + # boundary. Dhat is defined differently for regions that do and don't + # neighbor reflector regions boundary = self._notfirst_z_accel boundary_grps = boundary + (slice(None),) net_current = net_current_bottom[boundary_grps] @@ -2704,9 +2616,10 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_bottom = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_bottom - self._dhat[boundary_grps + (4,)] = np.where(is_adj_ref, - (net_current + dtilde * flux) / flux, - (net_current - dtilde * (flux_bottom - flux)) / (flux_bottom + flux)) + dhat = np.where(is_adj_ref, (net_current + dtilde * flux) / flux, + (net_current - dtilde * (flux_bottom - flux)) / + (flux_bottom + flux)) + self._dhat[boundary_grps + (4,)] = dhat # Cell flux of neighbor to top neig_flux = np.roll(self._flux, -1, axis=2) / dxdydz @@ -2721,106 +2634,108 @@ class CMFDRun(object): flux = cell_flux[boundary_grps] flux_top = neig_flux[boundary_grps] is_adj_ref = self._is_adj_ref_top - self._dhat[boundary_grps + (5,)] = np.where(is_adj_ref, - (net_current - dtilde * flux) / flux, - (net_current + dtilde * (flux_top - flux)) / (flux_top + flux)) + dhat = np.where(is_adj_ref, (net_current - dtilde * flux) / flux, + (net_current + dtilde * (flux_top - flux)) / + (flux_top + flux)) + self._dhat[boundary_grps + (5,)] = dhat def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = openmc.capi.Mesh() + cmfd_mesh = mesh.Mesh() # Store id of Mesh object - self._cmfd_mesh_id = cmfd_mesh.id + self._mesh_id = cmfd_mesh.id # Set dimension and parameters of Mesh object - cmfd_mesh.dimension = self._cmfd_mesh.dimension - cmfd_mesh.set_parameters(lower_left=self._cmfd_mesh.lower_left, - upper_right=self._cmfd_mesh.upper_right, - width=self._cmfd_mesh.width) + cmfd_mesh.dimension = self._mesh.dimension + cmfd_mesh.set_parameters(lower_left=self._mesh.lower_left, + upper_right=self._mesh.upper_right, + width=self._mesh.width) # Create Mesh Filter object, stored internally - mesh_filter = openmc.capi.MeshFilter() + mesh_filter = filter.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh # Set up energy filters, if applicable if self._energy_filters: # Create Energy Filter object, stored internally - energy_filter = openmc.capi.EnergyFilter() + energy_filter = filter.EnergyFilter() # Set bins for Energy Filter energy_filter.bins = self._egrid # Create Energy Out Filter object, stored internally - energyout_filter = openmc.capi.EnergyoutFilter() + energyout_filter = filter.EnergyoutFilter() # Set bins for Energy Filter energyout_filter.bins = self._egrid # Create Mesh Surface Filter object, stored internally - meshsurface_filter = openmc.capi.MeshSurfaceFilter() + meshsurface_filter = filter.MeshSurfaceFilter() # Set mesh for Mesh Surface Filter meshsurface_filter.mesh = cmfd_mesh # Create Legendre Filter object, stored internally - legendre_filter = openmc.capi.LegendreFilter() + legendre_filter = filter.LegendreFilter() # Set order for Legendre Filter legendre_filter.order = 1 # Create CMFD tallies, stored internally n_tallies = 4 - self._cmfd_tally_ids = [] + self._tally_ids = [] for i in range(n_tallies): - tally = openmc.capi.Tally() + cmfd_tally = tally.Tally() # Set nuclide bins - tally.nuclides = ['total'] - self._cmfd_tally_ids.append(tally.id) + cmfd_tally.nuclides = ['total'] + self._tally_ids.append(cmfd_tally.id) # Set attributes of CMFD flux, total tally if i == 0: # Set filters for tally if self._energy_filters: - tally.filters = [mesh_filter, energy_filter] + cmfd_tally.filters = [mesh_filter, energy_filter] else: - tally.filters = [mesh_filter] + cmfd_tally.filters = [mesh_filter] # Set scores, type, and estimator for tally - tally.scores = ['flux', 'total'] - tally.type = 'volume' - tally.estimator = 'analog' + cmfd_tally.scores = ['flux', 'total'] + cmfd_tally.type = 'volume' + cmfd_tally.estimator = 'analog' # Set attributes of CMFD neutron production tally elif i == 1: # Set filters for tally if self._energy_filters: - tally.filters = [mesh_filter, energy_filter, energyout_filter] + cmfd_tally.filters = [mesh_filter, energy_filter, + energyout_filter] else: - tally.filters = [mesh_filter] + cmfd_tally.filters = [mesh_filter] # Set scores, type, and estimator for tally - tally.scores = ['nu-scatter', 'nu-fission'] - tally.type = 'volume' - tally.estimator = 'analog' + cmfd_tally.scores = ['nu-scatter', 'nu-fission'] + cmfd_tally.type = 'volume' + cmfd_tally.estimator = 'analog' # Set attributes of CMFD surface current tally elif i == 2: # Set filters for tally if self._energy_filters: - tally.filters = [meshsurface_filter, energy_filter] + cmfd_tally.filters = [meshsurface_filter, energy_filter] else: - tally.filters = [meshsurface_filter] + cmfd_tally.filters = [meshsurface_filter] # Set scores, type, and estimator for tally - tally.scores = ['current'] - tally.type = 'mesh-surface' - tally.estimator = 'analog' + cmfd_tally.scores = ['current'] + cmfd_tally.type = 'mesh-surface' + cmfd_tally.estimator = 'analog' # Set attributes of CMFD P1 scatter tally elif i == 3: # Set filters for tally if self._energy_filters: - tally.filters = [mesh_filter, legendre_filter, energy_filter] + cmfd_tally.filters = [mesh_filter, legendre_filter, + energy_filter] else: - tally.filters = [mesh_filter, legendre_filter] + cmfd_tally.filters = [mesh_filter, legendre_filter] # Set scores for tally - tally.scores = ['scatter'] - tally.type = 'volume' - tally.estimator = 'analog' + cmfd_tally.scores = ['scatter'] + cmfd_tally.type = 'volume' + cmfd_tally.estimator = 'analog' # Set all tallies to be active from beginning - tally.active = True - + cmfd_tally.active = True diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 1a2d87091e..8f2091796b 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -1,11 +1,16 @@ #include +#include + +#include "xtensor/xtensor.hpp" -#include "openmc/cmfd_solver.h" #include "openmc/error.h" #include "openmc/constants.h" +#include "openmc/capi.h" namespace openmc { +namespace cmfd { + //============================================================================== // Global variables //============================================================================== @@ -22,14 +27,30 @@ int nx, ny, nz, ng; xt::xtensor indexmap; +} // namespace cmfd + +//============================================================================== +// MATRIX_TO_INDICES converts a matrix index to spatial and group +// indices +//============================================================================== + +void matrix_to_indices(int irow, int& g, int& i, int& j, int& k) +{ + g = irow % cmfd::ng; + i = cmfd::indexmap(irow/cmfd::ng, 0); + j = cmfd::indexmap(irow/cmfd::ng, 1); + k = cmfd::indexmap(irow/cmfd::ng, 2); +} + //============================================================================== // GET_DIAGONAL_INDEX returns the index in CSR index array corresponding to // the diagonal element of a specified row //============================================================================== -int get_diagonal_index(int row) { - for (int j = indptr[row]; j < indptr[row+1]; j++) { - if (indices[j] == row) +int get_diagonal_index(int row) +{ + for (int j = cmfd::indptr[row]; j < cmfd::indptr[row+1]; j++) { + if (cmfd::indices[j] == row) return j; } @@ -41,15 +62,16 @@ int get_diagonal_index(int row) { // SET_INDEXMAP sets the elements of indexmap based on input coremap //============================================================================== -void set_indexmap(int* coremap) { - for (int z = 0; z < nz; z++) { - for (int y = 0; y < ny; y++) { - for (int x = 0; x < nx; x++) { - if (coremap[(z*ny*nx) + (y*nx) + x] != CMFD_NOACCEL) { - int counter = coremap[(z*ny*nx) + (y*nx) + x]; - indexmap(counter, 0) = x; - indexmap(counter, 1) = y; - indexmap(counter, 2) = z; +void set_indexmap(const int* coremap) +{ + for (int z = 0; z < cmfd::nz; z++) { + for (int y = 0; y < cmfd::ny; y++) { + for (int x = 0; x < cmfd::nx; x++) { + if (coremap[(z*cmfd::ny*cmfd::nx) + (y*cmfd::nx) + x] != CMFD_NOACCEL) { + int counter = coremap[(z*cmfd::ny*cmfd::nx) + (y*cmfd::nx) + x]; + cmfd::indexmap(counter, 0) = x; + cmfd::indexmap(counter, 1) = y; + cmfd::indexmap(counter, 2) = z; } } } @@ -60,23 +82,24 @@ void set_indexmap(int* coremap) { // CMFD_LINSOLVER_1G solves a one group CMFD linear system //============================================================================== -int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol) { +int cmfd_linsolver_1g(const double* A_data, const double* b, double* x, + double tol) +{ // Set overrelaxation parameter double w = 1.0; // Perform Gauss-Seidel iterations for (int igs = 1; igs <= 10000; igs++) { - double tmpx[dim]; double err = 0.0; // Copy over x vector - std::copy(x, x+dim, tmpx); + std::vector tmpx {x, x+cmfd::dim}; // Perform red/black Gauss-Seidel iterations for (int irb = 0; irb < 2; irb++) { // Loop around matrix rows - for (int irow = 0; irow < dim; irow++) { + for (int irow = 0; irow < cmfd::dim; irow++) { int g, i, j, k; matrix_to_indices(irow, g, i, j, k); @@ -88,10 +111,10 @@ int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol) { // Perform temporary sums, first do left of diag, then right of diag double tmp1 = 0.0; - for (int icol = indptr[irow]; icol < didx; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; - for (int icol = didx + 1; icol < indptr[irow + 1]; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = cmfd::indptr[irow]; icol < didx; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; + for (int icol = didx + 1; icol < cmfd::indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; // Solve for new x double x1 = (b[irow] - tmp1) / A_data[didx]; @@ -106,12 +129,12 @@ int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol) { } // Check convergence - err = std::sqrt(err / dim); + err = std::sqrt(err / cmfd::dim); if (err < tol) return igs; // Calculate new overrelaxation parameter - w = 1.0/(1.0 - 0.25 * spectral * w); + w = 1.0/(1.0 - 0.25 * cmfd::spectral * w); } // Throw error, as max iterations met @@ -125,23 +148,24 @@ int cmfd_linsolver_1g(double* A_data, double* b, double* x, double tol) { // CMFD_LINSOLVER_2G solves a two group CMFD linear system //============================================================================== -int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { +int cmfd_linsolver_2g(const double* A_data, const double* b, double* x, + double tol) +{ // Set overrelaxation parameter double w = 1.0; // Perform Gauss-Seidel iterations for (int igs = 1; igs <= 10000; igs++) { - double tmpx[dim]; double err = 0.0; // Copy over x vector - std::copy(x, x+dim, tmpx); + std::vector tmpx {x, x+cmfd::dim}; // Perform red/black Gauss-Seidel iterations for (int irb = 0; irb < 2; irb++) { // Loop around matrix rows - for (int irow = 0; irow < dim; irow+=2) { + for (int irow = 0; irow < cmfd::dim; irow+=2) { int g, i, j, k; matrix_to_indices(irow, g, i, j, k); @@ -168,14 +192,14 @@ int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { // Perform temporary sums, first do left of diag, then right of diag double tmp1 = 0.0; double tmp2 = 0.0; - for (int icol = indptr[irow]; icol < d1idx; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; - for (int icol = indptr[irow+1]; icol < d2idx-1; icol++) - tmp2 += A_data[icol] * x[indices[icol]]; - for (int icol = d1idx + 2; icol < indptr[irow + 1]; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; - for (int icol = d2idx + 1; icol < indptr[irow + 2]; icol++) - tmp2 += A_data[icol] * x[indices[icol]]; + for (int icol = cmfd::indptr[irow]; icol < d1idx; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; + for (int icol = cmfd::indptr[irow+1]; icol < d2idx-1; icol++) + tmp2 += A_data[icol] * x[cmfd::indices[icol]]; + for (int icol = d1idx + 2; icol < cmfd::indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; + for (int icol = d2idx + 1; icol < cmfd::indptr[irow + 2]; icol++) + tmp2 += A_data[icol] * x[cmfd::indices[icol]]; // Adjust with RHS vector tmp1 = b[irow] - tmp1; @@ -196,12 +220,12 @@ int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { } // Check convergence - err = std::sqrt(err / dim); + err = std::sqrt(err / cmfd::dim); if (err < tol) return igs; // Calculate new overrelaxation parameter - w = 1.0/(1.0 - 0.25 * spectral * w); + w = 1.0/(1.0 - 0.25 * cmfd::spectral * w); } // Throw error, as max iterations met @@ -215,29 +239,30 @@ int cmfd_linsolver_2g(double* A_data, double* b, double* x, double tol) { // CMFD_LINSOLVER_NG solves a general CMFD linear system //============================================================================== -int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol) { +int cmfd_linsolver_ng(const double* A_data, const double* b, double* x, + double tol) +{ // Set overrelaxation parameter double w = 1.0; // Perform Gauss-Seidel iterations for (int igs = 1; igs <= 10000; igs++) { - double tmpx[dim]; double err = 0.0; // Copy over x vector - std::copy(x, x+dim, tmpx); + std::vector tmpx {x, x+cmfd::dim}; // Loop around matrix rows - for (int irow = 0; irow < dim; irow++) { + for (int irow = 0; irow < cmfd::dim; irow++) { // Get index of diagonal for current row int didx = get_diagonal_index(irow); // Perform temporary sums, first do left of diag, then right of diag double tmp1 = 0.0; - for (int icol = indptr[irow]; icol < didx; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; - for (int icol = didx + 1; icol < indptr[irow + 1]; icol++) - tmp1 += A_data[icol] * x[indices[icol]]; + for (int icol = cmfd::indptr[irow]; icol < didx; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; + for (int icol = didx + 1; icol < cmfd::indptr[irow + 1]; icol++) + tmp1 += A_data[icol] * x[cmfd::indices[icol]]; // Solve for new x double x1 = (b[irow] - tmp1) / A_data[didx]; @@ -251,12 +276,12 @@ int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol) { } // Check convergence - err = std::sqrt(err / dim); + err = std::sqrt(err / cmfd::dim); if (err < tol) return igs; // Calculate new overrelaxation parameter - w = 1.0/(1.0 - 0.25 * spectral * w); + w = 1.0/(1.0 - 0.25 * cmfd::spectral * w); } // Throw error, as max iterations met @@ -266,50 +291,40 @@ int cmfd_linsolver_ng(double* A_data, double* b, double* x, double tol) { return -1; } -//============================================================================== -// MATRIX_TO_INDICES converts a matrix index to spatial and group -// indices -//============================================================================== - -void matrix_to_indices(int irow, int& g, int& i, int& j, int& k) { - g = irow % ng; - i = indexmap(irow/ng, 0); - j = indexmap(irow/ng, 1); - k = indexmap(irow/ng, 2); -} - //============================================================================== // OPENMC_INITIALIZE_LINSOLVER sets the fixed variables that are used for the // linear solver //============================================================================== extern "C" -void openmc_initialize_linsolver(int* indptr, int len_indptr, int* indices, - int n_elements, int dim, double spectral, - int* cmfd_indices, int* map) { +void openmc_initialize_linsolver(const int* indptr, int len_indptr, + const int* indices, int n_elements, int dim, + double spectral, const int* cmfd_indices, + const int* map) +{ // Store elements of indptr for (int i = 0; i < len_indptr; i++) - openmc::indptr.push_back(indptr[i]); + cmfd::indptr.push_back(indptr[i]); // Store elements of indices for (int i = 0; i < n_elements; i++) - openmc::indices.push_back(indices[i]); + cmfd::indices.push_back(indices[i]); // Set dimenion of CMFD problem and specral radius - openmc::dim = dim; - openmc::spectral = spectral; + cmfd::dim = dim; + cmfd::spectral = spectral; // Set number of groups - openmc::ng = cmfd_indices[3]; + cmfd::ng = cmfd_indices[3]; // Set problem dimensions and indexmap if 1 or 2 group problem - if (openmc::ng == 1 || openmc::ng == 2) { - openmc::nx = cmfd_indices[0]; - openmc::ny = cmfd_indices[1]; - openmc::nz = cmfd_indices[2]; + if (cmfd::ng == 1 || cmfd::ng == 2) { + cmfd::nx = cmfd_indices[0]; + cmfd::ny = cmfd_indices[1]; + cmfd::nz = cmfd_indices[2]; // Resize indexmap and set its elements - openmc::indexmap.resize({static_cast(dim), 3}); + cmfd::indexmap.resize({static_cast(dim), 3}); set_indexmap(map); } } @@ -320,8 +335,10 @@ void openmc_initialize_linsolver(int* indptr, int len_indptr, int* indices, //============================================================================== extern "C" -int openmc_run_linsolver(double* A_data, double* b, double* x, double tol) { - switch (ng) { +int openmc_run_linsolver(const double* A_data, const double* b, double* x, + double tol) +{ + switch (cmfd::ng) { case 1: return cmfd_linsolver_1g(A_data, b, x, tol); case 2: diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc index 83d9bed0c4..731aa70a93 100644 --- a/src/relaxng/settings.rnc +++ b/src/relaxng/settings.rnc @@ -59,8 +59,6 @@ element settings { element dagmc { xsd:boolean }? & - element run_cmfd { xsd:boolean }? & - element run_mode { xsd:string }? & element seed { xsd:positiveInteger }? & diff --git a/src/settings.cpp b/src/settings.cpp index 36657c6c62..3ff6475eb4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -687,11 +687,6 @@ void read_settings_xml() } } - // Check for cmfd run - if (check_for_node(root, "run_cmfd")) { - cmfd_run = get_node_value_bool(root, "run_cmfd"); - } - // Resonance scattering parameters if (check_for_node(root, "resonance_scattering")) { xml_node node_res_scat = root.child("resonance_scattering"); diff --git a/tests/regression_tests/cmfd_feed/settings.xml b/tests/regression_tests/cmfd_feed/settings.xml index 742aa22330..24b0b6ab50 100644 --- a/tests/regression_tests/cmfd_feed/settings.xml +++ b/tests/regression_tests/cmfd_feed/settings.xml @@ -23,7 +23,4 @@ 10 - - true - diff --git a/tests/regression_tests/cmfd_nofeed/settings.xml b/tests/regression_tests/cmfd_nofeed/settings.xml index b56acb4b76..24b0b6ab50 100644 --- a/tests/regression_tests/cmfd_nofeed/settings.xml +++ b/tests/regression_tests/cmfd_nofeed/settings.xml @@ -23,7 +23,4 @@ 10 - - true - From 5c04eb0d3baceb066b723bd04e20e5229bb3267f Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 12 Dec 2018 12:20:48 -0500 Subject: [PATCH 49/58] Update test suite with 2g, ng solvers --- include/openmc/constants.h | 2 +- openmc/cmfd.py | 21 +- tests/regression_tests/cmfd_feed/test.py | 120 +++- .../cmfd_feed_2g/geometry.xml | 31 + .../cmfd_feed_2g/materials.xml | 59 ++ .../cmfd_feed_2g/results_true.dat | 434 ++++++++++++ .../cmfd_feed_2g/settings.xml | 25 + .../regression_tests/cmfd_feed_2g/tallies.xml | 21 + tests/regression_tests/cmfd_feed_2g/test.py | 46 ++ .../cmfd_feed_ng/geometry.xml | 31 + .../cmfd_feed_ng/materials.xml | 59 ++ .../cmfd_feed_ng/results_true.dat | 621 ++++++++++++++++++ .../cmfd_feed_ng/settings.xml | 25 + .../regression_tests/cmfd_feed_ng/tallies.xml | 21 + tests/regression_tests/cmfd_feed_ng/test.py | 47 ++ tests/regression_tests/cmfd_nofeed/test.py | 24 +- tests/testing_harness.py | 9 + 17 files changed, 1575 insertions(+), 21 deletions(-) create mode 100644 tests/regression_tests/cmfd_feed_2g/geometry.xml create mode 100644 tests/regression_tests/cmfd_feed_2g/materials.xml create mode 100644 tests/regression_tests/cmfd_feed_2g/results_true.dat create mode 100644 tests/regression_tests/cmfd_feed_2g/settings.xml create mode 100644 tests/regression_tests/cmfd_feed_2g/tallies.xml create mode 100644 tests/regression_tests/cmfd_feed_2g/test.py create mode 100644 tests/regression_tests/cmfd_feed_ng/geometry.xml create mode 100644 tests/regression_tests/cmfd_feed_ng/materials.xml create mode 100644 tests/regression_tests/cmfd_feed_ng/results_true.dat create mode 100644 tests/regression_tests/cmfd_feed_ng/settings.xml create mode 100644 tests/regression_tests/cmfd_feed_ng/tallies.xml create mode 100644 tests/regression_tests/cmfd_feed_ng/test.py diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 96fc19b2bd..7697843510 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -438,7 +438,7 @@ constexpr int RUN_MODE_VOLUME {5}; // CMFD CONSTANTS // For non-accelerated regions on coarse mesh overlay -constexpr int CMFD_NOACCEL {99999}; +constexpr int CMFD_NOACCEL {-1}; } // namespace openmc diff --git a/openmc/cmfd.py b/openmc/cmfd.py index bb40da618e..5626f72eed 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -252,6 +252,10 @@ class CMFDRun(object): CMFD problem * "math" - Create adjoint matrices mathematically as the transpose of loss and production CMFD matrices + indices : numpy.ndarray + Stores spatial and group dimensions as [nx, ny, nz, ng] + cmfd_src : numpy.ndarray + CMFD source distribution calculated from solving CMFD equations entropy : list of floats "Shannon entropy" from cmfd fission source, stored for each generation that CMFD is invoked @@ -443,6 +447,14 @@ class CMFDRun(object): def gauss_seidel_tolerance(self): return self._gauss_seidel_tolerance + @property + def indices(self): + return self._indices + + @property + def cmfd_src(self): + return self._cmfd_src + @property def dom(self): return self._dom @@ -555,7 +567,7 @@ class CMFDRun(object): def adjoint_type(self, adjoint_type): check_type('CMFD adjoint type', adjoint_type, str) check_value('CMFD adjoint type', adjoint_type, - ['math', 'phyical']) + ['math', 'physical']) self._adjoint_type = adjoint_type @power_monitor.setter @@ -1005,7 +1017,7 @@ class CMFDRun(object): # Get all data entries for particular row in matrix data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] for i in range(len(cols)): - fh.write('({:3d}, {:3d}): {:0.8f}\n'.format( + fh.write('{:3d}, {:3d}, {:0.8f}\n'.format( row, cols[i], data[i])) # Save matrix in scipy format @@ -1572,8 +1584,9 @@ class CMFDRun(object): kerr = abs(k_o - k_n) / k_n # Calculate max error in source - serr = np.sqrt(np.sum(np.where(s_n > 0, ((s_n-s_o) / s_n)**2, 0)) - / len(s_n)) + with np.errstate(divide='ignore', invalid='ignore'): + serr = np.sqrt(np.sum(np.where(s_n > 0, ((s_n-s_o) / s_n)**2, 0)) + / len(s_n)) # Check for convergence iconv = kerr < self._cmfd_ktol and serr < self._stol diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 368e81aee2..61d4ae2eff 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -1,8 +1,17 @@ from tests.testing_harness import CMFDTestHarness import openmc import numpy as np +import scipy.sparse -def test_cmfd_feed(): +def test_cmfd_physical_adjoint(): + """ Test physical adjoint functionality of CMFD + + This test runs CMFD with a physical adjoint calculation and asserts that the + adjoint k-effective and flux vector are equal to the non-adjoint + k-effective and flux vector at the last batch (equivalent for 1 group + problems). + + """ # Initialize and set CMFD mesh cmfd_mesh = openmc.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 @@ -12,10 +21,111 @@ def test_cmfd_feed(): # Initialize and run CMFDRun object cmfd_run = openmc.CMFDRun() - cmfd_run.cmfd_mesh = cmfd_mesh - cmfd_run.cmfd_begin = 5 - cmfd_run.cmfd_display = 'dominance' - cmfd_run.cmfd_feedback = True + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run_adjoint = True + cmfd_run.adjoint_type = 'physical' + cmfd_run.run() + assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) + assert(cmfd_run._adj_keff == cmfd_run._keff) + +def test_cmfd_math_adjoint(): + """ Test mathematical adjoint functionality of CMFD + + This test runs CMFD with a mathematical adjoint calculation and asserts that + the adjoint k-effective and flux vector are equal to the non-adjoint + k-effective and flux vector at the last batch (equivalent for 1 group + problems). + + """ + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -10.0, -1.0, -1.0 + cmfd_mesh.upper_right = 10.0, 1.0, 1.0 + cmfd_mesh.dimension = 10, 1, 1 + cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run_adjoint = True + cmfd_run.adjoint_type = 'math' + cmfd_run.run() + assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) + assert(cmfd_run._adj_keff == cmfd_run._keff) + +def test_cmfd_write_matrices(): + """ Test write matrices functionality of CMFD + + This test runs CMFD with feedback and loads the loss/production matrices + and flux vector that are saved to disk, and checks to make sure these + values are consistent with each other and simulation results. + + """ + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -10.0, -1.0, -1.0 + cmfd_mesh.upper_right = 10.0, 1.0, 1.0 + cmfd_mesh.dimension = 10, 1, 1 + cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.write_matrices = True + cmfd_run.run() + + # Load loss matrix from numpy output file + loss_np = scipy.sparse.load_npz('loss.npz').todense() + # Load loss matrix from data file + loss_dat = np.loadtxt("loss.dat", delimiter=',') + + # Go through each element of loss_dat and compare to loss_np + for elem in loss_dat: + assert(np.isclose(loss_np[int(elem[0]), int(elem[1])], elem[2])) + + # Load production matrix from numpy output file + prod_np = scipy.sparse.load_npz('prod.npz').todense() + # Load production matrix from data file + prod_dat = np.loadtxt("prod.dat", delimiter=',') + + # Go through each element of prod_dat and compare to prod_np + for elem in prod_dat: + assert(np.isclose(prod_np[int(elem[0]), int(elem[1])], elem[2])) + + # Load flux vector from numpy output file + flux_np = np.load('fluxvec.npy') + # Load flux from data file + flux_dat = np.loadtxt("fluxvec.dat", delimiter='\n') + + # Compare flux from numpy file, .dat file, and from simulation + assert(np.all(np.isclose(flux_np, cmfd_run._phi))) + assert(np.all(np.isclose(flux_np, flux_dat))) + +def test_cmfd_feed(): + """ Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -10.0, -1.0, -1.0 + cmfd_mesh.upper_right = 10.0, 1.0, 1.0 + cmfd_mesh.dimension = 10, 1, 1 + cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() diff --git a/tests/regression_tests/cmfd_feed_2g/geometry.xml b/tests/regression_tests/cmfd_feed_2g/geometry.xml new file mode 100644 index 0000000000..9fdd2ddcb6 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/geometry.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + 2 2 + -1.25984 -1.25984 + 1.25984 1.25984 + + 763 763 + 763 763 + + + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_2g/materials.xml b/tests/regression_tests/cmfd_feed_2g/materials.xml new file mode 100644 index 0000000000..bfa2d8842d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/materials.xml @@ -0,0 +1,59 @@ + + + + 300 + + + + + + 300 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 300 + + + + + + + + + + 300 + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_2g/results_true.dat b/tests/regression_tests/cmfd_feed_2g/results_true.dat new file mode 100644 index 0000000000..2be15c4e88 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/results_true.dat @@ -0,0 +1,434 @@ +k-combined: +1.038883E+00 1.017026E-02 +tally 1: +1.167304E+02 +1.362680E+03 +1.157179E+02 +1.339273E+03 +1.167087E+02 +1.362782E+03 +1.164493E+02 +1.356411E+03 +tally 2: +4.400233E+01 +9.698989E+01 +6.541957E+01 +2.144299E+02 +1.863245E+02 +1.738212E+03 +1.038047E+02 +5.390768E+02 +4.368854E+01 +9.564001E+01 +6.489620E+01 +2.109923E+02 +1.836691E+02 +1.687792E+03 +1.022706E+02 +5.232407E+02 +4.433402E+01 +9.859049E+01 +6.548060E+01 +2.150470E+02 +1.839219E+02 +1.692209E+03 +1.031634E+02 +5.323852E+02 +4.380686E+01 +9.606723E+01 +6.480355E+01 +2.103905E+02 +1.868847E+02 +1.747254E+03 +1.038109E+02 +5.391360E+02 +tally 3: +6.192979E+01 +1.921761E+02 +0.000000E+00 +0.000000E+00 +2.033842E-02 +4.375294E-05 +4.296338E+00 +9.280716E-01 +3.493776E+00 +6.157094E-01 +0.000000E+00 +0.000000E+00 +9.876716E+01 +4.880113E+02 +9.043140E-01 +4.156054E-02 +6.138292E+01 +1.887853E+02 +0.000000E+00 +0.000000E+00 +1.591722E-02 +2.328432E-05 +4.296798E+00 +9.289349E-01 +3.584967E+00 +6.444749E-01 +0.000000E+00 +0.000000E+00 +9.717439E+01 +4.724254E+02 +8.435691E-01 +3.666151E-02 +6.192881E+01 +1.924016E+02 +0.000000E+00 +0.000000E+00 +1.796382E-02 +3.190854E-05 +4.343238E+00 +9.514039E-01 +3.504683E+00 +6.157615E-01 +0.000000E+00 +0.000000E+00 +9.818871E+01 +4.822799E+02 +8.401346E-01 +3.664037E-02 +6.130607E+01 +1.882837E+02 +0.000000E+00 +0.000000E+00 +1.220252E-02 +1.711667E-05 +4.245413E+00 +9.056738E-01 +3.468894E+00 +6.033679E-01 +0.000000E+00 +0.000000E+00 +9.885292E+01 +4.888720E+02 +9.509199E-01 +4.670951E-02 +tally 4: +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.200260E+00 +4.243011E+00 +3.728022E+01 +6.953801E+01 +9.178009E+00 +4.218903E+00 +3.738925E+01 +6.993833E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.019225E+00 +4.079528E+00 +3.709185E+01 +6.883085E+01 +9.037478E+00 +4.095846E+00 +3.710303E+01 +6.888447E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.178009E+00 +4.218903E+00 +3.738925E+01 +6.993833E+01 +9.200260E+00 +4.243011E+00 +3.728022E+01 +6.953801E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.097360E+00 +4.147125E+00 +3.700603E+01 +6.851110E+01 +9.003417E+00 +4.059372E+00 +3.717266E+01 +6.912709E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.042233E+00 +4.097018E+00 +3.711597E+01 +6.891834E+01 +9.107597E+00 +4.161211E+00 +3.715168E+01 +6.904544E+01 +9.037478E+00 +4.095846E+00 +3.710303E+01 +6.888447E+01 +9.019225E+00 +4.079528E+00 +3.709185E+01 +6.883085E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.107597E+00 +4.161211E+00 +3.715168E+01 +6.904544E+01 +9.042233E+00 +4.097018E+00 +3.711597E+01 +6.891834E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.003417E+00 +4.059372E+00 +3.717266E+01 +6.912709E+01 +9.097360E+00 +4.147125E+00 +3.700603E+01 +6.851110E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +6.195013E+01 +1.923032E+02 +1.022516E+02 +5.230716E+02 +1.418310E+01 +1.011490E+01 +4.677537E+01 +1.094859E+02 +6.139884E+01 +1.888834E+02 +1.007512E+02 +5.078162E+02 +1.403236E+01 +9.884347E+00 +4.598486E+01 +1.058070E+02 +6.194677E+01 +1.925125E+02 +1.016864E+02 +5.172531E+02 +1.407797E+01 +9.971785E+00 +4.623118E+01 +1.069663E+02 +6.131828E+01 +1.883600E+02 +1.023107E+02 +5.236668E+02 +1.375796E+01 +9.501501E+00 +4.638162E+01 +1.076532E+02 +cmfd indices +2.000000E+00 +2.000000E+00 +1.000000E+00 +2.000000E+00 +k cmfd +1.034617E+00 +1.034521E+00 +1.036479E+00 +1.035658E+00 +1.032505E+00 +1.027277E+00 +1.029326E+00 +1.029881E+00 +1.035579E+00 +1.034753E+00 +1.031963E+00 +1.034797E+00 +1.035383E+00 +1.035908E+00 +1.035629E+00 +1.036034E+00 +cmfd entropy +1.999493E+00 +1.999058E+00 +1.999331E+00 +1.999370E+00 +1.999383E+00 +1.999455E+00 +1.999640E+00 +1.999810E+00 +1.999881E+00 +1.999937E+00 +1.999973E+00 +1.999965E+00 +1.999972E+00 +1.999982E+00 +1.999998E+00 +1.999984E+00 +cmfd balance +3.993043E-04 +4.168558E-04 +6.384693E-04 +3.928217E-04 +3.789833E-04 +2.684860E-04 +4.849910E-04 +1.084019E-03 +1.091775E-03 +5.459767E-04 +4.455545E-04 +4.011468E-04 +3.710248E-04 +3.577152E-04 +3.845993E-04 +3.900672E-04 +cmfd dominance ratio + 6.239E-03 + 6.303E-03 + 6.347E-03 + 6.388E-03 + 6.340E-03 + 6.334E-03 + 6.270E-03 + 6.173E-03 + 6.068E-03 + 6.061E-03 + 6.016E-03 + 6.108E-03 + 6.117E-03 + 6.074E-03 + 6.005E-03 + 5.996E-03 +cmfd openmc source comparison +1.931386E-05 +2.839162E-05 +1.407962E-05 +6.718148E-06 +9.164193E-06 +1.382539E-05 +2.871606E-05 +4.192300E-05 +5.327516E-05 +6.566500E-05 +6.655541E-05 +6.034977E-05 +6.004677E-05 +5.711623E-05 +6.271264E-05 +6.425363E-05 +cmfd source +2.510278E-01 +2.480592E-01 +2.501750E-01 +2.507380E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/cmfd_feed_2g/settings.xml b/tests/regression_tests/cmfd_feed_2g/settings.xml new file mode 100644 index 0000000000..9e483e6a09 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/settings.xml @@ -0,0 +1,25 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + -1.25984 -1.25984 -1 1.25984 1.25984 1 + + + + + + 2 2 1 + -1.25984 -1.25984 -1.0 + 1.25984 1.25984 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_2g/tallies.xml b/tests/regression_tests/cmfd_feed_2g/tallies.xml new file mode 100644 index 0000000000..477a078331 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -1.25984 -1.25984 -1.0 + 1.25984 1.25984 1.0 + 2 2 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py new file mode 100644 index 0000000000..8377e208a8 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -0,0 +1,46 @@ +from tests.testing_harness import CMFDTestHarness +import openmc +import numpy as np + +def test_cmfd_feed_2g(): + """ Test 2 group CMFD solver results with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 + cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 + cmfd_mesh.dimension = 2, 2, 1 + cmfd_mesh.energy = [0.0, 0.625, 20000000] + cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True + cmfd_run.downscatter = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Create output string of all CMFD results to pass into testing harness + outstr = 'cmfd indices\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\nk cmfd\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\ncmfd entropy\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\ncmfd balance\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\ncmfd dominance ratio\n' + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\ncmfd openmc source comparison\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\ncmfd source\n' + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + order='F') + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) + outstr += '\n' + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness.main() diff --git a/tests/regression_tests/cmfd_feed_ng/geometry.xml b/tests/regression_tests/cmfd_feed_ng/geometry.xml new file mode 100644 index 0000000000..9fdd2ddcb6 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/geometry.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + 2 2 + -1.25984 -1.25984 + 1.25984 1.25984 + + 763 763 + 763 763 + + + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_ng/materials.xml b/tests/regression_tests/cmfd_feed_ng/materials.xml new file mode 100644 index 0000000000..bfa2d8842d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/materials.xml @@ -0,0 +1,59 @@ + + + + 300 + + + + + + 300 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 300 + + + + + + + + + + 300 + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_ng/results_true.dat b/tests/regression_tests/cmfd_feed_ng/results_true.dat new file mode 100644 index 0000000000..fea3d38b8b --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/results_true.dat @@ -0,0 +1,621 @@ +k-combined: +1.030507E+00 9.667655E-03 +tally 1: +1.159445E+02 +1.344573E+03 +1.159285E+02 +1.344429E+03 +1.161340E+02 +1.349209E+03 +1.160434E+02 +1.347427E+03 +tally 2: +3.482032E+01 +7.610041E+01 +5.123532E+01 +1.648226E+02 +1.063498E+01 +7.110316E+00 +9.001212E+00 +5.085589E+00 +1.435947E+02 +1.322778E+03 +7.400822E+01 +3.425876E+02 +3.415144E+01 +7.318850E+01 +5.046314E+01 +1.597317E+02 +1.028893E+01 +6.661534E+00 +8.653997E+00 +4.698660E+00 +1.442064E+02 +1.338979E+03 +7.405073E+01 +3.428774E+02 +3.431740E+01 +7.390622E+01 +5.109288E+01 +1.639090E+02 +1.034894E+01 +6.746021E+00 +8.657005E+00 +4.712481E+00 +1.383790E+02 +1.200016E+03 +7.320761E+01 +3.351661E+02 +3.426169E+01 +7.373156E+01 +5.042174E+01 +1.596859E+02 +1.041331E+01 +6.828453E+00 +8.729287E+00 +4.779221E+00 +1.374766E+02 +1.182015E+03 +7.390660E+01 +3.416196E+02 +tally 3: +4.842028E+01 +1.472522E+02 +0.000000E+00 +0.000000E+00 +1.766768E-02 +3.669087E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.489090E+00 +7.670426E-01 +2.522457E+00 +3.988419E-01 +0.000000E+00 +0.000000E+00 +6.323118E+00 +2.515554E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +9.772798E-02 +7.833336E-04 +3.020916E-01 +5.962947E-03 +0.000000E+00 +0.000000E+00 +2.669802E+00 +4.494602E-01 +0.000000E+00 +0.000000E+00 +6.995726E+01 +3.061105E+02 +6.245439E-01 +2.516241E-02 +4.767259E+01 +1.425559E+02 +0.000000E+00 +0.000000E+00 +1.511055E-02 +2.706989E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.359388E+00 +7.162206E-01 +2.420435E+00 +3.693966E-01 +0.000000E+00 +0.000000E+00 +6.097921E+00 +2.335376E+00 +0.000000E+00 +0.000000E+00 +2.973834E-03 +2.952905E-06 +9.585516E-02 +7.950754E-04 +3.306195E-01 +7.226156E-03 +0.000000E+00 +0.000000E+00 +2.606740E+00 +4.273433E-01 +0.000000E+00 +0.000000E+00 +7.004531E+01 +3.068103E+02 +5.609353E-01 +2.054700E-02 +4.844434E+01 +1.474236E+02 +0.000000E+00 +0.000000E+00 +6.909228E-03 +6.822381E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.337728E+00 +7.036827E-01 +2.418767E+00 +3.680337E-01 +0.000000E+00 +0.000000E+00 +6.114057E+00 +2.352184E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.057559E-01 +8.659764E-04 +3.268970E-01 +6.949165E-03 +0.000000E+00 +0.000000E+00 +2.538042E+00 +4.047397E-01 +0.000000E+00 +0.000000E+00 +6.922289E+01 +2.996756E+02 +6.190458E-01 +2.456695E-02 +4.757150E+01 +1.421640E+02 +0.000000E+00 +0.000000E+00 +8.765775E-03 +1.054666E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.436793E+00 +7.430145E-01 +2.473841E+00 +3.843375E-01 +0.000000E+00 +0.000000E+00 +6.104077E+00 +2.339598E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.061129E-01 +8.936114E-04 +3.092546E-01 +6.384841E-03 +0.000000E+00 +0.000000E+00 +2.539292E+00 +4.045561E-01 +0.000000E+00 +0.000000E+00 +6.994273E+01 +3.059846E+02 +5.887450E-01 +2.235244E-02 +tally 4: +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.109363E+00 +3.174618E+00 +2.099346E+00 +2.784777E-01 +2.785196E+01 +4.850659E+01 +7.069021E+00 +3.132588E+00 +2.149589E+00 +2.900131E-01 +2.768129E+01 +4.790811E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.175941E+00 +3.224156E+00 +2.135542E+00 +2.863424E-01 +2.728334E+01 +4.655111E+01 +7.206774E+00 +3.256252E+00 +2.075924E+00 +2.712640E-01 +2.747967E+01 +4.722188E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.069021E+00 +3.132588E+00 +2.149589E+00 +2.900131E-01 +2.768129E+01 +4.790811E+01 +7.109363E+00 +3.174618E+00 +2.099346E+00 +2.784777E-01 +2.785196E+01 +4.850659E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.108601E+00 +3.175833E+00 +2.103259E+00 +2.783890E-01 +2.784173E+01 +4.846581E+01 +7.107753E+00 +3.173064E+00 +2.087728E+00 +2.737368E-01 +2.779197E+01 +4.828855E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.148041E+00 +3.216417E+00 +2.124230E+00 +2.831606E-01 +2.743879E+01 +4.708424E+01 +7.081749E+00 +3.151711E+00 +2.062609E+00 +2.682691E-01 +2.761154E+01 +4.768316E+01 +7.206774E+00 +3.256252E+00 +2.075924E+00 +2.712640E-01 +2.747967E+01 +4.722188E+01 +7.175941E+00 +3.224156E+00 +2.135542E+00 +2.863424E-01 +2.728334E+01 +4.655111E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.081749E+00 +3.151711E+00 +2.062609E+00 +2.682691E-01 +2.761154E+01 +4.768316E+01 +7.148041E+00 +3.216417E+00 +2.124230E+00 +2.831606E-01 +2.743879E+01 +4.708424E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.107753E+00 +3.173064E+00 +2.087728E+00 +2.737368E-01 +2.779197E+01 +4.828855E+01 +7.108601E+00 +3.175833E+00 +2.103259E+00 +2.783890E-01 +2.784173E+01 +4.846581E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +4.843795E+01 +1.473599E+02 +8.846575E+00 +4.912671E+00 +7.292412E+01 +3.326224E+02 +1.096673E+01 +7.549697E+00 +4.119614E+00 +1.064248E+00 +3.323487E+01 +6.913295E+01 +4.768770E+01 +1.426471E+02 +8.521329E+00 +4.554207E+00 +7.297983E+01 +3.330439E+02 +1.083867E+01 +7.393362E+00 +3.955093E+00 +9.810363E-01 +3.309790E+01 +6.852254E+01 +4.845125E+01 +1.474656E+02 +8.533824E+00 +4.579629E+00 +7.208076E+01 +3.249387E+02 +1.100395E+01 +7.627044E+00 +3.908798E+00 +9.610162E-01 +3.287630E+01 +6.763801E+01 +4.758027E+01 +1.422161E+02 +8.577918E+00 +4.614174E+00 +7.278634E+01 +3.313433E+02 +1.072190E+01 +7.216342E+00 +3.922303E+00 +9.640585E-01 +3.334768E+01 +6.955922E+01 +cmfd indices +2.000000E+00 +2.000000E+00 +1.000000E+00 +3.000000E+00 +k cmfd +1.024488E+00 +1.022339E+00 +1.024720E+00 +1.025459E+00 +1.029180E+00 +1.022688E+00 +1.019305E+00 +1.020356E+00 +1.023384E+00 +1.023519E+00 +1.026360E+00 +cmfd entropy +1.999710E+00 +1.999709E+00 +1.999704E+00 +1.999747E+00 +1.999885E+00 +1.999911E+00 +1.999905E+00 +1.999936E+00 +1.999873E+00 +1.999826E+00 +1.999768E+00 +cmfd balance +3.179889E-04 +4.166064E-04 +2.746180E-04 +3.345930E-04 +5.129605E-04 +4.008511E-04 +3.628451E-04 +3.401727E-04 +3.166255E-04 +2.642675E-04 +2.482640E-04 +cmfd dominance ratio + 3.737E-03 + 3.786E-03 + 3.798E-03 + 3.789E-03 + 3.741E-03 + 3.772E-03 + 3.793E-03 + 3.684E-03 + 3.740E-03 + 3.764E-03 + 3.798E-03 +cmfd openmc source comparison +3.559244E-05 +4.429506E-05 +2.739679E-05 +2.715448E-05 +5.105340E-05 +4.069206E-05 +4.033877E-05 +3.669734E-05 +3.540373E-05 +2.907247E-05 +2.734478E-05 +cmfd source +2.564687E-01 +2.445733E-01 +2.473769E-01 +2.515811E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/cmfd_feed_ng/settings.xml b/tests/regression_tests/cmfd_feed_ng/settings.xml new file mode 100644 index 0000000000..9e483e6a09 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/settings.xml @@ -0,0 +1,25 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + -1.25984 -1.25984 -1 1.25984 1.25984 1 + + + + + + 2 2 1 + -1.25984 -1.25984 -1.0 + 1.25984 1.25984 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_ng/tallies.xml b/tests/regression_tests/cmfd_feed_ng/tallies.xml new file mode 100644 index 0000000000..477a078331 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -1.25984 -1.25984 -1.0 + 1.25984 1.25984 1.0 + 2 2 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py new file mode 100644 index 0000000000..e49f912aaa --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -0,0 +1,47 @@ +from tests.testing_harness import CMFDTestHarness +import openmc +import numpy as np + +def test_cmfd_feed_ng(): + """ Test n group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 + cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 + cmfd_mesh.dimension = 2, 2, 1 + cmfd_mesh.energy = [0.0, 0.625, 5.53080, 20000000] + cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + + # Initialize and run CMFDRun object + cmfd_run = openmc.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.reset = [5] + cmfd_run.begin = 10 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True + cmfd_run.downscatter = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Create output string of all CMFD results to pass into testing harness + outstr = 'cmfd indices\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\nk cmfd\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\ncmfd entropy\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\ncmfd balance\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\ncmfd dominance ratio\n' + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\ncmfd openmc source comparison\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\ncmfd source\n' + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + order='F') + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) + outstr += '\n' + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index 4e34029a9e..11f8187659 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -2,7 +2,9 @@ from tests.testing_harness import CMFDTestHarness import openmc import numpy as np + def test_cmfd_nofeed(): + """ Test 1 group CMFD solver without CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = openmc.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 @@ -12,28 +14,28 @@ def test_cmfd_nofeed(): # Initialize and run CMFDRun object cmfd_run = openmc.CMFDRun() - cmfd_run.cmfd_mesh = cmfd_mesh - cmfd_run.cmfd_begin = 5 - cmfd_run.cmfd_display = 'dominance' - cmfd_run.cmfd_feedback = False + cmfd_run.mesh = cmfd_mesh + cmfd_run.begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = False cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() # Create output string of all CMFD results to pass into testing harness outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.product(cmfd_run.indices), order='F') outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) outstr += '\n' diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 50f02ee63c..408ddf1878 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -165,6 +165,15 @@ class CMFDTestHarness(TestHarness): finally: self._cleanup() + def _cleanup(self): + """Delete output files for numpy matrices and flux vectors.""" + super()._cleanup() + output = ['loss.npz', 'loss.dat', 'prod.npz', 'prod.dat', + 'fluxvec.npy', 'fluxvec.dat'] + for f in output: + if os.path.exists(f): + os.remove(f) + class ParticleRestartTestHarness(TestHarness): """Specialized TestHarness for running OpenMC particle restart tests.""" From a9cc674cbc617c140c2cbcdaf88e6a4b11ce66df Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 12 Dec 2018 12:45:20 -0500 Subject: [PATCH 50/58] Use public accessor functions in CMFD test suite --- tests/regression_tests/cmfd_feed/test.py | 14 +++++++------- tests/regression_tests/cmfd_feed_2g/test.py | 14 +++++++------- tests/regression_tests/cmfd_feed_ng/test.py | 14 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 61d4ae2eff..242ae3d1e5 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -131,19 +131,19 @@ def test_cmfd_feed(): # Create output string of all CMFD results to pass into testing harness outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), order='F') outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) outstr += '\n' diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py index 8377e208a8..dba70809bf 100644 --- a/tests/regression_tests/cmfd_feed_2g/test.py +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -24,19 +24,19 @@ def test_cmfd_feed_2g(): # Create output string of all CMFD results to pass into testing harness outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), order='F') outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) outstr += '\n' diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py index e49f912aaa..fec9cceabf 100644 --- a/tests/regression_tests/cmfd_feed_ng/test.py +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -25,19 +25,19 @@ def test_cmfd_feed_ng(): # Create output string of all CMFD results to pass into testing harness outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._indices]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._k_cmfd]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._entropy]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._balance]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run._dom]) + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run._src_cmp]) + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run._indices), + cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), order='F') outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) outstr += '\n' From 8535a8953a9a2bdd46f9decaa5a66b2ebef263a2 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 12 Dec 2018 14:47:29 -0500 Subject: [PATCH 51/58] Add __init__.py to new test directories --- tests/regression_tests/cmfd_feed_2g/__init__.py | 0 tests/regression_tests/cmfd_feed_ng/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/regression_tests/cmfd_feed_2g/__init__.py create mode 100644 tests/regression_tests/cmfd_feed_ng/__init__.py diff --git a/tests/regression_tests/cmfd_feed_2g/__init__.py b/tests/regression_tests/cmfd_feed_2g/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/cmfd_feed_ng/__init__.py b/tests/regression_tests/cmfd_feed_ng/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From f049f76bb47f748551659a71ae483c13554e812d Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 12 Dec 2018 18:13:26 -0500 Subject: [PATCH 52/58] Deallocate CMFD arrays in openmc_finalize --- src/api.F90 | 4 ++++ src/cmfd_solver.cpp | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/api.F90 b/src/api.F90 index f45ffb235d..640ad910a5 100644 --- a/src/api.F90 +++ b/src/api.F90 @@ -84,6 +84,9 @@ contains subroutine free_memory_bank() bind(C) end subroutine free_memory_bank + + subroutine free_memory_cmfd() bind(C) + end subroutine free_memory_cmfd end interface call free_memory_geometry() @@ -101,6 +104,7 @@ contains call free_memory_tally_filter() call free_memory_tally_derivative() call free_memory_bank() + call free_memory_cmfd() #ifdef DAGMC call free_memory_dagmc() #endif diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 8f2091796b..1d5d1bc441 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -348,5 +348,15 @@ int openmc_run_linsolver(const double* A_data, const double* b, double* x, } } +//============================================================================== +// Fortran compatibility +//============================================================================== + +extern "C" void free_memory_cmfd() +{ + cmfd::indptr.clear(); + cmfd::indices.clear(); +} + } // namespace openmc \ No newline at end of file From d4cfb811140d21061fca8ba896876ae170c69899 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 13 Dec 2018 17:17:59 -0500 Subject: [PATCH 53/58] Fix test suite, remove cmfd from openmc/__init.py --- openmc/__init__.py | 1 - openmc/cmfd.py | 90 +++++++++++---------- src/cmfd_solver.cpp | 2 + tests/regression_tests/cmfd_feed/test.py | 18 ++--- tests/regression_tests/cmfd_feed_2g/test.py | 6 +- tests/regression_tests/cmfd_feed_ng/test.py | 6 +- tests/regression_tests/cmfd_nofeed/test.py | 6 +- 7 files changed, 66 insertions(+), 63 deletions(-) diff --git a/openmc/__init__.py b/openmc/__init__.py index 4627c340b2..aec7209c94 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -20,7 +20,6 @@ from openmc.trigger import * from openmc.tally_derivative import * from openmc.tallies import * from openmc.mgxs_library import * -from openmc.cmfd import * from openmc.executor import * from openmc.statepoint import * from openmc.summary import * diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 5626f72eed..c207d3b255 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -19,7 +19,7 @@ from ctypes import c_int, c_double import numpy as np from scipy import sparse -from openmc.capi import _dll, core, settings, filter, mesh, tally +import openmc.capi from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) from openmc.exceptions import OpenMCError @@ -639,9 +639,10 @@ class CMFDRun(object): # Check keyword arguments and pass to C API init function if 'openmc_args' in kwargs: - core.init(args=kwargs['openmc_args'], intracomm=self._intracomm) + openmc.capi.init(args=kwargs['openmc_args'], + intracomm=self._intracomm) else: - core.init(intracomm=self._intracomm) + openmc.capi.init(intracomm=self._intracomm) # Configure CMFD parameters and tallies self._configure_cmfd() @@ -659,7 +660,7 @@ class CMFDRun(object): self._initialize_linsolver() # Initialize simulation - core.simulation_init() + openmc.capi.simulation_init() status = 0 while status == 0: @@ -667,24 +668,24 @@ class CMFDRun(object): self._cmfd_init_batch() # Run next batch - status = core.next_batch() + status = openmc.capi.next_batch() # Perform CMFD calculation if on if self._cmfd_on: self._execute_cmfd() # Write CMFD output if CMFD on for current batch - if core.master(): + if openmc.capi.master(): self._write_cmfd_output() # Finalize simuation - core.simulation_finalize() + openmc.capi.simulation_finalize() # Print out CMFD timing statistics self._write_cmfd_timing_stats() # Finalize and free memory - core.finalize() + openmc.capi.finalize() def _initialize_linsolver(self): # Determine number of rows in CMFD matrix @@ -704,7 +705,7 @@ class CMFDRun(object): args = temp_loss.indptr, len(temp_loss.indptr), \ temp_loss.indices, len(temp_loss.indices), n, \ self._spectral, self._indices, coremap - return _dll.openmc_initialize_linsolver(*args) + return openmc.capi._dll.openmc_initialize_linsolver(*args) def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" @@ -726,7 +727,7 @@ class CMFDRun(object): def _write_cmfd_timing_stats(self): """Write CMFD timing stats to buffer after finalizing simulation""" - if core.master(): + if openmc.capi.master(): outstr = ("=====================> " "CMFD TIMING STATISTICS <====================\n\n" " Time in CMFD = {:.5E} seconds\n" @@ -752,7 +753,7 @@ class CMFDRun(object): def _read_cmfd_input(self): """Sets values of additional instance variables based on user input""" # Print message to user and flush output to stdout - if settings.verbosity >= 7 and core.master(): + if openmc.capi.settings.verbosity >= 7 and openmc.capi.master(): print(' Configuring CMFD parameters for simulation') sys.stdout.flush() @@ -766,7 +767,7 @@ class CMFDRun(object): self._indices[i] = n # Check if in continuous energy mode - if not settings.run_CE: + if not openmc.capi.settings.run_CE: raise OpenMCError('CMFD must be run in continuous energy mode') # Set number of energy groups @@ -847,7 +848,7 @@ class CMFDRun(object): """Handles CMFD options at the beginning of each batch""" # Get current batch through C API # Add 1 as next_batch has not been called yet - current_batch = core.current_batch() + 1 + current_batch = openmc.capi.current_batch() + 1 # Check to activate CMFD diffusion and possible feedback if self._begin == current_batch: @@ -860,7 +861,7 @@ class CMFDRun(object): def _execute_cmfd(self): """Runs CMFD calculation on master node""" # Run CMFD on single processor on master - if core.master(): + if openmc.capi.master(): # Start CMFD timer time_start_cmfd = time.time() @@ -874,7 +875,7 @@ class CMFDRun(object): self._k_cmfd.append(self._keff) # Check to perform adjoint on last batch - if (core.current_batch() == settings.batches + if (openmc.capi.current_batch() == openmc.capi.settings.batches and self._run_adjoint): self._cmfd_solver_execute(adjoint=True) @@ -885,19 +886,19 @@ class CMFDRun(object): self._cmfd_reweight(True) # Stop CMFD timer - if core.master(): + if openmc.capi.master(): time_stop_cmfd = time.time() self._time_cmfd += time_stop_cmfd - time_start_cmfd def _cmfd_tally_reset(self): """Resets all CMFD tallies in memory""" # Print message - if settings.verbosity >= 6 and core.master(): + if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): print(' CMFD tallies reset') sys.stdout.flush() # Reset CMFD tallies - tallies = tally.tallies + tallies = openmc.capi.tallies for tally_id in self._tally_ids: tallies[tally_id].reset() @@ -1073,7 +1074,7 @@ class CMFDRun(object): self._cmfd_src = cmfd_src / np.sum(cmfd_src) # Compute entropy - if settings.entropy_on: + if openmc.capi.settings.entropy_on: # Compute source times log_2(source) source = self._cmfd_src[self._cmfd_src > 0] \ * np.log(self._cmfd_src[self._cmfd_src > 0])/np.log(2) @@ -1113,12 +1114,12 @@ class CMFDRun(object): outside = self._count_bank_sites() # Check and raise error if source sites exist outside of CMFD mesh - if core.master() and outside: + if openmc.capi.master() and outside: raise OpenMCError('Source sites outside of the CMFD mesh') # Have master compute weight factors, ignore any zeros in # sourcecounts or cmfd_src - if core.master(): + if openmc.capi.master(): # Compute normalization factor norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) @@ -1150,13 +1151,13 @@ class CMFDRun(object): self._weightfactors = self._intracomm.bcast( self._weightfactors) - m = mesh.meshes[self._mesh_id] + m = openmc.capi.meshes[self._mesh_id] energy = self._egrid ng = self._indices[3] # Get xyz locations and energies of all particles in source bank - source_xyz = core.source_bank()['xyz'] - source_energies = core.source_bank()['E'] + source_xyz = openmc.capi.source_bank()['xyz'] + source_energies = openmc.capi.source_bank()['E'] # Convert xyz location to the CMFD mesh index mesh_ijk = np.floor((source_xyz-m.lower_left)/m.width).astype(int) @@ -1174,13 +1175,13 @@ class CMFDRun(object): # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight - core.source_bank()['wgt'] *= self._weightfactors[ + openmc.capi.source_bank()['wgt'] *= self._weightfactors[ mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] - if core.master() and np.any(source_energies < energy[0]): + if openmc.capi.master() and np.any(source_energies < energy[0]): print(' WARNING: Source pt below energy grid') sys.stdout.flush() - if core.master() and np.any(source_energies > energy[-1]): + if openmc.capi.master() and np.any(source_energies > energy[-1]): print(' WARNING: Source pt above energy grid') sys.stdout.flush() @@ -1194,8 +1195,8 @@ class CMFDRun(object): """ # Initialize variables - m = mesh.meshes[self._mesh_id] - bank = core.source_bank() + m = openmc.capi.meshes[self._mesh_id] + bank = openmc.capi.source_bank() energy = self._egrid sites_outside = np.zeros(1, dtype=bool) ng = self._indices[3] @@ -1204,8 +1205,8 @@ class CMFDRun(object): count = np.zeros(self._sourcecounts.shape) # Get xyz locations and energies of each particle in source bank - source_xyz = core.source_bank()['xyz'] - source_energies = core.source_bank()['E'] + source_xyz = openmc.capi.source_bank()['xyz'] + source_energies = openmc.capi.source_bank()['E'] # Convert xyz location to mesh index and ravel index to scalar mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) @@ -1490,7 +1491,7 @@ class CMFDRun(object): s_o = np.zeros((n,)) # Set initial guess - k_n = core.keff()[0] + k_n = openmc.capi.keff()[0] k_o = k_n dw = self._w_shift k_s = k_o + dw @@ -1521,7 +1522,8 @@ class CMFDRun(object): s_o /= k_lo # Compute new flux with C++ solver - innerits = _dll.openmc_run_linsolver(loss.data, s_o, phi_n, toli) + innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, s_o, + phi_n, toli) # Compute new source vector s_n = prod.dot(phi_n) @@ -1592,7 +1594,7 @@ class CMFDRun(object): iconv = kerr < self._cmfd_ktol and serr < self._stol # Print out to user - if self._power_monitor and core.master(): + if self._power_monitor and openmc.capi.master(): str1 = ' {:d}:'.format(iter) str2 = 'k-eff: {:0.8f}'.format(k_n) str3 = 'k-error: {:.5E}'.format(kerr) @@ -1644,13 +1646,13 @@ class CMFDRun(object): self._openmc_src.fill(0.) # Set mesh widths - self._hxyz[:,:,:,:] = mesh.meshes[self._mesh_id].width + self._hxyz[:,:,:,:] = openmc.capi.meshes[self._mesh_id].width # Reset keff_bal to zero self._keff_bal = 0. # Get tallies in-memory - tallies = tally.tallies + tallies = openmc.capi.tallies # Ravel coremap as 1d array similar to how tally data is arranged coremap = np.ravel(self._coremap.swapaxes(0, 2)) @@ -1855,7 +1857,7 @@ class CMFDRun(object): num_accel = self._mat_dim # Get openmc k-effective - keff = core.keff()[0] + keff = openmc.capi.keff()[0] # Define leakage in each mesh cell and energy group leakage = (((self._current[:,:,:,_CURRENTS['out_right'],:] - @@ -2655,7 +2657,7 @@ class CMFDRun(object): def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = mesh.Mesh() + cmfd_mesh = openmc.capi.Mesh() # Store id of Mesh object self._mesh_id = cmfd_mesh.id # Set dimension and parameters of Mesh object @@ -2665,29 +2667,29 @@ class CMFDRun(object): width=self._mesh.width) # Create Mesh Filter object, stored internally - mesh_filter = filter.MeshFilter() + mesh_filter = openmc.capi.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh # Set up energy filters, if applicable if self._energy_filters: # Create Energy Filter object, stored internally - energy_filter = filter.EnergyFilter() + energy_filter = openmc.capi.EnergyFilter() # Set bins for Energy Filter energy_filter.bins = self._egrid # Create Energy Out Filter object, stored internally - energyout_filter = filter.EnergyoutFilter() + energyout_filter = openmc.capi.EnergyoutFilter() # Set bins for Energy Filter energyout_filter.bins = self._egrid # Create Mesh Surface Filter object, stored internally - meshsurface_filter = filter.MeshSurfaceFilter() + meshsurface_filter = openmc.capi.MeshSurfaceFilter() # Set mesh for Mesh Surface Filter meshsurface_filter.mesh = cmfd_mesh # Create Legendre Filter object, stored internally - legendre_filter = filter.LegendreFilter() + legendre_filter = openmc.capi.LegendreFilter() # Set order for Legendre Filter legendre_filter.order = 1 @@ -2695,7 +2697,7 @@ class CMFDRun(object): n_tallies = 4 self._tally_ids = [] for i in range(n_tallies): - cmfd_tally = tally.Tally() + cmfd_tally = openmc.capi.Tally() # Set nuclide bins cmfd_tally.nuclides = ['total'] self._tally_ids.append(cmfd_tally.id) diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 1d5d1bc441..1fb95d1b76 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -356,6 +356,8 @@ extern "C" void free_memory_cmfd() { cmfd::indptr.clear(); cmfd::indices.clear(); + // Resize indexmap to be an empty array + cmfd::indexmap.resize({0}); } diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 242ae3d1e5..f19a9aede9 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -1,5 +1,5 @@ from tests.testing_harness import CMFDTestHarness -import openmc +from openmc import cmfd import numpy as np import scipy.sparse @@ -13,14 +13,14 @@ def test_cmfd_physical_adjoint(): """ # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 cmfd_mesh.upper_right = 10.0, 1.0, 1.0 cmfd_mesh.dimension = 10, 1, 1 cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.feedback = True @@ -41,14 +41,14 @@ def test_cmfd_math_adjoint(): """ # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 cmfd_mesh.upper_right = 10.0, 1.0, 1.0 cmfd_mesh.dimension = 10, 1, 1 cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.feedback = True @@ -68,14 +68,14 @@ def test_cmfd_write_matrices(): """ # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 cmfd_mesh.upper_right = 10.0, 1.0, 1.0 cmfd_mesh.dimension = 10, 1, 1 cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.display = {'dominance': True} @@ -114,14 +114,14 @@ def test_cmfd_write_matrices(): def test_cmfd_feed(): """ Test 1 group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 cmfd_mesh.upper_right = 10.0, 1.0, 1.0 cmfd_mesh.dimension = 10, 1, 1 cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.display = {'dominance': True} diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py index dba70809bf..4efe4c4dd1 100644 --- a/tests/regression_tests/cmfd_feed_2g/test.py +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -1,11 +1,11 @@ from tests.testing_harness import CMFDTestHarness -import openmc +from openmc import cmfd import numpy as np def test_cmfd_feed_2g(): """ Test 2 group CMFD solver results with CMFD feedback""" # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 cmfd_mesh.dimension = 2, 2, 1 @@ -13,7 +13,7 @@ def test_cmfd_feed_2g(): cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.display = {'dominance': True} diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py index fec9cceabf..74fbe0a64c 100644 --- a/tests/regression_tests/cmfd_feed_ng/test.py +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -1,11 +1,11 @@ from tests.testing_harness import CMFDTestHarness -import openmc +from openmc import cmfd import numpy as np def test_cmfd_feed_ng(): """ Test n group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 cmfd_mesh.dimension = 2, 2, 1 @@ -13,7 +13,7 @@ def test_cmfd_feed_ng(): cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.reset = [5] cmfd_run.begin = 10 diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index 11f8187659..1d6896cc95 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -1,19 +1,19 @@ from tests.testing_harness import CMFDTestHarness -import openmc +from openmc import cmfd import numpy as np def test_cmfd_nofeed(): """ Test 1 group CMFD solver without CMFD feedback""" # Initialize and set CMFD mesh - cmfd_mesh = openmc.CMFDMesh() + cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 cmfd_mesh.upper_right = 10.0, 1.0, 1.0 cmfd_mesh.dimension = 10, 1, 1 cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 # Initialize and run CMFDRun object - cmfd_run = openmc.CMFDRun() + cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.begin = 5 cmfd_run.display = {'dominance': True} From 1c226a8178a134a00fe36fa6d1da8e5abbdd88dd Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 14 Dec 2018 05:31:33 -0500 Subject: [PATCH 54/58] add openmc.checkvalue when importing openmc --- openmc/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openmc/__init__.py b/openmc/__init__.py index aec7209c94..715550302b 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -1,5 +1,6 @@ from openmc.arithmetic import * from openmc.cell import * +from openmc.checkvalue import * from openmc.mesh import * from openmc.element import * from openmc.geometry import * From b87515b7fbc2ed99eebd3b0b24915096bea03fdc Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 14 Dec 2018 20:33:13 -0500 Subject: [PATCH 55/58] Update output string, kwargs, docstring --- openmc/cmfd.py | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index c207d3b255..bbfc635698 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -53,8 +53,7 @@ _CURRENTS = { class CMFDMesh(object): - """A structured Cartesian mesh used for Coarse Mesh Finite Difference - (CMFD) acceleration. + """"A structured Cartesian mesh used for CMFD acceleration. Attributes ---------- @@ -188,10 +187,7 @@ class CMFDMesh(object): class CMFDRun(object): - r"""Class to run openmc with CMFD acceleration through the C API. Running - openmc in this manner obviates the need for defining CMFD parameters - through a cmfd.xml file. Instead, all input parameters should be passed - through the CMFDRun instance. + r"""Class for running CMFD acceleration through the C API. Attributes ---------- @@ -623,11 +619,8 @@ class CMFDRun(object): intracomm : mpi4py.MPI.Intracomm or None MPI intercommunicator to pass through C API. Set to MPI.COMM_WORLD by default - - Keyword arguments - ----------------- - openmc_args : list of str - Arguments to pass to ``openmc.capi.init()``, as a list + **kwargs + All keyword arguments are passed to :func:`openmc.capi.init`. """ # Store intracomm for part of CMFD routine where MPI reduce and @@ -637,12 +630,8 @@ class CMFDRun(object): elif intracomm is None and have_mpi: self._intracomm = MPI.COMM_WORLD - # Check keyword arguments and pass to C API init function - if 'openmc_args' in kwargs: - openmc.capi.init(args=kwargs['openmc_args'], - intracomm=self._intracomm) - else: - openmc.capi.init(intracomm=self._intracomm) + # Run and pass arguments to C API init function + openmc.capi.init(intracomm=self._intracomm, **kwargs) # Configure CMFD parameters and tallies self._configure_cmfd() @@ -710,19 +699,19 @@ class CMFDRun(object): def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" # Display CMFD k-effective - str1 = '{:>11s}CMFD k: {:0.5f}'.format('', self._k_cmfd[-1]) + outstr = '{:>11s}CMFD k: {:0.5f}'.format('', self._k_cmfd[-1]) # Display value of additional fields based on display dict - str2 = '\n' + outstr += '\n' if self._display['dominance']: - str2 += '{:>11s}Dom Rat: {:0.5f}\n'.format('', self._dom[-1]) + outstr += '{:>11s}Dom Rat: {:0.5f}\n'.format('', self._dom[-1]) if self._display['entropy']: - str2 += '{:>11s}CMFD Ent: {:0.5f}\n'.format('', self._entropy[-1]) + outstr += '{:>11s}CMFD Ent: {:0.5f}\n'.format('', self._entropy[-1]) if self._display['source']: - str2 += '{:>11s}RMS Src: {:0.5f}\n'.format('', self._src_cmp[-1]) + outstr += '{:>11s}RMS Src: {:0.5f}\n'.format('', self._src_cmp[-1]) if self._display['balance']: - str2 += '{:>11s}RMS Bal: {:0.5f}\n'.format('', self._balance[-1]) + outstr += '{:>11s}RMS Bal: {:0.5f}\n'.format('', self._balance[-1]) - print('{:s}{:s}'.format(str1, str2), end='') + print('{:s}'.format(outstr)) sys.stdout.flush() def _write_cmfd_timing_stats(self): From bb45f220d4418d1f5ecaf75f4f0b180e4975ca85 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 19 Dec 2018 02:04:11 -0500 Subject: [PATCH 56/58] clean up CMFD testing harness, adjoint logic, use run_in_memory --- openmc/capi/core.py | 9 +- openmc/cmfd.py | 182 ++++++++------------ tests/regression_tests/cmfd_feed/test.py | 45 ++--- tests/regression_tests/cmfd_feed_2g/test.py | 24 +-- tests/regression_tests/cmfd_feed_ng/test.py | 24 +-- tests/regression_tests/cmfd_nofeed/test.py | 23 +-- tests/testing_harness.py | 29 +++- 7 files changed, 122 insertions(+), 214 deletions(-) diff --git a/openmc/capi/core.py b/openmc/capi/core.py index ef606d6352..540413e7bf 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -141,6 +141,7 @@ def find_material(xyz): else: return mats[instance.value] + def hard_reset(): """Reset tallies, timers, and pseudo-random number generator state.""" _dll.openmc_hard_reset() @@ -330,7 +331,7 @@ def statepoint_write(filename=None, write_source=True): @contextmanager -def run_in_memory(intracomm=None): +def run_in_memory(**kwargs): """Provides context manager for calling OpenMC shared library functions. This function is intended to be used in a 'with' statement and ensures that @@ -346,11 +347,11 @@ def run_in_memory(intracomm=None): Parameters ---------- - intracomm : mpi4py.MPI.Intracomm or None - MPI intracommunicator + **kwargs + All keyword arguments are passed to :func:`init`. """ - init(intracomm=intracomm) + init(**kwargs) try: yield finally: diff --git a/openmc/cmfd.py b/openmc/cmfd.py index bbfc635698..167331cd2f 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -608,7 +608,7 @@ class CMFDRun(object): check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2) self._gauss_seidel_tolerance = gauss_seidel_tolerance - def run(self, intracomm=None, **kwargs): + def run(self, **kwargs): """Run OpenMC with coarse mesh finite difference acceleration This method is called by user to run CMFD once instance variables of @@ -616,65 +616,61 @@ class CMFDRun(object): Parameters ---------- - intracomm : mpi4py.MPI.Intracomm or None - MPI intercommunicator to pass through C API. Set to MPI.COMM_WORLD - by default **kwargs - All keyword arguments are passed to :func:`openmc.capi.init`. + All keyword arguments are passed to + :func:`openmc.capi.run_in_memory`. """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made - if intracomm is not None: - self._intracomm = intracomm - elif intracomm is None and have_mpi: + if 'intracomm' in kwargs.keys() and kwargs['intracomm'] is not None: + self._intracomm = kwargs['intracomm'] + elif have_mpi: self._intracomm = MPI.COMM_WORLD - # Run and pass arguments to C API init function - openmc.capi.init(intracomm=self._intracomm, **kwargs) + # Run and pass arguments to C API run_in_memory function + with openmc.capi.run_in_memory(**kwargs): + # Configure CMFD parameters and tallies + self._configure_cmfd() - # Configure CMFD parameters and tallies - self._configure_cmfd() + # Set up CMFD coremap + self._set_coremap() - # Set up CMFD coremap - self._set_coremap() + # Compute and store array indices used to build cross section + # arrays + self._precompute_array_indices() - # Compute and store array indices used to build cross section arrays - self._precompute_array_indices() + # Compute and store row and column indices used to build CMFD + # matrices + self._precompute_matrix_indices() - # Compute and store row and column indices used to build CMFD matrices - self._precompute_matrix_indices() + # Initialize all variables used for linear solver in C++ + self._initialize_linsolver() - # Initialize all variables used for linear solver in C++ - self._initialize_linsolver() + # Initialize simulation + openmc.capi.simulation_init() - # Initialize simulation - openmc.capi.simulation_init() + status = 0 + while status == 0: + # Initialize CMFD batch + self._cmfd_init_batch() - status = 0 - while status == 0: - # Initialize CMFD batch - self._cmfd_init_batch() + # Run next batch + status = openmc.capi.next_batch() - # Run next batch - status = openmc.capi.next_batch() + # Perform CMFD calculation if on + if self._cmfd_on: + self._execute_cmfd() - # Perform CMFD calculation if on - if self._cmfd_on: - self._execute_cmfd() + # Write CMFD output if CMFD on for current batch + if openmc.capi.master(): + self._write_cmfd_output() - # Write CMFD output if CMFD on for current batch - if openmc.capi.master(): - self._write_cmfd_output() + # Finalize simuation + openmc.capi.simulation_finalize() - # Finalize simuation - openmc.capi.simulation_finalize() - - # Print out CMFD timing statistics - self._write_cmfd_timing_stats() - - # Finalize and free memory - openmc.capi.finalize() + # Print out CMFD timing statistics + self._write_cmfd_timing_stats() def _initialize_linsolver(self): # Determine number of rows in CMFD matrix @@ -703,15 +699,19 @@ class CMFDRun(object): # Display value of additional fields based on display dict outstr += '\n' if self._display['dominance']: - outstr += '{:>11s}Dom Rat: {:0.5f}\n'.format('', self._dom[-1]) + outstr += ('{:>11s}Dom Rat: {:0.5f}\n' + .format('', self._dom[-1])) if self._display['entropy']: - outstr += '{:>11s}CMFD Ent: {:0.5f}\n'.format('', self._entropy[-1]) + outstr += ('{:>11s}CMFD Ent: {:0.5f}\n' + .format('', self._entropy[-1])) if self._display['source']: - outstr += '{:>11s}RMS Src: {:0.5f}\n'.format('', self._src_cmp[-1]) + outstr += ('{:>11s}RMS Src: {:0.5f}\n' + .format('', self._src_cmp[-1])) if self._display['balance']: - outstr += '{:>11s}RMS Bal: {:0.5f}\n'.format('', self._balance[-1]) + outstr += ('{:>11s}RMS Bal: {:0.5f}\n' + .format('', self._balance[-1])) - print('{:s}'.format(outstr)) + print(outstr) sys.stdout.flush() def _write_cmfd_timing_stats(self): @@ -926,12 +926,29 @@ class CMFDRun(object): # Start timer for build time_start_buildcmfd = time.time() - # Build loss and production matrices - loss, prod = self._build_matrices(physical_adjoint) + # Build the loss and production matrices + if not adjoint: + # Build matrices without adjoint calculation + loss = self._build_loss_matrix(False) + prod = self._build_prod_matrix(False) + else: + # Build adjoint matrices by running adjoint calculation + if self._adjoint_type == 'physical': + loss = self._build_loss_matrix(True) + prod = self._build_prod_matrix(True) + # Build adjoint matrices as transpose of non-adjoint matrices + else: + loss = self._build_loss_matrix(False).transpose() + prod = self._build_prod_matrix(False).transpose() - # Check for mathematical adjoint calculation - if adjoint and self._adjoint_type == 'math': - loss, prod = self._compute_adjoint(loss, prod) + # Write out the matrices. + if self._write_matrices: + if not adjoint: + self._write_matrix(loss, 'loss') + self._write_matrix(prod, 'prod') + else: + self._write_matrix(loss, 'adj_loss') + self._write_matrix(prod, 'adj_prod') # Stop timer for build time_stop_buildcmfd = time.time() @@ -1237,67 +1254,6 @@ class CMFDRun(object): return sites_outside[0] - def _build_matrices(self, adjoint): - """Build loss and production matrices and write these matrices - - Parameters - ---------- - adjoint : bool - Whether or not to run an adjoint calculation - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD loss matrix - prod : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - """ - # Build loss and production matrices - loss = self._build_loss_matrix(adjoint) - prod = self._build_prod_matrix(adjoint) - - # Write out matrices - if self._write_matrices: - if adjoint: - self._write_matrix(loss, 'adj_loss') - self._write_matrix(prod, 'adj_prod') - else: - self._write_matrix(loss, 'loss') - self._write_matrix(prod, 'prod') - - return loss, prod - - def _compute_adjoint(self, loss, prod): - """Computes a mathematical adjoint of the CMFD problem by transposing - production and loss matrices passed in as arguments - - Parameters - ---------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD loss matrix - prod : scipy.sparse.spmatrix - Sparse matrix storing elements of CMFD production matrix - - Returns - ------- - loss : scipy.sparse.spmatrix - Sparse matrix storing elements of adjoint CMFD loss matrix - prod : scipy.sparse.spmatrix - Sparse matrix storing elements of adjoint CMFD production matrix - - """ - # Transpose matrices - loss = np.transpose(loss) - prod = np.transpose(prod) - - # Write out matrices - if self._write_matrices: - self._write_matrix(loss, 'adj_loss') - self._write_matrix(prod, 'adj_prod') - - return loss, prod - def _build_loss_matrix(self, adjoint): # Extract spatial and energy indices and define matrix dimension ng = self._indices[3] diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index f19a9aede9..04ac442e0e 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -3,11 +3,12 @@ from openmc import cmfd import numpy as np import scipy.sparse -def test_cmfd_physical_adjoint(): - """ Test physical adjoint functionality of CMFD - This test runs CMFD with a physical adjoint calculation and asserts that the - adjoint k-effective and flux vector are equal to the non-adjoint +def test_cmfd_physical_adjoint(): + """Test physical adjoint functionality of CMFD + + This test runs CMFD with a physical adjoint calculation and asserts that + the adjoint k-effective and flux vector are equal to the non-adjoint k-effective and flux vector at the last batch (equivalent for 1 group problems). @@ -31,11 +32,12 @@ def test_cmfd_physical_adjoint(): assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) assert(cmfd_run._adj_keff == cmfd_run._keff) -def test_cmfd_math_adjoint(): - """ Test mathematical adjoint functionality of CMFD - This test runs CMFD with a mathematical adjoint calculation and asserts that - the adjoint k-effective and flux vector are equal to the non-adjoint +def test_cmfd_math_adjoint(): + """Test mathematical adjoint functionality of CMFD + + This test runs CMFD with a mathematical adjoint calculation and asserts + that the adjoint k-effective and flux vector are equal to the non-adjoint k-effective and flux vector at the last batch (equivalent for 1 group problems). @@ -59,8 +61,9 @@ def test_cmfd_math_adjoint(): assert(np.all(cmfd_run._phi == cmfd_run._adj_phi)) assert(cmfd_run._adj_keff == cmfd_run._keff) + def test_cmfd_write_matrices(): - """ Test write matrices functionality of CMFD + """Test write matrices functionality of CMFD This test runs CMFD with feedback and loads the loss/production matrices and flux vector that are saved to disk, and checks to make sure these @@ -111,8 +114,9 @@ def test_cmfd_write_matrices(): assert(np.all(np.isclose(flux_np, cmfd_run._phi))) assert(np.all(np.isclose(flux_np, flux_dat))) + def test_cmfd_feed(): - """ Test 1 group CMFD solver with CMFD feedback""" + """Test 1 group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 @@ -129,25 +133,6 @@ def test_cmfd_feed(): cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() - # Create output string of all CMFD results to pass into testing harness - outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), - order='F') - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' - # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py index 4efe4c4dd1..22cc7e1602 100644 --- a/tests/regression_tests/cmfd_feed_2g/test.py +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -2,8 +2,9 @@ from tests.testing_harness import CMFDTestHarness from openmc import cmfd import numpy as np + def test_cmfd_feed_2g(): - """ Test 2 group CMFD solver results with CMFD feedback""" + """Test 2 group CMFD solver results with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 @@ -22,25 +23,6 @@ def test_cmfd_feed_2g(): cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() - # Create output string of all CMFD results to pass into testing harness - outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), - order='F') - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' - # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py index 74fbe0a64c..db0b44f3e1 100644 --- a/tests/regression_tests/cmfd_feed_ng/test.py +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -2,8 +2,9 @@ from tests.testing_harness import CMFDTestHarness from openmc import cmfd import numpy as np + def test_cmfd_feed_ng(): - """ Test n group CMFD solver with CMFD feedback""" + """Test n group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 @@ -23,25 +24,6 @@ def test_cmfd_feed_ng(): cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() - # Create output string of all CMFD results to pass into testing harness - outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run._cmfd_src, np.product(cmfd_run.indices), - order='F') - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' - # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index 1d6896cc95..36f6930268 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -4,7 +4,7 @@ import numpy as np def test_cmfd_nofeed(): - """ Test 1 group CMFD solver without CMFD feedback""" + """Test 1 group CMFD solver without CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() cmfd_mesh.lower_left = -10.0, -1.0, -1.0 @@ -21,25 +21,6 @@ def test_cmfd_nofeed(): cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run() - # Create output string of all CMFD results to pass into testing harness - outstr = 'cmfd indices\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) - outstr += '\nk cmfd\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) - outstr += '\ncmfd entropy\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) - outstr += '\ncmfd balance\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) - outstr += '\ncmfd dominance ratio\n' - outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) - outstr += '\ncmfd openmc source comparison\n' - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) - outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.product(cmfd_run.indices), - order='F') - outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) - outstr += '\n' - # Initialize and run CMFD test harness - harness = CMFDTestHarness('statepoint.20.h5', cmfd_results=outstr) + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 408ddf1878..d8baf32061 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -133,9 +133,30 @@ class HashedTestHarness(TestHarness): class CMFDTestHarness(TestHarness): """Specialized TestHarness for running OpenMC CMFD tests.""" - def __init__(self, statepoint_name, cmfd_results=None): + def __init__(self, statepoint_name, cmfd_run): self._sp_name = statepoint_name - self._cmfd_results = cmfd_results + self._create_cmfd_result_str(cmfd_run) + + def _create_cmfd_result_str(self, cmfd_run): + """Create CMFD result string from variables of CMFDRun instance""" + outstr = 'cmfd indices\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.indices]) + outstr += '\nk cmfd\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.k_cmfd]) + outstr += '\ncmfd entropy\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.entropy]) + outstr += '\ncmfd balance\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.balance]) + outstr += '\ncmfd dominance ratio\n' + outstr += '\n'.join(['{0:10.3E}'.format(x) for x in cmfd_run.dom]) + outstr += '\ncmfd openmc source comparison\n' + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfd_run.src_cmp]) + outstr += '\ncmfd source\n' + cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.product(cmfd_run.indices), + order='F') + outstr += '\n'.join(['{0:12.6E}'.format(x) for x in cmfdsrc]) + outstr += '\n' + self._cmfdrun_results = outstr def execute_test(self): """Don't call _run_openmc as OpenMC will be called through C API for @@ -145,7 +166,7 @@ class CMFDTestHarness(TestHarness): try: self._test_output_created() results = self._get_results() - results += self._cmfd_results + results += self._cmfdrun_results self._write_results(results) self._compare_results() finally: @@ -159,7 +180,7 @@ class CMFDTestHarness(TestHarness): try: self._test_output_created() results = self._get_results() - results += self._cmfd_results + results += self._cmfdrun_results self._write_results(results) self._overwrite_results() finally: From c14680c26dd1f66a890beb18f023d806607ab2cd Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Wed, 19 Dec 2018 02:24:42 -0500 Subject: [PATCH 57/58] Update documentation about C API dependence of CMFD and necessary import statements --- docs/source/pythonapi/base.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index d3c53d1738..f39421b286 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -203,7 +203,9 @@ Coarse Mesh Finite Difference Acceleration CMFD is implemented in OpenMC and allows users to accelerate fission source convergence during inactive neutron batches. To run CMFD, the CMFDRun class -should be used. +should be used, and :mod:`from openmc import cmfd` should be included at the +top of the Python input file. Additionally, this class has a dependence on the +C API. .. autosummary:: :toctree: generated From 9f63b5a3b7b9265c467411eb0f519895cb7d94cb Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Thu, 20 Dec 2018 00:40:08 -0500 Subject: [PATCH 58/58] Use kwargs instead of kwargs.keys() --- openmc/cmfd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 167331cd2f..c13b6991d4 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -623,7 +623,7 @@ class CMFDRun(object): """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made - if 'intracomm' in kwargs.keys() and kwargs['intracomm'] is not None: + if 'intracomm' in kwargs and kwargs['intracomm'] is not None: self._intracomm = kwargs['intracomm'] elif have_mpi: self._intracomm = MPI.COMM_WORLD