Source code for DABISpectralToolbox.DABISpectralND

# -*- coding: utf-8 -*-

"""
=================================
DABI Spectral ND
=================================

Created on Mon Jul  9 11:35:12 2012

@author: Daniele Bigoni (dabi@imm.dtu.dk)

Implementation of Spectral Methods in n dimension.

It uses the package ``DABISpectral1D`` as basic polynomials in order to construct higher dimensional rules by tensor product.

"""

__revision__ = filter(str.isdigit, "$Revision: 97 $")

__author__ = "Daniele Bigoni"
__copyright__ = """Copyright 2012, Daniele Bigoni"""
__credits__ = ["Daniele Bigoni"]
__maintainer__ = "Daniele Bigoni"
__email__ = "dabi@imm.dtu.dk"
__status__ = "Production"

import numpy as np

from scipy.misc import comb

import sys

import itertools

def MultiIndex(d,N):
    """
    MultiIndex(): generates the multi index ordering for the construction of multidimensional Generalized Vandermonde matrices
    
    Syntax:
        ``IDX = MultiIndex(d,N)``
    
    Input:
        * ``d`` = (int) dimension of the simplex
        * ``N`` = (int) the maximum value of the sum of the indeces
    
    OUTPUT:
        * ``IDX`` = (2d-array,int) array containing the ordered multi indeces        
    """
    
    # Compute the size of the number of multi-index elements (Pascal's simplex)
    NIDX = 0
    for i in range(0,N+1):
        NIDX = NIDX + comb( i+(d-1),d-1,True)
    
    # Allocate memory
    IDX = np.zeros((NIDX,d),dtype=int)
    
    iIDX = 1 # index in the multi-index table on which the first n-th order is
    for n in range(1,N+1):
        IDX[iIDX,0] = n
        # Start recursion
        iIDX = __MultiIndexRec(IDX,d,iIDX+1,0)
    
    return IDX
    
def __MultiIndexRec(IDX,d,m,i):
    # Start splitting
    mFirstSplit = m-1
    mLastSplit = m-1
    mNew = m
    if (i+1 < d):
        while (IDX[mLastSplit,i] > 1):
            IDX[mNew,:i] = IDX[mLastSplit,:i]
            IDX[mNew,i] = IDX[mLastSplit,i]-1
            IDX[mNew,i+1] = IDX[mLastSplit,i+1]+1
            mLastSplit = mNew
            mNew = mNew + 1
            # Call recursion on sub set
            mNew = __MultiIndexRec(IDX,d,mNew,i+1)
        # Move
        IDX[mNew,:i] = IDX[mFirstSplit,:i]
        IDX[mNew,i+1] = IDX[mFirstSplit,i]
        mNew = mNew + 1
        # Call recursion on sub set
        mNew = __MultiIndexRec(IDX,d,mNew,i+1)
    return mNew

[docs]class PolyND: polys = [] DIM = 0 def __init__(self, polys): """ Initialization of the N-dimensional Polynomial instance Syntax: ``p = PolyND(polys)`` Input: * ``polys`` = (list,DABISpectral1D.Poly1D) list of polynomial instances of the class ``DABISpectral1D.Poly1D`` .. seealso:: DABISpectral1D.Poly1D """ self.polys = polys self.DIM = len(self.polys)
[docs] def GaussQuadrature(self, Ns, normed=False, warnings=True): """ GaussQuadrature(): computes the tensor product of the Guass Points and weights Syntax: ``(x,w) = GaussQuadrature(Ns,[normed=False],[warnings=True])`` Input: * ``Ns`` = (list,int) n-dimensional list with the order of approximation of each polynomial * ``normed`` = (optional,boolean) whether the weights will be normalized or not * ``warnings`` = (optional,boolean) set whether to ask for confirmation when it is required to allocate more then 100Mb of memory Output: * ``x`` = tensor product of the collocation points * ``w`` = tensor product of the weights .. warning:: The lengths of ``Ns`` has to be conform to the number of polynomials with which you have instantiated ``PolyND`` """ # Memory allocation for which the user will get a warning message (Mb) warningMem = 100.0 if self.DIM != len(Ns) : print "The number of elements in Ns is not consistent" return ####################### # Estimate memory usage Ncoll = np.prod(np.asarray(Ns) + 1) SDOUBLE = sys.getsizeof(0.0) SARRAY = sys.getsizeof(np.asarray([])) xMem = self.DIM * Ncoll * SDOUBLE + SARRAY wMem = Ncoll * SDOUBLE + SARRAY totMem = xMem + wMem # Print out information print( "----------------------------------------------------------------------" ) print( "DABISpectralND.GaussQuadrature: Memory usage information:" ) print( "\t X Points: %10.2f Mb " % (xMem * 1e-6) ) print( "\t Weights: %10.2f Mb " % (wMem * 1e-6) ) print( "Total Memory: %10.2f Mb " % (totMem * 1e-6) ) print( "N of collocation points: %d " % (Ncoll) ) if warnings and totMem * 1e-6 > warningMem: opt = 'a' while (opt != 'c' and opt != 'b' and opt != 'q'): print("The total memory that will be allocated exceed %10.2fMb. Chose one , of the following options:" % (warningMem)) print("\t [c]: continue") print("\t [q]: exit") opt = sys.stdin.read(1) if (opt == 'q'): return print( "----------------------------------------------------------------------" ) x,w = self.polys[0].GaussQuadrature(Ns[0],normed) wKron = w xs = [x] for i in range(1,self.DIM): x,w = self.polys[i].GaussQuadrature(Ns[i],normed) wKron = np.kron(wKron, w) xs.append(x) xKron = np.asarray(list(itertools.product(*xs))) return (xKron, wKron)
[docs] def GaussLobattoQuadrature(self, Ns, normed=False, warnings=True): """ GaussLobattoQuadrature(): computes the tensor product of the Guass Lobatto Points and weights Syntax: ``(x,w) = GaussLobattoQuadrature(Ns,[normed=False],[warnings=True])`` Input: * ``Ns`` = (list,int) n-dimensional list with the order of approximation of each polynomial * ``normed`` = (optional,boolean) whether the weights will be normalized or not * ``warnings`` = (optional,boolean) set whether to ask for confirmation when it is required to allocate more then 100Mb of memory Output: * ``x`` = tensor product of the collocation points * ``w`` = tensor product of the weights .. warning:: The lengths of ``Ns`` has to be conform to the number of polynomials with which you have instantiated ``PolyND`` """ # Memory allocation for which the user will get a warning message (Mb) warningMem = 100.0 if self.DIM != len(Ns) : print "The number of elements in Ns is not consistent" return ####################### # Estimate memory usage Ncoll = np.prod(np.asarray(Ns) + 1) SDOUBLE = sys.getsizeof(0.0) SARRAY = sys.getsizeof(np.asarray([])) xMem = self.DIM * Ncoll * SDOUBLE + SARRAY wMem = Ncoll * SDOUBLE + SARRAY totMem = xMem + wMem # Print out information print( "----------------------------------------------------------------------" ) print( "DABISpectralND.GaussLobattoQuadrature: Memory usage information:" ) print( "\t X Points: %10.2f Mb " % (xMem * 1e-6) ) print( "\t Weights: %10.2f Mb " % (wMem * 1e-6) ) print( "Total Memory: %10.2f Mb " % (totMem * 1e-6) ) print( "N of collocation points: %d " % (Ncoll) ) if warnings and totMem * 1e-6 > warningMem: opt = 'a' while (opt != 'c' and opt != 'b' and opt != 'q'): print("The total memory that will be allocated exceed %10.2fMb. Chose one , of the following options:" % (warningMem)) print("\t [c]: continue") print("\t [q]: exit") opt = sys.stdin.read(1) if (opt == 'q'): return print( "----------------------------------------------------------------------" ) x,w = self.polys[0].GaussLobattoQuadrature(Ns[0]) wKron = w xs = [x] for i in range(1,self.DIM): x,w = self.polys[i].GaussLobattoQuadrature(Ns[i]) wKron = np.kron(wKron, w) xs.append(x) xKron = np.asarray(list(itertools.product(*xs))) return (xKron, wKron)
[docs] def GradVandermonde(self,rs,Ns,ks,norms=None,usekron=True,output=True,warnings=True): """ GradVandermonde(): initialize the tensor product of the k-th gradient of the modal basis. Syntax: ``V = GradVandermonde(r,N,k,[norms=None],[usekron=True],[output=True],[warnings=True])`` Input: * ``rs`` = (list of 1d-array,float) ``n``-dimensional list of set of points on which to evaluate the polynomials (by default they are not the kron product of the points. See ``usekron`` option) * ``Ns`` = (list,int) n-dimensional list with the maximum orders of approximation of each polynomial * ``ks`` = (list,int) n-dimensional list with derivative orders * ``norms`` = (default=None,list,boolean) n-dimensional list of boolean, True -> orthonormal, False -> orthogonal, None -> all orthonormal * ``usekron`` = (optional,boolean) set whether to apply the kron product of the single dimensional Vandermonde matrices or to multiply column-wise. kron(rs) and usekron==False is equal to rs and usekron==True * ``output`` = (optional,boolean) set whether to print out information about memory allocation * ``warnings`` = (optional,boolean) set whether to ask for confirmation when it is required to allocate more then 100Mb of memory OUTPUT: * ``V`` = Tensor product of the Generalized Vandermonde matrices .. warning:: The lengths of ``Ns`` , ``rs`` , ``ks`` , ``norms`` has to be conform to the number of polynomials with which you have instantiated ``PolyND`` """ # Memory allocation for which the user will get a warning message (Mb) warningMem = 100.0 if usekron: if (self.DIM != len(rs) or self.DIM != len(Ns) or self.DIM != len(ks) or ( norms != None and self.DIM != len(norms))) : print "The number of elements in rs, Ns, ks and norms is not consistent" return else: if ( not (type(rs) == np.ndarray and rs.shape[1] == self.DIM) or self.DIM != len(Ns) or self.DIM != len(ks) or ( norms != None and self.DIM != len(norms))) : print "The number of elements in rs, Ns, ks and norms is not consistent" return if norms == None: norms = np.ones(self.DIM) ####################### # Estimate memory usage if usekron: Ncolls = np.zeros(self.DIM) for i in range(0,self.DIM): Ncolls[i] = len(rs[i]) SDOUBLE = sys.getsizeof(0.0) SARRAY = sys.getsizeof(np.asarray([])) VMem = np.prod((np.asarray(Ns)+1) * Ncolls) * SDOUBLE + SARRAY totMem = VMem else: Ncolls = rs.shape[0] SDOUBLE = sys.getsizeof(0.0) SARRAY = sys.getsizeof(np.asarray([])) VMem = np.prod(np.asarray(Ns)+1) * Ncolls * SDOUBLE + SARRAY totMem = VMem # Print out information if output: print( "----------------------------------------------------------------------" ) print( "DABISpectralND.GradVandermonde: Memory usage information:" ) print( "\t Tensor Basis: %10.2f Mb " % (VMem * 1e-6) ) print( "Total Memory: %10.2f Mb " % (totMem * 1e-6) ) if warnings and totMem * 1e-6 > warningMem: opt = 'a' while (opt != 'c' and opt != 'b' and opt != 'q'): print("The total memory that will be allocated exceed %10.2fMb. Chose one , of the following options:" % (warningMem)) print("\t [c]: continue") print("\t [q]: exit") opt = sys.stdin.read(1) if (opt == 'q'): return print( "----------------------------------------------------------------------" ) if usekron: VKron = self.polys[0].GradVandermonde1D(rs[0],Ns[0],ks[0],norms[0]) for i in range(1,self.DIM): VKron = np.kron(VKron, self.polys[i].GradVandermonde1D(rs[i],Ns[i],ks[i],norms[i])) else: VKron = np.ones((rs.shape[0],1)) for i in range(0,self.DIM): VKronNew = np.zeros((VKron.shape[0],VKron.shape[1] * (Ns[i]+1))) V = self.polys[i].GradVandermonde1D(rs[:,i],Ns[i],ks[i],norms[i]) for col in range(0,VKron.shape[1]): VKronNew[:,col*(Ns[i]+1):(col+1)*(Ns[i]+1)] = np.tile(VKron[:,col],(Ns[i]+1,1)).T * V VKron = VKronNew return VKron
[docs] def GradVandermondePascalSimplex(self,rs,N,ks,norms=None,output=True,warnings=True): """ GradVandermondePascalSimplex(): initialize k-th gradient of the modal basis up to the total order N Syntax: ``V = GradVandermonde(r,N,k,[norms=None],[output=True],[warnings=True])`` Input: * ``rs`` = (list of 1d-array,float) ``n``-dimensional list of set of points on which to evaluate the polynomials * ``N`` = (int) the maximum orders of the polynomial basis * ``ks`` = (list,int) n-dimensional list with derivative orders * ``norms`` = (default=None,list,boolean) n-dimensional list of boolean, True -> orthonormal, False -> orthogonal, None -> all orthonormal * ``output`` = (optional,boolean) set whether to print out information about memory allocation * ``warnings`` = (optional,boolean) set whether to ask for confirmation when it is required to allocate more then 100Mb of memory OUTPUT: * ``V`` = Generalized Vandermonde matrix up to the N-th order .. warning:: The lengths of ``rs`` , ``ks`` , ``norms`` has to be conform to the number of polynomials with which you have instantiated ``PolyND`` """ # Memory allocation for which the user will get a warning message (Mb) warningMem = 100.0 if (self.DIM != len(rs) or self.DIM != len(ks) or ( norms != None and self.DIM != len(norms))) : print "The number of elements in rs, Ns, ks and norms is not consistent" return if norms == None: norms = np.ones(self.DIM) ####################### # Estimate memory usage Ncolls = 1 for i in range(0,self.DIM): Ncolls = Ncolls * len(rs[i]) # Number of basis computed using the pascal simplex formula Nbasis = 0 for i in range(0,N+1): Nbasis = Nbasis + comb( i+(self.DIM-1),self.DIM-1,True) SDOUBLE = sys.getsizeof(0.0) SARRAY = sys.getsizeof(np.asarray([])) VMem = Nbasis * Ncolls * SDOUBLE + SARRAY totMem = VMem # Print out information if output: print( "----------------------------------------------------------------------" ) print( "DABISpectralND.GradVandermondePascalSimplex Memory usage information:" ) print( "\t Tensor Basis: %10.2f Mb " % (VMem * 1e-6) ) print( "Total Memory: %10.2f Mb " % (totMem * 1e-6) ) if warnings and totMem * 1e-6 > warningMem: opt = 'a' while (opt != 'c' and opt != 'b' and opt != 'q'): print("The total memory that will be allocated exceed %10.2fMb. Chose one , of the following options:" % (warningMem)) print("\t [c]: continue") print("\t [q]: exit") opt = sys.stdin.read(1) if (opt == 'q'): return print( "----------------------------------------------------------------------" ) # Compute combinations of collocation points xKron = np.asarray(list(itertools.product(*rs))) # Compute single Generalized Vandermonde Matrices Vs = [] for i in range(0,self.DIM): Vs.append(self.polys[i].GradVandermonde1D(xKron[:,i],N,ks[i],norms[i])) # Make space for the Vandermonde matrix V = np.ones((Ncolls,Nbasis)) # Compute the Pascal's Simplex of the basis functions IDX = MultiIndex(self.DIM,N) for i in range(0,np.size(IDX,0)): for j in range(0,np.size(IDX,1)): V[:,i] = V[:,i] * Vs[j][:,IDX[i,j]] return V