Introducing pyemaps

pyemaps - Python Modules for Transmission Electron Diffraction Simulations

Introducing pyemaps

  1. Overview
  2. Requirements
  3. Installation
  4. Basic Usage
  5. Getting Started
  6. Visualisation
  7. Licence
  8. Frequently Asked Questions
  9. Release Notes

Overview

pyemaps package is a collection of python modules and libraries designed for transmission electron diffraction simulations and related crystallographic calculations. Main features include:

Kinematic and Dynamic Diffraction Simulations

Electron Powder Diffraction Simulations

Crystallographic Transformations and Calculations

Crystal Structure Factors Calculations

  • X-Ray Structure Factors
  • Electron Structure Factor in V (volts)
  • Electron Structure Factor in 1/Å^2
  • Electron Absorption Structure Factor in 1/Å^2

pyemaps comes with a set of helper classes with the intent of making accesses to above feature easy for users:

pyemaps is based on the proprietary Fortran applications released as backend of cloudEMAPS2.0.

Check pyemaps release notes for updates and releases. We welcome comments and suggestions from our user community. For reporting any issues and requesting pyemaps improvements, or sharing scripts using pyemaps, please go to our support page.

If you benefit from pyemaps in your microscopy and crystallography research and education and would like to donate, go to PayPal. Your generous donations are greatly appreciated and will keep us in the business of providing free software to the communities.

Requirements

Linux support planned in future releases, stay tuned.

Installation

(.venv) $ pip install pyemaps

where .venv is the python virtual environment

PYEMAPS_CRYSTALS environment variable is optional. But setting it to a directory where all custom crystal data files are located provides central location for organizing your own crystal data. pyemaps also searches this directory for your crystal data.

    PYEMAPS_CRYSTALS=<local directory>

See FAQ for solutions to possible installation issues.

Basic Usage

from pyemaps import Crystal
from pyemaps import DP

Getting Started

Run the following on command line, after above successful installation:

python sample.py

where sample.py is as follows:

    from pyemaps import Crystal as cr
    from pyemaps import showDif, showBloch
    from pyemaps import DPList
    from pyemaps import BImgList
    # create a crystal class instance and load it with builtin silicon data
    c_name = 'Silicon'
    si = cr.from_builtin(c_name)

    # generate diffraction on the crystal instance with all default controls
    # parameters, default controls returned as the first output ignored
    
    dpl = DPList(c_name)

    emc, si_dp = si.generateDP()
    dpl.add(emc, si_dp)    

    #plot and show the diffraction pattern using pyemaps built-in plot function
    showDif(dpl)

    #hide Kikuchi lines
    showDif(dpl, kshow=False) 

    #hide both Kukuchi line and Miller Indices
    showDif(dpl, kshow=False, ishow=False) 

    #hide Miller Indices
    showDif(dpl, ishow=False)

    #Generate dynamic diffraction patterns using pyemaps' bloch module
    bloch_imgs_list = BImgList(c_name)
    emc, img = si.generateBloch(sampling = 20) #with all default parameters
    
    #create a dynamic diffraction pattern list /w assiated controls

    bloch_imgs_list.add(emc, img) 
    
    showBloch(bloch_imgs_list) #grey color map
    showBloch(bloch_imgs_list, bColor=True) #with predefined color map

The alternative to run the above without creating sample.py:

python -m pyemaps --sample (-s)

The diffraction plot is generated with silicon crystal data built in the package:

crystal Silicon: dw = iso
cell 5.4307 5.4307 5.4307 90 90 90
atom si 0.125 0.125 0.125 0.4668 1.00
spg 227 2

and default electron microscope and sample control parameters:

zone axis: (0,0,1)
microscope mode: normal
microscope camera length : 1000 mm
microscope voltage: 200 V
sample tilt: (0.0,0.0)
sample offset: (0.0,0.0)
spot size: 0.05 Å

The following is the dynamic diffraction pattern for Silicon builtin crystal with sampling set at 20. The left is the image in gray scale and the righ in a predefined color map

To see all crystal names with builtin data, call:

from pyemaps import Crystal as cr
cr.list_all_builtin_crystals()

To use a crystal data not in built-in database in above format (as xtl format), replace the code in sample.py:

from pyemaps import Crystal as cr
si = cr.from_builtin('Silicon')

with:

from pyemaps import Crystal as cr
si = cr.from_xtl(fn)

CIF format has recently been added to crystal data sources where pyemaps can import:

from pyemaps import Crystal as cr
si = cr.from_cif(fn)

where fn is a crystal data file name. See release notes for details how pyemaps imports .cif data

Note: pyemaps searches for fn if the full path is provided. Otherwise, it will look up the file in current working directory or in the directory set by PYEMAPS_CRYSTALS environment variable. In latter cases, fn is the file name without path.

Checking pyemaps version and displaying copyright information:

python -m pyemaps -c (--copyright)
python -m pyemaps -v (--version)

Visualisation

Accessing diffraction patterns data is easy for pyemaps users to visualize the diffraction patterns in any programs other than pyemaps’ builtin plot with python’s matplotlib library:

Sample scripts designed for you to explore pyemaps features are available in pyemaps’ samples directory:

More samples code will be added as more features and releases are available.

To copy all of the samples from pyemaps package to the current working directory, following pyemaps installation. Run:

python -m pyemaps -cp (--copysamples)

all of the samples will be copied from pyemaps install directory to a folder named pyemaps_samples in your current working directory.

Licence

pyemaps is distributed for electron diffraction and microscopy research, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for more details.

Additional copyright notices and license terms applicable to portions of pyemaps are set forth in the COPYING file.

Contact supprort@emlabsoftware.com for any questions regarding the licence terms.

Frequently Asked Questions

A: pyemaps installation includes version requirements on pip, its setuptools and wheel that make downloading and installing the package correct and fast. These tools and their right versions may not come with your python installation and environment. Without them, pip tries to re-build pyemaps from source and will fail due to the fact that pyemaps contains extensions modules. Make sure you have the latest version of pip, setuptools, and wheel.

    pip install -U pip setuptools wheel 

before re-installing pyemaps again.

A: Follow the steps below to install and run pyemaps in Anaconda and Jupyter Notebooks

1) Install Anaconda: 
   You may need to make a clean install by doing a "Full Uninstall" of existing Anaconda by 
   deleting old environment and packages folders as old packages can cause dependencies issues.
2) (In some systems) Download Microsoft Visual Studio Build Tools needed for building required PyCifRW-4.4.3 from source
   Follow https://www.scivision.dev/python-windows-visual-c-14-required/ to download the latest (or any version >= 2019) 
   Restart after the installation is completed. 
   NOTE: This step is no longer needed starting from version 0.4.3
3) Create a new environment in Anaconsa called 'pyemaps' in Anaconda command prompt:
   conda create -n pyemaps python=3.7 
   Please note, 3.7 is the only supported python version for now, more version support upcoming.
4) Activate the new conda environment:
   conda activate pyemaps
5) Install latest pyemaps & its runtime distributables from Intel Fortran needed for pyemaps modules:
   pip install pyemaps (or pyemaps==X.X.X)
   pip install intel-fortran-rt (NOTE: This install is no longer needed starting from version 0.4.3)
6) Install Jupyter:
   pip install jupyter
7) Run "conda install ipywidgets" and "conda install widgetsnbextension"
   python -m ipykernel install --user --name pyemaps --display-name "pyemaps (python 3.7)" 
   note: replace --display-name value with your own string if desired
8) Run Jupyter local server:
   jupyter notebook
   Create a new notebook file to run pyemaps tasks.

Release Notes

0.3.3 Alpha May 4th, 2022

NEW

0.3.4 Alpha May 7th, 2022

NEW

IMPROVED

0.3.5 Alpha May 11th, 2022

NEW

    from pyemaps import EMC

this class mirror the following dictionary of controls parameters:

    DEF_CONTROLS = dict(zone = (0,0,1),
                    tilt = (0.0,0.0),
                    defl = (0.0,0.0),
                    cl = 1000,
                    vt = 200
                    )

IMPROVED

See new sample code in the package for details how to use the new function.

0.3.6 Alpha May 24th, 2022

NEW

The basic usage of CSF to generate and output CSF data is embedded in Crystal class method:

    generateCSF(kv = 100,               <= High Voltage
                smax = 0.5,             <= Limit for Scattering Angle θ / Wavelength 
                sftype = 1,             <= Structure Factor Types (1-4 as listed in Overview section)
                aptype = 0)             <= With Absorption or Not
    printCSF(SFS)                       <= SFS: Output Structure Factor Data from generateCSF() call           

An excerpt of output from the sample code si_csf.py run for electron absorption structure factors:

-----Electron Absorption Structure Factor in 1/Å^2-----
    crystal            : Silicon
    h k l              : Miller Index
    s-w                : Sin(ϴ)/Wavelength <= 1.0
    d-s                : D-Spacing
    high voltage       : 100 V

    SF output format   : (amplitude, phase)

h   k    l        s-w             d-s          amplitude         phase

1   1   1    0.1594684670    3.1354161069    0.0005996839    0.000000
0   0   2    0.1841383247    2.7153500000    1.655526e-35    180.000000
0   2   2    0.2604109162    1.9200423983    0.0007835663    0.000000
1   1   3    0.3053588663    1.6374176590    0.0005298455    0.000000
2   2   2    0.3189369340    1.5677080534    9.850372e-19    0.000000
0   0   4    0.3682766494    1.3576750000    0.0007046324    0.000000
...

See sample code si_csf.py for detailed guide on using these methods.

0.3.7 Alpha May 31st, 2022

NEW

    generatePowder(kv = 100,                <= High Voltage
                  t2max = 0.05,             <= Scattering Angle 2θ Limit
                  smax = 1.0,               <= Scattering Angle θ/ Wavelength Limit
                  eta = 1.0,                <= Mixing Coefficient Between Gaussian And Lorentzian
                  gamma = 0.001...)         <= Gamma Is the Fwhm
    plotPowder(PW)                          <= PW: Output powder Data from generatePowder()         

The first methods takes user input of high voltage and scattering angle 2ϴ, along with many others to generate the electron powder diffraction in intensity array. plotPowder plots single powder diffraction, while powder.py sample code included in the package also demonstates the electron pwoder diffarction of two crystals: Silicon and Diamond. The latter is with absorption. See sample code _powder.py__ for details on using these mothods.

0.3.8 Alpha June 15th, 2022

IMPROVED

0.3.9 Alpha June 28th, 2022

NEW

The function tries to extract crystal information for cell parameters, unit cells from defined field keys and does its best to match space group data from information provided with what is in pyemaps. We welcome your contributions to the parsing and compiling of CIF in pyemaps in order to improve this feature. More document on this feature along with others are forthcoming.

    from pyemaps import Crystal as cr
    si = cr.from_builtin('Silicon')
    
    vd = si.d2r()
    print(f'\nDefault real space to reciprocal space transform: \n{vd}')
    vd = si.r2d()
    print(f'\nDefault reciprocal space to real space transform: \n{vd}')

    # real to reciprocal transformation
    v = (1.0, 1.0, 2.0)
    v_recip = si.d2r(v) 
    print(f'\nReal space to reciprocal space transform for {v}:\n{v_recip}')
  
    
    #reciprocal to real transformation
    v_ = si.r2d(v_recip) # v_ ~= v
    print(f'\nReciprocal space to real space transform for {v_recip}:\n{v_}')

    #angle in real space
    v1 = (1.0, 1.0, 2.0)
    v2 = (1.0, 1.0, 1.0)
    real_a = si.angle(v1, v2)
    print(f'\nAngle in real space by vectors {v1} and {v2}: \n{real_a} \u00B0')

    #angle in reciprocal space
    recip_a = si.angle(v1, v2, type = 1)
    print(f'\nAngle in reciprocal space by vectors {v1} and {v2}: \n{recip_a} \u00B0')

    #vector length in real space
    r_vlen = si.vlen(v)
    print(f'\nLength in real space for vector {v}:\n{r_vlen} in \u212B')

    #vector length in reciprocal space
    recip_vlen = si.vlen(v, type = 1)
    print(f'\nLength in reciprocal space for vector {v}:\n{recip_vlen} in 1/\u212B')

    #wave length with high voltage of 200 V
    print(f'\nWave length with high voltage of 200 V:\n{si.wavelength(200)} \u212B')

IMPROVEMENTS

Examples of the above improvements shown as follows:

    from pyemaps import Crystal as cr
    from pyemaps import CrystalClassError, DPError
    try:
        cf = cr.from_cif(cif_fn)
        _, cf_dp = cf.generateDP()
    except (CrystalClassError, DPError) as v:   #<---- Notice the new error handling
        print(f'Loading {cif_fn} failed with message: {v}')
    except:
        print('Other unknown failures, exiting...')
        exit(1)
        
    cf_dp.plot()
    # print the diffraction pattern using the builtin format
    print(f'Diffraction Pattern:\n{cf_dp}\n\n')

    # get the raw diffraction pattern in python dictionary 
    # in case you want to import DP into your own program
    dp_dict = cf_dp.__dict__
    print(f'Raw diffraction pattern in python dictionary:\n{dp_dict}\n\n')
    
    # or the raw data of each components of kinematic diffraction pattern 
    # into your program and/or print them out
    print(f'# of Kikuchi lines: {cf_dp.nklines}\nKikuchi lines list:\n{cf_dp.klines}\n\n')
    print(f'# of diffracted beams (a.k.a Disks): {cf_dp.ndisks}\ndiffracted beams list:\n{cf_dp.disks}\n\n')
    print(f'# of HOLZ lines: {cf_dp.nhlines}\nHOLZ lines list:\n{cf_dp.hlines}')

See errors.py for all exception classes.

0.4.0 Alpha July 4th, 2022

IMPROVEMENTS

0.4.1 Alpha July 22th, 2022

NEW

IMPROVEMENTS

    showDif(dpl)   # dpl is a list of DP and their associated control parameters. 

    #hide Kikuchi lines
    showDif(dpl, kshow=False) 

    #hide both Kukuchi line and Miller Indices
    showDif(dpl, kshow=False, ishow=False) 

    #hide Miller Indices
    showDif(dpl, ishow=False)

    #save images as <crystal.name>.PNG in current directory
    showDif(dpl, bSave=True) # bSave default is False
    showBloch(dpl, bSave=True)

Detailed plotting function implementations are lised in display.py.

0.4.2 Alpha July 28th, 2022

IMPROVEMENTS

*Dynamic Diffraction Performance and Computation Accuracy Improvements: Switching to LAPACK libraries in eigen calculations has resulted in great improvements in matrix computations employed in Bloch module. An average of over 100% performance improvements. More performance improvements are planned with OpenMP implementations in compute intense Bloch module. Stay tuned…

*Bug Fixes: Fixed runtime error in display functions on some system.

0.4.3 Alpha Augusr 19th, 2022 (upcoming)

IMPROVEMENTS

*Installation Dependencies Removed: CIF reader support for python 3.7 is now added. As result, pyemaps installation no longer requires of MSVC build tool to build it from the source package and additional runtime installation requirement also removed.