Skip to content

Commit 6ae6f6d

Browse files
committed
voltage sensor angle residual mod 2pi
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
1 parent 81e0334 commit 6ae6f6d

File tree

3 files changed

+46
-3
lines changed

3 files changed

+46
-3
lines changed

docs/user_manual/components.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,10 +627,12 @@ A sensor only has output for state estimation. For other calculation types, sens
627627
$$
628628
\begin{eqnarray}
629629
& u_{\text{residual}} = u_{\text{measured}} - u_{\text{state}} \\
630-
& \theta_{\text{residual}} = \theta_{\text{measured}} - \theta_{\text{state}}
630+
& \theta_{\text{residual}} = \theta_{\text{measured}} - \theta_{\text{state}} \pmod{2 \pi}
631631
\end{eqnarray}
632632
$$
633633

634+
The $\pmod{2\pi}$ is handled such that $-\pi \lt \theta_{\text{angle},\text{residual}} \leq \pi$.
635+
634636
### Generic Power Sensor
635637

636638
* type name: `generic_power_sensor`

power_grid_model_c/power_grid_model/include/power_grid_model/component/voltage_sensor.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ template <symmetry_tag sym> class VoltageSensor : public GenericVoltageSensor {
151151
} else {
152152
value.u_residual = (real(u1_measured) - cabs(u)) * u_rated_;
153153
}
154-
value.u_angle_residual = arg(u1_measured) - arg(u);
154+
value.u_angle_residual = arg(ComplexValue<symmetric_t>{exp(1.0i * (arg(u1_measured) - arg(u)))});
155155
return value;
156156
}
157157

@@ -160,7 +160,7 @@ template <symmetry_tag sym> class VoltageSensor : public GenericVoltageSensor {
160160
value.id = id();
161161
value.energized = 1;
162162
value.u_residual = (u_measured_ - cabs(u)) * u_rated_ / sqrt3;
163-
value.u_angle_residual = u_angle_measured_ - arg(u);
163+
value.u_angle_residual = arg(ComplexValue<asymmetric_t>{exp(1.0i * (u_angle_measured_ - arg(u)))});
164164
return value;
165165
}
166166
};

tests/cpp_unit_tests/test_voltage_sensor.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,47 @@ TEST_CASE("Test voltage sensor") {
363363
CHECK(sym_voltage_sensor_asym_output.u_angle_residual[2] == doctest::Approx(-0.2));
364364
}
365365

366+
SUBCASE("Angle = ± pi ∓ 0.1") {
367+
RealValue<symmetric_t> const u_measured{10.1e3};
368+
RealValue<symmetric_t> const u_angle_measured{pi - 0.1};
369+
double const u_sigma = 1.0;
370+
double const u_rated = 10.0e3;
371+
372+
VoltageSensorInput<symmetric_t> voltage_sensor_input{};
373+
voltage_sensor_input.id = 0;
374+
voltage_sensor_input.measured_object = 1;
375+
voltage_sensor_input.u_sigma = u_sigma;
376+
voltage_sensor_input.u_measured = u_measured;
377+
voltage_sensor_input.u_angle_measured = u_angle_measured;
378+
379+
VoltageSensor<symmetric_t> const voltage_sensor{voltage_sensor_input, u_rated};
380+
381+
ComplexValue<symmetric_t> const u_calc_sym{1.02 * exp(1i * (-pi + 0.1))};
382+
VoltageSensorOutput<symmetric_t> sym_voltage_sensor_sym_output =
383+
voltage_sensor.get_output<symmetric_t>(u_calc_sym);
384+
385+
ComplexValue<asymmetric_t> const u_calc_asym{1.02 * exp(1i * (-pi + 0.1)), 1.03 * exp(1i * (-pi + 0.2)),
386+
1.04 * exp(1i * (-pi + 0.3))};
387+
VoltageSensorOutput<asymmetric_t> sym_voltage_sensor_asym_output =
388+
voltage_sensor.get_output<asymmetric_t>(u_calc_asym);
389+
390+
// Check sym output
391+
CHECK(sym_voltage_sensor_sym_output.id == 0);
392+
CHECK(sym_voltage_sensor_sym_output.energized == 1);
393+
CHECK(sym_voltage_sensor_sym_output.u_residual == doctest::Approx(-100.0));
394+
CHECK(sym_voltage_sensor_sym_output.u_angle_residual == doctest::Approx(-0.2).epsilon(1e-12));
395+
396+
// Check asym output
397+
CHECK(sym_voltage_sensor_asym_output.id == 0);
398+
CHECK(sym_voltage_sensor_asym_output.energized == 1);
399+
CHECK(sym_voltage_sensor_asym_output.u_residual[0] == doctest::Approx(-100.0 / sqrt3));
400+
CHECK(sym_voltage_sensor_asym_output.u_residual[1] == doctest::Approx(-200.0 / sqrt3));
401+
CHECK(sym_voltage_sensor_asym_output.u_residual[2] == doctest::Approx(-300.0 / sqrt3));
402+
CHECK(sym_voltage_sensor_asym_output.u_angle_residual[0] == doctest::Approx(-0.2).epsilon(1e-12));
403+
CHECK(sym_voltage_sensor_asym_output.u_angle_residual[1] == doctest::Approx(-0.3));
404+
CHECK(sym_voltage_sensor_asym_output.u_angle_residual[2] == doctest::Approx(-0.4));
405+
}
406+
366407
SUBCASE("Angle = nan") {
367408
RealValue<symmetric_t> const u_measured{10.1e3};
368409
RealValue<symmetric_t> const u_angle_measured{nan};

0 commit comments

Comments
 (0)