65 if (
sn.nstations != 2)
return false;
66 bool anyPositive =
false;
71 if (!anyPositive)
return false;
73 std::size_t iInf =
sn.nstations, iDps =
sn.nstations;
74 for (std::size_t i = 0; i <
sn.nstations; ++i) {
76 if (s == SchedStrategy::INF) {
77 if (iInf !=
sn.nstations)
return false;
79 }
else if (s == SchedStrategy::DPS) {
80 if (iDps !=
sn.nstations)
return false;
86 if (iInf ==
sn.nstations || iDps ==
sn.nstations)
return false;
87 const double c =
sn.stations[iDps].nservers;
88 if (std::isfinite(c) && c != 1.0)
return false;
89 if (
sn.nchains !=
sn.nclasses)
return false;
90 for (std::size_t ch = 0; ch <
sn.nchains; ++ch)
91 if (
sn.inchain[ch].size() > 1)
return false;
93 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
94 if (!(
sn.classes[r].population > 0))
return false;
95 const std::size_t sts[2] = {iInf, iDps};
96 for (std::size_t k = 0; k < 2; ++k) {
98 if (!std::isfinite(rate) || rate <= 0)
return false;
100 if (std::isfinite(scv) && std::fabs(scv - 1.0) > 1e-6)
return false;
102 if (
sn.stations[iDps].schedparam.size() !=
sn.nclasses)
return false;
104 if (!std::isfinite(wgt) || wgt <= 0)
return false;
108 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
111 if (std::fabs(vi - vd) > 1e-9 * std::max(1.0, vi))
return false;
130 "solver_nc_dps_analyzer: Morrison's W_m are erfc integrals; this backend has no "
131 "transcendental arithmetic");
140 "solver_nc_dps_analyzer: applies only to a CLOSED network of exactly two stations, "
141 "one infinite-server (think) station and one single-server DPS station with "
142 "exponential service and one visit each per cycle (see nc_is_dps_model).");
144 const std::size_t M =
sn.nstations, K =
sn.nclasses;
145 std::size_t iInf = M, iDps = M;
146 for (std::size_t i = 0; i < M; ++i) {
147 if (
sn.stations[i].sched == SchedStrategy::INF) iInf = i;
148 if (
sn.stations[i].sched == SchedStrategy::DPS) iDps = i;
150 if (iInf == M || iDps == M)
151 throw InputError(
"solver_nc_dps_analyzer: requires one INF and one DPS station");
153 std::vector<T> Npop(K, zero), Z(K, zero), S(K, zero), w(K, zero);
154 for (std::size_t r = 0; r < K; ++r) {
158 w[r] =
sn.stations[iDps].schedparam[r];
164 Matrix<T> Q(M, K, zero), U(M, K, zero), R(M, K, zero), Tp(M, K, zero);
165 std::vector<T> X(K, zero), C(K, zero);
167 const double cd =
sn.stations[iDps].nservers;
175 std::string qs =
"[";
176 for (std::size_t r = 0; r < K; ++r) {
179 if (!std::isfinite(q) || q < 0.0 || q > np) bad =
true;
181 qs += (r > 0 ?
", " :
"") + std::to_string(q);
184 if (bad || !std::isfinite(udps) || udps > 1.0 + 1e-9) {
186 "SolverNC method 'morrison': the heavy-usage expansion returned an infeasible result (DPS queue "
187 "lengths " + qs +
"], DPS utilization " + std::to_string(udps) +
"; usage rho="
189 +
"). The model is outside the moderately-heavy regime the expansion assumes; use SolverCTMC, "
190 "SolverJMT or SolverLDES instead.");
193 for (std::size_t r = 0; r < K; ++r) {
194 const T q = mor.
Q[r];
195 const T x = T((Npop[r] - q) / Z[r]);
197 Q(iInf, r) = T(Npop[r] - q);
199 for (std::size_t i = 0; i < M; ++i) Tp(i, r) = T(x * V(i, r));
200 U(iInf, r) = Q(iInf, r);
201 U(iDps, r) = T(x * V(iDps, r) * S[r] / c);
202 if (x > zero) C[r] = T(Npop[r] / x);
204 for (std::size_t i = 0; i < M; ++i)
205 for (std::size_t r = 0; r < K; ++r)
206 if (Tp(i, r) > zero) R(i, r) = T(Q(i, r) / Tp(i, r));
215 out.
sol.lG = std::numeric_limits<double>::quiet_NaN();
216 out.
sol.method =
"morrison";
DpsMorrisonResult< T > npfqn_dps_morrison(const std::vector< T > &N, const std::vector< T > &Z, const std::vector< T > &S, const std::vector< T > &w)
Evaluates Morrison's two-term approximation.
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.