2D Experiments
An abstract type representing a type of experimental NMR analysis. The type system is split between fixed-peak experiments (where peak positions are constant between spectra) and moving-peak experiments (where peak positions can vary).
Required Fields
All concrete subtypes must include the following fields:
specdata: ASpecDataobject containing the observed and simulated data plus maskpeaks: AnObservablelist of peaks in the experimentclusters: AnObservablelist of clusters of peakstouched: AnObservablelist of touched clustersisfitting: AnObservableboolean indicating if real-time fitting is activexradius: AnObservablenumber defining peak detection radius in x dimensionyradius: AnObservablenumber defining peak detection radius in y dimensionstate: AnObservabledictionary of GUI state variables
Required Implementation
Concrete subtypes must implement both the core analysis functions below and the visualization functions documented separately. Note that many other functions have default implementations that can be used unless special behaviour is needed.
Type Hierarchy
The Experiment type has two immediate subtypes that handle position behaviour:
FixedPeakExperiment: Implementation wherehasfixedpositions(expt) = trueMovingPeakExperiment: Implementation wherehasfixedpositions(expt) = false
Concrete experiment types should inherit from one of these intermediate types rather than directly from Experiment.
addpeak!(expt, position, [label], [xradius], [yradius])
Add a new peak to the experiment at the specified position.
Arguments:
expt: The experimentposition: APoint2fspecifying the (x,y) coordinates of the peaklabel: Optional string label for the peak (defaults to auto-generated)xradius: Optional peak radius in x dimension (defaults to experiment default)yradius: Optional peak radius in y dimension (defaults to experiment default)
simulate!(z, peak, expt, [xbounds], [ybounds])
Simulate a single peak in the experiment.
Arguments:
z: Array to store simulation resultspeak: The peak to simulateexpt: The experimentxbounds: Optional bounds for x dimension simulationybounds: Optional bounds for y dimension simulation
Default Implementations
The abstract type provides several default implementations that can be used as-is or overridden when needed:
mask!(z, peak, expt)
Default implementation generates an elliptical mask for each peak using maskellipse!. Only needs to be overridden if a different peak shape is required.
Arguments:
z: Array to store mask resultspeak: The peak to maskexpt: The experiment
postfit!(peak, expt)
Perform additional fitting operations after spectrum fitting.
Arguments:
peak: The peak to post-fitexpt: The experiment
slicelabel(expt, idx)
Generate a label for a specific slice/plane of the experiment.
Arguments:
expt: The experimentidx: The slice index
Returns:
- A string label for the slice
peakinfotext(expt, idx)
Generate information text about a specific peak.
Arguments:
expt: The experimentidx: The peak index
Returns:
- A string containing formatted peak information
experimentinfo(expt)
Generate information text about the experiment.
Returns:
- A string containing formatted experiment information
completestate!(state, expt)
Set up observables for the GUI state.
Arguments:
state: The GUI state dictionaryexpt: The experiment
Functions Handled by Abstract Type
The following functions are implemented generically and do not need to be reimplemented by concrete subtypes:
Peak Management
nslices(expt): Get the number of slices in the experimentnpeaks(expt): Get the number of peaks in the experimentmovepeak!(expt, idx, newpos): Move a peak to a new positiondeletepeak!(expt, idx): Delete a specific peakdeleteallpeaks!(expt): Delete all peaks
Data Processing
mask!(z, peaks, expt): Calculate peak masks and update internal specdatasimulate!(z, peaks, expt): Simulate the experiment and update internal specdatafit!(expt): Fit the peaks in the experimentfit!(cluster, expt): Fit a specific cluster of peakschecktouched!(expt): Check which clusters have been modified
Observable Setup
setupexptobservables!(expt): Set up reactive behaviours for experiment observables
Required Visualization Functions
Concrete subtypes must implement the following visualization functions:
makepeakplot!(gui, state, expt)
Create the interactive peak plot in the GUI context. This function is crucial for real-time visualization and interaction.
Arguments:
gui: The GUI context containing plot panelsstate: The GUI state dictionaryexpt: The experiment
save_peak_plots!(expt, folder)
Save publication-quality plots for all peaks to a specified folder.
Arguments:
expt: The experimentfolder: String path to the output folder
Utility Functions
bounds(mask): Calculate bounds from a maskpeak_plot_data(peak, expt): Extract plotting data for a single peakplot_peak!(ax, peak, expt): Plot a single peak's data
Implemented Experiment Types
The codebase includes implementations for several specific experiment types:
RelaxationExperiment: For relaxation measurementsHetNOEExperiment: For heteronuclear NOE measurementsPREExperiment: For paramagnetic relaxation enhancement measurements
Each implementation specialises the simulation and fitting behaviour for its specific experiment type while inheriting the common functionality from the abstract type.
Guide: Creating a New Experiment Type
Here's a step-by-step guide to implementing a new type of NMR experiment:
Choose Base Type
- Inherit from
FixedPeakExperimentif peak positions are constant between spectra - Inherit from
MovingPeakExperimentif peak positions can vary
- Inherit from
Define Structure
struct MyNewExperiment <: FixedPeakExperiment # Required fields specdata peaks clusters touched isfitting xradius yradius state # Experiment-specific fields my_special_parameter endConstructor
- Create a constructor that initializes all required fields
- Set up observables using
setupexptobservables! - Initialize experiment-specific parameters
Core Analysis Functions
- Implement
addpeak!to set up experiment-specific peak parametersfunction addpeak!(expt::MyNewExperiment, initialposition::Point2f, label="") # Create basic peak newpeak = Peak(initialposition, label) # Add experiment-specific parameters newpeak.parameters[:my_param] = Parameter("My Parameter", initial_value) # Add post-fit parameters that will be saved in results newpeak.postparameters[:final_result] = Parameter("Final Result", 0.0) push!(expt.peaks[], newpeak) notify(expt.peaks) end - Implement
simulate!for your specific peak shapes/behavior - Implement
postfit!to calculate final parameters from fit results
- Implement
Information Functions
- Implement
slicelabelfor spectrum navigation - Implement
peakinfotextto show fit results - Implement
experimentinfoto show experiment details
- Implement
Visualization Functions
- Implement
makepeakplot!for the interactive GUIfunction makepeakplot!(gui, state, expt::MyNewExperiment) # Create appropriate plot(s) for your data gui[:axpeakplot] = ax = Axis(gui[:panelpeakplot][1,1], xlabel="My X Label", ylabel="My Y Label") # Add plot elements plot!(ax, ...) end - Implement
save_peak_plots!for publication figuresfunction save_peak_plots!(expt::MyNewExperiment, folder::AbstractString) CairoMakie.activate!() for peak in expt.peaks[] fig = Figure() # Create publication-quality figure save(joinpath(folder, "peak_$(peak.label[]).pdf"), fig) end GLMakie.activate!() end
- Implement
Optional Overrides
- Override
mask!only if you need non-elliptical peak shapes - Override clustering functions only if you need special peak grouping
- Override other default implementations only if needed
- Override
Remember:
- Post-fit parameters (
postparameters) are what get saved in results files - Peak parameters (
parameters) are used during fitting - Use the existing implementations (RelaxationExperiment, HetNOEExperiment, PREExperiment) as templates
- Most functionality can be inherited from the abstract type