1 #!/bin/env python 2 # File: Psi4Util.py 3 # Author: Manish Sud <msud@san.rr.com> 4 # 5 # Copyright (C) 2024 Manish Sud. All rights reserved. 6 # 7 # The functionality available in this file is implemented using Psi4, an open 8 # source quantum chemistry software package. 9 # 10 # This file is part of MayaChemTools. 11 # 12 # MayaChemTools is free software; you can redistribute it and/or modify it under 13 # the terms of the GNU Lesser General Public License as published by the Free 14 # Software Foundation; either version 3 of the License, or (at your option) any 15 # later version. 16 # 17 # MayaChemTools is distributed in the hope that it will be useful, but without 18 # any warranty; without even the implied warranty of merchantability of fitness 19 # for a particular purpose. See the GNU Lesser General Public License for more 20 # details. 21 # 22 # You should have received a copy of the GNU Lesser General Public License 23 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 24 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 25 # Boston, MA, 02111-1307, USA. 26 # 27 28 from __future__ import print_function 29 30 import os 31 import sys 32 import re 33 import glob 34 35 import MiscUtil 36 37 __all__ = ["CalculateSinglePointEnergy", "InitializePsi4", "JoinMethodNameAndBasisSet", "ListPsi4RunParamaters", "RetrieveIsocontourRangeFromCubeFile", "RetrieveMinAndMaxValueFromCubeFile", "PerformGeometryOptimization", "ProcessPsi4ConstrainTorrionsParameters", "ProcessPsi4CubeFilesParameters", "ProcessPsi4OptionsParameters", "ProcessPsi4RunParameters", "ProcessPsi4DDXSolvationParameters", "RemoveScratchFiles", "SetupPsi4DDXSolvationOptions", "UpdatePsi4OptionsParameters", "UpdatePsi4RunParameters", "UpdatePsi4OutputFileUsingPID"] 38 39 def InitializePsi4(Psi4RunParams = None, Psi4OptionsParams = None, PrintVersion = False, PrintHeader = False): 40 """Import Psi4 module and configure it for running Psi4 jobs. 41 42 Arguments: 43 Psi4RunParams (dict): Runtime parameter name and value pairs. 44 Psi4OptionsParams (dict): Option name and value pairs. This is simply 45 passed to ps4.set_options(). 46 PrintVersion (bool): Print version number. 47 PrintHeader (bool): Print header information. 48 49 Returns: 50 Object: Psi4 module reference. 51 52 """ 53 54 # Import Psi4... 55 try: 56 import psi4 57 except ImportError as ErrMsg: 58 sys.stderr.write("\nFailed to import Psi4 module/package: %s\n" % ErrMsg) 59 sys.stderr.write("Check/update your Psi4 environment and try again.\n\n") 60 sys.exit(1) 61 62 Psi4Handle = psi4 63 64 if PrintVersion: 65 MiscUtil.PrintInfo("Importing Psi4 module (Psi4 v%s)...\n" % (Psi4Handle.__version__)) 66 67 # Update Psi4 run paramaters... 68 if Psi4RunParams is not None: 69 UpdatePsi4RunParameters(Psi4Handle, Psi4RunParams) 70 71 # Update Psi4 options... 72 if Psi4OptionsParams is not None: 73 UpdatePsi4OptionsParameters(Psi4Handle, Psi4OptionsParams) 74 75 # Print header after updating Psi4 run parameters... 76 if PrintHeader: 77 Psi4Handle.print_header() 78 79 return Psi4Handle 80 81 def CalculateSinglePointEnergy(psi4, Molecule, Method, BasisSet, ReturnWaveFunction = False, Quiet = False): 82 """Calculate single point electronic energy in Hartrees using a specified 83 method and basis set. 84 85 Arguments: 86 psi4 (Object): Psi4 module reference. 87 Molecule (Object): Psi4 molecule object. 88 Method (str): A valid method name. 89 BasisSet (str): A valid basis set. 90 ReturnWaveFunction (bool): Return wave function. 91 Quiet (bool): Flag to print error message. 92 93 Returns: 94 float: Total electronic energy in Hartrees. 95 (float, psi4 object): Energy and wavefuction. 96 97 """ 98 99 Status = False 100 Energy, WaveFunction = [None] * 2 101 102 try: 103 MethodAndBasisSet = JoinMethodNameAndBasisSet(Method, BasisSet) 104 if ReturnWaveFunction: 105 Energy, WaveFunction = psi4.energy(MethodAndBasisSet, molecule = Molecule, return_wfn = True) 106 else: 107 Energy = psi4.energy(MethodAndBasisSet, molecule = Molecule, return_wfn = False) 108 Status = True 109 except Exception as ErrMsg: 110 if not Quiet: 111 MiscUtil.PrintWarning("Psi4Util.CalculateSinglePointEnergy: Failed to calculate energy:\n%s\n" % ErrMsg) 112 113 return (Status, Energy, WaveFunction) if ReturnWaveFunction else (Status, Energy) 114 115 def PerformGeometryOptimization(psi4, Molecule, Method, BasisSet, ReturnWaveFunction = True, Quiet = False): 116 """Perform geometry optimization using a specified method and basis set. 117 118 Arguments: 119 psi4 (Object): Psi4 module reference. 120 Molecule (Object): Psi4 molecule object. 121 Method (str): A valid method name. 122 BasisSet (str): A valid basis set. 123 ReturnWaveFunction (bool): Return wave function. 124 Quiet (bool): Flag to print error message. 125 126 Returns: 127 float: Total electronic energy in Hartrees. 128 (float, psi4 object): Energy and wavefuction. 129 130 """ 131 132 Status = False 133 Energy, WaveFunction = [None] * 2 134 135 try: 136 MethodAndBasisSet = JoinMethodNameAndBasisSet(Method, BasisSet) 137 if ReturnWaveFunction: 138 Energy, WaveFunction = psi4.optimize(MethodAndBasisSet, molecule = Molecule, return_wfn = True) 139 else: 140 Energy = psi4.optimize(MethodAndBasisSet, molecule = Molecule, return_wfn = False) 141 Status = True 142 except Exception as ErrMsg: 143 if not Quiet: 144 MiscUtil.PrintWarning("Psi4Util.PerformGeometryOptimization: Failed to perform geometry optimization:\n%s\n" % ErrMsg) 145 146 return (Status, Energy, WaveFunction) if ReturnWaveFunction else (Status, Energy) 147 148 def JoinMethodNameAndBasisSet(MethodName, BasisSet): 149 """Join method name and basis set using a backslash delimiter. 150 An empty basis set specification is ignored. 151 152 Arguments: 153 MethodName (str): A valid method name. 154 BasisSet (str): A valid basis set or an empty string. 155 156 Returns: 157 str: MethodName/BasisSet or MethodName 158 159 """ 160 161 return MethodName if MiscUtil.IsEmpty(BasisSet) else "%s/%s" % (MethodName, BasisSet) 162 163 def GetAtomPositions(psi4, WaveFunction, InAngstroms = True): 164 """Retrieve a list of lists containing coordinates of all atoms in the 165 molecule available in Psi4 wave function. By default, the atom positions 166 are returned in Angstroms. The Psi4 default is Bohr. 167 168 Arguments: 169 psi4 (Object): Psi4 module reference. 170 WaveFunction (Object): Psi4 wave function reference. 171 InAngstroms (bool): True - Positions in Angstroms; Otherwise, in Bohr. 172 173 Returns: 174 None or list : List of lists containing atom positions. 175 176 Examples: 177 178 for AtomPosition in Psi4Util.GetAtomPositions(Psi4Handle, WaveFunction): 179 print("X: %s; Y: %s; Z: %s" % (AtomPosition[0], AtomPosition[1], 180 AtomPosition[2])) 181 182 """ 183 184 if WaveFunction is None: 185 return None 186 187 AtomPositions = WaveFunction.molecule().geometry().to_array() 188 if InAngstroms: 189 AtomPositions = AtomPositions * psi4.constants.bohr2angstroms 190 191 return AtomPositions.tolist() 192 193 def ListPsi4RunParamaters(psi4): 194 """List values for a key set of the following Psi4 runtime parameters: 195 Memory, NumThreads, OutputFile, ScratchDir, DataDir. 196 197 Arguments: 198 psi4 (object): Psi4 module reference. 199 200 Returns: 201 None 202 203 """ 204 205 MiscUtil.PrintInfo("\nListing Psi4 run options:") 206 207 # Memory in bytes... 208 Memory = psi4.get_memory() 209 MiscUtil.PrintInfo("Memory: %s (B); %s (MB)" % (Memory, Memory/(1024*1024))) 210 211 # Number of threads... 212 NumThreads = psi4.get_num_threads() 213 MiscUtil.PrintInfo("NumThreads: %s " % (NumThreads)) 214 215 # Output file... 216 OutputFile = psi4.core.get_output_file() 217 MiscUtil.PrintInfo("OutputFile: %s " % (OutputFile)) 218 219 # Scratch dir... 220 psi4_io = psi4.core.IOManager.shared_object() 221 ScratchDir = psi4_io.get_default_path() 222 MiscUtil.PrintInfo("ScratchDir: %s " % (ScratchDir)) 223 224 # Data dir... 225 DataDir = psi4.core.get_datadir() 226 MiscUtil.PrintInfo("DataDir: %s " % (DataDir)) 227 228 def UpdatePsi4OptionsParameters(psi4, OptionsInfo): 229 """Update Psi4 options using psi4.set_options(). 230 231 Arguments: 232 psi4 (object): Psi4 module reference. 233 OptionsInfo (dictionary) : Option name and value pairs for setting 234 global and module options. 235 236 Returns: 237 None 238 239 """ 240 if OptionsInfo is None: 241 return 242 243 if len(OptionsInfo) == 0: 244 return 245 246 try: 247 psi4.set_options(OptionsInfo) 248 except Exception as ErrMsg: 249 MiscUtil.PrintWarning("Psi4Util.UpdatePsi4OptionsParameters: Failed to set Psi4 options\n%s\n" % ErrMsg) 250 251 def UpdatePsi4RunParameters(psi4, RunParamsInfo): 252 """Update Psi4 runtime parameters. The supported parameter names along with 253 their default values are as follows: MemoryInGB: 1; NumThreads: 1, OutputFile: 254 stdout; ScratchDir: auto; RemoveOutputFile: True. 255 256 Arguments: 257 psi4 (object): Psi4 module reference. 258 RunParamsInfo (dictionary) : Parameter name and value pairs for 259 configuring Psi4 jobs. 260 261 Returns: 262 None 263 264 """ 265 266 # Set default values for possible arguments... 267 Psi4RunParams = {"MemoryInGB": 1, "NumThreads": 1, "OutputFile": "stdout", "ScratchDir" : "auto", "RemoveOutputFile": True} 268 269 # Set specified values for possible arguments... 270 for Param in Psi4RunParams: 271 if Param in RunParamsInfo: 272 Psi4RunParams[Param] = RunParamsInfo[Param] 273 274 # Memory... 275 Memory = int(Psi4RunParams["MemoryInGB"]*1024*1024*1024) 276 psi4.core.set_memory_bytes(Memory, True) 277 278 # Number of threads... 279 psi4.core.set_num_threads(Psi4RunParams["NumThreads"], quiet = True) 280 281 # Output file... 282 OutputFile = Psi4RunParams["OutputFile"] 283 if not re.match("^stdout$", OutputFile, re.I): 284 # Possible values: stdout, quiet, devnull, or filename 285 if re.match("^(quiet|devnull)$", OutputFile, re.I): 286 # Psi4 output is redirected to /dev/null after call to be_quiet function... 287 psi4.core.be_quiet() 288 else: 289 # Delete existing output file at the start of the first Psi4 run... 290 if Psi4RunParams["RemoveOutputFile"]: 291 if os.path.isfile(OutputFile): 292 os.remove(OutputFile) 293 294 # Append to handle output from multiple Psi4 runs for molecules in 295 # input file... 296 Append = True 297 psi4.core.set_output_file(OutputFile, Append) 298 299 # Scratch directory... 300 ScratchDir = Psi4RunParams["ScratchDir"] 301 if not re.match("^auto$", ScratchDir, re.I): 302 if not os.path.isdir(ScratchDir): 303 MiscUtil.PrintError("ScratchDir is not a directory: %s" % ScratchDir) 304 psi4.core.IOManager.shared_object().set_default_path(os.path.abspath(os.path.expanduser(ScratchDir))) 305 306 def ProcessPsi4OptionsParameters(ParamsOptionName, ParamsOptionValue): 307 """Process parameters for setting up Psi4 options and return a map 308 containing processed parameter names and values. 309 310 ParamsOptionValue is a comma delimited list of Psi4 option name and value 311 pairs for setting global and module options. The names are 'option_name' 312 for global options and 'module_name__option_name' for options local to a 313 module. The specified option names must be valid Psi4 names. No validation 314 is performed. 315 316 The specified option name and value pairs are processed and passed to 317 psi4.set_options() as a dictionary. The supported value types are float, 318 integer, boolean, or string. The float value string is converted into a float. 319 The valid values for a boolean string are yes, no, true, false, on, or off. 320 321 Arguments: 322 ParamsOptionName (str): Command line input parameters option name. 323 ParamsOptionValue (str): Comma delimited list of parameter name and value pairs. 324 325 Returns: 326 dictionary: Processed parameter name and value pairs. 327 328 """ 329 330 OptionsInfo = {} 331 332 if re.match("^(auto|none)$", ParamsOptionValue, re.I): 333 return None 334 335 ParamsOptionValue = ParamsOptionValue.strip() 336 if not ParamsOptionValue: 337 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 338 339 ParamsOptionValueWords = ParamsOptionValue.split(",") 340 if len(ParamsOptionValueWords) % 2: 341 MiscUtil.PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 342 343 # Validate paramater name and value pairs... 344 for Index in range(0, len(ParamsOptionValueWords), 2): 345 Name = ParamsOptionValueWords[Index].strip() 346 Value = ParamsOptionValueWords[Index + 1].strip() 347 348 if MiscUtil.IsInteger(Value): 349 Value = int(Value) 350 elif MiscUtil.IsFloat(Value): 351 Value = float(Value) 352 353 OptionsInfo[Name] = Value 354 355 return OptionsInfo 356 357 def ProcessPsi4RunParameters(ParamsOptionName, ParamsOptionValue, InfileName = None, ParamsDefaultInfo = None): 358 """Process parameters for Psi4 runs and return a map containing processed 359 parameter names and values. 360 361 ParamsOptionValue a comma delimited list of parameter name and value pairs 362 for configuring Psi4 jobs. 363 364 The supported parameter names along with their default and possible 365 values are shown below: 366 367 MemoryInGB,1,NumThreads,1,OutputFile,auto,ScratchDir,auto, 368 RemoveOutputFile,yes 369 370 Possible values: OutputFile - stdout, quiet, or FileName; OutputFile - 371 DirName; RemoveOutputFile - yes, no, true, or false 372 373 These parameters control the runtime behavior of Psi4. 374 375 The default for 'OutputFile' is a file name <InFileRoot>_Psi4.out. The PID 376 is appened the output file name during multiprocessing. The 'stdout' value 377 for 'OutputType' sends Psi4 output to stdout. The 'quiet' or 'devnull' value 378 suppresses all Psi4 output. 379 380 The default 'Yes' value of 'RemoveOutputFile' option forces the removal 381 of any existing Psi4 before creating new files to append output from 382 multiple Psi4 runs. 383 384 The option 'ScratchDir' is a directory path to the location of scratch 385 files. The default value corresponds to Psi4 default. It may be used to 386 override the deafult path. 387 388 Arguments: 389 ParamsOptionName (str): Command line Psi4 run parameters option name. 390 ParamsOptionValues (str): Comma delimited list of parameter name and value pairs. 391 InfileName (str): Name of input file. 392 ParamsDefaultInfo (dict): Default values to override for selected parameters. 393 394 Returns: 395 dictionary: Processed parameter name and value pairs. 396 397 Notes: 398 The parameter name and values specified in ParamsOptionValues are validated before 399 returning them in a dictionary. 400 401 """ 402 403 ParamsInfo = {"MemoryInGB": 1, "NumThreads": 1, "OutputFile": "auto", "ScratchDir" : "auto", "RemoveOutputFile": True} 404 405 # Setup a canonical paramater names... 406 ValidParamNames = [] 407 CanonicalParamNamesMap = {} 408 for ParamName in sorted(ParamsInfo): 409 ValidParamNames.append(ParamName) 410 CanonicalParamNamesMap[ParamName.lower()] = ParamName 411 412 # Update default values... 413 if ParamsDefaultInfo is not None: 414 for ParamName in ParamsDefaultInfo: 415 if ParamName not in ParamsInfo: 416 MiscUtil.PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessPsi4RunParameters is not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 417 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 418 419 if re.match("^auto$", ParamsOptionValue, re.I): 420 # No specific parameters to process except for parameters with possible auto value... 421 _ProcessPsi4RunAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName) 422 return ParamsInfo 423 424 ParamsOptionValue = ParamsOptionValue.strip() 425 if not ParamsOptionValue: 426 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 427 428 ParamsOptionValueWords = ParamsOptionValue.split(",") 429 if len(ParamsOptionValueWords) % 2: 430 MiscUtil.PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 431 432 # Validate paramater name and value pairs... 433 for Index in range(0, len(ParamsOptionValueWords), 2): 434 Name = ParamsOptionValueWords[Index].strip() 435 Value = ParamsOptionValueWords[Index + 1].strip() 436 437 CanonicalName = Name.lower() 438 if not CanonicalName in CanonicalParamNamesMap: 439 MiscUtil.PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 440 441 ParamName = CanonicalParamNamesMap[CanonicalName] 442 ParamValue = Value 443 444 if re.match("^MemoryInGB$", ParamName, re.I): 445 Value = float(Value) 446 if Value <= 0: 447 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 448 ParamValue = Value 449 elif re.match("^NumThreads$", ParamName, re.I): 450 Value = int(Value) 451 if Value <= 0: 452 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 453 ParamValue = Value 454 elif re.match("^ScratchDir$", ParamName, re.I): 455 if not re.match("^auto$", Value, re.I): 456 if not os.path.isdir(Value): 457 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. It must be a directory name." % (Value, Name, ParamsOptionName)) 458 ParamValue = Value 459 elif re.match("^RemoveOutputFile$", ParamName, re.I): 460 if re.match("^(yes|true)$", Value, re.I): 461 Value = True 462 elif re.match("^(no|false)$", Value, re.I): 463 Value = False 464 else: 465 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: yes, no, true, or false" % (Value, Name, ParamsOptionName)) 466 ParamValue = Value 467 468 # Set value... 469 ParamsInfo[ParamName] = ParamValue 470 471 # Handle paramaters with possible auto values... 472 _ProcessPsi4RunAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName) 473 474 return ParamsInfo 475 476 def _ProcessPsi4RunAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue, InfileName): 477 """Process parameters with possible auto values. 478 """ 479 480 Value = ParamsInfo["OutputFile"] 481 ParamValue = Value 482 if re.match("^auto$", Value, re.I): 483 if InfileName is not None: 484 # Use InfileName to setup output file. The OutputFile name is automatically updated using 485 # PID during multiprocessing... 486 InfileDir, InfileRoot, InfileExt = MiscUtil.ParseFileName(InfileName) 487 OutputFile = "%s_Psi4.out" % (InfileRoot) 488 else: 489 OutputFile = "Psi4.out" 490 elif re.match("^(devnull|quiet)$", Value, re.I): 491 OutputFile = "quiet" 492 else: 493 # It'll be treated as a filename and processed later... 494 OutputFile = Value 495 496 ParamsInfo["OutputFile"] = OutputFile 497 498 # OutputFileSpecified is used to track the specified value of the paramater. 499 # It may be used by the calling function to dynamically override the value of 500 # OutputFile to suprress the Psi4 output based on the initial value. 501 ParamsInfo["OutputFileSpecified"] = ParamValue 502 503 def ProcessPsi4ConstrainTorrionsParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 504 """Process parameters for Psi4 constrain torsions around rotatable bonds and 505 return a map containing processed parameter names and values. 506 507 ParamsOptionValue is a comma delimited list of parameter name and value pairs 508 for generating cube files. 509 510 The supported parameter names along with their default and possible 511 values are shown below: 512 513 ignoreHydrogens, yes, rotBondsSMARTSMode, NonStrict, 514 rotBondsSMARTSPattern, Auto 515 516 Arguments: 517 ParamsOptionName (str): Command line Psi4 constrain torsions option name. 518 ParamsOptionValues (str): Comma delimited list of parameter name and value pairs. 519 ParamsDefaultInfo (dict): Default values to override for selected parameters. 520 521 Returns: 522 dictionary: Processed parameter name and value pairs. 523 524 """ 525 526 ParamsInfo = {"IgnoreHydrogens":True, "RotBondsSMARTSMode": "SemiStrict", "RotBondsSMARTSPattern": "auto"} 527 528 # Setup a canonical paramater names... 529 ValidParamNames = [] 530 CanonicalParamNamesMap = {} 531 for ParamName in sorted(ParamsInfo): 532 ValidParamNames.append(ParamName) 533 CanonicalParamNamesMap[ParamName.lower()] = ParamName 534 535 # Update default values... 536 if ParamsDefaultInfo is not None: 537 for ParamName in ParamsDefaultInfo: 538 if ParamName not in ParamsInfo: 539 MiscUtil.PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessPsi4ConstrainTorrionsParameters not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 540 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 541 542 if re.match("^auto$", ParamsOptionValue, re.I): 543 # No specific parameters to process except for parameters with possible auto value... 544 _ProcessPsi4ConstrainTorsionsAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 545 return ParamsInfo 546 547 ParamsOptionValue = ParamsOptionValue.strip() 548 if not ParamsOptionValue: 549 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 550 551 ParamsOptionValueWords = ParamsOptionValue.split(",") 552 if len(ParamsOptionValueWords) % 2: 553 MiscUtil.PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 554 555 # Validate paramater name and value pairs... 556 for Index in range(0, len(ParamsOptionValueWords), 2): 557 Name = ParamsOptionValueWords[Index].strip() 558 Value = ParamsOptionValueWords[Index + 1].strip() 559 560 CanonicalName = Name.lower() 561 if not CanonicalName in CanonicalParamNamesMap: 562 MiscUtil.PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 563 564 ParamName = CanonicalParamNamesMap[CanonicalName] 565 ParamValue = Value 566 567 if re.match("^IgnoreHydrogens$", ParamName, re.I): 568 if re.match("^(yes|true)$", Value, re.I): 569 Value = True 570 elif re.match("^(no|false)$", Value, re.I): 571 Value = False 572 else: 573 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: yes, no, true, or false" % (Value, Name, ParamsOptionName)) 574 ParamValue = Value 575 elif re.match("^RotBondsSMARTSMode$", ParamName, re.I): 576 if not re.match("^(NonStrict|SemiStrict|Strict|Specify)$", Value, re.I): 577 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: NonStrict, SemiStrict, Strict, or Specify" % (Value, Name, ParamsOptionName)) 578 ParamValue = Value 579 580 # Set value... 581 ParamsInfo[ParamName] = ParamValue 582 583 # Handle paramaters with possible auto values... 584 _ProcessPsi4ConstrainTorsionsAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 585 586 return ParamsInfo 587 588 def _ProcessPsi4ConstrainTorsionsAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 589 """Process parameters with possible auto values. 590 """ 591 592 if re.match("^Specify$", ParamsInfo["RotBondsSMARTSMode"], re.I): 593 if re.match("^auto$", ParamsInfo["RotBondsSMARTSPattern"], re.I): 594 MiscUtil.PrintError("The parameter value, auto, specified for parameter name, RotBondsSMARTSPattern, using \"%s\ is not allowed during, specify, value for parameter name, RotBondsSMARTSMode. You must specify a valid SMARTS pattern using parameter name, RotBondsSMARTSPattern." % ParamsOptionName) 595 else: 596 if not re.match("^auto$", ParamsInfo["RotBondsSMARTSPattern"], re.I): 597 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, RotBondsSMARTSPattern, using \"%s\ is not allowed during, %s, value for parameter name, RotBondsSMARTSMode." % (ParamsInfo["RotBondsSMARTSPattern"], ParamsOptionName, ParamsInfo["RotBondsSMARTSMode"])) 598 599 # Setup default SMARTS pattern... 600 Name = "RotBondsSMARTSMode" 601 Value = ParamsInfo[Name] 602 if re.match("^NonStrict$", Value, re.I): 603 RotBondsSMARTSPattern = "[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]" 604 elif re.match("^SemiStrict$", Value, re.I): 605 RotBondsSMARTSPattern = "[!$(*#*)&!D1&!$(C(F)(F)F)&!$(C(Cl)(Cl)Cl)&!$(C(Br)(Br)Br)&!$(C([CH3])([CH3])[CH3])]-!@[!$(*#*)&!D1&!$(C(F)(F)F)&!$(C(Cl)(Cl)Cl)&!$(C(Br)(Br)Br)&!$(C([CH3])([CH3])[CH3])]" 606 elif re.match("^Strict$", Value, re.I): 607 RotBondsSMARTSPattern = "[!$(*#*)&!D1&!$(C(F)(F)F)&!$(C(Cl)(Cl)Cl)&!$(C(Br)(Br)Br)&!$(C([CH3])([CH3])[CH3])&!$([CD3](=[N,O,S])-!@[#7,O,S!D1])&!$([#7,O,S!D1]-!@[CD3]=[N,O,S])&!$([CD3](=[N+])-!@[#7!D1])&!$([#7!D1]-!@[CD3]=[N+])]-!@[!$(*#*)&!D1&!$(C(F)(F)F)&!$(C(Cl)(Cl)Cl)&!$(C(Br)(Br)Br)&!$(C([CH3])([CH3])[CH3])]" 608 elif re.match("^Specify$", Value, re.I): 609 RotBondsSMARTSPattern = ParamsInfo["RotBondsSMARTSPattern"].strip() 610 if not len(RotBondsSMARTSPattern): 611 MiscUtil.PrintError("Empty value specified using parameter name, RotBondsSMARTSPattern, using \"%s\ option" % ParamsOptionName) 612 else: 613 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: NonStrict, SemiStrict, Strict, or Specify" % (Value, Name, ParamsOptionName)) 614 615 ParamsInfo["RotBondsSMARTSPattern"] = RotBondsSMARTSPattern 616 617 return 618 619 def ProcessPsi4DDXSolvationParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 620 """Process parameters for Psi4 DDX solvation and return a map containing 621 processed parameter names and values. 622 623 ParamsOptionValue is a space delimited list of parameter name and value pairs 624 for solvation energy calculatios. 625 626 The supported parameter names along with their default and possible 627 values are shown below: 628 629 SolvationModel PCM Solvent water solventEpsilon None radiiSet UFF 630 631 solvationModel: Solvation model for calculating solvation energy. The 632 corresponding Psi4 option is DDX_MODEL. 633 634 solvent: Solvent to use. The corresponding Ps4 option is DDX_SOLVENT. 635 636 solventEpsilon: Dielectric constant of the solvent. The corresponding 637 Psi4 option is DDX_SOLVENT_EPSILON. 638 639 radiiSet: Radius set for cavity spheres. The corresponding Psi option is 640 DDX_RADII_SET. 641 642 Arguments: 643 ParamsOptionName (str): Command line Psi4 DDX solvation option name. 644 ParamsOptionValues (str): Space delimited list of parameter name and value pairs. 645 ParamsDefaultInfo (dict): Default values to override for selected parameters. 646 647 Returns: 648 dictionary: Processed parameter name and value pairs. 649 650 """ 651 652 ParamsInfo = {"SolvationModel": "PCM", "Solvent": "water", "SolventEpsilon": None, "RadiiSet": "UFF", "RadiiScaling": "auto"} 653 654 # Setup a canonical paramater names... 655 ValidParamNames = [] 656 CanonicalParamNamesMap = {} 657 for ParamName in sorted(ParamsInfo): 658 ValidParamNames.append(ParamName) 659 CanonicalParamNamesMap[ParamName.lower()] = ParamName 660 661 # Update default values... 662 if ParamsDefaultInfo is not None: 663 for ParamName in ParamsDefaultInfo: 664 if ParamName not in ParamsInfo: 665 MiscUtil.PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessPsi4DDXSolvationParameters not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 666 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 667 668 ParamsOptionValue = ParamsOptionValue.strip() 669 if not ParamsOptionValue: 670 MiscUtil.PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 671 672 if re.match("^auto$", ParamsOptionValue, re.I): 673 _ProcessPsi4DDXSolvationAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 674 return ParamsInfo 675 676 ParamsOptionValueWords = ParamsOptionValue.split() 677 if len(ParamsOptionValueWords) % 2: 678 MiscUtil.PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 679 680 for Index in range(0, len(ParamsOptionValueWords), 2): 681 Name = ParamsOptionValueWords[Index].strip() 682 Value = ParamsOptionValueWords[Index + 1].strip() 683 684 CanonicalName = Name.lower() 685 if not CanonicalName in CanonicalParamNamesMap: 686 MiscUtil.PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 687 688 ParamName = CanonicalParamNamesMap[CanonicalName] 689 ParamValue = Value 690 691 if re.match("^SolvationModel$", ParamName, re.I): 692 if not re.match("^(COSMO|PCM)$", Value, re.I): 693 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: COSMO or PCM" % (Value, Name, ParamsOptionName)) 694 ParamValue = Value 695 elif re.match("^Solvent$", ParamName, re.I): 696 if MiscUtil.IsEmpty(Value): 697 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is empty." % (Value, Name, ParamsOptionName)) 698 ParamValue = Value 699 elif re.match("^SolventEpsilon$", ParamName, re.I): 700 if re.match("^none$", Value, re.I): 701 Value = None 702 else: 703 if not MiscUtil.IsNumber(Value): 704 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 705 Value = float(Value) 706 if Value <= 0: 707 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: >= 0" % (Value, Name, ParamsOptionName)) 708 ParamValue = Value 709 elif re.match("^RadiiSet$", ParamName, re.I): 710 if not re.match("^(UFF|Bondi)$", Value, re.I): 711 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: UFF or Bondi" % (Value, Name, ParamsOptionName)) 712 ParamValue = Value 713 elif re.match("^RadiiScaling$", ParamName, re.I): 714 if not re.match("^auto$", Value, re.I): 715 if not MiscUtil.IsNumber(Value): 716 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 717 Value = float(Value) 718 if Value <= 0: 719 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: >= 0" % (Value, Name, ParamsOptionName)) 720 ParamValue = Value 721 else: 722 ParamValue = Value 723 724 # Set value... 725 ParamsInfo[ParamName] = ParamValue 726 727 SolventEpsilon = ParamsInfo["SolventEpsilon"] 728 if SolventEpsilon is not None and SolventEpsilon > 0.0: 729 Solvent = ParamsInfo["Solvent"] 730 if not MiscUtil.IsEmpty(Solvent): 731 MiscUtil.PrintWarning(" You've specified values for both \"solvent\" and \"solventEpsilon\" parameters using \"%s\" option. The parameter value, %s, specified for paramater name \"solvent\" is being ignored..." % (ParamsOptionName, Solvent)) 732 733 # Handle paramaters with possible auto values... 734 _ProcessPsi4DDXSolvationAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 735 return ParamsInfo 736 737 def _ProcessPsi4DDXSolvationAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 738 """Process parameters with possible auto values. 739 """ 740 741 ParamValue = "%s" % ParamsInfo["RadiiScaling"] 742 if re.match("^auto$", ParamValue, re.I): 743 if re.match("^UFF$", ParamsInfo["RadiiSet"], re.I): 744 ParamValue = 1.1 745 elif re.match("^Bondi$", ParamsInfo["RadiiSet"], re.I): 746 ParamValue = 1.2 747 else: 748 ParamValue = 0.0 749 else: 750 ParamValue = float(ParamValue) 751 ParamsInfo["RadiiScaling"] = ParamValue 752 753 return 754 755 def SetupPsi4DDXSolvationOptions(SolvationMode, ParamsInfo): 756 """Setup Psi4 options for calculating solvation energy using DDX module. 757 758 Arguments: 759 SolvationMode (bool): Set DDX option for solvation calculation. 760 ParamsInfo (dict): Psi4 DDX parameter name and value pairs. 761 762 Returns: 763 dictionary: Psi4 Option name and value pairs. 764 765 """ 766 767 # Initialize DDX solvation options... 768 DDXOptionsInfo = {} 769 DDXOptionsInfo["DDX"] = True if SolvationMode else False 770 771 # Setup DDX solvation options... 772 ParamNameToDDXOptionID = {"SolvationModel": "DDX_MODEL", "Solvent": "DDX_SOLVENT", "SolventEpsilon": "DDX_SOLVENT_EPSILON", "RadiiSet": "DDX_RADII_SET", "RadiiScaling": "DDX_RADII_SCALING"} 773 774 for ParamName in ParamNameToDDXOptionID: 775 DDXOptionID = ParamNameToDDXOptionID[ParamName] 776 DDXOptionsInfo[DDXOptionID] = ParamsInfo[ParamName] 777 778 # Check for the presence fo both solvent and solvent epsilon parameters... 779 if DDXOptionsInfo["DDX_SOLVENT_EPSILON"] is None: 780 DDXOptionsInfo.pop("DDX_SOLVENT_EPSILON", None) 781 else: 782 DDXOptionsInfo.pop("DDX_SOLVENT", None) 783 784 return DDXOptionsInfo 785 786 def ProcessPsi4CubeFilesParameters(ParamsOptionName, ParamsOptionValue, ParamsDefaultInfo = None): 787 """Process parameters for Psi4 runs and return a map containing processed 788 parameter names and values. 789 790 ParamsOptionValue is a comma delimited list of parameter name and value pairs 791 for generating cube files. 792 793 The supported parameter names along with their default and possible 794 values are shown below: 795 796 GridSpacing, 0.2, GridOverage, 4.0, IsoContourThreshold, 0.85 797 798 GridSpacing: Units: Bohr. A higher value reduces the size of the cube files 799 on the disk. This option corresponds to Psi4 option CUBIC_GRID_SPACING. 800 801 GridOverage: Units: Bohr.This option corresponds to Psi4 option 802 CUBIC_GRID_OVERAGE. 803 804 IsoContourThreshold captures specified percent of the probability density 805 using the least amount of grid points. This option corresponds to Psi4 option 806 CUBEPROP_ISOCONTOUR_THRESHOLD. 807 808 Arguments: 809 ParamsOptionName (str): Command line Psi4 cube files option name. 810 ParamsOptionValues (str): Comma delimited list of parameter name and value pairs. 811 ParamsDefaultInfo (dict): Default values to override for selected parameters. 812 813 Returns: 814 dictionary: Processed parameter name and value pairs. 815 816 """ 817 818 ParamsInfo = {"GridSpacing": 0.2, "GridOverage": 4.0, "IsoContourThreshold": 0.85} 819 820 # Setup a canonical paramater names... 821 ValidParamNames = [] 822 CanonicalParamNamesMap = {} 823 for ParamName in sorted(ParamsInfo): 824 ValidParamNames.append(ParamName) 825 CanonicalParamNamesMap[ParamName.lower()] = ParamName 826 827 # Update default values... 828 if ParamsDefaultInfo is not None: 829 for ParamName in ParamsDefaultInfo: 830 if ParamName not in ParamsInfo: 831 MiscUtil.PrintError("The default parameter name, %s, specified using \"%s\" to function ProcessPsi4CubeFilesParameters not a valid name. Supported parameter names: %s" % (ParamName, ParamsDefaultInfo, " ".join(ValidParamNames))) 832 ParamsInfo[ParamName] = ParamsDefaultInfo[ParamName] 833 834 if re.match("^auto$", ParamsOptionValue, re.I): 835 # No specific parameters to process except for parameters with possible auto value... 836 _ProcessPsi4CubeFilesAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 837 return ParamsInfo 838 839 ParamsOptionValue = ParamsOptionValue.strip() 840 if not ParamsOptionValue: 841 PrintError("No valid parameter name and value pairs specified using \"%s\" option" % ParamsOptionName) 842 843 ParamsOptionValueWords = ParamsOptionValue.split(",") 844 if len(ParamsOptionValueWords) % 2: 845 MiscUtil.PrintError("The number of comma delimited paramater names and values, %d, specified using \"%s\" option must be an even number." % (len(ParamsOptionValueWords), ParamsOptionName)) 846 847 # Validate paramater name and value pairs... 848 for Index in range(0, len(ParamsOptionValueWords), 2): 849 Name = ParamsOptionValueWords[Index].strip() 850 Value = ParamsOptionValueWords[Index + 1].strip() 851 852 CanonicalName = Name.lower() 853 if not CanonicalName in CanonicalParamNamesMap: 854 MiscUtil.PrintError("The parameter name, %s, specified using \"%s\" is not a valid name. Supported parameter names: %s" % (Name, ParamsOptionName, " ".join(ValidParamNames))) 855 856 ParamName = CanonicalParamNamesMap[CanonicalName] 857 ParamValue = Value 858 859 if re.match("^(GridSpacing|GridOverage)$", ParamName, re.I): 860 if not MiscUtil.IsFloat(Value): 861 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 862 Value = float(Value) 863 if Value <= 0: 864 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: > 0" % (Value, Name, ParamsOptionName)) 865 ParamValue = Value 866 elif re.match("^IsoContourThreshold$", ParamName, re.I): 867 if not MiscUtil.IsFloat(Value): 868 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option must be a float." % (Value, Name, ParamsOptionName)) 869 Value = float(Value) 870 if Value <= 0 or Value > 1: 871 MiscUtil.PrintError("The parameter value, %s, specified for parameter name, %s, using \"%s\" option is not a valid value. Supported values: >= 0 and <= 1" % (Value, Name, ParamsOptionName)) 872 ParamValue = Value 873 874 # Set value... 875 ParamsInfo[ParamName] = ParamValue 876 877 # Handle paramaters with possible auto values... 878 _ProcessPsi4CubeFilesAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue) 879 880 return ParamsInfo 881 882 def _ProcessPsi4CubeFilesAutoParameters(ParamsInfo, ParamsOptionName, ParamsOptionValue): 883 """Process parameters with possible auto values. 884 """ 885 886 # No auto parameters to process 887 return 888 889 def RetrieveIsocontourRangeFromCubeFile(CubeFileName): 890 """Retrieve isocontour range values from the cube file. The range 891 values are retrieved from the second line in the cube file after 892 the string 'Isocontour range'. 893 894 Arguments: 895 CubeFileName (str): Cube file name. 896 897 Returns: 898 float: Minimum range value. 899 float: Maximum range value. 900 901 """ 902 903 IsocontourRangeMin, IsocontourRangeMax = [None] * 2 904 905 CubeFH = open(CubeFileName, "r") 906 if CubeFH is None: 907 MiscUtil.PrintError("Couldn't open cube file: %s.\n" % (CubeFileName)) 908 909 # Look for isocontour range in the first 2 comments line... 910 RangeLine = None 911 LineCount = 0 912 for Line in CubeFH: 913 LineCount += 1 914 Line = Line.rstrip() 915 if re.search("Isocontour range", Line, re.I): 916 RangeLine = Line 917 break 918 919 if LineCount >= 2: 920 break 921 CubeFH.close() 922 923 if RangeLine is None: 924 return (IsocontourRangeMin, IsocontourRangeMax) 925 926 LineWords = RangeLine.split(":") 927 928 ContourRangeWord = LineWords[-1] 929 ContourRangeWord = re.sub("(\(|\)| )", "", ContourRangeWord) 930 931 ContourLevel1, ContourLevel2 = ContourRangeWord.split(",") 932 ContourLevel1 = float(ContourLevel1) 933 ContourLevel2 = float(ContourLevel2) 934 935 if ContourLevel1 < ContourLevel2: 936 IsocontourRangeMin = ContourLevel1 937 IsocontourRangeMax = ContourLevel2 938 else: 939 IsocontourRangeMin = ContourLevel2 940 IsocontourRangeMax = ContourLevel1 941 942 return (IsocontourRangeMin, IsocontourRangeMax) 943 944 def RetrieveMinAndMaxValueFromCubeFile(CubeFileName): 945 """Retrieve minimum and maxmimum grid values from the cube file. 946 947 Arguments: 948 CubeFileName (str): Cube file name. 949 950 Returns: 951 float: Minimum value. 952 float: Maximum value. 953 954 """ 955 956 MinValue, MaxValue = [sys.float_info.max, sys.float_info.min] 957 958 CubeFH = open(CubeFileName, "r") 959 if CubeFH is None: 960 MiscUtil.PrintError("Couldn't open cube file: %s.\n" % (CubeFileName)) 961 962 # Ignore first two comments lines: 963 # 964 # The first two lines of the header are comments, they are generally ignored by parsing packages or used as two default labels. 965 # 966 # Ignore lines upto the last section of the header lines: 967 # 968 # The third line has the number of atoms included in the file followed by the position of the origin of the volumetric data. 969 # The next three lines give the number of voxels along each axis (x, y, z) followed by the axis vector. 970 # The last section in the header is one line for each atom consisting of 5 numbers, the first is the atom number, the second 971 # is the charge, and the last three are the x,y,z coordinates of the atom center. 972 # 973 Line = CubeFH.readline() 974 Line = CubeFH.readline() 975 Line = CubeFH.readline() 976 CubeFH.close() 977 978 Line = Line.strip() 979 LineWords = Line.split() 980 NumOfAtoms = int(LineWords[0]) 981 982 HeaderLinesCount = 6 + NumOfAtoms 983 984 # Ignore header lines... 985 CubeFH = open(CubeFileName, "r") 986 LineCount = 0 987 for Line in CubeFH: 988 LineCount += 1 989 if LineCount >= HeaderLinesCount: 990 break 991 992 # Process values.... 993 for Line in CubeFH: 994 Line = Line.strip() 995 for Value in Line.split(): 996 Value = float(Value) 997 998 if Value < MinValue: 999 MinValue = Value 1000 if Value > MaxValue: 1001 MaxValue = Value 1002 1003 return (MinValue, MaxValue) 1004 1005 def UpdatePsi4OutputFileUsingPID(OutputFile, PID = None): 1006 """Append PID to output file name. The PID is automatically retrieved 1007 during None value of PID. 1008 1009 Arguments: 1010 OutputFile (str): Output file name. 1011 PID (int): Process ID or None. 1012 1013 Returns: 1014 str: Update output file name. Format: <OutFieRoot>_<PID>.<OutFileExt> 1015 1016 """ 1017 1018 if re.match("stdout|devnull|quiet", OutputFile, re.I): 1019 return OutputFile 1020 1021 if PID is None: 1022 PID = os.getpid() 1023 1024 FileDir, FileRoot, FileExt = MiscUtil.ParseFileName(OutputFile) 1025 OutputFile = "%s_PID%s.%s" % (FileRoot, PID, FileExt) 1026 1027 return OutputFile 1028 1029 def RemoveScratchFiles(psi4, OutputFile, PID = None): 1030 """Remove any leftover scratch files associated with the specified output 1031 file. The file specification, <OutfileRoot>.*<PID>.* is used to collect and 1032 remove files from the scratch directory. In addition, the file 1033 psi.<PID>.clean, in current directory is removed. 1034 1035 Arguments: 1036 psi4 (object): psi4 module reference. 1037 OutputFile (str): Output file name. 1038 PID (int): Process ID or None. 1039 1040 Returns: 1041 None 1042 1043 """ 1044 1045 if re.match("stdout|devnull|quiet", OutputFile, re.I): 1046 # Scratch files are associated to stdout prefix... 1047 OutputFile = "stdout" 1048 1049 if PID is None: 1050 PID = os.getpid() 1051 1052 OutfileDir, OutfileRoot, OutfileExt = MiscUtil.ParseFileName(OutputFile) 1053 1054 ScratchOutfilesSpec = os.path.join(psi4.core.IOManager.shared_object().get_default_path(), "%s.*%s.*" % (OutfileRoot, PID)) 1055 for ScratchFile in glob.glob(ScratchOutfilesSpec): 1056 os.remove(ScratchFile) 1057 1058 # Remove any psi.<PID>.clean in the current directory... 1059 ScratchFile = os.path.join(os.getcwd(), "psi.%s.clean" % (PID)) 1060 if os.path.isfile(ScratchFile): 1061 os.remove(ScratchFile)