From eb7a4cebdb75a14b1027f6701b17fbf5dcf464db Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Fri, 5 Jun 2026 09:00:48 +0200 Subject: [PATCH 01/13] experiment on split docs Signed-off-by: zhen0427 --- .github/workflows/build-docs.yml | 37 +++ .gitignore | 4 +- .../{components.md => 001-components.md} | 265 ----------------- docs/user_manual/002-components.md | 272 ++++++++++++++++++ scripts/build-docs.py | 29 ++ 5 files changed, 341 insertions(+), 266 deletions(-) create mode 100644 .github/workflows/build-docs.yml rename docs/user_manual/{components.md => 001-components.md} (82%) create mode 100644 docs/user_manual/002-components.md create mode 100644 scripts/build-docs.py diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 0000000000..1d161f2715 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +name: Build Documentation + +on: + push: + paths: + - "docs/**" + - "scripts/**" + pull_request: + paths: + - "docs/**" + - "scripts/**" + +jobs: + build-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Build docs + run: python scripts/build_docs.py + + - name: Upload generated docs + uses: actions/upload-artifact@v4 + with: + name: user-manual-docs + path: docs/user_manual/components.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 67ac5313de..210c45b0c4 100644 --- a/.gitignore +++ b/.gitignore @@ -519,4 +519,6 @@ docs/jupyter_execute/ # git add .vscode/ --force .vscode/ -wheelhouse/ \ No newline at end of file +wheelhouse/ + +docs/user_manual/components.md \ No newline at end of file diff --git a/docs/user_manual/components.md b/docs/user_manual/001-components.md similarity index 82% rename from docs/user_manual/components.md rename to docs/user_manual/001-components.md index ee34cd978c..94da192e06 100644 --- a/docs/user_manual/components.md +++ b/docs/user_manual/001-components.md @@ -1056,268 +1056,3 @@ $$ $$ The $\pmod{2\pi}$ is handled such that $-\pi \lt i_{\text{angle},\text{residual}} \leq \pi$. - -## Fault - -* type name: `fault` -* base: [base](#base) - -`fault` defines a short circuit location in the grid. -A fault can only happen at a `node`. - -#### Input - -| name | data type | unit | description | required | update | valid values | -|----------------|-----------------------------------------------------------|---------|-----------------------------------------------------|:-------------------------------------------------------------------------------------------------------:|:--------:|:-----------------:| -| `status` | `int8_t` | - | whether the fault is active | ✔ | ✔ | `0` or `1` | -| `fault_type` | {py:class}`FaultType ` | - | the type of the fault | ✨ only for short circuit | ✔ | | -| `fault_phase` | {py:class}`FaultPhase ` | - | the phase(s) of the fault | ❌ default `FaultPhase.default_value` (see [below](#fault-types-fault-phases-and-default-values)) | ✔ | | -| `fault_object` | `int32_t` | - | ID of the component where the short circuit happens | ✔ | ✔ | A valid `node` ID | -| `r_f` | `double` | ohm (Ω) | short circuit resistance | ❌ default `0.0` | ✔ | | -| `x_f` | `double` | ohm (Ω) | short circuit reactance | ❌ default `0.0` | ✔ | | - -```{note} -Multiple faults may exist within one calculation. -Currently, all faults in one scenario are required to have the same `fault_type` and `fault_phase`. -Across scenarios in a batch, the `fault_type` and `fault_phase` may differ. -``` - -```{note} -If any of the faults in any of the scenarios within a batch are not `three_phase` (i.e. `fault_type` is not -`FaultType.three_phase`), the calculation is treated as asymmetric. -``` - -#### Steady state output - -A `fault` has no steady state output. - -#### Short circuit output - -| name | data type | unit | description | -|-------------|-------------------|------------|---------------| -| `i_f` | `RealValueOutput` | ampere (A) | current | -| `i_f_angle` | `RealValueOutput` | rad | current angle | - -#### Electric Model - -##### Fault types, fault phases and default values - -Four types of short circuit fault are included in power-grid-model, each with their own set of supported values for -`fault_phase`. -In case the `fault_phase` is not specified or is equal to `FaultPhase.default_value`, the power-grid-model assumes a -`fault_type`-dependent set of fault phases. -The supported values of `fault_phase`, as well as its default value, are listed in the table below. - -| `fault_type` | supported values of `fault_phase` | `FaultPhase.default_value` | description | -|------------------------------------|---------------------------------------------------|----------------------------|------------------------------------------------------------------------| -| `FaultType.three_phase` | `FaultPhase.abc` | `FaultPhase.abc` | Three phases are connected with fault impedance. | -| `FaultType.single_phase_to_ground` | `FaultPhase.a`, `FaultPhase.b`, `FaultPhase.c` | `FaultPhase.a` | One phase is grounded with fault impedance, and other phases are open. | -| `FaultType.two_phase` | `FaultPhase.bc`, `FaultPhase.ac`, `FaultPhase.ab` | `FaultPhase.bc` | Two phases are connected with fault impedance. | -| `FaultType.two_phase_to_ground` | `FaultPhase.bc`, `FaultPhase.ac`, `FaultPhase.ab` | `FaultPhase.bc` | Two phases are connected with fault impedance then grounded. | - -## Regulator - -* type name: `regulator` -* base: [base](#base) - -`regulator` is an abstract regulator that is coupled to a given `regulated_object`. For each `regulator`, a switch is -defined between the `regulator` and the `regulated_object`. -Which object types are supported as `regulated_object` is regulator type-dependent. - -#### Input - -| name | data type | unit | description | required | update | valid values | -|--------------------|-----------|------|-------------------------------------------|:--------:|:--------:|:---------------------------:| -| `regulated_object` | `int32_t` | - | ID of the regulated object | ✔ | ❌ | a valid regulated object ID | -| `status` | `int8_t` | - | connection status to the regulated object | ✔ | ✔ | `0` or `1` | - -### Transformer tap regulator - -* type name: `transformer_tap_regulator` -* base: [regulator](#regulator) - -`transformer_tap_regulator` defines a regulator for transformers in the grid. -A transformer tap regulator regulates a component that is either a -[transformer](#transformer) or a -[Three-Winding Transformer](#three-winding-transformer). - -The transformer tap regulator changes the `tap_pos` of the transformer it regulates in the range set by the user via -`tap_min` and `tap_max` (i.e., `(tap_min <= tap_pos <= tap_max)` or `(tap_min >= tap_pos >= tap_max)`). -It regulates the tap position so that the voltage on the control side is in the chosen voltage band. -Other points further into the grid on the control side, away from the transformer, can also be regulated by providing -the cumulative impedance across branches to that point as an additional line drop compensation. -This line drop compensation only affects the controlled voltage and does not have any impact on the actual grid. -It may therefore be treated as a virtual impedance in the grid. - -```{note} -The regulator outputs the optimal tap position of the transformer. -The actual grid state is not changed after calculations are done. -``` - -#### Input - -| name | data type | unit | description | required | update | valid values | -|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------|:----------------------------:|:--------:|:----------------------------------------------------------------:| -| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](#transformer) and {py:class}`Branch3Side ` if it the regulated object is a [Three-Winding Transformer](#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | -| `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔ | `>= 0` | -| `u_band` | `double` | volt (V) | the width of the voltage band ($=2*\left(\Delta U\right)_{\text{acceptable}}$) | ✨ only for power flow | ✔ | `> 0` (see below) | -| `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | -| `line_drop_compensation_x` | `double` | ohm (Ω) | compensation for voltage drop due to reactance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | - -The following additional requirements exist on the input parameters. - -* Depending on the resultant voltage being transformed, the voltage band must be sufficiently large: At zero line drop - compensation if the expected resultant voltages are in the proximity of the rated transformer voltages, it is - recommended to have the $u_{band} >= \frac{u_2}{1+ u_1 / \text{tap}_{\text{size}}}$ -* The line drop compensation is small, in the sense that its product with the typical current through the transformer is - much smaller (in absolute value) than the smallest change in voltage due to a change in tap position. -* The `control_side` of a transformer regulator should be on the relatively further side to a source in the connected - grid. - -These requirements make sure no edge cases with undefined behavior are encountered. -Typical real-world power grids already satisfy these requirements and they should therefore not cause any problems. - -#### Steady state output - -| name | data type | unit | description | -|-----------|-----------|------|----------------------| -| `tap_pos` | `int8_t` | - | optimal tap position | - -#### Short circuit output - -A `transformer_tap_regulator` has no short circuit output. - -#### Electric Model - -The transformer tap regulator itself does not have a direct contribution to the grid state. -Instead, it regulates the tap position of the regulated object until the voltage at the control side is in the specified -voltage band: - -$$ -U_{\text{control}} \in - \left[U_{\text{set}} - \frac{U_{\text{band}}}{2}, U_{\text{set}} + \frac{U_{\text{band}}}{2}\right] -$$ - -##### Line drop compensation - -The transformer tap regulator tries to regulate the voltage in a specified virtual location in the grid, according to -the folowing model. - -```txt -tap_side control_side part of grid where voltage is to be regulated -------^\oo -*---------------------virtual_impedance----* - | U_node, I_transformer Z_comp U_control - | | - regulator <=======================================/ -``` - -The control voltage is the voltage at the node, compensated with the voltage drop corresponding to the specified line -drop compensation. - -$$ -\begin{aligned} - Z_{\text{compensation}} &= r_{\text{compensation}} + \mathrm{j} x_{\text{compensation}} \\ - U_{\text{control}} &= \left|\underline{U}_{\text{node}} - \underline{I}_{\text{transformer,out}} - \cdot \underline{Z}_{\text{compensation}}\right| - = \left|\underline{U}_{\text{node}} + \underline{I}_{\text{transformer}} - \cdot \underline{Z}_{\text{compensation}}\right| -\end{aligned} -$$ - -where $\underline{U}_{\text{node}}$ and $\underline{I}_{\text{transformer}}$ are the calculated voltage and current -phasors at the control side and may be obtained from a regular power flow calculation. -The plus sign in the last equality follows from canceling minus signs from the current direction convention and the -compensation direction. - -For example, if we want to regulate the voltage at `load_7` in the following grid, the line drop compensation impedance -is the approximate impedance of `line_5`. - -```txt -node_1 --- transformer_4 --- node_2 --- line_5 --- node_3 - | | | -source_6 | load_7 - transformer_tap_regulator_8 -``` - -### Voltage Regulator - -* type name: `voltage_regulator` -* base: {hoverxreftooltip}`user_manual/components:regulator` - -`voltage_regulator` defines a regulator for voltage-controlled generators in the grid. -A voltage regulator adjusts the reactive power output of a generator to maintain the voltage at its connection node -at a specified setpoint. - -The voltage regulator changes the reactive power output of the generator it regulates to achieve the reference voltage -`u_ref` at the generator's node. -If `q_min` and `q_max` are provided, the reactive power is constrained within this range (i.e., `q_min <= q <= q_max` or -`q_min >= q >= q_max`). -If these limits are not provided, the reactive power can take any value needed to maintain the voltage setpoint. - -```{warning} -Voltage regulation is only supported by the [Newton-Raphson power flow](./calculations.md#newton-raphson-power-flow) method. -``` - -```{note} -The `regulated_object` must reference a generator (`sym_gen` or `asym_gen`) or a load (`sym_load` or `asym_load`). -Each generator or load can have at most one voltage regulator. -When multiple voltage-regulated generators are connected to the same node, they should all specify the same `u_ref` -value to avoid conflicting voltage setpoints. -``` - -```{warning} -Reactive power limit checking is not yet fully implemented. When `q_min` and `q_max` are specified, -the intended behavior is that if the required reactive power to maintain `u_ref` exceeds these limits, -the voltage regulator should operate at the limit and the voltage may deviate from `u_ref`. -``` - -#### Input - -| name | data type | unit | description | required | update | valid values | -| -------- | --------- | -------------------------- | --------------------------------------------------- | :--------------------------: | :------: | :----------: | -| `u_ref` | `double` | - | reference voltage in per-unit at the generator node | ✨ only for power flow | ✔ | `> 0` | -| `q_min` | `double` | volt-ampere-reactive (var) | minimum reactive power limit of the generator | ❌ | ✔ | | -| `q_max` | `double` | volt-ampere-reactive (var) | maximum reactive power limit of the generator | ❌ | ✔ | | - -#### Steady state output - -| name | data type | unit | description | -| ----------------- | ----------------- | -------------------------- | -------------------------------------------------------------------- | -| `q` | `RealValueOutput` | volt-ampere-reactive (var) | reactive power provided by the voltage regulator | -| `limit_violated` | `int8_t` | - | reactive power limit violation indicator (not yet fully implemented) | - -#### Short circuit output - -A `voltage_regulator` has no short circuit output. - -#### Electric Model - -The voltage regulator controls the generator to behave as a **PV node** in power flow calculations: - -$$ -\begin{aligned} - P_{\text{gen}} &= P_{\text{specified}} \\ - \left|U_{\text{node}}\right| &= U_{\text{ref}} \\ - Q_{\text{gen}} &= \text{calculated to satisfy } U_{\text{ref}} -\end{aligned} -$$ - -When `q_min` and `q_max` are provided, the reactive power should be constrained: - -$$ - Q_{\text{min}} \leq Q_{\text{gen}} \leq Q_{\text{max}} -$$ - -When fully implemented, if the reactive power constraints are violated, the generator will operate at the limit and the -node becomes a PQ node: - -$$ -\begin{aligned} - P_{\text{gen}} &= P_{\text{specified}} \\ - Q_{\text{gen}} &= Q_{\text{min}} \text{ or } Q_{\text{max}} \\ - \left|U_{\text{node}}\right| &= \text{calculated from power flow} -\end{aligned} -$$ - -In this case, `limit_violated` will indicate which limit was exceeded, and the actual voltage at the node may differ -from `u_ref`. diff --git a/docs/user_manual/002-components.md b/docs/user_manual/002-components.md new file mode 100644 index 0000000000..1b89bc836f --- /dev/null +++ b/docs/user_manual/002-components.md @@ -0,0 +1,272 @@ + + +# Components + +## Fault + +* type name: `fault` +* base: [base](#base) + +`fault` defines a short circuit location in the grid. +A fault can only happen at a `node`. + +#### Input + +| name | data type | unit | description | required | update | valid values | +|----------------|-----------------------------------------------------------|---------|-----------------------------------------------------|:-------------------------------------------------------------------------------------------------------:|:--------:|:-----------------:| +| `status` | `int8_t` | - | whether the fault is active | ✔ | ✔ | `0` or `1` | +| `fault_type` | {py:class}`FaultType ` | - | the type of the fault | ✨ only for short circuit | ✔ | | +| `fault_phase` | {py:class}`FaultPhase ` | - | the phase(s) of the fault | ❌ default `FaultPhase.default_value` (see [below](#fault-types-fault-phases-and-default-values)) | ✔ | | +| `fault_object` | `int32_t` | - | ID of the component where the short circuit happens | ✔ | ✔ | A valid `node` ID | +| `r_f` | `double` | ohm (Ω) | short circuit resistance | ❌ default `0.0` | ✔ | | +| `x_f` | `double` | ohm (Ω) | short circuit reactance | ❌ default `0.0` | ✔ | | + +```{note} +Multiple faults may exist within one calculation. +Currently, all faults in one scenario are required to have the same `fault_type` and `fault_phase`. +Across scenarios in a batch, the `fault_type` and `fault_phase` may differ. +``` + +```{note} +If any of the faults in any of the scenarios within a batch are not `three_phase` (i.e. `fault_type` is not +`FaultType.three_phase`), the calculation is treated as asymmetric. +``` + +#### Steady state output + +A `fault` has no steady state output. + +#### Short circuit output + +| name | data type | unit | description | +|-------------|-------------------|------------|---------------| +| `i_f` | `RealValueOutput` | ampere (A) | current | +| `i_f_angle` | `RealValueOutput` | rad | current angle | + +#### Electric Model + +##### Fault types, fault phases and default values + +Four types of short circuit fault are included in power-grid-model, each with their own set of supported values for +`fault_phase`. +In case the `fault_phase` is not specified or is equal to `FaultPhase.default_value`, the power-grid-model assumes a +`fault_type`-dependent set of fault phases. +The supported values of `fault_phase`, as well as its default value, are listed in the table below. + +| `fault_type` | supported values of `fault_phase` | `FaultPhase.default_value` | description | +|------------------------------------|---------------------------------------------------|----------------------------|------------------------------------------------------------------------| +| `FaultType.three_phase` | `FaultPhase.abc` | `FaultPhase.abc` | Three phases are connected with fault impedance. | +| `FaultType.single_phase_to_ground` | `FaultPhase.a`, `FaultPhase.b`, `FaultPhase.c` | `FaultPhase.a` | One phase is grounded with fault impedance, and other phases are open. | +| `FaultType.two_phase` | `FaultPhase.bc`, `FaultPhase.ac`, `FaultPhase.ab` | `FaultPhase.bc` | Two phases are connected with fault impedance. | +| `FaultType.two_phase_to_ground` | `FaultPhase.bc`, `FaultPhase.ac`, `FaultPhase.ab` | `FaultPhase.bc` | Two phases are connected with fault impedance then grounded. | + +## Regulator + +* type name: `regulator` +* base: [base](#base) + +`regulator` is an abstract regulator that is coupled to a given `regulated_object`. For each `regulator`, a switch is +defined between the `regulator` and the `regulated_object`. +Which object types are supported as `regulated_object` is regulator type-dependent. + +#### Input + +| name | data type | unit | description | required | update | valid values | +|--------------------|-----------|------|-------------------------------------------|:--------:|:--------:|:---------------------------:| +| `regulated_object` | `int32_t` | - | ID of the regulated object | ✔ | ❌ | a valid regulated object ID | +| `status` | `int8_t` | - | connection status to the regulated object | ✔ | ✔ | `0` or `1` | + +### Transformer tap regulator + +* type name: `transformer_tap_regulator` +* base: [regulator](#regulator) + +`transformer_tap_regulator` defines a regulator for transformers in the grid. +A transformer tap regulator regulates a component that is either a +[transformer](#transformer) or a +[Three-Winding Transformer](#three-winding-transformer). + +The transformer tap regulator changes the `tap_pos` of the transformer it regulates in the range set by the user via +`tap_min` and `tap_max` (i.e., `(tap_min <= tap_pos <= tap_max)` or `(tap_min >= tap_pos >= tap_max)`). +It regulates the tap position so that the voltage on the control side is in the chosen voltage band. +Other points further into the grid on the control side, away from the transformer, can also be regulated by providing +the cumulative impedance across branches to that point as an additional line drop compensation. +This line drop compensation only affects the controlled voltage and does not have any impact on the actual grid. +It may therefore be treated as a virtual impedance in the grid. + +```{note} +The regulator outputs the optimal tap position of the transformer. +The actual grid state is not changed after calculations are done. +``` + +#### Input + +| name | data type | unit | description | required | update | valid values | +|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------|:----------------------------:|:--------:|:----------------------------------------------------------------:| +| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](#transformer) and {py:class}`Branch3Side ` if it the regulated object is a [Three-Winding Transformer](#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | +| `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔ | `>= 0` | +| `u_band` | `double` | volt (V) | the width of the voltage band ($=2*\left(\Delta U\right)_{\text{acceptable}}$) | ✨ only for power flow | ✔ | `> 0` (see below) | +| `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | +| `line_drop_compensation_x` | `double` | ohm (Ω) | compensation for voltage drop due to reactance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | + +The following additional requirements exist on the input parameters. + +* Depending on the resultant voltage being transformed, the voltage band must be sufficiently large: At zero line drop + compensation if the expected resultant voltages are in the proximity of the rated transformer voltages, it is + recommended to have the $u_{band} >= \frac{u_2}{1+ u_1 / \text{tap}_{\text{size}}}$ +* The line drop compensation is small, in the sense that its product with the typical current through the transformer is + much smaller (in absolute value) than the smallest change in voltage due to a change in tap position. +* The `control_side` of a transformer regulator should be on the relatively further side to a source in the connected + grid. + +These requirements make sure no edge cases with undefined behavior are encountered. +Typical real-world power grids already satisfy these requirements and they should therefore not cause any problems. + +#### Steady state output + +| name | data type | unit | description | +|-----------|-----------|------|----------------------| +| `tap_pos` | `int8_t` | - | optimal tap position | + +#### Short circuit output + +A `transformer_tap_regulator` has no short circuit output. + +#### Electric Model + +The transformer tap regulator itself does not have a direct contribution to the grid state. +Instead, it regulates the tap position of the regulated object until the voltage at the control side is in the specified +voltage band: + +$$ +U_{\text{control}} \in + \left[U_{\text{set}} - \frac{U_{\text{band}}}{2}, U_{\text{set}} + \frac{U_{\text{band}}}{2}\right] +$$ + +##### Line drop compensation + +The transformer tap regulator tries to regulate the voltage in a specified virtual location in the grid, according to +the folowing model. + +```txt +tap_side control_side part of grid where voltage is to be regulated +------^\oo -*---------------------virtual_impedance----* + | U_node, I_transformer Z_comp U_control + | | + regulator <=======================================/ +``` + +The control voltage is the voltage at the node, compensated with the voltage drop corresponding to the specified line +drop compensation. + +$$ +\begin{aligned} + Z_{\text{compensation}} &= r_{\text{compensation}} + \mathrm{j} x_{\text{compensation}} \\ + U_{\text{control}} &= \left|\underline{U}_{\text{node}} - \underline{I}_{\text{transformer,out}} + \cdot \underline{Z}_{\text{compensation}}\right| + = \left|\underline{U}_{\text{node}} + \underline{I}_{\text{transformer}} + \cdot \underline{Z}_{\text{compensation}}\right| +\end{aligned} +$$ + +where $\underline{U}_{\text{node}}$ and $\underline{I}_{\text{transformer}}$ are the calculated voltage and current +phasors at the control side and may be obtained from a regular power flow calculation. +The plus sign in the last equality follows from canceling minus signs from the current direction convention and the +compensation direction. + +For example, if we want to regulate the voltage at `load_7` in the following grid, the line drop compensation impedance +is the approximate impedance of `line_5`. + +```txt +node_1 --- transformer_4 --- node_2 --- line_5 --- node_3 + | | | +source_6 | load_7 + transformer_tap_regulator_8 +``` + +### Voltage Regulator + +* type name: `voltage_regulator` +* base: {hoverxreftooltip}`user_manual/components:regulator` + +`voltage_regulator` defines a regulator for voltage-controlled generators in the grid. +A voltage regulator adjusts the reactive power output of a generator to maintain the voltage at its connection node +at a specified setpoint. + +The voltage regulator changes the reactive power output of the generator it regulates to achieve the reference voltage +`u_ref` at the generator's node. +If `q_min` and `q_max` are provided, the reactive power is constrained within this range (i.e., `q_min <= q <= q_max` or +`q_min >= q >= q_max`). +If these limits are not provided, the reactive power can take any value needed to maintain the voltage setpoint. + +```{warning} +Voltage regulation is only supported by the [Newton-Raphson power flow](./calculations.md#newton-raphson-power-flow) method. +``` + +```{note} +The `regulated_object` must reference a generator (`sym_gen` or `asym_gen`) or a load (`sym_load` or `asym_load`). +Each generator or load can have at most one voltage regulator. +When multiple voltage-regulated generators are connected to the same node, they should all specify the same `u_ref` +value to avoid conflicting voltage setpoints. +``` + +```{warning} +Reactive power limit checking is not yet fully implemented. When `q_min` and `q_max` are specified, +the intended behavior is that if the required reactive power to maintain `u_ref` exceeds these limits, +the voltage regulator should operate at the limit and the voltage may deviate from `u_ref`. +``` + +#### Input + +| name | data type | unit | description | required | update | valid values | +| -------- | --------- | -------------------------- | --------------------------------------------------- | :--------------------------: | :------: | :----------: | +| `u_ref` | `double` | - | reference voltage in per-unit at the generator node | ✨ only for power flow | ✔ | `> 0` | +| `q_min` | `double` | volt-ampere-reactive (var) | minimum reactive power limit of the generator | ❌ | ✔ | | +| `q_max` | `double` | volt-ampere-reactive (var) | maximum reactive power limit of the generator | ❌ | ✔ | | + +#### Steady state output + +| name | data type | unit | description | +| ----------------- | ----------------- | -------------------------- | -------------------------------------------------------------------- | +| `q` | `RealValueOutput` | volt-ampere-reactive (var) | reactive power provided by the voltage regulator | +| `limit_violated` | `int8_t` | - | reactive power limit violation indicator (not yet fully implemented) | + +#### Short circuit output + +A `voltage_regulator` has no short circuit output. + +#### Electric Model + +The voltage regulator controls the generator to behave as a **PV node** in power flow calculations: + +$$ +\begin{aligned} + P_{\text{gen}} &= P_{\text{specified}} \\ + \left|U_{\text{node}}\right| &= U_{\text{ref}} \\ + Q_{\text{gen}} &= \text{calculated to satisfy } U_{\text{ref}} +\end{aligned} +$$ + +When `q_min` and `q_max` are provided, the reactive power should be constrained: + +$$ + Q_{\text{min}} \leq Q_{\text{gen}} \leq Q_{\text{max}} +$$ + +When fully implemented, if the reactive power constraints are violated, the generator will operate at the limit and the +node becomes a PQ node: + +$$ +\begin{aligned} + P_{\text{gen}} &= P_{\text{specified}} \\ + Q_{\text{gen}} &= Q_{\text{min}} \text{ or } Q_{\text{max}} \\ + \left|U_{\text{node}}\right| &= \text{calculated from power flow} +\end{aligned} +$$ + +In this case, `limit_violated` will indicate which limit was exceeded, and the actual voltage at the node may differ +from `u_ref`. diff --git a/scripts/build-docs.py b/scripts/build-docs.py new file mode 100644 index 0000000000..1a76da2082 --- /dev/null +++ b/scripts/build-docs.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +import sys +from pathlib import Path + +BASE_DIR = Path("docs/user_manual") +OUTPUT_FILE = BASE_DIR / "components.md" + +files = sorted(BASE_DIR.glob("*components*.md")) + +if not files: + print("No component files found.") + sys.exit(0) + +output = [] + +output.append("\n") + +for f in files: + content = f.read_text(encoding="utf-8") + + output.append(f"\n\n\n") + output.append(content.strip()) + +OUTPUT_FILE.write_text("\n".join(output), encoding="utf-8") + +print(f"Built: {OUTPUT_FILE}") From 1e91a98ee7f5bdf85a08df59250e2be322fd088c Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 10:59:34 +0200 Subject: [PATCH 02/13] fix SHA Signed-off-by: zhen0427 --- .github/workflows/build-docs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 1d161f2715..e1a1229522 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -20,10 +20,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@REPLACE_WITH_SHA with: python-version: "3.11" @@ -31,7 +31,7 @@ jobs: run: python scripts/build_docs.py - name: Upload generated docs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: user-manual-docs path: docs/user_manual/components.md \ No newline at end of file From d2185648943b1bb11edd5de68f46d15378831930 Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 11:10:36 +0200 Subject: [PATCH 03/13] fix SHA Signed-off-by: zhen0427 --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index e1a1229522..b9f5c81f96 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Python - uses: actions/setup-python@REPLACE_WITH_SHA + uses: actions/setup-python@a309ff8b50ed95fcb0a0dbff1373a8df3c7c7f36 with: python-version: "3.11" From e6aba542fe34dcb97af813847723e33ee67c843f Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 11:15:34 +0200 Subject: [PATCH 04/13] fix SHA 3 Signed-off-by: zhen0427 --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index b9f5c81f96..d8444d997e 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Setup Python - uses: actions/setup-python@a309ff8b50ed95fcb0a0dbff1373a8df3c7c7f36 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c with: python-version: "3.11" From 567eb13b39ecc6b5b23f393fc300487f016ac99a Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 11:22:57 +0200 Subject: [PATCH 05/13] fix build docs Signed-off-by: zhen0427 --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index d8444d997e..b78f765be7 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -28,7 +28,7 @@ jobs: python-version: "3.11" - name: Build docs - run: python scripts/build_docs.py + run: python scripts/build-docs.py - name: Upload generated docs uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a From 87e3d992067108f7bc828279e2a319a9f714e49c Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 11:35:34 +0200 Subject: [PATCH 06/13] try reveal components Signed-off-by: zhen0427 --- scripts/build-docs.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/build-docs.py b/scripts/build-docs.py index 1a76da2082..7ccc287160 100644 --- a/scripts/build-docs.py +++ b/scripts/build-docs.py @@ -4,26 +4,35 @@ import sys from pathlib import Path +import re BASE_DIR = Path("docs/user_manual") OUTPUT_FILE = BASE_DIR / "components.md" -files = sorted(BASE_DIR.glob("*components*.md")) +def extract_index(path: Path): + # 从 001-components.md 提取 001 + match = re.match(r"(\d+)", path.name) + return int(match.group(1)) if match else 9999 + +files = sorted( + BASE_DIR.glob("*components*.md"), + key=extract_index +) if not files: print("No component files found.") sys.exit(0) output = [] - output.append("\n") for f in files: - content = f.read_text(encoding="utf-8") + content = f.read_text(encoding="utf-8").strip() + # 保留结构连续性,但加清晰分隔 output.append(f"\n\n\n") - output.append(content.strip()) + output.append(content) OUTPUT_FILE.write_text("\n".join(output), encoding="utf-8") -print(f"Built: {OUTPUT_FILE}") +print(f"Built: {OUTPUT_FILE}") \ No newline at end of file From 8888a44e59f7e43e073ae073e9144f77776afe6e Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Mon, 15 Jun 2026 11:35:39 +0200 Subject: [PATCH 07/13] try reveal components Signed-off-by: zhen0427 --- scripts/build-docs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/build-docs.py b/scripts/build-docs.py index 7ccc287160..91afcff977 100644 --- a/scripts/build-docs.py +++ b/scripts/build-docs.py @@ -10,7 +10,6 @@ OUTPUT_FILE = BASE_DIR / "components.md" def extract_index(path: Path): - # 从 001-components.md 提取 001 match = re.match(r"(\d+)", path.name) return int(match.group(1)) if match else 9999 @@ -29,7 +28,6 @@ def extract_index(path: Path): for f in files: content = f.read_text(encoding="utf-8").strip() - # 保留结构连续性,但加清晰分隔 output.append(f"\n\n\n") output.append(content) From 3364b946c84f80a62d0878acb8d9e18159e1921a Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Tue, 16 Jun 2026 10:16:59 +0200 Subject: [PATCH 08/13] fix ruff Signed-off-by: zhen0427 --- scripts/build-docs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build-docs.py b/scripts/build-docs.py index 91afcff977..f363cca00f 100644 --- a/scripts/build-docs.py +++ b/scripts/build-docs.py @@ -2,9 +2,9 @@ # # SPDX-License-Identifier: MPL-2.0 +import re import sys from pathlib import Path -import re BASE_DIR = Path("docs/user_manual") OUTPUT_FILE = BASE_DIR / "components.md" @@ -33,4 +33,4 @@ def extract_index(path: Path): OUTPUT_FILE.write_text("\n".join(output), encoding="utf-8") -print(f"Built: {OUTPUT_FILE}") \ No newline at end of file +print(f"Built: {OUTPUT_FILE}") From 29f3c50e56f736d2335508ab3319ff7a8fc17a4f Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Tue, 16 Jun 2026 10:52:42 +0200 Subject: [PATCH 09/13] fix cross reference Signed-off-by: zhen0427 --- docs/user_manual/002-components.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/user_manual/002-components.md b/docs/user_manual/002-components.md index 1b89bc836f..f9d8d36f3a 100644 --- a/docs/user_manual/002-components.md +++ b/docs/user_manual/002-components.md @@ -9,7 +9,7 @@ SPDX-License-Identifier: MPL-2.0 ## Fault * type name: `fault` -* base: [base](#base) +* base: [base](001-components.md#base) `fault` defines a short circuit location in the grid. A fault can only happen at a `node`. @@ -67,7 +67,7 @@ The supported values of `fault_phase`, as well as its default value, are listed ## Regulator * type name: `regulator` -* base: [base](#base) +* base: [base](001-components.md#base) `regulator` is an abstract regulator that is coupled to a given `regulated_object`. For each `regulator`, a switch is defined between the `regulator` and the `regulated_object`. @@ -83,12 +83,12 @@ Which object types are supported as `regulated_object` is regulator type-depende ### Transformer tap regulator * type name: `transformer_tap_regulator` -* base: [regulator](#regulator) +* base: [regulator](001-components.md#regulator) `transformer_tap_regulator` defines a regulator for transformers in the grid. A transformer tap regulator regulates a component that is either a -[transformer](#transformer) or a -[Three-Winding Transformer](#three-winding-transformer). +[transformer](001-components.md#transformer) or a +[Three-Winding Transformer](001-components.md#three-winding-transformer). The transformer tap regulator changes the `tap_pos` of the transformer it regulates in the range set by the user via `tap_min` and `tap_max` (i.e., `(tap_min <= tap_pos <= tap_max)` or `(tap_min >= tap_pos >= tap_max)`). @@ -107,7 +107,7 @@ The actual grid state is not changed after calculations are done. | name | data type | unit | description | required | update | valid values | |----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------|:----------------------------:|:--------:|:----------------------------------------------------------------:| -| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](#transformer) and {py:class}`Branch3Side ` if it the regulated object is a [Three-Winding Transformer](#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | +| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](001-components.md#transformer) and {py:class}`Branch3Side ` if it the regulated object is a [Three-Winding Transformer](001-components.md#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | | `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔ | `>= 0` | | `u_band` | `double` | volt (V) | the width of the voltage band ($=2*\left(\Delta U\right)_{\text{acceptable}}$) | ✨ only for power flow | ✔ | `> 0` (see below) | | `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | From 727a74c10eae53f892733c397fb549433dd7fa68 Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Tue, 16 Jun 2026 11:13:36 +0200 Subject: [PATCH 10/13] fix table alignment Signed-off-by: zhen0427 --- docs/user_manual/002-components.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/user_manual/002-components.md b/docs/user_manual/002-components.md index f9d8d36f3a..a8138b61fc 100644 --- a/docs/user_manual/002-components.md +++ b/docs/user_manual/002-components.md @@ -105,13 +105,13 @@ The actual grid state is not changed after calculations are done. #### Input -| name | data type | unit | description | required | update | valid values | -|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------|:----------------------------:|:--------:|:----------------------------------------------------------------:| -| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](001-components.md#transformer) and {py:class}`Branch3Side ` if it the regulated object is a [Three-Winding Transformer](001-components.md#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | -| `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔ | `>= 0` | -| `u_band` | `double` | volt (V) | the width of the voltage band ($=2*\left(\Delta U\right)_{\text{acceptable}}$) | ✨ only for power flow | ✔ | `> 0` (see below) | -| `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | -| `line_drop_compensation_x` | `double` | ohm (Ω) | compensation for voltage drop due to reactance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔ | `>= 0` | +| name | data type | unit | description | required | update | valid values | +|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------------------------------------------------------------------------------------------------------|-------------------------------|----------|-------------| +| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](001-components.md#transformer) and {py:class}`Branch3Side ` if it is a [Three-Winding Transformer](001-components.md#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | +| `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔️ | `>= 0` | +| `u_band` | `double` | volt (V) | the width of the voltage band ($=2*(\Delta U)_{\text{acceptable}}$) | ✨ only for power flow | ✔️ | `> 0` (see below) | +| `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔️ | `>= 0` | +| `line_drop_compensation_x` | `double` | ohm (Ω) | compensation for voltage drop due to reactance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔️ | `>= 0` | The following additional requirements exist on the input parameters. From 66e6a1d37e98d4c7cf4de4447e8441b12610f83d Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Tue, 16 Jun 2026 11:40:54 +0200 Subject: [PATCH 11/13] fix table Signed-off-by: zhen0427 --- docs/user_manual/002-components.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/user_manual/002-components.md b/docs/user_manual/002-components.md index a8138b61fc..a2eabd9f6d 100644 --- a/docs/user_manual/002-components.md +++ b/docs/user_manual/002-components.md @@ -105,14 +105,13 @@ The actual grid state is not changed after calculations are done. #### Input -| name | data type | unit | description | required | update | valid values | -|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------------------------------------------------------------------------------------------------------|-------------------------------|----------|-------------| -| `control_side` | {py:class}`BranchSide ` if the regulated object is a [transformer](001-components.md#transformer) and {py:class}`Branch3Side ` if it is a [Three-Winding Transformer](001-components.md#three-winding-transformer) | - | the controlled side of the transformer | ✨ only for power flow | ❌ | `control_side` should be the relatively further side to a source | -| `u_set` | `double` | volt (V) | the voltage setpoint (at the center of the band) | ✨ only for power flow | ✔️ | `>= 0` | -| `u_band` | `double` | volt (V) | the width of the voltage band ($=2*(\Delta U)_{\text{acceptable}}$) | ✨ only for power flow | ✔️ | `> 0` (see below) | -| `line_drop_compensation_r` | `double` | ohm (Ω) | compensation for voltage drop due to resistance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔️ | `>= 0` | -| `line_drop_compensation_x` | `double` | ohm (Ω) | compensation for voltage drop due to reactance during transport (see [below](#line-drop-compensation)) | ❌ default `0.0` | ✔️ | `>= 0` | - +| name | data type | unit | description | required | update | valid values | +|------|-----------|------|-------------|----------|---------|--------------| +| `control_side` | {py:class}`BranchSide ` / {py:class}`Branch3Side ` | - | controlled side of transformer | ✨ only for power flow | ❌ | control_side should be further from source | +| `u_set` | `double` | volt (V) | voltage setpoint | ✨ only for power flow | ✔ | `>= 0` | +| `u_band` | `double` | volt (V) | voltage band width | ✨ only for power flow | ✔ | `> 0` | +| `line_drop_compensation_r` | `double` | ohm (Ω) | resistance compensation | ❌ default `0.0` | ✔ | `>= 0` | +| `line_drop_compensation_x` | `double` | ohm (Ω) | reactance compensation | ❌ default `0.0` | ✔ | `>= 0` | The following additional requirements exist on the input parameters. * Depending on the resultant voltage being transformed, the voltage band must be sufficiently large: At zero line drop From 004e5edd80c453d413656140cd3915014c2f356e Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Wed, 17 Jun 2026 09:39:42 +0200 Subject: [PATCH 12/13] fix code quality Signed-off-by: zhen0427 --- docs/user_manual/002-components.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/user_manual/002-components.md b/docs/user_manual/002-components.md index a2eabd9f6d..67b30c2865 100644 --- a/docs/user_manual/002-components.md +++ b/docs/user_manual/002-components.md @@ -106,12 +106,13 @@ The actual grid state is not changed after calculations are done. #### Input | name | data type | unit | description | required | update | valid values | -|------|-----------|------|-------------|----------|---------|--------------| +| ------ | ----------- | ------ | ------------- | ---------- | --------- | -------------- | | `control_side` | {py:class}`BranchSide ` / {py:class}`Branch3Side ` | - | controlled side of transformer | ✨ only for power flow | ❌ | control_side should be further from source | | `u_set` | `double` | volt (V) | voltage setpoint | ✨ only for power flow | ✔ | `>= 0` | | `u_band` | `double` | volt (V) | voltage band width | ✨ only for power flow | ✔ | `> 0` | | `line_drop_compensation_r` | `double` | ohm (Ω) | resistance compensation | ❌ default `0.0` | ✔ | `>= 0` | | `line_drop_compensation_x` | `double` | ohm (Ω) | reactance compensation | ❌ default `0.0` | ✔ | `>= 0` | + The following additional requirements exist on the input parameters. * Depending on the resultant voltage being transformed, the voltage band must be sufficiently large: At zero line drop From af8526a93825f79e5fa454b315965fccc60ec56d Mon Sep 17 00:00:00 2001 From: zhen0427 Date: Wed, 17 Jun 2026 10:03:32 +0200 Subject: [PATCH 13/13] run format Signed-off-by: zhen0427 --- scripts/build-docs.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/build-docs.py b/scripts/build-docs.py index f363cca00f..1e92293bde 100644 --- a/scripts/build-docs.py +++ b/scripts/build-docs.py @@ -9,14 +9,13 @@ BASE_DIR = Path("docs/user_manual") OUTPUT_FILE = BASE_DIR / "components.md" + def extract_index(path: Path): match = re.match(r"(\d+)", path.name) return int(match.group(1)) if match else 9999 -files = sorted( - BASE_DIR.glob("*components*.md"), - key=extract_index -) + +files = sorted(BASE_DIR.glob("*components*.md"), key=extract_index) if not files: print("No component files found.")