From 73a8d0700ed1a5b84139807b701646d1e8cede3e Mon Sep 17 00:00:00 2001 From: Ovidiu Mircescu Date: Tue, 5 Feb 2019 16:34:07 +0100 Subject: [PATCH 1/1] Initialization. --- CMakeLists.txt | 47 ++ src/CMakeLists.txt | 2 + src/cpp/CMakeLists.txt | 40 + src/cpp/Exceptions.cxx | 16 + src/cpp/Exceptions.hxx | 18 + src/cpp/Job.hxx | 22 + src/cpp/JobParametersProxy.cxx | 322 ++++++++ src/cpp/JobParametersProxy.hxx | 112 +++ src/cpp/Launcher.hxx | 106 +++ src/cpp/MonoPyJob.cxx | 118 +++ src/cpp/MonoPyJob.hxx | 29 + src/cpp/PyStudyFunction.cxx | 79 ++ src/cpp/PyStudyFunction.hxx | 25 + src/cpp/Sample.hxx | 413 ++++++++++ src/cpp/SamplePyConversions.hxx | 364 +++++++++ src/cpp/StudyFunction.hxx | 22 + src/cpp/TMonoPyJob.hxx | 87 ++ src/cpp/Test/CMakeLists.txt | 36 + src/cpp/Test/SampleTest.cxx | 258 ++++++ src/cpp/Test/SampleTest.hxx | 43 + src/cpp/Test/StudyGeneralTest.cxx | 78 ++ src/cpp/Test/StudyGeneralTest.hxx | 37 + src/cpp/Test/StudyRestartTest.cxx | 63 ++ src/cpp/Test/StudyRestartTest.hxx | 37 + src/cpp/Test/TestMain.cxx | 83 ++ src/cpp/YacsStudyFunction.cxx | 60 ++ src/cpp/YacsStudyFunction.hxx | 26 + src/pydefx/CMakeLists.txt | 13 + src/pydefx/__init__.py | 7 + src/pydefx/configuration.py | 33 + src/pydefx/parameters.py | 37 + src/pydefx/pyscript.py | 69 ++ src/pydefx/pystudy.py | 244 ++++++ src/pydefx/sample.py | 184 +++++ src/pydefx/samplecsviterator.py | 95 +++ src/pydefx/samplecsvmanager.py | 91 +++ src/pydefx/samplemanager.py | 0 src/pydefx/schemas/CMakeLists.txt | 6 + src/pydefx/schemas/idefix_pyschema.xml | 95 +++ src/pydefx/schemas/plugin.py | 76 ++ src/pyexample/findidefixdata.py | 12 + src/pyexample/idefixconfig.json | 5 + src/pyexample/idefixconfig.py | 2 + src/pyexample/idefixdata.csv | 1001 ++++++++++++++++++++++++ src/pyexample/idefixoutputnames.csv | 1 + src/pyexample/idefixstudy.py | 6 + src/pyexample/input.csv | 1001 ++++++++++++++++++++++++ src/pyexample/plugin.py | 106 +++ src/pyexample/runcase.sh | 5 + src/pyexample/scenario.py | 21 + src/pyexample/scenario_study.py | 5 + src/pyexample/syrthes_launch.py | 40 + src/pyexample/temposcen.py | 36 + src/pyexample/tempostudy.py | 6 + src/pyexample/tests1.py | 25 + src/pyexample/tests2.py | 29 + 56 files changed, 5794 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/cpp/CMakeLists.txt create mode 100644 src/cpp/Exceptions.cxx create mode 100644 src/cpp/Exceptions.hxx create mode 100644 src/cpp/Job.hxx create mode 100644 src/cpp/JobParametersProxy.cxx create mode 100644 src/cpp/JobParametersProxy.hxx create mode 100644 src/cpp/Launcher.hxx create mode 100644 src/cpp/MonoPyJob.cxx create mode 100644 src/cpp/MonoPyJob.hxx create mode 100644 src/cpp/PyStudyFunction.cxx create mode 100644 src/cpp/PyStudyFunction.hxx create mode 100644 src/cpp/Sample.hxx create mode 100644 src/cpp/SamplePyConversions.hxx create mode 100644 src/cpp/StudyFunction.hxx create mode 100644 src/cpp/TMonoPyJob.hxx create mode 100644 src/cpp/Test/CMakeLists.txt create mode 100644 src/cpp/Test/SampleTest.cxx create mode 100644 src/cpp/Test/SampleTest.hxx create mode 100644 src/cpp/Test/StudyGeneralTest.cxx create mode 100644 src/cpp/Test/StudyGeneralTest.hxx create mode 100644 src/cpp/Test/StudyRestartTest.cxx create mode 100644 src/cpp/Test/StudyRestartTest.hxx create mode 100644 src/cpp/Test/TestMain.cxx create mode 100644 src/cpp/YacsStudyFunction.cxx create mode 100644 src/cpp/YacsStudyFunction.hxx create mode 100644 src/pydefx/CMakeLists.txt create mode 100644 src/pydefx/__init__.py create mode 100644 src/pydefx/configuration.py create mode 100644 src/pydefx/parameters.py create mode 100644 src/pydefx/pyscript.py create mode 100644 src/pydefx/pystudy.py create mode 100644 src/pydefx/sample.py create mode 100644 src/pydefx/samplecsviterator.py create mode 100644 src/pydefx/samplecsvmanager.py create mode 100644 src/pydefx/samplemanager.py create mode 100644 src/pydefx/schemas/CMakeLists.txt create mode 100644 src/pydefx/schemas/idefix_pyschema.xml create mode 100644 src/pydefx/schemas/plugin.py create mode 100755 src/pyexample/findidefixdata.py create mode 100644 src/pyexample/idefixconfig.json create mode 100644 src/pyexample/idefixconfig.py create mode 100644 src/pyexample/idefixdata.csv create mode 100644 src/pyexample/idefixoutputnames.csv create mode 100644 src/pyexample/idefixstudy.py create mode 100644 src/pyexample/input.csv create mode 100644 src/pyexample/plugin.py create mode 100755 src/pyexample/runcase.sh create mode 100644 src/pyexample/scenario.py create mode 100644 src/pyexample/scenario_study.py create mode 100755 src/pyexample/syrthes_launch.py create mode 100644 src/pyexample/temposcen.py create mode 100644 src/pyexample/tempostudy.py create mode 100644 src/pyexample/tests1.py create mode 100644 src/pyexample/tests2.py diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fc35995 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,47 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(idefix) + +SET (CMAKE_CXX_STANDARD 11) +ENABLE_TESTING() +SET(BUILD_SHARED_LIBS TRUE) + +SET(CONFIGURATION_ROOT_DIR $ENV{CONFIGURATION_ROOT_DIR} CACHE PATH "Path to the Salome CMake configuration files") +IF(EXISTS ${CONFIGURATION_ROOT_DIR}) + LIST(APPEND CMAKE_MODULE_PATH "${CONFIGURATION_ROOT_DIR}/cmake") + INCLUDE(SalomeMacros) +ELSE() + MESSAGE(FATAL_ERROR "We absolutely need the Salome CMake configuration files, please define CONFIGURATION_ROOT_DIR !" +) +ENDIF() + +SET(KERNEL_ROOT_DIR $ENV{KERNEL_ROOT_DIR} CACHE PATH "Path to Salome KERNEL") +IF( EXISTS ${KERNEL_ROOT_DIR} ) + LIST(APPEND CMAKE_MODULE_PATH "${KERNEL_ROOT_DIR}/salome_adm/cmake_files") +ELSE() + MESSAGE(FATAL_ERROR "We absolutely need Salome KERNEL, please define KERNEL_ROOT_DIR") +ENDIF() +FIND_PACKAGE(SalomeKERNEL REQUIRED) +ADD_DEFINITIONS(${KERNEL_DEFINITIONS} ) +INCLUDE_DIRECTORIES(${KERNEL_INCLUDE_DIRS}) + +SET(YACS_ROOT_DIR $ENV{YACS_ROOT_DIR} CACHE PATH "Path to Salome YACS") +IF( EXISTS ${YACS_ROOT_DIR} ) + LIST(APPEND CMAKE_MODULE_PATH "${YACS_ROOT_DIR}/salome_adm/cmake_files") +ELSE() + MESSAGE(FATAL_ERROR "We absolutely need Salome YACS, please define YACS_ROOT_DIR") +ENDIF() +FIND_PACKAGE(SalomeYACS REQUIRED) +ADD_DEFINITIONS(${YACS_DEFINITIONS}) +INCLUDE_DIRECTORIES(${YACS_INCLUDE_DIRS}) + +SET(PY2CPP_ROOT_DIR $ENV{PY2CPP_ROOT_DIR} CACHE PATH "Path to py2cpp") +IF( EXISTS ${PY2CPP_ROOT_DIR} ) + LIST(APPEND CMAKE_MODULE_PATH "${PY2CPP_ROOT_DIR}/lib/cmake/py2cpp") +ELSE() + MESSAGE(FATAL_ERROR "We absolutely need py2cpp, please define PY2CPP_ROOT_DIR") +ENDIF() +FIND_PACKAGE(Py2cpp REQUIRED) + +SET(SALOME_INSTALL_PYTHON "${SALOME_INSTALL_PYTHON}" CACHE PATH "Install Python files") + +ADD_SUBDIRECTORY(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..39a985d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,2 @@ +ADD_SUBDIRECTORY(cpp) +ADD_SUBDIRECTORY(pydefx) diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt new file mode 100644 index 0000000..59a56df --- /dev/null +++ b/src/cpp/CMakeLists.txt @@ -0,0 +1,40 @@ +ADD_DEFINITIONS(${YACS_DEFINITIONS}) +INCLUDE_DIRECTORIES(${YACS_INCLUDE_DIRS}) + +SET(ydefx_SOURCES + PyStudyFunction.cxx + JobParametersProxy.cxx + Exceptions.cxx + MonoPyJob.cxx +) + +SET(ydefx_HEADERS + PyStudyFunction.hxx + StudyFunction.hxx + JobParametersProxy.hxx + Sample.hxx + SamplePyConversions.hxx + Exceptions.hxx + MonoPyJob.hxx + TMonoPyJob.hxx + Job.hxx + Launcher.hxx +) + +SET(ydefx_LINK + py2yacslib + py2cpp +) + +ADD_LIBRARY(ydefx ${ydefx_SOURCES}) +TARGET_LINK_LIBRARIES(ydefx ${ydefx_LINK}) +TARGET_INCLUDE_DIRECTORIES(ydefx PUBLIC + $ + $) + +INSTALL(TARGETS ydefx DESTINATION lib) +INSTALL(TARGETS ydefx EXPORT Findydefx LIBRARY DESTINATION lib) +INSTALL(FILES ${ydefx_HEADERS} DESTINATION include/ydefx) +INSTALL(EXPORT Findydefx DESTINATION lib/cmake/ydefx) + +ADD_SUBDIRECTORY(Test) diff --git a/src/cpp/Exceptions.cxx b/src/cpp/Exceptions.cxx new file mode 100644 index 0000000..abd8d23 --- /dev/null +++ b/src/cpp/Exceptions.cxx @@ -0,0 +1,16 @@ +#include "Exceptions.hxx" + +namespace ydefx +{ + +Exception::Exception(const std::string& message) +: std::exception() +, _message(message) +{ +} + +const char* Exception::what() const noexcept +{ + return _message.c_str(); +} +} diff --git a/src/cpp/Exceptions.hxx b/src/cpp/Exceptions.hxx new file mode 100644 index 0000000..6a336b4 --- /dev/null +++ b/src/cpp/Exceptions.hxx @@ -0,0 +1,18 @@ +#ifndef YDEFX_EXCEPTIONS_HXX +#define YDEFX_EXCEPTIONS_HXX +#include + +namespace ydefx +{ +class Exception:public std::exception +{ +public: + Exception(const std::string& message); + virtual const char* what() const noexcept; +private: + std::string _message; +}; + +} + +#endif // YDEFX_EXCEPTIONS_HXX diff --git a/src/cpp/Job.hxx b/src/cpp/Job.hxx new file mode 100644 index 0000000..64f48a6 --- /dev/null +++ b/src/cpp/Job.hxx @@ -0,0 +1,22 @@ +#ifndef YDEFX_JOB_H +#define YDEFX_JOB_H +#include + +namespace ydefx +{ + +class Job +{ +public: + virtual ~Job(){} + virtual std::string state()=0; + virtual double progress()=0; + virtual std::string dump()=0; + virtual bool launch()=0; // return false when it fails + virtual bool fetch()=0; // return false when it fails + virtual const std::string& lastError()=0; + virtual bool wait()=0; // Wait for the end of the job. Return false when it fails. + virtual void configureWaitDelay(int seconds)=0; +}; +} +#endif // YDEFX_JOB_H diff --git a/src/cpp/JobParametersProxy.cxx b/src/cpp/JobParametersProxy.cxx new file mode 100644 index 0000000..4911043 --- /dev/null +++ b/src/cpp/JobParametersProxy.cxx @@ -0,0 +1,322 @@ +#include "JobParametersProxy.hxx" +#include "Exceptions.hxx" +#include + +PyObject * py2cpp::toPy(const ydefx::JobParametersProxy& jp) +{ + PyObject * result = jp._pyParameters.get(); + Py_IncRef(result); + return result; +} + +namespace ydefx +{ + +JobParametersProxy::JobParametersProxy() +: _pyParameters(nullptr) +{ + py2cpp::PyFunction objConstructor; + objConstructor.loadExp("pydefx", "Parameters"); + _pyParameters = objConstructor(); +} + + +std::string +JobParametersProxy::getAttrString(const std::string& attributeName)const +{ + std::string result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr(attributeName); + return result; +} + +void +JobParametersProxy::setAttr(const std::string& attributeName, + const std::string& value) +{ + _pyParameters.getAttr("salome_parameters") + .setAttr(attributeName, py2cpp::toPyPtr(value)); +} + +int JobParametersProxy::getResAttr(const std::string& attributeName)const +{ + int result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr("resource_required") + .getAttr(attributeName); + return result; +} + +void JobParametersProxy::setResAttr(const std::string& attributeName, int value) +{ + _pyParameters.getAttr("salome_parameters") + .getAttr("resource_required") + .setAttr(attributeName, py2cpp::toPyPtr(value)); +} + +std::string JobParametersProxy::job_name()const +{ + return getAttrString("job_name"); +} + +void JobParametersProxy::job_name(const std::string& v) +{ + setAttr("job_name", v); +} + +std::string JobParametersProxy::job_type()const +{ + return getAttrString("job_type"); +} + +void JobParametersProxy::job_type(const std::string& v) +{ + setAttr("job_type", v); +} + +std::string JobParametersProxy::job_file()const +{ + return getAttrString("job_file"); +} + +void JobParametersProxy::job_file(const std::string& v) +{ + setAttr("job_file", v); +} + +std::string JobParametersProxy::pre_command()const +{ + return getAttrString("pre_command"); +} + +void JobParametersProxy::pre_command(const std::string& v) +{ + setAttr("pre_command", v); +} + +std::string JobParametersProxy::env_file()const +{ + return getAttrString("env_file"); +} + +void JobParametersProxy::env_file(const std::string& v) +{ + setAttr("env_file", v); +} + +std::list JobParametersProxy::in_files()const +{ + std::list result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr("in_files"); + return result; +} + +void JobParametersProxy::add_in_files(const std::list& pathList) +{ + std::list newList = in_files(); + newList.insert(newList.end(), pathList.begin(), pathList.end()); + in_files(newList); +} + +void JobParametersProxy::in_files(const std::list& pathList) +{ + _pyParameters.getAttr("salome_parameters") + .setAttr("in_files", py2cpp::toPyPtr(pathList)); +} + +std::string JobParametersProxy::work_directory()const +{ + return getAttrString("work_directory"); +} + +void JobParametersProxy::work_directory(const std::string& v) +{ + setAttr("work_directory", v); +} + +std::string JobParametersProxy::local_directory()const +{ + return getAttrString("local_directory"); +} + +void JobParametersProxy::local_directory(const std::string& v) +{ + setAttr("local_directory", v); +} + +std::string JobParametersProxy::result_directory()const +{ + return getAttrString("result_directory"); +} + +void JobParametersProxy::result_directory(const std::string& v) +{ + setAttr("result_directory", v); +} + +std::string JobParametersProxy::maximum_duration()const +{ + return getAttrString("maximum_duration"); +} + +void JobParametersProxy::maximum_duration(const std::string& v) +{ + setAttr("maximum_duration", v); +} + +// ResourceParameters +std::string JobParametersProxy::resource_name()const +{ + std::string result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr("resource_required") + .getAttr("name"); + return result; +} + +void JobParametersProxy::resource_name(const std::string& name) +{ + _pyParameters.getAttr("salome_parameters") + .getAttr("resource_required") + .setAttr("name", py2cpp::toPyPtr(name)); +} + +int JobParametersProxy::nb_proc()const +{ + return getResAttr("nb_proc"); +} + +void JobParametersProxy::nb_proc(int v) +{ + setResAttr("nb_proc", v); +} + +int JobParametersProxy::mem_mb()const +{ + return getResAttr("mem_mb"); +} + +void JobParametersProxy::mem_mb(int v) +{ + setResAttr("mem_mb", v); +} + +int JobParametersProxy::nb_node()const +{ + return getResAttr("nb_node"); +} + +void JobParametersProxy::nb_node(int v) +{ + setResAttr("nb_node", v); +} + +int JobParametersProxy::nb_proc_per_node()const +{ + return getResAttr("nb_proc_per_node"); +} + +void JobParametersProxy::nb_proc_per_node(int v) +{ + setResAttr("nb_proc_per_node", v); +} + +std::string JobParametersProxy::queue()const +{ + return getAttrString("queue"); +} + +void JobParametersProxy::queue(const std::string& v) +{ + setAttr("queue", v); +} + +std::string JobParametersProxy::partition()const +{ + return getAttrString("partition"); +} + +void JobParametersProxy::partition(const std::string& v) +{ + setAttr("partition", v); +} + +bool JobParametersProxy::exclusive()const +{ + bool result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr("exclusive"); + return result; +} + +void JobParametersProxy::exclusive(bool v) +{ + _pyParameters.getAttr("salome_parameters") + .setAttr("exclusive", py2cpp::toPyPtr(v)); +} + +unsigned int JobParametersProxy::mem_per_cpu()const +{ + int result; + py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters") + .getAttr("mem_per_cpu"); + if(result < 0) + result = 0; + return result; +} + +void JobParametersProxy::mem_per_cpu(unsigned int v) +{ + _pyParameters.getAttr("salome_parameters") + .setAttr("mem_per_cpu", py2cpp::toPyPtr(v)); +} + +std::string JobParametersProxy::wckey()const +{ + return getAttrString("wckey"); +} + +void JobParametersProxy::wckey(const std::string& v) +{ + setAttr("wckey", v); +} + +std::string JobParametersProxy::extra_params()const +{ + return getAttrString("extra_params"); +} + +void JobParametersProxy::extra_params(const std::string& v) +{ + setAttr("extra_params", v); +} + + +unsigned int JobParametersProxy::nb_branches()const +{ + unsigned int result; + py2cpp::pyResult(result) = _pyParameters.getAttr("nb_branches"); + return result; +} + +void JobParametersProxy::nb_branches(unsigned int v) +{ + _pyParameters.setAttr("nb_branches", py2cpp::toPyPtr(v)); +} + +void JobParametersProxy::configureResource(const std::string& resourceName) +{ + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyParameters, "configureResource"); + pyFn(resourceName); +} + +void JobParametersProxy::createResultDirectory(const std::string& basePath) +{ + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyParameters, "createResultDirectory"); + pyFn(basePath); +} + +} diff --git a/src/cpp/JobParametersProxy.hxx b/src/cpp/JobParametersProxy.hxx new file mode 100644 index 0000000..6392713 --- /dev/null +++ b/src/cpp/JobParametersProxy.hxx @@ -0,0 +1,112 @@ +#ifndef YDEFX_JOBPARAMETERSPROXY_H +#define YDEFX_JOBPARAMETERSPROXY_H +#include +#include +#include +#include + +namespace ydefx +{ +class JobParametersProxy; +} +namespace py2cpp +{ +PyObject * toPy(const ydefx::JobParametersProxy& jp); +} + +#include + +namespace ydefx +{ +class JobParametersProxy +{ +public: + JobParametersProxy(); + + std::string job_name()const; + void job_name(const std::string& v); + + std::string job_type()const; + void job_type(const std::string& v); + + std::string job_file()const; + void job_file(const std::string& v); + + std::string pre_command()const; + void pre_command(const std::string& v); + + std::string env_file()const; + void env_file(const std::string& v); + + std::list in_files()const; + void add_in_files(const std::list& pathList); + void in_files(const std::list& pathList); + + std::string work_directory()const; + void work_directory(const std::string& v); + + std::string local_directory()const; + void local_directory(const std::string& v); + + std::string result_directory()const; + void result_directory(const std::string& v); + + std::string maximum_duration()const; + void maximum_duration(const std::string& v); + + // ResourceParameters + std::string resource_name()const; + void resource_name(const std::string& name); + + int nb_proc()const; + void nb_proc(int v); + + int mem_mb()const; + void mem_mb(int v); + + int nb_node()const; + void nb_node(int v); + + int nb_proc_per_node()const; + void nb_proc_per_node(int v); + + std::string queue()const; + void queue(const std::string& v); + + std::string partition()const; + void partition(const std::string& v); + + bool exclusive()const; + void exclusive(bool v); + + unsigned int mem_per_cpu()const; + void mem_per_cpu(unsigned int v); + + std::string wckey()const; + void wckey(const std::string& v); + + std::string extra_params()const; + void extra_params(const std::string& v); + + unsigned int nb_branches()const; + void nb_branches(unsigned int v); + + /*! + * Set the resource and the default values associated with that resource: + * work_directory, nb_branches, nb_proc and wckey. + */ + void configureResource(const std::string& resourceName); + //! create a new result directory as a subdirectory of the given path. + void createResultDirectory(const std::string& basePath); + friend PyObject * py2cpp::toPy(const JobParametersProxy& jp); + +private: + py2cpp::PyPtr _pyParameters; +private: + std::string getAttrString(const std::string& attributeName)const; + void setAttr(const std::string& attributeName, const std::string& value); + int getResAttr(const std::string& attributeName)const; + void setResAttr(const std::string& attributeName, int value); +}; +} +#endif // YDEFX_JOBPARAMETERSPROXY_H diff --git a/src/cpp/Launcher.hxx b/src/cpp/Launcher.hxx new file mode 100644 index 0000000..8ba62da --- /dev/null +++ b/src/cpp/Launcher.hxx @@ -0,0 +1,106 @@ +#ifndef YDEFX_LAUNCHER_H +#define YDEFX_LAUNCHER_H + +#include "TMonoPyJob.hxx" + +namespace ydefx +{ +class Job; +class Launcher +{ +public: + Launcher():_lastError(){} + const std::string& lastError()const{return _lastError;} + + /*! + * Create and submit a new job. + * Return nullptr in case of failure. Check the error with lastError(). + */ + template + Job* submitMonoPyJob(const PyStudyFunction& fnScript, + Sample& sample, + const JobParametersProxy& params); + + /*! + * Connect to an already created job. + * Return nullptr in case of failure. Check the error with lastError(). + */ + template + Job* connectJob(const std::string& jobDump, Sample& sample); + + template + Job* submitMultiPyJob(const PyStudyFunction& fn, + Sample& sample, + const JobParametersProxy& params); + +private: + std::string _lastError; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Template implementations +//////////////////////////////////////////////////////////////////////////////// + +template +Job* Launcher::submitMonoPyJob(const PyStudyFunction& fnScript, + Sample& sample, + const JobParametersProxy& params) +{ + Job* result = nullptr; + _lastError = ""; + try + { + result = new TMonoPyJob(fnScript, sample, params); + } + catch(std::exception& e) + { + if(result != nullptr) + delete result; + result = nullptr; + _lastError = e.what(); + return result; + } + + if(!result->launch()) + { + _lastError = "Failed to submit job.\n"; + _lastError += result->lastError(); + delete result; + result = nullptr; + } + return result; +} + +template +Job* Launcher::connectJob(const std::string& jobDump, + Sample& sample) +{ + Job* result = nullptr; + _lastError = ""; + try + { + result = new TMonoPyJob(jobDump, sample); + } + catch(std::exception& e) + { + if(result != nullptr) + delete result; + result = nullptr; + _lastError = e.what(); + } + + return result; +} + +template +Job* Launcher::submitMultiPyJob(const PyStudyFunction& fn, + Sample& sample, + const JobParametersProxy& params) +{ + // TODO + _lastError = "Not implemented!"; + return nullptr; +} + +} +#endif // YDEFX_LAUNCHER_H diff --git a/src/cpp/MonoPyJob.cxx b/src/cpp/MonoPyJob.cxx new file mode 100644 index 0000000..3ff315d --- /dev/null +++ b/src/cpp/MonoPyJob.cxx @@ -0,0 +1,118 @@ +#include "MonoPyJob.hxx" +#include + +namespace ydefx +{ +MonoPyJob::MonoPyJob() +: _pyStudy() +, _lastError() +, _waitDelay(10) +{ + py2cpp::PyFunction objConstructor; + objConstructor.loadExp("pydefx", "PyStudy"); + _pyStudy = objConstructor(); +} + +MonoPyJob::~MonoPyJob() +{ +} + +std::string MonoPyJob::state() +{ + std::string result; + _lastError = ""; + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "getJobState"); + py2cpp::pyResult(result) = pyFn(); + } + catch(std::exception& e) + { + _lastError = "An error occured while retrieving job's state.\n"; + _lastError += e.what(); + } + return result; +} + +double MonoPyJob::progress() +{ + double result; + py2cpp::PyFunction pyFn; + _lastError = ""; + try + { + pyFn.loadExp(_pyStudy, "getProgress"); + py2cpp::pyResult(result) = pyFn(); + } + catch(std::exception& e) + { + _lastError = "An error occured while retrieving job's progress.\n"; + _lastError += e.what(); + } + return result; +} + +std::string MonoPyJob::dump() +{ + std::string result; + _lastError = ""; + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "dump"); + py2cpp::pyResult(result) = pyFn(); + } + catch(std::exception& e) + { + _lastError = "An error occured while dumping the job.\n"; + _lastError += e.what(); + } + return result; +} + +bool MonoPyJob::launch() +{ + _lastError = ""; + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "launch"); + pyFn(); + } + catch(std::exception& e) + { + _lastError = "An error occured while launching the job.\n"; + _lastError += e.what(); + } + return _lastError.empty(); +} + +const std::string& MonoPyJob::lastError() +{ + return _lastError; +} + +bool MonoPyJob::wait() +{ + _lastError = ""; + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "wait"); + pyFn(_waitDelay); + } + catch(std::exception& e) + { + _lastError = "An error occured while waiting the end of the job.\n"; + _lastError += e.what(); + } + return _lastError.empty(); +} + +void MonoPyJob::configureWaitDelay(int seconds) +{ + _waitDelay = seconds; +} + +} diff --git a/src/cpp/MonoPyJob.hxx b/src/cpp/MonoPyJob.hxx new file mode 100644 index 0000000..370ff32 --- /dev/null +++ b/src/cpp/MonoPyJob.hxx @@ -0,0 +1,29 @@ +#ifndef YDEFX_MONOPYJOB_HXX +#define YDEFX_MONOPYJOB_HXX +#include "Job.hxx" +#include + +namespace ydefx +{ +class MonoPyJob : public Job +{ +public: + MonoPyJob(); + virtual ~MonoPyJob(); + virtual std::string state(); + virtual double progress(); + virtual std::string dump(); + virtual bool launch(); // return false when it fails + virtual bool fetch()=0; // return false when it fails + virtual const std::string& lastError(); + virtual bool wait(); // Wait for the end of the job. Return false when it fails. + virtual void configureWaitDelay(int seconds); +protected: + py2cpp::PyPtr _pyStudy; + std::string _lastError; + int _waitDelay; +}; + +} + +#endif //YDEFX_MONOPYJOB_HXX diff --git a/src/cpp/PyStudyFunction.cxx b/src/cpp/PyStudyFunction.cxx new file mode 100644 index 0000000..88e6e2d --- /dev/null +++ b/src/cpp/PyStudyFunction.cxx @@ -0,0 +1,79 @@ +#include "PyStudyFunction.hxx" +#include +#include +#include + +namespace ydefx +{ +PyStudyFunction::PyStudyFunction() +: _content() +, _input() +, _output() +, _errors() +{ + _errors.push_back("Function \"_exec\" not found!"); +} + +PyStudyFunction::~PyStudyFunction(){} + +void PyStudyFunction::loadFile(const std::string& path) +{ + std::ifstream infile(path.c_str()); + std::stringstream buffer; + buffer << infile.rdbuf(); + loadString(buffer.str()); +} + +void PyStudyFunction::loadString(const std::string& value) +{ + _content = value; + _input.clear(); + _output.clear(); + _errors.clear(); + // TODO: use py2yacs + Py2yacs p2y; + p2y.load(value); + if(p2y.getGlobalErrors().size() == 0) + { + const std::list& fn_prop = p2y.getFunctionProperties(); + std::list::const_iterator prop_it = fn_prop.begin(); + while(prop_it != fn_prop.end() && prop_it->_name != "_exec") + ++ prop_it; + if(prop_it != fn_prop.end() && prop_it->_errors.empty()) + { + for(const std::string& name: prop_it->_input_ports) + _input.push_back(name); + for(const std::string& name: prop_it->_output_ports) + _output.push_back(name); + } + } + // TODO: deal with the errors +} + +void PyStudyFunction::save(const std::string& path) +{ + std::ofstream outfile(path.c_str()); + outfile << _content; +} + +const std::string& PyStudyFunction::content()const +{ + return _content; +} + +const std::list& PyStudyFunction::inputNames()const +{ + return _input; +} + +const std::list& PyStudyFunction::outputNames()const +{ + return _output; +} + +const std::list& PyStudyFunction::errors()const +{ + return _errors; +} + +} diff --git a/src/cpp/PyStudyFunction.hxx b/src/cpp/PyStudyFunction.hxx new file mode 100644 index 0000000..6646b79 --- /dev/null +++ b/src/cpp/PyStudyFunction.hxx @@ -0,0 +1,25 @@ +#ifndef YDEFX_PYSTUDYFUNCTION_H +#define YDEFX_PYSTUDYFUNCTION_H +#include "StudyFunction.hxx" +namespace ydefx +{ +class PyStudyFunction : StudyFunction +{ +public: + PyStudyFunction(); + virtual ~PyStudyFunction(); + virtual void loadFile(const std::string& path); + virtual void loadString(const std::string&); + virtual void save(const std::string& path); + virtual const std::string& content()const; + virtual const std::list& inputNames()const; + virtual const std::list& outputNames()const; + virtual const std::list& errors()const; +private: + std::string _content; + std::list _input; + std::list _output; + std::list _errors; +}; +} +#endif // YDEFX_PYSTUDYFUNCTION_H diff --git a/src/cpp/Sample.hxx b/src/cpp/Sample.hxx new file mode 100644 index 0000000..47cafa8 --- /dev/null +++ b/src/cpp/Sample.hxx @@ -0,0 +1,413 @@ +#ifndef YDEFX_SAMPLE_H +#define YDEFX_SAMPLE_H + +#include +#include +#include +#include + +namespace ydefx +{ + +template +class VariablesGroup +{ +public: + VariablesGroup(); + VariablesGroup( const T& defaultValue); + std::list names()const; + bool isSet(const std::string& name, std::size_t index)const; + /*! Get the values for a variable name. + * Pay attention to default values. Use isSet function in order to know + * if the value has been set. + */ + const std::vector& get(const std::string& name)const; + const T& get(const std::string& name, std::size_t index)const; + void set(const std::string& name, const std::vector& values); + void set(const std::string& name, std::size_t index, const T& value); + void clear(); //! names and values are cleared. + void clearValues(); //! values are cleared, but names are kept. + void resetNames(const std::list& names); + void removeName(const std::string& name); + void addName(const std::string& name); + void unset(const std::string& name, std::size_t index); + bool isPointSet(std::size_t index)const; //! every variable is set + void setDefault(const T& defaultValue) {_defaultValue = defaultValue;} + std::size_t maxSize()const; +private: + std::map > _variables; + std::map > _status; + T _defaultValue; +}; + +enum class ExecutionState {NOTEXECUTED, DONE, ERROR}; + +template +class OneTypeSample +{ +public: + const VariablesGroup& inputs()const; + VariablesGroup& inputs(); + const VariablesGroup& outputs()const; + VariablesGroup& outputs(); +protected: + bool isPointSet(int index); + void unset(const std::string& name, std::size_t index); +private: + VariablesGroup _inputs; + VariablesGroup _outputs; +}; + +template class Sample; + +// no type sample +template <> +class Sample<> +{ +public: + const std::vector& errors()const{return _errors;} + std::vector& errors(){return _errors;} + void clearErrors(){_errors.clear();} + void setError(std::size_t index, const std::string& message) + { + if(index >= _errors.size()) + _errors.resize(index+1); + _errors[index] = message; + } + + std::string getError(std::size_t index)const + { + std::string result(""); + if(index < _errors.size()) + result = _errors[index]; + return result; + } + + virtual ExecutionState pointState(int index) + { + ExecutionState result = ExecutionState::DONE; + if(index < _errors.size()) + if(!_errors[index].empty()) + result = ExecutionState::ERROR; + return result; + } + virtual void clear(){_errors.clear();} + virtual void clearInputValues(){} + virtual void clearOutputValues(){_errors.clear();} + + virtual std::list allInputNames()const + {return std::list();} + + virtual std::list allOutputNames()const + {return std::list();} + + virtual void unset(const std::string& name, std::size_t index){} + virtual std::size_t maxSize()const {return 0;} + +private: + std::vector _errors; +}; + +// multi type sample +template +class Sample : public OneTypeSample, public Sample +{ +public: + virtual ExecutionState pointState(int index); + virtual void clear(); //! names and values are cleared. + virtual void clearInputValues(); //! values are cleared, but names are kept. + virtual void clearOutputValues(); //! values are cleared, but names are kept. + virtual std::list allInputNames()const; + virtual std::list allOutputNames()const; + virtual void unset(const std::string& name, std::size_t index); + virtual std::size_t maxSize()const; //! outputs ignored! + + template + const VariablesGroup& inputs()const + {return OneTypeSample::inputs();} + + template + VariablesGroup& inputs() + {return OneTypeSample::inputs();} + + template + const VariablesGroup& outputs()const + {return OneTypeSample::outputs();} + + template + VariablesGroup& outputs() + {return OneTypeSample::outputs();} + +}; + +// Sample +template +ExecutionState Sample::pointState(int index) +{ + ExecutionState result = Sample::pointState(index); + if(result == ExecutionState::DONE) + if(!OneTypeSample::isPointSet(index)) + result = ExecutionState::NOTEXECUTED; + return result; +} + +template +void Sample::clear() +{ + OneTypeSample::inputs().clear(); + OneTypeSample::outputs().clear(); + Sample::clear(); +} + +template +void Sample::clearInputValues() +{ + OneTypeSample::inputs().clearValues(); + Sample::clearInputValues(); +} + +template +void Sample::clearOutputValues() +{ + OneTypeSample::outputs().clearValues(); + Sample::clearOutputValues(); +} + +template +std::list Sample::allInputNames()const +{ + std::list result = OneTypeSample::inputs().names(); + result.splice(result.end(), Sample::allInputNames()); + return result; +} + +template +std::list Sample::allOutputNames()const +{ + std::list result = OneTypeSample::outputs().names(); + result.splice(result.end(),Sample::allOutputNames()); + return result; +} + +template +void Sample::unset(const std::string& name, std::size_t index) +{ + OneTypeSample::unset(name, index); + Sample::unset(name, index); +} + +template +std::size_t Sample::maxSize()const +{ + std::size_t result=OneTypeSample::inputs().maxSize(); + std::size_t superR=Sample::maxSize(); + if(superR > result) + result = superR; + return result; +} + +// VariablesGroup + +template +VariablesGroup::VariablesGroup( ) +: _variables() +, _status() +, _defaultValue(T()) +{ +} + +template +VariablesGroup::VariablesGroup( const T& defaultValue) +: _variables() +, _status() +, _defaultValue(defaultValue) +{ +} + +template +std::list VariablesGroup::names()const +{ + std::list result; + for(const auto& it : _variables) + result.push_back(it.first); + return result; +} + +template +bool VariablesGroup::isSet(const std::string& name, std::size_t index)const +{ + bool result = false; + auto it = _status.find(name); + if(it != _status.end()) + if(index < it->second.size()) + result = it->second[index]; + return result; +} + +template +bool VariablesGroup::isPointSet(std::size_t index)const +{ + bool allSet = true; + std::map >::const_iterator it; + for( it = _status.begin(); it != _status.end() && allSet; it++) + allSet = index < it->second.size() && it->second[index]; + return allSet; +} + +template +std::size_t VariablesGroup::maxSize()const +{ + std::size_t result = 0; + for(const auto& it : _variables) + if(result < it.second.size()) + result = it.second.size(); + return result; +} + +template +const std::vector& VariablesGroup::get(const std::string& name)const +{ + static const std::vector emptyVector; + auto it = _variables.find(name); + if(it != _variables.end()) + return it->second; + else + return emptyVector; +} + +template +const T& VariablesGroup::get(const std::string& name, std::size_t index)const +{ + auto it = _variables.find(name); + if(it != _variables.end()) + if(index < it->second.size()) + return it->second[index]; + return _defaultValue; +} + +template +void VariablesGroup::set(const std::string& name, const std::vector& values) +{ + _variables[name] = values; + std::vector& status = _status[name]; + status.clear(); + status.insert(status.begin(), values.size(), true); +} + +template +void VariablesGroup::set(const std::string& name, std::size_t index, const T& value) +{ + std::vector& values = _variables[name]; + std::vector& status = _status[name]; + if(index >= values.size()) + { + values.resize(index+1, _defaultValue); + status.resize(index+1, false); + } + values[index] = value; + status[index] = true; +} + +template +void VariablesGroup::clear() +{ + _variables.clear(); + _status.clear(); +} + +template +void VariablesGroup::clearValues() +{ + for(auto& it : _variables) + { + it.second.clear(); + } + for(auto& it : _status) + { + it.second.clear(); + } +} + +template +void VariablesGroup::resetNames(const std::list& names) +{ + clear(); + for(const auto& it : names) + { + _variables[it].clear(); + _status[it].clear(); + } +} + +template +void VariablesGroup::removeName(const std::string& name) +{ + _variables.erase(name); + _status.erase(name); +} + +template +void VariablesGroup::addName(const std::string& name) +{ + _variables[name]; + _status[name]; +} + +template +void VariablesGroup::unset(const std::string& name, std::size_t index) +{ + auto it = _status.find(name); + if(it != _status.end()) + if(index < it->second.size()) + { + it->second[index] = false; + if(index+1 == it->second.size()) + { + // remove all the unset values at the end of the list + while(index>0 && !it->second[index]) + --index; + it->second.resize(index); + _variables[name].resize(index); + } + } +} + +// OneTypeSample +template +const VariablesGroup& OneTypeSample::inputs()const +{ + return _inputs; +} + +template +VariablesGroup& OneTypeSample::inputs() +{ + return _inputs; +} + +template +const VariablesGroup& OneTypeSample::outputs()const +{ + return _outputs; +} + +template +VariablesGroup& OneTypeSample::outputs() +{ + return _outputs; +} + +template +bool OneTypeSample::isPointSet(int index) +{ + return _inputs.isPointSet(index) && _outputs.isPointSet(index); +} + +template +void OneTypeSample::unset(const std::string& name, std::size_t index) +{ + _outputs.unset(name, index); +} + +}//namespace ydefx +#endif // YDEFX_SAMPLE_H diff --git a/src/cpp/SamplePyConversions.hxx b/src/cpp/SamplePyConversions.hxx new file mode 100644 index 0000000..5d39d36 --- /dev/null +++ b/src/cpp/SamplePyConversions.hxx @@ -0,0 +1,364 @@ +#ifndef YDEFX_SAMPLEPYCONVERSIONS_HXX +#define YDEFX_SAMPLEPYCONVERSIONS_HXX + +#include +#include "Sample.hxx" +#include "Exceptions.hxx" + +namespace ydefx +{ +/*! + * The goal of this class is to iterate over the types of a sample. + * S : template type of the sample. + * For the highest level call, this is Sample. + * Ts : list of types managed by this converter. This list is included in the + * list of sample types. + */ +template +class SamplePyConverter; + +/*! + * Convert input variables of a sample to a python dictionary. + * All input variables should have the same size. No verification is done here. + * The returned value is NULL in case of error. + */ +template +PyObject* inputToPy(const Sample& sample); + +/*! + * Fill the input values of the sample from a python dictionary. + * The sample should already contain the names of the input values. + * For each type in the sample, for each variable name of the iterated type, + * fetch the values from the python dictionary and put them in the c++ + * container. The old input values of the sample are cleared. + * The returned value contains the conversion errors if they exist. + * Keys from the python dictionary that do not exist in the sample are ignored. + */ +template +py2cpp::ConversionCheck inputFromPy(PyObject* obj, Sample& sample); + +/*! + * Convert output variables of a sample to a python dictionary. + * Some points could have unset output values. The python dictionary will have + * None values for the unset points. + * The size of the lists of values in the python dictionary will be the same for + * every variable name. It will be sample.maxSize(). + * The returned value is NULL in case of error. + */ +template +PyObject* outputToPy(const Sample& sample); + +/*! + * Fill the output values of a sample from a python dictionary. + * The sample should already contain the keys (variable names) of the output + * values. The python dictionary may contain None values. A None value means the + * value is not set. + * Keys of the python dictionary which do not exist in the c++ sample are + * ignored. + */ +template +py2cpp::ConversionCheck outputFromPy(PyObject* obj, Sample& sample); + +/*! + * Fill the error values of a sample from a python list of strings. + * The python list may contain None values which are translated to empty + * strings. + */ +template +py2cpp::ConversionCheck errorsFromPy(PyObject* obj, Sample& sample); + +/*! + * A python sample object is created and it contains input and output names of + * variables. It also contains input values, but output values and errors are + * not copied. + */ +template +py2cpp::PyPtr createPySample(const Sample& sample); + +/*! + * Fetch output values and errors from the python objet. + * The c++ sample should already contain the names of variables when this + * function is called. + */ +template +py2cpp::ConversionCheck fetchResults(PyObject* obj, Sample& sample); + +//////////////////////////////////////////////////////////////////////////////// +// Template implementations +//////////////////////////////////////////////////////////////////////////////// + +template +class SamplePyConverter +{ +public: + bool inputToPy(const S& sample, PyObject* result){return true;} + py2cpp::ConversionCheck inputFromPy(PyObject* obj, S& sample) + { return py2cpp::ConversionCheck();} + bool outputToPy(const S& sample, PyObject* result){return true;} + py2cpp::ConversionCheck outputFromPy(PyObject* obj, S& sample) + { return py2cpp::ConversionCheck();} +}; + +template +class SamplePyConverter : public SamplePyConverter +{ +public: + /*! Add sample.inputs to result. + * result should be a python dictionary. + */ + bool inputToPy(const S& sample, PyObject* result) + { + bool ok = true; + std::list names = sample.OneTypeSample::inputs().names(); + for(const std::string& name : names) + { + const std::vector& values = sample.OneTypeSample::inputs().get(name); + py2cpp::PyPtr pyValue(py2cpp::toPy(values)); + ok = PyDict_SetItemString(result, name.c_str(), pyValue.get()) == 0; + if(!ok) + break; + } + if(ok) + ok = SamplePyConverter::inputToPy(sample, result); + return ok; + } + + py2cpp::ConversionCheck inputFromPy(PyObject* obj, S& sample) + { + py2cpp::ConversionCheck check; + std::list names = sample.OneTypeSample::inputs().names(); + for(const std::string& name : names) + { + PyObject* pyValues = PyDict_GetItemString(obj, name.c_str()); + if(!pyValues) + { + std::string error = "The key '"; + error += name; + error += "' was not found in the python object.\n"; + check.addErrorMessage(error); + } + else if(PyList_Check(pyValues)) + { + std::size_t size = PyList_Size(pyValues); + std::size_t i=size; + // We prefer to fill the values from highest index to 0 in order + // to avoid the resize of the vector when the index grows. + bool stop = (i == 0) || !check; + while(!stop) + { + i--; + T value; + PyObject* pyVal = PyList_GetItem(pyValues, i); + check.addError(py2cpp::fromPy(pyVal, value)); + if(check) + sample.OneTypeSample::inputs().set(name, i, value); + stop = (i == 0) || !check; + } + } + else + check.addError("sample input list", obj); + } + if(check) + check = SamplePyConverter::inputFromPy(obj, sample); + return check; + } + + bool outputToPy(const S& sample, PyObject* result) + { + bool ok = true; + std::size_t maxsize = sample.maxSize(); + std::list names = sample.OneTypeSample::outputs().names(); + for(const std::string& name : names) + { + py2cpp::PyPtr newList( PyList_New(maxsize)); + if(newList == nullptr) + { + ok = false; + break; + } + for(std::size_t i=0; i::outputs().isSet(name, i)) + { + const T& cppValue = sample.OneTypeSample::outputs().get(name,i); + PyObject * pyValue = py2cpp::toPy(cppValue); + PyList_SetItem(newList.get(), i, pyValue); + } + else + { + Py_INCREF(Py_None); + PyList_SetItem(newList.get(), i, Py_None); + } + } + ok = PyDict_SetItemString(result, name.c_str(), newList.get()) == 0; + if(!ok) + break; + } + if(ok) + ok = SamplePyConverter::outputToPy(sample, result); + return ok; + } + + py2cpp::ConversionCheck outputFromPy(PyObject* obj, S& sample) + { + py2cpp::ConversionCheck check; + std::list names = sample.OneTypeSample::outputs().names(); + for(const std::string& name : names) + { + PyObject* pyValues = PyDict_GetItemString(obj, name.c_str()); + if(!pyValues) + { + std::string error = "The key '"; + error += name; + error += "' was not found in the python object.\n"; + check.addErrorMessage(error); + } + else if(PyList_Check(pyValues)) + { + std::size_t size = PyList_Size(pyValues); + std::size_t i=size; + // We prefer to fill the values from highest index to 0 in order + // to avoid the resize of the vector when the index grows. + bool stop = (i == 0) || !check; + while(!stop) + { + i--; + PyObject* pyVal = PyList_GetItem(pyValues, i); + if(Py_None == pyVal) + { + sample.OneTypeSample::outputs().unset(name, i); + } + else + { + T value; + check.addError(py2cpp::fromPy(pyVal, value)); + if(check) + sample.OneTypeSample::outputs().set(name, i, value); + } + stop = (i == 0) || !check; + } + } + else + check.addError("sample input list", obj); + } + if(check) + check = SamplePyConverter::outputFromPy(obj, sample); + return check; + } +}; + +template +PyObject* inputToPy(const Sample& sample) +{ + PyObject * result = PyDict_New(); + if(result) + { + SamplePyConverter, Ts...> converter; + if(! converter.inputToPy(sample, result)) + { + Py_XDECREF(result); + result = nullptr; + } + } + // TODO: errors + return result; +} + +template +py2cpp::ConversionCheck inputFromPy(PyObject* obj, Sample& sample) +{ + py2cpp::ConversionCheck check; + if(PyDict_Check(obj)) + { + sample.clearInputValues(); + SamplePyConverter, Ts...> converter; + check.addError(converter.inputFromPy(obj,sample)); + } + else + check.addError("Sample input", obj); + + return check; +} + +template +PyObject* outputToPy(const Sample& sample) +{ + PyObject * result = PyDict_New(); + if(result) + { + SamplePyConverter, Ts...> converter; + if(! converter.outputToPy(sample, result)) + { + Py_XDECREF(result); + result = nullptr; + } + } + return result; +} + +template +py2cpp::ConversionCheck outputFromPy(PyObject* obj, Sample& sample) +{ + py2cpp::ConversionCheck check; + if(PyDict_Check(obj)) + { + sample.clearOutputValues(); + SamplePyConverter, Ts...> converter; + check.addError(converter.outputFromPy(obj,sample)); + } + else + check.addError("Sample output", obj); + return check; +} + +template +py2cpp::ConversionCheck errorsFromPy(PyObject* obj, Sample& sample) +{ + py2cpp::ConversionCheck check; + if(PyList_Check(obj)) + { + sample.clearErrors(); + std::size_t size = PyList_Size(obj); + sample.errors().resize(size); + for(std::size_t i=0; i < size && check; i++) + { + PyObject* pyValue = PyList_GetItem(obj, i); + // Py_None values are translated to empty strings in c++. + std::string cppValue(""); + if(Py_None != pyValue) + check.addError(py2cpp::fromPy(pyValue, cppValue)); + if(check) + sample.errors().push_back(cppValue); + } + } + else + check.addError("Sample error", obj); + return check; +} + +template +py2cpp::PyPtr createPySample(const Sample& sample) +{ + py2cpp::PyFunction sampleConstructor; + sampleConstructor.loadExp("pydefx", "Sample"); + py2cpp::PyPtr result = sampleConstructor(sample.allInputNames(), + sample.allOutputNames()); + + py2cpp::PyFunction setInputValues; + setInputValues.loadExp(result, "setInputValues"); + setInputValues(inputToPy(sample)); + + return result; +} + +template +py2cpp::ConversionCheck fetchResults(const py2cpp::PyPtr& obj, Sample& sample) +{ + py2cpp::ConversionCheck check; + check.addError(outputFromPy(obj.getAttr("_output").get(), sample)); + check.addError(errorsFromPy(obj.getAttr("_messages").get(), sample)); + return check; +} + +} +#endif //YDEFX_SAMPLEPYCONVERSIONS_HXX diff --git a/src/cpp/StudyFunction.hxx b/src/cpp/StudyFunction.hxx new file mode 100644 index 0000000..c58f9ef --- /dev/null +++ b/src/cpp/StudyFunction.hxx @@ -0,0 +1,22 @@ +#ifndef YDEFX_STUDYFUNCTION_H +#define YDEFX_STUDYFUNCTION_H +#include +#include + +namespace ydefx +{ +class StudyFunction +{ +public: + virtual ~StudyFunction(){} + virtual void loadFile(const std::string& path)=0; + virtual void loadString(const std::string&)=0; + virtual void save(const std::string& path)=0; + virtual const std::string& content()const=0; + virtual const std::list& inputNames()const=0; + virtual const std::list& outputNames()const=0; + virtual const std::list& errors()const=0; + std::list datafiles; +}; +} +#endif // YDEFX_STUDYFUNCTION_H diff --git a/src/cpp/TMonoPyJob.hxx b/src/cpp/TMonoPyJob.hxx new file mode 100644 index 0000000..4351e5e --- /dev/null +++ b/src/cpp/TMonoPyJob.hxx @@ -0,0 +1,87 @@ +#ifndef YDEFX_TMONOPYJOB_HXX +#define YDEFX_TMONOPYJOB_HXX +#include "JobParametersProxy.hxx" +#include "MonoPyJob.hxx" +#include "Sample.hxx" +#include "SamplePyConversions.hxx" +#include "PyStudyFunction.hxx" +#include + +namespace ydefx +{ +template +class TMonoPyJob : public MonoPyJob +{ +public: + //! Create a new job. + TMonoPyJob(const PyStudyFunction& fnScript, + Sample& sample, + const JobParametersProxy& params) + : MonoPyJob() + , _sample(sample) + { + if(_lastError.empty()) // no errors during parent contruction + { + try + { + py2cpp::PyPtr pySample = createPySample(sample); + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "createNewJob"); + pyFn(fnScript.content(), pySample, params); + } + catch(std::exception& e) + { + _lastError = "An error occured while creating the job.\n"; + _lastError += e.what(); + } + } + } + + //! Connect to an existing job. + TMonoPyJob(const std::string& jobDump, Sample& sample) + : MonoPyJob() + , _sample(sample) + { + if(_lastError.empty()) // no errors during parent contruction + { + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "loadFromString"); + pyFn(jobDump); + } + catch(std::exception& e) + { + _lastError = "An error occured while creating the job.\n"; + _lastError += e.what(); + } + } + } + + virtual ~TMonoPyJob(){} + virtual bool fetch() + { + _lastError = ""; + try + { + py2cpp::PyFunction pyFn; + pyFn.loadExp(_pyStudy, "getResult"); + fetchResults(_pyStudy.getAttr("sample"), _sample); + } + catch(std::exception& e) + { + _lastError = "An error occured while fetching the results.\n"; + _lastError += e.what(); + } + return _lastError.empty(); + } + + const Sample& getSample()const{return _sample;} + +private: + Sample& _sample; +}; + +} + +#endif //YDEFX_TMONOPYJOB_HXX diff --git a/src/cpp/Test/CMakeLists.txt b/src/cpp/Test/CMakeLists.txt new file mode 100644 index 0000000..12b1d1f --- /dev/null +++ b/src/cpp/Test/CMakeLists.txt @@ -0,0 +1,36 @@ +FIND_PACKAGE(CppUnit REQUIRED) + +# additional include directories +INCLUDE_DIRECTORIES( + ${CPPUNIT_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +# libraries to link to +SET(_link_LIBRARIES + ${CPPUNIT_LIBRARIES} + py2cpp + ydefx +) + +SET(Test_SOURCES + SampleTest.cxx +) + +ADD_EXECUTABLE(SampleTest ${Test_SOURCES}) +TARGET_LINK_LIBRARIES(SampleTest ${_link_LIBRARIES}) +ADD_TEST(YdefxSampleTest SampleTest) +SET_TESTS_PROPERTIES(YdefxSampleTest PROPERTIES ENVIRONMENT + "PYTHONPATH=${CMAKE_SOURCE_DIR}/src:$ENV{PYTHONPATH}") + +ADD_EXECUTABLE(StudyGeneralTest StudyGeneralTest.cxx) +TARGET_LINK_LIBRARIES(StudyGeneralTest ${_link_LIBRARIES}) +ADD_TEST(YdefxStudyGeneralTest StudyGeneralTest) +SET_TESTS_PROPERTIES(YdefxStudyGeneralTest PROPERTIES ENVIRONMENT + "PYTHONPATH=${CMAKE_SOURCE_DIR}/src:$ENV{PYTHONPATH}") + +ADD_EXECUTABLE(StudyRestartTest StudyRestartTest.cxx) +TARGET_LINK_LIBRARIES(StudyRestartTest ${_link_LIBRARIES}) +ADD_TEST(YdefxStudyRestartTest StudyRestartTest) +SET_TESTS_PROPERTIES(YdefxStudyRestartTest PROPERTIES ENVIRONMENT + "PYTHONPATH=${CMAKE_SOURCE_DIR}/src:$ENV{PYTHONPATH}") diff --git a/src/cpp/Test/SampleTest.cxx b/src/cpp/Test/SampleTest.cxx new file mode 100644 index 0000000..e2e3c78 --- /dev/null +++ b/src/cpp/Test/SampleTest.cxx @@ -0,0 +1,258 @@ +#include "SampleTest.hxx" +#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx +#include + +void SampleTest::setUp() +{ +} + +void SampleTest::tearDown() +{ +} + +void SampleTest::cleanUp() +{ +} + +void SampleTest::pointState() +{ + ydefx::Sample mySample; + CPPUNIT_ASSERT(mySample.maxSize() == 0); + std::vector int_vals = {1, 7, 42}; + mySample.inputs().set("toto", int_vals); + mySample.outputs().addName("lili"); + for(unsigned int i = 0; i < 5; ++i) + CPPUNIT_ASSERT(mySample.pointState(i)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.maxSize() == 3); + mySample.errors().resize(3); + mySample.errors()[1] = "Error detected!"; + CPPUNIT_ASSERT(mySample.pointState(0)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(1)==ydefx::ExecutionState::ERROR); + CPPUNIT_ASSERT(mySample.pointState(2)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(3)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.maxSize() == 3); + + mySample.outputs().set("lili", 0, 4.5); + CPPUNIT_ASSERT(mySample.pointState(0)==ydefx::ExecutionState::DONE); + CPPUNIT_ASSERT(mySample.pointState(1)==ydefx::ExecutionState::ERROR); + CPPUNIT_ASSERT(mySample.pointState(2)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(3)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.maxSize() == 3); + + mySample.outputs().set("lili", 5, 42.3); + CPPUNIT_ASSERT(mySample.pointState(0)==ydefx::ExecutionState::DONE); + CPPUNIT_ASSERT(mySample.pointState(1)==ydefx::ExecutionState::ERROR); + CPPUNIT_ASSERT(mySample.pointState(2)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(3)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(4)==ydefx::ExecutionState::NOTEXECUTED); + // no input defined for n°5 => not executed + // TODO: add a new state: incomplete + CPPUNIT_ASSERT(mySample.pointState(5)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.maxSize() == 3); + + mySample.inputs().set("toto", 5, 7); + CPPUNIT_ASSERT(mySample.pointState(4)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(5)==ydefx::ExecutionState::DONE); + CPPUNIT_ASSERT(mySample.maxSize() == 6); + + mySample.outputs().set("lili", 4, 23.5); + CPPUNIT_ASSERT(mySample.pointState(4)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.maxSize() == 6); + + mySample.unset("lili", 5); + CPPUNIT_ASSERT(mySample.pointState(0)==ydefx::ExecutionState::DONE); + CPPUNIT_ASSERT(mySample.pointState(1)==ydefx::ExecutionState::ERROR); + CPPUNIT_ASSERT(mySample.pointState(2)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(3)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(4)==ydefx::ExecutionState::NOTEXECUTED); + CPPUNIT_ASSERT(mySample.pointState(5)==ydefx::ExecutionState::NOTEXECUTED); + + CPPUNIT_ASSERT(mySample.outputs().maxSize() == 4); +} + +void SampleTest::pyConversions() +{ + ydefx::Sample mySample; + std::vector int_vals = {1, 7, 42}; + Py_Initialize(); + { + // Test inputs + py2cpp::PyPtr pyObj(inputToPy(mySample)); + CPPUNIT_ASSERT(pyObj.repr() == "{}"); + mySample.inputs().set("toto", int_vals); + pyObj.reset(inputToPy(mySample)); + CPPUNIT_ASSERT(pyObj.repr() == "{'toto': [1, 7, 42]}"); + std::vector double_vals = {1.5, 3.2, 7.5}; + mySample.inputs().set("dval", double_vals); + pyObj.reset(inputToPy(mySample)); + ydefx::Sample frompySample; + frompySample.inputs().addName("toto"); + frompySample.inputs().addName("dval"); + py2cpp::ConversionCheck check(inputFromPy(pyObj.get(), frompySample)); + CPPUNIT_ASSERT(check); + CPPUNIT_ASSERT(double_vals == frompySample.inputs().get("dval")); + CPPUNIT_ASSERT(int_vals == frompySample.inputs().get("toto")); + + // Test outputs + pyObj.reset(outputToPy(mySample)); + CPPUNIT_ASSERT(pyObj.repr() == "{}"); + check.reset(); + check.addError(outputFromPy(pyObj.get(), frompySample)); + CPPUNIT_ASSERT(check); + CPPUNIT_ASSERT(0 == frompySample.allOutputNames().size()); + CPPUNIT_ASSERT(double_vals == frompySample.inputs().get("dval")); + CPPUNIT_ASSERT(int_vals == frompySample.inputs().get("toto")); + mySample.outputs().addName("dout"); + mySample.outputs().set("dout", 1, 1.5); + pyObj.reset(outputToPy(mySample)); + frompySample.outputs().addName("dout"); + check.addError(outputFromPy(pyObj.get(), frompySample)); + CPPUNIT_ASSERT(check); + CPPUNIT_ASSERT(1.5 == frompySample.outputs().get("dout", 1)); + CPPUNIT_ASSERT(frompySample.outputs().isSet("dout",1)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("dout",0)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("dout",2)); + + mySample.outputs().addName("strout"); + mySample.outputs().set("strout", 1, "myvalue"); + pyObj.reset(outputToPy(mySample)); + frompySample.outputs().addName("strout"); + check.addError(outputFromPy(pyObj.get(), frompySample)); + CPPUNIT_ASSERT(check); + CPPUNIT_ASSERT(1.5 == frompySample.outputs().get("dout", 1)); + CPPUNIT_ASSERT(frompySample.outputs().isSet("dout",1)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("dout",0)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("dout",2)); + CPPUNIT_ASSERT(frompySample.outputs().get("strout", 1) + == "myvalue"); + CPPUNIT_ASSERT(frompySample.outputs().isSet("strout",1)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("strout",0)); + CPPUNIT_ASSERT(!frompySample.outputs().isSet("strout",2)); + } + Py_Finalize(); +} + +void SampleTest::pyConversionsErrors() +{ + Py_Initialize(); + { + ydefx::Sample resultSample; + std::vector double_vals = {1.5, 3.2, 7.5}; + ydefx::Sample > vectSample; + // errors on input variables + vectSample.inputs >().addName("p1"); + vectSample.inputs >().set("p1", 0, double_vals); + py2cpp::PyPtr pyObj(inputToPy(vectSample)); + resultSample.inputs().addName("noname"); + py2cpp::ConversionCheck check; + check.addError(inputFromPy(pyObj.get(), resultSample)); + // The error contains the name of the unfound key. + CPPUNIT_ASSERT(check.getMessage().find("noname")!= std::string::npos); + resultSample.clear(); + resultSample.inputs().addName("p1"); + check.reset(); + check.addError(inputFromPy(pyObj.get(), resultSample)); + // The error contains the name of the type "double" which fails to be + // converted. + CPPUNIT_ASSERT(check.getMessage().find("double")!= std::string::npos); + ydefx::Sample > vectIntSample; + vectIntSample.inputs >().addName("p1"); + check.reset(); + check.addError(inputFromPy(pyObj.get(), vectIntSample)); + CPPUNIT_ASSERT(check.getMessage().find("int")!= std::string::npos); + CPPUNIT_ASSERT(check.getMessage().find("std::vector")!= std::string::npos); + py2cpp::PyPtr notADict(py2cpp::toPy(42)); + check.reset(); + check.addError(inputFromPy(notADict.get(), vectIntSample)); + CPPUNIT_ASSERT(check.getMessage().find("Sample input")!= std::string::npos); + + // errors on output variables + resultSample.outputs().addName("o1"); + check.reset(); + check.addError(outputFromPy(pyObj.get(), resultSample)); + CPPUNIT_ASSERT(check.getMessage().find("o1")!= std::string::npos); + + vectSample.outputs >().addName("o1"); + vectSample.outputs >().set("o1", 0, double_vals); + pyObj.reset(outputToPy(vectSample)); + check.reset(); + check.addError(outputFromPy(pyObj.get(), resultSample)); + CPPUNIT_ASSERT(check.getMessage().find("double")!= std::string::npos); + + check.reset(); + check.addError(outputFromPy(notADict.get(), resultSample)); + CPPUNIT_ASSERT(check.getMessage().find("Sample output")!= std::string::npos); + } + Py_Finalize(); +} + +void SampleTest::params() +{ + Py_Initialize(); + { + try + { + ydefx::JobParametersProxy jpp; + CPPUNIT_ASSERT(jpp.resource_name() == "localhost"); + CPPUNIT_ASSERT(jpp.job_type() == "yacs_file"); + std::list testList = {"toto", "titi","tata"}; + jpp.in_files(testList); + CPPUNIT_ASSERT(jpp.in_files() == testList); + std::list addList = {"zozo", "zizi"}; + jpp.add_in_files(addList); + CPPUNIT_ASSERT(jpp.in_files() == std::list({"toto", "titi", + "tata", "zozo", "zizi"})); + jpp.env_file("a"); + jpp.exclusive(true); + jpp.extra_params("b"); + jpp.job_file("c"); + jpp.job_name("d"); + jpp.job_type("e"); + jpp.local_directory("fi"); + jpp.maximum_duration("dur"); + jpp.mem_mb(5); + jpp.mem_per_cpu(42); + jpp.nb_branches(18); + jpp.nb_node(15); + jpp.nb_proc(13); + jpp.nb_proc_per_node(12); + jpp.partition("gg"); + jpp.pre_command("pc"); + jpp.queue("qq"); + jpp.resource_name("rn"); + jpp.result_directory("rd"); + jpp.wckey("wk"); + jpp.work_directory("wd"); + CPPUNIT_ASSERT(jpp.env_file() == "a"); + CPPUNIT_ASSERT(jpp.exclusive()); + CPPUNIT_ASSERT(jpp.extra_params() == "b"); + CPPUNIT_ASSERT(jpp.job_file() == "c"); + CPPUNIT_ASSERT(jpp.job_name() == "d"); + CPPUNIT_ASSERT(jpp.job_type() == "e"); + CPPUNIT_ASSERT(jpp.local_directory() == "fi"); + CPPUNIT_ASSERT(jpp.maximum_duration() == "dur"); + CPPUNIT_ASSERT(jpp.mem_mb() == 5); + CPPUNIT_ASSERT(jpp.mem_per_cpu() == 42); + CPPUNIT_ASSERT(jpp.nb_branches() == 18); + CPPUNIT_ASSERT(jpp.nb_node() == 15); + CPPUNIT_ASSERT(jpp.nb_proc() == 13); + CPPUNIT_ASSERT(jpp.nb_proc_per_node() == 12); + CPPUNIT_ASSERT(jpp.partition() == "gg"); + CPPUNIT_ASSERT(jpp.pre_command() == "pc"); + CPPUNIT_ASSERT(jpp.queue() == "qq"); + CPPUNIT_ASSERT(jpp.resource_name() == "rn"); + CPPUNIT_ASSERT(jpp.result_directory() == "rd"); + CPPUNIT_ASSERT(jpp.wckey() == "wk"); + CPPUNIT_ASSERT(jpp.work_directory() == "wd"); + } + catch(std::exception& e) + { + std::cout << e.what(); + CPPUNIT_FAIL("Unexpected exception!"); + } + } + Py_Finalize(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( SampleTest ); +#include "TestMain.cxx" diff --git a/src/cpp/Test/SampleTest.hxx b/src/cpp/Test/SampleTest.hxx new file mode 100644 index 0000000..cd1b7e8 --- /dev/null +++ b/src/cpp/Test/SampleTest.hxx @@ -0,0 +1,43 @@ +// Copyright (C) 2018 EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef YDEFX_SAMPLETEST_HXX +#define YDEFX_SAMPLETEST_HXX + +#include + +class SampleTest: public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(SampleTest); + CPPUNIT_TEST(pointState); + CPPUNIT_TEST(pyConversions); + CPPUNIT_TEST(pyConversionsErrors); + CPPUNIT_TEST(params); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp(); + void tearDown(); + void cleanUp(); + void pointState(); + void pyConversions(); + void pyConversionsErrors(); + void params(); +}; + +#endif // YDEFX_SAMPLETEST_HXX diff --git a/src/cpp/Test/StudyGeneralTest.cxx b/src/cpp/Test/StudyGeneralTest.cxx new file mode 100644 index 0000000..b804c1b --- /dev/null +++ b/src/cpp/Test/StudyGeneralTest.cxx @@ -0,0 +1,78 @@ +#include "StudyGeneralTest.hxx" +#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx +#include + +void SampleTest::setUp() +{ +} + +void SampleTest::tearDown() +{ +} + +void SampleTest::cleanUp() +{ +} + +void SampleTest::fullStudy() +{ + Py_Initialize(); + { + ydefx::JobParametersProxy jobParams; + jobParams.configureResource("localhost"); + jobParams.createResultDirectory("/tmp"); + std::string pyScript = +"def _exec(a, b):\n" +" d = a / b\n" +" return d\n"; + + ydefx::PyStudyFunction studyFunction; + studyFunction.loadString(pyScript); + CPPUNIT_ASSERT(studyFunction.errors().empty()); + const std::list& inputs = studyFunction.inputNames(); + CPPUNIT_ASSERT(std::find(inputs.begin(), inputs.end(), "a")!=inputs.end()); + CPPUNIT_ASSERT(std::find(inputs.begin(), inputs.end(), "b")!=inputs.end()); + const std::list& outputs = studyFunction.outputNames(); + CPPUNIT_ASSERT(std::find(outputs.begin(), outputs.end(), "d") + != outputs.end()); + + ydefx::Sample sample; + std::vector a_vals = {1.1, 2.2, 3.4, 5.5}; + std::vector b_vals = {2.2, 4.4, 6.8, 11}; + sample.inputs().set("a", a_vals); + sample.inputs().set("b", b_vals); + sample.outputs().addName("d"); + + ydefx::Launcher l; + ydefx::Job* myJob = l.submitMonoPyJob(studyFunction, sample, jobParams); + CPPUNIT_ASSERT(myJob); + CPPUNIT_ASSERT(l.lastError().empty()); + std::string jobDump = myJob->dump(); + CPPUNIT_ASSERT(myJob->lastError().empty()); + std::string jobState = myJob->state(); + CPPUNIT_ASSERT(myJob->lastError().empty()); + CPPUNIT_ASSERT(jobState == "QUEUED" || jobState == "RUNNING" + || jobState == "FINISHED"); + double progress = myJob->progress(); + CPPUNIT_ASSERT(progress >= 0.0 && progress <= 1.0 ); + CPPUNIT_ASSERT(myJob->lastError().empty()); + bool ok = myJob->wait(); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT(myJob->lastError().empty()); + jobState = myJob->state(); + CPPUNIT_ASSERT(jobState == "FINISHED"); + progress = myJob->progress(); + CPPUNIT_ASSERT(progress == 1.0); + ok = myJob->fetch(); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT(myJob->lastError().empty()); + std::vector expectedResult = {0.5, 0.5, 0.5, 0.5}; + const std::vector& result = sample.outputs().get("d"); + CPPUNIT_ASSERT(expectedResult == result); + delete myJob; + } + Py_Finalize(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( SampleTest ); +#include "TestMain.cxx" diff --git a/src/cpp/Test/StudyGeneralTest.hxx b/src/cpp/Test/StudyGeneralTest.hxx new file mode 100644 index 0000000..ab362fa --- /dev/null +++ b/src/cpp/Test/StudyGeneralTest.hxx @@ -0,0 +1,37 @@ +// Copyright (C) 2018 EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef YDEFX_SAMPLETEST_HXX +#define YDEFX_SAMPLETEST_HXX + +#include + +class SampleTest: public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(SampleTest); + CPPUNIT_TEST(fullStudy); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp(); + void tearDown(); + void cleanUp(); + void fullStudy(); +}; + +#endif // YDEFX_SAMPLETEST_HXX diff --git a/src/cpp/Test/StudyRestartTest.cxx b/src/cpp/Test/StudyRestartTest.cxx new file mode 100644 index 0000000..afe04ac --- /dev/null +++ b/src/cpp/Test/StudyRestartTest.cxx @@ -0,0 +1,63 @@ +#include "StudyRestartTest.hxx" +#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx +#include + +void SampleTest::setUp() +{ +} + +void SampleTest::tearDown() +{ +} + +void SampleTest::cleanUp() +{ +} + +void SampleTest::studyTest() +{ + Py_Initialize(); + { + ydefx::JobParametersProxy jobParams; + jobParams.configureResource("localhost"); + jobParams.createResultDirectory("/tmp"); + + std::string pyScript = +"def _exec(a, b):\n" +" d = a / b\n" +" return d\n"; + ydefx::PyStudyFunction studyFunction; + studyFunction.loadString(pyScript); + + ydefx::Sample sample; + std::vector a_vals = {1.1, 2.2, 3.4, 5.5}; + std::vector b_vals = {2.2, 4.4, 6.8, 11}; + sample.inputs().set("a", a_vals); + sample.inputs().set("b", b_vals); + sample.outputs().addName("d"); + + ydefx::Launcher l; + ydefx::Job* myJob = l.submitMonoPyJob(studyFunction, sample, jobParams); + std::string jobDump = myJob->dump(); + delete myJob; + + ydefx::Job* restoredJob = l.connectJob(jobDump, sample); + CPPUNIT_ASSERT(restoredJob); + CPPUNIT_ASSERT(l.lastError().empty()); + bool ok = restoredJob->wait(); + CPPUNIT_ASSERT(ok); + double progress = restoredJob->progress(); + CPPUNIT_ASSERT(progress == 1.0); + ok = restoredJob->fetch(); + CPPUNIT_ASSERT(ok); + std::vector expectedResult = {0.5, 0.5, 0.5, 0.5}; + const std::vector& result = sample.outputs().get("d"); + CPPUNIT_ASSERT(expectedResult == result); + CPPUNIT_ASSERT(restoredJob->lastError().empty()); + delete restoredJob; + } + Py_Finalize(); +} + +CPPUNIT_TEST_SUITE_REGISTRATION( SampleTest ); +#include "TestMain.cxx" diff --git a/src/cpp/Test/StudyRestartTest.hxx b/src/cpp/Test/StudyRestartTest.hxx new file mode 100644 index 0000000..115f1ab --- /dev/null +++ b/src/cpp/Test/StudyRestartTest.hxx @@ -0,0 +1,37 @@ +// Copyright (C) 2018 EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef YDEFX_SAMPLETEST_HXX +#define YDEFX_SAMPLETEST_HXX + +#include + +class SampleTest: public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(SampleTest); + CPPUNIT_TEST(studyTest); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp(); + void tearDown(); + void cleanUp(); + void studyTest(); +}; + +#endif // YDEFX_SAMPLETEST_HXX diff --git a/src/cpp/Test/TestMain.cxx b/src/cpp/Test/TestMain.cxx new file mode 100644 index 0000000..5344725 --- /dev/null +++ b/src/cpp/Test/TestMain.cxx @@ -0,0 +1,83 @@ +// Copyright (C) 2018 EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// ============================================================================ +/*! + * Main program source for Unit Tests with cppunit package does not depend + * on actual tests, so we use the same for all partial unit tests. + */ +// ============================================================================ + +int main(int argc, char* argv[]) +{ + // --- Create the event manager and test controller + CPPUNIT_NS::TestResult controller; + + // --- Add a listener that collects test result + CPPUNIT_NS::TestResultCollector result; + controller.addListener( &result ); + + // --- Add a listener that print dots as test run. +#ifdef WIN32 + CPPUNIT_NS::TextTestProgressListener progress; +#else + CPPUNIT_NS::BriefTestProgressListener progress; +#endif + controller.addListener( &progress ); + + // --- Get the top level suite from the registry + + CPPUNIT_NS::Test *suite = + CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest(); + + // --- Adds the test to the list of test to run + + CPPUNIT_NS::TestRunner runner; + runner.addTest( suite ); + runner.run( controller); + + // --- Print test in a compiler compatible format. + std::ofstream testFile; + testFile.open("test.log", std::ios::out | std::ios::app); + testFile << "------ Idefix test log:" << std::endl; + CPPUNIT_NS::CompilerOutputter outputter( &result, testFile ); + outputter.write(); + + // --- Run the tests. + + bool wasSucessful = result.wasSuccessful(); + testFile.close(); + + // --- Return error code 1 if the one of test failed. + + return wasSucessful ? 0 : 1; +} diff --git a/src/cpp/YacsStudyFunction.cxx b/src/cpp/YacsStudyFunction.cxx new file mode 100644 index 0000000..d15339b --- /dev/null +++ b/src/cpp/YacsStudyFunction.cxx @@ -0,0 +1,60 @@ +#include "YacsStudyFunction.hxx" +#include +#include +namespace ydefx +{ +YacsStudyFunction::YacsStudyFunction() +: _content() +, _input() +, _output() +, _errors() +{ + _errors.push_back("Function \"_exec\" not found!"); +} + +YacsStudyFunction::~YacsStudyFunction(){} + +void YacsStudyFunction::loadFile(const std::string& path) +{ + std::ifstream infile(path.c_str()); + std::stringstream buffer; + buffer << infile.rdbuf(); + loadString(buffer.str()); +} + +void YacsStudyFunction::loadString(const std::string& value) +{ + _content = value; + _input.clear(); + _output.clear(); + _errors.clear(); + // TODO: use YACS +} + +void YacsStudyFunction::save(const std::string& path) +{ + std::ofstream outfile(path.c_str()); + outfile << _content; +} + +const std::string& YacsStudyFunction::content()const +{ + return _content; +} + +const std::list& YacsStudyFunction::inputNames()const +{ + return _input; +} + +const std::list& YacsStudyFunction::outputNames()const +{ + return _output; +} + +const std::list& YacsStudyFunction::errors()const +{ + return _errors; +} + +} diff --git a/src/cpp/YacsStudyFunction.hxx b/src/cpp/YacsStudyFunction.hxx new file mode 100644 index 0000000..b937b5d --- /dev/null +++ b/src/cpp/YacsStudyFunction.hxx @@ -0,0 +1,26 @@ +#ifndef YDEFX_YACSSTUDYFUNCTION_H +#define YDEFX_YACSSTUDYFUNCTION_H +#include "StudyFunction.hxx" +namespace ydefx +{ +//TODO not implemented! +class YacsStudyFunction : StudyFunction +{ +public: + YacsStudyFunction(); + virtual ~YacsStudyFunction(); + virtual void loadFile(const std::string& path); + virtual void loadString(const std::string&); + virtual void save(const std::string& path); + virtual const std::string& content()const; + virtual const std::list& inputNames()const; + virtual const std::list& outputNames()const; + virtual const std::list& errors()const; +private: + std::string _content; + std::list _input; + std::list _output; + std::list _errors; +}; +} +#endif // YDEFX_YACSSTUDYFUNCTION_H diff --git a/src/pydefx/CMakeLists.txt b/src/pydefx/CMakeLists.txt new file mode 100644 index 0000000..1fcda2f --- /dev/null +++ b/src/pydefx/CMakeLists.txt @@ -0,0 +1,13 @@ +SET(SCRIPTS + __init__.py + configuration.py + parameters.py + pyscript.py + pystudy.py + sample.py + samplecsviterator.py + samplecsvmanager.py + ) + +INSTALL(FILES ${SCRIPTS} DESTINATION ${SALOME_INSTALL_PYTHON}/pydefx) +ADD_SUBDIRECTORY(schemas) diff --git a/src/pydefx/__init__.py b/src/pydefx/__init__.py new file mode 100644 index 0000000..4fd26f9 --- /dev/null +++ b/src/pydefx/__init__.py @@ -0,0 +1,7 @@ +from .parameters import Parameters +from .pyscript import PyScript +from .pystudy import PyStudy +from .sample import Sample + +import salome +salome.salome_init() diff --git a/src/pydefx/configuration.py b/src/pydefx/configuration.py new file mode 100644 index 0000000..a1496aa --- /dev/null +++ b/src/pydefx/configuration.py @@ -0,0 +1,33 @@ +import salome +import tempfile +import pathlib + +def defaultWorkingDir(resource): + resManager= salome.lcc.getResourcesManager() + resource_definition = resManager.GetResourceDefinition(resource) + return resource_definition.working_directory + +def defaultNbBranches(resource): + resManager= salome.lcc.getResourcesManager() + resource_definition = resManager.GetResourceDefinition(resource) + ret = resource_definition.nb_node + if ret < 1: + ret = 1 + return ret + +def defaultBaseDirectory(): + """Return the default path to the root of any new result directory.""" + return str(pathlib.Path.home()) + +def newResultDirectory(basedir=None): + """ A new directory is created and the path is returned.""" + if basedir is None : + basedir = defaultBaseDirectory() + return tempfile.mkdtemp(prefix='idefix',dir=basedir) + +def defaultWckey(resource="localhost"): + result = "" + if resource != "localhost": + result = "P11U5:CARBONES" + return result + diff --git a/src/pydefx/parameters.py b/src/pydefx/parameters.py new file mode 100644 index 0000000..da5d2f2 --- /dev/null +++ b/src/pydefx/parameters.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +import salome +from . import configuration + +class Parameters: + def __init__(self, resource="localhost", + nb_branches=None, salome_parameters=None): + if salome_parameters is None : + job_params = salome.JobParameters() + job_params.job_type = "yacs_file" + job_params.resource_required = salome.ResourceParameters() + job_params.resource_required.name = resource + job_params.job_name = "idefix_job" + job_params.wckey = configuration.defaultWckey(resource) + job_params.work_directory = configuration.defaultWorkingDir(resource) + if nb_branches is None: + nb_branches = configuration.defaultNbBranches(resource) + job_params.resource_required.nb_proc = nb_branches + self.nb_branches = nb_branches + self.salome_parameters = job_params + else: + if nb_branches is None: + nb_branches = salome_parameters.resource_required.nb_proc + self.nb_branches = nb_branches + self.salome_parameters = salome_parameters + + def configureResource(self, resource): + self.salome_parameters.resource_required.name = resource + self.salome_parameters.work_directory = configuration.defaultWorkingDir( + resource) + nb_branches = configuration.defaultNbBranches(resource) + self.salome_parameters.resource_required.nb_proc = nb_branches + self.nb_branches = nb_branches + self.salome_parameters.wckey = configuration.defaultWckey(resource) + + def createResultDirectory(self, result_base_dir): + self.salome_parameters.result_directory = configuration.newResultDirectory(result_base_dir) diff --git a/src/pydefx/pyscript.py b/src/pydefx/pyscript.py new file mode 100644 index 0000000..8895d17 --- /dev/null +++ b/src/pydefx/pyscript.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from . import sample + +class PyScriptException(Exception): + pass + +class PyScript: + def __init__(self): + self.script = "" + + def loadFile(self,path): + with open(path, "r") as f: + self.script = f.read() + + def loadString(self, script): + self.script = script + + def saveFile(self, path): + with open(path, "w") as f: + f.write(self.script) + + def getAllProperties(self): + """ + functions,errors = myscript.getAllProperties() + print(errors) # list of syntax errors in the script + for f in functions: + print(f.name) # function name + print(f.inputs) # list of input variable names + print(f.outputs) # list of output variable names + print(f.errors) # list of py2yacs errors in the function + print(f.imports) # list of import statements in the function + """ + import py2yacs + return py2yacs.get_properties(self.script) + + def getFunctionProperties(self, fname): + """ + Properties of the _exec function: + fn_properties, errors = myscript.getExecProperties("_exec") + fn_properties.name : "_exec" + fn_properties.inputs : list of input variable names + fn_properties.outputs : list of output variable names + fn_properties.errors : list of py2yacs errors in the function + fn_properties.imports : list of import statements in the function + errors : list of syntax errors in the script + fn_properties is None if the "_exec" function does not exist. + """ + functions,errors = self.getAllProperties() + fn_properties = next((f for f in functions if f.name == fname), None) + return fn_properties, errors + + def CreateEmptySample(self): + """ + Create a sample with input and output variable names set. + """ + fn = "_exec" + fn_properties, errors = self.getFunctionProperties(fn) + if len(errors) > 0: + error_string = "global errors:\n" + error_string += '\n'.join(errors) + raise PyScriptException(error_string) + if fn_properties is None: + raise PyScriptException("Function {} not found!".format(fn)) + if len(fn_properties.errors) > 0: + error_string = "Errors in function {}:\n".format(fn) + error_string += '\n'.join(fn_properties.errors) + raise PyScriptException(error_string) + return sample.Sample(fn_properties.inputs, fn_properties.outputs) + diff --git a/src/pydefx/pystudy.py b/src/pydefx/pystudy.py new file mode 100644 index 0000000..aeea713 --- /dev/null +++ b/src/pydefx/pystudy.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +import inspect +import pathlib +import tempfile +import os +import salome +import json +from . import samplecsvmanager +from . import parameters +from . import configuration + +def defaultSampleManager(): + return samplecsvmanager.SampleManager() + +def prepareDirectoryForLaunch(sample, result_directory, nb_branches, script, + sampleManager=None): + """ + sample : Sample type + result_directory : path to a result working directory. + nb_branches : int + script : string + return: + extra_in_files: list of files to add to salome_parameters.in_files + yacs_schema_path: path to the yacs schema (xml file). + """ + if sampleManager is None: + sampleManager = defaultSampleManager() + # export sample to result_directory + inputFiles = sampleManager.prepareRun(sample, result_directory) + # export nbbranches + configpath = os.path.join(result_directory, "idefixconfig.json") + dicconfig = {} + dicconfig["nbbranches"] = nb_branches + dicconfig["studymodule"] = "idefixstudy" + dicconfig["sampleIterator"] = sampleManager.getModuleName() + with open(configpath, "w") as f: + json.dump(dicconfig, f, indent=2) + studypath = os.path.join(result_directory, "idefixstudy.py") + with open(studypath, "w") as f: + f.write(script) + # find generic schema + filename = inspect.getframeinfo(inspect.currentframe()).filename + install_directory = pathlib.Path(filename).resolve().parent + yacs_schema_path = os.path.join(install_directory, "schemas", + "idefix_pyschema.xml") + plugin_path = os.path.join(install_directory, "schemas", "plugin.py") + # create salome params + extra_in_files = [] + extra_in_files.extend([configpath, studypath, plugin_path]) + extra_in_files.extend(inputFiles) + return extra_in_files, yacs_schema_path + +class PyStudy: + JOB_DUMP_NAME = "jobDump.xml" + def __init__(self): + self.job_id = -1 + + # Study creation functions + def createNewJob(self, script, sample, params, sampleManager=None): + """ + Create a new job out of those parameters: + script : string + sample : sample to be evaluated (Sample class) + params : job submission parameters (Parameters class) + The result directory will contain all the files needed for a launch and a + job is created but not launched. + """ + self.sample = sample + self.params = params + self.params.salome_parameters.job_type = self.jobType() + if sampleManager is None: + self.sampleManager = defaultSampleManager() + else: + self.sampleManager = sampleManager + tmp_workdir = self.params.salome_parameters.result_directory + extra_in_files, yacs_schema_path = prepareDirectoryForLaunch(self.sample, + tmp_workdir, + self.params.nb_branches, + script, + self.sampleManager) + self.params.salome_parameters.in_files.extend(extra_in_files) + self.params.salome_parameters.job_file = yacs_schema_path + launcher = salome.naming_service.Resolve('/SalomeLauncher') + self.job_id = launcher.createJob(self.params.salome_parameters) + return self.job_id + + def loadFromDirectory(self, path, sampleManager=None): + """ + Recover a study from a result directory where a previous study was launched. + """ + if sampleManager is None: + sampleManager = defaultSampleManager() + self.sampleManager = sampleManager + self.sample = sampleManager.loadSample(path) + job_string = loadJobString(path) + launcher = salome.naming_service.Resolve('/SalomeLauncher') + self.job_id = launcher.restoreJob(job_string) + if job_id >= 0: + salome_params = launcher.getJobParameters(job_id) + self.params = parameters.Parameters(salome_parameters=salome_params) + self.getResult() + return self.job_id + + def loadFromString(self, jobstring, sampleManager=None): + """ + Recover a study from a string which contains the description of the job. + This string can be obtained by launcher.dumpJob. + """ + if sampleManager is None: + sampleManager = defaultSampleManager() + self.sampleManager = sampleManager + launcher = salome.naming_service.Resolve('/SalomeLauncher') + self.job_id = launcher.restoreJob(jobstring) + self.params = None + self.sample = None + if self.job_id >= 0: + salome_params = launcher.getJobParameters(self.job_id) + self.params = parameters.Parameters(salome_parameters=salome_params) + self.sample = sampleManager.loadSample(salome_params.result_directory) + self.getResult() + else: + raise Exception("Failed to restore the job.") + + def loadFromId(self, jobid, sampleManager=None): + """ + Connect the study to an already created job. + The result directory of the job must be already prepared for launch. + """ + if jobid < 0: + return + if sampleManager is None: + sampleManager = defaultSampleManager() + self.sampleManager = sampleManager + self.job_id = jobid + launcher = salome.naming_service.Resolve('/SalomeLauncher') + salome_params = launcher.getJobParameters(job_id) + self.params = parameters.Parameters(salome_parameters=salome_params) + self.sample = sampleManager.loadSample(salome_params.result_directory) + self.script = None + return + + # launch parameters functions + def jobType(self): + return "yacs_file" + + # TODO: may be deprecated + def createDefaultParameters(self, resource="localhost", + nb_branches=None, + result_base_dir=None): + """ + Create the Parameters structure and the result directory. + The result directory created here is needed by the job. + """ + newParams = parameters.Parameters(resource, nb_branches) + newParams.salome_parameters.job_type = self.jobType() + newParams.salome_parameters.job_name = "idefix_job" + newParams.salome_parameters.result_directory = configuration.newResultDirectory(result_base_dir) + return newParams + + # Job management functions + def launch(self): + """ + The job should have been already created. + """ + if self.job_id < 0 : + raise Exception("Nothing to launch! Job is not created!") + tmp_workdir = self.params.salome_parameters.result_directory + # run the job + launcher = salome.naming_service.Resolve('/SalomeLauncher') + launcher.launchJob(self.job_id) + #save the job + job_string = launcher.dumpJob(self.job_id) + jobDumpPath = os.path.join(tmp_workdir, PyStudy.JOB_DUMP_NAME) + with open(jobDumpPath, "w") as f: + f.write(job_string) + + def getResult(self): + if self.job_id < 0 : + raise Exception("Cannot get the results if the job is not created!") + launcher = salome.naming_service.Resolve('/SalomeLauncher') + state = launcher.getJobState(self.job_id) + if state == "CREATED" : + raise Exception("Cannot get the results if the job is not launched!") + tmp_workdir = self.params.salome_parameters.result_directory + if 1 == launcher.getJobWorkFile(self.job_id, + self.sampleManager.getResultFileName(), + tmp_workdir): + self.sampleManager.loadResult(self.sample, tmp_workdir) + return self.sample + + def getJobState(self): + if self.job_id < 0: + return "NOT_CREATED" + launcher = salome.naming_service.Resolve('/SalomeLauncher') + return launcher.getJobState(self.job_id) + + def getProgress(self): + if self.job_id < 0: + return 0.0 + state = self.getJobState() + if state == "CREATED" or state == "QUEUED" : + return 0.0 + self.getResult(); + return self.sample.progressRate() + + def dump(self): + if self.job_id < 0 : + raise Exception("Cannot dump the job if it is not created!") + launcher = salome.naming_service.Resolve('/SalomeLauncher') + return launcher.dumpJob(self.job_id) + + def wait(self, sleep_delay=10): + """ Wait for the end of the job """ + launcher = salome.naming_service.Resolve('/SalomeLauncher') + job_id = self.job_id + jobState = launcher.getJobState(job_id) + import time + while jobState=="QUEUED" or jobState=="IN_PROCESS" or jobState=="RUNNING" : + time.sleep(sleep_delay) + jobState = launcher.getJobState(job_id) + + +### Deprecated!!!! +def dumpJob(result_directory, jobString): + """ + Save the jobString to a file into result_directory. + result_directory is a string representing a path to a directory. + jobString is a string representing the serialization of a job. + Use loadJobString for reloading the string saved here. + """ + jobDumpPath = os.path.join(result_directory, PyStudy.JOB_DUMP_NAME) + with open(jobDumpPath, "w") as f: + f.write(job_string) + +def loadJobString(result_directory): + """ + Return the jobString saved by the dumpJob function into a directory. + Use dumpJob for saving a the string. + """ + jobDumpPath = os.path.join(result_directory, PyStudy.JOB_DUMP_NAME) + with open(jobDumpPath, "r") as f: + job_string = f.read() + return job_string + diff --git a/src/pydefx/sample.py b/src/pydefx/sample.py new file mode 100644 index 0000000..5f0e54e --- /dev/null +++ b/src/pydefx/sample.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +import csv +from enum import Enum + +class SampleException(Exception): + pass + +class PointState(Enum): + """ + Is the point evaluated? + """ + NO = 1 + YES = 2 + ERROR = 3 + +class Sample: + def __init__(self, input_names, output_names): + """ + input_names : gives the names and the order of input variables + output_names : gives the names and the order of output variables + """ + if len(input_names) < 1 : + SampleException("A sample should have at least one input variable.") + self._input_names = input_names + self._output_names = output_names + self._input={} + self._output={} # {output_name:[output_values]} + self._messages=[] # list. Possible values in the list: + # None = point not evaluated, + # "" = results available, no error + # "any string"= error message for that index. + + def __iter__(self): + return Dico_iter(self._input) + + def inputIterator(self): + """ + Iterate over input values. + """ + return Dico_iter(self._input) + + def setInputValues(self, inputDict): + if len(inputDict) != len(self._input_names): + raise SampleException("Incorrect number of keys!") + size = None + for name in self._input_names: + if size is None: + size = len(inputDict[name]) + else: + if size != len(inputDict[name]): + raise SampleException("Inconsistency in input variable sizes!") + self._input = inputDict + # Fill all results with None + self._messages = [ None for i in range(size)] + self._output.clear() + for name in self._output_names : + self._output[name] = [ None for i in range(size)] + + def addResult(self, index, out_values, message): + """ + Add results for index. + """ + if message is None: + message = "" + if message : + # case of error + for name in self._output_names : + self._output[name][index] = None + else: + for name in self._output_names : + self._output[name][index] = out_values[name] + + self._messages[index] = message + + def getPointState(self, index): + message = self._messages[index] + if message is None: + return PointState.NO + elif message: + return PointState.ERROR + else: + return PointState.YES + + def findFirstId(self, in_values): + """ + Find the index of the first point in the sample which contains exactly the + same input values as in in_values. + in_values is a dictionary with {name : value} of input variables in one + point. + """ + if self._input_names is None or len(self._input_names) == 0 : + raise SampleException("List of input variables not defined.") + firstName = self._input_names[0] + maxSize = len(self._input[firstName]) + curId = 0 + foundId = -1 + for curId in range(maxSize): + if self.isValidId(self, curId, in_values) : + foundId = curId + break + return foundId + + + def isValidId(self, idToCheck, inputToCheck): + """ + Verify the input values at the idToCheck position are exactly the same as + the values in inputToCheck. Return True or False. + """ + ok = True + try: + for name in self._input_names: + if self._input[name][idToCheck] != inputToCheck[name] : + ok = False + break + except: + ok = False + return ok + + def checkId(self, idToCheck, inputToCheck): + """ + Verify the input values at the idToCheck position are exactly the same as + the values in inputToCheck. Raise SampleException if it is not the case. + """ + for name in self._input_names: + if not name in self._input: + raise SampleException( + "Variable name {} expected and not found ".format(name)) + if self._input[name][idToCheck] != inputToCheck[name] : + raise SampleException( + "Expected value for variable {} is {} and found {}".format(name, + self._input[name][idToCheck], + inputToCheck[name])) + + def getInputNames(self): + return self._input_names + + def getOutputNames(self): + return self._output_names + + def progressRate(self): + numberOfPoints = len(self._messages) + unevaluatedPoints = self._messages.count(None) + result = 0.0 + if(numberOfPoints > 0): + result = 1.0 - unevaluatedPoints / numberOfPoints + return result + + def __str__(self): + result = "" + for name in self._input_names: + result += name+"," + for name in self._output_names: + result += name+"," + result += "messages\n" + for i in range(len(self._messages)): + for name in self._input_names: + result += repr(self._input[name][i]) +"," + for name in self._output_names: + result += repr(self._output[name][i]) +"," + result += str(self._messages[i])+"\n" + return result + +class Dico_iter: + """ + >>> si=Dico_iter({"x":[1,2,3], "y":["a","b","c"]}) + >>> for i in si: + >>> print(i) + {'x': 1, 'y': 'a'} + {'x': 2, 'y': 'b'} + {'x': 3, 'y': 'c'} + """ + def __init__(self, s): + self.iters={} + for k, v in s.items(): + self.iters[k] = iter(v) + + def __iter__(self): + return self + + def __next__(self): + result = {} + for k,v in self.iters.items(): + result[k] = next(v) + return result diff --git a/src/pydefx/samplecsviterator.py b/src/pydefx/samplecsviterator.py new file mode 100644 index 0000000..7b68e10 --- /dev/null +++ b/src/pydefx/samplecsviterator.py @@ -0,0 +1,95 @@ +import csv +import os + +def tofile(t): + result = {} + for k,v in t: + if isinstance(v, (int, float)): + result[k]=v + else: + result[k] = repr(e) + return result + +def fromfile(t): + result = {} + for k,v in t: + if isinstance(v, str): + result[k] = eval(e) + else: + result[k] = e + return result + +class SampleIterator: + DATAFILE = "idefixdata.csv" + OUTPUTNAMESFILE = "idefixoutputnames.csv" + RESULTFILE = "idefixresult.csv" + ERRORCOLUMN = "idefix_error" + IDCOLUMN ="idefix_id" + def __init__(self, directory=None): + if directory: + datapath = os.path.join(directory, SampleIterator.DATAFILE) + outputnamespath = os.path.join(directory, SampleIterator.OUTPUTNAMESFILE) + resultpath = os.path.join(directory, SampleIterator.RESULTFILE) + else: + datapath = SampleIterator.DATAFILE + outputnamespath = SampleIterator.OUTPUTNAMESFILE + resultpath = SampleIterator.RESULTFILE + + self.iterNb = -1 + self.datafile = open(datapath, newline='') + self.data = csv.DictReader(self.datafile, quoting=csv.QUOTE_NONNUMERIC) + result_columns = [SampleIterator.IDCOLUMN] + result_columns.extend(self.data.fieldnames) + self.outputnames = _loadOutputNames(outputnamespath) + result_columns.extend(self.outputnames) + result_columns.append(SampleIterator.ERRORCOLUMN) + self.result_file = open(resultpath, 'w', newline='') + self.result_csv = csv.DictWriter( self.result_file, + fieldnames=result_columns, + quoting=csv.QUOTE_NONNUMERIC ) + self.result_csv.writeheader() + self.result_file.flush() + + def addResult(self, currentId, currentInput, currentOutput, currentError): + """ + currentId : int value + currentInput : dictionary {"input name":value} + currentOutput : result returned by _exec. Can be a tuple, a simple value or + None in case of error. + currentError : string or None if no error + """ + currentRecord = {} + currentRecord[SampleIterator.IDCOLUMN] = currentId + for name in self.data.fieldnames: + currentRecord[name] = currentInput[name] + if currentError is None: + if len(self.outputnames) == 1 : + outputname = self.outputnames[0] + currentRecord[outputname] = currentOutput + elif len(self.outputnames) > 1 : + outputIter = iter(currentOutput) + for name in self.outputnames: + currentRecord[name] = next(outputIter) + else: + for name in self.outputnames: + currentRecord[name] = None + currentRecord[SampleIterator.ERRORCOLUMN] = currentError + self.result_csv.writerow(currentRecord) + self.result_file.flush() + + def __next__(self): + self.iterNb += 1 + return self.iterNb, next(self.data) + #TODO: we need to close the files somewhere, but we cannot do it here + + def __iter__(self): + return self + +# Private functions +def _loadOutputNames(filepath): + outputnames = [] + with open(filepath, "r") as namesfile: + for line in namesfile: + line = line.rstrip() # remove whitespaces at the end + outputnames.append(line) + return outputnames diff --git a/src/pydefx/samplecsvmanager.py b/src/pydefx/samplecsvmanager.py new file mode 100644 index 0000000..2c1e696 --- /dev/null +++ b/src/pydefx/samplecsvmanager.py @@ -0,0 +1,91 @@ +import csv +import inspect +import os +import pathlib +from .samplecsviterator import SampleIterator +from . import samplecsviterator +from . import sample + +class SampleManager: + def __init__(self): + pass + + def prepareRun(self, sample, directory): + """ + Create a dump of the sample in the given directory. + Return a list of files to add to the input files list of the job. + """ + datapath = os.path.join(directory, SampleIterator.DATAFILE) + with open(datapath, 'w', newline='') as csvfile: + writer = csv.DictWriter(csvfile, + fieldnames=sample.getInputNames(), + quoting=csv.QUOTE_NONNUMERIC ) + writer.writeheader() + writer.writerows(sample.inputIterator()) + + outnamespath = os.path.join(directory, SampleIterator.OUTPUTNAMESFILE) + with open(outnamespath, 'w') as outputfile: + for v in sample.getOutputNames(): + outputfile.write(v+'\n') + filename = inspect.getframeinfo(inspect.currentframe()).filename + install_directory = pathlib.Path(filename).resolve().parent + iteratorFile = os.path.join(install_directory, "samplecsviterator.py") + return [datapath, + outnamespath, + iteratorFile + ] + + def loadResult(self, sample, directory): + """ The directory should contain a file with the name given by + getResultFileName. The results are loaded from that file to the sample. + Return the modified sample. + """ + datapath = os.path.join(directory, SampleIterator.RESULTFILE) + with open(datapath, newline='') as datafile: + data = csv.DictReader(datafile, quoting=csv.QUOTE_NONNUMERIC) + for elt in data: + index = int(elt[SampleIterator.IDCOLUMN]) # float to int + input_vals = {} + for name in sample.getInputNames(): + input_vals[name] = elt[name] + output_vals = {} + for name in sample.getOutputNames(): + output_vals[name] = elt[name] + try: + sample.checkId(index, input_vals) + except Exception as err: + extraInfo = "Error on processing file {} index number {}:".format( + datapath, str(index)) + raise Exception(extraInfo + str(err)) + sample.addResult(index, output_vals, elt[SampleIterator.ERRORCOLUMN]) + return sample + + def loadSample(self, directory): + """ The directory should contain the files created by prepareRun. A new + sample object is created and returned from those files. + This function is used to recover a previous run. + """ + outputnamesfile = os.path.join(directory, SampleIterator.OUTPUTNAMESFILE) + outputnames = samplecsviterator._loadOutputNames(outputnamesfile) + inputFilePath = os.path.join(directory, SampleIterator.DATAFILE) + with open(inputFilePath) as datafile: + data = csv.DictReader(datafile, quoting=csv.QUOTE_NONNUMERIC) + inputvalues = {} + for name in data.fieldnames: + inputvalues[name] = [] + for line in data: + for name in data.fieldnames: + inputvalues[name].append(line[name]) + result = sample.Sample(data.fieldnames, outputnames) + result.setInputValues(inputvalues) + return result + + + def getModuleName(self): + """ + Return the module name which contains the class SampleIterator. + """ + return "samplecsviterator" + + def getResultFileName(self): + return SampleIterator.RESULTFILE diff --git a/src/pydefx/samplemanager.py b/src/pydefx/samplemanager.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pydefx/schemas/CMakeLists.txt b/src/pydefx/schemas/CMakeLists.txt new file mode 100644 index 0000000..fbd799b --- /dev/null +++ b/src/pydefx/schemas/CMakeLists.txt @@ -0,0 +1,6 @@ +SET(SCHEMA_FILES + plugin.py + idefix_pyschema.xml + ) + +INSTALL(FILES ${SCHEMA_FILES} DESTINATION ${SALOME_INSTALL_PYTHON}/pydefx/schemas) diff --git a/src/pydefx/schemas/idefix_pyschema.xml b/src/pydefx/schemas/idefix_pyschema.xml new file mode 100644 index 0000000..40b028c --- /dev/null +++ b/src/pydefx/schemas/idefix_pyschema.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Initialisation OptimizerLoop0 + + OptimizerLoop0 evalSamples + OptimizerLoop0.Solver i0 + + + Initialisation nbbranches + OptimizerLoop0 nbBranches + + + Initialisation studymodule + OptimizerLoop0.Solver studymodule + + + OptimizerLoop0.Solver o0 + OptimizerLoop0 evalResults + + + OptimizerLoop0nbBranches + 4 + + + + + + diff --git a/src/pydefx/schemas/plugin.py b/src/pydefx/schemas/plugin.py new file mode 100644 index 0000000..3abc68f --- /dev/null +++ b/src/pydefx/schemas/plugin.py @@ -0,0 +1,76 @@ +import SALOMERuntime +import pickle +import json +import importlib + +class myalgosync(SALOMERuntime.OptimizerAlgSync): + def __init__(self): + SALOMERuntime.OptimizerAlgSync.__init__(self, None) + + def setPool(self,pool): + """Must be implemented to set the pool""" + self.pool=pool + + def getTCForIn(self): + """return typecode of type expected as Input of the internal node """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForOut(self): + """return typecode of type expected as Output of the internal node""" + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForAlgoInit(self): + """return typecode of type expected as input for initialize """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForAlgoResult(self): + """return typecode of type expected as output of the algorithm """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("int") + + def initialize(self,input): + """Optional method called on initialization. + The type of "input" is returned by "getTCForAlgoInit" + """ + with open("idefixconfig.json", "r") as f: + self.config = json.load(f) + + def start(self): + """Start to fill the pool with samples to evaluate.""" + itModuleName = self.config["sampleIterator"] + itModule = importlib.import_module(itModuleName) + self.data = itModule.SampleIterator() + values=None + for i in range(0, self.getNbOfBranches()): + try: + newid, values = next(self.data) + self.pool.pushInSample(newid, pickle.dumps(values, protocol=0).decode()) + except StopIteration: + pass + + def takeDecision(self): + """ This method is called each time a sample has been evaluated. It can + either add new samples to evaluate in the pool, do nothing (wait for + more samples), or empty the pool to finish the evaluation. + """ + currentId=self.pool.getCurrentId() + samplebyte=self.pool.getCurrentInSample().getStringValue().encode() + sample = pickle.loads(samplebyte) + resultbyte=self.pool.getCurrentOutSample().getStringValue().encode() + error,result = pickle.loads(resultbyte) + self.data.addResult(currentId, sample, result, error) + try: + newid, values = next(self.data) + self.pool.pushInSample(newid, pickle.dumps(values, protocol=0).decode()) + except StopIteration: + pass + + def finish(self): + """Optional method called when the algorithm has finished, successfully + or not, to perform any necessary clean up.""" + self.pool.destroyAll() + + def getAlgoResult(self): + """return the result of the algorithm. + The object returned is of type indicated by getTCForAlgoResult. + """ + return 0 diff --git a/src/pyexample/findidefixdata.py b/src/pyexample/findidefixdata.py new file mode 100755 index 0000000..bf34e9c --- /dev/null +++ b/src/pyexample/findidefixdata.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +#from inspect import currentframe, getframeinfo +#from pathlib import Path + +#filename = getframeinfo(currentframe()).filename +#parent = Path(filename).resolve().parent +import inspect +import pathlib + +filename = inspect.getframeinfo(inspect.currentframe()).filename +parent = pathlib.Path(filename).resolve().parent +print(parent) diff --git a/src/pyexample/idefixconfig.json b/src/pyexample/idefixconfig.json new file mode 100644 index 0000000..b08cc10 --- /dev/null +++ b/src/pyexample/idefixconfig.json @@ -0,0 +1,5 @@ +{ + "nbbranches": 8, + "studymodule": "idefixstudy", + "sampleIterator": "samplecsviterator" +} \ No newline at end of file diff --git a/src/pyexample/idefixconfig.py b/src/pyexample/idefixconfig.py new file mode 100644 index 0000000..e9e0cf9 --- /dev/null +++ b/src/pyexample/idefixconfig.py @@ -0,0 +1,2 @@ +nbbranches=8 +studymodule="idefixstudy" diff --git a/src/pyexample/idefixdata.csv b/src/pyexample/idefixdata.csv new file mode 100644 index 0000000..22caa39 --- /dev/null +++ b/src/pyexample/idefixdata.csv @@ -0,0 +1,1001 @@ +"x","y","z" +0.0,0.0,0.0 +1.0,0.0,0.0 +2.0,0.0,0.0 +3.0,0.0,0.0 +4.0,0.0,0.0 +5.0,0.0,0.0 +6.0,0.0,0.0 +7.0,0.0,0.0 +8.0,0.0,0.0 +9.0,0.0,0.0 +0.0,10.0,0.0 +1.0,10.0,0.0 +2.0,10.0,0.0 +3.0,10.0,0.0 +4.0,10.0,0.0 +5.0,10.0,0.0 +6.0,10.0,0.0 +7.0,10.0,0.0 +8.0,10.0,0.0 +9.0,10.0,0.0 +0.0,20.0,0.0 +1.0,20.0,0.0 +2.0,20.0,0.0 +3.0,20.0,0.0 +4.0,20.0,0.0 +5.0,20.0,0.0 +6.0,20.0,0.0 +7.0,20.0,0.0 +8.0,20.0,0.0 +9.0,20.0,0.0 +0.0,30.0,0.0 +1.0,30.0,0.0 +2.0,30.0,0.0 +3.0,30.0,0.0 +4.0,30.0,0.0 +5.0,30.0,0.0 +6.0,30.0,0.0 +7.0,30.0,0.0 +8.0,30.0,0.0 +9.0,30.0,0.0 +0.0,40.0,0.0 +1.0,40.0,0.0 +2.0,40.0,0.0 +3.0,40.0,0.0 +4.0,40.0,0.0 +5.0,40.0,0.0 +6.0,40.0,0.0 +7.0,40.0,0.0 +8.0,40.0,0.0 +9.0,40.0,0.0 +0.0,50.0,0.0 +1.0,50.0,0.0 +2.0,50.0,0.0 +3.0,50.0,0.0 +4.0,50.0,0.0 +5.0,50.0,0.0 +6.0,50.0,0.0 +7.0,50.0,0.0 +8.0,50.0,0.0 +9.0,50.0,0.0 +0.0,60.0,0.0 +1.0,60.0,0.0 +2.0,60.0,0.0 +3.0,60.0,0.0 +4.0,60.0,0.0 +5.0,60.0,0.0 +6.0,60.0,0.0 +7.0,60.0,0.0 +8.0,60.0,0.0 +9.0,60.0,0.0 +0.0,70.0,0.0 +1.0,70.0,0.0 +2.0,70.0,0.0 +3.0,70.0,0.0 +4.0,70.0,0.0 +5.0,70.0,0.0 +6.0,70.0,0.0 +7.0,70.0,0.0 +8.0,70.0,0.0 +9.0,70.0,0.0 +0.0,80.0,0.0 +1.0,80.0,0.0 +2.0,80.0,0.0 +3.0,80.0,0.0 +4.0,80.0,0.0 +5.0,80.0,0.0 +6.0,80.0,0.0 +7.0,80.0,0.0 +8.0,80.0,0.0 +9.0,80.0,0.0 +0.0,90.0,0.0 +1.0,90.0,0.0 +2.0,90.0,0.0 +3.0,90.0,0.0 +4.0,90.0,0.0 +5.0,90.0,0.0 +6.0,90.0,0.0 +7.0,90.0,0.0 +8.0,90.0,0.0 +9.0,90.0,0.0 +0.0,0.0,100.0 +1.0,0.0,100.0 +2.0,0.0,100.0 +3.0,0.0,100.0 +4.0,0.0,100.0 +5.0,0.0,100.0 +6.0,0.0,100.0 +7.0,0.0,100.0 +8.0,0.0,100.0 +9.0,0.0,100.0 +0.0,10.0,100.0 +1.0,10.0,100.0 +2.0,10.0,100.0 +3.0,10.0,100.0 +4.0,10.0,100.0 +5.0,10.0,100.0 +6.0,10.0,100.0 +7.0,10.0,100.0 +8.0,10.0,100.0 +9.0,10.0,100.0 +0.0,20.0,100.0 +1.0,20.0,100.0 +2.0,20.0,100.0 +3.0,20.0,100.0 +4.0,20.0,100.0 +5.0,20.0,100.0 +6.0,20.0,100.0 +7.0,20.0,100.0 +8.0,20.0,100.0 +9.0,20.0,100.0 +0.0,30.0,100.0 +1.0,30.0,100.0 +2.0,30.0,100.0 +3.0,30.0,100.0 +4.0,30.0,100.0 +5.0,30.0,100.0 +6.0,30.0,100.0 +7.0,30.0,100.0 +8.0,30.0,100.0 +9.0,30.0,100.0 +0.0,40.0,100.0 +1.0,40.0,100.0 +2.0,40.0,100.0 +3.0,40.0,100.0 +4.0,40.0,100.0 +5.0,40.0,100.0 +6.0,40.0,100.0 +7.0,40.0,100.0 +8.0,40.0,100.0 +9.0,40.0,100.0 +0.0,50.0,100.0 +1.0,50.0,100.0 +2.0,50.0,100.0 +3.0,50.0,100.0 +4.0,50.0,100.0 +5.0,50.0,100.0 +6.0,50.0,100.0 +7.0,50.0,100.0 +8.0,50.0,100.0 +9.0,50.0,100.0 +0.0,60.0,100.0 +1.0,60.0,100.0 +2.0,60.0,100.0 +3.0,60.0,100.0 +4.0,60.0,100.0 +5.0,60.0,100.0 +6.0,60.0,100.0 +7.0,60.0,100.0 +8.0,60.0,100.0 +9.0,60.0,100.0 +0.0,70.0,100.0 +1.0,70.0,100.0 +2.0,70.0,100.0 +3.0,70.0,100.0 +4.0,70.0,100.0 +5.0,70.0,100.0 +6.0,70.0,100.0 +7.0,70.0,100.0 +8.0,70.0,100.0 +9.0,70.0,100.0 +0.0,80.0,100.0 +1.0,80.0,100.0 +2.0,80.0,100.0 +3.0,80.0,100.0 +4.0,80.0,100.0 +5.0,80.0,100.0 +6.0,80.0,100.0 +7.0,80.0,100.0 +8.0,80.0,100.0 +9.0,80.0,100.0 +0.0,90.0,100.0 +1.0,90.0,100.0 +2.0,90.0,100.0 +3.0,90.0,100.0 +4.0,90.0,100.0 +5.0,90.0,100.0 +6.0,90.0,100.0 +7.0,90.0,100.0 +8.0,90.0,100.0 +9.0,90.0,100.0 +0.0,0.0,200.0 +1.0,0.0,200.0 +2.0,0.0,200.0 +3.0,0.0,200.0 +4.0,0.0,200.0 +5.0,0.0,200.0 +6.0,0.0,200.0 +7.0,0.0,200.0 +8.0,0.0,200.0 +9.0,0.0,200.0 +0.0,10.0,200.0 +1.0,10.0,200.0 +2.0,10.0,200.0 +3.0,10.0,200.0 +4.0,10.0,200.0 +5.0,10.0,200.0 +6.0,10.0,200.0 +7.0,10.0,200.0 +8.0,10.0,200.0 +9.0,10.0,200.0 +0.0,20.0,200.0 +1.0,20.0,200.0 +2.0,20.0,200.0 +3.0,20.0,200.0 +4.0,20.0,200.0 +5.0,20.0,200.0 +6.0,20.0,200.0 +7.0,20.0,200.0 +8.0,20.0,200.0 +9.0,20.0,200.0 +0.0,30.0,200.0 +1.0,30.0,200.0 +2.0,30.0,200.0 +3.0,30.0,200.0 +4.0,30.0,200.0 +5.0,30.0,200.0 +6.0,30.0,200.0 +7.0,30.0,200.0 +8.0,30.0,200.0 +9.0,30.0,200.0 +0.0,40.0,200.0 +1.0,40.0,200.0 +2.0,40.0,200.0 +3.0,40.0,200.0 +4.0,40.0,200.0 +5.0,40.0,200.0 +6.0,40.0,200.0 +7.0,40.0,200.0 +8.0,40.0,200.0 +9.0,40.0,200.0 +0.0,50.0,200.0 +1.0,50.0,200.0 +2.0,50.0,200.0 +3.0,50.0,200.0 +4.0,50.0,200.0 +5.0,50.0,200.0 +6.0,50.0,200.0 +7.0,50.0,200.0 +8.0,50.0,200.0 +9.0,50.0,200.0 +0.0,60.0,200.0 +1.0,60.0,200.0 +2.0,60.0,200.0 +3.0,60.0,200.0 +4.0,60.0,200.0 +5.0,60.0,200.0 +6.0,60.0,200.0 +7.0,60.0,200.0 +8.0,60.0,200.0 +9.0,60.0,200.0 +0.0,70.0,200.0 +1.0,70.0,200.0 +2.0,70.0,200.0 +3.0,70.0,200.0 +4.0,70.0,200.0 +5.0,70.0,200.0 +6.0,70.0,200.0 +7.0,70.0,200.0 +8.0,70.0,200.0 +9.0,70.0,200.0 +0.0,80.0,200.0 +1.0,80.0,200.0 +2.0,80.0,200.0 +3.0,80.0,200.0 +4.0,80.0,200.0 +5.0,80.0,200.0 +6.0,80.0,200.0 +7.0,80.0,200.0 +8.0,80.0,200.0 +9.0,80.0,200.0 +0.0,90.0,200.0 +1.0,90.0,200.0 +2.0,90.0,200.0 +3.0,90.0,200.0 +4.0,90.0,200.0 +5.0,90.0,200.0 +6.0,90.0,200.0 +7.0,90.0,200.0 +8.0,90.0,200.0 +9.0,90.0,200.0 +0.0,0.0,300.0 +1.0,0.0,300.0 +2.0,0.0,300.0 +3.0,0.0,300.0 +4.0,0.0,300.0 +5.0,0.0,300.0 +6.0,0.0,300.0 +7.0,0.0,300.0 +8.0,0.0,300.0 +9.0,0.0,300.0 +0.0,10.0,300.0 +1.0,10.0,300.0 +2.0,10.0,300.0 +3.0,10.0,300.0 +4.0,10.0,300.0 +5.0,10.0,300.0 +6.0,10.0,300.0 +7.0,10.0,300.0 +8.0,10.0,300.0 +9.0,10.0,300.0 +0.0,20.0,300.0 +1.0,20.0,300.0 +2.0,20.0,300.0 +3.0,20.0,300.0 +4.0,20.0,300.0 +5.0,20.0,300.0 +6.0,20.0,300.0 +7.0,20.0,300.0 +8.0,20.0,300.0 +9.0,20.0,300.0 +0.0,30.0,300.0 +1.0,30.0,300.0 +2.0,30.0,300.0 +3.0,30.0,300.0 +4.0,30.0,300.0 +5.0,30.0,300.0 +6.0,30.0,300.0 +7.0,30.0,300.0 +8.0,30.0,300.0 +9.0,30.0,300.0 +0.0,40.0,300.0 +1.0,40.0,300.0 +2.0,40.0,300.0 +3.0,40.0,300.0 +4.0,40.0,300.0 +5.0,40.0,300.0 +6.0,40.0,300.0 +7.0,40.0,300.0 +8.0,40.0,300.0 +9.0,40.0,300.0 +0.0,50.0,300.0 +1.0,50.0,300.0 +2.0,50.0,300.0 +3.0,50.0,300.0 +4.0,50.0,300.0 +5.0,50.0,300.0 +6.0,50.0,300.0 +7.0,50.0,300.0 +8.0,50.0,300.0 +9.0,50.0,300.0 +0.0,60.0,300.0 +1.0,60.0,300.0 +2.0,60.0,300.0 +3.0,60.0,300.0 +4.0,60.0,300.0 +5.0,60.0,300.0 +6.0,60.0,300.0 +7.0,60.0,300.0 +8.0,60.0,300.0 +9.0,60.0,300.0 +0.0,70.0,300.0 +1.0,70.0,300.0 +2.0,70.0,300.0 +3.0,70.0,300.0 +4.0,70.0,300.0 +5.0,70.0,300.0 +6.0,70.0,300.0 +7.0,70.0,300.0 +8.0,70.0,300.0 +9.0,70.0,300.0 +0.0,80.0,300.0 +1.0,80.0,300.0 +2.0,80.0,300.0 +3.0,80.0,300.0 +4.0,80.0,300.0 +5.0,80.0,300.0 +6.0,80.0,300.0 +7.0,80.0,300.0 +8.0,80.0,300.0 +9.0,80.0,300.0 +0.0,90.0,300.0 +1.0,90.0,300.0 +2.0,90.0,300.0 +3.0,90.0,300.0 +4.0,90.0,300.0 +5.0,90.0,300.0 +6.0,90.0,300.0 +7.0,90.0,300.0 +8.0,90.0,300.0 +9.0,90.0,300.0 +0.0,0.0,400.0 +1.0,0.0,400.0 +2.0,0.0,400.0 +3.0,0.0,400.0 +4.0,0.0,400.0 +5.0,0.0,400.0 +6.0,0.0,400.0 +7.0,0.0,400.0 +8.0,0.0,400.0 +9.0,0.0,400.0 +0.0,10.0,400.0 +1.0,10.0,400.0 +2.0,10.0,400.0 +3.0,10.0,400.0 +4.0,10.0,400.0 +5.0,10.0,400.0 +6.0,10.0,400.0 +7.0,10.0,400.0 +8.0,10.0,400.0 +9.0,10.0,400.0 +0.0,20.0,400.0 +1.0,20.0,400.0 +2.0,20.0,400.0 +3.0,20.0,400.0 +4.0,20.0,400.0 +5.0,20.0,400.0 +6.0,20.0,400.0 +7.0,20.0,400.0 +8.0,20.0,400.0 +9.0,20.0,400.0 +0.0,30.0,400.0 +1.0,30.0,400.0 +2.0,30.0,400.0 +3.0,30.0,400.0 +4.0,30.0,400.0 +5.0,30.0,400.0 +6.0,30.0,400.0 +7.0,30.0,400.0 +8.0,30.0,400.0 +9.0,30.0,400.0 +0.0,40.0,400.0 +1.0,40.0,400.0 +2.0,40.0,400.0 +3.0,40.0,400.0 +4.0,40.0,400.0 +5.0,40.0,400.0 +6.0,40.0,400.0 +7.0,40.0,400.0 +8.0,40.0,400.0 +9.0,40.0,400.0 +0.0,50.0,400.0 +1.0,50.0,400.0 +2.0,50.0,400.0 +3.0,50.0,400.0 +4.0,50.0,400.0 +5.0,50.0,400.0 +6.0,50.0,400.0 +7.0,50.0,400.0 +8.0,50.0,400.0 +9.0,50.0,400.0 +0.0,60.0,400.0 +1.0,60.0,400.0 +2.0,60.0,400.0 +3.0,60.0,400.0 +4.0,60.0,400.0 +5.0,60.0,400.0 +6.0,60.0,400.0 +7.0,60.0,400.0 +8.0,60.0,400.0 +9.0,60.0,400.0 +0.0,70.0,400.0 +1.0,70.0,400.0 +2.0,70.0,400.0 +3.0,70.0,400.0 +4.0,70.0,400.0 +5.0,70.0,400.0 +6.0,70.0,400.0 +7.0,70.0,400.0 +8.0,70.0,400.0 +9.0,70.0,400.0 +0.0,80.0,400.0 +1.0,80.0,400.0 +2.0,80.0,400.0 +3.0,80.0,400.0 +4.0,80.0,400.0 +5.0,80.0,400.0 +6.0,80.0,400.0 +7.0,80.0,400.0 +8.0,80.0,400.0 +9.0,80.0,400.0 +0.0,90.0,400.0 +1.0,90.0,400.0 +2.0,90.0,400.0 +3.0,90.0,400.0 +4.0,90.0,400.0 +5.0,90.0,400.0 +6.0,90.0,400.0 +7.0,90.0,400.0 +8.0,90.0,400.0 +9.0,90.0,400.0 +0.0,0.0,500.0 +1.0,0.0,500.0 +2.0,0.0,500.0 +3.0,0.0,500.0 +4.0,0.0,500.0 +5.0,0.0,500.0 +6.0,0.0,500.0 +7.0,0.0,500.0 +8.0,0.0,500.0 +9.0,0.0,500.0 +0.0,10.0,500.0 +1.0,10.0,500.0 +2.0,10.0,500.0 +3.0,10.0,500.0 +4.0,10.0,500.0 +5.0,10.0,500.0 +6.0,10.0,500.0 +7.0,10.0,500.0 +8.0,10.0,500.0 +9.0,10.0,500.0 +0.0,20.0,500.0 +1.0,20.0,500.0 +2.0,20.0,500.0 +3.0,20.0,500.0 +4.0,20.0,500.0 +5.0,20.0,500.0 +6.0,20.0,500.0 +7.0,20.0,500.0 +8.0,20.0,500.0 +9.0,20.0,500.0 +0.0,30.0,500.0 +1.0,30.0,500.0 +2.0,30.0,500.0 +3.0,30.0,500.0 +4.0,30.0,500.0 +5.0,30.0,500.0 +6.0,30.0,500.0 +7.0,30.0,500.0 +8.0,30.0,500.0 +9.0,30.0,500.0 +0.0,40.0,500.0 +1.0,40.0,500.0 +2.0,40.0,500.0 +3.0,40.0,500.0 +4.0,40.0,500.0 +5.0,40.0,500.0 +6.0,40.0,500.0 +7.0,40.0,500.0 +8.0,40.0,500.0 +9.0,40.0,500.0 +0.0,50.0,500.0 +1.0,50.0,500.0 +2.0,50.0,500.0 +3.0,50.0,500.0 +4.0,50.0,500.0 +5.0,50.0,500.0 +6.0,50.0,500.0 +7.0,50.0,500.0 +8.0,50.0,500.0 +9.0,50.0,500.0 +0.0,60.0,500.0 +1.0,60.0,500.0 +2.0,60.0,500.0 +3.0,60.0,500.0 +4.0,60.0,500.0 +5.0,60.0,500.0 +6.0,60.0,500.0 +7.0,60.0,500.0 +8.0,60.0,500.0 +9.0,60.0,500.0 +0.0,70.0,500.0 +1.0,70.0,500.0 +2.0,70.0,500.0 +3.0,70.0,500.0 +4.0,70.0,500.0 +5.0,70.0,500.0 +6.0,70.0,500.0 +7.0,70.0,500.0 +8.0,70.0,500.0 +9.0,70.0,500.0 +0.0,80.0,500.0 +1.0,80.0,500.0 +2.0,80.0,500.0 +3.0,80.0,500.0 +4.0,80.0,500.0 +5.0,80.0,500.0 +6.0,80.0,500.0 +7.0,80.0,500.0 +8.0,80.0,500.0 +9.0,80.0,500.0 +0.0,90.0,500.0 +1.0,90.0,500.0 +2.0,90.0,500.0 +3.0,90.0,500.0 +4.0,90.0,500.0 +5.0,90.0,500.0 +6.0,90.0,500.0 +7.0,90.0,500.0 +8.0,90.0,500.0 +9.0,90.0,500.0 +0.0,0.0,600.0 +1.0,0.0,600.0 +2.0,0.0,600.0 +3.0,0.0,600.0 +4.0,0.0,600.0 +5.0,0.0,600.0 +6.0,0.0,600.0 +7.0,0.0,600.0 +8.0,0.0,600.0 +9.0,0.0,600.0 +0.0,10.0,600.0 +1.0,10.0,600.0 +2.0,10.0,600.0 +3.0,10.0,600.0 +4.0,10.0,600.0 +5.0,10.0,600.0 +6.0,10.0,600.0 +7.0,10.0,600.0 +8.0,10.0,600.0 +9.0,10.0,600.0 +0.0,20.0,600.0 +1.0,20.0,600.0 +2.0,20.0,600.0 +3.0,20.0,600.0 +4.0,20.0,600.0 +5.0,20.0,600.0 +6.0,20.0,600.0 +7.0,20.0,600.0 +8.0,20.0,600.0 +9.0,20.0,600.0 +0.0,30.0,600.0 +1.0,30.0,600.0 +2.0,30.0,600.0 +3.0,30.0,600.0 +4.0,30.0,600.0 +5.0,30.0,600.0 +6.0,30.0,600.0 +7.0,30.0,600.0 +8.0,30.0,600.0 +9.0,30.0,600.0 +0.0,40.0,600.0 +1.0,40.0,600.0 +2.0,40.0,600.0 +3.0,40.0,600.0 +4.0,40.0,600.0 +5.0,40.0,600.0 +6.0,40.0,600.0 +7.0,40.0,600.0 +8.0,40.0,600.0 +9.0,40.0,600.0 +0.0,50.0,600.0 +1.0,50.0,600.0 +2.0,50.0,600.0 +3.0,50.0,600.0 +4.0,50.0,600.0 +5.0,50.0,600.0 +6.0,50.0,600.0 +7.0,50.0,600.0 +8.0,50.0,600.0 +9.0,50.0,600.0 +0.0,60.0,600.0 +1.0,60.0,600.0 +2.0,60.0,600.0 +3.0,60.0,600.0 +4.0,60.0,600.0 +5.0,60.0,600.0 +6.0,60.0,600.0 +7.0,60.0,600.0 +8.0,60.0,600.0 +9.0,60.0,600.0 +0.0,70.0,600.0 +1.0,70.0,600.0 +2.0,70.0,600.0 +3.0,70.0,600.0 +4.0,70.0,600.0 +5.0,70.0,600.0 +6.0,70.0,600.0 +7.0,70.0,600.0 +8.0,70.0,600.0 +9.0,70.0,600.0 +0.0,80.0,600.0 +1.0,80.0,600.0 +2.0,80.0,600.0 +3.0,80.0,600.0 +4.0,80.0,600.0 +5.0,80.0,600.0 +6.0,80.0,600.0 +7.0,80.0,600.0 +8.0,80.0,600.0 +9.0,80.0,600.0 +0.0,90.0,600.0 +1.0,90.0,600.0 +2.0,90.0,600.0 +3.0,90.0,600.0 +4.0,90.0,600.0 +5.0,90.0,600.0 +6.0,90.0,600.0 +7.0,90.0,600.0 +8.0,90.0,600.0 +9.0,90.0,600.0 +0.0,0.0,700.0 +1.0,0.0,700.0 +2.0,0.0,700.0 +3.0,0.0,700.0 +4.0,0.0,700.0 +5.0,0.0,700.0 +6.0,0.0,700.0 +7.0,0.0,700.0 +8.0,0.0,700.0 +9.0,0.0,700.0 +0.0,10.0,700.0 +1.0,10.0,700.0 +2.0,10.0,700.0 +3.0,10.0,700.0 +4.0,10.0,700.0 +5.0,10.0,700.0 +6.0,10.0,700.0 +7.0,10.0,700.0 +8.0,10.0,700.0 +9.0,10.0,700.0 +0.0,20.0,700.0 +1.0,20.0,700.0 +2.0,20.0,700.0 +3.0,20.0,700.0 +4.0,20.0,700.0 +5.0,20.0,700.0 +6.0,20.0,700.0 +7.0,20.0,700.0 +8.0,20.0,700.0 +9.0,20.0,700.0 +0.0,30.0,700.0 +1.0,30.0,700.0 +2.0,30.0,700.0 +3.0,30.0,700.0 +4.0,30.0,700.0 +5.0,30.0,700.0 +6.0,30.0,700.0 +7.0,30.0,700.0 +8.0,30.0,700.0 +9.0,30.0,700.0 +0.0,40.0,700.0 +1.0,40.0,700.0 +2.0,40.0,700.0 +3.0,40.0,700.0 +4.0,40.0,700.0 +5.0,40.0,700.0 +6.0,40.0,700.0 +7.0,40.0,700.0 +8.0,40.0,700.0 +9.0,40.0,700.0 +0.0,50.0,700.0 +1.0,50.0,700.0 +2.0,50.0,700.0 +3.0,50.0,700.0 +4.0,50.0,700.0 +5.0,50.0,700.0 +6.0,50.0,700.0 +7.0,50.0,700.0 +8.0,50.0,700.0 +9.0,50.0,700.0 +0.0,60.0,700.0 +1.0,60.0,700.0 +2.0,60.0,700.0 +3.0,60.0,700.0 +4.0,60.0,700.0 +5.0,60.0,700.0 +6.0,60.0,700.0 +7.0,60.0,700.0 +8.0,60.0,700.0 +9.0,60.0,700.0 +0.0,70.0,700.0 +1.0,70.0,700.0 +2.0,70.0,700.0 +3.0,70.0,700.0 +4.0,70.0,700.0 +5.0,70.0,700.0 +6.0,70.0,700.0 +7.0,70.0,700.0 +8.0,70.0,700.0 +9.0,70.0,700.0 +0.0,80.0,700.0 +1.0,80.0,700.0 +2.0,80.0,700.0 +3.0,80.0,700.0 +4.0,80.0,700.0 +5.0,80.0,700.0 +6.0,80.0,700.0 +7.0,80.0,700.0 +8.0,80.0,700.0 +9.0,80.0,700.0 +0.0,90.0,700.0 +1.0,90.0,700.0 +2.0,90.0,700.0 +3.0,90.0,700.0 +4.0,90.0,700.0 +5.0,90.0,700.0 +6.0,90.0,700.0 +7.0,90.0,700.0 +8.0,90.0,700.0 +9.0,90.0,700.0 +0.0,0.0,800.0 +1.0,0.0,800.0 +2.0,0.0,800.0 +3.0,0.0,800.0 +4.0,0.0,800.0 +5.0,0.0,800.0 +6.0,0.0,800.0 +7.0,0.0,800.0 +8.0,0.0,800.0 +9.0,0.0,800.0 +0.0,10.0,800.0 +1.0,10.0,800.0 +2.0,10.0,800.0 +3.0,10.0,800.0 +4.0,10.0,800.0 +5.0,10.0,800.0 +6.0,10.0,800.0 +7.0,10.0,800.0 +8.0,10.0,800.0 +9.0,10.0,800.0 +0.0,20.0,800.0 +1.0,20.0,800.0 +2.0,20.0,800.0 +3.0,20.0,800.0 +4.0,20.0,800.0 +5.0,20.0,800.0 +6.0,20.0,800.0 +7.0,20.0,800.0 +8.0,20.0,800.0 +9.0,20.0,800.0 +0.0,30.0,800.0 +1.0,30.0,800.0 +2.0,30.0,800.0 +3.0,30.0,800.0 +4.0,30.0,800.0 +5.0,30.0,800.0 +6.0,30.0,800.0 +7.0,30.0,800.0 +8.0,30.0,800.0 +9.0,30.0,800.0 +0.0,40.0,800.0 +1.0,40.0,800.0 +2.0,40.0,800.0 +3.0,40.0,800.0 +4.0,40.0,800.0 +5.0,40.0,800.0 +6.0,40.0,800.0 +7.0,40.0,800.0 +8.0,40.0,800.0 +9.0,40.0,800.0 +0.0,50.0,800.0 +1.0,50.0,800.0 +2.0,50.0,800.0 +3.0,50.0,800.0 +4.0,50.0,800.0 +5.0,50.0,800.0 +6.0,50.0,800.0 +7.0,50.0,800.0 +8.0,50.0,800.0 +9.0,50.0,800.0 +0.0,60.0,800.0 +1.0,60.0,800.0 +2.0,60.0,800.0 +3.0,60.0,800.0 +4.0,60.0,800.0 +5.0,60.0,800.0 +6.0,60.0,800.0 +7.0,60.0,800.0 +8.0,60.0,800.0 +9.0,60.0,800.0 +0.0,70.0,800.0 +1.0,70.0,800.0 +2.0,70.0,800.0 +3.0,70.0,800.0 +4.0,70.0,800.0 +5.0,70.0,800.0 +6.0,70.0,800.0 +7.0,70.0,800.0 +8.0,70.0,800.0 +9.0,70.0,800.0 +0.0,80.0,800.0 +1.0,80.0,800.0 +2.0,80.0,800.0 +3.0,80.0,800.0 +4.0,80.0,800.0 +5.0,80.0,800.0 +6.0,80.0,800.0 +7.0,80.0,800.0 +8.0,80.0,800.0 +9.0,80.0,800.0 +0.0,90.0,800.0 +1.0,90.0,800.0 +2.0,90.0,800.0 +3.0,90.0,800.0 +4.0,90.0,800.0 +5.0,90.0,800.0 +6.0,90.0,800.0 +7.0,90.0,800.0 +8.0,90.0,800.0 +9.0,90.0,800.0 +0.0,0.0,900.0 +1.0,0.0,900.0 +2.0,0.0,900.0 +3.0,0.0,900.0 +4.0,0.0,900.0 +5.0,0.0,900.0 +6.0,0.0,900.0 +7.0,0.0,900.0 +8.0,0.0,900.0 +9.0,0.0,900.0 +0.0,10.0,900.0 +1.0,10.0,900.0 +2.0,10.0,900.0 +3.0,10.0,900.0 +4.0,10.0,900.0 +5.0,10.0,900.0 +6.0,10.0,900.0 +7.0,10.0,900.0 +8.0,10.0,900.0 +9.0,10.0,900.0 +0.0,20.0,900.0 +1.0,20.0,900.0 +2.0,20.0,900.0 +3.0,20.0,900.0 +4.0,20.0,900.0 +5.0,20.0,900.0 +6.0,20.0,900.0 +7.0,20.0,900.0 +8.0,20.0,900.0 +9.0,20.0,900.0 +0.0,30.0,900.0 +1.0,30.0,900.0 +2.0,30.0,900.0 +3.0,30.0,900.0 +4.0,30.0,900.0 +5.0,30.0,900.0 +6.0,30.0,900.0 +7.0,30.0,900.0 +8.0,30.0,900.0 +9.0,30.0,900.0 +0.0,40.0,900.0 +1.0,40.0,900.0 +2.0,40.0,900.0 +3.0,40.0,900.0 +4.0,40.0,900.0 +5.0,40.0,900.0 +6.0,40.0,900.0 +7.0,40.0,900.0 +8.0,40.0,900.0 +9.0,40.0,900.0 +0.0,50.0,900.0 +1.0,50.0,900.0 +2.0,50.0,900.0 +3.0,50.0,900.0 +4.0,50.0,900.0 +5.0,50.0,900.0 +6.0,50.0,900.0 +7.0,50.0,900.0 +8.0,50.0,900.0 +9.0,50.0,900.0 +0.0,60.0,900.0 +1.0,60.0,900.0 +2.0,60.0,900.0 +3.0,60.0,900.0 +4.0,60.0,900.0 +5.0,60.0,900.0 +6.0,60.0,900.0 +7.0,60.0,900.0 +8.0,60.0,900.0 +9.0,60.0,900.0 +0.0,70.0,900.0 +1.0,70.0,900.0 +2.0,70.0,900.0 +3.0,70.0,900.0 +4.0,70.0,900.0 +5.0,70.0,900.0 +6.0,70.0,900.0 +7.0,70.0,900.0 +8.0,70.0,900.0 +9.0,70.0,900.0 +0.0,80.0,900.0 +1.0,80.0,900.0 +2.0,80.0,900.0 +3.0,80.0,900.0 +4.0,80.0,900.0 +5.0,80.0,900.0 +6.0,80.0,900.0 +7.0,80.0,900.0 +8.0,80.0,900.0 +9.0,80.0,900.0 +0.0,90.0,900.0 +1.0,90.0,900.0 +2.0,90.0,900.0 +3.0,90.0,900.0 +4.0,90.0,900.0 +5.0,90.0,900.0 +6.0,90.0,900.0 +7.0,90.0,900.0 +8.0,90.0,900.0 +9.0,90.0,900.0 diff --git a/src/pyexample/idefixoutputnames.csv b/src/pyexample/idefixoutputnames.csv new file mode 100644 index 0000000..b478595 --- /dev/null +++ b/src/pyexample/idefixoutputnames.csv @@ -0,0 +1 @@ +s diff --git a/src/pyexample/idefixstudy.py b/src/pyexample/idefixstudy.py new file mode 100644 index 0000000..895c6ad --- /dev/null +++ b/src/pyexample/idefixstudy.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +def _exec(x,y,z): + s = (x + y + z) / z + import time + time.sleep(1) + return s diff --git a/src/pyexample/input.csv b/src/pyexample/input.csv new file mode 100644 index 0000000..56956a6 --- /dev/null +++ b/src/pyexample/input.csv @@ -0,0 +1,1001 @@ +"x","y","z" +0,0,0 +1,0,0 +2,0,0 +3,0,0 +4,0,0 +5,0,0 +6,0,0 +7,0,0 +8,0,0 +9,0,0 +0,10,0 +1,10,0 +2,10,0 +3,10,0 +4,10,0 +5,10,0 +6,10,0 +7,10,0 +8,10,0 +9,10,0 +0,20,0 +1,20,0 +2,20,0 +3,20,0 +4,20,0 +5,20,0 +6,20,0 +7,20,0 +8,20,0 +9,20,0 +0,30,0 +1,30,0 +2,30,0 +3,30,0 +4,30,0 +5,30,0 +6,30,0 +7,30,0 +8,30,0 +9,30,0 +0,40,0 +1,40,0 +2,40,0 +3,40,0 +4,40,0 +5,40,0 +6,40,0 +7,40,0 +8,40,0 +9,40,0 +0,50,0 +1,50,0 +2,50,0 +3,50,0 +4,50,0 +5,50,0 +6,50,0 +7,50,0 +8,50,0 +9,50,0 +0,60,0 +1,60,0 +2,60,0 +3,60,0 +4,60,0 +5,60,0 +6,60,0 +7,60,0 +8,60,0 +9,60,0 +0,70,0 +1,70,0 +2,70,0 +3,70,0 +4,70,0 +5,70,0 +6,70,0 +7,70,0 +8,70,0 +9,70,0 +0,80,0 +1,80,0 +2,80,0 +3,80,0 +4,80,0 +5,80,0 +6,80,0 +7,80,0 +8,80,0 +9,80,0 +0,90,0 +1,90,0 +2,90,0 +3,90,0 +4,90,0 +5,90,0 +6,90,0 +7,90,0 +8,90,0 +9,90,0 +0,0,100 +1,0,100 +2,0,100 +3,0,100 +4,0,100 +5,0,100 +6,0,100 +7,0,100 +8,0,100 +9,0,100 +0,10,100 +1,10,100 +2,10,100 +3,10,100 +4,10,100 +5,10,100 +6,10,100 +7,10,100 +8,10,100 +9,10,100 +0,20,100 +1,20,100 +2,20,100 +3,20,100 +4,20,100 +5,20,100 +6,20,100 +7,20,100 +8,20,100 +9,20,100 +0,30,100 +1,30,100 +2,30,100 +3,30,100 +4,30,100 +5,30,100 +6,30,100 +7,30,100 +8,30,100 +9,30,100 +0,40,100 +1,40,100 +2,40,100 +3,40,100 +4,40,100 +5,40,100 +6,40,100 +7,40,100 +8,40,100 +9,40,100 +0,50,100 +1,50,100 +2,50,100 +3,50,100 +4,50,100 +5,50,100 +6,50,100 +7,50,100 +8,50,100 +9,50,100 +0,60,100 +1,60,100 +2,60,100 +3,60,100 +4,60,100 +5,60,100 +6,60,100 +7,60,100 +8,60,100 +9,60,100 +0,70,100 +1,70,100 +2,70,100 +3,70,100 +4,70,100 +5,70,100 +6,70,100 +7,70,100 +8,70,100 +9,70,100 +0,80,100 +1,80,100 +2,80,100 +3,80,100 +4,80,100 +5,80,100 +6,80,100 +7,80,100 +8,80,100 +9,80,100 +0,90,100 +1,90,100 +2,90,100 +3,90,100 +4,90,100 +5,90,100 +6,90,100 +7,90,100 +8,90,100 +9,90,100 +0,0,200 +1,0,200 +2,0,200 +3,0,200 +4,0,200 +5,0,200 +6,0,200 +7,0,200 +8,0,200 +9,0,200 +0,10,200 +1,10,200 +2,10,200 +3,10,200 +4,10,200 +5,10,200 +6,10,200 +7,10,200 +8,10,200 +9,10,200 +0,20,200 +1,20,200 +2,20,200 +3,20,200 +4,20,200 +5,20,200 +6,20,200 +7,20,200 +8,20,200 +9,20,200 +0,30,200 +1,30,200 +2,30,200 +3,30,200 +4,30,200 +5,30,200 +6,30,200 +7,30,200 +8,30,200 +9,30,200 +0,40,200 +1,40,200 +2,40,200 +3,40,200 +4,40,200 +5,40,200 +6,40,200 +7,40,200 +8,40,200 +9,40,200 +0,50,200 +1,50,200 +2,50,200 +3,50,200 +4,50,200 +5,50,200 +6,50,200 +7,50,200 +8,50,200 +9,50,200 +0,60,200 +1,60,200 +2,60,200 +3,60,200 +4,60,200 +5,60,200 +6,60,200 +7,60,200 +8,60,200 +9,60,200 +0,70,200 +1,70,200 +2,70,200 +3,70,200 +4,70,200 +5,70,200 +6,70,200 +7,70,200 +8,70,200 +9,70,200 +0,80,200 +1,80,200 +2,80,200 +3,80,200 +4,80,200 +5,80,200 +6,80,200 +7,80,200 +8,80,200 +9,80,200 +0,90,200 +1,90,200 +2,90,200 +3,90,200 +4,90,200 +5,90,200 +6,90,200 +7,90,200 +8,90,200 +9,90,200 +0,0,300 +1,0,300 +2,0,300 +3,0,300 +4,0,300 +5,0,300 +6,0,300 +7,0,300 +8,0,300 +9,0,300 +0,10,300 +1,10,300 +2,10,300 +3,10,300 +4,10,300 +5,10,300 +6,10,300 +7,10,300 +8,10,300 +9,10,300 +0,20,300 +1,20,300 +2,20,300 +3,20,300 +4,20,300 +5,20,300 +6,20,300 +7,20,300 +8,20,300 +9,20,300 +0,30,300 +1,30,300 +2,30,300 +3,30,300 +4,30,300 +5,30,300 +6,30,300 +7,30,300 +8,30,300 +9,30,300 +0,40,300 +1,40,300 +2,40,300 +3,40,300 +4,40,300 +5,40,300 +6,40,300 +7,40,300 +8,40,300 +9,40,300 +0,50,300 +1,50,300 +2,50,300 +3,50,300 +4,50,300 +5,50,300 +6,50,300 +7,50,300 +8,50,300 +9,50,300 +0,60,300 +1,60,300 +2,60,300 +3,60,300 +4,60,300 +5,60,300 +6,60,300 +7,60,300 +8,60,300 +9,60,300 +0,70,300 +1,70,300 +2,70,300 +3,70,300 +4,70,300 +5,70,300 +6,70,300 +7,70,300 +8,70,300 +9,70,300 +0,80,300 +1,80,300 +2,80,300 +3,80,300 +4,80,300 +5,80,300 +6,80,300 +7,80,300 +8,80,300 +9,80,300 +0,90,300 +1,90,300 +2,90,300 +3,90,300 +4,90,300 +5,90,300 +6,90,300 +7,90,300 +8,90,300 +9,90,300 +0,0,400 +1,0,400 +2,0,400 +3,0,400 +4,0,400 +5,0,400 +6,0,400 +7,0,400 +8,0,400 +9,0,400 +0,10,400 +1,10,400 +2,10,400 +3,10,400 +4,10,400 +5,10,400 +6,10,400 +7,10,400 +8,10,400 +9,10,400 +0,20,400 +1,20,400 +2,20,400 +3,20,400 +4,20,400 +5,20,400 +6,20,400 +7,20,400 +8,20,400 +9,20,400 +0,30,400 +1,30,400 +2,30,400 +3,30,400 +4,30,400 +5,30,400 +6,30,400 +7,30,400 +8,30,400 +9,30,400 +0,40,400 +1,40,400 +2,40,400 +3,40,400 +4,40,400 +5,40,400 +6,40,400 +7,40,400 +8,40,400 +9,40,400 +0,50,400 +1,50,400 +2,50,400 +3,50,400 +4,50,400 +5,50,400 +6,50,400 +7,50,400 +8,50,400 +9,50,400 +0,60,400 +1,60,400 +2,60,400 +3,60,400 +4,60,400 +5,60,400 +6,60,400 +7,60,400 +8,60,400 +9,60,400 +0,70,400 +1,70,400 +2,70,400 +3,70,400 +4,70,400 +5,70,400 +6,70,400 +7,70,400 +8,70,400 +9,70,400 +0,80,400 +1,80,400 +2,80,400 +3,80,400 +4,80,400 +5,80,400 +6,80,400 +7,80,400 +8,80,400 +9,80,400 +0,90,400 +1,90,400 +2,90,400 +3,90,400 +4,90,400 +5,90,400 +6,90,400 +7,90,400 +8,90,400 +9,90,400 +0,0,500 +1,0,500 +2,0,500 +3,0,500 +4,0,500 +5,0,500 +6,0,500 +7,0,500 +8,0,500 +9,0,500 +0,10,500 +1,10,500 +2,10,500 +3,10,500 +4,10,500 +5,10,500 +6,10,500 +7,10,500 +8,10,500 +9,10,500 +0,20,500 +1,20,500 +2,20,500 +3,20,500 +4,20,500 +5,20,500 +6,20,500 +7,20,500 +8,20,500 +9,20,500 +0,30,500 +1,30,500 +2,30,500 +3,30,500 +4,30,500 +5,30,500 +6,30,500 +7,30,500 +8,30,500 +9,30,500 +0,40,500 +1,40,500 +2,40,500 +3,40,500 +4,40,500 +5,40,500 +6,40,500 +7,40,500 +8,40,500 +9,40,500 +0,50,500 +1,50,500 +2,50,500 +3,50,500 +4,50,500 +5,50,500 +6,50,500 +7,50,500 +8,50,500 +9,50,500 +0,60,500 +1,60,500 +2,60,500 +3,60,500 +4,60,500 +5,60,500 +6,60,500 +7,60,500 +8,60,500 +9,60,500 +0,70,500 +1,70,500 +2,70,500 +3,70,500 +4,70,500 +5,70,500 +6,70,500 +7,70,500 +8,70,500 +9,70,500 +0,80,500 +1,80,500 +2,80,500 +3,80,500 +4,80,500 +5,80,500 +6,80,500 +7,80,500 +8,80,500 +9,80,500 +0,90,500 +1,90,500 +2,90,500 +3,90,500 +4,90,500 +5,90,500 +6,90,500 +7,90,500 +8,90,500 +9,90,500 +0,0,600 +1,0,600 +2,0,600 +3,0,600 +4,0,600 +5,0,600 +6,0,600 +7,0,600 +8,0,600 +9,0,600 +0,10,600 +1,10,600 +2,10,600 +3,10,600 +4,10,600 +5,10,600 +6,10,600 +7,10,600 +8,10,600 +9,10,600 +0,20,600 +1,20,600 +2,20,600 +3,20,600 +4,20,600 +5,20,600 +6,20,600 +7,20,600 +8,20,600 +9,20,600 +0,30,600 +1,30,600 +2,30,600 +3,30,600 +4,30,600 +5,30,600 +6,30,600 +7,30,600 +8,30,600 +9,30,600 +0,40,600 +1,40,600 +2,40,600 +3,40,600 +4,40,600 +5,40,600 +6,40,600 +7,40,600 +8,40,600 +9,40,600 +0,50,600 +1,50,600 +2,50,600 +3,50,600 +4,50,600 +5,50,600 +6,50,600 +7,50,600 +8,50,600 +9,50,600 +0,60,600 +1,60,600 +2,60,600 +3,60,600 +4,60,600 +5,60,600 +6,60,600 +7,60,600 +8,60,600 +9,60,600 +0,70,600 +1,70,600 +2,70,600 +3,70,600 +4,70,600 +5,70,600 +6,70,600 +7,70,600 +8,70,600 +9,70,600 +0,80,600 +1,80,600 +2,80,600 +3,80,600 +4,80,600 +5,80,600 +6,80,600 +7,80,600 +8,80,600 +9,80,600 +0,90,600 +1,90,600 +2,90,600 +3,90,600 +4,90,600 +5,90,600 +6,90,600 +7,90,600 +8,90,600 +9,90,600 +0,0,700 +1,0,700 +2,0,700 +3,0,700 +4,0,700 +5,0,700 +6,0,700 +7,0,700 +8,0,700 +9,0,700 +0,10,700 +1,10,700 +2,10,700 +3,10,700 +4,10,700 +5,10,700 +6,10,700 +7,10,700 +8,10,700 +9,10,700 +0,20,700 +1,20,700 +2,20,700 +3,20,700 +4,20,700 +5,20,700 +6,20,700 +7,20,700 +8,20,700 +9,20,700 +0,30,700 +1,30,700 +2,30,700 +3,30,700 +4,30,700 +5,30,700 +6,30,700 +7,30,700 +8,30,700 +9,30,700 +0,40,700 +1,40,700 +2,40,700 +3,40,700 +4,40,700 +5,40,700 +6,40,700 +7,40,700 +8,40,700 +9,40,700 +0,50,700 +1,50,700 +2,50,700 +3,50,700 +4,50,700 +5,50,700 +6,50,700 +7,50,700 +8,50,700 +9,50,700 +0,60,700 +1,60,700 +2,60,700 +3,60,700 +4,60,700 +5,60,700 +6,60,700 +7,60,700 +8,60,700 +9,60,700 +0,70,700 +1,70,700 +2,70,700 +3,70,700 +4,70,700 +5,70,700 +6,70,700 +7,70,700 +8,70,700 +9,70,700 +0,80,700 +1,80,700 +2,80,700 +3,80,700 +4,80,700 +5,80,700 +6,80,700 +7,80,700 +8,80,700 +9,80,700 +0,90,700 +1,90,700 +2,90,700 +3,90,700 +4,90,700 +5,90,700 +6,90,700 +7,90,700 +8,90,700 +9,90,700 +0,0,800 +1,0,800 +2,0,800 +3,0,800 +4,0,800 +5,0,800 +6,0,800 +7,0,800 +8,0,800 +9,0,800 +0,10,800 +1,10,800 +2,10,800 +3,10,800 +4,10,800 +5,10,800 +6,10,800 +7,10,800 +8,10,800 +9,10,800 +0,20,800 +1,20,800 +2,20,800 +3,20,800 +4,20,800 +5,20,800 +6,20,800 +7,20,800 +8,20,800 +9,20,800 +0,30,800 +1,30,800 +2,30,800 +3,30,800 +4,30,800 +5,30,800 +6,30,800 +7,30,800 +8,30,800 +9,30,800 +0,40,800 +1,40,800 +2,40,800 +3,40,800 +4,40,800 +5,40,800 +6,40,800 +7,40,800 +8,40,800 +9,40,800 +0,50,800 +1,50,800 +2,50,800 +3,50,800 +4,50,800 +5,50,800 +6,50,800 +7,50,800 +8,50,800 +9,50,800 +0,60,800 +1,60,800 +2,60,800 +3,60,800 +4,60,800 +5,60,800 +6,60,800 +7,60,800 +8,60,800 +9,60,800 +0,70,800 +1,70,800 +2,70,800 +3,70,800 +4,70,800 +5,70,800 +6,70,800 +7,70,800 +8,70,800 +9,70,800 +0,80,800 +1,80,800 +2,80,800 +3,80,800 +4,80,800 +5,80,800 +6,80,800 +7,80,800 +8,80,800 +9,80,800 +0,90,800 +1,90,800 +2,90,800 +3,90,800 +4,90,800 +5,90,800 +6,90,800 +7,90,800 +8,90,800 +9,90,800 +0,0,900 +1,0,900 +2,0,900 +3,0,900 +4,0,900 +5,0,900 +6,0,900 +7,0,900 +8,0,900 +9,0,900 +0,10,900 +1,10,900 +2,10,900 +3,10,900 +4,10,900 +5,10,900 +6,10,900 +7,10,900 +8,10,900 +9,10,900 +0,20,900 +1,20,900 +2,20,900 +3,20,900 +4,20,900 +5,20,900 +6,20,900 +7,20,900 +8,20,900 +9,20,900 +0,30,900 +1,30,900 +2,30,900 +3,30,900 +4,30,900 +5,30,900 +6,30,900 +7,30,900 +8,30,900 +9,30,900 +0,40,900 +1,40,900 +2,40,900 +3,40,900 +4,40,900 +5,40,900 +6,40,900 +7,40,900 +8,40,900 +9,40,900 +0,50,900 +1,50,900 +2,50,900 +3,50,900 +4,50,900 +5,50,900 +6,50,900 +7,50,900 +8,50,900 +9,50,900 +0,60,900 +1,60,900 +2,60,900 +3,60,900 +4,60,900 +5,60,900 +6,60,900 +7,60,900 +8,60,900 +9,60,900 +0,70,900 +1,70,900 +2,70,900 +3,70,900 +4,70,900 +5,70,900 +6,70,900 +7,70,900 +8,70,900 +9,70,900 +0,80,900 +1,80,900 +2,80,900 +3,80,900 +4,80,900 +5,80,900 +6,80,900 +7,80,900 +8,80,900 +9,80,900 +0,90,900 +1,90,900 +2,90,900 +3,90,900 +4,90,900 +5,90,900 +6,90,900 +7,90,900 +8,90,900 +9,90,900 diff --git a/src/pyexample/plugin.py b/src/pyexample/plugin.py new file mode 100644 index 0000000..1cb16ba --- /dev/null +++ b/src/pyexample/plugin.py @@ -0,0 +1,106 @@ +import SALOMERuntime +import pickle +import json + +datafile="input.csv" +resultfile="output.csv" +class myalgosync(SALOMERuntime.OptimizerAlgSync): + def __init__(self): + SALOMERuntime.OptimizerAlgSync.__init__(self, None) + + def setPool(self,pool): + """Must be implemented to set the pool""" + self.pool=pool + + def getTCForIn(self): + """return typecode of type expected as Input of the internal node """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForOut(self): + """return typecode of type expected as Output of the internal node""" + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForAlgoInit(self): + """return typecode of type expected as input for initialize """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("string") + + def getTCForAlgoResult(self): + """return typecode of type expected as output of the algorithm """ + return SALOMERuntime.getSALOMERuntime().getTypeCode("int") + + def initialize(self,input): + """Optional method called on initialization. + The type of "input" is returned by "getTCForAlgoInit" + """ + with open("idefixconfig.json", "r") as f: + self.config = json.load(f) + + def start(self): + """Start to fill the pool with samples to evaluate.""" + self.id=0 + self.datafile=open(datafile, newline='') + #self.datafile=open(datafile) + import csv + self.data = csv.DictReader(self.datafile, quoting=csv.QUOTE_NONNUMERIC) + values=None + for i in range(0, self.getNbOfBranches()): + try: + values = next(self.data) + self.pool.pushInSample(self.id, + pickle.dumps(values, protocol=0).decode()) + self.id += 1 + except StopIteration: + pass + if values is not None: + with open(resultfile,"w") as f: + f.write("id,") + for v in values.keys(): + f.write(v+",") + f.write("\n") + + def takeDecision(self): + """ This method is called each time a sample has been evaluated. It can + either add new samples to evaluate in the pool, do nothing (wait for + more samples), or empty the pool to finish the evaluation. + """ + currentId=self.pool.getCurrentId() + samplebyte=self.pool.getCurrentInSample().getStringValue().encode() + sample = pickle.loads(samplebyte) + resultbyte=self.pool.getCurrentOutSample().getStringValue().encode() + error,result = pickle.loads(resultbyte) + with open(resultfile,"a") as f: + f.write(repr(currentId)+",") + for v in sample.values(): + f.write(repr(v)+",") + if error is None: + try: + for v in result: + f.write(repr(v)+",") + except TypeError: + # result is not iterable, it is just one number + f.write(repr(result)) + else: + error="Error:"+error.split('\n')[0] + f.write(error) + f.write("\n") + try: + values = next(self.data) + self.pool.pushInSample(self.id, pickle.dumps(values, protocol=0).decode()) + self.id += 1 + except StopIteration: + pass + + def finish(self): + """Optional method called when the algorithm has finished, successfully + or not, to perform any necessary clean up.""" + try: + self.datafile.close() + except: + pass + self.pool.destroyAll() + + def getAlgoResult(self): + """return the result of the algorithm. + The object returned is of type indicated by getTCForAlgoResult. + """ + return 0 diff --git a/src/pyexample/runcase.sh b/src/pyexample/runcase.sh new file mode 100755 index 0000000..fe560fe --- /dev/null +++ b/src/pyexample/runcase.sh @@ -0,0 +1,5 @@ +#!/bin/bash +commande=$* +/home/I35256/salome/base/appli_DEV/salome -t +/home/I35256/salome/base/appli_DEV/salome shell $commande +/home/I35256/salome/base/appli_DEV/salome killall diff --git a/src/pyexample/scenario.py b/src/pyexample/scenario.py new file mode 100644 index 0000000..a7b1ed4 --- /dev/null +++ b/src/pyexample/scenario.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +import pydefx +import os + +myParams = pydefx.Parameters(resource="localhost",nb_branches=2); +#wd = os.path.join(myParams.salome_parameters.work_directory, "minifixtest") +#myParams.salome_parameters.local_directory = "/toto/titi/tata" + +myScript = pydefx.PyScript() +myScript.loadFile("scenario_study.py") + +mySample = myScript.CreateEmptySample() +print(mySample.getInputNames()) +print(mySample.getOutputNames()) + +mySample.setInputValues({ "x":[0.1, 0.2, 0.3, 0.4, 0.5], + "y":[1.0, 2.0, 3.0, 4.0, 5.0], + "z":["a", "b", "c", "d", "e"]}) + +myStudy = pydefx.PyStudy(myScript.script, mySample, myParams) +myStudy.run() diff --git a/src/pyexample/scenario_study.py b/src/pyexample/scenario_study.py new file mode 100644 index 0000000..14575bb --- /dev/null +++ b/src/pyexample/scenario_study.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +def _exec(x,y,z): + s = x + y + t = "{}={}".format(z,repr(s)) + return s,t diff --git a/src/pyexample/syrthes_launch.py b/src/pyexample/syrthes_launch.py new file mode 100755 index 0000000..93904b2 --- /dev/null +++ b/src/pyexample/syrthes_launch.py @@ -0,0 +1,40 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +import pydefix +import os + +# load data +import csv + +STUDY_DIR = "/home/I35256/openturns/work/py/cassyrthes" + +mydata = {} +with open("syrthes.csv", newline='') as datafile: + csvdata = csv.DictReader(datafile, quoting=csv.QUOTE_NONNUMERIC) + for field in csvdata.fieldnames: + mydata[field] = [] + for line in csvdata: + for field in csvdata.fieldnames: + mydata[field].append(line[field]) + +myParams = pydefix.Parameters(resource="eole",nb_branches=26); +wd = os.path.join(myParams.salome_parameters.work_directory, "test_syrthes") +myParams.salome_parameters.work_directory = wd +myParams.salome_parameters.local_directory = STUDY_DIR +myParams.salome_parameters.in_files = ["brique_ech.syd", "Makefile", "Mesh", + "run.sh", "syrthes.py", "user_cond.c"] +#myParams.salome_parameters.resource_required.nb_node = 2 +myScript = pydefix.PyScript() +myScript.loadFile(os.path.join(STUDY_DIR, "etude.py")) + +mySample = myScript.CreateEmptySample() +print(mySample.getInputNames()) +print(mySample.getOutputNames()) + +mySample.setInputValues(mydata) + +myStudy = pydefix.PyStudy(myScript, mySample, myParams) +myStudy.run() + +print(myStudy.getJobState()) +print(myStudy.getResult().progressRate()) diff --git a/src/pyexample/temposcen.py b/src/pyexample/temposcen.py new file mode 100644 index 0000000..71d2f9f --- /dev/null +++ b/src/pyexample/temposcen.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +import pydefx +import os + +# load data +import csv + +mydata = {} +with open("input.csv", newline='') as datafile: + csvdata = csv.DictReader(datafile, quoting=csv.QUOTE_NONNUMERIC) + for field in csvdata.fieldnames: + mydata[field] = [] + for line in csvdata: + for field in csvdata.fieldnames: + mydata[field].append(line[field]) + +myScript = pydefx.PyScript() +myScript.loadFile("tempostudy.py") + +mySample = myScript.CreateEmptySample() +print(mySample.getInputNames()) +print(mySample.getOutputNames()) + +mySample.setInputValues(mydata) + +myStudy = pydefx.PyStudy() +myParams = myStudy.createDefaultParameters(resource="localhost"); + +wd = os.path.join(myParams.salome_parameters.work_directory, "minifixtest") +myParams.salome_parameters.work_directory = wd + +myStudy.createNewJob(myScript.script, mySample, myParams) +myStudy.launch() + +print(myStudy.getJobState()) +print(myStudy.getResult().progressRate()) diff --git a/src/pyexample/tempostudy.py b/src/pyexample/tempostudy.py new file mode 100644 index 0000000..895c6ad --- /dev/null +++ b/src/pyexample/tempostudy.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +def _exec(x,y,z): + s = (x + y + z) / z + import time + time.sleep(1) + return s diff --git a/src/pyexample/tests1.py b/src/pyexample/tests1.py new file mode 100644 index 0000000..78cb03f --- /dev/null +++ b/src/pyexample/tests1.py @@ -0,0 +1,25 @@ +import pydefx + +myParams = pydefx.Parameters() +myParams.configureResource("localhost") +myParams.createResultDirectory("/tmp") + +pyScript = """ +def _exec(a, b): + d = a / b + return d""" + +myScript = pydefx.PyScript() +myScript.loadString(pyScript) + +mySample = myScript.CreateEmptySample() +mydata = {"a":[1.1, 2.2, 3.4, 5.5], + "b":[2.2, 4.4, 6.8, 11]} +mySample.setInputValues(mydata) + +myStudy = pydefx.PyStudy() +myStudy.createNewJob(myScript.script, mySample, myParams) +print(myStudy.getProgress()) +myStudy.launch() +print(myStudy.getJobState()) +print(myStudy.getProgress()) diff --git a/src/pyexample/tests2.py b/src/pyexample/tests2.py new file mode 100644 index 0000000..829e421 --- /dev/null +++ b/src/pyexample/tests2.py @@ -0,0 +1,29 @@ +import pydefx + +myParams = pydefx.Parameters() +myParams.configureResource("localhost") +myParams.createResultDirectory("/tmp") + +pyScript = """ +def _exec(a, b): + d = a / b + return d""" + +myScript = pydefx.PyScript() +myScript.loadString(pyScript) + +mySample = myScript.CreateEmptySample() +mydata = {"a":[1.1, 2.2, 3.4, 5.5], + "b":[2.2, 4.4, 6.8, 11]} +mySample.setInputValues(mydata) + +myStudy = pydefx.PyStudy() +myStudy.createNewJob(myScript.script, mySample, myParams) +myStudy.launch() + +strdmp= myStudy.dump() +restoredJob = pydefx.PyStudy() +restoredJob.loadFromString(strdmp) + +print(myStudy.getJobState()) +print(myStudy.getProgress()) -- 2.39.2