Skip to content

Commit b3fb74d

Browse files
committed
Refactor wind (wind.py) and shear (shear.py) modules to connect to new separation bubble approach
1 parent caae299 commit b3fb74d

File tree

2 files changed

+33
-195
lines changed

2 files changed

+33
-195
lines changed

aeolis/shear.py

Lines changed: 8 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
# package modules
4040
from aeolis.utils import *
41-
41+
from aeolis.separation import compute_separation
4242

4343
# initialize logger
4444
logger = logging.getLogger(__name__)
@@ -142,10 +142,8 @@ def __init__(self, x, y, z, dx, dy, L, l, z0,
142142
self.z0 = z0
143143

144144

145-
def __call__(self, x, y, z, taux, tauy, u0, udir,
146-
process_separation, c, mu_b,
147-
taus0, taun0, sep_filter_iterations, zsep_y_filter,
148-
plot=False):
145+
def __call__(self, p, x, y, z, u0, udir,
146+
taux, tauy, taus0, taun0, plot=False):
149147

150148
'''Compute wind shear for given wind speed and direction
151149
@@ -222,10 +220,9 @@ def __call__(self, x, y, z, taux, tauy, u0, udir,
222220
# rotate to horizontal computational grid and add to tau0
223221
# =====================================================================
224222

225-
# Compute separation bubble
226-
if process_separation:
227-
zsep = self.separation(c, mu_b, sep_filter_iterations, zsep_y_filter)
228-
z_origin = gc['z'].copy()
223+
if p['process_separation']:
224+
z_origin = gc['z'][:].copy()
225+
zsep = compute_separation(p, gc['z'], gc['dx'])
229226
gc['z'] = np.maximum(gc['z'], zsep)
230227

231228
# Compute wind shear stresses on computational grid
@@ -238,7 +235,7 @@ def __call__(self, x, y, z, taux, tauy, u0, udir,
238235
gc['taux'] = np.maximum(gc['taux'], 0.)
239236

240237
# Compute the influence of the separation on the shear stress
241-
if process_separation:
238+
if p['process_separation']:
242239
gc['hsep'] = gc['z'] - z_origin
243240
self.separation_shear(gc['hsep'])
244241

@@ -264,7 +261,7 @@ def __call__(self, x, y, z, taux, tauy, u0, udir,
264261
gi['taux'] = self.interpolate(gc['x'], gc['y'], gc['taux'], gi['x'], gi['y'], taus0)
265262
gi['tauy'] = self.interpolate(gc['x'], gc['y'], gc['tauy'], gi['x'], gi['y'], taun0)
266263

267-
if process_separation:
264+
if p['process_separation']:
268265
gi['hsep'] = self.interpolate(gc['x'], gc['y'], gc['hsep'], gi['x'], gi['y'], 0. )
269266

270267
# Final plots and lay-out
@@ -368,158 +365,6 @@ def set_computational_grid(self, udir):
368365

369366
return self
370367

371-
372-
373-
def separation(self, c, mu_b, sep_filter_iterations, zsep_y_filter):
374-
375-
# Initialize grid and bed dimensions
376-
gc = self.cgrid
377-
378-
x = gc['x']
379-
y = gc['y']
380-
z = gc['z']
381-
382-
nx = len(gc['z'][1])
383-
ny = len(gc['z'][0])
384-
dx = gc['dx']
385-
dy = gc['dy']
386-
387-
# Initialize arrays
388-
389-
dzx = np.zeros(gc['z'].shape)
390-
391-
dzdx0 = np.zeros(gc['z'].shape)
392-
dzdx1 = np.zeros(gc['z'].shape)
393-
394-
stall = np.zeros(gc['z'].shape)
395-
bubble = np.zeros(gc['z'].shape)
396-
397-
k = np.array(range(0, nx))
398-
399-
zsep = np.zeros(z.shape) # total separation bubble
400-
zsep_new = np.zeros(z.shape) # first-oder separation bubble surface
401-
402-
zfft = np.zeros((ny,nx), dtype=complex)
403-
404-
# Compute bed slope angle in x-dir
405-
dzx[:,:-2] = np.rad2deg(np.arctan((z[:,2:]-z[:,:-2])/(2.*dx)))
406-
dzx[:,-2] = dzx[:,-3]
407-
dzx[:,-1] = dzx[:,-2]
408-
409-
# Determine location of separation bubbles
410-
'''Separation bubble exist if bed slope angle (lee side)
411-
is larger than max angle that wind stream lines can
412-
follow behind an obstacle (mu_b = ..)'''
413-
414-
stall += np.logical_and(abs(dzx) > mu_b, dzx < 0.)
415-
416-
stall[:,1:-1] += np.logical_and(stall[:,1:-1]==0, stall[:,:-2]>0., stall[:,2:]>0.)
417-
418-
# Define separation bubble
419-
bubble[:,:-1] = (stall[:,:-1] == 0.) * (stall[:,1:] > 0.)
420-
421-
# Better solution for cleaner separation bubble, but no working Barchan dune (yet)
422-
p = 1
423-
bubble[:,p:] = bubble[:,:-p]
424-
bubble[:,-p:] = 0
425-
426-
bubble = bubble.astype(int)
427-
428-
# Count separation bubbles
429-
n = np.sum(bubble)
430-
bubble_n = np.asarray(np.where(bubble == True)).T
431-
432-
433-
# Walk through all separation bubbles and determine polynoms
434-
j = 9999
435-
for k in range(0, n):
436-
437-
i = bubble_n[k,1]
438-
j = bubble_n[k,0]
439-
440-
#Bart: check for negative wind direction
441-
if np.sum(gc['taux']) >= 0:
442-
idir = 1
443-
else:
444-
idir = -1
445-
446-
ix_neg = (dzx[j, i+idir*5:] >= 0) # i + 5??
447-
448-
if np.sum(ix_neg) == 0:
449-
zbrink = z[j,i] # z level of brink at z(x0)
450-
else:
451-
zbrink = z[j,i] - z[j,i+idir*5+idir*np.where(ix_neg)[0][0]]
452-
453-
# Better solution and cleaner separation bubble, but no working Barchan dune (yet)
454-
dzdx0 = (z[j,i] - z[j,i-3]) / (3.*dx)
455-
456-
a = dzdx0 / c
457-
ls = np.minimum(np.maximum((3.*zbrink/(2.*c) * (1. + a/4. + a**2/8.)), 0.1), 200.)
458-
459-
a2 = -3 * zbrink/ls**2 - 2 * dzdx0 / ls
460-
a3 = 2 * zbrink/ls**3 + dzdx0 / ls**2
461-
462-
i_max = min(i+int(ls/dx)+1,int(nx-1))
463-
464-
if idir == 1:
465-
xs = x[j,i:i_max] - x[j,i]
466-
else:
467-
xs = -(x[j,i:i_max] - x[j,i])
468-
469-
zsep_new[j,i:i_max] = (a3*xs**3 + a2*xs**2 + dzdx0*xs + z[j,i])
470-
471-
# Choose maximum of bedlevel, previous zseps and new zseps
472-
zsep[j,:] = np.maximum.reduce([z[j,:], zsep[j,:], zsep_new[j,:]])
473-
474-
for filter_iter in range(sep_filter_iterations):
475-
476-
zsep_new = np.zeros(zsep.shape)
477-
478-
Cut = 1.5
479-
dk = 2.0 * np.pi / (np.max(x))
480-
zfft[j,:] = np.fft.fft(zsep[j,:])
481-
zfft[j,:] *= np.exp(-(dk*k*dx)**2/(2.*Cut**2))
482-
zsep_fft = np.real(np.fft.ifft(zfft[j,:]))
483-
484-
if np.sum(ix_neg) == 0:
485-
zbrink = zsep_fft[i]
486-
else:
487-
zbrink = zsep_fft[i] - zsep_fft[i+idir*5+idir*np.where(ix_neg)[0][0]]
488-
489-
# First order polynom
490-
dzdx1 = (zsep_fft[i] - zsep_fft[i-3])/(3.*dx)
491-
492-
a = dzdx1 / c
493-
494-
ls = np.minimum(np.maximum((3.*zbrink/(2.*c) * (1. + a/4. + a**2/8.)), 0.1), 200.)
495-
496-
497-
a2 = -3 * zbrink/ls**2 - 2 * dzdx1 / ls
498-
a3 = 2 * zbrink/ls**3 + dzdx1 / ls**2
499-
500-
i_max1 = min(i+idir*int(ls/dx),int(nx-1))
501-
502-
if idir == 1:
503-
xs1 = x[j,i:i_max1] - x[j,i]
504-
else:
505-
xs1 = -(x[j,i:i_max1] - x[j,i])
506-
507-
zsep_new[j, i:i_max1] = (a3*xs1**3 + a2*xs1**2 + dzdx1*xs1 + zbrink)
508-
509-
# Pick the maximum seperation bubble hieght at all locations
510-
zsep[j,:] = np.maximum.reduce([z[j,:], zsep[j,:], zsep_new[j,:]])
511-
512-
513-
# Smooth surface of separation bubbles over y direction
514-
if zsep_y_filter:
515-
zsep = ndimage.gaussian_filter1d(zsep, sigma=0.2, axis=0)
516-
517-
#Correct for any seperation bubbles that are below the bed surface following smoothing
518-
ilow = zsep < z
519-
zsep[ilow] = z[ilow]
520-
521-
return zsep
522-
523368

524369
def compute_shear(self, u0, nfilter=(1., 2.)):
525370
'''Compute wind shear perturbation for given free-flow wind

aeolis/wind.py

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# package modules
3737
import aeolis.shear
3838
from aeolis.utils import *
39-
39+
from aeolis.separation import compute_separation, set_sep_params
4040

4141
# initialize logger
4242
logger = logging.getLogger(__name__)
@@ -71,6 +71,11 @@ def initialize(s, p):
7171
z0 = calculate_z0(p, s)
7272

7373
if p['process_shear']:
74+
75+
# Get separation parameters based on Perturbation theory settings
76+
if p['process_separation'] and p['sep_auto_tune']:
77+
p = set_sep_params(p, s)
78+
7479
if p['ny'] > 0:
7580
if p['method_shear'] == 'fft':
7681
s['shear'] = aeolis.shear.WindShear(s['x'], s['y'], s['zb'],
@@ -219,10 +224,11 @@ def calculate_z0(p, s):
219224
return z0
220225

221226

227+
# Compute shear velocity field (including separation)
222228
def shear(s,p):
223229

224-
# Compute shear velocity field (including separation)
225230

231+
# Quasi-2D shear model (DUNA (?) approach)
226232
if 'shear' in s.keys() and p['process_shear'] and p['ny'] > 0 and p['method_shear'] == 'duna2d':
227233

228234
shear_params = {'alfa': 3, 'beta': 1, 'c': p['c_b'], 'mu_b': p['mu_b'], 'sep_filter_iterations': p['sep_filter_iterations'], 'zsep_y_filter': p['zsep_y_filter'], 'process_separation': p['process_separation'], 'tau_sep': 0.5, 'slope': 0.2, 'rhoa': p['rhoa'], 'shear_type': p['method_shear']}
@@ -235,6 +241,7 @@ def shear(s,p):
235241

236242
s = stress_velocity(s, p)
237243

244+
# Quasi-2D shear model (1D analytical over 2D grid)
238245
elif 'shear' in s.keys() and p['process_shear'] and p['ny'] > 0 and p['method_shear'] == 'quasi2d':
239246

240247
z0 = calculate_z0(p, s)
@@ -248,36 +255,39 @@ def shear(s,p):
248255
s['taun'] = - s['tau'] * np.cos((-p['alfa'] + s['udir']) / 180. * np.pi)
249256
s = stress_velocity(s, p)
250257

258+
# 2D shear model (FFT approach)
251259
elif 'shear' in s.keys() and p['process_shear'] and p['ny'] > 0:
252-
s['shear'](x=s['x'], y=s['y'], z=s['zb'],
253-
taux=s['taus'], tauy=s['taun'],
254-
u0=s['uw'][0,0], udir=s['udir'][0,0],
255-
process_separation = p['process_separation'],
256-
c = p['c_b'],
257-
mu_b = p['mu_b'],
258-
taus0 = s['taus0'][0,0], taun0 = s['taun0'][0,0],
259-
sep_filter_iterations=p['sep_filter_iterations'],
260-
zsep_y_filter=p['zsep_y_filter'])
261260

261+
# Run shear model
262+
s['shear'](p=p, x=s['x'], y=s['y'], z=s['zb'], u0=s['uw'][0,0], udir=s['udir'][0,0],
263+
taux=s['taus'], tauy=s['taun'], taus0 = s['taus0'][0,0], taun0 = s['taun0'][0,0])
264+
265+
# Get shear stress (tau) from module, compute magnitude and transform to shear velocity
262266
s['taus'], s['taun'] = s['shear'].get_shear()
263267
s['tau'] = np.hypot(s['taus'], s['taun'])
264-
265268
s = stress_velocity(s,p)
266269

267270
# Returns separation surface
268271
if p['process_separation']:
269272
s['hsep'] = s['shear'].get_separation()
270273
s['zsep'] = s['hsep'] + s['zb']
271274

272-
275+
# 1D shear model
273276
elif p['process_shear'] and p['ny'] == 0: #NTC - Added in 1D only capabilities
274277
s = compute_shear1d(s, p)
275278
s = stress_velocity(s, p)
276279

277280
if p['process_separation']:
278-
zsep = separation1d(s, p)
279-
s['zsep'] = zsep
281+
dx = s['ds'][0, 0]
282+
z_bed = s['zb'][0, :] # 1D bed
283+
udir = s['udir'][0, 0] # wind-aligned direction
284+
285+
# Compute separation bubble
286+
zsep = compute_separation(p, z_bed, dx, udir)
287+
s['zsep'] = zsep[np.newaxis, :] # shape back to (1, nx)
280288
s['hsep'] = s['zsep'] - s['zb']
289+
290+
# Compute influence of searation bubble on shear
281291
tau_sep = 0.5
282292
slope = 0.2 # according to Durán 2010 (Sauermann 2001: c = 0.25 for 14 degrees)
283293
delta = 1. / (slope * tau_sep)
@@ -286,23 +296,6 @@ def shear(s,p):
286296
s['taun'] *= zsepdelta
287297
s = stress_velocity(s, p)
288298

289-
# if p['process_nelayer']:
290-
# if p['th_nelayer']:
291-
292-
# ustar = s['ustar'].copy()
293-
# ustars = s['ustars'].copy()
294-
# ustarn = s['ustarn'].copy()
295-
296-
# s['zne'][:,:] = p['ne_file']
297-
298-
# ix = s['zb'] <= s['zne']
299-
# s['ustar'][ix] = np.maximum(0., s['ustar'][ix] - (s['zne'][ix]-s['zb'][ix])* (1/p['layer_thickness']) * s['ustar'][ix])
300-
301-
# ix = ustar != 0.
302-
# s['ustars'][ix] = s['ustar'][ix] * (ustars[ix] / ustar[ix])
303-
# s['ustarn'][ix] = s['ustar'][ix] * (ustarn[ix] / ustar[ix])
304-
305-
306299
return s
307300

308301
def velocity_stress(s, p):

0 commit comments

Comments
 (0)