Beispiel #1
0
    def test_spanwise_dvs(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path,
                               "ref/test_Cylinder_spanwise_dvs.ref")

        with BaseRegTest(refFile, train=train) as handler:
            handler.root_print("Test 1: Basic FFD, global DVs")
            radius = 1.0
            height = 10.0

            DVCon = DVConstraints()
            surf = self.make_cylinder_mesh(radius, height)
            DVCon.setSurface(surf)
            # DVCon.writeSurfaceTecplot('cylinder_surface.dat')

            ffd_name = os.path.join(self.base_path,
                                    "../inputFiles/cylinder_ffd.xyz")
            self.make_ffd(ffd_name, radius, height)
            DVGeo = DVGeometry(ffd_name)

            DVGeo.addSpanwiseLocalDV("shape",
                                     "i",
                                     lower=-0.5,
                                     upper=0.5,
                                     axis="y",
                                     scale=1.0)

            size = DVGeo._getNDVSpanwiseLocal()
            DVCon.setDVGeo(DVGeo)

            leList = [[0, 0, 0], [-radius / 2, 0, height]]
            xAxis = [-1, 0, 0]
            yAxis = [0, 1, 0]
            DVCon.addLERadiusConstraints(leList,
                                         nSpan=5,
                                         axis=yAxis,
                                         chordDir=xAxis,
                                         scaled=False)
            # DVCon.writeTecplot('cylinder_constraints.dat')

            funcs = {}
            DVCon.evalFunctions(funcs)
            print(funcs)
            handler.root_add_dict("funcs1", funcs, rtol=1e-6, atol=1e-6)

            np.random.seed(0)
            DVGeo.setDesignVars({"shape": (np.random.rand(size) - 0.5)})

            funcs = {}
            DVCon.evalFunctions(funcs)
            handler.root_add_dict("funcs2", funcs, rtol=1e-6, atol=1e-6)
            print(funcs)

            funcsSens = {}
            DVCon.evalFunctionsSens(funcsSens)
            print(funcsSens)
            handler.root_add_dict("funcsSens", funcsSens, rtol=1e-6, atol=1e-6)
            print(funcsSens)
Beispiel #2
0
    def test_24_rot0_nonaligned(self, train=False, refDeriv=False):
        """
        Test 24
        This test ensures that the scaling attributes (scale_x, scale_y, and scale_z) are effective when rotType=0 is selected.
        Moreover, this test ensures that rotType=0 reference axis can handle (given appropriate input parameters) FFD blocks that are not aligned with the main system of reference, e.g. the blades of a 3-bladed wind turbine rotor.
        The newly added input parameters rot0ang and rot0axis are used to provide the user control on this.
        The operations that pyGeo performs for this test are the following:
        We start from an initial "vertical" FFD box which, using the combination of rotType=0, rot0ang=-90, and rot0axis=[1,0,0] for addRefAxis(), is first rotated to have its "spanwise" axis along the y axis.
        Then, the script scales the 2nd section along the z axis for a "thickness" increase and the 4th section along the x axis for "chord" increase, it adds a +/- 30 deg twist respectively, and finally rotates the deformed FFD back in the initial position.
        The twist is added to ensure that the operation order is maintained, and the scaling preserves the orthogonality of the FFD in the section plane.
        This is a particular case as the FFD box is already aligned with the main axis and we "flip" the y and z axes, but the same criteria can be applied to a general rotation.
        """
        refFile = os.path.join(self.base_path, "ref/test_DVGeometry_24.ref")
        with BaseRegTest(refFile, train=train) as handler:
            handler.root_print("Test twist and scaling for FFDs non-aligned to main system of reference")
            DVGeo = DVGeometry(os.path.join(self.base_path, "../../input_files/2x1x8_rectangle.xyz"))
            rotType = 0
            xfraction = 0.5
            nRefAxPts = DVGeo.addRefAxis("RefAx", xFraction=xfraction, alignIndex="k", rotType=rotType, rot0ang=-90)

            fix_root_sect = 1
            nTwist = nRefAxPts - fix_root_sect

            DVGeo.addGlobalDV(dvName="twist", value=[0] * nTwist, func=commonUtils.twist, lower=-90, upper=90, scale=1)
            DVGeo.addGlobalDV(
                dvName="thickness", value=[1.0] * nTwist, func=commonUtils.thickness, lower=0.7, upper=5.0, scale=1
            )
            DVGeo.addGlobalDV(
                dvName="chord", value=[1.0] * nTwist, func=commonUtils.chord, lower=0.7, upper=5.0, scale=1
            )

            commonUtils.testSensitivities(DVGeo, refDeriv, handler, pointset=2)

            x = DVGeo.getValues()

            # Modifying the twist
            keyName = "twist"
            twistTest = [30, 0, -30]
            x[keyName] = twistTest

            # Modifying the chord
            keyName = "thickness"
            thickTest = [3.0, 1.0, 1.0]
            x[keyName] = thickTest

            # Modifying the chord
            keyName = "chord"
            chordTest = [1.0, 1.0, 2.0]
            x[keyName] = chordTest

            DVGeo.setDesignVars(x)

            DVGeo.update("testPoints")
            FFD_coords = DVGeo.FFD.coef.copy()

            handler.root_add_val("Updated FFD coordinates", FFD_coords, rtol=1e-12, atol=1e-12)
Beispiel #3
0
    def test_1(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, 'ref/test_Cylinder_01.ref')

        with BaseRegTest(refFile, train=train) as handler:
            handler.root_print("Test 1: Basic FFD, global DVs")
            radius = 1.0
            height = 10.0

            DVCon = DVConstraints()
            surf = self.make_cylinder_mesh(radius, height)
            DVCon.setSurface(surf)
            # DVCon.writeSurfaceTecplot('cylinder_surface.dat')

            ffd_name = os.path.join(self.base_path,
                                    '../inputFiles/cylinder_ffd.xyz')
            self.make_ffd(ffd_name, radius, height)
            DVGeo = DVGeometry(ffd_name)
            nAxPts = DVGeo.addRefAxis('thru',
                                      xFraction=0.5,
                                      alignIndex='i',
                                      raySize=1.0)

            def scale_circle(val, geo):
                for i in range(nAxPts):
                    geo.scale['thru'].coef[i] = val[0]

            DVGeo.addGeoDVGlobal('scale_circle', func=scale_circle, value=[1])
            DVCon.setDVGeo(DVGeo)

            leList = [[0, 0, 0], [-radius / 2, 0, height]]
            xAxis = [-1, 0, 0]
            yAxis = [0, 1, 0]
            DVCon.addLERadiusConstraints(leList,
                                         nSpan=5,
                                         axis=yAxis,
                                         chordDir=xAxis,
                                         scaled=False)
            # DVCon.writeTecplot('cylinder_constraints.dat')

            funcs = {}
            DVCon.evalFunctions(funcs)
            print(funcs)
            handler.root_add_dict('funcs1', funcs, rtol=1e-6, atol=1e-6)

            DVGeo.setDesignVars({'scale_circle': 0.5})

            funcs = {}
            DVCon.evalFunctions(funcs)
            handler.root_add_dict('funcs2', funcs, rtol=1e-6, atol=1e-6)
            print(funcs)

            funcsSens = {}
            DVCon.evalFunctionsSens(funcsSens)
            print(funcsSens)
            handler.root_add_dict('funcsSens', funcsSens, rtol=1e-6, atol=1e-6)
            print(funcsSens)
Beispiel #4
0
    def test_spanwise_dvs(self, train=False, refDeriv=False):
        """
        Test spanwise_dvs
        """
        # refFile = os.path.join(self.base_path,'ref/test_DVGeometry_spanwise_dvs.ref')
        # with BaseRegTest(refFile, train=train) as handler:
        #     handler.root_print("Test spanwise local variables writing function")

        meshfile = os.path.join(self.base_path, "../../input_files/c172.stl")
        ffdfile = os.path.join(self.base_path, "../../input_files/c172.xyz")
        testmesh = mesh.Mesh.from_file(meshfile)
        # test mesh dim 0 is triangle index
        # dim 1 is each vertex of the triangle
        # dim 2 is x, y, z dimension

        # create a DVGeo object with a few local thickness variables
        DVGeo = DVGeometry(ffdfile)
        DVGeo.addSpanwiseLocalDV("shape", "i", lower=-0.5, upper=0.5, axis="y", scale=1.0)

        # create a DVConstraints object for the wing
        DVCon = DVConstraints()
        DVCon.setDVGeo(DVGeo)
        p0 = testmesh.vectors[:, 0, :] / 1000
        v1 = testmesh.vectors[:, 1, :] / 1000 - p0
        v2 = testmesh.vectors[:, 2, :] / 1000 - p0
        DVCon.setSurface([p0, v1, v2])

        leList = [[0.7, 0.0, 0.1], [0.7, 0.0, 2.4]]
        teList = [[0.9, 0.0, 0.1], [0.9, 0.0, 2.4]]

        nSpan = 10
        nChord = 10
        name = "thickness_con"
        DVCon.addThicknessConstraints2D(leList, teList, nSpan, nChord, name=name)

        size = DVGeo._getNDVSpanwiseLocal()

        np.random.seed(0)
        DVGeo.setDesignVars({"shape": (np.random.rand(size) - 0.5)})

        funcs = {}
        DVCon.evalFunctions(funcs)
        # print(funcs)

        for i in range(nChord):
            for j in range(nSpan - 1):
                np.testing.assert_allclose(funcs[name][i * nChord + j + 1], funcs[name][i * nChord + j], rtol=2e-15)
Beispiel #5
0
def deform_DVGeo(geo):
    # =========================================================================
    # Setup DVGeometry object
    # =========================================================================
    # rst DVGeometry
    DVGeo = DVGeometry(input_files + "deform_geometry_ffd.xyz")

    # Create reference axis
    nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k")

    # Set the Twist Variable
    def twist(val, geo):
        for i in range(nRefAxPts):
            geo.rot_z["wing"].coef[i] = val[i]

    # Add the Twist Design Variable to DVGeo
    DVGeo.addGlobalDV(dvName="twist",
                      value=[0] * nRefAxPts,
                      func=twist,
                      lower=-10,
                      upper=10,
                      scale=1.0)

    # Get Design Variables
    dvDict = DVGeo.getValues()

    # Set First Twist Section to 5deg
    dvDict["twist"][0] = 5

    # Set Design Variables
    DVGeo.setDesignVars(dvDict)
    # rst DVGeometry (end)

    # =========================================================================
    # Update pyGeo Object and output result
    # =========================================================================
    # rst UpdatePyGeo
    DVGeo.updatePyGeo(geo, "tecplot", "wingNew", nRefU=10, nRefV=10)
Beispiel #6
0
        reg_write(funcsSens['mdo_tutorial_cl']['shape'][0][13], 1e-10,1e-10)
        reg_write(funcsSens['mdo_tutorial_cmz']['shape'][0][13], 1e-10,1e-10)
        reg_write(funcsSens['mdo_tutorial_drag']['shape'][0][13], 1e-10,1e-10)
else:
    # For the complex....we just do successive perturbation
    for ii in range(3):
        xRef = {'twist':[0.0, 0.0], 'span':[0.0], 'shape':numpy.zeros(72, dtype='D')}
        if ii == 0:
            xRef['twist'][0] += h*1j
        elif ii == 1:
            xRef['span'][0] += h*1j
        else:
            xRef['shape'][13] += h*1j

        CFDSolver.resetFlow(ap)
        DVGeo.setDesignVars(xRef)
        CFDSolver(ap, writeSolution=False)
        funcs = {}
        CFDSolver.evalFunctions(ap, funcs)

        if MPI.COMM_WORLD.rank == 0:
            if ii == 0:
                for key in ['cl','cmz','drag']:
                    print('funcs[%s]:'%key)
                    reg_write(numpy.real(funcs['mdo_tutorial_%s'%key]),1e-10,1e-10)

            if ii == 0:
                print('Twist[0] Derivatives:')
            elif ii == 1:
                print('Span Derivatives:')
            elif ii == 2:
    def test_parent_shape_child_rot(self, train=False, refDeriv=False):
        ffd_name = '../../tests/inputFiles/small_cube.xyz'
        self.make_cube_ffd(ffd_name, 0.1, 0.1, 0.1, 0.8, 0.8, 0.8)
        small = DVGeometry(ffd_name, child=True)
        small.addRefAxis('ref', xFraction=0.5, alignIndex='j')

        x0 = 0.0
        y0 = 0.0
        z0 = 0.0
        dx = 1.0
        dy = 1.0
        dz = 1.0

        axes = ['k', 'j', 'i']
        slices = numpy.array(
            # Slice 1
            [
                [[[x0, y0, z0], [x0 + dx, y0, z0], [x0 + 2 * dx, y0, z0]],
                 [[x0, y0 + dy, z0], [x0 + dx, y0 + dy, z0],
                  [x0 + 2 * dx, y0 + dy, z0]]],
                # Slice 2
                [[[x0, y0, z0 + dz], [x0 + dx, y0, z0 + dz],
                  [x0 + 2 * dx, y0, z0 + dz]],
                 [[x0, y0 + dy, z0 + dz], [x0 + dx, y0 + dy, z0 + dz],
                  [x0 + 2 * dx, y0 + dy, z0 + dz]]]
            ],
            dtype='d')

        N0 = [2]
        N1 = [2]
        N2 = [3]
        ffd_name = '../../tests/inputFiles/big_cube.xyz'

        geo_utils.write_wing_FFD_file(ffd_name, slices, N0, N1, N2, axes=axes)
        big = DVGeometry(ffd_name)
        big.addRefAxis('ref', xFraction=0.5, alignIndex='j')
        big.addChild(small)

        # Add point set
        points = numpy.array([[0.5, 0.5, 0.5]])
        big.addPointSet(points, 'X')

        # Add only translation variables
        add_vars(big, 'big', local='z', rotate='y')
        add_vars(small, 'small', rotate='y')

        ang = 45
        ang_r = numpy.deg2rad(ang)

        # Modify design variables
        x = big.getValues()

        # add a local shape change
        x['local_z_big'] = numpy.array(
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5])
        big.setDesignVars(x)
        Xs = big.update('X')

        # add a rotation of the child FFD
        x['rotate_y_small'] = ang
        big.setDesignVars(x)
        Xrot_ffd = big.update('X')

        # the modification caused by the child FFD should be the same as rotating the deformed point of the parent
        # (you would think)
        rot_mat = numpy.array([[numpy.cos(ang_r), 0,
                                numpy.sin(ang_r)], [0, 1, 0],
                               [-numpy.sin(ang_r), 0,
                                numpy.cos(ang_r)]])
        Xrot = numpy.dot(rot_mat, (Xs - points).T) + points.T

        numpy.testing.assert_array_almost_equal(Xrot_ffd.T, Xrot)
Beispiel #8
0
#rst Add local dvs
# Comment out one or the other
DVGeo.addGeoDVLocal('local', lower=-0.5, upper=0.5, axis='y', scale=1)
DVGeo.addGeoDVSectionLocal('slocal',
                           secIndex='k',
                           axis=1,
                           lower=-0.5,
                           upper=0.5,
                           scale=1)

#rst Embed points
gridFile = 'wing_vol.cgns'
meshOptions = {'gridFile': gridFile}
mesh = USMesh(options=meshOptions)
coords = mesh.getSurfaceCoordinates()

DVGeo.addPointSet(coords, 'coords')

#rst Change dvs
dvDict = DVGeo.getValues()
dvDict['twist'] = numpy.linspace(0, 50, nRefAxPts)[1:]
dvDict['dihedral'] = numpy.linspace(0, 3, nRefAxPts)[1:]
dvDict['taper'] = numpy.array([1.2, 0.5])
dvDict['slocal'][::5] = 0.5
DVGeo.setDesignVars(dvDict)

#rst Update
DVGeo.update('coords')
DVGeo.writePlot3d('ffd_deformed.xyz')
DVGeo.writePointSet('coords', 'surf')
Beispiel #9
0
# Case 1: Rear Ramp angle, fixed length
# Case 2: Upper and lower ramp angles, fixed length
# Case 3: Nose length ( Do with global FFD)
# Case 4: Overall length ( Do with global FFD)
# Case 5: Shape

# xDV['length'][2] = 1.75#2.0#1.05
# xDV['angleVars'][2] = 0.15
# xDV['angleVars'][0] = 0.19
# xDV['noseLen'][0] = -0.1
# xDV['angleVars'][1] = 0.18
# xDV['angleVars'][2] = 0.18
# xDV['angleVars'][3] = 0.12

DVGeo.setDesignVars(xDV)
mesh.setSurfaceCoordinates(DVGeo.update(ptSetName))
mesh.warpMesh()
DVGeo.writeTecplot("warpedFFD.dat")
# mesh.writeOFGridTecplot('warped.dat')
mesh.writeGrid()

# # Repeat ================
# #xDV['length'][2] = 1.25#2.0#1.05
# xDV['angleVars'][2] = 0.3

# DVGeo.setDesignVars(xDV)
# #coords = DVGeo.update(ptSetName)
# # for i in range(coords0.shape[0]):
# #     if coords0[i,1]==0:
# #         print 'x',coords[i,:]
Beispiel #10
0
        # Set the chord as well
        geo.scale["wing"].coef[-1] = val[3]

    coords = hyp.getSurfaceCoordinates()
    DVGeo = DVGeometry(ffdFile)
    coef = DVGeo.FFD.vols[0].coef.copy()

    # First determine the reference chord lengths:
    nSpan = coef.shape[2]
    ref = numpy.zeros((nSpan, 3))

    for k in range(nSpan):
        max_x = numpy.max(coef[:, :, k, 0])
        min_x = numpy.min(coef[:, :, k, 0])

        ref[k, 0] = min_x + 0.25 * (max_x - min_x)
        ref[k, 1] = numpy.average(coef[:, :, k, 1])
        ref[k, 2] = numpy.average(coef[:, :, k, 2])

    c0 = Curve(X=ref, k=2)
    DVGeo.addRefAxis("wing", c0)
    DVGeo.addGlobalDV("winglet", [0, 0, 0, 1], winglet, lower=-5, upper=5)
    DVGeo.addPointSet(coords, "coords")
    DVGeo.setDesignVars({"winglet": [1.5, 2.5, -2.0, 0.60]})
    hyp.setSurfaceCoordinates(DVGeo.update("coords"))

# Run and write grid
hyp.run()
hyp.writeCGNS(volumeFile)
Beispiel #11
0
    const_offset = 0.3 * np.ones(10)
    local_perturb = np.cos(np.linspace(0, 4 * np.pi, 10)) / 10 + np.linspace(
        -0.05, 0.05, 10)
    newDV[idx] = const_offset + local_perturb

# add a constant offset (downward) to the upper points, plus a linear ramp and a trigonometric local change
# this will shrink the cylinder height-wise and make it wavy
for idx in [upper_front_idx, upper_rear_idx]:
    const_offset = -0.3 * np.ones(10)
    local_perturb = np.sin(np.linspace(0, 4 * np.pi, 10)) / 20 + np.linspace(
        0.05, -0.10, 10)
    newDV[idx] = const_offset + local_perturb

# we've created an array with design variable perturbations. Now set the FFD control points with them
# and update the point sets so we can see how they changed
DVGeo.setDesignVars({"shape": newDV.copy()})

Xmod = DVGeo.update("cylinder")
FFDmod = DVGeo.update("ffd")

# Create tecplot output that contains the FFD control points, embedded volume, and pointset
DVGeo.writeTecplot(fileName="deformed_embedded.dat", solutionTime=1)

# rst plot
# cast the 3D pointsets to 2D for plotting (ignoring depth)
FFDplt = FFDptset[:, :2]
FFDmodplt = FFDmod[:, :2]
Xptplt = Xpt[:, :2]
Xmodplt = Xmod[:, :2]

# plot the new and deformed pointsets and control points
Beispiel #12
0
        # Set the chord as well
        geo.scale['wing'].coef[-1] = val[3]

    coords = hyp.getSurfaceCoordinates()
    DVGeo = DVGeometry(ffd_file)
    coef = DVGeo.FFD.vols[0].coef.copy()

    # First determine the reference chord lengths:
    nSpan = coef.shape[2]
    ref = numpy.zeros((nSpan, 3))

    for k in range(nSpan):
        max_x = numpy.max(coef[:, :, k, 0])
        min_x = numpy.min(coef[:, :, k, 0])

        ref[k, 0] = min_x + 0.25 * (max_x - min_x)
        ref[k, 1] = numpy.average(coef[:, :, k, 1])
        ref[k, 2] = numpy.average(coef[:, :, k, 2])

    c0 = pySpline.Curve(X=ref, k=2)
    DVGeo.addRefAxis('wing', c0)
    DVGeo.addGeoDVGlobal('winglet', [0, 0, 0, 1], winglet, lower=-5, upper=5)
    DVGeo.addPointSet(coords, 'coords')
    DVGeo.setDesignVars({'winglet': [1.5, 2.5, -2.0, .60]})
    hyp.setSurfaceCoordinates(DVGeo.update('coords'))

# Run and write grid
hyp.run()
hyp.writeCGNS('717.cgns')
Beispiel #13
0
    def test_parent_shape_child_rot(self, train=False, refDeriv=False):
        ffd_name = "../../input_files/small_cube.xyz"
        self.make_cube_ffd(ffd_name, 0.1, 0.1, 0.1, 0.8, 0.8, 0.8)
        small = DVGeometry(ffd_name, child=True)
        small.addRefAxis("ref", xFraction=0.5, alignIndex="j")

        x0 = 0.0
        y0 = 0.0
        z0 = 0.0
        dx = 1.0
        dy = 1.0
        dz = 1.0

        axes = ["k", "j", "i"]
        slices = np.array(
            # Slice 1
            [
                [
                    [[x0, y0, z0], [x0 + dx, y0, z0], [x0 + 2 * dx, y0, z0]],
                    [[x0, y0 + dy, z0], [x0 + dx, y0 + dy, z0],
                     [x0 + 2 * dx, y0 + dy, z0]],
                ],
                # Slice 2
                [
                    [[x0, y0, z0 + dz], [x0 + dx, y0, z0 + dz],
                     [x0 + 2 * dx, y0, z0 + dz]],
                    [[x0, y0 + dy, z0 + dz], [x0 + dx, y0 + dy, z0 + dz],
                     [x0 + 2 * dx, y0 + dy, z0 + dz]],
                ],
            ],
            dtype="d",
        )

        N0 = [2]
        N1 = [2]
        N2 = [3]
        ffd_name = "../../input_files/big_cube.xyz"

        geo_utils.write_wing_FFD_file(ffd_name, slices, N0, N1, N2, axes=axes)
        big = DVGeometry(ffd_name)
        big.addRefAxis("ref", xFraction=0.5, alignIndex="j")
        big.addChild(small)

        # Add point set
        points = np.array([[0.5, 0.5, 0.5]])
        big.addPointSet(points, "X")

        # Add only translation variables
        add_vars(big, "big", local="z", rotate="y")
        add_vars(small, "small", rotate="y")

        ang = 45
        ang_r = np.deg2rad(ang)

        # Modify design variables
        x = big.getValues()

        # add a local shape change
        x["local_z_big"] = np.array(
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5])
        big.setDesignVars(x)
        Xs = big.update("X")

        # add a rotation of the child FFD
        x["rotate_y_small"] = ang
        big.setDesignVars(x)
        Xrot_ffd = big.update("X")

        # the modification caused by the child FFD should be the same as rotating the deformed point of the parent
        # (you would think)
        rot_mat = np.array([[np.cos(ang_r), 0, np.sin(ang_r)], [0, 1, 0],
                            [-np.sin(ang_r), 0,
                             np.cos(ang_r)]])
        Xrot = np.dot(rot_mat, (Xs - points).T) + points.T

        np.testing.assert_array_almost_equal(Xrot_ffd.T, Xrot)
Beispiel #14
0
class RegTestPyGeo(unittest.TestCase):

    N_PROCS = 1

    def setUp(self):
        # Store the path where this current script lives
        # This all paths in the script are relative to this path
        # This is needed to support testflo running directories and files as inputs
        self.base_path = os.path.dirname(os.path.abspath(__file__))

    def generate_dvgeo_dvcon(self, geometry, addToDVGeo=False, intersected=False):
        """
        This function creates the DVGeometry and DVConstraints objects for each geometry used in this class.

        The C172 wing represents a typical use case with twist and shape variables.

        The rectangular box is primarily used to test unscaled constraint function
        values against known values for thickness, volume, and surface area.

        The BWB is used for the triangulated surface and volume constraint tests.

        The RAE 2822 wing is used for the curvature constraint test.
        """

        if geometry == "c172":
            meshFile = os.path.join(self.base_path, "../../input_files/c172.stl")
            ffdFile = os.path.join(self.base_path, "../../input_files/c172.xyz")
            xFraction = 0.25
            meshScale = 1e-3
        elif geometry == "box":
            meshFile = os.path.join(self.base_path, "../../input_files/2x1x8_rectangle.stl")
            ffdFile = os.path.join(self.base_path, "../../input_files/2x1x8_rectangle.xyz")
            xFraction = 0.5
            meshScale = 1
        elif geometry == "bwb":
            meshFile = os.path.join(self.base_path, "../../input_files/bwb.stl")
            ffdFile = os.path.join(self.base_path, "../../input_files/bwb.xyz")
            xFraction = 0.25
            meshScale = 1
        elif geometry == "rae2822":
            ffdFile = os.path.join(self.base_path, "../../input_files/deform_geometry_ffd.xyz")
            xFraction = 0.25

        DVGeo = DVGeometry(ffdFile, child=self.child)
        DVCon = DVConstraints()
        nRefAxPts = DVGeo.addRefAxis("wing", xFraction=xFraction, alignIndex="k")
        self.nTwist = nRefAxPts - 1

        if self.child:
            parentFFD = os.path.join(self.base_path, "../../input_files/parent.xyz")
            self.parentDVGeo = DVGeometry(parentFFD)
            self.parentDVGeo.addChild(DVGeo)
            DVCon.setDVGeo(self.parentDVGeo)
        else:
            DVCon.setDVGeo(DVGeo)

        # Add design variables
        def twist(val, geo):
            for i in range(1, nRefAxPts):
                geo.rot_z["wing"].coef[i] = val[i - 1]

        DVGeo.addGlobalDV(dvName="twist", value=[0] * self.nTwist, func=twist, lower=-10, upper=10, scale=1)
        DVGeo.addLocalDV("local", lower=-0.5, upper=0.5, axis="y", scale=1)

        # RAE 2822 does not have a DVCon surface so we just return
        if geometry == "rae2822":
            return DVGeo, DVCon

        # Get the mesh from the STL
        testMesh = mesh.Mesh.from_file(meshFile)
        # dim 0 is triangle index
        # dim 1 is each vertex of the triangle
        # dim 2 is x, y, z dimension

        p0 = testMesh.vectors[:, 0, :] * meshScale
        v1 = testMesh.vectors[:, 1, :] * meshScale - p0
        v2 = testMesh.vectors[:, 2, :] * meshScale - p0
        DVCon.setSurface([p0, v1, v2], addToDVGeo=addToDVGeo)

        # Add the blob surface for the BWB
        if geometry == "bwb":
            objFile = os.path.join(self.base_path, "../../input_files/blob_bwb_wing.stl")
            testObj = mesh.Mesh.from_file(objFile)
            p0b = testObj.vectors[:, 0, :]
            v1b = testObj.vectors[:, 1, :] - p0b
            v2b = testObj.vectors[:, 2, :] - p0b
            if intersected:
                p0b = p0b + np.array([0.0, 0.3, 0.0])
            DVCon.setSurface([p0b, v1b, v2b], name="blob")

        return DVGeo, DVCon

    def wing_test_twist(self, DVGeo, DVCon, handler):
        funcs = {}
        funcsSens = {}
        # change the DVs
        xDV = DVGeo.getValues()
        xDV["twist"] = np.linspace(0, 10, len(xDV["twist"]))
        if self.child:
            # Twist needs to be set on the parent FFD to get accurate derivatives
            self.parentDVGeo.setDesignVars(xDV)
        else:
            DVGeo.setDesignVars(xDV)
        # check the constraint values changed
        DVCon.evalFunctions(funcs, includeLinear=True)

        handler.root_add_dict("funcs_twisted", funcs, rtol=1e-6, atol=1e-6)
        # check the derivatives are still right
        DVCon.evalFunctionsSens(funcsSens, includeLinear=True)
        # regress the derivatives
        handler.root_add_dict("derivs_twisted", funcsSens, rtol=1e-6, atol=1e-6)
        return funcs, funcsSens

    def wing_test_deformed(self, DVGeo, DVCon, handler):
        funcs = {}
        funcsSens = {}
        xDV = DVGeo.getValues()
        np.random.seed(37)
        xDV["local"] = np.random.normal(0.0, 0.05, len(xDV["local"]))
        DVGeo.setDesignVars(xDV)
        DVCon.evalFunctions(funcs, includeLinear=True)
        DVCon.evalFunctionsSens(funcsSens, includeLinear=True)
        handler.root_add_dict("funcs_deformed", funcs, rtol=1e-6, atol=1e-6)
        handler.root_add_dict("derivs_deformed", funcsSens, rtol=1e-6, atol=1e-6)
        return funcs, funcsSens

    def test_thickness1D(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_thickness1D.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            ptList = [[0.8, 0.0, 0.1], [0.8, 0.0, 5.0]]
            DVCon.addThicknessConstraints1D(ptList, nCon=10, axis=[0, 1, 0])

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, checkDerivs=True)
            # 1D thickness should be all ones at the start
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(10), name="thickness_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            # 1D thickness shouldn't change much under only twist
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(10), name="thickness_twisted", rtol=1e-2, atol=1e-2
            )

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_thickness1D_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_thickness1D_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            ptList = [[0.0, 0.0, 0.1], [0.0, 0.0, 5.0]]
            ptList2 = [[-0.5, 0.0, 2.0], [0.5, 0.0, 2.0]]
            DVCon.addThicknessConstraints1D(ptList, nCon=3, axis=[0, 1, 0], scaled=False)
            DVCon.addThicknessConstraints1D(ptList, nCon=3, axis=[1, 0, 0], scaled=False)
            DVCon.addThicknessConstraints1D(ptList2, nCon=3, axis=[0, 0, 1], scaled=False)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Check that unscaled thicknesses are computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(3), name="thickness_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_1"], 2.0 * np.ones(3), name="thickness_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_2"], 8.0 * np.ones(3), name="thickness_base", rtol=1e-7, atol=1e-7
            )

    def test_thickness2D(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_thickness2D.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            leList = [[0.7, 0.0, 0.1], [0.7, 0.0, 5.0]]
            teList = [[0.9, 0.0, 0.1], [0.9, 0.0, 5.0]]

            DVCon.addThicknessConstraints2D(leList, teList, 5, 5)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # 2D thickness should be all ones at the start
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(25), name="thickness_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            # 2D thickness shouldn't change much under only twist
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(25), name="thickness_twisted", rtol=1e-2, atol=1e-2
            )

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_thickness2D_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_thickness2D_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            leList = [[-0.25, 0.0, 0.1], [-0.25, 0.0, 7.9]]
            teList = [[0.75, 0.0, 0.1], [0.75, 0.0, 7.9]]

            leList2 = [[0.0, -0.25, 0.1], [0.0, -0.25, 7.9]]
            teList2 = [[0.0, 0.25, 0.1], [0.0, 0.25, 7.9]]

            leList3 = [[-0.5, -0.25, 0.1], [0.5, -0.25, 0.1]]
            teList3 = [[-0.5, 0.25, 0.1], [0.5, 0.25, 0.1]]

            DVCon.addThicknessConstraints2D(leList, teList, 2, 2, scaled=False)
            DVCon.addThicknessConstraints2D(leList2, teList2, 2, 2, scaled=False)
            DVCon.addThicknessConstraints2D(leList3, teList3, 2, 2, scaled=False)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Check that unscaled thicknesses are computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_0"], np.ones(4), name="thickness_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_1"], 2.0 * np.ones(4), name="thickness_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_thickness_constraints_2"], 8.0 * np.ones(4), name="thickness_base", rtol=1e-7, atol=1e-7
            )

    def test_volume(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_volume.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            leList = [[0.7, 0.0, 0.1], [0.7, 0.0, 5.0]]
            teList = [[0.9, 0.0, 0.1], [0.9, 0.0, 5.0]]

            DVCon.addVolumeConstraint(leList, teList, 5, 5)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Volume should be normalized to 1 at the start
            handler.assert_allclose(
                funcs["DVCon1_volume_constraint_0"], np.ones(1), name="volume_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            # Volume shouldn't change much with twist only
            handler.assert_allclose(
                funcs["DVCon1_volume_constraint_0"], np.ones(1), name="volume_twisted", rtol=1e-2, atol=1e-2
            )

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_volume_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_volume_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            # this projects in the z direction which is of dimension 8
            # 1x0.5x8 = 4
            leList = [[-0.5, -0.25, 0.1], [0.5, -0.25, 0.1]]
            teList = [[-0.5, 0.25, 0.1], [0.5, 0.25, 0.1]]

            DVCon.addVolumeConstraint(leList, teList, 4, 4, scaled=False)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Check that unscaled volume is computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_volume_constraint_0"], 4.0 * np.ones(1), name="volume_base", rtol=1e-7, atol=1e-7
            )

    def test_LeTe(self, train=False, refDeriv=False):
        """
        LeTe constraint test using the iLow, iHigh method
        """
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_LeTe.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            if self.child:
                DVCon.addLeTeConstraints(0, "iLow", childIdx=0)
                DVCon.addLeTeConstraints(0, "iHigh", childIdx=0)
            else:
                DVCon.addLeTeConstraints(0, "iLow")
                DVCon.addLeTeConstraints(0, "iHigh")

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # LeTe constraints should be all zero at the start
            for i in range(2):
                handler.assert_allclose(
                    funcs["DVCon1_lete_constraint_" + str(i)], np.zeros(4), name="lete_" + str(i), rtol=1e-7, atol=1e-7
                )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            # Global DVs should produce no change, especially twist
            for i in range(2):
                handler.assert_allclose(
                    funcs["DVCon1_lete_constraint_" + str(i)],
                    np.zeros(4),
                    name="lete_twisted_" + str(i),
                    rtol=1e-7,
                    atol=1e-7,
                )
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_thicknessToChord(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_thicknessToChord.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            ptList = [[0.8, 0.0, 0.1], [0.8, 0.0, 5.0]]
            DVCon.addThicknessToChordConstraints1D(ptList, nCon=10, axis=[0, 1, 0], chordDir=[1, 0, 0])

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_thickness_to_chord_constraints_0"], np.ones(10), name="toverc_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_thickness_to_chord_constraints_0"],
                np.ones(10),
                name="toverc_twisted",
                rtol=1e-3,
                atol=1e-3,
            )

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_surfaceArea(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_surfaceArea.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            DVCon.addSurfaceAreaConstraint()

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_surfaceArea_constraints_0"], np.ones(1), name="surface_area_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_surfaceArea_constraints_0"], np.ones(1), name="surface_area_twisted", rtol=1e-3, atol=1e-3
            )

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_surfaceArea_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_surfaceArea_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            DVCon.addSurfaceAreaConstraint(scaled=False)
            # 2x1x8 box has surface area 2*(8*2+1*2+8*1) = 52
            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_surfaceArea_constraints_0"],
                52.0 * np.ones(1),
                name="surface_area_base",
                rtol=1e-7,
                atol=1e-7,
            )

    def test_projectedArea(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_projectedArea.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            DVCon.addProjectedAreaConstraint()

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_projectedArea_constraints_0"],
                np.ones(1),
                name="projected_area_base",
                rtol=1e-7,
                atol=1e-7,
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)

            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_projectedArea_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_projectedArea_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            DVCon.addProjectedAreaConstraint(scaled=False)
            DVCon.addProjectedAreaConstraint(axis="z", scaled=False)
            DVCon.addProjectedAreaConstraint(axis="x", scaled=False)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, checkDerivs=False)
            handler.assert_allclose(
                funcs["DVCon1_projectedArea_constraints_0"],
                8 * 2 * np.ones(1),
                name="projected_area_base",
                rtol=1e-7,
                atol=1e-7,
            )
            handler.assert_allclose(
                funcs["DVCon1_projectedArea_constraints_1"],
                1 * 2 * np.ones(1),
                name="projected_area_base",
                rtol=1e-7,
                atol=1e-7,
            )
            handler.assert_allclose(
                funcs["DVCon1_projectedArea_constraints_2"],
                8 * 1 * np.ones(1),
                name="projected_area_base",
                rtol=1e-7,
                atol=1e-7,
            )

    def test_circularity(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_circularity.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            DVCon.addCircularityConstraint(
                origin=[0.8, 0.0, 2.5],
                rotAxis=[0.0, 0.0, 1.0],
                radius=0.1,
                zeroAxis=[0.0, 1.0, 0.0],
                angleCW=180.0,
                angleCCW=180.0,
                nPts=10,
            )

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_circularity_constraints_0"], np.ones(9), name="circularity_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_circularity_constraints_0"], np.ones(9), name="circularity_twisted", rtol=1e-7, atol=1e-7
            )
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_colinearity(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_colinearity.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            DVCon.addColinearityConstraint(
                np.array([0.7, 0.0, 1.0]), lineAxis=np.array([0.0, 0.0, 1.0]), distances=[0.0, 1.0, 2.5]
            )

            # Skip derivatives check here because true zero values cause difficulties for the partials
            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, checkDerivs=False)
            handler.assert_allclose(
                funcs["DVCon1_colinearity_constraints_0"], np.zeros(3), name="colinearity_base", rtol=1e-7, atol=1e-7
            )

            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_linearConstraintShape(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_linearConstraintShape.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")
            lIndex = DVGeo.getLocalIndex(0)
            indSetA = []
            indSetB = []
            for i in range(lIndex.shape[0]):
                indSetA.append(lIndex[i, 0, 0])
                indSetB.append(lIndex[i, 0, 1])
            if self.child:
                DVCon.addLinearConstraintsShape(
                    indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0, childIdx=0
                )
            else:
                DVCon.addLinearConstraintsShape(indSetA, indSetB, factorA=1.0, factorB=-1.0, lower=0, upper=0)
            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_compositeVolume_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_compositeVolume_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            # this projects in the z direction which is of dimension 8
            # 1x0.5x8 = 4
            leList = [[-0.5, -0.25, 0.1], [0.5, -0.25, 0.1]]
            teList = [[-0.5, 0.25, 0.1], [0.5, 0.25, 0.1]]

            # this projects in the x direction which is of dimension 2
            # 2x0.6x7.8 = 9.36
            leList2 = [[0.0, -0.25, 0.1], [0.0, -0.25, 7.9]]
            teList2 = [[0.0, 0.35, 0.1], [0.0, 0.35, 7.9]]
            DVCon.addVolumeConstraint(leList, teList, 4, 4, scaled=False, addToPyOpt=False)
            DVCon.addVolumeConstraint(leList2, teList2, 4, 4, scaled=False, addToPyOpt=False)
            vols = ["DVCon1_volume_constraint_0", "DVCon1_volume_constraint_1"]
            DVCon.addCompositeVolumeConstraint(vols=vols, scaled=False)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Check that unscaled volumes are computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_volume_constraint_0"], 4.0 * np.ones(1), name="volume1_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_volume_constraint_1"], 9.36 * np.ones(1), name="volume2_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_composite_volume_constraint_2"],
                13.36 * np.ones(1),
                name="volume_composite_base",
                rtol=1e-7,
                atol=1e-7,
            )

    def test_location_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_location_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            ptList = [[0.0, 0.0, 0.0], [0.0, 0.0, 8.0]]
            ptList2 = [[0.0, 0.2, 0.0], [0.0, -0.2, 8.0]]

            # TODO this constraint seems buggy. for example, when scaled, returns a bunch of NaNs
            DVCon.addLocationConstraints1D(ptList=ptList, nCon=10, scaled=False)
            DVCon.addProjectedLocationConstraints1D(ptList=ptList2, nCon=10, scaled=False, axis=[0.0, 1.0, 0.0])
            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)

            exact_vals = np.zeros((30,))
            exact_vals[2::3] = np.linspace(0, 8, 10)
            # should be 10 evenly spaced points along the z axis originating from 0,0,0
            handler.assert_allclose(
                funcs["DVCon1_location_constraints_0"], exact_vals, name="locations_match", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(
                funcs["DVCon1_location_constraints_1"],
                exact_vals,
                name="projected_locations_match",
                rtol=1e-7,
                atol=1e-7,
            )

    def test_planarity_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_planarity_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            DVCon.addPlanarityConstraint(origin=[0.0, 0.5, 0.0], planeAxis=[0.0, 1.0, 0.0])
            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)

    def test_planarity_tri(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_planarity_tri.ref")
        with BaseRegTest(refFile, train=train) as handler:
            # Set up a dummy DVGeo and DVCon
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box")

            # Set a DVConstraints surface consisting of a simple plane with 2 triangles
            p0 = np.zeros(shape=(2, 3))
            p1 = np.zeros(shape=(2, 3))
            p2 = np.zeros(shape=(2, 3))

            vertex1 = np.array([0.5, -0.25, 0.0])
            vertex2 = np.array([0.5, -0.25, 4.0])
            vertex3 = np.array([-0.5, -0.25, 0.0])
            vertex4 = np.array([-0.5, -0.25, 4.0])

            p0[:, :] = vertex1
            p2[:, :] = vertex4
            p1[0, :] = vertex2
            p1[1, :] = vertex3

            v1 = p1 - p0
            v2 = p2 - p0
            DVCon.setSurface([p0, v1, v2], name="tri")

            DVCon.addPlanarityConstraint(origin=[0.0, -0.25, 2.0], planeAxis=[0.0, 1.0, 0.0], surfaceName="tri")

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, checkDerivs=False)

            # this should be coplanar and the planarity constraint should be zero
            handler.assert_allclose(
                funcs["DVCon1_planarity_constraints_0"], np.zeros(1), name="planarity", rtol=1e-7, atol=1e-7
            )

    def test_monotonic(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_monotonic.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            if self.child:
                DVCon.addMonotonicConstraints("twist", childIdx=0)
                DVCon.addMonotonicConstraints("twist", start=1, stop=2, childIdx=0)
            else:
                DVCon.addMonotonicConstraints("twist")
                DVCon.addMonotonicConstraints("twist", start=1, stop=2)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_monotonic_constraint_0"], np.zeros(2), name="monotonicity", rtol=1e-7, atol=1e-7
            )
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            handler.assert_allclose(
                funcs["DVCon1_monotonic_constraint_0"],
                -5.0 * np.ones(2),
                name="monotonicity_twisted",
                rtol=1e-7,
                atol=1e-7,
            )

            funcs = {}
            funcsSens = {}
            # change the DVs arbitrarily
            xDV = DVGeo.getValues()
            xDV["twist"][0] = 1.0
            xDV["twist"][1] = -3.5
            xDV["twist"][2] = -2.5
            DVGeo.setDesignVars(xDV)
            # check the constraint values changed
            DVCon.evalFunctions(funcs, includeLinear=True)
            handler.root_add_dict("funcs_arb_twist", funcs, rtol=1e-6, atol=1e-6)
            # check the derivatives are still right
            DVCon.evalFunctionsSens(funcsSens, includeLinear=True)
            # regress the derivatives
            handler.root_add_dict("derivs_arb_twist", funcsSens, rtol=1e-6, atol=1e-6)
            handler.assert_allclose(
                funcs["DVCon1_monotonic_constraint_0"],
                np.array([4.5, -1.0]),
                name="monotonicity_arb_twist",
                rtol=1e-7,
                atol=1e-7,
            )
            handler.assert_allclose(
                funcs["DVCon1_monotonic_constraint_1"],
                np.array([-1.0]),
                name="monotonicity_arb_twist_1",
                rtol=1e-7,
                atol=1e-7,
            )

    @unittest.skipIf(missing_geograd, "requires geograd")
    def test_triangulatedSurface(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_triangulatedSurface.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("bwb", addToDVGeo=True)

            DVCon.addTriangulatedSurfaceConstraint("default", "default", "blob", None, rho=10.0, addToPyOpt=True)
            DVCon.addTriangulatedSurfaceConstraint("default", "default", "blob", None, rho=1000.0, addToPyOpt=True)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, fdstep=1e-3)
            handler.assert_allclose(
                funcs["DVCon1_trisurf_constraint_0_KS"], 0.34660627481696404, name="KS", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(funcs["DVCon1_trisurf_constraint_0_perim"], 0.0, name="perim", rtol=1e-7, atol=1e-7)
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    @unittest.skipIf(missing_geograd, "requires geograd")
    def test_triangulatedSurface_intersected(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_triangulatedSurface_intersected.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("bwb", addToDVGeo=True, intersected=True)

            DVCon.addTriangulatedSurfaceConstraint("default", "default", "blob", None, rho=10.0, addToPyOpt=True)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            np.testing.assert_array_less(np.zeros(1), funcs["DVCon1_trisurf_constraint_0_perim"])

    def test_triangulatedVolume_box(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_triangulatedVolume_box.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("box", addToDVGeo=True)

            DVCon.addTriangulatedVolumeConstraint(scaled=False, name="unscaled_vol_con")
            DVCon.addTriangulatedVolumeConstraint(scaled=True)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            # Check that the scaled and unscaled volumes are computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_trivolume_constraint_1"], 1.0, name="scaled_volume_base", rtol=1e-7, atol=1e-7
            )
            handler.assert_allclose(funcs["unscaled_vol_con"], 16.0, name="unscaled_volume_base", rtol=1e-7, atol=1e-7)

    def test_triangulatedVolume_bwb(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_triangulatedVolume_bwb.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("bwb", addToDVGeo=True)

            DVCon.addTriangulatedVolumeConstraint(scaled=False, name="unscaled_vol_con")
            DVCon.addTriangulatedVolumeConstraint(scaled=True)

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)

            # Check that the scaled and unscaled volumes are computed correctly at baseline
            handler.assert_allclose(
                funcs["DVCon1_trivolume_constraint_1"], 1.0, name="scaled_volume_base", rtol=1e-7, atol=1e-7
            )
            # BWB volume computed with meshmixer
            handler.assert_allclose(
                funcs["unscaled_vol_con"], 1103.57, name="unscaled_volume_base", rtol=1e-7, atol=1e-7
            )

    def test_curvature(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_curvature.ref")
        with BaseRegTest(refFile, train=train) as handler:

            # Use the RAE 2822 wing because we have a PLOT3D surface file for it
            DVGeo, DVCon = self.generate_dvgeo_dvcon("rae2822", addToDVGeo=True)
            surfFile = os.path.join(self.base_path, "../../input_files/deform_geometry_wing.xyz")

            # Add both scaled and unscaled curvature constraints
            DVCon.addCurvatureConstraint(surfFile, curvatureType="mean")
            DVCon.addCurvatureConstraint(surfFile, curvatureType="mean", scaled=False, name="unscaled_curvature_con")

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, fdstep=1e-5)
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_curvature1D(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_curvature1D.ref")
        with BaseRegTest(refFile, train=train) as handler:

            # Use the RAE 2822 wing because we have a PLOT3D surface file for it
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172", addToDVGeo=True)

            # Add both scaled and unscaled curvature constraints
            startP = [0.1, 0, 1.0]
            endP = [1.5, 0, 1.0]
            axis = [0, 1, 0]
            nPts = 10
            DVCon.addCurvatureConstraint1D(startP, endP, nPts, axis, "mean")
            DVCon.addCurvatureConstraint1D(
                startP, endP, nPts, axis, "mean", scaled=False, name="unscaled_curvature_con"
            )
            DVCon.addCurvatureConstraint1D(startP, endP, nPts, axis, "aggregated", KSCoeff=10.0)
            DVCon.addCurvatureConstraint1D(
                startP, endP, nPts, axis, "aggregated", KSCoeff=10.0, scaled=False, name="unscaled_curvature_con"
            )

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, checkDerivs=False)
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)

    def test_LERadius(self, train=False, refDeriv=False):
        refFile = os.path.join(self.base_path, "ref/test_DVConstraints_LERadius.ref")
        with BaseRegTest(refFile, train=train) as handler:
            DVGeo, DVCon = self.generate_dvgeo_dvcon("c172")

            leList = [[1e-4, 0, 1e-3], [1e-3, 0, 2.5], [0.15, 0, 5.0]]

            # Add both scaled and unscaled LE radius constraints
            DVCon.addLERadiusConstraints(leList, 5, [0, 1, 0], [-1, 0, 0])
            DVCon.addLERadiusConstraints(leList, 5, [0, 1, 0], [-1, 0, 0], scaled=False, name="unscaled_radius_con")

            funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_twist(DVGeo, DVCon, handler)
            funcs, funcsSens = self.wing_test_deformed(DVGeo, DVCon, handler)
Beispiel #15
0
def test6():
    # ****************************************************************************
    printHeader('MDO tutorial RANS Geometric Variables')
    # ****************************************************************************
    aeroOptions = copy.deepcopy(defOpts)

    # Now set the options that need to be overwritten for this example:
    aeroOptions.update(
        {'gridfile': '../inputFiles/mdo_tutorial_rans.cgns',
         'mgcycle':'2w',
         'equationtype':'RANS',
         'smoother':'dadi',
         'nsubiterturb':3,
         'nsubiter':3,
         'cfl':1.5,
         'cflcoarse':1.25,
         'ncyclescoarse':250,
         'ncycles':750,
         'monitorvariables':['resrho','resturb','cl','cd','cmz','yplus','totalr'],
         'usenksolver':True,
         'l2convergence':1e-17,
         'l2convergencecoarse':1e-2,
         'nkswitchtol':1e-4,
         'adjointl2convergence': 1e-16,
         'nkls': 'non monotone',
         'frozenturbulence':False,
         'nkjacobianlag':2,
     }
    )

    # Setup aeroproblem, cfdsolver
    ap = AeroProblem(name='mdo_tutorial', alpha=1.8, mach=0.80, 
                     altitude=10000.0, areaRef=45.5, chordRef=3.25, evalFuncs=['cl','cmz','drag'])
    
    ap.addDV('alpha')
    ap.addDV('mach')
    CFDSolver = ADFLOW(options=aeroOptions)
    if 'complex' in sys.argv:
        DVGeo = DVGeometry('../inputFiles/mdo_tutorial_ffd.fmt', complex=True)
    else:
        DVGeo = DVGeometry('../inputFiles/mdo_tutorial_ffd.fmt', complex=False)

    nTwist = 2
    DVGeo.addRefAxis('wing', pyspline.Curve(x=numpy.linspace(5.0/4.0, 1.5/4.0+7.5, nTwist), 
                                            y=numpy.zeros(nTwist),
                                            z=numpy.linspace(0,14, nTwist), k=2))
    def twist(val, geo):
        for i in xrange(nTwist):
            geo.rot_z['wing'].coef[i] = val[i]

    def span(val, geo):
        # Span
        C = geo.extractCoef('wing')
        s = geo.extractS('wing')
        for i in xrange(len(C)-1):
            C[-1, 2] = C[-1, 2] + val[0]
        geo.restoreCoef(C, 'wing')

    DVGeo.addGeoDVGlobal('twist', [0]*nTwist, twist, lower=-10, upper=10, scale=1.0)
    DVGeo.addGeoDVGlobal('span', [0], span, lower=-10, upper=10, scale=1.0)
    DVGeo.addGeoDVLocal('shape', lower=-0.5, upper=0.5, axis='y', scale=10.0)
    mesh = MBMesh(options={'gridFile':'../inputFiles/mdo_tutorial_rans.cgns'})
    CFDSolver.setMesh(mesh)
    CFDSolver.setDVGeo(DVGeo)
    #Aeroproblem must be set before we can call DVGeo.setDesignVars
    CFDSolver.setAeroProblem(ap)
    if not 'complex' in sys.argv:
        # Solve system
        CFDSolver(ap, writeSolution=False)
        funcs = {}
        CFDSolver.evalFunctions(ap, funcs)
        # Solve sensitivities
        funcsSens = {}
        CFDSolver.evalFunctionsSens(ap, funcsSens)

        # Write values and derivatives out:
        if MPI.COMM_WORLD.rank == 0:
            for key in ['cl','cmz','drag']:
                print 'funcs[%s]:'%key
                reg_write(funcs['mdo_tutorial_%s'%key],1e-10,1e-10)
            # Now write the derivatives in the same order the CS will do them:
            print ('Twist[0] Derivatives:')
            reg_write(funcsSens['mdo_tutorial_cl']['twist'][0][0], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_cmz']['twist'][0][0], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_drag']['twist'][0][0], 1e-10,1e-10)

            print ('Span Derivatives:')
            reg_write(funcsSens['mdo_tutorial_cl']['span'][0], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_cmz']['span'][0], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_drag']['span'][0], 1e-10,1e-10)

            print ('shape[13] Derivatives:')
            reg_write(funcsSens['mdo_tutorial_cl']['shape'][0][13], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_cmz']['shape'][0][13], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_drag']['shape'][0][13], 1e-10,1e-10)

            print ('mach Derivatives:')
            reg_write(funcsSens['mdo_tutorial_cl']['mach_mdo_tutorial'], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_cmz']['mach_mdo_tutorial'], 1e-10,1e-10)
            reg_write(funcsSens['mdo_tutorial_drag']['mach_mdo_tutorial'], 1e-10,1e-10)

    else:
        # For the complex....we just do successive perturbation
        for ii in range(4):
            xRef = {'twist':[0.0, 0.0], 'span':[0.0], 'shape':numpy.zeros(72, dtype='D'), 'mach_mdo_tutorial':0.8}
            if ii == 0:
                xRef['twist'][0] += h*1j
            elif ii == 1:      
                xRef['span'][0] += h*1j
            elif ii == 2:
                xRef['shape'][13] += h*1j
            else:
                xRef['mach_mdo_tutorial']+=h*1j

            ap.setDesignVars(xRef)
            CFDSolver.resetFlow(ap)
            DVGeo.setDesignVars(xRef)
            CFDSolver(ap, writeSolution=False)
            funcs = {}
            CFDSolver.evalFunctions(ap, funcs)

            if MPI.COMM_WORLD.rank == 0:
                if ii == 0:
                    for key in ['cl','cmz','drag']:
                        print 'funcs[%s]:'%key
                        reg_write(numpy.real(funcs['mdo_tutorial_%s'%key]),1e-10,1e-10)

                if ii == 0:
                    print ('Twist[0] Derivatives:')
                elif ii == 1:
                    print ('Span Derivatives:')
                elif ii == 2:
                    print ('shape[13] Derivatives:')
                elif ii == 3:
                    print ('mach Derivatives:')

                for key in ['cl','cmz','drag']:
                    deriv = numpy.imag(funcs['mdo_tutorial_%s'%key])/h
                    reg_write(deriv,1e-10,1e-10)

    del CFDSolver
    del mesh