Skip to content

Commit

Permalink
set xsf as default format for structures visualization (#1756)
Browse files Browse the repository at this point in the history
* set xsf as default format for structures visualization

* added _prepare_chemdoodle method

* added check visualization format
  • Loading branch information
elsapassaro authored and giovannipizzi committed Jul 18, 2018
1 parent 7fafaac commit ee29447
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 121 deletions.
22 changes: 22 additions & 0 deletions aiida/backends/tests/restapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,28 @@ def test_structure_visualization(self):
url,
response, uuid=node_uuid)

def test_xsf_visualization(self):
"""
Get the list of given calculation inputs
"""
from aiida.backends.tests.dataclasses import simplify
node_uuid = self.get_dummy_data()["structuredata"][0]["uuid"]
url = self.get_url_prefix() + '/structures/' + str(
node_uuid) + '/content/visualization?visformat=xsf'
with self.app.test_client() as client:
rv = client.get(url)
response = json.loads(rv.data)
expected_visdata = "CRYSTAL\nPRIMVEC 1\n 2.0000000000 0.0000000000 0.0000000000\n 0.0000000000 2.0000000000 0.0000000000\n 0.0000000000 0.0000000000 2.0000000000\nPRIMCOORD 1\n1 1\n56 0.0000000000 0.0000000000 0.0000000000\n"
self.assertEquals(simplify(response["data"]["visualization"]["str_viz_info"]["data"]),simplify(expected_visdata))
self.assertEquals(response["data"]["visualization"]["str_viz_info"]["format"],"xsf")
self.assertEquals(response["data"]["visualization"]["dimensionality"],
{u'dim': 3, u'value': 8.0, u'label': u'volume'})
self.assertEquals(response["data"]["visualization"]["pbc"], [True,True,True])
self.assertEquals(response["data"]["visualization"]["formula"], "Ba")
RESTApiTestCase.compare_extra_response_data(self, "structures",
url,
response, uuid=node_uuid)

def test_cif(self):
"""
Test download of cif file
Expand Down
114 changes: 114 additions & 0 deletions aiida/orm/data/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,47 @@ def _get_cif_ase_inline(struct=None, parameters=None):
return {'cif': cif}


def atom_kinds_to_html(atom_kind):
"""
Construct in html format
an alloy with 0.5 Ge, 0.4 Si and 0.1 vacancy is represented as
Ge<sub>0.5</sub> + Si<sub>0.4</sub> + vacancy<sub>0.1</sub>
Args:
atom_kind: a string with the name of the atomic kind, as printed by
kind.get_symbols_string(), e.g. Ba0.80Ca0.10X0.10
Returns:
html code for rendered formula
"""

# Parse the formula (TODO can be made more robust though never fails if
# it takes strings generated with kind.get_symbols_string())
import re
elements = re.findall(r'([A-Z][a-z]*)([0-1][.[0-9]*]?)?', atom_kind)

# Compose the html string
html_formula_pieces = []

for element in elements:

# replace element X by 'vacancy'
species = element[0] if element[0] != 'X' else 'vacancy'
weight = element[1] if element[1] != '' else None

if weight is not None:
html_formula_pieces.append(species + '<sub>' + weight +
'</sub>')
else:
html_formula_pieces.append(species)

html_formula = ' + '.join(html_formula_pieces)

return html_formula


class StructureData(Data):
"""
This class contains the information about a given structure, i.e. a
Expand Down Expand Up @@ -976,6 +1017,79 @@ def _prepare_tcod(self, main_file_name="", **kwargs):
from aiida.tools.dbexporters.tcod import export_cif
return export_cif(self, **kwargs).encode('utf-8'), {}

def _prepare_chemdoodle(self, main_file_name=""):
"""
Write the given structure to a string of format required by ChemDoodle.
"""
import numpy as np
from itertools import product
import json

supercell_factors=[1, 1, 1]

# Get cell vectors and atomic position
lattice_vectors = np.array(self.get_attr('cell'))
base_sites = self.get_attr('sites')

start1 = -int(supercell_factors[0] / 2)
start2 = -int(supercell_factors[1] / 2)
start3 = -int(supercell_factors[2] / 2)

stop1 = start1 + supercell_factors[0]
stop2 = start2 + supercell_factors[1]
stop3 = start3 + supercell_factors[2]

grid1 = range(start1, stop1)
grid2 = range(start2, stop2)
grid3 = range(start3, stop3)

atoms_json = []

# Manual recenter of the structure
center = (lattice_vectors[0] + lattice_vectors[1] +
lattice_vectors[2]) / 2.

for ix, iy, iz in product(grid1, grid2, grid3):
for base_site in base_sites:
shift = (ix * lattice_vectors[0] + iy * lattice_vectors[1] + \
iz * lattice_vectors[2] - center).tolist()

kind_name = base_site['kind_name']
kind_string = self.get_kind(kind_name).get_symbols_string()

atoms_json.append(
{'l': kind_string,
'x': base_site['position'][0] + shift[0],
'y': base_site['position'][1] + shift[1],
'z': base_site['position'][2] + shift[2],
# 'atomic_elements_html': kind_string
'atomic_elements_html': atom_kinds_to_html(kind_string)
})

cell_json = {
"t": "UnitCell",
"i": "s0",
"o": (-center).tolist(),
"x": (lattice_vectors[0] - center).tolist(),
"y": (lattice_vectors[1] - center).tolist(),
"z": (lattice_vectors[2] - center).tolist(),
"xy": (lattice_vectors[0] + lattice_vectors[1]
- center).tolist(),
"xz": (lattice_vectors[0] + lattice_vectors[2]
- center).tolist(),
"yz": (lattice_vectors[1] + lattice_vectors[2]
- center).tolist(),
"xyz": (lattice_vectors[0] + lattice_vectors[1]
+ lattice_vectors[2] - center).tolist(),
}

return_dict = {"s": [cell_json],
"m": [{"a": atoms_json}],
"units": '&Aring;'
}

return json.dumps(return_dict), {}

def _prepare_xyz(self, main_file_name=""):
"""
Write the given structure to a string of format XYZ.
Expand Down
128 changes: 7 additions & 121 deletions aiida/restapi/translator/data/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,10 @@
# For further information please visit http://www.aiida.net #
###########################################################################
from aiida.restapi.translator.data import DataTranslator
from aiida.restapi.common.exceptions import RestValidationError
from aiida.restapi.common.exceptions import RestInputValidationError
from aiida.common.exceptions import LicensingException
import numpy as np

def atom_kinds_to_html(atom_kind):
"""
Construct in html format
an alloy with 0.5 Ge, 0.4 Si and 0.1 vacancy is represented as
Ge<sub>0.5</sub> + Si<sub>0.4</sub> + vacancy<sub>0.1</sub>
Args:
atom_kind: a string with the name of the atomic kind, as printed by
kind.get_symbols_string(), e.g. Ba0.80Ca0.10X0.10
Returns:
html code for rendered formula
"""

# Parse the formula (TODO can be made more robust though never fails if
# it takes strings generated with kind.get_symbols_string())
import re
elements = re.findall(r'([A-Z][a-z]*)([0-1][.[0-9]*]?)?', atom_kind)

# Compose the html string
html_formula_pieces = []

for element in elements:

# replace element X by 'vacancy'
species = element[0] if element[0] != 'X' else 'vacancy'
weight = element[1] if element[1] != '' else None

if weight is not None:
html_formula_pieces.append(species + '<sub>' + weight +
'</sub>')
else:
html_formula_pieces.append(species)

html_formula = ' + '.join(html_formula_pieces)

return html_formula


class StructureDataTranslator(DataTranslator):
"""
Translator relative to resource 'structures' and aiida class StructureData
Expand Down Expand Up @@ -80,98 +39,25 @@ def __init__(self, **kwargs):


@staticmethod
def get_visualization_data(node, format=None, supercell_factors=[1, 1, 1]):
def get_visualization_data(node, format=None):
"""
Returns: data in specified format. If format is not specified returns data
in a format required by chemdoodle to visualize a structure.
in xsf format in order to visualize the structure with JSmol.
"""
response = {}
response["str_viz_info"] = {}

if format is None:
format = 'xsf'

if format in node.get_export_formats():
try:
response["str_viz_info"]["data"] = node._exportstring(format)[0]
response["str_viz_info"]["format"] = format
except LicensingException as e:
response = e.message

else:
import numpy as np
from itertools import product


# Validate supercell factors
if type(supercell_factors) is not list:
raise RestValidationError('supercell factors have to be a list of three integers')

for fac in supercell_factors:
if type(fac) is not int:
raise RestValidationError('supercell factors have to be '
'integers')

# Get cell vectors and atomic position
lattice_vectors = np.array(node.get_attr('cell'))
base_sites = node.get_attr('sites')

start1 = -int(supercell_factors[0] / 2)
start2 = -int(supercell_factors[1] / 2)
start3 = -int(supercell_factors[2] / 2)

stop1 = start1 + supercell_factors[0]
stop2 = start2 + supercell_factors[1]
stop3 = start3 + supercell_factors[2]

grid1 = range(start1, stop1)
grid2 = range(start2, stop2)
grid3 = range(start3, stop3)

atoms_json = []

# Manual recenter of the structure
center = (lattice_vectors[0] + lattice_vectors[1] +
lattice_vectors[2])/2.

for ix, iy, iz in product(grid1, grid2, grid3):
for base_site in base_sites:

shift = (ix*lattice_vectors[0] + iy*lattice_vectors[1] + \
iz*lattice_vectors[2] - center).tolist()

kind_name = base_site['kind_name']
kind_string = node.get_kind(kind_name).get_symbols_string()

atoms_json.append(
{'l': kind_string,
'x': base_site['position'][0] + shift[0],
'y': base_site['position'][1] + shift[1],
'z': base_site['position'][2] + shift[2],
# 'atomic_elements_html': kind_string
'atomic_elements_html': atom_kinds_to_html(kind_string)
})

cell_json = {
"t": "UnitCell",
"i": "s0",
"o": (-center).tolist(),
"x": (lattice_vectors[0]-center).tolist(),
"y": (lattice_vectors[1]-center).tolist(),
"z": (lattice_vectors[2]-center).tolist(),
"xy": (lattice_vectors[0] + lattice_vectors[1]
- center).tolist(),
"xz": (lattice_vectors[0] + lattice_vectors[2]
- center).tolist(),
"yz": (lattice_vectors[1] + lattice_vectors[2]
- center).tolist(),
"xyz": (lattice_vectors[0] + lattice_vectors[1]
+ lattice_vectors[2] - center).tolist(),
}

# These will be passed to ChemDoodle
response["str_viz_info"]["data"] = {"s": [cell_json],
"m": [{"a": atoms_json}],
"units": '&Aring;'
}
response["str_viz_info"]["format"] = "default (ChemDoodle)"
raise RestInputValidationError("The format {} is not supported.".format(format))

# Add extra information
response["dimensionality"] = node.get_dimensionality()
Expand Down

0 comments on commit ee29447

Please sign in to comment.