示例#1
0
 def get_ti2(self, mvals, AGrad=False, AHess=False):
     """ Get the hydration free energy using two-point thermodynamic integration. """
     self.hfe_dict = OrderedDict()
     dD = np.zeros((self.FF.np,len(self.IDs)))
     beta = 1. / (kb * self.hfe_temperature)
     for ilabel, label in enumerate(self.IDs):
         os.chdir(label)
         # This dictionary contains observables keyed by each phase.
         data = defaultdict(dict)
         for p in ['gas', 'liq']:
             os.chdir(p)
             # Load the results from molecular dynamics.
             results = lp_load('md_result.p')
             # Time series of hydration energies.
             H = results['Hydration']
             # Store the average hydration energy.
             data[p]['Hyd'] = np.mean(H)
             if AGrad:
                 dE = results['Potential_Derivatives']
                 dH = results['Hydration_Derivatives']
                 # Calculate the parametric derivative of the average hydration energy.
                 data[p]['dHyd'] = np.mean(dH,axis=1)-beta*(flat(np.matrix(dE)*col(H)/len(H))-np.mean(dE,axis=1)*np.mean(H))
             os.chdir('..')
         # Calculate the hydration free energy as the average of liquid and gas hydration energies.
         # Note that the molecular dynamics methods return energies in kJ/mol.
         self.hfe_dict[label] = 0.5*(data['liq']['Hyd']+data['gas']['Hyd']) / 4.184
         if AGrad:
             # Calculate the derivative of the hydration free energy.
             dD[:, ilabel] = 0.5*self.whfe[ilabel]*(data['liq']['dHyd']+data['gas']['dHyd']) / 4.184
         os.chdir('..')
     calc_hfe = np.array(self.hfe_dict.values())
     D = self.whfe*(calc_hfe - np.array(self.expval.values()))
     return D, dD
示例#2
0
 def get_ti2(self, mvals, AGrad=False, AHess=False):
     """ Get the hydration free energy using two-point thermodynamic integration. """
     self.hfe_dict = OrderedDict()
     dD = np.zeros((self.FF.np,len(self.IDs)))
     beta = 1. / (kb * self.hfe_temperature)
     for ilabel, label in enumerate(self.IDs):
         os.chdir(label)
         # This dictionary contains observables keyed by each phase.
         data = defaultdict(dict)
         for p in ['gas', 'liq']:
             os.chdir(p)
             # Load the results from molecular dynamics.
             results = lp_load('md_result.p')
             # Time series of hydration energies.
             H = results['Hydration']
             # Store the average hydration energy.
             data[p]['Hyd'] = np.mean(H)
             if AGrad:
                 dE = results['Potential_Derivatives']
                 dH = results['Hydration_Derivatives']
                 # Calculate the parametric derivative of the average hydration energy.
                 data[p]['dHyd'] = np.mean(dH,axis=1)-beta*(flat(np.dot(dE, col(H))/len(H))-np.mean(dE,axis=1)*np.mean(H))
             os.chdir('..')
         # Calculate the hydration free energy as the average of liquid and gas hydration energies.
         # Note that the molecular dynamics methods return energies in kJ/mol.
         self.hfe_dict[label] = 0.5*(data['liq']['Hyd']+data['gas']['Hyd']) / 4.184
         if AGrad:
             # Calculate the derivative of the hydration free energy.
             dD[:, ilabel] = 0.5*self.whfe[ilabel]*(data['liq']['dHyd']+data['gas']['dHyd']) / 4.184
         os.chdir('..')
     calc_hfe = np.array(list(self.hfe_dict.values()))
     D = self.whfe*(calc_hfe - np.array(list(self.expval.values())))
     return D, dD
示例#3
0
 def para_solver(L):
     # Levenberg-Marquardt
     # HT = H + (L-1)**2*np.diag(np.diag(H))
     # Attempt to use plain Levenberg
     HT = H + (L-1)**2*np.eye(len(H))
     logger.debug("Inverting Scaled Hessian:\n")                       ###
     logger.debug(" G:\n")                                             ###
     pvec1d(G,precision=5, loglevel=DEBUG)                                   ###
     logger.debug(" HT: (Scal = %.4f)\n" % (1+(L-1)**2))               ###
     pmat2d(HT,precision=5, loglevel=DEBUG)                                  ###
     Hi = invert_svd(np.mat(HT))
     dx = flat(-1 * Hi * col(G))
     logger.debug(" dx:\n")                                            ###
     pvec1d(dx,precision=5, loglevel=DEBUG)                                  ###
     # dxa = -solve(HT, G)
     # dxa = flat(dxa)
     # print " dxa:"                                           ###
     # pvec1d(dxa,precision=5)                                 ###
     # print                                                   ###
     sol = flat(0.5*row(dx)*np.mat(H)*col(dx))[0] + np.dot(dx,G)
     for i in self.excision:    # Reinsert deleted coordinates - don't take a step in those directions
         dx = np.insert(dx, i, 0)
     return dx, sol
示例#4
0
 def get_exp(self, mvals, AGrad=False, AHess=False):
     """ Get the hydration free energy using the Zwanzig formula.  We will obtain two different estimates along with their uncertainties. """
     self.hfe_dict = OrderedDict()
     self.hfe_err = OrderedDict()
     dD = np.zeros((self.FF.np,len(self.IDs)))
     kT = (kb * self.hfe_temperature)
     beta = 1. / (kb * self.hfe_temperature)
     for ilabel, label in enumerate(self.IDs):
         os.chdir(label)
         # This dictionary contains observables keyed by each phase.
         data = defaultdict(dict)
         for p in ['gas', 'liq']:
             os.chdir(p)
             # Load the results from molecular dynamics.
             results = lp_load('md_result.p')
             L = len(results['Potentials'])
             if p == "gas":
                 Eg = results['Potentials']
                 Eaq = results['Potentials'] + results['Hydration']
                 # Mean and standard error of the exponentiated hydration energy.
                 expmbH = np.exp(-1.0*beta*results['Hydration'])
                 data[p]['Hyd'] = -kT*np.log(np.mean(expmbH))
                 # Estimate standard error by bootstrap method.  We also multiply by the 
                 # square root of the statistical inefficiency of the hydration energy time series.
                 data[p]['HydErr'] = np.std([-kT*np.log(np.mean(expmbH[np.random.randint(L,size=L)])) for i in range(100)]) * np.sqrt(statisticalInefficiency(results['Hydration']))
                 if AGrad: 
                     dEg = results['Potential_Derivatives']
                     dEaq = results['Potential_Derivatives'] + results['Hydration_Derivatives']
                     data[p]['dHyd'] = (flat(np.matrix(dEaq)*col(expmbH)/L)-np.mean(dEg,axis=1)*np.mean(expmbH)) / np.mean(expmbH)
             elif p == "liq":
                 Eg = results['Potentials'] - results['Hydration']
                 Eaq = results['Potentials']
                 # Mean and standard error of the exponentiated hydration energy.
                 exppbH = np.exp(+1.0*beta*results['Hydration'])
                 data[p]['Hyd'] = +kT*np.log(np.mean(exppbH))
                 # Estimate standard error by bootstrap method.  We also multiply by the 
                 # square root of the statistical inefficiency of the hydration energy time series.
                 data[p]['HydErr'] = np.std([+kT*np.log(np.mean(exppbH[np.random.randint(L,size=L)])) for i in range(100)]) * np.sqrt(statisticalInefficiency(results['Hydration']))
                 if AGrad: 
                     dEg = results['Potential_Derivatives'] - results['Hydration_Derivatives']
                     dEaq = results['Potential_Derivatives']
                     data[p]['dHyd'] = -(flat(np.matrix(dEg)*col(exppbH)/L)-np.mean(dEaq,axis=1)*np.mean(exppbH)) / np.mean(exppbH)
             os.chdir('..')
         # Calculate the hydration free energy using gas phase, liquid phase or the average of both.
         # Note that the molecular dynamics methods return energies in kJ/mol.
         if self.hfemode == 'exp_gas':
             self.hfe_dict[label] = data['gas']['Hyd'] / 4.184
             self.hfe_err[label] = data['gas']['HydErr'] / 4.184
         elif self.hfemode == 'exp_liq':
             self.hfe_dict[label] = data['liq']['Hyd'] / 4.184
             self.hfe_err[label] = data['liq']['HydErr'] / 4.184
         elif self.hfemode == 'exp_both':
             self.hfe_dict[label] = 0.5*(data['liq']['Hyd']+data['gas']['Hyd']) / 4.184
             self.hfe_err[label] = 0.5*(data['liq']['HydErr']+data['gas']['HydErr']) / 4.184
         if AGrad:
             # Calculate the derivative of the hydration free energy.
             if self.hfemode == 'exp_gas':
                 dD[:, ilabel] = self.whfe[ilabel]*data['gas']['dHyd'] / 4.184
             elif self.hfemode == 'exp_liq':
                 dD[:, ilabel] = self.whfe[ilabel]*data['liq']['dHyd'] / 4.184
             elif self.hfemode == 'exp_both':
                 dD[:, ilabel] = 0.5*self.whfe[ilabel]*(data['liq']['dHyd']+data['gas']['dHyd']) / 4.184
         os.chdir('..')
     calc_hfe = np.array(self.hfe_dict.values())
     D = self.whfe*(calc_hfe - np.array(self.expval.values()))
     return D, dD
示例#5
0
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)
示例#6
0
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')
示例#7
0
 def deprod(vec):
     return flat(np.dot(G,col(vec)))/L
示例#8
0
 def get_exp(self, mvals, AGrad=False, AHess=False):
     """ Get the hydration free energy using the Zwanzig formula.  We will obtain two different estimates along with their uncertainties. """
     self.hfe_dict = OrderedDict()
     self.hfe_err = OrderedDict()
     dD = np.zeros((self.FF.np, len(self.IDs)))
     kT = (kb * self.hfe_temperature)
     beta = 1. / (kb * self.hfe_temperature)
     for ilabel, label in enumerate(self.IDs):
         os.chdir(label)
         # This dictionary contains observables keyed by each phase.
         data = defaultdict(dict)
         for p in ['gas', 'liq']:
             os.chdir(p)
             # Load the results from molecular dynamics.
             results = lp_load('md_result.p')
             L = len(results['Potentials'])
             if p == "gas":
                 Eg = results['Potentials']
                 Eaq = results['Potentials'] + results['Hydration']
                 # Mean and standard error of the exponentiated hydration energy.
                 expmbH = np.exp(-1.0 * beta * results['Hydration'])
                 data[p]['Hyd'] = -kT * np.log(np.mean(expmbH))
                 # Estimate standard error by bootstrap method.  We also multiply by the
                 # square root of the statistical inefficiency of the hydration energy time series.
                 data[p]['HydErr'] = np.std([
                     -kT *
                     np.log(np.mean(expmbH[np.random.randint(L, size=L)]))
                     for i in range(100)
                 ]) * np.sqrt(statisticalInefficiency(results['Hydration']))
                 if AGrad:
                     dEg = results['Potential_Derivatives']
                     dEaq = results['Potential_Derivatives'] + results[
                         'Hydration_Derivatives']
                     data[p]['dHyd'] = (
                         flat(np.matrix(dEaq) * col(expmbH) / L) -
                         np.mean(dEg, axis=1) *
                         np.mean(expmbH)) / np.mean(expmbH)
             elif p == "liq":
                 Eg = results['Potentials'] - results['Hydration']
                 Eaq = results['Potentials']
                 # Mean and standard error of the exponentiated hydration energy.
                 exppbH = np.exp(+1.0 * beta * results['Hydration'])
                 data[p]['Hyd'] = +kT * np.log(np.mean(exppbH))
                 # Estimate standard error by bootstrap method.  We also multiply by the
                 # square root of the statistical inefficiency of the hydration energy time series.
                 data[p]['HydErr'] = np.std([
                     +kT *
                     np.log(np.mean(exppbH[np.random.randint(L, size=L)]))
                     for i in range(100)
                 ]) * np.sqrt(statisticalInefficiency(results['Hydration']))
                 if AGrad:
                     dEg = results['Potential_Derivatives'] - results[
                         'Hydration_Derivatives']
                     dEaq = results['Potential_Derivatives']
                     data[p]['dHyd'] = -(
                         flat(np.matrix(dEg) * col(exppbH) / L) -
                         np.mean(dEaq, axis=1) * np.mean(exppbH)) / np.mean(
                             exppbH)
             os.chdir('..')
         # Calculate the hydration free energy using gas phase, liquid phase or the average of both.
         # Note that the molecular dynamics methods return energies in kJ/mol.
         if self.hfemode == 'exp_gas':
             self.hfe_dict[label] = data['gas']['Hyd'] / 4.184
             self.hfe_err[label] = data['gas']['HydErr'] / 4.184
         elif self.hfemode == 'exp_liq':
             self.hfe_dict[label] = data['liq']['Hyd'] / 4.184
             self.hfe_err[label] = data['liq']['HydErr'] / 4.184
         elif self.hfemode == 'exp_both':
             self.hfe_dict[label] = 0.5 * (data['liq']['Hyd'] +
                                           data['gas']['Hyd']) / 4.184
             self.hfe_err[label] = 0.5 * (data['liq']['HydErr'] +
                                          data['gas']['HydErr']) / 4.184
         if AGrad:
             # Calculate the derivative of the hydration free energy.
             if self.hfemode == 'exp_gas':
                 dD[:,
                    ilabel] = self.whfe[ilabel] * data['gas']['dHyd'] / 4.184
             elif self.hfemode == 'exp_liq':
                 dD[:,
                    ilabel] = self.whfe[ilabel] * data['liq']['dHyd'] / 4.184
             elif self.hfemode == 'exp_both':
                 dD[:, ilabel] = 0.5 * self.whfe[ilabel] * (
                     data['liq']['dHyd'] + data['gas']['dHyd']) / 4.184
         os.chdir('..')
     calc_hfe = np.array(list(self.hfe_dict.values()))
     D = self.whfe * (calc_hfe - np.array(list(self.expval.values())))
     return D, dD
示例#9
0
 def covde(vec):
     return flat(np.mat(G)*col(vec))/L - Gbar*np.mean(vec)
示例#10
0
 def covde(vec):
     return flat(np.mat(G) * col(vec)) / L - Gbar * np.mean(vec)
示例#11
0
 def _compute(self, dx):
     self.dx = dx.copy()
     Tmp = np.mat(self.H)*col(dx)
     Reg_Term   = self.Penalty.compute(xkd+flat(dx), Obj0)
     self.Val   = (X + np.dot(dx, G) + 0.5*row(dx)*Tmp + Reg_Term[0] - data['X'])[0,0]
     self.Grad  = flat(col(G) + Tmp) + Reg_Term[1]
示例#12
0
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)
示例#13
0
    def step(self, xk, data, trust):
        """ Computes the next step in the parameter space.  There are lots of tricks here that I will document later.

        @param[in] G The gradient
        @param[in] H The Hessian
        @param[in] trust The trust radius
        
        """
        from scipy import optimize

        X, G, H = (data['X0'], data['G0'], data['H0']) if self.bhyp else (data['X'], data['G'], data['H'])
        H1 = H.copy()
        H1 = np.delete(H1, self.excision, axis=0)
        H1 = np.delete(H1, self.excision, axis=1)
        Eig = eig(H1)[0]            # Diagonalize Hessian
        Emin = min(Eig)
        if Emin < self.eps:         # Mix in SD step if Hessian minimum eigenvalue is negative
            # Experiment.
            Adj = max(self.eps, 0.01*abs(Emin)) - Emin
            logger.info("Hessian has a small or negative eigenvalue (%.1e), mixing in some steepest descent (%.1e) to correct this.\n" % (Emin, Adj))
            logger.info("Eigenvalues are:\n")   ###
            pvec1d(Eig)                ###
            H += Adj*np.eye(H.shape[0])

        if self.bhyp:
            G = np.delete(G, self.excision)
            H = np.delete(H, self.excision, axis=0)
            H = np.delete(H, self.excision, axis=1)
            xkd = np.delete(xk, self.excision)
            if self.Objective.Penalty.fmul != 0.0:
                warn_press_key("Using the multiplicative hyperbolic penalty is discouraged!")
            # This is the gradient and Hessian without the contributions from the hyperbolic constraint.
            Obj0 = {'X':X,'G':G,'H':H}
            class Hyper(object):
                def __init__(self, HL, Penalty):
                    self.H = HL.copy()
                    self.dx = 1e10 * np.ones(len(HL),dtype=float)
                    self.Val = 0
                    self.Grad = np.zeros(len(HL),dtype=float)
                    self.Hess = np.zeros((len(HL),len(HL)),dtype=float)
                    self.Penalty = Penalty
                def _compute(self, dx):
                    self.dx = dx.copy()
                    Tmp = np.mat(self.H)*col(dx)
                    Reg_Term   = self.Penalty.compute(xkd+flat(dx), Obj0)
                    self.Val   = (X + np.dot(dx, G) + 0.5*row(dx)*Tmp + Reg_Term[0] - data['X'])[0,0]
                    self.Grad  = flat(col(G) + Tmp) + Reg_Term[1]
                def compute_val(self, dx):
                    if norm(dx - self.dx) > 1e-8:
                        self._compute(dx)
                    return self.Val
                def compute_grad(self, dx):
                    if norm(dx - self.dx) > 1e-8:
                        self._compute(dx)
                    return self.Grad
                def compute_hess(self, dx):
                    if norm(dx - self.dx) > 1e-8:
                        self._compute(dx)
                    return self.Hess
            def hyper_solver(L):
                dx0 = np.zeros(len(xkd),dtype=float)
                #dx0 = np.delete(dx0, self.excision)
                # HL = H + (L-1)**2*np.diag(np.diag(H))
                # Attempt to use plain Levenberg
                HL = H + (L-1)**2*np.eye(len(H))

                HYP = Hyper(HL, self.Objective.Penalty)
                try:
                    Opt1 = optimize.fmin_bfgs(HYP.compute_val,dx0,fprime=HYP.compute_grad,gtol=1e-5,full_output=True,disp=0)
                except:
                    Opt1 = optimize.fmin(HYP.compute_val,dx0,full_output=True,disp=0)
                try:
                    Opt2 = optimize.fmin_bfgs(HYP.compute_val,-xkd,fprime=HYP.compute_grad,gtol=1e-5,full_output=True,disp=0)
                except:
                    Opt2 = optimize.fmin(HYP.compute_val,-xkd,full_output=True,disp=0)
                #Opt2 = optimize.fmin(HYP.compute_val,-xkd,full_output=True,disp=0)
                dx1, sol1 = Opt1[0], Opt1[1]
                dx2, sol2 = Opt2[0], Opt2[1]
                dxb, sol = (dx1, sol1) if sol1 <= sol2 else (dx2, sol2)
                for i in self.excision:    # Reinsert deleted coordinates - don't take a step in those directions
                    dxb = np.insert(dxb, i, 0)
                return dxb, sol
        else:
            # G0 and H0 are used for determining the expected function change.
            G0 = G.copy()
            H0 = H.copy()
            G = np.delete(G, self.excision)
            H = np.delete(H, self.excision, axis=0)
            H = np.delete(H, self.excision, axis=1)
            
            logger.debug("Inverting Hessian:\n")                 ###
            logger.debug(" G:\n")                                ###
            pvec1d(G,precision=5, loglevel=DEBUG)                ###
            logger.debug(" H:\n")                                ###
            pmat2d(H,precision=5, loglevel=DEBUG)                ###
            
            Hi = invert_svd(np.mat(H))
            dx = flat(-1 * Hi * col(G))
            
            logger.debug(" dx:\n")                               ###
            pvec1d(dx,precision=5, loglevel=DEBUG)                     ###
            # dxa = -solve(H, G)          # Take Newton Raphson Step ; use -1*G if want steepest descent.
            # dxa = flat(dxa)
            # print " dxa:"                              ###
            # pvec1d(dxa,precision=5)                    ###
            
            logger.info('\n')                                      ###
            for i in self.excision:    # Reinsert deleted coordinates - don't take a step in those directions
                dx = np.insert(dx, i, 0)
            def para_solver(L):
                # Levenberg-Marquardt
                # HT = H + (L-1)**2*np.diag(np.diag(H))
                # Attempt to use plain Levenberg
                HT = H + (L-1)**2*np.eye(len(H))
                logger.debug("Inverting Scaled Hessian:\n")                       ###
                logger.debug(" G:\n")                                             ###
                pvec1d(G,precision=5, loglevel=DEBUG)                                   ###
                logger.debug(" HT: (Scal = %.4f)\n" % (1+(L-1)**2))               ###
                pmat2d(HT,precision=5, loglevel=DEBUG)                                  ###
                Hi = invert_svd(np.mat(HT))
                dx = flat(-1 * Hi * col(G))
                logger.debug(" dx:\n")                                            ###
                pvec1d(dx,precision=5, loglevel=DEBUG)                                  ###
                # dxa = -solve(HT, G)
                # dxa = flat(dxa)
                # print " dxa:"                                           ###
                # pvec1d(dxa,precision=5)                                 ###
                # print                                                   ###
                sol = flat(0.5*row(dx)*np.mat(H)*col(dx))[0] + np.dot(dx,G)
                for i in self.excision:    # Reinsert deleted coordinates - don't take a step in those directions
                    dx = np.insert(dx, i, 0)
                return dx, sol
    
        def solver(L):
            return hyper_solver(L) if self.bhyp else para_solver(L)
    
        def trust_fun(L):
            N = norm(solver(L)[0])
            logger.debug("\rL = %.4e, Hessian diagonal addition = %.4e: found length %.4e, objective is %.4e\n" % (L, (L-1)**2, N, (N - trust)**2))
            return (N - trust)**2

        def search_fun(L):
            # Evaluate ONLY the objective function.  Most useful when
            # the objective is cheap, but the derivative is expensive.
            dx, sol = solver(L) # dx is how much the step changes from the previous step.
            # This is our trial step.
            xk_ = dx + xk
            Result = self.Objective.Full(xk_,0,verbose=False)['X'] - data['X']
            logger.info("Searching! Hessian diagonal addition = %.4e, L = % .4e, length %.4e, result %.4e\n" % ((L-1)**2,L,norm(dx),Result))
            return Result
        
        if self.trust0 > 0: # This is the trust region code.
            bump = False
            dx, expect = solver(1)
            dxnorm = norm(dx)
            if dxnorm > trust:
                bump = True
                # Tried a few optimizers here, seems like Brent works well.
                # Okay, the problem with Brent is that the tolerance is fractional.  
                # If the optimized value is zero, then it takes a lot of meaningless steps.
                LOpt = optimize.brent(trust_fun,brack=(self.lmg,self.lmg*4),tol=1e-6)
                ### Result = optimize.fmin_powell(trust_fun,3,xtol=self.search_tol,ftol=self.search_tol,full_output=1,disp=0)
                ### LOpt = Result[0]
                dx, expect = solver(LOpt)
                dxnorm = norm(dx)

                logger.info("\rLevenberg-Marquardt: %s step found (length %.3e), % .8f added to Hessian diagonal\n" % ('hyperbolic-regularized' if self.bhyp else 'Newton-Raphson', dxnorm, (LOpt-1)**2))
        else: # This is the nonlinear search code.
            # First obtain a step that is the same length as the provided trust radius.
            LOpt = optimize.brent(trust_fun,brack=(self.lmg,self.lmg*4),tol=1e-6)
            bump = False
            Result = optimize.brent(search_fun,brack=(LOpt,LOpt*4),tol=self.search_tol,full_output=1)
            ### optimize.fmin(search_fun,0,xtol=1e-8,ftol=data['X']*0.1,full_output=1,disp=0)
            ### Result = optimize.fmin_powell(search_fun,3,xtol=self.search_tol,ftol=self.search_tol,full_output=1,disp=0)
            dx, _ = solver(Result[0])
            expect = Result[1]

        ## Decide which parameters to redirect.
        ## Currently not used.
        if self.Objective.Penalty.ptyp in [3,4,5]:
            self.FF.make_redirect(dx+xk)

        return dx, expect, bump
示例#14
0
    def MainOptimizer(self,b_BFGS=0):
        """ The main ForceBalance adaptive trust-radius pseudo-Newton optimizer.  Tried and true in many situations. :)

        Usually this function is called with the BFGS or NewtonRaphson
        method.  The NewtonRaphson method is consistently the best
        method I have, because I always provide at least an
        approximate Hessian to the objective function.  The BFGS
        method is vestigial and currently does not work.

        BFGS is a pseudo-Newton method in the sense that it builds an
        approximate Hessian matrix from the gradient information in previous
        steps; Newton-Raphson requires the actual Hessian matrix.
        However, the algorithms are similar in that they both compute the
        step by inverting the Hessian and multiplying by the gradient.

        The method adaptively changes the step size.  If the step is
        sufficiently good (i.e. the objective function goes down by a
        large fraction of the predicted decrease), then the step size
        is increased; if the step is bad, then it rejects the step and
        tries again.

        The optimization is terminated after either a function value or
        step size tolerance is reached.

        @param[in] b_BFGS Switch to use BFGS (True) or Newton-Raphson (False)

        """
        if any(['liquid' in tgt.name.lower() for tgt in self.Objective.Targets]) and self.conv_obj < 1e-3:
            warn_press_key("Condensed phase targets detected - may not converge with current choice of convergence_objective (%.e)\nRecommended range is 1e-2 - 1e-1 for this option." % self.conv_obj)
        # Parameters for the adaptive trust radius
        a = self.adapt_fac  # Default value is 0.5, decrease to make more conservative.  Zero to turn off all adaptive.
        b = self.adapt_damp # Default value is 0.5, increase to make more conservative
        printcool( "Main Optimizer\n%s Mode%s" % ("BFGS" if b_BFGS else "Newton-Raphson", " (Static Radius)" if a == 0.0 else " (Adaptive Radius)"), ansi=1, bold=1)
        # First, set a bunch of starting values
        Ord         = 1 if b_BFGS else 2
        #Ord         = 2
        global ITERATION_NUMBER
        ITERATION_NUMBER = 0
        global GOODSTEP
        Best_Step = 1
        if all(i in self.chk for i in ['xk','X','G','H','ehist','x_best','xk_prev','trust']):
            logger.info("Reading initial objective, gradient, Hessian from checkpoint file\n")
            xk, X, G, H, ehist     = self.chk['xk'], self.chk['X'], self.chk['G'], self.chk['H'], self.chk['ehist']
            X_best, xk_prev, trust = self.chk['x_best'], self.chk['xk_prev'], self.chk['trust']
        else:
            xk       = self.mvals0.copy()
            logger.info('\n')
            data     = self.Objective.Full(xk,Ord,verbose=True)
            X, G, H  = data['X'], data['G'], data['H']
            ehist    = np.array([X])
            xk_prev  = xk.copy()
            trust    = abs(self.trust0)
            X_best   = X

        X_prev   = X
        G_prev   = G.copy()
        H_stor   = H.copy()
        ndx    = 0.0
        color  = "\x1b[1m"
        nxk = norm(xk)
        ngr = norm(G)

        Quality  = 0.0
        restep = False
        GOODSTEP = 1
        Ord         = 1 if b_BFGS else 2

        while 1: # Loop until convergence is reached.
            ## Put data into the checkpoint file
            self.chk = {'xk': xk, 'X' : X, 'G' : G, 'H': H, 'ehist': ehist,
                        'x_best': X_best,'xk_prev': xk_prev, 'trust': trust}
            if self.wchk_step:
                self.writechk()
            stdfront = len(ehist) > self.hist and np.std(np.sort(ehist)[:self.hist]) or (len(ehist) > 0 and np.std(ehist) or 0.0)
            stdfront *= 2
            logger.info("%6s%12s%12s%12s%14s%12s%12s\n" % ("Step", "  |k|  ","  |dk|  "," |grad| ","    -=X2=-  ","Delta(X2)", "StepQual"))
            logger.info("%6i%12.3e%12.3e%12.3e%s%14.5e\x1b[0m%12.3e% 11.3f\n\n" % (ITERATION_NUMBER, nxk, ndx, ngr, color, X, stdfront, Quality))
            # Check the convergence criteria
            if ngr < self.conv_grd:
                logger.info("Convergence criterion reached for gradient norm (%.2e)\n" % self.conv_grd)
                break
            if ITERATION_NUMBER == self.maxstep:
                logger.info("Maximum number of optimization steps reached (%i)\n" % ITERATION_NUMBER)
                break
            if ndx < self.conv_stp and ITERATION_NUMBER > 0 and not restep:
                logger.info("Convergence criterion reached in step size (%.2e)\n" % self.conv_stp)
                break
            if stdfront < self.conv_obj and len(ehist) > self.hist and not restep: # Factor of two is so [0,1] stdev is normalized to 1
                logger.info("Convergence criterion reached for objective function (%.2e)\n" % self.conv_obj)
                break
            if self.print_grad:
                bar = printcool("Total Gradient",color=4)
                self.FF.print_map(vals=G,precision=8)
                logger.info(bar)
            if self.print_hess:
                bar = printcool("Total Hessian",color=4)
                pmat2d(H,precision=8)
                logger.info(bar)
            for key, val in self.Objective.ObjDict.items():
                if Best_Step:
                    self.Objective.ObjDict_Last[key] = val
            restep = False
            dx, dX_expect, bump = self.step(xk, data, trust)
            old_pk = self.FF.create_pvals(xk)
            old_xk = xk.copy()
            # Increment the iteration counter.
            ITERATION_NUMBER += 1
            # Take a step in the parameter space.
            xk += dx
            if self.print_vals:
                pk = self.FF.create_pvals(xk)
                dp = pk - old_pk
                bar = printcool("Mathematical Parameters (Current + Step = Next)",color=5)
                self.FF.print_map(vals=["% .4e %s %.4e = % .4e" % (old_xk[i], '+' if dx[i] >= 0 else '-', abs(dx[i]), xk[i]) for i in range(len(xk))])
                logger.info(bar)
                bar = printcool("Physical Parameters (Current + Step = Next)",color=5)
                self.FF.print_map(vals=["% .4e %s %.4e = % .4e" % (old_pk[i], '+' if dp[i] >= 0 else '-', abs(dp[i]), pk[i]) for i in range(len(pk))])
                logger.info(bar)
            # Evaluate the objective function and its derivatives.
            data        = self.Objective.Full(xk,Ord,verbose=True)
            X, G, H = data['X'], data['G'], data['H']
            ndx = norm(dx)
            nxk = norm(xk)
            ngr = norm(G)
            drc = abs(flat(dx)).argmax()

            dX_actual = X - X_prev
            try:
                Quality = dX_actual / dX_expect
            except:
                logger.warning("Warning: Step size of zero detected (i.e. wrong direction).  Try reducing the finite_difference_h parameter\n")
                Quality = 1.0 # This is a step length of zero.

            if Quality <= 0.25 and X < (X_prev + self.err_tol) and self.trust0 > 0:
                # If the step quality is bad, then we should decrease the trust radius.
                trust = max(ndx*(1./(1+a)), self.mintrust)
                logger.info("Low quality step, reducing trust radius to % .4e\n" % trust)
            if Quality >= 0.75 and bump and self.trust0 > 0:
                # If the step quality is good, then we should increase the trust radius.
                # The 'a' factor is how much we should grow or shrink the trust radius each step
                # and the 'b' factor determines how closely we are tied down to the original value.
                # Recommend values 0.5 and 0.5
                trust += a*trust*np.exp(-b*(trust/self.trust0 - 1))
            if X > (X_prev + self.err_tol):
                Best_Step = 0
                # Toggle switch for rejection (experimenting with no rejection)
                Rejects = True
                GOODSTEP = 0
                Reevaluate = True
                trust = max(ndx*(1./(1+a)), self.mintrust)
                logger.info("Rejecting step and reducing trust radius to % .4e\n" % trust)
                if Rejects:
                    xk = xk_prev.copy()
                    if Reevaluate:
                        restep = True
                        color = "\x1b[91m"
                        logger.info("%6s%12s%12s%12s%14s%12s%12s\n" % ("Step", "  |k|  ","  |dk|  "," |grad| ","    -=X2=-  ","Delta(X2)", "StepQual"))
                        logger.info("%6i%12.3e%12.3e%12.3e%s%14.5e\x1b[0m%12.3e% 11.3f\n\n" % (ITERATION_NUMBER, nxk, ndx, ngr, color, X, stdfront, Quality))
                        printcool("Objective function rises!\nRe-evaluating at the previous point..",color=1)
                        ITERATION_NUMBER += 1
                        data        = self.Objective.Full(xk,Ord,verbose=True)
                        GOODSTEP = 1
                        X, G, H = data['X'], data['G'], data['H']
                        X_prev = X
                        dx *= 0
                        ndx = norm(dx)
                        nxk = norm(xk)
                        ngr = norm(G)
                        Quality = 0.0
                        color = "\x1b[0m"
                    else:
                        color = "\x1b[91m"
                        G = G_prev.copy()
                        H = H_stor.copy()
                        data = deepcopy(datastor)
                    continue
            else:
                GOODSTEP = 1
                if X > X_best:
                    Best_Step = 0
                    color = "\x1b[95m"
                else:
                    Best_Step = 1
                    color = "\x1b[92m"
                    X_best = X
                ehist = np.append(ehist, X)
            # Hessian update for BFGS.
            if b_BFGS:
                Hnew = H_stor.copy()
                Dx   = col(xk - xk_prev)
                Dy   = col(G  - G_prev)
                Mat1 = (Dy*Dy.T)/(Dy.T*Dx)[0,0]
                Mat2 = ((Hnew*Dx)*(Hnew*Dx).T)/(Dx.T*Hnew*Dx)[0,0]
                Hnew += Mat1-Mat2
                H = Hnew.copy()
                data['H'] = H.copy()

            datastor= deepcopy(data)
            G_prev  = G.copy()
            H_stor  = H.copy()
            xk_prev = xk.copy()
            X_prev  = X
            if len(self.FF.parmdestroy_this) > 0:
                self.FF.parmdestroy_save.append(self.FF.parmdestroy_this)
                self.FF.linedestroy_save.append(self.FF.linedestroy_this)
        
        bar = printcool("Final objective function value\nFull: % .6e  Un-penalized: % .6e" % (data['X'],data['X0']), '@', bold=True, color=2)
        return xk
示例#15
0
    def extract(self, engines, FF, mvals, h, pgrad, AGrad=True):
        #==========================================#
        #  Physical constants and local variables. #
        #==========================================#
        # Energies in kJ/mol and lengths in nanometers.
        kB = 0.008314472471220214
        kT = kB * self.temperature
        Beta = 1.0 / kT
        mBeta = -Beta
        # Conversion factor between 1 kJ/mol -> bar nm^3
        pconv = 16.6054

        # Number of molecules in the liquid phase.
        mol = Molecule(
            os.path.basename(os.path.splitext(engines[0].mdtraj)[0]) + ".gro")
        nmol = len(mol.molecules)

        #======================================================#
        #  Get simulation properties depending on the engines. #
        #======================================================#
        if self.engname == "gromacs":
            # Default names
            deffnm1 = os.path.basename(os.path.splitext(engines[0].mdene)[0])
            deffnm2 = os.path.basename(os.path.splitext(engines[1].mdene)[0])
            # Figure out which energy terms and present and their order.
            energyterms1 = engines[0].energy_termnames(edrfile="%s.%s" %
                                                       (deffnm1, "edr"))
            energyterms2 = engines[1].energy_termnames(edrfile="%s.%s" %
                                                       (deffnm2, "edr"))
            # Grab energy terms to print and keep track of energy term order.
            ekeep1 = [
                'Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature',
                'Volume'
            ]
            ekeep2 = [
                'Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature'
            ]

            ekeep_order1 = [
                key
                for (key, value
                     ) in sorted(energyterms1.items(), key=lambda (k, v): v)
                if key in ekeep1
            ]
            ekeep_order2 = [
                key
                for (key, value
                     ) in sorted(energyterms2.items(), key=lambda (k, v): v)
                if key in ekeep2
            ]

            # Perform energy component analysis and return properties.
            engines[0].callgmx(
                ("g_energy " + "-f %s.%s " %
                 (deffnm1, "edr") + "-o %s-energy.xvg " % deffnm1 + "-xvg no"),
                stdin="\n".join(ekeep1))
            engines[1].callgmx(
                ("g_energy " + "-f %s.%s " %
                 (deffnm2, "edr") + "-o %s-energy.xvg " % deffnm2 + "-xvg no"),
                stdin="\n".join(ekeep2))

        # Read data and store properties by grabbing columns in right order.
        data1 = np.loadtxt("%s-energy.xvg" % deffnm1)
        data2 = np.loadtxt("%s-energy.xvg" % deffnm2)
        Energy = data1[:, ekeep_order1.index("Total-Energy") + 1]
        Potential = data1[:, ekeep_order1.index("Potential") + 1]
        Kinetic = data1[:, ekeep_order1.index("Kinetic-En.") + 1]
        Temperature = data1[:, ekeep_order1.index("Temperature") + 1]
        Volume = data1[:, ekeep_order1.index("Volume") + 1]
        mEnergy = data2[:, ekeep_order2.index("Total-Energy") + 1]
        mPotential = data2[:, ekeep_order2.index("Potential") + 1]
        mKinetic = data2[:, ekeep_order2.index("Kinetic-En.") + 1]

        #============================================#
        #  Compute the potential energy derivatives. #
        #============================================#
        logger.info(("Calculating potential energy derivatives " +
                     "with finite difference step size: %f\n" % h))
        printcool("Initializing arrays to lengths %d" % len(Energy),
                  color=4,
                  bold=True)

        G = energy_derivatives(engines[0], FF, mvals, h, pgrad, len(Energy),
                               AGrad)
        Gm = energy_derivatives(engines[1], FF, mvals, h, pgrad, len(mEnergy),
                                AGrad)

        #=======================================#
        #  Quantity properties and derivatives. #
        #=======================================#
        # Average and error.
        E_avg, E_err = mean_stderr(Energy)
        Em_avg, Em_err = mean_stderr(mEnergy)
        Vol_avg, Vol_err = mean_stderr(Volume)

        Hvap_avg = Em_avg - E_avg / nmol - self.pressure * Vol_avg / nmol / pconv + kT
        Hvap_err = np.sqrt((E_err / nmol)**2 + Em_err**2 + (self.pressure**2) *
                           (Vol_err**2) / (float(nmol)**2) / (pconv**2))
        # Analytic first derivative.
        Hvap_grad = np.mean(Gm, axis=1)
        Hvap_grad += mBeta * (flat(np.mat(Gm) * col(mEnergy)) / len(mEnergy) \
                               - np.mean(mEnergy) * np.mean(Gm, axis=1))
        Hvap_grad -= np.mean(G, axis=1) / nmol
        Hvap_grad += Beta * (flat(np.mat(G) * col(Energy)) / len(Energy) \
                               - np.mean(Energy) * np.mean(G, axis=1))/nmol
        Hvap_grad += (Beta*self.pressure/nmol/pconv) * \
          (flat(np.mat(G) * col(Volume)) / len(Volume) \
           - np.mean(Volume) * np.mean(G, axis=1))

        return Hvap_avg, Hvap_err, Hvap_grad
示例#16
0
    def extract(self, engines, FF, mvals, h, pgrad, AGrad=True):
        #==========================================#
        #  Physical constants and local variables. #
        #==========================================#
        # Energies in kJ/mol and lengths in nanometers.
        kB = 0.008314472471220214
        kT = kB * self.temperature
        Beta = 1.0 / kT
        mBeta = -Beta

        #======================================================#
        #  Get simulation properties depending on the engines. #
        #======================================================#
        if self.engname == "gromacs":
            # Default name
            deffnm = os.path.basename(os.path.splitext(engines[0].mdene)[0])
            # What energy terms are there and what is their order
            energyterms = engines[0].energy_termnames(edrfile="%s.%s" %
                                                      (deffnm, "edr"))
            # Grab energy terms to print and keep track of energy term order.
            ekeep = ['Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature']
            ekeep += ['Volume', 'Density']

            ekeep_order = [
                key for (
                    key,
                    value) in sorted(energyterms.items(), key=lambda (k, v): v)
                if key in ekeep
            ]

            # Perform energy component analysis and return properties.
            engines[0].callgmx(
                ("g_energy " + "-f %s.%s " %
                 (deffnm, "edr") + "-o %s-energy.xvg " % deffnm + "-xvg no"),
                stdin="\n".join(ekeep))

        # Read data and store properties by grabbing columns in right order.
        data = np.loadtxt("%s-energy.xvg" % deffnm)
        Energy = data[:, ekeep_order.index("Total-Energy") + 1]
        Potential = data[:, ekeep_order.index("Potential") + 1]
        Kinetic = data[:, ekeep_order.index("Kinetic-En.") + 1]
        Volume = data[:, ekeep_order.index("Volume") + 1]
        Temperature = data[:, ekeep_order.index("Temperature") + 1]
        Density = data[:, ekeep_order.index("Density") + 1]

        #============================================#
        #  Compute the potential energy derivatives. #
        #============================================#
        logger.info(("Calculating potential energy derivatives " +
                     "with finite difference step size: %f\n" % h))
        printcool("Initializing array to length %i" % len(Energy),
                  color=4,
                  bold=True)
        G = energy_derivatives(engines[0], FF, mvals, h, pgrad, len(Energy),
                               AGrad)

        #=======================================#
        #  Quantity properties and derivatives. #
        #=======================================#
        # Average and error.
        Rho_avg, Rho_err = mean_stderr(Density)
        # Analytic first derivative.
        Rho_grad = mBeta * (flat(np.mat(G) * col(Density)) / len(Density) \
                            - np.mean(Density) * np.mean(G, axis=1))

        return Rho_avg, Rho_err, Rho_grad
示例#17
0
 def create_mvals(pvals):
     return flat(numpy.dot(inverse_tm_i, col(pvals - force_field.pvals0)))
示例#18
0
 def deprod(vec):
     return flat(np.mat(G)*col(vec))/L
示例#19
0
 def deprod(vec):
     return flat(np.dot(G,col(vec)))/L
示例#20
0
 def deprod(vec):
     return flat(np.mat(G) * col(vec)) / L
示例#21
0
 def covde(vec):
     return flat(np.dot(G,col(vec)))/L - Gbar*np.mean(vec)
示例#22
0
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')
示例#23
0
    def extract(self, engines, FF, mvals, h, pgrad, AGrad=True):         
        #==========================================#
        #  Physical constants and local variables. #
        #==========================================#
        # Energies in kJ/mol and lengths in nanometers.
        kB    = 0.008314472471220214
        kT    = kB*self.temperature
        Beta  = 1.0/kT
        mBeta = -Beta
 
        #======================================================#
        #  Get simulation properties depending on the engines. #
        #======================================================#
        if self.engname == "gromacs":
            # Default name
            deffnm = os.path.basename(os.path.splitext(engines[0].mdene)[0])
            # What energy terms are there and what is their order
            energyterms = engines[0].energy_termnames(edrfile="%s.%s" % (deffnm, "edr"))
            # Grab energy terms to print and keep track of energy term order.
            ekeep  = ['Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature']
            ekeep += ['Volume', 'Density']

            ekeep_order = [key for (key, value) in
                           sorted(energyterms.items(), key=lambda (k, v) : v)
                           if key in ekeep]

            # Perform energy component analysis and return properties.
            engines[0].callgmx(("g_energy " +
                                "-f %s.%s " % (deffnm, "edr") +
                                "-o %s-energy.xvg " % deffnm +
                                "-xvg no"),
                                stdin="\n".join(ekeep))
            
        # Read data and store properties by grabbing columns in right order.
        data        = np.loadtxt("%s-energy.xvg" % deffnm)            
        Energy      = data[:, ekeep_order.index("Total-Energy") + 1]
        Potential   = data[:, ekeep_order.index("Potential") + 1]
        Kinetic     = data[:, ekeep_order.index("Kinetic-En.") + 1]
        Volume      = data[:, ekeep_order.index("Volume") + 1]
        Temperature = data[:, ekeep_order.index("Temperature") + 1]
        Density     = data[:, ekeep_order.index("Density") + 1]

        #============================================#
        #  Compute the potential energy derivatives. #
        #============================================#
        logger.info(("Calculating potential energy derivatives " +
                     "with finite difference step size: %f\n" % h))
        printcool("Initializing array to length %i" % len(Energy),
                  color=4, bold=True)    
        G = energy_derivatives(engines[0], FF, mvals, h, pgrad, len(Energy), AGrad)
        
        #=======================================#
        #  Quantity properties and derivatives. #
        #=======================================#
        # Average and error.
        Rho_avg, Rho_err = mean_stderr(Density)
        # Analytic first derivative.
        Rho_grad = mBeta * (flat(np.mat(G) * col(Density)) / len(Density) \
                            - np.mean(Density) * np.mean(G, axis=1))
            
        return Rho_avg, Rho_err, Rho_grad
示例#24
0
 def covde(vec):
     return flat(np.dot(G,col(vec)))/L - Gbar*np.mean(vec)
示例#25
0
    def extract(self, engines, FF, mvals, h, pgrad, AGrad=True): 
        #==========================================#
        #  Physical constants and local variables. #
        #==========================================#
        # Energies in kJ/mol and lengths in nanometers.
        kB      = 0.008314472471220214
        kT      = kB*self.temperature
        Beta    = 1.0/kT
        mBeta   = -Beta
        # Conversion factor between 1 kJ/mol -> bar nm^3 
        pconv   = 16.6054

        # Number of molecules in the liquid phase.
        mol     = Molecule(os.path.basename(os.path.splitext(engines[0].mdtraj)[0]) +
                           ".gro")
        nmol = len(mol.molecules)

        #======================================================#
        #  Get simulation properties depending on the engines. #
        #======================================================#
        if self.engname == "gromacs":
            # Default names
            deffnm1 = os.path.basename(os.path.splitext(engines[0].mdene)[0])
            deffnm2 = os.path.basename(os.path.splitext(engines[1].mdene)[0])
            # Figure out which energy terms and present and their order. 
            energyterms1 = engines[0].energy_termnames(edrfile="%s.%s" % (deffnm1, "edr"))
            energyterms2 = engines[1].energy_termnames(edrfile="%s.%s" % (deffnm2, "edr"))
            # Grab energy terms to print and keep track of energy term order.
            ekeep1  = ['Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature', 'Volume']
            ekeep2  = ['Total-Energy', 'Potential', 'Kinetic-En.', 'Temperature']

            ekeep_order1 = [key for (key, value)
                            in sorted(energyterms1.items(), key=lambda (k, v) : v)
                            if key in ekeep1]
            ekeep_order2 = [key for (key, value)
                            in sorted(energyterms2.items(), key=lambda (k, v) : v)
                            if key in ekeep2]

            # Perform energy component analysis and return properties.
            engines[0].callgmx(("g_energy " +
                                "-f %s.%s " % (deffnm1, "edr") +
                                "-o %s-energy.xvg " % deffnm1 +
                                "-xvg no"),
                                stdin="\n".join(ekeep1))
            engines[1].callgmx(("g_energy " +
                                "-f %s.%s " % (deffnm2, "edr") +
                                "-o %s-energy.xvg " % deffnm2 +
                                "-xvg no"),
                                stdin="\n".join(ekeep2))

        # Read data and store properties by grabbing columns in right order.
        data1       = np.loadtxt("%s-energy.xvg" % deffnm1)
        data2       = np.loadtxt("%s-energy.xvg" % deffnm2)
        Energy      = data1[:, ekeep_order1.index("Total-Energy") + 1]
        Potential   = data1[:, ekeep_order1.index("Potential") + 1]
        Kinetic     = data1[:, ekeep_order1.index("Kinetic-En.") + 1]
        Temperature = data1[:, ekeep_order1.index("Temperature") + 1]
        Volume      = data1[:, ekeep_order1.index("Volume") + 1]
        mEnergy     = data2[:, ekeep_order2.index("Total-Energy") + 1]
        mPotential  = data2[:, ekeep_order2.index("Potential") + 1]
        mKinetic    = data2[:, ekeep_order2.index("Kinetic-En.") + 1]
        
        #============================================#
        #  Compute the potential energy derivatives. #
        #============================================#
        logger.info(("Calculating potential energy derivatives " +
                     "with finite difference step size: %f\n" % h))
        printcool("Initializing arrays to lengths %d" % len(Energy),
                  color=4, bold=True)
        
        G  = energy_derivatives(engines[0], FF, mvals, h, pgrad, len(Energy), AGrad)
        Gm = energy_derivatives(engines[1], FF, mvals, h, pgrad, len(mEnergy), AGrad)
                
        #=======================================#
        #  Quantity properties and derivatives. #
        #=======================================#
        # Average and error.
        E_avg, E_err     = mean_stderr(Energy)
        Em_avg, Em_err   = mean_stderr(mEnergy)
        Vol_avg, Vol_err = mean_stderr(Volume)
                
        Hvap_avg = Em_avg - E_avg/nmol - self.pressure*Vol_avg/nmol/pconv + kT 
        Hvap_err = np.sqrt((E_err/nmol)**2 + Em_err**2
                           + (self.pressure**2) * (Vol_err**2)/(float(nmol)**2)/(pconv**2))
        # Analytic first derivative.
        Hvap_grad  = np.mean(Gm, axis=1)
        Hvap_grad += mBeta * (flat(np.mat(Gm) * col(mEnergy)) / len(mEnergy) \
                               - np.mean(mEnergy) * np.mean(Gm, axis=1))
        Hvap_grad -= np.mean(G, axis=1)/nmol
        Hvap_grad += Beta * (flat(np.mat(G) * col(Energy)) / len(Energy) \
                               - np.mean(Energy) * np.mean(G, axis=1))/nmol
        Hvap_grad += (Beta*self.pressure/nmol/pconv) * \
          (flat(np.mat(G) * col(Volume)) / len(Volume) \
           - np.mean(Volume) * np.mean(G, axis=1))

        return Hvap_avg, Hvap_err, Hvap_grad