示例#1
0
    def setup_blocks(self, testID, isComplex=False):
        # Make tiny FFD
        ffd_name = '../inputFiles/tiny_cube_{:02d}.xyz'.format(testID)
        file_name = os.path.join(self.base_path, ffd_name)
        self.make_cube_ffd(file_name, 1, 1, 1, 1, 1, 1)
        tiny = DVGeometry(file_name, child=True, complex=isComplex)
        os.remove(file_name)
        tiny.addRefAxis('ref', xFraction=0.5, alignIndex='j', rotType=7)

        # Make tiny FFD
        ffd_name = '../inputFiles/small_cube_{:02d}.xyz'.format(testID)
        file_name = os.path.join(self.base_path, ffd_name)
        self.make_cube_ffd(file_name, 0, 0, 0, 2, 2, 2)
        small = DVGeometry(file_name, child=True, complex=isComplex)
        os.remove(file_name)
        small.addRefAxis('ref', xFraction=0.5, alignIndex='j')

        # Make big FFD
        ffd_name = '../inputFiles/big_cube_{:02d}.xyz'.format(testID)
        file_name = os.path.join(self.base_path, ffd_name)
        self.make_cube_ffd(file_name, 0, 0, 0, 3, 3, 3)
        big = DVGeometry(file_name, complex=isComplex)
        os.remove(file_name)
        big.addRefAxis('ref', xFraction=0.5, alignIndex='i')
        big.addChild(small)
        small.addChild(tiny)

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

        return big, small, tiny
    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)
示例#3
0
文件: run_warp.py 项目: sseraj/idwarp
                          scale=[1.0, 1.0])
# lowerA = [0.,0.,0.,0.]
# upperA = [0.3,0.3,0.3,0.3]
# DVGeoChild.addGeoDVGlobal('angleVars', z1, angleVars,
#                      lower=lowerA, upper=upperA, scale=1.0)

# lowerL = [-1.,-1.,-1.,-1.]
# upperL = [2.0,2.0,2.0,2.0]
# DVGeoChild.addGeoDVGlobal('noseLen', x1, noseLength,
#                      lower=lowerL, upper=upperL, scale=1.0)

# DVGeo.addGeoDVGlobal('angleVars', z1, angleVars,
#                      lower=lowerA, upper=upperA, scale=1.0)

# Add the child to the parent
DVGeo.addChild(DVGeoChild)

ptSetName = "allSurfs"
freezeDict = {
}  # '0':['jLow'],'1':['jLow'],'2':['jLow']}#'0':['jLow'],'1':['jHigh','jLow']}
DVGeo.addPointSet(coords0, ptSetName, faceFreeze=freezeDict)
xDV = DVGeo.getValues()

# Required design variables
# Rear ramp angle, fixed 200 mm length
# overall length
# nose length
# Ramp shape
# Ground separation
# Lower ramp angle
示例#4
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)
示例#5
0
def createFFD():

    # Bounding box for bump
    x_root_range = [-0.1, 3.1]
    y_root_range = [-0.1, 1.1]
    z_root_range = [-0.1, 1.1]

    # Number of FFD control points per dimension
    nX = 10  # streamwise
    nY = 2
    nZ = 2  # only modify the top control points

    # Compute grid points
    span_dist = np.linspace(0, 1, nX)
    x_sections = span_dist * (x_root_range[1] -
                              x_root_range[0]) + x_root_range[0]
    z_sections = z_root_range

    X = np.zeros((nX, nY, nZ))
    Y = np.zeros((nX, nY, nZ))
    Z = np.zeros((nX, nY, nZ))
    for i in range(nX):
        for j in range(nY):
            for k in range(nZ):
                X[i, j, k] = x_sections[i]
                Y[i, j, k] = y_root_range[j]
                Z[i, j, k] = z_sections[k]
    # rst Write
    # Write FFD to file
    filename = "plateffdp.xyz"
    f = open(filename, "w")
    f.write("\t\t1\n")
    f.write("\t\t%d\t\t%d\t\t%d\n" % (nX, nY, nZ))
    for set in [X, Y, Z]:
        for k in range(nZ):
            for j in range(nY):
                for i in range(nX):
                    f.write("\t%3.8f" % (set[i, j, k]))
                f.write("\n")
    f.close()

    # Create the child FFD, actual DVs defined on this

    # Bounding box for bump
    x_root_range_c = optOptions['bumpBounds']
    y_root_range_c = [-0.05, 1.05]
    z_root_range_c = [-0.05, 0.95]

    # Number of FFD control points per dimension
    nXc = optOptions['NX']  # streamwise
    nYc = 2
    nZc = 3  # only modify the top control points

    # Compute grid points
    span_dist_c = np.linspace(0, 1, nXc)
    x_sections_c = span_dist_c * (x_root_range_c[1] -
                                  x_root_range_c[0]) + x_root_range_c[0]
    z_span_dist_c = np.linspace(0, 1, nZc)
    z_sections_c = z_span_dist_c * (z_root_range_c[1] -
                                    z_root_range_c[0]) + z_root_range_c[0]

    Xc = np.zeros((nXc, nYc, nZc))
    Yc = np.zeros((nXc, nYc, nZc))
    Zc = np.zeros((nXc, nYc, nZc))
    for i in range(nXc):
        for j in range(nYc):
            for k in range(nZc):
                Xc[i, j, k] = x_sections_c[i]
                Yc[i, j, k] = y_root_range_c[j]
                Zc[i, j, k] = z_sections_c[k]
    # rst Write
    # Write FFD to file
    filenamec = "plateffdc.xyz"
    fc = open(filenamec, "w")
    fc.write("\t\t1\n")
    fc.write("\t\t%d\t\t%d\t\t%d\n" % (nXc, nYc, nZc))
    for set in [Xc, Yc, Zc]:
        for k in range(nZc):
            for j in range(nYc):
                for i in range(nXc):
                    fc.write("\t%3.8f" % (set[i, j, k]))
                fc.write("\n")
    fc.close()

    # define the design variables

    #add point set here? no
    # meshOptions = warpOptions
    # mesh = USMesh(options=meshOptions)
    # coords = mesh.getSurfaceCoordinates()

    DVGeo = DVGeometry(filename)

    #DVGeo.addPointSet(coords, "coords")
    DVGeoc = DVGeometry(filenamec, child=True)
    DVGeoc.addRefAxis('dummy_axis', xFraction=0.1, alignIndex='i')
    DVGeo.addChild(DVGeoc)

    # local design vars are just the Z-positions of (some) upper control points
    length = x_root_range_c[1] - x_root_range_c[0]
    z_mid = (z_root_range_c[1] + z_root_range_c[0]) / 2
    frac = optOptions['DVFraction']
    P1 = [x_root_range_c[0] + frac * length, 0, z_mid / 2]
    P2 = [x_root_range_c[1] - frac * length, 0, 3 * z_mid / 2]
    PS = geo_utils.PointSelect(psType='y', pt1=P1, pt2=P2)

    #
    vname = "pnts"
    UB = optOptions['DVUpperBound']
    DVGeoc.addGeoDVLocal(dvName=vname,
                         lower=0.0,
                         upper=UB,
                         axis="z",
                         scale=1,
                         pointSelect=PS)

    return DVGeo


# # test out on a mesh
# gridFile = "grid_struct_69x49_vol_mod2.cgns"
# meshOptions = warpOptions
# meshOptions["gridfile"] = gridFile
# mesh = USMesh(options=meshOptions)
# coords = mesh.getSurfaceCoordinates()

# DVGeo.addPointSet(coords, "coords")
# dvDict = DVGeoc.getValues()
# #for i in range(int(nXc/2)):
# dvDict["pnts"][:] = 0.2
# # dvDict["pnts"][2] = 0.2
# # dvDict["pnts"][3] = 0.2
# # dvDict["pnts"][4] = 0.1
# # dvDict["pnts"][5] = 0.1
# # dvDict["pnts"][6] = 0.1
# # dvDict["pnts"][7] = 0.1
# DVGeoc.setDesignVars(dvDict)
# DVGeoc.printDesignVariables()
# new_coords = DVGeo.update("coords")
# DVGeoc.writePlot3d("ffdc_deformed.xyz")
# #DVGeo.writePointSet("coords", "surf")

# # move the mesh using idwarp

# # Reset the newly computed surface coordiantes
# mesh.setSurfaceCoordinates(new_coords)

# # Actually run the mesh warping
# mesh.warpMesh()

# # Write the new grid file.
# mesh.writeGrid(f'ffd_warped.cgns')
示例#6
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)
nTwist_front = nRefAxPts_front - 1

# Create DVGeometry object for back wing
FFDFile_back = 'ffd_back_wing.xyz'
DVGeo_back = DVGeometry(FFDFile_back, child=True)

# Create reference axis for the back wing
nRefAxPts_back = DVGeo_back.addRefAxis('wing_back',
                                       xFraction=0.25,
                                       alignIndex='k')
nTwist_back = nRefAxPts_back - 1

# Set up global DVGeometry object
FFDFile_GLOBAL = 'ffd_global.xyz'
DVGeo_GLOBAL = DVGeometry(FFDFile_GLOBAL)
DVGeo_GLOBAL.addChild(DVGeo_front)
DVGeo_GLOBAL.addChild(DVGeo_back)

#rst dvgeos


# Set up global design variables
def twist_front(val, geo):
    for i in range(1, nRefAxPts_front):
        geo.rot_z['wing_front'].coef[i] = val[i - 1]


def twist_back(val, geo):
    for i in range(1, nRefAxPts_back):
        geo.rot_z['wing_back'].coef[i] = val[i - 1]