update
This commit is contained in:
commit
7e494f552b
|
@ -0,0 +1,16 @@
|
|||
SOURCES = $(wildcard src/*.cpp)
|
||||
OBJECTS = $(SOURCES:%.cpp=%.o)
|
||||
APPLICATION = arma-flow
|
||||
CXXFLAGS = -Wall -c -O2 -std=c++17 -o
|
||||
LDFLAGS = -larmadillo -loptions -lstdc++fs
|
||||
|
||||
all: ${OBJECTS} ${APPLICATION}
|
||||
|
||||
${APPLICATION}: ${OBJECTS}
|
||||
${CXX} -o $@ ${OBJECTS} ${LDFLAGS}
|
||||
|
||||
${OBJECTS}:
|
||||
${CXX} ${CXXFLAGS} $@ ${@:%.o=%.cpp}
|
||||
|
||||
clean:
|
||||
rm -f ${APPLICATION} ${OBJECTS}
|
|
@ -0,0 +1,18 @@
|
|||
# arma-flow
|
||||
|
||||
A simple power flow calculator using Newton's method.
|
||||
|
||||
## 1. Requirements
|
||||
|
||||
* A newer version of [Armadillo](http://arma.sourceforge.net/)
|
||||
* Tested on 8.400.0
|
||||
|
||||
* The [Options](https://mulholland.xyz/docs/options/) library
|
||||
|
||||
* [RapidJSON]() for JSON output
|
||||
|
||||
* Compiler with supports C++17
|
||||
|
||||
## 2. Usage
|
||||
|
||||
See `arma-flow --help`.
|
|
@ -0,0 +1,66 @@
|
|||
#include "args.hpp"
|
||||
|
||||
namespace flow
|
||||
{
|
||||
args::args() : arg_parser_(
|
||||
"A simple power flow calculator using Newton's method.\n"
|
||||
"usage: arma-flow [--version] [-h | --help] [-o <output_file>]\n"
|
||||
" -n <node_data_file> -e <edge_data_file> [-r]\n"
|
||||
" [-i <max_iterations>] [-a <accuracy>]\n"
|
||||
" [-v | --verbose]",
|
||||
"arma-flow version 0.0.1")
|
||||
{
|
||||
arg_parser_.newString("o", "result");
|
||||
arg_parser_.newString("n");
|
||||
arg_parser_.newString("e");
|
||||
arg_parser_.newFlag("r");
|
||||
arg_parser_.newInt("i", 100);
|
||||
arg_parser_.newDouble("a", 0.00001);
|
||||
arg_parser_.newFlag("verbose v");
|
||||
}
|
||||
|
||||
bool args::input_file_path(std::string& nodes, std::string& edges)
|
||||
{
|
||||
if (!arg_parser_.found("n") || !arg_parser_.found("e"))
|
||||
return false;
|
||||
nodes = arg_parser_.getString("n");
|
||||
edges = arg_parser_.getString("e");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool args::output_file_path(std::string& output)
|
||||
{
|
||||
output = arg_parser_.getString("o");
|
||||
return arg_parser_.found("o");
|
||||
}
|
||||
|
||||
bool args::remove_first_line()
|
||||
{
|
||||
return arg_parser_.getFlag("r");
|
||||
}
|
||||
|
||||
int args::max_iterations()
|
||||
{
|
||||
return arg_parser_.getInt("i");
|
||||
}
|
||||
|
||||
double args::accuracy()
|
||||
{
|
||||
return arg_parser_.getDouble("a");
|
||||
}
|
||||
|
||||
bool args::verbose()
|
||||
{
|
||||
return arg_parser_.getFlag("v");
|
||||
}
|
||||
|
||||
void args::help()
|
||||
{
|
||||
arg_parser_.exitHelp();
|
||||
}
|
||||
|
||||
void args::parse(int argc, char** argv)
|
||||
{
|
||||
arg_parser_.parse(argc, argv);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <options.h>
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class args
|
||||
{
|
||||
options::ArgParser arg_parser_;
|
||||
|
||||
public:
|
||||
explicit args();
|
||||
|
||||
void parse(int argc, char** argv);
|
||||
|
||||
bool input_file_path(std::string& nodes, std::string& edges);
|
||||
|
||||
bool output_file_path(std::string& output);
|
||||
|
||||
bool remove_first_line();
|
||||
|
||||
int max_iterations();
|
||||
|
||||
double accuracy();
|
||||
|
||||
bool verbose();
|
||||
|
||||
void help();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#include "calc.hpp"
|
||||
#include "executor.hpp"
|
||||
|
||||
namespace flow
|
||||
{
|
||||
std::complex<double> calc::edge_data::impedance() const
|
||||
{
|
||||
const auto deno = r * r + x * x;
|
||||
return { r / deno, -x / deno };
|
||||
}
|
||||
|
||||
std::complex<double> calc::edge_data::grounding_admittance() const
|
||||
{
|
||||
return { 0, b };
|
||||
}
|
||||
|
||||
void calc::init(const arma::dmat& nodes, const arma::dmat& edges,
|
||||
int max_iterations, double accuracy)
|
||||
{
|
||||
if (max_iterations <= 0)
|
||||
executor::print_and_exit("Bad number of iterations. Positive integer expected.");
|
||||
if (accuracy < 0 || accuracy > 1)
|
||||
executor::print_and_exit("Invalid accuracy.");
|
||||
if (nodes.n_cols != 5 || edges.n_cols != 6)
|
||||
executor::print_and_exit("Bad input matrix format.");
|
||||
nodes.each_row([this](const arma::rowvec& row)
|
||||
{
|
||||
using node_type = node_data::node_type;
|
||||
auto type = node_type::swing;
|
||||
switch (static_cast<unsigned>(row[4]))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
type = node_type::pq;
|
||||
break;
|
||||
case 2:
|
||||
type = node_type::pv;
|
||||
break;
|
||||
default:
|
||||
executor::print_and_exit("Bad node type.");
|
||||
}
|
||||
nodes_.push_back({
|
||||
node_num_++, row[0], row[1], row[2], row[3], type
|
||||
});
|
||||
});
|
||||
edges.each_row([this](const arma::rowvec& row)
|
||||
{
|
||||
auto n1 = static_cast<unsigned>(row[0]) - 1;
|
||||
auto n2 = static_cast<unsigned>(row[1]) - 1;
|
||||
if (n1 >= node_num_ || n2 >= node_num_)
|
||||
executor::print_and_exit("Bad node offset.");
|
||||
edges_.push_back({
|
||||
&nodes_[n1], &nodes_[n2], row[2], row[3], row[4], row[5]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void calc::node_admittance()
|
||||
{
|
||||
arma::cx_mat node_adm_cplx(node_num_, node_num_, arma::fill::zeros);
|
||||
for (auto&& edge : edges_) {
|
||||
const auto n1 = edge.n1->offset;
|
||||
const auto n2 = edge.n2->offset;
|
||||
const auto impedance = edge.impedance();
|
||||
// Whether this edge has transformer.
|
||||
if (edge.k) {
|
||||
node_adm_cplx.at(n1, n1) += impedance;
|
||||
node_adm_cplx.at(n2, n2) += impedance / (edge.k * edge.k);
|
||||
node_adm_cplx.at(n1, n2) -= impedance / edge.k;
|
||||
node_adm_cplx.at(n2, n1) -= impedance / edge.k;
|
||||
}
|
||||
else {
|
||||
const auto delta_diag = impedance + edge.grounding_admittance();
|
||||
node_adm_cplx.at(n1, n1) += delta_diag;
|
||||
node_adm_cplx.at(n2, n2) += delta_diag;
|
||||
node_adm_cplx.at(n1, n2) -= impedance;
|
||||
node_adm_cplx.at(n2, n1) -= impedance;
|
||||
}
|
||||
}
|
||||
node_adm_real_ = arma::real(node_adm_cplx);
|
||||
node_adm_imag_ = arma::imag(node_adm_cplx);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <armadillo>
|
||||
#include <vector>
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class calc
|
||||
{
|
||||
struct node_data
|
||||
{
|
||||
unsigned long offset;
|
||||
double v, g, p, q;
|
||||
enum node_type {
|
||||
pq, pv, swing
|
||||
} type;
|
||||
};
|
||||
|
||||
struct edge_data
|
||||
{
|
||||
node_data *n1, *n2;
|
||||
double r, x, b, k;
|
||||
std::complex<double> impedance() const;
|
||||
std::complex<double> grounding_admittance() const;
|
||||
};
|
||||
|
||||
std::vector<node_data> nodes_;
|
||||
|
||||
unsigned long node_num_ = 0;
|
||||
|
||||
std::vector<edge_data> edges_;
|
||||
|
||||
arma::dmat node_adm_real_;
|
||||
|
||||
arma::dmat node_adm_imag_;
|
||||
|
||||
public:
|
||||
explicit calc() = default;
|
||||
|
||||
void init(const arma::dmat& nodes, const arma::dmat& edges,
|
||||
int max_iterations, double accuracy);
|
||||
|
||||
void node_admittance();
|
||||
|
||||
arma::dmat* get_node_adm_real()
|
||||
{
|
||||
return &node_adm_real_;
|
||||
}
|
||||
|
||||
arma::dmat* get_node_adm_imag()
|
||||
{
|
||||
return &node_adm_imag_;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include "executor.hpp"
|
||||
#include "factory.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
namespace flow
|
||||
{
|
||||
void executor::print_and_exit(const std::string& message)
|
||||
{
|
||||
std::cout << message << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void executor::execute(int argc, char** argv) const
|
||||
{
|
||||
auto factory = factory::get();
|
||||
auto args = factory->get_args();
|
||||
args->parse(argc, argv);
|
||||
std::string path_to_nodes, path_to_edges;
|
||||
if (!args->input_file_path(path_to_nodes, path_to_edges))
|
||||
args->help();
|
||||
auto input = factory->get_input();
|
||||
const auto remove = args->remove_first_line();
|
||||
if (!input->from_csv_file(path_to_nodes, remove))
|
||||
print_and_exit("Failed to read edge data from file.");
|
||||
const auto nodes = input->get_mat();
|
||||
if (!input->from_csv_file(path_to_edges, remove))
|
||||
print_and_exit("Failed to read edge data from file.");
|
||||
const auto edges = input->get_mat();
|
||||
auto calc = factory->get_calc();
|
||||
calc->init(nodes, edges, args->max_iterations(), args->accuracy());
|
||||
calc->node_admittance();
|
||||
const auto node_adm_real = calc->get_node_adm_real();
|
||||
const auto node_adm_imag = calc->get_node_adm_imag();
|
||||
const auto verbose = args->verbose();
|
||||
if (verbose) {
|
||||
std::cout << "Real part of node admittance matrix:" << std::endl;
|
||||
output::print_dmat(*node_adm_real);
|
||||
std::cout << "Imaginary part of node admittance matrix:" << std::endl;
|
||||
output::print_dmat(*node_adm_imag);
|
||||
}
|
||||
std::string output_path;
|
||||
if (!args->output_file_path(output_path))
|
||||
std::cout << "Output file path not specified. Using result-*.csv by default." << std::endl;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class executor
|
||||
{
|
||||
public:
|
||||
explicit executor() = default;
|
||||
|
||||
static void print_and_exit(const std::string& message);
|
||||
|
||||
void execute(int argc, char** argv) const;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "input.hpp"
|
||||
#include "args.hpp"
|
||||
#include "executor.hpp"
|
||||
#include "calc.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class factory
|
||||
{
|
||||
input input_;
|
||||
|
||||
args args_;
|
||||
|
||||
executor executor_;
|
||||
|
||||
calc calc_;
|
||||
|
||||
output output_;
|
||||
|
||||
static factory singleton_;
|
||||
|
||||
explicit factory() = default;
|
||||
|
||||
public:
|
||||
|
||||
static factory* get()
|
||||
{
|
||||
return &singleton_;
|
||||
}
|
||||
|
||||
input* get_input()
|
||||
{
|
||||
return &input_;
|
||||
}
|
||||
|
||||
args* get_args()
|
||||
{
|
||||
return &args_;
|
||||
}
|
||||
|
||||
executor* get_executor()
|
||||
{
|
||||
return &executor_;
|
||||
}
|
||||
|
||||
calc* get_calc()
|
||||
{
|
||||
return &calc_;
|
||||
}
|
||||
|
||||
output* get_output()
|
||||
{
|
||||
return &output_;
|
||||
}
|
||||
};
|
||||
|
||||
inline factory factory::singleton_;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#include <experimental/filesystem>
|
||||
|
||||
#include "input.hpp"
|
||||
|
||||
namespace flow
|
||||
{
|
||||
bool input::from_csv_file(const std::string& path, bool remove_first_line)
|
||||
{
|
||||
std::ifstream ifstream;
|
||||
ifstream.exceptions(std::ifstream::failbit);
|
||||
namespace fs = std::experimental::filesystem;
|
||||
try {
|
||||
ifstream.open(path[0] == '/' ? path : fs::current_path().string() + '/' + path);
|
||||
if (remove_first_line)
|
||||
ifstream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
return do_read(ifstream);
|
||||
}
|
||||
|
||||
const arma::dmat& input::get_mat() const
|
||||
{
|
||||
return mat_;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <armadillo>
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class input
|
||||
{
|
||||
/// The loaded matrix.
|
||||
arma::dmat mat_;
|
||||
|
||||
/**
|
||||
* Read matrix from an istream.
|
||||
*
|
||||
* @param istream The istream to be read.
|
||||
* @return Whether read is successful.
|
||||
*/
|
||||
bool do_read(std::istream& istream)
|
||||
{
|
||||
mat_.clear();
|
||||
return mat_.load(istream, arma::csv_ascii);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Read matrix from a CSV file. The first line will be ignored.
|
||||
*
|
||||
* @param path Path to file.
|
||||
* @param remove_first_line Whether to remove the first line of CSV file.
|
||||
* @return Whether the file is successfully read.
|
||||
*/
|
||||
bool from_csv_file(const std::string& path, bool remove_first_line);
|
||||
|
||||
/**
|
||||
* Get loaded matrix.
|
||||
*/
|
||||
const arma::dmat& get_mat() const;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include "factory.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
flow::factory::get()->get_executor()->execute(argc, argv);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#include <iomanip>
|
||||
|
||||
#include "output.hpp"
|
||||
#include "executor.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
namespace flow
|
||||
{
|
||||
int output::max_elems_per_line()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
|
||||
return 10;
|
||||
const auto width = csbi.dwSize.X;
|
||||
#else
|
||||
winsize win;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
|
||||
const auto width = win.ws_col;
|
||||
#endif // _WIN32
|
||||
return std::floor((width - 7) / 11);
|
||||
}
|
||||
|
||||
void output::print_dmat(const arma::dmat& mat)
|
||||
{
|
||||
mat.each_row([](const arma::rowvec& row)
|
||||
{
|
||||
std::cout << std::setprecision(7) << std::left;
|
||||
auto counter = 0;
|
||||
auto elems = max_elems_per_line();
|
||||
for (auto&& elem : row)
|
||||
{
|
||||
if (++counter > elems) {
|
||||
std::cout << "...(" << row.n_elem - elems << ')';
|
||||
break;
|
||||
}
|
||||
std::cout << std::setw(10) << elem;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <armadillo>
|
||||
|
||||
|
||||
namespace flow
|
||||
{
|
||||
class output
|
||||
{
|
||||
static int max_elems_per_line();
|
||||
public:
|
||||
|
||||
static void print_dmat(const arma::dmat& mat);
|
||||
|
||||
};
|
||||
}
|
Reference in New Issue