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',)?"
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',)?"
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",
);
[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'
[ ]: