You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

476 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Copyright (c) [2024] []
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Docstring in io.
"""
from os import environ
from pickle import dump, load
from typing import Final
import numpy as np
from sisl.io import fdfSileSiesta
from grogupy.constants import ACCEPTED_INPUTS
def read_grogupy_fdf(path: str) -> tuple[dict, list, list]:
"""It reads the simulation parameters, magnetic entities and pairs from the fdf.
Args:
path : string
The path to the .fdf file
Returns:
fdf_arguments : dict
The read input arguments from the fdf file
magnetic_entities : list
It contains the dictionaries associated with the magnetic entities
pairs : dict
It contains the dictionaries associated with the pair information
"""
# read fdf file
fdf = fdfSileSiesta(path)
fdf_arguments = dict()
InputFile = fdf.get("InputFile")
if InputFile is not None:
fdf_arguments["infile"] = InputFile
OutputFile = fdf.get("OutputFile")
if OutputFile is not None:
fdf_arguments["outfile"] = OutputFile
ScfXcfOrientation = fdf.get("ScfXcfOrientation")
if ScfXcfOrientation is not None:
fdf_arguments["scf_xcf_orientation"] = np.array(
ScfXcfOrientation.split()[:3], dtype=float
)
XCF_Rotation = fdf.get("XCF_Rotation")
if XCF_Rotation is not None:
rotations = []
# iterate over rows
for rot in XCF_Rotation:
# convert row to dictionary
dat = np.array(rot.split()[:9], dtype=float)
o = dat[:3]
vw = dat[3:].reshape(2, 3)
rotations.append(dict(o=o, vw=vw))
fdf_arguments["ref_xcf_orientations"] = rotations
Kset = fdf.get("INTEGRAL.Kset")
if Kset is not None:
fdf_arguments["kset"] = int(Kset)
Kdirs = fdf.get("INTEGRAL.Kdirs")
if Kdirs is not None:
fdf_arguments["kdirs"] = Kdirs
# This is permitted because it means automatic Ebot definition
ebot = fdf.get("INTEGRAL.Ebot")
try:
fdf_arguments["ebot"] = float(ebot)
except:
fdf_arguments["ebot"] = None
Eset = fdf.get("INTEGRAL.Eset")
if Eset is not None:
fdf_arguments["eset"] = int(Eset)
Esetp = fdf.get("INTEGRAL.Esetp")
if Esetp is not None:
fdf_arguments["esetp"] = float(Esetp)
ParallelSolver = fdf.get("GREEN.ParallelSolver")
if ParallelSolver is not None:
fdf_arguments["parallel_solver_for_Gk"] = bool(ParallelSolver)
PadawanMode = fdf.get("PadawanMode")
if PadawanMode is not None:
fdf_arguments["padawan_mode"] = bool(PadawanMode)
Pairs = fdf.get("Pairs")
if Pairs is not None:
pairs = []
# iterate over rows
for fdf_pair in Pairs:
# convert data
dat = np.array(fdf_pair.split()[:5], dtype=int)
# create pair dictionary
my_pair = dict(ai=dat[0], aj=dat[1], Ruc=np.array(dat[2:]))
pairs.append(my_pair)
MagneticEntities = fdf.get("MagneticEntities")
if MagneticEntities is not None:
magnetic_entities = []
# iterate over magnetic entities
for mag_ent in MagneticEntities:
# drop comments from data
row = mag_ent.split()
dat = []
for string in row:
if string.find("#") != -1:
break
dat.append(string)
# cluster input
if dat[0] in {"Cluster", "cluster"}:
magnetic_entities.append(dict(atom=[int(_) for _ in dat[1:]]))
continue
# atom input, same as cluster, but raises
# error when multiple atoms are given
if dat[0] in {"Atom", "atom"}:
if len(dat) > 2:
raise Exception("Atom input must be a single integer")
magnetic_entities.append(dict(atom=int(dat[1])))
continue
# atom and shell information
elif dat[0] in {"AtomShell", "Atomshell", "atomShell", "atomshell"}:
magnetic_entities.append(
dict(atom=int(dat[1]), l=[int(_) for _ in dat[2:]])
)
continue
# atom and orbital information
elif dat[0] in {"AtomOrbital", "Atomorbital", "tomOrbital", "atomorbital"}:
magnetic_entities.append(
dict(atom=int(dat[1]), orb=[int(_) for _ in dat[2:]])
)
continue
# orbital information
elif dat[0] in {"Orbitals", "orbitals"}:
magnetic_entities.append(dict(orb=[int(_) for _ in dat[1:]]))
continue
else:
raise Exception("Unrecognizable magnetic entity in .fdf!")
return fdf_arguments, magnetic_entities, pairs
def process_input_args(
DEFAULT_ARGUMENTS: dict,
fdf_arguments: dict,
command_line_arguments: dict,
accepted_inputs: dict = ACCEPTED_INPUTS,
) -> dict:
"""It returns the final simulation parameters based on the inputs.
The merging is done in the order of priority:
1. command line arguments
2. fdf arguments
3. default arguments
Args:
default_arguments : dict
Default arguments from grogupy
fdf_arguments : dict
Arguments read from the fdf input file
command_line_arguments : dict
Arguments from the command line
Returns:
dict
The final simulation parameters
"""
# copy input so it does not get changed
default_arguments = DEFAULT_ARGUMENTS.copy()
# iterate over fdf_arguments and update default arguments
for key, value in fdf_arguments.items():
if value is not None and key in accepted_inputs:
default_arguments[key] = value
# iterate over command_line_arguments and update default arguments
for key, value in command_line_arguments.items():
if value is not None and key in accepted_inputs:
default_arguments[key] = value
return default_arguments
def save_pickle(outfile: str, data: dict) -> None:
"""Saves the data in the outfile with pickle.
Args:
outfile : str
Path to outfile
data : dict
Contains the data
"""
# save dictionary
with open(outfile, "wb") as output_file:
dump(data, output_file)
def load_pickle(infile: str) -> dict:
"""Loads the data from the infile with pickle.
Args:
infile : str
Path to infile
Returns:
data : dict
A dictionary of data
"""
# open and read file
with open(infile, "rb") as input_file:
data = load(input_file)
return data
def print_job_description(simulation_parameters: dict) -> None:
"""It prints the parameters and the description of the job.
Args:
simulation_parameters : dict
It contains the simulations parameters
"""
print(
"================================================================================================================================================================"
)
try:
print(f"SLURM job ID: {environ["SLURM_JOB_ID"]}")
except:
print("SLURM job ID not found.")
print("Input file: ")
print(simulation_parameters["infile"])
print("Output file: ")
print(simulation_parameters["outfile"])
print(
"Number of nodes in the parallel cluster: ",
simulation_parameters["parallel_size"],
)
if simulation_parameters["parallel_solver_for_Gk"]:
print("solver used for Greens function calculation: parallel")
else:
print("solver used for Greens function calculation: sequential")
print(
"================================================================================================================================================================"
)
print("Cell [Ang]: ")
print(simulation_parameters["cell"])
print(
"================================================================================================================================================================"
)
print("DFT axis: ")
print(simulation_parameters["scf_xcf_orientation"])
print("Quantization axis and perpendicular rotation directions:")
for ref in simulation_parameters["ref_xcf_orientations"]:
print(ref["o"], " --» ", ref["vw"])
print(
"================================================================================================================================================================"
)
print("Parameters for the contour integral:")
print("Number of k points: ", simulation_parameters["kset"])
print("k point directions: ", simulation_parameters["kdirs"])
if simulation_parameters["automatic_ebot"]:
print(
"Ebot: ",
simulation_parameters["ebot"],
" WARNING: This was automatically determined!",
)
else:
print("Ebot: ", simulation_parameters["ebot"])
print("Eset: ", simulation_parameters["eset"])
print("Esetp: ", simulation_parameters["esetp"])
print(
"================================================================================================================================================================"
)
def print_parameters(simulation_parameters: dict) -> None:
"""It prints the simulation parameters for the grogu out.
Args:
simulation_parameters : dict
It contains the simulations parameters
"""
print(
"================================================================================================================================================================"
)
try:
print(f"SLURM job ID: {environ["SLURM_JOB_ID"]}")
except:
print("SLURM job ID not found.")
print("Input file: ")
print(simulation_parameters["infile"])
print("Output file: ")
print(simulation_parameters["outfile"])
print(
"Number of nodes in the parallel cluster: ",
simulation_parameters["parallel_size"],
)
print(
"================================================================================================================================================================"
)
print("Cell [Ang]: ")
print(simulation_parameters["cell"])
print(
"================================================================================================================================================================"
)
print("DFT axis: ")
print(simulation_parameters["scf_xcf_orientation"])
print("Quantization axis and perpendicular rotation directions:")
for ref in simulation_parameters["ref_xcf_orientations"]:
print(ref["o"], " --» ", ref["vw"])
print(
"================================================================================================================================================================"
)
print("Parameters for the contour integral:")
print("Number of k points: ", simulation_parameters["kset"])
print("k point directions: ", simulation_parameters["kdirs"])
print("Ebot: ", simulation_parameters["ebot"])
print("Eset: ", simulation_parameters["eset"])
print("Esetp: ", simulation_parameters["esetp"])
print(
"================================================================================================================================================================"
)
def print_atoms_and_pairs(magnetic_entities: list, pairs: list) -> None:
"""It prints the pair and magnetic entity information for the grogu out.
Args:
magnetic_entities : dict
It contains the data on the magnetic entities
pairs : dict
It contains the data on the pairs
"""
print("Atomic information: ")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
print(
"[atom index]Element(orbitals) x [Ang] y [Ang] z [Ang] Sx Sy Sz Q Lx Ly Lz Jx Jy Jz"
)
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
# iterate over magnetic entities
for mag_ent in magnetic_entities:
# iterate over atoms
for tag, xyz in zip(mag_ent["tags"], mag_ent["xyz"]):
# coordinates and tag
print(f"{tag} {xyz[0]} {xyz[1]} {xyz[2]}")
print("")
print(
"================================================================================================================================================================"
)
print("Anisotropy [meV]")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
print("Magnetic entity x [Ang] y [Ang] z [Ang]")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
# iterate over magnetic entities
for mag_ent in magnetic_entities:
# iterate over atoms
for tag, xyz in zip(mag_ent["tags"], mag_ent["xyz"]):
# coordinates and tag
print(f"{tag} {xyz[0]} {xyz[1]} {xyz[2]}")
print("Consistency check: ", mag_ent["K_consistency"])
print("K: # Kxx, Kxy, Kxz, Kyx, Kyy, Kyz, Kzx, Kzy, Kzz")
print(mag_ent["K"])
print("")
print(
"================================================================================================================================================================"
)
print("Exchange [meV]")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
print("Magnetic entity1 Magnetic entity2 [i j k] d [Ang]")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
# iterate over pairs
for pair in pairs:
# print pair parameters
print(
f"{pair['tags'][0]} {pair['tags'][1]} {pair['Ruc']} d [Ang] {pair['dist']}"
)
# print magnetic parameters
print("Isotropic: ", pair["J_iso"], " # Tr[J] / 3")
print("")
print("DMI: ", pair["D"], " # Dx, Dy, Dz")
print("")
print(
"Symmetric-anisotropy: ",
pair["J_S"],
" # J_S = J - J_iso * I > Jxx, Jyy, Jxy, Jxz, Jyz",
)
print("")
print("J: # Jxx, Jxy, Jxz, Jyx, Jyy, Jyz, Jzx, Jzy, Jzz")
print(pair["J"])
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
print(
"================================================================================================================================================================"
)
def print_runtime_information(times: dict) -> None:
"""It prints the runtime information for the grogu out.
Args:
times : dict
It contains the runtime data
"""
print("Runtime information: ")
print(f"Total runtime: {times['end_time'] - times['start_time']} s")
print(
"----------------------------------------------------------------------------------------------------------------------------------------------------------------"
)
print(f"Initial setup: {times['setup_time'] - times['start_time']} s")
print(
f"Hamiltonian conversion and XC field extraction: {times['H_and_XCF_time'] - times['setup_time']:.3f} s"
)
print(
f"Pair and site datastructures creations: {times['site_and_pair_dictionaries_time'] - times['H_and_XCF_time']:.3f} s"
)
print(
f"k set creation and distribution: {times['k_set_time'] - times['site_and_pair_dictionaries_time']:.3f} s"
)
print(
f"Rotating XC potential: {times['reference_rotations_time'] - times['k_set_time']:.3f} s"
)
print(
f"Greens function inversion: {times['green_function_inversion_time'] - times['reference_rotations_time']:.3f} s"
)
print(
f"Calculate energies and magnetic components: {times['end_time'] - times['green_function_inversion_time']:.3f} s"
)