1# -*- coding: utf-8 -*- 2 3#------------------------------------------------------------------------------- 4 5# This file is part of Code_Saturne, a general-purpose CFD tool. 6# 7# Copyright (C) 1998-2021 EDF S.A. 8# 9# This program is free software; you can redistribute it and/or modify it under 10# the terms of the GNU General Public License as published by the Free Software 11# Foundation; either version 2 of the License, or (at your option) any later 12# version. 13# 14# This program is distributed in the hope that it will be useful, but WITHOUT 15# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17# details. 18# 19# You should have received a copy of the GNU General Public License along with 20# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 21# Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 23#------------------------------------------------------------------------------- 24 25""" 26This module defines the 'Equation parameters' page. 27 28This module contains the following classes and function: 29- SchemeDelegate 30- SolverDelegate 31- PrecisionDelegate 32- IterationDelegate 33- StandardItemModelScheme 34- StandardItemModelSolver 35- NumericalParamEquationView 36""" 37 38#------------------------------------------------------------------------------- 39# Library modules import 40#------------------------------------------------------------------------------- 41 42import logging 43 44#------------------------------------------------------------------------------- 45# Third-party modules 46#------------------------------------------------------------------------------- 47 48from code_saturne.Base.QtCore import * 49from code_saturne.Base.QtGui import * 50from code_saturne.Base.QtWidgets import * 51 52#------------------------------------------------------------------------------- 53# Application modules import 54#------------------------------------------------------------------------------- 55 56from code_saturne.model.Common import GuiParam 57from code_saturne.Base.QtPage import ComboModel, DoubleValidator, IntValidator 58from code_saturne.Base.QtPage import from_qvariant, to_text_string 59from NumericalParamEquationNeptune import Ui_NumericalParamEquation 60from code_saturne.model.NumericalParamEquationModelNeptune import NumericalParamEquatModel 61from code_saturne.model.GlobalNumericalParametersModel import GlobalNumericalParametersModel 62 63#------------------------------------------------------------------------------- 64# log config 65#------------------------------------------------------------------------------- 66 67logging.basicConfig() 68log = logging.getLogger("NumericalParamEquationView") 69log.setLevel(GuiParam.DEBUG) 70 71 72#------------------------------------------------------------------------------- 73# Combo box delegate for the scheme 74#------------------------------------------------------------------------------- 75 76class SchemeDelegate(QItemDelegate): 77 """ 78 Use of a combo box in the table. 79 """ 80 def __init__(self, parent, dicoM2V, dicoV2M): 81 super(SchemeDelegate, self).__init__(parent) 82 self.parent = parent 83 self.dicoM2V = dicoM2V 84 self.dicoV2M = dicoV2M 85 86 87 def createEditor(self, parent, option, index): 88 editor = QComboBox(parent) 89 self.modelCombo = ComboModel(editor, 3, 1) 90 self.modelCombo.addItem(self.tr(self.dicoM2V["centered"]), 'centered') 91 self.modelCombo.addItem(self.tr(self.dicoM2V["upwind"]), 'upwind') 92 self.modelCombo.addItem(self.tr(self.dicoM2V["solu"]), 'solu') 93 94 editor.installEventFilter(self) 95 return editor 96 97 98 def setEditorData(self, comboBox, index): 99 row = index.row() 100 col = index.column() 101 string = index.model().getData(index)[col] 102 self.modelCombo.setItem(str_view=string) 103 104 105 def setModelData(self, comboBox, model, index): 106 txt = str(comboBox.currentText()) 107 value = self.modelCombo.dicoV2M[txt] 108 log.debug("SchemeDelegate value = %s"%value) 109 110 selectionModel = self.parent.selectionModel() 111 for idx in selectionModel.selectedIndexes(): 112 if idx.column() == index.column(): 113 model.setData(idx, self.dicoM2V[value], Qt.DisplayRole) 114 115 116#------------------------------------------------------------------------------- 117# Combo box delegate for the solver 118#------------------------------------------------------------------------------- 119 120class SolverDelegate(QItemDelegate): 121 """ 122 Use of a combo box in the table. 123 """ 124 def __init__(self, parent, dicoM2V, dicoV2M): 125 super(SolverDelegate, self).__init__(parent) 126 self.parent = parent 127 self.dicoM2V = dicoM2V 128 self.dicoV2M = dicoV2M 129 130 131 def createEditor(self, parent, option, index): 132 editor = QComboBox(parent) 133 self.modelCombo = ComboModel(editor, 12, 1) 134 self.modelCombo.addItem(self.tr(self.dicoM2V["automatic"]), 'automatic') 135 self.modelCombo.addItem(self.tr(self.dicoM2V["jacobi"]), 'jacobi') 136 self.modelCombo.addItem(self.tr(self.dicoM2V["pcg"]), 'pcg') 137 self.modelCombo.addItem(self.tr(self.dicoM2V["cgstab"]), 'cgstab') 138 self.modelCombo.addItem(self.tr(self.dicoM2V["jacobi_saturne"]), 'jacobi_saturne') 139 self.modelCombo.addItem(self.tr(self.dicoM2V["pcg_saturne"]), 'pcg_saturne') 140 self.modelCombo.addItem(self.tr(self.dicoM2V["bicgstab_saturne"]), 'bicgstab_saturne') 141 self.modelCombo.addItem(self.tr(self.dicoM2V["bicgstab2_saturne"]), 'bicgstab2_saturne') 142 self.modelCombo.addItem(self.tr(self.dicoM2V["gmres_saturne"]), 'gmres_saturne') 143 self.modelCombo.addItem(self.tr(self.dicoM2V["gauss_seidel_saturne"]), 'gauss_seidel_saturne') 144 self.modelCombo.addItem(self.tr(self.dicoM2V["sym_gauss_seidel_saturne"]), 'sym_gauss_seidel_saturne') 145 self.modelCombo.addItem(self.tr(self.dicoM2V["pcr3_saturne"]), 'pcr3_saturne') 146 147 editor.installEventFilter(self) 148 return editor 149 150 151 def setEditorData(self, comboBox, index): 152 row = index.row() 153 col = index.column() 154 string = index.model().getData(index)[col] 155 self.modelCombo.setItem(str_view=string) 156 157 158 def setModelData(self, comboBox, model, index): 159 txt = str(comboBox.currentText()) 160 value = self.modelCombo.dicoV2M[txt] 161 log.debug("SolverDelegate value = %s"%value) 162 163 selectionModel = self.parent.selectionModel() 164 for idx in selectionModel.selectedIndexes(): 165 if idx.column() == index.column(): 166 model.setData(idx, self.dicoM2V[value], Qt.DisplayRole) 167 168 169#------------------------------------------------------------------------------- 170# Line edit delegate for the value 171#------------------------------------------------------------------------------- 172 173class PrecisionDelegate(QItemDelegate): 174 def __init__(self, parent=None): 175 super(PrecisionDelegate, self).__init__(parent) 176 self.parent = parent 177 178 179 def createEditor(self, parent, option, index): 180 editor = QLineEdit(parent) 181 v = DoubleValidator(editor, min=0.) 182 editor.setValidator(v) 183 return editor 184 185 186 def setEditorData(self, editor, index): 187 editor.setAutoFillBackground(True) 188 value = from_qvariant(index.model().data(index, Qt.DisplayRole), to_text_string) 189 editor.setText(value) 190 191 192 def setModelData(self, editor, model, index): 193 if not editor.isModified(): 194 return 195 if editor.validator().state == QValidator.Acceptable: 196 value = from_qvariant(editor.text(), float) 197 for idx in self.parent.selectionModel().selectedIndexes(): 198 if idx.column() == index.column(): 199 model.setData(idx, value, Qt.DisplayRole) 200 201 202#------------------------------------------------------------------------------- 203# Line edit delegate for the value 204#------------------------------------------------------------------------------- 205 206class IterationDelegate(QItemDelegate): 207 def __init__(self, parent=None): 208 super(IterationDelegate, self).__init__(parent) 209 self.parent = parent 210 211 212 def createEditor(self, parent, option, index): 213 editor = QLineEdit(parent) 214 v = IntValidator(editor, min=1) 215 editor.setValidator(v) 216 return editor 217 218 219 def setEditorData(self, editor, index): 220 editor.setAutoFillBackground(True) 221 value = from_qvariant(index.model().data(index, Qt.DisplayRole), to_text_string) 222 editor.setText(value) 223 224 225 def setModelData(self, editor, model, index): 226 if not editor.isModified(): 227 return 228 if editor.validator().state == QValidator.Acceptable: 229 value = from_qvariant(editor.text(), float) 230 for idx in self.parent.selectionModel().selectedIndexes(): 231 if idx.column() == index.column(): 232 model.setData(idx, value, Qt.DisplayRole) 233 234 235#------------------------------------------------------------------------------- 236# Scheme class 237#------------------------------------------------------------------------------- 238 239class StandardItemModelScheme(QStandardItemModel): 240 241 def __init__(self, NPE, dicoM2V, dicoV2M): 242 """ 243 """ 244 QStandardItemModel.__init__(self) 245 self.mdl = NPE 246 self.dicoM2V = dicoM2V 247 self.dicoV2M = dicoV2M 248 249 self.headers = [self.tr("Name"), 250 self.tr("Scheme"), 251 self.tr("Slope\nTest")] 252 253 self.setColumnCount(len(self.headers)) 254 255 self._data = [] 256 257 258 def data(self, index, role): 259 if not index.isValid(): 260 return None 261 262 if role == Qt.ToolTipRole: 263 return None 264 265 elif role == Qt.DisplayRole: 266 data = self._data[index.row()][index.column()] 267 if data: 268 return data 269 else: 270 return None 271 272 elif role == Qt.CheckStateRole: 273 data = self._data[index.row()][index.column()] 274 if index.column() == 2: 275 if data == 'on': 276 return Qt.Checked 277 else: 278 return Qt.Unchecked 279 280 elif role == Qt.TextAlignmentRole: 281 return Qt.AlignCenter 282 283 return None 284 285 286 def flags(self, index): 287 if not index.isValid(): 288 return Qt.ItemIsEnabled 289 290 elif index.column() == 0 : 291 return Qt.ItemIsEnabled | Qt.ItemIsSelectable 292 elif index.column() == 2: 293 data = self._data[index.row()][1] 294 if data == 'upwind': 295 return Qt.ItemIsSelectable 296 else: 297 return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable 298 else: 299 return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable 300 301 302 def headerData(self, section, orientation, role): 303 if orientation == Qt.Horizontal and role == Qt.DisplayRole: 304 return self.headers[section] 305 return None 306 307 308 def setData(self, index, value, role): 309 if not index.isValid(): 310 return Qt.ItemIsEnabled 311 312 # Update the row in the table 313 row = index.row() 314 col = index.column() 315 var = self._data[row][0] 316 317 # Scheme 318 if col == 1: 319 new_scheme = from_qvariant(value, to_text_string) 320 self._data[row][col] = new_scheme 321 self.mdl.setSchemeModel(var, self.dicoV2M[new_scheme]) 322 self._data[row][2] = str(self.mdl.getSlopeTestStatus(var)) 323 324 # Slope Test 325 elif col == 2: 326 state = from_qvariant(value, int) 327 if state == Qt.Unchecked: 328 self._data[row][col] = "off" 329 self.mdl.setSlopeTestStatus(var,"off") 330 else: 331 self._data[row][col] = "on" 332 self.mdl.setSlopeTestStatus(var,"on") 333 334 self.dataChanged.emit(index, index) 335 return True 336 337 338 def getData(self, index): 339 row = index.row() 340 return self._data[row] 341 342 343 def newItem(self, variable): 344 """ 345 Add/load a variable in the model. 346 """ 347 row = self.rowCount() 348 349 scheme = self.dicoM2V[self.mdl.getSchemeModel(variable)] 350 slope = self.mdl.getSlopeTestStatus(variable) 351 352 var = [variable, scheme, slope] 353 354 self._data.append(var) 355 self.setRowCount(row+1) 356 357 358#------------------------------------------------------------------------------- 359# Solver class 360#------------------------------------------------------------------------------- 361 362class StandardItemModelSolver(QStandardItemModel): 363 364 def __init__(self, NPE, dicoM2V, dicoV2M): 365 """ 366 """ 367 QStandardItemModel.__init__(self) 368 self.mdl = NPE 369 self.dicoM2V = dicoM2V 370 self.dicoV2M = dicoV2M 371 372 self.headers = [self.tr("Name"), 373 self.tr("Solver"), 374 self.tr("Solver\nprecision"), 375 self.tr("Maximum\niteration")] 376 377 self.setColumnCount(len(self.headers)) 378 379 self._data = [] 380 381 382 def data(self, index, role): 383 if not index.isValid(): 384 return None 385 386 if role == Qt.ToolTipRole: 387 return None 388 389 elif role == Qt.DisplayRole: 390 data = self._data[index.row()][index.column()] 391 if data: 392 return str(data) 393 else: 394 return None 395 396 elif role == Qt.TextAlignmentRole: 397 return Qt.AlignCenter 398 399 return None 400 401 402 def flags(self, index): 403 if not index.isValid(): 404 return Qt.ItemIsEnabled 405 406 if index.column() == 0 : 407 return Qt.ItemIsEnabled | Qt.ItemIsSelectable 408 else: 409 return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable 410 411 412 def headerData(self, section, orientation, role): 413 if orientation == Qt.Horizontal and role == Qt.DisplayRole: 414 return self.headers[section] 415 return None 416 417 418 def setData(self, index, value, role): 419 if not index.isValid(): 420 return Qt.ItemIsEnabled 421 422 # Update the row in the table 423 row = index.row() 424 col = index.column() 425 var = self._data[row][0] 426 427 # Solver 428 if col == 1: 429 new_solver = from_qvariant(value, to_text_string) 430 self._data[row][col] = new_solver 431 self.mdl.setSolverModel(var, self.dicoV2M[new_solver]) 432 433 # Precision solver 434 elif col == 2: 435 coeff = from_qvariant(value, float) 436 self._data[row][col] = coeff 437 self.mdl.setSolverPrecision(var, coeff) 438 439 # Maximum iteration 440 elif col == 3: 441 coeff = from_qvariant(value, int) 442 self._data[row][col] = coeff 443 self.mdl.setMaximumIteration(var, coeff) 444 445 self.dataChanged.emit(index, index) 446 return True 447 448 449 def getData(self, index): 450 row = index.row() 451 return self._data[row] 452 453 454 def newItem(self, variable): 455 """ 456 Add/load a variable in the model. 457 """ 458 row = self.rowCount() 459 460 solver = self.dicoM2V[self.mdl.getSolverModel(variable)] 461 prec = self.mdl.getSolverPrecision(variable) 462 iter = self.mdl.getMaximumIteration(variable) 463 464 var = [variable, solver, prec, iter] 465 466 self._data.append(var) 467 self.setRowCount(row+1) 468 469 470#------------------------------------------------------------------------------- 471# Main class 472#------------------------------------------------------------------------------- 473 474class NumericalParamEquationView(QWidget, Ui_NumericalParamEquation): 475 """ 476 """ 477 def __init__(self, parent, case): 478 """ 479 Constructor 480 """ 481 QWidget.__init__(self, parent) 482 Ui_NumericalParamEquation.__init__(self) 483 self.setupUi(self) 484 485 self.case = case 486 self.case.undoStopGlobal() 487 self.mdl = NumericalParamEquatModel(self.case) 488 489 # dico 490 self.dicoM2V = {"automatic" : 'Automatic', 491 "jacobi" : 'Jacobi (neptune_cfd)', 492 "pcg" : 'PCG (neptune_cfd)', 493 "cgstab" : 'CGSTAB (neptune_cfd)', 494 "jacobi_saturne" : 'Jacobi (code_saturne)', 495 "pcg_saturne" : 'PCG (code_saturne)', 496 "bicgstab_saturne" : 'BICGSTAB (code_saturne)', 497 "bicgstab2_saturne" : 'BICGSTAB-2 (code_saturne)', 498 "gmres_saturne" : 'GMRES (code_saturne)', 499 "gauss_seidel_saturne" : 'Gauss-Seidel (code_saturne)', 500 "sym_gauss_seidel_saturne" : 'Sym. Gauss-Seidel (code_saturne)', 501 "pcr3_saturne" : 'PCR3 (code_saturne)', 502 "centered" : 'Centered', 503 "upwind" : 'Upwind', 504 "solu" : 'SOLU'} 505 self.dicoV2M = {"Automatic" : 'automatic', 506 "Jacobi (neptune_cfd)" : 'jacobi', 507 "PCG (neptune_cfd)" : 'pcg', 508 "CGSTAB (neptune_cfd)" : 'cgstab', 509 "Jacobi (code_saturne)" : 'jacobi_saturne', 510 "PCG (code_saturne)" : 'pcg_saturne', 511 "BICGSTAB (code_saturne)" : 'bicgstab_saturne', 512 "BICGSTAB-2 (code_saturne)" : 'bicgstab2_saturne', 513 "GMRES (code_saturne)" : 'gmres_saturne', 514 "Gauss-Seidel (code_saturne)" : 'gauss_seidel_saturne', 515 "Sym. Gauss-Seidel (code_saturne)" : 'sym_gauss_seidel_saturne', 516 "PCR3 (code_saturne)" : 'pcr3_saturne', 517 "Centered" : 'centered', 518 "Upwind" : 'upwind', 519 "SOLU" : 'solu'} 520 521 # Scheme 522 self.modelScheme = StandardItemModelScheme(self.mdl, self.dicoM2V, self.dicoV2M) 523 self.tableViewScheme.setModel(self.modelScheme) 524 self.tableViewScheme.setAlternatingRowColors(True) 525 self.tableViewScheme.resizeColumnToContents(0) 526 self.tableViewScheme.resizeRowsToContents() 527 self.tableViewScheme.setSelectionBehavior(QAbstractItemView.SelectItems) 528 self.tableViewScheme.setSelectionMode(QAbstractItemView.ExtendedSelection) 529 self.tableViewScheme.setEditTriggers(QAbstractItemView.DoubleClicked) 530 if QT_API == "PYQT4": 531 self.tableViewScheme.horizontalHeader().setResizeMode(QHeaderView.Stretch) 532 elif QT_API == "PYQT5": 533 self.tableViewScheme.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) 534 535 delegateScheme = SchemeDelegate(self.tableViewScheme, self.dicoM2V, self.dicoV2M) 536 537 self.tableViewScheme.setItemDelegateForColumn(1, delegateScheme) 538 539 # Solveur 540 self.modelSolver = StandardItemModelSolver(self.mdl, self.dicoM2V, self.dicoV2M) 541 self.tableViewSolver.setModel(self.modelSolver) 542 self.tableViewSolver.setAlternatingRowColors(True) 543 self.tableViewSolver.resizeColumnToContents(0) 544 self.tableViewSolver.resizeRowsToContents() 545 self.tableViewSolver.setSelectionBehavior(QAbstractItemView.SelectItems) 546 self.tableViewSolver.setSelectionMode(QAbstractItemView.ExtendedSelection) 547 self.tableViewSolver.setEditTriggers(QAbstractItemView.DoubleClicked) 548 if QT_API == "PYQT4": 549 self.tableViewSolver.horizontalHeader().setResizeMode(QHeaderView.Stretch) 550 elif QT_API == "PYQT5": 551 self.tableViewSolver.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) 552 553 delegateSolver = SolverDelegate(self.tableViewSolver, self.dicoM2V, self.dicoV2M) 554 delegatePrecision = PrecisionDelegate(self.tableViewSolver) 555 delegateIteration = IterationDelegate(self.tableViewSolver) 556 self.tableViewSolver.setItemDelegateForColumn(1, delegateSolver) 557 self.tableViewSolver.setItemDelegateForColumn(2, delegatePrecision) 558 self.tableViewSolver.setItemDelegateForColumn(3, delegateIteration) 559 if QT_API == "PYQT4": 560 self.tableViewSolver.horizontalHeader().setResizeMode(QHeaderView.Stretch) 561 elif QT_API == "PYQT5": 562 self.tableViewSolver.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) 563 564 # Connect signals to slots 565 self.modelSolver.dataChanged.connect(self.dataChangedSolver) 566 self.modelScheme.dataChanged.connect(self.dataChangedScheme) 567 self.tabWidgetScheme.currentChanged[int].connect(self.slotchanged) 568 569 # load variables 570 for var in self.mdl.getVariableList() : 571 self.modelSolver.newItem(var) 572 self.modelScheme.newItem(var) 573 self.tableViewSolver.resizeRowsToContents() 574 575 self.tabWidgetScheme.setCurrentIndex(self.case['current_tab']) 576 577 self.case.undoStartGlobal() 578 579 580 def dataChangedSolver(self, topLeft, bottomRight): 581 for row in range(topLeft.row(), bottomRight.row()+1): 582 self.tableViewSolver.resizeRowToContents(row) 583 for col in range(topLeft.column(), bottomRight.column()+1): 584 self.tableViewSolver.resizeColumnToContents(col) 585 586 587 def dataChangedScheme(self, topLeft, bottomRight): 588 for row in range(topLeft.row(), bottomRight.row()+1): 589 self.tableViewScheme.resizeRowToContents(row) 590 for col in range(topLeft.column(), bottomRight.column()+1): 591 self.tableViewScheme.resizeColumnToContents(col) 592 593 594 @pyqtSlot(int) 595 def slotchanged(self, index): 596 """ 597 Changed tab 598 """ 599 self.case['current_tab'] = index 600