<commit_before>/*-----------------------------------------------------------------------------

 Copyright 2017 Hopsan Group

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

 The full license is available in the file LICENSE.
 For details about the 'Hopsan Group' or information about Authors and
 Contributors see the HOPSANGROUP and AUTHORS files that are located in
 the Hopsan source code root directory.

-----------------------------------------------------------------------------*/

//!
//! @file   ModelUtilities.cpp
//! @author FluMeS
//! @date   2012-05-30
//!
//! @brief Contains model specific helpfunctions for CLI
//!
//$Id$

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstring>

#include "exe_utilities.h"

#include "HopsanEssentials.h"

using namespace std;
using namespace hopsan;

void printWarningMessage(std::string msg)
{
    std::cout << "Warning: " << msg << "\n";
}

void printErrorMessage(std::string msg)
{
    std::cout << "Error: " << msg << "\n";
}

void splitStringOnDelimiter(const std::string &rString, const char delim, std::vector<std::string> &rSplitVector)
{
    rSplitVector.clear();
    string item;
    stringstream ss(rString);
    while(getline(ss, item, delim))
    {
        rSplitVector.push_back(item);
    }
}

void saveResults(ComponentSystem *pSys, const string &rFileName, const SaveResults howMany, const SaveDescriptions descriptions, string prefix, ofstream *pFile)
{
    bool doCloseFile=false;
    if (!pFile)
    {
        pFile = new ofstream;
        pFile->open(rFileName.c_str());
        if (!pFile->good())
        {
            printErrorMessage("Could not open: " + rFileName + " for writing!");
            delete pFile;
            return;
        }
        doCloseFile = true;
    }

    if (pSys)
    {
        // First save time vector for this system
        //! @todo alias a for time ? is that even posible
        if (howMany == Final)
        {
            *pFile << prefix.c_str() << "time,";
            if(descriptions == NameAliasUnit) {
                *pFile << ",s,";
            }
            *pFile << std::scientific << pSys->getTime() << endl;
        }
        else if (howMany == Full)
        {
            vector<double> *pLogTimeVector = pSys->getLogTimeVector();
            if (pLogTimeVector->size() > 0)
            {
                *pFile << prefix.c_str() << "time";
                if(descriptions == NameAliasUnit) {
                    *pFile << ",,s";
                }
                for (size_t t=0; t<pLogTimeVector->size(); ++t)
                {
                    *pFile << "," << std::scientific << (*pLogTimeVector)[t];
                }
                *pFile << endl;
            }
        }

        // Now save log data vectors for all subcomponents
        vector<HString> names = pSys->getSubComponentNames();
        for (size_t c=0; c<names.size(); ++c)
        {
            Component *pComp = pSys->getSubComponent(names[c]);
            if (pComp)
            {
                //cout << "comp: " << c << " of: " << names.size() << endl;
                vector<Port*> ports = pComp->getPortPtrVector();
                for (size_t p=0; p<ports.size(); ++p)
                {
                    //cout << "port: " << p << " of: " << ports.size() << endl;
                    Port *pPort = ports[p];
                    if (!pPort->isLoggingEnabled())
                    {
                        // Ignore ports that have logging disabled
                        continue;
                    }
                    const vector<NodeDataDescription> *pVars = pPort->getNodeDataDescriptions();
                    if (pVars)
                    {
                        for (size_t v=0; v<pVars->size(); ++v)
                        {
                            HString fullname = prefix.c_str() + pComp->getName() + "#" + pPort->getName() + "#" + pVars->at(v).name;

                            if (howMany == Final)
                            {
                                *pFile << fullname.c_str();
                                if(descriptions == NameAliasUnit) {
                                    *pFile << "," << pPort->getVariableAlias(v).c_str() << "," << pVars->at(v).unit.c_str();
                                }
                                *pFile << "," << std::scientific << pPort->readNode(v) << endl;
                            }
                            else if (howMany == Full)
                            {
                                // Only write something if data has been logged (skip ports that are not logged)
                                // We assume that the data vector has been cleared
                                if (pPort->getLogDataVectorPtr()->size() > 0)
                                {
                                    *pFile << fullname.c_str();
                                    if(descriptions == NameAliasUnit) {
                                        *pFile << "," << pPort->getVariableAlias(v).c_str() << "," << pVars->at(v).unit.c_str();
                                    }
                                    //! @todo what about time vector
                                    vector< vector<double> > *pLogData = pPort->getLogDataVectorPtr();
                                    for (size_t t=0; t<pSys->getNumActuallyLoggedSamples(); ++t)
                                    {
                                        *pFile << "," << std::scientific << (*pLogData)[t][v];
                                    }
                                    *pFile << endl;
                                }
                            }
                        }
                    }
                }

                // Recurse into subsystems
                if (pComp->isComponentSystem())
                {
                    saveResults(static_cast<ComponentSystem*>(pComp), rFileName, howMany, descriptions, prefix+pComp->getName().c_str()+"$", pFile);
                }
            }
        }
    }

    if (doCloseFile)
    {
        pFile->close();
        delete pFile;
    }
}

void transposeCSVresults(const std::string &rFileName)
{
    ifstream infile(rFileName.c_str());
    if ( infile.good() )
    {
        vector< vector<string> > linesVec;
        vector<string> lineVec;
        // Read all lines into string vectors
        while(!infile.eof())
        {
            string line;
            getline(infile, line);

            // Split on delimiter
            splitStringOnDelimiter(line, ',', lineVec);

            // Store line vector
            if (lineVec.size() > 0)
            {
                linesVec.push_back(lineVec);
                lineVec.clear();

                // Clear data but reserve memory for next run
                size_t n = lineVec.size();
                lineVec.clear();
                lineVec.reserve(n);
            }
        }
        infile.close();

        // Write back transposed
        ofstream ofile(rFileName.c_str());
        if ( ofile.good() )
        {
            size_t rows = linesVec.size();
            if (rows > 0)
            {
                // Assumes same nr cols on each row
                size_t cols = linesVec[0].size();
                for (size_t c=0; c<cols; ++c)
                {
                    ofile << linesVec[0][c];
                    //cout << linesVec[0][c];
                    for (size_t r=1; r<rows; ++r)
                    {
                        ofile << "," <<  linesVec[r][c];
                        //cout << ", " <<  linesVec[r][c];
                    }
                    ofile << endl;
                    //cout << endl;
                }
            }
        }
        ofile.close();
    }
}

//! @todo should we use CSV parser instead?
void importParameterValuesFromCSV(const std::string filePath, hopsan::ComponentSystem* pSystem)
{
    if (pSystem)
    {
        std::ifstream file;
        file.open(filePath.c_str());
        if ( file.is_open() )
        {
            std::vector<std::string> lineVec;
            std::string line;
            while ( file.good() )
            {
                getline(file, line);
                if (*line.begin() != '#')
                {
                    // Split on delimiter
                    splitStringOnDelimiter(line, ',', lineVec);

                    // Parse line vector
                    if (lineVec.size() == 2)
                    {
                        setParameter(lineVec[0], lineVec[1], pSystem);
                    }
                    else
                    {
                        // Print error for non-empty lines
                        if (lineVec.size() > 0)
                        {
                            printWarningMessage(string("Wrong line format: ") + line);
                        }
                    }
                }
            }
            file.close();
        }
        else
        {
            printErrorMessage(string("Could not open file: ")+filePath);
        }
    }
}

bool startsWith(std::string str, std::string match)
{
    if(str.find(match) == 0) {
        return true;
    }
    else {
        return false;
    }
}

bool endsWith(std::string str, std::string match)
{
    if (str.length() >= match.length()) {
        return (0 == str.compare (str.length() - match.length(), match.length(), match));
    } else {
        return false;
    }
}

bool setParameter(string &rParName, string &rParValue, ComponentSystem *pSystem)
{
    std::replace(rParName.begin(), rParName.end(), ':', '$');
    std::replace(rParName.begin(), rParName.end(), '.', '#');

    if(endsWith(rParName, "#y")) {
        std::cout << "Replacing #y with #Value\n";
        rParName.erase(rParName.length()-2,2);
        rParName.append("#Value");
    };

    std::vector<std::string> vec2, nameVec, syshierarcy;
    string componentName, parameterName;
    splitStringOnDelimiter(rParName, '$', vec2);

    // Last of vec2 will contain the rest of the name filed (comp and param name)
    int i;
    for (i=0; i<int(vec2.size())-1; ++i) {
        syshierarcy.push_back(vec2[i]);
    }

    // Split last name part into comp and param name
    splitStringOnDelimiter(vec2[i], '#', nameVec);
    if (nameVec.size() == 2 || nameVec.size() == 3 ) {
        if (nameVec.size() == 2) {
            componentName = nameVec[0];
            parameterName = nameVec[1];
        }
        if (nameVec.size() == 3) {
            // Set component name and reset the parameter (startvalue) name
            componentName = nameVec[0];
            parameterName = nameVec[1]+"#"+nameVec[2];
        }

        std::cout << "componentName = " << componentName << "\n";

        // Dig down subsystem hiearchy
        ComponentSystem *pParentSys = pSystem;
        for (size_t s=1; s<syshierarcy.size(); ++s) {
            //! @todo what about first level (0), should we check that name is ok
            ComponentSystem *pSubSys = pParentSys->getSubComponentSystem(syshierarcy[s].c_str());
            if (!pSubSys) {
                printErrorMessage("Subsystem: "+syshierarcy[s]+" could not be found in parent system: "+pParentSys->getName().c_str());
                return false;
            }
            else {
                pParentSys = pSubSys;
            }
        }

        // Set the parameter value if component is found
        if (pParentSys) {
            Component *pComp = 0;
            // If syshierarcy is empty then we are setting a parameter in the top-level system
            if (syshierarcy.empty()) {
                pComp = pParentSys;
            }
            else {
                pComp = pParentSys->getSubComponent(componentName.c_str());
            }

            if (pComp) {
                bool ok = pComp->setParameterValue(parameterName.c_str(), rParValue.c_str());
                std::cout << "Setting parameter: " << pComp->getName().c_str() << ", " << parameterName << " = " << rParValue << "\n";
                if (!ok) {
                    std::cout << "Failed!\n";
                    return false;
                }
                std::cout << "Success!\n";

            }
            else {
                printErrorMessage("No component: " + componentName + " in system: " + pParentSys->getName().c_str() );
                return false;
            }
        }
    }
    else {
        std::cout << "Unknown error!\n";
        return false;
    }

    return true;
}

void printHelpText(ComponentSystem *pSystem)
{
    std::cout << "This is a stand-alone executable Hopsan model.\n";
    std::cout << "Model name: " << pSystem->getName().c_str() << "\n";
    std::cout << "\n";
    std::cout << "Usage: " << pSystem->getName().c_str() << " [FLAG]\n";
    std::cout << "Usage: " << pSystem->getName().c_str() << " [OPTIONS]\n";
    std::cout << "\n";
    std::cout << "Flags:\n";
    std::cout << "  -h, --help            Display this help text\n";
    std::cout << "  -p, --parameters      List all tunable model parameters\n";
    std::cout << "\n";
    std::cout << "Options (cannot be used together with flags):\n";
    std::cout << "  start=[real]                Simulation start time\n";
    std::cout << "  step=[real]                 Simulation step size\n";
    std::cout << "  stop=[real]                 Simulation stop time\n";
    std::cout << "  samples=[integer]           Number of log samples\n";
    std::cout << "  results=full/final          Print full results or final results only\n";
    std::cout << "  samples=[integer]           Number of log samples\n";
    std::cout << "  transpose=true/false        Transpose to column-wise CSV file\n";
    std::cout << "  descriptions=all/namesonly  Print names, alias and unit or only name\n";
    std::cout << "  parameterfile=[filename]    Specify parameter input file\n";
    std::cout << "\n";
    std::cout << "Examples:\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " start=0 step=0.001 stop=3\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " samples=1000 transpose=true descriptions=namesonly\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " engine.rpm=2000 results=final\n";
}

void appendParameters(ComponentSystem *pSystem, std::vector<HString> &allParameters, HString prefix="")
{
    std::vector<HString> sysParameters;
    pSystem->getParameterNames(sysParameters);
    for(HString &par : sysParameters) {
        std::string temp = prefix.c_str();
        temp.append(pSystem->getName().c_str());
        temp.append("#");
        temp.append(par.c_str());
        par = temp.c_str();
    }
    allParameters.insert(allParameters.end(), sysParameters.begin(), sysParameters.end());

    for(Component *pComp : pSystem->getSubComponents()) {
        if(pComp->isComponentSystem()) {
            prefix.append(pComp->getName().c_str());
            prefix.append("$");
            appendParameters(reinterpret_cast<ComponentSystem*>(pComp), allParameters);
        }
        std::vector<HString> compParameters;
        pComp->getParameterNames(compParameters);
        for(HString &par : compParameters) {
            std::string temp = prefix.c_str();
            temp.append(pComp->getName().c_str());
            temp.append("#");
            temp.append(par.c_str());
            par = temp.c_str();
        }
        allParameters.insert(allParameters.end(), compParameters.begin(), compParameters.end());
    }
}

void printParameters(ComponentSystem *pSystem)
{
    std::vector<HString> allParameters;
    appendParameters(pSystem, allParameters);
    std::sort(allParameters.begin(), allParameters.end());

    std::cout << "Model parameters:\n";
    for(HString &par : allParameters) {
        par.replace("$", ":");
        par.replace("#", ".");
        std::cout << "  " << par.c_str() << "\n";
    }
}
<commit_msg>Removed excessive ouput from model executables<commit_after>/*-----------------------------------------------------------------------------

 Copyright 2017 Hopsan Group

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

 The full license is available in the file LICENSE.
 For details about the 'Hopsan Group' or information about Authors and
 Contributors see the HOPSANGROUP and AUTHORS files that are located in
 the Hopsan source code root directory.

-----------------------------------------------------------------------------*/

//!
//! @file   ModelUtilities.cpp
//! @author FluMeS
//! @date   2012-05-30
//!
//! @brief Contains model specific helpfunctions for CLI
//!
//$Id$

#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstring>

#include "exe_utilities.h"

#include "HopsanEssentials.h"

using namespace std;
using namespace hopsan;

void printWarningMessage(std::string msg)
{
    std::cout << "Warning: " << msg << "\n";
}

void printErrorMessage(std::string msg)
{
    std::cout << "Error: " << msg << "\n";
}

void splitStringOnDelimiter(const std::string &rString, const char delim, std::vector<std::string> &rSplitVector)
{
    rSplitVector.clear();
    string item;
    stringstream ss(rString);
    while(getline(ss, item, delim))
    {
        rSplitVector.push_back(item);
    }
}

void saveResults(ComponentSystem *pSys, const string &rFileName, const SaveResults howMany, const SaveDescriptions descriptions, string prefix, ofstream *pFile)
{
    bool doCloseFile=false;
    if (!pFile)
    {
        pFile = new ofstream;
        pFile->open(rFileName.c_str());
        if (!pFile->good())
        {
            printErrorMessage("Could not open: " + rFileName + " for writing!");
            delete pFile;
            return;
        }
        doCloseFile = true;
    }

    if (pSys)
    {
        // First save time vector for this system
        //! @todo alias a for time ? is that even posible
        if (howMany == Final)
        {
            *pFile << prefix.c_str() << "time,";
            if(descriptions == NameAliasUnit) {
                *pFile << ",s,";
            }
            *pFile << std::scientific << pSys->getTime() << endl;
        }
        else if (howMany == Full)
        {
            vector<double> *pLogTimeVector = pSys->getLogTimeVector();
            if (pLogTimeVector->size() > 0)
            {
                *pFile << prefix.c_str() << "time";
                if(descriptions == NameAliasUnit) {
                    *pFile << ",,s";
                }
                for (size_t t=0; t<pLogTimeVector->size(); ++t)
                {
                    *pFile << "," << std::scientific << (*pLogTimeVector)[t];
                }
                *pFile << endl;
            }
        }

        // Now save log data vectors for all subcomponents
        vector<HString> names = pSys->getSubComponentNames();
        for (size_t c=0; c<names.size(); ++c)
        {
            Component *pComp = pSys->getSubComponent(names[c]);
            if (pComp)
            {
                //cout << "comp: " << c << " of: " << names.size() << endl;
                vector<Port*> ports = pComp->getPortPtrVector();
                for (size_t p=0; p<ports.size(); ++p)
                {
                    //cout << "port: " << p << " of: " << ports.size() << endl;
                    Port *pPort = ports[p];
                    if (!pPort->isLoggingEnabled())
                    {
                        // Ignore ports that have logging disabled
                        continue;
                    }
                    const vector<NodeDataDescription> *pVars = pPort->getNodeDataDescriptions();
                    if (pVars)
                    {
                        for (size_t v=0; v<pVars->size(); ++v)
                        {
                            HString fullname = prefix.c_str() + pComp->getName() + "#" + pPort->getName() + "#" + pVars->at(v).name;

                            if (howMany == Final)
                            {
                                *pFile << fullname.c_str();
                                if(descriptions == NameAliasUnit) {
                                    *pFile << "," << pPort->getVariableAlias(v).c_str() << "," << pVars->at(v).unit.c_str();
                                }
                                *pFile << "," << std::scientific << pPort->readNode(v) << endl;
                            }
                            else if (howMany == Full)
                            {
                                // Only write something if data has been logged (skip ports that are not logged)
                                // We assume that the data vector has been cleared
                                if (pPort->getLogDataVectorPtr()->size() > 0)
                                {
                                    *pFile << fullname.c_str();
                                    if(descriptions == NameAliasUnit) {
                                        *pFile << "," << pPort->getVariableAlias(v).c_str() << "," << pVars->at(v).unit.c_str();
                                    }
                                    //! @todo what about time vector
                                    vector< vector<double> > *pLogData = pPort->getLogDataVectorPtr();
                                    for (size_t t=0; t<pSys->getNumActuallyLoggedSamples(); ++t)
                                    {
                                        *pFile << "," << std::scientific << (*pLogData)[t][v];
                                    }
                                    *pFile << endl;
                                }
                            }
                        }
                    }
                }

                // Recurse into subsystems
                if (pComp->isComponentSystem())
                {
                    saveResults(static_cast<ComponentSystem*>(pComp), rFileName, howMany, descriptions, prefix+pComp->getName().c_str()+"$", pFile);
                }
            }
        }
    }

    if (doCloseFile)
    {
        pFile->close();
        delete pFile;
    }
}

void transposeCSVresults(const std::string &rFileName)
{
    ifstream infile(rFileName.c_str());
    if ( infile.good() )
    {
        vector< vector<string> > linesVec;
        vector<string> lineVec;
        // Read all lines into string vectors
        while(!infile.eof())
        {
            string line;
            getline(infile, line);

            // Split on delimiter
            splitStringOnDelimiter(line, ',', lineVec);

            // Store line vector
            if (lineVec.size() > 0)
            {
                linesVec.push_back(lineVec);
                lineVec.clear();

                // Clear data but reserve memory for next run
                size_t n = lineVec.size();
                lineVec.clear();
                lineVec.reserve(n);
            }
        }
        infile.close();

        // Write back transposed
        ofstream ofile(rFileName.c_str());
        if ( ofile.good() )
        {
            size_t rows = linesVec.size();
            if (rows > 0)
            {
                // Assumes same nr cols on each row
                size_t cols = linesVec[0].size();
                for (size_t c=0; c<cols; ++c)
                {
                    ofile << linesVec[0][c];
                    //cout << linesVec[0][c];
                    for (size_t r=1; r<rows; ++r)
                    {
                        ofile << "," <<  linesVec[r][c];
                        //cout << ", " <<  linesVec[r][c];
                    }
                    ofile << endl;
                    //cout << endl;
                }
            }
        }
        ofile.close();
    }
}

//! @todo should we use CSV parser instead?
void importParameterValuesFromCSV(const std::string filePath, hopsan::ComponentSystem* pSystem)
{
    if (pSystem)
    {
        std::ifstream file;
        file.open(filePath.c_str());
        if ( file.is_open() )
        {
            std::vector<std::string> lineVec;
            std::string line;
            while ( file.good() )
            {
                getline(file, line);
                if (*line.begin() != '#')
                {
                    // Split on delimiter
                    splitStringOnDelimiter(line, ',', lineVec);

                    // Parse line vector
                    if (lineVec.size() == 2)
                    {
                        setParameter(lineVec[0], lineVec[1], pSystem);
                    }
                    else
                    {
                        // Print error for non-empty lines
                        if (lineVec.size() > 0)
                        {
                            printWarningMessage(string("Wrong line format: ") + line);
                        }
                    }
                }
            }
            file.close();
        }
        else
        {
            printErrorMessage(string("Could not open file: ")+filePath);
        }
    }
}

bool startsWith(std::string str, std::string match)
{
    if(str.find(match) == 0) {
        return true;
    }
    else {
        return false;
    }
}

bool endsWith(std::string str, std::string match)
{
    if (str.length() >= match.length()) {
        return (0 == str.compare (str.length() - match.length(), match.length(), match));
    } else {
        return false;
    }
}

bool setParameter(string &rParName, string &rParValue, ComponentSystem *pSystem)
{
    std::replace(rParName.begin(), rParName.end(), ':', '$');
    std::replace(rParName.begin(), rParName.end(), '.', '#');

    if(endsWith(rParName, "#y")) {
        rParName.erase(rParName.length()-2,2);
        rParName.append("#Value");
    };

    std::vector<std::string> vec2, nameVec, syshierarcy;
    string componentName, parameterName;
    splitStringOnDelimiter(rParName, '$', vec2);

    // Last of vec2 will contain the rest of the name filed (comp and param name)
    int i;
    for (i=0; i<int(vec2.size())-1; ++i) {
        syshierarcy.push_back(vec2[i]);
    }

    // Split last name part into comp and param name
    splitStringOnDelimiter(vec2[i], '#', nameVec);
    if (nameVec.size() == 2 || nameVec.size() == 3 ) {
        if (nameVec.size() == 2) {
            componentName = nameVec[0];
            parameterName = nameVec[1];
        }
        if (nameVec.size() == 3) {
            // Set component name and reset the parameter (startvalue) name
            componentName = nameVec[0];
            parameterName = nameVec[1]+"#"+nameVec[2];
        }

        // Dig down subsystem hiearchy
        ComponentSystem *pParentSys = pSystem;
        for (size_t s=1; s<syshierarcy.size(); ++s) {
            //! @todo what about first level (0), should we check that name is ok
            ComponentSystem *pSubSys = pParentSys->getSubComponentSystem(syshierarcy[s].c_str());
            if (!pSubSys) {
                printErrorMessage("Subsystem: "+syshierarcy[s]+" could not be found in parent system: "+pParentSys->getName().c_str());
                return false;
            }
            else {
                pParentSys = pSubSys;
            }
        }

        // Set the parameter value if component is found
        if (pParentSys) {
            Component *pComp = 0;
            // If syshierarcy is empty then we are setting a parameter in the top-level system
            if (syshierarcy.empty()) {
                pComp = pParentSys;
            }
            else {
                pComp = pParentSys->getSubComponent(componentName.c_str());
            }

            if (pComp) {
                bool ok = pComp->setParameterValue(parameterName.c_str(), rParValue.c_str());
                std::cout << "Setting parameter: " << pComp->getName().c_str() << "." << parameterName << " = " << rParValue << "\n";
                if (!ok) {
                    std::cout << "Failed!\n";
                    return false;
                }
            }
            else {
                printErrorMessage("No component: " + componentName + " in system: " + pParentSys->getName().c_str() );
                return false;
            }
        }
    }
    else {
        std::cout << "Unknown error!\n";
        return false;
    }

    return true;
}

void printHelpText(ComponentSystem *pSystem)
{
    std::cout << "This is a stand-alone executable Hopsan model.\n";
    std::cout << "Model name: " << pSystem->getName().c_str() << "\n";
    std::cout << "\n";
    std::cout << "Usage: " << pSystem->getName().c_str() << " [FLAG]\n";
    std::cout << "Usage: " << pSystem->getName().c_str() << " [OPTIONS]\n";
    std::cout << "\n";
    std::cout << "Flags:\n";
    std::cout << "  -h, --help            Display this help text\n";
    std::cout << "  -p, --parameters      List all tunable model parameters\n";
    std::cout << "\n";
    std::cout << "Options (cannot be used together with flags):\n";
    std::cout << "  start=[real]                Simulation start time\n";
    std::cout << "  step=[real]                 Simulation step size\n";
    std::cout << "  stop=[real]                 Simulation stop time\n";
    std::cout << "  samples=[integer]           Number of log samples\n";
    std::cout << "  results=full/final          Print full results or final results only\n";
    std::cout << "  samples=[integer]           Number of log samples\n";
    std::cout << "  transpose=true/false        Transpose to column-wise CSV file\n";
    std::cout << "  descriptions=all/namesonly  Print names, alias and unit or only name\n";
    std::cout << "  parameterfile=[filename]    Specify parameter input file\n";
    std::cout << "\n";
    std::cout << "Examples:\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " start=0 step=0.001 stop=3\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " samples=1000 transpose=true descriptions=namesonly\n";
    std::cout << "  >> " << pSystem->getName().c_str() << " engine.rpm=2000 results=final\n";
}

void appendParameters(ComponentSystem *pSystem, std::vector<HString> &allParameters, HString prefix="")
{
    std::vector<HString> sysParameters;
    pSystem->getParameterNames(sysParameters);
    for(HString &par : sysParameters) {
        std::string temp = prefix.c_str();
        temp.append(pSystem->getName().c_str());
        temp.append("#");
        temp.append(par.c_str());
        par = temp.c_str();
    }
    allParameters.insert(allParameters.end(), sysParameters.begin(), sysParameters.end());

    for(Component *pComp : pSystem->getSubComponents()) {
        if(pComp->isComponentSystem()) {
            prefix.append(pComp->getName().c_str());
            prefix.append("$");
            appendParameters(reinterpret_cast<ComponentSystem*>(pComp), allParameters);
        }
        std::vector<HString> compParameters;
        pComp->getParameterNames(compParameters);
        for(HString &par : compParameters) {
            std::string temp = prefix.c_str();
            temp.append(pComp->getName().c_str());
            temp.append("#");
            temp.append(par.c_str());
            par = temp.c_str();
        }
        allParameters.insert(allParameters.end(), compParameters.begin(), compParameters.end());
    }
}

void printParameters(ComponentSystem *pSystem)
{
    std::vector<HString> allParameters;
    appendParameters(pSystem, allParameters);
    std::sort(allParameters.begin(), allParameters.end());

    std::cout << "Model parameters:\n";
    for(HString &par : allParameters) {
        par.replace("$", ":");
        par.replace("#", ".");
        std::cout << "  " << par.c_str() << "\n";
    }
}
<|endoftext|>


<commit_before>#pragma once

#include <memory>

#include "Runtime/IFactory.hpp"
#include "Runtime/IObj.hpp"
#include "Runtime/IObjectStore.hpp"
#include "Runtime/IVParamObj.hpp"
#include "Runtime/RetroTypes.hpp"

namespace urde {
class IObjectStore;

/** Shared data-structure for CToken references, analogous to std::shared_ptr */
class CObjectReference {
  friend class CSimplePool;
  friend class CToken;

  u16 x0_refCount = 0;
  u16 x2_lockCount = 0;
  bool x3_loading = false; /* Rightmost bit of lockCount */
  SObjectTag x4_objTag;
  IObjectStore* xC_objectStore = nullptr;
  std::unique_ptr<IObj> x10_object;
  CVParamTransfer x14_params;

  /** Mechanism by which CToken decrements 1st ref-count, indicating CToken invalidation or reset.
   *  Reaching 0 indicates the CToken should delete the CObjectReference */
  u16 RemoveReference();

  CObjectReference(IObjectStore& objStore, std::unique_ptr<IObj>&& obj, const SObjectTag& objTag,
                   CVParamTransfer buildParams);
  CObjectReference(std::unique_ptr<IObj>&& obj);

  /** Indicates an asynchronous load transaction has been submitted and is not yet finished */
  bool IsLoading() const { return x3_loading; }

  /** Indicates an asynchronous load transaction has finished and object is completely loaded */
  bool IsLoaded() const { return x10_object.operator bool(); }

  /** Decrements 2nd ref-count, performing unload or async-load-cancel if 0 reached */
  void Unlock();

  /** Increments 2nd ref-count, performing async-factory-load if needed */
  void Lock();

  void CancelLoad();

  /** Pointer-synchronized object-destructor, another building Lock cycle may be performed after */
  void Unload();

  /** Synchronous object-fetch, guaranteed to return complete object on-demand, blocking build if not ready */
  IObj* GetObject();

public:
  const SObjectTag& GetObjectTag() const { return x4_objTag; }

  ~CObjectReference();
};

/** Counted meta-object, reference-counting against a shared CObjectReference
 *  This class is analogous to std::shared_ptr and C++11 rvalues have been implemented accordingly
 *  (default/empty constructor, move constructor/assign) */
class CToken {
  friend class CModel;
  friend class CSimplePool;

  CObjectReference* x0_objRef = nullptr;
  bool x4_lockHeld = false;

  void RemoveRef();

  CToken(CObjectReference* obj);

public:
  /* Added to test for non-null state */
  operator bool() const { return x0_objRef != nullptr; }
  void Unlock();
  void Lock();
  bool IsLocked() const { return x4_lockHeld; }
  bool IsLoaded() const;
  IObj* GetObj();
  const IObj* GetObj() const { return const_cast<CToken*>(this)->GetObj(); }
  CToken& operator=(const CToken& other);
  CToken& operator=(CToken&& other);
  CToken() = default;
  CToken(const CToken& other);
  CToken(CToken&& other) noexcept;
  CToken(IObj* obj);
  CToken(std::unique_ptr<IObj>&& obj);
  const SObjectTag* GetObjectTag() const;
  ~CToken();
};

template <class T>
class TToken : public CToken {
public:
  static std::unique_ptr<TObjOwnerDerivedFromIObj<T>> GetIObjObjectFor(std::unique_ptr<T>&& obj) {
    return TObjOwnerDerivedFromIObj<T>::GetNewDerivedObject(std::move(obj));
  }
  TToken() = default;
  TToken(const CToken& other) : CToken(other) {}
  TToken(CToken&& other) : CToken(std::move(other)) {}
  TToken(std::unique_ptr<T>&& obj) : CToken(GetIObjObjectFor(std::move(obj))) {}
  TToken& operator=(std::unique_ptr<T>&& obj) {
    *this = CToken(GetIObjObjectFor(std::move(obj)));
    return this;
  }
  T* GetObj() {
    TObjOwnerDerivedFromIObj<T>* owner = static_cast<TObjOwnerDerivedFromIObj<T>*>(CToken::GetObj());
    if (owner)
      return owner->GetObj();
    return nullptr;
  }
  const T* GetObj() const { return const_cast<TToken<T>*>(this)->GetObj(); }
  T* operator->() { return GetObj(); }
  const T* operator->() const { return GetObj(); }
  T& operator*() { return *GetObj(); }
  const T& operator*() const { return *GetObj(); }
};

template <class T>
class TCachedToken : public TToken<T> {
protected:
  T* m_obj = nullptr;

public:
  TCachedToken() = default;
  TCachedToken(const CToken& other) : TToken<T>(other) {}
  TCachedToken(CToken&& other) : TToken<T>(std::move(other)) {}
  T* GetObj() {
    if (!m_obj)
      m_obj = TToken<T>::GetObj();
    return m_obj;
  }
  const T* GetObj() const { return const_cast<TCachedToken<T>*>(this)->GetObj(); }
  T* operator->() { return GetObj(); }
  const T* operator->() const { return GetObj(); }
  void Unlock() {
    TToken<T>::Unlock();
    m_obj = nullptr;
  }

  TCachedToken& operator=(const TCachedToken& other) {
    CToken::operator=(other);
    m_obj = nullptr;
    return *this;
  }
  TCachedToken& operator=(const CToken& other) {
    CToken::operator=(other);
    m_obj = nullptr;
    return *this;
  }
};

template <class T>
class TLockedToken : public TCachedToken<T> {
public:
  TLockedToken() = default;
  TLockedToken(const TLockedToken& other) : TCachedToken<T>(other) { CToken::Lock(); }
  TLockedToken& operator=(const TLockedToken& other) {
    CToken oldTok = std::move(*this);
    TCachedToken<T>::operator=(other);
    CToken::Lock();
    return *this;
  }
  TLockedToken(const CToken& other) : TCachedToken<T>(other) { CToken::Lock(); }
  TLockedToken& operator=(const CToken& other) {
    CToken oldTok = std::move(*this);
    TCachedToken<T>::operator=(other);
    CToken::Lock();
    return *this;
  }
  TLockedToken(CToken&& other) {
    CToken oldTok = std::move(*this);
    *this = TCachedToken<T>(std::move(other));
    CToken::Lock();
  }
};

} // namespace urde
<commit_msg>CToken: Make operator bool explicit<commit_after>#pragma once

#include <memory>

#include "Runtime/IFactory.hpp"
#include "Runtime/IObj.hpp"
#include "Runtime/IObjectStore.hpp"
#include "Runtime/IVParamObj.hpp"
#include "Runtime/RetroTypes.hpp"

namespace urde {
class IObjectStore;

/** Shared data-structure for CToken references, analogous to std::shared_ptr */
class CObjectReference {
  friend class CSimplePool;
  friend class CToken;

  u16 x0_refCount = 0;
  u16 x2_lockCount = 0;
  bool x3_loading = false; /* Rightmost bit of lockCount */
  SObjectTag x4_objTag;
  IObjectStore* xC_objectStore = nullptr;
  std::unique_ptr<IObj> x10_object;
  CVParamTransfer x14_params;

  /** Mechanism by which CToken decrements 1st ref-count, indicating CToken invalidation or reset.
   *  Reaching 0 indicates the CToken should delete the CObjectReference */
  u16 RemoveReference();

  CObjectReference(IObjectStore& objStore, std::unique_ptr<IObj>&& obj, const SObjectTag& objTag,
                   CVParamTransfer buildParams);
  CObjectReference(std::unique_ptr<IObj>&& obj);

  /** Indicates an asynchronous load transaction has been submitted and is not yet finished */
  bool IsLoading() const { return x3_loading; }

  /** Indicates an asynchronous load transaction has finished and object is completely loaded */
  bool IsLoaded() const { return x10_object.operator bool(); }

  /** Decrements 2nd ref-count, performing unload or async-load-cancel if 0 reached */
  void Unlock();

  /** Increments 2nd ref-count, performing async-factory-load if needed */
  void Lock();

  void CancelLoad();

  /** Pointer-synchronized object-destructor, another building Lock cycle may be performed after */
  void Unload();

  /** Synchronous object-fetch, guaranteed to return complete object on-demand, blocking build if not ready */
  IObj* GetObject();

public:
  const SObjectTag& GetObjectTag() const { return x4_objTag; }

  ~CObjectReference();
};

/** Counted meta-object, reference-counting against a shared CObjectReference
 *  This class is analogous to std::shared_ptr and C++11 rvalues have been implemented accordingly
 *  (default/empty constructor, move constructor/assign) */
class CToken {
  friend class CModel;
  friend class CSimplePool;

  CObjectReference* x0_objRef = nullptr;
  bool x4_lockHeld = false;

  void RemoveRef();

  CToken(CObjectReference* obj);

public:
  /* Added to test for non-null state */
  explicit operator bool() const { return x0_objRef != nullptr; }
  void Unlock();
  void Lock();
  bool IsLocked() const { return x4_lockHeld; }
  bool IsLoaded() const;
  IObj* GetObj();
  const IObj* GetObj() const { return const_cast<CToken*>(this)->GetObj(); }
  CToken& operator=(const CToken& other);
  CToken& operator=(CToken&& other);
  CToken() = default;
  CToken(const CToken& other);
  CToken(CToken&& other) noexcept;
  CToken(IObj* obj);
  CToken(std::unique_ptr<IObj>&& obj);
  const SObjectTag* GetObjectTag() const;
  ~CToken();
};

template <class T>
class TToken : public CToken {
public:
  static std::unique_ptr<TObjOwnerDerivedFromIObj<T>> GetIObjObjectFor(std::unique_ptr<T>&& obj) {
    return TObjOwnerDerivedFromIObj<T>::GetNewDerivedObject(std::move(obj));
  }
  TToken() = default;
  TToken(const CToken& other) : CToken(other) {}
  TToken(CToken&& other) : CToken(std::move(other)) {}
  TToken(std::unique_ptr<T>&& obj) : CToken(GetIObjObjectFor(std::move(obj))) {}
  TToken& operator=(std::unique_ptr<T>&& obj) {
    *this = CToken(GetIObjObjectFor(std::move(obj)));
    return this;
  }
  T* GetObj() {
    TObjOwnerDerivedFromIObj<T>* owner = static_cast<TObjOwnerDerivedFromIObj<T>*>(CToken::GetObj());
    if (owner)
      return owner->GetObj();
    return nullptr;
  }
  const T* GetObj() const { return const_cast<TToken<T>*>(this)->GetObj(); }
  T* operator->() { return GetObj(); }
  const T* operator->() const { return GetObj(); }
  T& operator*() { return *GetObj(); }
  const T& operator*() const { return *GetObj(); }
};

template <class T>
class TCachedToken : public TToken<T> {
protected:
  T* m_obj = nullptr;

public:
  TCachedToken() = default;
  TCachedToken(const CToken& other) : TToken<T>(other) {}
  TCachedToken(CToken&& other) : TToken<T>(std::move(other)) {}
  T* GetObj() {
    if (!m_obj)
      m_obj = TToken<T>::GetObj();
    return m_obj;
  }
  const T* GetObj() const { return const_cast<TCachedToken<T>*>(this)->GetObj(); }
  T* operator->() { return GetObj(); }
  const T* operator->() const { return GetObj(); }
  void Unlock() {
    TToken<T>::Unlock();
    m_obj = nullptr;
  }

  TCachedToken& operator=(const TCachedToken& other) {
    CToken::operator=(other);
    m_obj = nullptr;
    return *this;
  }
  TCachedToken& operator=(const CToken& other) {
    CToken::operator=(other);
    m_obj = nullptr;
    return *this;
  }
};

template <class T>
class TLockedToken : public TCachedToken<T> {
public:
  TLockedToken() = default;
  TLockedToken(const TLockedToken& other) : TCachedToken<T>(other) { CToken::Lock(); }
  TLockedToken& operator=(const TLockedToken& other) {
    CToken oldTok = std::move(*this);
    TCachedToken<T>::operator=(other);
    CToken::Lock();
    return *this;
  }
  TLockedToken(const CToken& other) : TCachedToken<T>(other) { CToken::Lock(); }
  TLockedToken& operator=(const CToken& other) {
    CToken oldTok = std::move(*this);
    TCachedToken<T>::operator=(other);
    CToken::Lock();
    return *this;
  }
  TLockedToken(CToken&& other) {
    CToken oldTok = std::move(*this);
    *this = TCachedToken<T>(std::move(other));
    CToken::Lock();
  }
};

} // namespace urde
<|endoftext|>


<commit_before>/**
 * Following the amd Intro OpenCL Tutorial.
 * Some things are deprecated and needed change.
 * Comments by me.
 */

#include <utility>
#include <CL/cl.hpp>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <iterator>
#include "imloader.h"

/**
 * Checks that err is 0, otherwise exits with debug information. Since this is
 * the lazy solution where we don't check which error we got, CL_SUCCESS
 * wouldn't give any extra information. Should be 0 anyway, and this is usable
 * without a definition of that aswell. If you ever see this output, you should
 * probably put in actual checks in the relevant location.
 */
#define CHECK(err)                                                             \
  if (err) {                                                                   \
    std::cerr << "UNEXPECTED ERROR " << err << " at " << __FILE__ << ":"       \
              << __LINE__ << " in function " << __func__ << std::endl;         \
    exit(-1);                                                                  \
  }

/**
 * If build fails, print the compilation output and exit.
 */
void checkBuildErr(cl_int err, cl::Device *d, cl::Program *p) {
  if (err != CL_SUCCESS) {
    std::cerr << "ERROR: Building OpenCL program failed!\n";
    std::string log;
    p->getBuildInfo(*d, (cl_program_build_info)CL_PROGRAM_BUILD_LOG, &log);
    std::cerr << log << std::endl;
    exit(-2);
  }
}

int main() {
  cl_int err;

  // Since we can't specify which platform we actually want (in this program,
  // no interaction), this won't help with the helloworld. However, print the
  // information.
  {
    // Get platforms available
    std::vector<cl::Platform> platformList;
    err = cl::Platform::get(&platformList);
    CHECK(err);
    if (platformList.size() == 0) {
      std::cerr << "Found no platforms" << std::endl;
      exit(-1);
    }

    // Print info about all available platforms
    for (cl::Platform &p : platformList) {
      std::cerr << "Platform: " << p.getInfo<CL_PLATFORM_VENDOR>() << "\n";
      std::cerr << "Profile: " << p.getInfo<CL_PLATFORM_PROFILE>() << "\n";
      std::cerr << "Version: " << p.getInfo<CL_PLATFORM_VERSION>() << "\n";
      std::cerr << "Name: " << p.getInfo<CL_PLATFORM_NAME>() << "\n";
      std::cerr << "Extensions: " << p.getInfo<CL_PLATFORM_EXTENSIONS>()
                << "\n";
      std::cerr << std::endl;
    }
  }

  // Create the context. Iterates through the platforms and picks the first
  // one with a GPU, then creates a context from that.
  cl::Context context(CL_DEVICE_TYPE_GPU, NULL, NULL, NULL, &err);
  if (err) {
    // Fall back to CPU
    context = cl::Context(CL_DEVICE_TYPE_CPU, NULL, NULL, NULL, &err);
  }
  CHECK(err);

  // Get device
  std::vector<cl::Device> devices;
  devices = context.getInfo<CL_CONTEXT_DEVICES>();
  if (devices.size() == 0) {
    std::cerr << "Found no devices" << std::endl;
  }

  // Print info about all available devices
  for (cl::Device &dev : devices) {
    std::cerr << "Name: " << dev.getInfo<CL_DEVICE_NAME>() << "\n";
    std::cerr << "Clock freq: " << dev.getInfo<CL_DEVICE_MAX_CLOCK_FREQUENCY>()
              << "\n";
    std::cerr << "global mem: " << dev.getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>()
              << "\n";
    std::cerr << "global mem cache: "
              << dev.getInfo<CL_DEVICE_GLOBAL_MEM_CACHE_SIZE>() << "\n";
    std::cerr << "local mem: " << dev.getInfo<CL_DEVICE_LOCAL_MEM_SIZE>()
              << "\n";
    std::cerr << "max workgroup size: "
              << dev.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>() << "\n";
    std::cerr << std::endl;
  }

  iml::Image img("foo.png");
  if (!img) {
    std::cerr << "Image loaded incorrectly" << std::endl;
    exit(-1);
  }
  cl::Image2D img_in(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                     {CL_RGBA, CL_UNSIGNED_INT8}, img.width(), img.height(), 0,
                     img.data, &err);
  if (err) {
    switch (err) {
    case CL_INVALID_CONTEXT:
      std::cerr << "CL_INVALID_CONTEXT" << std::endl;
      break;
    case CL_INVALID_VALUE:
      std::cerr << "CL_INVALID_VALUE" << std::endl;
      break;
    case CL_INVALID_IMAGE_SIZE:
      std::cerr << "CL_INVALID_IMAGE_SIZE" << std::endl;
      break;
    case CL_INVALID_HOST_PTR:
      std::cerr << "CL_INVALID_HOST_PTR" << std::endl;
      break;
    case CL_IMAGE_FORMAT_NOT_SUPPORTED:
      std::cerr << "CL_IMAGE_FORMAT_NOT_SUPPORTED" << std::endl;
      break;
    case CL_MEM_OBJECT_ALLOCATION_FAILURE:
      std::cerr << "CL_MEM_OBJECT_ALLOCATION_FAILURE" << std::endl;
      break;
    case CL_INVALID_OPERATION:
      std::cerr << "CL_INVALID_OPERATION: no device supporting image"
                << std::endl;
      break;
    }
    CHECK(err);
  }
  cl::Image2D img_out(context, CL_MEM_WRITE_ONLY, {CL_RGBA, CL_UNSIGNED_INT8},
                      img.width(), img.height(), 0, 0, &err);
  CHECK(err);

  // Load kernel source
  std::ifstream file("kernel.cl");
  if (!file) {
    std::cerr << "Kernel source file not opened correctly" << std::endl;
    exit(-1);
  }
  std::string prog{std::istreambuf_iterator<char>(file),
                   std::istreambuf_iterator<char>()};

  // Create program
  cl::Program::Sources source(1,
                              std::make_pair(prog.c_str(), prog.length() + 1));
  cl::Program program(context, source);
  err = program.build(devices, "");
  checkBuildErr(err, &devices[0], &program);

  // Create kernel
  cl::Kernel kernel(program, "invert", &err);
  CHECK(err);
  err = kernel.setArg(0, img_in);
  err = kernel.setArg(1, img_out);
  if (err == CL_INVALID_MEM_OBJECT) {
    std::cerr << "kernel argument setting failed with CL_INVALID_MEM_OBJECT"
              << std::endl;
  }
  CHECK(err);

  // Queue kernel
  cl::CommandQueue queue(context, devices[0], 0, &err);
  CHECK(err);
  cl::Event event;
  err = queue.enqueueNDRangeKernel(kernel, cl::NullRange,
                                   cl::NDRange(img.width(), img.height()),
                                   cl::NDRange(1, 1), NULL, &event);
  CHECK(err);

  // Queue reading result
  event.wait();
  cl::size_t<3> origin;
  origin[0] = 0;
  origin[1] = 0;
  origin[2] = 0;
  cl::size_t<3> region;
  region[0] = img.width();
  region[1] = img.height();
  region[2] = 1;
  err = queue.enqueueReadImage(img_out, CL_TRUE, origin, region, 0, 0,
                               (void *)img.data);
  CHECK(err);

  bool written = iml::writepng("out.png", &img);
  if (!written) {
    std::cerr << "Couldn't write file" << std::endl;
  }
}
<commit_msg>didn't really test anything anyway<commit_after>/**
 * Following the amd Intro OpenCL Tutorial.
 * Some things are deprecated and needed change.
 * Comments by me.
 */

#include <utility>
#include <CL/cl.hpp>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <iterator>
#include "imloader.h"

/**
 * Checks that err is 0, otherwise exits with debug information. Since this is
 * the lazy solution where we don't check which error we got, CL_SUCCESS
 * wouldn't give any extra information. Should be 0 anyway, and this is usable
 * without a definition of that aswell. If you ever see this output, you should
 * probably put in actual checks in the relevant location.
 */
#define CHECK(err)                                                             \
  if (err) {                                                                   \
    std::cerr << "UNEXPECTED ERROR " << err << " at " << __FILE__ << ":"       \
              << __LINE__ << " in function " << __func__ << std::endl;         \
    exit(-1);                                                                  \
  }

/**
 * If build fails, print the compilation output and exit.
 */
void checkBuildErr(cl_int err, cl::Device *d, cl::Program *p) {
  if (err != CL_SUCCESS) {
    std::cerr << "ERROR: Building OpenCL program failed!\n";
    std::string log;
    p->getBuildInfo(*d, (cl_program_build_info)CL_PROGRAM_BUILD_LOG, &log);
    std::cerr << log << std::endl;
    exit(-2);
  }
}

int main() {
  cl_int err;

  // Since we can't specify which platform we actually want (in this program,
  // no interaction), this won't help with the helloworld. However, print the
  // information.
  {
    // Get platforms available
    std::vector<cl::Platform> platformList;
    err = cl::Platform::get(&platformList);
    CHECK(err);
    if (platformList.size() == 0) {
      std::cerr << "Found no platforms" << std::endl;
      exit(-1);
    }

    // Print info about all available platforms
    for (cl::Platform &p : platformList) {
      std::cerr << "Platform: " << p.getInfo<CL_PLATFORM_VENDOR>() << "\n";
      std::cerr << "Profile: " << p.getInfo<CL_PLATFORM_PROFILE>() << "\n";
      std::cerr << "Version: " << p.getInfo<CL_PLATFORM_VERSION>() << "\n";
      std::cerr << "Name: " << p.getInfo<CL_PLATFORM_NAME>() << "\n";
      std::cerr << "Extensions: " << p.getInfo<CL_PLATFORM_EXTENSIONS>()
                << "\n";
      std::cerr << std::endl;
    }
  }

  // Create the context. Iterates through the platforms and picks the first
  // one with a GPU, then creates a context from that.
  cl::Context context(CL_DEVICE_TYPE_GPU, NULL, NULL, NULL, &err);
  if (err) {
    // Fall back to CPU
    context = cl::Context(CL_DEVICE_TYPE_CPU, NULL, NULL, NULL, &err);
  }
  CHECK(err);

  // Get device
  std::vector<cl::Device> devices;
  devices = context.getInfo<CL_CONTEXT_DEVICES>();
  if (devices.size() == 0) {
    std::cerr << "Found no devices" << std::endl;
  }

  // Print info about all available devices
  for (cl::Device &dev : devices) {
    std::cerr << "Name: " << dev.getInfo<CL_DEVICE_NAME>() << "\n";
    std::cerr << "Clock freq: " << dev.getInfo<CL_DEVICE_MAX_CLOCK_FREQUENCY>()
              << "\n";
    std::cerr << "global mem: " << dev.getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>()
              << "\n";
    std::cerr << "global mem cache: "
              << dev.getInfo<CL_DEVICE_GLOBAL_MEM_CACHE_SIZE>() << "\n";
    std::cerr << "local mem: " << dev.getInfo<CL_DEVICE_LOCAL_MEM_SIZE>()
              << "\n";
    std::cerr << "max workgroup size: "
              << dev.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>() << "\n";
    std::cerr << std::endl;
  }

  iml::Image img("foo.png");
  if (!img) {
    std::cerr << "Image loaded incorrectly" << std::endl;
    exit(-1);
  }
  cl::Image2D img_in(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                     {CL_RGBA, CL_UNSIGNED_INT8}, img.width(), img.height(), 0,
                     img.data, &err);
  if (err) {
    switch (err) {
    case CL_INVALID_CONTEXT:
      std::cerr << "CL_INVALID_CONTEXT" << std::endl;
      break;
    case CL_INVALID_VALUE:
      std::cerr << "CL_INVALID_VALUE" << std::endl;
      break;
    case CL_INVALID_IMAGE_SIZE:
      std::cerr << "CL_INVALID_IMAGE_SIZE" << std::endl;
      break;
    case CL_INVALID_HOST_PTR:
      std::cerr << "CL_INVALID_HOST_PTR" << std::endl;
      break;
    case CL_IMAGE_FORMAT_NOT_SUPPORTED:
      std::cerr << "CL_IMAGE_FORMAT_NOT_SUPPORTED" << std::endl;
      break;
    case CL_MEM_OBJECT_ALLOCATION_FAILURE:
      std::cerr << "CL_MEM_OBJECT_ALLOCATION_FAILURE" << std::endl;
      break;
    case CL_INVALID_OPERATION:
      std::cerr << "CL_INVALID_OPERATION: no device supporting image"
                << std::endl;
      break;
    }
    CHECK(err);
  }
  cl::Image2D img_out(context, CL_MEM_WRITE_ONLY, {CL_RGBA, CL_UNSIGNED_INT8},
                      img.width(), img.height(), 0, 0, &err);
  CHECK(err);

  // Load kernel source
  std::ifstream file("kernel.cl");
  if (!file) {
    std::cerr << "Kernel source file not opened correctly" << std::endl;
    exit(-1);
  }
  std::string prog{std::istreambuf_iterator<char>(file),
                   std::istreambuf_iterator<char>()};

  // Create program
  cl::Program::Sources source(1,
                              std::make_pair(prog.c_str(), prog.length() + 1));
  cl::Program program(context, source);
  err = program.build(devices, "");
  checkBuildErr(err, &devices[0], &program);

  // Create kernel
  cl::Kernel kernel(program, "invert", &err);
  CHECK(err);
  err = kernel.setArg(0, img_in);
  err = kernel.setArg(1, img_out);
  CHECK(err);

  // Queue kernel
  cl::CommandQueue queue(context, devices[0], 0, &err);
  CHECK(err);
  cl::Event event;
  err = queue.enqueueNDRangeKernel(kernel, cl::NullRange,
                                   cl::NDRange(img.width(), img.height()),
                                   cl::NDRange(1, 1), NULL, &event);
  CHECK(err);

  // Queue reading result
  event.wait();
  cl::size_t<3> origin;
  origin[0] = 0;
  origin[1] = 0;
  origin[2] = 0;
  cl::size_t<3> region;
  region[0] = img.width();
  region[1] = img.height();
  region[2] = 1;
  err = queue.enqueueReadImage(img_out, CL_TRUE, origin, region, 0, 0,
                               (void *)img.data);
  CHECK(err);

  bool written = iml::writepng("out.png", &img);
  if (!written) {
    std::cerr << "Couldn't write file" << std::endl;
  }
}
<|endoftext|>


<commit_before>///
/// @file  LoadBalancer.cpp
/// @brief The LoadBalancer assigns work to the individual threads
///        in the computation of the special leaves in the
///        Lagarias-Miller-Odlyzko and Deleglise-Rivat prime
///        counting algorithms.
///
///        Simply parallelizing the computation of the special
///        leaves in the Lagarias-Miller-Odlyzko algorithm by
///        subdividing the sieve interval by the number of threads
///        into equally sized subintervals does not scale because
///        the distribution of the special leaves is highly skewed
///        and most special leaves are in the first few segments
///        whereas later on there are very few special leaves.
///
///        This LoadBalancer gradually increases the number of
///        segments to sieve as long the expected runtime of the
///        sieve distance is smaller than the expected finish time
///        of the algorithm. Near the end the LoadBalancer will
///        gradually decrease the number of segments to sieve in
///        order to prevent that 1 thread will run much longer
///        than all the other threads.
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <LoadBalancer.hpp>
#include <primecount-internal.hpp>
#include <S2Status.hpp>
#include <Sieve.hpp>
#include <imath.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <calculator.hpp>
#include <json.hpp>

#include <stdint.h>
#include <cmath>
#include <string>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;

namespace primecount {

int LoadBalancer::get_threads(int threads) const
{
  if (is_resume(copy_, "S2_hard", x_, y_, z_))
  {
    double percent = copy_["S2_hard"]["percent"];
    int backup_threads = copy_["S2_hard"]["threads"];
    threads = max(threads, backup_threads);
    print_resume(percent, x_);
  }

  return threads;
}

/// backup thread
void LoadBalancer::backup(int threads,
                          int thread_id,
                          int64_t low,
                          int64_t segments,
                          int64_t segment_size)
{
  double percent = status_.getPercent(low_, z_, s2_total_, s2_approx_);
  double seconds = get_wtime() - backup_time_;

  string tid = "thread" + to_string(thread_id);

  json_["S2_hard"]["threads"] = threads;
  json_["S2_hard"]["low"] = low_;
  json_["S2_hard"]["segments"] = segments_;
  json_["S2_hard"]["segment_size"] = segment_size_;
  json_["S2_hard"]["s2_hard"] = to_string(s2_total_);
  json_["S2_hard"]["percent"] = percent;
  json_["S2_hard"]["seconds"] = get_wtime() - time_;
  json_["S2_hard"][tid]["low"] = low;
  json_["S2_hard"][tid]["segments"] = segments;
  json_["S2_hard"][tid]["segment_size"] = segment_size;

  if (seconds > 60)
  {
    backup_time_ = get_wtime();
    store_backup(json_);
  }
}

/// backup result
void LoadBalancer::backup()
{
  json_.erase("S2_hard");

  json_["S2_hard"]["x"] = to_string(x_);
  json_["S2_hard"]["y"] = y_;
  json_["S2_hard"]["z"] = z_;
  json_["S2_hard"]["s2_hard"] = to_string(s2_total_);
  json_["S2_hard"]["percent"] = 100;
  json_["S2_hard"]["seconds"] = get_wtime() - time_;

  store_backup(json_);
}

/// resume thread
bool LoadBalancer::resume(int thread_id,
                          int64_t& low,
                          int64_t& segments,
                          int64_t& segment_size)
{
  if (is_resume(copy_, "S2_hard", thread_id, x_, y_, z_))
  {
    string tid = "thread" + to_string(thread_id);
    low = copy_["S2_hard"][tid]["low"];
    segments = copy_["S2_hard"][tid]["segments"];
    segment_size = copy_["S2_hard"][tid]["segment_size"];
    return true;
  }

  return false;
}

// resume result
bool LoadBalancer::resume(maxint_t& s2_hard, double& time) const
{
  if (is_resume(copy_, "S2_hard", x_, y_, z_) &&
      copy_["S2_hard"].count("low") == 0)
  {
    double percent = copy_["S2_hard"]["percent"];
    double seconds = copy_["S2_hard"]["seconds"];

    s2_hard = calculator::eval<maxint_t>(copy_["S2_hard"]["s2_hard"]);
    time = get_wtime() - seconds;
    print_resume(percent, x_);
    return true;
  }

  return false;
}

LoadBalancer::LoadBalancer(maxint_t x,
                           int64_t y,
                           int64_t z,
                           maxint_t s2_approx) :
  low_(0),
  max_low_(0),
  x_(x),
  y_(y),
  z_(z),
  segments_(1),
  s2_total_(0),
  s2_approx_(s2_approx),
  time_(get_wtime()),
  backup_time_(get_wtime()),
  status_(x),
  json_(load_backup())
{
  // Read only json copy that is accessed
  // in parallel by multiple threads
  copy_ = json_;

  if (is_resume(json_, "S2_hard", x_, y_, z_))
  {
    double seconds = copy_["S2_hard"]["seconds"];
    s2_total_ = calculator::eval<maxint_t>(copy_["S2_hard"]["s2_hard"]);
    time_ = get_wtime() - seconds;

    if (json_["S2_hard"].count("low"))
    {
      low_ = copy_["S2_hard"]["low"];
      segments_ = copy_["S2_hard"]["segments"];
      segment_size_ = copy_["S2_hard"]["segment_size"];
    }
  }
  else
  {
    json_.erase("S2_hard");
    json_["S2_hard"]["x"] = to_string(x_);
    json_["S2_hard"]["y"] = y_;
    json_["S2_hard"]["z"] = z_;
  }

  init_size();
  maxint_t x16 = iroot<6>(x);
  double alpha = get_alpha(x, y);
  smallest_hard_leaf_ = (int64_t) (x / (y * sqrt(alpha) * x16));
}

void LoadBalancer::init_size()
{
  // start with a tiny segment_size as most
  // special leaves are in the first few segments
  // and we need to ensure that all threads are
  // assigned an equal amount of work
  int64_t sqrtz = isqrt(z_);
  int64_t log = ilog(sqrtz);
  log = max(log, 1);
  segment_size_ = sqrtz / log;

  int64_t min_size = 1 << 9;
  segment_size_ = max(segment_size_, min_size);
  segment_size_ = Sieve::get_segment_size(segment_size_);

  // try to use a segment size that fits exactly
  // into the CPUs L1 data cache
  int64_t l1_dcache_size = 1 << 15;
  max_size_ = l1_dcache_size * 30;
  max_size_ = max(max_size_, sqrtz);
  max_size_ = Sieve::get_segment_size(max_size_);
}

maxint_t LoadBalancer::get_result() const
{
  return s2_total_;
}

double LoadBalancer::get_time() const
{
  return time_;
}

bool LoadBalancer::get_work(int threads,
                            int thread_id,
                            int64_t* low,
                            int64_t* segments,
                            int64_t* segment_size,
                            maxint_t s2,
                            Runtime& runtime)
{
  #pragma omp critical (LoadBalancer)
  {
    s2_total_ += s2;

    update(low, segments, runtime);

    *low = low_;
    *segments = segments_;
    *segment_size = segment_size_;

    low_ += segments_ * segment_size_;

    backup(threads, thread_id, *low, *segments, *segment_size);

    if (is_print())
      status_.print(s2_total_, s2_approx_);
  }

  return *low <= z_;
}

void LoadBalancer::update(int64_t* low,
                          int64_t* segments,
                          Runtime& runtime)
{
  if (*low > max_low_)
  {
    max_low_ = *low;
    segments_ = *segments;

    if (segment_size_ < max_size_)
      segment_size_ = min(segment_size_ * 2, max_size_);
    else
    {
      double next = get_next(runtime);
      next = in_between(0.25, next, 2.0);
      next *= segments_;
      next = max(1.0, next);
      segments_ = (int64_t) next;
    }
  }

  auto high = low_ + segments_ * segment_size_;

  // Most hard special leaves are located just past
  // smallest_hard_leaf_. In order to prevent assigning
  // the bulk of work to a single thread we reduce
  // the number of segments to a minimum.
  //
  if (smallest_hard_leaf_ >= low_ &&
      smallest_hard_leaf_ <= high)
  {
    segments_ = 1;
  }
}

double LoadBalancer::get_next(Runtime& runtime) const
{
  double min_secs = runtime.init * 10;
  double run_secs = runtime.secs;

  min_secs = max(min_secs, 0.01);
  run_secs = max(run_secs, min_secs / 10);

  double rem = remaining_secs();
  double threshold = rem / 4;
  threshold = max(threshold, min_secs);

  return threshold / run_secs;
}

/// Remaining seconds till finished
double LoadBalancer::remaining_secs() const
{
  double percent = status_.getPercent(low_, z_, s2_total_, s2_approx_);
  percent = in_between(20, percent, 100);

  double total_secs = get_wtime() - time_;
  double secs = total_secs * (100 / percent) - total_secs;
  return secs;
}

} // namespace
<commit_msg>Refactor<commit_after>///
/// @file  LoadBalancer.cpp
/// @brief The LoadBalancer assigns work to the individual threads
///        in the computation of the special leaves in the
///        Lagarias-Miller-Odlyzko and Deleglise-Rivat prime
///        counting algorithms.
///
///        Simply parallelizing the computation of the special
///        leaves in the Lagarias-Miller-Odlyzko algorithm by
///        subdividing the sieve interval by the number of threads
///        into equally sized subintervals does not scale because
///        the distribution of the special leaves is highly skewed
///        and most special leaves are in the first few segments
///        whereas later on there are very few special leaves.
///
///        This LoadBalancer gradually increases the number of
///        segments to sieve as long the expected runtime of the
///        sieve distance is smaller than the expected finish time
///        of the algorithm. Near the end the LoadBalancer will
///        gradually decrease the number of segments to sieve in
///        order to prevent that 1 thread will run much longer
///        than all the other threads.
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <LoadBalancer.hpp>
#include <primecount-internal.hpp>
#include <S2Status.hpp>
#include <Sieve.hpp>
#include <imath.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <calculator.hpp>
#include <json.hpp>

#include <stdint.h>
#include <cmath>
#include <string>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;

namespace primecount {

int LoadBalancer::get_threads(int threads) const
{
  if (is_resume(copy_, "S2_hard", x_, y_, z_))
  {
    double percent = copy_["S2_hard"]["percent"];
    int backup_threads = copy_["S2_hard"]["threads"];
    threads = max(threads, backup_threads);
    print_resume(percent, x_);
  }

  return threads;
}

/// backup thread
void LoadBalancer::backup(int threads,
                          int thread_id,
                          int64_t low,
                          int64_t segments,
                          int64_t segment_size)
{
  double percent = status_.getPercent(low_, z_, s2_total_, s2_approx_);
  double seconds = get_wtime() - backup_time_;

  string tid = "thread" + to_string(thread_id);

  json_["S2_hard"]["threads"] = threads;
  json_["S2_hard"]["low"] = low_;
  json_["S2_hard"]["segments"] = segments_;
  json_["S2_hard"]["segment_size"] = segment_size_;
  json_["S2_hard"]["s2_hard"] = to_string(s2_total_);
  json_["S2_hard"]["percent"] = percent;
  json_["S2_hard"]["seconds"] = get_wtime() - time_;
  json_["S2_hard"][tid]["low"] = low;
  json_["S2_hard"][tid]["segments"] = segments;
  json_["S2_hard"][tid]["segment_size"] = segment_size;

  if (seconds > 60)
  {
    backup_time_ = get_wtime();
    store_backup(json_);
  }
}

/// backup result
void LoadBalancer::backup()
{
  json_.erase("S2_hard");

  json_["S2_hard"]["x"] = to_string(x_);
  json_["S2_hard"]["y"] = y_;
  json_["S2_hard"]["z"] = z_;
  json_["S2_hard"]["s2_hard"] = to_string(s2_total_);
  json_["S2_hard"]["percent"] = 100;
  json_["S2_hard"]["seconds"] = get_wtime() - time_;

  store_backup(json_);
}

/// resume thread
bool LoadBalancer::resume(int thread_id,
                          int64_t& low,
                          int64_t& segments,
                          int64_t& segment_size)
{
  if (is_resume(copy_, "S2_hard", thread_id, x_, y_, z_))
  {
    string tid = "thread" + to_string(thread_id);
    low = copy_["S2_hard"][tid]["low"];
    segments = copy_["S2_hard"][tid]["segments"];
    segment_size = copy_["S2_hard"][tid]["segment_size"];
    return true;
  }

  return false;
}

// resume result
bool LoadBalancer::resume(maxint_t& s2_hard, double& time) const
{
  if (is_resume(copy_, "S2_hard", x_, y_, z_) &&
      !copy_["S2_hard"].count("low"))
  {
    double percent = copy_["S2_hard"]["percent"];
    double seconds = copy_["S2_hard"]["seconds"];

    s2_hard = calculator::eval<maxint_t>(copy_["S2_hard"]["s2_hard"]);
    time = get_wtime() - seconds;
    print_resume(percent, x_);
    return true;
  }

  return false;
}

LoadBalancer::LoadBalancer(maxint_t x,
                           int64_t y,
                           int64_t z,
                           maxint_t s2_approx) :
  low_(0),
  max_low_(0),
  x_(x),
  y_(y),
  z_(z),
  segments_(1),
  s2_total_(0),
  s2_approx_(s2_approx),
  time_(get_wtime()),
  backup_time_(get_wtime()),
  status_(x),
  json_(load_backup())
{
  // Read only json copy that is accessed
  // in parallel by multiple threads
  copy_ = json_;

  if (is_resume(json_, "S2_hard", x_, y_, z_))
  {
    double seconds = copy_["S2_hard"]["seconds"];
    s2_total_ = calculator::eval<maxint_t>(copy_["S2_hard"]["s2_hard"]);
    time_ = get_wtime() - seconds;

    if (json_["S2_hard"].count("low"))
    {
      low_ = copy_["S2_hard"]["low"];
      segments_ = copy_["S2_hard"]["segments"];
      segment_size_ = copy_["S2_hard"]["segment_size"];
    }
  }
  else
  {
    json_.erase("S2_hard");
    json_["S2_hard"]["x"] = to_string(x_);
    json_["S2_hard"]["y"] = y_;
    json_["S2_hard"]["z"] = z_;
  }

  init_size();
  maxint_t x16 = iroot<6>(x);
  double alpha = get_alpha(x, y);
  smallest_hard_leaf_ = (int64_t) (x / (y * sqrt(alpha) * x16));
}

void LoadBalancer::init_size()
{
  // start with a tiny segment_size as most
  // special leaves are in the first few segments
  // and we need to ensure that all threads are
  // assigned an equal amount of work
  int64_t sqrtz = isqrt(z_);
  int64_t log = ilog(sqrtz);
  log = max(log, 1);
  segment_size_ = sqrtz / log;

  int64_t min_size = 1 << 9;
  segment_size_ = max(segment_size_, min_size);
  segment_size_ = Sieve::get_segment_size(segment_size_);

  // try to use a segment size that fits exactly
  // into the CPUs L1 data cache
  int64_t l1_dcache_size = 1 << 15;
  max_size_ = l1_dcache_size * 30;
  max_size_ = max(max_size_, sqrtz);
  max_size_ = Sieve::get_segment_size(max_size_);
}

maxint_t LoadBalancer::get_result() const
{
  return s2_total_;
}

double LoadBalancer::get_time() const
{
  return time_;
}

bool LoadBalancer::get_work(int threads,
                            int thread_id,
                            int64_t* low,
                            int64_t* segments,
                            int64_t* segment_size,
                            maxint_t s2,
                            Runtime& runtime)
{
  #pragma omp critical (LoadBalancer)
  {
    s2_total_ += s2;

    update(low, segments, runtime);

    *low = low_;
    *segments = segments_;
    *segment_size = segment_size_;

    low_ += segments_ * segment_size_;

    backup(threads, thread_id, *low, *segments, *segment_size);

    if (is_print())
      status_.print(s2_total_, s2_approx_);
  }

  return *low <= z_;
}

void LoadBalancer::update(int64_t* low,
                          int64_t* segments,
                          Runtime& runtime)
{
  if (*low > max_low_)
  {
    max_low_ = *low;
    segments_ = *segments;

    if (segment_size_ < max_size_)
      segment_size_ = min(segment_size_ * 2, max_size_);
    else
    {
      double next = get_next(runtime);
      next = in_between(0.25, next, 2.0);
      next *= segments_;
      next = max(1.0, next);
      segments_ = (int64_t) next;
    }
  }

  auto high = low_ + segments_ * segment_size_;

  // Most hard special leaves are located just past
  // smallest_hard_leaf_. In order to prevent assigning
  // the bulk of work to a single thread we reduce
  // the number of segments to a minimum.
  //
  if (smallest_hard_leaf_ >= low_ &&
      smallest_hard_leaf_ <= high)
  {
    segments_ = 1;
  }
}

double LoadBalancer::get_next(Runtime& runtime) const
{
  double min_secs = runtime.init * 10;
  double run_secs = runtime.secs;

  min_secs = max(min_secs, 0.01);
  run_secs = max(run_secs, min_secs / 10);

  double rem = remaining_secs();
  double threshold = rem / 4;
  threshold = max(threshold, min_secs);

  return threshold / run_secs;
}

/// Remaining seconds till finished
double LoadBalancer::remaining_secs() const
{
  double percent = status_.getPercent(low_, z_, s2_total_, s2_approx_);
  percent = in_between(20, percent, 100);

  double total_secs = get_wtime() - time_;
  double secs = total_secs * (100 / percent) - total_secs;
  return secs;
}

} // namespace
<|endoftext|>


<commit_before>// Copyright (c) 2014, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE source in the root of the Project.

#include <boost/python.hpp>
#include <boost/optional.hpp>

#include <nix.hpp>

#include <accessors.hpp>
#include <transmorgify.hpp>

#include <PyEntity.hpp>

using namespace nix;
using namespace boost::python;

namespace nixpy {

// Label

void setSampledDimensionLabel(SampledDimension& dim, const boost::optional<std::string>& label) {
    if (label)
        dim.label(*label);
    else
        dim.label(boost::none);
}

void setRangeDimensionLabel(RangeDimension& dim, const boost::optional<std::string>& label) {
    if (label)
        dim.label(*label);
    else
        dim.label(boost::none);
}

void setSetDimensionLabels(SetDimension& dim, const std::vector<std::string>& labels) {
    if (!labels.empty())
        dim.labels(labels);
    else
        dim.labels(boost::none);
}

// Unit

void setSampledDimensionUnit(SampledDimension& dim, const boost::optional<std::string>& unit) {
    if (unit)
        dim.unit(*unit);
    else
        dim.unit(boost::none);
}

void setRangeDimensionUnit(RangeDimension& dim, const boost::optional<std::string>& unit) {
    if (unit)
        dim.unit(*unit);
    else
        dim.unit(boost::none);
}

// Offset

void setSampledDimensionOffset(SampledDimension& dim, const boost::optional<double>& offset) {
    if (offset)
        dim.offset(*offset);
    else
        dim.offset(boost::none);
}

std::vector<double> getSampledDimensionAxis1(SampledDimension& dim, const size_t count) {
    return dim.axis(count);
}

std::vector<double> getSampledDimensionAxis2(SampledDimension& dim, const size_t count,
					    const size_t start_index) {
    return dim.axis(count, start_index);
}

void PyDimensions::do_export() {

    enum_<DimensionType>("DimensionType")
        .value("Sample", DimensionType::Sample)
        .value("Range" , DimensionType::Range)
        .value("Set"   , DimensionType::Set)
        ;

    class_<SampledDimension>("SampledDimension")
        .add_property("index", &SampledDimension::index)
        .add_property("dimension_type", &SampledDimension::dimensionType)
        .add_property("label",
                      OPT_GETTER(std::string, SampledDimension, label),
                      setSampledDimensionLabel)
        .add_property("unit",
                      OPT_GETTER(std::string, SampledDimension, unit),
                      setSampledDimensionUnit)
        .add_property("sampling_interval",
                      GETTER(double, SampledDimension, samplingInterval),
                      SETTER(double, SampledDimension, samplingInterval))
        .add_property("offset",
                      OPT_GETTER(double, SampledDimension, offset),
                      setSampledDimensionOffset)
        .def("position_at", &SampledDimension::positionAt, doc::sampled_dimension_position_at)
        .def("index_of", &SampledDimension::positionAt, doc::sampled_dimension_index_of)
        .def("axis", getSampledDimensionAxis1, doc::sampled_dimension_axis)
        .def("axis", getSampledDimensionAxis2, doc::sampled_dimension_axis)
        ;

    class_<RangeDimension>("RangeDimension")
        .add_property("index", &RangeDimension::index)
        .add_property("dimension_type", &RangeDimension::dimensionType)
        .add_property("label",
                      OPT_GETTER(std::string, RangeDimension, label),
                      setRangeDimensionLabel)
        .add_property("unit",
                      OPT_GETTER(std::string, RangeDimension, unit),
                      setRangeDimensionUnit)
        .add_property("ticks",
                      GETTER(std::vector<double>, RangeDimension, ticks),
                      REF_SETTER(std::vector<double>, RangeDimension, ticks))
        ;

    class_<SetDimension>("SetDimension")
        .add_property("index", &SetDimension::index)
        .add_property("dimension_type", &SetDimension::dimensionType)
        .add_property("labels",
                      GETTER(std::vector<std::string>, SetDimension, labels),
                      setSetDimensionLabels)
        ;
}

}
<commit_msg>fixed copy and paste bug in index_of method<commit_after>// Copyright (c) 2014, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE source in the root of the Project.

#include <boost/python.hpp>
#include <boost/optional.hpp>

#include <nix.hpp>

#include <accessors.hpp>
#include <transmorgify.hpp>

#include <PyEntity.hpp>

using namespace nix;
using namespace boost::python;

namespace nixpy {

// Label

void setSampledDimensionLabel(SampledDimension& dim, const boost::optional<std::string>& label) {
    if (label)
        dim.label(*label);
    else
        dim.label(boost::none);
}

void setRangeDimensionLabel(RangeDimension& dim, const boost::optional<std::string>& label) {
    if (label)
        dim.label(*label);
    else
        dim.label(boost::none);
}

void setSetDimensionLabels(SetDimension& dim, const std::vector<std::string>& labels) {
    if (!labels.empty())
        dim.labels(labels);
    else
        dim.labels(boost::none);
}

// Unit

void setSampledDimensionUnit(SampledDimension& dim, const boost::optional<std::string>& unit) {
    if (unit)
        dim.unit(*unit);
    else
        dim.unit(boost::none);
}

void setRangeDimensionUnit(RangeDimension& dim, const boost::optional<std::string>& unit) {
    if (unit)
        dim.unit(*unit);
    else
        dim.unit(boost::none);
}

// Offset

void setSampledDimensionOffset(SampledDimension& dim, const boost::optional<double>& offset) {
    if (offset)
        dim.offset(*offset);
    else
        dim.offset(boost::none);
}

std::vector<double> getSampledDimensionAxis1(SampledDimension& dim, const size_t count) {
    return dim.axis(count);
}

std::vector<double> getSampledDimensionAxis2(SampledDimension& dim, const size_t count,
					    const size_t start_index) {
    return dim.axis(count, start_index);
}

void PyDimensions::do_export() {

    enum_<DimensionType>("DimensionType")
        .value("Sample", DimensionType::Sample)
        .value("Range" , DimensionType::Range)
        .value("Set"   , DimensionType::Set)
        ;

    class_<SampledDimension>("SampledDimension")
        .add_property("index", &SampledDimension::index)
        .add_property("dimension_type", &SampledDimension::dimensionType)
        .add_property("label",
                      OPT_GETTER(std::string, SampledDimension, label),
                      setSampledDimensionLabel)
        .add_property("unit",
                      OPT_GETTER(std::string, SampledDimension, unit),
                      setSampledDimensionUnit)
        .add_property("sampling_interval",
                      GETTER(double, SampledDimension, samplingInterval),
                      SETTER(double, SampledDimension, samplingInterval))
        .add_property("offset",
                      OPT_GETTER(double, SampledDimension, offset),
                      setSampledDimensionOffset)
        .def("position_at", &SampledDimension::positionAt, doc::sampled_dimension_position_at)
        .def("index_of", &SampledDimension::indexOf, doc::sampled_dimension_index_of)
        .def("axis", getSampledDimensionAxis1, doc::sampled_dimension_axis)
        .def("axis", getSampledDimensionAxis2, doc::sampled_dimension_axis)
        ;

    class_<RangeDimension>("RangeDimension")
        .add_property("index", &RangeDimension::index)
        .add_property("dimension_type", &RangeDimension::dimensionType)
        .add_property("label",
                      OPT_GETTER(std::string, RangeDimension, label),
                      setRangeDimensionLabel)
        .add_property("unit",
                      OPT_GETTER(std::string, RangeDimension, unit),
                      setRangeDimensionUnit)
        .add_property("ticks",
                      GETTER(std::vector<double>, RangeDimension, ticks),
                      REF_SETTER(std::vector<double>, RangeDimension, ticks))
        ;

    class_<SetDimension>("SetDimension")
        .add_property("index", &SetDimension::index)
        .add_property("dimension_type", &SetDimension::dimensionType)
        .add_property("labels",
                      GETTER(std::vector<std::string>, SetDimension, labels),
                      setSetDimensionLabels)
        ;
}

}
<|endoftext|>


<commit_before>// -*- mode:c++; indent-tabs-mode:nil; -*-

/*
  Copyright (c) 2014, Anders Ronnbrant, anders.ronnbrant@gmail.com

  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.
*/

#include "RecorderHDF5.h"
#include "RecorderBase.h"

#include "zmqutils.h"

#include <zmq.hpp>

#include <chrono>
#include <map>

typedef std::chrono::milliseconds msec;
typedef std::chrono::microseconds usec;

RecorderHDF5::RecorderHDF5()
    : RecorderBase("HDF5Backend") {
}

RecorderHDF5::~RecorderHDF5() {
  if (poller_running_.load()) {
    stop();
  }
}

void
RecorderHDF5::start() {
  printf("Starting ...\n");
  poller_running_.store(true);
  poller_thread_ = std::thread(&RecorderHDF5::run, this);
}

void
RecorderHDF5::stop() {
  printf("Stopping ...\n");
  poller_running_.store(false);
  if (poller_thread_.joinable()) {
    poller_thread_.join();
  }
}

void
RecorderHDF5::run() {
  auto t1 = std::chrono::high_resolution_clock::now();
  zmq::socket_t sock(*RecorderBase::socket_context, ZMQ_PULL);
  zmqutils::bind(&sock, RecorderBase::socket_address.c_str());
  zmq::message_t zmsg;
  bool messages_to_process = true;
  std::array<int32_t, 4096> counter;
  int64_t count = 0;
  zmq_pollitem_t pollitems[] = { { sock, 0, ZMQ_POLLIN, 0 } };
  while (poller_running_.load() || messages_to_process) {
    if (!zmqutils::poll(pollitems)) {
      messages_to_process = false;
      continue;
    }

    auto type = zmqutils::pop<PayloadType>(&sock, &zmsg);

    switch (type) {
      case PayloadType::DATA: {
        sock.recv(&zmsg);
        auto num_params = zmsg.size() / sizeof(Item);
        count += num_params;
        for (size_t i = 0; i < num_params; ++i) {
          auto* item = static_cast<Item*>(zmsg.data()) + i;
          int32_t rec_id = item->recorder_id;
          counter[rec_id] += 1;
        }
      } break;;
      case PayloadType::INIT_ITEM: {
        auto init = zmqutils::pop<InitItem>(&sock, &zmsg);
        printf("(ITEM): %4d-%d '%s' '%s' '%s'\n",
               init.recorder_id,
               init.key,
               init.name,
               init.unit,
               init.desc);
      } break;;
      case PayloadType::INIT_RECORDER: {
        auto init = zmqutils::pop<InitRecorder>(&sock, &zmsg);
        printf("(REC):  %4d(%ld) L%d '%s'\n",
               init.recorder_id,
               init.external_id,
               init.recorder_num_items,
               init.recorder_name);
        // int32_t rec_id = init.recorder_id;
        // counter.reserve(rec_id);
      } break;;
      default:
        break;;
    }

    size_t idx = 0;
    while (idx < (zmsg.size()/sizeof(Item))) {
      ++idx;
    }
  }
  sock.close();

  auto const mib = 1<<20;

  auto t2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<usec>(t2 - t1);
  double duration_msec = duration.count()/1000.0;
  printf("Messages:     %ld (%.3fms)\n", count, duration_msec);
  printf("Messages/sec: %.1f (%.1fMiB/sec)\n",
         count * 1000 / duration_msec,
         sizeof(Item) * count * 1000 / (mib * duration_msec));

  for (size_t i = 0; i < counter.max_size(); ++i) {
    if (counter[i] > 0)
      printf("(RECV): %2lu:%d\n", i, counter[i]);
  }
}
<commit_msg>Add print function for data items (debugging only)<commit_after>// -*- mode:c++; indent-tabs-mode:nil; -*-

/*
  Copyright (c) 2014, Anders Ronnbrant, anders.ronnbrant@gmail.com

  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.
*/

#include "RecorderHDF5.h"
#include "RecorderBase.h"

#include "zmqutils.h"

#include <zmq.hpp>

#include <chrono>
#include <map>

typedef std::chrono::milliseconds msec;
typedef std::chrono::microseconds usec;

namespace {
void printItem(Item const* item) {
  auto info = static_cast<int8_t>(item->info);
  printf("(DATA): %d, %4d, %4d  %x %x %x\n",
         item->time,
         item->recorder_id,
         item->key,
         item->info,
         info & 0x0F,
         info >> 4);
}
}  // namespace

RecorderHDF5::RecorderHDF5()
    : RecorderBase("HDF5Backend") {
}

RecorderHDF5::~RecorderHDF5() {
  if (poller_running_.load()) {
    stop();
  }
}

void
RecorderHDF5::start() {
  printf("Starting ...\n");
  poller_running_.store(true);
  poller_thread_ = std::thread(&RecorderHDF5::run, this);
}

void
RecorderHDF5::stop() {
  printf("Stopping ...\n");
  poller_running_.store(false);
  if (poller_thread_.joinable()) {
    poller_thread_.join();
  }
}

void
RecorderHDF5::run() {
  auto t1 = std::chrono::high_resolution_clock::now();
  zmq::socket_t sock(*RecorderBase::socket_context, ZMQ_PULL);
  zmqutils::bind(&sock, RecorderBase::socket_address.c_str());
  zmq::message_t zmsg;
  bool messages_to_process = true;
  std::array<int32_t, 4096> counter;
  int64_t count = 0;
  zmq_pollitem_t pollitems[] = { { sock, 0, ZMQ_POLLIN, 0 } };
  while (poller_running_.load() || messages_to_process) {
    if (!zmqutils::poll(pollitems)) {
      messages_to_process = false;
      continue;
    }

    auto type = zmqutils::pop<PayloadType>(&sock, &zmsg);

    switch (type) {
      case PayloadType::DATA: {
        sock.recv(&zmsg);
        auto num_params = zmsg.size() / sizeof(Item);
        count += num_params;
        for (size_t i = 0; i < num_params; ++i) {
          auto* item = static_cast<Item*>(zmsg.data()) + i;
          int32_t rec_id = item->recorder_id;
          counter[rec_id] += 1;
        }
      } break;;
      case PayloadType::INIT_ITEM: {
        auto init = zmqutils::pop<InitItem>(&sock, &zmsg);
        printf("(ITEM): %4d-%d '%s' '%s' '%s'\n",
               init.recorder_id,
               init.key,
               init.name,
               init.unit,
               init.desc);
      } break;;
      case PayloadType::INIT_RECORDER: {
        auto init = zmqutils::pop<InitRecorder>(&sock, &zmsg);
        printf("(REC):  %4d(%ld) L%d '%s'\n",
               init.recorder_id,
               init.external_id,
               init.recorder_num_items,
               init.recorder_name);
        // int32_t rec_id = init.recorder_id;
        // counter.reserve(rec_id);
      } break;;
      default:
        break;;
    }

    size_t idx = 0;
    while (idx < (zmsg.size()/sizeof(Item))) {
      ++idx;
    }
  }
  sock.close();

  auto const mib = 1<<20;

  auto t2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<usec>(t2 - t1);
  double duration_msec = duration.count()/1000.0;
  printf("Messages:     %ld (%.3fms)\n", count, duration_msec);
  printf("Messages/sec: %.1f (%.1fMiB/sec)\n",
         count * 1000 / duration_msec,
         sizeof(Item) * count * 1000 / (mib * duration_msec));

  for (size_t i = 0; i < counter.max_size(); ++i) {
    if (counter[i] > 0)
      printf("(RECV): %2lu:%d\n", i, counter[i]);
  }
}
<|endoftext|>


<commit_before>#include "RoomViewList.hpp"

#include <QMenu>
#include <QContextMenuEvent>
#include <QScrollBar>

#include "matrix/Room.hpp"

RoomViewList::RoomViewList(QWidget *parent) : QListWidget(parent), menu_(new QMenu(this)) {
  connect(this, &QListWidget::currentItemChanged, [this](QListWidgetItem *item, QListWidgetItem *previous) {
      (void)previous;
      if(item != nullptr) {
        auto &room = *reinterpret_cast<matrix::Room *>(item->data(Qt::UserRole).value<quintptr>());
        activated(room);
      }
    });

  auto move_up = menu_->addAction(tr("Move up"));
  connect(move_up, &QAction::triggered, [this]() {
      auto r = row(items_.at(context_));
      auto item = takeItem(r);
      insertItem(r > 0 ? r - 1 : 0, item);
      setCurrentItem(item);
    });
  auto move_down = menu_->addAction(tr("Move down"));
  connect(move_down, &QAction::triggered, [this]() {
      auto r = row(items_.at(context_));
      auto item = takeItem(r);
      insertItem(r+1, item);
      setCurrentItem(item);
    });
  auto pop_out_action = menu_->addAction(QIcon::fromTheme("window-open"), tr("&Pop out"));
  connect(pop_out_action, &QAction::triggered, [this]() {
      pop_out(*context_);
      release(*context_);
    });
  auto close = menu_->addAction(QIcon::fromTheme("window-close"), tr("&Close"));
  connect(close, &QAction::triggered, [this]() {
      release(*context_);
    });

  QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  setSizePolicy(policy);
}

void RoomViewList::add(matrix::Room &room) {
  auto item = new QListWidgetItem;
  item->setToolTip(room.id());
  item->setText(room.pretty_name());
  item->setData(Qt::UserRole, QVariant::fromValue(reinterpret_cast<quintptr>(&room)));
  addItem(item);
  items_.emplace(&room, item);
  claimed(room);
  update();
}

void RoomViewList::release(matrix::Room &room) {
  delete items_.at(&room);
  released(room);
}

void RoomViewList::activate(matrix::Room &room) {
  auto &item = *items_.at(&room);
  scrollToItem(&item);
  setCurrentItem(&item);
  activated(room);
}

void RoomViewList::update_name(matrix::Room &room) {
  items_.at(&room)->setText(room.pretty_name());
}

void RoomViewList::contextMenuEvent(QContextMenuEvent *e) {
  if(auto item = itemAt(e->pos())) {
    context_ = reinterpret_cast<matrix::Room *>(item->data(Qt::UserRole).value<quintptr>());
    menu_->popup(e->globalPos());
  }
}

QSize RoomViewList::sizeHint() const {
  auto margins = contentsMargins();
  return QSize(sizeHintForColumn(0) + verticalScrollBar()->sizeHint().width() + margins.left() + margins.right(),
               fontMetrics().lineSpacing() + horizontalScrollBar()->sizeHint().height());
}
<commit_msg>Fix dangling pointers in RoomViewList<commit_after>#include "RoomViewList.hpp"

#include <QMenu>
#include <QContextMenuEvent>
#include <QScrollBar>

#include "matrix/Room.hpp"

RoomViewList::RoomViewList(QWidget *parent) : QListWidget(parent), menu_(new QMenu(this)) {
  connect(this, &QListWidget::currentItemChanged, [this](QListWidgetItem *item, QListWidgetItem *previous) {
      (void)previous;
      if(item != nullptr) {
        auto &room = *reinterpret_cast<matrix::Room *>(item->data(Qt::UserRole).value<quintptr>());
        activated(room);
      }
    });

  auto move_up = menu_->addAction(tr("Move up"));
  connect(move_up, &QAction::triggered, [this]() {
      auto r = row(items_.at(context_));
      auto item = takeItem(r);
      insertItem(r > 0 ? r - 1 : 0, item);
      setCurrentItem(item);
    });
  auto move_down = menu_->addAction(tr("Move down"));
  connect(move_down, &QAction::triggered, [this]() {
      auto r = row(items_.at(context_));
      auto item = takeItem(r);
      insertItem(r+1, item);
      setCurrentItem(item);
    });
  auto pop_out_action = menu_->addAction(QIcon::fromTheme("window-open"), tr("&Pop out"));
  connect(pop_out_action, &QAction::triggered, [this]() {
      pop_out(*context_);
      release(*context_);
    });
  auto close = menu_->addAction(QIcon::fromTheme("window-close"), tr("&Close"));
  connect(close, &QAction::triggered, [this]() {
      release(*context_);
    });

  QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  setSizePolicy(policy);
}

void RoomViewList::add(matrix::Room &room) {
  auto item = new QListWidgetItem;
  item->setToolTip(room.id());
  item->setText(room.pretty_name());
  item->setData(Qt::UserRole, QVariant::fromValue(reinterpret_cast<quintptr>(&room)));
  addItem(item);
  items_.emplace(&room, item);
  claimed(room);
  update();
}

void RoomViewList::release(matrix::Room &room) {
  auto it = items_.find(&room);
  assert(it != items_.end());
  delete it->second;
  items_.erase(it);
  released(room);
}

void RoomViewList::activate(matrix::Room &room) {
  auto &item = *items_.at(&room);
  scrollToItem(&item);
  setCurrentItem(&item);
  activated(room);
}

void RoomViewList::update_name(matrix::Room &room) {
  items_.at(&room)->setText(room.pretty_name());
}

void RoomViewList::contextMenuEvent(QContextMenuEvent *e) {
  if(auto item = itemAt(e->pos())) {
    context_ = reinterpret_cast<matrix::Room *>(item->data(Qt::UserRole).value<quintptr>());
    menu_->popup(e->globalPos());
  }
}

QSize RoomViewList::sizeHint() const {
  auto margins = contentsMargins();
  return QSize(sizeHintForColumn(0) + verticalScrollBar()->sizeHint().width() + margins.left() + margins.right(),
               fontMetrics().lineSpacing() + horizontalScrollBar()->sizeHint().height());
}
<|endoftext|>