Skip to main content

A Python package to build, manipulate and analyze polygonal meshes.

Project description

PolyMesh - A Python Library for Compound Meshes with Jagged Topologies

Binder CircleCI Documentation Status License PyPI Python 3.7‒3.10 Code style: black

Warning PolyMesh is in the early stages of it's lifetime, and some concepts may change in the future. If you want long-term stability, wait until version 1.0, which is planned to be released if the core concepts all seem to sit and the documentation covers all major concepts.

The PolyMesh library aims to provide the tools to build and analyse meshes with complex topologies. Meshes can be built like a dictionary, using arbitarily nested layouts and then be translated to VTK or PyVista. For plotting, there is also support for K3D, Matplotlib and Plotly.

The data model is built around Awkward, which makes it possible to attach nested, variable-sized data to the points or the cells in a mesh, also providing interfaces to other popular libraries like Pandas or PyArrow. Implementations are fast as we rely on the vector math capabilities of NumPy, while other computationally sensitive calculations are JIT-compiled using Numba where necessary.

Here and there we also use NetworkX, SciPy, SymPy and scikit-learn.

Motivating example

from polymesh import PolyData, PointData, LineData
from polymesh.space import CartesianFrame
from polymesh.grid import Grid
from polymesh.cells import H8, TET4, L2
from polymesh.utils.topology import H8_to_TET4, H8_to_L2
from polymesh.utils.space import frames_of_lines
import numpy as np

size = 10, 10, 5
shape = 10, 10, 5
grid = Grid(size=size, shape=shape, eshape='H8')
grid.centralize()

coords = grid.coords()  # coordinates
topo = grid.topology()  # topology
centers = grid.centers()

b_left = centers[:, 0] < 0
b_right = centers[:, 0] >= 0
b_front = centers[:, 1] >= 0
b_back = centers[:, 1] < 0
iTET4 = np.where(b_left)[0]
iH8 = np.where(b_right & b_back)[0]
iL2 = np.where(b_right & b_front)[0]
_, tTET4 = H8_to_TET4(coords, topo[iTET4])
_, tL2 = H8_to_L2(coords, topo[iL2])
tH8 = topo[iH8]

# crate supporting pointcloud
frame = CartesianFrame(dim=3)
pd = PointData(coords=coords, frame=frame)
mesh = PolyData(pd, frame=frame)

# add tetrahedra
cdTET4 = TET4(topo=tTET4, frames=frame)
mesh['tetra'] = PolyData(cdTET4, frame=frame)

# add hexahedra
cdH8 = H8(topo=tH8, frames=frame)
mesh['hex'] = PolyData(cdH8, frame=frame)

# add lines
cdL2 = L2(topo=tL2, frames=frames_of_lines(coords, tL2))
mesh['line'] = LineData(cdL2, frame=frame)

# finalize the mesh and lock the layout
mesh.to_standard_form()
mesh.lock(create_mappers=True)

PolyMesh can also be used as a configuration tool for external plotting libraries:

# configure tetratedra
mesh['tetra'].config['A', 'color'] = 'green'

# configure hexahedra
mesh['hex'].config['A', 'color'] = 'blue'

# configure lines
mesh['line'].config['A', 'color'] = 'red'
mesh['line'].config['A', 'line_width'] = 3
mesh['line'].config['A', 'render_lines_as_tubes'] = True

# plot with PyVista
mesh.plot(notebook=True, jupyter_backend='static', config_key=('A'),
          show_edges=True, window_size=(600, 480))

Attaching data to the cells of the created blocks and plotting the results using PyVista:

scalars_TET4 = 100*np.random.rand(len(cdTET4))
cdTET4.db['scalars'] = scalars_TET4

scalars_H8 = 100*np.random.rand(len(cdH8))
cdH8.db['scalars'] = scalars_H8

scalars_L2 = 100*np.random.rand(len(cdL2))
cdL2.db['scalars'] = scalars_L2
mesh['line'].config['B', 'render_lines_as_tubes'] = True
mesh['line'].config['B', 'line_width'] = 3

mesh.plot(notebook=True, jupyter_backend='static', config_key=('B'), 
          cmap='plasma', show_edges=True, window_size=(600, 480), 
          scalars='scalars')

PolyMesh makes it easy to transfer data from the cells to the supporting point cloud:

# this 'pulls' data from the cells
scalars = mesh.pointdata.pull('scalars') 

Then we can plot the smoothed data:

mesh.plot(notebook=True, jupyter_backend='static', config_key=('A'),
          show_edges=True, window_size=(600, 480), scalars=scalars, 
          cmap='plasma')

Customizing the distribution mechanism

The smoothing procedure can be fine tuned using arbitrary weighting of cellular data. Define some scalar data on the cells and plot it using PyVista:

cdTET4.db['scalars'] = np.full(len(cdTET4), -100)
cdH8.db['scalars'] = np.full(len(cdH8), 100)
cdL2.db['scalars'] = np.full(len(cdL2), 0)

mesh.plot(notebook=True, jupyter_backend='static', config_key=('B'), 
          cmap='jet', show_edges=True, window_size=(600, 480), 
          scalars='scalars')

The default smoothing mechanism uses the volumes of the cells to determine nodal distribution factors.

scalars = mesh.pd.pull('scalars')

mesh.plot(notebook=True, jupyter_backend='static', config_key=('A'),
          show_edges=True, window_size=(600, 480), scalars=scalars, 
          cmap='jet')

If you want you can give more presence to the hexahedral cells by increasing their weights:

v = mesh.volumes()
idH8 = mesh['hex'].cd.id  # cell indices of hexahedra
v[idH8] *= 5  # 500% of original weight
ndf = mesh.nodal_distribution_factors(weights=v)
scalars = mesh.pd.pull('scalars', ndf=ndf)

mesh.plot(notebook=True, jupyter_backend='static', config_key=('A'),
          show_edges=True, window_size=(600, 480), scalars=scalars, 
          cmap='jet')

or by decreasing them:

v = mesh.volumes()
idH8 = mesh['hex'].cd.id  # cell indices of hexahedra
v[idH8] /= 5  # 20% of original weight
ndf = mesh.nodal_distribution_factors(weights=v)
scalars = mesh.pd.pull('scalars', ndf=ndf)

mesh.plot(notebook=True, jupyter_backend='static', config_key=('A'),
          show_edges=True, window_size=(600, 480), scalars=scalars, 
          cmap='jet')

It can be observed how the colors change arounf the boundary of hexahedral cells.

Point-related data alsobe plotted using the K3D library:

from k3d.colormaps import matplotlib_color_maps

cmap=matplotlib_color_maps.Jet
mesh.k3dplot(scalars=scalars, menu_visibility=False, cmap=cmap)

Documentation

The documentation is hosted on ReadTheDocs, where you can find more examples.

Installation

PolyMesh can be installed from PyPI using pip on Python >= 3.7:

>>> pip install polymesh

License

This package is licensed under the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polymesh-0.0.14.tar.gz (103.1 kB view hashes)

Uploaded Source

Built Distribution

polymesh-0.0.14-py3-none-any.whl (128.6 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page