Skip to content
Open
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ doc/_build
input_files
.isort.cfg
*.vscode
tests/reg_tests/reports/
tests/reg_tests/test_MPhysGeo*_out
59 changes: 47 additions & 12 deletions pygeo/mphys/mphys_dvgeo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Standard Python modules
import inspect

# External modules
from mpi4py import MPI
import numpy as np
Expand Down Expand Up @@ -36,6 +39,8 @@ def initialize(self):
# since `nom_add_discipline_coords` can be called before `setup`
self.omPtInOutDict = {}

self.update_jac = True

def setup(self):
# create a constraints object to go with this DVGeo(s)
self.DVCon = DVConstraints()
Expand Down Expand Up @@ -97,7 +102,7 @@ def setup(self):
for _, DVGeo in self.DVGeos.items():
self.DVCon.setDVGeo(DVGeo, name=DVConName)

self.omPtSetList = []
self.omPtSetList = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change the variable name too now that it is a dictionary?


def compute(self, inputs, outputs):
# check for inputs that have been added but the points have not been added to dvgeo
Expand Down Expand Up @@ -221,26 +226,47 @@ def nom_add_discipline_coords(self, discipline, points=None, DVGeoName=None, **k

else:
# we are provided with points. we can do the full initialization now
self.nom_addPointSet(points, outputName, add_output=False, DVGeoName=DVGeoName, **kwargs)
self.nom_addPointSet(points, outputName, add_output=False, DVGeoName=DVGeoName, distributed=True, **kwargs)
self.add_input(inputName, distributed=True, val=points.flatten())
self.add_output(outputName, distributed=True, val=points.flatten())

def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, **kwargs):
def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, distributed=True, **kwargs):
"""Add a pointset to the DVGeo object and create an output for it in the OpenMDAO component.

Parameters
----------
points : numpy array
3D points to add to the DVGeo object, shape (N,3) or (3N,)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is "3N," a typo or intentional?

ptName : str
Name for the pointset
add_output : bool, optional
Whether to add the deformed points as an output of the component, by default True
DVGeoName : str, optional
The name of the DVGeo to add the points to, necessary if there are multiple DVGeo objects. By default `None`.
distributed : bool, optional
Whether the output of the component should be a distributed variable, by default True

Returns
-------
None or float
If using DVGeometryESP or DVGeometryVSP, returns the maximum distance between pointset and the CAD model
"""
# if we have multiple DVGeos use the one specified by name
DVGeo = self.nom_getDVGeo(DVGeoName=DVGeoName)

# add the points to the dvgeo object
dMaxGlobal = None
if isinstance(DVGeo, DVGeometryESP):
# DVGeoESP can return a value to check the pointset distribution
dMaxGlobal = DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs)
# DVGeoESP and DVGeoVSP can return a value to check the pointset distribution
# Also, the addPointSet method for some DVGeos takes a distributed kwarg, in which case we can pass it
sig = inspect.signature(DVGeo.addPointSet)
if "distributed" in sig.parameters:
dMaxGlobal = DVGeo.addPointSet(points.reshape(-1, 3), ptName, distributed=distributed, **kwargs)
else:
DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs)
self.omPtSetList.append(ptName)
dMaxGlobal = DVGeo.addPointSet(points.reshape(-1, 3), ptName, **kwargs)
self.omPtSetList[ptName] = {"distributed": distributed}

if add_output:
# add an output to the om component
self.add_output(ptName, distributed=True, val=points.flatten())
self.add_output(ptName, distributed=distributed, val=points.flatten())

return dMaxGlobal

Expand Down Expand Up @@ -269,6 +295,12 @@ def nom_getDVGeo(self, childName=None, comp=None, DVGeoName=None):
DVGeometry object
DVGeometry object held by this geometry component
"""
# Calling this function before setup is not allowed because the DVGeo object(s) do not exist yet
if not hasattr(self, "DVGeos"):
raise RuntimeError(
"Cannot call `nom_getDVGeo` before OM_DVGEOCOMP's `setup` method has been called. If you are calling this function in the `setup` method of a group containing an OM_DVGEOCOMP, move the call to `configure` instead."
) from None

# if we have multiple DVGeos use the one specified by name
if self.multDVGeo:
DVGeo = self.DVGeos[DVGeoName]
Expand Down Expand Up @@ -1100,8 +1132,11 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
for k in xdot:
# check if this dv is present
if k in d_inputs:
# do the allreduce
xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM)
# do the allreduce if the pointset is distributed
if self.omPtSetList[ptSetName]["distributed"]:
xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM)
else:
xdotg[k] = xdot[k]

# accumulate in the dict
d_inputs[k] += xdotg[k][0]
25 changes: 21 additions & 4 deletions tests/reg_tests/test_MPhysGeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@
@unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper")
@parameterized_class(ffd_test_params)
class TestDVGeoMPhysFFD(unittest.TestCase):
N_PROCS = 2

def setUp(self):
# give the OM Group access to the test case attributes
dvInfo = self.dvInfo
Expand All @@ -286,7 +288,7 @@ def configure(self):
points[0, :] = [0.25, 0, 0]
points[1, :] = [-0.25, 0, 0]
ptName = "testPoints"
self.geometry.nom_addPointSet(points.flatten(), ptName)
self.geometry.nom_addPointSet(points.flatten(), ptName, distributed=False)

# create a reference axis for the parent
axisPoints = [[-1.0, 0.0, 0.0], [1.5, 0.0, 0.0]]
Expand Down Expand Up @@ -323,7 +325,7 @@ def configure(self):

self.add_constraint(f"geometry.{ptName}")

self.prob = Problem(model=FFDGroup())
self.prob = Problem(model=FFDGroup(), reports=False)

def test_run_model(self):
self.prob.setup()
Expand Down Expand Up @@ -404,7 +406,7 @@ def twist(val, geo):
self.add_design_var("local")
self.add_objective(paramKwargs["name"])

p = Problem(model=BoxGeo())
p = Problem(model=BoxGeo(), reports=False)
return p

def test_undeformed_vals(self):
Expand Down Expand Up @@ -529,7 +531,7 @@ def configure(self):

self.add_constraint(f"geometry.{ptName}")

self.prob = Problem(model=ESPGroup())
self.prob = Problem(model=ESPGroup(), reports=False)

def test_run_model(self):
self.prob.setup()
Expand Down Expand Up @@ -562,5 +564,20 @@ def test_deriv_rev(self):
commonUtils.assert_check_totals(totals, atol=1e-5, rtol=1e-5)


@unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper")
class TestGetDVGeoError(unittest.TestCase):
# Make sure we get an error if we try to call nom_getDVGeo before setup
def test_getDVGeo_error(self):
class BadGroup(Group):
def setup(self):
geometryComp = OM_DVGEOCOMP(file=outerFFD, type="ffd")
self.add_subsystem("geometry", geometryComp, promotes=["*"])
geometryComp.nom_getDVGeo()

prob = Problem(model=BadGroup(), reports=False)
with self.assertRaises(RuntimeError):
prob.setup()


if __name__ == "__main__":
unittest.main()