Skip to content

arrays

CableArray

CableArray(
    data: DataFrame,
    tensile_strength: ITensileStrength | None = None,
)

Bases: ElementArray

Physical description of a cable.

Holds catalog data for one cable type and provides RRTS (Residual Rated Tensile Strength) calculations via rrts and utilization_rate. Use cut_strands to declare the number of damaged strands per layer.

The tensile strength model is handled by AdditiveLayerRts by default, but any ITensileStrength implementation can be injected via the tensile_strength constructor argument.

Parameters:

Name Type Description Default

data

DataFrame

Input data as a DataFrame matching CableArrayInput.

required

tensile_strength

ITensileStrength | None

Optional tensile strength model. Defaults to AdditiveLayerRts.

None
Source code in src/mechaphlowers/entities/arrays.py
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
def __init__(
    self,
    data: pd.DataFrame,
    tensile_strength: "ITensileStrength | None" = None,
) -> None:
    super().__init__(data)
    self.input_units: dict[str, str] = (
        options.input_units.cable_array.copy()
    )
    if tensile_strength is None:
        from mechaphlowers.core.models.cable.cable_strength import (
            AdditiveLayerRts,
        )  # noqa: PLC0415

        self._tensile_strength: ITensileStrength = AdditiveLayerRts(
            self.data
        )
    else:
        self._tensile_strength = tensile_strength

cut_strands property writable

cut_strands: ndarray

Delegated to the tensile strength model. See ITensileStrength.cut_strands.

data_mecha property

data_mecha: DataFrame

Returns mechanical data for cable. These attributes are stored in mecha_attributes

data_original property

data_original: DataFrame

Original dataframe with the exact same data as input: original units and no columns added

data_thermal property

data_thermal: DataFrame

Returns thermal data for cable. These attributes are stored in thermal_attributes

high_safety property writable

high_safety: bool

Delegated to the tensile strength model. See ITensileStrength.high_safety.

is_bimetallic property

is_bimetallic: NDArray[bool]

Whether the cable is bimetallic.

nb_strand_per_layer property

nb_strand_per_layer: ndarray

Delegated to the tensile strength model. See ITensileStrength.nb_strand_per_layer.

rrts property

rrts: float

Delegated to the tensile strength model. See ITensileStrength.rrts.

safety_coefficient property

safety_coefficient: float

Delegated to the tensile strength model. See ITensileStrength.safety_coefficient.

add_units

add_units(input_units: dict[str, str]) -> None

Add dictionary of units of the data input . This will overrides the default input_units dict

input_units has the following format:

1
2
3
4
{
    "column_name_0": "unit0",
    "column_name_1": "unit1",
}

Parameters:

Name Type Description Default

input_units

dict[str, str]

dictionary of columns names and corresponding units

required
Source code in src/mechaphlowers/entities/arrays.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def add_units(self, input_units: dict[str, str]) -> None:
    """Add dictionary of units of the data input . This will overrides the default `input_units` dict

    `input_units` has the following format:
    ```py
    {
        "column_name_0": "unit0",
        "column_name_1": "unit1",
    }
    ```

    Args:
        input_units (dict[str, str]): dictionary of columns names and corresponding units
    """
    self.input_units.update(input_units)

rts_coverage

rts_coverage() -> float

Delegated to the tensile strength model. See ITensileStrength.rts_coverage.

Source code in src/mechaphlowers/entities/arrays.py
774
775
776
777
778
def rts_coverage(self) -> float:
    """Delegated to the tensile strength model. See
    [`ITensileStrength.rts_coverage`][mechaphlowers.core.models.cable.cable_strength.ITensileStrength.rts_coverage].
    """
    return self._tensile_strength.rts_coverage()

utilization_rate

utilization_rate(tension_sup_N: ndarray) -> ndarray

Delegated to the tensile strength model. See ITensileStrength.utilization_rate.

Source code in src/mechaphlowers/entities/arrays.py
798
799
800
801
802
def utilization_rate(self, tension_sup_N: np.ndarray) -> np.ndarray:
    """Delegated to the tensile strength model. See
    [`ITensileStrength.utilization_rate`][mechaphlowers.core.models.cable.cable_strength.ITensileStrength.utilization_rate].
    """
    return self._tensile_strength.utilization_rate(tension_sup_N)

DefaultValueWarning

Bases: Warning

Warning for default values being used when not provided by user.

ElementArray

ElementArray(data: DataFrame)

Bases: ABC

Source code in src/mechaphlowers/entities/arrays.py
52
53
54
55
56
def __init__(self, data: pd.DataFrame) -> None:
    _data = self._drop_extra_columns(data)
    self._data: pd.DataFrame = _data
    # dict of default input units
    self.input_units: dict[str, str] = {}

data property

data: DataFrame

Returns a copy of self._data that converts values into SI units

data_original property

data_original: DataFrame

Original dataframe with the exact same data as input: original units and no columns added

add_units

add_units(input_units: dict[str, str]) -> None

Add dictionary of units of the data input . This will overrides the default input_units dict

input_units has the following format:

1
2
3
4
{
    "column_name_0": "unit0",
    "column_name_1": "unit1",
}

Parameters:

Name Type Description Default

input_units

dict[str, str]

dictionary of columns names and corresponding units

required
Source code in src/mechaphlowers/entities/arrays.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def add_units(self, input_units: dict[str, str]) -> None:
    """Add dictionary of units of the data input . This will overrides the default `input_units` dict

    `input_units` has the following format:
    ```py
    {
        "column_name_0": "unit0",
        "column_name_1": "unit1",
    }
    ```

    Args:
        input_units (dict[str, str]): dictionary of columns names and corresponding units
    """
    self.input_units.update(input_units)

ObstacleArray

ObstacleArray(data: DataFrame)

Bases: ElementArray

Obstacles-related data, such as obstacle altitude and distance from the line.

They are typically used to compute clearance-related checks.

Source code in src/mechaphlowers/entities/arrays.py
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
def __init__(
    self,
    data: pd.DataFrame,
) -> None:
    if data.empty:
        columns_names = list(ObstacleArrayInput.__annotations__.keys())
        empty_df = pd.DataFrame(columns=columns_names)
        super().__init__(empty_df)
    else:
        super().__init__(data)
        # Check if points from the same obstacle have the same indices
        points_has_same_indices = data.duplicated(
            subset=['name', 'point_index']
        ).any()
        if points_has_same_indices:
            raise ValueError(
                "An obstacle have two points with the same point_index"
            )
        # Check if each group of 'name' has only one unique 'span_index'
        obstacle_has_same_span_index = (
            data.groupby('name')['span_index'].nunique().eq(1).all()
        )
        if not obstacle_has_same_span_index:
            raise ValueError(
                "All points from the same obstacle should have the same span_index"
            )

data_original property

data_original: DataFrame

Original dataframe with the exact same data as input: original units and no columns added

add_obstacle

add_obstacle(
    name: str,
    span_index: int,
    coords: ndarray,
    object_type: str = 'ground',
    support_reference: Literal['left', 'right'] = 'left',
    span_length: ndarray | None = None,
    overwrite: bool = True,
)

Method used for adding an obstacle to ObstacleArray

coords format: [[x0, y0, z0], [x1, y1, z1],...]

If support_reference == "right", span_length is required.

If overwrite == True, will overwrite if name already exists. Else, it will add points to obstacle

Source code in src/mechaphlowers/entities/arrays.py
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
def add_obstacle(
    self,
    name: str,
    span_index: int,
    coords: np.ndarray,
    object_type: str = "ground",
    support_reference: Literal['left', 'right'] = 'left',
    span_length: np.ndarray | None = None,
    overwrite: bool = True,
):
    """
    Method used for adding an obstacle to ObstacleArray

    coords format: [[x0, y0, z0], [x1, y1, z1],...]

    If support_reference == "right", span_length is required.

    If overwrite == True, will overwrite if name already exists. Else, it will add points to obstacle
    """
    if len(coords.shape) != 2 or coords.shape[1] != 3:
        raise TypeError(
            "coords have incorrect dimension: it should be (n x 3)"
        )

    nb_points = coords.shape[0]

    x = coords[:, 0]

    if support_reference == 'right':
        if span_length is None:
            raise TypeError(
                "If support_reference is set to 'right', span_length is required"
            )
        x = self.reverse_x_coord(x, span_length, span_index)
    point_index = np.arange(nb_points)

    if name in self._data["name"].tolist():
        indices_existing_obstacle = self._data.index[
            self._data["name"] == name
        ]
        if overwrite:
            self._data.drop(indices_existing_obstacle, inplace=True)
            self._data.reset_index(drop=True, inplace=True)
        else:
            point_index = point_index + len(indices_existing_obstacle)

    new_obstacle = pd.DataFrame(
        {
            "name": [name] * nb_points,
            "point_index": point_index,
            "span_index": [span_index] * nb_points,
            "x": x,
            "y": coords[:, 1],
            "z": coords[:, 2],
            "object_type": [object_type] * nb_points,
        }
    )
    self._data = pd.concat([self._data, new_obstacle], ignore_index=True)
    logger.debug(f"Obstacle {name} added")

add_units

add_units(input_units: dict[str, str]) -> None

Add dictionary of units of the data input . This will overrides the default input_units dict

input_units has the following format:

1
2
3
4
{
    "column_name_0": "unit0",
    "column_name_1": "unit1",
}

Parameters:

Name Type Description Default

input_units

dict[str, str]

dictionary of columns names and corresponding units

required
Source code in src/mechaphlowers/entities/arrays.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def add_units(self, input_units: dict[str, str]) -> None:
    """Add dictionary of units of the data input . This will overrides the default `input_units` dict

    `input_units` has the following format:
    ```py
    {
        "column_name_0": "unit0",
        "column_name_1": "unit1",
    }
    ```

    Args:
        input_units (dict[str, str]): dictionary of columns names and corresponding units
    """
    self.input_units.update(input_units)

delete_obstacle

delete_obstacle(
    obs_names_to_delete: str | list[str],
) -> None

Deletes obstacles by name. Can delete multiple obstacles at once

Parameters:

Name Type Description Default

obs_names_to_delete

str | list[str]

str or list of obstacles to delete

required

Raises:

Type Description
ValueError

If obstacle name to delete is not found

TypeError

If obs_names_to_delete is not a str or a list

Source code in src/mechaphlowers/entities/arrays.py
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
def delete_obstacle(self, obs_names_to_delete: str | list[str]) -> None:
    """Deletes obstacles by name. Can delete multiple obstacles at once

    Args:
        obs_names_to_delete (str | list[str]): str or list of obstacles to delete

    Raises:
        ValueError: If obstacle name to delete is not found
        TypeError: If obs_names_to_delete is not a str or a list
    """
    if isinstance(obs_names_to_delete, str):
        indices_to_drop = self._data.index[
            self._data["name"] == obs_names_to_delete
        ].tolist()
        if len(indices_to_drop) == 0:
            raise ValueError(
                f"Obstacle {obs_names_to_delete} was not found."
            )
    elif isinstance(obs_names_to_delete, list):
        indices_to_drop = []
        existing_names = set(self._data["name"])
        for name_to_delete in obs_names_to_delete:
            if name_to_delete not in existing_names:
                raise ValueError(
                    f"Obstacle {name_to_delete} was not found."
                )
            indices_to_drop.extend(
                self._data.index[self._data["name"] == name_to_delete]
            )
    else:
        raise TypeError("obs_names_to_delete must be a str or list[str]")

    self._data.drop(indices_to_drop, inplace=True)
    self._data.reset_index(drop=True, inplace=True)

delete_point

delete_point(obs_name: str, point_index: int) -> None

Delete single point of an obstacle.

Refers to the point by obstacle name and point index.

Parameters:

Name Type Description Default

obs_name

str

name of obstacle point to delete

required

point_index

int

point_index of point to delete

required
Source code in src/mechaphlowers/entities/arrays.py
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
def delete_point(self, obs_name: str, point_index: int) -> None:
    """Delete single point of an obstacle.

    Refers to the point by obstacle name and point index.

    Args:
        obs_name (str): name of obstacle point to delete
        point_index (int): point_index of point to delete
    """
    index_to_drop = self._data.index[
        (self._data["name"] == obs_name)
        & (self._data["point_index"] == point_index)
    ]
    if len(index_to_drop) == 0:
        logger.warning(
            f"Point {point_index} of obstacle {obs_name} was not found. Did not delete anything"
        )
        warnings.warn(
            f"Point {point_index} of obstacle {obs_name} was not found. Did not delete anything"
        )
    else:
        self._data.sort_values(by=["name", "point_index"], inplace=True)
        self._data.drop(index_to_drop, inplace=True)
        self._data.reset_index(drop=True, inplace=True)
        obstacle_mask = self._data["name"] == obs_name
        obstacle_indices = self._data.index[obstacle_mask].to_numpy()
        self._data.loc[obstacle_indices, "point_index"] = np.arange(
            len(obstacle_indices)
        )

SectionArray

SectionArray(
    data: DataFrame,
    sagging_parameter: float | None = None,
    sagging_temperature: float | None = None,
    bundle_number: int = 1,
)

Bases: ElementArray

Description of an overhead line section.

Parameters:

Name Type Description Default

data

DataFrame

Input data

required

sagging_parameter

float | None

Sagging parameter

None

sagging_temperature

float | None

Sagging temperature, in Celsius degrees

None
Source code in src/mechaphlowers/entities/arrays.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def __init__(
    self,
    data: pd.DataFrame,
    sagging_parameter: float | None = None,
    sagging_temperature: float | None = None,
    bundle_number: int = 1,
) -> None:
    super().__init__(data)  # type: ignore[arg-type]

    if (
        sagging_parameter is not None
        and "sagging_parameter" in data.columns
    ):
        raise ValueError(
            "sagging_parameter provided both as argument and in data columns."
            " Please provide it only once."
        )
    elif (
        sagging_parameter is None
        and "sagging_parameter" not in data.columns
    ):
        warnings.warn(
            "sagging_parameter not provided. A default value will be computed.",
            DefaultValueWarning,
        )
        sagging_parameter = self.default_sagging_parameter()
    if sagging_parameter is not None:
        self.set_sagging_parameter(sagging_parameter)

    if (
        sagging_temperature is not None
        and "sagging_temperature" in data.columns
    ):
        raise ValueError(
            "sagging_temperature provided both as argument and in data columns."
            " Please provide it only once."
        )
    elif (
        sagging_temperature is None
        and "sagging_temperature" not in data.columns
    ):
        self.set_sagging_temperature(
            options.data.sagging_temperature_default
        )
    elif sagging_temperature is not None:
        self.set_sagging_temperature(sagging_temperature)
    if bundle_number < 1:
        raise ValueError(
            f"bundle_number should be a positive integer. Received: {bundle_number}"
        )
    self.bundle_number = bundle_number
    self.input_units = options.input_units.section_array.copy()
    self._check_insulator_length(self._data)
    self._angle_direction: Literal["clockwise", "anticlockwise"] = (
        "anticlockwise"
    )
    self.geolocator: GeoLocator = GeoLocator()
    logger.debug("Section Array initialized.")

angle_direction property writable

angle_direction: Literal['clockwise', 'anticlockwise']

Affects line_angle, crossarm_length sign

If "anticlockwise", line_angle is anticlockwise and crossarm_length is away from user (left). If "clockwise", line_angle is clockwise and crossarm_length is towards user (right).

Defaults to "anticlockwise".

data_original property

data_original: DataFrame

Original dataframe with the exact same data as input (except for sagging_parameter and sagging_temperature which are added if not provided in input) original units and no (other) columns added

add_units

add_units(input_units: dict[str, str]) -> None

Add dictionary of units of the data input . This will overrides the default input_units dict

input_units has the following format:

1
2
3
4
{
    "column_name_0": "unit0",
    "column_name_1": "unit1",
}

Parameters:

Name Type Description Default

input_units

dict[str, str]

dictionary of columns names and corresponding units

required
Source code in src/mechaphlowers/entities/arrays.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def add_units(self, input_units: dict[str, str]) -> None:
    """Add dictionary of units of the data input . This will overrides the default `input_units` dict

    `input_units` has the following format:
    ```py
    {
        "column_name_0": "unit0",
        "column_name_1": "unit1",
    }
    ```

    Args:
        input_units (dict[str, str]): dictionary of columns names and corresponding units
    """
    self.input_units.update(input_units)

compute_ground_altitude classmethod

compute_ground_altitude(
    suspension: NDArray[bool],
    conductor_attachment_altitude: ndarray,
    insulator_length: ndarray,
    bundle_number: int,
    support_height: ndarray | None = None,
) -> ndarray

Generate ground altitude array.

Source code in src/mechaphlowers/entities/arrays.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
@classmethod
def compute_ground_altitude(
    cls,
    suspension: npt.NDArray[np.bool],
    conductor_attachment_altitude: np.ndarray,
    insulator_length: np.ndarray,
    bundle_number: int,
    support_height: np.ndarray | None = None,
) -> np.ndarray:
    """Generate ground altitude array."""

    if support_height is None:
        altitude_correction = np.zeros_like(conductor_attachment_altitude)
        support_height = np.full_like(
            conductor_attachment_altitude,
            options.ground.default_support_length,
        )
    else:
        altitude_correction = np.where(  # type: ignore
            np.isnan(support_height),
            0.0,
            cls.compute_ground_altitude_correction(
                suspension,
                insulator_length,
                bundle_number,
            ),
        )
        support_height = np.where(
            np.isnan(support_height),
            options.ground.default_support_length,
            support_height,
        )

    return (
        conductor_attachment_altitude
        - support_height
        + altitude_correction
    )

correct_insulator_length classmethod

correct_insulator_length(data: DataFrame) -> None

Correct insulator length to be at least 0.01 m to avoid numerical issues.

Source code in src/mechaphlowers/entities/arrays.py
275
276
277
278
279
280
281
@classmethod
def correct_insulator_length(cls, data: pd.DataFrame) -> None:
    """Correct insulator length to be at least 0.01 m to avoid numerical issues."""
    cls._check_insulator_length(data)
    data["insulator_length"] = np.where(
        data["insulator_length"] < 0.01, 0.01, data["insulator_length"]
    )

equivalent_span

equivalent_span() -> float

Compute equivalent span length.

Used in the default value of sagging_parameter.

compute equivalent span

\(L_{eq} = \sqrt{\sum(L_i ^ 3)/\sum L_i}\)

Returns:

Name Type Description
float float

equivalent span length (m)

Source code in src/mechaphlowers/entities/arrays.py
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
def equivalent_span(self) -> float:
    """Compute equivalent span length.

    Used in the default value of sagging_parameter.

    compute equivalent span:
       $L_{eq} = \\sqrt{\\sum(L_i ^ 3)/\\sum L_i}$


    Returns:
        float: equivalent span length (m)
    """
    span_length = self._data.span_length.to_numpy()
    span_length_3 = span_length.copy() ** 3

    return np.sqrt(np.nansum(span_length_3) / np.nansum(span_length))

get_azimuth

get_azimuth(
    unit: str = 'deg',
    output_direction: Literal[
        'clockwise', 'anticlockwise'
    ] = 'anticlockwise',
) -> ndarray

Compute azimuth angle (or bearing) of the section. By default, using anti-clockwise sense : 0 is toward North. 90 degrees is toward West.

Parameters:

Name Type Description Default

unit

str

Output unit. Defaults to "deg".

'deg'

output_direction

Literal['clockwise', 'anticlockwise']

Angle sense for output. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"

'anticlockwise'

Returns:

Type Description
ndarray

np.ndarray: array of the azimuth of each span.

Source code in src/mechaphlowers/entities/arrays.py
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
585
586
587
588
589
590
591
592
593
def get_azimuth(
    self,
    unit: str = "deg",
    output_direction: Literal[
        "clockwise", "anticlockwise"
    ] = "anticlockwise",
) -> np.ndarray:
    """Compute azimuth angle (or bearing) of the section.
    By default, using anti-clockwise sense : 0 is toward North. 90 degrees is toward West.

    Args:
        unit (str, optional): Output unit. Defaults to "deg".
        output_direction (Literal["clockwise", "anticlockwise"]): Angle sense for output. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"

    Returns:
        np.ndarray: array of the azimuth of each span.
    """
    self.geolocator._check_gps_available()
    line_angles_degrees = (
        Q_(self.data["line_angle"].to_numpy(), "rad").to("deg").m
    )
    azimuth_anticlockwise = get_azimuth_from_line_angles(
        line_angles_degrees,
        self.geolocator._azimuth_0,  # type: ignore[arg-type]
        input_unit="deg",
        output_unit=unit,
    )

    if output_direction == "anticlockwise":
        return azimuth_anticlockwise
    elif output_direction == "clockwise":
        return -azimuth_anticlockwise
    else:
        raise ValueError(
            f"output_direction should be 'clockwise' or 'anticlockwise', received {output_direction}"
        )

get_gps

get_gps() -> tuple[ndarray, ndarray]

Compute GPS coordinates for all pylons.

Requires set_starting_gps() or set_starting_lambert93() to have been called first.

Returns:

Type Description
tuple[ndarray, ndarray]

tuple[np.ndarray, np.ndarray]: (latitudes, longitudes) in decimal degrees.

Source code in src/mechaphlowers/entities/arrays.py
595
596
597
598
599
600
601
602
603
604
605
606
607
608
def get_gps(self) -> tuple[np.ndarray, np.ndarray]:
    """Compute GPS coordinates for all pylons.

    Requires set_starting_gps() or set_starting_lambert93() to have been called first.

    Returns:
        tuple[np.ndarray, np.ndarray]: (latitudes, longitudes) in decimal degrees.
    """
    line_angles_degrees = (
        Q_(self.data["line_angle"].to_numpy(), "rad").to("deg").m
    )
    return self.geolocator.get_gps(
        line_angles_degrees, self.data["span_length"].to_numpy()
    )

get_lambert93

get_lambert93() -> tuple[ndarray, ndarray]

Compute Lambert 93 coordinates for all pylons.

Requires set_starting_gps() or set_starting_lambert93() to have been called first.

Returns:

Type Description
tuple[ndarray, ndarray]

tuple[np.ndarray, np.ndarray]: (easting, northing) in Lambert 93 meters.

Source code in src/mechaphlowers/entities/arrays.py
610
611
612
613
614
615
616
617
618
619
620
621
622
623
def get_lambert93(self) -> tuple[np.ndarray, np.ndarray]:
    """Compute Lambert 93 coordinates for all pylons.

    Requires set_starting_gps() or set_starting_lambert93() to have been called first.

    Returns:
        tuple[np.ndarray, np.ndarray]: (easting, northing) in Lambert 93 meters.
    """
    line_angles_degrees = (
        Q_(self.data["line_angle"].to_numpy(), "rad").to("deg").m
    )
    return self.geolocator.get_lambert93(
        line_angles_degrees, self.data["span_length"].to_numpy()
    )

set_starting_gps

set_starting_gps(
    latitude_0: float,
    longitude_0: float,
    azimuth_0: float,
    azimuth_direction: Literal[
        'clockwise', 'anticlockwise'
    ] = 'anticlockwise',
) -> None

Set the starting GPS point and azimuth for coordinate computation.

Parameters:

Name Type Description Default

latitude_0

float

Latitude of the first support in decimal degrees.

required

longitude_0

float

Longitude of the first support in decimal degrees.

required

azimuth_0

float

Azimuth of the first span in degrees, anti-clockwise by default. 0 means North, 90 means West.

required

azimuth_direction

Literal['clockwise', 'anticlockwise']

Angle sense for azimuth_0. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"

'anticlockwise'
Source code in src/mechaphlowers/entities/arrays.py
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
def set_starting_gps(
    self,
    latitude_0: float,
    longitude_0: float,
    azimuth_0: float,
    azimuth_direction: Literal[
        "clockwise", "anticlockwise"
    ] = "anticlockwise",
) -> None:
    """Set the starting GPS point and azimuth for coordinate computation.

    Args:
        latitude_0 (float): Latitude of the first support in decimal degrees.
        longitude_0 (float): Longitude of the first support in decimal degrees.
        azimuth_0 (float): Azimuth of the first span in degrees, anti-clockwise by default. 0 means North, 90 means West.
        azimuth_direction (Literal["clockwise", "anticlockwise"]): Angle sense for azimuth_0. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"
    """
    if azimuth_direction == "clockwise":
        self.geolocator.set_starting_gps(
            latitude_0, longitude_0, -azimuth_0
        )
    elif azimuth_direction == "anticlockwise":
        self.geolocator.set_starting_gps(
            latitude_0, longitude_0, azimuth_0
        )
    else:
        raise ValueError(
            f"azimuth_direction should be 'clockwise' or 'anticlockwise', received {azimuth_direction}"
        )

set_starting_lambert93

set_starting_lambert93(
    easting: float,
    northing: float,
    azimuth_0: float,
    azimuth_direction: Literal[
        'clockwise', 'anticlockwise'
    ] = 'anticlockwise',
) -> None

Set the starting point from Lambert 93 coordinates and azimuth.

Parameters:

Name Type Description Default

easting

float

Lambert 93 easting coordinate in meters.

required

northing

float

Lambert 93 northing coordinate in meters.

required

azimuth_0

float

Azimuth of the first span in degrees, anti-clockwise. 0 means North, 90 means West.

required

azimuth_direction

Literal['clockwise', 'anticlockwise']

Angle sense for azimuth_0. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"

'anticlockwise'
Source code in src/mechaphlowers/entities/arrays.py
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
def set_starting_lambert93(
    self,
    easting: float,
    northing: float,
    azimuth_0: float,
    azimuth_direction: Literal[
        "clockwise", "anticlockwise"
    ] = "anticlockwise",
) -> None:
    """Set the starting point from Lambert 93 coordinates and azimuth.

    Args:
        easting (float): Lambert 93 easting coordinate in meters.
        northing (float): Lambert 93 northing coordinate in meters.
        azimuth_0 (float): Azimuth of the first span in degrees, anti-clockwise. 0 means North, 90 means West.
        azimuth_direction (Literal["clockwise", "anticlockwise"]): Angle sense for azimuth_0. If set to "clockwise": 90 means East, -90 means West. Default to "anticlockwise"
    """

    if azimuth_direction == "clockwise":
        self.geolocator.set_starting_lambert93(
            easting, northing, -azimuth_0
        )
    elif azimuth_direction == "anticlockwise":
        self.geolocator.set_starting_lambert93(
            easting, northing, azimuth_0
        )
    else:
        raise ValueError(
            f"azimuth_direction should be 'clockwise' or 'anticlockwise', received {azimuth_direction}"
        )

WeatherArray

WeatherArray(data: DataFrame)

Bases: ElementArray

Weather-related data, such as wind and ice.

They're typically used to compute weather-related loads on the cable.

Source code in src/mechaphlowers/entities/arrays.py
871
872
873
874
875
876
877
878
def __init__(
    self,
    data: pd.DataFrame,
) -> None:
    super().__init__(data)  # type: ignore[arg-type]
    self.input_units: dict[str, str] = {
        "ice_thickness": "cm",
    }

data property

data: DataFrame

Returns a copy of self._data that converts values into SI units

data_original property

data_original: DataFrame

Original dataframe with the exact same data as input: original units and no columns added

add_units

add_units(input_units: dict[str, str]) -> None

Add dictionary of units of the data input . This will overrides the default input_units dict

input_units has the following format:

1
2
3
4
{
    "column_name_0": "unit0",
    "column_name_1": "unit1",
}

Parameters:

Name Type Description Default

input_units

dict[str, str]

dictionary of columns names and corresponding units

required
Source code in src/mechaphlowers/entities/arrays.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def add_units(self, input_units: dict[str, str]) -> None:
    """Add dictionary of units of the data input . This will overrides the default `input_units` dict

    `input_units` has the following format:
    ```py
    {
        "column_name_0": "unit0",
        "column_name_1": "unit1",
    }
    ```

    Args:
        input_units (dict[str, str]): dictionary of columns names and corresponding units
    """
    self.input_units.update(input_units)