def test_read_objective_function(tmpdir): # Save an objective function file. lp_dump({"X": 1.0}, os.path.join(tmpdir, "objective.p")) assert numpy.isclose( TargetAnalysisFactory._read_objective_function(tmpdir), 1.0)
def mock_target(tmpdir) -> Tuple[Optimization, EvaluatorTarget, str]: """Create a mock evaluator target directory which is populated with a dummy set of results. Returns ------- A tuple of the parent optimization, the mock target and the path to the directory in which the files were created. """ with temporary_cd(str(tmpdir)): # Mock the target to analyze. target = create_evaluator_target("evaluator-target-1", ["data-set-1"]) optimization = create_optimization("project-1", "study-1", "optimization-1", [target]) optimization.analysis_environments = [] # Create a dummy data set and estimated result. reference_data_set = create_data_set("data-set-1", 1) DataSetCollection(data_sets=[reference_data_set]).to_evaluator().json( "training-set.json") results = RequestResult() results.estimated_properties = reference_data_set.to_evaluator() results.json("results.json") lp_dump({"X": 1.0}, "objective.p") return optimization, target, str(tmpdir)
def run_simulation(self, label, liq, AGrad=True): """ Submit a simulation to the Work Queue or run it locally. Inputs: label = The name of the molecule (and hopefully the folder name that you're running in) liq = True/false flag indicating whether to run in liquid or gas phase """ wq = getWorkQueue() # Create a dictionary of MD options that the script will read. md_opts = OrderedDict() md_opts['temperature'] = self.hfe_temperature md_opts['pressure'] = self.hfe_pressure md_opts['minimize'] = True if liq: sdnm = 'liq' md_opts['nequil'] = self.liquid_eq_steps md_opts['nsteps'] = self.liquid_md_steps md_opts['timestep'] = self.liquid_timestep md_opts['sample'] = self.liquid_interval else: sdnm = 'gas' md_opts['nequil'] = self.gas_eq_steps md_opts['nsteps'] = self.gas_md_steps md_opts['timestep'] = self.gas_timestep md_opts['sample'] = self.gas_interval eng_opts = deepcopy(self.engine_opts) # Enforce implicit solvent in the liquid simulation. # We need to be more careful with this when running explicit solvent. eng_opts['implicit_solvent'] = liq eng_opts['coords'] = os.path.basename(self.molecules[label]) if not os.path.exists(sdnm): os.makedirs(sdnm) os.chdir(sdnm) if not os.path.exists('md_result.p'): # Link in a bunch of files... what were these again? link_dir_contents(os.path.join(self.root,self.rundir),os.getcwd()) # Link in the scripts required to run the simulation for f in self.scripts: LinkFile(os.path.join(os.path.split(__file__)[0],"data",f),os.path.join(os.getcwd(),f)) # Link in the coordinate file. LinkFile(self.molecules[label], './%s' % os.path.basename(self.molecules[label])) # Store names of previous trajectory files. self.last_traj += [os.path.join(os.getcwd(), i) for i in self.extra_output] # Write target, engine and simulation options to disk. lp_dump((self.OptionDict, eng_opts, md_opts), 'simulation.p') # Execute the script for running molecular dynamics. cmdstr = '%s python md_ism_hfe.py %s' % (self.prefix, "-g" if AGrad else "") if wq == None: logger.info("Running condensed phase simulation locally.\n") logger.info("You may tail -f %s/npt.out in another terminal window\n" % os.getcwd()) _exec(cmdstr, copy_stderr=True, outfnm='md.out') else: queue_up(wq, command = cmdstr+' &> md.out', tag='%s:%s/%s' % (self.name, label, "liq" if liq else "gas"), input_files = self.scripts + ['simulation.p', 'forcefield.p', os.path.basename(self.molecules[label])], output_files = ['md_result.p', 'md.out'] + self.extra_output, tgt=self, verbose=False, print_time=3600) os.chdir('..')
def mock_target(tmpdir) -> Tuple[Optimization, RechargeTarget, str]: """Create a mock recharge target directory which is populated with a dummy set of results. Returns ------- A tuple of the parent optimization, the mock target and the path to the directory in which the files were created. """ with temporary_cd(str(tmpdir)): # Mock the target to analyze. target = create_recharge_target("recharge-target-1", ["qc-data-set-1"]) optimization = create_optimization("project-1", "study-1", "optimization-1", [target]) optimization.analysis_environments = [ ChemicalEnvironment.Alkane, ChemicalEnvironment.Alcohol, ] # Create a dummy set of residuals. with open("residuals.json", "w") as file: json.dump({"C": 9.0, "CO": 4.0}, file) lp_dump({"X": 1.0}, "objective.p") return optimization, target, str(tmpdir)
def run_simulation(self, label, liq, AGrad=True): """ Submit a simulation to the Work Queue or run it locally. Inputs: label = The name of the molecule (and hopefully the folder name that you're running in) liq = True/false flag indicating whether to run in liquid or gas phase """ wq = getWorkQueue() # Create a dictionary of MD options that the script will read. md_opts = OrderedDict() md_opts['temperature'] = self.hfe_temperature md_opts['pressure'] = self.hfe_pressure md_opts['minimize'] = True if liq: sdnm = 'liq' md_opts['nequil'] = self.liquid_eq_steps md_opts['nsteps'] = self.liquid_md_steps md_opts['timestep'] = self.liquid_timestep md_opts['sample'] = self.liquid_interval else: sdnm = 'gas' md_opts['nequil'] = self.gas_eq_steps md_opts['nsteps'] = self.gas_md_steps md_opts['timestep'] = self.gas_timestep md_opts['sample'] = self.gas_interval eng_opts = deepcopy(self.engine_opts) # Enforce implicit solvent in the liquid simulation. # We need to be more careful with this when running explicit solvent. eng_opts['implicit_solvent'] = liq eng_opts['coords'] = os.path.basename(self.molecules[label]) if not os.path.exists(sdnm): os.makedirs(sdnm) os.chdir(sdnm) if not os.path.exists('md_result.p'): # Link in a bunch of files... what were these again? link_dir_contents(os.path.join(self.root,self.rundir),os.getcwd()) # Link in the scripts required to run the simulation for f in self.scripts: LinkFile(os.path.join(os.path.split(__file__)[0],"data",f),os.path.join(os.getcwd(),f)) # Link in the coordinate file. LinkFile(self.molecules[label], './%s' % os.path.basename(self.molecules[label])) # Store names of previous trajectory files. self.last_traj += [os.path.join(os.getcwd(), i) for i in self.extra_output] # Write target, engine and simulation options to disk. lp_dump((self.OptionDict, eng_opts, md_opts), 'simulation.p') # Execute the script for running molecular dynamics. cmdstr = '%s python md_ism_hfe.py %s' % (self.prefix, "-g" if AGrad else "") if wq is None: logger.info("Running condensed phase simulation locally.\n") logger.info("You may tail -f %s/npt.out in another terminal window\n" % os.getcwd()) _exec(cmdstr, copy_stderr=True, outfnm='md.out') else: queue_up(wq, command = cmdstr+' &> md.out', tag='%s:%s/%s' % (self.name, label, "liq" if liq else "gas"), input_files = self.scripts + ['simulation.p', 'forcefield.p', os.path.basename(self.molecules[label])], output_files = ['md_result.p', 'md.out'] + self.extra_output, tgt=self, verbose=False, print_time=3600) os.chdir('..')
def submit_jobs(self, mvals, AGrad=True, AHess=True): """This routine is called by Objective.stage() and will run before "get". It submits the jobs and the stage() function will wait for jobs to complete. Parameters ---------- mvals : list Mathematical parameter values. AGrad : Boolean Switch to turn on analytic gradient. AHess : Boolean Switch to turn on analytic Hessian. Returns ------- Nothing. """ # Set up and run the simulation chain on all points. for pt in self.points: # Create subdir try: os.makedirs(str(pt.idnr)) except OSError as exception: if exception.errno != errno.EEXIST: raise # Goto subdir os.chdir(str(pt.idnr)) # Link dir contents from target subdir to current temp directory. for f in self.scripts: LinkFile(os.path.join(self.root, self.tempdir, f), os.path.join(os.getcwd(), f)) link_dir_contents(os.path.join(self.root, self.tgtdir, str(pt.idnr)), os.getcwd()) # Dump the force field to a pickle file with wopen('forcebalance.p') as f: lp_dump((self.FF, mvals, self.OptionDict, AGrad), f) # Run the simulation chain for point. cmdstr = ("%s python md_chain.py " % self.mdpfx + " ".join(self.quantities) + " " + "--engine %s " % self.engname + "--length %d " % self.n_sim_chain + "--name %s " % self.simpfx + "--temperature %f " % pt.temperature + "--pressure %f " % pt.pressure + "--nequil %d " % self.eq_steps + "--nsteps %d " % self.md_steps) _exec(cmdstr, copy_stderr=True, outfnm='md_chain.out') os.chdir('..')
def submit_jobs(self, mvals, AGrad=True, AHess=True): """This routine is called by Objective.stage() and will run before "get". It submits the jobs and the stage() function will wait for jobs to complete. Parameters ---------- mvals : list Mathematical parameter values. AGrad : Boolean Switch to turn on analytic gradient. AHess : Boolean Switch to turn on analytic Hessian. Returns ------- Nothing. """ # Set up and run the simulation chain on all points. for pt in self.points: # Create subdir try: os.makedirs(str(pt.idnr)) except OSError as exception: if exception.errno != errno.EEXIST: raise # Goto subdir os.chdir(str(pt.idnr)) # Link dir contents from target subdir to current temp directory. for f in self.scripts: LinkFile(os.path.join(self.root, self.tempdir, f), os.path.join(os.getcwd(), f)) link_dir_contents(os.path.join(self.root, self.tgtdir, str(pt.idnr)), os.getcwd()) # Dump the force field to a pickle file lp_dump((self.FF, mvals, self.OptionDict, AGrad), 'forcebalance.p') # Run the simulation chain for point. cmdstr = ("%s python md_chain.py " % self.mdpfx + " ".join(self.quantities) + " " + "--engine %s " % self.engname + "--length %d " % self.n_sim_chain + "--name %s " % self.simpfx + "--temperature %f " % pt.temperature + "--pressure %f " % pt.pressure + "--nequil %d " % self.eq_steps + "--nsteps %d " % self.md_steps) _exec(cmdstr, copy_stderr=True, outfnm='md_chain.out') os.chdir('..')
def test_analysis_missing_result(monkeypatch, force_field): """Test that the correction exception is raised in the case where a the expected results of a target are missing.""" optimization = create_optimization( "project-1", "study-1", "optimization-1", [ create_evaluator_target("evaluator-target-1", ["data-set-1"]), create_recharge_target("recharge-target-1", ["qc-data-set-1"]), ], ) optimization.force_field = force_field with temporary_cd(): # Save the expected results files. os.makedirs(os.path.join("result", "optimize")) for target in optimization.targets: os.makedirs(os.path.join("targets", target.id)) os.makedirs(os.path.join("optimize.tmp", target.id, "iter_0000")) lp_dump( {"X": 1.0}, os.path.join("optimize.tmp", target.id, "iter_0000", "objective.p"), ) with open("optimization.json", "w") as file: file.write(optimization.json()) monkeypatch.setattr(OptimizationAnalysisFactory, "_load_refit_force_field", lambda: force_field) # Mock a missing target result. monkeypatch.setattr( EvaluatorAnalysisFactory, "analyze", lambda *args, **kwargs: EvaluatorTargetResult( objective_function=1.0, statistic_entries=[]), ) monkeypatch.setattr( RechargeAnalysisFactory, "analyze", lambda *args, **kwargs: None, ) with pytest.raises(RuntimeError) as error_info: OptimizationAnalysisFactory.analyze(True) assert "The results of the recharge-target-1 target could not be found" in str( error_info.value)
def mock_target_outputs(targets: List[FittingTarget], parameters: List[SMIRNOFFParameter]): for target in targets: target_directory = os.path.join("optimize.tmp", target.name, "iter_0000") os.makedirs(target_directory, exist_ok=True) lp_dump( {"G": numpy.random.randn(len(parameters))}, os.path.join(target_directory, "objective.p"), )
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 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')
def main(): """Usage: (gmxprefix.sh) md_chain.py <list of quantities> --engine <gromacs> --length <n> --name <name> --temperature <T> --pressure <p> --nequil <nequil> --nsteps <nsteps> This program is meant to be called automatically by ForceBalance. """ printcool("ForceBalance 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'] engines = [] ## Setup and carry out simulations in chain for i in range(args.length): # Simulation files if engname == "gromacs": ndx_flag = False coords = args.name + str(i + 1) + ".gro" top_file = args.name + str(i + 1) + ".top" mdp_file = args.name + str(i + 1) + ".mdp" ndx_file = args.name + str(i + 1) + ".ndx" if os.path.exists(ndx_file): ndx_flag = True mol = Molecule(coords) #---- # Set coordinates and molecule for engine #---- EngOpts = OrderedDict([("FF", FF), ("pbc", True), ("coords", coords), ("mol", mol)]) if engname == "gromacs": # Gromacs-specific options EngOpts["gmx_top"] = top_file EngOpts["gmx_mdp"] = mdp_file if ndx_flag: EngOpts["gmx_ndx"] = ndx_file printcool_dictionary(EngOpts) # Create engine objects and store them for subsequent analysis. s = Engine(name=args.name + str(i + 1), **EngOpts) #=====================# # Run the simulation. # #=====================# MDOpts = OrderedDict([("nsteps", args.nsteps), ("nequil", args.nequil)]) printcool("Molecular dynamics simulation", color=4, bold=True) s.md(verbose=True, **MDOpts) engines.append(s) #======================================================================# # Extract the quantities of interest from the MD simulations and dump # # the results to file. # # =====================================================================# results = OrderedDict() for q in args.quantities: logger.info("Extracting %s...\n" % q) # Initialize quantity objstr = "Quantity_" + q.capitalize() dm = il.import_module('..quantity', package='forcebalance.quantity') Quantity = getattr(dm, objstr)(engname, args.temperature, args.pressure) Q, Qerr, Qgrad = Quantity.extract(engines, FF, mvals, h, pgrad, AGrad) results.setdefault("values", []).append(Q) results.setdefault("errors", []).append(Qerr) results.setdefault("grads", []).append(Qgrad) logger.info("Finished!\n") # Print out results for the quantity and its derivative. Sep = printcool(("%s: % .4f +- % .4f \nAnalytic Derivative:" % (q.capitalize(), Q, Qerr))) FF.print_map(vals=Qgrad) # Dump results to file logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump((np.asarray(results["values"]), np.asarray( results["errors"]), np.asarray(results["grads"])), 'md_result.p')
def main(): """Usage: (gmxprefix.sh) md_chain.py <list of quantities> --engine <gromacs> --length <n> --name <name> --temperature <T> --pressure <p> --nequil <nequil> --nsteps <nsteps> This program is meant to be called automatically by ForceBalance. """ printcool("ForceBalance 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'] engines = [] ## Setup and carry out simulations in chain for i in range(args.length): # Simulation files if engname == "gromacs": ndx_flag = False coords = args.name + str(i+1) + ".gro" top_file = args.name + str(i+1) + ".top" mdp_file = args.name + str(i+1) + ".mdp" ndx_file = args.name + str(i+1) + ".ndx" if os.path.exists(ndx_file): ndx_flag = True mol = Molecule(coords) #---- # Set coordinates and molecule for engine #---- EngOpts = OrderedDict([("FF", FF), ("pbc", True), ("coords", coords), ("mol", mol)]) if engname == "gromacs": # Gromacs-specific options EngOpts["gmx_top"] = top_file EngOpts["gmx_mdp"] = mdp_file if ndx_flag: EngOpts["gmx_ndx"] = ndx_file printcool_dictionary(EngOpts) # Create engine objects and store them for subsequent analysis. s = Engine(name=args.name+str(i+1), **EngOpts) #=====================# # Run the simulation. # #=====================# MDOpts = OrderedDict([("nsteps", args.nsteps), ("nequil", args.nequil)]) printcool("Molecular dynamics simulation", color=4, bold=True) s.md(verbose=True, **MDOpts) engines.append(s) #======================================================================# # Extract the quantities of interest from the MD simulations and dump # # the results to file. # # =====================================================================# results = OrderedDict() for q in args.quantities: logger.info("Extracting %s...\n" % q) # Initialize quantity objstr = "Quantity_" + q.capitalize() dm = il.import_module('..quantity', package='forcebalance.quantity') Quantity = getattr(dm, objstr)(engname, args.temperature, args.pressure) Q, Qerr, Qgrad = Quantity.extract(engines, FF, mvals, h, pgrad, AGrad) results.setdefault("values", []).append(Q) results.setdefault("errors", []).append(Qerr) results.setdefault("grads", []).append(Qgrad) logger.info("Finished!\n") # Print out results for the quantity and its derivative. Sep = printcool(("%s: % .4f +- % .4f \nAnalytic Derivative:" % (q.capitalize(), Q, Qerr))) FF.print_map(vals=Qgrad) # Dump results to file logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump((np.asarray(results["values"]), np.asarray(results["errors"]), np.asarray(results["grads"])), 'md_result.p')
#!/usr/bin/env python from mslib import MSMS from forcebalance.nifty import lp_load, lp_dump import numpy as np import os # Designed to be called from GenerateQMData.py # I wrote this because MSMS seems to have a memory leak xyz, radii, density = lp_load(open('msms_input.p')) MS = MSMS(coords = list(xyz), radii = radii) MS.compute(density=density) vfloat, vint, tri = MS.getTriangles() with open(os.path.join('msms_output.p'), 'w') as f: lp_dump(vfloat, 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) 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')
# coding: utf-8 from forcebalance.nifty import lp_dump import pickle import os for f in os.listdir('.'): if os.path.isdir(f): os.chdir(f) print(f) for pf in os.listdir('.'): if pf.endswith('.pickle'): data = pickle.load(open(pf, 'rb'), encoding='latin1') lp_dump(data, pf[:-5]) print(pf[:-5] + ' replaced') os.chdir('..')
def main(): """Usage: (prefix.sh) md_one.py -T, --temperature <temperature in kelvin> -P, --pressure <pressure in atm> -g, --grad (if gradients of output timeseries are desired) -eq, --nequil <number of equilibration MD steps> -md, --nsteps <number of production MD steps> -dt, --timestep <number of production MD steps> -sp, --sample <number of production MD steps> -nt, --threads <number of CPU threads to use> -min, --minimize <minimize the energy> This program is meant to be called automatically by ForceBalance because force field options are loaded from the 'forcefield.p' file, and simulation options are loaded from the 'simulation.p' file. The files are separated because the same force field file may be used for many simulations. """ # Write the force field file. FF.make(mvals) # Read the command line options (they may override the options from file.) AGrad = args['gradient'] for i in ['temperature', 'pressure', 'nequil', 'nsteps', 'timestep', 'sample', 'threads', 'minimize']: if i in args: MDOpts[i] = args[i] MDOpts['nsave'] = int(1000.0*MDOpts['sample']/MDOpts['timestep']) if 'save_traj' in TgtOpts: MDOpts['save_traj'] = TgtOpts['save_traj'] #---- # Print some options. # At this point, engine and MD options should be SET! #---- printcool("ForceBalance simulation using engine: %s" % engname.upper(), color=4, bold=True) printcool_dictionary(args, title="Options from command line") printcool_dictionary(EngOpts, title="Engine options") printcool_dictionary(MDOpts, title="Molecular dynamics options") #---- # For convenience, assign some local variables. #---- # Finite difference step size h = TgtOpts['h'] # Active parameters to differentiate pgrad = TgtOpts['pgrad'] # Create instances of the MD Engine objects. Engine = EngineClass(**EngOpts) click() # Start timer. # This line runs the condensed phase simulation. #---- # The molecular dynamics simulation returns a dictionary of properties # In the future, the properties will be stored as data inside the object Results = Engine.molecular_dynamics(**MDOpts) if AGrad: Results['Potential_Derivatives'] = energy_derivatives(Engine, FF, mvals, h, pgrad, dipole=False)['potential'] # Set up engine and calculate the potential in the other phase. EngOpts_ = deepcopy(EngOpts) EngOpts_['implicit_solvent'] = not EngOpts['implicit_solvent'] Engine_ = EngineClass(**EngOpts_) Engine_.xyz_omms = Engine.xyz_omms Energy_ = Engine_.energy() Results_ = {'Potentials' : Energy_} if AGrad: Derivs_ = energy_derivatives(Engine_, FF, mvals, h, pgrad, dipole=False)['potential'] Results_['Potential_Derivatives'] = Derivs_ # Calculate the hydration energy of each snapshot and its parametric derivatives. if EngOpts['implicit_solvent']: Energy_liq = Results['Potentials'] Energy_gas = Results_['Potentials'] if AGrad: Derivs_liq = Results['Potential_Derivatives'] Derivs_gas = Results_['Potential_Derivatives'] else: Energy_gas = Results['Potentials'] Energy_liq = Results_['Potentials'] if AGrad: Derivs_gas = Results['Potential_Derivatives'] Derivs_liq = Results_['Potential_Derivatives'] Results['Hydration'] = Energy_liq - Energy_gas if AGrad: Results['Hydration_Derivatives'] = Derivs_liq - Derivs_gas # Code of the future! # Don't know how to use it yet though. # Engine.molecular_dynamics(**MDOpts) # logger.info("MD simulation took %.3f seconds\n" % click()) # # Extract properties. # Results = Engine.md_extract(OrderedDict([(i, {}) for i in Tgt.timeseries.keys()])) # potential = properties['Potential'] # Calculate energy and dipole derivatives if needed. # if AGrad: # Results['derivatives'] = energy_derivatives(Engine, FF, mvals, h, pgrad, dipole='dipole' in Tgt.timeseries.keys()) # Dump results to file logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump(Results, 'md_result.p')
def main(): """Usage: (prefix.sh) md_one.py -T, --temperature <temperature in kelvin> -P, --pressure <pressure in atm> -g, --grad (if gradients of output timeseries are desired) -eq, --nequil <number of equilibration MD steps> -md, --nsteps <number of production MD steps> -dt, --timestep <number of production MD steps> -sp, --sample <number of production MD steps> -nt, --threads <number of CPU threads to use> -min, --minimize <minimize the energy> This program is meant to be called automatically by ForceBalance because force field options are loaded from the 'forcefield.p' file, and simulation options are loaded from the 'simulation.p' file. The files are separated because the same force field file may be used for many simulations. """ # Write the force field file. FF.make(mvals) # Read the command line options (they may override the options from file.) AGrad = args['gradient'] for i in [ 'temperature', 'pressure', 'nequil', 'nsteps', 'timestep', 'sample', 'threads', 'minimize' ]: if i in args: MDOpts[i] = args[i] MDOpts['nsave'] = int(1000.0 * MDOpts['sample'] / MDOpts['timestep']) if 'save_traj' in TgtOpts: MDOpts['save_traj'] = TgtOpts['save_traj'] #---- # Print some options. # At this point, engine and MD options should be SET! #---- printcool("ForceBalance simulation using engine: %s" % engname.upper(), color=4, bold=True) printcool_dictionary(args, title="Options from command line") printcool_dictionary(EngOpts, title="Engine options") printcool_dictionary(MDOpts, title="Molecular dynamics options") #---- # For convenience, assign some local variables. #---- # Finite difference step size h = TgtOpts['h'] # Active parameters to differentiate pgrad = TgtOpts['pgrad'] # Create instances of the MD Engine objects. Engine = EngineClass(**EngOpts) click() # Start timer. # This line runs the condensed phase simulation. #---- # The molecular dynamics simulation returns a dictionary of properties # In the future, the properties will be stored as data inside the object Results = Engine.molecular_dynamics(**MDOpts) if AGrad: Results['Potential_Derivatives'] = energy_derivatives( Engine, FF, mvals, h, pgrad, dipole=False)['potential'] # Set up engine and calculate the potential in the other phase. EngOpts_ = deepcopy(EngOpts) EngOpts_['implicit_solvent'] = not EngOpts['implicit_solvent'] Engine_ = EngineClass(**EngOpts_) Engine_.xyz_omms = Engine.xyz_omms Energy_ = Engine_.energy() Results_ = {'Potentials': Energy_} if AGrad: Derivs_ = energy_derivatives(Engine_, FF, mvals, h, pgrad, dipole=False)['potential'] Results_['Potential_Derivatives'] = Derivs_ # Calculate the hydration energy of each snapshot and its parametric derivatives. if EngOpts['implicit_solvent']: Energy_liq = Results['Potentials'] Energy_gas = Results_['Potentials'] if AGrad: Derivs_liq = Results['Potential_Derivatives'] Derivs_gas = Results_['Potential_Derivatives'] else: Energy_gas = Results['Potentials'] Energy_liq = Results_['Potentials'] if AGrad: Derivs_gas = Results['Potential_Derivatives'] Derivs_liq = Results_['Potential_Derivatives'] Results['Hydration'] = Energy_liq - Energy_gas if AGrad: Results['Hydration_Derivatives'] = Derivs_liq - Derivs_gas # Code of the future! # Don't know how to use it yet though. # Engine.molecular_dynamics(**MDOpts) # logger.info("MD simulation took %.3f seconds\n" % click()) # # Extract properties. # Results = Engine.md_extract(OrderedDict([(i, {}) for i in Tgt.timeseries.keys()])) # potential = properties['Potential'] # Calculate energy and dipole derivatives if needed. # if AGrad: # Results['derivatives'] = energy_derivatives(Engine, FF, mvals, h, pgrad, dipole='dipole' in Tgt.timeseries.keys()) # Dump results to file logger.info("Writing final force field.\n") pvals = FF.make(mvals) logger.info("Writing all simulation data to disk.\n") lp_dump(Results, 'md_result.p')
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)
#---- Scd_avg, Scd_e = mean_stderr(Scds) Scd_err = flat(Scd_e) GScd = mBeta * (((np.mat(G) * Scds) / L) - (np.mat(np.average(G, axis = 1)).T * 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 == 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") <<<<<<< HEAD with wopen(os.path.join('npt_result.p')) as f: 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),f) ======= 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),'npt_result.p') >>>>>>> 8b8c6b3f716f0991da16a3906109647307384a39 if __name__ == "__main__": main()
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 test_analysis(monkeypatch, force_field, dummy_conda_env): optimization = create_optimization( "project-1", "study-1", "optimization-1", [ create_evaluator_target("evaluator-target-1", ["data-set-1"]), create_recharge_target("recharge-target-1", ["qc-data-set-1"]), ], ) optimization.force_field = force_field with temporary_cd(os.path.dirname(dummy_conda_env)): # Save the expected results files. os.makedirs(os.path.join("result", "optimize")) for target in optimization.targets: os.makedirs(os.path.join("targets", target.id)) os.makedirs(os.path.join("optimize.tmp", target.id, "iter_0000")) os.makedirs(os.path.join("optimize.tmp", target.id, "iter_0001")) # Add enough output files to make it look like only one full iteration has # finished. lp_dump( {"X": 1.0}, os.path.join("optimize.tmp", target.id, "iter_0000", "objective.p"), ) lp_dump( {"X": 1.0}, os.path.join("optimize.tmp", optimization.targets[0].id, "iter_0001", "objective.p"), ) with open("optimization.json", "w") as file: file.write(optimization.json()) optimization.force_field.to_openff().to_file( os.path.join("result", "optimize", "force-field.offxml")) # Mock the already tested functions. monkeypatch.setattr(OptimizationAnalysisFactory, "_load_refit_force_field", lambda: force_field) monkeypatch.setattr( EvaluatorAnalysisFactory, "analyze", lambda *args, **kwargs: EvaluatorTargetResult( objective_function=1.0, statistic_entries=[]), ) monkeypatch.setattr( RechargeAnalysisFactory, "analyze", lambda *args, **kwargs: RechargeTargetResult( objective_function=1.0, statistic_entries=[]), ) OptimizationAnalysisFactory.analyze(True) for target in optimization.targets: assert os.path.isfile( os.path.join("analysis", target.id, "iteration-0.json")) assert not os.path.isfile( os.path.join("analysis", target.id, "iteration-1.json")) result = OptimizationResult.parse_file( os.path.join("analysis", "optimization-results.json")) assert len(result.target_results) == 1 assert all(target.id in result.target_results[0] for target in optimization.targets) assert result.refit_force_field.inner_content == force_field.inner_content