Using CAD-Based Geometries

cad-based-geometry

In this notebook we'll be exploring how to use CAD-based geometries in OpenMC via the DagMC toolkit. The models we'll be using in this notebook have already been created using Trelis and faceted into a surface mesh represented as .h5m files in the Mesh Oriented DatABase format. We'll be retrieving these files using the function below.

In [1]:
import urllib.request

fuel_pin_url = 'https://tinyurl.com/y3ugwz6w' # 1.2 MB
teapot_url = 'https://tinyurl.com/y4mcmc3u' # 29 MB

def download(url):
    """
    Helper function for retrieving dagmc models
    """
    u = urllib.request.urlopen(url)
    
    if u.status != 200:
        raise RuntimeError("Failed to download file.")
    
    # save file as dagmc.h5m
    with open("dagmc.h5m", 'wb') as f:
        f.write(u.read())

This notebook is intended to demonstrate how DagMC problems are run in OpenMC. For more information on how DagMC models are created, please refer to the DagMC User's Guide.

CAD-Based Geometry in OpenMC using DagMC

In [2]:
%matplotlib inline
from IPython.display import Image
import openmc

To start, we'll be using a simple U235 fuel pin surrounded by a water moderator, so let's create those materials.

In [3]:
 # materials
u235 = openmc.Material(name="fuel")
u235.add_nuclide('U235', 1.0, 'ao')
u235.set_density('g/cc', 11)
u235.id = 40

water = openmc.Material(name="water")
water.add_nuclide('H1', 2.0, 'ao')
water.add_nuclide('O16', 1.0, 'ao')
water.set_density('g/cc', 1.0)
water.add_s_alpha_beta('c_H_in_H2O')
water.id = 41

mats = openmc.Materials([u235, water])
mats.export_to_xml()

Now let's get our DAGMC geometry. We'll be using prefabricated models in this notebook. For information on how to create your own DAGMC models, you can refer to the instructions here.

Let's download the DAGMC model. These models come in the form of triangle surface meshes stored using the the Mesh Oriented datABase (MOAB) in an HDF5 file with the extension .h5m. An example of a coarse triangle mesh looks like:

In [4]:
Image("./images/cylinder_mesh.png", width=350)
Out[4]:

First we'll need to grab some pre-made DagMC models.

In [5]:
download(fuel_pin_url)

OpenMC expects that the model has the name "dagmc.h5m" so we'll name the file that and indicate to OpenMC that a DAGMC geometry is being used by setting the settings.dagmc attribute to True.

In [6]:
settings = openmc.Settings()
settings.dagmc = True
settings.batches = 10
settings.inactive = 2
settings.particles = 5000
settings.export_to_xml()

Unlike conventional geometries in OpenMC, we really have no way of knowing what our model looks like at this point. Thankfully DagMC geometries can be plotted just like any other OpenMC geometry to give us an idea of what we're now working with.

Note that material assignments have already been applied to this model. Materials can be assigned either using ids or names of materials in the materials.xml file. It is recommended that material names are used for assignment for readability.

In [7]:
p = openmc.Plot()
p.width = (25.0, 25.0)
p.pixels = (400, 400)
p.color_by = 'material'
p.colors = {u235: 'yellow', water: 'blue'}
openmc.plot_inline(p)

Now that we've had a chance to examine the model a bit, we can finish applying our settings and add a source.

In [8]:
settings.source = openmc.Source(space=openmc.stats.Box([-4., -4., -4.],
                                                       [ 4.,  4.,  4.]))
settings.export_to_xml()

Tallies work in the same way when using DAGMC geometries too. We'll add a tally on the fuel cell here.

In [9]:
tally = openmc.Tally()
tally.scores = ['total']
tally.filters = [openmc.CellFilter(1)]
tallies = openmc.Tallies([tally])
tallies.export_to_xml()

Note: Applying tally filters in DagMC models requires prior knowledge of the model. Here, we know that the fuel cell's volume ID in the CAD sofware is 1. To identify cells without use of CAD software, load them into the OpenMC plotter where cell, material, and volume IDs can be identified for native both OpenMC and DagMC geometries.

Now we're ready to run the simulation just like any other OpenMC run.

In [10]:
openmc.run()
                                %%%%%%%%%%%%%%%
                           %%%%%%%%%%%%%%%%%%%%%%%%
                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                                    %%%%%%%%%%%%%%%%%%%%%%%%
                                     %%%%%%%%%%%%%%%%%%%%%%%%
                 ###############      %%%%%%%%%%%%%%%%%%%%%%%%
                ##################     %%%%%%%%%%%%%%%%%%%%%%%
                ###################     %%%%%%%%%%%%%%%%%%%%%%%
                ####################     %%%%%%%%%%%%%%%%%%%%%%
                #####################     %%%%%%%%%%%%%%%%%%%%%
                ######################     %%%%%%%%%%%%%%%%%%%%
                #######################     %%%%%%%%%%%%%%%%%%
                 #######################     %%%%%%%%%%%%%%%%%
                 ######################     %%%%%%%%%%%%%%%%%
                  ####################     %%%%%%%%%%%%%%%%%
                    #################     %%%%%%%%%%%%%%%%%
                     ###############     %%%%%%%%%%%%%%%%
                       ############     %%%%%%%%%%%%%%%
                          ########     %%%%%%%%%%%%%%
                                      %%%%%%%%%%%

                   | The OpenMC Monte Carlo Code
         Copyright | 2011-2019 MIT and OpenMC contributors
           License | http://openmc.readthedocs.io/en/latest/license.html
           Version | 0.11.0-dev
          Git SHA1 | 2c0b16e73d5d81a2f849f4e2bfae5eb5319f2417
         Date/Time | 2019-06-18 08:54:17
    OpenMP Threads | 2

 Reading settings XML file...
 Reading cross sections XML file...
 Reading materials XML file...
 Reading DAGMC geometry...
Loading file dagmc.h5m
Initializing the GeomQueryTool...
Using faceting tolerance: 0.0001
Building OBB Tree...
 Reading U235 from /home/shriwise/opt/openmc/xs/nndc_hdf5/U235.h5
 Reading H1 from /home/shriwise/opt/openmc/xs/nndc_hdf5/H1.h5
 Reading O16 from /home/shriwise/opt/openmc/xs/nndc_hdf5/O16.h5
 Reading c_H_in_H2O from /home/shriwise/opt/openmc/xs/nndc_hdf5/c_H_in_H2O.h5
 Maximum neutron transport energy: 20000000.000000 eV for U235
 Reading tallies XML file...
 Writing summary.h5 file...
 Initializing source particles...

 ====================>     K EIGENVALUE SIMULATION     <====================

  Bat./Gen.      k            Average k
  =========   ========   ====================
        1/1    1.15789
        2/1    1.05169
        3/1    1.00736
        4/1    0.97863    0.99300 +/- 0.01436
        5/1    0.95316    0.97972 +/- 0.01566
        6/1    0.95079    0.97248 +/- 0.01322
        7/1    0.96879    0.97175 +/- 0.01027
        8/1    0.94253    0.96688 +/- 0.00970
        9/1    0.97406    0.96790 +/- 0.00826
       10/1    0.97362    0.96862 +/- 0.00719
 Creating state point statepoint.10.h5...

 =======================>     TIMING STATISTICS     <=======================

 Total time for initialization     = 2.4575e-01 seconds
   Reading cross sections          = 1.3129e-01 seconds
 Total time in simulation          = 1.6897e+00 seconds
   Time in transport only          = 1.6829e+00 seconds
   Time in inactive batches        = 3.1605e-01 seconds
   Time in active batches          = 1.3737e+00 seconds
   Time synchronizing fission bank = 2.8739e-03 seconds
     Sampling source sites         = 2.5550e-03 seconds
     SEND/RECV source sites        = 2.8512e-04 seconds
   Time accumulating tallies       = 7.8450e-06 seconds
 Total time for finalization       = 1.7404e-04 seconds
 Total time elapsed                = 1.9588e+00 seconds
 Calculation Rate (inactive)       = 31640.8 particles/second
 Calculation Rate (active)         = 29119.1 particles/second

 ============================>     RESULTS     <============================

 k-effective (Collision)     = 0.97020 +/- 0.00750
 k-effective (Track-length)  = 0.96862 +/- 0.00719
 k-effective (Absorption)    = 0.95742 +/- 0.00791
 Combined k-effective        = 0.96203 +/- 0.00898
 Leakage Fraction            = 0.57677 +/- 0.00317

More Complicated Geometry

Neat! But this pincell is something we could've done with CSG. Let's take a look at something more complex. We'll download a pre-built model of the Utah teapot and use it here.

In [11]:
download(teapot_url)
In [12]:
Image("./images/teapot.jpg", width=600)
Out[12]:

Our teapot is made out of iron, so we'll want to create that material and make sure it is in our materials.xml file.

In [13]:
iron = openmc.Material(name="iron")
iron.add_nuclide("Fe54", 0.0564555822608)
iron.add_nuclide("Fe56", 0.919015287728)
iron.add_nuclide("Fe57", 0.0216036861685)
iron.add_nuclide("Fe58", 0.00292544384231)
iron.set_density("g/cm3", 7.874)
mats = openmc.Materials([iron, water])
mats.export_to_xml()

To make sure we've updated the file correctly, let's make a plot of the teapot.

In [14]:
p = openmc.Plot()
p.basis = 'xz'
p.origin = (0.0, 0.0, 0.0)
p.width = (30.0, 20.0)
p.pixels = (450, 300)
p.color_by = 'material'
p.colors = {iron: 'gray', water: 'blue'}
openmc.plot_inline(p)

Here we start to see some of the advantages CAD geometries provide. This particular file was pulled from the GrabCAD and pushed through the DAGMC workflow without modification (other than the addition of material assignments). It would take a considerable amount of time to create a model like this using CSG!

In [15]:
p.width = (18.0, 6.0)
p.basis = 'xz'
p.origin = (10.0, 0.0, 5.0)
p.pixels = (600, 200)
p.color_by = 'material'
openmc.plot_inline(p)

Now let's brew some tea! ... using a very hot neutron source. We'll use some well-placed point sources distributed throughout the model.

In [16]:
settings = openmc.Settings()
settings.dagmc = True
settings.batches = 10
settings.particles = 5000
settings.run_mode = "fixed source"

src_locations = ((-4.0, 0.0, -2.0),
                 ( 4.0, 0.0, -2.0),
                 ( 4.0, 0.0, -6.0),
                 (-4.0, 0.0, -6.0),
                 (10.0, 0.0, -4.0),
                 (-8.0, 0.0, -4.0))

# we'll use the same energy for each source
src_e = openmc.stats.Discrete(x=[12.0,], p=[1.0,])

# create source for each location
sources = []
for loc in src_locations:
    src_pnt = openmc.stats.Point(xyz=loc)
    src = openmc.Source(space=src_pnt, energy=src_e)
    sources.append(src)

src_str = 1.0 / len(sources)
for source in sources:
    source.strength = src_str

settings.source = sources
settings.export_to_xml()

...and setup a couple mesh tallies. One for the kettle, and one for the water inside.

In [17]:
mesh = openmc.RegularMesh()
mesh.dimension = (120, 1, 40)
mesh.lower_left = (-20.0, 0.0, -10.0)
mesh.upper_right = (20.0, 1.0, 4.0)

mesh_filter = openmc.MeshFilter(mesh)

pot_filter = openmc.CellFilter([1])
pot_tally = openmc.Tally()
pot_tally.filters = [mesh_filter, pot_filter]
pot_tally.scores = ['flux']

water_filter = openmc.CellFilter([5])
water_tally = openmc.Tally()
water_tally.filters = [mesh_filter, water_filter]
water_tally.scores = ['flux']


tallies = openmc.Tallies([pot_tally, water_tally])
tallies.export_to_xml()
In [18]:
openmc.run()
                                %%%%%%%%%%%%%%%
                           %%%%%%%%%%%%%%%%%%%%%%%%
                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                                    %%%%%%%%%%%%%%%%%%%%%%%%
                                     %%%%%%%%%%%%%%%%%%%%%%%%
                 ###############      %%%%%%%%%%%%%%%%%%%%%%%%
                ##################     %%%%%%%%%%%%%%%%%%%%%%%
                ###################     %%%%%%%%%%%%%%%%%%%%%%%
                ####################     %%%%%%%%%%%%%%%%%%%%%%
                #####################     %%%%%%%%%%%%%%%%%%%%%
                ######################     %%%%%%%%%%%%%%%%%%%%
                #######################     %%%%%%%%%%%%%%%%%%
                 #######################     %%%%%%%%%%%%%%%%%
                 ######################     %%%%%%%%%%%%%%%%%
                  ####################     %%%%%%%%%%%%%%%%%
                    #################     %%%%%%%%%%%%%%%%%
                     ###############     %%%%%%%%%%%%%%%%
                       ############     %%%%%%%%%%%%%%%
                          ########     %%%%%%%%%%%%%%
                                      %%%%%%%%%%%

                   | The OpenMC Monte Carlo Code
         Copyright | 2011-2019 MIT and OpenMC contributors
           License | http://openmc.readthedocs.io/en/latest/license.html
           Version | 0.11.0-dev
          Git SHA1 | 2c0b16e73d5d81a2f849f4e2bfae5eb5319f2417
         Date/Time | 2019-06-18 08:54:35
    OpenMP Threads | 2

 Reading settings XML file...
 Reading cross sections XML file...
 Reading materials XML file...
 Reading DAGMC geometry...
Loading file dagmc.h5m
Initializing the GeomQueryTool...
Using faceting tolerance: 0.001
Building OBB Tree...
 Reading Fe54 from /home/shriwise/opt/openmc/xs/nndc_hdf5/Fe54.h5
 Reading Fe56 from /home/shriwise/opt/openmc/xs/nndc_hdf5/Fe56.h5
 Reading Fe57 from /home/shriwise/opt/openmc/xs/nndc_hdf5/Fe57.h5
 Reading Fe58 from /home/shriwise/opt/openmc/xs/nndc_hdf5/Fe58.h5
 Reading H1 from /home/shriwise/opt/openmc/xs/nndc_hdf5/H1.h5
 Reading O16 from /home/shriwise/opt/openmc/xs/nndc_hdf5/O16.h5
 Reading c_H_in_H2O from /home/shriwise/opt/openmc/xs/nndc_hdf5/c_H_in_H2O.h5
 Maximum neutron transport energy: 20000000.000000 eV for Fe58
 Reading tallies XML file...
 Writing summary.h5 file...
 Initializing source particles...

 ===============>     FIXED SOURCE TRANSPORT SIMULATION     <===============

 Simulating batch 1
 Simulating batch 2
 Simulating batch 3
 Simulating batch 4
 Simulating batch 5
 Simulating batch 6
 Simulating batch 7
 Simulating batch 8
 Simulating batch 9
 Simulating batch 10
 Creating state point statepoint.10.h5...

 =======================>     TIMING STATISTICS     <=======================

 Total time for initialization     = 4.9659e+00 seconds
   Reading cross sections          = 6.3783e-01 seconds
 Total time in simulation          = 1.5112e+01 seconds
   Time in transport only          = 1.4102e+01 seconds
   Time in active batches          = 1.5112e+01 seconds
   Time accumulating tallies       = 2.8790e-04 seconds
 Total time for finalization       = 3.2375e-02 seconds
 Total time elapsed                = 2.0224e+01 seconds
 Calculation Rate (active)         = 3308.59 particles/second

 ============================>     RESULTS     <============================

 Leakage Fraction            = 0.62594 +/- 0.00117

Note that the performance is significantly lower than our pincell model due to the increased complexity of the model, but it allows us to examine tally results like these:

In [19]:
sp = openmc.StatePoint("statepoint.10.h5")

water_tally = sp.get_tally(scores=['flux'], id=water_tally.id)
water_flux = water_tally.mean
water_flux.shape = (40, 120)
water_flux = water_flux[::-1, :]

pot_tally = sp.get_tally(scores=['flux'], id=pot_tally.id)
pot_flux = pot_tally.mean
pot_flux.shape = (40, 120)
pot_flux = pot_flux[::-1, :]

del sp
In [20]:
from matplotlib import pyplot as plt
fig = plt.figure(figsize=(18, 16))

sub_plot1 = plt.subplot(121, title="Kettle Flux")
sub_plot1.imshow(pot_flux)

sub_plot2 = plt.subplot(122, title="Water Flux")
sub_plot2.imshow(water_flux)
Out[20]:
<matplotlib.image.AxesImage at 0x7f90d12b8198>