dirt_atom 0.1.4

Per-atom DEM data (radius, density) with pack/unpack and MaterialTable for DIRT
# dirt_atom

Per-atom DEM data and material property tables for DIRT.

## What it does

`dirt_atom` provides the core per-atom data structures, material property system, and
particle insertion for Discrete Element Method (DEM) simulations on the SOIL substrate:

- **`DemAtom`** — Per-atom extension data (radius, density, inverse inertia, orientation,
  angular velocity/momentum, torque, rigid-body id), registered via SOIL's `AtomData` derive
  with `#[forward]` / `#[reverse]` / `#[zero]` attributes controlling MPI pack/unpack and
  per-timestep zeroing.
- **`MaterialTable`** — Named materials with mechanical properties (Young's modulus,
  Poisson's ratio, restitution, friction, cohesion/surface energy, rolling/twisting) plus
  precomputed per-pair mixing tables for contact-force evaluation.
- **`DemAtomPlugin`** — Registers `DemAtom` and builds the `MaterialTable` from
  `[[dem.materials]]` config.
- **`DemAtomInsertPlugin`** — Particle insertion from `[[particles.insert]]`: random
  placement (with overlap checking), rate-based trickle insertion, or file-based loading
  (CSV, LAMMPS dump, LAMMPS data).
- **`RadiusSpec`** — Fixed radius or statistical distribution (uniform, gaussian, lognormal,
  discrete).

## Key types and plugins

| Item | Kind | Role |
|------|------|------|
| `DemAtomPlugin` | Plugin | Registers `DemAtom`, builds `MaterialTable`, sets `Atom::ntypes` |
| `DemAtomInsertPlugin` | Plugin | Setup + runtime systems for random/rate/file insertion |
| `DemAtom` | AtomData | Per-atom radius, density, `inv_inertia`, `quaternion`, `omega`, `ang_mom`, `torque`, `body_id` |
| `MaterialTable` | Resource | Per-material + per-pair (`*_ij`) precomputed contact properties |
| `MaterialConfig` / `DemConfig` | Config | Deserialized `[[dem.materials]]` / `[dem]` |
| `InsertConfig` / `ParticlesConfig` | Config | Deserialized `[[particles.insert]]` / `[particles]` |
| `RadiusSpec` / `RadiusDistribution` | Config | Fixed value or sampled radius distribution |
| `same_body(dem, i, j)` | fn | True when atoms `i` and `j` share a rigid body |

## AtomData attributes on `DemAtom`

- **`#[forward]`** — sent from owner to ghost atoms each timestep (`omega`, `body_id`).
- **`#[reverse]`** — ghost contributions summed back into the owner (`torque`).
- **`#[zero]`** — zeroed before each force computation (`torque`).

Fields without attributes are communicated only during atom migration.

## TOML configuration

```toml
[dem]
contact_model = "hertz"        # "hertz" (default) or "hooke"
adhesion_model = "jkr"         # "jkr" (default) or "dmt", used when surface_energy > 0
rolling_model = "constant"     # "constant" (default) or "sds"
twisting_model = "constant"    # "constant" (default) or "sds"

[[dem.materials]]
name = "glass"
youngs_mod = 8.7e9
poisson_ratio = 0.3
restitution = 0.95
friction = 0.4                 # default 0.4
# rolling_friction = 0.1       # default 0.0 (disabled)
# cohesion_energy = 0.05       # SJKR cohesion, J/m² (default 0.0)
# surface_energy = 0.05        # JKR/DMT adhesion, J/m² (default 0.0; mutually exclusive with cohesion_energy)
# twisting_friction = 0.0
# kn = 0.0 / kt = 0.0          # Hooke stiffnesses (0 = use Hertz/Mindlin)

[[particles.insert]]
material = "glass"             # must match a [[dem.materials]] name
count = 100
radius = 0.001                 # or a distribution table
density = 2500.0
```

## Usage

```rust
use dirt_atom::{Elastic, Friction, Material, MaterialTable};

let mut table = MaterialTable::new();
let glass = table.add(Material::new("glass", Elastic::new(8.7e9, 0.3, 0.95))
    .with_friction(Friction { sliding: 0.4, rolling: 0.1, twisting: 0.0 }))?;
let steel = table.add(Material::new("steel", Elastic::new(200e9, 0.28, 0.8))
    .with_friction(Friction { sliding: 0.3, rolling: 0.05, twisting: 0.0 }))?;
table.build_pair_tables();

// Per-pair properties indexed as table_ij[i][j]:
let friction = table.friction_ij[glass.index()][steel.index()];
let e_eff = table.e_eff_ij[glass.index()][steel.index()];
```

Per-pair tables are mixed with **geometric mean** (friction, restitution, rolling/twisting
friction, cohesion/surface energy, SDS damping), **harmonic mean** (Hooke and SDS stiffnesses),
and **effective-modulus** formulas for Hertz (`E*`) and Mindlin (`G*`).

## License

MIT OR Apache-2.0