Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add triangulated surface constraint to MPhys wrapper #192

Merged
merged 9 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pygeo/constraints/DVCon.py
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,7 @@ def addThicknessToChordConstraints1D(

def addTriangulatedSurfaceConstraint(
self,
comm,
surface_1_name=None,
DVGeo_1_name="default",
surface_2_name="default",
Expand Down Expand Up @@ -1464,6 +1465,7 @@ def addTriangulatedSurfaceConstraint(

bernardopacini marked this conversation as resolved.
Show resolved Hide resolved
# Finally add constraint object
self.constraints[typeName][conName] = TriangulatedSurfaceConstraint(
comm,
conName,
surface_1,
surface_1_name,
Expand Down
14 changes: 8 additions & 6 deletions pygeo/constraints/areaConstraint.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# External modules
from mpi4py import MPI
import numpy as np

# Local modules
Expand All @@ -22,6 +21,7 @@ class TriangulatedSurfaceConstraint(GeometricConstraint):

def __init__(
self,
comm,
name,
surface_1,
surface_1_name,
Expand All @@ -41,6 +41,8 @@ def __init__(

super().__init__(name, 2, -1e10, 0.0, scale, None, addToPyOpt)

self.comm = comm

# get the point sets
self.surface_1_name = surface_1_name
self.surface_2_name = surface_2_name
Expand Down Expand Up @@ -233,7 +235,7 @@ def evalTriangulatedSurfConstraint(self):
mindist_tmp,
self.rho,
self.maxdim,
MPI.COMM_WORLD.py2f(),
self.comm.py2f(),
)
# second run gets the well-conditioned KS
KS, perim_length, mindist, _, _ = geograd_parallel.compute(
Expand All @@ -246,16 +248,16 @@ def evalTriangulatedSurfConstraint(self):
mindist,
self.rho,
self.maxdim,
MPI.COMM_WORLD.py2f(),
self.comm.py2f(),
)

self.perim_length = perim_length
self.minimum_distance = mindist

if self.perim_length > self.max_perim:
failflag = True
if MPI.COMM_WORLD.rank == 0:
print("Intersection length ", str(perim_length), " exceeds tol, returning fail flag")
if self.comm.rank == 0:
print(f"Intersection length {self.perim_length} exceeds tol {self.max_perim}, returning fail flag")
else:
failflag = False
return KS, perim_length, failflag
Expand All @@ -275,7 +277,7 @@ def evalTriangulatedSurfConstraintSens(self):
self.minimum_distance,
self.rho,
self.maxdim,
MPI.COMM_WORLD.py2f(),
self.comm.py2f(),
)
return deriv_output

Expand Down
51 changes: 43 additions & 8 deletions pygeo/mphys/mphys_dvgeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from mpi4py import MPI
import numpy as np
import openmdao.api as om
from openmdao.api import AnalysisError

# Local modules
from .. import DVConstraints, DVGeometry, DVGeometryESP, DVGeometryVSP
Expand Down Expand Up @@ -72,8 +73,14 @@ def compute(self, inputs, outputs):
constraintfunc = dict()
self.DVCon.evalFunctions(constraintfunc, includeLinear=True)
comm = self.comm
if comm.rank == 0:
for constraintname in constraintfunc:

for constraintname in constraintfunc:
# if any constraint returned a fail flag throw an error to OpenMDAO
# all constraints need the same fail flag, no <name_> prefix
if constraintname == "fail":
raise AnalysisError("Analysis error in geometric constraints")

if comm.rank == 0:
outputs[constraintname] = constraintfunc[constraintname]

# we ran a compute so the inputs changed. update the dvcon jac
Expand Down Expand Up @@ -218,16 +225,16 @@ def nom_addThicknessConstraints2D(self, name, leList, teList, nSpan=10, nChord=1
else:
self.add_output(name, distributed=True, shape=(0,))

def nom_addThicknessConstraints1D(self, name, ptList, nCon, axis):
self.DVCon.addThicknessConstraints1D(ptList, nCon, axis, name=name)
def nom_addThicknessConstraints1D(self, name, ptList, nCon, axis, scaled=True):
bernardopacini marked this conversation as resolved.
Show resolved Hide resolved
self.DVCon.addThicknessConstraints1D(ptList, nCon, axis, name=name, scaled=scaled)
comm = self.comm
if comm.rank == 0:
self.add_output(name, distributed=True, val=np.ones(nCon), shape=nCon)
else:
self.add_output(name, distributed=True, shape=(0))

def nom_addVolumeConstraint(self, name, leList, teList, nSpan=10, nChord=10):
self.DVCon.addVolumeConstraint(leList, teList, nSpan=nSpan, nChord=nChord, name=name)
def nom_addVolumeConstraint(self, name, leList, teList, nSpan=10, nChord=10, surfaceName="default"):
self.DVCon.addVolumeConstraint(leList, teList, nSpan=nSpan, nChord=nChord, name=name, surfaceName=surfaceName)
comm = self.comm
if comm.rank == 0:
self.add_output(name, distributed=True, val=1.0)
Expand Down Expand Up @@ -273,6 +280,32 @@ def nom_addLinearConstraintsShape(self, name, indSetA, indSetB, factorA, factorB
else:
self.add_output(name, distributed=True, shape=0)

def nom_addTriangulatedSurfaceConstraint(
self,
name,
surface_1_name=None,
DVGeo_1_name="default",
surface_2_name="default",
DVGeo_2_name="default",
bernardopacini marked this conversation as resolved.
Show resolved Hide resolved
rho=50.0,
heuristic_dist=None,
max_perim=3.0,
):
self.DVCon.addTriangulatedSurfaceConstraint(
comm=self.comm,
surface_1_name=surface_1_name,
DVGeo_1_name=DVGeo_1_name,
surface_2_name=surface_2_name,
DVGeo_2_name=DVGeo_2_name,
rho=rho,
heuristic_dist=heuristic_dist,
max_perim=max_perim,
name=name,
)

self.add_output(f"{name}_KS", distributed=False, val=0)
self.add_output(f"{name}_perim", distributed=False, val=0)

def nom_addRefAxis(self, childIdx=None, **kwargs):
# references axes are only needed in FFD-based DVGeo objects
if self.geo_type != "ffd":
Expand All @@ -284,9 +317,11 @@ def nom_addRefAxis(self, childIdx=None, **kwargs):
else:
return self.DVGeo.children[childIdx].addRefAxis(**kwargs)

def nom_setConstraintSurface(self, surface):
def nom_setConstraintSurface(
self, surface, name="default", addToDVGeo=False, DVGeoName="default", surfFormat="point-vector"
):
# constraint needs a triangulated reference surface at initialization
self.DVCon.setSurface(surface)
self.DVCon.setSurface(surface, name=name, addToDVGeo=addToDVGeo, DVGeoName=DVGeoName, surfFormat=surfFormat)

def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
# only do the computations when we have more than zero entries in d_inputs in the reverse mode
Expand Down
19 changes: 15 additions & 4 deletions tests/reg_tests/test_DVConstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# External modules
from baseclasses import BaseRegTest
from mpi4py import MPI
import numpy as np
from parameterized import parameterized_class
from stl import mesh
Expand Down Expand Up @@ -140,6 +141,7 @@ def setUp(self):
# 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__))
self.comm = MPI.COMM_WORLD

# Skip multi component test if DVGeometryMulti cannot be imported (i.e. pySurf is not installed)
if self.multi and not pysurfInstalled:
Expand Down Expand Up @@ -847,8 +849,12 @@ def test_triangulatedSurface(self, train=False, refDeriv=False):
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)
DVCon.addTriangulatedSurfaceConstraint(
self.comm, "default", "default", "blob", None, rho=10.0, addToPyOpt=True
)
DVCon.addTriangulatedSurfaceConstraint(
self.comm, "default", "default", "blob", None, rho=1000.0, addToPyOpt=True
)

funcs, funcsSens = generic_test_base(DVGeo, DVCon, handler, fdstep=1e-3)
handler.assert_allclose(
Expand All @@ -864,7 +870,9 @@ def test_triangulatedSurface_intersected(self, train=False, refDeriv=False):
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)
DVCon.addTriangulatedSurfaceConstraint(
self.comm, "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"])
Expand Down Expand Up @@ -967,6 +975,7 @@ def setUp(self):
# 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__))
self.comm = MPI.COMM_WORLD

def test_triangulatedSurface_intersected_2DVGeos(self, train=False, refDeriv=False):
refFile = os.path.join(self.base_path, "ref/test_DVConstraints_triangulatedSurface_intersected_2DVGeos.ref")
Expand Down Expand Up @@ -1017,7 +1026,9 @@ def twist(val, geo):
p0b = p0b + np.array([0.0, 0.3, 0.0])
DVCon.setSurface([p0b, v1b, v2b], name="blob", addToDVGeo=True, DVGeoName="second")

DVCon.addTriangulatedSurfaceConstraint("default", "default", "blob", "second", rho=10.0, addToPyOpt=True)
DVCon.addTriangulatedSurfaceConstraint(
self.comm, "default", "default", "blob", "second", rho=10.0, addToPyOpt=True
)

funcs = {}
DVCon.evalFunctions(funcs, includeLinear=True)
Expand Down