# <<BEGIN-copyright>>
# <<END-copyright>>
"""
Store and report warnings and errors in GNDS data, in order to discover problems in the library.
The reactionSuite.check() function returns a nested list of warning objects:
>>> warnings = reactionSuite.check()
>>> print warnings
May include or exclude specific classes of warning using the filter command.
filter() returns a new context instance:
>>> warnings2 = warnings.filter( exclude=[warning.energyImbalance] )
Or, for easier searching you may wish to flatten the list (to get warnings alone without context messages):
>>> flat = warnings.flatten()
cmattoon, Jan 2012
"""
__metaclass__ = type
[docs]class context:
"""
Store warnings in context. This class contains location information (reactionSuite, reaction, etc)
plus a nested list of warnings or other context instances
"""
def __init__(self, message='', warningList=None):
self.message = message
self.warningList = warningList or []
def __len__(self):
return len( self.warningList )
def __getitem__(self, idx):
return self.warningList[idx]
def __str__(self):
if len(self.warningList)==0:
return self.message + ": no problems encountered"
return '\n'.join( self.toStringList() )
def __eq__(self, other):
return self.message == other.message and self.warningList == other.warningList
[docs] def filter(self, include=None, exclude=None):
"""Filter warning list to only include (or exclude) specific classes of warning. For example:
>>> newWarnings = warnings.filter( exclude=[warning.energyImbalance, warning.Q_mismatch] )
Note that if both 'include' and 'exclude' lists are provided, exclude is ignored."""
if include is None and exclude is None: return self
newWarningList = []
for warning in self.warningList:
if isinstance( warning, context ):
newContext = warning.filter( include, exclude )
if newContext: newWarningList.append( newContext )
elif include is not None:
if warning.__class__ in include:
newWarningList.append( warning )
else: # exclude is not None:
if warning.__class__ not in exclude:
newWarningList.append( warning )
return context( self.message, newWarningList )
[docs] def flatten(self):
"""From a nested hierarchy of warnings, get back a flat list for easier searching:
>>> w = reactionSuite.check()
>>> warningList = w.flatten()"""
List = []
for val in self.warningList:
if isinstance(val, warning):
List.append(val)
else:
List += val.flatten()
return List
[docs] def toStringList( self, indent='', dIndent=' ' ):
""" Format warnings for printing. Returns a list of warning strings with indentation. """
s = ['%s%s' % (indent, self.message)]
for warning in self.warningList:
s += warning.toStringList( indent+dIndent )
return s
[docs]class warning( BaseException ):
"""
General warning class. Contains link to problem object,
xpath in case the object leaves memory,
and information about the warning or error.
"""
def __init__(self, obj=None):
self.obj = obj
self.xpath = ''
if hasattr( obj, 'toXLink' ):
self.xpath = obj.toXLink()
def __str__(self):
return "Generic warning for %s" % self.xpath
def __eq__(self, other):
return self.xpath == other.xpath
[docs] def toStringList( self, indent='' ):
return ['%sWARNING: %s' % (indent, self)]
#
# specific warning classes:
#
[docs]class NotImplemented( warning ):
def __init__(self, form, obj=None):
warning.__init__(self, obj)
self.form = form
def __str__(self):
return "Checking not yet implemented for %s type data" % self.form
def __eq__(self, other):
return (self.form == other.form and self.xpath == other.xpath)
[docs]class UnorthodoxParticleNotImplemented( warning):
def __init__(self, particleId, obj=None):
warning.__init__(self, obj)
self.particleId = particleId
def __str__(self):
return "Checking not yet implemented for unorthodox particle '%s'" % self.particleId
def __eq__(self, other):
return (self.particleId == other.particleId)
# resonance region:
[docs]class badScatteringRadius( warning ):
def __init__(self, factor=3.0, gotAP=None, expectedAP=None, L=None, E=None ):
self.factor=factor
self.gotAP=gotAP
self.expectedAP=expectedAP
self.L=L
self.E=E
def __str__(self):
direction = 'high'
if self.gotAP<self.expectedAP: direction = 'low'
s="Scattering radius (%s) at least a factor of %s too %s compared to expectations (%s)"%(map(str,(self.gotAP,self.factor,direction,self.expectedAP)))
if self.L is not None: s+=" for L=%i"%self.L
if self.E is not None: s+=" for E=%s"%str(self.E)
return s
[docs]class badSpinStatisticalWeights( warning ):
def __init__(self, L, gJ, expectedgJ, reaction=None):
self.L=L
self.gJ=gJ
self.expectedgJ=expectedgJ
self.reaction=reaction
def __str__(self):
direction = 'many'
if self.gJ<self.expectedgJ: direction = 'few'
s="The spin statical weights for L=%i sums to %s, but should sum to %s. You have too %s channels "%(self.L, str(self.gJ), str(self.expectedgJ), direction)
if self.reaction is not None:
s+='for reaction '+self.reaction
return s
[docs]class invalidAngularMomentaCombination( warning ):
def __init__(self,L,S,J,name):
self.L=L
self.S=S
self.J=J
self.name=name
def __str__(self):
return 'Invalid angular momenta combination: cannot couple L = %s and S = %s up to J = %s for channel "%s"' % (str(self.L),str(self.S),str(self.J),self.name)
[docs]class invalidParity( warning ):
def __init__(self,L,S,J,Pi,name):
self.L=L
self.S=S
self.J=J
self.Pi=Pi
self.name=name
def __str__(self):
return 'Invalid parity %s for channel "%s" with L=%s, S=%s, J=%s' % (str(self.Pi),self.name,str(self.L),str(self.S),str(self.J))
[docs]class unknownSpinParity( warning):
def __init__(self,reaction):
self.reaction = reaction
def __str__(self):
return "Could not determine product spin/parities for reaction '%s'" % self.reaction
[docs]class missingResonanceChannel( warning ):
def __init__(self,L,S,J,name):
self.L=L
self.S=S
self.J=J
self.name=name
def __str__(self):
return 'Missing a channel with angular momenta combination L = %s, S = %s and J = %s for "%s"' % (
self.L, self.S, self.J, self.name)
[docs]class invalidSpinCombination( warning ):
def __init__(self,Ia,Ib,S,name):
self.Ia=Ia
self.Ib=Ib
self.S=S
self.name=name
def __str__(self):
return 'Invalid spin combination: cannot couple Ia = %s and Ib = %s up to S = %s for channel "%s"' % (str(self.Ia),str(self.Ib),str(self.S),self.name)
[docs]class potentialScatteringNotConverged( warning ):
def __init__(self, L, E, fom, fomTarget):
self.L=L
self.E=E
self.fom=fom
self.fomTarget=fomTarget
def __str__(self):
s="Potential scattering hasn't converged by L=%i at E=%s eV, xs[%i]/xs[0]=%s > %s"%\
(self.L,str(self.E),self.L,str(100.*self.fom)+'%',str(100.*self.fomTarget)+'%')
return s
[docs]class RRmultipleRegions( warning ):
def __init__(self, obj=None):
warning.__init__(self, obj)
def __str__(self):
return "Use of more than one resolved resonance regions is deprecated"
[docs]class URRdomainMismatch( warning ):
def __init__(self, Lval, Jval, obj=None):
warning.__init__(self, obj)
self.Lval = Lval
self.Jval = Jval
def __str__(self):
return "Unresolved L=%i / J=%.1f widths don't span URR energy limits" % (self.Lval, self.Jval)
def __eq__(self, other):
return (self.Lval == other.Lval and self.Jval == other.Jval)
[docs]class URRinsufficientEnergyGrid( warning ):
def __init__(self, Lval, Jval, eLow, eHigh, obj=None):
warning.__init__(self, obj)
self.Lval = Lval
self.Jval = Jval
self.eLow = eLow
self.eHigh = eHigh
def __str__(self):
return "More points needed in L=%i J=%.1f unresolved widths between %s and %s" % (self.Lval, self.Jval, self.eLow, self.eHigh)
def __eq__(self, other):
return (self.Lval == other.Lval and self.Jval == other.Jval and self.eLow == other.eLow and self.eHigh == other.eHigh)
[docs]class unresolvedLink( warning ):
def __init__(self,link):
self.link=link
def __str__(self):
return "Unresolved link to %s" % (str(self.link))
# reaction objects:
[docs]class WicksLimitError( warning ):
def __init__(self, percentErr, energy_in, obj=None):
warning.__init__(self, obj)
self.percentErr = percentErr
self.energy_in = energy_in
def __str__(self):
return "Wick's limit too low by %.3f%% at %s" % (100*self.percentErr, self.energy_in)
def __eq__(self, other):
return (self.xpath == other.xpath and self.percentErr == other.percentErr and self.energy_in == other.energy_in)
[docs]class ZAbalanceWarning( warning ):
def __str__(self):
return "ZA doesn't balance for this reaction!"
[docs]class Q_mismatch( warning ):
def __init__(self, Qcalc, Qactual, obj=None):
warning.__init__(self, obj)
self.Qcalc = Qcalc
self.Qactual = Qactual
def __str__(self):
return "Calculated and tabulated Q-values disagree: %s vs %s!" % (self.Qcalc, self.Qactual)
def __eq__(self, other):
return (self.xpath == other.xpath and self.Qcalc == other.Qcalc and self.Qactual == other.Qactual)
[docs]class badFissionEnergyRelease( warning ):
def __init__(self, worstCase, obj=None):
warning.__init__(self, obj)
self.worstCase = worstCase
def __str__(self):
return "Fission energy release seems unphysical. Worst case: %s" % (self.worstCase)
def __eq__(self, other):
return (self.xpath == other.xpath and self.worstCase == other.worstCase)
[docs]class threshold_mismatch( warning ):
def __init__(self, threshold, thresholdCalc, obj=None):
warning.__init__(self, obj)
self.threshold = threshold
self.thresholdCalc = thresholdCalc
def __str__(self):
return "Calculated and tabulated thresholds disagree: %s vs %s!" % (self.thresholdCalc, self.threshold)
def __eq__(self, other):
return (self.xpath == other.xpath and self.threshold == other.threshold
and self.thresholdCalc == other.thresholdCalc)
[docs]class Coulomb_threshold_mismatch( threshold_mismatch ):
def __str__(self):
return "Tabulated threshold %s below calculated threshold %s!" % (self.threshold, self.thresholdCalc)
[docs]class nonZero_crossSection_at_threshold( warning ):
def __init__(self, value, obj=None):
warning.__init__(self, obj)
self.value = value
def __str__(self):
return "First cross section point for threshold reaction should be 0, not %s" % self.value
def __eq__(self, other):
return (self.xpath == other.xpath and self.value == other.value)
[docs]class gapInCrossSection( warning ):
def __init__(self, minGapEnergy, maxGapEnergy, obj=None):
warning.__init__(self, obj)
self.minGapEnergy = minGapEnergy
self.maxGapEnergy = maxGapEnergy
def __str__(self):
return "Gap in cross section data from %s to %s" % (self.minGapEnergy, self.maxGapEnergy)
def __eq__(self, other):
return (self.xpath == other.xpath and self.minGapEnergy == other.minGapEnergy
and self.maxGapEnergy == other.maxGapEnergy)
[docs]class negativeCrossSection( warning ):
def __init__(self, energy_in, index, obj=None):
warning.__init__(self, obj)
self.energy_in = energy_in
self.index = index
def __str__(self):
return "Negative cross section encountered at %s (index %i)!" % (self.energy_in, self.index)
def __eq__(self, other):
return (self.xpath == other.xpath and self.energy_in == other.energy_in
and self.index == other.index)
[docs]class badCrossSectionReference( warning ):
def __str__(self):
return "Cross section reference outside of current reactionSuite not allowed!"
[docs]class summedCrossSectionMismatch( warning ):
def __init__(self, maxPercentDiff, obj=None):
warning.__init__(self, obj)
self.maxPercentDiff = maxPercentDiff
def __str__(self):
msg = ("Cross section does not match sum of linked reaction cross sections! Max diff: %.2f%%" %
self.maxPercentDiff)
return msg
def __eq__(self, other):
return (self.xpath == other.xpath and self.maxPercentDiff == other.maxPercentDiff)
[docs]class summedCrossSectionDomainMismatch( warning ):
def __str__(self):
return "Cross section domain does not match domain of linked reaction cross section sum"
[docs]class summedCrossSectionZeroDivision( warning ):
def __str__(self):
return "Divide by 0 error when comparing summed cross section to components!"
[docs]class summedMultiplicityMismatch( warning ):
def __init__(self, maxPercentDiff, obj=None):
warning.__init__(self, obj)
self.maxPercentDiff = maxPercentDiff
def __str__(self):
msg = ("Multiplicity does not match sum of linked product multiplicities! Max diff: %.2f%%" %
self.maxPercentDiff)
return msg
def __eq__(self, other):
return (self.xpath == other.xpath and self.maxPercentDiff == other.maxPercentDiff)
[docs]class summedMultiplicityDomainMismatch( warning ):
def __str__(self):
return "Multiplicity domain does not match domain of linked product multiplicity sum"
[docs]class summedMultiplicityZeroDivision( warning ):
def __str__(self):
return "Divide by 0 error when comparing summed multiplicity to components!"
[docs]class negativeMultiplicity( warning ):
def __init__(self, value, obj=None):
warning.__init__(self, obj)
self.value = value
def __str__(self):
return "Encountered negative multiplicity (%s)!" % self.value
def __eq__(self, other):
return (self.xpath == other.xpath and self.value == other.value)
[docs]class domain_mismatch( warning ):
def __init__(self, lowBound, highBound, xscLowBound, xscHighBound, obj=None):
warning.__init__(self, obj)
self.lowBound, self.highBound = lowBound, highBound
self.xscLowBound, self.xscHighBound = xscLowBound, xscHighBound
def __str__(self):
return "Domain doesn't match the cross section domain: (%s -> %s) vs (%s -> %s)" % (self.lowBound,
self.highBound, self.xscLowBound, self.xscHighBound)
def __eq__(self, other):
return (self.xpath == other.xpath and self.lowBound == other.lowBound
and self.highBound == other.highBound)
[docs]class missingDistribution( warning ):
def __init__(self, productName, obj=None):
warning.__init__(self, obj)
self.productName = productName
def __str__(self):
return "Missing distribution (required for all '%s' products)!" % self.productName
def __eq__(self, other):
return (self.xpath == other.xpath and self.productName == other.productName)
""" eliminate this one?
class noDistributions( warning ):
# more serious than missingDistributions:
def __str__(self):
return "No distribution data available for *any* of this reaction's products!"
"""
[docs]class missingRecoilDistribution( warning ):
def __str__(self):
return "Recoil distribution type specified, but recoil partner has unsupported distribution type!"
[docs]class wrongDistributionComponent( warning ):
def __init__(self, component, reactionType='2-body', obj=None):
warning.__init__(self, obj)
self.component = component
self.reactionType = reactionType
def __str__(self):
return "%s is not a valid distribution component for %s reaction" % (self.component, self.reactionType)
def __eq__(self, other):
return (self.xpath == other.xpath and self.component == other.component
and self.reactionType == other.reactionType)
[docs]class wrong2BodyFrame( warning ):
def __str__(self):
return ("2-body reaction not in center-of-mass frame!")
[docs]class flatIncidentEnergyInterpolation( warning ):
def __str__(self):
return ("For distributions, flat interpolation along incident energy is unphysical!")
[docs]class energyDistributionBadU( warning ):
def __str__(self):
return ("For energy distribution functional form, parameter 'U' results in negative outgoing energies!")
[docs]class negativeDiscreteGammaEnergy( warning ):
def __str__(self):
return ("Discrete gamma energy <= 0")
[docs]class MadlandNixBadParameters( warning ):
def __init__(self, EFL, EFH, minTm, obj=None):
warning.__init__(self, obj)
self.EFL = EFL
self.EFH = EFH
self.minTm = minTm
def __str__(self):
return ( "Madland-Nix fission spectrum contains negative parameters: EFL=%s, EFH=%s, min(Tm)=%s"
% (self.EFL, self.EFH, self.minTm) )
def __eq__(self, other):
return (self.xpath == other.xpath and self.EFL == other.EFL
and self.EFH == other.EFH and self.minTm == other.minTm)
[docs]class weightsDontSumToOne( warning ):
def __str__(self):
return ("Weights don't sum to 1.0!")
[docs]class unnormalizedDistribution( warning ):
def __init__(self, energy_in, index, integral, obj=None):
warning.__init__(self, obj)
self.energy_in = energy_in
self.index = index
self.integral = integral
def __str__(self):
return "Unnormalized distribution! At energy_in = %s (index %i), integral = %s" % (self.energy_in,
self.index, self.integral)
def __eq__(self, other):
return (self.xpath == other.xpath and self.energy_in == other.energy_in
and self.index == other.index and self.integral == other.integral)
[docs]class unnormalizedKMDistribution( unnormalizedDistribution ):
pass # identical to unnormalizedDistribution, except it needs a special fix() function
[docs]class incompleteDistribution( warning ):
def __init__(self, energy_in, lowerMu, upperMu, obj=None):
warning.__init__(self, obj)
self.energy_in = energy_in
self.lowerMu = lowerMu
self.upperMu = upperMu
def __str__(self):
return ("Incomplete angular coverage (mu = %.2f to %.2f) for distribution at incident energy %s"
% (self.lowerMu, self.upperMu, self.energy_in))
def __eq__(self, other):
return (self.xpath == other.xpath and self.energy_in == other.energy_in
and self.lowerMu == other.lowerMu and self.upperMu == other.upperMu)
[docs]class negativeProbability( warning ):
def __init__(self, energy_in, energy_out=None, mu=None, value=None, obj=None):
warning.__init__(self, obj)
self.energy_in = energy_in
self.energy_out = energy_out
self.mu = mu
self.value = value
def __str__(self):
msg = "Negative probabilities encountered. Incident energy: %s" % self.energy_in
if self.mu is not None: msg += ", mu: %s" % self.mu
if self.energy_out is not None: msg += ", outgoing energy: %s" % self.energy_out
if self.value is not None: msg += ", worst case: %s" % self.value
return msg
def __eq__(self, other):
return (self.xpath == other.xpath and self.energy_in == other.energy_in
and self.energy_out == other.energy_out and self.mu == other.mu)
[docs]class missingCoulombIdenticalParticlesFlag( warning ):
def __init__(self, obj=None):
warning.__init__(self, obj)
def __str__(self):
return "Need 'identicalParticles=\"true\"' when target==projectile"
[docs]class incorrectCoulombIdenticalParticlesFlag( warning ):
def __init__(self, projectile, target, obj=None):
warning.__init__(self, obj)
self.projectile = projectile
self.target = target
def __str__(self):
return "doubleDifferentialCrossSection claims that target (%s) & projectile (%s) are identical!"\
% (self.target, self.projectile)
def __eq__(self, other):
return (self.target == other.target and self.projectile == other.projectile)
[docs]class energyImbalance( warning ):
def __init__(self, energy_in, index, availableEnergy, deposition_per_product, obj=None):
warning.__init__(self, obj)
self.energy_in = energy_in
self.index = index
self.availableEnergy = availableEnergy
self.deposition_per_product = deposition_per_product
self.total_deposited = sum( [val[1] for val in deposition_per_product] )
if self.availableEnergy == 0: self.total_deposited = float('inf')
def __str__(self):
per_product = ', '.join(["%s = %.4g%%" % (key,val) for key,val in self.deposition_per_product[:5]])
if len( self.deposition_per_product ) > 5: per_product += ', ...'
return ("Energy imbalance at incident energy %s (index %i). Total deposited = %.4g%% (%s)" %
(self.energy_in, self.index, self.total_deposited, per_product))
def __eq__(self, other):
return (self.xpath == other.xpath and self.energy_in == other.energy_in
and self.index == other.index and self.availableEnergy == other.availableEnergy
and self.deposition_per_product == other.deposition_per_product
and self.total_deposited == other.total_deposited)
[docs]class fissionEnergyImbalance( energyImbalance ):
def __str__(self):
per_product = ', '.join(["%s = %.4g%%" % (key,val) for key,val in self.deposition_per_product[:5]])
if len( self.deposition_per_product ) > 5: per_product += ', ...'
return ("Fission energy imbalance at incident energy %s (index %i). Total deposited = %.4g%% (%s), leaving insufficient energy for fission products!" %
(self.energy_in, self.index, self.total_deposited, per_product))
[docs]class valueOutOfRange( warning ):
def __init__(self, contextMessage, value, lowerBound, upperBound, obj=None):
warning.__init__(self, obj)
self.contextMessage = contextMessage
self.value = value
self.lowerBound = lowerBound
self.upperBound = upperBound
def __str__(self):
return ("%s. Value=%s, should be in range %s -> %s" % (self.contextMessage, self.value,
self.lowerBound, self.upperBound) )
def __eq__(self, other):
return (self.xpath == other.xpath and self.contextMessage == other.contextMessage
and self.value == other.value and self.lowerBound == other.lowerBound
and self.upperBound == other.upperBound)
[docs]class testSkipped( warning ):
""" indicate if test was skipped due to missing information """
def __init__(self, testName, reason, obj=None):
warning.__init__(self, obj)
self.testName = testName
self.reason = reason
def __str__(self):
return ("Skipped test %s: %s" % (self.testName, self.reason))
def __eq__(self, other):
return (self.xpath == other.xpath and self.testName == other.testName and self.reason == other.reason)
[docs]class ExceptionRaised( warning ):
""" if we run into an exception when running check(), try to exit gracefully and return this warning """
def __init__(self, Exception_String, obj=None):
warning.__init__(self, obj)
self.Exception_String = Exception_String
def __str__(self):
return ("Encountered Exception: %s" % self.Exception_String)
def __eq__(self, other):
return (self.xpath == other.xpath and self.Exception_String == other.Exception_String)
[docs]class EnergyDepositionExceptionRaised( ExceptionRaised ):
def __str__(self):
return ("Exception raised when calculating energy deposition: %s" % self.Exception_String)
[docs]class SkippedCoulombElasticEnergyDeposition( warning ):
def __str__(self):
return ("Energy/momentum deposition cannot be computed for charged-particle elastic")
### covarianceSuite warnings: ###
[docs]class cyclicDependency( warning ):
def __init__(self, cycle, obj=None):
warning.__init__(self, obj)
self.cycle = tuple(cycle)
def __str__(self):
if len(self.cycle) == 2:
return ("Cyclic dependency in summed covariances for sections %s and %s" % self.cycle)
else:
return ("Cyclic dependency in summed covariances for section %s" % self.cycle)
def __eq__(self, other):
return (self.xpath == other.xpath and self.cycle == other.cycle)
[docs]class varianceTooSmall( warning ):
def __init__(self, index, variance, obj=None):
warning.__init__(self, obj)
self.index = index
self.variance = variance
def __str__(self):
return ("Encountered very small variance (%e%%) at index %i." % (self.variance, self.index))
def __eq__(self, other):
return (self.xpath == other.xpath and self.index == other.index
and self.variance == other.variance)
[docs]class negativeEigenvalues( warning ):
def __init__(self, negativeCount, worstCase, obj=None):
warning.__init__(self, obj)
self.negativeCount = negativeCount
self.worstCase = worstCase
def __str__(self):
return ("%i negative eigenvalues! Worst case = %e" % (self.negativeCount, self.worstCase) )
def __eq__(self, other):
return (self.xpath == other.xpath and self.negativeCount == other.negativeCount
and self.worstCase == other.worstCase)
[docs]class badEigenvalueRatio( warning ):
def __init__(self, ratio, obj=None):
warning.__init__(self, obj)
self.ratio = ratio
def __str__(self):
return ("Ratio of smallest/largest eigenvalue (%e) is too small" % (self.ratio) )
def __eq__(self, other):
return (self.xpath == other.xpath and self.ratio == other.ratio)
[docs]class invalidShortRangeVarianceData( warning ):
def __init__(self, matrixType, obj=None):
warning.__init__(self, obj)
self.matrixType = matrixType
def __str__(self):
return ("shortRangeSelfScalingVariance should contain a diagonal array, contains %s" % self.matrixType)
def __eq__(self, other):
return (self.xpath == other.xpath and self.matrixType == other.matrixType)