1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! The `gridoxide rao` subcommand's `--parameters` flag.
//!
//! `main.rs`'s argument handling is hand-rolled, so it is pinned here by
//! driving the built binary rather than calling into the library.
//!
//! The flag exists because the optimizer could be told to minimize cost, hold
//! MNECs, run a second preventive pass or measure in amperes — and the binary
//! could ask for none of it. The Cucumber gate validated 1862 assertions' worth
//! of behaviour no user could reach.
use std::process::{Command, Output};
mod openrao;
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_gridoxide"))
.args(args)
.output()
.expect("run gridoxide")
}
fn stdout(output: &Output) -> String {
String::from_utf8_lossy(&output.stdout).into_owned()
}
/// A `RaoParameters` document changes the answer, and changes it to the
/// reference's own.
///
/// This is the reference's scenario 3.4.1.1 — "selection of cheapest of 3
/// equivalent network actions" — and it is built so the two objectives cannot
/// agree. Three actions each secure the network; the cheapest buys the least
/// room. So the test is a **pair**: asserting only that `--parameters` produces
/// `closeBeFr4` would pass just as well if the flag did nothing and the default
/// happened to pick it.
#[test]
fn rao_parameters_select_the_objective() {
let network = openrao::path("files/cases/epic92/2Nodes4ParallelLines.uct");
let crac = openrao::path("files/crac/epic92/crac-92-1-1.json");
let config = openrao::path("files/configurations/epic92/RaoParameters_dc_minObjective.json");
// Max-min-margin, the default: buy the most room, whatever it costs.
let margin = stdout(&run(&[
"rao",
network.to_str().unwrap(),
"--crac",
crac.to_str().unwrap(),
]));
assert!(margin.contains("closeBeFr2"), "default run was:\n{margin}");
assert!(margin.contains("closeBeFr3"), "default run was:\n{margin}");
// MIN_COST: the reference asserts `closeBeFr4` alone, for a margin of 250 —
// less room than the two-action answer above, and cheaper.
let cost = stdout(&run(&[
"rao",
network.to_str().unwrap(),
"--crac",
crac.to_str().unwrap(),
"--parameters",
config.to_str().unwrap(),
]));
assert!(cost.contains("closeBeFr4"), "costly run was:\n{cost}");
assert!(!cost.contains("closeBeFr2"), "costly run was:\n{cost}");
assert!(!cost.contains("closeBeFr3"), "costly run was:\n{cost}");
assert!(cost.contains("250.0 MW"), "costly run was:\n{cost}");
}
/// A `--parameters` path that does not exist is an invocation error, not a
/// silent fall back to the defaults.
///
/// Falling back is the failure this whole subcommand is built against: a study
/// that asked for MIN_COST and got max-min-margin has no way to tell from its
/// own output.
#[test]
fn a_missing_parameters_file_is_refused() {
let network = openrao::path("files/cases/epic92/2Nodes4ParallelLines.uct");
let crac = openrao::path("files/crac/epic92/crac-92-1-1.json");
let out = run(&[
"rao",
network.to_str().unwrap(),
"--crac",
crac.to_str().unwrap(),
"--parameters",
"/nonexistent/RaoParameters.json",
]);
assert_eq!(out.status.code(), Some(2), "a bad invocation exits 2");
}
/// `--depth` beats the file, because a flag the user typed should beat a file
/// they pointed at.
#[test]
fn an_explicit_depth_wins_over_the_configuration() {
let network = openrao::path("files/cases/epic92/2Nodes4ParallelLines.uct");
let crac = openrao::path("files/crac/epic92/crac-92-1-1.json");
let config = openrao::path("files/configurations/epic92/RaoParameters_dc_minObjective.json");
let out = run(&[
"rao",
network.to_str().unwrap(),
"--crac",
crac.to_str().unwrap(),
"--parameters",
config.to_str().unwrap(),
"--depth",
"1",
]);
assert_eq!(out.status.code(), Some(0), "{}", stdout(&out));
}