<img src="https://raw.githubusercontent.com/Ameyanagi/xraydb-rs/main/assets/icon-512.png" alt="" width="88" align="left" hspace="12">
# xraydb-rs
X-ray reference data for the elements in Rust. A pure-Rust port of the [XrayDB](https://github.com/xraypy/XrayDB) project.
The complete database — elements, absorption edges, emission lines, Elam and Chantler cross-sections, Waasmaier–Kirfel form factors — is compiled into your binary as a 3 MB zstd blob and decoded lazily on first use. No runtime data files, no network access.
## Install
```toml
[dependencies]
xraydb = "0.2"
```
## Usage
```rust
use xraydb::{CrossSectionKind, XrayDb};
let db = XrayDb::try_new()?;
// Element facts — symbol, name, or Z all work as identifiers
assert_eq!(db.atomic_number("Fe")?, 26);
assert_eq!(db.symbol("iron")?, "Fe");
assert_eq!(db.atomic_name("26")?, "iron");
// Absorption edges
assert_eq!(db.xray_edge("Fe", "K")?.energy, 7112.0);
// Mass attenuation from the Elam tables (cm²/g)
let mu = db.mu_elam_at("Fe", 10_000.0, CrossSectionKind::Total)?;
// Compound attenuation (1/cm), by formula and density
let mu_water = db.material_mu_at("H2O", 1.0, 10_000.0, CrossSectionKind::Total)?;
assert!((mu_water - 5.33).abs() < 0.05);
// Anomalous scattering factors from the Chantler tables
let f1 = db.f1_chantler_at("Fe", 10_000.0)?;
let f2 = db.f2_chantler_at("Fe", 10_000.0)?;
// Refractive index decrements: n = 1 - delta - i*beta
let n = db.xray_delta_beta("SiO2", 2.2, 10_000.0)?;
println!("delta={:.3e} beta={:.3e} atlen={:.3} cm", n.delta, n.beta, n.attenuation_length_cm);
# Ok::<(), xraydb::XrayDbError>(())
```
### Scalar and batch forms
Every array-valued calculation has an `_at` scalar counterpart that allocates nothing. Use the batch form when you have many energies — it resolves the element and its table rows once for the whole slice.
```rust
# use xraydb::{CrossSectionKind, XrayDb};
# let db = XrayDb::try_new()?;
assert_eq!(mu.len(), energies.len());
# Ok::<(), xraydb::XrayDbError>(())
```
### Chemical formulas
One parser serves the whole crate, so anything `material_mu` accepts, `xray_delta_beta` accepts too.
| plain | `H2O` |
| nested groups | `Mn(SO4)2(H2O)7` |
| fractional stoichiometry | `Fe0.7Mg0.3O`, `Fe.7Mg.3O` |
| scientific notation | `Zn1.e-5Fe3O4` |
| fractional group multipliers | `(N2)0.7808(O2)0.2095` |
| weight-percent mixtures | `Ru1wt%SiO2` |
| deuterium alias | `D2O` (counted as hydrogen) |
### Named materials
```rust
# use xraydb::{CrossSectionKind, XrayDb};
# let db = XrayDb::try_new()?;
let kapton = db.find_material("kapton").expect("in the materials table");
assert_eq!(kapton.formula, "C22H10N2O5");
assert_eq!(kapton.density, 1.42);
// Density is taken from the table unless you override it
let mu = db.material_mu_named("kapton", &[10_000.0], CrossSectionKind::Total, None)?;
# Ok::<(), xraydb::XrayDbError>(())
```
### Optics (feature `optics`)
Crystal Darwin widths and mirror/multilayer reflectivity. Parameter structs with `Default` keep call sites readable.
```toml
[dependencies]
xraydb = { version = "0.2", features = ["optics"] }
```
```rust
# #[cfg(feature = "optics")] {
use xraydb::{DarwinParams, MirrorParams, XrayDb};
let db = XrayDb::try_new()?;
let dw = db.darwin_width(DarwinParams {
energy: 10_000.0,
crystal: "Si",
hkl: (1, 1, 1),
..Default::default()
})?.expect("Si(111) diffracts at 10 keV");
println!("Darwin width: {:.2} eV FWHM", dw.energy_fwhm);
formula: "Pt",
theta: &angles,
energy: 10_000.0,
density: 21.45,
roughness: 5.0,
..Default::default()
})?;
# }
# Ok::<(), xraydb::XrayDbError>(())
```
### Energy clamping
Following upstream XrayDB, energies outside a table's range are **clamped** to its endpoints rather than rejected. Check first with `XrayDb::elam_energy_range()` and `db.chantler_energy_range(element)`.
## Command-line tool
```sh
cargo install --path xraydb-cli # installs `xraydb`
xraydb element Fe
xraydb edges Fe
xraydb lines Cu --level K
xraydb mu H2O --density 1.0 --energy 5000:15000/11
xraydb delta-beta SiO2 --density 2.2 --energy 10000
xraydb guess-edge --energy 7100
xraydb materials
```
Energies accept a single value, a comma list, `start:stop:step`, or `start:stop/count` (log-spaced). Add `--json` or `--csv` for machine-readable output.
### For scripts and agents
The CLI is designed to be driven programmatically as well as typed:
```sh
xraydb commands --json # every subcommand, argument, enum value, and default
```
- `--json` works on **every** subcommand, including `commands` itself, so an agent can
discover the whole interface in one call instead of parsing `--help` prose.
- With `--json`, **failures are also JSON**, written to stderr as
`{"error": "...", "context": [...]}`. Without it, errors stay plain text.
- On failure stdout is left empty, so a consumer parsing stdout never sees partial output.
- Exit code is `0` on success and `1` on error. A broken pipe (`| head`) exits `0`.
- Colour is only emitted to a TTY, and `NO_COLOR` disables it.
## Browser demo
A zero-dependency demo page with a periodic-table selector and live cross-section plots:
```sh
wasm-pack build --target web --release xraydb-wasm --out-dir ../web/pkg
python3 -m http.server -d web 8080
```
Then open <http://localhost:8080>. See [web/README.md](web/README.md).
## Workspace Structure
| `xraydb-data` | Shared serde data model (`#![no_std]`) |
| `xraydb-generate` | Binary that parses raw data sources into compressed binary format |
| `xraydb-lib` | Main library crate (`xraydb`) with embedded compressed data |
| `xraydb-wasm` | WASM bindings via `wasm-bindgen` |
| `xraydb-cli` | `xraydb` command-line tool |
| `web/` | Browser demo built on `xraydb-wasm` |
## Performance
Typical timings on an M-series Mac, release build (see `cargo bench`):
| `f2_chantler_at` | ~120 ns |
| `mu_elam_at` | ~113 ns |
| `xray_edge` | ~78 ns |
| `xray_delta_beta` | ~1.1 µs |
| `mu_elam` batch of 200 | ~7 µs |
## Development
```sh
cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all --check
```
Install the pre-commit hook (runs fmt, clippy, and tests):
```sh
git config core.hooksPath .githooks
```
The library contains no `unsafe` (`#![forbid(unsafe_code)]`) and no `unwrap`/`expect`
outside test code (lint-enforced).
## Regenerating the data
```sh
git clone https://github.com/xraypy/XrayDB.git XrayDB
cargo run -p xraydb-generate --release
```
## Attribution
The X-ray data used in this project comes from the [XrayDB](https://github.com/xraypy/XrayDB) project by Matt Newville et al., which is placed in the public domain (CC0 1.0). This Rust port is an independent reimplementation.
Data sources include:
- **Elam/Ravel/Sieber tables** — photoabsorption, scattering, and emission line data
- **Chantler tables** — anomalous scattering factors (f', f'') and mass attenuation coefficients
- **Waasmaier-Kirfel coefficients** — elastic (Thomson) scattering factors f0
## License
Dual-licensed under MIT and Apache-2.0. See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE).
The underlying X-ray data is in the public domain (CC0 1.0) from the [XrayDB](https://github.com/xraypy/XrayDB) project.