# 1. Welding Example #01: Basics#

The goal of this small example is to introduce the main functionalities and interfaces to create and describe a simple welding application using the WelDX package.

## 1.1. Imports#

```# enable interactive plots on Jupyterlab with ipympl and jupyterlab-matplotlib installed
# %matplotlib widget
```
```import numpy as np
import pandas as pd

from weldx import (
Q_,
CoordinateSystemManager,
Geometry,
LinearHorizontalTraceSegment,
LocalCoordinateSystem,
Trace,
WeldxFile,
WXRotation,
get_groove,
)
```

## 1.2. create a simple welding groove shape#

We start of defining our welding application example by defining the base groove shape. For this examples we assume the groove shape to be constant along the entire workpiece.

The groove shall have the following attributes:

• a workpiece thickness of 5 mm

• a single sided V-Groove but weld with 50 degree groove angle

• root gap and face of 1 mm

To generate the groove shape, we can use the `get_groove` function of the of `iso_groove` that implements all groove types and shapes defined in ISO 9692-1:2013. For all available groove types and options take a look at the extensive docstring of `get_groove` and the groove_type tutorial notebook.

Be aware that we must pass along all groove parameters as Quantities with a specified unit using the default `Q_` type imported above. All units are automatically converted to SI units for most mathematical operations in the background so we can specify parameters in any appropriate unit we desire.

```groove = get_groove(
groove_type="VGroove",
workpiece_thickness=Q_(5, "mm"),
groove_angle=Q_(50, "deg"),
root_face=Q_(1, "mm"),
root_gap=Q_(1, "mm"),
)
```

Using the `plot` function of the created groove instance gives a quick overview of the created geometry.

```groove.plot(raster_width="0.2mm")
```

## 1.3. create 3d workpiece geometry#

Once we have created our desired 2d groove shape, we can simply extend the groove shape into 3d-space to create a volumetric workpiece.

To do this, two steps are missing:

1. we have to decide on a weld seam length first (we will use 300 mm in this example)

2. create a trace object that defines the path of our element through space. (we use a simple linear trace in this example)

```# define the weld seam length in mm
seam_length = Q_(300, "mm")

# create a linear trace segment a the complete weld seam trace
trace_segment = LinearHorizontalTraceSegment(seam_length)
trace = Trace(trace_segment)
```

Once we have defined the trace object, we can create the workpiece geometry by combining the groove profile with the trace.

```# create 3d workpiece geometry from the groove profile and trace objects
geometry = Geometry(groove.to_profile(width_default=Q_(5, "mm")), trace)
```

To visualize the geometry we simply call its `plot` function. Since it internally rasterizes the data, we need to provide the raster widths:

```# rasterize geometry
profile_raster_width = "2mm"  # resolution of each profile in mm
trace_raster_width = "30mm"  # space between profiles in mm
```

Here is the plot:

```def ax_setup(ax):
ax.legend()
ax.set_xlabel("x / mm")
ax.set_ylabel("y / mm")
ax.set_zlabel("z / mm")
ax.view_init(30, -10)
ax.set_ylim([-5.5, 5.5])
ax.set_zlim([0, 13])

color_dict = {
"tcp_contact": (255, 0, 0),
"tcp_wire": (0, 150, 0),
"T1": (255, 0, 150),
"T2": (255, 150, 150),
"T3": (255, 150, 0),
"specimen": (0, 0, 255),
}
```
```ax = geometry.plot(
profile_raster_width,
trace_raster_width,
color=color_dict["specimen"],
show_wireframe=True,
label="groove",
)
ax_setup(ax)
```

## 1.4. Setup the Coordinate System Manager (CSM)#

Once we have created the 3d geometry it is now time to describe the movement of the wire during the welding process. To handle different moving coordinate systems and objects we use the CoordinateSystemManager.

Start by creating a new instance of the CSM. When setting up a CSM instance we have to supply a name that indicates the reference coordinate system which is a static Cartesian coordinate system that defines an origin.

```# crete a new coordinate system manager with default base coordinate system
csm = CoordinateSystemManager("base")
```

The trace we created earlier to extend the groove shape into 3d has its own associated coordinate system that starts in the origin of the groove (see point (0,0) in our first plot of the groove profile) and has the x-axis running along the direction of the weld seam by convention.

We simply add the trace coordinate system to our coordinate system manager defining it as the workpiece coordinate system.

```# add the workpiece coordinate system
coordinate_system_name="workpiece",
reference_system_name="base",
lcs=trace.coordinate_system,
)
```

Now that we have added the workpiece coordinate system to the CSM, we can attach a rasterized representation of our geometry to it:

```csm.assign_data(
geometry.spatial_data(profile_raster_width, trace_raster_width),
"specimen",
"workpiece",
)
```

## 1.5. generate the tcp movement of the wire tip#

For this example, we want the tip of the wire (i.e. the robot TCP during welding) to move along the center of the groove at 2 mm from the bottom of the workpiece with a speed of 10 mm/s.

We begin by defining the start and end points relative to our workpiece coordinate system. Note that the z-axis of the workpiece coordinate system is pointing upwards (see Figure 1). Hence we use a positive offset of 2 mm in z direction from our workpiece. For the x-axis we start the weld 5 mm into the weldseam and 5 mm before reaching the end of the weldseam.

```tcp_start_point = Q_([5.0, 0.0, 2.0], "mm")
tcp_end_point = Q_([-5.0, 0.0, 2.0], "mm") + np.append(seam_length, Q_([0, 0], "mm"))
```

To completely describe the TCP movement in space and time we need to supply time information for the start and end point. Lets say the weld starts on 2020-04-20 10:00:00. We calculate the time

```v_weld = Q_(10, "mm/s")
s_weld = (tcp_end_point - tcp_start_point)[0]  # length of the weld
t_weld = s_weld / v_weld

t_start = pd.Timedelta("0s")
t_end = pd.Timedelta(str(t_weld.to_base_units()))
```

The two points and timestamps are enough to create the linear moving coordinate system. We can interpolate the movement with a higher resolution later.

The orientation of the wire has the z coordinate facing downwards towards the workpiece. The workpiece z-coordinate is facing upwards. We add a constant 180 degree rotation around the x-axis to orientate the wire coordinate system correctly. Orientations can be described using the scipy Rotation objects

```rot = WXRotation.from_euler(seq="x", angles=180, degrees=True)
```

With the defined coordinates, the constant orientation and the associated times we can create the coordinate system for the wire tcp.

```coords = np.stack([tcp_start_point, tcp_end_point])

tcp_wire = LocalCoordinateSystem(
coordinates=coords, orientation=rot, time=[t_start, t_end]
)
```

Add the new coordinate system to the coordinate system manager relative to the workpiece.

```# add the workpiece coordinate system
coordinate_system_name="tcp_wire",
reference_system_name="workpiece",
lcs=tcp_wire,
)
```

Lets say The wire extends 10 mm from the contact tip. We can add the contact tip as another point using the coordinate system manager. To simplify things we now use the tcp_wire coordinate system as reference. All we need to add is the z-offset along the wire. Note that we have to provide a negative offset along the z-axis since the wire-tcp z-axis is pointing downwards.

```tcp_contact = LocalCoordinateSystem(coordinates=Q_([0, 0, -10], "mm"))
```
```# add the workpiece coordinate system
coordinate_system_name="tcp_contact",
reference_system_name="tcp_wire",
lcs=tcp_contact,
)
```

We can create a simple plot of the relations between our our coordinate systems

```csm
```

## 1.6. plot the TCP trajectory#

To examine the movement of our wire TCP and contact tip, lets create a simple plot. We only have a linear movement so we don’t have to add additional timestamps to the moving coordinate systems to increase the resolution of the traces.

```ax = csm.plot(
coordinate_systems=["tcp_contact", "tcp_wire"],
colors=color_dict,
show_vectors=False,
show_wireframe=True,
)
ax_setup(ax)
```

## 1.7. Add static temperature measurement points#

With everything setup we can now start adding some measurements with associated points in space. We add a temperature measurements T1, T2, T3 to the surface of the weld seam.

```# add the workpiece coordinate system
csm.add_cs("T1", "workpiece", LocalCoordinateSystem(coordinates=Q_([200, 3, 5], "mm")))
csm.add_cs("T2", "T1", LocalCoordinateSystem(coordinates=Q_([0, 1, 0], "mm")))
csm.add_cs("T3", "T2", LocalCoordinateSystem(coordinates=Q_([0, 1, 0], "mm")))
```
```ax = csm.plot(
coordinate_systems=["tcp_contact", "tcp_wire", "T1", "T2", "T3"],
reference_system="workpiece",
colors=color_dict,
show_vectors=False,
show_wireframe=True,
)
ax_setup(ax)
```
```csm
```

## 1.8. K3D Visualization#

```csm.plot(
backend="k3d",
coordinate_systems=["tcp_contact", "tcp_wire", "T1", "T2", "T3"],
colors=color_dict,
limits=(0, 0, 0, 300, 100, 100),
show_vectors=False,
show_traces=True,
show_data_labels=False,
show_labels=False,
show_origins=True,
)
```

## 1.9. using ASDF#

Now we write all of our structured data to an ASDF file and have a look at the ASDF header.

```tree = {"workpiece": {"groove": groove, "length": seam_length}, "CSM": csm}
```
```file = WeldxFile(tree=tree, mode="rw")
```
```file.header()
```
```#ASDF 1.0.0
#ASDF_STANDARD 1.5.0
%YAML 1.1
%TAG ! tag:stsci.edu:asdf/
%TAG !weldx! asdf://weldx.bam.de/weldx/tags/
--- !core/asdf-1.1.0
asdf_library: !core/software-1.0.0 {author: The ASDF Developers, homepage: 'http://github.com/asdf-format/asdf',
name: asdf, version: 2.15.1}
history:
extensions:
extension_class: asdf.extension.BuiltinExtension
software: !core/software-1.0.0 {name: asdf, version: 2.15.1}
extension_class: weldx.asdf.extension.WeldxExtension
extension_uri: asdf://weldx.bam.de/weldx/extensions/weldx-0.1.2
software: !core/software-1.0.0 {name: weldx, version: 0.6.8.dev5+g3bef3e0.d20230911}
CSM: !weldx!core/transformations/coordinate_system_hierarchy-0.1.0
name: Coordinate system manager 0
graph: !weldx!core/graph/di_graph-0.1.0
root_node: !weldx!core/graph/di_node-0.1.0
name: base
attributes:
data: {}
edges:
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1 {}
target_node: !weldx!core/graph/di_node-0.1.0
name: workpiece
attributes:
data:
specimen: !weldx!core/geometry/spatial_data-0.1.1
coordinates: !weldx!units/quantity-0.1.0
value: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [176, 3]
units: !weldx!units/units-0.1.0 millimeter
triangles: !core/ndarray-1.0.0
data: []
datatype: uint64
shape: [344, 3]
edges:
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1
time: !weldx!time/timedeltaindex-0.1.0
values: !core/ndarray-1.0.0
data: []
datatype: int64
shape: [2]
start: !weldx!time/timedelta-0.1.0 P0DT0H0M0S
end: !weldx!time/timedelta-0.1.0 P0DT0H0M29S
min: !weldx!time/timedelta-0.1.0 P0DT0H0M0S
max: !weldx!time/timedelta-0.1.0 P0DT0H0M29S
orientations: !weldx!core/variable-0.1.1
name: orientations
dimensions: [c, v]
dtype: <f8
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [3, 3]
coordinates: !weldx!core/variable-0.1.1
name: coordinates
dimensions: [time, c]
dtype: <f8
units: !weldx!units/units-0.1.0 millimeter
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [2, 3]
target_node: !weldx!core/graph/di_node-0.1.0
name: tcp_wire
attributes:
data: {}
edges:
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1
coordinates: !weldx!core/variable-0.1.1
name: coordinates
dimensions: [c]
dtype: <f8
units: !weldx!units/units-0.1.0 millimeter
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [3]
target_node: !weldx!core/graph/di_node-0.1.0
name: tcp_contact
attributes:
data: {}
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1
coordinates: !weldx!core/variable-0.1.1
name: coordinates
dimensions: [c]
dtype: <f8
units: !weldx!units/units-0.1.0 millimeter
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [3]
target_node: !weldx!core/graph/di_node-0.1.0
name: T1
attributes:
data: {}
edges:
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1
coordinates: !weldx!core/variable-0.1.1
name: coordinates
dimensions: [c]
dtype: <f8
units: !weldx!units/units-0.1.0 millimeter
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [3]
target_node: !weldx!core/graph/di_node-0.1.0
name: T2
attributes:
data: {}
edges:
- !weldx!core/graph/di_edge-0.1.0
direction: bwd
attributes:
defined: true
transformation: !weldx!core/transformations/local_coordinate_system-0.1.1
coordinates: !weldx!core/variable-0.1.1
name: coordinates
dimensions: [c]
dtype: <f8
units: !weldx!units/units-0.1.0 millimeter
data: !core/ndarray-1.0.0
data: []
datatype: float64
shape: [3]
target_node: !weldx!core/graph/di_node-0.1.0
name: T3
attributes:
data: {}
subsystems: []
workpiece:
groove: !weldx!groove/iso_9692_1_2013_12/VGroove-0.1.0
t: !weldx!units/quantity-0.1.0 {value: 5, units: !weldx!units/units-0.1.0 millimeter}
alpha: !weldx!units/quantity-0.1.0 {value: 50, units: !weldx!units/units-0.1.0 degree}
b: !weldx!units/quantity-0.1.0 {value: 1, units: !weldx!units/units-0.1.0 millimeter}
c: !weldx!units/quantity-0.1.0 {value: 1, units: !weldx!units/units-0.1.0 millimeter}
code_number: ['1.3', '1.5']
length: !weldx!units/quantity-0.1.0 {value: 300, units: !weldx!units/units-0.1.0 millimeter}
```