Files
netgen/tests/pytest/test_edgedescriptor_python.py
2026-05-04 18:47:29 +02:00

233 lines
8.3 KiB
Python

"""
EdgeDescriptor: Python bindings — EdgeDescriptor class, mesh access,
names, deprecated properties, numpy dtype, manual construction.
"""
import pytest
def test_edge_descriptor_python_class():
"""EdgeDescriptor Python class has all expected properties."""
from netgen.meshing import EdgeDescriptor
ed = EdgeDescriptor()
assert ed.edgenr == -1
assert ed.surfnr == (-1, -1)
assert ed.name == "default"
assert ed.singedge_left == 0.0
assert ed.singedge_right == 0.0
assert ed.tlosurf == -1
ed.edgenr = 5
ed.surfnr = (2, 3)
ed.name = "myedge"
ed.singedge_left = 1.5
ed.singedge_right = 2.5
ed.tlosurf = 7
assert ed.edgenr == 5
assert ed.surfnr == (2, 3)
assert ed.name == "myedge"
assert ed.singedge_left == 1.5
assert ed.singedge_right == 2.5
assert ed.tlosurf == 7
assert "myedge" in repr(ed)
def test_mesh_edge_descriptors_access():
"""Mesh.EdgeDescriptors() returns iterable, Mesh.EdgeDescriptor(i) returns ref."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
eds = mesh.EdgeDescriptors()
assert len(eds) == 12
ed0 = mesh.EdgeDescriptor(1)
assert ed0.edgenr >= 0
assert ed0.surfnr[0] >= 0 or ed0.surfnr[1] >= 0
# modifying via reference should stick
old_name = ed0.name
ed0.name = "test_rename"
assert mesh.EdgeDescriptor(1).name == "test_rename"
ed0.name = old_name # restore
def test_mesh_add_edge_descriptor():
"""Mesh.Add(EdgeDescriptor) works."""
from netgen.meshing import Mesh, EdgeDescriptor
mesh = Mesh(dim=3)
ed = EdgeDescriptor()
ed.edgenr = 42
ed.name = "added_edge"
idx = mesh.Add(ed)
assert idx == 1
assert mesh.GetNED() == 1
assert mesh.EdgeDescriptor(1).edgenr == 42
assert mesh.EdgeDescriptor(1).name == "added_edge"
def test_edge_descriptor_names_from_mesh():
"""EdgeDescriptor names match cd2names for named edges."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
box = Box(Pnt(0,0,0), Pnt(1,1,1))
for i, e in enumerate(box.edges):
e.name = f"edge_{i}"
geo = OCCGeometry(box)
mesh = geo.GenerateMesh(maxh=0.5)
for i in range(mesh.GetNED()):
ed = mesh.EdgeDescriptor(i+1)
cd2name = mesh.GetCD2Name(i)
assert ed.name == cd2name, f"ed[{i}].name={ed.name} != cd2name={cd2name}"
def test_cd2names_syncs_to_edgedescriptor():
"""SetCD2Name updates EdgeDescriptor, GetCD2Name reads from EdgeDescriptor."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
# SetCD2Name is 1-based, GetCD2Name is 0-based
mesh.SetCD2Name(1, "renamed_edge")
assert mesh.EdgeDescriptor(1).name == "renamed_edge"
assert mesh.GetCD2Name(0) == "renamed_edge"
def test_2d_spline_edgedescriptor_names():
"""2D spline BC names propagated to EdgeDescriptor after meshing."""
from netgen.geom2d import SplineGeometry
geo = SplineGeometry()
p = [geo.AppendPoint(x,y) for x,y in [(0,0),(1,0),(1,1),(0,1)]]
geo.Append(["line", p[0], p[1]], leftdomain=1, rightdomain=0, bc="bottom")
geo.Append(["line", p[1], p[2]], leftdomain=1, rightdomain=0, bc="right")
geo.Append(["line", p[2], p[3]], leftdomain=1, rightdomain=0, bc="top")
geo.Append(["line", p[3], p[0]], leftdomain=1, rightdomain=0, bc="left")
mesh = geo.GenerateMesh(maxh=0.3)
expected_names = {"bottom", "right", "top", "left"}
found_names = set()
for i in range(1, mesh.GetNED()+1):
ed = mesh.EdgeDescriptor(i)
assert ed.edgenr >= 0
# surfnr[0] is leftdom, surfnr[1] is rightdom
assert ed.surfnr[0] == 1 or ed.surfnr[1] == 1 # at least one side is domain 1
# EdgeDescriptor name should be the actual BC name, not "default"
assert ed.name != "default", f"ED {i} has name 'default' instead of actual BC name"
found_names.add(ed.name)
assert found_names == expected_names, f"ED names {found_names} != expected {expected_names}"
def test_backward_compat_fields():
"""Segment index property is 0-based edge descriptor index; edgenr throws."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
seg = list(mesh.Elements1D())[0]
assert seg.index >= 1, "1-based index should be >= 1"
with pytest.raises(AttributeError):
_ = seg.edgenr
def test_surfaces_property_throws():
"""Accessing seg.surfaces raises AttributeError directing to EdgeDescriptor."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
seg = list(mesh.Elements1D())[0]
with pytest.raises(AttributeError, match="surfaces.*removed.*EdgeDescriptor"):
_ = seg.surfaces
def test_singular_property_throws():
"""Accessing seg.singular raises AttributeError directing to EdgeDescriptor."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
seg = list(mesh.Elements1D())[0]
with pytest.raises(AttributeError, match="singular.*EdgeDescriptor"):
_ = seg.singular
def test_surfaces_constructor_param_ignored():
"""Segment(surfaces=[...]) is accepted but surfaces live on EdgeDescriptor."""
from netgen.meshing import Mesh, Element1D, MeshPoint, Pnt
mesh = Mesh(dim=2)
p1 = mesh.Add(MeshPoint(Pnt(0, 0, 0)))
p2 = mesh.Add(MeshPoint(Pnt(1, 0, 0)))
# should not crash even though surfaces= is passed
seg = Element1D([p1, p2], surfaces=[5, 6], index=1)
mesh.Add(seg)
# the segment is added; surfaces are not stored on segment
assert len(list(mesh.Elements1D())) == 1
def test_numpy_element1d_edsi_field():
"""NumPy access to Elements1D has 'index' field, all >= 0."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
segs = mesh.Elements1D().NumPy()
assert "index" in segs.dtype.names, f"fields: {segs.dtype.names}"
indices = segs["index"]
assert len(indices) > 0
assert all(indices >= 1), f"found index values < 1 in numpy array"
def test_numpy_dtype_no_edgenr_field():
"""NumPy dtype for Elements1D should not contain 'edgenr' field."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
segs = mesh.Elements1D().NumPy()
assert "edgenr" not in segs.dtype.names, \
f"'edgenr' should be removed from numpy dtype, got fields: {segs.dtype.names}"
def test_numpy_index_field_matches_edsi():
"""NumPy 'index' field should match seg.index for every segment."""
pytest.importorskip("netgen.occ")
from netgen.occ import Box, Pnt, OCCGeometry
geo = OCCGeometry(Box(Pnt(0,0,0), Pnt(1,1,1)))
mesh = geo.GenerateMesh(maxh=0.5)
segs_np = mesh.Elements1D().NumPy()
segs_list = list(mesh.Elements1D())
for i, seg in enumerate(segs_list):
assert segs_np["index"][i] == seg.index, \
f"seg[{i}]: numpy index={segs_np['index'][i]} != index={seg.index}"
def test_manual_mesh_construction_1d():
"""Manually constructed 1D mesh with Element1D has assigned index on segments."""
from netgen.meshing import Mesh, MeshPoint, Element1D
from netgen.libngpy._meshing import Pnt as MeshPnt
mesh = Mesh(dim=1)
pts = [mesh.Add(MeshPoint(MeshPnt(x, 0, 0))) for x in [0.0, 0.25, 0.5, 0.75]]
for i in range(len(pts) - 1):
mesh.Add(Element1D(index=1, vertices=[pts[i], pts[i+1]]))
segs = list(mesh.Elements1D())
assert len(segs) == 3
for seg in segs:
assert seg.index == 1
def test_fdindex_python_property():
"""EdgeDescriptor.fdindex is readable and writable from Python."""
from netgen.meshing import EdgeDescriptor
ed = EdgeDescriptor()
assert ed.fdindex == -1, "default fdindex should be -1"
ed.fdindex = 42
assert ed.fdindex == 42