Skip to content

Commit

Permalink
Merge branch 'main' into mphys_cons_fix
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardopacini committed Mar 14, 2023
2 parents 9785fdf + 5b7066e commit 831e51d
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 40 deletions.
4 changes: 4 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(

# Finally add constraint object
self.constraints[typeName][conName] = TriangulatedSurfaceConstraint(
comm,
conName,
surface_1,
surface_1_name,
Expand Down Expand Up @@ -3139,6 +3141,8 @@ def addMonotonicConstraints(
self, key, slope=1.0, name=None, start=0, stop=-1, config=None, childIdx=None, comp=None, DVGeoName="default"
):
"""
Add monotonic constraints to a given design variable.
Parameters
----------
key : str
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
47 changes: 41 additions & 6 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 @@ -71,7 +72,13 @@ def compute(self, inputs, outputs):
# compute the DVCon constraint values
constraintfunc = dict()
self.DVCon.evalFunctions(constraintfunc, includeLinear=True)
comm = self.comm

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")
outputs[constraintname] = constraintfunc[constraintname]

# we ran a compute so the inputs changed. update the dvcon jac
Expand Down Expand Up @@ -212,12 +219,12 @@ def nom_addThicknessConstraints2D(self, name, leList, teList, nSpan=10, nChord=1
self.DVCon.addThicknessConstraints2D(leList, teList, nSpan, nChord, lower=1.0, name=name)
self.add_output(name, distributed=False, val=np.ones((nSpan * nChord,)), shape=nSpan * nChord)

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):
self.DVCon.addThicknessConstraints1D(ptList, nCon, axis, name=name, scaled=scaled)
self.add_output(name, distributed=False, val=np.ones(nCon), shape=nCon)

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)
self.add_output(name, distributed=False, val=1.0)

def nom_add_LETEConstraint(self, name, volID, faceID, topID=None, childIdx=None):
Expand All @@ -243,6 +250,32 @@ def nom_addLinearConstraintsShape(self, name, indSetA, indSetB, factorA, factorB
lSize = len(indSetA)
self.add_output(name, distributed=False, val=np.zeros(lSize), shape=lSize)

def nom_addTriangulatedSurfaceConstraint(
self,
name,
surface_1_name=None,
DVGeo_1_name="default",
surface_2_name="default",
DVGeo_2_name="default",
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 @@ -254,9 +287,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
8 changes: 8 additions & 0 deletions pygeo/parameterization/BaseDVGeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,12 @@ def mapXDictToDVGeo(self, inDict):
def mapXDictToComp(self, inDict):
"""
The inverse of :func:`mapXDictToDVGeo`, where we map the DVs to the composite space
Parameters
----------
inDict : dict
The DVs to be mapped
Returns
-------
dict
Expand All @@ -234,10 +236,12 @@ def mapXDictToComp(self, inDict):
def mapVecToDVGeo(self, inVec):
"""
This is the vector version of :func:`mapXDictToDVGeo`, where the actual mapping is done
Parameters
----------
inVec : ndarray
The DVs in a single 1D array
Returns
-------
ndarray
Expand All @@ -250,10 +254,12 @@ def mapVecToDVGeo(self, inVec):
def mapVecToComp(self, inVec):
"""
This is the vector version of :func:`mapXDictToComp`, where the actual mapping is done
Parameters
----------
inVec : ndarray
The DVs in a single 1D array
Returns
-------
ndarray
Expand All @@ -266,10 +272,12 @@ def mapVecToComp(self, inVec):
def mapSensToComp(self, inVec):
"""
Maps the sensitivity matrix to the composite design space
Parameters
----------
inVec : ndarray
The sensitivities to be mapped
Returns
-------
ndarray
Expand Down
50 changes: 31 additions & 19 deletions pygeo/parameterization/DVGeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def __init__(self, fileName, *args, isComplex=False, child=False, faceFreeze=Non
# Jacobians:
self.JT = {}
self.nPts = {}
self.dCoefdDVUpdated = False

# dictionary to save any coordinate transformations we are given
self.coordXfer = {}
Expand Down Expand Up @@ -1592,6 +1593,9 @@ def setDesignVars(self, dvDict):
for pointSet in self.updated:
self.updated[pointSet] = False

# also flag the dCoefdDV as out of date
self.dCoefdDVUpdated = False

# Now call setValues on the children. This way the
# variables will be set on the children
for child in self.children:
Expand Down Expand Up @@ -2414,8 +2418,13 @@ def totalSensitivityTransProd(self, vec, ptSetName, config=None):

def computeDVJacobian(self, config=None):
"""
return J_temp for a given config
return dCoefdDV for a given config
"""

# if dCoefdDV is not out of date, return immediately
if self.dCoefdDVUpdated:
return self.dCoefdDV

# These routines are not recursive. They compute the derivatives at this level and
# pass information down one level for the next pass call from the routine above

Expand All @@ -2435,37 +2444,40 @@ def computeDVJacobian(self, config=None):
# this is the jacobian from accumulated derivative dependence from parent to child
J_casc = self._cascadedDVJacobian(config=config)

J_temp = None
dCoefdDV = None

# add them together
if J_attach is not None:
J_temp = sparse.lil_matrix(J_attach)
dCoefdDV = sparse.lil_matrix(J_attach)

if J_spanwiselocal is not None:
if J_temp is None:
J_temp = sparse.lil_matrix(J_spanwiselocal)
if dCoefdDV is None:
dCoefdDV = sparse.lil_matrix(J_spanwiselocal)
else:
J_temp += J_spanwiselocal
dCoefdDV += J_spanwiselocal

if J_sectionlocal is not None:
if J_temp is None:
J_temp = sparse.lil_matrix(J_sectionlocal)
if dCoefdDV is None:
dCoefdDV = sparse.lil_matrix(J_sectionlocal)
else:
J_temp += J_sectionlocal
dCoefdDV += J_sectionlocal

if J_local is not None:
if J_temp is None:
J_temp = sparse.lil_matrix(J_local)
if dCoefdDV is None:
dCoefdDV = sparse.lil_matrix(J_local)
else:
J_temp += J_local
dCoefdDV += J_local

if J_casc is not None:
if J_temp is None:
J_temp = sparse.lil_matrix(J_casc)
if dCoefdDV is None:
dCoefdDV = sparse.lil_matrix(J_casc)
else:
J_temp += J_casc
dCoefdDV += J_casc

self.dCoefdDV = dCoefdDV
self.dCoefdDVUpdated = True

return J_temp
return dCoefdDV

def computeTotalJacobian(self, ptSetName, config=None):
"""Return the total point jacobian in CSR format since we
Expand All @@ -2480,7 +2492,7 @@ def computeTotalJacobian(self, ptSetName, config=None):

# compute the derivatives of the coefficients of this level wrt all of the design
# variables at this level and all levels above
J_temp = self.computeDVJacobian(config=config)
dCoefdDV = self.computeDVJacobian(config=config)

# now get the derivative of the points for this level wrt the coefficients(dPtdCoef)
if self.FFD.embeddedVolumes[ptSetName].dPtdCoef is not None:
Expand Down Expand Up @@ -2513,8 +2525,8 @@ def computeTotalJacobian(self, ptSetName, config=None):
new_dPtdCoef = sparse.coo_matrix((new_data, (new_row, new_col)), shape=(Nrow, Ncol)).tocsr()

# Do Sparse Mat-Mat multiplication and resort indices
if J_temp is not None:
self.JT[ptSetName] = (J_temp.T * new_dPtdCoef.T).tocsr()
if dCoefdDV is not None:
self.JT[ptSetName] = (dCoefdDV.T * new_dPtdCoef.T).tocsr()
self.JT[ptSetName].sort_indices()

# Add in child portion
Expand Down
10 changes: 5 additions & 5 deletions pygeo/parameterization/DVGeoCST.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def addPointSet(self, points, ptName, boundTol=1e-10, **kwargs):
boundTol : float, optional
Small absolute deviation by which the airfoil coordinates can exceed the initial
minimum and maximum x coordinates, by default 1e-10.
kwargs
\*\*kwargs
Any other parameters are ignored.
"""
# Convert points to the type specified at initialization (with isComplex) and store the points
Expand Down Expand Up @@ -556,7 +556,7 @@ def totalSensitivity(self, dIdpt, ptSetName, comm=None, **kwargs):
If you have many to do, it is faster to do many at once.
ptSetName : str
The name of set of points we are dealing with
kwargs
\*\*kwargs
Any other parameters ignored, but this is maintained to allow the same
interface as other DVGeo implementations.
Expand Down Expand Up @@ -691,7 +691,7 @@ def totalSensitivityProd(self, vec, ptSetName, **kwargs):
values are the derivative seeds of the corresponding design variable.
ptSetName : str
The name of set of points we are dealing with
kwargs
\*\*kwargs
Any other parameters ignored, but this is maintained to allow the same
interface as other DVGeo implementations.
Expand Down Expand Up @@ -799,7 +799,7 @@ def update(self, ptSetName, **kwargs):
ptSetName : str
Name of point-set to return. This must match ones of the
given in an :func:`addPointSet()` call.
kwargs
\*\*kwargs
Any other parameters ignored, but this is maintained to allow the same
interface as other DVGeo implementations.
Expand Down Expand Up @@ -1207,7 +1207,7 @@ def plotCST(upperCoeff, lowerCoeff, N1=0.5, N2=1.0, nPts=100, ax=None, **kwargs)
Number of coordinates to compute on each surface.
ax : matplotlib Axes, optional
Axes on which to plot airfoil.
**kwargs
\*\*kwargs
Keyword arguments passed to matplotlib.pyplot.plot
Returns
Expand Down
Loading

0 comments on commit 831e51d

Please sign in to comment.