#!/usr/bin/env python """ @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 division import sys import os import math import numpy as np import periodictable as pt import argparse import logging base_dir = os.path.dirname(__file__) or '.' package_dir = os.path.join(base_dir, '../..') package_dir = os.path.abspath(package_dir) sys.path.append(package_dir) import pmsco.pmsco import pmsco.cluster as mc import pmsco.project as mp import pmsco.data as md 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) 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_broadening = 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 def main(): args, unknown_args = pmsco.pmsco.parse_cli() if unknown_args: project_args = parse_project_args(unknown_args) else: project_args = None project = create_project(project_args.element) pmsco.pmsco.set_common_args(project, args) set_project_args(project, project_args) pmsco.pmsco.run_project(project) if __name__ == '__main__': main() sys.exit(0)