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

Streamplot #1670

Merged
merged 2 commits into from
May 14, 2020
Merged

Streamplot #1670

merged 2 commits into from
May 14, 2020

Conversation

danshapero
Copy link
Contributor

Resolves #1634 . This patch adds:

  • a streamline function to generate integral curves of vector fields
  • a helper class Streamplotter to accumulate all the calculated streamlines and, most importantly, to calculate whether a point is too close to these streamlines
  • a streamplot function that picks some seed points and uses the above to generate a streamline plot

The streamline generator uses an adaptive Runge-Kutta 1/2 scheme. The adaptivity needs a length scale, for which we use the mesh.cell_size member. This is really convenient but it means this is restricted to triangular and not quad meshes for now.

The streamlines are constrained to have a minimum length and stay a certain distance apart. When one streamline can pass very near itself -- for example spiraling around an attracting fixed point -- things get difficult. If you're too stringent in keeping a streamline far away from itself, you can terminate early because each proposed point is, obviously, close to the previous point. To fix this, I made the streamline integration work on chunks of some length that exceeds the spacing. Updating the grid that tracks distance to the streamlines is lagged by one chunk, which means you can have a maximum of one chunk length of self-proximity. With a reasonable chunk length, the streamlines stay far enough apart from themselves and others while also growing long enough. Hopefully this explains why there's so much code here.

@pefarrell
Copy link
Contributor

Lawrence and I discussed replacing the use of CellSize in mesh.cell_size (which only works on simplices) with CellDiameter (which works on more element types). If you make that change, can you apply this to quadrilateral elements also?

@danshapero
Copy link
Contributor Author

danshapero commented Apr 29, 2020

Yes, I just tried replacing CellSize with CellDiameter and running the streamplot test on a quad mesh and everything worked just fine. Do you think we should handle that as part of this PR or save it for later?

Copy link
Contributor

@wence- wence- left a comment

Choose a reason for hiding this comment

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

Mostly small style comments.

firedrake/plot.py Show resolved Hide resolved
firedrake/plot.py Outdated Show resolved Hide resolved
firedrake/plot.py Show resolved Hide resolved
y = x + ds * v
v1 = direction * function.at(y, tolerance=loc_tolerance)
yield y, v1, ds
break
Copy link
Contributor

Choose a reason for hiding this comment

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

It's kind of a shame that the boundary handling has to be repeated, but I see it's slightly different both times, so meh.

firedrake/plot.py Show resolved Hide resolved
firedrake/plot.py Outdated Show resolved Hide resolved
firedrake/plot.py Outdated Show resolved Hide resolved
firedrake/plot.py Outdated Show resolved Hide resolved
start_points = generator.permutation(np.array(start_points))

for x in start_points:
streamplotter.add_streamline(x)
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that you add all the points, what is the rationale for permuting them? Is it that streamlines are merged in the order they're seen?

Minor, just do:

for x in generator.permutation(...):
    streamplotter.add_streamline(x)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The algorithm tries every grid point to initialize a streamline, but many of them will get "covered" by an earlier streamline and thus not used at all. I've found that randomizing the insertion order usually starts the streamlines far apart at first, which gives you longer streamlines. The Right Thing To Do is to use farthest point seeding, but that requires spatial data structures we don't have if you want it to run in less than O(n^2). Randomized approaches happen to work pretty well and could be improved on using Poisson disk sampling.

Copy link
Contributor

Choose a reason for hiding this comment

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

So we have some r-trees lying around, but I don't know what spatial data structures you need.

firedrake/plot.py Show resolved Hide resolved
@danshapero
Copy link
Contributor Author

I think I addressed everything. Once it's good then I'll rebase from master to fix the conflict. I can do another rebase to make the history sensible or squash it all down to one commit.

The approach for seeding points right now is pretty blunt but I think it's at least tolerably good. I have some ideas for how to improve that by finding the fixed points of the vector field first, but I'll save that for later.

@wence-
Copy link
Contributor

wence- commented May 13, 2020

Can you rebase/squash and resolve the conflicts, then I think this is good.

@danshapero
Copy link
Contributor Author

All squashed and I've switched quiver to streamplot in the Stokes demo notebooks.

@wence- wence- merged commit abbc622 into firedrakeproject:master May 14, 2020
@wence-
Copy link
Contributor

wence- commented May 14, 2020

Thanks!

@danshapero danshapero deleted the streamplot branch August 11, 2021 16:01
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.

visualize vector fields with streamplot
3 participants