Source code for scarabee.coeur.core_tile

from .._scarabee import (
    DiffusionData,
    FormFactors,
)
from typing import Optional
from abc import ABC, abstractmethod
import copy


class CoreTile(ABC):
    """
    A CoreTile represents a unique geometry unit in a a nodal full core
    simulation, typically a single fuel assembly or reflector region.
    """

    def __init__(self):
        pass

    @property
    @abstractmethod
    def num_x_slots(self) -> int:
        """
        Number of unique nodes which should be used along the x direction.
        """
        return 0

    @property
    @abstractmethod
    def num_y_slots(self) -> int:
        """
        Number of unique nodes which should be used along the y direction.
        """
        return 0

    @property
    @abstractmethod
    def x_width(self) -> float:
        """
        Width of the tile along the x direction.
        """
        return 0.0

    @property
    @abstractmethod
    def y_width(self) -> float:
        """
        Width of the tile along the y direction.
        """
        return 0.0

    @abstractmethod
    def rotate_clockwise(self):
        """
        Rotates the ADFs and form factors, corresponding to a 90 degree
        rotation of the assembly in the clockwise direction.
        """
        return self

    @abstractmethod
    def rotate_counterclockwise(self):
        """
        Rotates the ADFs and form factors, corresponding to a 90 degree
        rotation of the assembly in the counter-clockwise direction.
        """
        return self

    @abstractmethod
    def reflect_across_x_axis(self):
        """
        Performs a reflection of the ADFs and form factors across the x axis.
        This means that the +y and -y ADFs are swapped.
        """
        return self

    @abstractmethod
    def reflect_across_y_axis(self):
        """
        Performs a reflection of the ADFs and form factors across the y axis.
        This means that the +x and -x ADFs are swapped.
        """
        return self


[docs]class SimpleTile(CoreTile): """ Represents a single fuel assembly or reflector region, with diffusion cross sections, ADFs, CDFs, and form factors. Only a single set of cross sections are provided for the entire assembly. This is appropriate for symmetric fuel assemblies or reflector region. Parameters ---------- diffusion_data : DiffusionData The diffusion cross sections, ADFs, and CDFs for the tile. form_factors : FormFactors Form factors for the tile. """
[docs] def __init__(self, diffusion_data: DiffusionData, form_factors: FormFactors): self.diffusion_data = diffusion_data self.form_factors = form_factors
@property def num_x_slots(self) -> int: return 1 @property def num_y_slots(self) -> int: return 1 @property def x_width(self) -> float: return self.form_factors.x_width @property def y_width(self) -> float: return self.form_factors.y_width
[docs] def rotate_clockwise(self) -> CoreTile: self.diffusion_data.rotate_clockwise() self.form_factors.rotate_clockwise() return self
[docs] def rotate_counterclockwise(self) -> CoreTile: self.diffusion_data.rotate_counterclockwise() self.form_factors.rotate_counterclockwise() return self
[docs] def reflect_across_x_axis(self) -> CoreTile: self.diffusion_data.reflect_across_x_axis() self.form_factors.reflect_across_x_axis() return self
[docs] def reflect_across_y_axis(self) -> CoreTile: self.diffusion_data.reflect_across_y_axis() self.form_factors.reflect_across_y_axis() return self
[docs]class QuadrantsTile(CoreTile): """ Represents a single fuel assembly with diffusion cross sections, ADFs, CDFs, and form factors. Each quadrant of the assembly has a unique DiffusionData instance. This is appropriate for asymmetric fuel assemblies. A single FormFactors instance is provided, which can be generated from independent quadrant assembly calculations. Parameters ---------- quad1 : DiffusionData or None The diffusion cross sections, ADFs, and CDFs for the I quadrant. quad2 : DiffusionData or None The diffusion cross sections, ADFs, and CDFs for the II quadrant. quad3 : DiffusionData or None The diffusion cross sections, ADFs, and CDFs for the III quadrant. quad4 : DiffusionData or None The diffusion cross sections, ADFs, and CDFs for the IV quadrant. form_factors : FormFactors Form factors for the tile. """
[docs] def __init__( self, quad1: Optional[DiffusionData], quad2: Optional[DiffusionData], quad3: Optional[DiffusionData], quad4: Optional[DiffusionData], form_factors: FormFactors, ): non_none_count = len([q for q in [quad1, quad2, quad3, quad4] if q is not None]) if non_none_count == 3: raise RuntimeError( "Cannot define a QuadrantsTile with 3 quadrants. Must provide 1, 2, or 4 quadrants." ) if non_none_count == 2 and ( (quad1 is None and quad3 is None) or (quad2 is None and quad4 is None) ): raise RuntimeError( "If providing only 2 quadrants to a QuadrantsTile, they cannot be diagonal to one another." ) self.quad1 = quad1 self.quad2 = quad2 self.quad3 = quad3 self.quad4 = quad4 self.form_factors = form_factors
[docs] @staticmethod def from_independent_quadrant( diffusion_data: DiffusionData, form_factors: FormFactors ) -> "QuadrantsTile": """ Creates a QuadrantsTile from a single DiffusionData and FormFactors instance which were generated as independent quadrants, where ADFs and CDFs were generated along the symmetry axes of the assembly. Parameters ---------- diffusion_data : DiffusionData Few-group cross sections, ADFs, and CDFs for the quarter assembly. form_factors : FormFactors Form factors for the quarter assembly. Returns ------- QuadrantsTile """ q1_dd = copy.deepcopy(diffusion_data) q1_ff = copy.deepcopy(form_factors) q2_dd = copy.deepcopy(q1_dd) q2_ff = copy.deepcopy(q1_ff) q2_dd.rotate_counterclockwise() q2_ff.rotate_counterclockwise() q3_dd = copy.deepcopy(q2_dd) q3_ff = copy.deepcopy(q2_ff) q3_dd.rotate_counterclockwise() q3_ff.rotate_counterclockwise() q4_dd = copy.deepcopy(q3_dd) q4_ff = copy.deepcopy(q3_ff) q4_dd.rotate_counterclockwise() q4_ff.rotate_counterclockwise() return QuadrantsTile( q1_dd, q2_dd, q3_dd, q4_dd, FormFactors(q1_ff, q2_ff, q3_ff, q4_ff) )
@property def num_x_slots(self) -> int: if (self.quad1 is not None and self.quad2 is not None) or ( self.quad3 is not None and self.quad4 is not None ): return 2 else: return 1 @property def num_y_slots(self) -> int: if (self.quad1 is not None and self.quad4 is not None) or ( self.quad2 is not None and self.quad3 is not None ): return 2 else: return 1 @property def x_width(self) -> float: return self.form_factors.x_width @property def y_width(self) -> float: return self.form_factors.y_width
[docs] def rotate_clockwise(self) -> CoreTile: tmp = [self.quad1, self.quad2, self.quad3, self.quad4] for dd in tmp: if dd is not None: dd.rotate_clockwise() self.quad1 = tmp[1] self.quad2 = tmp[2] self.quad3 = tmp[3] self.quad4 = tmp[0] self.form_factors.rotate_clockwise() return self
[docs] def rotate_counterclockwise(self) -> CoreTile: tmp = [self.quad1, self.quad2, self.quad3, self.quad4] for dd in tmp: if dd is not None: dd.rotate_counterclockwise() self.quad1 = tmp[3] self.quad2 = tmp[0] self.quad3 = tmp[1] self.quad4 = tmp[2] self.form_factors.rotate_counterclockwise() return self
[docs] def reflect_across_x_axis(self) -> CoreTile: tmp = [self.quad1, self.quad2, self.quad3, self.quad4] for dd in tmp: if dd is not None: dd.reflect_across_x_axis() self.quad1 = tmp[3] self.quad2 = tmp[2] self.quad3 = tmp[1] self.quad4 = tmp[0] self.form_factors.reflect_across_x_axis() return self
[docs] def reflect_across_y_axis(self) -> CoreTile: tmp = [self.quad1, self.quad2, self.quad3, self.quad4] for dd in tmp: if dd is not None: dd.reflect_across_y_axis() self.quad1 = tmp[1] self.quad2 = tmp[0] self.quad3 = tmp[3] self.quad4 = tmp[2] self.form_factors.reflect_across_y_axis() return self