Add write to file. Finish readme. Add examples.

This commit is contained in:
CismonX 2018-04-09 14:15:16 +08:00
parent 81ea918884
commit 544a7341b9
12 changed files with 311 additions and 41 deletions

View File

@ -11,6 +11,67 @@ A simple power flow calculator using Newton's method.
* Compiler with supports C++17
## 2. Usage
## 2. Documentation
See `arma-flow --help`.
### 2.1 CLI options
* `-h | --help` : Prints the usage of arma-flow and exit.
* `--version` : Prints the version of arma-flow and exit.
* `-o <output_file_prefix>` : Prefix to output file. (Can be relative path)
* `-n <node_data_file>` : Path to node data file. (Can be relative path)
* `-e <edge_data_file>` : Path to edge data file. (Can be relative path)
* `-r` : Remove first line of input CSV files before parsing.
* `-i <max_iterations>` : Max number of iterations to be performed before aborting.
* `-a <accuracy>` : Max deviation to be tolerated.
* `-v | --verbose` : Output more text to STDOUT.
For example:
```bash
# Building arma-flow
git clone https://github.com/CismonX/arma-flow.git
cd arma-flow
make -j4
# Testing arma-flow
./arma-flow -n examples/3-gen-9-nodes/nodes.csv -e examples/3-gen-9-nodes/edges.csv -r
```
### 2.2 Input
Input node data file and edge data file should be of CSV format. Definition of each column is shown below:
#### 2.2.1 Node data file
* Node voltage (PV nodes and swing node)
* Generator power (active power, PV nodes)
* Load power (active power, PQ and PV nodes)
* Load power (reactive power, PQ nodes)
* Node type (0 - swing node, 1- PQ node, 2 - PV node)
#### 2.2.2 Edge data file
* First node id (equal to node data row offset, start at 0)
* Second node id (equal to node data row offset, start at 0)
* Resistance (real) -- R
* Resistance (imaginary) -- X
* Gounding admittance (divided by two) -- B/2
* Transformer ratio -- k
### 2.3 Output
The node admittance matrix and result of power flow calculation of the given system will be written to CSV files. If in verbose mode, some temporary data during calculation is printed to STDOUT.
* Note that the nodes are sorted before calculation, sequence of nodes in some verbose output may not be the same as input file.
#### 2.3.1 Node admittance matrix
The real part and imaginary part of node admittance matrix will be printed to "\<prefix\>node-admittance-real.csv" and "\<prefix\>node-admittance-imag.csv".
#### 2.3.2 Calculation result
The result of power flow calculation will be printed to "\<prefix\>result.csv". The definition of each column is given below:
* Node voltage
* Phase angle of node voltage (Radian)
* Node power (active)
* Node power (reactive)

View File

@ -0,0 +1,10 @@
V1,V2,R,X,B,k
1,4,0,0.0576,0,1
2,7,0,0.0625,0,1
3,9,0,0.0586,0,1
4,5,0.01,0.085,0.088,0
4,6,0.017,0.092,0.079,0
5,7,0.032,0.161,0.153,0
6,9,0.039,0.17,0.179,0
7,8,0.0085,0.072,0.0745,0
8,9,0.0119,0.1008,0.1045,0
1 V1 V2 R X B k
2 1 4 0 0.0576 0 1
3 2 7 0 0.0625 0 1
4 3 9 0 0.0586 0 1
5 4 5 0.01 0.085 0.088 0
6 4 6 0.017 0.092 0.079 0
7 5 7 0.032 0.161 0.153 0
8 6 9 0.039 0.17 0.179 0
9 7 8 0.0085 0.072 0.0745 0
10 8 9 0.0119 0.1008 0.1045 0

View File

@ -0,0 +1,10 @@
U,Generator,P,Q,type
1.04,0,0,0,0
1.025,1.63,0,0,2
1.025,0.85,0,0,2
1,0,0,0,1
1,0,1.25,0.5,1
1,0,0.9,0.3,1
1,0,0,0,1
1,0,1,0.35,1
1,0,0,0,1
1 U Generator P Q type
2 1.04 0 0 0 0
3 1.025 1.63 0 0 2
4 1.025 0.85 0 0 2
5 1 0 0 0 1
6 1 0 1.25 0.5 1
7 1 0 0.9 0.3 1
8 1 0 0 0 1
9 1 0 1 0.35 1
10 1 0 0 0 1

View File

@ -0,0 +1,47 @@
V1,V2,R,X,B,k
1,2,0.0035,0.0411,0.6987,0
1,39,0.0010,0.0250,0.7500,0
2,3,0.0013,0.0151,0.2572,0
2,25,0.0070,0.0086,0.1460,0
3,4,0.0013,0.0213,0.2214,0
3,18,0.0011,0.0133,0.2138,0
4,5,0.0008,0.0128,0.1342,0
4,14,0.0008,0.0129,0.1382,0
5,6,0.0002,0.0026,0.0434,0
5,8,0.0008,0.0112,0.1476,0
6,7,0.0006,0.0092,0.1130,0
6,11,0.0007,0.0082,0.1389,0
7,8,0.0004,0.0046,0.0780,0
8,9,0.0023,0.0363,0.3804,0
9,39,0.0010,0.0250,1.2000,0
10,11,0.0004,0.0043,0.0729,0
10,13,0.0004,0.0043,0.0729,0
13,14,0.0009,0.0101,0.1723,0
14,15,0.0018,0.0217,0.3660,0
15,16,0.0009,0.0094,0.1710,0
16,17,0.0007,0.0089,0.1342,0
16,19,0.0016,0.0195,0.3040,0
16,21,0.0008,0.0135,0.2548,0
16,24,0.0003,0.0059,0.0680,0
17,18,0.0007,0.0082,0.1319,0
17,27,0.0013,0.0713,0.3216,0
21,22,0.0008,0.0140,0.2565,0
22,23,0.0006,0.0096,0.1846,0
23,24,0.0022,0.0350,0.3610,0
25,26,0.0032,0.0323,0.5130,0
26,27,0.0014,0.0147,0.2396,0
26,28,0.0043,0.0474,0.7802,0
26,29,0.0057,0.0625,1.0290,0
28,29,0.0014,0.0151,0.2490,0
12,11,0.0016,0.0435,0,1.006
12,13,0.0016,0.0435,0,1.006
6,31,0,0.0250,0,1.070
10,32,0,0.02,0,1.070
19,33,0.0007,0.0142,0,1.070
20,34,0.0009,0.0180,0,1.009
22,35,0,0.0143,0,1.025
23,36,0.0005,0.0272,0,1.000
25,37,0.0006,0.0232,0,1.025
2,30,0.0006,0.0232,0,1.025
29,38,0.0008,0.0156,0,1.025
19,20,0.0007,0.0138,0,1.060
1 V1 V2 R X B k
2 1 2 0.0035 0.0411 0.6987 0
3 1 39 0.0010 0.0250 0.7500 0
4 2 3 0.0013 0.0151 0.2572 0
5 2 25 0.0070 0.0086 0.1460 0
6 3 4 0.0013 0.0213 0.2214 0
7 3 18 0.0011 0.0133 0.2138 0
8 4 5 0.0008 0.0128 0.1342 0
9 4 14 0.0008 0.0129 0.1382 0
10 5 6 0.0002 0.0026 0.0434 0
11 5 8 0.0008 0.0112 0.1476 0
12 6 7 0.0006 0.0092 0.1130 0
13 6 11 0.0007 0.0082 0.1389 0
14 7 8 0.0004 0.0046 0.0780 0
15 8 9 0.0023 0.0363 0.3804 0
16 9 39 0.0010 0.0250 1.2000 0
17 10 11 0.0004 0.0043 0.0729 0
18 10 13 0.0004 0.0043 0.0729 0
19 13 14 0.0009 0.0101 0.1723 0
20 14 15 0.0018 0.0217 0.3660 0
21 15 16 0.0009 0.0094 0.1710 0
22 16 17 0.0007 0.0089 0.1342 0
23 16 19 0.0016 0.0195 0.3040 0
24 16 21 0.0008 0.0135 0.2548 0
25 16 24 0.0003 0.0059 0.0680 0
26 17 18 0.0007 0.0082 0.1319 0
27 17 27 0.0013 0.0713 0.3216 0
28 21 22 0.0008 0.0140 0.2565 0
29 22 23 0.0006 0.0096 0.1846 0
30 23 24 0.0022 0.0350 0.3610 0
31 25 26 0.0032 0.0323 0.5130 0
32 26 27 0.0014 0.0147 0.2396 0
33 26 28 0.0043 0.0474 0.7802 0
34 26 29 0.0057 0.0625 1.0290 0
35 28 29 0.0014 0.0151 0.2490 0
36 12 11 0.0016 0.0435 0 1.006
37 12 13 0.0016 0.0435 0 1.006
38 6 31 0 0.0250 0 1.070
39 10 32 0 0.02 0 1.070
40 19 33 0.0007 0.0142 0 1.070
41 20 34 0.0009 0.0180 0 1.009
42 22 35 0 0.0143 0 1.025
43 23 36 0.0005 0.0272 0 1.000
44 25 37 0.0006 0.0232 0 1.025
45 2 30 0.0006 0.0232 0 1.025
46 29 38 0.0008 0.0156 0 1.025
47 19 20 0.0007 0.0138 0 1.060

View File

@ -0,0 +1,40 @@
U,Generator,P,Q,type
1,0,0,0,1
1,0,0,0,1
1,0,3.22,0.024,1
1,0,5,1.84,1
1,0,0,0,1
1,0,0,0,1
1,0,2.338,0.84,1
1,0,5.22,1.76,1
1,0,0,0,1
1,0,0,0,1
1,0,0,0,1
1,0,0.075,0.88,1
1,0,0,0,1
1,0,0,0,1
1,0,3.2,1.53,1
1,0,3.29,0.323,1
1,0,0,0,1
1,0,1.58,0.3,1
1,0,0,0,1
1,0,6.28,1.03,1
1,0,2.47,1.15,1
1,0,0,0,1
1,0,2.47,0.846,1
1,0,3.08,-0.92,1
1,0,2.24,0.472,1
1,0,1.39,0.17,1
1,0,2.81,0.755,1
1,0,2.06,0.276,1
1,0,2.835,0.269,1
1.0475,2.50,0,0,2
0.9820,0,0.092,0.046,0
0.9831,6.50,0,0,2
0.9972,6.32,0,0,2
1.0123,5.08,0,0,2
1.0493,6.50,0,0,2
1.0635,5.60,0,0,2
1.0278,5.40,0,0,2
1.0265,8.30,0,0,2
1.0300,10.00,11.04,2.5,2
1 U Generator P Q type
2 1 0 0 0 1
3 1 0 0 0 1
4 1 0 3.22 0.024 1
5 1 0 5 1.84 1
6 1 0 0 0 1
7 1 0 0 0 1
8 1 0 2.338 0.84 1
9 1 0 5.22 1.76 1
10 1 0 0 0 1
11 1 0 0 0 1
12 1 0 0 0 1
13 1 0 0.075 0.88 1
14 1 0 0 0 1
15 1 0 0 0 1
16 1 0 3.2 1.53 1
17 1 0 3.29 0.323 1
18 1 0 0 0 1
19 1 0 1.58 0.3 1
20 1 0 0 0 1
21 1 0 6.28 1.03 1
22 1 0 2.47 1.15 1
23 1 0 0 0 1
24 1 0 2.47 0.846 1
25 1 0 3.08 -0.92 1
26 1 0 2.24 0.472 1
27 1 0 1.39 0.17 1
28 1 0 2.81 0.755 1
29 1 0 2.06 0.276 1
30 1 0 2.835 0.269 1
31 1.0475 2.50 0 0 2
32 0.9820 0 0.092 0.046 0
33 0.9831 6.50 0 0 2
34 0.9972 6.32 0 0 2
35 1.0123 5.08 0 0 2
36 1.0493 6.50 0 0 2
37 1.0635 5.60 0 0 2
38 1.0278 5.40 0 0 2
39 1.0265 8.30 0 0 2
40 1.0300 10.00 11.04 2.5 2

View File

@ -10,13 +10,13 @@ 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"
"usage: arma-flow [--version] [-h | --help] [-o <output_file_prefix>]\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("o", "result-");
arg_parser_.newString("n");
arg_parser_.newString("e");
arg_parser_.newFlag("r");

View File

@ -136,37 +136,41 @@ namespace flow
epsilon_ = epsilon;
}
void calc::node_admittance()
std::pair<arma::dmat, arma::dmat> calc::node_admittance()
{
arma::cx_mat node_adm_cplx(num_nodes_, num_nodes_, arma::fill::zeros);
arma::cx_mat adm_orig(num_nodes_, num_nodes_, arma::fill::zeros);
for (auto&& edge : edges_) {
const auto impedance = edge.impedance();
const auto m = node_offset(edge.m);
const auto n = node_offset(edge.n);
// Whether this edge has transformer.
if (edge.k) {
node_adm_cplx.at(m, m) += impedance;
node_adm_cplx.at(n, n) += impedance / std::pow(edge.k, 2);
node_adm_cplx.at(m, n) -= impedance / edge.k;
node_adm_cplx.at(n, m) -= impedance / edge.k;
node_adm_cplx.at(m, m) += adm_orig(edge.m, edge.m) = impedance;
node_adm_cplx.at(n, n) += adm_orig(edge.n, edge.n) = impedance / std::pow(edge.k, 2);
node_adm_cplx.at(m, n) -= adm_orig(edge.m, edge.n) = impedance / edge.k;
node_adm_cplx.at(n, m) -= adm_orig(edge.n, edge.m) = impedance / edge.k;
} else {
const auto delta_diag = impedance + edge.grounding_admittance();
node_adm_cplx.at(m, m) += delta_diag;
node_adm_cplx.at(n, n) += delta_diag;
node_adm_cplx.at(m, n) -= impedance;
node_adm_cplx.at(n, m) -= impedance;
node_adm_cplx.at(m, m) += adm_orig(edge.m, edge.m) = delta_diag;
node_adm_cplx.at(n, n) += adm_orig(edge.n, edge.n) = delta_diag;
node_adm_cplx.at(m, n) -= adm_orig(edge.m, edge.n) = impedance;
node_adm_cplx.at(n, m) -= adm_orig(edge.n, edge.m) = impedance;
}
adj_.at(m, n) = 1;
adj_.at(n, m) = 1;
}
n_adm_g_ = arma::real(node_adm_cplx);
n_adm_b_ = arma::imag(node_adm_cplx);
const auto n_adm_orig_g = arma::real(adm_orig);
const auto n_adm_orig_b = arma::imag(adm_orig);
if (verbose_) {
writer::println("Real part of node admittance matrix:");
writer::print_mat(n_adm_g_);
writer::print_mat(n_adm_orig_g);
writer::println("Imaginary part of node admittance matrix:");
writer::print_mat(n_adm_b_);
writer::print_mat(n_adm_orig_b);
}
return { n_adm_orig_g, n_adm_orig_b };
}
void calc::iterate_init()
@ -262,6 +266,8 @@ namespace flow
unsigned calc::solve()
{
if (verbose_)
writer::println("Number of iterations: ", n_iter_, " (begin)");
jacobian();
prepare_solve();
const auto x_vec = spsolve(j_, f_x_, "superlu").col(0);
@ -280,8 +286,9 @@ namespace flow
writer::print_mat(f_.t());
}
update_f_x();
writer::println("Number of iterations: ", ++n_iter_);
return n_iter_;
if (verbose_)
writer::println("Number of iterations: ", n_iter_, " (end)");
return n_iter_++;
}
double calc::get_max() const
@ -297,27 +304,43 @@ namespace flow
arma::dmat calc::result()
{
p_[num_nodes_ - 1] = calc_p(num_nodes_ - 1);
for (auto&& elem : p_)
if (approx_zero(elem))
elem = 0;
vec_elem_foreach(delta_v_, [this](auto& elem, auto row)
{
auto i = row + num_pq_;
q_[i] = calc_q(i);
});
q_[num_nodes_ - 1] = calc_q(num_nodes_ - 1);
for (auto&& elem : q_)
if (approx_zero(elem))
elem = 0;
arma::colvec v(num_nodes_);
vec_elem_foreach(v, [this](auto& elem, auto col)
{
elem = std::sqrt(std::pow(e_[col], 2) + std::pow(f_[col], 2));
if (approx_zero(elem))
elem = 0;
});
arma::colvec theta(num_nodes_);
vec_elem_foreach(theta, [this](auto& elem, auto col)
vec_elem_foreach(theta, [this](auto& elem, auto row)
{
elem = std::atan(f_[col] / e_[col]);
elem = std::atan(f_[row] / e_[row]);
if (approx_zero(elem))
elem = 0;
});
arma::colvec node_id(num_nodes_);
vec_elem_foreach(node_id, [this](auto& elem, auto col)
vec_elem_foreach(node_id, [this](auto& elem, auto row)
{
elem = nodes_[col].id;
elem = nodes_[row].id;
});
return join_rows(node_id, join_rows(join_rows(p_, q_), join_rows(v, theta)));
arma::dmat retval = join_rows(node_id, join_rows(join_rows(v, theta), join_rows(p_, q_)));
arma::dmat sorted_retval(num_nodes_, 4);
retval.each_row([&sorted_retval](const arma::rowvec& row)
{
sorted_retval.row(row[0]) = row.subvec(1, row.n_elem - 1);
});
return sorted_retval;
}
}

View File

@ -121,10 +121,10 @@ namespace flow
bool verbose_ = false;
/// Max deviation to be tolerated.
double epsilon_;
double epsilon_ = 0;
/// Number of iterations.
unsigned n_iter_ = 0;
unsigned n_iter_ = 1;
/**
* Get offset of sorted node by ID.
@ -212,6 +212,12 @@ namespace flow
return -f_[row] * j_elem_a(row) - e_[row] * j_elem_c(row);
}
/// Check whether a value is approximately zero.
bool approx_zero(double val) const
{
return std::abs(val) <= epsilon_;
}
public:
/**
* Default constructor.
@ -227,7 +233,7 @@ namespace flow
/**
* Calculate node admittance.
*/
void node_admittance();
std::pair<arma::dmat, arma::dmat> node_admittance();
/**
* Initialize iteratiob.

View File

@ -1,10 +1,8 @@
//
// arma-flow/executor.cpp
//
// @author CismonX
//
#include <iostream>
// arma-flow/executor.cpp
//
// @author CismonX
//
#include "executor.hpp"
#include "factory.hpp"
@ -19,6 +17,7 @@ namespace flow
auto args = factory->get_args();
auto input = factory->get_reader();
auto calc = factory->get_calc();
auto writer = factory->get_writer();
// Parse options.
args->parse(argc, argv);
@ -48,10 +47,13 @@ namespace flow
std::string output_path;
if (!args->output_file_path(output_path) && verbose)
writer::notice("Output file path not specified. Defaulted to result-*.csv.");
writer->set_output_path_prefix(output_path);
// Initialize calculation.
calc->init(nodes, edges, verbose, epsilon);
calc->node_admittance();
const auto admittance = calc->node_admittance();
writer->to_csv_file("node-admittance-real.csv", admittance.first);
writer->to_csv_file("node-admittance-imag.csv", admittance.second);
calc->iterate_init();
// Do iteration.
@ -62,12 +64,13 @@ namespace flow
writer::println("Finished. Total number of iterations: ", num_iterations);
const auto result = calc->result();
if (verbose) {
writer::println("Result(n, P, Q, V, theta):");
writer::println("Result [V, theta(in rads), P, Q]:");
writer::print_mat(result);
}
break;
writer->to_csv_file("flow.csv", result, "V,theta,P,Q");
return;
}
} while (num_iterations < max);
writer::println("Exceeds max number of iterations. Aborted.");
writer::error("Exceeds max number of iterations. Aborted.");
}
}

View File

@ -25,11 +25,11 @@ namespace flow
? path : fs::current_path().string() + '/' + path);
if (remove_first_line)
ifstream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return do_read(ifstream);
}
catch (const std::exception&) {
return false;
}
return do_read(ifstream);
}
const arma::dmat& reader::get_mat() const

View File

@ -11,9 +11,9 @@
#include <unistd.h>
#endif // _WIN32
#include <iomanip>
#include <experimental/filesystem>
#include "writer.hpp"
#include "executor.hpp"
namespace flow
{
@ -32,22 +32,62 @@ namespace flow
return std::floor((width - 7) / 11);
}
std::string writer::double_to_string(double val)
{
auto str = std::to_string(val ? val : std::abs(val));
auto pos = str.find_last_not_of('0');
if (pos == 0 || str[pos] == '.')
--pos;
return str.substr(0, pos + 1);
}
void writer::print_mat(const arma::dmat& mat)
{
mat.each_row([](const arma::rowvec& row)
{
std::cout << std::setprecision(6) << std::left;
std::cout << std::left;
auto counter = 0;
auto elems = max_elems_per_line();
for (auto&& elem : row)
{
for (auto&& elem : row) {
if (++counter > elems) {
std::cout << "...(" << row.n_elem - elems << ')';
break;
}
std::cout << std::setw(10) << elem << ' ';
std::cout << std::setw(10) << double_to_string(elem) << ' ';
}
std::cout << std::endl;
});
}
bool writer::to_csv_file(const std::string& path, const arma::dmat& mat, const std::string& header)
{
std::ofstream ofstream;
ofstream.exceptions(std::ifstream::failbit);
namespace fs = std::experimental::filesystem;
try {
const auto real_path = output_path_prefix_ + path;
ofstream.open(
#ifdef _WIN32
real_path[1] == ':'
#else
real_path[0] == '/'
#endif // _WIN32
? real_path : fs::current_path().string() + '/' + real_path);
if (header.length())
ofstream << header << std::endl;
mat.each_row([&ofstream](const arma::rowvec& row)
{
for (auto col = 0U; col < row.n_elem; ++col) {
ofstream << double_to_string(row[col]);
if (col != row.n_elem - 1)
ofstream << ',';
}
ofstream << std::endl;
});
return true;
}
catch (const std::exception&) {
return false;
}
}
}

View File

@ -13,6 +13,9 @@ namespace flow
/// Provides write utilities.
class writer
{
/// Prefix of output file path.
std::string output_path_prefix_;
/**
* Determines width of stdout.
*
@ -20,6 +23,14 @@ namespace flow
*/
static int max_elems_per_line();
/**
* Convert double to pretty string.
*
* @param val Value to be converted.
* @return Converted string.
*/
static std::string double_to_string(double val);
public:
/**
* Print a line to stdout.
@ -55,11 +66,30 @@ namespace flow
exit(1);
}
/**
* Set output path prefix.
*
* @param prefix Output path prefix.
*/
void set_output_path_prefix(const std::string& prefix)
{
output_path_prefix_ = prefix;
}
/**
* Print a matrix to stdout.
*
* @param mat Matrix to be printed.
*/
static void print_mat(const arma::dmat& mat);
/**
* Write a matrix to a file in CSV format.
*
* @param path Path to CSV file.
* @param mat Matrix to be printed.
* @param header Header of CSV file
*/
bool to_csv_file(const std::string& path, const arma::dmat& mat, const std::string& header = "");
};
}