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]
15class CalibrationLampReferences(Enum): 16 mpq_atto_deuterium_halogen = "7315273LS-Deuterium-Halogen_CC-VISNIR.lmp" 17 mpq_atto_halogen = "7315273LS-Halogen_CC-VISNIR.lmp"
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
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
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
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
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)
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.