139 lines
4.8 KiB
Python
139 lines
4.8 KiB
Python
"""
|
|
@package projects.common.clusters.crystals
|
|
cluster generators for some common bulk crystals
|
|
|
|
@author Matthias Muntwiler, matthias.muntwiler@psi.ch
|
|
|
|
@copyright (c) 2015-19 by Paul Scherrer Institut @n
|
|
Licensed under the Apache License, Version 2.0 (the "License"); @n
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import math
|
|
import numpy as np
|
|
import os.path
|
|
import periodictable as pt
|
|
import logging
|
|
|
|
import pmsco.cluster as cluster
|
|
import pmsco.dispatch as dispatch
|
|
import pmsco.project as project
|
|
from pmsco.helpers import BraceMessage as BMsg
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ZincblendeCluster(cluster.ClusterGenerator):
|
|
def __init__(self, proj):
|
|
super(ZincblendeCluster, self).__init__(proj)
|
|
self.atomtype1 = 30
|
|
self.atomtype2 = 16
|
|
self.bulk_lattice = 1.0
|
|
self.surface = (1, 1, 1)
|
|
|
|
@classmethod
|
|
def check(cls, outfilename=None, model_dict=None, domain_dict=None):
|
|
"""
|
|
function to test and debug the cluster generator.
|
|
|
|
to use this function, you don't need to import or initialize anything but the class.
|
|
though the project class is used internally, the result does not depend on any project settings.
|
|
|
|
@param outfilename: name of output file for the cluster (XYZ format).
|
|
the file is written to the same directory where this module is located.
|
|
if empty or None, no file is written.
|
|
|
|
@param model_dict: dictionary of model parameters to override the default values.
|
|
|
|
@param domain_dict: dictionary of domain parameters to override the default values.
|
|
|
|
@return: @ref pmsco.cluster.Cluster object
|
|
"""
|
|
proj = project.Project()
|
|
dom = project.ModelSpace()
|
|
dom.add_param('dlat', 10.)
|
|
dom.add_param('rmax', 5.0)
|
|
if model_dict:
|
|
dom.start.update(model_dict)
|
|
|
|
try:
|
|
proj.domains[0].update({'zrot': 0.})
|
|
except IndexError:
|
|
proj.add_domain({'zrot': 0.})
|
|
if domain_dict:
|
|
proj.domains[0].update(domain_dict)
|
|
proj.add_scan("", 'C', '1s')
|
|
|
|
clu_gen = cls(proj)
|
|
index = dispatch.CalcID(0, 0, 0, -1, -1)
|
|
clu = clu_gen.create_cluster(dom.start, index)
|
|
|
|
if outfilename:
|
|
project_dir = os.path.dirname(os.path.abspath(__file__))
|
|
outfilepath = os.path.join(project_dir, outfilename)
|
|
clu.save_to_file(outfilepath, fmt=cluster.FMT_XYZ, comment="{0} {1} {2}".format(cls, index, str(dom.start)))
|
|
|
|
return clu
|
|
|
|
def count_emitters(self, model, index):
|
|
return 1
|
|
|
|
def create_cluster(self, model, index):
|
|
"""
|
|
calculate a specific set of atom positions given the optimizable parameters.
|
|
|
|
@param model (dict) optimizable parameters
|
|
@arg model['dlat'] bulk lattice constant in Angstrom
|
|
@arg model['rmax'] cluster radius
|
|
@arg model['phi'] azimuthal rotation angle in degrees
|
|
|
|
@param dom (dict) domain
|
|
@arg dom['term'] surface termination
|
|
"""
|
|
clu = cluster.Cluster()
|
|
clu.comment = "{0} {1}".format(self.__class__, index)
|
|
clu.set_rmax(model['rmax'])
|
|
a_lat = model['dlat']
|
|
dom = self.project.domains[index]
|
|
try:
|
|
term = int(dom['term'])
|
|
except ValueError:
|
|
term = pt.elements.symbol(dom['term'].strip().number)
|
|
|
|
if self.surface == (0, 0, 1):
|
|
# identity matrix
|
|
m = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
|
elif self.surface == (1, 1, 1):
|
|
# this will map the [111] direction onto the z-axis
|
|
m1 = np.array([1, -1, 0]) * math.sqrt(1/2)
|
|
m2 = np.array([0.5, 0.5, -1]) * math.sqrt(2/3)
|
|
m3 = np.array([1, 1, 1]) * math.sqrt(1/3)
|
|
m = np.array([m1, m2, m3])
|
|
else:
|
|
raise ValueError("unsupported surface specification")
|
|
|
|
# lattice vectors
|
|
a1 = np.matmul(m, np.array((1.0, 0.0, 0.0)) * a_lat)
|
|
a2 = np.matmul(m, np.array((0.0, 1.0, 0.0)) * a_lat)
|
|
a3 = np.matmul(m, np.array((0.0, 0.0, 1.0)) * a_lat)
|
|
|
|
# basis
|
|
b1 = [np.array((0.0, 0.0, 0.0)), (a2 + a3) / 2, (a3 + a1) / 2, (a1 + a2) / 2]
|
|
if term == self.atomtype1:
|
|
d1 = np.array((0, 0, 0))
|
|
d2 = (a1 + a2 + a3) / 4
|
|
else:
|
|
d1 = -(a1 + a2 + a3) / 4
|
|
d2 = np.array((0, 0, 0))
|
|
for b in b1:
|
|
clu.add_bulk(self.atomtype1, b + d1, a1, a2, a3)
|
|
clu.add_bulk(self.atomtype2, b + d2, a1, a2, a3)
|
|
|
|
return clu
|