5#ifndef LINE_SOLVERS_WRAPPERS_JMT_SOLVER_JMT_H
6#define LINE_SOLVERS_WRAPPERS_JMT_SOLVER_JMT_H
103inline bool is_file(
const std::string& p) {
105 return !p.empty() && ::stat(p.c_str(), &st) == 0 && S_ISREG(st.st_mode);
108inline bool is_exec(
const std::string& p) {
110 return !p.empty() && ::stat(p.c_str(), &st) == 0 && ::access(p.c_str(), X_OK) == 0;
113inline std::string env_or_empty(
const char* name) {
114 const char* v = std::getenv(name);
115 return v !=
nullptr ? std::string(v) : std::string();
118inline std::string exe_dir() {
120 const ssize_t n = ::readlink(
"/proc/self/exe", buf,
sizeof(buf) - 1);
121 if (n <= 0)
return std::string();
122 std::string p(buf,
static_cast<std::size_t
>(n));
123 const std::size_t s = p.find_last_of(
'/');
124 return s == std::string::npos ? std::string() : p.substr(0, s);
127inline std::string cwd() {
129 return ::getcwd(buf,
sizeof(buf)) !=
nullptr ? std::string(buf) : std::string();
142inline std::string jmt_jar_path() {
143 const std::string forced = env_or_empty(
"LINE_JMT_DIR");
144 if (!forced.empty())
return is_file(forced +
"/JMT.jar") ? forced : std::string();
145 const std::string roots[2] = {exe_dir(), cwd()};
146 for (
int r = 0; r < 2; ++r) {
147 if (roots[r].empty())
continue;
148 std::string dir = roots[r];
149 for (
int k = 0; k < 7; ++k) {
150 if (is_file(dir +
"/JMT.jar"))
return dir;
151 if (is_file(dir +
"/common/JMT.jar"))
return dir +
"/common";
155 return std::string();
159inline std::string find_java() {
160 const std::string forced = env_or_empty(
"LINE_JAVA");
161 if (!forced.empty())
return is_exec(forced) ? forced : std::string();
162 const std::string home = env_or_empty(
"JAVA_HOME");
163 if (!home.empty() && is_exec(home +
"/bin/java"))
return home +
"/bin/java";
164 const std::string path = env_or_empty(
"PATH");
166 while (b <= path.size()) {
167 const std::size_t e = path.find(
':', b);
168 const std::string dir =
169 path.substr(b, e == std::string::npos ? std::string::npos : e - b);
170 if (!dir.empty() && is_exec(dir +
"/java"))
return dir +
"/java";
171 if (e == std::string::npos)
break;
174 return std::string();
178inline bool docker_consented() {
179 const std::string v = env_or_empty(
"LINE_JMT_DOCKER");
180 return v ==
"1" || v ==
"true" || v ==
"TRUE" || v ==
"yes" || v ==
"y" || v ==
"Y";
183inline std::string read_file(
const std::string& path) {
184 std::ifstream f(path.c_str(), std::ios::binary);
185 if (!f)
throw InputError(
"SolverJMT: cannot read '" + path +
"'");
186 std::ostringstream ss;
191inline void write_file(
const std::string& path,
const std::string& text) {
192 std::ofstream f(path.c_str(), std::ios::binary);
193 if (!f)
throw InputError(
"SolverJMT: cannot write '" + path +
"'");
194 f.write(text.data(),
static_cast<std::streamsize
>(text.size()));
195 if (!f)
throw InputError(
"SolverJMT: write failed on '" + path +
"'");
199inline std::string json_escape(
const std::string& s) {
201 out.reserve(s.size() + 16);
202 for (std::size_t i = 0; i < s.size(); ++i) {
203 const unsigned char c =
static_cast<unsigned char>(s[i]);
205 case '"': out +=
"\\\"";
break;
206 case '\\': out +=
"\\\\";
break;
207 case '\n': out +=
"\\n";
break;
208 case '\r': out +=
"\\r";
break;
209 case '\t': out +=
"\\t";
break;
213 std::snprintf(buf,
sizeof(buf),
"\\u%04x", c);
232inline std::string json_string_field(
const std::string& body,
const std::string& key) {
233 const std::string needle =
"\"" + key +
"\"";
234 std::size_t p = body.find(needle);
235 if (p == std::string::npos)
return std::string();
236 p = body.find(
':', p + needle.size());
237 if (p == std::string::npos)
return std::string();
239 while (p < body.size() && (body[p] ==
' ' || body[p] ==
'\t' || body[p] ==
'\n')) ++p;
240 if (p >= body.size() || body[p] !=
'"')
return std::string();
243 while (p < body.size() && body[p] !=
'"') {
244 if (body[p] ==
'\\' && p + 1 < body.size()) {
247 case 'n': out.push_back(
'\n');
break;
248 case 'r': out.push_back(
'\r');
break;
249 case 't': out.push_back(
'\t');
break;
251 if (p + 4 < body.size()) {
252 const long cp = std::strtol(body.substr(p + 1, 4).c_str(),
nullptr, 16);
253 if (cp < 0x80) out.push_back(
static_cast<char>(cp));
258 default: out.push_back(body[p]);
261 out.push_back(body[p]);
273inline util::ProcResult
jmt_run_docker(
const std::string& image,
const std::string& mode,
274 const std::string& model_path,
long seed,
279 if (mode ==
"sim")
return "jsim";
280 if (mode ==
"mva")
return "jmva";
281 throw InputError(
"SolverJMT: unknown JMT analysis mode '" + mode +
"'");
286 return !detail::jmt_jar_path().empty() && !detail::find_java().empty();
294 const std::string& model_path,
long seed,
296 std::string url = rest_url;
297 while (!url.empty() && url[url.size() - 1] ==
'/') url.erase(url.size() - 1);
299 if (url.size() < 8 || url.substr(url.size() - 8) !=
"/solve/" + mode.substr(0, 1))
300 if (url.find(
"/api/v") == std::string::npos) url +=
"/api/v1/solve/" + mode;
302 std::string body =
"{\"model\":{\"content\":\"" +
303 detail::json_escape(detail::read_file(model_path)) +
304 "\",\"base64\":false}";
308 if (mode ==
"sim") body +=
",\"options\":{\"seed\":" + std::to_string(seed) +
"}";
313 throw InputError(
"SolverJMT: the JMT REST server at " + url +
" answered HTTP " +
314 std::to_string(resp.
status));
315 const std::string status = detail::json_string_field(resp.
body,
"status");
316 if (status !=
"completed") {
317 const std::string err = detail::json_string_field(resp.
body,
"error");
318 throw InputError(
"SolverJMT: the JMT REST solve failed: " +
319 (err.empty() ? std::string(
"unspecified error") : err));
321 const std::string
xml = detail::json_string_field(resp.
body,
"result_xml");
324 "SolverJMT: the JMT REST response carries no result document. The server was asked "
325 "to include the raw output; check that include_raw_output is not disabled");
329 r.
out = detail::json_string_field(resp.
body,
"stdout");
344 const std::string jar_dir = detail::jmt_jar_path();
345 const std::string java = detail::find_java();
346 if (!jar_dir.empty() && !java.empty()) {
347 std::vector<std::string> argv;
348 argv.push_back(java);
349 argv.push_back(
"-cp");
350 argv.push_back(jar_dir +
"/JMT.jar");
351 argv.push_back(
"jmt.commandline.Jmt");
352 argv.push_back(mode);
353 argv.push_back(model_path);
354 argv.push_back(
"-seed");
355 argv.push_back(std::to_string(seed));
356 argv.push_back(
"--illegal-access=permit");
364 throw NumericError(
"SolverJMT: the JMT run did not finish within " +
365 std::to_string(
opt.timeout) +
"s and was killed");
371 std::string image =
opt.container.empty() ? detail::env_or_empty(
"LINE_JMT_IMAGE")
382 "SolverJMT: no way to reach JMT. Install a Java runtime and put JMT.jar in common/ "
383 "(https://line-solver.sourceforge.net/latest/JMT.jar), or point options.rest_url at a "
384 "JMT REST server, or set LINE_JMT_DOCKER=1 to allow the " +
385 image +
" image to be pulled and used");
399 const std::string& model_path,
long seed,
402 const std::size_t slash = model_path.find_last_of(
'/');
403 const std::string base =
404 slash == std::string::npos ? model_path : model_path.substr(slash + 1);
405 const std::string staged = work.
path() +
"/" + base;
406 detail::write_file(staged, detail::read_file(model_path));
408 const std::string uid = std::to_string(
static_cast<long>(::getuid()));
409 const std::string gid = std::to_string(
static_cast<long>(::getgid()));
410 std::vector<std::string> argv;
411 argv.push_back(
"docker");
412 argv.push_back(
"run");
413 argv.push_back(
"--rm");
414 argv.push_back(
"--user");
415 argv.push_back(uid +
":" + gid);
416 argv.push_back(
"-v");
417 argv.push_back(work.
path() +
":" + work.
path());
418 argv.push_back(
"-w");
419 argv.push_back(work.
path());
420 argv.push_back(image);
421 argv.push_back(mode);
422 argv.push_back(base);
423 argv.push_back(
"-seed");
424 argv.push_back(std::to_string(seed));
427 throw NumericError(
"SolverJMT: the JMT container run did not finish within " +
428 std::to_string(
opt.timeout) +
"s and was killed");
430 const std::string produced = staged +
"-result." +
jmt_result_ext(mode);
431 if (detail::is_file(produced))
432 detail::write_file(model_path +
"-result." +
jmt_result_ext(mode),
433 detail::read_file(produced));
454 const std::string& command_output) {
455 if (!detail::is_file(result_path)) {
457 "SolverJMT: JMT did not output a result file, the simulation has likely failed.";
458 if (!command_output.empty()) msg +=
" JMT output: " + command_output;
461 const std::unique_ptr<xml::Element> doc =
xml::parse_file(result_path);
462 std::vector<JmtMeasure> out;
463 const std::vector<const xml::Element*> ms = doc->by_tag(
"measure");
464 for (std::size_t i = 0; i < ms.size(); ++i) {
471 m.
mean = std::strtod(e->
attr(
"meanValue").c_str(),
nullptr);
475 m.
lower = std::strtod(e->
attr(
"lowerLimit").c_str(),
nullptr);
476 m.
upper = std::strtod(e->
attr(
"upperLimit").c_str(),
nullptr);
521 const std::vector<JmtMeasure>& measures,
bool confint) {
522 const std::size_t M =
sn.nstations, K =
sn.nclasses, F =
sn.regions.size();
524 const double nan = std::numeric_limits<double>::quiet_NaN();
542 for (std::size_t f = 0; f < F; ++f)
543 for (std::size_t r = 0; r < K; ++r) {
548 std::map<std::string, std::size_t> station_of, class_of;
549 for (std::size_t i = 1; i <=
sn.nodes.size(); ++i) station_of[
sn.nodes[i - 1].name] = i;
550 for (std::size_t r = 1; r <= K; ++r) class_of[
sn.classes[r - 1].name] = r;
553 std::vector<double> chainpop(K, 0.0);
554 for (std::size_t c = 0; c <
sn.inchain.size(); ++c) {
556 for (std::size_t r :
sn.inchain[c]) {
557 const double n =
sn.classes[r - 1].population;
558 if (std::isfinite(n)) tot += n;
560 for (std::size_t r :
sn.inchain[c]) chainpop[r - 1] = tot;
563 for (std::size_t i = 0; i < measures.size(); ++i) {
571 if (is_fcr && m.
station.compare(0, 8,
"FCRegion") == 0) {
572 const long f = std::strtol(m.
station.substr(8).c_str(),
nullptr, 10);
573 if (f < 1 ||
static_cast<std::size_t
>(f) > F)
continue;
574 const std::size_t row = M +
static_cast<std::size_t
>(f) - 1;
580 const double per_class = m.
mean /
static_cast<double>(K);
581 for (std::size_t r = 0; r < K; ++r) {
601 const auto ni = station_of.find(m.
station);
602 const auto ci = class_of.find(m.
job_class);
603 if (ni == station_of.end() || ci == class_of.end())
continue;
605 const auto cp =
sn.nodeparam.find(ni->second);
606 if (cp ==
sn.nodeparam.end())
continue;
608 if (hit.empty()) hit.assign(K, zero);
611 for (std::size_t r = 0; r < cp->second.hitclass.size(); ++r)
612 if (cp->second.hitclass[r] == ci->second)
617 const auto ni = station_of.find(m.
station);
618 const auto ci = class_of.find(m.
job_class);
619 if (ni == station_of.end() || ci == class_of.end())
continue;
620 const std::size_t ist =
sn.nodes[ni->second - 1].station;
621 if (ist == 0)
continue;
622 const std::size_t r = ci->second;
623 const bool open = !std::isfinite(
sn.classes[r - 1].population);
629 res.
avg.QN(ist - 1, r - 1) = v;
630 if (confint) res.
QCI(ist - 1, r - 1) = ci_v;
632 res.
avg.UN(ist - 1, r - 1) = v;
633 if (confint) res.
UCI(ist - 1, r - 1) = ci_v;
635 res.
avg.RN(ist - 1, r - 1) = v;
636 if (confint) res.
RCI(ist - 1, r - 1) = ci_v;
638 res.
avg.TN(ist - 1, r - 1) = v;
639 if (confint) res.
TCI(ist - 1, r - 1) = ci_v;
641 res.
avg.AN(ist - 1, r - 1) = v;
642 if (confint) res.
ACI(ist - 1, r - 1) = ci_v;
662 const std::size_t M =
sn.nstations, K =
sn.nclasses, F =
sn.regions.size();
667 for (std::size_t f = 0; f < F; ++f)
668 for (std::size_t r = 0; r < K; ++r) res.
TNfcr(f, r) = res.
avg.TN(M + f, r);
670 for (std::size_t f = 0; f < F; ++f) {
672 for (std::size_t r = 1; r <= K; ++r) {
674 for (std::size_t ii = 1; ii <= M; ++ii) {
676 for (std::size_t j = 1; j <= M; ++j) {
678 for (std::size_t rp = 1; rp <= K; ++rp) {
679 const std::size_t a = (j - 1) * K + (rp - 1);
680 const std::size_t b = (ii - 1) * K + (r - 1);
681 if (
sn.rt.rows() <= a ||
sn.rt.cols() <= b)
continue;
718 const std::string& command_output) {
719 if (!detail::is_file(result_path)) {
721 "SolverJMT: JMT did not output a result file, the analysis has likely failed.";
722 if (!command_output.empty()) msg +=
" JMT output: " + command_output;
725 const std::size_t M =
sn.nstations, K =
sn.nclasses, C =
sn.nchains;
737 std::map<std::string, std::size_t> station_of;
738 for (std::size_t i = 1; i <= M; ++i)
739 station_of[
sn.nodes[
sn.station_to_node[i - 1] - 1].name] = i;
741 const std::unique_ptr<xml::Element> doc =
xml::parse_file(result_path);
743 const std::vector<const xml::Element*>
nc = doc->by_tag(
"normconst");
746 std::numeric_limits<double>::quiet_NaN())
748 std::strtod(
nc[0]->attr(
"logValue").c_str(),
nullptr));
752 if (
sn.sourceIdx != 0)
753 for (std::size_t r = 0; r < K; ++r)
754 if (!
sn.disabled[
sn.sourceIdx - 1][r])
755 res.
avg.TN(
sn.sourceIdx - 1, r) =
sn.rates(
sn.sourceIdx - 1, r);
757 const std::vector<const xml::Element*> blocks = doc->by_tag(
"stationresults");
758 for (std::size_t b = 0; b < blocks.size(); ++b) {
759 const auto si = station_of.find(blocks[b]->attr(
"station"));
760 if (si == station_of.end())
continue;
761 const std::size_t i = si->second;
765 double ns =
sn.stations[i - 1].nservers;
766 for (
const T& s :
sn.stations[i - 1].lldscaling)
768 const std::vector<const xml::Element*> crs = blocks[b]->by_tag(
"classresults");
769 for (std::size_t c = 1; c <= crs.size() && c <= C; ++c) {
770 const std::vector<const xml::Element*> ms = crs[c - 1]->by_tag(
"measure");
777 double chain_tput = std::numeric_limits<double>::quiet_NaN();
778 for (std::size_t m = 0; m < ms.size(); ++m) {
779 if (ms[m]->attr(
"measureType") ==
"Throughput") {
780 const std::string traw = ms[m]->attr(
"meanValue");
781 chain_tput = traw ==
"NaN" ? std::numeric_limits<double>::quiet_NaN()
782 : std::strtod(traw.c_str(),
nullptr);
786 for (std::size_t m = 0; m < ms.size(); ++m) {
787 const std::string kind = ms[m]->attr(
"measureType");
788 const std::string raw = ms[m]->attr(
"meanValue");
789 const double val = raw ==
"NaN" ? std::numeric_limits<double>::quiet_NaN()
790 : std::strtod(raw.c_str(),
nullptr);
792 const double vchain_ref =
795 for (std::size_t k :
sn.inchain[c - 1]) {
799 if (kind ==
"Utilization") {
800 if (vchain_ref == 0.0)
continue;
801 v = stk * chain_tput / vchain_ref * al;
802 if (std::isfinite(ns)) v /= ns;
804 }
else if (kind ==
"Throughput") {
806 }
else if (kind ==
"Number of Customers") {
807 if (stchain == 0.0 || vchain_ref == 0.0)
continue;
808 res.
avg.QN(i - 1, k - 1) =
810 }
else if (kind ==
"Residence time" || kind ==
"Residence Time") {
811 if (stchain == 0.0 || vchain_ref == 0.0)
continue;
813 if (c - 1 <
sn.visits.size()) {
814 const std::size_t sfi =
sn.stateful_of_station(i);
815 if (sfi != 0 &&
sn.visits[c - 1].rows() >= sfi)
818 if (visits == 0.0)
continue;
819 v = (val / visits) * stk / stchain / vchain_ref * al;
839 return {
"default",
"jsim",
"replication",
"jmva",
"jmva.amva",
"jmva.mva",
840 "jmva.recal",
"jmva.comom",
"jmva.chow",
"jmva.bs",
"jmva.aql",
841 "jmva.lin",
"jmva.dmlin"};
872 if (method ==
"replication" && !std::isfinite(
opt.max_simulated_time))
873 return "SolverJMT: the 'replication' method needs a finite timespan; a transient mean "
874 "over an unstated horizon is not a quantity";
876 double maxFinite = 0.0;
877 for (std::size_t i = 0; i <
sn.nstations; ++i) {
878 const double c =
static_cast<double>(
sn.stations[i].nservers);
879 if (std::isfinite(c)) maxFinite = std::max(maxFinite, c);
881 if (maxFinite > 1.0)
return "SolverJMT: " + method +
" does not support multi-server stations";
883 for (std::size_t i = 0; i <
sn.nstations; ++i) {
884 const std::vector<T>& alpha =
sn.stations[i].lldscaling;
885 if (alpha.empty())
continue;
888 if (c == 1.0)
continue;
889 bool ok = (c >= 1.0) && (c == std::floor(c));
890 for (std::size_t n = 0; ok && n < alpha.size(); ++n)
894 return "SolverJMT: station '" +
sn.stations[i].name +
895 "' uses a load-dependent scaling that is not the multiserver encoding alpha(n) "
896 "= min(n,c): JMT has no representation for it, since both the JSIM and the JMVA "
897 "writer carry the scaling as a server count, and the model would be solved at "
898 "the nominal service rate. Use SolverCTMC, SolverNC, SolverMVA or SolverSSA, "
899 "which read sn.lldscaling directly";
909 const bool is_jmva = method.compare(0, 4,
"jmva") == 0;
910 const bool is_jsim = method ==
"default" || method ==
"jsim" || method ==
"replication";
911 if (is_jmva || is_jsim) {
913 if (!cap_reason.empty())
return cap_reason;
915 return std::string();
929 if (
opt.samples < 5000.0)
opt.samples = 5000.0;
931 if (std::find(valid.begin(), valid.end(),
opt.method) == valid.end())
933 if (
opt.method ==
"replication")
935 "SolverJMT: 'replication' is the transient average over independent seeds and is "
936 "composed from jmt_sample_sys_aggr, not from this steady-state dispatch; call "
937 "jmt::jmt_replication (jmt_logs.h), which is what -a tran reaches");
938 if (
sn.has_immediate_feedback())
940 "SolverJMT: JMT has no immediate-feedback semantics (a job re-entering service "
941 "while HOLDING the server); use SolverCTMC, SolverSSA or SolverLDES");
951 const bool is_mva =
opt.method.compare(0, 4,
"jmva") == 0;
952 const std::string mode = is_mva ?
"mva" :
"sim";
955 const std::string model_path = work.
path() +
"/model." + (is_mva ?
"jmva" :
"jsim");
977 const std::string result_path = model_path +
"-result." +
jmt_result_ext(mode);
990 for (std::size_t i = 0; i <
sn.nstations; ++i)
991 for (std::size_t r = 0; r <
sn.nclasses; ++r) res.
avg.WN(i, r) = WNst(i, r);
1002 for (std::size_t i = 0; i <
sn.nstations; ++i) {
1003 if (
sn.stations[i].nodetype != qn::NodeType::Source)
continue;
1004 for (std::size_t r = 0; r <
sn.nclasses; ++r)
1018 for (std::size_t i = 0; i <
sn.nstations; ++i)
1019 for (std::size_t r = 0; r <
sn.nclasses; ++r)
1027 res.
avg.actualmethod =
opt.method;
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
static void substep(const char *fmt,...)
Write one indented progress line.
static void step(const char *fmt,...)
Write one progress line.
void keep()
Leave the directory in place, for a caller that wants to inspect it.
const std::string & path() const
The directory itself, with no trailing separator.
Docker primitives for the backends that legitimately ship an image.
The exception types the port throws.
Minimal HTTP/1.1 client, enough to talk to a line-*-rest service.
Port of @@JMTIO: a refreshed NetworkStruct written out as a JMT .jsimg simulation model.
Port of @@JMTIO/writeJMVA.m: the CHAIN-level product-form model in the JMVA interchange format.
Running progress log of a LINE solver run (the "solver console").
Response post_json(const std::string &url, const std::string &json, int timeoutMillis)
POST a JSON document.
bool docker_has_storage_for(const std::string &image)
std::string jmt_write_jsim(const qn::NetworkStruct< T > &sn, const JmtWriteOptions &opt)
Port of @@JMTIO/writeJSIM.m: serialize sn as a JMT .jsimg document.
std::string jmt_buffer_capacity_refusal(const qn::NetworkStruct< T > &sn, bool jmva_engine)
JmtWriter::buffer_capacity_refusal without a document: the writer's own binding-buffer verdict,...
bool docker_daemon_available()
True if the Docker daemon is reachable.
bool docker_has_local_image(const std::string &image)
std::string write_jmva(const qn::NetworkStruct< T > &L, const std::string &path, const std::string &method, std::size_t samples)
Port of writeJMVA(sn, outputFileName, options).
bool docker_pull(const std::string &image)
Pulls an image, streaming Docker's progress to stdout and stderr.
static const char * JMT_DOCKER_IMAGE
The default JMT REST/Docker image, MATLAB's jmtDockerImage candidate.
void jmt_region_losses(const qn::NetworkStruct< T > &sn, JmtResult< T > &res)
The region loss table, port of the sn.nregions > 0 tail of getResults.m.
util::ProcResult jmt_run(const std::string &mode, const std::string &model_path, long seed, const JmtOptions &opt)
Port of jmtRun: one batch analysis, leaving the result where the JMT CLI itself would leave it.
JmtResult< T > jmt_parse_jmva(const qn::NetworkStruct< T > &sn, const std::string &result_path, const std::string &command_output)
Port of getResultsJMVA.m: the per-CHAIN answer spread back over the classes.
util::ProcResult jmt_run_docker(const std::string &image, const std::string &mode, const std::string &model_path, long seed, const JmtOptions &opt)
Run the analysis inside the JMT container and copy the result back beside the model,...
util::ProcResult jmt_solve_rest(const std::string &rest_url, const std::string &mode, const std::string &model_path, long seed, const JmtOptions &opt)
Port of jmtSolveRest: POST the model document, write the result document back beside the model so the...
std::vector< std::string > jmt_list_valid_methods()
Port of SolverJMT.listValidMethods.
JmtResult< T > solver_jmt_run_analyzer(const qn::NetworkStruct< T > &sn, const JmtOptions &opt_in)
Port of @@SolverJMT/runAnalyzer.m, the jsim and jmva arms.
JmtResult< T > jmt_map_measures(const qn::NetworkStruct< T > &sn, const std::vector< JmtMeasure > &measures, bool confint)
Port of getResults.m: the measures mapped onto the metric matrices.
bool jmt_available()
True when a local JVM and common/JMT.jar are both present.
std::vector< JmtMeasure > jmt_parse_measures(const std::string &result_path, const std::string &command_output)
Port of getResultsJSIM: every <measure> of the result document.
const char * jmt_result_ext(const std::string &mode)
The suffix jmt.commandline.Jmt appends to the model path, per mode.
std::string jmt_method_refusal(const qn::NetworkStruct< T > &sn, const std::string &method, const JmtOptions &opt)
The structural half of SolverJMT's method gate; empty when admissible.
Matrix< T > sn_get_residt_from_respt(const qn::NetworkStruct< T > &L, const Matrix< T > &RN)
Port of sn_get_residt_from_respt: the per-JOB residence time.
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
bool jmva_is_closed_only(const std::string &method)
Port of SolverJMT.getFeatureSet (@@SolverJMT/SolverJMT.m:180-290).
ProcResult capture(const std::vector< std::string > &argv, int timeoutSeconds, bool mergeStderr=false)
Runs a command, capturing stdout and discarding stderr.
std::unique_ptr< Element > parse_file(const std::string &path)
Read and parse a file.
A queueing network and its refreshed NetworkStruct.
Chain aggregation and de-aggregation.
The DECLARED side of the gate: one feature set per solver.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
An HTTP response, with the body already de-chunked.
std::string body
Response body, decoded.
int status
HTTP status code.
The simulation controls the JSIM header carries, MATLAB's JMTIO properties.
std::string log_path
model.getLogPath, the logPath attribute
std::string file_name
base name; the header echoes it plus .jsimg
double max_simulated_time
One <measure> of a JMT result document, by its attributes.
The options of one JMT solve, SolverOptions('JMT') restricted to what is read.
int timeout
seconds, for the REST and subprocess arms
int iter_max
options.iter_max: the replication count of method = "replication".
std::string method
default | jsim | jmva | jmva.<alg>
std::string container
options.config.container, an image override
std::string rest_url
a JMT REST server; empty selects the local JVM
bool keep
keep the scratch directory after the solve
double confint
0 disables the confidence interval columns
double max_simulated_time
double samples
samples per measure; raised to 5000 below
The result of a JMT solve: the shared AvgResult plus what only JMT reports.
std::map< std::size_t, std::vector< T > > cache_hit_prob
Per Cache node (1-based node index), the per-class hit probability.
Matrix< T > MemOcc
FCR-only: weighted and memory occupation.
Matrix< T > ACI
confidence half-widths, empty when disabled
T log_norm_const
result.Prob.logNormConstAggr, the log normalizing constant JMVA reports.
Matrix< T > DropRateNfcr
(nregions x nclasses) carried and lost rate
static constexpr double FineTol
The metrics getAvg returns, after filtering.
The chain-level view of a layer, as sn_get_demands_chain returns it.
Matrix< T > ST
(M x K) class-level mean service time, 0 where disabled
std::vector< std::size_t > refstatchain
(C) 1-based reference station
Matrix< T > alpha
(M x K) class share of its chain's visits at a station
Matrix< T > STchain
(M x C) mean service time
Matrix< T > Vchain
(M x C) visits
static constexpr double Zero
FINITE CAPACITY REGIONS, MATLAB's refreshRegions output.
std::vector< DropStrategy > rule
per class
std::vector< bool > members
membership, independent of the caps
Outcome of a captured command.
int exitCode
Exit status, or -1 when the command could not run.
bool timedOut
True when the deadline expired and the child was killed.
std::string out
Everything the command wrote to stdout.
bool has_attr(const std::string &key) const
std::string attr(const std::string &key) const
Attribute value, or the empty string when absent (org.w3c.dom semantics).
Running an external command and capturing its output, with a deadline.
A scratch directory for the subprocess wrappers, the port's lineTempName.
A minimal XML DOM: read for the .lqnx interchange format, write for the JMT .jsimg and ....