Source code for pyMETHES.energy_distribution

#  Copyright (c) 2020-2021 ETH Zurich

"""
Module for the InstantaneousEnergyDistribution and TimeAveragedEnergyDistribution
classes.

Both classed inherit from the abstract class EnergyDistribution. The
InstantaneousEnergyDistribution class can be used to calculate the eedf and eepf
at a given point in time. The TimeAveragedEnergyDistribution class allows for
averaging the eedf and eepf over time. This is done by updating an histogram of
electron energies at every time-step.
"""

# Import Packages
from abc import ABC
import numpy as np

np.seterr(all='raise')


[docs]class EnergyDistribution(ABC): """ Abstract base class for electron energy distributions. Attributes: energy_bins (ndarray): bins to construct the histogram of electron energies energy_bin_centers (ndarray): centers of the energy bins (length - 1) eedf (ndarray): electron energy distribution function (eV-1) eepf (ndarray): electron energy probability function (eV-3/2) """ def __init__(self): self.energy_bins = None self.energy_bin_centers = None self.eedf = None self.eepf = None
[docs] def calculate_bin_centers(self) -> None: """ Calculate the center values of the energy bins. """ self.energy_bin_centers = 0.5 * (self.energy_bins[1:] + self.energy_bins[:-1])
[docs]class InstantaneousEnergyDistribution(EnergyDistribution): """ Stores the energy distribution of electrons. """
[docs] def calculate_distribution(self, energies, energy_bins=None) -> None: """ Calculates the eedf and eepf associated to the input electron energies. Args: energies (ndarray): array of electron energies energy_bins (ndarray): (optional) energy bins for calculating the eedf and eepf. If None, the energy bins are automatically determined with the Freedman Diaconis Estimator. """ self.eepf = None if energy_bins is None: (self.eedf, self.energy_bins) = np.histogram(energies, bins='fd', density=True) else: self.energy_bins = energy_bins self.eedf = np.histogram(energies, bins=energy_bins, density=True)[0] self.calculate_bin_centers() if 0 not in self.energy_bin_centers: self.eepf = self.eedf / np.sqrt(self.energy_bin_centers)
[docs]class TimeAveragedEnergyDistribution(EnergyDistribution): """ Stores the energy distribution of electrons (averaged over time after swarm equilibration). Attributes: cumulative_energy_histogram (ndarray): energy histogram cumulated over every time-step since steady-state """ def __init__(self): super().__init__() self.cumulative_energy_histogram = 0 self.energy_mean = None self.energy_mean_err = None
[docs] def generate_bins(self, num_bins: int, max_energy: float) -> None: """ Generate fixed energy bins for averaging the eedf over time. The bins are spaced linearly and range from 0 to 150% of the maximum electron energy at the step where the method is called. Args: num_bins (int): number of energy bins max_energy (float): maximum electron energy (eV) """ self.energy_bins = np.linspace(0, 1.5 * max_energy, num_bins) self.calculate_bin_centers()
[docs] def collect_histogram(self, energies) -> None: """ Updates the current eedf and eepf, using the electron energies given as input. The energy bins should be already defined and are re-used. This method should be called at every time-step. Args: energies (ndarray): array of electron energies """ self.cumulative_energy_histogram += np.histogram(energies, bins=self.energy_bins)[0]
[docs] def calculate_distribution(self, mean_energies: np.ndarray) -> None: """ Calculates the eedf and eepf using the cumulative energy histogram. Calculates the mean electron energy averaged over time. Args: mean_energies (ndarray): mean energies at every time-step after swarm equilibration """ integral = sum(self.cumulative_energy_histogram * np.diff(self.energy_bins)) self.eedf = self.cumulative_energy_histogram if integral != 0: self.eedf = self.eedf / integral if 0 not in self.energy_bin_centers: self.eepf = self.eedf / np.sqrt(self.energy_bin_centers) # calculate the mean energy self.energy_mean = np.mean(mean_energies) # At least 10 steps are required to estimate the errors if mean_energies.size > 9: # calculate the mean energy error self.energy_mean_err = np.std(mean_energies) / np.sqrt(mean_energies.size)