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

[FEATURE] Trimesh #14139

Open
gmerritt123 opened this issue Nov 4, 2024 · 4 comments
Open

[FEATURE] Trimesh #14139

gmerritt123 opened this issue Nov 4, 2024 · 4 comments

Comments

@gmerritt123
Copy link

Problem description

A huge amount of engineering is completed via the finite element method. A finite element "mesh" is a topologically-aware construct consisting of elements and nodes. Most commonly, triangular elements are used for numerical solver reasons.

Nodes have specified coordinates, and elements are described as the area (or volume) bound by a collection of node indices. e.g.

nds = {'x':[0,2,2,3] ,'y':[0,2,0,0]}
els = [(0,1,2),(1,2,3)]  #first element is area bound by node indices 0,1,2 and second element = area bound by node indices 1,2,3

The obvious advantage to storing the data this way is that node coordinates do not need to be stored multiple times, and topologic integrity.

I'm currently translating the above kind of data structure into one bokeh can use for a Patches glyph, e.g. the above becomes:

data = {'xs':[[0,2,2],[2,2,3]], 'ys':[[0,2,0],[2,0,0]]}

And often I run into size limitations (nodes/elements can be into 100s of thousands or even millions for my models):

image

Feature description

Specifically, I would like a means of rendering triangular 2D meshes: a sort of framework that would hold a CDS of node coordinates/nodal properties, and another "special" CDS/data model that holds a regular 2D array (e.g. shape = (nElements, 3)) describing the node indices comprising each element. The renderers would be comprise 2 glyphs: a Scatter for the nodes, and either Patches or some sort of special/custom Triangle glyph for the elements.

Potential alternatives

Thinking about this, it seems like a lot of the networkx Graph type work is related to this in that the data structure is very similar. However the key difference is that I want to render the area bound by connected nodes and not the connections themselves. So starting from the GraphLayoutProvider side of things might be an easier start/implementation.

Additional information

Worth mentioning that I'm not sure if generalizing out the TriMesh to a "RegularNMesh" would be viable, where the shape of the element array would determine the element glyph type --> e.g. shape (nElements, 3) = Triangles, shape (nElements, 4) = Quadrilaterals, etc. but just further food for thought.

@jbednar
Copy link
Contributor

jbednar commented Nov 4, 2024

HoloViews already provides Bokeh-based trimesh rendering, including optional server-side rendering of very large trimeshes using Datashader. If you want to try it, I'd recommend starting with the UXarray library , which is already set up to use HoloViews and Datashader for visualization.

@ianthomas23
Copy link
Member

I agree with the HoloViews/UXarray recommendation.

As for Bokeh itself, I am not sure that we'd want to support any sort of Mesh glyph specifically. What I would consider is a generalisation of the requirements here to support indexed coordinates in another CDS. So whereas at the moment you might use

source = ColumnDataSource(dict(xs=..., ys=...)
glyph = Patches(xs="xs", ys="ys", source=source)

you might instead have

source = ColumnDataSource(dict(indexes=...)
indexed_source = ColumnDataSource(dict(xs=..., ys=...))
glyph = Patches(indexes="indexes", source=source, indexed_source=indexed_source)

with API and names TBD. This approach could be used by any glyph, and the shape of the indexes would depend on what the glyph expects. This would support tri and quad meshes, and because the indexes in source can be a list-of-lists this also supports meshes containing a mixture of triangles, quads, etc. If the primitives all had the same number of sides the indexes could be a 2D numpy array of integers.

The benefit of this approach is that we'd have a generic solution that can be used in all glyphs. The indexed_source (the actual x-y coordinates) could be reused by multiple glyphs and only has to be sent to the frontend once, and when panning/zooming the transformed coordinates are only transformed once, which will be faster than now (although may not be measurably faster unless using a very large number of points).

The rendering would be identical to the Patches workaround currently used, so if you are using edge lines they will be rendered twice where two patches meet along a common edge. But we avoid having to understand what a mesh is in terms of topological integrity, etc.

A possible downside is that the two options of x-y coordinates and indexed coordinates may be difficult/impossible to express as Bokeh properties.

@gmerritt123
Copy link
Author

Thanks for the responses!

@jbednar , I made a quick holoviews-environment and took a look --> ran through the Trimesh example and saved an html. I inspected the the ColumnDataSource models in the document and it looks like Holoviews translates the data to two CDS's driving Scatter and Patches "primitive" glyphs for the nodes/elements respectively --> i.e. exactly what I'm already doing. I guess I should qualify my need for this request --> I'm (currently) fully serverless, using CustomJS and external resources to do all the data manipulation client side. I fully understand there are data-size limitations to this approach, but the convenience of being able to make these standalone interactive html tools and either directly embed them into a website or pass them to clients without any need to maintain a server etc. has always been a good fit for small-scale consultant projects. I've only run into sizing limitations now, when it's been requested that I add the ability to render finite element models on my cross section tool... and the major inefficiency I see is what I've outlined above.... and from what I can tell (and please correct me if I'm wrong), building a Holoviews Trimesh won't fix that because once I save it to html, it'll just do what I'm already doing.

@ianthomas23 Yes, that is a super interesting proposed way of supporting indexed coordinates, basically what I was getting at in my last paragraph but IMO is much better/more clear. User would be free to supply a ragged 2D array to source in your example so you could mix triangles/quads/hexes etc. etc. (which is what you're saying), and it'd also be extendable to MultiLine too (I think?)

You are right in that the honus of topological integrity would be shifted onto the user (i.e. user has total freedom to supply data that results in intersecting edges etc.), but I see that as added flexibility, not a drawback :-D

@bryevdv
Copy link
Member

bryevdv commented Nov 5, 2024

I think @ianthomas23 sketched proposal is probably the best approach. I would actually be very excited to see something like this go into Bokeh. But I do think that this would require very substantial up-front design planning and consideration. Specifically, things like CDSView and selection/inspection would not play nice with this as they currently stand. Basically all of the Bokeh "data model" of Bokeh accreted organically over the years. It would probably be a good idea to try and re-visit the way data is handled as a whole, in view of all the capabilities and requirements that are now known better than they were 12 years ago, but that might be getting into Bokeh 4.x territory.

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

No branches or pull requests

4 participants