Skip to content

Acquisition

sies.acquisition.configurations

Concrete acquisition configurations.

Only the geometry of the acquisition system lives here (positions of sources and receivers); the physics (frequency, contrast) belongs to the PDE models of pde.

ViewMode dataclass

ViewMode(nb_arcs=1, aperture=2 * np.pi, angle_of_view=2 * np.pi)

Angular coverage of an acquisition system.

Parameters:

Name Type Description Default
nb_arcs int

Number of separated arcs carrying sources/receivers.

1
aperture float

Angular aperture of each arc, in radians.

``2 * pi``
angle_of_view float

Angular range covered by the starting points of the arcs.

``2 * pi``

full_view property

full_view

bool: True if the configuration is a single full circle.

AcquisitionConfig

AcquisitionConfig(src_groups, rcv_groups, center)

Base class for acquisition configurations.

Sources and receivers are organized in groups. Receivers of a group only listen to the sources of the same group; all groups contain the same number of sources and receivers.

Parameters:

Name Type Description Default
src_groups list of ndarray

Source coordinates of each group, as (2, ns) arrays.

required
rcv_groups list of ndarray

Receiver coordinates of each group, as (2, nr) arrays.

required
center (ndarray, shape(2))

Reference center of the measurement system.

required
Source code in src/sies/acquisition/configurations.py
100
101
102
103
104
105
def __init__(self, src_groups: list[NDArray], rcv_groups: list[NDArray], center: NDArray):
    if len(src_groups) != len(rcv_groups):
        raise ValueError("Sources and receivers must have the same number of groups.")
    self._src_groups = src_groups
    self._rcv_groups = rcv_groups
    self.center = np.asarray(center, dtype=float).reshape(2)

nb_groups property

nb_groups

int: Number of source/receiver groups.

nb_sources_per_group property

nb_sources_per_group

int: Number of sources in each group.

nb_receivers_per_group property

nb_receivers_per_group

int: Number of receivers in each group.

nb_sources property

nb_sources

int: Total number of sources.

nb_receivers property

nb_receivers

int: Total number of receivers.

data_dim property

data_dim

int: Total number of scalar measurements (sources x receivers).

all_sources property

all_sources

ndarray: All source coordinates, concatenated group by group.

all_receivers property

all_receivers

ndarray: All receiver coordinates, concatenated group by group.

group

group(g)

Return the sources and receivers of a group.

Parameters:

Name Type Description Default
g int

Group index.

required

Returns:

Name Type Description
src (ndarray, shape(2, ns))

Source coordinates of the group.

rcv (ndarray, shape(2, nr))

Receiver coordinates of the group.

Source code in src/sies/acquisition/configurations.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def group(self, g: int) -> tuple[NDArray, NDArray]:
    """Return the sources and receivers of a group.

    Parameters
    ----------
    g : int
        Group index.

    Returns
    -------
    src : ndarray, shape (2, ns)
        Source coordinates of the group.
    rcv : ndarray, shape (2, nr)
        Receiver coordinates of the group.
    """
    return self._src_groups[g], self._rcv_groups[g]

source

source(s)

Return the coordinates of the s-th source (global index).

Parameters:

Name Type Description Default
s int

Global source index, in range(nb_sources).

required

Returns:

Type Description
(ndarray, shape(2))

Coordinates of the source.

Source code in src/sies/acquisition/configurations.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def source(self, s: int) -> NDArray:
    """Return the coordinates of the `s`-th source (global index).

    Parameters
    ----------
    s : int
        Global source index, in ``range(nb_sources)``.

    Returns
    -------
    ndarray, shape (2,)
        Coordinates of the source.
    """
    if not 0 <= s < self.nb_sources:
        raise IndexError("Source index out of range.")
    g, i = divmod(s, self.nb_sources_per_group)
    return self._src_groups[g][:, i]

receivers_of_source

receivers_of_source(s)

Return the receivers listening to the s-th source.

Parameters:

Name Type Description Default
s int

Global source index.

required

Returns:

Type Description
(ndarray, shape(2, nr))

Coordinates of the receivers of the source's group.

Source code in src/sies/acquisition/configurations.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def receivers_of_source(self, s: int) -> NDArray:
    """Return the receivers listening to the `s`-th source.

    Parameters
    ----------
    s : int
        Global source index.

    Returns
    -------
    ndarray, shape (2, nr)
        Coordinates of the receivers of the source's group.
    """
    if not 0 <= s < self.nb_sources:
        raise IndexError("Source index out of range.")
    g = s // self.nb_sources_per_group
    return self._rcv_groups[g]

plot

plot(ax=None, **kwargs)

Plot sources (crosses) and receivers (circles).

Parameters:

Name Type Description Default
ax Axes

Axes to draw on. A new figure is created if omitted.

None
**kwargs

Forwarded to plot.

{}

Returns:

Type Description
Axes

The axes containing the plot.

Source code in src/sies/acquisition/configurations.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def plot(self, ax=None, **kwargs):
    """Plot sources (crosses) and receivers (circles).

    Parameters
    ----------
    ax : matplotlib.axes.Axes, optional
        Axes to draw on. A new figure is created if omitted.
    **kwargs
        Forwarded to `plot`.

    Returns
    -------
    matplotlib.axes.Axes
        The axes containing the plot.
    """
    import matplotlib.pyplot as plt

    if ax is None:
        _, ax = plt.subplots()
    src, rcv = self.all_sources, self.all_receivers
    ax.plot(src[0], src[1], "x", label="sources", **kwargs)
    ax.plot(rcv[0], rcv[1], "o", fillstyle="none", label="receivers", **kwargs)
    ax.plot(*self.center, "r*")
    ax.set_aspect("equal")
    return ax

Concentric

Concentric(center, radius_src, nb_src, radius_rcv, nb_rcv, view=None, grouped=False)

Bases: AcquisitionConfig

Sources and receivers on two concentric circles.

Parameters:

Name Type Description Default
center (ndarray, shape(2))

Center of the measurement circles.

required
radius_src float

Radius of the source circle.

required
nb_src int

Number of sources per arc.

required
radius_rcv float

Radius of the receiver circle.

required
nb_rcv int

Number of receivers per arc.

required
view ViewMode

Angular coverage; full view by default.

None
grouped bool

If True, each arc forms an independent group (limited view); if False, every receiver listens to every source.

False

Attributes:

Name Type Description
radius_src, radius_rcv float

Radii of the measurement circles.

equispaced bool

True for a single full-view circle of equispaced positions, in which case the analytic CGPT reconstruction applies.

Source code in src/sies/acquisition/configurations.py
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def __init__(
    self,
    center: NDArray,
    radius_src: float,
    nb_src: int,
    radius_rcv: float,
    nb_rcv: int,
    view: ViewMode | None = None,
    grouped: bool = False,
):
    view = view or ViewMode()
    src_arcs = sources_on_circle(
        view.nb_arcs, nb_src, radius_src, center, view.aperture, view.angle_of_view
    )
    rcv_arcs = sources_on_circle(
        view.nb_arcs, nb_rcv, radius_rcv, center, view.aperture, view.angle_of_view
    )

    if grouped:
        src_groups, rcv_groups = src_arcs, rcv_arcs
    else:
        src_groups = [np.concatenate(src_arcs, axis=1)]
        rcv_groups = [np.concatenate(rcv_arcs, axis=1)]

    super().__init__(src_groups, rcv_groups, center)
    self.radius_src = radius_src
    self.radius_rcv = radius_rcv
    self.view = view
    self.equispaced = self.nb_groups == 1 and view.full_view

Coincided

Coincided(center, radius, nb_src, view=None, grouped=False)

Bases: Concentric

Coincided sources and receivers on a single circle.

Each source position also acts as a receiver: the simplest configuration for the conductivity problem.

Parameters:

Name Type Description Default
center (ndarray, shape(2))

Center of the measurement circle.

required
radius float

Radius of the measurement circle.

required
nb_src int

Number of sources (and receivers) per arc.

required
view ViewMode

Angular coverage; full view by default.

None
grouped bool

See Concentric.

False
Source code in src/sies/acquisition/configurations.py
308
309
310
311
312
313
314
315
316
def __init__(
    self,
    center: NDArray,
    radius: float,
    nb_src: int,
    view: ViewMode | None = None,
    grouped: bool = False,
):
    super().__init__(center, radius, nb_src, radius, nb_src, view, grouped)

sources_on_circle

sources_on_circle(nb_arcs, nb_per_arc, radius, center, aperture, angle_of_view=2 * np.pi)

Place sources/receivers on concentric circular arcs.

The arcs are equally distributed over [0, angle_of_view) and each covers the angle aperture.

Parameters:

Name Type Description Default
nb_arcs int

Number of arcs.

required
nb_per_arc int

Number of points per arc.

required
radius float

Radius of the measurement circle.

required
center (ndarray, shape(2))

Center of the measurement circle.

required
aperture float

Angular aperture of each arc.

required
angle_of_view float

Total angle of view.

``2 * pi``

Returns:

Type Description
list of ndarray

Coordinates of the points of each arc, as (2, nb_per_arc) arrays.

Source code in src/sies/acquisition/configurations.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def sources_on_circle(
    nb_arcs: int,
    nb_per_arc: int,
    radius: float,
    center: NDArray,
    aperture: float,
    angle_of_view: float = 2 * np.pi,
) -> list[NDArray]:
    """Place sources/receivers on concentric circular arcs.

    The arcs are equally distributed over ``[0, angle_of_view)`` and each
    covers the angle `aperture`.

    Parameters
    ----------
    nb_arcs : int
        Number of arcs.
    nb_per_arc : int
        Number of points per arc.
    radius : float
        Radius of the measurement circle.
    center : ndarray, shape (2,)
        Center of the measurement circle.
    aperture : float
        Angular aperture of each arc.
    angle_of_view : float, default ``2 * pi``
        Total angle of view.

    Returns
    -------
    list of ndarray
        Coordinates of the points of each arc, as ``(2, nb_per_arc)``
        arrays.
    """
    center = np.asarray(center, dtype=float).reshape(2, 1)
    arcs = []
    for n in range(nb_arcs):
        start = n / nb_arcs * angle_of_view
        angles = start + np.arange(nb_per_arc) / nb_per_arc * aperture
        arcs.append(radius * np.vstack([np.cos(angles), np.sin(angles)]) + center)
    return arcs