Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 262 additions & 2 deletions docs/model_method/model_method.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,264 @@
# `ModelMethod`

!!! warning
This page is still under construction.
The `ModelMethod` section contains all parameters and settings that define the mathematical model or Hamiltonian used in a simulation. This module abstracts the model into various levels so that the total Hamiltonian can be decomposed into contributions (or terms). The module currently covers common electronic structure methods (e.g., DFT, TB, GW,BSE, DMFT), and it is being actively developed to encompass further computational methods.


## Explanation of main sections

| Section | Key Quantities & Brief Explanation |
|---------------------------|--------------------------------------|
| **BaseModelMethod** | - `name`: Identifier for the mathematical model (e.g., DFT, TB, GW, etc.).<br>- `type`: Specifies the model sub-type (e.g., Wannier, DFTB, xTB, Slater-Koster).<br>- `external_reference`: URL or DOI for an external reference to the model.<br>- `numerical_settings`: A subsection holding numerical parameters (e.g., convergence criteria). |
| **ModelMethod** | Inherits from BaseModelMethod and adds:<br>- `contributions`: A subsection to include individual contributions or terms that build up the total Hamiltonian. |
| **ModelMethodElectronic** | Inherits from ModelMethod and adds electronic structure specific details:<br>- `is_spin_polarized`: Boolean indicating if spin polarization is considered (e.g., two spin channels).<br>- `relativity_method`: Specifies the relativistic treatment applied (e.g., scalar_relativistic, pseudo_scalar_relativistic). |


## List of electronic structure methods

<table>
<tr>
<th>Electronic Structure Method</th>
<th>Quantity</th>
<th>Explanation</th>
<th>Quantity Type</th>
</tr>
<!-- DFT -->
<tr>
<td rowspan="5"><strong>DFT</strong></td>
<td>jacobs_ladder</td>
<td>Classifies the XC functional (e.g., LDA, GGA, metaGGA, hybrid).</td>
<td>MEnum (options: LDA, GGA, metaGGA, hyperGGA, hybrid, unavailable)</td>
</tr>
<tr>
<td>xc_functionals</td>
<td>List of XCFunctional subsections.</td>
<td>SubSection (list)</td>
</tr>
<tr>
<td>exact_exchange_mixing_factor</td>
<td>Fraction of exact exchange mixed in.</td>
<td>np.float64</td>
</tr>
<tr>
<td>self_interaction_correction_method</td>
<td>Specifies any self-interaction correction applied.</td>
<td>str</td>
</tr>
<tr>
<td>van_der_waals_correction</td>
<td>Method used for van der Waals correction.</td>
<td>MEnum (options: TS, OBS, G06, JCHS, MDB, XC)</td>
</tr>
<!-- TB -->
<tr>
<td rowspan="4"><strong>TB</strong></td>
<td>n_orbitals_per_atom</td>
<td>Number of orbitals per atom used as basis.</td>
<td>np.int32</td>
</tr>
<tr>
<td>n_atoms_per_unit_cell</td>
<td>Number of atoms per unit cell (derived from total orbitals).</td>
<td>np.int32</td>
</tr>
<tr>
<td>n_total_orbitals</td>
<td>Total number of orbitals (n_orbitals_per_atom × n_atoms_per_unit_cell).</td>
<td>np.int32</td>
</tr>
<tr>
<td>orbitals_ref</td>
<td>References to OrbitalsState sections representing the orbital basis.</td>
<td>Reference</td>
</tr>
<!-- Wannier -->
<tr>
<td rowspan="4"><strong>Wannier</strong></td>
<td>is_maximally_localized</td>
<td>Flag indicating if orbitals are maximally localized.</td>
<td>bool</td>
</tr>
<tr>
<td>localization_type</td>
<td>Specifies 'maximally_localized' or 'single_shot' projection.</td>
<td>MEnum (options: single_shot, maximally_localized)</td>
</tr>
<tr>
<td>n_bloch_bands</td>
<td>Number of Bloch bands used for projection.</td>
<td>np.int32</td>
</tr>
<tr>
<td>energy_window_outer/inner</td>
<td>Energy window boundaries for orbital projection.</td>
<td>np.float64 (unit: electron_volt)</td>
</tr>
<!-- Slater-Koster -->
<tr>
<td rowspan="4"><strong>Slater-Koster</strong></td>
<td>orbital_1 &amp; orbital_2</td>
<td>References to the two OrbitalsState sections defining a bond.</td>
<td>Reference</td>
</tr>
<tr>
<td>bravais_vector</td>
<td>Lattice vector indicating bond direction (cell indices).</td>
<td>np.int32 array (shape: [3])</td>
</tr>
<tr>
<td>integral_value</td>
<td>Bond integral value.</td>
<td>np.float64</td>
</tr>
<tr>
<td>name</td>
<td>Resolved bond name (e.g., 'sss', 'sps', 'sds').</td>
<td>MEnum (options: sss, sps, sds)</td>
</tr>
<!-- GW -->
<tr>
<td rowspan="4"><strong>GW</strong></td>
<td>type</td>
<td>Specifies the GW cycle type (e.g., G0W0, scGW, etc.).</td>
<td>MEnum (options: G0W0, scGW, scGW0, scG0W, ev-scGW0, ev-scGW, qp-scGW0, qp-scGW)</td>
</tr>
<tr>
<td>analytical_continuation</td>
<td>Method for self-energy continuation.</td>
<td>MEnum (options: pade, contour_deformation, ppm_GodbyNeeds, ppm_HybertsenLouie, ppm_vonderLindenHorsh, ppm_FaridEngel, multi_pole)</td>
</tr>
<tr>
<td>interval_qp_corrections</td>
<td>Band index interval for quasiparticle corrections.</td>
<td>np.int32 array</td>
</tr>
<tr>
<td>screening_ref</td>
<td>Reference to the Screening section for Coulomb interaction parameters.</td>
<td>Reference</td>
</tr>
<!-- BSE -->
<tr>
<td rowspan="3"><strong>BSE</strong></td>
<td>type</td>
<td>BSE Hamiltonian type (Singlet, Triplet, IP, RPA).</td>
<td>MEnum (options: Singlet, Triplet, IP, RPA)</td>
</tr>
<tr>
<td>solver</td>
<td>Diagonalization algorithm used for the BSE Hamiltonian.</td>
<td>MEnum (options: Full-diagonalization, Lanczos-Haydock, GMRES, SLEPc, TDA)</td>
</tr>
<tr>
<td>screening_ref</td>
<td>Reference to the Screening section.</td>
<td>Reference</td>
</tr>
<!-- DMFT -->
<tr>
<td rowspan="7"><strong>DMFT</strong></td>
<td>impurity_solver</td>
<td>Method used for solving the impurity problem.</td>
<td>MEnum (options: CT-INT, CT-HYB, CT-AUX, ED, NRG, MPS, IPT, NCA, OCA, slave_bosons, hubbard_I)</td>
</tr>
<tr>
<td>n_impurities</td>
<td>Number of impurities mapped from the system.</td>
<td>np.int32</td>
</tr>
<tr>
<td>n_orbitals</td>
<td>Number of orbitals per impurity.</td>
<td>np.int32 array</td>
</tr>
<tr>
<td>orbitals_ref</td>
<td>References to OrbitalsState sections relevant for correlated orbitals.</td>
<td>Reference</td>
</tr>
<tr>
<td>n_electrons</td>
<td>Initial number of valence electrons per impurity.</td>
<td>np.float64 array</td>
</tr>
<tr>
<td>inverse_temperature</td>
<td>Inverse temperature (1/(kB*T)).</td>
<td>np.float64</td>
</tr>
<tr>
<td>magnetic_state</td>
<td>Magnetic ordering (paramagnetic, ferromagnetic, antiferromagnetic).</td>
<td>MEnum (options: paramagnetic, ferromagnetic, antiferromagnetic)</td>
</tr>
<!-- ExcitedStateMethodology -->
<tr>
<td rowspan="3"><strong>ExcitedStateMethodology</strong></td>
<td>n_states</td>
<td>Number of states used for excitations.</td>
<td>np.int32</td>
</tr>
<tr>
<td>n_empty_states</td>
<td>Number of empty states considered.</td>
<td>np.int32</td>
</tr>
<tr>
<td>broadening</td>
<td>Lifetime broadening applied to spectra.</td>
<td>np.float64</td>
</tr>
<!-- Photon -->
<tr>
<td rowspan="4"><strong>Photon</strong></td>
<td>multipole_type</td>
<td>Type for multipolar expansion.</td>
<td>MEnum (options: dipolar, quadrupolar, NRIXS, Raman)</td>
</tr>
<tr>
<td>polarization</td>
<td>Photon polarization vector (Cartesian coordinates).</td>
<td>np.float64 array</td>
</tr>
<tr>
<td>energy</td>
<td>Photon energy.</td>
<td>np.float64</td>
</tr>
<tr>
<td>momentum_transfer</td>
<td>Momentum transferred to the lattice (for inelastic scattering).</td>
<td>np.float64 array</td>
</tr>
</table>



## An example `ModelMethod` instance

```
# Create a SelfConsistency instance with SCF convergence criteria
scf_settings = SelfConsistency(
scf_minimization_algorithm="Pulay", # Example algorithm for SCF convergence
n_max_iterations=100, # Maximum number of SCF iterations allowed
threshold_change=1e-5, # Convergence threshold (change between iterations)
threshold_change_unit="hartree" # Unit for the convergence threshold
)

# Create an XCFunctional instance for the TPSSh hybrid functional (10% exact exchange)
tpssh_functional = XCFunctional(
libxc_name="XC_HYB_TPSSh", # Identifier following the libxc naming convention
name="hybrid", # Indicates that this is a hybrid XC functional
weight=0.10 # 10% exact exchange
)

# Instantiate a DFT method using TPSSh and enrich it with the numerical settings
dft_tpssh = DFT(
name="DFT",
type="DFT",
numerical_settings=[scf_settings]
)

# Append the TPSSh XCFunctional to the DFT method
dft_tpssh.xc_functionals.append(tpssh_functional)

```
105 changes: 103 additions & 2 deletions docs/model_system/model_system.md
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the chapters "Hierarchical ModelSystems" and "Normalization Workflow" best, since they give me an idea of what this tool models, how to use it, and what it means to normalize it. I'd actually prefer them more fleshed out.

Conversely, these tables should probably be listed in a reference.md. They can also be automatically generated, e.g. via

plugins:
  - mkdocstrings:
      handlers:
        python:
          options:
            show_docstring_attributes: true
            docstring_section_style: table

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it was clear here that these can be later on.
Wouldn't forget the references, though. Especially given the amount of information in the pydocs that @EBB2675 added.

Original file line number Diff line number Diff line change
@@ -1,4 +1,105 @@
# `ModelSystem`

!!! warning
This page is still under construction.
`ModelSystem` section represents the material system as used for the simulation input. It is defined in [src/nomad_simulations/schema_packages/model_system.py](https://github.com/nomad-coe/nomad-simulations/blob/develop/src/nomad_simulations/schema_packages/model_system.py) and inherits from the `System` base section. A `ModelSystem` may be a single configuration (e.g. a crystal or molecule), or one element in a parent-child tree used for complex systems (e.g., heterostructures or multi-component solutions).


The `ModelSystem` section encodes everything you need to describe the atomic configuration (or hierarchy of configurations) that your simulation acts on. Internally it extends NOMAD’s core `System` and adds:

- **Top-level positions** (`positions`) and **per-atom states** (`particle_states`)
- Support for **hierarchical subsystems** (`sub_systems`) with branching properties
- Helpers to go **to/from** ASE `Atoms` objects
- Automatic **type**, **dimensionality**, **symmetry** and **chemical formula** inference on normalization

---

## Core quantities

| Name | Type | Description |
|----------------------|---------------------------------|---------------------------------------------------------------------------------------------------|
| `name` | `str` | Optional verbose label (e.g. “hBN/G/hBN”). |
| `type` | Enum{`atom`,`bulk`,…} | Automatically determined: atom, molecule/cluster, 1D, surface, 2D, bulk, etc. |
| `dimensionality` | `int` | 0–3, determined by MatID topology‐scaling or ASE connectivity. |
| `is_representative` | `bool` | Only “representative” systems are normalized (default False). |
| `time_step` | `int` | If you store multiple time‐snapshots under `Simulation`, this tags each one. |
| **Cell** | SubSection `Cell` (repeatable) | One or more cell definitions (original/primitive/conventional). |
| **Symmetry** | SubSection `Symmetry` (repeatable) | Space‐group/punkt‐group info + derived “primitive” & “conventional” cells via MatID. |
| **ChemicalFormula** | SubSection `ChemicalFormula` | IUPAC, Hill, reduced, anonymous formats — inferred from ASE Atoms. |
| `positions` | `float[n_particles,3]` | Cartesian coordinates for _all_ atoms (in metres). |
| `n_particles` | `int` | Number of atoms (populated by `from_ase_atoms`, or you can set manually). |
| **particle_states** | SubSection `ParticleState` (→ `AtomsState`) | Per‐atom electronic state info; each entry holds `chemical_symbol`, `atomic_number`, `orbitals_state`, etc. |
| **sub_systems** | recursive SubSection `ModelSystem` | For grouping/branching (e.g. molecule→atoms, heterostructure→components). |
| `branch_label` | `str` | Name for this branch (e.g. “group_H2O”). |
| `branch_depth` | `int` | Depth in the tree (root 0, its children 1, etc.). |
| `particle_indices` | `int[*]` | Indices into parent’s `positions` / `particle_states` for this child. |
| `bond_list` | `int[*][2]` | Optional list of bonded‐atom index pairs. |
| `composition_formula`| `str` | e.g. `H(1)O(2)`, `group_H2O(1)H2O(3)`, etc., auto‐filled by normalization. |
| `total_charge`, `total_spin` | `int` | Overall system charge and spin multiplicity. |

---

## Key methods

```python
model = ModelSystem(is_representative=True)

# add a cell
from nomad_simulations.schema_packages.model_system import AtomicCell
import numpy as np, nomad_units as ureg
cell = AtomicCell(
name='AtomicCell',
lattice_vectors=np.eye(3)*5.43*ureg.angstrom,
periodic_boundary_conditions=[True,True,True]
)
model.cell.append(cell)

# set positions & per-atom states
model.positions = np.array([[0,0,0],[0.25,0.25,0.25]])*ureg.angstrom
from nomad_simulations.schema_packages.atoms_state import AtomsState
model.particle_states.append( AtomsState(chemical_symbol='Si', atomic_number=14) )
model.particle_states.append( AtomsState(chemical_symbol='Si', atomic_number=14) )

# convert to ASE
ase_atoms = model.to_ase_atoms(logger)

# build from an existing ASE.Atoms
model2 = ModelSystem()
model2.cell.append(cell)
model2.from_ase_atoms(ase_atoms, logger)

# normalize: auto‐fills type, dimensionality, symmetry, chemical_formula…
from nomad.datamodel import EntryArchive
archive = EntryArchive()
model.normalize(archive, logger)
```

## Hierarchical ModelSystems

You can nest systems to represent subgroups:

```python
ModelSystem (root)
├── sub_systems[0]: ModelSystem(branch_label="group_H2O", particle_indices=[0,1,2])
│ └── sub_systems[0]: ModelSystem(branch_label="H2O", particle_indices=[0,1,2])
└── sub_systems[1]: ModelSystem(branch_label="Cu")
└── …
```

After normalization, each branch gets a branch_depth, and its composition_formula is set:

```python
root: composition_formula = "group_H2O(1)Cu(1)"
group_H2O: composition_formula = "H2O(3)"
H2O (leaf): composition_formula = "H(1)O(2)"
Cu (leaf): composition_formula = "Cu(1)"
```

### Normalization Workflow

When you call `model.normalize(archive, logger)` on a **representative** ModelSystem, the following happen in order:

1. **to_ase_atoms** ➔ build ASE `Atoms`
2. **resolve_system_type_and_dimensionality** via MatID
3. **Symmetry.normalize** (bulk only) ➔ fills `bravais_lattice`, Wyckoff, appends primitive/conventional cells
4. **ChemicalFormula.normalize** ➔ fills `descriptive`, `reduced`, `iupac`, `hill`, `anonymous`
5. caches `elemental_composition` in `m_cache`
6. sets `type`, `dimensionality`, `composition_formula`, etc.