Skip to content

measures

Measurung module

This module provides functions to compute various measures on sections and spans.

PapotoParameterMeasure

Bases: ParameterMeasure

Class to compute PAPOTO parameter measures.

check_validity

check_validity()

Check the validity of the measure.

Source code in src/mechaphlowers/data/measures.py
195
196
197
def check_validity(self):
    """Check the validity of the measure."""
    return self._validity < self.validity_criteria

compute_papoto

compute_papoto(
    measures_converted,
) -> tuple[ndarray, ndarray, ndarray, ndarray, ndarray]

Compute the PAPOTO parameter and validity from the converted measures.

Assumes that measures_converted contains the following keys: 'a', 'HL', 'VL', 'HR', 'VR', 'H1', 'V1', 'H2', 'V2', 'H3', 'V3'.

Parameters:

Name Type Description Default

measures_converted

dict

dictionary of converted measures

required

Returns:

Name Type Description
mean_parameter ndarray

mean of the three PAPOTO parameters computed from the three pairs of points (1-2, 2-3, 1-3)

validity ndarray

validity criteria computed from the three PAPOTO parameters

parameter_1_2 ndarray

PAPOTO parameter computed from points 1 and 2

parameter_2_3 ndarray

PAPOTO parameter computed from points 2 and 3

parameter_1_3 ndarray

PAPOTO parameter computed from points 1 and 3

Source code in src/mechaphlowers/data/measures.py
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
155
156
157
158
159
160
161
162
163
164
165
def compute_papoto(
    self, measures_converted
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """Compute the PAPOTO parameter and validity from the converted measures.

    Assumes that measures_converted contains the following keys: 'a', 'HL', 'VL', 'HR', 'VR', 'H1', 'V1', 'H2', 'V2', 'H3', 'V3'.

    Args:
        measures_converted (dict): dictionary of converted measures

    Returns:
            mean_parameter (np.ndarray): mean of the three PAPOTO parameters computed from the three pairs of points (1-2, 2-3, 1-3)
            validity (np.ndarray): validity criteria computed from the three PAPOTO parameters
            parameter_1_2 (np.ndarray): PAPOTO parameter computed from points 1 and 2
            parameter_2_3 (np.ndarray): PAPOTO parameter computed from points 2 and 3
            parameter_1_3 (np.ndarray): PAPOTO parameter computed from points 1 and 3
    """

    parameter_1_2 = papoto_2_points(
        **self.select_points_in_dict(1, 2, measures_converted)
    )
    parameter_2_3 = papoto_2_points(
        **self.select_points_in_dict(2, 3, measures_converted)
    )
    parameter_1_3 = papoto_2_points(
        **self.select_points_in_dict(1, 3, measures_converted)
    )
    mean_parameter = np.mean(
        np.array([parameter_1_2, parameter_2_3, parameter_1_3]),
        axis=0,
    )
    validity = papoto_validity(parameter_1_2, parameter_2_3, parameter_1_3)
    return (
        mean_parameter,
        validity,
        parameter_1_2,
        parameter_2_3,
        parameter_1_3,
    )

input_conversion

input_conversion(data: dict) -> dict

Convert inputs to the required format.

Source code in src/mechaphlowers/data/measures.py
185
186
187
188
189
def input_conversion(self, data: dict) -> dict:
    """Convert inputs to the required format."""
    for key, value in data.items():
        data[key] = Q_(value, self.angle_unit).to("rad").magnitude
    return data

measure_method

measure_method(
    a: ndarray | float | int,
    HL: ndarray | float | int,
    VL: ndarray | float | int,
    HR: ndarray | float | int,
    VR: ndarray | float | int,
    H1: ndarray | float | int,
    V1: ndarray | float | int,
    H2: ndarray | float | int,
    V2: ndarray | float | int,
    H3: ndarray | float | int,
    V3: ndarray | float | int,
    angle_unit: str = 'grad',
)

Compute the PAPOTO measure.

Parameters:

Name Type Description Default

a

ndarray

Length of the span

required

HL

ndarray

horizontal angle of the left part of the span

required

VL

ndarray

vertical angle of the left part of the span

required

HR

ndarray

horizontal angle of the right part of the span

required

VR

ndarray

vertical angle of the right part of the span

required

H1

ndarray

horizontal angle of point 1

required

V1

ndarray

vertical angle of point 1

required

H2

ndarray

horizontal angle of point 2

required

V2

ndarray

vertical angle of point 2

required

H3

ndarray

horizontal angle of point 3

required

V3

ndarray

vertical angle of point 3

required

Returns: None

Source code in src/mechaphlowers/data/measures.py
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def measure_method(
    self,
    a: np.ndarray | float | int,
    HL: np.ndarray | float | int,
    VL: np.ndarray | float | int,
    HR: np.ndarray | float | int,
    VR: np.ndarray | float | int,
    H1: np.ndarray | float | int,
    V1: np.ndarray | float | int,
    H2: np.ndarray | float | int,
    V2: np.ndarray | float | int,
    H3: np.ndarray | float | int,
    V3: np.ndarray | float | int,
    angle_unit: str = "grad",
):
    """Compute the PAPOTO measure.

    Args:
        a (np.ndarray): Length of the span
        HL (np.ndarray): horizontal angle of the left part of the span
        VL (np.ndarray): vertical angle of the left part of the span
        HR (np.ndarray): horizontal angle of the right part of the span
        VR (np.ndarray): vertical angle of the right part of the span
        H1 (np.ndarray): horizontal angle of point 1
        V1 (np.ndarray): vertical angle of point 1
        H2 (np.ndarray): horizontal angle of point 2
        V2 (np.ndarray): vertical angle of point 2
        H3 (np.ndarray): horizontal angle of point 3
        V3 (np.ndarray): vertical angle of point 3
    Returns:
        None
    """
    logger.debug("Start running PapotoParameterMeasure.measure_method()")

    self.a = a
    self._base_measures = {
        "HL": HL,
        "VL": VL,
        "HR": HR,
        "VR": VR,
        "H1": H1,
        "V1": V1,
        "H2": H2,
        "V2": V2,
        "H3": H3,
        "V3": V3,
    }

    self.measures = float_to_array(dict(self._base_measures))
    self.angle_unit = angle_unit
    measures_converted = self.input_conversion(self.measures)
    measures_converted["a"] = a

    (
        self._parameter,
        self._validity,
        self.parameter_1_2,
        self.parameter_2_3,
        self.parameter_1_3,
    ) = self.compute_papoto(measures_converted)
    logger.debug("PapotoParameterMeasure.measure_method() ended")

select_points_in_dict staticmethod

select_points_in_dict(point_1, point_2, data)

Select points from the input array.

Source code in src/mechaphlowers/data/measures.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
@staticmethod
def select_points_in_dict(point_1, point_2, data):
    """Select points from the input array."""
    output_data = {
        key: value
        for key, value in data.items()
        if key not in ('H1', 'V1', 'H2', 'V2', 'H3', 'V3')
    }
    output_data["H1"], output_data["V1"] = (
        data[f"H{point_1}"],
        data[f"V{point_1}"],
    )
    output_data["H2"], output_data["V2"] = (
        data[f"H{point_2}"],
        data[f"V{point_2}"],
    )
    return output_data

uncertainty

uncertainty(
    draw_number: int = 1000,
    angle_error: float = 0.01,
    seed: int = 42,
) -> dict

Estimate uncertainty on the PAPOTO parameter using Monte Carlo method.

Parameters:

Name Type Description Default

draw_number

int

Number of Monte Carlo random draws. Defaults to 1000.

1000

angle_error

float

Magnitude of angle measurement error (uniform in [-angle_error, +angle_error]). Defaults to 0.01.

0.01

Returns:

Name Type Description
dict dict

Dictionary with statistics over valid and non-valid draws.

Raises:

Type Description
MeasurementDataNotAvailable

If measure_method() has not been called first.

Source code in src/mechaphlowers/data/measures.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
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
def uncertainty(
    self,
    draw_number: int = 1000,
    angle_error: float = 0.01,
    seed: int = 42,
) -> dict:
    """Estimate uncertainty on the PAPOTO parameter using Monte Carlo method.

    Args:
        draw_number (int): Number of Monte Carlo random draws. Defaults to 1000.
        angle_error (float): Magnitude of angle measurement error (uniform in
            [-angle_error, +angle_error]). Defaults to 0.01.

    Returns:
        dict: Dictionary with statistics over valid and non-valid draws.

    Raises:
        MeasurementDataNotAvailable: If measure_method() has not been called first.
    """

    # ===== Check inputs =====

    if not hasattr(self, 'measures'):
        raise MeasurementDataNotAvailable(
            "measure_method() must be called before uncertainty()."
        )

    self._validate_inputs_uncertainty(draw_number, angle_error)

    _draw_number: int = int(draw_number)
    _angle_error: float = float(angle_error)  # type: ignore[arg-type]

    # ===== Monte Carlo simulation =====

    rng = np.random.default_rng(seed)
    angle_keys = self._base_measures.keys()

    perturbed_converted: dict = {}
    for key in angle_keys:
        random_angle = (
            _angle_error * 2 * rng.random(_draw_number) - _angle_error
        )
        perturbed_converted[key] = (
            np.asarray(self._base_measures[key], dtype=float)
            + random_angle
        )

    self.input_conversion(perturbed_converted)
    perturbed_converted['a'] = self.a

    # Save intermediates from measure_method, compute_papoto overwrites them
    perturbed_parameter, validity, _, _, _ = self.compute_papoto(
        perturbed_converted
    )

    # ===== Post processing results =====
    results = self._post_process_uncertainty(perturbed_parameter, validity)

    return results

ParameterMeasure

Bases: ABC

Class to compute measures on parameters.

parameter abstractmethod property

parameter

Property to get the computed parameter.

validity property

validity

Method to compute validity criteria.

check_validity

check_validity()

Method to check validity of the measure.

Source code in src/mechaphlowers/data/measures.py
49
50
51
def check_validity(self):
    """Method to check validity of the measure."""
    raise NotImplementedError("Check validity method not implemented.")

measure_method abstractmethod

measure_method(*args, **kwargs)

Abstract method to be implemented by subclasses.

Source code in src/mechaphlowers/data/measures.py
35
36
37
38
@abstractmethod
def measure_method(self, *args, **kwargs):
    """Abstract method to be implemented by subclasses."""
    pass

uncertainty

uncertainty(*args, **kwargs)

Method to compute uncertainty.

Source code in src/mechaphlowers/data/measures.py
40
41
42
def uncertainty(self, *args, **kwargs):
    """Method to compute uncertainty."""
    raise NotImplementedError("Uncertainty method not implemented.")

param_calibration

Compute an approximation of the parameter at sagging temperature (usually 15 degrees Celsius), based on the measured parameter, at a given temperature.

This approximation is computed using only one loop of a Newton-Raphson method, more precision is not needed.

This method only works for one span at a time, so span index must be provided.

Parameters:

Name Type Description Default

measured_parameter

float

parameter value measured, using papoto method usually

required

measured_temperature

float

temperature at which the parameter was measured

required

section_array

SectionArray

Section array

required

cable_array

CableArray

Cable array

required

span_index

int

index of the span to compute the parameter for

required
Source code in src/mechaphlowers/data/measures.py
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
def param_calibration(
    measured_parameter: float,
    measured_temperature: float,
    section_array: SectionArray,
    cable_array: CableArray,
    span_index: int,
    sagging_temperature: float = 15.0,
) -> float:
    """Compute an approximation of the parameter at sagging temperature (usually 15 degrees Celsius), based on the measured parameter, at a given temperature.

    This approximation is computed using only one loop of a Newton-Raphson method, more precision is not needed.

    This method only works for one span at a time, so span index must be provided.

    Args:
        measured_parameter (float): parameter value measured, using papoto method usually
        measured_temperature (float): temperature at which the parameter was measured
        section_array (SectionArray): Section array
        cable_array (CableArray): Cable array
        span_index (int): index of the span to compute the parameter for
    """

    logger.debug("Start running param_calibration()")
    _ZETA = options.solver.param_calibration_zeta

    def compute_parameter(
        sagging_parameter: float,
        sagging_temperature: float,
        new_temperature: float,
    ):
        section_array.sagging_parameter = sagging_parameter
        section_array.sagging_temperature = sagging_temperature
        balance_engine = BalanceEngine(
            cable_array=cable_array, section_array=section_array
        )
        balance_engine.solve_adjustment()
        balance_engine.solve_change_state(new_temperature=new_temperature)
        return balance_engine.parameter[span_index]

    # first estimation of the parameter at sagging temperature
    param_approx = compute_parameter(
        measured_parameter, measured_temperature, sagging_temperature
    )

    # computing the delta function to be zeroed
    parameter_mes_0 = compute_parameter(
        param_approx, sagging_temperature, measured_temperature
    )
    delta = parameter_mes_0 - measured_parameter

    # computing derivative by finite difference
    parameter_mes_1 = compute_parameter(
        param_approx + _ZETA, sagging_temperature, measured_temperature
    )
    delta_1 = parameter_mes_1 - measured_parameter
    derivative = (delta_1 - delta) / _ZETA

    # derivative == 0 means we got the exact solution, avoid division by zero
    if derivative != 0:
        # newton-raphson update
        param_approx = param_approx - delta / derivative
    logger.debug(f"param_calibration() finished, param found: {param_approx}")
    return param_approx