pydefx 9.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. mpmcn.py +154 -0
  2. pydefx/__init__.py +32 -0
  3. pydefx/allpurposebuilder.py +56 -0
  4. pydefx/configuration.py +120 -0
  5. pydefx/defaultschemabuilder.py +55 -0
  6. pydefx/localbuilder.py +37 -0
  7. pydefx/localstudy.py +78 -0
  8. pydefx/multijobbuilder.py +33 -0
  9. pydefx/multijobstudy.py +84 -0
  10. pydefx/parameters.py +169 -0
  11. pydefx/plugins/jobexecutor.py +130 -0
  12. pydefx/plugins/lightexecutor.py +40 -0
  13. pydefx/plugins/localexecutor.py +81 -0
  14. pydefx/plugins/mainjob.py +56 -0
  15. pydefx/plugins/pointeval.py +39 -0
  16. pydefx/plugins/srunexecutor.py +87 -0
  17. pydefx/pyscript.py +110 -0
  18. pydefx/pystudy.py +327 -0
  19. pydefx/salome_proxy.py +88 -0
  20. pydefx/sample.py +263 -0
  21. pydefx/samplecsviterator.py +191 -0
  22. pydefx/samplecsvmanager.py +136 -0
  23. pydefx/schemas/idefix_pyschema.xml +106 -0
  24. pydefx/schemas/plugin.py +81 -0
  25. pydefx/slurmbuilder.py +33 -0
  26. pydefx/slurmstudy.py +77 -0
  27. pydefx/studyexception.py +41 -0
  28. pydefx/studyresult.py +54 -0
  29. pydefx-9.14.0.dist-info/METADATA +22 -0
  30. pydefx-9.14.0.dist-info/RECORD +45 -0
  31. pydefx-9.14.0.dist-info/WHEEL +4 -0
  32. salome/bin/salome/test/CTestTestfile.cmake +28 -0
  33. salome/bin/salome/test/cpp/CTestTestfile.cmake +36 -0
  34. salome/bin/salome/test/cpp/SampleTest +0 -0
  35. salome/bin/salome/test/cpp/StudyGeneralTest +0 -0
  36. salome/bin/salome/test/cpp/StudyRestartTest +0 -0
  37. salome/bin/salome/test/pyexample/CTestTestfile.cmake +25 -0
  38. salome/bin/salome/test/pyexample/insitu/insituiterator.py +48 -0
  39. salome/bin/salome/test/pyexample/insitu/insitumanager.py +46 -0
  40. salome/bin/salome/test/pyexample/runUnitTest.sh +26 -0
  41. salome/bin/salome/test/pyexample/test_default.py +79 -0
  42. salome/bin/salome/test/pyexample/test_insitu.py +60 -0
  43. salome/bin/salome/test/pyexample/test_mpmcn.py +40 -0
  44. salome/bin/salome/test/pyexample/test_prescript.py +52 -0
  45. salome/bin/salome/test/pyexample/test_ydefx_base.py +79 -0
pydefx/pyscript.py ADDED
@@ -0,0 +1,110 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2019-2024 EDF
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ #
18
+ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19
+ #
20
+ from . import sample
21
+ from salome.yacs import py2yacs
22
+
23
+ class PyScriptException(Exception):
24
+ pass
25
+
26
+ class PyScript:
27
+ def __init__(self):
28
+ self.script = ""
29
+ self.properties, self.errors = py2yacs.get_properties(self.script)
30
+
31
+ def loadFile(self,path):
32
+ with open(path, "r") as f:
33
+ self.script = f.read()
34
+ self.properties, self.errors = py2yacs.get_properties(self.script)
35
+
36
+ def loadString(self, script):
37
+ self.script = script
38
+ self.properties, self.errors = py2yacs.get_properties(self.script)
39
+
40
+ def content(self):
41
+ return self.script
42
+
43
+ def saveFile(self, path):
44
+ with open(path, "w") as f:
45
+ f.write(self.script)
46
+
47
+ def getAllProperties(self):
48
+ """
49
+ functions,errors = myscript.getAllProperties()
50
+ print(errors) # list of syntax errors in the script
51
+ for f in functions:
52
+ print(f.name) # function name
53
+ print(f.inputs) # list of input variable names
54
+ print(f.outputs) # list of output variable names
55
+ print(f.errors) # list of py2yacs errors in the function
56
+ print(f.imports) # list of import statements in the function
57
+ """
58
+ return py2yacs.get_properties(self.script)
59
+
60
+ def getFunctionProperties(self, fname = "_exec"):
61
+ """
62
+ Properties of the _exec function:
63
+ fn_properties = myscript.getFunctionProperties()
64
+ fn_properties.name : "_exec"
65
+ fn_properties.inputs : list of input variable names
66
+ fn_properties.outputs : list of output variable names
67
+ fn_properties.errors : list of py2yacs errors in the function
68
+ fn_properties.imports : list of import statements in the function
69
+ fn_properties is None if the "_exec" function does not exist.
70
+ """
71
+ fn_properties = next((f for f in self.properties if f.name == fname), None)
72
+ return fn_properties
73
+
74
+ def getOutputNames(self, fname = "_exec"):
75
+ errorsText = self.getErrors(fname)
76
+ if len(errorsText) > 0:
77
+ raise PyScriptException(errorsText)
78
+ fnProperties = self.getFunctionProperties(fname)
79
+ return fnProperties.outputs
80
+
81
+ def getInputNames(self, fname = "_exec"):
82
+ errorsText = self.getErrors(fname)
83
+ if len(errorsText) > 0:
84
+ raise PyScriptException(errorsText)
85
+ fnProperties = self.getFunctionProperties(fname)
86
+ return fnProperties.inputs
87
+
88
+ def getErrors(self, fname = "_exec"):
89
+ error_string = ""
90
+ if len(self.errors) > 0:
91
+ error_string = "global errors:\n"
92
+ error_string += '\n'.join(self.errors)
93
+ else:
94
+ properties = self.getFunctionProperties(fname)
95
+ if properties is None:
96
+ error_string += "Function {} not found in the script!".format(fname)
97
+ else:
98
+ error_string += '\n'.join(properties.errors)
99
+ return error_string
100
+
101
+ def CreateEmptySample(self):
102
+ """
103
+ Create a sample with input and output variable names set.
104
+ """
105
+ fn = "_exec"
106
+ errors = self.getErrors(fn)
107
+ if len(errors) > 0:
108
+ raise PyScriptException(errors)
109
+ fn_properties = self.getFunctionProperties(fn)
110
+ return sample.Sample(fn_properties.inputs, fn_properties.outputs)
pydefx/pystudy.py ADDED
@@ -0,0 +1,327 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2019-2024 EDF
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ #
18
+ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19
+ #
20
+ import inspect
21
+ import pathlib
22
+ import tempfile
23
+ import os
24
+ import json
25
+ from . import salome_proxy
26
+ from . import samplecsvmanager
27
+ from . import parameters
28
+ from . import configuration
29
+ from . import defaultschemabuilder
30
+ from .studyexception import StudyUseException, StudyRunException
31
+ from .studyresult import StudyResult
32
+
33
+ def defaultSampleManager():
34
+ return samplecsvmanager.SampleManager()
35
+
36
+ class PyStudy:
37
+ JOB_DUMP_NAME = "jobDump.xml"
38
+ def __init__(self, sampleManager=None, schemaBuilder=None):
39
+ self.job_id = -1
40
+ self.global_result = StudyResult()
41
+ if sampleManager is None:
42
+ self.sampleManager = defaultSampleManager()
43
+ else:
44
+ self.sampleManager = sampleManager
45
+ if schemaBuilder is None:
46
+ self.schemaBuilder = defaultschemabuilder.DefaultSchemaBuilder()
47
+ else:
48
+ self.schemaBuilder = schemaBuilder
49
+
50
+ # Study creation functions
51
+ def createNewJob(self, script, sample, params):
52
+ """
53
+ Create a new job out of those parameters:
54
+ script : script / pyscript type
55
+ sample : sample to be evaluated (Sample class)
56
+ params : job submission parameters (Parameters class)
57
+ The result directory will contain all the files needed for a launch and a
58
+ job is created but not launched.
59
+ """
60
+ self._check(script,sample)
61
+ self.sample = sample
62
+ self.params = params
63
+ self.params.salome_parameters.job_type = self.jobType()
64
+ tmp_workdir = self.params.salome_parameters.result_directory
65
+ schema_path, extra_files = self._prepareDirectoryForLaunch(tmp_workdir,
66
+ script)
67
+ # this list manipulation is needed because in_files is not a python list
68
+ # if we don't use a salome session. In that case swig uses a python tuple
69
+ # in order to map a std::list as a parameter of a structure.
70
+ in_files_as_list = list(self.params.salome_parameters.in_files)
71
+ self.params.salome_parameters.in_files = in_files_as_list + extra_files
72
+ self.params.salome_parameters.job_file = schema_path
73
+ launcher = salome_proxy.getLauncher()
74
+ self.job_id = launcher.createJob(self.params.salome_parameters)
75
+ return self.job_id
76
+
77
+ def loadFromDirectory(self, path):
78
+ """
79
+ Recover a study from a result directory where a previous study was launched.
80
+ """
81
+ self.sample = self.sampleManager.restoreSample(path)
82
+ job_string = loadJobString(path)
83
+ launcher = salome_proxy.getLauncher()
84
+ self.job_id = launcher.restoreJob(job_string)
85
+ if self.job_id >= 0:
86
+ salome_params = launcher.getJobParameters(self.job_id)
87
+ self.params = parameters.Parameters(salome_parameters=salome_params)
88
+ self.getResult()
89
+ return self.job_id
90
+
91
+ def loadFromString(self, jobstring):
92
+ """
93
+ Recover a study from a string which contains the description of the job.
94
+ This string can be obtained by launcher.dumpJob.
95
+ """
96
+ launcher = salome_proxy.getLauncher()
97
+ self.job_id = launcher.restoreJob(jobstring)
98
+ self.params = None
99
+ self.sample = None
100
+ if self.job_id >= 0:
101
+ salome_params = launcher.getJobParameters(self.job_id)
102
+ self.params = parameters.Parameters(salome_parameters=salome_params)
103
+ #TODO: sampleManager should be loaded from result_directory
104
+ self.sample = self.sampleManager.restoreSample(
105
+ salome_params.result_directory)
106
+ self.getResult()
107
+ else:
108
+ raise StudyRunException("Failed to restore the job.")
109
+
110
+ def loadFromId(self, jobid):
111
+ """
112
+ Connect the study to an already created job.
113
+ The result directory of the job must be already prepared for launch.
114
+ """
115
+ if jobid < 0:
116
+ return
117
+ self.job_id = jobid
118
+ launcher = salome_proxy.getLauncher()
119
+ salome_params = launcher.getJobParameters(self.job_id)
120
+ self.params = parameters.Parameters(salome_parameters=salome_params)
121
+ #TODO: sampleManager should be loaded from result_directory
122
+ self.sample=self.sampleManager.restoreSample(salome_params.result_directory)
123
+ self.script = None
124
+ return
125
+
126
+ # launch parameters functions
127
+ def jobType(self):
128
+ return "yacs_file"
129
+
130
+ # TODO: may be deprecated
131
+ def createDefaultParameters(self, resource="localhost",
132
+ nb_branches=None,
133
+ result_base_dir=None):
134
+ """
135
+ Create the Parameters structure and the result directory.
136
+ The result directory created here is needed by the job.
137
+ """
138
+ newParams = parameters.Parameters(resource, nb_branches)
139
+ newParams.salome_parameters.job_type = self.jobType()
140
+ newParams.salome_parameters.job_name = "idefix_job"
141
+ newParams.salome_parameters.result_directory = configuration.newResultDirectory(result_base_dir)
142
+ return newParams
143
+
144
+ # Job management functions
145
+ def launch(self):
146
+ """
147
+ The job should have been already created.
148
+ """
149
+ if self.job_id < 0 :
150
+ raise StudyUseException("Nothing to launch! Job is not created!")
151
+ tmp_workdir = self.params.salome_parameters.result_directory
152
+ # run the job
153
+ launcher = salome_proxy.getLauncher()
154
+ launcher.launchJob(self.job_id)
155
+ #save the job
156
+ job_string = launcher.dumpJob(self.job_id)
157
+ jobDumpPath = os.path.join(tmp_workdir, PyStudy.JOB_DUMP_NAME)
158
+ with open(jobDumpPath, "w") as f:
159
+ f.write(job_string)
160
+
161
+ def getResult(self):
162
+ """
163
+ Try to get the result file and if it was possible the results are loaded in
164
+ the sample.
165
+ An exception may be thrown if it was not possible to get the file.
166
+ Return a StudyResult object.
167
+ """
168
+ self.global_result = StudyResult()
169
+ if self.job_id < 0 :
170
+ raise StudyUseException("Cannot get the results if the job is not created!")
171
+ launcher = salome_proxy.getLauncher()
172
+ state = launcher.getJobState(self.job_id)
173
+ tmp_workdir = self.params.salome_parameters.result_directory
174
+ searchResults = False
175
+ errorIfNoResults = False
176
+ errorMessage = ""
177
+ if state == "CREATED" :
178
+ raise StudyUseException("Cannot get the results if the job is not launched!")
179
+ elif state == "QUEUED" or state == "IN_PROCESS":
180
+ # no results available at this point. Try again later! Not an error.
181
+ searchResults = False
182
+ elif state == "FINISHED" :
183
+ # verify the return code of the execution
184
+ searchResults = True
185
+ if(launcher.getJobWorkFile(self.job_id, "logs/exit_code.log", tmp_workdir)):
186
+ exit_code_file = os.path.join(tmp_workdir, "exit_code.log")
187
+ exit_code = ""
188
+ if os.path.isfile(exit_code_file):
189
+ with open(exit_code_file) as myfile:
190
+ exit_code = myfile.read()
191
+ exit_code = exit_code.strip()
192
+ self.global_result.exit_code = exit_code
193
+ if exit_code == "0" :
194
+ errorIfNoResults = True # we expect to have full results
195
+ else:
196
+ errorMessage = "An error occured during the execution of the job."
197
+ else:
198
+ errorMessage = "Failed to get the exit code of the job."
199
+
200
+ elif state == "RUNNING" or state == "PAUSED" or state == "ERROR" :
201
+ # partial results may be available
202
+ searchResults = True
203
+ elif state == "FAILED":
204
+ # We may have some partial results because the job could have been
205
+ # canceled or stoped by timeout.
206
+ searchResults = True
207
+ errorMessage = "Job execution failed!"
208
+ if searchResults :
209
+ if 1 == launcher.getJobWorkFile(self.job_id,
210
+ self.sampleManager.getResultFileName(),
211
+ tmp_workdir):
212
+ try:
213
+ res = self.sampleManager.loadResult(self.sample, tmp_workdir)
214
+ self.global_result.result = res
215
+ except Exception as err:
216
+ if errorIfNoResults:
217
+ raise err
218
+ elif errorIfNoResults:
219
+ errorMessage = "The job is finished but we cannot get the result file!"
220
+ if len(errorMessage) > 0 :
221
+ warningMessage = """
222
+ The results you get may be incomplete or incorrect.
223
+ For further details, see {}/logs directory on {}.""".format(
224
+ self.params.salome_parameters.work_directory,
225
+ self.params.salome_parameters.resource_required.name)
226
+ errorMessage += warningMessage
227
+ self.global_result.error_message = errorMessage
228
+ raise StudyRunException(errorMessage)
229
+ return self.global_result
230
+
231
+ def resultAvailable(self):
232
+ """
233
+ Try to get the result and return True in case of success with no exception.
234
+ In case of success the results are loaded in the sample.
235
+ """
236
+ resultFound = False
237
+ try:
238
+ self.getResult()
239
+ resultFound = True
240
+ except:
241
+ resultFound = False
242
+ return resultFound
243
+
244
+ def getJobState(self):
245
+ if self.job_id < 0:
246
+ return "NOT_CREATED"
247
+ launcher = salome_proxy.getLauncher()
248
+ return launcher.getJobState(self.job_id)
249
+
250
+ def getProgress(self):
251
+ if self.job_id < 0:
252
+ return 0.0
253
+ state = self.getJobState()
254
+ if state == "CREATED" or state == "QUEUED" :
255
+ return 0.0
256
+ if not self.resultAvailable():
257
+ return 0.0
258
+ return self.sample.progressRate()
259
+
260
+ def dump(self):
261
+ if self.job_id < 0 :
262
+ raise StudyUseException("Cannot dump the job if it is not created!")
263
+ launcher = salome_proxy.getLauncher()
264
+ return launcher.dumpJob(self.job_id)
265
+
266
+ def wait(self, sleep_delay=10):
267
+ """ Wait for the end of the job """
268
+ launcher = salome_proxy.getLauncher()
269
+ job_id = self.job_id
270
+ jobState = launcher.getJobState(job_id)
271
+ import time
272
+ while jobState=="QUEUED" or jobState=="IN_PROCESS" or jobState=="RUNNING" :
273
+ time.sleep(sleep_delay)
274
+ jobState = launcher.getJobState(job_id)
275
+
276
+ def _prepareDirectoryForLaunch(self, result_directory, script):
277
+ """
278
+ result_directory : path to a result working directory.
279
+ script : script / pyscript type
280
+ return:
281
+ yacs_schema_path: path to the yacs schema (xml file).
282
+ extra_in_files: list of files to add to salome_parameters.in_files
283
+ """
284
+ if not os.path.exists(result_directory):
285
+ os.makedirs(result_directory)
286
+ # export sample to result_directory
287
+ inputFiles = self.sampleManager.prepareRun(self.sample, result_directory)
288
+
289
+ # export nbbranches
290
+ dicconfig = {}
291
+ dicconfig["nbbranches"] = self.params.nb_branches
292
+ dicconfig["studymodule"] = "idefixstudy"
293
+ dicconfig["sampleIterator"] = self.sampleManager.getModuleName()
294
+ configpath = configuration.exportConfig(dicconfig, result_directory)
295
+ studypath = os.path.join(result_directory, "idefixstudy.py")
296
+ with open(studypath, "w") as f:
297
+ f.write(script.script)
298
+ schema_path, extra_files = self.schemaBuilder.buildSchema(result_directory)
299
+
300
+ extra_files.extend([configpath, studypath])
301
+ extra_files.extend(inputFiles)
302
+ return schema_path, extra_files
303
+
304
+ def _check(self, script, sample):
305
+ "Raise StudyUseException if the sample does not match with the sample."
306
+ script_params = script.getInputNames()
307
+ sample_inputs = sample.getInputNames()
308
+ if len(script_params) < 1:
309
+ raise StudyUseException("The study function should have at least one parameter. None found.")
310
+ if len(script_params) != len(sample_inputs):
311
+ m="The study function should have the same number of parameters as the input variables in the sample ({} != {})."
312
+ raise StudyUseException(m.format(len(script_params), len(sample_inputs)))
313
+ for nm in script_params:
314
+ if nm not in sample_inputs:
315
+ raise StudyUseException("Parameter {} not found in the sample.".format(nm))
316
+
317
+ ### Deprecated!!!!
318
+ def loadJobString(result_directory):
319
+ """
320
+ Return the jobString saved by the dumpJob function into a directory.
321
+ Use dumpJob for saving the string.
322
+ """
323
+ jobDumpPath = os.path.join(result_directory, PyStudy.JOB_DUMP_NAME)
324
+ with open(jobDumpPath, "r") as f:
325
+ job_string = f.read()
326
+ return job_string
327
+
pydefx/salome_proxy.py ADDED
@@ -0,0 +1,88 @@
1
+ from salome.kernel import salome
2
+ from salome.kernel import pylauncher
3
+ import os
4
+ from salome.kernel.SALOME import SALOME_Exception
5
+ from .studyexception import StudyRunException
6
+
7
+ _use_salome_servers = None
8
+
9
+ def _default():
10
+ global _use_salome_servers
11
+ if _use_salome_servers is None:
12
+ try:
13
+ salome.salome_init()
14
+ _use_salome_servers = True
15
+ except RuntimeError:
16
+ _use_salome_servers = False
17
+
18
+ def forceSalomeServers():
19
+ global _use_salome_servers
20
+ if not _use_salome_servers:
21
+ salome.salome_init()
22
+ _use_salome_servers = True
23
+
24
+ def forceNoSalomeServers():
25
+ global _use_salome_servers
26
+ _use_salome_servers = False
27
+
28
+ def createSalomeParameters():
29
+ from salome.kernel.LifeCycleCORBA import JobParameters, ResourceParameters
30
+ _default()
31
+ if _use_salome_servers:
32
+ result = JobParameters()
33
+ result.resource_required = ResourceParameters()
34
+ else:
35
+ result = pylauncher.JobParameters_cpp()
36
+ return result
37
+
38
+ _resourceManager = None
39
+ def getResourcesManager():
40
+ global _resourceManager
41
+ _default()
42
+ if _resourceManager is None:
43
+ if _use_salome_servers:
44
+ _resourceManager = salome.lcc.getResourcesManager()
45
+ else:
46
+ catalog_path = os.environ.get("USER_CATALOG_RESOURCES_FILE", "")
47
+ if not os.path.isfile(catalog_path):
48
+ salome_path = os.environ.get("ROOT_SALOME_INSTALL", "")
49
+ catalog_path = os.path.join(salome_path, "CatalogResources.xml")
50
+ if not os.path.isfile(catalog_path):
51
+ catalog_path = ""
52
+ _resourceManager = pylauncher.ResourcesManager_cpp(catalog_path)
53
+ return _resourceManager
54
+
55
+ def format_salome_exception(f):
56
+ """
57
+ Get a more readable format of SALOME_Exception.
58
+ :param f: function that could raise SALOME_Exception.
59
+ """
60
+ def wrap_func(*args, **kwargs):
61
+ try:
62
+ return f(*args, **kwargs)
63
+ except SALOME_Exception as ex:
64
+ raise StudyRunException(ex.args[0].text)
65
+ return wrap_func
66
+
67
+ class LauncherWrap:
68
+ def __init__(self, launcher):
69
+ self._launcher = launcher
70
+
71
+ def __getattr__(self, name):
72
+ attr = getattr(self._launcher, name)
73
+ if callable(attr):
74
+ return format_salome_exception(attr)
75
+ else:
76
+ return attr
77
+
78
+ _launcher = None
79
+ def getLauncher():
80
+ global _launcher
81
+ _default()
82
+ if _launcher is None:
83
+ if _use_salome_servers:
84
+ _launcher = LauncherWrap(salome.naming_service.Resolve('/SalomeLauncher'))
85
+ else:
86
+ _launcher = pylauncher.Launcher_cpp()
87
+ _launcher.SetResourcesManager(getResourcesManager())
88
+ return _launcher