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
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
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.

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
663
664
665
666
667
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
687
688
689
690
691
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
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
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
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
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
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
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
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
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
143
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
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.correct_insulator_length()
    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

compute_ground_altitude() -> ndarray

Generate ground altitude array using attachment altitude, and arbitrary a support length.

Source code in src/mechaphlowers/entities/arrays.py
210
211
212
213
214
215
def compute_ground_altitude(self) -> np.ndarray:
    """Generate ground altitude array using attachment altitude, and arbitrary a support length."""
    return (
        self._data["conductor_attachment_altitude"].to_numpy()
        - options.ground.default_support_length
    )

correct_insulator_length

correct_insulator_length() -> None

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

Source code in src/mechaphlowers/entities/arrays.py
217
218
219
220
221
222
223
224
225
226
def correct_insulator_length(self) -> None:
    """Correct insulator length to be at least 0.01 m to avoid numerical issues."""
    if (self._data["insulator_length"] < 0.01).any():
        warnings.warn(
            "Some insulator_length values are less than 0.01 m. They will be set to 0.01 m to avoid numerical issues.",
            category=DataWarning,
        )
    self._data["insulator_length"] = self._data["insulator_length"].apply(
        lambda x: max(x, 0.01)
    )

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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
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
484
485
486
487
488
489
490
491
492
493
494
495
496
497
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
499
500
501
502
503
504
505
506
507
508
509
510
511
512
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
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
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
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
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
753
754
755
756
757
758
759
760
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)