Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding methods to automatically apply results to existing Tally objects. #2671

Merged
merged 34 commits into from
Jan 24, 2025

Conversation

pshriwise
Copy link
Contributor

@pshriwise pshriwise commented Aug 28, 2023

Description

One thing I've always found a bit clunky when working with our simulation results is the process of extracting user-specified tally results from the statepoint file. It seems to me that finding a match is left to the user, but it should be possible to automate that in the majority of cases.

The idea here is that we can now apply results from our statepoint file to the original tally objects created as inputs to the simulation. This simplifies situations like:

tally = openmc.Tally()
model.tallies = [tally]
# further tally setup here

sp_file = model.run()
with openmc.StatePoint(sp_file) as sp:
    sp_tally = sp.get_tally(id=tally.id)

# do result postprocessing here with the new sp_tally object, two objects to keep track of

to

tally = openmc.Tally()
model.tallies = [tally]
# further tally setup here

sp_file = model.run()
tally.add_results(sp_file)

# only one tally object to worry about 
# do result postprocessing here with original tally object

or even

tally = openmc.Tally()
model.tallies = [tally]
# further tally setup here

sp_file = model.run(apply_tally_results=True)

# do result postprocessing here with original tally

Keeping this as a draft for now as I'm kind of workshopping it. But I'll take care of the following dev tasks soon if there aren't any objections to heading this direction.

  • I have performed a self-review of my own code
  • I have followed the style guidelines for Python source files (if applicable)
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works (if applicable)

@pshriwise pshriwise marked this pull request as draft August 28, 2023 19:15
@pshriwise pshriwise force-pushed the apply_tally_results branch from 02d7f3d to 1838375 Compare August 29, 2023 03:35
@pshriwise pshriwise force-pushed the apply_tally_results branch from 1838375 to f72b96f Compare April 3, 2024 04:31
@pshriwise pshriwise force-pushed the apply_tally_results branch from f72b96f to 54c938e Compare April 10, 2024 08:24
@pshriwise pshriwise marked this pull request as ready for review April 10, 2024 09:22
Copy link
Contributor

@MicahGale MicahGale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this feature. My one concern is that right now this PR drops coverage.

openmc/statepoint.py Outdated Show resolved Hide resolved
openmc/statepoint.py Outdated Show resolved Hide resolved
@pshriwise
Copy link
Contributor Author

After a good amount of massaging related to the Tally.estimator and Tally object equivalence I came to the following conclusion:

When the Tally.estimator attribute is None this is effectively an indication that OpenMC should select an appropriate estimator for the filters and tallies applied. (This is often the case from user tally generation code I've seen). At runtime, OpenMC will select and apply an appropriate estimator with a preference for the tracklength estimator if allowed. This estimator is written to the state point file explicitly, as it should be, but it makes matching the original, "input" Tally object to the Tally object in the StatePoint object less straightforward.

I've updated the documentation about the Tally.estimator attribute to reflect this understanding and the Tally.__eq__ method allows a match between tallies if either of the Tally.estimator attributes are set to None.

With that, I think I'm pretty happy with the state of this pending further review or something I've missed (impossible); I'll be looking forward to updating tutorials/notebooks to use this feature as I think it will make setup and post-processing in a single script/notebook more intuitive.

@MicahGale @paulromano @jtramm I'd be curious to hear your thoughts in particular!

@pshriwise pshriwise self-assigned this Sep 20, 2024
…ifferent than an explicity specified estimator
…n Tally.__eq__.

The default value of None for the `Tally.estimator` attribute allows the C++ code to provide additional warnings/errors if a user explicitly specifies an estimator that is incompatible with the tally filters/scores. When this value is None, no entry appears in the XML inputs and OpenMC is free to choose an appropriate estimator without error.
@pshriwise pshriwise force-pushed the apply_tally_results branch from 8466fb4 to 2fb7e3d Compare October 16, 2024 15:46
@MicahGale
Copy link
Contributor

I still need to pull this branch and play around with it a bit. My first inclination for the equality of None is: why use implicit defaults? If an explicit default were you used where it just defaulted to track length, then all the __eq__ logic would be unnecessary, and would be overall more intuitive for the users.

@pshriwise
Copy link
Contributor Author

pshriwise commented Oct 16, 2024

I still need to pull this branch and play around with it a bit. My first inclination for the equality of None is: why use implicit defaults? If an explicit default were you used where it just defaulted to track length, then all the __eq__ logic would be unnecessary, and would be overall more intuitive for the users.

Yeah I agree that an explicit default might be more clear in terms of intention, but I think the equivalence logic would mainly remain the same. We can't really rely on 'tracklength' as the default value because there are restrictions for estimators on certain scores/filters, but the C++ will try to respect an explicitly set estimator on the Tally object currently.

// Check if user specified estimator

Explicitly setting tracklength is going to cause a lot of problems in the test suite and a lot of this implicit default is already baked into that in one way or another. I actually tried this out earlier in this PR but it was clear that it's sort of its own mess to untangle if we want to change that behavior.

Another option is on the C++ side, the calls to fatal_error could be reduced to warnings with an update to the applied estimator as needed, but I prefer the status quo tbh.

@MicahGale
Copy link
Contributor

Sorry for the delay, yes that makes sense for now. Long term it would be nice it the default were automatically changed as needed, but that's beyond this scope.

openmc/model/model.py Outdated Show resolved Hide resolved
openmc/statepoint.py Outdated Show resolved Hide resolved
openmc/statepoint.py Outdated Show resolved Hide resolved
openmc/statepoint.py Show resolved Hide resolved
openmc/statepoint.py Show resolved Hide resolved
openmc/tallies.py Outdated Show resolved Hide resolved
openmc/tallies.py Show resolved Hide resolved
openmc/tallies.py Outdated Show resolved Hide resolved
Copy link
Contributor

@paulromano paulromano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this usability improvement @pshriwise! Definitely will make our workshops run a little smoother :) I ended up modifying the approach a bit. Rather than separating out the _populate_tally method as you had done in the StatePoint class and then using that within the Tally class, I simply updated the Tally._read_results method to get all the information it needs (number of realizations, list of nuclides) and then add_results just adds the _sp_filename attribute that is used when _read_results is called. This makes the overall diff a bit smaller.

Some things we ought to consider as part of follow-on PRs:

  • Taking that logic you had as _populate_tally and perhaps having it be a Tally.from_hdf5 classmethod.
  • Setting Tally.nuclides to be ["total"] by default (explicit is better than implicit). I could definitely sense some awkwardness around this in our logic.

@paulromano
Copy link
Contributor

@MicahGale all of your comments should be addressed at this point. Let us know if you're satisfied and if so, we'll go ahead and merge. Thanks!

@paulromano paulromano enabled auto-merge (squash) January 24, 2025 15:29
@pshriwise
Copy link
Contributor Author

Rather than separating out the _populate_tally method as you had done in the StatePoint class and then using that within the Tally class, I simply updated the Tally._read_results method to get all the information it needs (number of realizations, list of nuclides) and then add_results just adds the _sp_filename attribute that is used when _read_results is called. This makes the overall diff a bit smaller.

Nice! I like it. More succinct; fewer methods to maintain. Thanks!

@pshriwise
Copy link
Contributor Author

  • Taking that logic you had as _populate_tally and perhaps having it be a Tally.from_hdf5 classmethod.

Yeah I think that might be nice. I'll snag the last commit with that here and make an issue.

  • Setting Tally.nuclides to be ["total"] by default (explicit is better than implicit). I could definitely sense some awkwardness around this in our logic.

I like it. As @MicahGale suggested, I think we might want to do something with the estimator that's more descriptive than None ( "auto" perhaps?). I can create an issue for that as well if you're on board.

Thanks for cleaning this up!

@paulromano paulromano merged commit f207d42 into openmc-dev:develop Jan 24, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants