Program Listing for File JSONLogger.cu
↰ Return to documentation for file (src/flamegpu/io/JSONLogger.cu
)
#include "flamegpu/io/JSONLogger.h"
#include <iostream>
#include <fstream>
#include <string>
#include "flamegpu/simulation/RunPlan.h"
#include "flamegpu/simulation/LogFrame.h"
namespace flamegpu {
namespace io {
JSONLogger::JSONLogger(const std::string &outPath, bool _prettyPrint, bool _truncateFile)
: out_path(outPath)
, prettyPrint(_prettyPrint)
, truncateFile(_truncateFile) { }
void JSONLogger::log(const RunLog &log, const RunPlan &plan, bool logSteps, bool logExit, bool logStepTime, bool logExitTime) const {
logCommon(log, &plan, false, logSteps, logExit, logStepTime, logExitTime);
}
void JSONLogger::log(const RunLog &log, bool logConfig, bool logSteps, bool logExit, bool logStepTime, bool logExitTime) const {
logCommon(log, nullptr, logConfig, logSteps, logExit, logStepTime, logExitTime);
}
void JSONLogger::writeAny(nlohmann::ordered_json& j, const detail::Any &value, const unsigned int elements) const {
if (elements == 1) {
if (value.type == std::type_index(typeid(float))) {
j = static_cast<const float*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(double))) {
j = static_cast<const double*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(int64_t))) {
j = static_cast<const int64_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(uint64_t))) {
j = static_cast<const uint64_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(int32_t))) {
j = static_cast<const int32_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(uint32_t))) {
j = static_cast<const uint32_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(int16_t))) {
j = static_cast<const int16_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(uint16_t))) {
j = static_cast<const uint16_t*>(value.ptr)[0];
} else if (value.type == std::type_index(typeid(int8_t))) {
j = static_cast<int32_t>(static_cast<const int8_t*>(value.ptr)[0]); // Char outputs weird if being used as an integer
} else if (value.type == std::type_index(typeid(uint8_t))) {
j = static_cast<uint32_t>(static_cast<const uint8_t*>(value.ptr)[0]); // Char outputs weird if being used as an integer
} else if (value.type == std::type_index(typeid(char))) {
j = static_cast<int32_t>(static_cast<const char*>(value.ptr)[0]); // Char outputs weird if being used as an integer
} else {
THROW exception::JSONError("Attempting to export value of unsupported type '%s', "
"in JSONLogger::writeAny()\n", value.type.name());
}
return;
}
// Loop through elements, to construct array
for (unsigned int el = 0; el < elements; ++el) {
if (value.type == std::type_index(typeid(float))) {
j.emplace_back(static_cast<const float*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(double))) {
j.emplace_back(static_cast<const double*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int64_t))) {
j.emplace_back(static_cast<const int64_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint64_t))) {
j.emplace_back(static_cast<const uint64_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int32_t))) {
j.emplace_back(static_cast<const int32_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint32_t))) {
j.emplace_back(static_cast<const uint32_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int16_t))) {
j.emplace_back(static_cast<const int16_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint16_t))) {
j.emplace_back(static_cast<const uint16_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int8_t))) {
j.emplace_back(static_cast<int32_t>(static_cast<const int8_t*>(value.ptr)[el])); // Char outputs weird if being used as an integer
} else if (value.type == std::type_index(typeid(uint8_t))) {
j.emplace_back(static_cast<uint32_t>(static_cast<const uint8_t*>(value.ptr)[el])); // Char outputs weird if being used as an integer
} else if (value.type == std::type_index(typeid(char))) {
j.emplace_back(static_cast<int32_t>(static_cast<const char*>(value.ptr)[el])); // Char outputs weird if being used as an integer
} else {
THROW exception::JSONError("Attempting to export value of unsupported type '%s', "
"in JSONLogger::writeAny()\n", value.type.name());
}
}
}
nlohmann::ordered_json JSONLogger::writeLogFrame(const StepLogFrame& frame, bool logTime) const {
nlohmann::ordered_json j;
if (logTime) {
j["step_time"] = frame.getStepTime();
}
writeCommonLogFrame(j, frame);
return j;
}
nlohmann::ordered_json JSONLogger::writeLogFrame(const ExitLogFrame& frame, bool logTime) const {
nlohmann::ordered_json j;
if (logTime) {
j["rtc_time"] = frame.getRTCTime();
j["init_time"] = frame.getInitTime();
j["exit_time"] = frame.getExitTime();
j["total_time"] = frame.getTotalTime();
}
writeCommonLogFrame(j, frame);
return j;
}
void JSONLogger::writeCommonLogFrame(nlohmann::ordered_json& j, const LogFrame &frame) const {
// Add static items
j["step_index"] = frame.getStepCount();
if (frame.getEnvironment().size()) {
// Add dynamic environment values
nlohmann::ordered_json j_env;
for (const auto &prop : frame.getEnvironment()) {
j_env[prop.first] = {};
// Log value
writeAny(j_env[prop.first], prop.second, prop.second.elements);
}
j["env"] = j_env;
}
if (frame.getAgents().size()) {
// Add dynamic agent values
nlohmann::ordered_json j_agents = {};
for (const auto &agent : frame.getAgents()) {
nlohmann::ordered_json j_t_agent;
// Log agent count if provided
if (agent.second.second != UINT_MAX) {
j_t_agent["count"][agent.second.second];
}
if (agent.second.first.size()) {
j_t_agent["variables"];
// This assumes that sort order places all variables of same name, different reduction consecutively
std::string current_variable;
// Log each reduction
for (auto &var : agent.second.first) {
// Log value
writeAny(j_t_agent["variables"][var.first.name][LoggingConfig::toString(var.first.reduction)], var.second, 1);
}
}
j_agents[agent.first.first][agent.first.second].push_back(j_t_agent);
}
if (j_agents.size()) {
j["agents"] = j_agents;
}
}
}
nlohmann::ordered_json JSONLogger::logConfig(const RunLog &log) const {
nlohmann::ordered_json j;
j["random_seed"] = log.getRandomSeed();
return j;
}
nlohmann::ordered_json JSONLogger::logConfig(const RunPlan &plan) const {
nlohmann::ordered_json j;
// Add static items
j["random_seed"] = plan.getRandomSimulationSeed();
j["steps"] = plan.getSteps();
// Add dynamic environment overrides
nlohmann::ordered_json dyn_j;
for (const auto& prop : plan.property_overrides) {
const EnvironmentData::PropData& env_prop = plan.environment->at(prop.first);
dyn_j[prop.first] = {};
writeAny(dyn_j[prop.first], prop.second, env_prop.data.elements);
}
if (dyn_j.size()) {
j["environment"] = dyn_j;
}
return j;
}
nlohmann::ordered_json JSONLogger::logPerformanceSpecs(const RunLog& log) const {
nlohmann::ordered_json j;
// Add static items
j["device_name"] = log.getPerformanceSpecs().device_name;
j["device_cc_major"] = log.getPerformanceSpecs().device_cc_major;
j["device_cc_minor"] = log.getPerformanceSpecs().device_cc_minor;
j["cuda_version"] = log.getPerformanceSpecs().cuda_version;
j["seatbelts"] = log.getPerformanceSpecs().seatbelts;
j["flamegpu_version"] = log.getPerformanceSpecs().flamegpu_version;
return j;
}
void JSONLogger::logSteps(nlohmann::ordered_json& j, const RunLog &log, bool logTime) const {
j["steps"] = {};
for (const auto &step : log.getStepLog()) {
j["steps"].push_back(writeLogFrame(step, logTime));
}
}
void JSONLogger::logExit(nlohmann::ordered_json& j, const RunLog &log, bool logTime) const {
j["exit"] = writeLogFrame(log.getExitLog(), logTime);
}
void JSONLogger::logCommon(nlohmann::ordered_json &j, const RunLog &log, const RunPlan *plan, bool doLogConfig, bool doLogSteps, bool doLogExit, bool doLogStepTime, bool doLogExitTime) const {
// Log config
if (plan) {
j["config"] = logConfig(*plan);
} else if (doLogConfig) {
j["config"] = logConfig(log);
}
if (doLogStepTime || doLogExitTime) {
j["performance_specs"] = logPerformanceSpecs(log);
}
// Log step log
if (doLogSteps) {
logSteps(j, log, doLogStepTime);
}
// Log exit log
if (doLogExit) {
logExit(j, log, doLogExitTime);
}
}
void JSONLogger::logCommon(const RunLog &log, const RunPlan *plan, bool doLogConfig, bool doLogSteps, bool doLogExit, bool doLogStepTime, bool doLogExitTime) const {
// Init writer
nlohmann::ordered_json j;
logCommon(j, log, plan, doLogConfig, doLogSteps, doLogExit, doLogStepTime, doLogExitTime);
// Perform output
std::ofstream out(out_path, std::ios::binary | (truncateFile ? std::ofstream::trunc : std::ofstream::app));
if (!out.is_open()) {
THROW exception::JSONError("Unable to open file '%s' for writing\n", out_path.c_str());
}
if (prettyPrint) {
out << std::setw(4);
}
out << j;
out.close();
}
} // namespace io
} // namespace flamegpu