aiida_pythonjob_ins.conversions =============================== .. py:module:: aiida_pythonjob_ins.conversions .. autoapi-nested-parse:: Mappings between Euphonic objects and AiiDA's native materials-science types. AiiDA ships domain data types for reciprocal-space data (https://aiida.readthedocs.io/projects/aiida-core/en/stable/topics/data_types.html#materials-science-data-types): * ``KpointsData`` -- k-/q-point positions (+ optional labels + cell). We use it as the q-point *specification* for Fourier interpolation, and as the natural representation of a phonon band path. * ``BandsData`` (a ``KpointsData`` subclass) -- band energies on those points. A Euphonic ``QpointPhononModes`` is essentially ``BandsData`` (frequencies) plus eigenvectors, so we build a ``BandsData`` by *composition* and keep the eigenvectors in our own ``QpointPhononModesData``. Using ``BandsData`` also unlocks existing AiiDA plotting, e.g. ``bands.show_mpl()`` pops up a matplotlib band-structure plot without any AiiDALab dependency. **These are plain converter functions, deliberately NOT calcfunctions.** They are the reusable "verbs" that *produce* an object (an AiiDA node or a euphonic object), called from contexts that each preclude the decorator: inside the workflow's calcfunctions (``generate_band_path``, ``assemble_bands``) and inside Data-class methods (``to_structure``/``to_kpoints``/``to_bands``), which must work without any engine. Several also take/return non-node objects (euphonic ``Crystal``, ``ndarray``), which a calcfunction could not accept. Provenance is recorded one level up, by the ``@calcfunction``/``PythonJob`` wrappers in :mod:`aiida_pythonjob_ins.workflows` that call these helpers. (The reverse direction -- node -> plain Python for aiida-pythonjob *inputs* -- is a deserialization concern and lives in :mod:`aiida_pythonjob_ins.serialization`.) Functions --------- .. autoapisummary:: aiida_pythonjob_ins.conversions.crystal_to_structure aiida_pythonjob_ins.conversions.structure_to_crystal aiida_pythonjob_ins.conversions.structure_to_spglib_cell aiida_pythonjob_ins.conversions.qpoints_to_kpoints_data aiida_pythonjob_ins.conversions.spectrum1d_to_xydata aiida_pythonjob_ins.conversions.spectrum_collection_labels aiida_pythonjob_ins.conversions.spectrum_collection_to_xydata aiida_pythonjob_ins.conversions.xydata_to_spectrum_collection aiida_pythonjob_ins.conversions.modes_to_bands_data Module Contents --------------- .. py:function:: crystal_to_structure(crystal: euphonic.Crystal) -> aiida.orm.StructureData Convert a Euphonic ``Crystal`` to a native AiiDA ``StructureData``. Single source of truth for the Crystal -> StructureData direction (used by ``EuphonicCrystalData`` and by ``CrystalStructureMixin.to_structure``). Uses AiiDA's native API only -- no ASE. Euphonic stores fractional positions; ``StructureData`` wants Cartesian. Masses are carried over for fidelity. .. py:function:: structure_to_crystal(structure: aiida.orm.StructureData) -> euphonic.Crystal Convert a native AiiDA ``StructureData`` to a Euphonic ``Crystal``. The reverse of :func:`crystal_to_structure`. ``Crystal`` requires atom masses, which ``StructureData`` carries on its kinds. .. py:function:: structure_to_spglib_cell(structure: aiida.orm.StructureData) -> tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray] Convert a ``StructureData`` to a spglib/seekpath cell tuple. Returns ``(lattice, positions, numbers)`` by reusing Euphonic's ``Crystal.to_spglib_cell`` (rather than re-deriving the species numbering by hand). .. py:function:: qpoints_to_kpoints_data(qpoints: numpy.ndarray, cell: numpy.ndarray, labels: list[tuple[int, str]] | None = None) -> aiida.orm.KpointsData Build a ``KpointsData`` from fractional q-points, a cell and labels. .. py:function:: spectrum1d_to_xydata(spectrum: Any) -> aiida.orm.XyData Convert a Euphonic ``Spectrum1D`` (e.g. a DOS) to a native ``XyData``. Uses ``get_bin_centres()`` so the x and y arrays have matching lengths (``Spectrum1D`` stores bin *edges* in ``x_data`` when it is histogram-like). Unit labels are recorded on the ``XyData`` arrays. .. py:function:: spectrum_collection_labels(collection: euphonic.Spectrum1DCollection) -> list[str] Derive a concise, human-readable legend label for each line of a collection. Only the ``line_data`` metadata keys that actually differ between lines are used, so a collection already grouped down to one line -- where every key is common rather than varying -- is labelled ``"Total"`` instead of repeating metadata that no longer distinguishes anything. ``atom_symbol``, if it varies, is rendered bare (``"C"``); other varying keys are appended in parentheses (``"C (order 2)"``) in the fixed order atom_symbol/quantum_order/detector_angle, with any other, unrecognised keys appended afterwards in sorted order for determinism. This is deliberately a plotting convenience, not a lossless encoding -- the full metadata travels separately (see :func:`spectrum_collection_to_xydata`). .. py:function:: spectrum_collection_to_xydata(collection: euphonic.Spectrum1DCollection) -> aiida.orm.XyData Convert a Euphonic ``Spectrum1DCollection`` to a native ``XyData``. Like :func:`spectrum1d_to_xydata`, one x array of bin centres is shared by every line; unlike it, there are several y arrays (one per line), and the collection's metadata -- which is what actually distinguishes the lines -- would otherwise be lost. It is preserved on the node as two attributes: * ``spectrum_metadata`` -- the metadata common to the whole collection; * ``spectrum_line_data`` -- the list of per-line metadata dicts. Both are ordinary AiiDA node attributes: JSON-serialisable Python values attached before the node is stored, becoming immutable once it is (matching the provenance guarantee AiiDA gives every stored node). They sit alongside ``XyData``'s own attributes without collision (its arrays are namespaced under an ``array|`` prefix). See :func:`xydata_to_spectrum_collection` for the reverse direction, which is what makes this round trip reversible. Each y array is additionally named with a concise label from :func:`spectrum_collection_labels`, so ``for name, y, unit in xy.get_y()`` is directly plottable without parsing the attached metadata. .. py:function:: xydata_to_spectrum_collection(xy: aiida.orm.XyData) -> euphonic.Spectrum1DCollection Convert a native ``XyData`` back to a Euphonic ``Spectrum1DCollection``. The reverse of :func:`spectrum_collection_to_xydata`: rebuilds the collection's metadata from the ``spectrum_metadata``/``spectrum_line_data`` node attributes, so the recovered collection can be grouped, selected and summed by that metadata exactly as the original could -- which is what lets the grouping step in :mod:`aiida_pythonjob_ins.workflows.tosca` operate on data read back from the graph rather than needing the original Python object. Bin *centres* are recovered, not edges (see the module-level note on :func:`spectrum1d_to_xydata`): the resulting collection is a point spectrum, which is sufficient for grouping, summing and broadening but not for exact rebinning. .. py:function:: modes_to_bands_data(modes: Any, kpoints: aiida.orm.KpointsData | None = None) -> aiida.orm.BandsData Compose a ``BandsData`` from Euphonic ``QpointPhononModes``. ``BandsData`` is a *join*: q-points + cell + high-symmetry labels (from the path) plus frequencies (from ``modes``); neither Euphonic class holds all of it (``QpointPhononModes`` has no labels; ``Spectrum1DCollection`` has no 3-D q-points/eigenvectors). Parameters ---------- modes A Euphonic ``QpointPhononModes`` object (supplies q-points + frequencies). kpoints Optional ``KpointsData`` providing the exact q-point positions *and* high-symmetry labels (e.g. the path used to compute ``modes``). If given, its q-points and cell are **validated** against ``modes`` (a mismatch means path and modes are inconsistent). If omitted, positions come from ``modes`` and labels fall back to Euphonic's automatic tick labels (``QpointPhononModes.get_dispersion().x_tick_labels``).