def test_dAgammaW0_dzeta_unit(self): """Test matrix against numerical example.""" M = 1 N = 1 K = M * N dAgamW0_dzeta = np.zeros((3 * (M + 1) * (N + 1))) zeta = np.array( (0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0)) zetaW = np.array( (1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 2.0, 0.0, 0.0, 2.0, 1.0, 0.0)) gammaW0 = np.array((1.0)) Cpp_dAgamma0_dZeta(zetaW, M, N, gammaW0, zeta, M, N, dAgamW0_dzeta) for foo in range(100): dZeta = np.random.random(len(zeta)) / 100.0 #1% variations # change in downwash dwApprox = np.dot(dAgamW0_dzeta, dZeta) # calculate AICs for numrical solution AIC = np.zeros((K, K)) AIC2 = np.zeros((K, K)) Cpp_AIC(zetaW, M, N, zeta, M, N, AIC) Cpp_AIC(zetaW, M, N, zeta + dZeta, M, N, AIC2) # calculate exact solution dwExact = np.dot(AIC2, gammaW0) - np.dot(AIC, gammaW0) # check less than 1% maximum rel error if False: print("test no. ", foo) print(np.dot(AIC, gammaW0)) print(dwExact) print(dwApprox) print((dwApprox - dwExact) / (np.dot(AIC, gammaW0))) # check less than 2% self.assertLess( np.absolute((dwApprox - dwExact) / (np.dot(AIC, gammaW0))), 0.01)
def test_dAgammaW0_dzeta_unit2DM10(self): """Test matrix against numerical example for 2D aerofoil, 10 panels, 90 wake panels.""" #Init AIC m = 10 mW = 190 n = 2 k = m * n kW = mW * n gammaW0 = np.ones((kW)) AIC = np.zeros((k, kW)) AIC2 = np.zeros((k, kW)) dAgamW0_dzeta = np.zeros((k, 3 * (m + 1) * (n + 1))) # initialize grid for 3D solver (Unit VR) chords = np.linspace(0.0, 1.0, m + 1, True) chordsW = np.linspace(1.0, mW * (1.0 / m), mW + 1, True) spans = np.linspace(-1000, 1000, n + 1, True) zeta = np.zeros(3 * len(chords) * len(spans)) zetaW = np.zeros(3 * len(chordsW) * len(spans)) kk = 0 for c in chords: for s in spans: zeta[3 * kk] = c zeta[3 * kk + 1] = s kk = kk + 1 kk = 0 for c in chordsW: for s in spans: zetaW[3 * kk] = c zetaW[3 * kk + 1] = s kk = kk + 1 # gen matrix Cpp_dAgamma0_dZeta(zetaW, mW, n, gammaW0, zeta, m, n, dAgamW0_dzeta) for foo in range(10): # gen random zeta dZeta = np.random.random(len(zeta)) / 1000.0 #1% chord panel vars # calc dw Approx dwApprox = np.dot(dAgamW0_dzeta, dZeta) # calc AICs for numerical comparison Cpp_AIC(zetaW, mW, n, zeta, m, n, AIC) Cpp_AIC(zetaW, mW, n, zeta + dZeta, m, n, AIC2) dwExact = np.dot(AIC2, gammaW0) - np.dot(AIC, gammaW0) if False: print("test no. ", foo) print(np.dot(AIC, gammaW0)) print(dwExact) print(dwApprox) print((dwExact - dwApprox) / np.dot(AIC, gammaW0)) self.assertLess( np.max( np.absolute( (dwApprox - dwExact) / (np.dot(AIC, gammaW0)))), 0.01)
def test_AIC_unit2DM10(self): """Compare AIC matrix to VLM.cpp AIC matrix""" #Init AIC m = 10 n = 1 k = m * n AIC = np.zeros((k, k)) # initialize grid for 3D solver (Unit VR) chords = np.linspace(0.0, 1.0, m + 1, True) spans = (-1000, 1000) zeta = np.zeros(3 * len(chords) * len(spans)) kk = 0 for c in chords: for s in spans: zeta[3 * kk] = c zeta[3 * kk + 1] = s kk = kk + 1 # call AIC matrix function Cpp_AIC(zeta, m, n, zeta, m, n, AIC) # load data for comparison data = np.loadtxt(TestDir + "unit2DM10.dat") # check all entries for i in range(k - 1): for j in range(k - 1): self.assertAlmostEqual(AIC[i, j], data[i, j], 4)
def test_AIC_image(self): """Compare image solution to full solution.""" m = 1 n = 10 k = m * n AIC = np.zeros((k, k)) gam = np.ones((k)) AIChalf = np.zeros((int(k / 2), int(k / 2))) gamHalf = np.ones((int(k / 2))) chords = np.linspace(0.0, 1.0, m + 1, True) spans = np.linspace(-5, 5, n + 1, True) spansHalf = np.linspace(0, 5, int(n / 2) + 1, True) zeta = np.zeros(3 * len(chords) * len(spans)) zetaHalf = np.zeros(3 * len(chords) * (int(len(spans) / 2) + 1)) kk = 0 for c in chords: for s in spans: zeta[3 * kk] = c zeta[3 * kk + 1] = s kk = kk + 1 kk = 0 for c in chords: for s in spansHalf: zetaHalf[3 * kk] = c zetaHalf[3 * kk + 1] = s kk = kk + 1 # call AIC matrix function Cpp_AIC(zeta, m, n, zeta, m, n, AIC) Cpp_AIC(zetaHalf, m, int(n / 2), zetaHalf, m, int(n / 2), AIChalf, True) # downwash w = np.dot(AIC, gam) wHalf = np.dot(AIChalf, gamHalf) self.assertTrue( np.all(np.abs(w[int(n / 2):] - wHalf) < np.finfo(float).eps))
def test_dAgamma0_dzeta_unit(self): """Test matrix against numerical example.""" M = 1 N = 1 K = M * N dAgam0_dzeta = np.zeros((3 * (M + 1) * (N + 1))) zeta = np.array( (0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0)) gamma0 = np.array((1.0)) dZeta = np.random.random(len(zeta)) / 10.0 #10% variations # create matrix Cpp_dAgamma0_dZeta(zeta, M, N, gamma0, zeta, M, N, dAgam0_dzeta) # change in downwash dwApprox = np.dot(dAgam0_dzeta, dZeta) # calculate AICs for numrical solution AIC = np.zeros((K, K)) AIC2 = np.zeros((K, K)) Cpp_AIC(zeta, M, N, zeta, M, N, AIC) Cpp_AIC(zeta + dZeta, M, N, zeta + dZeta, M, N, AIC2) # calculate exact solution dwExact = np.dot(AIC2, gamma0) - np.dot(AIC, gamma0) # check less than 1% maximum rel error self.assertLess( np.absolute((dwApprox - dwExact) / (np.dot(AIC, gamma0))), 0.01)
def test_AIC_unit(self): """Compare AIC matrix to trusted results.""" #Init AIC m = 1 n = 1 k = m * n AIC = np.zeros((k, k)) # initialize grid for 3D solver (Unit VR) chords = (0, 1) spans = (0, 1) zeta = np.zeros(3 * len(chords) * len(spans)) k = 0 for c in chords: for s in spans: zeta[3 * k] = c zeta[3 * k + 1] = s k = k + 1 # call AIC matrix function Cpp_AIC(zeta, 1, 1, zeta, 1, 1, AIC) self.assertAlmostEqual(AIC[0, 0], -0.900316, 6)
def test_AIC3vAIC(self): """Test against trusted result of typical AIC.""" #Init AIC m = 5 n = 1 k = m * n AIC = np.zeros((k, k)) AIC3 = np.zeros((3 * k, k)) # initialize grid for 3D solver (Unit VR) chords = np.linspace(0.0, 1.0, m + 1, True) spans = (-1000, 1000) zeta = np.zeros(3 * len(chords) * len(spans)) kk = 0 for c in chords: for s in spans: zeta[3 * kk] = c zeta[3 * kk + 1] = s kk = kk + 1 # call AIC matrix function Cpp_AIC(zeta, m, n, zeta, m, n, AIC) Cpp_AIC3(zeta, m, n, zeta, m, n, AIC3) for i in range(k - 1): for j in range(k - 1): self.assertAlmostEqual(AIC3[3 * i + 2, j], AIC[i, j], 4)
def genSSuvlm(gam, gamW, gamPri, zeta, zetaW, zetaPri, nu, m, n, mW, delS, imageMeth=False): """@details generate state-space matrices for linear UVLM. @param gam Reference circulation distribution on body. @param gamW Reference circulation distribution in the wake. @param gamPri Reference Rate of change of circulation on body. @param zeta Lattice vertices. @param zetaW Wake lattice vertices. @param zetaPri Vertex velocities. @param nu Atmospheric velocities. @param m Chordwise panels. @param n Spanwise. @param mW Chordwise panels in wake. @param delS Nondimensional timestep. @param imageMeth Use image method across xz-plane. @return E LHS state transfer matrix. @return F RHS state transfer matrix. @return G RHS Input matrix. @return C Output matrix. @return D Feedthrough matrix. @note All of the above should be nondimensional.""" midPoint = False # use midpoint approximation for derivative of gamma # init matrices E = np.zeros((2 * m * n + mW * n, 2 * m * n + mW * n)) F = np.zeros((2 * m * n + mW * n, 2 * m * n + mW * n)) G = np.zeros((2 * m * n + mW * n, 9 * (m + 1) * (n + 1))) C = np.zeros((3 * (m + 1) * (n + 1), 2 * m * n + mW * n)) D = np.zeros((3 * (m + 1) * (n + 1), 9 * (m + 1) * (n + 1))) # populate E AIC = np.zeros((m * n, m * n)) Cpp_AIC(zeta, m, n, zeta, m, n, AIC, imageMeth) AICw = np.zeros((m * n, mW * n)) Cpp_AIC(zetaW, mW, n, zeta, m, n, AICw, imageMeth) E[0:m * n, 0:m * n] = AIC E[0:m * n, m * n:m * n + mW * n] = AICw E[m * n:m * n + mW * n, m * n:m * n + mW * n] = np.eye(mW * n) if midPoint == True: E[m * n + mW * n:, 0:m * n] = np.eye(m * n) E[m * n + mW * n:, m * n + mW * n:] = -0.5 * delS * np.eye(m * n) else: E[m * n + mW * n:, 0:m * n] = -np.eye(m * n) E[m * n + mW * n:, m * n + mW * n:] = delS * np.eye(m * n) # populate F Cgam = np.zeros((mW * n, m * n)) Cgam[0:n, m * n - n:m * n] = np.eye(n) CgamW = np.zeros((mW * n, mW * n)) CgamW[n:, 0:mW * n - n] = np.eye(n * (mW - 1)) #CgamW[-1:,-1]=0.99975 F[m * n:m * n + mW * n, 0:m * n] = Cgam F[m * n:m * n + mW * n, m * n:m * n + mW * n] = CgamW if midPoint == True: F[m * n + mW * n:, 0:m * n] = np.eye(m * n) F[m * n + mW * n:, m * n + mW * n:] = 0.5 * delS * np.eye(m * n) else: F[m * n + mW * n:, 0:m * n] = -np.eye(m * n) # populate G W = np.zeros((m * n, 3 * (m + 1) * (n + 1))) Cpp_genW(zeta, m, n, W) dAgam_dZeta = np.zeros((m * n, 3 * (m + 1) * (n + 1))) Cpp_dAgamma0_dZeta(zeta, m, n, gam, zeta, m, n, dAgam_dZeta, imageMeth) dAwGamW_dZeta = np.zeros((m * n, 3 * (m + 1) * (n + 1))) #Cpp_dAgamma0_dZeta(zetaW, mW, n, gamW, zeta, m, n, dAwGamW_dZeta, imageMeth) dWzetaPri_dZeta = np.zeros((m * n, 3 * (m + 1) * (n + 1))) dWnuPri_dZeta = np.zeros((m * n, 3 * (m + 1) * (n + 1))) Cpp_dWzetaPri0_dZeta(zeta, m, n, zetaPri, dWzetaPri_dZeta) Cpp_dWzetaPri0_dZeta(zeta, m, n, nu, dWnuPri_dZeta) G[0:m * n, 0:3 * (m + 1) * (n + 1)] = 2 * W G[0:m * n, 3 * (m + 1) * (n + 1):6 * (m + 1) * ( n + 1 )] = -dAgam_dZeta - dAwGamW_dZeta + 2 * dWzetaPri_dZeta - 2 * dWnuPri_dZeta G[0:m * n, 6 * (m + 1) * (n + 1):] = -2 * W # populate output matrices C and D Xi = np.zeros((3 * m * n, 3 * (m + 1) * (n + 1))) H = np.zeros((3 * (m + 1) * (n + 1), 12 * m * n)) Y1 = np.zeros((12 * m * n, m * n)) Y2 = np.zeros((12 * m * n, 3 * (m + 1) * (n + 1))) Y3 = np.zeros((12 * m * n, 12 * m * n)) Y4 = np.zeros((3 * m * n, m * n)) Y5 = np.zeros((3 * m * n, 3 * (m + 1) * (n + 1))) AICs3 = np.zeros((12 * m * n, m * n)) AICs3w = np.zeros((12 * m * n, mW * n)) dAs3gam_dZeta = np.zeros((12 * m * n, 3 * (m + 1) * (n + 1))) dAs3wGamW_dZeta = np.zeros((12 * m * n, 3 * (m + 1) * (n + 1))) # gen interpolation and AIC matrices Cpp_genXi(m, n, 0.5, 0.5, Xi) Cpp_genH(m, n, H) Cpp_AIC3s(zeta, m, n, zeta, m, n, AICs3, imageMeth) Cpp_AIC3s(zetaW, mW, n, zeta, m, n, AICs3w, imageMeth) Cpp_dAs3gamma0_dZeta_num(zeta, m, n, gam, zeta, m, n, dAs3gam_dZeta, imageMeth) Cpp_dAs3gamma0_dZeta_num(zetaW, mW, n, gamW, zeta, m, n, dAs3wGamW_dZeta, imageMeth) # generate Y matrices vM0 = np.zeros((12 * m * n)) # collocation fluid-grid relative velocities vM0[:] = np.dot(AICs3, gam) + np.dot(AICs3w, gamW) + 2.0 * np.dot( H.transpose(), nu) - 2.0 * np.dot(H.transpose(), zetaPri) Cpp_Y1(vM0, zeta, m, n, Y1) Cpp_Y2(gam, vM0, m, n, Y2) Cpp_Y3(gam, zeta, m, n, Y3) Cpp_Y4(zeta, m, n, Y4) Cpp_Y5(gamPri, zeta, m, n, Y5) # Matrix C C[:, 0:m * n] = np.dot(H, Y1) - np.dot(H, np.dot(Y3, AICs3)) C[:, m * n:m * n + mW * n] = -np.dot(H, np.dot(Y3, AICs3w)) C[:, m * n + mW * n:] = 2.0 * np.dot(np.transpose(Xi), Y4) # Matrix D D[:, 0:3 * (m + 1) * (n + 1)] = 2.0 * np.dot(H, np.dot(Y3, H.transpose())) D[:, 3 * (m + 1) * (n + 1):6 * (m + 1) * (n + 1)] = (np.dot(H, Y2) - np.dot(H, np.dot(Y3, dAs3gam_dZeta + dAs3wGamW_dZeta)) + np.dot(np.transpose(Xi), Y5)) D[:, 6 * (m + 1) * (n + 1):] = -2.0 * np.dot(H, np.dot(Y3, H.transpose())) return E, F, G, C, D