.. include:: /_substitutions.rst

.. _output-file-page:

=====================
Output file structure
=====================

|LModeAk| writes all results to a single HDF5 output file, ``lmodea.h5``. The file is organised into data groups corresponding to:

- :ref:`ICdata`
- :ref:`NMdata`
- :ref:`LMdata`
- :ref:`WMdata`
- :ref:`CNMdata`
- :ref:`kptdata`

Each dataset is described in detail in the following sections. The overall structure of the output file is:

.. code-block:: text

    /
    ├── internal_coordinates/           Internal coordinate definitions
    │   ├── type                        (n_ic,)                     str
    │   ├── value                       (n_ic,)                     float
    │   ├── sym_key                     (n_ic,)                     int
    │   ├── n_atoms                     (n_ic,)                     int
    │   ├── offsets                     (n_ic + 1,)                 int
    │   ├── atom_indices                (n_tot,)                    int           
    │   ├── asymm_indices               (n_tot,)                    int
    │   ├── cell_indices                (n_tot, 3)                  int
    │   └── derivatives                 (n_tot, 3)                  float
    │
     ├── kpoints/                        Wavevector information
     │   ├── wavevectors                 (n_kpts, 3)                 float
     │   ├── labels                      (n_kpts,)                   str
     │   └── indices_in_dynmat_h5        (n_kpts,)                   int
     │
     ├── normal_modes/ ¹                 Harmonic normal mode data ¹
     │   ├── frequencies                 (n_kpts, n_nm, n_at, 3)     float
     │   └── eigenvectors                (n_kpts, n_nm, n_at, 3)     float
     │
     ├── Wilson_matrices/ ²              Wilson matrices ²
     │   ├── G_diagonal                  (n_kpts, n_ic,)             float
     │   ├── B_matrix                    (n_kpts, n_ic, 3*n_at,)     float
     │   └── D_matrix                    (n_kpts, n_ic, n_nm,)       float
     │
     ├── local_modes/                    Local mode data (LMA)
     │   ├── frequencies                 (n_kpts, n_ic,)             float
     │   ├── force_constants_au          (n_kpts, n_ic,)             float
     │   ├── force_constants             (n_kpts, n_ic,)             float
     │   ├── effective_force_constants   (n_kpts, n_ic,)             float
     │   └── adiabatic_vectors ³         (n_kpts, n_ic, n_at, 3)     float
     │
     └── CNM/ ⁴                          Characterisation of normal modes (CNM) ⁴
         └── amplitudes                  (n_kpts, n_nm, n_ic,)       float
      

    ¹ Written only when ``--write-normal`` is specified.
    ² Written only when ``--write-wilson`` is specified.
    ³ Written only when ``--write-adiabatic`` is specified.
    ⁴ Written only when ``--cnm`` is specified.

Here:

- ``n_at`` is the number of atoms.
- ``n_ic`` is the number of internal coordinates/local modes.
- ``n_nm`` is the number of normal modes.
- ``n_tot`` = :math:`\sum_i N_{\mathrm{atoms}}(i)` is the total number of atom entries across all
  internal coordinates.
- ``n_kpts`` is the number of k-points sampled in the calculation.

:ref:`ICdata` are the same for analysis performed at all wavevectors. The remaining data is ordered according to the :ref:`wavevectors <kptdata>` (``nkpts``) included in the calculation along the *first axis*, and follows the ordering of the :ref:`internal coordinates <ICdata>` (``n_ic``) or the :ref:`normal modes <NMdata>` (``n_nm``) along the *second axis*.

The :ref:`method <cli-bmethod>` used to construct the B matrix for local mode analysis (``primitive`` or ``supercell``) is stored in ``/attrs/b_method``. The stored ``B_matrix`` always corresponds to the one generated using the ``primitive`` method (shape ``(n_ic, 3*n_atoms)``) even if the ``supercell`` method was used for analysis. The ``supercell`` B matrix can be re-constructed directly from the :ref:`derivs` data. The attribute can be read as:

.. code-block:: python

    import h5py

    with h5py.File("lmodea.h5", "r") as f:
        b_method = f.attrs.get("b_method")
        print(b_method)  # e.g. b'primitive' or b'supercell'


.. _ICdata:

Internal coordinate definitions 
-------------------------------

The ``internal_coordinates`` group of the ``lmodea.h5`` file defines the set of internal coordinates used for the reported analysis. These data define the *topology* and
*real-space character* of each internal coordinate and are independent of
wavevector, normal-mode, or local-mode calculations. This contains the scalar quantities: :ref:`type`, :ref:`value`, :ref:`symkey`, :ref:`natoms`, and variable-length data: :ref:`atomidx`, :ref:`assymidx`, :ref:`cellidx`, :ref:`derivs`, organised according to :ref:`offsets`.

The scalar datasets have length ``n_ic`` and contain one entry per internal
coordinate. Because different internal coordinate types involve different numbers of atoms, variable-length data are stored using a **flattened array with offsets** scheme for memory-efficient storage. Offsets are stored in the ``offsets`` array of length ``n_ic + 1``. This representation supports arbitrary internal coordinate definitions without imposing a fixed maximum size.

All variable-length datasets share the same offset structure, allowing an internal coordinate to be reconstructed unambiguously:

.. code-block:: python

    import h5py

    with h5py.File("output.h5", "r") as f:
        ic_data = f["internal_coordinates"]
        offsets = ic_data["offsets"]

        i = 0  # internal coordinate index
        start, end = offsets[i], offsets[i+1]

        atom_indices = ic_data["atom_indices"][start:end]
        asymm_indices = ic_data["asymm_indices"][start:end]
        cell_indices = ic_data["cell_indices"][start:end]
        derivatives = ic_data["derivatives"][start:end]


.. _type:

``type``
********
    String identifying the coordinate type (e.g. ``"bond"``, ``"angle"``,
    ``"ring"``).

.. _value:

``value``
*********
    The equilibrium value of the internal coordinate (e.g. bond length in :math:`\AA`, angle in degrees)

.. _symkey:

``sym_key``
***********
    Integer label identifying symmetry-equivalent internal coordinates. Internal coordinates assigned the same ``sym_key`` are equivalent by space group symmetry of the studied system. :ref:`Special system-wide rigid-body translational coordinates <systrans_ics>` are assigned ``sym_key = 0``.

.. _natoms:

``n_atoms``
***********
    Number of atoms involved in the internal coordinate.

.. _offsets:

``offsets``
***********
    Cumulative offsets into all flattened internal-coordinate arrays (``atom_indices``, ``asymm_indices``, ``cell_indices``, ``derivatives``). For internal coordinate ``i``, the relevant slice is::

        start = offsets[i]
        end   = offsets[i + 1]


.. _atomidx:

``atom_indices``
****************
    Atom indices involved in each internal coordinate in canonical representation. Indices refer to atoms in the reference unit cell.

    
.. _assymidx:

``asymm_indices``
*****************
    Integer labels identifying symmetry-equivalent atoms involved in each internal coordinate.

    The labels are computed using spglib_ and correspond to equivalence under the full space group symmetry. Atoms assigned the same value in ``asymm_indices`` belong to the same symmetry orbit. The value itself corresponds to the index of a representative atom in the reference unit cell.

    These labels **do not** enumerate asymmetric-unit atoms sequentially, but instead reference representative atom indices as defined by spglib.


.. _cellidx:

``cell_indices``
****************
    Cell indices specify which periodic image of the unit cell each atom of the internal coordinate belongs to. They are stored as signed integer triples ``[nx, ny, nz]``, corresponding to integer translations along the lattice vectors relative to the reference cell at ``[0, 0, 0]``.

    The ``j``-th entry of ``cell_indices`` corresponds to the ``j``-th atom in ``atom_indices``.


.. _derivs:

``derivatives``
***************
    Derivatives of each internal coordinate with respect to the Cartesian
    displacements of its constituent atoms.

    Each entry is a vector of length three corresponding to the Cartesian
    components along the lattice axes:

    .. math::

        \frac{\partial q}{\partial \mathbf{r}_\alpha} = 
        \left[ \frac{\partial q}{\partial x_\alpha}, 
               \frac{\partial q}{\partial y_\alpha}, 
               \frac{\partial q}{\partial z_\alpha} \right]

    The derivatives are stored in a flattened array in the same order as
    ``atom_indices`` and ``cell_indices``. The ``j``-th entry of
    ``derivatives`` corresponds to the representation of the ``j``-th atom in ``atom_indices`` and in the unit cell defined by ``cell_indices``.

    These derivatives are used to construct the wavevector-dependent :math:`\mathbf{B}(\mathbf{k})` matrix, which projects Cartesian displacements onto internal coordinates.

    The returned array has shape ``(n_atoms(i), 3)`` and is ordered consistently with ``atom_indices`` and ``cell_indices``.



.. _kptdata:

Wavevector information
----------------------


.. _ktps:

``kpoints``
***********
    Wavevectors sampled in the calculation, expressed in reduced (fractional) coordinates with respect to the reciprocal lattice vectors.

.. _lbls:

``labels``
**********
    String labels associated with high-symmetry k-points. Entries corresponding to non-high-symmetry k-points are stored as empty strings.

.. _kidxdynmat:

``indices_in_dynmat_h5``
************************
    Indices in the input ``dynmat.h5`` file corresponding to each k-point analyzed in the calculation. This dataset records which dynamical matrix was used for each k-point in the ``lmodea.h5`` output, allowing direct correspondence between the analyzed k-points and their source data in ``dynmat.h5``.

    This is distinct from the ``kpoints/indices`` dataset in ``dynmat.h5``, which records the enumeration of the k-points in the output file that produced the phonon data. The ``indices_in_dynmat_h5`` dataset specifically tracks the position of each analyzed k-point within the ``dynmat.h5`` file's own k-point list.



.. _NMdata:

Harmonic normal mode data 
-------------------------


.. _nmodesfreqs:

``frequencies``
***************
    Harmonic normal mode frequencies in cm\ :sup:`--1` at each sampled wavevector.

    The dataset is ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*, and in ascending order along the *second axis*.

.. _nmodeseigenvecs:

``eigenvectors``
****************
    Harmonic normal mode eigenvectors corresponding to the normal mode
    frequencies.

    The dataset is odered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*, :ref:`normal mode <nmodesfreqs>` along the *second axis*, atom index along the *third axis*, and the Cartesian component (x, y, z) along the *fourth axis*.

    .. The dataset is indexed as ``(k, m, \alpha, \alpha)``, where ``k`` labels the
    .. wavevector, ``m`` the normal mode, ``\kappa`` the atom index in the
    .. reference unit cell, and ``\alpha`` the Cartesian component.


.. _WMdata:

Wilson matrices 
---------------

.. _Gdiagblock:

``G_diagonal``
**************
    Diagonal elements of the Wilson G matrix, storing the inertial weighting used in the calculation of local mode frequencies. These values should be the same for all wavevectors.

    The dataset is ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*, and according to :ref:`internal coordinate <ICdata>` ordering along the *second axis*.

.. _Bmatrixblock:

``B_matrix``
************
    Wavevector-dependent :math:`\mathbf{B}(\mathbf{k})` matrix at each wavevector, encoding the Cartesian-internal coordinate transformation obeying the wavevector-imposed phase relations and periodic boundary conditions.

    The dataset is ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*,according to :ref:`internal coordinate <ICdata>` ordering along the *second axis*, and follows the ordering of Cartesian atomic coordinates in the input structure along the *third axis*.

    The stored ``B_matrix`` always corresponds to the one generated using the ``primitive`` :ref:`method <cli-bmethod>` (shape ``(n_ic, 3*n_atoms)``) even if the ``supercell`` :ref:`method <cli-bmethod>` was used for analysis. The ``supercell`` B matrix can be re-constructed directly from the :ref:`derivs` data.

.. _Dmatrixblock:

``D_matrix``
************
    Wavevector-dependent :math:`\mathbf{D}(\mathbf{k})` matrix, encoding the coefficients that express normal coordinates as linear combinations of internal coordinates at each wavevector.

    The dataset is ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*,according to :ref:`internal coordinate <ICdata>` ordering along the *second axis*, and follows the :ref:`normal mode <NMdata>` ordering along the *third axis*.



.. _LMdata:

Local mode data (LMA)
---------------------

The ``local_modes`` group of the ``lmodea.h5`` file stores the local mode properties computed foe each wavevector included in the calculation.

The datasets are ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*, and follow the :ref:`internal coordinate <ICdata>` ordering along the *second axis*.


.. _lmodefreqsblock:

``frequencies``
***************
    Local mode frequencies in cm\ :sup:`--1` computed at each wavevector.

.. _lmodefcausblock:

``force_constants_au``
**********************
    Local mode force constants in atomic units computed at each wavevector.

    The local mode force constant corresponds to the second derivative of the energy with respect to the internal coordinate, i.e. :math:`k_n = \partial^2 E / \partial q_n^2`, evaluated along the adiabatically relaxed displacement path defined by the compliance matrix. Consequently, its unit depends on the dimensionality of the internal coordinate:

    * For bond-length coordinates (:math:`q_n` in Bohr), the unit is Hartree / Bohr\ :sup:`2`.
    * For angular or torsional coordinates (:math:`q_n` in radians, dimensionless), the unit is Hartree.

    In general, the unit is Energy / (internal coordinate)\ :sup:`2`.

.. _lmodefcblock:


``force_constants``
*******************
    Local mode force constants in conventional units computed at each wavevector.

    These are the same quantities as ``force_constants_au`` converted to conventional units. Their unit reflects the dimensionality of the internal coordinate:

    * For bond stretches, the unit is typically mdyn / :math:`\AA` (or equivalently N/m).
    * For angular or torsional coordinates, the unit corresponds to an energy
      (e.g. mdyn :math:`\AA` or an equivalent energy unit).

    In general, the unit is Energy / (internal coordinate)\ :sup:`2` expressed in conventional units.

.. _lmodefceffblock:

``effective_force_constants``
********************************
    Effective local mode force constants in conventional units computed at each wavevector.

    For bond-stretch coordinates, the effective force constant is identical to the local mode force constant and has units of mdyn / :math:`\AA` (≡ N/m).

    For angular and dihedral coordinates, the local mode force constant has units of energy because the internal coordinate (radians) is dimensionless. To allow direct comparison with bond-stretch force constants, an additional geometrical conversion factor is applied that introduces a length\ :sup:`--2` scaling so that it can be expressed in mdyn / :math:`\AA`.

    The effective force constants are therefore unit-normalized quantities intended for comparison across different types of internal coordinates.


.. _lmodevecsblock:

``adiabatic_vectors``
**********************
    Adiabatic vectors describing the atomic displacement patterns as the system relaxes in response to internal coordinate distortions, computed at each wavevector.

    This dataset is only written when the :ref:`--write-adiabatic <cli-write-adiabatic>` flag is specified.


.. _CNMdata:

Characterisation of normal modes (CNM)
--------------------------------------

The ``CNM`` group of the ``lmodea.h5`` file stores the local mode-normal mode similarity metric computed according to the Konkoli-Cremer Hessian-weighted amplitude.

This group is only written when the :ref:`--cnm <cli-cnm>` flag is specified.

The datasets are ordered according to :ref:`wavevector <ktps>` (``kpoints``) along the *first axis*, and follow the :ref:`normal mode <NMdata>` ordering along the *second axis* and :ref:`internal coordinate <ICdata>` ordering along the *third axis*.


.. _cnmamplitudesblock:

``amplitudes``
**************
    Local mode amplitudes for each normal mode at each wavevector. The relative magnitude of these values quantifies the contribution of each internal coordinate to each normal mode, or, equivalently, the participation of each normal mode in the response of the system to the distortion of each internal coordinate.

    These amplitudes are stored **raw** (unnormalised). If a row-normalised representation is required (e.g. for bar-chart visualisation), it can be computed by dividing each row by its sum or by using the ``--normalise-cnm`` option of the plotting utility.





.. _spglib: https://spglib.readthedocs.io/en/stable/
