Skip to content

Spectrum1D #

Spectrum1D(
    frequencies: FloatArray | None = None,
    amplitudes: ComplexArray | None = None,
    frequency_unit: FrequencyUnit = HERTZ,
    reference_frequency_hz: float | None = None,
    metadata: dict[str, str] | None = None,
)

1D frequency-domain spectrum.

Attributes:

Name Type Description
frequencies FloatNdArray

Frequency coordinates

amplitudes ComplexNdArray

Complex amplitudes

frequency_unit

Unit of frequency coordinates

reference_frequency_hz

Reference frequency for PPM calculations

metadata dict[str, str]

Optional metadata dictionary

processing_history class-attribute instance-attribute #

processing_history: ProcessingHistory | None = (
    betterproto2.field(
        6, betterproto2.TYPE_MESSAGE, optional=True
    )
)

Processing history - read-only audit log of all operations applied to this spectrum

history property #

history: Sequence[ProcessingHistoryEntry]

nucleus property writable #

nucleus: Nucleus

Nucleus type for this spectrum's frequency axis.

metadata class-attribute instance-attribute #

metadata: dict[str, str] = betterproto2.field(
    4,
    betterproto2.TYPE_MAP,
    map_meta=betterproto2.map_meta(
        betterproto2.TYPE_STRING, betterproto2.TYPE_STRING
    ),
)

Optional metadata

_frequency_axis class-attribute instance-attribute #

_frequency_axis: NdAxis | None = betterproto2.field(
    1, betterproto2.TYPE_MESSAGE, optional=True
)

Exactly one frequency axis (enforced by validation)

_amplitudes class-attribute instance-attribute #

_amplitudes: NdComplexArray | None = betterproto2.field(
    2, betterproto2.TYPE_MESSAGE, optional=True
)

1D complex amplitude data (shape = [N])

solvent_reference_ppm class-attribute instance-attribute #

solvent_reference_ppm: float | None = betterproto2.field(
    5, betterproto2.TYPE_FLOAT, optional=True
)

Solvent reference PPM for calibration (set from sample's solvent info)

_points_view instance-attribute #

_points_view: ndarray | None

_np_dirty_points instance-attribute #

_np_dirty_points: bool

_visualization_handler class-attribute instance-attribute #

_visualization_handler: str = ''

visualization_handler property writable #

visualization_handler: str

Plugin visualization handler ID (e.g., 'nanalysis:cosy_2d').

axis_role property writable #

axis_role: AxisRole

Physical role of this spectrum's frequency axis.

z property writable #

z: FloatArray

__module__ class-attribute instance-attribute #

__module__ = GrpcSpectrum1D.__module__

frequencies instance-attribute #

frequencies: FloatNdArray = np.asarray(
    frequencies, dtype=np.float64
)

amplitudes instance-attribute #

amplitudes: ComplexNdArray = np.asarray(
    amplitudes, dtype=np.complex128
)

frequency_unit instance-attribute #

frequency_unit = frequency_unit

reference_frequency_hz instance-attribute #

reference_frequency_hz = float(reference_frequency_hz)

is_complex property #

is_complex: bool

frequency_range property #

frequency_range: tuple[float, float]

frequency_range_hz property #

frequency_range_hz: tuple[float, float]

Frequency range of the spectrum in Hz.

_init_history #

_init_history() -> None

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

_copy_history_to #

_copy_history_to(other: Self) -> None

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

_axis_ppm #

_axis_ppm() -> ndarray

The frequency axis in PPM, whatever unit it is stored in.

A spectrum converted for display already holds PPM; anything else is baseband offset from the carrier and needs the reference frequency.

suppress_region #

suppress_region(
    center: float,
    width: float,
    method: SuppressionMethod = SUPPRESS_INTERPOLATE,
) -> Self

Suppress a spectral region in-place.

Parameters:

Name Type Description Default
center float

Center of the region to suppress (in spectrum frequency units).

required
width float

Full width of the region to suppress (in spectrum frequency units).

required
method SuppressionMethod

Suppression method enum.

SUPPRESS_INTERPOLATE

Returns:

Type Description
Self

Self (mutated in-place) for method chaining.

Raises:

Type Description
ValueError

If the region is entirely outside the spectrum range.

suppress_solvent_line #

suppress_solvent_line(
    expected_ppm: float,
    search_window_ppm: float = DEFAULT_SOLVENT_SEARCH_WINDOW_PPM,
) -> SolventLineFit | None

Subtract the fitted lineshape of the solvent line near a known shift.

Only the fitted solvent component is removed; any other signal in the same region, including signal underneath the solvent, is left in place. Calling this a second time on the same spectrum removes nothing further.

Parameters:

Name Type Description Default
expected_ppm float

The shift the sample's solvent predicts for the observed nucleus, in ppm.

required
search_window_ppm float

PPM searched either side of expected_ppm for the line's actual position.

DEFAULT_SOLVENT_SEARCH_WINDOW_PPM

Returns:

Type Description
SolventLineFit | None

The component that was subtracted, or None when no solvent line was

SolventLineFit | None

found or the spectrum was already suppressed.

Raises:

Type Description
ValueError

If the spectrum has no reference frequency, so no ppm position can be located on its axis.

reference_to_solvent #

reference_to_solvent(
    solvent: PredefinedSolvent,
    nucleus: Nucleus | None = None,
    search_window_ppm: float = 0.5,
) -> Self

Reference spectrum to a known solvent peak.

Finds the tallest peak within search_window_ppm of the expected solvent position and shifts the spectrum so that peak is at the correct PPM.

Parameters:

Name Type Description Default
solvent PredefinedSolvent

Predefined solvent enum value

required
nucleus Nucleus | None

Target nucleus (defaults to 1H)

None
search_window_ppm float

PPM range to search for solvent peak

0.5

Returns:

Type Description
Self

Self with shifted frequencies

Raises:

Type Description
ValueError

If solvent/nucleus combination not found or no peak in window

reference_to_ppm #

reference_to_ppm(
    target_ppm: float,
    search_window_ppm: float = 0.5,
    use_centroid: bool = False,
) -> Self

Shift spectrum so the tallest peak near target is at target_ppm.

Parameters:

Name Type Description Default
target_ppm float

The PPM value where the peak should be placed

required
search_window_ppm float

PPM range around target to search for peak

0.5
use_centroid bool

If True, use weighted centroid instead of maximum

False

Returns:

Type Description
Self

Self with shifted frequencies

Raises:

Type Description
ValueError

If reference_frequency_hz is not set or no peak found

reference_manual #

reference_manual(
    click_ppm: float, target_ppm: float
) -> Self

Manually reference spectrum by specifying current and target PPM.

This is for UI-driven referencing where user clicks on a peak and specifies what PPM it should be shifted to.

Parameters:

Name Type Description Default
click_ppm float

The current PPM value of the clicked position

required
target_ppm float

The PPM value it should be shifted to

required

Returns:

Type Description
Self

Self with shifted frequencies

Raises:

Type Description
ValueError

If reference_frequency_hz is not set

align_by_correlation #

align_by_correlation(reference: Spectrum1D) -> Self

Align this spectrum to a reference using cross-correlation.

Computes the lag that maximizes the cross-correlation between the magnitude spectra, then shifts the frequency axis by that amount.

Both spectra must use the same frequency spacing (same number of points over the same spectral width). The reference spectrum is not modified.

Parameters:

Name Type Description Default
reference Spectrum1D

Reference spectrum to align to.

required

Returns:

Type Description
Self

Self with shifted frequencies.

Raises:

Type Description
ValueError

If the spectra have different lengths.

_shift_in_axis_unit #

_shift_in_axis_unit(shift_ppm: float) -> float

Convert a PPM shift into the unit the frequency axis is stored in.

apply_harmonics_mask #

apply_harmonics_mask(
    frequency_hz: float = 50, k: float = 0.99
) -> Self

Masks out harmonics by a cosine wave.

Parameters:

Name Type Description Default
frequency_hz float

Frequency of the harmonics to mask.

50
k float

Cutoff threshold of the cosine wave (higher = thinner mask). Defaults to 0.99.

0.99

remove_peak #

remove_peak(
    peak_frequency_hz: float, width_hz: float = 5.0
) -> Self

Zero-out a small window around peak_frequency_hz.

halves_symmetry_score #

halves_symmetry_score(central_frequency_hz: float) -> float

Compute a simple symmetry score of |z| around central_frequency_hz. 1.0 is perfectly symmetric; 0.0 very asymmetric.

Parameters:

Name Type Description Default
central_frequency_hz float

Central frequency to split the spectrum with.

required

Returns:

Name Type Description
float float

Inverse cosine distance. The lower the score the higher the spectrum's symmetry rate.

find_peaks #

find_peaks(
    min_distance_hz: float | None = None,
    min_height_ratio: float = 0.05,
    prominence: float | None = None,
    use_magnitude: bool = False,
    adaptive: bool = False,
    snr_threshold: float = 5.0,
    mode: Literal["ml", "deconv"] = "ml",
    ml_threshold: float | None = None,
) -> list[PeakInfo]

Find peaks in spectrum with advanced filtering.

Parameters:

Name Type Description Default
min_distance_hz float | None

Minimum peak distance (in Hz). If None, auto ~1% span.

None
min_height_ratio float

Minimum peak height as a ratio of the maximum height (0-1). IGNORED ENTIRELY when adaptive=True, which takes no parameters at all.

0.05
prominence float | None

Minimum prominence as a ratio of the maximum height (0-1). When adaptive=False this defaults to None, meaning NO prominence filter is applied at all and every local maximum above min_height_ratio is reported. IGNORED when adaptive=True.

None
use_magnitude bool

If True, work on |z| else on Re(z).

False
adaptive bool

If True, run AUTO MODE: a statistically calibrated threshold pass followed by Richardson-Lucy deconvolution against the measured lineshape (see :func:tqt_nmr.analysis.peak_detection.detect_peak_indices_auto). It takes NO parameters — every quantity it needs is measured on the trace — so min_height_ratio, prominence, min_distance_hz and snr_threshold are all ignored. Not configurable by design.

False
snr_threshold float

DEPRECATED and ignored.

5.0
mode Literal['ml', 'deconv']

Which detector auto mode runs. Only meaningful with adaptive=True; the manual path always uses the fixed ratios above. "ml" (the default) is a learned model that finds shoulders and merged components a threshold pass cannot and is unaffected by line broadening; it requires a phased spectrum, since it detects inverted lines too and dispersive input yields peaks in the wrong places rather than none. "deconv" is the threshold pass plus deconvolution described under adaptive.

'ml'
ml_threshold float | None

The learned detector's operating point. Lower favours recall, higher favours precision. None uses the model's own default. Ignored unless mode="ml".

None

analyze_multiplet #

analyze_multiplet(
    center_frequency: float | None = None,
    search_width: float = 50.0,
    min_height_ratio: float = 0.1,
    method: Literal[
        "structure", "fast", "pascal", "deconvolution"
    ] = "structure",
    bootstrap_iterations: int | Literal["auto"] = "auto",
    bic_threshold: float = 15.0,
    peaks: list[PeakInfo] | None = None,
) -> MultipletAnalysis

Analyze peaks in a region to identify multiplet pattern and J-coupling.

Patterns reported: s, d, t, q, quint, sext, sept, the compound patterns (dd, dt, dq, ...), and m for a complex or unresolved multiplet.

Parameters:

Name Type Description Default
center_frequency float | None

Center of the search region (in spectrum units). If None and peaks provided, calculated from peaks.

None
search_width float

Width of search region on each side of center.

50.0
min_height_ratio float

Minimum peak height as ratio of tallest peak in region.

0.1
method Literal['structure', 'fast', 'pascal', 'deconvolution']

Analysis method: "structure" (default), "deconvolution", or "fast" (alias "pascal").

'structure'
bootstrap_iterations int | Literal['auto']

Number of bootstrap iterations for uncertainty estimation. Use "auto" for SNR-adaptive (default: SNR > 100 uses analytical, SNR 30-100 uses 50 iterations, SNR < 30 uses 200).

'auto'
bic_threshold float

BIC improvement threshold for peak count selection.

15.0
peaks list[PeakInfo] | None

Pre-detected peaks to use instead of re-detecting. When provided, the "fast" method uses these directly. For "structure"/"deconvolution", center_frequency and search_width are derived from peaks if not specified.

None

Returns:

Type Description
MultipletAnalysis

MultipletAnalysis with pattern, coupling constants, and confidence.

MultipletAnalysis

For deconvolution/structure methods, includes uncertainties and fit metrics.

analyze_multiplet_groups #

analyze_multiplet_groups(
    linkage_method: Literal[
        "single", "average", "complete", "ward"
    ] = "single",
    method: Literal[
        "structure", "fast", "pascal", "deconvolution"
    ] = "structure",
    peaks: list[PeakInfo] | None = None,
    infer_missing_lines: bool = False,
) -> MultipletGroupsAnalysis

Detect and analyze all multiplet groups in the spectrum.

Peaks come from AUTO-MODE detection (:meth:find_peaks with adaptive=True), which measures its own threshold from the trace.

Parameters:

Name Type Description Default
linkage_method Literal['single', 'average', 'complete', 'ward']

Hierarchical clustering linkage method ('single', 'average', 'complete', 'ward'). Default: 'single'.

'single'
method Literal['structure', 'fast', 'pascal', 'deconvolution']

Analysis method for pattern classification within each group: - "structure" (default): constrained J-pattern fit of the group's region, seeded by the group's peaks; reports J uncertainties. - "fast" (alias: "pascal"): position-only classification, no spectral fit. Cheap; use when the peak list is already reliable. - "deconvolution": peak-count selection, then the same constrained fit.

'structure'
peaks list[PeakInfo] | None

The lines to group and classify. Pass an already-detected (and possibly user-edited) peak list here to have grouping see exactly that list. Default (None) runs auto-mode detection.

None
infer_missing_lines bool

Allow a group to gain a line the detector did not pick, when the observed intensities are better explained by a pattern with one terminal line unobserved AND the trace holds the predicted intensity there. Off by default: an inferred line is a conclusion, not a measurement. Inferred lines are marked PeakOrigin.INFERRED, carry the PREDICTED height and no area, and are excluded from the group's measured arrays, so no integral or peak count changes either way.

False

Returns:

Type Description
MultipletGroupsAnalysis

MultipletGroupsAnalysis with all detected groups.

_infer_group_line #

_infer_group_line(
    group: MultipletGroup,
    all_peaks: list[PeakInfo],
    frequencies_hz: ndarray,
    amplitudes: ndarray,
    median_linewidth: float | None,
    detection_floor: float,
    enabled: bool,
    collect_into: list[PeakInfo] | None = None,
) -> list[PeakInfo]

A line this group's intensities demand but no detector picked, if any.

Returns a list of at most one so the caller stays branch-free; empty when the feature is off or the evidence did not justify a line.

Parameters:

Name Type Description Default
group MultipletGroup

The group to complete.

required
all_peaks list[PeakInfo]

Every detected line in the spectrum. Offered as neighbours in full, not just this group's: a line outside the group still puts its tail on the candidate position, and subtracting only the group's own lines reads an adjacent multiplet's flank as confirmation.

required
frequencies_hz ndarray

Spectrum frequency axis, Hz.

required
amplitudes ndarray

Real trace on that axis.

required
median_linewidth float | None

Fallback FWHM when this group's own lines report none, Hz.

required
detection_floor float

The detector's own height gate.

required
enabled bool

Whether inference was asked for at all.

required
collect_into list[PeakInfo] | None

Appended to with whatever is found, so the caller can gather every group's inferred lines without a separate step.

None

_analyze_multiplet_deconvolution #

_analyze_multiplet_deconvolution(
    center_frequency: float,
    search_width: float = 50.0,
    min_height_ratio: float = 0.1,
    bootstrap_iterations: int | Literal["auto"] = 200,
    bic_threshold: float = 15.0,
) -> MultipletAnalysis

Analyze multiplet using deconvolution with BIC model selection.

_analyze_multiplet_structure #

_analyze_multiplet_structure(
    center_frequency: float,
    search_width: float = 50.0,
    bootstrap_iterations: int | Literal["auto"] = "auto",
) -> MultipletAnalysis

Analyze multiplet using structure deconvolution with CWT detection.

integrate_peaks #

integrate_peaks(
    peaks: list[PeakInfo],
    integration_width: float | None = None,
    normalize_to: int
    | Literal["largest", "smallest"]
    | None = None,
    use_magnitude: bool = False,
    width_multiplier: float = DEFAULT_INTEGRATION_HWHM_MULTIPLIER,
) -> list[IntegrationResult]

Calculate integrated areas for detected peaks.

Parameters:

Name Type Description Default
peaks list[PeakInfo]

List of peaks to integrate (from find_peaks())

required
integration_width float | None

Width of integration region on each side of peak (in spectrum units). If None, auto-calculates from the peak's half-width at half-height times width_multiplier.

None
normalize_to int | Literal['largest', 'smallest'] | None

Index of peak to normalize to 1.00. If None, no normalization.

None
use_magnitude bool

If True, integrate |z|, else integrate Re(z).

False
width_multiplier float

Integration half-window in multiples of the peak's HWHM. A Lorentzian carries a heavy tail, so a narrow window introduces a systematic, linewidth-dependent quantitation bias: +/-1 HWHM captures 50% of the area, +/-20 (the default) ~97%. Reduce only for crowded spectra where neighbouring peaks would overlap the window.

DEFAULT_INTEGRATION_HWHM_MULTIPLIER

Returns:

Type Description
list[IntegrationResult]

List of IntegrationResult with area and bounds for each peak.

integrate_shifts #

integrate_shifts(
    peaks: list[PeakInfo],
    normalize_to: int
    | Literal["largest", "smallest"]
    | None = "largest",
    use_magnitude: bool = False,
    group_tolerance_hz: float = 20.0,
) -> list[ShiftIntegrationResult]

Integrate chemical shifts (multiplet groups) rather than individual peaks.

Groups peaks by their group_id (from multiplet analysis) or by proximity, then integrates the full region spanning each group. Uses integrate_peaks internally and sums per-peak areas within each group.

Parameters:

Name Type Description Default
peaks list[PeakInfo]

List of peaks (from find_peaks(), ideally with group_id set)

required
normalize_to int | Literal['largest', 'smallest'] | None

Which group to normalize to 1.00.

'largest'
use_magnitude bool

If True, integrate |z|, else integrate Re(z).

False
group_tolerance_hz float

If peaks lack group_id, group by proximity (Hz).

20.0

Returns:

Type Description
list[ShiftIntegrationResult]

List of ShiftIntegrationResult, one per chemical shift group.

deconvolve_region #

deconvolve_region(
    f_min: float,
    f_max: float,
    n_peaks: int | None = None,
    bootstrap_iterations: int = 200,
) -> DeconvolutionResult

Full peak deconvolution for a spectral region.

Parameters:

Name Type Description Default
f_min float

Minimum frequency (Hz)

required
f_max float

Maximum frequency (Hz)

required
n_peaks int | None

Number of peaks to fit (auto-detect if None)

None
bootstrap_iterations int

Bootstrap iterations for uncertainty

200

Returns:

Type Description
DeconvolutionResult

DeconvolutionResult with fitted peaks and uncertainties

estimate_snr #

estimate_snr(
    signal_region: tuple[float, float] | None = None,
) -> float

Estimate signal-to-noise ratio of the spectrum.

Parameters:

Name Type Description Default
signal_region tuple[float, float] | None

Optional (f_min, f_max) to restrict signal calculation. If None, uses entire spectrum.

None

Returns:

Type Description
float

Signal-to-noise ratio (linear scale)

_peak_infos_from_indices #

_peak_infos_from_indices(
    x: ndarray,
    y: ndarray,
    idxs: ndarray,
    fallback_linewidth_hz: float | None = None,
) -> list[PeakInfo]

Wrap detected sample indices as PeakInfo, with prominence and FWHM.

correct_baseline #

correct_baseline(
    method: BaselineMethod1D = "arpls",
    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,
    valley_preserve_integral: bool = False,
    valley_clip_negative: bool = False,
    imaginary_method: ImaginaryBaselineMethod = "auto",
    allow_unsafe_poly_order: bool = False,
    **kwargs,
) -> Self

Apply baseline correction to the absorption channel.

Parameters:

Name Type Description Default
method BaselineMethod1D

Baseline correction method: - "arpls" (default): asymmetrically reweighted penalised least squares, restricted to a signal-free mask and fitted on an extrapolated extension of the trace. - "airpls": adaptive iteratively reweighted penalised least squares. Tends to inflate peak amplitudes; prefer "arpls". - "asls": asymmetric least squares with asymmetry and a length-normalised lambda. - "masked_poly": polynomial of order poly_order fitted to the signal-free mask across the whole spectrum. - "snip": iterative peak clipping, for wide-line spectra. - "polynomial": polynomial fitted to the spectrum edges only. - "als": fixed-iteration asymmetric least squares over the signal-free mask, with als_lambda iterations of als_iterations. - "valley": interpolation through the minima between peaks. - "edge": constant offset from the mean of the two edge medians. - "median": constant offset from the median of both edge regions.

'arpls'
edge_fraction float

Fraction of each spectrum edge used by the edge-anchored methods ("edge", "median", "polynomial").

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; the applied lambda is scaled by (N / 16384) ** 4 so the physical smoothing width is independent of spectrum length. Useful range 7-12.

10.0
asymmetry float

Asymmetry parameter p for "asls".

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; points with |y - median| > mask_sigma * sigma are excluded from the fit.

3.0
als_lambda float

Smoothness parameter for "als", quoted at 16384 points and scaled by (N / 16384) ** 4.

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
valley_preserve_integral bool

Rescale the corrected spectrum so its integral matches the input integral. The scale factor is unbounded when the corrected integral is small, so quantitative work must leave this off.

False
valley_clip_negative bool

Clamp negative values to zero before rescaling. Clipping makes the residual noise one-sided instead of zero-mean, which invalidates downstream noise/SNR estimates and biases integrals upward.

False
imaginary_method ImaginaryBaselineMethod

How to baseline-correct the dispersion (imaginary) channel: - "auto" (default): apply the same constant offset for the "edge" and "median" methods, and leave the dispersion channel untouched for every other method. Only a constant offset is well defined on a bipolar lineshape; anything else rotates the spectrum's phase. - "same": apply the absorption-channel method to the imaginary part. - "polynomial": use the edge-anchored polynomial fit. - "none": leave the imaginary part untouched.

'auto'
allow_unsafe_poly_order bool

Permit poly_order above MAX_SAFE_EDGE_POLY_ORDER for the edge-only "polynomial" method instead of clamping it with a warning.

False
**kwargs

Additional parameters (ignored for compatibility)

{}

Returns:

Type Description
Self

Self

Raises:

Type Description
ProcessingParameterError

If method is not a supported baseline method.

correct_phase_auto #

correct_phase_auto(
    method: Literal["acme", "peak_minima"] = "acme",
    peak_width: int = 100,
    return_phases: Literal[False] = False,
    verbose: bool = False,
    multi_start: bool | None = None,
    p0_starts: Sequence[float] | None = None,
    p1_starts: Sequence[float] | None = None,
    strategy: Literal["seeded", "multistart"] = "seeded",
    order: Literal["auto", 1, 2] | None = None,
) -> Self
correct_phase_auto(
    method: Literal["acme", "peak_minima"] = "acme",
    peak_width: int = 100,
    return_phases: Literal[True] = True,
    verbose: bool = False,
    multi_start: bool | None = None,
    p0_starts: Sequence[float] | None = None,
    p1_starts: Sequence[float] | None = None,
    strategy: Literal["seeded", "multistart"] = "seeded",
    order: Literal["auto", 1, 2] | None = None,
) -> tuple[Self, tuple[float, ...]]
correct_phase_auto(
    method: Literal["acme", "peak_minima"] = "acme",
    peak_width: int = 100,
    return_phases: bool = False,
    verbose: bool = False,
    multi_start: bool | None = None,
    p0_starts: Sequence[float] | None = None,
    p1_starts: Sequence[float] | None = None,
    strategy: Literal["seeded", "multistart"] = "seeded",
    order: Literal["auto", 1, 2] | None = None,
) -> Self | tuple[Self, tuple[float, ...]]

Apply automatic phase correction.

Parameters:

Name Type Description Default
method Literal['acme', 'peak_minima']

Objective used by strategy="multistart", and by the seeded strategy's fallback when the spectrum has too few resolved multiplets.

'acme'
peak_width int

Width of the ROI for the peak_minima objective, in points.

100
return_phases bool

Whether to also return the applied phases: (p0, p1) for a first-order model, (p0, p1, p2) when a second-order model is selected.

False
verbose bool

Whether to log the optimisation process.

False
multi_start bool | None

Try multiple starting points. Accepted by strategy="multistart" only.

None
p0_starts Sequence[float] | None

Explicit zero-order starting points in degrees, defaulting to 8 points spanning 0-315 deg. Accepted by strategy="multistart" only.

None
p1_starts Sequence[float] | None

Explicit first-order starting points in degrees, defaulting to (0.0,). Accepted by strategy="multistart" only.

None
strategy Literal['seeded', 'multistart']

Which algorithm to use.

  • "seeded" (default): measure the phase at the peaks and regress it.
  • "multistart": multi-start Nelder-Mead over p0_starts x p1_starts, ranked by :func:phase_objective.
'seeded'
order Literal['auto', 1, 2] | None

Model order for the seeded strategy -- 1, 2, "auto", or None to take it from NUCLEI_PHASE_ORDER.

None

Returns:

Type Description
Self | tuple[Self, tuple[float, ...]]

Self if return_phases=False (default), otherwise (Self, phases).

Raises:

Type Description
TypeError

If a multi-start-only argument is supplied to the seeded strategy.

ProcessingParameterError

If the model order is not recognised.

correct_phase_manual #

correct_phase_manual(
    p0: float,
    p1: float = 0.0,
    method: Literal["linear", "exponential"] = "linear",
    p2: float = 0.0,
) -> Self

Apply manual phase correction.

Parameters:

Name Type Description Default
p0 float

Zero-order phase correction (degrees)

required
p1 float

First-order phase correction (degrees)

0.0
method Literal['linear', 'exponential']

Manual phase correction method

'linear'
p2 float

Second-order phase correction (degrees). Supported by "linear".

0.0

Returns:

Type Description
Self

Self

to_ppm #

to_ppm() -> Self

Convert spectrum to PPM scale.

Returns:

Type Description
Self

New spectrum in PPM units (or copy if already in PPM)

Raises:

Type Description
ValueError

If reference frequency is not set

smooth #

smooth(
    sigma: float = 4.0, preserve_integral: bool = True
) -> Self

Apply Gaussian smoothing to spectrum.

Parameters:

Name Type Description Default
sigma float

Standard deviation for Gaussian kernel

4.0
preserve_integral bool

Whether to preserve total integral

True

Returns:

Type Description
Self

Self

trim_frequency_range #

trim_frequency_range(
    f_min: float = -inf, f_max: float = inf
) -> Self

Trim spectrum to specified frequency range.

Parameters:

Name Type Description Default
f_min float

Minimum frequency (in spectrum's current units)

-inf
f_max float

Maximum frequency (in spectrum's current units)

inf

Returns:

Type Description
Self

A new, trimmed spectrum (the original is left unchanged).

center_spectrum #

center_spectrum(method: str = 'max') -> Self

Center the spectrum by shifting the main peak to 0.

Parameters:

Name Type Description Default
method str

Centering method ("max" or "centroid")

'max'

Returns:

Type Description
Self

Self

convert_frequency_unit #

convert_frequency_unit(
    target_unit: FrequencyUnit,
    reference_freq_hz: float | None = None,
) -> Self

Convert spectrum to different frequency unit.

Parameters:

Name Type Description Default
target_unit FrequencyUnit

Target frequency unit.

required
reference_freq_hz float | None

Reference frequency in Hz, required when converting to or from PPM. If omitted, the spectrum's own reference_frequency_hz is used.

None

Raises:

Type Description
ProcessingParameterError

If a PPM conversion is requested and no reference frequency is available from either source.

shift #

shift(
    shift: float, shift_unit: FrequencyUnit | None = None
) -> Self

Shift the frequency axis by a constant amount.

If shift_unit is PPM, requires reference_frequency_hz.

interpolate_at #

interpolate_at(
    sample_frequencies: FloatArray,
) -> ComplexNdArray

Interpolate spectrum at given frequencies.

_is_one_sided_spectrum #

_is_one_sided_spectrum() -> bool

Detect whether the frequency axis covers only non-negative frequencies.

Prefers the fft_method recorded in metadata by Signal1D.to_spectrum; falls back to inspecting the axis (a two-sided, fftshift-ordered axis always contains negative frequencies).

normalize #

normalize(
    method: Literal["max", "rms", "unit"] = "max",
) -> Self

Normalize spectrum amplitudes.

Parameters:

Name Type Description Default
method Literal['max', 'rms', 'unit']

Normalization method: - "max": Divide by maximum absolute value - "rms": Divide by RMS value - "unit": Divide by L2 norm

'max'

Returns:

Type Description
Self

Self for method chaining

__post_init__ #

__post_init__() -> None

__bytes__ #

__bytes__() -> bytes

load #

load(
    stream: SupportsRead[bytes], size: int | None = None
) -> Self

parse classmethod #

parse(data: bytes) -> Self

FromString classmethod #

FromString(s: bytes) -> Self

_pb_amplitudes #

_pb_amplitudes() -> NdComplexArray

_ensure_storage_initialized #

_ensure_storage_initialized() -> None

_mark_dirty #

_mark_dirty(field: str) -> None

_sync_numpy_to_proto #

_sync_numpy_to_proto() -> None

_sync_proto_to_numpy #

_sync_proto_to_numpy() -> None

_pb_axis #

_pb_axis() -> NdAxis

_ensure_axis_initialized #

_ensure_axis_initialized() -> None

to_nd #

to_nd(
    visualization_handler: str | None = None,
) -> SpectrumNd

copy #

copy() -> Self

frequency_shift #

frequency_shift(shift: float) -> Self

Shift the frequency axis (used by center_spectrum).

apply_window_notch #

apply_window_notch(
    center_hz: float, width_hz: float, zero: bool = True
) -> Self

Apply a notch filter around center_hz (used by remove_peak).

The centre and the width are both expressed against the carrier, the same relation the PPM axis itself is built on, so a notch lands on the same position whichever unit the axis is stored in.

__len__ #

__len__() -> int

__getitem__ #

__getitem__(key: slice | int) -> Self
__getitem__(key: FloatArray) -> Self
__getitem__(
    key: slice | int | FloatArray,
) -> Self | complex | ComplexArray
  • slice/int -> Spectrum1D view copy using NumPy-first arrays
  • array of frequencies -> complex amplitudes via linear interpolation

to_signal #

to_signal(
    time_unit: TimeUnit = MICROSECOND,
    carrier_offset_hz: float | None = None,
    half: bool = False,
    one_sided: bool | None = None,
    first_point_scale: bool | None = None,
) -> Signal1D

Convert spectrum back to time-domain signal via inverse FFT.

Parameters:

Name Type Description Default
time_unit TimeUnit

Time unit for the output signal (default: MICROSECOND)

MICROSECOND
carrier_offset_hz float | None

Carrier offset in Hz. If provided, this offset is subtracted from frequencies before inverse FFT and stored in the output signal.

None
half bool

If True, return only the first half of the signal. Useful when the spectrum produces a mirrored/symmetric signal after IFFT.

False
one_sided bool | None

Whether the frequency axis holds only non-negative frequencies (as produced by Signal1D.to_spectrum(fft_method="real"), which is the default). If None, detected from the fft_method metadata entry, falling back to inspecting the axis. One-sided spectra are inverted with irfft; two-sided (fftshift-ordered) spectra with ifftshift + ifft. Inverting a one-sided spectrum with plain ifft yields meaningless output.

None
first_point_scale bool | None

Whether to undo the forward transform's first-point halving (doubling fid[0]) so the roundtrip is lossless. If None, read from the first_point_scale metadata flag recorded by to_spectrum.

None

Returns:

Name Type Description
Signal1D Signal1D

Time-domain signal

__repr__ #

__repr__() -> str

__str__ #

__str__() -> str

from_proto classmethod #

from_proto(proto: Spectrum1D | SpectrumNd | None) -> Self

_require_reference_for_unit #

_require_reference_for_unit() -> float | None

The reference frequency this axis' unit needs, named when it is absent.

Only a PPM axis needs one; other units convert by a fixed factor.