Working with NMR data

NMR measurements are arrays of data, with additional numerical data associated with each dimension, or axis. Within NMRTools, these data are stored as NMRData structures, which encapsulate the data, axis information, and metadata on acquisition or processing.

Loading NMR data

NMR data are loaded using the loadnmr function. This handles processed Bruker experiments and NMRPipe-format data.

# load bruker experiment number 1 from a directory '2D_HN'
# by default, NMRTools will load bruker processed data from proc 1
spec2d = loadnmr("exampledata/2D_HN/1")

# load a different processed spectrum
spec1d = loadnmr("exampledata/2D_HN/1/pdata/101")

# load data from NMRPipe format, using a template
spec3d = loadnmr("exampledata/pseudo3D_HN_R2/1/ft/test%03d.ft2")
Tip

loadnmr will attempt to locate and parse acquisition metadata, such as acqus files. If the spectrum file is located elsewhere (for example, if you are loading a file that was processed with NMRPipe), then you can specify the path to the experiment folder using the experimentfolder keyword argument.

When spectra are loaded, a simple algorithm runs to estimate the noise level, which is often used for subsequent plotting commands.

Manipulating spectrum data

NMRData structures encapsulate a standard Julia array. This can be accessed using the data command. Through multiple dispatch, most operations work directly on NMRData variables as if they are regular arrays, with the added benefit that axis information and metadata are preserved. Data can be sliced and accessed like a regular array using the usual square brackets:

spec1d[100:105]
spec2d[3:4, 10:14]

Value-based selectors can also be used to locate data using chemical shifts. Three selectors are defined:

  • At(x): select data precisely at the specified value
  • Near(x): select data at the nearest matching position
  • x .. y: select the range of data between x and y (closed interval)

For example:

spec1d[8.2 .. 8.3] # select between 8.2 and 8.3 ppm
spec2d[Near(8.25), 123 .. 124] # select near 8.25 ppm in the first dimension
                               # and between 123 and 124 ppm in the second dimension

When data are sliced, new NMRData structures are created and their axes are updated to match the new data size.

Warning

When NMRData structures are sliced, copied, or otherwise modified, they inherit the same dictionary of metadata as the original variable. This means that any changes to metadata will affect both variables. To resolve this, make a deepcopy of the variable. Note also that any acquisition metadata might not reflect the correct shape of the data any more.

Accessing axis data

Information on data dimensions is stored in NMRDimension structures. These can be accessed with the dims function:

# get the first dimension of this two-dimensional experiment
dims(spec2d, 1)
F1Dim Sampled{Float64} ReverseOrdered Regular DimensionalData.Dimensions.Lookups.Points
wrapping: 1216-element LinRange{Float64, Int64}:
 11.0712, 11.0668, 11.0624, 11.058, …, 5.7357, 5.73129, 5.72689, 5.72249

NMRDimensions can be treated like vectors (one-dimensional arrays) for most purposes, including indexing and slicing. Value-based selectors can also be used, as for spectrum data. Like spectrum data, the underlying numerical data can be accessed if needed using the data function.

A hierarchy of types is defined for NMR dimensions, reflecting the variety of different experiments:

  • NMRDimension
    • FrequencyDimension: with specific types F1Dim to F4Dim
    • NonFrequencyDimension
      • TimeDimension:
        • TrelaxDim: for relaxation times
        • TkinDim: for kinetic evolution times
        • T1Dim to T4Dim: for general frequency evolution periods
      • GradientDimension: for e.g. diffusion measurements, with specific types G1Dim to G4Dim
      • UnknownDimension: with specific types X1Dim to X4Dim

Chemical shift referencing

The reference function corrects the referencing of a spectrum by specifying a known chemical shift: you provide an old => new pair indicating where a reference peak currently appears and where it should appear.

# Load example data
spec2d_original = exampledata("2D_HN")

# Reference the 1H dimension: peak at 4.72 ppm should be at 4.70 ppm
spec2d = reference(spec2d_original, F2Dim, 4.72 => 4.70)

label!(spec2d_original, "unreferenced")
label!(spec2d, "referenced")
plot([spec2d_original, spec2d])
┌ Info: Referencing 15N
│   old_shift = 4.72
│   new_shift = 4.7
└   offset = -0.02
┌ Info: Indirect referencing using DSS Xi ratios
│   reference_nucleus = "15N"
└   effective_1H_bf_MHz = 600.2
┌ Info:   Indirect referencing 1H
│   expected_bf_MHz = 600.200014
│   current_bf_MHz = 600.2
└   offset_ppm = 0.0226

The dimension can be specified as a dimension index (1, 2, ...), a dimension type (F1Dim, F2Dim, ...), or a nucleus (H1, N15, ...).

By default, reference also applies indirect referencing to all other frequency dimensions using IUPAC Xi ratios. This ensures that, for example, correcting the ¹H reference in an HSQC automatically corrects the ¹⁵N or ¹³C axis too:

# Reference 1H; 15N dimension is automatically updated via Xi ratios
spec = reference(spec, H1, 4.72 => 4.70)

# Disable indirect referencing if needed
spec = reference(spec, H1, 4.72 => 4.70; indirect=false)

For aqueous samples, reference can determine the ¹H water chemical shift from the sample temperature and apply the correction automatically:

# Reference to water using temperature from spectrum metadata
spec = reference(spec)

# Or specify the temperature manually (in Kelvin)
spec = reference(spec; temperature=298.15)
Note

reference updates the axis values and adjusts metadata (:offsetppm, :offsethz, :sf) to record the altered referencing. See Chemical Shift Referencing for full details of indirect referencing and Xi ratio tables.

Accessing metadata

NMRData objects contain metadata on processing and acquisition parameters that are populated automatically upon loading a spectrum. Entries are divided into spectrum metadata - associated with the experiment in general - and axis metadata, that are associated with a particular dimension of the data.

Metadata entries are labelled by symbols such as :ns or :pulseprogram. Entries can be accessed using the metadata function, or directly as a dictionary-style lookup:

julia> metadata(spec2d, :ns)2
julia> spec2d[:title]"13C,15N ubiquitin\n500 uM in 10% D2O, 20 mM Pi pH 6.5, 277 K, SOFAST-HMQC"

Acquisition parameters from Bruker acqus files are also parsed when loading data, and can be accessed using the acqus function. Parameters are specified either as lower-case symbols or strings (not case-sensitive). An index can be specified for arrayed parameters such as pulse lengths or delays.

julia> acqus(spec2d, "TE")276.9988
julia> acqus(spec2d, :p, 1)9.199999999999998e-6

Axis metadata can be accessed by providing an additional label, which can either be numerical or the appropriate NMRDimension type, such as F1Dim etc:

julia> metadata(spec2d, F2Dim, :label)"15N"
julia> spec2d[2, :bf]6.0817738e7
julia> spec2d[F2Dim, :window]CosWindow(0.04783359999999993)