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
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)
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)
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
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
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
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
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
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()