def compile_kieft_elastic(outfile, material_params, K, P): print("# Computing Mott cross-sections using ELSEPA.") mott_fn = mott_dimfp(material_params, K[K > 10 * units.eV], threads=4) def elastic_cs_fn(E, costheta): return interpolate_f( lambda E: ac_phonon_dimfp(material_params)(E, costheta), lambda E: mott_fn(E, costheta), 100 * units.eV, 200 * units.eV)(E) print("# Computing elastic total cross-sections and iCDFs.") imfp = np.zeros(K.shape) * units('nm^-1') icdf = np.zeros((K.shape[0], P.shape[0])) * units.dimensionless tl = np.zeros(K.shape) * units.nm for i, E in enumerate(K): _imfp, _icdf = compute_tcs_icdf( lambda costheta: elastic_cs_fn(E, costheta), P, np.linspace(-1, 1, 100000)) imfp[i] = 2 * np.pi * _imfp icdf[i, :] = _icdf _itl = 2 * np.pi * compute_tcs( lambda costheta: elastic_cs_fn(E, costheta) * (1 - costheta), np.linspace(-1, 1, 100000)) tl[i] = 1 / _itl print('.', end='', flush=True) print() group = outfile.create_group("/kieft/elastic") group.add_scale("energy", K, 'eV') group.add_dataset("imfp", imfp, ("energy", ), 'nm^-1') group.add_dataset("costheta_icdf", icdf, ("energy", None), '') group.add_dataset("tl", tl, ("energy", ), 'nm')
def get_property(self, key): value = self.file.attrs[key] if isinstance(value, (float, int)): return value elif isinstance(value, bytes): return value.decode('ascii') else: return value[0] * units(value[1].decode('ascii'))
def load_quantity(yaml_data, name, target_units, optional=False): if optional and name not in yaml_data: return None value = units(yaml_data[name]) if not value.is_compatible_with(target_units): raise RuntimeError('Inconsistent units for {}'.format(name)) return value
def load(self, yaml_data, basedir): try: self.name = yaml_data.get('name') self.density = units(yaml_data['density']) self.elements = [ element(*el) for el in yaml_data['elements'].items() ] self.band_structure = band_structure(yaml_data['band_structure']) self.optical = optical(yaml_data['optical'], basedir) self.phonon = phonon(yaml_data['phonon'], self) except KeyError as ke: raise RuntimeError( "Expected key {} was not found in parameters file".format(ke)) if not self.density.is_compatible_with( units.gram / units.cubic_centimeter): raise RuntimeError("Provided density has inconsistent units")
def IMFP_ICDF(self, E, P): """Get the integrated inverse mean free path (IMFP) and inverse cumulative distribution function (ICDF). Parameters: - E: Energies to evaluate the imfp and icdf at - P: The cumulative probabilities for which the ICDF should be found. Returns: - IMFP: numpy array of len(E) - ICDF: numpy array of (len(E) × len(P)) """ imfp = np.zeros(len(E)) * units('nm^-1') icdf = np.zeros((len(E), len(P_omega))) * self.q.units for i, K in enumerate(E): timfp, ticdf = compute_tcs_icdf(self, P, self.q) imfp[i] = timfp icdf[i] = ticdf return imfp, icdf
def compile_ashley_imfp_icdf(dimfp, K, P_omega): """Compute inverse mean free path, and ω' ICDF for a dielectric function given in the format of Ashley (doi:10.1016/0368-2048(88)80019-7). Ashley transforms the dielectric function ε(ω, q) to ε(ω, ω'); with an analytical relationship between ω and ω'. Therefore, we only need to store the probability distribution for ω'. This function computes the total mean free path and ICDF for ω', given a function dimfp(K, ω'). This function calls dimfp() for ω' between 0 and K, despite conservation of momentum dictating that ω' < K/2. This is because some models (e.g. Kieft, doi:10.1088/0022-3727/41/21/215310) ignore this restriction. Parameters: - dimfp: function, taking 2 parameters (K, ω') and returning differential inverse mean free paths. - K: Array of interesting electron kinetic energies - P_omega: The probabilities for which the ICDF needs to be evaluated Returns: - Inverse mean free path array, same length as K - Inverse cumulative distribution function, a 2D array of shape (len(K) × len(P_omega)) """ imfp = np.zeros(K.shape) * units('nm^-1') icdf = np.zeros((K.shape[0], P_omega.shape[0])) * units.eV for i, E in enumerate(K): tcs, ticdf = compute_tcs_icdf( lambda w: dimfp(E, w), P_omega, np.linspace(0, E.magnitude, 100000) * E.units) imfp[i] = tcs.to('nm^-1') icdf[i] = ticdf.to('eV') print('.', end='', flush=True) print() return imfp, icdf
def compile_full_imfp_icdf(elf_omega, elf_q, elf_data, K, P_omega, n_omega_q, P_q, F): """Compute inverse mean free path, energy transfer ICDF and momentum transfer ICDF for an arbitrary dielectric function ε(ω, q). ε(ω, q) is given by elf_omega, elf_q and elf_data. The data is evaluated for energies given by K. P_omega is the probability axis for the energy loss ICDF. The momentum transfer ICDF is computed for each (K, omega), with the second parameter in n_omega_q evenly-spaced steps between 0 and K. The probability axis for the momentum ICDF is given by P_q. F is the Fermi energy of the material. This is used for a "Fermi correction" to prevent omega > K-F. This function is relativistically correct. Parameters: - elf_omega: ω for which the ELF 1/ε(ω, q) is known - elf_q: q for which the ELF is known - elf_data: The actual data. Shape is (len(ω) × len(q)) - K: Electron kinetic energies to evaluate at. - P_omega: The probabilities to evaluate the ICDF for energy loss at. - n_omega_q: The "energy loss axis" for the momentum ICDF - P_q: The probabilities to evaluate the ICDF for momentum transfer at. - F: The Fermi energy of the material Returns: - Inverse mean free path, same length as K - Stopping power, same length as K - ICDF for energy loss, shape (len(K) × len(P_omega)) - 2D ICDF for momentum transfer, shape (len(K) × n_omega_q × len(P_q)) """ K_units = units.eV q_units = units('nm^-1') mc2 = units.m_e * units.c**2 # Helper function, sqrt(2m/hbar^2 * K(1 + K/mc^2)), appears when getting # momentum boundaries from kinetic energy q_k = lambda _k: np.sqrt(2 * units.m_e * _k * (1 + _k / (2 * mc2))) / units.hbar def elf(omega, q): # Linear interpolation, with extrapolation if out of bounds def find_index(a, v): low_i = np.clip( np.searchsorted(a, v, side='right') - 1, 0, len(a) - 2) vl = a[low_i] vh = a[low_i + 1] return low_i, (v - vl) / (vh - vl) low_x, frac_x = find_index(elf_omega.magnitude, omega.to(elf_omega.units).magnitude) low_y, frac_y = find_index(elf_q.magnitude, q.to(elf_q.units).magnitude) elf = (1-frac_x)*(1-frac_y) * elf_data[low_x, low_y] + \ frac_x*(1-frac_y) * elf_data[low_x+1, low_y] + \ (1-frac_x)*frac_y * elf_data[low_x, low_y+1] + \ frac_x*frac_y * elf_data[low_x+1, low_y+1] elf[elf <= 0] = 0 return elf def q_part(eval_omega, eval_q): # Returns DCS[i,j] = ∫_0^{eval_q[j]} dq/q ELF(eval_omega[i], q) dcs_data = np.divide(elf(eval_omega[:, np.newaxis], eval_q), eval_q.magnitude) for i in range(len(eval_omega)): dcs_data[i, 1:] = cumtrapz(dcs_data[i, :], eval_q.magnitude) dcs_data[i, 0] = 0 return dcs_data eval_omega = np.geomspace(elf_omega[0].to(K_units).magnitude, (K[-1] - F).to(K_units).magnitude, 10000) * K_units eval_q = np.geomspace( (q_k(K[-1]) - q_k(K[-1] - elf_omega[0])).to(q_units).magnitude, 2 * q_k(K[-1]).to(q_units).magnitude, 10000) * q_units dcs_data = q_part(eval_omega, eval_q) inel_imfp = np.zeros(K.shape) * units('nm^-1') inel_sp = np.zeros(K.shape) * units('eV/nm') inel_omega_icdf = np.zeros((K.shape[0], P_omega.shape[0])) * K_units inel_q_2dicdf = np.zeros((K.shape[0], n_omega_q, P_q.shape[0])) * q_units for i, E in enumerate(K): tcs, sp, omega_icdf, q_2dicdf = tcs_2dicdf( dcs_data[eval_omega < E - F, :], eval_omega[eval_omega < E - F], eval_q, lambda omega: q_k(E) - q_k(np.maximum(0 * units.eV, E - omega)), lambda omega: q_k(E) + q_k(np.maximum(0 * units.eV, E - omega)), P_omega, np.linspace(0, (E - F).to(K_units).magnitude, n_omega_q) * K_units, P_q) tcs /= np.pi * units.a_0 * .5 * (1 - 1 / (E / mc2 + 1)**2) * mc2 sp /= np.pi * units.a_0 * .5 * (1 - 1 / (E / mc2 + 1)**2) * mc2 inel_imfp[i] = tcs.to('nm^-1') inel_sp[i] = sp.to('eV/nm') inel_omega_icdf[i] = omega_icdf.to('eV') inel_q_2dicdf[i] = q_2dicdf.to('nm^-1') print('.', end='', flush=True) print() return inel_imfp, inel_sp, inel_omega_icdf, inel_q_2dicdf
def get_dataset(self, name): h5_dset = self.group[name] data = np.copy(h5_dset[()]) * units(h5_dset.attrs['units'].decode('ascii')) return data