Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions casm/tools/shared/ase_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,58 @@ def make_ase_atoms(casm_structure: xtal.Structure) -> ase.Atoms:
)


def make_magnetic_ase_atoms(
casm_structure: xtal.Structure, magmom_dict: dict
) -> ase.Atoms:
"""Given a CASM Structure and magnetic moments to be used for each specific species,
convert it to an ASE Atoms. If a magnetic moment is not provided for a species, it is
set to 1.0

.. attention::

This method only works for atomic structures where magnetic moments are not explicit
degrees of freedom and are only added at the setup stage.
If the structure contains molecular information, an error will be raised.

Notes
-----

This method converts a CASM Structure object to an ASE Atoms object. It includes:

- the lattice vectors
- the atomic positions
- the atomic types

Parameters
----------
casm_structure : libcasm.xtal.Structure
magmom_dict : dict

Returns
-------
ase.Atoms

"""
if len(casm_structure.mol_type()):
raise ValueError(
"Error: only non-magnetic atomic structures may be converted using "
"to_ase_atoms"
)

symbols = casm_structure.atom_type()
positions = casm_structure.atom_coordinate_cart().transpose()
cell = casm_structure.lattice().column_vector_matrix().transpose()

magmoms = [
magmom_dict[symbol] if symbol in magmom_dict.keys() else 1.0
for symbol in symbols
]

return ase.Atoms(
symbols=symbols, positions=positions, cell=cell, pbc=True, magmoms=magmoms
)


def make_casm_structure(ase_atoms: ase.Atoms) -> xtal.Structure:
"""Given an ASE Atoms, convert it to a CASM Structure

Expand Down Expand Up @@ -351,11 +403,16 @@ def __init__(
incar_path = None
kpoints_path = None
settings = {}
species_settings = {}
if self.calctype_settings_dir is not None:
settings = json_io.read_required(
path=self.calctype_settings_dir / "calc.json"
)

species_settings = json_io.read_optional(
path=self.calctype_settings_dir / "species.json", default={}
)

incar_path = calctype_settings_dir / "INCAR"
if not incar_path.exists():
incar_path = None
Expand All @@ -369,15 +426,20 @@ def __init__(
calctype_settings_dir, which will be passed to the
:class:`ase.calculators.vasp.Vasp` calculator constructor."""

self.species_settings = species_settings

self.incar_path = incar_path
"""Optional[pathlib.Path]: Path to the template INCAR file, if it exists."""

self.kpoints_path = kpoints_path
"""Optional[pathlib.Path]: Path to the template KPOINTS file, if it exists."""

### Functions to convert between CASM Structure and ASE Atoms
if "magmoms" in species_settings.keys():
self._make_ase_atoms_f = make_magnetic_ase_atoms

self._make_ase_atoms_f = make_ase_atoms_f
else:
self._make_ase_atoms_f = make_ase_atoms_f
"""Callable[[libcasm.xtal.Structure], ase.Atoms]: Function to convert CASM
Structure to ASE Atoms."""

Expand Down Expand Up @@ -447,7 +509,13 @@ def setup(
vasp_calculator: ase.calculators.vasp.Vasp
The ASE VASP calculator object.
"""
ase_atoms = self._make_ase_atoms_f(casm_structure)
if "magmoms" in self.species_settings.keys():
ase_atoms = self._make_ase_atoms_f(
casm_structure, magmom_dict=self.species_settings["magmoms"]
)

else:
ase_atoms = self._make_ase_atoms_f(casm_structure)
vasp_calculator = self.make_calculator(ase_atoms=ase_atoms, calc_dir=calc_dir)

# Write INCAR, KPOINTS, POTCAR, POSCAR
Expand Down