Skip to content

Commit

Permalink
Merge aa4fac8 into 84baecd
Browse files Browse the repository at this point in the history
  • Loading branch information
OMalenfantThuot committed Oct 18, 2019
2 parents 84baecd + aa4fac8 commit 3f6f922
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -5,6 +5,6 @@ install:
- pip install -r requirements_dev.txt
- pip install -e .
script:
- pytest --cov=mlcalcdriver
- pytest --cov=mlcalcdriver -v
after_success:
- coveralls
85 changes: 85 additions & 0 deletions mlcalcdriver/base/posinp.py
Expand Up @@ -194,6 +194,78 @@ def _from_lines(cls, lines):
atoms.append(Atom(atom_type, position))
return cls(atoms, units, boundary_conditions, cell=cell)

@classmethod
def from_dict(cls, posinp):
r"""
Initialize the input positions from a dictionary.
Parameters
----------
posinp : dict
Posinp as a dictionary coming from an InputParams or
Logfile instance.
Returns
-------
Posinp
Posinp initialized from an dictionary.
>>> pos_dict = {
... "units": "reduced",
... "cell": [8.07007483423, 'inf', 4.65925987792],
... "positions": [
... {'C': [0.08333333333, 0.5, 0.25]},
... {'C': [0.41666666666, 0.5, 0.25]},
... {'C': [0.58333333333, 0.5, 0.75]},
... {'C': [0.91666666666, 0.5, 0.75]},
... ]
... }
>>> pos = Posinp.from_dict(pos_dict)
>>> pos.boundary_conditions
'surface'
The value of the "cell" key allows to derive the boundary
conditions. Replacing 'inf' by a number gives a posinp with
periodic boundary conditions:
>>> pos_dict["cell"] = [8.07007483423, 10.0, 4.65925987792]
>>> pos = Posinp.from_dict(pos_dict)
>>> pos.boundary_conditions
'periodic'
If there is no "cell" key, then the boundary conditions are set
to "free". Here, given that the units are reduced, this raises
a ValueError:
>>> del pos_dict["cell"]
>>> pos = Posinp.from_dict(pos_dict)
Traceback (most recent call last):
...
ValueError: Cannot use reduced units with free boundary conditions
"""
# Lower the keys of the posinp if needed
if "positions" not in posinp:
ref_posinp = deepcopy(posinp)
for key in ref_posinp:
posinp[key.lower()] = ref_posinp[key]
del posinp[key]
# Read data from the dictionary
atoms = [] # atomic positions
for atom in posinp["positions"]:
atoms.append(Atom.from_dict(atom))
units = posinp.get("units", "atomic") # Units of the coordinates
cell = posinp.get("cell") # Simulation cell size
# Infer the boundary conditions from the value of cell
if cell is None:
boundary_conditions = "free"
else:
if cell[1] in [".inf", "inf"]:
boundary_conditions = "surface"
else:
boundary_conditions = "periodic"
return cls(atoms, units, boundary_conditions, cell=cell)

@property
def atoms(self):
r"""
Expand Down Expand Up @@ -551,6 +623,19 @@ def __init__(self, atom_type, position):
self.position = position
self.mass = ATOMS_MASS[self.type]

@classmethod
def from_dict(cls, atom_dict):
r"""
Parameters
----------
atom_dict : dict
Information about an atom given by a dict whose only key is
the atom type and the value is the atomic position. This
format is mainly found in bigdft logfiles.
"""
[(atom_type, position)] = atom_dict.items()
return cls(atom_type, position)

@property
def type(self):
r"""
Expand Down
23 changes: 23 additions & 0 deletions mlcalcdriver/interfaces/ase_interface.py
@@ -1,4 +1,6 @@
import ase
import numpy as np
from mlcalcdriver.base import Posinp

def posinp_to_ase_atoms(posinp):
symbols, positions, masses = "", [], []
Expand All @@ -16,3 +18,24 @@ def posinp_to_ase_atoms(posinp):
symbols=symbols, positions=positions, masses=masses, cell=posinp.cell, pbc=pbc
)
return atoms

def ase_atoms_to_posinp(atoms):
pos_dict = {"units": "angstroem"}
positions = []
for at in atoms:
positions.append({at.symbol: at.position})
cell = atoms.get_cell()
print(cell)
if cell.orthorhombic:
if (cell==0.0).all():
new_cell = None
elif cell[1,1] in [0.0, np.inf]:
new_cell = [cell[0,0], str(np.inf), cell[2,2]]
else:
new_cell = [dim[i] for i, dim in enumerate(cell)]
else:
raise NotImplementedError("Non orthorhombic cells are not supported yet.")
print(new_cell)
pos_dict["positions"] = positions
pos_dict["cell"] = new_cell
return Posinp.from_dict(pos_dict)
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -7,7 +7,7 @@

setup(
name="mlcalcdriver",
version="0.1.0",
version="0.2.0",
author="Olivier Malenfant-Thuot",
author_email="malenfantthuotolivier@gmail.com",
description="A package to drive atomic calculations using machine learned models.",
Expand Down
6 changes: 6 additions & 0 deletions tests/posinp_files/periodic.xyz
@@ -0,0 +1,6 @@
4 angstroem
periodic 8.07007483423 8.9 4.65925987792
C 0.08333333333 0.5 0.25
C 0.41666666666 0.5 0.25
C 0.58333333333 0.5 0.75
C 0.91666666666 0.5 0.75
6 changes: 6 additions & 0 deletions tests/posinp_files/surface2.xyz
@@ -0,0 +1,6 @@
4 angstroem
surface 8.07007483423 .inf 4.65925987792
C 0.08333333333 0.5 0.25
C 0.41666666666 0.5 0.25
C 0.58333333333 0.5 0.75
C 0.91666666666 0.5 0.75
26 changes: 26 additions & 0 deletions tests/test_interface.py
@@ -0,0 +1,26 @@
import os
import pytest
from mlcalcdriver import Posinp
from mlcalcdriver.interfaces import posinp_to_ase_atoms, ase_atoms_to_posinp

pos_folder = "tests/posinp_files/"

class TestConversion:

def test_mol(self):
pos1 = Posinp.from_file(os.path.join(pos_folder, "N2.xyz"))
atoms = posinp_to_ase_atoms(pos1)
pos2 = ase_atoms_to_posinp(atoms)
assert pos1 == pos2

def test_surface(self):
pos1 = Posinp.from_file(os.path.join(pos_folder, "surface2.xyz"))
atoms = posinp_to_ase_atoms(pos1)
pos2 = ase_atoms_to_posinp(atoms)
assert pos1 == pos2

def test_periodic(self):
pos1 = Posinp.from_file(os.path.join(pos_folder, "periodic.xyz"))
atoms = posinp_to_ase_atoms(pos1)
pos2 = ase_atoms_to_posinp(atoms)
assert pos1 == pos2

0 comments on commit 3f6f922

Please sign in to comment.