-
Notifications
You must be signed in to change notification settings - Fork 22
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
DT majorant #106
DT majorant #106
Conversation
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.
Thanks for the changes! They look great! I suppose that the optimisation of the unionised grid construction we wish to add for later (and open an issue about it)? Unless do you prefer to try to trim some fat in this PR?
The only major point is what we discussed earlier :-) My instinct would be to have a unionisation as a option in the nuclearDatabase configuration dictionary. Then initMajorant
would be called (or not) in the activate
procedure. Also the updateMajorantXS
would decide between old and the unionised grid search based on the allocation status of the self % majorant
.
Part of my fear is that with initMajorant
and activate
being separate functions weird things may be happening (like the example I outlined in the code attached comment) in case of some sequences of calls and we would need to add checks to protect against those situations (which is not difficult, but there is always a danger of missing some nasty, specific call sequence).
Also I might have missed something but does the unionised grid for majorant require that URR is switched off for all nuclides?
@@ -3,7 +3,8 @@ module aceNeutronDatabase_class | |||
use numPrecision | |||
use endfConstants | |||
use universalVariables | |||
use genericProcedures, only : fatalError, numToChar | |||
use genericProcedures, only : fatalError, numToChar, concatenate, quickSort, & |
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 am guilty to keep forgetting about #37. Maybe we move the import of fatalError
to use errors_mod, only : fatalError
?
@@ -60,6 +61,8 @@ module aceNeutronDatabase_class | |||
!! nuclides -> array of aceNeutronNuclides with data | |||
!! materials -> array of ceNeutronMaterials with data | |||
!! Ebounds -> array with bottom (1) and top (2) energy bound | |||
!! majorant -> unionised majorant cross section | |||
!! eGrid -> unionised energy grid |
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.
Should we change the name to make it more explicit that it is the unionised grid?
To e.g. eGridUnion
? [Don't know to be honest?]
class(aceNeutronDatabase), intent(in) :: self | ||
real(defReal), intent(in) :: E | ||
class(RNG), intent(inout) :: rand | ||
integer(shortInt) :: i, matIdx | ||
integer(shortInt) :: idx | ||
real(defReal) :: f | ||
character(100), parameter :: Here = 'updateMajorantXS (aceNeutronDatabase_class.f90)' | ||
|
||
associate (maj => cache_majorantCache(1) ) | ||
maj % E = E | ||
maj % xs = ZERO | ||
|
||
do i = 1, size(self % activeMat) | ||
matIdx = self % activeMat(i) | ||
idx = binarySearch(self % eGrid, E) | ||
|
||
! Update if needed | ||
if( cache_materialCache(matIdx) % E_tot /= E) then | ||
call self % updateTotalMatXS(E, matIdx, rand) | ||
end if | ||
if(idx <= 0) then | ||
call fatalError(Here,'Failed to find energy: '//numToChar(E)//& | ||
' in unionised majorant grid') | ||
end if | ||
|
||
associate(E_top => self % eGrid(idx + 1), E_low => self % eGrid(idx)) | ||
f = (E - E_low) / (E_top - E_low) | ||
end associate | ||
|
||
maj % xs = self % majorant(idx+1) * f + (ONE - f) * self % majorant(idx) |
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 have a fear that if it happens that the initMajorant
is not called by the user, but for some reason a call to updateMajorantXS
is made, then the code will segment on the binary search.
Similarly in the case a following calls are made:
- activate with materials 1,2,3
- init majorant
- 2nd activate (reactivate) with materials 1,2,3,4
Then it is possible that the majorant will find itself in an invalid state (not representing the expected majorant).
What we do with this will depend on what we finally decide on havinginitMajorant
as a separate procedure in the interface, but it may involve someallocated(self % eGrid)
checks to give more descriptive error message)
do i = 1, size(self % activeMat) | ||
|
||
! Get current material index and number of nuclides in that material | ||
matIdx = self % activeMat(i) | ||
nNuc = size(self % materials(matIdx) % nuclides) | ||
|
||
! Loop over nuclides present in that material | ||
do j = 1, nNuc | ||
|
||
! Get index and check if it's already been added to the grid | ||
nucIdx = self % materials(matIdx) % nuclides(j) | ||
isDone = nucMap % getOrDefault(nucIdx, NOT_PRESENT) | ||
|
||
! If it's a new nuclide, add its energy grid to the unionised one | ||
if (isDone /= IN_SET) then | ||
|
||
tmpGrid = concatenate(tmpGrid, self % nuclides(nucIdx) % eGrid) | ||
|
||
! Sort and remove duplicates | ||
call quickSort(tmpGrid) | ||
tmpGrid = removeDuplicatesSorted(tmpGrid) | ||
|
||
! Add nuclide to the set | ||
call nucMap % add(nucIdx, IN_SET) | ||
|
||
end if | ||
|
||
end do | ||
end do |
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 perhaps not at this moment but the (relatively) low hanging fruits for optimisation here would be to create a real
version of the dynArray_class
and use it as the tmpGrid
. I guess it could take care of a large portion of the runtime by avoiding a lot of reallocation.
Of course dynArray
would than also have in-place versions of removeDuplicates
and quickSort
added.
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 a bit involved, so I will just open an issue. It will be needed for actual use with burnup or so.
Even though more than 70% of the time taken is spent sorting stuff, and this is unavoidable I think.
|
||
! Precalculate majorant xs for delta tracking | ||
if (loud) print '(A)', 'Building unionised majorant cross section' |
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.
if (loud) print '(A)', 'Building unionised majorant cross section' | |
if (loud) print '(A)', 'Building MG unionised majorant cross section' |
Maybe we should highlight that it is a MG case?
I agree with Mikolaj's comments. Related to that, I think, until the majorant construction is optimised, it is desirable to be able to switch off precomputing a majorant in the input file - otherwise we might encounter a nasty case with many nuclides that takes impractically long to run just from constructing the grid which would run just fine at the moment. It might require switching off majorant construction and recompiling. |
Issue #107 is addressed here. The unionised energy grid is build gradually from the nuclides grids instead of concatenating arrays and using quicksort. Now it takes a fraction of a second to precompute everything instead of ~10s. |
NuclearData/ceNeutronData/aceDatabase/aceNeutronDatabase_class.f90
Outdated
Show resolved
Hide resolved
@@ -51,19 +52,26 @@ module aceNeutronDatabase_class | |||
!! It's possible to use probability tables in the unresolved resonance range if | |||
!! ures is included in the input file | |||
!! | |||
!! NOTE: the unionised majorant is not calculated and used if probability tables |
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 think we should open an issue on this. It will be very desirable to still have a unionised majorant, e.g., if we want to accurately run fast problems. I understand it shouldn't have a big impact for thermal problems, even with burnup.
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.
Sure, I'll open an issue. I am not sure if one can a priori find out what's the majorant when using probability tables. One approach could be to sample, say, 1000 times per each energy when calculating the majorant, and retaining the highest xs.
!! activeMat -> array of materials present in the geometry | ||
!! nucToZaid -> map to link nuclide index to zaid index | ||
!! hasUrr -> ures probability tables flag, it's false by default | ||
!! hasDBRC -> DBRC flag, it's false by default | ||
!! has majorant -> unionised majorant cross section flag |
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.
hasMajorant, not has majorant
@@ -653,6 +692,9 @@ subroutine init(self, dict, ptr, silent ) | |||
self % Ebounds(2) = min(self % Ebounds(2), self % nuclides(i) % eGrid(j)) | |||
end do | |||
|
|||
! Read unionised majorant flag | |||
call dict % getOrDefault(self % hasMajorant, 'majorant', .false.) |
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 be inclined to set this to true by default. We run DT often and it's one less flag to set in that case, because we would always want the precomputed majorant (as we used to do in MG). It's also a very small price to pay if we happen to be running ST.
@@ -196,7 +197,7 @@ end function getMaterial | |||
!! Allows to retrive an access to nuclide data for all databases types |
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.
While we're here.... retrieve*
@@ -98,7 +89,7 @@ subroutine deltaTracking(self, p, tally, thisCycle, nextCycle) | |||
if (abs(majorant_inv) > huge(majorant_inv)) call fatalError(Here, "Majorant is 0") | |||
|
|||
DTLoop:do | |||
distance = -log( p% pRNG % get() ) * majorant_inv | |||
distance = -log( p % pRNG % get() ) * majorant_inv | |||
|
|||
! Move partice in the geometry |
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.
particle*
docs/User Manual.rst
Outdated
@@ -742,7 +742,9 @@ from ACE files. | |||
resonance probability tables treatment | |||
* DBRC (*optional*, default = no DBRC): list of ZAIDs of nuclides for which DBRC has | |||
to be applied. | |||
|
|||
* majorant (*optional*, default = 0): 1 for true; 0 for false; flag to activate the |
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 above, I think this should default to true. I suppose it comes down to which would we prefer to suffer in a worst case - a slower calculation or more memory. I think the memory is more tolerable.
Yet more changes to this PR:
|
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 feel bad that my only comments are tiny typos... But this looks great! We're a good bit more respectable after this!
|
||
needsUrr = (nuc % hasProbTab .and. E >= nuc % urrE(1) .and. E <= nuc % urrE(2)) | ||
|
||
! Check if present nuclide uses URR tabes |
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.
tables*
@@ -741,14 +742,14 @@ subroutine init(self, ACE, nucIdx, database) | |||
! Create a stack of MT reactions, devide them into ones that produce 2nd-ary |
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.
divide*
@@ -741,14 +742,14 @@ subroutine init(self, ACE, nucIdx, database) | |||
! Create a stack of MT reactions, devide them into ones that produce 2nd-ary | |||
! particlues and pure absorbtion |
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.
particles*, absorption*
@@ -217,15 +219,15 @@ end function getNuclide | |||
!! | |||
!! Return a pointer to a reaction | |||
!! | |||
!! Allows to retrive an access to reaction data for all databases types | |||
!! Allows to retrieve an access to reaction data for all databases types | |||
!! Reactions can be associated either with nuclides or materials. Thus, there is ambiguity | |||
!! whether material or nuclide should be asked to provide reaction data (using matIdx or nuIdx) |
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.
nucIdx*
Well, this is the result of several iterations already! Luckily we converged :) |
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.
Looks good! Thanks for all the hard work. The URR added some complication to this one ;-)
Thanks! Yes, accounting for URR was tricky, and I did spy on Serpent for that! But interestingly the worst interpolation errors were around the S(a,b) limit energies rather than in the URR range, in the problem I tried. Hopefully now it's more robust to all of that! |
This PR adds the precalculation of the majorant xss on a unionised grid for delta tracking.
Few things to notice: