Skip to main content

ndispers

English | 日本語

PyPI test apps

ndispers is a Python package for calculating refractive index dispersion of various crystals and glasses used in the field of nonlinear/ultrafast optics. It is based on Sellmeier equations and thermo-optic coefficients (dn/dT) reported in literature.

You can easily compute

  • Refractive index
  • Group delay
  • Group velocity
  • Group index
  • Group velocity dispersion
  • Third-order dispersion
  • Walk-off angles
  • dn/dT
  • d²n/dT²

as a function of

  1. Wavelength of light
  2. Polar (theta) or azimuthal (phi) angle of the wavevector with respect to the dielectric principal axes of an anisotropic crystal
  3. Temperature of the medium
  4. Polarization of light (ordinary or extraordinary ray)

The crystals also have nonlinear-optics methods:

  • Phase mismatch, dk
  • Phase-matching angles
  • Phase-matching factor, sinc²(dk·L/2)
  • Effective nonlinear coefficient, d_eff, with the tensor components scaled to the working wavelengths by Miller's rule (every non-centrosymmetric crystal in the package)

Why ndispers

Several tools already exist for this kind of calculation. The best-known and most extensive is SNLO by Arlee V. Smith (as-photonics.com, a Windows GUI). There are also web apps for refractive indices (refractiveindex.info) and phase matching (toolbox.lightcon.com), and the iOS app iPhasematch. Calculators exist for desktop, web and mobile — but none of them can become a component of your program.

ndispers provides the core of SNLO-style calculation — refractive index, dispersion, phase matching and d_eff — as a Python library. Every method is a plain function that takes and returns numpy arrays, so you can sweep wavelength, angle and temperature at once and build the results directly into your own numerical simulations (pulse propagation, OPO design, thermal analysis) or Jupyter notebooks. And every coefficient is published with its source: type bbo? and you see which table of which paper the numbers come from, so a single class name in your Methods section makes the calculation reproducible.

Being an ordinary, introspectable Python library has one more consequence: ndispers is agent-ready. Everything an AI assistant needs is self-describing — media enumerate with dir(), every docstring carries its literature source and validity range, and llms.txt condenses the whole API, units and pitfalls into a single page. Hand that link to your assistant and ask in plain language — "find the phase-matching angle for 1064 nm type-I SHG in BBO and plot its temperature dependence" — and it can install, compute and plot without you writing a line of code. A GUI or web calculator cannot be driven this way.

Nor is it limited to nonlinear crystals. Under the same interface it also covers linear birefringent crystals that SNLO does not treat (α-BBO, calcite, quartz, YVO₄, …) and optical glasses (fused silica, CaF₂, SF10, …), aiming to handle ultrafast-optics calculations — waveplates, polarizers and dispersion management included — in one package.

Strengths at a glance

  • Embeddable — a library, not a GUI. All methods accept numpy arrays and run as pre-compiled numpy functions, so the only runtime dependency is numpy (no sympy, no first-call code-generation pause). Medium objects are picklable and can be passed straight to multiprocessing / joblib.
  • Agent-ready — everything a GUI hides is exposed as introspectable Python, and llms.txt hands an AI assistant the whole API in one page: give it the link and ask in plain language. The calculator becomes conversational.
  • Transparent sourcing — each medium's docstring states the Sellmeier reference and its wavelength/temperature validity range, and the constants property returns the coefficient values themselves. You can always trace where a number came from.
  • Multiple Sellmeier equations per crystal — e.g. LBO ships Kato 1994, Kato–Kuroda 2018, Ghosh 1995 and manufacturer fits (Castech, Newlight) as separate classes, so you can compare disagreeing literature yourself.
  • Materials beyond SNLO — linear birefringent crystals (α-BBO, calcite, quartz, sapphire, MgF₂, YVO₄) and isotropic media (fused silica, CaF₂, BaF₂, YAG, N-BK7, SF glasses, Si, Ge, …) with the same API.
  • Analytic temperature derivatives — dn/dT and d²n/dT² are computed from differentiated expressions, not finite differences; useful for temperature tuning and thermal-lens analysis.
  • Nonlinear-optics toolkit — phase mismatch Δk, direct phase-matching-angle solutions, the sinc² phase-matching factor, and d_eff wavelength-scaled by Miller's rule for every non-centrosymmetric crystal.
  • Easy to extend — subclass the point-group base class and write one file of Sellmeier and d coefficients to add a crystal. Requests and contributions are welcome on GitHub.
  • Cross-platformpip install ndispers on Linux, macOS or Windows; runs on clusters and Colab alike.
  • Verified transcription — coefficients are cross-checked against the literature by independent re-extraction and pinned by regression tests, so copy errors are caught by machinery, not luck.

Installation

Requires Python 3.10 or later.

pip install ndispers

The only dependency is numpy: the dispersion formulas are written as sympy expressions, but the functions the package evaluates are generated from them ahead of time and shipped. To work with the expressions themselves (n_expr and friends), or to evaluate your own subclass of a medium, add sympy with pip install ndispers[sym].

Quick start

For a guided tour with plots — from inspecting a crystal's Sellmeier equation to phase-matching curves — see the tutorial notebook, readable directly on GitHub. The essentials:

Make an object of a β-BBO crystal:

>>> import ndispers as nd
>>> bbo = nd.media.crystals.BetaBBO_Eimerl1987()

To compute a refractive index:

>>> bbo.n(0.532, 0, 25, pol='o')
1.674884049110459
>>> bbo.n(0.532, 3.1416/2, 25, pol='e')
1.5554658787539917

where the four arguments are, respectively,

  1. wavelength (in micrometer),
  2. theta angle (in radian),
  3. temperature (in degree Celsius),
  4. polarization (pol='o' or 'e', ordinary or extraordinary ray; default 'o').

At theta = 0 (propagation along the optic axis) the o-ray and e-ray indices coincide. Scalar input returns a float. Each argument also accepts a numpy array, returning an array of the same shape:

>>> import numpy as np
>>> bbo.n(np.arange(0.2, 1.5, 0.2), 0, 25, pol='o')
array([1.89001202, 1.69328828, 1.66985875, 1.6612891 , 1.65633946,
       1.65252664, 1.64903624])

The other dispersion methods (GD, GV, ng, GVD, TOD, woa_theta, woa_phi, dndT, dndT2) take the same arguments:

>>> bbo.GVD(0.8, 0, 25, pol='o')   # fs^2/mm
71.86403019943364

Optically isotropic media — the glasses and the cubic crystal CaF₂ — take only wavelength and temperature, since there is no angle or polarization to specify:

>>> silica = nd.media.glasses.FusedSilica()
>>> silica.n(1.064, 20)
1.4495857898590634

To look into the material information, its Sellmeier equation and the literature the coefficients come from, use help(bbo) — or simply bbo? in IPython/Jupyter. bbo.constants returns the coefficient values themselves.

Phase matching

Phase-matching angles for sum-frequency generation are solved directly. For Type-I SHG of 1064 nm in β-BBO at 25 °C:

>>> bbo.pmAngles_sfg(1.064, 1.064, 25, deg=True)
{'wl3': 0.532,
 'ooe': {'theta': [22.884169498625802], 'phi': None},
 'eeo': {'theta': [], 'phi': None},
 'oee': {'theta': [32.56045545648089], 'phi': None},
 'eoe': {'theta': [32.56045545648089], 'phi': None},
 'eoo': {'theta': [], 'phi': None},
 'oeo': {'theta': [], 'phi': None}}

For difference-frequency generation and optical parametric amplification/oscillation the same interaction is entered from the pump side: pmAngles_dfg(wl_p, wl_s, T) gives the angles (and the idler), tuning_dfg(wl_p, angle, T, pol_s, pol_i, pol_p) the signal/idler pairs that phase-match at a fixed angle — one point of an OPO tuning curve — and dk_dfg, pmFactor_dfg, qpm_period_dfg, deff_dfg mirror their SFG counterparts with waves read as (signal, idler, pump). For x-cut KTP pumped at 1064 nm, KTP_zx().tuning_dfg(1.064, np.pi/2, 25, 'o', 'e', 'o') returns the familiar noncritical pair 1571 / 3298 nm; with qpm_period= the same method gives the temperature tuning of a periodically poled crystal.

dk_sfg and pmFactor_sfg give the phase mismatch and the sinc² phase-matching factor for arbitrary angles, and qpm_period_sfg the quasi-phase-matching period — for 1064 nm SHG in 5% MgO:LiNbO₃ along x with all waves extraordinary (d33), MgOLN_Zelmon1997().qpm_period_sfg(1.064, 1.064, np.pi/2, 20, 'e', 'e', 'e') gives the familiar ~7.0 µm.

The effective nonlinear coefficient at that angle, for the φ = 90° cut:

>>> bbo.deff_sfg(1.064, 1.064, np.radians(22.88), np.radians(90), 25, 'o', 'o', 'e')
-1.9937...  # pm/V (the overall sign is a convention); d22 = 2.2 pm/V at 1.064 µm SHG
            # (Shoji et al. 1999), walk-off included
>>> bbo.d_sfg("d22", 0.8, 0.8, 25)
2.3300...   # d22 for 800 nm SHG by Miller's rule

Every non-centrosymmetric crystal has this (KDP, CLBO, LBO, KTP, ...); help(bbo.deff_sfg) and the Conventions page describe the sign and angle conventions, and each crystal's _d_note says where its coefficients come from and how far Miller scaling has been tested for it.

Available media

Every medium is a class in nd.media.crystals or nd.media.glasses; the media catalog lists the class names with each one's Sellmeier equation, validity range and references. Several crystals come in more than one parameterisation, named after the literature or vendor the coefficients come from — pick the source you want to rely on.

Nonlinear optical crystals

Non-centrosymmetric: phase matching, acceptance bandwidths and d_eff are available.

Material Abbreviation Formula Point group Optical class
β-Barium borate β-BBO β-BaB₂O₄ 3m negative uniaxial
Lithium triborate LBO LiB₃O₅ mm2 biaxial, three principal planes
Potassium titanyl phosphate KTP KTiOPO₄ mm2 biaxial, three principal planes
Bismuth triborate BiBO BiB₃O₆ 2 biaxial (monoclinic), three principal planes
Cesium lithium borate CLBO CsLiB₆O₁₀ 4̄2m negative uniaxial
Potassium dihydrogen phosphate KDP KH₂PO₄ 4̄2m negative uniaxial
Deuterated potassium dihydrogen phosphate DKDP, KD*P KD₂PO₄ 4̄2m negative uniaxial
Potassium beryllium fluoroborate KBBF KBe₂BO₃F₂ 32 negative uniaxial
Rubidium beryllium fluoroborate RBBF RbBe₂BO₃F₂ 32 negative uniaxial
Lithium tetraborate LB4 (also LBT) Li₂B₄O₇ 4mm negative uniaxial
Lithium iodate LiIO₃ 6 negative uniaxial
Zinc germanium phosphide ZGP ZnGeP₂ 4̄2m positive uniaxial, mid-infrared
Silver thiogallate AGS AgGaS₂ 4̄2m negative uniaxial, mid-infrared
Silver gallium selenide AGSe AgGaSe₂ 4̄2m negative uniaxial, mid-infrared
α-Quartz SiO₂ 32 positive uniaxial
Lithium niobate, 5% MgO-doped congruent MgO:LN MgO:LiNbO₃ 3m negative uniaxial, both rays
Lithium niobate, 1% MgO-doped stoichiometric MgO:SLN MgO:LiNbO₃ 3m negative uniaxial, e-ray only
Lithium tantalate, 1% MgO-doped stoichiometric MgO:SLT MgO:LiTaO₃ 3m negative uniaxial

Birefringent optical crystals

Centrosymmetric, so no second-order nonlinearity; dispersion, walk-off and thermo-optics for windows, polarizers and compensators.

Material Abbreviation Formula Point group Optical class
α-Barium borate α-BBO α-BaB₂O₄ 3̄m negative uniaxial
Calcite CaCO₃ 3̄m negative uniaxial
Sapphire α-Al₂O₃ 3̄m negative uniaxial
Magnesium fluoride MgF₂ 4/mmm positive uniaxial
Yttrium orthovanadate YVO₄ YVO₄ 4/m positive uniaxial

Optically isotropic media

One refractive index, no angle or polarization argument: methods take (wl_um, T_degC).

Material Formula
Fused silica SiO₂ (amorphous)
Calcium fluoride CaF₂ (cubic, m3̄m)
Lithium fluoride LiF (cubic, m3̄m)
Barium fluoride BaF₂ (cubic, m3̄m)
Yttrium aluminium garnet (YAG) Y₃Al₅O₁₂ (cubic, m3̄m)
N-BK7, SF10, SF11, SF57 (SCHOTT) borosilicate crown and dense flint glasses
Zinc selenide, zinc sulfide ZnSe, ZnS (cubic, 4̄3m; CVD grades)
Silicon, germanium Si, Ge (cubic, m3̄m)
Diamond C (cubic, m3̄m)

Where a material has several parameterisations they are not interchangeable — each is faithful to its own source, and some sources are better than others. The validation page says which to reach for and why.

For biaxial crystals one class per principal dielectric plane is provided; the angle argument is the one that varies in that plane (φ in xy, θ in yz and zx), and an instance's theta_rad / phi_rad attributes say which. Media whose Sellmeier equation carries no temperature term (α-BBO, calcite, sapphire, quartz, MgF₂, 5% MgO:LiNbO₃, BiBO, the mid-infrared crystals, YAG, N-BK7, LiF, BaF₂, and the rest of the isotropic set) still take the temperature argument for a uniform signature and ignore it; their dndT returns 0.

Parallel processing

Medium objects are picklable, including after use, so they can be passed directly to multiprocessing, concurrent.futures or joblib workers, and stored with pickle/shelve. Lambdified dispersion functions are cached per class, so constructing many instances of the same crystal is cheap.

Documentation

  • Browser apps — a refractive-index explorer and a phase-matching calculator, running client-side with nothing to install.
  • Tutorial notebook — the basic workflow, from inspecting a crystal's Sellmeier equation to phase-matching curves, viewable directly on GitHub.
  • Validation — what has been checked against the literature, with the numbers and their sources, and the caveats the audit turned up.
  • ndispers.readthedocs.io — conventions (units, angles, sign conventions) and the media catalog of every crystal and glass with its formula, validity range and references.

Development

git clone https://github.com/akihiko-shimura/ndispers.git
cd ndispers
uv sync
uv run pytest

To run the tutorial notebook, add the notebook group (matplotlib, IPython, JupyterLab, marimo):

uv sync --group notebook
uv run jupyter lab examples/basic_usage.ipynb

Releases are published to PyPI by pushing a version tag; see docs/RELEASING.md.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ndispers-0.19.0.tar.gz (274.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ndispers-0.19.0-py3-none-any.whl (421.1 kB view details)

Uploaded Python 3

File details

Details for the file ndispers-0.19.0.tar.gz.

File metadata

  • Download URL: ndispers-0.19.0.tar.gz
  • Upload date:
  • Size: 274.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ndispers-0.19.0.tar.gz
Algorithm Hash digest
SHA256 16c9c6329219fc66a391363ff2a9f7ec7182be14c70772ae08daa172bc4031bd
MD5 f024fa418e74b3cc7d73be96680fc812
BLAKE2b-256 6ac5138ebf44447c8608fbc9274291a7efacdd7802c4604f1efe629c3c825878

See more details on using hashes here.

Provenance

The following attestation bundles were made for ndispers-0.19.0.tar.gz:

Publisher: publish.yml on akihiko-shimura/ndispers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ndispers-0.19.0-py3-none-any.whl.

File metadata

  • Download URL: ndispers-0.19.0-py3-none-any.whl
  • Upload date:
  • Size: 421.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ndispers-0.19.0-py3-none-any.whl
Algorithm Hash digest
SHA256 df5814f031d38338f5b56427243f833a12900a66efeb22fa638fa5d55b3767e4
MD5 f570794a84dd9f3fad51aa99109f35a3
BLAKE2b-256 edd7b445c2121148b76582d982597b1b6bf6c06be1c976a416cb908793d78027

See more details on using hashes here.

Provenance

The following attestation bundles were made for ndispers-0.19.0-py3-none-any.whl:

Publisher: publish.yml on akihiko-shimura/ndispers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.19.0 This release

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.1

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.1

2 files

0.9.0

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.1.13

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page