Skip to content

Commit

Permalink
Merge pull request #1285 from nmounet/fix_1028_structure_to_pymagen
Browse files Browse the repository at this point in the history
Fix 1028 structure to pymagen
  • Loading branch information
giovannipizzi authored Mar 15, 2018
2 parents 708ca95 + cae4b24 commit 10dc160
Show file tree
Hide file tree
Showing 2 changed files with 421 additions and 46 deletions.
297 changes: 294 additions & 3 deletions aiida/backends/tests/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,9 @@ class TestStructureDataFromAse(AiidaTestCase):

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_ase(self):
"""
Tests roundtrip ASE -> StructureData -> ASE
"""
from aiida.orm.data.structure import StructureData
import ase

Expand All @@ -1957,6 +1960,9 @@ def test_ase(self):

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_1(self):
"""
Tests roundtrip ASE -> StructureData -> ASE, with tags
"""
from aiida.orm.data.structure import StructureData
import ase

Expand Down Expand Up @@ -1987,6 +1993,10 @@ def test_conversion_of_types_1(self):

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_2(self):
"""
Tests roundtrip ASE -> StructureData -> ASE, with tags, and
changing the atomic masses
"""
from aiida.orm.data.structure import StructureData
import ase

Expand Down Expand Up @@ -2019,6 +2029,9 @@ def test_conversion_of_types_2(self):

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_3(self):
"""
Tests StructureData -> ASE, with all sorts of kind names
"""
from aiida.orm.data.structure import StructureData

a = StructureData()
Expand Down Expand Up @@ -2050,6 +2063,9 @@ def test_conversion_of_types_3(self):

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_4(self):
"""
Tests ASE -> StructureData -> ASE, in particular conversion tags / kind names
"""
from aiida.orm.data.structure import StructureData
import ase

Expand All @@ -2060,9 +2076,18 @@ def test_conversion_of_types_4(self):
s = StructureData(ase=atoms)
kindnames = set([k.name for k in s.kinds])
self.assertEquals(kindnames, set(['Fe', 'Fe1', 'Fe4']))
# check roundtrip ASE -> StructureData -> ASE
atoms2 = s.get_ase()
self.assertEquals(list(atoms2.get_tags()),list(atoms.get_tags()))
self.assertEquals(list(atoms2.get_chemical_symbols()),list(atoms.get_chemical_symbols()))
self.assertEquals(atoms2.get_chemical_formula(),'Fe5')

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_5(self):
"""
Tests ASE -> StructureData -> ASE, in particular conversion tags / kind names
(subtle variation of test_conversion_of_types_4)
"""
from aiida.orm.data.structure import StructureData
import ase

Expand All @@ -2073,6 +2098,34 @@ def test_conversion_of_types_5(self):
s = StructureData(ase=atoms)
kindnames = set([k.name for k in s.kinds])
self.assertEquals(kindnames, set(['Fe', 'Fe1', 'Fe4']))
# check roundtrip ASE -> StructureData -> ASE
atoms2 = s.get_ase()
self.assertEquals(list(atoms2.get_tags()),list(atoms.get_tags()))
self.assertEquals(list(atoms2.get_chemical_symbols()),list(atoms.get_chemical_symbols()))
self.assertEquals(atoms2.get_chemical_formula(),'Fe5')

@unittest.skipIf(not has_ase(), "Unable to import ase")
def test_conversion_of_types_6(self):
"""
Tests roundtrip StructureData -> ASE -> StructureData, with tags/kind names
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[4,0,0],[0,4,0],[0,0,4]])
a.append_atom(position=(0,0,0), symbols='Ni', name='Ni1')
a.append_atom(position=(2,2,2), symbols='Ni', name='Ni2')
a.append_atom(position=(1,0,1), symbols='Cl', name='Cl')
a.append_atom(position=(1,3,1), symbols='Cl', name='Cl')

b = a.get_ase()
self.assertEquals(b.get_chemical_symbols(), ['Ni', 'Ni', 'Cl','Cl'])
self.assertEquals(list(b.get_tags()), [1, 2, 0, 0])

c = StructureData(ase=b)
self.assertEquals(c.get_site_kindnames(), ['Ni1', 'Ni2', 'Cl','Cl'])
self.assertEquals([k.symbol for k in c.kinds], ['Ni', 'Ni', 'Cl'])
self.assertEquals([s.position for s in c.sites],
[(0.,0.,0.),(2.,2.,2.),(1.,0.,1.),(1.,3.,1.)])


class TestStructureDataFromPymatgen(AiidaTestCase):
Expand All @@ -2090,7 +2143,8 @@ class TestStructureDataFromPymatgen(AiidaTestCase):
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_1(self):
"""
Test's imput is derived from COD entry 9011963, processed with
Tests roundtrip pymatgen -> StructureData -> pymatgen
Test's input is derived from COD entry 9011963, processed with
cif_mark_disorder (from cod-tools) and abbreviated.
"""
from aiida.orm.data.structure import StructureData
Expand Down Expand Up @@ -2162,6 +2216,7 @@ def test_1(self):
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_2(self):
"""
Tests xyz -> pymatgen -> StructureData
Input source: http://pymatgen.org/_static/Molecule.html
"""
from aiida.orm.data.structure import StructureData
Expand Down Expand Up @@ -2202,6 +2257,42 @@ def test_2(self):
[round(x, 2) for x in list(struct.sites[4].position)],
[5.77, 5.89, 5.73])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_partial_occ_and_spin(self):
"""
Tests pymatgen -> StructureData, with partial occupancies and spins.
This should raise a ValueError.
"""
from aiida.orm.data.structure import StructureData
import pymatgen

Fe_spin_up = pymatgen.structure.Specie('Fe',0,properties={'spin':1})
Mn_spin_up = pymatgen.structure.Specie('Mn',0,properties={'spin':1})
Fe_spin_down = pymatgen.structure.Specie('Fe',0,properties={'spin':-1})
Mn_spin_down = pymatgen.structure.Specie('Mn',0,properties={'spin':-1})
FeMn1 = pymatgen.Composition({Fe_spin_up:0.5,Mn_spin_up:0.5})
FeMn2 = pymatgen.Composition({Fe_spin_down:0.5,Mn_spin_down:0.5})
a = pymatgen.structure.Structure(lattice=[[4,0,0],[0,4,0],[0,0,4]],
species=[FeMn1,FeMn2],
coords=[[0,0,0],[0.5,0.5,0.5]])

with self.assertRaises(ValueError):
StructureData(pymatgen=a)

# same, with vacancies
Fe1 = pymatgen.Composition({Fe_spin_up:0.5})
Fe2 = pymatgen.Composition({Fe_spin_down:0.5})
a = pymatgen.structure.Structure(lattice=[[4,0,0],[0,4,0],[0,0,4]],
species=[Fe1,Fe2],
coords=[[0,0,0],[0.5,0.5,0.5]])

with self.assertRaises(ValueError):
StructureData(pymatgen=a)


class TestPymatgenFromStructureData(AiidaTestCase):
"""
Expand All @@ -2219,7 +2310,7 @@ class TestPymatgenFromStructureData(AiidaTestCase):
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_1(self):
"""
Test the check of periodic boundary conditions.
Tests the check of periodic boundary conditions.
"""
from aiida.orm.data.structure import StructureData

Expand All @@ -2239,6 +2330,9 @@ def test_1(self):
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_2(self):
"""
Tests ASE -> StructureData -> pymatgen
"""
from aiida.orm.data.structure import StructureData
import ase

Expand Down Expand Up @@ -2274,7 +2368,8 @@ def test_2(self):
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_3(self):
"""
Test the conversion of StructureData to pymatgen's Molecule.
Tests the conversion of StructureData to pymatgen's Molecule
(ASE -> StructureData -> pymatgen)
"""
from aiida.orm.data.structure import StructureData
import ase
Expand All @@ -2299,7 +2394,203 @@ def test_3(self):
[2.0, 2.0, 2.0],
[3.0, 3.0, 3.0]])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_roundtrip(self):
"""
Tests roundtrip StructureData -> pymatgen -> StructureData
(no spins)
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[5.6,0,0],[0,5.6,0],[0,0,5.6]])
a.append_atom(position=(0,0,0), symbols='Cl')
a.append_atom(position=(2.8,0,2.8), symbols='Cl')
a.append_atom(position=(0,2.8,2.8), symbols='Cl')
a.append_atom(position=(2.8,2.8,0), symbols='Cl')
a.append_atom(position=(2.8,2.8,2.8), symbols='Na')
a.append_atom(position=(2.8,0,0), symbols='Na')
a.append_atom(position=(0,2.8,0), symbols='Na')
a.append_atom(position=(0,0,2.8), symbols='Na')

b = a.get_pymatgen()
c = StructureData(pymatgen=b)
self.assertEquals(c.get_site_kindnames(), ['Cl','Cl','Cl','Cl','Na','Na','Na','Na'])
self.assertEquals([k.symbol for k in c.kinds], ['Cl','Na'])
self.assertEquals([s.position for s in c.sites],
[(0.,0.,0.),(2.8,0,2.8),(0,2.8,2.8),(2.8,2.8,0),(2.8,2.8,2.8),(2.8,0,0),(0,2.8,0),(0,0,2.8)])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_roundtrip_kindnames(self):
"""
Tests roundtrip StructureData -> pymatgen -> StructureData
(no spins, but with all kind of kind names)
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[5.6,0,0],[0,5.6,0],[0,0,5.6]])
a.append_atom(position=(0,0,0), symbols='Cl',name='Cl')
a.append_atom(position=(2.8,0,2.8), symbols='Cl',name='Cl10')
a.append_atom(position=(0,2.8,2.8), symbols='Cl',name='Cla')
a.append_atom(position=(2.8,2.8,0), symbols='Cl',name='cl_x')
a.append_atom(position=(2.8,2.8,2.8), symbols='Na',name='Na1')
a.append_atom(position=(2.8,0,0), symbols='Na',name='Na2')
a.append_atom(position=(0,2.8,0), symbols='Na',name='Na_Na')
a.append_atom(position=(0,0,2.8), symbols='Na',name='Na4')

b = a.get_pymatgen()
self.assertEquals([site.properties['kind_name'] for site in b.sites],
['Cl','Cl10','Cla','cl_x','Na1','Na2','Na_Na','Na4'])

c = StructureData(pymatgen=b)
self.assertEquals(c.get_site_kindnames(), ['Cl','Cl10','Cla','cl_x','Na1','Na2','Na_Na','Na4'])
self.assertEquals(c.get_symbols_set(), set(['Cl','Na']))
self.assertEquals([s.position for s in c.sites],
[(0.,0.,0.),(2.8,0,2.8),(0,2.8,2.8),(2.8,2.8,0),(2.8,2.8,2.8),(2.8,0,0),(0,2.8,0),(0,0,2.8)])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_roundtrip_spins(self):
"""
Tests roundtrip StructureData -> pymatgen -> StructureData
(with spins)
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[5.6,0,0],[0,5.6,0],[0,0,5.6]])
a.append_atom(position=(0,0,0), symbols='Mn',name='Mn1')
a.append_atom(position=(2.8,0,2.8), symbols='Mn',name='Mn1')
a.append_atom(position=(0,2.8,2.8), symbols='Mn',name='Mn1')
a.append_atom(position=(2.8,2.8,0), symbols='Mn',name='Mn1')
a.append_atom(position=(2.8,2.8,2.8), symbols='Mn',name='Mn2')
a.append_atom(position=(2.8,0,0), symbols='Mn',name='Mn2')
a.append_atom(position=(0,2.8,0), symbols='Mn',name='Mn2')
a.append_atom(position=(0,0,2.8), symbols='Mn',name='Mn2')

b = a.get_pymatgen(add_spin=True)
# check the spins
self.assertEquals([s.as_dict()['properties']['spin'] for s in b.species],
[-1, -1, -1, -1, 1, 1, 1, 1])
# back to StructureData
c = StructureData(pymatgen=b)
self.assertEquals(c.get_site_kindnames(), ['Mn1','Mn1','Mn1','Mn1','Mn2','Mn2','Mn2','Mn2'])
self.assertEquals([k.symbol for k in c.kinds], ['Mn','Mn'])
self.assertEquals([s.position for s in c.sites],
[(0.,0.,0.),(2.8,0,2.8),(0,2.8,2.8),(2.8,2.8,0),(2.8,2.8,2.8),(2.8,0,0),(0,2.8,0),(0,0,2.8)])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_roundtrip_partial_occ(self):
"""
Tests roundtrip StructureData -> pymatgen -> StructureData
(with partial occupancies).
Structure initially from ICSD (id: 251993).
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[3.9912, 0.0, 0.0],
[-1.9956, 3.456480591584452, 0.0],
[0.0, 0.0, 16.2958]])
a.append_atom(position=(0.0,0.0,13.49455198), symbols='Fe')
a.append_atom(position=(0.0,0.0,2.80124802), symbols='Fe')
a.append_atom(position=(0.0,0.0,5.34665198), symbols='Fe')
a.append_atom(position=(0.0,0.0,10.94914802), symbols='Fe')
a.append_atom(position=(1.9956,1.15239062923,12.22185), symbols='Fe',weights=0.9)
a.append_atom(position=(0.0,2.30408996235,4.07395), symbols='Fe',weights=0.9)
a.append_atom(position=(0.0,2.30408996235,12.22185),symbols='Ge')
a.append_atom(position=(1.9956,1.15239062923,4.07395),symbols='Ge')
a.append_atom(position=(1.9956,1.15239062923,14.8373259),symbols='Te')
a.append_atom(position=(0.0,2.30408996235,1.4584741),symbols='Te')
a.append_atom(position=(0.0,2.30408996235,6.6894259),symbols='Te')
a.append_atom(position=(1.9956,1.15239062923,9.6063741),symbols='Te')

# a few checks on the structure kinds and symbols
self.assertEquals(a.get_symbols_set(),set(['Fe', 'Ge', 'Te']))
self.assertEquals(a.get_site_kindnames(),
['Fe','Fe','Fe','Fe','FeX','FeX','Ge','Ge','Te','Te','Te','Te'])
self.assertEquals(a.get_formula(),'Fe4Ge2Te4{Fe0.90X0.10}2')

b = a.get_pymatgen()
# check the partial occupancies
self.assertEquals([s.as_dict() for s in b.species_and_occu],
[{'Fe':1.0},{'Fe':1.0},{'Fe':1.0},{'Fe':1.0},
{'Fe':0.9},{'Fe':0.9},{'Ge':1.0},{'Ge':1.0},
{'Te':1.0},{'Te':1.0},{'Te':1.0},{'Te':1.0}])

# back to StructureData
c = StructureData(pymatgen=b)
self.assertEquals(c.cell,[[3.9912, 0.0, 0.0],
[-1.9956, 3.456480591584452, 0.0],
[0.0, 0.0, 16.2958]])
self.assertEquals(c.get_symbols_set(),set(['Fe', 'Ge', 'Te']))
self.assertEquals(c.get_site_kindnames(),
['Fe','Fe','Fe','Fe','FeX','FeX','Ge','Ge','Te','Te','Te','Te'])
self.assertEquals(c.get_formula(),'Fe4Ge2Te4{Fe0.90X0.10}2')
self.assertEquals([s.position for s in c.sites],
[(0.0, 0.0, 13.49455198),
(0.0, 0.0, 2.80124802),
(0.0, 0.0, 5.34665198),
(0.0, 0.0, 10.94914802),
(1.9956, 1.15239062923, 12.22185),
(0.0, 2.30408996235, 4.07395),
(0.0, 2.30408996235, 12.22185),
(1.9956, 1.15239062923, 4.07395),
(1.9956, 1.15239062923, 14.8373259),
(0.0, 2.30408996235, 1.4584741),
(0.0, 2.30408996235, 6.6894259),
(1.9956, 1.15239062923, 9.6063741)])

@unittest.skipIf(not has_pymatgen(), "Unable to import pymatgen")
@unittest.skipIf(has_pymatgen() and
StrictVersion(get_pymatgen_version()) !=
StrictVersion('4.5.3'),
"Mismatch in the version of pymatgen (expected 4.5.3)")
def test_partial_occ_and_spin(self):
"""
Tests StructureData -> pymatgen, with partial occupancies and spins.
This should raise a ValueError.
"""
from aiida.orm.data.structure import StructureData

a = StructureData(cell=[[4,0,0],[0,4,0],[0,0,4]])
a.append_atom(position=(0,0,0), symbols=('Fe','Al'),weights=(0.8,0.2),name='FeAl1')
a.append_atom(position=(2,2,2), symbols=('Fe','Al'),weights=(0.8,0.2),name='FeAl2')

# a few checks on the structure kinds and symbols
self.assertEquals(a.get_symbols_set(),set(['Fe', 'Al']))
self.assertEquals(a.get_site_kindnames(),['FeAl1','FeAl2'])
self.assertEquals(a.get_formula(),'{Al0.20Fe0.80}2')

with self.assertRaises(ValueError):
a.get_pymatgen(add_spin=True)

# same, with vacancies
a = StructureData(cell=[[4,0,0],[0,4,0],[0,0,4]])
a.append_atom(position=(0,0,0), symbols='Fe',weights=0.8,name='FeX1')
a.append_atom(position=(2,2,2), symbols='Fe',weights=0.8,name='FeX2')

# a few checks on the structure kinds and symbols
self.assertEquals(a.get_symbols_set(),set(['Fe']))
self.assertEquals(a.get_site_kindnames(),['FeX1','FeX2'])
self.assertEquals(a.get_formula(),'{Fe0.80X0.20}2')

with self.assertRaises(ValueError):
a.get_pymatgen(add_spin=True)


class TestArrayData(AiidaTestCase):
"""
Tests the ArrayData objects.
Expand Down
Loading

0 comments on commit 10dc160

Please sign in to comment.