Skip to content

Important API functions for CTR modeling

canrong qiu edited this page May 28, 2021 · 12 revisions

Here I will give a list of important API functions frequently used in CTR modeling to help you better understand your model script.

UnitCell

UnitCell is a class object defined in the module file sxrd_dafy.py, which is located at DaFy/models/structure_tools. It is used to create a unitcell object responsible for q calculation for any (H,K,L). With unitcell instance, you can easily transform fractional coordinates to Cartesian coordinates. Example to use it

import models.structure_tools.sxrd_dafy as model
lat_pars = [5.038, 5.434, 7.3707, 90, 90, 90]
unitcell = model.UnitCell(*lat_pars)
h, k, l = [0,0,0],[0,0,0],[0.1,0.2,0.3]
unitcell.abs_hkl(h, k, l)#This is equal to the inverse lattice spacing 1/d_hkl
q_par,q_ver = unitcell.q_par_q_ver(h, k, l) #calculate the q vector in horizontal direction and vertical direction
pt_fr = np.array([0.2,0.1,0.5])#coordinates in fractional unit in lattice coordinate system
pt_cat = np.dot(unitcell.lattice.RealTM, pt_fr)#coordinates in Cartesian system
pt_fr == np.dot(unitcell.lattice.RealTMInv,pt_cat)#True

Slab

Slab is an important class object to build CTR structure model, including bulk slab, surface slab and sorbate slab. This class instance stores the information of atoms in one unitcell. This class object is also defined in the module file sxrd_dafy.py. Example to create a bulk slab

import models.structure_tools.sxrd_dafy as model
bulk = model.Slab(T_factor='u')
#add one atom in the slab, you can use a for loop to add multiple atoms
bulk.add_atom(id='atom_id_1', element = 'O', x =0, y=0, z=0, u = 0.1, oc = 1.0, m = 1.0)

It worths mentioning here the meaning of T_factor attribute. The attribute T_factor is a tag for two different interpretations of Thermal factor, which can be either 'u' or 'B'. When T_factor = 'u', it represent the u you give in add_atom is the Gaussian distribution width due to thermal vibration width in Å. If T_factor = 'B', then it is mathmatically related to u by B = 8pi*square(u). Depending on the data source of your bulk file, the DWF variable can either be 'B' or 'u'. The symbol 'B' is very commonly used in the community of crystallography, while surface scientists prefer to use the symbol 'u' instead. The 'B' will be internally converted to 'u' for DWF calculation. If you have bulk file in the right format (refer to bulk file format for details), then you can take advantage of the util function add_atom_in_slab to add all containing atoms in the file to an initialized Slab.

import models.structure_tools.sxrd_dafy as model
from models.structure_tools import tool_box
bulk = model.Slab(T_factor='u')#doublecheck is it a 'u' or a 'B'.
tool_box.add_atom_in_slab(bulk,'/Users/canrong/apps/DaFy/util/batchfile/hematite_rcut/bulk.str')

As described in CTR conceptual model, you will need to define Slab instance for bulk, surface and sorbate seperately. It is staightforward to define Slab for bulk and surface slabs, if you have bulk and surface files ready. Just follow the example above. To define an Slab for a sorbate motif is more tricky, but there are a lot of API functions to help you with that. Refer to the section script generator GUI for a variety of predefined structural motifs. In the process of model script generating wizard, the hard work is already done for you. The following snippet just give you some sense of how to build a sorbate Slab using one of the API class object (Tetrahedra motif in this case).

import models.structure_tools.sxrd_dafy as model
from models.structure_tools import sorbate_tool_beta as sorbate_tool
from models.structure_tools import tool_box
surface_1 = model.Slab(c = 1.0)
tool_box.add_atom_in_slab(surface_1, '/Users/canrong/apps/DaFy/util/batchfile/hematite_rcut/half_layer2.str')
sorbate_instance_1 = sorbate_tool.Tetrahedra.build_instance(substrate_domain = surface_1,anchored_ids={'attach_atm_ids': ['O1_1_0', 'O1_3_0'], 'offset': [None, None], 'anchor_ref': 'Fe1_4_0', 'anchor_offset': None},binding_mode = {'mode': 'BD'},structure_pars_dict={'top_angle_offset': 0.0, 'angle_offset': [0, 0], 'phi': 0.0, 'edge_offset': [0, 0]},anchor_id = 'As1',ids = ['As1', 'O1', 'O2'], els = ['As', 'O', 'O'], lat_pars = [5.038, 5.434, 7.3707, 90, 90, 90], T=None, T_INV = None)
domain_sorbate_1 = sorbate_instance_1.domain#The slab object for the sorbate motif

AtomGroup

AtomGroup makes it possible to group multiple atoms (from the same or different slabs) together in a way that can be defined. Quit often, it is reasonable to group atoms together in CTR fitting process. For example, atoms on the same atomic layer should move together in the vertical direction with a same occupancy. Depending on the intrinsic substrate surface symmetry, the layer atoms may move in the opposite direction along either x or y axis (e.g glide plane symmetry). To define the atomic movement in a symmetrical way, you just need to provide the associated symmetry matrix (a list of 9 values, will be reshapped to 3by3 matrix). This is very cumbersome work could be easily done as described in the script generator wizard. See the following example.

atm_gp_L1 = model.AtomGroup(instance_name = 'atm_gp_L1')
atm_gp_L1.add_atom(surface_1,'O1_2_0',matrix=[1, 0, 0, 0, 1, 0, 0, 0, 1])
atm_gp_L1.add_atom(surface_1,'O1_1_0',matrix=[-1, 0, 0, 0, 1, 0, 0, 0, 1])
atm_gp_L2 = model.AtomGroup(instance_name = 'atm_gp_L2')
atm_gp_L2.add_atom(surface_1,'O1_4_0',matrix=[1, 0, 0, 0, 1, 0, 0, 0, 1])
atm_gp_L2.add_atom(surface_1,'O1_3_0',matrix=[-1, 0, 0, 0, 1, 0, 0, 0, 1])
atm_gp_L3 = model.AtomGroup(instance_name = 'atm_gp_L3')
atm_gp_L3.add_atom(surface_1,'Fe1_6_0',matrix=[1, 0, 0, 0, 1, 0, 0, 0, 1])
atm_gp_L3.add_atom(surface_1,'Fe1_4_0',matrix=[-1, 0, 0, 0, 1, 0, 0, 0, 1])
#/atmgroup/end#

Sample

Sample is a class object that wrap all slab objects together to build a CTR structure model. To achieve the solution to deal with multi-domain structure model, the value of class argument slabs will take a python dictionary data structure. The value for each item is another dictionary of 5 items, including slab (the surface slab which the sorbate will associate with), sorbate (the sorbate slab), wt (normalized domain weight), sorbate_sym (the surface symmetry of sorbate motif), and layered_water (the layering water object). See the following example.

surface_parms = {'delta1': 0.0, 'delta2': 0.1391}
domains = {}
for i in range(num_surface_slabs):
    domains['domain{}'.format(i+1)] = {}
    domains['domain{}'.format(i+1)]['slab'] = globals()['surface_{}'.format(i+1)]
    domains['domain{}'.format(i+1)]['sorbate'] = [globals()['domain_sorbate_{}'.format(j+1)] for j in range(num_sorbate_slabs)]
    domains['domain{}'.format(i+1)]['wt'] = getattr(globals()['rgh_wt'],'wt_domain{}'.format(i+1))
    domains['domain{}'.format(i+1)]['sorbate_sym'] = globals()['sorbate_syms_{}'.format(i+1)]
    domains['domain{}'.format(i+1)]['layered_water'] = rgh_lw
sample = model.Sample(inst = inst, bulk_slab = bulk, slabs = domains, unit_cell = unitcell, surface_parms = surface_parms)

With a sample defined in this way, we can calculate the CTR structue factor using function f_calc_all(h,k,l). To execute this function, you need to extract h, k, l as numpy.array format from the data instance.

condition_ctr = data.ctr_data_all[:,-1]<100
condition_used_ctr = data.binary_comparison_and(data_use_array, condition_ctr)
if True in list(condition_used_ctr):
    h_, k_, x_,LB_,dL_ = data.ctr_data_all[condition_used_ctr][:,0], data.ctr_data_all[condition_used_ctr][:,1], data.ctr_data_all[condition_used_ctr][:,2],data.ctr_data_all[condition_used_ctr][:,4],data.ctr_data_all[condition_used_ctr][:,5]
    f_ = sample.calc_f_all(h_, k_, x_)
    F_ = abs(f_*f_)

Besides the CTR structure factor, the calculation of RAXS structure factor is also implemented using f_calc_raxs_all(h,k,l,E,tag). To run this function, you need to provide two extra arguments, E, which is the energy variables in eV, and tag, which is a tag label of integer (>=100). The tag label is used to distinguish the CTR data (tag label from 1 to 99) from the RAXS data (tag label from 100 on). This standard will cause no problem unless you have more than 99 CTR datasets. NO WAY!

condition_raxs = data.ctr_data_all[:,-1]>=100
condition_used_raxs = data.binary_comparison_and(data_use_array, condition_raxs)
if True in list(condition_used_raxs):
    h_, k_, E_, l_, LB_,dL_, tag = data.ctr_data_all[condition_used_raxs][:,0], data.ctr_data_all[condition_used_raxs][:,1], data.ctr_data_all[condition_used_raxs][:,2],data.ctr_data_all[condition_used_raxs][:,3], data.ctr_data_all[condition_used_raxs][:,4],data.ctr_data_all[condition_used_raxs][:,5], data.ctr_data_all[condition_used_raxs][:,-1]-100
    f_ = sample.calc_f_all_RAXS(h_, k_, l_, E_,tag)
    F_ = abs(f_*f_)
Clone this wiki locally