diff --git a/GridKit/Model/PhasorDynamics/Partition/README.md b/GridKit/Model/PhasorDynamics/Partition/README.md new file mode 100644 index 000000000..b9cbd4222 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Partition/README.md @@ -0,0 +1,215 @@ +# Partitions in Phasor Dynamics + +## Overview and Purpose + +The concept of "Partition" is used in phasor dynamics in GridKit to refer +to dividing up a system model into smaller parts. +Partitions for a system are defined in a JSON file with an extension of +`.partition.json`, which is separate from the system model +definition +but refers to buses by number from the system model definition. +Defining a partition file for a system is optional, but needed to enable +parallel model evaluation for some solvers. + +This file defines a nested, hierarchical structure of containers +of type `Partition`. +The format is general and could accommodate any combination of nested +partitions, including the common structure of a single level of partitions +dividing the system for a single level of parallel evaluation. +Any more complex structure can also be flattened +into a single level for solvers that require +only a single level of partitioning. +The JSON file schema is defined in `partition.schema.json`, and is also +described below. + +The partitioning infrastructure is intended to support automated +partitioning, where an application would analyze model +structure and generate `partition.json` file for a given computational +environment. +For now, partitions need to be defined by the user-written JSON file. + +## Partition Definitions and JSON Structure + +A `Partition` is a container for buses and other partitions. +Each partition has the following properties: +- `name` of type `string` identifying the partition +- `external` of type `array of integers` containing the + bus numbers external to the partition. These bus numbers must correspond to + buses in the model JSON file. Any bus connecting to this partition and also + to other partitions is considered external. This property is optional: + omitting it implies there are no external buses. +- `internal` of type `array of integers` containing the bus numbers internal + to the partition. These bus numbers must correspond to + buses in the model JSON file. Any bus completely unique to this partition + should go in this list. This property is optional: + omitting it implies there are no internal buses. +- `partitions` of type `array of Partition` + containing zero or more Partitions which are child partitions of this + partition. This property is optional: + omitting it implies there are no child partitions. + +The root-level object of the JSON file is a partition. + +The hierarchy is primarily for convenience. All internal buses are truly +isolated to that partition and may not be connected to any other partition, +even child partitions. All external buses are globally visible and may connect +even across branches of the hierarchy. There is no requirement for the structure +of the hierarchy to be balanced. + +## Validation Rules and Component Assignment + +Any violation of the rules below results in a exception indicating a badly +formed partition. +Component assignment is done automatically. This avoids the error-prone +requirement for the user to manually assign both buses and components and +ensure they are compatible. + +1. The file must follow the `partition.schema.json` structure. +2. Every bus must appear somewhere in the file, +either as `external` or `internal`, but not both. +3. Internal buses must only appear uniquely in one `Partition`. +External buses may appear more than once. +4. Each `Component` that has bus ports shall be assigned to the first +`Partition`, in the order partitions are listed in the file, +which contains every one of the `Component`'s connected buses as either an +internal or external bus in that partition itself. If no such partition exists, +return an error indicating a badly +formed partition. +5. Some components are connected to each other by signals. +Place signal-connected devices together, +in the first partition containing all bus ports of all of them. +Signal nodes go with the devices that use them. +If a `Component` is left unassigned or if two connected +components are assigned to different partitions, +raise an exception indicating a badly +formed partition. + +Partition names are not required to be unique, though this is a recommended. + +For now, only buses can divide partitions. +In the future it is envisioned that signals may also be part of the +partition definition, allowing connected components to be in separate +partitions. + +## Examples + +### Ten Generator Case - Divide into Two Partitions + +File name: `TenGen.partition.json` + +```JSON +{ + "name": "TenGen", + "partitions": [ + { + "name": "A", + "internal": [ + 1, + 2, + 3, + 4, + 5 + ], + "external": [ + 6 + ] + }, + { + "name": "B", + "internal": [ + 7, + 8, + 9, + 10 + ], + "external": [ + 6 + ] + } + ] +} +``` + +![TenGen partition](TenGen.png) + +*Figure 1: TenGen split into two partitions, A and B. Buses 1–5 are internal to A and buses 7–10 are internal to B (red). Bus 6 is external for both (blue); the dashed line indicates the partitions couple at Bus 6.* + +### WECC 240 Case - Divide into Four Partitions + +File name: `WECC240.partition.json` + +(This file can be found under `examples/PhasorDynamics/Partitions/WECC240`.) + +![WECC240 partition](WECC240.png) + +*Figure 2: WECC240 split into four regional partitions: Northwest, Southwest, NorCal and SoCal. Internal buses (red) belong to exactly one partition. Boundary buses (blue) are external and appear in every partition that touches them; dashed lines show where +buses couple between partitions.* + + +### Sample Multi-Level Partition + +This example shows how a multi-level hierarchical structure can be implemented +with partitions. + +```JSON +{ + "name": "root", + "partitions": [ + { + "name": "A", + "internal": [ + 1 + ], + "external": [ + 5 + ] + }, + { + "name": "B", + "partitions": [ + { + "name": "B1", + "internal": [ + 2 + ], + "external": [ + 5, + 6 + ] + }, + { + "name": "C", + "partitions": [ + { + "name": "C1", + "internal": [ + 3 + ], + "external": [ + 6, + 7 + ] + }, + { + "name": "C2", + "internal": [ + 4 + ], + "external": [ + 7 + ] + } + ] + } + ] + } + ] +} +``` + +![Sample multi-Level partition](deep.png) + +*Figure 3: Sample multi-level partition scheme. The partitions +containing buses are +A, B1, C1, and C2, whereas root, B and C are partitions only +containing other partitions.* diff --git a/GridKit/Model/PhasorDynamics/Partition/TenGen.png b/GridKit/Model/PhasorDynamics/Partition/TenGen.png new file mode 100644 index 000000000..06ebd6fea Binary files /dev/null and b/GridKit/Model/PhasorDynamics/Partition/TenGen.png differ diff --git a/GridKit/Model/PhasorDynamics/Partition/WECC240.png b/GridKit/Model/PhasorDynamics/Partition/WECC240.png new file mode 100644 index 000000000..b39600056 Binary files /dev/null and b/GridKit/Model/PhasorDynamics/Partition/WECC240.png differ diff --git a/GridKit/Model/PhasorDynamics/Partition/deep.png b/GridKit/Model/PhasorDynamics/Partition/deep.png new file mode 100644 index 000000000..d577619e2 Binary files /dev/null and b/GridKit/Model/PhasorDynamics/Partition/deep.png differ diff --git a/GridKit/Model/PhasorDynamics/Partition/partition.schema.json b/GridKit/Model/PhasorDynamics/Partition/partition.schema.json new file mode 100644 index 000000000..f9a7c414c --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Partition/partition.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$ref": "#/$defs/partition", + "$defs": { + "buses": { + "type": "array", + "items": { "type": "integer", "minimum": 0 }, + "uniqueItems": true + }, + "partition": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "internal": { "$ref": "#/$defs/buses" }, + "external": { "$ref": "#/$defs/buses" }, + "partitions": { + "type": "array", + "items": { "$ref": "#/$defs/partition" } + } + }, + "additionalProperties": false + } + } +} diff --git a/examples/PhasorDynamics/Partitions/TenGen/TenGen.partition.json b/examples/PhasorDynamics/Partitions/TenGen/TenGen.partition.json new file mode 100644 index 000000000..16d6c8d9c --- /dev/null +++ b/examples/PhasorDynamics/Partitions/TenGen/TenGen.partition.json @@ -0,0 +1,30 @@ +{ + "name": "TenGen", + "partitions": [ + { + "name": "A", + "internal": [ + 1, + 2, + 3, + 4, + 5 + ], + "external": [ + 6 + ] + }, + { + "name": "B", + "internal": [ + 7, + 8, + 9, + 10 + ], + "external": [ + 6 + ] + } + ] +} diff --git a/examples/PhasorDynamics/Partitions/TenGen/TenGen.png b/examples/PhasorDynamics/Partitions/TenGen/TenGen.png new file mode 100644 index 000000000..06ebd6fea Binary files /dev/null and b/examples/PhasorDynamics/Partitions/TenGen/TenGen.png differ diff --git a/examples/PhasorDynamics/Partitions/WECC240/WECC240.partition.json b/examples/PhasorDynamics/Partitions/WECC240/WECC240.partition.json new file mode 100644 index 000000000..ddc07e17c --- /dev/null +++ b/examples/PhasorDynamics/Partitions/WECC240/WECC240.partition.json @@ -0,0 +1,288 @@ +{ + "name": "WECC240", + "partitions": [ + { + "name": "SoCal", + "internal": [ + 1403, + 2000, + 2030, + 2100, + 2130, + 2201, + 2202, + 2203, + 2233, + 2301, + 2302, + 2332, + 2400, + 2401, + 2402, + 2403, + 2405, + 2406, + 2407, + 2408, + 2409, + 2410, + 2411, + 2431, + 2434, + 2438, + 2439, + 2501, + 2502, + 2503, + 2533, + 2600, + 2601, + 2602, + 2603, + 2605, + 2606, + 2607, + 2608, + 2609, + 2610, + 2611, + 2612, + 2613, + 2614, + 2615, + 2616, + 2617, + 2618, + 2619, + 2620, + 2621, + 2630, + 2631, + 2637, + 2638, + 2901, + 2902 + ], + "external": [ + 1201, + 1301, + 1401, + 2404 + ] + }, + { + "name": "NorCal", + "internal": [ + 3101, + 3102, + 3103, + 3104, + 3105, + 3133, + 3135, + 3201, + 3202, + 3203, + 3204, + 3205, + 3234, + 3301, + 3302, + 3303, + 3304, + 3305, + 3333, + 3401, + 3402, + 3403, + 3404, + 3405, + 3432, + 3433, + 3501, + 3531, + 3601, + 3631, + 3701, + 3731, + 3801, + 3802, + 3803, + 3804, + 3805, + 3806, + 3831, + 3835, + 3836, + 3891, + 3892, + 3893, + 3894, + 3895, + 3896, + 3897, + 3901, + 3902, + 3903, + 3904, + 3905, + 3906, + 3907, + 3908, + 3909, + 3910, + 3911, + 3912, + 3913, + 3914, + 3915, + 3916, + 3917, + 3918, + 3919, + 3920, + 3921, + 3922, + 3923, + 3924, + 3925, + 3926, + 3931, + 3932, + 3933, + 8002, + 8003, + 8004, + 8005, + 8033, + 8034 + ], + "external": [ + 2404, + 4001, + 6402, + 8001 + ] + }, + { + "name": "Southwest", + "internal": [ + 1001, + 1002, + 1003, + 1004, + 1032, + 1034, + 1101, + 1102, + 1131, + 1202, + 1232, + 1302, + 1303, + 1331, + 1333, + 1402, + 1431, + 2604, + 2634, + 6101, + 6102, + 6103, + 6104, + 6132, + 6201, + 6202, + 6203, + 6204, + 6205, + 6231, + 6235, + 6301, + 6302, + 6303, + 6304, + 6305, + 6333, + 6335, + 6401, + 6403, + 6404, + 6433, + 6501, + 6502, + 6503, + 6504, + 6505, + 6506, + 6507, + 6508, + 6509, + 6510, + 6533, + 7001, + 7002, + 7031, + 7032 + ], + "external": [ + 1201, + 1301, + 1401, + 4003, + 4005, + 4008, + 4102, + 4103, + 6402 + ] + }, + { + "name": "Northwest", + "internal": [ + 4002, + 4004, + 4006, + 4007, + 4009, + 4010, + 4031, + 4035, + 4039, + 4090, + 4091, + 4092, + 4093, + 4094, + 4095, + 4096, + 4097, + 4101, + 4104, + 4131, + 4132, + 4201, + 4202, + 4203, + 4204, + 4231, + 4232, + 5001, + 5002, + 5003, + 5004, + 5031, + 5032 + ], + "external": [ + 4001, + 4003, + 4005, + 4008, + 4102, + 4103, + 8001 + ] + } + ] +} diff --git a/examples/PhasorDynamics/Partitions/WECC240/WECC240.png b/examples/PhasorDynamics/Partitions/WECC240/WECC240.png new file mode 100644 index 000000000..b39600056 Binary files /dev/null and b/examples/PhasorDynamics/Partitions/WECC240/WECC240.png differ