-
Notifications
You must be signed in to change notification settings - Fork 509
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
Restricted file source #2916
Restricted file source #2916
Conversation
it appears that before that geometry is not initialized
add an option to, if a particle is rejected, kill that one and move to next particle instead of resampling. This is less efficient, but avoids renormalization
…into restricted_file_source
also apply a bit of formatting
…into restricted_file_source
Very nice. But as for implementation, it looks like this may duplicate the other rejection code introduced in #2235. In fact there is no reason this should strictly pertain to a file source; it applies to any sort of source. This is very nice to have. Did you consider just hoisting that code into the source base class rather than separately implementing it for the file source class? Secondly, I'm not sold on the kill strategy being different from just resampling, but with some extra steps in between. Can you expand on how you thought this would bias the results? Lastly, as for the "hypercube" terminology... IMO it just adds a layer of opacity that obscures what the intention of the code is in favor of using some fun mathematical terminology. It'd be much nicer to just include |
@gridley Thanks for the comments - and yes, there is some code duplication from #2235, i.e. the Independent source. It's not entirely identical but close. The difference btw 'kill' and 'resample' ends up in normalization. If resampling you are always guaranteed that your source will emit the full source strength, albeit in a restricted phase space, which means that phase space is being oversampled. It is of course not all that difficult to downweight all your results by some factor to reflect this, but if you can live with somewhat inefficient runtime, kill is perhaps an easier option. Then the thing behaves as if you were to put some perfect neutron/photon absorber in that phase space. This is for "fixed source" runs of course. Lastly, agreed - hypercube (or in fact hyperrectangle to be precise) may be an overly fancy term. I have nothing against using something more prosaic. It may be that it is much better to have a set of coordinate specific bounds, as you suggest, or even the option of a boolean function. I did consider allowing a filter to be passed in as a restriction to piggy-back on existing infrastructure, but rejected that idea, since it requires a connection to the tally-infrastructures. I was a little worried about odd side-effects. |
correct wrong condition
instead of a full coordinate vector instead use lower_left upper_right for spatial coordinates, and energy_bounds and time_bounds for energy and time. This should be more workable
@gridley Upon rereading your comment, perhaps you are suggesting hoisting the entire piece of code into the SourceBase class? I first read it as only the domain related bit, but this may not have been what you meant? |
Hey Erik, yes, I think if it doesn't specifically pertain to sources from a file this should be in the base class. The capability added here seems to pertain to any source, so it would be nice to allow it as well for instance with a box source or similar. Now there is a slight overlap with that other rejection sampling code, but finding their intersection shouldn't be too bad. Do you see any barrier to that? |
Hi @gridley I see what you mean, and I agree. I don't see any real obstacles as such, so I'll get to it. Thanks! |
The source may now be restricted if it inherits from RestrictedSource instead of directly from Source
One more thought to add — with the generic constraints dictionary, we could also refactor the |
@paulromano I've taken a quick look - looks good to me. I really like the dictionary idea. I think this does what we need/intend. I'll run a few tests tom. but I don't expect any problems. Totally agree with having a 2nd review btw. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I've reviewed the code! Here is my overarching concern.
The Source
class should probably be 100% responsible for site rejection. We have left IndependentSource
with its own ability to reject based on if the location is in fissionable material, but that should also go into Source
if you ask me!
Also, it appears that the rejection constraints that are implemented in Source
are getting bypassed by IndependentSource
. Ideally putting in this work to generalize rejection sampling should also be enjoyed by any child classes. Moreover, child classes should not have the option to override sample
. It's a violation of the open-closed principle: we are allowing the class behavior to be modified rather than extended.
On top of that we're not clearly separating responsibilities at this point. The Source
class should be responsible for site rejection, but we've left IndependentSource
responsible for doing non-fissionable region rejections.
I know we're not hard and fast on developer documentation at this point, but going into the future it'd be nice to write a paragraph about the general structure of the Source
system in the C++ code. It right now is a little opaque that child classes should be responsible for proposing source sites, while the parent class potentially rejects them.
include/openmc/source.h
Outdated
|
||
static unique_ptr<Source> create(pugi::xml_node node); | ||
|
||
protected: | ||
// Methods that can be overridden |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is kind of extraneous. I don't think basic language features should be documented, but will leave this suggestion up to your discretion.
Instead, what is this function supposed to do? What would a base class have to implement? A comment should be there saying it I think.
include/openmc/source.h
Outdated
void check_for_constraints(pugi::xml_node node); | ||
bool satisfies_constraints(SourceSite& s) const; | ||
bool satisfies_spatial_constraints(Position r) const; | ||
bool satisfies_energy_constraints(const double E) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking values we pass in as const doesn't hurt; just as an observation I don't think there's much of anywhere else we do this.
For types longer than 8 bytes or so, such as the Position there, it might be good to pass as a const reference instead.
include/openmc/source.h
Outdated
protected: | ||
// Note that this method is not used since IndependentSource directly | ||
// overrides the sample method | ||
SourceSite sample_no_rejection(uint64_t* seed) const override { return {}; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh OK! I see what sample no rejection does. This is the proposal distribution. The first thing that came to mind for me reading "no_rejection" is that this implements some other sampling method that avoids using rejection. Instead it's the proposal distribution inside a rejection loop. I'd suggest renaming to "sample_proposal" or similar.
This should definitely be documented in the base class with a brief explainer on how it interacts with child classes.
include/openmc/source.h
Outdated
protected: | ||
// Note that this method is not used since IndependentSource directly | ||
// overrides the sample method | ||
SourceSite sample_no_rejection(uint64_t* seed) const override { return {}; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moreover, I find it funny that we've taken the base class route here, so ideally IndependentSource
should NOT override sample
. That way it retains the ability to do source site rejection.
It appears to me that this approach does not allow source site rejection with IndependentSource
. That's no good! The sample
method in the base class should NOT be virtual. Rather, base classes should only fill out the proposal distribution. This way we guarantee rejection functionality on any source class.
src/source.cpp
Outdated
} | ||
} | ||
} | ||
// If at this point found is false, part. is outside all cells/materials |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not abbreviate particle
. I was thinking this was a typo period and referring to a CAD part!
src/source.cpp
Outdated
// Now search to see if location exists in geometry | ||
found = exhaustive_find_cell(p); | ||
// Check if sampled position satisfies spatial constraints | ||
found = satisfies_spatial_constraints(site.r); | ||
|
||
// Check if spatial site is in fissionable material |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this fissionable check to the outer Source
code. I really deeply believe that's the right thing to do here.
Why should an IndependentSource
enjoy fissionable rejection but not a FileSource
?
src/source.cpp
Outdated
|
||
// Sample position and apply rejection on spatial domains | ||
Position r; | ||
while (true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than a while/break
, I'd suggest hearkening back to our Fortran roots here with the do/while
construct more commonly used in that language.
src/source.cpp
Outdated
Position r; | ||
while (true) { | ||
r = space_->mesh()->sample_element(element, seed); | ||
if (this->satisfies_spatial_constraints(r)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see why you'd want to separate this out as its own function now. It's nice to pre-screen out some rejection sites before passing back to the Source
class's rejection loop.
// Replace spatial position with the one already sampled | ||
site.r = mesh_location.second; | ||
SourceSite site; | ||
while (true) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again do/while
is a natural, beautiful way to handle this type of loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, but when I tried this, clang-format put the closing semicolon on a line by itself, which looks both ugly and confusing to me. Sooo, I'm going to leave this one as is (I will use do/while
above though)
@gridley Thanks for the detailed comments. I pretty much agree with everything you've said. Ideally, the derived classes should not override the
If you have ideas for how to address those two points while having |
Hey Paul! OK, yes, that makes sense now. I agree that it'd be a bummer to not take advantage of some efficiency considerations in rejection in favor of the most separated responsibility code design. Regardless, I think you could still make So are you saying the objective in mesh source sampling is to preserve the element sampling probabilities even after applying a rejection filter? I am not 100% understanding that one right now. I would have guessed it to be natural to have different element sampling probabilities in the end after applying a rejection filter, but I can imagine there are applications where it's desirable to preserve these after applying rejection. |
@gridley I've updated this branch in response to all your excellent comments. Notably, the interaction between the I've also added a generic "fissionable" constraint that can be used with any source class and that replaces the Let me know if you see any further issues! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how you handled potentially mixed constraint handling! It looks 99% of the way good to go but I have a few small questions before approving.
accepted = true; | ||
site.wgt = 0.0; | ||
if (!accepted) { | ||
++n_reject; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is not thread safe use of a static variable, although the probability of two threads simultaneously hitting it is low. I suppose that having these be 32 bit types means you'll never get unreasonable values. Maybe the counters should be confined to a single thread? I know it doesn't influence correctness but worth a little thought.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also had this realization looking through the code and thought about how much we should care about it (the same thing applies in the existing logic in IndependentSource
). The important thing is to trigger the condition when too many sites are getting rejected; even if there is a race condition on incrementing the number of accepts/rejects, that will still happen, just in a semi-fickle way. I was going to defer fixing it for now but if you think we should take care of it as part of this PR, I'm happy to do so. Let me know how I should proceed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! I'm just going to raise a separate issue to handle it.
Just wanted to say - thanks guys for all the work you've put in on this one. Much appreciated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but maybe we can #include <cassert>
and add assert
on to the pointers we're getting from dynamic casts? cassert
is a small header, so there's no tangible overhead from adding it. Alternatively dynamic casting to a reference rather than pointer would suffice.
It's better than the GSL Expects
approach because they're removed in any compile that's not debug:
https://stackoverflow.com/questions/34302265/does-cmake-build-type-release-imply-dndebug
Co-authored-by: church89 <l.chierici89@gmail.com> Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
Description
This PR adds functionality to spatially restrict emission of particles from a FileSource based on either domains (much like IndependentSource) or on the definition of a hypercube (strictly a hyperrectangle) in spatial, directional, energy, and time dimensions. This can for instance be useful when the particle-file was created by an external tool and some particles fall outside some important boundary in the problem.
Two rejection schemes are implemented:
I have not yet added regression tests.
Fixes # (issue)
None defined.
Checklist