def setUp(self): self.curDir = os.path.abspath(os.path.dirname(__file__)) self.datFile = os.path.join(self.curDir, "naca2412.dat") self.rng = np.random.default_rng(1) self.comm = MPI.COMM_WORLD if self.dvName in ["upper", "lower"]: numCST = self.dvNum else: numCST = 4 self.DVGeo = DVGeometryCST(self.datFile, comm=self.comm, isComplex=True, numCST=numCST) # Read in airfoil coordinates (use NACA 2412) coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros( (coords.shape[0], 1)))) # z-coordinates self.coords = coords.astype(complex) idxLE = np.argmin(coords[:, 0]) self.idxUpper = np.arange(0, idxLE) self.idxLower = np.arange(idxLE, coords.shape[0]) self.thickTE = coords[0, 1] - coords[-1, 1] self.ptName = "pt" self.sensTol = 1e-10 self.coordTol = 1e-10 self.CS_delta = 1e-200
def test_addPointSet_bluntTE( self): # includes a blunt trailing edge with points along it # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) nPointsTE = 6 # total points on the trailing edge pointsTE = np.ones((nPointsTE - 2, 3), dtype=float) pointsTE[:, 2] = 0 # z coordinates are zero pointsTE[:, 1] = np.linspace(coords[-1, 1] + 1e-4, coords[0, 1] - 1e-4, pointsTE.shape[0]) coords = np.vstack((coords, pointsTE)) idxLE = np.argmin(coords[:, 0]) # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) idxUpper = np.arange(1, idxLE + self.LEUpper) idxLower = np.arange(idxLE + self.LEUpper, coords.shape[0] - nPointsTE + 2 - 1) thickTE = coords[0, 1] - coords[coords.shape[0] - nPointsTE + 1, 1] self.DVGeo.addPointSet(coords, "test") # Arrays are short so this is fast enough for idx in idxUpper: self.assertIn(idx, self.DVGeo.points["test"]["upper"]) for idx in idxLower: self.assertIn(idx, self.DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, self.DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), self.DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), self.DVGeo.points["test"]["xMax"])
def test_addPointSet_closed(self): curDir = os.path.abspath(os.path.dirname(__file__)) datFile = os.path.join(curDir, "naca0012_closed.dat") comm = MPI.COMM_WORLD DVGeo = DVGeometryCST(datFile, comm=comm) # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) + 1 # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) idxUpper = np.arange(1, idxLE) idxLower = np.arange(idxLE, coords.shape[0] - 2) thickTE = coords[0, 1] - coords[-2, 1] DVGeo.addPointSet(coords, "test") # Arrays are short so this is fast enough for idx in idxUpper: self.assertIn(idx, DVGeo.points["test"]["upper"]) for idx in idxLower: self.assertIn(idx, DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), DVGeo.points["test"]["xMax"]) self.assertFalse(DVGeo.sharp)
def test_addPointSet_randomized(self): # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) idxUpper = np.arange(0, idxLE + self.LEUpper) idxLower = np.arange(idxLE + self.LEUpper, coords.shape[0]) thickTE = coords[0, 1] - coords[-1, 1] # Randomize the index order (do indices so we can track where they end up) rng = np.random.default_rng(1) # Maps from the original index to the new one (e.g., the first value is the index the first coordinate ends up at) idxShuffle = np.arange(0, coords.shape[0]) rng.shuffle(idxShuffle) coordsRand = np.zeros(coords.shape) coordsRand[idxShuffle, :] = coords idxUpperRand = np.sort(idxShuffle[idxUpper]) idxLowerRand = np.sort(idxShuffle[idxLower]) self.DVGeo.addPointSet(coordsRand, "test") # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) # Arrays are short so this is fast enough for idx in idxUpperRand: if idx != idxShuffle[0]: self.assertIn(idx, self.DVGeo.points["test"]["upper"]) for idx in idxLowerRand: if idx != idxShuffle[-1]: self.assertIn(idx, self.DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, self.DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), self.DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), self.DVGeo.points["test"]["xMax"])
def test_fitCST(self): """Test the CST parameter fitting""" # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(os.path.join(self.curDir, self.fName)) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) idxUpper = np.arange(0, idxLE + self.LEUpper) idxLower = np.arange(idxLE + self.LEUpper, coords.shape[0]) yTE = coords[0, 1] N1 = 0.5 N2 = 1.0 for nCST in range(2, 10): # Fit the CST parameters and then compute the coordinates # with those parameters and check that it's close upperCST = DVGeometryCST.computeCSTfromCoords(coords[idxUpper, 0], coords[idxUpper, 1], nCST, N1=N1, N2=N2) lowerCST = DVGeometryCST.computeCSTfromCoords(coords[idxLower, 0], coords[idxLower, 1], nCST, N1=N1, N2=N2) fitCoordsUpper = DVGeometryCST.computeCSTCoordinates( coords[idxUpper, 0], N1, N2, upperCST, yTE) fitCoordsLower = DVGeometryCST.computeCSTCoordinates( coords[idxLower, 0], N1, N2, lowerCST, -yTE) # Loosen the tolerances for the challenging e63 airfoil if self.fName == "e63.dat": if nCST < 4: atol = 1e-1 rtol = 1.0 else: atol = 1e-2 rtol = 6e-1 else: atol = 1e-3 rtol = 1e-1 np.testing.assert_allclose(fitCoordsUpper, coords[idxUpper, 1], atol=atol, rtol=rtol) np.testing.assert_allclose(fitCoordsLower, coords[idxLower, 1], atol=atol, rtol=rtol)
def test_addPointSet_sorted(self): # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) isUpper = np.zeros(coords.shape[0]) isUpper[:idxLE + self.LEUpper] = 1 isUpper = isUpper == 1 isLower = np.logical_not(isUpper) thickTE = coords[0, 1] - coords[-1, 1] # Divide up the points among the procs (mostly evenly, but not quite to check the harder case) if self.N_PROCS == 1: nPerProc = coords.shape[0] else: nPerProc = int(coords.shape[0] // (self.N_PROCS - 0.5)) rank = self.comm.rank if self.comm.rank < self.comm.size - 1: # all but last proc takes nPerProc elements self.DVGeo.addPointSet( coords[rank * nPerProc:(rank + 1) * nPerProc, :], "test") idxUpper = np.where(isUpper[rank * nPerProc:(rank + 1) * nPerProc])[0] idxLower = np.where(isLower[rank * nPerProc:(rank + 1) * nPerProc])[0] else: self.DVGeo.addPointSet(coords[rank * nPerProc:, :], "test") idxUpper = np.where(isUpper[rank * nPerProc:])[0] idxLower = np.where(isLower[rank * nPerProc:])[0] # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) # Arrays are short so this is fast enough for idx in idxUpper: if idx != 0: self.assertIn(idx, self.DVGeo.points["test"]["upper"]) for idx in idxLower: if idx != coords.shape[0] - 1: self.assertIn(idx, self.DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, self.DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), self.DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), self.DVGeo.points["test"]["xMax"])
def test_addPointSet_sorted(self): # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) idxUpper = np.arange(1, idxLE + self.LEUpper) idxLower = np.arange(idxLE + self.LEUpper, coords.shape[0] - 1) thickTE = coords[0, 1] - coords[-1, 1] self.DVGeo.addPointSet(coords, "test") # Arrays are short so this is fast enough for idx in idxUpper: self.assertIn(idx, self.DVGeo.points["test"]["upper"]) for idx in idxLower: self.assertIn(idx, self.DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, self.DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), self.DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), self.DVGeo.points["test"]["xMax"])
def test_addPointSet_randomized(self): # Read in airfoil coordinates to test with and split up the surfaces coords = readCoordFile(self.datFile) coords = np.hstack((coords, np.zeros((coords.shape[0], 1)))) idxLE = np.argmin(coords[:, 0]) isUpper = np.zeros(coords.shape[0]) isUpper[:idxLE + self.LEUpper] = 1 isUpper = isUpper == 1 isLower = np.logical_not(isUpper) thickTE = coords[0, 1] - coords[-1, 1] # Randomize the index order (do indices so we can track where they end up) rng = np.random.default_rng(1) # Maps from the original index to the new one (e.g., the first value is the index the first coordinate ends up at) idxShuffle = np.arange(0, coords.shape[0]) rng.shuffle(idxShuffle) # idxShuffle = (idxShuffle + 10) % coords.shape[0] coordsRand = np.zeros(coords.shape) isUpperRand = np.full(isUpper.shape, False) isLowerRand = np.full(isLower.shape, False) coordsRand[idxShuffle, :] = coords isUpperRand[idxShuffle] = isUpper isLowerRand[idxShuffle] = isLower # Maps from the shuffled index to the original one (e.g., the first value is # the original index of the first value in the shuffled array) idxInverseShuffle = np.zeros(idxShuffle.shape[0]) idxInverseShuffle[idxShuffle] = np.arange(0, coords.shape[0]) # Divide up the points among the procs (mostly evenly, but not quite to check the harder case) nPerProc = int(coordsRand.shape[0] // 3.5) rank = self.comm.rank if self.comm.rank < self.comm.size - 1: # all but last proc takes nPerProc elements self.DVGeo.addPointSet(coordsRand[rank * nPerProc:(rank + 1) * nPerProc, :], "test", rank=self.comm.rank) idxUpper = np.where(isUpperRand[rank * nPerProc:(rank + 1) * nPerProc])[0] idxLower = np.where(isLowerRand[rank * nPerProc:(rank + 1) * nPerProc])[0] # Figure out the local indices where the first and last coordinates in the dat file ended up idxStart = np.argwhere( 0 == idxInverseShuffle[rank * nPerProc:(rank + 1) * nPerProc]) idxEnd = np.argwhere( coords.shape[0] == idxInverseShuffle[rank * nPerProc:(rank + 1) * nPerProc]) else: self.DVGeo.addPointSet(coordsRand[rank * nPerProc:, :], "test", rank=self.comm.rank) idxUpper = np.where(isUpperRand[rank * nPerProc:])[0] idxLower = np.where(isLowerRand[rank * nPerProc:])[0] # Figure out the local indices where the first and last coordinates in the dat file ended up idxStart = np.argwhere(0 == idxInverseShuffle[rank * nPerProc:]) idxEnd = np.argwhere( coords.shape[0] == idxInverseShuffle[rank * nPerProc:]) # Turn the single element array to a number or None if the first # or last points aren't in this partition if idxStart: idxStart = idxStart.item() else: idxStart = None if idxEnd: idxEnd = idxEnd.item() else: idxEnd = None # Don't include the points at the corners of the trailing edge because it's not guaranteed # that they'll be included in the upper and lower surface (which is ok) # Arrays are short so this is fast enough for idx in idxUpper: if idx != idxStart: self.assertIn(idx, self.DVGeo.points["test"]["upper"]) for idx in idxLower: if idx != idxEnd: self.assertIn(idx, self.DVGeo.points["test"]["lower"]) np.testing.assert_equal(thickTE, self.DVGeo.points["test"]["thicknessTE"]) self.assertEqual(min(coords[:, 0]), self.DVGeo.points["test"]["xMin"]) self.assertEqual(max(coords[:, 0]), self.DVGeo.points["test"]["xMax"])