def x2w(self, x): """ Convert molar fraction of carbon to weight fraction Method only available if the chemical potentials are loaded from a Thermo-Calc generated file """ from periodictable import elements if isinstance(x, list): x = np.array(x) # select X(*) like columns xcols = [ c for c in self.chempot.columns if fnmatch(c, 'X(*)') and c != 'X(Z)' ] # created DataFrame with selected columns and rename them accordingly dfx = self.chempot[xcols].rename(mapper=lambda c: c[2:-1].title(), axis='columns') # get molar mass M = np.array([elements.symbol(el).mass for el in dfx.columns]) # interpolate composition xcomp = interp1d(dfx['C'].values, dfx.values.T, axis=1)(x) if xcomp.ndim == 1: sumxcomp = float(np.sum(xcomp.T * M)) else: sumxcomp = np.sum(xcomp.T * M, axis=1) return x * elements.symbol('C').mass / sumxcomp
def __init__(self, majorelement, x=None, w=None): if x is None: self.x = dict() else: self.x = x if w is None: self.w = dict() else: self.w = w self.major = majorelement # element that's accounted by balance self.M = None # average molar mass of the alloy # If major element passed in either x or w, then remove from respective dictionaries if self.major in self.x: print( 'Major element composition should not be provided and will be ignored.' ) self.x.pop(self.major) if self.major in self.w: print( 'Major element composition should not be provided and will be ignored.' ) self.w.pop(self.major) # Load molar masses of alloy elements self.Mi = {self.major: elements.symbol(self.major).mass} self.Mi.update({el: elements.symbol(el).mass for el in self.x.keys()}) self.Mi.update({el: elements.symbol(el).mass for el in self.w.keys()})
def generate_form_factors(N_atoms_uc, atom_list, hkl_interval): # Form Factor eval_pnts = np.linspace(hkl_interval[0],hkl_interval[1],hkl_interval[2]) ff_list = [] for i in range(N_atoms_uc): try: elSym = atom_list[i].label elSym = elements.symbol(elSym) elMass = atom_list[i].massNum if elMass == None: #The mass number will now generally be None -Tom 1/28/11 el = elSym else: el = elSym[elMass] #print el val = atom_list[i].valence if val != None: Mq = el.magnetic_ff[val].M_Q(eval_pnts) else: Mq = el.magnetic_ff[0].M_Q(eval_pnts) except: Mq = np.zeros(len(eval_pnts)) #Test to see how things look without ff #Mq = np.ones(len(eval_pnts)) ff_list = Mq # fake = np.ones(len(kaprange)) return np.array(ff_list)
def generate_form_factors(N_atoms_uc, atom_list, hkl_interval): # Form Factor eval_pnts = np.linspace(hkl_interval[0],hkl_interval[1],hkl_interval[2]) ff_list = [] for i in range(N_atoms_uc): try: elSym = atom_list[i].label elSym = elements.symbol(elSym) elMass = atom_list[i].massNum if elMass == None: #The mass number will now generally be None -Tom 1/28/11 el = elSym else: el = elSym[elMass] print el val = atom_list[i].valence if val != None: Mq = el.magnetic_ff[val].M_Q(eval_pnts) else: Mq = el.magnetic_ff[0].M_Q(eval_pnts) except: Mq = np.zeros(len(eval_pnts)) #Test to see how things look without ff #Mq = np.ones(len(eval_pnts)) ff_list = Mq # fake = np.ones(len(kaprange)) return np.array(ff_list)
def convert_composition(comp, to_mole): """Convert from mole to mass fraction Will convert to fractions (i.e., not percentages) Input: comp - dict, key is element symbol, value is amount to_mole - boolean, whether to convert to mole fraction. Note that this method assumes that the method always needs to be converted """ # Convert to other units total_weight = 0.0 for e,a in comp.iteritems(): weight = a / pt_elements.symbol(e).mass if to_mole else a * pt_elements.symbol(e).mass comp[e] = weight total_weight += weight for e,a in comp.iteritems(): comp[e] = a / total_weight
def F(self, HKL): F = 0 q = self.q(HKL) Q = np.sqrt(q.dot(q)) for i in xrange(len(self.basis)): f = elements.symbol(self.basis[i][0]).xray.f0(Q) F += (f * np.exp(-2.j*np.pi * (HKL[0] * self.basis[i][1] + HKL[1] * self.basis[i][2] + HKL[2] * self.basis[i][3]))) return F
def _getMolecularGraph(molecule): """ Generate a graph from the topology of molecule The graph nodes represent atoms and the graph edges represent bonds. Also, the graph nodes store element information. """ graph = nx.Graph() for i, element in enumerate(molecule.element): graph.add_node(i, element=element, number=elements.symbol(element).number) graph.add_edges_from(molecule.bonds) return graph
def F(self, HKL, rod_int=False, rod_alpha=0.1): F = 0 q = self.q(HKL) Q = np.sqrt(q.dot(q)) for i in range(len(self.basis)): f = elements.symbol(self.basis[i][0]).xray.f0(Q) F += (f * np.exp(-2.j*np.pi * (HKL[0] * self.basis[i][1] + HKL[1] * self.basis[i][2] + HKL[2] * self.basis[i][3]))) if(rod_int): qz111 = self.q([1,1,1])[2] return F/(1.-np.exp(-2.j*np.pi*HKL.dot([1,1,1])+rod_alpha)) #return F/(1.-np.exp(-2.j*np.pi*q[2]/qz111)) return F
def _getMolecularGraph(molecule): """ Generate a graph from the topology of molecule The graph nodes represent atoms and the graph edges represent bonds. Also, the graph nodes store element information. """ graph = nx.Graph() for i, element in enumerate(molecule.element): graph.add_node(i, element=element, number=elements.symbol(element.capitalize()).number) graph.add_edges_from(molecule.bonds) return graph
def F_many(self, HKLs, qs=None): if (qs.any() == None): qs = self.q_many(HKLs) Q = np.sqrt(np.sum(qs * qs, axis=1)) f_dict = dict() for el in self.unique_elements: f_dict[el] = elements.symbol(el).xray.f0(Q) f = [] for el in self.basis_el: f.append(f_dict[el]) f = np.array(f) H, x = np.meshgrid(HKLs.T[0], self.basis_xyz.T[0]) K, y = np.meshgrid(HKLs.T[1], self.basis_xyz.T[1]) L, z = np.meshgrid(HKLs.T[2], self.basis_xyz.T[2]) return np.sum(f * np.exp(-2.j * np.pi * (H * x + K * y + L * z)), axis=0)
def solar_abundance(element): '''For a given atomic number, return solar abundance from Asplund et al. 2009. Input ----- element : str The name of an element Output ------ index : int The atomic number of the element abundance : float The solar abundance of the atom in dex ''' abundance = solar.get(element, None) if abundance is None: return None, None index = elements.symbol(element).number return index, abundance
def get_atomic_number_symbol(Z=None, symbol=None): """This function returns a tuple of matching arrays of atomic numbers (Z) and chemical symbols (symbol). :param Z: atomic numbers :type Z: int, array like object of int's :param symbol: chemical symbols :type symbol: str, array like object of str :return: arrays of atomic numbers and chemical symbols :rtype: tuple of :class:`numpy.ndarray` Note: If both Z and symbol are provided the symbol will win out and change the Z to match. """ import numpy as np from periodictable import elements if isinstance(Z, int): Z = [Z] if isinstance(symbol, str): symbol = [symbol] if np.count_nonzero(symbol) == 0: if np.count_nonzero(Z) == 0: raise ValueError("Need to provide list of either Z's or symbols.") else: Z = np.asarray(Z) length = len(Z) symbol = np.empty(length, dtype='<U2') for i in range(length): symbol[i] = elements[Z[i]].symbol else: symbol = np.asarray(symbol) length = len(symbol) Z = np.empty(length, dtype=np.int64) for i in range(length): symbol[i] = symbol[i].capitalize() Z[i] = elements.symbol(symbol[i]).number return (Z, symbol)
def w2x(wC, w={}, x={}, y=dict(Fe=1.), fullcomp=False): """ Calculates the mole fraction of carbon from its weight fraction. Parameters ---------- wC : float weight fraction of C w : dict, optional known full composition of the alloy in weight fraction x : dict, optional known full composition of the alloy in mole fraction y : dict, optional site fraction of the substitutional elements fullcomp : bool, optional if True, returns a dict with full composition. if False, returns only C composition """ if w: w = {k: v*(1-wC)/(1-w['C']) for k, v in w.items()} w['C'] = wC elif x: w = x2w(0, x=x, fullcomp=True) w = {k: v*(1-wC) for k, v in w.items()} w['C'] = wC else: w = x2w(0, y=y, fullcomp=True) w = {k: v*(1-wC) for k, v in w.items()} w['C'] = wC x = {k: v/elements.symbol(k).mass for k, v in w.items()} sumx = sum(x.values()) x = {k: v/sumx for k, v in x.items()} if fullcomp: return x else: return x['C']
def x2w(xC, x={}, w={}, y=dict(Fe=1.), fullcomp=False): """ Calculates the weight fraction of carbon from its mole fraction. Parameters ---------- xC : float mole fraction of C x : dict, optional known full composition of the alloy in mole fraction w : dict, optional known full composition of the alloy in weight fraction y : dict, optional site fraction of the substitutional elements fullcomp : bool, optional if True, returns a dict with full composition. if False, returns only C composition """ if x: x = {k: v*(1-xC)/(1-x['C']) for k, v in x.items()} x['C'] = xC elif w: x = w2x(0, w=w, fullcomp=True) x = {k: v*(1-xC) for k, v in x.items()} x['C'] = xC else: x = {k: v*(1-xC) for k, v in y.items()} x['C'] = xC w = {k: v*elements.symbol(k).mass for k, v in x.items()} sumw = sum(w.values()) w = {k: v/sumw for k, v in w.items()} if fullcomp: return w else: return w['C']
def from_cif(filename, HKL_normal=[0, 0, 1], HKL_para_x=[1, 0, 0], offset_angle=0, E_keV=22.5, override_a=None, override_b=None, override_c=None, override_alpha=None, override_beta=None, override_gamma=None): # print(filename) cf = CifFile.ReadCif(open(filename)) data = cf.first_block() # Load unit cell parameters a = override_a if override_a else float( strip_brackets(data['_cell_length_a'])) b = override_b if override_b else float( strip_brackets(data['_cell_length_b'])) c = override_c if override_c else float( strip_brackets(data['_cell_length_c'])) alpha = override_alpha if override_alpha else float( strip_brackets(data['_cell_angle_alpha'])) beta = override_beta if override_beta else float( strip_brackets(data['_cell_angle_beta'])) gamma = override_gamma if override_gamma else float( strip_brackets(data['_cell_angle_gamma'])) #a = float(strip_brackets(data['_cell_length_a'])) #b = float(strip_brackets(data['_cell_length_b'])) #c = float(strip_brackets(data['_cell_length_c'])) #alpha = float(strip_brackets(data['_cell_angle_alpha'])) #beta = float(strip_brackets(data['_cell_angle_beta'])) #gamma = float(strip_brackets(data['_cell_angle_gamma'])) # Create the basis. # In the CIF file, the coordinates of the basis atoms are given # in fractions of the unit cell vectors a, b, c. _basis = [] atom_sites = data.GetLoop('_atom_site_label') elnames = atom_sites['_atom_site_label'] fractxs = atom_sites['_atom_site_fract_x'] fractys = atom_sites['_atom_site_fract_y'] fractzs = atom_sites['_atom_site_fract_z'] for i in range(len(elnames)): # In some files, the atoms have names like 'Co1', 'O1', 'O2', # and we need to strip the numbers to get the element name. elname = str.strip(str(elnames[i]), '123456789') el = elements.symbol(elname) max_E = 30 f1, f2 = el.xray.scattering_factors(energy=min(max_E, E_keV)) x = float(strip_brackets(fractxs[i])) y = float(strip_brackets(fractys[i])) z = float(strip_brackets(fractzs[i])) #TODO: test #_basis.append([f1 + 1.j * f2, np.array([x, y, z, 1])]) _basis.append([elname, np.array([x, y, z, 1])]) # The basis given by the atom_site_label entries is not the full # content of the unit cell. # We can create the full unit cell by applying the symmetry operations # defined by the crystal's space group to the atoms. # Luckily, we do not need to do this by ourselves, because all the # symmetry-equivalent positions are already given in the CIF file # under symmetry_equiv_pos_as_xyz. # "Copying" all atoms in atom_site_label to all symmetry-equivalent # positions will give the full unit cell. basis = [] sym_pos_arr = data.GetLoop('_symmetry_equiv_pos_as_xyz') for sym_pos in sym_pos_arr: # Some CIF files use the format # x,y,z # Others use the format: # 1 'x, y, z' # This approach will handle both cases: symp = sym_pos[len(sym_pos) - 1] # We will create a translation matrix that we can apply to an # atom to move it to this symmetry-equivalent position toks = symp.split(',') pos = ['x', 'y', 'z'] translation_matrix = np.zeros((4, 4)) translation_matrix[3, 3] = 1 # [row, column] # Loop through the three coordinates # of the symmetry-equivalent position for i in range(len(toks)): # Moves by x, y, or z are expressed by an suitable entry # in the translation matrix. The summand is then deleted from # the string. for j in range(len(pos)): if (('-%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = -1 toks[i] = toks[i].replace('-%s' % (pos[j]), '') # break elif (('+%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = 1 toks[i] = toks[i].replace('+%s' % (pos[j]), '') # break elif (('%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = 1 toks[i] = toks[i].replace('%s' % (pos[j]), '') # break # Now, the string should only consist of a fractional # coordinate move along the respective axis, # which we also enter into the translation matrix translation_matrix[i, 3] = float(eval(toks[i] + '.0')) if (False): print(translation_matrix) # Copy all atoms using the translation matrix. # Atoms landing outside the unit cell will be moved inside, # and duplicates will be removed. for atom in _basis: x, y, z, w = translation_matrix.dot(atom[1]) while (x < 0): x += 1 while (x >= 1): x -= 1 while (y < 0): y += 1 while (y >= 1): y -= 1 while (z < 0): z += 1 while (z >= 1): z -= 1 add_atom = True for i in range(len(basis)): if ((basis[i][0] == atom[0]) and (np.round(basis[i][1], 10) == np.round(x, 10)) and (np.round(basis[i][2], 10) == np.round(y, 10)) and (np.round(basis[i][3], 10) == np.round(z, 10))): add_atom = False break if (add_atom): basis.append([atom[0], x, y, z]) if (False): print('basis:', len(basis)) print(basis) return lattice(a, b, c, alpha, beta, gamma, basis, HKL_normal, HKL_para_x, offset_angle, E_keV=E_keV)
assert str(O[18])=='18-O' try: Fe[12] except KeyError,msg: assert msg.args[0] == '12 is not an isotope of Fe' # Check that "for el in elements" works and for iso in el works els = tuple(el for el in elements) assert els[0].number == 0 assert els[1].number == 1 isotopes = tuple(iso for iso in O) assert isotopes[0].isotope == 12 # 12 is the first oxygen isotope listed # Check that table lookup works and fails appropriately Fe.add_isotope(56) assert elements.symbol('Fe') == Fe assert elements.name('iron') == Fe assert elements.isotope('Fe') == Fe assert elements.isotope('56-Fe') == Fe[56] assert elements.isotope('D') == H[2] try: elements.symbol('Qu') except ValueError,msg: assert str(msg) == "unknown element Qu" try: elements.name('Qu') except ValueError,msg: assert str(msg) == "unknown element Qu" try: elements.isotope('Qu') except ValueError,msg:
def cgi_call(): form = cgi.FieldStorage() #print >>sys.stderr, form #print >>sys.stderr, "sample",form.getfirst('sample') #print >>sys.stderr, "mass",form.getfirst('mass') # Parse inputs errors = {}; calculate = form.getfirst('calculate','all') if calculate not in ('scattering','activation','all'): errors['calculate'] = "calculate should be one of 'scattering', 'activation' or 'all'" try: chem = formula(form.getfirst('sample')) except: errors['sample'] = error() try: fluence = float(form.getfirst('flux',100000)) except: errors['flux'] = error() try: fast_ratio = float(form.getfirst('fast','0')) except: errors['fast'] = error() try: Cd_ratio = float(form.getfirst('Cd','0')) except: errors['Cd'] = error() try: exposure = parse_hours(form.getfirst('exposure','1')) except: errors['exposure'] = error() try: mass_str = form.getfirst('mass','1') if mass_str.endswith('kg'): mass = 1000*float(mass_str[:-2]) elif mass_str.endswith('mg'): mass = 0.001*float(mass_str[:-2]) elif mass_str.endswith('ug'): mass = 1e-6*float(mass_str[:-2]) elif mass_str.endswith('g'): mass = float(mass_str[:-1]) else: mass = float(mass_str) except: errors['mass'] = error() try: density_type,density_value = parse_density(form.getfirst('density','0')) except: errors['density'] = error() try: #print >>sys.stderr,form.getlist('rest[]') rest_times = [parse_rest(v) for v in form.getlist('rest[]')] if not rest_times: rest_times = [0,1,24,360] except: errors['rest'] = error() try: decay_level = float(form.getfirst('decay','0.001')) except: errors['decay'] = error() try: thickness = float(form.getfirst('thickness', '1')) except: errors['thickness'] = error() try: wavelength_str = form.getfirst('wavelength','1').strip() if wavelength_str.endswith('meV'): wavelength = nsf.neutron_wavelength(float(wavelength_str[:-3])) elif wavelength_str.endswith('m/s'): wavelength = nsf.neutron_wavelength_from_velocity(float(wavelength_str[:-3])) elif wavelength_str.endswith('Ang'): wavelength = float(wavelength_str[:-3]) else: wavelength = float(wavelength_str) #print >>sys.stderr,wavelength_str except: errors['wavelength'] = error() try: xray_source = form.getfirst('xray','Cu Ka').strip() if xray_source.endswith('Ka'): xray_wavelength = elements.symbol(xray_source[:-2].strip()).K_alpha elif xray_source.endswith('keV'): xray_wavelength = xsf.xray_wavelength(float(xray_source[:-3])) elif xray_source.endswith('Ang'): xray_wavelength = float(xray_source[:-3]) elif xray_source[0].isalpha(): xray_wavelength = elements.symbol(xray_source).K_alpha else: xray_wavelength = float(xray_source) #print >>sys.stderr,"xray",xray_source,xray_wavelength except: errors['xray'] = error() try: abundance_source = form.getfirst('abundance','IAEA') if abundance_source == "NIST": abundance = activation.NIST2001_isotopic_abundance elif abundance_source == "IAEA": abundance = activation.IAEA1987_isotopic_abundance else: raise ValueError("abundance should be NIST or IAEA") except: errors['abundance'] = error() if errors: return {'success':False, 'error':'invalid request', 'detail':errors} # Fill in defaults #print >>sys.stderr,density_type,density_value,chem.density if density_type == 'default' or density_value == 0: # default to a density of 1 if chem.density is None: chem.density = 1 elif density_type == 'volume': chem.density = chem.molecular_mass/density_value elif density_type == 'natural': # if density is given, assume it is for natural abundance chem.natural_density = density_value elif density_type == 'isotope': chem.density = density_value else: raise ValueError("unknown density type %r"%density_type) result = {'success': True} result['sample'] = { 'formula': str(chem), 'mass': mass, 'density': chem.density, 'thickness': thickness, 'natural_density': chem.natural_density, } # Run calculations if calculate in ('activation', 'all'): try: env = activation.ActivationEnvironment(fluence=fluence,fast_ratio=fast_ratio, Cd_ratio=Cd_ratio) sample = activation.Sample(chem, mass=mass) sample.calculate_activation(env,exposure=exposure,rest_times=rest_times,abundance=abundance) decay_time = sample.decay_time(decay_level) total = [0]*len(sample.rest_times) rows = [] for el,activity_el in activation.sorted_activity(sample.activity.items()): total = [t+a for t,a in zip(total,activity_el)] rows.append({'isotope':el.isotope,'reaction':el.reaction,'product':el.daughter, 'halflife':el.Thalf_str,'comments':el.comments,'levels':activity_el}) result['activation'] = { 'flux': fluence, 'fast': fast_ratio, 'Cd': Cd_ratio, 'exposure': exposure, 'rest': rest_times, 'activity': rows, 'total': total, 'decay_level': decay_level, 'decay_time': decay_time, } #print >>sys.stderr,result except: result['activation'] = {"error": error()} #nsf_sears.replace_neutron_data() if calculate in ('scattering', 'all'): try: sld,xs,penetration = neutron_scattering(chem, wavelength=wavelength) result['scattering'] = { 'neutron': { 'wavelength': wavelength, 'energy': nsf.neutron_energy(wavelength), 'velocity': nsf.VELOCITY_FACTOR/wavelength, }, 'xs': {'coh': xs[0], 'abs': xs[1], 'incoh': xs[2]}, 'sld': {'real': sld[0], 'imag': sld[1], 'incoh': sld[2]}, 'penetration': penetration, 'transmission': 100*exp(-thickness/penetration), } except: missing = [str(el) for el in chem.atoms if not el.neutron.has_sld()] if any(missing): msg = "missing neutron cross sections for "+", ".join(missing) else: msg = error() result['scattering'] = {'error': msg } try: xsld = xray_sld(chem, wavelength=xray_wavelength) result['xray_scattering'] = { 'xray': { 'wavelength': xray_wavelength, 'energy': xsf.xray_energy(xray_wavelength), }, 'sld': {'real': xsld[0], 'imag': xsld[1]}, } except: result['xray_scattering'] = {'error': error()} return result
def __init__(self, a, b, c, alpha=90, beta=90, gamma=90, basis=[0, 0, 0], HKL_normal=[0, 0, 1], HKL_para_x=[1, 0, 0], offset_angle=0, energy_keV=22.5): self.a = a self.b = b self.c = c self.alpha = np.deg2rad(alpha) self.beta = np.deg2rad(beta) self.gamma = np.deg2rad(gamma) self.energy_keV = energy_keV self.k0 = id03.get_K_0(energy_keV) self.basis = basis # list of [Atomic_form_factor, x, y, z] for i in xrange(len(self.basis)): if (isinstance(self.basis[i][0], basestring)): f1, f2 = elements.symbol( self.basis[i][0]).xray.scattering_factors( energy=energy_keV) self.basis[i][0] = f1 + 1.j * f2 self.HKL_normal = HKL_normal self.HKL_para_x = HKL_para_x # calculate real space unit cell vectors self.A1 = np.array([self.a, 0, 0]) self.A2 = np.array( [self.b * np.cos(self.gamma), b * np.sin(self.gamma), 0]) A31 = self.c * np.cos(self.alpha) A32 = self.c / np.sin(self.gamma) * ( np.cos(self.beta) - np.cos(self.gamma) * np.cos(self.alpha)) A33 = np.sqrt(self.c**2 - A31**2 - A32**2) self.A3 = np.array([A31, A32, A33]) self.RealTM = np.array([[self.A1[0], self.A2[0], self.A3[0]], [self.A1[1], self.A2[1], self.A3[1]], [self.A1[2], self.A2[2], self.A3[2]]]) #TODO: remove if (0): from mayavi import mlab try: engine = mayavi.engine except NameError: from mayavi.api import Engine engine = Engine() engine.start() if len(engine.scenes) == 0: engine.new_scene(size=(600, 800)) scene = engine.scenes[0] fig = mlab.gcf(engine) mlab.figure(figure=fig, bgcolor=(1.0, 1.0, 1.0), fgcolor=(0.0, 0.0, 0.0), engine=engine) hkls = [[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]] lines = [[0, 1], [0, 2], [0, 4], [5, 1], [5, 4], [5, 7], [3, 1], [3, 2], [3, 7], [6, 4], [6, 2], [6, 7]] for line in lines: xyz1 = self.RealTM.dot(hkls[line[0]]) xyz2 = self.RealTM.dot(hkls[line[1]]) mlab.plot3d([xyz1[0], xyz2[0]], [xyz1[1], xyz2[1]], [xyz1[2], xyz2[2]]) mlab.show() # calculate reciprocal space unit cell vectors self._V_real = self.A1.dot(np.cross(self.A2, self.A3)) self.B1 = 2 * np.pi * np.cross(self.A2, self.A3) / self._V_real self.B2 = 2 * np.pi * np.cross(self.A3, self.A1) / self._V_real self.B3 = 2 * np.pi * np.cross(self.A1, self.A2) / self._V_real self.RecTM = np.array([[self.B1[0], self.B2[0], self.B3[0]], [self.B1[1], self.B2[1], self.B3[1]], [self.B1[2], self.B2[2], self.B3[2]]]) self._V_rec = self.B1.dot(np.cross(self.B2, self.B3)) # align surface normal to z axis q_normal = self.q(HKL_normal) q_normal /= np.sqrt(q_normal.dot(q_normal)) z = np.array([0, 0, 1]) v = np.cross(q_normal, z) I = np.zeros((3, 3)) I[0, 0] = 1 I[1, 1] = 1 I[2, 2] = 1 R = np.zeros((3, 3)) if (v[0] == 0 and v[1] == 0 and v[2] == 0): R = I # unit matrix elif (q_normal[0] == 0 and q_normal[1] == 0 and q_normal[2] == -1): R = np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]]) # rotation by 180deg around x else: vx = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]]) R = I + vx + vx.dot(vx) / (1. + q_normal.dot(z)) self.RecTM = R.dot(self.RecTM) # align projection of HKL_para_x to x axis q = self.q(HKL_para_x) rot = -np.arctan2(q[1], q[0]) + np.deg2rad(offset_angle) R = np.array([[np.cos(rot), -np.sin(rot), 0], [np.sin(rot), np.cos(rot), 0], [0, 0, 1]]) self.RecTM = R.dot(self.RecTM) self.RecTMInv = inv(self.RecTM)
def __init__(self, a, b, c, alpha=90, beta=90, gamma=90, basis=[0, 0, 0], HKL_normal=[0, 0, 1], HKL_para_x=[1, 0, 0], offset_angle=0, E_keV=22.5, theta_x=0, theta_y=0, theta_z=0): self.a = a self.b = b self.c = c self.theta_x = theta_x self.theta_y = theta_y self.theta_z = theta_z self.alpha = np.deg2rad(alpha) self.beta = np.deg2rad(beta) self.gamma = np.deg2rad(gamma) self.E_keV = E_keV self.k0 = get_k0(E_keV) self.basis = basis # list of [Atomic_form_factor, x, y, z] max_E = 30 for i in range(len(self.basis)): if (isinstance(self.basis[i][0], str)): f1, f2 = (elements.symbol( self.basis[i][0]).xray.scattering_factors( energy=min(E_keV, max_E))) #TODO: test #self.basis[i][0] = f1 + 1.j*f2 self.HKL_normal = HKL_normal self.HKL_para_x = HKL_para_x # Calculate real space unit cell vectors # We choose a Cartesian coordinate system, in which the a-vector # is parallel to the x-axis ... self.A1 = np.array([self.a, 0, 0]) # ... and the b-vector lies in the xy-plane. self.A2 = np.array( [self.b * np.cos(self.gamma), b * np.sin(self.gamma), 0]) # The c-vector in these Cartesian coordinates is then: A31 = self.c * np.cos(self.beta) A32 = (self.c / np.sin(self.gamma) * (np.cos(self.alpha) - (np.cos(self.beta) * np.cos(self.gamma)))) A33 = np.sqrt(self.c**2 - (A31**2 + A32**2)) self.A3 = np.array([A31, A32, A33]) # Contact Finn / Tim for a more detailed explanation # RealTM * (a, b, c) = (x, y, z) self.RealTM = np.array([[self.A1[0], self.A2[0], self.A3[0]], [self.A1[1], self.A2[1], self.A3[1]], [self.A1[2], self.A2[2], self.A3[2]]]) self.RealTMInv = inv(self.RealTM) # Calculate reciprocal space unit cell vectors self._V_real = self.A1.dot(np.cross(self.A2, self.A3)) self.B1 = 2 * np.pi * np.cross(self.A2, self.A3) / self._V_real self.B2 = 2 * np.pi * np.cross(self.A3, self.A1) / self._V_real self.B3 = 2 * np.pi * np.cross(self.A1, self.A2) / self._V_real # RecTM * (a*, b*, c*) = (qx, qy, qz) self.RecTM = np.array([[self.B1[0], self.B2[0], self.B3[0]], [self.B1[1], self.B2[1], self.B3[1]], [self.B1[2], self.B2[2], self.B3[2]]]) self._V_rec = self.B1.dot(np.cross(self.B2, self.B3)) # Align surface normal to qz axis # In reciprocal space, the surface normal will point "up" (along qz) #self.RecTM = copy.deepcopy(self.RecTM_) q_normal = self.q(HKL_normal) q_normal /= np.linalg.norm(q_normal) z = np.array([0, 0, 1]) v = np.cross(q_normal, z) # Our rotation axis # I = Identity matrix I = np.zeros((3, 3)) I[0, 0] = 1 I[1, 1] = 1 I[2, 2] = 1 R = np.zeros((3, 3)) if (v[0] == 0 and v[1] == 0 and v[2] == 0): R = I # If v = (0,0,0), q_normal is already parallel to q_z elif (q_normal[0] == 0 and q_normal[1] == 0 and q_normal[2] == -1): R = np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]]) # If q_normal = (0,0,-1), q_normal is already antiparallel to q_z, # so we just need to rotate by 180 deg around the x-axis # If none of the previous two edge cases apply, we can use the # solution as per http://math.stackexchange.com/questions/180418 else: vx = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]]) R = I + vx + vx.dot(vx) / (1. + q_normal.dot(z)) self.RecTM = R.dot(self.RecTM) #self.RecTM = copy.deepcopy(self.RecTM_) # Align projection of HKL_para_x onto x-axis # After this, turn by an additional offset_angle q = self.q(HKL_para_x) rot = -np.arctan2(q[1], q[0]) + np.deg2rad(offset_angle) R = np.array([[np.cos(rot), -np.sin(rot), 0], [np.sin(rot), np.cos(rot), 0], [0, 0, 1]]) self.RecTM = R.dot(self.RecTM) self.RecTMInv = inv(self.RecTM) self.unique_elements = np.unique(np.array(self.basis).T[0]) self.basis_xyz = [] self.basis_el = [] for atom in self.basis: self.basis_el.append(atom[0]) self.basis_xyz.append([atom[1], atom[2], atom[3]]) self.basis_xyz = np.array(self.basis_xyz) self.basis_el = np.array(self.basis_el) self.RealTM_ = copy.deepcopy(self.RealTM) self.RealTMInv_ = inv(self.RealTM_) self.RecTM_ = copy.deepcopy(self.RecTM) self.RecTMInv_ = inv(self.RecTM_) self.apply_xyz_rotation()
def test(): # Check that we can access element properties assert H.name == "hydrogen" assert H.symbol == "H" assert H.number == 1 assert helium.symbol == 'He' # Check that isotopes work and produce the correct strings and symbols O.add_isotope(18) assert H[2].symbol == 'D' assert H[3].symbol == 'T' assert O[18].symbol == 'O' assert str(H[2]) == 'D' assert str(H[3]) == 'T' assert str(O[18]) == '18-O' try: Fe[12] except KeyError as msg: assert msg.args[0] == '12 is not an isotope of Fe' # Check that "for el in elements" works and for iso in el works els = tuple(el for el in elements) assert els[0].number == 0 assert els[1].number == 1 isotopes = tuple(iso for iso in O) assert isotopes[0].isotope == 12 # 12 is the first oxygen isotope listed # Check that table lookup works and fails appropriately Fe.add_isotope(56) assert elements.symbol('Fe') == Fe assert elements.name('iron') == Fe assert elements.isotope('Fe') == Fe assert elements.isotope('56-Fe') == Fe[56] assert elements.isotope('D') == H[2] try: elements.symbol('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.name('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.isotope('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.isotope('4-D') except ValueError as msg: assert str(msg) == "unknown element 4-D" # Check that ions work assert Fe.ion[2].charge == 2 assert Fe.ions == (2, 3) assert str(Fe.ion[2]) == "Fe{2+}" assert str(O.ion[-2]) == "O{2-}" try: Fe.ion[1] raise Exception("accepts invalid ions") except ValueError as msg: assert str(msg) == "1 is not a valid charge for Fe" assert data_files()[0][0] == "periodictable-data/xsf"
def atomList2em(atomList, pixelSize, cubeSize, densityNegative=False): """ atomList2em: @param atomList: @param pixelSize: @param cubeSize: @param densityNegative: @return: """ from math import floor from pytom_volume import vol if len(atomList) == 0: raise RuntimeError('atomList2em : Your atom list is empty!') # get map volume = vol(cubeSize, cubeSize, cubeSize) volume.setAll(0.0) centroidX = 0 centroidY = 0 centroidZ = 0 for i in range(len(atomList)): centroidX += atomList[i].getX() centroidY += atomList[i].getY() centroidZ += atomList[i].getZ() centroidX = centroidX / len(atomList) centroidY = centroidY / len(atomList) centroidZ = centroidZ / len(atomList) centerX = floor(float(cubeSize) / 2.0) centerY = floor(float(cubeSize) / 2.0) centerZ = floor(float(cubeSize) / 2.0) shiftX = centroidX - centerX shiftY = centroidY - centerY shiftZ = centroidZ - centerZ for i in range(len(atomList)): atomList[i].setX(round(atomList[i].getX() / pixelSize) + centerX) atomList[i].setY(round(atomList[i].getY() / pixelSize) + centerY) atomList[i].setZ(round(atomList[i].getZ() / pixelSize) + centerZ) periodicTableAvailable = True try: # searching for periodic table library http://pypi.python.org/pypi/periodictable from periodictable import elements except ImportError: periodicTableAvailable = False for i in range(len(atomList)): x = int(atomList[i].getX()) y = int(atomList[i].getY()) z = int(atomList[i].getZ()) if x >= cubeSize or y >= cubeSize or z >= cubeSize: raise RuntimeError( 'Cube size is too small. Please specify a larger cube for PDB structure!' ) currentValue = volume(x, y, z) if periodicTableAvailable: atomName = atomList[i].getAtomType()[0] element = elements.symbol(atomName) mass = element.mass volume.setV(currentValue + mass, x, y, z) else: if atomList[i].getAtomType()[0] == 'H': ##maybe take this out volume.setV(currentValue + 1.0, x, y, z) elif atomList[i].getAtomType()[0] == 'C': volume.setV(currentValue + 6.0, x, y, z) elif atomList[i].getAtomType()[0] == 'N': volume.setV(currentValue + 7.0, x, y, z) elif atomList[i].getAtomType()[0] == 'O': volume.setV(currentValue + 8.0, x, y, z) elif atomList[i].getAtomType()[0] == 'P': volume.setV(currentValue + 15.0, x, y, z) elif atomList[i].getAtomType()[0] == 'S': volume.setV(currentValue + 16.0, x, y, z) if densityNegative: volume = volume * -1 return volume
def test(): # Check that we can access element properties assert H.name == "hydrogen" assert H.symbol == "H" assert H.number == 1 assert helium.symbol == 'He' # Check that isotopes work and produce the correct strings and symbols O.add_isotope(18) assert H[2].symbol == 'D' assert H[3].symbol == 'T' assert O[18].symbol == 'O' assert str(H[2]) == 'D' assert str(H[3]) == 'T' assert str(O[18]) == '18-O' try: Fe[12] except KeyError as msg: assert msg.args[0] == '12 is not an isotope of Fe' # Check that "for el in elements" works and for iso in el works els = tuple(el for el in elements) assert els[0].number == 0 assert els[1].number == 1 isotopes = tuple(iso for iso in O) assert isotopes[0].isotope == 12 # 12 is the first oxygen isotope listed # Check that table lookup works and fails appropriately Fe.add_isotope(56) assert elements.symbol('Fe') == Fe assert elements.name('iron') == Fe assert elements.isotope('Fe') == Fe assert elements.isotope('56-Fe') == Fe[56] assert elements.isotope('D') == H[2] try: elements.symbol('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.name('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.isotope('Qu') except ValueError as msg: assert str(msg) == "unknown element Qu" try: elements.isotope('4-D') except ValueError as msg: assert str(msg) == "unknown element 4-D" # Check that ions work assert Fe.ion[2].charge == 2 assert Fe.ions == (-4, -2, -1, 1, 2, 3, 4, 5, 6, 7) assert str(Fe.ion[2]) == "Fe{2+}" assert str(O.ion[-2]) == "O{2-}" try: Fe.ion[-3] raise Exception("accepts invalid ions") except ValueError as msg: assert str(msg) == "-3 is not a valid charge for Fe" assert data_files()[0][0] == "periodictable-data/xsf"
def lattice_from_cif(filename, HKL_normal=[0, 0, 1], HKL_para_x=[1, 0, 0], offset_angle=0, energy_keV=22.5): cf = CifFile.ReadCif(filename) data = cf.first_block() a = float(data['_cell_length_a']) b = float(data['_cell_length_b']) c = float(data['_cell_length_c']) alpha = float(data['_cell_angle_alpha']) beta = float(data['_cell_angle_beta']) gamma = float(data['_cell_angle_gamma']) _basis = [] atom_sites = data.GetLoop('_atom_site_label') for atom_site in atom_sites: f1, f2 = elements.symbol( atom_site[0]).xray.scattering_factors(energy=energy_keV) #_atom_site_x = atom_site['_atom_site_fract_x'] #print _atom_site_x _basis.append([ f1 + 1.j * f2, np.array([ float(atom_site[1]), float(atom_site[2]), float(atom_site[3]), 1 ]) ]) basis = [] sym_pos_arr = data.GetLoop('_symmetry_equiv_pos_as_xyz') for sym_pos in sym_pos_arr: toks = sym_pos[0].split(',') pos = ['x', 'y', 'z'] translation_matrix = np.zeros((4, 4)) translation_matrix[3, 3] = 1 # [row, column] for i in xrange(len(toks)): for j in xrange(len(pos)): if (('-%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = -1 toks[i] = toks[i].replace('-%s' % (pos[j]), '') break elif (('+%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = 1 toks[i] = toks[i].replace('+%s' % (pos[j]), '') break elif (('%s' % (pos[j])) in toks[i]): translation_matrix[i, j] = 1 toks[i] = toks[i].replace('%s' % (pos[j]), '') break translation_matrix[i, 3] = float(eval(toks[i] + '.0')) #print translation_matrix for atom in _basis: x, y, z, w = translation_matrix.dot(atom[1]) while (x < 0): x += 1 while (x >= 1): x -= 1 while (y < 0): y += 1 while (y >= 1): y -= 1 while (z < 0): z += 1 while (z >= 1): z -= 1 add_atom = True for i in xrange(len(basis)): if (basis[i] == [atom[0], x, y, z]): add_atom = False if (add_atom): basis.append([atom[0], x, y, z]) #print 'basis:', len(basis) #print basis return lattice(a, b, c, alpha, beta, gamma, basis, HKL_normal, HKL_para_x, offset_angle, energy_keV=energy_keV)