diff --git a/examples/moving-geometry.py b/examples/moving-geometry.py index 7337d1d617b34fb5577a35774bc7685ec49a86fe..f60e65ed781232fe87fd46845281738ed3698c95 100644 --- a/examples/moving-geometry.py +++ b/examples/moving-geometry.py @@ -78,6 +78,8 @@ def reconstruct_discr_from_nodes(actx, discr, x): discr_nodes, tagged=(FirstAxisIsElementsTag(),)) + from dataclasses import replace + megs = [] for igrp, grp in enumerate(discr.groups): nodes = np.stack([ @@ -85,10 +87,7 @@ def reconstruct_discr_from_nodes(actx, discr, x): for iaxis in range(discr.ambient_dim) ]) - meg = grp.mesh_el_group.copy( - vertex_indices=None, - nodes=nodes, - ) + meg = replace(grp.mesh_el_group, vertex_indices=None, nodes=nodes) megs.append(meg) mesh = discr.mesh.copy(groups=megs, vertices=None) diff --git a/meshmode/mesh/__init__.py b/meshmode/mesh/__init__.py index 3f9fb914706bf038a5d2833db8ae628f8cb03e89..24eb2d7b432d1458cee861255c22749d587b3c3e 100644 --- a/meshmode/mesh/__init__.py +++ b/meshmode/mesh/__init__.py @@ -21,8 +21,9 @@ THE SOFTWARE. """ from abc import ABC, abstractmethod -from dataclasses import dataclass, replace, field -from typing import Any, ClassVar, Hashable, Optional, Tuple, Type, Sequence +from dataclasses import dataclass, field, replace +from typing import Any, ClassVar, Hashable, Optional, Sequence, Tuple, Type +from warnings import warn import numpy as np import numpy.linalg as la @@ -32,6 +33,7 @@ from pytools import Record, memoize_method from meshmode.mesh.tools import AffineMap + __doc__ = """ .. autoclass:: MeshElementGroup @@ -117,23 +119,8 @@ class BTAG_PARTITION(BTAG_NO_BOUNDARY): # noqa: N801 .. versionadded:: 2017.1 """ - def __init__(self, part_id: PartID, part_nr=None): - if part_nr is not None: - from warnings import warn - warn("part_nr is deprecated and will stop working in March 2023. " - "Use part_id instead.", - DeprecationWarning, stacklevel=2) - self.part_id = int(part_nr) - else: - self.part_id = part_id - - @property - def part_nr(self): - from warnings import warn - warn("part_nr is deprecated and will stop working in March 2023. " - "Use part_id instead.", - DeprecationWarning, stacklevel=2) - return self.part_id + def __init__(self, part_id: PartID) -> None: + self.part_id = part_id def __hash__(self): return hash((type(self), self.part_id)) @@ -216,7 +203,8 @@ class MeshElementGroup(ABC): .. attribute:: vertex_indices An array of shape ``(nelements, nvertices)`` of (mesh-wide) - vertex indices. + vertex indices. This can also be *None* to support the case where the + associated mesh does not have any :attr:`~Mesh.vertices`. .. attribute:: nodes @@ -251,43 +239,9 @@ class MeshElementGroup(ABC): """ order: int - - # NOTE: the mesh supports not having vertices if no facial or nodal - # adjacency is required, so we can mark this as optional vertex_indices: Optional[np.ndarray] nodes: np.ndarray - - # TODO: Remove ` = None` when everything is constructed through the factory - unit_nodes: np.ndarray = None - - # FIXME: these should be removed! - # https://github.com/inducer/meshmode/issues/224 - element_nr_base: Optional[int] = None - node_nr_base: Optional[int] = None - - # TODO: remove when everything has been constructed through the factory - _factory_constructed: bool = False - - def __post_init__(self): - if not self._factory_constructed: - from warnings import warn - warn(f"Calling the constructor of '{type(self).__name__}' is " - "deprecated and will stop working in July 2022. " - f"Use '{type(self).__name__}.make_group' instead", - DeprecationWarning, stacklevel=2) - - def __getattribute__(self, name): - if name in ("element_nr_base", "node_nr_base"): - new_name = ("base_element_nrs" - if name == "element_nr_base" else - "base_node_nrs") - - from warnings import warn - warn(f"'{type(self).__name__}.{name}' is deprecated and will be " - f"removed in July 2022. Use 'Mesh.{new_name}' instead", - DeprecationWarning, stacklevel=2) - - return super().__getattribute__(name) + unit_nodes: np.ndarray @property def dim(self): @@ -313,21 +267,6 @@ class MeshElementGroup(ABC): def nnodes(self): return self.nelements * self.unit_nodes.shape[-1] - def copy(self, **kwargs: Any) -> "MeshElementGroup": - from warnings import warn - warn(f"{type(self).__name__}.copy is deprecated and will be removed in " - f"July 2022. {type(self).__name__} is now a dataclass, so " - "standard functions such as dataclasses.replace should be used " - "instead.", - DeprecationWarning, stacklevel=2) - - if "element_nr_base" not in kwargs: - kwargs["element_nr_base"] = None - if "node_nr_base" not in kwargs: - kwargs["node_nr_base"] = None - - return replace(self, **kwargs) - def __eq__(self, other): return ( type(self) is type(other) @@ -384,62 +323,10 @@ class _ModepyElementGroup(MeshElementGroup): .. attribute:: _modepy_space """ - # TODO: remove once `make_group` is used everywhere - dim: Optional[int] = None - _modepy_shape_cls: ClassVar[Type[mp.Shape]] = mp.Shape _modepy_shape: mp.Shape = field(default=None, repr=False) _modepy_space: mp.FunctionSpace = field(default=None, repr=False) - def __post_init__(self): - super().__post_init__() - if self._factory_constructed: - return - - # {{{ duplicates make_group below, keep in sync - - if self.unit_nodes is None: - if self.dim is None: - raise TypeError("either 'dim' or 'unit_nodes' must be provided") - else: - if self.dim is None: - object.__setattr__(self, "dim", self.unit_nodes.shape[0]) - - if self.unit_nodes.shape[0] != self.dim: - raise ValueError("'dim' does not match 'unit_nodes' dimension") - - # dim is now usable - assert self._modepy_shape_cls is not mp.Shape - object.__setattr__(self, "_modepy_shape", - # pylint: disable=abstract-class-instantiated - self._modepy_shape_cls(self.dim)) - object.__setattr__(self, "_modepy_space", - mp.space_for_shape(self._modepy_shape, self.order)) - - if self.unit_nodes is None: - unit_nodes = mp.edge_clustered_nodes_for_space( - self._modepy_space, self._modepy_shape) - object.__setattr__(self, "unit_nodes", unit_nodes) - - if self.nodes is not None: - if self.unit_nodes.shape[-1] != self.nodes.shape[-1]: - raise ValueError( - "'nodes' has wrong number of unit nodes per element." - f" expected {self.unit_nodes.shape[-1]}, " - f" but got {self.nodes.shape[-1]}.") - - if self.vertex_indices is not None: - if not issubclass(self.vertex_indices.dtype.type, np.integer): - raise TypeError("'vertex_indices' must be integral") - - if self.vertex_indices.shape[-1] != self.nvertices: - raise ValueError( - "'vertex_indices' has wrong number of vertices per element." - f" expected {self.nvertices}," - f" got {self.vertex_indices.shape[-1]}") - - # }}} - @property def nvertices(self): return self._modepy_shape.nvertices # pylint: disable=no-member @@ -486,11 +373,12 @@ class _ModepyElementGroup(MeshElementGroup): raise ValueError("'unit_nodes' size does not match the dimension " f"of a '{type(space).__name__}' space of order {order}") - return cls(order=order, vertex_indices=vertex_indices, nodes=nodes, - dim=dim, + return cls(order=order, + vertex_indices=vertex_indices, + nodes=nodes, unit_nodes=unit_nodes, - _modepy_shape=shape, _modepy_space=space, - _factory_constructed=True) + _modepy_shape=shape, + _modepy_space=space) # }}} @@ -500,6 +388,7 @@ class _ModepyElementGroup(MeshElementGroup): @dataclass(frozen=True, eq=False) class SimplexElementGroup(_ModepyElementGroup): r"""Inherits from :class:`MeshElementGroup`.""" + _modepy_shape_cls: ClassVar[Type[mp.Shape]] = mp.Simplex @property @@ -511,6 +400,7 @@ class SimplexElementGroup(_ModepyElementGroup): @dataclass(frozen=True, eq=False) class TensorProductElementGroup(_ModepyElementGroup): r"""Inherits from :class:`MeshElementGroup`.""" + _modepy_shape_cls: ClassVar[Type[mp.Shape]] = mp.Hypercube def is_affine(self): @@ -549,16 +439,6 @@ class NodalAdjacency: neighbors_starts: np.ndarray neighbors: np.ndarray - def copy(self, **kwargs: Any) -> "NodalAdjacency": - from warnings import warn - warn(f"{type(self).__name__}.copy is deprecated and will be removed in " - f"July 2022. {type(self).__name__} is now a dataclass, so " - "standard functions such as dataclasses.replace should be used " - "instead.", - DeprecationWarning, stacklevel=2) - - return replace(self, **kwargs) - def __eq__(self, other): return ( type(self) is type(other) @@ -605,16 +485,6 @@ class FacialAdjacencyGroup: igroup: int - def copy(self, **kwargs: Any) -> "FacialAdjacencyGroup": - from warnings import warn - warn(f"{type(self).__name__}.copy is deprecated and will be removed in " - f"July 2022. {type(self).__name__} is now a dataclass, so " - "standard functions such as dataclasses.replace should be used " - "instead.", - DeprecationWarning, stacklevel=2) - - return replace(self, **kwargs) - def __eq__(self, other): return ( type(self) is type(other) @@ -1000,16 +870,6 @@ class Mesh(Record): :attr:`facial_adjacency_groups` may be passed. """ - el_nr = 0 - node_nr = 0 - - new_groups = [] - for g in groups: - ng = replace(g, element_nr_base=el_nr, node_nr_base=node_nr) - new_groups.append(ng) - el_nr += ng.nelements - node_nr += ng.nnodes - if vertices is None: is_conforming = None @@ -1038,7 +898,7 @@ class Mesh(Record): self.face_id_dtype) Record.__init__( - self, vertices=vertices, groups=new_groups, + self, vertices=vertices, groups=groups, _nodal_adjacency=nodal_adjacency, _facial_adjacency_groups=facial_adjacency_groups, vertex_id_dtype=np.dtype(vertex_id_dtype), @@ -1076,8 +936,8 @@ class Mesh(Record): assert fagrp.neighbor_faces.dtype == self.face_id_dtype assert fagrp.neighbor_faces.shape == (nfagrp_elements,) - from meshmode.mesh.processing import \ - test_volume_mesh_element_orientations + from meshmode.mesh.processing import ( + test_volume_mesh_element_orientations) if self.dim == self.ambient_dim and not skip_element_orientation_test: # only for volume meshes, for now @@ -1093,9 +953,8 @@ class Mesh(Record): set_if_not_present("vertices") if "groups" not in kwargs: - kwargs["groups"] = [ - replace(group, element_nr_base=None, node_nr_base=None) - for group in self.groups] + kwargs["groups"] = self.groups + set_if_not_present("nodal_adjacency", "_nodal_adjacency") set_if_not_present("facial_adjacency_groups", "_facial_adjacency_groups") set_if_not_present("vertex_id_dtype") @@ -1269,9 +1128,9 @@ def _test_node_vertex_consistency(mesh, tol): if isinstance(mgrp, _ModepyElementGroup): assert _test_node_vertex_consistency_resampling(mesh, igrp, tol) else: - from warnings import warn - warn("not implemented: node-vertex consistency check for '%s'" - % type(mgrp).__name__) + warn("Not implemented: node-vertex consistency check for " + f"groups of type '{type(mgrp).__name__}'.", + stacklevel=3) return True @@ -1657,7 +1516,7 @@ def as_python(mesh, function_name="make_mesh"): recreate the mesh given as an input parameter. """ - from pytools.py_codegen import PythonCodeGenerator, Indentation + from pytools.py_codegen import Indentation, PythonCodeGenerator cg = PythonCodeGenerator() cg(""" # generated by meshmode.mesh.as_python diff --git a/meshmode/mesh/generation.py b/meshmode/mesh/generation.py index 16f83c120fcfc059e77d38e3d65779628d6723fa..cc1a331d2b2dd0c3bab4bb4ba6e91f3f32d37dec 100644 --- a/meshmode/mesh/generation.py +++ b/meshmode/mesh/generation.py @@ -672,9 +672,10 @@ def generate_sphere(r: float, order: int, *, from dataclasses import replace vertices = mesh.vertices * r / np.sqrt(np.sum(mesh.vertices**2, axis=0)) grp, = mesh.groups - grp = replace(grp, - nodes=grp.nodes * r / np.sqrt(np.sum(grp.nodes**2, axis=0)), - element_nr_base=None, node_nr_base=None) + grp = replace( + grp, + nodes=grp.nodes * r / np.sqrt(np.sum(grp.nodes**2, axis=0)) + ) from meshmode.mesh import Mesh return Mesh( @@ -748,8 +749,7 @@ def generate_surface_of_revolution( from dataclasses import replace vertices = ensure_radius(mesh.vertices) grp, = mesh.groups - grp = replace(grp, nodes=ensure_radius(grp.nodes), - element_nr_base=None, node_nr_base=None) + grp = replace(grp, nodes=ensure_radius(grp.nodes)) from meshmode.mesh import Mesh return Mesh( @@ -854,8 +854,7 @@ def generate_torus_and_cycle_vertices( # }}} from dataclasses import replace - grp = replace(grp, vertex_indices=vertex_indices, nodes=nodes, - element_nr_base=None, node_nr_base=None) + grp = replace(grp, vertex_indices=vertex_indices, nodes=nodes) from meshmode.mesh import Mesh return ( @@ -1002,8 +1001,7 @@ def refine_mesh_and_get_urchin_warper( def warp_mesh(mesh: Mesh) -> Mesh: from dataclasses import replace groups = [ - replace(grp, nodes=map_coords(grp.nodes), - element_nr_base=None, node_nr_base=None) + replace(grp, nodes=map_coords(grp.nodes)) for grp in mesh.groups] from meshmode.mesh import Mesh diff --git a/meshmode/mesh/processing.py b/meshmode/mesh/processing.py index a5013647dd93921c252c9f81b954b7a5fcdefea7..e1d387a201d991089740d5661d0e0abe8e21c74b 100644 --- a/meshmode/mesh/processing.py +++ b/meshmode/mesh/processing.py @@ -22,12 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from dataclasses import dataclass, replace from functools import reduce from typing import ( - Callable, Dict, Optional, Union, Tuple, Mapping, List, Set, Sequence, - ) - -from dataclasses import dataclass + Callable, Dict, List, Mapping, Optional, Sequence, Set, Tuple, Union) import numpy as np import numpy.linalg as la @@ -35,17 +33,9 @@ import numpy.linalg as la import modepy as mp from meshmode.mesh import ( - MeshElementGroup, - Mesh, - BTAG_PARTITION, - PartID, - FacialAdjacencyGroup, - InteriorAdjacencyGroup, - BoundaryAdjacencyGroup, - InterPartAdjacencyGroup -) - -from meshmode.mesh import _FaceIDs + BTAG_PARTITION, BoundaryAdjacencyGroup, FacialAdjacencyGroup, + InteriorAdjacencyGroup, InterPartAdjacencyGroup, Mesh, MeshElementGroup, PartID, + _FaceIDs) from meshmode.mesh.tools import AffineMap @@ -179,12 +169,10 @@ def _filter_mesh_groups( # }}} - from dataclasses import replace new_groups = [ replace(grp, vertex_indices=new_vertex_indices[igrp], - nodes=grp.nodes[:, filtered_group_elements[igrp], :].copy(), - element_nr_base=None, node_nr_base=None) + nodes=grp.nodes[:, filtered_group_elements[igrp], :].copy()) for igrp, grp in enumerate(mesh.groups)] return new_groups, required_vertex_indices @@ -742,7 +730,6 @@ def flip_simplex_element_group( "ij,dej->dei", flip_matrix, grp.nodes[:, grp_flip_flags]) - from dataclasses import replace return replace(grp, vertex_indices=new_vertex_indices, nodes=new_nodes) @@ -764,11 +751,9 @@ def perform_flips( grp_flip_flags = flip_flags[base_element_nr:base_element_nr + grp.nelements] if grp_flip_flags.any(): - new_grp = flip_simplex_element_group( - mesh.vertices, grp, grp_flip_flags) + new_grp = flip_simplex_element_group(mesh.vertices, grp, grp_flip_flags) else: - from dataclasses import replace - new_grp = replace(grp, element_nr_base=None, node_nr_base=None) + new_grp = replace(grp) new_groups.append(new_grp) @@ -842,7 +827,6 @@ def merge_disjoint_meshes( if any(mesh._facial_adjacency_groups is not None for mesh in meshes): facial_adjacency_groups = False - from dataclasses import replace if single_group: from pytools import single_valued ref_group = single_valued( @@ -876,8 +860,7 @@ def merge_disjoint_meshes( for group in mesh.groups: assert group.vertex_indices is not None new_vertex_indices = group.vertex_indices + vert_base - new_group = replace(group, vertex_indices=new_vertex_indices, - element_nr_base=None, node_nr_base=None) + new_group = replace(group, vertex_indices=new_vertex_indices) new_groups.append(new_group) @@ -926,7 +909,6 @@ def split_mesh_groups( new_groups: List[MeshElementGroup] = [] subgroup_to_group_map = {} - from dataclasses import replace for igrp, (base_element_nr, grp) in enumerate( zip(mesh.base_element_nrs, mesh.groups) ): @@ -942,7 +924,6 @@ def split_mesh_groups( new_groups.append(replace(grp, vertex_indices=grp.vertex_indices[mask, :].copy(), nodes=grp.nodes[:, mask, :].copy(), - element_nr_base=None, node_nr_base=None, )) mesh = Mesh( @@ -1332,17 +1313,15 @@ def map_mesh(mesh: Mesh, f: Callable[[np.ndarray], np.ndarray]) -> Mesh: # {{{ assemble new groups list - from dataclasses import replace new_groups = [] - for group in mesh.groups: mapped_nodes = f(group.nodes.reshape(mesh.ambient_dim, -1)) if not mapped_nodes.flags.c_contiguous: mapped_nodes = np.copy(mapped_nodes, order="C") - new_groups.append(replace(group, - nodes=mapped_nodes.reshape(*group.nodes.shape), - element_nr_base=None, node_nr_base=None)) + new_groups.append( + replace(group, nodes=mapped_nodes.reshape(*group.nodes.shape)) + ) # }}} @@ -1384,17 +1363,15 @@ def affine_map( # {{{ assemble new groups list - from dataclasses import replace new_groups = [] - for group in mesh.groups: mapped_nodes = f(group.nodes.reshape(mesh.ambient_dim, -1)) if not mapped_nodes.flags.c_contiguous: mapped_nodes = np.copy(mapped_nodes, order="C") - new_groups.append(replace(group, - nodes=mapped_nodes.reshape(*group.nodes.shape), - element_nr_base=None, node_nr_base=None)) + new_groups.append( + replace(group, nodes=mapped_nodes.reshape(*group.nodes.shape)) + ) # }}} @@ -1424,7 +1401,6 @@ def affine_map( return AffineMap(matrix, offset) - from dataclasses import replace facial_adjacency_groups = [] for old_fagrp_list in mesh.facial_adjacency_groups: fagrp_list = [] diff --git a/test/test_mesh.py b/test/test_mesh.py index 6d5014ecf9cd92f73c4cde457ea8b786c45694cc..c3dadef4f4f5bff90630c7faf6a09669ea326954 100644 --- a/test/test_mesh.py +++ b/test/test_mesh.py @@ -1104,38 +1104,6 @@ def test_tensor_torus(actx_factory, order, visualize=False): # }}} -# {{{ test_mesh_element_group_constructor - -@pytest.mark.parametrize(("shape_cls", "group_cls"), [ - (mp.Simplex, SimplexElementGroup), - (mp.Hypercube, TensorProductElementGroup)]) -def test_mesh_element_group_constructor(shape_cls, group_cls): - order = 7 - dim = 2 - - shape = shape_cls(dim) - space = mp.space_for_shape(shape, order) - - unit_nodes = mp.edge_clustered_nodes_for_space(space, shape) - nodes = np.stack([unit_nodes] * shape.dim) - - # from unit_nodes - meg_from_constructor = group_cls( - order, vertex_indices=None, nodes=nodes, unit_nodes=unit_nodes) - meg_from_factory = group_cls.make_group( - order, vertex_indices=None, nodes=nodes, unit_nodes=unit_nodes) - assert meg_from_constructor == meg_from_factory - - # from dim (with default unit nodes) - meg_from_constructor = group_cls( - order, vertex_indices=None, nodes=nodes, dim=dim) - meg_from_factory = group_cls.make_group( - order, vertex_indices=None, nodes=nodes, dim=dim) - assert meg_from_constructor == meg_from_factory - -# }}} - - # {{{ test_node_vertex_consistency_check def test_node_vertex_consistency_check(actx_factory): diff --git a/test/test_meshmode.py b/test/test_meshmode.py index b9b93b25da1cdac527d3b8997869726687ff4323..c2981866ad958b92c3567eed8dc19ae480221a81 100644 --- a/test/test_meshmode.py +++ b/test/test_meshmode.py @@ -876,8 +876,7 @@ def test_mesh_without_vertices(actx_factory): from dataclasses import replace grp, = mesh.groups groups = [ - replace(grp, nodes=grp.nodes, vertex_indices=None, - element_nr_base=None, node_nr_base=None) + replace(grp, nodes=grp.nodes, vertex_indices=None) for grp in mesh.groups] mesh = Mesh(None, groups, is_conforming=False)