Salome HOME
Initialization.
authorOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Tue, 5 Feb 2019 15:34:07 +0000 (16:34 +0100)
committerOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Tue, 5 Feb 2019 15:34:07 +0000 (16:34 +0100)
56 files changed:
CMakeLists.txt [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/cpp/CMakeLists.txt [new file with mode: 0644]
src/cpp/Exceptions.cxx [new file with mode: 0644]
src/cpp/Exceptions.hxx [new file with mode: 0644]
src/cpp/Job.hxx [new file with mode: 0644]
src/cpp/JobParametersProxy.cxx [new file with mode: 0644]
src/cpp/JobParametersProxy.hxx [new file with mode: 0644]
src/cpp/Launcher.hxx [new file with mode: 0644]
src/cpp/MonoPyJob.cxx [new file with mode: 0644]
src/cpp/MonoPyJob.hxx [new file with mode: 0644]
src/cpp/PyStudyFunction.cxx [new file with mode: 0644]
src/cpp/PyStudyFunction.hxx [new file with mode: 0644]
src/cpp/Sample.hxx [new file with mode: 0644]
src/cpp/SamplePyConversions.hxx [new file with mode: 0644]
src/cpp/StudyFunction.hxx [new file with mode: 0644]
src/cpp/TMonoPyJob.hxx [new file with mode: 0644]
src/cpp/Test/CMakeLists.txt [new file with mode: 0644]
src/cpp/Test/SampleTest.cxx [new file with mode: 0644]
src/cpp/Test/SampleTest.hxx [new file with mode: 0644]
src/cpp/Test/StudyGeneralTest.cxx [new file with mode: 0644]
src/cpp/Test/StudyGeneralTest.hxx [new file with mode: 0644]
src/cpp/Test/StudyRestartTest.cxx [new file with mode: 0644]
src/cpp/Test/StudyRestartTest.hxx [new file with mode: 0644]
src/cpp/Test/TestMain.cxx [new file with mode: 0644]
src/cpp/YacsStudyFunction.cxx [new file with mode: 0644]
src/cpp/YacsStudyFunction.hxx [new file with mode: 0644]
src/pydefx/CMakeLists.txt [new file with mode: 0644]
src/pydefx/__init__.py [new file with mode: 0644]
src/pydefx/configuration.py [new file with mode: 0644]
src/pydefx/parameters.py [new file with mode: 0644]
src/pydefx/pyscript.py [new file with mode: 0644]
src/pydefx/pystudy.py [new file with mode: 0644]
src/pydefx/sample.py [new file with mode: 0644]
src/pydefx/samplecsviterator.py [new file with mode: 0644]
src/pydefx/samplecsvmanager.py [new file with mode: 0644]
src/pydefx/samplemanager.py [new file with mode: 0644]
src/pydefx/schemas/CMakeLists.txt [new file with mode: 0644]
src/pydefx/schemas/idefix_pyschema.xml [new file with mode: 0644]
src/pydefx/schemas/plugin.py [new file with mode: 0644]
src/pyexample/findidefixdata.py [new file with mode: 0755]
src/pyexample/idefixconfig.json [new file with mode: 0644]
src/pyexample/idefixconfig.py [new file with mode: 0644]
src/pyexample/idefixdata.csv [new file with mode: 0644]
src/pyexample/idefixoutputnames.csv [new file with mode: 0644]
src/pyexample/idefixstudy.py [new file with mode: 0644]
src/pyexample/input.csv [new file with mode: 0644]
src/pyexample/plugin.py [new file with mode: 0644]
src/pyexample/runcase.sh [new file with mode: 0755]
src/pyexample/scenario.py [new file with mode: 0644]
src/pyexample/scenario_study.py [new file with mode: 0644]
src/pyexample/syrthes_launch.py [new file with mode: 0755]
src/pyexample/temposcen.py [new file with mode: 0644]
src/pyexample/tempostudy.py [new file with mode: 0644]
src/pyexample/tests1.py [new file with mode: 0644]
src/pyexample/tests2.py [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fc35995
--- /dev/null
@@ -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 (file)
index 0000000..39a985d
--- /dev/null
@@ -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 (file)
index 0000000..59a56df
--- /dev/null
@@ -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
+                          $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+                          $<INSTALL_INTERFACE:include>)
+
+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 (file)
index 0000000..abd8d23
--- /dev/null
@@ -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 (file)
index 0000000..6a336b4
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef YDEFX_EXCEPTIONS_HXX
+#define YDEFX_EXCEPTIONS_HXX
+#include <string>
+
+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 (file)
index 0000000..64f48a6
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef YDEFX_JOB_H
+#define YDEFX_JOB_H
+#include <string>
+
+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 (file)
index 0000000..4911043
--- /dev/null
@@ -0,0 +1,322 @@
+#include "JobParametersProxy.hxx"
+#include "Exceptions.hxx"
+#include <py2cpp/py2cpp.hxx>
+
+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<std::string> JobParametersProxy::in_files()const
+{
+  std::list<std::string> result;
+  py2cpp::pyResult(result) = _pyParameters.getAttr("salome_parameters")
+                                          .getAttr("in_files");
+  return result;
+}
+
+void JobParametersProxy::add_in_files(const std::list<std::string>& pathList)
+{
+  std::list<std::string> newList = in_files();
+  newList.insert(newList.end(), pathList.begin(), pathList.end());
+  in_files(newList);
+}
+
+void JobParametersProxy::in_files(const std::list<std::string>& 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 (file)
index 0000000..6392713
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef YDEFX_JOBPARAMETERSPROXY_H
+#define YDEFX_JOBPARAMETERSPROXY_H
+#include <py2cpp/PyPtr.hxx>
+#include <string>
+#include <list>
+#include <map>
+
+namespace ydefx
+{
+class JobParametersProxy;
+}
+namespace py2cpp
+{
+PyObject * toPy(const ydefx::JobParametersProxy& jp);
+}
+
+#include <py2cpp/py2cpp.hxx>
+
+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<std::string> in_files()const;
+  void add_in_files(const std::list<std::string>& pathList);
+  void in_files(const std::list<std::string>& 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 (file)
index 0000000..8ba62da
--- /dev/null
@@ -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 <class ...Ts>
+  Job* submitMonoPyJob(const PyStudyFunction& fnScript,
+                       Sample<Ts...>& sample,
+                       const JobParametersProxy& params);
+
+  /*!
+   * Connect to an already created job.
+   * Return nullptr in case of failure. Check the error with lastError().
+   */
+  template <class ...Ts>
+  Job* connectJob(const std::string& jobDump, Sample<Ts...>& sample);
+
+  template <class ...Ts>
+  Job* submitMultiPyJob(const PyStudyFunction& fn,
+                        Sample<Ts...>& sample,
+                        const JobParametersProxy& params);
+
+private:
+  std::string _lastError;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Template implementations
+////////////////////////////////////////////////////////////////////////////////
+
+template <class ...Ts>
+Job* Launcher::submitMonoPyJob(const PyStudyFunction& fnScript,
+                    Sample<Ts...>& sample,
+                    const JobParametersProxy& params)
+{
+  Job* result = nullptr;
+  _lastError = "";
+  try
+  {
+    result = new TMonoPyJob<Ts...>(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 <class ...Ts>
+Job* Launcher::connectJob(const std::string& jobDump,
+                      Sample<Ts...>& sample)
+{
+  Job* result = nullptr;
+  _lastError = "";
+  try
+  {
+    result = new TMonoPyJob<Ts...>(jobDump, sample);
+  }
+  catch(std::exception& e)
+  {
+    if(result != nullptr)
+      delete result;
+    result = nullptr;
+    _lastError = e.what();
+  }
+
+  return result;
+}
+
+template <class ...Ts>
+Job* Launcher::submitMultiPyJob(const PyStudyFunction& fn,
+                      Sample<Ts...>& 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 (file)
index 0000000..3ff315d
--- /dev/null
@@ -0,0 +1,118 @@
+#include "MonoPyJob.hxx"
+#include <py2cpp/py2cpp.hxx>
+
+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 (file)
index 0000000..370ff32
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef YDEFX_MONOPYJOB_HXX
+#define YDEFX_MONOPYJOB_HXX
+#include "Job.hxx"
+#include <py2cpp/PyPtr.hxx>
+
+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 (file)
index 0000000..88e6e2d
--- /dev/null
@@ -0,0 +1,79 @@
+#include "PyStudyFunction.hxx"
+#include <fstream>
+#include <sstream>
+#include <py2yacs.hxx>
+
+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<FunctionProperties>& fn_prop = p2y.getFunctionProperties();
+    std::list<FunctionProperties>::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<std::string>& PyStudyFunction::inputNames()const
+{
+  return _input;
+}
+
+const std::list<std::string>& PyStudyFunction::outputNames()const
+{
+  return _output;
+}
+
+const std::list<std::string>& PyStudyFunction::errors()const
+{
+  return _errors;
+}
+
+}
diff --git a/src/cpp/PyStudyFunction.hxx b/src/cpp/PyStudyFunction.hxx
new file mode 100644 (file)
index 0000000..6646b79
--- /dev/null
@@ -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<std::string>& inputNames()const;
+  virtual const std::list<std::string>& outputNames()const;
+  virtual const std::list<std::string>& errors()const;
+private:
+  std::string _content;
+  std::list<std::string> _input;
+  std::list<std::string> _output;
+  std::list<std::string> _errors;
+};
+}
+#endif // YDEFX_PYSTUDYFUNCTION_H
diff --git a/src/cpp/Sample.hxx b/src/cpp/Sample.hxx
new file mode 100644 (file)
index 0000000..47cafa8
--- /dev/null
@@ -0,0 +1,413 @@
+#ifndef YDEFX_SAMPLE_H\r
+#define YDEFX_SAMPLE_H\r
+\r
+#include <vector>\r
+#include <string>\r
+#include <list>\r
+#include <map>\r
+\r
+namespace ydefx\r
+{\r
+\r
+template <class T>\r
+class VariablesGroup\r
+{\r
+public:\r
+  VariablesGroup();\r
+  VariablesGroup( const T& defaultValue);\r
+  std::list<std::string> names()const;\r
+  bool isSet(const std::string& name, std::size_t index)const;\r
+  /*! Get the values for a variable name.\r
+   *  Pay attention to default values. Use isSet function in order to know\r
+   *  if the value has been set.\r
+   */\r
+  const std::vector<T>& get(const std::string& name)const;\r
+  const T& get(const std::string& name, std::size_t index)const;\r
+  void set(const std::string& name, const std::vector<T>& values);\r
+  void set(const std::string& name, std::size_t index, const T& value);\r
+  void clear(); //! names and values are cleared.\r
+  void clearValues(); //! values are cleared, but names are kept.\r
+  void resetNames(const std::list<std::string>& names);\r
+  void removeName(const std::string& name);\r
+  void addName(const std::string& name);\r
+  void unset(const std::string& name, std::size_t index);\r
+  bool isPointSet(std::size_t index)const; //! every variable is set\r
+  void setDefault(const T& defaultValue) {_defaultValue = defaultValue;}\r
+  std::size_t maxSize()const;\r
+private:\r
+  std::map<std::string, std::vector<T> > _variables;\r
+  std::map<std::string, std::vector<bool> > _status;\r
+  T _defaultValue;\r
+};\r
+\r
+enum class ExecutionState {NOTEXECUTED, DONE, ERROR};\r
+\r
+template <class T>\r
+class OneTypeSample\r
+{\r
+public:\r
+  const VariablesGroup<T>& inputs()const;\r
+  VariablesGroup<T>& inputs();\r
+  const VariablesGroup<T>& outputs()const;\r
+  VariablesGroup<T>& outputs();\r
+protected:\r
+  bool isPointSet(int index);\r
+  void unset(const std::string& name, std::size_t index);\r
+private:\r
+  VariablesGroup<T> _inputs;\r
+  VariablesGroup<T> _outputs;\r
+};\r
+\r
+template <class... Ts> class Sample;\r
+\r
+// no type sample\r
+template <>\r
+class Sample<>\r
+{\r
+public:\r
+  const std::vector<std::string>& errors()const{return _errors;}\r
+  std::vector<std::string>& errors(){return _errors;}\r
+  void clearErrors(){_errors.clear();}\r
+  void setError(std::size_t index, const std::string& message)\r
+  {\r
+    if(index >= _errors.size())\r
+      _errors.resize(index+1);\r
+    _errors[index] = message;\r
+  }\r
+  \r
+  std::string getError(std::size_t index)const\r
+  {\r
+    std::string result("");\r
+    if(index < _errors.size())\r
+      result = _errors[index];\r
+    return result;\r
+  }\r
+\r
+  virtual ExecutionState pointState(int index)\r
+  {\r
+    ExecutionState result = ExecutionState::DONE;\r
+    if(index < _errors.size())\r
+      if(!_errors[index].empty())\r
+        result = ExecutionState::ERROR;\r
+    return result;\r
+  }\r
+  virtual void clear(){_errors.clear();}\r
+  virtual void clearInputValues(){}\r
+  virtual void clearOutputValues(){_errors.clear();}\r
+\r
+  virtual std::list<std::string> allInputNames()const\r
+  {return std::list<std::string>();}\r
+\r
+  virtual std::list<std::string> allOutputNames()const\r
+  {return std::list<std::string>();}\r
+\r
+  virtual void unset(const std::string& name, std::size_t index){}\r
+  virtual std::size_t maxSize()const {return 0;}\r
+  \r
+private:\r
+  std::vector<std::string> _errors;\r
+};\r
+\r
+// multi type sample\r
+template <class T, class... Ts>\r
+class Sample<T, Ts...> : public OneTypeSample<T>, public Sample<Ts...>\r
+{\r
+public:\r
+  virtual ExecutionState pointState(int index);\r
+  virtual void clear(); //! names and values are cleared.\r
+  virtual void clearInputValues(); //! values are cleared, but names are kept.\r
+  virtual void clearOutputValues(); //! values are cleared, but names are kept.\r
+  virtual std::list<std::string> allInputNames()const;\r
+  virtual std::list<std::string> allOutputNames()const;\r
+  virtual void unset(const std::string& name, std::size_t index);\r
+  virtual std::size_t maxSize()const; //! outputs ignored!\r
+\r
+  template <class NT>\r
+  const VariablesGroup<NT>& inputs()const\r
+  {return OneTypeSample<NT>::inputs();}\r
+\r
+  template <class NT>\r
+  VariablesGroup<NT>& inputs()\r
+  {return OneTypeSample<NT>::inputs();}\r
+\r
+  template <class NT>\r
+  const VariablesGroup<NT>& outputs()const\r
+  {return OneTypeSample<NT>::outputs();}\r
+\r
+  template <class NT>\r
+  VariablesGroup<NT>& outputs()\r
+  {return OneTypeSample<NT>::outputs();}\r
+\r
+};\r
+\r
+// Sample<T, Ts...>\r
+template <class T, class... Ts>\r
+ExecutionState Sample<T, Ts...>::pointState(int index)\r
+{\r
+  ExecutionState result = Sample<Ts...>::pointState(index);\r
+  if(result == ExecutionState::DONE)\r
+    if(!OneTypeSample<T>::isPointSet(index))\r
+      result = ExecutionState::NOTEXECUTED;\r
+  return result;\r
+}\r
+\r
+template <class T, class... Ts>\r
+void Sample<T, Ts...>::clear()\r
+{\r
+  OneTypeSample<T>::inputs().clear();\r
+  OneTypeSample<T>::outputs().clear();\r
+  Sample<Ts...>::clear();\r
+}\r
+\r
+template <class T, class... Ts>\r
+void Sample<T, Ts...>::clearInputValues()\r
+{\r
+  OneTypeSample<T>::inputs().clearValues();\r
+  Sample<Ts...>::clearInputValues();\r
+}\r
+\r
+template <class T, class... Ts>\r
+void Sample<T, Ts...>::clearOutputValues()\r
+{\r
+  OneTypeSample<T>::outputs().clearValues();\r
+  Sample<Ts...>::clearOutputValues();\r
+}\r
+\r
+template <class T, class... Ts>\r
+std::list<std::string> Sample<T, Ts...>::allInputNames()const\r
+{\r
+  std::list<std::string> result = OneTypeSample<T>::inputs().names();\r
+  result.splice(result.end(), Sample<Ts...>::allInputNames());\r
+  return result;\r
+}\r
+\r
+template <class T, class... Ts>\r
+std::list<std::string> Sample<T, Ts...>::allOutputNames()const\r
+{\r
+  std::list<std::string> result = OneTypeSample<T>::outputs().names();\r
+  result.splice(result.end(),Sample<Ts...>::allOutputNames());\r
+  return result;\r
+}\r
+\r
+template <class T, class... Ts>\r
+void Sample<T, Ts...>::unset(const std::string& name, std::size_t index)\r
+{\r
+  OneTypeSample<T>::unset(name, index);\r
+  Sample<Ts...>::unset(name, index);\r
+}\r
+\r
+template <class T, class... Ts>\r
+std::size_t Sample<T, Ts...>::maxSize()const\r
+{\r
+  std::size_t result=OneTypeSample<T>::inputs().maxSize();\r
+  std::size_t superR=Sample<Ts...>::maxSize();\r
+  if(superR > result)\r
+    result = superR;\r
+  return result;\r
+}\r
+\r
+// VariablesGroup\r
+\r
+template <class T>\r
+VariablesGroup<T>::VariablesGroup( )\r
+: _variables()\r
+, _status()\r
+, _defaultValue(T())\r
+{\r
+}\r
+\r
+template <class T>\r
+VariablesGroup<T>::VariablesGroup( const T& defaultValue)\r
+: _variables()\r
+, _status()\r
+, _defaultValue(defaultValue)\r
+{\r
+}\r
+\r
+template <class T>\r
+std::list<std::string> VariablesGroup<T>::names()const\r
+{\r
+  std::list<std::string> result;\r
+  for(const auto& it : _variables)\r
+    result.push_back(it.first);\r
+  return result;\r
+}\r
+\r
+template <class T>\r
+bool VariablesGroup<T>::isSet(const std::string& name, std::size_t index)const\r
+{\r
+  bool result = false;\r
+  auto it = _status.find(name);\r
+  if(it != _status.end())\r
+    if(index < it->second.size())\r
+      result = it->second[index];\r
+  return result;\r
+}\r
+\r
+template <class T>\r
+bool VariablesGroup<T>::isPointSet(std::size_t index)const\r
+{\r
+  bool allSet = true;\r
+  std::map<std::string, std::vector<bool> >::const_iterator it;\r
+  for( it = _status.begin(); it != _status.end() && allSet; it++)\r
+    allSet = index < it->second.size() && it->second[index];\r
+  return allSet;\r
+}\r
+\r
+template <class T>\r
+std::size_t VariablesGroup<T>::maxSize()const\r
+{\r
+  std::size_t result = 0;\r
+  for(const auto& it : _variables)\r
+    if(result < it.second.size())\r
+      result = it.second.size();\r
+  return result;\r
+}\r
+\r
+template <class T>\r
+const std::vector<T>& VariablesGroup<T>::get(const std::string& name)const\r
+{\r
+  static const std::vector<T> emptyVector;\r
+  auto it = _variables.find(name);\r
+  if(it != _variables.end())\r
+    return it->second;\r
+  else\r
+    return emptyVector;\r
+}\r
+\r
+template <class T>\r
+const T& VariablesGroup<T>::get(const std::string& name, std::size_t index)const\r
+{\r
+  auto it = _variables.find(name);\r
+  if(it != _variables.end())\r
+    if(index < it->second.size())\r
+      return it->second[index];\r
+  return _defaultValue;\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::set(const std::string& name, const std::vector<T>& values)\r
+{\r
+  _variables[name] = values;\r
+  std::vector<bool>& status = _status[name];\r
+  status.clear();\r
+  status.insert(status.begin(), values.size(), true);\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::set(const std::string& name, std::size_t index, const T& value)\r
+{\r
+  std::vector<T>& values = _variables[name];\r
+  std::vector<bool>& status = _status[name];\r
+  if(index >= values.size())\r
+  {\r
+    values.resize(index+1, _defaultValue);\r
+    status.resize(index+1, false);\r
+  }\r
+  values[index] = value;\r
+  status[index] = true;\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::clear()\r
+{\r
+  _variables.clear();\r
+  _status.clear();\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::clearValues()\r
+{\r
+  for(auto& it : _variables)\r
+  {\r
+    it.second.clear();\r
+  }\r
+  for(auto& it : _status)\r
+  {\r
+    it.second.clear();\r
+  }\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::resetNames(const std::list<std::string>& names)\r
+{\r
+  clear();\r
+  for(const auto& it : names)\r
+  {\r
+    _variables[it].clear();\r
+    _status[it].clear();\r
+  }\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::removeName(const std::string& name)\r
+{\r
+  _variables.erase(name);\r
+  _status.erase(name);\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::addName(const std::string& name)\r
+{\r
+  _variables[name];\r
+  _status[name];\r
+}\r
+\r
+template <class T>\r
+void VariablesGroup<T>::unset(const std::string& name, std::size_t index)\r
+{\r
+  auto it = _status.find(name);\r
+  if(it != _status.end())\r
+    if(index < it->second.size())\r
+    {\r
+      it->second[index] = false;\r
+      if(index+1 == it->second.size())\r
+      {\r
+        // remove all the unset values at the end of the list\r
+        while(index>0 && !it->second[index])\r
+          --index;\r
+        it->second.resize(index);\r
+        _variables[name].resize(index);\r
+      }\r
+    }\r
+}\r
+  \r
+// OneTypeSample\r
+template <class T>\r
+const VariablesGroup<T>& OneTypeSample<T>::inputs()const\r
+{\r
+  return _inputs;\r
+}\r
+\r
+template <class T>\r
+VariablesGroup<T>& OneTypeSample<T>::inputs()\r
+{\r
+  return _inputs;\r
+}\r
+\r
+template <class T>\r
+const VariablesGroup<T>& OneTypeSample<T>::outputs()const\r
+{\r
+  return _outputs;\r
+}\r
+\r
+template <class T>\r
+VariablesGroup<T>& OneTypeSample<T>::outputs()\r
+{\r
+  return _outputs;\r
+}\r
+\r
+template <class T>\r
+bool OneTypeSample<T>::isPointSet(int index)\r
+{\r
+  return _inputs.isPointSet(index) && _outputs.isPointSet(index);\r
+}\r
+\r
+template <class T>\r
+void OneTypeSample<T>::unset(const std::string& name, std::size_t index)\r
+{\r
+  _outputs.unset(name, index);\r
+}\r
+\r
+}//namespace ydefx\r
+#endif // YDEFX_SAMPLE_H\r
diff --git a/src/cpp/SamplePyConversions.hxx b/src/cpp/SamplePyConversions.hxx
new file mode 100644 (file)
index 0000000..5d39d36
--- /dev/null
@@ -0,0 +1,364 @@
+#ifndef YDEFX_SAMPLEPYCONVERSIONS_HXX
+#define YDEFX_SAMPLEPYCONVERSIONS_HXX
+
+#include <py2cpp/py2cpp.hxx>
+#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...>.
+ * Ts : list of types managed by this converter. This list is included in the
+ *      list of sample types.
+ */
+template <class S, class... Ts>
+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 <class ...Ts>
+PyObject* inputToPy(const Sample<Ts...>& 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 <class ...Ts>
+py2cpp::ConversionCheck inputFromPy(PyObject* obj, Sample<Ts...>& 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 <class ...Ts>
+PyObject* outputToPy(const Sample<Ts...>& 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 <class ...Ts>
+py2cpp::ConversionCheck outputFromPy(PyObject* obj, Sample<Ts...>& 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 <class ...Ts>
+py2cpp::ConversionCheck errorsFromPy(PyObject* obj, Sample<Ts...>& 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 <class ...Ts>
+py2cpp::PyPtr createPySample(const Sample<Ts...>& 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 <class ...Ts>
+py2cpp::ConversionCheck fetchResults(PyObject* obj, Sample<Ts...>& sample);
+
+////////////////////////////////////////////////////////////////////////////////
+// Template implementations
+////////////////////////////////////////////////////////////////////////////////
+
+template <class S>
+class SamplePyConverter<S>
+{
+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 S, class T, class... Ts>
+class SamplePyConverter<S,T, Ts...> : public SamplePyConverter<S, Ts...>
+{
+public:
+  /*! Add sample.inputs<T> to result.
+   *  result should be a python dictionary.
+   */
+  bool inputToPy(const S& sample, PyObject* result)
+  {
+    bool ok = true;
+    std::list<std::string> names = sample.OneTypeSample<T>::inputs().names();
+    for(const std::string& name : names)
+    {
+      const std::vector<T>& values = sample.OneTypeSample<T>::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<S, Ts...>::inputToPy(sample, result);
+    return ok;
+  }
+
+  py2cpp::ConversionCheck inputFromPy(PyObject* obj, S& sample)
+  {
+    py2cpp::ConversionCheck check;
+    std::list<std::string> names = sample.OneTypeSample<T>::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<T>::inputs().set(name, i, value);
+          stop = (i == 0) || !check;
+        }
+      }
+      else
+        check.addError("sample input list", obj);
+    }
+    if(check)
+      check = SamplePyConverter<S, Ts...>::inputFromPy(obj, sample);
+    return check;
+  }
+
+  bool outputToPy(const S& sample, PyObject* result)
+  {
+    bool ok = true;
+    std::size_t maxsize = sample.maxSize();
+    std::list<std::string> names = sample.OneTypeSample<T>::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<maxsize; i++)
+      {
+        if(sample.OneTypeSample<T>::outputs().isSet(name, i))
+        {
+          const T& cppValue = sample.OneTypeSample<T>::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<S, Ts...>::outputToPy(sample, result);
+    return ok;
+  }
+
+  py2cpp::ConversionCheck outputFromPy(PyObject* obj, S& sample)
+  {
+    py2cpp::ConversionCheck check;
+    std::list<std::string> names = sample.OneTypeSample<T>::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<T>::outputs().unset(name, i);
+          }
+          else
+          {
+            T value;
+            check.addError(py2cpp::fromPy(pyVal, value));
+            if(check)
+              sample.OneTypeSample<T>::outputs().set(name, i, value);
+          }
+          stop = (i == 0) || !check;
+        }
+      }
+      else
+        check.addError("sample input list", obj);
+    }
+    if(check)
+      check = SamplePyConverter<S, Ts...>::outputFromPy(obj, sample);
+    return check;
+  }
+};
+
+template <class ...Ts>
+PyObject* inputToPy(const Sample<Ts...>& sample)
+{
+  PyObject * result = PyDict_New();
+  if(result)
+  {
+    SamplePyConverter<Sample<Ts...>, Ts...> converter;
+    if(! converter.inputToPy(sample, result))
+    {
+      Py_XDECREF(result);
+      result = nullptr;
+    }
+  }
+  // TODO: errors
+  return result;
+}
+
+template <class ...Ts>
+py2cpp::ConversionCheck inputFromPy(PyObject* obj, Sample<Ts...>& sample)
+{
+  py2cpp::ConversionCheck check;
+  if(PyDict_Check(obj))
+  {
+    sample.clearInputValues();
+    SamplePyConverter<Sample<Ts...>, Ts...> converter;
+    check.addError(converter.inputFromPy(obj,sample));
+  }
+  else
+    check.addError("Sample input", obj);
+
+  return check;
+}
+
+template <class ...Ts>
+PyObject* outputToPy(const Sample<Ts...>& sample)
+{
+  PyObject * result = PyDict_New();
+  if(result)
+  {
+    SamplePyConverter<Sample<Ts...>, Ts...> converter;
+    if(! converter.outputToPy(sample, result))
+    {
+      Py_XDECREF(result);
+      result = nullptr;
+    }
+  }
+  return result;
+}
+
+template <class ...Ts>
+py2cpp::ConversionCheck outputFromPy(PyObject* obj, Sample<Ts...>& sample)
+{
+  py2cpp::ConversionCheck check;
+  if(PyDict_Check(obj))
+  {
+    sample.clearOutputValues();
+    SamplePyConverter<Sample<Ts...>, Ts...> converter;
+    check.addError(converter.outputFromPy(obj,sample));
+  }
+  else
+    check.addError("Sample output", obj);
+  return check;
+}
+
+template <class ...Ts>
+py2cpp::ConversionCheck errorsFromPy(PyObject* obj, Sample<Ts...>& 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 <class ...Ts>
+py2cpp::PyPtr createPySample(const Sample<Ts...>& 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 <class ...Ts>
+py2cpp::ConversionCheck fetchResults(const py2cpp::PyPtr& obj, Sample<Ts...>& 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 (file)
index 0000000..c58f9ef
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef YDEFX_STUDYFUNCTION_H
+#define YDEFX_STUDYFUNCTION_H
+#include <string>
+#include <list>
+
+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<std::string>& inputNames()const=0;
+  virtual const std::list<std::string>& outputNames()const=0;
+  virtual const std::list<std::string>& errors()const=0;
+  std::list<std::string> datafiles;
+};
+}
+#endif // YDEFX_STUDYFUNCTION_H
diff --git a/src/cpp/TMonoPyJob.hxx b/src/cpp/TMonoPyJob.hxx
new file mode 100644 (file)
index 0000000..4351e5e
--- /dev/null
@@ -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 <py2cpp/py2cpp.hxx>
+
+namespace ydefx
+{
+template <class ...Ts>
+class TMonoPyJob : public MonoPyJob
+{
+public:
+  //! Create a new job.
+  TMonoPyJob(const PyStudyFunction& fnScript,
+             Sample<Ts...>& 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<Ts...>& 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<Ts...>& getSample()const{return _sample;}
+
+private:
+  Sample<Ts...>& _sample;
+};
+
+}
+
+#endif //YDEFX_TMONOPYJOB_HXX
diff --git a/src/cpp/Test/CMakeLists.txt b/src/cpp/Test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..12b1d1f
--- /dev/null
@@ -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 (file)
index 0000000..e2e3c78
--- /dev/null
@@ -0,0 +1,258 @@
+#include "SampleTest.hxx"
+#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx
+#include <algorithm>
+
+void SampleTest::setUp()
+{
+}
+
+void SampleTest::tearDown()
+{
+}
+
+void SampleTest::cleanUp()
+{
+}
+
+void SampleTest::pointState()
+{
+  ydefx::Sample<double, int> mySample;
+  CPPUNIT_ASSERT(mySample.maxSize() == 0);
+  std::vector<int> int_vals = {1, 7, 42};
+  mySample.inputs<int>().set("toto", int_vals);
+  mySample.outputs<double>().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<double>().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<double>().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<int>().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<double>().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<double>().maxSize() == 4);
+}
+
+void SampleTest::pyConversions()
+{
+  ydefx::Sample<double, int, std::string> mySample;
+  std::vector<int> int_vals = {1, 7, 42};
+  Py_Initialize();
+  {
+    // Test inputs
+    py2cpp::PyPtr pyObj(inputToPy(mySample));
+    CPPUNIT_ASSERT(pyObj.repr() == "{}");
+    mySample.inputs<int>().set("toto", int_vals);
+    pyObj.reset(inputToPy(mySample));
+    CPPUNIT_ASSERT(pyObj.repr() == "{'toto': [1, 7, 42]}");
+    std::vector<double> double_vals = {1.5, 3.2, 7.5};
+    mySample.inputs<double>().set("dval", double_vals);
+    pyObj.reset(inputToPy(mySample));
+    ydefx::Sample<double, int, std::string> frompySample;
+    frompySample.inputs<int>().addName("toto");
+    frompySample.inputs<double>().addName("dval");
+    py2cpp::ConversionCheck check(inputFromPy(pyObj.get(), frompySample));
+    CPPUNIT_ASSERT(check);
+    CPPUNIT_ASSERT(double_vals == frompySample.inputs<double>().get("dval"));
+    CPPUNIT_ASSERT(int_vals == frompySample.inputs<int>().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<double>().get("dval"));
+    CPPUNIT_ASSERT(int_vals == frompySample.inputs<int>().get("toto"));
+    mySample.outputs<double>().addName("dout");
+    mySample.outputs<double>().set("dout", 1, 1.5);
+    pyObj.reset(outputToPy(mySample));
+    frompySample.outputs<double>().addName("dout");
+    check.addError(outputFromPy(pyObj.get(), frompySample));
+    CPPUNIT_ASSERT(check);
+    CPPUNIT_ASSERT(1.5 == frompySample.outputs<double>().get("dout", 1));
+    CPPUNIT_ASSERT(frompySample.outputs<double>().isSet("dout",1));
+    CPPUNIT_ASSERT(!frompySample.outputs<double>().isSet("dout",0));
+    CPPUNIT_ASSERT(!frompySample.outputs<double>().isSet("dout",2));
+
+    mySample.outputs<std::string>().addName("strout");
+    mySample.outputs<std::string>().set("strout", 1, "myvalue");
+    pyObj.reset(outputToPy(mySample));
+    frompySample.outputs<std::string>().addName("strout");
+    check.addError(outputFromPy(pyObj.get(), frompySample));
+    CPPUNIT_ASSERT(check);
+    CPPUNIT_ASSERT(1.5 == frompySample.outputs<double>().get("dout", 1));
+    CPPUNIT_ASSERT(frompySample.outputs<double>().isSet("dout",1));
+    CPPUNIT_ASSERT(!frompySample.outputs<double>().isSet("dout",0));
+    CPPUNIT_ASSERT(!frompySample.outputs<double>().isSet("dout",2));
+    CPPUNIT_ASSERT(frompySample.outputs<std::string>().get("strout", 1)
+                   == "myvalue");
+    CPPUNIT_ASSERT(frompySample.outputs<std::string>().isSet("strout",1));
+    CPPUNIT_ASSERT(!frompySample.outputs<std::string>().isSet("strout",0));
+    CPPUNIT_ASSERT(!frompySample.outputs<std::string>().isSet("strout",2));
+  }
+  Py_Finalize();
+}
+
+void SampleTest::pyConversionsErrors()
+{
+  Py_Initialize();
+  {
+    ydefx::Sample<double> resultSample;
+    std::vector<double> double_vals = {1.5, 3.2, 7.5};
+    ydefx::Sample<std::vector<double> > vectSample;
+    // errors on input variables
+    vectSample.inputs<std::vector<double> >().addName("p1");
+    vectSample.inputs<std::vector<double> >().set("p1", 0, double_vals);
+    py2cpp::PyPtr pyObj(inputToPy(vectSample));
+    resultSample.inputs<double>().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<double>().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<std::vector<int> > vectIntSample;
+    vectIntSample.inputs<std::vector<int> >().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<double>().addName("o1");
+    check.reset();
+    check.addError(outputFromPy(pyObj.get(), resultSample));
+    CPPUNIT_ASSERT(check.getMessage().find("o1")!= std::string::npos);
+
+    vectSample.outputs<std::vector<double> >().addName("o1");
+    vectSample.outputs<std::vector<double> >().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<std::string> testList = {"toto", "titi","tata"};
+    jpp.in_files(testList);
+    CPPUNIT_ASSERT(jpp.in_files() == testList);
+    std::list<std::string> addList = {"zozo", "zizi"};
+    jpp.add_in_files(addList);
+    CPPUNIT_ASSERT(jpp.in_files() == std::list<std::string>({"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 (file)
index 0000000..cd1b7e8
--- /dev/null
@@ -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 <cppunit/extensions/HelperMacros.h>
+
+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 (file)
index 0000000..b804c1b
--- /dev/null
@@ -0,0 +1,78 @@
+#include "StudyGeneralTest.hxx"
+#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx
+#include <algorithm>
+
+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<std::string>& 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<std::string>& outputs = studyFunction.outputNames();
+    CPPUNIT_ASSERT(std::find(outputs.begin(), outputs.end(), "d")
+                                                              != outputs.end());
+    
+    ydefx::Sample<double> sample;
+    std::vector<double> a_vals = {1.1, 2.2, 3.4, 5.5};
+    std::vector<double> b_vals = {2.2, 4.4, 6.8, 11};
+    sample.inputs<double>().set("a", a_vals);
+    sample.inputs<double>().set("b", b_vals);
+    sample.outputs<double>().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<double> expectedResult = {0.5, 0.5, 0.5, 0.5};
+    const std::vector<double>& result = sample.outputs<double>().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 (file)
index 0000000..ab362fa
--- /dev/null
@@ -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 <cppunit/extensions/HelperMacros.h>
+
+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 (file)
index 0000000..afe04ac
--- /dev/null
@@ -0,0 +1,63 @@
+#include "StudyRestartTest.hxx"
+#include "../Launcher.hxx" // possible conflict with KERNEL/Launcher/Launcher.hxx
+#include <algorithm>
+
+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<double> sample;
+    std::vector<double> a_vals = {1.1, 2.2, 3.4, 5.5};
+    std::vector<double> b_vals = {2.2, 4.4, 6.8, 11};
+    sample.inputs<double>().set("a", a_vals);
+    sample.inputs<double>().set("b", b_vals);
+    sample.outputs<double>().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<double> expectedResult = {0.5, 0.5, 0.5, 0.5};
+    const std::vector<double>& result = sample.outputs<double>().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 (file)
index 0000000..115f1ab
--- /dev/null
@@ -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 <cppunit/extensions/HelperMacros.h>
+
+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 (file)
index 0000000..5344725
--- /dev/null
@@ -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 <cppunit/CompilerOutputter.h>
+#include <cppunit/TestResult.h>
+#include <cppunit/TestResultCollector.h>
+#include <cppunit/TextTestProgressListener.h>
+#include <cppunit/BriefTestProgressListener.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/TestRunner.h>
+#include <cppunit/TextTestRunner.h>
+#include <stdexcept>
+
+#include <iostream>
+#include <fstream>
+#include <stdlib.h>
+
+// ============================================================================
+/*!
+ *  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 (file)
index 0000000..d15339b
--- /dev/null
@@ -0,0 +1,60 @@
+#include "YacsStudyFunction.hxx"
+#include <fstream>
+#include <sstream>
+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<std::string>& YacsStudyFunction::inputNames()const
+{
+  return _input;
+}
+
+const std::list<std::string>& YacsStudyFunction::outputNames()const
+{
+  return _output;
+}
+
+const std::list<std::string>& YacsStudyFunction::errors()const
+{
+  return _errors;
+}
+
+}
diff --git a/src/cpp/YacsStudyFunction.hxx b/src/cpp/YacsStudyFunction.hxx
new file mode 100644 (file)
index 0000000..b937b5d
--- /dev/null
@@ -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<std::string>& inputNames()const;
+  virtual const std::list<std::string>& outputNames()const;
+  virtual const std::list<std::string>& errors()const;
+private:
+  std::string _content;
+  std::list<std::string> _input;
+  std::list<std::string> _output;
+  std::list<std::string> _errors;
+};
+}
+#endif // YDEFX_YACSSTUDYFUNCTION_H
diff --git a/src/pydefx/CMakeLists.txt b/src/pydefx/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1fcda2f
--- /dev/null
@@ -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 (file)
index 0000000..4fd26f9
--- /dev/null
@@ -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 (file)
index 0000000..a1496aa
--- /dev/null
@@ -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 (file)
index 0000000..da5d2f2
--- /dev/null
@@ -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 (file)
index 0000000..8895d17
--- /dev/null
@@ -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 (file)
index 0000000..aeea713
--- /dev/null
@@ -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 (file)
index 0000000..5f0e54e
--- /dev/null
@@ -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 (file)
index 0000000..7b68e10
--- /dev/null
@@ -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 (file)
index 0000000..2c1e696
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/src/pydefx/schemas/CMakeLists.txt b/src/pydefx/schemas/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fbd799b
--- /dev/null
@@ -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 (file)
index 0000000..40b028c
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version='1.0' encoding='iso-8859-1' ?>
+<proc name="newSchema_1">
+   <type name="string" kind="string"/>
+   <struct name="Engines/dataref">
+      <member name="ref" type="string"/>
+   </struct>
+   <type name="bool" kind="bool"/>
+   <sequence name="boolvec" content="bool"/>
+   <type name="double" kind="double"/>
+   <sequence name="dblevec" content="double"/>
+   <objref name="file" id="file"/>
+   <type name="int" kind="int"/>
+   <sequence name="intvec" content="int"/>
+   <struct name="stringpair">
+      <member name="name" type="string"/>
+      <member name="value" type="string"/>
+   </struct>
+   <sequence name="propvec" content="stringpair"/>
+   <objref name="pyobj" id="python:obj:1.0"/>
+   <sequence name="seqboolvec" content="boolvec"/>
+   <sequence name="seqdblevec" content="dblevec"/>
+   <sequence name="seqintvec" content="intvec"/>
+   <sequence name="seqpyobj" content="pyobj"/>
+   <sequence name="stringvec" content="string"/>
+   <sequence name="seqstringvec" content="stringvec"/>
+   <container name="DefaultContainer">
+      <property name="container_kind" value="Salome"/>
+      <property name="attached_on_cloning" value="0"/>
+      <property name="container_name" value="FactoryServer"/>
+      <property name="name" value="localhost"/>
+   </container>
+   <container name="container0">
+      <property name="container_kind" value="Salome"/>
+      <property name="attached_on_cloning" value="0"/>
+   </container>
+   <optimizer name="OptimizerLoop0" nbranch="4" loopWeight="-1" lib="plugin.py" entry="myalgosync">
+      <remote name="Solver" elementaryWeight="-1">
+         <script><code><![CDATA[import pickle
+import traceback
+import importlib
+inputvals=pickle.loads(i0.encode())
+idefixstudy=importlib.import_module(studymodule)
+error=None
+result=None
+try:
+  result=idefixstudy._exec(**inputvals)
+except Exception as e:
+  error=str(e) 
+  traceback.print_exc()
+o0=pickle.dumps((error, result), protocol=0).decode()
+]]></code></script>
+         <load container="container0"/>
+         <inport name="i0" type="string"/>
+         <inport name="studymodule" type="string"/>
+         <outport name="o0" type="string"/>
+      </remote>
+   </optimizer>
+   <inline name="Initialisation">
+      <script><code><![CDATA[import json
+
+with open("idefixconfig.json", "r") as f:
+  config = json.load(f)
+nbbranches=config["nbbranches"]
+studymodule=config["studymodule"]
+]]></code></script>
+      <load container="DefaultContainer"/>
+      <outport name="nbbranches" type="int"/>
+      <outport name="studymodule" type="string"/>
+   </inline>
+   <control> <fromnode>Initialisation</fromnode> <tonode>OptimizerLoop0</tonode> </control>
+   <datalink control="false">
+      <fromnode>OptimizerLoop0</fromnode> <fromport>evalSamples</fromport>
+      <tonode>OptimizerLoop0.Solver</tonode> <toport>i0</toport>
+   </datalink>
+   <datalink control="false">
+      <fromnode>Initialisation</fromnode> <fromport>nbbranches</fromport>
+      <tonode>OptimizerLoop0</tonode> <toport>nbBranches</toport>
+   </datalink>
+   <datalink control="false">
+      <fromnode>Initialisation</fromnode> <fromport>studymodule</fromport>
+      <tonode>OptimizerLoop0.Solver</tonode> <toport>studymodule</toport>
+   </datalink>
+   <datalink control="false">
+      <fromnode>OptimizerLoop0.Solver</fromnode> <fromport>o0</fromport>
+      <tonode>OptimizerLoop0</tonode> <toport>evalResults</toport>
+   </datalink>
+   <parameter>
+      <tonode>OptimizerLoop0</tonode><toport>nbBranches</toport>
+      <value><int>4</int></value>
+   </parameter>
+   <presentation name="OptimizerLoop0" x="180.5" y="34.5" width="170" height="214" expanded="1" expx="180.5" expy="34.5" expWidth="170" expHeight="214" shownState="0"/>
+   <presentation name="OptimizerLoop0.Solver" x="8" y="120" width="158" height="90" expanded="1" expx="8" expy="120" expWidth="158" expHeight="90" shownState="0"/>
+   <presentation name="Initialisation" x="14" y="34" width="158" height="90" expanded="1" expx="14" expy="34" expWidth="158" expHeight="90" shownState="0"/>
+   <presentation name="__ROOT__" x="0" y="0" width="354.5" height="252.5" expanded="1" expx="0" expy="0" expWidth="354.5" expHeight="252.5" shownState="0"/>
+</proc>
diff --git a/src/pydefx/schemas/plugin.py b/src/pydefx/schemas/plugin.py
new file mode 100644 (file)
index 0000000..3abc68f
--- /dev/null
@@ -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 (executable)
index 0000000..bf34e9c
--- /dev/null
@@ -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 (file)
index 0000000..b08cc10
--- /dev/null
@@ -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 (file)
index 0000000..e9e0cf9
--- /dev/null
@@ -0,0 +1,2 @@
+nbbranches=8
+studymodule="idefixstudy"
diff --git a/src/pyexample/idefixdata.csv b/src/pyexample/idefixdata.csv
new file mode 100644 (file)
index 0000000..22caa39
--- /dev/null
@@ -0,0 +1,1001 @@
+"x","y","z"\r
+0.0,0.0,0.0\r
+1.0,0.0,0.0\r
+2.0,0.0,0.0\r
+3.0,0.0,0.0\r
+4.0,0.0,0.0\r
+5.0,0.0,0.0\r
+6.0,0.0,0.0\r
+7.0,0.0,0.0\r
+8.0,0.0,0.0\r
+9.0,0.0,0.0\r
+0.0,10.0,0.0\r
+1.0,10.0,0.0\r
+2.0,10.0,0.0\r
+3.0,10.0,0.0\r
+4.0,10.0,0.0\r
+5.0,10.0,0.0\r
+6.0,10.0,0.0\r
+7.0,10.0,0.0\r
+8.0,10.0,0.0\r
+9.0,10.0,0.0\r
+0.0,20.0,0.0\r
+1.0,20.0,0.0\r
+2.0,20.0,0.0\r
+3.0,20.0,0.0\r
+4.0,20.0,0.0\r
+5.0,20.0,0.0\r
+6.0,20.0,0.0\r
+7.0,20.0,0.0\r
+8.0,20.0,0.0\r
+9.0,20.0,0.0\r
+0.0,30.0,0.0\r
+1.0,30.0,0.0\r
+2.0,30.0,0.0\r
+3.0,30.0,0.0\r
+4.0,30.0,0.0\r
+5.0,30.0,0.0\r
+6.0,30.0,0.0\r
+7.0,30.0,0.0\r
+8.0,30.0,0.0\r
+9.0,30.0,0.0\r
+0.0,40.0,0.0\r
+1.0,40.0,0.0\r
+2.0,40.0,0.0\r
+3.0,40.0,0.0\r
+4.0,40.0,0.0\r
+5.0,40.0,0.0\r
+6.0,40.0,0.0\r
+7.0,40.0,0.0\r
+8.0,40.0,0.0\r
+9.0,40.0,0.0\r
+0.0,50.0,0.0\r
+1.0,50.0,0.0\r
+2.0,50.0,0.0\r
+3.0,50.0,0.0\r
+4.0,50.0,0.0\r
+5.0,50.0,0.0\r
+6.0,50.0,0.0\r
+7.0,50.0,0.0\r
+8.0,50.0,0.0\r
+9.0,50.0,0.0\r
+0.0,60.0,0.0\r
+1.0,60.0,0.0\r
+2.0,60.0,0.0\r
+3.0,60.0,0.0\r
+4.0,60.0,0.0\r
+5.0,60.0,0.0\r
+6.0,60.0,0.0\r
+7.0,60.0,0.0\r
+8.0,60.0,0.0\r
+9.0,60.0,0.0\r
+0.0,70.0,0.0\r
+1.0,70.0,0.0\r
+2.0,70.0,0.0\r
+3.0,70.0,0.0\r
+4.0,70.0,0.0\r
+5.0,70.0,0.0\r
+6.0,70.0,0.0\r
+7.0,70.0,0.0\r
+8.0,70.0,0.0\r
+9.0,70.0,0.0\r
+0.0,80.0,0.0\r
+1.0,80.0,0.0\r
+2.0,80.0,0.0\r
+3.0,80.0,0.0\r
+4.0,80.0,0.0\r
+5.0,80.0,0.0\r
+6.0,80.0,0.0\r
+7.0,80.0,0.0\r
+8.0,80.0,0.0\r
+9.0,80.0,0.0\r
+0.0,90.0,0.0\r
+1.0,90.0,0.0\r
+2.0,90.0,0.0\r
+3.0,90.0,0.0\r
+4.0,90.0,0.0\r
+5.0,90.0,0.0\r
+6.0,90.0,0.0\r
+7.0,90.0,0.0\r
+8.0,90.0,0.0\r
+9.0,90.0,0.0\r
+0.0,0.0,100.0\r
+1.0,0.0,100.0\r
+2.0,0.0,100.0\r
+3.0,0.0,100.0\r
+4.0,0.0,100.0\r
+5.0,0.0,100.0\r
+6.0,0.0,100.0\r
+7.0,0.0,100.0\r
+8.0,0.0,100.0\r
+9.0,0.0,100.0\r
+0.0,10.0,100.0\r
+1.0,10.0,100.0\r
+2.0,10.0,100.0\r
+3.0,10.0,100.0\r
+4.0,10.0,100.0\r
+5.0,10.0,100.0\r
+6.0,10.0,100.0\r
+7.0,10.0,100.0\r
+8.0,10.0,100.0\r
+9.0,10.0,100.0\r
+0.0,20.0,100.0\r
+1.0,20.0,100.0\r
+2.0,20.0,100.0\r
+3.0,20.0,100.0\r
+4.0,20.0,100.0\r
+5.0,20.0,100.0\r
+6.0,20.0,100.0\r
+7.0,20.0,100.0\r
+8.0,20.0,100.0\r
+9.0,20.0,100.0\r
+0.0,30.0,100.0\r
+1.0,30.0,100.0\r
+2.0,30.0,100.0\r
+3.0,30.0,100.0\r
+4.0,30.0,100.0\r
+5.0,30.0,100.0\r
+6.0,30.0,100.0\r
+7.0,30.0,100.0\r
+8.0,30.0,100.0\r
+9.0,30.0,100.0\r
+0.0,40.0,100.0\r
+1.0,40.0,100.0\r
+2.0,40.0,100.0\r
+3.0,40.0,100.0\r
+4.0,40.0,100.0\r
+5.0,40.0,100.0\r
+6.0,40.0,100.0\r
+7.0,40.0,100.0\r
+8.0,40.0,100.0\r
+9.0,40.0,100.0\r
+0.0,50.0,100.0\r
+1.0,50.0,100.0\r
+2.0,50.0,100.0\r
+3.0,50.0,100.0\r
+4.0,50.0,100.0\r
+5.0,50.0,100.0\r
+6.0,50.0,100.0\r
+7.0,50.0,100.0\r
+8.0,50.0,100.0\r
+9.0,50.0,100.0\r
+0.0,60.0,100.0\r
+1.0,60.0,100.0\r
+2.0,60.0,100.0\r
+3.0,60.0,100.0\r
+4.0,60.0,100.0\r
+5.0,60.0,100.0\r
+6.0,60.0,100.0\r
+7.0,60.0,100.0\r
+8.0,60.0,100.0\r
+9.0,60.0,100.0\r
+0.0,70.0,100.0\r
+1.0,70.0,100.0\r
+2.0,70.0,100.0\r
+3.0,70.0,100.0\r
+4.0,70.0,100.0\r
+5.0,70.0,100.0\r
+6.0,70.0,100.0\r
+7.0,70.0,100.0\r
+8.0,70.0,100.0\r
+9.0,70.0,100.0\r
+0.0,80.0,100.0\r
+1.0,80.0,100.0\r
+2.0,80.0,100.0\r
+3.0,80.0,100.0\r
+4.0,80.0,100.0\r
+5.0,80.0,100.0\r
+6.0,80.0,100.0\r
+7.0,80.0,100.0\r
+8.0,80.0,100.0\r
+9.0,80.0,100.0\r
+0.0,90.0,100.0\r
+1.0,90.0,100.0\r
+2.0,90.0,100.0\r
+3.0,90.0,100.0\r
+4.0,90.0,100.0\r
+5.0,90.0,100.0\r
+6.0,90.0,100.0\r
+7.0,90.0,100.0\r
+8.0,90.0,100.0\r
+9.0,90.0,100.0\r
+0.0,0.0,200.0\r
+1.0,0.0,200.0\r
+2.0,0.0,200.0\r
+3.0,0.0,200.0\r
+4.0,0.0,200.0\r
+5.0,0.0,200.0\r
+6.0,0.0,200.0\r
+7.0,0.0,200.0\r
+8.0,0.0,200.0\r
+9.0,0.0,200.0\r
+0.0,10.0,200.0\r
+1.0,10.0,200.0\r
+2.0,10.0,200.0\r
+3.0,10.0,200.0\r
+4.0,10.0,200.0\r
+5.0,10.0,200.0\r
+6.0,10.0,200.0\r
+7.0,10.0,200.0\r
+8.0,10.0,200.0\r
+9.0,10.0,200.0\r
+0.0,20.0,200.0\r
+1.0,20.0,200.0\r
+2.0,20.0,200.0\r
+3.0,20.0,200.0\r
+4.0,20.0,200.0\r
+5.0,20.0,200.0\r
+6.0,20.0,200.0\r
+7.0,20.0,200.0\r
+8.0,20.0,200.0\r
+9.0,20.0,200.0\r
+0.0,30.0,200.0\r
+1.0,30.0,200.0\r
+2.0,30.0,200.0\r
+3.0,30.0,200.0\r
+4.0,30.0,200.0\r
+5.0,30.0,200.0\r
+6.0,30.0,200.0\r
+7.0,30.0,200.0\r
+8.0,30.0,200.0\r
+9.0,30.0,200.0\r
+0.0,40.0,200.0\r
+1.0,40.0,200.0\r
+2.0,40.0,200.0\r
+3.0,40.0,200.0\r
+4.0,40.0,200.0\r
+5.0,40.0,200.0\r
+6.0,40.0,200.0\r
+7.0,40.0,200.0\r
+8.0,40.0,200.0\r
+9.0,40.0,200.0\r
+0.0,50.0,200.0\r
+1.0,50.0,200.0\r
+2.0,50.0,200.0\r
+3.0,50.0,200.0\r
+4.0,50.0,200.0\r
+5.0,50.0,200.0\r
+6.0,50.0,200.0\r
+7.0,50.0,200.0\r
+8.0,50.0,200.0\r
+9.0,50.0,200.0\r
+0.0,60.0,200.0\r
+1.0,60.0,200.0\r
+2.0,60.0,200.0\r
+3.0,60.0,200.0\r
+4.0,60.0,200.0\r
+5.0,60.0,200.0\r
+6.0,60.0,200.0\r
+7.0,60.0,200.0\r
+8.0,60.0,200.0\r
+9.0,60.0,200.0\r
+0.0,70.0,200.0\r
+1.0,70.0,200.0\r
+2.0,70.0,200.0\r
+3.0,70.0,200.0\r
+4.0,70.0,200.0\r
+5.0,70.0,200.0\r
+6.0,70.0,200.0\r
+7.0,70.0,200.0\r
+8.0,70.0,200.0\r
+9.0,70.0,200.0\r
+0.0,80.0,200.0\r
+1.0,80.0,200.0\r
+2.0,80.0,200.0\r
+3.0,80.0,200.0\r
+4.0,80.0,200.0\r
+5.0,80.0,200.0\r
+6.0,80.0,200.0\r
+7.0,80.0,200.0\r
+8.0,80.0,200.0\r
+9.0,80.0,200.0\r
+0.0,90.0,200.0\r
+1.0,90.0,200.0\r
+2.0,90.0,200.0\r
+3.0,90.0,200.0\r
+4.0,90.0,200.0\r
+5.0,90.0,200.0\r
+6.0,90.0,200.0\r
+7.0,90.0,200.0\r
+8.0,90.0,200.0\r
+9.0,90.0,200.0\r
+0.0,0.0,300.0\r
+1.0,0.0,300.0\r
+2.0,0.0,300.0\r
+3.0,0.0,300.0\r
+4.0,0.0,300.0\r
+5.0,0.0,300.0\r
+6.0,0.0,300.0\r
+7.0,0.0,300.0\r
+8.0,0.0,300.0\r
+9.0,0.0,300.0\r
+0.0,10.0,300.0\r
+1.0,10.0,300.0\r
+2.0,10.0,300.0\r
+3.0,10.0,300.0\r
+4.0,10.0,300.0\r
+5.0,10.0,300.0\r
+6.0,10.0,300.0\r
+7.0,10.0,300.0\r
+8.0,10.0,300.0\r
+9.0,10.0,300.0\r
+0.0,20.0,300.0\r
+1.0,20.0,300.0\r
+2.0,20.0,300.0\r
+3.0,20.0,300.0\r
+4.0,20.0,300.0\r
+5.0,20.0,300.0\r
+6.0,20.0,300.0\r
+7.0,20.0,300.0\r
+8.0,20.0,300.0\r
+9.0,20.0,300.0\r
+0.0,30.0,300.0\r
+1.0,30.0,300.0\r
+2.0,30.0,300.0\r
+3.0,30.0,300.0\r
+4.0,30.0,300.0\r
+5.0,30.0,300.0\r
+6.0,30.0,300.0\r
+7.0,30.0,300.0\r
+8.0,30.0,300.0\r
+9.0,30.0,300.0\r
+0.0,40.0,300.0\r
+1.0,40.0,300.0\r
+2.0,40.0,300.0\r
+3.0,40.0,300.0\r
+4.0,40.0,300.0\r
+5.0,40.0,300.0\r
+6.0,40.0,300.0\r
+7.0,40.0,300.0\r
+8.0,40.0,300.0\r
+9.0,40.0,300.0\r
+0.0,50.0,300.0\r
+1.0,50.0,300.0\r
+2.0,50.0,300.0\r
+3.0,50.0,300.0\r
+4.0,50.0,300.0\r
+5.0,50.0,300.0\r
+6.0,50.0,300.0\r
+7.0,50.0,300.0\r
+8.0,50.0,300.0\r
+9.0,50.0,300.0\r
+0.0,60.0,300.0\r
+1.0,60.0,300.0\r
+2.0,60.0,300.0\r
+3.0,60.0,300.0\r
+4.0,60.0,300.0\r
+5.0,60.0,300.0\r
+6.0,60.0,300.0\r
+7.0,60.0,300.0\r
+8.0,60.0,300.0\r
+9.0,60.0,300.0\r
+0.0,70.0,300.0\r
+1.0,70.0,300.0\r
+2.0,70.0,300.0\r
+3.0,70.0,300.0\r
+4.0,70.0,300.0\r
+5.0,70.0,300.0\r
+6.0,70.0,300.0\r
+7.0,70.0,300.0\r
+8.0,70.0,300.0\r
+9.0,70.0,300.0\r
+0.0,80.0,300.0\r
+1.0,80.0,300.0\r
+2.0,80.0,300.0\r
+3.0,80.0,300.0\r
+4.0,80.0,300.0\r
+5.0,80.0,300.0\r
+6.0,80.0,300.0\r
+7.0,80.0,300.0\r
+8.0,80.0,300.0\r
+9.0,80.0,300.0\r
+0.0,90.0,300.0\r
+1.0,90.0,300.0\r
+2.0,90.0,300.0\r
+3.0,90.0,300.0\r
+4.0,90.0,300.0\r
+5.0,90.0,300.0\r
+6.0,90.0,300.0\r
+7.0,90.0,300.0\r
+8.0,90.0,300.0\r
+9.0,90.0,300.0\r
+0.0,0.0,400.0\r
+1.0,0.0,400.0\r
+2.0,0.0,400.0\r
+3.0,0.0,400.0\r
+4.0,0.0,400.0\r
+5.0,0.0,400.0\r
+6.0,0.0,400.0\r
+7.0,0.0,400.0\r
+8.0,0.0,400.0\r
+9.0,0.0,400.0\r
+0.0,10.0,400.0\r
+1.0,10.0,400.0\r
+2.0,10.0,400.0\r
+3.0,10.0,400.0\r
+4.0,10.0,400.0\r
+5.0,10.0,400.0\r
+6.0,10.0,400.0\r
+7.0,10.0,400.0\r
+8.0,10.0,400.0\r
+9.0,10.0,400.0\r
+0.0,20.0,400.0\r
+1.0,20.0,400.0\r
+2.0,20.0,400.0\r
+3.0,20.0,400.0\r
+4.0,20.0,400.0\r
+5.0,20.0,400.0\r
+6.0,20.0,400.0\r
+7.0,20.0,400.0\r
+8.0,20.0,400.0\r
+9.0,20.0,400.0\r
+0.0,30.0,400.0\r
+1.0,30.0,400.0\r
+2.0,30.0,400.0\r
+3.0,30.0,400.0\r
+4.0,30.0,400.0\r
+5.0,30.0,400.0\r
+6.0,30.0,400.0\r
+7.0,30.0,400.0\r
+8.0,30.0,400.0\r
+9.0,30.0,400.0\r
+0.0,40.0,400.0\r
+1.0,40.0,400.0\r
+2.0,40.0,400.0\r
+3.0,40.0,400.0\r
+4.0,40.0,400.0\r
+5.0,40.0,400.0\r
+6.0,40.0,400.0\r
+7.0,40.0,400.0\r
+8.0,40.0,400.0\r
+9.0,40.0,400.0\r
+0.0,50.0,400.0\r
+1.0,50.0,400.0\r
+2.0,50.0,400.0\r
+3.0,50.0,400.0\r
+4.0,50.0,400.0\r
+5.0,50.0,400.0\r
+6.0,50.0,400.0\r
+7.0,50.0,400.0\r
+8.0,50.0,400.0\r
+9.0,50.0,400.0\r
+0.0,60.0,400.0\r
+1.0,60.0,400.0\r
+2.0,60.0,400.0\r
+3.0,60.0,400.0\r
+4.0,60.0,400.0\r
+5.0,60.0,400.0\r
+6.0,60.0,400.0\r
+7.0,60.0,400.0\r
+8.0,60.0,400.0\r
+9.0,60.0,400.0\r
+0.0,70.0,400.0\r
+1.0,70.0,400.0\r
+2.0,70.0,400.0\r
+3.0,70.0,400.0\r
+4.0,70.0,400.0\r
+5.0,70.0,400.0\r
+6.0,70.0,400.0\r
+7.0,70.0,400.0\r
+8.0,70.0,400.0\r
+9.0,70.0,400.0\r
+0.0,80.0,400.0\r
+1.0,80.0,400.0\r
+2.0,80.0,400.0\r
+3.0,80.0,400.0\r
+4.0,80.0,400.0\r
+5.0,80.0,400.0\r
+6.0,80.0,400.0\r
+7.0,80.0,400.0\r
+8.0,80.0,400.0\r
+9.0,80.0,400.0\r
+0.0,90.0,400.0\r
+1.0,90.0,400.0\r
+2.0,90.0,400.0\r
+3.0,90.0,400.0\r
+4.0,90.0,400.0\r
+5.0,90.0,400.0\r
+6.0,90.0,400.0\r
+7.0,90.0,400.0\r
+8.0,90.0,400.0\r
+9.0,90.0,400.0\r
+0.0,0.0,500.0\r
+1.0,0.0,500.0\r
+2.0,0.0,500.0\r
+3.0,0.0,500.0\r
+4.0,0.0,500.0\r
+5.0,0.0,500.0\r
+6.0,0.0,500.0\r
+7.0,0.0,500.0\r
+8.0,0.0,500.0\r
+9.0,0.0,500.0\r
+0.0,10.0,500.0\r
+1.0,10.0,500.0\r
+2.0,10.0,500.0\r
+3.0,10.0,500.0\r
+4.0,10.0,500.0\r
+5.0,10.0,500.0\r
+6.0,10.0,500.0\r
+7.0,10.0,500.0\r
+8.0,10.0,500.0\r
+9.0,10.0,500.0\r
+0.0,20.0,500.0\r
+1.0,20.0,500.0\r
+2.0,20.0,500.0\r
+3.0,20.0,500.0\r
+4.0,20.0,500.0\r
+5.0,20.0,500.0\r
+6.0,20.0,500.0\r
+7.0,20.0,500.0\r
+8.0,20.0,500.0\r
+9.0,20.0,500.0\r
+0.0,30.0,500.0\r
+1.0,30.0,500.0\r
+2.0,30.0,500.0\r
+3.0,30.0,500.0\r
+4.0,30.0,500.0\r
+5.0,30.0,500.0\r
+6.0,30.0,500.0\r
+7.0,30.0,500.0\r
+8.0,30.0,500.0\r
+9.0,30.0,500.0\r
+0.0,40.0,500.0\r
+1.0,40.0,500.0\r
+2.0,40.0,500.0\r
+3.0,40.0,500.0\r
+4.0,40.0,500.0\r
+5.0,40.0,500.0\r
+6.0,40.0,500.0\r
+7.0,40.0,500.0\r
+8.0,40.0,500.0\r
+9.0,40.0,500.0\r
+0.0,50.0,500.0\r
+1.0,50.0,500.0\r
+2.0,50.0,500.0\r
+3.0,50.0,500.0\r
+4.0,50.0,500.0\r
+5.0,50.0,500.0\r
+6.0,50.0,500.0\r
+7.0,50.0,500.0\r
+8.0,50.0,500.0\r
+9.0,50.0,500.0\r
+0.0,60.0,500.0\r
+1.0,60.0,500.0\r
+2.0,60.0,500.0\r
+3.0,60.0,500.0\r
+4.0,60.0,500.0\r
+5.0,60.0,500.0\r
+6.0,60.0,500.0\r
+7.0,60.0,500.0\r
+8.0,60.0,500.0\r
+9.0,60.0,500.0\r
+0.0,70.0,500.0\r
+1.0,70.0,500.0\r
+2.0,70.0,500.0\r
+3.0,70.0,500.0\r
+4.0,70.0,500.0\r
+5.0,70.0,500.0\r
+6.0,70.0,500.0\r
+7.0,70.0,500.0\r
+8.0,70.0,500.0\r
+9.0,70.0,500.0\r
+0.0,80.0,500.0\r
+1.0,80.0,500.0\r
+2.0,80.0,500.0\r
+3.0,80.0,500.0\r
+4.0,80.0,500.0\r
+5.0,80.0,500.0\r
+6.0,80.0,500.0\r
+7.0,80.0,500.0\r
+8.0,80.0,500.0\r
+9.0,80.0,500.0\r
+0.0,90.0,500.0\r
+1.0,90.0,500.0\r
+2.0,90.0,500.0\r
+3.0,90.0,500.0\r
+4.0,90.0,500.0\r
+5.0,90.0,500.0\r
+6.0,90.0,500.0\r
+7.0,90.0,500.0\r
+8.0,90.0,500.0\r
+9.0,90.0,500.0\r
+0.0,0.0,600.0\r
+1.0,0.0,600.0\r
+2.0,0.0,600.0\r
+3.0,0.0,600.0\r
+4.0,0.0,600.0\r
+5.0,0.0,600.0\r
+6.0,0.0,600.0\r
+7.0,0.0,600.0\r
+8.0,0.0,600.0\r
+9.0,0.0,600.0\r
+0.0,10.0,600.0\r
+1.0,10.0,600.0\r
+2.0,10.0,600.0\r
+3.0,10.0,600.0\r
+4.0,10.0,600.0\r
+5.0,10.0,600.0\r
+6.0,10.0,600.0\r
+7.0,10.0,600.0\r
+8.0,10.0,600.0\r
+9.0,10.0,600.0\r
+0.0,20.0,600.0\r
+1.0,20.0,600.0\r
+2.0,20.0,600.0\r
+3.0,20.0,600.0\r
+4.0,20.0,600.0\r
+5.0,20.0,600.0\r
+6.0,20.0,600.0\r
+7.0,20.0,600.0\r
+8.0,20.0,600.0\r
+9.0,20.0,600.0\r
+0.0,30.0,600.0\r
+1.0,30.0,600.0\r
+2.0,30.0,600.0\r
+3.0,30.0,600.0\r
+4.0,30.0,600.0\r
+5.0,30.0,600.0\r
+6.0,30.0,600.0\r
+7.0,30.0,600.0\r
+8.0,30.0,600.0\r
+9.0,30.0,600.0\r
+0.0,40.0,600.0\r
+1.0,40.0,600.0\r
+2.0,40.0,600.0\r
+3.0,40.0,600.0\r
+4.0,40.0,600.0\r
+5.0,40.0,600.0\r
+6.0,40.0,600.0\r
+7.0,40.0,600.0\r
+8.0,40.0,600.0\r
+9.0,40.0,600.0\r
+0.0,50.0,600.0\r
+1.0,50.0,600.0\r
+2.0,50.0,600.0\r
+3.0,50.0,600.0\r
+4.0,50.0,600.0\r
+5.0,50.0,600.0\r
+6.0,50.0,600.0\r
+7.0,50.0,600.0\r
+8.0,50.0,600.0\r
+9.0,50.0,600.0\r
+0.0,60.0,600.0\r
+1.0,60.0,600.0\r
+2.0,60.0,600.0\r
+3.0,60.0,600.0\r
+4.0,60.0,600.0\r
+5.0,60.0,600.0\r
+6.0,60.0,600.0\r
+7.0,60.0,600.0\r
+8.0,60.0,600.0\r
+9.0,60.0,600.0\r
+0.0,70.0,600.0\r
+1.0,70.0,600.0\r
+2.0,70.0,600.0\r
+3.0,70.0,600.0\r
+4.0,70.0,600.0\r
+5.0,70.0,600.0\r
+6.0,70.0,600.0\r
+7.0,70.0,600.0\r
+8.0,70.0,600.0\r
+9.0,70.0,600.0\r
+0.0,80.0,600.0\r
+1.0,80.0,600.0\r
+2.0,80.0,600.0\r
+3.0,80.0,600.0\r
+4.0,80.0,600.0\r
+5.0,80.0,600.0\r
+6.0,80.0,600.0\r
+7.0,80.0,600.0\r
+8.0,80.0,600.0\r
+9.0,80.0,600.0\r
+0.0,90.0,600.0\r
+1.0,90.0,600.0\r
+2.0,90.0,600.0\r
+3.0,90.0,600.0\r
+4.0,90.0,600.0\r
+5.0,90.0,600.0\r
+6.0,90.0,600.0\r
+7.0,90.0,600.0\r
+8.0,90.0,600.0\r
+9.0,90.0,600.0\r
+0.0,0.0,700.0\r
+1.0,0.0,700.0\r
+2.0,0.0,700.0\r
+3.0,0.0,700.0\r
+4.0,0.0,700.0\r
+5.0,0.0,700.0\r
+6.0,0.0,700.0\r
+7.0,0.0,700.0\r
+8.0,0.0,700.0\r
+9.0,0.0,700.0\r
+0.0,10.0,700.0\r
+1.0,10.0,700.0\r
+2.0,10.0,700.0\r
+3.0,10.0,700.0\r
+4.0,10.0,700.0\r
+5.0,10.0,700.0\r
+6.0,10.0,700.0\r
+7.0,10.0,700.0\r
+8.0,10.0,700.0\r
+9.0,10.0,700.0\r
+0.0,20.0,700.0\r
+1.0,20.0,700.0\r
+2.0,20.0,700.0\r
+3.0,20.0,700.0\r
+4.0,20.0,700.0\r
+5.0,20.0,700.0\r
+6.0,20.0,700.0\r
+7.0,20.0,700.0\r
+8.0,20.0,700.0\r
+9.0,20.0,700.0\r
+0.0,30.0,700.0\r
+1.0,30.0,700.0\r
+2.0,30.0,700.0\r
+3.0,30.0,700.0\r
+4.0,30.0,700.0\r
+5.0,30.0,700.0\r
+6.0,30.0,700.0\r
+7.0,30.0,700.0\r
+8.0,30.0,700.0\r
+9.0,30.0,700.0\r
+0.0,40.0,700.0\r
+1.0,40.0,700.0\r
+2.0,40.0,700.0\r
+3.0,40.0,700.0\r
+4.0,40.0,700.0\r
+5.0,40.0,700.0\r
+6.0,40.0,700.0\r
+7.0,40.0,700.0\r
+8.0,40.0,700.0\r
+9.0,40.0,700.0\r
+0.0,50.0,700.0\r
+1.0,50.0,700.0\r
+2.0,50.0,700.0\r
+3.0,50.0,700.0\r
+4.0,50.0,700.0\r
+5.0,50.0,700.0\r
+6.0,50.0,700.0\r
+7.0,50.0,700.0\r
+8.0,50.0,700.0\r
+9.0,50.0,700.0\r
+0.0,60.0,700.0\r
+1.0,60.0,700.0\r
+2.0,60.0,700.0\r
+3.0,60.0,700.0\r
+4.0,60.0,700.0\r
+5.0,60.0,700.0\r
+6.0,60.0,700.0\r
+7.0,60.0,700.0\r
+8.0,60.0,700.0\r
+9.0,60.0,700.0\r
+0.0,70.0,700.0\r
+1.0,70.0,700.0\r
+2.0,70.0,700.0\r
+3.0,70.0,700.0\r
+4.0,70.0,700.0\r
+5.0,70.0,700.0\r
+6.0,70.0,700.0\r
+7.0,70.0,700.0\r
+8.0,70.0,700.0\r
+9.0,70.0,700.0\r
+0.0,80.0,700.0\r
+1.0,80.0,700.0\r
+2.0,80.0,700.0\r
+3.0,80.0,700.0\r
+4.0,80.0,700.0\r
+5.0,80.0,700.0\r
+6.0,80.0,700.0\r
+7.0,80.0,700.0\r
+8.0,80.0,700.0\r
+9.0,80.0,700.0\r
+0.0,90.0,700.0\r
+1.0,90.0,700.0\r
+2.0,90.0,700.0\r
+3.0,90.0,700.0\r
+4.0,90.0,700.0\r
+5.0,90.0,700.0\r
+6.0,90.0,700.0\r
+7.0,90.0,700.0\r
+8.0,90.0,700.0\r
+9.0,90.0,700.0\r
+0.0,0.0,800.0\r
+1.0,0.0,800.0\r
+2.0,0.0,800.0\r
+3.0,0.0,800.0\r
+4.0,0.0,800.0\r
+5.0,0.0,800.0\r
+6.0,0.0,800.0\r
+7.0,0.0,800.0\r
+8.0,0.0,800.0\r
+9.0,0.0,800.0\r
+0.0,10.0,800.0\r
+1.0,10.0,800.0\r
+2.0,10.0,800.0\r
+3.0,10.0,800.0\r
+4.0,10.0,800.0\r
+5.0,10.0,800.0\r
+6.0,10.0,800.0\r
+7.0,10.0,800.0\r
+8.0,10.0,800.0\r
+9.0,10.0,800.0\r
+0.0,20.0,800.0\r
+1.0,20.0,800.0\r
+2.0,20.0,800.0\r
+3.0,20.0,800.0\r
+4.0,20.0,800.0\r
+5.0,20.0,800.0\r
+6.0,20.0,800.0\r
+7.0,20.0,800.0\r
+8.0,20.0,800.0\r
+9.0,20.0,800.0\r
+0.0,30.0,800.0\r
+1.0,30.0,800.0\r
+2.0,30.0,800.0\r
+3.0,30.0,800.0\r
+4.0,30.0,800.0\r
+5.0,30.0,800.0\r
+6.0,30.0,800.0\r
+7.0,30.0,800.0\r
+8.0,30.0,800.0\r
+9.0,30.0,800.0\r
+0.0,40.0,800.0\r
+1.0,40.0,800.0\r
+2.0,40.0,800.0\r
+3.0,40.0,800.0\r
+4.0,40.0,800.0\r
+5.0,40.0,800.0\r
+6.0,40.0,800.0\r
+7.0,40.0,800.0\r
+8.0,40.0,800.0\r
+9.0,40.0,800.0\r
+0.0,50.0,800.0\r
+1.0,50.0,800.0\r
+2.0,50.0,800.0\r
+3.0,50.0,800.0\r
+4.0,50.0,800.0\r
+5.0,50.0,800.0\r
+6.0,50.0,800.0\r
+7.0,50.0,800.0\r
+8.0,50.0,800.0\r
+9.0,50.0,800.0\r
+0.0,60.0,800.0\r
+1.0,60.0,800.0\r
+2.0,60.0,800.0\r
+3.0,60.0,800.0\r
+4.0,60.0,800.0\r
+5.0,60.0,800.0\r
+6.0,60.0,800.0\r
+7.0,60.0,800.0\r
+8.0,60.0,800.0\r
+9.0,60.0,800.0\r
+0.0,70.0,800.0\r
+1.0,70.0,800.0\r
+2.0,70.0,800.0\r
+3.0,70.0,800.0\r
+4.0,70.0,800.0\r
+5.0,70.0,800.0\r
+6.0,70.0,800.0\r
+7.0,70.0,800.0\r
+8.0,70.0,800.0\r
+9.0,70.0,800.0\r
+0.0,80.0,800.0\r
+1.0,80.0,800.0\r
+2.0,80.0,800.0\r
+3.0,80.0,800.0\r
+4.0,80.0,800.0\r
+5.0,80.0,800.0\r
+6.0,80.0,800.0\r
+7.0,80.0,800.0\r
+8.0,80.0,800.0\r
+9.0,80.0,800.0\r
+0.0,90.0,800.0\r
+1.0,90.0,800.0\r
+2.0,90.0,800.0\r
+3.0,90.0,800.0\r
+4.0,90.0,800.0\r
+5.0,90.0,800.0\r
+6.0,90.0,800.0\r
+7.0,90.0,800.0\r
+8.0,90.0,800.0\r
+9.0,90.0,800.0\r
+0.0,0.0,900.0\r
+1.0,0.0,900.0\r
+2.0,0.0,900.0\r
+3.0,0.0,900.0\r
+4.0,0.0,900.0\r
+5.0,0.0,900.0\r
+6.0,0.0,900.0\r
+7.0,0.0,900.0\r
+8.0,0.0,900.0\r
+9.0,0.0,900.0\r
+0.0,10.0,900.0\r
+1.0,10.0,900.0\r
+2.0,10.0,900.0\r
+3.0,10.0,900.0\r
+4.0,10.0,900.0\r
+5.0,10.0,900.0\r
+6.0,10.0,900.0\r
+7.0,10.0,900.0\r
+8.0,10.0,900.0\r
+9.0,10.0,900.0\r
+0.0,20.0,900.0\r
+1.0,20.0,900.0\r
+2.0,20.0,900.0\r
+3.0,20.0,900.0\r
+4.0,20.0,900.0\r
+5.0,20.0,900.0\r
+6.0,20.0,900.0\r
+7.0,20.0,900.0\r
+8.0,20.0,900.0\r
+9.0,20.0,900.0\r
+0.0,30.0,900.0\r
+1.0,30.0,900.0\r
+2.0,30.0,900.0\r
+3.0,30.0,900.0\r
+4.0,30.0,900.0\r
+5.0,30.0,900.0\r
+6.0,30.0,900.0\r
+7.0,30.0,900.0\r
+8.0,30.0,900.0\r
+9.0,30.0,900.0\r
+0.0,40.0,900.0\r
+1.0,40.0,900.0\r
+2.0,40.0,900.0\r
+3.0,40.0,900.0\r
+4.0,40.0,900.0\r
+5.0,40.0,900.0\r
+6.0,40.0,900.0\r
+7.0,40.0,900.0\r
+8.0,40.0,900.0\r
+9.0,40.0,900.0\r
+0.0,50.0,900.0\r
+1.0,50.0,900.0\r
+2.0,50.0,900.0\r
+3.0,50.0,900.0\r
+4.0,50.0,900.0\r
+5.0,50.0,900.0\r
+6.0,50.0,900.0\r
+7.0,50.0,900.0\r
+8.0,50.0,900.0\r
+9.0,50.0,900.0\r
+0.0,60.0,900.0\r
+1.0,60.0,900.0\r
+2.0,60.0,900.0\r
+3.0,60.0,900.0\r
+4.0,60.0,900.0\r
+5.0,60.0,900.0\r
+6.0,60.0,900.0\r
+7.0,60.0,900.0\r
+8.0,60.0,900.0\r
+9.0,60.0,900.0\r
+0.0,70.0,900.0\r
+1.0,70.0,900.0\r
+2.0,70.0,900.0\r
+3.0,70.0,900.0\r
+4.0,70.0,900.0\r
+5.0,70.0,900.0\r
+6.0,70.0,900.0\r
+7.0,70.0,900.0\r
+8.0,70.0,900.0\r
+9.0,70.0,900.0\r
+0.0,80.0,900.0\r
+1.0,80.0,900.0\r
+2.0,80.0,900.0\r
+3.0,80.0,900.0\r
+4.0,80.0,900.0\r
+5.0,80.0,900.0\r
+6.0,80.0,900.0\r
+7.0,80.0,900.0\r
+8.0,80.0,900.0\r
+9.0,80.0,900.0\r
+0.0,90.0,900.0\r
+1.0,90.0,900.0\r
+2.0,90.0,900.0\r
+3.0,90.0,900.0\r
+4.0,90.0,900.0\r
+5.0,90.0,900.0\r
+6.0,90.0,900.0\r
+7.0,90.0,900.0\r
+8.0,90.0,900.0\r
+9.0,90.0,900.0\r
diff --git a/src/pyexample/idefixoutputnames.csv b/src/pyexample/idefixoutputnames.csv
new file mode 100644 (file)
index 0000000..b478595
--- /dev/null
@@ -0,0 +1 @@
+s
diff --git a/src/pyexample/idefixstudy.py b/src/pyexample/idefixstudy.py
new file mode 100644 (file)
index 0000000..895c6ad
--- /dev/null
@@ -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 (file)
index 0000000..56956a6
--- /dev/null
@@ -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 (file)
index 0000000..1cb16ba
--- /dev/null
@@ -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 (executable)
index 0000000..fe560fe
--- /dev/null
@@ -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 (file)
index 0000000..a7b1ed4
--- /dev/null
@@ -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 (file)
index 0000000..14575bb
--- /dev/null
@@ -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 (executable)
index 0000000..93904b2
--- /dev/null
@@ -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 (file)
index 0000000..71d2f9f
--- /dev/null
@@ -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 (file)
index 0000000..895c6ad
--- /dev/null
@@ -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 (file)
index 0000000..78cb03f
--- /dev/null
@@ -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 (file)
index 0000000..829e421
--- /dev/null
@@ -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())