API: Exchange1D

NMRAnalysis.Exchange1DModule
Exchange1D

Module for 1D chemical exchange analysis using Bloch-McConnell equations.

Supports R1ρ relaxation dispersion and CEST experiments with:

  • NoExchange (null model), TwoState, and TwoStateBinding exchange models
  • Full Bloch-McConnell Liouvillian construction
  • Joint fitting of multiple experiment types
source
NMRAnalysis.Exchange1D._OVERLAY_MARKER_COLORSConstant

Solid, lightened marker fill colours for the data panel of an overlay plot, one per curve (indexed the same way as the full-strength color=i used for fit lines and for the residual panel) — not transparency: alpha fades the error bars along with the marker fill, and overlapping semi-transparent error bars from several experiments composite into a muddy mess. A flat lighter colour keeps error bars crisp (their stroke colour is still forced to black — see the markerstrokecolor on each data scatter!) while letting the full-strength fit line read as the most prominent element on the plot. The residual panel has no competing fit line, so its markers use the regular full-strength color=i instead.

source
NMRAnalysis.Exchange1D.CESTExperimentType
CESTExperiment <: AbstractExperiment

Chemical Exchange Saturation Transfer experiment. Stores saturation offsets, B1 field strength, saturation time, and observed/predicted intensity profiles.

Loading, integration, and simulation are not yet implemented.

source
NMRAnalysis.Exchange1D.ExchangeProblemType
ExchangeProblem

A set of experiments to be fitted jointly to a shared exchange model.

integration records the peak-picking parameters (peakppm, noiseppm, ppmwidth) used to integrate all experiments in the problem, as a NamedTuple, or nothing before integrate! has been called. Kept here (rather than discarded once used) so it can be saved alongside the fit results for traceability back to the source spectra.

source
NMRAnalysis.Exchange1D.ExchangeProblemMethod
ExchangeProblem(filenames::Vector{String}, model::AbstractModel)

Construct an ExchangeProblem by loading experiments from filenames. Each file is classified and loaded via load_experiment.

source
NMRAnalysis.Exchange1D.FitResultType
FitResult

Result of a joint fit of exchange experiments. Fields are accessible via dot syntax.

Fields

  • params: fitted parameters with uncertainties (ComponentArray{Measurement})
  • params_value: fitted parameters as plain Float64, for simulation/plotting
  • params0: initial parameters used for the fit
  • chi2: chi-squared statistic
  • reduced_chi2: chi-squared / degrees of freedom
  • cov: parameter covariance matrix
  • nobs: number of observations
  • nparams: number of fitted (i.e. non-fixed) parameters
  • dof: degrees of freedom
  • fixed: flat indices of parameters held fixed during the fit (see fit)
  • prob: the ExchangeProblem that was fitted
source
NMRAnalysis.Exchange1D._ParamItemType

A flattened parameter item: individual scalar elements, with array elements expanded.

  • label: internal name (e.g. "spin.delta[1]")
  • flat_index: index into the underlying flat data array of the ComponentArray
  • value: the scalar value
  • section: top-level section name (e.g. "model", "spin", "nuisance")
source
Base.showMethod

Pretty multi-line display for FitResult with parameter tables and fit statistics.

source
NMRAnalysis.Exchange1D._dgpopulationMethod
_dgpopulation(pmodel::ComponentArray, key::Symbol)

Population fraction for key (e.g. :dGBpB), reconstructed from all dG* values currently stored in pmodel (the model parameter section), using the same reparametrization as TwoStateModel/ThreeStateModel's populations (population of the implicit reference state, e.g. A, is 1 / (1 + Σ exp(-dG))).

source
NMRAnalysis.Exchange1D._dgvalue_for_populationMethod
_dgvalue_for_population(pmodel::ComponentArray, key::Symbol, pnew)

Inverse of _dgpopulation: the dG value for key that reproduces the population fraction pnew, holding all other dG* values in pmodel fixed at their current values.

source
NMRAnalysis.Exchange1D._displayvalueMethod

Convert a parameter item's stored value to linear (display/entry) space. params is the enclosing top-level ComponentArray (with .model/.spin/ .nuisance sections), needed to reconstruct dG-parametrized populations.

source
NMRAnalysis.Exchange1D._format_concrangeMethod
_format_concrange(values::Vector{Float64}) -> String

Format the concentration(s) at which a molecule appears across experiments: a single value if consistent, otherwise the range observed (which usually signals a titration series or inconsistently-entered sample metadata).

source
NMRAnalysis.Exchange1D._isdgparamMethod
_isdgparam(item::_ParamItem) -> Bool

Whether a parameter follows the dG<X> convention (model.dGB, model.dGC, ...): an internal, unconstrained free energy difference (relative to a reference state, e.g. dGB = G_B - G_A) standing in for a population fraction. Unlike _islogparam, the user-facing value (pB, pC, ...) is not a function of this one parameter alone — it also depends on the other dG* values currently stored in the same model section, since the underlying population fractions must sum to 1 (see _dgpopulation / _dgvalue_for_population).

source
NMRAnalysis.Exchange1D._islogparamMethod
_islogparam(item::_ParamItem) -> Bool

Whether a parameter is stored internally as its natural logarithm. By convention this is signalled by a log prefix on the parameter name (e.g. model.logKd, model.logkoff) — any positive-only parameter can be switched to log-space storage (to keep it from going negative during fitting) simply by naming it this way in defaultparams/ default_spin_params/default_nuisance_params and exponentiating it where it's used. Users always see and enter the linear (exponentiated) value — the internal representation is invisible to them either way.

source
NMRAnalysis.Exchange1D._pretty_labelMethod
_pretty_label(item, state_labels, unique_fields) -> String

Convert an internal parameter label like spin.R2_22p31T[1] to a human-readable label like R₂ (A) or R₂ (A) [22.31 T].

source
NMRAnalysis.Exchange1D._prompt_concentrations!Method
_prompt_concentrations!(model, prob::ExchangeProblem)

For each molecule role required by model, check whether every experiment carries sample concentration metadata for it. If some (or all) experiments lack it — e.g. because they weren't matched to a sample containing this molecule — prompt the user for a fallback concentration value and store it in model.concentrations, which moleculeconcentration (and hence modelconcentrations) falls back to for experiments missing metadata.

Warning about a partial mismatch here, rather than only about a total one, is what turns a KeyError buried deep in the fit (see issue #36) into an actionable prompt at setup time.

source
NMRAnalysis.Exchange1D._prompt_moleculemap_manual!Method
_prompt_moleculemap_manual!(model, prob, roles) -> Bool

Fallback used by _prompt_moleculemap! when no experiment carries sample concentration metadata: since there is no list of molecule names to pick from, ask directly for a name and concentration for each role and populate both model.moleculemap and model.concentrations.

source
NMRAnalysis.Exchange1D._prompt_paramsMethod
_prompt_params(p0, prob, fixed) -> ComponentArray or nothing

Interactive parameter editor. Returns edited parameters, or nothing if cancelled.

fixed is a Set{Int} of flat parameter indices (mutated in place) that are held constant during fitting. Enter fix when editing a parameter to add it to this set (freezing it at its current value), or free to remove it.

Parameters stored internally in log space (see _islogparam) are displayed and entered here in linear space.

source
NMRAnalysis.Exchange1D._ratelowerboundMethod
_ratelowerbound(item) -> Float64

Lower bound for a raw (linear, un-fixed) parameter passed to the optimiser. Relaxation rates (spin.R1_*, spin.R2_*) are physically non-negative but, unlike Kd/koff (see _islogparam) or populations (see _isdgparam), are stored and fitted directly in linear space — so unlike those, nothing stops the optimiser from stepping into negative territory mid-search, which makes the Bloch-McConnell matrix exponential used to simulate CEST/R1ρ experiments numerically unstable (a relaxation rate < 0 is a growing, not decaying, mode). Bounding these at zero keeps the search in the physical region without changing how they're entered, displayed, or stored. Takes an _ParamItem (from _flatten_params_items, defined in interface.jl); left untyped here since interface.jl is included after this file.

source
NMRAnalysis.Exchange1D.combineplotsMethod
combineplots(plots) -> Plot

Create a combined figure from individual experiment plots, with scaled font sizes and figure dimensions so that the result is legible even with many experiments.

source
NMRAnalysis.Exchange1D.default_nuisance_paramsMethod
default_nuisance_params(expt::CESTExperiment) -> Vector{Pair{Symbol,Any}}

Return flat nuisance parameter entries for this CEST experiment: an overall intensity scale I0, tagged with the experiment type and field, e.g. :CEST_14p1T_I0. Shared across all CEST experiments at the same field — see simulate!.

source
NMRAnalysis.Exchange1D.default_nuisance_paramsMethod
default_nuisance_params(expt::R1Experiment) -> Vector{Pair{Symbol,Any}}

Return flat nuisance parameter entries for this R1 experiment, tagged with the experiment type and field, e.g. :R1_14p1T_I0, :R1_14p1T_inv_factor.

source
NMRAnalysis.Exchange1D.default_spin_paramsMethod
default_spin_params(expt::CESTExperiment, nstates) -> Vector{Pair{Symbol,Any}}

Return spin parameter entries needed by this CEST experiment: R1, R2 for the experiment's field, and chemical shifts (delta).

source
NMRAnalysis.Exchange1D.defaultparamsMethod
defaultparams(prob::ExchangeProblem) -> ComponentArray

Assemble a default parameter ComponentArray from the model and experiments.

Each experiment contributes spin parameters (via default_spin_params) and nuisance parameters (via default_nuisance_params). Both return flat Vector{Pair{Symbol,Any}} with self-describing keys (e.g. :R2_14p1T, :R1_14p1T_I0). Duplicate entries are merged automatically.

The returned ComponentArray has three sections:

  • model: exchange model parameters (kex, pB, Kd, koff, etc.)
  • spin: chemical shifts (delta, ppm) and field-dependent relaxation rates
  • nuisance: per-experiment amplitude and fitting parameters
source
NMRAnalysis.Exchange1D.detailMethod

A quieter status line for routine progress/confirmation messages (e.g. per-file loading) that don't need @info's logging prefix or full emphasis — @info/@warn are reserved here for things that genuinely need to stand out.

source
NMRAnalysis.Exchange1D.exchange1dMethod
exchange1d(filenames::Vector{String})

Interactive text-based interface for 1D chemical exchange analysis.

Guides the user through:

  1. Model selection
  2. Molecule mapping (if required by model)
  3. Peak integration parameters
  4. Parameter review and editing
  5. Fitting and display of results
  6. Saving results

Arguments

  • filenames: Vector of paths to NMR experiment directories
source
NMRAnalysis.Exchange1D.exchangematrixMethod
exchangematrix(model::InducedFitModel, params, expt) -> Matrix{Float64}

Build the 3×3 kinetic exchange matrix for the induced fit mechanism A + L ⇌ B ⇌ C. There is no direct A ⇌ C exchange. The pseudo-first-order on-rate A → B is kon * Lfree, with free ligand concentration obtained from the ligand mass balance Lfree = X0 - (B + C).

source
NMRAnalysis.Exchange1D.exchangematrixMethod
exchangematrix(model::ThreeStateBindingModel, params, expt)

Build the 3×3 exchange matrix for three-state parallel binding. B and C do not interconvert directly (kbc = kcb = 0).

source
NMRAnalysis.Exchange1D.exchangematrixMethod
exchangematrix(model::TwoStateBindingModel, params, expt) -> Matrix{Float64}

Build the 2×2 kinetic exchange matrix for two-state binding. Populations are derived from Kd and the total concentrations of the observed species (:A) and binding partner (:X) via the quadratic binding equation. Molecule names are looked up in sampleconcentrations using model.moleculemap.

source
NMRAnalysis.Exchange1D.exchangematrixMethod
exchangematrix(model::TwoStateModel, params, expt) -> Matrix{Float64}

Build the 2×2 kinetic exchange matrix for two-state intramolecular exchange. K[i,j] is the rate from state j to state i; column sums are zero.

source
NMRAnalysis.Exchange1D.experimentdirectoryMethod
experimentdirectory(expt::AbstractExperiment) -> String

Full experiment directory (the parent of the pdata/N folder spec[:filename] points into) — i.e. the path that was, or could be, passed to exchange1d.

source
NMRAnalysis.Exchange1D.experimentgroupsMethod
experimentgroups(key, experiments) -> Vector{Vector}

Group experiments by the value of key(experiment), preserving the order in which each group's key was first encountered. Used by overlayplots to find sets of experiments that share a saturation power or saturation time (and therefore differ only in the other one), so they can be drawn together.

source
NMRAnalysis.Exchange1D.experimentinfoMethod
experimentinfo(expt::CESTExperiment) -> Vector{Pair{String,String}}

Acquisition parameters worth recording alongside a saved fit (see _save_results): saturation field strength, saturation time, and the saturation offset range.

source
NMRAnalysis.Exchange1D.experimentinfoMethod
experimentinfo(expt::R1Experiment) -> Vector{Pair{String,String}}

Acquisition parameters worth recording alongside a saved fit (see _save_results): field strength, fitting model, and relaxation delay range.

source
NMRAnalysis.Exchange1D.experimentinfoMethod
experimentinfo(expt::R1rhoOffResExperiment) -> Vector{Pair{String,String}}

Acquisition parameters worth recording alongside a saved fit (see _save_results): field strength, spin-lock strength, and offset range.

source
NMRAnalysis.Exchange1D.experimentinfoMethod
experimentinfo(expt::R1rhoOnResExperiment) -> Vector{Pair{String,String}}

Acquisition parameters worth recording alongside a saved fit (see _save_results): field strength and spin-lock strength range.

source
NMRAnalysis.Exchange1D.field_labelMethod
field_label(expt) -> Symbol

Convert a magnetic field strength in Tesla to a Symbol for use as a ComponentArray key. E.g. field_label(14.1):14p1T.

source
NMRAnalysis.Exchange1D.fitMethod
fit(prob::ExchangeProblem, params0::ComponentArray; fixed=Set{Int}()) -> FitResult

Fit all experiments jointly using least-squares optimisation.

fixed is a set of flat indices (as produced by _flatten_params_items) into params0 that are held constant at their params0 value rather than being optimised. This lets, e.g., a chemical shift be pinned by the user while the rest of the model is fitted.

Returns a FitResult containing fitted parameters (with uncertainties), fit statistics, and a reference to the problem for display and plotting.

source
NMRAnalysis.Exchange1D.inducedfitequilibriumMethod
inducedfitequilibrium(model::InducedFitModel, params, expt) -> (Afree, B, C)

Equilibrium concentrations of free observed species (Afree), the initial ("open") complex (B) and the conformationally-selected ("closed") complex (C) for a two-step induced fit mechanism

A + L ⇌ B ⇌ C

with dissociation constant Kd and off-rate koff for the bimolecular step, and forward/backward rates kclose/kopen for the unimolecular conformational step. Solved analytically from the ligand/observed-species mass balance equations (Waudby, TITAN bmInducedFit). Shared by populations and exchangematrix.

source
NMRAnalysis.Exchange1D.integrate!Method
integrate!(prob::ExchangeProblem, peakppm, noiseppm, ppmwidth)

Integrate all experiments in the problem at the given peak and noise positions, and record them on prob.integration for later reference (e.g. when saving results).

source
NMRAnalysis.Exchange1D.liouvillianMethod
liouvillian(model, params, expt::AbstractExperiment, offset_hz, spinlock_hz)

Form the Liouvillian matrix for a given model, parameters, and experiment.

source
NMRAnalysis.Exchange1D.load_experimentMethod
load_experiment(filename) -> AbstractExperiment

Load an NMR experiment file and return the appropriate concrete experiment type based on its annotations.

Dispatches on annotations(spec, :experiment_type) and annotations(spec, :features):

  • "relaxation" + "R1"R1Experiment
  • "cest"CESTExperiment
  • "r1rho" + "on_resonance"/"off_resonance"R1rhoOnResExperiment/R1rhoOffResExperiment
source
NMRAnalysis.Exchange1D.modelconcentrationsMethod
modelconcentrations(model::InducedFitModel, expt) -> (A0, X0)

Look up total concentrations of the observed species and binding partner from the experiment sample concentrations dict, using the model's moleculemap to translate role symbols (:A, :X) to molecule names. Falls back to model.concentrations (populated interactively) when sample metadata is unavailable.

source
NMRAnalysis.Exchange1D.modelconcentrationsMethod
modelconcentrations(model::ThreeStateBindingModel, expt) -> (A0, X0)

Look up total concentrations of the observed species and binding partner from the experiment sample concentrations dict, using the model's moleculemap to translate role symbols (:A, :X) to molecule names. Falls back to model.concentrations (populated interactively) when sample metadata is unavailable.

source
NMRAnalysis.Exchange1D.modelconcentrationsMethod
modelconcentrations(model::TwoStateBindingModel, expt) -> (A0, X0)

Look up total concentrations of the observed species and binding partner from the experiment sample concentrations dict, using the model's moleculemap to translate role symbols (:A, :X) to molecule names. Falls back to model.concentrations (populated interactively) when sample metadata is unavailable.

source
NMRAnalysis.Exchange1D.modelorderMethod
modelorder(model::AbstractModel) -> Int

Sort key controlling where model appears in the model selection menu (lowest first). Defaults to placing a model without an explicit ordering after every model that has one, so a custom model added by a package extension (see docs/src/advanced/extending_exchange1d.md) still appears automatically without needing to touch this function — just define modelorder too if a particular position matters.

source
NMRAnalysis.Exchange1D.moleculeconcentrationMethod
moleculeconcentration(model, expt::AbstractExperiment, role::Symbol) -> Float64

Look up the concentration of the molecule assigned to role (e.g. :A, :X) for expt: first from the experiment's own sample metadata, falling back to model.concentrations (populated interactively when metadata is missing).

Raises an informative ArgumentError naming the molecule, its role, and the experiment when neither source has a value — e.g. because only some of the loaded experiments were matched to a sample containing this molecule — rather than letting a bare KeyError propagate from deep inside the fitting or plotting code.

source
NMRAnalysis.Exchange1D.overlaycestMethod
overlaycest(experiments, varyby::Symbol, params_value) -> Plot

Overlay CEST experiments that share every acquisition parameter except varyby (:saturation_time or :ν1) on shared axes, sorted by varyby, one colour per curve. Data, fit, and residuals for a given experiment share a colour; residual bands are shaded grey rather than green, as in overlayr1rhooffres. The fitted chemical shift(s) (params_value.spin.delta) are marked with dashed vertical lines, as on the individual per-experiment plot; observed/predicted intensities are normalised by the fitted I0 (see CESTExperiment's plot_result) so the baseline sits at 1.0.

The legend is drawn inset (:bottomright, where the CEST dip is not) rather than outside the axes, for the same alignment reason as overlayr1rhooffres.

Draw order (bottom to top): resonance-position vlines, then residual bands and zero-line, then data (markers + error bars) for every experiment, then every fit line in a second pass. See _OVERLAY_MARKER_COLORS.

source
NMRAnalysis.Exchange1D.overlayplotsMethod
overlayplots(result::FitResult) -> Vector

Additional plots overlaying similar experiments on shared axes, so that trends across a series are easy to compare directly (issue #39):

  • all off-resonance R1ρ experiments together, one curve per spin-lock power
  • CEST experiments sharing a saturation power (ν1), one curve per saturation time
  • CEST experiments sharing a saturation time, one curve per saturation power

These are kept separate from combineplots(plot(result)) — the per-experiment grid — rather than folded into it, so that grid doesn't grow denser (and illegible) as overlays are added; save or display them as their own figure(s) instead. Returns an empty vector if there is nothing to usefully overlay (fewer than two experiments in a group).

source
NMRAnalysis.Exchange1D.overlayr1rhooffresMethod
overlayr1rhooffres(experiments, params_value) -> Plot

Overlay all off-resonance R1ρ experiments on shared axes, one colour per spin-lock power, sorted by offset. Data, fit, and residuals for a given experiment share a colour; residual bands are shaded grey (rather than the green used for a single experiment's plot) so they read clearly with multiple overlapping colours. The fitted chemical shift(s) (params_value.spin.delta) are marked with dashed vertical lines, as on the individual per-experiment plot.

The legend is drawn inset (:topright), not outside the axes: an outer legend only widens the data panel, not the residual panel below it, which throws off their alignment under the shared link=:x.

Draw order (bottom to top): resonance-position vlines, then residual bands and zero-line, then data (markers + error bars) for every experiment, then every fit line in a second pass — so the fit lines render on top of every experiment's data rather than only their own, and the data markers render above the residual bands. See _OVERLAY_MARKER_COLORS.

source
NMRAnalysis.Exchange1D.plot_resultMethod
plot_result(prob::ExchangeProblem, fit_result)

Plot all experiments in the problem using the fitted parameters from fit_result (as returned by fit). Returns a vector of plots, one per experiment.

source
NMRAnalysis.Exchange1D.plot_resultMethod
plot_result(expt::R1Experiment, params; kwargs...)

Plot an R1 experiment with observed data (error bars), fitted curve, and residuals.

Upper panel: normalised intensities vs delay time with fit overlay. Lower panel: weighted residuals (observed - predicted) / σ.

source
NMRAnalysis.Exchange1D.populationsMethod
populations(model::InducedFitModel, params, expt) -> [pA, pB, pC]

Equilibrium populations of free (A), open-complex (B) and closed-complex (C) species, normalised to their sum (rather than to A0 directly) to stay exact under floating-point roundoff.

source
NMRAnalysis.Exchange1D.populationsMethod
populations(::ThreeStateModel, params, expt) -> [pA, pB, pC]

Populations are derived from the free energy differences dGB = G_B - G_A and dGC = G_C - G_A (in units of RT), relative to reference state A. This keeps all three populations in (0, 1) summing to 1 for any real dGB, dGC. This internal dG representation is just a fitting convenience — the user only ever sees/enters pB/pC themselves (see _isdgparam in interface.jl).

source
NMRAnalysis.Exchange1D.populationsMethod
populations(::TwoStateModel, params, expt) -> [pA, pB]

Population of state B is derived from the free energy difference dGB = G_B - G_A (in units of RT): pB = exp(-dGB)/(1+exp(-dGB)), rearranged as 1/(1+exp(dGB)) to avoid overflow for very negative dGB. This internal dGB representation is just a fitting convenience (it keeps pB in (0, 1) for any real value, unlike a raw population fraction) — the user only ever sees/enters pB itself (see _isdgparam in interface.jl).

source
NMRAnalysis.Exchange1D.prepare_outputfolderMethod

prepare_outputfolder(outputfolder)

This function checks if the outputfolder exists. If it doesn't, it creates it.
If it does exist, it deletes the folder and its contents, then recreates it.
Please note that this function will delete all files and subdirectories in the
specified outputfolder, so use it with caution.
source
NMRAnalysis.Exchange1D.residualsMethod
residuals(expt::AbstractExperiment)

Return weighted residuals (observed - predicted) / uncertainty for an experiment. Default implementation using the observed_intensities and predicted_intensities fields.

source
NMRAnalysis.Exchange1D.sectionheaderMethod

Bold, cyan section heading for a stage of the interactive workflow — consistent with the styling FitResult's show method already uses for its own section titles.

source
NMRAnalysis.Exchange1D.simulate!Method
simulate!(expt::CESTExperiment, model, params)

Simulate the CEST saturation profile, then scale it by the fitted I0 intensity (params.nuisance.CEST_<field>_I0) to match the observed, integration-normalised intensities.

source
NMRAnalysis.Exchange1D.simulate!Method
simulate!(expt::R1Experiment, model::AbstractModel, params::ComponentArray)

Simulate predicted intensities for an R1 experiment. The model argument is accepted for interface consistency but is not used (R1 decay is independent of chemical exchange).

Reads R1 from params.spin.R1_<field> and nuisance parameters (e.g. params.nuisance.R1_14p1T_I0) directly.

source
NMRAnalysis.Exchange1D.writeexperimentsummaryMethod
writeexperimentsummary(io::IO, prob::ExchangeProblem)

Write a plain-text summary of the experiments underlying prob: the model, its molecule-to-sample mapping (if any), and — for each experiment — its source filename, key acquisition parameters (via experimentinfo), and sample concentrations. Saved alongside fit results by _save_results so an analysis can always be traced back to its source data (issue #37).

source
RecipesBase.plotMethod

Plot all experiments in a FitResult, returning a vector of per-experiment plots.

source