nirs4all.synthesis.instruments module

Instrument archetype simulation for synthetic NIRS data generation.

This module provides realistic simulation of different NIR instrument types, including their optical characteristics, noise models, and measurement configurations. It also supports multi-sensor systems that stitch together signal chunks from different wavelength ranges, and multi-scan averaging.

Key Features:
  • 20+ instrument archetypes covering benchtop, handheld, process, and embedded

  • Multi-sensor stitching simulation (combining multiple detector ranges)

  • Multi-scan averaging with realistic noise reduction

  • Detector-specific noise models (shot, thermal, 1/f)

  • Wavelength calibration effects

  • Stray light and etalon interference

References

  • Workman Jr, J., & Weyer, L. (2012). Practical Guide and Spectral Atlas for Interpretive Near-Infrared Spectroscopy. CRC Press.

  • Siesler, H. W., Ozaki, Y., Kawata, S., & Heise, H. M. (2002). Near-Infrared Spectroscopy: Principles, Instruments, Applications. Wiley-VCH.

  • ASTM E1944-98(2017): Standard Practice for Describing and Measuring Performance of NIR Instruments.

class nirs4all.synthesis.instruments.DetectorType(value)[source]

Bases: str, Enum

Types of NIR detectors.

INGAAS = 'ingaas'
INGAAS_EXTENDED = 'ingaas_ext'
MCT = 'mct'
MEMS = 'mems'
PBS = 'pbs'
PBSE = 'pbse'
SI = 'si'
class nirs4all.synthesis.instruments.EdgeArtifactsConfig(enable_detector_rolloff: bool = False, enable_stray_light: bool = False, enable_truncated_peaks: bool = False, enable_edge_curvature: bool = False, detector_model: str = 'generic_nir', rolloff_severity: float = 0.3, stray_fraction: float = 0.001, stray_wavelength_dependent: bool = True, left_peak_amplitude: float = 0.0, right_peak_amplitude: float = 0.0, curvature_type: str = 'concave', left_curvature_severity: float = 0.0, right_curvature_severity: float = 0.0)[source]

Bases: object

Configuration for edge artifact effects in synthetic NIRS spectra.

Edge artifacts are common in NIR spectra and arise from various sources: - Detector sensitivity roll-off at spectral extremes - Stray light contamination - Truncated absorption peaks at measurement boundaries - Baseline curvature/bending at spectrum edges

These artifacts are well-documented in the literature: - Workman Jr, J., & Weyer, L. (2012). Practical Guide and Spectral Atlas

for Interpretive Near-Infrared Spectroscopy. CRC Press. Chapters 4-5.

  • Burns, D. A., & Ciurczak, E. W. (2007). Handbook of Near-Infrared Analysis. CRC Press. Chapters on instrumentation.

  • ASTM E1944-98(2017): Standard Practice for Describing and Measuring Performance of NIR Instruments.

enable_detector_rolloff

Enable detector sensitivity roll-off.

Type:

bool

enable_stray_light

Enable stray light effects.

Type:

bool

enable_truncated_peaks

Enable truncated absorption peaks.

Type:

bool

enable_edge_curvature

Enable baseline curvature at edges.

Type:

bool

detector_model

Detector model for roll-off (‘generic_nir’, ‘ingaas’, ‘pbs’, ‘silicon_ccd’). Defaults to ‘generic_nir’.

Type:

str

rolloff_severity

Severity of detector roll-off (0.0-1.0).

Type:

float

stray_fraction

Stray light fraction (0.0-0.02 typical).

Type:

float

stray_wavelength_dependent

Whether stray light varies with wavelength.

Type:

bool

left_peak_amplitude

Amplitude of truncated peak at low wavelength edge.

Type:

float

right_peak_amplitude

Amplitude of truncated peak at high wavelength edge.

Type:

float

curvature_type

Type of edge curvature (‘concave’, ‘convex’, ‘asymmetric’).

Type:

str

left_curvature_severity

Severity of left edge curvature (0.0-1.0).

Type:

float

right_curvature_severity

Severity of right edge curvature (0.0-1.0).

Type:

float

curvature_type: str = 'concave'
detector_model: str = 'generic_nir'
enable_detector_rolloff: bool = False
enable_edge_curvature: bool = False
enable_stray_light: bool = False
enable_truncated_peaks: bool = False
left_curvature_severity: float = 0.0
left_peak_amplitude: float = 0.0
right_curvature_severity: float = 0.0
right_peak_amplitude: float = 0.0
rolloff_severity: float = 0.3
stray_fraction: float = 0.001
stray_wavelength_dependent: bool = True
class nirs4all.synthesis.instruments.InstrumentArchetype(name: str, category: ~nirs4all.synthesis.instruments.InstrumentCategory, detector_type: ~nirs4all.synthesis.instruments.DetectorType, monochromator_type: ~nirs4all.synthesis.instruments.MonochromatorType, wavelength_range: ~typing.Tuple[float, float], spectral_resolution: float = 8.0, wavelength_accuracy: float = 0.5, photometric_noise: float = 0.0001, photometric_range: ~typing.Tuple[float, float] = (0.0, 3.0), snr: float = 10000.0, stray_light: float = 0.0001, warm_up_drift: float = 0.1, temperature_sensitivity: float = 0.01, scan_speed: float = 1.0, integration_time_ms: float = 100.0, optical_path: str = 'transmission', multi_sensor: ~nirs4all.synthesis.instruments.MultiSensorConfig = <factory>, multi_scan: ~nirs4all.synthesis.instruments.MultiScanConfig = <factory>, description: str = '')[source]

Bases: object

Parameterized NIR instrument simulation.

Represents a complete instrument model with optical, electronic, and measurement characteristics. Can be used to generate realistic synthetic spectra that match specific instrument types.

name

Instrument archetype name.

Type:

str

category

Instrument category (benchtop, handheld, etc.).

Type:

nirs4all.synthesis.instruments.InstrumentCategory

detector_type

Primary detector type.

Type:

nirs4all.synthesis.instruments.DetectorType

monochromator_type

Wavelength selection mechanism.

Type:

nirs4all.synthesis.instruments.MonochromatorType

wavelength_range

Nominal wavelength range (nm).

Type:

Tuple[float, float]

spectral_resolution

Spectral resolution (FWHM in nm).

Type:

float

wavelength_accuracy

Wavelength accuracy (nm).

Type:

float

photometric_noise

Photometric noise level (AU).

Type:

float

photometric_range

Photometric range (min, max AU).

Type:

Tuple[float, float]

snr

Signal-to-noise ratio at 1 AU.

Type:

float

stray_light

Stray light level (fraction).

Type:

float

warm_up_drift

Intensity drift during warm-up (%/hour).

Type:

float

temperature_sensitivity

Wavelength shift per °C.

Type:

float

scan_speed

Scans per second.

Type:

float

integration_time_ms

Integration time in milliseconds.

Type:

float

optical_path

Optical path type (‘transmission’, ‘reflection’, etc.).

Type:

str

multi_sensor

Multi-sensor configuration.

Type:

nirs4all.synthesis.instruments.MultiSensorConfig

multi_scan

Multi-scan averaging configuration.

Type:

nirs4all.synthesis.instruments.MultiScanConfig

description

Human-readable description.

Type:

str

category: InstrumentCategory
description: str = ''
detector_type: DetectorType
get_noise_model_params() Dict[str, float][source]

Get noise model parameters based on detector type.

integration_time_ms: float = 100.0
monochromator_type: MonochromatorType
multi_scan: MultiScanConfig
multi_sensor: MultiSensorConfig
name: str
optical_path: str = 'transmission'
photometric_noise: float = 0.0001
photometric_range: Tuple[float, float] = (0.0, 3.0)
scan_speed: float = 1.0
snr: float = 10000.0
spectral_resolution: float = 8.0
stray_light: float = 0.0001
temperature_sensitivity: float = 0.01
warm_up_drift: float = 0.1
wavelength_accuracy: float = 0.5
wavelength_range: Tuple[float, float]
class nirs4all.synthesis.instruments.InstrumentCategory(value)[source]

Bases: str, Enum

Categories of NIR instruments.

BENCHTOP = 'benchtop'
DIODE_ARRAY = 'diode_array'
EMBEDDED = 'embedded'
FILTER = 'filter'
FT_NIR = 'ft_nir'
HANDHELD = 'handheld'
PROCESS = 'process'
class nirs4all.synthesis.instruments.InstrumentSimulator(archetype: InstrumentArchetype, random_state: int | None = None)[source]

Bases: object

Apply instrument-specific effects to synthetic spectra.

Simulates the complete instrument response including: - Spectral resolution (instrumental broadening) - Multi-sensor stitching - Multi-scan averaging - Detector noise (shot, thermal, 1/f) - Wavelength calibration errors - Stray light effects - Etalon/fringing interference

archetype

The instrument archetype being simulated.

rng

Random number generator for reproducibility.

Example

>>> archetype = get_instrument_archetype("viavi_micronir")
>>> simulator = InstrumentSimulator(archetype, random_state=42)
>>> spectra_out = simulator.apply(spectra, wavelengths)
apply(spectra: ndarray, wavelengths: ndarray, temperature_offset: float = 0.0) Tuple[ndarray, ndarray][source]

Apply all instrument effects to spectra.

Parameters:
  • spectra – Input spectra array (n_samples, n_wavelengths).

  • wavelengths – Wavelength array in nm.

  • temperature_offset – Temperature deviation from calibration (°C).

Returns:

Tuple of (modified_spectra, output_wavelengths). Output wavelengths may differ if resampled to instrument grid.

class nirs4all.synthesis.instruments.MonochromatorType(value)[source]

Bases: str, Enum

Types of wavelength selection mechanisms.

AOTF = 'aotf'
DMD = 'dmd'
FABRY_PEROT = 'fabry_perot'
FILTER_WHEEL = 'filter_wheel'
FT = 'fourier_transform'
GRATING = 'grating'
LVF = 'lvf'
class nirs4all.synthesis.instruments.MultiScanConfig(enabled: bool = False, n_scans: int = 16, averaging_method: str = 'mean', scan_to_scan_noise: float = 0.001, wavelength_jitter: float = 0.05, discard_outliers: bool = False, outlier_threshold: float = 3.0)[source]

Bases: object

Configuration for multi-scan averaging/accumulation.

Real instruments often acquire multiple scans per sample and average them to improve signal-to-noise ratio. This config simulates that process.

enabled

Whether multi-scan mode is enabled.

Type:

bool

n_scans

Number of scans to simulate and average.

Type:

int

averaging_method

How to combine scans. Options: ‘mean’, ‘median’, ‘weighted’, ‘savgol’

Type:

str

scan_to_scan_noise

Additional noise between scans (simulates drift).

Type:

float

wavelength_jitter

Random wavelength shift between scans (nm).

Type:

float

discard_outliers

Whether to discard outlier scans.

Type:

bool

outlier_threshold

Z-score threshold for outlier detection.

Type:

float

averaging_method: str = 'mean'
discard_outliers: bool = False
enabled: bool = False
n_scans: int = 16
outlier_threshold: float = 3.0
scan_to_scan_noise: float = 0.001
wavelength_jitter: float = 0.05
class nirs4all.synthesis.instruments.MultiSensorConfig(enabled: bool = False, sensors: List[SensorConfig] = <factory>, stitch_method: str = 'weighted', stitch_smoothing: float = 10.0, add_stitch_artifacts: bool = True, artifact_intensity: float = 0.02)[source]

Bases: object

Configuration for multi-sensor spectral stitching.

Modern NIR instruments often use multiple sensors/detectors to cover wide wavelength ranges. This config controls how the signals are combined.

enabled

Whether multi-sensor mode is enabled.

Type:

bool

sensors

List of SensorConfig for each sensor.

Type:

List[nirs4all.synthesis.instruments.SensorConfig]

stitch_method

Method for combining overlapping regions. Options: ‘weighted’, ‘average’, ‘first’, ‘last’, ‘optimal’

Type:

str

stitch_smoothing

Smoothing window (nm) at stitch boundaries.

Type:

float

add_stitch_artifacts

Whether to simulate stitching artifacts.

Type:

bool

artifact_intensity

Intensity of stitching artifacts (0-1).

Type:

float

add_stitch_artifacts: bool = True
artifact_intensity: float = 0.02
enabled: bool = False
sensors: List[SensorConfig]
stitch_method: str = 'weighted'
stitch_smoothing: float = 10.0
class nirs4all.synthesis.instruments.SensorConfig(detector_type: DetectorType, wavelength_range: Tuple[float, float], spectral_resolution: float = 8.0, noise_level: float = 1.0, gain: float = 1.0, overlap_range: float = 20.0)[source]

Bases: object

Configuration for a single sensor/detector in a multi-sensor system.

Multi-sensor instruments use multiple detectors with different wavelength ranges, then stitch the signals together. This is common in extended-range instruments (e.g., 400-2500 nm coverage using Si + InGaAs detectors).

detector_type

Type of detector for this sensor.

Type:

nirs4all.synthesis.instruments.DetectorType

wavelength_range

(start, end) wavelength range in nm.

Type:

Tuple[float, float]

spectral_resolution

Resolution in nm (FWHM).

Type:

float

noise_level

Relative noise level (1.0 = standard).

Type:

float

gain

Detector gain multiplier.

Type:

float

overlap_range

Wavelength overlap with adjacent sensor for stitching (nm).

Type:

float

detector_type: DetectorType
gain: float = 1.0
noise_level: float = 1.0
overlap_range: float = 20.0
spectral_resolution: float = 8.0
wavelength_range: Tuple[float, float]
nirs4all.synthesis.instruments.get_instrument_archetype(name: str) InstrumentArchetype[source]

Get a predefined instrument archetype by name.

Parameters:

name – Instrument archetype name.

Returns:

InstrumentArchetype instance.

Raises:

KeyError – If archetype name not found.

Example

>>> archetype = get_instrument_archetype("foss_xds")
>>> print(archetype.wavelength_range)
(400, 2500)
nirs4all.synthesis.instruments.get_instrument_wavelength_info() Dict[str, Dict[str, Any]][source]

Get detailed information about all instrument wavelength grids.

Returns:

  • n_wavelengths: Number of wavelength points

  • wavelength_start: Start wavelength (nm)

  • wavelength_end: End wavelength (nm)

  • mean_step: Mean wavelength step (nm)

Return type:

Dictionary mapping instrument names to info dicts containing

Example

>>> info = get_instrument_wavelength_info()
>>> print(info["micronir_onsite"])
{'n_wavelengths': 125, 'wavelength_start': 908.0, ...}
nirs4all.synthesis.instruments.get_instrument_wavelengths(instrument: str) ndarray[source]

Get the wavelength grid for a known instrument.

Returns a copy of the predefined wavelength array for the specified instrument, enabling generation of synthetic data that matches real instrument wavelength grids exactly.

Parameters:

instrument – Instrument identifier (e.g., “micronir_onsite”, “foss_xds”).

Returns:

NumPy array of wavelengths in nm.

Raises:

ValueError – If the instrument is not recognized.

Example

>>> wl = get_instrument_wavelengths("micronir_onsite")
>>> print(f"MicroNIR: {len(wl)} wavelengths from {wl[0]:.0f} to {wl[-1]:.0f} nm")
MicroNIR: 125 wavelengths from 908 to 1676 nm
>>> # Use with SyntheticNIRSGenerator
>>> from nirs4all.synthesis import SyntheticNIRSGenerator
>>> gen = SyntheticNIRSGenerator(wavelengths=wl)
nirs4all.synthesis.instruments.get_instruments_by_category() Dict[str, List[str]][source]

Get all instruments organized by category.

Returns:

Dictionary mapping category name to list of instrument names.

nirs4all.synthesis.instruments.list_instrument_archetypes(category: InstrumentCategory | None = None) List[str][source]

List available instrument archetype names.

Parameters:

category – Optional filter by category.

Returns:

List of archetype names.

Example

>>> list_instrument_archetypes(InstrumentCategory.HANDHELD)
['viavi_micronir', 'scio', 'tellspec', 'linksquare', 'siware_neoscanner']
nirs4all.synthesis.instruments.list_instrument_wavelength_grids() List[str][source]

List all available predefined instrument wavelength grids.

Returns:

List of instrument identifiers.

Example

>>> grids = list_instrument_wavelength_grids()
>>> print(grids[:3])
['micronir_onsite', 'scio', 'neospectra_micro']