deltaFlow
main.C
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 Saud Zahir
3 *
4 * This file is part of deltaFlow.
5 *
6 * deltaFlow is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * deltaFlow is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with deltaFlow. If not, see
18 * <https://www.gnu.org/licenses/>.
19 */
20
26#include <chrono>
27#include <cmath>
28#include <complex>
29#include <memory>
30#include <utility>
31#include <vector>
32
33#include "Admittance.H"
34#include "Argparse.H"
35#include "Display.H"
36#include "GaussSeidel.H"
37#include "IEEE.H"
38#include "Logger.H"
39#include "NewtonRaphson.H"
40#include "OutputFile.H"
41#include "PSSE.H"
42#include "Qlim.H"
43#include "Reader.H"
44#include "Writer.H"
45
46int main(int argc, char* argv[]) {
48
49 auto startTime = std::chrono::high_resolution_clock::now();
50
51 ArgumentParser args(argc, argv);
52
53 std::string jobName = args.getJobName();
54 std::string inputFile = args.getInputFile();
55 SolverType solver = args.getSolverType();
56 InputFormat format = args.getInputFormat();
57 int maxIter = args.getMaxIterations();
58 double tolerance = args.getTolerance();
59
60 std::string solverName = (solver == SolverType::GaussSeidel) ? "Gauss-Seidel" : "Newton-Raphson";
61 std::string formatName = (format == InputFormat::IEEE) ? "IEEE Common Data Format" : "PSS/E Raw Format";
62
63 LOG_DEBUG("Job name :: {}", jobName);
64 LOG_DEBUG("Input file :: {}", inputFile);
65 LOG_DEBUG("Input format :: {}", formatName);
66 LOG_DEBUG("Solver :: {}", solverName);
67 LOG_DEBUG("Tolerance :: {:.6e}", tolerance);
68 LOG_DEBUG("Max iter :: {}", maxIter);
69
70 std::unique_ptr<Reader> reader;
71
72 switch (format) {
74 reader = std::make_unique<IEEECommonDataFormat>();
75 LOG_INFO("Reading IEEE Common Data Format file: {}", inputFile);
76 break;
78 reader = std::make_unique<PSSERawFormat>();
79 LOG_INFO("Reading PSS/E Raw Format file: {}", inputFile);
80 break;
81 default:
82 break;
83 }
84
85 reader->read(inputFile);
86
87 auto busData = reader->getBusData();
88 auto branchData = reader->getBranchData();
89
90 if (busData.ID.size() == 0 || branchData.From.size() == 0) {
91 LOG_ERROR("No bus or branch data found in '{}'. Check the file exists and is valid.", inputFile);
92 std::exit(1);
93 }
94
95 int N = busData.ID.size();
96 int nBranch = branchData.From.size();
97
98 LOG_INFO("Model: {} buses, {} branches", N, nBranch);
99
100 int nSlack = 0, nPV = 0, nPQ = 0;
101 for (int i = 0; i < N; ++i) {
102 if (busData.Type(i) == 1) nSlack++;
103 else if (busData.Type(i) == 2) nPV++;
104 else nPQ++;
105 }
106 LOG_DEBUG("Bus types: {} Slack, {} PV, {} PQ", nSlack, nPV, nPQ);
107
108 auto Y = computeAdmittanceMatrix(busData, branchData);
109 LOG_DEBUG("Admittance matrix computed ({}x{})", N, N);
110
111 Eigen::MatrixXd G = Y.array().real().matrix();
112 Eigen::MatrixXd B = Y.array().imag().matrix();
113
114 // Flat start: PQ buses -> V=1.0, delta=0 for all; PV/Slack keep file voltage
115 Eigen::VectorXd V(N);
116 Eigen::VectorXd delta = Eigen::VectorXd::Zero(N);
117
118 for (int i = 0; i < N; ++i) {
119 if (busData.Type(i) == 3) { // PQ bus
120 V(i) = 1.0;
121 } else {
122 V(i) = busData.V(i); // PV/Slack keep specified voltage
123 }
124 }
125
126 Eigen::VectorXi type_bus = busData.Type;
127
128 bool finalConverged = false;
129 int totalIterations = 0;
130 double finalError = 0.0;
131 std::vector<std::pair<int, double>> iterationHistory;
132
133 LOG_INFO("Starting {} solver ...", solverName);
134
135 switch (solver) {
137 double relaxation_coeff = args.getRelaxationCoefficient();
138
139 bool Q_lim_status = true;
140
141 while (Q_lim_status) {
142 Eigen::VectorXd Ps = busData.Pg - busData.Pl;
143 Eigen::VectorXd Qs = busData.Qg - busData.Ql;
144
145 std::vector<int> pv_indices;
146 for (int i = 0; i < N; ++i)
147 if (type_bus(i) == 2) pv_indices.push_back(i);
148
149 bool converged = GaussSeidel(Y, V, delta, type_bus, Ps, Qs, N,
150 maxIter, tolerance, relaxation_coeff,
151 &iterationHistory);
152
153 finalConverged = converged;
154
155 if (!converged) {
156 LOG_ERROR("Gauss-Seidel solver failed to converge.");
157 break;
158 }
159
160 Q_lim_status = checkQlimits(V, delta, type_bus, G, B,
161 busData, pv_indices, N);
162
163 if (Q_lim_status)
164 LOG_DEBUG("Re-running Gauss-Seidel with updated bus types ...");
165 }
166 break;
167 }
168
170 default: {
171 bool Q_lim_status = true;
172
173 while (Q_lim_status) {
174 Eigen::VectorXd Ps = busData.Pg - busData.Pl;
175 Eigen::VectorXd Qs = busData.Qg - busData.Ql;
176
177 std::vector<int> pq_indices;
178 std::vector<int> pv_indices;
179
180 for (int i = 0; i < N; ++i) {
181 if (type_bus(i) == 3) pq_indices.push_back(i);
182 else if (type_bus(i) == 2) pv_indices.push_back(i);
183 }
184
185 int n_pq = static_cast<int>(pq_indices.size());
186
187 bool converged = NewtonRaphson(G, B, Ps, Qs, V, delta,
188 N, n_pq, pq_indices, maxIter, tolerance, &iterationHistory);
189
190 finalConverged = converged;
191
192 if (!converged) {
193 LOG_ERROR("Newton-Raphson solver failed to converge.");
194 break;
195 }
196
197 Q_lim_status = checkQlimits(V, delta, type_bus, G, B,
198 busData, pv_indices, N);
199
200 if (Q_lim_status) {
201 LOG_DEBUG("Re-running Newton-Raphson with updated bus types ...");
202 }
203 }
204 break;
205 }
206 }
207
208 if (!iterationHistory.empty()) {
209 totalIterations = iterationHistory.back().first;
210 finalError = iterationHistory.back().second;
211 }
212
213 if (!finalConverged) {
214 auto endTime = std::chrono::high_resolution_clock::now();
215 double elapsedSec = std::chrono::duration<double>(endTime - startTime).count();
216 OutputFile::writeStatusFile(jobName, inputFile, solverName, formatName,
217 N, nBranch, maxIter, 0.0, tolerance, false, elapsedSec);
218 std::exit(1);
219 }
220
221 Eigen::VectorXcd Vc(N);
222 for (int i = 0; i < N; ++i)
223 Vc(i) = std::polar(V(i), delta(i));
224
225 Eigen::VectorXd P_net = busData.Pg - busData.Pl;
226 Eigen::VectorXd Q_net = busData.Qg - busData.Ql;
227
228 // Recalculate slack bus power injection
229 for (int i = 0; i < N; ++i) {
230 if (busData.Type(i) == 1) { // Slack
231 std::complex<double> Ii = Y.row(i) * Vc;
232 std::complex<double> Si = Vc(i) * std::conj(Ii);
233 P_net(i) = Si.real();
234 Q_net(i) = Si.imag();
235 }
236 }
237
238 // Recalculate reactive power for PV buses
239 for (int i = 0; i < N; ++i) {
240 if (busData.Type(i) == 2) { // PV
241 std::complex<double> Ii = Y.row(i) * Vc;
242 Q_net(i) = -std::imag(std::conj(Vc(i)) * Ii);
243 }
244 }
245
246 for (int i = 0; i < N; ++i) {
247 busData.V(i) = std::abs(Vc(i));
248 busData.delta(i) = std::arg(Vc(i)) * 180.0 / M_PI;
249 busData.Pg(i) = P_net(i) + busData.Pl(i);
250 busData.Qg(i) = Q_net(i) + busData.Ql(i);
251 }
252
253 double PLoss = busData.Pg.sum() - busData.Pl.sum();
254 double QLoss = busData.Qg.sum() - busData.Ql.sum();
255 LOG_DEBUG("Total real power loss: {:.6f} p.u.", PLoss);
256 LOG_DEBUG("Total reactive power loss: {:.6f} p.u.", QLoss);
257
258 dispBusData(busData);
259 dispLineFlow(busData, branchData, Y);
260
261 auto endTime = std::chrono::high_resolution_clock::now();
262 double elapsedSec = std::chrono::duration<double>(endTime - startTime).count();
263
264 writeOutputCSV(busData);
265
266 OutputFile::writeOutputFile(jobName, inputFile, solverName, formatName,
267 busData, branchData, Y, totalIterations, finalError, tolerance, elapsedSec);
268
269 OutputFile::writeStatusFile(jobName, inputFile, solverName, formatName,
270 N, nBranch, totalIterations, finalError, tolerance, finalConverged, elapsedSec);
271
272 OutputFile::writeDatFile(jobName, inputFile, solverName, formatName,
273 busData, branchData, iterationHistory, totalIterations, finalError,
274 tolerance, finalConverged, elapsedSec);
275
276 OutputFile::writeMessageFile(jobName, solverName, iterationHistory, tolerance, finalConverged);
277
278 fmt::print("\n");
279 fmt::print(fg(Display::LOGO_COLOR) | fmt::emphasis::bold,
280 " THE ANALYSIS HAS BEEN COMPLETED SUCCESSFULLY\n");
281 fmt::print("\n");
282 fmt::print(" Elapsed time : {:.3f} sec\n", elapsedSec);
283 fmt::print("\n");
284
285 return 0;
286}
Eigen::MatrixXcd computeAdmittanceMatrix(const BusData &busData, const BranchData &branchData)
Computes the complex bus admittance matrix ($$ Y_{bus} $$).
Definition Admittance.C:32
Declaration of functions and classes for constructing the bus admittance matrix ($$ Y_{bus} $$) in po...
Command-line argument parsing utilities for deltaFlow.
InputFormat
Supported input file formats.
Definition Argparse.H:50
@ IEEE
IEEE Common Data Format (.cdf, .txt)
@ PSSE
PSS/E Raw Data Format (.raw)
SolverType
Types of solvers supported by deltaFlow.
Definition Argparse.H:41
@ NewtonRaphson
Newton-Raphson iterative method.
@ GaussSeidel
Gauss-Seidel iterative method.
Display and formatting utilities for terminal and file output.
Declaration of the Gauss-Seidel load flow solver for power system analysis.
IEEE Common Data Format reader for deltaFlow.
Logger utilities for deltaFlow, providing logging macros and a singleton Logger class.
#define LOG_INFO(msg,...)
Macro for logging an info-level message.
Definition Logger.H:86
#define LOG_DEBUG(msg,...)
Macro for logging a debug-level message.
Definition Logger.H:85
#define LOG_ERROR(msg,...)
Macro for logging an error-level message.
Definition Logger.H:88
Declaration of the Newton-Raphson load flow solver for power system analysis.
Generates professionally formatted output files (.out, .sta)
PSS/E Raw Data Format reader for deltaFlow.
bool checkQlimits(const Eigen::VectorXd &V, const Eigen::VectorXd &delta, Eigen::VectorXi &type_bus, const Eigen::MatrixXd &G, const Eigen::MatrixXd &B, BusData &busData, const std::vector< int > &pv_bus_id, int n_bus)
Checks reactive power limits on PV buses after solver convergence.
Definition Qlim.C:34
Reactive power limit (Q-limit) checking for PV buses.
Abstract base class for power system data file readers.
void dispBusData(const BusData &busData)
Displays bus data in a human-readable format.
Definition Writer.C:36
void dispLineFlow(const BusData &busData, const BranchData &branchData, const Eigen::MatrixXcd &Y, double basemva)
Displays the line flow results, including power flow and losses.
Definition Writer.C:76
bool writeOutputCSV(const BusData &busData)
Writes bus data results to a CSV file.
Definition Writer.C:192
Output utilities for writing power system analysis results to CSV files.
Parses and stores command-line arguments for deltaFlow.
Definition Argparse.H:62
InputFormat getInputFormat() const noexcept
Get the input file format.
Definition Argparse.C:154
std::string getJobName() const noexcept
Get the job name.
Definition Argparse.C:134
double getTolerance() const noexcept
Get the convergence tolerance ($$ \epsilon $$).
Definition Argparse.C:138
SolverType getSolverType() const noexcept
Get the solver type.
Definition Argparse.C:150
std::string getInputFile() const noexcept
Get the input CDF file path.
Definition Argparse.C:130
double getRelaxationCoefficient() const noexcept
Get the relaxation coefficient ($$ \omega $$).
Definition Argparse.C:146
int getMaxIterations() const noexcept
Get the maximum number of iterations ($$ N_{max} $$).
Definition Argparse.C:142
int main(int argc, char *argv[])
Definition main.C:46
constexpr fmt::rgb LOGO_COLOR
deltaFlow logo color
Definition Display.H:50
void printTerminalBanner()
Prints the full colored banner to terminal.
Definition Display.H:313
bool writeOutputFile(const std::string &jobName, const std::string &inputFile, const std::string &solverName, const std::string &formatName, const BusData &busData, const BranchData &branchData, const Eigen::MatrixXcd &Y, int iterations, double finalError, double tolerance, double elapsedSec, double basemva=100.0)
Writes the main output file (.out) with full analysis results.
Definition OutputFile.H:112
bool writeDatFile(const std::string &jobName, const std::string &inputFile, const std::string &solverName, const std::string &formatName, const BusData &busData, const BranchData &branchData, const std::vector< std::pair< int, double > > &iterationHistory, int totalIterations, double finalError, double tolerance, bool converged, double elapsedSec, double basemva=100.0)
Writes the detailed data file (.dat) with full input/output records.
Definition OutputFile.H:457
bool writeStatusFile(const std::string &jobName, const std::string &inputFile, const std::string &solverName, const std::string &formatName, int nBus, int nBranch, int iterations, double finalError, double tolerance, bool converged, double elapsedSec)
Writes the status file (.sta) with a compact solver summary.
Definition OutputFile.H:314
bool writeMessageFile(const std::string &jobName, const std::string &solverName, const std::vector< std::pair< int, double > > &iterationHistory, double tolerance, bool converged)
Writes the message file (.msg) with iteration history.
Definition OutputFile.H:397