Beispiel #1
0
class NMaxWatersLigProt(RecordPortsMixin, ComputeCube):
    title = "NMax Waters"
    # version = "0.1.4"
    classification = [["Analysis"]]
    tags = ['Ligand', 'Protein', 'Waters']

    description = """
    This Cube determines the max number of waters for all the ligands that
    fits between the protein and ligand molecular surfaces. The cutoff distance
    parameters determines the max distance used between the volume grid points
    and the ligand-protein.
    """

    uuid = "c8608012-dc09-47de-8d23-fe2338419ff3"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Prohibited"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    cutoff = parameters.DecimalParameter(
        'cutoff',
        default=5.0,
        help_text=
        "Cutoff Distance between Volume grid points and ligand-protein in A")

    explicit_water = parameters.BooleanParameter(
        'explicit_water',
        default=False,
        help_text="""Enable MMPBSA calculation with explicit water""")

    water_number = parameters.IntegerParameter(
        'water_number',
        default=0,
        help_text=
        """If different from zero the selected water number will be used""")

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log
        self.nwaters = list()
        self.records = list()

    def process(self, record, port):
        try:
            mdrecord = MDDataRecord(record)

            protein = mdrecord.get_protein

            protein, ligand, water, exc = oeutils.split(protein,
                                                        ligand_res_name='LIG')

            if protein.NumAtoms() == 0:
                raise ValueError("The Protein Atom number is zero")

            ligand = mdrecord.get_ligand

            if self.opt['explicit_water']:

                if self.opt['water_number'] != 0:
                    self.opt['Logger'].info(
                        "User selected number of waters: {}".format(
                            self.opt['water_number']))
                    nmax = self.opt['water_number']
                else:
                    nmax = nmax_waters(protein, ligand, self.opt['cutoff'])
            else:
                self.opt['Logger'].info("MMPBSA Explicit Water set off")
                nmax = 0

            self.nwaters.append(nmax)
            self.records.append(record)

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return

    def end(self):

        max_waters = max(self.nwaters)

        if max_waters == 0:
            self.opt['Logger'].warn("[{}] Max number of waters is zero".format(
                self.title))
        else:
            self.opt['Logger'].info("[{}] Max number of Waters: {}".format(
                self.title, max_waters))

        for rec in self.records:
            rec.set_value(Fields.Analysis.max_waters, max_waters)
            self.success.emit(rec)

        return
Beispiel #2
0
class LigandChargeCube(RecordPortsMixin, ComputeCube):
    title = "Ligand Charge"
    # version = "0.1.4"
    classification = [["System Preparation"]]
    tags = ["Ligand"]
    description = """
    This Cube charges small organic molecules by using the ELF10 charge method 
    (based on am1bcc method). If the ligands are already charged and the user would 
    like to skip this stage the cube parameter “charge_ligand” can be used. 
    The cube requires a record as input with small organic molecules to be charged 
    and produces a new record with the charged molecules.
    """

    uuid = "ea184f6e-feb8-46f1-a89a-6b87270063a3"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {"default": 14000},
        "spot_policy": {"default": "Allowed"},
        "prefetch_count": {"default": 1},  # 1 molecule at a time
        "item_count": {"default": 1}  # 1 molecule at a time
    }

    max_conformers = parameters.IntegerParameter(
        'max_conformers',
        default=800,
        help_text="Max number of ligand conformers generated to charge the ligands")

    charge_ligands = parameters.BooleanParameter(
        'charge_ligands',
        default=True,
        description='Flag used to set if charge the ligands or not')

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log

    def process(self, record, port):
        try:

            if not record.has_value(Fields.primary_molecule):
                raise ValueError("Missing Primary Molecule field")

            ligand = record.get_value(Fields.primary_molecule)

            # Charge the ligand
            if self.opt['charge_ligands']:
                charged_ligand = ff_utils.assignELF10charges(ligand,
                                                             self.opt['max_conformers'],
                                                             strictStereo=False,
                                                             opt=self.opt)

                # If the ligand has been charged then transfer the computed
                # charges to the starting ligand
                map_charges = {at.GetIdx(): at.GetPartialCharge() for at in charged_ligand.GetAtoms()}
                for at in ligand.GetAtoms():
                    at.SetPartialCharge(map_charges[at.GetIdx()])
                self.log.info("[{}] Charges successfully applied to the ligand: {}".format(self.title,
                                                                                           ligand.GetTitle()))

            # Set the primary molecule with the newly charged molecule
            record.set_value(Fields.primary_molecule, ligand)

            self.success.emit(record)

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)
Beispiel #3
0
class GromacsRunCube(RecordPortsMixin, ComputeCube):
    uuid = "50310c8b-4e3a-4f9d-8853-4983363bc247"
    # version = "0.1.4"
    title = "Gromacs Run Cube"
    description = """
    This Cube runs Gromacs 
    """
    classification = [["Gromacs"]]
    tags = ["Gromacs", "MD"]

    # Override defaults for some parameters
    parameter_overrides = {
        "gpu_count": {
            "default": 1
        },
        "instance_type": {
            "default": "!g4"
        },  # Gpu Family selection
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Prohibited"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    cube_run_time = parameters.DecimalParameter(
        "cube_run_time",
        default=10.0,
        help_text="Gromacs Max Cube Running Time in hrs")

    verbose = parameters.BooleanParameter(
        "verbose",
        default=False,
        help_text="Enable/Disable Gromacs Std Output")

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log

    def process(self, record, port):

        opt = dict(self.opt)

        if not record.has_value(Fields.cycle_id):
            raise ValueError("Missing the current cycle ID")

        cycle_id = record.get_value(Fields.cycle_id)
        opt['cycle_id'] = cycle_id

        if not record.has_value(Fields.tpr_field):
            raise ValueError("Gromacs Tpr field is missing")

        tpr = record.get_value(Fields.tpr_field)
        opt['tpr'] = tpr

        if not record.has_value(Fields.prefix_name_field):
            raise ValueError("System prefix name is missing")

        opt['prefix_name'] = record.get_value(Fields.prefix_name_field)

        opt['record'] = record

        current_md_steps = gmx_run(opt)

        # Update the current iterations and the cycles
        record.set_value(Fields.current_iteration_field, current_md_steps)
        record.set_value(Fields.cycle_id, cycle_id + 1)

        self.success.emit(record)
Beispiel #4
0
class TrajPBSACube(RecordPortsMixin, ComputeCube):
    title = "Trajectory Poisson-Boltzmann and Surface Area Energies"
    # version = "0.1.4"
    classification = [["Analysis"]]
    tags = ['OEChem', 'Zap', 'TrajAnalysis', 'MMPBSA']
    description = """
    Protein-ligand interaction solvation energies are calculated on an existing MD trajectory.
    The trajectory is taken from pre-existing protein and ligand trajectory OEMols.
    The Poisson-Boltzmann and Surface Area methods in the OEZap toolkits are  used.
    The various energy components associated with the Poisson-Boltzmann and
    Surface Area energies are attached to the record as per-frame vectors of floats.
    The energy units are in kcal/mol.
    """

    uuid = "f6c96295-51fd-42df-8763-0f3b6f6d0e0d"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Allowed"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    explicit_water = parameters.BooleanParameter(
        'explicit_water',
        default=False,
        help_text="""Enable MMPBSA calculation with explicit water""")

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log

    def process(self, record, port):
        try:
            opt = self.opt
            # Logger string
            opt['Logger'].info(' Beginning TrajPBSACube')
            system_title = utl.RequestOEFieldType(record, Fields.title)
            opt['Logger'].info(
                '{} Attempting to compute MD Traj PBSA energies'.format(
                    system_title))

            # Check that the OETraj analysis has been done
            analysesDone = utl.RequestOEFieldType(record,
                                                  Fields.Analysis.analysesDone)
            if 'OETraj' not in analysesDone:
                raise ValueError(
                    '{} does not have OETraj analyses done'.format(
                        system_title))
            else:
                opt['Logger'].info(
                    '{} found OETraj analyses'.format(system_title))

            # Extract the relevant traj OEMols from the OETraj record
            oetrajRecord = utl.RequestOEFieldType(record,
                                                  Fields.Analysis.oetraj_rec)
            opt['Logger'].info('{} found OETraj record'.format(system_title))
            ligTraj = utl.RequestOEField(oetrajRecord, 'LigTraj',
                                         Types.Chem.Mol)
            opt['Logger'].info(
                '{} #atoms, #confs in ligand traj OEMol: {}, {}'.format(
                    system_title, ligTraj.NumAtoms(), ligTraj.NumConfs()))

            mdtrajrecord = MDDataRecord(oetrajRecord)

            if self.opt['explicit_water']:

                water_traj = oetrajRecord.get_value(
                    OEField('WatTraj', Types.Chem.Mol))
                opt['Logger'].info(
                    '{} #atoms, #confs in water traj OEMol: {}, {}'.format(
                        system_title, water_traj.NumAtoms(),
                        water_traj.NumConfs()))

                protTraj = mdtrajrecord.get_protein_traj

                prot_wat = oechem.OEMol(protTraj.GetActive())
                oechem.OEAddMols(prot_wat, water_traj.GetActive())

                prot_wat.DeleteConfs()

                for pr_conf, wat_conf in zip(protTraj.GetConfs(),
                                             water_traj.GetConfs()):
                    pr_wat_conf = oechem.OEMol(pr_conf)
                    oechem.OEAddMols(pr_wat_conf, wat_conf)
                    pr_wat_conf_xyz = oechem.OEFloatArray(prot_wat.NumAtoms() *
                                                          3)
                    pr_wat_conf.GetCoords(pr_wat_conf_xyz)
                    prot_wat.NewConf(pr_wat_conf_xyz)

                protTraj = prot_wat
            else:
                protTraj = mdtrajrecord.get_protein_traj

            opt['Logger'].info(
                '{} #atoms, #confs in protein traj OEMol: {}, {}'.format(
                    system_title, protTraj.NumAtoms(), protTraj.NumConfs()))

            # Compute PBSA energies for the protein-ligand complex
            PBSAdata = mmpbsa.TrajPBSA(ligTraj, protTraj)
            if PBSAdata is None:
                raise ValueError(
                    '{} Calculation of PBSA energies failed'.format(
                        system_title))

            # generate Surface Areas energy for buried SA based on 0.006 kcal/mol/A^2
            PBSAdata['OEZap_SA6_Bind'] = [
                sa * -0.006 for sa in PBSAdata['OEZap_BuriedArea']
            ]

            # If the OETraj Interaction Energies has been done calculate MMPBSA values
            if 'TrajIntE' in analysesDone:
                opt['Logger'].info(
                    '{} found TrajIntE analyses'.format(system_title))

                # Extract the relevant P-L Interaction Energies from the record
                intEdata = record.get_value(Fields.Analysis.oeintE_dict)
                opt['Logger'].info(
                    '{} found Traj intEdata data'.format(system_title))

                if self.opt['explicit_water']:

                    PLIntE = intEdata['protein_and_water_ligand_interE']
                    opt['Logger'].info(
                        '{} found Protein-Water and Ligand force field interaction energies'
                        .format(system_title))
                else:

                    PLIntE = intEdata['protein_ligand_interE']
                    opt['Logger'].info(
                        '{} found Protein-Ligand force field interaction energies'
                        .format(system_title))

                # Calculate  and store MMPB and MMPBSA energies on the trajPBSA record
                PBSAdata['OEZap_MMPB_Bind'] = [
                    eInt + eDesol for eInt, eDesol in zip(
                        PLIntE, PBSAdata['OEZap_PB_Desolvation'])
                ]
                PBSAdata['OEZap_MMPBSA6_Bind'] = [
                    eMMPB + eSA6
                    for eMMPB, eSA6 in zip(PBSAdata['OEZap_MMPB_Bind'],
                                           PBSAdata['OEZap_SA6_Bind'])
                ]

            # list field and change any NaNs to a really big float
            for key in PBSAdata.keys():
                opt['Logger'].info(
                    '{} TrajPBSACube PBSAdata[{}] of length {}'.format(
                        system_title, key, len(PBSAdata[key])))
                # change any NaNs to a really big float or else Orion WriterCube fails on JSON dict
                for i, x in enumerate(PBSAdata[key]):
                    if math.isnan(x):
                        opt['Logger'].info(
                            '{} found a NaN at PBSAdata[{}][{}]'.format(
                                system_title, key, i))
                        PBSAdata[key][i] = magic_big_float_to_replace_NaN

            # Add the PBSAdata dict to the record
            record.set_value(Fields.Analysis.oepbsa_dict, PBSAdata)

            analysesDone.append('TrajPBSA')
            record.set_value(Fields.Analysis.analysesDone, analysesDone)
            opt['Logger'].info(
                '{} finished writing TrajPBSA OERecord'.format(system_title))

            self.success.emit(record)

            del mdtrajrecord

        except Exception as e:
            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} in TrajPBSACube'.format(
                str(e)))
            self.log.error(traceback.format_exc())
            # Return failed mol
            self.failure.emit(record)

        return
Beispiel #5
0
class MDNptCube(RecordPortsMixin, ComputeCube):
    title = 'NPT Cube'
    # version = "0.1.4"
    classification = [['MD Simulations']]
    tags = ['Gromacs', 'OpenMM', 'NPT']

    description = """
    This Cube performs MD simulation in the NPT ensemble on the provided system. 
    The system must have been parametrized by the Force Field cube and the system Parmed 
    object must be present on the input record. In addition, a system identification 
    number must be present on the input record as well. This can be accomplished 
    by using the “ID Setting Cube”. The NPT MD simulation is performed by the selected 
    MD engine, currently OpenMM and Gromacs only. Restraints and constraints can be 
    used as well. Currently implicit solvent models can be used in OpenMM only. 
    The cube requires a record as input and produces a new record with the time evolved 
    system. The total sampling time can be set by using the “time” cube parameter 
    while the trajectory snapshots can be set by using the “trajectory_interval” cube 
    parameter.
    """

    uuid = "602d397b-d8a5-4388-a94a-ac3a54ff3bad"

    # Override defaults for some parameters
    parameter_overrides = {
        "gpu_count": {
            "default": 1
        },
        "instance_type": {
            "default": "g3.4xlarge"
        },  # Gpu Family selection
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Allowed"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    temperature = parameters.DecimalParameter('temperature',
                                              default=300.0,
                                              help_text="Temperature (Kelvin)")

    pressure = parameters.DecimalParameter('pressure',
                                           default=1.0,
                                           help_text="Pressure (atm)")

    time = parameters.DecimalParameter(
        'time', default=0.01, help_text="NPT simulation time in nanoseconds")

    restraints = parameters.StringParameter(
        'restraints',
        default='',
        help_text=""""Mask selection to apply harmonic restraints. 
        Possible keywords are: ligand, protein, water, ions, 
        ca_protein, cofactors. The selection can be refined 
        by using logical tokens: not, noh, and, or, diff, around""")

    restraintWt = parameters.DecimalParameter(
        'restraintWt',
        default=2.0,
        help_text="Restraint weight for xyz atom restraints in kcal/(mol A^2)")

    restraint_to_reference = parameters.BooleanParameter(
        'restraint_to_reference',
        default=True,
        help_text=
        'If True the starting reference system coordinates will be used '
        'to restraint the system')

    freeze = parameters.StringParameter(
        'freeze',
        default='',
        help_text="""Mask selection to freeze atoms along the MD simulation.
        Possible keywords are: ligand, protein, water, ions, ca_protein,
        cofactors. The selection can be refined by using logical tokens:
        not, noh, and, or, diff, around. Not currently implemented in Gromacs"""
    )

    nonbondedCutoff = parameters.DecimalParameter(
        'nonbondedCutoff',
        default=10,
        help_text="""The non-bonded cutoff in angstroms.
        This is ignored if non-bonded method is NoCutoff""")

    constraints = parameters.StringParameter(
        'constraints',
        default='H-Bonds',
        choices=['None', 'H-Bonds', 'H-Angles', 'All-Bonds'],
        help_text="""None, H-Bonds, H-Angles, or All-Bonds
        Which type of constraints to add to the system.
        None means no bonds are constrained.
        HBonds means bonds with hydrogen are constrained, etc.""")

    implicit_solvent = parameters.StringParameter(
        'implicit_solvent',
        default='None',
        choices=['None', 'HCT', 'OBC1', 'OBC2', 'GBn', 'GBn2'],
        help_text="Implicit Solvent Model. Not Currently implemented in Gromacs"
    )

    trajectory_interval = parameters.DecimalParameter(
        'trajectory_interval',
        default=0.0,
        help_text="""Time interval for trajectory snapshots in ns. 
        If 0 the trajectory file will not be generated""")

    reporter_interval = parameters.DecimalParameter(
        'reporter_interval',
        default=0.0,
        help_text="""Time interval for reporting data in ns. 
        If 0 the reporter file will not be generated""")

    trajectory_frames = parameters.IntegerParameter(
        'trajectory_frames',
        default=0,
        help_text="""The total number of trajectory frames. If it is 
            set to zero and the trajectory interval parameter is set 
            to zero no trajectory is generated. If it is different from zero 
            and the trajectory interval parameter is set to zero the produced 
            trajectory will have the selected number of frames. If different
            from zero and the trajectory interval parameter is different from 
            zero the total number of generated frames will be calculated by just 
            using the trajectory interval and the md time step (2fs and 4fs hmr on)"""
    )

    suffix = parameters.StringParameter(
        'suffix',
        default='npt',
        help_text='Filename suffix for output simulation files')

    center = parameters.BooleanParameter(
        'center',
        default=False,
        help_text='Center the system to the OpenMM unit cell')

    verbose = parameters.BooleanParameter(
        'verbose', default=True, help_text='Increase log file verbosity')

    hmr = parameters.BooleanParameter(
        'hmr',
        default=True,
        help_text=
        'On enables Hydrogen Mass Repartitioning. Not currently implemented in Gromacs'
    )

    save_md_stage = parameters.BooleanParameter(
        'save_md_stage',
        default=False,
        help_text="""Save the MD simulation stage. If True the MD,
           simulation data will be appended to the md simulation stages 
           otherwise the last MD stage will be overwritten""")

    md_engine = parameters.StringParameter(
        'md_engine',
        default='OpenMM',
        choices=['OpenMM', 'Gromacs'],
        help_text='Select the MD available engine')

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log
        self.opt['SimType'] = 'npt'

        return

    def process(self, record, port):
        try:
            # The copy of the dictionary option as local variable
            # is necessary to avoid filename collisions due to
            # the parallel cube processes
            opt = dict(self.opt)
            opt['CubeTitle'] = self.title
            # Logger string
            str_logger = '-' * 32 + ' NPT CUBE PARAMETERS ' + '-' * 32
            str_logger += "\n{:<25} = {:<10}".format("Cube Title",
                                                     opt['CubeTitle'])

            for k, v in sorted(self.parameters().items()):
                tmp_default = copy.deepcopy(v)

                if v.default is None:
                    tmp_default.default = 'None'
                elif isinstance(v, parameters.BooleanParameter):
                    if v.default:
                        tmp_default.default = 'True'
                    else:
                        tmp_default.default = 'False'
                else:
                    tmp_description = textwrap.fill(" ".join(
                        v.description.split()),
                                                    subsequent_indent=' ' * 39,
                                                    width=80)
                    str_logger += "\n{:<25} = {:<10} {}".format(
                        k, getattr(self.args, tmp_default.name),
                        tmp_description)

            str_logger += "\n{:<25} = {:<10}".format("Simulation Type",
                                                     opt['SimType'])

            # Create the MD record to use the MD Record API
            mdrecord = MDDataRecord(record)

            system_title = mdrecord.get_title

            opt['system_title'] = system_title
            opt['system_id'] = mdrecord.get_flask_id

            flask = mdrecord.get_stage_topology()
            mdstate = mdrecord.get_stage_state()

            # Update cube simulation parameters
            for field in record.get_fields(include_meta=True):
                field_name = field.get_name()
                if field_name in ['temperature', 'pressure']:
                    rec_value = record.get_value(field)
                    opt[field_name] = rec_value
                    opt['Logger'].info(
                        "{} Updating parameters for molecule: {} {} = {}".
                        format(self.title, system_title, field_name,
                               rec_value))

            if opt['restraint_to_reference']:
                opt['reference_state'] = mdrecord.get_stage_state(
                    stg_name=MDStageNames.ForceField)

            opt['out_directory'] = mdrecord.cwd
            opt['molecule'] = flask
            opt['str_logger'] = str_logger
            opt['Logger'].info('[{}] START NPT SIMULATION: {}'.format(
                opt['CubeTitle'], system_title))

            opt['out_fn'] = os.path.basename(opt['out_directory']) + '_' + \
                            opt['system_title'] + '_' + \
                            str(opt['system_id']) + '-' + \
                            opt['suffix']

            # Trajectory file name if any generated
            opt['trj_fn'] = opt['out_fn'] + '_' + 'traj.tar.gz'

            # Extract the Parmed structure and synchronize it with the last MD stage state
            parmed_structure = mdrecord.get_parmed(sync_stage_name='last')

            # Run the MD simulation
            new_mdstate = md_simulation(mdstate, parmed_structure, opt)

            # Update the system coordinates
            flask.SetCoords(new_mdstate.get_oe_positions())
            mdrecord.set_flask(flask)

            # Trajectory
            if opt['trajectory_interval'] or opt['trajectory_frames']:
                trajectory_fn = opt['trj_fn']
                if opt['md_engine'] == MDEngines.OpenMM:
                    trajectory_engine = MDEngines.OpenMM
                else:
                    trajectory_engine = MDEngines.Gromacs

            else:  # Empty Trajectory
                trajectory_fn = None
                trajectory_engine = None

            data_fn = opt['out_fn'] + '.tar.gz'

            if not mdrecord.add_new_stage(
                    self.title,
                    MDStageTypes.NPT,
                    flask,
                    new_mdstate,
                    data_fn,
                    append=opt['save_md_stage'],
                    log=opt['str_logger'],
                    trajectory_fn=trajectory_fn,
                    trajectory_engine=trajectory_engine,
                    trajectory_orion_ui=opt['system_title'] + '_' +
                    str(opt['system_id']) + '-' + opt['suffix'] + '.tar.gz'):

                raise ValueError("Problems adding in the new NPT Stage")

            self.success.emit(mdrecord.get_record)

            del mdrecord

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return
Beispiel #6
0
class MDMinimizeCube(RecordPortsMixin, ComputeCube):
    title = 'Minimization Cube'

    # version = "0.1.4"
    classification = [["MD Simulations"]]
    tags = ['OpenMM', 'Gromacs', 'Minimization']

    description = """
    This Cube performs energy minimization on the provided system. The system 
    must have been parametrized by the Force Field cube and the system Parmed 
    object must be present on the input record. In addition, a system identification 
    number must be present on the input record as well. This can be accomplished 
    by using the “ID Setting Cube”. The system minimization is performed by 
    the selected MD engine, currently OpenMM and Gromacs only. Restraints 
    and constraints can be used as well. Currently implicit solvent models can 
    be used in OpenMM only. The cube requires a record as input and produces 
    a new record with the minimized system.
    """

    uuid = "bdfeaabe-f93b-4a14-9754-d6ca0c18a009"

    # Override defaults for some parameters
    parameter_overrides = {
        "gpu_count": {
            "default": 1
        },
        "instance_type": {
            "default": "g3.4xlarge"
        },  # Gpu Family selection
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Allowed"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    steps = parameters.IntegerParameter(
        'steps',
        default=2000,
        help_text="""Number of minimization steps.
                  If 0 the minimization will continue
                  until convergence""")

    restraints = parameters.StringParameter(
        'restraints',
        default='',
        help_text=""""Mask selection to apply harmonic restraints. 
        Possible keywords are: ligand, protein, water, ions, 
        ca_protein, cofactors. The selection can be refined 
        by using logical tokens: not, noh, and, or, diff, around""")

    restraintWt = parameters.DecimalParameter(
        'restraintWt',
        default=5.0,
        help_text="Restraint weight for xyz atom restraints in kcal/(mol A^2)")

    restraint_to_reference = parameters.BooleanParameter(
        'restraint_to_reference',
        default=True,
        help_text=
        'If True the starting reference system coordinates will be used '
        'to restraint the system')

    freeze = parameters.StringParameter(
        'freeze',
        default='',
        help_text="""Mask selection to freeze atoms along the MD
        simulation. Possible keywords are: ligand, protein, water,
        ions, ca_protein, cofactors. The selection can be refined by
        using logical tokens: not, noh, and, or, diff, around. NOTE:
        Not currently implemented in Gromacs""")

    temperature = parameters.DecimalParameter('temperature',
                                              default=300,
                                              help_text="Temperature (Kelvin)")

    nonbondedCutoff = parameters.DecimalParameter(
        'nonbondedCutoff',
        default=10,
        help_text="""The non-bonded cutoff in angstroms.
        This is ignored if the non-bonded method is NoCutoff""")

    constraints = parameters.StringParameter(
        'constraints',
        default='H-Bonds',
        choices=['None', 'H-Bonds', 'H-Angles', 'All-Bonds'],
        help_text="""None, H-Bonds, H-Angles, or All-Bonds
        Which type of constraints to add to the system.
        None means no bonds are constrained.
        H-Bonds means bonds with hydrogen are constrained, etc.""")

    implicit_solvent = parameters.StringParameter(
        'implicit_solvent',
        default='None',
        choices=['None', 'HCT', 'OBC1', 'OBC2', 'GBn', 'GBn2'],
        help_text="Implicit Solvent Model. NOTE:"
        "Not currently implemented in Gromacs")

    center = parameters.BooleanParameter(
        'center',
        default=True,
        description='Center the system to the OpenMM and Gromacs unit cell')

    verbose = parameters.BooleanParameter(
        'verbose', default=True, description='Increase log file verbosity')

    suffix = parameters.StringParameter(
        'suffix',
        default='min',
        help_text='Filename suffix for output simulation files')

    hmr = parameters.BooleanParameter(
        'hmr',
        default=False,
        description='On enables Hydrogen Mass Repartitioning. NOTE:'
        'Not currently implemented in Gromacs')

    save_md_stage = parameters.BooleanParameter(
        'save_md_stage',
        default=True,
        help_text="""Save the MD simulation stage. If True the MD,
           simulation data will be appended to the md simulation stages 
           otherwise the last MD stage will be overwritten""")

    md_engine = parameters.StringParameter(
        'md_engine',
        default='OpenMM',
        choices=['OpenMM', 'Gromacs'],
        help_text='Select the MD available engine')

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log
        self.opt['SimType'] = 'min'
        return

    def process(self, record, port):
        try:
            # The copy of the dictionary option as local variable
            # is necessary to avoid filename collisions due to
            # the parallel cube processes
            opt = dict(self.opt)
            opt['CubeTitle'] = self.title

            # Logger string
            str_logger = '-' * 32 + ' MIN CUBE PARAMETERS ' + '-' * 32
            str_logger += "\n{:<25} = {:<10}".format("Cube Title",
                                                     opt['CubeTitle'])

            for k, v in sorted(self.parameters().items()):
                tmp_default = copy.deepcopy(v)

                if v.default is None:
                    tmp_default.default = 'None'
                elif isinstance(v, parameters.BooleanParameter):
                    if v.default:
                        tmp_default.default = 'True'
                    else:
                        tmp_default.default = 'False'
                else:
                    tmp_description = textwrap.fill(" ".join(
                        v.description.split()),
                                                    subsequent_indent=' ' * 39,
                                                    width=80)
                    str_logger += "\n{:<25} = {:<10} {}".format(
                        k, getattr(self.args, tmp_default.name),
                        tmp_description)

            str_logger += "\n{:<25} = {:<10}".format("Simulation Type",
                                                     opt['SimType'])

            # Create the MD record to use the MD Record API
            mdrecord = MDDataRecord(record)

            system_title = mdrecord.get_title

            opt['system_title'] = system_title
            opt['system_id'] = mdrecord.get_flask_id

            flask = mdrecord.get_stage_topology()
            mdstate = mdrecord.get_stage_state()

            if opt['restraint_to_reference']:
                opt['reference_state'] = mdrecord.get_stage_state(
                    stg_name=MDStageNames.ForceField)

            opt['out_directory'] = mdrecord.cwd
            opt['molecule'] = flask
            opt['str_logger'] = str_logger
            opt['Logger'].info('[{}] MINIMIZING System: {}'.format(
                opt['CubeTitle'], system_title))

            # Extract the Parmed structure and synchronize it with the last MD stage state
            parmed_structure = mdrecord.get_parmed(sync_stage_name='last')

            # Run the MD simulation
            new_mdstate = md_simulation(mdstate, parmed_structure, opt)

            # Update the flask coordinates
            flask.SetCoords(new_mdstate.get_oe_positions())
            mdrecord.set_flask(flask)

            data_fn = os.path.basename(
                mdrecord.cwd) + '_' + opt['system_title'] + '_' + str(
                    opt['system_id']) + '-' + opt['suffix'] + '.tar.gz'

            if not mdrecord.add_new_stage(self.title,
                                          MDStageTypes.MINIMIZATION,
                                          flask,
                                          new_mdstate,
                                          data_fn,
                                          append=opt['save_md_stage'],
                                          log=opt['str_logger']):

                raise ValueError("Problems adding the new Minimization Stage")

            self.success.emit(mdrecord.get_record)

            del mdrecord

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return
Beispiel #7
0
class MDComponentCube(RecordPortsMixin, ComputeCube):
    title = "MD Setting"
    # version = "0.1.4"
    classification = [["System Preparation"]]
    tags = ['Protein']
    description = """
    This Cube is used to componentize the cube input system.
    The cube detects if a Design Unit (DU) is present on the record 
    and it will extract the DU components in an ad-hoc container 
    (MDComponents). If the DU is not found on the input record, 
    the cube will try to create a DU by using the primary molecule
    present on the record; if it fails the primary molecule 
    will be split in components by using a more canonical splitting 
    function. 
    """

    uuid = "b85d652f-188a-4cc0-aefd-35c98e737f8d"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Prohibited"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    flask_title = parameters.StringParameter('flask_title',
                                             default='',
                                             help_text='Flask Title')

    multiple_flasks = parameters.BooleanParameter(
        'multiple_flasks',
        default=False,
        help_text="If Checked/True multiple flasks will be allowed")

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log
        self.count = 0
        self.opt['CubeTitle'] = self.title

    def process(self, record, port):
        try:

            if self.count > 0 and not self.opt['multiple_flasks']:
                raise ValueError("Multiple Flasks have been Detected")

            name = self.opt['flask_title']

            if record.has_value(Fields.design_unit_from_spruce):

                du = record.get_value(Fields.design_unit_from_spruce)

                self.opt['Logger'].info("[{}] Design Unit Detected".format(
                    self.title))

                if not name:
                    title_first12 = du.GetTitle()[0:12]

                    if title_first12:
                        name = title_first12
                    else:
                        name = 'Flask'

                md_components = MDComponents(du, components_title=name)

            else:  # The extended protein is already prepared to MD standard

                if not record.has_value(Fields.primary_molecule):
                    raise ValueError("Missing Primary Molecule field")

                molecules = record.get_value(Fields.primary_molecule)

                if not name:
                    title_first12 = molecules.GetTitle()[0:12]

                    if title_first12:
                        name = title_first12
                    else:
                        name = 'protein'

                md_components = MDComponents(molecules, components_title=name)

            # self.opt['Logger'].info(md_components.get_info)

            record.set_value(Fields.md_components, md_components)
            record.set_value(Fields.title, name)
            record.set_value(Fields.flaskid, self.count)

            self.count += 1

            self.success.emit(record)

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return
Beispiel #8
0
class SolvationCube(RecordPortsMixin, ComputeCube):
    title = "Solvation Packmol"
    # version = "0.1.4"
    classification = [["System Preparation"]]
    tags = ['Complex', 'Protein', 'Ligand', 'Solvation']
    description = """
    The solvation cube solvates a given solute input system by a
    periodic box of a solvent or a
    selected mixture of solvents. The solvents can be specified by
    comma separated smiles strings of each solvent component or
    selected keywords like tip3p for tip3p water geometry. For each
    component the user needs to specify its molar fractions as well.
    The solution can be neutralized by adding counter-ions. In addition,
    the ionic solution strength can be set adding salt. The cube
    requires a record as input with a solute molecule to solvate
    and produces an output record with the solvated solute.
    """

    uuid = "2e6130f6-2cba-48a4-9ef3-351a2970258a"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Allowed"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    density = parameters.DecimalParameter('density',
                                          default=1.03,
                                          help_text="Solution density in g/ml")

    padding_distance = parameters.DecimalParameter(
        'padding_distance',
        default=8.0,
        help_text=
        "The padding distance between the solute and the box edge in A")

    distance_between_atoms = parameters.DecimalParameter(
        'distance_between_atoms',
        default=2.0,
        help_text="The minimum distance between atoms in A")

    solvents = parameters.StringParameter(
        'solvents',
        default='tip3p',
        help_text=
        'Select solvents. The solvents are specified as comma separated smiles strings'
        'e.g. [H]O[H], C(Cl)(Cl)Cl, CS(=O)C or special keywords like tip3p')

    molar_fractions = parameters.StringParameter(
        'molar_fractions',
        default='1.0',
        help_text=
        "Molar fractions of each solvent components. The molar fractions are specified"
        "as comma separated molar fractions strings e.g. 0.5,0.2,0.3")

    verbose = parameters.BooleanParameter('verbose',
                                          default=False,
                                          help_text='Output Packmol log')

    geometry = parameters.StringParameter(
        'geometry',
        default='box',
        choices=['box', 'sphere'],
        help_text=
        "Geometry selection: box or sphere. Sphere cannot be used as periodic system "
        "along with MD simulation")

    close_solvent = parameters.BooleanParameter(
        'close_solvent',
        default=False,
        help_text=
        "If Checked/True solvent molecules will be placed very close to the solute"
    )

    salt = parameters.StringParameter(
        'salt',
        default='[Na+], [Cl-]',
        help_text='Salt type. The salt is specified as list of smiles strings. '
        'Each smiles string is the salt component dissociated in the '
        'solution e.g. Na+, Cl-')

    salt_concentration = parameters.DecimalParameter(
        'salt_concentration',
        default=50.0,
        help_text="Salt concentration in millimolar")

    neutralize_solute = parameters.BooleanParameter(
        'neutralize_solute',
        default=True,
        help_text=
        'Neutralize the solute by adding Na+ and Cl- counter-ions based on'
        'the solute formal charge')

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log

    def process(self, record, port):

        try:
            opt = dict(self.opt)

            if not record.has_value(Fields.md_components):
                raise ValueError("Missing the MD Components Field")

            md_components = record.get_value(Fields.md_components)

            solute, map_comp = md_components.create_flask

            if not record.has_value(Fields.title):
                self.log.warn("Missing Title field")
                solute_title = solute.GetTitle()[0:12]
            else:
                solute_title = record.get_value(Fields.title)

            self.log.info("[{}] solvating flask {}".format(
                self.title, solute_title))

            # Update cube simulation parameters
            for field in record.get_fields(include_meta=True):
                field_name = field.get_name()
                if field_name in ['molar_fractions', 'density', 'solvents']:
                    rec_value = record.get_value(field)
                    if field_name == 'molar_fractions':
                        opt[field_name] = str(rec_value)
                    else:
                        opt[field_name] = rec_value
                    opt['Logger'].info(
                        "{} Updating parameters for molecule: {} {} = {}".
                        format(self.title, solute.GetTitle(), field_name,
                               rec_value))
            # Set the flag to return the solvent molecule components
            opt['return_components'] = True

            # Solvate the system
            sol_system, solvent, salt, counter_ions = packmol.oesolvate(
                solute, **opt)

            # Separate the Water from the solvent
            pred_water = oechem.OEIsWater(checkHydrogens=True)
            water = oechem.OEMol()
            oechem.OESubsetMol(water, solvent, pred_water)

            if water.NumAtoms():
                if md_components.has_water:

                    water_comp = md_components.get_water

                    if not oechem.OEAddMols(water_comp, water):
                        raise ValueError(
                            "Cannot add the MD Component Water and the Packmol Water"
                        )

                    md_components.set_water(water_comp)

                else:
                    md_components.set_water(water)

                pred_not_water = oechem.OENotAtom(
                    oechem.OEIsWater(checkHydrogens=True))
                solvent_not_water = oechem.OEMol()
                oechem.OESubsetMol(solvent_not_water, solvent, pred_not_water)

                if solvent_not_water.NumAtoms():
                    solvent = solvent_not_water
                else:
                    solvent = oechem.OEMol()

            self.log.info(
                "[{}] Solvated simulation flask {} yielding {} atoms overall".
                format(self.title, solute_title, sol_system.NumAtoms()))
            sol_system.SetTitle(solute.GetTitle())

            if salt is not None and counter_ions is not None:
                if not oechem.OEAddMols(counter_ions, salt):
                    raise ValueError(
                        "Cannot add the salt component and the counter ion component"
                    )
            elif salt is not None:
                counter_ions = salt
            else:
                pass

            if md_components.has_solvent:
                solvent_comp = md_components.get_solvent
                if not oechem.OEAddMols(solvent_comp, solvent):
                    raise ValueError(
                        "Cannot add the MD Component solvent and the Packmol Solvent"
                    )
            else:
                solvent_comp = solvent

            if solvent_comp.NumAtoms():
                md_components.set_solvent(solvent_comp)

            if counter_ions is not None:
                if md_components.has_counter_ions:
                    counter_ions_comp = md_components.get_counter_ions
                    if not oechem.OEAddMols(counter_ions_comp, counter_ions):
                        raise ValueError(
                            "Cannot add the MD Component counter ions and the Packmol counter ions"
                        )
                else:
                    counter_ions_comp = counter_ions

                md_components.set_counter_ions(counter_ions_comp)

            # Set Box Vectors
            vec_data = pack_utils.getData(sol_system, tag='box_vectors')
            box_vec = pack_utils.decodePyObj(vec_data)
            md_components.set_box_vectors(box_vec)

            flask, map_comp = md_components.create_flask
            record.set_value(Fields.md_components, md_components)
            record.set_value(Fields.flask, flask)
            record.set_value(Fields.title, solute_title)

            self.success.emit(record)

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return
Beispiel #9
0
class CollectionSetting(RecordPortsMixin, ComputeCube):
    title = "Collection Setting"
    # version = "0.1.4"
    classification = [["System Preparation"]]
    tags = ['System', 'Complex', 'Protein', 'Ligand']
    description = """
    This Cube sets a record collection state in open or closed for safety by
    using the cube bool parameter open. A True value will open the record
    collection enabling the shard writing and deleting. In Orion if on the record
    the collection field is not present one will be created.
    """

    uuid = "b3821952-a5ed-4028-867c-3f71185442aa"

    # Override defaults for some parameters
    parameter_overrides = {
        "memory_mb": {
            "default": 14000
        },
        "spot_policy": {
            "default": "Prohibited"
        },
        "prefetch_count": {
            "default": 1
        },  # 1 molecule at a time
        "item_count": {
            "default": 1
        }  # 1 molecule at a time
    }

    open = parameters.BooleanParameter('open',
                                       default=True,
                                       help_text='Open or Close a Collection')

    def begin(self):
        self.opt = vars(self.args)
        self.opt['Logger'] = self.log
        self.collection = None

    def process(self, record, port):
        try:

            if in_orion():

                session = APISession

                if record.has_value(Fields.collection):

                    if self.collection is None:

                        collection_id = record.get_value(Fields.collection)

                        collection = session.get_resource(
                            ShardCollection, collection_id)

                        self.collection = collection

                        if self.opt['open']:

                            if self.collection.state == "open":
                                pass
                            else:
                                self.collection.open()
                else:
                    if self.collection is None:

                        job_id = environ.get('ORION_JOB_ID')

                        self.collection = ShardCollection.create(
                            session, job_id)

                        job_id = environ.get('ORION_JOB_ID')

                        if job_id:
                            session.tag_resource(self.collection,
                                                 "Job {}".format(job_id))

                    record.set_value(Fields.collection, self.collection.id)

            self.success.emit(record)

        except Exception as e:

            print("Failed to complete", str(e), flush=True)
            self.opt['Logger'].info('Exception {} {}'.format(
                str(e), self.title))
            self.log.error(traceback.format_exc())
            self.failure.emit(record)

        return

    def end(self):
        if in_orion():
            if not self.opt['open']:
                if self.collection is not None:
                    if self.collection.state == "close":
                        pass
                    else:
                        self.collection.close()