Page MenuHomec4science

nmtransform.py
No OneTemporary

File Metadata

Created
Thu, May 9, 23:40

nmtransform.py

"""Contains functions for doing the inverse and forward normal mode transforms.
Copyright (C) 2013, Joshua More and Michele Ceriotti
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/>.
Classes:
nm_trans: Uses matrix multiplication to do normal mode transformations.
nm_rescale: Uses matrix multiplication to do ring polymer contraction
or expansion.
nm_fft: Uses fast-Fourier transforms to do normal modes transformations.
Functions:
mk_nm_matrix: Makes a matrix to transform between the normal mode and bead
representations.
mk_rs_matrix: Makes a matrix to transform between one number of beads and
another. Higher normal modes in the case of an expansion are set to zero.
"""
__all__ = ['nm_trans', 'nm_rescale', 'nm_fft']
import numpy as np
from ipi.utils.messages import verbosity, info
def mk_nm_matrix(nbeads):
"""Gets the matrix that transforms from the bead representation
to the normal mode representation.
If we return from this function a matrix C, then we transform between the
bead and normal mode representation using q_nm = C . q_b, q_b = C.T . q_nm
Args:
nbeads: The number of beads.
"""
b2nm = np.zeros((nbeads,nbeads))
b2nm[0,:] = np.sqrt(1.0)
for j in range(nbeads):
for i in range(1, nbeads/2+1):
b2nm[i,j] = np.sqrt(2.0)*np.cos(2*np.pi*j*i/float(nbeads))
for i in range(nbeads/2+1, nbeads):
b2nm[i,j] = np.sqrt(2.0)*np.sin(2*np.pi*j*i/float(nbeads))
if (nbeads%2) == 0:
b2nm[nbeads/2,0:nbeads:2] = 1.0
b2nm[nbeads/2,1:nbeads:2] = -1.0
return b2nm/np.sqrt(nbeads)
def mk_rs_matrix(nb1, nb2):
"""Gets the matrix that transforms a path with nb1 beads into one with
nb2 beads.
If we return from this function a matrix T, then we transform between the
system with nb1 bead and the system of nb2 beads using q_2 = T . q_1
Args:
nb1: The initial number of beads.
nb2: The final number of beads.
"""
if (nb1 == nb2):
return np.identity(nb1,float)
elif (nb1 > nb2):
b1_nm = mk_nm_matrix(nb1)
nm_b2 = mk_nm_matrix(nb2).T
#builds the "reduction" matrix that picks the normal modes we want to keep
b1_b2 = np.zeros((nb2, nb1), float)
b1_b2[0,0] = 1.0
for i in range(1, nb2/2+1):
b1_b2[i,i] = 1.0
b1_b2[nb2-i, nb1-i] = 1.0
if (nb2 % 2 == 0):
#if we are contracting down to an even number of beads, then we have to
#pick just one of the last degenerate modes to match onto the single
#stiffest mode in the new path
b1_b2[nb2/2, nb1-nb2/2] = 0.0
rs_b1_b2 = np.dot(nm_b2, np.dot(b1_b2, b1_nm))
return rs_b1_b2*np.sqrt(float(nb2)/float(nb1))
else:
return mk_rs_matrix(nb2, nb1).T*(float(nb2)/float(nb1))
class nm_trans:
"""Helper class to perform beads <--> normal modes transformation.
Attributes:
_b2nm: The matrix to transform between the bead and normal mode
representations.
_nm2b: The matrix to transform between the normal mode and bead
representations.
"""
def __init__(self, nbeads):
"""Initializes nm_trans.
Args:
nbeads: The number of beads.
"""
self._b2nm = mk_nm_matrix(nbeads)
self._nm2b = self._b2nm.T
def b2nm(self, q):
"""Transforms a matrix to the normal mode representation.
Args:
q: A matrix with nbeads rows, in the bead representation.
"""
return np.dot(self._b2nm,q)
def nm2b(self, q):
"""Transforms a matrix to the bead representation.
Args:
q: A matrix with nbeads rows, in the normal mode representation.
"""
return np.dot(self._nm2b,q)
class nm_rescale:
"""Helper class to rescale a ring polymer between different number of beads.
Attributes:
_b1tob2: The matrix to transform between a ring polymer with 'nbeads1'
beads and another with 'nbeads2' beads.
_b2tob1: The matrix to transform between a ring polymer with 'nbeads2'
beads and another with 'nbeads1' beads.
"""
def __init__(self, nbeads1, nbeads2):
"""Initializes nm_rescale.
Args:
nbeads1: The initial number of beads.
nbeads2: The rescaled number of beads.
"""
self._b1tob2 = mk_rs_matrix(nbeads1,nbeads2)
self._b2tob1 = self._b1tob2.T*(float(nbeads1)/float(nbeads2))
def b1tob2(self, q):
"""Transforms a matrix from one value of beads to another.
Args:
q: A matrix with nbeads1 rows, in the bead representation.
"""
return np.dot(self._b1tob2,q)
def b2tob1(self, q):
"""Transforms a matrix from one value of beads to another.
Args:
q: A matrix with nbeads2 rows, in the bead representation.
"""
return np.dot(self._b2tob1,q)
class nm_fft:
"""Helper class to perform beads <--> normal modes transformation
using Fast Fourier transforms.
Attributes:
fft: The fast-Fourier transform function to transform between the
bead and normal mode representations.
ifft: The inverse fast-Fourier transform function to transform
between the normal mode and bead representations.
qdummy: A matrix to hold a copy of the bead positions to transform
them to the normal mode representation.
qnmdummy: A matrix to hold a copy of the normal modes to transform
them to the bead representation.
nbeads: The number of beads.
natoms: The number of atoms.
"""
def __init__(self, nbeads, natoms):
"""Initializes nm_trans.
Args:
nbeads: The number of beads.
natoms: The number of atoms.
"""
self.nbeads = nbeads
self.natoms = natoms
try:
import pyfftw
info("Import of PyFFTW successful", verbosity.medium)
self.qdummy = pyfftw.n_byte_align_empty((nbeads, 3*natoms), 16, 'float32')
self.qnmdummy = pyfftw.n_byte_align_empty((nbeads//2+1, 3*natoms), 16, 'complex64')
self.fft = pyfftw.FFTW(self.qdummy, self.qnmdummy, axes=(0,), direction='FFTW_FORWARD')
self.ifft = pyfftw.FFTW(self.qnmdummy, self.qdummy, axes=(0,), direction='FFTW_BACKWARD')
except ImportError: #Uses standard numpy fft library if nothing better
#is available
info("Import of PyFFTW unsuccessful, using NumPy library instead", verbosity.medium)
self.qdummy = np.zeros((nbeads,3*natoms), dtype='float32')
self.qnmdummy = np.zeros((nbeads//2+1,3*natoms), dtype='complex64')
def dummy_fft(self):
self.qnmdummy = np.fft.rfft(self.qdummy, axis=0)
def dummy_ifft(self):
self.qdummy = np.fft.irfft(self.qnmdummy, n=self.nbeads, axis=0)
self.fft = lambda: dummy_fft(self)
self.ifft = lambda: dummy_ifft(self)
def b2nm(self, q):
"""Transforms a matrix to the normal mode representation.
Args:
q: A matrix with nbeads rows and 3*natoms columns,
in the bead representation.
"""
if self.nbeads == 1:
return q
self.qdummy[:] = q
self.fft()
if self.nbeads == 2:
return self.qnmdummy.real/np.sqrt(self.nbeads)
nmodes = self.nbeads/2
self.qnmdummy /= np.sqrt(self.nbeads)
qnm = np.zeros(q.shape)
qnm[0,:] = self.qnmdummy[0,:].real
if self.nbeads % 2 == 0:
self.qnmdummy[1:-1,:] *= np.sqrt(2)
(qnm[1:nmodes,:], qnm[self.nbeads:nmodes:-1,:]) = (self.qnmdummy[1:-1,:].real, self.qnmdummy[1:-1,:].imag)
qnm[nmodes,:] = self.qnmdummy[nmodes,:].real
else:
self.qnmdummy[1:,:] *= np.sqrt(2)
(qnm[1:nmodes+1,:], qnm[self.nbeads:nmodes:-1,:]) = (self.qnmdummy[1:,:].real, self.qnmdummy[1:,:].imag)
return qnm
def nm2b(self, qnm):
"""Transforms a matrix to the bead representation.
Args:
qnm: A matrix with nbeads rows and 3*natoms columns,
in the normal mode representation.
"""
if self.nbeads == 1:
return qnm
if self.nbeads == 2:
self.qnmdummy[:] = qnm
self.ifft()
return self.qdummy*np.sqrt(self.nbeads)
nmodes = self.nbeads/2
odd = self.nbeads - 2*nmodes # 0 if even, 1 if odd
qnm_complex = np.zeros((nmodes+1, len(qnm[0,:])), complex)
qnm_complex[0,:] = qnm[0,:]
if not odd:
(qnm_complex[1:-1,:].real, qnm_complex[1:-1,:].imag) = (qnm[1:nmodes,:], qnm[self.nbeads:nmodes:-1,:])
qnm_complex[1:-1,:] /= np.sqrt(2)
qnm_complex[nmodes,:] = qnm[nmodes,:]
else:
(qnm_complex[1:,:].real, qnm_complex[1:,:].imag) = (qnm[1:nmodes+1,:], qnm[self.nbeads:nmodes:-1,:])
qnm_complex[1:,:] /= np.sqrt(2)
self.qnmdummy[:] = qnm_complex
self.ifft()
return self.qdummy*np.sqrt(self.nbeads)

Event Timeline