44 const std::size_t I =
sn.nodes.size(), K =
sn.classes.size();
45 std::vector<bool>
fluid(I + 1,
false);
46 for (std::size_t ind = 1; ind <= I; ++ind)
fluid[ind] =
sn.is_fluid_node(ind);
47 for (std::size_t ind = 1; ind <= I; ++ind) {
48 if (!
fluid[ind])
continue;
49 const auto im =
sn.initmarking.find(ind);
50 if (im !=
sn.initmarking.end())
51 for (
const T& tok : im->second)
54 v.
reason =
"fluid place " +
sn.nodes[ind - 1].name +
55 " holds an initial token marking; a fluid place holds a level, set with "
59 for (std::size_t src = 1; src <= I; ++src) {
62 if (
sn.rtnodes.rows() >= I * K &&
sn.rtnodes.cols() >= I * K)
63 for (std::size_t r = 0; r < K && !linked; ++r)
64 for (std::size_t s = 0; s < K && !linked; ++s)
69 v.
reason =
"Source " +
sn.nodes[src - 1].name +
" is routed into fluid place " +
70 sn.nodes[ind - 1].name +
71 "; a fluid place receives fluid only through setFluidRate and setFluidImpulse";
76 for (
const auto& kv :
sn.transparam) {
77 const std::size_t ind = kv.first;
79 const std::string& tname =
sn.nodes[ind - 1].name;
80 for (std::size_t m = 0; m < tp.
nmodes; ++m) {
83 : (
"Mode" + std::to_string(m + 1));
85 const char* arcname[3] = {
"enabling",
"inhibiting",
"firing"};
86 for (
int w = 0; w < 3; ++w) {
87 if (m >= arcs[w]->size())
continue;
89 for (std::size_t p = 0; p < A.
rows() && p < I; ++p) {
90 if (!
fluid[p + 1])
continue;
91 for (std::size_t r = 0; r < A.
cols(); ++r) {
94 if (!std::isfinite(a) || a == 0.0)
continue;
96 v.
reason =
"mode " + mname +
" of transition " + tname +
" has a token " + arcname[w] +
97 " arc on fluid place " +
sn.nodes[p].name +
98 "; a fluid place is read through setFluidGuard and moved through "
99 "setFluidRate/setFluidImpulse";
107 v.
reason =
"immediate mode " + mname +
" of transition " + tname +
108 " carries a fluid flow; an immediate mode runs for zero time, so its flow moves "
109 "nothing. Use setFluidImpulse";
112 std::vector<std::size_t> named;
113 for (
const auto& a : tp.
fluid_rates(m)) named.push_back(a.node);
114 for (
const auto& g : tp.
fluid_guards(m)) named.push_back(g.node);
115 for (
const auto& a : tp.
fluid_impulses(m)) named.push_back(a.node);
116 for (
const auto& d : tp.
fluid_deps(m)) named.push_back(d.node);
119 for (
const auto& a : tp.
fluid_rates(m)) flows = flows || a.node == fd.node;
122 v.
reason =
"mode " + mname +
" of transition " + tname +
123 " has a fluid flow dependence on place " +
124 (fd.node >= 1 && fd.node <= I ?
sn.nodes[fd.node - 1].name : std::to_string(fd.node)) +
125 " but no flow into or out of it; set the flow with setFluidRate first";
128 for (std::size_t rd : fd.read)
131 v.
reason =
"mode " + mname +
" of transition " + tname +
132 " has a fluid flow dependence reading a node that is not a token place; a flow "
133 "multiplier reads the marking, and a level is read by setFluidRateDependence";
137 for (std::size_t nd : named)
138 if (nd == 0 || nd > I || !
fluid[nd]) {
140 v.
reason =
"mode " + mname +
" of transition " + tname +
" has a fluid arc on place " +
141 (nd >= 1 && nd <= I ?
sn.nodes[nd - 1].name : std::to_string(nd)) +
142 ", which is not a fluid place; declare it with setFluid(true)";
149 v.
reason =
"mode " + mname +
" of transition " + tname +
150 " has a fluid rate dependence on a non-exponential firing time; only an "
151 "exponential clock can be re-timed by the level";