305 lines
11 KiB
Python
305 lines
11 KiB
Python
#***************************************************************************
|
|
#* *
|
|
#* Copyright (c) 2011, 2012 *
|
|
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
|
#* *
|
|
#* This program is free software; you can redistribute it and/or modify *
|
|
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
#* as published by the Free Software Foundation; either version 2 of *
|
|
#* the License, or (at your option) any later version. *
|
|
#* for detail see the LICENCE text file. *
|
|
#* *
|
|
#* 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 Library General Public License for more details. *
|
|
#* *
|
|
#* You should have received a copy of the GNU Library General Public *
|
|
#* License along with this program; if not, write to the Free Software *
|
|
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
#* USA *
|
|
#* *
|
|
#***************************************************************************
|
|
|
|
# numpy
|
|
import numpy as np
|
|
|
|
grav=9.81
|
|
|
|
class simEvolution:
|
|
def __init__(self, context=None, queue=None):
|
|
""" Constructor.
|
|
@param context OpenCL context where apply. Only for compatibility,
|
|
must be None.
|
|
@param queue OpenCL command queue. Only for compatibility,
|
|
must be None.
|
|
"""
|
|
self.context = context
|
|
self.queue = queue
|
|
|
|
def executeRK4(self, x, dx, p, dp, pos, vel, phi, dphi, fs, sea, body, waves, dt, t, stage):
|
|
""" Compute free surface RK4 stage evolution process (valid for stages 1,2 and 3).
|
|
@param x Output free surface z coordinates.
|
|
@param dx Output free surface z coordinates variation (dz/dt).
|
|
@param p Output potentials.
|
|
@param dp Output potentials variation (dphi/dt).
|
|
@param pos Input free surface positions.
|
|
@param vel Input free surface velocities.
|
|
@param phi Input potentials.
|
|
@param dphi Input potentials variation (dphi/dt).
|
|
@param fs Free surface instance.
|
|
@param sea Sea instance.
|
|
@param body Body instance.
|
|
@param waves Waves instance.
|
|
@param dt Time step.
|
|
@param t Actual time (without adding dt).
|
|
@param stage Runge-Kutta4 stage.
|
|
@return Input variables evoluted one time step.
|
|
"""
|
|
# --------------------------------------------
|
|
# Only free surface
|
|
# --------------------------------------------
|
|
h = fs['h']
|
|
nx = fs['Nx']
|
|
ny = fs['Ny']
|
|
nF = nx*ny
|
|
factor = 0.5
|
|
if stage > 2:
|
|
factor = 1.
|
|
for i in range(0,nx):
|
|
for j in range(0,ny):
|
|
x[i,j] = np.copy(pos[i,j][2])
|
|
dx[i,j] = np.copy(vel[i,j][2])
|
|
x[i,j] = x[i,j] + factor*dt*dx[i,j]
|
|
p[i*ny+j] = np.copy(phi[i*ny+j])
|
|
dp[i*ny+j] = np.copy(dphi[i*ny+j])
|
|
p[i*ny+j] = p[i*ny+j] + factor*dt*dp[i*ny+j]
|
|
# Impose values at beach (far free surface)
|
|
nbx = fs['Beachx']
|
|
nby = fs['Beachy']
|
|
for i in range(0,nx):
|
|
for j in range(0,nby) + range(ny-nby,ny):
|
|
[x[i,j],dx[i,j],p[i*ny+j],dp[i*ny+j]] = self.beach(pos[i,j], waves, factor*dt, t)
|
|
for j in range(0,ny):
|
|
for i in range(0,nbx) + range(nx-nbx,nx):
|
|
[x[i,j],dx[i,j],p[i*ny+j],dp[i*ny+j]] = self.beach(pos[i,j], waves, factor*dt, t)
|
|
# --------------------------------------------
|
|
# Sea boundaries, where potentials are fixed.
|
|
# We use the gradient projected over normal,
|
|
# see initialization for more details about
|
|
# this.
|
|
# --------------------------------------------
|
|
ids = ['front','back','left','right','bottom']
|
|
i0 = fs['N']
|
|
for index in ids:
|
|
s = sea[index]
|
|
nx = s['Nx']
|
|
ny = s['Ny']
|
|
for i in range(0,nx):
|
|
for j in range(0,ny):
|
|
p[i0 + i*ny+j] = 0.
|
|
dp[i0 + i*ny+j] = 0.
|
|
for w in waves['data']:
|
|
A = w[0]
|
|
T = w[1]
|
|
phase = w[2]
|
|
heading = np.pi*w[3]/180.0
|
|
wl = 0.5 * grav / np.pi * T*T
|
|
k = 2.0*np.pi/wl
|
|
frec = 2.0*np.pi/T
|
|
pos = s['pos'][i,j]
|
|
l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading)
|
|
normal = s['normal'][i,j]
|
|
hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h)
|
|
factor = np.dot(normal,np.array([np.cos(heading), np.sin(heading), 0.]))
|
|
amp = frec*A*np.sin(k*l - frec*(t+factor*dt) + phase)*hfact
|
|
p[i0 + i*ny+j] = p[i0 + i*ny+j] + factor*amp
|
|
amp = - grav*A*k*np.cos(k*l - frec*(t+factor*dt) + phase)*hfact
|
|
dp[i0 + i*ny+j] = dp[i0 + i*ny+j] + factor*amp
|
|
i0 = i0 + s['N']
|
|
|
|
def execute(self, dx1, dx2, dx3, dp1, dp2, dp3, fs, sea, body, waves, bem, dt, t):
|
|
""" Compute free surface evolution process (execute it on RK4 last stage).
|
|
@param dx1 Input free surface positions variation on stage 1.
|
|
@param dx2 Input free surface positions variation on stage 2.
|
|
@param dx3 Input free surface positions variation on stage 3.
|
|
@param dp1 Input free surface potentials variation on stage 1.
|
|
@param dp2 Input free surface potentials variation on stage 2.
|
|
@param dp3 Input free surface potentials variation on stage 3.
|
|
@param fs Free surface instance.
|
|
@param sea Sea instance.
|
|
@param body Body instance.
|
|
@param waves Waves instance.
|
|
@param bem Boundary Element Method instance.
|
|
@param dt Time step.
|
|
@param t Actual time (without adding dt).
|
|
@param stage Runge-Kutta4 stage.
|
|
@return Input variables evoluted one time step.
|
|
"""
|
|
h = fs['h']
|
|
nx = fs['Nx']
|
|
ny = fs['Ny']
|
|
nF = nx*ny
|
|
for i in range(0,nx):
|
|
for j in range(0,ny):
|
|
# In this stage dx4 and dp4 are directly known from the previous
|
|
# stage.
|
|
dx4 = fs['vel'][i,j][2]
|
|
dp4 = bem['dp4'][i*ny+j]
|
|
# And we only need to apply the integration scheme
|
|
fs['pos'][i,j][2] = fs['pos'][i,j][2] + dt/6. * (dx1[i,j] + 2.*dx2[i,j] + 2.*dx3[i,j] + dx4)
|
|
bem['p4'][i*ny+j] = bem['p4'][i*ny+j] + dt/6. * (dp1[i*ny+j] + 2.*dp2[i*ny+j] + 2.*dp3[i*ny+j] + dp4)
|
|
# In order to can apply the boundary condition at the free surface
|
|
# at the end of this RK4 stage, we need to store eta in a variable.
|
|
# x1 is safe because will be over written at the start of next
|
|
# time step.
|
|
fs['x1'][i,j] = fs['pos'][i,j][2]
|
|
# Impose values at beach (far free surface)
|
|
nbx = fs['Beachx']
|
|
nby = fs['Beachy']
|
|
for i in range(0,nx):
|
|
for j in range(0,nby) + range(ny-nby,ny):
|
|
[x,dummy,p,dummy] = self.beach(fs['pos'][i,j], waves, dt, t)
|
|
fs['pos'][i,j][2] = x
|
|
bem['p4'][i*ny+j] = p
|
|
fs['x1'][i,j] = fs['pos'][i,j][2]
|
|
for j in range(0,ny):
|
|
for i in range(0,nbx) + range(nx-nbx,nx):
|
|
[x,dummy,p,dummy] = self.beach(fs['pos'][i,j], waves, dt, t)
|
|
fs['pos'][i,j][2] = x
|
|
bem['p4'][i*ny+j] = p
|
|
fs['x1'][i,j] = fs['pos'][i,j][2]
|
|
# --------------------------------------------
|
|
# Sea boundaries, where potentials are fixed.
|
|
# We use the gradient projected over normal,
|
|
# see initialization for more details about
|
|
# this.
|
|
# --------------------------------------------
|
|
ids = ['front','back','left','right','bottom']
|
|
i0 = fs['N']
|
|
p = bem['p4']
|
|
dp = bem['dp4']
|
|
for index in ids:
|
|
s = sea[index]
|
|
nx = s['Nx']
|
|
ny = s['Ny']
|
|
for i in range(0,nx):
|
|
for j in range(0,ny):
|
|
p[i0 + i*ny+j] = 0.
|
|
dp[i0 + i*ny+j] = 0.
|
|
for w in waves['data']:
|
|
A = w[0]
|
|
T = w[1]
|
|
phase = w[2]
|
|
heading = np.pi*w[3]/180.0
|
|
wl = 0.5 * grav / np.pi * T*T
|
|
k = 2.0*np.pi/wl
|
|
frec = 2.0*np.pi/T
|
|
pos = s['pos'][i,j]
|
|
l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading)
|
|
normal = s['normal'][i,j]
|
|
hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h)
|
|
factor = np.dot(normal,np.array([np.cos(heading), np.sin(heading), 0.]))
|
|
amp = frec*A*np.sin(k*l - frec*(t+factor*dt) + phase)*hfact
|
|
p[i0 + i*ny+j] = p[i0 + i*ny+j] + factor*amp
|
|
amp = - grav*A*k*np.cos(k*l - frec*(t+factor*dt) + phase)*hfact
|
|
dp[i0 + i*ny+j] = dp[i0 + i*ny+j] + factor*amp
|
|
i0 = i0 + s['N']
|
|
|
|
def executeFSBC(self, x, fs, sea, body, waves, bem, dt, t, stage):
|
|
""" Compute free surface boundary conditions in order to get
|
|
free surface points velocity and potentials acceleration for
|
|
the next RK4 stage.
|
|
@param x Free surface z coordinates.
|
|
@param fs Free surface instance.
|
|
@param sea Sea boundaries instance.
|
|
@param body Body instance.
|
|
@param waves Waves instance.
|
|
@param bem Boundary Element Method instance.
|
|
@param dt Time step.
|
|
@param t Actual time (without adding dt).
|
|
"""
|
|
nx = fs['Nx']
|
|
ny = fs['Ny']
|
|
nF = nx*ny
|
|
factor = 0.5
|
|
if stage > 2:
|
|
factor = 1.
|
|
for i in range(0,nx):
|
|
for j in range(0,ny):
|
|
pos = np.copy(fs['pos'][i,j])
|
|
pos[2] = x[i,j]
|
|
gradVal = bem['Ap'][i*ny+j]
|
|
normal = fs['normal'][i,j]
|
|
# v_z = dphi/dz - grad(phi)*grad(z) - U*dz/dx
|
|
dzdt = gradVal*normal[2]
|
|
# dphi/dt = - rho*g*z - 0.5*grad(phi)^2 + v_z*dphi/dz - p_0 - U*dphi/dx - dU/dt*x
|
|
dphidt = -grav*pos[2] - 0.5*np.dot(gradVal,gradVal) # + dzdt*gradVal*normal[2]
|
|
# We need to preserve data on free surface global
|
|
# velocity and potential values in order to use as
|
|
# input of the next RK4 stage
|
|
fs['vel'][i,j][2] = dzdt
|
|
bem['dp4'][i*ny+j] = dphidt
|
|
# Impose values at beach (far free surface)
|
|
nbx = fs['Beachx']
|
|
nby = fs['Beachy']
|
|
for i in range(0,nx):
|
|
for j in range(0,nby) + range(ny-nby,ny):
|
|
[dummy,dx,dummy,dp] = self.beach(fs['pos'][i,j], waves, factor*dt, t)
|
|
fs['vel'][i,j][2] = dx
|
|
bem['dp4'][i*ny+j] = dp
|
|
for j in range(0,ny):
|
|
for i in range(0,nbx) + range(nx-nbx,nx):
|
|
[dummy,dx,dummy,dp] = self.beach(fs['pos'][i,j], waves, factor*dt, t)
|
|
fs['vel'][i,j][2] = dx
|
|
bem['dp4'][i*ny+j] = dp
|
|
|
|
def beach(self, pos, waves, dt, t):
|
|
""" Compute far free surface where only
|
|
incident waves can be taken into account.
|
|
@param pos Free surface position.
|
|
@param waves Waves instance.
|
|
@param dt Time step.
|
|
@param t Actual time (without adding dt).
|
|
@return Position, velocity, potential and potential acceleration
|
|
"""
|
|
h = waves['h']
|
|
x = 0.
|
|
dx = 0.
|
|
p = 0.
|
|
dp = 0.
|
|
# Since values of the potencial, and this acceleration,
|
|
# depends on z, we need to compute first the positions.
|
|
for w in waves['data']:
|
|
A = w[0]
|
|
T = w[1]
|
|
phase = w[2]
|
|
heading = np.pi*w[3]/180.0
|
|
wl = 0.5 * grav / np.pi * T*T
|
|
k = 2.0*np.pi/wl
|
|
frec = 2.0*np.pi/T
|
|
l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading)
|
|
# hfact = np.sinh(k*(pos[2]+h)) / np.cosh(k*h)
|
|
hfact = 1.0
|
|
amp = A*np.sin(k*l - frec*(t+dt) + phase)*hfact
|
|
x = x + amp
|
|
amp = - A*frec*np.cos(k*l - frec*(t+dt) + phase)*hfact
|
|
dx = dx + amp
|
|
# And now we can compute potentials.
|
|
for w in waves['data']:
|
|
A = w[0]
|
|
T = w[1]
|
|
phase = w[2]
|
|
heading = np.pi*w[3]/180.0
|
|
wl = 0.5 * grav / np.pi * T*T
|
|
k = 2.0*np.pi/wl
|
|
frec = 2.0*np.pi/T
|
|
l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading)
|
|
hfact = np.cosh(k*(x+h)) / np.cosh(k*h)
|
|
amp = - grav/frec*A*np.sin(k*l - frec*(t+dt) + phase)*hfact
|
|
p = p + amp
|
|
amp = grav*A*np.cos(k*l - frec*(t+dt) + phase)*hfact
|
|
dp = dp + amp
|
|
return [x,dx,p,dp]
|
|
|