def __init__(self, v, t, b, Mach): self.mesh = Mesh(v, t, b) self.Mach = float(Mach) self.ode = Ode(self.ddt, self.J)
class Euler: def __init__(self, v, t, b, Mach): self.mesh = Mesh(v, t, b) self.Mach = float(Mach) self.ode = Ode(self.ddt, self.J) def integrate(self, *args, **argv): return self.ode.integrate(*args, **argv) @property def nt(self): return self.mesh.nt @property def time(self): return self.ode.t @property def soln(self): return reshape(self.ode.y, [-1,4]) @property def Wref(self): Wr = self.freeStream(1)[0] Wr[2] = Wr[1] return Wr def freeStream(self, nT=None): if nT is None: nT = self.nt R, gamma, rho0, T0 = 8.314 / 29E-3, 1.4, 1.225, 288.75 u0 = self.Mach * sqrt(gamma * R * T0) E0 = T0 * R / (gamma - 1) + 0.5 * u0**2 return array([rho0, rho0 * u0, 0, rho0 * E0]) + zeros([nT, 1]) def ddt(self, W): assert W.size == self.nt * 4 shp = W.shape W = W.reshape([-1, 4]) m = self.mesh gradW = m.gradTri(W) xt = m.xt() dxt = m.dxt # boundary condition categories xeBnd = m.v[m.e[m.ieBnd,:2],:].mean(1) isWall = ~m.isFar(xeBnd) # interior flux #WL, WR = W[m.e[:,2],:], W[m.e[:,3],:] WL, WR = m.leftRightTri(W) WL[m.ieBnd[~isWall]] = self.freeStream(1) WE = 0.5*(WL+WR) dW = WR - WL #dWL = (gradW[m.e[:,2],:] * dxt[:,:,newaxis]).sum(1) #dWR = (gradW[m.e[:,3],:] * dxt[:,:,newaxis]).sum(1) gWL, gWR = m.leftRightTri(gradW) dWL = einsum( 'nij, ni -> nj', gWL, dxt ) dWR = einsum( 'nij, ni -> nj', gWR, dxt ) flux = fluxE(WE, m.n) + fluxD(WE, dW, dWL, dWR, m.n) # boundary flux WBnd, nBnd = WL[m.ieBnd,:], m.n[m.ieBnd,:] flux[m.ieBnd[isWall]] = wallBc(WBnd[isWall,:], nBnd[isWall,:]) # accumunate flux to cells return (m.distributeFlux(flux) / m.a[:,newaxis]).reshape(shp) def J(self, W): if not self.__dict__.has_key('matJacDistFlux'): self.prepareJacMatrices() assert W.size == self.nt * 4 shp = W.shape W = W.reshape([-1, 4]) m = self.mesh # prepare values at edge gradW = m.gradTri(W) xt = m.xt(); dxt = m.dxt WL, WR = m.leftRightTri(W) isFar = m.isFar(m.v[m.e[m.ieBnd,:2],:].mean(1)) WL[m.ieBnd[isFar]] = self.freeStream(1) WE, dW = 0.5 * (WL + WR), WR - WL JE_WE = jacE(WE, m.n) gWL, gWR = m.leftRightTri(gradW) dWL = einsum( 'nij, ni -> nj', gWL, dxt ) dWR = einsum( 'nij, ni -> nj', gWR, dxt ) JD_WE, JD_dW, JD_dWL, JD_dWR = jacD(WE, dW, dWL, dWR, m.n) # modify for wall BC J_WE = JE_WE + JD_WE ieWall = m.ieBnd[~isFar] J_WE[ieWall,:] = jacWall(WE[ieWall], m.n[ieWall]) J_flux = block_diags(J_WE) * self.matJacWE \ + block_diags(JD_dW) * self.matJacdW \ + block_diags(JD_dWL) * self.matJacdWL \ + block_diags(JD_dWR) * self.matJacdWR return self.matJacDistFlux * J_flux def prepareJacMatrices(self): m = self.mesh matL = accumarray(m.e[:,2], m.t.shape[0]).mat.T.tolil() matR = accumarray(m.e[:,3], m.t.shape[0]).mat.T.tolil() # WL on far bc is freestream isFar = m.isFar(m.v[m.e[m.ieBnd,:2],:].mean(1)) matL[m.ieBnd[isFar],:] = 0 # average and difference self.matJacWL = spkron(matL, eye(4)).tocsr() self.matJacWR = spkron(matR, eye(4)).tocsr() self.matJacWE = spkron(0.5 * (matL + matR), eye(4)).tocsr() self.matJacdW = spkron(matR - matL, eye(4)).tocsr() # distribute flux matrix D = block_diags(1./ m.a[:,newaxis,newaxis]) self.matJacDistFlux = spkron(D * m.matDistFlux, eye(4)).tocsr() # Tri to edg gradient with boundary copy matTriToBnd = accumarray(m.e[m.ieBnd,2], m.t.shape[0]).mat.T matGradTriEdg = m.matGradTriEdg + m.bcmatGradTriEdg * matTriToBnd # left and right gradient in cells edgOfTri = invertMap(m.e[:,2:])[1].reshape([-1, 3]) avgEdgToTri = (accumarray(edgOfTri[:,0], m.e.shape[0]).mat.T + \ accumarray(edgOfTri[:,1], m.e.shape[0]).mat.T + \ accumarray(edgOfTri[:,2], m.e.shape[0]).mat.T) / 3. matGradTri = spkron(avgEdgToTri, eye(2)) * matGradTriEdg xt = m.xt(); dxt = xt[m.e[:,3],:] - xt[m.e[:,2],:] matL = spkron(accumarray(m.e[:,2], m.t.shape[0]).mat.T, eye(2)) matR = spkron(accumarray(m.e[:,3], m.t.shape[0]).mat.T, eye(2)) matJacdWL = block_diags(dxt[:,newaxis,:]) * matL * matGradTri matJacdWR = block_diags(dxt[:,newaxis,:]) * matR * matGradTri self.matJacdWL = spkron(matJacdWL, eye(4)).tocsr() self.matJacdWR = spkron(matJacdWR, eye(4)).tocsr() def metric(self, W=None): if W is None: W = self.soln gradV = self.mesh.gradTriVrt(W / self.Wref) gradV /= ((gradV**2).sum(1)[:,newaxis,:])**.25 return (gradV[:,newaxis,:,:] * gradV[:,:,newaxis,:]).sum(-1) # return hessian.sum(-1) def checkJacobian(self): ''' Generate a flow with small linear variation (supress numerical diss.), check adjoint ddt vs Euler ddt ''' xt = self.mesh.xt() R = 0.2 * self.mesh.diameter() decay = exp(-(xt**2).sum(1) / R**2)[:,newaxis] nT = self.mesh.t.shape[0] # random flow field variation = 0.1 * (random.rand(nT, 4) - 0.5) W0 = self.freeStream() + self.Wref * variation # random perturbation EPS = 0.000001 variation = EPS * (random.rand(nT, 4) - 0.5) W1 = W0 + self.Wref * variation # compare ddt with Jacobian deltaFD = self.ddt(ravel(W1)) - self.ddt(ravel(W0)) deltaJac = self.J(0.5 * (W0 + W1)) * ravel(W1 - W0) # difference print sqrt(((deltaFD - deltaJac)**2).sum()), \ sqrt((deltaJac**2).sum()), sqrt((deltaFD**2).sum())