Spectrum2D
#
Spectrum2D(
f1: FloatArray | None = None,
f2: FloatArray | None = None,
amplitudes: ndarray | None = None,
f1_unit: FrequencyUnit = HERTZ,
f2_unit: FrequencyUnit = HERTZ,
metadata: dict[str, Any] | None = None,
reference_spectrum_f1: Spectrum1D | None = None,
reference_spectrum_f2: Spectrum1D | None = None,
f1_reference_frequency_hz: float | None = None,
f2_reference_frequency_hz: float | None = None,
)
2D frequency-domain spectrum for NMR experiments (e.g., COSY, HSQC).
Attributes:
| Name | Type | Description |
|---|---|---|
f1, |
f2
|
FloatArray (np.ndarray[float64]) - frequency axes |
amplitudes |
ndarray
|
np.ndarray[complex] with shape (len(f1), len(f2)) |
f1_unit, |
f2_unit
|
FrequencyUnit |
f1_reference_frequency_hz, |
f2_reference_frequency_hz
|
Reference frequencies for PPM conversion |
processing_history
class-attribute
instance-attribute
#
processing_history: ProcessingHistory | None = (
betterproto2.field(
8, betterproto2.TYPE_MESSAGE, optional=True
)
)
Processing history - read-only audit log of all operations applied to this spectrum
history
property
#
Read-only access to processing history entries.
Returns:
| Type | Description |
|---|---|
Sequence[ProcessingHistoryEntry]
|
Immutable sequence of history entries. Returns empty tuple if |
Sequence[ProcessingHistoryEntry]
|
no history has been recorded yet. |
_frequency_axis_1
class-attribute
instance-attribute
#
_frequency_axis_1: NdAxis | None = betterproto2.field(
1, betterproto2.TYPE_MESSAGE, optional=True
)
Exactly two frequency axes (enforced by validation)
e.g., f1
_frequency_axis_2
class-attribute
instance-attribute
#
_frequency_axis_2: NdAxis | None = betterproto2.field(
2, betterproto2.TYPE_MESSAGE, optional=True
)
e.g., f2
_amplitudes
class-attribute
instance-attribute
#
_amplitudes: NdComplexArray | None = betterproto2.field(
3, betterproto2.TYPE_MESSAGE, optional=True
)
2D complex amplitude data (shape = [N, M])
is_jres
property
#
Whether this plane is a J-resolved experiment (detected, never configured).
is_jres_tilted
property
#
Whether :meth:tilt_jres has already sheared this plane.
visualization_handler
property
writable
#
Plugin visualization handler ID (e.g., 'nanalysis:cosy_2d').
f1_unit
property
writable
#
Frequency unit for first dimension (F1/indirect).
f1_reference_frequency_hz
property
writable
#
Reference frequency for F1 dimension (for PPM conversion).
f2_reference_frequency_hz
property
writable
#
Reference frequency for F2 dimension (for PPM conversion).
f2_imaginary
property
writable
#
The F2-dispersion quadrant RI + i*II, or None for non-hypercomplex data.
A hypercomplex transform takes the real part along F2 before transforming F1, so the F2 dispersion channel has to live somewhere if an F2 phase correction is to remain possible afterwards; it lives here. It is a processing intermediate: it is deliberately not a proto field and does not survive serialization, so it is available for as long as the spectrum stays in the process that transformed it.
f1_acquisition_mode
property
#
How the indirect dimension was quadrature-detected.
Read from the metadata the transform stamps; "undefined" when the spectrum
did not come from a 2D transform that knew the mode.
is_hypercomplex
property
#
Whether the F2-dispersion quadrant is available for an F2 phase correction.
True only for a spectrum produced by the hypercomplex transform in this process: the second quadrant is a processing intermediate and is not carried over the wire, so a spectrum received from gRPC reports False.
is_f1_phase_sensitive
property
#
Whether an F1 phase correction can do anything to this spectrum.
A complex amplitudes array is not evidence: a magnitude-mode (QF) or
undefined acquisition still comes out of a complex transform with a non-zero
imaginary part, and phasing it only fits a meaningless ramp to the mirror-image
artefacts. Only a phase-sensitive F1 acquisition scheme -- States, States-TPPI,
Echo-Antiecho or TPPI -- gives F1 a phase worth correcting.
is_f2_phase_sensitive
property
#
Whether an F2 phase correction can do anything to this spectrum.
The direct dimension is quadrature detected in every 2D experiment, so this is
true whenever there is an imaginary channel to rotate into: either the ordinary
complex amplitudes of a non-hypercomplex transform, or the second quadrant.
_init_history
#
Initialize the processing history.
Called from init of Signal1D/Spectrum1D. Always creates a ProcessingHistory instance - history is always enabled.
_record_history_entry
#
_record_history_entry(
operation: str,
parameters: dict[str, str],
shape_before: tuple[int, ...],
shape_after: tuple[int, ...],
source: str = "tqt_nmr",
) -> None
Record a processing operation to the history.
This is an internal method - not exposed to users. Called by @track_operation decorator and TrackedArray.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operation
|
str
|
Name of the operation (e.g., "correct_phase_manual"). |
required |
parameters
|
dict[str, str]
|
Operation parameters as string key-value pairs. |
required |
shape_before
|
tuple[int, ...]
|
Shape of amplitudes before operation. |
required |
shape_after
|
tuple[int, ...]
|
Shape of amplitudes after operation. |
required |
source
|
str
|
Source of the operation (e.g., "tqt_nmr", "numpy"). |
'tqt_nmr'
|
_copy_history_to
#
Copy processing history to another object.
Used by copy() methods to preserve history lineage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Self
|
Object to copy history to. |
required |
detect_jres
#
Measure whether this plane's multiplets lie on a shearable line.
tilt_jres
#
Shear the plane so multiplets become vertical.
The slope is measured from the plane's own peaks, so an unconventional slope is followed as readily as +/-1. A plane whose peaks do not lie on a shearable line, and a plane already tilted, are returned untouched with the reason logged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
force
|
bool
|
Shear on the measured slope even when the alignment is too weak to pass detection. A plane with no measurable slope at all is still left alone. |
False
|
Returns:
| Type | Description |
|---|---|
Self
|
Self, for chaining. |
symmetrize_f1
#
Symmetrise the plane about F1 = 0, suppressing asymmetric artefacts.
Each point is scaled down to the smaller of its own magnitude and its mirror's; the phase of genuine, symmetric peaks is untouched. A no-op on an F1 axis with fewer than two points or a zero increment.
Returns:
| Type | Description |
|---|---|
Self
|
Self, for chaining. |
skyline_projection_f2
#
skyline_projection_f2() -> Spectrum1D
Maximum-magnitude projection onto F2 — a decoupled 1D spectrum once tilted.
Returns:
| Type | Description |
|---|---|
Spectrum1D
|
A real-valued :class: |
process_jres
#
Run the conventional JRES chain if, and only if, a shear collapses this plane.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
symmetrize
|
bool
|
Also symmetrise about F1 = 0 after tilting (standard practice). |
True
|
Returns:
| Type | Description |
|---|---|
Self
|
Self, unchanged when the plane's peaks do not lie on a shearable line. |
find_peaks
#
find_peaks(
min_distance_points: int | None = None,
min_height_ratio: float = 0.05,
use_magnitude: bool = True,
adaptive: bool = False,
snr_threshold: float = 5.0,
) -> list[PeakInfo2D]
Find peaks in 2D spectrum.
There is one detector, not a choice between named ones. Local maxima above
the height gate are found first, then the maxima that the structure of the
plane shows to be artefacts rather than resonances are rejected — see
:func:_reject_plane_artefacts. Nothing here reads a pulse-program name, a
title or a filename, and no experiment class is special-cased: t1 ridges and
edge features are properties of the numbers, so they are measured.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_distance_points
|
int | None
|
Minimum peak separation in points (in each dimension). If None, auto-calculates as ~2% of each axis. |
None
|
min_height_ratio
|
float
|
Minimum peak height as a ratio of the maximum height (0-1). Applies in BOTH modes. Unlike the 1D find_peaks, adaptive=True does not discard it: on a 2D plane it acts as a floor under the noise gate, because t1 noise ridges and truncation wiggles sit far above the thermal noise floor and a bare noise gate would report thousands of them. |
0.05
|
use_magnitude
|
bool
|
If True, work on |z| else on Re(z). When True the noise gate uses the noise floor of the magnitude plane itself, pedestal included. |
True
|
adaptive
|
bool
|
If True, raise the height threshold to snr_threshold noise sigmas whenever that exceeds min_height_ratio * max. The sigma comes from estimate_noise_plane, which measures each row along the direct dimension, so it is valid for a pseudo-2D plane whose indirect axis is a delay list; the gate is referenced to the measured baseline level. Defaults to False, matching the 1D find_peaks; the callers inside this class that want it pass adaptive=True explicitly. |
False
|
snr_threshold
|
float
|
When adaptive=True, minimum SNR for peak detection. |
5.0
|
Returns:
| Type | Description |
|---|---|
list[PeakInfo2D]
|
List of PeakInfo2D objects containing peak positions and heights, |
list[PeakInfo2D]
|
with the plane's measurable artefacts removed. |
calculate_peak_volumes
#
calculate_peak_volumes(
peaks: list[PeakInfo2D],
radius_factor: float = 3.0,
use_magnitude: bool = True,
) -> list[PeakInfo2D]
Calculate volumes for 2D peaks using elliptical integration regions.
For each peak, estimates the peak width in both dimensions by finding the half-height contour, then integrates within an ellipse of radius_factor * half-width.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
List of PeakInfo2D objects from find_peaks(). |
required |
radius_factor
|
float
|
Multiplier for the half-width to define the integration ellipse (default 3.0 = ~99% of Gaussian). |
3.0
|
use_magnitude
|
bool
|
If True, integrate |z|, else integrate Re(z). |
True
|
Returns:
| Type | Description |
|---|---|
list[PeakInfo2D]
|
The same peaks with volume field populated. |
estimate_snr
#
estimate_snr(
signal_region: tuple[
tuple[float, float], tuple[float, float]
]
| None = None,
) -> float
Estimate signal-to-noise ratio of the 2D spectrum.
The noise sigma comes from estimate_noise_plane, which measures the quiet regions of each row along the direct dimension.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal_region
|
tuple[tuple[float, float], tuple[float, float]] | None
|
Optional ((f1_min, f1_max), (f2_min, f2_max)) to restrict signal calculation. If None, uses entire spectrum. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Signal-to-noise ratio (linear scale) |
infer_multiplets
#
infer_multiplets(
peaks: list[PeakInfo2D] | None = None,
group_axis: Literal["f1", "f2"] = "f1",
snr_threshold: float = 30.0,
margin_factor: float = 2.0,
) -> list[MultipletGroup2D]
Infer multiplet patterns from 2D spectrum using slice-and-project.
The groups are the ones group_peaks() labels the peaks with — there is
one clustering, not two — so every returned MultipletGroup2D.group_id
equals the group_id carried by its member peaks and peak_indices index
into the peaks list exactly as supplied. Callers that have already
grouped their peaks keep those labels; ungrouped peaks are grouped here.
For each group, cuts a rectangle from the 2D spectrum, projects (sums) along the grouping axis to produce a 1D micro-spectrum along the spread axis, then runs 1D multiplet scoring (spacing regularity + amplitude symmetry) on the projection peaks.
For HSQC (group_axis="f1"): Projects onto the 1H axis → detects the 1H multiplet. For COSY (group_axis="f2"): Projects onto the 13C axis → detects coupling partners.
Frequencies and couplings are reported in Hz, converted from the axis' own unit through that axis' reference frequency; a PPM axis with no usable reference leaves the Hz values at 0.0 and the couplings unset rather than reporting a PPM number under a Hz name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D] | None
|
Pre-detected peaks. If None, runs find_peaks(adaptive=True). |
None
|
group_axis
|
Literal['f1', 'f2']
|
Which axis is the grouping (projected-over) axis. "f1" — spread along F2 gives multiplicity. "f2" — spread along F1 gives multiplicity. |
'f1'
|
snr_threshold
|
float
|
SNR threshold for peak detection (if peaks not provided). |
30.0
|
margin_factor
|
float
|
How much to expand the bounding box around each cluster (as a multiple of the cluster extent in each axis). |
2.0
|
Returns:
| Type | Description |
|---|---|
list[MultipletGroup2D]
|
List of MultipletGroup2D with pattern, coupling constant, and score. |
measure_peak_footprints
#
Populate each peak's width_f1 / width_f2, in that axis' own units.
An NMR lineshape is separable in the two dimensions, so a peak covers an axis-aligned ellipse whose semi-axes are half its measured line width on each axis. The widths are the same half-height measurements the grouping tolerances are read off, so a group's drawn extent and the rule that put its members together come from one measurement of the data.
Peaks whose flank runs off the plane before reaching half height take the median width of the peaks that could be measured, which is the same fallback the tolerances use; if nothing on an axis could be measured at all, that axis falls back to _MIN_WINDOW_POINTS of digital resolution so a footprint is never zero-sized.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
Peaks to measure, from find_peaks(). |
required |
use_magnitude
|
bool
|
If True, measure widths on |z|, else on Re(z). |
True
|
Returns:
| Type | Description |
|---|---|
list[PeakInfo2D]
|
The same peaks with width_f1 and width_f2 populated. |
group_extents
#
group_extents(
peaks: list[PeakInfo2D], use_magnitude: bool = True
) -> dict[int, tuple[float, float, float, float]]
Return each group's extent on the plane, in the axes' own units.
The extent is the axis-aligned bounding box of the group's members' elliptical footprints — the union of every member's centre ± half its line width on each axis — so it depends only on the data and not on the viewport, the zoom level, or which members happen to be on screen. It is the 2D counterpart of the 1D MultipletGroup's start/end frequencies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
Grouped peaks; ungrouped peaks are grouped here first. |
required |
use_magnitude
|
bool
|
If True, measure widths on |z|, else on Re(z). |
True
|
Returns:
| Type | Description |
|---|---|
dict[int, tuple[float, float, float, float]]
|
group_id → (f1_min, f1_max, f2_min, f2_max). |
grouping_tolerances
#
grouping_tolerances(
peaks: list[PeakInfo2D],
width_factor: float = DEFAULT_WIDTH_FACTOR,
use_magnitude: bool = True,
) -> tuple[float, float]
Return the (F1, F2) grouping tolerances in each axis' own units.
Each tolerance is width_factor times the median half-height width of the supplied peaks along that axis, measured on this spectrum's own plane and expressed in that axis' own coordinates. Nothing is carried across from the other axis, so the two tolerances differ by however much the axes differ in nucleus, span and digitization; and nothing is assumed about which nucleus or experiment produced the plane. Each tolerance is widened to _MIN_WINDOW_POINTS points of its own digital resolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
Peaks whose line widths set the scale, from find_peaks(). |
required |
width_factor
|
float
|
Multiple of the measured width to allow within a group. |
DEFAULT_WIDTH_FACTOR
|
use_magnitude
|
bool
|
If True, measure widths on |z|, else on Re(z). |
True
|
Returns:
| Type | Description |
|---|---|
tuple[float, float]
|
(tolerance_f1, tolerance_f2), each in the units of its own axis. |
group_peaks
#
group_peaks(
peaks: list[PeakInfo2D],
width_factor: float = DEFAULT_WIDTH_FACTOR,
tolerance_f1: float | None = None,
tolerance_f2: float | None = None,
) -> list[PeakInfo2D]
Group 2D peaks by proximity, along whichever directions the plane shows.
Which rule applies is measured, never inferred from a pulse-program name, a title or a filename: measure_alignment reads the direction the plane's own peaks step in when they are close together (see alignment_geometry). Three outcomes, all of them the same measurement:
The peaks line up on a diagonal — the components of one multiplet differ in
both coordinates but share shared_f1 * F1 + shared_f2 * F2. They are
grouped on that shared coordinate and chained along the direction they are
spread in. A J-resolved plane is this case with a slope near -1, but so is
any other plane whose multiplets happen to lie on a line, at any slope.
The peaks line up along one axis — the other coordinate is shared, because that dimension carries no structure (it is decoupled). Same rule, with the shared coordinate being one of the axes: cross peaks keep their whole multiplet without ever reaching across the decoupled axis.
The peaks do not line up — structure on both axes, or nothing but noise — and the per-axis rule below applies untouched.
Each axis is divided by its own tolerance, so one unit of scaled distance means "one tolerance" on either axis regardless of how the two axes differ in span, digitization or nucleus. Peaks are grouped so that every member of a group is within tolerance of every other member on both axes -- a chain of moderately spaced peaks does not merge endpoints that are far apart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
List of PeakInfo2D objects from find_peaks(). |
required |
width_factor
|
float
|
Multiple of the measured line width to allow within a group. Ignored on an axis given an explicit tolerance. |
DEFAULT_WIDTH_FACTOR
|
tolerance_f1
|
float | None
|
Explicit F1 tolerance in F1 axis units. |
None
|
tolerance_f2
|
float | None
|
Explicit F2 tolerance in F2 axis units. |
None
|
Returns:
| Type | Description |
|---|---|
list[PeakInfo2D]
|
The same peaks with group_id assigned. |
alignment_geometry
#
alignment_geometry(
peaks: list[PeakInfo2D],
) -> AlignmentGeometry | None
Measure the direction this plane's peaks line up in, or None if unmeasurable.
The measurement is made on the peaks themselves, in Hz, at the line width
this plane actually has on each axis — see
:func:~tqt_nmr.core.spectrum.mixins.jres.measure_alignment. It answers the
only question grouping needs: which combination of the two coordinates do
the components of one feature share, and along which direction are they
spread. No name, title, filename or nucleus takes part.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo2D]
|
The peaks to measure, from find_peaks(). |
required |
Returns:
| Type | Description |
|---|---|
AlignmentGeometry | None
|
The geometry, or None when there are fewer than two peaks or an axis is |
AlignmentGeometry | None
|
in ppm with no reference frequency, so the two axes cannot be put on one |
AlignmentGeometry | None
|
Hz scale. Grouping then falls back to the per-axis rule, which needs no |
AlignmentGeometry | None
|
such conversion. |
_axes_convert_to_hz
#
Whether both axes can be expressed in Hz, which the experiment rules need.
A PPM axis with no reference frequency cannot be placed on a Hz scale, so neither the J-resolved shear nor a coupling-sized tolerance means anything on it; grouping then falls back to the per-axis rule, which needs no such conversion because it works entirely in each axis' own units.
_peak_positions_hz
#
_peak_positions_hz(
peaks: list[PeakInfo2D],
) -> tuple[ndarray, ndarray]
The peaks' (F1, F2) positions in Hz, whatever unit the axes are stored in.
_find_peaks_local_max
#
_find_peaks_local_max(
z: ndarray,
min_distance_points: int | None,
min_height_ratio: float,
adaptive: bool,
snr_threshold: float,
use_magnitude: bool,
) -> list[PeakInfo2D]
Classical local maximum + threshold peak detection.
correct_phase_auto
#
correct_phase_auto(
axis: Literal["f1", "f2", "both"] = "both",
method: Literal["acme", "peak_minima"] = "acme",
trace: Literal["max", "sum"] = "max",
per_trace: bool = False,
multi_start: bool = False,
return_phases: bool = False,
) -> Self | tuple[Self, dict[str, tuple[float, float]]]
Apply automatic phase correction along the specified axis.
Phase is a property of the acquisition, not of an individual row or column, so a
single (p0, p1) pair is determined ONCE per axis from a representative trace and
then applied to every row/column. Phasing each trace independently
(per_trace=True) introduces discontinuities along the indirect dimension and
breaks the lineshape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
axis
|
Literal['f1', 'f2', 'both']
|
Which axis to correct ("f1", "f2", or "both") |
'both'
|
method
|
Literal['acme', 'peak_minima']
|
Objective used when a trace resolves too few multiplets for the peak regression. |
'acme'
|
trace
|
Literal['max', 'sum']
|
How to pick the representative trace - "max" uses the trace with the largest total magnitude, "sum" uses the projection (sum) over the other axis. |
'max'
|
per_trace
|
bool
|
Optimise and apply a separate phase for every row/column. Slow and physically wrong for most data; off by default. |
False
|
multi_start
|
bool
|
Accepted for signature compatibility with the settings API; the objective fallback on this path is single-start, so it selects nothing. |
False
|
return_phases
|
bool
|
If True, also return the phases applied per axis. |
False
|
Returns:
| Type | Description |
|---|---|
Self | tuple[Self, dict[str, tuple[float, float]]]
|
Self, or (Self, {"f1": (p0, p1), "f2": (p0, p1)}) if return_phases=True. |
correct_phase_manual
#
correct_phase_manual(
p0: float,
p1: float = 0.0,
axis: Literal["f1", "f2", "both"] = "both",
p2: float = 0.0,
) -> Self
Apply manual phase correction along specified axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p0
|
float
|
Zero-order phase correction (degrees) |
required |
p1
|
float
|
First-order phase correction (degrees) |
0.0
|
axis
|
Literal['f1', 'f2', 'both']
|
Which axis to correct ("f1", "f2", or "both") |
'both'
|
p2
|
float
|
Second-order phase correction (degrees) |
0.0
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
_phase_auto_hypercomplex
#
_phase_auto_hypercomplex(
axis: Literal["f1", "f2", "both"],
method: Literal["acme", "peak_minima"],
trace: Literal["max", "sum"],
return_phases: bool,
) -> Self | tuple[Self, dict[str, tuple[float, float]]]
Phase a hypercomplex plane through its quadrants.
F2 is measured on the F2-complex plane -- the absorption and dispersion channels of the plane that will actually be displayed -- and applied as a rotation between the quadrants. F1 is the complex axis of both planes, so it is an ordinary rotation applied to each of them.
_phase_along_last_axis
staticmethod
#
_phase_along_last_axis(
amps: ndarray,
method: Literal["acme", "peak_minima"],
trace: Literal["max", "sum"],
per_trace: bool,
) -> tuple[ndarray, tuple[float, float]]
Phase every row of amps (last axis), returning (phased, (p0, p1)).
correct_baseline
#
correct_baseline(
method: BaselineMethod2D = "arpls",
axis: Literal["f1", "f2", "both"] = "both",
edge_fraction: float = 0.1,
poly_order: int = 1,
smoothness_decades: float = 10.0,
asymmetry: float = 0.001,
iterations: int = 50,
tol: float = 0.001,
mask_sigma: float = 3.0,
als_lambda: float = 100000.0,
als_p: float = 0.01,
als_iterations: int = 10,
valley_prominence: float = 0.02,
imaginary_method: ImaginaryBaselineMethod = "auto",
allow_unsafe_poly_order: bool = False,
**kwargs,
) -> Self
Apply baseline correction to a 2D spectrum.
Every method other than "plane" is applied trace by trace along the requested axis and behaves exactly as the 1D method of the same name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
BaselineMethod2D
|
Baseline correction method. "plane" fits a single 2D plane to the
four corner regions; all other names are the 1D methods documented on
|
'arpls'
|
axis
|
Literal['f1', 'f2', 'both']
|
Which axis to correct ("f1", "f2", or "both"); ignored for "plane". |
'both'
|
edge_fraction
|
float
|
Fraction of each edge used by the edge-anchored methods and by "plane" to size its corner regions. |
0.1
|
poly_order
|
int
|
Polynomial order for "polynomial" and "masked_poly". |
1
|
smoothness_decades
|
float
|
Whittaker smoothness as log10(lambda) quoted at 16384 points, length-normalised per trace. |
10.0
|
asymmetry
|
float
|
Asymmetry parameter |
0.001
|
iterations
|
int
|
Maximum reweighting iterations for the Whittaker methods. |
50
|
tol
|
float
|
Relative convergence tolerance for the Whittaker methods. |
0.001
|
mask_sigma
|
float
|
Signal-mask threshold in robust noise sigma. |
3.0
|
als_lambda
|
float
|
Smoothness parameter for "als", quoted at 16384 points and
scaled by |
100000.0
|
als_p
|
float
|
Asymmetry parameter for "als". |
0.01
|
als_iterations
|
int
|
Iteration count for "als"; must be >= 1. |
10
|
valley_prominence
|
float
|
Minimum valley prominence as a fraction of max|y|. |
0.02
|
imaginary_method
|
ImaginaryBaselineMethod
|
How to treat the dispersion channel; see
|
'auto'
|
allow_unsafe_poly_order
|
bool
|
Permit |
False
|
**kwargs
|
Additional parameters (ignored for compatibility) |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
Raises:
| Type | Description |
|---|---|
ProcessingParameterError
|
If |
_baseline_plane
#
Fit and subtract a 2D plane estimated from the four corner regions.
Each corner contributes the median of a whole region rather than a single pixel, so an isolated noise spike cannot tilt the plane.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
edge_fraction
|
float
|
Fraction of each axis used to size the corner regions. |
required |
_baseline_per_axis
#
_baseline_per_axis(
axis: Literal["f1", "f2", "both"],
params: BaselineParams,
imaginary_method: ImaginaryBaselineMethod,
) -> None
Apply the 1D baseline estimator to every row and/or column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
axis
|
Literal['f1', 'f2', 'both']
|
Axis/axes to correct. |
required |
params
|
BaselineParams
|
Estimator selection and parameters. |
required |
imaginary_method
|
ImaginaryBaselineMethod
|
How to treat the dispersion channel. |
required |
__getitem__
#
Slice-only 2D indexing. Example: spec[:, 10:50] (Indexing with ints that reduce dimensionality is not supported here.)
phase_f2_hypercomplex
#
Rotate the F2 lineshape of a hypercomplex spectrum.
An F2 rotation of hypercomplex data is a rotation between the two quadrant
planes, so an ordinary ps2 along F2 must not be used instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p0
|
float
|
Zero-order phase in degrees. |
required |
p1
|
float
|
First-order phase in degrees, ramped across F2. |
0.0
|
p2
|
float
|
Second-order phase in degrees. |
0.0
|
Returns:
| Type | Description |
|---|---|
Self
|
Self. |
Raises:
| Type | Description |
|---|---|
ProcessingParameterError
|
If the second quadrant is not available. |
phase_f1_hypercomplex
#
Rotate the F1 lineshape, keeping the second quadrant in step.
F1 is the complex axis of both quadrant planes, so this is an ordinary phase rotation along F1 -- applied to both planes so a later F2 rotation still sees a consistent pair.
reverse_f1
#
Traverse the F1 amplitudes in the opposite sense, quadrant included.
Whether the transformed F1 axis runs with or against the frequency axis is a
property of the acquisition, and the import handlers flip it for the modes that
need it. Doing it through this method keeps the second hypercomplex quadrant in
step with amplitudes; flipping amplitudes alone would silently pair the
two planes the wrong way round for a later F2 phase correction.
f2_complex_plane
#
The F2-complex spectrum of the F1 absorption channel, RR + i*RI.
This is the array an F2 phase has to be measured on: it holds the F2
absorption and dispersion channels of the plane that will be displayed. Falls
back to amplitudes when the spectrum is not hypercomplex.
normalize
#
Normalize spectrum amplitudes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Literal['max', 'rms', 'unit']
|
Normalization method ("max", "rms", "unit") |
'max'
|
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
to_ppm
#
Convert spectrum frequency axes to PPM scale.
Returns:
| Type | Description |
|---|---|
Self
|
New spectrum with PPM units (or copy if already in PPM) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If reference frequency is not set for either axis and not already in PPM |
smooth
#
Apply 2D Gaussian smoothing to spectrum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float
|
Standard deviation for Gaussian kernel |
2.0
|
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
symmetrize
#
Symmetrize a 2D homonuclear spectrum by averaging with its transpose.
This is valid only for homonuclear experiments (COSY, NOESY, TOCSY) where F1 and F2 correspond to the same nucleus, and the amplitude matrix is square.
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
Raises:
| Type | Description |
|---|---|
ProcessingParameterError
|
If the spectrum is not square or not homonuclear |
trim_frequency_range
#
trim_frequency_range(
f1_min: float = -inf,
f1_max: float = inf,
f2_min: float = -inf,
f2_max: float = inf,
) -> Self
Trim spectrum to specified frequency ranges.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f1_min
|
float
|
Range for f1 axis (from) |
-inf
|
f1_max
|
float
|
Range for f1 axis (to) |
inf
|
f2_min
|
float
|
Range for f2 axis (from) |
-inf
|
f2_max
|
float
|
Range for f2 axis (to) |
inf
|
Returns:
| Type | Description |
|---|---|
Self
|
A new, trimmed spectrum (the original is left unchanged). |
convert_frequency_unit
#
convert_frequency_unit(
target_unit: FrequencyUnit,
axis: Literal["f1", "f2", "both"] = "both",
) -> Self
Convert frequency axes to different units.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_unit
|
FrequencyUnit
|
Target frequency unit |
required |
axis
|
Literal['f1', 'f2', 'both']
|
Which axis to convert |
'both'
|
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
get_base_spectrum
#
Get the main spectrum without reference data.
Returns a copy of this spectrum with reference fields set to None.
get_reference_f1
#
get_reference_f1() -> Spectrum1D | None
Get F1 (indirect dimension) reference spectrum.
get_reference_f2
#
get_reference_f2() -> Spectrum1D | None
Get F2 (direct dimension) reference spectrum.
apply_reference_correction
#
Remove the reference acquisitions' phase from the plane.
Only the reference's phase is removed; every |amplitude| is left
bit-for-bit unchanged, so there is no cost in signal-to-noise. Points where
the reference is exactly zero define no phase and are left alone.
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
to_signal
#
to_signal(
t1_unit: TimeUnit = MICROSECOND,
t2_unit: TimeUnit = MICROSECOND,
carrier_offset_hz_1: float | None = None,
carrier_offset_hz_2: float | None = None,
first_point_scale: bool | None = None,
) -> Signal2D
Convert spectrum back to time-domain signal via inverse 2D FFT.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t1_unit
|
TimeUnit
|
Time unit for t1 axis (default: MICROSECOND) |
MICROSECOND
|
t2_unit
|
TimeUnit
|
Time unit for t2 axis (default: MICROSECOND) |
MICROSECOND
|
carrier_offset_hz_1
|
float | None
|
Carrier offset for F1 in Hz. If provided, subtracted from F1 frequencies before inverse FFT and stored in output signal. |
None
|
carrier_offset_hz_2
|
float | None
|
Carrier offset for F2 in Hz. If provided, subtracted from F2 frequencies before inverse FFT and stored in output signal. |
None
|
first_point_scale
|
bool | None
|
Whether to undo the forward transform's first-point halving
(doubling |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Signal2D |
Signal2D
|
Time-domain 2D signal |
_setup_from_arrays
#
_setup_from_arrays(
f1: FloatArray,
f2: FloatArray,
amplitudes: ndarray,
f1_unit: FrequencyUnit,
f2_unit: FrequencyUnit,
) -> None
_sync_numpy_to_proto
#
Override to sync reference spectra before serialization.