Program Listing for File JSONLogger.cu
↰ Return to documentation for file (src/flamegpu/io/JSONLogger.cu
)
#include "flamegpu/io/JSONLogger.h"
#include <rapidjson/writer.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.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);
}
template<typename T>
void JSONLogger::writeAny(T &writer, const detail::Any &value, const unsigned int elements) const {
// Output value
if (elements > 1) {
writer.StartArray();
}
// Loop through elements, to construct array
for (unsigned int el = 0; el < elements; ++el) {
if (value.type == std::type_index(typeid(float))) {
writer.Double(static_cast<const float*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(double))) {
writer.Double(static_cast<const double*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int64_t))) {
writer.Int64(static_cast<const int64_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint64_t))) {
writer.Uint64(static_cast<const uint64_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int32_t))) {
writer.Int(static_cast<const int32_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint32_t))) {
writer.Uint(static_cast<const uint32_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int16_t))) {
writer.Int(static_cast<const int16_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(uint16_t))) {
writer.Uint(static_cast<const uint16_t*>(value.ptr)[el]);
} else if (value.type == std::type_index(typeid(int8_t))) {
writer.Int(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))) {
writer.Uint(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))) {
writer.Int(static_cast<int32_t>(static_cast<const char*>(value.ptr)[el])); // Char outputs weird if being used as an integer
} else {
THROW exception::RapidJSONError("Attempting to export value of unsupported type '%s', "
"in JSONLogger::writeAny()\n", value.type.name());
}
}
if (elements > 1) {
writer.EndArray();
}
}
template<typename T>
void JSONLogger::writeLogFrame(T& writer, const StepLogFrame& frame, bool logTime) const {
writer.StartObject();
{
if (logTime) {
writer.Key("step_time");
writer.Double(frame.getStepTime());
}
writeCommonLogFrame(writer, frame);
}
writer.EndObject();
}
template<typename T>
void JSONLogger::writeLogFrame(T& writer, const ExitLogFrame& frame, bool logTime) const {
writer.StartObject();
{
if (logTime) {
writer.Key("rtc_time");
writer.Double(frame.getRTCTime());
writer.Key("init_time");
writer.Double(frame.getInitTime());
writer.Key("exit_time");
writer.Double(frame.getExitTime());
writer.Key("total_time");
writer.Double(frame.getTotalTime());
}
writeCommonLogFrame(writer, frame);
}
writer.EndObject();
}
template<typename T>
void JSONLogger::writeCommonLogFrame(T &writer, const LogFrame &frame) const {
// Add static items
writer.Key("step_index");
writer.Uint(frame.getStepCount());
if (frame.getEnvironment().size()) {
// Add dynamic environment values
writer.Key("environment");
writer.StartObject();
{
for (const auto &prop : frame.getEnvironment()) {
writer.Key(prop.first.c_str());
// Log value
writeAny(writer, prop.second, prop.second.elements);
}
}
writer.EndObject();
}
if (frame.getAgents().size()) {
// Add dynamic agent values
writer.Key("agents");
writer.StartObject();
{
// This assumes that sort order places all agents of same name, different state consecutively
std::string current_agent;
for (const auto &agent : frame.getAgents()) {
// Start/end new agent
if (current_agent != agent.first.first) {
if (!current_agent.empty())
writer.EndObject();
current_agent = agent.first.first;
writer.Key(current_agent.c_str());
writer.StartObject();
}
// Start new state
writer.Key(agent.first.second.c_str());
writer.StartObject();
{
// Log agent count if provided
if (agent.second.second != UINT_MAX) {
writer.Key("count");
writer.Uint(agent.second.second);
}
if (agent.second.first.size()) {
writer.Key("variables");
writer.StartObject();
// 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) {
// Start/end new variable
if (current_variable != var.first.name) {
if (!current_variable.empty())
writer.EndObject();
current_variable = var.first.name;
writer.Key(current_variable.c_str());
writer.StartObject();
}
// Build name key for the variable
writer.Key(LoggingConfig::toString(var.first.reduction));
// Log value
writeAny(writer, var.second, 1);
}
if (!current_variable.empty())
writer.EndObject();
writer.EndObject();
}
}
writer.EndObject();
}
if (!current_agent.empty())
writer.EndObject();
}
writer.EndObject();
}
}
template<typename T>
void JSONLogger::logConfig(T &writer, const RunLog &log) const {
writer.Key("config");
writer.StartObject();
{
writer.Key("random_seed");
writer.Uint64(log.getRandomSeed());
}
writer.EndObject();
}
template<typename T>
void JSONLogger::logConfig(T &writer, const RunPlan &plan) const {
writer.Key("config");
writer.StartObject();
{
// Add static items
writer.Key("random_seed");
writer.Uint64(plan.getRandomSimulationSeed());
writer.Key("steps");
writer.Uint(plan.getSteps());
// Add dynamic environment overrides
writer.Key("environment");
writer.StartObject();
{
for (const auto &prop : plan.property_overrides) {
const EnvironmentData::PropData &env_prop = plan.environment->at(prop.first);
writer.Key(prop.first.c_str());
writeAny(writer, prop.second, env_prop.data.elements);
}
}
writer.EndObject();
}
writer.EndObject();
}
template<typename T>
void JSONLogger::logPerformanceSpecs(T& writer, const RunLog& log) const {
writer.Key("performance_specs");
writer.StartObject();
{
// Add static items
writer.Key("device_name");
writer.String(log.getPerformanceSpecs().device_name.c_str());
writer.Key("device_cc_major");
writer.Int(log.getPerformanceSpecs().device_cc_major);
writer.Key("device_cc_minor");
writer.Int(log.getPerformanceSpecs().device_cc_minor);
writer.Key("cuda_version");
writer.Int(log.getPerformanceSpecs().cuda_version);
writer.Key("seatbelts");
writer.Bool(log.getPerformanceSpecs().seatbelts);
writer.Key("flamegpu_version");
writer.String(log.getPerformanceSpecs().flamegpu_version.c_str());
}
writer.EndObject();
}
template<typename T>
void JSONLogger::logSteps(T &writer, const RunLog &log, bool logTime) const {
writer.Key("steps");
writer.StartArray();
{
for (const auto &step : log.getStepLog()) {
writeLogFrame(writer, step, logTime);
}
}
writer.EndArray();
}
template<typename T>
void JSONLogger::logExit(T &writer, const RunLog &log, bool logTime) const {
writer.Key("exit");
writeLogFrame(writer, log.getExitLog(), logTime);
}
template<typename T>
void JSONLogger::logCommon(T &writer, const RunLog &log, const RunPlan *plan, bool doLogConfig, bool doLogSteps, bool doLogExit, bool doLogStepTime, bool doLogExitTime) const {
// Begin json output object
writer->StartObject();
{
// Log config
if (plan) {
logConfig(*writer, *plan);
} else if (doLogConfig) {
logConfig(*writer, log);
}
if (doLogStepTime || doLogExitTime) {
logPerformanceSpecs(*writer, log);
}
// Log step log
if (doLogSteps) {
logSteps(*writer, log, doLogStepTime);
}
// Log exit log
if (doLogExit) {
logExit(*writer, log, doLogExitTime);
}
}
// End Json file
writer->EndObject();
}
void JSONLogger::logCommon(const RunLog &log, const RunPlan *plan, bool doLogConfig, bool doLogSteps, bool doLogExit, bool doLogStepTime, bool doLogExitTime) const {
// Init writer
rapidjson::StringBuffer s;
if (prettyPrint) {
// rapidjson::Writer doesn't have virtual methods, so can't pass rapidjson::PrettyWriter around as ptr to rapidjson::writer
auto writer = new rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfFlag>(s);
writer->SetIndent('\t', 1);
logCommon(writer, log, plan, doLogConfig, doLogSteps, doLogExit, doLogStepTime, doLogExitTime);
delete writer;
} else {
auto *writer = new rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfFlag>(s);
logCommon(writer, log, plan, doLogConfig, doLogSteps, doLogExit, doLogStepTime, doLogExitTime);
delete writer;
}
// Perform output
std::ofstream out(out_path, truncateFile ? std::ofstream::trunc : std::ofstream::app);
if (!out.is_open()) {
THROW exception::RapidJSONError("Unable to open file '%s' for writing\n", out_path.c_str());
}
out << s.GetString();
out << "\n";
out.close();
}
} // namespace io
} // namespace flamegpu