def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @param[in] mvals Mathematical parameter values @param[in] h Finite difference step size @param[in] phase The phase (liquid, gas) to perform the calculation on @param[in] AGrad Switch to turn derivatives on or off; if off, return all zeros @param[in] dipole Switch for dipole derivatives. @return G First derivative of the energies in a N_param x N_coord array @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array """ G = np.zeros((FF.np, length)) GDx = np.zeros((FF.np, length)) GDy = np.zeros((FF.np, length)) GDz = np.zeros((FF.np, length)) if not AGrad: return G, GDx, GDy, GDz def energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " " * 30))) EDG, _ = f12d3p(fdwrap(energy_driver, mvals, i), h, f0=ED0) if dipole: G[i, :] = EDG[:, 0] GDx[i, :] = EDG[:, 1] GDy[i, :] = EDG[:, 2] GDz[i, :] = EDG[:, 3] else: G[i, :] = EDG[:] #reset FF parameters FF.make(mvals) return G, GDx, GDy, GDz
def main(): ## Set some basic options. Note that 'forcefield' requires 'ffdir' ## which indicates the relative path of the force field. options, tgt_opts = parse_inputs(argv[1]) MyFF = FF(options) Prec = int(argv[2]) if 'read_mvals' in options: mvals = np.array(options['read_mvals']) else: mvals = np.zeros(len(MyFF.pvals0)) MyFF.make(mvals, False, 'NewFF', precision=Prec)
def main(): ## Set some basic options. Note that 'forcefield' requires 'ffdir' ## which indicates the relative path of the force field. options, tgt_opts = parse_inputs(argv[1]) MyFF = FF(options) Prec=int(argv[2]) if 'read_mvals' in options: mvals = np.array(options['read_mvals']) else: mvals = np.zeros(len(MyFF.pvals0)) MyFF.make(mvals,False,'NewFF',precision=Prec)
def Run_ForceBalance(input_file, debug=False, continue_=False): """ Create instances of ForceBalance components and run the optimizer. The triumvirate, trifecta, or trinity of components are: - The force field - The objective function - The optimizer Cipher: "All I gotta do here is pull this plug... and there you have to watch Apoc die" Apoc: "TRINITY" *chunk* The force field is a class defined in forcefield.py. The objective function is a combination of target classes and a penalty function class. The optimizer is a class defined in this file. """ try: ## The general options and target options that come from parsing the input file options, tgt_opts = parse_inputs(input_file) ## Set the continue_ option. if continue_: options['continue'] = True ## The force field component of the project forcefield = FF(options) ## The objective function objective = Objective(options, tgt_opts, forcefield) ## The optimizer component of the project optimizer = Optimizer(options, objective, forcefield) ## Actually run the optimizer. optimizer.Run() except: import traceback traceback.print_exc() if debug: import pdb pdb.post_mortem()
def get_optimizer(self): """ Return the optimizer object """ ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(self.input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) assert isinstance(options, dict), "Parser gave incorrect type for options" assert isinstance(tgt_opts, list), "Parser gave incorrect type for tgt_opts" for target in tgt_opts: assert isinstance( target, dict), "Parser gave incorrect type for target dict" ## The force field component of the project forcefield = FF(options) assert isinstance(forcefield, FF), "Expected forcebalance forcefield object" ## The objective function objective = Objective(options, tgt_opts, forcefield) assert isinstance(objective, Objective), "Expected forcebalance objective object" ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) assert isinstance(optimizer, Optimizer), "Expected forcebalance optimizer object" self.logger.debug(str(optimizer) + "\n") return optimizer
def test_liquid(self): """Check liquid target with existing simulation data""" # if not sys.version_info <= (2,7): # self.skipTest("Existing pickle file only works with Python 3") print("Setting input file to 'single.in'") input_file = 'single.in' ## The general options and target options that come from parsing the input file print("Parsing inputs...") options, tgt_opts = parse_inputs(input_file) print("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) forcefield = FF(options) objective = Objective(options, tgt_opts, forcefield) ## The optimizer component of the project print("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) assert isinstance(optimizer, Optimizer), "Expected forcebalance optimizer object" print(str(optimizer)) ## Actually run the optimizer. print("Done setting up! Running optimizer...") result = optimizer.Run() print("\nOptimizer finished. Final results:") print(str(result)) liquid_obj_value = optimizer.Objective.ObjDict['Liquid']['x'] assert liquid_obj_value < 20, "Liquid objective function should give < 20 (about 17.23) total value."
def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @param[in] mvals Mathematical parameter values @param[in] h Finite difference step size @param[in] phase The phase (liquid, gas) to perform the calculation on @param[in] AGrad Switch to turn derivatives on or off; if off, return all zeros @param[in] dipole Switch for dipole derivatives. @return G First derivative of the energies in a N_param x N_coord array @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array """ G = np.zeros((FF.np,length)) GDx = np.zeros((FF.np,length)) GDy = np.zeros((FF.np,length)) GDz = np.zeros((FF.np,length)) if not AGrad: return G, GDx, GDy, GDz def energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " "*30))) EDG, _ = f12d3p(fdwrap(energy_driver,mvals,i),h,f0=ED0) if dipole: G[i,:] = EDG[:,0] GDx[i,:] = EDG[:,1] GDy[i,:] = EDG[:,2] GDz[i,:] = EDG[:,3] else: G[i,:] = EDG[:] #reset FF parameters FF.make(mvals) return G, GDx, GDy, GDz
def energy_force_driver_all(self): """ Here we actually compute the interactions and return the energies and forces. I verified this to give the same answer as GROMACS. """ M = [] # Loop through the snapshots ThisFF = FF({'forcefield':['tip3p.xml'], 'ffdir':'', 'priors':{}},verbose=False) r_0 = ThisFF.pvals0[ThisFF.map['HarmonicBondForce.Bond/length/OW.HW']] * 10 k_ij = ThisFF.pvals0[ThisFF.map['HarmonicBondForce.Bond/k/OW.HW']] t_0 = ThisFF.pvals0[ThisFF.map['HarmonicAngleForce.Angle/angle/HW.OW.HW']] * 180 / np.pi k_ijk = ThisFF.pvals0[ThisFF.map['HarmonicAngleForce.Angle/k/HW.OW.HW']] q_o = ThisFF.pvals0[ThisFF.map['NonbondedForce.Atom/charge/tip3p-O']] q_h = ThisFF.pvals0[ThisFF.map['NonbondedForce.Atom/charge/tip3p-H']] sig = ThisFF.pvals0[ThisFF.map['NonbondedForce.Atom/sigma/tip3p-O']] eps = ThisFF.pvals0[ThisFF.map['NonbondedForce.Atom/epsilon/tip3p-O']] facel = 1389.35410 for I in range(self.ns): xyz = self.mol.xyzs[I] Bond_Energy = 0.0 Angle_Energy = 0.0 VdW_Energy = 0.0 Coulomb_Energy = 0.0 for i in range(0,len(xyz),3): o = i h1 = i+1 h2 = i+2 # First O-H bond. r_1 = xyz[h1] - xyz[o] r_1n = np.linalg.norm(r_1) Bond_Energy += 0.5 * k_ij * ((r_1n - r_0) / 10)**2 # Second O-H bond. r_2 = xyz[h2] - xyz[o] r_2n = np.linalg.norm(r_2) Bond_Energy += 0.5 * k_ij * ((r_2n - r_0) / 10)**2 # Angle. theta = np.arccos(np.dot(r_1, r_2) / (r_1n * r_2n)) * 180 / np.pi Angle_Energy += 0.5 * k_ijk * ((theta - t_0) * np.pi / 180)**2 for j in range(0, i, 3): oo = j hh1 = j+1 hh2 = j+2 # Lennard-Jones interaction. r_o_oo = np.linalg.norm(xyz[oo] - xyz[o]) / 10 sroo = sig / r_o_oo VdW_Energy += 4*eps*(sroo**12 - sroo**6) # Coulomb interaction. for k, l in itertools.product(*[[i,i+1,i+2],[j,j+1,j+2]]): q1 = q_o if (k % 3 == 0) else q_h q2 = q_o if (l % 3 == 0) else q_h Coulomb_Energy += q1*q2 / np.linalg.norm(xyz[k]-xyz[l]) Coulomb_Energy *= facel Energy = Bond_Energy + Angle_Energy + VdW_Energy + Coulomb_Energy Force = list(np.zeros(3*len(xyz))) M.append(np.array([Energy] + Force)) return M
def runTest(self): """Check continuation from a previous run""" self.logger.debug("\nSetting input file to 'test_continue.in'\n") input_file = 'test_continue.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) options['continue'] = True self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) self.assertEqual(dict, type(options), msg="\nParser gave incorrect type for options") self.assertEqual(list, type(tgt_opts), msg="\nParser gave incorrect type for tgt_opts") for target in tgt_opts: self.assertEqual( dict, type(target), msg="\nParser gave incorrect type for target dict") ## The force field component of the project forcefield = FF(options) self.assertEqual(FF, type(forcefield), msg="\nExpected forcebalance forcefield object") ## The objective function objective = Objective(options, tgt_opts, forcefield) self.assertEqual(Objective, type(objective), msg="\nExpected forcebalance objective object") ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") self.logger.debug(str(optimizer) + "\n") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') self.assertEqual(optimizer.iterinit, 2, msg="\nInitial iteration counter is incorrect") self.assertEqual(optimizer.iteration, 2, msg="\nFinal iteration counter is incorrect")
def runTest(self): """Check implicit hydration free energy study (Hydration target) converges to expected results""" self.logger.debug("\nSetting input file to 'optimize.in'\n") input_file='optimize.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) self.assertEqual(dict,type(options), msg="\nParser gave incorrect type for options") self.assertEqual(list,type(tgt_opts), msg="\nParser gave incorrect type for tgt_opts") for target in tgt_opts: self.assertEqual(dict, type(target), msg="\nParser gave incorrect type for target dict") ## The force field component of the project self.logger.debug("Creating forcefield using loaded options: ") forcefield = FF(options) self.logger.debug(str(forcefield) + "\n") self.assertEqual(FF, type(forcefield), msg="\nExpected forcebalance forcefield object") ## The objective function self.logger.debug("Creating object using loaded options and forcefield: ") objective = Objective(options, tgt_opts, forcefield) self.logger.debug(str(objective) + "\n") self.assertEqual(Objective, type(objective), msg="\nExpected forcebalance objective object") ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.logger.debug(str(optimizer) + "\n") self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') self.assertNdArrayEqual(EXPECTED_ETHANOL_RESULTS,forcefield.create_pvals(result),delta=0.02, msg="\nCalculation results have changed from previously calculated values.\n" "If this seems reasonable, update EXPECTED_ETHANOL_RESULTS in test_system.py with these values")
def energy_driver(mvals,FF,xyz,tky,verbose=False,dipole=False): """ Compute a set of snapshot energies (and optionally, dipoles) as a function of the force field parameters. ForceBalance creates the force field, TINKER reads it in, and we loop through the snapshots to compute the energies. @param[in] mvals Mathematical parameter values @param[in] FF ForceBalance force field object @return E A numpy array of energies in kilojoules per mole """ # Part of the command line argument to TINKER. basename = xyz[:-4] xin = "%s" % xyz + ("" if tky == None else " -k %s" % tky) xain = "%s.arc" % basename + ("" if tky == None else " -k %s" % tky) # Print the force field file from the ForceBalance object, with modified parameters. FF.make(mvals) # Execute TINKER. cmdstr = "./analyze %s" % xain oanl = _exec(cmdstr,stdin="E",print_command=verbose,print_to_screen=verbose) # Read potential energy from file. E = [] for line in oanl: if 'Total Potential Energy : ' in line: E.append(float(line.split()[4])) E = np.array(E) * 4.184 if dipole: # If desired, read dipole from file. D = [] for line in oanl: if 'Dipole X,Y,Z-Components :' in line: D.append([float(line.split()[i]) for i in range(-3,0)]) D = np.array(D) # Return a Nx4 array with energies in the first column and dipole in columns 2-4. answer = np.hstack((E.reshape(-1,1), D.reshape(-1,3))) return answer else: return E
def runTest(self): """Check voelz study runs without errors""" self.logger.debug("\nSetting input file to 'options.in'\n") input_file = 'options.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) self.assertEqual(dict, type(options), msg="\nParser gave incorrect type for options") self.assertEqual(list, type(tgt_opts), msg="\nParser gave incorrect type for tgt_opts") for target in tgt_opts: self.assertEqual( dict, type(target), msg="\nParser gave incorrect type for target dict") ## The force field component of the project self.logger.debug("Creating forcefield using loaded options: ") forcefield = FF(options) self.logger.debug(str(forcefield) + "\n") self.assertEqual(FF, type(forcefield), msg="\nExpected forcebalance forcefield object") ## The objective function self.logger.debug( "Creating object using loaded options and forcefield: ") objective = Objective(options, tgt_opts, forcefield) self.logger.debug(str(objective) + "\n") self.assertEqual(Objective, type(objective), msg="\nExpected forcebalance objective object") ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.logger.debug(str(optimizer) + "\n") self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n')
def test_continue(self): """Check continuation from a previous run""" if sys.version_info < (3, 0): pytest.skip("Existing pickle file only works with Python 3") self.logger.debug("\nSetting input file to 'test_continue.in'\n") input_file = 'test_continue.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) options['continue'] = True self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) assert isinstance(options, dict), "Parser gave incorrect type for options" assert isinstance(tgt_opts, list), "Parser gave incorrect type for tgt_opts" for target in tgt_opts: assert isinstance( target, dict), "Parser gave incorrect type for target dict" ## The force field component of the project forcefield = FF(options) assert isinstance(forcefield, FF), "Expected forcebalance forcefield object" ## The objective function objective = Objective(options, tgt_opts, forcefield) assert isinstance(objective, Objective), "Expected forcebalance objective object" ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) assert isinstance(optimizer, Optimizer), "Expected forcebalance optimizer object" self.logger.debug(str(optimizer) + '\n') ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') assert optimizer.iterinit == 2, "Initial iteration counter is incorrect" assert optimizer.iteration == 2, "Final iteration counter is incorrect"
def parseFBInput(self): """ This reads through the provided ForceBalance input file using the standard FB parse_inputs. It removes any non-AbInitio targets and removes any AbInitio targets that are previously MMOpt targets. This forms a dictionary (self.unique_res) containing targets belonging to the same residue based off of the target prefix. """ printcool("Reading Grids") #Parse FB input file self.options, self.tgt_opts = parse_inputs(self.fbinput) #Get force field in FB result directory ff_path = os.path.join("result", os.path.splitext(self.options["input_file"])[0]) self.options["ffdir"] = ff_path self.ff = FF(self.options) #Retain AbInitio targets that are not mmopt targets self.tgt_opts = [ l for l in self.tgt_opts if "ABINITIO" in l.get("type") and "mmopt" not in l.get("name") ] self.root = self.options["root"] self.options["input_file"] = "reopt" #Assemble targets from ImplementedTargets dictionary self.targets = [] for opts in self.tgt_opts: Tgt = Implemented_Targets[opts["type"]](self.options, opts, self.ff) self.targets.append(Tgt) #Combine targets that belong to one residue, splits on - or _ in target name (may not be completely sufficient...) self.unique_res = {} for i in range(len(self.tgt_opts)): name = re.split(r"_|-", self.tgt_opts[i]["name"])[0] if name in self.unique_res: self.unique_res[name].append(self.targets[i]) else: self.unique_res[name] = [] self.unique_res[name].append(self.targets[i])
def load_fb_force_field(root_directory: str) -> "FF": """Attempts to load the force field being refit from a force balance optimization directory. Parameters ---------- root_directory The directory containing the force balance input files. Returns ------- The loaded force balance force field object. """ from forcebalance.forcefield import FF from forcebalance.parser import parse_inputs with temporary_cd(root_directory): fb_options, _ = parse_inputs("optimize.in") fb_force_field = FF(fb_options) return fb_force_field
def runTest(self): """Check liquid target with existing simulation data""" if not sys.version_info <= (2, 7): self.skipTest("Existing pickle file only works with Python 3") self.logger.debug("\nSetting input file to 'single.in'\n") input_file = 'single.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) forcefield = FF(options) objective = Objective(options, tgt_opts, forcefield) ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") self.logger.debug(str(optimizer) + "\n") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') liquid_obj_value = optimizer.Objective.ObjDict['Liquid']['x'] self.assertTrue( liquid_obj_value < 20, msg= "\nLiquid objective function should give < 20 (about 17.23) total value." )
def main(): """ Usage: (runcuda.sh) npt.py <openmm|gromacs|tinker> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature> <pressure> This program is meant to be called automatically by ForceBalance on a GPU cluster (specifically, subroutines in openmmio.py). It is not easy to use manually. This is because the force field is read in from a ForceBalance 'FF' class. I wrote this program because automatic fitting of the density (or other equilibrium properties) is computationally intensive, and the calculations need to be distributed to the queue. The main instance of ForceBalance (running on my workstation) queues up a bunch of these jobs (using Work Queue). Then, I submit a bunch of workers to GPU clusters (e.g. Certainty, Keeneland). The worker scripts connect to. the main instance and receives one of these jobs. This script can also be executed locally, if you want to (e.g. for debugging). Just make sure you have the pickled 'forcebalance.p' file. """ printcool("ForceBalance condensed phase simulation using engine: %s" % engname.upper(), color=4, bold=True) #---- # Load the ForceBalance pickle file which contains: #---- # - Force field object # - Optimization parameters # - Options from the Target object that launched this simulation # - Switch for whether to evaluate analytic derivatives. FF,mvals,TgtOptions,AGrad = lp_load(open('forcebalance.p')) FF.ffdir = '.' # Write the force field file. FF.make(mvals) #---- # Load the options that are set in the ForceBalance input file. #---- # Finite difference step size h = TgtOptions['h'] pgrad = TgtOptions['pgrad'] # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps) liquid_timestep = TgtOptions['liquid_timestep'] liquid_nsteps = TgtOptions['liquid_md_steps'] liquid_nequil = TgtOptions['liquid_eq_steps'] liquid_intvl = TgtOptions['liquid_interval'] liquid_fnm = TgtOptions['liquid_coords'] gas_timestep = TgtOptions['gas_timestep'] gas_nsteps = TgtOptions['gas_md_steps'] gas_nequil = TgtOptions['gas_eq_steps'] gas_intvl = TgtOptions['gas_interval'] gas_fnm = TgtOptions['gas_coords'] # Number of threads, multiple timestep integrator, anisotropic box etc. threads = TgtOptions.get('md_threads', 1) mts = TgtOptions.get('mts_integrator', 0) rpmd_beads = TgtOptions.get('rpmd_beads', 0) force_cuda = TgtOptions.get('force_cuda', 0) anisotropic = TgtOptions.get('anisotropic_box', 0) minimize = TgtOptions.get('minimize_energy', 1) # Print all options. printcool_dictionary(TgtOptions, title="Options from ForceBalance") liquid_snapshots = (liquid_nsteps * liquid_timestep / 1000) / liquid_intvl liquid_iframes = 1000 * liquid_intvl / liquid_timestep gas_snapshots = (gas_nsteps * gas_timestep / 1000) / gas_intvl gas_iframes = 1000 * gas_intvl / gas_timestep logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (liquid_snapshots, liquid_iframes, liquid_timestep)) if liquid_snapshots < 2: raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * (liquid_intvl/liquid_timestep))) logger.info("For the gas phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (gas_snapshots, gas_iframes, gas_timestep)) if gas_snapshots < 2: raise Exception('Please set the number of gas time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * (gas_intvl/gas_timestep))) #---- # Loading coordinates #---- ML = Molecule(liquid_fnm) MG = Molecule(gas_fnm) # Determine the number of molecules in the condensed phase coordinate file. NMol = len(ML.molecules) #---- # Setting up MD simulations #---- EngOpts = OrderedDict() EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML), ("pbc", True)]) EngOpts["gas"] = OrderedDict([("coords", gas_fnm), ("mol", MG), ("pbc", False)]) GenOpts = OrderedDict([('FF', FF)]) if engname == "openmm": # OpenMM-specific options EngOpts["liquid"]["platname"] = 'CUDA' EngOpts["gas"]["platname"] = 'Reference' if force_cuda: try: Platform.getPlatformByName('CUDA') except: raise RuntimeError('Forcing failure because CUDA platform unavailable') if threads > 1: logger.warn("Setting the number of threads will have no effect on OpenMM engine.\n") elif engname == "gromacs": # Gromacs-specific options GenOpts["gmxpath"] = TgtOptions["gmxpath"] GenOpts["gmxsuffix"] = TgtOptions["gmxsuffix"] EngOpts["liquid"]["gmx_top"] = os.path.splitext(liquid_fnm)[0] + ".top" EngOpts["liquid"]["gmx_mdp"] = os.path.splitext(liquid_fnm)[0] + ".mdp" EngOpts["gas"]["gmx_top"] = os.path.splitext(gas_fnm)[0] + ".top" EngOpts["gas"]["gmx_mdp"] = os.path.splitext(gas_fnm)[0] + ".mdp" if force_cuda: logger.warn("force_cuda option has no effect on Gromacs engine.") if rpmd_beads > 0: raise RuntimeError("Gromacs cannot handle RPMD.") if mts: logger.warn("Gromacs not configured for multiple timestep integrator.") if anisotropic: logger.warn("Gromacs not configured for anisotropic box scaling.") elif engname == "tinker": # Tinker-specific options GenOpts["tinkerpath"] = TgtOptions["tinkerpath"] EngOpts["liquid"]["tinker_key"] = os.path.splitext(liquid_fnm)[0] + ".key" EngOpts["gas"]["tinker_key"] = os.path.splitext(gas_fnm)[0] + ".key" if force_cuda: logger.warn("force_cuda option has no effect on Tinker engine.") if rpmd_beads > 0: raise RuntimeError("TINKER cannot handle RPMD.") if mts: logger.warn("Tinker not configured for multiple timestep integrator.") EngOpts["liquid"].update(GenOpts) EngOpts["gas"].update(GenOpts) for i in EngOpts: printcool_dictionary(EngOpts[i], "Engine options for %s" % i) # Set up MD options MDOpts = OrderedDict() MDOpts["liquid"] = OrderedDict([("nsteps", liquid_nsteps), ("timestep", liquid_timestep), ("temperature", temperature), ("pressure", pressure), ("nequil", liquid_nequil), ("minimize", minimize), ("nsave", int(1000 * liquid_intvl / liquid_timestep)), ("verbose", True), ('save_traj', TgtOptions['save_traj']), ("threads", threads), ("anisotropic", anisotropic), ("nbarostat", 10), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) MDOpts["gas"] = OrderedDict([("nsteps", gas_nsteps), ("timestep", gas_timestep), ("temperature", temperature), ("nsave", int(1000 * gas_intvl / gas_timestep)), ("nequil", gas_nequil), ("minimize", minimize), ("threads", 1), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) # Energy components analysis disabled for OpenMM MTS because it uses force groups if (engname == "openmm" and mts): logger.warn("OpenMM with MTS integrator; energy components analysis will be disabled.\n") # Create instances of the MD Engine objects. Liquid = Engine(name="liquid", **EngOpts["liquid"]) Gas = Engine(name="gas", **EngOpts["gas"]) #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# printcool("Condensed phase molecular dynamics", color=4, bold=True) # This line runs the condensed phase simulation. prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"]) Rhos = prop_return['Rhos'] Potentials = prop_return['Potentials'] Kinetics = prop_return['Kinetics'] Volumes = prop_return['Volumes'] Dips = prop_return['Dips'] EDA = prop_return['Ecomps'] # Create a bunch of physical constants. # Energies are in kJ/mol # Lengths are in nanometers. L = len(Rhos) kB = 0.008314472471220214 T = temperature kT = kB * T mBeta = -1.0 / kT Beta = 1.0 / kT atm_unit = 0.061019351687175 bar_unit = 0.060221417930000 # This is how I calculated the prefactor for the dielectric constant. # eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2 # epsunit = 1.0*(debye**2) / nanometer**3 / BOLTZMANN_CONSTANT_kB / kelvin # prefactor = epsunit/eps0/3 prefactor = 30.348705333964077 # Gather some physical variables. Energies = Potentials + Kinetics Ene_avg, Ene_err = mean_stderr(Energies) pV = atm_unit * pressure * Volumes pV_avg, pV_err = mean_stderr(pV) Rho_avg, Rho_err = mean_stderr(Rhos) PrintEDA(EDA, NMol) #==============================================# # Now run the simulation for just the monomer. # #==============================================# # Run the OpenMM simulation, gather information. printcool("Gas phase molecular dynamics", color=4, bold=True) mprop_return = Gas.molecular_dynamics(**MDOpts["gas"]) mPotentials = mprop_return['Potentials'] mKinetics = mprop_return['Kinetics'] mEDA = mprop_return['Ecomps'] mEnergies = mPotentials + mKinetics mEne_avg, mEne_err = mean_stderr(mEnergies) PrintEDA(mEDA, 1) #============================================# # Compute the potential energy derivatives. # #============================================# logger.info("Calculating potential energy derivatives with finite difference step size: %f\n" % h) # Switch for whether to compute the derivatives two different ways for consistency. FDCheck = False # Create a double-precision simulation object if desired (seems unnecessary). DoublePrecisionDerivatives = False if engname == "openmm" and DoublePrecisionDerivatives and AGrad: logger.info("Creating Double Precision Simulation for parameter derivatives\n") Liquid = Engine(name="liquid", openmm_precision="double", **EngOpts["liquid"]) Gas = Engine(name="gas", openmm_precision="double", **EngOpts["gas"]) # Compute the energy and dipole derivatives. printcool("Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Energies), color=4, bold=True) G, GDx, GDy, GDz = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Energies), AGrad, dipole=True) printcool("Gas phase energy derivatives", color=4, bold=True) mG, _, __, ___ = energy_derivatives(Gas, FF, mvals, h, pgrad, len(mEnergies), AGrad, dipole=False) #==============================================# # Condensed phase properties and derivatives. # #==============================================# #---- # Density #---- # Build the first density derivative. GRho = mBeta * (flat(np.mat(G) * col(Rhos)) / L - np.mean(Rhos) * np.mean(G, axis=1)) # Print out the density and its derivative. Sep = printcool("Density: % .4f +- % .4f kg/m^3\nAnalytic Derivative:" % (Rho_avg, Rho_err)) FF.print_map(vals=GRho) logger.info(Sep) def calc_rho(b = None, **kwargs): if b == None: b = np.ones(L,dtype=float) if 'r_' in kwargs: r_ = kwargs['r_'] return bzavg(r_,b) # No need to calculate error using bootstrap, but here it is anyway # Rhoboot = [] # for i in range(numboots): # boot = np.random.randint(N,size=N) # Rhoboot.append(calc_rho(None,**{'r_':Rhos[boot]})) # Rhoboot = np.array(Rhoboot) # Rho_err = np.std(Rhoboot) if FDCheck: Sep = printcool("Numerical Derivative:") GRho1 = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_rho, {'r_':Rhos}) FF.print_map(vals=GRho1) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GRho, GRho1)] FF.print_map(vals=absfrac) #---- # Enthalpy of vaporization #---- H = Energies + pV V = np.array(Volumes) # Print out the liquid enthalpy. logger.info("Liquid enthalpy: % .4f kJ/mol, stdev % .4f ; (% .4f from energy, % .4f from pV)\n" % (np.mean(H), np.std(H), np.mean(Energies), np.mean(pV))) numboots = 1000 # The enthalpy of vaporization in kJ/mol. Hvap_avg = mEne_avg - Ene_avg / NMol + kT - np.mean(pV) / NMol Hvap_err = np.sqrt(Ene_err**2 / NMol**2 + mEne_err**2 + pV_err**2/NMol**2) # Build the first Hvap derivative. GHvap = np.mean(G,axis=1) GHvap += mBeta * (flat(np.mat(G) * col(Energies)) / L - Ene_avg * np.mean(G, axis=1)) GHvap /= NMol GHvap -= np.mean(mG,axis=1) GHvap -= mBeta * (flat(np.mat(mG) * col(mEnergies)) / L - mEne_avg * np.mean(mG, axis=1)) GHvap *= -1 GHvap -= mBeta * (flat(np.mat(G) * col(pV)) / L - np.mean(pV) * np.mean(G, axis=1)) / NMol Sep = printcool("Enthalpy of Vaporization: % .4f +- %.4f kJ/mol\nAnalytic Derivative:" % (Hvap_avg, Hvap_err)) FF.print_map(vals=GHvap) # Define some things to make the analytic derivatives easier. Gbar = np.mean(G,axis=1) def deprod(vec): return flat(np.mat(G)*col(vec))/L def covde(vec): return flat(np.mat(G)*col(vec))/L - Gbar*np.mean(vec) def avg(vec): return np.mean(vec) #---- # Thermal expansion coefficient #---- def calc_alpha(b = None, **kwargs): if b == None: b = np.ones(L,dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] if 'v_' in kwargs: v_ = kwargs['v_'] return 1/(kT*T) * (bzavg(h_*v_,b)-bzavg(h_,b)*bzavg(v_,b))/bzavg(v_,b) Alpha = calc_alpha(None, **{'h_':H, 'v_':V}) Alphaboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Alphaboot.append(calc_alpha(None, **{'h_':H[boot], 'v_':V[boot]})) Alphaboot = np.array(Alphaboot) Alpha_err = np.std(Alphaboot) * max([np.sqrt(statisticalInefficiency(V)),np.sqrt(statisticalInefficiency(H))]) # Thermal expansion coefficient analytic derivative GAlpha1 = -1 * Beta * deprod(H*V) * avg(V) / avg(V)**2 GAlpha2 = +1 * Beta * avg(H*V) * deprod(V) / avg(V)**2 GAlpha3 = deprod(V)/avg(V) - Gbar GAlpha4 = Beta * covde(H) GAlpha = (GAlpha1 + GAlpha2 + GAlpha3 + GAlpha4)/(kT*T) Sep = printcool("Thermal expansion coefficient: % .4e +- %.4e K^-1\nAnalytic Derivative:" % (Alpha, Alpha_err)) FF.print_map(vals=GAlpha) if FDCheck: GAlpha_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_alpha, {'h_':H,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GAlpha_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GAlpha, GAlpha_fd)] FF.print_map(vals=absfrac) #---- # Isothermal compressibility #---- def calc_kappa(b=None, **kwargs): if b == None: b = np.ones(L,dtype=float) if 'v_' in kwargs: v_ = kwargs['v_'] return bar_unit / kT * (bzavg(v_**2,b)-bzavg(v_,b)**2)/bzavg(v_,b) Kappa = calc_kappa(None,**{'v_':V}) Kappaboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Kappaboot.append(calc_kappa(None,**{'v_':V[boot]})) Kappaboot = np.array(Kappaboot) Kappa_err = np.std(Kappaboot) * np.sqrt(statisticalInefficiency(V)) # Isothermal compressibility analytic derivative Sep = printcool("Isothermal compressibility: % .4e +- %.4e bar^-1\nAnalytic Derivative:" % (Kappa, Kappa_err)) GKappa1 = +1 * Beta**2 * avg(V**2) * deprod(V) / avg(V)**2 GKappa2 = -1 * Beta**2 * avg(V) * deprod(V**2) / avg(V)**2 GKappa3 = +1 * Beta**2 * covde(V) GKappa = bar_unit*(GKappa1 + GKappa2 + GKappa3) FF.print_map(vals=GKappa) if FDCheck: GKappa_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_kappa, {'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GKappa_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GKappa, GKappa_fd)] FF.print_map(vals=absfrac) #---- # Isobaric heat capacity #---- def calc_cp(b=None, **kwargs): if b == None: b = np.ones(L,dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] Cp_ = 1/(NMol*kT*T) * (bzavg(h_**2,b) - bzavg(h_,b)**2) Cp_ *= 1000 / 4.184 return Cp_ Cp = calc_cp(None,**{'h_':H}) Cpboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Cpboot.append(calc_cp(None,**{'h_':H[boot]})) Cpboot = np.array(Cpboot) Cp_err = np.std(Cpboot) * np.sqrt(statisticalInefficiency(H)) # Isobaric heat capacity analytic derivative GCp1 = 2*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp2 = mBeta*covde(H**2) * 1000 / 4.184 / (NMol*kT*T) GCp3 = 2*Beta*avg(H)*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp = GCp1 + GCp2 + GCp3 Sep = printcool("Isobaric heat capacity: % .4e +- %.4e cal mol-1 K-1\nAnalytic Derivative:" % (Cp, Cp_err)) FF.print_map(vals=GCp) if FDCheck: GCp_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_cp, {'h_':H}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GCp_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GCp,GCp_fd)] FF.print_map(vals=absfrac) #---- # Dielectric constant #---- def calc_eps0(b=None, **kwargs): if b == None: b = np.ones(L,dtype=float) if 'd_' in kwargs: # Dipole moment vector. d_ = kwargs['d_'] if 'v_' in kwargs: # Volume. v_ = kwargs['v_'] b0 = np.ones(L,dtype=float) dx = d_[:,0] dy = d_[:,1] dz = d_[:,2] D2 = bzavg(dx**2,b)-bzavg(dx,b)**2 D2 += bzavg(dy**2,b)-bzavg(dy,b)**2 D2 += bzavg(dz**2,b)-bzavg(dz,b)**2 return prefactor*D2/bzavg(v_,b)/T Eps0 = calc_eps0(None,**{'d_':Dips, 'v_':V}) Eps0boot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Eps0boot.append(calc_eps0(None,**{'d_':Dips[boot], 'v_':V[boot]})) Eps0boot = np.array(Eps0boot) Eps0_err = np.std(Eps0boot)*np.sqrt(np.mean([statisticalInefficiency(Dips[:,0]),statisticalInefficiency(Dips[:,1]),statisticalInefficiency(Dips[:,2])])) # Dielectric constant analytic derivative Dx = Dips[:,0] Dy = Dips[:,1] Dz = Dips[:,2] D2 = avg(Dx**2)+avg(Dy**2)+avg(Dz**2)-avg(Dx)**2-avg(Dy)**2-avg(Dz)**2 GD2 = 2*(flat(np.mat(GDx)*col(Dx))/L - avg(Dx)*(np.mean(GDx,axis=1))) - Beta*(covde(Dx**2) - 2*avg(Dx)*covde(Dx)) GD2 += 2*(flat(np.mat(GDy)*col(Dy))/L - avg(Dy)*(np.mean(GDy,axis=1))) - Beta*(covde(Dy**2) - 2*avg(Dy)*covde(Dy)) GD2 += 2*(flat(np.mat(GDz)*col(Dz))/L - avg(Dz)*(np.mean(GDz,axis=1))) - Beta*(covde(Dz**2) - 2*avg(Dz)*covde(Dz)) GEps0 = prefactor*(GD2/avg(V) - mBeta*covde(V)*D2/avg(V)**2)/T Sep = printcool("Dielectric constant: % .4e +- %.4e\nAnalytic Derivative:" % (Eps0, Eps0_err)) FF.print_map(vals=GEps0) if FDCheck: GEps0_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_eps0, {'d_':Dips,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GEps0_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GEps0,GEps0_fd)] FF.print_map(vals=absfrac) logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") with wopen(os.path.join('npt_result.p')) as f: lp_dump((Rhos, Volumes, Potentials, Energies, Dips, G, [GDx, GDy, GDz], mPotentials, mEnergies, mG, Rho_err, Hvap_err, Alpha_err, Kappa_err, Cp_err, Eps0_err, NMol),f)
def energy_driver(mvals_): FF.make(mvals_) return engine.energy_dipole()
def energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy()
def runTest(self): """Check water tutorial study runs without errors""" self.logger.debug("\nSetting input file to 'very_simple.in'\n") input_file = 'very_simple.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) self.assertEqual(dict, type(options), msg="\nParser gave incorrect type for options") self.assertEqual(list, type(tgt_opts), msg="\nParser gave incorrect type for tgt_opts") for target in tgt_opts: self.assertEqual( dict, type(target), msg="\nParser gave incorrect type for target dict") ## The force field component of the project forcefield = FF(options) self.assertEqual(FF, type(forcefield), msg="\nExpected forcebalance forcefield object") ## The objective function objective = Objective(options, tgt_opts, forcefield) self.assertEqual(Objective, type(objective), msg="\nExpected forcebalance objective object") ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") self.logger.debug(str(optimizer) + "\n") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') self.assertNdArrayEqual( EXPECTED_WATER_RESULTS, result, delta=0.001, msg= "\nCalculation results have changed from previously calculated values.\n" "If this seems reasonable, update EXPECTED_WATER_RESULTS in test_system.py with these values" ) # Fail if calculation takes longer than previously to converge self.assertGreaterEqual(ITERATIONS_TO_CONVERGE, Counter(), msg="\nCalculation took longer than expected to converge (%d iterations vs previous of %d)" %\ (ITERATIONS_TO_CONVERGE, Counter()))
def runTest(self): """Check implicit hydration free energy study (Hydration target) converges to expected results""" self.logger.debug("\nSetting input file to 'optimize.in'\n") input_file = 'optimize.in' ## The general options and target options that come from parsing the input file self.logger.debug("Parsing inputs...\n") options, tgt_opts = parse_inputs(input_file) self.logger.debug("options:\n%s\n\ntgt_opts:\n%s\n\n" % (str(options), str(tgt_opts))) self.assertEqual(dict, type(options), msg="\nParser gave incorrect type for options") self.assertEqual(list, type(tgt_opts), msg="\nParser gave incorrect type for tgt_opts") for target in tgt_opts: self.assertEqual( dict, type(target), msg="\nParser gave incorrect type for target dict") ## The force field component of the project self.logger.debug("Creating forcefield using loaded options: ") forcefield = FF(options) self.logger.debug(str(forcefield) + "\n") self.assertEqual(FF, type(forcefield), msg="\nExpected forcebalance forcefield object") ## The objective function self.logger.debug( "Creating object using loaded options and forcefield: ") objective = Objective(options, tgt_opts, forcefield) self.logger.debug(str(objective) + "\n") self.assertEqual(Objective, type(objective), msg="\nExpected forcebalance objective object") ## The optimizer component of the project self.logger.debug("Creating optimizer: ") optimizer = Optimizer(options, objective, forcefield) self.logger.debug(str(optimizer) + "\n") self.assertEqual(Optimizer, type(optimizer), msg="\nExpected forcebalance optimizer object") ## Actually run the optimizer. self.logger.debug("Done setting up! Running optimizer...\n") result = optimizer.Run() self.logger.debug("\nOptimizer finished. Final results:\n") self.logger.debug(str(result) + '\n') self.assertNdArrayEqual( EXPECTED_ETHANOL_RESULTS, forcefield.create_pvals(result), delta=0.02, msg= "\nCalculation results have changed from previously calculated values.\n" "If this seems reasonable, update EXPECTED_ETHANOL_RESULTS in test_system.py with these values" )
def main(): """ Run the script with -h for help Usage: python npt_tinker.py input.xyz [-k input.key] liquid_production_steps liquid_timestep liquid_interval temperature(K) pressure(atm) """ if not os.path.exists(args.liquid_xyzfile): warn_press_key("Warning: %s does not exist, script cannot continue" % args.liquid_xyzfile) # Set up some conversion factors # All units are in kJ/mol N = niterations # Conversion factor for kT derived from: # In [6]: 1.0 / ((1.0 * kelvin * BOLTZMANN_CONSTANT_kB * AVOGADRO_CONSTANT_NA) / kilojoule_per_mole) # Out[6]: 120.27221251395186 T = temperature mBeta = -120.27221251395186 / temperature Beta = 120.27221251395186 / temperature kT = 0.0083144724712202 * temperature # Conversion factor for pV derived from: # In [14]: 1.0 * atmosphere * nanometer ** 3 * AVOGADRO_CONSTANT_NA / kilojoule_per_mole # Out[14]: 0.061019351687175 pcon = 0.061019351687175 # Load the force field in from the ForceBalance pickle. FF,mvals,h,AGrad = lp_load(open('forcebalance.p')) # Create the force field XML files. FF.make(mvals) #=================================================================# # Get the number of molecules from the liquid xyz file. # #=================================================================# xin = "%s" % args.liquid_xyzfile + ("" if args.liquid_keyfile == None else " -k %s" % args.liquid_keyfile) cmdstr = "./analyze %s" % xin oanl = _exec(cmdstr,stdin="G",print_command=True,print_to_screen=True) molflag = False for line in oanl: if 'Number of Molecules' in line: if not molflag: NMol = int(line.split()[-1]) molflag = True else: raise Exception("TINKER output contained more than one line with the words 'Number of Molecules'") if molflag: print "Detected %i Molecules" % NMol if not molflag: raise Exception("Failed to detect the number of molecules") #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# Rhos, Potentials, Kinetics, Volumes, Dips = run_simulation(args.liquid_xyzfile,args.liquid_keyfile,tstep=timestep,nstep=nsteps,neq=nequiliterations,npr=niterations,verbose=True) Energies = Potentials + Kinetics V = Volumes pV = pressure * Volumes H = Energies + pV # Get the energy and dipole gradients. print "Post-processing the liquid simulation snapshots." G, GDx, GDy, GDz = energy_dipole_derivatives(mvals,h,FF,args.liquid_xyzfile,args.liquid_keyfile,AGrad) print #==============================================# # Now run the simulation for just the monomer. # #==============================================# _a, mPotentials, mKinetics, _b, _c = run_simulation(args.gas_xyzfile,args.gas_keyfile,tstep=m_timestep,nstep=m_nsteps,neq=m_nequiliterations,npr=m_niterations,pbc=False) mEnergies = mPotentials + mKinetics mN = len(mEnergies) print "Post-processing the gas simulation snapshots." mG = energy_derivatives(mvals,h,FF,args.gas_xyzfile,args.gas_keyfile,AGrad) print numboots = 1000 def bootstats(func,inputs): # Calculate error using bootstats method dboot = [] for i in range(numboots): newins = {k : v[np.random.randint(len(v),size=len(v))] for k,v in inputs.items()} dboot.append(np.mean(func(**newins))) return func(**inputs),np.std(np.array(dboot)) def calc_arr(b = None, **kwargs): # This tomfoolery is required because of Python syntax; # default arguments must come after nondefault arguments # and kwargs must come at the end. This function is used # in bootstrap error calcs and also in derivative calcs. if 'arr' in kwargs: arr = kwargs['arr'] if b == None: b = np.ones(len(arr),dtype=float) return bzavg(arr,b) # The density in kg/m^3. # Note: Not really necessary to use bootstrap here, but good to # demonstrate the principle. Rho_avg, Rho_err = bootstats(calc_arr,{'arr':Rhos}) Rho_err *= np.sqrt(statisticalInefficiency(Rhos)) print "The finite difference step size is:",h # The first density derivative GRho = mBeta * (flat(np.mat(G) * col(Rhos)) / N - np.mean(Rhos) * np.mean(G, axis=1)) FDCheck = False Sep = printcool("Density: % .4f +- % .4f kg/m^3, Analytic Derivative" % (Rho_avg, Rho_err)) FF.print_map(vals=GRho) print Sep if FDCheck: Sep = printcool("Numerical Derivative:") GRho1 = property_derivatives(mvals, h, FF, args.liquid_xyzfile, args.liquid_keyfile, kT, calc_arr, {'arr':Rhos}) FF.print_map(vals=GRho1) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GRho, GRho1)] FF.print_map(vals=absfrac) # The enthalpy of vaporization in kJ/mol. Ene_avg, Ene_err = bootstats(calc_arr,{'arr':Energies}) mEne_avg, mEne_err = bootstats(calc_arr,{'arr':mEnergies}) pV_avg, pV_err = bootstats(calc_arr,{'arr':pV}) Ene_err *= np.sqrt(statisticalInefficiency(Energies)) mEne_err *= np.sqrt(statisticalInefficiency(mEnergies)) pV_err *= np.sqrt(statisticalInefficiency(pV)) Hvap_avg = mEne_avg - Ene_avg / NMol + kT - np.mean(pV) / NMol Hvap_err = np.sqrt(Ene_err**2 / NMol**2 + mEne_err**2 + pV_err**2/NMol**2) # Build the first Hvap derivative. GHvap = np.mean(G,axis=1) GHvap += mBeta * (flat(np.mat(G) * col(Energies)) / N - Ene_avg * np.mean(G, axis=1)) GHvap /= NMol GHvap -= np.mean(mG,axis=1) GHvap -= mBeta * (flat(np.mat(mG) * col(mEnergies)) / N - mEne_avg * np.mean(mG, axis=1)) GHvap *= -1 GHvap -= mBeta * (flat(np.mat(G) * col(pV)) / N - np.mean(pV) * np.mean(G, axis=1)) / NMol print "Box total energy:", np.mean(Energies) print "Monomer total energy:", np.mean(mEnergies) Sep = printcool("Enthalpy of Vaporization: % .4f +- %.4f kJ/mol, Derivatives below" % (Hvap_avg, Hvap_err)) FF.print_map(vals=GHvap) print Sep # Define some things to make the analytic derivatives easier. Gbar = np.mean(G,axis=1) def covde(vec): return flat(np.mat(G)*col(vec))/N - Gbar*np.mean(vec) def avg(vec): return np.mean(vec) ## Thermal expansion coefficient and bootstrap error estimation def calc_alpha(b = None, **kwargs): if 'h_' in kwargs: h_ = kwargs['h_'] if 'v_' in kwargs: v_ = kwargs['v_'] if b == None: b = np.ones(len(v_),dtype=float) return 1/(kT*T) * (bzavg(h_*v_,b)-bzavg(h_,b)*bzavg(v_,b))/bzavg(v_,b) Alpha, Alpha_err = bootstats(calc_alpha,{'h_':H, 'v_':V}) Alpha_err *= np.sqrt(max(statisticalInefficiency(V),statisticalInefficiency(H))) ## Thermal expansion coefficient analytic derivative GAlpha1 = mBeta * covde(H*V) / avg(V) GAlpha2 = Beta * avg(H*V) * covde(V) / avg(V)**2 GAlpha3 = flat(np.mat(G)*col(V))/N/avg(V) - Gbar GAlpha4 = Beta * covde(H) GAlpha = (GAlpha1 + GAlpha2 + GAlpha3 + GAlpha4)/(kT*T) Sep = printcool("Thermal expansion coefficient: % .4e +- %.4e K^-1\nAnalytic Derivative:" % (Alpha, Alpha_err)) FF.print_map(vals=GAlpha) if FDCheck: GAlpha_fd = property_derivatives(mvals, h, FF, args.liquid_xyzfile, args.liquid_keyfile, kT, calc_alpha, {'h_':H,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GAlpha_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GAlpha, GAlpha_fd)] FF.print_map(vals=absfrac) ## Isothermal compressibility # In [15]: 1.0*bar*nanometer**3/kilojoules_per_mole/item # Out[15]: 0.06022141792999999 bar_unit = 0.06022141793 def calc_kappa(b=None, **kwargs): if 'v_' in kwargs: v_ = kwargs['v_'] if b == None: b = np.ones(len(v_),dtype=float) return bar_unit / kT * (bzavg(v_**2,b)-bzavg(v_,b)**2)/bzavg(v_,b) Kappa, Kappa_err = bootstats(calc_kappa,{'v_':V}) Kappa_err *= np.sqrt(statisticalInefficiency(V)) ## Isothermal compressibility analytic derivative Sep = printcool("Isothermal compressibility: % .4e +- %.4e bar^-1\nAnalytic Derivative:" % (Kappa, Kappa_err)) GKappa1 = -1 * Beta**2 * avg(V) * covde(V**2) / avg(V)**2 GKappa2 = +1 * Beta**2 * avg(V**2) * covde(V) / avg(V)**2 GKappa3 = +1 * Beta**2 * covde(V) GKappa = bar_unit*(GKappa1 + GKappa2 + GKappa3) FF.print_map(vals=GKappa) if FDCheck: GKappa_fd = property_derivatives(mvals, h, FF, args.liquid_xyzfile, args.liquid_keyfile, kT, calc_kappa, {'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GKappa_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GKappa, GKappa_fd)] FF.print_map(vals=absfrac) ## Isobaric heat capacity def calc_cp(b=None, **kwargs): if 'h_' in kwargs: h_ = kwargs['h_'] if b == None: b = np.ones(len(h_),dtype=float) Cp_ = 1/(NMol*kT*T) * (bzavg(h_**2,b) - bzavg(h_,b)**2) Cp_ *= 1000 / 4.184 return Cp_ Cp, Cp_err = bootstats(calc_cp, {'h_':H}) Cp_err *= np.sqrt(statisticalInefficiency(H)) ## Isobaric heat capacity analytic derivative GCp1 = 2*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp2 = mBeta*covde(H**2) * 1000 / 4.184 / (NMol*kT*T) GCp3 = 2*Beta*avg(H)*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp = GCp1 + GCp2 + GCp3 Sep = printcool("Isobaric heat capacity: % .4e +- %.4e cal mol-1 K-1\nAnalytic Derivative:" % (Cp, Cp_err)) FF.print_map(vals=GCp) if FDCheck: GCp_fd = property_derivatives(mvals, h, FF, args.liquid_xyzfile, args.liquid_keyfile, kT, calc_cp, {'h_':H}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GCp_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GCp,GCp_fd)] FF.print_map(vals=absfrac) ## Dielectric constant # eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2 # epsunit = 1.0*(debye**2) / nanometer**3 / BOLTZMANN_CONSTANT_kB / kelvin # prefactor = epsunit/eps0/3 prefactor = 30.348705333964077 def calc_eps0(b=None, **kwargs): if 'd_' in kwargs: # Dipole moment vector. d_ = kwargs['d_'] if 'v_' in kwargs: # Volume. v_ = kwargs['v_'] if b == None: b = np.ones(len(v_),dtype=float) dx = d_[:,0] dy = d_[:,1] dz = d_[:,2] D2 = bzavg(dx**2,b)-bzavg(dx,b)**2 D2 += bzavg(dy**2,b)-bzavg(dy,b)**2 D2 += bzavg(dz**2,b)-bzavg(dz,b)**2 return prefactor*D2/bzavg(v_,b)/T Eps0, Eps0_err = bootstats(calc_eps0,{'d_':Dips, 'v_':V}) Eps0 += 1.0 Eps0_err *= np.sqrt(np.mean([statisticalInefficiency(Dips[:,0]),statisticalInefficiency(Dips[:,1]),statisticalInefficiency(Dips[:,2])])) ## Dielectric constant analytic derivative Dx = Dips[:,0] Dy = Dips[:,1] Dz = Dips[:,2] D2 = avg(Dx**2)+avg(Dy**2)+avg(Dz**2)-avg(Dx)**2-avg(Dy)**2-avg(Dz)**2 GD2 = 2*(flat(np.mat(GDx)*col(Dx))/N - avg(Dx)*(np.mean(GDx,axis=1))) - Beta*(covde(Dx**2) - 2*avg(Dx)*covde(Dx)) GD2 += 2*(flat(np.mat(GDy)*col(Dy))/N - avg(Dy)*(np.mean(GDy,axis=1))) - Beta*(covde(Dy**2) - 2*avg(Dy)*covde(Dy)) GD2 += 2*(flat(np.mat(GDz)*col(Dz))/N - avg(Dz)*(np.mean(GDz,axis=1))) - Beta*(covde(Dz**2) - 2*avg(Dz)*covde(Dz)) GEps0 = prefactor*(GD2/avg(V) - mBeta*covde(V)*D2/avg(V)**2)/T Sep = printcool("Dielectric constant: % .4e +- %.4e\nAnalytic Derivative:" % (Eps0, Eps0_err)) FF.print_map(vals=GEps0) if FDCheck: GEps0_fd = property_derivatives(mvals, h, FF, args.liquid_xyzfile, args.liquid_keyfile, kT, calc_eps0, {'d_':Dips,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GEps0_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GEps0,GEps0_fd)] FF.print_map(vals=absfrac) ## Print the final force field. pvals = FF.make(mvals) with open(os.path.join('npt_result.p'),'w') as f: lp_dump((Rhos, Volumes, Potentials, Energies, Dips, G, [GDx, GDy, GDz], mPotentials, mEnergies, mG, Rho_err, Hvap_err, Alpha_err, Kappa_err, Cp_err, Eps0_err, NMol),f)
def main(): """ Usage: (runcuda.sh) npt.py <openmm|gromacs|tinker> <lipid_nsteps> <lipid_timestep (fs)> <lipid_intvl (ps> <temperature> <pressure> This program is meant to be called automatically by ForceBalance on a GPU cluster (specifically, subroutines in openmmio.py). It is not easy to use manually. This is because the force field is read in from a ForceBalance 'FF' class. I wrote this program because automatic fitting of the density (or other equilibrium properties) is computationally intensive, and the calculations need to be distributed to the queue. The main instance of ForceBalance (running on my workstation) queues up a bunch of these jobs (using Work Queue). Then, I submit a bunch of workers to GPU clusters (e.g. Certainty, Keeneland). The worker scripts connect to. the main instance and receives one of these jobs. This script can also be executed locally, if you want to (e.g. for debugging). Just make sure you have the pickled 'forcebalance.p' file. """ printcool("ForceBalance condensed phase simulation using engine: %s" % engname.upper(), color=4, bold=True) #---- # Load the ForceBalance pickle file which contains: #---- # - Force field object # - Optimization parameters # - Options from the Target object that launched this simulation # - Switch for whether to evaluate analytic derivatives. FF, mvals, TgtOptions, AGrad = lp_load('forcebalance.p') FF.ffdir = '.' # Write the force field file. FF.make(mvals) #---- # Load the options that are set in the ForceBalance input file. #---- # Finite difference step size h = TgtOptions['h'] pgrad = TgtOptions['pgrad'] # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps) lipid_timestep = TgtOptions['lipid_timestep'] lipid_nsteps = TgtOptions['lipid_md_steps'] lipid_nequil = TgtOptions['lipid_eq_steps'] lipid_intvl = TgtOptions['lipid_interval'] lipid_fnm = TgtOptions['lipid_coords'] # Number of threads, multiple timestep integrator, anisotropic box etc. threads = TgtOptions.get('md_threads', 1) mts = TgtOptions.get('mts_integrator', 0) force_cuda = TgtOptions.get('force_cuda', 0) anisotropic = TgtOptions.get('anisotropic_box', 0) minimize = TgtOptions.get('minimize_energy', 1) # Print all options. printcool_dictionary(TgtOptions, title="Options from ForceBalance") lipid_snapshots = int((lipid_nsteps * lipid_timestep / 1000) / lipid_intvl) lipid_iframes = int(1000 * lipid_intvl / lipid_timestep) logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (lipid_snapshots, lipid_iframes, lipid_timestep)) if lipid_snapshots < 2: raise Exception('Please set the number of lipid time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * int(lipid_intvl/lipid_timestep))) #---- # Loading coordinates #---- ML = Molecule(lipid_fnm, toppbc=True) # Determine the number of molecules in the condensed phase coordinate file. NMol = len(ML.molecules) #---- # Setting up MD simulations #---- EngOpts = OrderedDict() EngOpts["lipid"] = OrderedDict([("coords", lipid_fnm), ("mol", ML), ("pbc", True)]) if "nonbonded_cutoff" in TgtOptions: EngOpts["lipid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"] if "vdw_cutoff" in TgtOptions: EngOpts["lipid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"] GenOpts = OrderedDict([('FF', FF)]) if engname == "openmm": # OpenMM-specific options EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA') # For now, always run gas phase calculations on the reference platform EngOpts["gas"]["platname"] = 'Reference' if force_cuda: try: Platform.getPlatformByName('CUDA') except: raise RuntimeError( 'Forcing failure because CUDA platform unavailable') EngOpts["liquid"]["platname"] = 'CUDA' if threads > 1: logger.warn( "Setting the number of threads will have no effect on OpenMM engine.\n" ) elif engname == "gromacs": # Gromacs-specific options GenOpts["gmxpath"] = TgtOptions["gmxpath"] GenOpts["gmxsuffix"] = TgtOptions["gmxsuffix"] EngOpts["lipid"]["gmx_top"] = os.path.splitext(lipid_fnm)[0] + ".top" EngOpts["lipid"]["gmx_mdp"] = os.path.splitext(lipid_fnm)[0] + ".mdp" EngOpts["lipid"]["gmx_eq_barostat"] = TgtOptions["gmx_eq_barostat"] if force_cuda: logger.warn("force_cuda option has no effect on Gromacs engine.") if mts: logger.warn( "Gromacs not configured for multiple timestep integrator.") if anisotropic: logger.warn("Gromacs not configured for anisotropic box scaling.") elif engname == "tinker": # Tinker-specific options GenOpts["tinkerpath"] = TgtOptions["tinkerpath"] EngOpts["lipid"]["tinker_key"] = os.path.splitext( lipid_fnm)[0] + ".key" if force_cuda: logger.warn("force_cuda option has no effect on Tinker engine.") if mts: logger.warn( "Tinker not configured for multiple timestep integrator.") EngOpts["lipid"].update(GenOpts) for i in EngOpts: printcool_dictionary(EngOpts[i], "Engine options for %s" % i) # Set up MD options MDOpts = OrderedDict() MDOpts["lipid"] = OrderedDict([("nsteps", lipid_nsteps), ("timestep", lipid_timestep), ("temperature", temperature), ("pressure", pressure), ("nequil", lipid_nequil), ("minimize", minimize), ("nsave", int(1000 * lipid_intvl / lipid_timestep)), ("verbose", False), ('save_traj', TgtOptions['save_traj']), ("threads", threads), ("anisotropic", anisotropic), ("mts", mts), ("faststep", faststep), ("bilayer", True)]) # Energy components analysis disabled for OpenMM MTS because it uses force groups if (engname == "openmm" and mts): logger.warn( "OpenMM with MTS integrator; energy components analysis will be disabled.\n" ) # Create instances of the MD Engine objects. Lipid = Engine(name="lipid", **EngOpts["lipid"]) #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# printcool("Condensed phase molecular dynamics", color=4, bold=True) # This line runs the condensed phase simulation. prop_return = Lipid.molecular_dynamics(**MDOpts["lipid"]) Rhos = prop_return['Rhos'] Potentials = prop_return['Potentials'] Kinetics = prop_return['Kinetics'] Volumes = prop_return['Volumes'] Dips = prop_return['Dips'] EDA = prop_return['Ecomps'] Als = prop_return['Als'] Scds = prop_return['Scds'] # Create a bunch of physical constants. # Energies are in kJ/mol # Lengths are in nanometers. L = len(Rhos) kB = 0.008314472471220214 T = temperature kT = kB * T mBeta = -1.0 / kT Beta = 1.0 / kT atm_unit = 0.061019351687175 bar_unit = 0.060221417930000 # This is how I calculated the prefactor for the dielectric constant. # eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2 # epsunit = 1.0*(debye**2) / nanometer**3 / BOLTZMANN_CONSTANT_kB / kelvin # prefactor = epsunit/eps0/3 prefactor = 30.348705333964077 # Gather some physical variables. Energies = Potentials + Kinetics Ene_avg, Ene_err = mean_stderr(Energies) pV = atm_unit * pressure * Volumes pV_avg, pV_err = mean_stderr(pV) Rho_avg, Rho_err = mean_stderr(Rhos) PrintEDA(EDA, NMol) #============================================# # Compute the potential energy derivatives. # #============================================# logger.info( "Calculating potential energy derivatives with finite difference step size: %f\n" % h) # Switch for whether to compute the derivatives two different ways for consistency. FDCheck = False # Create a double-precision simulation object if desired (seems unnecessary). DoublePrecisionDerivatives = False if engname == "openmm" and DoublePrecisionDerivatives and AGrad: logger.info( "Creating Double Precision Simulation for parameter derivatives\n") Lipid = Engine(name="lipid", openmm_precision="double", **EngOpts["lipid"]) # Compute the energy and dipole derivatives. printcool( "Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Energies), color=4, bold=True) G, GDx, GDy, GDz = energy_derivatives(Lipid, FF, mvals, h, pgrad, len(Energies), AGrad, dipole=True) #==============================================# # Condensed phase properties and derivatives. # #==============================================# #---- # Density #---- # Build the first density derivative. GRho = mBeta * (flat(np.dot(G, col(Rhos))) / L - np.mean(Rhos) * np.mean(G, axis=1)) # Print out the density and its derivative. Sep = printcool("Density: % .4f +- % .4f kg/m^3\nAnalytic Derivative:" % (Rho_avg, Rho_err)) FF.print_map(vals=GRho) logger.info(Sep) def calc_rho(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'r_' in kwargs: r_ = kwargs['r_'] return bzavg(r_, b) # No need to calculate error using bootstrap, but here it is anyway # Rhoboot = [] # for i in range(numboots): # boot = np.random.randint(N,size=N) # Rhoboot.append(calc_rho(None,**{'r_':Rhos[boot]})) # Rhoboot = np.array(Rhoboot) # Rho_err = np.std(Rhoboot) if FDCheck: Sep = printcool("Numerical Derivative:") GRho1 = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_rho, {'r_': Rhos}) FF.print_map(vals=GRho1) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GRho, GRho1) ] FF.print_map(vals=absfrac) #---- # Enthalpy of vaporization. Removed. #---- H = Energies + pV V = np.array(Volumes) # Define some things to make the analytic derivatives easier. Gbar = np.mean(G, axis=1) def deprod(vec): return flat(np.dot(G, col(vec))) / L def covde(vec): return flat(np.dot(G, col(vec))) / L - Gbar * np.mean(vec) def avg(vec): return np.mean(vec) #---- # Thermal expansion coefficient #---- def calc_alpha(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] if 'v_' in kwargs: v_ = kwargs['v_'] return 1 / (kT * T) * (bzavg(h_ * v_, b) - bzavg(h_, b) * bzavg(v_, b)) / bzavg(v_, b) Alpha = calc_alpha(None, **{'h_': H, 'v_': V}) Alphaboot = [] numboots = 1000 for i in range(numboots): boot = np.random.randint(L, size=L) Alphaboot.append(calc_alpha(None, **{'h_': H[boot], 'v_': V[boot]})) Alphaboot = np.array(Alphaboot) Alpha_err = np.std(Alphaboot) * max([ np.sqrt(statisticalInefficiency(V)), np.sqrt(statisticalInefficiency(H)) ]) # Thermal expansion coefficient analytic derivative GAlpha1 = -1 * Beta * deprod(H * V) * avg(V) / avg(V)**2 GAlpha2 = +1 * Beta * avg(H * V) * deprod(V) / avg(V)**2 GAlpha3 = deprod(V) / avg(V) - Gbar GAlpha4 = Beta * covde(H) GAlpha = (GAlpha1 + GAlpha2 + GAlpha3 + GAlpha4) / (kT * T) Sep = printcool( "Thermal expansion coefficient: % .4e +- %.4e K^-1\nAnalytic Derivative:" % (Alpha, Alpha_err)) FF.print_map(vals=GAlpha) if FDCheck: GAlpha_fd = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_alpha, { 'h_': H, 'v_': V }) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GAlpha_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GAlpha, GAlpha_fd) ] FF.print_map(vals=absfrac) #---- # Isothermal compressibility #---- def calc_kappa(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'v_' in kwargs: v_ = kwargs['v_'] return bar_unit / kT * (bzavg(v_**2, b) - bzavg(v_, b)**2) / bzavg( v_, b) Kappa = calc_kappa(None, **{'v_': V}) Kappaboot = [] for i in range(numboots): boot = np.random.randint(L, size=L) Kappaboot.append(calc_kappa(None, **{'v_': V[boot]})) Kappaboot = np.array(Kappaboot) Kappa_err = np.std(Kappaboot) * np.sqrt(statisticalInefficiency(V)) # Isothermal compressibility analytic derivative Sep = printcool( "Isothermal compressibility: % .4e +- %.4e bar^-1\nAnalytic Derivative:" % (Kappa, Kappa_err)) GKappa1 = +1 * Beta**2 * avg(V**2) * deprod(V) / avg(V)**2 GKappa2 = -1 * Beta**2 * avg(V) * deprod(V**2) / avg(V)**2 GKappa3 = +1 * Beta**2 * covde(V) GKappa = bar_unit * (GKappa1 + GKappa2 + GKappa3) FF.print_map(vals=GKappa) if FDCheck: GKappa_fd = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_kappa, {'v_': V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GKappa_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GKappa, GKappa_fd) ] FF.print_map(vals=absfrac) #---- # Isobaric heat capacity #---- def calc_cp(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] Cp_ = 1 / (NMol * kT * T) * (bzavg(h_**2, b) - bzavg(h_, b)**2) Cp_ *= 1000 / 4.184 return Cp_ Cp = calc_cp(None, **{'h_': H}) Cpboot = [] for i in range(numboots): boot = np.random.randint(L, size=L) Cpboot.append(calc_cp(None, **{'h_': H[boot]})) Cpboot = np.array(Cpboot) Cp_err = np.std(Cpboot) * np.sqrt(statisticalInefficiency(H)) # Isobaric heat capacity analytic derivative GCp1 = 2 * covde(H) * 1000 / 4.184 / (NMol * kT * T) GCp2 = mBeta * covde(H**2) * 1000 / 4.184 / (NMol * kT * T) GCp3 = 2 * Beta * avg(H) * covde(H) * 1000 / 4.184 / (NMol * kT * T) GCp = GCp1 + GCp2 + GCp3 Sep = printcool( "Isobaric heat capacity: % .4e +- %.4e cal mol-1 K-1\nAnalytic Derivative:" % (Cp, Cp_err)) FF.print_map(vals=GCp) if FDCheck: GCp_fd = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_cp, {'h_': H}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GCp_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GCp, GCp_fd) ] FF.print_map(vals=absfrac) #---- # Dielectric constant #---- def calc_eps0(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'd_' in kwargs: # Dipole moment vector. d_ = kwargs['d_'] if 'v_' in kwargs: # Volume. v_ = kwargs['v_'] b0 = np.ones(L, dtype=float) dx = d_[:, 0] dy = d_[:, 1] dz = d_[:, 2] D2 = bzavg(dx**2, b) - bzavg(dx, b)**2 D2 += bzavg(dy**2, b) - bzavg(dy, b)**2 D2 += bzavg(dz**2, b) - bzavg(dz, b)**2 return prefactor * D2 / bzavg(v_, b) / T Eps0 = calc_eps0(None, **{'d_': Dips, 'v_': V}) Eps0boot = [] for i in range(numboots): boot = np.random.randint(L, size=L) Eps0boot.append(calc_eps0(None, **{'d_': Dips[boot], 'v_': V[boot]})) Eps0boot = np.array(Eps0boot) Eps0_err = np.std(Eps0boot) * np.sqrt( np.mean([ statisticalInefficiency(Dips[:, 0]), statisticalInefficiency(Dips[:, 1]), statisticalInefficiency(Dips[:, 2]) ])) # Dielectric constant analytic derivative Dx = Dips[:, 0] Dy = Dips[:, 1] Dz = Dips[:, 2] D2 = avg(Dx**2) + avg(Dy**2) + avg( Dz**2) - avg(Dx)**2 - avg(Dy)**2 - avg(Dz)**2 GD2 = 2 * (flat(np.dot(GDx, col(Dx))) / L - avg(Dx) * (np.mean(GDx, axis=1))) - Beta * (covde(Dx**2) - 2 * avg(Dx) * covde(Dx)) GD2 += 2 * (flat(np.dot(GDy, col(Dy))) / L - avg(Dy) * (np.mean(GDy, axis=1))) - Beta * (covde(Dy**2) - 2 * avg(Dy) * covde(Dy)) GD2 += 2 * (flat(np.dot(GDz, col(Dz))) / L - avg(Dz) * (np.mean(GDz, axis=1))) - Beta * (covde(Dz**2) - 2 * avg(Dz) * covde(Dz)) GEps0 = prefactor * (GD2 / avg(V) - mBeta * covde(V) * D2 / avg(V)**2) / T Sep = printcool( "Dielectric constant: % .4e +- %.4e\nAnalytic Derivative:" % (Eps0, Eps0_err)) FF.print_map(vals=GEps0) if FDCheck: GEps0_fd = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_eps0, { 'd_': Dips, 'v_': V }) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GEps0_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GEps0, GEps0_fd) ] FF.print_map(vals=absfrac) #---- # Average area per lipid #---- Al_avg, Al_err = mean_stderr(Als) # Build the first A_l derivative. GAl = mBeta * (flat(np.dot(G, col(Als))) / L - np.mean(Als) * np.mean(G, axis=1)) # Print out A_l and its derivative. Sep = printcool( "Average Area per Lipid: % .4f +- % .4f nm^2\nAnalytic Derivative:" % (Al_avg, Al_err)) FF.print_map(vals=GAl) logger.info(Sep) def calc_al(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'a_' in kwargs: a_ = kwargs['a_'] return bzavg(a_, b) # calc_al(None, **{'a_': Als}) #---- # Bilayer Isothermal compressibility #---- kbT = 1.3806488e-23 * T def calc_lkappa(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 'a_' in kwargs: a_ = kwargs['a_'] al_var = bzavg(a_**2, b) - bzavg(a_, b)**2 # Avoid dividing by zero if A_L time series is too short. if abs(al_var) > 0: return (1e3 * 2 * kbT / 128) * (bzavg(a_, b) / al_var) else: return 0 * bzavg(a_, b) # Convert Als time series from nm^2 to m^2 Als_m2 = Als * 1e-18 LKappa = calc_lkappa(None, **{'a_': Als_m2}) al_avg = avg(Als_m2) al_sq_avg = avg(Als_m2**2) al_avg_sq = al_avg**2 al_var = al_sq_avg - al_avg_sq LKappaboot = [] for i in range(numboots): boot = np.random.randint(L, size=L) LKappaboot.append(calc_lkappa(None, **{'a_': Als_m2[boot]})) LKappaboot = np.array(LKappaboot) LKappa_err = np.std(LKappaboot) * np.sqrt(statisticalInefficiency(Als_m2)) # Bilayer Isothermal compressibility analytic derivative Sep = printcool( "Lipid Isothermal compressibility: % .4e +- %.4e N/nm^-1\nAnalytic Derivative:" % (LKappa, LKappa_err)) GLKappa1 = covde(Als_m2) / al_var GLKappa2 = (al_avg / al_var**2) * (covde(Als_m2**2) - (2 * al_avg * covde(Als_m2))) GLKappa = (1e3 * 2 * kbT / 128) * (GLKappa1 - GLKappa2) FF.print_map(vals=GLKappa) if FDCheck: GLKappa_fd = property_derivatives(Lipid, FF, mvals, h, pgrad, kT, calc_lkappa, {'a_': Als_m2}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GLKappa_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = [ "% .4e % .4e" % (i - j, (i - j) / j) for i, j in zip(GLKappa, GLKappa_fd) ] FF.print_map(vals=absfrac) #---- # Deuterium Order Parameter #---- Scd_avg, Scd_e = mean_stderr(Scds) Scd_err = flat(Scd_e) # In case I did the conversion incorrectly, this is the code that was here previously: # GScd = mBeta * (((np.mat(G) * Scds) / L) - (np.mat(np.average(G, axis = 1)).T * np.average(Scds, axis = 0))) GScd = mBeta * ( ((np.dot(G, Scds)) / L) - np.dot(col(np.average(G, axis=1)), row(np.average(Scds, axis=0)))) # Print out S_cd and its derivative. scd_avgerr = ' '.join('%.4f +- %.4f \n' % F for F in zip(Scd_avg, Scd_err)) Sep = printcool("Deuterium order parameter: %s \nAnalytic Derivative:" % scd_avgerr) FF.print_map(vals=GScd) logger.info(Sep) def calc_scd(b=None, **kwargs): if b is None: b = np.ones(L, dtype=float) if 's_' in kwargs: s_ = kwargs['s_'] return bzavg(s_, b) # calc_scd(None, **{'s_': Scds}) logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump((Rhos, Volumes, Potentials, Energies, Dips, G, [GDx, GDy, GDz], Rho_err, Alpha_err, Kappa_err, Cp_err, Eps0_err, NMol, Als, Al_err, Scds, Scd_err, LKappa_err), 'npt_result.p')
def main(): """ Usage: (runcuda.sh) npt.py <openmm|gromacs|tinker|amber> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature> <pressure> This program is meant to be called automatically by ForceBalance on a GPU cluster (specifically, subroutines in openmmio.py). It is not easy to use manually. This is because the force field is read in from a ForceBalance 'FF' class. I wrote this program because automatic fitting of the density (or other equilibrium properties) is computationally intensive, and the calculations need to be distributed to the queue. The main instance of ForceBalance (running on my workstation) queues up a bunch of these jobs (using Work Queue). Then, I submit a bunch of workers to GPU clusters (e.g. Certainty, Keeneland). The worker scripts connect to. the main instance and receives one of these jobs. This script can also be executed locally, if you want to (e.g. for debugging). Just make sure you have the pickled 'forcebalance.p' file. """ printcool("ForceBalance condensed phase simulation using engine: %s" % engname.upper(), color=4, bold=True) #---- # Load the ForceBalance pickle file which contains: #---- # - Force field object # - Optimization parameters # - Options from the Target object that launched this simulation # - Switch for whether to evaluate analytic derivatives. FF,mvals,TgtOptions,AGrad = lp_load('forcebalance.p') FF.ffdir = '.' # Write the force field file. FF.make(mvals) #---- # Load the options that are set in the ForceBalance input file. #---- # Finite difference step size h = TgtOptions['h'] pgrad = TgtOptions['pgrad'] # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps) liquid_timestep = TgtOptions['liquid_timestep'] liquid_nsteps = TgtOptions['liquid_md_steps'] liquid_nequil = TgtOptions['liquid_eq_steps'] liquid_intvl = TgtOptions['liquid_interval'] liquid_fnm = TgtOptions['liquid_coords'] gas_timestep = TgtOptions['gas_timestep'] gas_nsteps = TgtOptions['gas_md_steps'] gas_nequil = TgtOptions['gas_eq_steps'] gas_intvl = TgtOptions['gas_interval'] gas_fnm = TgtOptions['gas_coords'] # Number of threads, multiple timestep integrator, anisotropic box etc. threads = TgtOptions.get('md_threads', 1) mts = TgtOptions.get('mts_integrator', 0) rpmd_beads = TgtOptions.get('rpmd_beads', 0) force_cuda = TgtOptions.get('force_cuda', 0) nbarostat = TgtOptions.get('n_mcbarostat', 25) anisotropic = TgtOptions.get('anisotropic_box', 0) minimize = TgtOptions.get('minimize_energy', 1) # Print all options. printcool_dictionary(TgtOptions, title="Options from ForceBalance") liquid_snapshots = int((liquid_nsteps * liquid_timestep / 1000) / liquid_intvl) liquid_iframes = int(1000 * liquid_intvl / liquid_timestep) gas_snapshots = int((gas_nsteps * gas_timestep / 1000) / gas_intvl) gas_iframes = int(1000 * gas_intvl / gas_timestep) logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (liquid_snapshots, liquid_iframes, liquid_timestep)) if liquid_snapshots < 2: raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * int(liquid_intvl/liquid_timestep))) logger.info("For the gas phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (gas_snapshots, gas_iframes, gas_timestep)) if gas_snapshots < 2: raise Exception('Please set the number of gas time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * int(gas_intvl/gas_timestep))) #---- # Loading coordinates #---- ML = Molecule(liquid_fnm, toppbc=True) MG = Molecule(gas_fnm) # Determine the number of molecules in the condensed phase coordinate file. NMol = TgtOptions['n_molecules'] logger.info("There are %i molecules in the liquid\n" % (NMol)) #---- # Setting up MD simulations #---- EngOpts = OrderedDict() EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML), ("pbc", True)]) if "nonbonded_cutoff" in TgtOptions: EngOpts["liquid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"] if "vdw_cutoff" in TgtOptions: EngOpts["liquid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"] EngOpts["gas"] = OrderedDict([("coords", gas_fnm), ("mol", MG), ("pbc", False)]) GenOpts = OrderedDict([('FF', FF)]) if engname in ["openmm", "smirnoff"]: # OpenMM-specific options EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA') # For now, always run gas phase calculations on the reference platform EngOpts["gas"]["platname"] = 'Reference' if force_cuda: try: Platform.getPlatformByName('CUDA') except: raise RuntimeError('Forcing failure because CUDA platform unavailable') EngOpts["liquid"]["platname"] = 'CUDA' if threads > 1: logger.warn("Setting the number of threads will have no effect on OpenMM engine.\n") if engname == "smirnoff": if not TgtOptions['liquid_coords'].endswith('.pdb'): logger.error("With SMIRNOFF engine, please pass a .pdb file to liquid_coords.") raise RuntimeError EngOpts["liquid"]["pdb"] = TgtOptions['liquid_coords'] EngOpts["liquid"]["mol2"] = TgtOptions["mol2"] if not TgtOptions['gas_coords'].endswith('.pdb'): logger.error("With SMIRNOFF engine, please pass a .pdb file to gas_coords.") raise RuntimeError EngOpts["gas"]["pdb"] = TgtOptions['gas_coords'] EngOpts["gas"]["mol2"] = TgtOptions["mol2"] elif engname == "gromacs": # Gromacs-specific options GenOpts["gmxpath"] = TgtOptions["gmxpath"] GenOpts["gmxsuffix"] = TgtOptions["gmxsuffix"] EngOpts["liquid"]["gmx_top"] = os.path.splitext(liquid_fnm)[0] + ".top" EngOpts["liquid"]["gmx_mdp"] = os.path.splitext(liquid_fnm)[0] + ".mdp" EngOpts["liquid"]["gmx_eq_barostat"] = TgtOptions["gmx_eq_barostat"] EngOpts["gas"]["gmx_top"] = os.path.splitext(gas_fnm)[0] + ".top" EngOpts["gas"]["gmx_mdp"] = os.path.splitext(gas_fnm)[0] + ".mdp" if force_cuda: logger.warn("force_cuda option has no effect on Gromacs engine.") if rpmd_beads > 0: raise RuntimeError("Gromacs cannot handle RPMD.") if mts: logger.warn("Gromacs not configured for multiple timestep integrator.") if anisotropic: logger.warn("Gromacs not configured for anisotropic box scaling.") elif engname == "tinker": # Tinker-specific options GenOpts["tinkerpath"] = TgtOptions["tinkerpath"] EngOpts["liquid"]["tinker_key"] = os.path.splitext(liquid_fnm)[0] + ".key" EngOpts["gas"]["tinker_key"] = os.path.splitext(gas_fnm)[0] + ".key" if force_cuda: logger.warn("force_cuda option has no effect on Tinker engine.") if rpmd_beads > 0: raise RuntimeError("TINKER cannot handle RPMD.") if mts: logger.warn("Tinker not configured for multiple timestep integrator.") elif engname == "amber": # AMBER-specific options GenOpts["amberhome"] = TgtOptions["amberhome"] if os.path.exists(os.path.splitext(liquid_fnm)[0] + ".mdin"): EngOpts["liquid"]["mdin"] = os.path.splitext(liquid_fnm)[0] + ".mdin" if os.path.exists(os.path.splitext(gas_fnm)[0] + ".mdin"): EngOpts["gas"]["mdin"] = os.path.splitext(gas_fnm)[0] + ".mdin" EngOpts["liquid"]["leapcmd"] = os.path.splitext(liquid_fnm)[0] + ".leap" EngOpts["gas"]["leapcmd"] = os.path.splitext(gas_fnm)[0] + ".leap" EngOpts["liquid"]["pdb"] = liquid_fnm EngOpts["gas"]["pdb"] = gas_fnm if force_cuda: logger.warn("force_cuda option has no effect on Amber engine.") if rpmd_beads > 0: raise RuntimeError("AMBER cannot handle RPMD.") if mts: logger.warn("Amber not configured for multiple timestep integrator.") EngOpts["liquid"].update(GenOpts) EngOpts["gas"].update(GenOpts) for i in EngOpts: printcool_dictionary(EngOpts[i], "Engine options for %s" % i) # Set up MD options MDOpts = OrderedDict() MDOpts["liquid"] = OrderedDict([("nsteps", liquid_nsteps), ("timestep", liquid_timestep), ("temperature", temperature), ("pressure", pressure), ("nequil", liquid_nequil), ("minimize", minimize), ("nsave", int(1000 * liquid_intvl / liquid_timestep)), ("verbose", True), ('save_traj', TgtOptions['save_traj']), ("threads", threads), ("anisotropic", anisotropic), ("nbarostat", nbarostat), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) MDOpts["gas"] = OrderedDict([("nsteps", gas_nsteps), ("timestep", gas_timestep), ("temperature", temperature), ("nsave", int(1000 * gas_intvl / gas_timestep)), ("nequil", gas_nequil), ("minimize", minimize), ("threads", 1), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) # Energy components analysis disabled for OpenMM MTS because it uses force groups if (engname == "openmm" and mts): logger.warn("OpenMM with MTS integrator; energy components analysis will be disabled.\n") # Create instances of the MD Engine objects. Liquid = Engine(name="liquid", **EngOpts["liquid"]) Gas = Engine(name="gas", **EngOpts["gas"]) #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# printcool("Condensed phase molecular dynamics", color=4, bold=True) # This line runs the condensed phase simulation. click() prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"]) if hasattr(Liquid, 'freeze_atoms'): logger.info("Warning: freeze_atoms may result in incorrect system mass and incorrect density calculation\n") logger.info("Liquid phase MD simulation took %.3f seconds\n" % click()) Rhos = prop_return['Rhos'] Potentials = prop_return['Potentials'] Kinetics = prop_return['Kinetics'] Volumes = prop_return['Volumes'] Dips = prop_return['Dips'] EDA = prop_return['Ecomps'] # Create a bunch of physical constants. # Energies are in kJ/mol # Lengths are in nanometers. L = len(Rhos) kB = 0.008314472471220214 T = temperature kT = kB * T mBeta = -1.0 / kT Beta = 1.0 / kT atm_unit = 0.061019351687175 bar_unit = 0.060221417930000 # This is how I calculated the prefactor for the dielectric constant. # eps0 = 8.854187817620e-12 * coulomb**2 / newton / meter**2 # epsunit = 1.0*(debye**2) / nanometer**3 / BOLTZMANN_CONSTANT_kB / kelvin # prefactor = epsunit/eps0/3 prefactor = 30.348705333964077 # Gather some physical variables. Energies = Potentials + Kinetics Ene_avg, Ene_err = mean_stderr(Energies) pV = atm_unit * pressure * Volumes pV_avg, pV_err = mean_stderr(pV) Rho_avg, Rho_err = mean_stderr(Rhos) PrintEDA(EDA, NMol) #==============================================# # Now run the simulation for just the monomer. # #==============================================# # Run the OpenMM simulation, gather information. printcool("Gas phase molecular dynamics", color=4, bold=True) click() mprop_return = Gas.molecular_dynamics(**MDOpts["gas"]) logger.info("Gas phase MD simulation took %.3f seconds\n" % click()) mPotentials = mprop_return['Potentials'] mKinetics = mprop_return['Kinetics'] mEDA = mprop_return['Ecomps'] mEnergies = mPotentials + mKinetics mEne_avg, mEne_err = mean_stderr(mEnergies) PrintEDA(mEDA, 1) #============================================# # Compute the potential energy derivatives. # #============================================# logger.info("Calculating potential energy derivatives with finite difference step size: %f\n" % h) # Switch for whether to compute the derivatives two different ways for consistency. FDCheck = False # Create a double-precision simulation object if desired (seems unnecessary). DoublePrecisionDerivatives = False if engname == "openmm" and DoublePrecisionDerivatives and AGrad: logger.info("Creating Double Precision Simulation for parameter derivatives\n") Liquid = Engine(name="liquid", openmm_precision="double", **EngOpts["liquid"]) Gas = Engine(name="gas", openmm_precision="double", **EngOpts["gas"]) # Compute the energy and dipole derivatives. printcool("Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Energies), color=4, bold=True) click() G, GDx, GDy, GDz = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Energies), AGrad, dipole=True) logger.info("Condensed phase energy derivatives took %.3f seconds\n" % click()) click() printcool("Gas phase energy derivatives", color=4, bold=True) mG, _, __, ___ = energy_derivatives(Gas, FF, mvals, h, pgrad, len(mEnergies), AGrad, dipole=False) logger.info("Gas phase energy derivatives took %.3f seconds\n" % click()) #==============================================# # Condensed phase properties and derivatives. # #==============================================# #---- # Density #---- # Build the first density derivative. GRho = mBeta * (flat(np.dot(G, col(Rhos))) / L - np.mean(Rhos) * np.mean(G, axis=1)) # Print out the density and its derivative. Sep = printcool("Density: % .4f +- % .4f kg/m^3\nAnalytic Derivative:" % (Rho_avg, Rho_err)) FF.print_map(vals=GRho) logger.info(Sep) def calc_rho(b = None, **kwargs): if b is None: b = np.ones(L,dtype=float) if 'r_' in kwargs: r_ = kwargs['r_'] return bzavg(r_,b) # No need to calculate error using bootstrap, but here it is anyway # Rhoboot = [] # for i in range(numboots): # boot = np.random.randint(N,size=N) # Rhoboot.append(calc_rho(None,**{'r_':Rhos[boot]})) # Rhoboot = np.array(Rhoboot) # Rho_err = np.std(Rhoboot) if FDCheck: Sep = printcool("Numerical Derivative:") GRho1 = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_rho, {'r_':Rhos}) FF.print_map(vals=GRho1) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GRho, GRho1)] FF.print_map(vals=absfrac) #---- # Enthalpy of vaporization #---- H = Energies + pV V = np.array(Volumes) # Print out the liquid enthalpy. logger.info("Liquid enthalpy: % .4f kJ/mol, stdev % .4f ; (% .4f from energy, % .4f from pV)\n" % (np.mean(H), np.std(H), np.mean(Energies), np.mean(pV))) numboots = 1000 # The enthalpy of vaporization in kJ/mol. Hvap_avg = mEne_avg - Ene_avg / NMol + kT - np.mean(pV) / NMol Hvap_err = np.sqrt(Ene_err**2 / NMol**2 + mEne_err**2 + pV_err**2/NMol**2) # Build the first Hvap derivative. GHvap = np.mean(G,axis=1) GHvap += mBeta * (flat(np.dot(G, col(Energies))) / L - Ene_avg * np.mean(G, axis=1)) GHvap /= NMol GHvap -= np.mean(mG,axis=1) GHvap -= mBeta * (flat(np.dot(mG, col(mEnergies))) / L - mEne_avg * np.mean(mG, axis=1)) GHvap *= -1 GHvap -= mBeta * (flat(np.dot(G, col(pV))) / L - np.mean(pV) * np.mean(G, axis=1)) / NMol Sep = printcool("Enthalpy of Vaporization: % .4f +- %.4f kJ/mol\nAnalytic Derivative:" % (Hvap_avg, Hvap_err)) FF.print_map(vals=GHvap) # Define some things to make the analytic derivatives easier. Gbar = np.mean(G,axis=1) def deprod(vec): return flat(np.dot(G,col(vec)))/L def covde(vec): return flat(np.dot(G,col(vec)))/L - Gbar*np.mean(vec) def avg(vec): return np.mean(vec) #---- # Thermal expansion coefficient #---- def calc_alpha(b = None, **kwargs): if b is None: b = np.ones(L,dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] if 'v_' in kwargs: v_ = kwargs['v_'] return 1/(kT*T) * (bzavg(h_*v_,b)-bzavg(h_,b)*bzavg(v_,b))/bzavg(v_,b) Alpha = calc_alpha(None, **{'h_':H, 'v_':V}) Alphaboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Alphaboot.append(calc_alpha(None, **{'h_':H[boot], 'v_':V[boot]})) Alphaboot = np.array(Alphaboot) Alpha_err = np.std(Alphaboot) * max([np.sqrt(statisticalInefficiency(V)),np.sqrt(statisticalInefficiency(H))]) # Thermal expansion coefficient analytic derivative GAlpha1 = -1 * Beta * deprod(H*V) * avg(V) / avg(V)**2 GAlpha2 = +1 * Beta * avg(H*V) * deprod(V) / avg(V)**2 GAlpha3 = deprod(V)/avg(V) - Gbar GAlpha4 = Beta * covde(H) GAlpha = (GAlpha1 + GAlpha2 + GAlpha3 + GAlpha4)/(kT*T) Sep = printcool("Thermal expansion coefficient: % .4e +- %.4e K^-1\nAnalytic Derivative:" % (Alpha, Alpha_err)) FF.print_map(vals=GAlpha) if FDCheck: GAlpha_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_alpha, {'h_':H,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GAlpha_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GAlpha, GAlpha_fd)] FF.print_map(vals=absfrac) #---- # Isothermal compressibility #---- def calc_kappa(b=None, **kwargs): if b is None: b = np.ones(L,dtype=float) if 'v_' in kwargs: v_ = kwargs['v_'] return bar_unit / kT * (bzavg(v_**2,b)-bzavg(v_,b)**2)/bzavg(v_,b) Kappa = calc_kappa(None,**{'v_':V}) Kappaboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Kappaboot.append(calc_kappa(None,**{'v_':V[boot]})) Kappaboot = np.array(Kappaboot) Kappa_err = np.std(Kappaboot) * np.sqrt(statisticalInefficiency(V)) # Isothermal compressibility analytic derivative Sep = printcool("Isothermal compressibility: % .4e +- %.4e bar^-1\nAnalytic Derivative:" % (Kappa, Kappa_err)) GKappa1 = +1 * Beta**2 * avg(V**2) * deprod(V) / avg(V)**2 GKappa2 = -1 * Beta**2 * avg(V) * deprod(V**2) / avg(V)**2 GKappa3 = +1 * Beta**2 * covde(V) GKappa = bar_unit*(GKappa1 + GKappa2 + GKappa3) FF.print_map(vals=GKappa) if FDCheck: GKappa_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_kappa, {'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GKappa_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GKappa, GKappa_fd)] FF.print_map(vals=absfrac) #---- # Isobaric heat capacity #---- def calc_cp(b=None, **kwargs): if b is None: b = np.ones(L,dtype=float) if 'h_' in kwargs: h_ = kwargs['h_'] Cp_ = 1/(NMol*kT*T) * (bzavg(h_**2,b) - bzavg(h_,b)**2) Cp_ *= 1000 / 4.184 return Cp_ Cp = calc_cp(None,**{'h_':H}) Cpboot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Cpboot.append(calc_cp(None,**{'h_':H[boot]})) Cpboot = np.array(Cpboot) Cp_err = np.std(Cpboot) * np.sqrt(statisticalInefficiency(H)) # Isobaric heat capacity analytic derivative GCp1 = 2*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp2 = mBeta*covde(H**2) * 1000 / 4.184 / (NMol*kT*T) GCp3 = 2*Beta*avg(H)*covde(H) * 1000 / 4.184 / (NMol*kT*T) GCp = GCp1 + GCp2 + GCp3 Sep = printcool("Isobaric heat capacity: % .4e +- %.4e cal mol-1 K-1\nAnalytic Derivative:" % (Cp, Cp_err)) FF.print_map(vals=GCp) if FDCheck: GCp_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_cp, {'h_':H}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GCp_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GCp,GCp_fd)] FF.print_map(vals=absfrac) #---- # Dielectric constant #---- def calc_eps0(b=None, **kwargs): if b is None: b = np.ones(L,dtype=float) if 'd_' in kwargs: # Dipole moment vector. d_ = kwargs['d_'] if 'v_' in kwargs: # Volume. v_ = kwargs['v_'] b0 = np.ones(L,dtype=float) dx = d_[:,0] dy = d_[:,1] dz = d_[:,2] D2 = bzavg(dx**2,b)-bzavg(dx,b)**2 D2 += bzavg(dy**2,b)-bzavg(dy,b)**2 D2 += bzavg(dz**2,b)-bzavg(dz,b)**2 return prefactor*D2/bzavg(v_,b)/T Eps0 = calc_eps0(None,**{'d_':Dips, 'v_':V}) Eps0boot = [] for i in range(numboots): boot = np.random.randint(L,size=L) Eps0boot.append(calc_eps0(None,**{'d_':Dips[boot], 'v_':V[boot]})) Eps0boot = np.array(Eps0boot) Eps0_err = np.std(Eps0boot)*np.sqrt(np.mean([statisticalInefficiency(Dips[:,0]),statisticalInefficiency(Dips[:,1]),statisticalInefficiency(Dips[:,2])])) # Dielectric constant analytic derivative Dx = Dips[:,0] Dy = Dips[:,1] Dz = Dips[:,2] D2 = avg(Dx**2)+avg(Dy**2)+avg(Dz**2)-avg(Dx)**2-avg(Dy)**2-avg(Dz)**2 GD2 = 2*(flat(np.dot(GDx,col(Dx)))/L - avg(Dx)*(np.mean(GDx,axis=1))) - Beta*(covde(Dx**2) - 2*avg(Dx)*covde(Dx)) GD2 += 2*(flat(np.dot(GDy,col(Dy)))/L - avg(Dy)*(np.mean(GDy,axis=1))) - Beta*(covde(Dy**2) - 2*avg(Dy)*covde(Dy)) GD2 += 2*(flat(np.dot(GDz,col(Dz)))/L - avg(Dz)*(np.mean(GDz,axis=1))) - Beta*(covde(Dz**2) - 2*avg(Dz)*covde(Dz)) GEps0 = prefactor*(GD2/avg(V) - mBeta*covde(V)*D2/avg(V)**2)/T Sep = printcool("Dielectric constant: % .4e +- %.4e\nAnalytic Derivative:" % (Eps0, Eps0_err)) FF.print_map(vals=GEps0) if FDCheck: GEps0_fd = property_derivatives(Liquid, FF, mvals, h, pgrad, kT, calc_eps0, {'d_':Dips,'v_':V}) Sep = printcool("Numerical Derivative:") FF.print_map(vals=GEps0_fd) Sep = printcool("Difference (Absolute, Fractional):") absfrac = ["% .4e % .4e" % (i-j, (i-j)/j) for i,j in zip(GEps0,GEps0_fd)] FF.print_map(vals=absfrac) logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump((Rhos, Volumes, Potentials, Energies, Dips, G, [GDx, GDy, GDz], mPotentials, mEnergies, mG, Rho_err, Hvap_err, Alpha_err, Kappa_err, Cp_err, Eps0_err, NMol),'npt_result.p')
def main(): """ Usage: (runcuda.sh) nvt.py <openmm|gromacs|tinker> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature> This program is meant to be called automatically by ForceBalance on a GPU cluster (specifically, subroutines in openmmio.py). It is not easy to use manually. This is because the force field is read in from a ForceBalance 'FF' class. """ printcool("ForceBalance condensed phase NVT simulation using engine: %s" % engname.upper(), color=4, bold=True) #---- # Load the ForceBalance pickle file which contains: #---- # - Force field object # - Optimization parameters # - Options from the Target object that launched this simulation # - Switch for whether to evaluate analytic derivatives. FF,mvals,TgtOptions,AGrad = lp_load('forcebalance.p') FF.ffdir = '.' # Write the force field file. FF.make(mvals) #---- # Load the options that are set in the ForceBalance input file. #---- # Finite difference step size h = TgtOptions['h'] pgrad = TgtOptions['pgrad'] # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps) nvt_timestep = TgtOptions['nvt_timestep'] nvt_md_steps = TgtOptions['nvt_md_steps'] nvt_eq_steps = TgtOptions['nvt_eq_steps'] nvt_interval = TgtOptions['nvt_interval'] liquid_fnm = TgtOptions['nvt_coords'] # Number of threads, multiple timestep integrator, anisotropic box etc. threads = TgtOptions.get('md_threads', 1) mts = TgtOptions.get('mts_integrator', 0) rpmd_beads = TgtOptions.get('rpmd_beads', 0) force_cuda = TgtOptions.get('force_cuda', 0) nbarostat = TgtOptions.get('n_mcbarostat', 25) anisotropic = TgtOptions.get('anisotropic_box', 0) minimize = TgtOptions.get('minimize_energy', 1) # Print all options. printcool_dictionary(TgtOptions, title="Options from ForceBalance") nvt_snapshots = (nvt_timestep * nvt_md_steps / 1000) / nvt_interval nvt_iframes = 1000 * nvt_interval / nvt_timestep logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (nvt_snapshots, nvt_iframes, nvt_timestep)) if nvt_snapshots < 2: raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * (nvt_interval/nvt_timestep))) #---- # Loading coordinates #---- ML = Molecule(liquid_fnm, toppbc=True) # Determine the number of molecules in the condensed phase coordinate file. NMol = len(ML.molecules) TgtOptions['n_molecules'] = NMol logger.info("There are %i molecules in the liquid\n" % (NMol)) #---- # Setting up MD simulations #---- EngOpts = OrderedDict() EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML), ("pbc", True)]) if "nonbonded_cutoff" in TgtOptions: EngOpts["liquid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"] else: largest_available_cutoff = min(ML.boxes[0][:3]) / 2 - 0.1 EngOpts["liquid"]["nonbonded_cutoff"] = largest_available_cutoff logger.info("nonbonded_cutoff is by default set to the largest available value: %g Angstrom" %largest_available_cutoff) if "vdw_cutoff" in TgtOptions: EngOpts["liquid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"] # Hard Code nonbonded_cutoff to 13A for test #EngOpts["liquid"]["nonbonded_cutoff"] = EngOpts["liquid"]["vdw_cutoff"] = 13.0 GenOpts = OrderedDict([('FF', FF)]) if engname == "openmm": # OpenMM-specific options EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA') if force_cuda: try: Platform.getPlatformByName('CUDA') except: raise RuntimeError('Forcing failure because CUDA platform unavailable') EngOpts["liquid"]["platname"] = 'CUDA' if threads > 1: logger.warn("Setting the number of threads will have no effect on OpenMM engine.\n") EngOpts["liquid"].update(GenOpts) for i in EngOpts: printcool_dictionary(EngOpts[i], "Engine options for %s" % i) # Set up MD options MDOpts = OrderedDict() MDOpts["liquid"] = OrderedDict([("nsteps", nvt_md_steps), ("timestep", nvt_timestep), ("temperature", temperature), ("nequil", nvt_eq_steps), ("minimize", minimize), ("nsave", int(1000 * nvt_interval / nvt_timestep)), ("verbose", True), ('save_traj', TgtOptions['save_traj']), ("threads", threads), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) # Energy components analysis disabled for OpenMM MTS because it uses force groups if (engname == "openmm" and mts): logger.warn("OpenMM with MTS integrator; energy components analysis will be disabled.\n") # Create instances of the MD Engine objects. Liquid = Engine(name="liquid", **EngOpts["liquid"]) #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# printcool("Condensed phase NVT molecular dynamics", color=4, bold=True) click() prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"]) logger.info("Liquid phase MD simulation took %.3f seconds\n" % click()) Potentials = prop_return['Potentials'] #============================================# # Compute the potential energy derivatives. # #============================================# if AGrad: logger.info("Calculating potential energy derivatives with finite difference step size: %f\n" % h) # Switch for whether to compute the derivatives two different ways for consistency. FDCheck = False printcool("Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Potentials), color=4, bold=True) click() G, GDx, GDy, GDz = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info("Condensed phase energy derivatives took %.3f seconds\n" % click()) #==============================================# # Condensed phase properties and derivatives. # #==============================================# # Physical constants kB = 0.008314472471220214 T = temperature kT = kB * T # Unit: kJ/mol #--- Surface Tension ---- logger.info("Start Computing surface tension.\n") perturb_proportion = 0.0005 box_vectors = np.array(Liquid.xyz_omms[0][1]/nanometer) # obtain original box vectors from first frame delta_S = np.sqrt(np.sum(np.cross(box_vectors[0], box_vectors[1])**2)) * perturb_proportion * 2 # unit: nm^2. *2 for 2 surfaces # perturb xy area + click() scale_x = scale_y = np.sqrt(1 + perturb_proportion) scale_z = 1.0 / (1+perturb_proportion) # keep the box volumn while changing the area of xy plane Liquid.scale_box(scale_x, scale_y, scale_z) logger.info("scale_box+ took %.3f seconds\n" %click()) # Obtain energies and gradients Potentials_plus = Liquid.energy() logger.info("Calculation of energies for perturbed box+ took %.3f seconds\n" %click()) if AGrad: G_plus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info("Calculation of energy gradients for perturbed box+ took %.3f seconds\n" %click()) # perturb xy area - ( Note: also need to cancel the previous scaling) scale_x = scale_y = np.sqrt(1 - perturb_proportion) * (1.0/scale_x) scale_z = 1.0 / (1-perturb_proportion) * (1.0/scale_z) Liquid.scale_box(scale_x, scale_y, scale_z) logger.info("scale_box- took %.3f seconds\n" %click()) # Obtain energies and gradients Potentials_minus = Liquid.energy() logger.info("Calculation of energies for perturbed box- took %.3f seconds\n" %click()) if AGrad: G_minus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info("Calculation of energy gradients for perturbed box- took %.3f seconds\n" %click()) # Compute surface tension dE_plus = Potentials_plus - Potentials # Unit: kJ/mol dE_minus = Potentials_minus - Potentials # Unit: kJ/mol prefactor = -0.5 * kT / delta_S / 6.0221409e-1 # Unit mJ m^-2 # Following equation: γ = -kT/(2ΔS) * [ ln<exp(-ΔE+/kT)> - ln<exp(-ΔE-/kT)> ] #plus_avg, plus_err = mean_stderr(np.exp(-dE_plus/kT)) #minus_avg, minus_err = mean_stderr(np.exp(-dE_minus/kT)) #surf_ten = -0.5 * kT / delta_S * ( np.log(plus_avg) - np.log(minus_avg) ) / 6.0221409e-1 # convert to mJ m^-2 #surf_ten_err = 0.5 * kT / delta_S * ( np.log(plus_avg+plus_err) - np.log(plus_avg-plus_err) + np.log(minus_avg+minus_err) - np.log(minus_avg-minus_err) ) / 6.0221409e-1 exp_dE_plus = np.exp(-dE_plus/kT) exp_dE_minus = np.exp(-dE_minus/kT) surf_ten = prefactor * ( np.log(np.mean(exp_dE_plus)) - np.log(np.mean(exp_dE_minus)) ) # Use bootstrap method to estimate the error num_frames = len(exp_dE_plus) numboots = 1000 surf_ten_boots = np.zeros(numboots) for i in xrange(numboots): boots_ordering = np.random.randint(num_frames, size=num_frames) boots_exp_dE_plus = np.take(exp_dE_plus, boots_ordering) boots_exp_dE_minus = np.take(exp_dE_minus, boots_ordering) surf_ten_boots[i] = prefactor * ( np.log(np.mean(boots_exp_dE_plus)) - np.log(np.mean(boots_exp_dE_minus)) ) surf_ten_err = np.std(surf_ten_boots) * np.sqrt(np.mean([statisticalInefficiency(exp_dE_plus), statisticalInefficiency(exp_dE_minus)])) printcool("Surface Tension: % .4f +- %.4f mJ m^-2" % (surf_ten, surf_ten_err)) # Analytic Gradient of surface tension # Formula: β = 1/kT # ∂γ/∂α = -kT/(2ΔS) * { 1/<exp(-βΔE+)> * [<-β ∂E+/∂α exp(-βΔE+)> - <-β ∂E/∂α><exp(-βΔE+)>] # -1/<exp(-βΔE-)> * [<-β ∂E-/∂α exp(-βΔE-)> - <-β ∂E/∂α><exp(-βΔE-)>] } n_params = len(mvals) G_surf_ten = np.zeros(n_params) if AGrad: beta = 1.0 / kT plus_denom = np.mean(np.exp(-beta*dE_plus)) minus_denom = np.mean(np.exp(-beta*dE_minus)) for param_i in xrange(n_params): plus_left = np.mean(-beta * G_plus[param_i] * np.exp(-beta*dE_plus)) plus_right = np.mean(-beta * G[param_i]) * plus_denom minus_left = np.mean(-beta * G_minus[param_i] * np.exp(-beta*dE_minus)) minus_right = np.mean(-beta * G[param_i]) * minus_denom G_surf_ten[param_i] = prefactor * (1.0/plus_denom*(plus_left-plus_right) - 1.0/minus_denom*(minus_left-minus_right)) printcool("Analytic Derivatives:") FF.print_map(vals=G_surf_ten) logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all results to disk.\n") result_dict = {'surf_ten': surf_ten, 'surf_ten_err': surf_ten_err, 'G_surf_ten': G_surf_ten} lp_dump(result_dict, 'nvt_result.p')
def rpmd_energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole_rpmd() else: return engine.energy_rpmd()
def energy_derivatives_TINKER(FF, mvals, h, pgrad, length, AGrad=True): # find the prm file name lines = file("liquid.key").readlines() for line in lines: if "PARAMETERS" in line.upper(): prmprefix = line.split()[1].split(".prm")[0] #initialize the energy gradient and dipole gradient array G = np.zeros((FF.np,length)) GDx = np.zeros((FF.np,length)) GDy = np.zeros((FF.np,length)) GDz = np.zeros((FF.np,length)) if not AGrad: return G, GDx, GDy, GDz #Actually tinkerpath is an argument in FB tinkerpath = "$TINKERPATH" #Record key file except for the first line lines = file("liquid-md.key").readlines()[1:] ofile = open("runAna_m.sh","w") ofil1 = open("runAna_p.sh","w") #backup the current water.prm os.rename(prmprefix +".prm", prmprefix + ".prm.org") for i in pgrad: #minus and plus prmfile1 = open("liquid_%02d_m.key"%i, 'w') prmfile2 = open("liquid_%02d_p.key"%i, 'w') prmfile1.write("parameters ./%s_%02d_m.prm\n"%(prmprefix,i)) prmfile2.write("parameters ./%s_%02d_p.prm\n"%(prmprefix,i)) for line in lines: prmfile1.write(line) prmfile2.write(line) prmfile1.close() prmfile2.close() mvals_= mvals mvals_[i] += -abs(h) FF.make(mvals_) os.rename(prmprefix + ".prm", prmprefix + "_%02d_m.prm"%i) mvals_[i] += abs(h)*2.0 FF.make(mvals_) os.rename(prmprefix + ".prm", prmprefix + "_%02d_p.prm"%i) mvals_[i] += -abs(h) if i == pgrad[-1]: cmdstr1 = tinkerpath+"/analyze ./liquid-md.arc -k ./liquid_%02d_m.key G,E,M > liquid_%02d_m.out \n"%(i,i) cmdstr2 = tinkerpath+"/analyze ./liquid-md.arc -k ./liquid_%02d_p.key G,E,M > liquid_%02d_p.out \n"%(i,i) else: cmdstr1 = tinkerpath+"/analyze ./liquid-md.arc -k ./liquid_%02d_m.key G,E,M > liquid_%02d_m.out &\n"%(i,i) cmdstr2 = tinkerpath+"/analyze ./liquid-md.arc -k ./liquid_%02d_p.key G,E,M > liquid_%02d_p.out &\n"%(i,i) ofile.write(cmdstr1) ofil1.write(cmdstr2) ofile.close() ofil1.close() #wait 5sec, for safe time.sleep(5.0) os.rename(prmprefix + ".prm.org", prmprefix + ".prm") #use 1 core for each analyze job cmdstr3 = "sed 's/OPENMP-THREADS 12/OPENMP-THREADS 1/g' -i ./liquid_??_?.key" os.system(cmdstr3) cmdstr3 = "sed 's/openmp-threads 12/openmp-threads 1/g' -i ./liquid_??_?.key" os.system(cmdstr3) os.system("sh runAna_m.sh") os.system("sh runAna_p.sh") #Check whether all analyze jobs finished! readFlag = 0 while readFlag==0: cmdstr1 = "grep 'Dipole X,Y,Z-Components :' liquid_*_m.out >dipole_m.dat" cmdstr2 = "grep 'Dipole X,Y,Z-Components :' liquid_*_p.out >dipole_p.dat" os.system(cmdstr1) os.system(cmdstr2) nLinesDm = sum(1 for line in open("dipole_m.dat")) nLinesDp = sum(1 for line in open("dipole_p.dat")) if ((nLinesDm==length*len(pgrad)) and (nLinesDp==length*len(pgrad))): readFlag = 1 break else: print("Some analyze jobs are still running! I will sleep for 30 seconds !\r") time.sleep(30.0) #If all jobs finished, calculate numerical gradients if readFlag==1: for i in pgrad: eanl_m = [] eanl_p = [] dip_px = [] dip_py = [] dip_pz = [] dip_mx = [] dip_my = [] dip_mz = [] lines = file("liquid_%02d_m.out"%i).readlines() for line in lines: s = line.split() if 'Total Potential Energy : ' in line: eanl_m.append(float(s[4]) * 4.184) if 'Dipole X,Y,Z-Components :' in line: dip_mx.append(float(s[-3])) dip_my.append(float(s[-2])) dip_mz.append(float(s[-1])) lines = file("liquid_%02d_p.out"%i).readlines() for line in lines: s = line.split() if 'Total Potential Energy : ' in line: eanl_p.append(float(s[4]) * 4.184) if 'Dipole X,Y,Z-Components :' in line: dip_px.append(float(s[-3])) dip_py.append(float(s[-2])) dip_pz.append(float(s[-1])) #use np.array here eanl_p = np.array(eanl_p) eanl_m = np.array(eanl_m) dip_px = np.array(dip_px) dip_py = np.array(dip_py) dip_pz = np.array(dip_pz) dip_mx = np.array(dip_mx) dip_my = np.array(dip_my) dip_mz = np.array(dip_mz) #2-sides numerical grad. G[i,:] = (eanl_p - eanl_m)/(2*h) GDx[i,:] = (dip_px - dip_mx)/(2*h) GDy[i,:] = (dip_py - dip_my)/(2*h) GDz[i,:] = (dip_pz - dip_mz)/(2*h) return G, GDx, GDy, GDz
def __init__(self): self.Mao = 0 self.root = os.getcwd() options, tgt_opts = parse_inputs(input_file) self.forcefield = FF(options)
def main(): """ Usage: (runcuda.sh) nvt.py <openmm|gromacs|tinker> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature> This program is meant to be called automatically by ForceBalance on a GPU cluster (specifically, subroutines in openmmio.py). It is not easy to use manually. This is because the force field is read in from a ForceBalance 'FF' class. """ printcool("ForceBalance condensed phase NVT simulation using engine: %s" % engname.upper(), color=4, bold=True) #---- # Load the ForceBalance pickle file which contains: #---- # - Force field object # - Optimization parameters # - Options from the Target object that launched this simulation # - Switch for whether to evaluate analytic derivatives. FF, mvals, TgtOptions, AGrad = lp_load('forcebalance.p') FF.ffdir = '.' # Write the force field file. FF.make(mvals) #---- # Load the options that are set in the ForceBalance input file. #---- # Finite difference step size h = TgtOptions['h'] pgrad = TgtOptions['pgrad'] # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps) nvt_timestep = TgtOptions['nvt_timestep'] nvt_md_steps = TgtOptions['nvt_md_steps'] nvt_eq_steps = TgtOptions['nvt_eq_steps'] nvt_interval = TgtOptions['nvt_interval'] liquid_fnm = TgtOptions['nvt_coords'] # Number of threads, multiple timestep integrator, anisotropic box etc. threads = TgtOptions.get('md_threads', 1) mts = TgtOptions.get('mts_integrator', 0) rpmd_beads = TgtOptions.get('rpmd_beads', 0) force_cuda = TgtOptions.get('force_cuda', 0) nbarostat = TgtOptions.get('n_mcbarostat', 25) anisotropic = TgtOptions.get('anisotropic_box', 0) minimize = TgtOptions.get('minimize_energy', 1) # Print all options. printcool_dictionary(TgtOptions, title="Options from ForceBalance") nvt_snapshots = int((nvt_timestep * nvt_md_steps / 1000) / nvt_interval) nvt_iframes = int(1000 * nvt_interval / nvt_timestep) logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \ % (nvt_snapshots, nvt_iframes, nvt_timestep)) if nvt_snapshots < 2: raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \ % (2000 * int(nvt_interval,nvt_timestep))) #---- # Loading coordinates #---- ML = Molecule(liquid_fnm, toppbc=True) # Determine the number of molecules in the condensed phase coordinate file. NMol = len(ML.molecules) TgtOptions['n_molecules'] = NMol logger.info("There are %i molecules in the liquid\n" % (NMol)) #---- # Setting up MD simulations #---- EngOpts = OrderedDict() EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML), ("pbc", True)]) if "nonbonded_cutoff" in TgtOptions: EngOpts["liquid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"] else: largest_available_cutoff = min(ML.boxes[0][:3]) / 2 - 0.1 EngOpts["liquid"]["nonbonded_cutoff"] = largest_available_cutoff logger.info( "nonbonded_cutoff is by default set to the largest available value: %g Angstrom" % largest_available_cutoff) if "vdw_cutoff" in TgtOptions: EngOpts["liquid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"] # Hard Code nonbonded_cutoff to 13A for test #EngOpts["liquid"]["nonbonded_cutoff"] = EngOpts["liquid"]["vdw_cutoff"] = 13.0 GenOpts = OrderedDict([('FF', FF)]) if engname == "openmm": # OpenMM-specific options EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA') if force_cuda: try: Platform.getPlatformByName('CUDA') except: raise RuntimeError( 'Forcing failure because CUDA platform unavailable') EngOpts["liquid"]["platname"] = 'CUDA' if threads > 1: logger.warn( "Setting the number of threads will have no effect on OpenMM engine.\n" ) EngOpts["liquid"].update(GenOpts) for i in EngOpts: printcool_dictionary(EngOpts[i], "Engine options for %s" % i) # Set up MD options MDOpts = OrderedDict() MDOpts["liquid"] = OrderedDict([("nsteps", nvt_md_steps), ("timestep", nvt_timestep), ("temperature", temperature), ("nequil", nvt_eq_steps), ("minimize", minimize), ("nsave", int(1000 * nvt_interval / nvt_timestep)), ("verbose", True), ('save_traj', TgtOptions['save_traj']), ("threads", threads), ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)]) # Energy components analysis disabled for OpenMM MTS because it uses force groups if (engname == "openmm" and mts): logger.warn( "OpenMM with MTS integrator; energy components analysis will be disabled.\n" ) # Create instances of the MD Engine objects. Liquid = Engine(name="liquid", **EngOpts["liquid"]) #=================================================================# # Run the simulation for the full system and analyze the results. # #=================================================================# printcool("Condensed phase NVT molecular dynamics", color=4, bold=True) click() prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"]) logger.info("Liquid phase MD simulation took %.3f seconds\n" % click()) Potentials = prop_return['Potentials'] #============================================# # Compute the potential energy derivatives. # #============================================# if AGrad: logger.info( "Calculating potential energy derivatives with finite difference step size: %f\n" % h) # Switch for whether to compute the derivatives two different ways for consistency. FDCheck = False printcool( "Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Potentials), color=4, bold=True) click() G, GDx, GDy, GDz = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info("Condensed phase energy derivatives took %.3f seconds\n" % click()) #==============================================# # Condensed phase properties and derivatives. # #==============================================# # Physical constants kB = 0.008314472471220214 T = temperature kT = kB * T # Unit: kJ/mol #--- Surface Tension ---- logger.info("Start Computing surface tension.\n") perturb_proportion = 0.0005 box_vectors = np.array( Liquid.xyz_omms[0][1] / nanometer) # obtain original box vectors from first frame delta_S = np.sqrt( np.sum(np.cross(box_vectors[0], box_vectors[1])** 2)) * perturb_proportion * 2 # unit: nm^2. *2 for 2 surfaces # perturb xy area + click() scale_x = scale_y = np.sqrt(1 + perturb_proportion) scale_z = 1.0 / ( 1 + perturb_proportion ) # keep the box volumn while changing the area of xy plane Liquid.scale_box(scale_x, scale_y, scale_z) logger.info("scale_box+ took %.3f seconds\n" % click()) # Obtain energies and gradients Potentials_plus = Liquid.energy() logger.info( "Calculation of energies for perturbed box+ took %.3f seconds\n" % click()) if AGrad: G_plus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info( "Calculation of energy gradients for perturbed box+ took %.3f seconds\n" % click()) # perturb xy area - ( Note: also need to cancel the previous scaling) scale_x = scale_y = np.sqrt(1 - perturb_proportion) * (1.0 / scale_x) scale_z = 1.0 / (1 - perturb_proportion) * (1.0 / scale_z) Liquid.scale_box(scale_x, scale_y, scale_z) logger.info("scale_box- took %.3f seconds\n" % click()) # Obtain energies and gradients Potentials_minus = Liquid.energy() logger.info( "Calculation of energies for perturbed box- took %.3f seconds\n" % click()) if AGrad: G_minus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False) logger.info( "Calculation of energy gradients for perturbed box- took %.3f seconds\n" % click()) # Compute surface tension dE_plus = Potentials_plus - Potentials # Unit: kJ/mol dE_minus = Potentials_minus - Potentials # Unit: kJ/mol prefactor = -0.5 * kT / delta_S / 6.0221409e-1 # Unit mJ m^-2 # Following equation: γ = -kT/(2ΔS) * [ ln<exp(-ΔE+/kT)> - ln<exp(-ΔE-/kT)> ] #plus_avg, plus_err = mean_stderr(np.exp(-dE_plus/kT)) #minus_avg, minus_err = mean_stderr(np.exp(-dE_minus/kT)) #surf_ten = -0.5 * kT / delta_S * ( np.log(plus_avg) - np.log(minus_avg) ) / 6.0221409e-1 # convert to mJ m^-2 #surf_ten_err = 0.5 * kT / delta_S * ( np.log(plus_avg+plus_err) - np.log(plus_avg-plus_err) + np.log(minus_avg+minus_err) - np.log(minus_avg-minus_err) ) / 6.0221409e-1 exp_dE_plus = np.exp(-dE_plus / kT) exp_dE_minus = np.exp(-dE_minus / kT) surf_ten = prefactor * (np.log(np.mean(exp_dE_plus)) - np.log(np.mean(exp_dE_minus))) # Use bootstrap method to estimate the error num_frames = len(exp_dE_plus) numboots = 1000 surf_ten_boots = np.zeros(numboots) for i in range(numboots): boots_ordering = np.random.randint(num_frames, size=num_frames) boots_exp_dE_plus = np.take(exp_dE_plus, boots_ordering) boots_exp_dE_minus = np.take(exp_dE_minus, boots_ordering) surf_ten_boots[i] = prefactor * (np.log(np.mean(boots_exp_dE_plus)) - np.log(np.mean(boots_exp_dE_minus))) surf_ten_err = np.std(surf_ten_boots) * np.sqrt( np.mean([ statisticalInefficiency(exp_dE_plus), statisticalInefficiency(exp_dE_minus) ])) printcool("Surface Tension: % .4f +- %.4f mJ m^-2" % (surf_ten, surf_ten_err)) # Analytic Gradient of surface tension # Formula: β = 1/kT # ∂γ/∂α = -kT/(2ΔS) * { 1/<exp(-βΔE+)> * [<-β ∂E+/∂α exp(-βΔE+)> - <-β ∂E/∂α><exp(-βΔE+)>] # -1/<exp(-βΔE-)> * [<-β ∂E-/∂α exp(-βΔE-)> - <-β ∂E/∂α><exp(-βΔE-)>] } n_params = len(mvals) G_surf_ten = np.zeros(n_params) if AGrad: beta = 1.0 / kT plus_denom = np.mean(np.exp(-beta * dE_plus)) minus_denom = np.mean(np.exp(-beta * dE_minus)) for param_i in range(n_params): plus_left = np.mean(-beta * G_plus[param_i] * np.exp(-beta * dE_plus)) plus_right = np.mean(-beta * G[param_i]) * plus_denom minus_left = np.mean(-beta * G_minus[param_i] * np.exp(-beta * dE_minus)) minus_right = np.mean(-beta * G[param_i]) * minus_denom G_surf_ten[param_i] = prefactor * (1.0 / plus_denom * (plus_left - plus_right) - 1.0 / minus_denom * (minus_left - minus_right)) printcool("Analytic Derivatives:") FF.print_map(vals=G_surf_ten) logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all results to disk.\n") result_dict = { 'surf_ten': surf_ten, 'surf_ten_err': surf_ten_err, 'G_surf_ten': G_surf_ten } lp_dump(result_dict, 'nvt_result.p')