Skip to content

Commit 824caee

Browse files
committed
Merge branch 'main' into pytato-array-context-transforms
2 parents 110e689 + 871eb12 commit 824caee

File tree

6 files changed

+108
-26
lines changed

6 files changed

+108
-26
lines changed

.ci/install-downstream.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ cd "$DOWNSTREAM_PROJECT"
1414
echo "*** $DOWNSTREAM_PROJECT version: $(git rev-parse --short HEAD)"
1515

1616
transfer_requirements_git_urls ../requirements.txt ./requirements.txt
17-
sed -i "/egg=meshmode/ c git+file://$(readlink -f ..)#egg=meshmode" requirements.txt
17+
edit_requirements_txt_for_downstream_in_subdir
1818

1919
export CONDA_ENVIRONMENT=.test-conda-env-py3.yml
2020

@@ -23,10 +23,9 @@ export PYTEST_ADDOPTS="-k 'not (slowtest or octave or mpi)'"
2323

2424
if [[ "$DOWNSTREAM_PROJECT" = "mirgecom" ]]; then
2525
# can't turn off MPI in mirgecom
26-
sudo apt-get update
27-
sudo apt-get install openmpi-bin libopenmpi-dev
2826
export CONDA_ENVIRONMENT=conda-env.yml
2927
export CISUPPORT_PARALLEL_PYTEST=no
28+
echo "- mpi4py" >> "$CONDA_ENVIRONMENT"
3029
else
3130
sed -i "/mpi4py/ d" requirements.txt
3231
fi

README.rst

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,29 @@ meshmode: High-Order Meshes and Discontinuous Function Spaces
1111
:alt: Python Package Index Release Page
1212
:target: https://pypi.org/project/meshmode/
1313

14+
Meshmode provides the "boring bits" of high-order unstructured discretization,
15+
for simplices (triangles, tetrahedra) and tensor products (quads, hexahedra).
16+
Features:
17+
18+
- 1/2/3D, line/surface/volume discretizations in each, curvilinear supported.
19+
- "Everything is a (separate) discretization." (mesh boundaries are, element surfaces are,
20+
refined versions of the same mesh are) "Connections" transfer information
21+
between discretizations.
22+
- Periodic connectivity.
23+
- Mesh partitioning (not just) for distributed execution (e.g. via MPI).
24+
- Interpolatory, quadrature (overintegration), and modal element-local discretizations.
25+
- Independent of execution environment (GPU/CPU, numpy, ...)
26+
via `array contexts <https://github.com/inducer/arraycontext/>`__.
27+
- Simple mesh refinement (via bisection). Adjacency currently only
28+
maintained if uniform.
29+
- Input from Gmsh, Visualization to Vtk (both high-order curvilinear).
30+
- Easy data exchange with `Firedrake <https://www.firedrakeproject.org/>`__.
31+
32+
Meshmode emerged as the shared discretization layer for `pytential
33+
<https://github.com/inducer/pytential/>`__ (layer potentials) and `grudge
34+
<https://github.com/inducer/grudge>`__ (discontinous Galerkin).
35+
36+
Places on the web related to meshmode:
37+
1438
* `Source code on Github <https://github.com/inducer/meshmode>`_
1539
* `Documentation <https://documen.tician.de/meshmode>`_
16-
17-
.. TODO

meshmode/discretization/visualization.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,10 @@ def show_scalar_in_matplotlib_3d(self, field, **kwargs):
10161016

10171017
nodes = self._vis_nodes_numpy()
10181018
field = _resample_to_numpy(self.connection, self.vis_discr, field)
1019+
if not np.can_cast(field.dtype, float, "same_kind"):
1020+
raise ValueError(
1021+
f"fields of dtype {field.dtype} are not supported: "
1022+
"cannot be converted to float")
10191023

10201024
assert nodes.shape[0] == self.vis_discr.ambient_dim
10211025

meshmode/mesh/generation.py

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -646,28 +646,61 @@ def generate_torus_and_cycle_vertices(
646646
r_major: float, r_minor: float,
647647
n_major: int = 20, n_minor: int = 10, order: int = 1,
648648
node_vertex_consistency_tolerance: Optional[Union[float, bool]] = None,
649-
unit_nodes: Optional[np.ndarray] = None):
649+
unit_nodes: Optional[np.ndarray] = None,
650+
group_cls: Optional[type] = None,
651+
):
652+
from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup
653+
if group_cls is None:
654+
group_cls = SimplexElementGroup
655+
650656
a = r_major
651657
b = r_minor
652658
u, v = np.mgrid[0:2*np.pi:2*np.pi/n_major, 0:2*np.pi:2*np.pi/n_minor]
653659

654660
# https://web.archive.org/web/20160410151837/https://www.math.hmc.edu/~gu/curves_and_surfaces/surfaces/torus.html # noqa
655-
x = np.cos(u)*(a+b*np.cos(v))
656-
y = np.sin(u)*(a+b*np.cos(v))
657-
z = b*np.sin(v)
658-
vertices = (np.vstack((x[np.newaxis], y[np.newaxis], z[np.newaxis]))
661+
x = np.cos(u) * (a + b*np.cos(v))
662+
y = np.sin(u) * (a + b*np.cos(v))
663+
z = b * np.sin(v)
664+
665+
vertices = (
666+
np.vstack((x[np.newaxis], y[np.newaxis], z[np.newaxis]))
659667
.transpose(0, 2, 1).copy().reshape(3, -1))
660668

661669
def idx(i, j):
662670
return (i % n_major) + (j % n_minor) * n_major
663-
vertex_indices = ([(idx(i, j), idx(i+1, j), idx(i, j+1))
664-
for i in range(n_major) for j in range(n_minor)]
665-
+ [(idx(i+1, j), idx(i+1, j+1), idx(i, j+1))
666-
for i in range(n_major) for j in range(n_minor)])
671+
672+
if issubclass(group_cls, SimplexElementGroup):
673+
# NOTE: this makes two triangles from a the square like
674+
# (i, j+1) (i+1, j+1)
675+
# o---------o
676+
# | \ |
677+
# | \ |
678+
# | \ |
679+
# | \ |
680+
# o---------o
681+
# (i, j) (i+1, j)
682+
683+
vertex_indices = ([
684+
(idx(i, j), idx(i+1, j), idx(i, j+1))
685+
for i in range(n_major) for j in range(n_minor)
686+
] + [
687+
(idx(i+1, j), idx(i+1, j+1), idx(i, j+1))
688+
for i in range(n_major) for j in range(n_minor)
689+
])
690+
elif issubclass(group_cls, TensorProductElementGroup):
691+
# NOTE: this should match the order of the points in modepy
692+
vertex_indices = [
693+
(idx(i, j), idx(i+1, j), idx(i, j+1), idx(i+1, j+1))
694+
for i in range(n_major) for j in range(n_minor)
695+
]
696+
else:
697+
raise TypeError(f"unsupported 'group_cls': {group_cls}")
667698

668699
vertex_indices = np.array(vertex_indices, dtype=np.int32)
669-
grp = make_group_from_vertices(vertices, vertex_indices, order,
670-
unit_nodes=unit_nodes)
700+
grp = make_group_from_vertices(
701+
vertices, vertex_indices, order,
702+
unit_nodes=unit_nodes,
703+
group_cls=group_cls)
671704

672705
# ambient_dim, nelements, nunit_nodes
673706
nodes = grp.nodes.copy()
@@ -693,12 +726,9 @@ def idx(i, j):
693726
x = np.sum(nodes*rvec, axis=0) - a
694727

695728
minor_theta = np.arctan2(nodes[2], x)
696-
697-
nodes[0] = np.cos(major_theta)*(a+b*np.cos(
698-
minor_theta))
699-
nodes[1] = np.sin(major_theta)*(a+b*np.cos(
700-
minor_theta))
701-
nodes[2] = b*np.sin(minor_theta)
729+
nodes[0] = np.cos(major_theta) * (a + b*np.cos(minor_theta))
730+
nodes[1] = np.sin(major_theta) * (a + b*np.cos(minor_theta))
731+
nodes[2] = b * np.sin(minor_theta)
702732

703733
from meshmode.mesh import Mesh
704734
return (
@@ -716,7 +746,8 @@ def generate_torus(
716746
r_major: float, r_minor: float,
717747
n_major: int = 20, n_minor: int = 10, order: int = 1,
718748
node_vertex_consistency_tolerance: Optional[Union[float, bool]] = None,
719-
unit_nodes: Optional[np.ndarray] = None):
749+
unit_nodes: Optional[np.ndarray] = None,
750+
group_cls: Optional[type] = None):
720751
r"""Generate a torus.
721752
722753
.. figure:: images/torus.png
@@ -755,10 +786,12 @@ def generate_torus(
755786
:returns: a :class:`~meshmode.mesh.Mesh` of a torus.
756787
757788
"""
789+
758790
mesh, _, _ = generate_torus_and_cycle_vertices(
759791
r_major, r_minor, n_major, n_minor, order,
760792
node_vertex_consistency_tolerance=node_vertex_consistency_tolerance,
761-
unit_nodes=unit_nodes)
793+
unit_nodes=unit_nodes,
794+
group_cls=group_cls)
762795

763796
return mesh
764797

test/test_mesh.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ def test_quad_mesh_3d(mesh_name, order=3, visualize=False):
879879
# {{{ test_cube_icosahedron
880880

881881
@pytest.mark.parametrize("order", [2, 3])
882-
def test_cube_icosphere(actx_factory, order, visualize=True):
882+
def test_cube_icosphere(actx_factory, order, visualize=False):
883883
mesh = mgen.generate_sphere(
884884
r=1.0, order=order,
885885
group_cls=TensorProductElementGroup,
@@ -898,6 +898,29 @@ def test_cube_icosphere(actx_factory, order, visualize=True):
898898
# }}}
899899

900900

901+
# {{{ test_tensor_torus
902+
903+
@pytest.mark.parametrize("order", [3, 4])
904+
def test_tensor_torus(actx_factory, order, visualize=False):
905+
mesh = mgen.generate_torus(
906+
r_major=10.0, r_minor=5,
907+
n_major=24, n_minor=12,
908+
order=order,
909+
group_cls=TensorProductElementGroup,
910+
)
911+
912+
if not visualize:
913+
return
914+
915+
from meshmode.mesh.visualization import vtk_visualize_mesh
916+
actx = actx_factory()
917+
vtk_visualize_mesh(actx, mesh,
918+
f"quad_torus_order_{order:03d}.vtu",
919+
vtk_high_order=False, overwrite=True)
920+
921+
# }}}
922+
923+
901924
# {{{ mesh boundary gluing
902925

903926
@pytest.mark.parametrize("use_tree", [False, True])

test/test_visualization.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ def test_visualizers(actx_factory, dim, group_cls):
184184

185185
if mesh.dim == 2 and is_simplex:
186186
try:
187-
vis.show_scalar_in_matplotlib_3d(f, do_show=False)
187+
# NOTE: matplotlib only supports real fields
188+
vis.show_scalar_in_matplotlib_3d(actx.np.real(f), do_show=False)
188189
except ImportError:
189190
logger.info("matplotlib not available")
190191

0 commit comments

Comments
 (0)