Closed drewejohnson closed 3 weeks ago
This response got a little longer than I originally thought, so TL;DR:
Lots of this stuff might be possible now. The main thing in your way is the Blueprints, which lacks the flexibility to define more complicated things easily, and things like clipped hexes, which will need some head scratching. I would recommend trying to drive the Composites/Grids in code to prototype a model that you think works. See some recommendations below. From there we can talk about where to go from the input processing/blueprints side.
Glad to start a discussion about how this could be done in earnest! My main input from reading through the above is that we should try to think through what the reactor model should like like in terms of the ARMI Composite tree before we attempt to tackle the blueprints side of things. For some of these ex-core structures, i would imagine using Block/Assembly objects would make sense, but for many others, it would not (instead being a bare Component of some sort, or a general-purpose Composite, or a custom-defined Composite). All of these are technically possible today, but for a couple of things:
grids
was implemented with an eye towards) is to represent the movable-assembly portion of the core as one Core-like object (remember, these are governed by a 2-D grid), which we can call "active core". This would be a child of another Composite, which we can call "core". The "grid plate" would then be represented as another Composite object as "active core"'s sibling in "core". "Core" itself would hold the two in a 1-D grid, placing the "active core" above the "grid plate". Other stuff above or below the active/fuelled core region can be represented in much the same way. This give a reactor model something like this:
Historically, we have been sticking to a single Core object because A) input processing gets harder the more general it becomes and B) sticking to a more rigid Core->Assembly->Block->Component structure has made writing interfaces to external physics code a lot easier, since it gives you some guarantees about the tree structure. Adding extra children to the Reactor or more depth to the Core would mean the the interfaces need to know how to handle those extra structures properly. In something like MCNP that might not be too bad, but for a code that expects prisms all the way through, handling anything below, say, the grid plate requires more nuance.
I hope this helps! Its a big topic, so might be worth a little workshop or something to hash out some more ideas.
@youngmit thank you for the detailed explanation. I do appreciate getting a look at the design and inheritance model 👍
My main input from reading through the above is that we should try to think through what the reactor model should like like in terms of the ARMI Composite tree before we attempt to tackle the blueprints side of things.
Definitely agree
the permanent shield/clipped hex thing looks kind of hard
but for a code that expects prisms all the way through, handling anything below, say, the grid plate requires more nuance.
I wonder if an extra ring of some homogenized reflector + RPV might be a good workaround / substitute. Then it's still a regular structure. But the homogenzation would have to be block specific since the fraction of reflector + rpv + void varies by block... yikes
axial ex-core stuff actually is something we have talked a lot about in the past. This is actually possible now, but for the blueprints not really supporting it.
exciting! by blueprints not really supporting axial ex-core stuff, do you mean that's something that has to be made w/ a driver script, not the plain yaml file?
Adding extra children to the Reactor or more depth to the Core ...
If instead of adding the RPV to the Core, would it be easier w.r.t. existing interfaces to have the RPV and ex-core stuff be a child of the Reactor and a sibling to Core? Then anything that works on o.r.core
should be fine. Maybe?
I'll try and kick some stuff around and see what I can come up with.
I wonder if an extra ring of some homogenized reflector + RPV might be a good workaround / substitute. Then it's still a regular structure. But the homogenzation would have to be block specific since the fraction of reflector + rpv + void varies by block... yikes
You should see how we model molten salt reactors :) This essentially becomes a mesh overlay problem, which is super doable, but does erode some of the value-add of ARMI, since it is no longer possible to, say, thermally expand components. You could do that on the higher-level meshes that you are shoehorning into the ARMI model and re-map everything, but its definitely less nice :/ Still, having a prismatic, block-y thing with well-represented volume fractions is still mighty useful.
exciting! by blueprints not really supporting axial ex-core stuff, do you mean that's something that has to be made w/ a driver script, not the plain yaml file?
Yup, some sort of driver script. We actually had experimented with a plugin API that would let a plugin provide custom strategies for creating the reactor model itself (see below). In this way, from Blueprints would become the default method, but one could write some custom code that largely eschews Blueprints and just whips up a Reactor on it's own (perhaps parameterized on some custom input). Making this a plugin hook is necessary to make things like [armiapp] run [case settings]
work. However, if we are just spitballing how best to represent a Reactor model with these sorts of bells and whistles, I think a one-off driver script is the best place to start. Note that Blueprints are still needed to provide nuclide flags. This is pretty high on my list of priorities to resolve, since the supremacy of Blueprints has always rubbed me the wrong way, and this discussion is a perfect object lesson into why :)
If instead of adding the RPV to the Core, would it be easier w.r.t. existing interfaces to have the RPV and ex-core stuff be a child of the Reactor and a sibling to Core?
Yup! I was trying to hint at that, but probably could have made it more clear by including it in the diagram. I would imagine that the RPV would be next to the Core, not inside of it:
The wrinkle with treating the core as a thing that is itself a 1-D stack of things like the active core region on top of the grid plate (to take an example), is that a trivial implementation of, say, a global flux solver would probably want to model both the active core and the grid plate. If they are in one Core object with the fuel, plenum, grid plate, etc being stuffed into an Assembly, its a simple loop over Assemblies -> Blocks to form a neutronics model. If the grid plate is in some other object, that generality needs to be addressed. Not a bad thing by any means, but the main reason why we haven't gone out of our way to do it yet. The same thing goes for something like an RPV, core barrel or whatever. In your various physics interfaces (and implementing code), what object/scope do you pass in? The Core (thus ignoring RPV)? The Reactor (requiring the callee to chose which systems to include in its model)? A collection containing the Core and whatever other components are relevant (placing the burden on the caller)? All of these seem reasonable, depending on the circumstances and the desired methodology.
Regarding custom reactor model construction/initialization, I had in the past proposed the following plugin API:
@staticmethod
@HOOKSPEC
def defineReactorConstructionMethods():
"""
Return new methods by which a Reactor object can be created.
This allows for plugins to provide their own approach to Reactor model
construction than the default, Blueprints-defined method that comes with ARMI.
These can be useful for exotic reactor types which are not well served by the
Blueprints mechanism.
Returns
-------
methods : dict
Implementations should return a dictionary mapping of name to method,
where ``name`` is a unique string label for the corresponding method, and
``method`` is a function-like object that takes a ``Settings`` object as its
only argument and returns a fully-constructed ``Reactor`` object.
Note
----
Reactor construction is ultimately governed by the ``reactorConstructionMethod``
setting. To fully expose new methods, the names should also be provided as
Options to the ``reactorConstructionMethod`` setting as well.
See also
--------
defineSettings
armi.settings.setting.Option
Example
-------
In this example, we define a new function for creating a Reactor object. It uses
a new setting that we also provide to control its behavior. We also advertise
the method using the ``defineSettings()`` and
``defineReactorConstructionMethods()`` hooks::
def fancyReactorMaker(cs):
r = reactors.Reactor(name=cs["fancyReactorName"], blueprints=None)
// Do more magic!
return r
class MyPlugin(ArmiPlugin):
def defineSettings():
return [
# This setting is used to control/parameterize the function
# defined above.
setting.Setting("fancyReactorName", default=""),
setting.Option("reactorConstructionMethod", "fancyReactor")
]
def defineReactorConstructionMethods():
return {"fancyReactor": fancyReactorMaker}
"""
The direct motivation for this API was to make a custom reactor initializer that did some mesh overlay work to handle weirdly-shaped Blocks/Components (eerily similar to your fixed reflector situation). The intention was for this to also serve as a proving ground for more flexible Reactor model hierarchies and ex-core things that aren't yet supported by Blueprints. If you think this would be useful, I could try to get this on a branch for you to experiment with.
Thanks for the detailed response @youngmit. If you're comfortable pushing a branch of some level of usability (WIP or stable but not accepted) I could take a swing at using that hook. It might be a good way to drop in some shield / rpv-like classes
@drewejohnson finally got around from digging this up. Check out the rxConstructMethod
branch. This is the result of cherry-picking a couple of commits from an old branch and some minor retrofit, so may have goofed on something. Let me know if you run into any trouble.
@youngmit thank you for making that available. I'll let you know how it goes on my end
One possible solution to this would be to an ExCore
child to the Reactor
(just as the SFP was recently made a child of the Reactor.). Then Pylot could define parameters for the objects in that structure, like we do in so many other places in ARMI and in downstream plugins:
So, we would define:
class ExCore(composites.Composite)
, to be a hierarchical data object for storing ex-core structures.Reactor.excore
would be added as a attribute to the Reactor
class.ExCoreStructure
/ ExCoreBlock
would need to be created, so we have something to add Parameter
s to.This all seems quite easy to do. And then ex-core structures would be automatically read to-and-from the ARMI Database.
Downsides on this first draft, that ARMI could improve in future versions:
Shielding
or ContainmentVessel
or Pipe
, you would have to subclass our ExCoreBlock
and define them yourself. Obviously, my idea is just a starting place. Your double-cylinder would be built on top of this kind of entry-level infrastructure. I'm just thinking aloud about a place to start.
@jakehader This is the ticket in question.
In order for something like this to work, we need:
Reactor
object
a. Like what Michael did with the SFP
b. Without this, the new ex-core stuff would not be in the DB.Plugin
hook, that seems good.My last thought is: I don't want to have to hunt or guess at what is in the Reactor
, so I would prefer if all such things (and maybe the SFP
?) fell under reactor.excore
, so they were easy to find and identify. SOMETHING like that.
I think everything under reactor
could be fine. Having excore
creates another layer that might not add much value. I like to think if the reactor as "a plant" so to me, having the reactor be the parent with arbitrary definitions of containers / grids that sit in a flat structure could be the easiest. While in theory we could have things like pumps, heat exchangers, vessels, and physical equipment defined, I think the ability to say where in space a container relative to one another could be a first step and then if we want the reactor or a reactor to support logic for both things are interrelated or connected then that might be by the specific plugin or plugins that implement the things.
For example, having a dry cask storage pad with dry casks that sit in the grid cylindrical grid which contain core assemblies inside in a cartesian grid of the casks themselves. In this hypothetical, the core, SFP, and StoragePad would each have independent grids and the reactor would then have it's own grid that defines the relative orientation and origin of each grid underneath. Next, for a specific reactor design, the plugin or application might want some functionality to say, core assemblies shall not go straight to the StoragePad until some conditions are met (i.e., passing through the SFP, waiting for X amount of time, until peak decay heat is below a certain value, or all the above). I think applications should be able to subclass the Reactor if needed to get some type of this behavior and the plugin hooks should support an adaptive process to register new things as needed. In this example, may be the Reactor doesn't hold the logic or constraints but maybe a FuelHandler does.
Right now I'm just thinking let's add the capability to have "N" containers that sit on the reactor that house assemblies. How they get there based on other equipment or snap to that location using a FuelHandler may be the next step. Handling fluid systems like heat exchangers, pumps, pipes, etc. is probably too ambitious at first, and would require more thought about what a Reactor object truly is and isn't
Okay, we finally have a first pass at this in ARMI: https://github.com/terrapower/armi/pull/1891
We have two teams using this feature internally, so I fully expect if there are problems, they will be opened as smaller (less architectural) changes.
Thanks, all!
Wanted to start a discussion / propose a new feature: ex-core structures in ARMI
The base system I'd like to model is one or more concentric right cylinders defining ex-core structures like core barrel, reactor pressure vessel (RPV) and shielding. I had a thought of adding some features to the blueprints file, like a block that doesn't make it's way into the core, but still utilizes material resolution and dimension linking. A new
ComplexShape
could be added to model the various aspects of the canning like(dimensions made up) This information could be provided in the
blocks
section using circles rather than cylinders, and not placed in the core facemap. Maybe given a uniqueFlag
to show it's part of the ex-core for bookkeeping and other checks, e.g.,getFirstBlock(Flags.EXCORE)
I guess you could also model the upper / lower ex-core structures with dedicated axial layers. But you could run into a position like where the vertical ex-core structures that make up the radial shielding don't form a connection with the two lowest levels. Or they would, but the green and blue materials don't line up correctly
Another consideration would be "clipped" assemblies. In modeling the MHTGR in ARMI (#224), we'd likely have extra rings around the outside making up the "permanent reflector" which would then be clipped by the barrel to make up those complex outer shapes.
I've dropped a proxy barrel over the FFTF facemap as another example This will likely also have knock-on effects for tallying (full hex volume won't actually see neutrons) and visualization
The MHTGR also has some repeating arc-segments that make up coolant channels. The BEAVRS model has similar arcs for neutron shields between the core barrel and the RPV The region between the barrel, shield segments, and RPV could be filled with a
DerivedShape
to help model the down-comer region