Open drewj-usnctech opened 1 year ago
I agree that this should be possible and could also be valuable.
It would have to be made very clear what exactly corresponds to the "initial" HM mass. For instance, is it recalculated at the beginning of each cycle after fuel is shuffled? If so, that could potentially pose some challenges from an architectural standpoint because right now the cycles
setting is resolved at the very start of a run. So since it won't be known what the BOC HM mass is at later cycles, resolving the cycle lengths out until the end of a run might be a problem.
The only request I'd have is just to keep the docs up to date.
It would have to be made very clear what exactly corresponds to the "initial" HM mass
Great point. My gut would be BOL based on Reactor.getHMMass
, but if you have a Core
and SFP
child (or grandchild) of the reactor, do you want spent fuel or to-be-added-later fuel that technically exists at BOL to count? Maybe Core.getHMMass
?
I am not sure, but this might be a good place to consider subclassing the Standard Operator? I'd need look at the Operator definitions but we should write some requirements around how much math and how to do the math on an Operator if we extend in this way.
(I like the suggestion by the way - sometimes cross sections are better functionalized with burn-up versus time so maybe there is a good reason to know or control burnup steps intentionally)
Taking insight from @jakehader
I am not sure, but this might be a good place to consider subclassing the Standard Operator
I dug around.
burn steps
as a cycle parameterThere's no easy way around this: we would need to modify the schema for cycles
to support step burnup
and cumulative burnup
. Otherwise we can't even create a Settings
attribute with step burnup
One could expose their own Operator
to the App
with the getOperatorClassFromRunType
hook
but you would need a unique runType
string to get around how the hook is used
A workaround would be to switch the order of operations here: iterate over hooks first, then fallback to standard operator / mpi operator / snapshot operator
Okay so let's assume we've gotten a custom operator subclass, and our burnup-based depletion schedule is attached to the Settings
. How do we modify depletion schedule to provide step days
that the rest of ARMI supports?
The first time, as far as I can see, that a reactor is near the settings is Operator.initializeInterfaces
which feels like a good place to make our change. Before we start giving settings to interfaces that do stuff with settings.
So, we could override initializeInterfaces
to take the reactor power and core heavy metal mass, and convert all the step burnup
entries into step days
with days = burnup * hmMass / power
(taking correct units into consideration)
Unfortunately, we can't even make it through Operator.__init__
because the welcome headers fails try to write the step lengths for each cycle
During the first fetching of Operator.stepLengths
, _getStepAndCycleLengths
fails because there aren't any step days
or other "known" time step data in our cycle.
Now, if the printing of the welcome headers can be delayed until the operator can update the settings with the reactor, then the step lengths can still be printed. If it's not necessary to do the welcome header printing every time an operator is constructed, this might be easier. Something like
o = armi.operators.factory(settings)
reportingUtils.writeWelcomeHeaders(o, settings)
This sounds like a great refactor to support application based operators and to give more power to end users on how they want work to be performed and looks like you're on the right track. What do you need from us? Need us to propose a PR change?
Thanks for your thinking about this @drewj-usnctech . Looking at reportingUtils.writeWelcomeHeaders()
, it seems to me that it might be more appropriate if that were a method on the of the operator itself. I don't really see why it sits as a function in a different module, as it is intimately tied to the initialization of an operator.
If you moved writeWelcomeHeaders()
to be an Operator
method, then you could just make custom implementations for each operator. This could potentially solve your issue of getting beyond Operator.__init__()
. And it seems more natural anyway, IMO.
Thanks @jakehader and @keckler
I think there are three independent changes to bring this to fruition
armi.operators.getOperatorClassFromSettings
to allow plugin hooks to provide their own operator subclass type. If no plugin hooks provide an operator given the run type, then the previous pre-hook behavior of standard / mpi / snapshot could be usedreportingUtils.writeWelcomeHeaders
from Operator.__init__
. Where to put that functionality is worth some discussion*step burnup
(and concurrently or later cumulative burnup
) in cs["cycles"]
array**reportingUtils.writeWelcomeHeaders
boils down to four functions, only one of which is really tied to the operator: writing the operational conditions. So something like Operator.summarizeOperationalConditions() -> dict
would probably play nice w/ reportingUtils.writeWelcomeHeaders
I would lean towards moving the call to reportingUtils.writeWelcomeHeaders
to after the operator has been given a reactor. I think during or after armi.cases.case.Case.initializeOperator
?
cs["cycles"]
If step burnup
is added to cycles array, how should armi.utils.getStepLengths
and getCycleLengths
be updated? They both rely on armi.utils._getStepLengthsAndCycleLengths
which would need some logic to handle burnup steps. Maybe just error out because both need some length of time in days to be meaningful to people asking step and cycle lengths.
Same with armi.utils.hasBurnup
. This could probably support step burnup
logic because we just want to know if the Settings
produce a run with depletion. But it also relies on getStepLengths
so maybe a carve out if getStepLengths
fails?
There is a weird partially constructed problem where if I make an operator with burn steps, I can't do things like Operator.stepLengths
because I don't have a reactor to convert burnup steps to day steps.
Following this subclassing thread, it feels weird that this leads to two different operators, that differ only in their depletion schedule. I don't have more thoughts on how to remedy this, but it feels worth mentioning
@john-science similar discussions to power vs. power density discussions for #1365
An alternative time schedule for depletion that is commonly used in reactor analysis is burnup in units of (typically) power * time / mass. Common variants are MWd/kgHM or GWd/tHM (same units if you assume metric tonne = 1000 kg). The heavy metal mass is often taken as the initial loading, so you may see a MWd/kgHMi just to be more clear as heavy metal mass can change through operation.
This ticket proposes allowing specifying a depletion schedule with
step burnup
orcumulative burnup
units, mirroring thestep days
andcumulative days
supported in thecycles
arrays. An example input could look likewherein cycle
A
would deplete to 0.5, 1, 2, and 4 MWd/kgHM, and cycleB
would deplete out to 5, 10, 15, and 20 MWd/kgHM.It makes sense to disallow including burnup and day values in the same input, as the following would make no sense
The same power fraction and availability factors fields should still be supported as if the schedule had been specified with days.
Given the total reactor power (known from settings file) and the heavy metal mass of the reactor (through
Reactor.getHMMass()
), it is possible to convert burnup steps to day steps, and provide the day stepping that ARMI uses internally.