<commit_before>#ifndef VSMC_MPI_BACKEND_MPI_HPP
#define VSMC_MPI_BACKEND_MPI_HPP

#include <vsmc/internal/common.hpp>
#include <vsmc/core/weight.hpp>
#include <boost/mpi.hpp>

namespace vsmc {

/// \brief MPI Communicator
/// \ingroup MPI
///
/// \details
/// Use specialization of the singleton to configure different StateMPI
template <typename ID>
class MPICommunicator
{
    public :

    static MPICommunicator<ID> &instance ()
    {
        static MPICommunicator<ID> comm;

        return comm;
    }

    const MPI_Comm &get () const {return comm_;}

    void set (const MPI_Comm &comm) {comm_ = comm;}

    private :

    MPI_Comm comm_;

    MPICommunicator () : comm_(MPI_COMM_WORLD) {};
    MPICommunicator (const MPICommunicator<ID> &other);
    MPICommunicator<ID> &operator= (const MPICommunicator<ID> &other);
}; // class MPICommunicator

/// \brief Particle::weight_set_type subtype using MPI
/// \ingroup MPI
template <typename BaseState, typename ID>
class WeightSetMPI : public WeightSet<BaseState>
{
    public :

    typedef typename traits::SizeTypeTrait<BaseState>::type size_type;

    explicit WeightSetMPI (size_type N) :
        WeightSet<BaseState>(N), world_(MPICommunicator<ID>::instance().get(),
                boost::mpi::comm_duplicate),
        ess_(static_cast<double>(N) * world_.size()) {}

    size_type resample_size () const {return this->size() * world_.size();}

    template <typename OutputIter>
    OutputIter read_resample_weight (OutputIter first) const
    {
        world_.barrier();

        boost::mpi::all_gather(world_, this->weight_vec(), weight_gather_);
        for (int r = 0; r != world_.size(); ++r)
            for (size_type i = 0; i != this->size(); ++i, ++first)
                *first = weight_gather_[r][i];

        world_.barrier();

        return first;
    }

    template <typename RandomIter>
    RandomIter read_resample_weight (RandomIter first, int stride) const
    {
        world_.barrier();

        boost::mpi::all_gather(world_, this->weight_vec(), weight_gather_);
        for (int r = 0; r != world_.size(); ++r)
            for (size_type i = 0; i != this->size(); ++i, first += stride)
                *first = weight_gather_[r][i];

        world_.barrier();

        return first;
    }

    void set_equal_weight ()
    {
        world_.barrier();

        ess_ = static_cast<double>(this->size()) * world_.size();
        double ew = 1 / ess_;
        std::vector<double> &weight = this->weight_vec();
        std::vector<double> &log_weight = this->log_weight_vec();
        for (size_type i = 0; i != this->size(); ++i) {
            weight[i] = ew;
            log_weight[i] = 0;
        }

        world_.barrier();
    }

    double ess () const {return ess_;}

    private :

    boost::mpi::communicator world_;
    double ess_;
    mutable std::vector<std::vector<double> > weight_gather_;

    void normalize_weight ()
    {
        world_.barrier();

        std::vector<double> &weight = this->weight_vec();

        double lcoeff = 0;
        for (size_type i = 0; i != this->size(); ++i)
            lcoeff += weight[i];
        double gcoeff = 0;
        boost::mpi::all_reduce(world_, lcoeff, gcoeff, std::plus<double>());
        gcoeff = 1 / gcoeff;
        for (size_type i = 0; i != this->size(); ++i)
            weight[i] *= gcoeff;

        double less = 0;
        for (size_type i = 0; i != this->size(); ++i)
            less += weight[i] * weight[i];
        double gess = 0;
        boost::mpi::all_reduce(world_, less, gess, std::plus<double>());
        gess = 1 / gess;
        ess_ = gess;

        world_.barrier();
    }
}; // class WeightSetMPI

/// \brief Particle::value_type subtype using MPI
/// \ingroup MPI
template <typename BaseState, typename ID>
class StateMPI : public BaseState
{
    public :

    typedef typename traits::SizeTypeTrait<BaseState>::type size_type;
    typedef WeightSetMPI<BaseState, ID> weight_set_type;

    explicit StateMPI (size_type N) :
        BaseState(N), world_(MPICommunicator<ID>::instance().get(),
                boost::mpi::comm_duplicate),
        offset_(N * static_cast<size_type>(world_.rank())),
        copy_tag_(boost::mpi::environment::max_tag()) {}

    /// \brief Copy particles
    ///
    /// \details
    /// The tag `boost::mpi::environment::max_tag()` is reserved by vSMC for
    /// copy particles.
    template <typename IntType>
    void copy (size_type N, const IntType *copy_from)
    {
        VSMC_RUNTIME_ASSERT_STATE_COPY_SIZE_MISMATCH_MPI;

        world_.barrier();

        copy_from_.resize(N);
        if (world_.rank() == 0) {
            for (size_type i = 0; i != N; ++i)
                copy_from_[i] = copy_from[i];
        }
        boost::mpi::broadcast(world_, copy_from_, 0);

        copy_recv_.clear();
        copy_send_.clear();
        int rank_this = world_.rank();
        for (size_type to = 0; to != N; ++to) {
            size_type from = copy_from_[to];
            int rank_recv = rank(to);
            int rank_send = rank(from);
            size_type id_recv = local_id(to);
            size_type id_send = local_id(from);
            if (rank_this == rank_recv && rank_this == rank_send) {
                this->copy_particle(id_send, id_recv);
            } else if (rank_this == rank_recv) {
                copy_recv_.push_back(std::make_pair(rank_send, id_recv));
            } else if (rank_this == rank_send) {
                copy_send_.push_back(std::make_pair(rank_recv, id_send));
            }
        }

        for (int r = 0; r != world_.size(); ++r) {
            if (rank_this == r) {
                for (std::size_t i = 0; i != copy_recv_.size(); ++i) {
                    typename BaseState::state_pack_type pack;
                    world_.recv(copy_recv_[i].first, copy_tag_, pack);
                    this->state_unpack(copy_recv_[i].second, pack);
                }
            } else {
                for (std::size_t i = 0; i != copy_send_.size(); ++i) {
                    if (copy_send_[i].first == r) {
                        world_.send(copy_send_[i].first, copy_tag_,
                                this->state_pack(copy_send_[i].second));
                    }
                }
            }
            world_.barrier();
        }

        world_.barrier();
    }

    /// \brief A duplicated MPI communicator for this object
    const boost::mpi::communicator &world () const {return world_;}

    protected :

    size_type offset () const {return offset_;}

    int rank (size_type global_id) const
    {return static_cast<int>(global_id / this->size());}

    bool is_local (size_type global_id) const
    {return global_id >= offset_ && global_id < this->size() + offset_;}

    size_type local_id (size_type global_id) const
    {return global_id - this->size() * rank(global_id);}

    private :

    boost::mpi::communicator world_;
    size_type offset_;
    int copy_tag_;
    std::vector<size_type> copy_from_;
    std::vector<std::pair<int, size_type> > copy_recv_;
    std::vector<std::pair<int, size_type> > copy_send_;
}; // class StateMPI

} // namespace vsmc

#endif // VSMC_MPI_BACKEND_MPI_HPP
<commit_msg>modulize StateMPI::copy<commit_after>#ifndef VSMC_MPI_BACKEND_MPI_HPP
#define VSMC_MPI_BACKEND_MPI_HPP

#include <vsmc/internal/common.hpp>
#include <vsmc/core/weight.hpp>
#include <boost/mpi.hpp>

namespace vsmc {

/// \brief MPI Communicator
/// \ingroup MPI
///
/// \details
/// Use specialization of the singleton to configure different StateMPI
template <typename ID>
class MPICommunicator
{
    public :

    static MPICommunicator<ID> &instance ()
    {
        static MPICommunicator<ID> comm;

        return comm;
    }

    const MPI_Comm &get () const {return comm_;}

    void set (const MPI_Comm &comm) {comm_ = comm;}

    private :

    MPI_Comm comm_;

    MPICommunicator () : comm_(MPI_COMM_WORLD) {};
    MPICommunicator (const MPICommunicator<ID> &other);
    MPICommunicator<ID> &operator= (const MPICommunicator<ID> &other);
}; // class MPICommunicator

/// \brief Particle::weight_set_type subtype using MPI
/// \ingroup MPI
template <typename BaseState, typename ID>
class WeightSetMPI : public WeightSet<BaseState>
{
    public :

    typedef typename traits::SizeTypeTrait<BaseState>::type size_type;

    explicit WeightSetMPI (size_type N) :
        WeightSet<BaseState>(N), world_(MPICommunicator<ID>::instance().get(),
                boost::mpi::comm_duplicate),
        ess_(static_cast<double>(N) * world_.size()) {}

    size_type resample_size () const {return this->size() * world_.size();}

    template <typename OutputIter>
    OutputIter read_resample_weight (OutputIter first) const
    {
        world_.barrier();

        boost::mpi::all_gather(world_, this->weight_vec(), weight_gather_);
        for (int r = 0; r != world_.size(); ++r)
            for (size_type i = 0; i != this->size(); ++i, ++first)
                *first = weight_gather_[r][i];

        world_.barrier();

        return first;
    }

    template <typename RandomIter>
    RandomIter read_resample_weight (RandomIter first, int stride) const
    {
        world_.barrier();

        boost::mpi::all_gather(world_, this->weight_vec(), weight_gather_);
        for (int r = 0; r != world_.size(); ++r)
            for (size_type i = 0; i != this->size(); ++i, first += stride)
                *first = weight_gather_[r][i];

        world_.barrier();

        return first;
    }

    void set_equal_weight ()
    {
        world_.barrier();

        ess_ = static_cast<double>(this->size()) * world_.size();
        double ew = 1 / ess_;
        std::vector<double> &weight = this->weight_vec();
        std::vector<double> &log_weight = this->log_weight_vec();
        for (size_type i = 0; i != this->size(); ++i) {
            weight[i] = ew;
            log_weight[i] = 0;
        }

        world_.barrier();
    }

    double ess () const {return ess_;}

    private :

    boost::mpi::communicator world_;
    double ess_;
    mutable std::vector<std::vector<double> > weight_gather_;

    void normalize_weight ()
    {
        world_.barrier();

        std::vector<double> &weight = this->weight_vec();

        double lcoeff = 0;
        for (size_type i = 0; i != this->size(); ++i)
            lcoeff += weight[i];
        double gcoeff = 0;
        boost::mpi::all_reduce(world_, lcoeff, gcoeff, std::plus<double>());
        gcoeff = 1 / gcoeff;
        for (size_type i = 0; i != this->size(); ++i)
            weight[i] *= gcoeff;

        double less = 0;
        for (size_type i = 0; i != this->size(); ++i)
            less += weight[i] * weight[i];
        double gess = 0;
        boost::mpi::all_reduce(world_, less, gess, std::plus<double>());
        gess = 1 / gess;
        ess_ = gess;

        world_.barrier();
    }
}; // class WeightSetMPI

/// \brief Particle::value_type subtype using MPI
/// \ingroup MPI
template <typename BaseState, typename ID>
class StateMPI : public BaseState
{
    public :

    typedef typename traits::SizeTypeTrait<BaseState>::type size_type;
    typedef WeightSetMPI<BaseState, ID> weight_set_type;

    explicit StateMPI (size_type N) :
        BaseState(N), world_(MPICommunicator<ID>::instance().get(),
                boost::mpi::comm_duplicate),
        offset_(N * static_cast<size_type>(world_.rank())),
        copy_tag_(boost::mpi::environment::max_tag()) {}

    /// \brief Copy particles
    ///
    /// \param N The number of particles on all nodes
    /// \param copy_from A vector of length `N`, for each particle with global
    /// id `to`, `copy_from[to]` is the global id of the particle it shall
    /// copy.
    ///
    /// \details
    /// The `BaseState` type is required to have the following members
    /// - `state_pack_type`: A type that used to pack state values. It shall be
    /// serializable. That is, a `state_pack_type` object is acceptable by
    /// `boost::mpi::communicator::send` etc. Both
    /// `StateMatrix::state_pack_type` and `StateTuple::state_pack_type`
    /// satisfy this requirement if their template type parameter types are
    /// serializable. For user defined types, see document of Boost.Serialize
    /// of how to serialize a class object.
    /// - `state_pack`
    /// \code
    /// state_pack_type state_pack (size_type id) const;
    /// \endcode
    /// Given a local particle id on this node, pack the state values into a
    /// `state_pack_type` object.
    /// - `state_unpack`
    /// \code
    /// void state_unpack (size_type id, const state_pack_type &pack);
    /// \endcode
    /// Given a local particle id and a `state_pack_type` object, unpack it
    /// into the given position on this node.
    ///
    /// In vSMC, the resampling algorithms generate the number of replications
    /// of each particle. Particles with replication zero need to copy other
    /// particles. The vector of the number of replications is transfered to
    /// `copy_from` by `Particle::resample`, and it is generated in such a way
    /// that each particle will copy from somewhere close to itself. Therefore,
    /// transferring between nodes is minimized.
    ///
    /// This default implementation perform three stages of copy.
    /// - Stage one: Generate a local duplicate of `copy_from` on node `0` and
    /// broadcast it to all nodes.
    /// - Stage two: Perform local copy, copy those particles where the
    /// destination and source are both on this node. This is performed in
    /// parallel on each node.
    /// - Stage three: copy particles that need message passing between nodes.
    ///
    /// A derived class can override this `copy` method. For the following
    /// possible reasons,
    /// - Stage one is not needed or too expansive
    /// - Stage three is too expansive. The default implementation assumes
    /// `this->state_pack(id)` is not too expansive, and inter-node copy is
    /// rare anyway. If this is not the case, then it can be a performance
    /// bottle neck.
    template <typename IntType>
    void copy (size_type N, const IntType *copy_from)
    {
        VSMC_RUNTIME_ASSERT_STATE_COPY_SIZE_MISMATCH_MPI;

        world_.barrier();

        copy_from_.resize(N);
        if (world_.rank() == 0) {
            for (size_type i = 0; i != N; ++i)
                copy_from_[i] = copy_from[i];
        }
        boost::mpi::broadcast(world_, copy_from_, 0);

        copy_this_node(N, &copy_from_[0], copy_recv_, copy_send_);
        world_.barrier();

        copy_inter_node(copy_recv_, copy_send_);
        world_.barrier();
    }

    /// \brief A duplicated MPI communicator for this object
    const boost::mpi::communicator &world () const {return world_;}

    /// \brief The number of particles on all nodes
    size_type global_size () const
    {return this->size() * static_cast<size_type>(world_.size());}

    /// \brief The number of particles on nodes with ranks less than the rank
    /// of this node.
    size_type offset () const {return offset_;}

    /// \brief Given a global particle id return the rank of the node it
    /// belongs
    int rank (size_type global_id) const
    {return static_cast<int>(global_id / this->size());}

    /// \brief Given a global particle id check if it is on this `node`
    bool is_local (size_type global_id) const
    {return global_id >= offset_ && global_id < this->size() + offset_;}

    /// \brief Transfer a global particle id into a local particle id
    size_type local_id (size_type global_id) const
    {return global_id - this->size() * rank(global_id);}

    protected :

    /// \brief The MPI recv/send tag used by `copy_inter_node`
    int copy_tag () const {return copy_tag_;}

    /// \brief Perform local copy
    ///
    /// \param N The number of particles on all nodes
    /// \param copy_from_first The first iterator of the copy_from vector
    /// \param copy_recv All particles that shall be received at this node
    /// \param copy_send All particles that shall be send from this node
    ///
    /// \details
    /// `copy_from_first` can be a one-pass input iterator used to access a
    /// vector of size `N`, say `copy_from`.
    /// For each `to` in the range `0` to `N - 1`
    /// - If both `to` and `from = copy_from[to]` are particles on this node,
    /// invoke `copy_particle` to copy the parties. Otherwise,
    /// - If `to` is a particle on this node, insert a pair into `copy_recv`,
    /// whose values are the rank of the node from which this node shall
    /// receive the particle and the particle id *on this node* where the
    /// particle received shall be unpacked. Otherwise,
    /// - If `from = copy_from[to]` is a particle on this node, insert a pair
    /// into `copy_send`, whose values are the rank of the node to which this
    /// node shall send the particle  and the particle id *on this node* where
    /// the particle sent shall be packed. Otherwise do nothing.
    ///
    /// \note
    /// It is important the the vector access through `copy_from_first` is the
    /// same for all nodes. Otherwise the behavior is undefined.
    template <typename InputIter>
    void copy_this_node (size_type N, InputIter copy_from_first,
            std::vector<std::pair<int, size_type> > &copy_recv,
            std::vector<std::pair<int, size_type> > &copy_send)
    {
        copy_recv.clear();
        copy_send.clear();
        int rank_this = world_.rank();
        for (size_type to = 0; to != N; ++to, ++copy_from_first) {
            size_type from = *copy_from_first;
            int rank_recv = rank(to);
            int rank_send = rank(from);
            size_type id_recv = local_id(to);
            size_type id_send = local_id(from);
            if (rank_this == rank_recv && rank_this == rank_send) {
                this->copy_particle(id_send, id_recv);
            } else if (rank_this == rank_recv) {
                copy_recv.push_back(std::make_pair(rank_send, id_recv));
            } else if (rank_this == rank_send) {
                copy_send.push_back(std::make_pair(rank_recv, id_send));
            }
        }
    }

    /// \brief Perform global copy
    ///
    /// \param copy_recv The output vector `copy_recv` from `copy_this_node`
    /// \param copy_send The output vector `copy_send` from `copy_this_node`
    void copy_inter_node (
            const std::vector<std::pair<int, size_type> > &copy_recv,
            const std::vector<std::pair<int, size_type> > &copy_send)
    {
        int rank_this = world_.rank();
        for (int r = 0; r != world_.size(); ++r) {
            if (rank_this == r) {
                for (std::size_t i = 0; i != copy_recv.size(); ++i) {
                    typename BaseState::state_pack_type pack;
                    world_.recv(copy_recv_[i].first, copy_tag_, pack);
                    this->state_unpack(copy_recv[i].second, pack);
                }
            } else {
                for (std::size_t i = 0; i != copy_send.size(); ++i) {
                    if (copy_send_[i].first == r) {
                        world_.send(copy_send_[i].first, copy_tag_,
                                this->state_pack(copy_send[i].second));
                    }
                }
            }
        }
    }

    private :

    boost::mpi::communicator world_;
    size_type offset_;
    int copy_tag_;
    std::vector<size_type> copy_from_;
    std::vector<std::pair<int, size_type> > copy_recv_;
    std::vector<std::pair<int, size_type> > copy_send_;
}; // class StateMPI

} // namespace vsmc

#endif // VSMC_MPI_BACKEND_MPI_HPP
<|endoftext|>


<commit_before>// 01.01.2017 Султан Xottab_DUTY Урамаев
// Чем стрелы коленом ловить, гораздо интереснее отстреливать свои ноги. Продолжим.
#include <iostream>
#include <thread>

#include <GLFW/glfw3.h>
#include <dynlib/Dynlib.hpp>

#include "xdCore.hpp"
#include "Console.hpp"
#include "ConsoleCommand.hpp"
#include "xdEngine.hpp"
#include "Debug/QuantMS.hpp"

void InitializeConsole()
{
    ConsoleCommands = new CC_Container;
    Console = new xdConsole;
    Console->Initialize();
}

void destroyConsole()
{
    ConsoleCommands->Execute("config_save");
    ConsoleCommands->Destroy();
    delete ConsoleCommands;
    Console->CloseLog();
    delete Console;
}

void HelpCmdArgs()
{
    Console->Log("\nUse parameters with values only with quotes(-param \"value\")\n"\
        "-param value will return \"alue\"\n"\
        "-param \"value\" will return \"value\"\n\n"\
        "Available parameters:\n"\
        "-name - Specifies AppName, default is \"X-Day Engine\" \n"\
        "-game - Specifies game library to be attached, default is \"xdGame\";\n"
        "-datapath - Specifies path of application data folder, default is \"*WorkingDirectory*/appdata\"\n"\
        "-respath - Specifies path of resources folder, default is \"*WorkingDirectory*/res\"\n"\
        "-mainconfig - Specifies path and name of main config file (path/name.extension), default is \"*DataPath*/main.config\" \n"\
        "-mainlog - Specifies path and name of main log file (path/name.extension), default is \"*DataPath*/main.log\"\n"\
        "-nolog - Completely disables engine log. May increase performance\n"\
        "-nologflush - Disables log flushing. Useless if -nolog defined\n");

    Console->Log("\nИспользуйте параметры только с кавычками(-параметр \"значение\")\n"\
        "-параметр значение вернёт \"начени\"\n"\
        "-параметр \"значение\" вернёт \"значение\"\n"\
        "\nДоступные параметры:\n"\
        "-name - Задаёт AppName, по умолчанию: \"X-Day Engine\" \n"\
        "-game - Задаёт игровую библиотеку для подключения, по умолчанию: \"xdGame\";\n"
        "-datapath - Задаёт путь до папки с настройками, по умолчанию: \"*WorkingDirectory*/appdata\"\n"\
        "-respath - Задаёт путь до папки с ресурсами, по умолчанию: \"*WorkingDirectory*/res\"\n"\
        "-mainconfig - Задаёт путь и имя главного файла настроек (путь/имя.расширение), по умолчанию: \"*DataPath*/main.config\" \n"\
        "-mainlog - Задаёт путь и имя главного лог файла (путь/имя.расширение), по умолчанию: \"*DataPath*/main.log\"\n"\
        "-nolog - Полностью выключает лог движка. Может повысить производительность\n"\
        "-nologflush - Выключает сброс лога в файл. Не имеет смысла если задан -nolog\n", false);
}

void threadedConsole()
{
    while (!glfwWindowShouldClose(Engine.window))
    {
        std::string input;
        std::getline(std::cin, input);
        ConsoleCommands->Execute(input);
    }

}

void Startup()
{
    while (!glfwWindowShouldClose(Engine.window))
    {
        if (ConsoleCommands->GetBool("r_fullscreen"))
            glfwSetWindowMonitor(Engine.window, Engine.CurrentMonitor, 0, 0, Engine.CurrentMode->width, Engine.CurrentMode->height, Engine.CurrentMode->refreshRate);
        else
            glfwSetWindowMonitor(Engine.window, nullptr, 0, 0, Engine.CurrentMode->width-256, Engine.CurrentMode->height-256, Engine.CurrentMode->refreshRate);

        glfwPollEvents();
    }
}

int main(int argc, char* argv[])
{
    QuantMS();
#ifdef WINDOWS
    system("chcp 65001");
#endif
    Core.Initialize("X-Day Engine", **argv);
    InitializeConsole();

    Console->Log(Core.GetGLFWVersionString());
    Console->Log(Core.GetBuildString());
    Console->Log("Core.Params: " + Core.Params);
    Console->Log("Девиз: Чем стрелы коленом ловить, гораздо интереснее отстреливать свои ноги. Продолжим.", false);
    Console->Log("Slogan: It's more interesting to shoot your feet, than catch arrows by your knee. Let's continue.");
    HelpCmdArgs();

    ConsoleCommands->ExecuteConfig(Console->ConfigFile.string());

    glfwInit();
    Console->Log("GLFW initialized.");

    Engine.Initialize();
    Engine.xdCreateWindow();

    auto xdSoundModule = Dynlib::open(Core.GetModuleName("xdSound").c_str());
    if (xdSoundModule)
    {
        Console->Log("Module loaded successfully");
    }

    auto impFunc = (FunctionPointer)Dynlib::load(xdSoundModule, "funcToExport");
    if (impFunc)
        impFunc();
    else
        Console->Log("Failed to import function");

    if (Dynlib::close(xdSoundModule))
    {
        Console->Log("Module unloaded successfully");
    }

    std::thread WatchConsole(threadedConsole);
    WatchConsole.detach();
    Startup();

    glfwTerminate();
    Console->Log("GLFW terminated.");

    auto TotalTimer = QuantMS();
    ConsoleMsg("Total time: {} seconds", TotalTimer/1000000);
    destroyConsole();

    system("pause");
    return 0;
}
<commit_msg>"Fix" GLFW initialization "check"<commit_after>// 01.01.2017 Султан Xottab_DUTY Урамаев
// Чем стрелы коленом ловить, гораздо интереснее отстреливать свои ноги. Продолжим.
#include <iostream>
#include <thread>

#include <GLFW/glfw3.h>
#include <dynlib/Dynlib.hpp>

#include "xdCore.hpp"
#include "Console.hpp"
#include "ConsoleCommand.hpp"
#include "xdEngine.hpp"
#include "Debug/QuantMS.hpp"

void InitializeConsole()
{
    ConsoleCommands = new CC_Container;
    Console = new xdConsole;
    Console->Initialize();
}

void destroyConsole()
{
    ConsoleCommands->Execute("config_save");
    ConsoleCommands->Destroy();
    delete ConsoleCommands;
    Console->CloseLog();
    delete Console;
}

void HelpCmdArgs()
{
    Console->Log("\nUse parameters with values only with quotes(-param \"value\")\n"\
        "-param value will return \"alue\"\n"\
        "-param \"value\" will return \"value\"\n\n"\
        "Available parameters:\n"\
        "-name - Specifies AppName, default is \"X-Day Engine\" \n"\
        "-game - Specifies game library to be attached, default is \"xdGame\";\n"
        "-datapath - Specifies path of application data folder, default is \"*WorkingDirectory*/appdata\"\n"\
        "-respath - Specifies path of resources folder, default is \"*WorkingDirectory*/res\"\n"\
        "-mainconfig - Specifies path and name of main config file (path/name.extension), default is \"*DataPath*/main.config\" \n"\
        "-mainlog - Specifies path and name of main log file (path/name.extension), default is \"*DataPath*/main.log\"\n"\
        "-nolog - Completely disables engine log. May increase performance\n"\
        "-nologflush - Disables log flushing. Useless if -nolog defined\n");

    Console->Log("\nИспользуйте параметры только с кавычками(-параметр \"значение\")\n"\
        "-параметр значение вернёт \"начени\"\n"\
        "-параметр \"значение\" вернёт \"значение\"\n"\
        "\nДоступные параметры:\n"\
        "-name - Задаёт AppName, по умолчанию: \"X-Day Engine\" \n"\
        "-game - Задаёт игровую библиотеку для подключения, по умолчанию: \"xdGame\";\n"
        "-datapath - Задаёт путь до папки с настройками, по умолчанию: \"*WorkingDirectory*/appdata\"\n"\
        "-respath - Задаёт путь до папки с ресурсами, по умолчанию: \"*WorkingDirectory*/res\"\n"\
        "-mainconfig - Задаёт путь и имя главного файла настроек (путь/имя.расширение), по умолчанию: \"*DataPath*/main.config\" \n"\
        "-mainlog - Задаёт путь и имя главного лог файла (путь/имя.расширение), по умолчанию: \"*DataPath*/main.log\"\n"\
        "-nolog - Полностью выключает лог движка. Может повысить производительность\n"\
        "-nologflush - Выключает сброс лога в файл. Не имеет смысла если задан -nolog\n", false);
}

void threadedConsole()
{
    while (!glfwWindowShouldClose(Engine.window))
    {
        std::string input;
        std::getline(std::cin, input);
        ConsoleCommands->Execute(input);
    }

}

void Startup()
{
    while (!glfwWindowShouldClose(Engine.window))
    {
        if (ConsoleCommands->GetBool("r_fullscreen"))
            glfwSetWindowMonitor(Engine.window, Engine.CurrentMonitor, 0, 0, Engine.CurrentMode->width, Engine.CurrentMode->height, Engine.CurrentMode->refreshRate);
        else
            glfwSetWindowMonitor(Engine.window, nullptr, 0, 0, Engine.CurrentMode->width-256, Engine.CurrentMode->height-256, Engine.CurrentMode->refreshRate);

        glfwPollEvents();
    }
}

int main(int argc, char* argv[])
{
    QuantMS();
#ifdef WINDOWS
    system("chcp 65001");
#endif
    Core.Initialize("X-Day Engine", **argv);
    InitializeConsole();

    Console->Log(Core.GetGLFWVersionString());
    Console->Log(Core.GetBuildString());
    Console->Log("Core.Params: " + Core.Params);
    Console->Log("Девиз: Чем стрелы коленом ловить, гораздо интереснее отстреливать свои ноги. Продолжим.", false);
    Console->Log("Slogan: It's more interesting to shoot your feet, than catch arrows by your knee. Let's continue.");
    HelpCmdArgs();

    ConsoleCommands->ExecuteConfig(Console->ConfigFile.string());

    if (glfwInit())
        Console->Log("GLFW initialized.");
    else
        Console->Log("GLFW not initialized.");

    Engine.Initialize();
    Engine.xdCreateWindow();

    auto xdSoundModule = Dynlib::open(Core.GetModuleName("xdSound").c_str());
    if (xdSoundModule)
    {
        Console->Log("Module loaded successfully");
    }

    auto impFunc = (FunctionPointer)Dynlib::load(xdSoundModule, "funcToExport");
    if (impFunc)
        impFunc();
    else
        Console->Log("Failed to import function");

    if (Dynlib::close(xdSoundModule))
    {
        Console->Log("Module unloaded successfully");
    }

    std::thread WatchConsole(threadedConsole);
    WatchConsole.detach();
    Startup();

    glfwTerminate();
    Console->Log("GLFW terminated.");

    auto TotalTimer = QuantMS();
    ConsoleMsg("Total time: {} seconds", TotalTimer/1000000);
    destroyConsole();

    system("pause");
    return 0;
}
<|endoftext|>


<commit_before>/*=============================================================================
	Copyright (c) 2012-2014 Richard Otis

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

// N-Dimensional Simplex Point Generation

#ifndef INCLUDED_NDSIMPLEX
#define INCLUDED_NDSIMPLEX

#include "libgibbs/include/optimizer/halton.hpp"
#include "libgibbs/include/utils/primes.hpp"
#include <boost/assert.hpp>
#include <boost/math/special_functions/factorials.hpp>
#include <vector>
#include <algorithm>

struct NDSimplex {
	// Reference: Chasalow and Brand, 1995, "Algorithm AS 299: Generation of Simplex Lattice Points"
	template <typename Func> static inline void lattice (
			const std::size_t point_dimension,
			const std::size_t grid_points_per_major_axis,
			const Func &func
	) {
		BOOST_ASSERT(grid_points_per_major_axis >= 2);
		BOOST_ASSERT(point_dimension >= 1);
		typedef std::vector<double> PointType;
		const double lattice_spacing = 1.0 / (double)grid_points_per_major_axis;
		const PointType::value_type lower_limit = 0;
		const PointType::value_type upper_limit = 1;
		PointType point; // Contains current point
		PointType::iterator coord_find; // corresponds to 'j' in Chasalow and Brand

		// Special case: 1 component; only valid point is {1}
		if (point_dimension == 1) {
			point.push_back(1);
			func(point);
			return;
		}

		// Initialize algorithm
		point.resize(point_dimension, lower_limit); // Fill with smallest value (0)
		const PointType::iterator last_coord = --point.end();
		coord_find = point.begin();
		*coord_find = upper_limit;
		// point should now be {1,0,0,0....}

		do {
			func(point);
			*coord_find -= lattice_spacing;
			if (*coord_find < lattice_spacing/2) *coord_find = lower_limit; // workaround for floating point issues
			if (std::distance(coord_find,point.end()) > 2) {
				++coord_find;
				*coord_find = lattice_spacing + *last_coord;
				*last_coord = lower_limit;
			}
			else {
				*last_coord += lattice_spacing;
				while (*coord_find == lower_limit) --coord_find;
			}
		}
		while (*last_coord < upper_limit);

		func(point); // should be {0,0,...1}
	}

	static inline std::vector<std::vector<double>> lattice_complex(
			const std::vector<std::size_t> &components_in_sublattices,
			const std::size_t grid_points_per_major_axis
			) {
		using boost::math::factorial;
		typedef std::vector<double> PointType;
		typedef std::vector<PointType> PointCollection;
		std::vector<PointCollection> point_lattices; //  Simplex lattices for each sublattice
		std::vector<PointType> points; // The final return points (combination of all simplex lattices)
		std::size_t expected_points = 1;
		std::size_t point_dimension = 0;

		// TODO: Is there a way to do this without all the copying?
		for (auto i = components_in_sublattices.cbegin(); i != components_in_sublattices.cend(); ++i) {
			PointCollection simplex_points; // all points for this simplex
			const unsigned int q = *i; // number of components
			point_dimension += q;
			const unsigned int m = grid_points_per_major_axis - 2; // number of evenly spaced values _between_ 0 and 1
			auto point_add = [&simplex_points] (PointType &address) {
				simplex_points.push_back(address);
				std::cout << "point_add: [";
				for (auto u = address.begin(); u != address.end(); ++u) std::cout << *u << ",";
				std::cout << "]" << std::endl;
			};

			lattice(q, grid_points_per_major_axis, point_add);
			expected_points *= simplex_points.size();
			point_lattices.push_back(simplex_points); // push points for each simplex
		}
		std::cout << "expected_points: " << expected_points << std::endl;

		points.reserve(expected_points);

		for (auto p = 0; p < expected_points; ++p) {
			PointType point;
			std::size_t dividend = p;
			point.reserve(point_dimension);
			std::cout << "p : " << p << " indices: [";
			for (auto r = point_lattices.rbegin(); r != point_lattices.rend(); ++r) {
				std::cout << dividend % r->size() << ",";
				point.insert(point.end(),(*r)[dividend % r->size()].begin(),(*r)[dividend % r->size()].end());
				dividend = dividend / r->size();
			}
			std::cout << "]" << std::endl;
			std::reverse(point.begin(),point.end());
			points.push_back(point);
		}

		return points;
	}

	// Reference for Halton sequence: Hess and Polak, 2003.
	// Reference for uniformly sampling the simplex: Any text on the Dirichlet distribution
	template <typename Func> static inline void quasirandom_sample (
			const std::size_t point_dimension,
			const std::size_t number_of_points,
			const Func &func
	) {
		BOOST_ASSERT(point_dimension < primes_size()); // No realistic problem should ever violate this
		// TODO: Add the shuffling part to the Halton sequence. This will help with correlation problems for large N
		// TODO: Default-add the end-members (vertices) of the N-simplex
		for (auto sequence_pos = 1; sequence_pos <= number_of_points; ++sequence_pos) {
			std::vector<double> point;
			double point_sum = 0;
			for (auto i = 0; i < point_dimension; ++i) {
				// Draw the coordinate from an exponential distribution
				// N samples from the exponential distribution, when normalized to 1, will be distributed uniformly
				// on a facet of the N-simplex.
				// If X is uniformly distributed, then -LN(X) is exponentially distributed.
				// Since the Halton sequence is a low-discrepancy sequence over [0,1], we substitute it for the uniform distribution
				// This makes this algorithm deterministic and may also provide some domain coverage advantages over a
				// psuedo-random sample.
				double value = -log(halton(sequence_pos,primes[i]));
				point_sum += value;
				point.push_back(value);
			}
			for (auto i = point.begin(); i != point.end(); ++i) *i /= point_sum; // Normalize point to sum to 1
			func(point);
			if (point_dimension == 1) break; // no need to generate additional points; only one feasible point exists for 0-simplex
		}
	}
};

#endif
<commit_msg>Remove some debug code<commit_after>/*=============================================================================
	Copyright (c) 2012-2014 Richard Otis

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

// N-Dimensional Simplex Point Generation

#ifndef INCLUDED_NDSIMPLEX
#define INCLUDED_NDSIMPLEX

#include "libgibbs/include/optimizer/halton.hpp"
#include "libgibbs/include/utils/primes.hpp"
#include <boost/assert.hpp>
#include <boost/math/special_functions/factorials.hpp>
#include <vector>
#include <algorithm>

struct NDSimplex {
	// Reference: Chasalow and Brand, 1995, "Algorithm AS 299: Generation of Simplex Lattice Points"
	template <typename Func> static inline void lattice (
			const std::size_t point_dimension,
			const std::size_t grid_points_per_major_axis,
			const Func &func
	) {
		BOOST_ASSERT(grid_points_per_major_axis >= 2);
		BOOST_ASSERT(point_dimension >= 1);
		typedef std::vector<double> PointType;
		const double lattice_spacing = 1.0 / (double)grid_points_per_major_axis;
		const PointType::value_type lower_limit = 0;
		const PointType::value_type upper_limit = 1;
		PointType point; // Contains current point
		PointType::iterator coord_find; // corresponds to 'j' in Chasalow and Brand

		// Special case: 1 component; only valid point is {1}
		if (point_dimension == 1) {
			point.push_back(1);
			func(point);
			return;
		}

		// Initialize algorithm
		point.resize(point_dimension, lower_limit); // Fill with smallest value (0)
		const PointType::iterator last_coord = --point.end();
		coord_find = point.begin();
		*coord_find = upper_limit;
		// point should now be {1,0,0,0....}

		do {
			func(point);
			*coord_find -= lattice_spacing;
			if (*coord_find < lattice_spacing/2) *coord_find = lower_limit; // workaround for floating point issues
			if (std::distance(coord_find,point.end()) > 2) {
				++coord_find;
				*coord_find = lattice_spacing + *last_coord;
				*last_coord = lower_limit;
			}
			else {
				*last_coord += lattice_spacing;
				while (*coord_find == lower_limit) --coord_find;
			}
		}
		while (*last_coord < upper_limit);

		func(point); // should be {0,0,...1}
	}

	static inline std::vector<std::vector<double>> lattice_complex(
			const std::vector<std::size_t> &components_in_sublattices,
			const std::size_t grid_points_per_major_axis
			) {
		using boost::math::factorial;
		typedef std::vector<double> PointType;
		typedef std::vector<PointType> PointCollection;
		std::vector<PointCollection> point_lattices; //  Simplex lattices for each sublattice
		std::vector<PointType> points; // The final return points (combination of all simplex lattices)
		std::size_t expected_points = 1;
		std::size_t point_dimension = 0;

		// TODO: Is there a way to do this without all the copying?
		for (auto i = components_in_sublattices.cbegin(); i != components_in_sublattices.cend(); ++i) {
			PointCollection simplex_points; // all points for this simplex
			const unsigned int q = *i; // number of components
			point_dimension += q;
			const unsigned int m = grid_points_per_major_axis - 2; // number of evenly spaced values _between_ 0 and 1
			auto point_add = [&simplex_points] (PointType &address) {
				simplex_points.push_back(address);
			};

			lattice(q, grid_points_per_major_axis, point_add);
			expected_points *= simplex_points.size();
			point_lattices.push_back(simplex_points); // push points for each simplex
		}

		points.reserve(expected_points);

		for (auto p = 0; p < expected_points; ++p) {
			PointType point;
			std::size_t dividend = p;
			point.reserve(point_dimension);
			std::cout << "p : " << p << " indices: [";
			for (auto r = point_lattices.rbegin(); r != point_lattices.rend(); ++r) {
				std::cout << dividend % r->size() << ",";
				point.insert(point.end(),(*r)[dividend % r->size()].begin(),(*r)[dividend % r->size()].end());
				dividend = dividend / r->size();
			}
			std::cout << "]" << std::endl;
			std::reverse(point.begin(),point.end());
			points.push_back(point);
		}

		return points;
	}

	// Reference for Halton sequence: Hess and Polak, 2003.
	// Reference for uniformly sampling the simplex: Any text on the Dirichlet distribution
	template <typename Func> static inline void quasirandom_sample (
			const std::size_t point_dimension,
			const std::size_t number_of_points,
			const Func &func
	) {
		BOOST_ASSERT(point_dimension < primes_size()); // No realistic problem should ever violate this
		// TODO: Add the shuffling part to the Halton sequence. This will help with correlation problems for large N
		// TODO: Default-add the end-members (vertices) of the N-simplex
		for (auto sequence_pos = 1; sequence_pos <= number_of_points; ++sequence_pos) {
			std::vector<double> point;
			double point_sum = 0;
			for (auto i = 0; i < point_dimension; ++i) {
				// Draw the coordinate from an exponential distribution
				// N samples from the exponential distribution, when normalized to 1, will be distributed uniformly
				// on a facet of the N-simplex.
				// If X is uniformly distributed, then -LN(X) is exponentially distributed.
				// Since the Halton sequence is a low-discrepancy sequence over [0,1], we substitute it for the uniform distribution
				// This makes this algorithm deterministic and may also provide some domain coverage advantages over a
				// psuedo-random sample.
				double value = -log(halton(sequence_pos,primes[i]));
				point_sum += value;
				point.push_back(value);
			}
			for (auto i = point.begin(); i != point.end(); ++i) *i /= point_sum; // Normalize point to sum to 1
			func(point);
			if (point_dimension == 1) break; // no need to generate additional points; only one feasible point exists for 0-simplex
		}
	}
};

#endif
<|endoftext|>


<commit_before>/* GRANULATE - granulation of sound stored in a table

   Any parameter marked with '*' can receive updates from a real-time
   control source.

      p0  = output start time
    * p1  = input start time (will be constrained to window, p5-6)
            NOTE: Unlike other instruments, this applies to a table, not to
            an input sound file.
      p2  = total duration
    * p3  = amplitude multiplier

      p4  = input sound table (e.g., maketable("sndfile", ...))
      p5  = number of channels in sample table
    * p6  = input channel of sample table

    * p7  = input window start time
    * p8  = input window end time
    * p9  = wraparound (see below)

      The "input window" refers to the portion of the input sound table read
      by the granulator.  When the granulator reaches the end of the window,
      it wraps around to the beginning, unless p9 (wraparound) is false (0).
      In that case, please note that any data coming from time-varying tables
      (such as p3), may not span the entire table.

      It's best to arrange for the window start to be a little after the sound
      table start, and for the window end to be a little before the table end.

    * p10 = traversal rate

      The granulator moves through the input window at the "traversal rate."
      Here are some sample values:

            0     no movement
            1     move forward at normal rate
            2.5   move forward at a rate that is 2.5 times normal
            -1    move backward at normal rate

      The following parameters determine the character of individual grains.

      p11 = grain envelope table

    * p12 = grain hop time (time between successive grains).  This is the
            inverse of grain density (grains per second); you can use
            makeconverter(..., "inverse") to convert a table or real-time
            control source from density to hop time.

    * p13 = grain input time jitter
            Maximum randomly determined amount to add or subtract from the
            input start time for a grain.

    * p14 = grain output time jitter
            Maximum randomly determined amount to add or subtract from the
            output start time for a grain, which is controlled by p12 (grain
            hop time).

    * p15 = grain duration minimum
    * p16 = grain duration maximum

    * p17 = grain amplitude multiplier minimum
    * p18 = grain amplitude multiplier maximum

    * p19 = grain transposition (in linear octaves, relative to 0)
            [optional; if missing, no transposition]

      p20 = grain transposition collection
            If this is a table, it contains a list of transpositions (in oct.pc)
            from which to select randomly.  If it's not a table, it's ignored.
            The table cannot be updated dynamically.  The value of p19
            (transposition) affects the collection.
            [optional]

    * p21 = grain transposition jitter
            Maximum randomly determined amount to add or subtract from the
            current transposition value.  If p20 (transposition collection)
            is active, then jitter controls how much of the collection to
            choose from.  In this case, jitter is an oct.pc value.  For example,
            if the collection is [0.00, 0.02, 0.05, 0.07], then a jitter value
            of 0.05 will cause only the first 3 pitches to be chosen, whereas a
            jitter value of 0.07 would cause all 4 to be chosen.
            [optional; if missing, no transposition jitter]

      p22 = random seed (integer)
            [optional; if missing, uses system clock]

    * p23 = grain pan minimum (pctleft: 0-1)
    * p24 = grain pan maximum
            [optional, ignored if mono output; if both missing, min = 0 and
            min = 1; if max missing, max = min]

      p25 = use 3rd-order interpolation, instead of 2nd-order, when transposing.
            This is more CPU-intensive, and often doesn't make a noticeable
            difference.  (0: use 2nd-order, 1: use 3rd-order)
            [optional; 2nd-order interp is the default]

   John Gibson <johgibso at indiana dot edu>, 1/29/05
*/
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <assert.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <rt.h>
#include <rtdefs.h>
#include "GRANULATE.h"
#include "grainstream.h"

//#define DEBUG
//#define NDEBUG     // disable asserts

#define PRESERVE_GRAIN_DURATION  true     // regardless of transposition

#define USAGE_MESSAGE \
"Usage:\n" \
"  GRANULATE(start, inskip, dur, amp, sound_table, num_chans, input_chan,\n" \
"    window_start_time, window_end_time, wraparound, traversal_rate,\n" \
"    grain_env, grain_hoptime, grain_input_jitter, grain_output_jitter,\n" \
"    grain_dur_min, grain_dur_max, grain_amp_min, grain_amp_max,\n" \
"    grain_transposition, grain_transposition_collection,\n" \
"    grain_transposition_jitter, random_seed, grain_pan_min, grain_pan_max\n"

GRANULATE::GRANULATE() : Instrument()
{
   _stream = NULL;
   _branch = 0;
   _curwinstart = -DBL_MAX;
   _curwinend = -DBL_MAX;
   _block = NULL;
   _keepgoing = true;
   _stopped = false;
}

GRANULATE::~GRANULATE()
{
   delete _stream;
   delete [] _block;
}

int GRANULATE::init(double p[], int n_args)
{
   _nargs = n_args;
   if (_nargs < 19)
      return die("GRANULATE", USAGE_MESSAGE);
   const double outskip = p[0];
   const double dur = p[2];
   const int numinchans = int(p[5]);
   const int seed = _nargs > 22 ? int(p[22]) : 0;
   const bool use3rdOrderInterp = _nargs > 25 ? bool(p[25]) : false;

   if (rtsetoutput(outskip, dur, this) == -1)
      return DONT_SCHEDULE;

   if (outputChannels() > 2)
      return die("GRANULATE", "You can have only mono or stereo output.");
   _stereoOut = (outputChannels() == 2);

   int length;
   double *table = (double *) getPFieldTable(4, &length);
   if (table == NULL)
      return die("GRANULATE", "You must create a table containing the sound "
                              "to granulate.");
   _stream = new GrainStream(SR, table, length, numinchans, outputChannels(),
                             PRESERVE_GRAIN_DURATION, seed, use3rdOrderInterp);

   table = (double *) getPFieldTable(11, &length);
   if (table == NULL)
      return die("GRANULATE", "You must create a table containing the grain "
                              "envelope.");
   _stream->setGrainEnvelopeTable(table, length);

   if (_nargs > 20) {
      table = (double *) getPFieldTable(20, &length);
      if (table != NULL)
         _stream->setGrainTranspositionCollection(table, length);
   }

   _skip = (int) (SR / (float) resetval);

   return nSamps();
}

void GRANULATE::doupdate()
{
   double p[_nargs];
   update(p, _nargs, kInskip | kAmp | kInChan | kWinStart | kWinEnd | kWrap
         | kTraversal | kHopTime | kInJitter | kOutJitter | kMinDur | kMaxDur
         | kMinAmp | kMaxAmp | kTransp | kTranspJitter | kMinPan | kMaxPan);

   _amp = p[3];
   _stream->setInputChan(int(p[6]));
   if (p[7] != _curwinstart || p[8] != _curwinend) {
      _stream->setWindow(p[7], p[8]);
      _curwinstart = p[7];
      _curwinend = p[8];
   }
   _stream->setInskip(p[1]);     // do this after setting window
   _stream->setWraparound(bool(p[9]));
   _stream->setTraversalRateAndGrainHop(p[10], p[12]);
   _stream->setInputJitter(p[13]);
   _stream->setOutputJitter(p[14]);
   _stream->setGrainDuration(p[15], p[16]);
   _stream->setGrainAmp(p[17], p[18]);
   if (_nargs > 19)
      _stream->setGrainTransposition(p[19]);
   if (_nargs > 21)
      _stream->setGrainTranspositionJitter(p[21]);
   if (_nargs > 23 && _stereoOut) {
      const double min = p[23];
      const double max = _nargs > 24 ? p[24] : min;
      _stream->setGrainPan(min, max);
   }
}

int GRANULATE::configure()
{
   _block = new float [RTBUFSAMPS * outputChannels()];
   return _block ? 0 : -1;
}

inline const int min(const int a, const int b)
{
   return a < b ? a : b;
}

#if 0    // shows how to do single-frame I/O, but we use block I/O instead.

int GRANULATE::run()
{
   const int frames = framesToRun();
   const int outchans = outputChannels();
   int i;
   for (i = 0; i < frames; i++) {
      if (--_branch <= 0) {
         doupdate();
         _branch = _skip;
      }

      // If we're not in wrap mode, this returns false when it's time to stop.
      _keepgoing = _stream->prepare();

      float out[outchans];
      if (outchans == 2) {
         out[0] = _stream->lastL() * _amp;
         out[1] = _stream->lastR() * _amp;
      }
      else
         out[0] = _stream->lastL() * _amp;

      rtaddout(out);
      increment();

      if (!_keepgoing) {
// FIXME: how do we remove note from RTcmix queue?
         break;
      }
   }

   return i;
}

#else

int GRANULATE::run()
{
   // NOTE: Without a lot more code, we can't guarantee that doupdate will
   // be called exactly every _skip samples, the way we can when not doing
   // block I/O.  But this seems worth sacrificing for the clear performance
   // improvement that block I/O offers.

   const int frames = framesToRun();
   int blockframes = min(frames, _skip);
   int framesdone = 0;
   while (1) {
      if (_branch <= 0) {
         doupdate();
         _branch = _skip;
      }
      _branch -= blockframes;

      // If we're not in wrap mode, this returns false when it's time to stop.
      if (_keepgoing)
         _keepgoing = _stream->processBlock(_block, blockframes, _amp);
      rtbaddout(_block, blockframes);
      increment(blockframes);
      if (!_keepgoing && !_stopped) {
// FIXME: how do we remove note from RTcmix queue?
         const int samps = RTBUFSAMPS * outputChannels();
         for (int i = 0; i < samps; i++)
            _block[i] = 0.0f;
         advise("GRANULATE", "Reached end in non-wrap mode; stopping output.");
         _stopped = true;
      }
      framesdone += blockframes;
      if (framesdone == frames)
         break;
      assert(framesdone < frames);
      const int remaining = frames - framesdone;
      if (remaining < blockframes)
         blockframes = remaining;
   }
   return frames;
}

#endif

Instrument *makeGRANULATE()
{
   GRANULATE *inst;

   inst = new GRANULATE();
   inst->set_bus_config("GRANULATE");

   return inst;
}

void rtprofile()
{
   RT_INTRO("GRANULATE", makeGRANULATE);
}

<commit_msg>Fix usage comment.<commit_after>/* GRANULATE - granulation of sound stored in a table

   Any parameter marked with '*' can receive updates from a real-time
   control source.

      p0  = output start time
    * p1  = input start time (will be constrained to window, p5-6)
            NOTE: Unlike other instruments, this applies to a table, not to
            an input sound file.
      p2  = total duration
    * p3  = amplitude multiplier

      p4  = input sound table (e.g., maketable("soundfile", ...))
      p5  = number of channels in sample table
    * p6  = input channel of sample table

    * p7  = input window start time
    * p8  = input window end time
    * p9  = wraparound (see below)

      The "input window" refers to the portion of the input sound table read
      by the granulator.  When the granulator reaches the end of the window,
      it wraps around to the beginning, unless p9 (wraparound) is false (0).
      In that case, please note that any data coming from time-varying tables
      (such as p3), may not span the entire table.

      It's best to arrange for the window start to be a little after the sound
      table start, and for the window end to be a little before the table end.

    * p10 = traversal rate

      The granulator moves through the input window at the "traversal rate."
      Here are some sample values:

            0     no movement
            1     move forward at normal rate
            2.5   move forward at a rate that is 2.5 times normal
            -1    move backward at normal rate

      The following parameters determine the character of individual grains.

      p11 = grain envelope table

    * p12 = grain hop time (time between successive grains).  This is the
            inverse of grain density (grains per second); you can use
            makeconverter(..., "inverse") to convert a table or real-time
            control source from density to hop time.

    * p13 = grain input time jitter
            Maximum randomly determined amount to add or subtract from the
            input start time for a grain.

    * p14 = grain output time jitter
            Maximum randomly determined amount to add or subtract from the
            output start time for a grain, which is controlled by p12 (grain
            hop time).

    * p15 = grain duration minimum
    * p16 = grain duration maximum

    * p17 = grain amplitude multiplier minimum
    * p18 = grain amplitude multiplier maximum

    * p19 = grain transposition (in linear octaves, relative to 0)
            [optional; if missing, no transposition]

      p20 = grain transposition collection
            If this is a table, it contains a list of transpositions (in oct.pc)
            from which to select randomly.  If it's not a table, it's ignored.
            The table cannot be updated dynamically.  The value of p19
            (transposition) affects the collection.
            [optional]

    * p21 = grain transposition jitter
            Maximum randomly determined amount to add or subtract from the
            current transposition value.  If p20 (transposition collection)
            is active, then jitter controls how much of the collection to
            choose from.  In this case, jitter is an oct.pc value.  For example,
            if the collection is [0.00, 0.02, 0.05, 0.07], then a jitter value
            of 0.05 will cause only the first 3 pitches to be chosen, whereas a
            jitter value of 0.07 would cause all 4 to be chosen.
            [optional; if missing, no transposition jitter]

      p22 = random seed (integer)
            [optional; if missing, uses system clock]

    * p23 = grain pan minimum (pctleft: 0-1)
    * p24 = grain pan maximum
            [optional, ignored if mono output; if both missing, min = 0 and
            min = 1; if max missing, max = min]

      p25 = use 3rd-order interpolation, instead of 2nd-order, when transposing.
            This is more CPU-intensive, and often doesn't make a noticeable
            difference.  (0: use 2nd-order, 1: use 3rd-order)
            [optional; 2nd-order interp is the default]

   John Gibson <johgibso at indiana dot edu>, 1/29/05
*/
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <assert.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <rt.h>
#include <rtdefs.h>
#include "GRANULATE.h"
#include "grainstream.h"

//#define DEBUG
//#define NDEBUG     // disable asserts

#define PRESERVE_GRAIN_DURATION  true     // regardless of transposition

#define USAGE_MESSAGE \
"Usage:\n" \
"  GRANULATE(start, inskip, dur, amp, sound_table, num_chans, input_chan,\n" \
"    window_start_time, window_end_time, wraparound, traversal_rate,\n" \
"    grain_env, grain_hoptime, grain_input_jitter, grain_output_jitter,\n" \
"    grain_dur_min, grain_dur_max, grain_amp_min, grain_amp_max,\n" \
"    grain_transposition, grain_transposition_collection,\n" \
"    grain_transposition_jitter, random_seed, grain_pan_min, grain_pan_max\n"

GRANULATE::GRANULATE() : Instrument()
{
   _stream = NULL;
   _branch = 0;
   _curwinstart = -DBL_MAX;
   _curwinend = -DBL_MAX;
   _block = NULL;
   _keepgoing = true;
   _stopped = false;
}

GRANULATE::~GRANULATE()
{
   delete _stream;
   delete [] _block;
}

int GRANULATE::init(double p[], int n_args)
{
   _nargs = n_args;
   if (_nargs < 19)
      return die("GRANULATE", USAGE_MESSAGE);
   const double outskip = p[0];
   const double dur = p[2];
   const int numinchans = int(p[5]);
   const int seed = _nargs > 22 ? int(p[22]) : 0;
   const bool use3rdOrderInterp = _nargs > 25 ? bool(p[25]) : false;

   if (rtsetoutput(outskip, dur, this) == -1)
      return DONT_SCHEDULE;

   if (outputChannels() > 2)
      return die("GRANULATE", "You can have only mono or stereo output.");
   _stereoOut = (outputChannels() == 2);

   int length;
   double *table = (double *) getPFieldTable(4, &length);
   if (table == NULL)
      return die("GRANULATE", "You must create a table containing the sound "
                              "to granulate.");
   _stream = new GrainStream(SR, table, length, numinchans, outputChannels(),
                             PRESERVE_GRAIN_DURATION, seed, use3rdOrderInterp);

   table = (double *) getPFieldTable(11, &length);
   if (table == NULL)
      return die("GRANULATE", "You must create a table containing the grain "
                              "envelope.");
   _stream->setGrainEnvelopeTable(table, length);

   if (_nargs > 20) {
      table = (double *) getPFieldTable(20, &length);
      if (table != NULL)
         _stream->setGrainTranspositionCollection(table, length);
   }

   _skip = (int) (SR / (float) resetval);

   return nSamps();
}

void GRANULATE::doupdate()
{
   double p[_nargs];
   update(p, _nargs, kInskip | kAmp | kInChan | kWinStart | kWinEnd | kWrap
         | kTraversal | kHopTime | kInJitter | kOutJitter | kMinDur | kMaxDur
         | kMinAmp | kMaxAmp | kTransp | kTranspJitter | kMinPan | kMaxPan);

   _amp = p[3];
   _stream->setInputChan(int(p[6]));
   if (p[7] != _curwinstart || p[8] != _curwinend) {
      _stream->setWindow(p[7], p[8]);
      _curwinstart = p[7];
      _curwinend = p[8];
   }
   _stream->setInskip(p[1]);     // do this after setting window
   _stream->setWraparound(bool(p[9]));
   _stream->setTraversalRateAndGrainHop(p[10], p[12]);
   _stream->setInputJitter(p[13]);
   _stream->setOutputJitter(p[14]);
   _stream->setGrainDuration(p[15], p[16]);
   _stream->setGrainAmp(p[17], p[18]);
   if (_nargs > 19)
      _stream->setGrainTransposition(p[19]);
   if (_nargs > 21)
      _stream->setGrainTranspositionJitter(p[21]);
   if (_nargs > 23 && _stereoOut) {
      const double min = p[23];
      const double max = _nargs > 24 ? p[24] : min;
      _stream->setGrainPan(min, max);
   }
}

int GRANULATE::configure()
{
   _block = new float [RTBUFSAMPS * outputChannels()];
   return _block ? 0 : -1;
}

inline const int min(const int a, const int b)
{
   return a < b ? a : b;
}

#if 0    // shows how to do single-frame I/O, but we use block I/O instead.

int GRANULATE::run()
{
   const int frames = framesToRun();
   const int outchans = outputChannels();
   int i;
   for (i = 0; i < frames; i++) {
      if (--_branch <= 0) {
         doupdate();
         _branch = _skip;
      }

      // If we're not in wrap mode, this returns false when it's time to stop.
      _keepgoing = _stream->prepare();

      float out[outchans];
      if (outchans == 2) {
         out[0] = _stream->lastL() * _amp;
         out[1] = _stream->lastR() * _amp;
      }
      else
         out[0] = _stream->lastL() * _amp;

      rtaddout(out);
      increment();

      if (!_keepgoing) {
// FIXME: how do we remove note from RTcmix queue?
         break;
      }
   }

   return i;
}

#else

int GRANULATE::run()
{
   // NOTE: Without a lot more code, we can't guarantee that doupdate will
   // be called exactly every _skip samples, the way we can when not doing
   // block I/O.  But this seems worth sacrificing for the clear performance
   // improvement that block I/O offers.

   const int frames = framesToRun();
   int blockframes = min(frames, _skip);
   int framesdone = 0;
   while (1) {
      if (_branch <= 0) {
         doupdate();
         _branch = _skip;
      }
      _branch -= blockframes;

      // If we're not in wrap mode, this returns false when it's time to stop.
      if (_keepgoing)
         _keepgoing = _stream->processBlock(_block, blockframes, _amp);
      rtbaddout(_block, blockframes);
      increment(blockframes);
      if (!_keepgoing && !_stopped) {
// FIXME: how do we remove note from RTcmix queue?
         const int samps = RTBUFSAMPS * outputChannels();
         for (int i = 0; i < samps; i++)
            _block[i] = 0.0f;
         advise("GRANULATE", "Reached end in non-wrap mode; stopping output.");
         _stopped = true;
      }
      framesdone += blockframes;
      if (framesdone == frames)
         break;
      assert(framesdone < frames);
      const int remaining = frames - framesdone;
      if (remaining < blockframes)
         blockframes = remaining;
   }
   return frames;
}

#endif

Instrument *makeGRANULATE()
{
   GRANULATE *inst;

   inst = new GRANULATE();
   inst->set_bus_config("GRANULATE");

   return inst;
}

void rtprofile()
{
   RT_INTRO("GRANULATE", makeGRANULATE);
}

<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// File: CollectionOptimisation.cpp
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// License for the specific language governing rights and limitations under
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Collection top class definition
//
///////////////////////////////////////////////////////////////////////////////

#include <Collections/CollectionOptimisation.h>
#include <LibUtilities/BasicUtils/ParseUtils.hpp>

namespace Nektar
{
namespace Collections
{

// static manager for Operator ImplementationMap
map<OpImpTimingKey,OperatorImpMap> CollectionOptimisation::m_opImpMap;

CollectionOptimisation::CollectionOptimisation(
        LibUtilities::SessionReaderSharedPtr pSession,
        ImplementationType defaultType)
{
    int i;
    map<ElmtOrder, ImplementationType> defaults;
    map<ElmtOrder, ImplementationType>::iterator it;
    bool verbose  = (pSession.get()) &&
                    (pSession->DefinesCmdLineArgument("verbose")) &&
                    (pSession->GetComm()->GetRank() == 0);

    m_setByXml    = false;
    m_autotune    = false;
    m_maxCollSize = 0;
    m_defaultType = defaultType == eNoImpType ? eIterPerExp : defaultType;

    map<string, LibUtilities::ShapeType> elTypes;
    map<string, LibUtilities::ShapeType>::iterator it2;
    elTypes["S"] = LibUtilities::eSegment;
    elTypes["T"] = LibUtilities::eTriangle;
    elTypes["Q"] = LibUtilities::eQuadrilateral;
    elTypes["A"] = LibUtilities::eTetrahedron;
    elTypes["P"] = LibUtilities::ePyramid;
    elTypes["R"] = LibUtilities::ePrism;
    elTypes["H"] = LibUtilities::eHexahedron;

    // Set defaults for all element types.
    for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
    {
        defaults[ElmtOrder(it2->second, -1)] = m_defaultType;
    }

    if (defaultType == eNoImpType)
    {
        for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
        {
            for (int i = 1; i < 5; ++i)
            {
                defaults[ElmtOrder(it2->second, i)] = eStdMat;
            }
        }
    }

    map<string, OperatorType> opTypes;
    for (i = 0; i < SIZE_OperatorType; ++i)
    {
        opTypes[OperatorTypeMap[i]] = (OperatorType)i;
        m_global[(OperatorType)i] = defaults;
    }

    map<string, ImplementationType> impTypes;
    for (i = 0; i < SIZE_ImplementationType; ++i)
    {
        impTypes[ImplementationTypeMap[i]] = (ImplementationType)i;
    }

    if(pSession.get()) // turn off file reader if dummy pointer is given
    {
        TiXmlDocument &doc = pSession->GetDocument();
        TiXmlHandle docHandle(&doc);
        TiXmlElement *master = docHandle.FirstChildElement("NEKTAR").Element();
        ASSERTL0(master, "Unable to find NEKTAR tag in file.");

        TiXmlElement *xmlCol = master->FirstChildElement("COLLECTIONS");

        // Check if user has specified some options
        if (xmlCol)
        {
            // Set the maxsize and default implementation type if provided
            const char *maxSize = xmlCol->Attribute("MAXSIZE");
            m_maxCollSize = (maxSize ? atoi(maxSize) : 0);

            const char *defaultImpl = xmlCol->Attribute("DEFAULT");
            m_defaultType = defaultType;

            // If user has specified a default impl type, autotuning
            // and set this default across all operators.
            if (defaultType == eNoImpType && defaultImpl)
            {
                const std::string collinfo = string(defaultImpl);
                m_autotune = boost::iequals(collinfo, "auto");

                if (!m_autotune)
                {
                    for(i = 1; i < Collections::SIZE_ImplementationType; ++i)
                    {
                        if(boost::iequals(collinfo,
                                Collections::ImplementationTypeMap[i]))
                        {
                            m_defaultType = (Collections::ImplementationType) i;
                            break;
                        }
                    }

                    ASSERTL0(i != Collections::SIZE_ImplementationType,
                         "Unknown default collection scheme: "+collinfo);

                    // Override default types
                    for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
                    {
                        defaults[ElmtOrder(it2->second, -1)] = m_defaultType;
                    }

                    for (i = 0; i < SIZE_OperatorType; ++i)
                    {
                        m_global[(OperatorType)i] = defaults;
                    }
                }
            }

            // Now process operator-specific implementation selections
            TiXmlElement *elmt = xmlCol->FirstChildElement();
            while (elmt)
            {
                m_setByXml = true;

                string tagname = elmt->ValueStr();

                ASSERTL0(boost::iequals(tagname, "OPERATOR"),
                        "Only OPERATOR tags are supported inside the "
                        "COLLECTIONS tag.");

                const char *attr = elmt->Attribute("TYPE");
                ASSERTL0(attr, "Missing TYPE in OPERATOR tag.");
                string opType(attr);

                ASSERTL0(opTypes.count(opType) > 0,
                        "Unknown OPERATOR type " + opType + ".");

                OperatorType ot = opTypes[opType];

                TiXmlElement *elmt2 = elmt->FirstChildElement();

                while (elmt2)
                {
                    string tagname = elmt2->ValueStr();
                    ASSERTL0(boost::iequals(tagname, "ELEMENT"),
                            "Only ELEMENT tags are supported inside the "
                            "OPERATOR tag.");

                    const char *attr = elmt2->Attribute("TYPE");
                    ASSERTL0(attr, "Missing TYPE in ELEMENT tag.");

                    string elType(attr);
                    it2 = elTypes.find(elType);
                    ASSERTL0(it2 != elTypes.end(),
                            "Unknown element type "+elType+" in ELEMENT "
                            "tag");

                    const char *attr2 = elmt2->Attribute("IMPTYPE");
                    ASSERTL0(attr2, "Missing IMPTYPE in ELEMENT tag.");
                    string impType(attr2);
                    ASSERTL0(impTypes.count(impType) > 0,
                            "Unknown IMPTYPE type " + impType + ".");

                    const char *attr3 = elmt2->Attribute("ORDER");
                    ASSERTL0(attr3, "Missing ORDER in ELEMENT tag.");
                    string order(attr3);

                    if (order == "*")
                    {
                        m_global[ot][ElmtOrder(it2->second, -1)]
                                     = impTypes[impType];
                    }
                    else
                    {
                        vector<unsigned int> orders;
                        ParseUtils::GenerateSeqVector(order.c_str(), orders);

                        for (int i = 0; i < orders.size(); ++i)
                        {
                            m_global[ot][ElmtOrder(it2->second, orders[i])]
                                         = impTypes[impType];
                        }
                    }

                    elmt2 = elmt2->NextSiblingElement();
                }

                elmt = elmt->NextSiblingElement();
            }

            // Print out operator map
            if (verbose)
            {
                if (!m_setByXml && !m_autotune)
                {
                    cout << "Setting Collection optimisation using: "
                         << Collections::ImplementationTypeMap[m_defaultType]
                         << endl;
                }

                if (m_setByXml)
                {
                    map<OperatorType, map<ElmtOrder,
                                          ImplementationType> >::iterator mIt;
                    map<ElmtOrder, ImplementationType>::iterator eIt;
                    for (mIt = m_global.begin(); mIt != m_global.end(); mIt++)
                    {
                        cout << "Operator " << OperatorTypeMap[mIt->first]
                                                               << ":" << endl;

                        for (eIt = mIt->second.begin();
                                eIt != mIt->second.end(); eIt++)
                        {
                            cout << "- "
                                 << LibUtilities::ShapeTypeMap[eIt->first.first]
                                 << " order " << eIt->first.second << " -> "
                                 << ImplementationTypeMap[eIt->second] << endl;
                        }
                    }
                }
            }
        }
    }
}

OperatorImpMap  CollectionOptimisation::GetOperatorImpMap(
        StdRegions::StdExpansionSharedPtr pExp)
{
    map<OperatorType, map<ElmtOrder, ImplementationType> >::iterator it;
    map<ElmtOrder, ImplementationType>::iterator it2;

    OperatorImpMap ret;
    ElmtOrder searchKey(pExp->DetShapeType(),
                        pExp->GetBasisNumModes(0));
    ElmtOrder defSearch(pExp->DetShapeType(), -1);

    for (it = m_global.begin(); it != m_global.end(); ++it)
    {
        ImplementationType impType;

        it2 = it->second.find(searchKey);

        if (it2 == it->second.end())
        {
            it2 = it->second.find(defSearch);
            if (it2 == it->second.end())
            {
                // Shouldn't be able to reach here.
                impType = eNoCollection;
            }
            else
            {
                impType = it2->second;
            }
        }
        else
        {
            impType = it2->second;
        }

        ret[it->first] = impType;
    }

    return ret;
}

OperatorImpMap CollectionOptimisation::SetWithTimings(
        vector<StdRegions::StdExpansionSharedPtr> pCollExp,
        OperatorImpMap &impTypes,
        bool verbose )
{
    OperatorImpMap ret;

    StdRegions::StdExpansionSharedPtr pExp = pCollExp[0];

    // check to see if already defined for this expansion
    OpImpTimingKey OpKey(pExp,pCollExp.size(),pExp->GetNumBases());
    if(m_opImpMap.count(OpKey) != 0)
    {
        ret = m_opImpMap[OpKey];
        return ret;
    }

    int maxsize = pCollExp.size()*max(pExp->GetNcoeffs(),pExp->GetTotPoints());
    Array<OneD, NekDouble> inarray(maxsize,1.0);
    Array<OneD, NekDouble> outarray1(maxsize);
    Array<OneD, NekDouble> outarray2(maxsize);
    Array<OneD, NekDouble> outarray3(maxsize);

    Timer t;

    if(verbose)
    {
        cout << "Collection Implemenation for "
             << LibUtilities::ShapeTypeMap[pExp->DetShapeType()] << " ( ";
        for(int i = 0; i < pExp->GetNumBases(); ++i)
        {
            cout << pExp->GetBasis(i)->GetNumModes() <<" ";
        }
        cout << ")" <<  " for ngeoms = " << pCollExp.size() << endl;
    }
    // set  up an array of collections
    CollectionVector coll;
    for(int imp = 1; imp < SIZE_ImplementationType; ++imp)
    {
        ImplementationType impType = (ImplementationType)imp;
        OperatorImpMap impTypes;
        for (int i = 0; i < SIZE_OperatorType; ++i)
        {
            OperatorType opType = (OperatorType)i;
            OperatorKey opKey(pCollExp[0]->DetShapeType(), opType, impType,
                              pCollExp[0]->IsNodalNonTensorialExp());

            if (GetOperatorFactory().ModuleExists(opKey))
            {
                impTypes[opType] = impType;
            }
            else
            {
                cout << "Note: Implementation does not exist: " << opKey << endl;
            }
        }

        Collection collloc(pCollExp,impTypes);
        coll.push_back(collloc);
    }

    // Determine the number of tests to do in one second
    Array<OneD, int> Ntest(SIZE_OperatorType);
    for(int i = 0; i < SIZE_OperatorType; ++i)
    {
        OperatorType OpType = (OperatorType)i;

        t.Start();
        coll[0].ApplyOperator(OpType,
                           inarray,
                           outarray1,
                           outarray2,
                           outarray3);
        t.Stop();

        NekDouble oneTest = t.TimePerTest(1);

        Ntest[i] = max((int)(0.25/oneTest),1);
    }

    Array<OneD, NekDouble> timing(SIZE_ImplementationType);
    // loop over all operators and determine fastest implementation
    for(int i = 0; i < SIZE_OperatorType; ++i)
    {
        OperatorType OpType = (OperatorType)i;

        // call collection implementation in thorugh ExpList.
        for (int imp = 0; imp < coll.size(); ++imp)
        {
            if (coll[imp].HasOperator(OpType))
            {
                t.Start();
                for(int n = 0; n < Ntest[i]; ++n)
                {
                    coll[imp].ApplyOperator(OpType,
                                      inarray,
                                      outarray1,
                                      outarray2,
                                      outarray3);
                }
                t.Stop();
                timing[imp] = t.TimePerTest(Ntest[i]);
            }
            else
            {
                timing[imp] = 1000.0;
            }
        }
        // determine optimal implementation. Note +1 to
        // remove NoImplementationType flag
        int minImp = Vmath::Imin(coll.size(),timing,1)+1;

        if(verbose)
        {
            cout << "\t " << OperatorTypeMap[i] << ": "
                 << ImplementationTypeMap[minImp] << "\t (";
            for(int j = 0; j < coll.size(); ++j)
            {
                if (timing[j] > 999.0)
                {
                    cout << "-";
                }
                else
                {
                    cout << timing[j] ;
                }
                if(j != coll.size()-1)
                {
                    cout <<", ";
                }
            }
            cout << ")" <<endl;
        }
        // could reset global map if reusing  method?
        //m_global[OpType][pExp->DetShapeType()] = (ImplementationType)minImp;
        // set up new map
        ret[OpType] = (ImplementationType)minImp;
    }

    // store map for use by another expansion.
    m_opImpMap[OpKey] = ret;
    return ret;
}

}
}
<commit_msg>Added specific default implementation choices for PhysDeriv op.<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// File: CollectionOptimisation.cpp
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// License for the specific language governing rights and limitations under
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Collection top class definition
//
///////////////////////////////////////////////////////////////////////////////

#include <Collections/CollectionOptimisation.h>
#include <LibUtilities/BasicUtils/ParseUtils.hpp>

namespace Nektar
{
namespace Collections
{

// static manager for Operator ImplementationMap
map<OpImpTimingKey,OperatorImpMap> CollectionOptimisation::m_opImpMap;

CollectionOptimisation::CollectionOptimisation(
        LibUtilities::SessionReaderSharedPtr pSession,
        ImplementationType defaultType)
{
    int i;
    map<ElmtOrder, ImplementationType> defaults;
    map<ElmtOrder, ImplementationType> defaultsPhysDeriv;
    map<ElmtOrder, ImplementationType>::iterator it;
    bool verbose  = (pSession.get()) &&
                    (pSession->DefinesCmdLineArgument("verbose")) &&
                    (pSession->GetComm()->GetRank() == 0);

    m_setByXml    = false;
    m_autotune    = false;
    m_maxCollSize = 0;
    m_defaultType = defaultType == eNoImpType ? eIterPerExp : defaultType;

    map<string, LibUtilities::ShapeType> elTypes;
    map<string, LibUtilities::ShapeType>::iterator it2;
    elTypes["S"] = LibUtilities::eSegment;
    elTypes["T"] = LibUtilities::eTriangle;
    elTypes["Q"] = LibUtilities::eQuadrilateral;
    elTypes["A"] = LibUtilities::eTetrahedron;
    elTypes["P"] = LibUtilities::ePyramid;
    elTypes["R"] = LibUtilities::ePrism;
    elTypes["H"] = LibUtilities::eHexahedron;

    // Set defaults for all element types.
    for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
    {
        defaults          [ElmtOrder(it2->second, -1)] = m_defaultType;
        defaultsPhysDeriv [ElmtOrder(it2->second, -1)] = m_defaultType;
    }

    if (defaultType == eNoImpType)
    {
        for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
        {
            defaultsPhysDeriv [ElmtOrder(it2->second, -1)] = eNoCollection;
            for (int i = 1; i < 5; ++i)
            {
                defaults[ElmtOrder(it2->second, i)] = eStdMat;
            }
            for (int i = 1; i < 3; ++i)
            {
                defaultsPhysDeriv[ElmtOrder(it2->second, i)] = eSumFac;
            }
        }
    }

    map<string, OperatorType> opTypes;
    for (i = 0; i < SIZE_OperatorType; ++i)
    {
        opTypes[OperatorTypeMap[i]] = (OperatorType)i;
        switch ((OperatorType)i)
        {
            case ePhysDeriv:
                m_global[(OperatorType)i] = defaultsPhysDeriv;
                break;
            default:
                m_global[(OperatorType)i] = defaults;
        }
    }

    map<string, ImplementationType> impTypes;
    for (i = 0; i < SIZE_ImplementationType; ++i)
    {
        impTypes[ImplementationTypeMap[i]] = (ImplementationType)i;
    }

    if(pSession.get()) // turn off file reader if dummy pointer is given
    {
        TiXmlDocument &doc = pSession->GetDocument();
        TiXmlHandle docHandle(&doc);
        TiXmlElement *master = docHandle.FirstChildElement("NEKTAR").Element();
        ASSERTL0(master, "Unable to find NEKTAR tag in file.");

        TiXmlElement *xmlCol = master->FirstChildElement("COLLECTIONS");

        // Check if user has specified some options
        if (xmlCol)
        {
            // Set the maxsize and default implementation type if provided
            const char *maxSize = xmlCol->Attribute("MAXSIZE");
            m_maxCollSize = (maxSize ? atoi(maxSize) : 0);

            const char *defaultImpl = xmlCol->Attribute("DEFAULT");
            m_defaultType = defaultType;

            // If user has specified a default impl type, autotuning
            // and set this default across all operators.
            if (defaultType == eNoImpType && defaultImpl)
            {
                const std::string collinfo = string(defaultImpl);
                m_autotune = boost::iequals(collinfo, "auto");

                if (!m_autotune)
                {
                    for(i = 1; i < Collections::SIZE_ImplementationType; ++i)
                    {
                        if(boost::iequals(collinfo,
                                Collections::ImplementationTypeMap[i]))
                        {
                            m_defaultType = (Collections::ImplementationType) i;
                            break;
                        }
                    }

                    ASSERTL0(i != Collections::SIZE_ImplementationType,
                         "Unknown default collection scheme: "+collinfo);

                    // Override default types
                    for (it2 = elTypes.begin(); it2 != elTypes.end(); ++it2)
                    {
                        defaults[ElmtOrder(it2->second, -1)] = m_defaultType;
                    }

                    for (i = 0; i < SIZE_OperatorType; ++i)
                    {
                        m_global[(OperatorType)i] = defaults;
                    }
                }
            }

            // Now process operator-specific implementation selections
            TiXmlElement *elmt = xmlCol->FirstChildElement();
            while (elmt)
            {
                m_setByXml = true;

                string tagname = elmt->ValueStr();

                ASSERTL0(boost::iequals(tagname, "OPERATOR"),
                        "Only OPERATOR tags are supported inside the "
                        "COLLECTIONS tag.");

                const char *attr = elmt->Attribute("TYPE");
                ASSERTL0(attr, "Missing TYPE in OPERATOR tag.");
                string opType(attr);

                ASSERTL0(opTypes.count(opType) > 0,
                        "Unknown OPERATOR type " + opType + ".");

                OperatorType ot = opTypes[opType];

                TiXmlElement *elmt2 = elmt->FirstChildElement();

                while (elmt2)
                {
                    string tagname = elmt2->ValueStr();
                    ASSERTL0(boost::iequals(tagname, "ELEMENT"),
                            "Only ELEMENT tags are supported inside the "
                            "OPERATOR tag.");

                    const char *attr = elmt2->Attribute("TYPE");
                    ASSERTL0(attr, "Missing TYPE in ELEMENT tag.");

                    string elType(attr);
                    it2 = elTypes.find(elType);
                    ASSERTL0(it2 != elTypes.end(),
                            "Unknown element type "+elType+" in ELEMENT "
                            "tag");

                    const char *attr2 = elmt2->Attribute("IMPTYPE");
                    ASSERTL0(attr2, "Missing IMPTYPE in ELEMENT tag.");
                    string impType(attr2);
                    ASSERTL0(impTypes.count(impType) > 0,
                            "Unknown IMPTYPE type " + impType + ".");

                    const char *attr3 = elmt2->Attribute("ORDER");
                    ASSERTL0(attr3, "Missing ORDER in ELEMENT tag.");
                    string order(attr3);

                    if (order == "*")
                    {
                        m_global[ot][ElmtOrder(it2->second, -1)]
                                     = impTypes[impType];
                    }
                    else
                    {
                        vector<unsigned int> orders;
                        ParseUtils::GenerateSeqVector(order.c_str(), orders);

                        for (int i = 0; i < orders.size(); ++i)
                        {
                            m_global[ot][ElmtOrder(it2->second, orders[i])]
                                         = impTypes[impType];
                        }
                    }

                    elmt2 = elmt2->NextSiblingElement();
                }

                elmt = elmt->NextSiblingElement();
            }

            // Print out operator map
            if (verbose)
            {
                if (!m_setByXml && !m_autotune)
                {
                    cout << "Setting Collection optimisation using: "
                         << Collections::ImplementationTypeMap[m_defaultType]
                         << endl;
                }

                if (m_setByXml)
                {
                    map<OperatorType, map<ElmtOrder,
                                          ImplementationType> >::iterator mIt;
                    map<ElmtOrder, ImplementationType>::iterator eIt;
                    for (mIt = m_global.begin(); mIt != m_global.end(); mIt++)
                    {
                        cout << "Operator " << OperatorTypeMap[mIt->first]
                                                               << ":" << endl;

                        for (eIt = mIt->second.begin();
                                eIt != mIt->second.end(); eIt++)
                        {
                            cout << "- "
                                 << LibUtilities::ShapeTypeMap[eIt->first.first]
                                 << " order " << eIt->first.second << " -> "
                                 << ImplementationTypeMap[eIt->second] << endl;
                        }
                    }
                }
            }
        }
    }
}

OperatorImpMap  CollectionOptimisation::GetOperatorImpMap(
        StdRegions::StdExpansionSharedPtr pExp)
{
    map<OperatorType, map<ElmtOrder, ImplementationType> >::iterator it;
    map<ElmtOrder, ImplementationType>::iterator it2;

    OperatorImpMap ret;
    ElmtOrder searchKey(pExp->DetShapeType(),
                        pExp->GetBasisNumModes(0));
    ElmtOrder defSearch(pExp->DetShapeType(), -1);

    for (it = m_global.begin(); it != m_global.end(); ++it)
    {
        ImplementationType impType;

        it2 = it->second.find(searchKey);

        if (it2 == it->second.end())
        {
            it2 = it->second.find(defSearch);
            if (it2 == it->second.end())
            {
                // Shouldn't be able to reach here.
                impType = eNoCollection;
            }
            else
            {
                impType = it2->second;
            }
        }
        else
        {
            impType = it2->second;
        }

        ret[it->first] = impType;
    }

    return ret;
}

OperatorImpMap CollectionOptimisation::SetWithTimings(
        vector<StdRegions::StdExpansionSharedPtr> pCollExp,
        OperatorImpMap &impTypes,
        bool verbose )
{
    OperatorImpMap ret;

    StdRegions::StdExpansionSharedPtr pExp = pCollExp[0];

    // check to see if already defined for this expansion
    OpImpTimingKey OpKey(pExp,pCollExp.size(),pExp->GetNumBases());
    if(m_opImpMap.count(OpKey) != 0)
    {
        ret = m_opImpMap[OpKey];
        return ret;
    }

    int maxsize = pCollExp.size()*max(pExp->GetNcoeffs(),pExp->GetTotPoints());
    Array<OneD, NekDouble> inarray(maxsize,1.0);
    Array<OneD, NekDouble> outarray1(maxsize);
    Array<OneD, NekDouble> outarray2(maxsize);
    Array<OneD, NekDouble> outarray3(maxsize);

    Timer t;

    if(verbose)
    {
        cout << "Collection Implemenation for "
             << LibUtilities::ShapeTypeMap[pExp->DetShapeType()] << " ( ";
        for(int i = 0; i < pExp->GetNumBases(); ++i)
        {
            cout << pExp->GetBasis(i)->GetNumModes() <<" ";
        }
        cout << ")" <<  " for ngeoms = " << pCollExp.size() << endl;
    }
    // set  up an array of collections
    CollectionVector coll;
    for(int imp = 1; imp < SIZE_ImplementationType; ++imp)
    {
        ImplementationType impType = (ImplementationType)imp;
        OperatorImpMap impTypes;
        for (int i = 0; i < SIZE_OperatorType; ++i)
        {
            OperatorType opType = (OperatorType)i;
            OperatorKey opKey(pCollExp[0]->DetShapeType(), opType, impType,
                              pCollExp[0]->IsNodalNonTensorialExp());

            if (GetOperatorFactory().ModuleExists(opKey))
            {
                impTypes[opType] = impType;
            }
            else
            {
                cout << "Note: Implementation does not exist: " << opKey << endl;
            }
        }

        Collection collloc(pCollExp,impTypes);
        coll.push_back(collloc);
    }

    // Determine the number of tests to do in one second
    Array<OneD, int> Ntest(SIZE_OperatorType);
    for(int i = 0; i < SIZE_OperatorType; ++i)
    {
        OperatorType OpType = (OperatorType)i;

        t.Start();
        coll[0].ApplyOperator(OpType,
                           inarray,
                           outarray1,
                           outarray2,
                           outarray3);
        t.Stop();

        NekDouble oneTest = t.TimePerTest(1);

        Ntest[i] = max((int)(0.25/oneTest),1);
    }

    Array<OneD, NekDouble> timing(SIZE_ImplementationType);
    // loop over all operators and determine fastest implementation
    for(int i = 0; i < SIZE_OperatorType; ++i)
    {
        OperatorType OpType = (OperatorType)i;

        // call collection implementation in thorugh ExpList.
        for (int imp = 0; imp < coll.size(); ++imp)
        {
            if (coll[imp].HasOperator(OpType))
            {
                t.Start();
                for(int n = 0; n < Ntest[i]; ++n)
                {
                    coll[imp].ApplyOperator(OpType,
                                      inarray,
                                      outarray1,
                                      outarray2,
                                      outarray3);
                }
                t.Stop();
                timing[imp] = t.TimePerTest(Ntest[i]);
            }
            else
            {
                timing[imp] = 1000.0;
            }
        }
        // determine optimal implementation. Note +1 to
        // remove NoImplementationType flag
        int minImp = Vmath::Imin(coll.size(),timing,1)+1;

        if(verbose)
        {
            cout << "\t " << OperatorTypeMap[i] << ": "
                 << ImplementationTypeMap[minImp] << "\t (";
            for(int j = 0; j < coll.size(); ++j)
            {
                if (timing[j] > 999.0)
                {
                    cout << "-";
                }
                else
                {
                    cout << timing[j] ;
                }
                if(j != coll.size()-1)
                {
                    cout <<", ";
                }
            }
            cout << ")" <<endl;
        }
        // could reset global map if reusing  method?
        //m_global[OpType][pExp->DetShapeType()] = (ImplementationType)minImp;
        // set up new map
        ret[OpType] = (ImplementationType)minImp;
    }

    // store map for use by another expansion.
    m_opImpMap[OpKey] = ret;
    return ret;
}

}
}
<|endoftext|>