|
|
# 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"
|
|
|
)
|