Source code for soprano.collection.generate.airss
# Soprano - a library to crack crystals! by Simone Sturniolo
# Copyright (C) 2016 - Science and Technology Facility Council
# Soprano is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Soprano is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Bindings for AIRSS Buildcell program for random structure generation"""
import copy
import hashlib
import os
import subprocess as sp
from ase import io as ase_io
# Internal imports
from soprano.utils import safe_communicate, seedname
# Python 2-to-3 compatibility
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
[docs]
def airssGen(
input_file,
n=100,
buildcell_command="buildcell",
buildcell_path=None,
clone_calc=True,
):
"""Generator function binding to AIRSS' Buildcell.
This function searches for a buildcell executable and uses it to
generate multiple new Atoms structures for a collection.
| Args:
| input_file (str or file): the .cell file with appropriate comments
| specifying the details of buildcell's
| construction work.
| n (int): number of structures to generate. If set to None the
| generator goes on indefinitely.
| buildcell_command (str): command required to call the buildcell
| executable.
| buildcell_path (str): path where the buildcell executable can be
| found. If not present, the buildcell command
| will be invoked directly (assuming the
| executable is in the system PATH).
| clone_calc (bool): if True, the CASTEP calculator in the input file
| will be copied and attached to the new structures.
| This means that for example any additional CASTEP
| keywords/blocks in the input file will be carried
| on to the new structures. Default is True.
| Returns:
| airssGenerator (generator): an iterable object that yields structures
| created by buildcell.
"""
# First: check that AIRSS is even installed
if buildcell_path is None:
buildcell_path = ""
airss_cmd = [os.path.join(buildcell_path, buildcell_command)]
try:
stdout, stderr = sp.Popen(
airss_cmd + ["-h"], stdout=sp.PIPE, stderr=sp.PIPE
).communicate()
except OSError:
# Not even installed!
raise RuntimeError("No instance of Buildcell found on this system")
# Now open the given input file
try:
input_file = open(input_file) # If it's a string
except TypeError:
pass # If it's already a file
template = input_file.read()
# Now get the file name
basename = seedname(input_file.name)
input_file.close()
# Calculator (if needed)
calc = None
if clone_calc:
calc = ase_io.read(input_file.name).calc
# And keep track of the count!
# (at least if it's not infinite)
i = 0
while True:
if n is not None:
if i >= n:
return
i += 1
# Generate a structure
subproc = sp.Popen(
airss_cmd,
universal_newlines=True,
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
)
stdout, stderr = safe_communicate(subproc, template)
# Now turn it into a proper Atoms object
# To do this we need to make it look like a file to ASE's io.read
try:
newcell = ase_io.read(StringIO(stdout), format="castep-cell")
except Exception:
# If ANYTHING happens, let's consider that stdout might be wrong
raise RuntimeError(
f"Invalid output from buildcell:\nstdout:\n{stdout}" f"\nstderr:\n{stderr}"
)
if clone_calc:
newcell.calc = copy.deepcopy(calc)
# Generate it a name, function of its properties
postfix = hashlib.md5(str(newcell.get_positions()).encode()).hexdigest()
newcell.info["name"] = f"{basename}_{postfix}"
yield newcell