.. include:: /_substitutions.rst

.. _interfaces-page:

Interfaces
==========

Available interfaces
--------------------

The current version of |LModeAk| supports inputs from the programs listed below. Data necessary for the analysis is read from the input files and written into a standard ``dynmat.h5`` file in HDF5 format. If your preferred program is not listed, see :ref:`dynmat` for a guide to writing this standardised input file from custom input files.

+------------------+---------------------+--------------------------------------------------------------------+
| Program          | ``--program`` value | Supported input                                                    |
+==================+=====================+====================================================================+
| CASTEP (default) | ``castep`` / ``c``  | ``.castep`` output (structure + dynamical matrix)                  |
+------------------+---------------------+--------------------------------------------------------------------+
| CASTEP (.phonon) | ``castep-phonon``   | ``.phonon`` output (structure + frequencies + eigenvectors)        |
+------------------+---------------------+--------------------------------------------------------------------+
| CRYSTAL          | ``crystal`` / ``x`` | ``.out`` output (structure) + ``.DYNMAT/.DAT`` (dynamical matrices)|
+------------------+---------------------+--------------------------------------------------------------------+
| Phonopy          | ``phonopy`` / ``p`` | ``phonopy.yaml`` (structure) + ``qpoints.yaml`` (dynamical matrix) |
+------------------+---------------------+--------------------------------------------------------------------+
| dynmat.h5        | ``dynmat``          | Preformatted ``dynmat.h5`` (structure + dynamical matrix)          |
+------------------+---------------------+--------------------------------------------------------------------+

CASTEP (default)
----------------

When the ``--program`` flag is set to ``castep`` / ``c`` (or unspecified), |LModeAk| reads the main ``.castep`` output file from a CASTEP calculation.

To use a dynamical matrix obtained with CASTEP, |LModeAk| requires that the ``.castep`` output file is generated from a run including the following parameters in the ``.param`` file::

    TASK : Phonon
    PHONON_WRITE_DYNAMICAL : TRUE

If a calculation in which the dynamical matrix output was not requested was already performed and a ``.check`` file was written, re-starting from that checkpoint with ``PHONON_WRITE_DYNAMICAL : TRUE`` will result in only a short post-processing job.


CASTEP (.phonon)
----------------

When the ``--program`` flag is set to ``castep-phonon``, |LModeAk| reads the output of a CASTEP phonon calculation from a ``.phonon`` file, and attempt to re-construct the dynamical matrix from its eigenform (frequencies and eigenvectors). This may result in lower numerical precision than the default method, which reads the dynamical matrix directly from the ``.castep`` file, and is therefore not recommended if a ``.castep`` file written with ``PHONON_WRITE_DYNAMICAL : TRUE`` is available.



CRYSTAL
-------

.. warning::
   This interface is only available if you are using CRYSTAL27 or a later version.

To use dynamical matrices as computed by CRYSTAL, |LModeAk| requires:

- ``*.out`` (output file, containing the structure)
- ``*.DYNMAT/DYNMAT.DAT`` (dynamical matrices)
  
To produce the ``*.DYNAMAT/DYNAMAT.DAT`` unit in CRYSTAL it is necessary to insert the ``DYNMATPRT`` keyword in the ``FREQCALC`` input block, please refer to the `CRYSTAL Documentation <https://www.crystal.unito.it/include/manuals/crystal23.pdf>`_::

    [...]
    FREQCALC
    [...]
    DISPERSI
    DYNMATPRT
    END
    [...]




Phonopy
-------

To use a dynamical matrix obtained with Phonopy, |LModeAk| requires:

- ``phonopy.yaml`` (structure)
- ``qpoints.yaml`` (dynamical matrix)

The preferred way to produce these files is to include the following lines in the ``phonopy.conf`` file::

    WRITEDM = .TRUE.
    QPOINTS = .TRUE.

To calculate the dynamical matrices, Phonopy reads the q-point list from a ``QPOINTS`` file in the current directory. The first line is the number of q-points, followed by reduced-coordinate q-points, e.g.::

    182
    0.000000 0.000000 0.000000
    0.045455 0.000000 0.000000
    0.090909 0.000000 0.000000
    0.136364 0.000000 0.000000
    ...

A suitable q-point path can be generated using the ``seekpath`` utility.


.. _dynmat:

dynmat.h5
---------

If you already have ``dynmat.h5`` in |LModeAk| format, use ``--program dynmat`` to skip parsing external formats.

Expected structure (datasets and units)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* ``crystal/numbers`` – (n_atoms,) int; atomic numbers.
* ``crystal/cell`` – (3, 3) float64; lattice vectors in ångström.
* ``crystal/positions`` – (n_atoms, 3) float64; fractional coords
* ``kpoints/wavevectors`` – (n_kpts, 3) float64; reduced coordinates.
* ``kpoints/labels`` – (n_kpts,) bytes; optional labels (e.g., ``b'GAMMA'``).
* ``kpoints/directions`` – (n_kpts, 3) float64; optional line directions (NaN if unused).
* ``dynamical_matrices`` – (n_kpts, 3*n_atoms, 3*n_atoms) complex128; dynamical matrices in Hartree/bohr².
* ``phase_convention`` – scalar bytes; ``b'atom'`` or ``b'cell'`` (controls phase conversion).

Example Python recipe to build ``dynmat.h5``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

    import h5py, numpy as np
    
    # Crystal data
    atomic_numbers = np.array([8, 12], dtype=np.int64)
    n_atoms = len(atomic_numbers)
    lattice_vectors = np.array([[2.1058552, 2.1058552, 0.0000000],
                                [2.1058552, 0.0000000, 2.1058552],
                                [0.0000000, 2.1058552, 2.1058552]], dtype=float)  # Å
    fractional_coords = np.array([[0.500000, 0.500000, 0.500000],
                                  [0.000000, 0.000000, 0.000000]], dtype=float) # fractional
    # k-points
    wavevectors = np.array([[0.000000, 0.000000, 0.000000],
                            [0.250000, 0.000000, 0.250000]
                            [0.500000, 0.000000, 0.500000]], dtype=float)
    n_kpts = wavevectors.shape[0]
    labels = np.array([b"GAMMA", b"", b"X"], dtype="S10")
    directions = np.full((n_kpts, 3), np.nan)

    # Dynamical matrices (Hartree/bohr^2) -- replace with pre-computed dynamical matrices
    dynmats = np.zeros((n_kpts, 3*n_atoms, 3*n_atoms), dtype=np.complex128)

    with h5py.File("dynmat.h5", "w") as f:
        
        f.create_dataset("crystal/numbers", data=atomic_numbers)
        f.create_dataset("crystal/cell", data=lattice_vectors)
        f.create_dataset("crystal/positions", data=fractional_coords)
        
        f.create_dataset("kpoints/wavevectors", data=wavevectors)
        f.create_dataset("kpoints/labels", data=labels)
        f.create_dataset("kpoints/directions", data=directions)
        
        f.create_dataset("dynamical_matrices", data=dynmats)
        # Phase convention: b'cell' (e.g. CASTEP) or b'atom' (e.g. Phonopy)
        f.create_dataset("phase_convention", data=np.string_("cell"))

Notes
^^^^^
- Units: dynamical matrices must be in Hartree/bohr²; convert before writing.
- Shapes: ``dynamical_matrices`` is full (not Hermitian-packed) and ordered as (k, αi, βj) with 3N DOF.
- Labels/directions are optional; use empty strings/NaNs if not available.
