Stop depletion between each timestep
Today @paulromano and I were discussing the fact that it would be nice to be able to halt depletion calculations between time steps to examine or modify reaction rates, material compositions, etc. using the Python CAPI. Making the Integrator classes iterable seems like it would be really useful for this purpose.
For example, in an MSR model one might want to remove fission products between each step.
for result in integrator:
remove_fission_products(result)
This would be a great feature! We might already support something kind of like this through https://github.com/openmc-dev/openmc/blob/a7076138f2dba72217de60b9239bd6857251db06/openmc/deplete/abc.py#L835-L841
This is called for every step that isn't the first step in a restart calculation, so something like
class MSRIntegrator(PredictorIntegrator):
def _get_bos_data_from_operator(self, step, power, bos_conc):
conc = self._remove_fission_products(bos_conc)
return super()._get_bos_data_from_operator(step, power, conc)
Granted, this would require a new integrator for each scheme, which could get cumbersome. Maybe supplying a function that modifies the BOS concentrations?
I was also thinking it would be nice to be able to call integrate multiple times. This might involve moving some of the arguments that are currently on the constructor to integrate instead. Maybe something like:
op = openmc.deplete.Operator(...)
x = SomeIntegrator(op)
x.integrate(timesteps1, power1)
# do something interesting
...
x.integrate(timesteps2, power2)
I was thinking of updating the Integrate.__iter__ method s.t. it would yield a Results object and concentrations at each step, but I like @paulromano's suggestion for it's fine-grain control. Perhaps some combination of the two?
op = openmc.deplete.Operator(...)
x = SomeIntegrator(op)
x.add_timestep(dt, power)
for step in x:
do_something_interesting(op, step)
if continue_depletion(op, step):
x.add_timestep(dt, power)
And for a standard depletion run, something like:
op = openmc.deplete.Operator(...)
x = SomeIntegrator(op)
x.add_timesteps(dts, powers)
x.integrate_all()
Is that a little too convoluted?
At the standpoint of user, the multiple callings of integrate could be considered as follows,
op1 = openmc.deplete.Operator(...)
x1 = SomeIntegrator(op1)
x1.integrate(timesteps1, power1)
# fission product processing or even fuel shuffle
res1 = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5")
op2 = openmc.deplete.Operator(..., previous_results=res1)
x2 = SomeIntegrator(op2)
x2.integrate(timesteps2, power2)
Am I following your thoughts? May be the saving and loading results file could be removed via returning the results to integrate,
res1 = x1.integrate(timesteps1, power1)
res1_corrected = res1.someprocess()
res2 = x1.integrate(timesteps2, power2, res1_corrected)
I think supporting something like a one-step integrate and multi-step integrate_all (name TBD) could be done pretty easily. Then you could perform some actions like @pshriwise is interested in with
prev_res = None
for dt, power in zip(time_steps, powers):
prev_res = x.integrate(dt, power, prev_res=prev_res)
prev_res = do_stuff(prev_res)
Or to maintain the one-shot approach
x.integrate_all(time_steps, powers)
But just to confuse potential users, currently Integrator.integrate does all the time steps, meaning some deprecation / warning period where integrate actually does a single step is tricky
A couple things to be wary of
- Units on time step. Converting from days to seconds isn't too bad, but we also support burnup which, while not difficult, is something that
x.integratewould have to interpret. Or we say these methods only support time steps of seconds and provide some way to convert between burnup - Units on power (W, W/mass) and the new
source_ratesfor normalization. Not a difficult conversion, but something that we currently support and should probably continue to support - Final EOL transport solution. With this loop, we would have to provide ample documentation and methods (
x.solve_eol?) that just re-run a solution w/o depletion
Hey guys, does #1884 solve this (or at least the intent of it)? The crux of the ticket is we would like a way to do the following:
Integrator.integrate(...)
# do stuff
Integrator.integrate(...)
After #1884 we can do this with:
model.deplete(...)
# do stuff
model.deplete(...)
Where # do stuff can be move rods, modify materials through the C-API, whatever.
As one more option, I made a gist not long ago showing how this can be done using ResultsList.export_to_materials.