diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a94337e79..aa1271be5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -46,6 +46,8 @@ changes - Reshape `SpatialData` coordinates to ``(-1, 3)`` before exporting with ``meshio`` for compatibility. [:pull:`723`] +- `SpatialData`, `LocalCoordinateSystem` and `CoordinateSystemManager` now require units [:pull:`731`] + fixes ===== diff --git a/tutorials/01_01_introduction.ipynb b/tutorials/01_01_introduction.ipynb index c38c02684..f961ec11c 100644 --- a/tutorials/01_01_introduction.ipynb +++ b/tutorials/01_01_introduction.ipynb @@ -192,7 +192,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/tutorials/01_03_geometry.ipynb b/tutorials/01_03_geometry.ipynb index 69bbf8f48..6e20b5375 100644 --- a/tutorials/01_03_geometry.ipynb +++ b/tutorials/01_03_geometry.ipynb @@ -391,7 +391,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.9" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/tutorials/transformations_01_coordinate_systems.ipynb b/tutorials/transformations_01_coordinate_systems.ipynb index 6de458560..f860a3dc0 100644 --- a/tutorials/transformations_01_coordinate_systems.ipynb +++ b/tutorials/transformations_01_coordinate_systems.ipynb @@ -44,7 +44,8 @@ "from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import\n", "\n", "import weldx.transformations as tf\n", - "import weldx.visualization as vs" + "import weldx.visualization as vs\n", + "from weldx import Q_" ] }, { @@ -83,11 +84,13 @@ "outputs": [], "source": [ "# create a translated coordinate system\n", - "lcs_01 = tf.LocalCoordinateSystem(coordinates=[2, 4, -1])\n", + "lcs_01 = tf.LocalCoordinateSystem(coordinates=Q_([2, 4, -1], \"mm\"))\n", "\n", "# create a rotated coordinate system using a rotation matrix as basis\n", "rotation_matrix = tf.WXRotation.from_euler(\"z\", np.pi / 3).as_matrix()\n", - "lcs_02 = tf.LocalCoordinateSystem(orientation=rotation_matrix, coordinates=[0, 0, 3])" + "lcs_02 = tf.LocalCoordinateSystem(\n", + " orientation=rotation_matrix, coordinates=Q_([0, 0, 3], \"mm\")\n", + ")" ] }, { @@ -132,11 +135,13 @@ "e_y = [-2, 1, 0]\n", "e_z = [0, 0, 5]\n", "lcs_03 = tf.LocalCoordinateSystem.from_axis_vectors(\n", - " x=e_x, y=e_y, z=e_z, coordinates=[1, 1, 0]\n", + " x=e_x, y=e_y, z=e_z, coordinates=Q_([1, 1, 0], \"mm\")\n", ")\n", "\n", "# create a negatively oriented coordinate system with 2 vectors\n", - "lcs_04 = tf.LocalCoordinateSystem.from_axis_vectors(y=e_y, z=e_z, coordinates=[1, 1, 2])" + "lcs_04 = tf.LocalCoordinateSystem.from_axis_vectors(\n", + " y=e_y, z=e_z, coordinates=Q_([1, 1, 2], \"mm\")\n", + ")" ] }, { @@ -178,10 +183,10 @@ "source": [ "# create a coordinate system by a 90° rotation around the x axis and subsequent 45° rotation around the y axis\n", "lcs_05 = tf.LocalCoordinateSystem.from_euler(\n", - " sequence=\"x\", angles=90, degrees=True, coordinates=[1, -1, 0]\n", + " sequence=\"x\", angles=90, degrees=True, coordinates=Q_([1, -1, 0], \"mm\")\n", ")\n", "lcs_06 = tf.LocalCoordinateSystem.from_euler(\n", - " sequence=\"xy\", angles=[90, 45], degrees=True, coordinates=[2, -2, 0]\n", + " sequence=\"xy\", angles=[90, 45], degrees=True, coordinates=Q_([2, -2, 0], \"mm\")\n", ")" ] }, @@ -340,7 +345,7 @@ " rot_z = tf.WXRotation.from_euler(\"z\", rot_angles[2]).as_matrix()\n", "\n", " orientation = np.matmul(rot_z, np.matmul(rot_y, rot_x))\n", - " location = [t_x, t_y, t_z]\n", + " location = Q_([t_x, t_y, t_z], \"mm\")\n", " return [orientation, location]\n", "\n", "\n", @@ -582,7 +587,7 @@ "outputs": [], "source": [ "lcs_child_in_parent = tf.LocalCoordinateSystem.from_euler(\n", - " sequence=\"xy\", angles=[90, 45], degrees=True, coordinates=[2, 3, 0]\n", + " sequence=\"xy\", angles=[90, 45], degrees=True, coordinates=Q_([2, 3, 0], \"mm\")\n", ")\n", "lcs_parent_in_child = lcs_child_in_parent.invert()" ] @@ -691,7 +696,7 @@ "time = [\"2010-02-01\", \"2010-02-02\"]\n", "\n", "orientation_mov = [[0, -1, 0], [1, 0, 0], [0, 0, 1]]\n", - "coordinates_mov = [[-3, 0, 0], [0, 0, 2]]\n", + "coordinates_mov = Q_([[-3, 0, 0], [0, 0, 2]], \"mm\")\n", "lcs_mov_in_ref = tf.LocalCoordinateSystem(\n", " orientation=orientation_mov, coordinates=coordinates_mov, time=time\n", ")\n", @@ -700,12 +705,12 @@ " [[1, 0, 0], [0, 1, 0], [0, 0, 1]],\n", " [[0, -1, 0], [1, 0, 0], [0, 0, 1]],\n", "]\n", - "coordinates_rot = [1, 0, 2]\n", + "coordinates_rot = Q_([1, 0, 2], \"mm\")\n", "lcs_rot_in_ref = tf.LocalCoordinateSystem(\n", " orientation=orientation_rot, coordinates=coordinates_rot, time=time\n", ")\n", "\n", - "coordinates_movrot = [[0, 3, 0], [-2, 3, 2]]\n", + "coordinates_movrot = Q_([[0, 3, 0], [-2, 3, 2]], \"mm\")\n", "orientation_movrot = [\n", " [[1, 0, 0], [0, 1, 0], [0, 0, 1]],\n", " [[0, -1, 0], [1, 0, 0], [0, 0, 1]],\n", @@ -768,7 +773,7 @@ "# original coordinate system\n", "time = [\"2010-02-02\", \"2010-02-07\"]\n", "\n", - "coordinates_tdp = [[0, 3, 0], [-2, 3, 2]]\n", + "coordinates_tdp = Q_([[0, 3, 0], [-2, 3, 2]], \"mm\")\n", "orientation_tdp = [\n", " [[1, 0, 0], [0, 1, 0], [0, 0, 1]],\n", " [[0, -1, 0], [1, 0, 0], [0, 0, 1]],\n", @@ -867,13 +872,16 @@ "outputs": [], "source": [ "lcs_target_in_ref = tf.LocalCoordinateSystem.from_euler(\n", - " sequence=\"zy\", angles=[90, 45], degrees=True, coordinates=[2, -2, 0]\n", + " sequence=\"zy\", angles=[90, 45], degrees=True, coordinates=Q_([2, -2, 0], \"mm\")\n", ")\n", "lcs_ref_in_target = lcs_target_in_ref.invert()\n", "\n", - "points_in_ref = np.array(\n", - " [[-1, 1, 0], [1, 1, 0], [1, -1, 0], [-1, -1, 0], [-1, 1, 0]], dtype=float\n", - ").transpose()\n", + "points_in_ref = Q_(\n", + " np.array(\n", + " [[-1, 1, 0], [1, 1, 0], [1, -1, 0], [-1, -1, 0], [-1, 1, 0]], dtype=float\n", + " ).transpose(),\n", + " \"mm\",\n", + ")\n", "\n", "# Transform points to target system\n", "points_in_target = (\n", @@ -908,7 +916,7 @@ " limits=(-3, 3),\n", " title=\"Data in original system\",\n", ")\n", - "ax_0_trans.plot(points_in_ref[0], points_in_ref[1], points_in_ref[2])\n", + "ax_0_trans.plot(points_in_ref[0].m, points_in_ref[1].m, points_in_ref[2].m)\n", "\n", "\n", "# second plot\n", @@ -921,7 +929,7 @@ " limits=(-3, 3),\n", " title=\"Data in original system\",\n", ")\n", - "ax_1_trans.plot(points_in_target[0], points_in_target[1], points_in_target[2])" + "ax_1_trans.plot(points_in_target[0].m, points_in_target[1].m, points_in_target[2].m)" ] }, { @@ -946,7 +954,7 @@ "outputs": [], "source": [ "time = pd.TimedeltaIndex([0, 5], \"D\")\n", - "coordinates_tdp = [[0, 3, 0], [-2, 3, 2]]\n", + "coordinates_tdp = Q_([[0, 3, 0], [-2, 3, 2]], \"mm\")\n", "orientation_tdp = [\n", " [[1, 0, 0], [0, 1, 0], [0, 0, 1]],\n", " [[0, -1, 0], [1, 0, 0], [0, 0, 1]],\n", @@ -1020,7 +1028,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.4" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/tutorials/transformations_02_coordinate_system_manager.ipynb b/tutorials/transformations_02_coordinate_system_manager.ipynb index 1e0419783..a71e7c6c5 100644 --- a/tutorials/transformations_02_coordinate_system_manager.ipynb +++ b/tutorials/transformations_02_coordinate_system_manager.ipynb @@ -38,7 +38,8 @@ "from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import\n", "\n", "import weldx.transformations as tf\n", - "import weldx.visualization as vs" + "import weldx.visualization as vs\n", + "from weldx import Q_" ] }, { @@ -80,12 +81,17 @@ "metadata": {}, "outputs": [], "source": [ - "lcs_specimen_in_root = tf.LocalCoordinateSystem(coordinates=[0, 1, -2])\n", + "lcs_specimen_in_root = tf.LocalCoordinateSystem(coordinates=Q_([0, 1, -2], \"mm\"))\n", "\n", "csm.create_cs_from_euler(\n", - " \"flange\", \"root\", sequence=\"x\", angles=20, degrees=True, coordinates=[-1, -1, 2]\n", + " \"flange\",\n", + " \"root\",\n", + " sequence=\"x\",\n", + " angles=20,\n", + " degrees=True,\n", + " coordinates=Q_([-1, -1, 2], \"mm\"),\n", ")\n", - "csm.create_cs(\"torch\", \"flange\", coordinates=[0, 0, -1])\n", + "csm.create_cs(\"torch\", \"flange\", coordinates=Q_([0, 0, -1], \"mm\"))\n", "csm.add_cs(\"specimen\", \"root\", lcs_specimen_in_root)" ] }, @@ -265,7 +271,9 @@ "metadata": {}, "outputs": [], "source": [ - "csm.add_cs(\"torch\", \"flange\", tf.LocalCoordinateSystem(coordinates=[0, 0, -2]))" + "csm.add_cs(\n", + " \"torch\", \"flange\", tf.LocalCoordinateSystem(coordinates=Q_([0, 0, -2], \"mm\"))\n", + ")" ] }, { @@ -381,24 +389,27 @@ "outputs": [], "source": [ "sensor_positions_in_root = xr.DataArray(\n", - " [[0, -1, 0], [0, -2, 0]], dims=[\"n\", \"c\"], coords={\"c\": [\"x\", \"y\", \"z\"]}\n", + " Q_([[0, -1, 0], [0, -2, 0]], \"mm\"), dims=[\"n\", \"c\"], coords={\"c\": [\"x\", \"y\", \"z\"]}\n", ")\n", "\n", "specimen_geometry_in_specimen = xr.DataArray(\n", - " [\n", - " [0, 1, 0],\n", - " [1, 1, 0],\n", - " [2, 1, 0],\n", - " [0, 2, 0],\n", - " [1, 2, 0],\n", - " [2, 2, 0],\n", - " [0, 3, 0],\n", - " [1, 3, 0],\n", - " [2, 3, 0],\n", - " [0, 4, 0],\n", - " [1, 4, 0],\n", - " [2, 4, 0],\n", - " ],\n", + " Q_(\n", + " [\n", + " [0, 1, 0],\n", + " [1, 1, 0],\n", + " [2, 1, 0],\n", + " [0, 2, 0],\n", + " [1, 2, 0],\n", + " [2, 2, 0],\n", + " [0, 3, 0],\n", + " [1, 3, 0],\n", + " [2, 3, 0],\n", + " [0, 4, 0],\n", + " [1, 4, 0],\n", + " [2, 4, 0],\n", + " ],\n", + " \"mm\",\n", + " ),\n", " dims=[\"n\", \"c\"],\n", " coords={\"c\": [\"x\", \"y\", \"z\"]},\n", ")\n", @@ -506,16 +517,16 @@ "outputs": [], "source": [ "parent_time = pd.TimedeltaIndex([1, 6], \"D\")\n", - "parent_coords = [[1, -1, 0], [0, -1, 0]]\n", + "parent_coords = Q_([[1, -1, 0], [0, -1, 0]], \"mm\")\n", "parent_orientation = -tf.WXRotation.from_euler(\"z\", [0, np.pi / 2]).as_matrix()\n", "\n", "child_time = pd.TimedeltaIndex([2, 5], \"D\")\n", - "child_coords = [0, 2, 0]\n", + "child_coords = Q_([0, 2, 0], \"mm\")\n", "child_orientation = tf.WXRotation.from_euler(\"z\", [0, np.pi / 2]).as_matrix()\n", "\n", "\n", "childchild_time = pd.TimedeltaIndex([3, 4], \"D\")\n", - "childchild_coords = [2, 0, 0]\n", + "childchild_coords = Q_([2, 0, 0], \"mm\")\n", "childchild_orientation = tf.WXRotation.from_euler(\"z\", [0, np.pi / 2]).as_matrix()\n", "\n", "\n", @@ -689,9 +700,9 @@ "outputs": [], "source": [ "csm_robot = tf.CoordinateSystemManager(\"robot head\", \"robot coordinate systems\")\n", - "csm_robot.create_cs(\"torch\", \"robot head\", coordinates=[0, 0, -2])\n", - "csm_robot.create_cs(\"mount point 1\", \"robot head\", coordinates=[0, 1, -1])\n", - "csm_robot.create_cs(\"mount point 2\", \"robot head\", coordinates=[0, -1, -1])\n", + "csm_robot.create_cs(\"torch\", \"robot head\", coordinates=Q_([0, 0, -2], \"mm\"))\n", + "csm_robot.create_cs(\"mount point 1\", \"robot head\", coordinates=Q_([0, 1, -1], \"mm\"))\n", + "csm_robot.create_cs(\"mount point 2\", \"robot head\", coordinates=Q_([0, -1, -1], \"mm\"))\n", "csm_robot" ] }, @@ -709,7 +720,7 @@ "outputs": [], "source": [ "csm_scanner = tf.CoordinateSystemManager(\"scanner\", \"scanner coordinate systems\")\n", - "csm_scanner.create_cs(\"mount point 1\", \"scanner\", coordinates=[0, 0, 2])\n", + "csm_scanner.create_cs(\"mount point 1\", \"scanner\", coordinates=Q_([0, 0, 2], \"mm\"))\n", "csm_scanner" ] }, @@ -746,8 +757,8 @@ "outputs": [], "source": [ "csm_specimen = tf.CoordinateSystemManager(\"specimen\", \"specimen coordinate systems\")\n", - "csm_specimen.create_cs(\"thermocouple 1\", \"specimen\", coordinates=[1, 1, 0])\n", - "csm_specimen.create_cs(\"thermocouple 2\", \"specimen\", coordinates=[1, 4, 0])\n", + "csm_specimen.create_cs(\"thermocouple 1\", \"specimen\", coordinates=Q_([1, 1, 0], \"mm\"))\n", + "csm_specimen.create_cs(\"thermocouple 2\", \"specimen\", coordinates=Q_([1, 4, 0], \"mm\"))\n", "csm_specimen" ] }, @@ -758,8 +769,8 @@ "outputs": [], "source": [ "csm_global = tf.CoordinateSystemManager(\"root\", \"global coordinate systems\")\n", - "csm_global.create_cs(\"specimen\", \"root\", coordinates=[1, 2, 3])\n", - "csm_global.create_cs(\"robot head\", \"root\", coordinates=[4, 5, 6])\n", + "csm_global.create_cs(\"specimen\", \"root\", coordinates=Q_([1, 2, 3], \"mm\"))\n", + "csm_global.create_cs(\"robot head\", \"root\", coordinates=Q_([4, 5, 6], \"mm\"))\n", "csm_global" ] }, @@ -907,7 +918,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/weldx/asdf/cli/welding_schema.py b/weldx/asdf/cli/welding_schema.py index 4b65aad82..a48f47eda 100644 --- a/weldx/asdf/cli/welding_schema.py +++ b/weldx/asdf/cli/welding_schema.py @@ -98,7 +98,7 @@ def single_pass_weld_example( rot = WXRotation.from_euler(seq="x", angles=180, degrees=True) - coords = [tcp_start_point.magnitude, tcp_end_point.magnitude] + coords = Q_([tcp_start_point.magnitude, tcp_end_point.magnitude], "mm") tcp_wire = lcs(coordinates=coords, orientation=rot, time=[t_start, t_end]) @@ -109,7 +109,7 @@ def single_pass_weld_example( lcs=tcp_wire, ) - tcp_contact = lcs(coordinates=[0, 0, -10]) + tcp_contact = lcs(coordinates=Q_([0, 0, -10], "mm")) # add the workpiece coordinate system csm.add_cs( diff --git a/weldx/geometry.py b/weldx/geometry.py index 759e54786..989a42bc4 100644 --- a/weldx/geometry.py +++ b/weldx/geometry.py @@ -2591,7 +2591,7 @@ def spatial_data( # `from_geometry_raster`. if isinstance(self._profile, VariableProfile): rasterization = self.rasterize(profile_raster_width, trace_raster_width) - return SpatialData(np.swapaxes(rasterization.m, 0, 1)) + return SpatialData(np.swapaxes(rasterization, 0, 1)) rasterization = self.rasterize( profile_raster_width, trace_raster_width, stack=False @@ -2633,6 +2633,7 @@ def to_file( trace_raster_width=trace_raster_width, stack=False, ) + raster_data = raster_data * _DEFAULT_LEN_UNIT SpatialData.from_geometry_raster(raster_data, True).to_file(file_name) @@ -2668,6 +2669,15 @@ def __post_init__(self, time): # make sure we have correct dimension order self.coordinates = self.coordinates.transpose(..., "n", "c") + if not isinstance(self.coordinates.data, pint.Quantity): + raise TypeError("Coordinates need to be quantities") + if not self.coordinates.data.u.is_compatible_with(_DEFAULT_LEN_UNIT): + raise pint.DimensionalityError( + self.coordinates.units, + _DEFAULT_LEN_UNIT, + extra_msg="\nThe coordinates units must represent a length.", + ) + if self.triangles is not None: if not isinstance(self.triangles, np.ndarray): self.triangles = np.array(self.triangles, dtype="uint") @@ -2679,13 +2689,15 @@ def __post_init__(self, time): raise ValueError("SpatialData triangulation must be a 2d array") @staticmethod - def from_file(file_name: Union[str, Path]) -> SpatialData: + def from_file(file_name: Union[str, Path], units: str = "mm") -> SpatialData: """Create an instance from a file. Parameters ---------- file_name : Name of the source file. + units : + Length unit assigned to data. Returns ------- @@ -2696,7 +2708,7 @@ def from_file(file_name: Union[str, Path]) -> SpatialData: mesh = meshio.read(file_name) triangles = mesh.cells_dict.get("triangle") - return SpatialData(mesh.points, triangles) + return SpatialData(Q_(mesh.points, units), triangles) @staticmethod def _shape_raster_points( @@ -2923,7 +2935,7 @@ def plot( show_wireframe=show_wireframe, ) - def to_file(self, file_name: Union[str, Path]): + def to_file(self, file_name: Union[str, Path], units: str = "mm"): """Write spatial data into a file. The extension prescribes the output format. @@ -2932,10 +2944,12 @@ def to_file(self, file_name: Union[str, Path]): ---------- file_name : Name of the file + units : + Conversion target for length unit before export. """ mesh = meshio.Mesh( - points=self.coordinates.data.reshape(-1, 3), + points=self.coordinates.data.to(units).m.reshape(-1, 3), cells={"triangle": self.triangles}, ) mesh.write(file_name) diff --git a/weldx/manifests/weldx-0.1.1.yaml b/weldx/manifests/weldx-0.1.1.yaml index 4a85a1735..6dce83326 100644 --- a/weldx/manifests/weldx-0.1.1.yaml +++ b/weldx/manifests/weldx-0.1.1.yaml @@ -61,6 +61,8 @@ tags: schema_uri: asdf://weldx.bam.de/weldx/schemas/core/transformations/coordinate_system_hierarchy-0.1.0 - tag_uri: asdf://weldx.bam.de/weldx/tags/core/transformations/local_coordinate_system-0.1.0 schema_uri: asdf://weldx.bam.de/weldx/schemas/core/transformations/local_coordinate_system-0.1.0 +- tag_uri: asdf://weldx.bam.de/weldx/tags/core/transformations/local_coordinate_system-0.1.1 + schema_uri: asdf://weldx.bam.de/weldx/schemas/core/transformations/local_coordinate_system-0.1.1 - tag_uri: asdf://weldx.bam.de/weldx/tags/core/transformations/rotation-0.1.0 schema_uri: asdf://weldx.bam.de/weldx/schemas/core/transformations/rotation-0.1.0 - tag_uri: asdf://weldx.bam.de/weldx/tags/core/variable-0.1.0 diff --git a/weldx/schemas/weldx.bam.de/weldx/core/geometry/spatial_data-0.1.1.yaml b/weldx/schemas/weldx.bam.de/weldx/core/geometry/spatial_data-0.1.1.yaml index 13f0fdf4b..1c549ecaf 100644 --- a/weldx/schemas/weldx.bam.de/weldx/core/geometry/spatial_data-0.1.1.yaml +++ b/weldx/schemas/weldx.bam.de/weldx/core/geometry/spatial_data-0.1.1.yaml @@ -14,18 +14,20 @@ examples: - A simple `SpatialData` with triangulation - | ! - coordinates: !core/ndarray-1.0.0 - data: - - [0.0, 0.0, 0.0] - - [1.0, 0.0, 0.0] - - [1.0, 1.0, 0.0] - - [0.0, 1.0, 0.0] - datatype: float64 - shape: [4, 3] + coordinates: ! + value: !core/ndarray-1.0.0 + data: + - [0.0, 0.0, 0.0] + - [1.0, 0.0, 0.0] + - [1.0, 1.0, 0.0] + - [0.0, 1.0, 0.0] + datatype: float64 + shape: [4, 3] + units: ! millimeter triangles: !core/ndarray-1.0.0 data: - - [0, 1, 2] - - [2, 3, 0] + - [0, 1, 0] + - [1, 0, 0] datatype: uint32 shape: [2, 3] - @@ -47,6 +49,7 @@ examples: name: data dimensions: [p, n, c] dtype: millimeter data: !core/ndarray-1.0.0 data: - - [0.0, 0.0, 0.0] @@ -55,6 +58,7 @@ examples: - [0.0, 1.0, 1.0] datatype: float64 shape: [2, 2, 3] + units: ! inch type: object properties: @@ -62,14 +66,12 @@ properties: description: | The coordinates of the data points. oneOf: - - tag: "tag:stsci.edu:asdf/core/ndarray-1.*" - properties: - datatype: - type: string - enum: [float32, float64] - wx_shape: [(~), 3] - tag: "asdf://weldx.bam.de/weldx/tags/core/data_array-0.1.*" wx_shape: [..., 3] + wx_unit: "m" + - tag: "asdf://weldx.bam.de/weldx/tags/units/quantity-0.1.*" + wx_shape: [(~), 3] + wx_unit: "m" attributes: description: | An arbitrary set of attributes. For example, normals, colors or measured point data can be stored using this diff --git a/weldx/schemas/weldx.bam.de/weldx/core/transformations/local_coordinate_system-0.1.1.yaml b/weldx/schemas/weldx.bam.de/weldx/core/transformations/local_coordinate_system-0.1.1.yaml new file mode 100644 index 000000000..9f77ca9f0 --- /dev/null +++ b/weldx/schemas/weldx.bam.de/weldx/core/transformations/local_coordinate_system-0.1.1.yaml @@ -0,0 +1,124 @@ +%YAML 1.1 +--- +$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" +id: "asdf://weldx.bam.de/weldx/schemas/core/transformations/local_coordinate_system-0.1.1" + +title: | + Schema that describes a local coordinate system +description: | + A local coordinate system is described by time-dependent coordinates and orientations towards a reference system. + +examples: + - + - A constant transformation describing a translation of 1 mm into the z direction + - | + ! + coordinates: ! + name: coordinates + dimensions: [c] + dtype: millimeter + data: !core/ndarray-1.0.0 + data: [0.0, 0.0, 1.0] + datatype: float64 + shape: [3] + - + - | + A time dependent transformation describing a linear movement from 5 mm to 295 mm + over 20 seconds along the x-axis direction. + - | + ! + time: ! + values: !core/ndarray-1.0.0 + data: [0, 20000000000] + datatype: int64 + shape: [2] + start: ! P0DT0H0M0S + end: ! P0DT0H0M20S + min: ! P0DT0H0M0S + max: ! P0DT0H0M20S + coordinates: ! + name: coordinates + dimensions: [time, c] + dtype: millimeter + data: !core/ndarray-1.0.0 + data: + - [5.0, 0.0, 0.0] + - [295.0, 0.0, 0.0] + datatype: float64 + shape: [2, 3] + - + - A static transformation describing an euler rotation of 45 degree around the x-axis. + - | + ! + orientations: ! + name: orientations + dimensions: [c, v] + dtype: + time: ! + values: !core/ndarray-1.0.0 + data: [0, 20000000000] + datatype: int64 + shape: [2] + start: ! P0DT0H0M0S + end: ! P0DT0H0M20S + min: ! P0DT0H0M0S + max: ! P0DT0H0M20S + orientations: ! + name: orientations + dimensions: [time, c, v] + dtype: dict: """Serialize into tree.""" tree = copy(obj.__dict__) # shallow copy so we dont change original object if tree["coordinates"].ndim <= 2: - tree["coordinates"] = tree["coordinates"].values + tree["coordinates"] = tree["coordinates"].data return tree def from_yaml_tree(self, node: dict, tag: str, ctx) -> SpatialData: """Reconstruct from yaml node.""" + from weldx.constants import Q_ + + if tag == "asdf://weldx.bam.de/weldx/tags/core/geometry/spatial_data-0.1.0": + node["coordinates"] = Q_(node["coordinates"], "mm") # legacy return SpatialData(**node) def select_tag(self, obj, tags, ctx) -> str: diff --git a/weldx/tags/core/transformations/local_coordinate_system.py b/weldx/tags/core/transformations/local_coordinate_system.py index 2d957d40e..2588adf82 100644 --- a/weldx/tags/core/transformations/local_coordinate_system.py +++ b/weldx/tags/core/transformations/local_coordinate_system.py @@ -1,5 +1,5 @@ -from weldx.asdf.constants import WELDX_TAG_URI_BASE from weldx.asdf.types import WeldxConverter +from weldx.constants import _DEFAULT_LEN_UNIT, Q_ from weldx.core import TimeSeries from weldx.tags.core.common_types import Variable from weldx.transformations import LocalCoordinateSystem @@ -8,7 +8,10 @@ class LocalCoordinateSystemConverter(WeldxConverter): """Serialization class for weldx.transformations.LocalCoordinateSystem""" - tags = [WELDX_TAG_URI_BASE + "core/transformations/local_coordinate_system-0.1.*"] + tags = [ + "asdf://weldx.bam.de/weldx/tags/" + "core/transformations/local_coordinate_system-0.1.*" + ] types = [LocalCoordinateSystem] def to_yaml_tree(self, obj: LocalCoordinateSystem, tag: str, ctx) -> dict: @@ -58,6 +61,12 @@ def from_yaml_tree(self, node: dict, tag: str, ctx): if coordinates is not None and not isinstance(coordinates, TimeSeries): coordinates = node["coordinates"].data + if ( + tag == "asdf://weldx.bam.de/weldx/tags/core/transformations" + "/local_coordinate_system-0.1.0" + ): # legacy + coordinates = Q_(coordinates, _DEFAULT_LEN_UNIT) + return LocalCoordinateSystem( orientation=orientations, coordinates=coordinates, diff --git a/weldx/tests/_helpers.py b/weldx/tests/_helpers.py index ae3285546..7ae91586d 100644 --- a/weldx/tests/_helpers.py +++ b/weldx/tests/_helpers.py @@ -3,6 +3,7 @@ from typing import Any import numpy as np +import pint from weldx.constants import Q_ from weldx.transformations import LocalCoordinateSystem, WXRotation @@ -110,8 +111,10 @@ def matrix_is_close(mat_a, mat_b, abs_tol=1e-9) -> bool: True or False """ - mat_a = np.array(mat_a, dtype=float) - mat_b = np.array(mat_b, dtype=float) + if not isinstance(mat_a, pint.Quantity): + mat_a = np.array(mat_a, dtype=float) + if not isinstance(mat_b, pint.Quantity): + mat_b = np.array(mat_b, dtype=float) if mat_a.shape != mat_b.shape: return False diff --git a/weldx/tests/asdf_tests/test_asdf_core.py b/weldx/tests/asdf_tests/test_asdf_core.py index dc0dc7ffa..7e3de048a 100644 --- a/weldx/tests/asdf_tests/test_asdf_core.py +++ b/weldx/tests/asdf_tests/test_asdf_core.py @@ -250,7 +250,7 @@ def test_local_coordinate_system_coords_timeseries( ): """Test reading and writing a LCS with a `TimeSeries` as coordinates to asdf.""" # create inputs to lcs __init__ - me = ME("a*t", dict(a=Q_([[1, 0, 0]], "1/s"))) + me = ME("a*t", dict(a=Q_([[1, 0, 0]], "m/s"))) ts = TimeSeries(data=me) ref_time = None @@ -284,7 +284,7 @@ def test_local_coordinate_system_shape_violation(): coords={"u": ["x", "y", "z"], "v": [0, 1, 2]}, ) coordinates = xr.DataArray( - data=[1, 2], + data=Q_([1, 2], "mm"), dims=["c"], coords={"c": ["x", "y"]}, ) @@ -302,7 +302,7 @@ def test_local_coordinate_system_shape_violation(): coords={"c": ["x", "y"], "v": [0, 1]}, ) coordinates = xr.DataArray( - data=[1, 2, 3], + data=Q_([1, 2, 3], "mm"), dims=["u"], coords={"u": ["x", "y", "z"]}, ) @@ -320,18 +320,18 @@ def test_local_coordinate_system_shape_violation(): def get_example_coordinate_system_manager(): """Get a consistent CoordinateSystemManager instance for test purposes.""" csm = tf.CoordinateSystemManager("root") - csm.create_cs("lcs_01", "root", coordinates=[1, 2, 3]) + csm.create_cs("lcs_01", "root", coordinates=Q_([1, 2, 3], "mm")) csm.create_cs( "lcs_02", "root", orientation=WXRotation.from_euler("z", np.pi / 3).as_matrix(), - coordinates=[4, -7, 8], + coordinates=Q_([4, -7, 8], "mm"), ) csm.create_cs( "lcs_03", "lcs_02", orientation=WXRotation.from_euler("y", np.pi / 11), - coordinates=[4, -7, 8], + coordinates=Q_([4, -7, 8], "mm"), ) return csm @@ -349,7 +349,9 @@ def test_coordinate_system_manager(copy_arrays, lazy_load): def get_coordinate_system_manager_with_subsystems(nested: bool): - lcs = [tf.LocalCoordinateSystem(coordinates=[i, -i, -i]) for i in range(12)] + lcs = [ + tf.LocalCoordinateSystem(coordinates=Q_([i, -i, -i], "mm")) for i in range(12) + ] # create global system csm_global = tf.CoordinateSystemManager("base", "Global System", "2000-06-08") @@ -421,12 +423,12 @@ def test_coordinate_system_manager_time_dependencies( if csm_time_ref is None: lcs_tdp_1_time_ref = pd.Timestamp("2000-03-17") lcs_tdp_1 = tf.LocalCoordinateSystem( - coordinates=[[1, 2, 3], [4, 5, 6]], + coordinates=Q_([[1, 2, 3], [4, 5, 6]], "mm"), time=pd.TimedeltaIndex([1, 2], "D"), time_ref=lcs_tdp_1_time_ref, ) lcs_tdp_2 = tf.LocalCoordinateSystem( - coordinates=[[3, 7, 3], [9, 5, 8]], + coordinates=Q_([[3, 7, 3], [9, 5, 8]], "mm"), time=pd.TimedeltaIndex([1, 2], "D"), time_ref=pd.Timestamp("2000-03-21"), ) @@ -457,19 +459,20 @@ def test_coordinate_system_manager_time_dependencies( def test_coordinate_system_manager_with_data(copy_arrays, lazy_load): """Test if data attached to a CSM is stored and read correctly.""" csm = tf.CoordinateSystemManager("root", "csm") - csm.create_cs("cs_1", "root", coordinates=[1, 1, 1]) - csm.create_cs("cs_2", "root", coordinates=[-1, -1, -1]) - csm.create_cs("cs_11", "cs_1", coordinates=[1, 1, 1]) + csm.create_cs("cs_1", "root", coordinates=Q_([1, 1, 1], "mm")) + csm.create_cs("cs_2", "root", coordinates=Q_([-1, -1, -1], "mm")) + csm.create_cs("cs_11", "cs_1", coordinates=Q_([1, 1, 1], "mm")) - data_11 = SpatialData(coordinates=np.array([[1.0, 2.0, 3.0], [3.0, 2.0, 1.0]])) + data_11 = SpatialData(coordinates=Q_([[1.0, 2.0, 3.0], [3.0, 2.0, 1.0]], "mm")) data_2 = SpatialData( - coordinates=np.array( + coordinates=Q_( [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], - ] + ], + "mm", ), triangles=np.array([[0, 1, 2], [0, 2, 3]], dtype="uint32"), ) @@ -810,6 +813,7 @@ def test_asdf_serialization(copy_arrays, lazy_load, reshape): if reshape: coordinates = np.array(coordinates).reshape((2, 2, 3)) time = ["0s", "1s"] + coordinates = Q_(coordinates, "mm") pc = SpatialData(coordinates=coordinates, triangles=triangles, time=time) tree = {"point_cloud": pc} diff --git a/weldx/tests/test_geometry.py b/weldx/tests/test_geometry.py index 2f8014dbe..030e62592 100644 --- a/weldx/tests/test_geometry.py +++ b/weldx/tests/test_geometry.py @@ -2368,7 +2368,7 @@ def test_trace_local_coordinate_system(): position_on_segment = linear_segment.length.m * weight position = radial_segment.length.m + position_on_segment - expected_coordinates = np.array([-position_on_segment, 2, 0]) + expected_coordinates = Q_([-position_on_segment, 2, 0], "mm") cs_expected = tf.LocalCoordinateSystem( orientation=expected_orientation, coordinates=expected_coordinates ) @@ -3033,6 +3033,10 @@ def test_class_creation(arguments): Tuple of arguments that are passed to the `__init__` method """ + a = list(arguments) + a[0] = Q_(a[0], "mm") + arguments = tuple(a) + pc = SpatialData(*arguments) assert isinstance(pc.coordinates, DataArray) assert np.allclose(pc.coordinates.data, arguments[0]) @@ -3064,6 +3068,10 @@ def test_class_creation_exceptions(arguments, exception_type, test_name): A string starting with an `#` that describes the test. """ + a = list(arguments) + a[0] = Q_(a[0], "mm") + arguments = tuple(a) + with pytest.raises(exception_type): SpatialData(*arguments) @@ -3103,8 +3111,11 @@ def test_comparison(kwargs_mod: dict, expected_result: bool): """ from copy import deepcopy + if "coordinates" in kwargs_mod: + kwargs_mod["coordinates"] = Q_(kwargs_mod["coordinates"], "mm") + default_kwargs = dict( - coordinates=[[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], + coordinates=Q_([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], "mm"), triangles=[[0, 1, 2], [2, 3, 0]], attributes=dict(data=[1, 2, 3]), ) @@ -3116,7 +3127,7 @@ def test_comparison(kwargs_mod: dict, expected_result: bool): assert (reference == other) == expected_result - assert np.all(reference.limits() == np.array([[0, 0, 0], [1, 1, 0]])) + assert np.all(reference.limits() == Q_([[0, 0, 0], [1, 1, 0]], "mm")) # test_read_write_file ------------------------------------------------------------- @@ -3137,7 +3148,7 @@ def test_read_write_file(filename: Union[str, Path]): Name of the file """ - points = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]] + points = Q_([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]], "mm") triangles = [[0, 1, 2], [2, 3, 0]] data = SpatialData(points, triangles) @@ -3148,22 +3159,22 @@ def test_read_write_file(filename: Union[str, Path]): data.to_file(filepath) data_read = SpatialData.from_file(filepath) - assert np.allclose(data.coordinates, data_read.coordinates) + assert np.allclose(data.coordinates.data, data_read.coordinates.data) assert np.allclose(data.triangles, data_read.triangles) @staticmethod def test_time_dependent_data(): """Simple test for assigning and transforming time dependent data.""" time = ["0s", "3s", "6s", "9s"] - data = np.array([[[0, 0, 0], [0, 1.0, np.sin(i)], [0, 2, 0]] for i in range(4)]) - transformed_x = np.repeat([0.0, 3, 6, 9], 3) + data = Q_([[[0, 0, 0], [0, 1.0, np.sin(i)], [0, 2, 0]] for i in range(4)], "mm") + transformed_x = Q_(np.repeat([0.0, 3, 6, 9], 3), "mm") sd = SpatialData(coordinates=data, time=time) csm = CoordinateSystemManager("specimen") csm.create_cs( "scanner", "specimen", - coordinates=[[0, 0, 0], [15, 0, 0]], + coordinates=Q_([[0, 0, 0], [15, 0, 0]], "mm"), time=["0s", "15s"], ) @@ -3173,6 +3184,6 @@ def test_time_dependent_data(): csm.assign_data(sd, "scan_data2", "scanner", "specimen") for data_name in ["scan_data", "scan_data2"]: - test = csm.get_data(data_name, "specimen").coordinates.values.reshape(-1, 3) + test = csm.get_data(data_name, "specimen").coordinates.data.reshape(-1, 3) assert np.all(test[:, 1:] == data.reshape(-1, 3)[:, 1:]) assert np.all(test[:, 0] == transformed_x) diff --git a/weldx/tests/test_time.py b/weldx/tests/test_time.py index 50154e6ef..d6f1010cf 100644 --- a/weldx/tests/test_time.py +++ b/weldx/tests/test_time.py @@ -244,8 +244,12 @@ def test_init( @pytest.mark.parametrize( "time_dep_type", [ - LocalCoordinateSystem(coordinates=np.zeros((2, 3)), time=["2s", "3s"]), - LocalCoordinateSystem(coordinates=np.zeros((2, 3)), time=["2000", "2001"]), + LocalCoordinateSystem( + coordinates=Q_(np.zeros((2, 3)), "mm"), time=["2s", "3s"] + ), + LocalCoordinateSystem( + coordinates=Q_(np.zeros((2, 3)), "mm"), time=["2000", "2001"] + ), TimeSeries(Q_([2, 4, 1], "m"), TDI([1, 2, 3], "s")), TimeSeries(Q_([2, 4, 1], "m"), ["2001", "2002", "2003"]), ], diff --git a/weldx/tests/test_visualization.py b/weldx/tests/test_visualization.py index e47e22efe..6fa22f08f 100644 --- a/weldx/tests/test_visualization.py +++ b/weldx/tests/test_visualization.py @@ -4,13 +4,9 @@ import pandas as pd import pytest -# pylint: disable=W0611 -from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import - import weldx.transformations as tf import weldx.visualization as vs - -# pylint: enable=W0611 +from weldx.constants import Q_ def test_plot_coordinate_system(): @@ -23,7 +19,7 @@ def test_plot_coordinate_system(): [[0, 1, 0], [-1, 0, 0], [0, 0, 1]], [[-1, 0, 0], [0, -1, 0], [0, 0, 1]], ] - coordinates_tdp = [[0, 0, 1], [0, 0, 2], [0, -1, 0]] + coordinates_tdp = Q_([[0, 0, 1], [0, 0, 2], [0, -1, 0]], "mm") lcs_tdp = tf.LocalCoordinateSystem( orientation=orientation_tdp, coordinates=coordinates_tdp, time=time ) diff --git a/weldx/tests/transformations/_util.py b/weldx/tests/transformations/_util.py index 9b5edb422..82ff6ec88 100644 --- a/weldx/tests/transformations/_util.py +++ b/weldx/tests/transformations/_util.py @@ -67,7 +67,6 @@ def check_coordinate_system( """ orientation_expected = np.array(orientation_expected) - coordinates_expected = np.array(coordinates_expected) if time is not None: assert orientation_expected.ndim == 3 or coordinates_expected.ndim == 2 @@ -78,7 +77,7 @@ def check_coordinate_system( lcs.orientation, orientation_expected, positive_orientation_expected ) - assert np.allclose(lcs.coordinates.values, coordinates_expected, atol=1e-9) + assert np.allclose(lcs.coordinates.data, coordinates_expected, atol=1e-9) def check_cs_close(lcs_0, lcs_1): diff --git a/weldx/tests/transformations/test_cs_manager.py b/weldx/tests/transformations/test_cs_manager.py index 089b6373c..ebe43120a 100644 --- a/weldx/tests/transformations/test_cs_manager.py +++ b/weldx/tests/transformations/test_cs_manager.py @@ -27,11 +27,11 @@ def csm_fix(): """Create default coordinate system fixture.""" csm_default = CSM("root") - lcs_1 = LCS(coordinates=[0, 1, 2]) - lcs_2 = LCS(coordinates=[0, -1, -2]) - lcs_3 = LCS(coordinates=[-1, -2, -3]) - lcs_4 = LCS(r_mat_y(1 / 2), [1, 2, 3]) - lcs_5 = LCS(r_mat_y(3 / 2), [2, 3, 1]) + lcs_1 = LCS(coordinates=Q_([0, 1, 2], "mm")) + lcs_2 = LCS(coordinates=Q_([0, -1, -2], "mm")) + lcs_3 = LCS(coordinates=Q_([-1, -2, -3], "mm")) + lcs_4 = LCS(r_mat_y(1 / 2), Q_([1, 2, 3], "mm")) + lcs_5 = LCS(r_mat_y(3 / 2), Q_([2, 3, 1], "mm")) csm_default.add_cs("lcs1", "root", lcs_1) csm_default.add_cs("lcs2", "root", lcs_2) csm_default.add_cs("lcs3", "lcs1", lcs_3) @@ -44,7 +44,7 @@ def csm_fix(): @pytest.fixture() def list_of_csm_and_lcs_instances(): """Get a list of LCS and CSM instances.""" - lcs = [LCS(coordinates=[i, 0, 0]) for i in range(11)] + lcs = [LCS(coordinates=Q_([i, 0, 0], "mm")) for i in range(11)] csm_0 = CSM("lcs0", "csm0") csm_0.add_cs("lcs1", "lcs0", lcs[1]) @@ -94,19 +94,24 @@ def test_init(): def test_add_cs(): """Test the 'add_cs' function.""" csm = CSM("r") - ts = TimeSeries(MathematicalExpression("a*t", dict(a=Q_("1/s")))) + ts = TimeSeries(MathematicalExpression("a*t", dict(a=Q_("m/s")))) lcs_data = [ - ("a", "r", LCS(coordinates=[0, 1, 2]), True), - ("b", "r", LCS(coordinates=[0, -1, -2]), False), - ("b", "r", LCS(coordinates=[[0, -1, -2], [8, 2, 7]], time=["1s", "2s"]), False), - ("c", "b", LCS(r_mat_y(1 / 2), [1, 2, 3]), True), - ("c", "b", LCS(coordinates=[-1, -2, -3]), True), - ("b", "c", LCS(coordinates=[-1, -2, -3]), False), - ("b", "c", LCS(coordinates=[-1, -2, -3]), True), - ("d", "b", LCS(coordinates=[0, 1, 2]), True), - ("d", "b", LCS(r_mat_y(1 / 2), [1, 2, 3]), True), - ("e", "a", LCS(r_mat_y(3 / 2), [2, 3, 1]), True), + ("a", "r", LCS(coordinates=Q_([0, 1, 2], "mm")), True), + ("b", "r", LCS(coordinates=Q_([0, -1, -2], "mm")), False), + ( + "b", + "r", + LCS(coordinates=Q_([[0, -1, -2], [8, 2, 7]], "mm"), time=["1s", "2s"]), + False, + ), + ("c", "b", LCS(r_mat_y(1 / 2), Q_([1, 2, 3], "mm")), True), + ("c", "b", LCS(coordinates=Q_([-1, -2, -3], "mm")), True), + ("b", "c", LCS(coordinates=Q_([-1, -2, -3], "mm")), False), + ("b", "c", LCS(coordinates=Q_([-1, -2, -3], "mm")), True), + ("d", "b", LCS(coordinates=Q_([0, 1, 2], "mm")), True), + ("d", "b", LCS(r_mat_y(1 / 2), Q_([1, 2, 3], "mm")), True), + ("e", "a", LCS(r_mat_y(3 / 2), Q_([2, 3, 1], "mm")), True), ("e", "a", LCS(coordinates=ts), True), ] exp_num_cs = 1 @@ -184,12 +189,12 @@ def test_add_cs_reference_time( timestamp_lcs_2 = pd.Timestamp("2000-01-03") csm = tf.CoordinateSystemManager("root", time_ref=timestamp_csm) lcs_1 = tf.LocalCoordinateSystem( - coordinates=[[1, 2, 3], [3, 2, 1]], + coordinates=Q_([[1, 2, 3], [3, 2, 1]], "mm"), time=pd.TimedeltaIndex([1, 2]), time_ref=timestamp_lcs_1, ) lcs_2 = tf.LocalCoordinateSystem( - coordinates=[[1, 5, 3], [3, 5, 1]], + coordinates=Q_([[1, 5, 3], [3, 5, 1]], "mm"), time=pd.TimedeltaIndex([0, 2]), time_ref=timestamp_lcs_2, ) @@ -209,7 +214,7 @@ def test_add_cs_reference_time( def test_add_coordinate_system_timeseries(): """Test if adding an LCS with a `TimeSeries` as coordinates is possible.""" csm = CSM("r") - me = MathematicalExpression("a*t", dict(a=Q_([[1, 0, 0]], "1/s"))) + me = MathematicalExpression("a*t", dict(a=Q_([[1, 0, 0]], "m/s"))) ts = TimeSeries(me) lcs = LCS(coordinates=ts) @@ -256,7 +261,7 @@ def test_create_cs_from_axis_vectors( angles = [[30, 45, 60], [40, 35, 80], [1, 33, 7], [90, 180, 270]] o = WXRotation.from_euler("xyz", angles, degrees=True).as_matrix() - c = [[-1, 3, 2], [4, 2, 4], [5, 1, 2], [3, 3, 3]] + c = Q_([[-1, 3, 2], [4, 2, 4], [5, 1, 2], [3, 3, 3]], "mm") if not time_dep_orient: o = o[0] @@ -560,7 +565,7 @@ def test_delete_coordinate_system_exceptions( [(0, ("cs_1", "root")), (1, ("cs_3", "cs_1")), (2, ("cs_1", "cs_2"))], [(1, 0), (2, 0)], [], - [(1, (1, ("cs_3", "cs_1", None, [1, 0, 0])))], + [(1, (1, ("cs_3", "cs_1", None, Q_([1, 0, 0], "mm"))))], [], [False, False, True], ), @@ -642,7 +647,7 @@ def test_delete_coordinate_system_exceptions( [(0, ("cs_1", "root")), (1, ("cs_3", "cs_1")), (2, ("cs_1", "cs_2"))], [(2, 1), (1, 0)], [], - [(2, (2, ("cs_1", "cs_2", None, [1, 0, 0])))], + [(2, (2, ("cs_1", "cs_2", None, Q_([1, 0, 0], "mm"))))], [], [False, False, False], ), @@ -847,9 +852,9 @@ def test_time_union( lcs = [] for i, _ in enumerate(lcs_times): if isinstance(lcs_times[i], pd.TimedeltaIndex): - coordinates = [[j, j, j] for j in range(len(lcs_times[i]))] + coordinates = Q_([[j, j, j] for j in range(len(lcs_times[i]))], "mm") else: - coordinates = [1, 2, 3] + coordinates = Q_([1, 2, 3], "mm") lcs += [ tf.LocalCoordinateSystem( None, @@ -935,11 +940,14 @@ def test_time_union_time_series_coords( lcs_ts_time = Q_([1, 2], "s") csm = CSM("base") - csm.create_cs("st", "base", coordinates=[2, 2, 2]) + csm.create_cs("st", "base", coordinates=Q_([2, 2, 2], "mm")) csm.create_cs("ts", "base", lcs_ts_orientation, ts, lcs_ts_time) if add_discrete_lcs: csm.create_cs( - "tdp", "base", coordinates=[[2, 4, 5], [2, 2, 2]], time=Q_([2, 3], "s") + "tdp", + "base", + coordinates=Q_([[2, 4, 5], [2, 2, 2]], "mm"), + time=Q_([2, 3], "s"), ) if exp_time is not None: @@ -953,13 +961,13 @@ def test_time_union_time_series_coords( @pytest.mark.parametrize( "system_name, reference_name, exp_orientation, exp_coordinates", [ - ("lcs_1", None, r_mat_z(0.5), [1, 2, 3]), - ("lcs_2", None, r_mat_y(0.5), [3, -3, 1]), - ("lcs_3", None, r_mat_x(0.5), [1, -1, 3]), - ("lcs_3", "root", [[0, 1, 0], [0, 0, -1], [-1, 0, 0]], [6, -4, 0]), - ("root", "lcs_3", [[0, 0, -1], [1, 0, 0], [0, -1, 0]], [0, -6, -4]), - ("lcs_3", "lcs_1", [[0, 0, -1], [0, -1, 0], [-1, 0, 0]], [-6, -5, -3]), - ("lcs_1", "lcs_3", [[0, 0, -1], [0, -1, 0], [-1, 0, 0]], [-3, -5, -6]), + ("lcs_1", None, r_mat_z(0.5), Q_([1, 2, 3], "m")), + ("lcs_2", None, r_mat_y(0.5), Q_([3, -3, 1], "m")), + ("lcs_3", None, r_mat_x(0.5), Q_([1, -1, 3], "m")), + ("lcs_3", "root", [[0, 1, 0], [0, 0, -1], [-1, 0, 0]], Q_([6, -4, 0], "m")), + ("root", "lcs_3", [[0, 0, -1], [1, 0, 0], [0, -1, 0]], Q_([0, -6, -4], "m")), + ("lcs_3", "lcs_1", [[0, 0, -1], [0, -1, 0], [-1, 0, 0]], Q_([-6, -5, -3], "m")), + ("lcs_1", "lcs_3", [[0, 0, -1], [0, -1, 0], [-1, 0, 0]], Q_([-3, -5, -6], "m")), ], ) def test_get_local_coordinate_system_no_time_dep( @@ -984,9 +992,9 @@ def test_get_local_coordinate_system_no_time_dep( """ # setup csm = tf.CoordinateSystemManager(root_coordinate_system_name="root") - csm.create_cs("lcs_1", "root", r_mat_z(0.5), [1, 2, 3]) - csm.create_cs("lcs_2", "root", r_mat_y(0.5), [3, -3, 1]) - csm.create_cs("lcs_3", "lcs_2", r_mat_x(0.5), [1, -1, 3]) + csm.create_cs("lcs_1", "root", r_mat_z(0.5), Q_([1, 2, 3], "m")) + csm.create_cs("lcs_2", "root", r_mat_y(0.5), Q_([3, -3, 1], "m")) + csm.create_cs("lcs_3", "lcs_2", r_mat_x(0.5), Q_([1, -1, 3], "m")) check_coordinate_system( csm.get_cs(system_name, reference_name), @@ -1360,6 +1368,9 @@ def test_get_local_coordinate_system_time_dep( error """ + if exp_coordinates is not None: + exp_coordinates = Q_(exp_coordinates, "mm") + # setup ------------------------------------------- # set reference times time_refs = [t if t is None else pd.Timestamp(t) for t in time_refs] @@ -1368,25 +1379,25 @@ def test_get_local_coordinate_system_time_dep( time_1 = TDI([0, 3, 12], "D") time_ref_1 = time_refs[1] orientation_1 = None - coordinates_1 = [[i, 0, 0] for i in [0, 0.25, 1]] + coordinates_1 = Q_([[i, 0, 0] for i in [0, 0.25, 1]], "mm") # moves in negative x-direction and rotates positively around the x-axis time_2 = TDI([0, 4, 8, 12], "D") time_ref_2 = time_refs[2] - coordinates_2 = [[-i, 0, 0] for i in [0, 1 / 3, 2 / 3, 1]] + coordinates_2 = Q_([[-i, 0, 0] for i in [0, 1 / 3, 2 / 3, 1]], "mm") orientation_2 = r_mat_x([0, 2 / 3, 4 / 3, 2]) # rotates negatively around the x-axis time_3 = TDI([0, 3, 6, 9, 12], "D") time_ref_3 = time_refs[3] - coordinates_3 = [1, 0, 0] + coordinates_3 = Q_([1, 0, 0], "mm") orientation_3 = r_mat_x([0, -0.5, -1, -1.5, -2]) # static time_4 = None time_ref_4 = None orientation_4 = None - coordinates_4 = [0, 1, 0] + coordinates_4 = Q_([0, 1, 0], "mm") csm = tf.CoordinateSystemManager("root", "CSM", time_refs[0]) csm.create_cs("cs_1", "root", orientation_1, coordinates_1, time_1, time_ref_1) @@ -1431,7 +1442,7 @@ def test_get_local_coordinate_system_time_dep( ("trl", "r", [[1, 1, 1], [-2, 1, 1], [-3, 2, 1]], [1, 2, 3], [0, 90, 90]), ], ) -@pytest.mark.parametrize("units", [None, "inch", "mm"]) +@pytest.mark.parametrize("units", ["inch", "mm"]) def test_get_local_coordinate_system_timeseries( lcs, in_lcs, exp_coords, exp_time, exp_angles, units ): @@ -1460,11 +1471,10 @@ def test_get_local_coordinate_system_timeseries( coords_1 = [0, 0, 1] coords_2 = [1, 0, 0] - if units: - translation = Q_(translation, units) - exp_coords = Q_(exp_coords, units) - coords_1 = Q_(coords_1, units) - coords_2 = Q_(coords_2, units) + translation = Q_(translation, units) + exp_coords = Q_(exp_coords, units) + coords_1 = Q_(coords_1, units) + coords_2 = Q_(coords_2, units) csm = CSM("r") csm.create_cs("rot", "r", rotation, coords_1, time=Q_([1, 2], "s")) @@ -1515,11 +1525,11 @@ def test_get_local_coordinate_system_exceptions( time_2 = TDI([4, 7], "D") csm = tf.CoordinateSystemManager(root_coordinate_system_name="root") - csm.create_cs("cs_1", "root", r_mat_z(0.5), [1, 2, 3]) - csm.create_cs("cs_2", "root", r_mat_y(0.5), [3, -3, 1]) - csm.create_cs("cs_3", "cs_2", r_mat_x(0.5), [1, -1, 3]) - csm.create_cs("cs_4", "cs_2", r_mat_x([0, 1]), [2, -1, 2], time=time_1) - csm.create_cs("cs_5", "root", r_mat_y([0, 1]), [1, -7, 3], time=time_2) + csm.create_cs("cs_1", "root", r_mat_z(0.5), Q_([1, 2, 3], "mm")) + csm.create_cs("cs_2", "root", r_mat_y(0.5), Q_([3, -3, 1], "mm")) + csm.create_cs("cs_3", "cs_2", r_mat_x(0.5), Q_([1, -1, 3], "mm")) + csm.create_cs("cs_4", "cs_2", r_mat_x([0, 1]), Q_([2, -1, 2], "mm"), time=time_1) + csm.create_cs("cs_5", "root", r_mat_y([0, 1]), Q_([1, -7, 3], "mm"), time=time_2) # test with pytest.raises(exception_type): @@ -1544,7 +1554,7 @@ def test_get_local_coordinate_system_exceptions( ("r", "s", False), ], ) -@pytest.mark.parametrize("units", [None, "inch", "mm"]) +@pytest.mark.parametrize("units", ["inch", "mm"]) def test_get_cs_exception_timeseries(lcs, in_lcs, exp_exception, units): """Test exceptions of get_cs method if 1 lcs has a `TimeSeries` as coordinates. @@ -1563,12 +1573,8 @@ def test_get_cs_exception_timeseries(lcs, in_lcs, exp_exception, units): me_units = f"{units}/s" if units else "1/s" me = MathematicalExpression("a*t", {"a": Q_([0, 1, 0], me_units)}) ts = TimeSeries(me) - translation = [[1, 0, 0], [2, 0, 0]] - coords_1 = [1, 0, 0] - - if units: - translation = Q_(translation, units) - coords_1 = Q_(coords_1, units) + translation = Q_([[1, 0, 0], [2, 0, 0]], units) + coords_1 = Q_([1, 0, 0], units) csm = CSM("r") csm.create_cs("trl1", "r", coordinates=translation, time=Q_([1, 2], "s")) @@ -1681,9 +1687,9 @@ def test_merge_reference_times( """ # setup - lcs_static = tf.LocalCoordinateSystem(coordinates=[1, 1, 1]) + lcs_static = tf.LocalCoordinateSystem(coordinates=Q_([1, 1, 1], "mm")) lcs_dynamic = tf.LocalCoordinateSystem( - coordinates=[[0, 4, 2], [7, 2, 4]], time=TDI([4, 8], "D") + coordinates=Q_([[0, 4, 2], [7, 2, 4]], "mm"), time=TDI([4, 8], "D") ) time_ref_parent = None if time_ref_day_parent is not None: @@ -1868,7 +1874,7 @@ def test_unmerge_merged_serially(list_of_csm_and_lcs_instances, additional_cs): count = 0 for parent_cs, target_csm in additional_cs.items(): - lcs = LCS(coordinates=[count, count + 1, count + 2]) + lcs = LCS(coordinates=Q_([count, count + 1, count + 2], "mm")) csm_mg.add_cs(f"additional_{count}", parent_cs, lcs) csm[target_csm].add_cs(f"additional_{count}", parent_cs, lcs) count += 1 @@ -1929,7 +1935,7 @@ def test_unmerge_merged_nested(list_of_csm_and_lcs_instances, additional_cs): count = 0 exp_orphan_node_warning = False for parent_cs, target_csm in additional_cs.items(): - lcs = LCS(coordinates=[count, count + 1, count + 2]) + lcs = LCS(coordinates=Q_([count, count + 1, count + 2], "mm")) csm_mg.add_cs(f"additional_{count}", parent_cs, lcs) if target_csm == 0: csm[target_csm].add_cs(f"additional_{count}", parent_cs, lcs) @@ -2012,7 +2018,7 @@ def test_delete_cs_with_serially_merged_subsystems( # add some additional coordinate systems target_system_index = [0, 2, 5, 7, 10] for i, _ in enumerate(target_system_index): - lcs = LCS(coordinates=[i, 2 * i, -i]) + lcs = LCS(coordinates=Q_([i, 2 * i, -i], "mm")) csm_mg.add_cs(f"add{i}", f"lcs{target_system_index[i]}", lcs) # just to avoid useless tests (delete does nothing if the lcs doesn't exist) @@ -2064,10 +2070,10 @@ def test_delete_cs_with_nested_subsystems( csm_mg = deepcopy(csm[0]) csm_n3 = deepcopy(csm[3]) - csm_n3.add_cs("nes0", "lcs8", LCS(coordinates=[1, 2, 3])) + csm_n3.add_cs("nes0", "lcs8", LCS(coordinates=Q_([1, 2, 3], "mm"))) csm_n3.merge(csm[5]) csm_n2 = deepcopy(csm[2]) - csm_n2.add_cs("nes1", "lcs5", LCS(coordinates=[-1, -2, -3])) + csm_n2.add_cs("nes1", "lcs5", LCS(coordinates=Q_([-1, -2, -3], "mm"))) csm_n2.merge(csm_n3) csm_mg.merge(csm[1]) csm_mg.merge(csm[4]) @@ -2076,7 +2082,7 @@ def test_delete_cs_with_nested_subsystems( # add some additional coordinate systems target_system_indices = [0, 2, 5, 7, 10] for i, target_system_index in enumerate(target_system_indices): - lcs = LCS(coordinates=[i, 2 * i, -i]) + lcs = LCS(coordinates=Q_([i, 2 * i, -i], "mm")) csm_mg.add_cs(f"add{i}", f"lcs{target_system_index}", lcs) # just to avoid useless tests (delete does nothing if the lcs doesn't exist) @@ -2099,20 +2105,20 @@ def test_delete_cs_with_nested_subsystems( def test_plot(): """Test if the plot function runs without problems. Output is not checked.""" csm_global = CSM("root", "global coordinate systems") - csm_global.create_cs("specimen", "root", coordinates=[1, 2, 3]) - csm_global.create_cs("robot head", "root", coordinates=[4, 5, 6]) + csm_global.create_cs("specimen", "root", coordinates=Q_([1, 2, 3], "m")) + csm_global.create_cs("robot head", "root", coordinates=Q_([4, 5, 6], "m")) csm_specimen = CSM("specimen", "specimen coordinate systems") - csm_specimen.create_cs("thermocouple 1", "specimen", coordinates=[1, 1, 0]) - csm_specimen.create_cs("thermocouple 2", "specimen", coordinates=[1, 4, 0]) + csm_specimen.create_cs("thermocouple 1", "specimen", coordinates=Q_([1, 1, 0], "m")) + csm_specimen.create_cs("thermocouple 2", "specimen", coordinates=Q_([1, 4, 0], "m")) csm_robot = CSM("robot head", "robot coordinate systems") - csm_robot.create_cs("torch", "robot head", coordinates=[0, 0, -2]) - csm_robot.create_cs("mount point 1", "robot head", coordinates=[0, 1, -1]) - csm_robot.create_cs("mount point 2", "robot head", coordinates=[0, -1, -1]) + csm_robot.create_cs("torch", "robot head", coordinates=Q_([0, 0, -2], "m")) + csm_robot.create_cs("mount point 1", "robot head", coordinates=Q_([0, 1, -1], "m")) + csm_robot.create_cs("mount point 2", "robot head", coordinates=Q_([0, -1, -1], "m")) csm_scanner = CSM("scanner", "scanner coordinate systems") - csm_scanner.create_cs("mount point 1", "scanner", coordinates=[0, 0, 2]) + csm_scanner.create_cs("mount point 1", "scanner", coordinates=Q_([0, 0, 2], "m")) csm_robot.merge(csm_scanner) csm_global.merge(csm_robot) @@ -2135,10 +2141,10 @@ def setup_csm_test_assign_data() -> tf.CoordinateSystemManager: """ # test setup lcs1_in_root = tf.LocalCoordinateSystem( - tf.WXRotation.from_euler("z", np.pi / 2).as_matrix(), [1, 2, 3] + tf.WXRotation.from_euler("z", np.pi / 2).as_matrix(), Q_([1, 2, 3], "mm") ) - lcs2_in_root = tf.LocalCoordinateSystem(r_mat_y(0.5), [3, -3, 1]) - lcs3_in_lcs2 = tf.LocalCoordinateSystem(r_mat_x(0.5), [1, -1, 3]) + lcs2_in_root = tf.LocalCoordinateSystem(r_mat_y(0.5), Q_([3, -3, 1], "mm")) + lcs3_in_lcs2 = tf.LocalCoordinateSystem(r_mat_x(0.5), Q_([1, -1, 3], "mm")) csm = tf.CoordinateSystemManager(root_coordinate_system_name="root") csm.add_cs("lcs_1", "root", lcs1_in_root) @@ -2168,27 +2174,27 @@ def setup_csm_test_assign_data() -> tf.CoordinateSystemManager: ( "lcs_3", "my_data", - [[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], + Q_([[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], "mm"), "lcs_1", - [[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], + Q_([[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], "mm"), ), ( "lcs_3", "my_data", - SpatialData([[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]]), + SpatialData(Q_([[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], "mm")), "lcs_1", - [[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], + Q_([[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], "mm"), ), ( "lcs_3", "my_data", xr.DataArray( - data=[[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], + data=Q_([[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], "mm"), dims=["n", "c"], coords={"c": ["x", "y", "z"]}, ), "lcs_1", - [[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], + Q_([[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], "mm"), ), ], ) @@ -2517,8 +2523,8 @@ def _coordinates_from_value(val, clip_min=None, clip_max=None): if clip_min is not None and clip_max is not None: val = np.clip(val, clip_min, clip_max) if len(val) > 1: - return [[v, 2 * v, -v] for v in val] - return [val[0], 2 * val[0], -val[0]] + return Q_([[v, 2 * v, -v] for v in val], "mm") + return Q_([val[0], 2 * val[0], -val[0]], "mm") @pytest.mark.parametrize( @@ -2592,7 +2598,7 @@ def test_interp_time( ) lcs_3 = tf.LocalCoordinateSystem( - WXRotation.from_euler("y", 1).as_matrix(), [4, 2, 0] + WXRotation.from_euler("y", 1).as_matrix(), Q_([4, 2, 0], "mm") ) csm.add_cs("lcs_3", "lcs_2", lcs_3) @@ -2661,7 +2667,7 @@ def test_issue_289_interp_outside_time_range( """ angles = [45, 135] if time_dep_orient else 135 orientation = WXRotation.from_euler("x", angles, degrees=True).as_matrix() - coordinates = [[0, 0, 0], [1, 1, 1]] if time_dep_coords else [1, 1, 1] + coordinates = Q_([[0, 0, 0], [1, 1, 1]] if time_dep_coords else [1, 1, 1], "mm") if time_dep_coords or time_dep_orient: time = ["5s", "6s"] if all_less else ["0s", "1s"] else: @@ -2677,12 +2683,12 @@ def test_issue_289_interp_outside_time_range( exp_angle = 45 if time_dep_orient and all_less else 135 exp_orient = WXRotation.from_euler("x", exp_angle, degrees=True).as_matrix() - exp_coords = [0, 0, 0] if time_dep_coords and all_less else [1, 1, 1] + exp_coords = Q_([0, 0, 0] if time_dep_coords and all_less else [1, 1, 1], "mm") assert cs_br.is_time_dependent is False assert cs_br.time is None - assert cs_br.coordinates.values.shape == (3,) - assert cs_br.orientation.values.shape == (3, 3) + assert cs_br.coordinates.data.shape == (3,) + assert cs_br.orientation.data.shape == (3, 3) assert np.all(cs_br.coordinates.data == exp_coords) assert np.all(cs_br.orientation.data == exp_orient) @@ -2739,7 +2745,7 @@ def test_coordinate_system_manager_create_coordinate_system(): time = TDI([0, 6, 12, 18], "H") orientations = np.matmul(rot_mat_x, rot_mat_y) - coords = [[1, 0, 0], [-1, 0, 2], [3, 5, 7], [-4, -5, -6]] + coords = Q_([[1, 0, 0], [-1, 0, 2], [3, 5, 7], [-4, -5, -6]], "mm") csm = tf.CoordinateSystemManager("root") lcs_default = tf.LocalCoordinateSystem() @@ -2749,7 +2755,7 @@ def test_coordinate_system_manager_create_coordinate_system(): check_coordinate_system( csm.get_cs("lcs_init_default"), lcs_default.orientation, - lcs_default.coordinates, + lcs_default.coordinates.data, True, ) @@ -2767,7 +2773,7 @@ def test_coordinate_system_manager_create_coordinate_system(): check_coordinate_system( csm.get_cs("lcs_euler_default"), orientations[0], - lcs_default.coordinates, + lcs_default.coordinates.data, True, ) @@ -2787,25 +2793,24 @@ def test_coordinate_system_manager_transform_data(): """Test the coordinate system managers transform_data function.""" # define some coordinate systems # TODO: test more unique rotations - not 90° - lcs1_in_root = tf.LocalCoordinateSystem(r_mat_z(0.5), [1, 2, 3]) - lcs2_in_root = tf.LocalCoordinateSystem(r_mat_y(0.5), [3, -3, 1]) - lcs3_in_lcs2 = tf.LocalCoordinateSystem(r_mat_x(0.5), [1, -1, 3]) + lcs1_in_root = tf.LocalCoordinateSystem(r_mat_z(0.5), Q_([1, 2, 3], "mm")) + lcs2_in_root = tf.LocalCoordinateSystem(r_mat_y(0.5), Q_([3, -3, 1], "mm")) + lcs3_in_lcs2 = tf.LocalCoordinateSystem(r_mat_x(0.5), Q_([1, -1, 3], "mm")) csm = tf.CoordinateSystemManager(root_coordinate_system_name="root") csm.add_cs("lcs_1", "root", lcs1_in_root) csm.add_cs("lcs_2", "root", lcs2_in_root) csm.add_cs("lcs_3", "lcs_2", lcs3_in_lcs2) - data_list = [[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]] - data_exp = np.array([[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]]) + data_list = Q_([[1, -3, -1], [2, 4, -1], [-1, 2, 3], [3, -4, 2]], "mm") + data_exp = Q_([[-5, -2, -4], [-5, -9, -5], [-9, -7, -2], [-8, -1, -6]], "mm") # input list - data_list_transformed = csm.transform_data(data_list, "lcs_3", "lcs_1") + data_list_transformed = csm.transform_data(data_list, "lcs_3", "lcs_1").data assert matrix_is_close(data_list_transformed, data_exp) # input numpy array - data_np = np.array(data_list) - data_numpy_transformed = csm.transform_data(data_np, "lcs_3", "lcs_1") + data_numpy_transformed = csm.transform_data(data_list, "lcs_3", "lcs_1").data assert matrix_is_close(data_numpy_transformed, data_exp) # input single numpy vector @@ -2813,7 +2818,9 @@ def test_coordinate_system_manager_transform_data(): # assert ut.vector_is_close(data_numpy_transformed, data_exp[0, :]) # input xarray - data_xr = xr.DataArray(data=data_np, dims=["n", "c"], coords={"c": ["x", "y", "z"]}) + data_xr = xr.DataArray( + data=data_list, dims=["n", "c"], coords={"c": ["x", "y", "z"]} + ) data_xr_transformed = csm.transform_data(data_xr, "lcs_3", "lcs_1") assert matrix_is_close(data_xr_transformed.data, data_exp) diff --git a/weldx/tests/transformations/test_local_cs.py b/weldx/tests/transformations/test_local_cs.py index 523d41fc2..49050ca3b 100644 --- a/weldx/tests/transformations/test_local_cs.py +++ b/weldx/tests/transformations/test_local_cs.py @@ -5,6 +5,7 @@ import numpy as np import pandas as pd +import pint import pytest import xarray as xr from pandas import TimedeltaIndex as TDI # noqa @@ -39,7 +40,8 @@ @pytest.mark.parametrize("data_array_coords", [True, False]) def test_init(orient, coords, time, time_ref, exception, data_array_coords): """Test the ´__init__´ method.""" - print(coords) + if not isinstance(coords, pint.Quantity): + coords = Q_(coords, "mm") if data_array_coords: coords = ut.xr_3d_vector(coords, time) @@ -92,7 +94,7 @@ def test_init_time_formats(time, time_ref, time_exp, time_ref_exp): """ # setup orientation = r_mat_z([0.5, 1.0, 1.5]) - coordinates = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + coordinates = Q_([[1, 2, 3], [4, 5, 6], [7, 8, 9]], "mm") lcs = tf.LocalCoordinateSystem(orientation, coordinates, time, time_ref=time_ref) # check results @@ -107,9 +109,9 @@ def test_init_time_formats(time, time_ref, time_exp, time_ref_exp): @pytest.mark.parametrize( "coordinates, orientation, time, warning", [ - (np.zeros(3), np.eye(3, 3), TDI([0, 2], "s"), UserWarning), - (np.zeros((2, 3)), np.eye(3, 3), TDI([0, 2], "s"), None), - (np.zeros(3), np.eye(3, 3), None, None), + (Q_(np.zeros(3), "mm"), np.eye(3, 3), TDI([0, 2], "s"), UserWarning), + (Q_(np.zeros((2, 3)), "mm"), np.eye(3, 3), TDI([0, 2], "s"), None), + (Q_(np.zeros(3), "mm"), np.eye(3, 3), None, None), ], ) def test_time_warning(coordinates, orientation, time, warning): @@ -162,7 +164,7 @@ def test_init_time_dsx(time_o, time_c, time_exp, time_ref): """ orientations = WXRotation.from_euler("z", range(len(time_o))).as_matrix() - coordinates = [[i, i, i] for i in range(len(time_o))] + coordinates = Q_([[i, i, i] for i in range(len(time_o))], "mm") dax_o = ut.xr_3d_matrix(orientations, time_o) dax_c = ut.xr_3d_vector(coordinates, time_c) @@ -203,7 +205,8 @@ def test_init_expr_time_series_as_coord(time, time_ref, angles): """ coordinates = MathematicalExpression( - expression="a*t+b", parameters=dict(a=Q_([1, 0, 0], "1/s"), b=[1, 2, 3]) + expression="a*t+b", + parameters=dict(a=Q_([1, 0, 0], "m/s"), b=Q_([1, 2, 3], "m")), ) ts_coord = TimeSeries(data=coordinates) @@ -270,7 +273,7 @@ def test_from_axis_vectors( time_ref = "2011-07-22" if has_time_ref else None angles = [[30, 45, 60], [40, 35, 80], [1, 33, 7], [90, 180, 270]] o = WXRotation.from_euler("xyz", angles, degrees=True).as_matrix() - c = [[-1, 3, 2], [4, 2, 4], [5, 1, 2], [3, 3, 3]] + c = Q_([[-1, 3, 2], [4, 2, 4], [5, 1, 2], [3, 3, 3]], "mm") if not time_dep_orient: o = o[0] @@ -349,7 +352,7 @@ def test_reset_reference_time(time, time_ref, time_ref_new, time_exp): """ orientation = WXRotation.from_euler("z", [1, 2, 3]).as_matrix() - coordinates = [[i, i, i] for i in range(3)] + coordinates = Q_([[i, i, i] for i in range(3)], "mm") lcs = tf.LocalCoordinateSystem(orientation, coordinates, time, time_ref=time_ref) lcs.reset_reference_time(time_ref_new) @@ -388,7 +391,7 @@ def test_reset_reference_time_exceptions( """ orientation = WXRotation.from_euler("z", [1, 2, 3]).as_matrix() - coordinates = [[i, i, i] for i in range(3)] + coordinates = Q_([[i, i, i] for i in range(3)], "mm") time = TDI([1, 2, 3], "D") lcs = tf.LocalCoordinateSystem(orientation, coordinates, time, time_ref=time_ref) @@ -466,10 +469,11 @@ def test_interp_time_discrete( Expected coordinates of the result """ + coordinates_exp = Q_(coordinates_exp, "mm") # setup lcs = tf.LocalCoordinateSystem( orientation=r_mat_z([0, 0.5, 1, 0.5]), - coordinates=np.array([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]]), + coordinates=Q_([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]], "mm"), time=TDI([10, 14, 18, 22], "D"), time_ref=time_ref_lcs, ) @@ -513,7 +517,7 @@ def test_issue_289_interp_outside_time_range( """ angles = [45, 135] if time_dep_orient else 135 orientation = WXRotation.from_euler("x", angles, degrees=True).as_matrix() - coordinates = [[0, 0, 0], [1, 1, 1]] if time_dep_coords else [1, 1, 1] + coordinates = Q_([[0, 0, 0], [1, 1, 1]] if time_dep_coords else [1, 1, 1], "mm") if time_dep_coords or time_dep_orient: time = ["5s", "6s"] if all_less else ["0s", "1s"] else: @@ -524,12 +528,12 @@ def test_issue_289_interp_outside_time_range( exp_angle = 45 if time_dep_orient and all_less else 135 exp_orient = WXRotation.from_euler("x", exp_angle, degrees=True).as_matrix() - exp_coords = [0, 0, 0] if time_dep_coords and all_less else [1, 1, 1] + exp_coords = Q_([0, 0, 0] if time_dep_coords and all_less else [1, 1, 1], "mm") assert lcs_interp.is_time_dependent is False assert lcs_interp.time is None - assert lcs_interp.coordinates.values.shape == (3,) - assert lcs_interp.orientation.values.shape == (3, 3) + assert lcs_interp.coordinates.data.shape == (3,) + assert lcs_interp.orientation.data.shape == (3, 3) assert np.all(lcs_interp.coordinates.data == exp_coords) assert np.all(lcs_interp.orientation.data == exp_orient) @@ -540,18 +544,18 @@ def test_issue_289_interp_outside_time_range( def test_interp_time_discrete_single_time(): """Test that single value interpolation results in a static system.""" orientation = WXRotation.from_euler("x", [45, 135], degrees=True).as_matrix() - coordinates = [[0, 0, 0], [2, 2, 2]] + coordinates = Q_([[0, 0, 0], [2, 2, 2]], "mm") time = ["1s", "3s"] lcs = LCS(orientation, coordinates, time) - exp_coords = [1, 1, 1] + exp_coords = Q_([1, 1, 1], "mm") exp_orient = WXRotation.from_euler("x", 90, degrees=True).as_matrix() lcs_interp = lcs.interp_time("2s") assert lcs_interp.is_time_dependent is False assert lcs_interp.time.equals(Time("2s")) - assert lcs_interp.coordinates.values.shape == (3,) - assert lcs_interp.orientation.values.shape == (3, 3) + assert lcs_interp.coordinates.data.shape == (3,) + assert lcs_interp.orientation.data.shape == (3, 3) assert np.all(lcs_interp.coordinates.data == exp_coords) assert np.allclose(lcs_interp.orientation.data, exp_orient) @@ -568,7 +572,7 @@ def test_interp_time_discrete_outside_value_range_both_sides(): """ orientation = WXRotation.from_euler("x", [45, 135], degrees=True).as_matrix() - coordinates = [[0, 0, 0], [2, 2, 2]] + coordinates = Q_([[0, 0, 0], [2, 2, 2]], "mm") time = ["2s", "3s"] lcs = LCS(orientation, coordinates, time) @@ -722,7 +726,7 @@ def test_interp_time_exceptions( """ orientation = r_mat_z([1, 2, 3]) - coordinates = [[i, i, i] for i in range(3)] + coordinates = Q_([[i, i, i] for i in range(3)], "mm") time_lcs = TDI([1, 2, 3], "D") lcs = tf.LocalCoordinateSystem( @@ -740,8 +744,8 @@ def test_interp_time_exceptions( "lcs_lhs, lcs_rhs, orientation_exp, coordinates_exp, time_exp, time_ref_exp", [ ( # 1 - both static - LCS(r_mat_y(0.5), [1, 4, 2]), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_y(0.5), Q_([1, 4, 2], "mm")), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), [[0, -1, 0], [0, 0, 1], [-1, 0, 0]], [-1, 8, 3], None, @@ -750,11 +754,11 @@ def test_interp_time_exceptions( ( # 2 - left system orientation time dependent LCS( r_mat_z([0, 0.5, 1]), - [1, 4, 2], + Q_([1, 4, 2], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), r_mat_z([0.5, 1, 1.5]), [[-1, 8, 3], [-1, 8, 3], [-1, 8, 3]], TDI([1, 3, 5], "D"), @@ -763,21 +767,21 @@ def test_interp_time_exceptions( ( # 3 - left system coordinates time dependent LCS( r_mat_y(0.5), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), [[[0, -1, 0], [0, 0, 1], [-1, 0, 0]] for _ in range(3)], [[-4, 10, 2], [5, 11, 9], [0, 2, 0]], TDI([1, 3, 5], "D"), TS("2020-02-02"), ), ( # 4 - right system orientation time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z([0, 0.5, 1]), - [1, 4, 2], + Q_([1, 4, 2], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -787,10 +791,10 @@ def test_interp_time_exceptions( TS("2020-02-02"), ), ( # 5 - right system coordinates time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z(0.5), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -800,10 +804,10 @@ def test_interp_time_exceptions( TS("2020-02-02"), ), ( # 6 - right system fully time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z([0, 0.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -815,13 +819,13 @@ def test_interp_time_exceptions( ( # 7 - both fully time dependent - same time and reference time LCS( r_mat_z([1, 0, 0]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), LCS( r_mat_z([0, 0.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -833,13 +837,13 @@ def test_interp_time_exceptions( ( # 8 - both fully time dependent - different time but same reference time LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([2, 4, 6], "D"), TS("2020-02-02"), ), LCS( r_mat_z([0.75, 1.25, 0.75]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -851,13 +855,13 @@ def test_interp_time_exceptions( ( # 9 - both fully time dependent - different time and reference time #1 LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-03"), ), LCS( r_mat_z([0.75, 1.25, 0.75]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -869,13 +873,13 @@ def test_interp_time_exceptions( ( # 10 - both fully time dependent - different time and reference time #2 LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([3, 5, 7], "D"), TS("2020-02-01"), ), LCS( r_mat_z([0.75, 1.25, 0.75]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -907,6 +911,7 @@ def test_addition( Expected reference time of the resulting coordinate system """ + coordinates_exp = Q_(coordinates_exp, "mm") check_coordinate_system( lcs_lhs + lcs_rhs, orientation_exp, @@ -924,8 +929,8 @@ def test_addition( "lcs_lhs, lcs_rhs, orientation_exp, coordinates_exp, time_exp, time_ref_exp", [ ( # 1 - both static - LCS([[0, -1, 0], [0, 0, 1], [-1, 0, 0]], [-1, 8, 3]), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS([[0, -1, 0], [0, 0, 1], [-1, 0, 0]], Q_([-1, 8, 3], "mm")), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), r_mat_y(0.5), [1, 4, 2], None, @@ -934,11 +939,11 @@ def test_addition( ( # 2 - left system orientation time dependent LCS( r_mat_z([0.5, 1, 1.5]), - [-1, 8, 3], + Q_([-1, 8, 3], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), r_mat_z([0, 0.5, 1]), [[1, 4, 2], [1, 4, 2], [1, 4, 2]], TDI([1, 3, 5], "D"), @@ -947,21 +952,21 @@ def test_addition( ( # 3 - left system coordinates time dependent LCS( [[0, -1, 0], [0, 0, 1], [-1, 0, 0]], - [[-4, 10, 2], [5, 11, 9], [0, 2, 0]], + Q_([[-4, 10, 2], [5, 11, 9], [0, 2, 0]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), r_mat_y([0.5, 0.5, 0.5]), [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], TDI([1, 3, 5], "D"), TS("2020-02-02"), ), ( # 4 - right system orientation time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z([0, 0.5, 1]), - [1, 4, 2], + Q_([1, 4, 2], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -971,10 +976,10 @@ def test_addition( TS("2020-02-02"), ), ( # 5 - right system coordinates time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z(0.5), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -984,10 +989,10 @@ def test_addition( TS("2020-02-02"), ), ( # 6 - right system fully time dependent - LCS(r_mat_z(0.5), [3, 7, 1]), + LCS(r_mat_z(0.5), Q_([3, 7, 1], "mm")), LCS( r_mat_z([0, 0.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -999,13 +1004,13 @@ def test_addition( ( # 7 - both fully time dependent - same time and reference time LCS( r_mat_z([1, 0.5, 1]), - [[7, 9, 6], [7, 1, 10], [-6, -4, -10]], + Q_([[7, 9, 6], [7, 1, 10], [-6, -4, -10]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), LCS( r_mat_z([0, 0.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -1017,13 +1022,13 @@ def test_addition( ( # 8 - both fully time dependent - different time but same reference time LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([2, 4, 6], "D"), TS("2020-02-02"), ), LCS( r_mat_z([1, 1.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -1035,13 +1040,13 @@ def test_addition( ( # 9 - both fully time dependent - different time and reference time #1 LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-03"), ), LCS( r_mat_z([1, 1.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -1053,13 +1058,13 @@ def test_addition( ( # 10 - both fully time dependent - different time and reference time #2 LCS( r_mat_z([1.5, 1.0, 0.75]), - [[4, 2, 5], [3, -3, 2], [1, 7, -9]], + Q_([[4, 2, 5], [3, -3, 2], [1, 7, -9]], "mm"), TDI([3, 5, 7], "D"), TS("2020-02-01"), ), LCS( r_mat_z([1, 1.5, 1]), - [[3, 7, 1], [4, -2, 8], [-5, 3, -1]], + Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm"), TDI([1, 3, 5], "D"), TS("2020-02-02"), ), @@ -1091,6 +1096,7 @@ def test_subtraction( Expected reference time of the resulting coordinate system """ + coordinates_exp = Q_(coordinates_exp, "mm") check_coordinate_system( lcs_lhs - lcs_rhs, orientation_exp, @@ -1109,7 +1115,7 @@ def test_subtraction( [ ({}, {}, {}, True), (dict(expression="2*a*t"), {}, {}, False), - (dict(parameters=dict(a=Q_([[2, 0, 0]], "1/s"))), {}, {}, False), + (dict(parameters=dict(a=Q_([[2, 0, 0]], "m/s"))), {}, {}, False), ({}, dict(data=Q_(np.ones((2, 3)), "mm"), time=Q_([1, 2], "s")), {}, False), ({}, {}, dict(orientation=[[0, -1, 0], [1, 0, 0], [0, 0, 1]]), False), ({}, {}, dict(time_ref=TS("11:12")), False), @@ -1140,7 +1146,7 @@ def test_comparison_coords_timeseries( Expected result of the comparison """ - me = MathematicalExpression("a*t", dict(a=Q_([[1, 0, 0]], "1/s"))) + me = MathematicalExpression("a*t", dict(a=Q_([[1, 0, 0]], "m/s"))) ts = TimeSeries(data=me) lcs = LCS(coordinates=ts) @@ -1174,8 +1180,8 @@ def test_coordinate_system_init(): orientation_fix = r_mat_z(1) orientation_tdp = r_mat_z([0, 0.25, 0.5]) - coordinates_fix = np.array([3, 7, 1], dtype=float) - coordinates_tdp = np.array([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], dtype=float) + coordinates_fix = Q_([3, 7, 1], "mm") + coordinates_tdp = Q_([[3, 7, 1], [4, -2, 8], [-5, 3, -1]], "mm") # numpy - no time dependency lcs = tf.LocalCoordinateSystem( @@ -1247,7 +1253,7 @@ def test_coordinate_system_init(): ) time_exp = TDI([1, 2, 3, 4, 5, 6], "s") - coordinates_exp = np.array( + coordinates_exp = Q_( [ [3, 7, 1], [3, 7, 1], @@ -1256,7 +1262,7 @@ def test_coordinate_system_init(): [-0.5, 0.5, 3.5], [-5, 3, -1], ], - dtype=float, + "mm", ) orientation_exp = r_mat_z([0, 0.125, 0.25, 0.375, 0.5, 0.5]) check_coordinate_system(lcs, orientation_exp, coordinates_exp, True, time_exp) @@ -1325,7 +1331,7 @@ def test_coordinate_system_factories_no_time_dependency(): # setup ----------------------------------------------- angles = [np.pi / 3, np.pi / 4, np.pi / 5] - coordinates = [4, -2, 6] + coordinates = Q_([4, -2, 6], "mm") orientation_pos = WXRotation.from_euler("xyz", angles).as_matrix() # construction with euler ----------------------------- @@ -1353,7 +1359,7 @@ def test_coordinate_system_factories_time_dependent(): time = TDI([0, 6, 12, 18], "H") orientations = np.matmul(rot_mat_x, rot_mat_y) - coords = [[1, 0, 0], [-1, 0, 2], [3, 5, 7], [-4, -5, -6]] + coords = Q_([[1, 0, 0], [-1, 0, 2], [3, 5, 7], [-4, -5, -6]], "mm") # construction with euler ----------------------------- @@ -1377,25 +1383,25 @@ def test_coordinate_system_invert(): """ # fix --------------------------------------- lcs0_in_lcs1 = tf.LocalCoordinateSystem.from_axis_vectors( - x=[1, 1, 0], y=[-1, 1, 0], coordinates=[2, 0, 2] + x=[1, 1, 0], y=[-1, 1, 0], coordinates=Q_([2, 0, 2], "mm") ) lcs1_in_lcs0 = lcs0_in_lcs1.invert() exp_orientation = r_mat_z(-1 / 4) - exp_coordinates = [-np.sqrt(2), np.sqrt(2), -2] + exp_coordinates = Q_([-np.sqrt(2), np.sqrt(2), -2], "mm") check_coordinate_system(lcs1_in_lcs0, exp_orientation, exp_coordinates, True) lcs0_in_lcs1_2 = lcs1_in_lcs0.invert() check_coordinate_system( - lcs0_in_lcs1_2, lcs0_in_lcs1.orientation, lcs0_in_lcs1.coordinates, True + lcs0_in_lcs1_2, lcs0_in_lcs1.orientation, lcs0_in_lcs1.coordinates.data, True ) # time dependent ---------------------------- time = TDI([1, 2, 3, 4], "s") orientation = r_mat_z([0, 0.5, 1, 0.5]) - coordinates = np.array([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]]) + coordinates = Q_([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]], "mm") lcs0_in_lcs1 = tf.LocalCoordinateSystem( orientation=orientation, coordinates=coordinates, time=time @@ -1403,14 +1409,18 @@ def test_coordinate_system_invert(): lcs1_in_lcs0 = lcs0_in_lcs1.invert() orientation_exp = r_mat_z([0, 1.5, 1, 1.5]) - coordinates_exp = np.array([[-2, -8, -7], [-9, 4, -2], [0, 2, -1], [-1, 3, -2]]) + coordinates_exp = Q_([[-2, -8, -7], [-9, 4, -2], [0, 2, -1], [-1, 3, -2]], "mm") check_coordinate_system(lcs1_in_lcs0, orientation_exp, coordinates_exp, True, time) lcs0_in_lcs1_2 = lcs1_in_lcs0.invert() check_coordinate_system( - lcs0_in_lcs1_2, lcs0_in_lcs1.orientation, lcs0_in_lcs1.coordinates, True, time + lcs0_in_lcs1_2, + lcs0_in_lcs1.orientation, + lcs0_in_lcs1.coordinates.data, + True, + time, ) @@ -1450,7 +1460,7 @@ def test_coordinate_system_time_interpolation(): """Test the local coordinate systems interp_time and interp_like functions.""" time_0 = TDI([10, 14, 18, 22], "D") orientation = r_mat_z([0, 0.5, 1, 0.5]) - coordinates = np.array([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]]) + coordinates = Q_([[2, 8, 7], [4, 9, 2], [0, 2, 1], [3, 1, 2]], "mm") lcs = tf.LocalCoordinateSystem( orientation=orientation, coordinates=coordinates, time=time_0 diff --git a/weldx/time.py b/weldx/time.py index 053b966ac..0314e4afb 100644 --- a/weldx/time.py +++ b/weldx/time.py @@ -149,7 +149,7 @@ class 'TimeDependent' are supported too. passed directly to `Time` as `time` parameter: >>> from weldx import LocalCoordinateSystem as LCS - >>> lcs = LCS(coordinates=[[0, 0, 0], [1, 1, 1]], time=["1s", "2s"]) + >>> lcs = LCS(coordinates=Q_([[0, 0, 0], [1, 1, 1]], "mm"), time=["1s", "2s"]) >>> t_from_lcs = Time(lcs) >>> >>> from weldx import TimeSeries diff --git a/weldx/transformations/local_cs.py b/weldx/transformations/local_cs.py index 65a58fc14..624a2946f 100644 --- a/weldx/transformations/local_cs.py +++ b/weldx/transformations/local_cs.py @@ -13,7 +13,7 @@ from scipy.spatial.transform import Rotation as Rot import weldx.util as ut -from weldx.constants import _DEFAULT_LEN_UNIT +from weldx.constants import _DEFAULT_LEN_UNIT, Q_ from weldx.core import TimeSeries from weldx.time import Time, TimeDependent, types_time_like, types_timestamp_like from weldx.transformations.types import types_coordinates, types_orientation @@ -284,11 +284,18 @@ def _build_coordinates( """Create xarray coordinates from different formats and time-inputs.""" if isinstance(coordinates, TimeSeries): if coordinates.is_expression: + if not coordinates.units.is_compatible_with(_DEFAULT_LEN_UNIT): + raise pint.DimensionalityError( + coordinates.units, + _DEFAULT_LEN_UNIT, + extra_msg="\nThe units resulting from the expression must " + "represent a length.", + ) return coordinates coordinates = cls._coords_from_discrete_time_series(coordinates) if coordinates is None: - coordinates = np.zeros(3) + coordinates = Q_(np.zeros(3), _DEFAULT_LEN_UNIT) if not isinstance(coordinates, xr.DataArray): if not isinstance(coordinates, (np.ndarray, pint.Quantity)): @@ -296,26 +303,15 @@ def _build_coordinates( coordinates = ut.xr_3d_vector(coordinates, time) - if isinstance(coordinates.data, pint.Quantity): - # The first branch is a workaround until units are mandatory - if coordinates.data.u == pint.Unit(""): - coordinates.data = coordinates.data.m - elif not coordinates.data.is_compatible_with(_DEFAULT_LEN_UNIT): - raise pint.DimensionalityError( - coordinates.data.u, - _DEFAULT_LEN_UNIT, - extra_msg="\nThe coordinates require units representing a length.", - ) - - if not isinstance(coordinates.data, pint.Quantity) and not ( - coordinates.shape == (3,) and np.allclose(coordinates.data, np.zeros(3)) + c_data = coordinates.data + if not isinstance(c_data, pint.Quantity) or not c_data.is_compatible_with( + _DEFAULT_LEN_UNIT ): - - warnings.warn( - "Coordinates without units are deprecated and won't be supported in " - "the future", - DeprecationWarning, - stacklevel=2, + unit = c_data.u if isinstance(c_data, pint.Quantity) else None + raise pint.DimensionalityError( + unit, + _DEFAULT_LEN_UNIT, + extra_msg="\nThe coordinates require units representing a length.", ) # make sure we have correct "time" format