based on internal repository c9a2ac8 2019-01-03 16:04:57 +0100 tagged rev-master-2.0.0
263 lines
9.4 KiB
Python
263 lines
9.4 KiB
Python
#!/usr/bin/env python2
|
|
|
|
"""
|
|
@package pmsco.projects.fcc
|
|
scattering calculation project for the (111) surface of an arbitrary face-centered cubic crystal
|
|
|
|
@author Matthias Muntwiler, matthias.muntwiler@psi.ch
|
|
|
|
@copyright (c) 2015 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 argparse
|
|
import logging
|
|
|
|
import pmsco.cluster as mc
|
|
import pmsco.project as mp
|
|
from pmsco.helpers import BraceMessage as BMsg
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FCC111Project(mp.Project):
|
|
def __init__(self):
|
|
"""
|
|
initialize a project instance
|
|
"""
|
|
super(FCC111Project, self).__init__()
|
|
self.scan_dict = {}
|
|
self.element = "Ni"
|
|
|
|
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['dl1l2'] distance between top and second layer (may deviate from bulk)
|
|
@arg model['rmax'] cluster radius
|
|
@arg model['phi'] azimuthal rotation angle in degrees
|
|
"""
|
|
clu = mc.Cluster()
|
|
clu.comment = "{0} {1}".format(self.__class__, index)
|
|
clu.set_rmax(model['rmax'])
|
|
# fcc lattice constant
|
|
a_lat = model['dlat']
|
|
# surface lattice constant of the (111) surface
|
|
a_surf = a_lat / math.sqrt(2.0)
|
|
|
|
# lattice vectors
|
|
# a1 and a2 span the (111) surface
|
|
a1 = np.array((a_surf, 0.0, 0.0))
|
|
a2 = np.array((a_surf / 2.0, a_surf * math.sqrt(3.0) / 2.0, 0.0))
|
|
a3 = np.array((0.0, a_surf * math.sqrt(3.0) / 3.0, a_lat * math.sqrt(3.0) / 3))
|
|
|
|
a_l1 = np.array((0.0, 0.0, 0.0))
|
|
a_l2 = np.array(((a1[0] + a2[0]) * 2.0 / 3.0,
|
|
(a1[1] + a2[1]) * 2.0 / 3.0,
|
|
-(model['dl1l2'])))
|
|
a_l3 = np.array(((a1[0] + a2[0]) / 3.0,
|
|
(a1[1] + a2[1]) / 3.0,
|
|
-(a3[2] + model['dl1l2'])))
|
|
a_bulk = np.array((0.0, 0.0,
|
|
-(2.0 * a3[2] + model['dl1l2'])))
|
|
|
|
clu.add_layer(self.element, a_l1, a1, a2)
|
|
clu.add_layer(self.element, a_l2, a1, a2)
|
|
clu.add_layer(self.element, a_l3, a1, a2)
|
|
clu.add_bulk(self.element, a_bulk, a1, a2, a3, a_bulk[2] + 0.01)
|
|
|
|
clu.set_emitter(a_l1)
|
|
|
|
clu.rotate_z(model['phi'])
|
|
|
|
return clu
|
|
|
|
def create_params(self, model, index):
|
|
"""
|
|
set a specific set of parameters given the optimizable parameters.
|
|
|
|
par = optimizable parameters
|
|
par['V0'] = inner potential
|
|
par['Zsurf'] = position of surface
|
|
"""
|
|
params = mp.Params()
|
|
|
|
params.title = "fcc(111)"
|
|
params.comment = "{0} {1}".format(self.__class__, index)
|
|
params.cluster_file = ""
|
|
params.output_file = ""
|
|
params.initial_state = self.scans[index.scan].initial_state
|
|
params.spherical_order = 2
|
|
params.polarization = "H"
|
|
params.scattering_level = 5
|
|
params.fcut = 15.0
|
|
params.cut = 15.0
|
|
params.angular_resolution = 0.0
|
|
params.lattice_constant = 1.0
|
|
params.z_surface = model['Zsurf']
|
|
params.atom_types = 3
|
|
params.atomic_number = [pt.elements.symbol(self.element).number]
|
|
params.phase_file = []
|
|
params.msq_displacement = [0.00]
|
|
params.planewave_attenuation = 1.0
|
|
params.inner_potential = model['V0']
|
|
params.work_function = 4.5
|
|
params.symmetry_range = 360.0
|
|
params.polar_incidence_angle = 60.0
|
|
params.azimuthal_incidence_angle = 0.0
|
|
params.vibration_model = "P"
|
|
params.substrate_atomic_mass = pt.elements.symbol(self.element).mass
|
|
params.experiment_temperature = 300.0
|
|
params.debye_temperature = 400.0
|
|
params.debye_wavevector = 1.7558
|
|
params.rme_minus_value = 0.0
|
|
params.rme_minus_shift = 0.0
|
|
params.rme_plus_value = 1.0
|
|
params.rme_plus_shift = 0.0
|
|
# used by EDAC only
|
|
params.emitters = []
|
|
params.lmax = 15
|
|
params.dmax = 5.0
|
|
params.orders = [25]
|
|
|
|
return params
|
|
|
|
def create_domain(self):
|
|
"""
|
|
define the domain of the optimization parameters.
|
|
"""
|
|
dom = mp.Domain()
|
|
|
|
if self.mode == "single":
|
|
dom.add_param('rmax', 5.00, 5.00, 15.00, 2.50)
|
|
dom.add_param('phi', 0.00, 0.00, 0.00, 0.00)
|
|
dom.add_param('dlat', 3.52, 2.00, 5.00, 0.10)
|
|
dom.add_param('dl1l2', 2.03, 1.80, 2.20, 0.05)
|
|
dom.add_param('V0', 10.00, 0.00, 20.00, 1.00)
|
|
dom.add_param('Zsurf', 1.00, 0.00, 2.00, 0.50)
|
|
elif self.mode == "swarm":
|
|
dom.add_param('rmax', 7.50, 5.00, 15.00, 2.50)
|
|
dom.add_param('phi', 0.00, 0.00, 0.00, 0.00)
|
|
dom.add_param('dlat', 3.52, 2.00, 5.00, 0.10)
|
|
dom.add_param('dl1l2', 2.03, 1.80, 2.20, 0.05)
|
|
dom.add_param('V0', 10.00, 0.00, 20.00, 1.00)
|
|
dom.add_param('Zsurf', 1.00, 0.00, 2.00, 0.50)
|
|
elif self.mode == "grid":
|
|
dom.add_param('rmax', 7.50, 5.00, 15.00, 2.50)
|
|
dom.add_param('phi', 0.00, 0.00, 0.00, 0.00)
|
|
dom.add_param('dlat', 3.52, 2.00, 5.00, 0.10)
|
|
dom.add_param('dl1l2', 2.03, 1.80, 2.20, 0.05)
|
|
dom.add_param('V0', 10.00, 0.00, 20.00, 1.00)
|
|
dom.add_param('Zsurf', 1.00, 0.00, 2.00, 0.50)
|
|
else:
|
|
dom.add_param('rmax', 7.50, 5.00, 15.00, 2.50)
|
|
dom.add_param('phi', 0.00, 0.00, 0.00, 0.00)
|
|
dom.add_param('dlat', 3.52, 2.00, 5.00, 0.10)
|
|
dom.add_param('dl1l2', 2.03, 1.80, 2.20, 0.05)
|
|
dom.add_param('V0', 10.00, 0.00, 20.00, 1.00)
|
|
dom.add_param('Zsurf', 1.00, 0.00, 2.00, 0.50)
|
|
|
|
return dom
|
|
|
|
def create_project(element):
|
|
"""
|
|
create an FCC111Project calculation project.
|
|
|
|
@param element: symbol of the chemical element of the atoms contained in the cluster.
|
|
"""
|
|
|
|
project = FCC111Project()
|
|
project.element = element
|
|
|
|
project_dir = os.path.dirname(os.path.abspath(__file__))
|
|
project.data_dir = project_dir
|
|
|
|
# scan dictionary
|
|
# to select any number of scans, add their dictionary keys as scans option on the command line
|
|
project.scan_dict['default'] = {'filename': os.path.join(project_dir, "demo_holo_scan.etp"),
|
|
'emitter': "Ni", 'initial_state': "3s"}
|
|
project.scan_dict['holo'] = {'filename': os.path.join(project_dir, "demo_holo_scan.etp"),
|
|
'emitter': "Ni", 'initial_state': "3s"}
|
|
project.scan_dict['alpha'] = {'filename': os.path.join(project_dir, "demo_alpha_scan.etp"),
|
|
'emitter': "Ni", 'initial_state': "3s"}
|
|
|
|
project.add_symmetry({'default': 0.0})
|
|
|
|
return project
|
|
|
|
|
|
def set_project_args(project, project_args):
|
|
"""
|
|
set the project arguments of a MnGeTeProject calculation project.
|
|
|
|
@param project: project instance
|
|
|
|
@param project_args: (Namespace object) project arguments.
|
|
"""
|
|
|
|
scans = ['default']
|
|
try:
|
|
if project_args.scans:
|
|
scans = project_args.scans
|
|
else:
|
|
logger.warning(BMsg("missing scan argument, using {0}", scans[0]))
|
|
except AttributeError:
|
|
logger.warning(BMsg("missing scan argument, using {0}", scans[0]))
|
|
|
|
for scan_key in scans:
|
|
scan_spec = project.scan_dict[scan_key]
|
|
project.add_scan(**scan_spec)
|
|
logger.info(BMsg("add scan {filename} ({emitter} {initial_state})", **scan_spec))
|
|
|
|
try:
|
|
if project_args.element:
|
|
for scan in project.scans:
|
|
scan.emitter = project_args.element
|
|
logger.warning(BMsg("override emitters to {0}", project.emitter))
|
|
except AttributeError:
|
|
pass
|
|
|
|
try:
|
|
if project_args.initial_state:
|
|
project.initial_state = project_args.initial_state
|
|
logger.warning(BMsg("override initial states to {0}", project.initial_state))
|
|
except AttributeError:
|
|
pass
|
|
|
|
try:
|
|
if project_args.energy:
|
|
for scan in project.scans:
|
|
scan.energies = np.asarray((project_args.energy, ))
|
|
logger.warning(BMsg("override scan energy, set to {0}", project_args.energy))
|
|
except AttributeError:
|
|
pass
|
|
|
|
|
|
def parse_project_args(_args):
|
|
parser = argparse.ArgumentParser()
|
|
|
|
# main arguments
|
|
parser.add_argument('-e', '--element', help="chemical element symbol")
|
|
parser.add_argument('-s', '--scans', nargs="*", default=['default'],
|
|
help="nick names of scans to use in calculation (see create_project function)")
|
|
parser.add_argument('-i', '--initial-state',
|
|
help="inital state of photoelectron")
|
|
parser.add_argument('--energy', type=float,
|
|
help="kinetic energy of photoelectron (override scan file)")
|
|
|
|
parsed_args = parser.parse_known_args(_args)
|
|
return parsed_args
|