attoworld.spectrum

This module will contain functions related to the processing of spectra.

 1"""
 2This module will contain functions related to the processing of spectra.
 3"""
 4
 5from .calibration_data import (
 6    CalibrationData,
 7    get_calibration_path,
 8    CalibrationLampReferences,
 9)
10
11from .spectrum import (
12    wavelength_to_frequency,
13    frequency_to_wavelength,
14    transform_limited_pulse_from_spectrometer,
15)
16from ..personal.marco import (
17    load_calibration_data,
18    read_spectrometer_excel,
19    read_spectrum_ocean_optics,
20)
21
22__all__ = [
23    "CalibrationData",
24    "CalibrationLampReferences",
25    "get_calibration_path",
26    "wavelength_to_frequency",
27    "frequency_to_wavelength",
28    "transform_limited_pulse_from_spectrometer",
29    "load_calibration_data",
30    "read_spectrometer_excel",
31    "read_spectrum_ocean_optics",
32]
class CalibrationData(enum.Enum):
11class CalibrationData(Enum):
12    mpq_atto_reso_marco = "MPQ_Atto_Reso_Spectrometer_Marco.npz"
mpq_atto_reso_marco = <CalibrationData.mpq_atto_reso_marco: 'MPQ_Atto_Reso_Spectrometer_Marco.npz'>
class CalibrationLampReferences(enum.Enum):
15class CalibrationLampReferences(Enum):
16    mpq_atto_deuterium_halogen = "7315273LS-Deuterium-Halogen_CC-VISNIR.lmp"
17    mpq_atto_halogen = "7315273LS-Halogen_CC-VISNIR.lmp"
mpq_atto_deuterium_halogen = <CalibrationLampReferences.mpq_atto_deuterium_halogen: '7315273LS-Deuterium-Halogen_CC-VISNIR.lmp'>
mpq_atto_halogen = <CalibrationLampReferences.mpq_atto_halogen: '7315273LS-Halogen_CC-VISNIR.lmp'>
def get_calibration_path():
6def get_calibration_path():
7    with importlib.resources.path(__name__) as data_path:
8        return data_path
def wavelength_to_frequency( wavelengths_nm: numpy.ndarray, spectrum: numpy.ndarray, frequencies: Optional[numpy.ndarray] = None):
32def wavelength_to_frequency(
33    wavelengths_nm: np.ndarray,
34    spectrum: np.ndarray,
35    frequencies: Optional[np.ndarray] = None,
36):
37    """Convert a wavelength spectrum in W/nm into a frequency spectrum in W/THz
38
39    Args:
40        wavelength_nm (np.ndarray): the wavelengths included in the data, in nanometers
41        spectrum (np.ndarray): the spectrum corresponding to the input wavelength scale
42        frequencies: (optional) frequency vector for the output. If not specified, a vector will be calculated such that resolution and range are preserved.
43    Returns:
44        f: frequencies (Hz)
45        scaled_spectrum: the frequency-domain spectrum
46    """
47    # Contributed by Nick Karpowicz
48    input_frequencies = 1e9 * constants.speed_of_light / wavelengths_nm
49
50    if frequencies is None:
51        frequency_step = np.min(np.abs(np.diff(input_frequencies)))
52        min_frequency = np.min(input_frequencies)
53        max_frequency = np.max(input_frequencies)
54        frequency_count = int(np.ceil(max_frequency - min_frequency) / frequency_step)
55        frequencies = min_frequency + frequency_step * np.array(
56            range(0, frequency_count), dtype=float
57        )
58
59    # apply Jakobian scaling and use W/THz
60    scaled_spectrum = (
61        1e12 * constants.speed_of_light * spectrum / (input_frequencies**2)
62    )
63
64    scaled_spectrum = interpolate(
65        frequencies, input_frequencies, scaled_spectrum, inputs_are_sorted=False
66    )
67
68    return frequencies, scaled_spectrum

Convert a wavelength spectrum in W/nm into a frequency spectrum in W/THz

Arguments:
  • wavelength_nm (np.ndarray): the wavelengths included in the data, in nanometers
  • spectrum (np.ndarray): the spectrum corresponding to the input wavelength scale
  • frequencies: (optional) frequency vector for the output. If not specified, a vector will be calculated such that resolution and range are preserved.
Returns:

f: frequencies (Hz) scaled_spectrum: the frequency-domain spectrum

def frequency_to_wavelength( frequencies: numpy.ndarray, spectrum: numpy.ndarray, wavelengths: Optional[numpy.ndarray] = None):
 8def frequency_to_wavelength(
 9    frequencies: np.ndarray,
10    spectrum: np.ndarray,
11    wavelengths: Optional[np.ndarray] = None,
12):
13    """Convert a frequency spectrum in W/Hz into a wavelength spectrum in W/m.
14    SI units.
15    Args:
16        frequencies (np.ndarray): the frequencies included in the data, in Hz
17        spectrum (np.ndarray): the spectrum corresponding to the input frequency scale
18        wavelengths: (optional) wavelength vector for the output. If not specified, a the output data will be on the same grid, but scaled
19    Returns:
20        wavelengths: wavelengths in m
21        scaled_spectrum: the wavelength-domain spectrum
22    """
23    if wavelengths is None:
24        wavelengths = constants.speed_of_light / frequencies[frequencies > 0.0]
25        spectrum = spectrum[frequencies > 0.0]
26    else:
27        wavelengths = wavelengths[wavelengths > 0.0]
28
29    return wavelengths, spectrum / (wavelengths**2)

Convert a frequency spectrum in W/Hz into a wavelength spectrum in W/m. SI units.

Arguments:
  • frequencies (np.ndarray): the frequencies included in the data, in Hz
  • spectrum (np.ndarray): the spectrum corresponding to the input frequency scale
  • wavelengths: (optional) wavelength vector for the output. If not specified, a the output data will be on the same grid, but scaled
Returns:

wavelengths: wavelengths in m scaled_spectrum: the wavelength-domain spectrum

def transform_limited_pulse_from_spectrometer( wavelengths_nm: numpy.ndarray, spectrum: numpy.ndarray, gate_level: Optional[float] = None):
71def transform_limited_pulse_from_spectrometer(
72    wavelengths_nm: np.ndarray, spectrum: np.ndarray, gate_level: Optional[float] = None
73):
74    """Calculates the transform-limited pulse corresponding to a spectrum
75
76    Args:
77        wavelength_nm: the wavelengths included in the data, in nanometers
78        spectrum: the spectrum corresponding to the input wavelength scale
79        gate_level: (optional) level, relative to the maximum at which to apply a gate to the spectrum. For example, with gate_level=0.01, values less than 1% of the maximum signal will be set to zero
80
81    Returns:
82        t: time vector (s)
83        pulse: the pulse intensity vs. time
84    """
85    f, spec = wavelength_to_frequency(wavelengths_nm, spectrum)
86    if f is None:
87        raise ValueError("No frequencies!")
88    df = f[1] - f[0]
89    t = np.fft.fftshift(np.fft.fftfreq(f.shape[0], d=df))
90    gated_spectrum = np.array(spec)
91    if gate_level is not None:
92        gated_spectrum[spec < (gate_level * np.max(spec))] = 0.0
93
94    pulse = np.fft.fftshift(np.abs(np.fft.ifft(np.sqrt(gated_spectrum)))) ** 2
95
96    return t, pulse

Calculates the transform-limited pulse corresponding to a spectrum

Arguments:
  • wavelength_nm: the wavelengths included in the data, in nanometers
  • spectrum: the spectrum corresponding to the input wavelength scale
  • gate_level: (optional) level, relative to the maximum at which to apply a gate to the spectrum. For example, with gate_level=0.01, values less than 1% of the maximum signal will be set to zero
Returns:

t: time vector (s) pulse: the pulse intensity vs. time

def load_calibration_data(calibration_data_filepath):
10def load_calibration_data(calibration_data_filepath):
11    """
12    Load calibration data in a .npz file for the Reso spectrometer
13
14    Args:
15        calibration_data_filepath (StrOrBytesPath): path of the file to load
16    """
17    try:
18        calibration = np.load(calibration_data_filepath)
19        wavelength_calibration = calibration["wavelength"]
20        lamp_spec = calibration["lamp_ref"]
21        lamp_measbyReso = calibration["lamp_measured"]
22        calibration_smoothed = np.abs(calibration["corr_factor_smoothed"])
23    except FileNotFoundError:
24        print(
25            "Error: calibration data for the UV spectrometer not found.\n"
26            "Please copy the folder Attoworld/src/attoworld/spectrum/calibration_data into your current working directory\n"
27            "or alternatively create a calibration file with relative path ./calibration_data/Reso_Spectrometer_CalibrationCorrection.npz\n"
28        )
29        raise FileNotFoundError("calibration data not found")
30    return wavelength_calibration, lamp_spec, lamp_measbyReso, calibration_smoothed

Load calibration data in a .npz file for the Reso spectrometer

Arguments:
  • calibration_data_filepath (StrOrBytesPath): path of the file to load
def read_spectrometer_excel(filename):
165def read_spectrometer_excel(filename):
166    """Reads xls file (passed as the string filename without extension!) produced by the UV-VIS spectrometer RESONANCE VS-7550-VUV.
167
168    This function currently only works in Linux (Ubuntu), since it uses the OS system command to brutally copy the content of the xls file into a txt;
169    such copying is necessary because the xls file is not readable by pandas (it's a non-standard xls, I couldn't find any other workaround).
170    For windows there is a similar command, please replace the line
171    os.system("cat " + filename + ".xls > " + filename + ".txt")
172
173    44 (skipped) rows at the beginning of the file (all headers)
174
175    Args:
176        filename: name of the file (without extension)
177
178    Returns:
179        numpy array with the spectral data (see original excel for more details)"""
180    if "." in filename and "./" not in filename:
181        raise ValueError(
182            "in function read_spectrometer_excel, filename must be passed without extension (filename should not contain a dot)"
183        )
184    os.system("cat " + filename + ".xls > " + filename + ".txt")
185    dataF = pandas.read_table(
186        filename + ".txt", sep="\t", keep_default_na=False, skiprows=44
187    )  # keep_default_na=False,
188    data = []
189    for row in dataF.values:
190        data.append([])
191        for x in row:
192            if x == "":
193                data[-1].append(float("nan"))
194            else:
195                data[-1].append(float(x))
196    return np.array(data)

Reads xls file (passed as the string filename without extension!) produced by the UV-VIS spectrometer RESONANCE VS-7550-VUV.

This function currently only works in Linux (Ubuntu), since it uses the OS system command to brutally copy the content of the xls file into a txt; such copying is necessary because the xls file is not readable by pandas (it's a non-standard xls, I couldn't find any other workaround). For windows there is a similar command, please replace the line os.system("cat " + filename + ".xls > " + filename + ".txt")

44 (skipped) rows at the beginning of the file (all headers)

Arguments:
  • filename: name of the file (without extension)
Returns:

numpy array with the spectral data (see original excel for more details)

def read_spectrum_ocean_optics(filename):
 92def read_spectrum_ocean_optics(filename):
 93    """Reads a spectrum file from the Ocean Optics spectrometer and returns the wavelength and spectrum arrays.
 94    The file is expected to have one column of wavelength data and one column of spectrum data.
 95    wavelengths are in nm.
 96    14 lines of header are skipped."""
 97    data = pandas.read_table(filename, sep="\t", keep_default_na=True, skiprows=14)
 98    wvl = np.array(data.iloc[:, 0])
 99    spectrum = np.array(data.iloc[:, 1])
100    return wvl, spectrum

Reads a spectrum file from the Ocean Optics spectrometer and returns the wavelength and spectrum arrays. The file is expected to have one column of wavelength data and one column of spectrum data. wavelengths are in nm. 14 lines of header are skipped.