Salome HOME
activate pytel
[modules/hydrosolver.git] / src / HYDROGUI / HYDROSOLVERGUI.py
1 #  Copyright (C) 2012-2013 EDF
2 #
3 #  This file is part of SALOME HYDRO module.
4 #
5 #  SALOME HYDRO module is free software: you can redistribute it and/or modify
6 #  it under the terms of the GNU General Public License as published by
7 #  the Free Software Foundation, either version 3 of the License, or
8 #  (at your option) any later version.
9 #
10 #  SALOME HYDRO module is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU General Public License for more details.
14 #
15 #  You should have received a copy of the GNU General Public License
16 #  along with SALOME HYDRO module.  If not, see <http://www.gnu.org/licenses/>.
17
18 import sys
19 import os
20 import traceback
21 import logging
22
23 from PyQt5.QtWidgets import QMessageBox, QApplication, QDialog
24
25 import salome
26 import SALOME
27 import SalomePyQt
28 sgPyQt = SalomePyQt.SalomePyQt()
29 from salome.kernel.studyedit import getStudyEditor
30 from salome.kernel.logger import Logger
31 from salome.kernel import termcolor
32 logger = Logger("HYDROGUI", color = termcolor.BLUE)
33 #logger.setLevel(logging.ERROR)
34
35 import HYDROSOLVER_ORB
36 from salome.hydro.interpolz_gui import InterpolzDlg
37 from salome.hydro.gui_utils import HSGUIException, wait_cursor, get_and_check_selected_file_path
38 import salome.hydro.study as hydro_study
39 from salome.hydro.mascaret.eficas.appli import EficasForMascaretAppli
40 from salome.hydro.telemac2d.eficas.appli import EficasForTelemac2DAppli
41 from salome.hydro.coupling1d2d.eficas.appli import EficasForCoupling1D2DAppli
42 from salome.hydro.pytel.gui import create_case_pytel,  run_selected_case_pytel, edit_selected_case_pytel,  generate_job_for_selected_case_pytel
43 from salome.hydro.boundary_conditions.eficas.appli import EficasForBoundaryConditionsAppli
44
45 import BndConditionsDialog
46
47 ################################################
48 # GUI context class
49 # Used to store actions, menus, toolbars, etc...
50 ################################################
51
52 class GUIcontext:
53     # menus/toolbars/actions IDs
54     HYDRO_MENU_ID = 90
55     CREATE_MASCARET_CASE_ID = 941
56     RUN_MASCARET_ID = 942
57     EDIT_MASCARET_CASE_ID = 943
58     SHOW_LOG_ID = 944
59     CREATE_TELEMAC2D_CASE_ID = 945
60     RUN_TELEMAC2D_ID = 946
61     EDIT_TELEMAC2D_CASE_ID = 947
62     CREATE_COUPLING1D2D_CASE_ID = 948
63     EDIT_COUPLING1D2D_CASE_ID = 949
64     OPEN_SCHEMA_IN_YACS_ID = 950
65     CREATE_PYTEL_CASE_ID = 951
66     RUN_PYTEL_ID = 952
67     EDIT_PYTEL_CASE_ID = 953
68     GENERATE_JOB = 954
69     DEFINE_BOUNDARY_CONDITIONS_ID = 955
70     EDIT_BOUNDARY_CONDITIONS_FILE_ID = 956
71     GENERATE_INTERPOLZ_PY_ID = 957
72
73     def __init__( self ):
74         # create top-level menu
75         mid = sgPyQt.createMenu( "Hydro", -1, GUIcontext.HYDRO_MENU_ID, sgPyQt.defaultMenuGroup() )
76         # create toolbar
77         tid = sgPyQt.createTool( "Hydro" )
78         # create actions and fill menu and toolbar with actions
79         a = sgPyQt.createAction( GUIcontext.DEFINE_BOUNDARY_CONDITIONS_ID,
80                                  "Define boundary conditions", "Define boundary conditions",
81                                  "Define the boundary conditions for Telemac",
82                                  "define_boundary_conditions.png" )
83         sgPyQt.createMenu( a, mid )
84         sgPyQt.createTool( a, tid )
85
86         a = sgPyQt.createAction( GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID,
87                                  "Edit boundary conditions file", "Edit boundary conditions file",
88                                  "Create/edit the boundary conditions file for Telemac",
89                                  "edit_boundary_conditions_file.png" )
90         sgPyQt.createMenu( a, mid )
91         sgPyQt.createTool( a, tid )
92
93         a = sgPyQt.createSeparator()
94         sgPyQt.createMenu( a, mid )
95         sgPyQt.createTool( a, tid )
96
97         a = sgPyQt.createAction( GUIcontext.CREATE_MASCARET_CASE_ID,
98                                  "Create Mascaret case", "Create Mascaret case",
99                                  "Create a new Mascaret case", "create_case1d.png" )
100         sgPyQt.createMenu( a, mid )
101         sgPyQt.createTool( a, tid )
102
103         a = sgPyQt.createAction( GUIcontext.CREATE_TELEMAC2D_CASE_ID,
104                                  "Create Telemac2D case", "Create Telemac2D case",
105                                  "Create a new Telemac2D case", "create_case2d.png" )
106         sgPyQt.createMenu( a, mid )
107         sgPyQt.createTool( a, tid )
108
109         a = sgPyQt.createAction( GUIcontext.CREATE_COUPLING1D2D_CASE_ID,
110                                  "Create 1D / 2D coupling", "Create 1D / 2D coupling",
111                                  "Create a new 1D / 2D coupling", "create_case_couplage.png" )
112         sgPyQt.createMenu( a, mid )
113         sgPyQt.createTool( a, tid )
114
115         a = sgPyQt.createSeparator()
116         sgPyQt.createMenu( a, mid )
117         sgPyQt.createTool( a, tid )
118
119         a = sgPyQt.createAction( GUIcontext.CREATE_PYTEL_CASE_ID,
120                                 "Create case for Pytel execution", "Create case for Pytel execution",
121                                 "Create a new case for Pytel execution", "create_case_pytel.png" )
122
123         sgPyQt.createMenu( a, mid )
124         sgPyQt.createTool( a, tid )
125
126         a = sgPyQt.createSeparator()
127         sgPyQt.createMenu( a, mid )
128         sgPyQt.createTool( a, tid )
129
130         a = sgPyQt.createAction( GUIcontext.GENERATE_INTERPOLZ_PY_ID,
131                                  "Generate interpolz.py", "Generate interpolz.py",
132                                  "Generate interpolation script from the template", "generate_interpolz_py.png" )
133
134         sgPyQt.createMenu( a, mid )
135         sgPyQt.createTool( a, tid )
136
137         # the following action are used in context popup
138         sgPyQt.createAction( GUIcontext.RUN_MASCARET_ID, "Compute case", "Compute case",
139                              "Run Mascaret solver to compute the case" )
140         sgPyQt.createAction( GUIcontext.EDIT_MASCARET_CASE_ID, "Edit case", "Edit case",
141                              "Edit the selected Mascaret case" )
142         sgPyQt.createAction( GUIcontext.SHOW_LOG_ID, "Show log", "Show log",
143                              "Show the log for the selected variable" )
144
145         sgPyQt.createAction( GUIcontext.RUN_TELEMAC2D_ID, "Compute case", "Compute case",
146                              "Run Telemac2D solver to compute the case" )
147         sgPyQt.createAction( GUIcontext.EDIT_TELEMAC2D_CASE_ID, "Edit case", "Edit case",
148                              "Edit the selected Telemac2D case" )
149
150         sgPyQt.createAction( GUIcontext.EDIT_COUPLING1D2D_CASE_ID, "Edit coupling", "Edit coupling",
151                              "Edit the selected 1D / 2D coupling" )
152         sgPyQt.createAction( GUIcontext.OPEN_SCHEMA_IN_YACS_ID, "Open schema in YACS", "Open schema in YACS",
153                              "Open the selected 1D / 2D coupling schema in YACS" )
154
155         sgPyQt.createAction( GUIcontext.RUN_PYTEL_ID, "Compute case", "Compute case",
156                             "Run Pytel launcher to compute the case" )
157         sgPyQt.createAction( GUIcontext.EDIT_PYTEL_CASE_ID, "Edit case", "Edit case",
158                             "Edit the selected Pytel case" )
159         sgPyQt.createAction( GUIcontext.GENERATE_JOB, "Generate batch job", "Generate batch job",
160                              "Generate a batch job to run the selected case")
161
162 ################################################
163 # Global variables
164 ################################################
165
166 # study-to-context map
167 __study2context__   = {}
168 # current context
169 __current_context__ = None
170 # object counter
171 __objectid__ = 0
172
173 ################################################
174 # Internal methods
175 ################################################
176
177 ###
178 # get active study ID
179 ###
180 def _getStudyId():
181     return sgPyQt.getStudyId()
182
183 ###
184 # get active study
185 ###
186 def _getStudy():
187     studyId = _getStudyId()
188     study = getStudyManager().GetStudyByID( studyId )
189     return study
190
191 ###
192 # returns True if object has children
193 ###
194 def _hasChildren( sobj ):
195     if sobj:
196         study = _getStudy()
197         iter  = study.NewChildIterator( sobj )
198         while iter.More():
199             name = iter.Value().GetName()
200             if name:
201                 return True
202             iter.Next()
203             pass
204         pass
205     return False
206
207 ###
208 # get current GUI context
209 ###
210 def _getContext():
211     global __current_context__
212     return __current_context__
213
214 ###
215 # set and return current GUI context
216 # study ID is passed as parameter
217 ###
218 def _setContext( studyID ):
219     global __study2context__, __current_context__
220     if not __study2context__.has_key(studyID):
221         __study2context__[studyID] = GUIcontext()
222         pass
223     __current_context__ = __study2context__[studyID]
224     return __current_context__
225
226 ###
227 # increment object counter in the map
228 ###
229 def _incObjToMap( m, id ):
230     if id not in m: m[id] = 0
231     m[id] += 1
232     pass
233
234 ################################################
235 # Callback functions
236 ################################################
237
238 # called when module is activated
239 # returns True if activating is successfull and False otherwise
240 def activate():
241     ctx = _setContext( _getStudyId() )
242     return True
243
244 # called when module is deactivated
245 def deactivate():
246     pass
247
248 # called when active study is changed
249 # active study ID is passed as parameter
250 def activeStudyChanged( studyID ):
251     ctx = _setContext( _getStudyId() )
252     pass
253
254 # called when popup menu is invoked
255 # popup menu and menu context are passed as parameters
256 def createPopupMenu(popup, context):
257     ed = getStudyEditor()
258     _setContext(ed.studyId)
259     if salome.sg.SelectedCount() == 1:
260         # one object is selected
261         sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
262         selectedType = ed.getTypeId(sobj)
263         if selectedType == hydro_study.MASCARET_CASE_TYPE_ID:
264             popup.addAction(sgPyQt.action(GUIcontext.EDIT_MASCARET_CASE_ID))
265             popup.addAction(sgPyQt.action(GUIcontext.RUN_MASCARET_ID))
266         elif selectedType == hydro_study.TELEMAC2D_CASE_TYPE_ID:
267             popup.addAction(sgPyQt.action(GUIcontext.EDIT_TELEMAC2D_CASE_ID))
268             popup.addAction(sgPyQt.action(GUIcontext.RUN_TELEMAC2D_ID))
269         elif selectedType == hydro_study.COUPLING1D2D_CASE_TYPE_ID:
270             popup.addAction(sgPyQt.action(GUIcontext.EDIT_COUPLING1D2D_CASE_ID))
271             popup.addAction(sgPyQt.action(GUIcontext.OPEN_SCHEMA_IN_YACS_ID))
272         elif selectedType == hydro_study.LOG_TYPE_ID:
273             popup.addAction(sgPyQt.action(GUIcontext.SHOW_LOG_ID))
274         elif selectedType == hydro_study.PYTEL_CASE_TYPE_ID:
275            popup.addAction(sgPyQt.action(GUIcontext.EDIT_PYTEL_CASE_ID))
276            popup.addAction(sgPyQt.action(GUIcontext.RUN_PYTEL_ID))
277            popup.addAction(sgPyQt.action(GUIcontext.GENERATE_JOB))
278
279 # called when GUI action is activated
280 # action ID is passed as parameter
281 def OnGUIEvent( commandID ):
282     try:
283         dict_command[commandID]()
284     except HSGUIException, exc:
285         QMessageBox.critical(sgPyQt.getDesktop(),
286                                    QApplication.translate("OnGUIEvent", "Error"),
287                                    unicode(exc))
288     except:
289         logger.exception("Unhandled exception caught in HYDROSOLVER GUI")
290         msg = QApplication.translate("OnGUIEvent",
291             "Unhandled error happened in HYDROSOLVER module. Please report a bug on "
292             '<a href="https://forge-pleiade.der.edf.fr/projects/salome-rex/issues">SALOME bugtracker</a>, '
293             'category "HYDROSOLVER", describing how you got there and including the following traceback:')
294         msgBox = QMessageBox(QMessageBox.Critical,
295                                    QApplication.translate("OnGUIEvent", "Error"),
296                                    msg,
297                                    parent = sgPyQt.getDesktop())
298         msgBox.setDetailedText(traceback.format_exc())
299         msgBox.exec_()
300
301 ################################################
302 # GUI actions implementation
303 ################################################
304
305 # --------------------------------------------------------------------------------------- #
306 # Dialog box for text display (deprecated, it was only used in the calcium coupling test) #
307 # --------------------------------------------------------------------------------------- #
308 """
309 from TextDisplayDialog import Ui_TextDisplayDialog
310
311 class MyTextDisplayDialog(Ui_TextDisplayDialog, QDialog):
312
313     def __init__(self, parent = None, modal = 0):
314         QDialog.__init__(self, parent)
315         Ui_TextDisplayDialog.__init__(self)
316         self.setupUi(self)
317         (self.closeButton.clicked.connect(self.close)
318
319     def set_log(self, log):
320         self.contentTextEdit.setPlainText(log)
321         self.setWindowTitle("Coupling log")
322 """
323
324 ###
325 # Open Eficas for Mascaret to create a new case
326 ###
327 def create_mascaret_case():
328     EficasForMascaretAppli()
329
330 ###
331 # Open Eficas for Mascaret to edit the selected case
332 ###
333 def edit_mascaret_case():
334     EficasForMascaretAppli(get_and_check_selected_file_path())
335
336 # Run Mascaret on selected case
337 def run_mascaret():
338     try:
339         with wait_cursor:
340             ed = hydro_study.HydroStudyEditor()
341             sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
342             (file_list, lig_file, input_vars, output_vars) = ed.get_mascaret_params_from_case(sobj)
343             var_names = [var["NOM"].strip() for var in output_vars]
344             mascaret_vars = [var["VARIABLE_MASCARET"].strip() for var in output_vars]
345             engine = salome.lcc.FindOrLoadComponent("FactoryServer", "MASCARET")
346             logger.debug("Calling MASCARET.Compute(%s, %s, %s)" %
347                          (file_list, lig_file, mascaret_vars))
348             output_values = engine.Compute(file_list, lig_file, mascaret_vars)
349             ed.add_results_to_mascaret_case(sobj, var_names, output_values)
350             salome.sg.updateObjBrowser( 0 )
351     except SALOME.SALOME_Exception, exc:
352         salome.sg.updateObjBrowser( 0 )
353         msg = unicode(QApplication.translate("run_mascaret",
354                       "An error happened while trying to run Mascaret:"))
355         msg += "\n" + exc.details.text
356         raise HSGUIException(msg)
357
358 # Display selected log (deprecated, it was only used in the calcium coupling test)
359 def show_log():
360     ed = hydro_study.HydroStudyEditor()
361     sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
362     if sobj is not None:
363         (found, attr) = getStudyEditor().builder.FindAttribute(
364                                                 sobj, "AttributeParameter")
365         log = attr.GetString("log")
366         dialog = MyTextDisplayDialog(sgPyQt.getDesktop())
367         dialog.set_log(log)
368         dialog.show()
369
370 ###
371 # Open Eficas for Telemac2D to create a new case
372 ###
373 def create_telemac2d_case():
374     EficasForTelemac2DAppli()
375
376 ###
377 # Open Eficas for Telemac2D to edit the selected case
378 ###
379 def edit_telemac2d_case():
380     EficasForTelemac2DAppli(get_and_check_selected_file_path())
381
382 # Run Telemac2D on selected case
383 def run_telemac2d():
384     engine = None
385     try:
386         with wait_cursor:
387             ed = hydro_study.HydroStudyEditor()
388             entry = salome.sg.getSelected(0)
389             engine = salome.lcc.FindOrLoadComponent("Telemac2DContainer", "TELEMAC2D")
390             engine.Compute(ed.editor.studyId, entry)
391             # Stop container after execution so that we can use another fortran user file in the next run
392             engine.GetContainerRef().Shutdown()
393     except SALOME.SALOME_Exception, exc:
394         if engine is not None:
395             engine.GetContainerRef().Shutdown()
396         msg = unicode(QApplication.translate("run_telemac2d",
397                       "An error happened while trying to run Telemac2D:"))
398         msg += "\n" + exc.details.text
399         raise HSGUIException(msg)
400     except Exception, exc:
401         logger.exception("An error happened in the computation (Telemac2D probably crashed).")
402         try:
403             engine.GetContainerRef().Shutdown()
404         except:
405             pass
406         msg = unicode(QApplication.translate("run_telemac2d",
407                       "An error happened in the computation (Telemac2D probably crashed, "
408                       "see logs and listing files for more details):"))
409         msg += "\n" + unicode(exc)
410         raise HSGUIException(msg)
411
412 ###
413 # Open Eficas for 1D / 2D Coupling to create a new coupling case
414 ###
415 def create_coupling1d2d_case():
416     EficasForCoupling1D2DAppli()
417
418 ###
419 # Open Eficas for 1D / 2D Coupling to edit the selected coupling case
420 ###
421 def edit_coupling1d2d_case():
422     EficasForCoupling1D2DAppli(get_and_check_selected_file_path())
423
424 def open_schema_in_yacs():
425     ed = getStudyEditor()
426     sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
427     filename = sobj.GetComment()
428     if filename.endswith(".comm"):
429         filename = filename[:-5] + ".xml"
430     if not os.path.isfile(filename):
431         raise HSGUIException(QApplication.translate("open_schema_in_yacs",
432                                   "Schema file %1 does not exist").arg(filename))
433     yg = salome.ImportComponentGUI("YACS")
434     yg.loadSchema(filename)
435
436 ###
437 # Open Eficas for boundary conditions definition
438 ###
439 def define_boundary_conditions():
440     EficasForBoundaryConditionsAppli()
441
442 ###
443 # Open dialog for boundary conditions edition
444 ###
445 def edit_boundary_conditions_file():
446     desktop = sgPyQt.getDesktop()
447     dlg = BndConditionsDialog.BoundaryConditionsDialog(desktop)
448     dlg.exec_()
449
450 ###
451 # Open dialog for interpolz.py script generation
452 ###
453 def generate_interpolz_py():
454     desktop = sgPyQt.getDesktop()
455     dlg = InterpolzDlg(desktop)
456     dlg.show()
457
458 ###
459 # Commands dictionary
460 ###
461 dict_command = {
462     GUIcontext.CREATE_MASCARET_CASE_ID: create_mascaret_case,
463     GUIcontext.RUN_MASCARET_ID: run_mascaret,
464     GUIcontext.EDIT_MASCARET_CASE_ID: edit_mascaret_case,
465     GUIcontext.SHOW_LOG_ID: show_log,
466     GUIcontext.CREATE_TELEMAC2D_CASE_ID: create_telemac2d_case,
467     GUIcontext.RUN_TELEMAC2D_ID: run_telemac2d,
468     GUIcontext.EDIT_TELEMAC2D_CASE_ID: edit_telemac2d_case,
469     GUIcontext.CREATE_COUPLING1D2D_CASE_ID: create_coupling1d2d_case,
470     GUIcontext.EDIT_COUPLING1D2D_CASE_ID: edit_coupling1d2d_case,
471     GUIcontext.OPEN_SCHEMA_IN_YACS_ID: open_schema_in_yacs,
472     GUIcontext.CREATE_PYTEL_CASE_ID: create_case_pytel,
473     GUIcontext.RUN_PYTEL_ID: run_selected_case_pytel,
474     GUIcontext.EDIT_PYTEL_CASE_ID: edit_selected_case_pytel,
475     GUIcontext.GENERATE_JOB: generate_job_for_selected_case_pytel,
476     GUIcontext.DEFINE_BOUNDARY_CONDITIONS_ID: define_boundary_conditions,
477     GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID: edit_boundary_conditions_file,
478     GUIcontext.GENERATE_INTERPOLZ_PY_ID: generate_interpolz_py,
479     }