From 829b4ada2c79a153b05c1e1cae14c270f8780864 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 31 Oct 2022 17:06:12 -0700 Subject: [PATCH 01/18] Cmagspin bug fix --- casm/casm/vasp/io/attribute_classes.py | 139 +++++++++--------- casm/casm/vaspwrapper/vasp_calculator_base.py | 74 +++++----- 2 files changed, 105 insertions(+), 108 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index a31f250..23f887b 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,29 +1,24 @@ -class DofClassError(Exception): - """Exception handling""" - def __init__(self, message): - self.message = message - - def __str__(self): - """ Writes out the error message - - Returns - ------- - string - - """ - return self.message +import math class CmagspinAttr: """Class containing information specific to Cmagspin dof. - This object will be constructed from casm.project.structure.StructureInfo class - which digests its information. + This object will be constructed from casm.project.structure.StructureInfo class + which digests its information. + + self.atom_props: List[Dict] - Contains the list of atom properites (with atom name and it's value) + Look at the example below for detailed description of object properties + + Consider the example of NaFeO2 with Fe having a +5 magnetic moment and rest all being 0 + self.atom_props: + [ + {"site_index":0, "atom": "Na", "value":0}, + {"site_index":1, "atom":"Fe", "value":5}, + {"site_index":2, "atom":"O","value":0}, + {"site_index":3, "atom":"O","value":0} + ] + """ - self.atom_props: List[Dict] - Contains the list of atom properites (with atom name and it's value) - Look at the example below for detailed description of object properties - - Consider the example of NaFeO2 with Fe having a +5 magnetic moment and rest all being 0 - self.atom_props: [{"site_index":0, "atom": "Na", "value":0},{"site_index":1,"atom":"Fe", "value":5},{"site_index":2, "atom":"O","value":0},{"site_index":3, "atom":"O","value":0}]""" def __init__(self, structure_info): """Constructs the CmagspinAttr object from StructureInfo object @@ -32,75 +27,83 @@ def __init__(self, structure_info): structure_info : casm.project.structure.StructureInfo """ - try: - self.atom_props = [{ - "site_index": - x, - "atom": - structure_info.atom_type[x], - "value": - structure_info.atom_properties["Cmagspin"]["value"][x] - } for x in range(0, len(structure_info.atom_type))] - except: - raise DofClassError( - "Could not construct CmagspinAttr class!! Check if you're dealing with Cmagspin dof calculations" - ) + if "Cmagspin" not in list(structure_info.atom_properties.keys()): + raise RuntimeError( + "Could not construct CmagspinAttr class. " + "Check if you're dealing with Cmagspin dof calculations.") + self.atom_props = [{ + "site_index": site_index, + "atom": atom_type, + "value": magmom_value + } for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["Cmagspin"]["value"], + ))] def vasp_input_tags(self, sort=True): - """Returns a dictionary of VASP input tags specific to collinear magnetic spin calculations. - The collinear magnetic spin specific tags are as follows: - - MAGMOM, ISPIN + """Returns a dictionary of MAGMOM, ISPIN input tags + specific to collinear magnetic VASP calculations. Parameters ---------- - sort: bool (This should match the sort used to write POSCAR file (whether the basis atoms are sorted)) + sort: bool, optional + This should match the sort used to write + POSCAR file (whether the basis atoms are sorted)) Returns ------- - dict{"vasp_input_tag": "value"} + dict + { + "MAGMOM": magmom_string, + "ISPIN": 2 + } """ - #TODO: Group together atoms of same MAGMOM together - #TODO: Also add ISPIN default tag which is required if missed in INCAR.base - if sort is True: self.atom_props.sort(key=lambda x: x["atom"]) magmom_value = "" - for atom_props in self.atom_props: - magmom_value = magmom_value + str(atom_props["value"][0]) + " " - - tags = dict() - tags["MAGMOM"] = magmom_value - return tags - - def vasp_output_dictionary(self, outcar, sort=True): - """Returns the attribute specific vasp output dictionary which can be updated - to the whole output dictionary which will be printed as properties.calc.json. - + for i, atom_props in enumerate(self.atom_props): + if i == 0: + number_of_same_magmoms = 1 + elif math.isclose(atom_props["value"][0], + self.atom_props[i - 1]["value"][0]): + number_of_same_magmoms += 1 + else: + magmom_value += (str(number_of_same_magmoms) + "*" + + str(self.atom_props[i - 1]["value"][0]) + " ") + number_of_same_magmoms = 1 + + return dict(MAGMOM=magmom_value, ISPIN=2) + + @staticmethod + def vasp_output_dictionary(outcar, unsort_dict): + """Returns the attribute specific vasp output + dictionary which can be updated to the whole output + dictionary which will be printed as properties.calc.json. For Cmagspin, this will be magnetic moment of each individual species Parameters ---------- - outcar : casm.vasp.io.outcar (Class containing information about magmom of individual species from OUTCAR) - sort : bool (This should be the same sort used while writing POSCAR) + outcar : casm.vasp.io.outcar + Outcar containing magmom information + unsort_dict : dict + ``Poscar.unsort_dict()`` useful for reordering + the magmom values Returns ------- - dict{"Cmagspin":{"value":[list]}} + dict + { + "Cmagspin":{ + "value":[] + } + } """ - if sort is True: - self.atom_props.sort(key=lambda x: x["atom"]) - - permutation_vector_to_unsort = [ - x["site_index"] for x in self.atom_props - ] - - output = {} - output["Cmagspin"] = {} - output["Cmagspin"]["value"] = [[mag] for site_index, mag in sorted( - zip(permutation_vector_to_unsort, outcar.mag))] + output = dict(Cmagspin={}) + for i in range(len(outcar.mag)): + output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] return output diff --git a/casm/casm/vaspwrapper/vasp_calculator_base.py b/casm/casm/vaspwrapper/vasp_calculator_base.py index b2e8699..a76c744 100644 --- a/casm/casm/vaspwrapper/vasp_calculator_base.py +++ b/casm/casm/vaspwrapper/vasp_calculator_base.py @@ -28,6 +28,7 @@ def error_job(message): print(str(e)) sys.stdout.flush() + def complete_job(jobid=None): """Complete job by given ID, or detect ID from environment""" import prisms_jobs as jobs @@ -38,6 +39,7 @@ def complete_job(jobid=None): print(str(e)) sys.stdout.flush() + class VaspCalculatorBase(object): """ Base class containing all the basic functions that method classes can inherit @@ -52,6 +54,7 @@ class VaspCalculatorBase(object): sort : bool """ + def __init__(self, selection, calctype=None, auto=True, sort=True): """set up attributes for the base class""" self.selection = selection @@ -411,18 +414,18 @@ def submit(self): sys.stdout.flush() # construct a Job job = jobs.Job(name=jobname(config_data["name"]), - account=settings["account"], - nodes=nodes, - ppn=ppn, - walltime=settings["walltime"], - pmem=settings["pmem"], - qos=settings["qos"], - queue=settings["queue"], - message=settings["message"], - email=settings["email"], - priority=settings["priority"], - command=cmd, - auto=self.auto) + account=settings["account"], + nodes=nodes, + ppn=ppn, + walltime=settings["walltime"], + pmem=settings["pmem"], + qos=settings["qos"], + queue=settings["queue"], + message=settings["message"], + email=settings["email"], + priority=settings["priority"], + command=cmd, + auto=self.auto) print("Submitting") sys.stdout.flush() @@ -724,46 +727,37 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): output["global_properties"]["energy"]["value"] = zcar.E[-1] if structure_info.atom_properties is not None: + # if you have cmagspin sitedofs if "Cmagspin" in list(structure_info.atom_properties.keys()): - output["global_properties"]["Cmagspin"] = {} - cmagspin_specific_output = attribute_classes.CmagspinAttr( - structure_info).vasp_output_dictionary(ocar) + cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict) output["atom_properties"].update(cmagspin_specific_output) - #TODO: Need a better way to write global magmom. I don't like what I did here + + output["global_properties"]["Cmagspin"] = {} output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] - #TODO: When you don't have Cmagspin but have magnetic calculations. This part can be removed if you runall magnetic calculations as Cmagspin calculations. - #TODO: Need a better way of doing this. Some code duplication here. + # if you don't have cmagspin but have other sitedofs with ispin 2 else: if ocar.ispin == 2: output["global_properties"]["Cmagspin"] = {} - output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] + output["global_properties"]["Cmagspin"][ + "value"] = zcar.mag[-1] + if ocar.lorbit in [1, 2, 11, 12]: - output["atom_properties"]["Cmagspin"] = {} - output["atom_properties"]["Cmagspin"]["value"] = [ - None for i in range(len(contcar.basis)) - ] - - for i, v in enumerate(contcar.basis): - output["atom_properties"]["Cmagspin"]["value"][ - unsort_dict[i]] = [ - noindent.NoIndent(ocar.mag[i]) - ] - - #TODO: Code duplication here. If you have a magnetic calculation without dofs, you still need to write magmom values. This can be removed if you run all the magnetic calculations as Cmagspin dof calculations. - #TODO: If you still want to have this particular functionality, wrap it up in a helper function to avoid code duplication. + cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict) + output["atom_properties"].update( + cmagspin_specific_output) + + # if you don't have any sitedofs else: if ocar.ispin == 2: output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] if ocar.lorbit in [1, 2, 11, 12]: - output["atom_properties"]["Cmagspin"] = {} - output["atom_properties"]["Cmagspin"]["value"] = [ - None for i in range(len(contcar.basis)) - ] - - for i, v in enumerate(contcar.basis): - output["atom_properties"]["Cmagspin"]["value"][ - unsort_dict[i]] = [noindent.NoIndent(ocar.mag[i])] + cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict) + output["atom_properties"].update(cmagspin_specific_output) + return output From b06354bc7a24fa585afd34d77279cb4121ac6980 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 31 Oct 2022 17:22:09 -0700 Subject: [PATCH 02/18] Cunitmagspin support --- casm/casm/vasp/io/attribute_classes.py | 46 ++- casm/casm/vasp/io/incar.py | 311 ++++++++++++------ casm/casm/vaspwrapper/vasp_calculator_base.py | 16 +- 3 files changed, 244 insertions(+), 129 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 23f887b..873f2f1 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -27,19 +27,24 @@ def __init__(self, structure_info): structure_info : casm.project.structure.StructureInfo """ - if "Cmagspin" not in list(structure_info.atom_properties.keys()): + if "Cmagspin" in list(structure_info.atom_properties.keys()): + self.magspin = "Cmagspin" + elif "Cunitmagspin" in list(structure_info.atom_properties.keys()): + self.magspin = "Cunitmagspin" + else: raise RuntimeError( "Could not construct CmagspinAttr class. " - "Check if you're dealing with Cmagspin dof calculations.") - self.atom_props = [{ - "site_index": site_index, - "atom": atom_type, - "value": magmom_value - } for site_index, (atom_type, magmom_value) in enumerate( - zip( - structure_info.atom_type, - structure_info.atom_properties["Cmagspin"]["value"], - ))] + "Check if you're dealing with Cmagspin dof calculations." + ) + self.atom_props = [ + {"site_index": site_index, "atom": atom_type, "value": magmom_value} + for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties[self.magspin]["value"], + ) + ) + ] def vasp_input_tags(self, sort=True): """Returns a dictionary of MAGMOM, ISPIN input tags @@ -67,18 +72,23 @@ def vasp_input_tags(self, sort=True): for i, atom_props in enumerate(self.atom_props): if i == 0: number_of_same_magmoms = 1 - elif math.isclose(atom_props["value"][0], - self.atom_props[i - 1]["value"][0]): + elif math.isclose( + atom_props["value"][0], self.atom_props[i - 1]["value"][0] + ): number_of_same_magmoms += 1 else: - magmom_value += (str(number_of_same_magmoms) + "*" + - str(self.atom_props[i - 1]["value"][0]) + " ") + magmom_value += ( + str(number_of_same_magmoms) + + "*" + + str(self.atom_props[i - 1]["value"][0]) + + " " + ) number_of_same_magmoms = 1 return dict(MAGMOM=magmom_value, ISPIN=2) @staticmethod - def vasp_output_dictionary(outcar, unsort_dict): + def vasp_output_dictionary(outcar, unsort_dict, magspin_type="Cmagspin"): """Returns the attribute specific vasp output dictionary which can be updated to the whole output dictionary which will be printed as properties.calc.json. @@ -102,8 +112,8 @@ def vasp_output_dictionary(outcar, unsort_dict): } """ - output = dict(Cmagspin={}) + output = {magspin_type: {}} for i in range(len(outcar.mag)): - output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] + output[magspin_type]["value"][unsort_dict[i]] = [outcar.mag[i]] return output diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index 5e3bab3..834d765 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -1,5 +1,4 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) +from __future__ import absolute_import, division, print_function, unicode_literals from builtins import * import re @@ -8,37 +7,100 @@ # List of tags in VASP sorted by the data type associated with it VASP_TAG_INT_LIST = [ - 'ialgo', 'ibrion', 'icharg', 'images', 'ismear', 'ispin', 'istart', 'isym', - 'lorbit', 'nbands', 'ndav', 'ngx', 'ngxf', 'ngy', 'ngyf', 'ngz', 'ngzf', - 'npar', 'ncore', 'spind', 'nsw', 'isif', 'kpar', 'voskown', 'nsim', - 'nedos', 'lmaxfock', 'lmaxmix', 'nkred', 'ivdw', 'nelmin', 'nelm', - 'nelmdl', 'ldautype', 'ldauprint', 'ldauprint', 'ichain' + "ialgo", + "ibrion", + "icharg", + "images", + "ismear", + "ispin", + "istart", + "isym", + "lorbit", + "nbands", + "ndav", + "ngx", + "ngxf", + "ngy", + "ngyf", + "ngz", + "ngzf", + "npar", + "ncore", + "spind", + "nsw", + "isif", + "kpar", + "voskown", + "nsim", + "nedos", + "lmaxfock", + "lmaxmix", + "nkred", + "ivdw", + "nelmin", + "nelm", + "nelmdl", + "ldautype", + "ldauprint", + "ldauprint", + "ichain", ] VASP_TAG_FLOAT_LIST = [ - 'ediff', 'ediffg', 'emax', 'emin', 'encut', 'potim', 'sigma', 'enmax', - 'symprec', 'time', 'hfscreen', 'amix', 'bmix', 'amix_mag', 'bmix_mag', - 'spring' + "ediff", + "ediffg", + "emax", + "emin", + "encut", + "potim", + "sigma", + "enmax", + "symprec", + "time", + "hfscreen", + "amix", + "bmix", + "amix_mag", + "bmix_mag", + "spring", ] VASP_TAG_BOOL_LIST = [ - 'lcharg', 'lsorbit', 'lwave', 'lscalapack', 'lscalu', 'lplane', 'lhfcalc', - 'shiftred', 'evenonly', 'oddonly', 'addgrid', 'ldau', 'lasph', 'lclimb', - 'ldneb', 'lnebcell', 'ltangentold' + "lcharg", + "lsorbit", + "lwave", + "lscalapack", + "lscalu", + "lplane", + "lhfcalc", + "shiftred", + "evenonly", + "oddonly", + "addgrid", + "ldau", + "lasph", + "lclimb", + "ldneb", + "lnebcell", + "ltangentold", ] # Site-wise list of arrays of FLOAT -VASP_TAG_SITEF_LIST = ['magmom', 'rwigs'] +VASP_TAG_SITEF_LIST = ["magmom", "rwigs"] # Species-wise list of arrays of FLOAT -VASP_TAG_SPECF_LIST = ['ldauu', 'ldauj'] +VASP_TAG_SPECF_LIST = ["ldauu", "ldauj"] # Site-wise list of arrays of INT -VASP_TAG_SPECI_LIST = ['ldaul'] +VASP_TAG_SPECI_LIST = ["ldaul"] -VASP_TAG_STRING_LIST = [ - 'algo', 'prec', 'system', 'precfock', 'lreal', 'metagga' -] +VASP_TAG_STRING_LIST = ["algo", "prec", "system", "precfock", "lreal", "metagga"] # The master list of VASP tags is a union of the above -> need to allow for 'miscellaneous' ? -VASP_TAG_LIST = VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + VASP_TAG_SPECI_LIST + \ - VASP_TAG_BOOL_LIST + VASP_TAG_FLOAT_LIST + \ - VASP_TAG_STRING_LIST + VASP_TAG_SPECF_LIST +VASP_TAG_LIST = ( + VASP_TAG_INT_LIST + + VASP_TAG_SITEF_LIST + + VASP_TAG_SPECI_LIST + + VASP_TAG_BOOL_LIST + + VASP_TAG_FLOAT_LIST + + VASP_TAG_STRING_LIST + + VASP_TAG_SPECF_LIST +) class IncarError(Exception): @@ -55,32 +117,25 @@ class Incar(object): tags: a dict of all INCAR settings All input tags and associated values are stored as key-value pairs in the dicionary called 'tags'. - """ - def __init__(self, - filename, - species=None, - poscar=None, - sort=True, - structure_info=None): - """ Construct an Incar object from 'filename'""" + """ + + def __init__( + self, filename, species=None, poscar=None, sort=True, structure_info=None + ): + """Construct an Incar object from 'filename'""" self.read(filename, species, poscar, sort, structure_info) - def read(self, - filename, - species=None, - poscar=None, - sort=True, - structure_info=None): - """ Read an INCAR file """ + def read(self, filename, species=None, poscar=None, sort=True, structure_info=None): + """Read an INCAR file""" self.tags = dict() try: - file = open(filename, 'r') + file = open(filename, "r") except: raise IncarError("Could not open file: '" + filename + "'") # parse INCAR into self.tags for line in file: - line = re.split('=', re.split('#', line)[0]) + line = re.split("=", re.split("#", line)[0]) if len(line) == 2: self.tags[line[0].strip()] = line[1].strip() self._verify_tags() @@ -92,7 +147,7 @@ def read(self, file.close() def _make_natural_type(self): - """ Convert self.tags values from strings into their 'natural type' (int, float, etc.) """ + """Convert self.tags values from strings into their 'natural type' (int, float, etc.)""" for tag in self.tags: if self.tags[tag] is None or str(self.tags[tag]).strip() == "": self.tags[tag] = None @@ -101,39 +156,55 @@ def _make_natural_type(self): try: self.tags[tag] = int(self.tags[tag]) except ValueError: - raise IncarError("Could not convert '" + tag + - "' : '" + self.tags[tag] + "' to int") + raise IncarError( + "Could not convert '" + + tag + + "' : '" + + self.tags[tag] + + "' to int" + ) elif tag.lower() in VASP_TAG_FLOAT_LIST: try: - self.tags[tag] = float(self.tags[tag].lower().replace( - 'd', 'e')) + self.tags[tag] = float(self.tags[tag].lower().replace("d", "e")) except ValueError: - raise IncarError("Could not convert '" + tag + - "' : '" + self.tags[tag] + - "' to float") + raise IncarError( + "Could not convert '" + + tag + + "' : '" + + self.tags[tag] + + "' to float" + ) elif tag.lower() in VASP_TAG_BOOL_LIST: - if not self.tags[tag].lower() in ['.true.', '.false.']: - raise IncarError("Could not find '" + tag + "' : '" + - self.tags[tag].lower() + - "' in ['.true.','.false.']") + if not self.tags[tag].lower() in [".true.", ".false."]: + raise IncarError( + "Could not find '" + + tag + + "' : '" + + self.tags[tag].lower() + + "' in ['.true.','.false.']" + ) else: - self.tags[tag] = (self.tags[tag].lower() == '.true.') + self.tags[tag] = self.tags[tag].lower() == ".true." elif tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: temp = [] for value in self.tags[tag].split(): try: - item = value.split('*') + item = value.split("*") if len(item) == 1: temp.append(float(value)) else: if item[0] != 0: temp.append( - str(item[0]) + '*' + - str(float(item[1]))) + str(item[0]) + "*" + str(float(item[1])) + ) except ValueError: - raise IncarError("Could not convert '" + tag + - "' : '" + self.tags[tag] + - "' to float list") + raise IncarError( + "Could not convert '" + + tag + + "' : '" + + self.tags[tag] + + "' to float list" + ) self.tags[tag] = temp elif tag.lower() in VASP_TAG_SPECI_LIST: temp = [] @@ -141,96 +212,123 @@ def _make_natural_type(self): try: temp.append(int(value)) except ValueError: - raise IncarError("Could not convert '" + tag + - "' : '" + self.tags[tag] + - "' to int list") + raise IncarError( + "Could not convert '" + + tag + + "' : '" + + self.tags[tag] + + "' to int list" + ) self.tags[tag] = temp elif tag.lower() in VASP_TAG_STRING_LIST: self._check_string_tag(tag, self.tags[tag]) def _check_string_tag(self, tag, value): - """ Check that string-valued tags are allowed values """ - if tag.lower() == 'prec': + """Check that string-valued tags are allowed values""" + if tag.lower() == "prec": if value.lower() not in [ - 'low', 'medium', 'high', 'normal', 'single', 'accurate' + "low", + "medium", + "high", + "normal", + "single", + "accurate", ]: raise IncarError("Unknown 'prec' value: '" + value) - elif tag.lower() == 'algo': + elif tag.lower() == "algo": if value.lower() not in [ - 'normal', 'veryfast', 'fast', 'conjugate', 'all', 'damped', - 'subrot', 'eigenval', 'none', 'nothing', 'chi', 'gw0', - 'gw', 'scgw0', 'scgw' + "normal", + "veryfast", + "fast", + "conjugate", + "all", + "damped", + "subrot", + "eigenval", + "none", + "nothing", + "chi", + "gw0", + "gw", + "scgw0", + "scgw", ]: raise IncarError("Unknown 'algo' value: '" + value) def _verify_tags(self): - """ Check that only allowed INCAR tags are in self.tags """ + """Check that only allowed INCAR tags are in self.tags""" for tag in self.tags: if tag.lower() in VASP_TAG_LIST: continue else: - print(("Warning: unknown INCAR tag '" + tag + - "' with value '" + str(self.tags[tag]) + "'")) + print( + ( + "Warning: unknown INCAR tag '" + + tag + + "' with value '" + + str(self.tags[tag]) + + "'" + ) + ) def update(self, species, poscar, sort=True, structure_info=None): - """ Update Incar object to reflect Species settings """ + """Update Incar object to reflect Species settings""" - #TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. - #TODO: Need a better update function, it's very construed and difficult to understand. Apologies! + # TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. + # TODO: Need a better update function, it's very construed and difficult to understand. Apologies! if structure_info is not None and structure_info.atom_properties is not None: - if "Cmagspin" in list(structure_info.atom_properties.keys()): + if "Cmagspin" or "Cunitmagspin" in list( + structure_info.atom_properties.keys() + ): vasp_input_tags_to_append = attribute_classes.CmagspinAttr( - structure_info).vasp_input_tags() + structure_info + ).vasp_input_tags() self.tags.update(vasp_input_tags_to_append) if sort == False: # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0. + self.tags[key] = 0.0 for site in poscar.basis: - self.tags[key] += float( - species[site.occupant].tags[key]) + self.tags[key] += float(species[site.occupant].tags[key]) if key.lower() in VASP_TAG_INT_LIST: self.tags[key] = int(self.tags[key]) else: self.tags[key] = [] - if key.lower() in (VASP_TAG_SPECF_LIST + - VASP_TAG_SPECI_LIST): + if key.lower() in (VASP_TAG_SPECF_LIST + VASP_TAG_SPECI_LIST): # add the value of the 'tag' for each species into the self.tags list for spec in poscar.type_atoms_alias: self.tags[key].append(species[spec].tags[key]) else: # add the value of the 'tag' for each atom into the self.tags list for site in poscar.basis: - self.tags[key].append( - species[site.occupant].tags[key]) + self.tags[key].append(species[site.occupant].tags[key]) else: pos = poscar.basis_dict() # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): # for key in species[species.keys()[0]].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0. + self.tags[key] = 0.0 for site in poscar.basis: - self.tags[key] += float( - species[site.occupant].tags[key]) + self.tags[key] += float(species[site.occupant].tags[key]) if key.lower() in VASP_TAG_INT_LIST: self.tags[key] = int(self.tags[key]) else: self.tags[key] = [] # add the value of the 'tag' for each atom into the self.tags list for alias in sorted(pos.keys()): - if key.lower() in (VASP_TAG_SPECF_LIST + - VASP_TAG_SPECI_LIST): + if key.lower() in (VASP_TAG_SPECF_LIST + VASP_TAG_SPECI_LIST): # for species-specific tags, use the value specified for the # species whose pseudopotential is being used for this alias for name in species.keys(): - if species[name].alias == alias and species[ - name].write_potcar: - self.tags[key].append( - species[name].tags[key]) + if ( + species[name].alias == alias + and species[name].write_potcar + ): + self.tags[key].append(species[name].tags[key]) break else: for name in species.keys(): @@ -241,12 +339,14 @@ def update(self, species, poscar, sort=True, structure_info=None): if species[name].alias == alias: if count > 0: self.tags[key].append( - str(count) + "*" + - str(species[name].tags[key])) + str(count) + + "*" + + str(species[name].tags[key]) + ) def write(self, filename): try: - incar_write = open(filename, 'w') + incar_write = open(filename, "w") except IOError as e: raise e for tag in self.tags: @@ -254,17 +354,22 @@ def write(self, filename): pass else: if tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: - incar_write.write('{} = {}\n'.format( - tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) + incar_write.write( + "{} = {}\n".format( + tag.upper(), remove_chars(self.tags[tag], "[\[\],']") + ) + ) elif tag.lower() in VASP_TAG_SPECI_LIST: - incar_write.write('{} = {}\n'.format( - tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) + incar_write.write( + "{} = {}\n".format( + tag.upper(), remove_chars(self.tags[tag], "[\[\],']") + ) + ) elif tag.lower() in VASP_TAG_BOOL_LIST: if self.tags[tag] == True: - incar_write.write('{} = .TRUE.\n'.format(tag.upper())) + incar_write.write("{} = .TRUE.\n".format(tag.upper())) else: - incar_write.write('{} = .FALSE.\n'.format(tag.upper())) + incar_write.write("{} = .FALSE.\n".format(tag.upper())) else: - incar_write.write('{} = {}\n'.format( - tag.upper(), self.tags[tag])) + incar_write.write("{} = {}\n".format(tag.upper(), self.tags[tag])) incar_write.close() diff --git a/casm/casm/vaspwrapper/vasp_calculator_base.py b/casm/casm/vaspwrapper/vasp_calculator_base.py index a76c744..b2f6021 100644 --- a/casm/casm/vaspwrapper/vasp_calculator_base.py +++ b/casm/casm/vaspwrapper/vasp_calculator_base.py @@ -726,23 +726,24 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): output["global_properties"]["energy"] = {} output["global_properties"]["energy"]["value"] = zcar.E[-1] + if ocar.ispin == 2: + output["global_properties"]["Cmagspin"] = {} + output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] + if structure_info.atom_properties is not None: # if you have cmagspin sitedofs if "Cmagspin" in list(structure_info.atom_properties.keys()): cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( ocar, unsort_dict) output["atom_properties"].update(cmagspin_specific_output) - - output["global_properties"]["Cmagspin"] = {} - output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] + elif "Cunitmagspin" in list(structure_info.atom_properties.keys()): + cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict, "Cunitmagspin") + output["atom_properties"].update(cmagspin_specific_output) # if you don't have cmagspin but have other sitedofs with ispin 2 else: if ocar.ispin == 2: - output["global_properties"]["Cmagspin"] = {} - output["global_properties"]["Cmagspin"][ - "value"] = zcar.mag[-1] - if ocar.lorbit in [1, 2, 11, 12]: cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( ocar, unsort_dict) @@ -752,7 +753,6 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): # if you don't have any sitedofs else: if ocar.ispin == 2: - output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] if ocar.lorbit in [1, 2, 11, 12]: cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( ocar, unsort_dict) From 2a16f98f9038e2f3d566a82113c57ceeaddbbd99 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 31 Oct 2022 17:22:57 -0700 Subject: [PATCH 03/18] remove unnecessary changes --- casm/casm/vasp/io/incar.py | 311 ++++++++++++------------------------- 1 file changed, 103 insertions(+), 208 deletions(-) diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index 834d765..2226269 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -1,4 +1,5 @@ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import (absolute_import, division, print_function, + unicode_literals) from builtins import * import re @@ -7,100 +8,37 @@ # List of tags in VASP sorted by the data type associated with it VASP_TAG_INT_LIST = [ - "ialgo", - "ibrion", - "icharg", - "images", - "ismear", - "ispin", - "istart", - "isym", - "lorbit", - "nbands", - "ndav", - "ngx", - "ngxf", - "ngy", - "ngyf", - "ngz", - "ngzf", - "npar", - "ncore", - "spind", - "nsw", - "isif", - "kpar", - "voskown", - "nsim", - "nedos", - "lmaxfock", - "lmaxmix", - "nkred", - "ivdw", - "nelmin", - "nelm", - "nelmdl", - "ldautype", - "ldauprint", - "ldauprint", - "ichain", + 'ialgo', 'ibrion', 'icharg', 'images', 'ismear', 'ispin', 'istart', 'isym', + 'lorbit', 'nbands', 'ndav', 'ngx', 'ngxf', 'ngy', 'ngyf', 'ngz', 'ngzf', + 'npar', 'ncore', 'spind', 'nsw', 'isif', 'kpar', 'voskown', 'nsim', + 'nedos', 'lmaxfock', 'lmaxmix', 'nkred', 'ivdw', 'nelmin', 'nelm', + 'nelmdl', 'ldautype', 'ldauprint', 'ldauprint', 'ichain' ] VASP_TAG_FLOAT_LIST = [ - "ediff", - "ediffg", - "emax", - "emin", - "encut", - "potim", - "sigma", - "enmax", - "symprec", - "time", - "hfscreen", - "amix", - "bmix", - "amix_mag", - "bmix_mag", - "spring", + 'ediff', 'ediffg', 'emax', 'emin', 'encut', 'potim', 'sigma', 'enmax', + 'symprec', 'time', 'hfscreen', 'amix', 'bmix', 'amix_mag', 'bmix_mag', + 'spring' ] VASP_TAG_BOOL_LIST = [ - "lcharg", - "lsorbit", - "lwave", - "lscalapack", - "lscalu", - "lplane", - "lhfcalc", - "shiftred", - "evenonly", - "oddonly", - "addgrid", - "ldau", - "lasph", - "lclimb", - "ldneb", - "lnebcell", - "ltangentold", + 'lcharg', 'lsorbit', 'lwave', 'lscalapack', 'lscalu', 'lplane', 'lhfcalc', + 'shiftred', 'evenonly', 'oddonly', 'addgrid', 'ldau', 'lasph', 'lclimb', + 'ldneb', 'lnebcell', 'ltangentold' ] # Site-wise list of arrays of FLOAT -VASP_TAG_SITEF_LIST = ["magmom", "rwigs"] +VASP_TAG_SITEF_LIST = ['magmom', 'rwigs'] # Species-wise list of arrays of FLOAT -VASP_TAG_SPECF_LIST = ["ldauu", "ldauj"] +VASP_TAG_SPECF_LIST = ['ldauu', 'ldauj'] # Site-wise list of arrays of INT -VASP_TAG_SPECI_LIST = ["ldaul"] +VASP_TAG_SPECI_LIST = ['ldaul'] -VASP_TAG_STRING_LIST = ["algo", "prec", "system", "precfock", "lreal", "metagga"] +VASP_TAG_STRING_LIST = [ + 'algo', 'prec', 'system', 'precfock', 'lreal', 'metagga' +] # The master list of VASP tags is a union of the above -> need to allow for 'miscellaneous' ? -VASP_TAG_LIST = ( - VASP_TAG_INT_LIST - + VASP_TAG_SITEF_LIST - + VASP_TAG_SPECI_LIST - + VASP_TAG_BOOL_LIST - + VASP_TAG_FLOAT_LIST - + VASP_TAG_STRING_LIST - + VASP_TAG_SPECF_LIST -) +VASP_TAG_LIST = VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + VASP_TAG_SPECI_LIST + \ + VASP_TAG_BOOL_LIST + VASP_TAG_FLOAT_LIST + \ + VASP_TAG_STRING_LIST + VASP_TAG_SPECF_LIST class IncarError(Exception): @@ -117,25 +55,32 @@ class Incar(object): tags: a dict of all INCAR settings All input tags and associated values are stored as key-value pairs in the dicionary called 'tags'. - """ - - def __init__( - self, filename, species=None, poscar=None, sort=True, structure_info=None - ): - """Construct an Incar object from 'filename'""" + """ + def __init__(self, + filename, + species=None, + poscar=None, + sort=True, + structure_info=None): + """ Construct an Incar object from 'filename'""" self.read(filename, species, poscar, sort, structure_info) - def read(self, filename, species=None, poscar=None, sort=True, structure_info=None): - """Read an INCAR file""" + def read(self, + filename, + species=None, + poscar=None, + sort=True, + structure_info=None): + """ Read an INCAR file """ self.tags = dict() try: - file = open(filename, "r") + file = open(filename, 'r') except: raise IncarError("Could not open file: '" + filename + "'") # parse INCAR into self.tags for line in file: - line = re.split("=", re.split("#", line)[0]) + line = re.split('=', re.split('#', line)[0]) if len(line) == 2: self.tags[line[0].strip()] = line[1].strip() self._verify_tags() @@ -147,7 +92,7 @@ def read(self, filename, species=None, poscar=None, sort=True, structure_info=No file.close() def _make_natural_type(self): - """Convert self.tags values from strings into their 'natural type' (int, float, etc.)""" + """ Convert self.tags values from strings into their 'natural type' (int, float, etc.) """ for tag in self.tags: if self.tags[tag] is None or str(self.tags[tag]).strip() == "": self.tags[tag] = None @@ -156,55 +101,39 @@ def _make_natural_type(self): try: self.tags[tag] = int(self.tags[tag]) except ValueError: - raise IncarError( - "Could not convert '" - + tag - + "' : '" - + self.tags[tag] - + "' to int" - ) + raise IncarError("Could not convert '" + tag + + "' : '" + self.tags[tag] + "' to int") elif tag.lower() in VASP_TAG_FLOAT_LIST: try: - self.tags[tag] = float(self.tags[tag].lower().replace("d", "e")) + self.tags[tag] = float(self.tags[tag].lower().replace( + 'd', 'e')) except ValueError: - raise IncarError( - "Could not convert '" - + tag - + "' : '" - + self.tags[tag] - + "' to float" - ) + raise IncarError("Could not convert '" + tag + + "' : '" + self.tags[tag] + + "' to float") elif tag.lower() in VASP_TAG_BOOL_LIST: - if not self.tags[tag].lower() in [".true.", ".false."]: - raise IncarError( - "Could not find '" - + tag - + "' : '" - + self.tags[tag].lower() - + "' in ['.true.','.false.']" - ) + if not self.tags[tag].lower() in ['.true.', '.false.']: + raise IncarError("Could not find '" + tag + "' : '" + + self.tags[tag].lower() + + "' in ['.true.','.false.']") else: - self.tags[tag] = self.tags[tag].lower() == ".true." + self.tags[tag] = (self.tags[tag].lower() == '.true.') elif tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: temp = [] for value in self.tags[tag].split(): try: - item = value.split("*") + item = value.split('*') if len(item) == 1: temp.append(float(value)) else: if item[0] != 0: temp.append( - str(item[0]) + "*" + str(float(item[1])) - ) + str(item[0]) + '*' + + str(float(item[1]))) except ValueError: - raise IncarError( - "Could not convert '" - + tag - + "' : '" - + self.tags[tag] - + "' to float list" - ) + raise IncarError("Could not convert '" + tag + + "' : '" + self.tags[tag] + + "' to float list") self.tags[tag] = temp elif tag.lower() in VASP_TAG_SPECI_LIST: temp = [] @@ -212,123 +141,96 @@ def _make_natural_type(self): try: temp.append(int(value)) except ValueError: - raise IncarError( - "Could not convert '" - + tag - + "' : '" - + self.tags[tag] - + "' to int list" - ) + raise IncarError("Could not convert '" + tag + + "' : '" + self.tags[tag] + + "' to int list") self.tags[tag] = temp elif tag.lower() in VASP_TAG_STRING_LIST: self._check_string_tag(tag, self.tags[tag]) def _check_string_tag(self, tag, value): - """Check that string-valued tags are allowed values""" - if tag.lower() == "prec": + """ Check that string-valued tags are allowed values """ + if tag.lower() == 'prec': if value.lower() not in [ - "low", - "medium", - "high", - "normal", - "single", - "accurate", + 'low', 'medium', 'high', 'normal', 'single', 'accurate' ]: raise IncarError("Unknown 'prec' value: '" + value) - elif tag.lower() == "algo": + elif tag.lower() == 'algo': if value.lower() not in [ - "normal", - "veryfast", - "fast", - "conjugate", - "all", - "damped", - "subrot", - "eigenval", - "none", - "nothing", - "chi", - "gw0", - "gw", - "scgw0", - "scgw", + 'normal', 'veryfast', 'fast', 'conjugate', 'all', 'damped', + 'subrot', 'eigenval', 'none', 'nothing', 'chi', 'gw0', + 'gw', 'scgw0', 'scgw' ]: raise IncarError("Unknown 'algo' value: '" + value) def _verify_tags(self): - """Check that only allowed INCAR tags are in self.tags""" + """ Check that only allowed INCAR tags are in self.tags """ for tag in self.tags: if tag.lower() in VASP_TAG_LIST: continue else: - print( - ( - "Warning: unknown INCAR tag '" - + tag - + "' with value '" - + str(self.tags[tag]) - + "'" - ) - ) + print(("Warning: unknown INCAR tag '" + tag + + "' with value '" + str(self.tags[tag]) + "'")) def update(self, species, poscar, sort=True, structure_info=None): - """Update Incar object to reflect Species settings""" + """ Update Incar object to reflect Species settings """ - # TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. - # TODO: Need a better update function, it's very construed and difficult to understand. Apologies! + #TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. + #TODO: Need a better update function, it's very construed and difficult to understand. Apologies! if structure_info is not None and structure_info.atom_properties is not None: - if "Cmagspin" or "Cunitmagspin" in list( - structure_info.atom_properties.keys() - ): + if "Cmagspin" or "Cunitmagspin" in list(structure_info.atom_properties.keys()): vasp_input_tags_to_append = attribute_classes.CmagspinAttr( - structure_info - ).vasp_input_tags() + structure_info).vasp_input_tags() self.tags.update(vasp_input_tags_to_append) if sort == False: # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0.0 + self.tags[key] = 0. for site in poscar.basis: - self.tags[key] += float(species[site.occupant].tags[key]) + self.tags[key] += float( + species[site.occupant].tags[key]) if key.lower() in VASP_TAG_INT_LIST: self.tags[key] = int(self.tags[key]) else: self.tags[key] = [] - if key.lower() in (VASP_TAG_SPECF_LIST + VASP_TAG_SPECI_LIST): + if key.lower() in (VASP_TAG_SPECF_LIST + + VASP_TAG_SPECI_LIST): # add the value of the 'tag' for each species into the self.tags list for spec in poscar.type_atoms_alias: self.tags[key].append(species[spec].tags[key]) else: # add the value of the 'tag' for each atom into the self.tags list for site in poscar.basis: - self.tags[key].append(species[site.occupant].tags[key]) + self.tags[key].append( + species[site.occupant].tags[key]) else: pos = poscar.basis_dict() # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): # for key in species[species.keys()[0]].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0.0 + self.tags[key] = 0. for site in poscar.basis: - self.tags[key] += float(species[site.occupant].tags[key]) + self.tags[key] += float( + species[site.occupant].tags[key]) if key.lower() in VASP_TAG_INT_LIST: self.tags[key] = int(self.tags[key]) else: self.tags[key] = [] # add the value of the 'tag' for each atom into the self.tags list for alias in sorted(pos.keys()): - if key.lower() in (VASP_TAG_SPECF_LIST + VASP_TAG_SPECI_LIST): + if key.lower() in (VASP_TAG_SPECF_LIST + + VASP_TAG_SPECI_LIST): # for species-specific tags, use the value specified for the # species whose pseudopotential is being used for this alias for name in species.keys(): - if ( - species[name].alias == alias - and species[name].write_potcar - ): - self.tags[key].append(species[name].tags[key]) + if species[name].alias == alias and species[ + name].write_potcar: + self.tags[key].append( + species[name].tags[key]) break else: for name in species.keys(): @@ -339,14 +241,12 @@ def update(self, species, poscar, sort=True, structure_info=None): if species[name].alias == alias: if count > 0: self.tags[key].append( - str(count) - + "*" - + str(species[name].tags[key]) - ) + str(count) + "*" + + str(species[name].tags[key])) def write(self, filename): try: - incar_write = open(filename, "w") + incar_write = open(filename, 'w') except IOError as e: raise e for tag in self.tags: @@ -354,22 +254,17 @@ def write(self, filename): pass else: if tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: - incar_write.write( - "{} = {}\n".format( - tag.upper(), remove_chars(self.tags[tag], "[\[\],']") - ) - ) + incar_write.write('{} = {}\n'.format( + tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_SPECI_LIST: - incar_write.write( - "{} = {}\n".format( - tag.upper(), remove_chars(self.tags[tag], "[\[\],']") - ) - ) + incar_write.write('{} = {}\n'.format( + tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_BOOL_LIST: if self.tags[tag] == True: - incar_write.write("{} = .TRUE.\n".format(tag.upper())) + incar_write.write('{} = .TRUE.\n'.format(tag.upper())) else: - incar_write.write("{} = .FALSE.\n".format(tag.upper())) + incar_write.write('{} = .FALSE.\n'.format(tag.upper())) else: - incar_write.write("{} = {}\n".format(tag.upper(), self.tags[tag])) + incar_write.write('{} = {}\n'.format( + tag.upper(), self.tags[tag])) incar_write.close() From 3aabd2e7faaf07342661887282e8a0ac267cad8f Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 31 Oct 2022 17:44:31 -0700 Subject: [PATCH 04/18] squash bug in magmom writer --- casm/casm/vasp/io/attribute_classes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 873f2f1..01d45a2 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -84,6 +84,13 @@ def vasp_input_tags(self, sort=True): + " " ) number_of_same_magmoms = 1 + if i == len(self.atom_props) - 1: + magmom_value += ( + str(number_of_same_magmoms) + + "*" + + str(self.atom_props[i]["value"][0]) + + " " + ) return dict(MAGMOM=magmom_value, ISPIN=2) From 6e143c857ed2be90b8cd14afccf906e24472a118 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Wed, 9 Nov 2022 10:03:54 -0800 Subject: [PATCH 05/18] attribute classes output dictionary change --- casm/casm/vasp/io/attribute_classes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 01d45a2..799eb27 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -119,7 +119,9 @@ def vasp_output_dictionary(outcar, unsort_dict, magspin_type="Cmagspin"): } """ - output = {magspin_type: {}} + output = {} + output[magspin_type] = {} + output[magspin_type]["value"] = {} for i in range(len(outcar.mag)): output[magspin_type]["value"][unsort_dict[i]] = [outcar.mag[i]] From 0ac20b29faa850d9c449192f70d2e58bee56b435 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Wed, 9 Nov 2022 10:08:36 -0800 Subject: [PATCH 06/18] attribute classes output dictionary change --- casm/casm/vasp/io/attribute_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 799eb27..09e7b0d 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -121,7 +121,7 @@ def vasp_output_dictionary(outcar, unsort_dict, magspin_type="Cmagspin"): """ output = {} output[magspin_type] = {} - output[magspin_type]["value"] = {} + output[magspin_type]["value"] = [None] * len(unsort_dict) for i in range(len(outcar.mag)): output[magspin_type]["value"][unsort_dict[i]] = [outcar.mag[i]] From 9e29e2c11d35c55ce2f33f45a3b94483601ec5e0 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Tue, 25 Apr 2023 16:32:56 -0700 Subject: [PATCH 07/18] added support for SOunitmagspin --- casm/casm/vasp/io/attribute_classes.py | 319 +++++++++++++++--- casm/casm/vasp/io/incar.py | 194 ++++++++--- casm/casm/vaspwrapper/vasp_calculator_base.py | 31 +- casm/setup.py | 2 +- 4 files changed, 425 insertions(+), 121 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 09e7b0d..6478867 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,4 +1,258 @@ import math +import numpy as np + + +def get_incar_magmom_from_magmom_values(magmom_values): + """Returns an INCAR magmom string from a + list of magmom values. The magmom values should + be listed in the order of atoms in POSCAR + + Parameters + ---------- + magmom_values : np.ndarray + + Returns + ------- + str + + """ + + magmom = "" + for i, value in enumerate(magmom_values): + if i == 0: + number_of_same_magmoms = 1 + elif math.isclose(value, magmom_values[i - 1]): + number_of_same_magmoms += 1 + else: + magmom += (str(number_of_same_magmoms) + "*" + + str(magmom_values[i - 1]) + " ") + number_of_same_magmoms = 1 + if i == len(magmom_values) - 1: + magmom += str(number_of_same_magmoms) + "*" + str( + magmom_values[i]) + " " + + return magmom + + +class SOunitmagspinAttr: + """Class containing information specific to Cmagspin dof. + This object will be constructed from casm.project.structure.StructureInfo class + which digests its information. + + self.atom_props: List[Dict] - Contains the list of atom properites (with atom name and it's value) + Look at the example below for detailed description of object properties + + Consider the example of NaFeO2 with Fe having a +5 magnetic moment and rest all being 0 + self.atom_props: + [ + {"site_index":0, "atom": "Na", "value":0}, + {"site_index":1, "atom":"Fe", "value":5}, + {"site_index":2, "atom":"O","value":0}, + {"site_index":3, "atom":"O","value":0} + ] + """ + + def __init__(self, structure_info): + """Constructs the CmagspinAttr object from StructureInfo object + + Parameters + ---------- + structure_info : casm.project.structure.StructureInfo + + """ + if "SOunitmagspin" not in list(structure_info.atom_properties.keys()): + raise RuntimeError( + "Could not construct SOunitmagspinAttr class. " + "Check if you're dealing with Cmagspin dof calculations.") + + self.atom_props = [{ + "site_index": site_index, + "atom": atom_type, + "value": magmom_value + } for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["SOunitmagspin"]["value"], + ))] + + def vasp_input_tags(self, sort=True): + """Returns a dictionary of MAGMOM, ISPIN input tags + specific to collinear magnetic VASP calculations. + + Parameters + ---------- + sort: bool, optional + This should match the sort used to write + POSCAR file (whether the basis atoms are sorted)) + + Returns + ------- + dict + { + "MAGMOM": magmom_string, + "ISPIN": 2 + } + + """ + if sort is True: + self.atom_props.sort(key=lambda x: x["atom"]) + + magmom_values = np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props])) + + incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) + + return dict(MAGMOM=incar_magmom_str, + ISPIN=2, + LSORBIT=".TRUE.", + LNONCOLLINEAR=".TRUE.") + + @staticmethod + def vasp_output_dictionary(outcar, unsort_dict): + """Returns the attribute specific vasp output + dictionary which can be updated to the whole output + dictionary which will be printed as properties.calc.json. + For Cmagspin, this will be magnetic moment of each individual species + + Parameters + ---------- + outcar : casm.vasp.io.outcar + Outcar containing magmom information + unsort_dict : dict + ``Poscar.unsort_dict()`` useful for reordering + the magmom values + + Returns + ------- + dict + { + "Cmagspin":{ + "value":[] + } + } + + """ + output = {} + # output["Cmagspin"] = {} + # output["Cmagspin"]["value"] = [None] * len(unsort_dict) + # output["Cunitmagspin"] = {} + # output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) + # for i in range(len(outcar.mag)): + # output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] + # output["Cunitmagspin"]["value"][unsort_dict[i]] = [ + # outcar.mag[i] / abs(outcar.mag[i]) + # ] + + raise NotImplementedError("Not implemented this part yet") + + +class CunitmagspinAttr: + """Class containing information specific to Cmagspin dof. + This object will be constructed from casm.project.structure.StructureInfo class + which digests its information. + + self.atom_props: List[Dict] - Contains the list of atom properites (with atom name and it's value) + Look at the example below for detailed description of object properties + + Consider the example of NaFeO2 with Fe having a +5 magnetic moment and rest all being 0 + self.atom_props: + [ + {"site_index":0, "atom": "Na", "value":0}, + {"site_index":1, "atom":"Fe", "value":5}, + {"site_index":2, "atom":"O","value":0}, + {"site_index":3, "atom":"O","value":0} + ] + """ + + def __init__(self, structure_info): + """Constructs the CmagspinAttr object from StructureInfo object + + Parameters + ---------- + structure_info : casm.project.structure.StructureInfo + + """ + if "Cunitmagspin" not in list(structure_info.atom_properties.keys()): + raise RuntimeError( + "Could not construct CunitmagspinAttr class. " + "Check if you're dealing with Cmagspin dof calculations.") + + self.atom_props = [{ + "site_index": site_index, + "atom": atom_type, + "value": magmom_value + } for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["Cunitmagspin"]["value"], + ))] + + def vasp_input_tags(self, sort=True): + """Returns a dictionary of MAGMOM, ISPIN input tags + specific to collinear magnetic VASP calculations. + + Parameters + ---------- + sort: bool, optional + This should match the sort used to write + POSCAR file (whether the basis atoms are sorted)) + + Returns + ------- + dict + { + "MAGMOM": magmom_string, + "ISPIN": 2 + } + + """ + if sort is True: + self.atom_props.sort(key=lambda x: x["atom"]) + + magmom_values = np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props])) + + incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) + + return dict(MAGMOM=incar_magmom_str, ISPIN=2) + + @staticmethod + def vasp_output_dictionary(outcar, unsort_dict): + """Returns the attribute specific vasp output + dictionary which can be updated to the whole output + dictionary which will be printed as properties.calc.json. + For Cmagspin, this will be magnetic moment of each individual species + + Parameters + ---------- + outcar : casm.vasp.io.outcar + Outcar containing magmom information + unsort_dict : dict + ``Poscar.unsort_dict()`` useful for reordering + the magmom values + + Returns + ------- + dict + { + "Cmagspin":{ + "value":[] + } + } + + """ + output = {} + output["Cmagspin"] = {} + output["Cmagspin"]["value"] = [None] * len(unsort_dict) + output["Cunitmagspin"] = {} + output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) + for i in range(len(outcar.mag)): + output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] + output["Cunitmagspin"]["value"][unsort_dict[i]] = [ + outcar.mag[i] / abs(outcar.mag[i]) + ] + + return output class CmagspinAttr: @@ -27,24 +281,19 @@ def __init__(self, structure_info): structure_info : casm.project.structure.StructureInfo """ - if "Cmagspin" in list(structure_info.atom_properties.keys()): - self.magspin = "Cmagspin" - elif "Cunitmagspin" in list(structure_info.atom_properties.keys()): - self.magspin = "Cunitmagspin" - else: + if "Cmagspin" not in list(structure_info.atom_properties.keys()): raise RuntimeError( "Could not construct CmagspinAttr class. " - "Check if you're dealing with Cmagspin dof calculations." - ) - self.atom_props = [ - {"site_index": site_index, "atom": atom_type, "value": magmom_value} - for site_index, (atom_type, magmom_value) in enumerate( - zip( - structure_info.atom_type, - structure_info.atom_properties[self.magspin]["value"], - ) - ) - ] + "Check if you're dealing with Cmagspin dof calculations.") + self.atom_props = [{ + "site_index": site_index, + "atom": atom_type, + "value": magmom_value + } for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["Cmagspin"]["value"], + ))] def vasp_input_tags(self, sort=True): """Returns a dictionary of MAGMOM, ISPIN input tags @@ -68,34 +317,14 @@ def vasp_input_tags(self, sort=True): if sort is True: self.atom_props.sort(key=lambda x: x["atom"]) - magmom_value = "" - for i, atom_props in enumerate(self.atom_props): - if i == 0: - number_of_same_magmoms = 1 - elif math.isclose( - atom_props["value"][0], self.atom_props[i - 1]["value"][0] - ): - number_of_same_magmoms += 1 - else: - magmom_value += ( - str(number_of_same_magmoms) - + "*" - + str(self.atom_props[i - 1]["value"][0]) - + " " - ) - number_of_same_magmoms = 1 - if i == len(self.atom_props) - 1: - magmom_value += ( - str(number_of_same_magmoms) - + "*" - + str(self.atom_props[i]["value"][0]) - + " " - ) - - return dict(MAGMOM=magmom_value, ISPIN=2) + magmom_values = np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props])) + incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) + + return dict(MAGMOM=incar_magmom_str, ISPIN=2) @staticmethod - def vasp_output_dictionary(outcar, unsort_dict, magspin_type="Cmagspin"): + def vasp_output_dictionary(outcar, unsort_dict): """Returns the attribute specific vasp output dictionary which can be updated to the whole output dictionary which will be printed as properties.calc.json. @@ -120,9 +349,9 @@ def vasp_output_dictionary(outcar, unsort_dict, magspin_type="Cmagspin"): """ output = {} - output[magspin_type] = {} - output[magspin_type]["value"] = [None] * len(unsort_dict) + output["Cmagspin"] = {} + output["Cmagspin"]["value"] = [None] * len(unsort_dict) for i in range(len(outcar.mag)): - output[magspin_type]["value"][unsort_dict[i]] = [outcar.mag[i]] + output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] return output diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index 2226269..6bdb597 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -1,5 +1,4 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) +from __future__ import absolute_import, division, print_function, unicode_literals from builtins import * import re @@ -8,40 +7,101 @@ # List of tags in VASP sorted by the data type associated with it VASP_TAG_INT_LIST = [ - 'ialgo', 'ibrion', 'icharg', 'images', 'ismear', 'ispin', 'istart', 'isym', - 'lorbit', 'nbands', 'ndav', 'ngx', 'ngxf', 'ngy', 'ngyf', 'ngz', 'ngzf', - 'npar', 'ncore', 'spind', 'nsw', 'isif', 'kpar', 'voskown', 'nsim', - 'nedos', 'lmaxfock', 'lmaxmix', 'nkred', 'ivdw', 'nelmin', 'nelm', - 'nelmdl', 'ldautype', 'ldauprint', 'ldauprint', 'ichain' + "ialgo", + "ibrion", + "icharg", + "images", + "ismear", + "ispin", + "istart", + "isym", + "lorbit", + "nbands", + "ndav", + "ngx", + "ngxf", + "ngy", + "ngyf", + "ngz", + "ngzf", + "npar", + "ncore", + "spind", + "nsw", + "isif", + "kpar", + "voskown", + "nsim", + "nedos", + "lmaxfock", + "lmaxmix", + "nkred", + "ivdw", + "nelmin", + "nelm", + "nelmdl", + "ldautype", + "ldauprint", + "ldauprint", + "ichain", ] VASP_TAG_FLOAT_LIST = [ - 'ediff', 'ediffg', 'emax', 'emin', 'encut', 'potim', 'sigma', 'enmax', - 'symprec', 'time', 'hfscreen', 'amix', 'bmix', 'amix_mag', 'bmix_mag', - 'spring' + "ediff", + "ediffg", + "emax", + "emin", + "encut", + "potim", + "sigma", + "enmax", + "symprec", + "time", + "hfscreen", + "amix", + "bmix", + "amix_mag", + "bmix_mag", + "spring", ] VASP_TAG_BOOL_LIST = [ - 'lcharg', 'lsorbit', 'lwave', 'lscalapack', 'lscalu', 'lplane', 'lhfcalc', - 'shiftred', 'evenonly', 'oddonly', 'addgrid', 'ldau', 'lasph', 'lclimb', - 'ldneb', 'lnebcell', 'ltangentold' + "lcharg", + "lsorbit", + "lwave", + "lscalapack", + "lscalu", + "lplane", + "lhfcalc", + "shiftred", + "evenonly", + "oddonly", + "addgrid", + "ldau", + "lasph", + "lclimb", + "ldneb", + "lnebcell", + "ltangentold", ] # Site-wise list of arrays of FLOAT -VASP_TAG_SITEF_LIST = ['magmom', 'rwigs'] +VASP_TAG_SITEF_LIST = ["magmom", "rwigs"] # Species-wise list of arrays of FLOAT -VASP_TAG_SPECF_LIST = ['ldauu', 'ldauj'] +VASP_TAG_SPECF_LIST = ["ldauu", "ldauj"] # Site-wise list of arrays of INT -VASP_TAG_SPECI_LIST = ['ldaul'] +VASP_TAG_SPECI_LIST = ["ldaul"] VASP_TAG_STRING_LIST = [ - 'algo', 'prec', 'system', 'precfock', 'lreal', 'metagga' + "algo", "prec", "system", "precfock", "lreal", "metagga" ] # The master list of VASP tags is a union of the above -> need to allow for 'miscellaneous' ? -VASP_TAG_LIST = VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + VASP_TAG_SPECI_LIST + \ - VASP_TAG_BOOL_LIST + VASP_TAG_FLOAT_LIST + \ - VASP_TAG_STRING_LIST + VASP_TAG_SPECF_LIST +VASP_TAG_LIST = (VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + + VASP_TAG_SPECI_LIST + VASP_TAG_BOOL_LIST + + VASP_TAG_FLOAT_LIST + VASP_TAG_STRING_LIST + + VASP_TAG_SPECF_LIST) class IncarError(Exception): + def __init__(self, msg): self.msg = msg @@ -55,14 +115,15 @@ class Incar(object): tags: a dict of all INCAR settings All input tags and associated values are stored as key-value pairs in the dicionary called 'tags'. - """ + """ + def __init__(self, filename, species=None, poscar=None, sort=True, structure_info=None): - """ Construct an Incar object from 'filename'""" + """Construct an Incar object from 'filename'""" self.read(filename, species, poscar, sort, structure_info) def read(self, @@ -71,16 +132,16 @@ def read(self, poscar=None, sort=True, structure_info=None): - """ Read an INCAR file """ + """Read an INCAR file""" self.tags = dict() try: - file = open(filename, 'r') + file = open(filename, "r") except: raise IncarError("Could not open file: '" + filename + "'") # parse INCAR into self.tags for line in file: - line = re.split('=', re.split('#', line)[0]) + line = re.split("=", re.split("#", line)[0]) if len(line) == 2: self.tags[line[0].strip()] = line[1].strip() self._verify_tags() @@ -92,7 +153,7 @@ def read(self, file.close() def _make_natural_type(self): - """ Convert self.tags values from strings into their 'natural type' (int, float, etc.) """ + """Convert self.tags values from strings into their 'natural type' (int, float, etc.)""" for tag in self.tags: if self.tags[tag] is None or str(self.tags[tag]).strip() == "": self.tags[tag] = None @@ -106,29 +167,29 @@ def _make_natural_type(self): elif tag.lower() in VASP_TAG_FLOAT_LIST: try: self.tags[tag] = float(self.tags[tag].lower().replace( - 'd', 'e')) + "d", "e")) except ValueError: raise IncarError("Could not convert '" + tag + "' : '" + self.tags[tag] + "' to float") elif tag.lower() in VASP_TAG_BOOL_LIST: - if not self.tags[tag].lower() in ['.true.', '.false.']: + if not self.tags[tag].lower() in [".true.", ".false."]: raise IncarError("Could not find '" + tag + "' : '" + self.tags[tag].lower() + "' in ['.true.','.false.']") else: - self.tags[tag] = (self.tags[tag].lower() == '.true.') + self.tags[tag] = self.tags[tag].lower() == ".true." elif tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: temp = [] for value in self.tags[tag].split(): try: - item = value.split('*') + item = value.split("*") if len(item) == 1: temp.append(float(value)) else: if item[0] != 0: temp.append( - str(item[0]) + '*' + + str(item[0]) + "*" + str(float(item[1]))) except ValueError: raise IncarError("Could not convert '" + tag + @@ -149,22 +210,39 @@ def _make_natural_type(self): self._check_string_tag(tag, self.tags[tag]) def _check_string_tag(self, tag, value): - """ Check that string-valued tags are allowed values """ - if tag.lower() == 'prec': + """Check that string-valued tags are allowed values""" + if tag.lower() == "prec": if value.lower() not in [ - 'low', 'medium', 'high', 'normal', 'single', 'accurate' + "low", + "medium", + "high", + "normal", + "single", + "accurate", ]: raise IncarError("Unknown 'prec' value: '" + value) - elif tag.lower() == 'algo': + elif tag.lower() == "algo": if value.lower() not in [ - 'normal', 'veryfast', 'fast', 'conjugate', 'all', 'damped', - 'subrot', 'eigenval', 'none', 'nothing', 'chi', 'gw0', - 'gw', 'scgw0', 'scgw' + "normal", + "veryfast", + "fast", + "conjugate", + "all", + "damped", + "subrot", + "eigenval", + "none", + "nothing", + "chi", + "gw0", + "gw", + "scgw0", + "scgw", ]: raise IncarError("Unknown 'algo' value: '" + value) def _verify_tags(self): - """ Check that only allowed INCAR tags are in self.tags """ + """Check that only allowed INCAR tags are in self.tags""" for tag in self.tags: if tag.lower() in VASP_TAG_LIST: continue @@ -173,22 +251,32 @@ def _verify_tags(self): "' with value '" + str(self.tags[tag]) + "'")) def update(self, species, poscar, sort=True, structure_info=None): - """ Update Incar object to reflect Species settings """ + """Update Incar object to reflect Species settings""" - #TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. - #TODO: Need a better update function, it's very construed and difficult to understand. Apologies! + # TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. + # TODO: Need a better update function, it's very construed and difficult to understand. Apologies! if structure_info is not None and structure_info.atom_properties is not None: - if "Cmagspin" or "Cunitmagspin" in list(structure_info.atom_properties.keys()): + if "Cmagspin" in list(structure_info.atom_properties.keys()): vasp_input_tags_to_append = attribute_classes.CmagspinAttr( structure_info).vasp_input_tags() self.tags.update(vasp_input_tags_to_append) + if "Cunitmagspin" in list(structure_info.atom_properties.keys()): + vasp_input_tags_to_append = attribute_classes.CunitmagspinAttr( + structure_info).vasp_input_tags() + self.tags.update(vasp_input_tags_to_append) + + if "SOunitmagspin" in list(structure_info.atom_properties.keys()): + vasp_input_tags_to_append = attribute_classes.SOunitmagspinAttr( + structure_info).vasp_input_tags() + self.tags.update(vasp_input_tags_to_append) + if sort == False: # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0. + self.tags[key] = 0.0 for site in poscar.basis: self.tags[key] += float( species[site.occupant].tags[key]) @@ -212,7 +300,7 @@ def update(self, species, poscar, sort=True, structure_info=None): for key in list(species.values())[0].tags.keys(): # for key in species[species.keys()[0]].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0. + self.tags[key] = 0.0 for site in poscar.basis: self.tags[key] += float( species[site.occupant].tags[key]) @@ -227,8 +315,8 @@ def update(self, species, poscar, sort=True, structure_info=None): # for species-specific tags, use the value specified for the # species whose pseudopotential is being used for this alias for name in species.keys(): - if species[name].alias == alias and species[ - name].write_potcar: + if (species[name].alias == alias + and species[name].write_potcar): self.tags[key].append( species[name].tags[key]) break @@ -246,7 +334,7 @@ def update(self, species, poscar, sort=True, structure_info=None): def write(self, filename): try: - incar_write = open(filename, 'w') + incar_write = open(filename, "w") except IOError as e: raise e for tag in self.tags: @@ -254,17 +342,17 @@ def write(self, filename): pass else: if tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: - incar_write.write('{} = {}\n'.format( + incar_write.write("{} = {}\n".format( tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_SPECI_LIST: - incar_write.write('{} = {}\n'.format( + incar_write.write("{} = {}\n".format( tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_BOOL_LIST: if self.tags[tag] == True: - incar_write.write('{} = .TRUE.\n'.format(tag.upper())) + incar_write.write("{} = .TRUE.\n".format(tag.upper())) else: - incar_write.write('{} = .FALSE.\n'.format(tag.upper())) + incar_write.write("{} = .FALSE.\n".format(tag.upper())) else: - incar_write.write('{} = {}\n'.format( + incar_write.write("{} = {}\n".format( tag.upper(), self.tags[tag])) incar_write.close() diff --git a/casm/casm/vaspwrapper/vasp_calculator_base.py b/casm/casm/vaspwrapper/vasp_calculator_base.py index b2f6021..8b66172 100644 --- a/casm/casm/vaspwrapper/vasp_calculator_base.py +++ b/casm/casm/vaspwrapper/vasp_calculator_base.py @@ -701,7 +701,7 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): # For example: # 'unsort_dict[0]' returns the index into the unsorted POSCAR of the first atom in the sorted POSCAR output["atom_type"] = initial_structure.atom_type - #output["atoms_per_type"] = initial_structure.num_atoms + # output["atoms_per_type"] = initial_structure.num_atoms output["coordinate_mode"] = contcar.coordinate_mode # as lists @@ -729,6 +729,10 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): if ocar.ispin == 2: output["global_properties"]["Cmagspin"] = {} output["global_properties"]["Cmagspin"]["value"] = zcar.mag[-1] + if ocar.lorbit in [1, 2, 11, 12]: + cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict) + output["atom_properties"].update(cmagspin_specific_output) if structure_info.atom_properties is not None: # if you have cmagspin sitedofs @@ -736,27 +740,10 @@ def properties(vaspdir, initial_structurefile=None, speciesfile=None): cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( ocar, unsort_dict) output["atom_properties"].update(cmagspin_specific_output) - elif "Cunitmagspin" in list(structure_info.atom_properties.keys()): - cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( - ocar, unsort_dict, "Cunitmagspin") - output["atom_properties"].update(cmagspin_specific_output) - - # if you don't have cmagspin but have other sitedofs with ispin 2 - else: - if ocar.ispin == 2: - if ocar.lorbit in [1, 2, 11, 12]: - cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( - ocar, unsort_dict) - output["atom_properties"].update( - cmagspin_specific_output) - - # if you don't have any sitedofs - else: - if ocar.ispin == 2: - if ocar.lorbit in [1, 2, 11, 12]: - cmagspin_specific_output = attribute_classes.CmagspinAttr.vasp_output_dictionary( - ocar, unsort_dict) - output["atom_properties"].update(cmagspin_specific_output) + if "Cunitmagspin" in list(structure_info.atom_properties.keys()): + cunitmagspin_specific_output = attribute_classes.CunitmagspinAttr.vasp_output_dictionary( + ocar, unsort_dict) + output["atom_properties"].update(cunitmagspin_specific_output) return output diff --git a/casm/setup.py b/casm/setup.py index fab3a61..fe4fa23 100644 --- a/casm/setup.py +++ b/casm/setup.py @@ -19,7 +19,7 @@ def script_str(file): ] print(console_scripts) -with open(os.path.join('..', 'README.md'), encoding='utf-8') as f: +with open('DESC.md', 'r') as f: long_description = f.read() setup( From 57f43f784875d8dca89d6a70e682145f36fcdc46 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Tue, 25 Apr 2023 16:56:35 -0700 Subject: [PATCH 08/18] removed unnecessary changes to incar --- casm/casm/vasp/io/incar.py | 181 +++++++++++-------------------------- 1 file changed, 52 insertions(+), 129 deletions(-) diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index 6bdb597..fe77a34 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -1,4 +1,5 @@ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import (absolute_import, division, print_function, + unicode_literals) from builtins import * import re @@ -7,97 +8,37 @@ # List of tags in VASP sorted by the data type associated with it VASP_TAG_INT_LIST = [ - "ialgo", - "ibrion", - "icharg", - "images", - "ismear", - "ispin", - "istart", - "isym", - "lorbit", - "nbands", - "ndav", - "ngx", - "ngxf", - "ngy", - "ngyf", - "ngz", - "ngzf", - "npar", - "ncore", - "spind", - "nsw", - "isif", - "kpar", - "voskown", - "nsim", - "nedos", - "lmaxfock", - "lmaxmix", - "nkred", - "ivdw", - "nelmin", - "nelm", - "nelmdl", - "ldautype", - "ldauprint", - "ldauprint", - "ichain", + 'ialgo', 'ibrion', 'icharg', 'images', 'ismear', 'ispin', 'istart', 'isym', + 'lorbit', 'nbands', 'ndav', 'ngx', 'ngxf', 'ngy', 'ngyf', 'ngz', 'ngzf', + 'npar', 'ncore', 'spind', 'nsw', 'isif', 'kpar', 'voskown', 'nsim', + 'nedos', 'lmaxfock', 'lmaxmix', 'nkred', 'ivdw', 'nelmin', 'nelm', + 'nelmdl', 'ldautype', 'ldauprint', 'ldauprint', 'ichain' ] VASP_TAG_FLOAT_LIST = [ - "ediff", - "ediffg", - "emax", - "emin", - "encut", - "potim", - "sigma", - "enmax", - "symprec", - "time", - "hfscreen", - "amix", - "bmix", - "amix_mag", - "bmix_mag", - "spring", + 'ediff', 'ediffg', 'emax', 'emin', 'encut', 'potim', 'sigma', 'enmax', + 'symprec', 'time', 'hfscreen', 'amix', 'bmix', 'amix_mag', 'bmix_mag', + 'spring' ] VASP_TAG_BOOL_LIST = [ - "lcharg", - "lsorbit", - "lwave", - "lscalapack", - "lscalu", - "lplane", - "lhfcalc", - "shiftred", - "evenonly", - "oddonly", - "addgrid", - "ldau", - "lasph", - "lclimb", - "ldneb", - "lnebcell", - "ltangentold", + 'lcharg', 'lsorbit', 'lwave', 'lscalapack', 'lscalu', 'lplane', 'lhfcalc', + 'shiftred', 'evenonly', 'oddonly', 'addgrid', 'ldau', 'lasph', 'lclimb', + 'ldneb', 'lnebcell', 'ltangentold' ] # Site-wise list of arrays of FLOAT -VASP_TAG_SITEF_LIST = ["magmom", "rwigs"] +VASP_TAG_SITEF_LIST = ['magmom', 'rwigs'] # Species-wise list of arrays of FLOAT -VASP_TAG_SPECF_LIST = ["ldauu", "ldauj"] +VASP_TAG_SPECF_LIST = ['ldauu', 'ldauj'] # Site-wise list of arrays of INT -VASP_TAG_SPECI_LIST = ["ldaul"] +VASP_TAG_SPECI_LIST = ['ldaul'] VASP_TAG_STRING_LIST = [ - "algo", "prec", "system", "precfock", "lreal", "metagga" + 'algo', 'prec', 'system', 'precfock', 'lreal', 'metagga' ] # The master list of VASP tags is a union of the above -> need to allow for 'miscellaneous' ? -VASP_TAG_LIST = (VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + - VASP_TAG_SPECI_LIST + VASP_TAG_BOOL_LIST + - VASP_TAG_FLOAT_LIST + VASP_TAG_STRING_LIST + - VASP_TAG_SPECF_LIST) +VASP_TAG_LIST = VASP_TAG_INT_LIST + VASP_TAG_SITEF_LIST + VASP_TAG_SPECI_LIST + \ + VASP_TAG_BOOL_LIST + VASP_TAG_FLOAT_LIST + \ + VASP_TAG_STRING_LIST + VASP_TAG_SPECF_LIST class IncarError(Exception): @@ -115,7 +56,7 @@ class Incar(object): tags: a dict of all INCAR settings All input tags and associated values are stored as key-value pairs in the dicionary called 'tags'. - """ + """ def __init__(self, filename, @@ -123,7 +64,7 @@ def __init__(self, poscar=None, sort=True, structure_info=None): - """Construct an Incar object from 'filename'""" + """ Construct an Incar object from 'filename'""" self.read(filename, species, poscar, sort, structure_info) def read(self, @@ -132,16 +73,16 @@ def read(self, poscar=None, sort=True, structure_info=None): - """Read an INCAR file""" + """ Read an INCAR file """ self.tags = dict() try: - file = open(filename, "r") + file = open(filename, 'r') except: raise IncarError("Could not open file: '" + filename + "'") # parse INCAR into self.tags for line in file: - line = re.split("=", re.split("#", line)[0]) + line = re.split('=', re.split('#', line)[0]) if len(line) == 2: self.tags[line[0].strip()] = line[1].strip() self._verify_tags() @@ -153,7 +94,7 @@ def read(self, file.close() def _make_natural_type(self): - """Convert self.tags values from strings into their 'natural type' (int, float, etc.)""" + """ Convert self.tags values from strings into their 'natural type' (int, float, etc.) """ for tag in self.tags: if self.tags[tag] is None or str(self.tags[tag]).strip() == "": self.tags[tag] = None @@ -167,29 +108,29 @@ def _make_natural_type(self): elif tag.lower() in VASP_TAG_FLOAT_LIST: try: self.tags[tag] = float(self.tags[tag].lower().replace( - "d", "e")) + 'd', 'e')) except ValueError: raise IncarError("Could not convert '" + tag + "' : '" + self.tags[tag] + "' to float") elif tag.lower() in VASP_TAG_BOOL_LIST: - if not self.tags[tag].lower() in [".true.", ".false."]: + if not self.tags[tag].lower() in ['.true.', '.false.']: raise IncarError("Could not find '" + tag + "' : '" + self.tags[tag].lower() + "' in ['.true.','.false.']") else: - self.tags[tag] = self.tags[tag].lower() == ".true." + self.tags[tag] = (self.tags[tag].lower() == '.true.') elif tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: temp = [] for value in self.tags[tag].split(): try: - item = value.split("*") + item = value.split('*') if len(item) == 1: temp.append(float(value)) else: if item[0] != 0: temp.append( - str(item[0]) + "*" + + str(item[0]) + '*' + str(float(item[1]))) except ValueError: raise IncarError("Could not convert '" + tag + @@ -210,39 +151,22 @@ def _make_natural_type(self): self._check_string_tag(tag, self.tags[tag]) def _check_string_tag(self, tag, value): - """Check that string-valued tags are allowed values""" - if tag.lower() == "prec": + """ Check that string-valued tags are allowed values """ + if tag.lower() == 'prec': if value.lower() not in [ - "low", - "medium", - "high", - "normal", - "single", - "accurate", + 'low', 'medium', 'high', 'normal', 'single', 'accurate' ]: raise IncarError("Unknown 'prec' value: '" + value) - elif tag.lower() == "algo": + elif tag.lower() == 'algo': if value.lower() not in [ - "normal", - "veryfast", - "fast", - "conjugate", - "all", - "damped", - "subrot", - "eigenval", - "none", - "nothing", - "chi", - "gw0", - "gw", - "scgw0", - "scgw", + 'normal', 'veryfast', 'fast', 'conjugate', 'all', 'damped', + 'subrot', 'eigenval', 'none', 'nothing', 'chi', 'gw0', + 'gw', 'scgw0', 'scgw' ]: raise IncarError("Unknown 'algo' value: '" + value) def _verify_tags(self): - """Check that only allowed INCAR tags are in self.tags""" + """ Check that only allowed INCAR tags are in self.tags """ for tag in self.tags: if tag.lower() in VASP_TAG_LIST: continue @@ -251,17 +175,16 @@ def _verify_tags(self): "' with value '" + str(self.tags[tag]) + "'")) def update(self, species, poscar, sort=True, structure_info=None): - """Update Incar object to reflect Species settings""" + """ Update Incar object to reflect Species settings """ - # TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. - # TODO: Need a better update function, it's very construed and difficult to understand. Apologies! + #TODO: Need a way to deal with conflicting dof setups. There won't be an issue if user isn't stupid. + #TODO: Need a better update function, it's very construed and difficult to understand. Apologies! if structure_info is not None and structure_info.atom_properties is not None: if "Cmagspin" in list(structure_info.atom_properties.keys()): vasp_input_tags_to_append = attribute_classes.CmagspinAttr( structure_info).vasp_input_tags() self.tags.update(vasp_input_tags_to_append) - if "Cunitmagspin" in list(structure_info.atom_properties.keys()): vasp_input_tags_to_append = attribute_classes.CunitmagspinAttr( structure_info).vasp_input_tags() @@ -276,7 +199,7 @@ def update(self, species, poscar, sort=True, structure_info=None): # for each 'tag' in the IndividualSpecies, create a list in self.tags for key in list(species.values())[0].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0.0 + self.tags[key] = 0. for site in poscar.basis: self.tags[key] += float( species[site.occupant].tags[key]) @@ -300,7 +223,7 @@ def update(self, species, poscar, sort=True, structure_info=None): for key in list(species.values())[0].tags.keys(): # for key in species[species.keys()[0]].tags.keys(): if key.lower() in (VASP_TAG_INT_LIST + VASP_TAG_FLOAT_LIST): - self.tags[key] = 0.0 + self.tags[key] = 0. for site in poscar.basis: self.tags[key] += float( species[site.occupant].tags[key]) @@ -315,8 +238,8 @@ def update(self, species, poscar, sort=True, structure_info=None): # for species-specific tags, use the value specified for the # species whose pseudopotential is being used for this alias for name in species.keys(): - if (species[name].alias == alias - and species[name].write_potcar): + if species[name].alias == alias and species[ + name].write_potcar: self.tags[key].append( species[name].tags[key]) break @@ -334,7 +257,7 @@ def update(self, species, poscar, sort=True, structure_info=None): def write(self, filename): try: - incar_write = open(filename, "w") + incar_write = open(filename, 'w') except IOError as e: raise e for tag in self.tags: @@ -342,17 +265,17 @@ def write(self, filename): pass else: if tag.lower() in VASP_TAG_SITEF_LIST + VASP_TAG_SPECF_LIST: - incar_write.write("{} = {}\n".format( + incar_write.write('{} = {}\n'.format( tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_SPECI_LIST: - incar_write.write("{} = {}\n".format( + incar_write.write('{} = {}\n'.format( tag.upper(), remove_chars(self.tags[tag], "[\[\],']"))) elif tag.lower() in VASP_TAG_BOOL_LIST: if self.tags[tag] == True: - incar_write.write("{} = .TRUE.\n".format(tag.upper())) + incar_write.write('{} = .TRUE.\n'.format(tag.upper())) else: - incar_write.write("{} = .FALSE.\n".format(tag.upper())) + incar_write.write('{} = .FALSE.\n'.format(tag.upper())) else: - incar_write.write("{} = {}\n".format( + incar_write.write('{} = {}\n'.format( tag.upper(), self.tags[tag])) incar_write.close() From 23c40f9fdcb54b041227ad73a31083fe3525cec0 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Tue, 25 Apr 2023 17:10:11 -0700 Subject: [PATCH 09/18] lnoncollinear tag support in INCAR --- casm/casm/vasp/io/attribute_classes.py | 4 ++-- casm/casm/vasp/io/incar.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 6478867..73f5e25 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -104,8 +104,8 @@ def vasp_input_tags(self, sort=True): return dict(MAGMOM=incar_magmom_str, ISPIN=2, - LSORBIT=".TRUE.", - LNONCOLLINEAR=".TRUE.") + LSORBIT=True, + LNONCOLLINEAR=True) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index fe77a34..2789a82 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -22,7 +22,7 @@ VASP_TAG_BOOL_LIST = [ 'lcharg', 'lsorbit', 'lwave', 'lscalapack', 'lscalu', 'lplane', 'lhfcalc', 'shiftred', 'evenonly', 'oddonly', 'addgrid', 'ldau', 'lasph', 'lclimb', - 'ldneb', 'lnebcell', 'ltangentold' + 'ldneb', 'lnebcell', 'ltangentold', 'lnoncollinear' ] # Site-wise list of arrays of FLOAT VASP_TAG_SITEF_LIST = ['magmom', 'rwigs'] From 4b38b04835533824da2c472ebc32f1ab4b365c1f Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Tue, 25 Apr 2023 17:19:17 -0700 Subject: [PATCH 10/18] use existing MAGMOM converter to string --- casm/casm/vasp/io/attribute_classes.py | 45 ++------------------------ 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 73f5e25..eda65d5 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,39 +1,5 @@ -import math import numpy as np - -def get_incar_magmom_from_magmom_values(magmom_values): - """Returns an INCAR magmom string from a - list of magmom values. The magmom values should - be listed in the order of atoms in POSCAR - - Parameters - ---------- - magmom_values : np.ndarray - - Returns - ------- - str - - """ - - magmom = "" - for i, value in enumerate(magmom_values): - if i == 0: - number_of_same_magmoms = 1 - elif math.isclose(value, magmom_values[i - 1]): - number_of_same_magmoms += 1 - else: - magmom += (str(number_of_same_magmoms) + "*" + - str(magmom_values[i - 1]) + " ") - number_of_same_magmoms = 1 - if i == len(magmom_values) - 1: - magmom += str(number_of_same_magmoms) + "*" + str( - magmom_values[i]) + " " - - return magmom - - class SOunitmagspinAttr: """Class containing information specific to Cmagspin dof. This object will be constructed from casm.project.structure.StructureInfo class @@ -100,9 +66,7 @@ def vasp_input_tags(self, sort=True): magmom_values = np.ravel( np.array([atom_prop["value"] for atom_prop in self.atom_props])) - incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) - - return dict(MAGMOM=incar_magmom_str, + return dict(MAGMOM=magmom_values, ISPIN=2, LSORBIT=True, LNONCOLLINEAR=True) @@ -212,9 +176,7 @@ def vasp_input_tags(self, sort=True): magmom_values = np.ravel( np.array([atom_prop["value"] for atom_prop in self.atom_props])) - incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) - - return dict(MAGMOM=incar_magmom_str, ISPIN=2) + return dict(MAGMOM=magmom_values, ISPIN=2) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): @@ -319,9 +281,8 @@ def vasp_input_tags(self, sort=True): magmom_values = np.ravel( np.array([atom_prop["value"] for atom_prop in self.atom_props])) - incar_magmom_str = get_incar_magmom_from_magmom_values(magmom_values) - return dict(MAGMOM=incar_magmom_str, ISPIN=2) + return dict(MAGMOM=magmom_values, ISPIN=2) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): From 5fa2d126db2be5fa7e1058d05a72cea4336cd47c Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 8 May 2023 12:14:57 -0700 Subject: [PATCH 11/18] Fix magmom value writing for Cunitmagspin --- casm/casm/vasp/io/attribute_classes.py | 91 ++++++++++++++------------ 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index eda65d5..a181534 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,5 +1,6 @@ import numpy as np + class SOunitmagspinAttr: """Class containing information specific to Cmagspin dof. This object will be constructed from casm.project.structure.StructureInfo class @@ -29,17 +30,18 @@ def __init__(self, structure_info): if "SOunitmagspin" not in list(structure_info.atom_properties.keys()): raise RuntimeError( "Could not construct SOunitmagspinAttr class. " - "Check if you're dealing with Cmagspin dof calculations.") - - self.atom_props = [{ - "site_index": site_index, - "atom": atom_type, - "value": magmom_value - } for site_index, (atom_type, magmom_value) in enumerate( - zip( - structure_info.atom_type, - structure_info.atom_properties["SOunitmagspin"]["value"], - ))] + "Check if you're dealing with Cmagspin dof calculations." + ) + + self.atom_props = [ + {"site_index": site_index, "atom": atom_type, "value": magmom_value} + for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["SOunitmagspin"]["value"], + ) + ) + ] def vasp_input_tags(self, sort=True): """Returns a dictionary of MAGMOM, ISPIN input tags @@ -64,12 +66,10 @@ def vasp_input_tags(self, sort=True): self.atom_props.sort(key=lambda x: x["atom"]) magmom_values = np.ravel( - np.array([atom_prop["value"] for atom_prop in self.atom_props])) + np.array([atom_prop["value"] for atom_prop in self.atom_props]) + ) - return dict(MAGMOM=magmom_values, - ISPIN=2, - LSORBIT=True, - LNONCOLLINEAR=True) + return dict(MAGMOM=magmom_values, ISPIN=2, LSORBIT=True, LNONCOLLINEAR=True) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): @@ -139,17 +139,18 @@ def __init__(self, structure_info): if "Cunitmagspin" not in list(structure_info.atom_properties.keys()): raise RuntimeError( "Could not construct CunitmagspinAttr class. " - "Check if you're dealing with Cmagspin dof calculations.") - - self.atom_props = [{ - "site_index": site_index, - "atom": atom_type, - "value": magmom_value - } for site_index, (atom_type, magmom_value) in enumerate( - zip( - structure_info.atom_type, - structure_info.atom_properties["Cunitmagspin"]["value"], - ))] + "Check if you're dealing with Cmagspin dof calculations." + ) + + self.atom_props = [ + {"site_index": site_index, "atom": atom_type, "value": magmom_value} + for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["Cunitmagspin"]["value"], + ) + ) + ] def vasp_input_tags(self, sort=True): """Returns a dictionary of MAGMOM, ISPIN input tags @@ -174,7 +175,8 @@ def vasp_input_tags(self, sort=True): self.atom_props.sort(key=lambda x: x["atom"]) magmom_values = np.ravel( - np.array([atom_prop["value"] for atom_prop in self.atom_props])) + np.array([atom_prop["value"] for atom_prop in self.atom_props]) + ) return dict(MAGMOM=magmom_values, ISPIN=2) @@ -210,9 +212,12 @@ def vasp_output_dictionary(outcar, unsort_dict): output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) for i in range(len(outcar.mag)): output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] - output["Cunitmagspin"]["value"][unsort_dict[i]] = [ - outcar.mag[i] / abs(outcar.mag[i]) - ] + if outcar.mag[i] < 1: + output["Cunitmagspin"]["value"][unsort_dict[i]] = [0.0] + else: + output["Cunitmagspin"]["value"][unsort_dict[i]] = [ + outcar.mag[i] / abs(outcar.mag[i]) + ] return output @@ -246,16 +251,17 @@ def __init__(self, structure_info): if "Cmagspin" not in list(structure_info.atom_properties.keys()): raise RuntimeError( "Could not construct CmagspinAttr class. " - "Check if you're dealing with Cmagspin dof calculations.") - self.atom_props = [{ - "site_index": site_index, - "atom": atom_type, - "value": magmom_value - } for site_index, (atom_type, magmom_value) in enumerate( - zip( - structure_info.atom_type, - structure_info.atom_properties["Cmagspin"]["value"], - ))] + "Check if you're dealing with Cmagspin dof calculations." + ) + self.atom_props = [ + {"site_index": site_index, "atom": atom_type, "value": magmom_value} + for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["Cmagspin"]["value"], + ) + ) + ] def vasp_input_tags(self, sort=True): """Returns a dictionary of MAGMOM, ISPIN input tags @@ -280,7 +286,8 @@ def vasp_input_tags(self, sort=True): self.atom_props.sort(key=lambda x: x["atom"]) magmom_values = np.ravel( - np.array([atom_prop["value"] for atom_prop in self.atom_props])) + np.array([atom_prop["value"] for atom_prop in self.atom_props]) + ) return dict(MAGMOM=magmom_values, ISPIN=2) From efe3f71b04d94ae8a78ce1ad248dfc1312086599 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Mon, 8 May 2023 12:16:34 -0700 Subject: [PATCH 12/18] Cunitmagspin magmom writing fixes --- casm/casm/vasp/io/attribute_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index a181534..a007b00 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -212,7 +212,7 @@ def vasp_output_dictionary(outcar, unsort_dict): output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) for i in range(len(outcar.mag)): output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] - if outcar.mag[i] < 1: + if abs(outcar.mag[i]) < 1: output["Cunitmagspin"]["value"][unsort_dict[i]] = [0.0] else: output["Cunitmagspin"]["value"][unsort_dict[i]] = [ From 756c1005585ad5ab435ed5f79efe3e633bb93a1c Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Thu, 29 Feb 2024 11:11:06 -0800 Subject: [PATCH 13/18] added input file support for NCunitmagspin --- casm/casm/vasp/io/attribute_classes.py | 109 +++++++++++++++++++++++++ casm/casm/vasp/io/incar.py | 5 ++ 2 files changed, 114 insertions(+) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index a007b00..a1ab1aa 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,6 +1,115 @@ import numpy as np +class NCunitmagspinAttr: + """Class containing information specific to Cmagspin dof. + This object will be constructed from casm.project.structure.StructureInfo class + which digests its information. + + self.atom_props: List[Dict] - Contains the list of atom properites (with atom name and it's value) + Look at the example below for detailed description of object properties + + Consider the example of NaFeO2 with Fe having a +5 magnetic moment and rest all being 0 + self.atom_props: + [ + {"site_index":0, "atom": "Na", "value":0}, + {"site_index":1, "atom":"Fe", "value":5}, + {"site_index":2, "atom":"O","value":0}, + {"site_index":3, "atom":"O","value":0} + ] + """ + + def __init__(self, structure_info): + """Constructs the CmagspinAttr object from StructureInfo object + + Parameters + ---------- + structure_info : casm.project.structure.StructureInfo + + """ + if "NCunitmagspin" not in list(structure_info.atom_properties.keys()): + raise RuntimeError( + "Could not construct NCunitmagspinAttr class. " + "Check if you're dealing with Cmagspin dof calculations." + ) + + self.atom_props = [ + {"site_index": site_index, "atom": atom_type, "value": magmom_value} + for site_index, (atom_type, magmom_value) in enumerate( + zip( + structure_info.atom_type, + structure_info.atom_properties["NCunitmagspin"]["value"], + ) + ) + ] + + def vasp_input_tags(self, sort=True): + """Returns a dictionary of MAGMOM, ISPIN input tags + specific to collinear magnetic VASP calculations. + + Parameters + ---------- + sort: bool, optional + This should match the sort used to write + POSCAR file (whether the basis atoms are sorted)) + + Returns + ------- + dict + { + "MAGMOM": magmom_string, + "ISPIN": 2 + } + + """ + if sort is True: + self.atom_props.sort(key=lambda x: x["atom"]) + + magmom_values = np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props]) + ) + + return dict(MAGMOM=magmom_values, ISPIN=2, LSORBIT=False, LNONCOLLINEAR=True) + + @staticmethod + def vasp_output_dictionary(outcar, unsort_dict): + """Returns the attribute specific vasp output + dictionary which can be updated to the whole output + dictionary which will be printed as properties.calc.json. + For Cmagspin, this will be magnetic moment of each individual species + + Parameters + ---------- + outcar : casm.vasp.io.outcar + Outcar containing magmom information + unsort_dict : dict + ``Poscar.unsort_dict()`` useful for reordering + the magmom values + + Returns + ------- + dict + { + "Cmagspin":{ + "value":[] + } + } + + """ + output = {} + # output["Cmagspin"] = {} + # output["Cmagspin"]["value"] = [None] * len(unsort_dict) + # output["Cunitmagspin"] = {} + # output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) + # for i in range(len(outcar.mag)): + # output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] + # output["Cunitmagspin"]["value"][unsort_dict[i]] = [ + # outcar.mag[i] / abs(outcar.mag[i]) + # ] + + raise NotImplementedError("Not implemented this part yet") + + class SOunitmagspinAttr: """Class containing information specific to Cmagspin dof. This object will be constructed from casm.project.structure.StructureInfo class diff --git a/casm/casm/vasp/io/incar.py b/casm/casm/vasp/io/incar.py index 2789a82..aef78eb 100644 --- a/casm/casm/vasp/io/incar.py +++ b/casm/casm/vasp/io/incar.py @@ -190,6 +190,11 @@ def update(self, species, poscar, sort=True, structure_info=None): structure_info).vasp_input_tags() self.tags.update(vasp_input_tags_to_append) + if "NCunitmagspin" in list(structure_info.atom_properties.keys()): + vasp_input_tags_to_append = attribute_classes.NCunitmagspinAttr( + structure_info).vasp_input_tags() + self.tags.update(vasp_input_tags_to_append) + if "SOunitmagspin" in list(structure_info.atom_properties.keys()): vasp_input_tags_to_append = attribute_classes.SOunitmagspinAttr( structure_info).vasp_input_tags() From 301606573b704d8ada26a7d0f4b0b2d90a4359d6 Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Thu, 29 Feb 2024 11:47:23 -0800 Subject: [PATCH 14/18] test --- casm/casm/vasp/io/attribute_classes.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index a1ab1aa..e9222b3 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -283,8 +283,11 @@ def vasp_input_tags(self, sort=True): if sort is True: self.atom_props.sort(key=lambda x: x["atom"]) - magmom_values = np.ravel( - np.array([atom_prop["value"] for atom_prop in self.atom_props]) + magmom_values = "".join( + str(x) + for x in np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props]) + ) ) return dict(MAGMOM=magmom_values, ISPIN=2) From e20bfa7dd82cc68a7bf72773cb593a14586a66ce Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Thu, 29 Feb 2024 11:49:09 -0800 Subject: [PATCH 15/18] test --- casm/casm/vasp/io/attribute_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index e9222b3..363a4f9 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -284,7 +284,7 @@ def vasp_input_tags(self, sort=True): self.atom_props.sort(key=lambda x: x["atom"]) magmom_values = "".join( - str(x) + str(x) + " " for x in np.ravel( np.array([atom_prop["value"] for atom_prop in self.atom_props]) ) From 8e1b726e6740793b95a190828b483ce2e7fd95ae Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Thu, 29 Feb 2024 11:56:11 -0800 Subject: [PATCH 16/18] test --- casm/casm/vasp/io/attribute_classes.py | 38 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 363a4f9..a2798bf 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -1,6 +1,35 @@ import numpy as np +def get_incar_magmom_from_magmom_values(magmom_values): + """Returns an INCAR magmom string from a + list of magmom values. The magmom values should + be listed in the order of atoms in POSCAR + Parameters + ---------- + magmom_values : np.ndarray + Returns + ------- + str + """ + + magmom = "" + for i, value in enumerate(magmom_values): + if i == 0: + number_of_same_magmoms = 1 + elif np.isclose(value, magmom_values[i - 1]): + number_of_same_magmoms += 1 + else: + magmom += ( + str(number_of_same_magmoms) + "*" + str(magmom_values[i - 1]) + " " + ) + number_of_same_magmoms = 1 + if i == len(magmom_values) - 1: + magmom += str(number_of_same_magmoms) + "*" + str(magmom_values[i]) + " " + + return magmom + + class NCunitmagspinAttr: """Class containing information specific to Cmagspin dof. This object will be constructed from casm.project.structure.StructureInfo class @@ -283,14 +312,11 @@ def vasp_input_tags(self, sort=True): if sort is True: self.atom_props.sort(key=lambda x: x["atom"]) - magmom_values = "".join( - str(x) + " " - for x in np.ravel( - np.array([atom_prop["value"] for atom_prop in self.atom_props]) - ) + magmom_values = np.ravel( + np.array([atom_prop["value"] for atom_prop in self.atom_props]) ) - return dict(MAGMOM=magmom_values, ISPIN=2) + return dict(MAGMOM=get_incar_magmom_from_magmom_values(magmom_values), ISPIN=2) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): From a28ace5cdb66b5c7378bb70635dde46c41f9d0db Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Thu, 29 Feb 2024 12:02:21 -0800 Subject: [PATCH 17/18] revert back to my own magmom writer --- casm/casm/vasp/io/attribute_classes.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index a2798bf..319b5ad 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -98,7 +98,12 @@ def vasp_input_tags(self, sort=True): np.array([atom_prop["value"] for atom_prop in self.atom_props]) ) - return dict(MAGMOM=magmom_values, ISPIN=2, LSORBIT=False, LNONCOLLINEAR=True) + return dict( + MAGMOM=get_incar_magmom_from_magmom_values(magmom_values), + ISPIN=2, + LSORBIT=False, + LNONCOLLINEAR=True, + ) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): @@ -207,7 +212,12 @@ def vasp_input_tags(self, sort=True): np.array([atom_prop["value"] for atom_prop in self.atom_props]) ) - return dict(MAGMOM=magmom_values, ISPIN=2, LSORBIT=True, LNONCOLLINEAR=True) + return dict( + MAGMOM=get_incar_magmom_from_magmom_values(magmom_values), + ISPIN=2, + LSORBIT=True, + LNONCOLLINEAR=True, + ) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): @@ -427,7 +437,7 @@ def vasp_input_tags(self, sort=True): np.array([atom_prop["value"] for atom_prop in self.atom_props]) ) - return dict(MAGMOM=magmom_values, ISPIN=2) + return dict(MAGMOM=get_incar_magmom_from_magmom_values(magmom_values), ISPIN=2) @staticmethod def vasp_output_dictionary(outcar, unsort_dict): From b792bbb9d7460526f02338c26d69da37c6a616cb Mon Sep 17 00:00:00 2001 From: seshasaibehara Date: Fri, 1 Mar 2024 17:23:02 -0800 Subject: [PATCH 18/18] cunitmagspin magmom writer lower threshold for making magmom 0.0 --- casm/casm/vasp/io/attribute_classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/casm/casm/vasp/io/attribute_classes.py b/casm/casm/vasp/io/attribute_classes.py index 319b5ad..c6118bd 100644 --- a/casm/casm/vasp/io/attribute_classes.py +++ b/casm/casm/vasp/io/attribute_classes.py @@ -360,7 +360,7 @@ def vasp_output_dictionary(outcar, unsort_dict): output["Cunitmagspin"]["value"] = [None] * len(unsort_dict) for i in range(len(outcar.mag)): output["Cmagspin"]["value"][unsort_dict[i]] = [outcar.mag[i]] - if abs(outcar.mag[i]) < 1: + if abs(outcar.mag[i]) < 0.5: output["Cunitmagspin"]["value"][unsort_dict[i]] = [0.0] else: output["Cunitmagspin"]["value"][unsort_dict[i]] = [