def create_compound_ghmc_integrator(testsystem): """ Sets up a compound integrator that uses GHMC. Parameters ---------- testsystem - a SystemSetup object that contains details such as the temperature, timestep, et cetera. Returns ------- simtk.openmm.openmm.CompoundIntegrator """ integrator = GHMCIntegrator( temperature=testsystem.temperature, collision_rate=testsystem.collision_rate, timestep=testsystem.timestep, nsteps=testsystem.nsteps_per_ghmc, ) ncmc_propagation_integrator = GHMCIntegrator( temperature=testsystem.temperature, collision_rate=testsystem.collision_rate, timestep=testsystem.timestep, nsteps=testsystem.nsteps_per_ghmc, ) compound_integrator = openmm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmc_propagation_integrator) compound_integrator.setCurrentIntegrator(0) return compound_integrator
def create_compound_gbaoab_integrator(testsystem): """ Sets up a compound integrator that uses gBAOAB. Parameters ---------- testsystem - a SystemSetup object that contains details such as the temperature, timestep, et cetera. Returns ------- simtk.openmm.openmm.CompoundIntegrator """ integrator = GBAOABIntegrator( temperature=testsystem.temperature, collision_rate=testsystem.collision_rate, timestep=testsystem.timestep, constraint_tolerance=testsystem.constraint_tolerance, ) ncmc_propagation_integrator = GBAOABIntegrator( temperature=testsystem.temperature, collision_rate=testsystem.collision_rate, timestep=testsystem.timestep, constraint_tolerance=testsystem.constraint_tolerance, ) compound_integrator = openmm.CompoundIntegrator() compound_integrator.addIntegrator(integrator) compound_integrator.addIntegrator(ncmc_propagation_integrator) compound_integrator.setCurrentIntegrator(0) return compound_integrator
def collect_switching_data(system, positions, functions, temperature, collision_rate, timestep, platform, ghmc_nsteps=200, ncmc_nsteps=50, niterations=100, direction='insert', ncmc_integrator=None): """ Collect switching data. """ work_n = np.zeros([niterations], np.float64) # work[iteration] is work (log probability) in kT from iteration `iteration` # Create integrators. integrator = openmm.CompoundIntegrator() # Create GHMC integrator. from openmmtools.integrators import GHMCIntegrator ghmc_integrator = GHMCIntegrator(temperature=temperature, collision_rate=collision_rate, timestep=timestep) integrator.addIntegrator(ghmc_integrator) # Create an NCMC switching integrator. integrator.addIntegrator(ncmc_integrator) # Create Context context = openmm.Context(system, integrator, platform) context.setPositions(positions) context.setVelocitiesToTemperature(temperature) naccept_n = np.zeros([niterations], np.int32) ntrials_n = np.zeros([niterations], np.int32) for iteration in range(niterations): # Equilibrate integrator.setCurrentIntegrator(0) if direction == 'insert': context.setParameter('x0', 0) elif direction == 'delete': context.setParameter('x0', 1) else: raise Exception("direction '%s' unknown; must be 'insert' or 'delete'." % direction) integrator.step(ghmc_nsteps) # Switch integrator.setCurrentIntegrator(1) ncmc_integrator.reset() if ncmc_nsteps == 0: integrator.step(1) else: integrator.step(ncmc_nsteps) #print("The step is %d" % ncmc_integrator.get_step()) work_n[iteration] = - ncmc_integrator.getLogAcceptanceProbability(context) if ncmc_integrator.has_statistics: (naccept_n[iteration], ntrials_n[iteration]) = ncmc_integrator.getGHMCStatistics(context) if ncmc_integrator.has_statistics: print('GHMC: %d / %d accepted' % (naccept_n.sum(), ntrials_n.sum())) # Clean up del context, integrator return work_n
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()
# Create the compound integrator langevin = integrators.LangevinIntegrator(splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) ncmc_langevin = integrators.ExternalPerturbationLangevinIntegrator( splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) integrator = openmm.CompoundIntegrator() integrator.addIntegrator(langevin) integrator.addIntegrator(ncmc_langevin) # Create context if args.platform == 'CUDA': platform = openmm.Platform.getPlatformByName('CUDA') platform.setPropertyDefaultValue('DeterministicForces', 'true') properties = {'CudaPrecision': 'mixed'} context = openmm.Context(wbox.system, integrator, platform, properties) elif args.platform == 'OpenCL': platform = openmm.Platform.getPlatformByName('OpenCL') properties = {'OpenCLPrecision': 'mixed'} context = openmm.Context(wbox.system, integrator, platform, properties) elif args.platform == 'CPU': platform = openmm.Platform.getPlatformByName('CPU')
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)
def create_ideal_system(npert=50, nprop=1, deltachem=0.0, platform='CPU'): """ Create small box of water that can impliment ideal mixing with the SaltSwap osmostat. Parameters ---------- npert: int the number of NCMC perturbations nprop: int the number of Langevin propagation steps per NCMC perturbation. deltachem: float the difference in chemical potential between two water molecules and NaCl that has the same nonbonded parameters as two water molecules. platform: str The computational platform. Either 'CPU', 'CUDA', or 'OpenCL'. Returns ------- ncmc_swapper: saltswap.swapper the driver that can perform NCMC exchanges langevin: openmmtools.integrator the integrator for equilibrium sampling. """ # Setting the parameters of the simulation timestep = 2.0 * unit.femtoseconds box_edge = 25.0 * unit.angstrom splitting = 'V R O R V' temperature = 300. * unit.kelvin collision_rate = 1. / unit.picoseconds pressure = 1. * unit.atmospheres # Make the water box test system with a fixed pressure wbox = WaterBox(box_edge=box_edge, model='tip3p', nonbondedMethod=app.PME, cutoff=10 * unit.angstrom, ewaldErrorTolerance=1E-4) wbox.system.addForce(openmm.MonteCarloBarostat(pressure, temperature)) # Create the compound integrator langevin = integrators.LangevinIntegrator(splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) ncmc_langevin = integrators.ExternalPerturbationLangevinIntegrator( splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) integrator = openmm.CompoundIntegrator() integrator.addIntegrator(langevin) integrator.addIntegrator(ncmc_langevin) # Create context if platform == 'CUDA': platform = openmm.Platform.getPlatformByName('CUDA') platform.setPropertyDefaultValue('DeterministicForces', 'true') properties = {'CudaPrecision': 'mixed'} context = openmm.Context(wbox.system, integrator, platform, properties) elif platform == 'OpenCL': platform = openmm.Platform.getPlatformByName('OpenCL') properties = {'OpenCLPrecision': 'mixed'} context = openmm.Context(wbox.system, integrator, platform, properties) elif platform == 'CPU': platform = openmm.Platform.getPlatformByName('CPU') context = openmm.Context(wbox.system, integrator, platform) else: raise Exception('Platform name {0} not recognized.'.format( args.platform)) context.setPositions(wbox.positions) context.setVelocitiesToTemperature(temperature) # Create the swapper object for the insertion and deletion of salt ncmc_swapper = Swapper(system=wbox.system, topology=wbox.topology, temperature=temperature, delta_chem=deltachem, ncmc_integrator=ncmc_langevin, pressure=pressure, npert=npert, nprop=nprop) # Set the nonbonded parameters of the ions to be the same as water. This is critical for ideal mixing. ncmc_swapper.cation_parameters = ncmc_swapper.water_parameters ncmc_swapper.anion_parameters = ncmc_swapper.water_parameters ncmc_swapper._set_parampath() return context, ncmc_swapper, langevin
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 run_alchemical_langevin_integrator(nsteps=0, splitting="O { V R H R V } O"): """Check that the AlchemicalLangevinSplittingIntegrator reproduces the analytical free energy difference for a harmonic oscillator deformation, using BAR. Up to 6*sigma is tolerated for error. The total work (protocol work + shadow work) is used. """ #max deviation from the calculated free energy NSIGMA_MAX = 6 n_iterations = 200 # number of forward and reverse protocols # These are the alchemical functions that will be used to control the system temperature = 298.0 * unit.kelvin sigma = 1.0 * unit.angstrom # stddev of harmonic oscillator kT = kB * temperature # thermal energy beta = 1.0 / kT # inverse thermal energy K = kT / sigma**2 # spring constant corresponding to sigma mass = 39.948 * unit.amu period = unit.sqrt(mass / K) # period of harmonic oscillator timestep = period / 20.0 collision_rate = 1.0 / period dF_analytical = 1.0 parameters = dict() parameters['testsystems_HarmonicOscillator_x0'] = (0 * sigma, 2 * sigma) parameters['testsystems_HarmonicOscillator_U0'] = (0 * kT, 1 * kT) alchemical_functions = { 'forward': { name: '(1-lambda)*%f + lambda*%f' % (value[0].value_in_unit_system(unit.md_unit_system), value[1].value_in_unit_system(unit.md_unit_system)) for (name, value) in parameters.items() }, 'reverse': { name: '(1-lambda)*%f + lambda*%f' % (value[1].value_in_unit_system(unit.md_unit_system), value[0].value_in_unit_system(unit.md_unit_system)) for (name, value) in parameters.items() }, } # Create harmonic oscillator testsystem testsystem = testsystems.HarmonicOscillator(K=K, mass=mass) system = testsystem.system positions = testsystem.positions # Get equilibrium samples from initial and final states burn_in = 5 * 20 # 5 periods thinning = 5 * 20 # 5 periods # Collect forward and reverse work values directions = ['forward', 'reverse'] work = { direction: np.zeros([n_iterations], np.float64) for direction in directions } platform = openmm.Platform.getPlatformByName("Reference") for direction in directions: positions = testsystem.positions # Create equilibrium and nonequilibrium integrators equilibrium_integrator = GHMCIntegrator(temperature=temperature, collision_rate=collision_rate, timestep=timestep) nonequilibrium_integrator = AlchemicalNonequilibriumLangevinIntegrator( temperature=temperature, collision_rate=collision_rate, timestep=timestep, alchemical_functions=alchemical_functions[direction], splitting=splitting, nsteps_neq=nsteps, measure_shadow_work=True) # Create compound integrator compound_integrator = openmm.CompoundIntegrator() compound_integrator.addIntegrator(equilibrium_integrator) compound_integrator.addIntegrator(nonequilibrium_integrator) # Create Context context = openmm.Context(system, compound_integrator, platform) context.setPositions(positions) # Collect work samples for iteration in range(n_iterations): # # Generate equilibrium sample # compound_integrator.setCurrentIntegrator(0) equilibrium_integrator.reset() compound_integrator.step(thinning) # # Generate nonequilibrium work sample # compound_integrator.setCurrentIntegrator(1) nonequilibrium_integrator.reset() # Check initial conditions after reset current_lambda = nonequilibrium_integrator.getGlobalVariableByName( 'lambda') assert current_lambda == 0.0, 'initial lambda should be 0.0 (was %f)' % current_lambda current_step = nonequilibrium_integrator.getGlobalVariableByName( 'step') assert current_step == 0.0, 'initial step should be 0 (was %f)' % current_step compound_integrator.step(max( 1, nsteps)) # need to execute at least one step work[direction][ iteration] = nonequilibrium_integrator.get_total_work( dimensionless=True) # Check final conditions before reset current_lambda = nonequilibrium_integrator.getGlobalVariableByName( 'lambda') assert current_lambda == 1.0, 'final lambda should be 1.0 (was %f) for splitting %s' % ( current_lambda, splitting) current_step = nonequilibrium_integrator.getGlobalVariableByName( 'step') assert int(current_step) == max( 1, nsteps ), 'final step should be %d (was %f) for splitting %s' % (max( 1, nsteps), current_step, splitting) nonequilibrium_integrator.reset() # Clean up del context del compound_integrator dF, ddF = pymbar.BAR(work['forward'], work['reverse']) nsigma = np.abs(dF - dF_analytical) / ddF print( "analytical DeltaF: {:12.4f}, DeltaF: {:12.4f}, dDeltaF: {:12.4f}, nsigma: {:12.1f}" .format(dF_analytical, dF, ddF, nsigma)) if nsigma > NSIGMA_MAX: raise Exception( "The free energy difference for the nonequilibrium switching for splitting '%s' and %d steps is not zero within statistical error." % (splitting, nsteps))
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 generate_simulation_and_driver(settings): ofolder = settings['output']['dir'] input_pdbx_file = settings['input']['dir'] + '/' + settings['name'] + '.cif' custom_xml = settings['input']['dir'] + '/' + settings['name'] + '.ffxml' forcefield = app.ForceField('amber10-constph.xml', 'gaff.xml', custom_xml, 'tip3p.xml', 'ions_tip3p.xml') # Load structure # The input should be an mmcif/pdbx file' pdb_object = app.PDBxFile(input_pdbx_file) # Atoms , connectivity, residues topology = pdb_object.topology # 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" if os.path.isdir(ofolder): shutil.rmtree(ofolder) os.makedirs(ofolder) # Naming the output files os.chdir(ofolder) if not os.path.isdir('tmp'): os.makedirs('tmp') # 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 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 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) # TODO disable in implicit solvent # 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 # Register the timeout handling driver = TautomerForceFieldProtonDrive( temperature, topology, system, forcefield, [custom_xml] + ["amber10-constph.xml"], pressure=pressure, perturbations_per_trial=10000, 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("CUDA") # properties = {"Precision": "double"} platform = mm.Platform.getPlatformByName("CPU") # Set up calibration mode # SAMS settings if "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) properties = None # 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 and "PME" in sysprops: 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() else: salinator = None # 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) topology_file_content = open(input_pdbx_file, "r").read() return simulation, driver, pdb_object
def create_system(args, splitting): """ Create a test system that's able to run saltswap. Parameters ---------- args: splitting: """ # Fixed simulation parameters temperature = 300.0 * unit.kelvin collision_rate = 1.0 / unit.picoseconds pressure = 1.0 * unit.atmospheres salt_concentration = args.conc * unit.molar # Get the test system and add the barostat. testobj = getattr(testsystems, args.testsystem) testsys = testobj(nonbondedMethod=app.PME, cutoff=10 * unit.angstrom, ewaldErrorTolerance=1E-4, switch_width=1.5 * unit.angstrom) testsys.system.addForce( openmm.MonteCarloBarostat(pressure, temperature)) # Create the compound integrator langevin = integrators.LangevinIntegrator( splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) ncmc_langevin = integrators.ExternalPerturbationLangevinIntegrator( splitting=splitting, temperature=temperature, timestep=timestep, collision_rate=collision_rate, measure_shadow_work=False, measure_heat=False) integrator = openmm.CompoundIntegrator() integrator.addIntegrator(langevin) integrator.addIntegrator(ncmc_langevin) # Create context if args.platform == 'CUDA': platform = openmm.Platform.getPlatformByName('CUDA') platform.setPropertyDefaultValue('DeterministicForces', 'true') properties = {'CudaPrecision': 'mixed'} context = openmm.Context(testsys.system, integrator, platform, properties) elif args.platform == 'OpenCL': platform = openmm.Platform.getPlatformByName('OpenCL') properties = {'OpenCLPrecision': 'mixed'} context = openmm.Context(testsys.system, integrator, platform, properties) elif args.platform == 'CPU': platform = openmm.Platform.getPlatformByName('CPU') context = openmm.Context(testsys.system, integrator, platform) else: raise Exception('Platform name {0} not recognized.'.format( args.platform)) context.setPositions(testsys.positions) context.setVelocitiesToTemperature(temperature) # Create the swapper object for the insertion and deletion of salt salinator = wrappers.Salinator(context=context, system=testsys.system, topology=testsys.topology, ncmc_integrator=ncmc_langevin, salt_concentration=salt_concentration, pressure=pressure, temperature=temperature, npert=npert, water_name=args.water_name) # Neutralize the system and initialize the number of salt pairs. salinator.neutralize() salinator.initialize_concentration() return salinator, langevin, integrator