def solve(self,T=10.4,version="scalar",quiet_mode=True,display=False,\ save_figs=False,filename="figure",destination=None): """ The solver solves the 2D wave equation with varying coefficients inside the spatial derivatives. By default the solver uses the scalar version of the implementation. Other implementations includes a vectorized version, and soon to be implemented Cython version. Variables : T : End time for simulation version : version of implementation Choices include : - scalar (default) - vectorized - cython quiet_mode : If True (default), solver does not display any progress message. And if False, solver gives detailed information what it is doing. save_figs : If True, saves the plots at certain time steps. These figures can later be preprocessed to a single gif file. If False (defualt), no figures are saved. """ if save_figs or display: viz = self.Visualize(destination=destination,filename=filename) destination = viz.destination # in case None was given as destination if version=="scalar" or version=="vectorized": self.version = version else: import sys print "Only following versions have been implemented for the solver : " print "scalar and vectorized" print "versions must be case-sensitive" sys.exit(1) import numpy as np self.T = T dt = self.physprm.gridprm.dt Lx = self.physprm.gridprm.Lx Ly = self.physprm.gridprm.Ly dt = self.physprm.gridprm.dt dx = self.physprm.gridprm.dx dy = self.physprm.gridprm.dy Nx = self.physprm.gridprm.Nx Ny = self.physprm.gridprm.Ny Nt = int(round(T/float(dt))) t = np.linspace(0,T,Nt+1) #t = np.arange(0,T+dt,dt) x = np.linspace(0,Lx,Nx+3) y = np.linspace(0,Ly,Ny+3) self.x = x self.y = y if self.physprm.gridprm.problem3D : if version=="scalar": print "Only vectorized version available, setting version to vectorized." version="vectorized" Lz = self.physprm.gridprm.Lz dz = self.physprm.gridprm.dz Nz = self.physprm.gridprm.Nz z = np.linspace(0,Lz,Nz+3) self.z = z # Set bcs (either Dirichlet BCs or Neumann BCs) : BC = self.BC # Define the arrays to store the three time steps : # time step u^{n+1} : if self.physprm.gridprm.problem3D : u = np.zeros([Nx+3,Ny+3,Nz+3]) self.u = u # time step u^{n} : u_1 = np.zeros([Nx+3,Ny+3,Nz+3]) self.u_1 = u_1 # time step u^{n-1} : u_2 = np.zeros([Nx+3,Ny+3,Nz+3]) self.u_2 = u_2 else : u = np.zeros([Nx+3,Ny+3]) self.u = u # time step u^{n} : u_1 = np.zeros([Nx+3,Ny+3]) self.u_1 = u_1 # time step u^{n-1} : u_2 = np.zeros([Nx+3,Ny+3]) self.u_2 = u_2 if version=="vectorized": xv = x[:,np.newaxis] # for vectorized function evaluations yv = y[np.newaxis,:] if self.physprm.gridprm.problem3D : import scitools.easyviz as plt # what do we do here ? xx,yy,zz = plt.ndgrid(x,y,z) # These are the real physical points on our grid : Ix = range(1,u.shape[0]-1) self.Ix = Ix Iy = range(1,u.shape[1]-1) self.Iy = Iy if self.physprm.gridprm.problem3D : Iz = range(1,u.shape[2]-1) self.Iz = Iz b = self.physprm.b # Assuming the following are functions f = self.physprm.f I = self.physprm.I V = self.physprm.V q = self.physprm.q if self.physprm.gridprm.problem3D : def q_i_half(x,y,z,x_dx): return 0.5*(q(x,y,z)+q(x_dx,y,z)) def q_j_half(x,y,z,y_dy): return 0.5*(q(x,y,z)+q(x,y_dy,z)) def q_k_half(x,y,z,z_dz): return 0.5*(q(x,y,z)+q(x,y,z_dz)) q_i_phalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_i_mhalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_j_phalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_j_mhalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_k_phalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_k_mhalf_array = np.zeros([Nx+3,Ny+3,Nz+3]) q_i_phalf_array[1:-1,1:-1,1:-1] = q_i_half(x[1:-1],y[1:-1],z[1:-1],x[2:]) q_i_mhalf_array[1:-1,1:-1,1:-1] = q_i_half(x[1:-1],y[1:-1],z[1:-1],x[:-2]) q_j_phalf_array[1:-1,1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],z[1:-1],y[2:]) q_j_mhalf_array[1:-1,1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],z[1:-1],y[:-2]) q_k_phalf_array[1:-1,1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],z[1:-1],z[2:]) q_k_mhalf_array[1:-1,1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],z[1:-1],z[:-2]) else : # The following functions will be used as well later through-out def q_i_half(x,y,x_dx): # used arithmetic mean for q(i+1/2,j) and the other corresponding terms return 0.5*(q(x,y)+q(x_dx,y)) # x_dx should either be x + dx or x - dx def q_j_half(x,y,y_dy): return 0.5*(q(x,y)+q(x,y_dy)) # y_dy should either be y + dy or y - dy if self.outlet: q_a = np.zeros([Nx+3,Ny+3]) C5 = np.zeros([Nx+3,Ny+3]) q_i_phalf_array = np.zeros([Nx+3,Ny+3]) q_i_mhalf_array = np.zeros([Nx+3,Ny+3]) q_j_phalf_array = np.zeros([Nx+3,Ny+3]) q_j_mhalf_array = np.zeros([Nx+3,Ny+3]) # Pre-compute q, f and V. if version=="scalar": # Fill all q_***_arrays with their respective values. if self.outlet: for i in Ix: for j in Iy: q_a[i,j] = q(x[i],y[j]) # used for outlet bcs C5[i,j] = (dx/dt)/np.sqrt(q_a[i,j]) for i in Ix: for j in Iy: q_i_phalf_array[i,j] = q_i_half(x[i],y[j],x[i+1]) q_i_mhalf_array[i,j] = q_i_half(x[i],y[j],x[i-1]) q_j_phalf_array[i,j] = q_j_half(x[i],y[j],y[j+1]) q_j_mhalf_array[i,j] = q_j_half(x[i],y[j],y[j-1]) elif version=="vectorized": q_i_phalf_array[1:-1,1:-1] = q_i_half(x[1:-1],y[1:-1],x[2:]) q_i_mhalf_array[1:-1,1:-1] = q_i_half(x[1:-1],y[1:-1],x[:-2]) q_j_phalf_array[1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],y[2:]) q_j_mhalf_array[1:-1,1:-1] = q_j_half(x[1:-1],y[1:-1],y[:-2]) if self.outlet: q_a[1:-1,1:-1] = q(x[1:-1],y[1:-1]) # used for outlet bcs C5[1:-1,1:-1] = (dx/dt)/np.sqrt(q_a[1:-1,1:-1]) if not self.bypass_stability: if self.physprm.gridprm.problem3D : q_val = (np.fabs(q(x[:],y[:],z[:]))).max() stab_coeff = (1/float(np.sqrt(q_val)))*(1/np.sqrt(1/dx**2 + 1/dy**2 + 1/dz**2)) else: q_val = (np.fabs(q(x[:],y[:]))).max() stab_coeff = (1/float(np.sqrt(q_val)))*(1/np.sqrt(1/dx**2 + 1/dy**2)) if stab_coeff < t[1] : import sys error_msg = "Time step dt=%f exceeds the stability limit %f." sys.exit(error_msg%(t[1],stab_coeff)) # Following constants will be used throughout the solver C1 = 2.0/(2.0 + b*dt) Cb = b*dt*0.5 - 1.0 C11 = dt*(1 - 0.5*b*dt) dtdt = dt*dt C2 = dtdt/(dx*dx) C3 = dtdt/(dy*dy) if self.physprm.gridprm.problem3D : C4 = dtdt/(dz*dz) ########## Step 1 : Initialize for the zeroth time step ########## if not quiet_mode: print "Initializing zeroth time step" if version=="scalar": for i in Ix: for j in Iy: u_2[i,j] = I(x[i-Ix[0]],y[j-Iy[0]]) elif version=="vectorized": if self.physprm.gridprm.problem3D : u_2[:,:] = I(xv,yv,z[:]) else : u_2[:,:] = I(xv,yv) if save_figs : # store graphs -> create animation at end import time if self.physprm.gridprm.problem3D : plt.setp(interactive=False) h = plt.isosurface(xx,yy,zz,u_2,-3) h.setp(opacity=0.5) plt.shading("interp") plt.daspect([1,1,1]) plt.view(3) plt.axis("tight") plt.show() raw_input("enter") else : viz.store_fig(t[0],Ix,Iy,u_2,u_1,u) time.sleep(1.0) elif display: import time viz.display(t[0],Ix,Iy,u_2,u_1,u) time.sleep(1.0) ############ Update ghost points for u_2 ############ if not quiet_mode: print "Updating ghost points" if self.physprm.gridprm.problem3D : BC(self.u_2,Ix,Iy,Iz,version) else : BC(self.u_2,Ix,Iy,version) ########## Step 2 : Implement special case for t[n]=0 ########## """ Here we use a leap frog scheme for u_t (short notation for du/dt) and insert the unknown u(x,y,-dt) into the main scheme to find an expression for u_1, i.e u^{n}_{i,j}. """ if not quiet_mode: print "Initializing time step dt" # Inserting u^{n=-1} = u_1 - 2*dt*V(x,y) into the main scheme and solving for u_1 : # Insert n = 0 in main scheme (see while loop) and insert expression for u^-1 if version=="scalar": for i in Ix: for j in Iy: q_i_phalf = q_i_phalf_array[i,j] q_i_mhalf = q_i_mhalf_array[i,j] q_j_phalf = q_j_phalf_array[i,j] q_j_mhalf = q_j_mhalf_array[i,j] u_1[i,j] = u_2[i,j] + C11*V(x[i],y[j])\ + 0.5*C2*(q_i_phalf*(u_2[i+1,j]-u_2[i,j])-\ q_i_mhalf*(u_2[i,j]-u_2[i-1,j]))\ + 0.5*C3*(q_j_phalf*(u_2[i,j+1]-u_2[i,j])-\ q_j_mhalf*(u_2[i,j]-u_2[i,j-1]))\ + 0.5*dtdt*f(x[i],y[j],t[0]) elif version=="vectorized": if self.physprm.gridprm.problem3D : u_1[1:-1,1:-1,1:-1] = u_2[1:-1,1:-1,1:-1] + C11*V(x[1:-1],y[1:-1],z[1:-1])\ + 0.5*C2*(q_i_phalf_array[1:-1,1:-1,1:-1]*\ (u_2[2:,1:-1,1:-1]-u_2[1:-1,1:-1,1:-1])-\ q_i_mhalf_array[1:-1,1:-1,1:-1]*\ (u_2[1:-1,1:-1,1:-1]-u_2[:-2,1:-1,1:-1]))\ + 0.5*C3*(q_j_phalf_array[1:-1,1:-1,1:-1]*\ (u_2[1:-1,2:,1:-1]-u_2[1:-1,1:-1,1:-1])-\ q_j_mhalf_array[1:-1,1:-1,1:-1]*\ (u_2[1:-1,1:-1,1:-1]-u_2[1:-1,:-2,1:-1]))\ + 0.5*C4*(q_k_phalf_array[1:-1,1:-1,1:-1]*\ (u_2[1:-1,1:-1,2:]-u_2[1:-1,1:-1,1:-1])-\ q_k_mhalf_array[1:-1,1:-1,1:-1]*\ (u_2[1:-1,1:-1,1:-1]-u_2[1:-1,1:-1,:-2]))\ + 0.5*dtdt*f(x[1:-1],y[1:-1],z[1:-1],t[0]) else : u_1[1:-1,1:-1] = u_2[1:-1,1:-1] + C11*V(x[1:-1],y[1:-1])\ + 0.5*C2*(q_i_phalf_array[1:-1,1:-1]*\ (u_2[2:,1:-1]-u_2[1:-1,1:-1])-\ q_i_mhalf_array[1:-1,1:-1]*\ (u_2[1:-1,1:-1]-u_2[:-2,1:-1]))\ + 0.5*C3*(q_j_phalf_array[1:-1,1:-1]*\ (u_2[1:-1,2:]-u_2[1:-1,1:-1])-\ q_j_mhalf_array[1:-1,1:-1]*\ (u_2[1:-1,1:-1]-u_2[1:-1,:-2]))\ + 0.5*dtdt*f(x[1:-1],y[1:-1],t[0]) if save_figs : if self.physprm.gridprm.problem3D : plt.setp(interactive=False) h = plt.isosurface(xx,yy,zz,u_2,-3) h.setp(opacity=0.5) plt.shading("interp") plt.daspect([1,1,1]) plt.view(3) plt.axis("tight") plt.show() else : viz.store_fig(t[1],Ix,Iy,u_2,u_1,u) time.sleep(0.5) elif display: viz.display(t[1],Ix,Iy,u_2,u_1,u) time.sleep(0.5) ############ Update ghost points for u_1 ############ if not quiet_mode: print "Updating ghost points" if self.physprm.gridprm.problem3D : BC(self.u_1,Ix,Iy,Iz,version) else : BC(self.u_1,Ix,Iy,version) if self.outlet: term = np.zeros([Nx+3,Ny+3]) ############ Run scheme for all time steps ############ n = 1 if not quiet_mode: print "Starting scheme" while t[n]<self.T-1e-9: if not quiet_mode: print "Starting time iteration for t=%.3f, n=%g"%(t[n],n) if version=="scalar": for i in Ix: for j in Iy: # use arithmetic mean for q(i+1/2,j) etc. q_i_phalf = q_i_phalf_array[i,j] q_i_mhalf = q_i_mhalf_array[i,j] q_j_phalf = q_j_phalf_array[i,j] q_j_mhalf = q_j_mhalf_array[i,j] u[i,j] = C1*(2*u_1[i,j] + Cb*u_2[i,j]\ + C2*(q_i_phalf*(u_1[i+1,j]-u_1[i,j])-\ q_i_mhalf*(u_1[i,j]-u_1[i-1,j]))\ + C3*(q_j_phalf*(u_1[i,j+1]-u_1[i,j])-\ q_j_mhalf*(u_1[i,j]-u_1[i,j-1]))\ + dtdt*f(x[i],y[j],t[n])) elif version=="vectorized": if self.physprm.gridprm.problem3D : u[1:-1,1:-1,1:-1] = C1*(2*u_1[1:-1,1:-1,1:-1] \ + Cb*u_2[1:-1,1:-1,1:-1]\ + C2*(q_i_phalf_array[1:-1,1:-1,1:-1]*\ (u_1[2:,1:-1,1:-1]-u_1[1:-1,1:-1,1:-1])-\ q_i_mhalf_array[1:-1,1:-1,1:-1]*\ (u_1[1:-1,1:-1,1:-1]-u_1[:-2,1:-1,1:-1]))\ + C3*(q_j_phalf_array[1:-1,1:-1,1:-1]*\ (u_1[1:-1,2:,1:-1]-u_1[1:-1,1:-1,1:-1])-\ q_j_mhalf_array[1:-1,1:-1,1:-1]*\ (u_1[1:-1,1:-1,1:-1]-u_1[1:-1,:-2,1:-1]))\ + C4*(q_k_phalf_array[1:-1,1:-1,1:-1]*\ (u_1[1:-1,1:-1,2:]-u_1[1:-1,1:-1,1:-1])-\ q_k_mhalf_array[1:-1,1:-1,1:-1]*\ (u_1[1:-1,1:-1,1:-1]-u_1[1:-1,1:-1,:-2]))\ + dtdt*f(x[1:-1],y[1:-1],z[1:-1],t[n])) else : u[1:-1,1:-1] = C1*(2*u_1[1:-1,1:-1] + Cb*u_2[1:-1,1:-1]\ + C2*(q_i_phalf_array[1:-1,1:-1]*(u_1[2:,1:-1]-u_1[1:-1,1:-1])-\ q_i_mhalf_array[1:-1,1:-1]*(u_1[1:-1,1:-1]-u_1[:-2,1:-1]))\ + C3*(q_j_phalf_array[1:-1,1:-1]*(u_1[1:-1,2:]-u_1[1:-1,1:-1])-\ q_j_mhalf_array[1:-1,1:-1]*(u_1[1:-1,1:-1]-u_1[1:-1,:-2]))\ + dtdt*f(x[1:-1],y[1:-1],t[n])) ############ Update the ghost points for u ############ if not quiet_mode: print "Updating ghost points" if self.physprm.gridprm.problem3D : BC(self.u,Ix,Iy,Iz,version) else : if self.outlet: if version=="scalar": for i in Ix: for j in Iy: term[i,j] = C5[i,j]*(u_2[i,j] - u[i,j]) elif version=="vectorized": term[1:-1,1:-1] = C5[1:-1,1:-1]*(u_2[1:-1,1:-1] - u[1:-1,1:-1]) BC(self.u,Ix,Iy,version,term) else: BC(self.u,Ix,Iy,version) ############ Update all values for next time iteration ############ if self.physprm.gridprm.problem3D : u_2[:,:,:], u_1[:,:,:] = u_1, u else : u_2[:,:], u_1[:,:] = u_1, u n += 1 # Update time level if save_figs : if self.physprm.gridprm.problem3D : plt.setp(interactive=False) h = plt.isosurface(xx,yy,zz,u_2,-3) h.setp(opacity=0.5) plt.shading("interp") plt.daspect([1,1,1]) plt.view(3) plt.axis("tight") plt.show() else : viz.store_fig(t[n],Ix,Iy,u_2,u_1,u) elif display: viz.display(t[n],Ix,Iy,u_2,u_1,u) if save_figs: viz.movie() return u
from math import * import numpy as np import scitools.easyviz as plt #from scitools.easyviz.gnuplot_ import * h0 = 2277. # Height of the top of the mountain (m) R = 4. # The radius of the mountain (km) x = y = np.linspace(-10., 10., 41) xv, yv = plt.ndgrid(x, y) # Grid for x, y values (km) hv = h0 / (1 + (xv**2 + yv**2) / (R**2)) # Compute height (m) x = y = np.linspace(-10., 10., 11) x2v, y2v = plt.ndgrid(x, y) # Define a coarser grid for the vector field h2v = h0 / (1 + (x2v**2 + y2v**2) / (R**2)) # Compute height for new grid dhdx, dhdy = np.gradient(h2v) # Compute the gradient vector (dh/dx,dh/dy) # Draw contours and gradient field of h plt.figure(9) plt.quiver(x2v, y2v, dhdx, dhdy, 0, 'r') plt.hold('on') plt.contour(xv, yv, hv) plt.axis('equal') # end draw contours and gradient field of h x = y = np.linspace(-5, 5, 11) xv, yv = plt.ndgrid(x, y) u = xv**2 + 2 * yv - .5 * xv * yv v = -3 * yv
from math import * import numpy as np import scitools.easyviz as plt #from scitools.easyviz.gnuplot_ import * h0 = 2277. # Height of the top of the mountain (m) R = 4. # The radius of the mountain (km) x = y = np.linspace(-10., 10., 41) xv, yv = plt.ndgrid(x, y) # Grid for x, y values (km) hv = h0/(1+(xv**2+yv**2)/(R**2)) # Compute height (m) x = y = np.linspace(-10.,10.,11) x2v, y2v = plt.ndgrid(x, y) # Define a coarser grid for the vector field h2v = h0/(1+(x2v**2+y2v**2)/(R**2)) # Compute height for new grid dhdx, dhdy = np.gradient(h2v) # Compute the gradient vector (dh/dx,dh/dy) # Draw contours and gradient field of h plt.figure(9) plt.quiver(x2v, y2v, dhdx, dhdy, 0, 'r') plt.hold('on') plt.contour(xv, yv, hv) plt.axis('equal') # end draw contours and gradient field of h x = y = np.linspace(-5, 5, 11) xv, yv = plt.ndgrid(x, y) u = xv**2 + 2*yv - .5*xv*yv v = -3*yv
from math import * import numpy as np import scitools.easyviz as plt h0 = 2277. # Height of the top of the mountain (m) R = 4. # The radius of the mountain (km) x = y = np.linspace(-10., 10., 41) xv, yv = plt.ndgrid(x, y) hv = h0/(1+(xv**2+yv**2)/(R**2)) s = np.linspace(0, 2*np.pi, 100) curve_x = 10*(1 - s/(2*np.pi))*np.cos(s) curve_y = 10*(1 - s/(2*np.pi))*np.sin(s) curve_z = h0/(1 + 100*(1 - s/(2*np.pi))**2/(R**2)) # Simple plot of mountain plt.figure(1) plt.mesh(xv, yv, hv) # Simple plot of mountain and parametric curve plt.figure(2) plt.surf(xv, yv, hv) plt.hold('on') # add the parametric curve. Last parameter controls color of the curve plt.plot3(curve_x, curve_y, curve_z, 'b-') # endsimpleplots # Default two-dimensional contour plot
from math import * import numpy as np import scitools.easyviz as plt h0 = 2277. # Height of the top of the mountain (m) R = 4. # The radius of the mountain (km) x = y = np.linspace(-10., 10., 41) xv, yv = plt.ndgrid(x, y) hv = h0 / (1 + (xv**2 + yv**2) / (R**2)) s = np.linspace(0, 2 * np.pi, 100) curve_x = 10 * (1 - s / (2 * np.pi)) * np.cos(s) curve_y = 10 * (1 - s / (2 * np.pi)) * np.sin(s) curve_z = h0 / (1 + 100 * (1 - s / (2 * np.pi))**2 / (R**2)) # Simple plot of mountain plt.figure(1) plt.mesh(xv, yv, hv) # Simple plot of mountain and parametric curve plt.figure(2) plt.surf(xv, yv, hv) plt.hold('on') # add the parametric curve. Last parameter controls color of the curve plt.plot3(curve_x, curve_y, curve_z, 'b-') # endsimpleplots # Default two-dimensional contour plot