Source code for scarabee.reseau.equivalence_theory

from .._scarabee import (
    Vector,
    Direction,
    MOCDriver,
    BoundaryCondition,
    CMFD,
    ADF,
    CDF,
)
from .nodal_flux import NodalFlux1D, NodalFlux2D
from .symmetry import Symmetry
import numpy as np
from typing import List, Tuple


[docs]def compute_adf_cdf_from_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], symmetry: Symmetry, independent_quadrant: bool, ) -> Tuple[np.ndarray, np.ndarray]: """ Computes the assembly and corner discontinuity factors in the few-group structure using the CMFD results. Currently, this method neglects the coupled x-y terms in the nodal construction when computing the CDFs. This simplifies the algorithm, but also ensures problems won't occur if trying to run single-pin cell "assembly" calculations. Parameters ---------- moc : MOCDriver Solved MOC problem for which the equivalence factors will be computed. moc_to_nodal_cond_scheme : list of list of ints Scheme used to condense from the MOC problem to the resulting nodal diffusion parameters. symmetry : Symmetry Indicates the symmetry of the problem. independent_quadrant : bool If quarter symmetry is indicated (symmetry = Symmetry.Quarter), then unique ADFs and CDFs will still be generated for each side, as if full symmetry were being used. Returns ------- ADF : ndarray Assembly Discontinuity Factors CDF : ndarray Corner Discontinuity Factors Raises ------ RuntimeError If moc does not have a CMFD instance, or if it is not possible to create a condensation scheme to go from the CMFD group structure to the few-group structure. """ if moc.cmfd is None: raise RuntimeError("moc must have a CMFD instance") if not moc.solved: raise RuntimeError("moc must be in a solved state") cmfd_condensation_scheme = moc.cmfd.condensation_scheme NG = len(moc_to_nodal_cond_scheme) # Create the condensation scheme to go from CMFD to few group structure cmfd_to_nodal_cond_scheme = [] for G in range(len(moc_to_nodal_cond_scheme)): cmfd_grps = [] # Get lower group for g in range(len(cmfd_condensation_scheme)): if cmfd_condensation_scheme[g][0] == moc_to_nodal_cond_scheme[G][0]: cmfd_grps.append(g) if len(cmfd_grps) == 0: raise RuntimeError( "Could not create condensation scheme from CMFD structure to few-group structure." ) # Get upper group for g in range(len(cmfd_condensation_scheme)): if cmfd_condensation_scheme[g][-1] == moc_to_nodal_cond_scheme[G][-1]: cmfd_grps.append(g) if len(cmfd_grps) == 1: raise RuntimeError( "Could not create condensation scheme from CMFD structure to few-group structure." ) cmfd_to_nodal_cond_scheme.append(cmfd_grps) # Create empty arrays for ADFs and CDFs adf = np.ones((NG, 6)) cdf = np.ones((NG, 4)) # Compute the homogeneous flux for each few-group hom_flux = _get_hom_flux_from_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) # Now we can go get the surface fluxes if symmetry in [Symmetry.Quarter, Symmetry.Half, Symmetry.Full]: het_flux_xp = _get_het_flux_xp_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) het_flux_yp = _get_het_flux_yp_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) het_flux_I = _get_het_flux_I_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) adf[:, ADF.XP] = het_flux_xp / hom_flux.flux_x.pos_surf_flux() adf[:, ADF.XN] = adf[:, ADF.XP] adf[:, ADF.YP] = het_flux_yp / hom_flux.flux_y.pos_surf_flux() adf[:, ADF.YN] = adf[:, ADF.YP] cdf[:, CDF.I] = het_flux_I / hom_flux(0.5 * hom_flux.dx, 0.5 * hom_flux.dy) cdf[:, CDF.II] = cdf[:, CDF.I] cdf[:, CDF.III] = cdf[:, CDF.I] cdf[:, CDF.IV] = cdf[:, CDF.I] if symmetry in [Symmetry.Half, Symmetry.Full] or independent_quadrant: het_flux_xn = _get_het_flux_xn_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) het_flux_II = _get_het_flux_II_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) adf[:, ADF.XN] = het_flux_xn / hom_flux.flux_x.neg_surf_flux() cdf[:, CDF.II] = het_flux_II / hom_flux(-0.5 * hom_flux.dx, 0.5 * hom_flux.dy) cdf[:, CDF.III] = cdf[:, CDF.II] if symmetry == Symmetry.Full or independent_quadrant: het_flux_yn = _get_het_flux_yn_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) het_flux_III = _get_het_flux_III_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) het_flux_IV = _get_het_flux_IV_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme ) adf[:, ADF.YN] = het_flux_yn / hom_flux.flux_y.neg_surf_flux() cdf[:, CDF.III] = het_flux_III / hom_flux( -0.5 * hom_flux.dx, -0.5 * hom_flux.dy ) cdf[:, CDF.IV] = het_flux_IV / hom_flux(0.5 * hom_flux.dx, -0.5 * hom_flux.dy) return adf, cdf
[docs]def compute_adf_cdf_from_moc( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], symmetry: Symmetry, independent_quadrant: bool, ) -> Tuple[np.ndarray, np.ndarray]: """ Computes the assembly and corner discontinuity factors in the few-group structure using the MOC results. Parameters ---------- moc : MOCDriver Solved MOC problem for which the equivalence factors will be computed. moc_to_nodal_cond_scheme : list of list of ints Scheme used to condense from the MOC problem to the resulting nodal diffusion parameters. symmetry : Symmetry Indicates the symmetry of the problem. independent_quadrant : bool If quarter symmetry is indicated (symmetry = Symmetry.Quarter), then unique ADFs and CDFs will still be generated for each side, as if full symmetry were being used. Returns ------- ADF : ndarray Assembly Discontinuity Factors CDF : ndarray Corner Discontinuity Factors """ NG = len(moc_to_nodal_cond_scheme) # First, compute the homogeneous flux homog_flux = np.zeros(NG) total_volume = 0.0 for i in range(moc.nfsr): Vi = moc.volume(i) total_volume += Vi for G in range(NG): gmin, gmax = moc_to_nodal_cond_scheme[G][:] for g in range(gmin, gmax + 1): homog_flux[G] += Vi * moc.flux(i, g) homog_flux /= total_volume # Create empty arrays for ADFs and CDFs adf = np.ones((NG, 6)) cdf = np.zeros((NG, 4)) if symmetry == Symmetry.Full or ( symmetry == Symmetry.Quarter and independent_quadrant ): # Get flux along surfaces xn_segments = moc.trace_fsr_segments( Vector(moc.x_min + 0.001, moc.y_max), Direction(0.0, -1.0) ) xp_segments = moc.trace_fsr_segments( Vector(moc.x_max - 0.001, moc.y_max), Direction(0.0, -1.0) ) yn_segments = moc.trace_fsr_segments( Vector(moc.x_min, moc.y_min + 0.001), Direction(1.0, 0.0) ) yp_segments = moc.trace_fsr_segments( Vector(moc.x_min, moc.y_max - 0.001), Direction(1.0, 0.0) ) xn_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, xn_segments) xp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, xp_segments) yn_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, yn_segments) yp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, yp_segments) # Get flux at corners I_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_max - 0.001, moc.y_max - 0.001), Direction(-1.0, -1.0), ) II_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_min + 0.001, moc.y_max - 0.001), Direction(1.0, -1.0), ) III_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_min + 0.001, moc.y_min + 0.001), Direction(1.0, 1.0), ) IV_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_max - 0.001, moc.y_min + 0.001), Direction(-1.0, 1.0), ) for G in range(NG): adf[G, ADF.XN] = xn_flx[G] / homog_flux[G] adf[G, ADF.XP] = xp_flx[G] / homog_flux[G] adf[G, ADF.YN] = yn_flx[G] / homog_flux[G] adf[G, ADF.YP] = yp_flx[G] / homog_flux[G] # The ADFs on the +/- Z sides will be left at unity cdf[G, CDF.I] = I_flx[G] / homog_flux[G] cdf[G, CDF.II] = II_flx[G] / homog_flux[G] cdf[G, CDF.III] = III_flx[G] / homog_flux[G] cdf[G, CDF.IV] = IV_flx[G] / homog_flux[G] elif symmetry == Symmetry.Half: # Get flux along surfaces xn_segments = moc.trace_fsr_segments( Vector(moc.x_min + 0.001, moc.y_max), Direction(0.0, -1.0) ) xp_segments = moc.trace_fsr_segments( Vector(moc.x_max - 0.001, moc.y_max), Direction(0.0, -1.0) ) yp_segments = moc.trace_fsr_segments( Vector(moc.x_min, moc.y_max - 0.001), Direction(1.0, 0.0) ) xn_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, xn_segments) xp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, xp_segments) yp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, yp_segments) # Get flux at corners I_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_max - 0.001, moc.y_max - 0.001), Direction(-1.0, -1.0), ) II_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_min + 0.001, moc.y_max - 0.001), Direction(1.0, -1.0), ) for G in range(NG): adf[G, ADF.XN] = xn_flx[G] / homog_flux[G] adf[G, ADF.XP] = xp_flx[G] / homog_flux[G] adf[G, ADF.YP] = yp_flx[G] / homog_flux[G] adf[G, ADF.YN] = adf[G, ADF.YP] # The ADFs on the +/- Z sides will be left at unity cdf[G, CDF.I] = I_flx[G] / homog_flux[G] cdf[G, CDF.II] = II_flx[G] / homog_flux[G] cdf[G, CDF.III] = cdf[G, CDF.II] cdf[G, CDF.IV] = cdf[G, CDF.I] else: # Quarter symmetry # Get flux along surfaces xp_segments = moc.trace_fsr_segments( Vector(moc.x_max - 0.001, moc.y_max), Direction(0.0, -1.0) ) yp_segments = moc.trace_fsr_segments( Vector(moc.x_min, moc.y_max - 0.001), Direction(1.0, 0.0) ) xp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, xp_segments) yp_flx = _compute_average_line_flux(moc, moc_to_nodal_cond_scheme, yp_segments) # Get flux at corners I_flx = _compute_few_group_flux( moc, moc_to_nodal_cond_scheme, Vector(moc.x_max - 0.001, moc.y_max - 0.001), Direction(-1.0, -1.0), ) for G in range(NG): adf[G, ADF.XP] = xp_flx[G] / homog_flux[G] adf[G, ADF.XN] = adf[G, ADF.XP] adf[G, ADF.YP] = yp_flx[G] / homog_flux[G] adf[G, ADF.YN] = adf[G, ADF.YP] # The ADFs on the +/- Z sides will be left at unity cdf[G, CDF.I] = I_flx[G] / homog_flux[G] cdf[G, CDF.II] = cdf[G, CDF.I] cdf[G, CDF.III] = cdf[G, CDF.I] cdf[G, CDF.IV] = cdf[G, CDF.I] return adf, cdf
def _compute_few_group_flux( moc: MOCDriver, condensation_scheme: List[List[int]], r: Vector, u: Direction ) -> List[float]: """ Computes the flux in the few-group scheme at the given position and direction. Parameters ---------- moc : MOCDriver Solved MOC problem for which the flux will be computed. condensation_scheme : list of list of ints Scheme used to condense the flux estimates from the MOC problem. r : Vector Position where the flux is evaluated. u : Direction Direction vector to disambiguate the position. Returns ------- list of float Values of the few-group flux at the given position. """ flux = [0.0 for G in range(len(condensation_scheme))] for G in range(len(condensation_scheme)): gmin, gmax = condensation_scheme[G][:] for g in range(gmin, gmax + 1): flux[G] += moc.flux(r, u, g) return flux def _compute_average_line_flux( moc: MOCDriver, condensation_scheme: List[List[int]], segments: List[Tuple[int, float]], ) -> np.ndarray: """ Computes the average flux along a set of line segments in the few-group structure. Parameters ---------- moc : MOCDriver Solved MOC problem for which the flux will be computed. condensation_scheme : list of list of ints Scheme used to condense the flux estimates from the MOC problem. segments : list of tuples of int and float List of flat source region index and segment length tuples. Returns ------- ndarray Values of the few-group flux alone the line. """ total_length = 0.0 for s in segments: total_length += s[1] invs_tot_length = 1.0 / total_length flux = np.zeros(len(condensation_scheme)) for G in range(len(condensation_scheme)): gmin, gmax = condensation_scheme[G][:] for g in range(gmin, gmax + 1): for s in segments: flux[G] += s[1] * moc.flux(s[0], g) flux *= invs_tot_length return flux def _get_hom_flux_from_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> NodalFlux2D: NG = len(cmfd_to_nodal_cond_scheme) keff = moc.keff dx = moc.x_max - moc.x_min dy = moc.y_max - moc.y_min # Compute average flux for assembly flux_spec = moc.homogenize_flux_spectrum() avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += flux_spec[g] # Homogenize the xs and condense for assembly fine_xs = moc.homogenize() fine_diff_xs = fine_xs.diffusion_xs() few_diff_xs = fine_diff_xs.condense(moc_to_nodal_cond_scheme, flux_spec) # Condense currents for the assembly j_x_neg = _get_avg_x_neg_current_cmfd(moc, cmfd_to_nodal_cond_scheme) j_x_pos = _get_avg_x_pos_current_cmfd(moc, cmfd_to_nodal_cond_scheme) j_y_neg = _get_avg_y_neg_current_cmfd(moc, cmfd_to_nodal_cond_scheme) j_y_pos = _get_avg_y_pos_current_cmfd(moc, cmfd_to_nodal_cond_scheme) return NodalFlux2D( dx, dy, keff, few_diff_xs, avg_flux, j_x_neg, j_x_pos, j_y_neg, j_y_pos ) def _get_het_flux_I_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: cmfd = moc.cmfd i = cmfd.nx - 1 j = cmfd.ny - 1 tile_nodal_flux = _get_2d_nodal_flux_from_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme, i, j ) return tile_nodal_flux(0.5 * tile_nodal_flux.dx, 0.5 * tile_nodal_flux.dy) def _get_het_flux_II_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: cmfd = moc.cmfd i = 0 j = cmfd.ny - 1 tile_nodal_flux = _get_2d_nodal_flux_from_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme, i, j ) return tile_nodal_flux(-0.5 * tile_nodal_flux.dx, 0.5 * tile_nodal_flux.dy) def _get_het_flux_III_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: i = 0 j = 0 tile_nodal_flux = _get_2d_nodal_flux_from_cmfd( moc, moc_to_nodal_cond_scheme, cmfd_to_nodal_cond_scheme, i, j ) return tile_nodal_flux(-0.5 * tile_nodal_flux.dx, -0.5 * tile_nodal_flux.dy) def _get_het_flux_IV_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cond_scheme: List[List[int]], ) -> np.ndarray: cmfd = moc.cmfd i = cmfd.nx - 1 j = 0 tile_nodal_flux = _get_2d_nodal_flux_from_cmfd( moc, moc_to_nodal_cond_scheme, cond_scheme, i, j ) return tile_nodal_flux(0.5 * tile_nodal_flux.dx, -0.5 * tile_nodal_flux.dy) def _get_2d_nodal_flux_from_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], i: int, j: int, ) -> NodalFlux2D: NG = len(moc_to_nodal_cond_scheme) keff = moc.keff cmfd = moc.cmfd tally_j_xp = i != cmfd.nx - 1 or moc.x_max_bc != BoundaryCondition.Reflective tally_j_xn = i != 0 or moc.x_min_bc != BoundaryCondition.Reflective tally_j_yp = j != cmfd.ny - 1 or moc.y_max_bc != BoundaryCondition.Reflective tally_j_yn = j != 0 or moc.y_min_bc != BoundaryCondition.Reflective dx = cmfd.dx[i] dy = cmfd.dy[j] # Get surface indices s_x_neg = cmfd.get_x_neg_surf(i, j) s_x_pos = cmfd.get_x_pos_surf(i, j) s_y_neg = cmfd.get_y_neg_surf(i, j) s_y_pos = cmfd.get_y_pos_surf(i, j) # Homogenize average flux for the tile cmfd_tile_fsrs = cmfd.tile_fsr_list(i, j) tile_flux_spec = moc.homogenize_flux_spectrum(cmfd_tile_fsrs) avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += tile_flux_spec[g] # Homogenize the xs and condense fine_tile_xs = moc.homogenize(cmfd_tile_fsrs) fine_tile_diff_xs = fine_tile_xs.diffusion_xs() few_diff_xs = fine_tile_diff_xs.condense(moc_to_nodal_cond_scheme, tile_flux_spec) # Condense currents for the tile j_x_neg = np.zeros(NG) j_x_pos = np.zeros(NG) j_y_neg = np.zeros(NG) j_y_pos = np.zeros(NG) for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): if tally_j_xn: j_x_neg[G] += cmfd.current(g, s_x_neg) if tally_j_xp: j_x_pos[G] += cmfd.current(g, s_x_pos) if tally_j_yn: j_y_neg[G] += cmfd.current(g, s_y_neg) if tally_j_yp: j_y_pos[G] += cmfd.current(g, s_y_pos) return NodalFlux2D( dx, dy, keff, few_diff_xs, avg_flux, j_x_neg, j_x_pos, j_y_neg, j_y_pos ) def _get_avg_x_pos_current_cmfd( moc: MOCDriver, cmfd_to_nodal_cond_scheme: List[List[int]] ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) cmfd = moc.cmfd current = np.zeros(NG) if moc.x_max_bc == BoundaryCondition.Reflective: return current dlts = cmfd.dy i = cmfd.nx - 1 for j in range(cmfd.ny): # Get surface indices s = cmfd.get_x_pos_surf(i, j) # Condense currents for the tile for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): # Weight current contribution by length of segment current[G] += cmfd.current(g, s) * dlts[j] # Normalize by length of xp surface current /= moc.y_max - moc.y_min return current def _get_avg_x_neg_current_cmfd( moc: MOCDriver, cmfd_to_nodal_cond_scheme: List[List[int]] ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) cmfd = moc.cmfd current = np.zeros(NG) if moc.x_min_bc == BoundaryCondition.Reflective: return current dlts = cmfd.dy i = 0 for j in range(cmfd.ny): # Get surface indices s = cmfd.get_x_neg_surf(i, j) # Condense currents for the tile for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): # Weight current contribution by length of segment current[G] += cmfd.current(g, s) * dlts[j] # Normalize by length of xp surface current /= moc.y_max - moc.y_min return current def _get_avg_y_pos_current_cmfd( moc: MOCDriver, cmfd_to_nodal_cond_scheme: List[List[int]] ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) cmfd = moc.cmfd current = np.zeros(NG) if moc.y_max_bc == BoundaryCondition.Reflective: return current dlts = cmfd.dx j = cmfd.ny - 1 for i in range(cmfd.nx): # Get surface indices s = cmfd.get_y_pos_surf(i, j) # Condense currents for the tile for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): # Weight current contribution by length of segment current[G] += cmfd.current(g, s) * dlts[i] # Normalize by length of xp surface current /= moc.x_max - moc.x_min return current def _get_avg_y_neg_current_cmfd( moc: MOCDriver, cmfd_to_nodal_cond_scheme: List[List[int]] ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) cmfd = moc.cmfd current = np.zeros(NG) if moc.y_min_bc == BoundaryCondition.Reflective: return current dlts = cmfd.dx j = 0 for i in range(cmfd.nx): # Get surface indices s = cmfd.get_y_neg_surf(i, j) # Condense currents for the tile for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): # Weight current contribution by length of segment current[G] += cmfd.current(g, s) * dlts[i] # Normalize by length of xp surface current /= moc.x_max - moc.x_min return current def _get_het_flux_xp_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) keff = moc.keff cmfd = moc.cmfd tally_j_pos = moc.x_max_bc != BoundaryCondition.Reflective dlts = cmfd.dy node_fluxes = [] i = cmfd.nx - 1 x_min = 0.0 x_max = cmfd.dx[-1] for j in range(cmfd.ny): # For tile (i, j), get the FSR list cmfd_tile_fsrs = cmfd.tile_fsr_list(i, j) # Get surface indices s_neg = cmfd.get_x_neg_surf(i, j) s_pos = cmfd.get_x_pos_surf(i, j) # Homogenize average flux for the tile tile_flux_spec = moc.homogenize_flux_spectrum(cmfd_tile_fsrs) avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += tile_flux_spec[g] # Homogenize the xs and condense fine_tile_xs = moc.homogenize(cmfd_tile_fsrs) fine_tile_diff_xs = fine_tile_xs.diffusion_xs() few_diff_xs = fine_tile_diff_xs.condense( moc_to_nodal_cond_scheme, tile_flux_spec ) # Condense currents for the tile j_neg = np.zeros(NG) j_pos = np.zeros(NG) for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): j_neg[G] += cmfd.current(g, s_neg) if tally_j_pos: j_pos[G] += cmfd.current(g, s_pos) # Create 1D nodal flux object node_fluxes.append( NodalFlux1D(x_min, x_max, keff, few_diff_xs, avg_flux, j_neg, j_pos) ) # Now we need to get the average heterogeneous flux on the surface het_flux = np.zeros(NG) for G in range(NG): for j in range(cmfd.ny): het_flux[G] += node_fluxes[j].pos_surf_flux(G) * dlts[j] het_flux /= moc.y_max - moc.y_min return het_flux def _get_het_flux_xn_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) keff = moc.keff cmfd = moc.cmfd tally_j_neg = moc.x_min_bc != BoundaryCondition.Reflective dlts = cmfd.dy node_fluxes = [] i = 0 x_min = 0.0 x_max = cmfd.dx[0] for j in range(cmfd.ny): # For tile (i, j), get the FSR list cmfd_tile_fsrs = cmfd.tile_fsr_list(i, j) # Get surface indices s_neg = cmfd.get_x_neg_surf(i, j) s_pos = cmfd.get_x_pos_surf(i, j) # Homogenize average flux for the tile tile_flux_spec = moc.homogenize_flux_spectrum(cmfd_tile_fsrs) avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += tile_flux_spec[g] # Homogenize the xs and condense fine_tile_xs = moc.homogenize(cmfd_tile_fsrs) fine_tile_diff_xs = fine_tile_xs.diffusion_xs() few_diff_xs = fine_tile_diff_xs.condense( moc_to_nodal_cond_scheme, tile_flux_spec ) # Condense currents for the tile j_neg = np.zeros(NG) j_pos = np.zeros(NG) for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): if tally_j_neg: j_neg[G] += cmfd.current(g, s_neg) j_pos[G] += cmfd.current(g, s_pos) # Create 1D nodal flux object node_fluxes.append( NodalFlux1D(x_min, x_max, keff, few_diff_xs, avg_flux, j_neg, j_pos) ) # Now we need to get the average heterogeneous flux on the surface het_flux = np.zeros(NG) for G in range(NG): for j in range(cmfd.ny): het_flux[G] += node_fluxes[j].neg_surf_flux(G) * dlts[j] het_flux /= moc.y_max - moc.y_min return het_flux def _get_het_flux_yp_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) keff = moc.keff cmfd = moc.cmfd tally_j_pos = moc.y_max_bc != BoundaryCondition.Reflective dlts = cmfd.dx node_fluxes = [] j = cmfd.ny - 1 y_min = 0.0 y_max = cmfd.dy[-1] for i in range(cmfd.nx): # For tile (i, j), get the FSR list cmfd_tile_fsrs = cmfd.tile_fsr_list(i, j) # Get surface indices s_neg = cmfd.get_y_neg_surf(i, j) s_pos = cmfd.get_y_pos_surf(i, j) # Homogenize average flux for the tile tile_flux_spec = moc.homogenize_flux_spectrum(cmfd_tile_fsrs) avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += tile_flux_spec[g] # Homogenize the xs and condense fine_tile_xs = moc.homogenize(cmfd_tile_fsrs) fine_tile_diff_xs = fine_tile_xs.diffusion_xs() few_diff_xs = fine_tile_diff_xs.condense( moc_to_nodal_cond_scheme, tile_flux_spec ) # Condense currents for the tile j_neg = np.zeros(NG) j_pos = np.zeros(NG) for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): j_neg[G] += cmfd.current(g, s_neg) if tally_j_pos: j_pos[G] += cmfd.current(g, s_pos) # Create 1D nodal flux object node_fluxes.append( NodalFlux1D(y_min, y_max, keff, few_diff_xs, avg_flux, j_neg, j_pos) ) # Now we need to get the average heterogeneous flux on the surface het_flux = np.zeros(NG) for G in range(NG): for i in range(cmfd.nx): het_flux[G] += node_fluxes[i].pos_surf_flux(G) * dlts[i] het_flux /= moc.x_max - moc.x_min return het_flux def _get_het_flux_yn_cmfd( moc: MOCDriver, moc_to_nodal_cond_scheme: List[List[int]], cmfd_to_nodal_cond_scheme: List[List[int]], ) -> np.ndarray: NG = len(cmfd_to_nodal_cond_scheme) keff = moc.keff cmfd = moc.cmfd tally_j_neg = moc.y_min_bc != BoundaryCondition.Reflective dlts = cmfd.dx node_fluxes = [] j = 0 y_min = 0.0 y_max = cmfd.dy[0] for i in range(cmfd.nx): # For tile (i, j), get the FSR list cmfd_tile_fsrs = cmfd.tile_fsr_list(i, j) # Get surface indices s_neg = cmfd.get_y_neg_surf(i, j) s_pos = cmfd.get_y_pos_surf(i, j) # Homogenize average flux for the tile tile_flux_spec = moc.homogenize_flux_spectrum(cmfd_tile_fsrs) avg_flux = np.zeros(NG) for G in range(NG): for g in range( moc_to_nodal_cond_scheme[G][0], moc_to_nodal_cond_scheme[G][1] + 1 ): avg_flux[G] += tile_flux_spec[g] # Homogenize the xs and condense fine_tile_xs = moc.homogenize(cmfd_tile_fsrs) fine_tile_diff_xs = fine_tile_xs.diffusion_xs() few_diff_xs = fine_tile_diff_xs.condense( moc_to_nodal_cond_scheme, tile_flux_spec ) # Condense currents for the tile j_neg = np.zeros(NG) j_pos = np.zeros(NG) for G in range(NG): for g in range( cmfd_to_nodal_cond_scheme[G][0], cmfd_to_nodal_cond_scheme[G][1] + 1 ): if tally_j_neg: j_neg[G] += cmfd.current(g, s_neg) j_pos[G] += cmfd.current(g, s_pos) # Create 1D nodal flux object node_fluxes.append( NodalFlux1D(y_min, y_max, keff, few_diff_xs, avg_flux, j_neg, j_pos) ) # Now we need to get the average heterogeneous flux on the surface het_flux = np.zeros(NG) for G in range(NG): for i in range(cmfd.nx): het_flux[G] += node_fluxes[i].neg_surf_flux(G) * dlts[i] het_flux /= moc.x_max - moc.x_min return het_flux