Source code for fudge.gnds.resonances

# <<BEGIN-copyright>>
# <<END-copyright>>

"""
This module contains the resonances, resolved and unresolved classes.

class hierarchy:

    * resonances contains one or more of scatteringRadius, resolved and unresolved

    * resolved and unresolved contain a list of energy regions. In each region,

        - resolved may have SLBW, MLBW, RM, RML formats
        - unresolved has L-dependant or E-dependant format

      each of these subsections has a list of resonances and attributes
"""

# TODO: move to a 'resonances' module

import fractions
from pqu import PQU
from xData import XYs as XYsModule, constant as constantModule, link as linkModule, table as tableModule, regions as regionsModule
from fudge.gnds import suites as suitesModule
from fudge.gnds import abstractClasses as abstractClassesModule
from fudge.gnds.channelData import Q as QModule

import xData.ancestry as ancestryModule

__metaclass__ = type


[docs]class resonances( ancestryModule.ancestry ) : """ This is the top-level class for storing resonance parameters. For light targets it may contain only a scattering radius. For heavier targets it typically contains a resolved and/or unresolved section. resonances also has a boolean flag 'reconstructCrossSection'. If False, either the cross section has already been reconstructed, or the parameters are given for information only and no reconstruction should be performed. """ moniker = 'resonances' children = ('scatteringRadius', 'resolved', 'unresolved') def __init__(self, scatteringRadius_, resolved_=None, unresolved_=None): ancestryModule.ancestry.__init__( self ) self.scatteringRadius = scatteringRadius_ self.resolved = resolved_ self.unresolved = unresolved_ for child in (self.scatteringRadius, self.resolved, self.unresolved): if child is not None: child.setAncestor( self ) def __str__( self ) : """ string representation """ return( self.toString( simpleString = False ) )
[docs] def convertUnits( self, unitMap ): """ unitMap is a dictionary with old/new unit pairs where the old unit is the key (e.g., { 'eV' : 'MeV', 'b' : 'mb' }). """ for child in self.children: if getattr(self, child) is not None: getattr(self, child).convertUnits( unitMap )
[docs] def check( self, info ): from fudge.gnds import warning warnings = [] for child in self.children: section = getattr(self,child) if section is not None: warningList = section.check(info) if warningList: warnings.append( warning.context( section.moniker, warningList ) ) return warnings
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xmlString = [ '%s<%s>' % ( indent, self.moniker ) ] for child in self.children: section = getattr(self, child) if section is not None: xmlString += section.toXMLList( indent2, **kwargs ) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] def domain( self, unitTo = None, asPQU = False ): """ Return resonance region domain as a tuple of floats: (lowest edge, highest edge). options: unitTo: convert output to specified unit (given as a string). asPQU = True: return a tuple of PhysicalQuantityWithUncertainty instances instead of floats. """ bounds = [ (self.scatteringRadius.domainMin, self.scatteringRadius.domainMax) ] if self.resolved: if self.resolved.multipleRegions: bounds += [(reg.domainMin, reg.domainMax) for reg in self.resolved.regions] else: bounds.append( (self.resolved.domainMin, self.resolved.domainMax) ) if self.unresolved: bounds.append( (self.unresolved.domainMin, self.unresolved.domainMax) ) for idx in range(len(bounds)-1): assert bounds[idx][1] == bounds[idx+1][0], "Resonance region boundaries don't match!" if( asPQU ): return (bounds[0][0], bounds[-1][1]) elif unitTo: return (bounds[0][0].getValue(unitTo), bounds[-1][1].getValue(unitTo)) else: return (bounds[0][0].value, bounds[-1][1].value)
@property def reconstructCrossSection( self ): if self.resolved and not self.resolved.evaluated.useForSelfShieldingOnly: return True if self.unresolved and not self.unresolved.evaluated.useForSelfShieldingOnly: return True return False
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) scatRadius, RRR, URR = None,None,None for child in element: if child.tag==scatteringRadius.moniker: scatRadius = scatteringRadius.parseXMLNode( child, xPath, linkData ) elif child.tag==resolved.moniker: RRR = resolved.parseXMLNode( child, xPath, linkData ) elif child.tag==unresolved.moniker: URR = unresolved.parseXMLNode( child, xPath, linkData ) else: raise Exception("unknown element '%s' encountered in resonances!" % child.tag) res = cls( scatteringRadius_ = scatRadius, resolved_=RRR, unresolved_=URR ) xPath.pop() return res
[docs] def toString( self, simpleString = False ) : """Returns a string representation of self. If simpleString is True, the string contains only an overview without listing resonances""" s = 'Resonances:\n' if self.scatteringRadius: s += self.scatteringRadius.toString( simpleString = simpleString ) if self.resolved: s += self.resolved.toString( simpleString = simpleString ) if self.unresolved: s += self.unresolved.toString( simpleString = simpleString ) return( s )
[docs]class scatteringRadius( ancestryModule.ancestry ): """ The scatteringRadius determines scattering length. May be constant or energy-dependent. Each resonances section contains a scatteringRadius. The resolved / unresolved regions may also provide their own copy that overrides the top-level definition. The RMatrix class also provides a way of defining channel-specific radii (deprecated but necessary to handle ENDF evaluations) """ moniker = 'scatteringRadius' def __init__( self, form=None ) : ancestryModule.ancestry.__init__( self ) form.setAncestor( self ) self.form = form def __eq__(self, other): if not isinstance(other, scatteringRadius): return False return( self.form==other.form ) def __str__(self): return self.form.moniker def __nonzero__(self): return bool(self.form)
[docs] def toString( self, simpleString = False ) : """Returns a string representation of self. If simpleString is True, the string contains only an overview without listing resonances""" if simpleString: return str(self) return str(self)
[docs] def check( self, info ): """ Checks that the scattering radius is within a factor of 3 of the expected scattering radius of AP = 0.123 * self.target.getMass('amu')**(1./3.) + 0.08 This default AP is roughly the nuclear radius, so getting within a factor of 3 of the default shouldn't be a problem. A similar test is included in PSYCHE, but PSYCHE's cannot handle LRF=7 :param info: :return: """ from fudge.gnds import warning warnings = [] target = info['reactionSuite'].PoPs[ info['reactionSuite'].target ] if target.id in info['reactionSuite'].PoPs.aliases: target = info['reactionSuite'].PoPs[ target.pid ] expectedAP = 10.0*( 0.123 * target.getMass('amu')**(1./3.) + 0.08 ) # expected radius in fm factor=3.0 if self.isEnergyDependent(): egrid=self.form.domainGrid APs = self.getValueAs( 'fm', energy_grid=egrid ) for iE,AP in enumerate(APs): if AP/expectedAP > factor or AP/expectedAP < 1./factor: warning.badScatteringRadius(factor=factor, gotAP=AP, expectedAP=expectedAP, E=egrid[iE]) else: AP = self.form.constant if self.form.rangeUnit != 'fm': AP *= PQU.PQU(1, self.form.rangeUnit).getValueAs('fm') if AP/expectedAP > factor or AP/expectedAP < 1./factor: warning.badScatteringRadius(factor=factor, gotAP=AP, expectedAP=expectedAP) return warnings
[docs] def convertUnits( self, unitMap ): self.form.convertUnits( unitMap )
[docs] def isEnergyDependent(self): return isinstance( self.form, XYsModule.XYs1d )
[docs] def getValueAs( self, unit, energy_grid=None ): if self.isEnergyDependent(): if energy_grid is None: raise NameError("Missing: energy_grid to evaluate E-dependent scattering radius") energy_unit = self.form.axes[-1].unit xScale = self.form.domainUnitConversionFactor( energy_unit ) yScale = self.form.rangeUnitConversionFactor( unit ) return [ yScale * self.form.evaluate( xScale * e ) for e in energy_grid ] else: oldUnit = self.form.rangeUnit factor = PQU.PQU(1, oldUnit).getValueAs( unit ) return self.form.constant * factor
[docs] def toXMLList( self, indent = '', **kwargs ): indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xml = ['%s<%s>' % (indent, self.moniker)] xml += self.form.toXMLList( indent2, **kwargs ) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) form = { constantModule.constant1d.moniker: constantModule.constant1d, XYsModule.XYs1d.moniker: XYsModule.XYs1d, }[ element[0].tag ].parseXMLNode( element[0], xPath, linkData ) SR = cls( form ) xPath.pop() return SR
[docs]class hardSphereRadius( scatteringRadius): moniker = 'hardSphereRadius'
[docs]class spin( fractions.Fraction ): """ Store spins for a collection of resonances. Check denominator (must be integer or half-integer) """ def __init__( self, *args ): fractions.Fraction.__init__( self, *args ) if self.denominator not in (1,2): raise ValueError("Illegal spin '%s': must be integer or half-integer" % self)
[docs]class parity: """ Store parity for a collection of resonances. Allowed values for the parity are +1 or -1. """ def __init__(self, parity): self.value = int(parity) if self.value not in (1,-1): raise ValueError("%d is not a legal value for parity!" % self.value) def __str__(self): return str(self.value) def __int__(self): return self.value def __copy__( self ) : return( parity( self.value ) )
[docs]class resonanceParameters( ancestryModule.ancestry ): # FIXME: still needed? It's an extra level of nesting... """ Light-weight wrapper around a table. """ moniker = 'resonanceParameters' def __init__(self, table): ancestryModule.ancestry.__init__(self) self.table = table self.table.setAncestor(self)
[docs] def check( self, info ): warnings=[] return warnings
[docs] def convertUnits( self, unitMap ): self.table.convertUnits( unitMap )
[docs] def toXMLList(self, indent = '', **kwargs): indent2 = indent+' ' xmlList = ['%s<%s>' % (indent, self.moniker)] xmlList += self.table.toXMLList(indent2, **kwargs) xmlList[-1] += '</%s>' % self.moniker return xmlList
[docs] @staticmethod def parseXMLNode( element, xPath, linkData ): xPath.append( element.tag ) rps = resonanceParameters( tableModule.table.parseXMLNode( element.find(tableModule.table.moniker), xPath, linkData ) ) xPath.pop() return rps
# resolved/unresolved regions:
[docs]class resolved( abstractClassesModule.component ): """ class for resolved resonances """ moniker = 'resolved' def __init__(self, domainMin, domainMax, domainUnit): abstractClassesModule.component.__init__( self, allowedClasses=(energyIntervals, BreitWigner, RMatrix) ) self.domainMin = domainMin self.domainMax = domainMax self.domainUnit = domainUnit
[docs] def toString(self, simpleString = False): if isinstance( self.evaluated, energyIntervals ): return ("Resolved region with DEPRECATED multiple regions\n") else: return ("Resolved resonances in %s form\n" % self.evaluated.moniker )
[docs] def check( self, info ): import warning warnings = [] if isinstance(self.evaluated, energyIntervals): warnings.append( warning.RRmultipleRegions() ) warningList = self.evaluated.check(info) if warningList: warnings.append(warning.context(self.evaluated.moniker,warningList)) return warnings
[docs] def convertUnits( self, unitMap ): if self.domainUnit in unitMap: newUnit = unitMap[self.domainUnit] factor = PQU.PQU(1, self.domainUnit).getValueAs( newUnit ) self.domainMin *= factor self.domainMax *= factor self.domainUnit = newUnit for form in self: form.convertUnits( unitMap )
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xmlString = [ '%s<%s domainMin="%r" domainMax="%r" domainUnit="%s">' % ( indent, self.moniker, self.domainMin, self.domainMax, self.domainUnit) ] xmlString += self.evaluated.toXMLList( indent2, **kwargs ) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) RRR = cls(**getAttrs(element)) for child in element: formClass = { BreitWigner.moniker: BreitWigner, RMatrix.moniker: RMatrix, energyIntervals.moniker: energyIntervals, }.get( child.tag ) if formClass is None: raise Exception("unknown resolved resonance form '%s'!" % child.tag) RRR.add(formClass.parseXMLNode( child, xPath, linkData )) xPath.pop() return RRR
[docs]class unresolved( abstractClassesModule.component ): moniker = 'unresolved' def __init__(self, domainMin, domainMax, domainUnit): abstractClassesModule.component.__init__( self, allowedClasses=(unresolvedTabulatedWidths, energyIntervals) ) self.domainMin = domainMin self.domainMax = domainMax self.domainUnit = domainUnit
[docs] def toString( self, simpleString = False ): return ("Unresolved resonances in %s form\n" % self.evaluated.moniker )
[docs] def check( self, info ): from fudge.gnds import warning warnings = [] for L in self.evaluated.Ls: for J in L.Js: elist = J.levelSpacing.data.convertAxisToUnit(1, self.domainUnit).domainGrid if elist[0] > self.domainMin or elist[-1] < self.domainMax: warnings.append( warning.URRdomainMismatch( L.L, J.J, J ) ) missingPoints = [i1 for i1 in range(1,len(elist)) if elist[i1] > 3*elist[i1-1]] for idx in missingPoints: warnings.append( warning.URRinsufficientEnergyGrid( L.L, J.J, PQU.PQU(elist[idx-1],self.domainUnit), PQU.PQU(elist[idx],self.domainUnit), J ) ) return warnings
[docs] def convertUnits( self, unitMap ): if self.domainUnit in unitMap: newUnit = unitMap[self.domainUnit] factor = PQU.PQU(1, self.domainUnit).getValueAs(newUnit) self.domainMin *= factor self.domainMax *= factor self.domainUnit = newUnit for form in self: form.convertUnits(unitMap)
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xmlString = [ '%s<%s domainMin="%s" domainMax="%s" domainUnit="%s">' % ( indent, self.moniker, self.domainMin, self.domainMax, self.domainUnit ) ] xmlString += self.evaluated.toXMLList( indent2, **kwargs ) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) URR = cls( **getAttrs(element) ) for child in element: formClass = { unresolvedTabulatedWidths.moniker: unresolvedTabulatedWidths, energyIntervals.moniker: energyIntervals, }.get( child.tag ) if formClass is None: raise Exception("unknown unresolved resonance form '%s'!" % child.tag) URR.add(formClass.parseXMLNode( child, xPath, linkData )) xPath.pop() return URR
[docs]class energyIntervals(ancestryModule.ancestry): """ Resonance region may be broken up into multiple energy intervals (deprecated) """ moniker = 'energyIntervals' def __init__(self, label): ancestryModule.ancestry.__init__(self) self.label = label self.__intervals = [] def __len__(self): return len(self.__intervals) def __getitem__(self, item): return self.__intervals[item]
[docs] def append(self, item): self.__intervals.append(item) item.setAncestor(self, attribute='index')
[docs] def check( self, info ): import warning warnings = [] for idx, interval in enumerate(self): info['energyIntervalIndex'] = idx warningList = interval.check(info) if warningList: warnings.append(warning.context('%s[@index="%d"' % (interval.moniker, interval.index) ,warningList)) del info['energyIntervalIndex'] return warnings
[docs] def convertUnits(self, unitMap): for interval in self: interval.convertUnits(unitMap)
@property def useForSelfShieldingOnly(self): for interval in self: if interval.evaluated.useForSelfShieldingOnly: return True return False
[docs] def toXMLList(self, indent='', **kwargs): indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xmlString = [ '%s<%s label="%s">' % (indent, self.moniker, self.label) ] for interval in self: xmlString += interval.toXMLList(indent2, **kwargs) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @classmethod def parseXMLNode(cls, element, xPath, linkData): xPath.append( element.tag ) EIs = cls(element.get('label')) for child in element: EIs.append( energyInterval.parseXMLNode(child,xPath,linkData) ) xPath.pop() return EIs
[docs]class energyInterval(ancestryModule.ancestry): """ single energy interval, for use inside energyIntervals """ moniker = 'energyInterval' def __init__(self, index, data, domainMin, domainMax, domainUnit): ancestryModule.ancestry.__init__(self) self.index = index data.setAncestor(self) self.evaluated = data self.domainMin = domainMin self.domainMax = domainMax self.domainUnit = domainUnit
[docs] def check( self, info ): import warning warnings = [] warningList = self.evaluated.check(info) if warningList: warnings.append(warning.context( self.evaluated.moniker, warningList )) return warnings
[docs] def convertUnits( self, unitMap ): if self.domainUnit in unitMap: newUnit = unitMap[self.domainUnit] factor = PQU.PQU(1, self.domainUnit).getValueAs(newUnit) self.domainMin *= factor self.domainMax *= factor self.domainUnit = newUnit self.evaluated.convertUnits(unitMap)
[docs] def toString(self, simpleString = False): return ("%s resonances, %s to %s. Contains %i resonances" % (self.evaluated, self.domainMin, self.domainMax, len(self.evaluated) ) )
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xmlString = ['%s<%s index="%s" domainMin="%r" domainMax="%r" domainUnit="%s">' % ( indent, self.moniker, self.index, self.domainMin, self.domainMax, self.domainUnit ) ] xmlString += self.evaluated.toXMLList( indent2, **kwargs ) xmlString[-1] += '</%s>' % self.moniker return xmlString
@property def BreitWigner(self): if isinstance(self.evaluated, BreitWigner): return self.evaluated return None @property def RMatrix(self): if isinstance(self.evaluated, RMatrix): return self.evaluated return None
[docs] @classmethod def parseXMLNode(cls, element, xPath, linkData): xPath.append('%s[@label="%s"]' % (cls.moniker, element.get('label'))) formClass = { BreitWigner.moniker: BreitWigner, RMatrix.moniker: RMatrix, unresolvedTabulatedWidths.moniker: unresolvedTabulatedWidths, }.get( element[0].tag ) if formClass is None: raise Exception("unknown unresolved resonance form '%s'!" % element[0].tag) data = formClass.parseXMLNode(element[0], xPath, linkData) EI = cls( data=data, **getAttrs(element) ) xPath.pop() return EI
def _resonance_checker(self,info,things): """ Common resolved resonance checks :param self: a reference to the resonanceFormalismBaseClass (or derived) or RMatrix class :param info: the dictionary of checking options, shared by all .check() member functions. For the potential scattering convergence test, the 'potentialScatteringCoverganceCriteria' value is the limit on the ratio of the Lth cross section term to the 0th one. It is printed as a percent but should be entered as a fraction. :param things: list of things to check the obvious way (with a .check() function) :return: """ from fudge.gnds import warning from collections import OrderedDict warnings=[] # Check the member data for thing in things: warningList = thing.check(info) if warningList: warnings.append(warning.context(thing.moniker,warningList)) # Setup for the physics checks import fudge.processing.resonances.reconstructResonances as rrReconstructModule rrReconstructor = rrReconstructModule.getResonanceReconstructionClass(self)( info['reactionSuite'], sectionIndex = info.get('energyIntervalIndex') ) # Check for mismatched spins warnings+=rrReconstructor.setResonanceParametersByChannel(warnOnly=True) #multipleSScheme='NJOY', 'ENDF' or None # Check for missing channels via sum rule of statistical weights sumgJ={} for cc in rrReconstructor.channels: if isinstance(cc,OrderedDict): iterateThroughThis=cc # for SLBW else: iterateThroughThis=[cc] # so everyone else can iterate like SLBW for c in iterateThroughThis: if (c.reaction,c.l) not in sumgJ: sumgJ[(c.reaction,c.l)]=0.0 sumgJ[(c.reaction,c.l)]+=c.gfact keys = sumgJ.keys() keys.sort() for rxn,L in keys: if abs(abs(sumgJ[(rxn,L)]) - abs(2.0*L+1.0)) > 1e-6: warnings.append(warning.badSpinStatisticalWeights(L,sumgJ[(rxn,L)],2.*L+1,rxn)) # setup for checking allowed angular momentum: def getSList(Ia,Ib): '''Get possible spins''' smin=abs(Ia-Ib) smax=Ia+Ib nS = int(smax-smin)+1 return [iS+smin for iS in range(nS)] LMax = 0 allSs = [] rxnList = [] for cc in rrReconstructor.channels: if isinstance(cc,OrderedDict): iterateThroughThis=cc # for SLBW else: iterateThroughThis=[cc] # so everyone else can iterate like SLBW for c in iterateThroughThis: if c.eliminated: continue LMax = max(c.l,LMax) if c.reaction not in rxnList: rxnList.append(c.reaction) if c.channelClass not in [rrReconstructModule.FISSIONCHANNEL,rrReconstructModule.COMPETATIVECHANNEL]: try: spinList = getSList(*rrReconstructor.getParticleSpins(c.reaction)) for s in spinList: if s not in allSs: allSs.append(s) except: warnings.append(warning.unknownSpinParity(c.reaction)) # determine the min & max J allowed Jmin = min(allSs) Jmax = LMax + max(allSs) nJ = int(Jmax - Jmin) # Check the allowed angular momenta for L in range(0,LMax+1): for iJ in range(0,nJ+1): J=iJ+Jmin for rxn in rxnList: if 'ission' not in rxn: try: spinList = getSList(*rrReconstructor.getParticleSpins(c.reaction)) except: warnings.append(warning.unknownSpinParity(c.reaction)) continue for S in spinList: if not J in rrReconstructModule.getAllowedTotalSpins(L,S,useFactor2Trick=False): continue for cc in rrReconstructor.channels: if isinstance(cc, OrderedDict): iterateThroughThis = cc # for SLBW else: iterateThroughThis = [cc] # so everyone else can iterate like SLBW for c in iterateThroughThis: gotIt = False if rxn==c.reaction and \ rrReconstructModule.spins_equal(c.l,L) and \ rrReconstructModule.spins_equal(c.J,J) and \ rrReconstructModule.spins_equal(c.s,S): gotIt=True break if not gotIt and 'apture' not in rxn: theWarning=warning.missingResonanceChannel(L,S,J,rxn) if str(theWarning) not in map(str,warnings): warnings.append(theWarning) # Check for convergence in L import numpy almostXS={} for cc in rrReconstructor.channels: if isinstance(cc,OrderedDict): iterateThroughThis=cc # for SLBW else: iterateThroughThis=[cc] # so everyone else can iterate like SLBW for c in iterateThroughThis: egrid = numpy.array([max(rrReconstructor.lowerBound, rrReconstructor.lowerBound + c.Xi), rrReconstructor.upperBound]) phis=rrReconstructor.phiByChannel(c,egrid) almostXS.setdefault(c.l,[]).extend( list(pow(numpy.sin(phis)/rrReconstructor.k(egrid),2.0)) ) fom=max(almostXS[max(almostXS.keys())])/max(almostXS[min(almostXS.keys())]) fomTarget=info.get('potentialScatteringCoverganceCriteria',0.001) if fom > fomTarget: warnings.append(warning.potentialScatteringNotConverged(c.l, rrReconstructor.upperBound, fom, fomTarget)) return warnings ################################################################ # now we come to specific implementations for resolved region: # ################################################################
[docs]class BreitWigner( ancestryModule.ancestry ) : moniker = 'BreitWigner' singleLevel = 'singleLevel' multiLevel = 'multiLevel' optAttrList = ( 'calculateChannelRadius', 'computeAngularDistribution', 'LvaluesNeededForConvergence', 'useForSelfShieldingOnly') def __init__(self, label, approximation, resonanceParameters=None, scatteringRadius=None, **kwargs): """LdependentScatteringRadii is a list of dictionaries, one for each L with APL != AP only used in ENDF for Reich_Moore form resonanceParameters should be a gnds.core.math.table.table instance.""" index = 0 for attr in self.optAttrList: setattr( self, attr, kwargs.get(attr) ) if self.computeAngularDistribution: self.computeAngularDistribution = bool(self.computeAngularDistribution) self.label = label if approximation not in (BreitWigner.singleLevel, BreitWigner.multiLevel): raise TypeError("Unknown approximation '%s' for BreitWigner resonance section" % approximation) self.approximation = approximation self.resonanceParameters = resonanceParameters or [] if self.resonanceParameters: self.resonanceParameters.setAncestor( self ) self.__scatteringRadius = scatteringRadius if self.__scatteringRadius: self.__scatteringRadius.setAncestor( self ) ancestryModule.ancestry.__init__( self )
[docs] def check( self, info ): return _resonance_checker(self, info, [self.scatteringRadius,self.resonanceParameters])
[docs] def convertUnits( self, unitMap ): if self.scatteringRadius is not None: self.scatteringRadius.convertUnits(unitMap) self.resonanceParameters.convertUnits(unitMap)
def __getitem__(self, idx): return self.resonanceParameters.table[idx] def __len__(self): return len(self.resonanceParameters.table)
[docs] def addResonance(self, resonance): """ insert a new resonance in the resonance parameter table """ #resonance = (energy, J, l, ... ) self.resonanceParameters.table.addRow( resonance )
@property def scatteringRadius(self): if self.__scatteringRadius is not None: return self.__scatteringRadius else: return self.findClassInAncestry(resonances).scatteringRadius @scatteringRadius.setter def scatteringRadius(self, value): """ Can be set to None or to a scatteringRadius instance. """ self.__scatteringRadius = value if value is not None: if not isinstance(value, scatteringRadius): raise TypeError("Scattering radius can't be set to type '%s'" % type(value)) self.__scatteringRadius.setAncestor(self)
[docs] def toXMLList( self, indent = '', **kwargs ): incrementalIndent = kwargs.get( 'incrementalIndent', ' ' ) indent2 = indent + incrementalIndent if not self.moniker : raise NotImplementedError ("Please use specific formalisms (MLBW, RM, etc) instead of the BaseClass") xmlString = '%s<%s label="%s" approximation="%s"' % ( indent, self.moniker, self.label, self.approximation ) for attr in self.optAttrList: if getattr(self,attr,None): attrVal = getattr(self,attr) if type(attrVal) is bool: attrVal = str(attrVal).lower() xmlString += ' %s="%s"' % (attr, attrVal ) xmlString = [xmlString+'>'] if self.__scatteringRadius is not None: xmlString += self.__scatteringRadius.toXMLList( indent2, **kwargs ) if self.resonanceParameters: xmlString.extend( self.resonanceParameters.toXMLList( indent2, **kwargs ) ) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) radius = element.find( scatteringRadius.moniker ) if radius is not None: radius = scatteringRadius.parseXMLNode( radius, xPath, linkData ) linkData['conversionTable'] = {'index':int, 'L':int} # inform table class how to treat columns parameters = resonanceParameters.parseXMLNode( element.find( resonanceParameters.moniker ), xPath, linkData ) attrs = getAttrs( element ) label = attrs.pop("label") approximation = attrs.pop("approximation") resonanceData = cls( label, approximation, parameters, radius, **attrs ) del linkData['conversionTable'] xPath.pop() return resonanceData
############################################# # R-matrix (LRF=7 in ENDF) is more complex: # #############################################
[docs]class RMatrix( ancestryModule.ancestry ): """ RMatrix is a general container for resolved resonance parameters. It can handle standard Lane & Thomas R-Matrix, but can also use various approximations (Single- and Multi-level Breit Wigner plus Reich-Moore). Internally, resonances are sorted into spin groups, each with a conserved total angular momentum and parity. """ moniker = 'RMatrix' optAttrList = ('approximation','boundaryCondition','relativisticKinematics','supportsAngularReconstruction', 'reducedWidthAmplitudes','calculatePenetrability','calculateShift','calculateChannelRadius', 'useForSelfShieldingOnly') def __init__(self, label, resonanceReactions_, spinGroups_, **kwargs): ancestryModule.ancestry.__init__(self) self.label = label self.resonanceReactions = resonanceReactions_ self.spinGroups = spinGroups_ for attr in self.optAttrList: setattr(self, attr, kwargs.get(attr)) def __getitem__(self, idx): return self.spinGroups[idx] def __len__(self): return len(self.spinGroups) @property def resonanceReactions(self): return self.__resonanceReactions @resonanceReactions.setter def resonanceReactions(self, value): if not isinstance(value, resonanceReactions): raise TypeError("Must be a resonanceReactions instance") value.setAncestor(self) self.__resonanceReactions = value @property def spinGroups(self): return self.__spinGroups @spinGroups.setter def spinGroups(self, value): if not isinstance(value, spinGroups): raise TypeError("Must be a spinGroups instance") value.setAncestor(self) self.__spinGroups = value
[docs] def check( self, info ): return _resonance_checker(self, info, [self.resonanceReactions] + list(self.spinGroups))
[docs] def convertUnits( self, unitMap ): for reac in self.resonanceReactions: reac.convertUnits( unitMap ) for sg in self.spinGroups: sg.convertUnits( unitMap )
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + ' ' xmlString = ['%s<%s label="%s"' % ( indent, self.moniker, self.label ) ] for attr in self.optAttrList: if getattr(self,attr): attrVal = getattr(self,attr) if type(attrVal) is bool: attrVal = str(attrVal).lower() xmlString[0] += ' %s="%s"' % (attr,attrVal) xmlString[0] += '>' xmlString += self.resonanceReactions.toXMLList( indent=indent2, **kwargs ) xmlString += self.spinGroups.toXMLList(indent=indent2, **kwargs) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @staticmethod def parseXMLNode( element, xPath, linkData ): xPath.append( element.tag ) RRs = resonanceReactions() RRs.parseXMLNode( element.find(resonanceReactions.moniker), xPath, linkData ) linkData['conversionTable'] = {'index':int, 'L':int, 'channelSpin':spin} SGs = spinGroups() SGs.parseXMLNode( element.find(spinGroups.moniker), xPath, linkData ) attrs = getAttrs(element, required=RMatrix.optAttrList) label = attrs.pop("label") tmp = RMatrix( label, RRs, SGs, **attrs ) del linkData['conversionTable'] xPath.pop() return tmp
[docs]class resonanceReactions( suitesModule.suite ): moniker = 'resonanceReactions' def __init__( self ) : suitesModule.suite.__init__( self, [resonanceReaction])
[docs] def check( self, info ): from fudge.gnds import warning warnings = [] for c in self: warningList = c.check(info) if warningList: warnings.append(warning.context(str(c.moniker)+' '+str(c.label),warningList)) return warnings
[docs]class resonanceReaction( ancestryModule.ancestry): """ Describes one reaction channel that opens up in the resonance region. In an R-Matrix section, all open reaction channels should be described in the list of resonanceReaction elements """ moniker = 'resonanceReaction' fission = 'fission' # special tokens to support fission reactions fissionProduct = 'fissionProduct' def __init__( self, label, reactionLink, ejectile, Q=None, scatteringRadius=None, hardSphereRadius=None, computeShiftFactor=False, eliminated=False): ancestryModule.ancestry.__init__(self) self.label = label self.reactionLink = reactionLink self.__ejectile = ejectile self.__residual = None self.Q = Q self.__scatteringRadius = scatteringRadius self.hardSphereRadius = hardSphereRadius self.computeShiftFactor = computeShiftFactor self.eliminated = eliminated @property def ejectile(self): return self.__ejectile @property def residual(self): if self.__residual is None: if self.ejectile == resonanceReaction.fission: return resonanceReaction.fissionProduct products = set([p.pid for p in self.reactionLink.link.outputChannel.products]) products.remove( self.ejectile ) if len(products) != 1: raise ValueError("Cannot compute resonanceReaction residual!") self.__residual = products.pop() return self.__residual @property def scatteringRadius(self): if self.__scatteringRadius is not None: return self.__scatteringRadius else: return self.findClassInAncestry(resonances).scatteringRadius @scatteringRadius.setter def scatteringRadius(self, value): """ Can be set to None or to a scatteringRadius instance. """ self.__scatteringRadius = value if value is not None: if not isinstance(value, scatteringRadius): raise TypeError("Scattering radius can't be set to type '%s'" % type(value)) self.__scatteringRadius.setAncestor(self)
[docs] def check( self, info ): from fudge.gnds import warning warnings = [] # check the reaction link theLinkTarget = None try: theLinkTarget = self.reactionLink.follow(self.getRootAncestor()) if theLinkTarget is None: warnings.append(warning.unresolvedLink(self.reactionLink)) except: warnings.append(warning.unresolvedLink(self.reactionLink)) # check the radii for thing in [self.scatteringRadius, self.hardSphereRadius]: if thing is None: continue warningList = thing.check(info) if warningList: warnings.append(warning.context(thing.moniker,warningList)) return warnings
[docs] def convertUnits( self, unitMap ): for child in ('Q','scatteringRadius','hardSphereRadius'): if getattr(self,child) is not None: getattr(self,child).convertUnits(unitMap)
[docs] def isFission( self ): return self.reactionLink.link.outputChannel.isFission()
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent+' ' attrstring = '' if self.ejectile is not None: attrstring += ' ejectile="%s"' % self.ejectile if self.computeShiftFactor: attrstring += ' computeShiftFactor="true"' if self.eliminated: attrstring += ' eliminated="true"' xmlString = ['%s<%s label="%s"%s>' % (indent, self.moniker, self.label, attrstring) ] xmlString += self.reactionLink.toXMLList( indent=indent2, **kwargs ) if self.Q is not None: xmlString += self.Q.toXMLList( indent=indent2, **kwargs ) if self.__scatteringRadius is not None: xmlString += self.__scatteringRadius.toXMLList( indent=indent2, **kwargs) if self.hardSphereRadius is not None: xmlString += self.hardSphereRadius.toXMLList( indent=indent2, **kwargs) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @staticmethod def parseXMLNode( element, xPath, linkData ): xPath.append( element.tag ) reactionLink = linkModule.link.parseXMLNode( element.find('link'), xPath, linkData ) Qval, scatRad, hsRad = None, None, None if element.find( QModule.component.moniker ): Qval = QModule.component() # FIXME suite.parseXMLNode is not a static method Qval.parseXMLNode( element.find( QModule.component.moniker ), xPath, linkData ) if element.find( scatteringRadius.moniker ): scatRad = scatteringRadius.parseXMLNode( element.find( scatteringRadius.moniker ), xPath, linkData ) if element.find( hardSphereRadius.moniker ): hsRad = hardSphereRadius.parseXMLNode( element.find( hardSphereRadius.moniker), xPath, linkData) tmp = resonanceReaction( element.get('label'), reactionLink=reactionLink, ejectile=element.get('ejectile'), Q = Qval, scatteringRadius=scatRad, hardSphereRadius=hsRad, computeShiftFactor=getBool( element.get('computeShiftFactor','false') ), eliminated=getBool( element.get('eliminated','false') ) ) xPath.pop() return tmp
[docs]class channels( suitesModule.suite ): moniker = 'channels' def __init__( self ) : suitesModule.suite.__init__( self, [channel])
[docs]class channel( ancestryModule.ancestry ): """ Use if necessary to override the true scattering radius, effective radius, etc. for a single spin-group / channel combination. """ moniker = 'channel' def __init__(self, label, resonanceReaction, columnIndex, L=None, channelSpin=None, scatteringRadius=None, hardSphereRadius=None, penetrability=None, shiftFactor=None, phaseShift=None, boundaryConditionOverride=None): ancestryModule.ancestry.__init__(self) self.label = label self.resonanceReaction = resonanceReaction self.columnIndex = columnIndex self.L = L self.channelSpin = channelSpin self.scatteringRadius = scatteringRadius self.hardSphereRadius = hardSphereRadius self.penetrability = penetrability self.shiftFactor = shiftFactor self.phaseShift = phaseShift self.boundaryConditionOverride = boundaryConditionOverride
[docs] def convertUnits( self, unitMap ): for child in ('scatteringRadius','hardSphereRadius'): if getattr(self,child) is not None: getattr(self,child).convertUnits(unitMap)
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent+' ' attrs = "" if self.L is not None: attrs += ' L="%d"' % self.L if self.channelSpin is not None: attrs += ' channelSpin="%s"' % self.channelSpin if self.boundaryConditionOverride is not None: attrs += ' boundaryConditionOverride="%s"' % self.boundaryConditionOverride xmlString = ['%s<%s label="%s" resonanceReaction="%s"%s columnIndex="%d">' % (indent, self.moniker, self.label, self.resonanceReaction, attrs, self.columnIndex) ] for attr in ('scatteringRadius', 'hardSphereRadius', 'penetrability', 'shiftFactor', 'phaseShift'): if getattr(self, attr) is not None: xmlString += getattr(self, attr).toXMLList( indent=indent2, **kwargs ) xmlString[-1] += '</%s>' % self.moniker return xmlString
[docs] @staticmethod def parseXMLNode( element, xPath, linkData ): xPath.append( element.tag ) kwargs = getAttrs(element) for child in element: childClass = {'scatteringRadius': scatteringRadius, 'hardSphereRadius': hardSphereRadius, 'penetrability': None, # FIXME last three not yet implemented 'shiftFactor': None, 'phaseShift': None}.get( child.tag ) kwargs[ child.tag ] = childClass.parseXMLNode( child, xPath, linkData ) tmp = channel( **kwargs ) xPath.pop() return tmp
[docs]class spinGroups( suitesModule.suite ): """ Contains a list of spinGroup nodes """ moniker = 'spinGroups' def __init__(self): suitesModule.suite.__init__(self, [spinGroup])
[docs]class spinGroup( ancestryModule.ancestry ): """ Single group with same Jpi (conserved). Each spin group contains an AP (scattering radius), along with 1 or more resonance widths. """ moniker = 'spinGroup' def __init__(self, label, spin, parity, channels, resonanceParameters): ancestryModule.ancestry.__init__(self) self.label = label self.spin = spin self.parity = parity self.channels = channels self.resonanceParameters = resonanceParameters def __getitem__(self, idx): return self.resonanceParameters[idx] def __len__(self): return len(self.resonanceParameters.table) def __lt__(self, other): # for sorting spin groups by Jpi. group J values together return (self.spin, self.parity) < (other.spin, other.parity) @property def channels(self): return self.__channels @channels.setter def channels(self, value): if not isinstance(value, channels): raise TypeError("Must be a channels instance") value.setAncestor(self) self.__channels = value @property def resonanceParameters(self): return self.__resonanceParameters @resonanceParameters.setter def resonanceParameters(self, value): if not isinstance(value, resonanceParameters): raise TypeError("Must be a resonanceParameters instance") value.setAncestor(self) self.__resonanceParameters = value
[docs] def check( self, info ): from fudge.gnds import warning warnings = [] for thing in []: if thing is None: continue warningList = thing.check(info) if warningList: warnings.append(warning.context(thing.moniker,warningList)) return warnings
[docs] def convertUnits( self, unitMap ): for chan in self.channels: chan.convertUnits(unitMap) self.resonanceParameters.convertUnits(unitMap)
[docs] def toXMLList( self, indent = '', **kwargs ) : incrementalIndent = kwargs.get( 'incrementalIndent', ' ' ) indent2 = indent + incrementalIndent xml = ['%s<%s label="%s" spin="%s" parity="%s">' % (indent, self.moniker, self.label, self.spin, self.parity)] xml += self.channels.toXMLList(indent=indent2, **kwargs) if self.resonanceParameters.table.columns: # need not contain any data xml.extend( self.resonanceParameters.toXMLList( indent2, **kwargs ) ) xml[-1] += '</%s>' % self.moniker return xml
[docs] @staticmethod def parseXMLNode( element, xPath, linkData ): xPath.append( element.tag ) parameters = resonanceParameters.parseXMLNode(element.find(resonanceParameters.moniker), xPath, linkData) chs = channels() chs.parseXMLNode(element.find(channels.moniker), xPath, linkData) SG = spinGroup(channels=chs, resonanceParameters=parameters, **getAttrs(element)) xPath.pop() return SG
############################################# # end of R-Matrix (LRF=7) classes # ############################################# #################################################### # remaining classes are for unresolved resonances: # ####################################################
[docs]class unresolvedTabulatedWidths(ancestryModule.ancestry): moniker = 'tabulatedWidths' def __init__(self, label, approximation, resonanceReactions_, Ls_, scatteringRadius=None, useForSelfShieldingOnly=False): """ L_sections is a list of URR_Lsections, which in turn contains a list of URR_Jsections. The average widths are given in the J_sections. """ ancestryModule.ancestry.__init__(self) self.label = label self.approximation = approximation self.resonanceReactions = resonanceReactions_ self.scatteringRadius = scatteringRadius self.Ls = Ls_ self.useForSelfShieldingOnly = useForSelfShieldingOnly def __len__(self): return len(self.Ls) @property def resonanceReactions(self): return self.__resonanceReactions @resonanceReactions.setter def resonanceReactions(self, value): if not isinstance(value, resonanceReactions): raise TypeError("Must be a resonanceReactions instance") value.setAncestor(self) self.__resonanceReactions = value @property def scatteringRadius( self ): if self.__scatteringRadius is not None: return self.__scatteringRadius else: return self.findClassInAncestry(resonances).scatteringRadius @scatteringRadius.setter def scatteringRadius(self, value): """ Can be set to None or to a scatteringRadius instance. """ self.__scatteringRadius = value if value is not None: if not isinstance(value, scatteringRadius): raise TypeError("Scattering radius can't be set to type '%s'" % type(value)) self.__scatteringRadius.setAncestor(self) @property def Ls(self): return self.__Ls @Ls.setter def Ls(self, value): if not isinstance(value, URR_Lsections): raise TypeError("Must be a URR_Lsections instance") value.setAncestor(self) self.__Ls = value
[docs] def convertUnits( self, unitMap ): if self.__scatteringRadius is not None: self.__scatteringRadius.convertUnits(unitMap) for lval in self.Ls: for jval in lval.Js: jval.convertUnits(unitMap)
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) attrs = ' label="%s" approximation="%s"' % (self.label, self.approximation) if self.useForSelfShieldingOnly: attrs += ' useForSelfShieldingOnly="true"' xml = ['%s<%s%s>' % (indent, self.moniker, attrs)] xml += self.resonanceReactions.toXMLList( indent2, **kwargs ) if self.__scatteringRadius: xml += self.__scatteringRadius.toXMLList( indent2, **kwargs ) xml += self.Ls.toXMLList( indent2, **kwargs ) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append( element.tag ) linkData['conversionTable'] = {'index':int} resonanceReactions_ = resonanceReactions() resonanceReactions_.parseXMLNode( element.find(resonanceReactions.moniker), xPath, linkData ) radius = element.find( scatteringRadius.moniker ) if radius is not None: radius = scatteringRadius.parseXMLNode( radius, xPath, linkData ) Ls_ = URR_Lsections() Ls_.parseXMLNode( element.find( Ls_.moniker ), xPath, linkData ) result = cls( element.get('label'), element.get('approximation'), resonanceReactions_, Ls_=Ls_, scatteringRadius=radius, useForSelfShieldingOnly=element.get('useForSelfShieldingOnly') == 'true' ) del linkData['conversionTable'] xPath.pop() return result
[docs]class URR_interpolationTable(ancestryModule.ancestry): """ Base class for unresolved level spacing and widths Data are stored as XYs1d or regions1d. """ allowedSubclasses = (XYsModule.XYs1d, regionsModule.regions1d) def __init__( self, data ): ancestryModule.ancestry.__init__(self) self.data = data @property def data( self ): return self.__data @data.setter def data( self, value ): if not isinstance(value, self.allowedSubclasses): raise TypeError("levelSpacing must be an XYs1d or regions1d instance") value.setAncestor(self) self.__data = value
[docs] def toXMLList( self, indent='', **kwargs ): indent2 = indent + kwargs.get('incrementalIndent', ' ') xml = ['%s<%s>' % (indent, self.moniker)] xml += self.data.toXMLList(indent2, **kwargs) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append(element.tag) child, = element[:] data = None for subclass in cls.allowedSubclasses: if child.tag == subclass.moniker: data = subclass.parseXMLNode(child, xPath, linkData) break result = cls(data) xPath.pop() return result
[docs]class URR_Lsections( suitesModule.suite ): """ tabulated widths contains a list of 'L' sections, each of which contains a list of 'J' sections """ moniker = "Ls" def __init__( self ): suitesModule.suite.__init__(self, (URR_Lsection,))
[docs]class URR_Lsection(ancestryModule.ancestry): """ unresolved average widths, grouped by L. Contains list of J-values: """ moniker = 'L' def __init__(self, label, L, Js): ancestryModule.ancestry.__init__(self) self.label = label self.L = L self.Js = Js @property def value(self): return self.L @property def Js(self): return self.__Js @Js.setter def Js(self, value): if not isinstance(value, (URR_Jsections)): raise TypeError("Must be URR_Jsections instance") value.setAncestor(self) self.__Js = value
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xml = ['%s<%s label="%s" value="%d">' % (indent, self.moniker, self.label, self.value)] xml += self.Js.toXMLList( indent2, **kwargs ) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode(cls, element, xPath, linkData): xPath.append( '%s[@label="%s"]' % (element.tag, element.get('label')) ) Js = URR_Jsections() Js.parseXMLNode( element.find(Js.moniker), xPath, linkData ) result = cls( element.get('label'), int(element.get('value')), Js ) xPath.pop() return result
[docs]class URR_Jsections( suitesModule.suite ): moniker = "Js" def __init__( self ): suitesModule.suite.__init__(self, (URR_Jsection,))
[docs]class URR_Jsection(ancestryModule.ancestry): """ Unresolved average parameters for a specific L/J. Contains interpolation tables for the level spacing and widths, plus degrees of freedom for each open channel (degrees of freedom are typically 1 or 2 indicating how many channel spins contribute) """ moniker = 'J' def __init__(self, label, J, levelSpacing_, widths_): ancestryModule.ancestry.__init__(self) self.label = label self.J = J self.levelSpacing = levelSpacing_ self.widths = widths_ @property def value(self): return str(self.J) @property def levelSpacing(self): return self.__levelSpacing @levelSpacing.setter def levelSpacing(self, spacing): if not isinstance(spacing, levelSpacing): raise TypeError("Expected levelSpacing instance, got %s instead" % type(spacing)) spacing.setAncestor(self) self.__levelSpacing = spacing @property def widths( self ): return self.__widths @widths.setter def widths( self, widths ): if not isinstance(widths, URR_widths): raise TypeError("Expected URR_widths instance, got %s instead" % type(widths)) widths.setAncestor(self) self.__widths = widths
[docs] def convertUnits( self, unitMap ): self.levelSpacing.data.convertUnits(unitMap) for width in self.widths: width.data.convertUnits(unitMap)
[docs] def toXMLList( self, indent = '', **kwargs ) : indent2 = indent + kwargs.get( 'incrementalIndent', ' ' ) xml = ['%s<%s label="%s" value="%s">' % (indent,self.moniker,self.label,self.J)] xml += self.levelSpacing.toXMLList(indent2, **kwargs) xml += self.widths.toXMLList(indent2, **kwargs) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode(cls, element, xPath, linkData): xPath.append( '%s[@label="%s"]' % (element.tag, element.get('label')) ) levelSpacing_ = levelSpacing.parseXMLNode( element.find(levelSpacing.moniker), xPath, linkData ) widths_ = URR_widths() widths_.parseXMLNode( element.find(URR_widths.moniker), xPath, linkData ) Jsec = URR_Jsection( element.get('label'), fractions.Fraction(element.get('value')), levelSpacing_, widths_ ) xPath.pop() return Jsec
[docs]class levelSpacing(URR_interpolationTable): """ Contains the average level spacing, stored in XYs1d or regions1d. """ moniker = "levelSpacing"
[docs]class URR_widths( suitesModule.suite ): """ Contains all average channel widths for an L/J combination. """ moniker = "widths" def __init__(self): suitesModule.suite.__init__(self, allowedClasses=(URR_width,))
[docs]class URR_width(URR_interpolationTable): """ Stores the average width and number of degrees of freedom for a single channel Degrees of freedom are typically 0, 1 or 2 depending on how many channel spins contribute, but it may be non-integer for a reaction that has a threshold somewhere inside the unresolved region. Average width is stored as XYs1d or regions1d. """ moniker = "width" def __init__(self, resonanceReaction, data, degreesOfFreedom = 0): URR_interpolationTable.__init__(self, data) self.resonanceReaction = resonanceReaction self.degreesOfFreedom = degreesOfFreedom @property def label(self): return self.resonanceReaction
[docs] def toXMLList( self, indent='', **kwargs ): indent2 = indent + kwargs.get('incrementalIndent', ' ') attrs = '' if self.degreesOfFreedom > 0: attrs += ' degreesOfFreedom="%g"' % self.degreesOfFreedom xml = ['%s<%s resonanceReaction="%s"%s>' % (indent, self.moniker, self.resonanceReaction, attrs)] xml += self.data.toXMLList(indent2, **kwargs) xml[-1] += '</%s>' % self.moniker return xml
[docs] @classmethod def parseXMLNode( cls, element, xPath, linkData ): xPath.append(element.tag) child, = element[:] data = None for subclass in cls.allowedSubclasses: if child.tag == subclass.moniker: data = subclass.parseXMLNode(child, xPath, linkData) break result = cls(element.get('resonanceReaction'), data, floatOrint(element.get('degreesOfFreedom',0))) xPath.pop() return result
# helper functions for reading in from xml:
[docs]def getBool( value ): return {'true':True, '1':True, 'false':False, '0':False}[value]
[docs]def floatOrint( value ): if float( value ).is_integer(): return int( value ) return float( value )
[docs]def getAttrs(element, exclude=(), required=()): """ Convert attributes to proper type (float, bool, int, etc), returning a dictionary. Anything in the 'exclude' list is omitted, anything in the 'required' list is automatically set to False if not present. """ conversionTable = { 'domainMin':float, 'domainMax':float, 'value':float, 'channelSpin':spin, 'reconstructCrossSection':getBool, 'supportsAngularReconstruction':getBool, 'multipleRegions': getBool, 'calculateChannelRadius':getBool, 'computeAngularDistribution':getBool, 'useForSelfShieldingOnly':getBool, 'calculateShift':getBool,'calculatePenetrability':getBool, 'channelIndex':int, 'columnIndex':int, 'LvaluesNeededForConvergence':int, 'ENDF_MT':int, 'index':int, 'L':int, 'spin':spin, 'parity':parity, 'boundaryConditionOverride':float, } attrs = dict( element.items() ) for key in attrs.keys(): if key in exclude: attrs.pop(key) elif key in conversionTable: attrs[key] = conversionTable[key]( attrs[key] ) for val in required: if val not in attrs: attrs[val] = False return attrs