Modeling a Pin-Cell ================================= In this example, we'll create a simple pin-cell model. This will demonstrate the basics of building a geometry with ``mcnpy`` and setting up the supporting MCNP cards to run the simulation. Initializing the Model --------------------------------- The start of any ``mcnpy`` script begins with imports as with any other Python package. .. code-block:: python import mcnpy as mp When ``mcnpy`` is imported, you'll see the following output: .. code-block:: python Metamodel Gateway Server Started This indicates that the link between the Python and Java portions of the API has been established. After a slight delay, the module will finish importing and ``mcnpy`` will otherwise perform like a standard Python package. .. code-block:: python deck = mp.Deck() This creates our MCNP deck (or input file). Currently, the deck is empty as can be seen by calling ``print(deck)``. .. code-block:: MCNP Deck **CELL CARDS** Cells = 0 Universes = 0 **SURFACE CARDS** Surfaces = 0 **DATA CARDS** Geometry = 0 Transformation = 0 Physics = 0 Source = 0 Var. Reduction = 0 Tallies = 0 Tal. Settings = 0 Output = 0 Termination = 0 Misc = 0 Other Data = 0 **MATERIAL CARDS** Materials = 0 Mat. Settings = 0 .. note:: As cards are added to the deck, this output will update accordingly. Defining Materials --------------------------------- Now a new MCNP deck has been created. Cards can now be created and added to the deck. We will start with the material cards. .. code-block:: python from mcnpy.elements import * enr = 3 u_enr = U[238]%(100-enr) + U[235]%enr fuel = mp.Material(u_enr + O[16]@2) Materials can be defined symbolically using the singleton objects from ``mcnpy.elements`` such as ``U`` and ``O`` for uranium and oxygen, respectively. To assign the appropriate fractions to each isotope, the operators in the following table should be used. .. csv-table:: :header: "Operation", "Symbol (Python)", "Symbol (MCNP)" :widths: 20, 20, 20 :align: center "Atomic Fraction", ``@``, ``+`` "Weight Fraction", ``*``, ``-`` "Weight Percent", ``%``, N/A Unlike is native MCNP syntax, operators can be combined within a single material. Here, the uranium enrichment is defined using Weight percentages while the final |UO2| mixture uses atomic fractions. When the associated material card is generated, all conversions between the fraction types will be handled automatically. As a fissionable material, it is also important to add a thermal scattering card. .. code-block:: python # Include S(a,B). Normally added as a separate card in MCNP. fuel.s_alpha_beta = ['o_in_uo2', 'u238_in_uo2'] .. code-block:: python mod = mp.Nuclide('h1', 2.00) + mp.Nuclide('8016', 1.0) # Alternatively... # mod = mp.Material(H[1]@2 + O[16]) mod.s_alpha_beta = 'lwtr' solutes = {SN: 1.5, FE: 0.2, CR: 0.1} zirc4 = ZR%(100-sum(solutes.values())) for sol in solutes: zirc4 += sol%solutes[sol] clad = mp.Material(zirc4) gap = mp.Material(H[1]@0.5 + HE[4]@0.5) .. note:: Numeric ZAIDs are still perfectly valid. The ``mcnpy.Material`` objects can now be added to the deck. .. code-block:: python deck += [fuel, mod, clad, gap] After creating any object corresponding to an MCNP card (Cell, Surface, Material, etc.), it must be added to the deck. This is easily done using the ``+=`` operation. Defining Surfaces --------------------------------- Next, the surface cards will be defined. The pin-cell will be defined using several Z-cylinders along with a rectangular bounding box. .. code-block:: python fuel_outer_radius = mp.ZCylinder(name=1, x0=0, y0=0, r=0.39) clad_inner_radius = mp.ZCylinder(name=2, x0=0, y0=0, r=0.40) clad_outer_radius = mp.ZCylinder(name=3, x0=0, y0=0, r=0.46) The bounding box for the pin-cell will be defined using an RPP macrobody. Macrobodies can be specified in the same way as simple surfaces. .. code-block:: python pitch = 1.26 bounding_box = mp.RectangularPrism(x0=-pitch/2, x1=pitch/2, y0=-pitch/2, y1=pitch/2, z0=-pitch/2, z1=pitch/2, boundary_type='reflective', name=4) deck += [fuel_outer_radius, clad_inner_radius, clad_outer_radius, bounding_box] The X, Y, and Z limits for the macrobody are defined using a variable ``pitch`` for the pin-cell. We also use ``boundary_type`` to specify reflecting boundaries (or ``*`` in MCNP syntax). Defining Regions --------------------------------- With all the surfaces defined, the regions that will define the cells can be created next. To define ``mcnpy.Region`` objects, a series of special operators are applied to surfaces. .. code-block:: python fuel_region = -fuel_outer_radius & -bounding_box gap_region = +fuel_outer_radius & -clad_inner_radius & -bounding_box clad_region = +clad_inner_radius & -clad_outer_radius & -bounding_box mod_region = +clad_outer_radius & -bounding_box outside_region = +bounding_box .. note:: Since a region is not an MCNP card itself, we do not need to directly add them to the deck. Instead, each ``mcnpy.Region`` will be added to an ``mcnpy.Cell``. Halfspaces are defined by prepending a ``mcnpy.Surface`` with ``+`` or ``-`` for positive and negative senses, respectively. Halfspaces may then be combined with the following operations: .. csv-table:: :header: "Operation", "Symbol (Python)", "Symbol (MCNP)","Semantics" :widths: 20, 20, 20, 20 :align: center "Union", ``|``, ``:``, "OR" "Intersection", ``&``, `space`, "AND" "Complement", ``~``, ``#``, "NOT" .. note:: Complements may be applied to cells or regions. Defining Cells --------------------------------- .. code-block:: python fuel_cell = mp.Cell(1, fuel_region, fuel*10.0) # Alternatively... # fuel_cell = mp.Cell(name=1, region=fuel_region, density=-10.0, # material=fuel) A ``mcnpy.Cell`` is defined for the fuel and requires a ``name``, ``region``, ``density``, and ``material``. Densities can be conveniently assigned using operators on the material argument. These operators are described in the following table. .. csv-table:: :header: "Unit", "Symbol (Python)", "Symbol (MCNP)" :widths: 20, 20, 20 :align: center |atm-b-cm|, ``@``, ``+`` |gcm3|, ``*``, ``-`` .. code-block:: python clad_cell = mp.Cell(2, clad_region, clad*6.6) mod_cell = mp.Cell(3, mod_region, mod*1.0) gap_cell = mp.Cell(4, gap_region, gap*1.78e-4) void_cell = mp.Cell(5, outside_region, None) deck += [fuel_cell, clad_cell, mod_cell, gap_cell, void_cell] The other cells are defined in the same way, except for ``void_cell``. Since this cell corresponds to a region outside of the pin-cell's reflecting boundaries, its material is set to void via ``material=None`` and no density is required. Setting Cell Importances --------------------------------- MCNP requires that particle importances are set for each cell in the model. For this example, we will set the importance of neutrons to 1.0 in each cell *except* ``void_cell`` where the importance will be 0.0. Particle types (and many other enumerated inputs) can be set using a string or through an ``Enum`` like ``mcnpy.Particle.NEUTRON``. .. code-block:: python for cell in deck.cells.values(): if cell.material is None: cell.importances = {mp.Particle.NEUTRON : 0.0} else: cell.importances = {'n' : 1.0} Importances are provided as dictionaries where each importance is a key and lists of particles are the values. Here importances are set by looping over the cells. Source Definition --------------------------------- For the pin-cell, we will be solving an eigenvalue problem. This can be set up with MCNP's *KCODE* card. .. code-block:: python # Define criticality source deck += mp.CriticalitySource(histories=1e4, keff_guess=1.0, skip_cycles=200, cycles=1200) deck += mp.CriticalitySourcePoints([(0,0,0), (0,0,0.5), (0,0,-0.5)]) Using ``mcnpy.CriticalitySource`` and ``mcnpy.CriticalitySourcePoints``, we are able to configure the source definition for the eigenvalue problem. Output Control --------------------------------- To enable easy post-processing, it is useful to generate an MCTAL (tally summary) file. This can be done in a simple one line command. .. code-block:: python deck += mp.PrintDump(print_mctal=1) With the last card added to the deck, we can once again call ``print(deck)`` to see what the deck now includes: .. code-block:: MCNP Deck **CELL CARDS** Cells = 5 Universes = 1 **SURFACE CARDS** Surfaces = 4 **DATA CARDS** Geometry = 0 Transformation = 0 Physics = 0 Source = 2 Var. Reduction = 0 Tallies = 0 Tal. Settings = 0 Output = 1 Termination = 0 Misc = 0 Other Data = 0 **MATERIAL CARDS** Materials = 4 Mat. Settings = 2 Writing the MCNP File --------------------------------- Now that the pin-cell model is complete, the final step is to write the model to a file which MCNP can understand. This is done by invoking ``write(filename)``. .. code-block:: python deck.write('my_pincell.mcnp') The following MCNP file should be generated: .. code-block:: mcnp $ This file was written with mcnpy 1 1 -10.0 -1 -4 IMP:N=1.0 2 3 -6.6 2 -3 -4 IMP:N=1.0 3 2 -1.0 3 -4 IMP:N=1.0 4 4 -1.78E-4 1 -2 -4 IMP:N=1.0 5 0 4 IMP:N=0.0 1 CZ 0.39 2 CZ 0.4 3 CZ 0.46 *4 RPP -0.63 0.63 -0.63 0.63 -0.63 0.63 M1 92238 0.9696278737791809 92235 0.030372126220819046 8016 2.0 M2 1001 2.0 8016 1.0 M3 40000 -0.982 50000 -0.015 26000 -0.002 24000 -0.001 M4 1001 0.5 2004 0.5 MT1 O2-U U-O2 MT2 LWTR KCODE 10000.0 1.0 200.0 1200.0 KSRC 0.0 0.0 0.0 0.0 0.0 0.5 0.0 0.0 -0.5 PRDMP 0.0 -60.0 1.0 With a complete input file, you are now ready to run MCNP. The generated file will always follow MCNP's syntax and can even be read and modified using ``mcnpy``. The full model can also be generated using .. code-block:: python import mcnpy as mp # Pin-cell example class pincell = mp.example.Pincell() # Deck object for the pin-cell deck = pincell.deck .. |gcm3| replace:: g/cm\ :sup:`3` .. |atm-b-cm| replace:: atom/b-cm .. |UO2| replace:: UO\ :sub:`2`