-
Notifications
You must be signed in to change notification settings - Fork 525
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
Resonace Covariance Module #1024
Conversation
…as previous range
Addressed the comments by @paulromano. Added in warning on the sampling method that it can produce nonphysical negative parameters which may cause reconstruction to fail. This is because the covariance matrix assumes a multivariate normal distribution from which negative numbers may be drawn. Further discussion and possible solutions to this issue can be found in a Rochman paper here. |
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.
Here are a few more comments on the PR. I would also recommend running pycodestyle on your resonance_covariance.py to address style issues.
openmc/data/endf.py
Outdated
@@ -71,6 +71,24 @@ def float_endf(s): | |||
return float(_ENDF_FLOAT_RE.sub(r'\1e\2', s)) | |||
|
|||
|
|||
def int_endf(s): | |||
"""Conver string to int. Used for INTG records where blank entries |
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.
Typo: convert
openmc/data/endf.py
Outdated
@@ -71,6 +71,24 @@ def float_endf(s): | |||
return float(_ENDF_FLOAT_RE.sub(r'\1e\2', s)) | |||
|
|||
|
|||
def int_endf(s): |
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.
Change to _int_endf
since it is unlikely a user would ever need this
openmc/data/endf.py
Outdated
@@ -249,6 +267,50 @@ def get_tab2_record(file_obj): | |||
|
|||
return params, Tabulated2D(breakpoints, interpolation) | |||
|
|||
def get_intg_record(file_obj): |
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.
Add another blank line before this function (PEP8: 2 lines between functions/classes at top level)
openmc/data/endf.py
Outdated
corr = corr + corr.T - np.diag(corr.diagonal()) | ||
return corr | ||
|
||
|
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.
delete one blank line here
openmc/data/resonance_covariance.py
Outdated
self.cov_subset = cov_subset | ||
|
||
def sample_resonance_parameters(self, n_samples, resonances, use_subset=False): | ||
"""Return a list size 'n_samples' of openmc.data.ResonanceRange objects. |
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 would say something more like "Sample resonance parameters based on the covariances." The type of the object returned (and its length) are described in the "Returns" section so probably not necessary to mention them here too.
openmc/data/resonance_covariance.py
Outdated
res_range.parameters = sample_params | ||
samples.append(res_range) | ||
|
||
self.samples = samples |
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.
As we discussed before, the samples
object should be returned directly rather than being set as an attribute of the current instance.
openmc/data/resonance_covariance.py
Outdated
cov_subset[tri_indices] = cov_subset_vals | ||
|
||
self.parameters_subset = parameters_subset | ||
self.cov_subset = cov_subset |
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.
As with the sampling method, this method should return an object rather than setting attributes of the current instance. I think what would make the most sense is to return a new instance of ResonanceCovarianceRange
with the specified subset of parameters/covariance matrix. This would allow you to get rid of the use_subset
argument on the sampling method since calling sample_resonance_parameters
on the new instance would use the subset by default.
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The covariance module also has the ability to sample a new set of parameters using the covariance matrix. Currently the sampling uses np.multivariate_normal(). Because parameters are assumed to have a multivariate normal distribution this method doesn't not currently guarantee that sampled parameters will be positive." |
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.
np.multivariate_normal -> numpy.multivariate_normal
openmc/data/resonance_covariance.py
Outdated
items : list | ||
Items from the CONT record at the start of the resonance range | ||
subsection | ||
file2params : openmc.data.ResonanceRange object |
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 looks like this is still actually taking a DataFrame of resonance parameters rather than the ResonanceRange
object that owns it. I think it would be better to use the ResonanceRange
object directly and store it as an attribute of the ResonanceCovarianceRange
instance. That way, when sample_resonance_parameters
is called, there would be no need to pass the parameters as they could be retrieved from the attribute.
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.
Same comment applies to ReichMooreCovariance
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The sampling routine requires the incorpotation of the `openmc.data.ResonanceRange` for the same resonance range object. This allows each sample itself to be its own `openmc.data.ResonanceRange` with a new set of parameters. Looking at some of the sampled parameters below:" |
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.
Typo: incorpotation
openmc/data/resonance_covariance.py
Outdated
parameters = self.parameters | ||
cov = self.covariance | ||
# Copy ResonanceRange object | ||
res_range = copy.copy(self.file2res) |
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.
Right now, you're creating a single copy of the resonance range. If multiple samples are requested, this method actually returns a list with the same object repeated N times (this is evident in the Jupyter notebook where you can see that two different samples have the same parameters). You should be creating N copies of the original object and then setting the parameters appropriately for each one.
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.
Saw similar code being used for each formalism/mpar so thought I could pull it out front but guess copy.copy() and .append() behaved differently than I thought. Should be better now.
openmc/data/resonance_covariance.py
Outdated
'captureWidth', 'fissionWidth', 'competitiveWidth'] | ||
sample_params = pd.DataFrame.from_records(records, | ||
columns=columns) | ||
res_range.parameters = sample_params |
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.
Related to the previous comment, this line overwrites the parameters on each iteration of the for loop. (also, I don't see a similar line updating the parameters for the mpar=4/5 and the RM cases)
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, hopefully this is the last round!
openmc/data/resonance_covariance.py
Outdated
@@ -0,0 +1,743 @@ | |||
from collections import defaultdict, MutableSequence, Iterable |
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.
Remove defaultdict, Iterable (not used)
openmc/data/resonance_covariance.py
Outdated
mpar : int | ||
Number of parameters in covariance matrix for each individual resonance | ||
formalism : str | ||
String descriptor of formalism |
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.
Need to add description of file2res here (since it is assumed to be present for any subclass)
openmc/data/resonance_covariance.py
Outdated
self.energy_min = energy_min | ||
self.energy_max = energy_max | ||
|
||
def res_subset(self, parameter_str, bounds): |
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.
What do you think about just naming this subset
? The res
seems kind of superfluous to me.
openmc/data/resonance_covariance.py
Outdated
gn = sample[1::mpar] | ||
gg = sample[2::mpar] | ||
gfa = sample[3::mpar] if mpar > 3 else parameters['fissionWidthA'].values | ||
gfb = sample[3::mpar] if mpar > 3 else parameters['fissionWidthB'].values |
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.
4::mpar
openmc/data/resonance_covariance.py
Outdated
|
||
# Add parameters from File 2 | ||
parameters = _add_file2_contributions(parameters, | ||
resonance.parameters) |
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 looks like the last few lines here (starting with columns = ...
) are the same for each branch of the if -- I think those could be moved out of the if altogether (so that they're not repeated). The only thing that's different is in the lcomp == 1 case, mpar
is not set, but that looks like a bug since it is used in the call below.
openmc/data/resonance_covariance.py
Outdated
|
||
# Add parameters from File 2 | ||
parameters = _add_file2_contributions(parameters, | ||
resonance.parameters) |
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.
Same comment here -- the lines starting from columns = ...
looks redundant on the branches of the if.
@@ -39,7 +39,7 @@ def sm150(): | |||
def gd154(): | |||
"""Gd154 ENDF data (contains Reich Moore resonance range)""" | |||
filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-064_Gd_154.endf') | |||
return openmc.data.IncidentNeutron.from_endf(filename) | |||
return openmc.data.IncidentNeutron.from_endf(filename, covariance = 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.
covariance=True
openmc/data/resonance_covariance.py
Outdated
res_cov_range.covariance = cov_subset | ||
return res_cov_range | ||
|
||
def sample_resonance_parameters(self, n_samples): |
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.
What do you think about naming this simply sample()
?
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.
Makes sense to me. Wasn't sure how descriptive the names needed to be but as with res_subset
I think that having them as methods of the objects provides enough context
It looks like the build is failing with Python 3.4 on the following test. |
This PR incorporates into the API the ability to parse resonance covariance data from ENDF File 32. Its intended functionality can be seen in the new example notebook: openmc/examples/jupyter/nuclear-data-resonance-covariance.ipynb
Namely:
My first PR so comments on style encouraged!