From 9567f97cb1bea524f1f41f7ba991e3b4f980482c Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sun, 30 Aug 2026 15:07:04 -0700 Subject: [PATCH 1/4] add DC components related to Raw files, and some assessment of dynamic capabilities that may be imported from other tools --- config/cmake/addSundials.cmake | 40 +++ .../developer-guide/openipsl-compatibility.md | 154 +++++++++ .../powerdynamics-compatibility.md | 92 +++++ .../psse-raw-dc-compatibility.md | 75 +++++ src/fileInput/gridDynReadRAW.cpp | 318 +++++++++++++++++- src/griddyn/CMakeLists.txt | 2 + src/griddyn/links/RawDcLine.cpp | 245 ++++++++++++++ src/griddyn/links/RawDcLine.h | 85 +++++ test/systemTests/testInputs.cpp | 40 +++ .../input_tests/psse_dc_components.raw | 33 ++ 10 files changed, 1081 insertions(+), 3 deletions(-) create mode 100644 docs/developer-guide/openipsl-compatibility.md create mode 100644 docs/developer-guide/powerdynamics-compatibility.md create mode 100644 docs/developer-guide/psse-raw-dc-compatibility.md create mode 100644 src/griddyn/links/RawDcLine.cpp create mode 100644 src/griddyn/links/RawDcLine.h create mode 100644 test/test_files/input_tests/psse_dc_components.raw diff --git a/config/cmake/addSundials.cmake b/config/cmake/addSundials.cmake index c89fd3a2a..749d8ae62 100644 --- a/config/cmake/addSundials.cmake +++ b/config/cmake/addSundials.cmake @@ -146,6 +146,20 @@ set(SUNDIALS_TEST_ENABLE_UNIT_TESTS OFF CACHE INTERNAL "") set(SUNDIALS_TEST_ENABLE_DIFF_OUTPUT OFF CACHE INTERNAL "") set(SUNDIALS_TEST_ANSWER_DIR "" CACHE INTERNAL "") +# SUNDIALS defaults this Podman-specific argument even when it discovers and +# uses Docker. Docker rejects --tls-verify, so clear the optional container +# arguments before SUNDIALS creates its local-CI helper targets. GridDyn does +# not use those targets for its normal build or test flow. +set(SUNDIALS_TEST_CONTAINER_RUN_EXTRA_ARGS + "" + CACHE STRING "Extra arguments to pass to Docker/Podman for SUNDIALS local CI targets" FORCE +) + +option(GRIDDYN_ENABLE_SUNDIALS_LOCAL_CI + "Include SUNDIALS Docker/Podman local-CI helper targets in the Visual Studio solution build" + OFF +) + if(${PROJECT_NAME}_ENABLE_OPENMP_SUNDIALS) set(SUNDIALS_ENABLE_OPENMP ON CACHE INTERNAL "") endif() @@ -166,6 +180,32 @@ endif() add_subdirectory("${sundials_SOURCE_DIR}" "${sundials_BINARY_DIR}") +# SUNDIALS creates these developer-only targets whenever Docker or Podman is +# discovered. They are not part of GridDyn's test suite and must not start a +# container as a side effect of Visual Studio's Build Solution command. +if(NOT GRIDDYN_ENABLE_SUNDIALS_LOCAL_CI) + string(TOLOWER "${SUNDIALS_PRECISION}" _griddyn_sundials_precision) + foreach(_griddyn_sundials_local_ci_target + setup_local_ci + test_local_ci + setup_local_ci_${SUNDIALS_INDEX_SIZE}_${_griddyn_sundials_precision} + test_local_ci_${SUNDIALS_INDEX_SIZE}_${_griddyn_sundials_precision} + ) + if(TARGET ${_griddyn_sundials_local_ci_target}) + set_property( + TARGET ${_griddyn_sundials_local_ci_target} + PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE + ) + set_property( + TARGET ${_griddyn_sundials_local_ci_target} + PROPERTY EXCLUDE_FROM_ALL TRUE + ) + endif() + endforeach() + unset(_griddyn_sundials_precision) + unset(_griddyn_sundials_local_ci_target) +endif() + if(NOT MSVC) set(CMAKE_C_FLAGS "${_griddyn_saved_c_flags}") set(CMAKE_CXX_FLAGS "${_griddyn_saved_cxx_flags}") diff --git a/docs/developer-guide/openipsl-compatibility.md b/docs/developer-guide/openipsl-compatibility.md new file mode 100644 index 000000000..1a0a3c81e --- /dev/null +++ b/docs/developer-guide/openipsl-compatibility.md @@ -0,0 +1,154 @@ +# OpenIPSL dynamic-model assessment + +This document inventories the local OpenIPSL Modelica library as potential GridDyn C++ models and validation references. It uses the same evidence standard as the [ANDES compatibility roadmap](andes-compatibility.md): a shared model name or state count is not evidence of compatible dynamics. + +## Source snapshot and scope + +The assessment was refreshed from `C:\data\Documents\codeProjects\OpenIPSL` at commit `8155c73f51ceeec935eb158247c7c043eb697ff5` (2026-04-13). Its package metadata identifies it as OpenIPSL `3.2.0-dev`. The inventory below covers the primary models under `OpenIPSL.Electrical`; it deliberately groups base classes, helper blocks, connector variants, and examples under their composed model rather than treating each as an independent GridDyn feature. + +OpenIPSL is Modelica source, not an input format that GridDyn can read. GridDyn currently has optional FMI 2 import support in `src/fmi/`, but no Modelica or OpenIPSL reader. There are therefore two distinct integration routes: + +1. **Native C++ model:** port a Modelica model's equations and parameters into GridDyn, add the normal input/attachment path, and validate it against captured OpenIPSL results. This is the preferred path for common synchronous and renewable models. +2. **FMI boundary:** compile a pinned OpenIPSL model with a Modelica tool and use a GridDyn FMI wrapper. This is suitable for an isolated pilot, but is not native OpenIPSL support and still needs interface, initialization, event, and solver-coupling tests. + +## Priority and status + +Priority reflects expected usefulness, reuse of existing GridDyn code, and prerequisite cost. It is intentionally rough, not a commitment. + +| Priority | Meaning | +| --- | --- | +| **P0 — validate now** | A close GridDyn implementation already exists; create OpenIPSL references and establish equation/trajectory parity before adding more models. | +| **P1 — high-value native work** | Common dynamic model or enabling interface; begin after, or alongside, the relevant P0 baseline. | +| **P2 — targeted extension** | Valuable for particular studies, but specialized or dependent on P1 infrastructure. | +| **P3 — architectural/research** | Requires unbalanced, electromagnetic, stochastic, or other GridDyn capabilities beyond a self-contained positive-sequence model. | + +| Status | Meaning | +| --- | --- | +| Existing candidate | GridDyn has a plausible model, but no OpenIPSL parity result. | +| New model | GridDyn lacks a model-specific analogue. | +| Structural extension | The primary missing piece is a connection, controller, measurement, or event interface. | + +No row is currently *validated against OpenIPSL*. Existing ANDES/PSSE DYR references and GridDyn component tests are useful evidence for P0 selection, not Modelica parity. + +## Priority overview + +| Order | Deliverable | Why it comes first | +| --- | --- | --- | +| 1 | P0 synchronous-machine and basic-control reference suite | Establishes conventions for bases, dq signs, initialization, limits, and event comparison. | +| 2 | P1 PSS/e machine, governor, exciter, and induction-motor gaps | Extends conventional-transmission dynamic studies without changing GridDyn's network abstraction. | +| 3 | P1 WECC renewable foundation: `REGCA1` + `REECA1/REECB1` + `REPCA1` | A composable inverter/current-limit/plant-control interface enables the highest-value modern generation models. | +| 4 | P2 specialized controls, FACTS, wind/PV detail, and dynamic load behavior | Builds on the validated controller and converter interfaces. | +| 5 | P3 three-phase, mono/tri, and VSD models | Requires an unbalanced multi-conductor network and is not a direct extension of GridDyn's present positive-sequence dynamic path. | + +## 1. Synchronous machines and induction motors + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| `Machines.PSSE.GENROU` | `GenModelGENROU` | **P0 — Existing candidate** | Best first machine reference. Compare flux/transient states, saturation, dq current, rotor angle/speed, electrical power, and machine/system-base conversion. | +| `Machines.PSSE.GENCLS` | `GenModelClassical` | **P0 — Existing candidate** | Validate swing-equation sign, damping, initial angle, and power/base conventions. | +| `Machines.PSAT.Order2`, `Order3`, `Order4`, `Order5_Type1`, `Order5_Type2`, `Order6` | `GenModelClassical`, `GenModel3`, `GenModel4`, `GenModel5`, `GenModel5type2`, `GenModel6/6type2` | **P0 — Existing candidates** | Match each order individually. Equal order does not establish matching stator algebraics, saturation, or initialization. | +| `Machines.PSAT.MotorTypeI`, `MotorTypeIII`, `MotorTypeV` | `MotorLoad3`, `MotorLoad5` | **P1 — Existing candidates** | Audit slip, mechanical torque, stall/protection, and initialization before choosing a mapping. | +| `Machines.PSSE.CIM5`, `CIM6` | No verified exact analogue | **P1 — New models** | PSS/e induction-motor models are a high-value extension beyond the current motor candidates; model single-/double-cage behavior, saturation, load torque, and type flags explicitly. | +| `Machines.PSSE.GENSAL`, `GENSAE`, `GENROE` | No exact model | **P1 — New models** | Add salient-pole and/or exponential-saturation equations. Do not silently substitute `GENROU`. | +| `Machines.PSSE.GENTPJ` | No exact model | **P2 — New model** | WECC type-J round-rotor machine with saturation on both axes; prioritize after the conventional machine base is validated. | +| `Machines.PSSE.Plant` | Generator plus controller attachment | **P1 — Structural extension** | OpenIPSL's replaceable machine/exciter/governor/PSS composition is a good reference for a declarative GridDyn dynamic-plant assembly API. | + +## 2. Excitation, limiting, and voltage compensation + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| `Controls.PSSE.ES.SEXS` | `ExciterSEXS` | **P0 — Existing candidate** | Validate voltage sensing, lead-lag convention, limits, and field-voltage initialization. | +| `Controls.PSSE.ES.EXST1` | `ExciterEXST1` | **P0 — Existing candidate** | Compare limiter selector behavior; decide and document support for zero-time-constant bypasses. | +| `Controls.PSSE.ES.EXAC1`, `EXAC2` | `ExciterEXAC1`, `ExciterEXAC2` | **P0 — Existing candidates** | Validate measured-voltage path, saturation/rectifier loading, and all limits. | +| `Controls.PSSE.ES.ESDC1A`, `ESDC2A` | `ExciterDC1A`, `ExciterDC2A` | **P1 — Existing candidates** | Audit transducer, switching, and saturation paths before claiming equivalence. | +| `Controls.PSSE.ES.IEEET1`, `IEEET2` | `ExciterIEEEtype1`, `ExciterIEEEtype2` | **P1 — Existing candidates** | GridDyn's generic class names need model-specific equation and limiter audits. | +| `Controls.CGMES.ES.ExcSEXS` | `ExciterSEXS` | **P1 — Existing candidate** | Validate the CGMES signal and parameter conventions separately from the PSS/e wrapper. | +| `Controls.PSSE.ES.AC7B`, `AC8B`, `DC4B`, `ESST1A`, `ESST2A`, `ESST4B`, `ESAC1A`, `ESAC2A` | No exact models | **P1 — New models** | Prioritize the models required by target PSS/e cases. They require explicit limiter/root treatment and a reusable exciter block library. | +| `Controls.PSSE.ES.IEEEX1`, `SCRX`, `ST5B`, `URST5T`, `EXNI`, `EXBAS`, `ESURRY` | No exact models | **P2 — New models** | Add after the common AC/DC exciter blocks are established. | +| `Controls.PSSE.OEL.OEL`, `Controls.PSSE.UEL.MNLEX2`, `Controls.PSSE.COMP.IEEEVC` | No exact analogue | **P1 — Structural extension** | Add field-current/under-excitation and terminal-current-compensation input paths to the exciter interface; then port the model-specific limits. | +| PSAT `AVRTypeI/II/III`, `OEL`, `FieldCurrent`; Simulink excitation/OEL blocks | Existing exciters are only candidates | **P2 — Existing candidates / new blocks** | Treat the PSAT and Simulink families as separate equations, not aliases of PSS/e names. | + +## 3. Turbine-governors and power-system stabilizers + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| `Controls.PSSE.TG.TGOV1` | `GovernorTgov1` | **P0 — Existing candidate** | GridDyn already has DYR-order, limiter, initialization, Jacobian, and isolated-trajectory coverage; make it the first OpenIPSL governor comparison. | +| `Controls.PSSE.TG.IEEEG1` | `GovernorIeeeG1` | **P0 — Existing candidate** | Compare lead/lag ordering, turbine fractions, rate/position limits, and multi-machine attachment. | +| `Controls.PSSE.PSS.IEEEST` | `StabilizerIEEEST` | **P0 — Existing candidate** | Test zero-time-constant bypasses, filters, lead-lag states, and output limits. | +| `Controls.PSSE.TG.HYGOV`, `IEESGO` | `GovernorHydro`, `GovernorReheat` are candidates | **P1 — Existing candidates** | Perform an equation, water-column/reheat, limit, and parameter-unit audit before reuse. | +| `Controls.PSSE.TG.GAST`, `GGOV1`, `GGOV1DU`, `DEGOV`, `IEEEG2`, `WEHGOV`, `WPIDHY`, `WSIEG1` | No exact models | **P1/P2 — New models** | Start with `GAST` and `GGOV1` (**P1**) if common PSS/e cases require them. The remaining engine/wind/hydro variants are **P2** after reusable governor limit and mode-selector blocks exist. | +| `Controls.PSSE.TG.ConstantPower`; PSAT `TGTypeI–VI`; CGMES `GovHydroIEEE0`; Simulink TG blocks | Basic/reheat/hydro classes are candidates | **P2 — Existing candidates / new models** | Establish whether a GridDyn governor can expose the reference and mechanical-power ports required by each family. | +| `Controls.PSSE.PSS.PSS2A`, `PSS2B`, `IEE2ST`, `STAB2A`, `STAB3`, `STABNI`, `STBSVC` | No exact model | **P2 — New models** | Add dual/remote measurement inputs, filters, mode selection, and signal routing. | +| PSAT `PSSTypeI/II/III`; Simulink PSS; `DisabledPSS` | `StabilizerIEEEST` / `StabilizerST2CUT` are only candidates | **P2 — Existing candidates / new blocks** | Keep the source-family semantics explicit. `DisabledPSS` is mainly a plant-assembly behavior. | + +## 4. Renewable, inverter, wind, solar, and storage models + +This is the principal new high-value OpenIPSL family. GridDyn's `GenModelInverter` is not an implementation of these models; a reusable positive-sequence converter/current-controller interface is the prerequisite. + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| `Renewables.PSSE.InverterInterface.REGCA1` | Generic `GenModelInverter` only | **P1 — New model** | First native renewable block: terminal-voltage filtering, current limits, LVAC logic, and P/Q current commands. | +| `Renewables.PSSE.ElectricalController.REECA1`, `REECB1`, `REECCU1` | No exact model | **P1 — New models** | Add on top of `REGCA1`; preserve reactive-current priority, voltage-dip logic, limit ordering, and frozen-state behavior. | +| `Renewables.PSSE.PlantController.REPCA1` | No exact model | **P1 — New model** | Plant-level voltage/reactive/active-power/frequency control; requires local and remote measurement interfaces. | +| `Renewables.PSSE.Wind`, `PV`, `BESS` | No exact model | **P1 — Structural extension** | Compose `REGCA1` + `REEC*` + `REPCA1` into attachable plant templates. BESS additionally needs energy/state-of-charge semantics before it is a complete storage model. | +| `Renewables.PSSE.WindDriveTrain.WTDTA1` | No exact model | **P2 — New model** | Add after the renewable plant electrical interface; model shaft/torsional states and mechanical-power connection. | +| `Renewables.PSSE.AddOnBlocks.IrradianceToPower` | `FileLoad`-like signal sources only | **P2 — Structural extension** | Define time-series/irradiance input policy and clipping before adding PV availability behavior. | +| `Wind.PSSE.WT3G*`, `WT4G*`; PSAT type-3; GE type-3 | No exact model | **P2 — New model families** | Port only after the generic renewable controller foundation. Prefer mapping each vendor/standard block to the reusable components instead of a monolithic generator class. | +| PSAT constant-PQ PV; PowerFactory WECC `PVD1`; PowerFactory DIgSILENT PV plant/array/control models | No exact model | **P2 — New model families** | Use as validation/extension targets after the `REGC/REEC/REPCA` path. Detailed PV-array/DC-link models may be outside phasor-time-domain scope. | +| `VSD.Generic.AC2DCandDC2AC`, `VoltsHertzController` | No exact model | **P3 — Structural extension** | Cross-domain power-electronics models require a justified phasor/DC coupling contract and may be better isolated through FMI first. | + +## 5. Loads + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| PSAT `PQ`, `ZIP`, `ZIP_ExtInput` | `ZipLoad` | **P0 — Existing candidate** | Start with constant-P/Q and static ZIP coefficients; separately validate external-input semantics. | +| PSAT `FrequencyDependent` | `FDepLoad` / `FrequencySensitiveLoad` | **P0 — Existing candidate** | Compare frequency reference, exponent/damping signs, and voltage dependence. | +| PSAT `VoltageDependent` | `ExponentialLoad` | **P1 — Existing candidate** | Verify the exponent, base, and low-voltage behavior. | +| PSAT `Mixed` | `AggregateLoad` is a candidate | **P1 — Existing candidate** | Match composition, initialization, and contribution reporting. | +| PSAT `ExponentialRecovery`, `ThermostaticallyControlled` | No exact model | **P1/P2 — New models** | Exponential recovery is **P1** for long-term voltage studies; thermostatic load is **P2** and needs switching/population semantics. | +| PSAT `PQvar`; PSS/e `Load`, `Load_switch`, `Load_variation`, `Load_ExtInput` | `RampLoad`, `FileLoad`, `SourceLoad` are candidates | **P2 — Structural extension** | Define event, interpolation, and externally driven-input semantics; do not equate time-series behavior with a static load. | +| Noise injections | No exact model | **P3 — New capability** | Requires a reproducible stochastic process, seed management, and solver-compatible noise policy. | + +## 6. FACTS, branches, shunts, events, and measurements + +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --- | --- | --- | --- | +| PSS/e `CSVGN1`, `SVC`; PSAT `STATCOM` | `Svd` / `VSCShunt` are candidates | **P1/P2 — New models** | Start (**P1**) with an SVC controlled-susceptance interface and limits; STATCOM and its converter current/limit controls are **P2**. | +| PSAT `TCSC` | No exact model | **P2 — New model** | Add thyristor firing/reactance control and series-compensation limits. | +| `Branches.Generic.ULTC`, PSAT `ULTC_VoltageControl`, Simulink `LTC` | `AdjustableTransformer` is a building block | **P1 — Structural extension** | Add discrete tap position, deadband, delay, and event/root behavior. | +| `Branches.PSAT.PhaseShiftingTransformer` | Existing transformer links are candidates | **P2 — Structural extension** | Add controlled phase-shift logic after tap-control infrastructure exists. | +| `Branches.PSSE/PSAT.TwoWindingTransformer`, PSAT `ThreeWindingTransformer`, `PwLine`, fixed shunts/capacitor banks | `AcLine`, transformer links, `ThreeWindingTransformer`, `ZipLoad` shunts | **P0/P1 — Existing candidates** | Fixed network equivalents are **P0** base-conversion checks; switched/modified capacitor-bank controls are **P1** event/control work. | +| `Events.Breaker`, `PwFault`, `PwFaultPQ` | breakers, fault/event infrastructure, control relays | **P1 — Structural extension** | Validate fault formulation, event time, clearing, and topology-update semantics. | +| `Sensors.PwVoltage`, `PwCurrent`, `SoftPMU` | sensors, `Pmu`, bus/relay measurements | **P2 — Structural extension** | Establish phasor, filter, frequency, and reporting-time convention. | +| Voltage/current source models | `SourceLoad` is only a partial analogue | **P2 — New interface** | Add controlled-source injection only where required by validation cases. | + +## 7. Three-phase and mono/tri models + +OpenIPSL now contains three-phase buses, 1/2/3-phase lines, transformers and connection variants, capacitor banks, static/dynamic wye/delta loads, and mono/tri coupling models. GridDyn has some three-phase load support, but no matching unbalanced three-phase network, transformer-connection, or sequence/phase interface. + +| OpenIPSL family | Priority / status | Required GridDyn work | +| --- | --- | --- | +| `ThreePhase.Buses`, `Branches.Lines`, `Branches.Transformer`, `Banks` | **P3 — Architectural** | A multi-conductor unbalanced network formulation, phase-voltage/current connectors, and compatible sparse/Jacobian infrastructure. | +| `ThreePhase.Loads` static and dynamic wye/delta models | **P3 — Architectural** | Correct phase/neutral and delta coupling; existing `ThreePhaseLoad` is not sufficient evidence of equivalence. | +| `ThreePhase.Branches.MonoTri` | **P3 — Architectural** | A defined positive-sequence to three-phase/sequence coupling boundary, including transformer vector groups and zero/negative-sequence treatment. | + +## Validation policy + +For each mapping, retain a minimized OpenIPSL case and CSV/JSON reference data in the GridDyn test tree. Record the OpenIPSL commit, Modelica compiler/version, solver settings, source checksum, event definition, variable names/units, sampling times, and per-signal tolerances. Regular C++ tests must consume the checked-in reference; they must not require a Modelica toolchain. + +Compare in this order: + +1. **Equation audit:** parameters, inputs/outputs, bases, signs, limits, and discontinuities. +2. **Initialization:** all dynamic and selected algebraic states at time zero. +3. **Equilibrium:** no-disturbance run with bounded drift. +4. **Trajectory:** common-time samples through a small step and a cleared network event; verify event times separately. + +Solver differences can justify reviewed numeric tolerance. They do not justify a different limiter transition, event sequence, or post-event equilibrium. + +## Immediate next actions + +1. Build P0 single-machine/infinite-bus OpenIPSL cases for `GENCLS` and `GENROU`, followed by `TGOV1`, `SEXS`, and `IEEEST`. +2. Add captured OpenIPSL initialization and trajectory references to GridDyn tests; do not run Modelica as part of the C++ suite. +3. Audit the P1 PSS/e candidates (`ESDC1A/2A`, `IEEET1/2`, `HYGOV`, `IEESGO`, `CIM5/6`) before selecting reuse or native ports. +4. Design the converter/controller attachment contract for `REGCA1` + `REEC*` + `REPCA1`; then use it for Wind, PV, and BESS templates. +5. Defer three-phase/VSD work until a project decision establishes the desired unbalanced-network scope. diff --git a/docs/developer-guide/powerdynamics-compatibility.md b/docs/developer-guide/powerdynamics-compatibility.md new file mode 100644 index 000000000..42a949b6b --- /dev/null +++ b/docs/developer-guide/powerdynamics-compatibility.md @@ -0,0 +1,92 @@ +# PowerDynamics.jl dynamic-model assessment + +This document identifies dynamic models implemented by the local [PowerDynamics.jl](https://github.com/JuliaEnergy/PowerDynamics.jl) project that GridDyn does not currently implement as a model-specific C++ class. It complements the [OpenIPSL assessment](openipsl-compatibility.md): several PowerDynamics models are explicit Julia ports of OpenIPSL PSS/e models, so PowerDynamics supplies implementation and regression-test evidence rather than a separate physical-model family. + +## Scope and evidence + +The inspected checkout is `C:\data\Documents\codeProjects\PowerDynamics.jl`, commit `908306c67b85fb24249955277b97e2e4f3d9b837` (2026-08-28). + +The primary inventory is the public model list in `src/Library/Library.jl`. The project registers OpenIPSL comparison tests for `GENCLS`, `GENROU`, `GENROE`, `GENSAL`, `GENSAE`, `IEEET1`, `SCRX`, `ESST1A`, `ESST4B`, `EXST1`, `IEEEG1`, `GGOV1`, `HYGOV`, and `IEEEST` in `test/runtests.jl`. Its `test/OpenIPSL_test/ModelTransferRules.md` also documents the source-model-to-Julia translation and reference-generation workflow. + +That is strong source material, but it is not GridDyn validation. A C++ port still needs an equation audit, GridDyn initialization test, and checked-in reference trajectory. Do not run Julia or a Modelica compiler from the regular GridDyn test suite. + +## Priority definitions + +| Priority | Meaning | +| --- | --- | +| **P0 — port and validate** | PowerDynamics has a model-specific implementation and OpenIPSL test path; GridDyn has no exact model. | +| **P1 — high-value extension** | A commonly useful GridDyn capability is missing, although PowerDynamics validation is incomplete or the work requires a reusable interface. | +| **P2 — targeted extension** | Useful but specialized, experimental, or dependent on a P0/P1 design decision. | +| **P3 — different simulation scope** | The model retains fast electromagnetic/filter states or needs structural changes beyond GridDyn's present positive-sequence dynamic path. | + +## Models GridDyn does not have exactly + +### 1. Synchronous machines + +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| --- | --- | --- | --- | +| `PSSE_GENROE` | No exact round-rotor exponential-saturation machine | **P0** | PowerDynamics has a registered OpenIPSL regression. It is the direct route to PSS/e `GENROE`, not a justification to substitute GridDyn `GENROU` (quadratic saturation). | +| `PSSE_GENSAL` | No exact salient-pole quadratic-saturation machine | **P0** | Has an OpenIPSL test path. Add a distinct salient-pole dq machine and compare saturation, flux states, current, angle, speed, and bases. | +| `PSSE_GENSAE` | No exact salient-pole exponential-saturation machine | **P0** | Has an OpenIPSL test path; naturally follows `GENSAL` once the salient-pole structure exists. | +| `SauerPaiMachine` | No verified exact equivalent | **P1** | A detailed synchronous-machine formulation with explicit dq flux and torque variables. Audit against `GenModel6`/`GenModel8` before deciding whether it is a new model or a validation target for an existing generic model. | +| `Swing` | No standalone swing-injection component | **P2** | A reusable mechanical-power-to-angle/frequency block with a prescribed voltage magnitude. GridDyn generator models overlap physically, but not necessarily as a composable network injection. | +| `VariableFrequencySlack` | No equivalent dynamic slack model identified | **P2** | Supplies a rotating slack voltage driven by a configurable frequency. Useful for benchmark/reference cases and grid-forming studies. | + +### 2. Excitation systems and governor controls + +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| --- | --- | --- | --- | +| `PSSE_SCRX` | No exact static exciter | **P0** | PowerDynamics has an OpenIPSL test. Requires its lead-lag, bus-/solid-fed switching, crowbar, and field-current behavior—not merely a generic high-gain AVR. | +| `PSSE_ESST1A` | No exact static exciter | **P0** | OpenIPSL-tested model with limiter, rectifier-loading, and feedback behavior; a useful common PSS/e exciter target. | +| `PSSE_ESST4B` | No exact static exciter | **P0** | OpenIPSL-tested model. It is distinct from GridDyn's existing `ExciterESST3A`. | +| `PSSE_GGOV1_EXPERIMENTAL` | No exact gas/engine governor | **P2** | This is a valuable specification/prototype for `GGOV1`, but PowerDynamics marks it experimental and its test documents known OpenIPSL/reference issues. Do an independent equation and reference audit before porting. | +| `PSSE_HYGOV` | `GovernorHydro` is a candidate, not exact support | **P1 validation** | PowerDynamics has an OpenIPSL comparison. Use it to decide whether GridDyn's hydro model can be corrected/validated or a dedicated `HYGOV` is needed. | +| `PSSE_IEEET1` | `ExciterIEEEtype1` is a candidate | **P1 validation** | The PowerDynamics/OpenIPSL test is a concrete starting point for proving or rejecting equivalence. | +| `AVRFixed`, `AVRTypeI`, `GovFixed`, `TurbineGovTypeI` | Existing GridDyn AVR/governor classes are only candidates | **P2** | Simple reusable controls; audit equations and ports before mapping by generic type name. | + +### 3. Inverters and converter controls + +| PowerDynamics model(s) | GridDyn status | Priority | Why it matters | +| --- | --- | --- | --- | +| `IdealDroopInverter` | No model-specific grid-forming droop inverter | **P1** | RMS-friendly voltage-source inverter with filtered P/Q droop, voltage/frequency setpoints, and angle state. A strong first grid-forming implementation target. | +| `DroopOuter`, `DroopInverter` | No equivalent composable grid-forming controller | **P1** | Separates the outer droop law from the electrical plant, which is a useful GridDyn architecture for testing and reuse. | +| `SimpleGFL`, `SimpleGFLDC` | No grid-following inverter with PLL/current-control/DC-link model | **P1/P3** | The AC current-control/PLL portion is **P1** converter-interface work; the DC-link capacitor and fast control details in `SimpleGFLDC` are **P3** unless GridDyn explicitly expands beyond phasor RMS dynamics. | +| `SimplePLL`, `PLL_LPF` | No reusable PLL component identified | **P1** | Needed by grid-following renewable models and by the OpenIPSL `REGCA1`/`REEC*`/`REPCA1` roadmap. | +| `LFilter`, `LCFilter`, `LCLFilter`, `VC`, `CC1`, `CC2`, controlled voltage/current sources | No equivalent composable filter/controller library | **P3** | These retain converter/filter electromagnetic states in a global dq frame. Use them as a design reference or FMI pilot unless a deliberate EMT-like GridDyn extension is approved. | + +### 4. Dynamic branches, shunts, and faults + +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| --- | --- | --- | --- | +| `DynamicSeriesRLBranch` | No AC series-RL branch with current states | **P1** | A positive-sequence dq branch with explicit R-L current dynamics and optional transformer ratios. This is distinct from GridDyn's algebraic `AcLine`; GridDyn's dynamic `DcLink` does not cover AC dq line states. | +| `DynamicCShunt` | No AC capacitor shunt with voltage/current states | **P2** | A dynamic capacitor branch; useful for converter/filter and fast-transient studies, but outside ordinary RMS phasor network models. | +| `DynamicParallelRCShunt` | No AC parallel RC shunt with states | **P2** | Extends the capacitor case with explicit resistor current. Prioritize only with a clear dynamic-network use case. | +| `RXGroundFault` | No separately attachable R-X ground-fault injection model | **P2** | GridDyn has line-fault and relay mechanisms, but this model is a controllable bus-ground impedance injection with active/reactive fault output. | +| `PiLine_fault` | No exact Pi-line fault component | **P2 validation** | GridDyn `AcLine` has fault-location support, so first compare semantics (fault impedance, location, clearing, and topology) rather than adding a duplicate class. | + +## Existing GridDyn overlap — use as validation targets, not new ports + +These PowerDynamics models should not be counted as clear GridDyn gaps. Their value is the available Julia/OpenIPSL formulation and tests. + +| PowerDynamics model(s) | GridDyn candidate | Recommended action | +| --- | --- | --- | +| `PSSE_GENCLS`, `PSSE_GENROU`, `ClassicalMachine` | `GenModelClassical`, `GenModelGENROU` | Add/compare against captured references before changing C++ equations. | +| `PSSE_EXST1`, `PSSE_IEEEST`, `PSSE_IEEEG1` | `ExciterEXST1`, `StabilizerIEEEST`, `GovernorIeeeG1` | Reuse PowerDynamics' tested Modelica cases to obtain another independent reference path. | +| `TGOV1` | `GovernorTgov1` | Validate instead of porting. | +| `PQLoad`, `ZIPLoad`, `VoltageDependentLoad`, `ConstantYLoad`, `ConstantCurrentLoad`, `PSSE_Load` | `ZipLoad`, `ExponentialLoad`, `SourceLoad` are candidates | Audit the voltage/current and low-voltage semantics; GridDyn has related static-load classes. | +| `PiLine`, `Breaker`, `StaticShunt` | `AcLine`, `ZBreaker`, fixed-admittance `ZipLoad` | Treat as power-flow/event and base-conversion validation cases. | + +## Recommended sequence + +1. Use PowerDynamics' OpenIPSL-tested source and test structures to create GridDyn P0 references for `GENROE`, `GENSAL`, `GENSAE`, `SCRX`, `ESST1A`, and `ESST4B`. +2. Audit and resolve the existing GridDyn candidates for `HYGOV` and `IEEET1`; do not create duplicates before the equation comparison. +3. Decide whether GridDyn will support positive-sequence converter control as a first-class model family. If so, implement the P1 `IdealDroopInverter`/PLL/controller interface and align it with the OpenIPSL `REGCA1`/`REEC*`/`REPCA1` plan. +4. Add `DynamicSeriesRLBranch` only with a clear solver and initialization design for AC current states. Keep dynamic shunts and dq filter models scoped to the same decision. +5. Treat PowerDynamics' `GGOV1` as a design aid, not validation evidence, until its documented reference issues are independently resolved. + +## Regression policy + +Keep a minimized source case and captured trajectory in GridDyn. Record the PowerDynamics commit, original OpenIPSL commit, Julia/Modelica tool versions if used, solver settings, event definition, variable names/units, sampling times, and signal tolerances. Compare initialization, no-disturbance equilibrium, trajectories, and event times separately. + +PowerDynamics is a useful independent implementation, but GridDyn must not claim compatibility merely because a Julia model or a similar C++ class exists. + diff --git a/docs/developer-guide/psse-raw-dc-compatibility.md b/docs/developer-guide/psse-raw-dc-compatibility.md new file mode 100644 index 000000000..e9a3916f8 --- /dev/null +++ b/docs/developer-guide/psse-raw-dc-compatibility.md @@ -0,0 +1,75 @@ +# PSS/E RAW DC compatibility status + +This note records the intended scope and known limitations of GridDyn's PSS/E +RAW DC import. It is a compatibility layer for the AC power-flow model used +by PowerModels.jl; it is not a replacement for GridDyn's physical DC-network +models (`DcBus`, `DcLink`, `AcDcConverter`, `VSCShunt`, and `Hvdc`). + +## Implemented scope + +The RAW reader recognizes these section labels: + +| RAW section | GridDyn representation | Power-flow behavior | +| --- | --- | --- | +| `BEGIN TWO-TERMINAL DC DATA` / `BEGIN TWO-TERMINAL DC LINE DATA` | `links::RawDcLine` | Scheduled active terminal transfer; PQ-terminal reactive variable and voltage setpoint | +| `BEGIN VOLTAGE SOURCE CONVERTER DATA` / `BEGIN VSC DC LINE DATA` | `links::RawDcLine` | PowerModels-compatible zero initial active transfer; PQ-terminal reactive variable and voltage setpoint | + +Two-terminal active transfer follows PowerModels' RAW conversion: + +- `MDC == 1`: `abs(SETVL)` MW. +- `MDC == 2`: `abs(SETVL / VSCHD / 1000)` MW. +- `MDC == 0`: out of service. + +For VSC data, the active transfer starts at zero, as it does in +PowerModels' PSS/E importer. The VSC loss slope and rating are retained on +the compatibility link; loss intercept and RAW limit fields are retained in +its description for diagnostics. + +`RawDcLine` keeps the active-transfer convention of PowerModels' `dcline` +model. At each connected PQ AC bus, it adds the reactive terminal variable +and voltage-magnitude equality needed to retain the bus reactive balance. +This is deliberately separate from GridDyn's physical DC components. + +## Numerical evidence + +The regression input is +`test/test_files/input_tests/psse_dc_components.raw`. It is solved by both +GridDyn and PowerModels' JuMP/IPOPT AC power flow. + +For the shared 100 MVA test case, both give these bus-2 results: + +| Quantity | Value | +| --- | ---: | +| voltage magnitude | `1.000000` pu | +| voltage angle | `-0.025284658` rad | + +The combined reactive power from the two RAW DC terminals at bus 2 is also +checked at `-0.0781963933423` pu. The regression is +`InputTests.PssERawDcComponentsImportAsScheduledLinks`. + +## Known differences and follow-up work + +| Topic | Current behavior | Required follow-up | +| --- | --- | --- | +| Physical HVDC equations | RAW records use the PowerModels-style AC-terminal `dcline` abstraction, not a DC grid or converter commutation model. | Map RAW data to GridDyn's native DC models only after validating the PSS/E control and base-conversion semantics. | +| Reactive limits | Two-terminal angle-derived limits and VSC `MINQ`/`MAXQ` are recorded as diagnostics, but are not solver-enforced. | Add limiter equations/state transitions and test constrained cases against a version-matched PSS/E reference. | +| Multiple DC terminals at one PQ bus | PowerModels creates redundant voltage equalities and has a non-unique split of terminal reactive power. GridDyn selects the first RAW DC terminal as the voltage controller and assigns the combined required reactive power there. Bus voltages, angles, and combined reactive power match. | Define and test a documented allocation rule if per-terminal reactive reporting must reproduce a particular PowerModels solver result. | +| VSC losses and nonzero active control | The PowerModels RAW importer initializes VSC active terminal powers at zero, so `loss0` and `loss1` do not alter the regression solution. GridDyn retains the slope as link loss metadata. | Add cases and equations for nonzero active VSC control and affine losses when the intended RAW/PSS/E semantics are established. | +| Active/reactive ratings | Ratings are imported for reporting/violation checks, not imposed as feasibility constraints. | Add solver-enforced apparent-power and terminal power limits. | +| Multi-terminal DC | `BEGIN MULTI-TERMINAL DC LINE DATA` remains unsupported and is skipped. | Implement a separate multi-terminal topology/model; do not flatten it into independent two-terminal transfers. | +| Dynamics | The compatibility link is for AC power flow only. | Validate physical LCC/VSC controls and trajectories against PSS/E or CIGRE benchmark cases using GridDyn's native DC classes. | +| RAW-version coverage | Field handling is based on PowerModels' importer and representative RAW records, not a complete version-specific PSS/E data-format audit. | Audit against the licensed PSS/E RAW manual for each advertised RAW version, including defaults and all control modes. | + +## Validation procedure + +Use PowerModels' JuMP/IPOPT path, rather than its native `compute_ac_pf`, +because the latter does not support `dcline` records: + +```julia +solve_ac_pf("psse_dc_components.raw", Ipopt.Optimizer) +``` + +Compare bus voltage magnitude/angle, active generation, active DC terminal +flows, and the total reactive DC terminal injection at each bus. Individual +reactive flows at a bus with multiple DC terminals should only be compared +after choosing an explicit allocation convention. diff --git a/src/fileInput/gridDynReadRAW.cpp b/src/fileInput/gridDynReadRAW.cpp index aeef937cb..98bebe83d 100644 --- a/src/fileInput/gridDynReadRAW.cpp +++ b/src/fileInput/gridDynReadRAW.cpp @@ -13,9 +13,12 @@ #include "griddyn/Generator.h" #include "griddyn/GridBus.h" #include "griddyn/GridDynSimulation.h" +#include "griddyn/Link.h" #include "griddyn/Load.h" +#include "griddyn/primary/AcBus.h" #include "griddyn/links/AcLine.h" #include "griddyn/links/AdjustableTransformer.h" +#include "griddyn/links/RawDcLine.h" #include "griddyn/links/ThreeWindingTransformer.h" #include "griddyn/loads/Svd.h" #include "griddyn/primary/AcBus.h" @@ -33,6 +36,7 @@ #include #include #include +#include #include #include @@ -259,6 +263,17 @@ static void rawReadTXadj(CoreObject* parentObject, std::vector& busList, BasicReaderInfo& opt); +static void rawReadTwoTerminalDc(CoreObject* parentObject, + const std::array& records, + const std::vector& busList, + index_t sequence, + std::unordered_set& voltageControlledBuses); +static void rawReadVscDc(CoreObject* parentObject, + const std::array& records, + const std::vector& busList, + index_t sequence, + std::unordered_set& voltageControlledBuses); + // static int rawReadDCLine(CoreObject* parentObject, // stringVec& txlines, // std::vector& busList, @@ -274,7 +289,9 @@ namespace { GENERATOR, TX, SWITCHED_SHUNT, - TXADJ + TXADJ, + TWO_TERMINAL_DC, + VSC_DC }; } // namespace @@ -448,6 +465,8 @@ void loadRaw(CoreObject* parentObject, stringVec txlines; txlines.resize(5); int tline = 5; + index_t dcLineSequence = 0; + std::unordered_set dcVoltageControlledBuses; bool moreSections = true; @@ -559,6 +578,9 @@ void loadRaw(CoreObject* parentObject, std::getline(file, txlines[3]); std::getline(file, txlines[4]); } + if (!moreData) { + break; + } if (opt.version >= 33) { tline = rawReadTxV33( parentObject, txlines, busList, opt, impedanceCorrectionTables); @@ -568,6 +590,62 @@ void loadRaw(CoreObject* parentObject, } } break; + case SectionType::TWO_TERMINAL_DC: + while (moreData) { + if (checkNextLine(file, line)) { + std::array records{line, {}, {}}; + if (!std::getline(file, records[1]) || !std::getline(file, records[2])) { + std::cerr << "Incomplete two-terminal DC record\n"; + moreData = false; + moreSections = false; + continue; + } + trimString(records[1]); + trimString(records[2]); + if (records[1].empty() || records[2].empty() || records[1][0] == '0' || + records[2][0] == '0') { + std::cerr << "Incomplete two-terminal DC record\n"; + moreData = false; + continue; + } + rawReadTwoTerminalDc(parentObject, + records, + busList, + ++dcLineSequence, + dcVoltageControlledBuses); + } else { + moreData = false; + } + } + break; + case SectionType::VSC_DC: + while (moreData) { + if (checkNextLine(file, line)) { + std::array records{line, {}, {}}; + if (!std::getline(file, records[1]) || !std::getline(file, records[2])) { + std::cerr << "Incomplete VSC DC record\n"; + moreData = false; + moreSections = false; + continue; + } + trimString(records[1]); + trimString(records[2]); + if (records[1].empty() || records[2].empty() || records[1][0] == '0' || + records[2][0] == '0') { + std::cerr << "Incomplete VSC DC record\n"; + moreData = false; + continue; + } + rawReadVscDc(parentObject, + records, + busList, + ++dcLineSequence, + dcVoltageControlledBuses); + } else { + moreData = false; + } + } + break; case SectionType::UNKNOWN: default: while (moreData) { @@ -589,6 +667,237 @@ void loadRaw(CoreObject* parentObject, file.close(); } +static GridBus* rawDcLookupBus(const std::vector& busList, int busNumber) +{ + if ((busNumber <= 0) || std::cmp_greater_equal(busNumber, busList.size()) || + busList[busNumber] == nullptr) { + return nullptr; + } + return busList[busNumber]; +} + +static double rawDcField(const stringVector& record, size_t index); + +/** + * Add the simple, AC-terminal DC-line representation used by PowerModels for + * PSS/E RAW imports. RawDcLine represents the scheduled active transfer and + * PowerModels-style terminal reactive/voltage equations, rather than a + * physical DC network or converter. This preserves the existing physical DC + * and converter models. + */ +static links::RawDcLine* addRawDcCompatibilityLink(CoreObject* parentObject, + GridBus* fromBus, + GridBus* toBus, + index_t sequence, + double scheduledPower, + double lossFraction, + double rating, + bool enabled, + double fromVoltageTarget, + double toVoltageTarget, + bool controlFromVoltage, + bool controlToVoltage, + const std::string& description) +{ + auto* link = new links::RawDcLine(parentObject->getName() + "_psse_dc_" + + std::to_string(sequence)); + link->setDescription(description); + link->updateBus(fromBus, 1); + link->updateBus(toBus, 2); + try { + parentObject->add(link); + } + catch (const ObjectAddFailure&) { + addToParentWithRename(link, parentObject); + } + + link->set("pset", scheduledPower, MW); + link->set("lossfraction", lossFraction); + link->set("from_vtarget", fromVoltageTarget); + link->set("to_vtarget", toVoltageTarget); + link->set("from_voltage_control", controlFromVoltage ? 1.0 : 0.0); + link->set("to_voltage_control", controlToVoltage ? 1.0 : 0.0); + if (rating > 0.0) { + link->set("rating", rating, MW); + } + if (!enabled) { + // Keep an out-of-service DC record from contributing to either AC + // terminal without allowing Link::disable() to cascade to a bus. + link->switchMode(1, true); + link->switchMode(2, true); + } + return link; +} + +static bool rawDcUseVoltageControl(GridBus* bus, + int busNumber, + std::unordered_set& voltageControlledBuses) +{ + auto* acBus = dynamic_cast(bus); + if ((acBus == nullptr) || + (acBus->getMode(cPflowSolverMode) != static_cast(GridBus::BusType::PQ))) { + return false; + } + // PowerModels adds a voltage equality for every dcline terminal. The + // equations are redundant if several RAW DC records share a PQ bus. Keep + // one controller and retain the other terminal's specified q=0 start + // value, producing a nonsingular GridDyn system with the same voltage. + return voltageControlledBuses.insert(busNumber).second; +} + +static void rawReadTwoTerminalDc(CoreObject* parentObject, + const std::array& records, + const std::vector& busList, + index_t sequence, + std::unordered_set& voltageControlledBuses) +{ + const auto header = splitlineQuotes(records[0]); + const auto rectifier = splitline(records[1]); + const auto inverter = splitline(records[2]); + if ((header.size() < 5) || rectifier.empty() || inverter.empty()) { + std::cerr << "Invalid two-terminal DC record\n"; + return; + } + + const auto mdc = numeric_conversion(header[1], 0); + const auto setvl = numeric_conversion(header[3], 0.0); + const auto vschd = numeric_conversion(header[4], 0.0); + double powerDemand = 0.0; + if (mdc == 1) { + powerDemand = std::abs(setvl); + } else if (mdc == 2) { + if (vschd == 0.0) { + std::cerr << "Two-terminal DC current-control record has zero VSCHD\n"; + } else { + // Match PowerModels' PSS/E RAW dcline conversion exactly. + powerDemand = std::abs(setvl / vschd / 1000.0); + } + } + + const auto fromBusNumber = numeric_conversion(rectifier[0], 0); + const auto toBusNumber = numeric_conversion(inverter[0], 0); + auto* fromBus = rawDcLookupBus(busList, fromBusNumber); + auto* toBus = rawDcLookupBus(busList, toBusNumber); + if ((fromBus == nullptr) || (toBus == nullptr)) { + std::cerr << "Invalid AC bus in two-terminal DC record\n"; + return; + } + + const auto name = std::string(trim(removeQuotes(header[0]))); + const auto resistance = numeric_conversion(header[2], 0.0); + const auto fromVoltageTarget = fromBus->getVoltage(); + const auto toVoltageTarget = toBus->getVoltage(); + const auto fromQmin = -powerDemand * std::cos(rawDcField(rectifier, 3) * kPI / 180.0); + const auto toQmin = -powerDemand * std::cos(rawDcField(inverter, 3) * kPI / 180.0); + addRawDcCompatibilityLink( + parentObject, + fromBus, + toBus, + sequence, + powerDemand, + 0.0, + powerDemand, + mdc != 0, + fromVoltageTarget, + toVoltageTarget, + rawDcUseVoltageControl(fromBus, fromBusNumber, voltageControlledBuses), + rawDcUseVoltageControl(toBus, toBusNumber, voltageControlledBuses), + "PSS/E RAW two-terminal DC compatibility import; name='" + name + "', MDC=" + + std::to_string(mdc) + ", RDC=" + std::to_string(resistance) + ", SETVL=" + + std::to_string(setvl) + ", VSCHD=" + std::to_string(vschd) + ", qminf=" + + std::to_string(fromQmin) + ", qmaxf=0, qmint=" + std::to_string(toQmin) + + ", qmaxt=0"); +} + +static double rawDcField(const stringVector& record, size_t index) +{ + return (index < record.size()) ? numeric_conversion(record[index], 0.0) : 0.0; +} + +static double rawVscTransferLimit(const stringVector& converter) +{ + // PSS/E VSC converter fields: SMAX, IMAX, PWF, MAXQ, MINQ start at 8. + if (converter.size() < 13) { + return 0.0; + } + const auto smax = rawDcField(converter, 8); + const auto imax = rawDcField(converter, 9); + if ((smax == 0.0) && (imax == 0.0)) { + return std::max(std::abs(rawDcField(converter, 11)), std::abs(rawDcField(converter, 12))); + } + return std::min(imax, smax); +} + +static void rawReadVscDc(CoreObject* parentObject, + const std::array& records, + const std::vector& busList, + index_t sequence, + std::unordered_set& voltageControlledBuses) +{ + const auto header = splitlineQuotes(records[0]); + const auto fromConverter = splitline(records[1]); + const auto toConverter = splitline(records[2]); + if ((header.size() < 3) || (fromConverter.size() < 2) || (toConverter.size() < 2)) { + std::cerr << "Invalid VSC DC record\n"; + return; + } + + const auto fromBusNumber = numeric_conversion(fromConverter[0], 0); + const auto toBusNumber = numeric_conversion(toConverter[0], 0); + auto* fromBus = rawDcLookupBus(busList, fromBusNumber); + auto* toBus = rawDcLookupBus(busList, toBusNumber); + if ((fromBus == nullptr) || (toBus == nullptr)) { + std::cerr << "Invalid AC bus in VSC DC record\n"; + return; + } + + const auto mdc = numeric_conversion(header[1], 0); + const auto fromType = numeric_conversion(fromConverter[1], 0); + const auto toType = numeric_conversion(toConverter[1], 0); + const auto name = std::string(trim(removeQuotes(header[0]))); + const auto resistance = numeric_conversion(header[2], 0.0); + const auto loss0 = + (rawDcField(fromConverter, 5) + rawDcField(toConverter, 5) + rawDcField(fromConverter, 7) + + rawDcField(toConverter, 7)) * + 1e-3; + const auto loss1 = (rawDcField(fromConverter, 6) + rawDcField(toConverter, 6)) * 1e-3; + const auto rating = + std::max(rawVscTransferLimit(fromConverter), rawVscTransferLimit(toConverter)); + const auto fromVoltageTarget = + (rawDcField(fromConverter, 2) == 1.0) ? + rawDcField(fromConverter, 4) : + 1.0; + const auto toVoltageTarget = + (rawDcField(toConverter, 2) == 1.0) ? + rawDcField(toConverter, 4) : + 1.0; + + // PowerModels initializes VSC RAW dclines at zero active and reactive flow. + // Retain its loss and limit data in the description while preserving that + // solvable, scheduled-link behavior. GridDyn's physical VSC models stay + // available for native DC-network inputs. + addRawDcCompatibilityLink( + parentObject, + fromBus, + toBus, + sequence, + 0.0, + loss1, + rating, + (mdc != 0) && (fromType != 0) && (toType != 0), + fromVoltageTarget, + toVoltageTarget, + rawDcUseVoltageControl(fromBus, fromBusNumber, voltageControlledBuses), + rawDcUseVoltageControl(toBus, toBusNumber, voltageControlledBuses), + "PSS/E RAW VSC DC compatibility import; name='" + name + "', MDC=" + + std::to_string(mdc) + ", RDC=" + std::to_string(resistance) + ", loss0=" + + std::to_string(loss0) + ", loss1=" + std::to_string(loss1) + ", qminf=" + + std::to_string(rawDcField(fromConverter, 12)) + ", qmaxf=" + + std::to_string(rawDcField(fromConverter, 11)) + ", qmint=" + + std::to_string(rawDcField(toConverter, 12)) + ", qmaxt=" + + std::to_string(rawDcField(toConverter, 11))); +} + static int getPSSversion(const std::string& line) { int ver = 29; @@ -618,11 +927,14 @@ static int getPSSversion(const std::string& line) return ver; } -static constexpr std::array, 17> sectionNames{{ +static constexpr std::array, 20> sectionNames{{ {"BEGIN FIXED SHUNT", SectionType::FIXED_SHUNT}, {"BEGIN SWITCHED SHUNT DATA", SectionType::SWITCHED_SHUNT}, {"BEGIN AREA INTERCHANGE DATA", SectionType::UNKNOWN}, - {"BEGIN TWO-TERMINAL DC LINE DATA", SectionType::UNKNOWN}, + {"BEGIN TWO-TERMINAL DC LINE DATA", SectionType::TWO_TERMINAL_DC}, + {"BEGIN TWO-TERMINAL DC DATA", SectionType::TWO_TERMINAL_DC}, + {"BEGIN VOLTAGE SOURCE CONVERTER DATA", SectionType::VSC_DC}, + {"BEGIN VSC DC LINE DATA", SectionType::VSC_DC}, {"BEGIN TRANSFORMER IMPEDANCE CORRECTION DATA", SectionType::UNKNOWN}, {"BEGIN IMPEDANCE CORRECTION DATA", SectionType::UNKNOWN}, {"BEGIN MULTI-TERMINAL DC LINE DATA", SectionType::UNKNOWN}, diff --git a/src/griddyn/CMakeLists.txt b/src/griddyn/CMakeLists.txt index cbcb6abea..db3592278 100644 --- a/src/griddyn/CMakeLists.txt +++ b/src/griddyn/CMakeLists.txt @@ -220,6 +220,7 @@ set(load_sources set(link_headers links/DcLink.h + links/RawDcLine.h links/AcDcConverter.h links/VSCShunt.h links/AdjustableTransformer.h @@ -237,6 +238,7 @@ set(link_sources links/Link.cpp links/AdjustableTransformer.cpp links/DcLink.cpp + links/RawDcLine.cpp links/AcDcConverter.cpp links/VSCShunt.cpp links/Subsystem.cpp diff --git a/src/griddyn/links/RawDcLine.cpp b/src/griddyn/links/RawDcLine.cpp new file mode 100644 index 000000000..ddba49499 --- /dev/null +++ b/src/griddyn/links/RawDcLine.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2014-2026, Lawrence Livermore National Security + * See the top-level NOTICE for additional details. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "RawDcLine.h" + +#include "../GridBus.h" +#include "utilities/MatrixDataCompact.hpp" + +#include + +namespace griddyn::links { +using units::convert; +using units::puMW; +using units::unit; + +RawDcLine::RawDcLine(const std::string& objName): Link(objName) {} + +index_t RawDcLine::fromReactiveOffset(const SolverMode& sMode) const +{ + return controlFromVoltage ? offsets.getAlgOffset(sMode) : kNullLocation; +} + +index_t RawDcLine::toReactiveOffset(const SolverMode& sMode) const +{ + const auto offset = offsets.getAlgOffset(sMode); + if (!controlToVoltage || (offset == kNullLocation)) { + return kNullLocation; + } + return offset + (controlFromVoltage ? 1 : 0); +} + +void RawDcLine::set(std::string_view param, double val, unit unitType) +{ + if (param == "from_vtarget") { + fromVoltageTarget = val; + } else if (param == "to_vtarget") { + toVoltageTarget = val; + } else if (param == "from_q") { + fromReactivePower = convert(val, unitType, puMW, systemBasePower); + } else if (param == "to_q") { + toReactivePower = convert(val, unitType, puMW, systemBasePower); + } else if (param == "from_voltage_control") { + controlFromVoltage = (val > 0.5); + } else if (param == "to_voltage_control") { + controlToVoltage = (val > 0.5); + } else { + Link::set(param, val, unitType); + } +} + +double RawDcLine::get(std::string_view param, unit unitType) const +{ + if (param == "from_vtarget") { + return fromVoltageTarget; + } + if (param == "to_vtarget") { + return toVoltageTarget; + } + if (param == "from_q") { + return convert(fromReactivePower, puMW, unitType, systemBasePower); + } + if (param == "to_q") { + return convert(toReactivePower, puMW, unitType, systemBasePower); + } + if (param == "from_voltage_control") { + return controlFromVoltage ? 1.0 : 0.0; + } + if (param == "to_voltage_control") { + return controlToVoltage ? 1.0 : 0.0; + } + return Link::get(param, unitType); +} + +void RawDcLine::pFlowObjectInitializeA(CoreTime time0, std::uint32_t flags) +{ + Link::pFlowObjectInitializeA(time0, flags); + offsets.local().local.algSize = localStateSizes(cPflowSolverMode).algSize; + offsets.local().local.jacSize = localJacobianCount(cPflowSolverMode); + updateLocalCache(); +} + +StateSizes RawDcLine::localStateSizes(const SolverMode& sMode) const +{ + StateSizes sizes; + if (hasAlgebraic(sMode) && isConnected()) { + sizes.algSize = static_cast(controlFromVoltage) + + static_cast(controlToVoltage); + } + return sizes; +} + +count_t RawDcLine::localJacobianCount(const SolverMode& sMode) const +{ + return localStateSizes(sMode).algSize; +} + +void RawDcLine::updateLocalCache() +{ + if (!isEnabled() || !isConnected()) { + linkFlows = {}; + return; + } + Link::updateLocalCache(); + linkFlows.P1 = Pset; + linkFlows.P2 = Pset - (std::abs(Pset) * lossFraction); + linkFlows.Q1 = fromReactivePower; + linkFlows.Q2 = toReactivePower; +} + +void RawDcLine::updateLocalCache(const IOdata& /*inputs*/, + const StateData& stateDataValue, + const SolverMode& sMode) +{ + if (!isEnabled() || !isConnected() || !stateDataValue.updateRequired(linkInfo.seqID)) { + return; + } + Link::updateLocalCache(noInputs, stateDataValue, sMode); + const auto fromOffset = fromReactiveOffset(sMode); + const auto toOffset = toReactiveOffset(sMode); + if (fromOffset != kNullLocation) { + fromReactivePower = stateDataValue.state[fromOffset]; + } + if (toOffset != kNullLocation) { + toReactivePower = stateDataValue.state[toOffset]; + } + linkFlows.P1 = Pset; + linkFlows.P2 = Pset - (std::abs(Pset) * lossFraction); + linkFlows.Q1 = fromReactivePower; + linkFlows.Q2 = toReactivePower; +} + +void RawDcLine::outputPartialDerivatives(id_type_t busId, + const StateData& stateDataValue, + MatrixData& matrixDataValue, + const SolverMode& sMode) +{ + if (!isEnabled() || !isConnected()) { + return; + } + updateLocalCache(noInputs, stateDataValue, sMode); + if ((busId == B1->getID()) && controlFromVoltage) { + matrixDataValue.assign(QOUT_LOCATION, fromReactiveOffset(sMode), 1.0); + } else if ((busId == B2->getID()) && controlToVoltage) { + matrixDataValue.assign(QOUT_LOCATION, toReactiveOffset(sMode), 1.0); + } +} + +count_t RawDcLine::outputDependencyCount(index_t num, const SolverMode& sMode) const +{ + if (!hasAlgebraic(sMode) || (num != QOUT_LOCATION) || !isConnected()) { + return 0; + } + return static_cast(controlFromVoltage) + static_cast(controlToVoltage); +} + +void RawDcLine::jacobianElements(const IOdata& /*inputs*/, + const StateData& /*stateDataValue*/, + MatrixData& matrixDataValue, + const IOlocs& /*inputLocs*/, + const SolverMode& sMode) +{ + if (!hasAlgebraic(sMode) || !isConnected()) { + return; + } + if (controlFromVoltage) { + matrixDataValue.assignCheckCol(fromReactiveOffset(sMode), + B1->getOutputLoc(sMode, VOLTAGE_IN_LOCATION), + -1.0); + } + if (controlToVoltage) { + matrixDataValue.assignCheckCol(toReactiveOffset(sMode), + B2->getOutputLoc(sMode, VOLTAGE_IN_LOCATION), + -1.0); + } +} + +void RawDcLine::residual(const IOdata& inputs, + const StateData& stateDataValue, + double resid[], + const SolverMode& sMode) +{ + updateLocalCache(inputs, stateDataValue, sMode); + if (!hasAlgebraic(sMode) || !isConnected()) { + return; + } + if (controlFromVoltage) { + resid[fromReactiveOffset(sMode)] = + fromVoltageTarget - B1->getVoltage(stateDataValue, sMode); + } + if (controlToVoltage) { + resid[toReactiveOffset(sMode)] = toVoltageTarget - B2->getVoltage(stateDataValue, sMode); + } +} + +void RawDcLine::setState(CoreTime time, + const double state[], + const double /*dstateDt*/[], + const SolverMode& sMode) +{ + const auto fromOffset = fromReactiveOffset(sMode); + const auto toOffset = toReactiveOffset(sMode); + if (fromOffset != kNullLocation) { + fromReactivePower = state[fromOffset]; + } + if (toOffset != kNullLocation) { + toReactivePower = state[toOffset]; + } + prevTime = time; + updateLocalCache(); +} + +void RawDcLine::guessState(CoreTime /*time*/, + double state[], + double /*dstateDt*/[], + const SolverMode& sMode) +{ + const auto fromOffset = fromReactiveOffset(sMode); + const auto toOffset = toReactiveOffset(sMode); + if (fromOffset != kNullLocation) { + state[fromOffset] = fromReactivePower; + } + if (toOffset != kNullLocation) { + state[toOffset] = toReactivePower; + } +} + +void RawDcLine::getStateName(stringVec& stNames, + const SolverMode& sMode, + const std::string& prefix) const +{ + const auto fromOffset = fromReactiveOffset(sMode); + const auto toOffset = toReactiveOffset(sMode); + const std::string statePrefix = prefix + getName() + ':'; + if (fromOffset != kNullLocation) { + stNames[fromOffset] = statePrefix + "q_from"; + } + if (toOffset != kNullLocation) { + stNames[toOffset] = statePrefix + "q_to"; + } +} + +} // namespace griddyn::links diff --git a/src/griddyn/links/RawDcLine.h b/src/griddyn/links/RawDcLine.h new file mode 100644 index 000000000..df7fef96c --- /dev/null +++ b/src/griddyn/links/RawDcLine.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014-2026, Lawrence Livermore National Security + * See the top-level NOTICE for additional details. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include "../Link.h" + +namespace griddyn::links { + +/** + * @brief PowerModels-compatible representation of a PSS/E RAW DC line. + * + * PSS/E RAW DC records are translated by PowerModels into the ``dcline`` + * abstraction. It is not a physical DC network: active terminal powers are + * scheduled, and a terminal on a PQ AC bus receives an independent reactive + * power variable together with a voltage-magnitude setpoint. This class + * supplies those AC-terminal equations without altering GridDyn's physical + * DcBus, DcLink, AcDcConverter, VSCShunt, or Hvdc models. + * + * A GridDyn PV/slack bus already eliminates its reactive balance equation, so + * the compatibility reactive variable is only required on a PQ terminal. A + * reader must select at most one voltage controller for any PQ bus; additional + * RAW DC terminals at that bus retain their specified initial reactive flow. + */ +class RawDcLine final: public Link { + private: + double fromVoltageTarget = 1.0; + double toVoltageTarget = 1.0; + double fromReactivePower = 0.0; + double toReactivePower = 0.0; + bool controlFromVoltage = false; + bool controlToVoltage = false; + + index_t fromReactiveOffset(const SolverMode& sMode) const; + index_t toReactiveOffset(const SolverMode& sMode) const; + + public: + explicit RawDcLine(const std::string& objName = "rawdcline_$"); + + void set(std::string_view param, + double val, + units::unit unitType = units::defunit) override; + double get(std::string_view param, + units::unit unitType = units::defunit) const override; + + void pFlowObjectInitializeA(CoreTime time0, std::uint32_t flags) override; + StateSizes localStateSizes(const SolverMode& sMode) const override; + count_t localJacobianCount(const SolverMode& sMode) const override; + + void updateLocalCache() override; + void updateLocalCache(const IOdata& inputs, + const StateData& stateDataValue, + const SolverMode& sMode) override; + using Link::outputPartialDerivatives; + void outputPartialDerivatives(id_type_t busId, + const StateData& stateDataValue, + MatrixData& matrixDataValue, + const SolverMode& sMode) override; + count_t outputDependencyCount(index_t num, const SolverMode& sMode) const override; + void jacobianElements(const IOdata& inputs, + const StateData& stateDataValue, + MatrixData& matrixDataValue, + const IOlocs& inputLocs, + const SolverMode& sMode) override; + void residual(const IOdata& inputs, + const StateData& stateDataValue, + double resid[], + const SolverMode& sMode) override; + void setState(CoreTime time, + const double state[], + const double dstateDt[], + const SolverMode& sMode) override; + void guessState(CoreTime time, + double state[], + double dstateDt[], + const SolverMode& sMode) override; + void getStateName(stringVec& stNames, + const SolverMode& sMode, + const std::string& prefix = "") const override; +}; + +} // namespace griddyn::links diff --git a/test/systemTests/testInputs.cpp b/test/systemTests/testInputs.cpp index 7e0cd0e57..5823f4582 100644 --- a/test/systemTests/testInputs.cpp +++ b/test/systemTests/testInputs.cpp @@ -9,6 +9,7 @@ #include "griddyn/GridBus.h" #include "griddyn/Link.h" #include "griddyn/links/AdjustableTransformer.h" +#include "griddyn/links/RawDcLine.h" #include #include #include @@ -207,6 +208,45 @@ TEST_F(InputTests, TestPowerFlowInputs) } } +TEST_F(InputTests, PssERawDcComponentsImportAsScheduledLinks) +{ + gds = std::make_unique(); + ASSERT_NO_THROW(loadFile(gds, std::string(INPUT_TEST_DIRECTORY) + "psse_dc_components.raw")); + + EXPECT_EQ(gds->getInt("totalbuscount"), 2); + // One AC branch plus the two PSS/E DC records. The imported records use + // the scheduled-link compatibility model and do not replace GridDyn's + // native DcLink, AcDcConverter, or Hvdc models. + ASSERT_EQ(gds->getInt("totallinkcount"), 3); + auto* twoTerminal = dynamic_cast(gds->getLink(1)); + auto* vsc = dynamic_cast(gds->getLink(2)); + ASSERT_NE(twoTerminal, nullptr); + ASSERT_NE(vsc, nullptr); + + // Match PowerModels' RAW conversion: a mode-1 two-terminal record imports + // SETVL as a lossless scheduled transport power, while a VSC record starts + // at zero active power and carries its affine loss slope. Bus 2 is PQ, so + // the first DC terminal adds PowerModels' q variable and voltage equality. + EXPECT_NEAR(twoTerminal->get("pset", units::MW), 5.0, 1e-9); + EXPECT_NEAR(twoTerminal->get("lossfraction"), 0.0, 1e-12); + EXPECT_NEAR(vsc->get("pset", units::MW), 0.0, 1e-12); + EXPECT_NEAR(vsc->get("lossfraction"), 0.003, 1e-12); + EXPECT_EQ(twoTerminal->get("to_voltage_control"), 1.0); + EXPECT_EQ(vsc->get("to_voltage_control"), 0.0); + + gds->powerflow(); + requireState(GridDynSimulation::GridState::POWERFLOW_COMPLETE); + // Reference: PowerModels.solve_ac_pf(..., Ipopt.Optimizer) on this RAW + // input gives vm=1.0 and va=-0.025284658 rad at bus 2. Both RAW DC + // records touch that bus, so their individual q variables are degenerate + // in PowerModels; compare their terminal total instead. + EXPECT_NEAR(gds->getBus(1)->getVoltage(), 1.0, 1e-8); + EXPECT_NEAR(gds->getBus(1)->getAngle(), -0.025284658, 1e-8); + EXPECT_NEAR(twoTerminal->getReactivePower(2) + vsc->getReactivePower(2), + -0.0781963933423, + 1e-7); +} + struct CompareCase { std::array fileNames; size_t fileCount; diff --git a/test/test_files/input_tests/psse_dc_components.raw b/test/test_files/input_tests/psse_dc_components.raw new file mode 100644 index 000000000..9f343d44d --- /dev/null +++ b/test/test_files/input_tests/psse_dc_components.raw @@ -0,0 +1,33 @@ +0, 100.00, 33, 0, 1, 60.00 +GridDyn PSS/E RAW DC component import test +Two-bus system with PowerModels-compatible two-terminal and VSC DC records +1,'SLACK',230.0000,3,1,1,1,1.000000,0.000000,1.10000,0.90000,1.10000,0.90000 +2,'LOAD', 230.0000,1,1,1,1,1.000000,0.000000,1.10000,0.90000,1.10000,0.90000 +0 / END OF BUS DATA, BEGIN LOAD DATA +2,'1 ',1,1,1,20.000,5.000,0.000,0.000,0.000,0.000,1,1 +0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA +0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA +1,'1 ',30.000,0.000,100.000,-100.000,1.00000,0,100.000,0.00000,0.10000,0.00000,0.00000,1.00000,1,100.0,100.000,0.000,1,1.0000 +0 / END OF GENERATOR DATA, BEGIN BRANCH DATA +1,2,'1 ',0.01000,0.10000,0.00000,100.00,100.00,100.00,0.00000,0.00000,0.00000,0.00000,1,1,1.0,1,1.0000 +0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA +0 / END OF TRANSFORMER DATA, BEGIN AREA DATA +0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA +'TTDC Ln 1',1,0.1000,5.000,500.000,0.000,0.0000,0.00000,I,0.00,20,1.00000 +1,2,90.00,18.10,0.0140,0.5780,230.0,0.09772,1.00000,1.00000,1.00000,0.00624,0,0,0,1,0.00000 +2,2,90.00,17.40,0.0140,0.5670,230.0,0.07134,1.00000,1.00000,1.00000,0.00625,0,0,0,1,0.00000 +0 / END OF TWO-TERMINAL DC DATA, BEGIN VOLTAGE SOURCE CONVERTER DATA +'VSCDC Ln 1',1,0.0000,1,1.0,0,1.0,0,1.0,0,1.0 +1,1,1,150.000,1.00000,1000.0000,1.5000,0.00,250.00,1000.00,0.5000,100.00,-100.00,1,100.00 +2,2,1,-20.000,1.00000,1000.0000,1.5000,0.00,250.00,1000.00,0.5000,100.00,-100.00,2,100.00 +0 / END OF VOLTAGE SOURCE CONVERTER DATA, BEGIN IMPEDANCE CORRECTION DATA +0 / END OF IMPEDANCE CORRECTION DATA, BEGIN MULTI-TERMINAL DC DATA +0 / END OF MULTI-TERMINAL DC DATA, BEGIN MULTI-SECTION LINE DATA +0 / END OF MULTI-SECTION LINE DATA, BEGIN ZONE DATA +0 / END OF ZONE DATA, BEGIN INTER-AREA TRANSFER DATA +0 / END OF INTER-AREA TRANSFER DATA, BEGIN OWNER DATA +0 / END OF OWNER DATA, BEGIN FACTS CONTROL DEVICE DATA +0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA +0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DEVICE DATA +0 / END OF GNE DEVICE DATA +Q From 68454b6071f014b89e272b62a451d91f9fc4e26a Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sun, 30 Aug 2026 15:42:50 -0700 Subject: [PATCH 2/4] update guides and fix some warnings for visual studio 2026 --- config/cmake/compiler_flags.cmake | 9 +++- .../activsg-dyr-compatibility-plan.md | 32 +++++++++----- .../developer-guide/openipsl-compatibility.md | 42 ++++++++++++++++--- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/config/cmake/compiler_flags.cmake b/config/cmake/compiler_flags.cmake index 822eb6431..e142991ed 100644 --- a/config/cmake/compiler_flags.cmake +++ b/config/cmake/compiler_flags.cmake @@ -129,7 +129,14 @@ if(MSVC) add_compile_options(/EHsc /MP /utf-8) target_compile_options(build_flags_target INTERFACE /EHsc /utf-8) - target_link_options(compile_flags_target INTERFACE /debug:fastlink /incremental) + # VS 2026 (MSVC 19.50+) removed /DEBUG:FASTLINK. Newer linkers select + # full debug information when /DEBUG is enabled, while /INCREMENTAL + # remains supported. + if(MSVC_VERSION LESS 1950) + target_link_options(compile_flags_target INTERFACE /debug:fastlink /incremental) + else() + target_link_options(compile_flags_target INTERFACE /incremental) + endif() if(${PROJECT_NAME}_ENABLE_EXTRA_COMPILER_WARNINGS) target_compile_options(compile_flags_target INTERFACE /W4 /sdl /wd4244) endif(${PROJECT_NAME}_ENABLE_EXTRA_COMPILER_WARNINGS) diff --git a/docs/developer-guide/activsg-dyr-compatibility-plan.md b/docs/developer-guide/activsg-dyr-compatibility-plan.md index d80edafad..5d1a970f1 100644 --- a/docs/developer-guide/activsg-dyr-compatibility-plan.md +++ b/docs/developer-guide/activsg-dyr-compatibility-plan.md @@ -108,20 +108,32 @@ is required before accepting the power flow or initializing the DYR. ## Implementation order +The cross-case source availability and P1/P2 ranking is maintained in the +[OpenIPSL dynamic-model assessment](openipsl-compatibility.md#synthetic-case-demand-overlay). +That overlay distinguishes models that can be ported from an inspected +OpenIPSL/ANDES/PowerDynamics implementation from source gaps that require +equations before implementation. + 1. **Power-flow prerequisites:** resolve the remaining ACTIVSg2000 control-device parity difference, add grouped PSS/E remote-voltage regulation, and investigate the ACTIVSg25k RAW-versus-MATPOWER source-state mismatch. ACTIVSg10k RAW/EPC three-winding topology is validated. -2. **Machine and governor coverage:** implement `GENSAL`, `GGOV1`, `HYGOV`, - and `GAST`. These cover 15,151 unsupported records and unblock many of - their attached excitation/control models. -3. **Largest synchronous-excitation gap:** implement `ESST4B`, `IEEET1`, - `SCRX`, and `EXPIC1` (11,578 records). -4. **Renewable generation:** implement and validate `REGCA1` plus `REECA1`, - then the coupled `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` Type-3 system (5,150 - records). -5. **Remaining excitation families:** `ESDC2A`, `ESAC6A`, and `ESAC1A` - (1,919 records). +2. **P1 conventional machine/governor foundation:** implement `GENSAL`, + `HYGOV`, and `GGOV1`. These high-demand models have external equation + sources; `GGOV1` is the largest missing family and its PowerDynamics port + is experimental, so use OpenIPSL as the equation authority. +3. **P1 synchronous excitation:** implement `ESST4B`, `IEEET1`, and `SCRX` + from the available OpenIPSL/PowerDynamics references. Defer `EXPIC1` to P2 + because no exact source was found; ANDES's `SEXS` conversion is only an + approximation. +4. **P1 renewable generation:** implement and validate `REGCA1` plus + `REECA1` (and `REPCA1` for Texas7k), then the coupled + `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` Type-3 system. The latter two models have + no exact external source and must be derived or obtained as part of the + complete system, not silently omitted. +5. **P2 remaining excitation/source gaps:** `ESDC2A` and `ESAC1A` have + OpenIPSL references; `ESAC6A` requires an exact equation source before a + port. Add `EXPIC1` only after its exact behavior is obtained. 6. **Reader and validation hardening:** table-driven DYR dispatch, strict unknown-model diagnostics, exact bus-plus-machine-ID resolution, minimized fixtures, and whole-case trajectory regressions. diff --git a/docs/developer-guide/openipsl-compatibility.md b/docs/developer-guide/openipsl-compatibility.md index 1a0a3c81e..f30a4d1c8 100644 --- a/docs/developer-guide/openipsl-compatibility.md +++ b/docs/developer-guide/openipsl-compatibility.md @@ -35,11 +35,40 @@ No row is currently *validated against OpenIPSL*. Existing ANDES/PSSE DYR refere | Order | Deliverable | Why it comes first | | --- | --- | --- | | 1 | P0 synchronous-machine and basic-control reference suite | Establishes conventions for bases, dq signs, initialization, limits, and event comparison. | -| 2 | P1 PSS/e machine, governor, exciter, and induction-motor gaps | Extends conventional-transmission dynamic studies without changing GridDyn's network abstraction. | -| 3 | P1 WECC renewable foundation: `REGCA1` + `REECA1/REECB1` + `REPCA1` | A composable inverter/current-limit/plant-control interface enables the highest-value modern generation models. | -| 4 | P2 specialized controls, FACTS, wind/PV detail, and dynamic load behavior | Builds on the validated controller and converter interfaces. | +| 2 | P1 synthetic-case conventional gaps: `GGOV1`, `ESST4B`, `GENSAL`, `HYGOV`, `IEEET1`, and `SCRX` | These source-backed models account for 26,519 missing records across the supplied ACTIVSg and Texas7k cases. | +| 3 | P1 renewable foundation and Type-3 wind bundle | `REGCA1` + `REECA1/REECB1` + `REPCA1` is needed by the synthetic cases; `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` must be treated as one coupled system. | +| 4 | P2 source gaps and specialized controls | Implement exact `EXPIC1` and `ESAC6A` only after the P1 cases; no exact implementation was found in the assessed OpenIPSL, ANDES, or PowerDynamics sources. | | 5 | P3 three-phase, mono/tri, and VSD models | Requires an unbalanced multi-conductor network and is not a direct extension of GridDyn's present positive-sequence dynamic path. | +## Synthetic-case demand overlay + +The P1/P2 ordering above is adjusted using the dynamic-model inventories in +[the ACTIVSg plan](activsg-dyr-compatibility-plan.md) and the Texas7k demand +section of [the ANDES roadmap](andes-compatibility.md). Counts below are the +sum of the five ACTIVSg cases and Texas7k, where a Texas7k record exists. An +external source means an equation-level model is available for audit or port; +it does **not** mean that GridDyn support is complete or that similarly named +models are interchangeable. + +| Demand group | Synthetic-case records | Exact external source(s) | Revised priority / action | +| --- | ---: | --- | --- | +| `GGOV1` | 6,977 | OpenIPSL; PowerDynamics (experimental); ANDES inventory | **P1.** Port from the OpenIPSL equations; use PowerDynamics only as a design aid because its `GGOV1` reference documents known issues. | +| `ESST4B` | 5,949 | OpenIPSL; PowerDynamics | **P1.** Port and validate with the existing PowerDynamics/OpenIPSL reference path. | +| `GENSAL` + `HYGOV` | 4,312 + 4,351 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Implement the salient-pole machine before attaching the hydro governor. | +| `IEEET1` + `SCRX` | 3,105 + 1,825 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Audit the existing GridDyn Type-1 candidate, then add `SCRX` as a distinct exciter. | +| `REGCA1` + `REECA1` + `REPCA1` | 1,374 + 1,367 + 174 | OpenIPSL; ANDES inventory | **P1.** Deliver the converter, electrical control, and plant-control chain together. | +| Type-3 wind: `WT3G1` + `WT3E1` + `WT3P1` + `WT3T1` | 695 each | `WT3G1`/`WT3E1`: OpenIPSL; `WT3P1`/`WT3T1`: no exact source found | **P1.** Do not ship a partial Type-3 mapping; port or derive the missing pitch/turbine equations as part of the same system. | +| Texas WECC wind: `WTARA1`, `WTTQA1`, `WTPTA1` | 121 + 121 + 105 | ANDES inventory only | **P2.** Use the ANDES equations as the starting reference after the P1 renewable interface is in place. | +| `EXPIC1` + `ESAC6A` | 1,156 + 813 | No exact source found | **P2 source gap.** ANDES's `SEXS` conversions are documented approximations; obtain or derive exact equations before implementing. | +| `ESAC1A` + `ESDC2A` + `GAST` | 718 + 397 + 30 | OpenIPSL (`ESAC1A`/`ESDC2A`/`GAST`); ANDES inventory for `ESDC2A`/`GAST` | **P2.** Port only after the shared exciter/governor interfaces are proven by P1 work. | +| `USRBUS`/`USRMDL` (`PLNTBU1`, `REAX3BU1`, `REAX4BU1`) | 21 Texas7k records | None; vendor/user-written models | **Blocked external dependency.** DYR parameters are insufficient; obtain the compiled-model equations or an approved equivalent. | + +The models with no exact source across the assessed libraries are therefore +`EXPIC1`, `ESAC6A`, `WT3P1`, and `WT3T1`, plus the Texas7k user-written model +records. `WT3P1` and `WT3T1` are P1 despite that gap because the synthetic +cases require the complete Type-3 wind system; `EXPIC1` and `ESAC6A` remain P2 +because their demand is lower and no exact equation source is presently known. + ## 1. Synchronous machines and induction motors | OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | @@ -64,7 +93,8 @@ No row is currently *validated against OpenIPSL*. Existing ANDES/PSSE DYR refere | `Controls.PSSE.ES.IEEET1`, `IEEET2` | `ExciterIEEEtype1`, `ExciterIEEEtype2` | **P1 — Existing candidates** | GridDyn's generic class names need model-specific equation and limiter audits. | | `Controls.CGMES.ES.ExcSEXS` | `ExciterSEXS` | **P1 — Existing candidate** | Validate the CGMES signal and parameter conventions separately from the PSS/e wrapper. | | `Controls.PSSE.ES.AC7B`, `AC8B`, `DC4B`, `ESST1A`, `ESST2A`, `ESST4B`, `ESAC1A`, `ESAC2A` | No exact models | **P1 — New models** | Prioritize the models required by target PSS/e cases. They require explicit limiter/root treatment and a reusable exciter block library. | -| `Controls.PSSE.ES.IEEEX1`, `SCRX`, `ST5B`, `URST5T`, `EXNI`, `EXBAS`, `ESURRY` | No exact models | **P2 — New models** | Add after the common AC/DC exciter blocks are established. | +| `Controls.PSSE.ES.IEEEX1`, `SCRX`, `ST5B`, `URST5T`, `EXNI`, `EXBAS`, `ESURRY` | No exact models | **P2, except `SCRX` P1 — New models** | `SCRX` is a P1 synthetic-case gap with an OpenIPSL/PowerDynamics reference; add the other models after the common AC/DC exciter blocks are established. | +| PSS/e `EXPIC1`, `ESAC6A` | No exact model in OpenIPSL, ANDES, or PowerDynamics | **P2 — Source gaps** | Do not use ANDES's documented approximate `SEXS` conversion as exact support. Obtain or derive the model equations before porting. | | `Controls.PSSE.OEL.OEL`, `Controls.PSSE.UEL.MNLEX2`, `Controls.PSSE.COMP.IEEEVC` | No exact analogue | **P1 — Structural extension** | Add field-current/under-excitation and terminal-current-compensation input paths to the exciter interface; then port the model-specific limits. | | PSAT `AVRTypeI/II/III`, `OEL`, `FieldCurrent`; Simulink excitation/OEL blocks | Existing exciters are only candidates | **P2 — Existing candidates / new blocks** | Treat the PSAT and Simulink families as separate equations, not aliases of PSS/e names. | @@ -76,7 +106,7 @@ No row is currently *validated against OpenIPSL*. Existing ANDES/PSSE DYR refere | `Controls.PSSE.TG.IEEEG1` | `GovernorIeeeG1` | **P0 — Existing candidate** | Compare lead/lag ordering, turbine fractions, rate/position limits, and multi-machine attachment. | | `Controls.PSSE.PSS.IEEEST` | `StabilizerIEEEST` | **P0 — Existing candidate** | Test zero-time-constant bypasses, filters, lead-lag states, and output limits. | | `Controls.PSSE.TG.HYGOV`, `IEESGO` | `GovernorHydro`, `GovernorReheat` are candidates | **P1 — Existing candidates** | Perform an equation, water-column/reheat, limit, and parameter-unit audit before reuse. | -| `Controls.PSSE.TG.GAST`, `GGOV1`, `GGOV1DU`, `DEGOV`, `IEEEG2`, `WEHGOV`, `WPIDHY`, `WSIEG1` | No exact models | **P1/P2 — New models** | Start with `GAST` and `GGOV1` (**P1**) if common PSS/e cases require them. The remaining engine/wind/hydro variants are **P2** after reusable governor limit and mode-selector blocks exist. | +| `Controls.PSSE.TG.GAST`, `GGOV1`, `GGOV1DU`, `DEGOV`, `IEEEG2`, `WEHGOV`, `WPIDHY`, `WSIEG1` | No exact models | **`GGOV1` P1; remainder P2 — New models** | `GGOV1` is the largest synthetic-case gap (6,977 records) and has OpenIPSL/experimental-PowerDynamics references. `GAST` and the remaining engine/wind/hydro variants are P2 after reusable governor limit and mode-selector blocks exist. | | `Controls.PSSE.TG.ConstantPower`; PSAT `TGTypeI–VI`; CGMES `GovHydroIEEE0`; Simulink TG blocks | Basic/reheat/hydro classes are candidates | **P2 — Existing candidates / new models** | Establish whether a GridDyn governor can expose the reference and mechanical-power ports required by each family. | | `Controls.PSSE.PSS.PSS2A`, `PSS2B`, `IEE2ST`, `STAB2A`, `STAB3`, `STABNI`, `STBSVC` | No exact model | **P2 — New models** | Add dual/remote measurement inputs, filters, mode selection, and signal routing. | | PSAT `PSSTypeI/II/III`; Simulink PSS; `DisabledPSS` | `StabilizerIEEEST` / `StabilizerST2CUT` are only candidates | **P2 — Existing candidates / new blocks** | Keep the source-family semantics explicit. `DisabledPSS` is mainly a plant-assembly behavior. | @@ -93,7 +123,7 @@ This is the principal new high-value OpenIPSL family. GridDyn's `GenModelInverte | `Renewables.PSSE.Wind`, `PV`, `BESS` | No exact model | **P1 — Structural extension** | Compose `REGCA1` + `REEC*` + `REPCA1` into attachable plant templates. BESS additionally needs energy/state-of-charge semantics before it is a complete storage model. | | `Renewables.PSSE.WindDriveTrain.WTDTA1` | No exact model | **P2 — New model** | Add after the renewable plant electrical interface; model shaft/torsional states and mechanical-power connection. | | `Renewables.PSSE.AddOnBlocks.IrradianceToPower` | `FileLoad`-like signal sources only | **P2 — Structural extension** | Define time-series/irradiance input policy and clipping before adding PV availability behavior. | -| `Wind.PSSE.WT3G*`, `WT4G*`; PSAT type-3; GE type-3 | No exact model | **P2 — New model families** | Port only after the generic renewable controller foundation. Prefer mapping each vendor/standard block to the reusable components instead of a monolithic generator class. | +| `Wind.PSSE.WT3G*`, `WT4G*`; PSAT type-3; GE type-3 | No exact model | **Type-3 PSS/e system P1; remainder P2 — New model families** | `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` are required by ACTIVSg and must be implemented as one coupled P1 system after the renewable controller foundation. Keep other vendor/standard families P2 and map them to reusable components rather than a monolithic generator class. | | PSAT constant-PQ PV; PowerFactory WECC `PVD1`; PowerFactory DIgSILENT PV plant/array/control models | No exact model | **P2 — New model families** | Use as validation/extension targets after the `REGC/REEC/REPCA` path. Detailed PV-array/DC-link models may be outside phasor-time-domain scope. | | `VSD.Generic.AC2DCandDC2AC`, `VoltsHertzController` | No exact model | **P3 — Structural extension** | Cross-domain power-electronics models require a justified phasor/DC coupling contract and may be better isolated through FMI first. | From a904bdd4411d35ca81029e589673b6286d199136 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:45:55 +0000 Subject: [PATCH 3/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- config/cmake/addSundials.cmake | 28 +-- config/cmake/compiler_flags.cmake | 5 +- .../developer-guide/openipsl-compatibility.md | 196 +++++++++--------- .../powerdynamics-compatibility.md | 87 ++++---- .../psse-raw-dc-compatibility.md | 48 ++--- src/fileInput/gridDynReadRAW.cpp | 65 +++--- src/griddyn/links/RawDcLine.cpp | 5 +- src/griddyn/links/RawDcLine.h | 7 +- 8 files changed, 211 insertions(+), 230 deletions(-) diff --git a/config/cmake/addSundials.cmake b/config/cmake/addSundials.cmake index 749d8ae62..666d5f5c2 100644 --- a/config/cmake/addSundials.cmake +++ b/config/cmake/addSundials.cmake @@ -146,12 +146,10 @@ set(SUNDIALS_TEST_ENABLE_UNIT_TESTS OFF CACHE INTERNAL "") set(SUNDIALS_TEST_ENABLE_DIFF_OUTPUT OFF CACHE INTERNAL "") set(SUNDIALS_TEST_ANSWER_DIR "" CACHE INTERNAL "") -# SUNDIALS defaults this Podman-specific argument even when it discovers and -# uses Docker. Docker rejects --tls-verify, so clear the optional container -# arguments before SUNDIALS creates its local-CI helper targets. GridDyn does -# not use those targets for its normal build or test flow. -set(SUNDIALS_TEST_CONTAINER_RUN_EXTRA_ARGS - "" +# SUNDIALS defaults this Podman-specific argument even when it discovers and uses Docker. Docker +# rejects --tls-verify, so clear the optional container arguments before SUNDIALS creates its +# local-CI helper targets. GridDyn does not use those targets for its normal build or test flow. +set(SUNDIALS_TEST_CONTAINER_RUN_EXTRA_ARGS "" CACHE STRING "Extra arguments to pass to Docker/Podman for SUNDIALS local CI targets" FORCE ) @@ -180,26 +178,22 @@ endif() add_subdirectory("${sundials_SOURCE_DIR}" "${sundials_BINARY_DIR}") -# SUNDIALS creates these developer-only targets whenever Docker or Podman is -# discovered. They are not part of GridDyn's test suite and must not start a -# container as a side effect of Visual Studio's Build Solution command. +# SUNDIALS creates these developer-only targets whenever Docker or Podman is discovered. They are +# not part of GridDyn's test suite and must not start a container as a side effect of Visual +# Studio's Build Solution command. if(NOT GRIDDYN_ENABLE_SUNDIALS_LOCAL_CI) string(TOLOWER "${SUNDIALS_PRECISION}" _griddyn_sundials_precision) foreach(_griddyn_sundials_local_ci_target - setup_local_ci - test_local_ci + setup_local_ci test_local_ci setup_local_ci_${SUNDIALS_INDEX_SIZE}_${_griddyn_sundials_precision} test_local_ci_${SUNDIALS_INDEX_SIZE}_${_griddyn_sundials_precision} ) if(TARGET ${_griddyn_sundials_local_ci_target}) set_property( - TARGET ${_griddyn_sundials_local_ci_target} - PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE - ) - set_property( - TARGET ${_griddyn_sundials_local_ci_target} - PROPERTY EXCLUDE_FROM_ALL TRUE + TARGET ${_griddyn_sundials_local_ci_target} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD + TRUE ) + set_property(TARGET ${_griddyn_sundials_local_ci_target} PROPERTY EXCLUDE_FROM_ALL TRUE) endif() endforeach() unset(_griddyn_sundials_precision) diff --git a/config/cmake/compiler_flags.cmake b/config/cmake/compiler_flags.cmake index e142991ed..00d10b0d5 100644 --- a/config/cmake/compiler_flags.cmake +++ b/config/cmake/compiler_flags.cmake @@ -129,9 +129,8 @@ if(MSVC) add_compile_options(/EHsc /MP /utf-8) target_compile_options(build_flags_target INTERFACE /EHsc /utf-8) - # VS 2026 (MSVC 19.50+) removed /DEBUG:FASTLINK. Newer linkers select - # full debug information when /DEBUG is enabled, while /INCREMENTAL - # remains supported. + # VS 2026 (MSVC 19.50+) removed /DEBUG:FASTLINK. Newer linkers select full debug information + # when /DEBUG is enabled, while /INCREMENTAL remains supported. if(MSVC_VERSION LESS 1950) target_link_options(compile_flags_target INTERFACE /debug:fastlink /incremental) else() diff --git a/docs/developer-guide/openipsl-compatibility.md b/docs/developer-guide/openipsl-compatibility.md index f30a4d1c8..6ec0f9a8e 100644 --- a/docs/developer-guide/openipsl-compatibility.md +++ b/docs/developer-guide/openipsl-compatibility.md @@ -15,30 +15,30 @@ OpenIPSL is Modelica source, not an input format that GridDyn can read. GridDyn Priority reflects expected usefulness, reuse of existing GridDyn code, and prerequisite cost. It is intentionally rough, not a commitment. -| Priority | Meaning | -| --- | --- | -| **P0 — validate now** | A close GridDyn implementation already exists; create OpenIPSL references and establish equation/trajectory parity before adding more models. | -| **P1 — high-value native work** | Common dynamic model or enabling interface; begin after, or alongside, the relevant P0 baseline. | -| **P2 — targeted extension** | Valuable for particular studies, but specialized or dependent on P1 infrastructure. | -| **P3 — architectural/research** | Requires unbalanced, electromagnetic, stochastic, or other GridDyn capabilities beyond a self-contained positive-sequence model. | - -| Status | Meaning | -| --- | --- | -| Existing candidate | GridDyn has a plausible model, but no OpenIPSL parity result. | -| New model | GridDyn lacks a model-specific analogue. | +| Priority | Meaning | +| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| **P0 — validate now** | A close GridDyn implementation already exists; create OpenIPSL references and establish equation/trajectory parity before adding more models. | +| **P1 — high-value native work** | Common dynamic model or enabling interface; begin after, or alongside, the relevant P0 baseline. | +| **P2 — targeted extension** | Valuable for particular studies, but specialized or dependent on P1 infrastructure. | +| **P3 — architectural/research** | Requires unbalanced, electromagnetic, stochastic, or other GridDyn capabilities beyond a self-contained positive-sequence model. | + +| Status | Meaning | +| -------------------- | --------------------------------------------------------------------------------------- | +| Existing candidate | GridDyn has a plausible model, but no OpenIPSL parity result. | +| New model | GridDyn lacks a model-specific analogue. | | Structural extension | The primary missing piece is a connection, controller, measurement, or event interface. | -No row is currently *validated against OpenIPSL*. Existing ANDES/PSSE DYR references and GridDyn component tests are useful evidence for P0 selection, not Modelica parity. +No row is currently _validated against OpenIPSL_. Existing ANDES/PSSE DYR references and GridDyn component tests are useful evidence for P0 selection, not Modelica parity. ## Priority overview -| Order | Deliverable | Why it comes first | -| --- | --- | --- | -| 1 | P0 synchronous-machine and basic-control reference suite | Establishes conventions for bases, dq signs, initialization, limits, and event comparison. | -| 2 | P1 synthetic-case conventional gaps: `GGOV1`, `ESST4B`, `GENSAL`, `HYGOV`, `IEEET1`, and `SCRX` | These source-backed models account for 26,519 missing records across the supplied ACTIVSg and Texas7k cases. | -| 3 | P1 renewable foundation and Type-3 wind bundle | `REGCA1` + `REECA1/REECB1` + `REPCA1` is needed by the synthetic cases; `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` must be treated as one coupled system. | -| 4 | P2 source gaps and specialized controls | Implement exact `EXPIC1` and `ESAC6A` only after the P1 cases; no exact implementation was found in the assessed OpenIPSL, ANDES, or PowerDynamics sources. | -| 5 | P3 three-phase, mono/tri, and VSD models | Requires an unbalanced multi-conductor network and is not a direct extension of GridDyn's present positive-sequence dynamic path. | +| Order | Deliverable | Why it comes first | +| ----- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | P0 synchronous-machine and basic-control reference suite | Establishes conventions for bases, dq signs, initialization, limits, and event comparison. | +| 2 | P1 synthetic-case conventional gaps: `GGOV1`, `ESST4B`, `GENSAL`, `HYGOV`, `IEEET1`, and `SCRX` | These source-backed models account for 26,519 missing records across the supplied ACTIVSg and Texas7k cases. | +| 3 | P1 renewable foundation and Type-3 wind bundle | `REGCA1` + `REECA1/REECB1` + `REPCA1` is needed by the synthetic cases; `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` must be treated as one coupled system. | +| 4 | P2 source gaps and specialized controls | Implement exact `EXPIC1` and `ESAC6A` only after the P1 cases; no exact implementation was found in the assessed OpenIPSL, ANDES, or PowerDynamics sources. | +| 5 | P3 three-phase, mono/tri, and VSD models | Requires an unbalanced multi-conductor network and is not a direct extension of GridDyn's present positive-sequence dynamic path. | ## Synthetic-case demand overlay @@ -50,18 +50,18 @@ external source means an equation-level model is available for audit or port; it does **not** mean that GridDyn support is complete or that similarly named models are interchangeable. -| Demand group | Synthetic-case records | Exact external source(s) | Revised priority / action | -| --- | ---: | --- | --- | -| `GGOV1` | 6,977 | OpenIPSL; PowerDynamics (experimental); ANDES inventory | **P1.** Port from the OpenIPSL equations; use PowerDynamics only as a design aid because its `GGOV1` reference documents known issues. | -| `ESST4B` | 5,949 | OpenIPSL; PowerDynamics | **P1.** Port and validate with the existing PowerDynamics/OpenIPSL reference path. | -| `GENSAL` + `HYGOV` | 4,312 + 4,351 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Implement the salient-pole machine before attaching the hydro governor. | -| `IEEET1` + `SCRX` | 3,105 + 1,825 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Audit the existing GridDyn Type-1 candidate, then add `SCRX` as a distinct exciter. | -| `REGCA1` + `REECA1` + `REPCA1` | 1,374 + 1,367 + 174 | OpenIPSL; ANDES inventory | **P1.** Deliver the converter, electrical control, and plant-control chain together. | -| Type-3 wind: `WT3G1` + `WT3E1` + `WT3P1` + `WT3T1` | 695 each | `WT3G1`/`WT3E1`: OpenIPSL; `WT3P1`/`WT3T1`: no exact source found | **P1.** Do not ship a partial Type-3 mapping; port or derive the missing pitch/turbine equations as part of the same system. | -| Texas WECC wind: `WTARA1`, `WTTQA1`, `WTPTA1` | 121 + 121 + 105 | ANDES inventory only | **P2.** Use the ANDES equations as the starting reference after the P1 renewable interface is in place. | -| `EXPIC1` + `ESAC6A` | 1,156 + 813 | No exact source found | **P2 source gap.** ANDES's `SEXS` conversions are documented approximations; obtain or derive exact equations before implementing. | -| `ESAC1A` + `ESDC2A` + `GAST` | 718 + 397 + 30 | OpenIPSL (`ESAC1A`/`ESDC2A`/`GAST`); ANDES inventory for `ESDC2A`/`GAST` | **P2.** Port only after the shared exciter/governor interfaces are proven by P1 work. | -| `USRBUS`/`USRMDL` (`PLNTBU1`, `REAX3BU1`, `REAX4BU1`) | 21 Texas7k records | None; vendor/user-written models | **Blocked external dependency.** DYR parameters are insufficient; obtain the compiled-model equations or an approved equivalent. | +| Demand group | Synthetic-case records | Exact external source(s) | Revised priority / action | +| ----------------------------------------------------- | ---------------------: | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `GGOV1` | 6,977 | OpenIPSL; PowerDynamics (experimental); ANDES inventory | **P1.** Port from the OpenIPSL equations; use PowerDynamics only as a design aid because its `GGOV1` reference documents known issues. | +| `ESST4B` | 5,949 | OpenIPSL; PowerDynamics | **P1.** Port and validate with the existing PowerDynamics/OpenIPSL reference path. | +| `GENSAL` + `HYGOV` | 4,312 + 4,351 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Implement the salient-pole machine before attaching the hydro governor. | +| `IEEET1` + `SCRX` | 3,105 + 1,825 | OpenIPSL; PowerDynamics; ANDES inventory | **P1.** Audit the existing GridDyn Type-1 candidate, then add `SCRX` as a distinct exciter. | +| `REGCA1` + `REECA1` + `REPCA1` | 1,374 + 1,367 + 174 | OpenIPSL; ANDES inventory | **P1.** Deliver the converter, electrical control, and plant-control chain together. | +| Type-3 wind: `WT3G1` + `WT3E1` + `WT3P1` + `WT3T1` | 695 each | `WT3G1`/`WT3E1`: OpenIPSL; `WT3P1`/`WT3T1`: no exact source found | **P1.** Do not ship a partial Type-3 mapping; port or derive the missing pitch/turbine equations as part of the same system. | +| Texas WECC wind: `WTARA1`, `WTTQA1`, `WTPTA1` | 121 + 121 + 105 | ANDES inventory only | **P2.** Use the ANDES equations as the starting reference after the P1 renewable interface is in place. | +| `EXPIC1` + `ESAC6A` | 1,156 + 813 | No exact source found | **P2 source gap.** ANDES's `SEXS` conversions are documented approximations; obtain or derive exact equations before implementing. | +| `ESAC1A` + `ESDC2A` + `GAST` | 718 + 397 + 30 | OpenIPSL (`ESAC1A`/`ESDC2A`/`GAST`); ANDES inventory for `ESDC2A`/`GAST` | **P2.** Port only after the shared exciter/governor interfaces are proven by P1 work. | +| `USRBUS`/`USRMDL` (`PLNTBU1`, `REAX3BU1`, `REAX4BU1`) | 21 Texas7k records | None; vendor/user-written models | **Blocked external dependency.** DYR parameters are insufficient; obtain the compiled-model equations or an approved equivalent. | The models with no exact source across the assessed libraries are therefore `EXPIC1`, `ESAC6A`, `WT3P1`, and `WT3T1`, plus the Texas7k user-written model @@ -71,96 +71,96 @@ because their demand is lower and no exact equation source is presently known. ## 1. Synchronous machines and induction motors -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| `Machines.PSSE.GENROU` | `GenModelGENROU` | **P0 — Existing candidate** | Best first machine reference. Compare flux/transient states, saturation, dq current, rotor angle/speed, electrical power, and machine/system-base conversion. | -| `Machines.PSSE.GENCLS` | `GenModelClassical` | **P0 — Existing candidate** | Validate swing-equation sign, damping, initial angle, and power/base conventions. | -| `Machines.PSAT.Order2`, `Order3`, `Order4`, `Order5_Type1`, `Order5_Type2`, `Order6` | `GenModelClassical`, `GenModel3`, `GenModel4`, `GenModel5`, `GenModel5type2`, `GenModel6/6type2` | **P0 — Existing candidates** | Match each order individually. Equal order does not establish matching stator algebraics, saturation, or initialization. | -| `Machines.PSAT.MotorTypeI`, `MotorTypeIII`, `MotorTypeV` | `MotorLoad3`, `MotorLoad5` | **P1 — Existing candidates** | Audit slip, mechanical torque, stall/protection, and initialization before choosing a mapping. | -| `Machines.PSSE.CIM5`, `CIM6` | No verified exact analogue | **P1 — New models** | PSS/e induction-motor models are a high-value extension beyond the current motor candidates; model single-/double-cage behavior, saturation, load torque, and type flags explicitly. | -| `Machines.PSSE.GENSAL`, `GENSAE`, `GENROE` | No exact model | **P1 — New models** | Add salient-pole and/or exponential-saturation equations. Do not silently substitute `GENROU`. | -| `Machines.PSSE.GENTPJ` | No exact model | **P2 — New model** | WECC type-J round-rotor machine with saturation on both axes; prioritize after the conventional machine base is validated. | -| `Machines.PSSE.Plant` | Generator plus controller attachment | **P1 — Structural extension** | OpenIPSL's replaceable machine/exciter/governor/PSS composition is a good reference for a declarative GridDyn dynamic-plant assembly API. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Machines.PSSE.GENROU` | `GenModelGENROU` | **P0 — Existing candidate** | Best first machine reference. Compare flux/transient states, saturation, dq current, rotor angle/speed, electrical power, and machine/system-base conversion. | +| `Machines.PSSE.GENCLS` | `GenModelClassical` | **P0 — Existing candidate** | Validate swing-equation sign, damping, initial angle, and power/base conventions. | +| `Machines.PSAT.Order2`, `Order3`, `Order4`, `Order5_Type1`, `Order5_Type2`, `Order6` | `GenModelClassical`, `GenModel3`, `GenModel4`, `GenModel5`, `GenModel5type2`, `GenModel6/6type2` | **P0 — Existing candidates** | Match each order individually. Equal order does not establish matching stator algebraics, saturation, or initialization. | +| `Machines.PSAT.MotorTypeI`, `MotorTypeIII`, `MotorTypeV` | `MotorLoad3`, `MotorLoad5` | **P1 — Existing candidates** | Audit slip, mechanical torque, stall/protection, and initialization before choosing a mapping. | +| `Machines.PSSE.CIM5`, `CIM6` | No verified exact analogue | **P1 — New models** | PSS/e induction-motor models are a high-value extension beyond the current motor candidates; model single-/double-cage behavior, saturation, load torque, and type flags explicitly. | +| `Machines.PSSE.GENSAL`, `GENSAE`, `GENROE` | No exact model | **P1 — New models** | Add salient-pole and/or exponential-saturation equations. Do not silently substitute `GENROU`. | +| `Machines.PSSE.GENTPJ` | No exact model | **P2 — New model** | WECC type-J round-rotor machine with saturation on both axes; prioritize after the conventional machine base is validated. | +| `Machines.PSSE.Plant` | Generator plus controller attachment | **P1 — Structural extension** | OpenIPSL's replaceable machine/exciter/governor/PSS composition is a good reference for a declarative GridDyn dynamic-plant assembly API. | ## 2. Excitation, limiting, and voltage compensation -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| `Controls.PSSE.ES.SEXS` | `ExciterSEXS` | **P0 — Existing candidate** | Validate voltage sensing, lead-lag convention, limits, and field-voltage initialization. | -| `Controls.PSSE.ES.EXST1` | `ExciterEXST1` | **P0 — Existing candidate** | Compare limiter selector behavior; decide and document support for zero-time-constant bypasses. | -| `Controls.PSSE.ES.EXAC1`, `EXAC2` | `ExciterEXAC1`, `ExciterEXAC2` | **P0 — Existing candidates** | Validate measured-voltage path, saturation/rectifier loading, and all limits. | -| `Controls.PSSE.ES.ESDC1A`, `ESDC2A` | `ExciterDC1A`, `ExciterDC2A` | **P1 — Existing candidates** | Audit transducer, switching, and saturation paths before claiming equivalence. | -| `Controls.PSSE.ES.IEEET1`, `IEEET2` | `ExciterIEEEtype1`, `ExciterIEEEtype2` | **P1 — Existing candidates** | GridDyn's generic class names need model-specific equation and limiter audits. | -| `Controls.CGMES.ES.ExcSEXS` | `ExciterSEXS` | **P1 — Existing candidate** | Validate the CGMES signal and parameter conventions separately from the PSS/e wrapper. | -| `Controls.PSSE.ES.AC7B`, `AC8B`, `DC4B`, `ESST1A`, `ESST2A`, `ESST4B`, `ESAC1A`, `ESAC2A` | No exact models | **P1 — New models** | Prioritize the models required by target PSS/e cases. They require explicit limiter/root treatment and a reusable exciter block library. | -| `Controls.PSSE.ES.IEEEX1`, `SCRX`, `ST5B`, `URST5T`, `EXNI`, `EXBAS`, `ESURRY` | No exact models | **P2, except `SCRX` P1 — New models** | `SCRX` is a P1 synthetic-case gap with an OpenIPSL/PowerDynamics reference; add the other models after the common AC/DC exciter blocks are established. | -| PSS/e `EXPIC1`, `ESAC6A` | No exact model in OpenIPSL, ANDES, or PowerDynamics | **P2 — Source gaps** | Do not use ANDES's documented approximate `SEXS` conversion as exact support. Obtain or derive the model equations before porting. | -| `Controls.PSSE.OEL.OEL`, `Controls.PSSE.UEL.MNLEX2`, `Controls.PSSE.COMP.IEEEVC` | No exact analogue | **P1 — Structural extension** | Add field-current/under-excitation and terminal-current-compensation input paths to the exciter interface; then port the model-specific limits. | -| PSAT `AVRTypeI/II/III`, `OEL`, `FieldCurrent`; Simulink excitation/OEL blocks | Existing exciters are only candidates | **P2 — Existing candidates / new blocks** | Treat the PSAT and Simulink families as separate equations, not aliases of PSS/e names. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| ----------------------------------------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Controls.PSSE.ES.SEXS` | `ExciterSEXS` | **P0 — Existing candidate** | Validate voltage sensing, lead-lag convention, limits, and field-voltage initialization. | +| `Controls.PSSE.ES.EXST1` | `ExciterEXST1` | **P0 — Existing candidate** | Compare limiter selector behavior; decide and document support for zero-time-constant bypasses. | +| `Controls.PSSE.ES.EXAC1`, `EXAC2` | `ExciterEXAC1`, `ExciterEXAC2` | **P0 — Existing candidates** | Validate measured-voltage path, saturation/rectifier loading, and all limits. | +| `Controls.PSSE.ES.ESDC1A`, `ESDC2A` | `ExciterDC1A`, `ExciterDC2A` | **P1 — Existing candidates** | Audit transducer, switching, and saturation paths before claiming equivalence. | +| `Controls.PSSE.ES.IEEET1`, `IEEET2` | `ExciterIEEEtype1`, `ExciterIEEEtype2` | **P1 — Existing candidates** | GridDyn's generic class names need model-specific equation and limiter audits. | +| `Controls.CGMES.ES.ExcSEXS` | `ExciterSEXS` | **P1 — Existing candidate** | Validate the CGMES signal and parameter conventions separately from the PSS/e wrapper. | +| `Controls.PSSE.ES.AC7B`, `AC8B`, `DC4B`, `ESST1A`, `ESST2A`, `ESST4B`, `ESAC1A`, `ESAC2A` | No exact models | **P1 — New models** | Prioritize the models required by target PSS/e cases. They require explicit limiter/root treatment and a reusable exciter block library. | +| `Controls.PSSE.ES.IEEEX1`, `SCRX`, `ST5B`, `URST5T`, `EXNI`, `EXBAS`, `ESURRY` | No exact models | **P2, except `SCRX` P1 — New models** | `SCRX` is a P1 synthetic-case gap with an OpenIPSL/PowerDynamics reference; add the other models after the common AC/DC exciter blocks are established. | +| PSS/e `EXPIC1`, `ESAC6A` | No exact model in OpenIPSL, ANDES, or PowerDynamics | **P2 — Source gaps** | Do not use ANDES's documented approximate `SEXS` conversion as exact support. Obtain or derive the model equations before porting. | +| `Controls.PSSE.OEL.OEL`, `Controls.PSSE.UEL.MNLEX2`, `Controls.PSSE.COMP.IEEEVC` | No exact analogue | **P1 — Structural extension** | Add field-current/under-excitation and terminal-current-compensation input paths to the exciter interface; then port the model-specific limits. | +| PSAT `AVRTypeI/II/III`, `OEL`, `FieldCurrent`; Simulink excitation/OEL blocks | Existing exciters are only candidates | **P2 — Existing candidates / new blocks** | Treat the PSAT and Simulink families as separate equations, not aliases of PSS/e names. | ## 3. Turbine-governors and power-system stabilizers -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| `Controls.PSSE.TG.TGOV1` | `GovernorTgov1` | **P0 — Existing candidate** | GridDyn already has DYR-order, limiter, initialization, Jacobian, and isolated-trajectory coverage; make it the first OpenIPSL governor comparison. | -| `Controls.PSSE.TG.IEEEG1` | `GovernorIeeeG1` | **P0 — Existing candidate** | Compare lead/lag ordering, turbine fractions, rate/position limits, and multi-machine attachment. | -| `Controls.PSSE.PSS.IEEEST` | `StabilizerIEEEST` | **P0 — Existing candidate** | Test zero-time-constant bypasses, filters, lead-lag states, and output limits. | -| `Controls.PSSE.TG.HYGOV`, `IEESGO` | `GovernorHydro`, `GovernorReheat` are candidates | **P1 — Existing candidates** | Perform an equation, water-column/reheat, limit, and parameter-unit audit before reuse. | -| `Controls.PSSE.TG.GAST`, `GGOV1`, `GGOV1DU`, `DEGOV`, `IEEEG2`, `WEHGOV`, `WPIDHY`, `WSIEG1` | No exact models | **`GGOV1` P1; remainder P2 — New models** | `GGOV1` is the largest synthetic-case gap (6,977 records) and has OpenIPSL/experimental-PowerDynamics references. `GAST` and the remaining engine/wind/hydro variants are P2 after reusable governor limit and mode-selector blocks exist. | -| `Controls.PSSE.TG.ConstantPower`; PSAT `TGTypeI–VI`; CGMES `GovHydroIEEE0`; Simulink TG blocks | Basic/reheat/hydro classes are candidates | **P2 — Existing candidates / new models** | Establish whether a GridDyn governor can expose the reference and mechanical-power ports required by each family. | -| `Controls.PSSE.PSS.PSS2A`, `PSS2B`, `IEE2ST`, `STAB2A`, `STAB3`, `STABNI`, `STBSVC` | No exact model | **P2 — New models** | Add dual/remote measurement inputs, filters, mode selection, and signal routing. | -| PSAT `PSSTypeI/II/III`; Simulink PSS; `DisabledPSS` | `StabilizerIEEEST` / `StabilizerST2CUT` are only candidates | **P2 — Existing candidates / new blocks** | Keep the source-family semantics explicit. `DisabledPSS` is mainly a plant-assembly behavior. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Controls.PSSE.TG.TGOV1` | `GovernorTgov1` | **P0 — Existing candidate** | GridDyn already has DYR-order, limiter, initialization, Jacobian, and isolated-trajectory coverage; make it the first OpenIPSL governor comparison. | +| `Controls.PSSE.TG.IEEEG1` | `GovernorIeeeG1` | **P0 — Existing candidate** | Compare lead/lag ordering, turbine fractions, rate/position limits, and multi-machine attachment. | +| `Controls.PSSE.PSS.IEEEST` | `StabilizerIEEEST` | **P0 — Existing candidate** | Test zero-time-constant bypasses, filters, lead-lag states, and output limits. | +| `Controls.PSSE.TG.HYGOV`, `IEESGO` | `GovernorHydro`, `GovernorReheat` are candidates | **P1 — Existing candidates** | Perform an equation, water-column/reheat, limit, and parameter-unit audit before reuse. | +| `Controls.PSSE.TG.GAST`, `GGOV1`, `GGOV1DU`, `DEGOV`, `IEEEG2`, `WEHGOV`, `WPIDHY`, `WSIEG1` | No exact models | **`GGOV1` P1; remainder P2 — New models** | `GGOV1` is the largest synthetic-case gap (6,977 records) and has OpenIPSL/experimental-PowerDynamics references. `GAST` and the remaining engine/wind/hydro variants are P2 after reusable governor limit and mode-selector blocks exist. | +| `Controls.PSSE.TG.ConstantPower`; PSAT `TGTypeI–VI`; CGMES `GovHydroIEEE0`; Simulink TG blocks | Basic/reheat/hydro classes are candidates | **P2 — Existing candidates / new models** | Establish whether a GridDyn governor can expose the reference and mechanical-power ports required by each family. | +| `Controls.PSSE.PSS.PSS2A`, `PSS2B`, `IEE2ST`, `STAB2A`, `STAB3`, `STABNI`, `STBSVC` | No exact model | **P2 — New models** | Add dual/remote measurement inputs, filters, mode selection, and signal routing. | +| PSAT `PSSTypeI/II/III`; Simulink PSS; `DisabledPSS` | `StabilizerIEEEST` / `StabilizerST2CUT` are only candidates | **P2 — Existing candidates / new blocks** | Keep the source-family semantics explicit. `DisabledPSS` is mainly a plant-assembly behavior. | ## 4. Renewable, inverter, wind, solar, and storage models This is the principal new high-value OpenIPSL family. GridDyn's `GenModelInverter` is not an implementation of these models; a reusable positive-sequence converter/current-controller interface is the prerequisite. -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| `Renewables.PSSE.InverterInterface.REGCA1` | Generic `GenModelInverter` only | **P1 — New model** | First native renewable block: terminal-voltage filtering, current limits, LVAC logic, and P/Q current commands. | -| `Renewables.PSSE.ElectricalController.REECA1`, `REECB1`, `REECCU1` | No exact model | **P1 — New models** | Add on top of `REGCA1`; preserve reactive-current priority, voltage-dip logic, limit ordering, and frozen-state behavior. | -| `Renewables.PSSE.PlantController.REPCA1` | No exact model | **P1 — New model** | Plant-level voltage/reactive/active-power/frequency control; requires local and remote measurement interfaces. | -| `Renewables.PSSE.Wind`, `PV`, `BESS` | No exact model | **P1 — Structural extension** | Compose `REGCA1` + `REEC*` + `REPCA1` into attachable plant templates. BESS additionally needs energy/state-of-charge semantics before it is a complete storage model. | -| `Renewables.PSSE.WindDriveTrain.WTDTA1` | No exact model | **P2 — New model** | Add after the renewable plant electrical interface; model shaft/torsional states and mechanical-power connection. | -| `Renewables.PSSE.AddOnBlocks.IrradianceToPower` | `FileLoad`-like signal sources only | **P2 — Structural extension** | Define time-series/irradiance input policy and clipping before adding PV availability behavior. | -| `Wind.PSSE.WT3G*`, `WT4G*`; PSAT type-3; GE type-3 | No exact model | **Type-3 PSS/e system P1; remainder P2 — New model families** | `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` are required by ACTIVSg and must be implemented as one coupled P1 system after the renewable controller foundation. Keep other vendor/standard families P2 and map them to reusable components rather than a monolithic generator class. | -| PSAT constant-PQ PV; PowerFactory WECC `PVD1`; PowerFactory DIgSILENT PV plant/array/control models | No exact model | **P2 — New model families** | Use as validation/extension targets after the `REGC/REEC/REPCA` path. Detailed PV-array/DC-link models may be outside phasor-time-domain scope. | -| `VSD.Generic.AC2DCandDC2AC`, `VoltsHertzController` | No exact model | **P3 — Structural extension** | Cross-domain power-electronics models require a justified phasor/DC coupling contract and may be better isolated through FMI first. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| --------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `Renewables.PSSE.InverterInterface.REGCA1` | Generic `GenModelInverter` only | **P1 — New model** | First native renewable block: terminal-voltage filtering, current limits, LVAC logic, and P/Q current commands. | +| `Renewables.PSSE.ElectricalController.REECA1`, `REECB1`, `REECCU1` | No exact model | **P1 — New models** | Add on top of `REGCA1`; preserve reactive-current priority, voltage-dip logic, limit ordering, and frozen-state behavior. | +| `Renewables.PSSE.PlantController.REPCA1` | No exact model | **P1 — New model** | Plant-level voltage/reactive/active-power/frequency control; requires local and remote measurement interfaces. | +| `Renewables.PSSE.Wind`, `PV`, `BESS` | No exact model | **P1 — Structural extension** | Compose `REGCA1` + `REEC*` + `REPCA1` into attachable plant templates. BESS additionally needs energy/state-of-charge semantics before it is a complete storage model. | +| `Renewables.PSSE.WindDriveTrain.WTDTA1` | No exact model | **P2 — New model** | Add after the renewable plant electrical interface; model shaft/torsional states and mechanical-power connection. | +| `Renewables.PSSE.AddOnBlocks.IrradianceToPower` | `FileLoad`-like signal sources only | **P2 — Structural extension** | Define time-series/irradiance input policy and clipping before adding PV availability behavior. | +| `Wind.PSSE.WT3G*`, `WT4G*`; PSAT type-3; GE type-3 | No exact model | **Type-3 PSS/e system P1; remainder P2 — New model families** | `WT3G1`/`WT3E1`/`WT3P1`/`WT3T1` are required by ACTIVSg and must be implemented as one coupled P1 system after the renewable controller foundation. Keep other vendor/standard families P2 and map them to reusable components rather than a monolithic generator class. | +| PSAT constant-PQ PV; PowerFactory WECC `PVD1`; PowerFactory DIgSILENT PV plant/array/control models | No exact model | **P2 — New model families** | Use as validation/extension targets after the `REGC/REEC/REPCA` path. Detailed PV-array/DC-link models may be outside phasor-time-domain scope. | +| `VSD.Generic.AC2DCandDC2AC`, `VoltsHertzController` | No exact model | **P3 — Structural extension** | Cross-domain power-electronics models require a justified phasor/DC coupling contract and may be better isolated through FMI first. | ## 5. Loads -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| PSAT `PQ`, `ZIP`, `ZIP_ExtInput` | `ZipLoad` | **P0 — Existing candidate** | Start with constant-P/Q and static ZIP coefficients; separately validate external-input semantics. | -| PSAT `FrequencyDependent` | `FDepLoad` / `FrequencySensitiveLoad` | **P0 — Existing candidate** | Compare frequency reference, exponent/damping signs, and voltage dependence. | -| PSAT `VoltageDependent` | `ExponentialLoad` | **P1 — Existing candidate** | Verify the exponent, base, and low-voltage behavior. | -| PSAT `Mixed` | `AggregateLoad` is a candidate | **P1 — Existing candidate** | Match composition, initialization, and contribution reporting. | -| PSAT `ExponentialRecovery`, `ThermostaticallyControlled` | No exact model | **P1/P2 — New models** | Exponential recovery is **P1** for long-term voltage studies; thermostatic load is **P2** and needs switching/population semantics. | -| PSAT `PQvar`; PSS/e `Load`, `Load_switch`, `Load_variation`, `Load_ExtInput` | `RampLoad`, `FileLoad`, `SourceLoad` are candidates | **P2 — Structural extension** | Define event, interpolation, and externally driven-input semantics; do not equate time-series behavior with a static load. | -| Noise injections | No exact model | **P3 — New capability** | Requires a reproducible stochastic process, seed management, and solver-compatible noise policy. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| ---------------------------------------------------------------------------- | --------------------------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| PSAT `PQ`, `ZIP`, `ZIP_ExtInput` | `ZipLoad` | **P0 — Existing candidate** | Start with constant-P/Q and static ZIP coefficients; separately validate external-input semantics. | +| PSAT `FrequencyDependent` | `FDepLoad` / `FrequencySensitiveLoad` | **P0 — Existing candidate** | Compare frequency reference, exponent/damping signs, and voltage dependence. | +| PSAT `VoltageDependent` | `ExponentialLoad` | **P1 — Existing candidate** | Verify the exponent, base, and low-voltage behavior. | +| PSAT `Mixed` | `AggregateLoad` is a candidate | **P1 — Existing candidate** | Match composition, initialization, and contribution reporting. | +| PSAT `ExponentialRecovery`, `ThermostaticallyControlled` | No exact model | **P1/P2 — New models** | Exponential recovery is **P1** for long-term voltage studies; thermostatic load is **P2** and needs switching/population semantics. | +| PSAT `PQvar`; PSS/e `Load`, `Load_switch`, `Load_variation`, `Load_ExtInput` | `RampLoad`, `FileLoad`, `SourceLoad` are candidates | **P2 — Structural extension** | Define event, interpolation, and externally driven-input semantics; do not equate time-series behavior with a static load. | +| Noise injections | No exact model | **P3 — New capability** | Requires a reproducible stochastic process, seed management, and solver-compatible noise policy. | ## 6. FACTS, branches, shunts, events, and measurements -| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | -| --- | --- | --- | --- | -| PSS/e `CSVGN1`, `SVC`; PSAT `STATCOM` | `Svd` / `VSCShunt` are candidates | **P1/P2 — New models** | Start (**P1**) with an SVC controlled-susceptance interface and limits; STATCOM and its converter current/limit controls are **P2**. | -| PSAT `TCSC` | No exact model | **P2 — New model** | Add thyristor firing/reactance control and series-compensation limits. | -| `Branches.Generic.ULTC`, PSAT `ULTC_VoltageControl`, Simulink `LTC` | `AdjustableTransformer` is a building block | **P1 — Structural extension** | Add discrete tap position, deadband, delay, and event/root behavior. | -| `Branches.PSAT.PhaseShiftingTransformer` | Existing transformer links are candidates | **P2 — Structural extension** | Add controlled phase-shift logic after tap-control infrastructure exists. | +| OpenIPSL model(s) | GridDyn analogue | Priority / status | Notes | +| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| PSS/e `CSVGN1`, `SVC`; PSAT `STATCOM` | `Svd` / `VSCShunt` are candidates | **P1/P2 — New models** | Start (**P1**) with an SVC controlled-susceptance interface and limits; STATCOM and its converter current/limit controls are **P2**. | +| PSAT `TCSC` | No exact model | **P2 — New model** | Add thyristor firing/reactance control and series-compensation limits. | +| `Branches.Generic.ULTC`, PSAT `ULTC_VoltageControl`, Simulink `LTC` | `AdjustableTransformer` is a building block | **P1 — Structural extension** | Add discrete tap position, deadband, delay, and event/root behavior. | +| `Branches.PSAT.PhaseShiftingTransformer` | Existing transformer links are candidates | **P2 — Structural extension** | Add controlled phase-shift logic after tap-control infrastructure exists. | | `Branches.PSSE/PSAT.TwoWindingTransformer`, PSAT `ThreeWindingTransformer`, `PwLine`, fixed shunts/capacitor banks | `AcLine`, transformer links, `ThreeWindingTransformer`, `ZipLoad` shunts | **P0/P1 — Existing candidates** | Fixed network equivalents are **P0** base-conversion checks; switched/modified capacitor-bank controls are **P1** event/control work. | -| `Events.Breaker`, `PwFault`, `PwFaultPQ` | breakers, fault/event infrastructure, control relays | **P1 — Structural extension** | Validate fault formulation, event time, clearing, and topology-update semantics. | -| `Sensors.PwVoltage`, `PwCurrent`, `SoftPMU` | sensors, `Pmu`, bus/relay measurements | **P2 — Structural extension** | Establish phasor, filter, frequency, and reporting-time convention. | -| Voltage/current source models | `SourceLoad` is only a partial analogue | **P2 — New interface** | Add controlled-source injection only where required by validation cases. | +| `Events.Breaker`, `PwFault`, `PwFaultPQ` | breakers, fault/event infrastructure, control relays | **P1 — Structural extension** | Validate fault formulation, event time, clearing, and topology-update semantics. | +| `Sensors.PwVoltage`, `PwCurrent`, `SoftPMU` | sensors, `Pmu`, bus/relay measurements | **P2 — Structural extension** | Establish phasor, filter, frequency, and reporting-time convention. | +| Voltage/current source models | `SourceLoad` is only a partial analogue | **P2 — New interface** | Add controlled-source injection only where required by validation cases. | ## 7. Three-phase and mono/tri models OpenIPSL now contains three-phase buses, 1/2/3-phase lines, transformers and connection variants, capacitor banks, static/dynamic wye/delta loads, and mono/tri coupling models. GridDyn has some three-phase load support, but no matching unbalanced three-phase network, transformer-connection, or sequence/phase interface. -| OpenIPSL family | Priority / status | Required GridDyn work | -| --- | --- | --- | -| `ThreePhase.Buses`, `Branches.Lines`, `Branches.Transformer`, `Banks` | **P3 — Architectural** | A multi-conductor unbalanced network formulation, phase-voltage/current connectors, and compatible sparse/Jacobian infrastructure. | -| `ThreePhase.Loads` static and dynamic wye/delta models | **P3 — Architectural** | Correct phase/neutral and delta coupling; existing `ThreePhaseLoad` is not sufficient evidence of equivalence. | -| `ThreePhase.Branches.MonoTri` | **P3 — Architectural** | A defined positive-sequence to three-phase/sequence coupling boundary, including transformer vector groups and zero/negative-sequence treatment. | +| OpenIPSL family | Priority / status | Required GridDyn work | +| --------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ThreePhase.Buses`, `Branches.Lines`, `Branches.Transformer`, `Banks` | **P3 — Architectural** | A multi-conductor unbalanced network formulation, phase-voltage/current connectors, and compatible sparse/Jacobian infrastructure. | +| `ThreePhase.Loads` static and dynamic wye/delta models | **P3 — Architectural** | Correct phase/neutral and delta coupling; existing `ThreePhaseLoad` is not sufficient evidence of equivalence. | +| `ThreePhase.Branches.MonoTri` | **P3 — Architectural** | A defined positive-sequence to three-phase/sequence coupling boundary, including transformer vector groups and zero/negative-sequence treatment. | ## Validation policy diff --git a/docs/developer-guide/powerdynamics-compatibility.md b/docs/developer-guide/powerdynamics-compatibility.md index 42a949b6b..30e190cd2 100644 --- a/docs/developer-guide/powerdynamics-compatibility.md +++ b/docs/developer-guide/powerdynamics-compatibility.md @@ -12,69 +12,69 @@ That is strong source material, but it is not GridDyn validation. A C++ port sti ## Priority definitions -| Priority | Meaning | -| --- | --- | -| **P0 — port and validate** | PowerDynamics has a model-specific implementation and OpenIPSL test path; GridDyn has no exact model. | -| **P1 — high-value extension** | A commonly useful GridDyn capability is missing, although PowerDynamics validation is incomplete or the work requires a reusable interface. | -| **P2 — targeted extension** | Useful but specialized, experimental, or dependent on a P0/P1 design decision. | -| **P3 — different simulation scope** | The model retains fast electromagnetic/filter states or needs structural changes beyond GridDyn's present positive-sequence dynamic path. | +| Priority | Meaning | +| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| **P0 — port and validate** | PowerDynamics has a model-specific implementation and OpenIPSL test path; GridDyn has no exact model. | +| **P1 — high-value extension** | A commonly useful GridDyn capability is missing, although PowerDynamics validation is incomplete or the work requires a reusable interface. | +| **P2 — targeted extension** | Useful but specialized, experimental, or dependent on a P0/P1 design decision. | +| **P3 — different simulation scope** | The model retains fast electromagnetic/filter states or needs structural changes beyond GridDyn's present positive-sequence dynamic path. | ## Models GridDyn does not have exactly ### 1. Synchronous machines -| PowerDynamics model | GridDyn status | Priority | Why it matters | -| --- | --- | --- | --- | -| `PSSE_GENROE` | No exact round-rotor exponential-saturation machine | **P0** | PowerDynamics has a registered OpenIPSL regression. It is the direct route to PSS/e `GENROE`, not a justification to substitute GridDyn `GENROU` (quadratic saturation). | -| `PSSE_GENSAL` | No exact salient-pole quadratic-saturation machine | **P0** | Has an OpenIPSL test path. Add a distinct salient-pole dq machine and compare saturation, flux states, current, angle, speed, and bases. | -| `PSSE_GENSAE` | No exact salient-pole exponential-saturation machine | **P0** | Has an OpenIPSL test path; naturally follows `GENSAL` once the salient-pole structure exists. | -| `SauerPaiMachine` | No verified exact equivalent | **P1** | A detailed synchronous-machine formulation with explicit dq flux and torque variables. Audit against `GenModel6`/`GenModel8` before deciding whether it is a new model or a validation target for an existing generic model. | -| `Swing` | No standalone swing-injection component | **P2** | A reusable mechanical-power-to-angle/frequency block with a prescribed voltage magnitude. GridDyn generator models overlap physically, but not necessarily as a composable network injection. | -| `VariableFrequencySlack` | No equivalent dynamic slack model identified | **P2** | Supplies a rotating slack voltage driven by a configurable frequency. Useful for benchmark/reference cases and grid-forming studies. | +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| ------------------------ | ---------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PSSE_GENROE` | No exact round-rotor exponential-saturation machine | **P0** | PowerDynamics has a registered OpenIPSL regression. It is the direct route to PSS/e `GENROE`, not a justification to substitute GridDyn `GENROU` (quadratic saturation). | +| `PSSE_GENSAL` | No exact salient-pole quadratic-saturation machine | **P0** | Has an OpenIPSL test path. Add a distinct salient-pole dq machine and compare saturation, flux states, current, angle, speed, and bases. | +| `PSSE_GENSAE` | No exact salient-pole exponential-saturation machine | **P0** | Has an OpenIPSL test path; naturally follows `GENSAL` once the salient-pole structure exists. | +| `SauerPaiMachine` | No verified exact equivalent | **P1** | A detailed synchronous-machine formulation with explicit dq flux and torque variables. Audit against `GenModel6`/`GenModel8` before deciding whether it is a new model or a validation target for an existing generic model. | +| `Swing` | No standalone swing-injection component | **P2** | A reusable mechanical-power-to-angle/frequency block with a prescribed voltage magnitude. GridDyn generator models overlap physically, but not necessarily as a composable network injection. | +| `VariableFrequencySlack` | No equivalent dynamic slack model identified | **P2** | Supplies a rotating slack voltage driven by a configurable frequency. Useful for benchmark/reference cases and grid-forming studies. | ### 2. Excitation systems and governor controls -| PowerDynamics model | GridDyn status | Priority | Why it matters | -| --- | --- | --- | --- | -| `PSSE_SCRX` | No exact static exciter | **P0** | PowerDynamics has an OpenIPSL test. Requires its lead-lag, bus-/solid-fed switching, crowbar, and field-current behavior—not merely a generic high-gain AVR. | -| `PSSE_ESST1A` | No exact static exciter | **P0** | OpenIPSL-tested model with limiter, rectifier-loading, and feedback behavior; a useful common PSS/e exciter target. | -| `PSSE_ESST4B` | No exact static exciter | **P0** | OpenIPSL-tested model. It is distinct from GridDyn's existing `ExciterESST3A`. | -| `PSSE_GGOV1_EXPERIMENTAL` | No exact gas/engine governor | **P2** | This is a valuable specification/prototype for `GGOV1`, but PowerDynamics marks it experimental and its test documents known OpenIPSL/reference issues. Do an independent equation and reference audit before porting. | -| `PSSE_HYGOV` | `GovernorHydro` is a candidate, not exact support | **P1 validation** | PowerDynamics has an OpenIPSL comparison. Use it to decide whether GridDyn's hydro model can be corrected/validated or a dedicated `HYGOV` is needed. | -| `PSSE_IEEET1` | `ExciterIEEEtype1` is a candidate | **P1 validation** | The PowerDynamics/OpenIPSL test is a concrete starting point for proving or rejecting equivalence. | -| `AVRFixed`, `AVRTypeI`, `GovFixed`, `TurbineGovTypeI` | Existing GridDyn AVR/governor classes are only candidates | **P2** | Simple reusable controls; audit equations and ports before mapping by generic type name. | +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| ----------------------------------------------------- | --------------------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `PSSE_SCRX` | No exact static exciter | **P0** | PowerDynamics has an OpenIPSL test. Requires its lead-lag, bus-/solid-fed switching, crowbar, and field-current behavior—not merely a generic high-gain AVR. | +| `PSSE_ESST1A` | No exact static exciter | **P0** | OpenIPSL-tested model with limiter, rectifier-loading, and feedback behavior; a useful common PSS/e exciter target. | +| `PSSE_ESST4B` | No exact static exciter | **P0** | OpenIPSL-tested model. It is distinct from GridDyn's existing `ExciterESST3A`. | +| `PSSE_GGOV1_EXPERIMENTAL` | No exact gas/engine governor | **P2** | This is a valuable specification/prototype for `GGOV1`, but PowerDynamics marks it experimental and its test documents known OpenIPSL/reference issues. Do an independent equation and reference audit before porting. | +| `PSSE_HYGOV` | `GovernorHydro` is a candidate, not exact support | **P1 validation** | PowerDynamics has an OpenIPSL comparison. Use it to decide whether GridDyn's hydro model can be corrected/validated or a dedicated `HYGOV` is needed. | +| `PSSE_IEEET1` | `ExciterIEEEtype1` is a candidate | **P1 validation** | The PowerDynamics/OpenIPSL test is a concrete starting point for proving or rejecting equivalence. | +| `AVRFixed`, `AVRTypeI`, `GovFixed`, `TurbineGovTypeI` | Existing GridDyn AVR/governor classes are only candidates | **P2** | Simple reusable controls; audit equations and ports before mapping by generic type name. | ### 3. Inverters and converter controls -| PowerDynamics model(s) | GridDyn status | Priority | Why it matters | -| --- | --- | --- | --- | -| `IdealDroopInverter` | No model-specific grid-forming droop inverter | **P1** | RMS-friendly voltage-source inverter with filtered P/Q droop, voltage/frequency setpoints, and angle state. A strong first grid-forming implementation target. | -| `DroopOuter`, `DroopInverter` | No equivalent composable grid-forming controller | **P1** | Separates the outer droop law from the electrical plant, which is a useful GridDyn architecture for testing and reuse. | -| `SimpleGFL`, `SimpleGFLDC` | No grid-following inverter with PLL/current-control/DC-link model | **P1/P3** | The AC current-control/PLL portion is **P1** converter-interface work; the DC-link capacitor and fast control details in `SimpleGFLDC` are **P3** unless GridDyn explicitly expands beyond phasor RMS dynamics. | -| `SimplePLL`, `PLL_LPF` | No reusable PLL component identified | **P1** | Needed by grid-following renewable models and by the OpenIPSL `REGCA1`/`REEC*`/`REPCA1` roadmap. | -| `LFilter`, `LCFilter`, `LCLFilter`, `VC`, `CC1`, `CC2`, controlled voltage/current sources | No equivalent composable filter/controller library | **P3** | These retain converter/filter electromagnetic states in a global dq frame. Use them as a design reference or FMI pilot unless a deliberate EMT-like GridDyn extension is approved. | +| PowerDynamics model(s) | GridDyn status | Priority | Why it matters | +| ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `IdealDroopInverter` | No model-specific grid-forming droop inverter | **P1** | RMS-friendly voltage-source inverter with filtered P/Q droop, voltage/frequency setpoints, and angle state. A strong first grid-forming implementation target. | +| `DroopOuter`, `DroopInverter` | No equivalent composable grid-forming controller | **P1** | Separates the outer droop law from the electrical plant, which is a useful GridDyn architecture for testing and reuse. | +| `SimpleGFL`, `SimpleGFLDC` | No grid-following inverter with PLL/current-control/DC-link model | **P1/P3** | The AC current-control/PLL portion is **P1** converter-interface work; the DC-link capacitor and fast control details in `SimpleGFLDC` are **P3** unless GridDyn explicitly expands beyond phasor RMS dynamics. | +| `SimplePLL`, `PLL_LPF` | No reusable PLL component identified | **P1** | Needed by grid-following renewable models and by the OpenIPSL `REGCA1`/`REEC*`/`REPCA1` roadmap. | +| `LFilter`, `LCFilter`, `LCLFilter`, `VC`, `CC1`, `CC2`, controlled voltage/current sources | No equivalent composable filter/controller library | **P3** | These retain converter/filter electromagnetic states in a global dq frame. Use them as a design reference or FMI pilot unless a deliberate EMT-like GridDyn extension is approved. | ### 4. Dynamic branches, shunts, and faults -| PowerDynamics model | GridDyn status | Priority | Why it matters | -| --- | --- | --- | --- | -| `DynamicSeriesRLBranch` | No AC series-RL branch with current states | **P1** | A positive-sequence dq branch with explicit R-L current dynamics and optional transformer ratios. This is distinct from GridDyn's algebraic `AcLine`; GridDyn's dynamic `DcLink` does not cover AC dq line states. | -| `DynamicCShunt` | No AC capacitor shunt with voltage/current states | **P2** | A dynamic capacitor branch; useful for converter/filter and fast-transient studies, but outside ordinary RMS phasor network models. | -| `DynamicParallelRCShunt` | No AC parallel RC shunt with states | **P2** | Extends the capacitor case with explicit resistor current. Prioritize only with a clear dynamic-network use case. | -| `RXGroundFault` | No separately attachable R-X ground-fault injection model | **P2** | GridDyn has line-fault and relay mechanisms, but this model is a controllable bus-ground impedance injection with active/reactive fault output. | -| `PiLine_fault` | No exact Pi-line fault component | **P2 validation** | GridDyn `AcLine` has fault-location support, so first compare semantics (fault impedance, location, clearing, and topology) rather than adding a duplicate class. | +| PowerDynamics model | GridDyn status | Priority | Why it matters | +| ------------------------ | --------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `DynamicSeriesRLBranch` | No AC series-RL branch with current states | **P1** | A positive-sequence dq branch with explicit R-L current dynamics and optional transformer ratios. This is distinct from GridDyn's algebraic `AcLine`; GridDyn's dynamic `DcLink` does not cover AC dq line states. | +| `DynamicCShunt` | No AC capacitor shunt with voltage/current states | **P2** | A dynamic capacitor branch; useful for converter/filter and fast-transient studies, but outside ordinary RMS phasor network models. | +| `DynamicParallelRCShunt` | No AC parallel RC shunt with states | **P2** | Extends the capacitor case with explicit resistor current. Prioritize only with a clear dynamic-network use case. | +| `RXGroundFault` | No separately attachable R-X ground-fault injection model | **P2** | GridDyn has line-fault and relay mechanisms, but this model is a controllable bus-ground impedance injection with active/reactive fault output. | +| `PiLine_fault` | No exact Pi-line fault component | **P2 validation** | GridDyn `AcLine` has fault-location support, so first compare semantics (fault impedance, location, clearing, and topology) rather than adding a duplicate class. | ## Existing GridDyn overlap — use as validation targets, not new ports These PowerDynamics models should not be counted as clear GridDyn gaps. Their value is the available Julia/OpenIPSL formulation and tests. -| PowerDynamics model(s) | GridDyn candidate | Recommended action | -| --- | --- | --- | -| `PSSE_GENCLS`, `PSSE_GENROU`, `ClassicalMachine` | `GenModelClassical`, `GenModelGENROU` | Add/compare against captured references before changing C++ equations. | -| `PSSE_EXST1`, `PSSE_IEEEST`, `PSSE_IEEEG1` | `ExciterEXST1`, `StabilizerIEEEST`, `GovernorIeeeG1` | Reuse PowerDynamics' tested Modelica cases to obtain another independent reference path. | -| `TGOV1` | `GovernorTgov1` | Validate instead of porting. | +| PowerDynamics model(s) | GridDyn candidate | Recommended action | +| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| `PSSE_GENCLS`, `PSSE_GENROU`, `ClassicalMachine` | `GenModelClassical`, `GenModelGENROU` | Add/compare against captured references before changing C++ equations. | +| `PSSE_EXST1`, `PSSE_IEEEST`, `PSSE_IEEEG1` | `ExciterEXST1`, `StabilizerIEEEST`, `GovernorIeeeG1` | Reuse PowerDynamics' tested Modelica cases to obtain another independent reference path. | +| `TGOV1` | `GovernorTgov1` | Validate instead of porting. | | `PQLoad`, `ZIPLoad`, `VoltageDependentLoad`, `ConstantYLoad`, `ConstantCurrentLoad`, `PSSE_Load` | `ZipLoad`, `ExponentialLoad`, `SourceLoad` are candidates | Audit the voltage/current and low-voltage semantics; GridDyn has related static-load classes. | -| `PiLine`, `Breaker`, `StaticShunt` | `AcLine`, `ZBreaker`, fixed-admittance `ZipLoad` | Treat as power-flow/event and base-conversion validation cases. | +| `PiLine`, `Breaker`, `StaticShunt` | `AcLine`, `ZBreaker`, fixed-admittance `ZipLoad` | Treat as power-flow/event and base-conversion validation cases. | ## Recommended sequence @@ -89,4 +89,3 @@ These PowerDynamics models should not be counted as clear GridDyn gaps. Their va Keep a minimized source case and captured trajectory in GridDyn. Record the PowerDynamics commit, original OpenIPSL commit, Julia/Modelica tool versions if used, solver settings, event definition, variable names/units, sampling times, and signal tolerances. Compare initialization, no-disturbance equilibrium, trajectories, and event times separately. PowerDynamics is a useful independent implementation, but GridDyn must not claim compatibility merely because a Julia model or a similar C++ class exists. - diff --git a/docs/developer-guide/psse-raw-dc-compatibility.md b/docs/developer-guide/psse-raw-dc-compatibility.md index e9a3916f8..5838592fd 100644 --- a/docs/developer-guide/psse-raw-dc-compatibility.md +++ b/docs/developer-guide/psse-raw-dc-compatibility.md @@ -1,7 +1,7 @@ # PSS/E RAW DC compatibility status This note records the intended scope and known limitations of GridDyn's PSS/E -RAW DC import. It is a compatibility layer for the AC power-flow model used +RAW DC import. It is a compatibility layer for the AC power-flow model used by PowerModels.jl; it is not a replacement for GridDyn's physical DC-network models (`DcBus`, `DcLink`, `AcDcConverter`, `VSCShunt`, and `Hvdc`). @@ -9,10 +9,10 @@ models (`DcBus`, `DcLink`, `AcDcConverter`, `VSCShunt`, and `Hvdc`). The RAW reader recognizes these section labels: -| RAW section | GridDyn representation | Power-flow behavior | -| --- | --- | --- | -| `BEGIN TWO-TERMINAL DC DATA` / `BEGIN TWO-TERMINAL DC LINE DATA` | `links::RawDcLine` | Scheduled active terminal transfer; PQ-terminal reactive variable and voltage setpoint | -| `BEGIN VOLTAGE SOURCE CONVERTER DATA` / `BEGIN VSC DC LINE DATA` | `links::RawDcLine` | PowerModels-compatible zero initial active transfer; PQ-terminal reactive variable and voltage setpoint | +| RAW section | GridDyn representation | Power-flow behavior | +| ---------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------- | +| `BEGIN TWO-TERMINAL DC DATA` / `BEGIN TWO-TERMINAL DC LINE DATA` | `links::RawDcLine` | Scheduled active terminal transfer; PQ-terminal reactive variable and voltage setpoint | +| `BEGIN VOLTAGE SOURCE CONVERTER DATA` / `BEGIN VSC DC LINE DATA` | `links::RawDcLine` | PowerModels-compatible zero initial active transfer; PQ-terminal reactive variable and voltage setpoint | Two-terminal active transfer follows PowerModels' RAW conversion: @@ -21,44 +21,44 @@ Two-terminal active transfer follows PowerModels' RAW conversion: - `MDC == 0`: out of service. For VSC data, the active transfer starts at zero, as it does in -PowerModels' PSS/E importer. The VSC loss slope and rating are retained on +PowerModels' PSS/E importer. The VSC loss slope and rating are retained on the compatibility link; loss intercept and RAW limit fields are retained in its description for diagnostics. `RawDcLine` keeps the active-transfer convention of PowerModels' `dcline` -model. At each connected PQ AC bus, it adds the reactive terminal variable +model. At each connected PQ AC bus, it adds the reactive terminal variable and voltage-magnitude equality needed to retain the bus reactive balance. This is deliberately separate from GridDyn's physical DC components. ## Numerical evidence The regression input is -`test/test_files/input_tests/psse_dc_components.raw`. It is solved by both +`test/test_files/input_tests/psse_dc_components.raw`. It is solved by both GridDyn and PowerModels' JuMP/IPOPT AC power flow. For the shared 100 MVA test case, both give these bus-2 results: -| Quantity | Value | -| --- | ---: | -| voltage magnitude | `1.000000` pu | -| voltage angle | `-0.025284658` rad | +| Quantity | Value | +| ----------------- | -----------------: | +| voltage magnitude | `1.000000` pu | +| voltage angle | `-0.025284658` rad | The combined reactive power from the two RAW DC terminals at bus 2 is also -checked at `-0.0781963933423` pu. The regression is +checked at `-0.0781963933423` pu. The regression is `InputTests.PssERawDcComponentsImportAsScheduledLinks`. ## Known differences and follow-up work -| Topic | Current behavior | Required follow-up | -| --- | --- | --- | -| Physical HVDC equations | RAW records use the PowerModels-style AC-terminal `dcline` abstraction, not a DC grid or converter commutation model. | Map RAW data to GridDyn's native DC models only after validating the PSS/E control and base-conversion semantics. | -| Reactive limits | Two-terminal angle-derived limits and VSC `MINQ`/`MAXQ` are recorded as diagnostics, but are not solver-enforced. | Add limiter equations/state transitions and test constrained cases against a version-matched PSS/E reference. | -| Multiple DC terminals at one PQ bus | PowerModels creates redundant voltage equalities and has a non-unique split of terminal reactive power. GridDyn selects the first RAW DC terminal as the voltage controller and assigns the combined required reactive power there. Bus voltages, angles, and combined reactive power match. | Define and test a documented allocation rule if per-terminal reactive reporting must reproduce a particular PowerModels solver result. | -| VSC losses and nonzero active control | The PowerModels RAW importer initializes VSC active terminal powers at zero, so `loss0` and `loss1` do not alter the regression solution. GridDyn retains the slope as link loss metadata. | Add cases and equations for nonzero active VSC control and affine losses when the intended RAW/PSS/E semantics are established. | -| Active/reactive ratings | Ratings are imported for reporting/violation checks, not imposed as feasibility constraints. | Add solver-enforced apparent-power and terminal power limits. | -| Multi-terminal DC | `BEGIN MULTI-TERMINAL DC LINE DATA` remains unsupported and is skipped. | Implement a separate multi-terminal topology/model; do not flatten it into independent two-terminal transfers. | -| Dynamics | The compatibility link is for AC power flow only. | Validate physical LCC/VSC controls and trajectories against PSS/E or CIGRE benchmark cases using GridDyn's native DC classes. | -| RAW-version coverage | Field handling is based on PowerModels' importer and representative RAW records, not a complete version-specific PSS/E data-format audit. | Audit against the licensed PSS/E RAW manual for each advertised RAW version, including defaults and all control modes. | +| Topic | Current behavior | Required follow-up | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| Physical HVDC equations | RAW records use the PowerModels-style AC-terminal `dcline` abstraction, not a DC grid or converter commutation model. | Map RAW data to GridDyn's native DC models only after validating the PSS/E control and base-conversion semantics. | +| Reactive limits | Two-terminal angle-derived limits and VSC `MINQ`/`MAXQ` are recorded as diagnostics, but are not solver-enforced. | Add limiter equations/state transitions and test constrained cases against a version-matched PSS/E reference. | +| Multiple DC terminals at one PQ bus | PowerModels creates redundant voltage equalities and has a non-unique split of terminal reactive power. GridDyn selects the first RAW DC terminal as the voltage controller and assigns the combined required reactive power there. Bus voltages, angles, and combined reactive power match. | Define and test a documented allocation rule if per-terminal reactive reporting must reproduce a particular PowerModels solver result. | +| VSC losses and nonzero active control | The PowerModels RAW importer initializes VSC active terminal powers at zero, so `loss0` and `loss1` do not alter the regression solution. GridDyn retains the slope as link loss metadata. | Add cases and equations for nonzero active VSC control and affine losses when the intended RAW/PSS/E semantics are established. | +| Active/reactive ratings | Ratings are imported for reporting/violation checks, not imposed as feasibility constraints. | Add solver-enforced apparent-power and terminal power limits. | +| Multi-terminal DC | `BEGIN MULTI-TERMINAL DC LINE DATA` remains unsupported and is skipped. | Implement a separate multi-terminal topology/model; do not flatten it into independent two-terminal transfers. | +| Dynamics | The compatibility link is for AC power flow only. | Validate physical LCC/VSC controls and trajectories against PSS/E or CIGRE benchmark cases using GridDyn's native DC classes. | +| RAW-version coverage | Field handling is based on PowerModels' importer and representative RAW records, not a complete version-specific PSS/E data-format audit. | Audit against the licensed PSS/E RAW manual for each advertised RAW version, including defaults and all control modes. | ## Validation procedure @@ -70,6 +70,6 @@ solve_ac_pf("psse_dc_components.raw", Ipopt.Optimizer) ``` Compare bus voltage magnitude/angle, active generation, active DC terminal -flows, and the total reactive DC terminal injection at each bus. Individual +flows, and the total reactive DC terminal injection at each bus. Individual reactive flows at a bus with multiple DC terminals should only be compared after choosing an explicit allocation convention. diff --git a/src/fileInput/gridDynReadRAW.cpp b/src/fileInput/gridDynReadRAW.cpp index 98bebe83d..e158b61ab 100644 --- a/src/fileInput/gridDynReadRAW.cpp +++ b/src/fileInput/gridDynReadRAW.cpp @@ -15,7 +15,6 @@ #include "griddyn/GridDynSimulation.h" #include "griddyn/Link.h" #include "griddyn/Load.h" -#include "griddyn/primary/AcBus.h" #include "griddyn/links/AcLine.h" #include "griddyn/links/AdjustableTransformer.h" #include "griddyn/links/RawDcLine.h" @@ -686,21 +685,21 @@ static double rawDcField(const stringVector& record, size_t index); * and converter models. */ static links::RawDcLine* addRawDcCompatibilityLink(CoreObject* parentObject, - GridBus* fromBus, - GridBus* toBus, - index_t sequence, - double scheduledPower, - double lossFraction, - double rating, - bool enabled, - double fromVoltageTarget, - double toVoltageTarget, - bool controlFromVoltage, - bool controlToVoltage, - const std::string& description) + GridBus* fromBus, + GridBus* toBus, + index_t sequence, + double scheduledPower, + double lossFraction, + double rating, + bool enabled, + double fromVoltageTarget, + double toVoltageTarget, + bool controlFromVoltage, + bool controlToVoltage, + const std::string& description) { - auto* link = new links::RawDcLine(parentObject->getName() + "_psse_dc_" + - std::to_string(sequence)); + auto* link = + new links::RawDcLine(parentObject->getName() + "_psse_dc_" + std::to_string(sequence)); link->setDescription(description); link->updateBus(fromBus, 1); link->updateBus(toBus, 2); @@ -802,11 +801,10 @@ static void rawReadTwoTerminalDc(CoreObject* parentObject, toVoltageTarget, rawDcUseVoltageControl(fromBus, fromBusNumber, voltageControlledBuses), rawDcUseVoltageControl(toBus, toBusNumber, voltageControlledBuses), - "PSS/E RAW two-terminal DC compatibility import; name='" + name + "', MDC=" + - std::to_string(mdc) + ", RDC=" + std::to_string(resistance) + ", SETVL=" + - std::to_string(setvl) + ", VSCHD=" + std::to_string(vschd) + ", qminf=" + - std::to_string(fromQmin) + ", qmaxf=0, qmint=" + std::to_string(toQmin) + - ", qmaxt=0"); + "PSS/E RAW two-terminal DC compatibility import; name='" + name + + "', MDC=" + std::to_string(mdc) + ", RDC=" + std::to_string(resistance) + + ", SETVL=" + std::to_string(setvl) + ", VSCHD=" + std::to_string(vschd) + ", qminf=" + + std::to_string(fromQmin) + ", qmaxf=0, qmint=" + std::to_string(toQmin) + ", qmaxt=0"); } static double rawDcField(const stringVector& record, size_t index) @@ -856,21 +854,16 @@ static void rawReadVscDc(CoreObject* parentObject, const auto toType = numeric_conversion(toConverter[1], 0); const auto name = std::string(trim(removeQuotes(header[0]))); const auto resistance = numeric_conversion(header[2], 0.0); - const auto loss0 = - (rawDcField(fromConverter, 5) + rawDcField(toConverter, 5) + rawDcField(fromConverter, 7) + - rawDcField(toConverter, 7)) * + const auto loss0 = (rawDcField(fromConverter, 5) + rawDcField(toConverter, 5) + + rawDcField(fromConverter, 7) + rawDcField(toConverter, 7)) * 1e-3; const auto loss1 = (rawDcField(fromConverter, 6) + rawDcField(toConverter, 6)) * 1e-3; const auto rating = std::max(rawVscTransferLimit(fromConverter), rawVscTransferLimit(toConverter)); const auto fromVoltageTarget = - (rawDcField(fromConverter, 2) == 1.0) ? - rawDcField(fromConverter, 4) : - 1.0; + (rawDcField(fromConverter, 2) == 1.0) ? rawDcField(fromConverter, 4) : 1.0; const auto toVoltageTarget = - (rawDcField(toConverter, 2) == 1.0) ? - rawDcField(toConverter, 4) : - 1.0; + (rawDcField(toConverter, 2) == 1.0) ? rawDcField(toConverter, 4) : 1.0; // PowerModels initializes VSC RAW dclines at zero active and reactive flow. // Retain its loss and limit data in the description while preserving that @@ -889,13 +882,13 @@ static void rawReadVscDc(CoreObject* parentObject, toVoltageTarget, rawDcUseVoltageControl(fromBus, fromBusNumber, voltageControlledBuses), rawDcUseVoltageControl(toBus, toBusNumber, voltageControlledBuses), - "PSS/E RAW VSC DC compatibility import; name='" + name + "', MDC=" + - std::to_string(mdc) + ", RDC=" + std::to_string(resistance) + ", loss0=" + - std::to_string(loss0) + ", loss1=" + std::to_string(loss1) + ", qminf=" + - std::to_string(rawDcField(fromConverter, 12)) + ", qmaxf=" + - std::to_string(rawDcField(fromConverter, 11)) + ", qmint=" + - std::to_string(rawDcField(toConverter, 12)) + ", qmaxt=" + - std::to_string(rawDcField(toConverter, 11))); + "PSS/E RAW VSC DC compatibility import; name='" + name + "', MDC=" + std::to_string(mdc) + + ", RDC=" + std::to_string(resistance) + ", loss0=" + std::to_string(loss0) + + ", loss1=" + std::to_string(loss1) + + ", qminf=" + std::to_string(rawDcField(fromConverter, 12)) + + ", qmaxf=" + std::to_string(rawDcField(fromConverter, 11)) + + ", qmint=" + std::to_string(rawDcField(toConverter, 12)) + + ", qmaxt=" + std::to_string(rawDcField(toConverter, 11))); } static int getPSSversion(const std::string& line) diff --git a/src/griddyn/links/RawDcLine.cpp b/src/griddyn/links/RawDcLine.cpp index ddba49499..65fb89936 100644 --- a/src/griddyn/links/RawDcLine.cpp +++ b/src/griddyn/links/RawDcLine.cpp @@ -8,7 +8,6 @@ #include "../GridBus.h" #include "utilities/MatrixDataCompact.hpp" - #include namespace griddyn::links { @@ -86,8 +85,8 @@ StateSizes RawDcLine::localStateSizes(const SolverMode& sMode) const { StateSizes sizes; if (hasAlgebraic(sMode) && isConnected()) { - sizes.algSize = static_cast(controlFromVoltage) + - static_cast(controlToVoltage); + sizes.algSize = + static_cast(controlFromVoltage) + static_cast(controlToVoltage); } return sizes; } diff --git a/src/griddyn/links/RawDcLine.h b/src/griddyn/links/RawDcLine.h index df7fef96c..33fae0b66 100644 --- a/src/griddyn/links/RawDcLine.h +++ b/src/griddyn/links/RawDcLine.h @@ -40,11 +40,8 @@ class RawDcLine final: public Link { public: explicit RawDcLine(const std::string& objName = "rawdcline_$"); - void set(std::string_view param, - double val, - units::unit unitType = units::defunit) override; - double get(std::string_view param, - units::unit unitType = units::defunit) const override; + void set(std::string_view param, double val, units::unit unitType = units::defunit) override; + double get(std::string_view param, units::unit unitType = units::defunit) const override; void pFlowObjectInitializeA(CoreTime time0, std::uint32_t flags) override; StateSizes localStateSizes(const SolverMode& sMode) const override; From a86f462115239e2a08cb3f9df0562a379f945fc0 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sun, 30 Aug 2026 16:08:27 -0700 Subject: [PATCH 4/4] clang tidy and cpplint fixes --- src/fileInput/gridDynReadRAW.cpp | 94 ++++++++++++++++---------------- src/griddyn/links/RawDcLine.cpp | 1 + src/griddyn/links/RawDcLine.h | 1 + 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/fileInput/gridDynReadRAW.cpp b/src/fileInput/gridDynReadRAW.cpp index 98bebe83d..28fcb8384 100644 --- a/src/fileInput/gridDynReadRAW.cpp +++ b/src/fileInput/gridDynReadRAW.cpp @@ -330,6 +330,50 @@ static GridBus* findBus(std::vector& busList, const std::string& line) return busList[index]; } +static void readRawBusSection(CoreObject* parentObject, + std::ifstream& file, + std::string& line, + std::vector& busList, + BasicReaderInfo& opt) +{ + while (checkNextLine(file, line)) { + const auto pos = line.find_first_of(','); + const auto index = numeric_conversion(line.substr(0, pos), 0); + + if (std::cmp_greater_equal(index, busList.size())) { + if (index < 100000000) { + busList.resize((2 * index) + 1, nullptr); + } else { + std::cerr << "Bus index overload " << index << '\n'; + } + } + if (busList[index] == nullptr) { + busList[index] = gBusfactory->makeTypeObject(); + busList[index]->set("basepower", opt.base); + busList[index]->setUserID(index); + + rawReadBus(busList[index], line, opt); + auto* tobj = parentObject->find(busList[index]->getName()); + if (tobj == nullptr) { + parentObject->add(busList[index]); + } else { + const auto prevName = busList[index]->getName(); + busList[index]->setName(prevName + '_' + + std::to_string(busList[index]->getInt("basevoltage"))); + try { + parentObject->add(busList[index]); + } + catch (const ObjectAddFailure&) { + busList[index]->setName(prevName); + addToParentWithRename(busList[index], parentObject); + } + } + } else { + std::cerr << "Invalid bus code " << index << '\n'; + } + } +} + void loadRaw(CoreObject* parentObject, const std::string& fileName, const BasicReaderInfo& readerOptions) @@ -344,7 +388,6 @@ void loadRaw(CoreObject* parentObject, GridLoad* loadObject; Generator* gen; GridBus* bus; - index_t index; size_t pos; /*load up the factories*/ @@ -416,51 +459,8 @@ void loadRaw(CoreObject* parentObject, temp1 = temp1 + '\n' + line; // set the case description parentObject->setDescription(temp1); - // get the bus data section - // bus data doesn't have a header but it is always first - bool moreData = true; - while (moreData) { - if (checkNextLine(file, line)) { - // get the index - pos = line.find_first_of(','); - temp1 = line.substr(0, pos); - index = numeric_conversion(temp1, 0); - - if (std::cmp_greater_equal(index, busList.size())) { - if (index < 100000000) { - busList.resize((2 * index) + 1, nullptr); - } else { - std::cerr << "Bus index overload " << index << '\n'; - } - } - if (busList[index] == nullptr) { - busList[index] = gBusfactory->makeTypeObject(); - busList[index]->set("basepower", opt.base); - busList[index]->setUserID(index); - - rawReadBus(busList[index], line, opt); - auto* tobj = parentObject->find(busList[index]->getName()); - if (tobj == nullptr) { - parentObject->add(busList[index]); - } else { - auto prevName = busList[index]->getName(); - busList[index]->setName(prevName + '_' + - std::to_string(busList[index]->getInt("basevoltage"))); - try { - parentObject->add(busList[index]); - } - catch (const ObjectAddFailure&) { - busList[index]->setName(prevName); - addToParentWithRename(busList[index], parentObject); - } - } - } else { - std::cerr << "Invalid bus code " << index << '\n'; - } - } else { - moreData = false; - } - } + // Bus data does not have a header but is always the first section. + readRawBusSection(parentObject, file, line, busList, opt); stringVec txlines; txlines.resize(5); @@ -472,7 +472,7 @@ void loadRaw(CoreObject* parentObject, while (moreSections) { const SectionType currSection = findSectionType(line); - moreData = true; + bool moreData = true; switch (currSection) { case SectionType::LOAD: while (moreData) { diff --git a/src/griddyn/links/RawDcLine.cpp b/src/griddyn/links/RawDcLine.cpp index ddba49499..a654f01e6 100644 --- a/src/griddyn/links/RawDcLine.cpp +++ b/src/griddyn/links/RawDcLine.cpp @@ -10,6 +10,7 @@ #include "utilities/MatrixDataCompact.hpp" #include +#include namespace griddyn::links { using units::convert; diff --git a/src/griddyn/links/RawDcLine.h b/src/griddyn/links/RawDcLine.h index df7fef96c..2c2c4146e 100644 --- a/src/griddyn/links/RawDcLine.h +++ b/src/griddyn/links/RawDcLine.h @@ -7,6 +7,7 @@ #pragma once #include "../Link.h" +#include namespace griddyn::links {