Example #1
0
def main(_cores: int = None,
         _time: int = None,
         _nodes: int = 1,
         _sub: bool = True):
    # Load in the logger
    utilities.load_logger_config()
    logger = logging.getLogger(__name__)

    # Load in the configuration for this user
    logger.debug("Calling load_phd_config")
    config = utilities.load_phd_config()

    # Parse the configuration!
    logger.debug("Assigning variables for users queueing")
    max_nodes = config['QUEUING']['max_nodes']
    node_types = config['QUEUING']['node_types']
    max_time = config['QUEUING']['max_time']
    high_priority_nodes = config['QUEUING']['high_priority_nodes']

    # Creates a list of (num_cores, max_time)
    avail_nodes = list(zip(node_types, max_time))

    # Assign the vars passed
    logger.debug("Checking for any passed args")
    cores = _cores
    time = _time
    nodes = _nodes
    sub = _sub

    # If no args passed, check for command line args
    if cores is None or time is None:
        # Arg Parser Here
        logger.debug("Parsing args")
        parser = argparse.ArgumentParser(
            description="Submits a DMD simulation to a queuing system")
        parser.add_argument(
            "-N",
            dest="nodes",
            type=int,
            required=False,
            default=1,
            help=f"The number of nodes to run the DMD simulation on.")
        parser.add_argument(
            "-n",
            dest="cores",
            type=int,
            required=True,
            help=f"The number of cores per node to run the DMD simulation with."
        )
        parser.add_argument(
            "-t",
            dest="time",
            type=int,
            required=True,
            help="The amount of time to submit the job for (hours).")
        parser.add_argument("-sub",
                            dest="sub",
                            action='store_true',
                            help="Submit the job to the queueing system.")
        args = parser.parse_args()

        if args.nodes > max_nodes or args.cores > max(
                node_types) or args.time > max(max_time):
            logger.error(
                "Invalid args parsed based off of what user has specified in config file"
            )
            parser.print_help()
            sys.exit(1)

        cores = args.cores
        time = args.time
        nodes = args.nodes
        sub = args.sub

    # Finds the appropriate node to use
    logger.debug("Checking to ensure user has enough resources")
    big_enough_nodes = list(filter(lambda i: i[0] >= cores, avail_nodes))
    enough_time_nodes = list(filter(lambda i: i[1] >= time, big_enough_nodes))

    possible_nodes = sorted(enough_time_nodes, key=lambda x: x[0])
    if not possible_nodes:
        logger.error("User does not have access to resources")
        raise ValueError

    # Grab the minimum processing node
    node_type = min(possible_nodes)[0]
    logger.debug(f"Using node type: {node_type}")

    high_priority = True if node_type in high_priority_nodes else False

    # Create the jinja2 dictionary
    job_params = {
        "date": datetime.datetime.now(),
        "submit_dir": os.path.realpath(os.getcwd()),
        "node_type": node_type,
        "cores": cores,
        "nodes": nodes,
        "high_priority": ",highp" if high_priority else "",
        "user": os.environ["USER"],
        "job_name": os.path.basename(os.getcwd()),
        "run_script": os.path.join(os.path.dirname(__file__), "rundmd.py"),
        "time": time
    }

    # Make the submit script for the queuing system
    home = os.path.expanduser("~")
    path_to_template = os.path.join(home, ".config/phd3")

    if not os.path.isdir(path_to_template):
        path_to_template = os.path.join(home, ".phd3")

    logger.debug(f"Finding file in: {path_to_template} ")
    templateLoader = jinja2.FileSystemLoader(searchpath=path_to_template)
    templateEnv = jinja2.Environment(loader=templateLoader)
    TEMPLATE_FILE = "submit.j2"
    try:
        template = templateEnv.get_template(TEMPLATE_FILE)

    except jinja2.exceptions.TemplateNotFound:
        logger.error(f"Template file not found in {path_to_template}")
        raise

    logger.debug("Found template file!")

    # Now we dump text to a file
    outputText = template.render(job_params)
    try:
        logger.debug(f"Writing out submit file: {constants.SUBMIT_FILE_NAME}")
        with open(constants.SUBMIT_FILE_NAME, "w+") as submit_file:
            submit_file.write(outputText)

    except IOError:
        logger.exception("Could not write out submit file!")
        sys.exit(1)

    if not os.path.isfile("dmdinput.json"):
        logger.warn("[WARNING] ==>> No dmdinput.json file found")

    # Submit the Job!!
    if sub:
        logger.info("Submitting job to queue")
        with Popen(
                f"{config['QUEUING']['submit']} {constants.SUBMIT_FILE_NAME}",
                shell=True,
                universal_newlines=True,
                stdin=PIPE,
                stdout=PIPE,
                stderr=PIPE,
                bufsize=1,
                env=os.environ) as shell:
            while shell.poll() is None:
                logger.info(shell.stdout.readline().strip())
                logger.info(shell.stderr.readline().strip())
Example #2
0
    def __init__(self,
                 cores,
                 run_dir: str = './',
                 time=-1,
                 coord=None,
                 parameters: dict = None):
        logger.debug("Beginning Calculation")

        logger.debug("Initializing variables")
        self._submit_directory = os.path.abspath(os.getcwd())
        self._scratch_directory = os.path.abspath(run_dir)
        self._config = utilities.load_phd_config()
        self._turbodir = self._config["PATHS"]['TURBODIR']
        self._cores = cores
        self._time_to_run = time
        self._para_arch = "SMP"
        self._resub = False
        self._timer_went_off = False
        self._coord_file = ""

        #Start the general signal for sigUSR1
        #This will call function calculation_alarm_handler if SIGUSR1 is sent to the node
        signal.signal(signal.SIGUSR1, self.calculation_alarm_handler)

        # We need to make sure that we have some parameters for this job!
        if parameters is None:
            logger.debug("Checking for definput.json file")
            if os.path.isfile("definput.json"):
                self._parameter_file = os.path.join(self._submit_directory,
                                                    "definput.json")

            else:
                logger.error("No parameters specified for the job!")
                raise FileNotFoundError("definput.json")

            # read in the parameters here to the dictionary!!
            try:
                # Read in the user supplied parameters from the input file
                logger.debug("Loading in parameters")
                with open(self._parameter_file, 'r') as inputfile:
                    self._raw_parameters = json.load(inputfile)

            except IOError:
                logger.exception("Could not open the parameter file correctly")
                raise

        else:
            logger.debug("Using parameters passed")
            self._raw_parameters = parameters

        # we want to make sure that turbomole environment is setup properly first now in case we have to run define or x2t!
        logger.debug("Setting up TURBOMOLE Environment")
        self.setup_turbomole_env_parallel(self._cores, self._para_arch,
                                          self._scratch_directory,
                                          self._turbodir)

        #Check to make sure that we have valid parameters before continuing!
        try:
            logger.debug("Ensuring proper parameters")
            utilities.valid_qm_parameters(self._raw_parameters)

        except exceptions.ParameterError:
            logger.error("Invalid parameters provided!")
            raise

        # Now we are in the submit directory, check for a coord file first!
        if coord is None:
            logger.debug("Looking for a coord file")
            if os.path.isfile("coord"):
                logger.debug(
                    f"Found a coord file: {os.path.join(self._submit_directory, 'coord')}"
                )
                self._coord_file = os.path.join(self._submit_directory,
                                                "coord")

            #Check to see if there is a .xyz to use instead
            else:
                logger.debug(
                    f"Looking for an .xyz file in: {self._submit_directory}")
                files = [
                    f for f in os.listdir(self._submit_directory)
                    if os.path.isfile(os.path.join(self._submit_directory, f))
                ]
                for f in files:
                    if os.path.splitext(f)[-1].lower() == ".xyz":
                        logger.info(
                            f"Found an .xyz file to use: {f}, will convert to coord"
                        )
                        utilities.xyz_to_coord(f)
                        self._coord_file = os.path.join(
                            self._submit_directory, "coord")
                        break

                #No such luck, can't do the job then!
                if self._coord_file == "":
                    logger.error(
                        "You don't seem to have provided a coord file, cannot proceed!"
                    )
                    raise FileNotFoundError("coord")

        else:
            self._coords = coord

        # At this point we will set up the turbomole job if it is not already setup (or more importantly, the control file!)
        if not os.path.isfile("control"):
            logger.debug(
                "Control file does not exist. Attempting to run setupturbomole.py"
            )
            stm = setupTMjob(self._raw_parameters)

        #Remove the stop file if it exists right now, otherwise we will have issues
        if os.path.isfile(os.path.join(self._submit_directory, "stop")):
            logger.warning("Stop file exists before job has run")
            logger.warning("Removing stop file and continuing")
            os.remove(os.path.join(self._submit_directory, "stop"))

        #We now want to transfer all of the files from this directory to the scratch directory if a diff. directory
        if self._scratch_directory != self._submit_directory:
            logger.info(
                f"Copying file from {self._submit_directory} to {self._scratch_directory}"
            )
            self._scratch_directory = os.path.join(
                self._scratch_directory,
                os.path.basename(self._submit_directory))
            utilities.copy_directories(self._submit_directory,
                                       self._scratch_directory)

            os.chdir(self._scratch_directory)

        # Check for MOs in tar.gz format
        for mo_file in constants.MO_FILES:
            if os.path.isfile(f"{mo_file}.tar.gz"):
                logger.info(f"[Untaring] ==>> {f}")
                with tarfile.open(f"{mo_file}.tar.gz", "r:gz") as tar:
                    tar.extractall()

                os.remove(f"{mo_file}.tar.gz")

        switcher = {
            "numforce": self._numforce,
            "nf": self._numforce,
            "forceopt": self._forceopt,
            "geo": self._geo,
            "singlepoint": self._singlepoint,
            "sp": self._singlepoint,
            "trans": self._trans,
            "escf": self._escf,
            "woelfling": self._woelfling,
            "egeo": self._egeo,
            "eforceopt": self._eforceopt,
            "enumforce": self._enumforce,
            "enf": self._enumforce
        }

        calc = switcher.get(self._raw_parameters["calculation"].lower(), None)
        try:
            logger.debug("Beginning the TURBOMOLE calulcation")

            # Arm the timer
            if self._time_to_run != -1:
                logger.info("Starting the timer")
                signal.signal(signal.SIGALRM, self.calculation_alarm_handler)
                signal.alarm(int((self._time_to_run * 60 - 45) * 60))

            start = timer()
            calc()
            end = timer()
            logger.info(
                f"Time elapsed: {datetime.timedelta(seconds = int(end -start))}"
            )
            if self._time_to_run != -1:
                # Turn off the timer now!
                logger.info("Turning off timer")
                signal.alarm(0)

        except:
            logger.error(
                f"Failed executing the commands for the job type provided: {self._raw_parameters['calculation']}"
            )
            raise

        #Now we copy the files back!
        if self._scratch_directory != self._submit_directory:
            utilities.copy_directories(self._scratch_directory,
                                       self._submit_directory)
            os.chdir(self._submit_directory)
            shutil.rmtree(self._scratch_directory)

        self.clean_up()
        logger.debug("Finished Calculation")

        if self._resub:
            logger.info("Resubmitting the job!")
            submitturbomole.main(_cores=self._cores, _time=self._time_to_run)
Example #3
0
#!/usr/bin/env python3
"""
Author  ==>> David J. Reilley
Author  ==>> Matthew R. Hennefarth
Date    ==>> April 16, 2020
"""

#Utility Paths
import phd3.utility.utilities as utilities

config = utilities.load_phd_config()

#Standard Library Imports
import logging
import os
import shutil
import pkg_resources

#3rd Party Libraries
import propka
from propka import run

#Titrate/PHD3
from . import montecarlo
from ..utility import constants, exceptions

logger = logging.getLogger(__name__)

#Turn off the propka logging
propka_logger = logging.getLogger("propka")
propka_logger.setLevel(logging.CRITICAL)
Example #4
0
def free_energy_correction(temp: float = 298.15):
    vibscale = 1
    gcorr = 0

    frozen_coords = False
    if not os.path.isfile("coord"):
        logger.exception("No coord file!")
        raise ValueError("coord")

    try:
        with open('coord') as coordFile:
            numatoms = -1
            for line in coordFile:
                if line[0] == '$' and "$coord" not in line:
                    logger.debug("Finished in the coordinate section")
                    break
                if len(line.split()) > 4:
                    logger.info(
                        "Frozen Coordinates detected; calculating vibrational free energy correction"
                    )
                    frozen_coords = True
                    gcorr = gcorrvib(temp)
                    break
                numatoms += 1

    except:
        logger.exception("Error reading the coord file!")
        raise

    if not frozen_coords:
        if numatoms == 1:
            logger.info(
                "Single atom detected; calculating translational free energy correction"
            )
            gcorr = gcorrtrans()

        else:
            with open("vibspectrum") as vibfile:
                for line in vibfile:
                    if not line.startswith(
                        ('$', '#')) and float(line[20:].split()[0]) < 0:
                        logger.warning(
                            f"Imaginary frequency detected: {line[20:].split()[0]}"
                        )

            logger.debug("Setting up environment to call freeh")
            config = utilities.load_phd_config()
            utilities.setup_turbomole_env(config["PATHS"]["TURBODIR"])

            try:
                logger.debug("Calling freeh")
                freehrun = subprocess.run(['freeh'],
                                          input=f'\n{vibscale}\n\nq\n',
                                          stdout=subprocess.PIPE,
                                          universal_newlines=True)

            except:
                logger.exception("Error in calling 'freeh' program!")
                raise ValueError

            chempotline = False
            linenum = 0
            for line in freehrun.stdout.split('\n'):
                if chempotline:
                    linenum += 1

                elif 'chem.pot.' in line.split():
                    chempotline = True

                if linenum == 3:
                    try:
                        logger.debug(
                            "Trying to grab the gcorr value from freeh output")
                        gcorr = float(line.split()[5]) / 2625.50

                    except ValueError:
                        logger.exception(
                            "Error in converting str to float in freeh output")
                        raise

                    break
    if not gcorr:
        raise ValueError("Could not get a free energy correction")

    return gcorr
Example #5
0
    def __init__(self, cores: int = 1, run_dir: str='./', time=-1, pro: protein.Protein=None, parameters: dict=None):

        logger.debug("Initializing variables")
        self._submit_directory = os.path.abspath(os.getcwd())
        self._scratch_directory = os.path.abspath(run_dir)
        self._config = utilities.load_phd_config()
        self._cores = cores
        self._time_to_run = time
        self._timer_went_off = False
        self._start_time = 0
        self._resub = False

        utilities.setup_dmd_environ()

        if parameters is None:
            logger.debug("Checking for a dmdinput.json file")
            if os.path.isfile("dmdinput.json"):
                self._parameter_file = os.path.join(self._submit_directory, "dmdinput.json")

            else:
                logger.error("No parameters specified for the job!")
                raise FileNotFoundError("dmdinput.json")

            #Now we read in the parameters here to a dictionary
            try:
                logger.debug("Loading in parameters")
                with open(self._parameter_file, 'r') as inputfile:
                    self._raw_parameters = json.load(inputfile)

            except IOError:
                logger.exception("Could not open the parameter file correctly!")
                raise

        else:
            logger.debug("Using parameters passed")
            self._raw_parameters = parameters

        # Now we check to see if the parameters are indeed valid
        try:
            utilities.valid_dmd_parameters(self._raw_parameters)

        except ValueError:
            logger.exception("Missing a parameter definition!")
            raise

        except ParameterError:
            logger.exception("Invalid parameter specification")
            raise

        if self._raw_parameters["titr"]["titr on"]:
            self._titration = titrate_protein(self._raw_parameters["titr"], self._raw_parameters["Custom protonation states"])
            self._raw_parameters = self._titration.expand_commands(self._raw_parameters)            

        else:
            self._titration = None

        if pro is None:
            if not os.path.isfile("initial.pdb"):
                logger.debug("initial.pdb not found, will try setting up from scratch")
                sj = setupDMDjob(parameters=self._raw_parameters)
                sj.full_setup()

        else:
            logger.debug("Will setup the protein for DMD")
            sj = setupDMDjob(parameters=self._raw_parameters, pro=pro)
            sj.full_setup()

        self.update_start_time()
        self.update_commands()

        if self._scratch_directory != self._submit_directory:
            self._scratch_directory = os.path.join(self._scratch_directory, os.path.basename(self._submit_directory))
            utilities.copy_directories(self._submit_directory, self._scratch_directory)
            os.chdir(self._scratch_directory)

        # We can arm the timer
        if self._time_to_run != -1:
            logger.info("Starting the timer")
            signal.signal(signal.SIGALRM, self.calculation_alarm_handler)
            signal.alarm((self._time_to_run * 60 - 55) * 60)

        # We loop over the steps here and will pop elements off the beginning of the dictionary
        while len(self._commands.values()) != 0:
            logger.info("")
            repeat = False
            if self._timer_went_off:
                logger.info("Timer went off, not continuing onto next command")
                break

            # Grab the next step dictionary to do
            steps = self._commands[list(self._commands.keys())[0]]
            logger.debug(f"On step: {steps}")
            updated_parameters = self._raw_parameters.copy()

            for changes in steps:
                logger.debug(f"Updating {changes}: changing {updated_parameters[changes]} to {steps[changes]}")
                updated_parameters[changes] = steps[changes]
            
            start = timer()
            if updated_parameters["titr"]["titr on"]:
                if self._titration is None:
                    logger.warning("Titration feature cannot be turned on in the middle of a run")
                    raise ValueError("Titration turned on")
                
                if os.path.isfile(updated_parameters["Movie File"]):
                    utilities.make_movie("initial.pdb", updated_parameters["Movie File"], "_tmpMovie.pdb")
                    #Append to movie
                    with open("_tmpMovie.pdb", 'r') as tmpMovie, open("movie.pdb", 'a') as movie:
                        for line in tmpMovie:
                            movie.write(line)

                    try:
                        last_frame = utilities.last_frame("_tmpMovie.pdb")
                    
                    except IndexError:
                        #grab last initial.pdb, echo and movie.pdb and place over current initial, echo, and movie.pdb and
                        #add updated_parameters to list
                        logger.warning("Going back one iteration")
                        if not os.path.isfile("_older_echo") or not os.path.isfile("_older_movie.pdb"):
                            logger.error("Cannot go back a step!")
                            raise 

                        shutil.move("_older_echo", updated_parameters["Echo File"])
                        last_frame = utilities.last_frame("_older_movie.pdb")
                        shutil.move("_older_movie.pdb", "movie.pdb")
                        
                        if os.path.isfile("_last_movie.pdb"):
                            os.remove("_last_movie.pdb")
                        
                        if os.path.isfile("_last_echo"):
                            os.remove("_last_echo")

                        self._titration._step -=2
                        repeat = True
                        self._start_time -= (2*updated_parameters["Time"])
                    
                    else:
                        #Clean up our mess
                        logger.debug("Removing _tmpMovie.pdb file")
                        os.remove("_tmpMovie.pdb")
                        logger.debug(f"Removing {updated_parameters['Movie File']} file")
                        os.remove(updated_parameters["Movie File"])
                    
                    #TODO check to see if any of the protonation states are invalids (ie, they affect statically held protonation
                    #states defined by the user)

                    #TODO, check if this raises any exceptions, and if so, bo back a step
                    try:
                        updated_parameters["Custom protonation states"] = self._titration.evaluate_pkas(last_frame)

                    except Propka_Error:
                        #grab last initial.pdb, echo and movie.pdb and place over current initial, echo, and movie.pdb and
                        #add updated_parameters to list
                        logger.warning("Going back one iteration")
                        if not os.path.isfile("_last_echo") or not os.path.isfile("_last_movie.pdb"):
                            logger.error("Cannot go back a step!")
                            raise 

                        shutil.move("_last_echo", updated_parameters["Echo File"])
                        last_frame = utilities.last_frame("_last_movie.pdb")
                        shutil.move("_last_movie.pdb", "movie.pdb")

                        self._titration._step -=1
                        self._start_time -= updated_parameters["Time"]
                        repeat = True
                        updated_parameters["Custom protonation states"] = self._titration.evaluate_pkas(last_frame)

                    else:
                        if not repeat:
                            if os.path.isfile("_older_movie.pdb"):
                                os.remove("_older_movie.pdb")

                            if os.path.isfile("_last_movie.pdb"):
                                shutil.copy("_last_movie.pdb", "_older_movie.pdb")

                            if os.path.isfile("movie.pdb"):
                                shutil.copy("movie.pdb", "_last_movie.pdb")

                            if os.path.isfile("_older_echo"):
                                os.remove("_older_echo")

                            if os.path.isfile("_last_echo"):
                                shutil.copy("_last_echo", "_older_echo")

                            if os.path.isfile(updated_parameters["Echo File"]):
                                shutil.copy(updated_parameters["Echo File"], "_last_echo")
                    
                else:
                    last_frame = utilities.load_pdb("initial.pdb")
                    try:
                        updated_parameters["Custom protonation states"] = self._titration.evaluate_pkas(last_frame)
                
                    except Propka_Error:
                        logger.warn("Propka run weirdly, though hopefully it doesn't matter since we skip!")
                                     
                if os.path.isfile(updated_parameters['Restart File']):
                    logger.debug(f"Removing {updated_parameters['Restart File']} file")
                    os.remove(updated_parameters['Restart File'])

                sj = setupDMDjob(parameters=updated_parameters, pro=last_frame)
                
                #This will not do a quick dmd setup, so we should be able to expedite that slightly. Also no topparam file either
                #creates state, start and outConstr, inConstr
                sj.titrate_setup()

                #We don't want to use the restart file, so set last var to False
                self.run_dmd(updated_parameters, self._start_time, False)

            elif "Custom protonation states" in steps.keys():
                logger.warning("Why are you trying to change the protonation state in the middle of DMD?")

            elif "Frozen atoms" in steps.keys() or "Restrict Displacement" in steps.keys():
                logger.warning("Cannot freeze atoms or change displacement between atoms in the middle of a run.")
                logger.warning("This does not make any...ignoring these")

            else:
                # We can just run the job with no issues other than those raised from the above
                self.run_dmd(updated_parameters, self._start_time, True)

            end = timer()

            self.print_summary(updated_parameters['Time'], end-start)

            if not repeat:
                self._commands.pop(list(self._commands.keys())[0])
            
            # Update the new start time!
            self._start_time += updated_parameters["Time"]

        if self._commands:
            logger.info("Did not finish all of the commands, will save the remaining commands")
            if "Resubmit" in self._raw_parameters.keys():
                if self._raw_parameters["Resubmit"]:
                    self._resub = True

        else:
            logger.debug("Finished all commands...writing final dmdinput.json")

        logger.debug("Setting remaining commands to the rest of the commands")
       
        self._raw_parameters["Remaining Commands"] = self._commands
        if self._titration is not None and self._commands:
            logger.debug("Condensing any commands remaining from the titratable feature")
            self._raw_parameters = self._titration.condense_commands(self._raw_parameters)

        elif self._titration is not None:
            self._raw_parameters["Commands"].clear()

        with open("dmdinput.json", 'w') as dmdinput:
            logger.debug("Dumping to json")
            json.dump(self._raw_parameters, dmdinput, indent=4)


        if self._scratch_directory != self._submit_directory:
            utilities.copy_directories(self._scratch_directory, self._submit_directory)
            os.chdir(self._submit_directory)
            shutil.rmtree(self._scratch_directory)

        if self._resub:
            logger.info("Resubmitting the job!")
            submitdmd.main(_cores=self._cores, _time=self._time_to_run)

        if os.path.isdir("dmd_backup"):
            shutil.rmtree("dmd_backup")

        if os.path.isfile("remaining_commands.json"):
            os.remove("remaining_commands.json")