Dans_Diffraction.functions_crystallography (version 3.8.2)
index
c:\users\grp66007\onedrive - diamond light source ltd\pythonprojects\dans_diffraction\dans_diffraction\functions_crystallography.py

Module: functions_crystallography.py
 
By Dan Porter, PhD
Diamond
2018
 
Usage: 
    - Run this file in an interactive console
    OR
    - from Dans_Diffraction import functions_crystallography as fc
 
Version 3.8.2
Last updated: 02/07/23
 
Version History:
09/07/15 0.1    Version History started.
30/10/17 1.0    Many updates
06/01/18 2.0    Renamed functions_crystallography.py
02/03/18 2.1    Removed call to tkinter
05/04/18 2.2    Added invert_sym
02/05/18 2.3    Added comparison of lower case elements and symmetry values
04/06/18 2.4    Added new checks to readcif, corrected atomic_properties for python3/numpy1.14
08/06/18 2.5    Corrected a few errors and added some more comments
06/03/19 2.6    Added print_atom_properties
14/08/19 2.7    Added new Dans Element Properties file with extra comment line, new functions added
03/04/20 2.8    Updated attenuation to work with arrays of elements
19/04/20 2.9    Added write_cif, made alterations to readcif for speed and readability, added spacegroup
01/05/20 3.0    Updated atom_properties, now have atomic properties above 92 with warning. Some changes to readcif.
05/05/20 3.0.1  Further changes to readcif. Changed method of symmetry_ops2magnetic. Added str2element
12/05/20 3.1.0  More readcif changes, added atomic_scattering_factor, element_charge_string, split_compound
26/05/20 3.1.1  Updated magnetic space groups, added magnetic positions (was only generators), added write_mcif
09/06/20 3.2    Updated gen_sym_mat, symmetry_ops2magnetic, added sym_op_det
03/09/20 3.2.1  Updated cif_symmetry to allow for missing magnetic centring
21/01/21 3.3    Added xray_scattering_factor_resonant and xray_dispersion_corrections functions added
26/01/21 3.4    Added xray attenuation, transmission and refractive index
31/03/21 3.5    Added point groups, gen_sym_unique
10/06/21 3.6    Corrected mistake in DebyeWaller function. Added x-ray scattering factors from Waasmaier and Kirfel
15/11/21 3.7    Added diffractometer orientation commands from Busing & Levy, H. You
12/01/22 3.7.1  Added gen_sym_axial_vector
23/04/22 3.7.2  Corrected magnitude of Q in magnetic_structure_factors
07/05/23 3.8.0  Added electron_scattering_factors and electron wavelength formula
22/05/23 3.8.1  Added wyckoff_label, find_spacegroup
02/07/23 3.8.2  Added wavelength options to several functions, plut DeBroglie wavelength function
 
Acknoledgements:
    April 2020  Thanks to ChunHai Wang for helpful suggestions in readcif!
    May 2023    Thanks to Carmelo Prestipino for adding electron scattering factors
 
@author: DGPorter

 
Modules
       
Dans_Diffraction.functions_general
json
numpy
os
re
sys

 
Functions
       
Bmatrix(UV)
Calculate the Busing and Levy B matrix from a real space UV
"choose the x-axis parallel to a*, the y-axis in the plane of a* and b*, and the z-axis perpendicular to that plane"
From: W. R. Busing and H. A. Levy, Acta Cryst. (1967). 22, 457-464
"Angle calculations for 3- and 4-circle X-ray and neutron diffractometers"
See also: https://docs.mantidproject.org/nightly/concepts/Lattice.html
Q2hkl(qvec, UVstar)
Index vectors in an orthonal basis with a reciprocal basis
:param qvec: [nx3] array of coordinates in an orthogonal basis in A-1
:param UV: [3x3] array of unit cell vectors [[a1,a2,a3],[b1,b2,b3],[c1,c2,c3]]
:return: [nx3] array of vectors in reciprocal lattice units
RcSp(UV)
Generate reciprocal cell from real space unit vecors
Usage:
UVs = RcSp(UV)
  UV = [[3x3]] matrix of vectors [a,b,c]
UV2latpar(UV)
Convert UV=[a,b,c] to a,b,c,alpha,beta,gamma
 a,b,c,alpha,beta,gamma = UV2latpar(UV)
arrange_atom_order(list_of_elements)
Arrange a list of elements in the correct chemical order
  ['Li','Co','O'] = arrange_atom_order(['Co', 'O', 'Li'])
:param list_of_elements: [list of str]
:return: [list of str]
atom_properties(elements=None, fields=None)
Loads the atomic properties of a particular atom from a database
Atomic properties, scattering lengths, form factors and absorption edges for elements upto and including Uranium.
Values are taken from various online sources, see data/README.md for more details.
 
Usage:
        A = atom_properties() >> returns structured array of all properties for all atoms A[0]['Element']='H'
        A = atom_properties('Co') >> returns structured array of all properties for 1 element
        B = atom_properties('Co','Weight') >> returns regular 1x1 array
        B = atom_properties(['Co','O'],'Weight') >> retruns regular 2x1 array
        A = atom_properties('Co',['a1','b1','a2','b2','a3','b3','a4','b4','c']) >> returns structured array of requested properties
        A = atom_properties(['Co','O'],['Z','Weight']) >> returns 2x2 structured array
 
Available properties:
    A = atom_properties()
    print(A.dtype.names)
    print(a[25]['Element'])
    >> 'Fe'
 
Available information includes:
      Z             = Element number
      Element       = Element symbol
      Name          = Element name
      Group         = Element Group on periodic table
      Period        = Element period on periodic table
      Block         = Element electronic block (s,p,d,f)
      ValenceE      = Number of valence electrons
      Config        = Element electronic orbital configuration
      Radii         = Atomic Radii (pm)
      Weight        = Standard atomic weight (g)
      Coh_b         = Bound coherent neutron scattering length
      Inc_b         = Bound incoherent neutron scattering length
      Nabs          = Neutron absorption coefficient
      Nscat         = Neutron scattering coefficient
      a1            = Electron form factor
      b1            = Electron form factor
      a2            = Electron form factor
      b2            = Electron form factor
      a3            = Electron form factor
      b3            = Electron form factor
      a4            = Electron form factor
      b4            = Electron form factor
      c             = Electron form factor
      j0_A          = Magnetic Form factor
      j0_a          = Magnetic Form factor
      j0_B          = Magnetic Form factor
      j0_b          = Magnetic Form factor
      j0_C          = Magnetic Form factor
      j0_c          = Magnetic Form factor
      j0_D          = Magnetic Form factor
      K             = x-ray absorption edge
      L1            = x-ray absorption edge
      L2            = x-ray absorption edge
      L3            = x-ray absorption edge
      M1...         = x-ray absorption edge
atomic_scattering_factor(element, energy_kev=None)
Read atomic scattering factor table, giving f1+f2 for different energies
From: http://henke.lbl.gov/optical_constants/asf.html
:param element: str or list of str, name of element. If element string includes a number, this will multiply values
:param energy_kev: float or list energy in keV (None to return original, uninterpolated list)
:return: f1, f2, shape dependent on shapes of element and energy_kev:  float, or [ene] or [ele, ene]
attenuation(element_z, energy_keV)
Returns the x-ray mass attenuation, u/p, in cm^2/g
  e.g. A = attenuation(23,np.arange(7,8,0.01)) # Cu
       A = attenuation([23,24,25], 5.6)
       a = attenuation(19,4.5) # K
balance_atom_charge(list_of_elements, occupancy=None)
Determine the default charges and assign remaining charge to
unspecified elements
:param list_of_elements:
:return: [list of charges]
biso2uiso(biso)
Convert B isotropic thermal parameters to U isotropic thermal parameters
:param biso: Biso value or array
:return: Uiso value or array
cal2theta(qmag, energy_kev=17.794, wavelength_a=None)
Calculate theta at particular energy in keV from |Q|
 twotheta = cal2theta(Qmag,energy_kev=17.794)
calc_vol(UV)
Calculate volume in Angstrom^3 from unit vectors
caldspace(twotheta, energy_kev=17.794, wavelength_a=None)
Calculate d-spacing from two-theta
 dspace = caldspace(tth, energy_kev)
callattice(twotheta, energy_kev=17.794, hkl=(1, 0, 0))
Calculate cubic lattice parameter, a from reflection two-theta
:param twotheta: Bragg angle, deg
:param energy_kev: energy in keV
:param hkl: reflection (cubic only
:return: float, lattice contant
calqmag(twotheta, energy_kev=17.794, wavelength_a=None)
Calculate |Q| at a particular 2-theta (deg) for energy in keV
 magQ = calqmag(twotheta, energy_kev=17.794)
cif2dict(cifvals)
From a dict of key:value pairs generated from a *.cif file (using readcif),
read standard crystallographic infromation into a crystal dictionary, with keys:
    'filename' - cif filename
    'name' - name of structure
    'unit vector'   - [3x3] array of basis vectors [a,b,c]
    'parent cell'   - [3x3] array of parent basis vectors [a,b,c]
    'symmetry'      - list of string symmetry operations
    'space group'   - Space Group string
    'space group number' - Space group number
    'cif'           - cif dict
    The following are specific to atomis within the unit cell:
    'atom type'     - list of elements e.g. ['Co','O', 'O']
    'atom label'    - list of atom site names e.g. ['Co1, 'O1', 'O2']
    'atom position' - [nx3] array of atomic positions
    'atom occupancy'- [nx1] array of atom site occupancies
    'atom uiso'     - [nx1] array of atom site isotropic thermal parameters
    'atom uaniso'   - [nx6] array of atom site anisotropic thermal parameters
    'mag moment'    - [nx3] array of atom site magnetic moment vectors
    'mag time'      - [nx3] array of atom site magnetic time symmetry
    'misc'] = [element, label, cif_pos, occ, Uiso]
:param cifvals: dict from readcif
:return: dict
cif2table(cif)
Generate Latex table of atomic positions from cif dictionary
:param cif: cif dict from readcif
:return: str
cif_check(cifvals, required_keys=None, bad_value='?')
Returns True if all basic required cif parameter are available and real.
E.G.:
    cifvals = readcif(file.cif)
    if cif_check(cifvals):
        print('File OK')
:param cifvals: dict of cif keys form readcif
:param required_keys: list of key strings, or None for default
:param bad_value: if this value is in the cif key item, return False
:return: bool
cif_symmetry(cifvals)
Read symmetries from a cif dict
:param cifvals:
:return: list(symmetry_operations), list(magnetic_operations), list(time operations)
count_atoms(list_of_elements, occupancy=None, divideby=1, latex=False)
Count atoms in a list of elements, returning condenced list of elements
:param list_of_elements: list of element symbols
:param occupancy: list of occupancies of each element (default=None)
:param divideby: divide each count by this (default=1)
:param latex: False*/True
:return: [list of str]
count_charges(list_of_elements, occupancy=None, divideby=1, latex=False)
Count atoms in a list of elements, returning condenced list of elements with charges
:param list_of_elements: list of element symbols
:param occupancy: list of occupancies of each element (default=None)
:param divideby: divide each count by this (default=1)
:param latex: False*/True
:return: [list of str]
cut2powder(qx, qy, qz, cut)
Convert 2D reciprocal space cut to powder pattern
:param qx: [n,m] array of Q coordinates in x
:param qy: [n,m] array of Q coordinates in y
:param qz: [n,m] array or float of Q coordinates in z
:param cut: [n,m] array of intensities
:return: qm[o], pow[o]
debroglie_energy(wavelength_a, mass_kg)
Calcualte the energy in electronvolts using DeBroglie's formula
  E [keV] = h^2 / (2 * e * mass [kg] * A^2 * lambda^2 [A] * 1e3)
:param wavelength_a: wavelength in Angstroms
:param mass_kg: mass in kg
:return: energy in keV
debroglie_wavelength(energy_kev, mass_kg)
Calcualte the wavelength in Angstroms using DeBroglie's formula
  lambda [A] = h  / sqrt( 2 * mass [kg] * E [keV] * 1e3 * e )
:param energy_kev: energy in keV
:param mass_kg: mass in kg
:return: wavelength in Anstroms
debyewaller(uiso, Qmag=0)
Calculate the debye waller factor for a particular Q
 T = debyewaller(uiso,Qmag=[0])
 
    T = exp( -2*pi^2*Uiso/d^2 )
    T = exp( -Uiso/2 * Q^2 )
default_atom_charge(element)
Returns the default charge value for the element
:param element: str: 'Fe'
:return: int or nan
detector_angle_size(detector_distance_mm=565, height_mm=67, width_mm=34, pixel_size_um=172)
Return the angular spread of the detector
  delta_width, gamma_width, pixel_width = detector_angle_size(distance, height, widht, pixel)
:param detector_distance_mm: Detector distance from sample in mm
:param height_mm: Detector size in the vertical direction, in mm
:param width_mm: Detector size in the horizontal direction, in mm
:param pixel_size_um: Pixel size in microns
:return x_angle: total angular spread in the diffractometer x-axis, in degrees
:return z_angle: total angular spread in the diffractometer z-axis, in degrees
:return pixel_angle: angular spread per pixel, in degrees
detector_coverage(wavelength_a=1.5, resolution=0.1, delta=0, gamma=0, detector_distance_mm=565, height_mm=67, width_mm=34)
Determine volume of reciprocal space covered by the detector based on angular spread
:param wavelength_a: Incicent beam wavelength, in Angstroms
:param resolution: Incident beam resolution, in inverse-Angstroms
:param delta: float or array, detector vertical rotation
:param gamma: float or array, detector horizontal rotation
:param detector_distance_mm: Detector distance from sample in mm
:param height_mm: Detector size in the vertical direction, in mm
:param width_mm: Detector size in the horizontal direction, in mm
:return: volume in inverse-Angstroms
detector_rotate(detector_position_mm=(0, 1000.0, 0), delta=0.0, gamma=0.0, labmatrix=array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Return a position array for a vector rotated by delta and gamma, like a detector along [0,1,0]
:param detector_position_mm: detector position from sample in mm
:param delta: vertical rotation about z-axis in Deg
:param gamma: horizontal rotation about x-axis in Deg
:param labmatrix: [3*3] orientation matrix to convert to alternative basis
:return: array
detector_volume(wavelength_a=1.5, resolution=0.1, phi=0, chi=0, eta=0, mu=0, delta=0, gamma=0, detector_distance_mm=565, height_mm=67, width_mm=34, lab=array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Determine volume of reciprocal space covered by the detector, return list of voxels covered by detector
  total_volume, hkl = detector_volume(1.5, 0.1, chi=90, eta=30, delta=60)
The returned list of coordinates (hkl) is defined in the sample frame, where each coordinate specifies a voxel
in reciprocal space with size resolution(inverse-Anstroms) ** 3
As such, the total volume of reciprocal space measured is:
    total_volume = len(hkl) * fc.wavevector(energy_kev=energy_range_ev / 1000.) ** 3
 
:param wavelength_a: Incicent beam wavelength, in Angstroms
:param resolution: Incident beam resolution, in inverse-Angstroms (determines segmentation of calculation)
:param phi: float, phi-axis rotation about sample-z axis
:param chi: float, chi-axis rotation about sample-y' axis
:param eta: float, eta-axis rotation about sample-z'' axis
:param mu: float, mu-axis rotation about sample-x''' axis
:param delta: float, detector rotation vertical rotation
:param gamma: float, detector rotation horizontal rotation
:param detector_distance_mm: Detector distance from sample in mm
:param height_mm: Detector size in the vertical direction, in mm
:param width_mm: Detector size in the horizontal direction, in mm
:param lab: [3x3] Transformation matrix to change the lab coordinate system
:return total_volume: float, total volume of reciprocal space in inverse Angstroms
:return hkl: [nx3] array of voxels covered by detector, integer coordinates in the sample frame
detector_volume_scan(wavelength_a=1.5, resolution=0.1, phi=0, chi=0, eta=0, mu=0, delta=0, gamma=0, detector_distance_mm=565, height_mm=67, width_mm=34, lab=array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Return total reciprocal space volume covered by scanning an axis with a detector
  total, hkl, overlaps = detector_volume_scan(1.5, 0.1, phi=np.arange(-180,180))
Rotation axes can be specified as either floats or arrays, where all arrays must have the same length.
 
:param wavelength_a: Incicent beam wavelength, in Angstroms
:param resolution: Incident beam resolution, in inverse-Angstroms (determines segmentation of calculation)
:param phi: float or array, phi-axis about sample-z axis
:param chi: float or array, chi-axis about sample-y' axis
:param eta: float or array, eta-axis about sample-z'' axis
:param mu: float or array, mu-axis about sample-x''' axis
:param delta: float or array, detector vertical rotation
:param gamma: float or array, detector horizontal rotation
:param detector_distance_mm: Detector distance from sample in mm
:param height_mm: Detector size in the vertical direction, in mm
:param width_mm: Detector size in the horizontal direction, in mm
:param lab: [3x3] Transformation matrix to change the lab coordinate system
:return total: float, total volume covered, in cubic inverse Angstroms
:return hkl: [nx3] array of voxels covered by detector, integer coordinates in the sample frame
:return overlaps: [n] array of the number of times each voxel has been repeatedly measured (0 = only measurred once)
diff2lab(vec, lab=None)
Convert between diffractometer frame and lab frame
Lab frame according to Diamond I16 beamline
Diffractometer frame according to Fig. 1, H. You, J. Appl. Cryst 32 (1999), 614-623
  z-axis : axis parallel to the phi rotation axis when all angles 0 (towards wall (+x) in lab frame)
  x-axis : vector normal to phi axis where phi=0 (toward ceiling (+y) in lab frame)
  y-axis : vector normal to x,z axes (parallel to beam (+z) in lab frame)
:param vec: [3*n] array of vectors
:param lab: [3*3] transformation matrix, None=((0,1,0),(0,0,1),(1,0,0))
:return: [3*n] array of vectors
diff6circle2hkl(ub, phi=0, chi=0, eta=0, mu=0, delta=0, gamma=0, energy_kev=None, wavelength=1.0, lab=None)
Return [h,k,l] position of diffractometer axes with given UB and energy
:param ub: [3*3] array UB orientation matrix following Busing & Levy
:param phi: float sample angle in degrees
:param chi: float sample angle in degrees
:param eta: float sample angle in degrees
:param mu: float sample angle in degrees
:param delta: float detector angle in degrees
:param gamma: float detector angle in degrees
:param energy_kev: float energy in KeV
:param wavelength: float wavelength in A
:param lab: [3*3] lab transformation matrix
:return: [h,k,l]
diff6circlek(delta, gamma, energy_kev=None, wavelength=1.0, lab=None)
Calcualte incident and final wavevectors in diffractometer axis using detector angles
:param delta: float angle in degrees in vertical direction (about diff-z)
:param gamma: float angle in degrees in horizontal direction (about diff-x)
:param energy_kev: float energy in KeV
:param wavelength: float wavelength in A
:param lab: [3*3] lab transformation matrix
:return: [1*3], [1*3] : ki, kf
diff6circleq(delta, gamma, energy_kev=None, wavelength=1.0, lab=None)
Calcualte wavevector in diffractometer axis using detector angles
:param delta: float angle in degrees in vertical direction (about diff-z)
:param gamma: float angle in degrees in horizontal direction (about diff-x)
:param energy_kev: float energy in KeV
:param wavelength: float wavelength in A
:param lab: [3*3] lab transformation matrix
:return: [1*3]
diffractometer_Q(eta, delta, energy_kev=8.0)
Calculate wavevector transfer, Q for diffractometer within the scattering plane.
   Qx,Qy = diffractometer_Q(eta,delta,energy_kev)
   eta = angle (deg) of sample
   delta = angle (deg) of detector
   energy_kev = energy in keV
 
   Coordinate system (right handed):
    x - direction along beam, +ve is from source to sample
    y - direction verticle
    z - direction horizontal
 
   Note: Currently only in the verticle geometry!
diffractometer_rotation(phi=0, chi=0, eta=0, mu=0)
Generate the 6-axis diffracometer rotation matrix
  R = M * E * X * P
Also called Z in H. You, J. Appl. Cryst 32 (1999), 614-623
The diffractometer coordinate system has the convention (all angles zero):
    x-axis points vertically, perpendicular to beam (mu is about x)
    y-axis points along the direction of the beam
    z-axis points along the phi axis, perpendicular to x and y
The vertical scattering plane is in the y-x axis
The horizontal scattering plane is in the y-z axis
   vec' = np.dot(diffractometer_rotation(phi, chi, eta, mu), vec)
   vec must be 1D or column vector (3*n)
:param phi: float angle in degrees, left handed roation about z'''
:param chi: float angle in degrees, right handed rotation about y''
:param eta: float angle in degrees, left handed rotation about z'
:param mu: float angle in degrees, right handed rotation about x
:return:  [3*3] array
diffractometer_step(wavelength_a=1.5, resolution=0.1, phi=0, chi=0, eta=0, mu=0, delta=0, gamma=0, lab=array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Determine the euler angle steps required to scan through reciprocal space at the required resolutuion
    phi_step, chi_step, eta_step, mu_step = diffractometer_step(1.5, 0.1, chi=90, eta=20, delta=40)
The returned step size will provide a rotation that moves the wavevector transfer by approximately the resolutuion.
Any steps returning 0 are unsensitive to the rotation in this position
:param wavelength_a: Incicent beam wavelength, in Angstroms
:param resolution: float, combined resolution required, in inverse Angstroms
:param phi: float, phi-axis about sample-z axis
:param chi: float, chi-axis about sample-y' axis
:param eta: float, eta-axis about sample-z'' axis
:param mu: float, mu-axis about sample-x''' axis
:param delta: float, detector vertical rotation
:param gamma: float, detector horizontal rotation
:param lab: [3x3] Transformation matrix to change the lab coordinate system
:returns: phi_step, chi_step, eta_step, mu_step
dspace2q(dspace)
Calculate d-spacing from |Q|
     Qmag = q2dspace(dspace)
electron_energy(wavelength_a)
Calcualte the electron energy in electronvolts using DeBroglie's formula
  E [eV] ~ 1.5 / lambda^2 [nm]
:param wavelength_a: electron wavelength in Angstroms
:return: energy in eV
electron_scattering_factor(element, Qmag=0)
Read X-ray scattering factor table, calculate f(|Q|)
Uses the coefficients for analytical approximation to the scattering factors 
  Peng, L. M.; Acta Crystallogr A  1996, 52 (2), 257–276. 
  Peng, L.-M.  Acta Cryst A 1998, 54 (4), 481–485. 
Qff = xray_scattering_factor(element, Qmag=[0])
:param element: [n*str] list or array of elements
:param Qmag: [m] array of wavevector distance, in A^-1
:return: [m*n] array of scattering factors
electron_wavelength(energy_ev)
Calcualte the electron wavelength in Angstroms using DeBroglie's formula
  lambda [nm] ~ sqrt( 1.5 / E [eV] )
:param energy_ev: electron energy in eV
:return: wavelength in Anstroms
element_charge_string(symbol, occupancy=1.0, charge=0.0, latex=False)
Return formatted string of element with occupancy and charge
:param symbol: str - element string
:param occupancy: float - element occupancy or number of elements
:param charge: float - element charge
:param latex: if True, returns string formatted with latex tags
:return: str
element_name(element=None)
Returns the element name
:param element: str
:return: int
element_symbol(element_Z=None)
Returns the element sympol for element_Z
:param element_z: int or array or None for all elements
:return: str
element_z(element)
Returns the element number Z
:param element: str
:return: int
energy2wave(energy_kev)
Converts energy in keV to wavelength in A
 wavelength_a = energy2wave(energy_kev)
 lambda [A] = h*c/E = 12.3984 / E [keV]
euler_moment(mxmymz, uv)
Convert moment mxmymz coordinates from cif into eulerian basis
:param mxmymz: [nx3] array, units of Bohr magneton, directed along a,b,c
:param uv: [3x3] array, basis vectors [a,b,c]
:return: moments [nx3] array, units of Bohr magneton, directed along x,y,z
euler_unit_vector(uvw, uv)
Convert vector in a specific basis to a cartesian basis and normalise to a unit vector
:param uvw: [nx3] array as [[u,v,w]]
:param uv: [3x3], basis vectors [a,b,c]
:return: [nx3] array xyz/|xyz|, where x,y,z = u*a+v*b+w*c
filter_transmission(chemical_formula, energy_kev, density, thickness_um=100)
Calculate transmission of x-ray through a slab of material
Equivalent to https://henke.lbl.gov/optical_constants/filter2.html
Based on formulas from: Henke, Gullikson, and Davis, Atomic Data and Nuclear Data Tables 54 no.2, 181-342 (July 1993)
:param chemical_formula: str molecular formula
:param energy_kev: float or array, x-ray energy in keV
:param density: float density in g/cm^3
:param thickness_um: slab thickness in microns
:return: float or array
find_spacegroup(sg_symbol)
Find a spacegroup based on the identifying symbol
:param sg_symbol: str, e.g. 'Fd-3m'
:return: spacegroup dict or None if not found
fitincell(uvw)
Set all fractional coodinates between 0 and 1
Usage:
  uvw = fitincell(uvw)
  uvw = [[nx3]] array of fractional vectors [u,v,w]
genHKL(H, K=None, L=None)
Generate HKL array with all combinations within range specified
Usage:
  HKL = genHKL(max)
   int max = generates each h,k,l from -max to max
  HKL = genHKL([min,max])
   int min, int max = generates h,l,l from min to max
  HKL = genHKL([hmin,hmax],[kmin,kmax],[lmin,lmax])
   hmin,hmax = min and max values for h, k and l respectivley
 
  array HKL = [[nx3]] array of [h,k,l] vectors
 
E.G.
  HKL = genHKL(-3)
E.G.
  HKL = genHKL([-1,1])
E.G.
  HKL = genHKL([-2,2],[0,3],[1,1])
gen_lattice_parameters(lattice_parameters=(), *args, **kwargs)
Generate list of lattice parameters:
 a,b,c,alpha,beta,gamma = gen_lattice_parameters(args)
args:
  1 -> a=b=c=1,alpha=beta=gamma=90
  [1,2,3] -> a=1,b=2,c=3,alpha=beta=gamma=90
  [1,2,3,120] -> a=1,b=2,c=3,alpha=beta=90,gamma=120
  [1,2,3,10,20,30] -> a=1,b=2,c=3,alpha=10,beta=20,gamma=30
  1,2,3,10,20,30 -> a=1,b=2,c=3,alpha=10,beta=20,gamma=30
  a=1,b=2,c=3,alpha=10,beta=20,gamma=30 -> a=1,b=2,c=3,alpha=10,beta=20,gamma=30
:param lattice_parameters: float or list
:param args: floats
:param kwargs: lattice parameters
:return: a,b,c,alpha,beta,gamma
gen_sym_axial_vector(sym_ops, x, y, z)
 Transform axial vector by symmetry operations
Usage:
  uvw = gen_symcen_pos(sym_ops,cen_ops,x,y,z)
  sym_ops = [n*'x,y,z'] array of string symmetry operations
  cen_ops = [m*'x,y,z'] array of string centring operations
  x,y,z = fractional coordinates of atomic posiiton to be modified by symmetry
  uvw = [[nx3]] array of symmetry defined factional coordinates [u,v,w]
 
E.G.
  uvw = gen_symcen_pos(['x,y,z','y,-x,z+1/2'],['x,y,z','x+1/3,y+1/3,z'],0.1,0.2,0.3)
  uvw >> [[0.1,0.2,0.3] , [0.433,0.533,0.3] , [0.2,-0.1,0.8] , [0.533,-0.233,0.8]]
:param sym_ops:
:param x:
:param y:
:param z:
:return:
gen_sym_mat(sym_ops)
Generate transformation matrix from symmetry operation
Currently very ugly but it seems to work
Tested in Test/test_gen_sym_mat - found to be fast and reliable.
sym_mat = gen_sym_mat(['x,y,z','y,-x,z+1/2'])
sym_mat[0] = [[ 1.,  0.,  0.,  0.],
              [ 0.,  1.,  0.,  0.],
              [ 0.,  0.,  1.,  0.],
              [ 0.,  0.,  0.,  1.]])
sym_mat[1] = [[ 0. ,  1. ,  0. ,  0. ],
              [-1. ,  0. ,  0. ,  0. ],
              [ 0. ,  0. ,  1. ,  0.5],
              [ 0.,   0.,   0.,   1.]]
gen_sym_pos(sym_ops, x, y, z)
Generate positions from symmetry operations
Usage:
  uvw = gen_sym_pos(sym_ops,x,y,z)
  sym_ops = [n*'x,y,z'] array of string symmetry operations
  x,y,z = fractional coordinates of atomic posiiton to be modified by symmetry
  uvw = [[nx3]] array of symmetry defined factional coordinates [u,v,w]
 
E.G.
  uvw = gen_sym_pos(['x,y,z','y,-x,z+1/2'],0.1,0.2,0.3)
  uvw >> [[0.1,0.2,0.3] , [0.2,-0.1,0.8]]
gen_sym_ref(sym_ops, hkl)
Generate symmetric reflections given symmetry operations
    symhkl = gen_sym_ref(sym_ops,h,k,l)
gen_sym_unique(sym_ops, x, y, z, cen_ops=None)
Generate positions from symmetry operations with idential positions removed
Usage:
  uvw = gen_sym_unique(sym_ops,x,y,z)
E.G.
  uvw = gen_sym_unique(['x,y,z','y,-x,z+1/2','x,y,z'],0.1,0.2,0.3)
  uvw >> [[0.1,0.2,0.3] , [0.2,-0.1,0.8]]
:param sym_ops: list of str - symmetry operations
:param x: float
:param y: float
:param z: float
:param cen_ops: Optional - list of str for centring operations
:return: array of positions
gen_symcen_ops(sym_ops, cen_ops)
Build complete list of symmetry operations from symmetry and centring vectors
Usage:
  ops = gen_symcen_ops(sym_ops,cen_ops)
  sym_ops = [n*'x,y,z'] array of string symmetry operations
  cen_ops = [m*'x,y,z'] array of string centring operations
  ops = [n*m*'x,y,z'] array of string symmetry operations
 
E.G.
  ops = gen_symcen_pos(['x,y,z','y,-x,z+1/2'],['x,y,z','x+1/3,y+1/3,z'])
  >> ops = ['x,y,z','x+1/3,y+1/3,z','y,-x,z+1/2','y+1/3,-x+1/3,z+1/2']
gen_symcen_pos(sym_ops, cen_ops, x, y, z)
Generate positions from symmetry and centring operations
Usage:
  uvw = gen_symcen_pos(sym_ops,cen_ops,x,y,z)
  sym_ops = [n*'x,y,z'] array of string symmetry operations
  cen_ops = [m*'x,y,z'] array of string centring operations
  x,y,z = fractional coordinates of atomic posiiton to be modified by symmetry
  uvw = [[nx3]] array of symmetry defined factional coordinates [u,v,w]
 
E.G.
  uvw = gen_symcen_pos(['x,y,z','y,-x,z+1/2'],['x,y,z','x+1/3,y+1/3,z'],0.1,0.2,0.3)
  uvw >> [[0.1,0.2,0.3] , [0.433,0.533,0.3] , [0.2,-0.1,0.8] , [0.533,-0.233,0.8]]
getenergy()
group_intensities(q_values, intensity, min_overlap=0.01)
Group reflections within the overlap, returning the index max intensity of each group
:param q_values: [1*n] array of floats, position parameter of each reflection
:param intensity: [1*n] array of floats, intensity parameter of each reflection
:param min_overlap: float, how close the reflections are to be grouped
:return: group_idx: [1*m] array of int index
hkl2Q(hkl, UVstar)
Convert reflection indices (hkl) to orthogonal basis in A-1
:param hkl: [nx3] array of reflections
:param UV: [3x3] array of unit cell vectors [[a1,a2,a3],[b1,b2,b3],[c1,c2,c3]]
:return: [nx3] array of wavevectors in an orthogonal basis, in A-1
hkl2Qmag(hkl, UVstar)
Calcualte magnitude of Q from hkl reflection
:param hkl: [nx3] array of reflections
:param UV: [3x3] array of unit cell vectors
:return: [nx1] array of wavevector magnitudes, in A-1
hkl2dspace(hkl, UVstar)
Calcualte d-spacing from hkl reflection
:param hkl: [nx3] array of reflections
:param UV: [3x3] array of reciprocal unit cell vectors
:return: [nx1] array of d-spacing in A
hkl2str(hkl)
Convert hkl to string (h,k,l)
:param hkl:
:return: str '(h,k,l)'
hkl2twotheta(hkl, UVstar, energy_kev=17.794, wavelength_a=None)
Calcualte d-spacing from hkl reflection
:param hkl: [nx3] array of reflections
:param UV: [3x3] array of unit cell vectors
:param energy_kev: float energy in keV
:param wavelength_a: float wavelength in Angstroms
:return: [nx1] array of d-spacing in A
indx(Q, UV)
Index Q(x,y,z) on on lattice [h,k,l] with unit vectors UV
Usage:
  HKL = indx(Q,UV)
  Q = [[nx3]] array of vectors
  UV = [[3x3]] matrix of vectors [a,b,c]
invert_sym(sym_op)
Invert the sign of the given symmetry operation
Usage:
  new_op = invert_sym(sym_op)
  sym_op = str symmetry operation e.g. 'x,y,z'
  new_op = inverted symmetry
E.G.
  new_op = invert_sym('x,y,z')
  >> new_op = '-x,-y,-z'
labvector(vec, U=None, R=None, LAB=None)
Transform any vector through the orientation, rotation and lab transformations
:param vec: [n*3] array of vectors in the diffractometer frame
:param U: [3*3] oritenation matrix (see umatrix)
:param R: [3x3] rotation matrix (see diffractometer_rotation)
:param LAB: [3x3] transformation matrix between diffractometer frame and lab frame
:return: [n*3] array of Q vectors in the lab coordinate system
labwavevector(hkl, UV, U=None, R=None, LAB=None)
Calculate the lab wavevector using the unit-vector, oritenation matrix and rotation matrix
Returns vectors in the lab coordinate system, by default defined like Diamond Light Source:
  x-axis : away from synchrotron ring, towards wall
  y-axis : towards ceiling
  z-axis : along beam direction
:param hkl: [3xn] array of (h, k, l) reciprocal lattice vectors
:param UV: [3*3] Unit-vector matrix (see latpar2ub_rot)
:param U: [3*3] oritenation matrix (see umatrix)
:param R: [3x3] rotation matrix (see diffractometer_rotation)
:param LAB: [3x3] transformation matrix between diffractometer frame and lab frame
:return: [3xn] array of Q vectors in the lab coordinate system
latpar2uv(lattice_parameters=(), *args, **kwargs)
Convert a,b,c,alpha,beta,gamma to UV=[A,B,C]
 UV = latpar2uv(a,b,c,alpha=90.,beta=90.,gamma=120.)
 Vector c is defined along [0,0,1]
 Vector a and b are defined by the angles
latpar2uv_rot(lattice_parameters=(), *args, **kwargs)
Convert a,b,c,alpha,beta,gamma to UV=[A,B,C]
 UV = latpar2uv_rot(a,b,c,alpha=90.,beta=90.,gamma=120.)
 Vector b is defined along [0,1,0]
 Vector a and c are defined by the angles
latparBmatrix(a, b=None, c=None, alpha=90.0, beta=90.0, gamma=90.0)
Calculate the Busing and Levy B matrix from a real space UV
"choose  the x  axis parallel  to a, the  y  axis  in  the  plane  of  a  and  b,  and  the  z  axis
perpendicular  to  that  plane"
From: W. R. Busing and H. A. Levy, Acta Cryst. (1967). 22, 457-464
"Angle calculations for 3- and 4-circle X-ray and neutron diffractometers"
See also: https://docs.mantidproject.org/nightly/concepts/Lattice.html
:param a, b, c: float lattice parameters
:param alpha, beta, gamma: float lattice angles
:return: [3*3] array
latpar_reciprocal(UV)
Return the reciprocal lattice parameters in inverse-angstroms and degrees
:param UV: [3*3] unit vector [a,b,c]
:return: a*, b*, c*, alpha*, beta*, gamma*
latparvolume(a, b=None, c=None, alpha=90.0, beta=90.0, gamma=90.0)
Calcualte the unit cell volume in A^3
:param a, b, c: float lattice parameters
:param alpha, beta, gamma: float lattice angles
:return: float volume in Angstrom^3
lattice_hkl2dspace(hkl, lattice_parameters=(), *args, **kwargs)
Calcualte dspace from lattice parameters
:param hkl: [nx3] array of reflections
:param lattice_parameters: a,b,c,alpha,beta,gamma
:return: float, d-spacing in A
lattice_hkl2twotheta(hkl, energy_kev=17.794, lattice_parameters=(), *args, **kwargs)
Calcualte dspace from lattice parameters
:param hkl: [nx3] array of reflections
:param energy_kev: float, radiation energy in keV
:param lattice_parameters: a,b,c,alpha,beta,gamma
:return: float, d-spacing in A
load_pointgroup(pg_number)
Load point group using number
Point Groups:
Triclinic
  1 C1  (    1) GenPos:   1
  2 Ci  (   -1) GenPos:   2
Monoclinic
  3 C2  (    2) GenPos:   2
  4 Cs  (    m) GenPos:   2
  5 C2h (  2/m) GenPos:   4
Orthorhombic
  6 D2  (  222) GenPos:   4
  7 C2v (  mm2) GenPos:   4
  8 D2h (  mmm) GenPos:   8
Tetragonal
  9 C4  (    4) GenPos:   4
 10 S4  (   -4) GenPos:   4
 11 C4h (  4/m) GenPos:   8
 12 D4  (  422) GenPos:   8
 13 C4v (  4mm) GenPos:   8
 14 D2d ( -42m) GenPos:   8
 15 D4h (4/mmm) GenPos:  16
Trigonal
 16 C3  (    3) GenPos:   3
 17 C3i (   -3) GenPos:   6
 18 D3  (  312) GenPos:   6
 19 C3v (  3m1) GenPos:   6
 20 D3d ( -31m) GenPos:  12
Hexagonal
 21 C6  (    6) GenPos:   6
 22 C3h (   -6) GenPos:   6
 23 C6h (  6/m) GenPos:  12
 24 D6  (  622) GenPos:  12
 25 C6v (  6mm) GenPos:  12
 26 D3h ( -6m2) GenPos:  12
 27 D6h (6/mmm) GenPos:  24
Cubic
 28 T   (   23) GenPos:  12
 29 Th  (  m-3) GenPos:  24
 30 O   (  432) GenPos:  24
 31 Td  ( -43m) GenPos:  24
 32 Oh  ( m-3m) GenPos:  48
:param pg_number: int or str, e.g. 'cubic'
:return: dict
magnetic_form_factor(element, Qmag=0.0)
Read Magnetic form factor table, calculate <j0(|Q|)>
Analytical approximation of the magnetic form factor:
    <j0(|Q|)> = A*exp(-a*s^2) + B*exp(-b*s^2) + C*exp(-c*s^2) + D, where s = sin(theta)/lambda in A-1
See more about the approximatio here: https://www.ill.eu/sites/ccsl/ffacts/ffactnode3.html
Note: Currently only J0 terms are used and 5d atoms are not currently included
Usage:
     Qff = read_mff(element,Qmag)
     element = str element name, e.g. 'Co'
     Qmag = magnitude of Q=sin(theta)/lambda in A-1 at which to calcualte, can be a list or array to return multiple values
     Qff = Magnetic form factor for element at Qmag
E.G.
    Qff = read_mff('Co',np.arange(0,4,0.1))
maxHKL(Qmax, UV)
Returns the maximum indexes for given max radius
e.g.
    UVstar = RcSp([[3,0,0],[0,3,0],[0,0,10]]) # A^-1
    Qmax = 4.5 # A^-1
    max_hkl = maxHKL(Qmax,UVstar)
    max_hkl =
    >>> [3,3,8]
molecular_attenuation_length(chemical_formula, energy_kev, density, grazing_angle=90)
Calcualte X-Ray Attenuation Length
Equivalent to: https://henke.lbl.gov/optical_constants/atten2.html
Based on formulas from: Henke, Gullikson, and Davis, Atomic Data and Nuclear Data Tables 54 no.2, 181-342 (July 1993)
:param chemical_formula: str molecular formula
:param energy_kev: float or array, x-ray energy in keV
:param density: float density in g/cm^3
:param grazing_angle: incidence angle relative to the surface, in degrees
:return: float or array, in microns
molecular_reflectivity(chemical_formula, energy_kev, density, grazing_angle)
Calculate the specular reflectivity of a material
From: https://xdb.lbl.gov/Section4/Sec_4-2.html
:param chemical_formula: str molecular formula
:param energy_kev: float or array, x-ray energy in keV
:param density: float, density in g/cm^3
:param grazing_angle: float, incidence angle relative to the surface, in degrees
:return: float or array
molecular_refractive_index(chemical_formula, energy_kev, density)
Calculate Complex Index of Refraction
    n = 1 - (1/2pi)N*r0*lambda^2*(f1+if2) = 1 - Delta - iBeta
Equivalent to: https://henke.lbl.gov/optical_constants/getdb2.html
Based on formulas from: Henke, Gullikson, and Davis, Atomic Data and Nuclear Data Tables 54 no.2, 181-342 (July 1993)
:param chemical_formula: str molecular formula
:param energy_kev: float or array, x-ray energy in keV
:param density: float density in g/cm^3
:return: n(complex), Delta, Beta
molecular_weight(compound_name)
Calculate the molecular weight of given compound
:param compound_name: str elements
:return: float weight in g
neutron_energy(wavelength_a)
Calcualte the neutron energy in milli-electronvolts using DeBroglie's formula
  E [meV] ~ 81.82 / lambda^2 [A]
:param wavelength_a: neutron wavelength in Angstroms
:return: energy in meV
neutron_scattering_length(element)
Return neutron scattering length, b, in fm
Uses bound coherent scattering length from NIST
https://www.ncnr.nist.gov/resources/n-lengths/
 b = neutron_scattering_length('Co')
:param element: [n*str] list or array of elements
:return: [n] array of scattering lengths
neutron_wavelength(energy_mev)
Calcualte the neutron wavelength in Angstroms using DeBroglie's formula
  lambda [A] ~ sqrt( 81.82 / E [meV] )
:param energy_mev: neutron energy in meV
:return: wavelength in Anstroms
orbital_configuration(element, charge=None)
Returns the orbital configuration of an element as a list of strings
:param element: str, element name, e.g. 'Fe' or 'Fe3+' or '0.8Fe2+'
:param charge: int, element charge (overwrites charge given in element)
:return: ['1s2', '2s2', '2p6', ...]
orthogonal_axes(x_axis=(1, 0, 0), y_axis=(0, 1, 0))
Returns orthogonal right-handed cartesian axes based on the plane of two non-perpendicular vectors
E.G.
    x,y,z = orthogonal_axes([1,0,0],[0.5,0.5,0])
    >> x = array([1,0,0])
    >> y = array([0,1,0])
    >> z = array([0,0,1])
E.G.
    x,y,z = orthogonal_axes([0,1,0],[0,0,1])
    >> x = array([0,1,0])
    >> y = array([0,0,1])
    >> z = array([1,0,0])
peaks_on_plane(peak_x, peak_y, peak_height, peak_width, max_x, max_y, pixels_width=1001, background=0)
Creates a rectangular grid and adds Gaussian peaks with height "intensity"
:param peak_x: [nx1] array of x coordinates
:param peak_y: [nx1] array of y coordinates
:param peak_height: [nx1] array of peak heights
:param peak_width: [nx1] or float, gaussian width
:param max_x: grid will be created from -max_x : +max_x horizontally
:param max_y: grid will be created from -max_y : +max_y vertically
:param pixels_width: grid will contain pixels horizontally and the number vertically will be scaled
:param background: if >0, add a normaly distributed background with average level = background
:return: x, y, plane
peakwidth_deg(domain_size_a, twotheta, wavelength_a=None, energy_kev=None, instrument_resolution=0)
Return the peak width in degrees for a given two-theta based on the
crystallite domain size and instrument resolution.
  Equivalent to the Scherrer equation with shape factor 1.0
:param domain_size_a: crystallite domain size in Anstroms
:param twotheta: scattering angle in degrees
:param wavelength_a: incident beam wavelength, in Angstroms
:param energy_kev: or, incident beam energy, in keV
:param instrument_resolution: instrument resolution in inverse Anstroms
:return:
photoabsorption_crosssection(elements, energy_kev)
Calculate the photoabsorption cross section from the atomic scattering factors
    u = 2*r0*lambda*f2
See: https://henke.lbl.gov/optical_constants/intro.html
:param elements: str or list of string element symbol, if list, absorption will be summed over elements
:param energy_kev: float or array x-ray energy
:return: float or array [len(energy)] m^2
pointgroups()
Read pointgroup file, return dict
powder_average(tth, energy_kev)
Return the powder average correction for intensities at a given two-theta
    Ip = I0*PA,    PA = 1/|Q|^2
:param tth: two-theta in deg
:param energy_kev: energy in keV
:return: PA
print_atom_properties(elements=None)
Outputs string of stored atomic properties
:param elements: str or list or None
        str: 'Co'
        list: ['Co', 'O']
:return: str
q2dspace(qmag)
Calculate d-spacing from |Q|
     dspace = q2dspace(Qmag)
q2units(qmag, units='tth', energy_kev=None)
Convert |Q| in A^-1 to choice of units
:param qmag: float or array in inverse-Angstrom
:param units: str: 'tth', 'dspace', 'q' (raises ValueError if wrong)
:param energy_kev: float or None
:return: float or array
read_atom_properties_file(filedir)
Reads the text file "Dans Element Properties.txt"
Returns a list of dicts containing atomic properites from multiple sources
  data = read_atom_properties_file(filedir)
  data[22]['Element']
:param filedir: location of "Dans Element Properties.txt"
:return: [dict, ...]
readcif(filename=None, debug=False)
Open a Crystallographic Information File (*.cif) file and store all entries in a key:value dictionary
 Looped values are stored as lists under a single key entry
 All values are stored as strings
E.G.
  crys=readcif('somefile.cif')
  crys['_cell_length_a'] = '2.835(2)'
 
crys[key] = value
available keys are give by crys.keys()
 
To debug the file with outputted messages, use:
  cif = readcif(file, debug=True)
 
Some useful standard CIF keywords:
    _cell_length_a
    _cell_length_b
    _cell_length_c
    _cell_angle_alpha
    _cell_angle_beta
    _cell_angle_gamma
    _space_group_symop_operation_xyz
    _atom_site_label
    _atom_site_type_symbol
    _atom_site_occupancy
    _atom_site_U_iso_or_equiv
    _atom_site_fract_x
    _atom_site_fract_y
    _atom_site_fract_z
reciprocal_volume(twotheta, twotheta_min=0, energy_kev=None, wavelength_a=1.5)
Calculate the volume of reciprocal space covered between two scattering angles
:param twotheta: larger scattering angle (Bragg angle, 2theta)
:param twotheta_min: smaller scattering angle
:param energy_kev: photon energy, in keV
:param wavelength_a: wavelength in Angstroms
:return: float, volume, in Angstroms^-3
reflections_on_detector(qxqyqz, energy_kev, det_distance=1.0, det_normal=(0, -1.0, 0), delta=0.0, gamma=0.0, det_width=1.0, det_height=1.0, labmatrix=array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Return relative position of reflection on detector
:param qxqyqz: [nx3] array of reflection coordinates in Q-space [qx, qy, qz]
:param energy_kev: float, Incident beam energy in keV
:param det_distance: float, Detector distance in meters
:param det_normal: (dx,dy,dz) direction of detector normal
:param delta: float, angle in degrees in vertical direction (about diff-z)
:param gamma: float angle in degrees in horizontal direction (about diff-x)
:param det_width: float, detector width along x-axis in meters
:param det_height: float, detector height along z-axis in meters
:param labmatrix: [3x3] orientation array to convert to difference basis
:return uvw: [nx3] array of positions relative to the centre of the detector, or NaN if not incident
:return wavevector_difference: [n] array of wavevector difference in A-1
resolution2energy(res, twotheta=180.0)
Calcualte the energy required to achieve a specific resolution at a given two-theta
:param res: measurement resolution in A (==d-spacing)
:param twotheta: Bragg angle in Degrees
:return: float
rotmatrixx(mu)
Generate diffractometer rotation matrix mu about x-axis (right handed)
Equivalent to ROLL in the Tait–Bryan convention
Equivalent to mu in You et al. diffractometer convention (right handed)
    r = rotmatrix_x(mu)
    vec' = np.dot(r, vec)
vec must be 1D or column vector (3*n)
:param mu: float angle in degrees
:return: [3*3] array
rotmatrixy(chi)
Generate diffractometer rotation matrix chi about y-axis (right handed)
Equivalent to PITCH in the Tait–Bryan convention
Equivalent to -chi in You et al. diffractometer convention (left handed)
    r = rotmatrix_y(chi)
    vec' = np.dot(r, vec)
vec must be 1D or column vector (3*n)
:param chi: float angle in degrees
:return: [3*3] array
rotmatrixz(phi)
Generate diffractometer rotation matrix about z-axis (right handed)
Equivalent to YAW in the Tait-Bryan convention
Equivalent to -phi, -eta, -delta in You et al. diffractometer convention (left handed)
    r = rotmatrix_z(phi)
    vec' = np.dot(r, vec)
vec must be 1D or column vector (3*n)
:param phi: float angle in degrees
:return: [3*3] array
scale_intensity(intensity, wavevector_diff, resolution=0.5)
Scale intensity depending on wavevector difference and resolution
:param intensity: intensity value
:param wavevector_diff: distance from centre in inverse angstroms
:param resolution: resolution in inverse angstroms
:return:
scherrer_fwhm(size, twotheta, wavelength_a=None, energy_kev=None, shape_factor=0.9)
Use the Scherrer equation to calculate the size of a crystallite from a peak width
  L = K * lambda / fwhm * cos(theta)
See: https://en.wikipedia.org/wiki/Scherrer_equation
:param size: crystallite domain size in Angstroms
:param twotheta: 2*Bragg angle, in degrees
:param wavelength_a: incident beam wavelength, in Angstroms
:param energy_kev: or, incident beam energy, in keV
:param shape_factor: dimensionless shape factor, dependent on shape of crystallite
:return: float, peak full-width-at-half-max in degrees
scherrer_size(fwhm, twotheta, wavelength_a=None, energy_kev=None, shape_factor=0.9)
Use the Scherrer equation to calculate the size of a crystallite from a peak width
  L = K * lambda / fwhm * cos(theta)
See: https://en.wikipedia.org/wiki/Scherrer_equation
:param fwhm: full-width-half-maximum of a peak, in degrees
:param twotheta: 2*Bragg angle, in degrees
:param wavelength_a: incident beam wavelength, in Angstroms
:param energy_kev: or, incident beam energy, in keV
:param shape_factor: dimensionless shape factor, dependent on shape of crystallite
:return: float, crystallite domain size in Angstroms
spacegroup(sg_number)
Return a dict of information for a space group
:param sg_number: int space group number, as in the international tables of Crystallography
:return: dict
spacegroup_list(sg_numbers=None)
Return a structured list of space groups with general operations
:param sg_numbers: list of space group numbers (None for whole list)
:return: str
spacegroup_magnetic(msg_number)
Return dict of magnetic spacegroup, given magnetic spacegroup number
:param msg_number: magnetic space group number e.g. 61.433
:return: dict with keys:
    'parent number': sg,
    'space group number': number,
    'space group name': label,
    'setting': setting,
    'type name': type_name,
    'related group': rel_number,
    'related name': rel_name,
    'related setting': rel_setting,
    'operators general': xyz_op,
    'operators magnetic': mxmymz_op,
    'operators time': time,
spacegroup_magnetic_list(sg_number=None, sg_dict=None)
Return str list of magnetic space groups
:param sg_number: space group number (1-230)
:param sg_dict: alternate input, spacegroup dict from spacegroup function
:return: str
spacegroup_subgroups(sg_number)
Return dict of maximal subgroups for spacegroup
:param sg_number: space group number (1-230)
:return: dict
spacegroup_subgroups_list(sg_number=None, sg_dict=None)
Return str of maximal subgroups for spacegroup
:param sg_number: space group number (1-230)
:param sg_dict: alternate input, spacegroup dict from spacegroup function
:return: str
spacegroups()
Return a dict of all space groups
Loaded from json file created from the Bilbao crystallographic server: https://www.cryst.ehu.es/
:return: dict with keys:
    'space group number'
    'space group name html'
    'space group name'
    'web address generator'
    'web address wyckoff'
    'web address site check'     # address % (u, v, w),
    'general positions'
    'magnetic space groups',
    'positions centring'
    'positions multiplicity'
    'positions wyckoff letter'
    'positions symmetry'
    'positions coordinates'
    'subgroup number',
    'subgroup name',
    'subgroup index',
    'subgroup type'
spacegroups_magnetic(sg_number=None, sg_dict=None)
Returns dict of magnetic space groups for required space group
:param sg_number: space group number (1-230) or None to return all magnetic spacegroups
:param sg_dict: alternate input, spacegroup dict from spacegroup function
:return: dict
split_compound(compound_name)
Convert a molecular or compound name into a list of elements and numbers.
Assumes all element multiplications are to the LEFT of the element name.
Values in brackets are multiplied out.
E.g.
    split_compound('Mn0.3(Fe3.6(Co1.2)2)4(Mo0.7Pr44)3')
    >> ['Mn0.3', 'Fe14.4', 'Co9.6', 'Mo2.1', 'Pr132']
:param compound_name: str
:return: list of str
split_element_symbol(element)
From element symbol, split charge and occupancy
  symbol, occupancy, charge = split_element_symbol('Co3+')
Any numbers appended by +/- are taken as charge, otherwise they are counted as occupancy.
e.g.    element     >   symbol  |   occupancy   |   charge
        Co3+            Co          1.              3
        3.2O2-          O           3.2             -2
        fe3             Fe          3               0
:param element: str
:return: symbol: str
:return: occupancy: float
:return: charge: float
str2element(string)
Finds element name in string
sum_sym_ref(symhkl)
Remove equivalent hkl positions and return the number of times each is generated.
sym_mat2str(sym_mat, time=None)
Generate symmetry operation string from matrix
:param sym_mat: array [3x3] or [4x4]
:param time: +/-1 or None
:return: str 'x,y,z(,1)'
sym_op_det(sym_ops)
Return the determinant of a symmetry operation
:param sym_ops: str e.g. 'x,-y,z+1/2' or 'y, x+y, -z, -1' or list of str ['x,y,z',...]
:return: float |det| or list of floats
sym_op_mx(operations)
Convert each operation from x,y,z to mx,my,mz
:param operations: ['x,y,z',]
:return: ['mx,my,mz',]
sym_op_recogniser(sym_ops)
Evaluates symmetry operations and returns str name of operation
 
Doesn't work yet - works on simple systems but not trigonal or hexagonal operations - for example sg167
sym_op_time(operations)
Return the time symmetry of a symmetry operation
:param operations: list(str) e.g. ['x,-y,z+1/2', 'y, x+y, -z, -1']
:return: list, +/-1
symmetry_ops2magnetic(operations)
Convert list of string symmetry operations to magnetic symmetry operations
See Vesta_Manual.pdf Section 9.1.1 "Creation and editing of a vector"
Magnetic symmetry
    µ' = TPMµ
T = Time operators x,y,z,(+1)
P = Parity operator (determinant of M)
M = Symmetry operator without translations
 
:param operations: list of str ['x,y,z',]
:return: list of str ['x,y,z']
ubmatrix(uv, u)
Return UB matrix
:param uv: [3*3] unit vector [a,b,c]
:param u: [3*3] orientation matrix in the diffractometer frame
:return: [3*3] array
uiso2biso(uiso)
Convert U isotropic thermal parameters to B isotropic thermal parameters
:param uiso: Uiso value or array
:return: Biso value or array
umatrix(a_axis=None, b_axis=None, c_axis=None)
Define an orientation matrix in the diffractometer frame
Diffractometer frame according to Fig. 1, H. You, J. Appl. Cryst 32 (1999), 614-623
  z-axis : axis parallel to the phi rotation axis when all angles 0 (towards wall (+x) in lab frame)
  x-axis : vector normal to phi axis where phi=0 (toward ceiling (+y) in lab frame)
  y-axis : vector normal to x,z axes (parallel to beam (+z) in lab frame)
:param a_axis: direction of a in the diffractometer frame
:param b_axis: direction of b in the diffractometer frame
:param c_axis: direction of c in the diffractometer frame
:return: [3*3] array
warn(message, category=None, stacklevel=1, source=None)
Issue a warning, or maybe ignore it or raise an exception.
wave2energy(wavelength)
Converts wavelength in A to energy in keV
 energy_kev = wave2energy(wavelength_a)
 Energy [keV] = h*c/L = 12.3984 / lambda [A]
wavevector(energy_kev=None, wavelength=None)
Return wavevector = 2pi/lambda
wavevector_difference(Q, ki)
Returns the difference between reciprocal lattice coordinates and incident wavevector, in A-1
When the difference is zero, the condition for diffraction is met.
  Laue condition: Q = ha* + kb* + lc* == kf - ki
  Elastic scattering:            |kf| = |ki|
  Therefore:          |Q + ki| - |ki| = 0
  Expanding & simplifing: Q.Q + 2Q.ki = 0
:param Q: [[nx3]] array of reciprocal lattice coordinates, in A-1
:param ki: [x,y,z] incident wavevector, in A-1
:return: [nx1] array of difference, in A-1
wavevector_resolution(energy_range_ev=200, wavelength_range_a=None, domain_size_a=1000)
Calculate the combined wavevector resolutuion in inverse Angstroms
Combines instrument incident beam resolution and the mosaic of the crystal
:param energy_range_ev: Incident x-ray beam resolution in eV
:param wavelength_range_a: Incident beam resolution in Angstroms
:param domain_size_a: crysallite size in Angstroms (provides mosic using Scherrer equation)
:return: float, resolutuion in inverse Angstroms
write_cif(cifvals, filename=None, comments=None)
Write .cif file with values from a cif dict,
Only basic items are saved, rather than returning the orginal file
:param cifvals: dict from readcif
:param filename: filename to write (use None to return string)
:param comments: str comments to write to the file top matter
:return: None
write_mcif(cifvals, filename=None, comments=None)
Write magnetic .mcif file with values from a cif dict,
Only basic items are saved, rather than returning the original file
:param cifvals: dict from readcif
:param filename: filename to write (use None to return string)
:param comments: str comments to write to the file top matter
:return: None
wyckoff_labels(spacegroup_dict, UVW)
Return Wyckoff site labels for given positions
:param spacegroup_dict: spacegroup dict from tables, if magnetic spacegroup given, uses the parent spacegroup
:param UVW: n*3 array([u,v,w]) atomic positions in fractional coordinates
:return: list[n] of Wyckoff site letters
xray_attenuation_length(elements, energy_kev, atom_per_volume, grazing_angle=90)
Calcualte the attenuation length in microns
The depth into the material measured along the surface normal where the intensity of
x-rays falls to 1/e of its value at the surface.
  A = sin(th) / n * mu
:param elements: str or list of str, if list - absorption will be summed over elements
:param energy_kev: float array
:param atom_per_volume: float atoms per A^3
:param grazing_angle: incidence angle relative to the surface, in degrees
:return: float or array in microns
xray_dispersion_corrections(elements, energy_kev=None)
Read xray dispersion corrections from atomic scattering factor table, giving f' and f" for different energies
From: http://henke.lbl.gov/optical_constants/asf.html
:param elements: list of str, name of element
:param energy_kev: float or list energy in keV (None to return original, uninterpolated list)
:return: f', f" with shape (len(energy), len(elements))
xray_harmonic_rejection(elements, energy_kev, atom_per_volume, grazing_angle, harmonic=2)
Calculate the specular reflectivity of a material
From: https://xdb.lbl.gov/Section4/Sec_4-2.html
:param elements: str or list of str, if list - absorption will be summed over elements
:param energy_kev: float array
:param atom_per_volume: float atoms per A^3
:param grazing_angle: incidence angle relative to the surface, in degrees
:param harmonic: int harmonic multiple
:return: float or array
xray_reflectivity(elements, energy_kev, atom_per_volume, grazing_angle)
Calculate the specular reflectivity of a material
From: https://xdb.lbl.gov/Section4/Sec_4-2.html
:param elements: str or list of str, if list - absorption will be summed over elements
:param energy_kev: float array
:param atom_per_volume: float atoms per A^3
:param grazing_angle: incidence angle relative to the surface, in degrees
:return: float or array
xray_refractive_index(elements, energy_kev, atom_per_volume)
Calculate the complex index of refraction of a material
  n = 1 - (1/2pi)N*r0*lambda^2*(f1+if2) = 1 - Delta - iBeta
:param elements: str or list of str, if list atomic scattering factors will be summed over elements
:param energy_kev: float array
:param atom_per_volume: float atoms per A^3
:return: complex float or array
xray_scattering_factor(element, Qmag=0)
Read X-ray scattering factor table, calculate f(|Q|)
Uses the coefficients for analytical approximation to the scattering factors - ITC, p578 Table 6.1.1.4
 Qff = xray_scattering_factor(element, Qmag=[0])
:param element: [n*str] list or array of elements
:param Qmag: [m] array of wavevector distance, in A^-1
:return: [m*n] array of scattering factors
xray_scattering_factor_WaasKirf(element, Qmag=0)
Read X-ray scattering factor table, calculate f(|Q|)
Uses the coefficients for analytical approximation to the scattering factors from:
   "Waasmaier and Kirfel, Acta Cryst. (1995) A51, 416-431"
File from https://github.com/diffpy/libdiffpy/blob/master/src/runtime/f0_WaasKirf.dat
 Qff = xray_scattering_factor_WaasKirf(element, Qmag=[0])
:param element: [n*str] list or array of elements
:param Qmag: [m] array of wavevector distance, in A^-1
:return: [m*n] array of scattering factors
xray_scattering_factor_resonant(element, Qmag, energy_kev)
Read X-ray scattering factor table, calculate f(|Q|)
Uses the coefficients for analytical approximation to the scattering factors - ITC, p578
 Qff = xray_scattering_factor_resonant(element, energy_kev, qmag)
 
if resonant_energy = float(energy in keV), the total atomic scattering amplitude will be returned:
    f(|Q|, E) = f0(|Q|) + f'(E) - if''(E)
See:
:param element: [n*str] list or array of elements
:param Qmag: [m] array wavevector distance |Q|, in A^-1
:param energy_kev: [o] array energy in keV
:return: [m*n*o] complex array of scattering factors
xray_transmission(elements, energy_kev, atom_per_volume, distance_um)
Calculate the transimssion of x-rays through a thick slab
  T/T0 = exp(-n*mu*d)
:param elements: str or list of str, if list - absorption will be summed over elements
:param energy_kev: float array
:param atom_per_volume: float atoms per A^3
:param distance_um: float distance in microns
:return: float or array

 
Data
        ATOMFILE = r'C:\Users\grp66007\OneDrive - Diamond Light Sourc...Dans_Diffraction\data\Dans Element Properties.txt'
CIF_REQUIRES = ['_cell_length_a', '_cell_length_b', '_cell_length_c', '_cell_angle_alpha', '_cell_angle_beta', '_cell_angle_gamma', '_atom_site_label', '_atom_site_fract_x', '_atom_site_fract_y', '_atom_site_fract_z']
ELEMENT_LIST = ['Zr', 'Mo', 'Es', 'Eu', 'Fe', 'Fl', 'Fm', 'Fr', 'Ga', 'Gd', 'Ge', 'He', 'Hf', 'Hg', 'Ho', 'Hs', 'In', 'Ir', 'Kr', 'La', ...]
PENGFILE = r'C:\Users\grp66007\OneDrive - Diamond Light Sourc...s\Dans_Diffraction\Dans_Diffraction\data\peng.dat'
datadir = r'C:\Users\grp66007\OneDrive - Diamond Light Sourc...onProjects\Dans_Diffraction\Dans_Diffraction\data'
element_regex = re.compile('Zr|Mo|Es|Eu|Fe|Fl|Fm|Fr|Ga|Gd|Ge|He|...n|Co|Cr|Cs|Cu|Mn|Md|Mt|Rb|Rf|Rh|Rn|Ru|Sb|Sc|Se|S)