terrapower / armi

An open-source nuclear reactor analysis automation framework that helps design teams increase efficiency and quality
https://terrapower.github.io/armi/
Apache License 2.0
234 stars 89 forks source link

Finding hexagonal pin pitch for wire-less assemblies #252

Open drewejohnson opened 3 years ago

drewejohnson commented 3 years ago

(Came up working on a custom plugin for the HTGR model in #251 but felt it merited a different discussion)

HexBlocks have a getPinPitch method which was my first guess for finding the pin pitch for a hexagonal pin lattice. But the docs indicate that the pin pitch is defined by, and thus requires, wire and cladding. For an HTGR, where the fuel pellets might be placed in channels, these components might not exist in the reactor.

For a workaround, I found that adding a dummy component

        pitch:
            shape: Hexagon
            material: Void
            Tinput: 600
            Thot: 600
            ip: 3
            op: 3

worked well. But this would imply that each block would require this component, even if the same underlying lattice map was used.

Could ARMI be extended to support a pitch attribute in the lattice map section for hexagonal lattices. The C5G7 example gives something like

    UO2 grid:
        symmetry: full
        geom: cartesian
        lattice pitch:
            x: 1.26
            y: 1.26
ntouran commented 3 years ago

Excellent question. Yes the getPinPitch has an overly design-specific implementation and we'd like to support something more genetic.

Philosophy Aside: We have been slowly moving design-dependent inferences like that out of the composite tree model and into functions that are more appropriately filed. So like we just moved b.isDepletable to the physics/neutronics folder and changed it to isDepletable(b). So in this example, it may make sense to move b.getPinPitch to somewhere less general and call it like getPinPitch(b). Of course it's a tradeoff because at the extreme there's nothing useful left! :smile:

The LWR input you highlight above actually puts the pin-level grids on the blocks in the input. This is probably exactly what we'd want to do on the HexBlock level as well to properly support pins with locations in there. Then client code would just grab the block's grid and ask it for its pitch to get the pitch. Does this sound like a good approach? We can try to help build example inputs and a better getPinPitch function.

EDIT: I see this is what you've already begun in the inputs in #251. Let me load that up and try some code out.

drewejohnson commented 3 years ago

Thanks for the response.

Then client code would just grab the block's grid and ask it for its pitch to get the pitch.

sounds like a fantastic solution!

Using the files from #251, the blocks do have a spatialGrid defined but the pitch is set to 1. If this pitch could be set through the grids section, one, in theory, could use b.spatialGrid.pitch over b.getPinPitch. Or b.getPinPitch could rely on the spatial grid first before checking for wires and cladding. Kind of spit balling here and there might be more nuances under the hood

ntouran commented 3 years ago

Perhaps undocumented, but hex grids defined in blueprints default to a pitch of 1.0 but will take the x dimension if it is defined. (code here).

So if you change your grid definition to say:

pins:
  geom: hex
  symmetry: full
  lattice pitch:
    x: 2.5
  lattice map: |
    -   -   FP
      -   FP  FP
    -   CL  CL  CL
      FP  FP  FP  FP
    FP  FP  FP  FP  FP
      CL  CL  CL  CL
    FP  FP  FP  FP  FP
      FP  FP  FP  FP
    CL  CL  CL  CL  CL
      FP  FP  FP  FP
    FP  FP  FP  FP  FP
      CL  CL  CL  CL
    FP  FP  FP  FP  FP
      FP  FP  FP  FP
        CL  CL  CL
          FP  FP
            FP

Then the block grid will have a pitch of 2.5 cm.

import armi
armi.configure()
o = armi.init(2)
from armi.reactor.flags import Flags
b = o.r.core.getFirstBlock(Flags.FUEL)
b.spatialGrid.pitch

And you can get the pin coordinates with code like this:

x = []
y = []
import matplotlib.pyplot as plt
for c in b.getChildren(Flags.FUEL):
    for loc in c.spatialLocator:
       xi,yi,zi = loc.getGlobalCoordinates()
       x.append(xi)
       y.append(yi)
plt.plot(x,y,'o')
plt.show()

image

Additional disclaimers and warnings here: there are likely other issues and inconsistencies with this kind of thing since we haven't used in-block pin lattices much. For example the fuel spatialLocator is actually a collection of locators since it's using a grid, as opposed to most locators which are not iterable like that. But we're excited to polish it up.

drewejohnson commented 3 years ago

Oh neat find! Thanks for pointing that out. If that makes our work easier I might rely on that with the understanding it could get removed at a moment's notice

youngmit commented 3 years ago

the understanding it could get removed at a moment's notice

If there is behavior that you rely on, and makes sense as it is, feel free to make a PR with a test against that behavior! We need to start getting better at defining ARMI semantics so others may confidently use them, and this would be a step in the right direction.

drewejohnson commented 3 years ago

The weird bit is that the blueprints for a hex lattice find the pitched based on the x element. That's more the part that we're treating as if it could be removed in any given PR. Which is okay on our end.

I'm hesitant to make a PR w/ testing and codifying this practice because an attribute / key like x feels odd for hex grids. Finding the pitch for a hex grid based on a new attribute / key like pitch feels better but might require more tweaks on the blueprint processing side

youngmit commented 3 years ago

We might be able to have the blueprints accept a scalar as well as the more general x/y/z triplet. I ran into this myself recently and it is a bit odd to be sure...