Program Listing for File RunPlan.h

Return to documentation for file (include/flamegpu/simulation/RunPlan.h)

#ifndef INCLUDE_FLAMEGPU_SIMULATION_RUNPLAN_H_
#define INCLUDE_FLAMEGPU_SIMULATION_RUNPLAN_H_

#include <unordered_map>
#include <string>
#include <typeinfo>
#include <vector>
#include <memory>

#include "flamegpu/model/EnvironmentDescription.h"
#include "flamegpu/detail/Any.h"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/simulation/CUDAEnsemble.h"


namespace flamegpu {
namespace detail {
class AbstractSimRunner;
}
class ModelDescription;
class RunPlanVector;
class CUDASimulation;

namespace io {
class JSONLogger;
class XMLLogger;
}  // namespace io

class RunPlan {
    friend class RunPlanVector;
    friend class detail::AbstractSimRunner;
    friend class CUDASimulation;
    friend class io::JSONLogger;
    friend class io::XMLLogger;

 public:
    explicit RunPlan(const ModelDescription &environment);
    RunPlan& operator=(const RunPlan& other);

    void setRandomSimulationSeed(uint64_t random_seed);
    void setSteps(unsigned int steps);
    void setOutputSubdirectory(const std::string &subdir);
    template<typename T>
    void setProperty(const std::string &name, T value);
    template<typename T, flamegpu::size_type N>
    void setProperty(const std::string &name, const std::array<T, N> &value);
    template<typename T, flamegpu::size_type N = 0>
    void setProperty(const std::string &name, flamegpu::size_type index, T value);
#ifdef SWIG
    template<typename T>
    void setPropertyArray(const std::string &name, const std::vector<T> &value);
#endif
    uint64_t getRandomSimulationSeed() const;
    unsigned int getSteps() const;
    std::string getOutputSubdirectory() const;

    template<typename T>
    T getProperty(const std::string &name) const;
    template<typename T, flamegpu::size_type N>
    std::array<T, N> getProperty(const std::string &name) const;
    template<typename T, flamegpu::size_type N = 0>
    T getProperty(const std::string &name, flamegpu::size_type index) const;
#ifdef SWIG
    template<typename T>
    std::vector<T> getPropertyArray(const std::string &name);
#endif

    RunPlanVector operator+(const RunPlan& rhs) const;
    RunPlanVector operator+(const RunPlanVector& rhs) const;
    RunPlanVector operator*(unsigned int rhs) const;

    bool operator==(const RunPlan& rhs) const;
    bool operator!=(const RunPlan& rhs) const;

 private:
    explicit RunPlan(const std::shared_ptr<const std::unordered_map<std::string, EnvironmentData::PropData>> &environment, bool allow_0);
    uint64_t random_seed;
    unsigned int steps;
    std::string output_subdirectory;
    std::unordered_map<std::string, detail::Any> property_overrides;
    // This needs to be shared_ptr, reference goes out of scope, otherwise have a copy of the map per RunPlan
    std::shared_ptr<const std::unordered_map<std::string, EnvironmentData::PropData>> environment;
    bool allow_0_steps;
};

template<typename T>
void RunPlan::setProperty(const std::string &name, T value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlan::setProperty()\n",
            name.c_str(), it->second.data.elements);
    }
    // Store property
    property_overrides.erase(name);
    property_overrides.emplace(name, detail::Any(&value, sizeof(T), typeid(typename detail::type_decode<T>::type_t), detail::type_decode<T>::len_t));
}
template<typename T, flamegpu::size_type N>
void RunPlan::setProperty(const std::string &name, const std::array<T, N> &value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != N * detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property array '%s' length mismatch %u != %u "
            "in RunPlan::setProperty()\n",
            name.c_str(), it->second.data.elements, N * detail::type_decode<T>::len_t);
    }
    // Store property
    property_overrides.erase(name);
    property_overrides.emplace(name, detail::Any(value.data(), sizeof(T) * N, typeid(typename detail::type_decode<T>::type_t), detail::type_decode<T>::len_t * N));
}
template<typename T, flamegpu::size_type N>
void RunPlan::setProperty(const std::string &name, const flamegpu::size_type index, T value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (N && N != it->second.data.elements) {
        THROW exception::OutOfBoundsException("Environment property '%s' length mismatch '%u' != '%u', "
            "in RunPlan::setProperty()\n",
            name.c_str(), N, it->second.data.elements);
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (it->second.data.elements < t_index || t_index < index) {
        THROW exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlan::setProperty()\n");
    }
    // Check whether array already exists in property overrides
    auto it2 = property_overrides.find(name);
    if (it2 == property_overrides.end()) {
        // Clone default property first
        it2 = property_overrides.emplace(name, it->second.data).first;
    }
    // Store property
    memcpy(static_cast<T*>(it2->second.ptr) + index, &value, sizeof(T));
}
#ifdef SWIG
template<typename T>
void RunPlan::setPropertyArray(const std::string &name, const std::vector<T> &value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::setPropertyArray()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::setPropertyArray()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (detail::type_decode<T>::len_t * value.size() != it->second.data.elements) {
        THROW exception::InvalidEnvPropertyType("Environment property array length does not match the value provided, %u != %llu,"
            "in RunPlan::setPropertyArray()\n",
            name.c_str(), detail::type_decode<T>::len_t * value.size(), it->second.data.elements);
    }
    // Store property
    property_overrides.erase(name);
    property_overrides.emplace(name, detail::Any(value.data(), sizeof(T) * value.size(), typeid(typename detail::type_decode<T>::type_t), detail::type_decode<T>::len_t * value.size()));
}
#endif

template<typename T>
T RunPlan::getProperty(const std::string &name) const {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.elements);
    }
    // Check whether property already exists in property overrides
    const auto it2 = property_overrides.find(name);
    if (it2 != property_overrides.end()) {
        // The property has been overridden, return the value from the override.
        return *static_cast<T *>(it2->second.ptr);
    } else {
        // The property has not been overridden, so return the value from the environment
        return *static_cast<T *>(it->second.data.ptr);
    }
}
template<typename T, flamegpu::size_type N>
std::array<T, N> RunPlan::getProperty(const std::string &name) const {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != N * detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property array '%s' length mismatch %u != %u "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.elements, N);
    }
    // Check whether array already exists in property overrides
    const auto it2 = property_overrides.find(name);
    std::array<T, N> rtn;
    if (it2 != property_overrides.end()) {
        // The property has been overridden, return the override
        memcpy(rtn.data(), it2->second.ptr, it2->second.length);
    } else {
        // The property has not been overridden, return the environment property
        memcpy(rtn.data(), it->second.data.ptr, it->second.data.length);
    }
    return rtn;
}
template<typename T, flamegpu::size_type N>
T RunPlan::getProperty(const std::string &name, const flamegpu::size_type index) const {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (N && N != it->second.data.elements) {
        THROW exception::OutOfBoundsException("Environment property '%s' length mismatch '%u' != '%u', "
            "in RunPlan::getProperty()\n",
            name.c_str(), N, it->second.data.elements);
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (it->second.data.elements < t_index || t_index < index) {
        THROW exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlan::getProperty()\n");
    }
    // Check whether property already exists in property overrides
    const auto it2 = property_overrides.find(name);
    if (it2 != property_overrides.end()) {
        // The property has been overridden, return the override
        return static_cast<T *>(it2->second.ptr)[index];
    } else {
        // The property has not been overridden, return the environment property
        return static_cast<T *>(it->second.data.ptr)[index];
    }
}
#ifdef SWIG
template<typename T>
std::vector<T> RunPlan::getPropertyArray(const std::string &name) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlan::getProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements % detail::type_decode<T>::len_t != 0) {
        THROW exception::InvalidEnvPropertyType("Environmental property array '%s' length (%u) is not a multiple of vector length (%u), "
            "in RunPlan::getPropertyArray().",
             name.c_str(), detail::type_decode<T>::len_t, it->second.data.elements, detail::type_decode<T>::len_t);
    }
    // Check whether array already exists in property overrides
    const auto it2 = property_overrides.find(name);
    std::vector<T> rtn(it->second.data.elements / detail::type_decode<T>::len_t);
    if (it2 != property_overrides.end()) {
        // The property has been overridden, return the override
        memcpy(rtn.data(), it2->second.ptr, it2->second.length);
    } else {
        // The property has not been overridden, return the environment property
        memcpy(rtn.data(), it->second.data.ptr, it->second.data.length);
    }
    return rtn;
}
#endif

}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_RUNPLAN_H_