Metadata-Version: 2.4
Name: pymopsmap
Version: 0.3.1
Summary: Python wrapper for MOPSMAP aerosol optical properties computation
Project-URL: Homepage, https://github.com/walcark/pymopsmap
Project-URL: Issues, https://github.com/walcark/pymopsmap/issues
Author-email: walcark <kevin.walcarius@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.11
Requires-Dist: einops>=0.7
Requires-Dist: netcdf4>=1.7
Requires-Dist: numpy>=2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: scipy>=1.10
Requires-Dist: structlog>=24.0
Requires-Dist: tqdm>=4.0
Requires-Dist: xarray>=2024.1
Description-Content-Type: text/markdown

# PyMopsmap

<p align="center">
  <img src="https://github.com/walcark/pymopsmap/actions/workflows/ci.yml/badge.svg">
  <a href="https://pixi.sh"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json"></a>
  <a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json"></a>
  <a href="https://pypi.org/project/pymopsmap/"><img src="https://img.shields.io/pypi/v/pymopsmap.svg"></a>
  <a href="https://github.com/walcark/pymopsmap/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
  <img src="https://img.shields.io/badge/python-3.11%2B-blue">
</p>

Python wrapper for [MOPSMAP](https://mopsmap.net) — aerosol optical property computation based on Mie, T-matrix, and DDA single-particle scattering ([Gasteiger & Wiegner 2018, GMD](https://doi.org/10.5194/gmd-11-2739-2018)).

## Overview

PyMopsmap drives the MOPSMAP Fortran binary from Python. Given a set of aerosol microphysical parameters (shape, size distribution, refractive index), it:

1. resolves and downloads the required optical dataset files,
2. writes a MOPSMAP launch file and runs the binary,
3. parses the outputs into an `xarray`-backed `OptiProps` object,
4. caches the result on disk keyed by a blake2b hash of the inputs.

## Installation

### From PyPI

```bash
pip install pymopsmap
```

### From source (development)

```bash
git clone https://github.com/walcark/pymopsmap.git
cd pymopsmap
pixi install -e dev
```

| Environment | Dev tools | Command |
|---|---|---|
| `default` | No | `pixi install` |
| `dev` | ruff, mypy, pytest | `pixi install -e dev` |

### MOPSMAP binary

The MOPSMAP Fortran binary must be placed at `bin/mopsmap/mopsmap` relative to the repository root. Download it from [mopsmap.net](https://mopsmap.net).

### Optical dataset

MOPSMAP requires a pre-computed optical dataset. Set `PYMOPSMAP_DATASET_SOURCE` to a local path or HTTP base URL pointing to the dataset root:

```bash
export PYMOPSMAP_DATASET_SOURCE=/path/to/optical_dataset
# or
export PYMOPSMAP_DATASET_SOURCE=https://your-server.org/mopsmap_dataset
```

Dataset files are downloaded on demand and cached under `~/.cache/pymopsmap/` (override with `PYMOPSMAP_CACHE_DIR`).

### Verify

```bash
pixi run -e dev python -c "import pymopsmap; print('pymopsmap OK')"
```

## Quick start

```python
import pymopsmap as pm

mp = pm.MicroParameters(
    wavelength=[0.44, 0.55, 0.67],
    n_real=[1.45, 1.45, 1.45],
    n_imag=[1e-3, 1e-3, 1e-3],
    shape=pm.Sphere(),
    psd=pm.LognormalPSD(rm=0.1, sigma=1.5, n=1.0, rmin=0.01, rmax=10.0),
)

op = pm.compute(mp)     # → OptiProps (xarray Dataset)
kext = pm.kext(mp)      # → DataArray indexed by wavelength
```

### Batch computation over external parameters

```python
sweep = pm.ParametricSweep()
for rh, mp_rh in zip([0, 50, 80], [mp_rh0, mp_rh50, mp_rh80]):
    sweep.add(pm.ParticleMixture([mp_rh]), {"rh": rh})

op = pm.compute(sweep)   # → OptiProps with an extra 'rh' dimension
```

### CAMS aerosol adapter

```python
from pymopsmap.adapters import cams_to_kext, CamsAerosol, CamsVersion

kext = cams_to_kext(
    aerosol=CamsAerosol.SEA_SALT_CAMS,
    version=CamsVersion.V49_R1,
    wl_microns=[0.44, 0.55, 0.67],
    rh=[0, 50, 80, 99],
)
```

### OPAC aerosol adapter

```python
from pymopsmap.adapters.input.opac import OpacMix, OpacMixName, OpacHumidityMode

mix = OpacMix(OpacMixName.CONTINENTAL_AVERAGE)

# GEISA mode: wet PSD and refractive index interpolated from GEISA tables
op = mix.compute(wavelengths=[0.44, 0.55, 0.67], rhs=[0, 50, 80])

# Kappa mode: hygroscopic growth via κ parameterisation (Zieger et al. 2013)
# with volume-weighted refractive index mixing with water
op = mix.compute(
    wavelengths=[0.44, 0.55, 0.67],
    rhs=[0, 50, 80],
    mode=OpacHumidityMode.KAPPA,
)
```

## Output types

```python
op = pm.compute(mp, output_types=frozenset({
    pm.OutputType.INTEGRATED,
    pm.OutputType.PHASE_FUNCTION,
    pm.OutputType.LIDAR,
}))
```

Available: `INTEGRATED`, `LIDAR`, `PHASE_FUNCTION`, `SCATTERING_MATRIX`, `VOLUME_SCATTERING_FUNCTION`, `COEFF`.

## Design philosophy

The adapter layer (`adapters/input/`) is designed for progressive extension. Each adapter translates an external aerosol description format — CAMS reanalysis tables, OPAC climatology, user-defined files — into the common `MicroParameters` + `ParametricSweep` representation that the engine consumes. Adding support for a new data source means implementing one adapter without touching the engine or the cache.

The same extensibility applies to output adapters (`adapters/output/`): once optical properties are computed as `OptiProps`, they can be converted to any downstream format (e.g. SMART-G LUT) by adding an output adapter.

## Dataset cache

```python
pm.cache_status(mp)   # lists cached vs missing dataset files
pm.prefetch(mp)       # download without computing
```

## Project structure

```
src/pymopsmap/
├── models/      # MicroParameters, OptiProps, OutputRequest, particle systems
├── engine/      # MOPSMAP binary interface (launch file, runner, output parser)
├── cache/       # Optical dataset files and result cache
├── adapters/
│   ├── input/   # External data formats → MicroParameters  (e.g. CAMS, OPAC)
│   └── output/  # OptiProps → external formats             (e.g. SMART-G)
└── utils/       # Logging, types, temp files, caching
```

## Roadmap

- **Transparent remote dataset** — automatic download of the full optical dataset from a hosted server when `PYMOPSMAP_DATASET_SOURCE` is not set, removing the manual setup step.
- **Article validation** — complete reproduction of the figures from [Gasteiger & Wiegner (2018)](https://doi.org/10.5194/gmd-11-2739-2018) as a test suite, ensuring physical correctness of the computed optical properties across all shape types and size parameters.

## Development

```bash
pixi run -e dev test          # pytest + coverage
pixi run -e dev lint          # ruff
pixi run -e dev all           # fmt + lint + type-check + test
```
