Esempio n. 1
0
def mean_stderr(ts):
    """ Get mean and standard deviation of a time series. """
    if ts.ndim == 1:
        return np.mean(ts, axis=0), np.std(ts, axis=0) * np.sqrt(
            statisticalInefficiency(ts, warn=False) / len(ts))
    else:
        return np.mean(ts, axis=0), np.std(ts, axis=0) * np.sqrt(
            multiD_statisticalInefficiency(ts, warn=False) / len(ts))
Esempio n. 2
0
def mean_stderr(ts):
    """ Get mean and standard deviation of a time series. """
    return np.mean(ts), np.std(ts)*np.sqrt(statisticalInefficiency(ts, warn=False)/len(ts))
Esempio n. 3
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')
Esempio n. 4
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)
Esempio n. 5
0
def mean_stderr(ts):
    """ Get mean and standard deviation of a time series. """
    return np.mean(ts), np.std(ts)*np.sqrt(statisticalInefficiency(ts, warn=False)/len(ts))
Esempio n. 6
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
Esempio n. 7
0
def mean_stderr(ts):
    """ Get mean and standard deviation of a time series. """
    if ts.ndim == 1:
        return np.mean(ts, axis = 0), np.std(ts, axis = 0)*np.sqrt(statisticalInefficiency(ts, warn=False)/len(ts))
    else:
        return np.mean(ts, axis = 0), np.std(ts, axis = 0)*np.sqrt(multiD_statisticalInefficiency(ts, warn=False)/len(ts))
Esempio n. 8
0
kappa_boot = []
cp_boot = []

numboots = 1000
for i in range(numboots):
    boot = np.random.randint(L, size=L)
    alpha_hpke_boot.append(calc_alpha(h_pke[boot], vols[boot]))
    alpha_hcvke_boot.append(calc_alpha(h_cvke[boot], vols[boot]))
    kappa_boot.append(calc_kappa(vols[boot]))
    cp_boot.append(calc_cv(h_pke[boot], corrs[boot]))
alpha_hpke_boot = np.array(alpha_hpke_boot)
alpha_hcvke_boot = np.array(alpha_hcvke_boot)
kappa_boot = np.array(kappa_boot)
cp_boot = np.array(cp_boot)

alpha_hpke_err = np.std(alpha_hpke_boot) * max([np.sqrt(statisticalInefficiency(vols)),np.sqrt(statisticalInefficiency(h_pke))])
alpha_hcvke_err = np.std(alpha_hcvke_boot) * max([np.sqrt(statisticalInefficiency(vols)),np.sqrt(statisticalInefficiency(h_cvke))])
kappa_err = np.std(kappa_boot) * np.sqrt(statisticalInefficiency(vols))
cp_err = np.std(cp_boot) * np.sqrt(statisticalInefficiency(h_pke))

# cululative averages
plt.figure(figsize=(10,10))
plt.axhline(2.572, color='r', linewidth=2, label='Experiment')
plt.plot(time, alpha_hpke, '.', color='k', label='PKE')
plt.errorbar(time[-1], alpha_hpke[-1], yerr=alpha_hpke_err, color='k', linewidth=1.5)
plt.plot(time, alpha_hcvke, '.', color='c', label='CVKE')
plt.errorbar(time[-1], alpha_hcvke[-1], yerr=alpha_hcvke_err, color='c', linewidth=1.5)
plt.title('Thermal expansion coefficient')
plt.xlabel('Time (ps)')
plt.ylabel(r'$\alpha$ (10^-4 K^-1)')
#plt.xlim([0,400])
Esempio n. 9
0
def main():

    """
    Usage: (runcuda.sh) nvt.py <openmm|gromacs|tinker> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature>

    This program is meant to be called automatically by ForceBalance on
    a GPU cluster (specifically, subroutines in openmmio.py).  It is
    not easy to use manually.  This is because the force field is read
    in from a ForceBalance 'FF' class.
    """

    printcool("ForceBalance condensed phase NVT simulation using engine: %s" % engname.upper(), color=4, bold=True)

    #----
    # Load the ForceBalance pickle file which contains:
    #----
    # - Force field object
    # - Optimization parameters
    # - Options from the Target object that launched this simulation
    # - Switch for whether to evaluate analytic derivatives.
    FF,mvals,TgtOptions,AGrad = lp_load('forcebalance.p')
    FF.ffdir = '.'
    # Write the force field file.
    FF.make(mvals)

    #----
    # Load the options that are set in the ForceBalance input file.
    #----
    # Finite difference step size
    h = TgtOptions['h']
    pgrad = TgtOptions['pgrad']
    # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps)
    nvt_timestep = TgtOptions['nvt_timestep']
    nvt_md_steps = TgtOptions['nvt_md_steps']
    nvt_eq_steps = TgtOptions['nvt_eq_steps']
    nvt_interval = TgtOptions['nvt_interval']
    liquid_fnm = TgtOptions['nvt_coords']

    # Number of threads, multiple timestep integrator, anisotropic box etc.
    threads = TgtOptions.get('md_threads', 1)
    mts = TgtOptions.get('mts_integrator', 0)
    rpmd_beads = TgtOptions.get('rpmd_beads', 0)
    force_cuda = TgtOptions.get('force_cuda', 0)
    nbarostat = TgtOptions.get('n_mcbarostat', 25)
    anisotropic = TgtOptions.get('anisotropic_box', 0)
    minimize = TgtOptions.get('minimize_energy', 1)

    # Print all options.
    printcool_dictionary(TgtOptions, title="Options from ForceBalance")
    nvt_snapshots = (nvt_timestep * nvt_md_steps / 1000) / nvt_interval
    nvt_iframes = 1000 * nvt_interval / nvt_timestep
    logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \
        % (nvt_snapshots, nvt_iframes, nvt_timestep))
    if nvt_snapshots < 2:
        raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \
                            % (2000 * (nvt_interval/nvt_timestep)))

    #----
    # Loading coordinates
    #----
    ML = Molecule(liquid_fnm, toppbc=True)
    # Determine the number of molecules in the condensed phase coordinate file.
    NMol = len(ML.molecules)
    TgtOptions['n_molecules'] = NMol
    logger.info("There are %i molecules in the liquid\n" % (NMol))

    #----
    # Setting up MD simulations
    #----
    EngOpts = OrderedDict()
    EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML), ("pbc", True)])
    if "nonbonded_cutoff" in TgtOptions:
        EngOpts["liquid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"]
    else:
        largest_available_cutoff = min(ML.boxes[0][:3]) / 2 - 0.1
        EngOpts["liquid"]["nonbonded_cutoff"] = largest_available_cutoff
        logger.info("nonbonded_cutoff is by default set to the largest available value: %g Angstrom" %largest_available_cutoff)
    if "vdw_cutoff" in TgtOptions:
        EngOpts["liquid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"]
    # Hard Code nonbonded_cutoff to 13A for test
    #EngOpts["liquid"]["nonbonded_cutoff"] = EngOpts["liquid"]["vdw_cutoff"] = 13.0
    GenOpts = OrderedDict([('FF', FF)])
    if engname == "openmm":
        # OpenMM-specific options
        EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA')
        if force_cuda:
            try: Platform.getPlatformByName('CUDA')
            except: raise RuntimeError('Forcing failure because CUDA platform unavailable')
            EngOpts["liquid"]["platname"] = 'CUDA'
        if threads > 1: logger.warn("Setting the number of threads will have no effect on OpenMM engine.\n")

    EngOpts["liquid"].update(GenOpts)
    for i in EngOpts:
        printcool_dictionary(EngOpts[i], "Engine options for %s" % i)

    # Set up MD options
    MDOpts = OrderedDict()
    MDOpts["liquid"] = OrderedDict([("nsteps", nvt_md_steps), ("timestep", nvt_timestep),
                                    ("temperature", temperature),
                                    ("nequil", nvt_eq_steps), ("minimize", minimize),
                                    ("nsave", int(1000 * nvt_interval / nvt_timestep)),
                                    ("verbose", True),
                                    ('save_traj', TgtOptions['save_traj']),
                                    ("threads", threads),
                                    ("mts", mts), ("rpmd_beads", rpmd_beads), ("faststep", faststep)])

    # Energy components analysis disabled for OpenMM MTS because it uses force groups
    if (engname == "openmm" and mts): logger.warn("OpenMM with MTS integrator; energy components analysis will be disabled.\n")

    # Create instances of the MD Engine objects.
    Liquid = Engine(name="liquid", **EngOpts["liquid"])

    #=================================================================#
    # Run the simulation for the full system and analyze the results. #
    #=================================================================#

    printcool("Condensed phase NVT molecular dynamics", color=4, bold=True)
    click()
    prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"])
    logger.info("Liquid phase MD simulation took %.3f seconds\n" % click())
    Potentials = prop_return['Potentials']

    #============================================#
    #  Compute the potential energy derivatives. #
    #============================================#
    if AGrad:
        logger.info("Calculating potential energy derivatives with finite difference step size: %f\n" % h)
        # Switch for whether to compute the derivatives two different ways for consistency.
        FDCheck = False
        printcool("Condensed phase energy and dipole derivatives\nInitializing array to length %i" % len(Potentials), color=4, bold=True)
        click()
        G, GDx, GDy, GDz = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False)
        logger.info("Condensed phase energy derivatives took %.3f seconds\n" % click())

    #==============================================#
    #  Condensed phase properties and derivatives. #
    #==============================================#

    # Physical constants
    kB = 0.008314472471220214
    T = temperature
    kT = kB * T # Unit: kJ/mol

    #--- Surface Tension ----
    logger.info("Start Computing surface tension.\n")
    perturb_proportion = 0.0005
    box_vectors = np.array(Liquid.xyz_omms[0][1]/nanometer) # obtain original box vectors from first frame
    delta_S = np.sqrt(np.sum(np.cross(box_vectors[0], box_vectors[1])**2)) * perturb_proportion * 2 # unit: nm^2. *2 for 2 surfaces
    # perturb xy area +
    click()
    scale_x = scale_y = np.sqrt(1 + perturb_proportion)
    scale_z = 1.0 / (1+perturb_proportion) # keep the box volumn while changing the area of xy plane
    Liquid.scale_box(scale_x, scale_y, scale_z)
    logger.info("scale_box+ took %.3f seconds\n" %click())
    # Obtain energies and gradients
    Potentials_plus = Liquid.energy()
    logger.info("Calculation of energies for perturbed box+ took %.3f seconds\n" %click())
    if AGrad:
        G_plus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False)
        logger.info("Calculation of energy gradients for perturbed box+ took %.3f seconds\n" %click())
    # perturb xy area - ( Note: also need to cancel the previous scaling)
    scale_x = scale_y = np.sqrt(1 - perturb_proportion) * (1.0/scale_x)
    scale_z = 1.0 / (1-perturb_proportion) * (1.0/scale_z)
    Liquid.scale_box(scale_x, scale_y, scale_z)
    logger.info("scale_box- took %.3f seconds\n" %click())
    # Obtain energies and gradients
    Potentials_minus = Liquid.energy()
    logger.info("Calculation of energies for perturbed box- took %.3f seconds\n" %click())
    if AGrad:
        G_minus, _, _, _ = energy_derivatives(Liquid, FF, mvals, h, pgrad, len(Potentials), AGrad, dipole=False)
        logger.info("Calculation of energy gradients for perturbed box- took %.3f seconds\n" %click())
    # Compute surface tension
    dE_plus = Potentials_plus - Potentials # Unit: kJ/mol
    dE_minus = Potentials_minus - Potentials # Unit: kJ/mol
    prefactor = -0.5 * kT / delta_S / 6.0221409e-1 # Unit mJ m^-2
    # Following equation: γ = -kT/(2ΔS) * [ ln<exp(-ΔE+/kT)> - ln<exp(-ΔE-/kT)> ]
    #plus_avg, plus_err = mean_stderr(np.exp(-dE_plus/kT))
    #minus_avg, minus_err = mean_stderr(np.exp(-dE_minus/kT))
    #surf_ten = -0.5 * kT / delta_S * ( np.log(plus_avg) - np.log(minus_avg) ) / 6.0221409e-1 # convert to mJ m^-2
    #surf_ten_err = 0.5 * kT / delta_S * ( np.log(plus_avg+plus_err) - np.log(plus_avg-plus_err) + np.log(minus_avg+minus_err) - np.log(minus_avg-minus_err) ) / 6.0221409e-1
    exp_dE_plus = np.exp(-dE_plus/kT)
    exp_dE_minus = np.exp(-dE_minus/kT)
    surf_ten = prefactor * ( np.log(np.mean(exp_dE_plus)) - np.log(np.mean(exp_dE_minus)) )
    # Use bootstrap method to estimate the error
    num_frames = len(exp_dE_plus)
    numboots = 1000
    surf_ten_boots = np.zeros(numboots)
    for i in xrange(numboots):
        boots_ordering = np.random.randint(num_frames, size=num_frames)
        boots_exp_dE_plus = np.take(exp_dE_plus, boots_ordering)
        boots_exp_dE_minus = np.take(exp_dE_minus, boots_ordering)
        surf_ten_boots[i] = prefactor * ( np.log(np.mean(boots_exp_dE_plus)) - np.log(np.mean(boots_exp_dE_minus)) )
    surf_ten_err = np.std(surf_ten_boots) * np.sqrt(np.mean([statisticalInefficiency(exp_dE_plus), statisticalInefficiency(exp_dE_minus)]))

    printcool("Surface Tension:       % .4f +- %.4f mJ m^-2" % (surf_ten, surf_ten_err))
    # Analytic Gradient of surface tension
    # Formula:      β = 1/kT
    #           ∂γ/∂α = -kT/(2ΔS) * { 1/<exp(-βΔE+)> * [<-β ∂E+/∂α exp(-βΔE+)> - <-β ∂E/∂α><exp(-βΔE+)>]
    #                                -1/<exp(-βΔE-)> * [<-β ∂E-/∂α exp(-βΔE-)> - <-β ∂E/∂α><exp(-βΔE-)>] }
    n_params = len(mvals)
    G_surf_ten = np.zeros(n_params)
    if AGrad:
        beta = 1.0 / kT
        plus_denom = np.mean(np.exp(-beta*dE_plus))
        minus_denom = np.mean(np.exp(-beta*dE_minus))
        for param_i in xrange(n_params):
            plus_left = np.mean(-beta * G_plus[param_i] * np.exp(-beta*dE_plus))
            plus_right = np.mean(-beta * G[param_i]) * plus_denom
            minus_left = np.mean(-beta * G_minus[param_i] * np.exp(-beta*dE_minus))
            minus_right = np.mean(-beta * G[param_i]) * minus_denom
            G_surf_ten[param_i] = prefactor * (1.0/plus_denom*(plus_left-plus_right) - 1.0/minus_denom*(minus_left-minus_right))
        printcool("Analytic Derivatives:")
        FF.print_map(vals=G_surf_ten)

    logger.info("Writing final force field.\n")
    pvals = FF.make(mvals)

    logger.info("Writing all results to disk.\n")
    result_dict = {'surf_ten': surf_ten, 'surf_ten_err': surf_ten_err, 'G_surf_ten': G_surf_ten}
    lp_dump(result_dict, 'nvt_result.p')
Esempio n. 10
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')
Esempio n. 11
0
def mean_stderr(ts):
    return np.mean(ts), np.std(ts)*np.sqrt(statisticalInefficiency(ts,warn=False)/len(ts))
Esempio n. 12
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
Esempio n. 13
0
def main():
    """
    Usage: (runcuda.sh) nvt.py <openmm|gromacs|tinker> <liquid_nsteps> <liquid_timestep (fs)> <liquid_intvl (ps> <temperature>

    This program is meant to be called automatically by ForceBalance on
    a GPU cluster (specifically, subroutines in openmmio.py).  It is
    not easy to use manually.  This is because the force field is read
    in from a ForceBalance 'FF' class.
    """

    printcool("ForceBalance condensed phase NVT simulation using engine: %s" %
              engname.upper(),
              color=4,
              bold=True)

    #----
    # Load the ForceBalance pickle file which contains:
    #----
    # - Force field object
    # - Optimization parameters
    # - Options from the Target object that launched this simulation
    # - Switch for whether to evaluate analytic derivatives.
    FF, mvals, TgtOptions, AGrad = lp_load('forcebalance.p')
    FF.ffdir = '.'
    # Write the force field file.
    FF.make(mvals)

    #----
    # Load the options that are set in the ForceBalance input file.
    #----
    # Finite difference step size
    h = TgtOptions['h']
    pgrad = TgtOptions['pgrad']
    # MD options; time step (fs), production steps, equilibration steps, interval for saving data (ps)
    nvt_timestep = TgtOptions['nvt_timestep']
    nvt_md_steps = TgtOptions['nvt_md_steps']
    nvt_eq_steps = TgtOptions['nvt_eq_steps']
    nvt_interval = TgtOptions['nvt_interval']
    liquid_fnm = TgtOptions['nvt_coords']

    # Number of threads, multiple timestep integrator, anisotropic box etc.
    threads = TgtOptions.get('md_threads', 1)
    mts = TgtOptions.get('mts_integrator', 0)
    rpmd_beads = TgtOptions.get('rpmd_beads', 0)
    force_cuda = TgtOptions.get('force_cuda', 0)
    nbarostat = TgtOptions.get('n_mcbarostat', 25)
    anisotropic = TgtOptions.get('anisotropic_box', 0)
    minimize = TgtOptions.get('minimize_energy', 1)

    # Print all options.
    printcool_dictionary(TgtOptions, title="Options from ForceBalance")
    nvt_snapshots = int((nvt_timestep * nvt_md_steps / 1000) / nvt_interval)
    nvt_iframes = int(1000 * nvt_interval / nvt_timestep)
    logger.info("For the condensed phase system, I will collect %i snapshots spaced apart by %i x %.3f fs time steps\n" \
        % (nvt_snapshots, nvt_iframes, nvt_timestep))
    if nvt_snapshots < 2:
        raise Exception('Please set the number of liquid time steps so that you collect at least two snapshots (minimum %i)' \
                            % (2000 * int(nvt_interval,nvt_timestep)))

    #----
    # Loading coordinates
    #----
    ML = Molecule(liquid_fnm, toppbc=True)
    # Determine the number of molecules in the condensed phase coordinate file.
    NMol = len(ML.molecules)
    TgtOptions['n_molecules'] = NMol
    logger.info("There are %i molecules in the liquid\n" % (NMol))

    #----
    # Setting up MD simulations
    #----
    EngOpts = OrderedDict()
    EngOpts["liquid"] = OrderedDict([("coords", liquid_fnm), ("mol", ML),
                                     ("pbc", True)])
    if "nonbonded_cutoff" in TgtOptions:
        EngOpts["liquid"]["nonbonded_cutoff"] = TgtOptions["nonbonded_cutoff"]
    else:
        largest_available_cutoff = min(ML.boxes[0][:3]) / 2 - 0.1
        EngOpts["liquid"]["nonbonded_cutoff"] = largest_available_cutoff
        logger.info(
            "nonbonded_cutoff is by default set to the largest available value: %g Angstrom"
            % largest_available_cutoff)
    if "vdw_cutoff" in TgtOptions:
        EngOpts["liquid"]["vdw_cutoff"] = TgtOptions["vdw_cutoff"]
    # Hard Code nonbonded_cutoff to 13A for test
    #EngOpts["liquid"]["nonbonded_cutoff"] = EngOpts["liquid"]["vdw_cutoff"] = 13.0
    GenOpts = OrderedDict([('FF', FF)])
    if engname == "openmm":
        # OpenMM-specific options
        EngOpts["liquid"]["platname"] = TgtOptions.get("platname", 'CUDA')
        if force_cuda:
            try:
                Platform.getPlatformByName('CUDA')
            except:
                raise RuntimeError(
                    'Forcing failure because CUDA platform unavailable')
            EngOpts["liquid"]["platname"] = 'CUDA'
        if threads > 1:
            logger.warn(
                "Setting the number of threads will have no effect on OpenMM engine.\n"
            )

    EngOpts["liquid"].update(GenOpts)
    for i in EngOpts:
        printcool_dictionary(EngOpts[i], "Engine options for %s" % i)

    # Set up MD options
    MDOpts = OrderedDict()
    MDOpts["liquid"] = OrderedDict([("nsteps", nvt_md_steps),
                                    ("timestep", nvt_timestep),
                                    ("temperature", temperature),
                                    ("nequil", nvt_eq_steps),
                                    ("minimize", minimize),
                                    ("nsave",
                                     int(1000 * nvt_interval / nvt_timestep)),
                                    ("verbose", True),
                                    ('save_traj', TgtOptions['save_traj']),
                                    ("threads", threads), ("mts", mts),
                                    ("rpmd_beads", rpmd_beads),
                                    ("faststep", faststep)])

    # Energy components analysis disabled for OpenMM MTS because it uses force groups
    if (engname == "openmm" and mts):
        logger.warn(
            "OpenMM with MTS integrator; energy components analysis will be disabled.\n"
        )

    # Create instances of the MD Engine objects.
    Liquid = Engine(name="liquid", **EngOpts["liquid"])

    #=================================================================#
    # Run the simulation for the full system and analyze the results. #
    #=================================================================#

    printcool("Condensed phase NVT molecular dynamics", color=4, bold=True)
    click()
    prop_return = Liquid.molecular_dynamics(**MDOpts["liquid"])
    logger.info("Liquid phase MD simulation took %.3f seconds\n" % click())
    Potentials = prop_return['Potentials']

    #============================================#
    #  Compute the potential energy derivatives. #
    #============================================#
    if AGrad:
        logger.info(
            "Calculating potential energy derivatives with finite difference step size: %f\n"
            % h)
        # Switch for whether to compute the derivatives two different ways for consistency.
        FDCheck = False
        printcool(
            "Condensed phase energy and dipole derivatives\nInitializing array to length %i"
            % len(Potentials),
            color=4,
            bold=True)
        click()
        G, GDx, GDy, GDz = energy_derivatives(Liquid,
                                              FF,
                                              mvals,
                                              h,
                                              pgrad,
                                              len(Potentials),
                                              AGrad,
                                              dipole=False)
        logger.info("Condensed phase energy derivatives took %.3f seconds\n" %
                    click())

    #==============================================#
    #  Condensed phase properties and derivatives. #
    #==============================================#

    # Physical constants
    kB = 0.008314472471220214
    T = temperature
    kT = kB * T  # Unit: kJ/mol

    #--- Surface Tension ----
    logger.info("Start Computing surface tension.\n")
    perturb_proportion = 0.0005
    box_vectors = np.array(
        Liquid.xyz_omms[0][1] /
        nanometer)  # obtain original box vectors from first frame
    delta_S = np.sqrt(
        np.sum(np.cross(box_vectors[0], box_vectors[1])**
               2)) * perturb_proportion * 2  # unit: nm^2. *2 for 2 surfaces
    # perturb xy area +
    click()
    scale_x = scale_y = np.sqrt(1 + perturb_proportion)
    scale_z = 1.0 / (
        1 + perturb_proportion
    )  # keep the box volumn while changing the area of xy plane
    Liquid.scale_box(scale_x, scale_y, scale_z)
    logger.info("scale_box+ took %.3f seconds\n" % click())
    # Obtain energies and gradients
    Potentials_plus = Liquid.energy()
    logger.info(
        "Calculation of energies for perturbed box+ took %.3f seconds\n" %
        click())
    if AGrad:
        G_plus, _, _, _ = energy_derivatives(Liquid,
                                             FF,
                                             mvals,
                                             h,
                                             pgrad,
                                             len(Potentials),
                                             AGrad,
                                             dipole=False)
        logger.info(
            "Calculation of energy gradients for perturbed box+ took %.3f seconds\n"
            % click())
    # perturb xy area - ( Note: also need to cancel the previous scaling)
    scale_x = scale_y = np.sqrt(1 - perturb_proportion) * (1.0 / scale_x)
    scale_z = 1.0 / (1 - perturb_proportion) * (1.0 / scale_z)
    Liquid.scale_box(scale_x, scale_y, scale_z)
    logger.info("scale_box- took %.3f seconds\n" % click())
    # Obtain energies and gradients
    Potentials_minus = Liquid.energy()
    logger.info(
        "Calculation of energies for perturbed box- took %.3f seconds\n" %
        click())
    if AGrad:
        G_minus, _, _, _ = energy_derivatives(Liquid,
                                              FF,
                                              mvals,
                                              h,
                                              pgrad,
                                              len(Potentials),
                                              AGrad,
                                              dipole=False)
        logger.info(
            "Calculation of energy gradients for perturbed box- took %.3f seconds\n"
            % click())
    # Compute surface tension
    dE_plus = Potentials_plus - Potentials  # Unit: kJ/mol
    dE_minus = Potentials_minus - Potentials  # Unit: kJ/mol
    prefactor = -0.5 * kT / delta_S / 6.0221409e-1  # Unit mJ m^-2
    # Following equation: γ = -kT/(2ΔS) * [ ln<exp(-ΔE+/kT)> - ln<exp(-ΔE-/kT)> ]
    #plus_avg, plus_err = mean_stderr(np.exp(-dE_plus/kT))
    #minus_avg, minus_err = mean_stderr(np.exp(-dE_minus/kT))
    #surf_ten = -0.5 * kT / delta_S * ( np.log(plus_avg) - np.log(minus_avg) ) / 6.0221409e-1 # convert to mJ m^-2
    #surf_ten_err = 0.5 * kT / delta_S * ( np.log(plus_avg+plus_err) - np.log(plus_avg-plus_err) + np.log(minus_avg+minus_err) - np.log(minus_avg-minus_err) ) / 6.0221409e-1
    exp_dE_plus = np.exp(-dE_plus / kT)
    exp_dE_minus = np.exp(-dE_minus / kT)
    surf_ten = prefactor * (np.log(np.mean(exp_dE_plus)) -
                            np.log(np.mean(exp_dE_minus)))
    # Use bootstrap method to estimate the error
    num_frames = len(exp_dE_plus)
    numboots = 1000
    surf_ten_boots = np.zeros(numboots)
    for i in range(numboots):
        boots_ordering = np.random.randint(num_frames, size=num_frames)
        boots_exp_dE_plus = np.take(exp_dE_plus, boots_ordering)
        boots_exp_dE_minus = np.take(exp_dE_minus, boots_ordering)
        surf_ten_boots[i] = prefactor * (np.log(np.mean(boots_exp_dE_plus)) -
                                         np.log(np.mean(boots_exp_dE_minus)))
    surf_ten_err = np.std(surf_ten_boots) * np.sqrt(
        np.mean([
            statisticalInefficiency(exp_dE_plus),
            statisticalInefficiency(exp_dE_minus)
        ]))

    printcool("Surface Tension:       % .4f +- %.4f mJ m^-2" %
              (surf_ten, surf_ten_err))
    # Analytic Gradient of surface tension
    # Formula:      β = 1/kT
    #           ∂γ/∂α = -kT/(2ΔS) * { 1/<exp(-βΔE+)> * [<-β ∂E+/∂α exp(-βΔE+)> - <-β ∂E/∂α><exp(-βΔE+)>]
    #                                -1/<exp(-βΔE-)> * [<-β ∂E-/∂α exp(-βΔE-)> - <-β ∂E/∂α><exp(-βΔE-)>] }
    n_params = len(mvals)
    G_surf_ten = np.zeros(n_params)
    if AGrad:
        beta = 1.0 / kT
        plus_denom = np.mean(np.exp(-beta * dE_plus))
        minus_denom = np.mean(np.exp(-beta * dE_minus))
        for param_i in range(n_params):
            plus_left = np.mean(-beta * G_plus[param_i] *
                                np.exp(-beta * dE_plus))
            plus_right = np.mean(-beta * G[param_i]) * plus_denom
            minus_left = np.mean(-beta * G_minus[param_i] *
                                 np.exp(-beta * dE_minus))
            minus_right = np.mean(-beta * G[param_i]) * minus_denom
            G_surf_ten[param_i] = prefactor * (1.0 / plus_denom *
                                               (plus_left - plus_right) -
                                               1.0 / minus_denom *
                                               (minus_left - minus_right))
        printcool("Analytic Derivatives:")
        FF.print_map(vals=G_surf_ten)

    logger.info("Writing final force field.\n")
    pvals = FF.make(mvals)

    logger.info("Writing all results to disk.\n")
    result_dict = {
        'surf_ten': surf_ten,
        'surf_ten_err': surf_ten_err,
        'G_surf_ten': G_surf_ten
    }
    lp_dump(result_dict, 'nvt_result.p')