Repository: BAMWelDX/weldx

Issue:
CSM interp_time doesn't work with scalar values
The following script crashes

~~~ python
from weldx import CoordinateSystemManager
from weldx.constants import WELDX_QUANTITY as Q_

csm = CoordinateSystemManager("root")
csm.create_cs("lcs 1", "root", coordinates=[1, 2, -1])
csm.create_cs("lcs 2", "root", coordinates=[[1, 2, 0], [1, 1, 0]], time=Q_([0, 7], "s"))

csm.interp_time(Q_(1, "s"))
~~~

The error message is:

~~~
ValueError: applied function returned data with unexpected number of dimensions. Received 1 dimension(s) but expected 2 dimensions with names: ('time', 'c')
~~~

Creating a quantity with a list that contains a single item (`Q_([1], "s")`) still fails. Only if a second member is added, the script runs as expected.


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=h1) Report
> Merging [#243](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=desc) (980ed20) into [master](https://codecov.io/gh/BAMWelDX/weldx/commit/3427fe983cafff4cd83c13761d78b23dc7be14f5?el=desc) (3427fe9) will **decrease** coverage by `0.02%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/BAMWelDX/weldx/pull/243/graphs/tree.svg?width=650&height=150&src=pr&token=wdof1qQTsn)](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #243      +/-   ##
==========================================
- Coverage   99.34%   99.31%   -0.03%     
==========================================
  Files          72       72              
  Lines        3964     3813     -151     
==========================================
- Hits         3938     3787     -151     
  Misses         26       26              
```


| [Impacted Files](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [weldx/utility.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvdXRpbGl0eS5weQ==) | `96.03% <100.00%> (-0.02%)` | :arrow_down: |
| [weldx/geometry.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvZ2VvbWV0cnkucHk=) | `98.59% <0.00%> (-0.14%)` | :arrow_down: |
| [weldx/asdf/tags/weldx/time/datetimeindex.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvYXNkZi90YWdzL3dlbGR4L3RpbWUvZGF0ZXRpbWVpbmRleC5weQ==) | `96.87% <0.00%> (-0.10%)` | :arrow_down: |
| [weldx/transformations.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvdHJhbnNmb3JtYXRpb25zLnB5) | `99.37% <0.00%> (-0.01%)` | :arrow_down: |
| [weldx/core.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvY29yZS5weQ==) | `100.00% <0.00%> (ø)` | |
| [weldx/asdf/types.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvYXNkZi90eXBlcy5weQ==) | `100.00% <0.00%> (ø)` | |
| [weldx/measurement.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvbWVhc3VyZW1lbnQucHk=) | `100.00% <0.00%> (ø)` | |
| [weldx/asdf/extension.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvYXNkZi9leHRlbnNpb24ucHk=) | `100.00% <0.00%> (ø)` | |
| [weldx/welding/processes.py](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree#diff-d2VsZHgvd2VsZGluZy9wcm9jZXNzZXMucHk=) | `100.00% <0.00%> (ø)` | |
| ... and [37 more](https://codecov.io/gh/BAMWelDX/weldx/pull/243/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=footer). Last update [3427fe9...90b497c](https://codecov.io/gh/BAMWelDX/weldx/pull/243?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml
index 7257905..5ef3ea6 100644
--- a/.github/workflows/static_analysis.yml
+++ b/.github/workflows/static_analysis.yml
@@ -1,5 +1,5 @@
 name: static analysis
-on: [push]
+on: [push, pull_request]
 
 jobs:
   pydocstyle:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 950ef78..20867ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,8 @@
 - fix `time_ref_restore` not working correctly if no `time_ref` was
   set [[#221]](https://github.com/BAMWelDX/weldx/pull/221)
 - fix deprecated signature in `WXRotation` [[#224]](https://github.com/BAMWelDX/weldx/pull/224)
+- fix a bug with singleton dimensions in xarray
+  interpolation/matmul [[#243]](https://github.com/BAMWelDX/weldx/pull/243)
 
 ### dependencies
 
diff --git a/weldx/utility.py b/weldx/utility.py
index b21ef7b..9971258 100644
--- a/weldx/utility.py
+++ b/weldx/utility.py
@@ -383,7 +383,7 @@ def mat_vec_mul(a, b) -> np.ndarray:
         Resulting vector [n, ]
 
     """
-    return np.matmul(a, b[..., np.newaxis]).squeeze()
+    return np.matmul(a, b[..., np.newaxis]).squeeze(axis=-1)
 
 
 def swap_list_items(arr, i1, i2) -> list:


Test patch:
diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index 44d8f4a..7d26aba 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -1,5 +1,5 @@
 name: pytest
-on: [push]
+on: [push, pull_request]
 
 jobs:
   pytest:
diff --git a/tests/test_transformations.py b/tests/test_transformations.py
index a3924ec..b72b116 100644
--- a/tests/test_transformations.py
+++ b/tests/test_transformations.py
@@ -3906,6 +3906,34 @@ def test_coordinate_system_manager_interp_time():
         orient_0_exp, coords_0_exp, time_interp_lcs0
     )
 
+    for cs_name in csm_interp_single.graph.nodes:
+        if cs_name == "root":
+            continue
+        ps_name = csm_interp_single.get_parent_system_name(cs_name)
+
+        if cs_name == "lcs_0":
+            exp = lcs_0_in_root_exp
+            exp_inv = lcs_0_in_root_exp.invert()
+        else:
+            exp = csm.get_cs(cs_name, ps_name)
+            exp_inv = csm.get_cs(ps_name, cs_name)
+
+        lcs = csm_interp_single.get_cs(cs_name, ps_name)
+        lcs_inv = csm_interp_single.get_cs(ps_name, cs_name)
+
+        check_coordinate_systems_close(lcs, exp)
+        check_coordinate_systems_close(lcs_inv, exp_inv)
+
+    # specific coordinate system (single, scalar timestamp) -----------------
+    time_interp_lcs0 = TDI([3], "D")
+    csm_interp_single = csm.interp_time(time_interp_lcs0, None, "lcs_0")
+
+    coords_0_exp = np.array(coords_0_exp)[[0], :]  # modified in prev test !
+    orient_0_exp = np.array(orient_0_exp)[[0], :]  # modified in prev test !
+    lcs_0_in_root_exp = tf.LocalCoordinateSystem(
+        orient_0_exp, coords_0_exp, time_interp_lcs0
+    )
+
     for cs_name in csm_interp_single.graph.nodes:
         if cs_name == "root":
             continue