Skip to content

Commit

Permalink
SpatialSeries and DynamicTraceSegment (#699)
Browse files Browse the repository at this point in the history
  • Loading branch information
vhirtham authored Feb 28, 2022
1 parent 71dd04d commit 2a3ab44
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 162 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ repos:
- mdformat-config
# ----- Python formatting -----
- repo: https://github.com/sondrelg/pep585-upgrade
rev: v1
rev: v1.0.1
hooks:
- id: upgrade-type-hints
args: [ '--futures=true' ]
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
added
=====

- `SpatialSeries` and `DynamicTraceSegment` [:pull:`699`]

- first draft of the ``multi_pass_weld`` schema for WelDX files [:pull:`667`]

- add `GenericSeries` as base class supporting arrays and equations [:pull:`618`]
Expand Down
2 changes: 1 addition & 1 deletion tutorials/01_03_geometry.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
"version": "3.9.9"
}
},
"nbformat": 4,
Expand Down
12 changes: 6 additions & 6 deletions tutorials/welding_example_01_basics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@
"metadata": {},
"outputs": [],
"source": [
"coords = [tcp_start_point.magnitude, tcp_end_point.magnitude]\n",
"coords = np.stack([tcp_start_point, tcp_end_point])\n",
"\n",
"tcp_wire = LocalCoordinateSystem(\n",
" coordinates=coords, orientation=rot, time=[t_start, t_end]\n",
Expand Down Expand Up @@ -398,7 +398,7 @@
"metadata": {},
"outputs": [],
"source": [
"tcp_contact = LocalCoordinateSystem(coordinates=[0, 0, -10])"
"tcp_contact = LocalCoordinateSystem(coordinates=Q_([0, 0, -10], \"mm\"))"
]
},
{
Expand Down Expand Up @@ -469,9 +469,9 @@
"outputs": [],
"source": [
"# add the workpiece coordinate system\n",
"csm.add_cs(\"T1\", \"workpiece\", LocalCoordinateSystem(coordinates=[200, 3, 5]))\n",
"csm.add_cs(\"T2\", \"T1\", LocalCoordinateSystem(coordinates=[0, 1, 0]))\n",
"csm.add_cs(\"T3\", \"T2\", LocalCoordinateSystem(coordinates=[0, 1, 0]))"
"csm.add_cs(\"T1\", \"workpiece\", LocalCoordinateSystem(coordinates=Q_([200, 3, 5], \"mm\")))\n",
"csm.add_cs(\"T2\", \"T1\", LocalCoordinateSystem(coordinates=Q_([0, 1, 0], \"mm\")))\n",
"csm.add_cs(\"T3\", \"T2\", LocalCoordinateSystem(coordinates=Q_([0, 1, 0], \"mm\")))"
]
},
{
Expand Down Expand Up @@ -581,7 +581,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.9"
"version": "3.8.12"
}
},
"nbformat": 4,
Expand Down
26 changes: 17 additions & 9 deletions weldx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
Time
TimeSeries
GenericSeries
SpatialSeries
MathematicalExpression
CoordinateSystemManager
LocalCoordinateSystem
Expand All @@ -72,6 +73,7 @@
Shape
Trace
SpatialData
DynamicTraceSegment
**Full API Reference**
Expand Down Expand Up @@ -130,17 +132,21 @@
from weldx.constants import Q_, U_

# main modules
import weldx.time
import weldx.time # skipcq: PY-W2000

# skipcq: PY-W2000
import weldx.util # import this second to avoid circular dependencies
import weldx.core
import weldx.transformations
import weldx.core # skipcq: PY-W2000
import weldx.transformations # skipcq: PY-W2000
import weldx.config
import weldx.geometry
import weldx.welding
import weldx.geometry # skipcq: PY-W2000
import weldx.welding # skipcq: PY-W2000

# class imports to weldx namespace
from weldx.config import Config
from weldx.core import GenericSeries, MathematicalExpression, TimeSeries

# skipcq: PY-W2000
from weldx.core import GenericSeries, MathematicalExpression, TimeSeries, SpatialSeries
from weldx.geometry import (
ArcSegment,
Geometry,
Expand All @@ -150,8 +156,9 @@
Shape,
Trace,
SpatialData,
DynamicTraceSegment,
)
from weldx.transformations import (
from weldx.transformations import ( # skipcq: PY-W2000
CoordinateSystemManager,
LocalCoordinateSystem,
WXRotation,
Expand All @@ -161,10 +168,10 @@
from weldx.time import Time

# tags (this will partially import weldx.asdf but not the extension)
from weldx import tags
from weldx import tags # skipcq: PY-W2000

# asdf extensions
import weldx.asdf
import weldx.asdf # skipcq: PY-W2000
from weldx.asdf.file import WeldxFile

__all__ = (
Expand All @@ -190,6 +197,7 @@
"util",
"welding",
"TimeSeries",
"DynamicTraceSegment",
"LinearHorizontalTraceSegment",
"Config",
"Time",
Expand Down
95 changes: 88 additions & 7 deletions weldx/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from weldx.types import UnitLike

__all__ = ["GenericSeries", "MathematicalExpression", "TimeSeries"]
__all__ = ["GenericSeries", "MathematicalExpression", "TimeSeries", "SpatialSeries"]

_me_parameter_types = Union[pint.Quantity, str, Tuple[pint.Quantity, str], xr.DataArray]

Expand Down Expand Up @@ -276,7 +276,6 @@ def evaluate(self, **kwargs) -> Any:
k: v if isinstance(v, xr.DataArray) else xr.DataArray(v)
for k, v in self._parameters.items()
}

return self.function(**variables, **parameters)


Expand Down Expand Up @@ -1026,7 +1025,6 @@ def _init_discrete(
else:
# todo check data structure
pass

# check the constraints of derived types
self._check_constraints_discrete(data)
self._obj = data
Expand Down Expand Up @@ -1254,9 +1252,14 @@ def _evaluate_preprocessor(self, **kwargs) -> list[SeriesParameter]:
def _evaluate_expr(self, coords: list[SeriesParameter]) -> GenericSeries:
"""Evaluate the expression at the passed coordinates."""
if len(coords) == self._obj.num_variables:
eval_args = {v.symbol: v.data_array for v in coords}
data = self._obj.evaluate(**eval_args)
return self.__class__(data)
eval_args = {
v.symbol: v.data_array.assign_coords(
{v.dim: v.data_array.pint.dequantify()}
)
for v in coords
}
da = self._obj.evaluate(**eval_args)
return self.__class__(da)

# turn passed coords into parameters of the expression
new_series = deepcopy(self)
Expand Down Expand Up @@ -1430,7 +1433,6 @@ def _check_constraints_discrete(cls, data_array: xr.DataArray):
ref[k]["dimensionality"] = _units[k]
if k in _vals:
ref[k]["values"] = _vals[k]

ut.xr_check_coords(data_array, ref)

@classmethod
Expand Down Expand Up @@ -1546,3 +1548,82 @@ def interp_like(
"""
return NotImplemented


# --------------------------------------------------------------------------------------
# SpatialSeries
# --------------------------------------------------------------------------------------


class SpatialSeries(GenericSeries):
"""Describes a line in 3d space depending on the positional coordinate ``s``."""

_position_dim_name = "s"

_required_variables: list[str] = [_position_dim_name]
"""Required variable names"""

_required_dimensions: list[str] = [_position_dim_name, "c"]
"""Required dimensions"""
_required_dimension_units: dict[str, pint.Unit] = {_position_dim_name: ""}
"""Required units of a dimension"""
_required_dimension_coordinates: dict[str, list] = {"c": ["x", "y", "z"]}
"""Required coordinates of a dimension."""

def __init__(
self,
obj: Union[pint.Quantity, xr.DataArray, str, MathematicalExpression],
dims: Union[list[str], dict[str, str]] = None,
coords: dict[str, pint.Quantity] = None,
units: dict[str, Union[str, pint.Unit]] = None,
interpolation: str = None,
parameters: dict[str, Union[str, pint.Quantity, xr.DataArray]] = None,
):
if isinstance(obj, Q_):
obj = self._process_quantity(obj, dims, coords)
dims = None
coords = None
if parameters is not None:
parameters = self._process_parameters(parameters)
super().__init__(obj, dims, coords, units, interpolation, parameters)

@classmethod
def _process_quantity(
cls,
obj: Union[pint.Quantity, xr.DataArray, str, MathematicalExpression],
dims: Union[list[str], dict[str, str]],
coords: dict[str, pint.Quantity],
) -> xr.DataArray:
"""Turn a quantity into a a correctly formatted data array."""
if isinstance(coords, dict):
s = coords[cls._position_dim_name]
else:
s = coords
coords = {cls._position_dim_name: s}

if not isinstance(s, xr.DataArray):
if not isinstance(s, Q_):
s = Q_(s, "")
s = xr.DataArray(s, dims=[cls._position_dim_name]).pint.dequantify()
coords[cls._position_dim_name] = s

if "c" not in coords:
coords["c"] = ["x", "y", "z"]

if dims is None:
dims = [cls._position_dim_name, "c"]

return xr.DataArray(obj, dims=dims, coords=coords)

@staticmethod
def _process_parameters(params):
"""Turn quantity parameters into the correctly formatted data arrays."""
for k, v in params.items():
if isinstance(v, Q_) and v.size == 3:
params[k] = xr.DataArray(v, dims=["c"], coords=dict(c=["x", "y", "z"]))
return params

@property
def position_dim_name(self):
"""Return the name of the dimension that determines the position on the line."""
return self._position_dim_name
Loading

0 comments on commit 2a3ab44

Please sign in to comment.