Skip to content
Extraits de code Groupes Projets

Simple Wave simulation

  • Cloner avec SSH
  • Cloner avec HTTPS
  • Intégrer
  • Partager
    L'extrait de code peut être consulté sans aucune authentification.
    Rédigé par Michel Crucifix

    Simple wave simulation for illutrating LPHYS2264 course.

    wave.py 6,75 Kio
    ''' Shallow-water equation in beta plane, with initial conditios 
     Author : M. Crucifix, University catholique de Louvain 2013
     Based on : 
     An Unconditionally Stable Scheme for the Shallow Water Equations 
     M. Israeli, N. Naik and M. Cane  
     Monthly Weather Review,  vol. 128, p. 810,  2000 
     adimensional variables scaling 
     The dimensional equations and variables can be recovered by multiplying 
     h by H,(u,v) by U, t by T,and(x,y) by L,where  
     U = c ; H = c*c/g ; T = (c beta)^(-1/2) ; L = (c / beta )^1/2  
     so that the non dimensional coriolis parameter, around the equation 
     which equals beta * y ....  
     Free to redistribute modify .... but cite author, propose 
     modifications, and without any warranty whatsoever   '''
    
    import numpy as np
    import matplotlib as mpl 
    import matplotlib.pyplot as plt
    import td 
    
    
    def check_positive (i):
      if (i <=0 ):
        raise NamedError('negative space or time step supplied')
      else:
        pass
    
    class Field:
      ''' horizontal field' organised as a np.matrix    
          firt index corresponds to position on 'x' axis and 
          second index to position on 'y' axis ''' 
      def __init__ (self, val, m, n, dx, dy, dt):
        self.m = np.int(m)
        self.n = np.int(n)
        for i in (dx,dy,dt): check_positive(i)
        self.dx = dx 
        self.dy = dy 
        self.dt = dt
        self.dtdx = dt/dx
    
        self.d1 = int(np.floor(self.dtdx))
        self.d2 = int(self.d1 + 1)
        self.r  = self.dtdx - self.d1
    
        if (val.shape == (m+1,n+1)):
          self.field = val
        else:
          raise NameError('Wrong field shape')
        return (None)
    
     
      def R(self,i):
        ''' returns latitudinal transect of field at position i*dx + dt  (_R operator)'''
        if (np.int( i + self.d2 ) < self.m):
          return(self.field[i+self.d1,:] * (1-self.r) + self.field[i+self.d2,:] * self.r)
        else:
          return(self.field[self.m,:] + (self.field[self.m,:]-
                 self.field[self.m-1,:])*(self.dtdx - (self.m-i) ))
    
      def L(self,i):
        ''' returns latitudinal transect of field at position i*dx - dt  (_L operator)'''
        if (np.int( i - self.d2 ) > 0):
          return(self.field[i-self.d1,:] * (1-self.r) + self.field[i-self.d2,:] * self.r)
        else:
          return(self.field[0,:] + (self.field[0,:]-self.field[1,:])*( self.dtdx  - i ))
    
      def C(self,i):
        return (self.field[i,:])
    
    
    def fdy(vector,n, dy):
      tdy = np.zeros(n+1)
    #  tdy[0] =  (-vector[2] + 4*vector[1] - 3*vector[0])
    #  tdy[n] =  -(-vector[n-2] + 4*vector[n-1] - 3*vector[n]) 
      tdy[0] = 0.
      tdy[n] = 0.
      tdy[1:n] = (vector[range(2,n+1)] - vector[range(0,n-1)])/2.
      tdy /= dy
      return(tdy)
    
    class swfield:
    
      def field ( self, val ):
          return(Field(val, self.m, self.n, self.dx, self.dy, self.dt))
    
      def __init__ (self,m, n, dx, dy, dt, h, u, v, f, F, G, Q):
     
        self.dx = dx
        self.dy = dy
        self.ddy = dy*dy
        self.dt = dt
        self.tau = dt/2.
        self.ttau = self.tau*self.tau
        self.dyv = np.zeros((m+1,n+1))
        self.m = int(m)
        self.n = int(n)
        if (f.shape == (n+1,)):
          self.f = f
        else:
          raise NameError ('Coriolis factor ''f'' must be supplied as a n-vector')
    
        self.F = F
        self.G = G
        self.Q = Q
    
        self.h = self.field(h)
        self.u = self.field(u)
        self.v = self.field(v)
        self.II1 = self.field(np.zeros((m+1,n+1)))
        self.rhs = np.zeros((m+1,n+1))
    
        return(None)
    
      def update_dyv(self):
        for i in range(0,m+1): 
          self.dyv[i,:] = fdy(self.v.C(i), self.n, self.dy) 
        return(self)
    
      def update_II(self):
        self.II1 = self.field(self.u.field+self.h.field+self.tau*
              (self.f*self.v.field - self.dyv + self.F + self.Q))
        self.II2 = self.field(self.u.field-self.h.field+self.tau*
              (self.f*self.v.field + self.dyv + self.F - self.Q))
        return(self)
    
      def iterate_interior(self,i):
        I1 = self.II1.L(i)
        I2 = self.II2.R(i)
        cn = I1 + 0.5*(I2-I1) + self.tau*self.F[i,]
        dn = I1 - 0.5*(I2+I1) + self.tau*self.Q[i,]
    
        aa = -2./(self.ddy) - (1/(self.ttau) + self.f*self.f)
        bb = np.ones(self.n)/(self.ddy) 
        cc = np.ones(self.n)/(self.ddy) 
        dd = (self.f*self.u.C(i) + self.f*cn + fdy(self.h.C(i)+dn, self.n, self.dy) - 
              2*self.G[i,]) / self.tau - self.v.C(i) / self.ttau
        v_ = td.solve_s(aa,bb,cc,dd)
        u_ = self.tau * self.f * v_ + cn
        h_ = - self.tau * fdy(v_, self.n, self.dy) + dn
    
        return u_, v_, h_
    
      def iterate_west(self):
        i = 0
        I2 = self.II2.R(i)
        dn = - I2 
    
        # to do : the +self.f * self.f is incorrect, but
        # is necessary to ensure stability. Need to find
        # the way to keep the system stable without it
        aa = -2./(self.ddy) - (1/(self.ttau) * np.ones(self.n+1) + self.f * self.f)
        cc = np.ones(n)/(self.ddy)  + self.f[1:self.n+1]/(2.*self.dy)
        bb = np.ones(n)/(self.ddy)  - self.f[0:self.n]/(2.*self.dy)
    #
        dd = (fdy(self.h.C(i)+dn, self.n, self.dy) - 
              2*self.G[i,]) / self.tau - self.v.C(i) / self.ttau
        v_ = td.solve_s(aa,bb,cc,dd)
        u_ = np.zeros(self.n+1)
        h_ = - self.tau * fdy(v_, self.n, self.dy) + dn
    
        return u_, v_, h_
    
      def iterate_east(self):
        i = self.m
        dn = self.II1.L(self.m)
    
        aa = -2./(self.ddy) - (1/(self.ttau) * np.ones(self.n+1) + self.f * self.f )
        cc = np.ones(n)/(self.ddy)  - self.f[1:self.n+1]/(2.*self.dy)
        bb = np.ones(n)/(self.ddy)  + self.f[0:self.n]/(2.*self.dy)
    #
        dd = (fdy(self.h.C(i)+dn, self.n, self.dy) - 
              2*self.G[i,]) / self.tau - self.v.C(i) / self.ttau
        v_ = td.solve_s(aa,bb,cc,dd)
        u_ = np.zeros(self.n+1)
        h_ =  self.tau * fdy(v_, self.n, self.dy) + dn
    
        return u_, v_, h_
     
    
      def iterate (self):
        u_ = np.zeros((self.m+1,self.n+1))
        v_ = np.zeros((self.m+1,self.n+1))
        h_ = np.zeros((self.m+1,self.n+1))
    
        self.update_dyv()
        self.update_II()
    
        u_[0,:],v_[0,:],h_[0,:] = self.iterate_west()
        u_[self.m,:],v_[self.m,:],h_[self.m,:] = self.iterate_east()
    
        for i in range(1,self.m):
          u_[i,:],v_[i,:],h_[i,:] = self.iterate_interior(i)
    
        self.u = self.field(u_)
        self.v = self.field(v_)
        self.h = self.field(h_)
        return(self)
    
      def plot (self):
       plt.imshow(self.h.field.T,vmin=-0.4, vmax=0.4)
    
    import matplotlib.animation as animation
    
    if __name__ == "__main__": 
    
      m = 60
      n = 60
      dx = .6
      dy = .6
      dt = 0.3
      u = np.zeros((m+1,n+1))
      v = np.zeros((m+1,n+1))
      h = np.zeros((m+1,n+1))
      h[25:35,25:35] = 0.5
    
      beta = 1 # set to zero to simulate gravity waves
      f = ( -n/2 + np.arange(0,n+1) )  * dy * beta
      F = np.zeros((m+1, n+1))
      G = np.zeros((m+1, n+1))
      Q = np.zeros((m+1, n+1))
    
      H = swfield (m, n, dx, dy, dt, h, u, v, f, F, G, Q)
    
      def init():
        H.plot()
    
      def animate(i):
        H.iterate()
        print(i)
        # H.plot()
    
      fig = plt.figure()
    
      init()
      for i in range(1,350):
        fig.clear()
        animate(i)
        H.plot()
        plt.savefig('iter_%4.4i.png' % i)
     
      # anim.save('basic_animation', writer='ffmpeg')
      # above
      # does not work on the mac !!! ('rgba' not supported)
    0% Chargement en cours ou .
    You are about to add 0 people to the discussion. Proceed with caution.
    Terminez d'abord l'édition de ce message.
    Veuillez vous inscrire ou vous pour commenter