#!/usr/local/bin/env python

#=============================================================================================
# MODULE DOCSTRING
#=============================================================================================

"""
Utility methods used in validation

DESCRIPTION

TODO

COPYRIGHT AND LICENSE

@author Mark Friedrichs <friedrim@stanford.edu>

All code in this repository is released under the GNU General Public License.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.

"""

#=============================================================================================
# GLOBAL IMPORTS
#=============================================================================================

import sys
import os
import math
import doctest
import simtk.unit as units
import simtk.openmm

#=============================================================================================
# Validation Utilities
#=============================================================================================

class ValidationUtilities(object):

    def __init__(self, verbose=False):

        self._openMM              = simtk.openmm
        self._verbose             = verbose
        self._lengthUnit          = units.nanometer
        self._energyUnit          = units.kilojoule_per_mole
        self._radianUnit          = units.radian
        self._chargeUnit          = units.elementary_charge
        self._timeUnit            = units.picosecond
        self._dimensionlessUnit   = units.dimensionless

    #=============================================================================================
    # Get dimensionless unit
    #=============================================================================================

    def getDimensionlessUnit(self):
        """
        Return dimensionless unit
        """
        return self._dimensionlessUnit

    #=============================================================================================
    # Get length unit
    #=============================================================================================

    def getLengthUnit(self):
        """
        Return length unit
        """
        return self._lengthUnit

    #=============================================================================================
    # Get radian unit
    #=============================================================================================

    def getRadianUnit(self):
        """
        Return radian unit
        """
        return self._radianUnit

    #=============================================================================================
    # Get charge unit
    #=============================================================================================

    def getChargeUnit(self):
        """
        Return charge unit
        """
        return self._chargeUnit

    #=============================================================================================
    # Get time unit
    #=============================================================================================

    def getTimeUnit(self):
        """
        Return time unit
        """
        return self._timeUnit

    #=============================================================================================
    # Get energy unit
    #=============================================================================================

    def getEnergyUnit(self):
        """
        Return energy unit
        """
        return self._energyUnit

    #=============================================================================================
    # Get force unit
    #=============================================================================================

    def getForceUnit(self):
        """
        Return force unit
        """
        return self.getEnergyUnit()/self.getLengthUnit()

    #=============================================================================================
    # Copy masses from one system object to another
    #=============================================================================================

    def copySystemMasses(self, systemToGetMassesFrom, systemToCopyMassesTo):
        """
        Copy masses from one system object to another

        ARGUMENTS

        systemToGetMassesFrom (OpenMM::System) system to get masses from
        systemToCopyMassesTo  (OpenMM::System) system to copy masses to

        """
        for ii in range( systemToGetMassesFrom.getNumParticles() ):
            systemToCopyMassesTo.addParticle( systemToGetMassesFrom.getParticleMass( ii ) )

    #=============================================================================================
    # Copy box vectors to system object 
    #=============================================================================================

    def copyDefaultBoxVectors(self, boxVectors, systemToCopyBoxVectorsTo):
        """
        Copy boxVectors into system object

        ARGUMENTS

        boxVectors (OpenMM::System) boxes
        systemToCopyMassesTo  (OpenMM::System) system to copy masses to

        """
        if( self._verbose ):
            print "copyDefaultBoxVectors %s" % (str(boxVectors))
            #print "copyDefaultBoxVectors %s" % (str(systemToCopyBoxVectorsTo))

        systemToCopyBoxVectorsTo.setDefaultPeriodicBoxVectors( boxVectors[0], boxVectors[1], boxVectors[2] )

    #=============================================================================================
    # Load masses from array into a System
    #=============================================================================================

    def loadSystemMasses(self, massArray, systemToCopyMassesTo):
        """
        Load masses from an array into a OpenMM::System object

        ARGUMENTS

        massArray             (list) array of masses
        systemToCopyMassesTo  (OpenMM::System) system to load masses to

        """
        for ii in range( len( massArray ) ):
            systemToCopyMassesTo.addParticle( massArray[ii] )

    #=============================================================================================
    # Make copy of HarmonicBondForce
    #=============================================================================================

    def copyHarmonicBondForce(self, harmonicBondForceToCopy):
        """Return a copy of a OpenMM::HarmonicBondForce

        ARGUMENTS

        harmonicBondForceToCopy (OpenMM::HarmonicBondForce) force to copy

        """
        newHarmonicBondForce         = self._openMM.HarmonicBondForce()
        forceConstantUnit            = self._energyUnit / self._lengthUnit**2
        for ii in range( harmonicBondForceToCopy.getNumBonds() ):
            args = harmonicBondForceToCopy.getBondParameters(ii)
            #if self._verbose: print " Bond: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newHarmonicBondForce.addBond( args[0], args[1], args[2], args[3] )

        return newHarmonicBondForce

    #=============================================================================================
    # Make copy of HarmonicBondForce into a CustomBondForce
    #=============================================================================================

    def copyHarmonicBondForceToCustomBondForce(self, harmonicBondForceToCopy):
        """Return a copy of a OpenMM::CustomBondForce

        ARGUMENTS

        harmonicBondForceToCopy (OpenMM::HarmonicBondForce) force to copy

        """
        newCustomBondForce         = self._openMM.CustomBondForce("scale*k*(r-r0)^2")
        newCustomBondForce.addPerBondParameter("r0");
        newCustomBondForce.addPerBondParameter("k");
        newCustomBondForce.addGlobalParameter("scale", 0.5);
 
        for ii in range( harmonicBondForceToCopy.getNumBonds() ):
            args = harmonicBondForceToCopy.getBondParameters(ii)
            #if self._verbose: print " Bond: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            parameters = []
            parameters.append( args[2] )
            parameters.append( args[3] )
            newCustomBondForce.addBond( args[0], args[1], parameters )

        return newCustomBondForce

    #=============================================================================================
    # Copy exceptions from CustomNonBondedForce to a HarmonicBondForce
    #=============================================================================================

    def copyExceptionsFromNonbondedForceToCustomBondForce(self, nonbondedForceToCopy, methodType):
        """Return a copy of a OpenMM::CustomBondForce

        ARGUMENTS

        nonBondedForceToCopy (OpenMM::NonbondedForce) force to copy

        """
        if( methodType == 1 ):
            newCustomBondForce      = self._openMM.CustomBondForce("4*eps*((sigma/r)^12-(sigma/r)^6)+138.935456*q/r")
        else:
            # include rxn field

            newCustomBondForce      = self._openMM.CustomBondForce("4*eps*((sigma/r)^12-(sigma/r)^6)+138.935456*q*((1.0/r)+(krf*r*r)-crf)")

            lengthUnit              = self.getLengthUnit( )
            cutoffDistance          = nonbondedForceToCopy.getCutoffDistance()/lengthUnit
            reactionFieldDielectric = nonbondedForceToCopy.getReactionFieldDielectric()/self.getDimensionlessUnit()

            eps2                    = (reactionFieldDielectric - 1.0)/(2.0*reactionFieldDielectric+1.0)
            kValue                  = eps2/(cutoffDistance*cutoffDistance*cutoffDistance)
            newCustomBondForce.addGlobalParameter("krf", kValue );

            cValue                  = (1.0/cutoffDistance)*(3.0*reactionFieldDielectric)/(2.0*reactionFieldDielectric + 1.0)
            newCustomBondForce.addGlobalParameter("crf", cValue )

        newCustomBondForce.addPerBondParameter("q")
        newCustomBondForce.addPerBondParameter("sigma")
        newCustomBondForce.addPerBondParameter("eps")
 
        for ii in range( nonbondedForceToCopy.getNumExceptions() ):
            args       = nonbondedForceToCopy.getExceptionParameters(ii)
            parameters = []
            parameters.append( args[2] )
            parameters.append( args[3] )
            parameters.append( args[4] )
            newCustomBondForce.addBond( args[0], args[1], parameters )

        return newCustomBondForce

    #=============================================================================================
    # Make copy of HarmonicAngleForce into a CustomAngleForce
    #=============================================================================================

    def copyHarmonicAngleForceToCustomAngleForce(self, harmonicAngleForceToCopy):
        """Return a copy of a OpenMM::CustomAngleForce

        ARGUMENTS

        harmonicAngleForceToCopy (OpenMM::HarmonicAngleForce) force to copy

        """
        newCustomAngleForce         = self._openMM.CustomAngleForce("scale*k*(theta-theta0)^2")
        newCustomAngleForce.addPerAngleParameter("theta0");
        newCustomAngleForce.addPerAngleParameter("k");
        newCustomAngleForce.addGlobalParameter("scale", 0.5);
 
        for ii in range( harmonicAngleForceToCopy.getNumAngles() ):
            args = harmonicAngleForceToCopy.getAngleParameters(ii)
            #if self._verbose: print " Angle: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            parameters = []
            parameters.append( args[3] )
            parameters.append( args[4] )
            newCustomAngleForce.addAngle( args[0], args[1], args[2], parameters )

        return newCustomAngleForce

    #=============================================================================================
    # Make copy of PeriodicTorsionForce into a CustomTorsionForce
    #=============================================================================================

    def copyPeriodicTorsionForceToCustomTorsionForce(self, periodicTorsionForceToCopy):
        """Return a copy of a OpenMM::CustomTorsionForce

        ARGUMENTS

        periodicTorsionForceToCopy (OpenMM::HarmonicTorsionForce) force to copy

        """
        newCustomTorsionForce         = self._openMM.CustomTorsionForce("k*(1+cos(n*theta-theta0))")
        newCustomTorsionForce.addPerTorsionParameter("n");
        newCustomTorsionForce.addPerTorsionParameter("theta0");
        newCustomTorsionForce.addPerTorsionParameter("k");
 
        for ii in range( periodicTorsionForceToCopy.getNumTorsions() ):
            args = periodicTorsionForceToCopy.getTorsionParameters(ii)
            #if self._verbose: print " Torsion: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            parameters = []
            parameters.append( args[4] )
            parameters.append( args[5] )
            parameters.append( args[6] )
            newCustomTorsionForce.addTorsion( args[0], args[1], args[2], args[3], parameters )

        return newCustomTorsionForce

    #=============================================================================================
    # Gnerate a CustomExternalForce
    #=============================================================================================

    def createCustomExternalForce(self, numParticles):
        """Return a copy of a OpenMM::CustomExternalForce

        ARGUMENTS

        numParticles: number of particles

        """
        newCustomExternalForce         = self._openMM.CustomExternalForce("scale*(x+yscale*(y-y0)^2+zscale*cos(z-z0))")
        newCustomExternalForce.addPerParticleParameter("y0")
        newCustomExternalForce.addPerParticleParameter("yscale")
        newCustomExternalForce.addPerParticleParameter("z0")
        newCustomExternalForce.addPerParticleParameter("zscale")
        newCustomExternalForce.addGlobalParameter("scale", 0.5)
        y0     = 0.5 
        yscale = 0.05 
        z0     = 0.1 
        zscale = 0.02 
        for ii in range( numParticles ):
            parameters = []
            parameters.append( y0 )
            parameters.append( (yscale + 0.02*ii) )
            parameters.append( z0 )
            parameters.append( (zscale + 0.01*ii))
            newCustomExternalForce.addParticle( ii, parameters )

        return newCustomExternalForce

    #=============================================================================================
    # Make copy of NonbondedForce
    #=============================================================================================

    def copyNonbondedForceToCustomNonbondedForce(self, nonbondedForceToCopy, methodType):
        """Return a OpenMM::CustomNonbondedForce; exceptions are ALL zeroed

        ARGUMENTS

        nonbondedForceToCopy (OpenMM::NonbondedForce) force to copy

        """
        if( methodType == 1 ):
            newNonbondedForce       = self._openMM.CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6)+138.935456*q/r; q=q1*q2; sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)")
        else:

            # include rxn field

            newNonbondedForce       = self._openMM.CustomNonbondedForce("4*eps*((sigma/r)^12-(sigma/r)^6)+138.935456*q*((1.0/r)+(krf*r*r)-crf); q=q1*q2; sigma=0.5*(sigma1+sigma2); eps=sqrt(eps1*eps2)")

            lengthUnit              = self.getLengthUnit( )
            cutoffDistance          = nonbondedForceToCopy.getCutoffDistance()/lengthUnit
            newNonbondedForce.setCutoffDistance( cutoffDistance )
            reactionFieldDielectric = nonbondedForceToCopy.getReactionFieldDielectric()/self.getDimensionlessUnit()

            eps2                    = (reactionFieldDielectric - 1.0)/(2.0*reactionFieldDielectric+1.0)
            kValue                  = eps2/(cutoffDistance*cutoffDistance*cutoffDistance)
            newNonbondedForce.addGlobalParameter("krf", kValue )

            cValue                  = (1.0/cutoffDistance)*(3.0*reactionFieldDielectric)/(2.0*reactionFieldDielectric + 1.0)
            newNonbondedForce.addGlobalParameter("crf", cValue )
            #print "Rxn: %.4f %.4f" %( kValue, cValue ) 

        newNonbondedForce.addPerParticleParameter("q")
        newNonbondedForce.addPerParticleParameter("sigma")
        newNonbondedForce.addPerParticleParameter("eps")

        for ii in range( nonbondedForceToCopy.getNumParticles() ):
            args = nonbondedForceToCopy.getParticleParameters(ii)
            #if self._verbose: print "Nonbonded: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0], args[1], args[2])
            newNonbondedForce.addParticle( args )

        nonbondedMethod    = nonbondedForceToCopy.getNonbondedMethod()
        newNonbondedForce.setNonbondedMethod( nonbondedMethod )

        cutoffDistance     = nonbondedForceToCopy.getCutoffDistance()
        newNonbondedForce.setCutoffDistance( cutoffDistance )

        #chargeUnit         = self.getChargeUnit( )
        #lengthUnit         = self.getLengthUnit( )
        #energyUnit         = self.getEnergyUnit( )

        for ii in range( nonbondedForceToCopy.getNumExceptions() ):
            args   = nonbondedForceToCopy.getExceptionParameters(ii)
            #charge = (args[2]/(chargeUnit*chargeUnit))
            #eps    = (args[4]/energyUnit)
            #if self._verbose: print "copyNonbondedForceToCustomNonbondedForce: Custom Nonbonded exceptions: %5d %5d %5d [%s %s %s] %14.7f %14.7f" % (ii, args[0], args[1], str(args[2]), str(args[3]), str(args[4]),charge, eps)
            #if( eps ==  0.0 and charge == 0.0 ):
            newNonbondedForce.addExclusion( args[0], args[1] )

        return newNonbondedForce

    #=============================================================================================
    # Generate CustomGBSAOBCForce copy of GBSAOBCForce
    #=============================================================================================

    def copyGBSAOBCForceToCustomGBSAOBCForce(self, obcForceToCopy):
        """Return a OpenMM::CustomGBSAOBCForce; exceptions are ALL zeroed

        ARGUMENTS

        nonbondedForceToCopy (OpenMM::GBSAOBCForce) force to copy

        """
        newGBSAOBCForce       = self._openMM.CustomGBForce( )
        newGBSAOBCForce.setCutoffDistance( obcForceToCopy.getCutoffDistance() )

        newGBSAOBCForce.addPerParticleParameter("q")
        newGBSAOBCForce.addPerParticleParameter("radius")
        newGBSAOBCForce.addPerParticleParameter("scale")

        newGBSAOBCForce.addGlobalParameter("solventDielectric", obcForceToCopy.getSolventDielectric())
        newGBSAOBCForce.addGlobalParameter("soluteDielectric", obcForceToCopy.getSoluteDielectric())

        newGBSAOBCForce.addComputedValue("I", "step(r+sr2-or1)*0.5*(1/L-1/U+0.25*(1/U^2-1/L^2)*(r-sr2*sr2/r)+0.5*log(L/U)/r+C);"
                                  "U=r+sr2;"
                                  "C=2*(1/or1-1/L)*step(sr2-r-or1);"
                                  "L=max(or1, D);"
                                  "D=abs(r-sr2);"
                                  "sr2 = scale2*or2;"
                                  "or1 = radius1-0.009; or2 = radius2-0.009", self._openMM.CustomGBForce.ParticlePairNoExclusions)
        newGBSAOBCForce.addComputedValue("B", "1/(1/or-tanh(1*psi-0.8*psi^2+4.85*psi^3)/radius);"
                                  "psi=I*or; or=radius-0.009", self._openMM.CustomGBForce.SingleParticle)
        newGBSAOBCForce.addEnergyTerm("28.3919551*(radius+0.14)^2*(radius/B)^6-0.5*138.935485*(1/soluteDielectric-1/solventDielectric)*q^2/B", self._openMM.CustomGBForce.SingleParticle)
        newGBSAOBCForce.addEnergyTerm("-138.935485*(1/soluteDielectric-1/solventDielectric)*q1*q2/f;"
                          "f=sqrt(r^2+B1*B2*exp(-r^2/(4*B1*B2)))", self._openMM.CustomGBForce.ParticlePairNoExclusions)


        for ii in range( obcForceToCopy.getNumParticles() ):
            args = obcForceToCopy.getParticleParameters(ii)
            #if self._verbose: print "GBSAOBC: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0], args[1], args[2])
            newGBSAOBCForce.addParticle( args )

        nonbondedMethod    = obcForceToCopy.getNonbondedMethod()
        newGBSAOBCForce.setNonbondedMethod( nonbondedMethod )

        return newGBSAOBCForce

    #=============================================================================================
    # Make copy of HarmonicAngleForce
    #=============================================================================================

    def copyHarmonicAngleForce(self, harmonicAngleForceToCopy):
        """Return a copy of a OpenMM::HarmonicAngleForce

        ARGUMENTS

        harmonicAngleForceToCopy (OpenMM::HarmonicAngleForce) force to copy

        """
        newHarmonicAngleForce         = self._openMM.HarmonicAngleForce()
        forceConstantUnit             = self._energyUnit / self._radianUnit**2
        for ii in range( harmonicAngleForceToCopy.getNumAngles() ):
            args = harmonicAngleForceToCopy.getAngleParameters(ii)
            #if self._verbose: print " Angle: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3]/self._radianUnit, args[3]/forceConstantUnit)
            newHarmonicAngleForce.addAngle( args[0], args[1], args[2], args[3], args[4] )

        return newHarmonicAngleForce

    #=============================================================================================
    # Print HarmonicAngleForce info
    #=============================================================================================

    def printHarmonicAngleForce(self, testName, harmonicAngleForce, maxPrint=10):
        """Print OpenMM::HarmonicAngleForce info

        ARGUMENTS

        harmonicAngleForce (OpenMM::HarmonicAngleForce) to print

        """
        print "\n\n" + testName
        print "%d harmonicAngleForce entries " % harmonicAngleForce.getNumAngles()
        ii                 = 0
        lengthUnit         = self.getRadianUnit( )
        forceConstantUnit  = self.getEnergyUnit( ) / lengthUnit**2
        while( ii < harmonicAngleForce.getNumAngles() ):
            args = harmonicAngleForce.getAngleParameters(ii)
            print "HarmonicAngle: %5d [%5d %5d %5d] [%14.7e %14.7e]" % (ii, args[0], args[1], args[2], args[3]/lengthUnit, args[4]/forceConstantUnit )
            ii += 1 
            if( ii == maxPrint and harmonicAngleForce.getNumAngles() > 2*maxPrint  ):ii = harmonicAngleForce.getNumAngles() - maxPrint
        sys.stdout.flush()

        return

    #=============================================================================================
    # Make copy of PeriodicTorsionForce
    #=============================================================================================

    def copyPeriodicTorsionForce(self, periodicTorsionForceToCopy):
        """Return a copy of a OpenMM::PeriodicTorsionForce

        ARGUMENTS

        periodicTorsionForceToCopy (OpenMM::PeriodicTorsionForce) force to copy

        """
        newPeriodicTorsionForce       = self._openMM.PeriodicTorsionForce()
        for ii in range( periodicTorsionForceToCopy.getNumTorsions() ):
            args = periodicTorsionForceToCopy.getTorsionParameters(ii)
            #if self._verbose: print " Periodic Torsion: %5d [%5d %5d %5d %5d] %3d [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[4], args[5]/self._radianUnit, args[6]/self._energyUnit)
            newPeriodicTorsionForce.addTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6] )

        return newPeriodicTorsionForce

    #=============================================================================================
    # Print info on PeriodicTorsionForce
    #=============================================================================================

    def printPeriodicTorsionForce(self, testname, periodicTorsionForce, maxPrint = 10):

        """Print info on OpenMM::PeriodicTorsionForce

        ARGUMENTS

        periodicTorsionForce (OpenMM::PeriodicTorsionForce)

        """
        print "\n\n" + testname
        print "%d torsions" % periodicTorsionForce.getNumTorsions()
        ii                 = 0
        lengthUnit         = self.getRadianUnit( )
        forceConstantUnit  = self.getEnergyUnit( ) / lengthUnit**2
        energyUnit         = self.getEnergyUnit( )
        while( ii < periodicTorsionForce.getNumTorsions() ):
            args = periodicTorsionForce.getTorsionParameters(ii)
            print "PeriodicTorsion: %5d [%5d %5d %5d %5d] %3d [%14.7e %14.7e]" % (ii, args[0], args[1], args[2], args[3], args[4], args[5]/lengthUnit, args[6]/energyUnit)
            ii  += 1
            if( ii == maxPrint and periodicTorsionForce.getNumTorsions() > 2*maxPrint  ):ii = periodicTorsionForce.getNumTorsions() - maxPrint
        sys.stdout.flush()

        return

    #=============================================================================================
    # Make copy of RBTorsionForce
    #=============================================================================================

    def copyRBTorsionForce(self, rbTorsionForceToCopy):
        """Return a copy of a OpenMM::RBTorsionForce

        ARGUMENTS

        rbTorsionForceToCopy (OpenMM::RBTorsionForce) force to copy

        """
        newRBTorsionForce       = self._openMM.RBTorsionForce()
        for ii in range( rbTorsionForceToCopy.getNumTorsions() ):
            args = rbTorsionForceToCopy.getTorsionParameters(ii)
            #if self._verbose: print " RB torsion: %5d [%5d %5d 5d %5d] [%14.7f %14.7f %14.7f %14.7f %14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])
            newRBTorsionForce.addTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9] )

        return newRBTorsionForce

    #=============================================================================================
    # Make copy of CMAPTorsionForce
    #=============================================================================================

    def copyCMAPTorsionForce(self, cmapTorsionForceToCopy):
        """Return a copy of a OpenMM::CMAPTorsionForce

        ARGUMENTS

        cmapTorsionForceToCopy (OpenMM::CMAPTorsionForce) force to copy

        """
        newCMAPTorsionForce       = self._openMM.CMAPTorsionForce()

        # torsions

        for ii in range( cmapTorsionForceToCopy.getNumTorsions() ):
            args = cmapTorsionForceToCopy.getTorsionParameters(ii)
            newCMAPTorsionForce.addTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8] )

        # maps

        for ii in range( cmapTorsionForceToCopy.getNumMaps() ):
            args = cmapTorsionForceToCopy.getMapParameters(ii)
            newCMAPTorsionForce.addMap( args[0], args[1] );

        return newCMAPTorsionForce

    #=============================================================================================
    # Print CMAPTorsionForce
    #=============================================================================================

    def printCMAPTorsionForce(self, header, cmapTorsionForce, maxPrint = 10):
        """Return a copy of a OpenMM::CMAPTorsionForce

        ARGUMENTS

        cmapTorsionForceToCopy (OpenMM::CMAPTorsionForce) force to copy

        """

        if( header ):
            print "\n\n" + header

        print "%d CMAP torsions " % cmapTorsionForce.getNumTorsions()
        ii = 0  
        while( ii < cmapTorsionForce.getNumTorsions() ):
            args = cmapTorsionForce.getTorsionParameters(ii)
            print "   %5d map=%2d [%5d %5d %5d %5d] [%5d %5d %5d %5d]" % (ii, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
            ii  += 1 
            if( ii == maxPrint and cmapTorsionForce.getNumTorsions() > 2*maxPrint  ):ii = cmapTorsionForce.getNumTorsions() - maxPrint
        sys.stdout.flush()

        # torsion maps

        print "%d CMAP torsion maps " % cmapTorsionForce.getNumMaps()
        for ii in range( cmapTorsionForce.getNumMaps() ):
            args = cmapTorsionForce.getMapParameters(ii)
            print "%d map size=%d " % (ii, args[0] )
            jj = 0
            mapLen = len( args[1] )
            while( jj < mapLen ):
                print "  %15.7e" % (args[1][jj] )
                jj  += 1 
                if( jj == maxPrint and mapLen > 2*maxPrint  ):jj = mapLen - maxPrint
        sys.stdout.flush()
       
        return

    #=============================================================================================
    # Make copy of NonbondedForce
    #     includeExceptions = -2, then zero out charge and eps for nonbonded
    #     includeExceptions =  2, then zero out charge and eps for nonbonded exceptions
    #     includeExceptions =  1, then include unmodified nonbonded exceptions
    #     includeExceptions =  0, then do not include any nonbonded exceptions
    #=============================================================================================

    def copyNonbondedForce(self, nonbondedForceToCopy, includeExceptions = 1):
        """Return a copy of a OpenMM::NonbondedForce

        ARGUMENTS

        nonbondedForceToCopy (OpenMM::NonbondedForce) force to copy

        """
        newNonbondedForce       = self._openMM.NonbondedForce()
        for ii in range( nonbondedForceToCopy.getNumParticles() ):
            args = nonbondedForceToCopy.getParticleParameters(ii)
            #if self._verbose: print "Nonbonded: %5d [%s %s %s]" % (ii, str(args[0]), str( args[1] ), str(args[2]) )
            if( includeExceptions == -2 ):
                args[0] = 0.0
                args[2] = 0.0
            newNonbondedForce.addParticle( args[0], args[1], args[2])

        nonbondedMethod           = nonbondedForceToCopy.getNonbondedMethod()
        newNonbondedForce.setNonbondedMethod( nonbondedMethod )

        cutoffDistance            = nonbondedForceToCopy.getCutoffDistance()
        newNonbondedForce.setCutoffDistance( cutoffDistance )

        reactionFieldDielectric   = nonbondedForceToCopy.getReactionFieldDielectric()
        newNonbondedForce.setReactionFieldDielectric( reactionFieldDielectric )

        ewaldErrorTolerance       = nonbondedForceToCopy.getEwaldErrorTolerance()
        newNonbondedForce.setEwaldErrorTolerance( ewaldErrorTolerance )

        if( includeExceptions ):
            for ii in range( nonbondedForceToCopy.getNumExceptions() ):
                args = nonbondedForceToCopy.getExceptionParameters(ii)
                #if self._verbose: print "Nonbonded exceptions: %5d %5d %5d [%s %s %s]" % (ii, args[0], args[1], str(args[2]), str(args[3]), str(args[4]))
                #if( includeExceptions == 1 or ( (includeExceptions == 2) and (args[2] == 0.0) ) ):
                if( includeExceptions == 2 ):
                    args[2] = 0.0
                    args[4] = 0.0
                newNonbondedForce.addException( args[0], args[1], args[2], args[3], args[4] )
                    # if self._verbose: print "Nonbonded exceptions: %5d %5d %5d [%s %s %s]" % (ii, args[0], args[1], str(args[2]), str(args[3]), str(args[4]))
        else:
                if self._verbose: print "Nonbonded exceptions not included." 

        return newNonbondedForce

    #=============================================================================================
    # Make copy of GbsaObcForce
    #=============================================================================================

    def copyGbsaObcForce(self, gbsaObcForceToCopy):
        """Return a copy of a OpenMM::GbsaObcForce

        ARGUMENTS

        gbsaObcForceToCopy (OpenMM::GbsaObcForce) force to copy

        """
        newGbsaObcForce       = self._openMM.GBSAOBCForce()
        for ii in range( gbsaObcForceToCopy.getNumParticles() ):
            args = gbsaObcForceToCopy.getParticleParameters(ii)
            #if self._verbose: print "GbsaObc: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0], args[1], args[2])
            newGbsaObcForce.addParticle( args[0], args[1], args[2])

        nonbondedMethod           = gbsaObcForceToCopy.getNonbondedMethod()
        newGbsaObcForce.setNonbondedMethod( nonbondedMethod )

        cutoffDistance            = gbsaObcForceToCopy.getCutoffDistance()
        newGbsaObcForce.setCutoffDistance( cutoffDistance )

        soluteDielectric          = gbsaObcForceToCopy.getSoluteDielectric()
        newGbsaObcForce.setSoluteDielectric( soluteDielectric )

        solventDielectric          = gbsaObcForceToCopy.getSolventDielectric()
        newGbsaObcForce.setSolventDielectric( solventDielectric )

        return newGbsaObcForce

    #=============================================================================================
    # Make copy of GbviForce
    #=============================================================================================

    def copyGbviForce(self, gbviForceToCopy):
        """Return a copy of a OpenMM::GbviForce

        ARGUMENTS

        gbviForceToCopy (OpenMM::GbviForce) force to copy

        """
        newGbviForce       = self._openMM.GBVIForce()
        for ii in range( gbviForceToCopy.getNumParticles() ):
            args = gbviForceToCopy.getParticleParameters(ii)
            #if self._verbose: print "Gbvi: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0], args[1], args[2])
            newGbviForce.addParticle( args[0], args[1], args[2])

        for ii in range( gbviForceToCopy.getNumBonds() ):
            args = gbviForceToCopy.getBondParameters(ii)
            #if self._verbose: print "Gbvi bonds: %5d [%6d %6d %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit)
            newGbviForce.addBond( args[0], args[1], args[2])

        nonbondedMethod           = gbviForceToCopy.getNonbondedMethod()
        newGbviForce.setNonbondedMethod( nonbondedMethod )

        cutoffDistance            = gbviForceToCopy.getCutoffDistance()
        newGbviForce.setCutoffDistance( cutoffDistance )

        soluteDielectric          = gbviForceToCopy.getSoluteDielectric()
        newGbviForce.setSoluteDielectric( soluteDielectric )

        solventDielectric          = gbviForceToCopy.getSolventDielectric()
        newGbviForce.setSolventDielectric( solventDielectric )

        return newGbviForce

    #=============================================================================================
    # Make a copy of the total system
    #=============================================================================================

    def copySystem(self, validationParameterFileParser, convertConstraintsToBonds = 0 ):
        """Return a copy of the system

        ARGUMENTS

        ValidationParameterFileParser object

        """

        # build system

        # masses

        newSystem  = self._openMM.System()
        self.loadSystemMasses( validationParameterFileParser.getMasses(), newSystem )

        # harmonic bond force

        if( validationParameterFileParser.getNumberOfHarmonicBonds() > 0 ):
            newHarmonicBondForce   = self.copyHarmonicBondForce( validationParameterFileParser.getHarmonicBondForce() )
            newSystem.addForce(newHarmonicBondForce)

        # harmonic angle force

        if( validationParameterFileParser.getNumberOfHarmonicAngles() > 0 ):
            newHarmonicAngleForce   = self.copyHarmonicAngleForce( validationParameterFileParser.getHarmonicAngleForce() )
            newSystem.addForce(newHarmonicAngleForce)

        # periodic torsion force

        if( validationParameterFileParser.getNumberOfPeriodicTorsions() > 0 ):
            newPeriodicTorsionForce   = self.copyPeriodicTorsionForce( validationParameterFileParser.getPeriodicTorsionForce() )
            newSystem.addForce(newPeriodicTorsionForce)

        # RB torsion force

        if( validationParameterFileParser.getNumberOfRBTorsions() > 0 ):
            newRBTorsionForce   = self.copyRBTorsionForce( validationParameterFileParser.getRBTorsionForce() )
            newSystem.addForce(newRBTorsionForce)

        # nonbonded force

        if( validationParameterFileParser.getNumberOfNonbondeds() > 0 ):
            newNonbondedForce   = self.copyNonbondedForce( validationParameterFileParser.getNonbondedForce() )
            newSystem.addForce(newNonbondedForce)

        # GBSA Obc force

        if( validationParameterFileParser.getNumberOfGbsaObcs() > 0 ):
            newGbsaObcForce = self.copyGbsaObcForce( validationParameterFileParser.getGbsaObcForce() )
            newSystem.addForce(newGbsaObcForce)

        # GBVI force

        if( validationParameterFileParser.getNumberOfGbvis() > 0 ):
            newGbviForce = self.copyGbviForce( validationParameterFileParser.getGbviForce() )
            newSystem.addForce(newGbviForce)

        # constraints

        if( validationParameterFileParser.getNumberOfConstraints() > 0 ):
            constraints = validationParameterFileParser.getConstraints()
            if( convertConstraintsToBonds ):
                k = 284512.0000
                if( self._verbose ):
                    print "Constraints %d converted into bonds w/ k=%12.3f" % (len( constraints ), k )
                    sys.stdout.flush()
                for ii in range( len( constraints ) ):
                    particle1            = constraints[ii][0]
                    particle2            = constraints[ii][1]
                    constrainedDistance  = constraints[ii][2]
                    newHarmonicBondForce.addBond( particle1, particle2, constrainedDistance, k )
            else:
                for ii in range( len( constraints ) ):
                    particle1            = constraints[ii][0]
                    particle2            = constraints[ii][1]
                    constrainedDistance  = constraints[ii][2]
                    newSystem.addConstraint( particle1, particle2, constrainedDistance)

        # box

        boxVectors = validationParameterFileParser._boxVectors
        box0       = [ float(boxVectors[0][0]),  float(boxVectors[0][1]), float(boxVectors[0][2]) ]
        box1       = [ float(boxVectors[1][0]),  float(boxVectors[1][1]), float(boxVectors[1][2]) ]
        box2       = [ float(boxVectors[2][0]),  float(boxVectors[2][1]), float(boxVectors[2][2]) ]
        newSystem.setDefaultPeriodicBoxVectors( box0, box1, box2 )
        self.printDefaultBoxVectors( "copySystem", newSystem )

        # CMMotionRemover

        if( validationParameterFileParser.getCMMotionRemover() > 0 ):
            newSystem.addForce( self._openMM.CMMotionRemover( validationParameterFileParser.getCMMotionRemover() ) )

        return newSystem

    #=============================================================================================
    # Get the degrees of freedom for the system
    #=============================================================================================

    def getDegreesOfFreedom(self, validationParameterFileParser ):
        """Return degrees of freedom for the system

        ARGUMENTS

        ValidationParameterFileParser object

        """

        # build system

        # masses

        dof = 3*len(validationParameterFileParser.getMasses() ) -  validationParameterFileParser.getNumberOfConstraints() - 3
        return dof


    #=============================================================================================
    # Copy hash
    #=============================================================================================
    
    def copyHash( self, hashToCopy ): 
        newHash = {}  
        for k1,v1 in hashToCopy.iteritems():
            newHash[k1] = v1
    
        return newHash

    #=============================================================================================
    # Get default hash of test cases: return testHash[testName] = (default[parameters] = values)
    #=============================================================================================
    
    def getListFromFile( self, listFileName ):
        """Read list
    
        ARGUMENTS
        listFileName    (string)   -- file name
    
        """
    
        listFile = open( listFileName, 'r')
    
        returnList     = []
        while True:
    
            line = listFile.readline()
            if line is None: break
            if len(line) == 0: break
            
            if line.startswith('#'):
                doNothing = 1
            else:
                item = line.rstrip().split(None)
                if( len(item) > 0 ):
                    returnList.append( item[0] )
    
        return returnList
    
    #=============================================================================================
    # Get full test name
    #=============================================================================================
    
    def getFullTestName( self, systemName, testName ):
        """Get full test name
    
        ARGUMENTS
            systemName              (string) system name
            testName                (string) test name
        """
        return testName + '___' + systemName
    
    #=============================================================================================
    # Build default hash of test cases: return testHash[testName] = (default[parameters] = values)
    #=============================================================================================
    
    def buildDefaultTestHash( self, testList, systemList, defaultParameterHash ):
        """Build hash of tests to run
    
        ARGUMENTS
            systemList           (list) list of system names
            defaultParameterHash    (hash) default settings
    
        """
    
        testHash       = {}
        for testName in testList:
           # enter test if active and copy default hash
    
           for systemName in systemList:
               fullTestName             = self.getFullTestName( systemName, testName )
               hashCopy                 = self.copyHash( defaultParameterHash )
               testHash[fullTestName]   = hashCopy
               hashCopy['FullTestName'] = fullTestName
               hashCopy['TestName']     = testName
               hashCopy['SystemName']   = systemName
               
        return testHash
    
    #=============================================================================================
    # Edit test hash based on command file input
    #=============================================================================================
    
    def editTestHashBasedOnControlFile( self, controlFileName, testHash ):
    
        controlFile = open( controlFileName, 'r')
    
        activeBlock      = 0
        testBlockHash    = {}
        systemBlockHash  = {}
        while True:
    
            line = controlFile.readline()
            if line is None: break
            if len(line) <= 0: break
            
            #print "XXX %s" % line
            #sys.stdout.flush()
            # parse file
            # sample format:
    
            #    Block 
            #    Test [list of test names] (optional)
            #    System [list of systems]  (optional)
            #    Active 1
            #    Tolerance 1.0e-02
            #    Cutoff 1.0e-02
            #    EndBlock
    
            if line.startswith('Block'):
                tag = line.rstrip().split(None)
                for testName in testBlockHash.iterkeys():
                    testBlockHash[testName] = -1
                for systemName in systemBlockHash.iterkeys():
                    systemBlockHash[systemName] = -1
                activeBlock       = 1
                testActiveBlock   = 0
                systemActiveBlock = 0
            elif line.startswith('EndBlock'):
                activeBlock = 0
    
            # skip comments
    
            elif line.startswith('#'):
    
                nothing = 1
    
            elif line.startswith('Test'):
                testActiveBlock   = 1
                testList = line.rstrip().split(None)
                for testName in testList:
                    testBlockHash[testName] = 1
    
            elif line.startswith('System'):
                systemActiveBlock = 1
                systemList = line.rstrip().split(None)
                for systemName in systemList:
                    systemBlockHash[systemName] = 1
    
            # override settings
    
            # if testActiveBlock ==     0 and systemActiveBlock    == 0,  reset all entries
            # if testActiveBlock ==     0 and systemActiveBlock is not 0, reset all entries w/ systemName
            # if testActiveBlock is not 0 and systemActiveBlock     == 0, reset all entries w/ testName
            # if testActiveBlock is not 0 and systemActiveBlock is not 0, reset all entries w/ systemName and testName
    
            elif( activeBlock and len(line) > 1 ):

                # get key (string) and value (string, float, int)

                key, valueStr = line.rstrip().split(None)
                try:
                    value = float(valueStr) if '.' in valueStr else int(valueStr)
                except ValueError:
                    value = valueStr
 
                # all tests and all systems
    
                if( testActiveBlock == 0 and systemActiveBlock == 0 ):
                    for fullTestName, parameterHash in testHash.iteritems():
                        parameterHash[key] = value

                # all tests and listed systems

                elif( testActiveBlock == 0 ):
                    for fullTestName, parameterHash in testHash.iteritems():
                        systemName = parameterHash['SystemName']
                        if( systemName in systemBlockHash and systemBlockHash[systemName] == 1 ):
                            parameterHash[key] = value

                # all systems and listed tests

                elif( systemActiveBlock == 0 ):
                    for fullTestName, parameterHash in testHash.iteritems():
                        testName = parameterHash['TestName']
                        if( testName in testBlockHash and testBlockHash[testName] == 1):
                            parameterHash[key] = value

                # particular systems and tests

                else:
     
                    for testName,testFlag in testBlockHash.iteritems():
                        if( testFlag > 0 ):
                            for systemName, systemFlag in systemBlockHash.iteritems():
                                if( systemFlag > 0 ):
                                        fullTestName = self.getFullTestName( systemName, testName )
                                        if( fullTestName in testHash ):
                                            parameterHash      = testHash[fullTestName]
                                            parameterHash[key] = value
    
        controlFile.close()
    
        return
    
    #=============================================================================================
    # Print test hash
    #=============================================================================================
    
    def printTestHash(self, testHash, activeMatch = -1 ):
    
        outputString = "Test hash\n"
        firstTab     = 40
        secondTab    = 30
        for fullTestName in sorted( testHash.iterkeys() ):
            parameterHash = testHash[fullTestName]
            if( activeMatch == -1 or activeMatch == parameterHash['Active'] ):
                outputString += parameterHash['TestName'].rjust(firstTab) + " " + parameterHash['SystemName'].rjust(secondTab) + "\n"
                for key in sorted(parameterHash.iterkeys()):
                    if( key != 'TestName' and key != 'SystemName' and key != 'FullTestName' and ( activeMatch == -1 or activeMatch == parameterHash['Active']) ):
                        outputString += key.rjust(firstTab) + str( parameterHash[key] ).rjust(secondTab) + "\n"
                outputString += "\n"
    
        return outputString
    
    
    #=============================================================================================
    # Print hash
    #=============================================================================================
    
    def printHash(self, hash, idString = "" ):
    
        outputString = idString
        firstTab     = 40
        secondTab    = 60
        for key in sorted( hash.iterkeys() ):
            value         = hash[key]
            outputString += key.rjust(firstTab) + str( value ).rjust(secondTab) + "\n"
    
        return outputString
    
    #=============================================================================================
    # Set verbosity
    #=============================================================================================
    
    def setVerbose(self, verbose ):
        self._verbose = verbose 
    
    #=============================================================================================
    # Write positions to file
    #=============================================================================================
    
    def writePositionsToFile( self, positions, fileName = "" ):
    
        try:
            if( fileName ):
                positionsFile  = open( fileName, 'w')
                positionString = "%8d  Positions\n" % (len( positions) )
                positionsFile.write( positionString )
                for ii in range( len( positions) ):
                    positionString = "%8d  %18.10e %18.10e %18.10e\n" % ( ii, positions[ii][0], positions[ii][1], positions[ii][2] )
                    positionsFile.write( positionString )
                positionsFile.close()

            if( self._verbose ):
                print( "Positions -- output to file=%s\n" ) % (fileName)
 
            return

        except:
            print "Positions output exception <", sys.exc_info()[0], ">" 

    #=============================================================================================
    # Serialize system and output to file
    #=============================================================================================
    
    def serializeSystemToFile( self, system, fileName = "" ):
    
        try:
            serializationString = self._openMM.XmlSerializer.serializeSystem( system )

            if( fileName ):
                serializationFile = open( fileName, 'w')
                serializationFile.write( serializationString )
                serializationFile.close()

            if( self._verbose ):
                print( "Serialized system -- output to file=%s\n" ) % (fileName)
                # print( "Serialized system %s\n" ) % serializationString
 
            return serializationString

        except:
            print "Serialization Exception <", sys.exc_info()[0], ">" 
            print "Serialization Exception <", sys.exc_value,  ">" 

    #=============================================================================================
    # Serialize
    #=============================================================================================

    def serialize(self, testName, system, positions, serializeDirectory, serializeFileName ):

        if self._verbose: 
            print "Serialize: %s" % (testName)
            if system is not None:
                print "         Particles=%d NumForces=%d " % (system.getNumParticles(), system.getNumForces() )

        if system is None:
            print "Serialize : %s no system available" % (testName)
            return
                
        # serialize system

        xmlSerializeFileName  = serializeFileName + '.xml'
        fullSerializeFileName = os.path.join( serializeDirectory, xmlSerializeFileName )
        if( not os.path.isfile( fullSerializeFileName ) ):
            try:
                self.serializeSystemToFile( system, fullSerializeFileName )
                if self._verbose:
                    print "Serialized system to file %s" % (fullSerializeFileName)
                    sys.stdout.flush()
            except:
                print "serialization problem for xml file of %s" % (testName)
                sys.stdout.flush()
                raise
        else:
            if self._verbose:
                print "Serialized file %s exists -- present system not serialized." % (fullSerializeFileName)
    
        # write positions

        if( positions is not None ):
            positionFileName       = serializeFileName + '.txt'
            fullPositionFileName   = os.path.join( serializeDirectory, positionFileName )
            if( not os.path.isfile( fullPositionFileName ) ):
                self.writePositionsToFile(  positions, fullPositionFileName )
                if self._verbose:
                    print "Wrote positions to %s" % fullPositionFileName
                    sys.stdout.flush()
            else:
                print "Positions file %s exists -- not overwritten." % fullPositionFileName
                sys.stdout.flush()

    #=============================================================================================
    # Deserialize system from file
    #=============================================================================================
    
    def deserializeSystemFromFile( self, fileName):
    
        try:

            if( not os.path.exists( fileName ) ):
                print( "Serialized system file=%s is not available.\n" ) % (fileName)
                return

            serializationFile    = open( fileName, 'r')

            serializationString  = serializationFile.read( )
            serializationFile.close()
            if( self._verbose ):
                print( "Deserializing system from file=%s\n" ) % (fileName)
                #print( "Deserializing system from file=%s\n" ) % (serializationString)

            system = self._openMM.XmlSerializer.deserializeSystem( serializationString )

            if( self._verbose ):
                print( "Deserializing system from file=%s completed.\n" ) % (fileName)
                sys.stdout.flush()

            return system

        except:
            print "Deserialization Exception <", sys.exc_info()[0], ">" 
            print "Deserialization Exception <", sys.exc_value,  ">" 

    #=============================================================================================
    # Make copy of AmoebaHarmonicBondForce
    #=============================================================================================

    def copyAmoebaHarmonicBondForce(self, harmonicBondForceToCopy):
        """Return a copy of a OpenMM::AmoebaHarmonicBondForce

        ARGUMENTS

        uarmonicBondForceToCopy (OpenMM::AmoebaHarmonicBondForce) force to copy

        """
        newAmoebaHarmonicBondForce    = self._openMM.AmoebaHarmonicBondForce()

        newAmoebaHarmonicBondForce.setAmoebaGlobalHarmonicBondCubic(   harmonicBondForceToCopy.getAmoebaGlobalHarmonicBondCubic() )
        newAmoebaHarmonicBondForce.setAmoebaGlobalHarmonicBondQuartic( harmonicBondForceToCopy.getAmoebaGlobalHarmonicBondQuartic() )

        forceConstantUnit             = self._energyUnit / self._lengthUnit**2
        sys.stdout.flush()
        for ii in range( harmonicBondForceToCopy.getNumBonds() ):
            sys.stdout.flush()
            args = harmonicBondForceToCopy.getBondParameters(ii)
            #if self._verbose: print " AmoebaBond: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaHarmonicBondForce.addBond( args[0], args[1], args[2], args[3] )

        return newAmoebaHarmonicBondForce

    #=============================================================================================
    # Make copy of AmoebaHarmonicAngleForce
    #=============================================================================================

    def copyAmoebaHarmonicAngleForce(self, harmonicAngleForceToCopy):
        """Return a copy of a OpenMM::AmoebaHarmonicAngleForce

        ARGUMENTS

        uarmonicAngleForceToCopy (OpenMM::AmoebaHarmonicAngleForce) force to copy

        """
        newAmoebaHarmonicAngleForce    = self._openMM.AmoebaHarmonicAngleForce()

        newAmoebaHarmonicAngleForce.setAmoebaGlobalHarmonicAngleCubic(   harmonicAngleForceToCopy.getAmoebaGlobalHarmonicAngleCubic() )
        newAmoebaHarmonicAngleForce.setAmoebaGlobalHarmonicAngleQuartic( harmonicAngleForceToCopy.getAmoebaGlobalHarmonicAngleQuartic() )
        newAmoebaHarmonicAngleForce.setAmoebaGlobalHarmonicAnglePentic(  harmonicAngleForceToCopy.getAmoebaGlobalHarmonicAnglePentic() )
        newAmoebaHarmonicAngleForce.setAmoebaGlobalHarmonicAngleSextic(  harmonicAngleForceToCopy.getAmoebaGlobalHarmonicAngleSextic() )

        forceConstantUnit             = self._energyUnit / self._lengthUnit**2
        sys.stdout.flush()
        for ii in range( harmonicAngleForceToCopy.getNumAngles() ):
            sys.stdout.flush()
            args = harmonicAngleForceToCopy.getAngleParameters(ii)
            #if self._verbose: print " AmoebaAngle: %5d [%5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaHarmonicAngleForce.addAngle( args[0], args[1], args[2], args[3], args[4] )

        return newAmoebaHarmonicAngleForce

    #=============================================================================================
    # Make copy of AmoebaHarmonicInPlaneAngleForce
    #=============================================================================================

    def copyAmoebaHarmonicInPlaneAngleForce(self, harmonicInPlaneAngleForceToCopy):
        """Return a copy of a OpenMM::AmoebaHarmonicInPlaneAngleForce

        ARGUMENTS

        uarmonicInPlaneAngleForceToCopy (OpenMM::AmoebaHarmonicInPlaneAngleForce) force to copy

        """
        newAmoebaHarmonicInPlaneAngleForce    = self._openMM.AmoebaHarmonicInPlaneAngleForce()

        newAmoebaHarmonicInPlaneAngleForce.setAmoebaGlobalHarmonicInPlaneAngleCubic(   harmonicInPlaneAngleForceToCopy.getAmoebaGlobalHarmonicInPlaneAngleCubic() )
        newAmoebaHarmonicInPlaneAngleForce.setAmoebaGlobalHarmonicInPlaneAngleQuartic( harmonicInPlaneAngleForceToCopy.getAmoebaGlobalHarmonicInPlaneAngleQuartic() )
        newAmoebaHarmonicInPlaneAngleForce.setAmoebaGlobalHarmonicInPlaneAnglePentic(  harmonicInPlaneAngleForceToCopy.getAmoebaGlobalHarmonicInPlaneAnglePentic() )
        newAmoebaHarmonicInPlaneAngleForce.setAmoebaGlobalHarmonicInPlaneAngleSextic(  harmonicInPlaneAngleForceToCopy.getAmoebaGlobalHarmonicInPlaneAngleSextic() )

        forceConstantUnit             = self._energyUnit / self._lengthUnit**2
        sys.stdout.flush()
        for ii in range( harmonicInPlaneAngleForceToCopy.getNumAngles() ):
            sys.stdout.flush()
            args = harmonicInPlaneAngleForceToCopy.getAngleParameters(ii)
            #if self._verbose: print " AmoebaInPlaneAngle: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaHarmonicInPlaneAngleForce.addAngle( args[0], args[1], args[2], args[3], args[4], args[5] )

        return newAmoebaHarmonicInPlaneAngleForce

    #=============================================================================================
    # Make copy of AmoebaOutOfPlaneBendForce
    #=============================================================================================

    def copyAmoebaOutOfPlaneBendForce(self, outOfPlaneBendForceToCopy):
        """Return a copy of a OpenMM::AmoebaOutOfPlaneBendForce

        ARGUMENTS

        copyAmoebaOutOfPlaneBendForce (OpenMM::AmoebaOutOfPlaneBendForce) force to copy

        """
        newAmoebaOutOfPlaneBendForce    = self._openMM.AmoebaOutOfPlaneBendForce()

        newAmoebaOutOfPlaneBendForce.setAmoebaGlobalOutOfPlaneBendCubic(   outOfPlaneBendForceToCopy.getAmoebaGlobalOutOfPlaneBendCubic() )
        newAmoebaOutOfPlaneBendForce.setAmoebaGlobalOutOfPlaneBendQuartic( outOfPlaneBendForceToCopy.getAmoebaGlobalOutOfPlaneBendQuartic() )
        newAmoebaOutOfPlaneBendForce.setAmoebaGlobalOutOfPlaneBendPentic(  outOfPlaneBendForceToCopy.getAmoebaGlobalOutOfPlaneBendPentic() )
        newAmoebaOutOfPlaneBendForce.setAmoebaGlobalOutOfPlaneBendSextic(  outOfPlaneBendForceToCopy.getAmoebaGlobalOutOfPlaneBendSextic() )

        for ii in range( outOfPlaneBendForceToCopy.getNumOutOfPlaneBends() ):
            sys.stdout.flush()
            args = outOfPlaneBendForceToCopy.getOutOfPlaneBendParameters(ii)
            #if self._verbose: print " AmoebaOutOfPlaneBendForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaOutOfPlaneBendForce.addOutOfPlaneBend( args[0], args[1], args[2], args[3], args[4] )

        return newAmoebaOutOfPlaneBendForce

    #=============================================================================================
    # Make copy of AmoebaPiTorsionForce
    #=============================================================================================

    def copyAmoebaPiTorsionForce(self, piTorsionForceToCopy):
        """Return a copy of a OpenMM::AmoebaPiTorsionForce

        ARGUMENTS

        copyAmoebaPiTorsionForce (OpenMM::AmoebaPiTorsionForce) force to copy

        """
        newAmoebaPiTorsionForce    = self._openMM.AmoebaPiTorsionForce()

        for ii in range( piTorsionForceToCopy.getNumPiTorsions() ):
            sys.stdout.flush()
            args = piTorsionForceToCopy.getPiTorsionParameters(ii)
            #if self._verbose: print " AmoebaPiTorsionForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaPiTorsionForce.addPiTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6] )

        return newAmoebaPiTorsionForce

    #=============================================================================================
    # Make copy of AmoebaTorsionForce
    #=============================================================================================

    def copyAmoebaTorsionForce(self, torsionForceToCopy):
        """Return a copy of a OpenMM::AmoebaTorsionForce

        ARGUMENTS

        copyAmoebaTorsionForce (OpenMM::AmoebaTorsionForce) force to copy

        """
        newAmoebaTorsionForce    = self._openMM.AmoebaTorsionForce()

        for ii in range( torsionForceToCopy.getNumTorsions() ):
            sys.stdout.flush()
            args = torsionForceToCopy.getTorsionParameters(ii)
            #if self._verbose: print " AmoebaTorsionForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            print "Torsion args %d" % len( args )
            if self._verbose: print " AmoebaTorsionForce: %5d [%5d %5d %5d %5d]" % (ii, args[0], args[1], args[2], args[3])
            #if self._verbose: print " AmoebaTorsionForce: %s %s %s" % ( type( args[4] ), type( args[5] ),type( args[6] ) )
            #if self._verbose: print " AmoebaTorsionForce: %s %s %s" % ( args[4], type( args[5] ),type( args[6] ) )
            #if self._verbose: print " AmoebaTorsionForce: %s " % ( type(args[4][0]) )

            #torsion1    = simtk.openmm.vectord(2)
            #torsion1    = simtk.openmm.vectord
            #torsion1.append( float( args[4][0] ) )
            #torsion1.append( float( args[4][1] ) )

            torsion1    = simtk.openmm.vectord(2)
            torsion1[0] = args[4][0]
            torsion1[1] = args[4][1]
            #if self._verbose: print " AmoebaTorsionForce: type torsion1=%s " % ( type(torsion1) )

            torsion2    = simtk.openmm.vectord(2)
            torsion2[0] = args[5][0]
            torsion2[1] = args[5][1]

            torsion3    = simtk.openmm.vectord(2)
            torsion3[0] = args[6][0]
            torsion3[1] = args[6][1]

            #torsion1 = []
            #torsion1.append( float( args[4][0] ) )
            #torsion1.append( float( args[4][1] ) )

            #if self._verbose: print " AmoebaTorsionForce: %s" % ( type( torsion1 ) )
            #if self._verbose: print " AmoebaTorsionForce: %s" % ( torsion1 )
            #newAmoebaTorsionForce.addTorsion( args[0], args[1], args[2], args[3], torsion1, args[5], args[6] )
            newAmoebaTorsionForce.addTorsion( args[0], args[1], args[2], args[3], torsion1, torsion2, torsion3 )

        return newAmoebaTorsionForce

    #=============================================================================================
    # Make copy of AmoebaTorsionTorsionForce
    #=============================================================================================

    def copyAmoebaTorsionTorsionForce(self, torsiontorsionForceToCopy):
        """Return a copy of a OpenMM::AmoebaTorsionTorsionForce

        ARGUMENTS

        copyAmoebaTorsionTorsionForce (OpenMM::AmoebaTorsionTorsionForce) force to copy

        """
        newAmoebaTorsionTorsionForce    = self._openMM.AmoebaTorsionTorsionForce()

        for ii in range( torsiontorsionForceToCopy.getNumTorsionTorsions() ):
            args = torsiontorsionForceToCopy.getTorsionTorsionParameters(ii)
            #if self._verbose: print " AmoebaTorsionTorsionForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newAmoebaTorsionTorsionForce.addTorsionTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6] )

        for ii in range( torsiontorsionForceToCopy.getNumTorsionTorsionGrids() ):
            args = torsiontorsionForceToCopy.getTorsionTorsionGrid(ii)
            #if self._verbose: print " AmoebaTorsionTorsionForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            #print "TorsionTorsion args %d   %s"   % (len( args       ), type( args       ) )
            #print "TorsionTorsion args0 %d  %s"  % (len( args[0]    ), type( args[0]    ) )
            #print "TorsionTorsion args00 %d %s" % (len( args[0][0] ), type( args[0][0] ) )
            #grid  = simtk.openmm.vectorddd(25)
            #print "TorsionTorsionForce: type grid=%s " % ( type(grid) )
            #sys.stdout.flush()
            #if self._verbose: print " AmoebaTorsionTorsionForce: %5d [%12.4f %12.4f]" % (ii, args[0], args[1])
            #if self._verbose: print " AmoebaTorsionForce: %s %s %s" % ( type( args[4] ), type( args[5] ),type( args[6] ) )

            newAmoebaTorsionTorsionForce.setTorsionTorsionGrid( ii, args )

        return newAmoebaTorsionTorsionForce

    #=============================================================================================
    # Make copy of AmoebaStretchBendForce
    #=============================================================================================

    def copyAmoebaStretchBendForce(self, stretchBendForceToCopy):
        """Return a copy of a OpenMM::AmoebaStretchBendForce

        ARGUMENTS

        stretchBendForceToCopy (OpenMM::AmoebaStretchBendForce) force to copy

        """
        newAmoebaStretchBendForce    = self._openMM.AmoebaStretchBendForce()

        for ii in range( stretchBendForceToCopy.getNumStretchBends() ):
            args = stretchBendForceToCopy.getStretchBendParameters(ii)
            newAmoebaStretchBendForce.addStretchBend( args[0], args[1], args[2], args[3], args[4], args[5], args[6] )

        return newAmoebaStretchBendForce

    #=============================================================================================
    # Make copy of AmoebaUreyBradleyForce
    #=============================================================================================

    def copyAmoebaUreyBradleyForce(self, ureyBradleyForceToCopy):
        """Return a copy of a OpenMM::AmoebaUreyBradleyForce

        ARGUMENTS

        ureyBradleyForceToCopy (OpenMM::AmoebaUreyBradleyForce) force to copy

        """
        newAmoebaUreyBradleyForce    = self._openMM.AmoebaUreyBradleyForce()

        newAmoebaUreyBradleyForce.setAmoebaGlobalUreyBradleyCubic(   ureyBradleyForceToCopy.getAmoebaGlobalUreyBradleyCubic() )
        newAmoebaUreyBradleyForce.setAmoebaGlobalUreyBradleyQuartic( ureyBradleyForceToCopy.getAmoebaGlobalUreyBradleyQuartic() )

        for ii in range( ureyBradleyForceToCopy.getNumInteractions() ):
            args = ureyBradleyForceToCopy.getUreyBradleyParameters(ii)
            newAmoebaUreyBradleyForce.addAngle( args[0], args[1], args[2], args[3] )

        return newAmoebaUreyBradleyForce

    #=============================================================================================
    # Make copy of AmoebaWcaDispersionForce
    #=============================================================================================

    def copyAmoebaWcaDispersionForce(self, wcaDispersionForceToCopy):
        """Return a copy of a OpenMM::AmoebaWcaDispersionForce

        ARGUMENTS

        wcaDispersionForceToCopy (OpenMM::AmoebaWcaDispersionForce) force to copy

        """
        newAmoebaWcaDispersionForce    = self._openMM.AmoebaWcaDispersionForce()

        newAmoebaWcaDispersionForce.setEpso(     wcaDispersionForceToCopy.getEpso() )
        newAmoebaWcaDispersionForce.setEpsh(     wcaDispersionForceToCopy.getEpsh() )
        newAmoebaWcaDispersionForce.setRmino(    wcaDispersionForceToCopy.getRmino() )
        newAmoebaWcaDispersionForce.setRminh(    wcaDispersionForceToCopy.getRminh() )
        newAmoebaWcaDispersionForce.setAwater(   wcaDispersionForceToCopy.getAwater() )
        newAmoebaWcaDispersionForce.setShctd(    wcaDispersionForceToCopy.getShctd() )
        newAmoebaWcaDispersionForce.setDispoff(  wcaDispersionForceToCopy.getDispoff() )
        newAmoebaWcaDispersionForce.setSlevy(    wcaDispersionForceToCopy.getSlevy() )

        for ii in range( wcaDispersionForceToCopy.getNumParticles() ):
            args = wcaDispersionForceToCopy.getParticleParameters(ii)
            newAmoebaWcaDispersionForce.addParticle( args[0], args[1] )

        return newAmoebaWcaDispersionForce

    #=============================================================================================
    # Make copy of AmoebaGeneralizedKirkwoodForce
    #=============================================================================================

    def copyAmoebaGeneralizedKirkwoodForce(self, generalizedKirkwoodForceToCopy):
        """Return a copy of a OpenMM::AmoebaGeneralizedKirkwoodForce

        ARGUMENTS

        generalizedKirkwoodForceToCopy (OpenMM::AmoebaGeneralizedKirkwoodForce) force to copy

        """
        newAmoebaGeneralizedKirkwoodForce    = self._openMM.AmoebaGeneralizedKirkwoodForce()

        newAmoebaGeneralizedKirkwoodForce.setSolventDielectric(    generalizedKirkwoodForceToCopy.getSolventDielectric() )
        newAmoebaGeneralizedKirkwoodForce.setSoluteDielectric(     generalizedKirkwoodForceToCopy.getSoluteDielectric() )
        newAmoebaGeneralizedKirkwoodForce.setDielectricOffset(     generalizedKirkwoodForceToCopy.getDielectricOffset() )
        newAmoebaGeneralizedKirkwoodForce.setIncludeCavityTerm(    generalizedKirkwoodForceToCopy.getIncludeCavityTerm() )
        newAmoebaGeneralizedKirkwoodForce.setProbeRadius(          generalizedKirkwoodForceToCopy.getProbeRadius() )
        newAmoebaGeneralizedKirkwoodForce.setSurfaceAreaFactor(    generalizedKirkwoodForceToCopy.getSurfaceAreaFactor() )

        for ii in range( generalizedKirkwoodForceToCopy.getNumParticles() ):
            args = generalizedKirkwoodForceToCopy.getGeneralizedKirkwoodParameters(ii)
            newAmoebaGeneralizedKirkwoodForce.addParticle( args[0], args[1], args[2] )

        return newAmoebaGeneralizedKirkwoodForce

    #=============================================================================================
    # Make copy of AmoebaVdwForce
    #=============================================================================================

    def copyAmoebaVdwForce(self, vdwForceToCopy):
        """Return a copy of a OpenMM::AmoebaVdwForce

        ARGUMENTS

        vdwForceToCopy (OpenMM::AmoebaVdwForce) force to copy

        """
        newAmoebaVdwForce    = self._openMM.AmoebaVdwForce()

        newAmoebaVdwForce.setSigmaCombiningRule(     vdwForceToCopy.getSigmaCombiningRule() )
        newAmoebaVdwForce.setEpsilonCombiningRule(   vdwForceToCopy.getEpsilonCombiningRule() )
        newAmoebaVdwForce.setCutoff(                 vdwForceToCopy.getCutoff() )
        newAmoebaVdwForce.setUseNeighborList(        vdwForceToCopy.getUseNeighborList() )
        newAmoebaVdwForce.setPBC(                    vdwForceToCopy.getPBC() )

        for ii in range( vdwForceToCopy.getNumParticles() ):
            args = vdwForceToCopy.getParticleParameters(ii)
            newAmoebaVdwForce.addParticle( args[0], args[1], args[2], args[3], args[4] )

        for ii in range( vdwForceToCopy.getNumParticles() ):
            args = vdwForceToCopy.getParticleExclusions( ii )
            newAmoebaVdwForce.setParticleExclusions( ii, args )

        return newAmoebaVdwForce

    #=============================================================================================
    # Make copy of AmoebaMultipoleForce
    #=============================================================================================

    def copyAmoebaMultipoleForce(self, multipoleForceToCopy):
        """Return a copy of a OpenMM::AmoebaMultipoleForce

        ARGUMENTS

        multipoleForceToCopy (OpenMM::AmoebaMultipoleForce) force to copy

        """
        newAmoebaMultipoleForce    = self._openMM.AmoebaMultipoleForce()

        newAmoebaMultipoleForce.setNonbondedMethod(                multipoleForceToCopy.getNonbondedMethod() )
        newAmoebaMultipoleForce.setCutoffDistance(                 multipoleForceToCopy.getCutoffDistance() )
        newAmoebaMultipoleForce.setAEwald(                         multipoleForceToCopy.getAEwald() )
        newAmoebaMultipoleForce.setPmeBSplineOrder(                multipoleForceToCopy.getPmeBSplineOrder() )
        newAmoebaMultipoleForce.setPmeGridDimensions(              multipoleForceToCopy.getPmeGridDimensions() )
        newAmoebaMultipoleForce.setMutualInducedIterationMethod(   multipoleForceToCopy.getMutualInducedIterationMethod() )
        newAmoebaMultipoleForce.setMutualInducedMaxIterations(     multipoleForceToCopy.getMutualInducedMaxIterations() )
        newAmoebaMultipoleForce.setMutualInducedTargetEpsilon(     multipoleForceToCopy.getMutualInducedTargetEpsilon() )
        newAmoebaMultipoleForce.setElectricConstant(               multipoleForceToCopy.getElectricConstant() )
        newAmoebaMultipoleForce.setEwaldErrorTolerance(            multipoleForceToCopy.getEwaldErrorTolerance() )

        for ii in range( multipoleForceToCopy.getNumMultipoles() ):
            args = multipoleForceToCopy.getMultipoleParameters(ii)
            newAmoebaMultipoleForce.addParticle( args[0], args[1], args[2], args[3], args[4],  args[5], args[6], args[7], args[8], args[9] )

        for ii in range( 0, 8 ):
            for jj in range( multipoleForceToCopy.getNumMultipoles() ):
                args = multipoleForceToCopy.getCovalentMap( jj, ii )
                newAmoebaMultipoleForce.setCovalentMap( jj, ii, args )

        return newAmoebaMultipoleForce

    #=============================================================================================
    # Make copy of CMAPTorsionForce
    #=============================================================================================

    def copyCMAPTorsionForce(self, cMAPTorsionForceToCopy):
        """Return a copy of a OpenMM::CMAPTorsionForce

        ARGUMENTS

        copyCMAPTorsionForce (OpenMM::CMAPTorsionForce) force to copy

        """
        newCMAPTorsionForce    = self._openMM.CMAPTorsionForce()

        for ii in range( cMAPTorsionForceToCopy.getNumTorsions() ):
            args = cMAPTorsionForceToCopy.getTorsionParameters(ii)
            #if self._verbose: print " CMAPTorsionForce: %5d [%5d %5d %5d %5d] [%14.7f %14.7f]" % (ii, args[0], args[1], args[2], args[3], args[2]/self._lengthUnit, args[3]/forceConstantUnit)
            newCMAPTorsionForce.addTorsion( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8] )

        for ii in range( cMAPTorsionForceToCopy.getNumMaps() ):
            args = cMAPTorsionForceToCopy.getMapParameters( ii )
            newCMAPTorsionForce.addMap( args[0], args[1] )

        return newCMAPTorsionForce

    #=============================================================================================
    # Get force by name; return none if not found
    #=============================================================================================

    def getForceByName(self, system, forceName ):
        numForces = system.getNumForces()
        for ii in range( numForces ):
            force = system.getForce( ii ) 
            id    = str(force)
            if( forceName in id ):
                return force
        return None

    #=============================================================================================
    # Print default box vectors
    #=============================================================================================

    def printDefaultBoxVectors(self, testname, system ):

        lengthUnit         = self.getLengthUnit( )

        print "\n\n" + testname
        boxVectors = system.getDefaultPeriodicBoxVectors()
        print "Box %s" % (str(boxVectors))

    #=============================================================================================
    # Print nonbonded info
    #=============================================================================================

    def printNonbonded(self, testname, nonbondedForce):

        dimensionlessUnit  = self.getDimensionlessUnit( )
        chargeUnit         = self.getChargeUnit( )
        lengthUnit         = self.getLengthUnit( )
        energyUnit         = self.getEnergyUnit( )

        print "\n\n" + testname
        print "Nonbonded particles      %14d"   %  nonbondedForce.getNumParticles()
        print "Nonbonded exceptions     %14d"   %  nonbondedForce.getNumExceptions()
        print "Nonbond method           %14s"   %  nonbondedForce.getNonbondedMethod()
        print "Cutoff distance          %14.7f" % (nonbondedForce.getCutoffDistance()/lengthUnit)
        print "ReactionFieldDielectric  %14.7f" % (nonbondedForce.getReactionFieldDielectric()/dimensionlessUnit)
        print "EwaldErrorTolerance      %14.7e" %  nonbondedForce.getEwaldErrorTolerance()
        ii                 = 0
        maxPrint           = 10
        while( ii < nonbondedForce.getNumParticles() ):
            args = nonbondedForce.getParticleParameters(ii)
            print "Nonbonded: %5d [%14.7f %14.7f %14.7e]" % (ii, args[0]/chargeUnit, args[1]/lengthUnit, args[2]/energyUnit)
            ii  += 1
            if( ii == maxPrint and nonbondedForce.getNumParticles() > 2*maxPrint  ):ii = nonbondedForce.getNumParticles() - maxPrint

        ii                 = 0
        while( ii < nonbondedForce.getNumExceptions() ):
            args = nonbondedForce.getExceptionParameters(ii)
            print "Nonbonded exceptions: %5d [%5d %5d] [%14.7f %14.7f %14.7e]" % (ii, args[0], args[1], args[2]/chargeUnit**2, args[3]/lengthUnit, args[4]/energyUnit)
            ii  += 1
            if( ii == maxPrint and nonbondedForce.getNumExceptions() > 2*maxPrint  ):ii = nonbondedForce.getNumExceptions() - maxPrint

    #=============================================================================================
    # Print custom nonbonded info
    #=============================================================================================

    def printCustomNonbonded(self, testname, customNonbondedForce):

        lengthUnit         = self.getLengthUnit( )

        print "\n\n" + testname
        print "CustomNonbonded particles         %14d"   %  customNonbondedForce.getNumParticles()
        print "CustomNonbonded exclusions        %14d"   %  customNonbondedForce.getNumExclusions()
        print "CustomNonbonded particle/parm     %14d"   %  customNonbondedForce.getNumPerParticleParameters()
        print "CustomNonbonded particle/global   %14d"   %  customNonbondedForce.getNumGlobalParameters()
        print "CustomNonbonded functions         %14d"   %  customNonbondedForce.getNumFunctions()
        print "Energy expression                 %s"     %  customNonbondedForce.getEnergyFunction()
        print "Nonbonded method                  %14s"   %  customNonbondedForce.getNonbondedMethod()
        print "Cutoff distance                  %14.7f"  % (customNonbondedForce.getCutoffDistance()/lengthUnit)
        for jj in range( customNonbondedForce.getNumGlobalParameters() ):
            print "Global: %10s %15.7e" %( customNonbondedForce.getGlobalParameterName( jj ), customNonbondedForce.getGlobalParameterDefaultValue(jj) )

        ii                 = 0
        maxPrint           = 10
        while( ii < customNonbondedForce.getNumParticles() ):
            args = customNonbondedForce.getParticleParameters(ii)
            print "CustomNonbonded : %5d %s" % (ii, str(args))
            ii  += 1
            if( ii == maxPrint and customNonbondedForce.getNumParticles() > 2*maxPrint  ):ii = customNonbondedForce.getNumParticles() - maxPrint

        ii                 = 0
        while( ii < customNonbondedForce.getNumExclusions() ):
            args = customNonbondedForce.getExclusionParticles(ii)
            print "Nonbonded exceptions: %5d [%5d %5d]" % (ii, args[0], args[1] )
            ii  += 1
            if( ii == maxPrint and customNonbondedForce.getNumExclusions() > 2*maxPrint  ):ii = customNonbondedForce.getNumExclusions() - maxPrint

    #=============================================================================================
    # Print custom nonbonded info
    #=============================================================================================

    def printCustomBonded(self, testname, customBondedForce):

        lengthUnit         = self.getLengthUnit( )

        print "\n\n" + testname
        print "CustomBonded bonds             %14d"   %  customBondedForce.getNumBonds()
        print "CustomBonded particle/parm     %14d"   %  customBondedForce.getNumPerBondParameters()
        print "CustomBonded particle/global   %14d"   %  customBondedForce.getNumGlobalParameters()
        print "Energy expression                %s"   %  customBondedForce.getEnergyFunction()
        for jj in range( customBondedForce.getNumGlobalParameters() ):
            print "Global: %10s %15.7e" %( customBondedForce.getGlobalParameterName( jj ), customBondedForce.getGlobalParameterDefaultValue(jj) )

        ii                 = 0
        maxPrint           = 10
        while( ii < customBondedForce.getNumBonds() ):
            args = customBondedForce.getBondParameters(ii)
            print "CustomBonded : %5d %s" % (ii, str(args))
            ii  += 1
            if( ii == maxPrint and customBondedForce.getNumBonds() > 2*maxPrint  ):ii = customBondedForce.getNumBonds() - maxPrint

    #=============================================================================================
    # Print GBSA Obc info
    #=============================================================================================

    def printGbsaObc(self, testName, obcForce):

        dimensionlessUnit  = self.getDimensionlessUnit( )
        chargeUnit         = self.getChargeUnit( )
        lengthUnit         = self.getLengthUnit( )

        print "\n\n" + testName
        print "GbsaObc particles      %14d"   %  obcForce.getNumParticles()
        print "Gbsa Obc method        %14s"   %  obcForce.getNonbondedMethod()
        print "Solute dielectric      %14.7f" % (obcForce.getSoluteDielectric()/dimensionlessUnit)
        print "Solvent dielectric     %14.7f" % (obcForce.getSolventDielectric()/dimensionlessUnit)
        print "Cutoff distance        %14.7f" % (obcForce.getCutoffDistance()/lengthUnit)
        ii                 = 0
        maxPrint           = 10
        while( ii < obcForce.getNumParticles() ):
            args = obcForce.getParticleParameters(ii)
            print "GbsaObc: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0]/chargeUnit, args[1]/lengthUnit, args[2]/dimensionlessUnit)
            ii  += 1
            if( ii == maxPrint and obcForce.getNumParticles() > 2*maxPrint  ):ii = obcForce.getNumParticles() - maxPrint

    #=============================================================================================
    # Print Gbvi info
    #=============================================================================================

    def printGbvi(self, testName, gbviForce):

        energyUnit         = self.getEnergyUnit( )
        chargeUnit         = self.getChargeUnit( )
        lengthUnit         = self.getLengthUnit( )
        dimensionlessUnit  = self.getDimensionlessUnit( )

        print "\n\n" + testName
        print "Gbvi particles         %14d"   %  gbviForce.getNumParticles()
        print "Gbvi method            %14s"   %  gbviForce.getNonbondedMethod()
        print "Solute dielectric      %14.7f" % (gbviForce.getSoluteDielectric()/dimensionlessUnit)
        print "Solvent dielectric     %14.7f" % (gbviForce.getSolventDielectric()/dimensionlessUnit)
        print "Cutoff distance        %14.7f" % (gbviForce.getCutoffDistance()/lengthUnit)
        ii                 = 0
        maxPrint           = 10
        while( ii < gbviForce.getNumParticles() ):
            args = gbviForce.getParticleParameters(ii)
            #print "Gbvi: %5d [%14s %14s %14s]" % (ii, str(args[0]), str(args[1]), str(args[2]))
            print "Gbvi: %5d [%14.7f %14.7f %14.7f]" % (ii, args[0]/chargeUnit, args[1]/lengthUnit, args[2]/energyUnit)
            ii  += 1
            if( ii == maxPrint and gbviForce.getNumParticles() > 2*maxPrint  ):ii = gbviForce.getNumParticles() - maxPrint

        ii                 = 0
        maxPrint           = 10
        while( ii < gbviForce.getNumBonds() ):
            args = gbviForce.getBondParameters(ii)
            #print "Gbvi: %5d [%14s %14s %14s]" % (ii, str(args[0]), str(args[1]), str(args[2]))
            print "Gbvi bonds: %5d [%6d %6d %14.7f]" % (ii, args[0], args[1], args[2]/lengthUnit)
            ii  += 1
            if( ii == maxPrint and gbviForce.getNumBonds() > 2*maxPrint  ):ii = gbviForce.getNumBonds() - maxPrint

    #=============================================================================================
    # Add force
    #=============================================================================================

    def addForce(self, force, forceToAdd ):

        for ii in range( len( forceToAdd ) ):
            force[ii][0] += forceToAdd[ii][0]
            force[ii][1] += forceToAdd[ii][1]
            force[ii][2] += forceToAdd[ii][2]

        return

    #=============================================================================================
    # Subtract force
    #=============================================================================================

    def subtractForce(self, force, forceToAdd ):

        for ii in range( len( forceToAdd ) ):
            force[ii][0] -= forceToAdd[ii][0]
            force[ii][1] -= forceToAdd[ii][1]
            force[ii][2] -= forceToAdd[ii][2]

        return

    #=============================================================================================
    # Multiply force
    #=============================================================================================

    def multiplyForce(self, force, factor ):

        for ii in range( len( force ) ):
            force[ii][0] *= factor
            force[ii][1] *= factor
            force[ii][2] *= factor

        return

    #=============================================================================================
    # Set device id
    #=============================================================================================

    def setDeviceId(self, platform, deviceId ):

        if( deviceId < 0 ):
            return
      
        if( platform.getName() == 'Cuda' ):
            platform.setPropertyDefaultValue( "CudaDevice",  str(deviceId) );
        elif( platform.getName() == 'OpenCL' ):
            platform.setPropertyDefaultValue( "OpenCLDeviceIndex",  str(deviceId) );

        if( self._verbose ):
            print "Set gpu device id to %d" % ( deviceId )

    #=============================================================================================
    # Print platform properties
    #=============================================================================================

    def printPlatformProperties(self, context ):
        platform      = context.getPlatform()
        propertyNames = platform.getPropertyNames()
        ii = 0
        print "Properties for platform %s" % ( platform.getName() )
        while( ii < len( propertyNames ) ): 
            propertyName         = propertyNames[ii]
            propertyValue        = platform.getPropertyValue( context, propertyName )
            propertyDefaultValue = platform.getPropertyDefaultValue( propertyName )
            print "Property %s %s %s" % ( propertyName, propertyValue, propertyDefaultValue )
            ii = ii + 1

        return

    #=============================================================================================
    # Check is input is a number
    #=============================================================================================

    def isNumber(self, s):
        try:
            float(s)
            return True
        except ValueError:
            return False
