nirs4all.data.synthetic.wavenumber module

Wavenumber conversion utilities for NIR spectroscopy.

This module provides utilities for converting between wavenumber (cm⁻¹) and wavelength (nm) units, which is essential for physically-correct band placement.

Harmonic relationships (overtones, combinations) are LINEAR in wavenumber space, not in wavelength space. This is a critical distinction for generating realistic synthetic spectra with proper overtone and combination band placement.

Mathematical relationships:
  • λ (nm) = 10^7 / ν̃ (cm⁻¹)

  • ν̃ (cm⁻¹) = 10^7 / λ (nm)

  • Δλ ≈ Δν̃ × λ² / 10^7 (bandwidth conversion)

References

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

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

class nirs4all.data.synthetic.wavenumber.CombinationBandResult(mode1_cm: float, mode2_cm: float, wavenumber_cm: float, wavelength_nm: float, amplitude_factor: float, band_type: str)[source]

Bases: object

Result of combination band calculation.

amplitude_factor: float
band_type: str
mode1_cm: float
mode2_cm: float
wavelength_nm: float
wavenumber_cm: float
class nirs4all.data.synthetic.wavenumber.OvertoneResult(order: int, wavenumber_cm: float, wavelength_nm: float, amplitude_factor: float, bandwidth_factor: float)[source]

Bases: object

Result of overtone calculation.

amplitude_factor: float
bandwidth_factor: float
order: int
wavelength_nm: float
wavenumber_cm: float
nirs4all.data.synthetic.wavenumber.apply_hydrogen_bonding_shift(wavenumber_cm: float, h_bond_strength: float = 0.5, is_donor: bool = True) float[source]

Apply hydrogen bonding shift to a wavenumber.

Hydrogen bonding weakens X-H bonds, shifting stretching frequencies to lower wavenumbers (red shift). The shift magnitude depends on the hydrogen bond strength.

Typical shifts for O-H: - Free O-H: ~3650 cm⁻¹ - Weak H-bond: ~3500 cm⁻¹ - Strong H-bond: ~3200 cm⁻¹

Parameters:
  • wavenumber_cm – Original wavenumber in cm⁻¹.

  • h_bond_strength – Hydrogen bond strength (0 = none, 1 = very strong).

  • is_donor – Whether the group is a hydrogen bond donor.

Returns:

Shifted wavenumber in cm⁻¹.

Example

>>> apply_hydrogen_bonding_shift(3650, h_bond_strength=0.5)
3467.5  # Red-shifted by hydrogen bonding
nirs4all.data.synthetic.wavenumber.calculate_combination_band(mode1: str | float | List[str | float], mode2: str | float | None = None, band_type: str = 'sum', coupling_factor: float = 1.0) CombinationBandResult[source]

Calculate combination band position.

Combination bands arise from simultaneous excitation of two vibrational modes. - Sum bands: ν̃_comb = ν̃₁ + ν̃₂ (most common in NIR) - Difference bands: ν̃_comb = |ν̃₁ - ν̃₂| (less common)

Parameters:
  • mode1 – First vibration - either a vibration type string (e.g., ‘O-H_stretch’), a numeric wavenumber in cm⁻¹, or a list of two modes.

  • mode2 – Second vibration (same format as mode1). If mode1 is a list, this should be None.

  • band_type – ‘sum’ or ‘difference’.

  • coupling_factor – Mechanical coupling between modes (0-1, affects amplitude).

Returns:

CombinationBandResult with position and intensity information.

Example

>>> # O-H stretch + O-H bend combination (water) using strings
>>> result = calculate_combination_band("O-H_stretch", "O-H_bend")
>>> print(f"{result.wavelength_nm:.0f} nm")
1984 nm
>>> # Using a list of modes
>>> result = calculate_combination_band(["O-H_stretch", "O-H_bend"])
>>> # Using numeric values
>>> result = calculate_combination_band(3400, 1640)
nirs4all.data.synthetic.wavenumber.calculate_overtone_position(vibration_type_or_frequency: str | float, overtone_order: int, anharmonicity: float | None = None) OvertoneResult[source]

Calculate overtone band position with anharmonicity correction.

For a harmonic oscillator, overtones would be exactly at n × ν̃₀. However, real molecular vibrations are anharmonic, causing overtones to appear at slightly lower wavenumbers than the harmonic prediction.

The anharmonic wavenumber is: ν̃ₙ = n × ν̃₀ × (1 - n × χ) where χ is the anharmonicity constant (typically 0.01-0.03).

Parameters:
  • vibration_type_or_frequency – Either a vibration type string (e.g., ‘O-H_stretch’) from FUNDAMENTAL_VIBRATIONS, or a numeric fundamental frequency in cm⁻¹.

  • overtone_order – Order (1 = fundamental, 2 = 1st overtone, 3 = 2nd overtone).

  • anharmonicity – Anharmonicity constant χ. If None and vibration_type is a string, uses the default for that vibration type. Otherwise defaults to 0.02.

Returns:

OvertoneResult with position and intensity information.

Example

>>> result = calculate_overtone_position("O-H_stretch", 2)  # O-H 1st overtone
>>> print(f"{result.wavelength_nm:.0f} nm")
1442 nm  # (with anharmonicity)
>>> result = calculate_overtone_position(3400, 2)  # Numeric frequency
>>> print(f"{result.wavelength_nm:.0f} nm")
1503 nm
nirs4all.data.synthetic.wavenumber.classify_wavelength_zone(wavelength_nm: float) str | None[source]

Classify a wavelength into its corresponding NIR zone.

Parameters:

wavelength_nm – Wavelength in nm.

Returns:

Zone name string, or None if outside defined zones.

Example

>>> classify_wavelength_zone(1450)
'1st_overtones_OH_NH'
>>> classify_wavelength_zone(2300)
'combination_CH'
nirs4all.data.synthetic.wavenumber.convert_bandwidth_to_wavelength(bandwidth_cm: float, center_nm: float) float[source]

Convert bandwidth from wavenumber to wavelength units.

Since the relationship between wavenumber and wavelength is non-linear, the bandwidth conversion depends on the center wavelength/wavenumber.

The approximation is: Δλ ≈ Δν̃ × λ² / 10^7

This is derived from the differential: dλ = -dν̃ × (10^7 / ν̃²) = -dν̃ × λ² / 10^7 (taking absolute value for bandwidth)

Parameters:
  • bandwidth_cm – Bandwidth in cm⁻¹ (e.g., FWHM).

  • center_nm – Center wavelength in nm.

Returns:

Bandwidth in nm.

Example

>>> convert_bandwidth_to_wavelength(100, 1450)  # 100 cm⁻¹ at 1450 nm
21.025  # approximately
>>> convert_bandwidth_to_wavelength(100, 2200)  # Same bandwidth at 2200 nm
48.4    # Broader in nm due to non-linear relationship
nirs4all.data.synthetic.wavenumber.convert_bandwidth_to_wavenumber(bandwidth_nm: float, center_nm: float) float[source]

Convert bandwidth from wavelength to wavenumber units.

The inverse of convert_bandwidth_to_wavelength: Δν̃ ≈ Δλ × 10^7 / λ²

Parameters:
  • bandwidth_nm – Bandwidth in nm (e.g., FWHM).

  • center_nm – Center wavelength in nm.

Returns:

Bandwidth in cm⁻¹.

Example

>>> convert_bandwidth_to_wavenumber(25, 1450)  # 25 nm at 1450 nm
118.9   # approximately
nirs4all.data.synthetic.wavenumber.estimate_bandwidth_broadening(baseline_bandwidth_cm: float, h_bond_strength: float = 0.0, temperature_k: float = 298.0) float[source]

Estimate bandwidth broadening due to environmental effects.

Hydrogen bonding and temperature increase band widths through: - Distribution of H-bond geometries (inhomogeneous broadening) - Thermal population of vibrational levels

Parameters:
  • baseline_bandwidth_cm – Intrinsic bandwidth in cm⁻¹.

  • h_bond_strength – Hydrogen bond strength (0-1).

  • temperature_k – Temperature in Kelvin.

Returns:

Broadened bandwidth in cm⁻¹.

Example

>>> estimate_bandwidth_broadening(50, h_bond_strength=0.7)
85.0  # Broadened by hydrogen bonding
nirs4all.data.synthetic.wavenumber.get_all_zones_wavelength() List[Tuple[float, float, str]][source]

Get all NIR zones converted to wavelength space.

Returns:

List of (min_wavelength, max_wavelength, zone_name) tuples in nm.

Example

>>> zones = get_all_zones_wavelength()
>>> for min_wl, max_wl, name in zones:
...     print(f"{name}: {min_wl:.0f}-{max_wl:.0f} nm")
nirs4all.data.synthetic.wavenumber.get_nir_overtones_for_fundamental(fundamental_cm: float, max_order: int = 4, wavelength_range: Tuple[float, float] = (800, 2500), anharmonicity: float = 0.02) List[OvertoneResult][source]

Get all overtones of a fundamental that fall within the NIR range.

Parameters:
  • fundamental_cm – Fundamental vibration wavenumber in cm⁻¹.

  • max_order – Maximum overtone order to consider.

  • wavelength_range – NIR wavelength range in nm (min, max).

  • anharmonicity – Anharmonicity constant.

Returns:

List of OvertoneResult objects for bands within the NIR range.

Example

>>> # Get NIR overtones of O-H stretch
>>> overtones = get_nir_overtones_for_fundamental(3400)
>>> for ot in overtones:
...     print(ot)
nirs4all.data.synthetic.wavenumber.get_zone_wavelength_range(zone_name: str) Tuple[float, float] | None[source]

Get the wavelength range (nm) for a named NIR zone.

Parameters:

zone_name – Name of the NIR zone (e.g., ‘1st_overtones_OH_NH’).

Returns:

Tuple of (min_wavelength, max_wavelength) in nm, or None if not found.

Example

>>> get_zone_wavelength_range('1st_overtones_CH')
(1600.0, 1818.18...)
nirs4all.data.synthetic.wavenumber.wavelength_to_wavenumber(lambda_nm: float | ndarray) float | ndarray[source]

Convert wavelength (nm) to wavenumber (cm⁻¹).

The conversion follows the relationship: ν̃ = 10^7 / λ

Parameters:

lambda_nm – Wavelength in nm. Can be a scalar or numpy array.

Returns:

Wavenumber in cm⁻¹ (same shape as input).

Raises:

ValueError – If wavelength is zero or negative.

Example

>>> wavelength_to_wavenumber(1450)  # O-H 1st overtone region
6896.55...
>>> wavelength_to_wavenumber(np.array([1450, 1940]))
array([6896.55..., 5154.64...])
nirs4all.data.synthetic.wavenumber.wavenumber_to_wavelength(nu_cm: float | ndarray) float | ndarray[source]

Convert wavenumber (cm⁻¹) to wavelength (nm).

The conversion follows the relationship: λ = 10^7 / ν̃

Parameters:

nu_cm – Wavenumber in cm⁻¹. Can be a scalar or numpy array.

Returns:

Wavelength in nm (same shape as input).

Raises:

ValueError – If wavenumber is zero or negative.

Example

>>> wavenumber_to_wavelength(6896)  # 1st overtone O-H
1450.26...
>>> wavenumber_to_wavelength(np.array([6896, 5155]))
array([1450.26..., 1939.88...])