diff --git a/CMakeLists.txt b/CMakeLists.txt index 65976d25f46d1d60c94ac71a64b3684755de2693..f65eae2487967dcaacfcdfc26b3fa10e908d7341 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,10 @@ include(CheckFunctionExists) include(CheckIncludeFile) include(CheckCXXCompilerFlag) +if(ENABLE_WRAP_PYTHON AND NOT ENABLE_BUILD_SHARED) + set(ENABLE_BUILD_DYNAMIC ON) +endif(ENABLE_WRAP_PYTHON AND NOT ENABLE_BUILD_SHARED) + if(MSVC) # remove annoying warning about bool/int cast performance set(GMSH_CONFIG_PRAGMAS "#pragma warning(disable:4800 4244 4267)") diff --git a/utils/solvers/c++/onelab.h b/utils/solvers/c++/onelab.h index 912f0d2ddb67a6a91e4298b6b907d4b7b7e71936..f71601773699991585f1c2a681464c085989fa43 100644 --- a/utils/solvers/c++/onelab.h +++ b/utils/solvers/c++/onelab.h @@ -35,6 +35,7 @@ #include <set> #include <map> #include <iostream> +#include <algorithm> #include <sstream> #include "GmshSocket.h" @@ -54,12 +55,16 @@ namespace onelab{ // a help string std::string _help; // map of clients that use this parameter, associated with a "changed" flag - // (set to false if the client has already been run with the current value of - // the parameter) - std::map<std::string, bool> _clients; - // flag indicating that the "changed" flags of this parameter will always be - // reset to false when the parameter is updated - bool _neverChanged; + // (set to 0 if the client has already been run with the current value of + // the parameter; set to defaultChangedValue() when the value of a parameter + // has been changed; values between 1 and defaultChangedValue() can be used + // to "modulate" the degree of change, e.g. to change the action to be + // performed depending on the "severity" of the change) + std::map<std::string, int> _clients; + // flag indicating what the "changed" value should be set to when a + // parameter is updated to a different value (if set to 0, the parameter + // will appear as never being changed) + int _changedValue; // should the parameter be visible in the interface? bool _visible; // sould the paramete be "read-only" (not settable by the user) @@ -70,25 +75,32 @@ namespace onelab{ public: parameter(const std::string &name="", const std::string &label="", const std::string &help="") - : _name(name), _label(label), _help(help), _neverChanged(false), - _visible(true), _readOnly(false) {} + : _name(name), _label(label), _help(help), _visible(true), + _readOnly(false) + { + _changedValue = defaultChangedValue(); + } virtual ~parameter(){} void setName(const std::string &name){ _name = name; } void setLabel(const std::string &label){ _label = label; } void setHelp(const std::string &help){ _help = help; } - void setChanged(bool changed, const std::string &client="") + void setChanged(int changed, const std::string &client="") { if(client.size()){ - std::map<std::string, bool>::iterator it = _clients.find(client); + std::map<std::string, int>::iterator it = _clients.find(client); if(it != _clients.end()) it->second = changed; } else{ - for(std::map<std::string, bool>::iterator it = _clients.begin(); + for(std::map<std::string, int>::iterator it = _clients.begin(); it != _clients.end(); it++) it->second = changed; } } - void setNeverChanged(bool never){ _neverChanged = never; } + void setChangedValue(int value){ _changedValue = value; } + void setNeverChanged(bool never) + { + _changedValue = never ? 0 : defaultChangedValue(); + } void setVisible(bool visible){ _visible = visible; } void setReadOnly(bool readOnly){ _readOnly = readOnly; } void setAttribute(const std::string &key, const std::string &value) @@ -99,13 +111,13 @@ namespace onelab{ { _attributes = attributes; } - void setClients(const std::map<std::string, bool> &clients){ _clients = clients; } - void addClient(const std::string &client, bool changed) + void setClients(const std::map<std::string, int> &clients){ _clients = clients; } + void addClient(const std::string &client, int changed) { if(_clients.find(client) == _clients.end()) _clients[client] = changed; } - void addClients(const std::map<std::string, bool> &clients) + void addClients(const std::map<std::string, int> &clients) { _clients.insert(clients.begin(), clients.end()); } @@ -144,22 +156,24 @@ namespace onelab{ s = s.substr(1); return s; } - bool getChanged(const std::string &client="") const + int getChanged(const std::string &client="") const { if(client.size()){ - std::map<std::string, bool>::const_iterator it = _clients.find(client); + std::map<std::string, int>::const_iterator it = _clients.find(client); if(it != _clients.end()) return it->second; - else return false; + else return 0; } else{ - for(std::map<std::string, bool>::const_iterator it = _clients.begin(); + int changed = 0; + for(std::map<std::string, int>::const_iterator it = _clients.begin(); it != _clients.end(); it++){ - if(it->second) return true; + changed = std::max(changed, it->second); } - return false; + return changed; } } - bool getNeverChanged() const { return _neverChanged; } + int getChangedValue() const { return _changedValue; } + bool getNeverChanged() const { return _changedValue ? false : true; } bool getVisible() const { return _visible; } bool getReadOnly() const { return _readOnly; } std::string getAttribute(const std::string &key) const @@ -172,10 +186,11 @@ namespace onelab{ { return _attributes; } - const std::map<std::string, bool> &getClients() const { return _clients; } + const std::map<std::string, int> &getClients() const { return _clients; } static char charSep() { return '\0'; } static double maxNumber() { return 1e200; } - static std::string version() { return "1.1"; } + static std::string version() { return "1.2"; } + static int defaultChangedValue() { return 31; } static std::string getNextToken(const std::string &msg, std::string::size_type &first, char separator=charSep()) @@ -220,7 +235,7 @@ namespace onelab{ << sanitize(getName()) << charSep() << sanitize(getLabel()) << charSep() << sanitize(getHelp()) << charSep() - << (getNeverChanged() ? 1 : 0) << charSep() + << getChangedValue() << charSep() << (getVisible() ? 1 : 0) << charSep() << (getReadOnly() ? 1 : 0) << charSep() << _attributes.size() << charSep(); @@ -229,7 +244,7 @@ namespace onelab{ sstream << sanitize(it->first) << charSep() << sanitize(it->second) << charSep(); sstream << getClients().size() << charSep(); - for(std::map<std::string, bool>::const_iterator it = getClients().begin(); + for(std::map<std::string, int>::const_iterator it = getClients().begin(); it != getClients().end(); it++) sstream << sanitize(it->first) << charSep() << (it->second ? 1 : 0) << charSep(); @@ -243,7 +258,7 @@ namespace onelab{ setName(getNextToken(msg, pos)); setLabel(getNextToken(msg, pos)); setHelp(getNextToken(msg, pos)); - setNeverChanged(atoi(getNextToken(msg, pos).c_str())); + setChangedValue(atoi(getNextToken(msg, pos).c_str())); setVisible(atoi(getNextToken(msg, pos).c_str())); setReadOnly(atoi(getNextToken(msg, pos).c_str())); int numAttributes = atoi(getNextToken(msg, pos).c_str()); @@ -255,7 +270,7 @@ namespace onelab{ for(int i = 0; i < numClients; i++){ std::string client(getNextToken(msg, pos)); int changed = atoi(getNextToken(msg, pos).c_str()); - addClient(client, changed ? true : false); + addClient(client, changed); } return pos; } @@ -267,8 +282,7 @@ namespace onelab{ type = getNextToken(msg, first); name = getNextToken(msg, first); } - static bool fromFile(std::vector<std::string> &msg, - FILE *fp) + static bool fromFile(std::vector<std::string> &msg, FILE *fp) { msg.clear(); char tmp[1000]; @@ -284,8 +298,7 @@ namespace onelab{ } return true; } - static bool toFile(const std::vector<std::string> &msg, - FILE *fp, + static bool toFile(const std::vector<std::string> &msg, FILE *fp, const std::string &creator) { time_t now; @@ -300,6 +313,35 @@ namespace onelab{ } return true; } + virtual std::string toJSON() const + { + std::ostringstream sstream; + sstream << "\"type\":\"" << getType() << "\"" + << ", \"version\":\"" << version() << "\"" + << ", \"name\":\"" << sanitize(getName()) << "\"" + << ", \"label\":\"" << sanitize(getLabel()) << "\"" + << ", \"help\":\"" << sanitize(getHelp()) << "\"" + << ", \"changedValue\":" << getChangedValue() << "\"" + << ", \"visible\":" << (getVisible() ? "true" : "false") + << ", \"readOnly\":" << (getReadOnly() ? "true" : "false") + << ", \"attributes\":{ "; + for(std::map<std::string, std::string>::const_iterator it = _attributes.begin(); + it != _attributes.end(); it++){ + if(it != _attributes.begin()) sstream << ", "; + sstream << "\"" << sanitize(it->first) << "\":\"" + << sanitize(it->second) << "\""; + } + sstream << " }" + << ", \"clients\":{ "; + for(std::map<std::string, int>::const_iterator it = getClients().begin(); + it != getClients().end(); it++){ + if(it != getClients().begin()) sstream << ", "; + sstream << "\"" << sanitize(it->first) << "\":" + << (it->second ? "true" : "false"); + } + sstream << " }"; + return sstream.str(); + } }; class parameterLessThan{ @@ -315,18 +357,23 @@ namespace onelab{ // functions, etc.) are supposed to be exchanged as strings. class number : public parameter{ private: - double _value, _min, _max, _step; - // when in a loop, indicates current index in the vector _choices; - // is -1 when not in a loop + std::vector<double> _values, _choices; + double _min, _max, _step; + // when in a loop, indicates current index in the vector _choices; is -1 + // when not in a loop int _index; - std::vector<double> _choices; std::map<double, std::string> _valueLabels; public: number(const std::string &name="", double value=0., const std::string &label="", const std::string &help="") - : parameter(name, label, help), _value(value), - _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1) {} - void setValue(double value){ _value = value; } + : parameter(name, label, help), _values(std::vector<double>(1, value)), + _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1){} + number(const std::string &name, const std::vector<double> &values, + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(values), + _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1){} + void setValue(double value){ _values.resize(1); _values[0] = value; } + void setValues(const std::vector<double> &values){ _values = values; } void setMin(double min){ _min = min; } void setMax(double max){ _max = max; } void setStep(double step){ _step = step; } @@ -347,7 +394,9 @@ namespace onelab{ _valueLabels[value] = label; } std::string getType() const { return "number"; } - double getValue() const { return _value; } + double getValue() const { if(_values.empty()) return 0.; return _values[0]; } + const std::vector<double> &getValues() const { return _values; } + unsigned int getNumValues() const { return _values.size(); } double getMin() const { return _min; } double getMax() const { return _max; } double getStep() const { return _step; } @@ -371,9 +420,9 @@ namespace onelab{ setVisible(p.getVisible()); setReadOnly(p.getReadOnly()); setAttributes(p.getAttributes()); - if(p.getValue() != getValue()){ - setValue(p.getValue()); - setChanged(true); + if(p.getValue() != getValue()){ // FIXME change this + setValues(p.getValues()); + setChanged(getChangedValue()); } setMin(p.getMin()); setMax(p.getMax()); @@ -381,14 +430,19 @@ namespace onelab{ setIndex(p.getIndex()); setChoices(p.getChoices()); setValueLabels(p.getValueLabels()); - if(getNeverChanged()) setChanged(false); + if(getNeverChanged()) setChanged(0); } std::string toChar() const { std::ostringstream sstream; sstream.precision(16); - sstream << parameter::toChar() << _value << charSep() - << _min << charSep() << _max << charSep() << _step << charSep() + sstream << parameter::toChar() + << _values.size() << charSep(); + for(unsigned int i = 0; i < _values.size(); i++) + sstream << _values[i] << charSep(); + sstream << _min << charSep() + << _max << charSep() + << _step << charSep() << _index << charSep() << _choices.size() << charSep(); for(unsigned int i = 0; i < _choices.size(); i++) @@ -396,8 +450,8 @@ namespace onelab{ sstream << _valueLabels.size() << charSep(); for(std::map<double, std::string>::const_iterator it = _valueLabels.begin(); it != _valueLabels.end(); it++){ - sstream << it->first << charSep(); - sstream << sanitize(it->second) << charSep(); + sstream << it->first << charSep() + << sanitize(it->second) << charSep(); } return sstream.str(); } @@ -405,7 +459,9 @@ namespace onelab{ { std::string::size_type pos = parameter::fromChar(msg); if(!pos) return 0; - setValue(atof(getNextToken(msg, pos).c_str())); + _values.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _values.size(); i++) + _values[i] = atof(getNextToken(msg, pos).c_str()); setMin(atof(getNextToken(msg, pos).c_str())); setMax(atof(getNextToken(msg, pos).c_str())); setStep(atof(getNextToken(msg, pos).c_str())); @@ -420,28 +476,65 @@ namespace onelab{ } return pos; } + std::string toJSON() const + { + std::ostringstream sstream; + sstream.precision(16); + sstream << "{ " << parameter::toJSON() + << ", \"values\":["; + for(unsigned int i = 0; i < _values.size(); i++){ + if(i) sstream << ", "; + sstream << _values[i]; + } + sstream << " ]" + << ", \"min\":" << _min + << ", \"max\":" << _max + << ", \"step\":" << _step + << ", \"index\":" << _index + << ", \"choices\":[ "; + for(unsigned int i = 0; i < _choices.size(); i++){ + if(i) sstream << ", "; + sstream << _choices[i]; + } + sstream << " ]" + << ", \"valueLabels\":{ "; + for(std::map<double, std::string>::const_iterator it = _valueLabels.begin(); + it != _valueLabels.end(); it++){ + if(it != _valueLabels.begin()) sstream << ", "; + sstream << "\"" << sanitize(it->second) << "\":" << it->first; + } + sstream << " } }"; + return sstream.str(); + } }; - // The string class. A string has a mutable "kind": we do not derive - // specialized classes, because the kind should be changeable at runtime - // (e.g. from a client-dependent mathematical expression to a table of - // values). Kinds currently recognized by Gmsh are: "file". Possible - // kinds could be "complex", "matrix m n", "hostname", client-dependent - // mathematical expression, onelab mathematical expression (through mathex?), - // ... + // The string class. A string has a mutable "kind", that can be changed at + // runtime. Kinds leading to specific behavior in Gmsh are: "file". class string : public parameter{ private: - std::string _value, _kind; - std::vector<std::string> _choices; + std::vector<std::string> _values, _choices; + std::string _kind; public: string(const std::string &name="", const std::string &value="", const std::string &label="", const std::string &help="") - : parameter(name, label, help), _value(value), _kind("generic") {} - void setValue(const std::string &value){ _value = value; } + : parameter(name, label, help), _values(std::vector<std::string>(1, value)), + _kind("generic") {} + string(const std::string &name, const std::vector<std::string> &values, + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(values), + _kind("generic") {} + void setValue(const std::string &value){ _values.resize(1); _values[0] = value; } + void setValues(const std::vector<std::string> &values){ _values = values; } void setKind(const std::string &kind){ _kind = kind; } void setChoices(const std::vector<std::string> &choices){ _choices = choices; } std::string getType() const { return "string"; } - const std::string &getValue() const { return _value; } + const std::string &getValue() const + { + static std::string n(""); + if(_values.empty()) return n; return _values[0]; + } + const std::vector<std::string> &getValues() const { return _values; } + unsigned int getNumValues() const { return _values.size(); } const std::string &getKind() const { return _kind; } const std::vector<std::string> &getChoices() const { return _choices; } void update(const string &p) @@ -452,22 +545,25 @@ namespace onelab{ setVisible(p.getVisible()); setReadOnly(p.getReadOnly()); setAttributes(p.getAttributes()); - if(p.getValue() != getValue()){ - setValue(p.getValue()); - setChanged(true); + if(p.getValue() != getValue()){ // FIXME: change this + setValues(p.getValues()); + setChanged(getChangedValue()); } if(p.getKind() != getKind()){ setKind(p.getKind()); - setChanged(true); + setChanged(getChangedValue()); } setChoices(p.getChoices()); - if(getNeverChanged()) setChanged(false); + if(getNeverChanged()) setChanged(0); } std::string toChar() const { std::ostringstream sstream; - sstream << parameter::toChar() << sanitize(_value) << charSep() - << sanitize(_kind) << charSep() + sstream << parameter::toChar() + << _values.size() << charSep(); + for(unsigned int i = 0; i < _values.size(); i++) + sstream << sanitize(_values[i]) << charSep(); + sstream << sanitize(_kind) << charSep() << _choices.size() << charSep(); for(unsigned int i = 0; i < _choices.size(); i++) sstream << sanitize(_choices[i]) << charSep(); @@ -477,13 +573,34 @@ namespace onelab{ { std::string::size_type pos = parameter::fromChar(msg); if(!pos) return 0; - setValue(getNextToken(msg, pos)); + _values.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _values.size(); i++) + _values[i] = getNextToken(msg, pos); setKind(getNextToken(msg, pos)); _choices.resize(atoi(getNextToken(msg, pos).c_str())); for(unsigned int i = 0; i < _choices.size(); i++) _choices[i] = getNextToken(msg, pos); return pos; } + std::string toJSON() const + { + std::ostringstream sstream; + sstream << "{ " << parameter::toJSON() + << ", \"values\":[ " ; + for(unsigned int i = 0; i < _values.size(); i++){ + if(i) sstream << ", "; + sstream << "\"" << sanitize(_values[i]) << "\""; + } + sstream << " ] " + << ", \"kind\":\"" << sanitize(_kind) << "\"" + << ", \"choices\":[ "; + for(unsigned int i = 0; i < _choices.size(); i++){ + if(i) sstream << ", "; + sstream << "\"" << sanitize(_choices[i]) << "\""; + } + sstream << " ] }"; + return sstream.str(); + } }; // The parameter space, i.e., the set of parameters stored and handled by the @@ -535,11 +652,11 @@ namespace onelab{ typename std::set<T*, parameterLessThan>::iterator it = ps.find((T*)&p); if(it != ps.end()){ (*it)->update(p); - if(client.size()) (*it)->addClient(client, true); + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); } else{ T* newp = new T(p); - if(client.size()) newp->addClient(client, true); + if(client.size()) newp->addClient(client, parameter::defaultChangedValue()); ps.insert(newp); } return true; @@ -562,7 +679,7 @@ namespace onelab{ T tmp(name); typename std::set<T*, parameterLessThan>::iterator it = ps.find(&tmp); if(it != ps.end()){ - if(client.size()) (*it)->addClient(client, true); + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); p.push_back(**it); } } @@ -574,7 +691,7 @@ namespace onelab{ T tmp(name); typename std::set<T*, parameterLessThan>::iterator it = ps.find(&tmp); if(it != ps.end()){ - if(client.size()) (*it)->addClient(client, true); + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); return *it; } return NULL; @@ -640,21 +757,20 @@ namespace onelab{ } // check if some parameters have changed (optionnally only check the // parameters that depend on a given client) - bool getChanged(const std::string &client="") const + int getChanged(const std::string &client="") const { std::set<parameter*, parameterLessThan> ps; _getAllParameters(ps); + int changed = 0; for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); it != ps.end(); it++){ - if((*it)->getChanged(client)){ - return true; - } + changed = std::max(changed, (*it)->getChanged(client)); } - return false; + return changed; } // set the changed flag for all the parameters that depend on the given // client (or for all parameters if no client name is provided) - void setChanged(bool changed, const std::string &client="") + void setChanged(int changed, const std::string &client="") { std::set<parameter*, parameterLessThan> ps; _getAllParameters(ps); @@ -662,6 +778,17 @@ namespace onelab{ it != ps.end(); it++) (*it)->setChanged(changed, client); } + void thresholdChanged(int threshold, const std::string &client="") + { + std::set<parameter*, parameterLessThan> ps; + _getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++){ + int changed = (*it)->getChanged(client); + if(changed > threshold) + (*it)->setChanged(threshold, client); + } + } // serialize the parameter space (optionally only serialize those parameters // that depend on the given client) std::vector<std::string> toChar(const std::string &client="") const @@ -695,6 +822,29 @@ namespace onelab{ } return true; } + void toJSON(std::string &json, const std::string &creator="", + const std::string &client="") const + { + time_t now; + time(&now); + std::string t(ctime(&now)); + t.resize(t.size() - 1); + json.clear(); + json += "{ \"onelab\":{\n"; + json += " \"creator\":\"" + creator + "\",\n"; + json += " \"date\":\"" + t + "\",\n"; + json += " \"parameters\":[ \n"; + std::set<parameter*, parameterLessThan> ps; + _getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::const_iterator it = ps.begin(); + it != ps.end(); it++){ + if(client.empty() || (*it)->hasClient(client)){ + if((*it)->getAttribute("NotInDb") != "True") + json += " " + (*it)->toJSON() + "\n"; + } + } + json += "] }\n"; + } }; // The onelab client: a class that communicates with the onelab server. Each @@ -819,14 +969,18 @@ namespace onelab{ c->setId(_clients.size()); } void unregisterClient(client *c){ _clients.erase(c); } - void setChanged(bool changed, const std::string &client="") + void setChanged(int changed, const std::string &client="") { _parameterSpace.setChanged(changed, client); } - bool getChanged(const std::string &client="") + int getChanged(const std::string &client="") { return _parameterSpace.getChanged(client); } + void thresholdChanged(int value, const std::string &client="") + { + _parameterSpace.thresholdChanged(value, client); + } unsigned int getNumParameters(){ return _parameterSpace.getNumParameters(); } std::vector<std::string> toChar(const std::string &client="") { @@ -846,6 +1000,10 @@ namespace onelab{ if(parameter::fromFile(msg, fp)) return fromChar(msg, client); return false; } + void toJSON(std::string &json, const std::string &client="") + { + _parameterSpace.toJSON(json, client); + } }; // A local client, which lives in the same memory space as the server. @@ -945,8 +1103,7 @@ namespace onelab{ _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER, msg.size(), &msg[0]); return true; } - template <class T> bool _get(std::vector<T> &ps, - const std::string &name="") + template <class T> bool _get(std::vector<T> &ps, const std::string &name="") { ps.clear(); if(!_gmshClient) return false;