Skip to content

Commit

Permalink
Add a basic test case (and fix) minimum viable product
Browse files Browse the repository at this point in the history
  • Loading branch information
Robadob committed Sep 21, 2024
1 parent ff60459 commit b46de24
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 70 deletions.
3 changes: 1 addition & 2 deletions include/flamegpu/detail/Any.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct Any {
memcpy(ptr, _ptr, length);
}
template<typename T>
explicit Any(const T other)
explicit Any(const T &other)
: ptr(malloc(sizeof(T)))
, length(sizeof(T))
, type(typeid(T))
Expand Down Expand Up @@ -87,7 +87,6 @@ struct Any {
*/
const unsigned int elements;
};

} // namespace detail
} // namespace flamegpu

Expand Down
2 changes: 2 additions & 0 deletions include/flamegpu/flamegpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#include "flamegpu/simulation/LogFrame.h"
#include "flamegpu/util/cleanup.h"
#include "flamegpu/io/Telemetry.h"
#include "flamegpu/io/JSONRunPlanWriter.h"
#include "flamegpu/io/JSONRunPlanReader.h"

// This include has no impact if FLAMEGPU_VISUALISATION is not defined
#include "flamegpu/visualiser/visualiser_api.h"
Expand Down
9 changes: 3 additions & 6 deletions include/flamegpu/io/JSONRunPlanWriter.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
#ifndef INCLUDE_FLAMEGPU_IO_JSONRUNPLANWRITER_H_
#define INCLUDE_FLAMEGPU_IO_JSONRUNPLANWRITER_H_

#include <rapidjson/writer.h>

#include <memory>
#include <string>


#include "flamegpu/simulation/RunPlanVector.h"

namespace flamegpu {
Expand All @@ -15,14 +12,14 @@ namespace io {
* JSON format writer of RunPlanVector
*/
class JSONRunPlanWriter {
// Typedef for the writer used, as the full template specification is way too long
typedef rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfFlag> GenericJSONWriter;
/**
* Utility method for writing out a single RunPlan
* @param writer An initialised RapidJSON writer.
* @param rp RunPlan to be writer
* @tparam T Should be rapidjson::Writer
*/
static void writeRunPlan(std::unique_ptr<GenericJSONWriter>& writer, const RunPlan& rp);
template <typename T>
static void writeRunPlan(std::unique_ptr<T>& writer, const RunPlan& rp);

public:
/**
Expand Down
108 changes: 47 additions & 61 deletions src/flamegpu/io/JSONRunPlanReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace flamegpu {
namespace io {
class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, JSONRunPlanReader_impl> {
enum Mode { Root, Plan, Core, Properties, PropertyArray, Nop };
enum Mode { Root, PlanVector, Plan, Properties, PropertyArray, Nop };
std::stack<Mode> mode;
std::string lastKey;
/**
Expand All @@ -25,7 +25,8 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
public:
JSONRunPlanReader_impl(const std::string& _filename, RunPlanVector& _rpv)

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (11.0, 3.12, OFF, Release, ubuntu-20.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.0, 3.12, OFF, Release, ubuntu-20.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (11.2, 3.12, OFF, Release, ubuntu-20.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.0, 3.12, OFF, Release, ubuntu-22.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (11.8, 3.12, OFF, Release, ubuntu-22.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.6, 3.12, OFF, Beltsoff, ubuntu-22.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (11.8, 3.12, ON, Release, ubuntu-22.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.6, 3.12, OFF, Release, ubuntu-22.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (11.2, 3.12, ON, Release, ubuntu-20.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.0, 3.12, ON, Release, ubuntu-20.04)

when initialized here [-Werror=reorder]

Check failure on line 26 in src/flamegpu/io/JSONRunPlanReader.cpp

View workflow job for this annotation

GitHub Actions / build (12.6, 3.12, ON, Release, ubuntu-22.04)

when initialized here [-Werror=reorder]
: filename(_filename)
, rpv(_rpv) { }
, rpv(_rpv)
, current_array_index(0) { }
template<typename T>
bool processValue(const T val) {
Mode isArray = Nop;
Expand All @@ -44,30 +45,30 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
"in JSONRunPlanReader::load()\n", lastKey.c_str(), current_array_index, it->second.data.elements);
}
// Retrieve the linked any and replace the value
const auto rp = rpv.end();
auto &rp = rpv.back();
const std::type_index val_type = it->second.data.type;
if (it->second.data.elements ==0) {
if (it->second.data.elements == 1) {
// Properties don't exist by default, so must be created
if (val_type == std::type_index(typeid(float))) {
rp->setProperty(lastKey, static_cast<float>(val));
rp.setProperty(lastKey, static_cast<float>(val));
} else if (val_type == std::type_index(typeid(double))) {
rp->setProperty(lastKey, static_cast<double>(val));
rp.setProperty(lastKey, static_cast<double>(val));
} else if (val_type == std::type_index(typeid(int64_t))) {
rp->setProperty(lastKey, static_cast<int64_t>(val));
rp.setProperty(lastKey, static_cast<int64_t>(val));
} else if (val_type == std::type_index(typeid(uint64_t))) {
rp->setProperty(lastKey, static_cast<uint64_t>(val));
rp.setProperty(lastKey, static_cast<uint64_t>(val));
} else if (val_type == std::type_index(typeid(int32_t))) {
rp->setProperty(lastKey, static_cast<int32_t>(val));
rp.setProperty(lastKey, static_cast<int32_t>(val));
} else if (val_type == std::type_index(typeid(uint32_t))) {
rp->setProperty(lastKey, static_cast<uint32_t>(val));
rp.setProperty(lastKey, static_cast<uint32_t>(val));
} else if (val_type == std::type_index(typeid(int16_t))) {
rp->setProperty(lastKey, static_cast<int16_t>(val));
rp.setProperty(lastKey, static_cast<int16_t>(val));
} else if (val_type == std::type_index(typeid(uint16_t))) {
rp->setProperty(lastKey, static_cast<uint16_t>(val));
rp.setProperty(lastKey, static_cast<uint16_t>(val));
} else if (val_type == std::type_index(typeid(int8_t))) {
rp->setProperty(lastKey, static_cast<int8_t>(val));
rp.setProperty(lastKey, static_cast<int8_t>(val));
} else if (val_type == std::type_index(typeid(uint8_t))) {
rp->setProperty(lastKey, static_cast<uint8_t>(val));
rp.setProperty(lastKey, static_cast<uint8_t>(val));
} else {
THROW exception::RapidJSONError("RunPlan contains property '%s' of unsupported type '%s', "
"in JSONRunPlanReader::load()\n", lastKey.c_str(), val_type.name());
Expand All @@ -76,12 +77,12 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
// Arrays require more fiddly handling
// Create the array if this is the first item
if (current_array_index == 0) {
rp->property_overrides.emplace(lastKey, detail::Any(it->second));
rp.property_overrides.emplace(lastKey, detail::Any(it->second.data));
}
// Copy in the specific value
const auto prop_it = rp->property_overrides.at(lastKey);
const auto prop_it = rp.property_overrides.at(lastKey);
if (val_type == std::type_index(typeid(float))) {
static_cast<double*>(const_cast<void*>(prop_it.ptr))[current_array_index++] = static_cast<float>(val);
static_cast<float*>(const_cast<void*>(prop_it.ptr))[current_array_index++] = static_cast<float>(val);
} else if (val_type == std::type_index(typeid(double))) {
static_cast<double*>(const_cast<void*>(prop_it.ptr))[current_array_index++] = static_cast<double>(val);
} else if (val_type == std::type_index(typeid(int64_t))) {
Expand All @@ -105,7 +106,15 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
"in JSONRunPlanReader::load()\n", lastKey.c_str(), val_type.name());
}
}
} else {
} else if (mode.top() == Plan) {
if (lastKey == "steps") {
rpv.back().setSteps(static_cast<uint32_t>(val));
} else if (lastKey == "random_seed") {
rpv.back().setRandomSimulationSeed(static_cast<uint64_t>(val));
} else {
THROW exception::RapidJSONError("Unexpected value whilst parsing input file '%s'.\n", filename.c_str());
}
} else {
THROW exception::RapidJSONError("Unexpected value whilst parsing input file '%s'.\n", filename.c_str());
}
if (isArray == PropertyArray) {
Expand All @@ -117,31 +126,17 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
bool Bool(bool b) { return processValue<bool>(b); }
bool Int(int i) { return processValue<int32_t>(i); }
bool Uint(unsigned u) {
if (mode.top() == Plan) {
if (lastKey == "steps") {
rpv.end()->setSteps(u);
return true;
}
return false;
}
return processValue<uint32_t>(u);
}
bool Int64(int64_t i) { return processValue<int64_t>(i); }
bool Uint64(uint64_t u) {
if (mode.top() == Plan) {
if (lastKey == "random_seed") {
rpv.end()->setRandomSimulationSeed(u);
return true;
}
return false;
}
return processValue<uint64_t>(u);
}
bool Double(double d) { return processValue<double>(d); }
bool String(const char*s, rapidjson::SizeType, bool) {
if (mode.top() == Plan) {
if (lastKey == "output_subdirectory") {
rpv.end()->setOutputSubdirectory(s);
rpv.back().setOutputSubdirectory(s);
return true;
}
}
Expand All @@ -151,20 +146,13 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
bool StartObject() {
if (mode.empty()) {
mode.push(Root);
} else if (mode.top() == Plan) {
if (lastKey == "RunPlanVector") {
mode.push(Core);
} else {
THROW exception::RapidJSONError("Unexpected object start whilst parsing input file '%s'.\n", filename.c_str());
}
} else if (mode.top() == Core) {
if (lastKey == "properties") {
mode.push(Properties);
} else {
THROW exception::RapidJSONError("Unexpected object start whilst parsing input file '%s'.\n", filename.c_str());
}
} else if (mode.top() == PropertyArray) {
} else if (mode.top() == PlanVector) {
// Starting a new element of the RunPlanVector
rpv.push_back(RunPlan(rpv.environment, rpv.allow_0_steps));
mode.push(Plan);
} else if (mode.top() == Plan && lastKey == "properties") {
// Starting a new element of the RunPlanVector
mode.push(Properties);
} else {
THROW exception::RapidJSONError("Unexpected object start whilst parsing input file '%s'.\n", filename.c_str());
}
Expand All @@ -179,31 +167,29 @@ class JSONRunPlanReader_impl : public rapidjson::BaseReaderHandler<rapidjson::UT
return true;
}
bool StartArray() {
if (current_array_index != 0) {
THROW exception::RapidJSONError("Array start when current_array_index !=0, in file '%s'. This should never happen.\n", filename.c_str());
}
if (mode.top() == Plan && lastKey == "properties") {
mode.push(Properties);
} else if (mode.top() == Properties) {
if (mode.top() == Properties) {
if (current_array_index != 0) {
THROW exception::RapidJSONError("Array start when current_array_index !=0, in file '%s'. This should never happen.\n", filename.c_str());
}
mode.push(PropertyArray);
} else if (mode.top() == Root) {
mode.push(PlanVector);
} else {
THROW exception::RapidJSONError("Unexpected array start whilst parsing input file '%s'.\n", filename.c_str());
}
return true;
}
bool EndArray(rapidjson::SizeType) {
if (mode.top() == PropertyArray) {
mode.pop();
if (mode.top() == Properties) {
// Confirm env array had correct number of elements
const auto &prop = rpv.environment->at(lastKey);
if (current_array_index != prop.data.elements) {
THROW exception::RapidJSONError("Input file contains property '%s' with %u elements expected %u,"
"in JSONRunPlanReader::load()\n", lastKey.c_str(), current_array_index, prop.data.elements);
}
// Confirm env array had correct number of elements
const auto& prop = rpv.environment->at(lastKey);
if (current_array_index != prop.data.elements) {
THROW exception::RapidJSONError("Input file contains property '%s' with %u elements expected %u,"
"in JSONRunPlanReader::load()\n", lastKey.c_str(), current_array_index, prop.data.elements);
}
current_array_index = 0;
} else if (mode.top() == Properties) {
mode.pop();
} else if (mode.top() == PlanVector) {
mode.pop();
} else {
THROW exception::RapidJSONError("Unexpected array end whilst parsing input file '%s'.\n", filename.c_str());
Expand Down
11 changes: 10 additions & 1 deletion src/flamegpu/io/JSONRunPlanWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

namespace flamegpu {
namespace io {

// Typedef for the writer used, as the full template specification is way too long
typedef rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfFlag> GenericJSONWriter;

void JSONRunPlanWriter::save(const RunPlanVector& rpv, const std::string& output_filepath, const bool pretty_print) {
// Init writer
auto buffer = rapidjson::StringBuffer();
Expand Down Expand Up @@ -44,7 +48,10 @@ void JSONRunPlanWriter::save(const RunPlanVector& rpv, const std::string& output
* @param writer An initialised RapidJSON writer.
* @param rp RunPlan to be writer
*/
void JSONRunPlanWriter::writeRunPlan(std::unique_ptr<GenericJSONWriter> &writer, const RunPlan &rp) {

template <typename T>
void JSONRunPlanWriter::writeRunPlan(std::unique_ptr<T> &writer, const RunPlan &rp) {
writer->StartObject();
// Core
writer->Key("random_seed");
writer->Uint64(rp.random_seed);
Expand Down Expand Up @@ -100,6 +107,8 @@ void JSONRunPlanWriter::writeRunPlan(std::unique_ptr<GenericJSONWriter> &writer,
}
}
writer->EndObject();
writer->EndObject();
}
template void JSONRunPlanWriter::writeRunPlan(std::unique_ptr<GenericJSONWriter>& writer, const RunPlan& rp);
} // namespace io
} // namespace flamegpu
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ SET(TESTS_SRC
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/io/test_io.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/io/test_logging.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/io/test_logging_exceptions.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/io/test_runplan_io.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/io/test_telemetry.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/model/test_environment_description.cu
${CMAKE_CURRENT_SOURCE_DIR}/test_cases/model/test_model.cu
Expand Down
Loading

0 comments on commit b46de24

Please sign in to comment.