update docs and tests

This commit is contained in:
2026-04-15 23:21:08 -07:00
parent 0bf001c24b
commit 349142f0e6
3 changed files with 37 additions and 13 deletions

View File

@@ -33,10 +33,13 @@ class RadialProfile:
FUNCTION = "process"
DESCRIPTION = (
"Compute the azimuthally averaged radial profile from a centre point. "
"Compute an azimuthally averaged profile around a centre point. "
"At each radius, every pixel in the full 360° ring is averaged together, "
"so the profile is direction-independent — there is no clockwise/counter-clockwise "
"traversal and no start/end point along the ring. "
"Drag the centre marker on the preview to reposition the profile, "
"drag either end marker to change the radius. "
"Output x-axis is radius in physical xy units. "
"or drag either end marker (both just set the outer radius) to change the extent. "
"Output x-axis is radius in physical xy units."
)
KEYWORDS = ("azimuthal average", "ring average", "circular", "isotropic")

View File

@@ -1,6 +1,6 @@
# Radial Profile
Compute the azimuthally averaged radial profile from a centre point. The output x-axis is radius in physical xy units. Equivalent to gwy_data_field_angular_average used by Gwyddion's Radial Profile tool.
Compute an **azimuthally averaged** profile around a centre point on a DATA_FIELD. At each radius, every pixel in the full 360° ring around the centre is averaged together, so the profile is direction-independent — there is no clockwise/counter-clockwise traversal and no start or end point along the ring. The output is a single 1-D profile: value vs. radius.
## Inputs
@@ -18,11 +18,13 @@ Compute the azimuthally averaged radial profile from a centre point. The output
| Name | Type | Default | Description |
|------|------|---------|-------------|
| cx | FLOAT | 0.5 | Centre x position as a fraction of field width (0 = left, 1 = right) |
| cy | FLOAT | 0.5 | Centre y position as a fraction of field height (0 = top, 1 = bottom) |
| n_bins | INT | 128 | Number of radial bins (4-4096) |
## Interactive preview
The dashed circle around the centre shows the outer radius used by the profile. Pixels beyond it are not included in the averaging.
## Notes
- Pixels are assigned to radial bins by Euclidean distance; bins near the centre contain fewer pixels and may be noisier.
- Pixels are assigned to radial bins by Euclidean distance from the centre; inner bins contain fewer pixels and may be noisier.
- Physical x-axis units come from the field's si_unit_xy; uncalibrated fields produce pixel-unit radii.

View File

@@ -11,7 +11,7 @@ def test_radial_profile_constant_field():
node = RadialProfile()
field = make_field(data=np.full((64, 64), 2.5))
result, = node.process(field, cx=0.5, cy=0.5, n_bins=32)
result, = node.process(field, n_bins=32, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
assert isinstance(result, LineData)
assert len(result.data) == 32
@@ -26,7 +26,7 @@ def test_radial_profile_units():
node = RadialProfile()
field = make_field()
result, = node.process(field, cx=0.5, cy=0.5, n_bins=32)
result, = node.process(field, n_bins=32, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
assert result.x_unit == field.si_unit_xy
assert result.y_unit == field.si_unit_z
@@ -38,7 +38,7 @@ def test_radial_profile_x_axis_monotone():
node = RadialProfile()
field = make_field()
result, = node.process(field, cx=0.5, cy=0.5, n_bins=64)
result, = node.process(field, n_bins=64, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
assert result.x_axis[0] >= 0.0
assert np.all(np.diff(result.x_axis) > 0)
@@ -50,7 +50,7 @@ def test_radial_profile_off_centre():
node = RadialProfile()
field = make_field(data=np.ones((64, 64)))
result, = node.process(field, cx=0.0, cy=0.0, n_bins=32)
result, = node.process(field, n_bins=32, cx=0.0, cy=0.0, ex=1.0, ey=1.0)
assert len(result.data) == 32
finite = result.data[np.isfinite(result.data)]
assert np.allclose(finite, 1.0, atol=1e-10)
@@ -67,7 +67,7 @@ def test_radial_profile_radial_symmetry():
data = np.cos(r * np.pi / (xres / 2.0))
field = make_field(data=data)
result, = node.process(field, cx=0.5, cy=0.5, n_bins=32)
result, = node.process(field, n_bins=32, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
finite = result.data[np.isfinite(result.data)]
# The profile should vary (not constant)
assert np.std(finite) > 0.01
@@ -80,6 +80,25 @@ def test_radial_profile_n_bins():
field = make_field()
for n in (16, 64, 256):
result, = node.process(field, cx=0.5, cy=0.5, n_bins=n)
result, = node.process(field, n_bins=n, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
assert len(result.data) == n
assert len(result.x_axis) == n
def test_radial_profile_radius_controlled_by_endpoint():
"""The outer radius is set by the distance from (cx,cy) to (ex,ey)."""
from backend.nodes.radial_profile import RadialProfile
node = RadialProfile()
field = make_field()
# End at (1.0, 0.5): radius = 0.5 * xreal
short, = node.process(field, n_bins=32, cx=0.5, cy=0.5, ex=1.0, ey=0.5)
expected_r_short = 0.5 * field.xreal
assert np.isclose(short.x_axis[-1], expected_r_short, rtol=0.05)
# End at corner: radius = sqrt(xreal^2 + yreal^2) * 0.5 (half-diagonal)
diag, = node.process(field, n_bins=32, cx=0.5, cy=0.5, ex=1.0, ey=1.0)
expected_r_diag = 0.5 * np.hypot(field.xreal, field.yreal)
assert np.isclose(diag.x_axis[-1], expected_r_diag, rtol=0.05)
assert diag.x_axis[-1] > short.x_axis[-1]