def verticalVelocity(self, u): x = self.input.v('grid', 'axis', 'x') B = self.input.v('B', x=x).reshape([u.shape[0]] + [1] * (len(u.shape) - 1)) Bx = self.input.d('B', x=x, dim='x').reshape([u.shape[0]] + [1] * (len(u.shape) - 1)) Hx = self.input.d('H', x=x, dim='x').reshape([u.shape[0]] + [1] * (len(u.shape) - 1)) Bux = Bx / B * u + ny.derivative(u, 'x', self.input.slice('grid')) kmax = self.input.v('grid', 'maxIndex', 'z') w = -ny.integrate(Bux, 'z', kmax, np.arange(0, kmax + 1), self.input.slice('grid')) - u[:, -1, None, Ellipsis] * Hx return w
def svarFunction(Kv, F, Fsurf, Fbed, data, hasMatrix=False): """Solve a function Ds - (Kv s_z)_z = -u0_z + F subject to Kv s_z (-H) = Fbed Kv s_z (0) = Fsurf The returned solution has a part 'sCoef' and 'szCoef' for the forcing by u_z and a part 'sForced' and 'szForced' for the forcing by F, Fbed and Fsurf. Args: Kv: (ndarray(jmax+1, kmax+1, fmax+1)) - data on eddy diffusivity or a salinityMatrix as calculated before by this function. F: (ndarray(jmax+1, kmax+1, fmax+1, nRHS)) - interior forcing. nRHS is the number of individual forcing components Fsurf: (ndarray(jmax+1, 1, fmax+1, nRHS)) - surface forcing. nRHS is the number of individual forcing components Fbed: (ndarray(jmax+1, 1, fmax+1, nRHS)) - surface forcing. nRHS is the number of individual forcing components data: (DataContainer) - should at least contain 'grid', 'OMEGA' and 'u0' hasMatrix: (bool) - if True then it is assumed that Kv contains a salinityMatrix as calculated by this function before. The matrix is not computed again, which saves time. Returns: sCoef and szCoef: (ndarray(jmax+1, kmax+1, fmax+1, 1)) the solution s and its vertical derivative for the forcing by u0_z. the final dimension '1' denotes a single forcing component. sForced and szForced: (ndarray(jmax+1, kmax+1, fmax+1, nRHS)) the solution s and its vertical derivative for the other forcings. the solution is separated for each RHS term in F, Fsurf and Fbed. salinityMatrix: matrix used in computation. Can be reused for subsequent calls of this function. """ # Init jmax = data.v('grid', 'maxIndex', 'x') # maximum index of x grid (jmax+1 grid points incl. 0) kmax = data.v('grid', 'maxIndex', 'z') # maximum index of z grid (kmax+1 grid points incl. 0) fmax = data.v('grid', 'maxIndex', 'f') # maximum index of f grid (fmax+1 grid points incl. 0) OMEGA = data.v('OMEGA') uz = data.d('u0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') u0bed = data.v('u0', range(0, jmax+1), [-1], range(0, fmax+1)) ftot = 2*fmax+1 # Determine bandwidth of eddy viscosity matrix bandwidth = 0 for n in np.arange(fmax, -1, -1): if np.any(abs(Kv[:, :, n]) > 0): bandwidth = max(bandwidth, n) # Init Ctd nRHS = F.shape[-1] salinityMatrix = np.empty([jmax+1, 2*ftot+2*bandwidth+1, ftot*(kmax+1)], dtype=complex) szCoef = np.zeros([jmax+1, kmax+1, fmax+1, 1], dtype=complex) szForced = np.zeros([jmax+1, kmax+1, fmax+1, nRHS], dtype=complex) # build, save and solve the velocity matrices in every water column for j in range(0, jmax+1): # dz vectors dz = (data.v('grid', 'axis', 'z')[0, 1:]-data.v('grid', 'axis', 'z')[0, 0:-1])*data.n('H', j) dz = dz.reshape(dz.shape[0]) dz_down = dz[:kmax-1].reshape(kmax-1, 1, 1) dz_up = dz[1:kmax].reshape(kmax-1, 1, 1) dz_av = 0.5*(dz_down+dz_up) ##### LEFT HAND SIDE ##### if not hasMatrix: # Init A = np.zeros([2*ftot+2*bandwidth+1, ftot*(kmax+1)], dtype=complex) N = np.zeros([kmax+1, 2*bandwidth+1, ftot], dtype=complex) # Build eddy viscosity matrix blocks N[:, bandwidth, :] = Kv[j, :, 0].reshape(kmax+1, 1)*np.ones([1, ftot]) for n in range(1, bandwidth+1): N[:, bandwidth+n, :-n] = 0.5*Kv[j, :, n].reshape(kmax+1, 1)*np.ones([1, ftot]) N[:, bandwidth-n, n:] = 0.5*np.conj(Kv[j, :, n]).reshape(kmax+1, 1)*np.ones([1, ftot]) # Build matrix. Discretisation: central for second derivative, central for first derivative # NB. can use general numerical schemes as dz < 0 a = -N[:-2, :, :]/dz_down b = N[1:kmax, :, :]/dz_up+N[1:kmax, :, :]/dz_down c = -N[2:, :, :]/dz_up b[:, bandwidth, :] += (np.arange(-fmax, ftot-fmax)*1j*OMEGA).reshape((1, ftot))*dz_av.reshape((kmax-1, 1)) a = np.swapaxes(a, 0, 1) b = np.swapaxes(b, 0, 1) c = np.swapaxes(c, 0, 1) # Build matrix A[2*ftot:2*ftot+2*bandwidth+1, :-2*ftot] = a.reshape(a.shape[0], a.shape[1]*a.shape[2]) A[2*fmax+1:2*fmax+2*bandwidth+2, ftot:-ftot] = b.reshape(a.shape[0], a.shape[1]*a.shape[2]) A[0:2*bandwidth+1, 2*ftot:] = c.reshape(a.shape[0], a.shape[1]*a.shape[2]) # Boundary conditions # Surface (k=0) A[2*fmax+1:2*fmax+2*bandwidth+2, :ftot] = N[0, :, :] # Bed (k=kmax) A[2*fmax+1:2*fmax+2*bandwidth+2, -ftot:] = N[-1, :, :] # save matrix salinityMatrix[j, Ellipsis] = A[Ellipsis] bandwidthA = bandwidth+ftot else: A = Kv[j, Ellipsis] # if hasMatrix Av replaces the role of the matrix in this equation bandwidthA = (A.shape[0]-1)/2 ##### RIGHT HAND SIDE ##### # Implicit part of the forcing uz0*dz*Sx umat = np.zeros((kmax+1, ftot), dtype=complex) umat[1:-1, fmax:ftot] = -uz[j, 1:-1, :]*dz_av.reshape((kmax-1, 1)) uRHS_implicit = umat.reshape((ftot*(kmax+1), 1)) # Forcing from other factors uRHS = np.zeros([ftot*(kmax+1), nRHS], dtype=complex) uRHS[fmax:ftot, :] = Fsurf[j, 0, :, :] uRHS[-ftot+fmax:, :] = Fbed[j, 0, :, :] F_full = np.concatenate((np.zeros((jmax+1, kmax+1, fmax, nRHS)), F), 2) uRHS[ftot:-ftot, :] = 0.5*(F_full[j, 2:, :, :]-F_full[j, :-2, :, :]).reshape((kmax-1)*ftot, nRHS) ##### SOLVE ##### sz = solve_banded((bandwidthA, bandwidthA), A, np.concatenate((uRHS_implicit, uRHS), 1), overwrite_ab=True, overwrite_b=True) sz = sz.reshape(kmax+1, ftot, 1+nRHS) szCoef[j, :, :, :] = ny.eliminateNegativeFourier(sz[:, :, :1], 1) szForced[j, :, :, :] = ny.eliminateNegativeFourier(sz[:, :, 1:], 1) del sz, uRHS_implicit, uRHS ##### INTEGRATION CONSTANT ##### SIGMASAL = data.v('SIGMASAL') if hasMatrix: KvBed = data.v('Av', range(0, jmax+1), [kmax-1, kmax], range(0, fmax+1))/SIGMASAL # TODO: shift to turbulence model else: KvBed = Kv[:, [kmax-1, kmax], :] Dinv = np.diag((np.arange(0, fmax+1)*1j*OMEGA)) Dinv[0, 0] = np.inf Dinv[range(0, fmax+1), range(0, fmax+1)] = Dinv[range(0, fmax+1), range(0, fmax+1)]**(-1) z = ny.dimensionalAxis(data.slice('grid'), 'z') dzbed = z[:, [-1], 0] - z[:, [-2], 0] sbedCoef = -ny.complexAmplitudeProduct(KvBed[:, [-2], :], szCoef[:, [-2], :, :], 2)/dzbed.reshape(dzbed.shape+(1, 1)) - u0bed.reshape(u0bed.shape+(1,)) sbedCoef = np.dot(Dinv, sbedCoef) sbedCoef = np.rollaxis(sbedCoef, 0, 3) sbedForced = (Fbed-ny.complexAmplitudeProduct(KvBed[:, [-2], :], szForced[:, [-2], :, :], 2))/dzbed.reshape(dzbed.shape+(1, 1)) + F[:, [-1], :, :] sbedForced = np.dot(Dinv, sbedForced) sbedForced = np.rollaxis(sbedForced, 0, 3) ##### INTEGRATION ##### sCoef = ny.integrate(szCoef, 'z', kmax, np.arange(0, kmax+1), data.slice('grid')) + sbedCoef*np.ones((1, kmax+1, 1, 1)) sForced = ny.integrate(szForced, 'z', kmax, np.arange(0, kmax+1), data.slice('grid')) + sbedForced*np.ones((1, kmax+1, 1, 1)) ##### CLOSURE FOR THE DEPTH-AVERAGED TIME-AVERAGED SALINITY VARIATION ##### H = data.v('H', range(0, jmax+1)).reshape((jmax+1, 1, 1, 1)) sCoef[:, :, [0], :] -= (ny.integrate(sCoef[:, :, [0], :], 'z', kmax, 0, data.slice('grid'))/H)*np.ones((1, kmax+1, 1, 1)) sForced[:, :, [0], :] -= (ny.integrate(sForced[:, :, [0], :], 'z', kmax, 0, data.slice('grid'))/H)*np.ones((1, kmax+1, 1, 1)) if hasMatrix: return sCoef, sForced, szCoef, szForced, Kv else: return sCoef, sForced, szCoef, szForced, salinityMatrix
A = np.zeros((jmax + 1, jmax + 1)) ##### LEFT-HAND SIDE ##### x = ny.dimensionalAxis(data.slice('grid'), 'x')[:, 0, 0] dx = x[1:] - x[:-1] A[range(0, jmax), range(0, jmax)] = +AK[:-1] / dx # main diagonal A[range(0, jmax), range(1, jmax + 1)] = Q[:-1] - AK[1:] / dx # upper diagonal # BC closed end A[-1, -1] = -AK[-1] A[-1, 0] = AK[0] ##### RIGHT-HAND SIDE ##### sRHS = np.zeros((jmax + 1, nRHS)) sRHS[:-1, :] = F[:-1, :] sRHS[-1, :] = Q[0] * Fopen - Q[-1] * Fclosed + ny.integrate( F, 'x', 0, jmax, data.slice('grid')) ##### SOLVE ##### Sx = np.zeros((jmax + 1, 1, 1, nRHS)) Sx[:, 0, 0, :] = np.linalg.solve(A, sRHS) ##### INTEGRATE ###### # integrate from back to front to make sure that the landward BC is guaranteed S = ny.integrate(Sx, 'x', jmax, range(0, jmax + 1), data.slice('grid')) + Fclosed ##### CORRECTION ###### # Integration errors may cause the solution to not satisfy the boundary conditions. # By the definition of integration here, the landward boundary condition is satisfied, but the seaward condition is not # Apply a correction that scales the salinity profile #for i in range(0, nRHS):
def growthrate(self, Ceco, init=False): """ Function largely copied from growth function above, returns the growth rate mu for output purposes (IN FUTURE FIND BETTER WAY TO STRUCTURE THIS) """ jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') kp = self.input.v('kp') E0 = self.input.v('E0') HI = self.input.v('HI') mu = {} T = 49 * 25.655 * 3600 # approximate t = np.linspace(0, T, 1000) # Growth rate (Eppley, 1972) Temp = self.input.v('Temp', range(0, jmax + 1), [0], [0]) mu0 = self.input.v('mu00', range(0, jmax + 1), [0], [0]) mu_Eppley = .851 * 1.066**Temp mumax = self.input.v('mu00') * np.log( 2 ) * mu_Eppley # need log(2) as Eppley's number is in doublings/day (not in 1/day) mu['mumax'] = mumax # Limitations: N = Ceco['nitrogen'][:, 0] # Nitrogen. Assume well-mixed Phos = Ceco['phosphorous'][:, 0] # Phosphorous. Assume well-mixed Pprim = -ny.primitive( np.real(Ceco['phytoplankton']), 'z', 0, kmax, self.input.slice('grid')) # primitive counted from surface z = ny.dimensionalAxis(self.input.slice('grid'), 'z')[:, :, 0] # Light - background kbg = self.input.v('kbg') kbg = kbg * z.reshape((jmax + 1, kmax + 1, 1, 1)) # Light - sediment kc = self.input.v('kc') c00 = np.real( self.input.v('c0', range(0, jmax + 1), range(0, kmax + 1), [0], [0])) c00int = ny.integrate(c00, 'z', 0, range(0, kmax + 1), self.input.slice('grid')) c04 = np.real( self.input.v('c0', range(0, jmax + 1), range(0, kmax + 1), [2], [0])) c04int = ny.integrate(c04, 'z', 0, range(0, kmax + 1), self.input.slice('grid')) OMEGA = self.input.v('OMEGA') cint = c00int + c04int * np.cos(2 * OMEGA * t.reshape( (1, 1, 1, len(t)))) kc = (kc * cint) # Light - daily cycle omega_E = self.input.v('omega_E') # in 1/hr dE = np.maximum(np.sin(t * omega_E), 0).reshape((1, 1, 1, len(t))) # Light - self-shading kself = -kp * np.cumsum(Pprim, axis=1).reshape( (jmax + 1, kmax + 1, 1, 1)) # Light - total and decomposition # night dayinds = np.where(dE[0, 0, 0, :] > 0)[0] tau_night = np.float(len(dayinds)) / np.float(len(t)) # day k = kbg + kc + kself alpha_day = np.exp(k[:, :, :, dayinds]) E_day = E0 * dE[:, :, :, dayinds] * alpha_day dE_day = dE[:, :, :, dayinds] FE_day = E_day / np.sqrt(HI**2 + E_day**2) FE = {} FE['daily'] = (1. - dE_day) / (1 - E_day / E0) * FE_day FE['background'] = dE_day * (1. - alpha_day) / (1 - E_day / E0) * ( 1 - np.exp(kbg)) / (3. + 1e-4 - np.exp(kbg) - np.exp( kc[:, :, :, dayinds]) - np.exp(kself)) * FE_day FE['sediment'] = dE_day * (1. - alpha_day) / (1 - E_day / E0) * ( 1 - np.exp(kc[:, :, :, dayinds])) / (3. + 1e-4 - np.exp(kbg) - np.exp(kc[:, :, :, dayinds]) - np.exp(kself)) * FE_day FE['self-shading'] = dE_day * (1. - alpha_day) / (1 - E_day / E0) * ( 1 - np.exp(kself)) / (3. + 1e-4 - np.exp(kbg) - np.exp( kc[:, :, :, dayinds]) - np.exp(kself)) * FE_day # mu['FE']['daily'] = (1.-dE_day)/(4.-dE_day-np.exp(kbg)-np.exp(kc[:, :,:, dayinds])-np.exp(kself))*FE_day # mu['FE']['background'] = (1.-np.exp(kbg))/(4.-dE_day-np.exp(kbg)-np.exp(kc[:, :,:, dayinds])-np.exp(kself))*FE_day # mu['FE']['sediment'] = (1.-np.exp(kc[:, :,:, dayinds]))/(4.-dE_day-np.exp(kbg)-np.exp(kc[:, :,:, dayinds])-np.exp(kself))*FE_day # mu['FE']['self-shading']= (1.-np.exp(kself))/(4.-dE_day-np.exp(kbg)-np.exp(kc[:, :,:, dayinds])-np.exp(kself))*FE_day # N HN = self.input.v('HN') FN = (N / (HN + N)).reshape((jmax + 1, 1, 1, 1)) # Phos HP = self.input.v('HP') FP = (Phos / (HP + Phos)).reshape((jmax + 1, 1, 1, 1)) mu = mumax * tau_night * np.mean( np.minimum(np.minimum(FN, FP), FE_day), axis=-1) return mu, tau_night, mumax, FN, FP, FE
def PgrowthNE(self, Ch, Chprim, Chint, X, init=False): # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') kp = self.input.v('kp') E0 = self.input.v('E0') HI = self.input.v('HI') T = 8 * 25.655 * 3600 # approximate t = np.linspace(0, T, 100) # T = 49*25.655*3600 # approximate # t = np.linspace(0, T, 5000) # Growth rate (Eppley, 1972) Temp = self.input.v('Temp', range(0, jmax + 1), [0]) mu0 = self.input.v('mu00', range(0, jmax + 1), [0]) mu_Eppley = .851 * 1.066**Temp mumax = mu0 * np.log( 2 ) * mu_Eppley # need log(2) as Eppley's number is in doublings/day (not in 1/day) # Limitations: N = Ch[:, 0, 1] * X[:, 1] Phos = Ch[:, 0, 2] * X[:, 2] Ph = Ch[:, :, 0] Phprim = Chprim[:, :, 0] if init: self.G_Pgrowth = {} z = ny.dimensionalAxis(self.input.slice('grid'), 'z')[:, :, 0] # Light - background kbg = self.input.v('kbg') self.kbg = kbg * z.reshape((jmax + 1, kmax + 1, 1)) # Light - sediment kc = self.input.v('kc') c00 = np.real( self.input.v('c0', range(0, jmax + 1), range(0, kmax + 1), [0])) c00int = ny.integrate(c00, 'z', 0, range(0, kmax + 1), self.input.slice('grid')) c04 = np.real( self.input.v('c0', range(0, jmax + 1), range(0, kmax + 1), [2])) c04int = ny.integrate(c04, 'z', 0, range(0, kmax + 1), self.input.slice('grid')) OMEGA = self.input.v('OMEGA') cint = c00int + c04int * np.cos(2 * OMEGA * t.reshape( (1, 1, len(t)))) self.kc = (kc * cint) # Light - daily cycle omega_E = self.input.v('omega_E') # in 1/hr self.E = np.maximum(E0 * np.sin(t / 3600. * omega_E - 1.6), 0).reshape((1, 1, len(t))) if init or kp > 0: # Light - self-shading self.kself = -kp * np.cumsum(Phprim * X[:, [0]], axis=1).reshape( (jmax + 1, kmax + 1, 1)) # Light - total k = self.kbg + self.kc + self.kself E = self.E * np.exp(k) FE = E / np.sqrt(HI**2 + E**2) # N HN = self.input.v('HN') FN = (N / (HN + N)).reshape((jmax + 1, 1, 1)) # Phos HP = self.input.v('HP') FP = (Phos / (HP + Phos)).reshape((jmax + 1, 1, 1)) mu = mumax * np.mean(np.minimum(np.minimum(FN, FP), FE), axis=2) # mu = mumax*np.minimum(np.minimum(np.mean(FN, axis=2), np.mean(FP, axis=2)), np.mean(FE, axis=2)) muint = ny.integrate(mu * Ph, 'z', kmax, 0, self.input.slice('grid'))[:, 0] return {'growth': self.B * muint * X[:, 0]}
def zetaFunctionMassConservative(M, F, Fopen, Fclosed, data, source=None, hasMatrix=False): # Init jmax = data.v('grid', 'maxIndex', 'x') fmax = data.v('grid', 'maxIndex', 'f') OMEGA = data.v('OMEGA') ftot = 2 * fmax + 1 x = data.v('grid', 'axis', 'x') dx = (x[1:] - x[:-1]) * data.v('L') B = data.v('B', x=x[:-1] + .5 * (x[1:] - x[:-1])) # widths between two grid points ##### LEFT HAND SIDE ##### if not hasMatrix: # determine bandwith bandwidth = 0 for n in np.arange(ftot - 1, -1, -1): for m in np.arange(ftot - 1, -1, -1): if any(abs(M[:, 0, n, m]) > 0): bandwidth = max(bandwidth, n - m, -n + m) # init A = np.zeros([2 * ftot + 2 * bandwidth + 1, ftot * (jmax + 1)], dtype=complex) Mdiag = np.zeros([jmax + 1, 2 * bandwidth + 1, ftot], dtype=complex) Mdiag[:, bandwidth, :] = np.diagonal(M[:, 0, :, :], 0, 1, 2) for n in range(1, bandwidth + 1): Mdiag[:, bandwidth + n, :-n] = np.diagonal(M[:, 0, :, :], -n, 1, 2) Mdiag[:, bandwidth - n, n:] = np.diagonal(M[:, 0, :, :], n, 1, 2) # dx vectors Bdx_down = (dx[:jmax - 1] * B[:jmax - 1]).reshape(jmax - 1, 1, 1) Bdx_up = (dx[1:jmax] * B[1:jmax]).reshape(jmax - 1, 1, 1) dx_av = 0.5 * (dx[1:jmax] + dx[:jmax - 1]).reshape(jmax - 1, 1) # Build matrix a = Mdiag[:-2, :, :] / Bdx_down b = -Mdiag[1:-1, :, :] / Bdx_down - Mdiag[1:-1, :, :] / Bdx_up c = Mdiag[2:, :, :] / Bdx_up b[:, bandwidth, :] += (np.arange(-fmax, ftot - fmax) * 1j * OMEGA).reshape(1, ftot) * dx_av a = np.swapaxes(a, 0, 1) b = np.swapaxes(b, 0, 1) c = np.swapaxes(c, 0, 1) A[2 * ftot:2 * ftot + 2 * bandwidth + 1, :-2 * ftot] += a.reshape( a.shape[0], a.shape[1] * a.shape[2]) A[2 * fmax + 1:2 * fmax + 2 * bandwidth + 2, ftot:-ftot] += b.reshape(a.shape[0], a.shape[1] * a.shape[2]) A[:2 * bandwidth + 1, 2 * ftot:] += c.reshape(a.shape[0], a.shape[1] * a.shape[2]) # Boundary conditions # Sea (j=0) b = -Mdiag[0, :, :] / (dx[0] * B[0]) b[bandwidth, :] += 0.5 * np.arange(-fmax, ftot - fmax) * 1j * OMEGA * dx[0] c = Mdiag[1, :, :] / (dx[0] * B[0]) A[2 * fmax + 1:2 * fmax + 2 * bandwidth + 2, :ftot] += b A[:2 * bandwidth + 1, ftot:2 * ftot] += c # Weir (j=jmax) A[2 * fmax + 1:2 * fmax + 2 * bandwidth + 2, -ftot:] += Mdiag[-1, :, :] bandwidth = bandwidth + ftot else: A = M bandwidth = (A.shape[0] - 1) / 2 ##### RIGHT HAND SIDE ##### nRHS = F.shape[-1] zRHS = np.zeros([ftot * (jmax + 1), nRHS], dtype=complex) for i in range(0, nRHS): # forcing on open boundary zRHS[:ftot, i] = -np.arange(-fmax, fmax + 1) * 1j * OMEGA * Fopen[0, 0, :, i] # forcing on closed boundary zRHS[-ftot:, i] = Fclosed[0, 0, :, i] # internal forcing bdx_up = (dx[1:] * B[1:]).reshape(jmax - 1, 1) bdx_down = (dx[:-1] * B[:-1]).reshape(jmax - 1, 1) bdx0 = dx[0] * B[0] zRHS[ftot:-ftot, i] = -((F[2:, 0, :, i] - F[1:-1, 0, :, i]) / bdx_up - (F[1:-1, 0, :, i] - F[:-2, 0, :, i]) / bdx_down).reshape( (jmax - 1) * ftot) if source is not None: zRHS[ftot:-ftot, i] += ( 0.5 * (source[1:-1, 0, :, i] + source[2:, 0, :, i]) - 0.5 * (source[1:-1, 0, :, i] + source[:-2, 0, :, i])).reshape( (jmax - 1) * ftot) zRHS[:ftot, i] += -(F[1, 0, :, i] - F[0, 0, :, i]) / bdx0 ##### SOLVE ##### zetax = solve_banded((bandwidth, bandwidth), A, zRHS, overwrite_ab=False, overwrite_b=True) zetax = zetax.reshape(jmax + 1, 1, ftot, nRHS) # integrate to zeta zeta = integrate(zetax.reshape((jmax + 1, 1, 1, ftot, nRHS)), 'x', 0, np.arange(0, jmax + 1), data.slice('grid'), INTMETHOD='INTERPOLSIMPSON') zeta = zeta.reshape((jmax + 1, 1, ftot, nRHS)) zetaOpenBoundary = Fopen[0, None, 0, None, :, :] * np.ones( [jmax + 1, 1, ftot, nRHS], dtype=complex) zeta += zetaOpenBoundary return zeta, zetax, A
def run(self): """ Returns: Dictionary with results. At least contains the variables listed as output in the registry """ self.logger.info('Running module HydroLead') # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') G = self.input.v('G') ftot = 2 * fmax + 1 submodulesToRun = self.input.v('submodules') # check if the river term should be compensated for by reference level. Only if river is on and non-zero if 'river' in submodulesToRun and self.input.v('Q0') != 0: RiverReferenceCompensation = 1 else: RiverReferenceCompensation = 0 ################################################################################################################ # velocity as function of water level ################################################################################################################ # build, save and solve the velocity matrices in every water column Av = self.input.v('Av', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) F = np.zeros([jmax + 1, kmax + 1, ftot, RiverReferenceCompensation]) Fsurf = np.zeros([jmax + 1, 1, ftot, RiverReferenceCompensation]) Fbed = np.zeros([jmax + 1, 1, ftot, RiverReferenceCompensation]) if RiverReferenceCompensation: # for reference level variation F[:, :, fmax, 0] = -G * self.input.d('R', range(0, jmax + 1), dim='x').reshape( (jmax + 1, 1)) * np.ones((1, kmax + 1)) uCoef, uLead, uzCoef, uzLead, velocityMatrix = uFunctionMomentumConservative( Av, F, Fsurf, Fbed, self.input) ################################################################################################################ # water level ################################################################################################################ ## LHS terms utemp = uCoef.reshape( uCoef.shape[:2] + (1, ) + uCoef.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JuCoef = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuCoef = JuCoef.reshape(jmax + 1, 1, ftot, ftot) # reshape back to original grid BJuCoef = -G * JuCoef * self.input.v('B', np.arange( 0, jmax + 1)).reshape(jmax + 1, 1, 1, 1) # open BC: tide Fopen = np.zeros([1, 1, ftot, len(submodulesToRun)], dtype=complex) if 'tide' in submodulesToRun: Fopen[0, 0, fmax:, submodulesToRun.index('tide')] = ny.amp_phase_input( self.input.v('A0'), self.input.v('phase0'), (fmax + 1, )) # closed BC: river Fclosed = np.zeros([1, 1, ftot, len(submodulesToRun)], dtype=complex) if RiverReferenceCompensation: Fclosed[0, 0, fmax, submodulesToRun.index('river')] = -self.input.v('Q0') ## RHS terms IntForce = np.zeros( [jmax + 1, 1, ftot, len(submodulesToRun)], dtype=complex) if RiverReferenceCompensation: utemp = uLead.reshape( uLead.shape[:2] + (1, ) + uLead.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JuLead = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuLead = JuLead.reshape(jmax + 1, 1, ftot) * self.input.v( 'B', np.arange(0, jmax + 1)).reshape( jmax + 1, 1, 1) # reshape back to original grid IntForce[:, :, :, submodulesToRun.index('river')] = JuLead[:, :, :] Fclosed[0, 0, :, submodulesToRun.index('river')] += -JuLead[jmax, 0, :] ## Solve equation zetaCoef, zetaxCoef, zetaMatrix = zetaFunctionMassConservative( BJuCoef, IntForce, Fopen, Fclosed, self.input) zetax = ny.eliminateNegativeFourier(zetaxCoef, 2) zeta = ny.eliminateNegativeFourier(zetaCoef, 2) ################################################################################################################ # velocity ################################################################################################################ u = np.empty((jmax + 1, kmax + 1, ftot, len(submodulesToRun)), dtype=uCoef.dtype) uz = np.empty((jmax + 1, kmax + 1, ftot, len(submodulesToRun)), dtype=uCoef.dtype) for j in range(0, jmax + 1): u[j, :, :, :] = np.dot(uCoef[j, :, :, :], -G * zetaxCoef[j, 0, :, :]) uz[j, :, :, :] = np.dot(uzCoef[j, :, :, :], -G * zetaxCoef[j, 0, :, :]) if RiverReferenceCompensation: u[:, :, :, submodulesToRun.index('river')] += uLead[:, :, :, 0] uz[:, :, :, submodulesToRun.index('river')] += uzLead[:, :, :, 0] u = ny.eliminateNegativeFourier(u, 2) uz = ny.eliminateNegativeFourier(uz, 2) ################################################################################################################ # vertical velocity ################################################################################################################ w = self.verticalVelocity(u) ################################################################################################################ # Make final dictionary to return ################################################################################################################ d = {} d['velocityMatrix'] = velocityMatrix d['zetaMatrix'] = zetaMatrix d['zeta0'] = {} d['u0'] = {} d['w0'] = {} for i, submod in enumerate(submodulesToRun): nf = ny.functionTemplates.NumericalFunctionWrapper( zeta[:, :, :, i], self.input.slice('grid')) nf.addDerivative(zetax[:, :, :, i], 'x') d['zeta0'][submod] = nf.function nfu = ny.functionTemplates.NumericalFunctionWrapper( u[:, :, :, i], self.input.slice('grid')) nfu.addDerivative(uz[:, :, :, i], 'z') d['u0'][submod] = nfu.function d['w0'][submod] = w[:, :, :, i] return d
def run(self): self.logger.info('Running module SalinityFirst') # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') SIGMASAL = self.input.v('SIGMASAL') OMEGA = self.input.v('OMEGA') submodulesToRun = self.input.v('submodules') sx0 = self.input.d('s0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') u0 = self.input.v('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) s1var = self.input.v('s1var', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) zeta0 = self.input.v('zeta0', range(0, jmax + 1), [0], range(0, fmax + 1)) H = self.input.v('H', range(0, jmax + 1)) B = self.input.v('B', range(0, jmax + 1)) AKh = self.input.v('Kh', range(0, jmax + 1)) * B * H ################################################################################################################ # Second-order salinity variation as function of first-order salinity closure ################################################################################################################ # build, save and solve the velocity matrices in every water column # LHS Kv = self.input.v('salinityMatrix') hasMatrix = True if Kv is None: Kv = self.input.v('Av', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) / SIGMASAL hasMatrix = False # RHS # Allow for three submodules: advection, diffusion and nostress nRHS = len(self.input.getKeysOf('u1')) + 3 f_index = -1 f_names = [] F = np.zeros([jmax + 1, kmax + 1, fmax + 1, nRHS], dtype=complex) Fsurf = np.zeros([jmax + 1, 1, fmax + 1, nRHS], dtype=complex) Fbed = np.zeros([jmax + 1, 1, fmax + 1, nRHS], dtype=complex) if 'advection' in submodulesToRun: # advection by u0*sx1 sx1var = self.input.d('s1var', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') sz1var = self.input.d('s1var', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='z') w0 = self.input.v('w0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) f_index += 1 f_names.append(['advection', 'u0-s1']) F[:, :, :, f_index] = -ny.complexAmplitudeProduct( u0, sx1var, 2) - ny.complexAmplitudeProduct(w0, sz1var, 2) del sx1var, sz1var, w0 # advection by u1*sx0 submods = self.input.getKeysOf('u1') for mod in submods: u1 = self.input.v('u1', mod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) f_index += 1 f_names.append(['advection', 'u1_' + mod + '-s0']) F[:, :, :, f_index] = -ny.complexAmplitudeProduct(u1, sx0, 2) del u1 if 'diffusion' in submodulesToRun: f_index += 1 f_names.append(['diffusion', 's0']) F[:, :, 0, f_index] = (ny.derivative(AKh * sx0[:, 0, 0], 'x', self.input.slice('grid')) / (B * H)).reshape((jmax + 1, 1)) * np.ones( (1, kmax + 1)) if 'nostress' in submodulesToRun: D = (np.arange(0, fmax + 1) * 1j * OMEGA).reshape( (1, 1, fmax + 1)) * np.ones((jmax + 1, 1, 1)) Kvsz1z = D * s1var[:, [0], :] + ny.complexAmplitudeProduct( u0[:, [0], :], sx0[:, [0], :], 2) f_index += 1 f_names.append(['nostress', 's1-zeta0']) Fsurf[:, 0, :, f_index] = -ny.complexAmplitudeProduct( Kvsz1z, zeta0, 2).reshape((jmax + 1, fmax + 1)) del D, Kvsz1z sCoef, sForced, szCoef, szForced, salinityMatrix = svarFunction( Kv, F, Fsurf, Fbed, self.input, hasMatrix=hasMatrix) del Kv ################################################################################################################ # First-order salinity closure ################################################################################################################ ## LHS terms # First-order river discharge Q = -self.input.v('Q1', range(0, jmax + 1)) # Diffusion coefficient us = ny.complexAmplitudeProduct(u0, sCoef, 2)[:, :, 0, 0] # subtidal part of u*s us = ny.integrate(us, 'z', kmax, 0, self.input.slice('grid')).reshape(jmax + 1) AK = np.real(AKh) - np.real(B * us) del us ## RHS terms nRHS_clo = nRHS + len(self.input.getKeysOf('u1')) + 3 f_index_clo = -1 f_names_clo = [] F = np.zeros([jmax + 1, nRHS_clo]) Fopen = np.zeros([1, nRHS_clo]) Fclosed = np.zeros([1, nRHS_clo]) if 'advection' in submodulesToRun: # advection by u0*s2 us = ny.complexAmplitudeProduct(u0, sForced, 2)[:, :, [0], :] us = ny.integrate(us, 'z', kmax, 0, self.input.slice('grid')).reshape( jmax + 1, nRHS) for i in range(0, nRHS): f_index_clo += 1 f_names_clo.append(['advection', 'u0-s2_' + f_names[i][0]]) F[:, f_index_clo] = -ny.derivative(np.real(B * us[:, i]), 'x', self.input.slice('grid')) # advection by u1*s1 submods = self.input.getKeysOf('u1') for mod in submods: u1 = self.input.v('u1', mod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) us = ny.complexAmplitudeProduct(u1, s1var, 2)[:, :, 0] us = ny.integrate(us, 'z', kmax, 0, self.input.slice('grid')).reshape(jmax + 1) f_index_clo += 1 f_names_clo.append(['advection', 'u1_' + mod + '-s1']) F[:, f_index_clo] = -ny.derivative(np.real(B * us), 'x', self.input.slice('grid')) del u1 # surface term us = ny.complexAmplitudeProduct(u0[:, [0], :], s1var[:, [0], :], 2) us = ny.complexAmplitudeProduct(us[:, [0], :], zeta0, 2)[:, 0, 0] f_index_clo += 1 f_names_clo.append(['advection', 'surface']) F[:, f_index_clo] = -ny.derivative(np.real(B * us), 'x', self.input.slice('grid')) del us if 'diffusion' in submodulesToRun: # Bed terms Hx = self.input.d('H', range(0, jmax + 1), dim='x') sx1var = self.input.d('s1var', range(0, jmax + 1), kmax, 0, dim='x') f_index_clo += 1 f_names_clo.append(['diffusion', 'bedslope']) F[:, f_index_clo] = - ny.derivative(np.real(AKh*s1var[:, -1, 0]*Hx), 'x', self.input.slice('grid'))/H \ - np.real(AKh/H*sx1var*Hx) # Surface term f_index_clo += 1 f_names_clo.append(['diffusion', 'surface']) F[:, f_index_clo] = np.real(zeta0[:, 0, 0]) * ny.derivative( np.real(AKh * sx0[:, 0, 0]), 'x', self.input.slice('grid')) / H del sx1var, Hx ## Solve equation S1 = np.zeros((jmax + 1, 1, fmax + 1, nRHS_clo)) Sx1 = np.zeros((jmax + 1, 1, fmax + 1, nRHS_clo)) S1[:, 0, 0, :], Sx1[:, 0, 0, :] = sclosureFunction((Q, AK), F, Fopen, Fclosed, self.input) ################################################################################################################ # Second-order salinity variation ################################################################################################################ s2 = np.zeros((jmax + 1, kmax + 1, fmax + 1, nRHS + 1), dtype=complex) sz2 = np.zeros((jmax + 1, kmax + 1, fmax + 1, nRHS + 1), dtype=complex) s2[:, :, :, :-1] = sForced sz2[:, :, :, :-1] = szForced f_names.append(['advection', 'u0-S1']) s2[:, :, :, -1] = np.sum(ny.complexAmplitudeProduct(sCoef, Sx1, 2), 3) sz2[:, :, :, -1] = np.sum(ny.complexAmplitudeProduct(szCoef, Sx1, 2), 3) ################################################################################################################ # Make final dictionary to return ################################################################################################################ d = {} d['salinityMatrix'] = salinityMatrix d['s1'] = {} d['s2var'] = {} for submod in submodulesToRun: if submod in zip(*f_names_clo)[0]: d['s1'][submod] = {} if submod in zip(*f_names)[0]: d['s2var'][submod] = {} # if submod in zip(*f_names)[0]: for i, mod in enumerate(f_names_clo): nf = ny.functionTemplates.NumericalFunctionWrapper( S1[:, :, :, i], self.input.slice('grid')) nf.addDerivative(Sx1[:, :, :, i], 'x') if len(mod) == 1: d['s1'][mod[0]] = nf.function if len(mod) == 2: d['s1'][mod[0]][mod[1]] = nf.function for i, mod in enumerate(f_names): nf2 = ny.functionTemplates.NumericalFunctionWrapper( s2[:, :, :, i], self.input.slice('grid')) nf2.addDerivative(sz2[:, :, :, i], 'z') if len(mod) == 1: d['s2var'][mod[0]] = nf2.function if len(mod) == 2: d['s2var'][mod[0]][mod[1]] = nf2.function return d
def run(self): ################################################################################################################ ## 1. Init ################################################################################################################ # self.timers[0].tic() ## prepare output message self.logger.info('Running MAW turbulence model') denstr = '' if self.betac ==0: denstr = '- not including density effects' self.logger.info('\tMAW rel. difference in Av in last iteration: %s %s' % (self.difference, denstr)) d = {} jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') G = self.input.v('G') rho0 = self.input.v('RHO0') uzmin = self.input.v('uzmin') Avold = self.input.v('Av', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) Kvold = self.input.v('Kv', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) sfold = self.input.v('Roughness', range(0, jmax+1), 0, 0) # self.timers[0].toc() ################################################################################################################ ## 2. KEFitted run ################################################################################################################ # self.timers[1].tic() d.update(self.kem.run()) self.input.merge(d) # load data resulting from KEFitted model Avmid = self.input.v('Av', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) Kvmid = Avmid/self.input.v('sigma_rho', range(0, jmax+1), range(0, kmax+1), [0]) sfmid = self.input.v('Roughness', range(0, jmax+1), 0, 0) # self.timers[1].toc() ################################################################################################################ ## 3. Density effects ################################################################################################################ if self.betac == 0: # no density effect included, first let KEFitted spin up Av0 = Avmid[:, :, 0] Kv0 = Kvmid[:, :, 0] sf = sfmid Cd = 1. MA_av = 1. MA_kv = 1. Ri = 0. else: ## Load data # self.timers[2].tic() cz = self.input.d('c0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') + self.input.d('c1', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') + self.input.d('c2', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') uz = self.input.d('u0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') + self.input.d('u1', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='z') zeta0 = self.input.v('zeta0', range(0, jmax+1), [0], range(0, fmax+1)) H = self.input.v('grid', 'low', 'z', range(0, jmax+1)) - self.input.v('grid', 'high', 'z', range(0, jmax+1)) # self.timers[2].tic() ## Convert to time series # self.timers[3].tic() cz = ny.invfft2(cz, 2, 90) uz = ny.invfft2(uz, 2, 90) zeta0 = ny.invfft2(zeta0, 2, 90) Avmid = ny.invfft2(Avmid, 2, 90) Kvmid = ny.invfft2(Kvmid, 2, 90) # self.timers[3].toc() # self.timers[4].tic() uzmin = np.ones(uz.shape)*uzmin ## Compute Richardson number Ri = -G*self.betac/rho0*cz/(uz**2+uzmin**2) Rida = 1./H.reshape((jmax+1, 1, 1))*ny.integrate(Ri.reshape((jmax+1, kmax+1, 1, Ri.shape[-1])), 'z', kmax, 0, self.input.slice('grid')).reshape((jmax+1, 1, Ri.shape[-1])) # depth-average Rida += zeta0*(Ri[:, [0], :]-Rida)/H.reshape((jmax+1, 1, 1)) # depth average continued Rida0 = np.maximum(Rida, 0.) # only accept positive Ri Ribed = np.maximum(Ri[:, [-1], :], 0.) # only accept positive Ri Ribedmax = self.input.v('Ribedmax') Ribed = np.minimum(Ribed, Ribedmax) # 5-3-2018 limit near-bed Ri ## relaxation on Rida if hasattr(self, 'Rida'): # Relaxation of Ri_da using previously saved value dRida = self.Rida - Rida0 Rida = np.max((Rida0, np.min((dRida * (1 - self.RELAX), dRida * (1. + self.RELAX)), axis=0) + Rida0), axis=0) Rida = np.min((Rida, np.max((dRida * (1 - self.RELAX), dRida * (1. + self.RELAX)), axis=0) + Rida0), axis=0) self.Rida = Rida else: # No value saved if the init found an available Ri. Then start with full signal computed here (do not use saved value, as this has been truncated to frequency components) Rida = Rida0 # self.timers[4].toc() ## Compute damping functions # self.timers[5].tic() # Av MA_av = (1+10*Rida)**(-0.5) dAv = ny.fft(Avmid*MA_av, 2)[:, :, :fmax+1] - Avold Av = Avold + (1-self.RELAX)*(self.LOCAL*dAv + .5*(1-self.LOCAL)*dAv[[0]+range(0, jmax), :, :] + .5*(1-self.LOCAL)*dAv[range(1, jmax+1)+[jmax], :, :]) # Av = Avold + dAv Av0 = Av[:, :, 0] # Kv MA_kv = (1+3.33*Rida)**(-1.5) dKv = ny.fft(Kvmid*MA_kv, 2)[:, :, :fmax+1] - Kvold Kv = Kvold + (1-self.RELAX)*(self.LOCAL*dKv + .5*(1-self.LOCAL)*dKv[[0]+range(0, jmax), :, :] + .5*(1-self.LOCAL)*dKv[range(1, jmax+1)+[jmax], :, :]) # Kv = Kvold + dKv Kv0 = Kv[:, :, 0] # Sf Rfmean = np.mean(Ribed[:, 0, :]*(Kvmid*MA_kv)[:, 0, :]/(Avmid*MA_av)[:, 0, :], axis=-1) Cd = (1+5.5*Rfmean)**-2. damp_sf = Cd sf = sfmid*damp_sf dsf = sf - sfold sf = sfold + (1-self.RELAX)*(self.LOCAL*dsf + .5*(1-self.LOCAL)*dsf[[0]+range(0, jmax)] + .5*(1-self.LOCAL)*dsf[range(1, jmax+1)+[jmax]]) # self.timers[5].toc() # process for output MA_av = ny.fft(MA_av, 2)[:, :, :fmax+1] MA_kv = ny.fft(MA_kv, 2)[:, :, :fmax+1] Ri = ny.fft(Ri, 2)[:, :, :fmax+1] ################################################################################################################ ## Reference level ################################################################################################################ if self.referenceLevel == 'True': self.input.merge({'Av': Av0, 'Roughness': sf}) d['R'] = self.kem.RL.run()['R'] ################################################################################################################ ## Compute difference ################################################################################################################ Av0s = ny.savitzky_golay(Av0[:, 0], self.filterlength, 1) difference = np.max(abs(Av0s-Avold[:, 0, 0])/abs(Av0s+10**-4)) self.difference = copy.copy(difference) ############################################################################################################### # DEBUG plots ############################################################################################################### # import matplotlib.pyplot as plt # import step as st # x = ny.dimensionalAxis(self.input.slice('grid'), 'x')[:, 0,0] # # if self.betac > 0 and np.mod(self.iteration, 3)==0: # and self.difference>0.15: # st.configure() # plt.figure(1, figsize=(2,2)) # plt.subplot(1,2,1) # plt.plot(x/1000., Avold[:, 0, 0], label='old') # plt.plot(x/1000., Av0[:, 0], label='new') # plt.ylim(0, np.maximum(np.max(Av0[:, 0]), np.max(Avold[:, 0, 0]))) # plt.legend() # # plt.subplot(1,2,2) # plt.plot(x/1000., Avold[:, 0, 0]-Av0[:, 0]) # plt.twinx() # plt.plot(x/1000., self.input.v('f', range(0, jmax+1)), color='grey') # plt.ylim(0, 1.) # # st.save('plot_'+str(len(x))+'_'+str(self.iteration)) # # plt.figure(2, figsize=(1, 2)) # ws = self.input.v('ws0', range(0, jmax+1), 0, 0) # plt.plot(x/1000., ws) # # st.save('ws_'+str(len(x))+'_'+str(self.iteration)) ################################################################################################################ ## Prepare Output ################################################################################################################ # self.timers[6].tic() x = ny.dimensionalAxis(self.input.slice('grid'),'x')[:, 0,0] nf = ny.functionTemplates.NumericalFunctionWrapper(ny.savitzky_golay(Av0[:, 0], self.filterlength, 1).reshape((jmax+1, 1)), self.input.slice('grid')) nf.addDerivative(ny.savitzky_golay(ny.derivative(Av0[:, 0], 'x', self.input), self.filterlength, 1).reshape((jmax+1, 1)), 'x') nf.addDerivative(ny.savitzky_golay(ny.secondDerivative(Av0[:, 0], 'x', self.input), self.filterlength, 1).reshape((jmax+1, 1)), 'xx') d['Av'] = nf.function nf2 = ny.functionTemplates.NumericalFunctionWrapper(ny.savitzky_golay(Kv0[:, 0], self.filterlength, 1).reshape((jmax+1, 1)), self.input.slice('grid')) nf2.addDerivative(ny.savitzky_golay(ny.derivative(Kv0[:, 0], 'x', self.input), self.filterlength, 1).reshape((jmax+1, 1)), 'x') nf2.addDerivative(ny.savitzky_golay(ny.secondDerivative(Kv0[:, 0], 'x', self.input), self.filterlength, 1).reshape((jmax+1, 1)), 'xx') d['Kv'] = nf2.function d['Roughness'] = sf d['skin_friction'] = sfmid d['dampingFunctions'] = {} d['dampingFunctions']['Roughness'] = Cd d['dampingFunctions']['Av'] = MA_av d['dampingFunctions']['Kv'] = MA_kv d['Ri'] = Ri # self.timers[6].toc() ## Timers # self.timers[0].disp('0 init MAW') # self.timers[1].disp('1 KEFitted') # self.timers[2].disp('2 load data') # self.timers[3].disp('3 invfft') # self.timers[4].disp('4 Ri') # self.timers[5].disp('5 Compute Av, Kv, sf') # self.timers[6].disp('6 Load in dict') return d
def run(self): self.logger.info('Running module SalinityLead') # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') SIGMASAL = self.input.v('SIGMASAL') ################################################################################################################ # First-order salinity variation as function of leading-order salinity closure ################################################################################################################ # build, save and solve the velocity matrices in every water column Kv = self.input.v('Av', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1))/SIGMASAL F = np.zeros([jmax+1, kmax+1, fmax+1, 0]) Fsurf = np.zeros([jmax+1, 1, fmax+1, 0]) Fbed = np.zeros([jmax+1, 1, fmax+1, 0]) sCoef, _, szCoef, _, salinityMatrix = svarFunction(Kv, F, Fsurf, Fbed, self.input) ################################################################################################################ # Leading-order salinity closure ################################################################################################################ ## LHS terms # First-order river discharge Q = -self.input.v('Q1', range(0, jmax+1)) # First-order river discharge or zero if not available # u1riv = self.input.v('u1', 'river', range(0, jmax+1), range(0, kmax+1)) # B = self.input.v('B', range(0, jmax+1)) # if u1riv is None: # Q = np.zeros((jmax+1)) # self.logger.warning('No first-order river discharge found in module SalinityLead') # else: # Q = ny.integrate(u1riv, 'z', kmax, 0, self.input.slice('grid')) # Q = Q*B # del u1riv # Diffusion coefficient # part 1) diffusive transport H = self.input.v('H', range(0, jmax+1)) B = self.input.v('B', range(0, jmax+1)) Kh = self.input.v('Kh', range(0, jmax+1)) AK = np.real(H*B*Kh) # part 2) advective transport u0 = self.input.v('u0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) us = ny.complexAmplitudeProduct(u0, sCoef, 2)[:, :, 0, 0] # subtidal part of u*s us = ny.integrate(us, 'z', kmax, 0, self.input.slice('grid')).reshape(jmax+1) AK += -np.real(B*us) del us, u0, H, B ## RHS terms F = np.zeros([jmax+1, 1]) # dimensions (jmax+1, NumberOfForcings) # open BC: effect of the sea Fopen = np.zeros([1, 1]) Fopen[0, 0] = self.input.v('ssea') # closed BC: always zero. This is by assumption that the salinity vanishes for x=L Fclosed = np.zeros([1, 1]) ## Solve equation S0 = np.zeros((jmax+1, 1, fmax+1, 1)) Sx0 = np.zeros((jmax+1, 1, fmax+1, 1)) S0[:, 0, 0, :], Sx0[:, 0, 0, :] = sclosureFunction((Q, AK), F, Fopen, Fclosed, self.input) ################################################################################################################ # First-order salinity variation ################################################################################################################ s1 = ny.complexAmplitudeProduct(sCoef, Sx0, 2) sz1 = ny.complexAmplitudeProduct(szCoef, Sx0, 2) ################################################################################################################ # Make final dictionary to return ################################################################################################################ d = {} d['salinityMatrix'] = salinityMatrix nf = ny.functionTemplates.NumericalFunctionWrapper(S0[:, :, :, 0], self.input.slice('grid')) nf.addDerivative(Sx0[:, :, :, 0], 'x') d['s0'] = nf.function nf2 = ny.functionTemplates.NumericalFunctionWrapper(s1[:, :, :, 0], self.input.slice('grid')) nf2.addDerivative(sz1[:, :, :, 0], 'z') d['s1var'] = nf2.function return d
def run(self): ################################################################################################################ ## Get data ################################################################################################################ # grid sizes fmax = self.input.v('grid', 'maxIndex', 'f') kmax = self.input.v('grid', 'maxIndex', 'z') jmax = self.input.v('grid', 'maxIndex', 'x') # grid axes, dimensionless and with dimension x = self.input.v('grid', 'axis', 'x') # dimensionless x axis between 0 and 1 (jmax+1) z = self.input.v('grid', 'axis', 'z', 0) # dimensionless z axis between 0 and 1 (kmax+1) x_km = ny.dimensionalAxis(self.input.slice('grid'), 'x', x=x, z=0, f=0) # x axis in m between 0 and L (jmax+1) L = self.input.v('grid', 'high', 'x') # length in m (1) B = self.input.v('B', x=x, z=[0], f=[0]) # width (jmax+1, 1, 1) # variables Av = self.input.v('Av', x=x, z=0.5, f=range(0, fmax + 1)) # Eddy viscosity (jmax+1, fmax+1) Roughness = self.input.v('Roughness', x=x, z=0, f=range( 0, fmax + 1)) # Partial slip coefficient (jmax+1, fmax+1) zeta = self.input.v('zeta0', x=x, z=0, f=range( 0, fmax + 1)) + self.input.v( 'zeta1', x=x, z=0, f=range( 0, fmax + 1)) # water level (jmax+1, fmax+1) u = self.input.v('u0', x=x, z=z, f=range(0, fmax + 1)) + self.input.v( 'u1', x=x, z=z, f=range( 0, fmax + 1)) # horizontal velocity (jmax+1, kmax+1, fmax+1) c = self.input.v('c0', x=x, z=z, f=range(0, fmax + 1)) + self.input.v( 'c1', x=x, z=z, f=range(0, fmax + 1)) + self.input.v( 'c2', x=x, z=z, f=range( 0, fmax + 1)) # concentration (jmax+1, kmax+1, fmax+1) StotalB = ny.integrate( ny.integrate(B * c, 'z', kmax, 0, self.input.slice('grid')), 'x', 0, jmax, self.input.slice('grid')) # compute total sediment stock print 'Total sediment stock in domain (mln kg): ' + str( np.real(StotalB[0, 0, 0]) / 1.e6) # use data from measurements measurementset = self.input.v('measurementset') x_waterlevel = self.input.v(measurementset, 'x_waterlevel') x_velocity = self.input.v(measurementset, 'x_velocity') zeta_meas = self.input.v(measurementset, 'zeta', x=x_waterlevel / L, z=0, f=range(0, 3)) ucomp_meas = self.input.v(measurementset, 'u_comp', x=x_velocity / L, z=0, f=range(0, 3)) ################################################################################################################ ## Plot ################################################################################################################ st.configure() # Figure 1 - Water level amplitude plt.figure(1, figsize=(1, 2)) plt.subplot2grid((1, 8), (0, 0), colspan=7) for n in range(0, 3): if n == 0: p = plt.plot(x_km / 1000., abs(zeta[:, n] + self.input.v('R', range(0, jmax + 1))), label='$M_' + str(2 * n) + '$') else: p = plt.plot(x_km / 1000., abs(zeta[:, n]), label='$M_' + str(2 * n) + '$') plt.plot(x_waterlevel / 1000., abs(zeta_meas[:, n]), 'o', color=p[0].get_color()) plt.ylabel('$|\hat{\zeta}|$ $(m)$') plt.xlabel('x (km)') plt.legend(bbox_to_anchor=(1.15, 1.05)) plt.xlim(np.min(x_km / 1000.), np.max(x_km / 1000.)) plt.title('Water level amplitude') # Figure 2 - Water level phase plt.figure(2, figsize=(1, 2)) plt.subplot2grid((1, 8), (0, 0), colspan=7) for n in range(0, 3): p = plt.plot(x_km / 1000., -np.angle(zeta[:, n]) * 180 / np.pi, label='$M_' + str(2 * n) + '$') if n == 1 or n == 2: plt.plot(x_waterlevel / 1000., -np.angle(zeta_meas[:, n]) * 180 / np.pi, 'o', color=p[0].get_color()) plt.ylabel('$\phi(\hat{\zeta})$ $(deg)$') plt.xlabel('x (km)') plt.ylim(-180, 180) plt.legend(bbox_to_anchor=(1.15, 1.05)) plt.xlim(np.min(x_km / 1000.), np.max(x_km / 1000.)) plt.title('Water level phase') # Figure 3 - Velocity amplitude plt.figure(3, figsize=(1, 2)) plt.subplot2grid((1, 8), (0, 0), colspan=7) # velocity components for n in range(0, 3): p = plt.plot(x_km / 1000., abs(u[:, 0, n]), label='$M_' + str(2 * n) + '$') plt.plot(x_velocity / 1000., abs(ucomp_meas[:, n]), 'o', color=p[0].get_color()) plt.ylabel('$|\hat{u}|$ $(m/s)$') plt.xlabel('x (km)') plt.title('Surface velocity amplitude') plt.xlim(np.min(x_km / 1000.), np.max(x_km / 1000.)) plt.legend(bbox_to_anchor=(1.15, 1.05)) # Figure 4 - Roughness, Eddy viscosity plt.figure(4, figsize=(1, 2)) plt.subplot(1, 2, 1) for n in range(0, fmax + 1): plt.plot(x_km / 1000, abs(Av[:, n])) plt.ylabel(r'$|\hat{A}_{\nu}|$ $(m^2/s)$') plt.xlabel('x (km)') plt.subplot(1, 2, 2) for n in range(0, fmax + 1): plt.plot(x_km / 1000, abs(Roughness[:, n]), label='$M_' + str(2 * n) + '$') plt.ylabel(r'$|\hat{s}_{f}|$ $(m/s)$') plt.xlabel('x (km)') # Figure 5 - surface concentration plt.figure(5, figsize=(1, 2)) for n in range(0, fmax + 1): p = plt.plot(x_km / 1000., abs(c[:, 0, n])) plt.xlabel('x (km)') plt.ylabel('|c| $(kg/m^3)$') plt.title('Surface sediment concentration') # Figure 6 - availability plt.figure(11, figsize=(1, 1)) a = self.input.v('a', range(0, jmax + 1), 0, 0) if self.input.v('a') is not None: plt.plot(x_km / 1000., a) plt.legend() plt.xlabel('x (km)') plt.ylabel('a') plt.title('sediment availability') st.show() return {}
def run(self): self.logger.info('Running module StaticAvailability') ################################################################################################################ ## Init ################################################################################################################ jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') c0 = self.input.v('hatc0', 'a', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) c1_a0 = self.input.v('hatc1', 'a', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) c1_a0x = self.input.v('hatc1', 'ax', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) if isinstance(c1_a0x, bool): c1_a0x = np.zeros((jmax + 1, kmax + 1, fmax + 1)) d = {} c0_int = ny.integrate(c0, 'z', kmax, 0, self.input.slice('grid')) B = self.input.v('B', range(0, jmax + 1), [0], [0]) u0 = self.input.v('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) zeta0 = self.input.v('zeta0', range(0, jmax + 1), [0], range(0, fmax + 1)) Kh = self.input.v('Kh', range(0, jmax + 1), [0], [0]) ################################################################################################################ ## Second order closure ################################################################################################################ u1 = self.input.v('u1', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) d['T'] = {} d['F'] = {} T0 = 0 F0 = 0 ## Transport T ############################################################################################ ## T.1. - u0*c1_a0 # Total c1a_f0 = c1_a0 T0 += ny.integrate(ny.complexAmplitudeProduct(u0, c1a_f0, 2), 'z', kmax, 0, self.input.slice('grid')) # Decomposition for submod in self.input.getKeysOf('hatc1', 'a'): if submod == 'erosion': for subsubmod in self.input.getKeysOf('hatc1', 'a', 'erosion'): c1_a0_comp = self.input.v('hatc1', 'a', submod, subsubmod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) c1a_f0_comp_res = c1_a0_comp d['T'] = self.dictExpand( d['T'], subsubmod, ['TM' + str(2 * n) for n in range(0, fmax + 1) ]) # add submod index to dict if not already # transport with residual availability for n in range(0, fmax + 1): tmp = np.zeros(c1a_f0_comp_res.shape, dtype=complex) tmp[:, :, n] = c1a_f0_comp_res[:, :, n] tmp = ny.integrate( ny.complexAmplitudeProduct(u0, tmp, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T'][subsubmod]['TM' + str(2 * n)] += tmp else: c1_a0_comp = self.input.v('hatc1', 'a', submod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) c1a_f0_comp_res = c1_a0_comp d['T'] = self.dictExpand( d['T'], submod, ['TM' + str(2 * n) for n in range(0, fmax + 1) ]) # add submod index to dict if not already # transport with residual availability for n in range(0, fmax + 1): tmp = np.zeros(c1a_f0_comp_res.shape, dtype=complex) tmp[:, :, n] = c1a_f0_comp_res[:, :, n] tmp = ny.integrate(ny.complexAmplitudeProduct(u0, tmp, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T'][submod]['TM' + str(2 * n)] += tmp ## T.2. - u1*c0 # Total T0 += ny.integrate(ny.complexAmplitudeProduct(u1, c0, 2), 'z', kmax, 0, self.input.slice('grid')) # Decomposition for submod in self.input.getKeysOf('u1'): u1_comp = self.input.v('u1', submod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) d['T'] = self.dictExpand( d['T'], submod, ['TM' + str(2 * n) for n in range(0, fmax + 1) ]) # add submod index to dict if not already # transport with residual availability for n in range(0, fmax + 1): tmp = np.zeros(u1_comp.shape, dtype=complex) tmp[:, :, n] = u1_comp[:, :, n] if submod == 'stokes': tmp = ny.integrate(ny.complexAmplitudeProduct(tmp, c0, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T'][submod] = self.dictExpand( d['T'][submod], 'TM' + str(2 * n), ['return', 'drift']) d['T'][submod]['TM0']['return'] += tmp else: tmp = ny.integrate(ny.complexAmplitudeProduct(tmp, c0, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T'][submod]['TM' + str(2 * n)] += tmp ## T.5. - u0*c0*zeta0 # Total T0 += ny.complexAmplitudeProduct( ny.complexAmplitudeProduct(u0[:, [0], :], c0[:, [0], :], 2), zeta0, 2) # Decomposition uzeta = ny.complexAmplitudeProduct(u0[:, [0], :], zeta0, 2) d['T'] = self.dictExpand( d['T'], 'stokes', ['TM' + str(2 * n) for n in range(0, fmax + 1)]) # transport with residual availability for n in range(0, fmax + 1): tmp = np.zeros(c0[:, [0], :].shape, dtype=complex) tmp[:, :, n] = c0[:, [0], n] tmp = ny.complexAmplitudeProduct(uzeta, tmp, 2)[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T']['stokes']['TM' + str(2 * n)]['drift'] += tmp ## T.6. - u1riv*c2rivriv c2 = self.input.v('hatc2', 'a', 'erosion', 'river_river', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) u1riv = self.input.v('u1', 'river', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) if u1riv is not None: d['T'] = self.dictExpand( d['T'], 'river_river', 'TM0') # add submod index to dict if not already tmp = ny.integrate(ny.complexAmplitudeProduct(u1riv, c2, 2), 'z', kmax, 0, self.input.slice('grid')) if any(abs(tmp[:, 0, 0])) > 10**-14: d['T']['river_river']['TM0'] = tmp[:, 0, 0] T0 += tmp ## T.7. - diffusive part # Total c0x = self.input.d('hatc0', 'a', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') T0 += -Kh * ny.integrate(c0x, 'z', kmax, 0, self.input.slice('grid')) c2x = self.input.d('hatc2', 'a', 'erosion', 'river_river', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') T0 += -Kh * ny.integrate(c2x, 'z', kmax, 0, self.input.slice('grid')) # Decomposition d['T'] = self.dictExpand(d['T'], 'diffusion_tide', ['TM0']) d['T'] = self.dictExpand(d['T'], 'diffusion_river', ['TM0']) # transport with residual availability tmp = -(Kh * ny.integrate(c0x, 'z', kmax, 0, self.input.slice('grid')))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T']['diffusion_tide']['TM0'] = tmp tmp = -(Kh * ny.integrate(c2x, 'z', kmax, 0, self.input.slice('grid')))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['T']['diffusion_river']['TM0'] = tmp ## Diffusion F ############################################################################################ ## F.1. - u0*C1ax*f0 # Total F0 += ny.integrate(ny.complexAmplitudeProduct(u0, c1_a0x, 2), 'z', kmax, 0, self.input.slice('grid')) # Decomposition for submod in self.input.getKeysOf('hatc1', 'ax'): c1_ax0_comp = self.input.v('hatc1', 'ax', submod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) d['F'] = self.dictExpand( d['F'], submod, ['FM' + str(2 * n) for n in range(0, fmax + 1) ]) # add submod index to dict if not already # transport with residual availability for n in range(0, fmax + 1): tmp = np.zeros(u0.shape, dtype=complex) tmp[:, :, n] = u0[:, :, n] tmp = ny.integrate( ny.complexAmplitudeProduct(tmp, c1_ax0_comp, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['F'][submod]['FM' + str(2 * n)] += tmp ## F.3. - diffusive part # Total F0 += -Kh * ny.integrate(c0, 'z', kmax, 0, self.input.slice('grid')) F0 += -Kh * ny.integrate(c2, 'z', kmax, 0, self.input.slice('grid')) # Decomposition d['F'] = self.dictExpand(d['F'], 'diffusion_tide', ['FM0']) d['F'] = self.dictExpand(d['F'], 'diffusion_river', ['FM0']) # transport with residual availability tmp = -(Kh * ny.integrate(c0, 'z', kmax, 0, self.input.slice('grid')))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['F']['diffusion_tide']['FM0'] = tmp tmp = -(Kh * ny.integrate(c2, 'z', kmax, 0, self.input.slice('grid')))[:, 0, 0] if any(abs(tmp)) > 10**-14: d['F']['diffusion_river']['FM0'] = tmp ## Solve ################################################################################################ ## Add all mechanisms & compute a0c from src.DataContainer import DataContainer dc = DataContainer(d) dc.merge(self.input.slice('grid')) T_til = np.real(dc.v('T', range(0, jmax + 1))) F_til = np.real(dc.v('F', range(0, jmax + 1))) # DEBUG: CHECKS IF COMPOSITE T, F == total T, F # print np.max(abs((dc.v('T', range(0, jmax+1))-T0[:, 0, 0])/(T0[:, 0, 0]+10**-10))) # print np.max(abs((dc.v('F', range(0, jmax+1))-F0[:, 0, 0])/(F0[:, 0, 0]+10**-10))) integral = -ny.integrate(T_til / (F_til - 10**-6), 'x', 0, range(0, jmax + 1), self.input.slice('grid')) if self.input.v('Qsed') is None: G = 0 else: G = self.input.v('Qsed') / B[-1, 0, 0] P = ny.integrate(G / (F_til - 10**-6) * np.exp(-integral), 'x', 0, range(0, jmax + 1), self.input.slice('grid')) ################################################################################################################ # Boundary condition 1 ################################################################################################################ if self.input.v('sedbc') == 'astar': astar = self.input.v('astar') k = astar * ny.integrate(B[:, 0, 0], 'x', 0, jmax, self.input.slice('grid')) / ny.integrate( B[:, 0, 0] * np.exp(integral), 'x', 0, jmax, self.input.slice('grid')) f0 = (k - P) * np.exp(integral) f0x = (-T_til * f0 - G) / (F_til - 10**-6) ################################################################################################################ # Boundary condition 2 ################################################################################################################ elif self.input.v('sedbc') == 'csea': csea = self.input.v('csea') c000 = np.real(c0_int[0, 0, 0]) k = csea / c000 * (self.input.v('grid', 'low', 'z', 0) - self.input.v('grid', 'high', 'z', 0)) f0 = (k - P) * np.exp(integral) f0x = (-T_til * f0 - G) / (F_til - 10**-6) else: from src.util.diagnostics.KnownError import KnownError raise KnownError( 'sediment boundary sedbc not known: use astar or csea') ################################################################################################################ # Store in dict ################################################################################################################ d['a'] = f0 d['c0'] = c0 * f0.reshape((jmax + 1, 1, 1)) d['c1'] = c1_a0 * f0.reshape((jmax + 1, 1, 1)) + c1_a0x * f0x.reshape( (jmax + 1, 1, 1)) d['c2'] = c2 * f0.reshape((jmax + 1, 1, 1)) return d
def zetaFunctionUncoupled(n, M, F, Fopen, Fclosed, data, hasMatrix=False): # Init jmax = data.v('grid', 'maxIndex', 'x') OMEGA = data.v('OMEGA') x = data.v('grid', 'axis', 'x') dx = (x[1:] - x[:-1]) * data.v('L') B = data.v('B', x=x[:-1] + .5 * (x[1:] - x[:-1])) # widths between two grid points ##### LEFT HAND SIDE ##### if not hasMatrix: # init A = np.zeros([3, jmax + 1], dtype=complex) # dx vectors Bdx_down = (dx[:jmax - 1] * B[:jmax - 1]) Bdx_up = (dx[1:jmax] * B[1:jmax]) dx_av = 0.5 * (dx[1:jmax] + dx[:jmax - 1]) # Build matrix a = M[:-2] / Bdx_down b = -M[1:-1] / Bdx_down - M[1:-1] / Bdx_up c = M[2:] / Bdx_up b += n * 1j * OMEGA * dx_av A[2, :-2] += a A[1, 1:-1] += b A[0, 2:] += c # Boundary conditions # Sea (j=0) b = -M[0] / (dx[0] * B[0]) b += 0.5 * n * 1j * OMEGA * dx[0] c = M[1] / (dx[0] * B[0]) A[1, 0] += b A[0, 1] += c # Weir (j=jmax) A[1, -1] += M[-1] else: A = M ##### RIGHT HAND SIDE ##### nRHS = F.shape[-1] zRHS = np.zeros([jmax + 1, nRHS], dtype=complex) # forcing on open boundary zRHS[0, :] = -n * 1j * OMEGA * Fopen[0, :] # forcing on closed boundary zRHS[-1, :] = Fclosed[0, :] # internal forcing FdivBav = 0.5 * (F[1:, :] + F[:-1, :]) / B.reshape((jmax, 1)) zRHS[1:-1, :] = -FdivBav[1:] + FdivBav[:-1] zRHS[0, :] += -FdivBav[0] ##### SOLVE ##### zetax = solve_banded((1, 1), A, zRHS, overwrite_ab=False, overwrite_b=True) zetax = zetax.reshape(jmax + 1, nRHS) # integrate to zeta zeta = integrate(zetax.reshape((jmax + 1, 1, 1, nRHS)), 'x', 0, np.arange(0, jmax + 1), data.slice('grid'), INTMETHOD='TRAPEZOIDAL') zeta = zeta.reshape((jmax + 1, nRHS)) zeta += Fopen[0, :] return zeta, zetax, A
def uRelax(self, order, init): """Compute the absolute velocity and absolute velocity times the depth at 'order', i.e. |u|^<order>, (|u|(H+R+zeta))^<order>. Then make a relaxation of these signals using the the previous iteration and relaxtion factor set as class var. Implements two methods: order == None: truncation method. Else: scaling method init (bool): initial iteration? """ # Init jmax = self.input.v('grid', 'maxIndex', 'x') # maximum index of x grid (jmax+1 grid points incl. 0) kmax = self.input.v('grid', 'maxIndex', 'z') # maximum index of z grid (kmax+1 grid points incl. 0) fmax = self.input.v('grid', 'maxIndex', 'f') # maximum index of f grid (fmax+1 grid points incl. 0) depth = self.input.v('grid', 'low', 'z', range(0, jmax+1), [0], [0]) - self.input.v('grid', 'high', 'z', range(0, jmax+1), [0], [0]) # test if present u is grid-conform (may not be if the previous runs were on a different resolution) utest = self.input.v('u0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) no_u = False if isinstance(utest, bool): no_u = True ################################################################################################################ # 1. make the absolute velocity ################################################################################################################ c = ny.polyApproximation(np.abs, 8) # chebyshev coefficients for abs ## Truncation method if order == None: ## 1a. Gather velocity and zeta components zeta = 0 u = 0 comp = 0 while self.input.v('zeta'+str(comp)) and self.input.v('u'+str(comp)) and comp <= self.truncationorder and not no_u: zeta += self.input.v('zeta'+str(comp), range(0, jmax + 1), [0], range(0, fmax + 1)) u += self.input.v('u'+str(comp), range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) for submod in self.ignoreSubmodule: # remove submodules to be ignored try: zeta -= self.input.v('zeta'+str(comp), submod, range(0, jmax + 1), [0], range(0, fmax + 1)) u -= self.input.v('u'+str(comp), submod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) except: pass comp += 1 # if no data for u and zeta is in de DC, then take an initial estimate if comp == 0: u = np.zeros((jmax+1, kmax+1, fmax+1)) u[:, :, 0] = 1. zeta = np.zeros((jmax+1, 1, fmax+1)) usurf = u[:, [0], :] u = ny.integrate(u, 'z', kmax, 0, self.input.slice('grid')) ## 1b. Divide velocity by a maximum amplitude uamp = [(np.sum(np.abs(u), axis=-1)+10**-3).reshape((jmax+1, 1, 1)), (np.sum(np.abs(usurf), axis=-1)+10**-3).reshape((jmax+1, 1, 1))] u = u/uamp[0] usurf = usurf/uamp[1] ## 1c. Make absolute value uabs = [np.zeros(u.shape, dtype=complex), np.zeros(u.shape, dtype=complex)] # uabs at depth-av, surface for n in [0, 1]: # compute for DA (0) and surface (1) if n == 0: ut = u else: ut = usurf uabs[n][:, :, 0] = c[0] u2 = ny.complexAmplitudeProduct(ut, ut, 2) uabs[n] += c[2]*u2 u4 = ny.complexAmplitudeProduct(u2, u2, 2) uabs[n] += c[4]*u4 u6 = ny.complexAmplitudeProduct(u2, u4, 2) uabs[n] += c[6]*u6 del u2, u6 u8 = ny.complexAmplitudeProduct(u4, u4, 2) uabs[n] += c[8]*u8 del u4, u8 uabs[n] = uabs[n] * uamp[n].reshape((jmax+1, 1, 1)) # Absolute velocity * depth uabsH = uabs[0] + ny.complexAmplitudeProduct(uabs[1], zeta, 2) uabs = uabs[0]/depth + ny.complexAmplitudeProduct(uabs[1]-uabs[0]/depth, zeta, 2) ## Scaling method else: ## 1a. Gather velocity and zeta components zeta = [] u = [] usurf = [] for comp in range(0, order+1): if self.input.v('zeta'+str(comp)) and self.input.v('u'+str(comp)) and not no_u: zetatemp = self.input.v('zeta'+str(comp), range(0, jmax + 1), [0], range(0, fmax + 1)) utemp = self.input.v('u'+str(comp), range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) for submod in self.ignoreSubmodule: # remove submodules to be ignored try: zetatemp -= self.input.v('zeta'+str(comp), submod, range(0, jmax + 1), [0], range(0, fmax + 1)) utemp -= self.input.v('u'+str(comp), submod, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) except: pass # if no u and zeta in DC, then .. elif comp == 0: # .. add velocity 1 to subtidal leading order zetatemp = np.zeros((jmax+1, 1, fmax+1)) utemp = np.zeros((jmax+1, kmax+1, fmax+1)) utemp[:, :, 0] = 1. else: # .. add nothing at higher orders zetatemp = np.zeros((jmax+1, 1, fmax+1)) utemp = np.zeros((jmax+1, kmax+1, fmax+1)) zeta.append(zetatemp) usurf.append(utemp[:, [0], :]) u.append(ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) / depth) ## 1b. Divide velocity by a maximum amplitude uamp = [] uamp.append((np.sum(np.abs(sum(u)), axis=-1)+10**-3).reshape((jmax+1, 1, 1))) uamp.append((np.sum(np.abs(sum(usurf)), axis=-1)+10**-3).reshape((jmax+1, 1, 1))) u = [i/uamp[0] for i in u] usurf = [i/uamp[1] for i in usurf] ## 1c. Make absolute value uabs = [np.zeros(u[0].shape+(order+1,), dtype=complex), np.zeros(u[0].shape+(order+1,), dtype=complex)] for n in [0, 1]: # compute for DA (0) and surface (1) if n == 0: ut = u else: ut = usurf uabs[n][:, :, 0, 0] = c[0] for q in range(0, order+1): uabs[n][:, :, :, q] += c[2]*self.umultiply(2, q, ut) uabs[n][:, :, :, q] += c[4]*self.umultiply(4, q, ut) uabs[n][:, :, :, q] += c[6]*self.umultiply(6, q, ut) uabs[n][:, :, :, q] += c[8]*self.umultiply(8, q, ut) uabs[n] = uabs[n] * uamp[n].reshape((jmax+1, 1, 1, 1)) # Absolute velocity * depth uabsH = uabs[0][:, :, :, order]*depth for q in range(0, order): uabsH += ny.complexAmplitudeProduct(uabs[1][:, :, :, q]-uabs[0][:, :, :, q], zeta[order-q-1], 2) # Only keep uabs at current order uabs = uabs[0][:, :, :, order] # only keep DA part ################################################################################################################ # 2. Relaxtion ################################################################################################################ ## 2a. Relaxation on uabs if hasattr(self, 'u_prev_iter') and self.u_prev_iter.shape == uabs.shape: u_prev_iter = self.u_prev_iter else: u_prev_iter = uabs # take current uabs if no previous is available u_prev_iter2 = u_prev_iter - (uabs) u0 = np.max((uabs, np.min((u_prev_iter2 * (1 - self.RELAX), u_prev_iter2 * (1. + self.RELAX)), axis=0) + (uabs)), axis=0) u0 = np.min((u0, np.max((u_prev_iter2 * (1 - self.RELAX), u_prev_iter2 * (1. + self.RELAX)), axis=0) + uabs), axis=0) self.u_prev_iter = u0 # save velocity at bed for next iteration ## 2b. Relaxation on uabs*depth if hasattr(self, 'uH_prev_iter') and self.uH_prev_iter.shape == uabsH.shape: u_prev_iter = self.uH_prev_iter else: u_prev_iter = uabsH # take current uabsH if no previous is available u_prev_iter2 = u_prev_iter - (uabsH) uH0 = np.max((uabsH, np.min((u_prev_iter2 * (1 - self.RELAX), u_prev_iter2 * (1. + self.RELAX)), axis=0) + (uabsH)), axis=0) uH0 = np.min((uH0, np.max((u_prev_iter2 * (1 - self.RELAX), u_prev_iter2 * (1. + self.RELAX)), axis=0) + uabsH), axis=0) self.uH_prev_iter = uH0 # save velocity at bed for next iteration return u0, uH0
def component(self, ws, Kv, Kh): jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') z = ny.dimensionalAxis(self.input.slice('grid'), 'z')[:, :, 0] depth = (self.input.v('grid', 'low', 'z', range(0, jmax+1)) - self.input.v('grid', 'high', 'z', range(0, jmax+1))).reshape((jmax+1, 1)) B = self.input.v('B', range(0, jmax+1)) u0 = self.input.v('u0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) w0 = self.input.v('w0', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) zeta0 = self.input.v('zeta0', range(0, jmax+1), [0], range(0, fmax+1)) ################################################################################################################ # Leading order ################################################################################################################ Chat = np.zeros((jmax+1, kmax+1, fmax+1), dtype=complex) if ws[0,0] != 0: k = depth*ws[:, [-1]]/Kv[:, [-1]]*(1-np.exp(-ws[:, [-1]]/Kv[:, [-1]]*depth))**-1. else: k = 1 Chat[:, :, 0] = k*np.exp(-ws[:, [-1]]/Kv[:, [-1]]*(z+depth)) # k is such that depth-av Chat = 1. Chatx = ny.derivative(Chat, 'x', self.input.slice('grid')) Chatz = -(ws[:, [-1]]/Kv[:, [-1]]).reshape((jmax+1, 1, 1))*Chat ################################################################################################################ # First order ################################################################################################################ F = np.zeros((jmax+1, kmax+1, 2*fmax+1, 2), dtype=complex) Fsurf = np.zeros((jmax+1, 1, 2*fmax+1, 2), dtype=complex) Fbed = np.zeros((jmax+1, 1, 2*fmax+1, 2), dtype=complex) ## forcing terms # advection F[:, :, fmax:, 0] = -ny.complexAmplitudeProduct(u0, Chatx, 2) - ny.complexAmplitudeProduct(w0, Chatz, 2) F[:, :, fmax:, 1] = -ny.complexAmplitudeProduct(u0, Chat, 2) ## solve Chat1, _ = pFunction(1, ws, Kv, F[:, :, fmax+1], Fsurf[:, :, fmax+1], Fbed[:, :, fmax+1], self.input) Chat1 = ny.eliminateNegativeFourier(Chat1, 2) ################################################################################################################ # Closure ################################################################################################################ # transport T = {} T['adv'] = {} T['adv']['tide'] = np.real(ny.integrate(ny.complexAmplitudeProduct(u0, Chat1[:, :, :, 0], 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0]*B) for key in self.input.getKeysOf('u1'): utemp = self.input.v('u1', key, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) try: T['adv'][key] += np.real(ny.integrate(ny.complexAmplitudeProduct(utemp, Chat, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0]*B) except: T['adv'][key] = np.real(ny.integrate(ny.complexAmplitudeProduct(utemp, Chat, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0]*B) T['dif'] = - np.real(ny.integrate(ny.complexAmplitudeProduct(Kh, Chatx, 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0]*B) T['noflux'] = np.real(ny.complexAmplitudeProduct(ny.complexAmplitudeProduct(u0[:, [0], :], Chat[:, [0], :], 2), zeta0, 2)[:, 0, 0]*B) F = {} F['adv'] = {} F['adv']['tide'] = np.real(ny.integrate(ny.complexAmplitudeProduct(u0, Chat1[:, :, :, -1], 2), 'z', kmax, 0, self.input.slice('grid'))[:, 0, 0]*B) F['dif'] = - np.real(Kh[:, 0]*depth[:, 0]*B) H1 = np.real(depth[:, 0]*B) return T, F, H1, Chat[:, :, 0]
def main(self, components, ws, Kv, Kh, Csea, QC, S, Gamma, Gamma_factor, source): jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') taumax = self.input.v('taumax') or 1 if False:#hasattr(self, 'X'): #DEBUG X = self.X spinup = False else: X = np.zeros((jmax+1, len(components))) spinup = True # time integration and initial condition Chat_prim = np.zeros((jmax+1, kmax+1, len(components))) Chat_int = np.zeros((jmax+1, len(components))) Ts = np.zeros((jmax+1, len(components))) Fs = np.zeros((jmax+1, len(components))) Hs = np.zeros((jmax+1, len(components))) Chat = np.zeros((jmax+1, kmax+1, len(components))) ################################################################################################################ # Compute transport rates & potential concentrations ################################################################################################################ dc = self.input.slice('grid') for i, comp_name in enumerate(components): T, F, Hs[:, i], Chat[:, :, i] = self.component(ws[i], Kv[i], Kh[i]) dc.merge({comp_name: {'T': T}}) dc.merge({comp_name: {'F': F}}) Ts[:, i] = dc.v(comp_name, 'T', range(0, jmax+1)) Fs[:, i] = dc.v(comp_name, 'F', range(0, jmax+1)) # integrals Chat_prim[:, :, i] = -ny.primitive(np.real(Chat[:, :, i]), 'z', 0, kmax, self.input.slice('grid')) # primitive counted from surface Chat_int[:, i] = np.sum(Chat_prim[:, :, i], axis=1) ## process growth function so that no duplicate computations are done Gamma_flat = [item for sublist in Gamma for item in sublist] Gamma_list = list(set(Gamma_flat)) Gamma_index = [[Gamma_list.index(i) for i in j] for j in Gamma] ################################################################################################################ # Initial conditions ################################################################################################################ if spinup: for i, comp_name in enumerate(components): if i==0: P0 = Csea[i]*np.exp(-10*np.linspace(0, 1, jmax+1)) H = self.input.v('grid', 'low', 'z', range(0, jmax+1)) - self.input.v('grid', 'high', 'z', range(0, jmax+1)) X[:, i] = P0/np.real(ny.integrate(Chat[:, :, i], 'z', kmax, 0, self.input.slice('grid'))[:, 0])*H else: # initial condition (equilibrium without growth) integrand = ny.integrate(Ts[:, i]/(Fs[:, i]), 'x', 0, range(0, jmax+1), self.input.slice('grid')) P = QC[i]/(Fs[:, i])*np.exp(integrand.reshape((jmax+1, 1))*np.ones((1, jmax+1))-integrand.reshape((1, jmax+1))) Pint = ny.integrate(P.reshape((jmax+1, 1, 1, jmax+1)), 'x', 0, range(0, jmax+1), self.input.slice('grid')) Pint = Pint[range(0, jmax+1), 0, 0, range(0, jmax+1)] C0 = np.exp(-integrand)*Csea[i] + Pint X[:, i] = C0/Chat_int[:, i]*H self.Xprev = np.ones(X.shape)*np.inf ################################################################################################################ # Time integration ################################################################################################################ init_growth = True ctd = True # set time step if not spinup and self.input.v('dtau'): dtau = self.input.v('dtau')*24*3600. tau = np.linspace(0, dtau, np.ceil(dtau/self.DT)+1) i_tau_now = 0 dt = tau[1]-tau[0] else: dt = self.DT spinup = True # run time stepping dind = 0 # index for the iteration number self.dif_prev = np.inf # difference in previous step (only in spin-up) while ctd: dind +=1 ## Growth functions Gamma_eval = [fun(Chat, Chat_prim, Chat_int, X[:, :], init_growth) for fun in Gamma_list] init_growth = False Gamma_sum = [self.sumdict(copy(j)) for j in Gamma_eval] Gamma = [[Gamma_sum[i] for i in j] for j in Gamma_index] for i, comp_name in enumerate(components): G = sum([a*b for a,b in zip(Gamma_factor[i], Gamma[i])]) G += source[:, i] X[:, i] = cSolverTime(Ts[:, i], Fs[:, i], np.zeros(jmax+1), G, Hs[:, i], Hs[:, i]*X[:, i], X[0, i], 'flux', QC[i], dt, X[:, i], self.input) if spinup: dif = self.DT/dt*np.linalg.norm((X - self.Xprev)/(X+0.001*np.max(X)), np.inf) print dind, dif ## DEBUG # if np.max(X[:, 0]) > 50/1000. and dind > 100: # print 'exit simulation; non-realistic result' # ctd = False # X = np.nan*X ## END # print dif if dif < 10**-5 or dind>2000:#10**-8: ctd = False elif self.dif_prev < dif and dind > 100: # adjust time step if diffence increases after 200th iteration (only in spin-up) dt = dt/2. dind = 0 print 'timestep: ' + str(dt) else: self.Xprev = copy(X) self.dif_prev = dif else: i_tau_now += 1 if i_tau_now == len(tau)-1: ctd = False ################################################################################################################ ## Return ################################################################################################################ self.X = copy(X) Gamma = [[Gamma_eval[i] for i in j] for j in Gamma_index] # Gamma2 = [0]*len(Gamma) # for i in range(0, len(Gamma)): # for j in range(1, len(Gamma[i])): # mergeDicts(Gamma[i][0], Gamma[i][j]) # Gamma2[i] = Gamma[i][0] d = {} d['Ceco'] = {} d['Teco'] = {} d['Feco'] = {} d['Geco'] = {} for i, comp_name in enumerate(components): d['Ceco'][comp_name] = Chat[:, :, i]*X[:, i].reshape(jmax+1, 1) d['Geco'][comp_name] = {} for j in range(0, len(Gamma[i])): d['Geco'][comp_name].update(Gamma[i][j]) # d['Geco'][comp_name] = mergeDicts(d['Geco'][comp_name], Gamma[i][j]) d['Teco'][comp_name] = dc.data[comp_name]['T'] d['Feco'][comp_name] = dc.data[comp_name]['F'] return d
def run(self): """ Returns: Dictionary with results. At least contains the variables listed as output in the registry """ self.logger.info('Running module HydroFirst') # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') G = self.input.v('G') BETA = self.input.v('BETA') OMEGA = self.input.v('OMEGA') ftot = 2 * fmax + 1 submodulesToRun = self.input.v('submodules') # check if the river term should be compensated for by reference level. Only if river is on, non-zero and there is no leading-order contribution if 'river' in submodulesToRun and self.input.v( 'Q1') != 0 and not np.any( self.input.v('zeta0', 'river', range(0, jmax + 1), 0, 0)): RiverReferenceCompensation = 1 else: RiverReferenceCompensation = 0 ################################################################################################################ # velocity as function of water level ################################################################################################################ ## LHS terms # try to get velocityMatrix from the input. If it is not found, proceed to calculate the matrix again A = self.input.v( 'velocityMatrix' ) # known that this is numeric data, ask without arguments to retrieve full ndarray velocityMatrix = True if A is None: A = self.input.v('Av', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) velocityMatrix = False else: A = A ## RHS terms # Determine number/names of right hand side submodulesVelocityForcing = [ i for i in ['adv', 'nostress', 'baroc', 'mixing', 'river'] if i in submodulesToRun ] #submodulesVelocityConversion = [i for i, mod in enumerate(submodulesToRun) if mod in ['adv', 'nostress', 'baroc', 'mixing']] submodulesVelocityConversion = [ submodulesToRun.index(i) for i in submodulesVelocityForcing ] nRHS = len(submodulesVelocityForcing) F = np.zeros([jmax + 1, kmax + 1, ftot, nRHS], dtype=complex) Fsurf = np.zeros([jmax + 1, 1, ftot, nRHS], dtype=complex) Fbed = np.zeros([jmax + 1, 1, ftot, nRHS], dtype=complex) uFirst = np.zeros([jmax + 1, kmax + 1, ftot, len(submodulesToRun)], dtype=complex) uzFirst = np.zeros([jmax + 1, kmax + 1, ftot, len(submodulesToRun)], dtype=complex) u0 = self.input.v('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) zeta0 = self.input.v('zeta0', range(0, jmax + 1), [0], range(0, fmax + 1)) if RiverReferenceCompensation: # for reference level variation F[:, :, fmax, submodulesVelocityForcing.index('river')] = -G * self.input.d( 'R', range(0, jmax + 1), dim='x').reshape( (jmax + 1, 1)) * np.ones((1, kmax + 1)) if 'adv' in submodulesVelocityForcing: u0x = self.input.d('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') u0z = self.input.d('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='z') w0 = self.input.v('w0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) eta = ny.complexAmplitudeProduct( u0, u0x, 2) + ny.complexAmplitudeProduct(w0, u0z, 2) eta = np.concatenate((np.zeros([jmax + 1, kmax + 1, fmax]), eta), 2) F[:, :, :, submodulesVelocityForcing.index('adv')] = -eta if 'nostress' in submodulesVelocityForcing: D = (np.arange(0, fmax + 1) * 1j * OMEGA).reshape( (1, 1, fmax + 1)) * np.ones((jmax + 1, 1, 1)) zeta0x = self.input.d('zeta0', range(0, jmax + 1), [0], range(0, fmax + 1), dim='x') chi = D * u0[:, [0], :] + G * zeta0x chi = ny.complexAmplitudeProduct(chi, zeta0, 2) chi = np.concatenate((np.zeros([jmax + 1, 1, fmax]), chi), 2) Fsurf[:, :, :, submodulesVelocityForcing.index('nostress')] = -chi if 'baroc' in submodulesVelocityForcing: sx = self.input.d('s0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') Jsx = -ny.integrate( sx, 'z', 0, range(0, kmax + 1), self.input.slice('grid') ) # integral from z to 0 has its boundaries inverted and has a minus sign to compensate Jsx = np.concatenate((np.zeros([jmax + 1, kmax + 1, fmax]), Jsx), 2) F[:, :, :, submodulesVelocityForcing.index('baroc')] = -G * BETA * Jsx if 'mixing' in submodulesVelocityForcing: u0z = self.input.d('u0', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='z') Av1 = self.input.v('Av1', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) ksi = ny.complexAmplitudeProduct(u0z, Av1, 2) ksiz = ny.derivative(ksi, 1, self.input.slice('grid')) ksi = np.concatenate((np.zeros([jmax + 1, kmax + 1, fmax]), ksi), 2) ksiz = np.concatenate((np.zeros([jmax + 1, kmax + 1, fmax]), ksiz), 2) F[:, :, :, submodulesVelocityForcing.index('mixing')] = ksiz Fsurf[:, :, :, submodulesVelocityForcing.index('mixing')] = -ksi[:, [0], Ellipsis] ## Removed 14-7-2017 YMD: Roughness1*u0 and Av1*u0z should be equal, so this term cancels # if self.input.v('BottomBC') in ['PartialSlip']: # Fbed[:, :, :, submodulesVelocityForcing.index('mixing')] = -ksi[:,[kmax],Ellipsis] # roughness1 = self.input.v('Roughness1', range(0, jmax+1), [0], range(0, fmax+1)) # if roughness1 is not None: # ksi = ny.complexAmplitudeProduct(u0[:, [-1], :], roughness1, 2) # ksi = np.concatenate((np.zeros([jmax+1, 1, fmax]), ksi), 2) # Fbed[:, :, :, submodulesVelocityForcing.index('mixing')] = ksi ## Solve equation uCoef, uFirst[:, :, :, submodulesVelocityConversion], uzCoef, uzFirst[:, :, :, submodulesVelocityConversion], _ = uFunctionMomentumConservative( A, F, Fsurf, Fbed, self. input, hasMatrix =velocityMatrix ) ################################################################################################################ # water level ################################################################################################################ ## LHS terms # try to get zetaMatrix from the input. If it is not found, proceed to calculate the matrix again B = self.input.v( 'zetaMatrix' ) # known that this is numeric data, ask without arguments to retrieve full ndarray zetaMatrix = True if B is None: zetaMatrix = False utemp = uCoef.reshape( uCoef.shape[:2] + (1, ) + uCoef.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JuCoef = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuCoef = JuCoef.reshape(jmax + 1, 1, ftot, ftot) # reshape back to original grid B = -G * JuCoef * self.input.v('B', np.arange( 0, jmax + 1)).reshape(jmax + 1, 1, 1, 1) ## RHS terms # advection, no-stress, baroclinic utemp = uFirst.reshape( uFirst.shape[:2] + (1, ) + uFirst.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JuFirst = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuFirst = JuFirst.reshape( jmax + 1, 1, ftot, uFirst.shape[-1]) # reshape back to original grid # stokes if 'stokes' in submodulesToRun: gamma = ny.complexAmplitudeProduct(u0[:, 0, None, Ellipsis], zeta0, 2) gamma = np.concatenate((np.zeros([jmax + 1, 1, fmax]), gamma), 2) JuFirst[:, :, :, submodulesToRun.index('stokes')] = gamma BJuFirst = JuFirst * self.input.v('B', np.arange(0, jmax + 1)).reshape( jmax + 1, 1, 1, 1) # open BC: tide Fopen = np.zeros([1, 1, ftot, len(submodulesToRun)], dtype=complex) if 'tide' in submodulesToRun: Fopen[0, 0, fmax:, submodulesToRun.index('tide')] = ny.amp_phase_input( self.input.v('A1'), self.input.v('phase1'), (fmax + 1, )) # closed BC: river Fclosed = np.zeros([1, 1, ftot, len(submodulesToRun)], dtype=complex) if 'river' in submodulesToRun: Fclosed[0, 0, fmax, submodulesToRun.index('river')] = -self.input.v('Q1') # closed BC: other terms Fclosed += -JuFirst[jmax, 0, :, :] * self.input.v('B', jmax) ## Solve equation zetaCoef, zetaxCoef, _ = zetaFunctionMassConservative( B, BJuFirst, Fopen, Fclosed, self.input, hasMatrix=zetaMatrix) zetax = ny.eliminateNegativeFourier(zetaxCoef, 2) zeta = ny.eliminateNegativeFourier(zetaCoef, 2) ################################################################################################################ # velocity ################################################################################################################ u = np.empty((jmax + 1, kmax + 1, ftot, len(submodulesToRun)), dtype=uCoef.dtype) uz = np.empty((jmax + 1, kmax + 1, ftot, len(submodulesToRun)), dtype=uCoef.dtype) for j in range(0, jmax + 1): u[j, :, :, :] = np.dot(uCoef[j, :, :, :], -G * zetaxCoef[j, 0, :, :]) uz[j, :, :, :] = np.dot(uzCoef[j, :, :, :], -G * zetaxCoef[j, 0, :, :]) u += uFirst uz += uzFirst u = ny.eliminateNegativeFourier(u, 2) uz = ny.eliminateNegativeFourier(uz, 2) ################################################################################################################ # vertical velocity ################################################################################################################ w = self.verticalVelocity(u) ################################################################################################################ # Make final dictionary to return ################################################################################################################ d = {} d['zeta1'] = {} d['u1'] = {} d['w1'] = {} for i, submod in enumerate(submodulesToRun): nf = ny.functionTemplates.NumericalFunctionWrapper( zeta[:, :, :, i], self.input.slice('grid')) nf.addDerivative(zetax[:, :, :, i], 'x') d['zeta1'][submod] = nf.function nfu = ny.functionTemplates.NumericalFunctionWrapper( u[:, :, :, i], self.input.slice('grid')) nfu.addDerivative(uz[:, :, :, i], 'z') d['u1'][submod] = nfu.function d['w1'][submod] = w[:, :, :, i] return d
def run(self): # self.timers[0].tic() self.logger.info('Running module StaticAvailability') ################################################################################################################ ## Init ################################################################################################################ jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') L = self.input.v('L') self.x = self.input.v('grid', 'axis', 'x') self.zarr = ny.dimensionalAxis(self.input.slice('grid'), 'z')[:, :, 0]-self.input.v('R', x=self.x/L).reshape((len(self.x), 1)) #YMD 22-8-17 includes reference level; note that we take a reference frame z=[-H-R, 0] c00 = np.real(self.input.v('hatc0', 'a', range(0, jmax+1), range(0, kmax+1), 0)) c04 = np.abs(self.input.v('hatc0', 'a', range(0, jmax+1), range(0, kmax+1), 2)) # c20 = np.real(self.input.v('hatc2', 'a', range(0, jmax+1), range(0, kmax+1), 0)) # NB. do not include hatc2 in the definition of alpha1 here alpha1 = ny.integrate(c00, 'z', kmax, 0, self.input.slice('grid'))[:, 0] alpha1[-1] += alpha1[-2] # correct alpha1 at last point to prevent zero value alpha2 = ny.integrate(c04, 'z', kmax, 0, self.input.slice('grid'))[:, 0]/(alpha1+1e-10) + 1.e-3 # self.timers[0].toc() ################################################################################################################ ## Compute T and F ################################################################################################################ # self.timers[1].tic() d = self.compute_transport() G = self.compute_source() # self.timers[1].toc() ################################################################################################################ ## 4. Calculate availability ################################################################################################################ # self.timers[2].tic() # Add all mechanisms to datacontainer dctrans = DataContainer(d) # Calculate availability a, f0, f0x = self.availability(dctrans.v('F', range(0, jmax+1)), dctrans.v('T', range(0, jmax+1)), G, alpha1, alpha2) f0 = f0.reshape(jmax+1, 1) f0x = f0x.reshape(jmax+1, 1) d['a'] = a nfu = ny.functionTemplates.NumericalFunctionWrapper(f0[:, 0], self.input.slice('grid')) nfu.addDerivative(f0x[:, 0], 'x') d['f'] = nfu.function # self.timers[2].toc() ################################################################################################################ # 5. Calculate concentrations, i.e. a*hatc(a) + ax*hatc(ax) ################################################################################################################ # self.timers[3].tic() d['c0'] = {} d['c1'] = {} d['c2'] = {} # Calculate c0=f*hatc0 for submod in self.input.getKeysOf('hatc0', 'a'): c0_comp = self.input.v('hatc0', 'a', submod, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) d['c0'][submod] = {} tmp = f0[:, None] * c0_comp d['c0'][submod] = tmp # Calculate c1 = f*hatc1_f + fx*hatc1_fx for submod in self.input.getKeysOf('hatc1', 'a'): if submod == 'erosion': for subsubmod in self.input.getKeysOf('hatc1', 'a', 'erosion'): c1_comp = self.input.v('hatc1', 'a', 'erosion', subsubmod, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) d['c1'] = self.dictExpand(d['c1'], 'erosion', subsubmod) tmp = f0[:, None] * c1_comp d['c1']['erosion'][subsubmod] = tmp elif submod == 'sedadv': c1_comp_a = self.input.v('hatc1', 'a', 'sedadv', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) c1_comp_ax = self.input.v('hatc1', 'ax', 'sedadv', range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) d['c1'][submod] = {} tmp = f0[:, None] * c1_comp_a + f0x[:, None] * c1_comp_ax d['c1'][submod] = tmp else: c1_comp = self.input.v('hatc1', 'a', submod, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) d['c1'][submod] = {} tmp = f0[:, None] * c1_comp d['c1'][submod] = tmp # Calculate c2 = f*hatc2 for subsubmod in self.input.getKeysOf('hatc2', 'a', 'erosion'): c2_comp = self.input.v('hatc2', 'a', 'erosion', subsubmod, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1)) d['c2'] = self.dictExpand(d['c2'], 'erosion', subsubmod) tmp = f0[:, None] * c2_comp d['c2']['erosion'][subsubmod] = tmp # self.timers[3].toc() # self.timers[0].disp('time availability - init') # self.timers[1].disp('time availability - T, F') # self.timers[2].disp('time availability - a, f') # self.timers[3].disp('time availability - to dict') # self.timers[4].disp('time availability - cap') # self.timers[5].disp('time availability - trap') # [self.timers[i].reset() for i in range(0, len(self.timers))] return d
def run(self): self.currentOrder = self.input.v('order') if self.currentOrder < 2: return # Init if self.currentOrder == 2: maxOrder = self.input.v('maxOrder') jmax = self.input.v('grid', 'maxIndex', 'x') fmax = self.input.v('grid', 'maxIndex', 'f') OMEGA = self.input.v('OMEGA') G = self.input.v('G') self.submodulesToRun = self.input.v('submodules') # initialise arrays with surface stress and velocity data self.surf_stress = np.nan * np.empty( (jmax + 1, 1, fmax + 1, maxOrder, maxOrder), dtype=complex) # x, z, f, order, derivative self.surf_u_der = np.nan * np.empty( (jmax + 1, 1, fmax + 1, maxOrder, maxOrder + 2), dtype=complex) # x, z, f, order, derivative # update those parts of these arrays that are not updated later # surface stress D = (np.arange(0, fmax + 1) * 1j * OMEGA).reshape( (1, 1, fmax + 1)) * np.ones((jmax + 1, 1, 1)) u0 = self.input.v('u0', range(0, jmax + 1), [0], range(0, fmax + 1)) zeta0x = self.input.d('zeta0', range(0, jmax + 1), [0], range(0, fmax + 1), dim='x') self.surf_stress[:, :, :, 0, 0] = D * u0[:, [0], :] + G * zeta0x # surface der of u self.surf_u_der[:, :, :, 0, 0] = u0 self.surf_u_der[:, :, :, 0, 1] = self.input.d('u0', range(0, jmax + 1), [0], range(0, fmax + 1), dim='z') Av = self.input.v('Av', range(0, jmax + 1), [0], range(0, fmax + 1)) Avz = self.input.d('Av', range(0, jmax + 1), [0], range(0, fmax + 1), dim='z') u0z = self.input.d('u0', range(0, jmax + 1), [0], range(0, fmax + 1), dim='z') self.surf_u_der[:, :, :, 0, 2] = -self.Avinv_multiply( Av, (ny.complexAmplitudeProduct(Avz, u0z, 2) - self.surf_stress[:, :, :, 0, 0])) self.logger.info('Running module HydroHigher - order ' + str(self.currentOrder)) # Init jmax = self.input.v('grid', 'maxIndex', 'x') kmax = self.input.v('grid', 'maxIndex', 'z') fmax = self.input.v('grid', 'maxIndex', 'f') G = self.input.v('G') BETA = self.input.v('BETA') ftot = 2 * fmax + 1 try: maxContributions = int(self.input.v('maxContributions')) except: maxContributions = self.input.v('maxContributions') d = {} # update surf_stress and surf_u_der self.updateSurfaceData() ################################################################################################################ # Velocity in terms of water level gradient ################################################################################################################ ## LHS terms # try to get velocityMatrix from the input. If it is not found, proceed to calculate the matrix again A = self.input.v( 'velocityMatrix' ) # known that this is numeric data, ask without arguments to retrieve full ndarray velocityMatrix = True if A is None: A = self.input.v('Av', range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) velocityMatrix = False ## RHS terms # Determine number/names of right hand side nRHS, nRHSVelocity = self.__numberOfForcings() f_index = -1 f_names = [] # instantiate the forcing components in the equation F = np.zeros([jmax + 1, kmax + 1, ftot, nRHSVelocity], dtype=complex) Fsurf = np.zeros([jmax + 1, 1, ftot, nRHSVelocity], dtype=complex) Fbed = np.zeros([jmax + 1, 1, ftot, nRHSVelocity], dtype=complex) JuFirst = np.zeros([jmax + 1, 1, ftot, nRHS], dtype=complex) uFirst = np.zeros([jmax + 1, kmax + 1, ftot, nRHS], dtype=complex) uzFirst = np.zeros([jmax + 1, kmax + 1, ftot, nRHS], dtype=complex) # determine RHS terms per submodule - first for velocity # 1. Advection if 'adv' in self.submodulesToRun: for order1 in range(0, self.currentOrder): order2 = self.currentOrder - order1 - 1 # labels and submodules u_str1 = 'u' + str(order1) u_keys1 = self.input.getKeysOf(u_str1) u_str2 = 'u' + str(order2) u_keys2 = self.input.getKeysOf(u_str2) w_str = 'w' + str(order1) # retrieve data and make advection forcing for submod1 in u_keys1: for submod2 in u_keys2: u = self.input.v(u_str1, submod1, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) ux = self.input.d(u_str2, submod2, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') w = self.input.v(w_str, submod1, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) uz = self.input.d(u_str2, submod2, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='z') eta = ny.complexAmplitudeProduct( u, ux, 2) + ny.complexAmplitudeProduct(w, uz, 2) eta = np.concatenate( (np.zeros([jmax + 1, kmax + 1, fmax]), eta), 2) f_index += 1 f_names.append([ 'adv', submod1 + str(order1) + '-' + submod2 + str(order2) ]) F[:, :, :, f_index] = -eta del eta # 2. No-Stress if 'nostress' in self.submodulesToRun: chi = 0 for m in range(1, self.currentOrder + 1): for k in range(0, self.currentOrder - m + 1): zetapermutations = self.multiindex( m, self.currentOrder - m - k) # a. make the zeta^m product zetasum = 0 for perm in range(0, zetapermutations.shape[0]): zetaname = 'zeta' + str(zetapermutations[perm, 0]) zeta = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) for comp in range(1, zetapermutations.shape[1]): zetaname = 'zeta' + str(zetapermutations[perm, comp]) zeta2 = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) zeta = ny.complexAmplitudeProduct(zeta, zeta2, 2) zetasum = zetasum + zeta # b. make the (Av*uz)^(m) term (use m-1 as surf_stress contains (Av*uz)_z^(m)) Avuz = self.surf_stress[:, :, :, k, m - 1] # c. add all to chi chi += 1. / np.math.factorial( m) * ny.complexAmplitudeProduct(Avuz, zetasum, 2) chi = np.concatenate((np.zeros([jmax + 1, 1, fmax]), chi), 2) f_index += 1 f_names.append(['nostress', '']) Fsurf[:, :, :, f_index] = -chi del chi # 3. Density-drift # TODO # if 'densitydrift' in self.submodulesToRun: # beta_delta = 0 # for m in range(1, self.currentOrder): # for k in range(0, self.currentOrder-m): # zetapermutations = self.multiindex(m, self.currentOrder-m-k-1) # # a. make the zeta^m product # zetasum = 0 # for perm in range(0, zetapermutations.shape[0]): # zetaname = 'zeta'+str(zetapermutations[perm, 0]) # zeta = self.input.v(zetaname, range(0, jmax+1), [0], range(0, fmax+1)) # for comp in range(1, zetapermutations.shape[1]): # zetaname = 'zeta'+str(zetapermutations[perm, comp]) # zeta2 = self.input.v(zetaname, range(0, jmax+1), [0], range(0, fmax+1)) # zeta = ny.complexAmplitudeProduct(zeta, zeta2, 2) # zetasum = zetasum + zeta # # # b. make the (s_x)^(m) term # sname = 's'+str(k) # sx = self.input.d(sname, range(0, jmax+1), range(0, kmax+1), range(0, fmax+1), dim='x') # sxmdz = self.surfaceDerivative(sx, m-1, self.NUMORDER_SURFACE) # # # c. add all to beta_delta # beta_delta += G*BETA*1./np.math.factorial(m)*ny.complexAmplitudeProduct(sxmdz, zetasum, 2) # # beta_delta = np.concatenate((np.zeros([jmax+1, 1, fmax]), beta_delta), 2) # # f_index += 1 # f_names.append(['densitydrift', '']) # F[:, :, :, f_index] = -beta_delta*np.ones((jmax+1, kmax+1, fmax+1)) # 4. Baroclinic # Only use this when salinity on lower order is available if 'baroc' in self.submodulesToRun: s_str = 's' + str(self.currentOrder - 1) sx = self.input.d(s_str, range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='x') if sx is not None: Jsx = -G * BETA * ny.integrate( sx, 'z', 0, range(0, kmax + 1), self.input.slice('grid') ) # integral from z to 0 has its boundaries inverted and has a minus sign to compensate Jsx = np.concatenate( (np.zeros([jmax + 1, kmax + 1, fmax]), Jsx), 2) f_index += 1 f_names.append(['baroc', '']) F[:, :, :, f_index] = -Jsx del Jsx, sx # 5. Mixing if 'mixing' in self.submodulesToRun: ksi = np.zeros([jmax + 1, kmax + 1, fmax + 1], dtype=complex) for m in range(1, self.currentOrder + 1): Avm = self.input.v('Av' + str(m), range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1)) if Avm is not None: uz = self.input.d('u' + str(self.currentOrder - m), range(0, jmax + 1), range(0, kmax + 1), range(0, fmax + 1), dim='z') ksi += ny.complexAmplitudeProduct(Avm, uz, 2) ksi_z = ny.derivative(ksi, 'z', self.input.slice('grid')) ksi = np.concatenate((np.zeros([jmax + 1, kmax + 1, fmax]), ksi), 2) ksi_z = np.concatenate( (np.zeros([jmax + 1, kmax + 1, fmax]), ksi_z), 2) f_index += 1 f_names.append(['mixing', 'general']) F[:, :, :, f_index] = ksi_z Fsurf[:, :, :, f_index] = -ksi[:, [0], :] if self.input.v('BottomBC') in ['PartialSlip']: Fbed[:, :, :, f_index] = -ksi[:, [-1], :] # 5.b higher order no-slip coefficient for m in range(1, self.currentOrder + 1): roughness = self.input.v('Roughness' + str(m), range(0, jmax + 1), [0], range(0, fmax + 1)) if roughness is not None: u_str1 = 'u' + str(self.currentOrder - m) u = self.input.v(u_str1, range(0, jmax + 1), [kmax], range(0, fmax + 1)) ksi = ny.complexAmplitudeProduct(u, roughness, 2) ksi = np.concatenate( (np.zeros([jmax + 1, 1, fmax]), ksi), 2) Fbed[:, :, :, f_index] = ksi # 5.b Mixing-no-stress interaction ksi = np.zeros([jmax + 1, 1, fmax + 1], dtype=complex) for m in range(1, self.currentOrder + 1): for k in range(0, self.currentOrder - m + 1): for i in range(1, self.currentOrder - m - k + 1): zetapermutations = self.multiindex( m, self.currentOrder - m - k - i) # a. make the zeta^m product zetasum = 0 for perm in range(0, zetapermutations.shape[0]): zetaname = 'zeta' + str(zetapermutations[perm, 0]) zeta = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) for comp in range(1, zetapermutations.shape[1]): zetaname = 'zeta' + str(zetapermutations[perm, comp]) zeta2 = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) zeta = ny.complexAmplitudeProduct( zeta, zeta2, 2) zetasum = zetasum + zeta # b. make the (Av*uz)^(m) term Avuz = 0 for j in range(0, m + 1): if j == 0: Avder = self.input.v('Av' + str(i), range(0, jmax + 1), [0], range(0, fmax + 1)) else: Avder = self.input.d('Av' + str(i), range(0, jmax + 1), [0], range(0, fmax + 1), dim='z' * j) if Avder is not None: Avuz += scipy.misc.comb( m, j) * ny.complexAmplitudeProduct( Avder, self.surf_u_der[:, :, :, k, m - j + 1], 2) # use m-j+1 as we need u_z^(m-j) # c. add all to ksi if not isinstance(Avuz, int): ksi += 1. / np.math.factorial( m) * ny.complexAmplitudeProduct( Avuz, zetasum, 2) ksi = np.concatenate((np.zeros([jmax + 1, 1, fmax]), ksi), 2) f_index += 1 f_names.append(['mixing', 'no-stress']) Fsurf[:, :, :, f_index] = -ksi # 6. Stokes drift return flow # determine RHS terms per submodule - next for water level # Need to place this separate to make sure that stokes term are at the end. # This way they will not be taken into account for velocity equation if 'stokes' in self.submodulesToRun: gamma = 0 for m in range(1, self.currentOrder + 1): for k in range(0, self.currentOrder - m + 1): zetapermutations = self.multiindex( m, self.currentOrder - m - k) # a. make the zeta^m product zetasum = 0 for perm in range(0, zetapermutations.shape[0]): zetaname = 'zeta' + str(zetapermutations[perm, 0]) zeta = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) for comp in range(1, zetapermutations.shape[1]): zetaname = 'zeta' + str(zetapermutations[perm, comp]) zeta2 = self.input.v(zetaname, range(0, jmax + 1), [0], range(0, fmax + 1)) zeta = ny.complexAmplitudeProduct(zeta, zeta2, 2) zetasum = zetasum + zeta # b. make the (u)^(m-1) term umz = self.surf_u_der[:, :, :, k, m - 1] # c. add all to chi gamma += 1. / np.math.factorial( m) * ny.complexAmplitudeProduct(umz, zetasum, 2) gamma = np.concatenate((np.zeros([jmax + 1, 1, fmax]), gamma), 2) f_index += 1 f_names.append(['stokes', '']) JuFirst[:, :, :, f_index] = gamma ## Solve equation uCoef, uFirst[:, :, :, : nRHSVelocity], uzCoef, uzFirst[:, :, :, : nRHSVelocity], AMatrix = uFunctionMomentumConservative( A, F, Fsurf, Fbed, self.input, hasMatrix= velocityMatrix) if not velocityMatrix: d['velocityMatrix'] = AMatrix del AMatrix ################################################################################################################ # water level ################################################################################################################ ## LHS terms # try to get zetaMatrix from the input. If it is not found, proceed to calculate the matrix again B = self.input.v( 'zetaMatrix' ) # known that this is numeric data, ask without arguments to retrieve full ndarray zetaMatrix = True if B is None: zetaMatrix = False utemp = uCoef.reshape( uCoef.shape[:2] + (1, ) + uCoef.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JuCoef = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuCoef = JuCoef.reshape(jmax + 1, 1, ftot, ftot) # reshape back to original grid B = -G * JuCoef * self.input.v('B', np.arange( 0, jmax + 1)).reshape(jmax + 1, 1, 1, 1) ## RHS terms # advection, no-stress, baroclinic utemp = uFirst.reshape( uFirst.shape[:2] + (1, ) + uFirst.shape[2:] ) # reshape as the 'f' dimension is not grid conform; move it to a higher dimension JTemp = ny.integrate(utemp, 'z', kmax, 0, self.input.slice('grid')) JuFirst += JTemp.reshape( jmax + 1, 1, ftot, uFirst.shape[-1]) # reshape back to original grid BJuFirst = JuFirst * self.input.v('B', np.arange(0, jmax + 1)).reshape( jmax + 1, 1, 1, 1) # no open BC forcing & all terms in closed BC Fopen = np.zeros([1, 1, ftot, nRHS], dtype=complex) Fclosed = np.zeros([1, 1, ftot, nRHS], dtype=complex) Fclosed += -JuFirst[jmax, 0, :, :] * self.input.v('B', jmax) ## Solve equation zetaCoef, zetaxCoef, BMatrix = zetaFunctionMassConservative( B, BJuFirst, Fopen, Fclosed, self.input, hasMatrix=zetaMatrix) if not zetaMatrix: d['zetaMatrix'] = BMatrix del BMatrix zetax = ny.eliminateNegativeFourier(zetaxCoef, 2) zeta = ny.eliminateNegativeFourier(zetaCoef, 2) ################################################################################################################ # velocity ################################################################################################################ u = np.empty((jmax + 1, kmax + 1, ftot, nRHS), dtype=uCoef.dtype) for j in range(0, jmax + 1): u[j, :, :, :] = np.dot(uCoef[j, :, :, :], -G * zetaxCoef[j, 0, :, :]) u += uFirst u = ny.eliminateNegativeFourier(u, 2) ################################################################################################################ # Reduce number of components ################################################################################################################ # Select components by their velocity magnitude. # This is measured as the 1-norm over z and f and the 2-norm over x if maxContributions != 'all': for submod in self.submodulesToRun: f_names_numbers = zip( f_names, range(0, len(f_names)) ) # combine forcing names and its position in the 4th dimension of u and zeta f_submod = [ f_names_numbers[i] for i in range(0, len(f_names)) if f_names[i][0] == submod ] # take only the forcing component of a particular submodule # determine norm and sort the list if f_submod: unorm = [ np.linalg.norm( np.linalg.norm(u[:, :, :, i], 1, (1, 2)), 2, 0) for i in zip(*f_submod)[1] ] sorted_fnames = [ list(l) for l in zip(*sorted(zip(unorm, f_submod), key=lambda nrm: nrm[0])) ] # sorted list with entries (unorm, (forcing name, forcing position)) with smallest norm first else: unorm = [] sorted_fnames = [] # determine the contributions to be aggregated (redundant positions) redundant_positions = [ sorted_fnames[1][i][1] for i in range(0, len(unorm) - maxContributions) ] if len(redundant_positions) >= 1: # replace first redundant element by sum and label 'other' first_red_pos = redundant_positions[0] u[:, :, :, first_red_pos] = np.sum(u[:, :, :, redundant_positions], 3) zeta[:, :, :, first_red_pos] = np.sum( zeta[:, :, :, redundant_positions], 3) f_names[first_red_pos][1] = 'other' # remove other redundant positions u = np.delete(u, redundant_positions[1:], 3) zeta = np.delete(zeta, redundant_positions[1:], 3) [ f_names.pop(i) for i in sorted(redundant_positions[1:], reverse=True) ] ################################################################################################################ # vertical velocity ################################################################################################################ w = self.verticalVelocity(u) ################################################################################################################ # Make final dictionary to return ################################################################################################################ d['zeta' + str(self.currentOrder)] = {} d['u' + str(self.currentOrder)] = {} d['w' + str(self.currentOrder)] = {} for submod in self.submodulesToRun: if submod in zip(*f_names)[0]: d['zeta' + str(self.currentOrder)][submod] = {} d['u' + str(self.currentOrder)][submod] = {} d['w' + str(self.currentOrder)][submod] = {} for i, submod in enumerate(f_names): nf = ny.functionTemplates.NumericalFunctionWrapper( zeta[:, :, :, i], self.input.slice('grid')) nf.addDerivative(zetax[:, :, :, i], 'x') if submod == 'baroc': d['zeta' + str(self.currentOrder)][submod[0]] = nf.function d['u' + str(self.currentOrder)][submod[0]] = u[:, :, :, i] d['w' + str(self.currentOrder)][submod[0]] = w[:, :, :, i] else: d['zeta' + str(self.currentOrder)][submod[0]][submod[1]] = nf.function d['u' + str(self.currentOrder)][submod[0]][submod[1]] = u[:, :, :, i] d['w' + str(self.currentOrder)][submod[0]][submod[1]] = w[:, :, :, i] d['surfder'] = self.surf_u_der d['surfstress'] = self.surf_stress return d