#!/usr/bin/env python # coding: utf-8 # # Pet Tornado # # v1.3 28 May 2018, by Brian Fiedler # # tweeked September 2023 # # $\newcommand{\V}[1]{\vec{\boldsymbol{#1}}}$ # $\newcommand{\I}[1]{\widehat{\boldsymbol{\mathrm{#1}}}}$ # $\newcommand{\B}[1]{\overline{#1}}$ # $\newcommand{\pd}[2]{\frac{\partial#1}{\partial#2}}$ # $\newcommand{\dd}[2]{\frac{\D#1}{\D#2}}$ # $\newcommand{\pdt}[1]{\frac{\partial#1}{\partial t}}$ # $\newcommand{\ddt}[1]{\frac{\D#1}{\D t}}$ # $\newcommand{\D}{\mathrm{d}}$ # $\newcommand{\Ii}{\I{\imath}}$ # $\newcommand{\Ij}{\I{\jmath}}$ # $\newcommand{\Ik}{\I{k}}$ # $\newcommand{\VU}{\V{U}}$ # $\newcommand{\del}{\boldsymbol{\nabla}}$ # $\newcommand{\dt}{\cdot}$ # $\newcommand{\x}{\times}$ # $\newcommand{\dv}{\del\cdot}$ # $\newcommand{\curl}{\del\times}$ # $\newcommand{\lapl}{\nabla^2}$ # $\newcommand{\VI}[1]{\left\langle#1\right\rangle}$ # $\require{color}$ # A cubic domain, a box, with a buoyant blob is rotating (slowly) about it's central axis. The blob rises and creates # relative vorticity below. The stretching and amplification of the vorticity bears some resemblance to the formation of tornadoes within mesoscylones. # # The model equations look like they have a coriolis parameter in them, because the domain is rotating (and embedded within) the mesocyclone. Use your imagination. # In[1]: import matplotlib.pyplot as plt get_ipython().run_line_magic('matplotlib', 'inline') from IPython.display import display,clear_output, Image import time as Time import math, os, glob import numpy as np import scipy.fftpack import matplotlib matplotlib.rcParams.update({'font.size': 22}) from IPython.core.display import HTML import urllib.request # In[2]: HTML(urllib.request.urlopen('http://metrprof.xyz/metr4323.css').read().decode()) #HTML( open('metr4323.css').read() ) #or use this, if you have downloaded metr4233.css to your computer # We prognosticate the following dimensionless equations: # # $$ # \pdt{u} + u \pd{u}{x} + v \pd{u}{y} + w \pd{u}{z}= - \pd{P}{x} + \frac{f}{N} v # $$ # # $$ # \pdt{v}{t} + u \pd{v}{x} + v \pd{v}{y} + w \pd{v}{z}= - \pd{P}{y} - \frac{f}{N} u # $$ # # $$ # \pdt{w} + u \pd{w}{x} + v \pd{w}{y} + w \pd{w}{z}= - \pd{P}{z} + b # $$ # # $$ # \pd{u}{x} + \pd{v}{y} + \pd{w}{z} = 0 # $$ # # $$ # \pdt{b} + u \pd{b}{x} + v \pd{b}{y} + w \pd{b}{z}= - w # $$ # # The non-dimensionalization should be familiar to you. Note the time scale is $1/N$, not seconds. # In the code below, we use `fcoriolis`=$f/N$ # In[3]: ################ def ab_blend(dqdt,order): # blends the time derivatives for the 1st, 2nd and 3rd order Adams-Bashforth schemes if order==1: return dqdt[0] elif order==2: return 1.5*dqdt[0]-.5*dqdt[1] elif order==3: return (23*dqdt[0]-16*dqdt[1]+5*dqdt[2])/12. else: print("order", order ," not supported ") # ## Some functions for the 3-D C-grid # In[4]: ############################################## def make3dgrid(ix,iy,iz,xmax,ymax,zmax): # input number of desired gridpoints, and the span of the grid, # return 3-D arrays of the x y and z coordinates at the grid points dx=xmax/(ix-1.) dy=ymax/(iy-1.) dz=zmax/(iz-1.) x1=np.linspace(0,xmax,ix) y1=np.linspace(0,ymax,iy) z1=np.linspace(0,zmax,iz) x=np.zeros( (iz,iy,ix) ) y=np.zeros( (iz,iy,ix) ) z=np.zeros( (iz,iy,ix) ) for j in range(iy): for k in range(iz): x[k,j,:]=x1 for i in range(ix): for k in range(iz): y[k,:,i]=y1 for i in range(ix): for j in range(iy): z[:,j,i]=z1 return x,y,z,dx,dy,dz ############################################## def advect_3d(q,u,v,w,dx,dy,dz): # 3rd-order upwind advection dqdt=np.zeros(q.shape) dqmx=np.zeros(q.shape) dqpx=np.zeros(q.shape) dqmy=np.zeros(q.shape) dqpy=np.zeros(q.shape) dqmz=np.zeros(q.shape) dqpz=np.zeros(q.shape) # "m" is difference biased to the minus side, "p" to the plus side # must use first order "#1" if too close to wall dqmx[:,:,1] = q[:,:,1]-q[:,:,0] #1 dqmx[:,:,2:-1] = (2*q[:,:,3:]+3*q[:,:,2:-1]-6*q[:,:,1:-2]+q[:,:,:-3])/6. #3 dqpx[:,:,-2] = q[:,:,-1]-q[:,:,-2] #1 dqpx[:,:,1:-2] = -(2*q[:,:,0:-3]+3*q[:,:,1:-2]-6*q[:,:,2:-1]+q[:,:,3:])/6. #3 dqmy[:,1,:] = q[:,1,:]-q[:,0,:] #1 dqmy[:,2:-1,:] = (2*q[:,3:,:]+3*q[:,2:-1,:]-6*q[:,1:-2,:]+q[:,:-3,:])/6. #3 dqpy[:,-2,:] = q[:,-1,:]-q[:,-2,:] #1 dqpy[:,1:-2,:] = -(2*q[:,0:-3,:]+3*q[:,1:-2,:]-6*q[:,2:-1,:]+q[:,3:,:])/6. #3 dqmz[1,:,:] = q[1,:,:]-q[0,:,:] #1 dqmz[2:-1,:,:] = (2*q[3:,:,:]+3*q[2:-1,:,:]-6*q[1:-2,:,:]+q[:-3,:,:])/6. #3 dqpz[-2,:,:] = q[-1,:,:]-q[-2,:,:] #1 dqpz[1:-2,:,:] = -(2*q[0:-3,:,:]+3*q[1:-2,:,:]-6*q[2:-1,:,:]+q[3:,:,:])/6. #3 # use derivatives biased to the upwind side: dqdx=np.where(u>0.,dqmx,dqpx)/dx dqdy=np.where(v>0.,dqmy,dqpy)/dy dqdz=np.where(w>0.,dqmz,dqpz)/dz # advective terms: dqdt+=-u*dqdx dqdt+=-v*dqdy dqdt+=-w*dqdz return dqdt # In[5]: ############################################## # periodic version def advect_3dp(q,u,v,w,dx,dy,dz,per='U'): # 3rd-order upwind advection sh = q.shape Q = np.zeros( (sh[0],sh[1]+4,sh[2]+4) ) Q[:, 2:-2 , 2:-2] = q if per=='U': Q[ : , :2 , 2:-2 ] = q[ : , -3:-1 , : ] Q[ : , -2: , 2:-2] = q[ : , 1:3 , : ] Q[ : , 2:-2 , :2] = q[ : , : , -3:-1 ] Q[ : , 2:-2 , -2:] = q[ : , : , 1:3 ] elif per=='u': Q[ : , :2 , 2:-2 ] = q[ : , -2: , : ] Q[ : , -2: , 2:-2 ] = q[ : , :2 , : ] Q[ : , 2:-2 , :2 ] = q[ : , : , -3:-1 ] Q[ : , 2:-2 , -2: ] = q[ : , : , 1:3 ] elif per=='v': Q[ : , :2 , 2:-2 ] = q[ : , -3:-1 , : ] Q[ : , -2: , 2:-2 ] = q[ : , 1:3 , : ] Q[ : , 2:-2 , :2 ] = q[ : , : , -2: ] Q[ : , 2:-2 , -2: ] = q[ : , : , :2 ] elif per=='w': Q[ : , :2 , 2:-2 ] = q[ : , -2: , : ] Q[ : , -2: , 2:-2 ] = q[ : , :2 , : ] Q[ : , 2:-2 , :2 ] = q[ : , : , -2: ] Q[ : , 2:-2 , -2: ] = q[ : , : , :2 ] dqdt=np.zeros(sh) dqmx=np.zeros(sh) dqpx=np.zeros(sh) dqmy=np.zeros(sh) dqpy=np.zeros(sh) dqmz=np.zeros(sh) dqpz=np.zeros(sh) # "m" is difference biased to the minus side, "p" to the plus side # must use first order "#1" if too close to wall dqmx[:,:,:] = (2*Q[:,2:-2,3:-1] + 3*Q[:,2:-2,2:-2] - 6*Q[:,2:-2,1:-3] + Q[:,2:-2,:-4])/6. dqpx[:,:,:] = -(2*Q[:,2:-2,1:-3] + 3*Q[:,2:-2,2:-2] - 6*Q[:,2:-2,3:-1] + Q[:,2:-2,4:] )/6. dqmy[:,:,:] = (2*Q[:,3:-1,2:-2] + 3*Q[:,2:-2,2:-2] - 6*Q[:,1:-3,2:-2] + Q[:,:-4,2:-2])/6. dqpy[:,:,:] = -(2*Q[:,1:-3,2:-2] + 3*Q[:,2:-2,2:-2] - 6*Q[:,3:-1,2:-2] + Q[:,4:,2:-2] )/6. dqmz[1,:,:] = q[1,:,:]-q[0,:,:] #1 dqmz[2:-1,:,:] = (2*q[3:,:,:]+3*q[2:-1,:,:]-6*q[1:-2,:,:]+q[:-3,:,:])/6. #3 dqpz[-2,:,:] = q[-1,:,:]-q[-2,:,:] #1 dqpz[1:-2,:,:] = -(2*q[0:-3,:,:]+3*q[1:-2,:,:]-6*q[2:-1,:,:]+q[3:,:,:])/6. #3 # use derivatives biased to the upwind side: dqdx=np.where(u>0.,dqmx,dqpx)/dx dqdy=np.where(v>0.,dqmy,dqpy)/dy dqdz=np.where(w>0.,dqmz,dqpz)/dz # advective terms: dqdt+=-u*dqdx dqdt+=-v*dqdy dqdt+=-w*dqdz return dqdt # In[6]: # divergence on the C-grid def div_Cgrid(u,v,w,dx,dy,dz): return (u[:,:,1:]-u[:,:,:-1])/dx + (v[:,1:,:]-v[:,:-1,:])/dy + (w[1:,:,:]-w[:-1,:,:])/dz # In[7]: def pgf_3dp(p,dx,dy,dz): # 20 May 2016 Periodic # Calculates periodic pressure gradient force on the U-grid dudt=np.zeros( (p.shape[0]+1, p.shape[1]+1 , p.shape[2]+1 ) ) dvdt=np.zeros( dudt.shape ) dwdt=np.zeros( dudt.shape ) P = np.zeros( (p.shape[0], p.shape[1]+2 , p.shape[2]+2 ) ) P[:,1:-1,1:-1] = p P[:,0,1:-1] = p[:,-1,:] P[:,-1,1:-1] = p[:,0,:] P[:,1:-1,0] = p[:,:,-1] P[:,1:-1,-1] = p[:,:,0] dpx = (P[:,:,1:]-P[:,:,:-1])/dx dudt[ 1:-1 , : , : ] = -.25* ( dpx[:-1,:-1,:] + dpx[:-1,1:,:] +dpx[1:,:-1,:] + dpx[1:,1:,:] ) dudt[ 0, : , :] = -.5*(dpx[0,1:,:] + dpx[0,:-1,:] ) dudt[-1 ,: , :] = -.5*(dpx[-1,1:,:] + dpx[-1,:-1,:] ) dpy = (P[:,1:,:]-P[:,:-1,:])/dy dvdt[1:-1,:,:] = -.25* ( dpy[:-1,:,:-1] + dpy[:-1,:,1:] +dpy[1:,:,:-1] + dpy[1:,:,1:] ) dvdt[0, : , :] = -.5* ( dpy[0,:,1:] + dpy[0,:,:-1] ) dvdt[-1,: , :] = -.5* ( dpy[-1,:,1:] + dpy[-1,:,:-1] ) dpz = (P[1:,:,:]-P[:-1,:,:])/dz dwdt[1:-1, :, :] = -.25* ( dpz[:,:-1,:-1] + dpz[:,:-1,1:] +dpz[:,1:,:-1] + dpz[:,1:,1:] ) return dudt,dvdt,dwdt # In[8]: ############ #Interpolators for the C grid def v_to_u(v,bnd='slip'): iz,iy,ix = v.shape atu=np.zeros((iz,iy-1,ix+1)) atu[:,:,1:-1] = .25*( v[:,:-1,:-1] + v[:,:-1,1:] + v[:,1:,:-1] + v[:,1:,1:] ) if bnd=='slip': atu[:,:,0] = atu[:,:,1] atu[:,:,-1] = atu[:,:,-2] elif bnd=='per': atu[:,:,0] = .25*( v[:,:-1,-1] + v[:,:-1,0] + v[:,1:,-1] + v[:,1:,0] ) atu[:,:,-1] = atu[:,:,0] else: sys.exit('nothing for: '+bnd) return atu def w_to_u(w,bnd='slip'): iz,iy,ix = w.shape atu=np.zeros((iz-1,iy,ix+1)) atu[:,:,1:-1] = .25*( w[:-1,:,:-1] + w[:-1,:,1:] + w[1:,:,:-1] + w[1:,:,1:] ) if bnd=='slip': atu[:,:,0] = atu[:,:,1] atu[:,:,-1] = atu[:,:,-2] elif bnd=='per': atu[:,:,0] = .25*( w[:-1,:,-1] + w[:-1,:,0] + w[1:,:,-1] + w[1:,:,0] ) atu[:,:,-1] = atu[:,:,0] else: sys.exit('nothing for: '+bnd) return atu def u_to_v(u,bnd='slip'): iz,iy,ix = u.shape atv=np.zeros((iz,iy+1,ix-1)) atv[:,1:-1,:] = .25*( u[:,:-1,:-1] + u[:,:-1,1:] + u[:,1:,:-1] + u[:,1:,1:] ) if bnd=='slip': atv[:,0,:] = atv[:,1,:] atv[:,-1,:] = atv[:,-2,:] elif bnd=='per': atv[:,0,:] = .25*( u[:,-1,:-1] + u[:,-1,1:] + u[:,0,:-1] + u[:,0,1:] ) atv[:,-1,:] = atv[:,0,:] else: sys.exit('nothing for: '+bnd) return atv def w_to_v(w,bnd='slip'): iz,iy,ix = w.shape atv=np.zeros((iz-1,iy+1,ix)) atv[:,1:-1,:] = .25*( w[:-1,:-1,:] + w[:-1,1:,:] + w[1:,:-1,:] + w[1:,1:,:] ) if bnd=='slip': atv[:,0,:] = atv[:,1,:] atv[:,-1,:] = atv[:,-2,:] elif bnd=='per': atv[:,0,:] = .25*( w[:-1,-1,:] + w[:-1,0,:] + w[1:,-1,:] + w[1:,0,:] ) atv[:,-1,:] = atv[:,0,:] else: sys.exit('nothing for: '+bnd) return atv def u_to_w(u,bnd='slip'): iz,iy,ix = u.shape atw=np.zeros((iz+1,iy,ix-1)) atw[1:-1,:,:] = .25*( u[:-1,:,:-1] + u[:-1,:,1:] + u[1:,:,:-1] + u[1:,:,1:] ) if bnd=='slip': atw[0,:,:] = atw[1,:,:] atw[-1,:,:] = atw[-2,:,:] else: sys.exit('nothing for: '+bnd) return atw def v_to_w(v,bnd='slip'): iz,iy,ix = v.shape atw=np.zeros((iz+1,iy-1,ix)) atw[1:-1,:,:] = .25*( v[:-1,:-1,:] + v[:-1,1:,:] + v[1:,:-1,:] + v[1:,1:,:] ) if bnd=='slip': atw[0,:,:]=atw[1,:,:] atw[-1,:,:]=atw[-2,:,:] else: sys.exit('nothing for: '+bnd) return atw # These are easy for the C-grid. def u_to_p(u): return (u[:,:,:-1] + u[:,:,1:] )*.5 def v_to_p(v): return (v[:,:-1,:] + v[:,1:,:] )*.5 def w_to_p(w): return (w[:-1,:,:] + w[1:,:,:] )*.5 ############################################################# def U2p_3d(U): # interpolates U-grid array to a p-grid array return .125*( U[1:,:-1,1:] + U[1:,1:,1:] + U[1:,:-1,:-1] + U[1:,1:,:-1] + U[:-1,:-1,1:] + U[:-1,1:,1:] + U[:-1,:-1,:-1] + U[:-1,1:,:-1]) # In[9]: #######################3 # Expands the margins of a matplotlib axis, # and so prevents arrows on boundaries from being clipped. def stop_clipping(ax,marg=.02): # default is 2% increase l,r,b,t = ax.axis() dx,dy = r-l, t-b ax.axis([l-marg*dx, r+marg*dx, b-marg*dy, t+marg*dy]) # # non-hydrostatic pressure solver # In[10]: def lapl_p_3d(p,dx,dy,dz): # Returns Laplacian of p, d^2p/dx^2 + d^2/dy^2 + d^2/dz^2, # assumimg no normal pressure gradient at boundaries. # Uses image points outside of the domain of p, so the returned laplacian # is the same size as p sh=p.shape b = np.zeros((sh[0]+2,sh[1]+2,sh[2]+2)) # make bigger array # stuff in image point values: b[1:-1,1:-1,1:-1]=p b[1:-1,1:-1,0]=p[:,:,0] b[1:-1,1:-1,-1]=p[:,:,-1] b[1:-1,0,1:-1]=p[:,0,:] b[1:-1,-1,1:-1]=p[:,-1,:] b[0,1:-1,1:-1]=p[0,:,:] b[-1,1:-1,1:-1]=p[-1,:,:] dx2=dx*dx dy2=dy*dy dz2=dz*dz laplp=np.zeros(sh) laplp += (-2*b[1:-1,1:-1,1:-1] + b[1:-1,1:-1,:-2] + b[1:-1,1:-1,2:])/dx2 laplp += (-2*b[1:-1,1:-1,1:-1] + b[1:-1,2:,1:-1] + b[1:-1,:-2,1:-1])/dy2 laplp += (-2*b[1:-1,1:-1,1:-1] + b[2:,1:-1,1:-1] + b[:-2,1:-1,1:-1])/dz2 return laplp def lapl_p_3d_periodic(p,dx,dy,dz): # Returns Laplacian of p, d^2p/dx^2 + d^2/dy^2 + d^2/dz^2, # assumimg x and y are periodic, and no normal pressure gradient at top and bottom # Uses image points outside of the domain of p, so the returned Laplacian # is the same size as p sh=p.shape b = np.zeros((sh[0]+2,sh[1]+2,sh[2]+2)) # make bigger array # stuff in image point values: b[1:-1,1:-1,1:-1]=p b[1:-1,1:-1,0]=p[:,:,-1] b[1:-1,1:-1,-1]=p[:,:,0] b[1:-1,0,1:-1]=p[:,-1,:] b[1:-1,-1,1:-1]=p[:,0,:] b[0,1:-1,1:-1]=p[0,:,:] b[-1,1:-1,1:-1]=p[-1,:,:] dx2=dx*dx dy2=dy*dy dz2=dz*dz laplp = np.zeros(sh) laplp += (-2*b[1:-1,1:-1,1:-1] + b[1:-1,1:-1,:-2] + b[1:-1,1:-1,2:])/dx2 laplp += (-2*b[1:-1,1:-1,1:-1] + b[1:-1,2:,1:-1] + b[1:-1,:-2,1:-1])/dy2 laplp += (-2*b[1:-1,1:-1,1:-1] + b[2:,1:-1,1:-1] + b[:-2,1:-1,1:-1])/dz2 return laplp def poisson3d_p_fft_prep(sh,dx,dy,dz,lapl='discrete',periodic=False): # You must run this and save the array output for input into poisson_cosine_3. # poisson_cosine_3 is for box domains with boundary conditions of no normal # pressure gradient. As such, the pressure can be represented by a 3-D Fourier # cosine series. L = dx*sh[2] W = dy*sh[1] H = dz*sh[0] Ka = np.arange(sh[2]) # the wavenumbers of the cos functions in the x-direction Ma = np.arange(sh[1]) # ... in the y-direction Na = np.arange(sh[0]) # ... in the z-direction if periodic: # use both sine and cosine Ka[1::2] += 1 Ma[1::2] += 1 ka = Ka*np.pi/L ma = Ma*np.pi/W na = Na*np.pi/H # Suppose we have an amplitude of a certain Fourier mode of del2p. # (Usually del2p is the divergence of acceleration prior to adding the PGF.) # We want to find a mode of p such that the finite-difference form of # Laplacian operator operating on p give this amplitude. # The reciprocal of the lapl_op multipled by the amplitude of the Fourier # mode of del2p will give the amplitude of this Fourier mode of p. # Note: the continuous form, as opposed to discrete form, of lapl_op is very simple. # You are welcome to uncomment it and experiment with it. lapl_op = np.zeros(sh) if lapl == 'discrete': lapl_op[:] = (2*np.cos(ka*dx)-2)/dx**2 more = (2*np.cos(ma*dy)-2)/dy**2 lapl_op += more.reshape(1,sh[1],1) more = (2*np.cos(na*dz)-2)/dz**2 lapl_op += more.reshape(sh[0],1,1) else: # use simple, calculus form for d^2/dx^2, etc. lapl_op[:] = -ka**2 more = -ma**2 lapl_op += more.reshape(1,sh[1],1) more = -na**2 lapl_op += more.reshape(sh[0],1,1) lapl_op[0,0,0]=1. # Prevents division by zero. (The [0,0,0] Fourier mode is zero anyways) return 1./lapl_op # the inverse Laplacian operator def poisson3d_p_fft(del2p,inv_lapl_op,periodic=False): # 3-D pressure solver # del2p is the desired Laplacian of pressure. # inv_lapl_op is from poisson_cosine_3_prep # pressure is assumed to have no normal pressure gradient at boundaries. # Assumes a C-grid. The physical boundary is half a grid cell away from # the pressure points. Thus type=2. # dct means discrete cosine transform. # p has the same shape as d2p. Fastest shape (64,64,64), (128,128,128), etc if not periodic: sh = del2p.shape del2pt = scipy.fftpack.dct( del2p , axis=2, type=2) del2pt = scipy.fftpack.dct( del2pt , axis=1, type=2) del2pt = scipy.fftpack.dct( del2pt , axis=0, type=2) # del2pt is now the amplitudes of the Fourier modes of del2p pt = del2pt*inv_lapl_op # amplitudes of Fourier modes of pressure pt = scipy.fftpack.idct(pt,axis=0,type=2) # inverse transform of pt to p pt = scipy.fftpack.idct(pt,axis=1,type=2) # inverse transform of pt to p p = scipy.fftpack.idct(pt,axis=2,type=2) # inverse transform of pt to p p = p/(8*sh[0]*sh[1]*sh[2]) #The need for this division is convention of fft p -= p.mean() else: sh = del2p.shape del2pt = scipy.fftpack.rfft( del2p , axis=2) del2pt = scipy.fftpack.rfft( del2pt , axis=1) del2pt = scipy.fftpack.dct( del2pt , axis=0, type=2) # del2pt is now the amplitudes of the Fourier modes of del2p pt = del2pt*inv_lapl_op # amplitudes of Fourier modes of pressure pt = scipy.fftpack.idct(pt,axis=0,type=2) # inverse transform of pt to p pt = scipy.fftpack.irfft(pt,axis=1) # inverse transform of pt to p p = scipy.fftpack.irfft(pt,axis=2) # inverse transform of pt to p p = p/(2*sh[0]) #The need for this division is convention of fft p -= p.mean() return p # ## Test the non-hydrostatic pressure solver # In[11]: Nx,Ny,Nz = 129,129,129 # pressure solver much faster #Nx,Ny,Nz = 128,128,128 # pressure solver very slow xmax=1. ymax=2. zmax=3. np.random.seed(2) p_orig = np.random.random((Nz-1,Ny-1,Nx-1)) p_orig -= p_orig.mean() dx = xmax/(Nx-1) dy = ymax/(Ny-1) dz = zmax/(Nz-1) #uncomment either the cosine or periodic stuff #lapl_of_p = lapl_p_3d(p_orig, dx, dy,dz) #inv_lapl_op = poisson_cosine_3_prep(lapl_of_p,dx,dy,dz) #pp = poisson_cosine_3(lapl_of_p,inv_lapl_op) peri = True if peri: lapl_of_p = lapl_p_3d_periodic(p_orig, dx, dy, dz) else: lapl_of_p = lapl_p_3d(p_orig, dx, dy, dz) inv_lapl_op = poisson3d_p_fft_prep(lapl_of_p.shape,dx,dy,dz,lapl='discrete',periodic=peri) pp = poisson3d_p_fft(lapl_of_p,inv_lapl_op,periodic=peri) pp -= pp.mean() d2=( pp - p_orig )**2 error= np.sqrt( d2.mean() ) print("should be small:", error) # In[12]: get_ipython().run_cell_magic('timeit', '', 'pp = poisson3d_p_fft(lapl_of_p,inv_lapl_op)\n') #