def test_modeller(self): """Test addition of hydrogens to a PDB file using modeller.""" pdb = app.PDBFile( get_test_data("glu_ala_his_noH.pdb", "testsystems/tripeptides/")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") modeller = app.Modeller(pdb.topology, pdb.positions) modeller.addHydrogens(forcefield) # Should have 59 hydrogens after adding them assert modeller.topology.getNumAtoms() == 59 system = forcefield.createSystem(modeller.topology)
def test_create_system(self): """Create a system using the amber10 constph force field.""" cif = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides/")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( cif.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, )
def run_prep_ffxml_main(jsonfile): """Main simulation loop.""" log.info(f"Preparing a run from '{jsonfile}'") # TODO Validate json input with json schema? settings = yaml.load(open(jsonfile, "r")) log.debug(f"Loaded these settings. {settings}") try: format_vars: Dict[str, str] = settings["format_vars"] except KeyError: format_vars = dict() # Input files inp = settings["input"] idir = inp["dir"].format(**format_vars) # Make a ForceField object from user directed files # Look for block in input try: ff: Dict[str, List[str]] = settings["forcefield"] except KeyError: raise KeyError("No forcefield block specified") # Any files included with openmm/protons by default are retrieved here try: default_ff: List[str] = ff["default"] except KeyError: # In typical use case I wouldn't expect purely user files to be used. warn("'default' list missing from 'forcefield' block.", UserWarning) default_ff = [] # all user provided files here try: user_ff: List[str] = ff["user"] user_ff_paths: List[str] = [] for user_file in user_ff: rel_path = os.path.join(idir, user_file.format(**format_vars)) user_ff_paths.append(os.path.abspath(rel_path)) except KeyError: user_ff_paths = [] if len(default_ff) + len(user_ff_paths) == 0: raise ValueError("No forcefield files provided.") forcefield = app.ForceField(*(user_ff_paths + default_ff)) # Load structure # The input should be an mmcif/pdbx file' ifilename = inp["structure"].format(**format_vars) joined_path = os.path.join(idir, ifilename) input_pdbx_file = os.path.abspath(joined_path) pdb_object = app.PDBxFile(input_pdbx_file) # Atoms , connectivity, residues topology = pdb_object.topology # Store topology for serialization topology_file_content = open(input_pdbx_file, "r").read() # XYZ positions for every atom positions = pdb_object.positions # Quick fix for histidines in topology # Openmm relabels them HIS, which leads to them not being detected as # titratable. Renaming them fixes this. for residue in topology.residues(): if residue.name == "HIS": residue.name = "HIP" # TODO doublecheck if ASH GLH need to be renamed elif residue.name == "ASP": residue.name = "ASH" elif residue.name == "GLU": residue.name = "GLH" # Naming the output files out = settings["output"] odir = out["dir"].format(**format_vars) obasename = out["basename"].format(**format_vars) if not os.path.isdir(odir): os.makedirs(odir) lastdir = os.getcwd() os.chdir(odir) # File for resuming simulation output_checkpoint_file = f"{obasename}-checkpoint-0.xml" # Structure preprocessing settings if "preprocessing" in settings: preproc: Dict[str, Any] = settings["preprocessing"] # Steps of MD before starting the main loop num_thermalization_steps = int(preproc["num_thermalization_steps"]) pre_run_minimization_tolerance: unit.Quantity = float( preproc["minimization_tolerance_kjmol"] ) * unit.kilojoule / unit.mole minimization_max_iterations = int(preproc["minimization_max_iterations"]) # System Configuration sysprops = settings["system"] temperature = float(sysprops["temperature_k"]) * unit.kelvin if "salt_concentration_molar" in sysprops: salt_concentration: unit.Quantity = float( sysprops["salt_concentration_molar"] ) * unit.molar elif "PME" in sysprops: salt_concentration = 0.0 * unit.molar else: salt_concentration = None rigidWater = True constraints = app.HBonds if "PME" in sysprops: pmeprops = sysprops["PME"] nonbondedMethod = app.PME ewaldErrorTolerance = float(pmeprops["ewald_error_tolerance"]) barostatInterval = int(pmeprops["barostat_interval"]) switching_distance = float(pmeprops["switching_distance_nm"]) * unit.nanometers nonbondedCutoff = float(pmeprops["nonbonded_cutoff_nm"]) * unit.nanometers pressure = float(pmeprops["pressure_atm"]) * unit.atmosphere disp_corr = bool(pmeprops["dispersion_correction"]) system = forcefield.createSystem( topology, nonbondedMethod=nonbondedMethod, constraints=constraints, rigidWater=rigidWater, ewaldErrorTolerance=ewaldErrorTolerance, nonbondedCutoff=nonbondedCutoff, ) for force in system.getForces(): if isinstance(force, mm.NonbondedForce): force.setUseSwitchingFunction(True) force.setSwitchingDistance(switching_distance) force.setUseDispersionCorrection(disp_corr) # NPT simulation system.addForce(mm.MonteCarloBarostat(pressure, temperature, barostatInterval)) else: pressure = None system = forcefield.createSystem( topology, nonbondedMethod=app.NoCutoff, constraints=app.HBonds, rigidWater=True, ) # Integrator options integrator_opts = settings["integrator"] timestep = integrator_opts["timestep_fs"] * unit.femtosecond constraint_tolerance = integrator_opts["constraint_tolerance"] collision_rate = integrator_opts["collision_rate_per_ps"] / unit.picosecond number_R_steps = 1 integrator = ExternalGBAOABIntegrator( number_R_steps=number_R_steps, temperature=temperature, collision_rate=collision_rate, timestep=timestep, constraint_tolerance=constraint_tolerance, ) ncmc_propagation_integrator = ExternalGBAOABIntegrator( number_R_steps=number_R_steps, temperature=temperature, collision_rate=collision_rate, timestep=timestep, constraint_tolerance=constraint_tolerance, ) # Define a compound integrator compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmc_propagation_integrator) compound_integrator.setCurrentIntegrator(0) # Script specific settings if "importance" in settings: sampling_method = SamplingMethod.IMPORTANCE else: sampling_method = SamplingMethod.MCMC driver = ForceFieldProtonDrive( temperature, topology, system, forcefield, user_ff_paths + ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=100_000, propagations_per_step=1, ) if "reference_free_energies" in settings: # calibrated free energy values can be provided here gk_dict = settings["reference_free_energies"] # Clean comments inside dictionary to_delete = list() for key in gk_dict.keys(): if key.startswith("_"): to_delete.append(key) for key in to_delete: del (gk_dict[key]) # Make arrays for key, value in gk_dict.items(): # Convert to array of float val_array = np.asarray(value).astype(np.float64) if not val_array.ndim == 1: raise ValueError("Reference free energies should be simple lists.") gk_dict[key] = val_array driver.import_gk_values(gk_dict) # TODO allow platform specification for setup platform = mm.Platform.getPlatformByName("OpenCL") properties = {"OpenCLPrecision": "double"} # Set up calibration mode # SAMS settings if "SAMS" in settings and "importance" in settings: raise NotImplementedError("Cannot combine SAMS and importance sampling.") elif "SAMS" in settings: sams = settings["SAMS"] beta_burnin = float(sams["beta"]) min_burnin = int(sams["min_burn"]) min_slow = int(sams["min_slow"]) min_fast = int(sams["min_fast"]) flatness_criterion = float(sams["flatness_criterion"]) if sams["update_rule"] == "binary": update_rule = UpdateRule.BINARY elif sams["update_rule"] == "global": update_rule = UpdateRule.GLOBAL else: update_rule = UpdateRule.BINARY # Assumes calibration residue is always the last titration group if onesite if sams["sites"] == "multi": driver.enable_calibration( approach=SAMSApproach.MULTISITE, update_rule=update_rule, flatness_criterion=flatness_criterion, min_burn=min_burnin, min_slow=min_slow, min_fast=min_fast, beta_sams=beta_burnin, ) elif sams["sites"] == "one": if "group_index" in sams: calibration_titration_group_index = int(sams["group_index"]) else: calibration_titration_group_index = len(driver.titrationGroups) - 1 driver.enable_calibration( approach=SAMSApproach.ONESITE, group_index=calibration_titration_group_index, update_rule=update_rule, flatness_criterion=flatness_criterion, min_burn=min_burnin, min_slow=min_slow, min_fast=min_fast, ) # Define residue pools pools = {"calibration": [calibration_titration_group_index]} # TODO the pooling feature could eventually be exposed in the json driver.define_pools(pools) # Create simulation object # If calibration is required, this class will automatically deal with it. simulation = app.ConstantPHSimulation( topology, system, compound_integrator, driver, platform=platform, platformProperties=properties, ) simulation.context.setPositions(positions) # After the simulation system has been defined, we can add salt to the system using saltswap. if salt_concentration is not None: salinator = Salinator( context=simulation.context, system=system, topology=topology, ncmc_integrator=compound_integrator.getIntegrator(1), salt_concentration=salt_concentration, pressure=pressure, temperature=temperature, ) salinator.neutralize() salinator.initialize_concentration() if "neutral_charge_rule" not in sysprops: raise KeyError( "Specification of neutral_charge_rule for explicit solvent required in system." ) charge_rule = NeutralChargeRule(sysprops["neutral_charge_rule"]) driver.enable_neutralizing_ions( salinator.swapper, neutral_charge_rule=charge_rule ) else: # Implicit solvent salinator = None # Set the fixed titration state in case of importance sampling if sampling_method is SamplingMethod.IMPORTANCE: if "titration_states" in settings["importance"]: fixed_states = settings["importance"]["titration_states"] if len(fixed_states) != len(driver.titrationStates): raise IndexError( "The number of residues specified for importance sampling does not match the number of titratable residues." ) else: for group_id, state_id in enumerate(fixed_states): driver.set_titration_state( group_id, state_id, updateContextParameters=True, updateIons=True, ) # Minimize the initial configuration to remove bad contacts if "preprocessing" in settings: simulation.minimizeEnergy( tolerance=pre_run_minimization_tolerance, maxIterations=minimization_max_iterations, ) # Slightly equilibrate the system, detect early issues. simulation.step(num_thermalization_steps) # export the context create_protons_checkpoint_file( output_checkpoint_file, driver, simulation.context, simulation.system, simulation.integrator, topology_file_content, salinator=salinator, ) elif "systematic" in settings["importance"]: if settings["importance"]["systematic"]: # Make checkpoint files for the combinatorial space of residue states. for importance_index, state_combination in enumerate( product(*[np.arange(len(res)) for res in driver.titrationGroups]) ): for res_id, state_id in enumerate(state_combination): driver.set_titration_state( res_id, state_id, updateContextParameters=True, updateIons=True, ) # Minimize the initial configuration to remove bad contacts if "preprocessing" in settings: simulation.minimizeEnergy( tolerance=pre_run_minimization_tolerance, maxIterations=minimization_max_iterations, ) # Slightly equilibrate the system, detect early issues. simulation.step(num_thermalization_steps) # export the context create_protons_checkpoint_file( f"{obasename}-importance-state-{importance_index}-checkpoint-0.xml", driver, simulation.context, simulation.system, simulation.integrator, topology_file_content, salinator=salinator, ) os.chdir(lastdir) else: # Minimize the initial configuration to remove bad contacts if "preprocessing" in settings: simulation.minimizeEnergy( tolerance=pre_run_minimization_tolerance, maxIterations=minimization_max_iterations, ) # Slightly equilibrate the system, detect early issues. simulation.step(num_thermalization_steps) # export the context create_protons_checkpoint_file( output_checkpoint_file, driver, simulation.context, simulation.system, simulation.integrator, topology_file_content, salinator=salinator, ) os.chdir(lastdir)
def test_system_charge_reporting(self): """Test if the system_charge is correctly reported.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) initial_charge = ( 0 ) # Ion totals are 0, and the system starts in state 0 for both, GLU deprotonated, HIS protonated forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, ) # pools defined by their residue index for convenience later on driver.define_pools({"0": [0], "1": [1]}) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) filename = uuid.uuid4().hex + ".nc" ncfile = netCDF4.Dataset(filename, "w") print("Temporary file: ", filename) newreporter = tr.TitrationReporter(ncfile, 2) simulation.update_reporters.append(newreporter) # Regular MD step simulation.step(1) # Update the titration states forcibly from a pregenerated series glu_states = np.asarray([ 1, 4, 4, 1, 0, 2, 0, 2, 4, 3, 2, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 2, 1, 3, 1 ]) his_states = np.asarray([ 2, 1, 0, 1, 2, 2, 1, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1 ]) for i in range(len(glu_states)): simulation.drive.set_titration_state(0, glu_states[i], updateContextParameters=False) simulation.drive.set_titration_state(1, his_states[i], updateContextParameters=False) simulation.update_reporters[0].report(simulation) tot_charge = 0 for resi, residue in enumerate(simulation.drive.titrationGroups): icharge = int(floor(0.5 + residue.total_charge)) tot_charge += icharge assert ( ncfile["Protons/Titration/{}_charge".format(resi)][i] == icharge), "Residue charge is not recorded correctly." assert ( ncfile["Protons/Titration/complex_charge"][i] == tot_charge ), "The recorded complex total charge does not match the actual charge." # close files to avoid segfaults, possibly ncfile.close()
def test_atom_status_reporting(self): """Test if the atom_status is correctly reported.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, ) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) filename = uuid.uuid4().hex + ".nc" ncfile = netCDF4.Dataset(filename, "w") print("Temporary file: ", filename) newreporter = tr.TitrationReporter(ncfile, 2) simulation.update_reporters.append(newreporter) # Regular MD step simulation.step(1) # Update the titration states forcibly from a pregenerated series glu_states = np.asarray([ 1, 4, 4, 1, 0, 2, 0, 2, 4, 3, 2, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 2, 1, 3, 1 ]) his_states = np.asarray([ 2, 1, 0, 1, 2, 2, 1, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1 ]) for i in range(len(glu_states)): simulation.drive.set_titration_state(0, glu_states[i], updateContextParameters=False) simulation.drive.set_titration_state(1, his_states[i], updateContextParameters=False) simulation.update_reporters[0].report(simulation) # check glu self._verify_atom_status(i, 0, ncfile, simulation) # check his self._verify_atom_status(i, 1, ncfile, simulation) ncfile.close()
def test_reports(self): """Instantiate a ConstantPHSimulation at 300K/1 atm for a small peptide.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) num_atoms = pdb.topology.getNumAtoms() forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, ) num_titratable = len(driver.titrationGroups) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) filename = uuid.uuid4().hex + ".nc" print("Temporary file: ", filename) newreporter = tr.TitrationReporter(filename, 2) simulation.update_reporters.append(newreporter) # Regular MD step simulation.step(1) # Update the titration states using the uniform proposal simulation.update(6) # Basic checks for dimension assert (newreporter.ncfile["Protons/Titration"].dimensions["update"]. size == 3), "There should be 3 updates recorded." assert ( newreporter.ncfile["Protons/Titration"].dimensions["residue"].size == num_titratable ), "There should be {} residues recorded.".format(num_titratable) assert ( newreporter.ncfile["Protons/Titration"].dimensions["atom"].size == num_atoms), "There should be {} atoms recorded.".format(num_atoms) newreporter.ncfile.close()
def main(jsonfile): """Main simulation loop.""" settings = json.load(open(jsonfile)) # Parameters include format strings that are used to format the names of input and output files prms = settings["parameters"] # Input files inp = settings["input"] idir = inp["dir"].format(**prms) input_pdbx_file = os.path.join(idir, inp["calibration_system"].format(**prms)) # If supplied, tell the code to find and load supplied ffxml file custom_xml_provided = False if "ffxml" in inp: custom_xml_provided = True # Load the PDBxfile and the forcefield files if custom_xml_provided: custom_xml = os.path.join(idir, inp["ffxml"].format(**prms)) custom_xml = os.path.abspath(custom_xml) forcefield = app.ForceField('amber10-constph.xml', 'gaff.xml', custom_xml, 'tip3p.xml', 'ions_tip3p.xml') else: forcefield = app.ForceField('amber10-constph.xml', 'gaff.xml', 'tip3p.xml', 'ions_tip3p.xml') pdb_object = app.PDBxFile(input_pdbx_file) # Prepare the Simulation topology = pdb_object.topology positions = pdb_object.positions # Quick fix for histidines in topology # Openmm relabels them HIS, which leads to them not being detected as # titratable. Renaming them fixes this. for residue in topology.residues(): if residue.name == 'HIS': residue.name = 'HIP' # Naming the output files out = settings["output"] odir = out["dir"].format(**prms) if not os.path.isdir(odir): os.makedirs(odir) lastdir = os.getcwd() os.chdir(odir) name_netcdf = out["netcdf"].format(**prms) dcd_output_name = out["dcd"].format(**prms) # Files for resuming simulation resumes = out["resume_files"] output_context_xml = resumes["state"].format(**prms) output_drive_xml = resumes["drive"].format(**prms) output_calibration_json = resumes["calibration"].format(**prms) # Integrator options integrator_opts = prms["integrator"] timestep = integrator_opts["timestep_fs"] * unit.femtosecond constraint_tolerance = integrator_opts["constraint_tolerance"] collision_rate = integrator_opts["collision_rate_per_ps"] / unit.picosecond number_R_steps = 1 # Steps of MD before starting the main loop num_thermalization_steps = int(prms["num_thermalization_steps"]) # Steps of MD in between MC moves steps_between_updates = int(prms["steps_between_updates"]) ncmc = prms["ncmc"] counterion_method = ncmc["counterion_method"].lower() if counterion_method not in ["chen-roux", "chenroux", "background"]: raise ValueError( "Invalid ncmc counterion method, {}. Please pick Chen-Roux or background." .format(counterion_method)) ncmc_steps_per_trial = int(ncmc["steps_per_trial"]) prop_steps_per_trial = int(ncmc["propagations_per_step"]) total_iterations = int(prms["total_attempts"]) # settings for minimization minimization = prms["minimization"] pre_run_minimization_tolerance = float( minimization["tolerance_kjmol"]) * unit.kilojoule / unit.mole minimization_max_iterations = int(minimization["max_iterations"]) # SAMS settings sams = prms["SAMS"] beta_burnin = sams["beta_sams"] flatness_criterion = sams["flatness_criterion"] # Script specific settings # Register the timeout handling signal.signal(signal.SIGALRM, timeout_handler) script_timeout = 428400 # 119 hours # Platform Options platform = mm.Platform.getPlatformByName('CUDA') properties = { 'CudaPrecision': 'mixed', 'DeterministicForces': 'true', 'CudaDeviceIndex': os.environ['CUDA_VISIBLE_DEVICES'] } # System Configuration sysprops = prms["system"] nonbondedMethod = app.PME constraints = app.HBonds rigidWater = True ewaldErrorTolerance = float(sysprops["ewald_error_tolerance"]) barostatInterval = int(sysprops["barostat_interval"]) switching_distance = float( sysprops["switching_distance_nm"]) * unit.nanometers nonbondedCutoff = float(sysprops["nonbonded_cutoff_nm"]) * unit.nanometers pressure = float(sysprops["pressure_atm"]) * unit.atmosphere temperature = float(sysprops["temperature_k"]) * unit.kelvin system = forcefield.createSystem(topology, nonbondedMethod=nonbondedMethod, constraints=constraints, rigidWater=rigidWater, ewaldErrorTolerance=ewaldErrorTolerance, nonbondedCutoff=nonbondedCutoff) # for force in system.getForces(): if isinstance(force, mm.NonbondedForce): force.setUseSwitchingFunction(True) force.setSwitchingDistance(switching_distance) # NPT simulation system.addForce( mm.MonteCarloBarostat(pressure, temperature, barostatInterval)) integrator = ExternalGBAOABIntegrator( number_R_steps=number_R_steps, temperature=temperature, collision_rate=collision_rate, timestep=timestep, constraint_tolerance=constraint_tolerance) ncmc_propagation_integrator = ExternalGBAOABIntegrator( number_R_steps=number_R_steps, temperature=temperature, collision_rate=collision_rate, timestep=timestep, constraint_tolerance=constraint_tolerance) # Define a compound integrator compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmc_propagation_integrator) compound_integrator.setCurrentIntegrator(0) if custom_xml_provided: driver = ForceFieldProtonDrive( temperature, topology, system, forcefield, ['amber10-constph.xml', custom_xml], pressure=pressure, perturbations_per_trial=ncmc_steps_per_trial, propagations_per_step=prop_steps_per_trial) else: driver = ForceFieldProtonDrive( temperature, topology, system, forcefield, ['amber10-constph.xml'], pressure=pressure, perturbations_per_trial=ncmc_steps_per_trial, propagations_per_step=prop_steps_per_trial) # Assumes calibration residue is always the last titration group calibration_titration_group_index = len(driver.titrationGroups) - 1 # Define residue pools pools = {'calibration': [calibration_titration_group_index]} driver.define_pools(pools) # Create SAMS sampler simulation = app.ConstantPHCalibration( topology, system, compound_integrator, driver, group_index=calibration_titration_group_index, platform=platform, platformProperties=properties, samsProperties=sams) simulation.context.setPositions(positions) # After the simulation system has been defined, we can add salt to the system using saltswap. salinator = Salinator(context=simulation.context, system=system, topology=topology, ncmc_integrator=compound_integrator.getIntegrator(1), salt_concentration=0.150 * unit.molar, pressure=pressure, temperature=temperature) salinator.neutralize() salinator.initialize_concentration() swapper = salinator.swapper # Protons can use the scheme from [Chen2015]_ to maintain charge neutrality. # If the Chen-Roux scheme is requested, attach swapper. Else, use neutralizing background charge (happens under the hood of openmm). if counterion_method in ["chenroux", "chen-roux"]: simulation.drive.attach_swapper(swapper) # Minimize the initial configuration to remove bad contacts simulation.minimizeEnergy(tolerance=pre_run_minimization_tolerance, maxIterations=minimization_max_iterations) # Slightly equilibrate the system, detect early issues. simulation.step(num_thermalization_steps) # Add reporters, these write out simulation data at regular intervals dcdreporter = app.DCDReporter(dcd_output_name, int(steps_between_updates / 10)) ncfile = netCDF4.Dataset(name_netcdf, "w") metdatarep = MetadataReporter(ncfile) ncmcrep = NCMCReporter(ncfile, 1) titrep = TitrationReporter(ncfile, 1) simulation.reporters.append(dcdreporter) simulation.update_reporters.append(metdatarep) simulation.update_reporters.append(ncmcrep) simulation.update_reporters.append(titrep) samsrep = SAMSReporter(ncfile, 1) simulation.calibration_reporters.append(samsrep) # MAIN SIMULATION LOOP STARTS HERE # Raises an exception if the simulation runs out of time, so that the script can be killed cleanly from within python signal.alarm(script_timeout) try: for i in range(total_iterations): log.info("Iteration %i", i) if i == 5: log.info( "Simulation seems to be working. Suppressing debugging info." ) log.setLevel(logging.INFO) # Regular MD simulation.step(steps_between_updates) # Update protonation state simulation.update(1, pool='calibration') # Adapt SAMS weight simulation.adapt() # Reset timer signal.alarm(0) except TimeOutError: log.warn("Simulation ran out of time, saving current results.") finally: # export the context serialize_state(simulation.context, output_context_xml) # export the driver serialize_drive(simulation.drive, output_drive_xml) # export the calibration status serialize_sams_status(simulation, output_calibration_json) ncfile.close() os.chdir(lastdir)
# coding=utf-8 """ Adds hydrogens to 2HYY-noH.pdb """ from protons import app from simtk.openmm import openmm from simtk.unit import * from protons.app.integrators import GBAOABIntegrator # Load relevant template definitions for modeller, forcefield and topology app.Modeller.loadHydrogenDefinitions('imatinib-hydrogens.xml') forcefield = app.ForceField('amber10-constph.xml', 'gaff.xml', 'imatinib.xml', 'tip3p.xml', 'ions_tip3p.xml') pdb = app.PDBFile('2HYY-noH.pdb') modeller = app.Modeller(pdb.topology, pdb.positions) # The pdb contains solvent but not the right ions. # This would mean we need to equilibrate again even if we just add new ions. # In this case its easiest to just delete and re-add the solvent with the right amount of ions modeller.deleteWater() ions = [ atom for atom in modeller.topology.atoms() if atom.element.symbol in ['Cl', 'Na'] ] modeller.delete(ions) modeller.addHydrogens(forcefield=forcefield) modeller.addSolvent(forcefield, model='tip3p', padding=1.0*nanometers, positiveIon='Na+', negativeIon='Cl-', ionicStrength=120*millimolar, neutralize=True)
def test_reports(self): """Instantiate a simulation at 300K/1 atm for a small peptide with reporter.""" pdb = app.PDBxFile( get_test_data( "glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides" ) ) forcefield = app.ForceField( "amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml" ) system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=3, ) # prep the driver for calibration driver.enable_calibration( SAMSApproach.ONESITE, group_index=1, update_rule=UpdateRule.BINARY ) calibration = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) calibration.context.setPositions(pdb.positions) calibration.context.setVelocitiesToTemperature(temperature) filename = uuid.uuid4().hex + ".nc" print("Temporary file: ", filename) newreporter = sr.SAMSReporter(filename, 2) calibration.calibration_reporters.append(newreporter) # Regular MD step for iteration in range(4): calibration.step(1) # Update the titration states using the uniform proposal calibration.update(1) # adapt sams weights calibration.adapt() # Basic checks for dimension assert ( newreporter.ncfile["Protons/SAMS"].dimensions["adaptation"].size == 2 ), "There should be 2 updates recorded." assert ( newreporter.ncfile["Protons/SAMS"].dimensions["state"].size == 3 ), "There should be 3 states reported." newreporter.ncfile.close()
def test_reports_every_perturbation_saltswap(self): """Instantiate a ConstantPHSimulation at 300K/1 atm for a small peptide, save every perturbation step, with saltswap.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=3, ) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) filename = uuid.uuid4().hex + ".nc" print("Temporary file: ", filename) # The salinator initializes the system salts salinator = Salinator( context=simulation.context, system=simulation.system, topology=simulation.topology, ncmc_integrator=simulation.integrator.getIntegrator(1), salt_concentration=0.2 * unit.molar, pressure=pressure, temperature=temperature, ) salinator.neutralize() salinator.initialize_concentration() swapper = salinator.swapper driver.enable_neutralizing_ions(swapper) newreporter = ncr.NCMCReporter(filename, 1, cumulativeworkInterval=1) simulation.update_reporters.append(newreporter) # Regular MD step simulation.step(1) # Update the titration states using the uniform proposal simulation.update(4) # Basic checks for dimension assert (newreporter.ncfile["Protons/NCMC"].dimensions["update"].size == 4), "There should be 4 updates recorded." assert (newreporter.ncfile["Protons/NCMC"].dimensions["residue"].size == 2), "There should be 2 residues recorded." assert (newreporter.ncfile["Protons/NCMC"].dimensions["perturbation"]. size == 3), "There should be max 3 perturbations recorded." assert (newreporter.ncfile["Protons/NCMC"].dimensions["ion_site"].size == 1269), "The system should have 1269 potential ion sites." # Ensure clean exit newreporter.ncfile.sync() newreporter.ncfile.close()
def test_create_simulation(self): """Instantiate a ConstantPHSimulation at 300K/1 atm for a small peptide.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, ) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) # Regular MD step simulation.step(1) # Update the titration states using the uniform proposal simulation.update(1) print("Done!")
def test_create_constantphcalibration_with_reporters(self): """Test running a calibration using constant-pH with reporters.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=2, propagations_per_step=1, ) # prep the driver for calibration driver.enable_calibration(SAMSApproach.ONESITE, group_index=-1) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) # Outputfile for reporters newname = uuid4().hex + ".nc" ncfile = netCDF4.Dataset(newname, "w") tr = TitrationReporter(ncfile, 1) mr = MetadataReporter(ncfile) nr = NCMCReporter(ncfile, 1) sr = SAMSReporter(ncfile, 1) simulation.update_reporters.append(tr) simulation.update_reporters.append(mr) simulation.update_reporters.append(nr) simulation.calibration_reporters.append(sr) simulation.step(1) # Update the titration states using the uniform proposal simulation.update(1) # Adapt the weights using binary update. simulation.adapt() # Attempt to prevent segfaults by closing files ncfile.close()
def test_create_constantphcalibration_resume(self): """Test running a calibration using constant-pH.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, ) driver.enable_calibration(SAMSApproach.ONESITE, group_index=-1) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) simulation.step(1) # Update the titration states using the uniform proposal simulation.update(1) # Adapt the weights using binary update, a few times just to rack up the counters. for x in range(5): simulation.adapt() # retrieve the samsProperties # TODO use deserialize proton drive for this xml = simulation.drive.state_to_xml() driver2 = NCMCProtonDrive( temperature, pdb.topology, system, pressure=pressure, perturbations_per_trial=0, ) driver2.state_from_xml_tree(etree.fromstring(xml)) integrator2 = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator2 = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator2 = mm.CompoundIntegrator() compound_integrator2.addIntegrator(integrator2) compound_integrator2.addIntegrator(ncmcintegrator2) # Make a new calibration and do another step, ignore the state for this example simulation2 = app.ConstantPHSimulation( pdb.topology, system, compound_integrator2, driver2, platform=self._default_platform, ) assert (simulation2.drive.calibration_state._stage is simulation.drive.calibration_state._stage) # get going then simulation2.context.setPositions(pdb.positions) simulation2.context.setVelocitiesToTemperature(temperature) simulation2.step(1) # Update the titration states using the uniform proposal simulation2.update(1) # Adapt the weights using binary update. simulation2.adapt() assert ( simulation2.drive.calibration_state._current_adaptation == -simulation.drive.calibration_state._min_burn + 6 ), "The resumed calibration does not have the right adaptation_index"
def test_create_importance_sampling_reporters(self): """Instantiate a ConstantPHSimulation at 300K/1 atm for a small peptide using importance sampling with reporters.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, sampling_method=SamplingMethod.IMPORTANCE, ) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) newname = uuid4().hex + ".nc" ncfile = netCDF4.Dataset(newname, "w") tr = TitrationReporter(ncfile, 1) mr = MetadataReporter(ncfile) nr = NCMCReporter(ncfile, 1) simulation.update_reporters.append(tr) simulation.update_reporters.append(mr) simulation.update_reporters.append(nr) # Regular MD step simulation.step(1) # Update the titration states using the uniform proposal niters = 3 for x in range(niters): simulation.update(1) n_total_states = np.product( [len(group) for group in driver.titrationGroups]) assert (len(ncfile["Protons/Titration/update"][:]) == n_total_states * niters), "The wrong number of updates were recorded" work_values = np.split(ncfile["Protons/NCMC/total_work"][:], niters) init_states = ncfile["Protons/NCMC/initial_state"][:, :] assert np.all( init_states == 0), "States should all be zero at the start." first_run = work_values[0] assert np.all(np.unique(first_run) == np.sort( first_run)), "No work values should be duplicated." # Since instanteneous switching is used, this should be true for x in range(1, niters): assert np.all( np.isclose(work_values[x], first_run)), "Work should be equal for all states." # Switch state driver.set_titration_state(0, 3, updateContextParameters=True, updateIons=True) driver.set_titration_state(1, 1, updateContextParameters=True, updateIons=True) simulation.update(1) assert (ncfile["Protons/NCMC/initial_state"][-n_total_states, 0] == 3 ), "Initial state was not changed correctly." assert (ncfile["Protons/NCMC/initial_state"][-n_total_states, 1] == 1 ), "Initial state was not changed correctly." work_values = np.split(ncfile["Protons/NCMC/total_work"][:], niters + 1) assert not np.all( np.isclose(work_values[0], work_values[-1]) ), "Work values starting from different state should differ." return
def test_create_importance_sampling(self): """Instantiate a ConstantPHSimulation at 300K/1 atm for a small peptide using importance sampling.""" pdb = app.PDBxFile( get_test_data("glu_ala_his-solvated-minimized-renamed.cif", "testsystems/tripeptides")) forcefield = app.ForceField("amber10-constph.xml", "ions_tip3p.xml", "tip3p.xml") system = forcefield.createSystem( pdb.topology, nonbondedMethod=app.PME, nonbondedCutoff=1.0 * unit.nanometers, constraints=app.HBonds, rigidWater=True, ewaldErrorTolerance=0.0005, ) temperature = 300 * unit.kelvin integrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=False, ) ncmcintegrator = GBAOABIntegrator( temperature=temperature, collision_rate=1.0 / unit.picoseconds, timestep=2.0 * unit.femtoseconds, constraint_tolerance=1.0e-7, external_work=True, ) compound_integrator = mm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmcintegrator) pressure = 1.0 * unit.atmosphere system.addForce(mm.MonteCarloBarostat(pressure, temperature)) driver = ForceFieldProtonDrive( temperature, pdb.topology, system, forcefield, ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=0, sampling_method=SamplingMethod.IMPORTANCE, ) simulation = app.ConstantPHSimulation( pdb.topology, system, compound_integrator, driver, platform=self._default_platform, ) simulation.context.setPositions(pdb.positions) simulation.context.setVelocitiesToTemperature(temperature) # Regular MD step simulation.step(1) # Update the titration states using the uniform proposal simulation.update(1) # Total states is 15 but proposing the same state as current does not get added to statistics. assert simulation.drive.nattempted == 14, "Not enough switch were attempted." assert simulation.drive.naccepted == 0, "No acceptance should have occurred." assert (simulation.drive.nattempted == simulation.drive.nrejected ), "The rejection count should match the number of attempts" print("Done!")