Skip to content

Commit 637fe5f

Browse files
Initial lambda extraction (#566)
* initial lambda extraction * older model migration and unify with new helper * improved config checks and parameter reader * reduce scope * Update GridKit/Testing/Testing.hpp Co-authored-by: superwhiskers <whiskerdev@protonmail.com> * empty overrides --------- Co-authored-by: lukelowry <lukelowry@users.noreply.github.com> Co-authored-by: superwhiskers <whiskerdev@protonmail.com>
1 parent c077b17 commit 637fe5f

67 files changed

Lines changed: 894 additions & 696 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
- Added `Bus::setFault` to apply or clear a fault to ground directly at a bus.
99
- Removed the `BusFault` class in favor of `Bus::setFault`.
1010
- Added `enable` and `disable` solver events that put a branch in or out of service.
11+
- `verify()` returns `Model::ConfigurationChecks` listing every configuration problem instead of an error count.
12+
- Added `Model::ParameterReader`
1113

1214
## v0.2
1315

‎GridKit/Model/CMakeLists.txt‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ add_subdirectory(PhasorDynamics)
99
add_subdirectory(PowerFlow)
1010
add_subdirectory(PowerElectronics)
1111

12-
install(FILES Evaluator.hpp VariableMonitor.hpp VariableMonitorController.hpp
13-
DESTINATION include/GridKit/Model)
12+
install(
13+
FILES ConfigurationChecks.hpp
14+
Evaluator.hpp
15+
ParameterReader.hpp
16+
VariableMonitor.hpp
17+
VariableMonitorController.hpp
18+
DESTINATION include/GridKit/Model)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @file ConfigurationChecks.hpp
3+
* @author Luke Lowery (lukel@tamu.edu)
4+
* @brief Collects the configuration problems found by a model.
5+
*/
6+
7+
#pragma once
8+
9+
#include <format>
10+
#include <string>
11+
#include <string_view>
12+
#include <vector>
13+
14+
namespace GridKit
15+
{
16+
namespace Model
17+
{
18+
/**
19+
* @brief Collects the configuration problems found by a model.
20+
*
21+
* A model's verify() fills one instance with a message for every
22+
* condition that does not hold and returns it. The caller decides how
23+
* the messages are reported; passed() is true when there are none.
24+
*/
25+
class [[nodiscard]] ConfigurationChecks
26+
{
27+
public:
28+
ConfigurationChecks() = default;
29+
30+
/// @param[in] model Model name that prefixes every recorded message.
31+
explicit ConfigurationChecks(std::string_view model)
32+
: prefix_(std::format("{}: ", model))
33+
{
34+
}
35+
36+
/// Record a problem.
37+
void fail(std::string_view message)
38+
{
39+
errors_.push_back(prefix_ + std::string(message));
40+
}
41+
42+
/// Record the message when the condition does not hold.
43+
void check(bool condition, std::string_view message)
44+
{
45+
if (!condition)
46+
{
47+
fail(message);
48+
}
49+
}
50+
51+
/// True when no check has failed.
52+
bool passed() const
53+
{
54+
return errors_.empty();
55+
}
56+
57+
/// Every recorded message, in the order the checks ran.
58+
const std::vector<std::string>& errors() const
59+
{
60+
return errors_;
61+
}
62+
63+
private:
64+
std::string prefix_;
65+
std::vector<std::string> errors_;
66+
};
67+
} // namespace Model
68+
} // namespace GridKit

‎GridKit/Model/ParameterReader.hpp‎

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* @file ParameterReader.hpp
3+
* @author Luke Lowery (lukel@tamu.edu)
4+
* @brief Typed parameter loading from model data containers.
5+
*/
6+
7+
#pragma once
8+
9+
#include <cmath>
10+
#include <format>
11+
#include <optional>
12+
#include <stdexcept>
13+
#include <string_view>
14+
#include <variant>
15+
16+
#include <magic_enum/magic_enum.hpp>
17+
18+
#include <GridKit/Constants.hpp>
19+
#include <GridKit/Model/PhasorDynamics/ModelData.hpp>
20+
21+
namespace GridKit
22+
{
23+
namespace Model
24+
{
25+
/**
26+
* @brief Reads typed parameters out of a model data container.
27+
*
28+
* A read method returns the provided value or std::nullopt when the
29+
* parameter is omitted. The model chooses a default or requires a value.
30+
* Invalid values are rejected with std::invalid_argument naming the
31+
* model and parameter. Real parameters accept integer values and must
32+
* be finite.
33+
*
34+
* @tparam ModelDataT A model data container satisfying
35+
* PhasorDynamics::ModelData.
36+
*/
37+
template <PhasorDynamics::ModelData ModelDataT>
38+
class ParameterReader
39+
{
40+
public:
41+
using RealT = typename ModelDataT::RealT;
42+
using IdxT = typename ModelDataT::IdxT;
43+
using ParametersT = typename ModelDataT::Parameters;
44+
45+
/**
46+
* @param[in] data Model data container to read from.
47+
* @param[in] model Model name used in rejection messages.
48+
*/
49+
ParameterReader(const ModelDataT& data, const char* model)
50+
: parameters_(data.parameters),
51+
model_(model)
52+
{
53+
}
54+
55+
/// Read an optional real parameter.
56+
[[nodiscard]] std::optional<RealT> readReal(ParametersT key) const
57+
{
58+
const auto* value = find(key);
59+
if (value == nullptr)
60+
{
61+
return std::nullopt;
62+
}
63+
64+
RealT real{};
65+
if (const auto* real_value = std::get_if<RealT>(value))
66+
{
67+
real = *real_value;
68+
}
69+
else if (const auto* integer_value = std::get_if<IdxT>(value))
70+
{
71+
real = static_cast<RealT>(*integer_value);
72+
}
73+
else
74+
{
75+
reject(key, "must be numeric");
76+
}
77+
78+
if (!std::isfinite(real))
79+
{
80+
reject(key, "must be finite");
81+
}
82+
83+
return real;
84+
}
85+
86+
/// Read an optional non-negative real parameter.
87+
[[nodiscard]] std::optional<RealT> readNonNegative(ParametersT key) const
88+
{
89+
auto value = readReal(key);
90+
if (value && *value < ZERO<RealT>)
91+
{
92+
reject(key, "must be non-negative");
93+
}
94+
return value;
95+
}
96+
97+
/// Read an optional boolean parameter.
98+
[[nodiscard]] std::optional<bool> readBool(ParametersT key) const
99+
{
100+
return readExact<bool>(key, "must be boolean");
101+
}
102+
103+
/// Read an optional integer parameter.
104+
[[nodiscard]] std::optional<IdxT> readInteger(ParametersT key) const
105+
{
106+
return readExact<IdxT>(key, "must be an integer");
107+
}
108+
109+
private:
110+
using ParameterMapT = decltype(ModelDataT::parameters);
111+
using ValueT = typename ParameterMapT::mapped_type;
112+
113+
/// The provided value, or nullptr when the parameter is omitted.
114+
const ValueT* find(ParametersT key) const
115+
{
116+
const auto entry = parameters_.find(key);
117+
if (entry == parameters_.end())
118+
{
119+
return nullptr;
120+
}
121+
return &entry->second;
122+
}
123+
124+
/// Read an optional parameter that must hold exactly a T.
125+
template <typename T>
126+
[[nodiscard]] std::optional<T> readExact(ParametersT key, std::string_view reason) const
127+
{
128+
const auto* value = find(key);
129+
if (value == nullptr)
130+
{
131+
return std::nullopt;
132+
}
133+
134+
const auto* exact_value = std::get_if<T>(value);
135+
if (exact_value == nullptr)
136+
{
137+
reject(key, reason);
138+
}
139+
140+
return *exact_value;
141+
}
142+
143+
/// Reject the model data with a message naming the model and parameter.
144+
[[noreturn]] void reject(ParametersT key, std::string_view reason) const
145+
{
146+
throw std::invalid_argument(
147+
std::format("{}: parameter '{}' {}", model_, magic_enum::enum_name(key), reason));
148+
}
149+
150+
const ParameterMapT& parameters_;
151+
const char* model_;
152+
};
153+
} // namespace Model
154+
} // namespace GridKit

‎GridKit/Model/PhasorDynamics/Branch/Branch.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ namespace GridKit
7272
virtual int setAbsoluteTolerance(RealT rel_tol) override final;
7373
virtual int evaluateResidual() override final;
7474
virtual int evaluateJacobian() override final;
75-
virtual int verify() const override final;
75+
76+
virtual Model::ConfigurationChecks verify() const override final;
7677

7778
int setInService(bool in_service);
7879

‎GridKit/Model/PhasorDynamics/Branch/BranchImpl.hpp‎

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -154,34 +154,27 @@ namespace GridKit
154154
}
155155

156156
template <typename scalar_type, typename index_type>
157-
int Branch<scalar_type, index_type>::verify() const
157+
Model::ConfigurationChecks Branch<scalar_type, index_type>::verify() const
158158
{
159-
int ret = parameter_error_count_;
159+
Model::ConfigurationChecks checks("Branch");
160160

161-
auto check = [&](bool condition, const char* message)
162-
{
163-
if (!condition)
164-
{
165-
Log::error() << "Branch: " << message << '\n';
166-
ret += 1;
167-
}
168-
};
169-
170-
check(bus1_ != nullptr, "bus1 pointer is null");
171-
check(bus2_ != nullptr, "bus2 pointer is null");
172-
173-
check(std::isfinite(R_), "R must be finite");
174-
check(std::isfinite(X_), "X must be finite");
175-
check(std::isfinite(G_), "G must be finite");
176-
check(std::isfinite(B_), "B must be finite");
177-
check(std::isfinite(Gmag_), "Gmag must be finite");
178-
check(std::isfinite(Bmag_), "Bmag must be finite");
179-
check(std::isfinite(tap_), "tap must be finite");
180-
check(std::isfinite(phase_), "phase must be finite");
181-
check(R_ * R_ + X_ * X_ > RealT{0.0}, "R and X cannot both be zero");
182-
check(tap_ > RealT{0.0}, "tap must be positive");
183-
184-
return ret;
161+
checks.check(parameter_error_count_ == 0, "model data has invalid parameters");
162+
163+
checks.check(bus1_ != nullptr, "bus1 pointer is null");
164+
checks.check(bus2_ != nullptr, "bus2 pointer is null");
165+
166+
checks.check(std::isfinite(R_), "R must be finite");
167+
checks.check(std::isfinite(X_), "X must be finite");
168+
checks.check(std::isfinite(G_), "G must be finite");
169+
checks.check(std::isfinite(B_), "B must be finite");
170+
checks.check(std::isfinite(Gmag_), "Gmag must be finite");
171+
checks.check(std::isfinite(Bmag_), "Bmag must be finite");
172+
checks.check(std::isfinite(tap_), "tap must be finite");
173+
checks.check(std::isfinite(phase_), "phase must be finite");
174+
checks.check(R_ * R_ + X_ * X_ > RealT{0.0}, "R and X cannot both be zero");
175+
checks.check(tap_ > RealT{0.0}, "tap must be positive");
176+
177+
return checks;
185178
}
186179

187180
/// Put the branch in or out of service; out of service, it injects no current.

‎GridKit/Model/PhasorDynamics/Bus/BusSignalVoltageIn/BusSignalVoltageIn.hpp‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace GridKit
2424
* components _Vr_ and _Vi_ are read directly from signal inlets
2525
* `vr` and `vi` whenever Vr() or Vi() is called; the bus stores no
2626
* voltage of its own and never modifies it. Both voltage inlets are
27-
* mandatory: verify() logs an error and throws for an inlet that is not
27+
* mandatory: verify() reports an error for an inlet that is not
2828
* connected to a linked signal, and reading the voltage through an
2929
* unlinked inlet throws. No default voltage is ever used. The bus has
3030
* no unknowns and no equations (size() == 0, like @ref BusInfinite).
@@ -70,13 +70,14 @@ namespace GridKit
7070

7171
virtual int setBusID(IdxT) override final;
7272
virtual int allocate() override final;
73-
virtual int verify() const override final;
7473
virtual int tagDifferentiable() override final;
7574
virtual int setAbsoluteTolerance(RealT rel_tol) override final;
7675
virtual int initialize() override final;
7776
virtual int evaluateResidual() override final;
7877
virtual int evaluateJacobian() override final;
7978

79+
virtual Model::ConfigurationChecks verify() const override final;
80+
8081
virtual BusTypeT BusType() const override final
8182
{
8283
return BusTypeT::SIGNAL_VOLTAGE_IN;

0 commit comments

Comments
 (0)