def get_integrator(random_seed: int, args: ListOfArgs) -> mm.Integrator: """Helper function that returns requested integrator.""" print(" Integrator initialization...") integrator = mm.VerletIntegrator(10 * simtk.unit.femtosecond) # default integrator if args.SIM_RUN_SIMULATION: if args.SIM_INTEGRATOR_TYPE == "langevin": integrator = mm.LangevinIntegrator(args.SIM_TEMP, args.SIM_FRICTION_COEFF, args.SIM_TIME_STEP) integrator.setRandomNumberSeed(random_seed) elif args.SIM_INTEGRATOR_TYPE == "brownian": print(args.SIM_TEMP, args.SIM_FRICTION_COEFF, args.SIM_TIME_STEP) integrator = mm.BrownianIntegrator(args.SIM_TEMP, args.SIM_FRICTION_COEFF, args.SIM_TIME_STEP) integrator.setRandomNumberSeed(random_seed) elif args.SIM_INTEGRATOR_TYPE == "verlet": integrator = mm.VerletIntegrator(args.SIM_TIME_STEP) return integrator
def production(system, topology, ensemble, temperature, timestep, collision_rate, n_steps, nsteps_out, firstframe_name, log_name, traj_name, final_state_name, n_equil_steps=1000, ini_positions=None, ini_state_name=None, use_switch=False, r_switch=None, pressure=None, minimize=False, cuda=False, gpu_idxs=None, more_reporters=[], dynamics="Langevin", use_platform=None): if use_switch: # set switching function on nonbonded forces for i in range(system.getNumForces()): force = system.getForce(i) if force.__repr__().find("NonbondedForce") > -1: force.setUseSwitchingFunction(True) if r_switch is None: raise IOError("Need to input r_switch if use_switch = True") else: force.setSwitchingDistance(r_switch/unit.nanometer) if ensemble == "NVE": integrator = omm.VerletIntegrator(timestep) else: if dynamics == "Langevin": integrator = omm.LangevinIntegrator(temperature, collision_rate, timestep) elif dynamics == "Brownian": integrator = omm.BrownianIntegrator(temperature, collision_rate, timestep) else: raise IOError("dynamics must be Langevin or Brownian") if ensemble == "NPT": if pressure is None: raise ValueError("If ensemble is NPT need to specficy pressure") system.addForce(omm.MonteCarloBarostat(pressure, temperature)) if not use_platform is None: if use_platform == "CUDA": platform = omm.Platform.getPlatformByName('CUDA') if gpu_idxs is None: properties = {'DeviceIndex': '0'} else: properties = {'DeviceIndex': gpu_idxs} simulation = app.Simulation(topology, system, integrator, platform, properties) elif use_platform == "CPU": platform = omm.Platform.getPlatformByName("CPU") simulation = app.Simulation(topology, system, integrator, platform) else: raise ValueError("use_platform needs to be CUDA or CPU or not specfied") else: simulation = app.Simulation(topology, system, integrator) if not ini_positions is None: # set initial positions and box dimensions simulation.context.setPositions(ini_positions) #simulation.context.setPeriodicBoxVectors() elif not ini_position_file is None: simulation.loadState(ini_state_name) else: raise ValueError("Need to specify initial positions somehow!") if minimize: simulation.minimizeEnergy(tolerance=energy_minimization_tol) # initial equilibration simulation.step(n_equil_steps) # save the first frame minimized simulation.reporters.append(app.PDBReporter(firstframe_name, 1)) simulation.step(1) simulation.reporters.pop(0) # record coordinates simulation.reporters.append(app.DCDReporter(traj_name, nsteps_out)) simulation.reporters.append(app.StateDataReporter(log_name, nsteps_out, step=True, potentialEnergy=True, kineticEnergy=True, temperature=True, density=True, volume=True)) # add user-defined reporters for e.g. forces or velocities if len(more_reporters) > 0: for i in range(len(more_reporters)): simulation.reporters.append(more_reporters[i]) # run simulation! simulation.step(n_steps) # save final state. positions, box vectors. simulation.saveState(final_state_name)
def __init__(self, **kwargs): """ All numbers here are floats. Units specified in a parameter. Parameters ---------- N : int number of particles error_tol : float, optional Error tolerance parameter for variableLangevin integrator Values of around 0.01 are reasonable for a "nice" simulation (i.e. simulation with soft forces etc). Simulations with strong forces may need 0.001 or less OpenMM manual recommends 0.001, but our forces tend to be "softer" than theirs timestep : number timestep in femtoseconds. Mandatory for non-variable integrators. Ignored for variableLangevin integrator. Value of 70-80 are appropriate collision_rate : number collision rate in inverse picoseconds. values of 0.01 or 0.05 are often used. Consult with lab members on values. In brief, equilibrium simulations likely do not care about the exact dynamics you're using, and therefore can be simulated in a "ballistic" dynamics with col_rate of around 0.001-0.01. Dynamical simulations and active simulations may be more sensitive to col_rate, though this is still under discussion/investigation. Johannes converged on using 0.1 for loop extrusion simulations, just to be safe. PBCbox : (float,float,float) or False; default:False Controls periodic boundary conditions If PBCbox is False, do not use periodic boundary conditions If intending to use PBC, then set PBCbox to (x,y,z) where x,y,z are dimensions of the bounding box for PBC GPU : GPU index as a string ("0" for first, "1" for second etc.) Machines with 1 GPU automatically select their GPU. integrator : "langevin", "variableLangevin", "verlet", "variableVerlet", "brownian", optional Integrator to use (see Openmm class reference) mass : number or np.array Particle mass (default 100 amu) temperature : simtk.units.quantity(units.kelvin), optional Temperature of the simulation. Devault value is 300 K. verbose : bool, optional If True, prints a lot of stuff in the command line. length_scale : float, optional The geometric scaling factor of the system. By default, length_scale=1.0 and harmonic bonds and repulsive forces have the scale of 1 nm. max_Ek: float, optional raise error if kinetic energy in (kT/particle) exceeds this value platform : string, optional Platform to use: CUDA (preferred fast GPU platform) OpenCL (maybe slower GPU platofrm, does not need CUDA installed) CPU (medium speed parallelized CPU platform) reference (slow CPU platform for debug) verbose : bool, optional Shout out loud about every change. precision: str, optional (not recommended to change) mixed is optimal for most situations. If you are using double precision, it will be slower by a factor of 10 or so. save_decimals: int or False, optional Round to this number of decimals before saving. ``False`` is no rounding. Default is 2. It gives maximum error of 0.005, which is nearly always harmless but saves up to 40% of storage space (0.6 of the original) Using one decimal is safe most of the time, and reduces storage to 40% of int32. NOTE that using periodic boundary conditions will make storage advantage less. """ default_args = { "platform": "CUDA", "GPU": "0", "integrator": "variablelangevin", "temperature": 300, "PBCbox": False, "length_scale": 1.0, "mass": 100, "reporters": [], "max_Ek": 10, "precision": "mixed", "save_decimals": 2, "verbose": False, } valid_names = list(default_args.keys()) + [ "N", "error_tol", "collision_rate", "timestep", ] for i in kwargs.keys(): if i not in valid_names: raise ValueError( "incorrect argument provided: {0}. Allowed are {1}".format( i, valid_names)) if None in kwargs.values(): raise ValueError( "None is not allowed in arguments due to HDF5 incompatiliblity. Use False instead." ) default_args.update(kwargs) kwargs = default_args self.kwargs = kwargs platform = kwargs["platform"] self.GPU = kwargs["GPU"] # setting default GPU properties = {} if self.GPU.lower() != "default": if platform.lower() in ["cuda", "opencl"]: properties["DeviceIndex"] = str(self.GPU) properties["Precision"] = kwargs["precision"] self.properties = properties if platform.lower() == "opencl": platform_object = openmm.Platform.getPlatformByName("OpenCL") elif platform.lower() == "reference": platform_object = openmm.Platform.getPlatformByName("Reference") elif platform.lower() == "cuda": platform_object = openmm.Platform.getPlatformByName("CUDA") elif platform.lower() == "cpu": platform_object = openmm.Platform.getPlatformByName("CPU") else: raise RuntimeError("Undefined platform: {0}".format(platform)) self.platform = platform_object self.temperature = kwargs["temperature"] self.collisionRate = kwargs["collision_rate"] * (1 / simtk.unit.picosecond) self.integrator_type = kwargs["integrator"] if isinstance(self.integrator_type, string_types): self.integrator_type = str(self.integrator_type) if self.integrator_type.lower() == "langevin": self.integrator = openmm.LangevinIntegrator( self.temperature, kwargs["collision_rate"] * (1 / simtk.unit.picosecond), kwargs["timestep"] * simtk.unit.femtosecond, ) elif self.integrator_type.lower() == "variablelangevin": self.integrator = openmm.VariableLangevinIntegrator( self.temperature, kwargs["collision_rate"] * (1 / simtk.unit.picosecond), kwargs["error_tol"], ) elif self.integrator_type.lower() == "verlet": self.integrator = openmm.VariableVerletIntegrator( kwargs["timestep"] * simtk.unit.femtosecond) elif self.integrator_type.lower() == "variableverlet": self.integrator = openmm.VariableVerletIntegrator( kwargs["error_tol"]) elif self.integrator_type.lower() == "brownian": self.integrator = openmm.BrownianIntegrator( self.temperature, kwargs["collision_rate"] * (1 / simtk.unit.picosecond), kwargs["timestep"] * simtk.unit.femtosecond, ) else: logging.info("Using the provided integrator object") self.integrator = self.integrator_type self.integrator_type = "UserDefined" kwargs["integrator"] = "user_defined" self.N = kwargs["N"] self.verbose = kwargs["verbose"] self.reporters = kwargs["reporters"] self.forces_applied = False self.length_scale = kwargs["length_scale"] self.eK_critical = kwargs["max_Ek"] # Max allowed kinetic energy self.step = 0 self.block = 0 self.time = 0 self.nm = simtk.unit.nanometer self.kB = simtk.unit.BOLTZMANN_CONSTANT_kB * simtk.unit.AVOGADRO_CONSTANT_NA self.kT = self.kB * self.temperature * simtk.unit.kelvin # thermal energy # All masses are the same, # unless individual mass multipliers are specified in self.load() self.conlen = 1.0 * simtk.unit.nanometer * self.length_scale self.kbondScalingFactor = float( (2 * self.kT / self.conlen**2) / (simtk.unit.kilojoule_per_mole / simtk.unit.nanometer**2)) self.system = openmm.System() # adding PBC self.PBC = False if kwargs["PBCbox"] is not False: self.PBC = True PBCbox = np.array(kwargs["PBCbox"]) self.system.setDefaultPeriodicBoxVectors( [float(PBCbox[0]), 0.0, 0.0], [0.0, float(PBCbox[1]), 0.0], [0.0, 0.0, float(PBCbox[2])], ) self.force_dict = {} # Dictionary to store forces # saving arguments - not trying to save reporters because they are not serializable kwCopy = {i: j for i, j in kwargs.items() if i != "reporters"} for reporter in self.reporters: reporter.report("initArgs", kwCopy)