Release notes

v0.3.1

This release fixes solvent filtering and automatic phasing of multidimensional spectra, adds raw example data and BMRB shifts of ubiquitin, and reorganises the documentation. autoprocess now returns the real spectrum.

  • Breaking: autoprocess returns the real spectrum (as from realest). Pass real=false to keep the Multicomplex spectrum, for example to phase it further.
  • ppm(n) returns the function peak -> ppm(peak, n), e.g. sort(pks; by=ppm(1)).
  • register, the non-mutating form of register!, is now documented.
  • exampledata passes other keywords to loadfid or loadnmr, e.g. exampledata("2D_HN"; fid=true, firstpoint=Dict(T2Dim => 1.0)).
  • Example data. exampledata(name; fid=true) loads the raw data of an example data set with loadfid, and exampledata(name; path=true) returns its path. 3D_HNCA_pipe and 3D_HNCA_ucsf are now available through exampledata, and BMRB_ubiquitin returns the assigned chemical shifts of ubiquitin (BMRB entry 17769), which match the ubiquitin example spectra.
  • Plotting FIDs. nmrplot draws time-domain data with an imaginary unit (an FID) as a line rather than as points.
  • Automatic phasing of multidimensional spectra. estimatephase and autophase now estimate the phase of a dimension from 1D traces through the strongest cross peaks, pooled into one fit, instead of from a single trace summed over the other dimensions. Each trace combines the components of the other dimensions so that it does not depend on how they are phased, and ridges such as t₁ noise from residual water are left out. On the ¹H,¹⁵N example (2D_HN, with its half-dwell ¹⁵N first point unscaled) the estimates are within 4° and 13° of TopSpin's phases, where they were wrong by up to 90° before.
  • First-order phase search. estimatephase searches five times the maxdelay range when the best fit lies at its limit, with the prior on p1 widened to match, and keeps the wider result if it fits better. For indirect dimensions the default p1 is now nothing: first-order phases of 0 and -180° (a first point half a dwell time after t = 0) are both tried.
  • Solvent filtering. solventfilter takes the digital-filter group delay into account, filtering the FID from t = 0. Previously the build-up points before t = 0 were filtered as part of the decay, which left a baseline ripple with a period of 1/(group delay) across the spectrum. method=:hsvd no longer returns NaNs for FIDs with a group delay. When autoprocess filters the solvent from a 1D spectrum, it estimates the phase from the unfiltered spectrum (leaving out ±0.2 ppm around the solvent), since the solvent's dispersion tails help the estimate.
  • Fixes. sumexpts no longer fails on integer (DTYPA = 0) data with non-integer weights. sumexpts, savefid and savenmr no longer fail when the source experiment's files are read-only.
  • Documentation reorganised into tutorials, tools, how-to guides, concepts and reference pages, with executed examples for processing, combining experiments and BMRB peak lists, and an API reference split by topic. Docstring examples for nucleus, Power, Peak, finddim, acqus and channels are now doctests, run by the test suite.

v0.3.0

This release adds loading and processing of raw time-domain data, writing of Bruker data, a new peak framework and import of assigned chemical shifts from the BMRB. It contains breaking changes, listed first.

Breaking changes

  • loadnmr(...; allcomponents=true) returns different numbers for the same files. Imaginary components are now conjugated relative to TopSpin's files, and for 2D data the 2ir and 2ri files have swapped places: 2ir is now the coefficient of im1 (the direct dimension) and 2ri that of im2. For 2D data the element type is unchanged, so existing code will run without error but give different results. This follows the sign convention used throughout NMRTools: spectra are the complex conjugate of TopSpin's in every imaginary unit (real parts agree), so TopSpin's PHC0 and PHC1 can be passed straight to phase. Real data (the default, allcomponents=false) are unaffected.
  • 1D and pseudo-2D complex data are Multicomplex. loadnmr(...; allcomponents=true) returns Multicomplex{Float64,1,2} elements (imaginary unit im1) instead of Julia ComplexF64, as for every other NMRTools spectrum; use complex(spec) to convert. 3D data with all components now give tricomplex numbers; previously only four of the eight component files were used.
  • Loaded spectra record their imaginary units. loadnmr sets the :mcindex of each frequency dimension (1 for the direct dimension, 2 for the next, …), so loaded spectra can be phased, Fourier transformed back and processed like spectra produced by ft.
  • Peaks. SimplePeak and AbstractPeak are replaced by Peak{N}, holding a position in ppm, an intensity, a linewidth in Hz for each dimension and a label. detectpeaks returns a Vector{Peak{N}} and now works in any number of frequency dimensions.
  • detectpeaks keywords and defaults. snr_threshold is renamed snr (the old name still works, with a deprecation warning, and will be removed in a future release). Negative peaks are now detected as well as positive ones by default; pass sign=:positive for the previous behaviour. Intensities are heights above the median baseline.
  • Dependencies. Peaks.jl, ArtifactUtils.jl and VisualRegressionTests.jl are no longer dependencies of NMRTools.
  • Julia 1.10 or later is now required (previously 1.9).
  • DimensionalData.jl 0.30 is now required (previously 0.29). DimensionalData 0.30 contains breaking changes of its own (see its release notes), which affect code that uses DimensionalData functions directly on NMRData: for example DimensionalData.LookupArrays is now DimensionalData.Lookups, and the rtol keyword of At has been removed.

Raw time-domain data

Loading and processing raw data is new, and its interface is the most likely part of the package to change in a future release.

  • loadfid reads Bruker fid and ser files (1D to 3D) as Multicomplex data, with one imaginary unit per Fourier dimension recorded in its :mcindex metadata. It combines States, States-TPPI and echo/anti-echo quadrature, scales the t = 0 point, and keeps the digital filter's build-up points on a time axis starting before t = 0. Intensities match TopSpin's. loadnmr on a fid or ser path now calls loadfid.
  • Processing functions, each with a curried form for use with |>: apodize, zerofill, ft (including the inverse transform), phase, extract, frequencyshift, linearpredict (forward and backward) and solventfilter. ft removes the digital filter's group delay with a linear phase ramp, as TopSpin and NMRPipe do. extract(F1Dim, 6.0 .. 10.0) selects a region within a pipeline.
  • realest(spec) gives the purely real spectrum, real in every dimension, and ends a processing pipeline. real and imag act elementwise as defined by MulticomplexNumbers (removing the highest-order imaginary unit); abs and complex are also defined.
  • Automatic phasing: estimatepoles, estimatephase, autophase and autophase0. Without a dimension argument, autophase and autophase0 phase every frequency dimension in turn (a dimension with too little signal is left unphased, with a warning). The total correction applied is recorded in each frequency dimension's :phase metadata, and the estimate is logged at debug level. Automatic phasing is reliable for 1D spectra; for multidimensional data, and indirect dimensions in particular, check the result and set known phases with phase.
  • autoprocess chains solvent suppression, apodization, zero-filling, Fourier transformation and automatic phasing in one step, with per-dimension settings.
  • Cos2Window replaces Cos²Window, which remains as an alias. CosWindow, Cos2Window and GeneralSineWindow can be created without a length, which apodize then sets from the acquisition time.

Writing data

  • savenmr(path, spec; procno=1) writes frequency-domain data as Bruker processed data (pdata/<procno>/1r, 2rr, …).
  • savefid(path, fid) writes time-domain data as a Bruker fid or ser file, undoing everything loadfid does, so loadfid(savefid(...)) round-trips.

Peaks and assignments

  • findpeak refines a single peak near a given position, and findnearest finds the closest peak in a list.
  • register! and register translate a peak list onto a spectrum by a global search.
  • savepeaks and loadpeaks read and write NMRAnalysis.jl peaklist.csv files and Sparky lists. A peak list is also a Tables.jl table, so DataFrame(pks) works.
  • loadbmrb reads assigned chemical shifts from an NMR-STAR file or by BMRB ID, and peaklist builds peak lists from them by atom name (with residue offsets) or by nucleus.
  • Peak lists can be plotted with Plots (as lines in 1D, crosses in 2D) and with Makie (nmrplot and nmrplot!), with or without a spectrum.

Pipelines

Besides the processing functions, detectpeaks, findpeak, register/register!, estimatepoles, estimatephase, reference, shiftdim, setrelaxtimes, setkinetictimes, setgradientlist, replacedimension, decimate, savenmr, savefid and savepeaks have curried forms for use with |>, for example spec |> detectpeaks(snr=20) |> savepeaks("peaklist.csv").

Other changes

  • Plots: the normalisation logic of the contour and pseudo-2D recipes has been consolidated. In the recipe for several pseudo-2D datasets, normalize=refspec previously divided every dataset by the reference's scale, leaving them unnormalised relative to each other; each is now divided by its own scale, as in the other recipes. An invalid normalize value now throws an ArgumentError in the 1D recipes too.
  • Data loading is included in the precompilation workload, reducing the time to first load.