Updated documentation and tutorials.
Download and install section contains now information about how to install MsSpec using Docker. Tutorials have been updated a bit.
This commit is contained in:
parent
08c6ea7a34
commit
b011e4348c
|
@ -1,3 +1,7 @@
|
||||||
|
|
||||||
|
.. |LINUXSCRIPT| replace:: https://git.ipr.univ-rennes1.fr/epsi/msspec_python3/raw/branch/devel/utils/dockerized/linux/msspec
|
||||||
|
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
Download and install notes
|
Download and install notes
|
||||||
##########################
|
##########################
|
||||||
|
@ -9,36 +13,138 @@ Installation
|
||||||
|
|
||||||
|
|
||||||
There are 2 ways to install MsSpec. You can either:
|
There are 2 ways to install MsSpec. You can either:
|
||||||
- Use a Docker image
|
- Use a Docker image. This is, by far, the most straightforward and easy way to work with MsSpec.
|
||||||
- Compile your own version
|
- Compile your own version for your system.
|
||||||
|
|
||||||
|
|
||||||
Using a Docker image
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
You first need Docker (Docker Desktop) to be installed on your system.
|
1. Using a Docker image
|
||||||
This is really straightforward. Please see the `Docker website
|
-----------------------
|
||||||
<https://www.docker.com>`_ for more information.
|
|
||||||
|
You first need `Docker <https://www.docker.com>`_ to be installed on your system.
|
||||||
|
This is really straightforward. Please see the `Docker documentation
|
||||||
|
<https://docs.docker.com/engine/install/>`_ for more information depending on
|
||||||
|
your OS.
|
||||||
|
|
||||||
|
|
||||||
- pull the image from our repository:
|
* **For Linux**,
|
||||||
::
|
|
||||||
docker pull iprcnrs/msspec:latest
|
|
||||||
|
|
||||||
That's all. MsSpec is installed. Now the command to launch the MsSpec environment depends slighly on the
|
* Download :download:`this script <../../utils/dockerized/linux/msspec>` and
|
||||||
OS you are runing:
|
make it executable (with the *chmod u+x msspec* command)
|
||||||
|
|
||||||
- For Linux
|
* Place it in a location known to your $PATH (or add this location to your $PATH if needed).
|
||||||
::
|
The *~/.local/bin* folder is a good choice.
|
||||||
xhost +
|
|
||||||
docker run --rm -it -e DISPLAY=$DISPLAY --net=host -v msspec_data:/home/msspec/data msspec
|
|
||||||
|
|
||||||
- For Windows
|
* **For Windows**
|
||||||
::
|
|
||||||
docker run --rm -it -e DISPLAY=host.internal.display:0 -v msspec_data:/home/msspec/data msspec
|
* You need a running X server. Download and install `VcXsrv <https://sourceforge.net/projects/vcxsrv/>`_
|
||||||
|
|
||||||
|
* Install the small :download:`MsSpec frontend <../../utils/dockerized/windows/msspec_setup.exe>`
|
||||||
|
|
||||||
|
* While not necessary, it is also a good idea to use a better terminal application than the default one.
|
||||||
|
`Windows Terminal <https://www.microsoft.com/fr-fr/p/windows-terminal/9n0dx20hk701#activetab=pivot:overviewtab>`_
|
||||||
|
may be a good choice.
|
||||||
|
|
||||||
|
* **For Mac**
|
||||||
|
|
||||||
|
* *To be documented*
|
||||||
|
|
||||||
|
|
||||||
Compile your own version
|
Open a terminal in Linux, or a powershell in Windows and type in::
|
||||||
------------------------
|
|
||||||
|
msspec
|
||||||
|
|
||||||
|
The first time you run the command, it will download the msspec image (it may takes several minutes or half an hour
|
||||||
|
depending on your internet connexion).
|
||||||
|
The command will automatically create a new container and start it.
|
||||||
|
As the command was entered without any argument on the command-line, the help message should be printed on the screen
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
Usage: 1) msspec -p [PYTHON OPTIONS] SCRIPT [ARGUMENTS...]
|
||||||
|
2) msspec [-l FILE | -i | -h]
|
||||||
|
3) msspec [bash | reset]
|
||||||
|
|
||||||
|
Form (1) is used to launch a script
|
||||||
|
Form (2) is used to load a hdf5 data file
|
||||||
|
Form (3) is used to control the Docker container/image.
|
||||||
|
|
||||||
|
List of possible options:
|
||||||
|
-p Pass every arguments after this option to the msspec
|
||||||
|
virtual environment Python interpreter.
|
||||||
|
-i Run the interactive Python interpreter within msspec
|
||||||
|
virtual environment.
|
||||||
|
-l Load and display a *.hdf5 data file in a graphical
|
||||||
|
window.
|
||||||
|
-v Print the version.
|
||||||
|
-h Show this help message.
|
||||||
|
|
||||||
|
bash This command starts an interactive bash shell in the
|
||||||
|
MsSpec container.
|
||||||
|
reset This command removes the MsSpec container (but not the
|
||||||
|
image). Changes made in the container will be lost and
|
||||||
|
any new call to msspec will recreate a new fresh container.
|
||||||
|
|
||||||
|
|
||||||
|
2. Compile your own version
|
||||||
|
---------------------------
|
||||||
|
|
||||||
To install MsSpec this way, follow the instructions `here <https://git.ipr.univ-rennes1.fr/epsi/msspec_python3/src/branch/devel>`_
|
To install MsSpec this way, follow the instructions `here <https://git.ipr.univ-rennes1.fr/epsi/msspec_python3/src/branch/devel>`_
|
||||||
|
|
||||||
|
|
||||||
|
***************************
|
||||||
|
Running your Python scripts
|
||||||
|
***************************
|
||||||
|
|
||||||
|
You can run your MsSpec Python scripts (e.g. *my_script.py*) by typing in::
|
||||||
|
|
||||||
|
msspec -p my_script.py
|
||||||
|
|
||||||
|
This command is equivalent to activating the Python virtual environment MsSpec is intsalled in
|
||||||
|
and to run the Python interpreter of that environment (everything that follows the -p option is
|
||||||
|
passed to the python command).
|
||||||
|
|
||||||
|
You can also launch the Interactive Python (iPython) of MsSpec::
|
||||||
|
|
||||||
|
msspec -i
|
||||||
|
|
||||||
|
Inside this interactive session, you can run a script with the command::
|
||||||
|
|
||||||
|
%run my_script.py
|
||||||
|
|
||||||
|
You can interact with your filesystem with the classical *cd*, *ls*, *cp*, *rm*... commands.
|
||||||
|
and you can edit your script with::
|
||||||
|
|
||||||
|
%ed my_script.py
|
||||||
|
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
**If using the Docker image of MsSpec in Linux**, your home folder on the host machine is bind mounted
|
||||||
|
in the same location in the container and your UID and GID are also set so that creating files within
|
||||||
|
your home file hierarchy is totally transparent.
|
||||||
|
|
||||||
|
**If using the Docker image of MsSpec in Windows**, the drive containing your "My Documents" folder on the
|
||||||
|
host machine is bind mounted on the container at the root of the filesystem. For example, if your
|
||||||
|
"My documents" folder on Windows are in 'D:\\Home\\Bob\\MyDocuments', it will be available in the container as
|
||||||
|
'/D/Home/Bob/MyDocuments'. It has two consequences:
|
||||||
|
|
||||||
|
#. The msspec command will fail if running on another drive than the one where is located "My Documents"
|
||||||
|
|
||||||
|
#. You have to specify filenames with the Unix slashes. For example if you want to run the script located
|
||||||
|
in *.\\results\\my_script.py*, you will have to enter *msspec -p ./results/my_script.py*
|
||||||
|
|
||||||
|
|
||||||
|
**************
|
||||||
|
Uninstallation
|
||||||
|
**************
|
||||||
|
|
||||||
|
* **Under Linux**, type in::
|
||||||
|
|
||||||
|
msspec -u
|
||||||
|
|
||||||
|
* **Under Windows**, simply `uninstall the application from the Settings page
|
||||||
|
<https://support.microsoft.com/en-us/windows/uninstall-or-remove-apps-and-programs-in-windows-10-4b55f974-2cc6-2d2b-d092-5905080eaf98>`_.
|
||||||
|
A command window will pop-up and you will have to answer 'y' to remove MsSpec.
|
||||||
|
|
||||||
|
* **Under Mac OS**, *to be documented.*
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
:orphan:
|
|
||||||
|
|
||||||
.. automodule:: config
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:show-inheritance:
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
:orphan:
|
||||||
|
|
||||||
|
.. automodule:: looper
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
|
@ -0,0 +1,6 @@
|
||||||
|
:orphan:
|
||||||
|
|
||||||
|
.. automodule:: version
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
|
@ -1,181 +1,130 @@
|
||||||
# coding: utf-8
|
# coding: utf8
|
||||||
|
|
||||||
from msspec.utils import *
|
|
||||||
from ase.build import bulk
|
from ase.build import bulk
|
||||||
from ase.visualize import view
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from msspec.calculator import MSSPEC, XRaySource
|
from msspec.calculator import MSSPEC, XRaySource
|
||||||
from msspec.iodata import Data
|
from msspec.utils import hemispherical_cluster, get_atom_index
|
||||||
from itertools import product
|
|
||||||
|
|
||||||
DATA = None
|
def create_clusters(nplanes=6):
|
||||||
|
def get_AlN_tags_planes(side, emitter):
|
||||||
|
AlN = bulk('AlN', crystalstructure='wurtzite', a=3.11, c=4.975)
|
||||||
|
[atom.set('tag', i) for i, atom in enumerate(AlN)]
|
||||||
|
if side == 'Al':
|
||||||
|
AlN.rotate([0,0,1],[0,0,-1])
|
||||||
|
Al_planes = range(0, nplanes, 2)
|
||||||
|
N_planes = range(1, nplanes, 2)
|
||||||
|
else:
|
||||||
|
N_planes = range(0, nplanes, 2)
|
||||||
|
Al_planes = range(1, nplanes, 2)
|
||||||
|
if emitter == 'Al':
|
||||||
|
tags = [0, 2]
|
||||||
|
planes = Al_planes
|
||||||
|
else:
|
||||||
|
tags = [1, 3]
|
||||||
|
planes = N_planes
|
||||||
|
return AlN, tags, planes
|
||||||
|
|
||||||
def AlN_cluster(emitter_tag, emitter_plane, diameter=0, planes=0, term_anion=True, tetra_down=True):
|
|
||||||
"""
|
|
||||||
This function is a kind of overload of the hemispherical_cluster function with specific attributes
|
|
||||||
to the AlN structure
|
|
||||||
|
|
||||||
:param emitter_tag: An integer that allows to identify the kind of atom to use as the emitter
|
|
||||||
:param emitter_plane: The plane where the emitter is. 0 means the surface.
|
|
||||||
:param diameter: The diameter of the cluster (in Angstroms).
|
|
||||||
:param planes: The total number of planes.
|
|
||||||
:param term_anion: True if the surface plane is anion terminated, False otherwise
|
|
||||||
:param tetra_down: The orientation of the tetrahedral
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
# create the initial cluster of AlN
|
|
||||||
cluster = bulk('AlN', crystalstructure='wurtzite', a=3.11, c=4.975)
|
|
||||||
|
|
||||||
# tag each atom in the unit cell because they are all in a different chemical/geometrical environment
|
|
||||||
# (0 and 2 for Aluminum's atoms and 1 and 3 for Nitride's atoms)
|
|
||||||
[atom.set('tag', i) for i, atom in enumerate(cluster)]
|
|
||||||
|
|
||||||
# change the orientation of the tetrahedron
|
|
||||||
if not tetra_down:
|
|
||||||
cluster.rotate(180, 'y')
|
|
||||||
|
|
||||||
# From this base pattern, creat an hemispherically shaped cluster
|
|
||||||
cluster = hemispherical_cluster(cluster, emitter_tag=emitter_tag, emitter_plane=emitter_plane, diameter=diameter,
|
|
||||||
planes=planes)
|
|
||||||
|
|
||||||
# Depending on the number of planes above the emitter, the termination may not be the one you wish,
|
|
||||||
# we test this and raise an exception in such a case
|
|
||||||
|
|
||||||
# Get the termination by finding the kind of one atom located at the topmost z coordinate
|
|
||||||
termination = cluster[np.argsort(cluster.get_positions()[:, 2])[-1]].symbol
|
|
||||||
|
|
||||||
# test if the combination of parameters is possible
|
|
||||||
assert (termination == 'N' and term_anion) or (termination == 'Al' and not term_anion), (
|
|
||||||
"This termination isn't compatible with your others parameters, you must change the tag of "
|
|
||||||
"your emitter, the plane of your emitter or your termination")
|
|
||||||
|
|
||||||
return cluster
|
|
||||||
|
|
||||||
def create_clusters(side='Al', emitter='Al', diameter=15, planes=6):
|
|
||||||
clusters = []
|
clusters = []
|
||||||
if side == 'Al':
|
for side in ('Al', 'N'):
|
||||||
term_anion = tetra_down = False
|
for emitter in ('Al', 'N'):
|
||||||
elif side == 'N':
|
AlN, tags, planes = get_AlN_tags_planes(side, emitter)
|
||||||
term_anion = tetra_down = True
|
for emitter_tag in tags:
|
||||||
|
for emitter_plane in planes:
|
||||||
if emitter == 'Al':
|
cluster = hemispherical_cluster(AlN,
|
||||||
tags = (0, 2)
|
emitter_tag=emitter_tag,
|
||||||
level = '2p'
|
emitter_plane=emitter_plane,
|
||||||
ke = 1407.
|
planes=emitter_plane+2)
|
||||||
elif emitter == 'N':
|
cluster.absorber = get_atom_index(cluster, 0, 0, 0)
|
||||||
tags = (1, 3)
|
cluster.info.update({
|
||||||
level = '1s'
|
'emitter_plane': emitter_plane,
|
||||||
ke = 1083.
|
'emitter_tag' : emitter_tag,
|
||||||
|
'emitter' : emitter,
|
||||||
for emitter_tag, emitter_plane in product(tags, range(0, planes)):
|
'side' : side,
|
||||||
nplanes = emitter_plane + 2
|
})
|
||||||
try:
|
clusters.append(cluster)
|
||||||
cluster = AlN_cluster(emitter_tag, emitter_plane, diameter=diameter, planes=nplanes, term_anion=term_anion,
|
print("Added cluster {}-side, emitter {}(tag {:d}) in "
|
||||||
tetra_down=tetra_down)
|
"plane #{:d}".format(side, emitter, emitter_tag,
|
||||||
except AssertionError:
|
emitter_plane))
|
||||||
continue
|
|
||||||
cluster.absorber = get_atom_index(cluster, 0, 0, 0)
|
|
||||||
cluster.info.update({'emitter_plane': emitter_plane,
|
|
||||||
'emitter_tag': emitter_tag,
|
|
||||||
'emitter': emitter,
|
|
||||||
'side': side,
|
|
||||||
'level': level,
|
|
||||||
'ke': ke})
|
|
||||||
clusters.append(cluster)
|
|
||||||
|
|
||||||
return clusters
|
return clusters
|
||||||
|
|
||||||
def compute_polar_scans(clusters, theta=np.arange(-20., 80., 1.), phi=0., data=DATA):
|
|
||||||
|
def compute(clusters, theta=np.arange(-20., 80., 1.), phi=0.):
|
||||||
|
data = None
|
||||||
for ic, cluster in enumerate(clusters):
|
for ic, cluster in enumerate(clusters):
|
||||||
# select the spectroscopy of the calculation and create a new folder for each calculation
|
# Retrieve info from cluster object
|
||||||
side, emitter, tag, plane, level, ke = [cluster.info[k] for k in ('side', 'emitter', 'emitter_tag',
|
side = cluster.info['side']
|
||||||
'emitter_plane', 'level', 'ke')]
|
emitter = cluster.info['emitter']
|
||||||
calc = MSSPEC(spectroscopy='PED', folder='calc{:0>3d}_S{}_E{}_T{:d}_P{:d}'.format(ic, side, emitter, tag,
|
plane = cluster.info['emitter_plane']
|
||||||
plane))
|
tag = cluster.info['emitter_tag']
|
||||||
calc.calculation_parameters.scattering_order = max(1, min(4, plane))
|
|
||||||
|
# Set the level and the kinetic energy
|
||||||
|
if emitter == 'Al':
|
||||||
|
level = '2p'
|
||||||
|
ke = 1407.
|
||||||
|
elif emitter == 'N':
|
||||||
|
level = '1s'
|
||||||
|
ke = 1083.
|
||||||
|
|
||||||
|
calc = MSSPEC(spectroscopy='PED', algorithm='expansion')
|
||||||
|
|
||||||
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
||||||
calc.source_parameters.theta = -35
|
calc.source_parameters.theta = -35
|
||||||
|
|
||||||
calc.detector_parameters.angular_acceptance = 4.
|
calc.detector_parameters.angular_acceptance = 4.
|
||||||
calc.detector_parameters.average_sampling = 'medium'
|
calc.detector_parameters.average_sampling = 'medium'
|
||||||
calc.calculation_parameters.path_filtering = 'forward_scattering'
|
|
||||||
|
calc.calculation_parameters.scattering_order = max(1, min(4, plane))
|
||||||
|
calc.calculation_parameters.path_filtering = 'forward_scattering'
|
||||||
calc.calculation_parameters.off_cone_events = 1
|
calc.calculation_parameters.off_cone_events = 1
|
||||||
[a.set('forward_angle', 30.) for a in cluster]
|
[a.set('forward_angle', 30.) for a in cluster]
|
||||||
calc.calculation_parameters.vibrational_damping = 'averaged_tl'
|
|
||||||
[a.set('mean_square_vibration', 0.006) for a in cluster]
|
|
||||||
calc.set_atoms(cluster)
|
calc.set_atoms(cluster)
|
||||||
|
|
||||||
data = calc.get_theta_scan(level=level, theta=theta, phi=phi, kinetic_energy=ke, data=data)
|
data = calc.get_theta_scan(level=level, theta=theta, phi=phi,
|
||||||
|
kinetic_energy=ke, data=data)
|
||||||
dset = data[-1]
|
dset = data[-1]
|
||||||
dset.title = 'Polar scan {emitter:s}({level:s} tag {tag:d}) in plane #{plane:d}'.format(emitter=emitter,
|
dset.title = "\'{}\' side - {}({}) tag #{:d}, plane #{:d}".format(
|
||||||
level=level, tag=tag,
|
side, emitter, level, tag, plane)
|
||||||
plane=plane)
|
|
||||||
for name, value in cluster.info.items():
|
|
||||||
dset.add_parameter(group='AlnTuto', name=name, value=str(value), unit="")
|
|
||||||
#data.save('all_polar_scans.hdf5', append=True)
|
|
||||||
data.save('all_polar_scans.hdf5')
|
|
||||||
|
|
||||||
def analysis(filename='all_polar_scans.hdf5'):
|
return data
|
||||||
data=Data.load(filename)
|
|
||||||
# create datasets to store the sum of all emitter
|
|
||||||
|
def analysis(data):
|
||||||
tmp_data = {}
|
tmp_data = {}
|
||||||
for dset in data:
|
for dset in data:
|
||||||
# retrieve some info
|
info = dset.get_cluster().info
|
||||||
side = dset.get_parameter(group='AlnTuto', name='side')['value']
|
side = info['side']
|
||||||
emitter = dset.get_parameter(group='AlnTuto', name='emitter')['value']
|
emitter = info['emitter']
|
||||||
try:
|
try:
|
||||||
key = '{}_{}'.format(side, emitter)
|
key = '{}_{}'.format(side, emitter)
|
||||||
tmp_data[key] += dset.cross_section
|
tmp_data[key] += dset.cross_section
|
||||||
except KeyError:
|
except KeyError:
|
||||||
tmp_data[key] = dset.cross_section.copy()
|
tmp_data[key] = dset.cross_section.copy()
|
||||||
|
|
||||||
# get the index of theta = 0;
|
tmp_data['theta'] = dset.theta.copy()
|
||||||
it0 = np.where(dset.theta == 0)[0][0]
|
tmp_data['Al_side'] = tmp_data['Al_Al'] / tmp_data['Al_N']
|
||||||
for key, cs in tmp_data.items():
|
tmp_data['N_side'] = tmp_data['N_Al'] / tmp_data['N_N']
|
||||||
tmp_data[key + '_norm'] = cs / cs[it0]
|
|
||||||
|
|
||||||
tmp_data['Al_side'] = tmp_data['Al_Al_norm'] / tmp_data['Al_N_norm']
|
|
||||||
tmp_data['N_side'] = tmp_data['N_Al_norm'] / tmp_data['N_N_norm']
|
|
||||||
|
|
||||||
# now add all columns
|
# now add all columns
|
||||||
substrate_dset = data.add_dset('Total substrate signal')
|
substrate_dset = data.add_dset('Total substrate signal')
|
||||||
substrate_dset.add_columns(theta=dset.theta.copy())
|
|
||||||
substrate_dset.add_columns(**tmp_data)
|
substrate_dset.add_columns(**tmp_data)
|
||||||
|
|
||||||
view = substrate_dset.add_view('Al side',
|
|
||||||
title=r'AlN Polar scan in the (10$\bar{1}$0) azimuthal plane - Al side polarity',
|
|
||||||
xlabel=r'$\Theta (\degree$)',
|
|
||||||
ylabel='Signal (a.u.)')
|
|
||||||
view.select('theta', 'Al_Al_norm', legend='Al 2p')
|
|
||||||
view.select('theta', 'Al_N_norm', legend='N 1s')
|
|
||||||
view.set_plot_options(autoscale=True)
|
|
||||||
|
|
||||||
view = substrate_dset.add_view('N side',
|
|
||||||
title=r'AlN Polar scan in the (10$\bar{1}$0) azimuthal plane - N side polarity',
|
|
||||||
xlabel=r'$\Theta (\degree$)',
|
|
||||||
ylabel='Signal (a.u.)')
|
|
||||||
view.select('theta', 'N_Al_norm', legend='Al 2p')
|
|
||||||
view.select('theta', 'N_N_norm', legend='N 1s')
|
|
||||||
view.set_plot_options(autoscale=True)
|
|
||||||
|
|
||||||
view = substrate_dset.add_view('Ratios',
|
view = substrate_dset.add_view('Ratios',
|
||||||
title=r'Al(2p)/N(1s) ratios on both polar sides of AlN in the (10$\bar{1}$0) '
|
title=r'Al(2p)/N(1s) ratios on both polar '
|
||||||
|
r'sides of AlN in the (10$\bar{1}$0) '
|
||||||
r'azimuthal plane',
|
r'azimuthal plane',
|
||||||
xlabel=r'$\Theta (\degree$)',
|
xlabel=r'$\Theta (\degree$)',
|
||||||
ylabel='Intenisty ratio')
|
ylabel='Intenisty ratio')
|
||||||
view.select('theta', 'Al_side', legend='Al side', where="theta >= 0 and theta <=70")
|
view.select('theta', 'Al_side', legend='Al side',
|
||||||
view.select('theta', 'N_side', legend='N side', where="theta >= 0 and theta <=70")
|
where="theta >= 0 and theta <=70")
|
||||||
|
view.select('theta', 'N_side', legend='N side',
|
||||||
|
where="theta >= 0 and theta <=70")
|
||||||
view.set_plot_options(autoscale=True)
|
view.set_plot_options(autoscale=True)
|
||||||
|
|
||||||
data.save('analysis.hdf5')
|
return data
|
||||||
data.view()
|
|
||||||
|
|
||||||
|
|
||||||
DIAMETER = 10
|
clusters = create_clusters()
|
||||||
PLANES = 4
|
data = compute(clusters)
|
||||||
clusters = create_clusters(side='Al', emitter='Al', diameter=DIAMETER, planes=PLANES) + \
|
data = analysis(data)
|
||||||
create_clusters(side='Al', emitter='N', diameter=DIAMETER, planes=PLANES) + \
|
data.view()
|
||||||
create_clusters(side='N', emitter='Al', diameter=DIAMETER, planes=PLANES) + \
|
|
||||||
create_clusters(side='N', emitter='N', diameter=DIAMETER, planes=PLANES)
|
|
||||||
compute_polar_scans(clusters, phi=0.)
|
|
||||||
analysis()
|
|
||||||
|
|
|
@ -1,54 +1,25 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# coding: utf8
|
||||||
# vim: set fdm=indent ts=4 sw=4 sts=4 et ai tw=80 cc=+0 mouse=a nu : #
|
|
||||||
|
|
||||||
from msspec.calculator import MSSPEC, XRaySource
|
|
||||||
from msspec.utils import *
|
|
||||||
|
|
||||||
|
from msspec.calculator import MSSPEC
|
||||||
from ase.build import fcc111, add_adsorbate
|
from ase.build import fcc111, add_adsorbate
|
||||||
from ase.visualize import view
|
|
||||||
from msspec.iodata import cols2matrix
|
|
||||||
|
|
||||||
from matplotlib import pyplot as plt
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sys
|
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
all_z = np.arange(1.10, 1.50, 0.02)
|
all_z = np.arange(1.10, 1.65, 0.05)
|
||||||
all_z=(1.1,)
|
|
||||||
for zi, z0 in enumerate(all_z):
|
for zi, z0 in enumerate(all_z):
|
||||||
# construct the cluster
|
# construct the cluster
|
||||||
cluster = fcc111('Rh', size = (2,2,1))
|
cluster = fcc111('Rh', size = (2,2,1))
|
||||||
add_adsorbate(cluster, 'O', z0, position = 'fcc')
|
|
||||||
cluster.pop(3)
|
cluster.pop(3)
|
||||||
#cluster.rotate('z',np.pi/3.)
|
add_adsorbate(cluster, 'O', z0, position = 'fcc')
|
||||||
#view(cluster)
|
|
||||||
|
|
||||||
cluster.absorber = len(cluster) - 1
|
cluster.absorber = len(cluster) - 1
|
||||||
|
|
||||||
calc = MSSPEC(spectroscopy = 'PED', folder = './RhO_z')
|
# Define a calculator
|
||||||
|
calc = MSSPEC(spectroscopy='PED', algorithm='inversion')
|
||||||
calc.set_atoms(cluster)
|
calc.set_atoms(cluster)
|
||||||
calc.calculation_parameters.scattering_order = 3
|
|
||||||
calc.calculation_parameters.RA_cutoff = 1
|
|
||||||
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
|
||||||
|
|
||||||
# compute
|
# Compute
|
||||||
level = '1s'
|
data = calc.get_theta_phi_scan(level='1s', kinetic_energy=723, data=data)
|
||||||
ke = 723.
|
|
||||||
|
|
||||||
|
|
||||||
data = calc.get_theta_phi_scan(level=level, kinetic_energy=ke, data=data)
|
|
||||||
|
|
||||||
# OPTIONAL, this will create an image of the data that you can combine
|
|
||||||
# in an animated gif
|
|
||||||
dset = data[-1]
|
dset = data[-1]
|
||||||
theta, phi, Xsec = cols2matrix(dset.theta, dset.phi, dset.cross_section)
|
dset.title = "{:d}) z = {:.2f} angstroms".format(zi, z0)
|
||||||
X, Y = np.meshgrid(np.radians(phi), 2*np.tan(np.radians(theta/2.)))
|
|
||||||
fig = plt.figure()
|
|
||||||
ax = fig.add_subplot(111, projection='polar')
|
|
||||||
im = ax.pcolormesh(X, Y, Xsec)
|
|
||||||
theta_ticks = np.arange(0, 91, 15)
|
|
||||||
plt.yticks(2 * np.tan(np.radians(theta_ticks/2.)), theta_ticks)
|
|
||||||
plt.title(r"$z_0 = {:.2f} \AA$".format(z0))
|
|
||||||
plt.savefig('image{:03d}.png'.format(zi))
|
|
||||||
|
|
||||||
data.view()
|
data.view()
|
||||||
|
|
|
@ -1,31 +1,17 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# coding: utf8
|
||||||
# vim: set fdm=indent ts=4 sw=4 sts=4 et ai tw=80 cc=+0 mouse=a nu : #
|
|
||||||
|
|
||||||
from msspec.calculator import MSSPEC, XRaySource
|
|
||||||
from msspec.utils import *
|
|
||||||
|
|
||||||
|
from msspec.calculator import MSSPEC
|
||||||
from ase import Atoms
|
from ase import Atoms
|
||||||
|
|
||||||
import numpy as np
|
# Create an atomic chain O-Rh
|
||||||
|
cluster = Atoms(['O', 'Rh'], positions = [(0,0,0), (0,0,4.)])
|
||||||
|
|
||||||
# Defining global variables
|
# Create the calculator
|
||||||
a0 = 6.0
|
calc = MSSPEC(spectroscopy = 'PED')
|
||||||
symbols = ('Rh', 'O')
|
calc.set_atoms(cluster)
|
||||||
ke = 723.
|
cluster.absorber = 0
|
||||||
level = '1s'
|
|
||||||
|
|
||||||
data = None
|
# compute
|
||||||
for symbol in symbols:
|
data = calc.get_scattering_factors(level='1s', kinetic_energy=723)
|
||||||
cluster = Atoms(symbol*2, positions = [(0,0,0), (0,0,a0)])
|
|
||||||
[a.set('mt_radius', 1.5) for a in cluster]
|
|
||||||
|
|
||||||
# Create the calculator
|
|
||||||
calc = MSSPEC(spectroscopy = 'PED')
|
|
||||||
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
|
||||||
calc.set_atoms(cluster)
|
|
||||||
cluster.absorber = 0
|
|
||||||
|
|
||||||
# compute
|
|
||||||
data = calc.get_scattering_factors(level=level, kinetic_energy=ke, data=data)
|
|
||||||
|
|
||||||
data.view()
|
data.view()
|
||||||
|
|
|
@ -82,13 +82,13 @@ Here is the script for the computation. (:download:`download <RhO.py>`)
|
||||||
.. literalinclude:: RhO.py
|
.. literalinclude:: RhO.py
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
.. note::
|
.. .. note::
|
||||||
After runing this script, you will get 20 images in your folder. You can merge them in one animated gif image
|
.. After runing this script, you will get 20 images in your folder. You can merge them in one animated gif image
|
||||||
like this:
|
.. like this:
|
||||||
|
..
|
||||||
.. code-block:: bash
|
.. .. code-block:: bash
|
||||||
|
..
|
||||||
convert -delay 50 -loop 0 image*.png animation.gif
|
.. convert -delay 50 -loop 0 image*.png animation.gif
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# coding: utf-8
|
# coding: utf8
|
||||||
|
|
||||||
from msspec.calculator import MSSPEC
|
from msspec.calculator import MSSPEC
|
||||||
from msspec.utils import *
|
from msspec.utils import hemispherical_cluster, get_atom_index
|
||||||
|
|
||||||
from ase.build import bulk
|
from ase.build import bulk
|
||||||
from ase.visualize import view
|
from ase.visualize import view
|
||||||
|
@ -9,20 +9,15 @@ from ase.visualize import view
|
||||||
a0 = 3.6 # The lattice parameter in angstroms
|
a0 = 3.6 # The lattice parameter in angstroms
|
||||||
|
|
||||||
# Create the copper cubic cell
|
# Create the copper cubic cell
|
||||||
cluster = bulk('Cu', a=a0, cubic=True)
|
copper = bulk('Cu', a=a0, cubic=True)
|
||||||
# Repeat the cell many times along x, y and z
|
cluster = hemispherical_cluster(copper, planes=3, emitter_plane=2)
|
||||||
cluster = cluster.repeat((4, 4, 4))
|
|
||||||
# Put the center of the structure at the origin
|
|
||||||
center_cluster(cluster)
|
|
||||||
# Keep atoms inside a given radius
|
|
||||||
cluster = cut_sphere(cluster, radius=a0 + .01)
|
|
||||||
# Keep only atoms below the plane z <= 0
|
|
||||||
cluster = cut_plane(cluster, z=0.1)
|
|
||||||
|
|
||||||
# Set the absorber (the deepest atom centered in the xy-plane)
|
# Set the absorber (the deepest atom centered in the xy-plane)
|
||||||
cluster.absorber = get_atom_index(cluster, 0, 0, -a0)
|
cluster.absorber = get_atom_index(cluster, 0, 0, 0)
|
||||||
|
|
||||||
# Create a calculator for the PhotoElectron Diffration
|
# Create a calculator for the PhotoElectron Diffration
|
||||||
calc = MSSPEC(spectroscopy='PED')
|
calc = MSSPEC(spectroscopy='PED')
|
||||||
|
|
||||||
# Set the cluster to use for the calculation
|
# Set the cluster to use for the calculation
|
||||||
calc.set_atoms(cluster)
|
calc.set_atoms(cluster)
|
||||||
|
|
||||||
|
@ -31,4 +26,6 @@ data = calc.get_theta_scan(level='2p3/2')
|
||||||
|
|
||||||
# Show the results
|
# Show the results
|
||||||
data.view()
|
data.view()
|
||||||
#calc.shutdown()
|
|
||||||
|
# Clean temporary files
|
||||||
|
calc.shutdown()
|
|
@ -16,7 +16,7 @@ Begin by typing:
|
||||||
:linenos:
|
:linenos:
|
||||||
:lineno-start: 1
|
:lineno-start: 1
|
||||||
|
|
||||||
# coding: utf-8
|
# coding: utf8
|
||||||
|
|
||||||
from msspec.calculator import MSSPEC
|
from msspec.calculator import MSSPEC
|
||||||
from msspec.utils import *
|
from msspec.utils import *
|
||||||
|
@ -45,27 +45,37 @@ This is the job of the *ase* module, so load the module
|
||||||
from ase.build import bulk
|
from ase.build import bulk
|
||||||
from ase.visualize import view
|
from ase.visualize import view
|
||||||
|
|
||||||
a0 = 3.6
|
a0 = 3.6 # The lattice parameter in angstroms
|
||||||
cluster = bulk('Cu', a = a0, cubic = True)
|
|
||||||
|
# Create the copper cubic cell
|
||||||
|
copper = bulk('Cu', a=a0, cubic=True)
|
||||||
view(cluster)
|
view(cluster)
|
||||||
|
|
||||||
In line 6 we load the :py:func:`bulk` function to create our atomic object and,
|
In line 6 we load the :py:func:`bulk` function to create our atomic object and,
|
||||||
in line 7, we load the :py:func:`view` function to actually view our cluster.
|
in line 7, we load the :py:func:`view` function to actually view our cluster.
|
||||||
|
|
||||||
The creation of the cluster is done line 10. The :py:func:`bulk` needs one
|
The creation of the cluster starts on line 12. We create first the copper cubic
|
||||||
argument which are the chemical species ('Cu' in our example). We also pass 2
|
cell with the The :py:func:`bulk` function. It needs one argument which are the
|
||||||
keyword (optional) arguments here:
|
chemical species ('Cu' in our example). We also pass 2 keyword (optional) arguments
|
||||||
|
here:
|
||||||
|
|
||||||
* The lattice parameter *a* in units of angströms.
|
* The lattice parameter *a* in units of angströms.
|
||||||
* The *cubic* keyword, to obtain a cubic cell rather than the fully
|
* The *cubic* keyword, to obtain a cubic cell rather than the fully
|
||||||
reduced one which is not cubic
|
reduced one which is not cubic
|
||||||
|
|
||||||
From now on, you can save your script as 'Cu.py' and open a terminal window in
|
From now on, you can save your script as 'Cu.py' and open a terminal window in
|
||||||
the same directory as this file. Launch your script using python:
|
the same directory as this file. Launch your script using 'python' or 'msspec -p'
|
||||||
|
(depending on your installation):
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python2 Cu.py
|
python Cu.py
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
msspec -p Cu.py
|
||||||
|
|
||||||
and a graphical window (the ase-gui) should open with a cubic cell of copper
|
and a graphical window (the ase-gui) should open with a cubic cell of copper
|
||||||
like this one:
|
like this one:
|
||||||
|
@ -80,34 +90,38 @@ like this one:
|
||||||
Obviously, multiple scattering calculations need more atoms to be accurate. Due
|
Obviously, multiple scattering calculations need more atoms to be accurate. Due
|
||||||
to the forward focusing effect in photo-electron diffraction, the best suited
|
to the forward focusing effect in photo-electron diffraction, the best suited
|
||||||
geometry for the cluster is hemispherical. Obtaining such a cluster is a
|
geometry for the cluster is hemispherical. Obtaining such a cluster is a
|
||||||
straightforward process:
|
straightforward process thanks to the :py:func:`utils.hemispherical_cluster` function.
|
||||||
|
This function will basically create a cluster based on a pattern (the cubic copper
|
||||||
1. Repeat the previous cell in all 3 directions
|
cell here).
|
||||||
2. center the structure
|
|
||||||
3. Keep only atoms within a given radius from center
|
|
||||||
4. Keep only atoms within one halh of the sphere.
|
|
||||||
|
|
||||||
Modify your script like this and run it again.
|
Modify your script like this and run it again.
|
||||||
|
|
||||||
.. literalinclude:: Cu_simple.py
|
.. literalinclude:: Cu.py
|
||||||
:linenos:
|
:linenos:
|
||||||
:lineno-start: 1
|
:lineno-start: 1
|
||||||
:lines: 1-21
|
:lines: 1-13
|
||||||
|
|
||||||
|
|
||||||
Don't forget to add the line to view the cluster at the end of the script and run
|
Don't forget to add the line to view the cluster at the end of the script and run
|
||||||
the script again.
|
the script again. The :py:func:`hemispherical_cluster` works in 3 simple steps:
|
||||||
|
|
||||||
|
#. Repeat the given *pattern* in all 3 directions
|
||||||
|
#. Center this new set of atoms and cut a sphere from the center
|
||||||
|
#. Remove the upper half of the created 'sphere'.
|
||||||
|
|
||||||
|
To get more information about how to use this function, have a look at the :ref:`hemispherical_cluster_faq` section in the :ref:`faq`.
|
||||||
|
|
||||||
.. figure:: Cu_fig2.png
|
.. figure:: Cu_fig2.png
|
||||||
:align: center
|
:align: center
|
||||||
:width: 60%
|
:width: 60%
|
||||||
|
|
||||||
Figure 2. The different steps to output a cluster.
|
Figure 2. The different steps to output a cluster.
|
||||||
a) After repeat, b) after cut_sphere, c) after cut_plane
|
a) After repeat, b) after cutting a sphere, c) final cluster
|
||||||
|
|
||||||
|
|
||||||
Once you a cluster is built the next step is to define which atom in the cluster
|
Once your cluster is built the next step is to define which atom in the cluster
|
||||||
will absorbe the light. This atom is called the *absorber*.
|
will absorb the light. This atom is called the *absorber* (or the *emitter* since
|
||||||
|
it emits the photoelectron).
|
||||||
|
|
||||||
To specify which atom is the absorber, you need to understand that the cluster
|
To specify which atom is the absorber, you need to understand that the cluster
|
||||||
object is like a list of atoms. Each member of this list is an atom with its
|
object is like a list of atoms. Each member of this list is an atom with its
|
||||||
|
@ -115,7 +129,7 @@ own position. You need to locate the index of the atom in the list that you want
|
||||||
it to be the absorber. Then, put that number in the *cluster.absorber* attribute
|
it to be the absorber. Then, put that number in the *cluster.absorber* attribute
|
||||||
|
|
||||||
For example, suppose that you want the first atom of the list to be the
|
For example, suppose that you want the first atom of the list to be the
|
||||||
absorber. You should write:
|
absorber. You would write:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -123,24 +137,27 @@ absorber. You should write:
|
||||||
|
|
||||||
To find what is the index of the atom you'd like to be the absorber, you can
|
To find what is the index of the atom you'd like to be the absorber, you can
|
||||||
either get it while you are visualizing the structure within the ase-gui
|
either get it while you are visualizing the structure within the ase-gui
|
||||||
program. Or, you can use :py:func:`get_atom_index` function. This function takes
|
program (select an atom with the left mouse button and look at its index in the
|
||||||
4 arguments: the cluster to look the index for, and the x, y and z coordinates.
|
status line). Or, you can use :py:func:`utils.get_atom_index` function. This function
|
||||||
|
takes 4 arguments: the cluster to look the index for, and the x, y and z coordinates.
|
||||||
It will return the index of the closest atom to these coordinates. In our
|
It will return the index of the closest atom to these coordinates. In our
|
||||||
example, to get the deepest atom centered in the xy-plane, we write:
|
example, as we used the :py:func:`utils.hemispherical_cluster` function to create our
|
||||||
|
cluster, the *emitter* (*absorber*) is always located at the origin, so defining it
|
||||||
|
is straightforward:
|
||||||
|
|
||||||
.. literalinclude:: Cu_simple.py
|
.. literalinclude:: Cu.py
|
||||||
:linenos:
|
:linenos:
|
||||||
:lineno-start: 22
|
:lineno-start: 15
|
||||||
:lines: 22-23
|
:lines: 15-16
|
||||||
|
|
||||||
|
|
||||||
That's all for the cluster part. We now need to create a calculator for that
|
That's all for the cluster part. We now need to create a calculator for that
|
||||||
object. This is a 2 lines procedure:
|
object. This is a 2 lines procedure:
|
||||||
|
|
||||||
.. literalinclude:: Cu_simple.py
|
.. literalinclude:: Cu.py
|
||||||
:linenos:
|
:linenos:
|
||||||
:lineno-start: 24
|
:lineno-start: 18
|
||||||
:lines: 24-28
|
:lines: 18-22
|
||||||
|
|
||||||
When creating a new calculator, you must choose the kind of spectroscopy you
|
When creating a new calculator, you must choose the kind of spectroscopy you
|
||||||
will work with. In this example we choose 'PED' for *PhotoElectron Diffraction*.
|
will work with. In this example we choose 'PED' for *PhotoElectron Diffraction*.
|
||||||
|
@ -153,14 +170,14 @@ Other types of spectroscopies are:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now that everything is ready you can actually perform a calculation. The 2 lines
|
Now that everything is ready you can actually perform a calculation. The lines
|
||||||
below will produce a polar scan of the Cu(2p3/2) level with default parameters,
|
below will produce a polar scan of the Cu(2p3/2) level with default parameters,
|
||||||
store the results in the data object and display it in a graphical window.
|
store the results in the data object and display it in a graphical window.
|
||||||
|
|
||||||
.. literalinclude:: Cu_simple.py
|
.. literalinclude:: Cu.py
|
||||||
:linenos:
|
:linenos:
|
||||||
:lineno-start: 29
|
:lineno-start: 24
|
||||||
:lines: 29-33
|
:lines: 24-28
|
||||||
|
|
||||||
running this script will produce the following output
|
running this script will produce the following output
|
||||||
|
|
||||||
|
@ -177,7 +194,6 @@ By default, the program computes for :math:`\theta` angles in the -70°..+70°
|
||||||
range. This can be changed by using the *angles* keyword.
|
range. This can be changed by using the *angles* keyword.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:linenos:
|
|
||||||
|
|
||||||
#For a polar scan between 0° and 60° with 100 points
|
#For a polar scan between 0° and 60° with 100 points
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -201,7 +217,6 @@ work function of the sample, arbitrary set to 4.5 eV.
|
||||||
Of course, you can choose any kinetic energy you'd like:
|
Of course, you can choose any kinetic energy you'd like:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:linenos:
|
|
||||||
|
|
||||||
# To set the kinetic energy...
|
# To set the kinetic energy...
|
||||||
data = calc.get_theta_scan(level = '2p3/2', kinetic_energy = 300)
|
data = calc.get_theta_scan(level = '2p3/2', kinetic_energy = 300)
|
||||||
|
@ -209,9 +224,9 @@ Of course, you can choose any kinetic energy you'd like:
|
||||||
|
|
||||||
|
|
||||||
Below is the full code of this example. You can download it :download:`here
|
Below is the full code of this example. You can download it :download:`here
|
||||||
<Cu_simple.py>`
|
<Cu.py>`
|
||||||
|
|
||||||
.. literalinclude:: Cu_simple.py
|
.. literalinclude:: Cu.py
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
.. _tutorials:
|
||||||
|
|
||||||
|
####################
|
||||||
|
Learn from tutorials
|
||||||
|
####################
|
||||||
|
|
||||||
|
...About Photodiffraction
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
copper/tuto_ped_copper
|
||||||
|
nickel_chain/tuto_ped_nickel_chain
|
||||||
|
RhO/tuto_ped_RhO
|
||||||
|
temperature/tuto_ped_temperature
|
||||||
|
AlN/tuto_cluster
|
|
@ -10,12 +10,12 @@ from ase import Atom, Atoms
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
symbol = 'Ni' # The kind of atom for the chain
|
symbol = 'Ni' # The kind of atom for the chain
|
||||||
orders = (1, 5) # We will run the calculation for single scattering
|
orders = (1, 5) # We will run the calculation for single scattering
|
||||||
# and multiple scattering (5th diffusion order)
|
# and multiple scattering (5th diffusion order)
|
||||||
chain_lengths = (2,3,5) # We will run the calculation for differnt lengths
|
chain_lengths = (2,3,5) # We will run the calculation for differnt lengths
|
||||||
# of the atomic chain
|
# of the atomic chain
|
||||||
a = 3.5 # The distance bewteen 2 atoms
|
a = 3.499 * np.sqrt(2)/2 # The distance bewteen 2 atoms
|
||||||
|
|
||||||
# Define an empty variable to store all the results
|
# Define an empty variable to store all the results
|
||||||
all_data = None
|
all_data = None
|
||||||
|
@ -28,11 +28,12 @@ for chain_length in chain_lengths:
|
||||||
chain = Atoms([Atom(symbol, position = (0., 0., i*a)) for i in
|
chain = Atoms([Atom(symbol, position = (0., 0., i*a)) for i in
|
||||||
range(chain_length)])
|
range(chain_length)])
|
||||||
# 2- rotating the chain by 45 degrees with respect to the y axis
|
# 2- rotating the chain by 45 degrees with respect to the y axis
|
||||||
chain.rotate('y', np.radians(45.))
|
#chain.rotate('y', np.radians(45.))
|
||||||
|
chain.rotate(45., 'y')
|
||||||
# 3- setting a custom Muffin-tin radius of 1.5 angstroms for all
|
# 3- setting a custom Muffin-tin radius of 1.5 angstroms for all
|
||||||
# atoms (needed if you want to enlarge the distance between
|
# atoms (needed if you want to enlarge the distance between
|
||||||
# the atoms while keeping the radius constant)
|
# the atoms while keeping the radius constant)
|
||||||
[atom.set('mt_radius', 1.5) for atom in chain]
|
#[atom.set('mt_radius', 1.5) for atom in chain]
|
||||||
# 4- defining the absorber to be the first atom in the chain at
|
# 4- defining the absorber to be the first atom in the chain at
|
||||||
# x = y = z = 0
|
# x = y = z = 0
|
||||||
chain.absorber = 0
|
chain.absorber = 0
|
||||||
|
@ -43,7 +44,7 @@ for chain_length in chain_lengths:
|
||||||
# Here is how to tweak the scattering order
|
# Here is how to tweak the scattering order
|
||||||
calc.calculation_parameters.scattering_order = order
|
calc.calculation_parameters.scattering_order = order
|
||||||
# This line below is where we actually run the calculation
|
# This line below is where we actually run the calculation
|
||||||
all_data = calc.get_theta_scan(level='3s', kinetic_energy=1000.,
|
all_data = calc.get_theta_scan(level='3s', #kinetic_energy=1000.,
|
||||||
theta=np.arange(0., 80.), data=all_data)
|
theta=np.arange(0., 80.), data=all_data)
|
||||||
|
|
||||||
# OPTIONAL, to improve the display of the data we will change the dataset
|
# OPTIONAL, to improve the display of the data we will change the dataset
|
||||||
|
|
|
@ -30,7 +30,7 @@ trajectory for this electron which in turn lowers the signal.
|
||||||
:align: center
|
:align: center
|
||||||
:width: 80%
|
:width: 80%
|
||||||
|
|
||||||
Figure 4. Polar scan of a Ni chain of 2-5 atoms for single and mutliple (5th order)
|
Figure 1. Polar scan of a Ni chain of 2-5 atoms for single and mutliple (5th order)
|
||||||
scattering.
|
scattering.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,153 +1,129 @@
|
||||||
# coding: utf-8
|
# coding: utf8
|
||||||
|
|
||||||
from msspec.calculator import MSSPEC, XRaySource
|
|
||||||
from msspec.utils import *
|
|
||||||
from msspec.iodata import Data
|
|
||||||
from ase.build import bulk
|
|
||||||
|
|
||||||
|
from ase.build import bulk
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sys
|
from msspec.calculator import MSSPEC, XRaySource
|
||||||
|
from msspec.utils import hemispherical_cluster, get_atom_index
|
||||||
a0 = 3.6 # The lattice parameter in angstroms
|
|
||||||
phi = np.arange(0, 100) # An array of phi angles
|
|
||||||
all_T = np.arange(300, 1000, 100) # All temperatures used for the calculation
|
|
||||||
all_theta = np.array([45, 83]) # 2 polar angles, 83° is grazing detection
|
|
||||||
eps = 0.01 # a useful small value
|
|
||||||
|
|
||||||
DATA_FNAME = 'all_data.hdf5' # Where to store all the data
|
|
||||||
ANALYSIS_FNAME = 'analysis.hdf5'
|
|
||||||
|
|
||||||
|
|
||||||
def compute(filename):
|
def create_clusters(nplanes=3):
|
||||||
"""Will compute a phi scan for an emitter in the first, second and third plane
|
copper = bulk('Cu', a=3.6, cubic=True)
|
||||||
of a copper substrate for various polar angles and temperatures.
|
clusters = []
|
||||||
In a second step (outside this function), intensities from all these emitters
|
for emitter_plane in range(nplanes):
|
||||||
are added to get the signal of a substrate."""
|
cluster = hemispherical_cluster(copper,
|
||||||
calc = MSSPEC(spectroscopy='PED')
|
emitter_plane=emitter_plane,
|
||||||
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
planes=emitter_plane+2,
|
||||||
calc.muffintin_parameters.interstitial_potential = 14.1
|
shape='cylindrical')
|
||||||
|
cluster.absorber = get_atom_index(cluster, 0, 0, 0)
|
||||||
|
cluster.info.update({
|
||||||
|
'emitter_plane': emitter_plane,
|
||||||
|
})
|
||||||
|
clusters.append(cluster)
|
||||||
|
return clusters
|
||||||
|
|
||||||
calc.calculation_parameters.vibrational_damping = 'averaged_tl'
|
def compute(clusters, all_theta=[45., 83.],
|
||||||
calc.calculation_parameters.use_debye_model = True
|
all_T=np.arange(300., 1000., 400.)):
|
||||||
calc.calculation_parameters.debye_temperature = 343
|
data = None
|
||||||
calc.calculation_parameters.vibration_scaling = 1.2
|
for ic, cluster in enumerate(clusters):
|
||||||
|
# Retrieve info from cluster object
|
||||||
|
plane = cluster.info['emitter_plane']
|
||||||
|
|
||||||
calc.detector_parameters.average_sampling = 'high'
|
calc = MSSPEC(spectroscopy='PED', algorithm='expansion')
|
||||||
calc.detector_parameters.angular_acceptance = 5.7
|
calc.source_parameters.energy = XRaySource.AL_KALPHA
|
||||||
|
calc.muffintin_parameters.interstitial_potential = 14.1
|
||||||
|
|
||||||
filters = ['forward_scattering', 'backward_scattering']
|
calc.calculation_parameters.vibrational_damping = 'averaged_tl'
|
||||||
calc.calculation_parameters.path_filtering = filters
|
calc.calculation_parameters.use_debye_model = True
|
||||||
|
calc.calculation_parameters.debye_temperature = 343
|
||||||
|
calc.calculation_parameters.vibration_scaling = 1.2
|
||||||
|
|
||||||
calc.calculation_parameters.RA_cutoff = 3
|
calc.detector_parameters.average_sampling = 'high'
|
||||||
|
calc.detector_parameters.angular_acceptance = 5.7
|
||||||
|
|
||||||
# You can also choose a real potential and manually define the
|
for atom in cluster:
|
||||||
# electron mean free path
|
atom.set('forward_angle', 30)
|
||||||
#
|
atom.set('backward_angle', 30)
|
||||||
# calc.tmatrix_parameters.exchange_correlation = 'hedin_lundqvist_real'
|
filters = ['forward_scattering', 'backward_scattering']
|
||||||
# calc.calculation_parameters.mean_free_path = 10.
|
calc.calculation_parameters.path_filtering = filters
|
||||||
|
|
||||||
data = None # init an empty data object
|
calc.calculation_parameters.RA_cutoff = 2
|
||||||
for temperature in all_T:
|
|
||||||
for plane in range(3):
|
|
||||||
# We create a cylindrical cluster here with one plane below the emitter
|
|
||||||
# and 0, 1 or to planes above the emitter
|
|
||||||
cluster = bulk('Cu', a=a0, cubic=True)
|
|
||||||
cluster = cluster.repeat((20, 20, 8))
|
|
||||||
center_cluster(cluster)
|
|
||||||
cluster = cut_cylinder(cluster, radius=1.5 * a0 + eps)
|
|
||||||
cluster = cut_plane(cluster, z=-( a0/2 + eps))
|
|
||||||
cluster = cut_plane(cluster, z=(plane) * a0 / 2 + eps)
|
|
||||||
cluster.absorber = get_atom_index(cluster, 0, 0, 0)
|
|
||||||
|
|
||||||
calc.calculation_parameters.temperature = temperature
|
for T in all_T:
|
||||||
# the scattering order depends on the number of planes above
|
for theta in all_theta:
|
||||||
# the emitter to speed up calculations
|
calc.calculation_parameters.temperature = T
|
||||||
calc.calculation_parameters.scattering_order = 1 + plane
|
calc.calculation_parameters.scattering_order = min(1 + plane, 3)
|
||||||
|
calc.set_atoms(cluster)
|
||||||
|
data = calc.get_phi_scan(level='2p3/2', theta=theta,
|
||||||
|
phi=np.linspace(0, 100),
|
||||||
|
kinetic_energy=560, data=data)
|
||||||
|
dset = data[-1]
|
||||||
|
dset.title = "plane #{:d}, T={:f}K, theta={:f}°".format(plane,
|
||||||
|
T,
|
||||||
|
theta)
|
||||||
|
|
||||||
calc.set_atoms(cluster)
|
dset.add_parameter(group='misc', name='plane', value=plane, unit='')
|
||||||
# Here starts the calculation
|
dset.add_parameter(group='misc', name='T', value=T, unit='')
|
||||||
data = calc.get_phi_scan(level='2p3/2', theta=all_theta, phi=phi,
|
dset.add_parameter(group='misc', name='theta', value=theta, unit='')
|
||||||
kinetic_energy=560, data=data)
|
return data
|
||||||
|
|
||||||
data.save(filename)
|
|
||||||
|
|
||||||
def process_data(datafile, outputfile):
|
def analysis(data):
|
||||||
"""Will create another Data file with some phi-scans corresponding to 3
|
all_plane = []
|
||||||
planes at different temperatures and the anisotropy curve for 45° and
|
all_T = []
|
||||||
grazing detection.
|
all_theta = []
|
||||||
"""
|
for dset in data:
|
||||||
def get_signal(datafile, T=300, theta=45):
|
plane = dset.get_parameter('misc', 'plane')['value']
|
||||||
data = Data.load(datafile)
|
T = dset.get_parameter('misc', 'T')['value']
|
||||||
total = None
|
theta = dset.get_parameter('misc', 'theta')['value']
|
||||||
|
cs = dset.cross_section.copy()
|
||||||
|
phi = dset.phi.copy()
|
||||||
|
|
||||||
|
if plane not in all_plane: all_plane.append(plane)
|
||||||
|
if T not in all_T: all_T.append(T)
|
||||||
|
if theta not in all_theta: all_theta.append(theta)
|
||||||
|
|
||||||
|
def get_anisotropy(theta, T):
|
||||||
|
cs = None
|
||||||
for dset in data:
|
for dset in data:
|
||||||
p = {_['group'] + '.' + _['name']: _['value'] for _ in dset.parameters()}
|
|
||||||
temperature = np.float(p['CalculationParameters.temperature'])
|
|
||||||
if temperature != T: continue
|
|
||||||
i = np.where(dset.plane == -1)
|
|
||||||
j = np.where(dset.theta[i] == theta)
|
|
||||||
signal = dset.cross_section[i][j]
|
|
||||||
try:
|
try:
|
||||||
total += signal
|
_T = dset.get_parameter('misc', 'T')['value']
|
||||||
|
_theta = dset.get_parameter('misc', 'theta')['value']
|
||||||
|
_cs = dset.cross_section.copy()
|
||||||
|
phi = dset.phi.copy()
|
||||||
except:
|
except:
|
||||||
total = signal
|
continue
|
||||||
phi = dset.phi[i][j]
|
if _theta == theta and _T == T:
|
||||||
return phi, total
|
try:
|
||||||
|
cs += _cs
|
||||||
|
except:
|
||||||
|
cs = _cs
|
||||||
|
Imax = np.max(cs)
|
||||||
|
Imin = np.min(cs)
|
||||||
|
return (Imax - Imin)/Imax
|
||||||
|
|
||||||
|
# create a substrate dataset for each T and theta
|
||||||
|
anisotropy_dset = data.add_dset("all")
|
||||||
|
anisotropy_view = anisotropy_dset.add_view('Anisotropies',
|
||||||
|
title='Relative anisotropies for Cu(2p)',
|
||||||
|
marker='o',
|
||||||
|
xlabel='T (K)',
|
||||||
|
ylabel=r'$\frac{\Delta I / I_{max}(T)}{\Delta I_{300}'
|
||||||
|
r'/ I_{max}(300)} (\%)$')
|
||||||
|
|
||||||
analysis = Data('Temperature tutorial')
|
|
||||||
scans_dset = analysis.add_dset('Phi scans')
|
|
||||||
scans_view = scans_dset.add_view('Figure 1',
|
|
||||||
title=r'Cu(2p) Azimuthal scan for $\theta = 83\degree$',
|
|
||||||
xlabel=r'$\Phi (\degree$)',
|
|
||||||
ylabel='Signal (a.u.)')
|
|
||||||
anisotropy_dset = analysis.add_dset('Anisotropies')
|
|
||||||
anisotropy_view = anisotropy_dset.add_view('Figure 2',
|
|
||||||
title='Relative anisotropies for Cu(2p)',
|
|
||||||
marker='o',
|
|
||||||
xlabel='T (K)',
|
|
||||||
ylabel=r'$\frac{\Delta I / I_{max}(T)}{\Delta I_{300} / I_{max}(300)} (\%)$')
|
|
||||||
for theta in all_theta:
|
for theta in all_theta:
|
||||||
for T in all_T:
|
for T in all_T:
|
||||||
PHI, SIGNAL = get_signal(datafile, T=T, theta=theta)
|
A = get_anisotropy(theta, T)
|
||||||
for phi, signal in zip(PHI, SIGNAL):
|
A0 = get_anisotropy(theta, np.min(all_T))
|
||||||
scans_dset.add_row(phi=phi, signal=signal, theta=theta, temperature=T)
|
|
||||||
|
|
||||||
middleT = all_T[np.abs(all_T - np.mean(all_T)).argmin()]
|
anisotropy_dset.add_row(temperature=T, theta=theta, anisotropy=A/A0)
|
||||||
if theta == 83 and T in [np.min(all_T), middleT, np.max(all_T)]:
|
|
||||||
scans_view.select('phi', 'signal',
|
|
||||||
where='temperature == {:f} and theta == {:f}'.format(T, theta),
|
|
||||||
legend='{:.0f} K'.format(T))
|
|
||||||
|
|
||||||
PHI, SIGNAL0 = get_signal(datafile, T=np.min(all_T), theta=theta)
|
|
||||||
Imax = np.max(SIGNAL)
|
|
||||||
Imax0 = np.max(SIGNAL0)
|
|
||||||
dI = Imax - np.min(SIGNAL)
|
|
||||||
dI0 = Imax0 - np.min(SIGNAL0)
|
|
||||||
ani = (dI / Imax) / (dI0 / Imax0)
|
|
||||||
anisotropy_dset.add_row(temperature=T, anisotropy=ani, theta=theta)
|
|
||||||
|
|
||||||
anisotropy_view.select('temperature', 'anisotropy',
|
anisotropy_view.select('temperature', 'anisotropy',
|
||||||
where='theta == {:f}'.format(theta),
|
where='theta == {:f}'.format(theta),
|
||||||
legend=r'$\theta = {:.0f} \degree$'.format(theta))
|
legend=r'$\theta = {:.0f} \degree$'.format(theta))
|
||||||
|
return data
|
||||||
analysis.save(outputfile)
|
|
||||||
|
|
||||||
|
|
||||||
# A convenient way to run the script, just specify one or more of these calc, analyse or
|
|
||||||
# view keywords as arguments
|
|
||||||
# ... to calculate all the data, analyse the data and view the results, run
|
|
||||||
# python Cu_temperature.py calc analyse view
|
|
||||||
# ... to just view the results, simply run
|
|
||||||
# python Cu_temperature.py view
|
|
||||||
|
|
||||||
if 'calc' in sys.argv:
|
|
||||||
compute(DATA_FNAME)
|
|
||||||
if 'analyse' in sys.argv:
|
|
||||||
process_data(DATA_FNAME, ANALYSIS_FNAME)
|
|
||||||
if 'view' in sys.argv:
|
|
||||||
data = Data.load(ANALYSIS_FNAME)
|
|
||||||
data.view()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
clusters = create_clusters()
|
||||||
|
data = compute(clusters)
|
||||||
|
data = analysis(data)
|
||||||
|
data.view()
|
||||||
|
|
Loading…
Reference in New Issue