Process reference: line_of_sight

Generated from the docstrings of vaft.process.line_of_sight: 3 public functions. Each entry is what vaft.process.describe("line_of_sight.<name>") prints. All process categories

Overview

A line-integrated diagnostic measures B_i = int epsilon dl along sightline i. On a grid of cells that is a matrix product B = G epsilon with G_ij the length of sightline i inside cell j. This module builds G from exact segment-cell intersections, so it is a geometric statement about the chords and the grid, not a resampled image of a mask, and it converges as the grid is refined.

The operator knows nothing about which diagnostic the chords belong to or what the field is: soft X-ray emissivity, radiated power or density all integrate the same way. Machine geometry is loaded elsewhere, for VEST by vaft.machine_mapping.soft_x_rays.sxr_sightlines.

SymbolMeaningUnit
G_ijlength of chord i inside cell jm
epsilon_jfield value on cell jany, per metre
B_iline integral along chord iany

A grid node is a cell centre. Cell edges sit halfway between nodes and half a spacing beyond the outermost ones, so the cells tile the rectangle the grid spans plus half a cell all round, and a field sampled on the nodes of an equilibrium map is integrated as piecewise constant on those cells. Cells are flattened in C order over (R, Z), the order field.reshape(-1) gives for a field indexed (R, Z).

A chord is a straight segment in one poloidal plane. Its end points are (R, Z) at one toroidal angle; a sightline that crosses toroidal angle is not representable here.

Functions

build_line_integral_operatorThe path-length matrix of chords through the cells of an (R, Z) grid. Convention
clip_segment_to_polygonThe parameter intervals of a segment that lie inside a closed polygon. Convention
project_emissivityLine integrals of one or many (R, Z) fields through a path-length operator.

build_line_integral_operator

Machine-independentConvention-sensitive source
build_line_integral_operator(r, z, sightlines, *, domain=None)

The path-length matrix of chords through the cells of an (R, Z) grid.

NameTypeUnitDescription
rarray_likem

Major-radius grid axis, the cell centres, strictly increasing.

zarray_likem

Height grid axis, the cell centres, strictly increasing.

sightlinesSightlines-

The chords, one row of the operator each.

domaintuple of array_like, optionalm

(R, Z) vertices of a closed polygon, normally the limiter; each chord counts only where it is inside it.

Returns
—scipy.sparse.csr_matrixm

G shaped (len(sightlines), len(r) * len(z)); G @ field.reshape(-1) is the line integral of a field indexed (R, Z)

Convention

Nodes are cell centres, with edges halfway between them and half a spacing beyond the outermost node, and cells are flattened in C order over (R, Z). A chord running exactly along an interior edge is assigned to the cell above it, and along an outer edge to the outermost cell. Each entry is the exact length of the chord inside that cell, so a row sums to the chord’s length inside the gridded rectangle and the domain. Chords are straight in the (R, Z) plane; their toroidal angle does not enter the operator.

Raises

  • ValueError A grid axis is not strictly increasing or has fewer than two values, or the domain polygon is degenerate.
Processing steps · Applicability · Limitations · Provenance

Processing steps

  1. Clip each chord to the domain polygon, if one is given.
  2. For each inside interval, collect the parameters at which it crosses a cell edge in either direction.
  3. Assign each piece between consecutive crossings to the cell containing its midpoint, with the piece’s length as the entry.

Applicability

Machine-independent.

Limitations

A pencil beam: no finite aperture, no viewing cone, no etendue weighting and no reflection. Deterministic but not cached; build it once per grid and chord set and reuse it.

Provenance

  1. Siddon, R. L., Med. Phys. 12, 252 (1985).

clip_segment_to_polygon

Machine-independentConvention-sensitive source
clip_segment_to_polygon(p1, p2, polygon_r, polygon_z)
Show source vaft/process/line_of_sight.py, lines 101–179
def clip_segment_to_polygon(p1, p2, polygon_r, polygon_z) -> list[tuple[float, float]]:
    """The parameter intervals of a segment that lie inside a closed polygon.

    Parameters
    ----------
    p1 : sequence of float
        ``(R, Z)`` of the segment start [m].
    p2 : sequence of float
        ``(R, Z)`` of the segment end [m].
    polygon_r : array_like
        Polygon vertex major radii, closed or open [m].
    polygon_z : array_like
        Polygon vertex heights [m].

    Returns
    -------
    list of tuple of float
        ``(t_start, t_end)`` intervals of the segment parameter
        ``p = p1 + t (p2 - p1)``, ``0 <= t <= 1``, lying inside the polygon,
        in increasing order [-].

    Raises
    ------
    ValueError
        The polygon has fewer than three distinct vertices.

    Convention
    ----------
    Inside is decided by even-odd parity at each sub-interval's midpoint, so a
    non-convex polygon, such as a limiter with a divertor recess, yields
    several intervals. A segment grazing a vertex or running along an edge
    splits there with zero-length pieces dropped.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    The polygon is a single loop; holes are not supported.

    Provenance
    ----------
    .. [1] The even-odd rule for point-in-polygon, as
       :class:`matplotlib.path.Path` applies it.
    """
    from matplotlib.path import Path

    pr = np.asarray(polygon_r, dtype=float).reshape(-1)
    pz = np.asarray(polygon_z, dtype=float).reshape(-1)
    if pr.size != pz.size:
        raise ValueError("polygon_r and polygon_z must have the same length")
    if pr.size and (pr[0] != pr[-1] or pz[0] != pz[-1]):
        pr = np.append(pr, pr[0])
        pz = np.append(pz, pz[0])
    if np.unique(np.column_stack((pr, pz)), axis=0).shape[0] < 3:
        raise ValueError("a polygon needs at least three distinct vertices")
    a = np.asarray(p1, dtype=float)
    d = np.asarray(p2, dtype=float) - a
    e0 = np.column_stack((pr[:-1], pz[:-1]))
    e = np.column_stack((pr[1:], pz[1:])) - e0
    denom = d[0] * e[:, 1] - d[1] * e[:, 0]
    rel = e0 - a
    with np.errstate(divide="ignore", invalid="ignore"):
        t = (rel[:, 0] * e[:, 1] - rel[:, 1] * e[:, 0]) / denom
        u = (rel[:, 0] * d[1] - rel[:, 1] * d[0]) / denom
    hit = (denom != 0) & (u >= 0.0) & (u <= 1.0) & (t > 0.0) & (t < 1.0)
    cuts = np.unique(np.concatenate(([0.0, 1.0], t[hit])))
    mids = 0.5 * (cuts[:-1] + cuts[1:])
    inside = Path(np.column_stack((pr, pz))).contains_points(a + mids[:, None] * d)
    intervals: list[tuple[float, float]] = []
    for lo, hi, keep in zip(cuts[:-1], cuts[1:], inside):
        if not keep or hi <= lo:
            continue
        if intervals and intervals[-1][1] == lo:
            intervals[-1] = (intervals[-1][0], float(hi))
        else:
            intervals.append((float(lo), float(hi)))
    return intervals

The parameter intervals of a segment that lie inside a closed polygon.

NameTypeUnitDescription
p1sequence of floatm

(R, Z) of the segment start.

p2sequence of floatm

(R, Z) of the segment end.

polygon_rarray_likem

Polygon vertex major radii, closed or open.

polygon_zarray_likem

Polygon vertex heights.

Returns
—list of tuple of float-

(t_start, t_end) intervals of the segment parameter p = p1 + t (p2 - p1), 0 <= t <= 1, lying inside the polygon, in increasing order.

Convention

Inside is decided by even-odd parity at each sub-interval’s midpoint, so a non-convex polygon, such as a limiter with a divertor recess, yields several intervals. A segment grazing a vertex or running along an edge splits there with zero-length pieces dropped.

Raises

  • ValueError The polygon has fewer than three distinct vertices.
Applicability · Limitations · Provenance

Applicability

Machine-independent.

Limitations

The polygon is a single loop; holes are not supported.

Provenance

  1. The even-odd rule for point-in-polygon, as matplotlib.path.Path applies it.

project_emissivity

Machine-independent source
project_emissivity(emissivity, operator)
Show source vaft/process/line_of_sight.py, lines 302–341
def project_emissivity(emissivity, operator) -> np.ndarray:
    """Line integrals of one or many ``(R, Z)`` fields through a path-length operator.

    Parameters
    ----------
    emissivity : array_like
        Field indexed ``(..., R, Z)``; leading axes, such as time, are kept
        [any, per metre].
    operator : scipy.sparse matrix or ndarray
        From :func:`build_line_integral_operator` for the same grid [m].

    Returns
    -------
    numpy.ndarray
        Line integrals shaped ``(..., n_chords)`` [any].

    Raises
    ------
    ValueError
        The field's last two axes do not hold as many cells as the operator
        has columns.

    Applicability
    -------------
    Machine-independent.

    Limitations
    -----------
    Exactly linear: whatever the field's unit, the result is that unit times
    metres. No detector response, filter transmission or etendue is applied.
    """
    field = np.asarray(emissivity, dtype=float)
    if field.ndim < 2:
        raise ValueError("emissivity must be indexed (..., R, Z)")
    lead = field.shape[:-2]
    cells = field.shape[-2] * field.shape[-1]
    if cells != operator.shape[1]:
        raise ValueError(f"the field has {cells} cells but the operator {operator.shape[1]} columns")
    flat = field.reshape(-1, cells)
    return np.asarray(operator @ flat.T).T.reshape(*lead, operator.shape[0])

Line integrals of one or many (R, Z) fields through a path-length operator.

NameTypeUnitDescription
emissivityarray_likeany, per metre

Field indexed (..., R, Z); leading axes, such as time, are kept

operatorscipy.sparse matrix or ndarraym

From build_line_integral_operator for the same grid.

Returns
—numpy.ndarrayany

Line integrals shaped (..., n_chords).

Raises

  • ValueError The field’s last two axes do not hold as many cells as the operator has columns.
Applicability · Limitations

Applicability

Machine-independent.

Limitations

Exactly linear: whatever the field’s unit, the result is that unit times metres. No detector response, filter transmission or etendue is applied.

results matching ""

    No results matching ""