Skip to content

points

CoordsCalculator

CoordsCalculator(
    section_array: SectionArray,
    span_model: ISpan,
    cable_loads: CableLoads,
    get_displacement: Callable[[], ndarray],
    obstacle_array: ObstacleArray | None = None,
    **_,
)

Parameters:

Name Type Description Default

section_array

SectionArray

section array

required

span_model

ISpan

The span model to use for the points generation.

required

cable_loads

CableLoads

cable loads, used for beta angle

required

get_displacement

Callable

function that returns an array of chain displacement. Usually, comes from BalanceModel.get_displacement()

required
Source code in src/mechaphlowers/core/geometry/points.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
def __init__(
    self,
    section_array: SectionArray,
    span_model: ISpan,
    cable_loads: CableLoads,
    get_displacement: Callable[[], np.ndarray],
    obstacle_array: ObstacleArray | None = None,
    **_,
):
    """Initialize the CoordsCalculator object with section parameters and a span model.

    Args:
        section_array (SectionArray): section array
        span_model (ISpan): The span model to use for the points generation.
        cable_loads (CableLoads): cable loads, used for beta angle
        get_displacement (Callable): function that returns an array of chain displacement. Usually, comes from BalanceModel.get_displacement()
    """
    if obstacle_array is None:
        obstacle_array = ObstacleArray.build_empty_array()
    self.store_references(
        section_array,
        span_model,
        cable_loads,
        get_displacement,
        obstacle_array,
    )
    self.reset()

beta property

beta: ndarray

Get the beta angles for the cable spans. Beta is the angle du to the load on the cable

get_insulators

get_insulators() -> Points

Get the insulators in the section frame.

Source code in src/mechaphlowers/core/geometry/points.py
522
523
524
525
526
527
528
def get_insulators(self) -> Points:
    """Get the insulators in the section frame."""
    insulator_layers = get_insulator_layer(
        self.edge_arm_coords,
        self.get_attachments_coords(),
    )
    return Points.from_coords(insulator_layers)

get_points_for_plot

get_points_for_plot(
    project=False, frame_index=0
) -> tuple[Points, Points, Points]

Get Points objects for span, supports and insulators. Can be used for plotting 2D or 3D graphs.

Parameters:

Name Type Description Default

project

bool

Set to True if 2d graph: this project all objects into a support frame. Defaults to False.

False

frame_index

int

Index of the frame the projection is made. Should be between 0 and nb_supports-1 included. Unused if project is set to False. Defaults to 0.

0

Returns:

Type Description
tuple[Points, Points, Points]

tuple[Points, Points, Points]: Points for spans, supports and insulators respectively.

Raises:

Type Description
ValueError

frame_index is out of range

Source code in src/mechaphlowers/core/geometry/points.py
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
def get_points_for_plot(
    self,
    project=False,
    frame_index=0,
    # origin_position: Literal[
    #     "support", "attachment", "absolute"
    # ] = "attachment",
) -> tuple[Points, Points, Points]:
    """Get Points objects for span, supports and insulators.
    Can be used for plotting 2D or 3D graphs.

    Args:
        project (bool, optional): Set to True if 2d graph: this project all objects into a support frame. Defaults to False.
        frame_index (int, optional): Index of the frame the projection is made. Should be between 0 and nb_supports-1 included. Unused if project is set to False. Defaults to 0.

    Returns:
        tuple[Points, Points, Points]: Points for spans, supports and insulators respectively.

    Raises:
        ValueError: frame_index is out of range
    """
    spans_points: Points = self.get_spans("section")
    supports_points: Points = self.get_supports()
    insulators_points: Points = self.get_insulators()
    # TODO: remove this -> will eventually be delegated to GroupPoints
    if project:
        self._validate_frame_index(frame_index)
        spans_points, supports_points, insulators_points = (
            self.project_to_selected_frame(
                [spans_points, supports_points, insulators_points],
                frame_index,
            )
        )
    return spans_points, supports_points, insulators_points

get_sea_level_supports

get_sea_level_supports() -> Points

Get the supports in the section frame.

Source code in src/mechaphlowers/core/geometry/points.py
518
519
520
def get_sea_level_supports(self) -> Points:
    """Get the supports in the section frame."""
    return Points.from_coords(self.supports_ground_coords)

get_spans

get_spans(
    frame: Literal['cable', 'localsection', 'section'],
) -> Points

get_spans

Get the spans Points in the specified frame.

Parameters:

Name Type Description Default

frame

Literal['cable', 'localsection', 'section']

frame

required

Raises:

Type Description
ValueError

If the frame is not one of 'cable', 'localsection', or 'section'.

Returns:

Name Type Description
Points Points

Points object containing the spans in the specified frame.

Source code in src/mechaphlowers/core/geometry/points.py
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
def get_spans(
    self, frame: Literal["cable", "localsection", "section"]
) -> Points:
    """get_spans

    Get the spans Points in the specified frame.

    Args:
        frame (Literal['cable', 'localsection', 'section']): frame

    Raises:
        ValueError: If the frame is not one of 'cable', 'localsection', or 'section'.

    Returns:
        Points: Points object containing the spans in the specified frame.
    """
    self.set_cable_coordinates(resolution=cfg.graphics.resolution)
    if frame == "cable":
        x_span, y_span, z_span = self.span_in_cable_frame()
    elif frame == "localsection":
        x_span, y_span, z_span = self.span_in_localsection_frame()
    elif frame == "section":
        x_span, y_span, z_span = self.span_in_section_frame()
    else:
        raise ValueError(
            "Frame must be 'cable', 'localsection' or 'section'"
        )

    return Points.from_vectors(x_span, y_span, z_span)

get_supports

get_supports() -> Points

Get the supports in the section frame.

Source code in src/mechaphlowers/core/geometry/points.py
509
510
511
512
513
514
515
516
def get_supports(self) -> Points:
    """Get the supports in the section frame."""
    supports_layers = get_supports_layer(
        self.supports_ground_coords,
        self.center_arm_coords,
        self.edge_arm_coords,
    )
    return Points.from_coords(supports_layers)

project_to_selected_frame

project_to_selected_frame(
    points_array: list[PointsT], frame_index: int
) -> list[PointsT]

Project points object into a support frame.

Used for 2D plots that need to be projected in a specific frame.

Parameters:

Name Type Description Default

points_array

list[PointsT]

array of Points of SparsePoints objects

required

frame_index

int

Index of the frame the projection is made.

required

Returns:

Type Description
list[PointsT]

list[PointsT]: array of points object projected in local frame

list[PointsT]

projected into the frame of support number frame_index.

Source code in src/mechaphlowers/core/geometry/points.py
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
def project_to_selected_frame(
    self,
    points_array: list[PointsT],
    frame_index: int,
) -> list[PointsT]:
    """Project points object into a support frame.

    Used for 2D plots that need to be projected in a specific frame.

    Args:
        points_array (list[PointsT]): array of Points of SparsePoints objects
        frame_index (int): Index of the frame the projection is made.

    Returns:
        list[PointsT]: array of points object projected in local frame
        projected into the frame of support number `frame_index`.
    """
    translation_vector = -self.get_supports().coords[frame_index, 0]
    # set z coordinate to zero
    translation_vector[2] = 0.0
    angle_to_project = np.cumsum(self.line_angle)[frame_index]
    result_points = []
    for original_points in points_array:
        new_points = compute_new_frame(
            original_points, translation_vector, angle_to_project
        )
        # invert y axis to get more natural view
        x, y, z = new_points.vectors
        new_points.coords = np.array([x, -y, z]).T
        result_points.append(new_points)
    return result_points

refresh_obstacles

refresh_obstacles()

Need to be called when any modification to obstacle_array occurs

Source code in src/mechaphlowers/core/geometry/points.py
402
403
404
405
406
407
408
def refresh_obstacles(self):
    """Need to be called when any modification to obstacle_array occurs"""
    # obstacle_points are in absolute coordinates here
    self.obstacles_points = SparsePoints.builder_from_obstacle_array(
        self.obstacle_array
    )
    self.compute_obstacle_coords()

set_cable_coordinates

set_cable_coordinates(resolution: int) -> None

Set the span in the cable frame 2D coordinates based on the span model and resolution.

Source code in src/mechaphlowers/core/geometry/points.py
398
399
400
def set_cable_coordinates(self, resolution: int) -> None:
    """Set the span in the cable frame 2D coordinates based on the span model and resolution."""
    self.x_cable, self.z_cable = self.span_model.get_coords(resolution)

span_in_cable_frame

span_in_cable_frame() -> tuple[ndarray, ndarray, ndarray]

Get spans as vectors in the cable frame.

Source code in src/mechaphlowers/core/geometry/points.py
442
443
444
445
446
447
448
449
450
451
452
453
def span_in_cable_frame(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """Get spans as vectors in the cable frame."""
    # Rotate the cable with an angle to represent the wind
    self.set_cable_coordinates(resolution=cfg.graphics.resolution)
    x_span, y_span, z_span = cable_to_beta_plane(
        self.x_cable[:, :-1],
        self.z_cable[:, :-1],
        self.beta[:-1],
        self.plane.a_chain[:-1],
        self.plane.b_chain[:-1],
    )
    return x_span, y_span, z_span

span_in_localsection_frame

span_in_localsection_frame() -> (
    tuple[ndarray, ndarray, ndarray]
)

Get spans as vectors in the localsection frame.

Source code in src/mechaphlowers/core/geometry/points.py
455
456
457
458
459
460
461
462
463
def span_in_localsection_frame(
    self,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """Get spans as vectors in the localsection frame."""
    x_span, y_span, z_span = self.span_in_cable_frame()
    x_span, y_span, z_span = cable_to_localsection_frame(
        x_span, y_span, z_span, self.plane.azimuth_angle[:-1]
    )
    return x_span, y_span, z_span

span_in_section_frame

span_in_section_frame() -> tuple[ndarray, ndarray, ndarray]

Get spans as vectors in the section frame.

Source code in src/mechaphlowers/core/geometry/points.py
465
466
467
468
469
470
471
472
473
474
475
476
477
def span_in_section_frame(
    self,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """Get spans as vectors in the section frame."""
    # TODO: warning here : double call to set_cable_coordinates
    x_span, y_span, z_span = self.span_in_localsection_frame()
    x_span, y_span, z_span = translate_cable_to_support_from_attachments(
        x_span,
        y_span,
        z_span,
        self.get_attachments_coords(),
    )
    return x_span, y_span, z_span

Points

Points(coords: ndarray)

This class handles a set of points in 3D space, represented as a 3D numpy array. The points are stored in a 3D array with shape (number of layers, number of points, 3), where the last dimension represents the x, y, and z coordinates of each point.

It provides methods to convert the coordinates to vectors, points, and to create a Points object from.

Do not use this class directly, use the factory methods from_vectors or from_coords.

Examples:

1
2
3
4
5
6
7
>>> points = Points.from_vectors(x, y, z)
>>> span0, span1, span2, ... = points.coords
>>> span0
array([[x0, y0, z0],
    [x1, y1, z1],
    ...
    ])
Source code in src/mechaphlowers/core/geometry/points.py
94
95
96
97
98
99
def __init__(self, coords: np.ndarray):
    if coords.ndim != 3 or coords.shape[2] != 3:
        raise ValueError(
            "Coordinates must be a 3D array with shape (number of layers, number of points, 3)"
        )
    self.coords = coords

vectors property

vectors: tuple[ndarray, ndarray, ndarray]

Convert the coordinates to vectors. Returns the x, y, z coordinates as separate 2D arrays.

Returns:

Type Description
tuple[ndarray, ndarray, ndarray]

tuple[np.ndarray, np.ndarray, np.ndarray]: Three 2D arrays representing the x, y, and z coordinates.

Examples:

1
2
3
4
5
6
>>> x, y, z = points.vectors
>>> x
array([[x0_span0, x0_span1, x0_span2],
    [x1_span0, x1_span1, x1_span2],
    ...
    ])

flat_layer

flat_layer() -> ndarray

Convert the coordinates to a 2D array of points with a column dedicated to layer number for plotting or other uses as dataframe usage.

Not implemented yet

Returns:

Type Description
ndarray

np.ndarray: A 2D array of shape (number of layers x number of points, 4) where each row is a point (num layer, x, y, z).

Source code in src/mechaphlowers/core/geometry/points.py
156
157
158
159
160
161
162
163
164
def flat_layer(self) -> np.ndarray:
    """Convert the coordinates to a 2D array of points with a column dedicated to layer number for plotting or other uses as dataframe usage.

    Not implemented yet

    Returns:
        np.ndarray: A 2D array of shape (number of layers x number of points, 4) where each row is a point (num layer, x, y, z).
    """
    raise NotImplementedError

from_coords classmethod

from_coords(coords: ndarray) -> Self

Create Points from separate x, y, and z coordinates. Args: coords (np.ndarray): A 3D array of shape (layers, n_points, 3) where each row is a point (x, y, z).

Returns:

Name Type Description
Points Self

An instance of the Points class containing the coordinates.

Source code in src/mechaphlowers/core/geometry/points.py
194
195
196
197
198
199
200
201
202
203
@classmethod
def from_coords(cls, coords: np.ndarray) -> Self:
    """Create Points from separate x, y, and z coordinates.
    Args:
        coords (np.ndarray): A 3D array of shape (layers, n_points, 3) where each row is a point (x, y, z).

    Returns:
        Points: An instance of the Points class containing the coordinates.
    """
    return cls(coords)

from_vectors classmethod

from_vectors(x: ndarray, y: ndarray, z: ndarray) -> Self

Create Points from a vector of coordinates.

Parameters:

Name Type Description Default

x

ndarray

Array of x-coordinates (N, L).

required

y

ndarray

Array of y-coordinates (N, L).

required

z

ndarray

Array of z-coordinates (N, L).

required

Returns:

Name Type Description
Points Self

An instance of the Points class containing the coordinates.

Raises:

Type Description
ValueError

If x, y, or z are not 2D arrays.

Source code in src/mechaphlowers/core/geometry/points.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
@classmethod
def from_vectors(cls, x: np.ndarray, y: np.ndarray, z: np.ndarray) -> Self:
    # Mypy does not support the Self type from typing
    """Create Points from a vector of coordinates.

    Args:
        x (np.ndarray): Array of x-coordinates (N, L).
        y (np.ndarray): Array of y-coordinates (N, L).
        z (np.ndarray): Array of z-coordinates (N, L).

    Returns:
        Points: An instance of the Points class containing the coordinates.

    Raises:
        ValueError: If x, y, or z are not 2D arrays.
    """
    if x.ndim != 2 or y.ndim != 2 or z.ndim != 2:
        raise ValueError("x, y, and z must be 2D arrays")

    return cls(vectors_to_coords(x, y, z))

points

points(stack=False) -> ndarray

Convert the coordinates to a 2D array of points for plotting or other uses.

Parameters:

Name Type Description Default

stack

bool

If True, stack NaN values to separate spans when plotting. Defaults to False.

False

Returns:

Type Description
ndarray

np.ndarray: A 2D array of shape (number of points, 3) where each row is a point (x, y, z).

Examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> points_array = points.points(stack=False)
>>> points_array
array([[x0, y0, z0],
    [x1, y1, z1],
    [x2, y2, z2],
    ...
    ])
>>> points_array_stacked = points.points(stack=True)
>>> points_array_stacked
array([[x0_span0, y0_span0, z0_span0],
    [x1_span0, y1_span0, z1_span0],
    [x2_span0, y2_span0, z2_span0],
    ...
    [nan, nan, nan]
    [x0_span1, y0_span1, z0_span1],
    ...
    ])
Source code in src/mechaphlowers/core/geometry/points.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def points(self, stack=False) -> np.ndarray:
    """Convert the coordinates to a 2D array of points for plotting or other uses.

    Args:
        stack (bool, optional): If True, stack NaN values to separate spans when plotting. Defaults to False.

    Returns:
        np.ndarray: A 2D array of shape (number of points, 3) where each row is a point (x, y, z).

    Examples:
        >>> points_array = points.points(stack=False)
        >>> points_array
        array([[x0, y0, z0],
            [x1, y1, z1],
            [x2, y2, z2],
            ...
            ])
        >>> points_array_stacked = points.points(stack=True)
        >>> points_array_stacked
        array([[x0_span0, y0_span0, z0_span0],
            [x1_span0, y1_span0, z1_span0],
            [x2_span0, y2_span0, z2_span0],
            ...
            [nan, nan, nan]
            [x0_span1, y0_span1, z0_span1],
            ...
            ])
    """
    if stack is False:
        return coords_to_points(self.coords)
    else:
        return stack_nan(self.coords)

SparsePoints

SparsePoints(
    object_name: list,
    point_index: ndarray,
    span_index: ndarray,
    x: ndarray,
    y: ndarray,
    z: ndarray,
    object_type: list,
)

Class handle set of 3D points grouped by objects, but all objects do not have the same number of points.

Main use case is for managing obstacle points.

Source code in src/mechaphlowers/core/geometry/points.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def __init__(
    self,
    object_name: list,
    point_index: np.ndarray,
    span_index: np.ndarray,
    x: np.ndarray,
    y: np.ndarray,
    z: np.ndarray,
    object_type: list,
) -> None:
    self.object_name = object_name
    self.point_index = point_index
    self.span_index = span_index
    self.x = x
    self.y = y
    self.z = z
    self.object_type = object_type

dict_coords

dict_coords() -> dict

Returns a dictionary storing object coordinates.

Key is object name, value is coordinates of object.

Format: {'obs_0': [[x0, y0, z0], [x1, y1, z1], ...]}

Source code in src/mechaphlowers/core/geometry/points.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def dict_coords(self) -> dict:
    """Returns a dictionary storing object coordinates.

    Key is object name, value is coordinates of object.

    Format: {'obs_0': [[x0, y0, z0], [x1, y1, z1], ...]}
    """
    points = self.points()
    split_indices = np.nonzero(self.point_index == 0)[0]
    if len(split_indices) > 1:
        array_coords = np.split(points, split_indices[1:], axis=0)
    else:
        array_coords = [points]
    dict_coords = {}
    for i in range(len(split_indices)):
        object_name = self.object_name[split_indices[i]]
        dict_coords[object_name] = array_coords[i]
    return dict_coords

points

points(stack=False) -> ndarray

[[x0, y0, z0], # first obstacle [x1, y1, z1], [np.nan, np.nan, np.nan], # separator if stack=True [x0, y0, z0], # second obstacle [x1, y1, z1], [x2, y2, z2], [np.nan, np.nan, np.nan], # separator if stack=True ]

Source code in src/mechaphlowers/core/geometry/points.py
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def points(self, stack=False) -> np.ndarray:
    """
    [[x0, y0, z0], # first obstacle
    [x1, y1, z1],
    [np.nan, np.nan, np.nan], # separator if stack=True
    [x0, y0, z0], # second obstacle
    [x1, y1, z1],
    [x2, y2, z2],
    [np.nan, np.nan, np.nan], # separator if stack=True
    ]
    """
    points = self.coords
    # This method assume that points are correctly ordered by object and point index
    if stack:
        # get indices at the beginning of each object
        insert_indices = np.nonzero(self.point_index == 0)[0]
        nan_array = np.array([np.nan, np.nan, np.nan])
        points = np.insert(points, insert_indices, nan_array, axis=0)
    return points

compute_new_frame

compute_new_frame(
    points: PointsT,
    translation_vector: ndarray,
    angle_to_project: float64,
) -> PointsT

Change the frame of the given Points by applying a translation and a rotation.

Parameters:

Name Type Description Default

points

Points

points to transform

required

translation_vector

ndarray

translation vector to apply

required

angle_to_project

float64

angle of the rotation in radians

required

Returns:

Name Type Description
Points PointsT

new Points object in the new frame

Source code in src/mechaphlowers/core/geometry/points.py
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
def compute_new_frame(
    points: PointsT,
    translation_vector: np.ndarray,
    angle_to_project: np.float64,
) -> PointsT:
    """Change the frame of the given Points by applying a translation and a rotation.

    Args:
        points (Points): points to transform
        translation_vector (np.ndarray): translation vector to apply
        angle_to_project (np.float64): angle of the rotation in radians

    Returns:
        Points: new Points object in the new frame
    """
    points.coords = points.coords + translation_vector

    points.coords = rotate_vector(points.vectors, angle_to_project).T
    return points

coords_to_points

coords_to_points(coords: ndarray) -> ndarray

Convert the support coordinates to a format suitable for plotting.

Parameters:

Name Type Description Default

coords

ndarray

A 3D array of shape (layers, n_points, 3) where each row is a point (x, y, z).

required

Returns:

Type Description
ndarray

np.ndarray: A 2D array of shape (number of points, 3) where each row is a point (x, y, z).

Source code in src/mechaphlowers/core/geometry/points.py
63
64
65
66
67
68
69
70
71
72
def coords_to_points(coords: np.ndarray) -> np.ndarray:
    """Convert the support coordinates to a format suitable for plotting.

    Args:
        coords (np.ndarray): A 3D array of shape (layers, n_points, 3) where each row is a point (x, y, z).

    Returns:
        np.ndarray: A 2D array of shape (number of points, 3) where each row is a point (x, y, z).
    """
    return coords.reshape(-1, 3, order='F')

stack_nan

stack_nan(coords: ndarray) -> ndarray

Stack NaN values to the coords array to ensure consistent shape when plot and separate layers in a 2D array.

Source code in src/mechaphlowers/core/geometry/points.py
34
35
36
37
38
39
def stack_nan(coords: np.ndarray) -> np.ndarray:
    """Stack NaN values to the coords array to ensure consistent shape when plot and separate layers in a 2D array."""
    stack_array = np.zeros((coords.shape[0], 1, coords.shape[2])) * np.nan
    return np.concatenate((coords, stack_array), axis=1).reshape(
        -1, 3, order='C'
    )

vectors_to_coords

vectors_to_coords(
    x: ndarray, y: ndarray, z: ndarray
) -> ndarray

Convert 3 vectors of coordinates into an array of points.

Takes 3 numpy arrays representing x, y, and z coordinates and combines them into a single array of 3D points. The input vector format is expected to be (N, L) where N is the number of points per layer and L is the number of layers. The output will be an array of shape (number of layers, number of points, 3) where each row represents a point in 3D space.

Parameters:

Name Type Description Default

x

ndarray

Array of x-coordinates (N, L)

required

y

ndarray

Array of y-coordinates (N, L)

required

z

ndarray

Array of z-coordinates (N, L)

required

Returns:

Type Description
ndarray

np.ndarray: Array of points with shape (L,N,3) where N is the length of input vectors

Source code in src/mechaphlowers/core/geometry/points.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def vectors_to_coords(
    x: np.ndarray, y: np.ndarray, z: np.ndarray
) -> np.ndarray:
    """Convert 3 vectors of coordinates into an array of points.

    Takes 3 numpy arrays representing x, y, and z coordinates and combines them into a single array of 3D points.
    The input vector format is expected to be (N, L) where N is the number of points per layer and L is the number of layers.
    The output will be an array of shape (number of layers, number of points, 3) where each row represents a point in 3D space.

    Args:
        x (np.ndarray): Array of x-coordinates (N, L)
        y (np.ndarray): Array of y-coordinates (N, L)
        z (np.ndarray): Array of z-coordinates (N, L)

    Returns:
        np.ndarray: Array of points with shape (L,N,3) where N is the length of input vectors

    """
    return np.array([x, y, z]).T