Files
tono/tests/node_tests/tip_shape_estimate.py
2026-04-04 00:25:53 -07:00

87 lines
3.5 KiB
Python

import numpy as np
import pytest
from tests.node_tests._shared import make_field
def run_tip_shape(field, feature_type="edge", feature_radius=100e-9, n_points=100):
from backend.nodes.tip_shape_estimate import TipShapeEstimate
node = TipShapeEstimate()
tip_shape, parameters = node.process(
field=field,
feature_type=feature_type,
feature_radius=feature_radius,
n_points=n_points,
)
return tip_shape, parameters
# ── Output shape and type ────────────────────────────────────────────────────
def test_output_shape():
"""tip_shape must be a 2D DataField."""
from backend.data_types import DataField
field = make_field(shape=(64, 64), xreal=64e-9, yreal=64e-9)
tip_shape, _ = run_tip_shape(field, feature_type="edge", n_points=33)
assert isinstance(tip_shape, DataField)
assert tip_shape.data.ndim == 2
assert tip_shape.data.shape[0] == tip_shape.data.shape[1]
# ── Parameters table ─────────────────────────────────────────────────────────
def test_parameters_table():
"""parameters must be a RecordTable containing a tip_radius entry."""
from backend.data_types import RecordTable
field = make_field(shape=(64, 64), xreal=64e-9, yreal=64e-9)
_, parameters = run_tip_shape(field, feature_type="edge", n_points=33)
assert isinstance(parameters, RecordTable)
quantities = {row["quantity"] for row in parameters}
assert "tip_radius" in quantities
# ── Tip apex is maximum ──────────────────────────────────────────────────────
def test_tip_apex_is_maximum():
"""The centre of the tip shape should be the highest point."""
field = make_field(shape=(64, 64), xreal=64e-9, yreal=64e-9)
tip_shape, _ = run_tip_shape(field, feature_type="edge", n_points=33)
n = tip_shape.data.shape[0]
ci = n // 2
assert tip_shape.data[ci, ci] == pytest.approx(tip_shape.data.max(), abs=1e-20)
# ── Sphere feature produces valid estimate ───────────────────────────────────
def test_sphere_feature():
"""Using a synthetic sphere as input produces a valid tip estimate."""
from backend.data_types import DataField
# Create a synthetic sphere on a 64x64 grid.
R = 100e-9 # sphere radius
n = 64
pixel_size = 10e-9
xreal = n * pixel_size
Y, X = np.mgrid[:n, :n]
r = np.sqrt((X - 32) ** 2 + (Y - 32) ** 2) * pixel_size
data = np.sqrt(np.maximum(R ** 2 - r ** 2, 0.0))
field = make_field(data=data, xreal=xreal, yreal=xreal)
tip_shape, parameters = run_tip_shape(
field, feature_type="sphere", feature_radius=R, n_points=33,
)
# The output must be a valid 2D DataField.
assert isinstance(tip_shape, DataField)
assert tip_shape.data.ndim == 2
# Apex should be the maximum.
ci = tip_shape.data.shape[0] // 2
assert tip_shape.data[ci, ci] == pytest.approx(tip_shape.data.max(), abs=1e-20)
# Parameters should contain tip_radius.
quantities = {row["quantity"]: row["value"] for row in parameters}
assert "tip_radius" in quantities
# The estimated radius must be a positive finite number.
assert quantities["tip_radius"] > 0