def _getChantler(self, element, energy, column='f1', smoothing=0): """return energy-dependent data from Chantler table columns: f1, f2, mu_photo, mu_incoh, mu_total """ tab = ChantlerTable row = self.query(tab) if isinstance(element, int): row = row.filter(tab.id == element).all() else: row = row.filter(tab.element == element.title()).all() if len(row) > 0: row = row[0] if isinstance(row, tab): energy = as_ndarray(energy) emin, emax = min(energy), max(energy) # te = self.chantler_energies(element, emin=emin, emax=emax) te = np.array(json.loads(row.energy)) nemin = max(0, -5 + max(np.where(te <= emin)[0])) nemax = min(len(te), 6 + max(np.where(te <= emax)[0])) region = np.arange(nemin, nemax) te = te[region] if column == 'mu': column = 'mu_total' ty = np.array(json.loads(getattr(row, column)))[region] if column == 'f1': out = UnivariateSpline(te, ty, s=smoothing)(energy) else: out = np.exp(np.interp(np.log(energy), np.log(te), np.log(ty))) if isinstance(out, np.ndarray) and len(out) == 1: return out[0] return out
def _getChantler(self, element, energy, column='f1', smoothing=0): """return energy-dependent data from Chantler table columns: f1, f2, mu_photo, mu_incoh, mu_total """ tab = ChantlerTable row = self.query(tab) if isinstance(element, int): row = row.filter(tab.id==element).all() else: row = row.filter(tab.element==element.title()).all() if len(row) > 0: row = row[0] if isinstance(row, tab): energy = as_ndarray(energy) emin, emax = min(energy), max(energy) # te = self.chantler_energies(element, emin=emin, emax=emax) te = np.array(json.loads(row.energy)) nemin = max(0, -5 + max(np.where(te<=emin)[0])) nemax = min(len(te), 6 + max(np.where(te<=emax)[0])) region = np.arange(nemin, nemax) te = te[region] if column == 'mu': column = 'mu_total' ty = np.array(json.loads(getattr(row, column)))[region] if column == 'f1': out = UnivariateSpline(te, ty, s=smoothing)(energy) else: out = np.exp(np.interp(np.log(energy), np.log(te), np.log(ty))) if isinstance(out, np.ndarray) and len(out) == 1: return out[0] return out
def f0(self, ion, q): """Calculate f0(q) -- elastic x-ray scattering factor from Waasmaier and Kirfel arguments --------- ion: atomic number, atomic symbol or ionic symbol (case insensitive) of scatterer q: single q value, list, tuple, or numpy array of q value q = sin(theta) / lambda theta = incident angle, lambda = x-ray wavelength Z values from 1 to 98 (and symbols 'H' to 'Cf') are supported. The list of ionic symbols can be read with the function .f0_ions() """ tab = WaasmaierTable row = self.query(tab) if isinstance(ion, int): row = row.filter(tab.atomic_number == ion).all() else: row = row.filter(tab.ion == ion.title()).all() if len(row) > 0: row = row[0] if isinstance(row, tab): q = as_ndarray(q) f0 = row.offset for s, e in zip(json.loads(row.scale), json.loads(row.exponents)): f0 += s * np.exp(-e * q * q) return f0
def f0(self, ion, q): """Calculate f0(q) -- elastic x-ray scattering factor from Waasmaier and Kirfel arguments --------- ion: atomic number, atomic symbol or ionic symbol (case insensitive) of scatterer q: single q value, list, tuple, or numpy array of q value q = sin(theta) / lambda theta = incident angle, lambda = x-ray wavelength Z values from 1 to 98 (and symbols 'H' to 'Cf') are supported. The list of ionic symbols can be read with the function .f0_ions() """ tab = WaasmaierTable row = self.query(tab) if isinstance(ion, int): row = row.filter(tab.atomic_number==ion).all() else: row = row.filter(tab.ion==ion.title()).all() if len(row) > 0: row = row[0] if isinstance(row, tab): q = as_ndarray(q) f0 = row.offset for s, e in zip(json.loads(row.scale), json.loads(row.exponents)): f0 += s * np.exp(-e*q*q) return f0
def Elam_CrossSection(self, element, energies, kind='photo'): """returns Elam Cross Section values for an element and energies arguments --------- element: atomic number, atomic symbol for element energies: energies in eV to calculate cross-sections kind: one of 'photo', 'coh', and 'incoh' for photo-absorption, coherent scattering, and incoherent scattering cross sections, respectively. Data from Elam, Ravel, and Sieber. """ if isinstance(element, int): element = self.symbol(element) energies = 1.0 * as_ndarray(energies) tab = ScatteringTable if kind == 'photo': tab = PhotoAbsorptionTable row = self.query(tab).filter(tab.element == element.title()).all() if len(row) > 0: row = row[0] if not isinstance(row, tab): return None tab_lne = np.array(json.loads(row.log_energy)) if kind.lower().startswith('coh'): tab_val = np.array(json.loads(row.log_coherent_scatter)) tab_spl = np.array(json.loads(row.log_coherent_scatter_spline)) elif kind.lower().startswith('incoh'): tab_val = np.array(json.loads(row.log_incoherent_scatter)) tab_spl = np.array(json.loads(row.log_incoherent_scatter_spline)) else: tab_val = np.array(json.loads(row.log_photoabsorption)) tab_spl = np.array(json.loads(row.log_photoabsorption_spline)) emin_tab = 10 * int(0.102 * np.exp(tab_lne[0])) energies[np.where(energies < emin_tab)] = emin_tab out = np.exp(elam_spline(tab_lne, tab_val, tab_spl, np.log(energies))) if len(out) == 1: return out[0] return out
def Elam_CrossSection(self, element, energies, kind='photo'): """returns Elam Cross Section values for an element and energies arguments --------- element: atomic number, atomic symbol for element energies: energies in eV to calculate cross-sections kind: one of 'photo', 'coh', and 'incoh' for photo-absorption, coherent scattering, and incoherent scattering cross sections, respectively. Data from Elam, Ravel, and Sieber. """ if isinstance(element, int): element = self.symbol(element) energies = 1.0 * as_ndarray(energies) tab = ScatteringTable if kind == 'photo': tab = PhotoAbsorptionTable row = self.query(tab).filter(tab.element==element.title()).all() if len(row) > 0: row = row[0] if not isinstance(row, tab): return None tab_lne = np.array(json.loads(row.log_energy)) if kind.lower().startswith('coh'): tab_val = np.array(json.loads(row.log_coherent_scatter)) tab_spl = np.array(json.loads(row.log_coherent_scatter_spline)) elif kind.lower().startswith('incoh'): tab_val = np.array(json.loads(row.log_incoherent_scatter)) tab_spl = np.array(json.loads(row.log_incoherent_scatter_spline)) else: tab_val = np.array(json.loads(row.log_photoabsorption)) tab_spl = np.array(json.loads(row.log_photoabsorption_spline)) emin_tab = 10*int(0.102*np.exp(tab_lne[0])) energies[np.where(energies < emin_tab)] = emin_tab out = np.exp(elam_spline(tab_lne, tab_val, tab_spl, np.log(energies))) if len(out) == 1: return out[0] return out
def elam_spline(xin, yin, yspl_in, x): """ interpolate values from Elam photoabsorption and scattering tables, according to Elam, Numerical Recipes. Calc borrowed from D. Dale. """ x = as_ndarray(x) x[np.where(x < min(xin))] = min(xin) x[np.where(x > max(xin))] = max(xin) lo, hi = np.array([ (np.flatnonzero(xin < e)[-1], np.flatnonzero(xin > e)[0]) for e in x ]).transpose() diff = xin[hi] - xin[lo] if any(diff <= 0): raise ValueError('x must be strictly increasing') a = (xin[hi] - x) / diff b = (x - xin[lo]) / diff return (a * yin[lo] + b * yin[hi] + (diff * diff / 6) * ((a * a - 1) * a * yspl_in[lo] + (b * b - 1) * b * yspl_in[hi]))
def elam_spline(xin, yin, yspl_in, x): """ interpolate values from Elam photoabsorption and scattering tables, according to Elam, Numerical Recipes. Calc borrowed from D. Dale. """ x = as_ndarray(x) x[np.where(x < min(xin))] = min(xin) x[np.where(x > max(xin))] = max(xin) lo, hi = np.array([(np.flatnonzero(xin < e)[-1], np.flatnonzero(xin > e)[0]) for e in x]).transpose() diff = xin[hi] - xin[lo] if any(diff <= 0): raise ValueError('x must be strictly increasing') a = (xin[hi] - x) / diff b = (x - xin[lo]) / diff return (a * yin[lo] + b * yin[hi] + (diff*diff/6) * ((a*a - 1) * a * yspl_in[lo] + (b*b - 1) * b * yspl_in[hi] ))
def f1f2(z, energies, width=None, edge=None, _larch=None): """Return anomalous scattering factors f1, f2 from Cromer-Liberman Look-up and return f1, f2 for an element and array of energies from Cromer-Liberman (Cowan-Brennan implementation) Parameters ---------- z: atomic number of element energies: array of x-ray energies (in eV) width: width used to convolve values with lorentzian profile edge: x-ray edge ('K', 'L3', etc) used to lookup energy width for convolution. Returns: --------- f1, f2: anomalous scattering factors """ global CLLIB if CLLIB is None: CLLIB = get_dll('cldata') en = as_ndarray(energies) if not isinstance(z, int): z = atomic_number(z, _larch=_larch) if z is None: return None if z > 92: print( 'Cromer-Liberman data not available for Z>92') return if edge is not None or width is not None and _larch is not None: natwid = core_width(element=z, edge=edge, _larch=_larch) if width is None and natwid not in (None, []): width = natwid if width is not None: # will convolve! e_extra = int(width*80.0) estep = (en[1:] - en[:-1]).min() emin = min(en) - e_extra emax = max(en) + e_extra npts = 1 + abs(emax-emin+estep*0.02)/abs(estep) en = np.linspace(emin, emax, npts) nk = int(e_extra / estep) sig = width/2.0 lor = (1./(1 + ((np.arange(2*nk+1)-nk*1.0)/sig)**2))/(np.pi*sig) scale = lor.sum() # create ctypes pointers for the C function npts = len(en) p_z = ctypes.pointer(ctypes.c_int(int(z))) p_npts = ctypes.pointer(ctypes.c_int(npts)) p_en = (npts*ctypes.c_double)() p_f1 = (npts*ctypes.c_double)() p_f2 = (npts*ctypes.c_double)() for i in range(npts): p_en[i] = en[i] nout = CLLIB.f1f2(p_z, p_npts, p_en, p_f1, p_f2) f1 = np.array([i for i in p_f1[:]]) f2 = np.array([i for i in p_f2[:]]) if width is not None: # do the convolution f1 = np.interp(energies, en, convolve(f1, lor)[nk:-nk])/scale f2 = np.interp(energies, en, convolve(f2, lor)[nk:-nk])/scale return (f1, f2)
def f1f2(z, energies, width=None, edge=None, _larch=None): """Return anomalous scattering factors f1, f2 from Cromer-Liberman Look-up and return f1, f2 for an element and array of energies from Cromer-Liberman (Cowan-Brennan implementation) Parameters ---------- z: atomic number of element energies: array of x-ray energies (in eV) width: width used to convolve values with lorentzian profile edge: x-ray edge ('K', 'L3', etc) used to lookup energy width for convolution. Returns: --------- f1, f2: anomalous scattering factors """ global CLLIB if CLLIB is None: CLLIB = get_dll('cldata') en = as_ndarray(energies) if not isinstance(z, int): z = atomic_number(z, _larch=_larch) if z is None: return None if z > 92: print('Cromer-Liberman data not available for Z>92') return if edge is not None or width is not None and _larch is not None: natwid = core_width(element=z, edge=edge, _larch=_larch) if width is None and natwid not in (None, []): width = natwid if width is not None: # will convolve! e_extra = int(width * 80.0) estep = (en[1:] - en[:-1]).min() emin = min(en) - e_extra emax = max(en) + e_extra npts = 1 + abs(emax - emin + estep * 0.02) / abs(estep) en = np.linspace(emin, emax, npts) nk = int(e_extra / estep) sig = width / 2.0 lor = (1. / (1 + ((np.arange(2 * nk + 1) - nk * 1.0) / sig)**2)) / (np.pi * sig) scale = lor.sum() # create ctypes pointers for the C function npts = len(en) p_z = ctypes.pointer(ctypes.c_int(int(z))) p_npts = ctypes.pointer(ctypes.c_int(npts)) p_en = (npts * ctypes.c_double)() p_f1 = (npts * ctypes.c_double)() p_f2 = (npts * ctypes.c_double)() for i in range(npts): p_en[i] = en[i] nout = CLLIB.f1f2(p_z, p_npts, p_en, p_f1, p_f2) f1 = np.array([i for i in p_f1[:]]) f2 = np.array([i for i in p_f2[:]]) if width is not None: # do the convolution f1 = np.interp(energies, en, convolve(f1, lor)[nk:-nk]) / scale f2 = np.interp(energies, en, convolve(f2, lor)[nk:-nk]) / scale return (f1, f2)