Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pincell Maker #2608

Open
wants to merge 30 commits into
base: develop
Choose a base branch
from
Open

Pincell Maker #2608

wants to merge 30 commits into from

Conversation

sreegudala
Copy link

As OpenMC does not yet contain a cylindrical lattice geometry abstraction, certain subdivisions of typical light water reactor pincells must be done manually by the user when defining their inputs. While a helper function in the python interface (pin()) exists to aid with simple radial subdivision of pincells, that function does not allow azimuthal subdivision, and does not easily allow subdivision of the external moderator region. Furthermore, the pin() function generates new Material objects for each subdivided region, which can be highly inefficient if the different regions are sharing material data.

There are a few use cases where a more robust helper function would be desirable:

  1. As a multigroup random ray transport is likely to be added into OpenMC soon, we will typically need smaller mesh elements (see picture) present to ensure the accuracy of the flat source region approximation typically used by random ray. This motivates the creation of a new helper function in the python API to accommodate pincell subdivisions typically used by random ray.
  2. Even in Monte Carlo, people may have interest in tallying fine-grained data at the pincell level, for instance for multiphysics feedback or depletion. While the existing pin() function should handle cases where only radial subdivision within the fueled region is necessary, in some cases it may be useful to azimuthally subdivide the pin as well, or to create a mesh within the moderator region (e.g., to represent temperature/density different within the moderator).

This function accommodates the above two use cases. It allows a user to define a typical light water reactor pincell in terms of a series of surfaces (e.g., fuel radius, gap radius, clad radius) and fills for each region between the inputted surfaces. This function can also automatically subdivide each region into a variable number of radial sub-regions and azimuthal sectors. The function also defines an "implicit complement" region that spans all space outside of the outermost cylinder passed in, which itself can be subdivided azimuthally.

Theoretically, the pin() function could have been edited to accommodate these other types of use cases, rather than creation of a new function. However, doing so may have likely involved changing the API and behavior of that function (which would have broken any existing input decks using pin()). As such, the new function was built to be standalone.

Some of the internal python helper functions were edited to make them more efficient. They were edited so as not to break their APIs or any dependent functionality, so those changes are not expected to break any input decks. Changes to those functions (and the reasons motivating those changes) are detailed below:

CylinderSector Class in openmc/model/surface_composite.py:
CylinderSector objects were used to create the azimuthal sectors in the pin_radial_azimuthal function. This function takes two radii and two angles to create a region defined by two cylindrical and two planar surfaces. In the original function, there were some unnecessary or redundant surfaces being created. Most of the changes were to reduce the surfaces created to the absolute minimum amount necessary.

  1. A zero radius cylinder was created if r1=0 was passed. This was changed to no longer create a cylinder if the radius is 0.
  2. Planes were defined using both the angles bounding them and the radii of the cylinders, so different Plane objects were created for the same angle. CylinderSectors with the same angles but different radii would create their own planes, resulting in many extra surfaces. This was changed to defining the planes using only the angles bounding them.
  3. A Plane defined using θ degrees and a Plane defined using θ-180 degrees create two different Plane objects even though they define the same geometry. This resulted in twice the number of planes to be created when creating multiple CylinderSectors. This was changed to only use 0 to 180 degrees to create planes so that they are reused for planes defined by angles between 180 and 360 degrees.
  4. Another change was necessary to azimuthally subdivide the "implicit complement" region defined in the pin_radial_azimuthal function. In this case, the “outer cylinder’s” radius extends to infinity. To handle this case, r2 can now be set to None and an outer cylinder is no longer created.

subdivide() function in openmc/model/funcs.py:
The subdivide function allowed regions to be constructed from a set of surfaces “in order.” The original function was built to create regions bounded by Cylinders. This was updated to also handle CylinderSectors.

An example of the new pin_radial_azimuthal function for defining the standard PWR pincell available at openmc/examples/pincell is given below:

Original code:

# Define problem geometry

# Create cylindrical surfaces
fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR')
clad_ir = openmc.ZCylinder(r=0.40005, name='Clad IR')
clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR')

# Create a region represented as the inside of a rectangular prism
pitch = 1.25984
box = openmc.rectangular_prism(pitch, pitch, boundary_type='reflective')

# Create cells, mapping materials to regions
fuel = openmc.Cell(fill=uo2, region=-fuel_or)
gap = openmc.Cell(fill=helium, region=+fuel_or & -clad_ir)
clad = openmc.Cell(fill=zircaloy, region=+clad_ir & -clad_or)
water = openmc.Cell(fill=borated_water, region=+clad_or & box)

# Create a geometry
geometry = openmc.Geometry([fuel, gap, clad, water])

New code that uses helper function:

# Define problem geometry

# Create a region represented as the inside of a rectangular prism

pitch = 1.25984
box = openmc.rectangular_prism(pitch, pitch, boundary_type='reflective')

cell1 = openmc.Cell(name='Cell 1')
cell1.region = box

# Create cylindrical surfaces
fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR')
clad_ir = openmc.ZCylinder(r=0.40005, name='Clad IR')
clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR')
corner =  openmc.ZCylinder(r=pitch/2, name='Clad OR')

surfs = [fuel_or, clad_ir, clad_or, corner]
mats = [uo2, helium, zircaloy, borated_water, borated_water.clone()]
subdivs_r = {
        0 : 3,
        2 : 1,
        3 : 2
        }
subdivs_a = {
        0 : 4,
        2 : 4,
        3 : 8
        }
rad_div_types = {
        0 : "area",
        2 : "area",
        3 : "area"
        }

pin_universe = openmc.model.pin_radial_azimuthal(surfs, mats, subdivisions_r=subdivs_r, subdivisions_a=subdivs_a, rad_div_types=rad_div_types, implicit_azi_div=8)

# Fill Cell with the Lattice
cell1.fill = pin_universe

root = openmc.Universe(name='root universe')
root.add_cell(cell1)

# Create a geometry
geometry = openmc.Geometry(root)

Plots of the pincell geometry using the new helper function (before and after subdivision) are shown below:

Before subdivision:
myplot

After subdivision:
myplot2

As a larger test, we also coded a 2D C5G7 input deck, which is available at:
https://github.com/sreegudala/benchmarks/blob/master/c5g7/openmc/2d/build-xml-2d.py

C5G7 as defined with the new method generates the expected number of cells and runs cleanly. Beyond being much easier to read and maintain, the new C5G7 definition allows for fewer surfaces to be defined, as older methods for defining these types of azimuthal subdivision often resulted in twice the number of planes being generated as were necessary. The new helper method is notable in that the minimum number of planes are defined.

Plots of the native and subdivided C5G7 geometry (with subdivision typical of what is required for random ray) are given below:

Native C5G7 plot:
myplot

Subdivided C5G7 plot:
plot_c5g7_10k

A new regression test was also added to the OpenMC test suite that covers the function. The test is found at openmc/test/regression_tests/pin_radial_azimuthal.

@sreegudala sreegudala marked this pull request as ready for review July 27, 2023 14:07
@paulromano
Copy link
Contributor

@sreegudala Thanks for this pull request! This looks like a useful functionality to have. Would you be willing to split this up into two PRs, one that touches only the existing functionality (CylinderSector, etc.) and then a follow-on one with the new pin_radial_azimuthal function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants