Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions GridKit/Model/PhasorDynamics/Branch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ provides more flexibility for modeling.

Symbol | Units | Description | Note
------------|---------|---------------------------------| ------
$R$ | [p.u.] | Branch series resistance |
$X$ | [p.u.] | Branch series reactance |
$G$ | [p.u.] | Branch shunt conductance |
$B$ | [p.u.] | Branch shunt susceptance |
$R$ | [p.u.] | Branch series resistance |
$X$ | [p.u.] | Branch series reactance |
$G$ | [p.u.] | Branch shunt conductance |
$B$ | [p.u.] | Branch shunt susceptance |

### Model Derived Parameters
Note the difference between little-g and big-G, little-b, big-B in these equations.
Expand Down Expand Up @@ -81,10 +81,43 @@ None.
\end{aligned}
```

# Model Outputs
## Actions

This component accepts the following runtime events via `apply(Action)`. See
[EVENTS.md](../EVENTS.md) for the dispatch model and JSON schema.

JSON `"action"` | Params | Effect
----------------|---------------------|----------------------------------------------------------------------
`"open"` | none | Sets line status $S = 0$ (line tripped).
`"close"` | none | Sets line status $S = 1$ (line in service).
`"fault"` | `R`, `X`, `percent` | Stores the fault impedance and fractional position $f = \text{percent}/100$ along the line from `bus1`, and engages the fault ($U = 1$).
`"clear"` | none | Disengages the fault ($U = 0$). Stored impedance is unchanged.

The line status $S$ is implemented as a mask (0 or 1) on every
branch residual contribution, so the Jacobian sparsity pattern is fixed
across `Open`/`Close` cycles.

While the fault is engaged, the line is split at the fault point into two
series segments with admittances

``` math
y_{1m} = \dfrac{g + jb}{f}, \quad y_{m2} = \dfrac{g + jb}{1 - f}
```

connected by a shunt fault admittance $y_f = 1/(R_f + jX_f)$ to ground at
the fault point, where $g$, $b$ are the unfaulted line series admittance
parameters and $R_f$, $X_f$ are the fault impedance from the action payload.
The Norton-equivalent two-port admittance of this augmented network replaces
the unfaulted line series contribution to the residuals at `bus1` and `bus2`.
The line charging admittances $G/2$, $B/2$ at both terminals are unchanged.

The fault contribution is itself multiplied by the line status $S$: an open
line carries no fault current regardless of $U$.

## Model Outputs

Real and imaginary current at the branch's two buses
are model variables of the branch model: $I_{r1}$, $I_{i1}$, $I_{r2}$,
are model variables of the branch model: $I_{r1}$, $I_{i1}$, $I_{r2}$,
and $I_{i2}$.
Current is oriented leaving the branch (i.e. entering the bus).

Expand All @@ -96,7 +129,7 @@ Current magnitude $I_{m1}$ and $I_{m2}$ are the phasor magnitude of the current.
\end{aligned}
```

Active and reactive power ($P_1$, $Q_1$, $P_2$, and $Q_2$)
Active and reactive power ($P_1$, $Q_1$, $P_2$, and $Q_2$)
are the real and imaginary parts of the complex power at each end of the branch,
where the complex power is defined as $S=VI^{\ast}=(V_r + j V_i)(I_r - jI_i)$
``` math
Expand All @@ -112,7 +145,7 @@ Real and reactive power are oriented leaving the branch (i.e. entering the bus).

# Transformer Branch Model

**Note: Transformer model not yet implemented**
> **Note: Transformer model not yet implemented**

The branch model can be created by adding the ideal transformer in series with
the $`\pi`$ circuit as shown in Figure 2 where $`\tau`$ is a tap ratio
Expand All @@ -121,8 +154,8 @@ $`N = \tau e^{j \theta}`$.

<div align="center">
<img align="center" src="../../../../docs/Figures/transformer-branch.png">


Figure 2: Branch equivalent circuit
</div>

Expand Down
26 changes: 25 additions & 1 deletion GridKit/Model/PhasorDynamics/Bus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ sign.

<div align="center">
<img align="center" src="../../../../docs/Figures/bus_variables.jpg">

Figure 1: Needs to be changed to represent current balance instead of power
balance.
</div>
Expand All @@ -28,3 +28,27 @@ sign.
**Other Parameters**
Buses are uniquely defined by their ID (number or name). Besides, each bus
should have associated Nominal Voltage value.

## Actions

This component accepts the following runtime events via `apply(Action)`. See
[EVENTS.md](../EVENTS.md) for the dispatch model and JSON schema.

JSON | Params | Effect
----------------|----------|--------------------------------------------------------------------
`"fault"` | `R`, `X` | Stores the fault impedance and engages the fault ($U = 1$).
`"clear"` | none | Disengages the fault ($U = 0$). Stored impedance is unchanged.

While the fault is engaged, the bus current balance gains the contributions:

``` math
\begin{aligned}
G_f &= \dfrac{R}{R^2 + X^2}, \quad B_f = -\dfrac{X}{R^2 + X^2} \\
\Delta I_{rk} &= U(-G_f V_{rk} + B_f V_{ik}) \\
\Delta I_{ik} &= U(-B_f V_{rk} - G_f V_{ik})
\end{aligned}
```

The `percent` parameter of `Fault` is ignored by `Bus`. The fault gate $U$
is implemented as a mask (0 or 1) on the fault residual term,
so the Jacobian sparsity pattern is fixed across `Fault`/`Clear` cycles.
115 changes: 115 additions & 0 deletions GridKit/Model/PhasorDynamics/EVENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Phasor Dynamics Events

## Overview

This document describes the runtime event system for PhasorDynamics
components. A runtime event mutates the state of a single component during simulation. Events are scheduled in a solver file
(see [PDSim](../../../application/PhasorDynamics/README.md)) and delivered to components by `SystemModel` at their scheduled times. Buses are addressed for events by their existing `number`; devices by their
existing `id`.

## Event

An event is a triple of fields:

Name | Description
---------|------------------------------------------------------
`time` | Simulation time at which the event fires, in seconds
`target` | Routing key for the component receiving the event
`action` | The mutation to perform, drawn from the action vocabulary

For buses, the `target` is the bus's `number` rendered as a string
(e.g. `"1001"`). For devices, it is the device's `id` field from the case
file (e.g. `"BR_1001_1064_1"`). Buses and devices share a single routing
namespace; collisions between a bus number and a device id are an error at registration.


```cpp
struct Event {
double time;
std::string target;
Action action;
};
```

## Action vocabulary

`Action` is a `std::variant` of action structs in
`GridKit::PhasorDynamics::Actions`. Each struct has a canonical name string
exposed as `static constexpr std::string_view name`.

C++ type | JSON name | Params | Applies to
-----------------|-----------|---------------------|----------------
`Actions::Open` | `open` | none | `Branch`
`Actions::Close` | `close` | none | `Branch`
`Actions::Fault` | `fault` | `R`, `X`, `percent` | `Bus`, `Branch`
`Actions::Clear` | `clear` | none | `Bus`, `Branch`

The semantics of each action are documented in the receiving component's
README. For `fault`, the `percent` parameter (position along the line as a
percent in `[0, 100]`) is used by `Branch` and ignored by `Bus`.

## Dispatch

`Component::apply(const Action&)` is virtual on the base class. The default
throws. Components that handle events override it with a `std::visit` over
the variant, dispatching to per-action handlers and falling through to a
generic catch-all for unhandled action types:

```cpp
void Bus::apply(const Action& a) override {
std::visit(overloaded{
[this](const Actions::Fault& f) {},
[this](const Actions::Clear&) {},
[this](const auto& other) {
using A = std::decay_t<decltype(other)>;
throw std::runtime_error("Bus does not handle action '" + std::string(A::name) + "'");
}
}, a);
}
```

`SystemModel::apply(const Event&)` looks up the target by string id and
forwards the action to the component.

## Schedule

The `schedule` field of the solver file is an array of events. The parser
stable-sorts the schedule by `time` on read. If the input was not already
sorted, an info line is logged and parsing continues.

Multiple events at the same time are applied sequentially in listing order in a single re-init of the integrator (`IDACalcIC`). When two same-time events conflict on a single target, the last-listed event wins.

The same-time grouping uses exact `double` equality on `time`. Both sides of the comparison are read from the parsed event records, so events with the same JSON literal compare equal.

## Example schedule

A schedule that faults bus `2` at `t=1.0`, clears the fault at `t=1.1`, and
simultaneously opens line `L23` at `t=1.1`:

```json
"schedule": [
{ "time": 1.0, "target": "2", "action": "fault", "params": { "R": 0.0, "X": 1.0e-5 } },
{ "time": 1.1, "target": "2", "action": "clear" },
{ "time": 1.1, "target": "L23", "action": "open" }
]
```

## Errors

Failure | Source | Message
-------------------------------|---------------------|--------
Unknown target id | `SystemModel` | `No event target with id '<id>'`
Unhandled action for component | `Component::apply` | `Component '<class>' does not handle action '<name>'`
Unknown action string | parser | `unknown action '<str>' (valid: open, close, fault, clear)`
Missing required `params` | parser | `action '<name>' requires params field with <fields>`
Schedule entry past `tmax` | parser | `schedule entry at t=<t> exceeds tmax=<tmax>`

## Adding a new action

1. Add a struct to `GridKit::PhasorDynamics::Actions` with a
`static constexpr std::string_view name` and any payload fields.
2. Add the struct to the `Action` variant alias.
3. Add a parser case mapping the JSON name to the struct.
4. Override `apply` in the receiving component(s) to handle the new action.
5. Add a row to the action vocabulary table above and an entry in each
receiving component's README Actions section.
101 changes: 82 additions & 19 deletions application/PhasorDynamics/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,88 @@
# Input file for GridKit phasor dynamics application
# PDSim (Phasor Dynamics Simulation)

## Root elements

Name | Value
---------------------|-------------------------------------------------------
`system_model_file` | Path to the system model file[^1]
`dt` | A floating point value for time step size
`tmax` | A floating point value for max time
`events` | An array of event groups (see [Events](#events) below)
`output_file` | Path to output (CSV) file
`reference_file` | A string containing the name of the case
`error_tolerance` | A string containing the name of the case
PDSim runs a phasor-dynamics simulation defined by a *solver file*
(`*.solver.json`) and a *case file* (`*.case.json`). The case file specifies
the system model, and the solver file specifies the simulation enviornment, including
any runtime events scheduled during the simulation.

[^1]: See system model [case format](../../Model/PhasorDynamics/INPUT_FORMAT.md)
The runtime event system (action vocabulary, dispatch model, schedule
semantics) is documented in [`Model/PhasorDynamics/EVENTS.md`](../../GridKit/Model/PhasorDynamics/EVENTS.md).

## Events
## Usage

Each event group describes a system event that occurs at a given time point
```
pdsim <solver-file.solver.json>
```

Name | Value
--------------------|-------------------------------------------------------
`time` | A floating point value for time event occurs
`type` | Event type (one of { "fault_on", "fault_off" })
`element_id` | An integer value referencing the element associated with the event (e.g., bus fault id)
Relative paths in `case_file`, `output_file`, and `reference_file` are
resolved against the solver file's directory. Absolute paths are used as
given.

## Solver file format

### Top-level fields

Name | Required | Description
------------------|----------|------------------------------------------------------
`format_version` | no | Format version integer. Default `1`. Parser rejects unknown versions.
`case_file` | yes | Path to the case file
`dt` | yes | Reporting time step in seconds
`tmax` | yes | End time in seconds
`schedule` | no | Ordered array of events (see below). May be empty or omitted.
`output_file` | no | Path to monitor output (CSV)
`reference_file` | no | Path to reference CSV for validation
`error_tolerance` | no | Maximum allowed error vs reference. Default `1e-4`.

### Schedule

The `schedule` field is an array of events. Each event has the shape:

Name | Required | Description
---------|--------------------|------------------------------------------------------
`time` | yes | Simulation time at which the event fires, in seconds. Must be `≤ tmax`.
`target` | yes | Routing key of the component receiving the event (bus `number` or device `id`)
`action` | yes | Action name; one of `open`, `close`, `fault`, `clear`
`params` | iff action carries | Action payload (e.g. `{ R, X, percent }` for `fault`)

The action vocabulary, dispatch model, schedule canonicalization (stable
sort by time, last-listed wins on conflicts), and same-time semantics are
documented in
[`EVENTS.md`](../../GridKit/Model/PhasorDynamics/EVENTS.md).

## Output and validation

If `output_file` is given, it sets the destination for the case file's
default CSV monitor sink. Other monitor sinks declared in the case file
are unaffected. Output formatting (channels, delimiter) is controlled by
the case file's monitor declarations.

If both `output_file` and `reference_file` are given, PDSim compares the
output against the reference and exits with status `0` if the maximum
error is within `error_tolerance`, `1` otherwise.

## Example

```json
{
"format_version": 1,
"case_file": "texas.json",
"dt": 0.00416666666666,
"tmax": 20.0,
"schedule": [
{ "time": 10.0, "target": "1001", "action": "fault",
"params": { "R": 0.0, "X": 1.0e-5 } },
{ "time": 10.15, "target": "1001", "action": "clear" },
{ "time": 10.15, "target": "BR_1001_1064_1", "action": "open" }
],
"output_file": "texas.csv",
"reference_file": "texas.ref.csv",
"error_tolerance": 1.0e-4
}
```

This run faults bus `1001` at `t=10`, clears the fault and trips line
`BR_1001_1064_1` simultaneously at `t=10.15` (a 9-cycle fault followed by
isolation of the faulted equipment), and integrates to `t=20`. The two
events at `t=10.15` batch into one IDA re-init. Output is compared against
the reference file with tolerance `1e-4`.
Loading