Program Listing for File CSimulateDispersal.h

Return to documentation for file (CSimulateDispersal.h)

// This file is part of necsim project which is released under MIT license.
// See file **LICENSE.txt** or visit https://opensource.org/licenses/MIT) for full license details.

#ifndef NECSIM_CSIMULATEDISPERSAL_H
#define NECSIM_CSIMULATEDISPERSAL_H

#include <Python.h>
#include <structmember.h>
#include <cstring>
#include <string>
#include <memory>
#include "PyImports.h"
#include "PyTemplates.h"
#include "necsim.h"
#include "necsim/SimParameters.h"
#include "necsim/SimulateDispersal.h"
#include "CSimulation.h"

using namespace std;
using namespace necsim;

class PySimulateDispersal : public PyTemplate<SimulateDispersal>
{
public:
    shared_ptr<SimParameters> dispersalParameters;
    bool has_imported_maps;
    unique_ptr<std::string> output_database;
    bool printing;
    bool needs_update;

    PySimulateDispersal() : dispersalParameters(nullptr), has_imported_maps(false), output_database(nullptr),
                            printing(true), needs_update(true)
    {

    }

    void checkCompleted()
    {
        if(*output_database == "none")
        {
            throw runtime_error("Output database has not been set.");
        }
        if(!has_imported_maps)
        {
            throw runtime_error("Maps have not been imported - cannot start simulations.");
        }
    }

    void setDispersalParameters()
    {
        if(needs_update)
        {
            getGlobalLogger(logger, log_function);
            base_object->setSimulationParameters(dispersalParameters, printing);
            base_object->setDispersalParameters();
            printing = false;
            needs_update = false;
        }
    }
};

static PyObject* set_all_map_parameters(PySimulateDispersal* self, PyObject* args)
{
    char* landscape_type;
    char* fine_map_file;
    char* coarse_map_file;
    vector<string> path_fine;
    vector<unsigned long> number_fine;
    vector<double> rate_fine;
    vector<double> time_fine;
    vector<string> path_coarse;
    vector<unsigned long> number_coarse;
    vector<double> rate_coarse;
    vector<double> time_coarse;
    PyObject* p_path_fine;
    PyObject* p_number_fine;
    PyObject* p_rate_fine;
    PyObject* p_time_fine;
    PyObject* p_path_coarse;
    PyObject* p_number_coarse;
    PyObject* p_rate_coarse;
    PyObject* p_time_coarse;
    if(!PyArg_ParseTuple(args, "isiiiiiisiiiiisO!O!O!O!O!O!O!O!", &self->dispersalParameters->deme, &fine_map_file,
                         &self->dispersalParameters->fine_map_x_size, &self->dispersalParameters->fine_map_y_size,
                         &self->dispersalParameters->fine_map_x_offset, &self->dispersalParameters->fine_map_y_offset,
                         &self->dispersalParameters->sample_x_size, &self->dispersalParameters->sample_y_size,
                         &coarse_map_file, &self->dispersalParameters->coarse_map_x_size,
                         &self->dispersalParameters->coarse_map_y_size, &self->dispersalParameters->coarse_map_x_offset,
                         &self->dispersalParameters->coarse_map_y_offset, &self->dispersalParameters->coarse_map_scale,
                         &landscape_type, &PyList_Type, &p_path_fine, &PyList_Type, &p_number_fine, &PyList_Type,
                         &p_rate_fine, &PyList_Type, &p_time_fine, &PyList_Type, &p_path_coarse, &PyList_Type,
                         &p_number_coarse, &PyList_Type, &p_rate_coarse, &PyList_Type, &p_time_coarse))
    {
        return nullptr;
    }
    if(self->has_imported_maps)
    {
        PyErr_SetString(necsimError, (char*) "Maps have already been imported");
        return nullptr;
    }
    try
    {

        getGlobalLogger(self->logger, self->log_function);
        self->dispersalParameters->sample_x_offset = 0;
        self->dispersalParameters->sample_y_offset = 0;
        self->dispersalParameters->grid_x_size = self->dispersalParameters->sample_x_size;
        self->dispersalParameters->grid_y_size = self->dispersalParameters->sample_y_size;
        self->dispersalParameters->fine_map_file = fine_map_file;
        self->dispersalParameters->coarse_map_file = coarse_map_file;
        self->dispersalParameters->landscape_type = landscape_type;
        // Check for errors in each parsing of vector
        vector<bool> passed_errors;
        passed_errors.emplace_back(
                importPyListToVectorString(p_path_fine, path_fine, "Fine map paths must be strings."));
        passed_errors.emplace_back(
                importPyListToVectorULong(p_number_fine, number_fine, "Fine map numbers must be integers."));
        passed_errors.emplace_back(
                importPyListToVectorDouble(p_rate_fine, rate_fine, "Fine map rates must be floats."));
        passed_errors.emplace_back(
                importPyListToVectorDouble(p_time_fine, time_fine, "Fine map times must be floats."));
        passed_errors.emplace_back(
                importPyListToVectorString(p_path_coarse, path_coarse, "Coarse map paths must be strings."));
        passed_errors.emplace_back(
                importPyListToVectorULong(p_number_coarse, number_coarse, "Coarse map numbers must be integers."));
        passed_errors.emplace_back(
                importPyListToVectorDouble(p_rate_coarse, rate_coarse, "Coarse map rates must be floats."));
        passed_errors.emplace_back(
                importPyListToVectorDouble(p_time_coarse, time_coarse, "Coarse map times must be floats."));
        for(const auto &item: passed_errors)
        {
            if(!item)
            {
                removeGlobalLogger();
                return nullptr;
            }
        }
        self->dispersalParameters->setHistoricalMapParameters(path_fine, number_fine, rate_fine, time_fine, path_coarse,
                                                              number_coarse, rate_coarse, time_coarse);
        self->setDispersalParameters();
        self->base_object->importMaps();
        self->has_imported_maps = true;

    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;
}

PyObject* set_maps(PySimulateDispersal* self, PyObject* args)
{
    char* landscape_type;
    char* fine_map_file;
    char* coarse_map_file;
    // parse arguments
#ifdef DEBUG
    if(self == nullptr)
    {
        PyErr_SetString(necsimError, (char *) "self pointer is null. Please report this bug.");
        return nullptr;
    }
#endif // DEBUG
    if(!PyArg_ParseTuple(args, "isiiiiiisiiiiis", &self->dispersalParameters->deme, &fine_map_file,
                         &self->dispersalParameters->fine_map_x_size, &self->dispersalParameters->fine_map_y_size,
                         &self->dispersalParameters->fine_map_x_offset, &self->dispersalParameters->fine_map_y_offset,
                         &self->dispersalParameters->sample_x_size, &self->dispersalParameters->sample_y_size,
                         &coarse_map_file, &self->dispersalParameters->coarse_map_x_size,
                         &self->dispersalParameters->coarse_map_y_size, &self->dispersalParameters->coarse_map_x_offset,
                         &self->dispersalParameters->coarse_map_y_offset, &self->dispersalParameters->coarse_map_scale,
                         &landscape_type))
    {
        return nullptr;
    }
    if(self->has_imported_maps)
    {
        PyErr_SetString(necsimError, (char*) "Maps have already been imported");
        return nullptr;
    }
    try
    {
        getGlobalLogger(self->logger, self->log_function);
        self->dispersalParameters->sample_x_offset = 0;
        self->dispersalParameters->sample_y_offset = 0;
        self->dispersalParameters->grid_x_size = self->dispersalParameters->sample_x_size;
        self->dispersalParameters->grid_y_size = self->dispersalParameters->sample_y_size;
        self->dispersalParameters->fine_map_file = fine_map_file;
        self->dispersalParameters->coarse_map_file = coarse_map_file;
        self->dispersalParameters->landscape_type = landscape_type;
        self->needs_update = true;
        self->setDispersalParameters();
        self->base_object->importMaps();
        self->has_imported_maps = true;
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;

}

static PyObject* set_historical_map_parameters(PySimulateDispersal* self, PyObject* args)
{
    vector<string> path_fine;
    vector<unsigned long> number_fine;
    vector<double> rate_fine;
    vector<double> time_fine;
    vector<string> path_coarse;
    vector<unsigned long> number_coarse;
    vector<double> rate_coarse;
    vector<double> time_coarse;
    PyObject* p_path_fine;
    PyObject* p_number_fine;
    PyObject* p_rate_fine;
    PyObject* p_time_fine;
    PyObject* p_path_coarse;
    PyObject* p_number_coarse;
    PyObject* p_rate_coarse;
    PyObject* p_time_coarse;
    if(!PyArg_ParseTuple(args, "O!O!O!O!O!O!O!O!", &PyList_Type, &p_path_fine, &PyList_Type, &p_number_fine,
                         &PyList_Type, &p_rate_fine, &PyList_Type, &p_time_fine, &PyList_Type, &p_path_coarse,
                         &PyList_Type, &p_number_coarse, &PyList_Type, &p_rate_coarse, &PyList_Type, &p_time_coarse))
    {
        return nullptr;
    }
    try
    {
        getGlobalLogger(self->logger, self->log_function);
        importPyListToVectorString(p_path_fine, path_fine, "Fine map paths must be strings.");
        importPyListToVectorULong(p_number_fine, number_fine, "Fine map numbers must be integers.");
        importPyListToVectorDouble(p_rate_fine, rate_fine, "Fine map rates must be floats.");
        importPyListToVectorDouble(p_time_fine, time_fine, "Fine map times must be floats.");
        importPyListToVectorString(p_path_coarse, path_coarse, "Coarse map paths must be strings.");
        importPyListToVectorULong(p_number_coarse, number_coarse, "Coarse map numbers must be integers.");
        importPyListToVectorDouble(p_rate_coarse, rate_coarse, "Coarse map rates must be floats.");
        importPyListToVectorDouble(p_time_coarse, time_coarse, "Coarse map times must be floats.");
        self->dispersalParameters->setHistoricalMapParameters(path_fine, number_fine, rate_fine, time_fine, path_coarse,
                                                              number_coarse, rate_coarse, time_coarse);
        if(self->has_imported_maps && !path_fine.empty() && !number_fine.empty() && !rate_fine.empty()
           && !time_fine.empty() && !path_coarse.empty() && !number_coarse.empty() && !rate_coarse.empty()
           && !time_coarse.empty())
        {
            self->needs_update = true;
            self->setDispersalParameters();
            self->base_object->importMaps();
        }
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;
}

static PyObject* set_output_database(PySimulateDispersal* self, PyObject* args)
{

    char* output_file;
    // parse arguments
    if(!PyArg_ParseTuple(args, "s", &output_file))
    {
        return nullptr;
    }
    try
    {
        getGlobalLogger(self->logger, self->log_function);
        if(*self->output_database == "none")
        {
            string output_f = output_file;
            self->base_object->setOutputDatabase(output_f);
            *self->output_database = output_f;
        }
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }

    Py_RETURN_NONE;

}

static PyObject* set_dispersal_parameters(PySimulateDispersal* self, PyObject* args)
{

    char* dispersal_method;
    char* dispersal_file;
    double sigma, tau, m_prob, cutoff, dispersal_rel_cost;
    int restrict_self;
    // parse arguments
    if(!PyArg_ParseTuple(args, "ssdddddi", &dispersal_method, &dispersal_file, &sigma, &tau, &m_prob, &cutoff,
                         &dispersal_rel_cost, &restrict_self))
    {
        return nullptr;
    }
    try
    {
        getGlobalLogger(self->logger, self->log_function);
        self->dispersalParameters->setDispersalParameters(dispersal_method, sigma, tau, m_prob, cutoff,
                                                          dispersal_rel_cost, static_cast<bool>(restrict_self),
                                                          "closed", dispersal_file, "none");
        self->needs_update = true;
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }

    Py_RETURN_NONE;

}

static PyObject* runMDT(PySimulateDispersal* self, PyObject* args)
{
    try
    {
        int num_repeats, seed, num_workers;
        PyObject * p_num_steps;
        vector<unsigned long> num_steps;
        // parse arguments
        if(!PyArg_ParseTuple(args, "iO!ii", &num_repeats, &PyList_Type, &p_num_steps,
                             &seed, &num_workers))
        {
            return nullptr;
        }
        getGlobalLogger(self->logger, self->log_function);
        if(!importPyListToVectorULong(p_num_steps, num_steps, "Number of steps must be integers."))
        {
            return nullptr;
        }
        self->setDispersalParameters();
        self->base_object->setSeed(static_cast<unsigned long>(seed));
        self->base_object->setNumberRepeats(static_cast<unsigned long>(num_repeats));
        self->base_object->setNumberSteps(num_steps);
        self->base_object->setNumberWorkers(num_workers);
        if(!self->has_imported_maps)
        {
            self->base_object->importMaps();
        }
        self->checkCompleted();
        self->base_object->runMeanDistanceTravelled();
        self->base_object->writeDatabase("DISTANCES_TRAVELLED");
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;

}

static PyObject *runADT(PySimulateDispersal *self, PyObject *args)
{
    try
    {
        int num_repeats, seed, num_workers;
        PyObject * p_num_steps;
        vector<unsigned long> num_steps;
        // parse arguments
        if(!PyArg_ParseTuple(args, "iO!ii", &num_repeats, &PyList_Type, &p_num_steps,
                             &seed, &num_workers))
        {
            return nullptr;
        }
        getGlobalLogger(self->logger, self->log_function);
        if(!importPyListToVectorULong(p_num_steps, num_steps, "Number of steps must be integers."))
        {
            return nullptr;
        }
        self->setDispersalParameters();
        self->base_object->setSeed(static_cast<unsigned long>(seed));
        self->base_object->setNumberRepeats(static_cast<unsigned long>(num_repeats));
        self->base_object->setNumberSteps(num_steps);
        self->base_object->setNumberWorkers(num_workers);
        if(!self->has_imported_maps)
        {
            self->base_object->importMaps();
        }
        self->checkCompleted();
        self->base_object->runAllDistanceTravelled();
        self->base_object->writeDatabase("DISTANCES_TRAVELLED");
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;

}

static PyObject *runSRW(PySimulateDispersal *self, PyObject *args)
{
    try
    {
        int num_repeats, seed, num_workers;
        PyObject * p_num_x_samples;
        PyObject * p_num_y_samples;
        vector<Cell> samples;
        PyObject * p_num_steps;
        vector<unsigned long> num_steps;
        // parse arguments
        if(!PyArg_ParseTuple(args, "O!O!iO!ii", &PyList_Type, &p_num_x_samples, &PyList_Type, &p_num_y_samples, &num_repeats, &PyList_Type, &p_num_steps,
                             &seed, &num_workers))
        {
            return nullptr;
        }
        getGlobalLogger(self->logger, self->log_function);
        if(!importPyListsToVectorCell(p_num_x_samples, p_num_y_samples, samples, "Samples must be two lists of equal length of integers."))
        {
            return nullptr;
        }
        if(!importPyListToVectorULong(p_num_steps, num_steps, "Number of steps must be integers."))
        {
            return nullptr;
        }
        self->setDispersalParameters();
        self->base_object->setSeed(static_cast<unsigned long>(seed));
        self->base_object->setNumberRepeats(static_cast<unsigned long>(num_repeats));
        self->base_object->setNumberSteps(num_steps);
        self->base_object->setNumberWorkers(num_workers);
        if(!self->has_imported_maps)
        {
            self->base_object->importMaps();
        }
        self->checkCompleted();
        self->base_object->runSampleDistanceTravelled(samples);
        self->base_object->writeDatabase("DISTANCES_TRAVELLED");
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;

}

static PyObject* runMeanDispersal(PySimulateDispersal* self, PyObject* args)
{
    try
    {
        int num_repeats, seed, is_sequential;
        // parse arguments
        if(!PyArg_ParseTuple(args, "iii", &num_repeats, &seed, &is_sequential))
        {
            return nullptr;
        }
        getGlobalLogger(self->logger, self->log_function);
        self->setDispersalParameters();
        if(!self->has_imported_maps)
        {
            self->base_object->importMaps();
        }
        self->base_object->setSequential(static_cast<bool>(is_sequential));
        self->base_object->setSeed(static_cast<unsigned long>(seed));
        self->base_object->setNumberRepeats(static_cast<unsigned long>(num_repeats));
        self->checkCompleted();
        self->base_object->runMeanDispersalDistance();
        self->base_object->writeDatabase("DISPERSAL_DISTANCES");
    }
    catch(exception &e)
    {
        removeGlobalLogger();
        PyErr_SetString(necsimError, e.what());
        return nullptr;
    }
    Py_RETURN_NONE;

}

static PyObject* PySimulateDispersal_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
    auto self = (PySimulateDispersal*) PyTemplate_new<SimulateDispersal>(type, args, kwds);
    return (PyObject*) self;
}

static int PySimulateDispersal_init(PySimulateDispersal* self, PyObject* args, PyObject* kwds)
{
    auto out = PyTemplate_init<SimulateDispersal>(self, args, kwds);
    self->dispersalParameters = make_shared<SimParameters>();
    self->has_imported_maps = false;
    self->output_database = make_unique<std::string>("none");
    self->printing = true;
    self->needs_update = true;
    return out;
}

static void PySimulateDispersal_dealloc(PySimulateDispersal* self)
{
    if(self->dispersalParameters != nullptr)
    {
        self->dispersalParameters.reset();
        self->dispersalParameters = nullptr;
    }
    if(self->output_database != nullptr)
    {
        self->output_database.reset();
        self->output_database = nullptr;
    }
    PyTemplate_dealloc<SimulateDispersal>(self);
}

static PyMethodDef SimulateDispersalMethods[] =
        {
                {"set_dispersal_parameters",      (PyCFunction) set_dispersal_parameters,      METH_VARARGS,
                                                              "Sets the dispersal current_metacommunity_parameters for this simulation."},
                {"set_output_database",           (PyCFunction) set_output_database,           METH_VARARGS,
                                                              "Sets the output database for the simulation."},
                {"run_mean_dispersal_distance",   (PyCFunction) runMeanDispersal,              METH_VARARGS,
                                                              "Runs the dispersal simulation for the set current_metacommunity_parameters, calculating the mean distance per step."},
                {"run_mean_distance_travelled",   (PyCFunction) runMDT,                        METH_VARARGS,
                                                              "Runs the dispersal simulation for the set current_metacommunity_parameters, calculating the mean distance travelled."},
                {"run_all_distance_travelled",    (PyCFunction) runADT,                        METH_VARARGS,
                                                              "Runs the dispersal simulation for the set current_metacommunity_parameters on all habitable cells, calculating the mean distance travelled."},
                {"run_sample_distance_travelled", (PyCFunction) runSRW,                        METH_VARARGS,
                                                              "Runs the dispersal simulation for the set current_metacommunity_parameters on the given sample habitable cells, calculating the mean distance travelled."},
                {"import_maps",                   (PyCFunction) set_maps,                      METH_VARARGS,
                                                              "Imports the map files for the simulation. Should only be run once."},
                {"import_all_maps",               (PyCFunction) set_all_map_parameters,        METH_VARARGS,
                                                              "Imports all the map files with a single import."},
                {"set_historical_map_parameters", (PyCFunction) set_historical_map_parameters, METH_VARARGS,
                                                              "Sets the historical map current_metacommunity_parameters."},
                {nullptr,                         nullptr, 0, nullptr}
        };

static PyTypeObject genSimulateDispersalType()
{
    PyTypeObject retSimulateDispersalType = {PyVarObject_HEAD_INIT(nullptr, 0)};
    retSimulateDispersalType.tp_name = (char*) "libnecsim.CDispersalSimulation";
    retSimulateDispersalType.tp_basicsize = sizeof(PySimulateDispersal);
    retSimulateDispersalType.tp_itemsize = 0;
    retSimulateDispersalType.tp_dealloc = (destructor) PySimulateDispersal_dealloc;
    retSimulateDispersalType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC;
    retSimulateDispersalType.tp_doc = (char*) "Simulate a dispersal kernel on a landscape.";
    retSimulateDispersalType.tp_traverse = (traverseproc) PyTemplate_traverse<SimulateDispersal>;
    retSimulateDispersalType.tp_methods = SimulateDispersalMethods;
    //      .tp_members = PyTemplate_members<T>,
    retSimulateDispersalType.tp_getset = PyTemplate_gen_getsetters<SimulateDispersal>();
    retSimulateDispersalType.tp_init = (initproc) PySimulateDispersal_init;
    retSimulateDispersalType.tp_new = PySimulateDispersal_new;
    return retSimulateDispersalType;
}

static PyTypeObject C_SimulateDispersalType = genSimulateDispersalType();
#endif //NECSIM_CSIMULATEDISPERSAL_H