import sys
import olex
import olx
import olex_core
import os
import olexex
import OlexVFS
import gui
from ArgumentParser import ArgumentParser
from History import hist
from olexex import OlexRefinementModel
from olexFunctions import OV, SilentException
debug = OV.IsDebugging()
timer = debug
green = OV.GetParam('gui.green')
red = OV.GetParam('gui.red')
orange = OV.GetParam('gui.orange')
white = "#ffffff"
black = "#000000"
table = OV.GetVar('HtmlTableFirstcolColour')
import ExternalPrgParameters
from CifInfo import MergeCif
import TimeWatch
import time
import shutil
# The listeners expect a function
class ListenerManager(object):
def __init__(self):
self.onStart_listeners = []
self.onEnd_listeners = []
def register_listener(self, listener,event):
if event == "onEnd":
for l in self.onEnd_listeners:
if type(l.__self__) == type(listener.__self__):
return False
self.onEnd_listeners.append(listener)
elif event == "onStart":
for l in self.onStart_listeners:
if type(l.__self__) == type(listener.__self__):
return False
self.onStart_listeners.append(listener)
def startRun(self, caller):
for item in self.onStart_listeners:
item(caller)
def endRun(self, caller):
for item in self.onEnd_listeners:
item(caller)
LM = ListenerManager()
class RunPrg(ArgumentParser):
running = None
def __init__(self):
super(RunPrg, self).__init__()
self.demote = False
self.SPD, self.RPD = ExternalPrgParameters.get_program_dictionaries()
self.terminate = False
self.interrupted = False
self.tidy = False
self.method = None
self.Ralpha = 0
self.Nqual = 0
self.CFOM = 0
self.HasGUI = OV.HasGUI()
self.make_unique_names = False
self.shelx_files = r"%s/util/SHELX/" %self.basedir
self.isAllQ = False #If all atoms are q-peaks, this will be assigned to True
self.his_file = None
self.please_run_auto_vss = False
self.demo_mode = OV.FindValue('autochem_demo_mode',False)
self.broadcast_mode = OV.FindValue('broadcast_mode',False)
if self.demo_mode:
OV.demo_mode = True
if self.HasGUI:
from Analysis import PrgAnalysis
self.PrgAnalysis = PrgAnalysis
OV.registerFunction(self.run_auto_vss,False,'runprg')
self.params = olx.phil_handler.get_python_object()
OV.SetVar('SlideQPeaksVal','0') # reset q peak slider to display all peaks
if not self.filename:
print("No structure loaded")
return
self.original_filename = self.filename
olx.Stop('listen')
self.shelx_alias = OV.FileName().replace(' ', '').lower()
os.environ['FORT_BUFFERED'] = 'TRUE'
self.post_prg_output_html_message = ""
def __del__(self):
if self.method is not None and \
hasattr(self.method, "unregisterCallback") and \
callable(self.method.unregisterCallback):
self.method.unregisterCallback()
def run(self):
import time
import gui
gui.set_notification(OV.GetVar('gui_notification'))
OV.SetVar('gui_notification', "Refining...;%s;%s" %(green,white))
if RunPrg.running:
OV.SetVar('gui_notification', "Already running. Please wait...")
print("Already running. Please wait...")
return
RunPrg.running = self
caught_exception = None
try:
token = TimeWatch.start("Running %s" %self.program.name)
if timer:
t1 = time.time()
res = self.method.run(self)
if timer:
print("REFINEMENT: %.3f" %(time.time() - t1))
if not res:
return False
if not self.method.failure:
if timer:
t1 = time.time()
self.runAfterProcess()
if timer:
print("runAfterProcess: %.3f" %(time.time() - t1))
if timer:
t1 = time.time()
except Exception as e:
e_str = str(e)
if ("stoks.size() == scatterer" not in e_str)\
and ("Error during building of normal equations using OpenMP" not in e_str)\
and ("fsci != sc_map.end()" not in e_str):
sys.stdout.formatExceptionInfo()
else:
print("Error!: ")
caught_exception = e
finally:
self.endRun()
TimeWatch.finish(token)
sys.stdout.refresh = False
sys.stdout.graph = False
if timer:
print("endRun: %.3f" %(time.time() - t1))
RunPrg.running = False
if self.please_run_auto_vss:
self.run_auto_vss()
if caught_exception:
raise SilentException(caught_exception)
def run_auto_vss(self):
OV.paramStack.push('snum.refinement.max_cycles')
OV.paramStack.push('snum.refinement.max_peaks')
olx.Freeze(True)
try:
olex.m('compaq')
olex.m('compaq -a')
olx.VSS(True)
olex.m('compaq')
olex.m('refine 2 10')
olex.m('compaq')
olx.ATA()
olex.m('refine 2 10')
finally:
olx.Freeze(False)
OV.paramStack.pop(2)
def which_shelx(self, type="xl"):
a = olexex.which_program(type)
if a == "":
OV.Alert("Error", "ShelX %s is not found on this system.\nPlease make sure that the ShelX executable files can be found on system PATH." %type, "O")
OV.Cursor()
self.terminate = True
return a
def doBroadcast(self):
ext = "res"
copy_from = "%s/%s.%s" %(self.tempPath, self.shelx_alias, ext)
copy_to = "%s/listen.res" %(self.datadir)
if os.path.isfile(copy_from):
if copy_from.lower() != copy_to.lower():
shutil.copyfile(copy_from, copy_to)
def doFileResInsMagic(self):
import _ac6util
file_lock = OV.createFileLock(os.path.join(self.filePath, self.original_filename))
try:
extensions = ['res', 'lst', 'cif', 'fcf', 'mat', 'pdb', 'lxt']
if "xt" in self.program.name.lower():
extensions.append('hkl')
if self.broadcast_mode:
self.doBroadcast()
for ext in extensions:
if timer:
t = time.time()
if "xt" in self.program.name.lower() and ext != 'lst' and ext != 'lxt':
copy_from = "%s/%s_a.%s" %(self.tempPath, self.shelx_alias, ext)
else:
copy_from = "%s/%s.%s" %(self.tempPath, self.shelx_alias, ext)
copy_to = "%s/%s.%s" %(self.filePath, self.original_filename, ext)
if os.path.isfile(copy_from) and\
copy_from.lower() != copy_to.lower(): # could this ever be true??
digests = None
if copy_from.endswith(".hkl"):
digests = OV.get_AC_digests()
digests = _ac6util.onHKLChange(OV.HKLSrc(), copy_from, digests[0], digests[1])
shutil.copyfile(copy_from, copy_to)
if digests:
digests = digests.split(',')
OV.set_cif_item("_diffrn_oxdiff_ac3_digest_hkl", digests[0])
if len(digests) > 1:
OV.set_cif_item("_diffrn_oxdiff_ac6_digest_hkl_ed", digests[1])
from CifInfo import SaveCifInfo
SaveCifInfo()
if timer:
pass
#print "---- copying %s: %.3f" %(copy_from, time.time() -t)
finally:
OV.deleteFileLock(file_lock)
def doHistoryCreation(self, type="normal"):
if type == "first":
historyPath = "%s/%s.history" %(OV.StrDir(), OV.FileName())
if not os.path.exists(historyPath):
type = 'normal'
if type != "normal":
return
def setupFiles(self):
olx.User("%s" %OV.FilePath())
self.filePath = OV.FilePath()
self.fileName = OV.FileName()
self.tempPath = "%s/temp" %OV.StrDir()
if not os.path.exists(self.tempPath):
os.mkdir(self.tempPath)
## clear temp folder to avoid problems
if 'olex2' not in self.program.name:
old_temp_files = os.listdir(self.tempPath)
for file_n in old_temp_files:
try:
if "_.res" or "_.hkl" not in file_n:
os.remove(r'%s/%s' %(self.tempPath,file_n))
except OSError:
continue
self.hkl_src = OV.HKLSrc()
if not os.path.exists(self.hkl_src):
self.hkl_src = os.path.splitext(OV.FileFull())[0] + '.hkl'
if os.path.exists(self.hkl_src):
OV.HKLSrc(self.hkl_src)
print("HKL Source Filename reset to default file!")
else:
raise Exception("Please choose a reflection file")
self.hkl_src_name = os.path.splitext(os.path.basename(self.hkl_src))[0]
self.curr_file = OV.FileName()
if 'olex2' in self.program.name:
return
if olx.xf.GetIncludedFiles():
files = [(os.path.join(self.filePath, x),os.path.join(self.tempPath, x))
for x in olx.xf.GetIncludedFiles().split('\n')]
else:
files = []
files.append((self.hkl_src,
os.path.join(self.tempPath, self.shelx_alias) + ".hkl"))
files.append((os.path.join(self.filePath, self.curr_file) + ".ins",
os.path.join(self.tempPath, self.shelx_alias) + ".ins"))
files.append((os.path.splitext(self.hkl_src)[0] + ".fab",
os.path.join(self.tempPath, self.curr_file) + ".fab"))
for f in files:
if os.path.exists(f[0]) and not os.path.exists(f[1]):
shutil.copyfile(f[0], f[1])
def runAfterProcess(self):
if 'olex2' not in self.program.name:
if timer:
t = time.time()
self.doFileResInsMagic()
if timer:
print("--- doFilseResInsMagic: %.3f" %(time.time() - t))
if timer:
t = time.time()
if self.HasGUI:
olx.Freeze(True)
OV.reloadStructureAtreap(self.filePath, self.curr_file, update_gui=False)
if self.HasGUI:
olx.Freeze(False)
if timer:
print("--- reloadStructureAtreap: %.3f" %(time.time() - t))
# XT changes the HKL file - so it *will* match the file name
if 'xt' not in self.program.name.lower():
OV.HKLSrc(self.hkl_src)
else:
if self.broadcast_mode:
if timer:
t = time.time()
self.doBroadcast()
if timer:
print("--- doBroacast: %.3f" %(time.time() - t))
lstFile = '%s/%s.lst' %(self.filePath, self.original_filename)
if os.path.exists(lstFile):
os.remove(lstFile)
olx.DelIns("TREF")
if self.params.snum.refinement.auto.max_peaks:
max_peaks = olexex.OlexRefinementModel().getExpectedPeaks()
if max_peaks <= 5:
self.params.snum.refinement.auto.pruneQ = 0.5
self.params.snum.refinement.auto.assignQ = 6.0
OV.SetParam('snum.refinement.auto.pruneQ', 0.5)
OV.SetParam('snum.refinement.auto.assignQ', 6.0)
else:
self.params.snum.refinement.auto.pruneQ = 1.5
self.params.snum.refinement.auto.assignQ = 2.0
OV.SetParam('snum.refinement.auto.pruneQ', 1.5)
OV.SetParam('snum.refinement.auto.assignQ', 2.0)
def getProgramMethod(self, fun):
if fun == 'refine':
self.prgType = prgType = 'refinement'
prgDict = self.RPD
prg = self.params.snum.refinement.program
method = self.params.snum.refinement.method
else:
self.prgType = prgType = 'solution'
prgDict = self.SPD
prg = self.params.snum.solution.program
method = self.params.snum.solution.method
try:
program = prgDict.programs[prg]
except KeyError:
raise Exception("Please choose a valid %s program" %prgType)
try:
prgMethod = program.methods[method]
except KeyError:
raise Exception("Please choose a valid method for the %s program %s" %(prgType, prg))
return program, prgMethod
def startRun(self):
OV.CreateBitmap('%s' %self.bitmap)
LM.startRun(self)
def endRun(self):
self.method.unregisterCallback()
OV.DeleteBitmap('%s' %self.bitmap)
OV.Cursor()
LM.endRun(self)
def post_prg_html(self):
if not OV.HasGUI():
return
import gui.tools
typ = self.prgType.lower()
if typ=='refinement':
return
extra_msg = ""
if typ == "refinement":
extra_msg = "$spy.MakeHoverButton('small-Assign@refinement','ATA(1)')"
elif typ == "solution" and self.program.name.lower() != "shelxt":
extra_msg = gui.tools.TemplateProvider.get_template('run_auto_vss_box', force=debug)
message = "
%s | %s | " %(self.post_prg_output_html_message, extra_msg)
d = {
'program_output_type':"PROGRAM_OUTPUT_%s" %self.prgType.upper(),
'program_output_name':self.program.name,
'program_output_content': message
}
t = gui.tools.TemplateProvider.get_template('program_output', force=debug)%d
f_name = OV.FileName() + "_%s_output.html" %self.prgType
OlexVFS.write_to_olex(f_name, t)
# olx.html.Update()
class RunSolutionPrg(RunPrg):
def __init__(self):
RunPrg.__init__(self)
self.bitmap = 'solve'
self.program, self.method = self.getProgramMethod('solve')
self.run()
def run(self):
if int(olx.xf.au.GetAtomCount()) != 0:
if OV.HasGUI():
if OV.GetParam('user.alert_solve_anyway') == 'Y':
r = OV.Alert("Solve", "Are you sure you want to solve this again?",
'YNIR', "(Don't show this warning again)")
if "R" in r:
OV.SetParam('user.alert_solve_anyway', 'N')
if "Y" not in r: # N/C
self.terminate = True
return
OV.SetParam('snum.refinement.data_parameter_ratio', 0)
OV.SetParam('snum.NoSpherA2.use_aspherical', False)
self.startRun()
OV.SetParam('snum.refinement.auto.invert',True)
if OV.IsFileType('cif'):
OV.Reap('%s/%s.ins' %(self.filepath,self.filename))
self.setupSolve()
if self.terminate: return
self.setupFiles()
if self.terminate:
self.endRun()
self.endHook()
return
if self.params.snum.solution.graphical_output and self.HasGUI:
self.method.observe(self)
RunPrg.run(self)
def runAfterProcess(self):
olx.UpdateWght(0.1)
OV.SetParam('snum.refinement.suggested_weight','0.1 0')
OV.SetParam('snum.refinement.update_weight', False)
RunPrg.runAfterProcess(self)
self.method.post_solution(self)
self.post_prg_html()
self.doHistoryCreation()
OV.SetParam('snum.current_process_diagnostics','solution')
def setupSolve(self):
try:
self.sg = '\'' + olex.f(r'sg(%n)') + '\''
except:
self.sg = ""
self.formula = olx.xf.GetFormula()
if not self.formula:
if self.HasGUI:
import olex_gui
r = olex_gui.GetUserInput(1, "Please enter the structure composition", "")
if not r:
self.terminate = True
return
self.formula = r
else:
print('Please provide the structure composition')
self.terminate = True
if "olex2" not in self.program.name:
self.shelx = self.which_shelx(self.program)
args = self.method.pre_solution(self)
if args:
olex.m('reset ' + args)
else:
olx.Reset()
def doHistoryCreation(self):
OV.SetParam('snum.refinement.last_R1', 'Solution')
OV.SetParam('snum.refinement.last_wR2', 'Solution')
self.his_file = hist.create_history(solution=True)
OV.SetParam('snum.solution.current_history', self.his_file)
return self.his_file
class RunRefinementPrg(RunPrg):
running = None
def __init__(self):
RunPrg.__init__(self)
self.bitmap = 'refine'
self.program, self.method = self.getProgramMethod('refine')
if self.program is None or self.method is None:
return
self.refinement_observer_timer = 0
self.refinement_has_failed = []
OV.registerCallback("procout", self.refinement_observer)
self.run()
OV.unregisterCallback("procout", self.refinement_observer)
if OV.HasGUI():
if self.refinement_has_failed:
bg = red
fg = white
msg = " | ".join(self.refinement_has_failed)
if "warning" in msg.lower():
bg = orange
gui.set_notification("%s;%s;%s" % (msg, bg, fg))
elif OV.GetParam('snum.NoSpherA2.use_aspherical') == False:
gui.get_default_notification(txt="Refinement Finished",
txt_col='green_text')
else:
gui.get_default_notification(
txt="Refinement Finished
Please Cite NoSpherA2: DOI 10.1039/D0SC05526C",
txt_col='green_text')
def reset_params(self):
OV.SetParam('snum.refinement.hooft_str', "")
OV.SetParam('snum.refinement.flack_str', "")
OV.SetParam('snum.refinement.parson_str', "")
def run(self):
if RunRefinementPrg.running:
print("Already running. Please wait...")
return False
RunRefinementPrg.running = self
self.reset_params()
use_aspherical = OV.IsNoSpherA2()
result = False
try:
if use_aspherical == True:
make_fcf_only = OV.GetParam('snum.NoSpherA2.make_fcf_only')
if make_fcf_only == True:
self.make_fcf()
else:
result = self.deal_with_AAFF()
else:
self.startRun()
try:
self.setupRefine()
OV.File("%s/%s.ins" %(OV.FilePath(),self.original_filename))
self.setupFiles()
except Exception as err:
sys.stderr.formatExceptionInfo()
self.endRun()
return False
if self.terminate:
self.endRun()
return
if self.params.snum.refinement.graphical_output and self.HasGUI:
self.method.observe(self)
RunPrg.run(self)
except Exception as err:
sys.stderr.formatExceptionInfo()
self.terminate = True
finally:
if result == False:
self.terminate = True
if use_aspherical == True:
self.refinement_has_failed.append("Error during NoSpherA2")
RunRefinementPrg.running = None
def setupRefine(self):
self.method.pre_refinement(self)
self.shelx = self.which_shelx(self.program)
if self.params.snum.refinement.auto.assignQ:
_ = olexex.get_auto_q_peaks()
self.params.snum.refinement.max_peaks = _
OV.SetParam('snum.refinement.max_peaks', _)
import programSettings
programSettings.onMaxPeaksChange(_)
if olx.LSM().upper() == "CGLS" and olx.Ins("ACTA") != "n/a":
olx.DelIns("ACTA")
def doAutoTidyBefore(self):
olx.Clean('-npd -aq=0.1 -at')
if self.params.snum.refinement.auto.assignQ:
olx.Sel('atoms where xatom.peak>%s' %self.params.snum.refinement.auto.assignQ)
olx.Name('sel C')
if self.params.snum.refinement.auto.pruneU:
i = 0
uref = 0
for i in range(int(olx.xf.au.GetAtomCount())):
ueq = float(olx.xf.au.GetAtomUiso(i))
if uref:
if uref == ueq:
continue
else:
olx.Sel('atoms where xatom.uiso>%s' %self.params.snum.refinement.auto.pruneU)
olx.Kill('sel')
break
else:
uref = ueq
try:
pass
olx.Clean('-npd -aq=0.1 -at')
except:
pass
def doAutoTidyAfter(self):
auto = self.params.snum.refinement.auto
olx.Clean('-npd -aq=0.1 -at')
if self.tidy:
olx.Sel('atoms where xatom.uiso>0.07')
olx.Sel('atoms where xatom.peak<2&&xatom.peak>0')
olx.Kill('sel')
if self.isAllQ:
olx.Sel('atoms where xatom.uiso>0.07')
olx.Kill('sel')
if auto.pruneQ:
olx.Sel('atoms where xatom.peak<%.3f&&xatom.peak>0' %float(auto.pruneQ))
olx.Kill('sel')
#olx.ShowQ('a true') # uncomment me!
#olx.ShowQ('b true') # uncomment me!
if auto.pruneU:
olx.Sel('atoms where xatom.uiso>%s' %auto.pruneU)
olx.Kill('sel')
if auto.assignQ:
olx.Sel('atoms where xatom.peak>%s' %auto.assignQ)
olx.Name('sel C')
olx.Sel('-u')
if auto.assemble == True:
olx.Compaq(a=True)
olx.Move()
else:
pass
olx.Clean('-npd -aq=0.1 -at')
def runAfterProcess(self):
RunPrg.runAfterProcess(self)
if timer:
t = time.time()
self.method.post_refinement(self)
if timer:
print("-- self.method.post_refinement(self): %.3f" %(time.time()-t))
delete_stale_fcf()
if timer:
t = time.time()
self.post_prg_html()
self.doHistoryCreation()
if timer:
print("-- self.method.post_refinement(self): %3f" %(time.time()-t))
if self.R1 == 'n/a':
return
if self.params.snum.refinement.auto.tidy:
self.doAutoTidyAfter()
OV.File()
if OV.GetParam('snum.refinement.check_absolute_structure_after_refinement') and\
not OV.IsEDRefinement():
try:
self.isInversionNeeded(force=self.params.snum.refinement.auto.invert)
except Exception as e:
print("Could not determine whether structure inversion is needed: %s" % e)
if self.program.name == 'olex2.refine':
if OV.GetParam('snum.refinement.check_PDF'):
try:
self.check_PDF(force=self.params.snum.refinement.auto.remove_anharm)
except Exception as e:
print("Could not check PDF: %s" % e)
self.check_disp()
self.check_occu()
self.check_mu() #This is the L-M mu!
OV.SetParam('snum.init.skip_routine', False)
OV.SetParam('snum.current_process_diagnostics','refinement')
if timer:
t = time.time()
if self.params.snum.refinement.cifmerge_after_refinement:
try:
MergeCif(edit=False, force_create=False, evaluate_conflicts=False)
except Exception as e:
if debug:
sys.stdout.formatExceptionInfo()
print("Failed in CifMerge: '%s'" %str(e))
if timer:
print("-- MergeCif: %.3f" %(time.time()-t))
def refinement_observer(self, msg):
if self.refinement_observer_timer == 0:
self.refinement_observer_timer = time.time()
#if time.time() - self.refinement_observer_timer < 2:
#return
if "BAD AFIX CONNECTIVITY" in msg or "ATOM FOR AFIX" in msg:
self.refinement_has_failed.append("Hydrogens")
elif "REFINEMNET UNSTABLE" in msg:
self.refinement_has_failed.append("Unstable")
elif "???????" in msg:
self.refinement_has_failed.append("ShelXL Crashed!")
elif "** " in msg:
import re
regex = re.compile(r"\*\*(.*?)\*\*")
m = regex.findall(msg)
if m:
self.refinement_has_failed.append(m[0].strip())
def doHistoryCreation(self):
R1 = 0
self.his_file = ""
wR2 = 0
if olx.IsVar('cctbx_R1') == 'true':
R1 = float(olx.GetVar('cctbx_R1'))
olx.UnsetVar('cctbx_R1')
wR2 = float(olx.GetVar('cctbx_wR2'))
olx.UnsetVar('cctbx_wR2')
else:
try:
R1 = float(olx.Lst('R1'))
wR2 = float(olx.Lst('wR2'))
except:
pass
if R1:
OV.SetParam('snum.refinement.last_R1', str(R1))
OV.SetParam('snum.refinement.last_wR2',wR2)
if not self.params.snum.init.skip_routine:
try:
self.his_file = hist.create_history()
except Exception as ex:
sys.stderr.write("History could not be created\n")
if debug:
sys.stderr.formatExceptionInfo()
else:
print ("Skipping History")
self.R1 = R1
self.wR2 = wR2
else:
self.R1 = self.wR2 = "n/a"
self.his_file = None
print("The refinement has failed, no R value was returned by the refinement")
OV.SetParam('snum.refinement.current_history', self.his_file)
return self.his_file, self.R1
def isInversionNeeded(self, force=False):
if self.params.snum.init.skip_routine:
print ("Skipping absolute structure validation")
return
if olex_core.SGInfo()['Centrosymmetric'] == 1: return
from libtbx.utils import format_float_with_standard_uncertainty
from cctbx import sgtbx
if debug:
print("Checking absolute structure...")
inversion_needed = False
possible_racemic_twin = False
inversion_warning = "WARNING: Structure should be inverted (inv -f), unless there is a good reason not to do so."
racemic_twin_warning = "WARNING: Structure may be an inversion twin"
output = []
flack = OV.GetParam('snum.refinement.flack_str')
# check if the nversion twin refinement...
if not flack:
from cctbx.array_family import flex
rm = olexex.OlexRefinementModel()
twinning = rm.model.get('twin')
if twinning is not None:
twin_law = sgtbx.rot_mx([int(twinning['matrix'][j][i])
for i in range(3) for j in range(3)])
if twin_law.as_double() == sgtbx.rot_mx((-1,0,0,0,-1,0,0,0,-1)):
flack = olx.xf.rm.BASF(0)
OV.SetParam('snum.refinement.flack_str', flack)
parson = OV.GetParam('snum.refinement.parson_str')
hooft = self.method.getHooft()
if hooft and hasattr(hooft, 'p3_racemic_twin'):
if (hooft.p3_racemic_twin is not None and
round(hooft.p3_racemic_twin, 3) == 1):
possible_racemic_twin = True
elif hooft.p2_false is not None and round(hooft.p2_false, 3) == 1:
inversion_needed = True
s = format_float_with_standard_uncertainty(
hooft.hooft_y, hooft.sigma_y)
output.append("Hooft y: %s" %s)
elif flack or parson:
value = parson
if not value:
value = flack
fs = value.split("(")
val = float(fs[0])
if val != 0:
error = float(fs[1][:-1])
temp = val
while abs(temp) < 1.0:
temp *= 10
error /= 10
if val > 0.8 and val-error > 0.5:
inversion_needed = True
if parson:
output.append("Parson's q: %s" %parson)
if flack:
output.append("Flack x: %s" %flack)
print(', '.join(output))
if force and inversion_needed:
olex.m('Inv -f')
OV.File('%s.res' %OV.FileName())
OV.SetParam('snum.refinement.auto.invert',False)
print("The Structure has been inverted")
elif inversion_needed:
print(inversion_warning)
if possible_racemic_twin:
if (hooft.olex2_adaptor.twin_components is not None and
hooft.olex2_adaptor.twin_components[0].twin_law.as_double() != sgtbx.rot_mx((-1,0,0,0,-1,0,0,0,-1))):
print(racemic_twin_warning)
def check_PDF(self, force=False):
RM = OlexRefinementModel()
any_have_anh = False
label_list = []
for i, atom in enumerate(RM._atoms):
anh_adp = atom.get('anharmonic_adp')
if anh_adp == None:
continue
any_have_anh = True
label_list.append(atom['label'])
if any_have_anh == True:
olex.m("PDF")
problem = OV.GetVar("Negative_PDF")
Kuhs = OV.GetVar("Kuhs_Rule")
err_list = []
if problem == True:
err_list.append("Negative PDF found")
if force == True:
print("Making all anharmonic atoms hamrnoic again!")
for label in label_list:
print(label)
olex.m("anis %s" % label)
if Kuhs == True:
err_list.append("Kuhs' rule not fulfilled")
if err_list:
self.refinement_has_failed.extend(err_list)
def check_occu(self):
scatterers = self.cctbx.normal_eqns.xray_structure.scatterers()
wrong_occu = []
for sc in scatterers:
if sc.flags.grad_occupancy():
if sc.occupancy < 0 or sc.occupancy > 1.0:
wrong_occu.append(sc.label)
if len(wrong_occu) != 0:
if len(wrong_occu) == 1:
self.refinement_has_failed.append(f"{wrong_occu[0]} has unreasonable Occupancy")
else:
_ = ",".join(wrong_occu)
self.refinement_has_failed.append(f"{_} have unreasonable Occupancy")
def check_disp(self):
scatterers = self.cctbx.normal_eqns.xray_structure.scatterers()
refined_disp = []
for sc in scatterers:
if sc.flags.grad_fp() or sc.flags.grad_fdp():
fp, fdp = sc.fp, sc.fdp
refined_disp.append((sc, fp, fdp))
if refined_disp != []:
wavelength = float(olx.xf.exptl.Radiation())
from cctbx.eltbx import sasaki
from cctbx.eltbx import henke
from brennan import brennan
tables = [sasaki, henke, brennan()]
unreasonable_fp = []
unreasonable_fdp = []
for sc, fp, fdp in refined_disp:
e = str(sc.element_symbol())
table = []
for t in tables:
table.append(t.table(e))
fp_min_max = [135.0, 0.0]
fdp_min_max = [135.0, 0.0]
fp_average = 0.0
fdp_average = 0.0
for t in table:
temp = t.at_angstrom(wavelength)
fp_average += temp.fp()
fdp_average += temp.fdp()
fp_min_max = [min(fp_min_max[0], temp.fp()), max(fp_min_max[1], temp.fp())]
fdp_min_max = [min(fdp_min_max[0], temp.fdp()), max(fdp_min_max[1], temp.fdp())]
fp_average /= len(tables)
fdp_average /= len(tables)
fpdiff = (fp_min_max[1] - fp_min_max[0])
fdpdiff = (fdp_min_max[1] - fdp_min_max[0])
if fp_average + 2 * fpdiff < fp or fp_average - 2 * fpdiff > fp:
unreasonable_fp.append(sc.label)
if fdp_average + 2 * fdpdiff < fdp or fdp_average - 2 * fdpdiff > fdp:
unreasonable_fdp.append(sc.label)
if len(unreasonable_fdp) != 0:
if len(unreasonable_fdp) == 1:
self.refinement_has_failed.append("%s has strongly deviating f''" % unreasonable_fdp[0])
else:
self.refinement_has_failed.append("%s have strongly deviating f''" % ",".join(unreasonable_fdp))
if len(unreasonable_fp) != 0:
if len(unreasonable_fp) == 1:
self.refinement_has_failed.append("%s has strongly deviating f'" % unreasonable_fp[0])
else:
self.refinement_has_failed.append("%s have strongly deviating f'" % ",".join(unreasonable_fp))
def check_mu(self):
try:
mu = self.cctbx.normal_eqns.iterations_object.mu
if mu > 1E1:
self.refinement_has_failed.append("Mu of LM is very large!")
except AttributeError:
return
def make_fcf(self):
from refinement import FullMatrixRefine
table = str(OV.GetParam('snum.NoSpherA2.file'))
self.startRun()
try:
self.setupRefine()
OV.File("%s/%s.ins" %(OV.FilePath(),self.original_filename))
self.setupFiles()
except Exception as err:
sys.stderr.formatExceptionInfo()
print(err)
self.endRun()
return False
if self.terminate:
self.endRun()
return
if self.params.snum.refinement.graphical_output and self.HasGUI:
self.method.observe(self)
FM = FullMatrixRefine(
max_cycles=0,
max_peaks=1)
ne = FM.run(False,table)
fcf_cif, fmt_str = FM.create_fcf_content(list_code = 6)
with open(OV.file_ChangeExt(OV.FileFull(), 'fcf'), 'w') as f:
fcf_cif.show(out=f, loop_format_strings={'_refln':fmt_str})
return
def deal_with_AAFF(self):
HAR_log = None
try:
from cctbx import adptbx
Full_HAR = OV.GetParam('snum.NoSpherA2.full_HAR')
old_model = OlexRefinementModel()
converged = False
run = 0
HAR_log = open("%s/%s.NoSpherA2" %(OV.FilePath(),self.original_filename),"w")
HAR_log.write("NoSpherA2 in Olex2 for structure %s\n\n" %(OV.ModelSrc()))
import datetime
HAR_log.write("Refinement startet at: ")
HAR_log.write(str(datetime.datetime.now())+"\n")
HAR_log.write("Cycle SCF Energy Max shift: xyz/ESD Label Uij/ESD Label Max/ESD Label R1 wR2\n"+"*" * 110 + "\n")
HAR_log.write("{:3d}".format(run))
energy = None
source = OV.GetParam('snum.NoSpherA2.source')
if "Please S" in source:
olx.Alert("No tsc generator selected",\
"""Error: No generator for tsc files selected.
Please select one of the generators from the drop-down menu.""", "O", False)
OV.SetVar('NoSpherA2-Error',"TSC Generator unselected")
return
if energy == None:
HAR_log.write("{:^24}".format(" "))
else:
HAR_log.write("{:^24.10f}".format(energy))
HAR_log.write("{:>70}".format(" "))
r1_old = OV.GetParam('snum.refinement.last_R1')
wr2_old = OV.GetParam('snum.refinement.last_wR2')
if r1_old != "n/a" and r1_old != None:
HAR_log.write("{:>6.2f}".format(float(r1_old) * 100))
else:
HAR_log.write("{:>6}".format("N/A"))
if wr2_old != "n/a" and wr2_old != None:
HAR_log.write("{:>7.2f}".format(float(wr2_old) * 100))
else:
HAR_log.write("{:>7}".format("N/A"))
HAR_log.write("\n")
HAR_log.flush()
max_cycles = int(OV.GetParam('snum.NoSpherA2.Max_HAR_Cycles'))
calculate = OV.GetParam('snum.NoSpherA2.Calculate')
if calculate == True:
if OV.GetParam('snum.NoSpherA2.h_aniso') == True:
olx.Anis("$H", h=True)
if OV.GetParam('snum.NoSpherA2.h_afix') == True:
olex.m("Afix 0 $H")
add_disp = OV.GetParam('snum.NoSpherA2.add_disp')
if add_disp is True:
olex.m('gendisp -source=brennan -force')
while converged == False:
run += 1
HAR_log.write("{:3d}".format(run))
old_model = OlexRefinementModel()
OV.SetVar('Run_number', run)
self.refinement_has_failed = []
#Calculate Wavefunction
try:
from NoSpherA2.NoSpherA2 import NoSpherA2_instance as nsp2
v = nsp2.launch()
if v == False:
print("Error during NoSpherA2! Abnormal Ending of program!")
HAR_log.close()
return False
except NameError as error:
print("Error during NoSpherA2:")
print(error)
RunRefinementPrg.running = None
RunRefinementPrg.Terminate = True
HAR_log.close()
return False
Error_Status = OV.GetVar('NoSpherA2-Error')
if Error_Status != "None":
print("Error in NoSpherA2: %s" %Error_Status)
return False
tsc_exists = False
wfn_file = None
for file in os.listdir(olx.FilePath()):
if file == os.path.basename(OV.GetParam('snum.NoSpherA2.file')):
tsc_exists = True
elif file.endswith(".wfn"):
wfn_file = file
elif file.endswith(".wfx"):
wfn_file = file
elif file.endswith(".gbw"):
wfn_file = file
elif file.endswith(".tscb"):
tsc_exists = True
if tsc_exists == False:
print("Error during NoSpherA2: No .tsc file found")
RunRefinementPrg.running = None
HAR_log.close()
return False
# get energy from wfn file
#TODO Check if WFN is new, otherwise skip this!
energy = None
if source == "fragHAR" or source == "Hybdrid" or source == "DISCAMB" or "MATTS" in source or "hakkar" in source:
HAR_log.write("{:24}".format(" "))
else:
if (wfn_file != None) and (calculate == True):
if ".gbw" not in wfn_file:
with open(wfn_file, "rb") as f:
f.seek(-2000, os.SEEK_END)
fread = f.readlines()[-1].decode()
if "THE VIRIAL" in fread:
source = OV.GetParam('snum.NoSpherA2.source')
if "Gaussian" in source:
energy = float(fread.split()[3])
elif "ORCA" in source:
energy = float(fread.split()[4])
elif "pySCF" in source:
energy = float(fread.split()[4])
elif ".wfn" in source:
energy = float(fread[17:38])
elif "Tonto" in source:
energy = float(fread.split()[4])
else:
energy = 0.0
if energy is not None:
HAR_log.write("{:^24.10f}".format(energy))
else:
HAR_log.write("{:24}".format(" "))
fread = None
else:
HAR_log.write("{:24}".format(" "))
if OV.GetParam('snum.NoSpherA2.run_refine') == True:
# Run Least-Squares
self.startRun()
try:
self.setupRefine()
OV.File("%s/%s.ins" %(OV.FilePath(),self.original_filename))
self.setupFiles()
except Exception as err:
sys.stderr.formatExceptionInfo()
print(err)
self.endRun()
HAR_log.close()
return False
if self.terminate:
self.endRun()
return
if self.params.snum.refinement.graphical_output and self.HasGUI:
self.method.observe(self)
try:
RunPrg.run(self)
f_obs_sq,f_calc = self.cctbx.get_fo_sq_fc(self.cctbx.normal_eqns.one_h_linearisation)
if f_obs_sq != None and f_calc != None:
nsp2.set_f_calc_obs_sq_one_h_linearisation(f_calc,f_obs_sq,self.cctbx.normal_eqns.one_h_linearisation)
except Exception as e:
e_str = str(e)
if ("stoks.size() == scatterer" in e_str):
print("Insufficient number of scatterers in .tsc file!\nDid you forget to recalculate after adding or deleting atoms?")
elif ("Error during building of normal equations using OpenMP" in e_str):
print("Error initializing OpenMP refinement, try disabling it!")
elif ("fsci != sc_map.end()" in e_str):
print("An Atom was not found in the .tsc file!\nHave you renamed some and not recalcualted the tsc file?")
return
else:
break
new_model=OlexRefinementModel()
class results():
def __init__(self):
self.max_dxyz = 0
self.max_duij = 0
self.label_uij = None
self.label_xyz = None
self.r1 = 0
self.wr2 = 0
self.max_overall = 0
self.label_overall = None
def update_xyz(self, dxyz, label):
if dxyz > self.max_dxyz:
self.max_dxyz = dxyz
self.label_xyz = label
if dxyz > self.max_overall:
self.max_overall = dxyz
self.label_overall = label
def update_uij(self, duij, label):
if duij > self.max_duij:
self.max_duij = duij
self.label_uij = label
if duij > self.max_overall:
self.max_overall = duij
self.label_overall = label
def update_overall(self, d, label):
if d > self.max_overall:
self.max_overall = d
self.label_overall = label
try:
jac_tr = self.cctbx.normal_eqns.reparametrisation.jacobian_transpose_matching_grad_fc()
from scitbx.array_family import flex
cov_matrix = flex.abs(flex.sqrt(self.cctbx.normal_eqns.covariance_matrix().matrix_packed_u_diagonal()))
esds = jac_tr.transpose() * flex.double(cov_matrix)
jac_tr = None
annotations = self.cctbx.normal_eqns.reparametrisation.component_annotations
except:
HAR_log.close()
print ("Could not obtain cctbx object and calculate ESDs!\n")
return False
from NoSpherA2.utilities import run_with_bitmap
@run_with_bitmap('Analyzing shifts')
def analyze_shifts(results):
try:
matrix_run = 0
matrix_size = len(esds)
uc = self.cctbx.normal_eqns.xray_structure.unit_cell()
for i, atom in enumerate(new_model._atoms):
xyz = atom['crd'][0]
xyz2 = old_model._atoms[i]['crd'][0]
assert matrix_run + 2 < matrix_size, "Inconsistent size of annotations and expected parameters!"
if ".x" in annotations[matrix_run]:
for x in range(3):
# if parameter is fixed and therefore has 0 esd
if esds[matrix_run] > 0:
res = abs(xyz[x] - xyz2[x]) / esds[matrix_run]
if res > results.max_dxyz:
results.update_xyz(res, annotations[matrix_run])
matrix_run += 1
has_adp_new = atom.get('adp')
has_adp_old = old_model._atoms[i].get('adp')
has_anh_new = atom.get('anharmonic_adp')
has_anh_old = old_model._atoms[i].get('anharmonic_adp')
if has_adp_new != None and has_adp_old != None:
assert matrix_run + 5 < matrix_size, "Inconsistent size of annotations and expected parameters!"
adp = atom['adp'][0]
adp = (adp[0], adp[1], adp[2], adp[5], adp[4], adp[3])
adp2 = old_model._atoms[i]['adp'][0]
adp2 = (adp2[0], adp2[1], adp2[2], adp2[5], adp2[4], adp2[3])
adp = adptbx.u_cart_as_u_cif(uc, adp)
adp2 = adptbx.u_cart_as_u_cif(uc, adp2)
adp_esds = (esds[matrix_run],
esds[matrix_run + 1],
esds[matrix_run + 2],
esds[matrix_run + 3],
esds[matrix_run + 4],
esds[matrix_run + 5])
adp_esds = adptbx.u_star_as_u_cif(uc, adp_esds)
for u in range(6):
# if parameter is fixed and therefore has 0 esd
if adp_esds[u] > 0:
res = abs(adp[u] - adp2[u]) / adp_esds[u]
if res > results.max_duij:
results.update_uij(res, annotations[matrix_run + u])
matrix_run += 6
if matrix_run < len(annotations):
if has_anh_new != None and has_anh_old != None:
assert matrix_run + 24 < matrix_size, "Inconsistent size of annotations and expected parameters!"
adp_C = atom['anharmonic_adp']['C']
adp2_C = old_model._atoms[i]['anharmonic_adp']['C']
adp_esds_C = (esds[matrix_run:matrix_run + 10])
adp_D = atom['anharmonic_adp']['D']
adp2_D = old_model._atoms[i]['anharmonic_adp']['D']
adp_esds_D = (esds[matrix_run + 10:matrix_run + 25])
for u in range(10):
# if parameter is fixed and therefore has 0 esd
if adp_esds_C[u] > 0:
res = abs(adp_C[u] - adp2_C[u]) / adp_esds_C[u]
if res > results.max_overall:
results.update_overall(res, annotations[matrix_run + u])
for u in range(14):
# if parameter is fixed and therefore has 0 esd
if adp_esds_D[u] > 0:
res = abs(adp_D[u] - adp2_D[u]) / adp_esds_D[u]
if res > results.max_overall:
results.update_overall(res, annotations[matrix_run + u + 10])
matrix_run += 25
elif has_adp_new != None and has_adp_old == None:
assert matrix_run + 5 < matrix_size, "Inconsistent size of annotations and expected parameters!"
adp = atom['uiso'][0]
adp2 = adptbx.u_cart_as_u_cif(uc, new_model._atoms[i]['adp'][0])
adp_esds = (esds[matrix_run], esds[matrix_run + 1], esds[matrix_run + 2], esds[matrix_run + 3], esds[matrix_run + 4], esds[matrix_run + 5])
adp_esds = adptbx.u_star_as_u_cif(uc, adp_esds)
for u in range(6):
if esds[matrix_run] > 0:
res = abs(adp - adp2[u]) / adp_esds[u]
if res > results.max_duij:
results.update_uij(res, annotations[matrix_run])
matrix_run += 6
if matrix_run < len(annotations):
if ".C111" in annotations[matrix_run]:
matrix_run += 25
elif has_adp_old == None and has_adp_new == None:
if (i != len(new_model._atoms) - 1):
assert matrix_run < matrix_size, "Inconsistent size of annotations and expected parameters!"
adp = atom['uiso'][0]
adp2 = old_model._atoms[i]['uiso'][0]
adp_esd = esds[matrix_run]
if esds[matrix_run] > 0:
res = abs(adp - adp2) / adp_esd
if res > results.max_duij:
results.update_uij(res, annotations[matrix_run])
matrix_run += 1
if matrix_run < len(annotations):
if 'occ' in annotations[matrix_run]:
matrix_run += 1
HAR_log.write("{:>16.4f}".format(results.max_dxyz))
if results.label_xyz != None:
HAR_log.write("{:>10}".format(results.label_xyz))
else:
HAR_log.write("{:>10}".format("N/A"))
HAR_log.write("{:>10.4f}".format(results.max_duij))
if results.label_uij != None:
HAR_log.write("{:>12}".format(results.label_uij))
else:
HAR_log.write("{:>12}".format("N/A"))
HAR_log.write("{:>10.4f}".format(results.max_overall))
if results.label_overall != None:
HAR_log.write("{:>12}".format(results.label_overall))
else:
HAR_log.write("{:>12}".format("N/A"))
results.r1 = OV.GetParam('snum.refinement.last_R1')
results.wr2 = OV.GetParam('snum.refinement.last_wR2')
HAR_log.write("{:>6.2f}".format(float(results.r1) * 100))
HAR_log.write("{:>7.2f}".format(float(results.wr2) * 100))
HAR_log.write("\n")
HAR_log.flush()
except Exception as e:
HAR_log.write("!!!ERROR!!!\n")
HAR_log.close()
print("Error during analysis of shifts!")
raise e
r = results()
analyze_shifts(r)
if calculate == False:
converged = True
break
elif Full_HAR == False:
converged = True
break
elif (r.max_overall <= 0.01):
converged = True
break
elif run == max_cycles:
break
elif r1_old != "n/a":
if (float(r.r1) > float(r1_old) + 0.1) and (run > 1):
HAR_log.write(" !! R1 increased by more than 0.1, aborting before things explode !!\n")
self.refinement_has_failed.append("Error: R1 is not behaving nicely! Stopping!")
break
else:
r1_old = r.r1
wr2_old = r.wr2
except Exception as e :
if HAR_log != None:
HAR_log.close()
raise e
# Done with the while !Converged
OV.SetParam('snum.NoSpherA2.Calculate',False)
if converged == False:
HAR_log.write(" !!! WARNING: UNCONVERGED MODEL! PLEASE INCREASE MAX_CYCLE OR CHECK FOR MISTAKES !!!\n")
self.refinement_has_failed.append("Warning: Unconverged Model!")
if "DISCAMB" in source or "MATTS" in source:
unknown_sources = False
fn = os.path.join("olex2","Wfn_job","discambMATTS2tsc.log")
if not os.path.exists(fn):
fn = os.path.join("olex2","Wfn_job","discamb2tsc.log")
if not os.path.exists(fn):
HAR_log.write(" !!! WARNING: No output file found! !!!\n")
self.refinement_has_failed.append("Output file not found!")
else:
with open(fn) as discamb_log:
for i in discamb_log.readlines():
if "unassigned atom types" in i:
unknown_sources = True
if unknown_sources == True:
HAR_log.write(i)
if unknown_sources == True:
HAR_log.write(" !!! WARNING: Unassigned Atom Types! !!!\n")
self.refinement_has_failed.append("Unassigned Atom Types!")
HAR_log.write("*" * 110 + "\n")
HAR_log.write("Residual density Max:{:+8.3f}\n".format(OV.GetParam('snum.refinement.max_peak')))
HAR_log.write("Residual density Min:{:+8.3f}\n".format(OV.GetParam('snum.refinement.max_hole')))
HAR_log.write("Residual density RMS:{:+8.3f}\n".format(OV.GetParam('snum.refinement.res_rms')))
HAR_log.write("Goodness of Fit: {:8.4f}\n".format(OV.GetParam('snum.refinement.goof')))
HAR_log.write("Refinement finished at: ")
HAR_log.write(str(datetime.datetime.now()))
HAR_log.write("\n")
precise = OV.GetParam('snum.NoSpherA2.precise_output')
if precise == True:
olex.m("spy.NoSpherA2.write_precise_model_file()")
HAR_log.flush()
HAR_log.close()
with open("%s/%s.NoSpherA2" %(OV.FilePath(),self.original_filename), 'r') as f:
print(f.read())
return True
def AnalyseRefinementSource():
file_name = OV.ModelSrc()
ins_file_name = olx.file.ChangeExt(file_name, 'ins')
res_file_name = olx.file.ChangeExt(file_name, 'res')
hkl_file_name = olx.file.ChangeExt(file_name, 'hkl')
if olx.IsFileType('cif') == 'true':
if os.path.exists(ins_file_name) or os.path.exists(res_file_name):
olex.m('reap "%s"' %ins_file_name)
hkl_file_name = os.path.join(os.getcwd(), hkl_file_name)
if os.path.exists(hkl_file_name):
olx.HKLSrc(hkl_file_name)
return True
else:
return False
fn = os.path.normpath("%s/%s" %(olx.FilePath(), olx.xf.DataName(olx.xf.CurrentData())))
ins_file_name = fn + '.ins'
res_file_name = fn + '.res'
hkl_file_name = fn + '.hkl'
olex.m("export '%s'" %hkl_file_name)
if os.path.exists(res_file_name):
olex.m('reap "%s"' %res_file_name)
print('Loaded RES file extracted from CIF')
else:
OV.File("%s" %ins_file_name)
olex.m('reap "%s"' %ins_file_name)
olex.m("free xyz,Uiso")
print('Loaded INS file generated from CIF')
if os.path.exists(hkl_file_name):
olx.HKLSrc(hkl_file_name)
else:
print('HKL file is not in the CIF')
return False
return True
OV.registerFunction(AnalyseRefinementSource)
OV.registerFunction(RunRefinementPrg)
OV.registerFunction(RunSolutionPrg)
def delete_stale_fcf():
fcf = os.path.join(OV.FilePath(), OV.FileName() + '.fcf')
res = os.path.join(OV.FilePath(), OV.FileName() + '.res')
if os.path.exists(res) and os.path.exists(fcf):
diff = abs(os.path.getmtime(fcf) - os.path.getmtime(res))
# modified within 10 seconds
if diff < 10:
return False
else:
os.remove(fcf)
print("Deleted stale fcf: %s (%ss old)" %(fcf, int(diff)))
if OV.HasGUI():
import gui
gui.set_notification("Stalefcf filehas been deleted.")
return True