public release 2.2.0 - see README.md and CHANGES.md for details
This commit is contained in:
138
projects/common/clusters/crystals.py
Normal file
138
projects/common/clusters/crystals.py
Normal file
@ -0,0 +1,138 @@
|
||||
"""
|
||||
@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
|
Reference in New Issue
Block a user