Metadata-Version: 2.4
Name: tdmrsdm
Version: 2026.10.1
Summary: Temperature-Dependent Multi-Relaxation Spectroscopic Dielectric Model (TD-MRSDM) of soil permittivity, with the published, PySDS (v3) and organic-plus-mineral (allsoils_v1) constant sets.
Author-email: Morteza Khazaei <morteza.khazaei@usherbrooke.ca>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Morteza-Khazaei/TD-MRSDM
Project-URL: Issues, https://github.com/Morteza-Khazaei/TD-MRSDM/issues
Keywords: soil,dielectric,permittivity,microwave,remote sensing,TD-MRSDM
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Provides-Extra: analysis
Requires-Dist: pandas>=2.0; extra == "analysis"
Requires-Dist: scipy>=1.10; extra == "analysis"
Requires-Dist: scikit-learn>=1.2; extra == "analysis"
Requires-Dist: xlrd>=2.0; extra == "analysis"
Requires-Dist: python-calamine>=0.2; python_version >= "3.9" and extra == "analysis"
Provides-Extra: figures
Requires-Dist: tdmrsdm[analysis]; extra == "figures"
Requires-Dist: matplotlib>=3.7; extra == "figures"
Requires-Dist: seaborn>=0.12; extra == "figures"
Provides-Extra: torch
Requires-Dist: torch>=2.0; extra == "torch"
Provides-Extra: all
Requires-Dist: tdmrsdm[analysis,figures,torch]; extra == "all"
Provides-Extra: dev
Requires-Dist: tdmrsdm[all]; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mypy>=1.5; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Dynamic: license-file

# TD-MRSDM

[![PyPI](https://img.shields.io/pypi/v/tdmrsdm.svg)](https://pypi.org/project/tdmrsdm/)
[![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Python implementation of the temperature-dependent multi-relaxation spectroscopic dielectric model (TD-MRSDM) of Mironov & Savin (2019). It returns the complex permittivity of soil from frequency, temperature, organic matter, bulk density and moisture.

![TD-MRSDM architecture](figures/td_mrsdm_architecture.png)

For each water form *p* (bound *b*, transient *t*, unbound *u* or ice *i*), the ohmic conductivity, static permittivities (Clausius–Mossotti) and relaxation times (Eyring) are computed from temperature with the thawed (*t* ≥ 0 °C) or frozen (*t* ≤ −1 °C) constants. Debye relaxation gives each form's RI and NAC, which the refractive mixing model combines with SOM, bulk density and moisture into the soil permittivity. Equation and table numbers refer to the PySDS paper (Khazaei et al., submitted), whose Table A1 lists the `v3` constants.

## Install

```bash
pip install tdmrsdm                 # forward model, NumPy only
pip install "tdmrsdm[analysis]"     # + DDOAS loader, TrainableTDMRSDM, dependence, optimization
pip install "tdmrsdm[figures]"      # + validation figures
pip install "tdmrsdm[torch]"        # + TorchTDMRSDM (differentiable)
pip install "tdmrsdm[all]"          # everything
```

Python ≥ 3.8.

## Usage

```python
import numpy as np
from tdmrsdm import TDMRSDM

eps = TDMRSDM(freq_GHz=5.405, sst=20.0, som=np.array([3.0, 50.0]),
              sbd=np.array([1.3, 0.8]), ssm=np.array([0.35, 0.35]), optimize="v3").run()
# array([9.865+3.494j, 9.938+3.625j])  ->  eps.real = ε', eps.imag = ε''
```

| Argument | Unit | Description |
|----------|------|-------------|
| `freq_GHz` | GHz | frequency, scalar (calibrated 0.05–15 GHz) |
| `sst` | °C | soil temperature, scalar or array (−40 to +25 °C in the DDOAS) |
| `som` | % | soil organic matter |
| `sbd` | g/cm³ | dry bulk density |
| `ssm` | cm³/cm³ | volumetric moisture |
| `optimize` | – | constant set, see below (default `None`) |
| `constants_override` | – | dict with the structure of the JSON files in `src/tdmrsdm/constants/` |

`som`, `sbd` and `ssm` broadcast as NumPy arrays (per-pixel maps work). The result is `ε' + jε''` with `ε'' > 0` for a lossy medium.

Differentiable version (gradients with respect to `ssm`, scene scalars or per-pixel maps):

```python
import torch
from tdmrsdm import TorchTDMRSDM

model = TorchTDMRSDM(freq_GHz=1.26, sst=25.0, som=2.0, sbd=1.3, optimize="v3", precision="float64")
ssm = torch.tensor([0.05, 0.20, 0.40], dtype=torch.float64, requires_grad=True)
eps = model(ssm)                                   # complex tensor, same convention
```

## Constant sets

| `optimize` | File | Fitted on |
|------------|------|-----------|
| `None` | `td_mrsdm_constants_v2019.json` | published constants (Mironov & Savin, 2019) |
| `"v1"`, `"v2"` | `..._optimized_v1/v2.json` | earlier re-tunings, kept for reproducibility |
| `"v3"` | `..._optimized_v3.json` | denoised DDOAS, differential evolution on NRMSE; used by [PySDS](https://github.com/Morteza-Khazaei/PySDS) |
| `"allsoils_v1"` | `td_mrsdm_constants_allsoils_v1.json` | 7 organic (DDOAS) + 7 mineral laboratory soils (Karavayskiy, unpublished) |

Leave-one-soil-out NRMSE (range-normalized, %, mean over soils; from the file metadata):

| Constants | Organic RI / NAC | Mineral RI / NAC |
|-----------|------------------|------------------|
| `None` | 2.9 / 4.0 | 10.9 / 9.4 |
| `"v3"` | 2.7 / 3.3 | 11.0 / 9.1 |
| `"allsoils_v1"` | 3.4 / 4.3 | 4.1 / 5.4 |

Notes:
- Use `"v3"` or `None` for organic soils (≥ 30 % SOM) and `"allsoils_v1"` for mineral or mixed soils.
- `allsoils_v1`: no mineral soil was measured below 0 °C; three mineral soils have PTF-estimated SOM (±6.9 points); laboratory salinity is unknown and is absorbed by the conductivities.
- `v3`: the dry-soil attenuation term is slightly negative, so `ε''` can reach −0.04 for SSM below about 0.05 cm³/cm³. Clip it at zero if you need a passive medium.

## DDOAS validation tools

Needs `[analysis]`. The DDOAS files are in `data/ddoas/` of this repository, not in the PyPI package: run from a clone or pass `data_dir=`.

```python
from tdmrsdm import DDOASDataset, TrainableTDMRSDM

df = DDOASDataset(data_dir="data/ddoas", verbose=False).load_site("BV")
subset = df[(df["freq_GHz"] <= 5.0) & (df["st"].between(-5, 5))]     # st: temperature (°C)
ri, nac, metrics = TrainableTDMRSDM(verbose=False).predict(subset)[:3]
print(metrics["ri_r2"], metrics["nac_nrmse_pct"])
```

Notebooks for each DDOAS soil are in `examples/`.

## Layout

```
src/tdmrsdm/
  core.py          TDMRSDM, NumPy forward model
  torch_core.py    TorchTDMRSDM, PyTorch port                 [torch]
  constants/       2019, v1, v2, v3, allsoils_v1 JSON files
  dataset/         DDOASDataset                                [analysis]
  trainable.py     TrainableTDMRSDM                            [analysis]
  dependence/      dependence analysis                         [analysis]
  optimization/    constant re-tuning                          [analysis]
  figure/          validation figures                          [figures]
data/ddoas/        DDOAS .xls files (repository only)
examples/          notebooks
tests/             pytest suite
```

## Tests

```bash
pip install -e ".[dev]"
python -m pytest tests/
```

`tests/test_allsoils_parity.py` checks `TDMRSDM` and `TorchTDMRSDM` against the tdmrsdm-allsoils reference model for the `None`, `v3` and `allsoils_v1` constants.

## References

- Mironov, V. L., & Savin, I. V. (2019). Spectroscopic multirelaxation dielectric model of thawed and frozen Arctic soils considering the dependence on temperature and organic matter content. *Izvestiya, Atmospheric and Oceanic Physics*, 55(9), 986–995. https://doi.org/10.1134/S0001433819090305
- Savin, I., Mironov, V., Muzalevskiy, K., Fomin, S., Karavayskiy, A., Ruzicka, Z., & Lukin, Y. (2020). Dielectric database of organic Arctic soils (DDOAS). *Earth System Science Data*, 12, 3481–3487. https://doi.org/10.5194/essd-12-3481-2020

## License

MIT, see [LICENSE](LICENSE). Contact: Morteza Khazaei, morteza.khazaei@usherbrooke.ca.
