94 if (
opt.method !=
"avg" &&
opt.method !=
"dec")
96 "' is not a closed-form environment limit; the two are 'avg' "
97 "(fast environment) and 'dec' (slow environment)");
98 dec_ = (
opt.method ==
"dec");
99 if (
opt.stage_solver !=
"fluid")
101 "SolverENV limit: stage solver '" +
opt.stage_solver +
102 "' is not available; the limits solve each stage in STEADY STATE and the fluid "
103 "analyzer is the one this port wires into the environment");
104 if (!std::is_same<T, double>::value)
106 "SolverENV limit: a fluid stage integrates its drift with LSODA, which is double "
107 "only; rerun with --arith double");
114 envObj.reject_lqn_stages(
116 "the fast/slow limits read a stage's station rates directly -- 'dec' solves each "
117 "stage network in steady state and 'avg' builds one network at the probEnv-weighted "
118 "rates -- and a layered model has no such rate table, only the layers SolverLN "
121 const std::size_t E = envObj.nstages();
122 M = envObj.stage(0).model.nstations;
123 K = envObj.stage(0).model.nclasses;
124 for (std::size_t e = 1; e < E; ++e)
125 if (envObj.stage(e).model.nstations != M || envObj.stage(e).model.nclasses != K)
127 "SolverENV limit: every stage must have the same stations and classes; the "
128 "metrics are blended entrywise across them");
129 for (std::size_t e = 0; e < E; ++e) {
131 for (std::size_t ind = 0; ind <
sn.nodes.size(); ++ind)
134 "SolverENV limit: stage " + std::to_string(e + 1) +
135 " holds a Cache, whose environment-blended hit and miss ratios come from "
136 "SolverENV.accumCacheMetric over the per-stage cache results; no cache "
137 "metric is reported by the stage solver here, so the blend would drop "
138 "the answer the model is asked for");
143 EnvLimitSolution solve_dec() {
144 const std::size_t E = envObj.nstages();
145 EnvLimitSolution out;
147 out.prob_env = envObj.prob_env;
148 out.QN = Matrix<double>(M, K, 0.0);
149 out.UN = Matrix<double>(M, K, 0.0);
150 out.TN = Matrix<double>(M, K, 0.0);
151 out.QStage.assign(E, Matrix<double>(M, K, 0.0));
152 out.UStage.assign(E, Matrix<double>(M, K, 0.0));
153 out.TStage.assign(E, Matrix<double>(M, K, 0.0));
154 for (std::size_t e = 0; e < E; ++e) {
155 const fluid::FluidSolution s = stage_steady_state(envObj.stage(e).model);
156 const double p = envObj.prob_env[e];
157 for (std::size_t i = 0; i < M; ++i)
158 for (std::size_t r = 0; r < K; ++r) {
159 out.QStage[e](i, r) = s.QN(i, r);
160 out.UStage[e](i, r) = s.UN(i, r);
161 out.TStage[e](i, r) = s.TN(i, r);
162 out.QN(i, r) += p * s.QN(i, r);
163 out.UN(i, r) += p * s.UN(i, r);
164 out.TN(i, r) += p * s.TN(i, r);
171 EnvLimitSolution solve_avg() {
172 EnvLimitSolution out;
174 out.prob_env = envObj.prob_env;
175 const qn::NetworkStruct<T> avg = rate_averaged_model();
176 const fluid::FluidSolution s = stage_steady_state(avg);
177 out.QN = Matrix<double>(M, K, 0.0);
178 out.UN = Matrix<double>(M, K, 0.0);
179 out.TN = Matrix<double>(M, K, 0.0);
180 for (std::size_t i = 0; i < M; ++i)
181 for (std::size_t r = 0; r < K; ++r) {
182 out.QN(i, r) = s.QN(i, r);
183 out.UN(i, r) = s.UN(i, r);
184 out.TN(i, r) = s.TN(i, r);
189 fluid::FluidSolution stage_steady_state(
const qn::NetworkStruct<T>& sn)
const {
190 fluid::FluidOptions fo = opt.stage;
205 qn::NetworkStruct<T> rate_averaged_model()
const {
206 const std::size_t E = envObj.nstages();
207 qn::NetworkStruct<T> sn = envObj.stage(0).model;
208 std::vector<double> r(E, 0.0);
209 for (std::size_t i = 0; i < M; ++i) {
214 for (std::size_t k = 0; k < K; ++k) {
216 for (std::size_t e = 0; e < E && ok; ++e) {
217 const qn::NetworkStruct<T>& se = envObj.stage(e).model;
221 if (se.disabled[i][k]) {
225 r[e] = num_traits<T>::to_double(se.rates(i, k));
226 if (!(r[e] > 0.0) || !std::isfinite(r[e])) ok =
false;
229 double lo = r[0], hi = r[0], avg = 0.0;
230 for (std::size_t e = 0; e < E; ++e) {
231 lo = std::min(lo, r[e]);
232 hi = std::max(hi, r[e]);
233 avg += envObj.prob_env[e] * r[e];
237 if (hi - lo <= 1e-12 * std::max(1.0, hi))
continue;
238 sn.set_service(i + 1, k + 1,
246 Environment<T>& envObj;
248 std::size_t M = 0, K = 0;