Rustb 0.7.0

A package for calculating band, angle state, linear and nonlinear conductivities based on tight-binding models
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# Rustb–cryspglib integration and basis-ownership plan

Status: milestones A–E, F1 (exact Hamiltonian-symmetry certification plus
residual magnetic-group identification), and F2 (validated opt-in Hamiltonian
symmetrization) are implemented for Rustb 0.7 pre-release. Wiring weighted
meshes into response solvers, automatic non-s orbital representations,
gauge-covariant Peierls actions, and numerical band irrep/corep assignment
remain follow-up work.

## 1. Objective and non-objectives

Rustb will use cryspglib as an optional, pure-Rust crystallographic-symmetry
backend. The two crates remain independent projects and the dependency is
strictly one-way:

```text
Rustb Model
  -> checked crystal adapter
  -> cryspglib Crystal / symmetry analysis
  -> Rustb-owned result and query types
```

The structure adapter never assumes that a tight-binding Hamiltonian respects
every detected structure operation. Milestone F1 adds a separate, explicit
Hamiltonian certification call whose basis representation is supplied by the
caller (or by a deliberately strict scalar-site provider). Symmetrization and
numerical band-irrep assignment remain separate opt-in milestones.

The integration must preserve the common Rustb workflow in which a model has
orbitals but no atomic-site metadata.

## 2. Basis and ownership redesign

### 2.1 Ownership rule

`Model` remains the sole owner of the dense orbital arrays and Hamiltonian.
`Atom` does not own, copy, or borrow orbital storage. Instead, it holds typed
model-local orbital identifiers:

```rust
pub struct OrbitalId(usize);
pub struct AtomId(usize);

pub struct Atom {
    position: Array1<f64>,
    name: AtomType,
    orbitals: Vec<OrbitalId>,
    magnetic_moment: Option<[f64; 3]>,
}
```

An `OrbitalId` indexes the same row in `Model::orb` and
`Model::orb_projection`, and the corresponding orbital axes in `ham` and
`rmatrix`. This is a safe reference-like handle, not a raw pointer.

Rust references, raw pointers, and `Rc`/`Weak` are rejected for persistent
storage because they would make `Model` self-referential and fragile under
move, clone, serde, array reallocation, supercell construction, cutting, and
orbital reordering.

Borrowed access is provided at call time:

```rust
let atom = model.atom(AtomId::new(0))?;
for orbital in atom.orbitals() {
    println!("{:?}", orbital.position());
}
```

Here `AtomView<'model>` and `OrbitalRef<'model>` contain ordinary Rust borrows,
so the compiler prevents model mutation while a view is live.

### 2.2 Supported model states

The following are all valid:

- orbitals with no atoms (`atoms.is_empty()`);
- atoms with no selected TB orbitals;
- orbitals not assigned to any atom;
- an atom owning non-contiguous orbital IDs;
- orbital centers different from atomic positions.

Each orbital may belong to at most one atom. The reverse
`orbital -> Option<AtomId>` table is derived by `Model::validate()` and is not
serialized as a second source of truth.

### 2.3 Required invariants

`Model::validate()` checks at least:

- lattice and coordinate dimensions;
- finite lattice/positions and a non-singular lattice where required;
- `orb_projection.len() == norb`;
- every `OrbitalId` is in range;
- an orbital has at most one atomic owner;
- `ham.shape() == (hamR.nrows(), nsta, nsta)`;
- `hamR.ncols() == DIM`;
- `rmatrix`, when present, is aligned with `hamR`, `DIM`, and `nsta`.

Public symmetry entry points validate on every call because Rustb currently
exposes model fields publicly.

### 2.4 Migration of model-changing operations

Every operation that selects or reorders orbitals constructs one explicit map:

```text
old OrbitalId -> Option<new OrbitalId>
```

The same map updates orbital positions, projections, Hamiltonian axes,
position-matrix axes, and every atom's orbital IDs. This rule applies to
orbital removal, atom removal, atom/orbital reordering, supercells, cuts,
Floquet/Sambe models, unfolding, and Wannier90 import.

`remove_atom` keeps its historical cascade behavior during the 0.7 migration,
but clearer APIs are added:

- `remove_atoms_and_orbitals`;
- `remove_atoms_only`, which leaves the former orbitals unassigned;
- `prune_empty_atoms`.

## 3. Dependency and public API boundary

### 3.1 Cargo feature

Rustb adds one optional feature:

```toml
cryspglib = { path = "../cryspglib", version = "0.1.0", optional = true,
              default-features = false }

[features]
cryspglib = ["dep:cryspglib"]
```

cryspglib never depends on Rustb. Rustb does not wildcard-re-export
cryspglib. During local development Cargo uses the sibling path; publication
requires publishing the matching cryspglib version first.

### 3.2 Rustb API

The entry point is an extension trait implemented for 3D models:

```rust
pub trait CrystalSymmetry {
    fn crystal_symmetry(
        &self,
        parameters: &SymmetryParameters,
    ) -> Result<CrystalSymmetryDataset>;

    fn magnetic_crystal_symmetry_from_atoms(
        &self,
        parameters: &SymmetryParameters,
    ) -> Result<MagneticCrystalSymmetry>;

    fn magnetic_crystal_symmetry(
        &self,
        moments: &[[f64; 3]],
        parameters: &SymmetryParameters,
    ) -> Result<MagneticCrystalSymmetry>;
}
```

`SymmetryParameters` contains structural tolerances and the optional field
context. Every Atom has an optional finite Cartesian moment: `None` is the
nonmagnetic default; `Atom::set_magnetic_moment` attaches a moment; and
`Atom::clear_magnetic_moment` removes it. The `_from_atoms` entry points use
that stored metadata directly, while `magnetic_crystal_symmetry` remains an
explicit per-call override. `Model<true, ..>` only describes a spinful basis
and is never treated as proof of magnetic order.

Uniform electric and magnetic fields already encoded in a Hamiltonian are
supplied explicitly as optional Cartesian vectors in `SymmetryParameters` for
that analysis call and forwarded into cryspglib. They are not stored in
`Model`. The result distinguishes
the structural operation group from the effective subset preserving the fields:
electric field is time-even/polar; magnetic field is time-odd/axial. A later
Hamiltonian-representation validator remains necessary for arbitrary
symmetry-breaking terms that cannot be described by uniform E/B metadata.

When fields reduce the operation set, canonical high-symmetry points and
character tables of the structural database group are rejected with
`FieldReducedSymmetryData`; they are not presented as effective-group data.
Irreducible meshes are generated from the field-preserving operations,
including combined spatial×time-reversal operations where applicable.

Rustb owns its public result types so that cryspglib 0.x changes do not become
uncontrolled Rustb API changes. Results retain all setting context required by
later queries: space-group number, Hall number, transformation matrix, origin
shift, standardized lattice, operations, Wyckoff data, and atom mappings.

### 3.3 Conversion contract

Rustb stores real-space lattice vectors as rows and uses
`fractional.dot(model.lat)` for Cartesian coordinates. cryspglib uses
`lattice[cartesian_component][lattice_vector]`, so the adapter transposes:

```text
cryspglib_lattice[i][j] = model.lat[[j, i]]
```

The adapter uses atomic positions, never Wannier/orbital centers. Atomic types
are converted through `AtomType::atomic_number()` rather than enum casts.

The first implementation targets `DIM = 3`. A later explicit 2D adapter may
embed a 2x2 lattice into 3D with a user-provided positive vacuum length and
`AperiodicAxis::Z`. `DIM = 1` is rejected because cryspglib's layer API models
one, not two, aperiodic axes.

## 4. Functional milestones

### Milestone A — model ownership foundation

Implementation:

1. Introduce `AtomId`, `OrbitalId`, `AtomView`, and `OrbitalRef`.
2. Replace the atom orbital count with `Vec<OrbitalId>`.
3. Add explicit atom and orbital-only construction paths.
4. Add `Model::validate`, ownership lookup, and remapping helpers.
5. Migrate active model transformations away from prefix-count assumptions.
6. Keep the typed-ID serde representation validated on load. The implemented
   legacy-count reader maps an all-count file to contiguous IDs in Atom order
   and rejects files that mix legacy counts with typed IDs; the typed format is
   required for non-contiguous or unassigned ownership.

Expected result:

- atoms can safely reference arbitrary orbitals;
- dense orbital/Hamiltonian storage is unchanged;
- orbital-only models remain valid;
- cloning and serialization contain no self-references.

Tests:

- non-contiguous orbital ownership;
- unassigned orbitals and empty atoms;
- duplicate/out-of-range ownership errors;
- removal/reorder remapping;
- spinless/spinful and `NoRMatrix`/`HasRMatrix` validation;
- supercell, cut, Floquet, unfold, and serde regressions.

### Milestone B — optional symmetry adapter

Implementation:

1. Add the `cryspglib` Cargo feature and conditional module/export.
2. Add structured `TbError` variants preserving `cryspglib::SymError` as the
   source.
3. Validate the model and require non-empty, explicitly typed atoms.
4. Transpose the lattice and copy finite fractional site positions and atomic
   numbers into `cryspglib::Crystal`.
5. Convert the returned dataset into Rustb-owned types.

Expected result:

- feature-off Rustb has no cryspglib dependency in its API;
- feature-on 3D models return space group, Hall setting, point group,
  operations, Wyckoff letters, site symmetries, equivalent atoms, and
  primitive/standard cell metadata;
- malformed models return `Result::Err` and never panic.
- `tb_model(..., None)` is orbital-only and symmetry analysis rejects it with
  `MissingAtomicStructure`; no orbital center is promoted to an atom.

Tests:

- feature-off and feature-on compilation;
- a skew P1 lattice that detects accidental transpose;
- simple cubic Pm-3m;
- diamond/Si known group and equivalent atoms;
- atom centers deliberately different from orbital centers;
- missing atoms and invalid model invariants return structured errors.

### Milestone C — magnetic symmetry

Implementation:

1. Add explicit site-moment input and finite/count validation.
2. Call cryspglib magnetic analysis and preserve UNI, BNS, OG, parent Hall,
   time-reversal flags, and complete magnetic operations.
3. Never use operation-only classification where a detected parent Hall is
   available.

Expected result:

- nonmagnetic and magnetic results are distinguishable without sentinel
  values;
- the known ambiguous UNI settings retain sufficient parent/family Hall
  context and do not silently collapse to UNI 0.

Tests:

- `SPIN=true` without moments remains ordinary structural analysis;
- ferro-, antiferro-, and non-collinear fixtures;
- invalid moment length/NaN;
- an ambiguity regression retaining parent Hall context.

### Milestone D — high-symmetry queries and character tables

Implementation:

1. Expose canonical high-symmetry point records with an explicit coordinate
   frame tag.
2. Add a tested reciprocal-basis conversion from the cryspglib data-Hall frame
   to the original Rustb input frame before points may be passed to `k_path`.
3. Distinguish isolated points from parameterized symmetry lines.
4. Return structured character tables as well as formatted Markdown tables.
5. For magnetic groups, query corepresentations with the retained UNI and
   Hall-setting context.
6. Keep character-table operation headers in their canonical database frame
   and expose that exact ordered operation list separately from input-basis
   structural operations.

Expected result:

- users can list high-symmetry points for a detected model, choose a label,
  obtain its complete operation/class-column character table, and build a
  Rustb path in the original reciprocal basis;
- nonstandard Hall settings cannot silently reuse canonical coordinates.

Tests:

- standard and alternate Hall settings of the same group;
- reciprocal-coordinate round trips on a skew cell;
- ordinary character tables and magnetic corep tables;
- UNI 282–284 and other setting-sensitive cases;
- unsupported lines/settings return an explicit error.

### Milestone E — irreducible weighted meshes

Implementation:

1. Convert cryspglib grid addresses and mapping table into the Rustb-owned
   `IrreducibleKMesh` result.
2. Derive normalized multiplicity weights and retain the full-to-irreducible
   map in both cryspglib and Rustb `gen_kmesh` order.
3. Provide separate structural/time-reversal and explicit-moment magnetic mesh
   entry points so magnetic order is never inferred from `SPIN`.
4. Add weighted sampling to direct Brillouin-zone summation (follow-up).
5. Keep simplex and energy-cut algorithms on their full topological mesh until
   they receive a symmetry-aware topology implementation.

Expected result:

- callers can consume normalized irreducible meshes directly without assuming
  cryspglib and `gen_kmesh` share an index order;
- after response integration is added, direct sums can use symmetry reduction
  without changing normalization, while unsupported topological quadratures
  must reject weighted input instead of producing plausible but wrong numbers.

Tests:

- weights sum to one and multiplicities sum to the full mesh size;
- full-grid and weighted direct sums agree for invariant scalar/vector test
  integrands;
- shifted/unshifted meshes and time-reversal toggles;
- index ordering is checked rather than assumed to match `gen_kmesh`.

### Milestone F — Hamiltonian symmetry and band representations

#### F1 — implemented exact certification and residual MSG identification

Implementation flow:

1. `Model::check_hamiltonian_symmetry` validates model shapes, explicit Atom
   ownership of every orbital, finite matrix elements, unique `hamR`, complete
   `R <-> -R` storage, and
   `H(-R) = H(R)^dagger` before testing any operation.
2. Detect the Atom-based structural space group, form its grey extension
   `G + G 1'` by default, and then apply the per-call uniform electric/magnetic
   field context. Testing only the unitary structural group is available as a
   diagnostic mode but cannot return a certified final MSG.
3. Resolve every candidate through `BasisSymmetryRepresentation`. The built-in
   `ScalarSiteBasis` accepts only one `s` orbital per atom, total orbital
   ownership, and (by default) orbital centers coincident with their Atom.
   Custom Wannier gauges provide finite `CellShiftAction` matrices explicitly.
4. Represent the localized action as

   ```text
   g |b,R> = sum_(s,a) (D_s^g)_(a,b) |a, W R + s>.
   ```

   Validate its Laurent-operator unitarity before using it. Spinful scalar
   sites use the axial `SU(2)` lift of the Cartesian operation and the Rustb
   basis order `[all up orbitals, all down orbitals]`; anti-unitary operations
   include `i sigma_y K`.
5. On the complete finite real-space support, evaluate

   ```text
   K_g(R) = sum_(s,t) D_s^dagger H(W R + t - s) D_t.
   ```

   Require `H(R) = K_g(R)` for unitary operations and
   `H(R) = K_g(R)*` for anti-unitary operations. The comparison domain includes
   inverse images of all stored hopping blocks, so a transformed nonzero term
   cannot hide at an absent (implicitly zero) `R`. This is a proof on the
   finite Laurent polynomial, not a sampled-k heuristic.
6. Return one residual and worst `(R, bra, ket)` witness per operation, retain
   every validated localized action/sewing matrix for later little-group work,
   and distinguish `Preserved`, `Broken`, and `Unresolved`. An unresolved
   orbital action gives a lower bound and never masquerades as physical
   symmetry breaking.
7. Pass the survivor set to cryspglib's
   `ValidatedMagneticOperationSet`, which checks identity, duplicates,
   inverses, and closure. It derives the surviving group's own family Hall
   setting before UNI/BNS identification. The higher structural Hall is stored
   only as provenance, preventing the known Type-IV setting ambiguity from
   being “resolved” with the wrong supergroup Hall.

#### F2 — implemented forced Hamiltonian symmetrization

Public API:

```rust
let symmetrized = model.symmetrize_hamiltonian(
    &target_magnetic_group,
    &basis_representation,
    &HamiltonianSymmetrizationParameters::default(),
)?;
```

Implementation flow:

1. Validate the input Model, finite/Hermitian hopping support, tolerances, and
   the supplied magnetic operation set. cryspglib normalizes fractional
   translations and proves identity, uniqueness, inverses, and closure.
2. Recompute the admissible magnetic operations from the current Model's
   lattice, Atom positions and species, optional Atom moments, and explicit
   electric/magnetic field context. Every target operation must be present in
   this recomputed set. Any mismatch returns
   `TbError::TargetMagneticGroupIncompatible` before the basis provider is
   called and before any hopping matrix is averaged.
3. Identify the normalized target operations and require matching UNI,
   BNS/OG, and magnetic type metadata. The structural-parent SG/Hall fields
   are not confused with the residual MSG family SG/Hall fields.
4. Resolve and validate one localized Laurent action for every normalized
   target operation. Besides individual unitarity and orbital-centre geometry,
   require the actions to form a projective magnetic corepresentation:

   ```text
   sum_(s + W_g t = u) D_s^g (D_t^h)^(conj if g antiunitary)
       = z_(g,h) D_(u-n)^(gh),  |z_(g,h)| = 1.
   ```

   The global phase admits spin-half double groups and `T^2 = -1` without
   admitting unrelated per-operation matrices.
5. Apply the real-linear magnetic Reynolds projection on the complete
   symmetry-generated support:

   ```text
   H_sym(R) = (1 / |M|) sum_(g in M) P_g H(R),
   P_g H(R) = C_theta [sum_(s,t) D_s^dag H(W_g R+t-s) D_t].
   ```

   Close support under `R <-> -R`, enforce Hermiticity pairwise, keep `R=0`
   in `hamR` row zero, retain original support order where possible, and append
   generated blocks deterministically.
6. Return a new `Model`; `self` is unchanged. Existing `HasRMatrix` blocks are
   remapped by lattice vector, while newly generated Hamiltonian-support rows
   receive zero `rmatrix` blocks because position-matrix data is a different
   operator and is not silently symmetrized as a scalar.
7. Revalidate the returned Model and rerun every complete-support covariance
   equation. A failed postcheck returns an error rather than a partially or
   plausibly symmetrized Model.

Expected result:

- slightly symmetry-broken hoppings can be projected onto an explicitly chosen
  compatible MSG;
- lattice/Atom/species/moment/field incompatibility is a hard pre-projection
  error;
- the original Model is never mutated;
- nonsymmorphic cell shifts, antiunitary actions, spin-half projective phases,
  generated hopping support, Hermiticity, and `rmatrix` alignment remain
  explicit and testable.

Implemented tests:

- cubic hoppings `1, 2, 3` average to `2`, the input stays unchanged, and a
  second projection is idempotent;
- spinless grey projection removes imaginary time-reversal-breaking hopping;
- spinful grey projection removes a Zeeman term;
- support expansion creates missing cubic partners while keeping `hamR[0]=0`;
- `HasRMatrix` original rows retain their blocks and generated rows are zero;
- Type-IV staggered order preserves half-translation times time reversal;
- `T^2=-1` and anti-half-translation cell-shift composition are accepted;
- inconsistent projective actions and unsupported orbital metadata are hard
  errors;
- incompatible lattice, Atom type, stored moments, and external fields fail
  before basis resolution.

#### F3 — deferred band representations and general gauges

1. Add shell/channel/local-frame metadata and automatic complete `p`, `d`, and
   `f` atomic-harmonic representations. `OrbProj` alone is insufficient for a
   general Wannier gauge, repeated radial channels, incomplete shells, or
   spin-orbit-entangled orbitals.
2. Add gauge-covariant Peierls-field actions where spatial operations require
   position-dependent `U(1)` compensation.
3. Restrict retained sewing matrices to little groups, handle degenerate
   eigenspaces, and assign numerical band irreps/coreps with setting-aware
   cryspglib tables.

Expected result:

- structure symmetry and Hamiltonian symmetry are reported separately;
- unsupported hybrid/user Wannier representations require explicit user
  matrices rather than being guessed;
- a closed reduced operation set is identified as its final UNI/BNS magnetic
  group with family-Hall and coordinate-transform metadata;
- threshold-induced nonclosure and incomplete basis metadata are explicit
  `Inconclusive` results rather than guessed groups;
- no input model is modified; opt-in symmetrization returns a new model.

Tests:

- implemented: cubic scalar model retains all 96 grey operations;
- implemented: anisotropic hopping reduces cubic symmetry to a 16-operation
  orthorhombic grey group;
- implemented: complex directed hopping breaks pure time reversal and yields a
  Type-III group;
- implemented: spinful Zeeman onsite term exercises the axial `SU(2)` and
  anti-unitary paths;
- implemented: half translation produces separate zero/cross-cell action
  sectors and the correct lattice-gauge Bloch phase;
- implemented: uniform magnetic-field context reduces the grey candidate set
  before Hamiltonian checking;
- implemented: non-Hermitian public mutation is rejected and unsupported
  orbital metadata returns `Inconclusive`, not `Broken`;
- implemented: Type-IV staggered Zeeman order, nonsymmorphic complete-support
  covariance, orbital-cell representative gauge, and spinful antiunitary
  sewing conventions;
- implemented: forced symmetrization, incompatible-target rejection,
  projective-corepresentation validation, support expansion, idempotence, and
  `HasRMatrix` alignment;
- deferred with F3: arbitrary Wannier-gauge covariance, complete `p/d/f`
  shells, Peierls gauge compensation, and degenerate-band character/projector
  tests.

## 5. Verification commands

Use one BLAS backend at a time and run numerical tests in release mode:

```bash
cargo fmt --all -- --check
cargo check -p Rustb --no-default-features
cargo check -p Rustb --no-default-features --features cryspglib
cargo check -p Rustb --features intel-mkl-system,cryspglib
cargo test -p Rustb --release --features intel-mkl-system,cryspglib --lib
cargo clippy -p Rustb --release --all-targets --features intel-mkl-system,cryspglib -- -D warnings
cargo doc -p Rustb --release --no-deps --features intel-mkl-system,cryspglib
```

Do not use `--all-features`: Rustb's allocator features are mutually exclusive,
and BLAS backends must not be enabled together.

## 6. Delivery and acceptance

Each milestone is separately reviewable and must update its unit tests,
rustdoc, README, SKILLS, and CLAUDE notes with the public API. Existing generated
PDF outputs are user-owned and are never included in these changes.

The current delivery is accepted when milestones A–E, F1, and F2 compile and
pass release tests, and the plan records deferred F3 limitations explicitly. No
API may return a fake nonmagnetic or zero identifier on failure: invalid input
uses structured `Result` errors, while physically incomplete classification
uses an explicit `Inconclusive` report carrying its reason.