Skip to content

Implement support for pygeo and OM wrapper for custom meshes#64

Open
sabakhshi wants to merge 75 commits intojoanibal:mainfrom
sabakhshi:bakhshi_patch8
Open

Implement support for pygeo and OM wrapper for custom meshes#64
sabakhshi wants to merge 75 commits intojoanibal:mainfrom
sabakhshi:bakhshi_patch8

Conversation

@sabakhshi
Copy link
Copy Markdown
Collaborator

@sabakhshi sabakhshi commented Mar 2, 2026

This PR includes support for pygeo FFD parameterization in the OVLSolver class and support for dictionary input/custom meshes in the OM wrapper.

The pygeo api is very easy to use. The user just needs to set up their FFD box and their DVGeo object with all design variables set. Then it's just a matter of setting the DVGeo instance to the OVLSolver object using the set_DVGeo function. To apply any changes made to the DVGeo design variables to the OptVL mesh the update_DVGeo function needs to be called. This will embed any mesh surfaces in the FFD if needed and then perform the pointset update. The updated point set will then be set back into OptVL and the and update surfaces routine called to update the Fortran layer. Currently, OptVL will try to embed all meshes into the same FFD so for now we suggest that FFDs only be used with cases that only have a single mesh surface. A unit test is included for the pyGeo API. A FFD box generator for VLM meshes is also included in utilities.

The OM wrapper can also now be used with meshes and input dictionaries. When a mesh is used for a particular surface all geometry inputs for that surfaces are replaced with a single mesh input. The shape of this mesh input is set by the input_mesh_shape option in the OVLGroup class. Input arrays shapes of 1,2, or 3 dimensions can be chosen with it defaulting to 1 (which is a flat array). This input shape selection allows for wide compatibility with a variety of geometry parameterization components. A unit test for the mesh features in the OM wrapper are also included.

sabakhshi and others added 30 commits October 15, 2025 16:55
… fixed scale and translate of custom meshes not working
…fortran layer indexing bugs. runtime mesh updating still needs to be implemented
@sabakhshi sabakhshi marked this pull request as ready for review April 1, 2026 21:57
@sabakhshi sabakhshi requested a review from joanibal April 1, 2026 21:57
@sabakhshi sabakhshi changed the title Implement support for pygeo Implement support for pygeo and OM wrapper for custom meshes Apr 2, 2026
Copy link
Copy Markdown
Owner

@joanibal joanibal left a comment

Choose a reason for hiding this comment

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

Nice work! Here are a few questions and suggestions

self.set_reference_ad_seeds(ref_seeds)

# Since DVGeo seeds operate entirely within the python layer we set them here
if self.DVGeo is not None and dvgeo_seeds is not None:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This chunk appears 4 times with some variation. Could we generalize these chunks into a function?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I get what your saying but all four of these chunks have a different task despite the first couple lines being shared. Fundamentally, the first couple lines of each of these loops check if a surface has a pointset associated with it so I execute the desired task on a surface that is actually embedded in the FFD. I could put this check into another function but I can't generalize the actual task each chunk does. So there would still be a check if we have DVGeo and there would be a loop over surfaces with pointsets. Should I still do that?

sens[func_key][surf_key] = geom_seeds[surf_key] | mesh_seeds[surf_key]
for key in geom_seeds:
sens[func_key][key] = geom_seeds[key] | mesh_seeds[key] | dvgeo_seeds[key]
# sens[func_key].update(geom_seeds)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

left over comments? or was that intentional

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It was intentional. I wasn't sure what you thought of my approach of doing a union here. Basically you have all the seeds associated with the surface under a key of that surface's name. This was fine when all that was under that key was the geom_seeds but now we have mesh seeds and dvgeo seeds that are also associated with a surface and placed under that surfaces key name. We have to merge these dictionaries into one for easier optimization and merging nested dictionaries in Python is annoying and the union approach is the best I came up with. What do you think of this?

# prob.model.add_design_var("ovlsolver.Wing:zles")
# prob.model.add_design_var("ovlsolver.Wing:chords")
# prob.model.add_design_var("ovlsolver.Wing:mesh") # This is a really costly test
# prob.model.add_design_var("ovlsolver.Wing:aincs") # dCL/dalpha on the first element fails
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Can you explain this comment?

Copy link
Copy Markdown
Collaborator Author

@sabakhshi sabakhshi Apr 9, 2026

Choose a reason for hiding this comment

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

Yes. The first few I should delete since those inputs don't exist for a mesh surface. The test with respect to the mesh is really costly and dramatically increases the time required to run the tests since it computes the total derivative wrt to each (x,y,z) component of each point in the mesh. I could use a smaller mesh but I wanted to use what was given in aircraft.avl to match the nonmesh OM wrapper stuff. The last comment is test that I am struggling to get passed. When computing d(dCL/dalpha)/d(aincs), I found that the derivative wrt to the first two indicies of aincs was off.

UPDATE: Now that I look at the last issue again I think it would be fine if we just relaxed the test tolerance. For some reason I recall this being orders of magnitude off but it appear it isn't.

AssertionError: Not equal to tolerance rtol=0.01, atol=1e-09 deriv of ovlsolver.dCL/dalpha wrt ovlsolver.Wing:aincs does not agree with FD to rtol=0.01 Mismatched elements: 1 / 21 (4.76%) Mismatch at index: [0, 1]: -7.450580596923828e-09 (ACTUAL), 4.960020677913534e-09 (DESIRED) Max absolute difference among violations: 1.24106013e-08 Max relative difference among violations: 2.50212692 ACTUAL: array([[ 3.241003e-07, -7.450581e-09, -1.247972e-06, -4.470348e-07, 9.872019e-06, 7.454306e-06, 8.620322e-06, 3.216788e-05, 5.760044e-05, 9.340048e-05, 1.171790e-04, 1.486540e-04,... DESIRED: array([[ 3.213856e-07, 4.960021e-09, -1.235720e-06, -4.461515e-07, 9.864236e-06, 7.449540e-06, 8.614240e-06, 3.216888e-05, 5.758156e-05, 9.340394e-05, 1.171690e-04, 1.486690e-04,...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants