Program Listing for File LayerDescription.cpp

Return to documentation for file (src/flamegpu/model/LayerDescription.cpp)

#include "flamegpu/model/LayerDescription.h"

#include <utility>
#include <string>

#include "flamegpu/model/AgentFunctionDescription.h"
#include "flamegpu/model/SubModelDescription.h"
#include "flamegpu/model/SubModelData.h"

namespace flamegpu {

CLayerDescription::CLayerDescription(std::shared_ptr<LayerData> data)
    : layer(std::move(data)) { }
CLayerDescription::CLayerDescription(std::shared_ptr<const LayerData> data)
    : layer(std::const_pointer_cast<LayerData>(data)) { }

bool CLayerDescription::operator==(const CLayerDescription& rhs) const {
    return *this->layer == *rhs.layer;  // Compare content is functionally the same
}
bool CLayerDescription::operator!=(const CLayerDescription& rhs) const {
    return !(*this == rhs);
}

std::string CLayerDescription::getName() const {
    return layer->name;
}

flamegpu::size_type CLayerDescription::getIndex() const {
    return layer->index;
}
flamegpu::size_type CLayerDescription::getAgentFunctionsCount() const {
    // Safe down-cast
    return static_cast<flamegpu::size_type>(layer->agent_functions.size());
}
flamegpu::size_type CLayerDescription::getHostFunctionsCount() const {
    // Safe down-cast
    return static_cast<flamegpu::size_type>(layer->host_functions.size());
}
CAgentFunctionDescription CLayerDescription::getAgentFunction(unsigned int index) const {
    if (index < layer->agent_functions.size()) {
        auto it = layer->agent_functions.begin();
        for (unsigned int i = 0; i < index; ++i)
            ++it;
        return CAgentFunctionDescription(*it);
    }
    THROW exception::OutOfBoundsException("Index %d is out of bounds (only %d items exist) "
        "in LayerDescription.getAgentFunction().",
        index, layer->agent_functions.size());
}
FLAMEGPU_HOST_FUNCTION_POINTER CLayerDescription::getHostFunction(unsigned int index) const {
    if (index < layer->host_functions.size()) {
        auto it = layer->host_functions.begin();
        for (unsigned int i = 0; i < index; ++i)
            ++it;
        return *it;
    }
    THROW exception::OutOfBoundsException("Index %d is out of bounds (only %d items exist) "
        "in LayerDescription.getHostFunction().",
        index, layer->host_functions.size());
}

LayerDescription::LayerDescription(std::shared_ptr<LayerData> data)
    : CLayerDescription(std::move(data)) { }

void LayerDescription::addAgentFunction(const AgentFunctionDescription& afd) {
    addAgentFunction(CAgentFunctionDescription(afd));
}
void LayerDescription::addAgentFunction(const CAgentFunctionDescription &afd) {
    if (afd.function->model.lock() == layer->model.lock()) {
        auto m = layer->model.lock();
        // Find the same afd in the model hierarchy
        for (auto &agt : m->agents) {
            for (auto &fn : agt.second->functions) {
                if (*fn.second == afd) {
                    addAgentFunction(agt.first, fn.first);
                    return;
                }
            }
        }
    }
    THROW exception::DifferentModel("Attempted to add agent function description which is from a different model, "
        "in LayerDescription::addAgentFunction().");
}
void LayerDescription::addAgentFunction(const std::string &agentName, const std::string &functionName) {
    if (layer->sub_model) {
        THROW exception::InvalidLayerMember("A layer containing agent functions and/or host functions, may not also contain a submodel, "
        "in LayerDescription::addAgentFunction()\n");
    }
    if (layer->host_functions.size() || layer->host_functions_callbacks.size()) {
        THROW exception::InvalidLayerMember("A layer containing host functions, may not also contain agent functions, "
            "in LayerDescription::addAgentFunction()\n");
    }
    if (!layer->host_functions.empty() || !layer->host_functions_callbacks.empty()) {
        THROW exception::InvalidLayerMember("A layer containing a host function may not also contain an agent function"
            "in LayerDescription::addAgentFunction()\n");
    }
    // Locate the matching agent function in the model hierarchy
    auto mdl = layer->model.lock();
    if (!mdl) {
        THROW exception::ExpiredWeakPtr();
    }
    auto a = mdl->agents.find(agentName);
    if (a != mdl->agents.end()) {
        auto f = a->second->functions.find(functionName);
        if (f != a->second->functions.end()) {
            // Check it's not a duplicate agent fn
            if (layer->agent_functions.find(f->second) != layer->agent_functions.end()) {
                THROW exception::InvalidAgentFunc("Attempted to add agent function '%s' owned by agent '%s' to same layer twice, "
                    "in LayerDescription::addAgentFunction().",
                    functionName.c_str(), agentName.c_str());
            }
            auto a_agent_out = f->second->agent_output.lock();
            auto a_message_out = f->second->message_output.lock();
            auto a_message_in = f->second->message_input.lock();
            for (const auto &b : layer->agent_functions) {
                if (auto parent = b->parent.lock()) {
                    // Check that layer does not already contain function with same agent + states
                    // If agent matches
                    if (parent->name == a->second->name) {
                        // If they share a state
                        if (b->initial_state == f->second->initial_state ||
                            b->initial_state == f->second->end_state ||
                            b->end_state == f->second->initial_state ||
                            b->end_state == f->second->end_state) {
                            THROW exception::InvalidAgentFunc("Agent function '%s' owned by agent '%s' cannot be added to this layer as agent function '%s' "
                                "within the layer shares an input or output state, this is not permitted, "
                                "in LayerDescription::addAgentFunction().",
                                a->second->name.c_str(), agentName.c_str(), b->name.c_str());
                        }
                    }
                    // Check that the layer does not already contain function for the agent + state being output to
                    if (a_agent_out) {
                        // If agent matches
                        if (parent->name == a_agent_out->name) {
                            // If state matches
                            if (b->initial_state == f->second->agent_output_state) {
                                THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
                                    "within the layer requires the same agent state as an input, as this agent function births, "
                                    "in LayerDescription::addAgentFunction().",
                                    f->second->name.c_str(), b->name.c_str());
                            }
                        }
                    }
                    // Also check the inverse
                    auto b_agent_out = b->agent_output.lock();
                    if (b_agent_out) {
                        // If agent matches
                        if (a->second->name == b_agent_out->name) {
                            // If state matches
                            if (f->second->initial_state == b->agent_output_state) {
                                THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
                                    "within the layer agent births to the same agent state as this agent function requires as an input, "
                                    "in LayerDescription::addAgentFunction().",
                                    f->second->name.c_str(), b->name.c_str());
                            }
                        }
                    }
                }
                // Check the layer does not already contain function which outputs to same message list
                auto b_message_out = b->message_output.lock();
                auto b_message_in = b->message_input.lock();
                if ((a_message_out && b_message_out && a_message_out == b_message_out) ||
                    (a_message_out && b_message_in && a_message_out == b_message_in) ||
                    (a_message_in && b_message_out && a_message_in == b_message_out)) {  // Pointer comparison should be fine here
                    THROW exception::InvalidLayerMember("Agent functions '%s' cannot be added to this layer as agent function '%s' "
                        "within the layer also inputs or outputs to the same messagelist, this is not permitted, "
                        "in LayerDescription::addAgentFunction().",
                        f->second->name.c_str(), b->name.c_str());
                }
            }
            layer->agent_functions.insert(f->second);
            return;
        }
    }
    THROW exception::InvalidAgentFunc("Agent function '%s' owned by agent '%s' was not found, "
        "in LayerDescription::addAgentFunction()\n",
        functionName.c_str(), agentName.c_str());
}

void LayerDescription::addAgentFunction(const char * agentName, const char * functionName) {
    addAgentFunction(std::string(agentName), std::string(functionName));
}
void LayerDescription::addHostFunction(FLAMEGPU_HOST_FUNCTION_POINTER func_p) {
    if (layer->sub_model) {
        THROW exception::InvalidLayerMember("A layer containing a submodel may not also contain a host function, "
        "in LayerDescription::addHostFunction()\n");
    }
    if (!layer->host_functions.empty() || !layer->agent_functions.empty() || !layer->host_functions_callbacks.empty()) {
        THROW exception::InvalidLayerMember("A layer containing agent functions or a host function may not have another host function added, "
        "in LayerDescription::addHostFunction()\n");
    }
    if (!layer->host_functions.insert(func_p).second) {
        THROW exception::InvalidHostFunc("HostFunction has already been added to LayerDescription,"
            "in LayerDescription::addHostFunction().");
    }
}
void LayerDescription::addSubModel(const std::string &name) {
    if (!layer->host_functions.empty() || !layer->agent_functions.empty() || !layer->host_functions_callbacks.empty()) {
        THROW exception::InvalidLayerMember("A layer containing agent functions and/or host functions, may not also contain a submodel, "
        "in LayerDescription::addSubModel()\n");
    }
    if (layer->sub_model) {
        THROW exception::InvalidSubModel("Layer has already been assigned a submodel, "
            "in LayerDescription::addSubModel()\n");
    }
    // Find the correct submodel shared ptr
    auto mdl = layer->model.lock();
    if (!mdl) {
        THROW exception::ExpiredWeakPtr();
    }
    for (auto &sm : mdl->submodels) {
        if (sm.first == name) {
            layer->sub_model = sm.second;
            return;
        }
    }
    THROW exception::InvalidSubModel("SubModel '%s' does not belong to Model '%s', "
        "in LayerDescription::addSubModel()\n",
        name.c_str(), mdl->name.c_str());
}
void LayerDescription::addSubModel(const CSubModelDescription &submodel) {
    auto mdl = layer->model.lock();
    if (!mdl) {
        THROW exception::ExpiredWeakPtr();
    }
    if (submodel.submodel->model.lock() == mdl) {
        // Find the correct submodel shared ptr
        for (auto &sm : mdl->submodels) {
            if (sm.second.get() == submodel.submodel.get()) {
                addSubModel(sm.first);
                return;
            }
        }
    }
    THROW exception::InvalidSubModel("SubModel '%s' does not belong to Model '%s', "
        "in LayerDescription::addSubModel()\n",
        submodel.submodel->name.c_str(), mdl->name.c_str());
}

void LayerDescription::_addHostFunction(HostFunctionCallback* func_callback) {
    if (layer->sub_model) {
        THROW exception::InvalidLayerMember("A layer containing a submodel may not also contain a host function, "
        "in LayerDescription::addHostFunctionCallback()\n");
    }
    if (!layer->host_functions.empty() || !layer->agent_functions.empty() || !layer->host_functions_callbacks.empty()) {
        THROW exception::InvalidLayerMember("A layer containing agent functions or a host function may not also contain a host function, "
        "in LayerDescription::addHostFunctionCallback()\n");
    }
    if (!layer->host_functions_callbacks.insert(func_callback).second) {
            THROW exception::InvalidHostFunc("Attempted to add same host function callback twice,"
                "in LayerDescription::addHostFunctionCallback()");
        }
}
AgentFunctionDescription LayerDescription::AgentFunction(unsigned int index) {
    if (index < layer->agent_functions.size()) {
        auto it = layer->agent_functions.begin();
        for (unsigned int i = 0; i < index; ++i)
            ++it;
        return AgentFunctionDescription(*it);
    }
    THROW exception::OutOfBoundsException("Index %d is out of bounds (only %d items exist) "
        "in LayerDescription.getAgentFunction().",
        index, layer->agent_functions.size());
}

}  // namespace flamegpu