hqs_nmr.spectrumio

Deserialization and serialization from various NMR spectrum formats.

Functions

bruker_dir_to_jdx(directory, molecule_name)

Read spectral data from a Bruker directory and write it to a JCAMP-DX file.

read_bruker_dir(directory[, source, license_id])

Read spectral data from Bruker directory.

read_jdx(file[, source, license_id])

Read spectral data from a JCAMP-DX (JDX) file.

read_jdx_string(file_content[, source, ...])

Parses JDX spectral data from string.

read_magritek_dir(directory[, ...])

Read spectrum data from Magritek Spinsolve folder.

write_jdx(x_data, y_data, frequency_MHz[, ...])

Write a JCAMP-DX file.

Classes

SpectralBounds(first, last[, min, max])

An object for storing key points of spectral data.

class hqs_nmr.spectrumio.SpectralBounds(first: float, last: float, min: float | None = None, max: float | None = None)

An object for storing key points of spectral data.

Note, per JCAMP-DX 5.0 specification the keywords ‘MINX’ and ‘MAXX’ are optional. Also, ‘MAXX’ does not necessarily correspond to the first value (‘FIRSTX’).

first: float
last: float
min: float | None = None
max: float | None = None
__init__(first: float, last: float, min: float | None = None, max: float | None = None) → None
hqs_nmr.spectrumio._extract_solvent_jcampdx(data: dict[str, Any]) → list[str]

Extracts solvent string from a JCAMP-DX data dictionary if available.

With JCAMP-DX version 5.01 a new optional “.SOLVENT NAME” key has been added to store a description of the solvent. According to the specification this may include pH, ionic strength, if relevant. This function does not distinguish the solvent name from such additional data.

A few notes regarding the parsing via the nmrglue package: Since a JCAMP-DX file may contain more than one block (ends with “##END”), nmrglue collects all values of a key in a list. Furthermore, nmrglue normalizes the key in the data dictionary, i.e. while the JCAMP-DX file may contain “##.SOLVENTNAME” or “##.SOLVENT NAME”, the resulting data dictionary only contains a “.SOLVENTNAME” key in both cases.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Solvent string(s) or an empty list if no solvent information was found.

hqs_nmr.spectrumio._extract_solvent_bruker(data: dict[str, Any]) → str

Extracts solvent string from a Bruker data dictionary if available.

The Bruker directory format stores the solvent name in the acquisition file (acqus or acqu) under the non-standard key “##$SOLVENT”. The solvent name is usually enclosed in angle brackets (e.g. <DMSO>), which are stripped of with this function.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Solvent string or an empty string if no solvent information was found.

hqs_nmr.spectrumio._extract_solvent_magritek(data: dict[str, Any]) → str

Extracts solvent name from data dictionary from Magritek.

When reading a full directory, solvent should come from the acquisition parameters. If the FID data in a JCAMP-DX file has been loaded, solvent would try to be read from there if the first option fails or if it is the only available file in the directory.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Solvent string(s) or an empty list if no solvent information was found.

hqs_nmr.spectrumio._extract_temperature_jcampdx(data: dict[str, Any]) → list[float]

Extracts temperature (in Kelvin) from a JCAMP-DX data dictionary if available.

JCAMP-DX files store the temperature in degrees Celsius in the “##TEMPERATURE” field. Note that this function returns the temperature in Kelvin.

A few notes regarding the parsing via the nmrglue package: Since a JCAMP-DX file may contain more than one block (ends with “##END”), nmrglue collects all values of a key in a list.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Temperature(s) in Kelvin or an empty list if no temperature was found.

hqs_nmr.spectrumio._extract_temperature_bruker(data: dict[str, Any]) → float | None

Extracts temperature (in Kelvin) from a Bruker data dictionary if available.

The Bruker directory format stores the temperature (in Kelvin) in the acquisition file (acqus or acqu) under the non-standard key “##$TE”. The data dictionary produced by nmrglue.bruker.read_pdata holds the temperature already as a float.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Temperature in Kelvin or None if no solvent information was found.

hqs_nmr.spectrumio._extract_temperature_magritek(data: dict[str, Any]) → float | None

Extracts temperature from data dictionary from Magritek.

When reading a full directory, temperature should come from the acquisition parameters. Alternatively, if the FID data in a JCAMP-DX file has been loaded, temperature would try to be read from there. If the temperature come from the acquisition parameters, an average of the temperature of the benchtop box at the beginning (“StartProtocolTemperatureBox”) and end of the experiment (“CurrentTemperatureBox”) is taken.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Temperature in Kelvin, if available, otherwise None.

hqs_nmr.spectrumio._extract_isotopes_jcampdx(data: dict[str, Any]) → list[Isotope]

Extracts isotope from a JCAMP-DX data dictionary.

JCAMP-DX files store the isotope in the “##.OBSERVE NUCLEUS” field. The corresponding value usually contains a leading caret (e.g. ^13C), which is removed in this function.

A few notes regarding the parsing via the nmrglue package: Since a JCAMP-DX file may contain more than one block (ends with “##END”), nmrglue collects all values of a key in a list. Furthermore, nmrglue normalizes the key in the data dictionary, i.e. while the JCAMP-DX file may contain “##.OBSERVENUCLEUS” or “##.OBSERVE NUCLEUS”, the resulting data dictionary only contains a “.OBSERVENUCLEUS” key in both cases. NOTE: If both “.OBSERVENUCLEUS” and “.OBSERVE NUCLEUS” exist in the JCAMP-DX file, the nmrglue package unfortunately merges the two keys resulting in additional items in the isotope list. This function ensures that the isotope list has as many elements as the raw data for “.OBSERVEFREQUENCY”.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

List of isotopes.

hqs_nmr.spectrumio._extract_isotope_bruker(data: dict[str, Any]) → Isotope

Extracts isotope from a Bruker data dictionary.

The Bruker directory format stores the isotope in the processing file (procs or proc) “##$AXNUC”. The isotope string is usually enclosed in angle brackets (e.g. <1H>). Contrary to the solvent name, the isotope string stored in the data dictionary does not include the angle brackets. If the “##$AXNUC” key cannot be found in the procs file, it is attempted to extract the isotope from the acqus file instead (“##$NUC1” key).

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Isotope of observed nucleus.

hqs_nmr.spectrumio._extract_isotope_magritek(data: dict[str, Any]) → Isotope

Extracts isotope from data dictionary from Magritek.

When reading a full directory, isotope should come from the acquisition parameters. If a JCAMP-DX file was loaded (with FID data), isotope would also try to be read from there.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

Isotope of observed nucleus.

hqs_nmr.spectrumio._extract_spectrometer_freq_magritek(data: dict[str, Any]) → float

Extracts spectrometer frequency from data dictionary from Magritek.

When reading a full directory, spectrometer frequency should come from the acquisition parameters. If a JCAMP-DX file was loaded (with FID data), it would also try to be read from there.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Raises:

ValueError – Spectrometer frequency cannot be extracted.

Returns:

Spectrometer Frequency in MHz.

hqs_nmr.spectrumio._extract_xdata_jcampdx(jdx_content: str, npoints: int) → list[ndarray]

Extracts x-data (chemical shift values) directly from a JCAMP-DX file.

Custom extraction of x-data for the (XY..XY) format, which allows no even spacing along the x-axis. Note that nmrglue does neither support the XYPOINTS data class, nor returning the x array when the (XY..XY) specifier is used as XYDATA.

NOTE: it is assumed that the JCAMP-DX file contains only one block of XYDATA data.

This function can only correctly parse numbers in decimal notation, not scientific.

Parameters:
  • jdx_content – Content of the JCAMP-DX file.

  • npoints – Number of points in the spectrum.

Raises:

ValueError – Inconsistent JDX parameters.

Returns:

x-data with the real values of the (XY..XY) block.

hqs_nmr.spectrumio._extract_xbounds_jcampdx_ntuples(data: dict[str, Any]) → list[SpectralBounds]

Extracts frequency bounds for the ‘NTUPLES’ data class.

Note the following assumptions for are made regarding the data dictionary: 1. The parameters “FIRST” and “LAST” are given as strings containing comma-separated values,

i.e., require splitting and type conversion. The first column in the stringified lists corresponds to the x-data. For instance:

>>> data['MAX']
['9615.23789625902, 509205241,    0']
>>> data["FIRST"]
['9615.23789625902, -63493,       -514735']
  1. The values are given in Hertz.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

List of spectral bounds objects each containing first and last frequency value in Hz. If available, the object also contains the minimal and maximal frequencies.

Raises:

RuntimeError – Unable to extract minimal or maximal frequencies.

hqs_nmr.spectrumio._extract_xbounds_jcampdx_xydata(data: dict[str, Any]) → list[SpectralBounds]

Extracts frequency bounds for the ‘XYDATA’ data class.

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

List of spectral bounds objects each containing first and last frequency value in Hz. If available, the object also contains the minimal and maximal frequencies.

hqs_nmr.spectrumio._fourier_transformation(data: dict[str, Any], ydata: ndarray) → tuple[ndarray, ndarray]

Perform Fourier transformation of the NMR FID using nmrglue.

The parameters for Fourier transformation will be taken from the acquisition and processing parameters files, if available. If a JCAMP-DX file was loaded (with FID data), it would also try to be read from there.

Parameters:
  • data – Data dictionary resulting from parsing with nmrglue.

  • ydata – NMR FID data.

Raises:

ValueError – Missing required parameters for Fourier transformation.

Returns:

Tuple of Fourier transformed x-data and y-data.

hqs_nmr.spectrumio._sort_spectral_data(x: ndarray | None, y: ndarray | None) → tuple[ndarray, ndarray]

Enforce internal convention for sorting spectral data (increasing frequencies).

Parameters:
  • x – Array of frequency values (e.g. in ppm or Hz).

  • y – Array of intensity values corresponding to x.

Raises:

ValueError – If either x or y is None.

Returns:

Tuple of (x, y) arrays sorted in ascending order of x.

hqs_nmr.spectrumio._jcampdx_xydata(data: dict[str, Any], values: ndarray, jdx_path: Path | str) → tuple[ndarray, ndarray]

Extract and return sorted spectral data from a JCAMP-DX file using the XYDATA data class.

Supports both the (XY..XY) (arbitrary x-spacing) and X++(Y..Y) (even x-spacing) XYDATA formats. For (XY..XY), x-values are read directly from the file via _extract_xdata_jcampdx. For X++(Y..Y), x-values are reconstructed as a linearly spaced array from FIRSTX (or MAXX) to LASTX (or MINX).

Parameters:
  • data – Data dictionary resulting from parsing with nmrglue.

  • values – Y-data array as returned by nmrglue.jcampdx.read.

  • jdx_path – Path to the original JCAMP-DX file, used to inspect XYDATA format and to extract x-values for the (XY..XY) format.

Raises:
  • ValueError – If values is not a numpy.ndarray, or if the extracted x-data length does not match NPOINTS.

  • NotImplementedError – If the JCAMP-DX file contains multiple spectra (compound files), or if no supported ##XYDATA format specifier is found.

Returns:

Tuple of (x, y) arrays sorted in ascending order of x (frequency in Hz).

hqs_nmr.spectrumio._jcampdx_ntuples(data: dict[str, Any], values: list[ndarray]) → tuple[ndarray, ndarray]

Extract and return sorted spectral data from a JCAMP-DX file using the NTUPLES data class.

Reconstructs the x-axis as a linearly spaced array between the minimum and maximum frequency extracted via _extract_xbounds_jcampdx_ntuples, using the number of points given by the first column of VARDIM. Only the first array in values is used as y-data.

If the ‘.SHIFT REFERENCE’ key is available, the frequency array is re-referenced using the reference shift value and index ref_idx from this key, such that the final omega array contains the reference shift value at index ref_idx.

Parameters:
  • data – Data dictionary resulting from parsing with nmrglue. Must contain the keys VARDIM and at least one of MIN/LAST and MAX/FIRST for frequency bounds.

  • values – List of y-data arrays as returned by nmrglue.jcampdx.read for the NTUPLES data class.

Raises:

TypeError – If the NTUPLES spectral data does not have the expected type.

Returns:

Tuple of (x, y) arrays sorted in ascending order of x (frequency in Hz).

hqs_nmr.spectrumio._extract_shift_reference_jcampdx(data: dict[str, Any]) → list[tuple[int, float]]

Extract and parse the ‘.SHIFT REFERENCE’ values from a JCAMP-DX data dictionary.

Note that the key ‘.SHIFT REFERENCE’ is defined in JCAMP-DX v5.01 and contains a reference shift value at a certain data point (1-indexed).

Parameters:

data – Data dictionary resulting from parsing with nmrglue.

Returns:

A list of (index, reference value) tuples derived from all ‘SHIFT REFERENCE’ values.

hqs_nmr.spectrumio._apply_shift_reference_jcampdx(shift_reference: tuple[int, float], x_bounds: SpectralBounds, freq_MHz: float, xdata: ndarray) → ndarray

Apply a shift reference correction to a JCAMP-DX frequency axis.

The JCAMP-DX v5.01 ‘.SHIFT REFERENCE’ field provides a known chemical-shift value (in ppm) at a specific 1-indexed data point. This function uses that anchor to compute a constant frequency offset and shifts the entire x-axis array accordingly.

The mapping from the 1-indexed reference point to the array index accounts for the direction of the x-axis: when x_bounds.first < x_bounds.last (ascending order), the data are stored from low to high frequency, so the 1-indexed point from the right (JCAMP-DX convention) maps to n_points - ref_idx. In the descending case the mapping is ref_idx - 1.

Parameters:
  • shift_reference – A (ref_idx, ref_val_ppm) tuple where ref_idx is the 1-based data-point index of the reference peak and ref_val_ppm is its known chemical shift in ppm.

  • x_bounds – Spectral bounds object used to determine whether the frequency axis is stored in ascending or descending order.

  • freq_MHz – Spectrometer (observe) frequency in MHz, used to convert ref_val_ppm to Hz.

  • xdata – Frequency array in Hz to be corrected. Modified in-place and also returned.

Returns:

The corrected frequency array in Hz (same object as xdata).

hqs_nmr.spectrumio.read_jdx(file: Path | str, source: str = 'Unknown', license_id: str = 'Unknown') → NMRExperimentalSpectrum1D

Read spectral data from a JCAMP-DX (JDX) file.

NOTE: it is assumed that the JCAMP-DX file contains only one block, i.e. one spectrum and one isotope.

Parameters:
  • file – Path to the JDX file.

  • source – Owner or producer of the spectrum.

  • license_id – License associated with the spectrum (as SPDX identifier if possible).

Raises:
  • RuntimeError – Parsing failed.

  • NotImplementedError – Unsupported spectral data.

  • ValueError – Inconsistent JDX parameters.

Returns:

A NMRExperimentalSpectrum1D object with chemical shifts (in ppm) and intensities.

hqs_nmr.spectrumio.read_jdx_string(file_content: str, source: str = 'Unknown', license_id: str = 'Unknown') → tuple[ndarray, ndarray]

Parses JDX spectral data from string.

Note this function creates a temporary file in order to match the function interface of nmrglue.jcampdx.read.

Parameters:
  • file_content – Content of a JCAMP-DX file.

  • source – Owner or producer of the spectrum.

  • license_id – License associated with the spectrum (as SPDX identifier if possible).

Returns:

A tuple of chemical shifts (in ppm) and intensities.

hqs_nmr.spectrumio.read_bruker_dir(directory: Path | str, source: str = 'Unknown', license_id: str = 'Unknown') → NMRExperimentalSpectrum1D

Read spectral data from Bruker directory.

Parameters:
  • directory – Path to the “pdata” subdirectory of the Bruker directory, for example bmse0001230/pdata/1.

  • source – Owner or producer of the spectrum.

  • license_id – License associated with the spectrum (as SPDX identifier if possible).

Returns:

A NMRExperimentalSpectrum1D object containing chemical shifts (in ppm) and intensities.

hqs_nmr.spectrumio.read_magritek_dir(directory: Path | str, main_spectrum_file: Path | str | None = None, source: str = 'Unknown', license_id: str = 'Unknown') → NMRExperimentalSpectrum1D

Read spectrum data from Magritek Spinsolve folder.

The function nmrglue.spinsolve.read can read the files “nmr_fid.dx”, “data.1d”, “fid.1d”, “spectrum.1d”, and “spectrum_processed.1d” in that order of priority to get the spectral data. In addition, it reads the parameter files such as “acqu.par” (acquisition parameters) and “proc.par” (processing parameters), if present.

The output from nmrglue.spinsolve.read needs to be interpreted. It is a tuple where the first element is a nested dictionary with relevant information about the experiment. The second element is an array with the spectral points (ydata). The content of the nested dictionary depends on the file from where the spectral data is imported. In this function, four spectrum files are considered (in the following order):

1) The spectrum_processed.1d” binary file containing data in the frequency domain (Fourier-transformed spectral data and processed by Spinsolve). 2) The “spectrum.1d” binary file containing data in the frequency domain (Fourier-transformed spectral data and a frequency axis, before any processing is applied). 3) The “nmr_fid.dx” file containing NMR FID data in a JCAMP-DX standard file format (Fourier transform is required). 4) The “data.1d” binary file containing NMR FID data in the time domain.Therefore, Fourier transform is required and important parameters will be taken from the acquisition and the processing parameters files.

For now, reading an “fid.1d“ file, which appears to be an output of the Spinsolve Expert software, is not supported.

Note that the default order of spectrum files is changed prioritizing “spectrum_processed.1d” or “spectrum.1d” over “nmr_fid.dx” and “data.1d” to potentially avoid an unnecessary Fourier transformation.

Information related with the experiment will be primary extracted from the acquisition (“acqu.par”) and the processing (“proc.par”) parameters files. Note that depending on the spectrum file that is read, the parameters dictionary will have different content. If a “spectrum*.1d” file is found, the “spectrum” dictionary will have the spectral data and if the JCAMP-DX file is read the “dx” dictionary will contain important information. When dealing with full folders, the acquisition parameters will be read from the “acqu.par” file, from which information related to the experiment will be retrieved. That is important in combination with the “spectrum*.1d” binary file, since the “spectrum” dictionary only contains data points information (e.g., x-axis in ppm). When reading the JCAMP-DX file, the “dx” dictionary will contain the input to perform a Fourier transform, as well as other experimental details. In the case of reading the “data.1d” binary file, the “spectrum” dictionary will have some x-data points in the time domain that we will not be used, since for the Fourier transform, the important parameters will be taken from the acquisition (“acqu.par”) and the processing (“proc.par”) parameters files.

Alternatively, one an indicate which file to prioritize by setting the main_spectrum_file argument. This is useful if we only have a JCAMP-DX file with the FID data, since a standalone JCAMP-DX file normally contains the most relevant experimental parameters.

Parameters:
  • directory – Folder name containing the Magritek spectrum data.

  • main_spectrum_file – Name of the spectral file to be prioritized.

  • source – Owner or producer of the spectrum.

  • license_id – License associated with the spectrum (as SPDX identifier if possible).

Raises:

NotImplementedError – If no compatible spectrum file is found in the specified directory.

Returns:

A NMRExperimentalSpectrum1D object containing chemical shifts (in ppm) and intensities.

hqs_nmr.spectrumio._open_file(file: str | Path | IO, *args, **kwargs) → Generator[IO]

Context manager that opens a file or a path or just returns a given file-like object.

Parameters:
  • file (str | Path | IO) – The file name or a file-like object.

  • *args – Additional positional arguments passed to open.

  • **kwargs – Additional keyword arguments passed to open.

hqs_nmr.spectrumio.write_jdx(x_data: ndarray, y_data: ndarray, frequency_MHz: float, molecule_name: str | None = None, solvent: str = 'CDCl3', isotope: str = '1H', manual_shift: float | None = None, file: str | Path | TextIO | None = None) → None

Write a JCAMP-DX file.

This function writes a JCAMP-DX file from given x and y data. It is able of moving the x-axis by a specified amount. This is useful when the experimental spectrum is wrong referenced.

Parameters:
  • x_data – X data, shifts in ppm.

  • y_data – Y data, intensity.

  • frequency_MHz – Observation frequency in MHz (depends on the isotope).

  • molecule_name – Molecule name. Only needed when the file argument is None.

  • solvent – Solvent name. Defaults to “CDCl3”.

  • isotope – String representation of the isotope as [atomic mass number][symbol]. Defaults to “1H”.

  • manual_shift – Manual shift in ppm. Defaults to None.

  • file – The destination file either given string containing the filename or a file-like object.

hqs_nmr.spectrumio.bruker_dir_to_jdx(directory: Path | str, molecule_name: str) → None

Read spectral data from a Bruker directory and write it to a JCAMP-DX file.

Parameters:
  • directory – Path to the Bruker directory.

  • molecule_name – Molecule name.