public distro 2.1.0
This commit is contained in:
@ -11,7 +11,7 @@ TestCalcInterface is provided for testing the PMSCO code quickly without calling
|
||||
|
||||
@author Matthias Muntwiler
|
||||
|
||||
@copyright (c) 2015-18 by Paul Scherrer Institut @n
|
||||
@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
|
||||
@ -56,11 +56,25 @@ class Calculator(object):
|
||||
@arg the first element is the name of the main ETPI or ETPAI result file to be further processed.
|
||||
@arg the second element is a dictionary that lists the names of all created data files with their category.
|
||||
the dictionary key is the file name,
|
||||
the value is the file category (cluster, phase, etc.).
|
||||
the value is the file category (cluster, atomic, etc.).
|
||||
"""
|
||||
return None, None
|
||||
|
||||
|
||||
class AtomicCalculator(Calculator):
|
||||
"""
|
||||
abstract interface class to the atomic scattering calculation program.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InternalAtomicCalculator(AtomicCalculator):
|
||||
"""
|
||||
dummy atomic scattering class if scattering factors are calculated internally by the multiple scattering calculator.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class TestCalculator(Calculator):
|
||||
"""
|
||||
interface class producing random data for testing the MSCO code without calling an external program.
|
||||
|
@ -55,7 +55,10 @@ class EdacCalculator(calculator.Calculator):
|
||||
|
||||
@param filepath: (str) name and path of the file to be created.
|
||||
|
||||
@return dictionary of created files {filename: category}
|
||||
"""
|
||||
files = {}
|
||||
|
||||
with open(filepath, "w") as f:
|
||||
f.write("verbose off\n")
|
||||
f.write("cluster input {0}\n".format(params.cluster_file))
|
||||
@ -146,9 +149,16 @@ class EdacCalculator(calculator.Calculator):
|
||||
scatterers = ["scatterer {at} {fi}\n".format(at=at, fi=fi)
|
||||
for (at, fi) in params.phase_files.items()
|
||||
if os.path.isfile(fi)]
|
||||
if scatterers:
|
||||
rme = ["rmat {fi}\n".format(fi=fi)
|
||||
for (at, fi) in params.rme_files.items()
|
||||
if at == params.emitters[0][3] and os.path.isfile(fi)] or \
|
||||
["rmat inline 1 regular1 {l0} {pv} {pd} {mv} {md}\n".format(l0=params.l_init,
|
||||
pv=params.rme_plus_value, pd=params.rme_plus_shift,
|
||||
mv=params.rme_minus_value, md=params.rme_minus_shift)]
|
||||
if scatterers and rme:
|
||||
for scat in scatterers:
|
||||
f.write(scat)
|
||||
f.write(rme[0])
|
||||
else:
|
||||
f.write("muffin-tin\n")
|
||||
|
||||
@ -162,16 +172,27 @@ class EdacCalculator(calculator.Calculator):
|
||||
f.write("orders {0:d} ".format(len(params.orders)))
|
||||
f.write(" ".join(format(order, "d") for order in params.orders) + "\n")
|
||||
f.write("emission angle window {0:F}\n".format(params.angular_resolution / 2.0))
|
||||
# f.write("cluster output l(A) out.clu")
|
||||
# problems:
|
||||
# - muffin-tin relabels atoms
|
||||
# - there can be multiple atom types for the same chemical element
|
||||
# - we have to compare coordinates to find the mapping between input and output cluster
|
||||
# f.write("scan scatterer i phase-shifts i.pha")
|
||||
# f.write("scan scatterer i potential i.pot")
|
||||
|
||||
# scattering factor output (see project.Params.phase_output_classes)
|
||||
if params.phase_output_classes is not None:
|
||||
fn = "{0}.clu".format(params.output_file)
|
||||
f.write("cluster output l(A) {fn}\n".format(fn=fn))
|
||||
files[fn] = "output"
|
||||
try:
|
||||
cls = (cl for cl in params.phase_output_classes)
|
||||
except TypeError:
|
||||
cls = range(params.phase_output_classes)
|
||||
for cl in cls:
|
||||
fn = "{of}.{cl}.scat".format(cl=cl, of=params.output_file)
|
||||
f.write("scan scatterer {cl} phase-shifts {fn}\n".format(cl=cl, fn=fn))
|
||||
files[fn] = "output"
|
||||
|
||||
f.write("scan pd {0}\n".format(params.output_file))
|
||||
files[params.output_file] = "output"
|
||||
f.write("end\n")
|
||||
|
||||
return files
|
||||
|
||||
def run(self, params, cluster, scan, output_file):
|
||||
"""
|
||||
run EDAC with the given parameters and cluster.
|
||||
@ -205,13 +226,13 @@ class EdacCalculator(calculator.Calculator):
|
||||
params.cluster_file = clu_filename
|
||||
params.output_file = out_filename
|
||||
params.data_file = dat_filename
|
||||
params.emitters = cluster.get_emitters()
|
||||
params.emitters = cluster.get_emitters(['x', 'y', 'z', 'c'])
|
||||
|
||||
# save parameter files
|
||||
logger.debug("writing cluster file %s", clu_filename)
|
||||
cluster.save_to_file(clu_filename, fmt=mc.FMT_EDAC)
|
||||
logger.debug("writing input file %s", par_filename)
|
||||
self.write_input_file(params, scan, par_filename)
|
||||
files = self.write_input_file(params, scan, par_filename)
|
||||
|
||||
# run EDAC
|
||||
logger.info("calling EDAC with input file %s", par_filename)
|
||||
@ -244,6 +265,9 @@ class EdacCalculator(calculator.Calculator):
|
||||
logger.debug("save result to file %s", etpi_filename)
|
||||
md.save_data(etpi_filename, result_etpi)
|
||||
|
||||
files = {clu_filename: 'input', par_filename: 'input', dat_filename: 'output',
|
||||
etpi_filename: 'region'}
|
||||
files[clu_filename] = 'input'
|
||||
files[par_filename] = 'input'
|
||||
files[dat_filename] = 'output'
|
||||
files[etpi_filename] = 'region'
|
||||
|
||||
return etpi_filename, files
|
||||
|
0
pmsco/calculators/phagen/__init__.py
Normal file
0
pmsco/calculators/phagen/__init__.py
Normal file
43
pmsco/calculators/phagen/makefile
Normal file
43
pmsco/calculators/phagen/makefile
Normal file
@ -0,0 +1,43 @@
|
||||
SHELL=/bin/sh
|
||||
|
||||
# makefile for PHAGEN program and module
|
||||
#
|
||||
# the PHAGEN source code is not included in the public distribution.
|
||||
# please obtain the PHAGEN code from the original author,
|
||||
# and copy it to this directory before compilation.
|
||||
#
|
||||
# see the top-level makefile for additional information.
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .cpp .cxx .exe .f .h .i .o .py .pyf .so
|
||||
.PHONY: all clean phagen
|
||||
|
||||
FC?=gfortran
|
||||
F2PY?=f2py
|
||||
F2PYOPTS?=
|
||||
CC?=gcc
|
||||
CCOPTS?=
|
||||
SWIG?=swig
|
||||
SWIGOPTS?=
|
||||
PYTHON?=python
|
||||
PYTHONOPTS?=
|
||||
PYTHONINC?=
|
||||
PYTHON_CONFIG = ${PYTHON}-config
|
||||
PYTHON_CFLAGS ?= $(shell ${PYTHON_CONFIG} --cflags)
|
||||
PYTHON_EXT_SUFFIX ?= $(shell ${PYTHON_CONFIG} --extension-suffix)
|
||||
|
||||
all: phagen
|
||||
|
||||
phagen: phagen.exe phagen$(EXT_SUFFIX)
|
||||
|
||||
phagen.exe: phagen_scf.f msxas3.inc msxasc3.inc
|
||||
$(FC) $(FCOPTS) -o phagen.exe phagen_scf.f
|
||||
|
||||
phagen.pyf: | phagen_scf.f
|
||||
$(F2PY) -h phagen.pyf -m phagen phagen_scf.f only: libmain
|
||||
|
||||
phagen$(EXT_SUFFIX): phagen_scf.f phagen.pyf msxas3.inc msxasc3.inc
|
||||
$(F2PY) -c $(F2PYOPTS) -m phagen phagen.pyf phagen_scf.f
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o *.exe
|
102
pmsco/calculators/phagen/phagen_scf.f.patch
Normal file
102
pmsco/calculators/phagen/phagen_scf.f.patch
Normal file
@ -0,0 +1,102 @@
|
||||
--- phagen_scf.orig.f 2019-06-05 16:45:52.977855859 +0200
|
||||
+++ phagen_scf.f 2019-05-09 16:32:35.790286429 +0200
|
||||
@@ -174,6 +174,99 @@
|
||||
1100 format(//,1x,' ** phagen terminated normally ** ',//)
|
||||
end
|
||||
|
||||
+
|
||||
+c-----------------------------------------------------------------------
|
||||
+ subroutine libmain(infile,outfile,etcfile)
|
||||
+c main calculation routine
|
||||
+c entry point for external callers
|
||||
+c
|
||||
+c infile: name of parameter input file
|
||||
+c
|
||||
+c outfile: base name of output files
|
||||
+c output files with endings .list, .clu, .pha, .tl, .rad
|
||||
+c will be created
|
||||
+c-----------------------------------------------------------------------
|
||||
+ implicit real*8 (a-h,o-z)
|
||||
+c
|
||||
+ include 'msxas3.inc'
|
||||
+ include 'msxasc3.inc'
|
||||
+
|
||||
+ character*60 infile,outfile,etcfile
|
||||
+ character*70 listfile,clufile,tlfile,radfile,phafile
|
||||
+
|
||||
+c
|
||||
+c.. constants
|
||||
+ antoau = 0.52917715d0
|
||||
+ pi = 3.141592653589793d0
|
||||
+ ev = 13.6058d0
|
||||
+ zero = 0.d0
|
||||
+c.. threshold for linearity
|
||||
+ thresh = 1.d-4
|
||||
+c.. fortran io units
|
||||
+ idat = 5
|
||||
+ iwr = 6
|
||||
+ iphas = 30
|
||||
+ iedl0 = 31
|
||||
+ iwf = 32
|
||||
+ iof = 17
|
||||
+
|
||||
+ iii=LnBlnk(outfile)+1
|
||||
+ listfile=outfile
|
||||
+ listfile(iii:)='.list'
|
||||
+ clufile=outfile
|
||||
+ clufile(iii:)='.clu'
|
||||
+ phafile=outfile
|
||||
+ phafile(iii:)='.pha'
|
||||
+ tlfile=outfile
|
||||
+ tlfile(iii:)='.tl'
|
||||
+ radfile=outfile
|
||||
+ radfile(iii:)='.rad'
|
||||
+
|
||||
+ open(idat,file=infile,form='formatted',status='old')
|
||||
+ open(iwr,file=listfile,form='formatted',status='unknown')
|
||||
+ open(10,file=clufile,form='formatted',status='unknown')
|
||||
+ open(35,file=tlfile,form='formatted',status='unknown')
|
||||
+ open(55,file=radfile,form='formatted',status='unknown')
|
||||
+ open(iphas,file=phafile,form='formatted',status='unknown')
|
||||
+
|
||||
+ open(iedl0,form='unformatted',status='scratch')
|
||||
+ open(iof,form='unformatted',status='scratch')
|
||||
+ open(unit=21,form='unformatted',status='scratch')
|
||||
+ open(60,form='formatted',status='scratch')
|
||||
+ open(50,form='formatted',status='scratch')
|
||||
+ open(unit=13,form='formatted',status='scratch')
|
||||
+ open(unit=14,form='formatted',status='scratch')
|
||||
+ open(unit=11,status='scratch')
|
||||
+ open(unit=iwf,status='scratch')
|
||||
+ open(unit=33,status='scratch')
|
||||
+ open(unit=66,status='scratch')
|
||||
+
|
||||
+ call inctrl
|
||||
+ call intit(iof)
|
||||
+ call incoor
|
||||
+ call calphas
|
||||
+
|
||||
+ close(idat)
|
||||
+ close(iwr)
|
||||
+ close(10)
|
||||
+ close(35)
|
||||
+ close(55)
|
||||
+ close(iphas)
|
||||
+ close(iedl0)
|
||||
+ close(iof)
|
||||
+ close(60)
|
||||
+ close(50)
|
||||
+ close(13)
|
||||
+ close(14)
|
||||
+ close(11)
|
||||
+ close(iwf)
|
||||
+ close(33)
|
||||
+ close(66)
|
||||
+ close(21)
|
||||
+
|
||||
+ endsubroutine
|
||||
+
|
||||
+
|
||||
subroutine inctrl
|
||||
implicit real*8 (a-h,o-z)
|
||||
include 'msxas3.inc'
|
153
pmsco/calculators/phagen/runner.py
Normal file
153
pmsco/calculators/phagen/runner.py
Normal file
@ -0,0 +1,153 @@
|
||||
"""
|
||||
@package pmsco.calculators.phagen.runner
|
||||
Natoli/Sebilleau PHAGEN interface
|
||||
|
||||
this module runs the PHAGEN program to calculate scattering factors and radial matrix element.
|
||||
|
||||
@author Matthias Muntwiler
|
||||
|
||||
@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 logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from pmsco.calculators.calculator import AtomicCalculator
|
||||
from pmsco.calculators.phagen.phagen import libmain
|
||||
from pmsco.calculators.phagen.translator import Translator
|
||||
import pmsco.cluster
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PhagenCalculator(AtomicCalculator):
|
||||
"""
|
||||
use the PHAGEN program to calculate scattering factors and radial matrix element.
|
||||
|
||||
this produces scatterer, radial matrix element and cluster files for EDAC.
|
||||
"""
|
||||
|
||||
def run(self, params, cluster, scan, output_file):
|
||||
"""
|
||||
create the input file, run PHAGEN, and translate the output to EDAC format.
|
||||
|
||||
the following files are created in the job work directory:
|
||||
- scattering factor files in EDAC format.
|
||||
their names are `output_file + "_{atomclass}.scat"`.
|
||||
- radial matrix element file in EDAC format.
|
||||
its name is `output_file + ".rme"`.
|
||||
- cluster file in PMSCO format.
|
||||
its name is `output_file + ".clu"`.
|
||||
|
||||
the cluster and params objects are updated and linked to the scattering files
|
||||
so that they can be passed to EDAC without further modification.
|
||||
the radial matrix element is currently not used.
|
||||
|
||||
note that the scattering files are numbered according to the atomic environment and not chemical element.
|
||||
this means that the updated cluster (cluster object or ".clu" file)
|
||||
must be used in the scattering calculation.
|
||||
atomic index is not preserved - atoms in the input and output clusters can only be related by coordinate!
|
||||
|
||||
because PHAGEN generates a lot of files with hard-coded names,
|
||||
the function creates a temporary directory for PHAGEN and deletes it before returning.
|
||||
|
||||
@param params: pmsco.project.Params object.
|
||||
the phase_files attribute is updated with the paths of the scattering files.
|
||||
|
||||
@param cluster: pmsco.cluster.Cluster object.
|
||||
the cluster is updated with the one returned from PHAGEN.
|
||||
the atom classes are linked to the scattering files.
|
||||
|
||||
@param scan: pmsco.project.Scan object.
|
||||
the scan object is used to determine the kinetic energy range.
|
||||
|
||||
@param output_file: base path and name of the output files.
|
||||
|
||||
@return (None, dict) where dict is a list of output files with their category.
|
||||
the category is "atomic" for all output files.
|
||||
"""
|
||||
transl = Translator()
|
||||
transl.params.set_params(params)
|
||||
transl.params.set_cluster(cluster)
|
||||
transl.params.set_scan(scan)
|
||||
phagen_cluster = pmsco.cluster.Cluster()
|
||||
|
||||
files = {}
|
||||
prev_wd = os.getcwd()
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
os.chdir(temp_dir)
|
||||
os.mkdir("div")
|
||||
os.mkdir("div/wf")
|
||||
os.mkdir("plot")
|
||||
os.mkdir("data")
|
||||
|
||||
# prepare input for phagen
|
||||
infile = "phagen.in"
|
||||
outfile = "phagen.out"
|
||||
|
||||
try:
|
||||
transl.write_input(infile)
|
||||
report_infile = os.path.join(prev_wd, output_file + ".phagen.in")
|
||||
shutil.copy(infile, report_infile)
|
||||
files[report_infile] = "input"
|
||||
except IOError:
|
||||
logger.warning("error writing phagen input file {fi}.".format(fi=infile))
|
||||
|
||||
# call phagen
|
||||
libmain(infile, outfile)
|
||||
|
||||
# collect results
|
||||
try:
|
||||
phafile = outfile + ".pha"
|
||||
transl.parse_phagen_phase(phafile)
|
||||
report_phafile = os.path.join(prev_wd, output_file + ".phagen.pha")
|
||||
shutil.copy(phafile, report_phafile)
|
||||
files[report_phafile] = "output"
|
||||
except IOError:
|
||||
logger.error("error loading phagen phase file {fi}".format(fi=phafile))
|
||||
|
||||
try:
|
||||
radfile = outfile + ".rad"
|
||||
transl.parse_radial_file(radfile)
|
||||
report_radfile = os.path.join(prev_wd, output_file + ".phagen.rad")
|
||||
shutil.copy(radfile, report_radfile)
|
||||
files[report_radfile] = "output"
|
||||
except IOError:
|
||||
logger.error("error loading phagen radial file {fi}".format(fi=radfile))
|
||||
|
||||
try:
|
||||
clufile = outfile + ".clu"
|
||||
phagen_cluster.load_from_file(clufile, pmsco.cluster.FMT_PHAGEN_OUT)
|
||||
except IOError:
|
||||
logger.error("error loading phagen cluster file {fi}".format(fi=clufile))
|
||||
|
||||
finally:
|
||||
os.chdir(prev_wd)
|
||||
|
||||
# write edac files
|
||||
scatfile = output_file + "_{}.scat"
|
||||
scatfiles = transl.write_edac_scattering(scatfile)
|
||||
params.phase_files = {c: scatfiles[c] for c in scatfiles}
|
||||
files.update({scatfiles[c]: "atomic" for c in scatfiles})
|
||||
|
||||
rmefile = output_file + ".rme"
|
||||
transl.write_edac_emission(rmefile)
|
||||
files[rmefile] = "atomic"
|
||||
|
||||
cluster.update_atoms(phagen_cluster, {'c'})
|
||||
clufile = output_file + ".pmsco.clu"
|
||||
cluster.save_to_file(clufile, pmsco.cluster.FMT_PMSCO)
|
||||
files[clufile] = "cluster"
|
||||
|
||||
return None, files
|
411
pmsco/calculators/phagen/translator.py
Normal file
411
pmsco/calculators/phagen/translator.py
Normal file
@ -0,0 +1,411 @@
|
||||
"""
|
||||
@package pmsco.calculators.phagen.translator
|
||||
Natoli/Sebilleau PHAGEN interface
|
||||
|
||||
this module provides conversion between input/output files of PHAGEN and EDAC.
|
||||
|
||||
@author Matthias Muntwiler
|
||||
|
||||
@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 numpy as np
|
||||
|
||||
from pmsco.compat import open
|
||||
|
||||
## rydberg energy in electron volts
|
||||
ERYDBERG = 13.6056923
|
||||
|
||||
|
||||
def state_to_edge(state):
|
||||
"""
|
||||
translate spectroscopic notation to edge notation.
|
||||
|
||||
@param state: spectroscopic notation: "1s", "2s", "2p1/2", etc.
|
||||
@return: edge notation: "k", "l1", "l2", etc.
|
||||
note: if the j-value is not given, the lower j edge is returned.
|
||||
"""
|
||||
jshells = ['s', 'p1/2', 'p3/2', 'd3/2', 'd5/2', 'f5/2', 'f7/2']
|
||||
lshells = [s[0] for s in jshells]
|
||||
shell = int(state[0])
|
||||
try:
|
||||
subshell = jshells.index(state[1:]) + 1
|
||||
except ValueError:
|
||||
subshell = lshells.index(state[1]) + 1
|
||||
except IndexError:
|
||||
subshell = 1
|
||||
edge = "klmnop"[shell-1]
|
||||
if shell > 1:
|
||||
edge += str(subshell)
|
||||
return edge
|
||||
|
||||
|
||||
class TranslationParams(object):
|
||||
"""
|
||||
project parameters needed for translation.
|
||||
|
||||
energy unit is eV.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.initial_state = "1s"
|
||||
self.binding_energy = 0.
|
||||
self.cluster = None
|
||||
self.kinetic_energies = np.empty(0, dtype=np.float)
|
||||
|
||||
@property
|
||||
def l_init(self):
|
||||
return "spdf".index(self.initial_state[1])
|
||||
|
||||
@property
|
||||
def edge(self):
|
||||
return state_to_edge(self.initial_state)
|
||||
|
||||
def set_params(self, params):
|
||||
"""
|
||||
set the translation parameters.
|
||||
|
||||
@param params: a pmsco.project.Params object or
|
||||
a dictionary containing some or all public fields of this class.
|
||||
@return: None
|
||||
"""
|
||||
try:
|
||||
self.initial_state = params.initial_state
|
||||
self.binding_energy = params.binding_energy
|
||||
except AttributeError:
|
||||
for key in params:
|
||||
self.__setattr__(key, params[key])
|
||||
|
||||
def set_scan(self, scan):
|
||||
"""
|
||||
set the scan parameters.
|
||||
|
||||
@param scan: a pmsco.project.Scan object
|
||||
@return: None
|
||||
"""
|
||||
try:
|
||||
energies = scan.energies
|
||||
except AttributeError:
|
||||
try:
|
||||
energies = scan['e']
|
||||
except KeyError:
|
||||
energies = scan
|
||||
if not isinstance(energies, np.ndarray):
|
||||
energies = np.array(energies)
|
||||
self.kinetic_energies = np.resize(self.kinetic_energies, energies.shape)
|
||||
self.kinetic_energies = energies
|
||||
|
||||
def set_cluster(self, cluster):
|
||||
"""
|
||||
set the initial cluster.
|
||||
|
||||
@param cluster: a pmsco.cluster.Cluster object
|
||||
@return: None
|
||||
"""
|
||||
self.cluster = cluster
|
||||
|
||||
|
||||
class Translator(object):
|
||||
"""
|
||||
data conversion to/from phagen input/output files.
|
||||
|
||||
usage:
|
||||
1. set the translation parameters self.params.
|
||||
2. call write_input_file to create the phagen input files.
|
||||
3. call phagen on the input file.
|
||||
4. call parse_phagen_phase.
|
||||
5. call parse_radial_file.
|
||||
6. call write_edac_scattering to produce the EDAC scattering matrix files.
|
||||
7. call write_edac_emission to produce the EDAC emission matrix file.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
initialize the object instance.
|
||||
"""
|
||||
self.params = TranslationParams()
|
||||
dt = [('e', 'f4'), ('a', 'i4'), ('l', 'i4'), ('t', 'c16')]
|
||||
self.scattering = np.empty(0, dtype=dt)
|
||||
dt = [('e', 'f4'), ('dw', 'c16'), ('up', 'c16')]
|
||||
self.emission = np.empty(0, dtype=dt)
|
||||
|
||||
def write_cluster(self, f):
|
||||
"""
|
||||
write the cluster section of the PHAGEN input file.
|
||||
|
||||
requires a valid pmsco.cluster.Cluster in self.params.cluster.
|
||||
|
||||
@param f: file or output stream (an object with a write method)
|
||||
|
||||
@return: None
|
||||
"""
|
||||
for atom in self.params.cluster.data:
|
||||
d = {k: atom[k] for k in atom.dtype.names}
|
||||
f.write("{s} {t} {x} {y} {z}\n".format(**d))
|
||||
f.write("-1 -1 0. 0. 0.\n")
|
||||
|
||||
def write_ionicity(self, f):
|
||||
"""
|
||||
write the ionicity section of the PHAGEN input file.
|
||||
|
||||
ionicity is read from the 'q' column of the cluster.
|
||||
all atoms of a chemical element must have the same charge state
|
||||
because ionicity has to be specified per element.
|
||||
this function writes the average of all charge states of an element.
|
||||
|
||||
@param f: file or output stream (an object with a write method)
|
||||
|
||||
@return: None
|
||||
"""
|
||||
data = self.params.cluster.data
|
||||
elements = np.unique(data['t'])
|
||||
for element in elements:
|
||||
idx = np.where(data['t'] == element)
|
||||
charge = np.mean(data['q'][idx])
|
||||
f.write("{t} {q}\n".format(t=element, q=charge))
|
||||
|
||||
f.write("-1\n")
|
||||
|
||||
def write_input(self, f):
|
||||
"""
|
||||
write the PHAGEN input file.
|
||||
|
||||
@param f: file path or output stream (an object with a write method).
|
||||
|
||||
@return: None
|
||||
"""
|
||||
phagen_params = {}
|
||||
phagen_params['emin'] = self.params.kinetic_energies.min() / ERYDBERG
|
||||
phagen_params['emax'] = self.params.kinetic_energies.max() / ERYDBERG
|
||||
phagen_params['delta'] = (phagen_params['emax'] - phagen_params['emin']) / \
|
||||
(self.params.kinetic_energies.shape[0] - 1)
|
||||
if phagen_params['delta'] < 0.0001:
|
||||
phagen_params['delta'] = 0.1
|
||||
phagen_params['edge'] = state_to_edge(self.params.initial_state) # possibly not used
|
||||
phagen_params['edge1'] = 'm4' # auger not supported
|
||||
phagen_params['edge2'] = 'm4' # auger not supported
|
||||
phagen_params['cip'] = self.params.binding_energy / ERYDBERG
|
||||
if phagen_params['cip'] < 0.001:
|
||||
raise ValueError("binding energy parameter is zero.")
|
||||
|
||||
if np.sum(np.abs(self.params.cluster.data['q']) >= 0.001) > 0:
|
||||
phagen_params['ionzst'] = 'ionic'
|
||||
else:
|
||||
phagen_params['ionzst'] = 'neutral'
|
||||
|
||||
if hasattr(f, "write"):
|
||||
f.write("&job\n")
|
||||
f.write("calctype='xpd',\n")
|
||||
f.write("coor='angs',\n")
|
||||
f.write("cip={cip},\n".format(**phagen_params))
|
||||
f.write("edge='{edge}',\n".format(**phagen_params))
|
||||
f.write("edge1='{edge1}',\n".format(**phagen_params))
|
||||
f.write("edge2='{edge1}',\n".format(**phagen_params))
|
||||
f.write("gamma=0.03,\n")
|
||||
f.write("lmax_mode=2,\n")
|
||||
f.write("lmaxt=50,\n")
|
||||
f.write("emin={emin},\n".format(**phagen_params))
|
||||
f.write("emax={emax},\n".format(**phagen_params))
|
||||
f.write("delta={delta},\n".format(**phagen_params))
|
||||
f.write("potgen='in',\n")
|
||||
f.write("potype='hedin',\n")
|
||||
f.write("norman='stdcrm',\n")
|
||||
f.write("ovlpfac=0.0,\n")
|
||||
f.write("ionzst='{ionzst}',\n".format(**phagen_params))
|
||||
f.write("charelx='ex',\n")
|
||||
f.write("l2h=4\n")
|
||||
f.write("&end\n")
|
||||
f.write("comment 1\n")
|
||||
f.write("comment 2\n")
|
||||
f.write("\n")
|
||||
|
||||
self.write_cluster(f)
|
||||
self.write_ionicity(f)
|
||||
else:
|
||||
with open(f, "w") as fi:
|
||||
self.write_input(fi)
|
||||
|
||||
def parse_phagen_phase(self, f):
|
||||
"""
|
||||
parse the phase output file from PHAGEN.
|
||||
|
||||
the phase file is written to div/phases.dat.
|
||||
it contains the following columns:
|
||||
|
||||
@arg e energy (Ry)
|
||||
@arg x1 unknown 1
|
||||
@arg x2 unknown 2
|
||||
@arg na atom index (1-based)
|
||||
@arg nl angular momentum quantum number l
|
||||
@arg tr real part of the scattering matrix element
|
||||
@arg ti imaginary part of the scattering matrix element
|
||||
@arg ph phase shift
|
||||
|
||||
the data is translated into the self.scattering array.
|
||||
|
||||
@arg e energy (eV)
|
||||
@arg a atom index (1-based)
|
||||
@arg l angular momentum quantum number l
|
||||
@arg t complex scattering matrix element
|
||||
|
||||
@param f: file or path (any file-like or path-like object that can be passed to numpy.genfromtxt).
|
||||
|
||||
@return: None
|
||||
"""
|
||||
dt = [('e', 'f4'), ('x1', 'f4'), ('x2', 'f4'), ('na', 'i4'), ('nl', 'i4'),
|
||||
('tr', 'f8'), ('ti', 'f8'), ('ph', 'f4')]
|
||||
data = np.genfromtxt(f, dtype=dt)
|
||||
|
||||
self.scattering = np.resize(self.scattering, data.shape)
|
||||
scat = self.scattering
|
||||
scat['e'] = data['e'] * ERYDBERG
|
||||
scat['a'] = data['na']
|
||||
scat['l'] = data['nl']
|
||||
scat['t'] = data['tr'] + 1j * data['ti']
|
||||
|
||||
def write_edac_scattering(self, filename_format, phases=False):
|
||||
"""
|
||||
write scatterer files for EDAC.
|
||||
|
||||
produces one file for each atom class in self.scattering.
|
||||
|
||||
@param filename_format: file name including a placeholder {} for the atom class.
|
||||
|
||||
@param phases: write phase files instead of t-matrix files.
|
||||
|
||||
@return: dictionary that maps atom classes to file names
|
||||
"""
|
||||
if phases:
|
||||
write = self.write_edac_phase_file
|
||||
else:
|
||||
write = self.write_edac_scattering_file
|
||||
scat = self.scattering
|
||||
atoms = np.unique(scat['a'])
|
||||
files = {}
|
||||
for atom in atoms:
|
||||
f = filename_format.format(atom)
|
||||
sel = scat['a'] == atom
|
||||
idx = np.where(sel)
|
||||
atom_scat = scat[idx]
|
||||
write(f, atom_scat)
|
||||
files[atom] = f
|
||||
|
||||
return files
|
||||
|
||||
def write_edac_scattering_file(self, f, scat):
|
||||
"""
|
||||
write a scatterer file for EDAC.
|
||||
|
||||
@param f: file path or output stream (an object with a write method).
|
||||
|
||||
@param scat: a slice of the self.scattering array belonging to the same atom class.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
if hasattr(f, "write"):
|
||||
energies = np.unique(scat['e'])
|
||||
ne = energies.shape[0]
|
||||
lmax = scat['l'].max()
|
||||
if ne == 1:
|
||||
f.write("1 {lmax} regular tl\n".format(lmax=lmax))
|
||||
else:
|
||||
f.write("{nk} E(eV) {lmax} regular tl\n".format(nk=ne, lmax=lmax))
|
||||
for energy in energies:
|
||||
sel = scat['e'] == energy
|
||||
idx = np.where(sel)
|
||||
energy_scat = scat[idx]
|
||||
if ne > 1:
|
||||
f.write("{0:.3f} ".format(energy))
|
||||
for item in energy_scat:
|
||||
f.write(" {0:.6f} {1:.6f}".format(item['t'].real, item['t'].imag))
|
||||
for i in range(len(energy_scat), lmax + 1):
|
||||
f.write(" 0 0")
|
||||
f.write("\n")
|
||||
else:
|
||||
with open(f, "w") as fi:
|
||||
self.write_edac_scattering_file(fi, scat)
|
||||
|
||||
def write_edac_phase_file(self, f, scat):
|
||||
"""
|
||||
write a phase file for EDAC.
|
||||
|
||||
@param f: file path or output stream (an object with a write method).
|
||||
|
||||
@param scat: a slice of the self.scattering array belonging to the same atom class.
|
||||
|
||||
@return: None
|
||||
"""
|
||||
if hasattr(f, "write"):
|
||||
energies = np.unique(scat['e'])
|
||||
ne = energies.shape[0]
|
||||
lmax = scat['l'].max()
|
||||
if ne == 1:
|
||||
f.write("1 {lmax} regular real\n".format(lmax=lmax))
|
||||
else:
|
||||
f.write("{nk} E(eV) {lmax} regular real\n".format(nk=ne, lmax=lmax))
|
||||
for energy in energies:
|
||||
sel = scat['e'] == energy
|
||||
idx = np.where(sel)
|
||||
energy_scat = scat[idx]
|
||||
if ne > 1:
|
||||
f.write("{0:.3f} ".format(energy))
|
||||
for item in energy_scat:
|
||||
f.write(" {0:.6f}".format(np.angle(item['t'])))
|
||||
for i in range(len(energy_scat), lmax + 1):
|
||||
f.write(" 0")
|
||||
f.write("\n")
|
||||
else:
|
||||
with open(f, "w") as fi:
|
||||
self.write_edac_phase_file(fi, scat)
|
||||
|
||||
def parse_radial_file(self, f):
|
||||
"""
|
||||
parse the radial matrix element output file from phagen.
|
||||
|
||||
@param f: file or path (any file-like or path-like object that can be passed to numpy.genfromtxt).
|
||||
|
||||
@return: None
|
||||
"""
|
||||
dt = [('ar', 'f8'), ('ai', 'f8'), ('br', 'f8'), ('bi', 'f8')]
|
||||
data = np.genfromtxt(f, dtype=dt)
|
||||
|
||||
self.emission = np.resize(self.emission, data.shape)
|
||||
emission = self.emission
|
||||
emission['dw'] = data['ar'] + 1j * data['ai']
|
||||
emission['up'] = data['br'] + 1j * data['bi']
|
||||
|
||||
def write_edac_emission(self, f):
|
||||
"""
|
||||
write the radial photoemission matrix element in EDAC format.
|
||||
|
||||
requires self.emission, self.params.kinetic_energies and self.params.initial_state.
|
||||
|
||||
@param f: file path or output stream (an object with a write method).
|
||||
|
||||
@return: None
|
||||
"""
|
||||
if hasattr(f, "write"):
|
||||
l0 = self.params.l_init
|
||||
energies = self.params.kinetic_energies
|
||||
emission = self.emission
|
||||
emission['e'] = energies
|
||||
ne = energies.shape[0]
|
||||
if ne == 1:
|
||||
f.write("1 regular2 {l0}\n".format(l0=l0))
|
||||
else:
|
||||
f.write("{nk} E(eV) regular2 {l0}\n".format(nk=ne, l0=l0))
|
||||
for item in emission:
|
||||
if ne > 1:
|
||||
f.write("{0:.3f} ".format(item['e']))
|
||||
f.write(" {0:.6f} {1:.6f}".format(item['up'].real, item['up'].imag))
|
||||
f.write(" {0:.6f} {1:.6f}".format(item['dw'].real, item['dw'].imag))
|
||||
f.write("\n")
|
||||
else:
|
||||
with open(f, "w") as of:
|
||||
self.write_edac_emission(of)
|
Reference in New Issue
Block a user