Source code for magnopy.io._k_resolved

# MAGNOPY - Python package for magnons.
# Copyright (C) 2023-2025 Magnopy Team
#
# e-mail: anry@uv.es, web: magnopy.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.


from typing import Iterable

import matplotlib.pyplot as plt
import numpy as np
from wulfric.geometry import absolute_to_relative

# Save local scope at this moment
old_dir = set(dir())
old_dir.add("old_dir")


[docs] def output_k_resolved( data, data_headers=None, output_filename=None, kpoints=None, relative=False, rcell=None, flat_indices=None, digits=4, scientific_notation=True, ): r""" Outputs any k-resolved data. Parameters ---------- data : (N, M) |array-like|_ Some k-resolved data. N (:math:`\ge 1`) is the amount of kpoints. M is the number of data modes/entries. data_headers : (N, ) list of str, optional Header for the data columns. ``[f"data {i+1} for i in range(len(data))]`` by default. output_filename : str, optional Name of the file for saving the data. If ``None``, then return a list of lines. kpoints : (N, 3) |array-like|_, optional List of the kpoints. ``kpoints[i]`` correspond to the ``data[i]``. relative : bool, default False If ``relative == True``, then given ``kpoints`` are interpreted as relative to the reciprocal unit cell. Otherwise they are interpreted as absolute coordinates. rcell : (3, 3) |array-like|_, optional Reciprocal unit cell. Rows are interpreted as vectors, columns as Cartesian coordinates. flat_indices : (N, 3) |array-like|_, optional kpoints converted to the list of floats according to some rule. Typically used for plotting the band structure. If ``None`` provided, then computed automatically, based on the distance of the given ``kpoints`` (whether absolute or relative). digits : int, default 4 Number of digits after the comma for the data elements. scientific_notation : bool, default True Whether to use scientific notation for the data elements. Returns ------- lines : str Only returned if ``output_filename is None``. Use ``print("\n".join(lines))`` to output the results to the standard output stream. Notes ----- By default kpoints are interpreted as given in absolute coordinates in the reciprocal space. .. doctest:: >>> import magnopy.io as mio >>> omegas = [[1, 2], [1.2, 2.3]] >>> headers = ["mode 1", "mode 2"] >>> kpoints = [[0, 0, 0], [0.1, 0.2, 0.3]] >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_x k_y k_z 0.00000000 1.0000e+00 2.0000e+00 0.00000000 0.00000000 0.00000000 0.37416574 1.2000e+00 2.3000e+00 0.10000000 0.20000000 0.30000000 One can tell it to interpret the kpoints as given in relative coordinates to some reciprocal cell .. doctest:: >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints, relative=True) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_b1 k_b2 k_b3 0.00000000 1.0000e+00 2.0000e+00 0.00000000 0.00000000 0.00000000 0.37416574 1.2000e+00 2.3000e+00 0.10000000 0.20000000 0.30000000 If the reciprocal cell is provided, then the kpoints are converter either way and both are printed .. doctest:: >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints, relative=True, rcell=[[1, 0, 0], [0, 2, 0], [0, 0, 3]]) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_b1 k_b2 k_b3 k_x k_y k_z 0.00000000 1.0000e+00 2.0000e+00 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.37416574 1.2000e+00 2.3000e+00 0.10000000 0.20000000 0.30000000 0.10000000 0.40000000 0.90000000 >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints, relative=False, rcell=[[1, 0, 0], [0, 2, 0], [0, 0, 3]]) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_b1 k_b2 k_b3 k_x k_y k_z 0.00000000 1.0000e+00 2.0000e+00 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.37416574 1.2000e+00 2.3000e+00 0.10000000 0.10000000 0.10000000 0.10000000 0.20000000 0.30000000 One can control the number of digits in data .. doctest:: >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints, digits=6) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_x k_y k_z 0.00000000 1.000000e+00 2.000000e+00 0.00000000 0.00000000 0.00000000 0.37416574 1.200000e+00 2.300000e+00 0.10000000 0.20000000 0.30000000 and whether the scientific notation should be used .. doctest:: >>> lines = mio.output_k_resolved(data=omegas, data_headers=headers, kpoints=kpoints, scientific_notation=False) >>> print("\n".join(lines)) # flat index mode 1 mode 2 k_x k_y k_z 0.00000000 1.0000 2.0000 0.00000000 0.00000000 0.00000000 0.37416574 1.2000 2.3000 0.10000000 0.20000000 0.30000000 """ # Prepare format for the data elements chars = digits + 1 + 1 + 3 if scientific_notation: chars += 1 + 1 + 3 chars = max(chars, 8) # Format string for data elements if scientific_notation: fmt = f">{chars}.{digits}e" else: fmt = f">{chars}.{digits}f" # Format string for kpoints kp_fmt = ">12.8f" # Prepare the header header = ["#"] if flat_indices is not None or kpoints is not None: header.append(f"{'flat index':>12}") header = header + [f"{tmp:>{chars}}" for tmp in data_headers] if kpoints is not None: if rcell is not None: header.append( " ".join([f"{f'k_{comp}':>12}" for comp in ["b1", "b2", "b3"]]) ) header.append(" ".join([f"{f'k_{comp}':>12}" for comp in "xyz"])) elif relative: header.append( " ".join([f"{f'k_{comp}':>12}" for comp in ["b1", "b2", "b3"]]) ) else: header.append(" ".join([f"{f'k_{comp}':>12}" for comp in "xyz"])) header = " ".join(header) # Output header if output_filename is not None: f = open(output_filename, "w") f.write(header + "\n") else: lines = [header] # Output data for each kpoint for i in range(len(data)): line = [" "] if flat_indices is not None: line.append(f"{flat_indices[i]:{kp_fmt}}") elif kpoints is not None: if i == 0: line.append(f"{0.0:{kp_fmt}}") else: line.append( f"{np.linalg.norm(np.array(kpoints[i], dtype=float) - kpoints[i-1]):{kp_fmt}}" ) if isinstance(data[i], Iterable): for entry in data[i]: line.append(f"{entry:{fmt}}") else: line.append(f"{data[i]:{fmt}}") if kpoints is not None: if rcell is not None: if relative: k_vec = kpoints[i] @ np.array(rcell) line.append( " ".join([f"{kpoints[i][comp]:{kp_fmt}}" for comp in range(3)]) ) line.append( " ".join([f"{k_vec[comp]:{kp_fmt}}" for comp in range(3)]) ) else: k_vec_rel = absolute_to_relative(vector=kpoints[i], basis=rcell) line.append( " ".join([f"{k_vec_rel[comp]:{kp_fmt}}" for comp in range(3)]) ) line.append( " ".join([f"{kpoints[i][comp]:{kp_fmt}}" for comp in range(3)]) ) else: line.append( " ".join([f"{kpoints[i][comp]:{kp_fmt}}" for comp in range(3)]) ) line = " ".join(line) if output_filename is not None: f.write(line + "\n") else: lines.append(line) if output_filename is not None: f.close() else: return lines
[docs] def plot_k_resolved(data, kp=None, output_filename=None, ylabel=None): r""" Plot some k-resolved data. If only the ``data`` are given, then an index of the omegas is used for abscissa (x axis). Parameters ---------- data : (N, M) |array-like|_ Some k-resolved data. N (:math:`\ge 1`) is the amount of kpoints. M is the number of data modes/entries. kp : :py:class:`wulfric.Kpoints`, optional. Instance of the :py:class:`wulfric.Kpoints` class. It should be the same instance that were used in the preparation of the ``data``. output_filename : str, optional Name of the file for saving the image. If ``None``, then the graph would be opened in the interactive matplotlib window. ylabel : str, optional Label for the ordinate (y axis). """ data = np.array(data).T fig, ax = plt.subplots() if len(data.shape) == 2: for entry in data: if kp is not None: ax.plot(kp.flatten_points(), entry, lw=1, color="#A47864") else: ax.plot(entry, lw=1, color="#A47864") ax.set_xlim(0, len(entry)) else: if kp is not None: ax.plot(kp.flatten_points(), data, lw=1, color="#A47864") else: ax.plot(data, lw=1, color="#A47864") ax.set_xlim(0, len(data)) if kp is not None: ax.set_xticks(kp.ticks(), kp.labels, fontsize=13) ax.set_xlim(kp.ticks()[0], kp.ticks()[-1]) ax.vlines( kp.ticks(), 0, 1, lw=0.5, color="grey", ls="dashed", zorder=0, transform=ax.get_xaxis_transform(), ) if ylabel is not None: ax.set_ylabel(ylabel, fontsize=15) ax.hlines( 0, 0, 1, lw=0.5, color="grey", linestyle="dashed", transform=ax.get_yaxis_transform(), ) if output_filename is not None: plt.savefig(output_filename, dpi=400, bbox_inches="tight") else: plt.show() plt.close()
# Populate __all__ with objects defined in this file __all__ = list(set(dir()) - old_dir) # Remove all semi-private objects __all__ = [i for i in __all__ if not i.startswith("_")] del old_dir