Salome HOME
Adding generation of YACS scheme
[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 from eficasSalome import runEficas
45
46 import BndConditionsDialog
47
48 ################################################
49 # GUI context class
50 # Used to store actions, menus, toolbars, etc...
51 ################################################
52
53 class GUIcontext:
54     # menus/toolbars/actions IDs
55     HYDRO_MENU_ID = 90
56     CREATE_MASCARET_CASE_ID = 941
57     RUN_MASCARET_ID = 942
58     EDIT_MASCARET_CASE_ID = 943
59     SHOW_LOG_ID = 944
60     CREATE_TELEMAC2D_CASE_ID = 945
61     RUN_TELEMAC2D_ID = 946
62     EDIT_TELEMAC2D_CASE_ID = 947
63     CREATE_COUPLING1D2D_CASE_ID = 948
64     EDIT_COUPLING1D2D_CASE_ID = 949
65     OPEN_SCHEMA_IN_YACS_ID = 950
66     CREATE_PYTEL_CASE_ID = 951
67     RUN_PYTEL_ID = 952
68     EDIT_PYTEL_CASE_ID = 953
69     GENERATE_JOB = 954
70     #DEFINE_BOUNDARY_CONDITIONS_ID = 955
71     DEFINE_CAS_FILE = 955
72     EDIT_BOUNDARY_CONDITIONS_FILE_ID = 956
73     GENERATE_INTERPOLZ_PY_ID = 957
74     GEN_TELEMAC2D_PYTHON_ID = 958
75     GEN_TELEMAC2D_YACS_ID = 959
76
77     def __init__( self ):
78         # create top-level menu
79         mid = sgPyQt.createMenu( "Hydro", -1, GUIcontext.HYDRO_MENU_ID, sgPyQt.defaultMenuGroup() )
80         # create toolbar
81         tid = sgPyQt.createTool( "Hydro" )
82         # create actions and fill menu and toolbar with actions
83         a = sgPyQt.createAction( GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID,
84                                  "Edit boundary conditions file", "Edit boundary conditions file",
85                                  "Create/edit the boundary conditions file for Telemac",
86                                  "edit_boundary_conditions_file.png" )
87         sgPyQt.createMenu( a, mid )
88         sgPyQt.createTool( a, tid )
89
90
91         a = sgPyQt.createAction( GUIcontext.CREATE_MASCARET_CASE_ID,
92                                  "Create Mascaret case", "Create Mascaret case",
93                                  "Create a new Mascaret case", "create_case1d.png" )
94         sgPyQt.createMenu( a, mid )
95         sgPyQt.createTool( a, tid )
96
97         a = sgPyQt.createAction( GUIcontext.CREATE_TELEMAC2D_CASE_ID,
98                                  "Create Telemac2D case", "Create Telemac2D case",
99                                  "Create a new Telemac2D case", "create_case2d.png" )
100         sgPyQt.createMenu( a, mid )
101         sgPyQt.createTool( a, tid )
102
103         a = sgPyQt.createAction( GUIcontext.CREATE_COUPLING1D2D_CASE_ID,
104                                  "Create 1D / 2D coupling", "Create 1D / 2D coupling",
105                                  "Create a new 1D / 2D coupling", "create_case_couplage.png" )
106         sgPyQt.createMenu( a, mid )
107         sgPyQt.createTool( a, tid )
108
109         a = sgPyQt.createSeparator()
110
111         a = sgPyQt.createAction( GUIcontext.CREATE_PYTEL_CASE_ID,
112                                 "Create case for Pytel execution", "Create case for Pytel execution",
113                                 "Create a new case for Pytel execution", "create_case_pytel.png" )
114
115         sgPyQt.createMenu( a, mid )
116         sgPyQt.createTool( a, tid )
117
118         a = sgPyQt.createAction( GUIcontext.EDIT_PYTEL_CASE_ID,
119                                 "Edit case for Pytel execution", "Edit case for Pytel execution",
120                                 "Edit a new case for Pytel execution", "edit_case_pytel.png" )
121
122         sgPyQt.createMenu( a, mid )
123         sgPyQt.createTool( a, tid )
124
125         a = sgPyQt.createSeparator()
126
127         a = sgPyQt.createAction( GUIcontext.GENERATE_INTERPOLZ_PY_ID,
128                                  "Generate interpolz.py", "Generate interpolz.py",
129                                  "Generate interpolation script from the template", "generate_interpolz_py.png" )
130
131         sgPyQt.createMenu( a, mid )
132         sgPyQt.createTool( a, tid )
133
134         a = sgPyQt.createSeparator()
135         a = sgPyQt.createAction( GUIcontext.DEFINE_CAS_FILE,
136                                  "Edit cas file", "Edit cas file",
137                                  "Create/edit a .cas file for Telemac",
138                                  "define_cas_file.png" )
139         sgPyQt.createMenu( a, mid )
140         sgPyQt.createTool( a, tid )
141
142
143         # the following action are used in context popup
144         sgPyQt.createAction( GUIcontext.RUN_MASCARET_ID, "Compute case", "Compute case",
145                              "Run Mascaret solver to compute the case" )
146         sgPyQt.createAction( GUIcontext.EDIT_MASCARET_CASE_ID, "Edit case", "Edit case",
147                              "Edit the selected Mascaret case" )
148         sgPyQt.createAction( GUIcontext.SHOW_LOG_ID, "Show log", "Show log",
149                              "Show the log for the selected variable" )
150
151         sgPyQt.createAction( GUIcontext.GEN_TELEMAC2D_PYTHON_ID, "Generate Python script", 
152                              "Generate Python script",
153                              "Generate a Python script from the eficas date" )
154         sgPyQt.createAction( GUIcontext.GEN_TELEMAC2D_YACS_ID, "Generate YACS script", 
155                              "Generate YACS script",
156                              "Generate a YACS script from the eficas date" )
157         sgPyQt.createAction( GUIcontext.RUN_TELEMAC2D_ID, "Compute case", "Compute case",
158                              "Run Telemac2D solver to compute the case" )
159         sgPyQt.createAction( GUIcontext.EDIT_TELEMAC2D_CASE_ID, "Edit case", "Edit case",
160                              "Edit the selected Telemac2D case" )
161
162         sgPyQt.createAction( GUIcontext.EDIT_COUPLING1D2D_CASE_ID, "Edit coupling", "Edit coupling",
163                              "Edit the selected 1D / 2D coupling" )
164         sgPyQt.createAction( GUIcontext.OPEN_SCHEMA_IN_YACS_ID, "Open schema in YACS", "Open schema in YACS",
165                              "Open the selected 1D / 2D coupling schema in YACS" )
166
167         sgPyQt.createAction( GUIcontext.RUN_PYTEL_ID, "Compute case", "Compute case",
168                             "Run Pytel launcher to compute the case" )
169         sgPyQt.createAction( GUIcontext.EDIT_PYTEL_CASE_ID, "Edit case", "Edit case",
170                             "Edit the selected Pytel case" )
171         sgPyQt.createAction( GUIcontext.GENERATE_JOB, "Generate batch job", "Generate batch job",
172                              "Generate a batch job to run the selected case")
173
174         sgPyQt.createAction( GUIcontext.DEFINE_CAS_FILE, "Edit .cas file", "Edit .cas file",
175                              "Edit .cas file")
176
177
178 ################################################
179 # Global variables
180 ################################################
181
182 # study-to-context map
183 __study2context__   = {}
184 # current context
185 __current_context__ = None
186 # object counter
187 __objectid__ = 0
188
189 ################################################
190 # Internal methods
191 ################################################
192
193 ###
194 # get active study ID
195 ###
196 def _getStudyId():
197     return sgPyQt.getStudyId()
198
199 ###
200 # get active study
201 ###
202 def _getStudy():
203     studyId = _getStudyId()
204     study = getStudyManager().GetStudyByID( studyId )
205     return study
206
207 ###
208 # returns True if object has children
209 ###
210 def _hasChildren( sobj ):
211     if sobj:
212         study = _getStudy()
213         iter  = study.NewChildIterator( sobj )
214         while iter.More():
215             name = iter.Value().GetName()
216             if name:
217                 return True
218             iter.Next()
219             pass
220         pass
221     return False
222
223 ###
224 # get current GUI context
225 ###
226 def _getContext():
227     global __current_context__
228     return __current_context__
229
230 ###
231 # set and return current GUI context
232 # study ID is passed as parameter
233 ###
234 def _setContext( studyID ):
235     global __study2context__, __current_context__
236     if not __study2context__.has_key(studyID):
237         __study2context__[studyID] = GUIcontext()
238         pass
239     __current_context__ = __study2context__[studyID]
240     return __current_context__
241
242 ###
243 # increment object counter in the map
244 ###
245 def _incObjToMap( m, id ):
246     if id not in m: m[id] = 0
247     m[id] += 1
248     pass
249
250 ################################################
251 # Callback functions
252 ################################################
253
254 # called when module is activated
255 # returns True if activating is successfull and False otherwise
256 def activate():
257     ctx = _setContext( _getStudyId() )
258     return True
259
260 # called when module is deactivated
261 def deactivate():
262     pass
263
264 # called when active study is changed
265 # active study ID is passed as parameter
266 def activeStudyChanged( studyID ):
267     ctx = _setContext( _getStudyId() )
268     pass
269
270 # called when popup menu is invoked
271 # popup menu and menu context are passed as parameters
272 def createPopupMenu(popup, context):
273     ed = getStudyEditor()
274     _setContext(ed.studyId)
275     if salome.sg.SelectedCount() == 1:
276         # one object is selected
277         sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
278         selectedType = ed.getTypeId(sobj)
279         if selectedType == hydro_study.MASCARET_CASE_TYPE_ID:
280             popup.addAction(sgPyQt.action(GUIcontext.EDIT_MASCARET_CASE_ID))
281             popup.addAction(sgPyQt.action(GUIcontext.RUN_MASCARET_ID))
282         elif selectedType == hydro_study.TELEMAC2D_CASE_TYPE_ID:
283             popup.addAction(sgPyQt.action(GUIcontext.EDIT_TELEMAC2D_CASE_ID))
284             popup.addAction(sgPyQt.action(GUIcontext.RUN_TELEMAC2D_ID))
285             popup.addAction(sgPyQt.action(GUIcontext.GEN_TELEMAC2D_PYTHON_ID))
286             popup.addAction(sgPyQt.action(GUIcontext.GEN_TELEMAC2D_YACS_ID))
287         elif selectedType == hydro_study.COUPLING1D2D_CASE_TYPE_ID:
288             popup.addAction(sgPyQt.action(GUIcontext.EDIT_COUPLING1D2D_CASE_ID))
289             popup.addAction(sgPyQt.action(GUIcontext.OPEN_SCHEMA_IN_YACS_ID))
290         elif selectedType == hydro_study.LOG_TYPE_ID:
291             popup.addAction(sgPyQt.action(GUIcontext.SHOW_LOG_ID))
292         elif selectedType == hydro_study.PYTEL_CASE_TYPE_ID:
293            popup.addAction(sgPyQt.action(GUIcontext.EDIT_PYTEL_CASE_ID))
294            popup.addAction(sgPyQt.action(GUIcontext.RUN_PYTEL_ID))
295            popup.addAction(sgPyQt.action(GUIcontext.GENERATE_JOB))
296
297 # called when GUI action is activated
298 # action ID is passed as parameter
299 def OnGUIEvent( commandID ):
300     try:
301         dict_command[commandID]()
302     except HSGUIException, exc:
303         QMessageBox.critical(sgPyQt.getDesktop(),
304                                    QApplication.translate("OnGUIEvent", "Error"),
305                                    unicode(exc))
306     except:
307         logger.exception("Unhandled exception caught in HYDROSOLVER GUI")
308         msg = QApplication.translate("OnGUIEvent",
309             "Unhandled error happened in HYDROSOLVER module. Please report a bug on "
310             '<a href="https://forge-pleiade.der.edf.fr/projects/salome-rex/issues">SALOME bugtracker</a>, '
311             'category "HYDROSOLVER", describing how you got there and including the following traceback:')
312         msgBox = QMessageBox(QMessageBox.Critical,
313                                    QApplication.translate("OnGUIEvent", "Error"),
314                                    msg,
315                                    parent = sgPyQt.getDesktop())
316         msgBox.setDetailedText(traceback.format_exc())
317         msgBox.exec_()
318
319 ################################################
320 # GUI actions implementation
321 ################################################
322
323 # --------------------------------------------------------------------------------------- #
324 # Dialog box for text display (deprecated, it was only used in the calcium coupling test) #
325 # --------------------------------------------------------------------------------------- #
326 """
327 from TextDisplayDialog import Ui_TextDisplayDialog
328
329 class MyTextDisplayDialog(Ui_TextDisplayDialog, QDialog):
330
331     def __init__(self, parent = None, modal = 0):
332         QDialog.__init__(self, parent)
333         Ui_TextDisplayDialog.__init__(self)
334         self.setupUi(self)
335         (self.closeButton.clicked.connect(self.close)
336
337     def set_log(self, log):
338         self.contentTextEdit.setPlainText(log)
339         self.setWindowTitle("Coupling log")
340 """
341
342 ###
343 # Open Eficas for Mascaret to create a new case
344 ###
345 def create_mascaret_case():
346     EficasForMascaretAppli()
347
348 ###
349 # Open Eficas for Mascaret to edit the selected case
350 ###
351 def edit_mascaret_case():
352     EficasForMascaretAppli(get_and_check_selected_file_path())
353
354 # Run Mascaret on selected case
355 def run_mascaret():
356     try:
357         with wait_cursor:
358             ed = hydro_study.HydroStudyEditor()
359             sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
360             (file_list, lig_file, input_vars, output_vars) = ed.get_mascaret_params_from_case(sobj)
361             var_names = [var["NOM"].strip() for var in output_vars]
362             mascaret_vars = [var["VARIABLE_MASCARET"].strip() for var in output_vars]
363             engine = salome.lcc.FindOrLoadComponent("FactoryServer", "MASCARET")
364             logger.debug("Calling MASCARET.Compute(%s, %s, %s)" %
365                          (file_list, lig_file, mascaret_vars))
366             output_values = engine.Compute(file_list, lig_file, mascaret_vars)
367             ed.add_results_to_mascaret_case(sobj, var_names, output_values)
368             salome.sg.updateObjBrowser( 0 )
369     except SALOME.SALOME_Exception, exc:
370         salome.sg.updateObjBrowser( 0 )
371         msg = unicode(QApplication.translate("run_mascaret",
372                       "An error happened while trying to run Mascaret:"))
373         msg += "\n" + exc.details.text
374         raise HSGUIException(msg)
375
376 # Display selected log (deprecated, it was only used in the calcium coupling test)
377 def show_log():
378     ed = hydro_study.HydroStudyEditor()
379     sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
380     if sobj is not None:
381         (found, attr) = getStudyEditor().builder.FindAttribute(
382                                                 sobj, "AttributeParameter")
383         log = attr.GetString("log")
384         dialog = MyTextDisplayDialog(sgPyQt.getDesktop())
385         dialog.set_log(log)
386         dialog.show()
387
388 ###
389 # Open Eficas for Telemac2D to create a new case
390 ###
391 def create_telemac2d_case():
392     EficasForTelemac2DAppli()
393
394 ###
395 # Open Eficas for Telemac2D to edit the selected case
396 ###
397 def edit_telemac2d_case():
398     EficasForTelemac2DAppli(get_and_check_selected_file_path())
399
400 # Run Telemac2D on selected case
401 def run_telemac2d():
402     engine = None
403     try:
404         with wait_cursor:
405             ed = hydro_study.HydroStudyEditor()
406             entry = salome.sg.getSelected(0)
407             engine = salome.lcc.FindOrLoadComponent("Telemac2DContainer", "TELEMAC2D")
408             engine.Compute(ed.editor.studyId, entry)
409             # Stop container after execution so that we can use another fortran user file in the next run
410             engine.GetContainerRef().Shutdown()
411     except SALOME.SALOME_Exception, exc:
412         if engine is not None:
413             engine.GetContainerRef().Shutdown()
414         msg = unicode(QApplication.translate("run_telemac2d",
415                       "An error happened while trying to run Telemac2D:"))
416         msg += "\n" + exc.details.text
417         raise HSGUIException(msg)
418     except Exception, exc:
419         logger.exception("An error happened in the computation (Telemac2D probably crashed).")
420         try:
421             engine.GetContainerRef().Shutdown()
422         except:
423             pass
424         msg = unicode(QApplication.translate("run_telemac2d",
425                       "An error happened in the computation (Telemac2D probably crashed, "
426                       "see logs and listing files for more details):"))
427         msg += "\n" + unicode(exc)
428         raise HSGUIException(msg)
429
430 ###
431 # Generate a python script from the eficas file
432 ###
433 def generate_telemac2d_python():
434     try:
435         with wait_cursor:
436             ed = hydro_study.HydroStudyEditor()
437             sobj = get_and_check_selected_file_path()
438             ed.generate_study_script(sobj)
439     except SALOME.SALOME_Exception, exc:
440         salome.sg.updateObjBrowser( 0 )
441         msg = unicode(QApplication.translate("generate_telemac2d_python",
442                       "An error happened while trying to generate telemac2d Python script:"))
443         msg += "\n" + exc.details.text
444         raise HSGUIException(msg)
445
446 ###
447 # Generate a python script from the eficas file
448 ###
449 def generate_telemac2d_yacs():
450     try:
451         with wait_cursor:
452             ed = hydro_study.HydroStudyEditor()
453             sobj = get_and_check_selected_file_path()
454             ed.generate_study_yacs(sobj)
455     except SALOME.SALOME_Exception, exc:
456         salome.sg.updateObjBrowser( 0 )
457         msg = unicode(QApplication.translate("generate_telemac2d_yacs",
458                       "An error happened while trying to generate telemac2d Python script:"))
459         msg += "\n" + exc.details.text
460         raise HSGUIException(msg)
461
462 ###
463 # Open Eficas for 1D / 2D Coupling to create a new coupling case
464 ###
465 def create_coupling1d2d_case():
466     EficasForCoupling1D2DAppli()
467
468 ###
469 # Open Eficas for 1D / 2D Coupling to edit the selected coupling case
470 ###
471 def edit_coupling1d2d_case():
472     EficasForCoupling1D2DAppli(get_and_check_selected_file_path())
473
474 def open_schema_in_yacs():
475     ed = getStudyEditor()
476     sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
477     filename = sobj.GetComment()
478     if filename.endswith(".comm"):
479         filename = filename[:-5] + ".xml"
480     if not os.path.isfile(filename):
481         raise HSGUIException(QApplication.translate("open_schema_in_yacs",
482                                   "Schema file %1 does not exist").arg(filename))
483     yg = salome.ImportComponentGUI("YACS")
484     yg.loadSchema(filename)
485
486 ###
487 # Open Eficas for boundary conditions definition
488 ###
489 #def define_boundary_conditions():
490 #    EficasForBoundaryConditionsAppli()
491
492 ###
493 # Open dialog for boundary conditions edition
494 ###
495 def edit_boundary_conditions_file():
496     desktop = sgPyQt.getDesktop()
497     dlg = BndConditionsDialog.BoundaryConditionsDialog(desktop)
498     dlg.exec_()
499
500 ###
501 # Open dialog for interpolz.py script generation
502 ###
503 def generate_interpolz_py():
504     desktop = sgPyQt.getDesktop()
505     dlg = InterpolzDlg(desktop)
506     dlg.show()
507
508 ###
509 # Open dialog for boundary conditions edition
510 ###
511 def eficas_for_cas_Telemac2D():
512    runEficas(code='TELEMAC')
513 ###
514 # Commands dictionary
515 ###
516 dict_command = {
517     GUIcontext.CREATE_MASCARET_CASE_ID: create_mascaret_case,
518     GUIcontext.RUN_MASCARET_ID: run_mascaret,
519     GUIcontext.EDIT_MASCARET_CASE_ID: edit_mascaret_case,
520     GUIcontext.SHOW_LOG_ID: show_log,
521     GUIcontext.CREATE_TELEMAC2D_CASE_ID: create_telemac2d_case,
522     GUIcontext.RUN_TELEMAC2D_ID: run_telemac2d,
523     GUIcontext.GEN_TELEMAC2D_PYTHON_ID: generate_telemac2d_python,
524     GUIcontext.GEN_TELEMAC2D_YACS_ID: generate_telemac2d_yacs,
525     GUIcontext.EDIT_TELEMAC2D_CASE_ID: edit_telemac2d_case,
526     GUIcontext.CREATE_COUPLING1D2D_CASE_ID: create_coupling1d2d_case,
527     GUIcontext.EDIT_COUPLING1D2D_CASE_ID: edit_coupling1d2d_case,
528     GUIcontext.OPEN_SCHEMA_IN_YACS_ID: open_schema_in_yacs,
529     GUIcontext.CREATE_PYTEL_CASE_ID: create_case_pytel,
530     GUIcontext.RUN_PYTEL_ID: run_selected_case_pytel,
531     GUIcontext.EDIT_PYTEL_CASE_ID: edit_selected_case_pytel,
532     GUIcontext.GENERATE_JOB: generate_job_for_selected_case_pytel,
533     #GUIcontext.DEFINE_BOUNDARY_CONDITIONS_ID: define_boundary_conditions,
534     GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID: edit_boundary_conditions_file,
535     GUIcontext.GENERATE_INTERPOLZ_PY_ID: generate_interpolz_py,
536     GUIcontext.DEFINE_CAS_FILE: eficas_for_cas_Telemac2D,
537     }