From 754b588adc06fbbfe132831fa58cb41e0eed2338 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Tue, 2 Jul 2024 10:46:38 -0400 Subject: [PATCH 01/46] parameterized mphys test format --- .gitignore | 4 +- tests/reg_tests/test_MPhysGeo.py | 100 +++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/reg_tests/test_MPhysGeo.py diff --git a/.gitignore b/.gitignore index 9774ab5d..1592480b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ reg_tests/pygeo_reg.orig doc/_build *testflo_report.out input_files -.isort.cfg \ No newline at end of file +.isort.cfg +*.vscode +tests/reg_tests/reports/ diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py new file mode 100644 index 00000000..134b0d72 --- /dev/null +++ b/tests/reg_tests/test_MPhysGeo.py @@ -0,0 +1,100 @@ +import unittest +import os +import numpy as np +from parameterized import parameterized, parameterized_class + +import commonUtils +from pygeo.mphys import OM_DVGEOCOMP +from pyspline import Curve + +try: + from mphys.multipoint import Multipoint + from openmdao.api import IndepVarComp, Problem + from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal + + mphysInstalled = True + +except ImportError: + mphysInstalled = False + +input_path = os.path.dirname(os.path.abspath(__file__)) +parentFFDFile = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") +childFFDFile = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") +espBox = os.path.join(input_path, "../input_files/esp/box.csm") + +ffdPoints = np.array(()) + +globalDVFuncParams = ["mainX", -1.0, commonUtils.mainAxisPoints] +localDVFuncParams = ["xdir"] + +test_params = [ + {"name": "MPhys_FFD_global", "funcNames": ["nom_addGlobalDV"], "funcParams": [globalDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [-1.0]}, + {"name": "MPhys_FFD_local", "funcNames": ["nom_addLocalDV"], "funcParams": [localDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [12*[0.0]]}, +] + +@unittest.skipUnless(mphysInstalled, "OpenMDAO and MPhys are required to test the pyGeo MPhys wrapper") +@parameterized_class(test_params) +class TestDVGeoMPhysFFD(unittest.TestCase): + def setUp(self): + # give the OM Group access to the test case attributes + # points = self.points + # ptName = "pts" + funcNames = self.funcNames + funcParams = self.funcParams + upper = self.upper + lower = self.lower + val = self.val + + class FFDGroup(Multipoint): + def setup(self): + self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) + self.add_subsystem("geometry", OM_DVGEOCOMP(file=parentFFDFile, type="ffd")) + + def configure(self): + self.geometry.nom_addChild(childFFDFile, childName="child") + + points = np.zeros([2, 3]) + points[0, :] = [0.25, 0, 0] + points[1, :] = [-0.25, 0, 0] + ptName = "testPoints" + self.geometry.nom_addPointSet(points.flatten(), ptName) + + # create a reference axis for the parent + axisPoints = [[-1.0, 0.0, 0.0], [1.5, 0.0, 0.0]] + c1 = Curve(X=axisPoints, k=2) + self.geometry.nom_addRefAxis("mainAxis", curve=c1, axis="y") + + # create a reference axis for the child + axisPoints = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]] + c1 = Curve(X=axisPoints, k=2) + self.geometry.nom_addRefAxis("nestedAxis", childName="child", curve=c1, axis="y") + + for ii, func in enumerate(funcNames): + dvName = funcParams[ii][0] + getattr(self.geometry, func)(*funcParams[ii]) + + self.dvs.add_output(dvName, val[ii]) + self.connect(dvName, f"geometry.{dvName}") + self.add_design_var(dvName, upper=upper[ii], lower=lower[ii]) + + self.add_constraint(f"geometry.{ptName}") + + prob = Problem(model=FFDGroup()) + prob.setup(mode="rev") + + self.prob = prob + + def test_run_model(self): + self.prob.run_model() + + def testDVs(self): + self.prob.run_model() + + data = self.prob.check_totals(step=1e-7, compact_print=True) + for _, err in data.items(): + + rel_err = err["rel error"] + assert_near_equal(rel_err.forward, 0.0, 1e-5) + +if __name__ == "__main__": + unittest.main() From 789ab36ef1a2d25081bfee612f34c45bb841bc67 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Wed, 3 Jul 2024 14:30:21 -0400 Subject: [PATCH 02/46] Use assert_check_totals --- tests/reg_tests/test_MPhysGeo.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 134b0d72..6cdb7551 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -10,7 +10,7 @@ try: from mphys.multipoint import Multipoint from openmdao.api import IndepVarComp, Problem - from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal + from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal, assert_check_totals mphysInstalled = True @@ -80,7 +80,7 @@ def configure(self): self.add_constraint(f"geometry.{ptName}") prob = Problem(model=FFDGroup()) - prob.setup(mode="rev") + prob.setup(mode="rev", force_alloc_complex=True) self.prob = prob @@ -90,11 +90,8 @@ def test_run_model(self): def testDVs(self): self.prob.run_model() - data = self.prob.check_totals(step=1e-7, compact_print=True) - for _, err in data.items(): - - rel_err = err["rel error"] - assert_near_equal(rel_err.forward, 0.0, 1e-5) + totals = self.prob.check_totals(step=1e-7, compact_print=True) + assert_check_totals(totals) if __name__ == "__main__": unittest.main() From 0c5e392030faf5dfdd1a91aefde5c45c6990e360 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Wed, 3 Jul 2024 19:48:02 -0400 Subject: [PATCH 03/46] DVCon tests for most common functions on a box geometry --- tests/reg_tests/test_MPhysGeo.py | 177 +++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 11 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 6cdb7551..adb2f806 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -1,21 +1,21 @@ import unittest import os import numpy as np -from parameterized import parameterized, parameterized_class +from parameterized import parameterized_class +from stl import mesh import commonUtils from pygeo.mphys import OM_DVGEOCOMP from pyspline import Curve try: - from mphys.multipoint import Multipoint - from openmdao.api import IndepVarComp, Problem + from openmdao.api import IndepVarComp, Problem, Group from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal, assert_check_totals - mphysInstalled = True + omInstalled = True except ImportError: - mphysInstalled = False + omInstalled = False input_path = os.path.dirname(os.path.abspath(__file__)) parentFFDFile = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") @@ -27,13 +27,81 @@ globalDVFuncParams = ["mainX", -1.0, commonUtils.mainAxisPoints] localDVFuncParams = ["xdir"] -test_params = [ +test_params_ffd = [ {"name": "MPhys_FFD_global", "funcNames": ["nom_addGlobalDV"], "funcParams": [globalDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [-1.0]}, {"name": "MPhys_FFD_local", "funcNames": ["nom_addLocalDV"], "funcParams": [localDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [12*[0.0]]}, ] -@unittest.skipUnless(mphysInstalled, "OpenMDAO and MPhys are required to test the pyGeo MPhys wrapper") -@parameterized_class(test_params) +# DVConstraints functionals to test +test_params_constraints_box = [ + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 3, "axis": [1, 0, 0], "scaled": False}, + "valCheck": 2 * np.ones(3), + "valTol": 1e-4, + }, + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 5, "axis": [0, 1, 0], "scaled": False}, + "valCheck": np.ones(5), + "valTol": 3e-5, + }, + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], "nCon": 5, "axis": [0, 0, 1], "scaled": False}, + "valCheck": 8 * np.ones(5), + }, + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 3, "axis": [1, 0, 0], "scaled": False, "projected": True}, + "valCheck": 2 * np.ones(3), + "valTol": 2e-4, + }, + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 5, "axis": [0, 1, 0], "scaled": False, "projected": True}, + "valCheck": np.ones(5), + "valTol": 2e-4, + }, + { + "conFunc": "nom_addThicknessConstraints1D", + "kwargs": {"name": "func", "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], "nCon": 5, "axis": [0, 0, 1], "scaled": False, "projected": True}, + "valCheck": 8 * np.ones(5), + }, + { + "conFunc": "nom_addThicknessConstraints2D", + "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False}, + "valCheck": np.ones(6), + }, + { + "conFunc": "nom_addThicknessConstraints2D", + "kwargs": {"name": "func", "leList": [[0.0, -0.25, 0.1], [0.0, -0.25, 7.9]], "teList": [[0.0, 0.25, 0.1], [0.0, 0.25, 7.9]], "nSpan": 2, "nChord": 3, "scaled": False}, + "valCheck": 2 * np.ones(6), + }, + { + "conFunc": "nom_addThicknessConstraints2D", + "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False}, + "valCheck": 8 * np.ones(6), + }, + { + "conFunc": "nom_addThicknessConstraints2D", + "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False, "projected": True}, + "valCheck": 8 * np.ones(6), + }, + { + "conFunc": "nom_addVolumeConstraint", + "kwargs": {"name": "func", "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]], "nSpan": 4, "nChord": 4, "scaled": False}, + "valCheck": 4.0, + "valTol": 1e-4, + }, + {"conFunc": "nom_addSurfaceAreaConstraint", "kwargs": {"name": "func", "scaled": False}, "valCheck": 52.0}, + {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "x", "scaled": False}, "valCheck": 8.0, "valTol": 3e-2}, + {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "y", "scaled": False}, "valCheck": 16.0, "valTol": 3e-2}, + {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "z", "scaled": False}, "valCheck": 2.0, "valTol": 3e-2}, +] + +@unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper") +@parameterized_class(test_params_ffd) class TestDVGeoMPhysFFD(unittest.TestCase): def setUp(self): # give the OM Group access to the test case attributes @@ -45,7 +113,7 @@ def setUp(self): lower = self.lower val = self.val - class FFDGroup(Multipoint): + class FFDGroup(Group): def setup(self): self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) self.add_subsystem("geometry", OM_DVGEOCOMP(file=parentFFDFile, type="ffd")) @@ -80,7 +148,7 @@ def configure(self): self.add_constraint(f"geometry.{ptName}") prob = Problem(model=FFDGroup()) - prob.setup(mode="rev", force_alloc_complex=True) + prob.setup(mode="rev") self.prob = prob @@ -90,8 +158,95 @@ def test_run_model(self): def testDVs(self): self.prob.run_model() - totals = self.prob.check_totals(step=1e-7, compact_print=True) + totals = self.prob.check_totals(step=1e-7, out_stream=None) assert_check_totals(totals) + +@unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper") +@parameterized_class(test_params_constraints_box) +class TestDVConMPhysBox(unittest.TestCase): + def setUp(self): + # Random number generator + self.rand = np.random.default_rng(1) + + def get_box_prob(self): + """ + Generate an OpenMDAO problem with the OM_DVGEOCOMP component with the + functional dictated by the parameterized class. + """ + # Parameterized values + conFunc = self.conFunc + kwargs = self.kwargs + + meshFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.stl") + ffdFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") + xFraction = 0.5 + meshScale = 1.0 + + class BoxGeo(Group): + def setup(self): + self.geo = self.add_subsystem("geometry", OM_DVGEOCOMP(file=ffdFile, type="ffd"), promotes=["*"]) + + def configure(self): + # 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 + self.geo.nom_setConstraintSurface([p0, v1, v2], addToDVGeo=False) + + # Add the geometric functional + getattr(self.geo, conFunc)(**kwargs) + + # Add DVs + nRefAxPts = self.geo.nom_addRefAxis("wing", xFraction=xFraction, alignIndex="k") + self.nTwist = nRefAxPts - 1 + + def twist(val, geo): + for i in range(1, nRefAxPts): + geo.rot_z["wing"].coef[i] = val[i - 1] + + self.geo.nom_addGlobalDV(dvName="twist", value=[0] * self.nTwist, func=twist) + self.geo.nom_addLocalDV("local", axis="y") + + self.add_design_var("twist") + self.add_design_var("local") + self.add_objective(kwargs["name"]) + + p = Problem(model=BoxGeo()) + p.setup(mode="rev") + return p + + def test_undeformed_vals(self): + """ + Test the value of the functional on the baseline geometry. + """ + p = self.get_box_prob() + p.run_model() + val = p.get_val(self.kwargs["name"]) + tol = 1e-5 if not hasattr(self, "valTol") else self.valTol + assert_near_equal(val, self.valCheck, tolerance=tol) + + def test_deformed_derivs(self): + """ + Test the total derivatives on a random perturbation to the baseline. + """ + p = self.get_box_prob() + + # Pick some random deformed state + p.set_val("twist", self.rand.random() * 10) + p.set_val("local", self.rand.random() * 10) + + p.run_model() + + # Check total derivatives using a directional derivatives + totals = p.check_totals(step=1e-6, out_stream=None, directional=True) + assert_check_totals(totals, atol=1e-5, rtol=3e-5) + + if __name__ == "__main__": unittest.main() From 89469520e20525748aa7a0f0a8d2c31fd9284f09 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Wed, 3 Jul 2024 19:49:42 -0400 Subject: [PATCH 04/46] Removed unused assert_check_partials import --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index adb2f806..69fc544d 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -10,7 +10,7 @@ try: from openmdao.api import IndepVarComp, Problem, Group - from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal, assert_check_totals + from openmdao.utils.assert_utils import assert_near_equal, assert_check_totals omInstalled = True From 9d1a470ea0abf91a20d61d271e6082628017edeb Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Sun, 7 Jul 2024 14:11:53 -0400 Subject: [PATCH 05/46] Error if any derivative mode other than rev is selected --- pygeo/mphys/mphys_dvgeo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index c93c68c3..3fc74756 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -947,3 +947,5 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # in multiple objective seeds with totalSensitivity. we can remove the [0] # once we move back to totalSensitivityTransProd d_inputs[k] += xdotg[k][0] + else: + raise RuntimeError(f"OM_DVGEOCOMP supports only \"rev\" derivative mode, but \"{mode}\" was selected") From 01dca802553f9d386ea31c73a340a4cb2b6dbece Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Sun, 7 Jul 2024 14:17:16 -0400 Subject: [PATCH 06/46] Fixed if check --- pygeo/mphys/mphys_dvgeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 3fc74756..2bddd1a7 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -947,5 +947,5 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # in multiple objective seeds with totalSensitivity. we can remove the [0] # once we move back to totalSensitivityTransProd d_inputs[k] += xdotg[k][0] - else: + elif mode != "rev": raise RuntimeError(f"OM_DVGEOCOMP supports only \"rev\" derivative mode, but \"{mode}\" was selected") From 10263043ee16371a3d23a20c5591dd4d4f358adf Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Sun, 7 Jul 2024 14:19:48 -0400 Subject: [PATCH 07/46] Sorry black --- pygeo/mphys/mphys_dvgeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 2bddd1a7..132119e4 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -948,4 +948,4 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # once we move back to totalSensitivityTransProd d_inputs[k] += xdotg[k][0] elif mode != "rev": - raise RuntimeError(f"OM_DVGEOCOMP supports only \"rev\" derivative mode, but \"{mode}\" was selected") + raise RuntimeError(f'OM_DVGEOCOMP supports only "rev" derivative mode, but "{mode}" was selected') From b5c7035dc637c300d1944c157b895c4132dd789d Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Mon, 15 Jul 2024 13:10:31 -0400 Subject: [PATCH 08/46] start esp test --- pygeo/mphys/mphys_dvgeo.py | 2 +- tests/reg_tests/test_MPhysGeo.py | 116 +++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 22 deletions(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index c93c68c3..59e9a2ec 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -285,8 +285,8 @@ def nom_addLocalDV( self, dvName, axis="y", - pointSelect=None, childName=None, + pointSelect=None, isComposite=False, DVGeoName=None, prependName=False, diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 134b0d72..440f72c2 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -17,33 +17,46 @@ except ImportError: mphysInstalled = False +try: + # External modules + import pyOCSM # noqa + + # First party modules + from pygeo import DVGeometryESP + + ocsmInstalled = True + +except ImportError: + ocsmInstalled = False + +# input files for all DVGeo types input_path = os.path.dirname(os.path.abspath(__file__)) parentFFDFile = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") childFFDFile = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") espBox = os.path.join(input_path, "../input_files/esp/box.csm") -ffdPoints = np.array(()) +# parameters for FFD-based DVGeo tests +childName = "childFFD" +globalDVFuncParamsParent = ["mainX", -1.0, commonUtils.mainAxisPoints] +localDVFuncParamsParent = ["xdir"] +globalDVFuncParamsChild = ["nestedX", -0.5, commonUtils.childAxisPoints, childName] -globalDVFuncParams = ["mainX", -1.0, commonUtils.mainAxisPoints] -localDVFuncParams = ["xdir"] +globalDVParent = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsParent, "lower": -1.0, "upper": 0.0, "val": -1.0} +localDVParent = {"funcName": "nom_addLocalDV", "funcParams": localDVFuncParamsParent, "lower": -1.0, "upper": 1.0, "val": 12*[0.0]} +globalDVChild = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsChild, "lower": -1.0, "upper": 0.0, "val": -1.0} -test_params = [ - {"name": "MPhys_FFD_global", "funcNames": ["nom_addGlobalDV"], "funcParams": [globalDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [-1.0]}, - {"name": "MPhys_FFD_local", "funcNames": ["nom_addLocalDV"], "funcParams": [localDVFuncParams], "lower": [-1.0], "upper": [1.0], "val": [12*[0.0]]}, +ffd_test_params = [ + {"name": "MPhys_FFD_oneFFD_global", "dvInfo": [globalDVParent]}, + {"name": "MPhys_FFD_oneFFD_local", "dvInfo": [localDVParent]}, + {"name": "MPhys_FFD_childFFD_global", "dvInfo": [globalDVParent, globalDVChild]}, ] @unittest.skipUnless(mphysInstalled, "OpenMDAO and MPhys are required to test the pyGeo MPhys wrapper") -@parameterized_class(test_params) +@parameterized_class(ffd_test_params) class TestDVGeoMPhysFFD(unittest.TestCase): def setUp(self): # give the OM Group access to the test case attributes - # points = self.points - # ptName = "pts" - funcNames = self.funcNames - funcParams = self.funcParams - upper = self.upper - lower = self.lower - val = self.val + dvInfo = self.dvInfo class FFDGroup(Multipoint): def setup(self): @@ -51,7 +64,7 @@ def setup(self): self.add_subsystem("geometry", OM_DVGEOCOMP(file=parentFFDFile, type="ffd")) def configure(self): - self.geometry.nom_addChild(childFFDFile, childName="child") + self.geometry.nom_addChild(childFFDFile, childName=childName) points = np.zeros([2, 3]) points[0, :] = [0.25, 0, 0] @@ -67,15 +80,15 @@ def configure(self): # create a reference axis for the child axisPoints = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]] c1 = Curve(X=axisPoints, k=2) - self.geometry.nom_addRefAxis("nestedAxis", childName="child", curve=c1, axis="y") + self.geometry.nom_addRefAxis("nestedAxis", childName=childName, curve=c1, axis="y") - for ii, func in enumerate(funcNames): - dvName = funcParams[ii][0] - getattr(self.geometry, func)(*funcParams[ii]) + for dv in dvInfo: + dvName = dv["funcParams"][0] + getattr(self.geometry, dv["funcName"])(*dv["funcParams"]) - self.dvs.add_output(dvName, val[ii]) + self.dvs.add_output(dvName, dv["val"]) self.connect(dvName, f"geometry.{dvName}") - self.add_design_var(dvName, upper=upper[ii], lower=lower[ii]) + self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) self.add_constraint(f"geometry.{ptName}") @@ -96,5 +109,66 @@ def testDVs(self): rel_err = err["rel error"] assert_near_equal(rel_err.forward, 0.0, 1e-5) +# parameters for ESP-based DVGeo tests +esp_test_params = [{"N_PROCS": 1, "name": "serial"}, {"N_PROCS": 4, "name": "parallel_4procs"}] + +@unittest.skipUnless(mphysInstalled and ocsmInstalled, "OpenMDAO, MPhys, and ESP are required to test the ESP part of the pyGeo MPhys wrapper") +@parameterized_class(esp_test_params) +class TestDVGeoMPhysESP(unittest.TestCase): + def setUp(self): + self.input_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + def modelSetup(self): + + class ESPGroup(Multipoint): + def setup(self): + self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) + self.add_subsystem("geometry", OM_DVGEOCOMP(file=espBox, type="esp")) + + def configure(self): + + # add a point set on the surface + vertex1 = np.array([-2.0, -2.0, -2.0]) + vertex2 = np.array([1.5, 1.5, 1.5]) + left = np.array([-2.0, -1.1, -1.1]) + right = np.array([1.5, -1.2, -0.1]) + front = np.array([0.25, 1.5, 0.3]) + back = np.array([1.2, -2.0, -0.3]) + top = np.array([0.0, 0.1, 1.5]) + bottom = np.array([-1.9, -1.1, -2.0]) + initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) + distglobal = self.geometry.nom_addPointSet.addPointSet(initpts.flatten(), "mypts", cache_projections=False) + self.assertAlmostEqual(distglobal, 0.0, 8) + DVGeo._updateModel() + DVGeo._updateProjectedPts() + self.assertTrue(DVGeo.pointSetUpToDate) + self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) + + for dv in dvInfo: + self.geometry.nom_addESPVariable() + + self.dvs.add_output(dvName, dv["val"]) + self.connect(dvName, f"geometry.{dvName}") + self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) + + self.add_constraint(f"geometry.{ptName}") + + prob = Problem(model=ESPGroup()) + prob.setup(mode="rev") + + return prob + + def test_run_model(self): + self.prob.run_model() + + def testDVs(self): + self.prob.run_model() + + data = self.prob.check_totals(step=1e-7, compact_print=True) + for _, err in data.items(): + + rel_err = err["rel error"] + assert_near_equal(rel_err.forward, 0.0, 1e-5) + if __name__ == "__main__": unittest.main() From 8cf2e262ed5429766f32c0e76281fe509c05aba0 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 19 Jul 2024 11:45:22 -0400 Subject: [PATCH 09/46] Fix missing var check in totalSensitivityProd --- pygeo/parameterization/DVGeo.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pygeo/parameterization/DVGeo.py b/pygeo/parameterization/DVGeo.py index 2e70ce98..50a1f5a2 100644 --- a/pygeo/parameterization/DVGeo.py +++ b/pygeo/parameterization/DVGeo.py @@ -2498,7 +2498,7 @@ def totalSensitivityProd(self, vec, ptSetName, config=None): newvec = np.zeros(self.getNDV(), self.dtype) i = 0 - missingVars = set() # set of variables + missingVars = set(names) # set of variables to find in this DVGeo or its children for vecKey in vec: # check if the seed DV is actually a design variable for the DVGeo object if vecKey not in names: @@ -2522,8 +2522,6 @@ def totalSensitivityProd(self, vec, ptSetName, config=None): dv = geoObj.DV_listLocal[key] missingVars.discard(key) else: - # keep track of DVs which are in the full name list but not in this DVGeo object - missingVars.add(key) continue if key in vec: From 1dd8558403b51bb6fd25accaca5216f2e703f6bb Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 19 Jul 2024 11:46:11 -0400 Subject: [PATCH 10/46] Made projected area derivative inequality check consistent with primal --- pygeo/constraints/areaConstraint.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygeo/constraints/areaConstraint.py b/pygeo/constraints/areaConstraint.py index 821c5265..59bf8cf1 100644 --- a/pygeo/constraints/areaConstraint.py +++ b/pygeo/constraints/areaConstraint.py @@ -546,10 +546,10 @@ def evalFunctionsSens(self, funcsSens, config): v2 = p2[i, :] - p0[i, :] SAvec = np.cross(v1, v2) PA = np.dot(SAvec, self.axis) - if PA > 0: - PAb = areasb[i] - else: + if PA < 0: PAb = 0.0 + else: + PAb = areasb[i] SAvecb, _ = geo_utils.dot_b(SAvec, self.axis, PAb) v1b, v2b = geo_utils.cross_b(v1, v2, SAvecb) p2b[i, :] = p2b[i, :] + v2b From 65d20c6c0690e389f604b0b16459b376af86b7b9 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 19 Jul 2024 11:48:38 -0400 Subject: [PATCH 11/46] Add fwd mode derivatives to OM_DVGEOCOMP --- pygeo/mphys/mphys_dvgeo.py | 88 +++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 132119e4..80f7a574 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -890,10 +890,12 @@ def nom_setConstraintSurface( 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 - ni = len(list(d_inputs.keys())) + # only do the computations when we have more than zero entries in d_inputs + # in the reverse mode or d_outputs in the forward mode + doRev = mode == "rev" and len(list(d_inputs.keys())) > 0 + doFwd = mode == "fwd" and len(list(d_outputs.keys())) > 0 - if mode == "rev" and ni > 0: + if doFwd or doRev: # this flag will be set to True after every compute call. # if it is true, we assume the design has changed so we re-run the sensitivity update # there can be hundreds of calls to this routine due to thickness constraints, @@ -905,47 +907,63 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # set the flag to False so we dont run the update again if this is called w/o a compute in between self.update_jac = False + # Directly do Jacobian vector product with the derivatives from DVConstraints for constraintname in self.constraintfuncsens: for dvname in self.constraintfuncsens[constraintname]: - if dvname in d_inputs: + if constraintname in d_outputs and dvname in d_inputs: dcdx = self.constraintfuncsens[constraintname][dvname] - dout = d_outputs[constraintname] - jvtmp = np.dot(np.transpose(dcdx), dout) - d_inputs[dvname] += jvtmp + if doFwd: + din = d_inputs[dvname] + jvtmp = np.dot(dcdx, din) + d_outputs[constraintname] += jvtmp + elif doRev: + dout = d_outputs[constraintname] + jvtmp = np.dot(np.transpose(dcdx), dout) + d_inputs[dvname] += jvtmp for _, DVGeo in self.DVGeos.items(): + dvs = DVGeo.getVarNames() for ptSetName in DVGeo.ptSetNames: if ptSetName in self.omPtSetList: - dout = d_outputs[ptSetName].reshape(len(d_outputs[ptSetName]) // 3, 3) - - # only do the calc. if d_output is not zero on ANY proc - local_all_zeros = np.all(dout == 0) + # Process the seeds + if doFwd: + # Collect the d_inputs associated with the current DVGeo + seeds = {} + for k in d_inputs: + if k in dvs: + seeds[k] = d_inputs[k] + elif doRev: + seeds = d_outputs[ptSetName].reshape(len(d_outputs[ptSetName]) // 3, 3) + + # only do the calc. if seeds are not zero on ANY proc + local_all_zeros = np.all(seeds == 0) global_all_zeros = np.zeros(1, dtype=bool) # we need to communicate for this check otherwise we may hang self.comm.Allreduce([local_all_zeros, MPI.BOOL], [global_all_zeros, MPI.BOOL], MPI.LAND) - # global_all_zeros is a numpy array of size 1 + # Compute the Jacobian vector product if not global_all_zeros[0]: - # TODO totalSensitivityTransProd is broken. does not work with zero surface nodes on a proc - # xdot = DVGeo.totalSensitivityTransProd(dout, ptSetName) - xdot = DVGeo.totalSensitivity(dout, ptSetName) - - # loop over dvs and accumulate - xdotg = {} - for k in xdot: - # check if this dv is present - if k in d_inputs: - # do the allreduce - # TODO reove the allreduce when this is fixed in openmdao - # reduce the result ourselves for now. ideally, openmdao will do the reduction itself when this is fixed. this is because the bcast is also done by openmdao (pyoptsparse, but regardless, it is not done here, so reduce should also not be done here) - xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM) - - # accumulate in the dict - # TODO - # because we only do one point set at a time, we always want the 0th - # entry of this array since dvgeo always behaves like we are passing - # in multiple objective seeds with totalSensitivity. we can remove the [0] - # once we move back to totalSensitivityTransProd - d_inputs[k] += xdotg[k][0] - elif mode != "rev": - raise RuntimeError(f'OM_DVGEOCOMP supports only "rev" derivative mode, but "{mode}" was selected') + if doFwd: + d_outputs[ptSetName] += DVGeo.totalSensitivityProd(seeds, ptSetName) + elif doRev: + # TODO totalSensitivityTransProd is broken. does not work with zero surface nodes on a proc + # xdot = DVGeo.totalSensitivityTransProd(dout, ptSetName) + xdot = DVGeo.totalSensitivity(seeds, ptSetName) + + # loop over dvs and accumulate + xdotg = {} + for k in xdot: + # check if this dv is present + if k in d_inputs: + # do the allreduce + # TODO remove the allreduce when this is fixed in openmdao + # reduce the result ourselves for now. ideally, openmdao will do the reduction itself when this is fixed. this is because the bcast is also done by openmdao (pyoptsparse, but regardless, it is not done here, so reduce should also not be done here) + xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM) + + # accumulate in the dict + # TODO + # because we only do one point set at a time, we always want the 0th + # entry of this array since dvgeo always behaves like we are passing + # in multiple objective seeds with totalSensitivity. we can remove the [0] + # once we move back to totalSensitivityTransProd + d_inputs[k] += xdotg[k][0] From 35e9b70ac253f67f245381d8aa7055848fb05a2e Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 19 Jul 2024 12:06:41 -0400 Subject: [PATCH 12/46] Check both forward and reverse mode derivatives --- tests/reg_tests/test_MPhysGeo.py | 42 +++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 69fc544d..2ef09fae 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -147,15 +147,21 @@ def configure(self): self.add_constraint(f"geometry.{ptName}") - prob = Problem(model=FFDGroup()) - prob.setup(mode="rev") + self.prob = Problem(model=FFDGroup()) - self.prob = prob - def test_run_model(self): + self.prob.setup(mode="rev") self.prob.run_model() - def testDVs(self): + def test_deriv_fwd(self): + self.prob.setup(mode="fwd") + self.prob.run_model() + + totals = self.prob.check_totals(step=1e-7, out_stream=None) + assert_check_totals(totals) + + def test_deriv_rev(self): + self.prob.setup(mode="fwd") self.prob.run_model() totals = self.prob.check_totals(step=1e-7, out_stream=None) @@ -218,7 +224,6 @@ def twist(val, geo): self.add_objective(kwargs["name"]) p = Problem(model=BoxGeo()) - p.setup(mode="rev") return p def test_undeformed_vals(self): @@ -226,16 +231,35 @@ def test_undeformed_vals(self): Test the value of the functional on the baseline geometry. """ p = self.get_box_prob() + p.setup() p.run_model() val = p.get_val(self.kwargs["name"]) tol = 1e-5 if not hasattr(self, "valTol") else self.valTol assert_near_equal(val, self.valCheck, tolerance=tol) - def test_deformed_derivs(self): + def test_deformed_derivs_fwd(self): + """ + Test the total derivatives in forward mode on a random perturbation to the baseline. + """ + p = self.get_box_prob() + p.setup(mode="fwd") + + # Pick some random deformed state + p.set_val("twist", self.rand.random() * 10) + p.set_val("local", self.rand.random() * 10) + + p.run_model() + + # Check total derivatives using a directional derivatives + totals = p.check_totals(step=1e-6, out_stream=None, directional=False) + assert_check_totals(totals, atol=1e-5, rtol=3e-5) + + def test_deformed_derivs_rev(self): """ - Test the total derivatives on a random perturbation to the baseline. + Test the total derivatives in reverse mode on a random perturbation to the baseline. """ p = self.get_box_prob() + p.setup(mode="rev") # Pick some random deformed state p.set_val("twist", self.rand.random() * 10) @@ -244,7 +268,7 @@ def test_deformed_derivs(self): p.run_model() # Check total derivatives using a directional derivatives - totals = p.check_totals(step=1e-6, out_stream=None, directional=True) + totals = p.check_totals(step=1e-6, out_stream=None, directional=False) assert_check_totals(totals, atol=1e-5, rtol=3e-5) From 6f2e02e6a0ff8589585b766cddac9ae72eff88ef Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 19 Jul 2024 12:07:25 -0400 Subject: [PATCH 13/46] Removed explicit mode setting when not doing derivative test --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 2ef09fae..58d61ae0 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -150,7 +150,7 @@ def configure(self): self.prob = Problem(model=FFDGroup()) def test_run_model(self): - self.prob.setup(mode="rev") + self.prob.setup() self.prob.run_model() def test_deriv_fwd(self): From c6af0bd5ae99e665fb5493420ccbab95723974fb Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Mon, 12 Aug 2024 15:47:49 -0400 Subject: [PATCH 14/46] Reverse mode DVGeo derivatives should actually use reverse mode --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 58d61ae0..cfd7129d 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -161,7 +161,7 @@ def test_deriv_fwd(self): assert_check_totals(totals) def test_deriv_rev(self): - self.prob.setup(mode="fwd") + self.prob.setup(mode="rev") self.prob.run_model() totals = self.prob.check_totals(step=1e-7, out_stream=None) From 58debb9f724caa921c670960c748be66e16c7989 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Mon, 12 Aug 2024 15:51:18 -0400 Subject: [PATCH 15/46] Modify DVCon test FD step size --- tests/reg_tests/test_MPhysGeo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index cfd7129d..d351d1fb 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -268,8 +268,8 @@ def test_deformed_derivs_rev(self): p.run_model() # Check total derivatives using a directional derivatives - totals = p.check_totals(step=1e-6, out_stream=None, directional=False) - assert_check_totals(totals, atol=1e-5, rtol=3e-5) + totals = p.check_totals(step=1e-5, out_stream=None, directional=False) + assert_check_totals(totals, atol=5e-5, rtol=5e-5) if __name__ == "__main__": From f5ae8eaa0106a9397b6ad7d7a997fd8d74c766f8 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Mon, 12 Aug 2024 15:54:51 -0400 Subject: [PATCH 16/46] hide esp --- tests/reg_tests/test_MPhysGeo.py | 102 +++++++++++++++---------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 32214760..b61943d9 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -286,65 +286,65 @@ def test_deformed_derivs_rev(self): # parameters for ESP-based DVGeo tests -esp_test_params = [{"N_PROCS": 1, "name": "serial"}, {"N_PROCS": 4, "name": "parallel_4procs"}] - -@unittest.skipUnless(mphysInstalled and ocsmInstalled, "OpenMDAO, MPhys, and ESP are required to test the ESP part of the pyGeo MPhys wrapper") -@parameterized_class(esp_test_params) -class TestDVGeoMPhysESP(unittest.TestCase): - def setUp(self): - self.input_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - def modelSetup(self): - - class ESPGroup(Multipoint): - def setup(self): - self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) - self.add_subsystem("geometry", OM_DVGEOCOMP(file=espBox, type="esp")) - - def configure(self): - - # add a point set on the surface - vertex1 = np.array([-2.0, -2.0, -2.0]) - vertex2 = np.array([1.5, 1.5, 1.5]) - left = np.array([-2.0, -1.1, -1.1]) - right = np.array([1.5, -1.2, -0.1]) - front = np.array([0.25, 1.5, 0.3]) - back = np.array([1.2, -2.0, -0.3]) - top = np.array([0.0, 0.1, 1.5]) - bottom = np.array([-1.9, -1.1, -2.0]) - initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) - distglobal = self.geometry.nom_addPointSet.addPointSet(initpts.flatten(), "mypts", cache_projections=False) - self.assertAlmostEqual(distglobal, 0.0, 8) - DVGeo._updateModel() - DVGeo._updateProjectedPts() - self.assertTrue(DVGeo.pointSetUpToDate) - self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) - - for dv in dvInfo: - self.geometry.nom_addESPVariable() +# esp_test_params = [{"N_PROCS": 1, "name": "serial"}, {"N_PROCS": 4, "name": "parallel_4procs"}] + +# @unittest.skipUnless(mphysInstalled and ocsmInstalled, "OpenMDAO, MPhys, and ESP are required to test the ESP part of the pyGeo MPhys wrapper") +# @parameterized_class(esp_test_params) +# class TestDVGeoMPhysESP(unittest.TestCase): +# def setUp(self): +# self.input_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# def modelSetup(self): + +# class ESPGroup(Multipoint): +# def setup(self): +# self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) +# self.add_subsystem("geometry", OM_DVGEOCOMP(file=espBox, type="esp")) + +# def configure(self): + +# # add a point set on the surface +# vertex1 = np.array([-2.0, -2.0, -2.0]) +# vertex2 = np.array([1.5, 1.5, 1.5]) +# left = np.array([-2.0, -1.1, -1.1]) +# right = np.array([1.5, -1.2, -0.1]) +# front = np.array([0.25, 1.5, 0.3]) +# back = np.array([1.2, -2.0, -0.3]) +# top = np.array([0.0, 0.1, 1.5]) +# bottom = np.array([-1.9, -1.1, -2.0]) +# initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) +# distglobal = self.geometry.nom_addPointSet.addPointSet(initpts.flatten(), "mypts", cache_projections=False) +# self.assertAlmostEqual(distglobal, 0.0, 8) +# DVGeo._updateModel() +# DVGeo._updateProjectedPts() +# self.assertTrue(DVGeo.pointSetUpToDate) +# self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) + +# for dv in dvInfo: +# self.geometry.nom_addESPVariable() - self.dvs.add_output(dvName, dv["val"]) - self.connect(dvName, f"geometry.{dvName}") - self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) +# self.dvs.add_output(dvName, dv["val"]) +# self.connect(dvName, f"geometry.{dvName}") +# self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) - self.add_constraint(f"geometry.{ptName}") +# self.add_constraint(f"geometry.{ptName}") - prob = Problem(model=ESPGroup()) - prob.setup(mode="rev") +# prob = Problem(model=ESPGroup()) +# prob.setup(mode="rev") - return prob +# return prob - def test_run_model(self): - self.prob.run_model() +# def test_run_model(self): +# self.prob.run_model() - def testDVs(self): - self.prob.run_model() +# def testDVs(self): +# self.prob.run_model() - data = self.prob.check_totals(step=1e-7, compact_print=True) - for _, err in data.items(): +# data = self.prob.check_totals(step=1e-7, compact_print=True) +# for _, err in data.items(): - rel_err = err["rel error"] - assert_near_equal(rel_err.forward, 0.0, 1e-5) +# rel_err = err["rel error"] +# assert_near_equal(rel_err.forward, 0.0, 1e-5) if __name__ == "__main__": unittest.main() From 709a1e13f767fef16ea1ec061f79c4b2e64aeac1 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Mon, 12 Aug 2024 17:09:29 -0400 Subject: [PATCH 17/46] Cleaned up cross_b and dot_b --- pygeo/geo_utils/norm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygeo/geo_utils/norm.py b/pygeo/geo_utils/norm.py index 4ac721e4..d0363ff7 100644 --- a/pygeo/geo_utils/norm.py +++ b/pygeo/geo_utils/norm.py @@ -27,17 +27,17 @@ def cross_b(a, b, crossb): bb[1] = bb[1] + a[0] * crossb[2] ab[1] = ab[1] - b[0] * crossb[2] bb[0] = bb[0] - a[1] * crossb[2] - crossb[2] = 0.0 + ab[2] = ab[2] + b[0] * crossb[1] bb[0] = bb[0] + a[2] * crossb[1] ab[0] = ab[0] - b[2] * crossb[1] bb[2] = bb[2] - a[0] * crossb[1] - crossb[1] = 0.0 + ab[1] = ab[1] + b[2] * crossb[0] bb[2] = bb[2] + a[1] * crossb[0] ab[2] = ab[2] - b[1] * crossb[0] bb[1] = bb[1] - a[2] * crossb[0] - crossb[0] = 0.0 + return ab, bb @@ -48,8 +48,8 @@ def dot_b(a, b, dotb): ab = np.zeros_like(a) bb = np.zeros_like(b) - ab = ab + b * dotb - bb = bb + a * dotb + ab = b * dotb + bb = a * dotb return ab, bb From 863f065eeecd256db5f106e7a5e17b45cb702497 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Tue, 13 Aug 2024 10:53:59 -0400 Subject: [PATCH 18/46] add shape func dv test (doesn't work :/ --- tests/reg_tests/test_MPhysGeo.py | 49 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index b61943d9..d9c264f3 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -31,8 +31,9 @@ # input files for all DVGeo types input_path = os.path.dirname(os.path.abspath(__file__)) -parentFFDFile = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") -childFFDFile = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") +outerFFD = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") +innerFFD = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") +rectFFD = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") espBox = os.path.join(input_path, "../input_files/esp/box.csm") # parameters for FFD-based DVGeo tests @@ -40,15 +41,18 @@ globalDVFuncParamsParent = ["mainX", -1.0, commonUtils.mainAxisPoints] localDVFuncParamsParent = ["xdir"] globalDVFuncParamsChild = ["nestedX", -0.5, commonUtils.childAxisPoints, childName] +shapeFuncParamsParent = ["shapeFunc", []] globalDVParent = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsParent, "lower": -1.0, "upper": 0.0, "val": -1.0} localDVParent = {"funcName": "nom_addLocalDV", "funcParams": localDVFuncParamsParent, "lower": -1.0, "upper": 1.0, "val": 12*[0.0]} globalDVChild = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsChild, "lower": -1.0, "upper": 0.0, "val": -1.0} +shapeFuncDV = {"funcName": "nom_addShapeFunctionDV", "funcParams": shapeFuncParamsParent, "lower": -10.0, "upper": 10.0, "val": np.zeros((2))} ffd_test_params = [ - {"name": "MPhys_FFD_oneFFD_global", "dvInfo": [globalDVParent]}, - {"name": "MPhys_FFD_oneFFD_local", "dvInfo": [localDVParent]}, - {"name": "MPhys_FFD_childFFD_global", "dvInfo": [globalDVParent, globalDVChild]}, + {"name": "MPhys_FFD_oneFFD_global", "parentFFD": outerFFD, "childFFD": None, "dvInfo": [globalDVParent]}, # test_DVGeometry #1 + {"name": "MPhys_FFD_oneFFD_global+local", "parentFFD": outerFFD, "childFFD": None, "dvInfo": [globalDVParent, localDVParent]}, # test_DVGeometry #2 + {"name": "MPhys_FFD_childFFD_global", "parentFFD": outerFFD, "childFFD": innerFFD, "dvInfo": [globalDVParent, globalDVChild]}, # test_DVGeometry #3 + {"name": "MPhys_FFD_shapeFunc", "parentFFD": rectFFD, "childFFD": None, "dvInfo": [shapeFuncDV]}, # test_DVGeometry test_shape_functions ] # DVConstraints functionals to test @@ -125,15 +129,19 @@ class TestDVGeoMPhysFFD(unittest.TestCase): def setUp(self): # give the OM Group access to the test case attributes dvInfo = self.dvInfo + parentFFD = self.parentFFD + childFFD = self.childFFD class FFDGroup(Group): def setup(self): self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) - self.add_subsystem("geometry", OM_DVGEOCOMP(file=parentFFDFile, type="ffd")) + self.add_subsystem("geometry", OM_DVGEOCOMP(file=parentFFD, type="ffd")) def configure(self): - self.geometry.nom_addChild(childFFDFile, childName=childName) + # get the DVGeo object out of the geometry component + DVGeo = self.geometry.nom_getDVGeo() + # embed a dummy pointset in the parent FFD points = np.zeros([2, 3]) points[0, :] = [0.25, 0, 0] points[1, :] = [-0.25, 0, 0] @@ -145,15 +153,30 @@ def configure(self): c1 = Curve(X=axisPoints, k=2) self.geometry.nom_addRefAxis("mainAxis", curve=c1, axis="y") - # create a reference axis for the child - axisPoints = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]] - c1 = Curve(X=axisPoints, k=2) - self.geometry.nom_addRefAxis("nestedAxis", childName=childName, curve=c1, axis="y") + # local index is needed for shape function DV + lidx = DVGeo.getLocalIndex(0) + + # add child FFD if necessary for the test + if childFFD is not None: + self.geometry.nom_addChild(innerFFD, childName=childName) + # create a reference axis for the child + axisPoints = [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0]] + c1 = Curve(X=axisPoints, k=2) + self.geometry.nom_addRefAxis("nestedAxis", childName=childName, curve=c1, axis="y") + + # add each DV to the geometry for dv in dvInfo: dvName = dv["funcParams"][0] + + # parameters for shape func DV aren't known until the DVGeo object is created + if dv["funcName"] == "nom_addShapeFunctionDV": + dv["funcParams"][1] = commonUtils.getShapeFunc(lidx) + + # call the function being tested getattr(self.geometry, dv["funcName"])(*dv["funcParams"]) + # OM stuff self.dvs.add_output(dvName, dv["val"]) self.connect(dvName, f"geometry.{dvName}") self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) @@ -177,8 +200,8 @@ def test_deriv_rev(self): self.prob.setup(mode="rev") self.prob.run_model() - totals = self.prob.check_totals(step=1e-7, out_stream=None) - assert_check_totals(totals) + totals = self.prob.check_totals(step=1e-5, out_stream=None) + assert_check_totals(totals, atol=1e-5, rtol=1e-5) @unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper") From bb0705f66443402a1dbf54013e1f25af9594b16b Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Tue, 13 Aug 2024 10:54:21 -0400 Subject: [PATCH 19/46] pull generic part of shape func dv out to use it in mphys too --- tests/reg_tests/commonUtils.py | 52 ++++++++++++++++++++++++++++++ tests/reg_tests/test_DVGeometry.py | 46 +------------------------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/tests/reg_tests/commonUtils.py b/tests/reg_tests/commonUtils.py index 82a5b2b0..752c22c4 100644 --- a/tests/reg_tests/commonUtils.py +++ b/tests/reg_tests/commonUtils.py @@ -283,3 +283,55 @@ def spanX(val, geo): C[i, 0] *= val geo.restoreCoef(C, axis_key) + + +def getShapeFunc(lidx): + """ + Get shape dictionaries for use with shape function DVs. Common to DVGeometry and MPhys DVGeo tests. + Requires local index from DVGeo object and returns shapes. + """ + shape_1 = {} + shape_2 = {} + + k_center = 2 + i_center = 1 + n_chord = lidx.shape[0] + + for kk in [-1, 0, 1]: + if kk == 0: + k_weight = 1.0 + else: + k_weight = 0.5 + + for ii in range(n_chord): + # compute the chord weight. we want the shape to peak at i_center + if ii == i_center: + i_weight = 1.0 + elif ii < i_center: + # we are ahead of the center point + i_weight = ii / i_center + else: + # we are behind the center point + i_weight = (n_chord - ii - 1) / (n_chord - i_center - 1) + + # get the direction vectors with unit length + dir_up = np.array([0.0, 1.0, 0.0]) + # dir down can also be defined as an upwards pointing vector. Then, the DV itself + # getting a negative value means the surface would move down etc. For now, we define + # the vector as its pointing down, so a positive DV value moves the surface down. + dir_down = np.array([0.0, -1.0, 0.0]) + + # scale them by the i and k weights + dir_up *= k_weight * i_weight + dir_down *= k_weight * i_weight + + # get this point's global index and add to the dictionary with the direction vector. + gidx_up = lidx[ii, 1, kk + k_center] + gidx_down = lidx[ii, 0, kk + k_center] + + shape_1[gidx_up] = dir_up + # the lower face is perturbed with a separate dictionary + shape_2[gidx_down] = dir_down + + shapes = [shape_1, shape_2] + return shapes \ No newline at end of file diff --git a/tests/reg_tests/test_DVGeometry.py b/tests/reg_tests/test_DVGeometry.py index 487c6a7b..eaf23fa1 100644 --- a/tests/reg_tests/test_DVGeometry.py +++ b/tests/reg_tests/test_DVGeometry.py @@ -1401,51 +1401,7 @@ def test_shape_functions(self, train=False, refDeriv=False): # coef indices, which is what we need to set the shape lidx = DVGeo.getLocalIndex(0) - # create the dictionaries - shape_1 = {} - shape_2 = {} - - k_center = 2 - i_center = 1 - n_chord = lidx.shape[0] - - for kk in [-1, 0, 1]: - if kk == 0: - k_weight = 1.0 - else: - k_weight = 0.5 - - for ii in range(n_chord): - # compute the chord weight. we want the shape to peak at i_center - if ii == i_center: - i_weight = 1.0 - elif ii < i_center: - # we are ahead of the center point - i_weight = ii / i_center - else: - # we are behind the center point - i_weight = (n_chord - ii - 1) / (n_chord - i_center - 1) - - # get the direction vectors with unit length - dir_up = np.array([0.0, 1.0, 0.0]) - # dir down can also be defined as an upwards pointing vector. Then, the DV itself - # getting a negative value means the surface would move down etc. For now, we define - # the vector as its pointing down, so a positive DV value moves the surface down. - dir_down = np.array([0.0, -1.0, 0.0]) - - # scale them by the i and k weights - dir_up *= k_weight * i_weight - dir_down *= k_weight * i_weight - - # get this point's global index and add to the dictionary with the direction vector. - gidx_up = lidx[ii, 1, kk + k_center] - gidx_down = lidx[ii, 0, kk + k_center] - - shape_1[gidx_up] = dir_up - # the lower face is perturbed with a separate dictionary - shape_2[gidx_down] = dir_down - - shapes = [shape_1, shape_2] + shapes = commonUtils.getShapeFunc(lidx) DVGeo.addShapeFunctionDV("shape_func", shapes) # test derivatives From 230880b24d7f6eb894cb25bbd7f8f56565edf5d1 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Tue, 15 Oct 2024 10:52:58 -0400 Subject: [PATCH 20/46] Reenable fwd mode mphys tests --- tests/reg_tests/test_MPhysGeo.py | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index b6b8ac26..d9c264f3 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -189,12 +189,12 @@ def test_run_model(self): self.prob.setup() self.prob.run_model() - # def test_deriv_fwd(self): - # self.prob.setup(mode="fwd") - # self.prob.run_model() + def test_deriv_fwd(self): + self.prob.setup(mode="fwd") + self.prob.run_model() - # totals = self.prob.check_totals(step=1e-7, out_stream=None) - # assert_check_totals(totals) + totals = self.prob.check_totals(step=1e-7, out_stream=None) + assert_check_totals(totals) def test_deriv_rev(self): self.prob.setup(mode="rev") @@ -273,22 +273,22 @@ def test_undeformed_vals(self): tol = 1e-5 if not hasattr(self, "valTol") else self.valTol assert_near_equal(val, self.valCheck, tolerance=tol) - # def test_deformed_derivs_fwd(self): - # """ - # Test the total derivatives in forward mode on a random perturbation to the baseline. - # """ - # p = self.get_box_prob() - # p.setup(mode="fwd") + def test_deformed_derivs_fwd(self): + """ + Test the total derivatives in forward mode on a random perturbation to the baseline. + """ + p = self.get_box_prob() + p.setup(mode="fwd") - # # Pick some random deformed state - # p.set_val("twist", self.rand.random() * 10) - # p.set_val("local", self.rand.random() * 10) + # Pick some random deformed state + p.set_val("twist", self.rand.random() * 10) + p.set_val("local", self.rand.random() * 10) - # p.run_model() + p.run_model() - # # Check total derivatives using a directional derivatives - # totals = p.check_totals(step=1e-6, out_stream=None, directional=False) - # assert_check_totals(totals, atol=1e-5, rtol=3e-5) + # Check total derivatives using a directional derivatives + totals = p.check_totals(step=1e-6, out_stream=None, directional=False) + assert_check_totals(totals, atol=1e-5, rtol=3e-5) def test_deformed_derivs_rev(self): """ From 703272995a1a2d567fa335d46141c24c0df1be50 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Tue, 15 Oct 2024 11:05:56 -0400 Subject: [PATCH 21/46] Fix derivative problems with projected area constraint --- tests/reg_tests/test_MPhysGeo.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index d9c264f3..ecbc7269 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -211,14 +211,19 @@ def setUp(self): # Random number generator self.rand = np.random.default_rng(1) - def get_box_prob(self): + def get_box_prob(self, **kwargs): """ Generate an OpenMDAO problem with the OM_DVGEOCOMP component with the - functional dictated by the parameterized class. + functional dictated by the parameterized class. Custom keyword arguments + can be passed in, which will override any specified in the parameterized + constraint information. """ # Parameterized values conFunc = self.conFunc - kwargs = self.kwargs + paramKwargs = self.kwargs + + # Update the parameterized constraint keyword arguments with any manually specified ones + paramKwargs.update(kwargs) meshFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.stl") ffdFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") @@ -242,7 +247,7 @@ def configure(self): self.geo.nom_setConstraintSurface([p0, v1, v2], addToDVGeo=False) # Add the geometric functional - getattr(self.geo, conFunc)(**kwargs) + getattr(self.geo, conFunc)(**paramKwargs) # Add DVs nRefAxPts = self.geo.nom_addRefAxis("wing", xFraction=xFraction, alignIndex="k") @@ -257,7 +262,7 @@ def twist(val, geo): self.add_design_var("twist") self.add_design_var("local") - self.add_objective(kwargs["name"]) + self.add_objective(paramKwargs["name"]) p = Problem(model=BoxGeo()) return p @@ -277,7 +282,11 @@ def test_deformed_derivs_fwd(self): """ Test the total derivatives in forward mode on a random perturbation to the baseline. """ - p = self.get_box_prob() + if "addProjectedAreaConstraint" in self.conFunc: + # Use some random axis to avoid ill-conditioned derivatives + p = self.get_box_prob(axis=np.array([0.5, 3, -1])) + else: + p = self.get_box_prob() p.setup(mode="fwd") # Pick some random deformed state @@ -294,7 +303,12 @@ def test_deformed_derivs_rev(self): """ Test the total derivatives in reverse mode on a random perturbation to the baseline. """ - p = self.get_box_prob() + if "addProjectedAreaConstraint" in self.conFunc: + # Use some random axis to avoid ill-conditioned derivatives caused by triangle + # elements with normals orthogonal to the projection direction + p = self.get_box_prob(axis=np.array([0.5, 3, -1])) + else: + p = self.get_box_prob() p.setup(mode="rev") # Pick some random deformed state From cff644a7533e3778be2d6559243172536fb02a46 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Wed, 16 Oct 2024 11:53:15 -0400 Subject: [PATCH 22/46] formatting --- tests/reg_tests/test_MPhysGeo.py | 317 +++++++++++++++++++++++-------- 1 file changed, 234 insertions(+), 83 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index d9c264f3..92091f4c 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -43,86 +43,221 @@ globalDVFuncParamsChild = ["nestedX", -0.5, commonUtils.childAxisPoints, childName] shapeFuncParamsParent = ["shapeFunc", []] -globalDVParent = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsParent, "lower": -1.0, "upper": 0.0, "val": -1.0} -localDVParent = {"funcName": "nom_addLocalDV", "funcParams": localDVFuncParamsParent, "lower": -1.0, "upper": 1.0, "val": 12*[0.0]} -globalDVChild = {"funcName": "nom_addGlobalDV", "funcParams": globalDVFuncParamsChild, "lower": -1.0, "upper": 0.0, "val": -1.0} -shapeFuncDV = {"funcName": "nom_addShapeFunctionDV", "funcParams": shapeFuncParamsParent, "lower": -10.0, "upper": 10.0, "val": np.zeros((2))} +globalDVParent = { + "funcName": "nom_addGlobalDV", + "funcParams": globalDVFuncParamsParent, + "lower": -1.0, + "upper": 0.0, + "val": -1.0, +} +localDVParent = { + "funcName": "nom_addLocalDV", + "funcParams": localDVFuncParamsParent, + "lower": -1.0, + "upper": 1.0, + "val": 12 * [0.0], +} +globalDVChild = { + "funcName": "nom_addGlobalDV", + "funcParams": globalDVFuncParamsChild, + "lower": -1.0, + "upper": 0.0, + "val": -1.0, +} +shapeFuncDV = { + "funcName": "nom_addShapeFunctionDV", + "funcParams": shapeFuncParamsParent, + "lower": -10.0, + "upper": 10.0, + "val": np.zeros((2)), +} ffd_test_params = [ - {"name": "MPhys_FFD_oneFFD_global", "parentFFD": outerFFD, "childFFD": None, "dvInfo": [globalDVParent]}, # test_DVGeometry #1 - {"name": "MPhys_FFD_oneFFD_global+local", "parentFFD": outerFFD, "childFFD": None, "dvInfo": [globalDVParent, localDVParent]}, # test_DVGeometry #2 - {"name": "MPhys_FFD_childFFD_global", "parentFFD": outerFFD, "childFFD": innerFFD, "dvInfo": [globalDVParent, globalDVChild]}, # test_DVGeometry #3 - {"name": "MPhys_FFD_shapeFunc", "parentFFD": rectFFD, "childFFD": None, "dvInfo": [shapeFuncDV]}, # test_DVGeometry test_shape_functions + { + "name": "MPhys_FFD_oneFFD_global", + "parentFFD": outerFFD, + "childFFD": None, + "dvInfo": [globalDVParent], + }, # test_DVGeometry #1 + { + "name": "MPhys_FFD_oneFFD_global+local", + "parentFFD": outerFFD, + "childFFD": None, + "dvInfo": [globalDVParent, localDVParent], + }, # test_DVGeometry #2 + { + "name": "MPhys_FFD_childFFD_global", + "parentFFD": outerFFD, + "childFFD": innerFFD, + "dvInfo": [globalDVParent, globalDVChild], + }, # test_DVGeometry #3 + { + "name": "MPhys_FFD_shapeFunc", + "parentFFD": rectFFD, + "childFFD": None, + "dvInfo": [shapeFuncDV], + }, # test_DVGeometry test_shape_functions ] # DVConstraints functionals to test test_params_constraints_box = [ { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 3, "axis": [1, 0, 0], "scaled": False}, + "kwargs": { + "name": "func", + "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], + "nCon": 3, + "axis": [1, 0, 0], + "scaled": False, + }, "valCheck": 2 * np.ones(3), "valTol": 1e-4, }, { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 5, "axis": [0, 1, 0], "scaled": False}, + "kwargs": { + "name": "func", + "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], + "nCon": 5, + "axis": [0, 1, 0], + "scaled": False, + }, "valCheck": np.ones(5), "valTol": 3e-5, }, { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], "nCon": 5, "axis": [0, 0, 1], "scaled": False}, + "kwargs": { + "name": "func", + "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], + "nCon": 5, + "axis": [0, 0, 1], + "scaled": False, + }, "valCheck": 8 * np.ones(5), }, { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 3, "axis": [1, 0, 0], "scaled": False, "projected": True}, + "kwargs": { + "name": "func", + "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], + "nCon": 3, + "axis": [1, 0, 0], + "scaled": False, + "projected": True, + }, "valCheck": 2 * np.ones(3), "valTol": 2e-4, }, { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], "nCon": 5, "axis": [0, 1, 0], "scaled": False, "projected": True}, + "kwargs": { + "name": "func", + "ptList": [[0.0, 0.0, 0.1], [0.0, 0.0, 7.9]], + "nCon": 5, + "axis": [0, 1, 0], + "scaled": False, + "projected": True, + }, "valCheck": np.ones(5), "valTol": 2e-4, }, { "conFunc": "nom_addThicknessConstraints1D", - "kwargs": {"name": "func", "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], "nCon": 5, "axis": [0, 0, 1], "scaled": False, "projected": True}, + "kwargs": { + "name": "func", + "ptList": [[-0.5, 0.0, 4.0], [0.5, 0.0, 4.0]], + "nCon": 5, + "axis": [0, 0, 1], + "scaled": False, + "projected": True, + }, "valCheck": 8 * np.ones(5), }, { "conFunc": "nom_addThicknessConstraints2D", - "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False}, + "kwargs": { + "name": "func", + "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]], + "nSpan": 2, + "nChord": 3, + "scaled": False, + }, "valCheck": np.ones(6), }, { "conFunc": "nom_addThicknessConstraints2D", - "kwargs": {"name": "func", "leList": [[0.0, -0.25, 0.1], [0.0, -0.25, 7.9]], "teList": [[0.0, 0.25, 0.1], [0.0, 0.25, 7.9]], "nSpan": 2, "nChord": 3, "scaled": False}, + "kwargs": { + "name": "func", + "leList": [[0.0, -0.25, 0.1], [0.0, -0.25, 7.9]], + "teList": [[0.0, 0.25, 0.1], [0.0, 0.25, 7.9]], + "nSpan": 2, + "nChord": 3, + "scaled": False, + }, "valCheck": 2 * np.ones(6), }, { "conFunc": "nom_addThicknessConstraints2D", - "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False}, + "kwargs": { + "name": "func", + "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]], + "nSpan": 2, + "nChord": 3, + "scaled": False, + }, "valCheck": 8 * np.ones(6), }, { "conFunc": "nom_addThicknessConstraints2D", - "kwargs": {"name": "func", "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]], "nSpan": 2, "nChord": 3, "scaled": False, "projected": True}, + "kwargs": { + "name": "func", + "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]], + "nSpan": 2, + "nChord": 3, + "scaled": False, + "projected": True, + }, "valCheck": 8 * np.ones(6), }, { "conFunc": "nom_addVolumeConstraint", - "kwargs": {"name": "func", "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]], "nSpan": 4, "nChord": 4, "scaled": False}, + "kwargs": { + "name": "func", + "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]], + "nSpan": 4, + "nChord": 4, + "scaled": False, + }, "valCheck": 4.0, "valTol": 1e-4, }, {"conFunc": "nom_addSurfaceAreaConstraint", "kwargs": {"name": "func", "scaled": False}, "valCheck": 52.0}, - {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "x", "scaled": False}, "valCheck": 8.0, "valTol": 3e-2}, - {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "y", "scaled": False}, "valCheck": 16.0, "valTol": 3e-2}, - {"conFunc": "nom_addProjectedAreaConstraint", "kwargs": {"name": "func", "axis": "z", "scaled": False}, "valCheck": 2.0, "valTol": 3e-2}, + { + "conFunc": "nom_addProjectedAreaConstraint", + "kwargs": {"name": "func", "axis": "x", "scaled": False}, + "valCheck": 8.0, + "valTol": 3e-2, + }, + { + "conFunc": "nom_addProjectedAreaConstraint", + "kwargs": {"name": "func", "axis": "y", "scaled": False}, + "valCheck": 16.0, + "valTol": 3e-2, + }, + { + "conFunc": "nom_addProjectedAreaConstraint", + "kwargs": {"name": "func", "axis": "z", "scaled": False}, + "valCheck": 2.0, + "valTol": 3e-2, + }, ] + @unittest.skipUnless(omInstalled, "OpenMDAO is required to test the pyGeo MPhys wrapper") @parameterized_class(ffd_test_params) class TestDVGeoMPhysFFD(unittest.TestCase): @@ -165,7 +300,7 @@ def configure(self): c1 = Curve(X=axisPoints, k=2) self.geometry.nom_addRefAxis("nestedAxis", childName=childName, curve=c1, axis="y") - # add each DV to the geometry + # add each DV to the geometry for dv in dvInfo: dvName = dv["funcParams"][0] @@ -175,7 +310,7 @@ def configure(self): # call the function being tested getattr(self.geometry, dv["funcName"])(*dv["funcParams"]) - + # OM stuff self.dvs.add_output(dvName, dv["val"]) self.connect(dvName, f"geometry.{dvName}") @@ -309,65 +444,81 @@ def test_deformed_derivs_rev(self): # parameters for ESP-based DVGeo tests -# esp_test_params = [{"N_PROCS": 1, "name": "serial"}, {"N_PROCS": 4, "name": "parallel_4procs"}] - -# @unittest.skipUnless(mphysInstalled and ocsmInstalled, "OpenMDAO, MPhys, and ESP are required to test the ESP part of the pyGeo MPhys wrapper") -# @parameterized_class(esp_test_params) -# class TestDVGeoMPhysESP(unittest.TestCase): -# def setUp(self): -# self.input_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -# def modelSetup(self): - -# class ESPGroup(Multipoint): -# def setup(self): -# self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) -# self.add_subsystem("geometry", OM_DVGEOCOMP(file=espBox, type="esp")) - -# def configure(self): - -# # add a point set on the surface -# vertex1 = np.array([-2.0, -2.0, -2.0]) -# vertex2 = np.array([1.5, 1.5, 1.5]) -# left = np.array([-2.0, -1.1, -1.1]) -# right = np.array([1.5, -1.2, -0.1]) -# front = np.array([0.25, 1.5, 0.3]) -# back = np.array([1.2, -2.0, -0.3]) -# top = np.array([0.0, 0.1, 1.5]) -# bottom = np.array([-1.9, -1.1, -2.0]) -# initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) -# distglobal = self.geometry.nom_addPointSet.addPointSet(initpts.flatten(), "mypts", cache_projections=False) -# self.assertAlmostEqual(distglobal, 0.0, 8) -# DVGeo._updateModel() -# DVGeo._updateProjectedPts() -# self.assertTrue(DVGeo.pointSetUpToDate) -# self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) - -# for dv in dvInfo: -# self.geometry.nom_addESPVariable() - -# self.dvs.add_output(dvName, dv["val"]) -# self.connect(dvName, f"geometry.{dvName}") -# self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) - -# self.add_constraint(f"geometry.{ptName}") - -# prob = Problem(model=ESPGroup()) -# prob.setup(mode="rev") - -# return prob - -# def test_run_model(self): -# self.prob.run_model() - -# def testDVs(self): -# self.prob.run_model() - -# data = self.prob.check_totals(step=1e-7, compact_print=True) -# for _, err in data.items(): - -# rel_err = err["rel error"] -# assert_near_equal(rel_err.forward, 0.0, 1e-5) +esp_test_params = [ + {"name": "serial", "N_PROCS": 1}, + {"name": "parallel_4procs", "N_PROCS": 4}, +] + + +@unittest.skipUnless( + omInstalled and ocsmInstalled, + "OpenMDAO, MPhys, and ESP are required to test the ESP part of the pyGeo MPhys wrapper", +) +@parameterized_class(esp_test_params) +class TestDVGeoMPhysESP(unittest.TestCase): + def setUp(self): + procs = self.N_PROCS + + def modelSetup(self): + class ESPGroup(Group): + def setup(self): + self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) + self.add_subsystem("geometry", OM_DVGEOCOMP(file=espBox, type="esp")) + + def configure(self): + # get the DVGeo object out of the geometry component + DVGeo = self.geometry.nom_getDVGeo() + + # add a point set on the surface + vertex1 = np.array([-2.0, -2.0, -2.0]) + vertex2 = np.array([1.5, 1.5, 1.5]) + left = np.array([-2.0, -1.1, -1.1]) + right = np.array([1.5, -1.2, -0.1]) + front = np.array([0.25, 1.5, 0.3]) + back = np.array([1.2, -2.0, -0.3]) + top = np.array([0.0, 0.1, 1.5]) + bottom = np.array([-1.9, -1.1, -2.0]) + initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) + distglobal = self.geometry.nom_addPointSet.addPointSet( + initpts.flatten(), "mypts", cache_projections=False + ) + self.assertAlmostEqual(distglobal, 0.0, 8) + DVGeo._updateModel() + DVGeo._updateProjectedPts() + self.assertTrue(DVGeo.pointSetUpToDate) + self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) + + for dv in dvInfo: + self.geometry.nom_addESPVariable() + + self.dvs.add_output(dvName, dv["val"]) + self.connect(dvName, f"geometry.{dvName}") + self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) + + self.add_constraint(f"geometry.{ptName}") + + prob = Problem(model=ESPGroup()) + + return prob + + def test_run_model(self): + self.prob.setup() + self.prob.run_model() + + def test_deriv_fwd(self): + self.prob.setup(mode="fwd") + self.prob.run_model() + + totals = self.prob.check_totals(step=1e-7, out_stream=None) + assert_check_totals(totals) + + def test_deriv_rev(self): + self.prob.setup(mode="rev") + self.prob.run_model() + + totals = self.prob.check_totals(step=1e-5, out_stream=None) + assert_check_totals(totals, atol=1e-5, rtol=1e-5) + if __name__ == "__main__": unittest.main() From 0521abfd8b1ea6ada50689b7163387963f51e521 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Wed, 16 Oct 2024 11:53:35 -0400 Subject: [PATCH 23/46] ESP test --- tests/reg_tests/test_MPhysGeo.py | 41 +++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 92091f4c..5bdfe1d1 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -7,6 +7,7 @@ import commonUtils from pygeo.mphys import OM_DVGEOCOMP from pyspline import Curve +from baseclasses.utils import Error try: from openmdao.api import IndepVarComp, Problem, Group @@ -444,9 +445,13 @@ def test_deformed_derivs_rev(self): # parameters for ESP-based DVGeo tests +fullESPDV = {"name": "cubex0", "lower": np.array([-10.0]), "upper": np.array([10.0]), "scale": 0.1, "dh": 0.0001} +simpleESPDV = {"name": "cubey0"} +midESPDV = {"name": "cubez0", "lower": np.array([-10.0]), "upper": np.array([10.0])} + esp_test_params = [ - {"name": "serial", "N_PROCS": 1}, - {"name": "parallel_4procs", "N_PROCS": 4}, + {"name": "serial", "N_PROCS": 1, "dvInfo": [fullESPDV, simpleESPDV, midESPDV]}, + {"name": "parallel_4procs", "N_PROCS": 4, "dvInfo": [fullESPDV, simpleESPDV, midESPDV]}, ] @@ -457,6 +462,8 @@ def test_deformed_derivs_rev(self): @parameterized_class(esp_test_params) class TestDVGeoMPhysESP(unittest.TestCase): def setUp(self): + # give the OM Group access to the test case attributes + dvInfo = self.dvInfo procs = self.N_PROCS def modelSetup(self): @@ -468,6 +475,7 @@ def setup(self): def configure(self): # get the DVGeo object out of the geometry component DVGeo = self.geometry.nom_getDVGeo() + self.assertIsNotNone(DVGeo) # add a point set on the surface vertex1 = np.array([-2.0, -2.0, -2.0]) @@ -479,21 +487,42 @@ def configure(self): top = np.array([0.0, 0.1, 1.5]) bottom = np.array([-1.9, -1.1, -2.0]) initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) + + ptName = "mypts" distglobal = self.geometry.nom_addPointSet.addPointSet( - initpts.flatten(), "mypts", cache_projections=False + initpts.flatten(), ptName, cache_projections=False ) self.assertAlmostEqual(distglobal, 0.0, 8) DVGeo._updateModel() DVGeo._updateProjectedPts() self.assertTrue(DVGeo.pointSetUpToDate) - self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets["mypts"].proj_pts), 0.0, 10) + self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets[ptName].proj_pts), 0.0, 10) for dv in dvInfo: - self.geometry.nom_addESPVariable() + if "upper" not in dv: + dv["upper"] = None + + if "lower" not in dv: + dv["lower"] = None + + if "scale" not in dv: + dv["scale"] = None + + if "dh" not in dv: + dv["dh"] = 0.001 + + dvName = dv["name"] + self.geometry.nom_addESPVariable(dvName, dh=dv["dh"]) self.dvs.add_output(dvName, dv["val"]) self.connect(dvName, f"geometry.{dvName}") - self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"]) + + self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"], scaler=dv["scale"]) + + self.assertIsNotNone(DVGeo) + + with self.assertRaises(Error): + self.geometry.nom_addESPVariable("cubew0") self.add_constraint(f"geometry.{ptName}") From dbf00aecc94c097edcaa3e1a91a6cb2ebe4b7b06 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Wed, 16 Oct 2024 11:53:58 -0400 Subject: [PATCH 24/46] kwargs -> real args --- pygeo/mphys/mphys_dvgeo.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 59e9a2ec..77a56427 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -498,7 +498,32 @@ def nom_addVSPVariable(self, component, group, parm, isComposite=False, DVGeoNam if not isComposite: self.add_input(dvName, distributed=False, shape=1, val=val) - def nom_addESPVariable(self, desmptr_name, isComposite=False, DVGeoName=None, **kwargs): + def nom_addESPVariable(self, desmptr_name, rows=None, cols=None, dh=0.001, isComposite=False, DVGeoName=None): + """ + Add an ESP design variables to the DVGeometryESP object + Wrapper for :meth:`addVariable <.DVGeometryESP.addVariable>` + Input parameters are identical to those in wrapped function unless otherwise specified + + Parameters + ---------- + desmptr_name : str + See :meth:`addVariable <.DVGeometryESP.addVariable>` + rows : list or None, optional + See :meth:`addVariable <.DVGeometryESP.addVariable>` + cols : list or None, optional + See :meth:`addVariable <.DVGeometryESP.addVariable>` + dh : float, optional + See :meth:`addVariable <.DVGeometryESP.addVariable>` + isComposite : bool, optional + Whether this DV is to be included in the composite DVs, by default False + DVGeoName : string, optional + The name of the DVGeo to add DVs to, necessary if there are multiple DVGeo objects + + Raises + ------ + RuntimeError + Raised if the underlying DVGeo parameterization is not ESP-based + """ # if we have multiple DVGeos use the one specified by name DVGeo = self.nom_getDVGeo(DVGeoName=DVGeoName) @@ -507,7 +532,7 @@ def nom_addESPVariable(self, desmptr_name, isComposite=False, DVGeoName=None, ** raise RuntimeError(f"Only ESP-based DVGeo objects can use ESP DVs, not type: {type(DVGeo).__name__}") # actually add the DV to ESP - DVGeo.addVariable(desmptr_name, **kwargs) + DVGeo.addVariable(desmptr_name, rows=rows, cols=cols, dh=dh) # get the value val = DVGeo.DVs[desmptr_name].value.copy() From 48bdc15f6d34cf94c71759dd94c14d22a766c8aa Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Wed, 16 Oct 2024 15:29:41 -0400 Subject: [PATCH 25/46] fix --- tests/reg_tests/test_MPhysGeo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 5bdfe1d1..963b49db 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -466,7 +466,6 @@ def setUp(self): dvInfo = self.dvInfo procs = self.N_PROCS - def modelSetup(self): class ESPGroup(Group): def setup(self): self.add_subsystem("dvs", IndepVarComp(), promotes=["*"]) From 53102a7ebd9ffe92dbdedc96cfd32a1138753dd0 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 12:16:16 -0400 Subject: [PATCH 26/46] add parameter from DVGeoESP --- pygeo/mphys/mphys_dvgeo.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 487991fb..29c427dc 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -201,7 +201,11 @@ def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, **kwa DVGeo = self.nom_getDVGeo(DVGeoName=DVGeoName) # add the points to the dvgeo object - DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) + if isinstance(DVGeo, DVGeometryESP): + # DVGeoESP can return a value to check the pointset distribution + dMax_global = DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) + else: + DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) self.omPtSetList.append(ptName) if isinstance(DVGeo, DVGeometry): @@ -215,6 +219,9 @@ def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, **kwa # add an output to the om component self.add_output(ptName, distributed=True, val=points.flatten()) + if isinstance(DVGeo, DVGeometryESP): + return dMax_global + def nom_add_point_dict(self, point_dict): # add every pointset in the dict, and set the ptset name as the key for k, v in point_dict.items(): From 5a6c42aff75ae7ddbabe780f10e72d3d416a6be8 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 12:16:29 -0400 Subject: [PATCH 27/46] fix ESP test --- tests/reg_tests/test_MPhysGeo.py | 37 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 13622af5..40838af5 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -35,7 +35,7 @@ outerFFD = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") innerFFD = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") rectFFD = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") -espBox = os.path.join(input_path, "../input_files/esp/box.csm") +espBox = os.path.join(input_path, "../../input_files/esp/box.csm") # parameters for FFD-based DVGeo tests childName = "childFFD" @@ -478,7 +478,6 @@ class TestDVGeoMPhysESP(unittest.TestCase): def setUp(self): # give the OM Group access to the test case attributes dvInfo = self.dvInfo - procs = self.N_PROCS class ESPGroup(Group): def setup(self): @@ -488,7 +487,6 @@ def setup(self): def configure(self): # get the DVGeo object out of the geometry component DVGeo = self.geometry.nom_getDVGeo() - self.assertIsNotNone(DVGeo) # add a point set on the surface vertex1 = np.array([-2.0, -2.0, -2.0]) @@ -499,17 +497,13 @@ def configure(self): back = np.array([1.2, -2.0, -0.3]) top = np.array([0.0, 0.1, 1.5]) bottom = np.array([-1.9, -1.1, -2.0]) - initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) + self.initpts = np.vstack([vertex1, vertex2, left, right, front, back, top, bottom, left, right]) ptName = "mypts" - distglobal = self.geometry.nom_addPointSet.addPointSet( - initpts.flatten(), ptName, cache_projections=False - ) - self.assertAlmostEqual(distglobal, 0.0, 8) + self.distglobal = self.geometry.nom_addPointSet(self.initpts.flatten(), ptName, cache_projections=False) + self.projPts = DVGeo.pointSets[ptName].proj_pts DVGeo._updateModel() DVGeo._updateProjectedPts() - self.assertTrue(DVGeo.pointSetUpToDate) - self.assertAlmostEqual(np.linalg.norm(initpts - DVGeo.pointSets[ptName].proj_pts), 0.0, 10) for dv in dvInfo: if "upper" not in dv: @@ -527,24 +521,29 @@ def configure(self): dvName = dv["name"] self.geometry.nom_addESPVariable(dvName, dh=dv["dh"]) - self.dvs.add_output(dvName, dv["val"]) + self.dvs.add_output(dvName) self.connect(dvName, f"geometry.{dvName}") self.add_design_var(dvName, upper=dv["upper"], lower=dv["lower"], scaler=dv["scale"]) - self.assertIsNotNone(DVGeo) - - with self.assertRaises(Error): - self.geometry.nom_addESPVariable("cubew0") - self.add_constraint(f"geometry.{ptName}") - prob = Problem(model=ESPGroup()) - - return prob + self.prob = Problem(model=ESPGroup()) def test_run_model(self): self.prob.setup() + with self.assertRaises(Error): + try: + self.prob.model.geometry.nom_addESPVariable("cubew0") + except Error as e: + mes = e.message + raise e + + self.assertEqual(mes, 'User specified design parameter name "cubew0" which was not found in the CSM file') + + assert_near_equal(self.prob.model.distglobal, 0.0, 8) + assert_near_equal(np.linalg.norm(self.prob.model.initpts - self.prob.model.projPts), 0.0, 10) + self.prob.run_model() def test_deriv_fwd(self): From f3b7cee3d49401848d5f04f45c4ddb5503c12add Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:32:03 -0400 Subject: [PATCH 28/46] Adjust tol --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 40838af5..3dee7489 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -433,7 +433,7 @@ def test_deformed_derivs_fwd(self): # Check total derivatives using a directional derivatives totals = p.check_totals(step=1e-6, out_stream=None, directional=False) - assert_check_totals(totals, atol=1e-5, rtol=3e-5) + assert_check_totals(totals, atol=1e-5, rtol=1e-4) def test_deformed_derivs_rev(self): """ From 86c2926c54aae2b6595fabedd58e114e377ca397 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:36:50 -0400 Subject: [PATCH 29/46] Fix formatting --- tests/reg_tests/commonUtils.py | 2 +- tests/reg_tests/test_MPhysGeo.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/reg_tests/commonUtils.py b/tests/reg_tests/commonUtils.py index 752c22c4..063944ad 100644 --- a/tests/reg_tests/commonUtils.py +++ b/tests/reg_tests/commonUtils.py @@ -334,4 +334,4 @@ def getShapeFunc(lidx): shape_2[gidx_down] = dir_down shapes = [shape_1, shape_2] - return shapes \ No newline at end of file + return shapes diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 3dee7489..4dcab97f 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,11 +19,7 @@ omInstalled = False try: - # External modules - import pyOCSM # noqa - - # First party modules - from pygeo import DVGeometryESP + from pyOCSM import ocsm ocsmInstalled = True From 5dc707178f878d5606f71e5055c2122bc82680a6 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:41:50 -0400 Subject: [PATCH 30/46] Please flake --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 4dcab97f..bcd361ae 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,7 +19,7 @@ omInstalled = False try: - from pyOCSM import ocsm + from pyOCSM ocsmInstalled = True From 7603a7bf55900d9d140e9899bce4621169e90a36 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:43:42 -0400 Subject: [PATCH 31/46] Fix bad python --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index bcd361ae..aceb4ee2 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,7 +19,7 @@ omInstalled = False try: - from pyOCSM + import pyOCSM ocsmInstalled = True From 7f0cf59647f83e41238a5bf44f9b20b5f71d01af Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:49:47 -0400 Subject: [PATCH 32/46] Ignore unused import --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index aceb4ee2..70edb11e 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,7 +19,7 @@ omInstalled = False try: - import pyOCSM + import pyOCSM # noqa: E999 ocsmInstalled = True From b61fafd1c8589f6fc41e08c0d241b50afdf5836e Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:52:23 -0400 Subject: [PATCH 33/46] Flake 8 fixes --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 70edb11e..c69a5de6 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,7 +19,7 @@ omInstalled = False try: - import pyOCSM # noqa: E999 + import pyOCSM # noqa: E999,F401 ocsmInstalled = True From fc39ee922eed74d1c0e32f60b05b7d00225bc9ad Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 12:53:11 -0400 Subject: [PATCH 34/46] Remove previous uneeded ignore --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index c69a5de6..9e6b87ec 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -19,7 +19,7 @@ omInstalled = False try: - import pyOCSM # noqa: E999,F401 + import pyOCSM # noqa: F401 ocsmInstalled = True From 1b4b27ac33f5b69e79d142976568c0f8e5af76dc Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 13:00:09 -0400 Subject: [PATCH 35/46] isort --- tests/reg_tests/test_MPhysGeo.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 9e6b87ec..cba92e15 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -1,17 +1,18 @@ -import unittest import os +import unittest + +from baseclasses.utils import Error import numpy as np from parameterized import parameterized_class +from pygeo.mphys import OM_DVGEOCOMP +from pyspline import Curve from stl import mesh import commonUtils -from pygeo.mphys import OM_DVGEOCOMP -from pyspline import Curve -from baseclasses.utils import Error try: - from openmdao.api import IndepVarComp, Problem, Group - from openmdao.utils.assert_utils import assert_near_equal, assert_check_totals + from openmdao.api import Group, IndepVarComp, Problem + from openmdao.utils.assert_utils import assert_check_totals, assert_near_equal omInstalled = True From 7fadb247f61a19899c01078e8331973fcaee6117 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 13:01:58 -0400 Subject: [PATCH 36/46] first try --- tests/reg_tests/test_MPhysGeo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index cba92e15..5f63d38c 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -1,6 +1,8 @@ +# Standard Python modules import os import unittest +# External modules from baseclasses.utils import Error import numpy as np from parameterized import parameterized_class @@ -8,6 +10,7 @@ from pyspline import Curve from stl import mesh +# Local modules import commonUtils try: From 6aa078192eb656320def9f098a52ec088478ebfd Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 13:03:34 -0400 Subject: [PATCH 37/46] first try for sure --- tests/reg_tests/test_MPhysGeo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 5f63d38c..34a5c889 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -3,15 +3,15 @@ import unittest # External modules -from baseclasses.utils import Error import numpy as np from parameterized import parameterized_class -from pygeo.mphys import OM_DVGEOCOMP -from pyspline import Curve from stl import mesh # Local modules +from baseclasses.utils import Error import commonUtils +from pygeo.mphys import OM_DVGEOCOMP +from pyspline import Curve try: from openmdao.api import Group, IndepVarComp, Problem From 06681520486f6b0c56ecd6deda1c125ad29649ec Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 13:09:40 -0400 Subject: [PATCH 38/46] sure --- tests/reg_tests/test_MPhysGeo.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 34a5c889..081018df 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -3,17 +3,18 @@ import unittest # External modules +from baseclasses.utils import Error import numpy as np from parameterized import parameterized_class +from pygeo.mphys import OM_DVGEOCOMP +from pyspline import Curve from stl import mesh -# Local modules -from baseclasses.utils import Error +# First party modules import commonUtils -from pygeo.mphys import OM_DVGEOCOMP -from pyspline import Curve try: + # External modules from openmdao.api import Group, IndepVarComp, Problem from openmdao.utils.assert_utils import assert_check_totals, assert_near_equal @@ -23,6 +24,7 @@ omInstalled = False try: + # External modules import pyOCSM # noqa: F401 ocsmInstalled = True From e2d742fd85f60eeeeabf24367366001e0d191d35 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Thu, 17 Oct 2024 13:12:09 -0400 Subject: [PATCH 39/46] ? --- tests/reg_tests/test_MPhysGeo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 081018df..69ae0798 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -10,9 +10,6 @@ from pyspline import Curve from stl import mesh -# First party modules -import commonUtils - try: # External modules from openmdao.api import Group, IndepVarComp, Problem @@ -32,6 +29,9 @@ except ImportError: ocsmInstalled = False +# First party modules +import commonUtils + # input files for all DVGeo types input_path = os.path.dirname(os.path.abspath(__file__)) outerFFD = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") From a321803ac731d27ed1f53c7fd69df039155fdc16 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Thu, 17 Oct 2024 13:19:07 -0400 Subject: [PATCH 40/46] This one will work --- tests/reg_tests/test_MPhysGeo.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 69ae0798..4742d01d 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -4,12 +4,15 @@ # External modules from baseclasses.utils import Error +import commonUtils import numpy as np from parameterized import parameterized_class -from pygeo.mphys import OM_DVGEOCOMP from pyspline import Curve from stl import mesh +# First party modules +from pygeo.mphys import OM_DVGEOCOMP + try: # External modules from openmdao.api import Group, IndepVarComp, Problem @@ -29,8 +32,6 @@ except ImportError: ocsmInstalled = False -# First party modules -import commonUtils # input files for all DVGeo types input_path = os.path.dirname(os.path.abspath(__file__)) From fb15b8b26bc381f3764c72f5e73285d7f481b12c Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 25 Oct 2024 16:17:52 -0400 Subject: [PATCH 41/46] Remove directional kwarg that was unused and broke old OM versions --- tests/reg_tests/test_MPhysGeo.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 4742d01d..639f777f 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -434,8 +434,7 @@ def test_deformed_derivs_fwd(self): p.run_model() - # Check total derivatives using a directional derivatives - totals = p.check_totals(step=1e-6, out_stream=None, directional=False) + totals = p.check_totals(step=1e-6, out_stream=None) assert_check_totals(totals, atol=1e-5, rtol=1e-4) def test_deformed_derivs_rev(self): @@ -456,8 +455,7 @@ def test_deformed_derivs_rev(self): p.run_model() - # Check total derivatives using a directional derivatives - totals = p.check_totals(step=1e-5, out_stream=None, directional=False) + totals = p.check_totals(step=1e-5, out_stream=None) assert_check_totals(totals, atol=5e-5, rtol=5e-5) From 4871a3dd8c7fc7c6813f4ca2cb3a9ce40446d6f8 Mon Sep 17 00:00:00 2001 From: Hannah Hajdik Date: Fri, 25 Oct 2024 16:19:04 -0400 Subject: [PATCH 42/46] take out parallel case - test not set up for it --- tests/reg_tests/test_MPhysGeo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 4742d01d..d0973c00 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -468,7 +468,6 @@ def test_deformed_derivs_rev(self): esp_test_params = [ {"name": "serial", "N_PROCS": 1, "dvInfo": [fullESPDV, simpleESPDV, midESPDV]}, - {"name": "parallel_4procs", "N_PROCS": 4, "dvInfo": [fullESPDV, simpleESPDV, midESPDV]}, ] From e735915fc0490d7e0147e6453020d9d4fd3dea5c Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 25 Oct 2024 19:00:08 -0400 Subject: [PATCH 43/46] You're not gonna believe this one cool trick --- tests/reg_tests/test_MPhysGeo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index 639f777f..55a056f7 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -1,6 +1,7 @@ # Standard Python modules import os import unittest +import copy # External modules from baseclasses.utils import Error @@ -359,7 +360,7 @@ def get_box_prob(self, **kwargs): """ # Parameterized values conFunc = self.conFunc - paramKwargs = self.kwargs + paramKwargs = copy.deepcopy(self.kwargs) # Update the parameterized constraint keyword arguments with any manually specified ones paramKwargs.update(kwargs) From d44180105acbfa8f5f472bffc0c087bb81688e6e Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Fri, 25 Oct 2024 19:02:18 -0400 Subject: [PATCH 44/46] Isort is the devil --- tests/reg_tests/test_MPhysGeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index ac7fcd60..cb271e72 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -1,7 +1,7 @@ # Standard Python modules +import copy import os import unittest -import copy # External modules from baseclasses.utils import Error From 47121719e8f10a062723dcea476b5a7a9d11f444 Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Tue, 12 Nov 2024 09:48:53 -0500 Subject: [PATCH 45/46] Address Andrew's comments --- pygeo/mphys/mphys_dvgeo.py | 13 +++---------- tests/reg_tests/commonUtils.py | 26 ++++++++++++-------------- tests/reg_tests/test_MPhysGeo.py | 16 ++++++++-------- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/pygeo/mphys/mphys_dvgeo.py b/pygeo/mphys/mphys_dvgeo.py index 9d27168a..14eb74ee 100644 --- a/pygeo/mphys/mphys_dvgeo.py +++ b/pygeo/mphys/mphys_dvgeo.py @@ -201,9 +201,10 @@ def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, **kwa 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 - dMax_global = DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) + dMaxGlobal = DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) else: DVGeo.addPointSet(points.reshape(len(points) // 3, 3), ptName, **kwargs) self.omPtSetList.append(ptName) @@ -219,8 +220,7 @@ def nom_addPointSet(self, points, ptName, add_output=True, DVGeoName=None, **kwa # add an output to the om component self.add_output(ptName, distributed=True, val=points.flatten()) - if isinstance(DVGeo, DVGeometryESP): - return dMax_global + return dMaxGlobal def nom_add_point_dict(self, point_dict): # add every pointset in the dict, and set the ptset name as the key @@ -1071,14 +1071,7 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): # check if this dv is present if k in d_inputs: # do the allreduce - # TODO remove the allreduce when this is fixed in openmdao - # reduce the result ourselves for now. ideally, openmdao will do the reduction itself when this is fixed. this is because the bcast is also done by openmdao (pyoptsparse, but regardless, it is not done here, so reduce should also not be done here) xdotg[k] = self.comm.allreduce(xdot[k], op=MPI.SUM) # accumulate in the dict - # TODO - # because we only do one point set at a time, we always want the 0th - # entry of this array since dvgeo always behaves like we are passing - # in multiple objective seeds with totalSensitivity. we can remove the [0] - # once we move back to totalSensitivityTransProd d_inputs[k] += xdotg[k][0] diff --git a/tests/reg_tests/commonUtils.py b/tests/reg_tests/commonUtils.py index 063944ad..1c434582 100644 --- a/tests/reg_tests/commonUtils.py +++ b/tests/reg_tests/commonUtils.py @@ -285,10 +285,11 @@ def spanX(val, geo): geo.restoreCoef(C, axis_key) -def getShapeFunc(lidx): +def getShapeFunc(lidx, direction=None): """ Get shape dictionaries for use with shape function DVs. Common to DVGeometry and MPhys DVGeo tests. - Requires local index from DVGeo object and returns shapes. + Requires local index from DVGeo object and optionally a three-element numpy array for the + positive direction vector and returns shapes. """ shape_1 = {} shape_2 = {} @@ -297,6 +298,11 @@ def getShapeFunc(lidx): i_center = 1 n_chord = lidx.shape[0] + d_up = np.array([0.0, 1.0, 0.0]) + if direction is not None: + d_up = direction + d_up /= np.linalg.norm(d_up) + for kk in [-1, 0, 1]: if kk == 0: k_weight = 1.0 @@ -314,24 +320,16 @@ def getShapeFunc(lidx): # we are behind the center point i_weight = (n_chord - ii - 1) / (n_chord - i_center - 1) - # get the direction vectors with unit length - dir_up = np.array([0.0, 1.0, 0.0]) - # dir down can also be defined as an upwards pointing vector. Then, the DV itself - # getting a negative value means the surface would move down etc. For now, we define - # the vector as its pointing down, so a positive DV value moves the surface down. - dir_down = np.array([0.0, -1.0, 0.0]) - - # scale them by the i and k weights - dir_up *= k_weight * i_weight - dir_down *= k_weight * i_weight + # scale direction by the i and k weights + d_up_scaled = d_up * k_weight * i_weight # get this point's global index and add to the dictionary with the direction vector. gidx_up = lidx[ii, 1, kk + k_center] gidx_down = lidx[ii, 0, kk + k_center] - shape_1[gidx_up] = dir_up + shape_1[gidx_up] = d_up_scaled # the lower face is perturbed with a separate dictionary - shape_2[gidx_down] = dir_down + shape_2[gidx_down] = -d_up_scaled shapes = [shape_1, shape_2] return shapes diff --git a/tests/reg_tests/test_MPhysGeo.py b/tests/reg_tests/test_MPhysGeo.py index cb271e72..fd757c77 100644 --- a/tests/reg_tests/test_MPhysGeo.py +++ b/tests/reg_tests/test_MPhysGeo.py @@ -36,10 +36,10 @@ # input files for all DVGeo types input_path = os.path.dirname(os.path.abspath(__file__)) -outerFFD = os.path.join(input_path, "../../input_files/outerBoxFFD.xyz") -innerFFD = os.path.join(input_path, "../../input_files/simpleInnerFFD.xyz") -rectFFD = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") -espBox = os.path.join(input_path, "../../input_files/esp/box.csm") +outerFFD = os.path.join(input_path, "..", "..", "input_files", "outerBoxFFD.xyz") +innerFFD = os.path.join(input_path, "..", "..", "input_files", "simpleInnerFFD.xyz") +rectFFD = os.path.join(input_path, "..", "..", "input_files", "2x1x8_rectangle.xyz") +espBox = os.path.join(input_path, "..", "..", "input_files", "esp", "box.csm") # parameters for FFD-based DVGeo tests childName = "childFFD" @@ -334,7 +334,7 @@ def test_deriv_fwd(self): self.prob.run_model() totals = self.prob.check_totals(step=1e-7, out_stream=None) - assert_check_totals(totals) + assert_check_totals(totals, atol=1e-6, rtol=1e-6) def test_deriv_rev(self): self.prob.setup(mode="rev") @@ -365,8 +365,8 @@ def get_box_prob(self, **kwargs): # Update the parameterized constraint keyword arguments with any manually specified ones paramKwargs.update(kwargs) - meshFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.stl") - ffdFile = os.path.join(input_path, "../../input_files/2x1x8_rectangle.xyz") + meshFile = os.path.join(input_path, "..", "..", "input_files", "2x1x8_rectangle.stl") + ffdFile = os.path.join(input_path, "..", "..", "input_files", "2x1x8_rectangle.xyz") xFraction = 0.5 meshScale = 1.0 @@ -552,7 +552,7 @@ def test_deriv_fwd(self): self.prob.run_model() totals = self.prob.check_totals(step=1e-7, out_stream=None) - assert_check_totals(totals) + assert_check_totals(totals, atol=1e-6, rtol=1e-6) def test_deriv_rev(self): self.prob.setup(mode="rev") From ec99fe4de6a6acbb9c5d10ba14d8b9d784e46c0b Mon Sep 17 00:00:00 2001 From: Eytan Adler <63426601+eytanadler@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:31:30 -0500 Subject: [PATCH 46/46] Jostle the interpretation --- pygeo/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygeo/__init__.py b/pygeo/__init__.py index 3bdc954e..b10efb25 100644 --- a/pygeo/__init__.py +++ b/pygeo/__init__.py @@ -1,4 +1,4 @@ -__version__ = "1.14.0" +__version__ = "1.15.0" from .pyNetwork import pyNetwork from .pyGeo import pyGeo