-
Notifications
You must be signed in to change notification settings - Fork 53
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
Higher auto-refinement limits #285
Conversation
lib/vsdata.cpp
Outdated
auto_ref_max = 16; | ||
auto_ref_max_surf_elem = 20000; | ||
auto_ref_max = 32; | ||
auto_ref_max_surf_elem = 5000000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a need to increase auto_ref_max
-- 16 is more than sufficient for any practical case. For extreme cases it can be increased manually with the keys.
Also, I think increasing auto_ref_max_surf_elem
to 25x
from
Another option is to keep the current value and instead issue a warning in the terminal (we may want to add a feature where terminal messages are shown for a brief moment in the main window and then fade away) when the auto-selected refinement factor is less than the max of the orders of the mesh and the order of the field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what we want ideally is that these are set to the maximum values for what a "reasonable" mesh may need, and then for GLVis to select the actual auto-refinement values based on the order of the mesh (and solution).
For example auto_ref_max
should be at least max{ element_order, gf_order}
, maybe scaled by a factor of 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, maximum of some reasonable value and a value based on the grid function + mesh order 👍 , but it could be capped by some higher number maybe? To give the user a chance to change the value and not burn off the computer right away 🔥 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, "reasonable" auto-selected refinement factor is mainly driven by the time it takes to display the initial mesh. If possible, displaying the initial mesh should not take longer than ~0.1-0.3 sec on relatively new hardware. If we roughly say that the time to display some surface is proportional to to the point evaluations we need to perform, then we need to choose an upper bound for auto_ref_max_surf_elem
which gives the desired time limit. Of course, achieving this limit may not be possible when the number of the drawn surface elements is too big.
In the methods VisualizationSceneSolution::GetAutoRefineFactor()
and VisualizationSceneSolution3d::GetAutoRefineFactor()
, the number of point evaluations is computed as ne*(ref+1)*(ref+1)
where ne
is the number of surface elements in the mesh (i.e. number of elements in 2D meshes, embedded in 2D or 3D, or the number of boundary elements for 3D meshes). So the question is: how many point evaluations can we do in the allowed ~0.1-0.3 sec -- we can debate what exact value to aim for in this range. The evaluation speed per point will depend to some extend on the orders of the mesh and the field, so to make things more concrete, let's say we should measure the evaluation times for
Does this sound like a "reasonable" approach to determine the upper limit auto_ref_max_surf_elem
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regardless of how we do the auto-selection of refinement level, users will need to be aware that for big meshes they may not get any element sub-divisions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There could be if order_ref
will give a number lower than 20k, or no? 🤔 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order_ref
is just an initial value for ref
before the while
loop (when the if
is true). So if order_ref < 16 && ne*(order_ref+1)*(order_ref+1) <= 100000
, you will get more refinements than order_ref
and no less refinements than before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem, for me, with @tzanio's suggestion (even when the while
loop is moved after the if else
statement) is that it can result in big jumps in speed for small change in the input. For example, if ne*(order_ref+1)*(order_ref+1)
is close but less than 2M we get ref=order_ref
and if we increase ne
just a little to push ne*(order_ref+1)*(order_ref+1)
beyond 2M, then can immediately go down to ref=1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, like this, yes, in the modified version it would work, but I thought it is more logical to iterate to the same limiting number as in the criterion for order_ref
to keep continuity and not jump down, as you say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I was proposing would solve it (updated version to have the same numbers):
int ne = mesh->GetNE(), ref = 1;
int autoref_max = min(max(ne*(order_ref+1)*(order_ref+1), 100k), 2M)
while (ref < 16 && ne*(ref+1)*(ref+1) <= autoref_max) { ref++; }
(which implies ref==order_ref
if it is in the range and order_ref < 16
)
I created a poll for this, please vote here: #292 |
How about this? 😉 Please test it, it cannot pass the regression tests for obvious reasons and I do not have much time for it. |
Now all tests run, but there are some visible differences in the zoom level interestingly 🤔 . |
just making sure - this isn't the difference between the In order to compare to the baselines you have to download the baselines separately. I'm looking at the test outputs and see some differences but I don't see any visible differences in zoom. e.g. ( |
lib/vsdata.cpp
Outdated
} | ||
|
||
// limit the total number of elements | ||
int auto_ref_surf_elem = ne*(order_ref+1)*(order_ref+1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a critique, just trying to understand for myself...
Since order_ref>=1
, this auto refinement function will always try to refine the base number of elements ne
by at least 4x
. Is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dammit, you are right, it is shifted by one as the loop below has <=
😬 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the catch 😅 , it is fixed in 73a0174 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formula was right, the name of the variable is wrong -- ultimately we determine ref
so that the number of vertices in the refined representation is less than or equal to a given max value. So I think you should revert the formula and change the name of the variable to auto_ref_vert_max
or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The formula was wrong, but rather from different perspective than @justinlaughlin mentioned. The point of the this and following lines is to set ref=order_ref
when possible, but when the loop below gets to the point ref==order_ref
then ne*(ref+1)*(ref+1) <= auto_ref_surf_elem
with auto_ref_surf_elem = ne*(order_ref+1)*(order_ref+1)
, so ref
is increased and the algorithm ends with ref==order_ref+1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I understand now. I think your fix makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I rewrote the algorithm to vertices and rebalanced the test cases, so how about now @v-dobrev ?
CHANGELOG
Outdated
- Changed the auto refinement algorithm, which is now based on the order of | ||
the grid function and mesh. The total number of elements is limited to the | ||
range between 100k and 2M. If the upper limit is reached a warning is shown | ||
and you may still need to press 'o' if you want to increase the refinement | ||
even further. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're actually limiting the total number of vertices used for plotting the subdivided mesh. This is motivated by the observation that the vertices determine the size of the floating point data pushed to OpenGL, the other data is just connectivity information.
Also, it should be made clear that the choice of the limit number between 100k and 2M is chosen based on the order of the solution and the mesh and on the number of drawn elements.
Also, this limit is additionally restricted so that no more than 16 refinements are actually performed for auto-refinement.
Also, the order-based limit will be exceeded when the number of the drawn elements is big enough, i.e. when using refinement factor of 1 needs more vertices than the order-based limit.
Also, it is good to clarify that the "number of drawn elements" refers to the number of mesh elements for 2D meshes (including 2D meshes embedded in 3D) and to the number of boundary elements for 3D meshes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I improved the changelog a bit, so it should be more clear now 😉 I use the number of elements consistently with the comment above ☝️ . I am not sure if it addresses all points, but have a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tzanio , could you please reformulate it further? Also with the "vertices terminology" now 🙂 .
Here are some observations about the speed of the current auto-refinement choice (on Macbook pro with M2 Max chip):
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After #285 (comment), I realize the intended algorithm was different from my understanding (
Overall I think this PR is a big improvement 👍 .
in CHANGELOG and the beginning of README.md.
@@ -298,3 +304,46 @@ Key commands | |||
- `x`-component: `v_x` | |||
- `y`-component: `v_y` | |||
- `z`-component: `v_z` | |||
|
|||
## Auto-refinement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice and clear section, thanks for adding it @v-dobrev !
Checklist based on #285 (comment)
|
This is meant to address the issue where small meshes are curved but refined versions of them appear to lose the curvature in GLVis. See also this task in #284
The reasons for that behavior are the auto-refinement limits, which previously were:
auto_ref_max = 16
)auto_ref_max_surf_elem = 20000
)Both of these variables are set in
lib/vsdata.cpp
After considering several options, I suggest that we just increase those variables to 32 and 5M respectively.
These limits seems to work fine on modern hardware and the waiting time for large meshes is a few seconds on my Mac.
@psocratis and @dylan-copeland -- do you mind trying this on your use cases and reporting if it works well?