diff --git a/.gitmodules b/.gitmodules index 68bbbba7f..5ec1ebf92 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "third-party/nlohmann-json"] path = third-party/nlohmann-json url = https://github.com/nlohmann/json +[submodule "third-party/magic-enum"] + path = third-party/magic-enum + url = https://github.com/Neargye/magic_enum diff --git a/CMakeLists.txt b/CMakeLists.txt index 11acceb4a..dfd0f3f7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(PACKAGE_VERSION_PATCH "0") set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}") -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) if (MSVC) set(CMAKE_CXX_FLAGS "/Wall") @@ -83,7 +83,6 @@ endif("${isSystemDir}" STREQUAL "-1") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third-party/nlohmann-json/single_include) option(GRIDKIT_BUILD_SHARED "Build shared libraries" ON) option(GRIDKIT_BUILD_STATIC "Build static libraries" OFF) @@ -108,6 +107,13 @@ if(${GRIDKIT_ENABLE_ENZYME}) add_definitions(-DGRIDKIT_ENABLE_ENZYME) endif() +# add third-party libraries + +set(JSON_BuildTests OFF CACHE INTERNAL "") + +add_subdirectory(third-party/nlohmann-json) +add_subdirectory(third-party/magic-enum) + # Macro that adds libraries include(GridkitAddLibrary) diff --git a/examples/PhasorDynamics/Tiny/ThreeBus/Basic/ThreeBusBasic.cpp b/examples/PhasorDynamics/Tiny/ThreeBus/Basic/ThreeBusBasic.cpp index be2d1287f..7725c0cfd 100644 --- a/examples/PhasorDynamics/Tiny/ThreeBus/Basic/ThreeBusBasic.cpp +++ b/examples/PhasorDynamics/Tiny/ThreeBus/Basic/ThreeBusBasic.cpp @@ -83,6 +83,7 @@ int main() { using namespace GridKit::PhasorDynamics; using namespace AnalysisManager::Sundials; + using BusType = BusData::BusType; auto error_allowed = static_cast(1e-4); @@ -99,19 +100,19 @@ int main() // Bus 0 data.bus[0].bus_id = 0; - data.bus[0].bus_type = BusData::BusType::SLACK; + data.bus[0].bus_type = BusType::SLACK; data.bus[0].Vr0 = 1.06; data.bus[0].Vi0 = 0.0; // Bus 1 data.bus[1].bus_id = 1; - data.bus[1].bus_type = BusData::BusType::DEFAULT; + data.bus[1].bus_type = BusType::DEFAULT; data.bus[1].Vr0 = 1.0599558398065716; data.bus[1].Vi0 = -0.009675621941024773; // Bus 2 data.bus[2].bus_id = 2; - data.bus[2].bus_type = BusData::BusType::DEFAULT; + data.bus[2].bus_type = BusType::DEFAULT; data.bus[2].Vr0 = 0.9610827543495831; data.bus[2].Vi0 = -0.13122476630506485; @@ -119,89 +120,89 @@ int main() data.branch.resize(3); // Branch 0-1 - data.branch[0].bus1_id = data.bus[0].bus_id; - data.branch[0].bus2_id = data.bus[1].bus_id; - data.branch[0].R = 0.05; - data.branch[0].X = 0.21; - data.branch[0].G = 0; - data.branch[0].B = 0.1; + data.branch[0].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[0].ports[BranchPorts::bus2] = data.bus[1].bus_id; + data.branch[0].parameters[BranchParameters::R] = 0.05; + data.branch[0].parameters[BranchParameters::X] = 0.21; + data.branch[0].parameters[BranchParameters::G] = 0.; + data.branch[0].parameters[BranchParameters::B] = 0.1; // Branch 0-2 - data.branch[1].bus1_id = data.bus[0].bus_id; - data.branch[1].bus2_id = data.bus[2].bus_id; - data.branch[1].R = 0.06; - data.branch[1].X = 0.15; - data.branch[1].G = 0; - data.branch[1].B = 0.12; + data.branch[1].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[1].ports[BranchPorts::bus2] = data.bus[2].bus_id; + data.branch[1].parameters[BranchParameters::R] = 0.06; + data.branch[1].parameters[BranchParameters::X] = 0.15; + data.branch[1].parameters[BranchParameters::G] = 0.; + data.branch[1].parameters[BranchParameters::B] = 0.12; // Branch 1-2 - data.branch[2].bus1_id = data.bus[1].bus_id; - data.branch[2].bus2_id = data.bus[2].bus_id; - data.branch[2].R = 0.08; - data.branch[2].X = 0.27; - data.branch[2].G = 0; - data.branch[2].B = 0.45; + data.branch[2].ports[BranchPorts::bus1] = data.bus[1].bus_id; + data.branch[2].ports[BranchPorts::bus2] = data.bus[2].bus_id; + data.branch[2].parameters[BranchParameters::R] = 0.08; + data.branch[2].parameters[BranchParameters::X] = 0.27; + data.branch[2].parameters[BranchParameters::G] = 0.; + data.branch[2].parameters[BranchParameters::B] = 0.45; // Set generator data data.genrou.resize(2); // Generator on bus 1 - data.genrou[0].bus_id = 1; - data.genrou[0].p0 = 0.5; - data.genrou[0].q0 = -0.07588; - data.genrou[0].H = 2.7; - data.genrou[0].D = 0.; - data.genrou[0].Ra = 0.; - data.genrou[0].Tdop = 7.; - data.genrou[0].Tdopp = .04; - data.genrou[0].Tqopp = .05; - data.genrou[0].Tqop = .75; - data.genrou[0].Xd = 1.9; - data.genrou[0].Xdp = 0.17; - data.genrou[0].Xdpp = 0.15; - data.genrou[0].Xq = 0.4; - data.genrou[0].Xqp = 0.35; - data.genrou[0].Xqpp = 0.15; - data.genrou[0].Xl = 0.14999; - data.genrou[0].S10 = 0.; - data.genrou[0].S12 = 0.; + data.genrou[0].ports[GenrouPorts::bus] = 1; + data.genrou[0].parameters[GenrouParameters::p0] = 0.5; + data.genrou[0].parameters[GenrouParameters::q0] = -0.07588; + data.genrou[0].parameters[GenrouParameters::H] = 2.7; + data.genrou[0].parameters[GenrouParameters::D] = 0.; + data.genrou[0].parameters[GenrouParameters::Ra] = 0.; + data.genrou[0].parameters[GenrouParameters::Tdop] = 7.; + data.genrou[0].parameters[GenrouParameters::Tdopp] = .04; + data.genrou[0].parameters[GenrouParameters::Tqopp] = .05; + data.genrou[0].parameters[GenrouParameters::Tqop] = .75; + data.genrou[0].parameters[GenrouParameters::Xd] = 1.9; + data.genrou[0].parameters[GenrouParameters::Xdp] = 0.17; + data.genrou[0].parameters[GenrouParameters::Xdpp] = 0.15; + data.genrou[0].parameters[GenrouParameters::Xq] = 0.4; + data.genrou[0].parameters[GenrouParameters::Xqp] = 0.35; + data.genrou[0].parameters[GenrouParameters::Xqpp] = 0.15; + data.genrou[0].parameters[GenrouParameters::Xl] = 0.14999; + data.genrou[0].parameters[GenrouParameters::S10] = 0.; + data.genrou[0].parameters[GenrouParameters::S12] = 0.; // Generator on bus 2 - data.genrou[1].bus_id = 2; - data.genrou[1].p0 = 0.25; - data.genrou[1].q0 = 0.26587; - data.genrou[1].H = 1.6; - data.genrou[1].D = 0.; - data.genrou[1].Ra = 0.; - data.genrou[1].Tdop = 7.5; - data.genrou[1].Tdopp = .04; - data.genrou[1].Tqopp = .05; - data.genrou[1].Tqop = .75; - data.genrou[1].Xd = 2.3; - data.genrou[1].Xdp = 0.2; - data.genrou[1].Xdpp = 0.18; - data.genrou[1].Xq = 0.5; - data.genrou[1].Xqp = 0.5; - data.genrou[1].Xqpp = 0.18; - data.genrou[1].Xl = 0.15; - data.genrou[1].S10 = 0.; - data.genrou[1].S12 = 0.; + data.genrou[1].ports[GenrouPorts::bus] = 2; + data.genrou[1].parameters[GenrouParameters::p0] = 0.25; + data.genrou[1].parameters[GenrouParameters::q0] = 0.26587; + data.genrou[1].parameters[GenrouParameters::H] = 1.6; + data.genrou[1].parameters[GenrouParameters::D] = 0.; + data.genrou[1].parameters[GenrouParameters::Ra] = 0.; + data.genrou[1].parameters[GenrouParameters::Tdop] = 7.5; + data.genrou[1].parameters[GenrouParameters::Tdopp] = .04; + data.genrou[1].parameters[GenrouParameters::Tqopp] = .05; + data.genrou[1].parameters[GenrouParameters::Tqop] = .75; + data.genrou[1].parameters[GenrouParameters::Xd] = 2.3; + data.genrou[1].parameters[GenrouParameters::Xdp] = 0.2; + data.genrou[1].parameters[GenrouParameters::Xdpp] = 0.18; + data.genrou[1].parameters[GenrouParameters::Xq] = 0.5; + data.genrou[1].parameters[GenrouParameters::Xqp] = 0.5; + data.genrou[1].parameters[GenrouParameters::Xqpp] = 0.18; + data.genrou[1].parameters[GenrouParameters::Xl] = 0.15; + data.genrou[1].parameters[GenrouParameters::S10] = 0.; + data.genrou[1].parameters[GenrouParameters::S12] = 0.; // Set load data data.load.resize(1); // Load on bus 2 - data.load[0].bus_id = 2; - data.load[0].R = 0.4447197839297772; - data.load[0].X = 0.20330047265361242; + data.load[0].ports[LoadPorts::bus] = 2; + data.load[0].parameters[LoadParameters::R] = 0.4447197839297772; + data.load[0].parameters[LoadParameters::X] = 0.20330047265361242; // Set fault data data.bus_fault.resize(1); - data.bus_fault[0].bus_id = 2; - data.bus_fault[0].R = 0.0; - data.bus_fault[0].X = 1e-5; - data.bus_fault[0].status = false; + data.bus_fault[0].ports[BusFaultPorts::bus] = 2; + data.bus_fault[0].parameters[BusFaultParameters::R] = 0.0; + data.bus_fault[0].parameters[BusFaultParameters::X] = 1e-5; + data.bus_fault[0].parameters[BusFaultParameters::state0] = false; // // Instantiate system diff --git a/examples/PhasorDynamics/Tiny/ThreeBus/Classical/ThreeBusClassical.cpp b/examples/PhasorDynamics/Tiny/ThreeBus/Classical/ThreeBusClassical.cpp index ffa6a1560..78f4dc92f 100644 --- a/examples/PhasorDynamics/Tiny/ThreeBus/Classical/ThreeBusClassical.cpp +++ b/examples/PhasorDynamics/Tiny/ThreeBus/Classical/ThreeBusClassical.cpp @@ -35,6 +35,8 @@ using scalar_type = double; using real_type = double; using index_type = size_t; +using BusType = GridKit::PhasorDynamics::BusData::BusType; + struct OutputData { real_type t; @@ -99,13 +101,13 @@ int main() // Bus 0 data.bus[0].bus_id = 0; - data.bus[0].bus_type = BusData::BusType::SLACK; + data.bus[0].bus_type = BusType::SLACK; data.bus[0].Vr0 = 1.06; data.bus[0].Vi0 = 0.0; // Bus 1 data.bus[1].bus_id = 1; - data.bus[1].bus_type = BusData::BusType::DEFAULT; + data.bus[1].bus_type = BusType::DEFAULT; data.bus[1].Vr0 = 1.0599558398065716; data.bus[1].Vi0 = -0.009675621941024773; @@ -119,65 +121,65 @@ int main() data.branch.resize(3); // Branch 0-1 - data.branch[0].bus1_id = data.bus[0].bus_id; - data.branch[0].bus2_id = data.bus[1].bus_id; - data.branch[0].R = 0.05; - data.branch[0].X = 0.21; - data.branch[0].G = 0; - data.branch[0].B = 0.1; + data.branch[0].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[0].ports[BranchPorts::bus2] = data.bus[1].bus_id; + data.branch[0].parameters[BranchParameters::R] = 0.05; + data.branch[0].parameters[BranchParameters::X] = 0.21; + data.branch[0].parameters[BranchParameters::G] = 0.0; + data.branch[0].parameters[BranchParameters::B] = 0.1; // Branch 0-2 - data.branch[1].bus1_id = data.bus[0].bus_id; - data.branch[1].bus2_id = data.bus[2].bus_id; - data.branch[1].R = 0.06; - data.branch[1].X = 0.15; - data.branch[1].G = 0; - data.branch[1].B = 0.12; + data.branch[1].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[1].ports[BranchPorts::bus2] = data.bus[2].bus_id; + data.branch[1].parameters[BranchParameters::R] = 0.06; + data.branch[1].parameters[BranchParameters::X] = 0.15; + data.branch[1].parameters[BranchParameters::G] = 0.0; + data.branch[1].parameters[BranchParameters::B] = 0.12; // Branch 1-2 - data.branch[2].bus1_id = data.bus[1].bus_id; - data.branch[2].bus2_id = data.bus[2].bus_id; - data.branch[2].R = 0.08; - data.branch[2].X = 0.27; - data.branch[2].G = 0; - data.branch[2].B = 0.45; + data.branch[2].ports[BranchPorts::bus1] = data.bus[1].bus_id; + data.branch[2].ports[BranchPorts::bus2] = data.bus[2].bus_id; + data.branch[2].parameters[BranchParameters::R] = 0.08; + data.branch[2].parameters[BranchParameters::X] = 0.27; + data.branch[2].parameters[BranchParameters::G] = 0.0; + data.branch[2].parameters[BranchParameters::B] = 0.45; // Set generator data data.genclassical.resize(2); // Generator on bus 1 - data.genclassical[0].bus_id = data.bus[1].bus_id; - data.genclassical[0].p0 = 0.5; - data.genclassical[0].q0 = -0.07588; - data.genclassical[0].H = 2.7; - data.genclassical[0].D = 0.; - data.genclassical[0].Ra = 0.; - data.genclassical[0].Xdp = 0.17; + data.genclassical[0].ports[GenClassicalPorts::bus] = data.bus[1].bus_id; + data.genclassical[0].parameters[GenClassicalParameters::p0] = 0.5; + data.genclassical[0].parameters[GenClassicalParameters::q0] = -0.07588; + data.genclassical[0].parameters[GenClassicalParameters::H] = 2.7; + data.genclassical[0].parameters[GenClassicalParameters::D] = 0.; + data.genclassical[0].parameters[GenClassicalParameters::Ra] = 0.; + data.genclassical[0].parameters[GenClassicalParameters::Xdp] = 0.17; // Generator on bus 2 - data.genclassical[1].bus_id = data.bus[2].bus_id; - data.genclassical[1].p0 = 0.25; - data.genclassical[1].q0 = 0.26587; - data.genclassical[1].H = 1.6; - data.genclassical[1].D = 0.; - data.genclassical[1].Ra = 0.; - data.genclassical[1].Xdp = 0.2; + data.genclassical[1].ports[GenClassicalPorts::bus] = data.bus[2].bus_id; + data.genclassical[1].parameters[GenClassicalParameters::p0] = 0.25; + data.genclassical[1].parameters[GenClassicalParameters::q0] = 0.26587; + data.genclassical[1].parameters[GenClassicalParameters::H] = 1.6; + data.genclassical[1].parameters[GenClassicalParameters::D] = 0.; + data.genclassical[1].parameters[GenClassicalParameters::Ra] = 0.; + data.genclassical[1].parameters[GenClassicalParameters::Xdp] = 0.2; // Set load data data.load.resize(1); // Load on bus 2 - data.load[0].bus_id = 2; - data.load[0].R = 0.4447197839297772; - data.load[0].X = 0.20330047265361242; + data.load[0].ports[LoadPorts::bus] = 2; + data.load[0].parameters[LoadParameters::R] = 0.4447197839297772; + data.load[0].parameters[LoadParameters::X] = 0.20330047265361242; // Set fault data data.bus_fault.resize(1); - data.bus_fault[0].bus_id = 2; - data.bus_fault[0].R = 0.0; - data.bus_fault[0].X = 1e-5; - data.bus_fault[0].status = false; + data.bus_fault[0].ports[BusFaultPorts::bus] = 2; + data.bus_fault[0].parameters[BusFaultParameters::R] = 0.0; + data.bus_fault[0].parameters[BusFaultParameters::X] = 1e-5; + data.bus_fault[0].parameters[BusFaultParameters::state0] = false; // // Instantiate system diff --git a/examples/PhasorDynamics/Tiny/TwoBus/Basic/TwoBusBasic.cpp b/examples/PhasorDynamics/Tiny/TwoBus/Basic/TwoBusBasic.cpp index 55f660363..1d614ccfb 100644 --- a/examples/PhasorDynamics/Tiny/TwoBus/Basic/TwoBusBasic.cpp +++ b/examples/PhasorDynamics/Tiny/TwoBus/Basic/TwoBusBasic.cpp @@ -36,6 +36,8 @@ int main() using real_type = double; using index_type = size_t; + using BusType = BusData::BusType; + std::cout << "Example: TwoBusBasic\n"; // @@ -48,53 +50,53 @@ int main() data.bus.resize(2); data.bus[0].bus_id = 0; - data.bus[0].bus_type = BusData::BusType::DEFAULT; + data.bus[0].bus_type = BusType::DEFAULT; data.bus[0].Vr0 = 0.9949877346411762; data.bus[0].Vi0 = 0.09999703952427966; data.bus[1].bus_id = 1; - data.bus[1].bus_type = BusData::BusType::SLACK; + data.bus[1].bus_type = BusType::SLACK; data.bus[1].Vr0 = 1.0; data.bus[1].Vi0 = 0.0; // Set branch data data.branch.resize(1); - data.branch[0].bus1_id = data.bus[0].bus_id; - data.branch[0].bus2_id = data.bus[1].bus_id; - data.branch[0].R = 0.0; - data.branch[0].X = 0.1; - data.branch[0].G = 0.0; - data.branch[0].B = 0.0; + data.branch[0].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[0].ports[BranchPorts::bus2] = data.bus[1].bus_id; + data.branch[0].parameters[BranchParameters::R] = 0.0; + data.branch[0].parameters[BranchParameters::X] = 0.1; + data.branch[0].parameters[BranchParameters::G] = 0.0; + data.branch[0].parameters[BranchParameters::B] = 0.0; // Set generator data data.genrou.resize(1); - data.genrou[0].p0 = 1.; - data.genrou[0].q0 = 0.05013; - data.genrou[0].H = 3.; - data.genrou[0].D = 0.; - data.genrou[0].Ra = 0.; - data.genrou[0].Tdop = 7.; - data.genrou[0].Tdopp = .04; - data.genrou[0].Tqopp = .05; - data.genrou[0].Tqop = .75; - data.genrou[0].Xd = 2.1; - data.genrou[0].Xdp = 0.2; - data.genrou[0].Xdpp = 0.18; - data.genrou[0].Xq = 0.5; - data.genrou[0].Xqp = 0.5; - data.genrou[0].Xqpp = 0.18; - data.genrou[0].Xl = 0.15; - data.genrou[0].S10 = 0.; - data.genrou[0].S12 = 0.; + data.genrou[0].parameters[GenrouParameters::p0] = 1.; + data.genrou[0].parameters[GenrouParameters::q0] = 0.05013; + data.genrou[0].parameters[GenrouParameters::H] = 3.; + data.genrou[0].parameters[GenrouParameters::D] = 0.; + data.genrou[0].parameters[GenrouParameters::Ra] = 0.; + data.genrou[0].parameters[GenrouParameters::Tdop] = 7.; + data.genrou[0].parameters[GenrouParameters::Tdopp] = .04; + data.genrou[0].parameters[GenrouParameters::Tqopp] = .05; + data.genrou[0].parameters[GenrouParameters::Tqop] = .75; + data.genrou[0].parameters[GenrouParameters::Xd] = 2.1; + data.genrou[0].parameters[GenrouParameters::Xdp] = 0.2; + data.genrou[0].parameters[GenrouParameters::Xdpp] = 0.18; + data.genrou[0].parameters[GenrouParameters::Xq] = 0.5; + data.genrou[0].parameters[GenrouParameters::Xqp] = 0.5; + data.genrou[0].parameters[GenrouParameters::Xqpp] = 0.18; + data.genrou[0].parameters[GenrouParameters::Xl] = 0.15; + data.genrou[0].parameters[GenrouParameters::S10] = 0.; + data.genrou[0].parameters[GenrouParameters::S12] = 0.; // Add faults data.bus_fault.resize(1); - data.bus_fault[0].R = 0.0; - data.bus_fault[0].X = 1e-3; - data.bus_fault[0].status = false; + data.bus_fault[0].parameters[BusFaultParameters::R] = 0.0; + data.bus_fault[0].parameters[BusFaultParameters::X] = 1e-3; + data.bus_fault[0].parameters[BusFaultParameters::state0] = false; // // Instantiate system model diff --git a/examples/PhasorDynamics/Tiny/TwoBus/Tgov1/TwoBusTgov1.cpp b/examples/PhasorDynamics/Tiny/TwoBus/Tgov1/TwoBusTgov1.cpp index 177964547..eca4f0b86 100644 --- a/examples/PhasorDynamics/Tiny/TwoBus/Tgov1/TwoBusTgov1.cpp +++ b/examples/PhasorDynamics/Tiny/TwoBus/Tgov1/TwoBusTgov1.cpp @@ -38,6 +38,8 @@ int main() using real_type = double; using index_type = size_t; + using BusType = BusData::BusType; + std::cout << "Example: TwoBusTgov1 \n"; // @@ -50,31 +52,31 @@ int main() data.bus.resize(2); data.bus[0].bus_id = 0; - data.bus[0].bus_type = BusData::BusType::DEFAULT; + data.bus[0].bus_type = BusType::DEFAULT; data.bus[0].Vr0 = 0.9949877346411762; data.bus[0].Vi0 = 0.09999703952427966; data.bus[1].bus_id = 1; - data.bus[1].bus_type = BusData::BusType::SLACK; + data.bus[1].bus_type = BusType::SLACK; data.bus[1].Vr0 = 1.0; data.bus[1].Vi0 = 0.0; // Set branch data data.branch.resize(1); - data.branch[0].bus1_id = data.bus[0].bus_id; - data.branch[0].bus2_id = data.bus[1].bus_id; - data.branch[0].R = 0.0; - data.branch[0].X = 0.1; - data.branch[0].G = 0.0; - data.branch[0].B = 0.0; + data.branch[0].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[0].ports[BranchPorts::bus2] = data.bus[1].bus_id; + data.branch[0].parameters[BranchParameters::R] = 0.0; + data.branch[0].parameters[BranchParameters::X] = 0.1; + data.branch[0].parameters[BranchParameters::G] = 0.0; + data.branch[0].parameters[BranchParameters::B] = 0.0; // Add faults data.bus_fault.resize(1); - data.bus_fault[0].R = 0.0; - data.bus_fault[0].X = 1e-3; - data.bus_fault[0].status = false; + data.bus_fault[0].parameters[BusFaultParameters::R] = 0.0; + data.bus_fault[0].parameters[BusFaultParameters::X] = 1e-3; + data.bus_fault[0].parameters[BusFaultParameters::state0] = false; // // Instantiate system model @@ -85,24 +87,24 @@ int main() // Set generator data data.genrou.resize(1); - data.genrou[0].p0 = 1.; - data.genrou[0].q0 = 0.05013; - data.genrou[0].H = 3.; - data.genrou[0].D = 0.; - data.genrou[0].Ra = 0.; - data.genrou[0].Tdop = 7.; - data.genrou[0].Tdopp = .04; - data.genrou[0].Tqopp = .05; - data.genrou[0].Tqop = .75; - data.genrou[0].Xd = 2.1; - data.genrou[0].Xdp = 0.2; - data.genrou[0].Xdpp = 0.18; - data.genrou[0].Xq = 0.5; - data.genrou[0].Xqp = 0.5; - data.genrou[0].Xqpp = 0.18; - data.genrou[0].Xl = 0.15; - data.genrou[0].S10 = 0.; - data.genrou[0].S12 = 0.; + data.genrou[0].parameters[GenrouParameters::p0] = 1.; + data.genrou[0].parameters[GenrouParameters::q0] = 0.05013; + data.genrou[0].parameters[GenrouParameters::H] = 3.; + data.genrou[0].parameters[GenrouParameters::D] = 0.; + data.genrou[0].parameters[GenrouParameters::Ra] = 0.; + data.genrou[0].parameters[GenrouParameters::Tdop] = 7.; + data.genrou[0].parameters[GenrouParameters::Tdopp] = .04; + data.genrou[0].parameters[GenrouParameters::Tqopp] = .05; + data.genrou[0].parameters[GenrouParameters::Tqop] = .75; + data.genrou[0].parameters[GenrouParameters::Xd] = 2.1; + data.genrou[0].parameters[GenrouParameters::Xdp] = 0.2; + data.genrou[0].parameters[GenrouParameters::Xdpp] = 0.18; + data.genrou[0].parameters[GenrouParameters::Xq] = 0.5; + data.genrou[0].parameters[GenrouParameters::Xqp] = 0.5; + data.genrou[0].parameters[GenrouParameters::Xqpp] = 0.18; + data.genrou[0].parameters[GenrouParameters::Xl] = 0.15; + data.genrou[0].parameters[GenrouParameters::S10] = 0.; + data.genrou[0].parameters[GenrouParameters::S12] = 0.; data.gov.resize(1); diff --git a/src/Model/PhasorDynamics/Branch/Branch.cpp b/src/Model/PhasorDynamics/Branch/Branch.cpp index d679b76eb..e38753a73 100644 --- a/src/Model/PhasorDynamics/Branch/Branch.cpp +++ b/src/Model/PhasorDynamics/Branch/Branch.cpp @@ -1,170 +1,194 @@ -/** - * @file Branch.hpp - * @author Slaven Peles (peless@ornl.gov) - * @brief Definition of a phasor dynamics branch model. - * - * The model uses Cartesian coordinates. - * - */ - -#include "Branch.hpp" - -#include -#include - -#include -#include - -namespace GridKit -{ - namespace PhasorDynamics - { - /*! - * @brief Constructor for a pi-model branch - * - * Model size: - * - Number of equations = 0 - * - Number of internal variables = 0 - */ - template - Branch::Branch(bus_type* bus1, bus_type* bus2) - : bus1_(bus1), - bus2_(bus2), - R_(0.0), - X_(0.01), - G_(0.0), - B_(0.0), - bus1_id_(0), - bus2_id_(0) - { - size_ = 0; - } - - /** - * @brief Construct a new Branch - * - * @tparam ScalarT - scalar type - * @tparam IdxT - matrix/vector index type - * @param bus1 - pointer to bus-1 - * @param bus2 - pointer to bus-2 - * @param R - line series resistance - * @param X - line series reactance - * @param G - line shunt conductance - * @param B - line shunt charging - */ - template - Branch::Branch(bus_type* bus1, - bus_type* bus2, - real_type R, - real_type X, - real_type G, - real_type B) - : bus1_(bus1), - bus2_(bus2), - R_(R), - X_(X), - G_(G), - B_(B), - bus1_id_(0), - bus2_id_(0) - { - } - - template - Branch::Branch(bus_type* bus1, bus_type* bus2, const model_data_type& data) - : bus1_(bus1), - bus2_(bus2), - R_(data.R), - X_(data.X), - G_(data.G), - B_(data.B), - bus1_id_(data.bus1_id), - bus2_id_(data.bus2_id) - { - size_ = 0; - } - - /** - * @brief Destroy the Branch - * - * @tparam ScalarT - * @tparam IdxT - */ - template - Branch::~Branch() - { - // std::cout << "Destroy Branch..." << std::endl; - } - - /*! - * @brief allocate method computes sparsity pattern of the Jacobian. - */ - template - int Branch::allocate() - { - // std::cout << "Allocate Branch..." << std::endl; - return 0; - } - - /** - * Initialization of the branch model - * - */ - template - int Branch::initialize() - { - return 0; - } - - /** - * \brief Identify differential variables. - */ - template - int Branch::tagDifferentiable() - { - return 0; - } - - /** - * \brief Residual contribution of the branch is pushed to the - * two terminal buses. - * - */ - template - int Branch::evaluateResidual() - { - // std::cout << "Evaluating branch residual ...\n"; - real_type b = -X_ / (R_ * R_ + X_ * X_); - real_type g = R_ / (R_ * R_ + X_ * X_); - - Ir1() += -(g + 0.5 * G_) * Vr1() + (b + 0.5 * B_) * Vi1() + g * Vr2() - b * Vi2(); - Ii1() += -(b + 0.5 * B_) * Vr1() - (g + 0.5 * G_) * Vi1() + b * Vr2() + g * Vi2(); - Ir2() += g * Vr1() - b * Vi1() - (g + 0.5 * G_) * Vr2() + (b + 0.5 * B_) * Vi2(); - Ii2() += b * Vr1() + g * Vi1() - (b + 0.5 * B_) * Vr2() - (g + 0.5 * G_) * Vi2(); - - return 0; - } - - /** - * @brief Jacobian evaluation not implemented yet - * - * @tparam ScalarT - scalar data type - * @tparam IdxT - matrix index data type - * @return int - error code, 0 = success - */ - template - int Branch::evaluateJacobian() - { - std::cout << "Evaluate Jacobian for Branch..." << std::endl; - std::cout << "Jacobian evaluation not implemented!" << std::endl; - return 0; - } - - // Available template instantiations - template class Branch; - template class Branch; - template class Branch; - template class Branch; - - } // namespace PhasorDynamics -} // namespace GridKit +/** + * @file Branch.hpp + * @author Slaven Peles (peless@ornl.gov) + * @brief Definition of a phasor dynamics branch model. + * + * The model uses Cartesian coordinates. + * + */ + +#include "Branch.hpp" + +#include +#include + +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Constructor for a pi-model branch + * + * Model size: + * - Number of equations = 0 + * - Number of internal variables = 0 + */ + template + Branch::Branch(bus_type* bus1, bus_type* bus2) + : bus1_(bus1), + bus2_(bus2), + R_(0.0), + X_(0.01), + G_(0.0), + B_(0.0), + bus1_id_(0), + bus2_id_(0) + { + size_ = 0; + } + + /** + * @brief Construct a new Branch + * + * @tparam ScalarT - scalar type + * @tparam IdxT - matrix/vector index type + * @param bus1 - pointer to bus-1 + * @param bus2 - pointer to bus-2 + * @param R - line series resistance + * @param X - line series reactance + * @param G - line shunt conductance + * @param B - line shunt charging + */ + template + Branch::Branch(bus_type* bus1, + bus_type* bus2, + real_type R, + real_type X, + real_type G, + real_type B) + : bus1_(bus1), + bus2_(bus2), + R_(R), + X_(X), + G_(G), + B_(B), + bus1_id_(0), + bus2_id_(0) + { + } + + template + Branch::Branch(bus_type* bus1, bus_type* bus2, const model_data_type& data) + : bus1_(bus1), + bus2_(bus2) + { + if (data.parameters.contains(model_data_type::Parameters::R)) + { + R_ = std::get(data.parameters.at(model_data_type::Parameters::R)); + } + + if (data.parameters.contains(model_data_type::Parameters::X)) + { + X_ = std::get(data.parameters.at(model_data_type::Parameters::X)); + } + + if (data.parameters.contains(model_data_type::Parameters::G)) + { + G_ = std::get(data.parameters.at(model_data_type::Parameters::G)); + } + + if (data.parameters.contains(model_data_type::Parameters::B)) + { + B_ = std::get(data.parameters.at(model_data_type::Parameters::B)); + } + + if (data.ports.contains(model_data_type::Ports::bus1)) + { + bus1_id_ = data.ports.at(model_data_type::Ports::bus1); + } + + if (data.ports.contains(model_data_type::Ports::bus2)) + { + bus2_id_ = data.ports.at(model_data_type::Ports::bus2); + } + + size_ = 0; + } + + /** + * @brief Destroy the Branch + * + * @tparam ScalarT + * @tparam IdxT + */ + template + Branch::~Branch() + { + // std::cout << "Destroy Branch..." << std::endl; + } + + /*! + * @brief allocate method computes sparsity pattern of the Jacobian. + */ + template + int Branch::allocate() + { + // std::cout << "Allocate Branch..." << std::endl; + return 0; + } + + /** + * Initialization of the branch model + * + */ + template + int Branch::initialize() + { + return 0; + } + + /** + * \brief Identify differential variables. + */ + template + int Branch::tagDifferentiable() + { + return 0; + } + + /** + * \brief Residual contribution of the branch is pushed to the + * two terminal buses. + * + */ + template + int Branch::evaluateResidual() + { + // std::cout << "Evaluating branch residual ...\n"; + real_type b = -X_ / (R_ * R_ + X_ * X_); + real_type g = R_ / (R_ * R_ + X_ * X_); + + Ir1() += -(g + 0.5 * G_) * Vr1() + (b + 0.5 * B_) * Vi1() + g * Vr2() - b * Vi2(); + Ii1() += -(b + 0.5 * B_) * Vr1() - (g + 0.5 * G_) * Vi1() + b * Vr2() + g * Vi2(); + Ir2() += g * Vr1() - b * Vi1() - (g + 0.5 * G_) * Vr2() + (b + 0.5 * B_) * Vi2(); + Ii2() += b * Vr1() + g * Vi1() - (b + 0.5 * B_) * Vr2() - (g + 0.5 * G_) * Vi2(); + + return 0; + } + + /** + * @brief Jacobian evaluation not implemented yet + * + * @tparam ScalarT - scalar data type + * @tparam IdxT - matrix index data type + * @return int - error code, 0 = success + */ + template + int Branch::evaluateJacobian() + { + std::cout << "Evaluate Jacobian for Branch..." << std::endl; + std::cout << "Jacobian evaluation not implemented!" << std::endl; + return 0; + } + + // Available template instantiations + template class Branch; + template class Branch; + template class Branch; + template class Branch; + + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/Branch/Branch.hpp b/src/Model/PhasorDynamics/Branch/Branch.hpp index 3cd577f45..5be4657c6 100644 --- a/src/Model/PhasorDynamics/Branch/Branch.hpp +++ b/src/Model/PhasorDynamics/Branch/Branch.hpp @@ -1,144 +1,144 @@ -/** - * @file Branch.hpp - * @author Slaven Peles (peless@ornl.gov) - * @brief Declaration of a phasor dynamics branch model. - * - * The model uses Cartesian coordinates. - * - */ -#pragma once - -#include - -// Forward declarations. -namespace GridKit -{ - namespace PhasorDynamics - { - template - class BusBase; - - template - struct BranchData; - } // namespace PhasorDynamics -} // namespace GridKit - -namespace GridKit -{ - namespace PhasorDynamics - { - /** - * @brief Implementation of a pi-model branch between two buses. - * - * The model is implemented in Cartesian coordinates. Positive current - * direction is into the busses. - * - */ - template - class Branch : public Component - { - using Component::size_; - using Component::nnz_; - using Component::time_; - using Component::alpha_; - using Component::y_; - using Component::yp_; - using Component::tag_; - using Component::f_; - - using real_type = typename Component::real_type; - using bus_type = BusBase; - using model_data_type = BranchData; - - public: - Branch(bus_type* bus1, bus_type* bus2); - Branch(bus_type* bus1, bus_type* bus2, real_type R, real_type X, real_type G, real_type B); - Branch(bus_type* bus1, bus_type* bus2, const model_data_type& data); - virtual ~Branch(); - - virtual int allocate() override; - virtual int initialize() override; - virtual int tagDifferentiable() override; - virtual int evaluateResidual() override; - virtual int evaluateJacobian() override; - - virtual void updateTime(real_type /* t */, real_type /* a */) override - { - } - - public: - void setR(real_type R) - { - R_ = R; - } - - void setX(real_type X) - { - // std::cout << "Setting X ...\n"; - X_ = X; - } - - void setG(real_type G) - { - G_ = G; - } - - void setB(real_type B) - { - B_ = B; - } - - private: - ScalarT& Vr1() - { - return bus1_->Vr(); - } - - ScalarT& Vi1() - { - return bus1_->Vi(); - } - - ScalarT& Ir1() - { - return bus1_->Ir(); - } - - ScalarT& Ii1() - { - return bus1_->Ii(); - } - - ScalarT& Vr2() - { - return bus2_->Vr(); - } - - ScalarT& Vi2() - { - return bus2_->Vi(); - } - - ScalarT& Ir2() - { - return bus2_->Ir(); - } - - ScalarT& Ii2() - { - return bus2_->Ii(); - } - - private: - bus_type* bus1_; - bus_type* bus2_; - real_type R_; - real_type X_; - real_type G_; - real_type B_; - const IdxT bus1_id_; - const IdxT bus2_id_; - }; - - } // namespace PhasorDynamics -} // namespace GridKit +/** + * @file Branch.hpp + * @author Slaven Peles (peless@ornl.gov) + * @brief Declaration of a phasor dynamics branch model. + * + * The model uses Cartesian coordinates. + * + */ +#pragma once + +#include + +// Forward declarations. +namespace GridKit +{ + namespace PhasorDynamics + { + template + class BusBase; + + template + struct BranchData; + } // namespace PhasorDynamics +} // namespace GridKit + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Implementation of a pi-model branch between two buses. + * + * The model is implemented in Cartesian coordinates. Positive current + * direction is into the busses. + * + */ + template + class Branch : public Component + { + using Component::size_; + using Component::nnz_; + using Component::time_; + using Component::alpha_; + using Component::y_; + using Component::yp_; + using Component::tag_; + using Component::f_; + + using real_type = typename Component::real_type; + using bus_type = BusBase; + using model_data_type = BranchData; + + public: + Branch(bus_type* bus1, bus_type* bus2); + Branch(bus_type* bus1, bus_type* bus2, real_type R, real_type X, real_type G, real_type B); + Branch(bus_type* bus1, bus_type* bus2, const model_data_type& data); + virtual ~Branch(); + + virtual int allocate() override; + virtual int initialize() override; + virtual int tagDifferentiable() override; + virtual int evaluateResidual() override; + virtual int evaluateJacobian() override; + + virtual void updateTime(real_type /* t */, real_type /* a */) override + { + } + + public: + void setR(real_type R) + { + R_ = R; + } + + void setX(real_type X) + { + // std::cout << "Setting X ...\n"; + X_ = X; + } + + void setG(real_type G) + { + G_ = G; + } + + void setB(real_type B) + { + B_ = B; + } + + private: + ScalarT& Vr1() + { + return bus1_->Vr(); + } + + ScalarT& Vi1() + { + return bus1_->Vi(); + } + + ScalarT& Ir1() + { + return bus1_->Ir(); + } + + ScalarT& Ii1() + { + return bus1_->Ii(); + } + + ScalarT& Vr2() + { + return bus2_->Vr(); + } + + ScalarT& Vi2() + { + return bus2_->Vi(); + } + + ScalarT& Ir2() + { + return bus2_->Ir(); + } + + ScalarT& Ii2() + { + return bus2_->Ii(); + } + + private: + bus_type* bus1_; + bus_type* bus2_; + real_type R_{0.0}; + real_type X_{0.0}; + real_type G_{0.0}; + real_type B_{0.0}; + IdxT bus1_id_{0}; + IdxT bus2_id_{0}; + }; + + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/Branch/BranchData.hpp b/src/Model/PhasorDynamics/Branch/BranchData.hpp index 2d6a1d21a..c7851e290 100644 --- a/src/Model/PhasorDynamics/Branch/BranchData.hpp +++ b/src/Model/PhasorDynamics/Branch/BranchData.hpp @@ -6,18 +6,42 @@ */ #pragma once -#include -#include -#include -#include - -#include +#include namespace GridKit { namespace PhasorDynamics { - using json = nlohmann::json; + /// Initial parameters for a branch + enum class BranchParameters + { + R, ///< Line series resistance + X, ///< Line series reactance + G, ///< Line shunt conductance + B, ///< Line shunt charging + }; + + /// Ports for a branch + enum class BranchPorts + { + bus1, ///< Unique ID of bus 1 + bus2, ///< Unique ID of bus 2 + }; + + /// Variables able to be monitored for a branch + enum class BranchMonitorableVariables + { + ir1, + ii1, + im1, + p1, + q1, + ir2, + ii2, + im2, + p2, + q2, + }; /** * @brief Contains modeling data for a Branch @@ -26,166 +50,19 @@ namespace GridKit * @tparam IdxT Integer parameter data type * * Integer parameters are of the same type as matrix and vector indices. - * - * @todo Decide on naming scheme for model parameters. */ template - struct BranchData + struct BranchData : public ComponentData { - RealT R{0.0}; ///< Line series resistance - RealT X{0.0}; ///< Line series reactance - RealT G{0.0}; ///< Line shunt conductance - RealT B{0.0}; ///< Line shunt charging - - IdxT bus1_id{0}; ///< Unique ID of bus 1 - IdxT bus2_id{0}; ///< Unique ID of bus 2 + BranchData() = default; - std::optional freq_base; ///< Override for the system-wide base frequency - std::optional va_base; ///< Override for the system-wide power base - - std::string disambiguation_string; ///< Disambiguation string for this device - - /// Indices of the variables able to be monitored in the bitset - enum class MonitorableVariables : size_t - { - IR1, - II1, - IM1, - P1, - Q1, - IR2, - II2, - IM2, - P2, - Q2, - MAXIMUM, - }; - - /// Set indicating the variables being monitored - std::bitset>(MonitorableVariables::MAXIMUM)> - monitored_variables; + using Parameters = BranchParameters; + using Ports = BranchPorts; + using MonitorableVariables = BranchMonitorableVariables; }; - - /// JSON parser function implementation for the `BranchData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, BranchData& bd) - { - for (auto& raw_parameter : j.at("params").items()) - { - if (raw_parameter.key() == "R") - { - raw_parameter.value().get_to(bd.R); - } - else if (raw_parameter.key() == "X") - { - raw_parameter.value().get_to(bd.X); - } - else if (raw_parameter.key() == "G") - { - raw_parameter.value().get_to(bd.G); - } - else if (raw_parameter.key() == "B") - { - raw_parameter.value().get_to(bd.B); - } - else - { - throw "Invalid initial parameter"; - } - } - - for (auto& raw_port : j.at("ports").items()) - { - if (raw_port.key() == "bus1") - { - raw_port.value().get_to(bd.bus1_id); - } - else if (raw_port.key() == "bus2") - { - raw_port.value().get_to(bd.bus2_id); - } - else - { - throw "Invalid port mapping"; - } - } - - if (j.contains("freq_base")) - { - j.at("freq_base").get_to(bd.freq_base); - } - - if (j.contains("va_base")) - { - j.at("va_base").get_to(bd.va_base); - } - - j.at("id").get_to(bd.disambiguation_string); - - if (j.contains("mon")) - { - for (auto& raw_monitored_variable : j.at("mon")) - { - auto monitored = raw_monitored_variable.get(); - if (monitored == "ir1") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::IR1)); - } - else if (monitored == "ii1") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::II1)); - } - else if (monitored == "im1") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::IM1)); - } - else if (monitored == "p1") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::P1)); - } - else if (monitored == "q1") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::Q1)); - } - else if (monitored == "ir2") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::IR2)); - } - else if (monitored == "ii2") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::II2)); - } - else if (monitored == "im2") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::IM2)); - } - else if (monitored == "p2") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::P2)); - } - else if (monitored == "q2") - { - bd.monitored_variables.set(static_cast( - BranchData::MonitorableVariables::Q2)); - } - else - { - throw "Invalid monitored variable"; - } - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/Bus/BusData.hpp b/src/Model/PhasorDynamics/Bus/BusData.hpp index fb06b19d0..3a1ab6f88 100644 --- a/src/Model/PhasorDynamics/Bus/BusData.hpp +++ b/src/Model/PhasorDynamics/Bus/BusData.hpp @@ -11,14 +11,10 @@ #include #include -#include - namespace GridKit { namespace PhasorDynamics { - using json = nlohmann::json; - /** * @brief Contains modeling data for a Bus * @@ -68,93 +64,5 @@ namespace GridKit std::underlying_type_t>(MonitorableVariables::MAXIMUM)> monitored_variables; }; - - /// JSON parser function implementation for the `BusData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, BusData& bd) - { - j.at("name").get_to(bd.name); - - if (j.contains("init")) - { - for (auto& raw_parameter : j.at("init").items()) - { - if (raw_parameter.key() == "Vi") - { - raw_parameter.value().get_to(bd.Vi0); - } - else if (raw_parameter.key() == "Vr") - { - raw_parameter.value().get_to(bd.Vr0); - } - else - { - throw "Invalid initial parameter"; - } - } - } - - j.at("number").get_to(bd.bus_id); - - auto string_class = j.at("class").get(); - if (string_class == "bus") - { - bd.bus_type = BusData::BusType::DEFAULT; - } - else if (string_class == "infinite_bus") - { - bd.bus_type = BusData::BusType::SLACK; - } - else - { - throw "Invalid bus class"; - } - - j.at("v_base").get_to(bd.v_base); - - if (j.contains("freq_base")) - { - j.at("freq_base").get_to(bd.freq_base); - } - - if (j.contains("va_base")) - { - j.at("va_base").get_to(bd.va_base); - } - - if (j.contains("mon")) - { - for (auto& raw_monitored_variable : j.at("mon")) - { - auto monitored = raw_monitored_variable.get(); - if (monitored == "Vr") - { - bd.monitored_variables.set(static_cast( - BusData::MonitorableVariables::VR)); - } - else if (monitored == "Vi") - { - bd.monitored_variables.set(static_cast( - BusData::MonitorableVariables::VI)); - } - else if (monitored == "Vm") - { - bd.monitored_variables.set(static_cast( - BusData::MonitorableVariables::VM)); - } - else if (monitored == "Va") - { - bd.monitored_variables.set(static_cast( - BusData::MonitorableVariables::VA)); - } - else - { - throw "Invalid monitored variable"; - } - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/Bus/BusDataJSONParser.hpp b/src/Model/PhasorDynamics/Bus/BusDataJSONParser.hpp new file mode 100644 index 000000000..a8b66e44b --- /dev/null +++ b/src/Model/PhasorDynamics/Bus/BusDataJSONParser.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + using json = nlohmann::json; + + /// JSON parser function implementation for the `BusData` type + /// + /// See the `README.md` in `src/Model/PhasorDynamics` for more information + template + void from_json(const json& j, BusData& bd) + { + j.at("name").get_to(bd.name); + + if (j.contains("init")) + { + for (auto& raw_parameter : j.at("init").items()) + { + if (raw_parameter.key() == "Vi") + { + raw_parameter.value().get_to(bd.Vi0); + } + else if (raw_parameter.key() == "Vr") + { + raw_parameter.value().get_to(bd.Vr0); + } + else + { + throw "Invalid initial parameter"; + } + } + } + + j.at("number").get_to(bd.bus_id); + + auto string_class = j.at("class").get(); + if (string_class == "bus") + { + bd.bus_type = BusData::BusType::DEFAULT; + } + else if (string_class == "infinite_bus") + { + bd.bus_type = BusData::BusType::SLACK; + } + else + { + throw "Invalid bus class"; + } + + j.at("v_base").get_to(bd.v_base); + + if (j.contains("freq_base")) + { + j.at("freq_base").get_to(bd.freq_base); + } + + if (j.contains("va_base")) + { + j.at("va_base").get_to(bd.va_base); + } + + if (j.contains("mon")) + { + for (auto& raw_monitored_variable : j.at("mon")) + { + auto monitored = raw_monitored_variable.get(); + if (monitored == "Vr") + { + bd.monitored_variables.set(static_cast( + BusData::MonitorableVariables::VR)); + } + else if (monitored == "Vi") + { + bd.monitored_variables.set(static_cast( + BusData::MonitorableVariables::VI)); + } + else if (monitored == "Vm") + { + bd.monitored_variables.set(static_cast( + BusData::MonitorableVariables::VM)); + } + else if (monitored == "Va") + { + bd.monitored_variables.set(static_cast( + BusData::MonitorableVariables::VA)); + } + else + { + throw "Invalid monitored variable"; + } + } + } + } + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.cpp b/src/Model/PhasorDynamics/BusFault/BusFault.cpp index c14e90b0a..1eabbb862 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFault.cpp +++ b/src/Model/PhasorDynamics/BusFault/BusFault.cpp @@ -63,12 +63,28 @@ namespace GridKit */ template BusFault::BusFault(bus_type* bus, const DataT& data) - : bus_(bus), - R_(data.R), - X_(data.X), - status_(data.status), - busID_(data.bus_id) + : bus_(bus) { + if (data.parameters.contains(DataT::Parameters::R)) + { + R_ = std::get(data.parameters.at(DataT::Parameters::R)); + } + + if (data.parameters.contains(DataT::Parameters::X)) + { + X_ = std::get(data.parameters.at(DataT::Parameters::X)); + } + + if (data.parameters.contains(DataT::Parameters::state0)) + { + status_ = std::get(data.parameters.at(DataT::Parameters::state0)); + } + + if (data.ports.contains(DataT::Ports::bus)) + { + busID_ = data.ports.at(DataT::Ports::bus); + } + size_ = 0; } diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.hpp b/src/Model/PhasorDynamics/BusFault/BusFault.hpp index 9902d33c6..11ec32fb0 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFault.hpp +++ b/src/Model/PhasorDynamics/BusFault/BusFault.hpp @@ -88,11 +88,11 @@ namespace GridKit } private: - bus_type* bus_; - real_type R_; - real_type X_; - bool status_; - const IdxT busID_; + bus_type* bus_; + real_type R_{0.0}; + real_type X_{0.0}; + bool status_{false}; + IdxT busID_{0}; }; } // namespace PhasorDynamics diff --git a/src/Model/PhasorDynamics/BusFault/BusFaultData.hpp b/src/Model/PhasorDynamics/BusFault/BusFaultData.hpp index 43503fb45..ed7447ff1 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFaultData.hpp +++ b/src/Model/PhasorDynamics/BusFault/BusFaultData.hpp @@ -6,17 +6,35 @@ */ #pragma once -#include -#include -#include -#include - -#include +#include namespace GridKit { namespace PhasorDynamics { + /// Initial parameters for a bus fault + enum class BusFaultParameters + { + state0, ///< Whether or not the fault has happened + R, ///< Short to ground resistance + X, ///< Short to ground reactance + }; + + /// Ports supported for a bus fault + enum class BusFaultPorts + { + bus, ///< Unique ID of the bus where the fault occurs + control_signal, ///< Unique ID of the bus providing a control signal + }; + + /// Variables able to be monitored for a bus fault + enum class BusFaultMonitorableVariables + { + state, + ir, + ii, + }; + /** * @brief Contains modeling data for a short-to-ground fault * @@ -24,119 +42,19 @@ namespace GridKit * @tparam IdxT Integer parameter data type * * Integer parameters are of the same type as matrix and vector indices. - * - * @todo Decide on naming scheme for model parameters. */ template - struct BusFaultData + struct BusFaultData : public ComponentData { - RealT R{0.0}; ///< Short to ground resistance - RealT X{0.0}; ///< Short to ground reactance - bool status{false}; ///< If the fault has happened - - std::optional control_signal; ///< Unique ID of the bus providing a control signal - IdxT bus_id{0}; ///< Unique ID of the bus where the fault occurs + BusFaultData() = default; - std::optional freq_base; ///< Override for the system-wide base frequency - std::optional va_base; ///< Override for the system-wide power base - - std::string disambiguation_string; ///< Disambiguation string for this device - - /// Indices of the variables able to be monitored in the bitset - enum class MonitorableVariables : size_t - { - STATE, - IR, - II, - MAXIMUM, - }; - - /// Set of variables being monitored - std::bitset>(MonitorableVariables::MAXIMUM)> - monitored_variables; + using Parameters = BusFaultParameters; + using Ports = BusFaultPorts; + using MonitorableVariables = BusFaultMonitorableVariables; }; - - /// JSON parser function implementation for the `BusFaultData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, BusFaultData& bf) - { - for (auto& raw_parameter : j.at("params").items()) - { - if (raw_parameter.key() == "R") - { - raw_parameter.value().get_to(bf.R); - } - else if (raw_parameter.key() == "X") - { - raw_parameter.value().get_to(bf.X); - } - else if (raw_parameter.key() == "state0") - { - raw_parameter.value().get_to(bf.status); - } - else - { - throw "Invalid initial parameter"; - } - } - - for (auto& raw_port : j.at("ports").items()) - { - if (raw_port.key() == "bus") - { - raw_port.value().get_to(bf.bus_id); - } - else if (raw_port.key() == "control_signal") - { - raw_port.value().get_to(bf.control_signal); - } - else - { - throw "Invalid port mapping"; - } - } - - if (j.contains("freq_base")) - { - j.at("freq_base").get_to(bf.freq_base); - } - - if (j.contains("va_base")) - { - j.at("va_base").get_to(bf.va_base); - } - - j.at("id").get_to(bf.disambiguation_string); - - if (j.contains("mon")) - { - for (auto& raw_monitored_variable : j.at("mon")) - { - auto monitored = raw_monitored_variable.get(); - if (monitored == "state") - { - bf.monitored_variables.set(static_cast( - BusFaultData::MonitorableVariables::STATE)); - } - else if (monitored == "ir") - { - bf.monitored_variables.set(static_cast( - BusFaultData::MonitorableVariables::IR)); - } - else if (monitored == "ii") - { - bf.monitored_variables.set(static_cast( - BusFaultData::MonitorableVariables::II)); - } - else - { - throw "Invalid monitored variable"; - } - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/ComponentData.hpp b/src/Model/PhasorDynamics/ComponentData.hpp new file mode 100644 index 000000000..36e89de9a --- /dev/null +++ b/src/Model/PhasorDynamics/ComponentData.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Unified interface for `Component` data containers + * + * @tparam RealT Real parameter data type + * @tparam IdxT Integer parameter data type + */ + template + requires std::is_enum_v + && std::is_enum_v + && std::is_enum_v + struct ComponentData + { + /// Class of device this is for + std::string device_class; + + /// Mapping of parameters to parameter values + std::map> parameters; + + /// Mapping of ports to port values + std::map ports; + + /// Set of variables being monitored + std::set monitored_variables; + + std::optional freq_base; ///< Override for the system-wide base frequency + std::optional va_base; ///< Override for the system-wide power base + + std::string disambiguation_string; ///< Disambiguation string for this device + + protected: + ComponentData() = default; + }; + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/ComponentDataJSONParser.hpp b/src/Model/PhasorDynamics/ComponentDataJSONParser.hpp new file mode 100644 index 000000000..4a80676c4 --- /dev/null +++ b/src/Model/PhasorDynamics/ComponentDataJSONParser.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + using json = nlohmann::json; + + /// JSON parser function for the `ComponentData` class and descendants + template + requires std::is_enum_v + && std::is_enum_v + && std::is_enum_v + void from_json(const json& j, ComponentData& c) + { + j.at("class").get_to(c.device_class); + + for (auto& raw_parameter : j.at("params").items()) + { + auto key = magic_enum::enum_cast(raw_parameter.key()); + if (key.has_value()) + { + // NOTE: this is necessary because it doesn't seem like nlohmann/json handles std::variant + // out of the box + if (raw_parameter.value().is_boolean()) + { + c.parameters[key.value()] = raw_parameter.value().template get(); + } + else if (raw_parameter.value().is_number_float()) + { + c.parameters[key.value()] = raw_parameter.value().template get(); + } + else if (raw_parameter.value().is_number_integer()) + { + c.parameters[key.value()] = raw_parameter.value().template get(); + } + else + { + throw "Invalid initial parameter"; + } + } + else + { + throw "Invalid initial parameter"; + } + } + + for (auto& raw_port : j.at("ports").items()) + { + auto key = magic_enum::enum_cast(raw_port.key()); + if (key.has_value()) + { + raw_port.value().get_to(c.ports[key.value()]); + } + else + { + throw "Invalid port mapping"; + } + } + + if (j.contains("freq_base")) + { + j.at("freq_base").get_to(c.freq_base); + } + + if (j.contains("va_base")) + { + j.at("va_base").get_to(c.va_base); + } + + j.at("id").get_to(c.disambiguation_string); + + if (j.contains("mon")) + { + for (auto& raw_monitored_variable : j.at("mon")) + { + auto monitored = magic_enum::enum_cast(raw_monitored_variable.get()); + if (monitored.has_value()) + { + c.monitored_variables.insert(monitored.value()); + } + else + { + throw "Invalid monitored variable"; + } + } + } + } + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/INPUT_FORMAT.md b/src/Model/PhasorDynamics/INPUT_FORMAT.md index 775364d69..905fa983f 100644 --- a/src/Model/PhasorDynamics/INPUT_FORMAT.md +++ b/src/Model/PhasorDynamics/INPUT_FORMAT.md @@ -129,18 +129,18 @@ connected to a constant value. This list is subject to change. "case_name": "Two-bus test case 1", "case_description": "A two-bus test case for demonstrating the dynamics format", "case_comments": "This case is set up to monitor the voltage at both buses and the machine angle and speed", - "freq_base": 60, + "freq_base": 60.0, "va_base": 100e6 }, "buses": [ { "number": 1, "class": "bus", "name": "Bus 1", "init": {"Vr":0.994988, "Vi":0.099997}, "v_base": 115e3, "mon": ["Vr", "Vi"] }, - { "number": 2, "class": "infinite_bus", "name": "Bus 2", "init": {"Vr":1, "Vi":0}, "v_base": 115e3 } + { "number": 2, "class": "infinite_bus", "name": "Bus 2", "init": {"Vr":1.0, "Vi":0.0}, "v_base": 115e3 } ], "devices": [ - { "class": "branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0, "X":0.1, "G":0, "B":0} }, - { "class": "GENROU", "ports": {"bus":1}, "id": "1", "params": {"p0":1, "q0":0.05013, "H":3, "D":0, "Ra":0, "Tdop":7, "Tdopp":0.04, "Tqopp":0.05, - "Tqop":0.75, "Xd":2.1, "Xdp":0.2, "Xdpp":0.18, "Xq":0.5, "Xqp": 0, "Xqpp":0.18, "Xl":0.15, "S10":0, "S12":0}, "mon": ["delta", "omega"] }, - { "class": "bus_fault", "ports": {"bus":1}, "id": "1", "params": {"state0": false, "R":0, "X":1e-3} } + { "class": "branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0.0, "X":0.1, "G":0.0, "B":0.0} }, + { "class": "GENROU", "ports": {"bus":1}, "id": "1", "params": {"p0":1.0, "q0":0.05013, "H":3.0, "D":0.0, "Ra":0.0, "Tdop":7.0, "Tdopp":0.04, "Tqopp":0.05, + "Tqop":0.75, "Xd":2.1, "Xdp":0.2, "Xdpp":0.18, "Xq":0.5, "Xqp": 0.0, "Xqpp":0.18, "Xl":0.15, "S10":0.0, "S12":0.0}, "mon": ["delta", "omega"] }, + { "class": "bus_fault", "ports": {"bus":1}, "id": "1", "params": {"state0": false, "R":0.0, "X":1e-3} } ] } ``` diff --git a/src/Model/PhasorDynamics/Load/Load.hpp b/src/Model/PhasorDynamics/Load/Load.hpp index 53ae88985..337da51af 100644 --- a/src/Model/PhasorDynamics/Load/Load.hpp +++ b/src/Model/PhasorDynamics/Load/Load.hpp @@ -1,106 +1,104 @@ - - -#pragma once - -#include - -// Forward declarations. -namespace GridKit -{ - namespace PhasorDynamics - { - template - class BusBase; - - template - struct LoadData; - } // namespace PhasorDynamics -} // namespace GridKit - -namespace GridKit -{ - namespace PhasorDynamics - { - /*! - * @brief Implementation of a constant load. - * - */ - template - class Load : public Component - { - using Component::size_; - using Component::nnz_; - using Component::time_; - using Component::alpha_; - using Component::y_; - using Component::yp_; - using Component::tag_; - using Component::f_; - using Component::J_; - using Component::component_id_; - - using real_type = typename Component::real_type; - using bus_type = BusBase; - using model_data_type = LoadData; - - public: - Load(bus_type* bus); - Load(bus_type* bus, real_type R, real_type X); - Load(bus_type* bus, IdxT component_id); - Load(bus_type* bus, const model_data_type& data); - virtual ~Load(); - - virtual int allocate() override; - virtual int initialize() override; - virtual int tagDifferentiable() override; - virtual int evaluateResidual() override; - virtual int evaluateJacobian() override; - - virtual void updateTime(real_type /* t */, real_type /* a */) override - { - } - - public: - void setR(real_type R) - { - R_ = R; - } - - void setX(real_type X) - { - // std::cout << "Setting X ...\n"; - X_ = X; - } - - private: - ScalarT& Vr() - { - return bus_->Vr(); - } - - ScalarT& Vi() - { - return bus_->Vi(); - } - - ScalarT& Ir() - { - return bus_->Ir(); - } - - ScalarT& Ii() - { - return bus_->Ii(); - } - - public: - int evaluateResidualLocally(ScalarT*, ScalarT*); - - private: - bus_type* bus_{nullptr}; - real_type R_{0.1}; - real_type X_{0.01}; - }; - - } // namespace PhasorDynamics -} // namespace GridKit +#pragma once + +#include + +// Forward declarations. +namespace GridKit +{ + namespace PhasorDynamics + { + template + class BusBase; + + template + struct LoadData; + } // namespace PhasorDynamics +} // namespace GridKit + +namespace GridKit +{ + namespace PhasorDynamics + { + /*! + * @brief Implementation of a constant load. + * + */ + template + class Load : public Component + { + using Component::size_; + using Component::nnz_; + using Component::time_; + using Component::alpha_; + using Component::y_; + using Component::yp_; + using Component::tag_; + using Component::f_; + using Component::J_; + using Component::component_id_; + + using real_type = typename Component::real_type; + using bus_type = BusBase; + using model_data_type = LoadData; + + public: + Load(bus_type* bus); + Load(bus_type* bus, real_type R, real_type X); + Load(bus_type* bus, IdxT component_id); + Load(bus_type* bus, const model_data_type& data); + virtual ~Load(); + + virtual int allocate() override; + virtual int initialize() override; + virtual int tagDifferentiable() override; + virtual int evaluateResidual() override; + virtual int evaluateJacobian() override; + + virtual void updateTime(real_type /* t */, real_type /* a */) override + { + } + + public: + void setR(real_type R) + { + R_ = R; + } + + void setX(real_type X) + { + // std::cout << "Setting X ...\n"; + X_ = X; + } + + private: + ScalarT& Vr() + { + return bus_->Vr(); + } + + ScalarT& Vi() + { + return bus_->Vi(); + } + + ScalarT& Ir() + { + return bus_->Ir(); + } + + ScalarT& Ii() + { + return bus_->Ii(); + } + + public: + int evaluateResidualLocally(ScalarT*, ScalarT*); + + private: + bus_type* bus_{nullptr}; + real_type R_{0.1}; + real_type X_{0.01}; + }; + + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/Load/LoadData.hpp b/src/Model/PhasorDynamics/Load/LoadData.hpp index d5c0d3ebf..dbd439455 100644 --- a/src/Model/PhasorDynamics/Load/LoadData.hpp +++ b/src/Model/PhasorDynamics/Load/LoadData.hpp @@ -6,37 +6,51 @@ */ #pragma once -#include -#include +#include namespace GridKit { namespace PhasorDynamics { + /// Initial parameters for a load + enum class LoadParameters + { + R, ///< Load resistance + X, ///< Load reactance + }; + + /// Ports for a load + enum class LoadPorts + { + bus, ///< Unique ID of the bus to which the load is connected + }; + + /// Variables able to be monitored for a load + enum class LoadMonitorableVariables + { + // TODO: presumably some variables would make sense to monitor here + }; + /** - * @brief Contains modeling data for a Load + * @brief Contains modeling data for a load * * @tparam RealT Real parameter data type * @tparam IdxT Integer parameter data type * * Integer parameters are of the same type as matrix and vector indices. - * - * @todo Decide on naming scheme for model parameters. */ template - struct LoadData + struct LoadData : public ComponentData { - RealT R{0.0}; ///< Load resistance - RealT X{0.0}; ///< Load reactance - - IdxT bus_id{0}; ///< Unique ID of bus to which the load is connnected - - std::optional freq_base; ///< Override for the system-wide base frequency - std::optional va_base; ///< Override for the system-wide power base - - std::string disambiguation_string; ///< Disambiguation string for this device + LoadData() = default; - // TODO: add the monitorable variables to this + using Parameters = LoadParameters; + using Ports = LoadPorts; + using MonitorableVariables = LoadMonitorableVariables; }; } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/Load/LoadImpl.hpp b/src/Model/PhasorDynamics/Load/LoadImpl.hpp index 37958eea6..6cb57edfb 100644 --- a/src/Model/PhasorDynamics/Load/LoadImpl.hpp +++ b/src/Model/PhasorDynamics/Load/LoadImpl.hpp @@ -1,125 +1,129 @@ - -#include -#include - -#include "Load.hpp" -#include -#include - -namespace GridKit -{ - namespace PhasorDynamics - { - /*! - * @brief Constructor for a pi-model load - * - * System sizes: - * - Number of equations = 0 - * - Number of independent variables = 0 - */ - - template - Load::Load(bus_type* bus) - : bus_(bus) - { - size_ = 0; - } - - template - Load::Load(bus_type* bus, - real_type R, - real_type X) - : bus_(bus), - R_(R), - X_(X) - { - } - - template - Load::Load(bus_type* bus, - const model_data_type& data) - : bus_(bus), - R_(data.R), - X_(data.X) - { - } - - template - Load::Load(bus_type* bus, IdxT component_id) - : bus_(bus) - { - size_ = 0; - component_id_ = component_id; - } - - template - Load::~Load() - { - // std::cout << "Destroy Load..." << std::endl; - } - - /*! - * @brief allocate method computes sparsity pattern of the Jacobian. - */ - template - int Load::allocate() - { - // std::cout << "Allocate Load..." << std::endl; - return 0; - } - - /** - * Initialization of the load model - * - */ - template - int Load::initialize() - { - return 0; - } - - /** - * \brief Identify differential variables. - */ - template - int Load::tagDifferentiable() - { - return 0; - } - - /** - * @brief Residual contribution computed locally - * - */ - template - int Load::evaluateResidualLocally(ScalarT* y, ScalarT* f) - { - real_type b = -X_ / (R_ * R_ + X_ * X_); - real_type g = R_ / (R_ * R_ + X_ * X_); - - f[0] = -g * y[0] + b * y[1]; - f[1] = -b * y[0] - g * y[1]; - - return 0; - } - - /** - * @brief Residual contribution of the load is pushed to the bus. - * - */ - template - int Load::evaluateResidual() - { - std::vector y(2); - std::vector f(2); - y[0] = Vr(); - y[1] = Vi(); - evaluateResidualLocally(y.data(), f.data()); - Ir() += f[0]; - Ii() += f[1]; - - return 0; - } - - } // namespace PhasorDynamics -} // namespace GridKit +#include +#include + +#include "Load.hpp" +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Constructor for a pi-model load + * + * System sizes: + * - Number of equations = 0 + * - Number of independent variables = 0 + */ + template + Load::Load(bus_type* bus) + : bus_(bus) + { + size_ = 0; + } + + template + Load::Load(bus_type* bus, + real_type R, + real_type X) + : bus_(bus), + R_(R), + X_(X) + { + } + + template + Load::Load(bus_type* bus, + const model_data_type& data) + : bus_(bus) + { + if (data.parameters.contains(model_data_type::Parameters::R)) + { + R_ = std::get(data.parameters.at(model_data_type::Parameters::R)); + } + + if (data.parameters.contains(model_data_type::Parameters::X)) + { + X_ = std::get(data.parameters.at(model_data_type::Parameters::X)); + } + } + + template + Load::Load(bus_type* bus, IdxT component_id) + : bus_(bus) + { + size_ = 0; + component_id_ = component_id; + } + + template + Load::~Load() + { + // std::cout << "Destroy Load..." << std::endl; + } + + /*! + * @brief allocate method computes sparsity pattern of the Jacobian. + */ + template + int Load::allocate() + { + // std::cout << "Allocate Load..." << std::endl; + return 0; + } + + /** + * Initialization of the load model + * + */ + template + int Load::initialize() + { + return 0; + } + + /** + * \brief Identify differential variables. + */ + template + int Load::tagDifferentiable() + { + return 0; + } + + /** + * @brief Residual contribution computed locally + * + */ + template + int Load::evaluateResidualLocally(ScalarT* y, ScalarT* f) + { + real_type b = -X_ / (R_ * R_ + X_ * X_); + real_type g = R_ / (R_ * R_ + X_ * X_); + + f[0] = -g * y[0] + b * y[1]; + f[1] = -b * y[0] - g * y[1]; + + return 0; + } + + /** + * @brief Residual contribution of the load is pushed to the bus. + * + */ + template + int Load::evaluateResidual() + { + std::vector y(2); + std::vector f(2); + y[0] = Vr(); + y[1] = Vi(); + evaluateResidualLocally(y.data(), f.data()); + Ir() += f[0]; + Ii() += f[1]; + + return 0; + } + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.cpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.cpp index 3bc8891e4..4d7ceb54a 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.cpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.cpp @@ -23,8 +23,8 @@ namespace GridKit { namespace PhasorDynamics { - /*! - * @brief Constructor for a pi-model branch + /** + * @brief Constructor for a GENROU generator model with saturation * * Arguments passed to ModelEvaluatorImpl: * - Number of equations = 0 @@ -59,15 +59,10 @@ namespace GridKit { size_ = 20; setDerivedParams(); - - // Temporary, to eliminate compiler warnings - (void) busID_; - (void) unit_id_; } - /*! - * @brief Constructor for a pi-model branch - * + /** + * @brief Constructor for a GENROU generator model with saturation */ template Genrou::Genrou(bus_type* bus, @@ -117,35 +112,110 @@ namespace GridKit setDerivedParams(); } - /*! - * @brief Constructor for the GENROU generator with saturation. - * + /** + * @brief Constructor for a GENROU generator model with saturation */ template Genrou::Genrou(bus_type* bus, const model_data_type& data) : bus_(bus), - busID_(0), unit_id_(1), - gov_(nullptr), // <- TODO: Temporary, to be removed. - p0_(data.p0), - q0_(data.q0), - H_(data.H), - D_(data.D), - Ra_(data.Ra), - Tdop_(data.Tdop), - Tdopp_(data.Tdopp), - Tqopp_(data.Tqopp), - Tqop_(data.Tqop), - Xd_(data.Xd), - Xdp_(data.Xdp), - Xdpp_(data.Xdpp), - Xq_(data.Xq), - Xqp_(data.Xqp), - Xqpp_(data.Xqpp), - Xl_(data.Xl), - S10_(data.S10), - S12_(data.S12) + gov_(nullptr) // <- TODO: Temporary, to be removed. { + if (data.parameters.contains(model_data_type::Parameters::p0)) + { + p0_ = std::get(data.parameters.at(model_data_type::Parameters::p0)); + } + + if (data.parameters.contains(model_data_type::Parameters::q0)) + { + q0_ = std::get(data.parameters.at(model_data_type::Parameters::q0)); + } + + if (data.parameters.contains(model_data_type::Parameters::H)) + { + H_ = std::get(data.parameters.at(model_data_type::Parameters::H)); + } + + if (data.parameters.contains(model_data_type::Parameters::D)) + { + D_ = std::get(data.parameters.at(model_data_type::Parameters::D)); + } + + if (data.parameters.contains(model_data_type::Parameters::Ra)) + { + Ra_ = std::get(data.parameters.at(model_data_type::Parameters::Ra)); + } + + if (data.parameters.contains(model_data_type::Parameters::Tdop)) + { + Tdop_ = std::get(data.parameters.at(model_data_type::Parameters::Tdop)); + } + + if (data.parameters.contains(model_data_type::Parameters::Tdopp)) + { + Tdopp_ = std::get(data.parameters.at(model_data_type::Parameters::Tdopp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Tqopp)) + { + Tqopp_ = std::get(data.parameters.at(model_data_type::Parameters::Tqopp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Tqop)) + { + Tqop_ = std::get(data.parameters.at(model_data_type::Parameters::Tqop)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xd)) + { + Xd_ = std::get(data.parameters.at(model_data_type::Parameters::Xd)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xdp)) + { + Xdp_ = std::get(data.parameters.at(model_data_type::Parameters::Xdp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xdpp)) + { + Xdpp_ = std::get(data.parameters.at(model_data_type::Parameters::Xdpp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xq)) + { + Xq_ = std::get(data.parameters.at(model_data_type::Parameters::Xq)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xqp)) + { + Xqp_ = std::get(data.parameters.at(model_data_type::Parameters::Xqp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xqpp)) + { + Xqpp_ = std::get(data.parameters.at(model_data_type::Parameters::Xqpp)); + } + + if (data.parameters.contains(model_data_type::Parameters::Xl)) + { + Xl_ = std::get(data.parameters.at(model_data_type::Parameters::Xl)); + } + + if (data.parameters.contains(model_data_type::Parameters::S10)) + { + S10_ = std::get(data.parameters.at(model_data_type::Parameters::S10)); + } + + if (data.parameters.contains(model_data_type::Parameters::S12)) + { + S12_ = std::get(data.parameters.at(model_data_type::Parameters::S12)); + } + + if (data.ports.contains(model_data_type::Ports::bus)) + { + busID_ = data.ports.at(model_data_type::Ports::bus); + } + size_ = 20; setDerivedParams(); } diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.hpp index ffeed60af..7b8675550 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/Genrou.hpp @@ -119,33 +119,33 @@ namespace GridKit private: /* Identification */ bus_type* bus_; - const int busID_; - IdxT unit_id_; + IdxT busID_{0}; + IdxT unit_id_; //< @todo this should be removed // Governor Pointer gov_type* gov_; /* Initial terminal conditions */ - ScalarT p0_; - ScalarT q0_; + ScalarT p0_{0.0}; + ScalarT q0_{0.0}; /* Input parameters */ - real_type H_; - real_type D_; - real_type Ra_; - real_type Tdop_; - real_type Tdopp_; - real_type Tqopp_; - real_type Tqop_; - real_type Xd_; - real_type Xdp_; - real_type Xdpp_; - real_type Xq_; - real_type Xqp_; - real_type Xqpp_; - real_type Xl_; - real_type S10_; - real_type S12_; + real_type H_{0.0}; + real_type D_{0.0}; + real_type Ra_{0.0}; + real_type Tdop_{0.0}; + real_type Tdopp_{0.0}; + real_type Tqopp_{0.0}; + real_type Tqop_{0.0}; + real_type Xd_{0.0}; + real_type Xdp_{0.0}; + real_type Xdpp_{0.0}; + real_type Xq_{0.0}; + real_type Xqp_{0.0}; + real_type Xqpp_{0.0}; + real_type Xl_{0.0}; + real_type S10_{0.0}; + real_type S12_{0.0}; /* Derivied parameters */ real_type SA_; diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GenrouData.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GenrouData.hpp index f7836f87f..8e70c77c8 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GenrouData.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GenrouData.hpp @@ -6,17 +6,54 @@ */ #pragma once -#include -#include -#include -#include - -#include +#include namespace GridKit { namespace PhasorDynamics { + /// Initial parameters for a Genrou generator model + enum class GenrouParameters + { + p0, ///< Initial active power + q0, ///< Initial reactive power + H, ///< Rotor inertia + D, ///< Damping coefficient + Ra, ///< Winding resistance + Tdop, ///< Open circuit direct axis transient time + Tdopp, ///< Open circuit direct axis sub-transient time + Tqop, ///< Open circuit quadrature axis transient + Tqopp, ///< Open circuit quadrature axis sub-transient time + Xd, ///< Direct axis synchronous reactance + Xdp, ///< Direct axis transient reactance + Xdpp, ///< Direct axis sub-transient reactance + Xq, ///< Quadrature axis synchronous reactance + Xqp, ///< Quadrature axis transient reactance + Xqpp, ///< Quadrature axis sub-transient reactance + Xl, ///< Stator leakage reactance + S10, ///< Saturation factor at 1.0 pu flux + S12, ///< Saturation factor at 1.2 pu flux + }; + + /// Ports for a Genrou generator model + enum class GenrouPorts + { + bus, ///< Unique ID of the connecting bus + exciter_signal, ///< Unique ID of the bus providing the exciter signal + governor_signal, ///< Unique ID of the bus providing the governor signal + }; + + /// Variables able to be monitored for a Genrou generator model + enum class GenrouMonitorableVariables + { + ir, + ii, + p, + q, + delta, + omega, + }; + /** * @brief Contains modeling data for a Genrou generator model. * @@ -24,217 +61,19 @@ namespace GridKit * @tparam IdxT Integer parameter data type * * Integer parameters are of the same type as matrix and vector indices. - * - * @todo Decide on naming scheme for model parameters. */ template - struct GenrouData + struct GenrouData : public ComponentData { - RealT p0{0.0}; ///< Initial active power - RealT q0{0.0}; ///< Initial reactive power - RealT H{0.0}; ///< Rotor inertia - RealT D{0.0}; ///< Damping coefficient - RealT Ra{0.0}; ///< Winding resistance - RealT Tdop{0.0}; ///< Open circuit direct axis transient time - RealT Tdopp{0.0}; ///< Open circuit direct axis sub-transient time - RealT Tqop{0.0}; ///< Open circuit quadrature axis transient - RealT Tqopp{0.0}; ///< Open circuit quadrature axis sub-transient time - RealT Xd{0.0}; ///< Direct axis synchronous reactance - RealT Xdp{0.0}; ///< Direct axis transient reactance - RealT Xdpp{0.0}; ///< Direct axis sub-transient reactance - RealT Xq{0.0}; ///< Quadrature axis synchronous reactance - RealT Xqp{0.0}; ///< Quadrature axis transient reactance - RealT Xqpp{0.0}; ///< Quadrature axis sub-transient reactance - RealT Xl{0.0}; ///< Stator leakage reactance - RealT S10{0.0}; ///< Saturation factor at 1.0 pu flux - RealT S12{0.0}; ///< Saturation factor at 1.2 pu flux - - IdxT bus_id{0}; ///< Unique ID of the connecting bus - std::optional exciter_signal; ///< Unique ID of the bus providing the exciter signal - std::optional governor_signal; ///< Unique ID of the bus providing the governor signal + GenrouData() = default; - std::optional freq_base; ///< Override for the system-wide base frequency - std::optional va_base; ///< Override for the system-wide power base - - std::string disambiguation_string; ///< Disambiguation string for this device - - /// Indices of the variables able to be monitored in the bitset - enum class MonitorableVariables : size_t - { - IR, - II, - P, - Q, - DELTA, - OMEGA, - MAXIMUM, - }; - - /// Set of variables being monitored - std::bitset>(MonitorableVariables::MAXIMUM)> - monitored_variables; + using Parameters = GenrouParameters; + using Ports = GenrouPorts; + using MonitorableVariables = GenrouMonitorableVariables; }; - - /// JSON parser function implementation for the `GenrouData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, GenrouData& gd) - { - for (auto& raw_parameter : j.at("params").items()) - { - if (raw_parameter.key() == "p0") - { - raw_parameter.value().get_to(gd.p0); - } - else if (raw_parameter.key() == "q0") - { - raw_parameter.value().get_to(gd.q0); - } - else if (raw_parameter.key() == "H") - { - raw_parameter.value().get_to(gd.H); - } - else if (raw_parameter.key() == "D") - { - raw_parameter.value().get_to(gd.D); - } - else if (raw_parameter.key() == "Ra") - { - raw_parameter.value().get_to(gd.Ra); - } - else if (raw_parameter.key() == "Tdop") - { - raw_parameter.value().get_to(gd.Tdop); - } - else if (raw_parameter.key() == "Tdopp") - { - raw_parameter.value().get_to(gd.Tdopp); - } - else if (raw_parameter.key() == "Tqopp") - { - raw_parameter.value().get_to(gd.Tqopp); - } - else if (raw_parameter.key() == "Tqop") - { - raw_parameter.value().get_to(gd.Tqop); - } - else if (raw_parameter.key() == "Xd") - { - raw_parameter.value().get_to(gd.Xd); - } - else if (raw_parameter.key() == "Xdp") - { - raw_parameter.value().get_to(gd.Xdp); - } - else if (raw_parameter.key() == "Xdpp") - { - raw_parameter.value().get_to(gd.Xdpp); - } - else if (raw_parameter.key() == "Xq") - { - raw_parameter.value().get_to(gd.Xq); - } - else if (raw_parameter.key() == "Xqp") - { - raw_parameter.value().get_to(gd.Xqp); - } - else if (raw_parameter.key() == "Xqpp") - { - raw_parameter.value().get_to(gd.Xqpp); - } - else if (raw_parameter.key() == "Xl") - { - raw_parameter.value().get_to(gd.Xl); - } - else if (raw_parameter.key() == "S10") - { - raw_parameter.value().get_to(gd.S10); - } - else if (raw_parameter.key() == "S12") - { - raw_parameter.value().get_to(gd.S12); - } - else - { - throw "Invalid initial parameter"; - } - } - - for (auto& raw_port : j.at("ports").items()) - { - if (raw_port.key() == "bus") - { - raw_port.value().get_to(gd.bus_id); - } - else if (raw_port.key() == "governor_signal") - { - raw_port.value().get_to(gd.governor_signal); - } - else if (raw_port.key() == "exciter_signal") - { - raw_port.value().get_to(gd.exciter_signal); - } - else - { - throw "Invalid port mapping"; - } - } - - if (j.contains("freq_base")) - { - j.at("freq_base").get_to(gd.freq_base); - } - - if (j.contains("va_base")) - { - j.at("va_base").get_to(gd.va_base); - } - - j.at("id").get_to(gd.disambiguation_string); - - if (j.contains("mon")) - { - for (auto& raw_monitored_variable : j.at("mon")) - { - auto monitored = raw_monitored_variable.get(); - if (monitored == "ir") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::IR)); - } - else if (monitored == "ii") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::II)); - } - else if (monitored == "p") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::P)); - } - else if (monitored == "q") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::Q)); - } - else if (monitored == "delta") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::DELTA)); - } - else if (monitored == "omega") - { - gd.monitored_variables.set(static_cast( - GenrouData::MonitorableVariables::OMEGA)); - } - else - { - throw "Invalid monitored variable"; - } - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.cpp b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.cpp index 71da44979..e9c724477 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.cpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.cpp @@ -21,8 +21,7 @@ namespace GridKit namespace PhasorDynamics { /** - * @brief Constructor for a classical generator model. - * + * @brief Constructor for a classical generator model */ template GenClassical::GenClassical(bus_type* bus, int unit_id) @@ -38,15 +37,10 @@ namespace GridKit { size_ = 5; setDerivedParams(); - - // Temporary, to eliminate compiler warnings - (void) busID_; - (void) unit_id_; } - /*! - * @brief Constructor for a pi-model branch - * + /** + * @brief Constructor for a classical generator model */ template GenClassical::GenClassical(bus_type* bus, @@ -71,27 +65,54 @@ namespace GridKit setDerivedParams(); } - /*! + /** * @brief Constructor for a classical generator model - * */ template GenClassical::GenClassical(bus_type* bus, const DataT& data) : bus_(bus), - busID_(0), - unit_id_(0), - p0_(data.p0), - q0_(data.q0), - H_(data.H), - D_(data.D), - Ra_(data.Ra), - Xdp_(data.Xdp) + unit_id_(1) { + if (data.parameters.contains(DataT::Parameters::p0)) + { + p0_ = std::get(data.parameters.at(DataT::Parameters::p0)); + } + + if (data.parameters.contains(DataT::Parameters::q0)) + { + q0_ = std::get(data.parameters.at(DataT::Parameters::q0)); + } + + if (data.parameters.contains(DataT::Parameters::H)) + { + H_ = std::get(data.parameters.at(DataT::Parameters::H)); + } + + if (data.parameters.contains(DataT::Parameters::D)) + { + D_ = std::get(data.parameters.at(DataT::Parameters::D)); + } + + if (data.parameters.contains(DataT::Parameters::Ra)) + { + Ra_ = std::get(data.parameters.at(DataT::Parameters::Ra)); + } + + if (data.parameters.contains(DataT::Parameters::Xdp)) + { + Xdp_ = std::get(data.parameters.at(DataT::Parameters::Xdp)); + } + + if (data.ports.contains(DataT::Ports::bus)) + { + busID_ = data.ports.at(DataT::Ports::bus); + } + size_ = 5; setDerivedParams(); } - /*! + /** * @brief allocate method computes sparsity pattern of the Jacobian. */ template @@ -107,7 +128,6 @@ namespace GridKit /** * Initialization of the generator model - * */ template int GenClassical::initialize() @@ -155,7 +175,6 @@ namespace GridKit /** * \brief Residual for the generator model. - * */ template int GenClassical::evaluateResidual() @@ -213,6 +232,5 @@ namespace GridKit // Available template instantiations template class GenClassical; template class GenClassical; - } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp index 3c0125ab0..35581ae77 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp @@ -104,18 +104,18 @@ namespace GridKit private: /* Identification */ bus_type* bus_; - const int busID_; - int unit_id_; + IdxT busID_{0}; + int unit_id_; //< @todo this should be removed /* Initial terminal conditions */ - ScalarT p0_; - ScalarT q0_; + ScalarT p0_{0.0}; + ScalarT q0_{0.0}; /* Input parameters */ - real_type H_; - real_type D_; - real_type Ra_; - real_type Xdp_; + real_type H_{0.0}; + real_type D_{0.0}; + real_type Ra_{0.0}; + real_type Xdp_{0.0}; /* Derivied parameters */ real_type G_; diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp index c29013f32..3ff9cfd75 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp @@ -6,17 +6,42 @@ */ #pragma once -#include -#include -#include -#include - -#include +#include namespace GridKit { namespace PhasorDynamics { + /// Initial parameters for a classical generator model + enum class GenClassicalParameters + { + p0, ///< Initial active power + q0, ///< Initial reactive power + H, ///< Rotor inertia + D, ///< Damping coefficient + Ra, ///< Winding resistance + Xdp, ///< Direct axis transient reactance + }; + + /// Ports supported for a classical generator model + enum class GenClassicalPorts + { + bus, ///< Unique ID of the connecting bus + exciter_signal, ///< Unique ID of the bus providing the exciter signal + governor_signal, ///< Unique ID of the bus providing the governor signal + }; + + /// Variables able to be monitored for a classical generator model + enum class GenClassicalMonitorableVariables + { + ir, + ii, + p, + q, + delta, + omega, + }; + /** * @brief Contains modeling data for a GenClassical generator model. * @@ -28,154 +53,17 @@ namespace GridKit * @todo Decide on naming scheme for model parameters. */ template - struct GenClassicalData + struct GenClassicalData : public ComponentData { - IdxT unit_id{0}; ///< Unique component ID - RealT p0{0.0}; ///< Initial active power - RealT q0{0.0}; ///< Initial reactive power - RealT H{0.0}; ///< Rotor inertia - RealT D{0.0}; ///< Damping coefficient - RealT Ra{0.0}; ///< Winding resistance - RealT Xdp{0.0}; ///< Direct axis transient reactance - - IdxT bus_id{0}; ///< Unique ID of the connecting bus - std::optional exciter_signal; ///< Unique ID of the bus providing the exciter signal - std::optional governor_signal; ///< Unique ID of the bus providing the governor signal + GenClassicalData() = default; - std::optional freq_base; ///< Override for the system-wide base frequency - std::optional va_base; ///< Override for the system-wide power base - - std::string disambiguation_string; ///< Disambiguation string for this device - - /// Indices of the variables able to be monitored in the bitset - enum class MonitorableVariables : size_t - { - IR, - II, - P, - Q, - DELTA, - OMEGA, - MAXIMUM, - }; - - /// Set of variables being monitored - std::bitset>(MonitorableVariables::MAXIMUM)> - monitored_variables; + using Parameters = GenClassicalParameters; + using Ports = GenClassicalPorts; + using MonitorableVariables = GenClassicalMonitorableVariables; }; - - /// JSON parser function implementation for the `GenClassicalData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, GenClassicalData& gd) - { - for (auto& raw_parameter : j.at("params").items()) - { - if (raw_parameter.key() == "p0") - { - raw_parameter.value().get_to(gd.p0); - } - else if (raw_parameter.key() == "q0") - { - raw_parameter.value().get_to(gd.q0); - } - else if (raw_parameter.key() == "H") - { - raw_parameter.value().get_to(gd.H); - } - else if (raw_parameter.key() == "D") - { - raw_parameter.value().get_to(gd.D); - } - else if (raw_parameter.key() == "Ra") - { - raw_parameter.value().get_to(gd.Ra); - } - else if (raw_parameter.key() == "Xdp") - { - raw_parameter.value().get_to(gd.Xdp); - } - else - { - throw "Invalid initial parameter"; - } - } - - for (auto& raw_port : j.at("ports").items()) - { - if (raw_port.key() == "bus") - { - raw_port.value().get_to(gd.bus_id); - } - else if (raw_port.key() == "governor_signal") - { - raw_port.value().get_to(gd.governor_signal); - } - else if (raw_port.key() == "exciter_signal") - { - raw_port.value().get_to(gd.exciter_signal); - } - else - { - throw "Invalid port mapping"; - } - } - - if (j.contains("freq_base")) - { - j.at("freq_base").get_to(gd.freq_base); - } - - if (j.contains("va_base")) - { - j.at("va_base").get_to(gd.va_base); - } - - j.at("id").get_to(gd.disambiguation_string); - - if (j.contains("mon")) - { - for (auto& raw_monitored_variable : j.at("mon")) - { - auto monitored = raw_monitored_variable.get(); - if (monitored == "ir") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::IR)); - } - else if (monitored == "ii") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::II)); - } - else if (monitored == "p") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::P)); - } - else if (monitored == "q") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::Q)); - } - else if (monitored == "delta") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::DELTA)); - } - else if (monitored == "omega") - { - gd.monitored_variables.set(static_cast( - GenClassicalData::MonitorableVariables::OMEGA)); - } - else - { - throw "Invalid monitored variable"; - } - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/SystemModel.hpp b/src/Model/PhasorDynamics/SystemModel.hpp index 606916657..dbb256a2c 100644 --- a/src/Model/PhasorDynamics/SystemModel.hpp +++ b/src/Model/PhasorDynamics/SystemModel.hpp @@ -92,35 +92,67 @@ namespace GridKit // Add branches for (const auto& branchdata : data.branch) { - auto* branch = new Branch(getBus(branchdata.bus1_id), getBus(branchdata.bus2_id), branchdata); + IdxT bus1_index = 0; + if (branchdata.ports.contains(BranchData::Ports::bus1)) + { + bus1_index = branchdata.ports.at(BranchData::Ports::bus1); + } + + IdxT bus2_index = 0; + if (branchdata.ports.contains(BranchData::Ports::bus2)) + { + bus2_index = branchdata.ports.at(BranchData::Ports::bus2); + } + + auto* branch = new Branch(getBus(bus1_index), getBus(bus2_index), branchdata); addComponent(branch); } // Add loads for (const auto& loaddata : data.load) { - auto* load = new Load(getBus(loaddata.bus_id), loaddata); + IdxT bus_index = 0; + if (loaddata.ports.contains(LoadData::Ports::bus)) + { + bus_index = loaddata.ports.at(LoadData::Ports::bus); + } + auto* load = new Load(getBus(bus_index), loaddata); addComponent(load); } // Add GENROU generators for (const auto& gendata : data.genrou) { - auto* gen = new Genrou(getBus(gendata.bus_id), gendata); + IdxT bus_index = 0; + if (gendata.ports.contains(GenrouData::Ports::bus)) + { + bus_index = gendata.ports.at(GenrouData::Ports::bus); + } + auto* gen = new Genrou(getBus(bus_index), gendata); addComponent(gen); } // Add classical generators for (const auto& gendata : data.genclassical) { - auto* gen = new GenClassical(getBus(gendata.bus_id), gendata); + IdxT bus_index = 0; + if (gendata.ports.contains(GenClassicalData::Ports::bus)) + { + bus_index = gendata.ports.at(GenClassicalData::Ports::bus); + } + auto* gen = new GenClassical(getBus(bus_index), gendata); addComponent(gen); } // Add faults for (const auto& faultdata : data.bus_fault) { - auto* fault = new BusFault(getBus(faultdata.bus_id), faultdata); + IdxT bus_index = 0; + if (faultdata.ports.contains(BusFaultData::Ports::bus)) + { + bus_index = faultdata.ports.at(BusFaultData::Ports::bus); + } + auto* fault = new BusFault(getBus(bus_index), faultdata); addFault(fault); } } diff --git a/src/Model/PhasorDynamics/SystemModelData.hpp b/src/Model/PhasorDynamics/SystemModelData.hpp index b58d99b9d..4cddedabb 100644 --- a/src/Model/PhasorDynamics/SystemModelData.hpp +++ b/src/Model/PhasorDynamics/SystemModelData.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -9,14 +11,11 @@ #include #include #include -#include namespace GridKit { namespace PhasorDynamics { - using json = nlohmann::json; - /// A structure containing all of the data needed to reproduce a /// system model /// @@ -73,64 +72,5 @@ namespace GridKit std::vector load; ///< Loads within the model std::vector gov; ///< Governors within the model }; - - /// JSON parser function implementation for the `SystemModelData` type - /// - /// See the `README.md` in `src/Model/PhasorDynamics` for more information - template - void from_json(const json& j, SystemModelData& sm) - { - auto header = j.at("header"); - - if (header.contains("format_version")) - { - header.at("format_version").get_to(sm.format_version); - } - - if (header.contains("format_revision")) - { - header.at("format_revision").get_to(sm.format_revision); - } - - header.at("case_name").get_to(sm.case_name); - - if (header.contains("case_date_time")) - { - header.at("case_date_time").get_to(sm.case_date_time); - } - - header.at("case_description").get_to(sm.case_description); - header.at("case_comments").get_to(sm.case_comments); - header.at("freq_base").get_to(sm.freq_base); - header.at("va_base").get_to(sm.va_base); - j.at("buses").get_to(sm.bus); - - for (auto& raw_component : j.at("devices")) - { - auto kind = raw_component.at("class"); - if (kind == "branch") - { - typename SystemModelData::BranchDataT branch; - raw_component.get_to(branch); - sm.branch.push_back(branch); - } - else if (kind == "GENROU") - { - typename SystemModelData::GenrouDataT genrou; - raw_component.get_to(genrou); - sm.genrou.push_back(genrou); - } - else if (kind == "bus_fault") - { - typename SystemModelData::BusFaultDataT bus_fault; - raw_component.get_to(bus_fault); - sm.bus_fault.push_back(bus_fault); - } - else - { - throw "Invalid device class"; - } - } - } } // namespace PhasorDynamics } // namespace GridKit diff --git a/src/Model/PhasorDynamics/SystemModelDataJSONParser.hpp b/src/Model/PhasorDynamics/SystemModelDataJSONParser.hpp new file mode 100644 index 000000000..d9ee36784 --- /dev/null +++ b/src/Model/PhasorDynamics/SystemModelDataJSONParser.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + using json = nlohmann::json; + + /// JSON parser function implementation for the `SystemModelData` type + /// + /// See the `README.md` in `src/Model/PhasorDynamics` for more information + template + void from_json(const json& j, SystemModelData& sm) + { + auto header = j.at("header"); + + if (header.contains("format_version")) + { + header.at("format_version").get_to(sm.format_version); + } + + if (header.contains("format_revision")) + { + header.at("format_revision").get_to(sm.format_revision); + } + + header.at("case_name").get_to(sm.case_name); + + if (header.contains("case_date_time")) + { + header.at("case_date_time").get_to(sm.case_date_time); + } + + header.at("case_description").get_to(sm.case_description); + header.at("case_comments").get_to(sm.case_comments); + header.at("freq_base").get_to(sm.freq_base); + header.at("va_base").get_to(sm.va_base); + j.at("buses").get_to(sm.bus); + + for (auto& raw_component : j.at("devices")) + { + auto kind = raw_component.at("class"); + if (kind == "branch") + { + typename SystemModelData::BranchDataT branch; + raw_component.get_to(branch); + sm.branch.push_back(branch); + } + else if (kind == "GENROU") + { + typename SystemModelData::GenrouDataT genrou; + raw_component.get_to(genrou); + sm.genrou.push_back(genrou); + } + else if (kind == "bus_fault") + { + typename SystemModelData::BusFaultDataT bus_fault; + raw_component.get_to(bus_fault); + sm.bus_fault.push_back(bus_fault); + } + else + { + throw "Invalid device class"; + } + } + } + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/tests/UnitTests/CMakeLists.txt b/tests/UnitTests/CMakeLists.txt index a168f9f7d..38c12eeee 100644 --- a/tests/UnitTests/CMakeLists.txt +++ b/tests/UnitTests/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(AutomaticDifferentiation) add_subdirectory(PhasorDynamics) add_subdirectory(Solver) +add_subdirectory(Utilities) diff --git a/tests/UnitTests/PhasorDynamics/CMakeLists.txt b/tests/UnitTests/PhasorDynamics/CMakeLists.txt index 170e86db1..43e824dcb 100644 --- a/tests/UnitTests/PhasorDynamics/CMakeLists.txt +++ b/tests/UnitTests/PhasorDynamics/CMakeLists.txt @@ -11,8 +11,6 @@ add_executable(test_phasor_branch runBranchTests.cpp) target_link_libraries(test_phasor_branch GRIDKIT::phasor_dynamics_branch GRIDKIT::phasor_dynamics_bus) -add_executable(test_case_format runCaseFormatTests.cpp) - add_executable(test_phasor_load runLoadTests.cpp) target_link_libraries(test_phasor_load GRIDKIT::phasor_dynamics_load GRIDKIT::phasor_dynamics_load_dependency_tracking @@ -42,7 +40,6 @@ target_link_libraries(test_phasor_system GRIDKIT::phasor_dynamics_load add_test(NAME PhasorDynamicsBusTest COMMAND $) add_test(NAME PhasorDynamicsBranchTest COMMAND $) -add_test(NAME PhasorDynamicsCaseFormatTest COMMAND $) add_test(NAME PhasorDynamicsGenrouTest COMMAND $) add_test(NAME PhasorDynamicsGovernorTgov1Test COMMAND $) add_test(NAME PhasorDynamicsGenClassicalTest COMMAND $) @@ -51,7 +48,6 @@ add_test(NAME PhasorDynamicsSystemTest COMMAND $ install(TARGETS test_phasor_bus test_phasor_branch - test_case_format test_phasor_load test_phasor_genrou test_phasor_governortgov1 diff --git a/tests/UnitTests/PhasorDynamics/CaseFormatTests.hpp b/tests/UnitTests/PhasorDynamics/CaseFormatTests.hpp deleted file mode 100644 index 4a8c3bbac..000000000 --- a/tests/UnitTests/PhasorDynamics/CaseFormatTests.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace GridKit -{ - namespace Testing - { - using json = nlohmann::json; - - template - class CaseFormatTests - { - using SystemModelDataT = PhasorDynamics::SystemModelData; - - public: - CaseFormatTests() = default; - ~CaseFormatTests() = default; - - TestOutcome simpleParse() - { - const char data[] = - R"({ - "header": { - "format_version": 0, - "format_revision": 1, - "case_name": "Two-bus test case 1", - "case_description": "A two-bus test case for demonstrating the dynamics format", - "case_comments": "This case is set up to monitor the voltage at both buses and the machine angle and speed", - "freq_base": 60, - "va_base": 100e6 - }, - "buses": [ - { "number": 1, "class": "bus", "name": "Bus 1", "init": {"Vr":0.994988, "Vi":0.099997}, "v_base": 115e3, "mon": ["Vr", "Vi"] }, - { "number": 2, "class": "infinite_bus", "name": "Bus 2", "init": {"Vr":1, "Vi":0}, "v_base": 115e3 } - ], - "devices": [ - { "class": "branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0, "X":0.1, "G":0, "B":0} }, - { "class": "GENROU", "ports": {"bus":1}, "id": "1", "params": {"p0":1, "q0":0.05013, "H":3, "D":0, "Ra":0, "Tdop":7, "Tdopp":0.04, "Tqopp":0.05, - "Tqop":0.75, "Xd":2.1, "Xdp":0.2, "Xdpp":0.18, "Xq":0.5, "Xqp": 0, "Xqpp":0.18, "Xl":0.15, "S10":0, "S12":0}, "mon": ["delta", "omega"] }, - { "class": "bus_fault", "ports": {"bus":1}, "id": "1", "params": {"state0": false, "R":0, "X":1e-3} } - ] - })"; - - TestStatus success = true; - SystemModelDataT result = json::parse(data); - - success *= result.format_version == 0; - success *= result.format_revision == 1; - success *= result.case_name == "Two-bus test case 1"; - success *= result.case_date_time == std::nullopt; - success *= result.case_description == "A two-bus test case for demonstrating the dynamics format"; - success *= result.case_comments == "This case is set up to monitor the voltage at both buses and the machine angle and speed"; - success *= result.freq_base == 60.0; - success *= result.va_base == 100.0e6; - - success *= result.bus.size() == 2; - success *= result.branch.size() == 1; - success *= result.bus_fault.size() == 1; - success *= result.genrou.size() == 1; - success *= result.load.size() == 0; - - success *= result.bus[0].bus_id == 1; - success *= result.bus[0].bus_type == SystemModelDataT::BusDataT::BusType::DEFAULT; - success *= result.bus[0].name == "Bus 1"; - success *= result.bus[0].Vr0 == 0.994988; - success *= result.bus[0].Vi0 == 0.099997; - success *= result.bus[0].v_base == 115e3; - success *= result.bus[0].monitored_variables[static_cast(SystemModelDataT::BusDataT::MonitorableVariables::VR)]; - success *= result.bus[0].monitored_variables[static_cast(SystemModelDataT::BusDataT::MonitorableVariables::VI)]; - success *= result.bus[1].bus_id == 2; - success *= result.bus[1].bus_type == SystemModelDataT::BusDataT::BusType::SLACK; - success *= result.bus[1].name == "Bus 2"; - success *= result.bus[1].Vr0 == 1.0; - success *= result.bus[1].Vi0 == 0.0; - success *= result.bus[1].v_base == 115e3; - success *= result.bus[1].monitored_variables.none(); - - success *= result.branch[0].R == 0.0; - success *= result.branch[0].X == 0.1; - success *= result.branch[0].G == 0.0; - success *= result.branch[0].B == 0.0; - success *= result.branch[0].bus1_id == 1; - success *= result.branch[0].bus2_id == 2; - success *= result.branch[0].disambiguation_string == "1"; - success *= result.branch[0].monitored_variables.none(); - - success *= result.genrou[0].p0 == 1.0; - success *= result.genrou[0].q0 == 0.05013; - success *= result.genrou[0].H == 3.0; - success *= result.genrou[0].D == 0.0; - success *= result.genrou[0].Ra == 0.0; - success *= result.genrou[0].Tdop == 7.0; - success *= result.genrou[0].Tdopp == 0.04; - success *= result.genrou[0].Tqop == 0.75; - success *= result.genrou[0].Tqopp == 0.05; - success *= result.genrou[0].Xd == 2.1; - success *= result.genrou[0].Xdp == 0.2; - success *= result.genrou[0].Xdpp == 0.18; - success *= result.genrou[0].Xq == 0.5; - success *= result.genrou[0].Xqp == 0.0; - success *= result.genrou[0].Xqpp == 0.18; - success *= result.genrou[0].Xl == 0.15; - success *= result.genrou[0].S10 == 0.0; - success *= result.genrou[0].S12 == 0.0; - success *= result.genrou[0].bus_id == 1; - success *= result.genrou[0].disambiguation_string == "1"; - success *= result.genrou[0].monitored_variables[static_cast(SystemModelDataT::GenrouDataT::MonitorableVariables::DELTA)]; - success *= result.genrou[0].monitored_variables[static_cast(SystemModelDataT::GenrouDataT::MonitorableVariables::OMEGA)]; - - success *= result.bus_fault[0].R == 0.0; - success *= result.bus_fault[0].X == 1e-3; - success *= result.bus_fault[0].status == false; - success *= result.bus_fault[0].bus_id == 1; - success *= result.bus_fault[0].disambiguation_string == "1"; - success *= result.bus_fault[0].monitored_variables.none(); - - return success.report(__func__); - } - }; - } // namespace Testing -} // namespace GridKit diff --git a/tests/UnitTests/PhasorDynamics/SystemTests.hpp b/tests/UnitTests/PhasorDynamics/SystemTests.hpp index e6adb1bc4..39b5e0e81 100644 --- a/tests/UnitTests/PhasorDynamics/SystemTests.hpp +++ b/tests/UnitTests/PhasorDynamics/SystemTests.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,9 @@ namespace GridKit { namespace Testing { + using GridKit::PhasorDynamics::BranchParameters; + using GridKit::PhasorDynamics::BranchPorts; + template class SystemTests { @@ -66,12 +70,12 @@ namespace GridKit data.branch.resize(1); // Branch 0-1 - data.branch[0].bus1_id = data.bus[0].bus_id; - data.branch[0].bus2_id = data.bus[1].bus_id; - data.branch[0].R = 2.0; - data.branch[0].X = 4.0; - data.branch[0].G = 0.2; - data.branch[0].B = 1.2; + data.branch[0].ports[BranchPorts::bus1] = data.bus[0].bus_id; + data.branch[0].ports[BranchPorts::bus2] = data.bus[1].bus_id; + data.branch[0].parameters[BranchParameters::R] = 2.0; + data.branch[0].parameters[BranchParameters::X] = 4.0; + data.branch[0].parameters[BranchParameters::G] = 0.2; + data.branch[0].parameters[BranchParameters::B] = 1.2; // Create an empty system model system = new PhasorDynamics::SystemModel(data); diff --git a/tests/UnitTests/Utilities/CMakeLists.txt b/tests/UnitTests/Utilities/CMakeLists.txt new file mode 100644 index 000000000..fd9b96b1a --- /dev/null +++ b/tests/UnitTests/Utilities/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(test_case_format runCaseFormatTests.cpp) +target_link_libraries(test_case_format PRIVATE nlohmann_json::nlohmann_json magic_enum::magic_enum) + +add_test(NAME UtilitiesCaseFormatTest COMMAND $) + +install(TARGETS test_case_format) diff --git a/tests/UnitTests/Utilities/CaseFormatTests.hpp b/tests/UnitTests/Utilities/CaseFormatTests.hpp new file mode 100644 index 000000000..2a24a4b14 --- /dev/null +++ b/tests/UnitTests/Utilities/CaseFormatTests.hpp @@ -0,0 +1,132 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + namespace Testing + { + using json = nlohmann::json; + + template + class CaseFormatTests + { + public: + CaseFormatTests() = default; + ~CaseFormatTests() = default; + + TestOutcome simpleParse() + { + using namespace GridKit::PhasorDynamics; + using BusData = BusData; + using BusType = BusData::BusType; + + const char data[] = + R"({ + "header": { + "format_version": 0, + "format_revision": 1, + "case_name": "Two-bus test case 1", + "case_description": "A two-bus test case for demonstrating the dynamics format", + "case_comments": "This case is set up to monitor the voltage at both buses and the machine angle and speed", + "freq_base": 60.0, + "va_base": 100e6 + }, + "buses": [ + { "number": 1, "class": "bus", "name": "Bus 1", "init": {"Vr":0.994988, "Vi":0.099997}, "v_base": 115e3, "mon": ["Vr", "Vi"] }, + { "number": 2, "class": "infinite_bus", "name": "Bus 2", "init": {"Vr":1.0, "Vi":0.0}, "v_base": 115e3 } + ], + "devices": [ + { "class": "branch", "ports": {"bus1":1, "bus2":2}, "id": "1", "params": {"R":0.0, "X":0.1, "G":0.0, "B":0.0} }, + { "class": "GENROU", "ports": {"bus":1}, "id": "1", "params": {"p0":1.0, "q0":0.05013, "H":3.0, "D":0.0, "Ra":0.0, "Tdop":7.0, "Tdopp":0.04, "Tqopp":0.05, + "Tqop":0.75, "Xd":2.1, "Xdp":0.2, "Xdpp":0.18, "Xq":0.5, "Xqp": 0.0, "Xqpp":0.18, "Xl":0.15, "S10":0.0, "S12":0.0}, "mon": ["delta", "omega"] }, + { "class": "bus_fault", "ports": {"bus":1}, "id": "1", "params": {"state0": false, "R":0.0, "X":1e-3} } + ] + })"; + + TestStatus success = true; + SystemModelData result = json::parse(data); + + success *= result.format_version == 0; + success *= result.format_revision == 1; + success *= result.case_name == "Two-bus test case 1"; + success *= result.case_date_time == std::nullopt; + success *= result.case_description == "A two-bus test case for demonstrating the dynamics format"; + success *= result.case_comments == "This case is set up to monitor the voltage at both buses and the machine angle and speed"; + success *= result.freq_base == 60.0; + success *= result.va_base == 100.0e6; + + success *= result.bus.size() == 2; + success *= result.branch.size() == 1; + success *= result.bus_fault.size() == 1; + success *= result.genrou.size() == 1; + success *= result.load.size() == 0; + + success *= result.bus[0].bus_id == 1; + success *= result.bus[0].bus_type == BusType::DEFAULT; + success *= result.bus[0].name == "Bus 1"; + success *= result.bus[0].Vr0 == 0.994988; + success *= result.bus[0].Vi0 == 0.099997; + success *= result.bus[0].v_base == 115e3; + success *= result.bus[0].monitored_variables[static_cast(BusData::MonitorableVariables::VR)]; + success *= result.bus[0].monitored_variables[static_cast(BusData::MonitorableVariables::VI)]; + success *= result.bus[1].bus_id == 2; + success *= result.bus[1].bus_type == BusType::SLACK; + success *= result.bus[1].name == "Bus 2"; + success *= result.bus[1].Vr0 == 1.0; + success *= result.bus[1].Vi0 == 0.0; + success *= result.bus[1].v_base == 115e3; + success *= result.bus[1].monitored_variables.none(); + + success *= std::get(result.branch[0].parameters[BranchParameters::R]) == 0.0; + success *= std::get(result.branch[0].parameters[BranchParameters::X]) == 0.1; + success *= std::get(result.branch[0].parameters[BranchParameters::G]) == 0.0; + success *= std::get(result.branch[0].parameters[BranchParameters::B]) == 0.0; + success *= result.branch[0].ports[BranchPorts::bus1] == 1; + success *= result.branch[0].ports[BranchPorts::bus2] == 2; + success *= result.branch[0].disambiguation_string == "1"; + success *= result.branch[0].monitored_variables.empty(); + + success *= std::get(result.genrou[0].parameters[GenrouParameters::p0]) == 1.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::q0]) == 0.05013; + success *= std::get(result.genrou[0].parameters[GenrouParameters::H]) == 3.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::D]) == 0.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Ra]) == 0.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Tdop]) == 7.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Tdopp]) == 0.04; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Tqop]) == 0.75; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Tqopp]) == 0.05; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xd]) == 2.1; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xdp]) == 0.2; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xdpp]) == 0.18; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xq]) == 0.5; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xqp]) == 0.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xqpp]) == 0.18; + success *= std::get(result.genrou[0].parameters[GenrouParameters::Xl]) == 0.15; + success *= std::get(result.genrou[0].parameters[GenrouParameters::S10]) == 0.0; + success *= std::get(result.genrou[0].parameters[GenrouParameters::S12]) == 0.0; + success *= result.genrou[0].ports[GenrouPorts::bus] == 1; + success *= result.genrou[0].disambiguation_string == "1"; + success *= result.genrou[0].monitored_variables.contains(GenrouMonitorableVariables::delta); + success *= result.genrou[0].monitored_variables.contains(GenrouMonitorableVariables::omega); + + success *= std::get(result.bus_fault[0].parameters[BusFaultParameters::R]) == 0.0; + success *= std::get(result.bus_fault[0].parameters[BusFaultParameters::X]) == 1e-3; + success *= !std::get(result.bus_fault[0].parameters[BusFaultParameters::state0]); + success *= result.bus_fault[0].ports[BusFaultPorts::bus] == 1; + success *= result.bus_fault[0].disambiguation_string == "1"; + success *= result.bus_fault[0].monitored_variables.empty(); + + return success.report(__func__); + } + }; + } // namespace Testing +} // namespace GridKit diff --git a/tests/UnitTests/PhasorDynamics/runCaseFormatTests.cpp b/tests/UnitTests/Utilities/runCaseFormatTests.cpp similarity index 100% rename from tests/UnitTests/PhasorDynamics/runCaseFormatTests.cpp rename to tests/UnitTests/Utilities/runCaseFormatTests.cpp diff --git a/third-party/magic-enum b/third-party/magic-enum new file mode 160000 index 000000000..ecdeb1cea --- /dev/null +++ b/third-party/magic-enum @@ -0,0 +1 @@ +Subproject commit ecdeb1cea54d87b09bafe9999faf56654646209d