Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedding tolerance update #116

Merged
merged 13 commits into from
Mar 2, 2022
Merged

Embedding tolerance update #116

merged 13 commits into from
Mar 2, 2022

Conversation

marcomangano
Copy link
Contributor

@marcomangano marcomangano commented Feb 15, 2022

Purpose

The arbitrary tolerance currently set to determine if a point is embedded when interiorOnly is True (children FFDs most commonly use this option) was creating issues in some of my cases, leaving some points stationary as the rest of the mesh/geometry object is deformed:

This PR addressed this issue doing two things:

  • Updates the tolerance embTol to 1e-10 instead of eps * 50 (with eps being the tolerance for the Newton search in the projection subroutine in pySpline) and decouples it from eps;
  • enables the user to pass embTol as an argument for easier tuning of the mesh deformation properties.

Note that, as attachPoints is called from addPointSet, which is in turn usually called from other solvers methods (see how it's done in ADflow), other sibling PRs should be opened to propagate the new option to the user level.

Co-authored with @sseraj

Type of change

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (non-backwards-compatible fix or feature)
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)
  • Documentation update
  • Maintenance update
  • Other (please describe)

Testing

Checklist

  • I have run flake8 and black to make sure the code adheres to PEP-8 and is consistently formatted
  • I have run unit and regression tests which pass locally with my changes
  • I have added new tests that prove my fix is effective or that my feature works
  • I have added necessary documentation

@codecov
Copy link

codecov bot commented Feb 15, 2022

Codecov Report

Merging #116 (fdc2273) into master (743c55c) will decrease coverage by 0.00%.
The diff coverage is 100.00%.

❗ Current head fdc2273 differs from pull request most recent head fe012ee. Consider uploading reports for the commit fe012ee to get more accurate results

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #116      +/-   ##
==========================================
- Coverage   63.86%   63.86%   -0.01%     
==========================================
  Files          41       41              
  Lines       10932    10931       -1     
==========================================
- Hits         6982     6981       -1     
  Misses       3950     3950              
Impacted Files Coverage Δ
pygeo/parameterization/DVGeo.py 66.13% <100.00%> (ø)
pygeo/pyBlock.py 47.02% <100.00%> (-0.11%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 743c55c...fe012ee. Read the comment docs.

@marcomangano marcomangano marked this pull request as ready for review February 15, 2022 19:33
@marcomangano marcomangano requested a review from a team as a code owner February 15, 2022 19:33
@sseraj sseraj mentioned this pull request Feb 15, 2022
12 tasks
Copy link
Contributor

@sseraj sseraj left a comment

Choose a reason for hiding this comment

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

This looks fine to me, but I'll leave the approvals to others because I was involved in making the changes

Copy link
Contributor

@eirikurj eirikurj left a comment

Choose a reason for hiding this comment

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

Minor comments, let me know if this is unclear.

pygeo/pyBlock.py Outdated
Comment on lines 796 to 797
nIter = kwargs.get("nIter", 100)
eps = kwargs.get("eps", 1e-12)
Copy link
Contributor

Choose a reason for hiding this comment

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

Dont see the need to expand these here, as they are not used/needed in this function (see the comment below).
Also, this is creating "artificial" defaults for the following function, instead of using the func default.

pygeo/pyBlock.py Outdated
Comment on lines 802 to 809
volID, u, v, w, D = self.projectPoints(
coordinates, eps=eps, checkErrors=True, nIter=nIter, embTol=embTol
)
self.embeddedVolumes[ptSetName] = EmbeddedVolume(volID, u, v, w)
else:
volID, u, v, w, D = self.projectPoints(coordinates, checkErrors=False, eps=eps, nIter=nIter)
volID, u, v, w, D = self.projectPoints(
coordinates, eps=eps, checkErrors=False, nIter=nIter, embTol=embTol
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this function already takes in **kwargs (and keywords are not changed and passed to next func) why not just pass it on as **kwargs here instead of expanding and passing explicitly nIter and eps?

@@ -600,6 +613,8 @@ def addPointSet(self, points, ptName, origConfig=True, **kwargs):

"""

embTol = kwargs.get("embTol", 1e-10) # see pyBlock.attachPoints() documentation
Copy link
Contributor

Choose a reason for hiding this comment

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

Dont think this is needed here as its not used (see comment below for pyBlock on kwargs)

Comment on lines 634 to 636
self.FFD.attachPoints(self.points[ptName], ptName, interiorOnly=True, embTol=embTol, **kwargs)
else:
self.FFD.attachPoints(self.points[ptName], ptName, interiorOnly=False)
self.FFD.attachPoints(self.points[ptName], ptName, interiorOnly=False, embTol=embTol, **kwargs)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. See other comments on kwargs

@marcomangano
Copy link
Contributor Author

@eirikurj : We were waiting for a feedback on that! @sseraj and I spent some time discussing about it, and the final criteria we picked was to have the docstring for the argument (which then must be passed explicitly into the function) where the argument is actually used.

As embTol is used in attachPoints(), we expand it and pass it from the higher level - same way we do for nIter and eps in attachPoints() before passing them to projectPoints(). No strong opinion here, we were just looking for a consistent approach that puts the docstrings where the attributes are actually used.

Hope this is clear, if you still prefer to move all the expansions down to one level we can do it.

@eirikurj
Copy link
Contributor

@marcomangano I agree that the appropriate docstrings should be where the function parameters are defined/given, and thats all fine.
But, I just dont see the need to extract the values just to pass it on explicitly, unless its being used in that function or has some purpose, which it is not in this case. I feel this is not what the user expects, and is providing different defaults if the value is not given through the kwags. In that case, I would prefer an explicit function signature instead of relying on kwargs. There are however some implications by both approaches.
Instead of writing something long here, I think we could have a quick chat offline to discuss this in case I am missing something. I can also make quick edits and just push to a separate branch to show you what I had in mind.

@eirikurj
Copy link
Contributor

@marcomangano I pushed what we discussed offline, here are the two branches that have the changes/suggestions that we talked about. Would be good to have some discussion.

  • Suggestion 1 - This propagates the kwargs downstream. Benefits are that you dont have to define the downstream parameters like eps in other functions, they only appear in the function that they are defined. The downside is that potentially garbage kwargs are propagated all downstream, (like faceFreeze in the test).
  • Suggestion 2 - This has only addPointSet to take in kwargs since that is the only one that needs it, but the rest has explicit signatures. The benefits here that everything is explicit, and all defaults defined in the function. The downside is that the defaults are defined in several places.

@marcomangano
Copy link
Contributor Author

@marcomangano I pushed what we discussed offline, here are the two branches that have the changes/suggestions that we talked about. Would be good to have some discussion.

* [Suggestion 1](https://github.com/mdolab/pygeo/compare/ffd-bugfix...eirikurj:ffd-bugfix-patch1) - This propagates the `kwargs` downstream. Benefits are that you dont have to define the downstream parameters like `eps` in other functions, they only appear in the function that they are defined. The downside is that potentially garbage `kwargs` are propagated all downstream, (like `faceFreeze` in the test).

* [Suggestion 2](https://github.com/mdolab/pygeo/compare/ffd-bugfix...eirikurj:ffd-bugfix-patch2) - This has only `addPointSet` to take in `kwargs` since that is the only one that needs it, but the rest has explicit signatures. The benefits here that everything is explicit, and all defaults defined in the function. The downside is that the defaults are defined in several places.

Thanks for putting this together!!! From my point of view option 1 is better - and we can add a warning/check on **kwargs to be sure the user is aware of the garbage kwargs. What do you think about it?

marcomangano and others added 3 commits February 22, 2022 13:03
* Properly passing kwargs

* More fixes

* Remove removed variable in tests

Co-authored-by: Eirikur Jonsson <eirikurj@umich.edu>
@marcomangano
Copy link
Contributor Author

@eirikurj check the second-to-last commit, if we remove **kwargs from projectpoints() we should catch any unwanted kwarg in that function

@eirikurj
Copy link
Contributor

@marcomangano I dont think you can remove the **kwargs from projectPoints since addPointSet needs to be able to take in whatever to support the DVGeoMulti. The tests are failing due to that.

@sseraj
Copy link
Contributor

sseraj commented Feb 22, 2022

@eirikurj After some discussion, we decided to go for an approach closer to your Suggestion 2. Following a suggestion from @nwu63, I removed all keyword arguments from projectPoints to avoid duplicate definitions. All kwargs are now defined in attachPoints. Please check that this makes sense.

@marcomangano This should also catch unused kwargs. Popping compNames in addPointSet explicitly excludes it.

@@ -600,6 +613,9 @@ def addPointSet(self, points, ptName, origConfig=True, **kwargs):

"""

# compNames is only needed for DVGeometryMulti, so remove it if passed
kwargs.pop("compNames", None)
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a minor concern. DVGeoMulti has more arguments than just compNames, like applyIC and comm. While no constraints that are calling addPointSet are including these parameters at the moment, they may. Should we pop them here? If not, the following call to attachPoints will fail. To avoid that, you are basically back to my Suggestion 2. Not sure if this is a concern. @sseraj you may have more insights into if that is possibly needed for the constraints when using DVGeoMulti.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am fine with popping all the unnecessary kwargs here

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't anticipate adding the other kwargs like comm and applyIC to the constraints. If we do end up adding them in the future, we can also pop them. I would like to keep this list to a minimum though just so we catch any potential errors.

eirikurj
eirikurj previously approved these changes Feb 24, 2022
@marcomangano
Copy link
Contributor Author

@joanibal could you take a look at this?

@joanibal
Copy link
Collaborator

Sure, sorry I didn't know who was responsible for this review

Copy link
Collaborator

@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.

One small request

pygeo/pyBlock.py Outdated

# Project Points, if some were actually passed in:
if coordinates is not None:
if not interiorOnly:
volID, u, v, w, D = self.projectPoints(coordinates, checkErrors=True, eps=eps, nIter=nIter)
volID, u, v, w, D = self.projectPoints(coordinates, True, embTol, eps, nIter)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we get the CheckErrors=True back the in the function call, otherwise one has to look at projectPoints to see what the True is doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We had quite a long discussion about removing kwargs there, I would rather add a local var checkErrors : bool in this function and pass that instead of the bare True of False - would it work?

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the idea of adding a checkErrors local variable. My proposal would be to do checkErrors = not interiorOnly and move the projectPoints calls outside the if not interiorOnly block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

on it

self.embeddedVolumes[ptSetName] = EmbeddedVolume(volID, u, v, w)
else:
volID, u, v, w, D = self.projectPoints(coordinates, checkErrors=False, eps=eps, nIter=nIter)
Copy link
Collaborator

Choose a reason for hiding this comment

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

same as above

@@ -823,7 +818,7 @@ def attachPoints(self, coordinates, ptSetName, interiorOnly=False, faceFreeze=No
# Geometric Functions
# ----------------------------------------------------------------------

def projectPoints(self, x0, eps=1e-12, checkErrors=True, nIter=100):
def projectPoints(self, x0, checkErrors, embTol, eps, nIter):
Copy link
Collaborator

Choose a reason for hiding this comment

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

See earlier comments about keeping checkErrors a key word arg

@marcomangano
Copy link
Contributor Author

@eirikurj @sseraj @joanibal check out the updated function and let me know if this is clearer and cleaner. I now just wonder... should we add some kind of message at least, if we call attachPoints(None) ? Because right now nothing happens, and I am a bit confused about the scenario where we call attachPoints.. without points!

@sseraj
Copy link
Contributor

sseraj commented Feb 28, 2022

There are some scenarios where we have empty point sets (like in parallel when some procs have points and others don't). I think this is fine as is because attachPoints is only used internally.

@sseraj sseraj requested a review from eirikurj February 28, 2022 19:43
Copy link
Contributor

@sseraj sseraj left a comment

Choose a reason for hiding this comment

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

@marcomangano I think you can merge this. The last change was fairly minor.

@marcomangano marcomangano merged commit 80cf705 into master Mar 2, 2022
@marcomangano marcomangano deleted the ffd-bugfix branch March 2, 2022 22:14
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.

None yet

4 participants