This page was generated by nbsphinx from docs/notebooks/motion_list/motionlist.ipynb.

Demo of MotionBuilder

[1]:
%matplotlib inline
[2]:
import numpy as np
import matplotlib.pyplot as plt
import re
import sys
import xarray as xr
[3]:
try:
    from bapsf_motion.motion_builder import MotionBuilder
except ModuleNotFoundError:
    from pathlib import Path

    HERE = Path().cwd()
    BAPSF_MOTION = (HERE / ".." / ".." / ".." ).resolve()
    sys.path.append(str(BAPSF_MOTION))

    from bapsf_motion.motion_builder import MotionBuilder
[4]:
plt.rcParams.update(
    {
        # "figure.figsize": [12, 0.56 * 12],
        "figure.figsize": [10, 0.8 * 10],
        "font.size": 16,
    }
)

Let’s set up a typical rectangular grid for a probe on the East port.

[5]:
mb = MotionBuilder(
    space=[
        {"label": "x", "range": [-55, 55], "num": 221},
        {"label": "y", "range": [-55, 55], "num": 221},
    ],
    exclusions=[
        {"type": "lapd_xy", "port_location": "E", "cone_full_angle": 60},
        {"type": "circle", "radius": 5, "exclude": "inside"},
    ],
    layers=[
        {"type": "grid", "limits": [[0, 30], [-30, 30]], "steps": [11, 21]},
    ],
)

mb.mask.plot(x="x", y="y");

points = mb._ds["point_layer1"].data
flat_ax = np.prod(points.shape[:-1])
points = np.reshape(points, (flat_ax, points.shape[-1]))
pt1 = points
# print(pt1.shape)

plt.scatter(
    points[...,0],
    points[..., 1], s=4**2,
    color="red",
)

valid_points = mb.motion_list

plt.scatter(
    valid_points[..., 0],
    valid_points[..., 1],
    linewidth=1,
    s=6**2,
    facecolors="deepskyblue",
    edgecolors="black",
);
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[5], line 1
----> 1 mb = MotionBuilder(
      2     space=[
      3         {"label": "x", "range": [-55, 55], "num": 221},
      4         {"label": "y", "range": [-55, 55], "num": 221},

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/core.py:102, in MotionBuilder.__init__(self, space, layers, exclusions, layer_to_motionlist_scheme)
    100     for exclusion in exclusions:
    101         ex_type = exclusion.pop("type")
--> 102         self.add_exclusion(ex_type, **exclusion)
    104 self.generate()

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/core.py:337, in MotionBuilder.add_exclusion(self, ex_type, **settings)
    329     warnings.warn(
    330         f"The motion builder already has a govern exclusion layer "
    331         f"({self.exclusions[0]}).  Not adding exclusion layer "
    332         f"{exclusion}.)",
    333         ConfigurationWarning,
    334     )
    336 self.clear_motion_list()
--> 337 self.rebuild_mask()

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/core.py:605, in MotionBuilder.rebuild_mask(self)
    602 # The govern exclusion is always set to index 0 in self.exclusions.
    603 # Thus, iterate self.exclusions in reverse order.
    604 for ex in reversed(self.exclusions):
--> 605     ex.update_global_mask()

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/base.py:242, in GovernExclusion.update_global_mask(self)
    235     raise RuntimeError(
    236         f"For exclusion {self.name} skip_ds_add={self.skip_ds_add} and thus "
    237         f"the exclusion can not be merged into the global mask."
    238     )
    240 # Since GovernExclusion use the existing mask to generate its own
    241 # mask, the exclusion must be regenerated during every global update
--> 242 self.regenerate_exclusion()
    243 self.mask[...] = self.exclusion[...]

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/base.py:212, in BaseExclusion.regenerate_exclusion(self)
    204     raise RuntimeError(
    205         f"For exclusion {self.name} skip_ds_add={self.skip_ds_add} and thus "
    206         f"the exclusion can not be regenerated and updated in the Dataset.  "
    207         f"To get the exclusion matrix use the 'ex.exclusion' property."
    208     )
    210 self.composed_exclusions.clear()
--> 212 self._ds[self.name] = self._generate_exclusion()

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/lapd.py:307, in LaPDXYExclusion._generate_exclusion(self)
    302 def _generate_exclusion(self):
    303     """
    304     Generate and return the boolean mask corresponding to the
    305     exclusion configuration.
    306     """
--> 307     ex = self._generate_shadow_exclusion()
    308     self.composed_exclusions["shadow"] = ex
    310     ex = self._generate_chamber_exclusion()

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/lapd.py:418, in LaPDXYExclusion._generate_shadow_exclusion(self)
    417 def _generate_shadow_exclusion(self):
--> 418     ex = Shadow2DExclusion(
    419         self._ds,
    420         skip_ds_add=True,
    421         source_point=self.insertion_point,
    422     )
    423     return ex

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/shadow.py:97, in Shadow2DExclusion.__init__(self, ds, source_point, skip_ds_add)
     94 self._boundaries = None
     95 self._insertion_edge_indices = None
---> 97 super().__init__(
     98     ds,
     99     source_point=source_point,
    100     skip_ds_add=skip_ds_add,
    101 )

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/base.py:66, in BaseExclusion.__init__(self, ds, skip_ds_add, **kwargs)
     64 self._stored_exclusion = None
     65 if self.skip_ds_add:
---> 66     self._stored_exclusion = self._generate_exclusion()
     67     return
     69 # store this mask to the Dataset

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/shadow.py:190, in Shadow2DExclusion._generate_exclusion(self)
    186 corner_rays = self._build_corner_rays(edge_pool)
    188 # Reduce corner rays
    189 # - remove corner arrays that point to points behind nearer edges
--> 190 ray_mask = self._build_corner_ray_mask(corner_rays, edge_pool)
    191 corner_rays = corner_rays[ray_mask]
    193 # Build an array of fanned rays from all the corner rays

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/bapsf_motion/motion_builder/exclusions/shadow.py:314, in Shadow2DExclusion._build_corner_ray_mask(self, corner_rays, edge_pool)
    303 # determine if a corner_ray intersects an edge that is closer
    304 # to the insertion point
    305 # - solving the eqn:
   (...)    311 #     closer edge to the insertion point
    312 #
    313 point_deltas = edge_pool[..., 0, :] - self.source_point
--> 314 denominator = np.cross(corner_rays, edge_vectors[:, np.newaxis, ...]).swapaxes(
    315     0, 1
    316 )
    317 mu_array = np.cross(point_deltas, edge_vectors) / denominator
    318 nu_array = (
    319     np.cross(point_deltas[:, np.newaxis, ...], corner_rays).swapaxes(0, 1)
    320     / denominator
    321 )

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/numpy/_core/numeric.py:1679, in cross(a, b, axisa, axisb, axisc, axis)
   1677 b = moveaxis(b, axisb, -1)
   1678 if a.shape[-1] != 3 or b.shape[-1] != 3:
-> 1679     raise ValueError(
   1680         f"Both input arrays must be (arrays of) 3-dimensional vectors, "
   1681         f"but they are {a.shape[-1]} and {b.shape[-1]} dimensional instead."
   1682     )
   1684 # Create the output array
   1685 shape = *broadcast(a[..., 0], b[..., 0]).shape, 3

ValueError: Both input arrays must be (arrays of) 3-dimensional vectors, but they are 2 and 2 dimensional instead.
[6]:
mb._ds
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 mb._ds

NameError: name 'mb' is not defined

Let’s say we want the same setup as before, but with a probe on the 45 degree port. This is easily accomplished by just specifying the port_location as an angle for the LaPD exclusion layer.

[7]:
mb = MotionBuilder(
    space="lapd_xy",
    exclusions=[
        {"type": "lapd_xy", "port_location": 45, "cone_full_angle": 60},
    ],
    layers=[
        {"type": "grid", "limits": [[0, 30], [-30, 30]], "steps": [11, 21]},
    ],
)

mb.mask.plot(x="x", y="y");

points = mb._ds["point_layer1"].data
flat_ax = np.prod(points.shape[:-1])
points = np.reshape(points, (flat_ax, points.shape[-1]))
pt1 = points
# print(pt1.shape)

plt.scatter(
    points[...,0],
    points[..., 1], s=4**2,
    color="red",
)

valid_points = mb.motion_list

plt.scatter(
    valid_points[..., 0],
    valid_points[..., 1],
    linewidth=1,
    s=6**2,
    facecolors="deepskyblue",
    edgecolors="black",
);
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1250, in Dataset._construct_dataarray(self, name)
   1248             variable = self._variables[name]
   1249         except KeyError:
-> 1250             _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
   1251

KeyError: 'point_layer1'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1368, in Dataset.__getitem__(self, key)
   1366                 if isinstance(key, tuple):
   1367                     message += f"\nHint: use a list to select multiple variables, for example `ds[{list(key)}]`"
-> 1368                 raise KeyError(message) from e
   1369

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1250, in Dataset._construct_dataarray(self, name)
   1248             variable = self._variables[name]
   1249         except KeyError:
-> 1250             _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
   1251

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset_utils.py:79, in _get_virtual_variable(variables, key, dim_sizes)
     78 if len(split_key) != 2:
---> 79     raise KeyError(key)
     81 ref_name, var_name = split_key

KeyError: 'point_layer1'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[7], line 13
      9 )
     10
     11 mb.mask.plot(x="x", y="y");
     12
---> 13 points = mb._ds["point_layer1"].data
     14 flat_ax = np.prod(points.shape[:-1])
     15 points = np.reshape(points, (flat_ax, points.shape[-1]))
     16 pt1 = points

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1368, in Dataset.__getitem__(self, key)
   1364
   1365                 # If someone attempts `ds['foo' , 'bar']` instead of `ds[['foo', 'bar']]`
   1366                 if isinstance(key, tuple):
   1367                     message += f"\nHint: use a list to select multiple variables, for example `ds[{list(key)}]`"
-> 1368                 raise KeyError(message) from e
   1369
   1370         if utils.iterable_of_hashable(key):
   1371             return self._copy_listed(key)

KeyError: "No variable named 'point_layer1'. Did you mean one of ('point_layer0',)?"
../../_images/notebooks_motion_list_motionlist_9_1.png

Let’s se we just want a vertical line for a target in the top port.

[8]:
mb = MotionBuilder(
    space="lapd_xy",
    exclusions=[
        {"type": "lapd_xy", "port_location": "T", "cone_full_angle": 60},
    ],
    layers=[
        {"type": "grid", "limits": [[0, 0], [-20, 20]], "steps": [1, 11]},
    ],
)

mb.mask.plot(x="x", y="y");

points = mb._ds["point_layer1"].data
flat_ax = np.prod(points.shape[:-1])
points = np.reshape(points, (flat_ax, points.shape[-1]))
pt1 = points
# print(pt1.shape)

plt.scatter(
    points[...,0],
    points[..., 1], s=4**2,
    color="red",
)

valid_points = mb.motion_list

plt.scatter(
    valid_points[..., 0],
    valid_points[..., 1],
    linewidth=1,
    s=6**2,
    facecolors="deepskyblue",
    edgecolors="black",
);
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1250, in Dataset._construct_dataarray(self, name)
   1248             variable = self._variables[name]
   1249         except KeyError:
-> 1250             _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
   1251

KeyError: 'point_layer1'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1368, in Dataset.__getitem__(self, key)
   1366                 if isinstance(key, tuple):
   1367                     message += f"\nHint: use a list to select multiple variables, for example `ds[{list(key)}]`"
-> 1368                 raise KeyError(message) from e
   1369

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1250, in Dataset._construct_dataarray(self, name)
   1248             variable = self._variables[name]
   1249         except KeyError:
-> 1250             _, name, variable = _get_virtual_variable(self._variables, name, self.sizes)
   1251

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset_utils.py:79, in _get_virtual_variable(variables, key, dim_sizes)
     78 if len(split_key) != 2:
---> 79     raise KeyError(key)
     81 ref_name, var_name = split_key

KeyError: 'point_layer1'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[8], line 13
      9 )
     10
     11 mb.mask.plot(x="x", y="y");
     12
---> 13 points = mb._ds["point_layer1"].data
     14 flat_ax = np.prod(points.shape[:-1])
     15 points = np.reshape(points, (flat_ax, points.shape[-1]))
     16 pt1 = points

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/xarray/core/dataset.py:1368, in Dataset.__getitem__(self, key)
   1364
   1365                 # If someone attempts `ds['foo' , 'bar']` instead of `ds[['foo', 'bar']]`
   1366                 if isinstance(key, tuple):
   1367                     message += f"\nHint: use a list to select multiple variables, for example `ds[{list(key)}]`"
-> 1368                 raise KeyError(message) from e
   1369
   1370         if utils.iterable_of_hashable(key):
   1371             return self._copy_listed(key)

KeyError: "No variable named 'point_layer1'. Did you mean one of ('point_layer0',)?"
../../_images/notebooks_motion_list_motionlist_11_1.png

Let’s say we have a probe on the West port and want to take two grids of data where the first grid has a low density of points over a large area and the seconde gride has a high density of points over small area. This is easily accomplished by just defining the two grids as separate layers. The order in with the points will be taken depends on the order in which the layers are specified. In this example, the low density grid is completely measured first and then followed by the high density grid.

[9]:
mb = MotionBuilder(
    space="lapd_xy",
    exclusions=[
        {"type": "lapd_xy", "port_location": "W", "cone_full_angle": 60},
    ],
    layers=[
        {"type": "grid", "limits": [[-30, 30], [-20, 20]], "steps": [21, 11]},
        {"type": "grid", "limits": [[-20, 0], [-10, 10]], "steps": [15, 11]},
    ],
)

mb.mask.plot(x="x", y="y");

for player in mb.layers:
    points = player.points.data
    flat_ax = np.prod(points.shape[:-1])
    points = np.reshape(points, (flat_ax, points.shape[-1]))

    plt.scatter(
        points[...,0],
        points[..., 1], s=4**2,
        color="red",
    )

valid_points = mb.motion_list

plt.scatter(
    valid_points[..., 0],
    valid_points[..., 1],
    linewidth=1,
    s=6**2,
    facecolors="deepskyblue",
    edgecolors="black",
);
../../_images/notebooks_motion_list_motionlist_13_0.png
[10]:
mb.config
[10]:
{'space': {0: {'label': 'x', 'range': [-55.0, 55.0], 'num': 221},
  1: {'label': 'y', 'range': [-55.0, 55.0], 'num': 221}},
 'layer_to_motionlist_scheme': 'sequential',
 'exclusion': {0: {'diameter': 100,
   'port_location': 180,
   'type': 'lapd_xy',
   'cone_full_angle': 60,
   'include_cone': True,
   'pivot_radius': 58.771}},
 'layer': {0: {'npoints': [21, 11],
   'type': 'grid',
   'limits': [[-30.0, 30.0], [-20.0, 20.0]]},
  1: {'npoints': [15, 11],
   'type': 'grid',
   'limits': [[-20.0, 0.0], [-10.0, 10.0]]}}}
[11]:
import tomli_w
from pprint import pprint
[12]:
cstr = tomli_w.dumps(mb.config, multiline_strings=False)
pprint(cstr)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[12], line 1
----> 1 cstr = tomli_w.dumps(mb.config, multiline_strings=False)
      2 pprint(cstr)

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/tomli_w/_writer.py:60, in dumps(obj, multiline_strings, indent)
     56 def dumps(
     57     obj: Mapping[str, Any], /, *, multiline_strings: bool = False, indent: int = 4
     58 ) -> str:
     59     ctx = Context(multiline_strings, indent)
---> 60     return "".join(gen_table_chunks(obj, ctx, name=""))

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/tomli_w/_writer.py:97, in gen_table_chunks(table, ctx, name, inside_aot)
     95 key_part = format_key_part(k)
     96 display_name = f"{name}.{key_part}" if name else key_part
---> 97 yield from gen_table_chunks(v, ctx, name=display_name, inside_aot=in_aot)

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/tomli_w/_writer.py:95, in gen_table_chunks(table, ctx, name, inside_aot)
     93 else:
     94     yielded = True
---> 95 key_part = format_key_part(k)
     96 display_name = f"{name}.{key_part}" if name else key_part
     97 yield from gen_table_chunks(v, ctx, name=display_name, inside_aot=in_aot)

File ~/checkouts/readthedocs.org/user_builds/bapsf-motion/envs/latest/lib/python3.13/site-packages/tomli_w/_writer.py:175, in format_key_part(part)
    173     only_bare_key_chars = BARE_KEY_CHARS.issuperset(part)
    174 except TypeError:
--> 175     raise TypeError(
    176         f"Invalid mapping key '{part}' of type '{type(part).__qualname__}'."
    177         " A string is required."
    178     ) from None
    180 if part and only_bare_key_chars:
    181     return part

TypeError: Invalid mapping key '0' of type 'int'. A string is required.
[13]:
for key, val in mb.config["exclusion"]["0"].items():
    print(key, val, type(val))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[13], line 1
----> 1 for key, val in mb.config["exclusion"]["0"].items():
      2     print(key, val, type(val))

KeyError: '0'
[14]:
isinstance(mb.config["exclusion"]["0"]["pivot_radius"], np.generic)

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[14], line 1
----> 1 isinstance(mb.config["exclusion"]["0"]["pivot_radius"], np.generic)

KeyError: '0'
[ ]: