import os import sys import olx import olex_hkl import OlexVFS import time import math from io import StringIO import ntpath import cProfile import pstats #import cProfile, pstats, StringIO #pr = cProfile.Profile() #pr.enable() #pr.disable() #s = StringIO.StringIO() #sortby = 'cumulative' #ps = pstats.Stats(pr, stream=s).sort_stats(sortby) #ps.print_stats() #print s.getvalue() from PeriodicTable import PeriodicTable try: olx.current_hklsrc except: olx.current_hklsrc = None olx.current_hklsrc_mtime = None olx.current_reflections = None olx.current_mask = None olx.current_space_group = None olx.current_observations = None import olex import olex_core import time import cctbx_controller as cctbx_controller from cctbx import maptbx, miller, uctbx, crystal import iotbx from libtbx import easy_pickle, utils from olexFunctions import OV from scitbx.math import distributions from History import hist global twin_laws_d twin_laws_d = {} from scitbx.math import continued_fraction from cctbx import sgtbx, xray from cctbx.array_family import flex import smtbx.utils import numpy import itertools import operator import fractions from cctbx_olex_adapter import OlexCctbxAdapter class twin_rules: def __init__(self,space,twin_axis,hkl_rotation,angle,fom,threshold,rbasf=[0,0.5,0]): #changed default to bad rbasf self.space=space self.twin_axis=twin_axis self.hkl_rotation=hkl_rotation self.angle=angle self.fom=fom self.rbasf=rbasf self.threshold=threshold class OlexCctbxTwinLaws(OlexCctbxAdapter): def __init__(self): #super(OlexCctbxTwinLaws, self).__init__() OV.registerFunction(self.run,False,'twin') OV.registerFunction(self.get_details,False,'twin') OV.registerFunction(self.run_from_gui,False,'twin') def run_from_gui(self, personal=0): print("Searching for Twin laws...") OV.Cursor("busy", "Searching for Twin laws...") personal=int(personal) self.run(personal) #personal OV.Cursor() def setup(self): global twin_laws_d OlexCctbxAdapter.__init__(self) if OV.GetParam('snum.refinement.use_solvent_mask'): txt = "Sorry, using solvent masking and twinning is not supported yet." print(txt) html = "%s" %txt write_twinning_result_to_disk(html) OV.UpdateHtml() return if "_twin" in OV.HKLSrc(): print("It looks like your hklf file is already an hklf 5 format file") #this just checks for _twin and not for it actually being hklf5 return self.fofc=self.get_fo_sq_fc() print(">> Searching for Twin Laws <<") use_image = "" self.twin_laws_d = {} law_txt = "" l = 0 self.twin_law_gui_txt = "" r_list=[] self.filename = olx.FileName() #class-wide variable initialisation self.hklfile=OV.HKLSrc().rsplit('\\',1)[-1].rstrip(".hkl") self.model=self.xray_structure() hkl_sel_num=50 hkl,f_calc,f_obs,f_uncertainty,leastSquare,weights=self.get_differences() self.all_hkl=hkl fo2 = self.reflections.f_sq_obs_filtered symmetry_ops=fo2.crystal_symmetry().space_group().all_ops() self.symmetry_matrices=[] for matrix in symmetry_ops: self.symmetry_matrices+=[numpy.reshape(matrix.as_double_array()[0:9], (3,3))] self.hkl_symmetries=hkl for symm in self.symmetry_matrices: self.hkl_symmetries=numpy.append(self.hkl_symmetries,numpy.dot(symm,hkl.T).T,axis=0) self.all_f_calc=f_calc self.set_grid() self.all_f_obs=f_obs self.weights=weights self.all_sigma=f_uncertainty self.all_leastSquare=leastSquare self.metrical=self.getMetrical() self.metrical_inv=numpy.linalg.inv(self.metrical) self.orthogonalization=numpy.reshape(numpy.array(self.model.unit_cell().orthogonalization_matrix()),(3,3)) self.orthogonalization_inv=numpy.linalg.inv(self.orthogonalization) self.recip_orth=self.orthogonalization_inv.T self.recip_orth_inv=numpy.linalg.inv(self.recip_orth) self.overlap_threshold=OV.GetParam('snum.twinning.olex2.overlap_threshold') self.number_laws=OV.GetParam('snum.twinning.olex2.number_laws') self.cartesian_threshold=self.overlap_threshold #the threshold is provided in *cartesian* reciprocal space, to match with rotax's suggested 0.002AA self.cartesian_threshold_multiplier=numpy.min(numpy.linalg.norm(self.recip_orth,axis=0)) self.relative_threshold=self.cartesian_threshold/self.cartesian_threshold_multiplier self.rounded_is_closest=self.get_threshold_for_rounding() rank=numpy.argsort(leastSquare)[::-1] hkl_sel = numpy.copy(hkl[rank[:hkl_sel_num],:]) self.bad_fc=numpy.copy(f_calc[rank[:hkl_sel_num]]) self.bad_fo=numpy.copy(f_obs[rank[:hkl_sel_num]]) self.bad_hkl=hkl_sel self.bad_weights=numpy.copy(weights[rank[:hkl_sel_num]]) def update_threshold(self,threshold): self.cartesian_threshold=threshold self.relative_threshold=self.cartesian_threshold/self.cartesian_threshold_multiplier def reset_threshold(self): self.cartesian_threshold=self.overlap_threshold self.relative_threshold=self.cartesian_threshold/self.cartesian_threshold_multiplier def set_grid(self): fcalc=self.all_f_calc hkl=self.all_hkl symm=self.hkl_symmetries maximums=numpy.max(abs(symm),axis=0) self.hkl_grid=numpy.zeros((2*int(numpy.ceil(maximums[0])),2*int(numpy.ceil(maximums[1])),2*int(numpy.ceil(maximums[2])))) for i,item in enumerate(symm): #note to self: symm is the list of full (non-asu) hkl, not the symmetry matrices! self.hkl_grid[int(item[0]),int(item[1]),int(item[2])]=fcalc[i%len(hkl)] def get_threshold_for_rounding(self): adjacents_to_zero=numpy.array([[0,0,1],[0,1,-1],[0,1,0],[0,1,1],[1,-1,-1],[1,-1,0],[1,-1,1],[1,0,-1],[1,0,0],[1,0,1],[1,1,-1],[1,1,0],[1,1,1]]) transformed_adjacents=numpy.dot(self.recip_orth,adjacents_to_zero.T).T adjacent_distances=numpy.linalg.norm(transformed_adjacents,axis=1) minDist=numpy.min(adjacent_distances) maxDist=numpy.max(adjacent_distances) threshold_for_rounding=minDist/(2*maxDist) return threshold_for_rounding def get_details(self): self.setup() print("Orthog:") print(self.recip_orth) print("Bad Hkl") print(self.bad_hkl) def run(self, personal=0): self.setup() r_list=[] if personal==0: twin_laws=self.find_twin_laws() elif personal==1: #Angle-Axis twin_laws=self.find_AA_twin_laws() elif personal==2: #Sphere Search twin_laws=self.find_SS_twin_laws() else: print("Invalid parameter passed, personal=", personal) ordered_twins=sorted(twin_laws,key=lambda x: x.rbasf[1], reverse=False) ordered_twins=self.purge_duplicates(ordered_twins) top_twins=ordered_twins[:10] lawcount=0 for i, twin_law in enumerate(top_twins): basf=twin_law.rbasf[0] r=twin_law.rbasf[1] r_diff=twin_law.rbasf[2] lawcount += 1 name = " %s_twin%02d" % (self.hklfile, lawcount) filename = "%s.hkl" % (name) #self.make_hklf5(filename, twin_law, hkl, f_obs,f_uncertainty) self.make_hklf5_from_file(twin_law,new_file=filename) #ZZZZ self.twin_laws_d.setdefault(lawcount, {}) r_no, basf_no, f_data, history = self.run_twin_ref_shelx(twin_law.hkl_rotation.flatten(), basf) try: float(r) except: r = 0.99 r_list.append((r, lawcount, basf)) self.twin_laws_d[lawcount]['BASF'] = basf self.twin_laws_d[lawcount]['r'] = r self.twin_laws_d[lawcount]['r_diff'] = r_diff self.twin_laws_d[lawcount]['matrix'] = twin_law.hkl_rotation.flatten() self.twin_laws_d[lawcount]['name'] = name self.twin_laws_d[lawcount]['HKLSrc'] = OV.HKLSrc() make_twinning_gui(self.twin_laws_d) def run_twin_ref_shelx(self, law, basf): #law_ins = ' '.join(str(i) for i in law[:9]) #print "Testing: %s" %law_ins #file_path = olx.FilePath() #olx.Atreap("%s/notwin.ins" %file_path, b=True) #OV.AddIns("TWIN " + law_ins+" 2") #OV.AddIns("BASF %f"%basf) #curr_prg = OV.GetParam('snum.refinement.program') #curr_method = OV.GetParam('snum.refinement.method') #curr_cycles = OV.GetParam('snum.refinement.max_cycles') #OV.SetMaxCycles(5) #if curr_prg != 'olex2.refine': # OV.set_refinement_program(curr_prg, 'CGLS') #OV.File("%s.ins" %self.filename) rFile = open(olx.FileFull(), 'r') f_data = rFile.readlines() rFile.close() #OV.SetParam('snum.init.skip_routine','True') #OV.SetParam('snum.refinement.program','olex2.refine') #OV.SetParam('snum.refinement.method','Gauss-Newton') # try: # from RunPrg import RunRefinementPrg # a = RunRefinementPrg() # self.R1 = a.R1 # self.wR2 = a.wR2 # his_file = a.his_file # # OV.SetMaxCycles(curr_cycles) # OV.set_refinement_program(curr_prg, curr_method) # finally: # OV.SetParam('snum.init.skip_routine','False') #r = olx.Lst("R1") #olex_refinement_model = OV.GetRefinementModel(False) #if olex_refinement_model.has_key('twin'): # basf = olex_refinement_model['twin']['BASF'][0] #else: # basf = "n/a" return None, None, f_data, None def twinning_gui_def(self): if not self.twin_law_gui_txt: lines = ['search_for_twin_laws'] tools = ['search_for_twin_laws_t1'] else: lines = ['search_for_twin_laws', 'twin_laws'] tools = ['search_for_twin_laws_t1', 'twin_laws'] tbx = {"twinning": {"category":'tools', 'tbx_li':lines } } tbx_li = {'search_for_twin_laws':{"category":'analysis', 'image':'cctbx', 'tools':['search_for_twin_laws_t1'] }, 'twin_laws':{"category":'analysis', 'image':'cctbx', 'tools':['twin_laws'] } } tools = {'search_for_twin_laws_t1':{'category':'analysis', 'display':"%Search for Twin Laws%", 'colspan':1, 'hrefs':['spy.OlexCctbxTwinLaws()'] }, 'twin_laws': {'category':'analysis', 'colspan':1, 'before':self.twin_law_gui_txt, } } return {"tbx":tbx,"tbx_li":tbx_li,"tools":tools} def make_gui(self): """ works out the figure of merit """ #unused q = numpy.zeros((15)) dcopy = numpy.zeros((numpy.shape(mdisag)[0])) fom = 0.0 bc = 0 #bad = numpy.rint(hkl)+numpy.rint(v) - hkl neighbours = list(itertools.combinations_with_replacement(numpy.arange(-1,2,dtype=numpy.float64),3)) bad = numpy.zeros((len(neighbours), numpy.shape(mdisag)[0], 3)) d1s = numpy.zeros((len(neighbours), numpy.shape(mdisag)[0])) mdisag_rint = numpy.rint(mdisag) for i, neighbour in enumerate(neighbours): bad[i,:,:] = numpy.dot(mdisag_rint+neighbour - mdisag, GS.T) d1s[i,:] = numpy.sqrt(numpy.sum(bad[i,:,:]*bad[i,:,:], axis=1)) neighbours_min = numpy.zeros((numpy.shape(mdisag)[0], 3), dtype=numpy.float64) for i in range(numpy.shape(mdisag)[0]): neighbours_min[i,:] = bad[numpy.argmin(d1s[:,i]),i,:] bad_min = numpy.dot(mdisag_rint+neighbours_min - mdisag, GS.T) d_min = numpy.sqrt(numpy.sum(bad_min*bad_min, axis=1)) dcopy = numpy.copy(d_min) dsum = numpy.sum(dcopy) for i, q_i in enumerate(q): q[i]=dsum/(float(numpy.shape(mdisag)[0])-float(bc)) fom=q[i] if (fom<.002): break else: bc+=1 jp = numpy.argmax(dcopy) dsum = dsum - dcopy[jp] dcopy[jp] = 0. fom=1000.*fom q=1000.0*q return fom def get_differences(self): """; Returns the hkl, fcalc, fobs, uncertainty and the signed difference """ fot,fct=self.fofc weights = self.compute_weights(fot, fct) scale_factor = fot.scale_factor(fct, weights=weights) fot = fot.apply_scaling(factor=1/(scale_factor)) leastSquare = (fot.data()-fct.norm().data())/fot.sigmas() hkl=numpy.array(fot.indices()) fobs=numpy.array(fot.data()) fcalc=numpy.array(fct.norm().data()) funcertainty=numpy.array(fot.sigmas()) leastSquare=numpy.array(leastSquare) weights=numpy.array(weights) return hkl,fcalc,fobs,funcertainty,leastSquare,weights def getMetrical(self): model = self.model gl=model.unit_cell().metrical_matrix() g = numpy.zeros((3,3)) for i in range(3): g[i,i] = gl[i] g[0,1] = gl[3] g[0,2] = gl[4] g[1,2] = gl[5] g[1,0] = gl[3] g[2,0] = gl[4] g[2,1] = gl[5] return g def get_integral_twin_laws(self): twin_laws=[] with HiddenPrints(): cctbx_twin_laws = cctbx_controller.twin_laws(self.reflections) for twin_law in cctbx_twin_laws: law = twin_law.as_double_array()[0:9] law=numpy.around(numpy.reshape(numpy.array(law),(3,3)),decimals=3) [basf, r,r_diff]=self.basf_estimate(law) if(basf<0.02): continue twin_laws+=[twin_rules("Integral",[],law,0,0,self.cartesian_threshold,[basf,r,r_diff])] return twin_laws def find_twin_laws(self): hkl_all=self.all_hkl f_calc=self.all_f_calc f_obs=self.all_f_obs hkl=self.bad_hkl number_laws=OV.GetParam('snum.twinning.olex2.number_laws', 4) twin_laws=[] model=self.model found_early=False tests=False #tests=True if tests==True: #This is a test function which replaces the lower ones twin_laws+=self.find_twin_laws_tests() if twin_laws: found_early=True else: #threshold=0.002 #self.update_threshold(threshold) threshold=self.cartesian_threshold prev=len(twin_laws) twin_laws+=self.get_integral_twin_laws() post=len(twin_laws) if twin_laws: twin_laws=self.purge_duplicates(twin_laws) print ("Found " +str(post-prev)+" Integral Twins") if len(twin_laws)r_lower: #prioritise lower over upper basf value basf_minimum=basf_lower r_minimum=r_lower else: basf_minimum=basf_upper r_minimum=r_upper #need to establish a minimum, which could be at 0, before we can employ the golden section search while trialsaccuracy: basf_new=basf_lower+2/(3+math.sqrt(5))*(basf_upper-basf_lower) r_new=self.r_from_basf(basf_new,twin_component,short) if r_newaccuracy: basf_new=basf_lower+basf_upper-basf_minimum r_new=self.r_from_basf(basf_new,twin_component,short) if r_new<=r_minimum: if basf_minimumbasf_new: basf_upper=basf_minimum r_upper=r_minimum basf_minimum=basf_new r_minimum=r_new else: print("basf equivalent, breaking") break else: #r_new>r_minimum if basf_minimumbasf_new: basf_lower=basf_new r_lower=r_new else: print("basf equivalent, breaking") break trials +=1 r_difference=r_0-r_minimum if r_difference<0.001: return [0,r_0,0] return [basf_minimum, r_minimum, r_difference] def r_from_basf(self,basf,twin_component,short=False): if short: Fc_sq=self.bad_fc Fo_sq=self.bad_fo w=self.bad_weights else: Fc_sq=self.all_f_calc Fo_sq=self.all_f_obs w=self.weights Fo_sq=numpy.maximum(Fo_sq,0) fcalc=(1-basf)*Fc_sq+basf*twin_component scale = numpy.dot(w*Fo_sq,fcalc)/numpy.dot(w*fcalc,fcalc) fcalc = scale * fcalc #R = numpy.sum(numpy.abs(numpy.sqrt(numpy.maximum(Fo_sq,0))-numpy.sqrt(fcalc)))/numpy.sum(numpy.sqrt(numpy.maximum(Fo_sq,0))) r1=numpy.sum(abs(abs(numpy.sqrt(Fo_sq))-abs(numpy.sqrt(fcalc))))/numpy.sum(abs(numpy.sqrt(Fo_sq))) wr2=numpy.sqrt(numpy.sum(abs(numpy.dot(w*(Fo_sq-fcalc),Fo_sq-fcalc)))/numpy.sum(abs(w*Fo_sq**2))) return r1 #fcwfc=np.dot(w*fc_sq,fc_sq) #ktilde=np.dot(w*fo_sq,fc_sq)/fcwfc #r1=np.sum(abs(abs(np.sqrt(fo_sq))-abs(np.sqrt(ktilde*fc_sq))))/np.sum(abs(np.sqrt(fo_sq))) #r3=np.sum(abs(np.dot(w*(fo_sq-ktilde*fc_sq),fo_sq-ktilde*fc_sq)))/np.sum(abs(w*fo_sq**2)) #wr2=np.sqrt(r3) ##this is the wR2 def find_twin_axes(self, threshold,size,rotation_fraction): hkl=self.all_hkl model=self.model metrical_matrix=self.metrical metrical_inverse=self.metrical_inv orthogonalization_matrix=self.orthogonalization orthogonalization_inverse=self.orthogonalization_inv reciprocal_orthogonalization_matrix=self.recip_orth reciprocal_orthogonalization_inverse=self.recip_orth_inv self.update_threshold(threshold) perfect_reflection_number=math.exp(-13) possible_twin_laws=[] #angle and the sine and cosine of that, for use in the rotation formula base_rotation_angle=2.*math.pi/rotation_fraction for twin_axis in self.all_axes(size): #itertools.product(numpy.arange(-size,size+1),numpy.arange(-size,size+1),range(size+1)): reciprocal_law=False ##skip inverse axes #if(twin_axis[2]==0): #if(twin_axis[1]<0): #continue #if(twin_axis[1]==0): #if(twin_axis[0]<=0): #continue #if(fractions.gcd(fractions.gcd(twin_axis[0],twin_axis[1]),twin_axis[2])!=1): #continue #using the rodrigues formula to generate the matrices, the O^-1RO for that in lattice coordinates rotation_matrix_lattice_base=self.make_lattice_rotation(twin_axis,base_rotation_angle,orthogonalization_matrix,reciprocal_orthogonalization_matrix) recip_rot_lat_base=self.make_lattice_rotation(twin_axis,base_rotation_angle,reciprocal_orthogonalization_matrix,reciprocal_orthogonalization_matrix) new_hkl=hkl.copy() rec_hkl=hkl.copy() #this is the rotation matrix in the lattice coordinates! due to quirks and transforming a vector into cartesian space, rotating and #transforming back being a O^-1 R O type, we can square and more for 'bigger' angles. for r in numpy.arange(1,rotation_fraction): #every part of this loop relies on the previous completing the hkl rotations - this is for efficiency (about 10%) #reciprocal axis rec_hkl=numpy.dot(rec_hkl,recip_rot_lat_base.T) rec_hkl_displacement=self.find_fom(rec_hkl) #real axis new_hkl=numpy.dot(rotation_matrix_lattice_base,new_hkl.T).T #correct #if not self.sufficient_overlaps(new_hkl, threshold) and not self.sufficient_overlaps(rec_hkl, threshold) : #continue hkl_displacement=self.find_fom(new_hkl) if (rec_hkl_displacementperfect_reflection_number): reciprocal_rotation_lattice=numpy.linalg.matrix_power(recip_rot_lat_base,r) rbasf=self.basf_estimate(reciprocal_rotation_lattice) if rbasf[0]>1e-5: possible_twin_laws+=[twin_rules("Reciprocal",twin_axis,numpy.around(reciprocal_rotation_lattice,decimals=3),r*base_rotation_angle,rec_hkl_displacement,self.cartesian_threshold,rbasf=rbasf)] reciprocal_law=True if (hkl_displacementperfect_reflection_number): rotation_matrix_lattice=numpy.linalg.matrix_power(rotation_matrix_lattice_base,r) rbasf=self.basf_estimate(rotation_matrix_lattice) if rbasf[0]>1e-5: possible_twin_laws+=[twin_rules("Direct",twin_axis,numpy.around(rotation_matrix_lattice,decimals=3),r*base_rotation_angle,hkl_displacement,self.cartesian_threshold,rbasf=rbasf)] self.reset_threshold() return possible_twin_laws def make_lattice_rotation(self, axis, angle,axis_orthog, reciprocal_orthogonalization_matrix): cosine_value=math.cos(angle) sine_value=math.sin(angle) reciprocal_orthogonalization_inverse=numpy.linalg.inv(reciprocal_orthogonalization_matrix) axis_cartesian=numpy.dot(axis_orthog,axis) axis_unit_cartesian=axis_cartesian/self.size_of_3d_vector(axis_cartesian) cross_product_matrix=numpy.array([[0,-axis_unit_cartesian[2],axis_unit_cartesian[1]],[axis_unit_cartesian[2],0,-axis_unit_cartesian[0]],[-axis_unit_cartesian[1],axis_unit_cartesian[0],0]],dtype=float) matrix=numpy.eye(3)+sine_value*cross_product_matrix+(1-cosine_value)*numpy.linalg.matrix_power(cross_product_matrix,2) matrix_lattice=numpy.dot(reciprocal_orthogonalization_inverse,numpy.dot(matrix,reciprocal_orthogonalization_matrix)) return matrix_lattice def make_real_lattice_rotation(self, axis, angle): #believe this is actually the euclidean rotation - thus why I called it 'real' cosine_value=math.cos(angle) sine_value=math.sin(angle) cross_product_matrix=numpy.array([[0,-axis[2],axis[1]],[axis[2],0,-axis[0]],[-axis[1],axis[0],0]],dtype=float) matrix=numpy.eye(3)+sine_value*cross_product_matrix+(1-cosine_value)*numpy.linalg.matrix_power(cross_product_matrix,2) return matrix def find_fom(self, falsehkl): metrical=self.metrical_inv if all(numpy.sum(metrical, axis=1)>=0): #this is a loose condition which dictates rounding to the nearest integer will also give the closest reciprocal lattice point. displacement = (falsehkl+0.5)%1-0.5 distances=numpy.sqrt(numpy.multiply(displacement,numpy.dot(metrical,displacement.T).T).sum(1)) # distances=numpy.linalg.norm(numpy.dot(orthogonalization.T,displacement.T),axis=0) is alternative but slower else: hkl_dropped=numpy.floor(falsehkl) hkl_remainder=falsehkl%1-1 #where it would round to the closest, 1,1,1 will provide that distance! adjacents=numpy.array([[0,0,0],[1,0,0],[0,1,0],[1,1,0],[0,0,1],[1,0,1],[0,1,1],[1,1,1]]) distances=numpy.full(numpy.shape(falsehkl)[0],1000) for i in numpy.arange(0,8): displacement=hkl_remainder+adjacents[i] #hkl_adjacent=hkl_dropped+adjacents[i] #displacement=falsehkl-hkl_adjacent size=numpy.sqrt(numpy.multiply(displacement,numpy.dot(metrical,displacement.T).T).sum(1)) distances=numpy.minimum(distances,size) sortedDist=numpy.sort(distances) length=len(distances) filtered=sortedDist[:-int(length/2)] return numpy.average(filtered) def closest_points(self,hkl): # returns the closest points to hkl, bearing in mind that if the overlap threshold is sufficiently small then all points within that threshold will be mapped to their rounded point if self.overlap_threshold<=self.rounded_is_closest: return numpy.rint(hkl) else: hkl_dropped=numpy.floor(hkl) # hkl = dropped+remainder. Possibilities: dropped + delta. Distance = delta-remainder? hkl_remainder=hkl%1 #provides a decimal in 0 to 1 adjacents=numpy.array([[0,0,0],[1,0,0],[0,1,0],[1,1,0],[0,0,1],[1,0,1],[0,1,1],[1,1,1]]) distances=numpy.full(numpy.shape(hkl)[0],1000) closest=numpy.full(numpy.shape(hkl),0) for i in numpy.arange(0,8): displacement=-hkl_remainder+adjacents[i] size=numpy.sqrt(numpy.multiply(displacement,numpy.dot(self.metrical_inv,displacement.T).T).sum(1)) distances=numpy.minimum(distances,size) bestLocations=numpy.where(distances==size) closest[bestLocations]=hkl_dropped[bestLocations]+adjacents[i] return closest def find_number_overlaps(self, falsehkl, orthogonalization, threshold): metrical=self.metrical_inv displacement = (falsehkl+0.5)%1-0.5 threshold2=threshold**2 overlaps=0 size=numpy.sqrt(numpy.multiply(displacement,numpy.dot(metrical,displacement.T).T).sum(1))#size=numpy.dot(displacement,numpy.dot(metrical,displacement.T)) for line in displacement: size=numpy.dot(line,numpy.dot(metrical,line)) if sizethreshold2: overlaps-=1 if overlaps<35: return 0 return overlaps def make_hklf5(self, filename, twin_law_full, hkl, fo,sigmas): """ convert hklf4 file to hklf5 file """ #Pulled directly from Pascal's, then edited to generate its own second component hkl, as some files don't have avlues for all twin_law=twin_law_full.hkl_rotation basf=twin_law_full.rbasf[0] cell=self.cell hklf = open(filename,'w') hkl_new = numpy.dot(twin_law, hkl.T).T rounded_hkl_new=self.closest_points(hkl_new).astype(int) scale = 99999/numpy.max(fo) # keep Fo in the scale of F8 threshold=self.cartesian_threshold check1=True for i, hkl_new_i in enumerate(hkl_new): if(numpy.any(numpy.abs(self.closest_points(hkl_new_i)-hkl_new_i)>0.1)): hklf.write("%4d%4d%4d%8d%8d%4d\n"%(hkl[i,0],hkl[i,1], hkl[i,2], fo[i]*scale, sigmas[i]*scale, 1)) continue # looking where is the overlap #diff = numpy.sum(numpy.abs(hkl_new[i] - rounded_hkl_new[i])) diff = numpy.linalg.norm(numpy.dot(self.recip_orth(hkl_new[i] - rounded_hkl_new[i]))) if(diff0.1)): #diff = numpy.sum(numpy.abs(self.closest_points(hkl_new)-hkl_new)) diff = numpy.linalg.norm(numpy.dot(self.recip_orth,(hkl_new_rounded-hkl_new))) if diff0.1)): #diff = numpy.sum(numpy.abs(self.closest_points(hkl_new)-hkl_new)) diff = numpy.linalg.norm(numpy.dot(self.recip_orth(self.closest_points(hkl_new)-hkl_new))) if diff2*self.number_laws: print("Twin laws found on try " + str(i)) twin_laws=self.purge_duplicates(twin_laws) if len(twin_laws)>=self.number_laws: break viable_rotation_points+=[self.find_same_distance_points(vec0,threshold)] viable_rotation_points_0=viable_rotation_points[i] v=vec0 v_size=self.size_of_3d_vector(numpy.dot(recip_orth,v)) #all_v_minus_w=v-viable_rotation_points_0 v_euclid=numpy.dot(recip_orth,v) j=-1 while j1 or abs(cospq_angle)>1: continue vw_angle=numpy.arccos(cosvw_angle) pq_angle=numpy.arccos(cospq_angle) #take positive as anticlockwise and negative as clockwise if abs(vw_angle-pq_angle)<0.10472: #needs to be larger than 0.012 so put at 1 degree (0.0175) for now - changed to 6 degrees () v_w_dir=self.cross_product(v_per,w_per) p_q_dir=self.cross_product(p_per,q_per) if numpy.dot(v_w_dir,p_q_dir)<0: #they rotate in opposite directions continue elif numpy.dot(v_w_dir,unit_axis)<0: vw_angle*=-1 pq_angle*=-1 #the above should work but takes waaaay too long average_angle=(vw_angle+pq_angle)/2 rotation=self.make_real_lattice_rotation(unit_axis, average_angle) rotation_lattice=numpy.dot(orthogonalization_inverse,numpy.dot(rotation,recip_orth)) prot=numpy.dot(rotation_lattice,p) vrot=numpy.dot(rotation_lattice,v) if any(prot-q_scaled>0.1) or any(vrot-w_scaled>0.1): rotation_lattice=numpy.linalg.inv(rotation_lattice) prot=numpy.dot(rotation_lattice,p) vrot=numpy.dot(rotation_lattice,v) if any(prot-q_scaled>0.1) or any(vrot-w_scaled>0.1): continue duplicate=False for twin_law in twin_laws: if numpy.allclose(twin_law.hkl_rotation,rotation_lattice): duplicate=True if duplicate: continue #reciprocal axis rotated_hkl=numpy.dot(hkl,rotation_lattice.T) #if not self.sufficient_overlaps(rotated_hkl, threshold): #continue hkl_displacement=self.find_fom(rotated_hkl) if (hkl_displacement1e-10: twin_laws+=[twin_rules("Alt",numpy.dot(orthogonalization_inverse,unit_axis),rotation_lattice,average_angle,hkl_displacement,threshold,rbasf)] twin_laws=self.do_rounding(twin_laws) twin_laws=self.purge_duplicates(twin_laws) return twin_laws def find_twofold_axes_sphere(self,hkl,threshold=None,size=10): model=self.model twin_laws=[] recip_orth=self.recip_orth metrical_matrix=self.metrical metrical_inverse=self.metrical_inv recip_orth_inv=self.recip_orth_inv number_laws=self.number_laws a_star=recip_orth.T[0] b_star=recip_orth.T[1] c_star=recip_orth.T[2] b_star_cross_c_star=numpy.array(self.cross_product(b_star,c_star)) c_star_cross_a_star=numpy.array(self.cross_product(c_star,a_star)) a_star_cross_b_star=numpy.array(self.cross_product(a_star,b_star)) if not threshold: threshold=self.cartesian_threshold maxRdrop=0 hkl_choice=hkl[:size] viable_rotation_points=[] #for vec in hkl_choice: #viable_rotation_points+=[self.find_same_distance_points(vec,threshold)] #R=I+sintheta K+(1-costheta )K^2 #R=I+2K^2 for i,v in enumerate(hkl_choice): if len(twin_laws)>2*number_laws: print("Twin laws found on try " + str(i)) twin_laws=self.purge_duplicates(twin_laws) if len(twin_laws)>=number_laws: break v_size=self.size_of_3d_vector(numpy.dot(recip_orth,v)) viable_rotation_points+=[self.find_same_distance_points(v,threshold)] viable_rotation_points_0=viable_rotation_points[i] for w in viable_rotation_points_0: if all(w==-v): continue w_scaled=numpy.array(w)*v_size/self.size_of_3d_vector(numpy.dot(recip_orth,w)) axis=v+w_scaled axis_euclid=numpy.dot(recip_orth,axis) axis_unit=axis_euclid/self.size_of_3d_vector(axis_euclid) k=numpy.array([[0,-axis_unit[2],axis_unit[1]],[axis_unit[2],0,-axis_unit[0]],[-axis_unit[1],axis_unit[0],0]]) k2=numpy.dot(k,k) rot_mat_euclid=numpy.eye(3)+2*k2 rotation_lattice=numpy.dot(recip_orth_inv,numpy.dot(rot_mat_euclid,recip_orth)) duplicate=False for twin_law in twin_laws: if numpy.allclose(twin_law.hkl_rotation,rotation_lattice): duplicate=True if duplicate: continue rotated_hkl=numpy.dot(hkl,rotation_lattice.T) #if not self.sufficient_overlaps(rotated_hkl, threshold): #continue hkl_displacement=self.find_fom(rotated_hkl) if (hkl_displacement1e-10 and rbasf[2]>maxRdrop/2: twin_laws+=[twin_rules("Alt",v+w_scaled,rotation_lattice,numpy.pi,hkl_displacement,threshold,rbasf)] maxRdrop=max(maxRdrop,rbasf[2]) twin_laws=self.do_rounding(twin_laws) twin_laws=self.purge_duplicates(twin_laws) return twin_laws def find_same_distance_points(self, hkl, threshold=None): #finds points with the same d-spacing as a reciprocal lattice point hkl model=self.model rec_metrical=model.unit_cell().reciprocal_metrical_matrix() metrical=model.unit_cell().metrical_matrix() orthogonalization=self.recip_orth a_star=orthogonalization[0] b_star=orthogonalization[1] c_star=orthogonalization[2] mod_a_star=numpy.sqrt(rec_metrical[0]) mod_b_star=numpy.sqrt(rec_metrical[1]) mod_c_star=numpy.sqrt(rec_metrical[2]) a_star_dot_b_star=rec_metrical[3] a_star_dot_c_star=rec_metrical[4] b_star_dot_c_star=rec_metrical[5] mod_a_square=metrical[0] mod_b_square=metrical[1] mod_c_square=metrical[2] a_dot_b=metrical[3] a_dot_c=metrical[4] b_dot_c=metrical[5] cos_gam_star=a_star_dot_b_star/(mod_a_star*mod_b_star) cos_alp_star=b_star_dot_c_star/(mod_c_star*mod_b_star) cos_bet_star=a_star_dot_c_star/(mod_a_star*mod_c_star) sin_alp_star=numpy.sqrt(1-cos_alp_star**2) if not threshold: threshold=self.cartesian_threshold size_hkl=self.size_of_3d_vector(numpy.dot(orthogonalization,hkl)) max_distance=size_hkl+threshold min_distance=size_hkl-threshold same_distance_points=[] k_coeff_ext=-mod_a_star*(cos_gam_star-cos_bet_star*cos_alp_star)/(mod_b_star*sin_alp_star**2) k_sqrt_mult=1/(mod_b_star*sin_alp_star) k_coeff_int=mod_a_star**2*(cos_gam_star**2+cos_alp_star**2+cos_bet_star**2-1-2*cos_gam_star*cos_alp_star*cos_bet_star)/sin_alp_star**2 #The initial method to find the ranges was underestimating. Trying a new one. #now changed to squares as that's what my re-do of the maths implies! #h_lambda=[mod_a_square,a_dot_b,a_dot_c] #k_lambda=[a_dot_b,mod_b_square,b_dot_c] #l_lambda=[a_dot_c,b_dot_c,mod_c_square] #lambda_h=1/max_distance*(self.size_of_3d_vector(numpy.dot(orthogonalization,h_lambda))**2) #lambda_k=1/max_distance*(self.size_of_3d_vector(numpy.dot(orthogonalization,k_lambda))**2) #lambda_l=1/max_distance*(self.size_of_3d_vector(numpy.dot(orthogonalization,l_lambda))**2) #try to get the maximum h-value #max_h=numpy.floor(h_lambda[0]/lambda_h) #max_k=numpy.floor(k_lambda[1]/lambda_k) #max_l=numpy.floor(l_lambda[2]/lambda_l) max_h=max_distance*numpy.sqrt(mod_a_square) for h in range(0,int(math.floor(max_h))+1): k_sqrt=k_sqrt_mult*numpy.sqrt(max_distance**2+h**2*k_coeff_int) min_k=k_coeff_ext*h-k_sqrt max_k=k_coeff_ext*h+k_sqrt if h==0: min_k=max(0,min_k) for k in range(int(math.ceil(min_k)),int(math.floor(max_k))+1): discriminant_addition=-(h**2*mod_a_star**2+k**2*mod_b_star**2+2*h*k*a_star_dot_b_star)+(h*a_star_dot_c_star+k*b_star_dot_c_star)**2/mod_c_star**2 constant_l=(h*a_star_dot_c_star+k*b_star_dot_c_star)/mod_c_star**2 upper_discriminant=max_distance**2+discriminant_addition lower_discriminant=min_distance**2+discriminant_addition #still sometimes has upper <0 even when a twin laws is there. Needs work if upper_discriminant<0: continue l_U_max=-constant_l+math.sqrt(upper_discriminant)/mod_c_star#numpy.trunc(-constant_l+math.sqrt(upper_discriminant)/mod_c_star) l_U_min=-constant_l-math.sqrt(upper_discriminant)/mod_c_star#numpy.trunc(-constant_l-math.sqrt(upper_discriminant)/mod_c_star) if k==0: l_U_min=max(0,l_U_min) if lower_discriminant<0: possible_l=list(range(int(math.ceil(l_U_min)),int(math.floor(l_U_max)+1))) else: l_L_max=-constant_l+math.sqrt(lower_discriminant)/mod_c_star#numpy.trunc(-constant_l+math.sqrt(lower_discriminant)/mod_c_star) l_L_min=-constant_l-math.sqrt(lower_discriminant)/mod_c_star#numpy.trunc(-constant_l-math.sqrt(lower_discriminant)/mod_c_star) if k==0: l_L_min=max(0,l_L_min) if l_L_min==0: possible_l=list(range(int(math.ceil(l_L_max)),int(math.floor(l_U_max))+1)) else: possible_l=list(range(int(math.ceil(l_U_min)),int(math.floor(l_L_min))+1))+list(range(int(math.ceil(l_L_max)),int(math.floor(l_U_max))+1)) else: possible_l=list(range(int(math.ceil(l_U_min)),int(math.floor(l_L_min))+1))+list(range(int(math.ceil(l_L_max)),int(math.floor(l_U_max))+1)) for l in possible_l: size_h=self.size_of_3d_vector(numpy.dot(orthogonalization,[h,k,l])) if hkl[0]==h and hkl[1]==k and hkl[2]==l: continue if abs(size_hkl-size_h)1e-15: twin_laws_2+=[twin_law] continue rbasf=self.basf_estimate(twin_law.hkl_rotation) twin_law.rbasf=rbasf if rbasf[0]>1e-15: twin_laws_2+=[twin_law] return twin_laws_2 def cross_product(self,x,y): z=[0,0,0] z[0]=x[1]*y[2]-x[2]*y[1] z[1]=x[2]*y[0]-x[0]*y[2] z[2]=x[0]*y[1]-x[1]*y[0] return z def size_of_3d_vector(self,x): z=0 z+=x[0]*x[0] z+=x[1]*x[1] z+=x[2]*x[2] z=z**0.5 return z def test_law(self,twin_law,threshold): new_hkl=numpy.dot(twin_law,self.bad_hkl) #if self.sufficient_overlaps(new_hkl,threshold)==0: #return None fom=self.find_fom() if fom0.05)): print("Using twin law: %s" %twin_law_disp) print("This is a non-integral twin law, and a corresponding hklf 5 format file has been made.") # non integral twin law, need hklf5 OV.DelIns("TWIN") olx.HKLF(2) OV.DelIns("BASF") OV.AddIns("BASF %f" % basf) if not hklf5name: hklf5name = "%s_twin%02d.hkl" % (OV.HKLSrc().rsplit('\\', 1)[-1].rstrip(".hkl"), int(run_number)) _ = os.path.join(OV.FilePath(), hklf5name) OV.HKLSrc(_) else: print("Using twin law: %s" %twin_law_disp) print("This is an integral twin law, and twinning will be handled by the refinement program.") OV.DelIns("TWIN") olx.HKLF(0) OV.DelIns("BASF") OV.AddIns("BASF %f"%basf) OV.AddIns("TWIN %s" % twin_law_disp_rnd) OV.UpdateHtml() OV.registerFunction(on_twin_image_click) def write_twin_images_to_disk(name, fn_base): l = ['on', 'off'] for state in l: n = '%s%s.png'%(name,state) im_data = OlexVFS.read_from_olex(n) image_name = fn_base+n with open (image_name, 'wb') as wFile: wFile.write(im_data) return image_name def reset_twin_law_img(): global twin_laws_d olex_refinement_model = OV.GetRefinementModel(False) if 'twin' in olex_refinement_model: c = olex_refinement_model['twin']['matrix'] curr_law = [] for row in c: for el in row: curr_law.append(el) for i in range(3): curr_law.append(0.0) curr_law = tuple(curr_law) else: curr_law = (1, 0, 0, 0, 1, 0, 0, 0, 1) for law in twin_laws_d: name = twin_laws_d[law]['name'] matrix = twin_laws_d[law]['matrix'] if curr_law == matrix: OV.CopyVFSFile("%son.png" %name, "%s.png" %name,2) else: OV.CopyVFSFile("%soff.png" %name, "%s.png" %name,2) OV.UpdateHtml() OV.registerFunction(reset_twin_law_img) OlexCctbxTwinLaws_instance = OlexCctbxTwinLaws() #taken from stackoverflow 8391411 class HiddenPrints: def __enter__(self): self._original_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') def __exit__(self, exc_type, exc_val, exc_tb): try: sys.stdout.close() sys.stdout = self._original_stdout except: pass def round_matrix_elements(matrix): round_tol = 0.01 round_val_l = [0,0.25,0.5,0.75,1,0.333,0.666] round_disp_l = ['0','1/4','1/2','3/4','1','1/3','2/3'] round_l = list(zip(round_val_l, round_disp_l)) new_matrix = [] for item in matrix: try: item = round(item,3) except: m.append(item) continue sign = "" if abs(item) != item: sign = "-" if str(item) == "0.0": item = "0" elif str(item) == "1.0": item = "1" elif str(item) == "-1.0": item = "-1" else: for val,disp in round_l: if val-round_tol < abs(item) < val+round_tol: item = sign+disp break new_matrix.append(item) return new_matrix def find_2_fold_olex2(): import gui.tools cmd = "TestR" res = [_f for _f in gui.tools.scrub(cmd) if _f] if "matrix" not in " ".join(res).lower(): return matrix = res[-3:] i = 0 for line in res: if line.startswith('wR2'): basf = res[i+1].split()[2].strip() i += 1 basf = 0.2 r = 0.05 r_diff = -0.03 d = {} d.setdefault(1, {}) matrix = [float(i) for i in " ".join(matrix).split()] d[1]['matrix'] = matrix d[1]['BASF'] = basf d[1]['r'] = r d[1]['r_diff'] = r_diff d[1]['name'] = "law%s" % 1 d[1]['HKLSrc'] = OV.HKLSrc() d[1]['hklf5name'] = "%s.olex2_hklf5.hkl" % OV.FileName() make_twinning_gui(d) OV.registerFunction(find_2_fold_olex2) def make_law_images(twin_law, lawcount): global twin_laws_d from ImageTools import IT # IT = ImageTools() from PilTools import MatrixMaker MM = MatrixMaker() bg_col = OV.GetParam('gui.html.table_bg_colour') bg_col_img = IT.RGBToHTMLColor(IT.adjust_colour(bg_col.rgb,luminosity=1.3)) font_col = "#444444" font_col_basf = "#447744" basf = twin_law['BASF'] r = twin_law['r'] r_diff = twin_law['r_diff'] matrix = twin_law['matrix'] name = "law%s" %lawcount if basf == "n/a": font_col_basf = OV.GetParam('gui.blue').rgb elif float(basf) < 0.1: font_col_basf = OV.GetParam('gui.red').rgb basf = "%.2f" %float(basf) else: font_col_basf = OV.GetParam('gui.green_text').rgb basf = "%.2f" %float(basf) txt = [{'txt':"R=%.2f%%, -%.2f%%" %((float(r)*100),(float(r_diff)*100)), 'font_colour':font_col}, {'txt':"basf=%s" %str(basf), 'font_colour':font_col_basf}] states = ['on', 'off', 'hover', 'down'] rounded_matrix = round_matrix_elements(matrix) for state in states: image_name, img = MM.make_3x3_matrix_image(name, rounded_matrix, txt, state, bar_col=font_col_basf, bgcolor=bg_col_img) twin_laws_d[lawcount].setdefault('law_image_name', image_name) def init_twin_gui(): global twin_laws_d if twin_laws_d: if not twin_laws_d[1]['HKLSrc'] == OV.HKLSrc(): twin_laws_d = {} if not twin_laws_d: import pickle as pickle p = os.path.join(OV.StrDir(), 'twin_laws_d.pickle') if os.path.exists(p): with open(p, "rb") as infile: twin_laws_d = pickle.load(infile) else: twin_laws_d = {} if twin_laws_d: for idx in twin_laws_d: make_law_images(twin_laws_d[idx], len(twin_laws_d)) #for number in twin_laws_d: #law = twin_laws_d[number] #im = law.get('law_image') #OlexVFS.save_image_to_olex(im, "IMG_LAW%s" % number) OV.registerFunction(init_twin_gui, False, 'twin') def make_twinning_gui(laws): font_col = "#444444" font_col_basf = "#447744" bg_col = OV.GetParam('gui.html.table_bg_colour') global twin_laws_d twin_laws_d = laws p = os.path.join(OV.StrDir(), 'twin_laws_d.pickle') import pickle as pickle pickle.dump(twin_laws_d, open(p, "wb")) r_list = [] for count in laws: twin_law_d = laws[count] make_law_images(twin_law_d, count) r_list.append((twin_law_d['r'], count, twin_law_d['BASF'])) history = "" ins_file = "" law_txt = "" i = 0 html = "" %bg_col fn_base = get_twinning_result_filename().rstrip(".html") for r, run, basf in r_list: i += 1 image_name = twin_laws_d[run].get('law_image_name', "XX") #write_twin_images_to_disk(image_name, fn_base) use_image = "%s%soff.png" %(fn_base, image_name) img_src = "%s.png" %image_name name = twin_laws_d[run].get('name', "XX") #href = 'spy.on_twin_image_click(%s)' href = 'spy.on_twin_image_click(%s)>>html.Update' % (i,) law_txt = " " %(href, use_image) d = {'name':image_name, 'nameupper':image_name.upper(), 'tool_img':image_name, 'down':'down', 'up':'up', 'on':'on', 'hover':'hover', 'cmds':href, 'target':"", 'feedback':"", 'bgcolor':"#ff9999", } law_txt = ''' '''%d #self.twin_law_gui_txt += "%s" %(law_txt) control = "IMG_%s" %image_name.upper() html += law_txt #twin_laws_d[lawcount] = {'number':lawcount, #'law':matrix, #'R1':r, #'BASF':basf, #'law_image':img, #'law_txt':law_txt, #'law_image_name':image_name, #'name':name, #'ins_file':f_data, #'history':history, #} #l += 1 html += "" write_twinning_result_to_disk(html) OV.UpdateHtml() def write_twinning_result_to_disk(html): with open(get_twinning_result_filename(), 'w') as wFile: wFile.write(html)