def compute_partial_charges(self, molecule, method='am1bcc'): """ It computes the partial charges using antechamber. Parameters ---------- molecule : an offpele.topology.Molecule The offpele's Molecule object method : str The name of the method to use. One of ['gasteiger', 'am1bcc']. If None, 'am1bcc' will be used Returns ------- charges : simtk.unit.Quantity The array of partial charges Raises ------ ChargeMethodUnavailableError if the requested charge method can not be handled by this toolkit ChargeCalculationError if the charge method is supported by this toolkit, but fails """ SUPPORTED_CHARGE_METHODS = { 'am1bcc': { 'antechamber_keyword': 'bcc' }, 'gasteiger': { 'antechamber_keyword': 'gas' } } if method not in SUPPORTED_CHARGE_METHODS: raise ChargeMethodUnavailableError( 'partial_charge_method ' + '{} is not available from '.format(method) + 'AmberToolsToolkitWrapper. Available charge methods are ' + list(SUPPORTED_CHARGE_METHODS.keys())) off_molecule = molecule.off_molecule with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): net_charge = off_molecule.total_charge / \ unit.elementary_charge self._rdkit_toolkit_wrapper.to_sdf_file( molecule, tmpdir + '/molecule.sdf') subprocess.check_output([ "antechamber", "-i", "molecule.sdf", "-fi", "sdf", "-o", "charged.ac", "-fo", "ac", "-pf", "yes", "-dr", "n", "-c", SUPPORTED_CHARGE_METHODS[method]['antechamber_keyword'], "-nc", str(net_charge) ]) # Write out just charges subprocess.check_output([ "antechamber", "-dr", "n", "-i", "charged.ac", "-fi", "ac", "-o", "charged2.ac", "-fo", "ac", "-c", "wc", "-cf", "charges.txt", "-pf", "yes" ]) if not os.path.exists('charges.txt'): # TODO: copy files into local directory to aid debugging? raise ChargeCalculationError( "Antechamber/sqm partial charge calculation failed on " "molecule {} (SMILES {})".format( off_molecule.name, off_molecule.to_smiles())) # Read the charges with open('charges.txt', 'r') as infile: contents = infile.read() text_charges = contents.split() charges = np.zeros([off_molecule.n_atoms], np.float64) for index, token in enumerate(text_charges): charges[index] = float(token) charges = unit.Quantity(charges, unit.elementary_charge) assert len(charges) == len(molecule.rdkit_molecule.GetAtoms()), \ 'Partial charge computation failed as the length of ' \ + 'resulting partial charges does not match with the ' \ + 'number of atoms in molecule' return charges
def validate_rjmc_work_variance(top_prop, positions, geometry_method=0, num_iterations=10, md_steps=250, compute_timeseries=False, md_system=None, prespecified_conformers=None): """ Arguments ---------- top_prop : perses.rjmc.topology_proposal.TopologyProposal object topology_proposal md_system : openmm.System object, default None system from which md is conducted; the default is the top_prop._old_system geometry_method : int which geometry proposal method to use 0: neglect_angles = True (this is supposed to be the zero-variance method) 1: neglect_angles = False (this will accumulate variance) 2: use_sterics = True (this is experimental) num_iterations: int number of times to run md_steps integrator md_steps: int number of md_steps to run in each num_iteration compute_timeseries = bool (default False) whether to use pymbar detectEquilibration and subsampleCorrelated data from the MD run (the potential energy is the data) prespecified_conformers = None or unit.Quantity(np.array([num_iterations, system.getNumParticles(), 3]), unit = unit.nanometers) whether to input a unit.Quantity of conformers and bypass the conformer_generation/pymbar stage; None will default conduct this phase Returns ------- conformers : unit.Quantity(np.array([num_iterations, system.getNumParticles(), 3]), unit = unit.nanometers) decorrelated positions of the md run rj_works : list work from each conformer proposal """ from openmmtools import integrators from perses.utils.openeye import smiles_to_oemol import simtk.unit as unit import simtk.openmm as openmm from openmmtools.constants import kB from perses.rjmc.geometry import FFAllAngleGeometryEngine import tqdm temperature = 300.0 * unit.kelvin # unit-bearing temperature kT = kB * temperature # unit-bearing thermal energy beta = 1.0 / kT # unit-bearing inverse thermal energy #first, we must extract the top_prop relevant quantities topology = top_prop._old_topology if md_system == None: system = top_prop._old_system else: system = md_system if prespecified_conformers == None: #now we can specify conformations from MD integrator = integrators.LangevinIntegrator( collision_rate=1.0 / unit.picosecond, timestep=4.0 * unit.femtosecond, temperature=temperature) context = openmm.Context(system, integrator) context.setPositions(positions) openmm.LocalEnergyMinimizer.minimize(context) minimized_positions = context.getState(getPositions=True).getPositions( asNumpy=True) print(f"completed initial minimization") context.setPositions(minimized_positions) zeros = np.zeros([num_iterations, int(system.getNumParticles()), 3]) conformers = unit.Quantity(zeros, unit=unit.nanometers) rps = np.zeros((num_iterations)) print(f"conducting md sampling") for iteration in tqdm.trange(num_iterations): integrator.step(md_steps) state = context.getState(getPositions=True, getEnergy=True) new_positions = state.getPositions(asNumpy=True) conformers[iteration, :, :] = new_positions rp = state.getPotentialEnergy() * beta rps[iteration] = rp del context, integrator if compute_timeseries: print(f"computing production and data correlation") from pymbar import timeseries t0, g, Neff = timeseries.detectEquilibration(rps) series = timeseries.subsampleCorrelatedData(np.arange( t0, num_iterations), g=g) print(f"production starts at index {t0} of {num_iterations}") print(f"the number of effective samples is {Neff}") indices = t0 + series print(f"the filtered indices are {indices}") else: indices = range(num_iterations) else: conformers = prespecified_conformers indices = range(len(conformers)) #now we can define a geometry_engine if geometry_method == 0: geometry_engine = FFAllAngleGeometryEngine( metadata=None, use_sterics=False, n_bond_divisions=1000, n_angle_divisions=180, n_torsion_divisions=360, verbose=True, storage=None, bond_softening_constant=1.0, angle_softening_constant=1.0, neglect_angles=True) elif geometry_method == 1: geometry_engine = FFAllAngleGeometryEngine( metadata=None, use_sterics=False, n_bond_divisions=1000, n_angle_divisions=180, n_torsion_divisions=360, verbose=True, storage=None, bond_softening_constant=1.0, angle_softening_constant=1.0, neglect_angles=False) elif geometry_method == 2: geometry_engine = FFAllAngleGeometryEngine( metadata=None, use_sterics=True, n_bond_divisions=1000, n_angle_divisions=180, n_torsion_divisions=360, verbose=True, storage=None, bond_softening_constant=1.0, angle_softening_constant=1.0, neglect_angles=False) else: raise Exception(f"there is no geometry method for {geometry_method}") rj_works = [] print(f"conducting geometry proposals...") for indx in tqdm.trange(len(indices)): index = indices[indx] print(f"index {indx}") new_positions, logp_forward = geometry_engine.propose( top_prop, conformers[index], beta) logp_backward = geometry_engine.logp_reverse(top_prop, new_positions, conformers[index], beta) print( f"\tlogp_forward, logp_backward: {logp_forward}, {logp_backward}") added_energy = geometry_engine.forward_final_context_reduced_potential - geometry_engine.forward_atoms_with_positions_reduced_potential subtracted_energy = geometry_engine.reverse_final_context_reduced_potential - geometry_engine.reverse_atoms_with_positions_reduced_potential print( f"\tadded_energy, subtracted_energy: {added_energy}, {subtracted_energy}" ) work = logp_forward - logp_backward + added_energy - subtracted_energy rj_works.append(work) print(f"\ttotal work: {work}") return conformers, rj_works
def run_task_ase_one_layer(self, settings, systems, n_sweeps, n_steps_per_sweep=100, verbose_freq=1, temperature_pot_mm=unit.Quantity(300, unit.kelvin), temperature_kin_mm=unit.Quantity(300, unit.kelvin), label="0", checkpoint_freq=100, restart=False, ): """ Method that runs a HMC sampler. Parameters ---------- settings : dict Dictionary containing global ParaMol settings. systems : list of :obj:`ParaMol.System.system.ParaMolSystem` List containing instances of ParaMol systems. n_sweeps : int Number of MC sweeps to perform. n_steps_per_sweep : int Number of MD steps per MC sweep. verbose_freq : int Verbose frequency. temperature_pot_mm : unit.Quantity Temperature used for the MM potential part. temperature_kin_mm : unit.Quantity Temperature used for the MM kinetic part. label : int HMC sampler label. It has to be an integer number. checkpoint_freq : int Frequency at which checkpoint restart files are written. restart : bool Flag that controls whether or not to perform a restart. Returns ------- systems : list List with the updated instances of ParaMol System. """ from ase_interface import ANIENS from ase_interface import aniensloader from ase import units as ase_unit assert len(systems) == 1, "HMC task currently only supports one system at once." # Create QM Engines and initiate OpenMM for system in systems: system.convert_system_ref_arrays_to_list() # Create OpenMM system system.engine.init_openmm(create_system_params=system.engine._create_system_params) system.engine.get_masses() # Create QM Engine if system.interface is None: system.interface = ParaMolInterface() system.create_qm_engines(settings.qm_engine["qm_engine"], settings.qm_engine[settings.qm_engine["qm_engine"].lower()]) # Create ASE NN calculator # Get atom list and atomic numbers list system.engine.get_atom_list() """ mm_ase_engine = ASEWrapper(system_name=system.name, interface=system.interface, calculator=ANIENS(aniensloader('../ani_models/ani-1ccx_8x.info', 0)), n_atoms=system.n_atoms, atom_list=system.engine.atom_list, n_calculations=1, cell=None, work_dir_prefix="NN_ASEWorkDir_") """ calc = ANIENS(aniensloader('/home/joao/programs/ASE_ANI/ani_models/ani-2x_8x.info',0)) #calc = DFTD3(dft=calc, cutoff=np.sqrt(9000) * ase_units.Bohr, damping="bj", a1=0.5719, a2=3.6017, s8=0.5883, s6=1.000, alpha6=1.0) mm_ase_engine = ASEWrapper(system_name=system.name, interface=system.interface, calculator=calc, n_atoms=system.n_atoms, atom_list=system.engine.atom_list, n_calculations=1, cell=None, work_dir_prefix="NN_ASEWorkDir_") system = systems[0] self._label = label parameter_space, objective_function, optimizer = (None, None, None) # TODO: Once this is included in the main ParaMol version, add this line to the default dictionary settings.restart["restart_hmc_file_{}".format(self._label)] = "restart_hmc_{}.pickle".format(self._label) if restart: logging.info("Starting HMC sampler parametrization from a previous restart.") # Read HMCSampler pickle self.__dict__ = self.read_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label)) # Read data into system system.read_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label))) else: self._n = 1 # MM chain self._n_total_mm = 0 self._n_accepted_mm = 0 while self._n <= n_sweeps: if self._n % verbose_freq == 0: print("HMC sampler of system {} # Sweep number {}.".format(system.name, self._n)) print("HMC sampler of system {} # Acceptance rate of MM chain {:.4f}".format(system.name, self._acceptance_rate_mm())) if len(system.ref_coordinates) > 0 and self._n != 1: # Do not need to compute the QM energy if there are structures in the top ensemble or if we are not in the first ever iteration. system.engine.set_positions(system.ref_coordinates[-1]) potential_initial_mm = mm_ase_engine.run_calculation(coords=system.ref_coordinates[-1] * 10, label=int(self._label)) coord_to_run = system.ref_coordinates[-1] * 10 else: # Compute MM initial kinetic and potential energy potential_initial_mm = mm_ase_engine.run_calculation(coords=system.engine.get_positions().in_units_of(unit.angstrom)._value, label=int(self._label)) coord_to_run = system.engine.get_positions().in_units_of(unit.angstrom)._value # Run short MD using ASE coords, potential_initial_mm, kinetic_initial, forces_initial, potential_final_mm, kinetic_final, forces_final = mm_ase_engine.run_md(coords=coord_to_run, label=int(self._label), steps=n_steps_per_sweep, dt=0.5*ase_unit.fs, initial_temperature=300.0*ase_unit.kB,) # Compute MM final kinetic and potential energy kinetic_initial = unit.Quantity(kinetic_initial, unit.kilojoules_per_mole) potential_initial_mm = unit.Quantity(potential_initial_mm, unit.kilojoules_per_mole) kinetic_final = unit.Quantity(kinetic_final, unit.kilojoules_per_mole) potential_final_mm = unit.Quantity(potential_final_mm, unit.kilojoules_per_mole) coords = unit.Quantity(coords, unit.nanometers) if self._hmc_acceptance_criterion_mm(potential_final_mm, potential_initial_mm, kinetic_final, kinetic_initial, temperature_pot_mm, temperature_kin_mm): # Append energies, forces and conformations system.ref_energies.append(potential_final_mm._value) system.ref_coordinates.append(coords._value) system.n_structures += 1 # TODO: include code related to partial momentum refreshment elif len(system.ref_coordinates) > 0: # Append last accepted structure system.ref_energies.append(system.ref_energies[-1]) system.ref_coordinates.append(system.ref_coordinates[-1]) system.n_structures += 1 else: # No structures have been accepted yet. pass # Write restart files if self._n % checkpoint_freq == 0: self.write_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label), self.__dict__) system.write_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label))) system.write_coordinates_xyz("{}_hmc_{}.xyz".format(system.name, self._label)) self._n += 1 return systems
def distributeLipids( boxsize, resnames, sigmas, cutoff, mass=39.9 * unit.amu, # argon epsilon=0.238 * unit.kilocalories_per_mole, # argon, switch_width=3.4 * unit.angstrom, # argon ): nparticles = len(resnames) # Determine Lennard-Jones cutoff. cutoff = cutoff * unit.angstrom cutoff_type = openmm.NonbondedForce.CutoffPeriodic # Create an empty system object. system = openmm.System() # Periodic box vectors. a = unit.Quantity( (boxsize[0] * unit.angstrom, 0 * unit.angstrom, 0 * unit.angstrom)) b = unit.Quantity( (0 * unit.angstrom, boxsize[1] * unit.angstrom, 0 * unit.angstrom)) c = unit.Quantity( (0 * unit.angstrom, 0 * unit.angstrom, boxsize[2] * unit.angstrom)) system.setDefaultPeriodicBoxVectors(a, b, c) # Set up periodic nonbonded interactions with a cutoff. nb = openmm.NonbondedForce() nb.setNonbondedMethod(cutoff_type) nb.setCutoffDistance(cutoff) nb.setUseDispersionCorrection(True) nb.setUseSwitchingFunction(False) if (switch_width is not None): nb.setUseSwitchingFunction(True) nb.setSwitchingDistance(cutoff - switch_width) for s in sigmas: system.addParticle(mass) nb.addParticle(0.0 * unit.elementary_charge, s * unit.angstrom, epsilon) positions = subrandom_particle_positions( nparticles, system.getDefaultPeriodicBoxVectors(), 2) # Add the nonbonded force. system.addForce(nb) # Add a restraining potential to keep atoms in z=0 energy_expression = 'k * (z^2)' force = openmm.CustomExternalForce(energy_expression) force.addGlobalParameter('k', 10) for particle_index in range(nparticles): force.addParticle(particle_index, []) system.addForce(force) # Create topology. topology = app.Topology() chain = topology.addChain() elems = ['Ar', 'Cl', 'Na'] _, idx = np.unique(resnames, return_inverse=True) for i in idx: element = app.Element.getBySymbol(elems[i]) residue = topology.addResidue(elems[i], chain) topology.addAtom(elems[i], element, residue) topology.setUnitCellDimensions(unit.Quantity(boxsize, unit.angstrom)) # Simulate it from simtk.openmm import LangevinIntegrator, VerletIntegrator from simtk.openmm.app import Simulation, PDBReporter, StateDataReporter, PDBFile from simtk.unit import kelvin, picoseconds, picosecond, angstrom from sys import stdout from mdtraj.reporters import DCDReporter nsteps = 10000 freq = 1 integrator = VerletIntegrator(0.002 * picoseconds) simulation = Simulation(topology, system, integrator) simulation.context.setPositions(positions) simulation.minimizeEnergy() # simulation.reporters.append(DCDReporter('output.dcd', 1)) # simulation.reporters.append(StateDataReporter(stdout, 1000, potentialEnergy=True, totalEnergy=True, step=True, separator=' ')) simulation.step(nsteps) state = simulation.context.getState(getPositions=True, enforcePeriodicBox=True) allfinalpos = state.getPositions(asNumpy=True).value_in_unit(angstrom) # with open('topology.pdb', 'w') as f: # PDBFile.writeFile(topology, positions, f) # from htmd.molecule.molecule import Molecule # mol = Molecule('topology.pdb') # mol.read('output.dcd') return allfinalpos
def readAmberSystem(prmtop_filename=None, prmtop_loader=None, shake=None, gbmodel=None, soluteDielectric=1.0, solventDielectric=78.5, nonbondedCutoff=None, nonbondedMethod='NoCutoff', scee=1.2, scnb=2.0, mm=None, verbose=False, EwaldErrorTolerance=None, flexibleConstraints=True, rigidWater=True): """ Create an OpenMM System from an Amber prmtop file. ARGUMENTS (specify one or the other, but not both) prmtop_filename (String) - name of Amber prmtop file (new-style only) prmtop_loader (PrmtopLoader) - the loaded prmtop file OPTIONAL ARGUMENTS shake (String) - if 'h-bonds', will SHAKE all bonds to hydrogen and water; if 'all-bonds', will SHAKE all bonds and water (default: None) gbmodel (String) - if 'OBC', OBC GBSA will be used; if 'GBVI', GB/VI will be used (default: None) soluteDielectric (float) - The solute dielectric constant to use in the implicit solvent model (default: 1.0) solventDielectric (float) - The solvent dielectric constant to use in the implicit solvent model (default: 78.5) nonbondedCutoff (float) - if specified, will set nonbondedCutoff (default: None) scnb (float) - 1-4 Lennard-Jones scaling factor (default: 1.2) scee (float) - 1-4 electrostatics scaling factor (default: 2.0) mm - if specified, this module will be used in place of pyopenmm (default: None) verbose (boolean) - if True, print out information on progress (default: False) flexibleConstraints (boolean) - if True, flexible bonds will be added in addition ot constrained bonds rigidWater (boolean=True) If true, water molecules will be fully rigid regardless of the value passed for the shake argument NOTES Even if bonds are SHAKEn, their harmonic stretch terms are still included in the potential. TODO Should these option names be changed to reflect their 'sander' counterparts? EXAMPLES Create a system of alanine dipeptide in implicit solvent. >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa') >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop') >>> system = readAmberSystem(prmtop_filename) Parse a prmtop file of alanine dipeptide in explicit solvent. >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit') >>> prmtop_filename = os.path.join(directory, 'alanine-dipeptide.prmtop') >>> system = readAmberSystem(prmtop_filename) """ if prmtop_filename is None and prmtop_loader is None: raise Exception("Must specify a filename or loader") if prmtop_filename is not None and prmtop_loader is not None: raise Exception("Cannot specify both a filename and a loader") if prmtop_filename is not None: # Load prmtop file. if verbose: print "Reading prmtop file '%s'..." % prmtop_filename prmtop = PrmtopLoader(prmtop_filename) else: prmtop = prmtop_loader if prmtop.getIfCap() > 0: raise Exception("CAP option not currently supported") if prmtop.getIfPert() > 0: raise Exception("perturbation not currently supported") if prmtop.getIfBox() > 1: raise Exception("only standard periodic boxes are currently supported") # Use pyopenmm implementation of OpenMM by default. if mm is None: mm = simtk.openmm # Create OpenMM System. if verbose: print "Creating OpenMM system..." system = mm.System() # Populate system with atomic masses. if verbose: print "Adding particles..." for mass in prmtop.getMasses(): system.addParticle(mass) # Add constraints. isWater = [ prmtop.getResidueLabel(i) == 'WAT' for i in range(prmtop.getNumAtoms()) ] if shake in ('h-bonds', 'all-bonds', 'h-angles'): for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH(): system.addConstraint(iAtom, jAtom, rMin) if shake in ('all-bonds', 'h-angles'): for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH(): system.addConstraint(iAtom, jAtom, rMin) if rigidWater and shake == None: for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH(): if isWater[iAtom] and isWater[jAtom]: system.addConstraint(iAtom, jAtom, rMin) # Add harmonic bonds. if verbose: print "Adding bonds..." force = mm.HarmonicBondForce() if flexibleConstraints or (shake not in ('h-bonds', 'all-bonds', 'h-angles')): for (iAtom, jAtom, k, rMin) in prmtop.getBondsWithH(): if flexibleConstraints or not (rigidWater and isWater[iAtom] and isWater[jAtom]): force.addBond(iAtom, jAtom, rMin, 2 * k) if flexibleConstraints or (shake not in ('all-bonds', 'h-angles')): for (iAtom, jAtom, k, rMin) in prmtop.getBondsNoH(): force.addBond(iAtom, jAtom, rMin, 2 * k) system.addForce(force) # Add harmonic angles. if verbose: print "Adding angles..." force = mm.HarmonicAngleForce() if shake == 'h-angles': numConstrainedBonds = system.getNumConstraints() atomConstraints = [[]] * system.getNumParticles() for i in range(system.getNumConstraints()): c = system.getConstraintParameters(i) distance = c[2].value_in_unit(units.nanometer) atomConstraints[c[0]].append((c[1], distance)) atomConstraints[c[1]].append((c[0], distance)) for (iAtom, jAtom, kAtom, k, aMin) in prmtop.getAngles(): if shake == 'h-angles': type1 = prmtop.getAtomType(iAtom) type2 = prmtop.getAtomType(jAtom) type3 = prmtop.getAtomType(kAtom) numH = len( [type for type in (type1, type3) if type.startswith('H')]) constrained = (numH == 2 or (numH == 1 and type2.startswith('O'))) else: constrained = False if constrained: # Find the two bonds that make this angle. l1 = None l2 = None for bond in atomConstraints[jAtom]: if bond[0] == iAtom: l1 = bond[1] elif bond[0] == kAtom: l2 = bond[1] # Compute the distance between atoms and add a constraint length = math.sqrt(l1 * l1 + l2 * l2 - 2 * l1 * l2 * math.cos(aMin)) system.addConstraint(iAtom, kAtom, length) if flexibleConstraints or not constrained: force.addAngle(iAtom, jAtom, kAtom, aMin, 2 * k) system.addForce(force) # Add torsions. if verbose: print "Adding torsions..." force = mm.PeriodicTorsionForce() for (iAtom, jAtom, kAtom, lAtom, forceConstant, phase, periodicity) in prmtop.getDihedrals(): force.addTorsion(iAtom, jAtom, kAtom, lAtom, periodicity, phase, forceConstant) system.addForce(force) # Add nonbonded interactions. if verbose: print "Adding nonbonded interactions..." force = mm.NonbondedForce() if (prmtop.getIfBox() == 0): # System is non-periodic. if nonbondedMethod == 'NoCutoff': force.setNonbondedMethod(mm.NonbondedForce.NoCutoff) elif nonbondedMethod == 'CutoffNonPeriodic': if nonbondedCutoff is None: raise Exception("No cutoff value specified") force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic) force.setCutoffDistance(nonbondedCutoff) else: raise Exception( "Illegal nonbonded method for a non-periodic system") else: # System is periodic. # Set periodic box vectors for periodic system (boxBeta, boxX, boxY, boxZ) = prmtop.getBoxBetaAndDimensions() d0 = units.Quantity(0.0, units.angstroms) xVec = units.Quantity((boxX, d0, d0)) yVec = units.Quantity((d0, boxY, d0)) zVec = units.Quantity((d0, d0, boxZ)) system.setDefaultPeriodicBoxVectors(xVec, yVec, zVec) # Set cutoff. if nonbondedCutoff is None: # Compute cutoff automatically. min_box_width = min([ boxX / units.nanometers, boxY / units.nanometers, boxZ / units.nanometers ]) CLEARANCE_FACTOR = 0.97 # reduce the cutoff to be a bit smaller than 1/2 smallest box length nonbondedCutoff = units.Quantity( (min_box_width * CLEARANCE_FACTOR) / 2.0, units.nanometers) if nonbondedMethod != 'NoCutoff': force.setCutoffDistance(nonbondedCutoff) # Set nonbonded method. if nonbondedMethod == 'NoCutoff': force.setNonbondedMethod(mm.NonbondedForce.NoCutoff) elif nonbondedMethod == 'CutoffNonPeriodic': force.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic) elif nonbondedMethod == 'CutoffPeriodic': force.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic) elif nonbondedMethod == 'Ewald': force.setNonbondedMethod(mm.NonbondedForce.Ewald) elif nonbondedMethod == 'PME': force.setNonbondedMethod(mm.NonbondedForce.PME) else: raise Exception("Cutoff method not understood.") if EwaldErrorTolerance is not None: force.setEwaldErrorTolerance(EwaldErrorTolerance) # Add per-particle nonbonded parameters. sigmaScale = 2**(-1. / 6.) * 2.0 for (charge, (rVdw, epsilon)) in zip(prmtop.getCharges(), prmtop.getNonbondTerms()): sigma = rVdw * sigmaScale force.addParticle(charge, sigma, epsilon) # Add 1-4 Interactions excludedAtomPairs = set() sigmaScale = 2**(-1. / 6.) for (iAtom, lAtom, chargeProd, rMin, epsilon) in prmtop.get14Interactions(): chargeProd /= scee epsilon /= scnb sigma = rMin * sigmaScale force.addException(iAtom, lAtom, chargeProd, sigma, epsilon) excludedAtomPairs.add(min((iAtom, lAtom), (lAtom, iAtom))) # Add Excluded Atoms excludedAtoms = prmtop.getExcludedAtoms() excludeParams = (0.0, 0.1, 0.0) for iAtom in range(prmtop.getNumAtoms()): for jAtom in excludedAtoms[iAtom]: if min((iAtom, jAtom), (jAtom, iAtom)) in excludedAtomPairs: continue force.addException(iAtom, jAtom, excludeParams[0], excludeParams[1], excludeParams[2]) system.addForce(force) # Add virtual sites for water. epNames = ['EP', 'LP'] ep = [ i for i in range(prmtop.getNumAtoms()) if isWater[i] and prmtop.getAtomName(i)[:2] in epNames ] if len(ep) > 0: epRes = set((prmtop.getResidueNumber(i) for i in ep)) numRes = max(epRes) + 1 # For each residue that contains an "extra point", find the oxygen, hydrogens, and points. waterO = [] waterH = [] waterEP = [] for i in range(numRes): waterO.append([]) waterH.append([]) waterEP.append([]) for i in range(prmtop.getNumAtoms()): res = prmtop.getResidueNumber(i) if res in epRes: name = prmtop.getAtomName(i) if name[0] == 'O': waterO[res].append(i) if name[0] == 'H': waterH[res].append(i) if name[:2] in epNames: waterEP[res].append(i) # Record bond lengths for faster access. distOH = [None] * numRes distHH = [None] * numRes distOE = [None] * numRes for (atom1, atom2, k, dist) in prmtop.getBondsWithH() + prmtop.getBondsNoH(): res = prmtop.getResidueNumber(atom1) if res in epRes: name1 = prmtop.getAtomName(atom1) name2 = prmtop.getAtomName(atom2) if name1[0] == 'H' or name2[0] == 'H': if name1[0] == 'H' and name2[0] == 'H': distHH[res] = dist if name1[0] == 'O' or name2[0] == 'O': distOH[res] = dist elif (name1[0] == 'O' or name2[0] == 'O') and ( (name1[:2] in epNames or name2[:2] in epNames)): distOE[res] = dist # Loop over residues and add the virtual sites. outOfPlaneAngle = 54.735 * units.degree cosOOP = units.cos(outOfPlaneAngle) sinOOP = units.sin(outOfPlaneAngle) for res in range(numRes): if len(waterO[res]) == 1 and len(waterH[res]) == 2: if len(waterEP[res]) == 1: # Four point water weightH = distOE[res] / math.sqrt(distOH[res]**2 - (0.5 * distHH[res])**2) system.setVirtualSite( waterEP[res][0], mm.ThreeParticleAverageSite(waterO[res][0], waterH[res][0], waterH[res][1], 1 - weightH, weightH / 2, weightH / 2)) elif len(waterEP[res]) == 2: # Five point water weightH = cosOOP * distOE[res] / math.sqrt( distOH[res]**2 - (0.5 * distHH[res])**2) angleHOH = 2 * math.asin(0.5 * distHH[res] / distOH[res]) lenCross = (distOH[res]**2) * math.sin(angleHOH) weightCross = sinOOP * distOE[res] / lenCross system.setVirtualSite( waterEP[res][0], mm.OutOfPlaneSite(waterO[res][0], waterH[res][0], waterH[res][1], weightH / 2, weightH / 2, weightCross)) system.setVirtualSite( waterEP[res][1], mm.OutOfPlaneSite(waterO[res][0], waterH[res][0], waterH[res][1], weightH / 2, weightH / 2, -weightCross)) # Add GBSA model. if gbmodel is not None: if verbose: print "Adding GB parameters..." charges = prmtop.getCharges() symbls = None if gbmodel == 'GBn': symbls = prmtop.getAtomTypes() gb_parms = prmtop.getGBParms(symbls) if gbmodel == 'HCT': gb = customgb.GBSAHCTForce(solventDielectric, soluteDielectric, 'ACE') elif gbmodel == 'OBC1': gb = customgb.GBSAOBC1Force(solventDielectric, soluteDielectric, 'ACE') elif gbmodel == 'OBC2': gb = mm.GBSAOBCForce() gb.setSoluteDielectric(soluteDielectric) gb.setSolventDielectric(solventDielectric) elif gbmodel == 'GBn': gb = customgb.GBSAGBnForce(solventDielectric, soluteDielectric, 'ACE') else: raise Exception( "Illegal value specified for implicit solvent model") for iAtom in range(prmtop.getNumAtoms()): if gbmodel == 'OBC2': gb.addParticle(charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]) else: gb.addParticle( [charges[iAtom], gb_parms[iAtom][0], gb_parms[iAtom][1]]) system.addForce(gb) if nonbondedMethod == 'NoCutoff': gb.setNonbondedMethod(mm.NonbondedForce.NoCutoff) elif nonbondedMethod == 'CutoffNonPeriodic': gb.setNonbondedMethod(mm.NonbondedForce.CutoffNonPeriodic) gb.setCutoffDistance(nonbondedCutoff) elif nonbondedMethod == 'CutoffPeriodic': gb.setNonbondedMethod(mm.NonbondedForce.CutoffPeriodic) gb.setCutoffDistance(nonbondedCutoff) else: raise Exception("Illegal nonbonded method for use with GBSA") force.setReactionFieldDielectric(1.0) # TODO: Add GBVI terms? return system
def _compute_energies(self): """ Compute energies of all replicas at all states. """ from scipy import weave start_time = time.time() # Temporary storage for computed phi and psi angles. phi = units.Quantity(numpy.zeros([self.nstates], numpy.float64), units.radians) psi = units.Quantity(numpy.zeros([self.nstates], numpy.float64), units.radians) # Compute reference energies. for replica_index in range(self.nstates): # Compute reference energy once. reference_energy = self.reference_state.reduced_potential( self.replica_coordinates[replica_index], platform=self.energy_platform) self.u_kl[replica_index, :] = reference_energy # Compute torsion angles. for replica_index in range(self.nstates): # Compute torsion angles. phi[replica_index] = self._compute_torsion( self.replica_coordinates[replica_index], 4, 6, 8, 14) psi[replica_index] = self._compute_torsion( self.replica_coordinates[replica_index], 6, 8, 14, 16) # Compute torsion energies. code = """ for(int replica_index = 0; replica_index < nstates; replica_index++) { double phi = PHI1(replica_index); double psi = PSI1(replica_index); long state_index = 1; for(int phi_index = 0; phi_index < nbins; phi_index++) { for(int psi_index = 0; psi_index < nbins; psi_index++) { // Compute torsion angles double phi0 = phi_index * delta; double psi0 = psi_index * delta; // Compute torsion energies. U_KL2(replica_index,state_index) += kappa*cos(phi-phi0) + kappa*cos(psi-psi0); state_index += 1; } } } """ # Stage input temporarily. nstates = self.nstates nbins = self.nbins delta = self.delta / units.radians kappa = self.kappa phi = phi / units.radians psi = psi / units.radians u_kl = self.u_kl try: # Execute inline C code with weave. info = weave.inline( code, ['nstates', 'nbins', 'delta', 'kappa', 'phi', 'psi', 'u_kl'], headers=['<math.h>', '<stdlib.h>'], verbose=2) self.u_kl = u_kl except: for replica_index in range(self.nstates): # Compute torsion restraint energies for all states. state_index = 1 for phi_index in range(self.nbins): phi0 = float(phi_index) * self.delta / units.radians for psi_index in range(self.nbins): psi0 = float(psi_index) * self.delta / units.radians # Compute torsion energies. self.u_kl[replica_index, state_index] += ( self.kappa ) * math.cos(phi[replica_index] - phi0) + ( self.kappa) * math.cos(psi[replica_index] - psi0) #print "(%6d,%6d) : %16s %16s : %16.1f %16.1f" % (phi_index, psi_index, str(phi), str(psi), self.u_kl[replica_index,state_index], self.states[state_index].reduced_potential(self.replica_coordinates[replica_index])) # Increment state index. state_index += 1 end_time = time.time() elapsed_time = end_time - start_time time_per_energy = elapsed_time / float(self.nstates)**2 if self.verbose: print "Time to compute all energies %.3f s (%.3f per energy calculation).\n" % ( elapsed_time, time_per_energy) return
def get_standard_state_correction(self): """ Return the standard state correction. Returns ------- correction : float The standard-state correction, in kT """ initial_time = time.time() r_min = 0 * unit.nanometers r_max = 100 * unit.nanometers # TODO: Use maximum distance between atoms? # Create a System object containing two particles connected by the reference force system = openmm.System() system.addParticle(1.0 * unit.amu) system.addParticle(1.0 * unit.amu) force = self._create_restraint_force(0, 1) # Disable the PBC if on for this approximation of the analytical solution try: # This was added in OpenMM 7.1 force.setUsesPeriodicBoundaryConditions(False) except AttributeError: pass system.addForce(force) # Create a Reference context to evaluate energies on the CPU. integrator = openmm.VerletIntegrator(1.0 * unit.femtoseconds) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, integrator, platform) # Set default positions. positions = unit.Quantity(np.zeros([2,3]), unit.nanometers) context.setPositions(positions) # Create a function to compute integrand as a function of interparticle separation. beta = self.beta def integrand(r): """ Parameters ---------- r : float Inter-particle separation in nanometers Returns ------- dI : float Contribution to integrand (in nm^2). """ positions[1, 0] = r * unit.nanometers context.setPositions(positions) state = context.getState(getEnergy=True) potential = state.getPotentialEnergy() dI = 4.0 * math.pi * r**2 * math.exp(-beta * potential) return dI (shell_volume, shell_volume_error) = scipy.integrate.quad(lambda r : integrand(r), r_min / unit.nanometers, r_max / unit.nanometers) * unit.nanometers**3 # integrate shell volume logger.debug("shell_volume = %f nm^3" % (shell_volume / unit.nanometers**3)) # Compute standard-state volume for a single molecule in a box of size (1 L) / (avogadros number) # Should also generate constant V0 liter = 1000.0 * unit.centimeters**3 # one liter box_volume = liter / (unit.AVOGADRO_CONSTANT_NA*unit.mole) # standard state volume logger.debug("box_volume = %f nm^3" % (box_volume / unit.nanometers**3)) # Compute standard state correction for releasing shell restraints into standard-state box (in units of kT). DeltaG = - math.log(box_volume / shell_volume) logger.debug("Standard state correction: %.3f kT" % DeltaG) final_time = time.time() elapsed_time = final_time - initial_time logger.debug("restraints: _computeStandardStateCorrection: %.3f s elapsed" % elapsed_time) # Return standard state correction (in kT). return DeltaG
def compute_hydration_energies(molecules, parameters): """ Compute solvation energies of a set of molecules given a GBSA parameter set. ARGUMENTS molecules (list of OEMol) - molecules with GBSA assigned atom types in type field parameters (dict) - dictionary of GBSA parameters keyed on GBSA atom types RETURNS energies (dict) - energies[molecule] is the computed solvation energy of given molecule """ energies = dict( ) # energies[index] is the computed solvation energy of molecules[index] platform = openmm.Platform.getPlatformByName("Reference") for molecule in molecules: # Create OpenMM System. system = openmm.System() for atom in molecule.GetAtoms(): mass = OEGetDefaultMass(atom.GetAtomicNum()) system.addParticle(mass * units.amu) # Add nonbonded term. # nonbonded_force = openmm.NonbondedSoftcoreForce() # nonbonded_force.setNonbondedMethod(openmm.NonbondedForce.NoCutoff) # for atom in molecule.GetAtoms(): # charge = 0.0 * units.elementary_charge # sigma = 1.0 * units.angstrom # epsilon = 0.0 * units.kilocalories_per_mole # nonbonded_force.addParticle(charge, sigma, epsilon) # system.addForce(nonbonded_force) # Add GBSA term gbsa_force = openmm.GBSAOBCForce() gbsa_force.setNonbondedMethod( openmm.GBSAOBCForce.NoCutoff) # set no cutoff gbsa_force.setSoluteDielectric(1) gbsa_force.setSolventDielectric(78) # Build indexable list of atoms. atoms = [atom for atom in molecule.GetAtoms()] # Assign GBSA parameters. for atom in molecule.GetAtoms(): atomtype = atom.GetStringData("gbsa_type") # GBSA atomtype charge = atom.GetPartialCharge() * units.elementary_charge radius = parameters['%s_%s' % (atomtype, 'radius')] * units.angstroms scalingFactor = parameters[ '%s_%s' % (atomtype, 'scalingFactor')] * units.kilocalories_per_mole gbsa_force.addParticle(charge, radius, scalingFactor) # Add the force to the system. system.addForce(gbsa_force) # Build coordinate array. natoms = len(atoms) coordinates = units.Quantity(numpy.zeros([natoms, 3]), units.angstroms) for (index, atom) in enumerate(atoms): (x, y, z) = molecule.GetCoords(atom) coordinates[index, :] = units.Quantity(numpy.array([x, y, z]), units.angstroms) # Create OpenMM Context. timestep = 1.0 * units.femtosecond # arbitrary integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(system, integrator, platform) # Set the coordinates. context.setPositions(coordinates) # Get the energy state = context.getState(getEnergy=True) energies[molecule] = state.getPotentialEnergy() return energies
def WCADimer(N=natoms, density=density, mm=None, mass=mass, epsilon=epsilon, sigma=sigma, h=h, r0=r0, w=w): """ Create a bistable bonded pair of particles (indices 0 and 1) optionally surrounded by a Weeks-Chandler-Andersen fluid. The bistable potential has form U(r) = h*(1-((r-r0-w)/w)^2)^2 where r0 is the compact state separation, r0+2w is the extended state separation, and h is the barrier height. The WCA potential has form U(r) = 4 epsilon [ (sigma/r)^12 - (sigma/r)^6 ] + epsilon (r < r*) = 0 (r >= r*) where r* = 2^(1/6) sigma. OPTIONAL ARGUMENTS N (int) - total number of atoms (default: 2) density (float) - number density of particles (default: 0.96 / sigma**3) mass (simtk.unit.Quantity of mass) - particle mass (default: 39.948 amu) sigma (simtk.unit.Quantity of length) - Lennard-Jones sigma parameter (default: 0.3405 nm) epsilon (simtk.unit.Quantity of energy) - Lennard-Jones well depth (default: (119.8 Kelvin)*kB) h (simtk.unit.Quantity of energy) - bistable potential barrier height (default: ???) r0 (simtk.unit.Quantity of length) - bistable potential compact state separation (default: ???) w (simtk.unit.Quantity of length) - bistable potential extended state separation is r0+2*w (default: ???) """ # Choose OpenMM package. if mm is None: mm = openmm # Compute cutoff for WCA fluid. r_WCA = 2.**(1. / 6.) * sigma # cutoff at minimum of potential # Create system system = mm.System() # Compute total system volume. volume = N / density # Make system cubic in dimension. length = volume**(1.0 / 3.0) a = units.Quantity(numpy.array([1.0, 0.0, 0.0], numpy.float32), units.nanometer) * length / units.nanometer b = units.Quantity(numpy.array([0.0, 1.0, 0.0], numpy.float32), units.nanometer) * length / units.nanometer c = units.Quantity(numpy.array([0.0, 0.0, 1.0], numpy.float32), units.nanometer) * length / units.nanometer print "box edge length = %s" % str(length) system.setDefaultPeriodicBoxVectors(a, b, c) # Add particles to system. for n in range(N): system.addParticle(mass) # WCA: Lennard-Jones truncated at minim and shifted so potential is zero at cutoff. energy_expression = '4.0*epsilon*((sigma/r)^12 - (sigma/r)^6) + epsilon' # Create force. force = mm.CustomNonbondedForce(energy_expression) # Set epsilon and sigma global parameters. force.addGlobalParameter('epsilon', epsilon) force.addGlobalParameter('sigma', sigma) # Add particles for n in range(N): force.addParticle([]) # Add exclusion between bonded particles. force.addExclusion(0, 1) # Set periodic boundary conditions with cutoff. if (N > 2): force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffPeriodic) else: force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffNonPeriodic) print "setting cutoff distance to %s" % str(r_WCA) force.setCutoffDistance(r_WCA) # Add nonbonded force term to the system. system.addForce(force) # Add dimer potential to first two particles. dimer_force = openmm.CustomBondForce('h*(1-((r-r0-w)/w)^2)^2;') dimer_force.addGlobalParameter('h', h) # barrier height dimer_force.addGlobalParameter('r0', r0) # compact state separation dimer_force.addGlobalParameter('w', w) # second minimum is at r0 + 2*w dimer_force.addBond(0, 1, []) system.addForce(dimer_force) # Create initial coordinates using random positions. coordinates = units.Quantity(numpy.random.rand(N, 3), units.nanometer) * (length / units.nanometer) # Reposition dimer particles at compact minimum. coordinates[0, :] *= 0.0 coordinates[1, :] *= 0.0 coordinates[1, 0] = r0 # Return system and coordinates. return [system, coordinates]
def __rmul__(self, other): """Multiply a Vec3 by a constant.""" if unit.is_unit(other): return unit.Quantity(self, other) return Vec3(other * self[0], other * self[1], other * self[2])
'%s_%s' % (atomtype, 'scalingFactor')] * units.kilocalories_per_mole except Exception, exception: print "Cannot find parameters for atomtype '%s' in molecule '%s'" % ( atomtype, molecule.GetTitle()) print parameters.keys() raise exception gbsa_force.addParticle(charge, radius, scalingFactor) # # Add the force to the system. system.addForce(gbsa_force) # Build coordinate array. natoms = len(atoms) coordinates = units.Quantity(numpy.zeros([natoms, 3]), units.angstroms) for (index, atom) in enumerate(atoms): (x, y, z) = molecule.GetCoords(atom) coordinates[index, :] = units.Quantity(numpy.array([x, y, z]), units.angstroms) # Create OpenMM Context. timestep = 1.0 * units.femtosecond # arbitrary integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(system, integrator, platform) # Set the coordinates. context.setPositions(coordinates) # Get the energy state = context.getState(getEnergy=True)
def _create_phase(self, phase, reference_system, positions, atom_indices, thermodynamic_state, protocols=None): """ Create a repex object for a specified phase. Parameters ---------- phase : str The phase being initialized (one of ['complex', 'solvent', 'vacuum']) reference_system : simtk.openmm.System The reference system object from which alchemical intermediates are to be construcfted. positions : list of simtk.unit.Qunatity objects containing (natoms x 3) positions (as np or lists) The list of positions to be used to seed replicas in a round-robin way. atom_indices : dict atom_indices[phase][component] is the set of atom indices associated with component, where component is ['ligand', 'receptor', 'complex', 'solvent', 'ligand_counterions'] thermodynamic_state : ThermodynamicState Thermodynamic state from which reference temperature and pressure are to be taken. protocols : dict of list of AlchemicalState, optional, default=None If specified, the alchemical protocol protocols[phase] will be used for phase 'phase' instead of the default. """ # We add default repex options only on creation, on resume repex will pick them from the store file repex_parameters = { 'number_of_equilibration_iterations': 0, 'number_of_iterations': 100, 'timestep': 2.0 * unit.femtoseconds, 'collision_rate': 5.0 / unit.picoseconds, 'minimize': False, 'show_mixing_statistics': True, # this causes slowdown with iteration and should not be used for production 'displacement_sigma': 1.0 * unit.nanometers # attempt to displace ligand by this stddev will be made each iteration } repex_parameters.update(self._repex_parameters) # Make sure positions argument is a list of coordinate snapshots. if hasattr(positions, 'unit'): # Wrap in list. positions = [positions] # Check the dimensions of positions. for index in range(len(positions)): # Make sure it is recast as a np array. positions[index] = unit.Quantity(np.array(positions[index] / positions[index].unit), positions[index].unit) [natoms, ndim] = (positions[index] / positions[index].unit).shape if natoms != reference_system.getNumParticles(): raise Exception("positions argument must be a list of simtk.unit.Quantity of (natoms,3) lists or np array with units compatible with nanometers.") # Create metadata storage. metadata = dict() # Make a deep copy of the reference system so we don't accidentally modify it. reference_system = copy.deepcopy(reference_system) # TODO: Use more general approach to determine whether system is periodic. is_periodic = self._is_periodic(reference_system) # Make sure pressure is None if not periodic. if not is_periodic: thermodynamic_state.pressure = None # Compute standard state corrections for complex phase. metadata['standard_state_correction'] = 0.0 # TODO: Do we need to include a standard state correction for other phases in periodic boxes? if phase == 'complex-implicit': # Impose restraints for complex system in implicit solvent to keep ligand from drifting too far away from receptor. logger.debug("Creating receptor-ligand restraints...") reference_positions = positions[0] if self._restraint_type == 'harmonic': restraints = HarmonicReceptorLigandRestraint(thermodynamic_state, reference_system, reference_positions, atom_indices['receptor'], atom_indices['ligand']) elif self._restraint_type == 'flat-bottom': restraints = FlatBottomReceptorLigandRestraint(thermodynamic_state, reference_system, reference_positions, atom_indices['receptor'], atom_indices['ligand']) else: raise Exception("restraint_type of '%s' is not supported." % self._restraint_type) force = restraints.getRestraintForce() # Get Force object incorporating restraints reference_system.addForce(force) metadata['standard_state_correction'] = restraints.getStandardStateCorrection() # standard state correction in kT elif phase == 'complex-explicit': # For periodic systems, we do not use a restraint, but must add a standard state correction for the box volume. # TODO: What if the box volume fluctuates during the simulation? box_vectors = reference_system.getDefaultPeriodicBoxVectors() box_volume = thermodynamic_state._volume(box_vectors) STANDARD_STATE_VOLUME = 1660.53928 * unit.angstrom**3 metadata['standard_state_correction'] = - np.log(STANDARD_STATE_VOLUME / box_volume) # Use default alchemical protocols if not specified. if not protocols: protocols = self.default_protocols # Create alchemically-modified states using alchemical factory. logger.debug("Creating alchemically-modified states...") try: alchemical_indices = atom_indices['ligand_counterions'] + atom_indices['ligand'] except KeyError: alchemical_indices = atom_indices['ligand'] factory = AbsoluteAlchemicalFactory(reference_system, ligand_atoms=alchemical_indices, **self._alchemy_parameters) alchemical_states = protocols[phase] alchemical_system = factory.alchemically_modified_system thermodynamic_state.system = alchemical_system # Check systems for finite energies. # TODO: Refactor this into another function. finite_energy_check = False if finite_energy_check: logger.debug("Checking energies are finite for all alchemical systems.") integrator = openmm.VerletIntegrator(1.0 * unit.femtosecond) context = openmm.Context(alchemical_system, integrator) context.setPositions(positions[0]) for alchemical_state in alchemical_states: AbsoluteAlchemicalFactory.perturbContext(context, alchemical_state) potential = context.getState(getEnergy=True).getPotentialEnergy() if np.isnan(potential / unit.kilocalories_per_mole): raise Exception("Energy for system %d is NaN." % index) del context, integrator logger.debug("All energies are finite.") # Randomize ligand position if requested, but only for implicit solvent systems. if self._randomize_ligand and (phase == 'complex-implicit'): logger.debug("Randomizing ligand positions and excluding overlapping configurations...") randomized_positions = list() nstates = len(alchemical_states) for state_index in range(nstates): positions_index = np.random.randint(0, len(positions)) current_positions = positions[positions_index] new_positions = ModifiedHamiltonianExchange.randomize_ligand_position(current_positions, atom_indices['receptor'], atom_indices['ligand'], self._randomize_ligand_sigma_multiplier * restraints.getReceptorRadiusOfGyration(), self._randomize_ligand_close_cutoff) randomized_positions.append(new_positions) positions = randomized_positions if self._randomize_ligand and (phase == 'complex-explicit'): logger.warning("Ligand randomization requested, but will not be performed for explicit solvent simulations.") # Identify whether any atoms will be displaced via MC, unless option is turned off. mc_atoms = None if self._mc_displacement_sigma: mc_atoms = list() if 'ligand' in atom_indices: mc_atoms = atom_indices['ligand'] # Set up simulation. # TODO: Support MPI initialization? logger.debug("Creating replica exchange object...") store_filename = os.path.join(self._store_directory, phase + '.nc') self._store_filenames[phase] = store_filename simulation = ModifiedHamiltonianExchange(store_filename) simulation.create(thermodynamic_state, alchemical_states, positions, displacement_sigma=self._mc_displacement_sigma, mc_atoms=mc_atoms, options=repex_parameters, metadata=metadata) # Initialize simulation. # TODO: Use the right scheme for initializing the simulation without running. #logger.debug("Initializing simulation...") #simulation.run(0) # Clean up simulation. del simulation # Add to list of phases that have been set up. self._phases.append(phase) return
import os import numpy as np import matplotlib.pyplot as pyplot from simtk import unit from simtk.openmm.app.pdbfile import PDBFile import mdtraj as md from cg_openmm.cg_model.cgmodel import CGModel from cg_openmm.parameters.reweight import * from cg_openmm.ensembles.ens_build import * from cg_openmm.parameters.secondary_structure import * from cg_openmm.simulation.tools import * # Define Boltzmann's constant kB = unit.Quantity(0.0019872041, unit.kilocalorie_per_mole) total_simulation_time = 100.0 * unit.picosecond simulation_time_step = 5.0 * unit.femtosecond print_frequency = 5 temperature = 300.0 kT = kB * temperature output_directory = "scan_T_output" if not os.path.exists(output_directory): os.mkdir(output_directory) # Model settings polymer_length = 12 backbone_lengths = [1] sidechain_lengths = [1] sidechain_positions = [0] include_bond_forces = False include_bond_angle_forces = False
def trajectory_from_mdtraj(mdtrajectory, simple_topology=False, velocities=None): """ Construct a Trajectory object from an mdtraj.Trajectory object Parameters ---------- mdtrajectory : mdtraj.Trajectory Input mdtraj.Trajectory simple_topology : bool if `True` only a simple topology with n_atoms will be created. This cannot be used with complex CVs but loads and stores very fast velocities : np.array velocities in units of nm/ps Returns ------- openpathsampling.engines.Trajectory the constructed Trajectory instance """ trajectory = Trajectory() vel_unit = u.nanometer / u.picosecond if simple_topology: topology = Topology(*mdtrajectory.xyz[0].shape) else: topology = MDTrajTopology(mdtrajectory.topology) if velocities is None: empty_vel = u.Quantity(np.zeros(mdtrajectory.xyz[0].shape), vel_unit) engine = TopologyEngine(topology) for frame_num in range(len(mdtrajectory)): # mdtraj trajectories only have coordinates and box_vectors coord = u.Quantity(mdtrajectory.xyz[frame_num], u.nanometers) if velocities is not None: vel = u.Quantity(velocities[frame_num], vel_unit) else: vel = empty_vel if mdtrajectory.unitcell_vectors is not None: box_v = u.Quantity(mdtrajectory.unitcell_vectors[frame_num], u.nanometers) else: box_v = None statics = Snapshot.StaticContainer( coordinates=coord, box_vectors=box_v ) kinetics = Snapshot.KineticContainer(velocities=vel) snap = Snapshot( statics=statics, kinetics=kinetics, engine=engine ) trajectory.append(snap) return trajectory
def _run(self, state: interfaces.IState, minimize: bool) -> interfaces.IState: # update the transformers to account for sampled parameters # stored in the state self._transformers_update(state) assert abs(state.alpha - self._alpha) < 1e-6 # Run Monte Carlo position updates if minimize: state = self._run_min_mc(state) else: state = self._run_mc(state) # Run MonteCarlo parameter updates state = self._run_param_mc(state) coordinates = u.Quantity(state.positions, u.nanometer) velocities = u.Quantity(state.velocities, u.nanometer / u.picosecond) box_vectors = u.Quantity(state.box_vector, u.nanometer) # set the positions self._simulation.context.setPositions(coordinates) # if explicit solvent, then set the box vectors if self._options.solvation == "explicit": self._simulation.context.setPeriodicBoxVectors( [box_vectors[0].value_in_unit(u.nanometer), 0.0, 0.0], [0.0, box_vectors[1].value_in_unit(u.nanometer), 0.0], [0.0, 0.0, box_vectors[2].value_in_unit(u.nanometer)], ) # run energy minimization if minimize: self._simulation.minimizeEnergy(maxIterations=self._options.minimize_steps) # set the velocities self._simulation.context.setVelocities(velocities) # run timesteps self._simulation.step(self._options.timesteps) # extract coords, vels, energy and strip units if self._options.solvation == "implicit": snapshot = self._simulation.context.getState( getPositions=True, getVelocities=True, getEnergy=True ) elif self._options.solvation == "explicit": snapshot = self._simulation.context.getState( getPositions=True, getVelocities=True, getEnergy=True, enforcePeriodicBox=True, ) coordinates = snapshot.getPositions(asNumpy=True).value_in_unit(u.nanometer) velocities = snapshot.getVelocities(asNumpy=True).value_in_unit( u.nanometer / u.picosecond ) _check_for_nan(coordinates, velocities, self._rank) # if explicit solvent, the recover the box vectors if self._options.solvation == "explicit": box_vector = snapshot.getPeriodicBoxVectors().value_in_unit(u.nanometer) box_vector = np.array( (box_vector[0][0], box_vector[1][1], box_vector[2][2]) ) # just store zeros for implicit solvent else: box_vector = np.zeros(3) # get the energy e_potential = ( snapshot.getPotentialEnergy().value_in_unit(u.kilojoule / u.mole) / GAS_CONSTANT / self._temperature ) # store in state state.positions = coordinates state.velocities = velocities state.energy = e_potential state.box_vector = box_vector return state
def WCAFluid(N=natoms, density=density, mm=None, mass=mass, epsilon=epsilon, sigma=sigma): """ Create a Weeks-Chandler-Andersen system. OPTIONAL ARGUMENTS N (int) - total number of atoms (default: 150) density (float) - N sigma^3 / V (default: 0.96) sigma epsilon """ # Choose OpenMM package. if mm is None: mm = openmm # Create system system = mm.System() # Compute total system volume. volume = N / density # Make system cubic in dimension. length = volume**(1.0 / 3.0) # TODO: Can we change this to use tuples or 3x3 array? a = units.Quantity(numpy.array([1.0, 0.0, 0.0], numpy.float32), units.nanometer) * length / units.nanometer b = units.Quantity(numpy.array([0.0, 1.0, 0.0], numpy.float32), units.nanometer) * length / units.nanometer c = units.Quantity(numpy.array([0.0, 0.0, 1.0], numpy.float32), units.nanometer) * length / units.nanometer print "box edge length = %s" % str(length) system.setDefaultPeriodicBoxVectors(a, b, c) # Add particles to system. for n in range(N): system.addParticle(mass) # Create nonbonded force term implementing Kob-Andersen two-component Lennard-Jones interaction. energy_expression = '4.0*epsilon*((sigma/r)^12 - (sigma/r)^6) + epsilon' # Create force. force = mm.CustomNonbondedForce(energy_expression) # Set epsilon and sigma global parameters. force.addGlobalParameter('epsilon', epsilon) force.addGlobalParameter('sigma', sigma) # Add particles for n in range(N): force.addParticle([]) # Set periodic boundary conditions with cutoff. force.setNonbondedMethod(mm.CustomNonbondedForce.CutoffNonPeriodic) print "setting cutoff distance to %s" % str(r_WCA) force.setCutoffDistance(r_WCA) # Add nonbonded force term to the system. system.addForce(force) # Create initial coordinates using random positions. coordinates = units.Quantity(numpy.random.rand(N, 3), units.nanometer) * (length / units.nanometer) # Return system and coordinates. return [system, coordinates]
def run_endpoint_perturbation(lambda_thermodynamic_state, nonalchemical_thermodynamic_state, initial_hybrid_sampler_state, mc_move, n_iterations, factory, lambda_index=0, print_work=False, write_system=False, write_state=False, write_trajectories=False): """ Parameters ---------- lambda_thermodynamic_state : ThermodynamicState The thermodynamic state corresponding to the hybrid system at a lambda endpoint nonalchemical_thermodynamic_state : ThermodynamicState The nonalchemical thermodynamic state for the relevant endpoint initial_hybrid_sampler_state : SamplerState Starting positions for the sampler. Must be compatible with lambda_thermodynamic_state mc_move : MCMCMove The MCMove that will be used for sampling at the lambda endpoint n_iterations : int The number of iterations factory : HybridTopologyFactory The hybrid topology factory lambda_index : int, optional, default=0 The index, 0 or 1, at which to retrieve nonalchemical positions print_work : bool, optional, default=False If True, will print work values write_system : bool, optional, default=False If True, will write alchemical and nonalchemical System XML files write_state : bool, optional, default=False If True, write alchemical (hybrid) State XML files each iteration write_trajectories : bool, optional, default=False If True, will write trajectories Returns ------- df : float Free energy difference between alchemical and nonalchemical systems, estimated with EXP ddf : float Standard deviation of estimate, corrected for correlation, from EXP estimator. """ import mdtraj as md #run an initial minimization: mcmc_sampler = mcmc.MCMCSampler(lambda_thermodynamic_state, initial_hybrid_sampler_state, mc_move) mcmc_sampler.minimize(max_iterations=20) new_sampler_state = mcmc_sampler.sampler_state if write_system: with open(f'hybrid{lambda_index}-system.xml', 'w') as outfile: outfile.write( openmm.XmlSerializer.serialize( lambda_thermodynamic_state.system)) with open(f'nonalchemical{lambda_index}-system.xml', 'w') as outfile: outfile.write( openmm.XmlSerializer.serialize( nonalchemical_thermodynamic_state.system)) #initialize work array w = np.zeros([n_iterations]) non_potential = np.zeros([n_iterations]) hybrid_potential = np.zeros([n_iterations]) #run n_iterations of the endpoint perturbation: hybrid_trajectory = unit.Quantity( np.zeros([ n_iterations, lambda_thermodynamic_state.system.getNumParticles(), 3 ]), unit.nanometers) # DEBUG nonalchemical_trajectory = unit.Quantity( np.zeros([ n_iterations, nonalchemical_thermodynamic_state.system.getNumParticles(), 3 ]), unit.nanometers) # DEBUG for iteration in range(n_iterations): # Generate a new sampler state for the hybrid system mc_move.apply(lambda_thermodynamic_state, new_sampler_state) # Compute the hybrid reduced potential at the new sampler state hybrid_context, integrator = cache.global_context_cache.get_context( lambda_thermodynamic_state) new_sampler_state.apply_to_context(hybrid_context, ignore_velocities=True) hybrid_reduced_potential = lambda_thermodynamic_state.reduced_potential( hybrid_context) if write_state: state = hybrid_context.getState(getPositions=True, getParameters=True) state_xml = openmm.XmlSerializer.serialize(state) with open(f'state{iteration}_l{lambda_index}.xml', 'w') as outfile: outfile.write(state_xml) # Construct a sampler state for the nonalchemical system if lambda_index == 0: nonalchemical_positions = factory.old_positions( new_sampler_state.positions) elif lambda_index == 1: nonalchemical_positions = factory.new_positions( new_sampler_state.positions) else: raise ValueError( "The lambda index needs to be either one or zero for this to be meaningful" ) nonalchemical_sampler_state = SamplerState( nonalchemical_positions, box_vectors=new_sampler_state.box_vectors) if write_trajectories: state = hybrid_context.getState(getPositions=True) hybrid_trajectory[iteration, :, :] = state.getPositions( asNumpy=True) nonalchemical_trajectory[iteration, :, :] = nonalchemical_positions # Compute the nonalchemical reduced potential nonalchemical_context, integrator = cache.global_context_cache.get_context( nonalchemical_thermodynamic_state) nonalchemical_sampler_state.apply_to_context(nonalchemical_context, ignore_velocities=True) nonalchemical_reduced_potential = nonalchemical_thermodynamic_state.reduced_potential( nonalchemical_context) # Compute and store the work w[iteration] = nonalchemical_reduced_potential - hybrid_reduced_potential non_potential[iteration] = nonalchemical_reduced_potential hybrid_potential[iteration] = hybrid_reduced_potential if print_work: print( f'{iteration:8d} {hybrid_reduced_potential:8.3f} {nonalchemical_reduced_potential:8.3f} => {w[iteration]:8.3f}' ) if write_trajectories: if lambda_index == 0: nonalchemical_mdtraj_topology = md.Topology.from_openmm( factory._topology_proposal.old_topology) elif lambda_index == 1: nonalchemical_mdtraj_topology = md.Topology.from_openmm( factory._topology_proposal.new_topology) md.Trajectory( hybrid_trajectory / unit.nanometers, factory.hybrid_topology).save(f'hybrid{lambda_index}.pdb') md.Trajectory(nonalchemical_trajectory / unit.nanometers, nonalchemical_mdtraj_topology).save( f'nonalchemical{lambda_index}.pdb') # Analyze data and return results [t0, g, Neff_max] = timeseries.detectEquilibration(w) w_burned_in = w[t0:] [df, ddf] = pymbar.EXP(w_burned_in) ddf_corrected = ddf * np.sqrt(g) results = [df, ddf_corrected, t0, Neff_max] return results, non_potential, hybrid_potential
def strip_units(coords): return unit.Quantity(np.array(coords / coords.unit), coords.unit)
from __future__ import print_function, absolute_import import os import shutil import numpy as np import xml.etree.ElementTree as ET import simtk.unit as unit import simtk.openmm as omm import simtk.openmm.app as app import simulation.openmm.util as util import simulation.openmm.additional_reporters as additional_reporters global energy_minimization_tol energy_minimization_tol = unit.Quantity(value=10., unit=unit.kilojoule_per_mole) def adaptively_find_best_pressure(target_volume, ff_files, name, n_beads, cutoff, r_switch, refT, save_forces=False, cuda=False, p0=4000.): """Adaptively change pressure to reach target volume (density)""" temperature = refT*unit.kelvin collision_rate = 1.0/unit.picosecond timestep = 0.002*unit.picosecond n_steps = 5000 nsteps_out = 100 pressure = p0*unit.atmosphere # starting pressure dynamics = "Langevin" ensemble = "NPT" traj_idx = 1
def createAmberInputFiles(topology, system, state, nproteinatoms, openmm_forcefields_to_use, amber_forcefield_to_use, prmtop_filename, inpcrd_filename, verbose=False, shell='/bin/tcsh'): ''' Create AMBER input files from an OpenMM system. ARGUMENTS topology (simtk.openmm.app.Topology) - the topology of the system to be simulated system (simtk.openmm.System) - the System object for the system to be simulated state (simtk.openmm.State) - the State of the system to be simulated nproteinatoms (int) - number of protein atoms (required for centering) openmm_forcefields_to_use (list of string) - list of forcefield XML files to use in generating AMBER translation names amber_forcefield_to_use (string) - protein force field to be used in LEaP prmtop_filename (string) - the filename of the AMBER prmtop file to be created inpcrd_filename (string) - the filename of the AMBER inpcrd file to be created OPTIONAL ARGUMENTS verbose (boolean) - if True, will print verbose output NOTES Note that AmberTools must be installed. LEaP is used to create the input files. TODO * ?Use a single forcefields_to_use argument to automatically select the appropriate AMBER and OpenMM forcefield files. * Use temporary directory and copy out to desired filenames. * Decide on whether to keep ambernames.pdb or amber.pdb in each directory RETURNS None ''' from simtk import openmm from simtk import unit # Create box and center protein if verbose: print("Creating box and centering protein in unit cell...") integrator = openmm.VerletIntegrator(1.0 * unit.femtoseconds) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, integrator, platform) context.setState(state) state = context.getState(getPositions=True, enforcePeriodicBox=True) positions = state.getPositions(asNumpy=True) box_vectors = state.getPeriodicBoxVectors(asNumpy=True) mean = unit.Quantity((positions[0:nproteinatoms,:] / unit.angstrom).mean(0), unit.angstroms) for i in range(system.getNumParticles()): positions[i,:] -= mean[:] + box_vectors[0,0]/2.0 context.setPositions(positions) state = context.getState(getPositions=True, enforcePeriodicBox=True) positions = state.getPositions(asNumpy=True) del context # Configure text for LEaP input file leap_template = ''' # Set up solvated protein system for explicit solvent simulation. # Load AMBER ff99sb-ildn forcefield for protein. source %(amber_forcefield_to_use)s # Load modified ion parameters. loadAmberParams frcmod.ionsjc_tip3p # Load in protein (with all residues modeled in). system = loadPdb ambernames.pdb # Generate box. solvatebox system TIP3PBOX 0.0001 10000 iso # Check protein. check system # Report on net charge. charge system # Write parameters. saveAmberParm system amber.prmtop amber.crd # Exit quit ''' % vars() # Write LEaP input file. if verbose: print("Writing LEaP input file...") leap_filename = 'setup.leap.in' outfile = open(leap_filename, 'w') outfile.write(leap_template) outfile.close() # Clear leap.log. import os, os.path leap_log_filename = 'leap.log' if os.path.exists(leap_log_filename): os.remove(leap_log_filename) # Determine atom names. (atoms, sorted_atom_indices) = _assignNamesFromForceFieldTemplates(topology, system, openmm_forcefields_to_use) # Re-sort atoms. resort_atoms = True if resort_atoms: import copy atoms2 = copy.deepcopy(atoms) positions2 = copy.deepcopy(positions) for (new_index, old_index) in enumerate(sorted_atom_indices): atoms2[new_index] = atoms[old_index] atoms2[new_index].index = new_index+1 positions2[new_index,:] = positions[old_index,:] atoms = atoms2 positions = positions2 # Write PDB file with AMBER names for atoms and residues. amberpdb_filename = 'ambernames.pdb' outfile = open(amberpdb_filename, 'w') box_vectors = state.getPeriodicBoxVectors(asNumpy=True) index = 0 wrap_coordinates = False for atom in atoms: residue = atom.residue atomIndex = atom.index atomName = atom.name resName = atom.resname chainName = chr(ord('A') + residue.chain.index) resIndex = residue.index if len(resName) > 3: resName = resName[1:] outfile.write("ATOM %5d %-4s %3s %s%4d %8.3f%8.3f%8.3f 1.00 0.00\n" % (atomIndex%100000, atomName, resName, chainName, (resIndex+1)%10000, positions[index,0]/unit.angstrom, positions[index,1]/unit.angstrom, positions[index,2]/unit.angstrom)) index += 1 outfile.close() # Run tleap. import subprocess command = 'setenv AMBERHOME $AMBERHOME_CPU; tleap -f setup.leap.in >& setup.leap.out' try: output = subprocess.check_output(command, shell=True, executable=shell, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print(e, 'with output:\n' + e.output) raise e if verbose: print(output) # Write periodic AMBER .crd file. overwrite_crd = True if overwrite_crd: inpcrd_filename = 'amber.crd' outfile = open(inpcrd_filename, 'w') outfile.write('Automatically converted from OpenMM\n') natoms = system.getNumParticles() outfile.write('%6d\n' % natoms) nwritten = 0 for i in range(natoms): if nwritten == 6: outfile.write('\n') nwritten = 0 for k in range(3): outfile.write('%12.7f' % (positions[i][k] / unit.angstroms)) nwritten += 1 outfile.write('\n') for k in range(3): outfile.write('%12.7f' % (box_vectors[k][k]/unit.angstroms)) for k in range(3): outfile.write('%12.7f' % 90.0) outfile.write('\n') outfile.close() # Generate PDB file. import subprocess command = 'cat amber.crd | ambpdb -p amber.prmtop -aatm > amber.pdb' try: output = subprocess.check_output(command, shell=True, executable=shell, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print(e, 'with output:\n' + e.output) raise e print(output) return
def test_to_from_rdkit_core_props_filled(self): """Test RDKitToolkitWrapper to_rdkit() and from_rdkit() when given populated core property fields""" toolkit_wrapper = RDKitToolkitWrapper() # Replacing with a simple molecule with stereochemistry input_smiles = r'C\C(F)=C(/F)C[C@@](C)(Cl)Br' expected_output_smiles = r'[H][C]([H])([H])/[C]([F])=[C](\[F])[C]([H])([H])[C@@]([Cl])([Br])[C]([H])([H])[H]' molecule = Molecule.from_smiles(input_smiles, toolkit_registry=toolkit_wrapper) assert molecule.to_smiles( toolkit_registry=toolkit_wrapper) == expected_output_smiles # Populate core molecule property fields molecule.name = 'Alice' partial_charges = unit.Quantity( np.array([ -.9, -.8, -.7, -.6, -.5, -.4, -.3, -.2, -.1, 0., .1, .2, .3, .4, .5, .6, .7, .8 ]), unit.elementary_charge) molecule.partial_charges = partial_charges coords = unit.Quantity( np.array([['0.0', '1.0', '2.0'], ['3.0', '4.0', '5.0'], ['6.0', '7.0', '8.0'], ['9.0', '10.0', '11.0'], ['12.0', '13.0', '14.0'], ['15.0', '16.0', '17.0'], ['18.0', '19.0', '20.0'], ['21.0', '22.0', '23.0'], ['24.0', '25.0', '26.0'], ['27.0', '28.0', '29.0'], ['30.0', '31.0', '32.0'], ['33.0', '34.0', '35.0'], ['36.0', '37.0', '38.0'], ['39.0', '40.0', '41.0'], ['42.0', '43.0', '44.0'], ['45.0', '46.0', '47.0'], ['48.0', '49.0', '50.0'], ['51.0', '52.0', '53.0']]), unit.angstrom) molecule.add_conformer(coords) # Populate core atom property fields molecule.atoms[2].name = 'Bob' # Ensure one atom has its stereochemistry specified central_carbon_stereo_specified = False for atom in molecule.atoms: if (atom.atomic_number == 6) and atom.stereochemistry == "S": central_carbon_stereo_specified = True assert central_carbon_stereo_specified # Populate bond core property fields fractional_bond_orders = [float(val) for val in range(18)] for fbo, bond in zip(fractional_bond_orders, molecule.bonds): bond.fractional_bond_order = fbo # Do a first conversion to/from oemol rdmol = molecule.to_rdkit() molecule2 = Molecule.from_rdkit(rdmol) # Test that properties survived first conversion #assert molecule.to_dict() == molecule2.to_dict() assert molecule.name == molecule2.name # NOTE: This expects the same indexing scheme in the original and new molecule central_carbon_stereo_specified = False for atom in molecule2.atoms: if (atom.atomic_number == 6) and atom.stereochemistry == "S": central_carbon_stereo_specified = True assert central_carbon_stereo_specified for atom1, atom2 in zip(molecule.atoms, molecule2.atoms): assert atom1.to_dict() == atom2.to_dict() for bond1, bond2 in zip(molecule.bonds, molecule2.bonds): assert bond1.to_dict() == bond2.to_dict() assert (molecule._conformers[0] == molecule2._conformers[0]).all() for pc1, pc2 in zip(molecule._partial_charges, molecule2._partial_charges): pc1_ul = pc1 / unit.elementary_charge pc2_ul = pc2 / unit.elementary_charge assert_almost_equal(pc1_ul, pc2_ul, decimal=6) assert molecule2.to_smiles( toolkit_registry=toolkit_wrapper) == expected_output_smiles
def test_overlap(): """ BUGS TO REPORT: * Even if epsilon = 0, energy of two overlapping atoms is 'nan'. * Periodicity in 'nan' if dr = 0.1 even in nonperiodic system """ # Create a reference system. import testsystems print "Creating Lennard-Jones cluster system..." #[reference_system, coordinates] = testsystems.LennardJonesFluid() #receptor_atoms = [0] #alchemical_atoms = [1] [reference_system, coordinates] = testsystems.LysozymeImplicit() receptor_atoms = range(0,2603) # T4 lysozyme L99A alchemical_atoms = range(2603,2621) # p-xylene import simtk.unit as units unit = coordinates.unit coordinates = units.Quantity(numpy.array(coordinates / unit), unit) factory = AbsoluteAlchemicalFactory(reference_system, alchemical_atoms=alchemical_atoms) alchemical_state = AlchemicalState(0.00, 0.00, 0.00, 1.0) # Create the perturbed system. print "Creating alchemically-modified state..." alchemical_system = factory.createPerturbedSystem(alchemical_state) # Compare energies. import simtk.unit as units import simtk.openmm as openmm timestep = 1.0 * units.femtosecond print "Computing reference energies..." integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(reference_system, integrator) context.setPositions(coordinates) state = context.getState(getEnergy=True) reference_potential = state.getPotentialEnergy() del state, context, integrator print reference_potential print "Computing alchemical energies..." integrator = openmm.VerletIntegrator(timestep) context = openmm.Context(alchemical_system, integrator) dr = 0.1 * units.angstroms # TODO: Why does 0.1 cause periodic 'nan's? a = receptor_atoms[-1] b = alchemical_atoms[-1] delta = coordinates[a,:] - coordinates[b,:] for k in range(3): coordinates[alchemical_atoms,k] += delta[k] for i in range(30): r = dr * i coordinates[alchemical_atoms,0] += dr context.setPositions(coordinates) state = context.getState(getEnergy=True) alchemical_potential = state.getPotentialEnergy() print "%8.3f A : %f " % (r / units.angstroms, alchemical_potential / units.kilocalories_per_mole) del state, context, integrator return
def SMD(system, prmtop, platform, platformProperties, temperature, positions, velocities, keyInteraction, spring_k, dist_in, dist_fin, SMD_num, save_step, move_force_step): # See page 456 of http://ambermd.org/doc12/Amber17.pdf pullforce = mm.CustomExternalForce('k_sp*0.5*(dx^2+dy^2+dz^2); \ dx=x-(x0+displace_x); \ dy=y-(y0+displace_y); \ dz=z-(z0+displace_z);') pullforce.addPerParticleParameter('k_sp') pullforce.addPerParticleParameter('x0') pullforce.addPerParticleParameter('y0') pullforce.addPerParticleParameter('z0') pullforce.addGlobalParameter("displace_x", 0.0 * u.nanometer) pullforce.addGlobalParameter("displace_y", 0.0 * u.nanometer) pullforce.addGlobalParameter("displace_z", 0.0 * u.nanometer) keyInteraction_pos = [positions[keyInteraction[0]], positions[keyInteraction[1]]] keyInteraction_dist = np.linalg.norm(keyInteraction_pos[0] - keyInteraction_pos[1]) keyInteraction_vect = (keyInteraction_pos[1] - keyInteraction_pos[0]) / keyInteraction_dist keyInteraction_vect = u.Quantity(value=keyInteraction_vect, unit=u.nanometers) pullto = keyInteraction_pos[0] + 0.25 * keyInteraction_vect pullto_old = pullto pullforce.addParticle(keyInteraction[1], [spring_k, pullto[0], pullto[1], pullto[2] ]) system.addForce(pullforce) integrator = mm.LangevinIntegrator(temperature, 4/u.picosecond, 0.002*u.picosecond) simulation = app.Simulation(prmtop.topology, system, integrator, platform, platformProperties) simulation.context.setPositions(positions) simulation.context.setVelocities(velocities) force_val_old = -spring_k*(keyInteraction_dist - dist_in) energy_val_old = u.Quantity(value=0, unit=u.kilocalorie/u.mole) f=open('duck_'+str(temperature).split()[0].replace('.0','K')+'_'+str(SMD_num)+'.dat','w') steps = int((dist_fin.value_in_unit(u.nanometer) / 0.000001 - dist_in.value_in_unit(u.nanometer) / 0.000001)) / move_force_step pull_distance = 0.000001 * move_force_step #write trajectory top = md.load_prmtop('system_solv.prmtop') atom_subset = top.select('not water') simulation.reporters.append(app.StateDataReporter("smd_"+str(temperature).split()[0].replace('.0','K')+"_"+str(SMD_num)+".csv", move_force_step, step=True, time=True, totalEnergy=True, kineticEnergy=True, potentialEnergy=True, temperature=True, density=True, progress=True, totalSteps=move_force_step*steps, speed=True)) simulation.reporters.append(HDF5Reporter("smd_"+str(temperature).split()[0].replace('.0','K')+"_"+str(SMD_num)+".h5", move_force_step*20, atomSubset=atom_subset)) for i in range(steps): state = simulation.context.getState(getPositions=True) pos_keyInt = state.getPositions() keyInteraction_pos = [pos_keyInt[keyInteraction[0]], pos_keyInt[keyInteraction[1]]] keyInteraction_dist = np.linalg.norm(keyInteraction_pos[0]-keyInteraction_pos[1]) keyInteraction_vect = (keyInteraction_pos[1] - keyInteraction_pos[0]) / keyInteraction_dist keyInteraction_vect = u.Quantity(value=keyInteraction_vect, unit=u.nanometers) pullto = keyInteraction_pos[0] + (0.25 + float(i) * pull_distance) * keyInteraction_vect displace = pullto - pullto_old simulation.context.setParameter('displace_x', displace[0]) simulation.context.setParameter('displace_y', displace[1]) simulation.context.setParameter('displace_z', displace[2]) if i == 0: distance = 0.0 else: distance = pull_distance dist_spring = (0.25 + float(i) * pull_distance) * u.nanometer force_val = -spring_k * (keyInteraction_dist - dist_spring) energy_val = energy_val_old + (distance * u.nanometer) * 0.5 * (force_val+force_val_old) force_val_old = force_val energy_val_old = energy_val if (i%int(save_step/move_force_step)) == 0: f.write(str(i)+' '+str(dist_spring)+' '+str(keyInteraction_dist)+' '+str(force_val)+' '+str(energy_val)+'\n') simulation.step(move_force_step) #f.write(str(i)+' '+str(dist_spring)+' '+str(keyInteraction_dist)+' '+str(force_val)+' '+str(energy_val)+'\n') f.close()
def lambda_trace(reference_system, positions, receptor_atoms, ligand_atoms, platform_name=None, precision=None, annihilate_electrostatics=True, annihilate_sterics=False, nsteps=100): """ Compute potential energy as a function of lambda. """ # Create a factory to produce alchemical intermediates. factory = AbsoluteAlchemicalFactory( reference_system, ligand_atoms=ligand_atoms, annihilate_electrostatics=annihilate_electrostatics, annihilate_sterics=annihilate_sterics) platform = None if platform_name: # Get platform. platform = openmm.Platform.getPlatformByName(platform_name) if precision: if platform_name == 'CUDA': platform.setDefaultPropertyValue('CudaPrecision', precision) elif platform_name == 'OpenCL': platform.setDefaultPropertyValue('OpenCLPrecision', precision) # Take equally-sized steps. delta = 1.0 / nsteps def compute_potential(system, positions, platform=None): timestep = 1.0 * units.femtoseconds integrator = openmm.VerletIntegrator(timestep) if platform: context = openmm.Context(system, integrator, platform) else: context = openmm.Context(system, integrator) context.setPositions(positions) state = context.getState(getEnergy=True) potential = state.getPotentialEnergy() del integrator, context return potential # Compute unmodified energy. u_original = compute_potential(reference_system, positions, platform) # Scan through lambda values. lambda_i = np.zeros([nsteps + 1], np.float64) # lambda values for u_i u_i = units.Quantity(np.zeros([nsteps + 1], np.float64), units.kilocalories_per_mole ) # u_i[i] is the potential energy for lambda_i[i] for i in range(nsteps + 1): lambda_value = 1.0 - i * delta # compute lambda value for this step alchemical_system = factory.createPerturbedSystem( AlchemicalState(0, lambda_value, lambda_value, lambda_value)) lambda_i[i] = lambda_value u_i[i] = compute_potential(alchemical_system, positions, platform) print "%12.9f %24.8f kcal/mol" % (lambda_i[i], u_i[i] / units.kilocalories_per_mole) # Write figure as PDF. import pylab from matplotlib.backends.backend_pdf import PdfPages import matplotlib.pyplot as plt with PdfPages('lambda-trace.pdf') as pdf: fig = plt.figure(figsize=(10, 5)) ax = fig.add_subplot(111) plt.plot(1, u_original / units.kilocalories_per_mole, 'ro', label='unmodified') plt.plot(lambda_i, u_i / units.kilocalories_per_mole, 'k.', label='alchemical') plt.title('T4 lysozyme L99A + p-xylene : AMBER96 + OBC GBSA') plt.ylabel('potential (kcal/mol)') plt.xlabel('lambda') ax.legend() rstyle(ax) pdf.savefig() # saves the current figure into a pdf page plt.close() return
def readAmberCoordinates(filename, read_box=False, read_velocities=False, verbose=False, asNumpy=False): """ Read atomic coordinates (and optionally, box vectors) from Amber formatted coordinate file. ARGUMENTS filename (string) - name of Amber coordinates file to be read in system (simtk.openmm.System) - System object for which coordinates are to be read OPTIONAL ARGUMENTS verbose (boolean) - if True, will print out verbose information about the file being read asNumpy (boolean) - if True, results will be returned as Numpy arrays instead of lists of Vec3s EXAMPLES Read coordinates in vacuum. >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-gbsa') >>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd') >>> coordinates = readAmberCoordinates(crd_filename) Read coordinates in solvent. >>> directory = os.path.join(os.getenv('YANK_INSTALL_DIR'), 'test', 'systems', 'alanine-dipeptide-explicit') >>> crd_filename = os.path.join(directory, 'alanine-dipeptide.inpcrd') >>> [coordinates, box_vectors] = readAmberCoordinates(crd_filename, read_box=True) """ # Open coordinate file for reading. infile = open(filename, 'r') # Read title title = infile.readline().strip() if verbose: print "title: '%s'" % title # Read number of atoms natoms = int(infile.readline().split()[0]) if verbose: print "%d atoms" % natoms # Allocate storage for coordinates coordinates = [] # Read coordinates mm = simtk.openmm natoms_read = 0 while (natoms_read < natoms): line = infile.readline() if len(line) == 0: raise ValueError( "Unexpected end of file while reading coordinates") line = line.strip() elements = line.split() while (len(elements) > 0): coordinates.append( mm.Vec3(float(elements.pop(0)), float(elements.pop(0)), float(elements.pop(0)))) natoms_read += 1 if asNumpy: newcoords = numpy.zeros([natoms, 3], numpy.float32) for i in range(len(coordinates)): for j in range(3): newcoords[i, j] = coordinates[i][j] coordinates = newcoords # Assign units. coordinates = units.Quantity(coordinates, units.angstroms) # Read velocities if requested. velocities = None if (read_velocities): # Read velocities velocities = [] natoms_read = 0 while (natoms_read < natoms): line = infile.readline() if len(line) == 0: raise ValueError( "Unexpected end of file while reading velocities") line = line.strip() elements = line.split() while (len(elements) > 0): velocities.append( 20.455 * mm.Vec3(float(elements.pop(0)), float(elements.pop(0)), float(elements.pop(0)))) natoms_read += 1 if asNumpy: newvel = numpy.zeros([natoms, 3], numpy.float32) for i in range(len(velocities)): for j in range(3): newvel[i, j] = velocities[i][j] velocities = newvel # Assign units. velocities = units.Quantity(velocities, units.angstroms / units.picoseconds) # Read box size if present box_vectors = None if (read_box): line = infile.readline() if len(line) == 0: raise ValueError( "Unexpected end of file while reading box vectors") line = line.strip() elements = line.split() nelements = len(elements) box_dimensions = [0.0] * nelements for i in range(nelements): box_dimensions[i] = float(elements[i]) # TODO: Deal with non-standard box sizes. if nelements == 6: if asNumpy: a = units.Quantity(numpy.array([box_dimensions[0], 0.0, 0.0]), units.angstroms) b = units.Quantity(numpy.array([0.0, box_dimensions[1], 0.0]), units.angstroms) c = units.Quantity(numpy.array([0.0, 0.0, box_dimensions[2]]), units.angstroms) else: a = units.Quantity(mm.Vec3(box_dimensions[0], 0.0, 0.0), units.angstroms) b = units.Quantity(mm.Vec3(0.0, box_dimensions[1], 0.0), units.angstroms) c = units.Quantity(mm.Vec3(0.0, 0.0, box_dimensions[2]), units.angstroms) box_vectors = [a, b, c] else: raise Exception("Don't know what to do with box vectors: %s" % line) # Close file infile.close() if box_vectors and velocities: return (coordinates, box_vectors, velocities) if box_vectors: return (coordinates, box_vectors) if velocities: return (coordinates, velocities) return coordinates
def execute(self, directory, available_resources): import mdtraj logging.info('Extracting dielectrics: ' + self.id) base_exception = super(ExtractAverageDielectric, self).execute(directory, available_resources) if isinstance(base_exception, ExtractAverageDielectric): return base_exception charge_list = [] from simtk.openmm import XmlSerializer with open(self._system_path, 'rb') as file: self._system = XmlSerializer.deserialize(file.read().decode()) for force_index in range(self._system.getNumForces()): force = self._system.getForce(force_index) if not isinstance(force, openmm.NonbondedForce): continue for atom_index in range(force.getNumParticles()): charge = force.getParticleParameters(atom_index)[0] charge /= unit.elementary_charge charge_list.append(charge) dipole_moments = mdtraj.geometry.dipole_moments( self.trajectory, charge_list) dipole_moments, self._equilibration_index, self._statistical_inefficiency = \ timeseries.decorrelate_time_series(dipole_moments) sample_indices = timeseries.get_uncorrelated_indices( len(self.trajectory[self._equilibration_index:]), self._statistical_inefficiency) sample_indices = [ index + self._equilibration_index for index in sample_indices ] volumes = self.trajectory[sample_indices].unitcell_volumes self._uncorrelated_values = unit.Quantity(dipole_moments, None) self._uncorrelated_volumes = volumes * unit.nanometer**3 value, uncertainty = bootstrap(self._bootstrap_function, self._bootstrap_iterations, self._bootstrap_sample_size, dipoles=dipole_moments, volumes=volumes) self._value = EstimatedQuantity(unit.Quantity(value, None), unit.Quantity(uncertainty, None), self.id) logging.info('Extracted dielectrics: ' + self.id) return self._get_output_dictionary()
import netCDF4 as netcdf from simtk import unit import glob #lentraj = 10000 #perckeep = 0.8 ncfiles = glob.glob('AlkEthOH_*.nc') for i in ncfiles: #indkeep = int(lentraj*perckeep) data = netcdf.Dataset(i) xyz = data.variables['coordinates'] #xyzn = unit.Quantity(xyz[-indkeep:], unit.angstroms) xyzn = unit.Quantity(xyz[:], unit.angstroms) if len(xyzn) < 10000: print i, len(xyzn)
def execute(self, directory, available_resources): logging.info('Reweighting dielectric: {}'.format(self.id)) if len(self._reference_observables) == 0: return PropertyEstimatorException( directory=directory, message='There were no dipole moments to reweight.') if len(self._reference_volumes) == 0: return PropertyEstimatorException( directory=directory, message='There were no volumes to reweight.') if (not isinstance(self._reference_observables[0], unit.Quantity) or not isinstance(self._reference_volumes[0], unit.Quantity)): return PropertyEstimatorException( directory=directory, message='The reference observables should be ' 'a list of unit.Quantity wrapped ndarray\'s.') if len(self._reference_observables) != len(self._reference_volumes): return PropertyEstimatorException( directory=directory, message='The number of reference dipoles does ' 'not match the number of reference volumes.') for reference_dipoles, reference_volumes in zip( self._reference_observables, self._reference_volumes): if len(reference_dipoles) == len(reference_volumes): continue return PropertyEstimatorException( directory=directory, message='The number of reference dipoles does ' 'not match the number of reference volumes.') dipole_moments = self._prepare_observables_array( self._reference_observables) dipole_moments_sqr = np.array([[ np.dot(dipole, dipole) for dipole in np.transpose(dipole_moments) ]]) volumes = self._prepare_observables_array(self._reference_volumes) if self._bootstrap_uncertainties: reference_potentials = np.transpose( np.array(self._reference_reduced_potentials)) target_potentials = np.transpose( np.array(self._target_reduced_potentials)) frame_counts = np.array([ len(observable) for observable in self._reference_observables ]) # Construct an mbar object to get out the number of effective samples. import pymbar mbar = pymbar.MBAR(self._reference_reduced_potentials, frame_counts, verbose=False, relative_tolerance=1e-12) effective_samples = mbar.computeEffectiveSampleNumber().max() value, uncertainty = bootstrap( self._bootstrap_function, self._bootstrap_iterations, self._bootstrap_sample_size, frame_counts, reference_reduced_potentials=reference_potentials, target_reduced_potentials=target_potentials, dipoles=np.transpose(dipole_moments), dipoles_sqr=np.transpose(dipole_moments_sqr), volumes=np.transpose(volumes)) if effective_samples < self._required_effective_samples: uncertainty = sys.float_info.max self._value = EstimatedQuantity(unit.Quantity(value, None), unit.Quantity(uncertainty, None), self.id) else: return PropertyEstimatorException( directory=directory, message='Dielectric uncertainties may only' 'be bootstrapped.') logging.info('Dielectric reweighted: {}'.format(self.id)) return self._get_output_dictionary()
def run_task(self, settings, systems, n_sweeps, n_steps_per_sweep=100, verbose_freq=10, temperature_pot_qm=unit.Quantity(300, unit.kelvin), temperature_pot_mm=unit.Quantity(300, unit.kelvin), temperature_kin_mm=unit.Quantity(300, unit.kelvin), label="0", optimize_mm=False, ase_calculator_low_level=None, calculate_forces=False, checkpoint_freq=100, parametrization=False, sampling_mm=True, sampling_freq_mm=10, sampling_freq_qm=50, parametrization_freq=100, restart=False, seed=np.random.randint(2**32-1), system_mm_solute=None): """ Method that runs a HMC sampler. Parameters ---------- settings : dict Dictionary containing global ParaMol settings. systems : list of :obj:`ParaMol.System.system.ParaMolSystem` List containing instances of ParaMol systems. n_sweeps : int Number of MC sweeps to perform. n_steps_per_sweep : int Number of MD steps per MC sweep. verbose_freq : int Verbose frequency. temperature_pot_qm : unit.Quantity Temperature used for the QM potential part. temperature_pot_mm : unit.Quantity Temperature used for the MM potential part. temperature_kin_mm : unit.Quantity Temperature used for the MM kinetic part. label : int HMC sampler label. It has to be an integer number. optimize_mm : bool Flag that controls whether an MM optimization is performed before the HMC run. Only used if ase_calculator_low_level is None. ase_calculator_low_level : ase.calculator.*.* ASE calculator used for the low level chain. calculate_forces : bool Flag that signals whether or not to calculate forces when a structure is accepted. Only relevant for parametrization purposes (does not affect the HMC in itself). checkpoint_freq : int Frequency at which checkpoint restart files are written. parametrization : bool Flag that controls whether parametrization is to be performed. sampling_mm : bool Flag that controls whether or not to sample rejected MM structures. sampling_freq_mm : int Frequency at which MM structures rejected in the QM ensemble are sampled. sampling_freq_qm : int Frequency at which structures accepted in sample parametrization_freq : int Number of structures that has to be collected before performing parametrization. restart : bool Flag that controls whether or not to perform a restart. seed : int Numpy random seed. Returns ------- systems : listg List with the updated instances of ParaMol System. """ assert len(systems) == 1, "HMC task currently only supports one system at once." # Create QM Engines and initiate OpenMM for system in systems: system.convert_system_ref_arrays_to_list() # Reset any previously created OpenMM Context\ system.engine.context = None # HMC has to use a sympletic integrator such VelocityVerletIntegrator system.engine.integrator = VelocityVerletIntegratorAdapted(system.engine._integrator_params["stepSize"]) system.engine.init_openmm(create_system_params=system.engine._create_system_params) system.engine.get_masses() if system_mm_solute.interface is None: system_mm_solute.interface = ParaMolInterface() if system.interface is None: system.interface = ParaMolInterface() system_mm_solute.create_qm_engines(settings.qm_engine["qm_engine"], settings.qm_engine[settings.qm_engine["qm_engine"].lower()]) if ase_calculator_low_level is not None: from ase import units as ase_units mm_ase_engine = ASEWrapper(system_name=system.name, interface=system.interface, calculator=ase_calculator_low_level, n_atoms=system.n_atoms, atom_list=system.engine.atom_list, n_calculations=1, cell=None, work_dir_prefix="HMC_LL_ASEWorkDir_", opt_log_file="ase_md.log") temperature_kin_mm_units = unit.Quantity(temperature_kin_mm / ase_units.kB, unit.kelvin) else: mm_ase_engine = None temperature_kin_mm_units = temperature_kin_mm system = systems[0] self._label = label parameter_space, objective_function, optimizer = (None, None, None) # TODO: Once this is included in the main ParaMol version, add this line to the default dictionary settings.restart["restart_hmc_file_{}".format(self._label)] = "restart_hmc_{}.pickle".format(self._label) if restart: logging.info("Starting HMC sampler parametrization from a previous restart.") # Read HMCSampler pickle self.__dict__ = self.read_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label)) # Read data into system system.read_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label))) # Convert np.arrays to list system.convert_system_ref_arrays_to_list() # Calculate last accepted mm energy if not available self._last_accepted_mm_energy = None if self._last_accepted_mm_energy is None: if mm_ase_engine is None: potential_final_mm = unit.Quantity(system.engine.get_potential_energy(system.ref_coordinates[-1]), unit.kilojoules_per_mole) else: coord_to_run = np.asarray(system.ref_coordinates[-1]) * 10 potential_final_mm = mm_ase_engine.run_calculation(coords=coord_to_run, label=int(self._label)) self._last_accepted_mm_energy = potential_final_mm else: self._n = 1 self._n_total_qm = 0 self._n_accepted_qm = 0 # MM chain self._n_total_mm = 0 self._n_accepted_mm = 0 if parametrization: # Data used for the parametrization self._param_coordinates = [] self._param_energies = [] self._param_forces = [] self._param_n_structures = 0 self._param_sampling_freq_qm_id = 0 self._param_sampling_freq_mm_id = 0 self._parametrize = True else: self._parametrize = False if optimize_mm: logging.info("Performing MM optimization...") system.engine.minimize_system(tolerance=0.1, max_iter=0) # Set numpy seed np.random.seed(seed) mask_atoms = 14 while self._n <= n_sweeps: if len(system.ref_coordinates) > 0 and self._n != 1: # Do not need to compute the QM energy if there are structures in the top ensemble or if we are not in the first ever iteration. system.engine.set_positions(system.ref_coordinates[-1]) potential_initial_qm = system.ref_energies[-1] potential_initial_qm = unit.Quantity(potential_initial_qm, unit.kilojoules_per_mole) potential_initial_mm = self._last_accepted_mm_energy mm_solute_initial = unit.Quantity(system_mm_solute.engine.get_potential_energy(system.ref_coordinates[-1][:mask_atoms]), unit.kilojoules_per_mole) if mm_ase_engine is not None: coord_to_run = np.asarray(system.ref_coordinates[-1]) * 10 else: # Compute MM initial kinetic and potential energy coord_to_run = system.engine.get_positions().in_units_of(unit.angstrom)._value if mm_ase_engine is None: potential_initial_mm = unit.Quantity(system.engine.get_potential_energy(system.engine.get_positions()), unit.kilojoules_per_mole) else: potential_initial_mm = mm_ase_engine.run_calculation(coords=system.engine.get_positions().in_units_of(unit.angstrom)._value, label=int(self._label)) potential_initial_qm, _ = system_mm_solute.qm_engine.qm_engine.run_calculation(coords=coord_to_run[:mask_atoms], label=int(self._label)) potential_initial_qm = unit.Quantity(potential_initial_qm, unit.kilojoules_per_mole) mm_solute_initial = unit.Quantity(system_mm_solute.engine.get_potential_energy(coord_to_run[:mask_atoms]*0.1), unit.kilojoules_per_mole) if mm_ase_engine is None: # ---------------------------------------------------------------- # # OpenMM HMC # # ---------------------------------------------------------------- # # Use OpenMM as the low level # Sample new velocities and calculate kinetic energy new_velocities = system.engine.generate_maxwell_boltzmann_velocities(temperature_kin_mm) system.engine.set_velocities(new_velocities) system.engine.context.applyVelocityConstraints(1e-8) # Calculate kinetic energy kinetic_initial = unit.Quantity(system.engine.get_kinetic_energy(system.engine.get_velocities()), unit.kilojoules_per_mole) # Run classical MD simulation system.engine.integrator.step(n_steps_per_sweep) # Compute MM final kinetic and potential energy kinetic_final = unit.Quantity(system.engine.get_kinetic_energy(system.engine.get_velocities()), unit.kilojoules_per_mole) potential_final_mm = unit.Quantity(system.engine.get_potential_energy(system.engine.get_positions()), unit.kilojoules_per_mole) coords = system.engine.get_positions() else: # ---------------------------------------------------------------- # # ASE HMC # # ---------------------------------------------------------------- # # Use ASE as the low level # Run short MD using ASE coords, potential_initial_mm, kinetic_initial, forces_initial, potential_final_mm, kinetic_final, forces_final = mm_ase_engine.run_md(coords=coord_to_run, label=int(self._label), steps=n_steps_per_sweep, dt=1.0 * ase_units.fs, initial_temperature=temperature_kin_mm, ) # Compute MM final kinetic and potential energy kinetic_initial = unit.Quantity(kinetic_initial, unit.kilojoules_per_mole) potential_initial_mm = unit.Quantity(potential_initial_mm, unit.kilojoules_per_mole) kinetic_final = unit.Quantity(kinetic_final, unit.kilojoules_per_mole) potential_final_mm = unit.Quantity(potential_final_mm, unit.kilojoules_per_mole) coords = unit.Quantity(coords, unit.nanometers) # ---------------------------------------------------------------- # # Low Level->High Level # # ---------------------------------------------------------------- # if self._hmc_acceptance_criterion_mm(potential_final_mm, potential_initial_mm, kinetic_final, kinetic_initial, temperature_pot_mm, temperature_kin_mm_units): potential_final_qm, forces_final_qm = system_mm_solute.qm_engine.qm_engine.run_calculation(coords=coords[:mask_atoms].in_units_of(unit.angstrom)._value, label=int(self._label)) potential_final_qm = unit.Quantity(potential_final_qm, unit.kilojoules_per_mole) mm_solute_final = unit.Quantity(system_mm_solute.engine.get_potential_energy(coords[:mask_atoms]), unit.kilojoules_per_mole) potential_final_qm_mm = potential_final_mm - mm_solute_final + potential_final_qm potential_initial_qm_mm = potential_initial_mm - mm_solute_initial + potential_initial_qm #potential_initial_mm = mm_solute_initial #potential_final_mm = mm_solute_final # Nested Markov chain acceptance criterion qm_accepted = self._hmc_acceptance_criterion_qm(potential_final_qm_mm, potential_initial_qm_mm, potential_final_mm, potential_initial_mm, temperature_pot_qm, temperature_pot_mm) if qm_accepted: self._last_accepted_mm_energy = potential_final_mm # Update position of context if using ASE engine if mm_ase_engine is not None: system.engine.set_positions(coords) # Append energies, forces and conformations system.ref_energies.append(potential_final_qm._value) system.ref_coordinates.append(system.engine.get_positions()._value) if calculate_forces: system.ref_forces.append(forces_final_qm) system.n_structures += 1 elif len(system.ref_coordinates) > 0: # Append last accepted structure system.ref_energies.append(system.ref_energies[-1]) system.ref_coordinates.append(system.ref_coordinates[-1]) if calculate_forces: system.ref_forces.append(system.ref_forces[-1]) system.n_structures += 1 else: # No structures have been accepted yet. pass if self._parametrize: # TODO: include code related to partial momentum refreshment if sampling_freq_qm == self._param_sampling_freq_qm_id and len(system.ref_coordinates) > 0: self._param_coordinates.append(system.ref_coordinates[-1]) self._param_energies.append(system.ref_energies[-1]) if calculate_forces: self._param_forces.append(system.ref_forces[-1]) self._param_n_structures += 1 # Reset sampling counter self._param_sampling_freq_qm_id = 1 else: self._param_sampling_freq_qm_id += 1 # Write restart files if self._n % checkpoint_freq == 0: self.write_restart_pickle(settings.restart, system.interface, "restart_hmc_file_{}".format(self._label), self.__dict__) system.write_data(os.path.join(settings.restart["restart_dir"], "{}_hmc_{}.nc".format(system.name, self._label))) system_mm_solute.ref_coordinates = np.asarray(system.ref_coordinates)[:, :mask_atoms, :] system_mm_solute.write_coordinates_xyz("{}_hmc_{}.xyz".format(system_mm_solute.name, self._label)) if self._parametrize and (self._param_n_structures % parametrization_freq == 0 and self._param_n_structures > 0): system, parameter_space, objective_function, optimizer = self._run_parametrization(settings, system, parameter_space, objective_function, optimizer, calculate_forces) if self._n % verbose_freq == 0: self._print_output(system.name) self._n += 1 return systems
def _parse_parameters(self, path_to_parameters): """ It parses the parameters from ffld_server's output file. Parameters ---------- path_to_parameters : str The path to the ffld_server's output file Returns ------- params : an OPLSParameters object The set of lists of parameters grouped by parameter type. Thus, the dictionary has the following keys: atom_names, atom_types, charges, sigmas, and epsilons. """ params = defaultdict(list) with open(path_to_parameters) as f: section = 'out' name_to_index = dict() # To pair atom names and indexes for line in f: if line.startswith('OPLSAA FORCE FIELD TYPE ASSIGNED'): section = 'atoms' # Skip the next 3 lines f.readline() f.readline() f.readline() elif line.startswith(' Stretch'): section = 'bonds' elif line.startswith(' Bending'): section = 'angles' elif line.startswith(' proper Torsion'): section = 'propers' elif line.startswith(' improper Torsion'): section = 'impropers' elif line == '\n': continue elif section == 'atoms': if line.startswith('-'): continue fields = line.split() assert len(fields) > 7, 'Unexpected number of fields ' \ + 'found at line {}'.format(line) name_to_index[line[0:4]] = len(params['atom_names']) params['atom_names'].append(line[0:4]) params['atom_types'].append(fields[3]) params['charges'].append( unit.Quantity(float(fields[4]), unit.elementary_charge)) params['sigmas'].append( unit.Quantity(float(fields[5]), unit.angstrom)) params['epsilons'].append( unit.Quantity(float(fields[6]), unit.kilocalorie / unit.mole)) elif section == 'bonds': fields = line.split() assert len(fields) > 4, 'Unexpected number of fields ' \ + 'found at line {}'.format(line) params['bonds'].append({ 'atom1_idx': name_to_index[line[0:4]], 'atom2_idx': name_to_index[line[8:12]], 'spring_constant': unit.Quantity( float(fields[2]), unit.kilocalorie / (unit.angstrom**2 * unit.mole)), 'eq_dist': unit.Quantity(float(fields[3]), unit.angstrom) }) elif section == 'angles': fields = line.split() assert len(fields) > 5, 'Unexpected number of fields ' \ + 'found at line {}'.format(line) params['angles'].append({ 'atom1_idx': name_to_index[line[0:4]], 'atom2_idx': name_to_index[line[8:12]], 'atom3_idx': name_to_index[line[16:20]], 'spring_constant': unit.Quantity( float(fields[3]), unit.kilocalorie / (unit.radian**2 * unit.mole)), 'eq_angle': unit.Quantity(float(fields[4]), unit.degrees) }) return self.OPLSParameters(params)