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

SVD composite DVs for VSP and ESP #170

Merged
merged 35 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b2341ac
adding svd
ArshSaja Aug 31, 2022
f02bc7c
adding svd work
ArshSaja Aug 31, 2022
96ff85b
adding svd work
ArshSaja Sep 1, 2022
7f33347
changes are done
ArshSaja Sep 7, 2022
e88f999
Fixed DV name
ArshSaja Sep 7, 2022
baae253
Fixed the Dv name in addVarGroup
ArshSaja Sep 7, 2022
d008ce1
python ordered dict fix
ArshSaja Sep 9, 2022
206e0a4
fix array
ArshSaja Sep 10, 2022
53f074c
fix array
ArshSaja Sep 10, 2022
6239eb9
Minor Fixes
ArshSaja Sep 29, 2022
6b6ba21
column vector fix
ArshSaja Oct 2, 2022
dca64ad
column vector fix
ArshSaja Oct 2, 2022
59556b8
column vector fix
ArshSaja Oct 2, 2022
9bdd6ac
column vector fix
ArshSaja Oct 2, 2022
1ccb650
merging main
ArshSaja Jan 3, 2023
5512246
Merge branch 'main' of github.com:ArshSaja/pygeo into main
ArshSaja Jan 3, 2023
d90c59a
merging main
ArshSaja Jan 3, 2023
4cbdea6
adding tests for OpenVSP composite DV
ArshSaja Jan 3, 2023
3d56821
adding tests for ESP
ArshSaja Jan 6, 2023
d93a595
fixing code duplication and ove the identical function to basedvgeo
ArshSaja Jan 7, 2023
3fea5bf
abstractmethod is fixed
ArshSaja Jan 9, 2023
408b748
moving addcompostieDV to DVGeoSketch
ArshSaja Jan 9, 2023
7224a7e
adding tests for DVGeo
ArshSaja Jan 9, 2023
32a719d
adding tests for DVGeo
ArshSaja Jan 9, 2023
f347b62
removing comments
ArshSaja Jan 9, 2023
75f35f3
test ref file update
ArshSaja Jan 10, 2023
a7ffc0c
fixing leftover comments
ArshSaja Jan 10, 2023
0dc5bca
fixing leftover comments
ArshSaja Jan 10, 2023
345de90
minor fix
ArshSaja Jan 10, 2023
d3bcb78
Merge branch 'main' into SVDNEW
hajdik Jan 11, 2023
c967d00
adding composite option to all DVGeo
ArshSaja Jan 11, 2023
8bff09d
Merge branch 'SVDNEW' of github.com:ArshSaja/pygeo into SVDNEW
ArshSaja Jan 11, 2023
081bdac
minor fix to work composite in mphys
ArshSaja Jan 11, 2023
3e6680c
minor changes
ArshSaja Jan 18, 2023
e3916c9
minor changes
ArshSaja Jan 18, 2023
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
41 changes: 32 additions & 9 deletions pygeo/mphys/mphys_dvgeo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import openmdao.api as om
from .. import DVGeometry, DVConstraints
from ..constraints.baseConstraint import LinearConstraint

try:
from .. import DVGeometryVSP
Expand Down Expand Up @@ -141,21 +142,24 @@ def nom_add_point_dict(self, point_dict):
for k, v in point_dict.items():
self.nom_addPointSet(v, k)

def nom_addGlobalDV(self, dvName, value, func, childIdx=None):
def nom_addGlobalDV(self, dvName, value, func, childIdx=None, isComposite=False):
# global DVs are only added to FFD-based DVGeo objects
if self.geo_type != "ffd":
raise RuntimeError(f"Only FFD-based DVGeo objects can use global DVs, not type:{self.geo_type}")

# define the input
self.add_input(dvName, distributed=False, shape=len(value))
# When composite DVs are used, input is not required for the default DVs. Now the composite DVs are
# the actual DVs. So OpenMDAO don't need the default DVs as inputs.
if not isComposite:
self.add_input(dvName, distributed=False, shape=len(value))

# call the dvgeo object and add this dv
if childIdx is None:
self.DVGeo.addGlobalDV(dvName, value, func)
else:
self.DVGeo.children[childIdx].addGlobalDV(dvName, value, func)

def nom_addLocalDV(self, dvName, axis="y", pointSelect=None, childIdx=None):
def nom_addLocalDV(self, dvName, axis="y", pointSelect=None, childIdx=None, isComposite=False):
# local DVs are only added to FFD-based DVGeo objects
if self.geo_type != "ffd":
raise RuntimeError(f"Only FFD-based DVGeo objects can use local DVs, not type:{self.geo_type}")
Expand All @@ -164,10 +168,23 @@ def nom_addLocalDV(self, dvName, axis="y", pointSelect=None, childIdx=None):
nVal = self.DVGeo.addLocalDV(dvName, axis=axis, pointSelect=pointSelect)
else:
nVal = self.DVGeo.children[childIdx].addLocalDV(dvName, axis=axis, pointSelect=pointSelect)
self.add_input(dvName, distributed=False, shape=nVal)

# define the input
# When composite DVs are used, input is not required for the default DVs. Now the composite DVs are
# the actual DVs. So OpenMDAO don't need the default DVs as inputs.
if not isComposite:
self.add_input(dvName, distributed=False, shape=nVal)
return nVal

def nom_addVSPVariable(self, component, group, parm, **kwargs):
def nom_addGeoCompositeDV(self, dvName, ptSetName=None, u=None, scale=None, **kwargs):
# call the dvgeo object and add this dv
self.DVGeo.addCompositeDV(dvName, ptSetName=ptSetName, u=u, scale=scale, **kwargs)
val = self.DVGeo.getValues()

# define the input
self.add_input(dvName, distributed=False, shape=self.DVGeo.getNDV(), val=val[dvName][0])

def nom_addVSPVariable(self, component, group, parm, isComposite=False, **kwargs):
# VSP DVs are only added to VSP-based DVGeo objects
if self.geo_type != "vsp":
raise RuntimeError(f"Only VSP-based DVGeo objects can use VSP DVs, not type:{self.geo_type}")
Expand All @@ -181,10 +198,13 @@ def nom_addVSPVariable(self, component, group, parm, **kwargs):
# get the value
val = self.DVGeo.DVs[dvName].value.copy()

# add the input with the correct value, VSP DVs always have a size of 1
self.add_input(dvName, distributed=False, shape=1, val=val)
# define the input
# When composite DVs are used, input is not required for the default DVs. Now the composite DVs are
# the actual DVs. So OpenMDAO don't need the default DVs as inputs.
if not isComposite:
self.add_input(dvName, distributed=False, shape=1, val=val)

def nom_addESPVariable(self, desmptr_name, **kwargs):
def nom_addESPVariable(self, desmptr_name, isComposite=False, **kwargs):
# ESP DVs are only added to VSP-based DVGeo objects
if self.geo_type != "esp":
raise RuntimeError(f"Only ESP-based DVGeo objects can use ESP DVs, not type:{self.geo_type}")
Expand All @@ -196,7 +216,10 @@ def nom_addESPVariable(self, desmptr_name, **kwargs):
val = self.DVGeo.DVs[desmptr_name].value.copy()

# add the input with the correct value, VSP DVs always have a size of 1
self.add_input(desmptr_name, distributed=False, shape=val.shape, val=val)
# When composite DVs are used, input is not required for the default DVs. Now the composite DVs are
# the actual DVs. So OpenMDAO don't need the default DVs as inputs.
if not isComposite:
self.add_input(desmptr_name, distributed=False, shape=val.shape, val=val)

def nom_addThicknessConstraints2D(self, name, leList, teList, nSpan=10, nChord=10):
self.DVCon.addThicknessConstraints2D(leList, teList, nSpan, nChord, lower=1.0, name=name)
Expand Down
92 changes: 92 additions & 0 deletions pygeo/parameterization/BaseDVGeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from abc import ABC, abstractmethod
from collections import OrderedDict
import copy


class BaseDVGeometry(ABC):
Expand Down Expand Up @@ -184,3 +185,94 @@ def update(self, ptSetName):
Name of point-set to return. This must match ones of the given in an :func:`addPointSet()` call.
"""
pass

def mapXDictToDVGeo(self, inDict):
"""
Map a dictionary of DVs to the 'DVGeo' design, while keeping non-DVGeo DVs in place
without modifying them

Parameters
----------
inDict : dict
The dictionary of DVs to be mapped

Returns
-------
dict
The mapped DVs in the same dictionary format
"""
# first make a copy so we don't modify in place
inDictBase = inDict
userVec = inDict[self.DVComposite.name]
outVec = self.mapVecToDVGeo(userVec)
outDict = self.convertSensitivityToDict(outVec.reshape(1, -1), out1D=True, useCompositeNames=False)
# now merge inDict and outDict
for key in inDict:
outDict[key] = inDictBase[key]
return outDict

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
The mapped DVs
"""
# first make a copy so we don't modify in place
inDict = copy.deepcopy(inDict)
userVec = self.convertDictToSensitivity(inDict)
outVec = self.mapVecToComp(userVec)
outDict = self.convertSensitivityToDict(outVec.reshape(1, -1), out1D=True, useCompositeNames=True)
return outDict

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
The mapped DVs in a single 1D array
"""
inVec = inVec.reshape(self.getNDV(), -1)
outVec = self.DVComposite.u @ inVec
return outVec.flatten()

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
The mapped DVs in a single 1D array
"""
inVec = inVec.reshape(self.getNDV(), -1)
outVec = self.DVComposite.u.transpose() @ inVec
return outVec.flatten()

def mapSensToComp(self, inVec):
"""
Maps the sensitivity matrix to the composite design space
Parameters
----------
inVec : ndarray
The sensitivities to be mapped
Returns
-------
ndarray
The mapped sensitivity matrix
"""
outVec = inVec @ self.DVComposite.u # this is the same as (self.DVComposite.u.T @ inVec.T).T
return outVec
102 changes: 1 addition & 101 deletions pygeo/parameterization/DVGeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1401,11 +1401,10 @@ def addCompositeDV(self, dvName, ptSetName=None, u=None, scale=None):
raise ValueError("If u and s need to be computed, you must specify the ptSetName")
self.computeTotalJacobian(ptSetName)
J_full = self.JT[ptSetName].todense() # this is in CSR format but we convert it to a dense matrix
u, s, _ = np.linalg.svd(J_full)
u, s, _ = np.linalg.svd(J_full, full_matrices=False)
scale = np.sqrt(s)
# normalize the scaling
scale = scale * (NDV / np.sum(scale))

# map the initial design variable values
# we do this manually instead of calling self.mapVecToComp
# because self.DVComposite.u isn't available yet
Expand Down Expand Up @@ -3684,105 +3683,6 @@ def _unComplexifyCoef(self):

self.coef = self.coef.real.astype("d")

def mapXDictToDVGeo(self, inDict):
"""
Map a dictionary of DVs to the 'DVGeo' design, while keeping non-DVGeo DVs in place
without modifying them

Parameters
----------
inDict : dict
The dictionary of DVs to be mapped

Returns
-------
dict
The mapped DVs in the same dictionary format
"""
# first make a copy so we don't modify in place
inDict = copy.deepcopy(inDict)
userVec = inDict.pop(self.DVComposite.name)
outVec = self.mapVecToDVGeo(userVec)
outDict = self.convertSensitivityToDict(outVec.reshape(1, -1), out1D=True, useCompositeNames=False)
# now merge inDict and outDict
for key in inDict:
outDict[key] = inDict[key]
return outDict

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
The mapped DVs
"""
# first make a copy so we don't modify in place
inDict = copy.deepcopy(inDict)
userVec = self.convertDictToSensitivity(inDict)
outVec = self.mapVecToComp(userVec)
outDict = self.convertSensitivityToDict(outVec.reshape(1, -1), out1D=True, useCompositeNames=True)
return outDict

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
The mapped DVs in a single 1D array
"""
inVec = inVec.reshape(self.getNDV(), -1)
outVec = self.DVComposite.u @ inVec
return outVec.flatten()

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
The mapped DVs in a single 1D array
"""
inVec = inVec.reshape(self.getNDV(), -1)
outVec = self.DVComposite.u.T @ inVec
return outVec.flatten()

def mapSensToComp(self, inVec):
"""
Maps the sensitivity matrix to the composite design space

Parameters
----------
inVec : ndarray
The sensitivities to be mapped

Returns
-------
ndarray
The mapped sensitivity matrix
"""
outVec = inVec @ self.DVComposite.u # this is the same as (self.DVComposite.u.T @ inVec.T).T
return outVec

def computeTotalJacobianFD(self, ptSetName, config=None):
"""This function takes the total derivative of an objective,
I, with respect the points controlled on this processor using FD.
Expand Down
24 changes: 15 additions & 9 deletions pygeo/parameterization/DVGeoESP.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def __init__(

# will become a list of tuples with (DVName, localIndex) - used for finite difference load balancing
self.globalDVList = []
self.useComposite = False

self.suppress_stdout = suppress_stdout
self.exclude_edge_projections = exclude_edge_projections
Expand Down Expand Up @@ -590,6 +591,8 @@ def setDesignVars(self, dvDict, updateJacobian=True):
The keys of the dictionary must correspond to the design variable names.
Any additional keys in the dfvdictionary are simply ignored.
"""
if self.useComposite:
dvDict = self.mapXDictToDVGeo(dvDict)

# Just dump in the values
for key in dvDict:
Expand Down Expand Up @@ -767,7 +770,6 @@ def totalSensitivity(self, dIdpt, ptSetName, comm=None, config=None):
# # transpose dIdpt and vstack;
# # Now vstack the result with seamBar as that is far as the
# # forward FD jacobian went.
# tmp = np.vstack([dIdpt.T, dIdSeam.T])
tmp = dIdpt.T

# we also stack the pointset jacobian
Expand All @@ -781,14 +783,18 @@ def totalSensitivity(self, dIdpt, ptSetName, comm=None, config=None):
else:
dIdx = dIdx_local

# Now convert to dict:
dIdxDict = {}
for dvName in self.DVs:
dv = self.DVs[dvName]
jac_start = dv.globalStartInd
jac_end = jac_start + dv.nVal
# dIdxDict[dvName] = np.array([dIdx[:, i]]).T
dIdxDict[dvName] = dIdx[:, jac_start:jac_end]
if self.useComposite:
dIdx = self.mapSensToComp(dIdx)
dIdxDict = self.convertSensitivityToDict(dIdx, useCompositeNames=True)

else:
# Now convert to dict:
dIdxDict = {}
for dvName in self.DVs:
dv = self.DVs[dvName]
jac_start = dv.globalStartInd
jac_end = jac_start + dv.nVal
dIdxDict[dvName] = dIdx[:, jac_start:jac_end]

return dIdxDict

Expand Down
Loading