def _getFisxDetector(fitConfiguration, attenuatorsDetector=None): distance = fitConfiguration["concentrations"]["distance"] area = fitConfiguration["concentrations"]["area"] detectorMaterial = fitConfiguration["detector"]["detele"] if attenuatorsDetector is None: # user is not interested on accounting for detection efficiency if fitConfiguration["fit"]["escapeflag"]: # but wants to account for escape peaks # we can forget about efficiency but not about detector composition # assign "infinite" efficiency density = 0.0 thickness = 0.0 fisxDetector = Detector(detectorMaterial, density=density, thickness=thickness) else: # user is not interested on considering the escape peaks fisxDetector = None else: # make sure information is consistent if attenuatorsDetector[0] not in [ detectorMaterial, detectorMaterial + "1" ]: print("%s not equal to %s" % (attenuatorsDetector[0], detectorMaterial)) msg = "Inconsistent detector material between DETECTOR and ATTENUATORS tab" msg += "\n%s not equal to %s" % (attenuatorsDetector[0], detectorMaterial) raise ValueError(msg) if len(attenuatorsDetector) == 3: fisxDetector = Detector(detectorMaterial, density=attenuatorsDetector[1], thickness=attenuatorsDetector[2]) else: fisxDetector = Detector(detectorMaterial, density=attenuatorsDetector[1], thickness=attenuatorsDetector[2], funny=attenuatorsDetector[3]) fisxDetector.setActiveArea(area) fisxDetector.setDistance(distance) if fisxDetector is not None: nThreshold = fitConfiguration["detector"]["nthreshold"] fisxDetector.setMaximumNumberOfEscapePeaks(nThreshold) return fisxDetector
def testDetectorResults(self): from fisx import Elements from fisx import Detector elementsInstance = Elements() elementsInstance.initializeAsPyMca() # Make a detector from a formula detectorName = "NaI" detectorDensity = 4.0 detectorThickness = 0.00350 detectorInstance = Detector(detectorName, detectorDensity, detectorThickness) # Check the composition is returned as string and not bytes under # Python 3 composition = detectorInstance.getComposition(elementsInstance) if sys.version_info >= (3, ): for key in composition: self.assertTrue(isinstance(key, str), "Expected string, received %s" % type(key)) # check the returned keys are correct self.assertTrue( len(list(composition.keys())) == 2, "Incorrect number of keys returned") for key in ["Na", "I"]: self.assertTrue(key in composition, "key %s not found" % key) thickness = detectorInstance.getThickness() self.assertTrue( abs(thickness - detectorThickness) < 1.0e-7, "Wrong detector thickness!") density = detectorInstance.getDensity() self.assertTrue( abs(density - detectorDensity) < 1.0e-7, "Wrong detector density!") # check the return of the getEscape method escape = detectorInstance.getEscape(100.0, elementsInstance) if sys.version_info >= (3, ): for key in escape: self.assertTrue(isinstance(key, str), "Expected string, received %s" % type(key))
def _getDetector(webConfiguration): # Detector is described as a list [material, density, thickness] # Translation dictionnary Thickness = "thickness" Density = "density" Material = "material" Area = "area" Distance = "distance" material = webConfiguration["detector"].get(Material, None) if material is None: # No detector return None else: density = float(webConfiguration["detector"][Density]) thickness = float(webConfiguration["detector"][Thickness]) area = float(webConfiguration["detector"][Area]) distance = float(webConfiguration["detector"][Distance]) detectorInstance = Detector(material, density, thickness) detectorInstance.setActiveArea(area) detectorInstance.setDistance(distance) return detectorInstance
def getMultilayerFluorescence(multilayerSample, energyList, layerList=None, weightList=None, flagList=None, fulloutput=None, beamFilters=None, elementsList=None, attenuatorList=None, alphaIn=None, alphaOut=None, cascade=None, detector=None, elementsFromMatrix=False, secondary=None, materials=None, secondaryCalculationLimit=0.0): if DEBUG: print("Library actually using secondary = ", secondary) global xcom if xcom is None: if DEBUG: print("Getting fisx elements instance") xcom = getElementsInstance() if materials is not None: if DEBUG: print("Deleting materials") xcom.removeMaterials() for material in materials: if DEBUG: print("Adding material making sure no duplicates") xcom.addMaterial(material, errorOnReplace=1) # the instance if DEBUG: print("creating XRF instance") xrf = XRF() # the beam energies if not len(energyList): raise ValueError("Empty list of beam energies!!!") if DEBUG: print("setting beam") xrf.setBeam(energyList, weights=weightList, characteristic=flagList) # the beam filters (if any) if beamFilters is None: beamFilters = [] """ Due to wrapping constraints, the filter list must have the form: [[Material name or formula0, density0, thickness0, funny factor0], [Material name or formula1, density1, thickness1, funny factor1], ... [Material name or formulan, densityn, thicknessn, funny factorn]] Unless you know what you are doing, the funny factors must be 1.0 """ if DEBUG: print("setting beamFilters") xrf.setBeamFilters(beamFilters) # the sample description """ Due to wrapping constraints, the list must have the form: [[Material name or formula0, density0, thickness0, funny factor0], [Material name or formula1, density1, thickness1, funny factor1], ... [Material name or formulan, densityn, thicknessn, funny factorn]] Unless you know what you are doing, the funny factors must be 1.0 """ if DEBUG: print("setting sample") xrf.setSample(multilayerSample) # the attenuators if attenuatorList is not None: if len(attenuatorList) > 0: if DEBUG: print("setting attenuators") xrf.setAttenuators(attenuatorList) # the geometry if DEBUG: print("setting Geometry") if alphaIn is None: alphaIn = 45 if alphaOut is None: alphaOut = 45 xrf.setGeometry(alphaIn, alphaOut) # the detector if DEBUG: print("setting Detector") if detector is not None: # Detector can be a list as [material, density, thickness] # or a Detector instance if isinstance(detector, Detector): detectorInstance = detector elif len(detector) == 3: detectorInstance = Detector(detector[0], density=detector[1], thickness=detector[2]) else: detectorInstance = Detector(detector[0], density=detector[1], thickness=detector[2], funny=detector[3]) else: detectorInstance = Detector("") xrf.setDetector(detectorInstance) if detectorInstance.getActiveArea() > 0.0: useGeometricEfficiency = 1 else: useGeometricEfficiency = 0 matrixElementsList = [] for peak in elementsList: ele = peak.split()[0] considerIt = False for layer in multilayerSample: composition = xcom.getComposition(layer[0]) if ele in composition: considerIt = True if considerIt: if peak not in matrixElementsList: matrixElementsList.append(peak) if elementsFromMatrix: elementsList = matrixElementsList # the detector distance and solid angle ??? if elementsList in [None, []]: raise ValueError("Element list not specified") if len(elementsList): if len(elementsList[0]) == 3: # PyMca can send [atomic number, element, peak] actualElementList = [x[1] + " " + x[2] for x in elementsList] elif len(elementsList[0]) == 2: actualElementList = [x[0] + " " + x[1] for x in elementsList] else: actualElementList = elementsList # enabling the cache gets a (miserable) 15 % speed up if DEBUG: print("Using cascade cache") for layer in multilayerSample: composition = xcom.getComposition(layer[0]) for element in composition.keys(): xcom.setElementCascadeCacheEnabled(element, 1) for element in actualElementList: xcom.setElementCascadeCacheEnabled(element.split()[0], 1) if DEBUG: print("Calling getMultilayerFluorescence") t0 = time.time() expectedFluorescence = xrf.getMultilayerFluorescence( actualElementList, xcom, secondary=secondary, useGeometricEfficiency=useGeometricEfficiency, useMassFractions=elementsFromMatrix, secondaryCalculationLimit=secondaryCalculationLimit) if DEBUG: print("C++ elapsed TWO = ", time.time() - t0) if not elementsFromMatrix: # If one element was present in one layer and not on others, PyMca only # calculated contributions from the layers in which the element was # present for peakFamily in matrixElementsList: element, family = peakFamily.split()[0:2] key = element + " " + family if key in expectedFluorescence: # those layers where the amount of the material was 0 have # to present no contribution for iLayer in range(len(expectedFluorescence[key])): layerMaterial = multilayerSample[iLayer][0] if element not in xcom.getComposition(layerMaterial): expectedFluorescence[key][iLayer] = {} return expectedFluorescence
def testXRFResults(self): from fisx import Elements from fisx import Material from fisx import Detector from fisx import XRF elementsInstance = Elements() elementsInstance.initializeAsPyMca() # After the slow initialization (to be made once), the rest is fairly fast. xrf = XRF() xrf.setBeam( 16.0) # set incident beam as a single photon energy of 16 keV xrf.setBeamFilters([["Al1", 2.72, 0.11, 1.0]]) # Incident beam filters # Steel composition of Schoonjans et al, 2012 used to generate table I steel = { "C": 0.0445, "N": 0.04, "Si": 0.5093, "P": 0.02, "S": 0.0175, "V": 0.05, "Cr": 18.37, "Mn": 1.619, "Fe": 64.314, # calculated by subtracting the sum of all other elements "Co": 0.109, "Ni": 12.35, "Cu": 0.175, "As": 0.010670, "Mo": 2.26, "W": 0.11, "Pb": 0.001 } SRM_1155 = Material("SRM_1155", 1.0, 1.0) SRM_1155.setComposition(steel) elementsInstance.addMaterial(SRM_1155) xrf.setSample([["SRM_1155", 1.0, 1.0]]) # Sample, density and thickness xrf.setGeometry(45., 45.) # Incident and fluorescent beam angles detector = Detector("Si1", 2.33, 0.035) # Detector Material, density, thickness detector.setActiveArea(0.50) # Area and distance in consistent units detector.setDistance(2.1) # expected cm2 and cm. xrf.setDetector(detector) Air = Material("Air", 0.0012048, 1.0) Air.setCompositionFromLists( ["C1", "N1", "O1", "Ar1", "Kr1"], [0.0012048, 0.75527, 0.23178, 0.012827, 3.2e-06]) elementsInstance.addMaterial(Air) xrf.setAttenuators([["Air", 0.0012048, 5.0, 1.0], ["Be1", 1.848, 0.002, 1.0]]) # Attenuators fluo = xrf.getMultilayerFluorescence(["Cr K", "Fe K", "Ni K"], elementsInstance, secondary=2, useMassFractions=1) print( "\nElement Peak Energy Rate Secondary Tertiary" ) for key in fluo: for layer in fluo[key]: peakList = list(fluo[key][layer].keys()) peakList.sort() for peak in peakList: # energy of the peak energy = fluo[key][layer][peak]["energy"] # expected measured rate rate = fluo[key][layer][peak]["rate"] # primary photons (no attenuation and no detector considered) primary = fluo[key][layer][peak]["primary"] # secondary photons (no attenuation and no detector considered) secondary = fluo[key][layer][peak]["secondary"] # tertiary photons (no attenuation and no detector considered) tertiary = fluo[key][layer][peak].get("tertiary", 0.0) # correction due to secondary excitation enhancement2 = (primary + secondary) / primary enhancement3 = (primary + secondary + tertiary) / primary print("%s %s %.4f %.3g %.5g %.5g" % \ (key, peak + (13 - len(peak)) * " ", energy, rate, enhancement2, enhancement3)) # compare against expected values from Schoonjans et al. testXMI = True if (key == "Cr K") and peak.startswith("KL3"): second = 1.626 third = 1.671 elif (key == "Cr K") and peak.startswith("KM3"): second = 1.646 third = 1.694 elif (key == "Fe K") and peak.startswith("KL3"): second = 1.063 third = 1.064 elif (key == "Fe K") and peak.startswith("KL3"): second = 1.065 third = 1.066 else: testXMI = False if testXMI: discrepancy = 100 * (abs(second - enhancement2) / second) self.assertTrue(discrepancy < 1.5, "%s %s secondary discrepancy = %.1f %%" % \ (key, peak, discrepancy)) discrepancy = 100 * (abs(third - enhancement3) / third) self.assertTrue(discrepancy < 1.5, "%s %s tertiary discrepancy = %.1f %%" % \ (key, peak, discrepancy))
def getMultilayerFluorescence(multilayerSample, energyList, layerList=None, weightList=None, flagList=None, fulloutput=None, beamFilters=None, elementsList=None, attenuatorList=None, userattenuatorList=None, alphaIn=None, alphaOut=None, cascade=None, detector=None, elementsFromMatrix=False, secondary=None, materials=None, secondaryCalculationLimit=None, cache=1): if secondary is None: secondary = 0 if secondaryCalculationLimit is None: secondaryCalculationLimit = 0.0 if cache: cache = 1 else: cache = 0 _logger.info("Library requested to use secondary = %s", secondary) _logger.info("Library requested to use secondary limit = %s", secondaryCalculationLimit) _logger.info("Library requested to use cache = %d", cache) global xcom if xcom is None: _logger.debug("Getting fisx elements instance") xcom = getElementsInstance() if materials is not None: _logger.debug("Deleting materials") xcom.removeMaterials() for material in materials: _logger.debug("Adding material making sure no duplicates") xcom.addMaterial(material, errorOnReplace=1) # the instance _logger.debug("creating XRF instance") xrf = XRF() # the beam energies if not len(energyList): raise ValueError("Empty list of beam energies!!!") _logger.debug("setting beam") xrf.setBeam(energyList, weights=weightList, characteristic=flagList) # the beam filters (if any) if beamFilters is None: beamFilters = [] """ Due to wrapping constraints, the filter list must have the form: [[Material name or formula0, density0, thickness0, funny factor0], [Material name or formula1, density1, thickness1, funny factor1], ... [Material name or formulan, densityn, thicknessn, funny factorn]] Unless you know what you are doing, the funny factors must be 1.0 """ _logger.debug("setting beamFilters") xrf.setBeamFilters(beamFilters) # the sample description """ Due to wrapping constraints, the list must have the form: [[Material name or formula0, density0, thickness0, funny factor0], [Material name or formula1, density1, thickness1, funny factor1], ... [Material name or formulan, densityn, thicknessn, funny factorn]] Unless you know what you are doing, the funny factors must be 1.0 """ _logger.debug("setting sample") xrf.setSample(multilayerSample) # the attenuators if attenuatorList is not None: if len(attenuatorList) > 0: _logger.debug("setting attenuators") xrf.setAttenuators(attenuatorList) # the user attenuators if userattenuatorList is not None: i = 0 for userAttenuator in userattenuatorList: if isinstance(userAttenuator, tuple) or \ isinstance(userAttenuator, list): energy = userAttenuator[0] transmission = userAttenuator[1] if len(userAttenuator) == 4: name = userAttenuator[2] comment = userAttenuator[3] else: if userattenuatorList[userAttenuator]["use"]: energy = userattenuatorList[userAttenuator]["energy"] transmission = userattenuatorList[userAttenuator][ "transmission"] name = userattenuatorList[userAttenuator].get( "name", "UserFilter%d" % i) name = userattenuatorList[userAttenuator].get( "comment", "") else: continue ttable = TransmissionTable() ttable.setTransmissionTableFromLists(energy, transmission, name, comment) xrf.setTransmissionTable(ttable) # the geometry _logger.debug("setting Geometry") if alphaIn is None: alphaIn = 45 if alphaOut is None: alphaOut = 45 xrf.setGeometry(alphaIn, alphaOut) # the detector _logger.debug("setting Detector") if detector is not None: # Detector can be a list as [material, density, thickness] # or a Detector instance if isinstance(detector, Detector): detectorInstance = detector elif len(detector) == 3: detectorInstance = Detector(detector[0], density=detector[1], thickness=detector[2]) else: detectorInstance = Detector(detector[0], density=detector[1], thickness=detector[2], funny=detector[3]) else: detectorInstance = Detector("") xrf.setDetector(detectorInstance) if detectorInstance.getActiveArea() > 0.0: useGeometricEfficiency = 1 else: useGeometricEfficiency = 0 if elementsList in [None, []]: raise ValueError("Element list not specified") if len(elementsList): if len(elementsList[0]) == 3: # PyMca can send [atomic number, element, peak] actualElementsList = [x[1] + " " + x[2] for x in elementsList] elif len(elementsList[0]) == 2: actualElementsList = [x[0] + " " + x[1] for x in elementsList] else: actualElementsList = elementsList matrixElementsList = [] for peak in actualElementsList: ele = peak.split()[0] considerIt = False for layer in multilayerSample: composition = xcom.getComposition(layer[0]) if ele in composition: considerIt = True if considerIt: if peak not in matrixElementsList: matrixElementsList.append(peak) if elementsFromMatrix: elementsList = matrixElementsList t0 = time.time() if cache: # enabling the cascade cache gets a (miserable) 15 % speed up _logger.debug("FisxHelper Using cache") else: _logger.debug("FisxHelper Not using cache") treatedElements = [] emittedLines = [] for actualElement in actualElementsList: element = actualElement.split()[0] if element not in treatedElements: if cache: lines = xcom.getEmittedXRayLines(element) sampleEnergies = [lines[key] for key in lines] for e in sampleEnergies: if e not in emittedLines: emittedLines.append(e) treatedElements.append(element) for layer in multilayerSample: composition = xcom.getComposition(layer[0]) for element in composition.keys(): if element not in treatedElements: if cache: lines = xcom.getEmittedXRayLines(element) sampleEnergies = [lines[key] for key in lines] for e in sampleEnergies: if e not in emittedLines: emittedLines.append(e) treatedElements.append(element) if attenuatorList is not None: for layer in attenuatorList: composition = xcom.getComposition(layer[0]) for element in composition.keys(): if element not in treatedElements: if cache: lines = xcom.getEmittedXRayLines(element) sampleEnergies = [lines[key] for key in lines] for e in sampleEnergies: if e not in emittedLines: emittedLines.append(e) treatedElements.append(element) if hasattr(xcom, "updateCache"): composition = detectorInstance.getComposition(xcom) for element in composition.keys(): if element not in treatedElements: if cache: lines = xcom.getEmittedXRayLines(element) sampleEnergies = [lines[key] for key in lines] for e in sampleEnergies: if e not in emittedLines: emittedLines.append(e) treatedElements.append(element) for element in actualElementsList: if element.split()[0] not in treatedElements: treatedElements.append(element.split()[0]) for element in treatedElements: # this limit seems overestimated but still reasonable if xcom.getCacheSize(element) > 5000: _logger.info("Clearing cache for %s" % element) xcom.clearCache(element) if cache: _logger.info("Updating cache for %s" % element) xcom.updateCache(element, energyList) xcom.updateCache(element, emittedLines) else: # should I clear the cache to be sure? # for the time being, yes. _logger.info("No cache. Clearing cache for %s" % element) xcom.clearCache(element) xcom.setCacheEnabled(element, cache) _logger.info("Element %s cache size = %d", element, xcom.getCacheSize(element)) for element in actualElementsList: xcom.setElementCascadeCacheEnabled(element.split()[0], cache) if hasattr(xcom, "updateEscapeCache") and \ hasattr(xcom, "setEscapeCacheEnabled"): if detector is not None: for element in actualElementsList: if cache: lines = xcom.getEmittedXRayLines(element.split()[0]) lines_energy = [lines[key] for key in lines] for e in lines_energy: if e not in emittedLines: emittedLines.append(e) if not cache: if hasattr(xcom, "clearEscapeCache"): # the method is there but nor wrapped yet xcom.clearEscapeCache() xcom.setEscapeCacheEnabled(cache) if cache: xcom.updateEscapeCache(detectorInstance.getComposition(xcom), emittedLines, energyThreshold=detectorInstance.getEscapePeakEnergyThreshold(), \ intensityThreshold=detectorInstance.getEscapePeakIntensityThreshold(), \ nThreshold=detectorInstance.getEscapePeakNThreshold(), \ alphaIn=detectorInstance.getEscapePeakAlphaIn(), thickness=0) # No escape by the back considered yet else: _logger.debug("NOT CALLING UPDATE CACHE") _logger.info("C++ elapsed filling cache = %s", time.time() - t0) _logger.debug("Calling getMultilayerFluorescence") t0 = time.time() expectedFluorescence = xrf.getMultilayerFluorescence( actualElementsList, xcom, secondary=secondary, useGeometricEfficiency=useGeometricEfficiency, useMassFractions=elementsFromMatrix, secondaryCalculationLimit=secondaryCalculationLimit) _logger.info("C++ elapsed TWO = %s", time.time() - t0) if not elementsFromMatrix: # If one element was present in one layer and not on others, PyMca only # calculated contributions from the layers in which the element was # present for peakFamily in matrixElementsList: element, family = peakFamily.split()[0:2] key = element + " " + family if key in expectedFluorescence: # those layers where the amount of the material was 0 have # to present no contribution for iLayer in range(len(expectedFluorescence[key])): layerMaterial = multilayerSample[iLayer][0] if element not in xcom.getComposition(layerMaterial): expectedFluorescence[key][iLayer] = {} return expectedFluorescence