diff --git a/hypnotoad/cases/tokamak.py b/hypnotoad/cases/tokamak.py index 46d4a8ad..2d5c4cde 100644 --- a/hypnotoad/cases/tokamak.py +++ b/hypnotoad/cases/tokamak.py @@ -1452,7 +1452,7 @@ def Bt_axis(self): return self.fpol(self.psi_axis) / self.o_point.R -def read_geqdsk(filehandle, options={}, **kwargs): +def just_read_geqdsk(filehandle, options=None, **kwargs): """ Read geqdsk formatted data from a file object, returning a TokamakEquilibrium object @@ -1472,6 +1472,9 @@ def read_geqdsk(filehandle, options={}, **kwargs): from ..geqdsk._geqdsk import read as geq_read + if options is None: + options = {} + data = geq_read(filehandle) # Range of psi normalises psi derivatives @@ -1525,14 +1528,38 @@ def read_geqdsk(filehandle, options={}, **kwargs): # fpol constant in SOL fpol = np.concatenate([fpol, np.full(psiSOL.shape, fpol[-1])]) - return TokamakEquilibrium( - R1D, - Z1D, - psi2D, - psi1D, - fpol, + return dict( + R1D=R1D, + Z1D=Z1D, + psi2D=psi2D, + psi1D=psi1D, + fpol1D=fpol, pressure=pressure, wall=wall, - options=options, - **kwargs, + ) + + +def read_geqdsk(filehandle, options=None, **kwargs): + """ + Read geqdsk formatted data from a file object, returning + a TokamakEquilibrium object + + Inputs + ------ + filehandle A file handle to read + options Options|dict passed to TokamakEquilibrium + kwargs Other keywords passed to TokamakEquilibrim + These override values in options. + + Options + ------- + reverse_current = bool Changes the sign of poloidal flux psi + extrapolate_profiles = bool Extrapolate pressure using exponential + """ + + if options is None: + options = {} + + return TokamakEquilibrium( + **just_read_geqdsk(filehandle), options=options, **kwargs, ) diff --git a/hypnotoad/gui/gui.py b/hypnotoad/gui/gui.py index 97337174..76983acc 100644 --- a/hypnotoad/gui/gui.py +++ b/hypnotoad/gui/gui.py @@ -4,12 +4,16 @@ """ import ast +import contextlib import copy -import options +import functools import os import pathlib import yaml +import numpy as np +import options + from Qt.QtWidgets import ( QFileDialog, QMainWindow, @@ -28,6 +32,7 @@ from ..cases import tokamak from ..core.mesh import BoutMesh from ..core.equilibrium import SolutionError +from ..utils import critical from ..__init__ import __version__ @@ -51,6 +56,29 @@ def _table_item_edit_display(item): item.setText(item.text()[: -len(default_marker)]) +def psinorm_to_psi(psinorm, psi_axis, psi_sep): + if psinorm is None: + return None + return psi_axis + psinorm * (psi_sep[0] - psi_axis) + + +def psi_to_psinorm(psi, psi_axis, psi_sep): + if psi is None: + return None + return (psi - psi_axis) / (psi_sep[0] - psi_axis) + + +@contextlib.contextmanager +def disconnected(signal, function): + """Temporarily disconnect a function from a signal + + """ + try: + yield signal.disconnect(function) + finally: + signal.connect(function) + + class HypnotoadGui(QMainWindow, Ui_Hypnotoad): """A graphical interface for Hypnotoad @@ -127,6 +155,125 @@ def set_clicked(widget, function): self.options_form.itemDoubleClicked.connect(_table_item_edit_display) self.update_options_form() + # Equilibrium tab + self.equilibrium_plot_widget = MatplotlibWidget(self.equilibrium_plotting_area) + + set_clicked(self.eq_geqdsk_browse, self.eq_select_geqdsk_file) + self.eq_geqdsk_lineedit.editingFinished.connect(self.eq_read_geqdsk) + + # Different limits in psi + self.psi_contours = { + "core": { + "unnorm_widget": self.psi_coreDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="core", direction="norm" + ), + "norm_widget": self.psinorm_coreDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="core", direction="unnorm" + ), + "style": "solid", + "enabled": True, + }, + "sol": { + "unnorm_widget": self.psi_solDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="sol", direction="norm" + ), + "norm_widget": self.psinorm_solDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="sol", direction="unnorm" + ), + "style": "dashed", + "enabled": True, + }, + "sol_inner": { + "unnorm_widget": self.psi_sol_innerDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="sol_inner", direction="norm" + ), + "norm_widget": self.psinorm_sol_innerDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="sol_inner", direction="unnorm" + ), + "style": "dotted", + "enabled": False, + "parent": "sol", + }, + "pf": { + "unnorm_widget": self.psi_pfDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="pf", direction="norm" + ), + "norm_widget": self.psinorm_pfDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="pf", direction="unnorm" + ), + "style": "dashdot", + "enabled": False, + "parent": "core", + }, + "pf_lower": { + "unnorm_widget": self.psi_pf_lowerDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="pf_lower", direction="norm" + ), + "norm_widget": self.psinorm_pf_lowerDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="pf_lower", direction="unnorm" + ), + "style": "dashdot", + "enabled": False, + "parent": "pf", + }, + "pf_upper": { + "unnorm_widget": self.psi_pf_upperDoubleSpinBox, + "unnorm_slot": lambda value: self.update_linked_psi( + value, name="pf_upper", direction="norm" + ), + "norm_widget": self.psinorm_pf_upperDoubleSpinBox, + "norm_slot": lambda value: self.update_linked_psi( + value, name="pf_upper", direction="unnorm" + ), + "style": "dashdot", + "enabled": False, + "parent": "pf", + }, + } + + self.deal_with_separate_psi_contour(False, "sol_inner") + self.separateInnerSolCheckBox.stateChanged.connect( + lambda state: self.deal_with_separate_psi_contour(state, name="sol_inner") + ) + self.deal_with_separate_psi_contour(False, "pf") + self.separatePrivateFluxCheckBox.stateChanged.connect( + lambda state: self.deal_with_separate_psi_contour(state, name="pf") + ) + self.deal_with_separate_psi_contour(False, "pf_lower") + self.deal_with_separate_psi_contour(False, "pf_upper") + self.separate_upper_lowerPrivateFluxCheckBox.stateChanged.connect( + lambda state: self.deal_with_separate_psi_contour(state, name="pf_lower") + ) + self.separate_upper_lowerPrivateFluxCheckBox.stateChanged.connect( + lambda state: self.deal_with_separate_psi_contour(state, name="pf_upper") + ) + + def add_to_and_update_options_form(value, name): + self.options[name] = value + self.update_options_form() + + for name, contour in self.psi_contours.items(): + contour["unnorm_widget"].valueChanged.connect(contour["unnorm_slot"]) + contour["unnorm_widget"].valueChanged.connect( + functools.partial(add_to_and_update_options_form, name=f"psi_{name}") + ) + contour["norm_widget"].valueChanged.connect(contour["norm_slot"]) + contour["norm_widget"].valueChanged.connect( + functools.partial( + add_to_and_update_options_form, name=f"psinorm_{name}" + ) + ) + def help_about(self): """About Hypnotoad @@ -385,8 +532,22 @@ def read_geqdsk(self): return try: - with open(geqdsk_filename, "rt") as f: - self.eq = tokamak.read_geqdsk(f, options=copy.deepcopy(self.options)) + try: + self.eq = tokamak.TokamakEquilibrium( + self.eq_data["R1D"], + self.eq_data["Z1D"], + self.eq_data["psi2D"], + self.eq_data["psi1D"], + self.eq_data["fpol1D"], + self.eq_data["pressure"], + self.eq_data["wall"], + options=copy.deepcopy(self.options), + ) + except AttributeError: + with open(geqdsk_filename, "rt") as f: + self.eq = tokamak.read_geqdsk( + f, options=copy.deepcopy(self.options) + ) except (ValueError, RuntimeError) as e: error_message = QErrorMessage() error_message.showMessage(str(e)) @@ -406,7 +567,7 @@ def run(self): """ - if not hasattr(self, "eq"): + if not (hasattr(self, "eq") or hasattr(self, "eq_data")): self.statusbar.showMessage("Missing equilibrium file!") self.geqdsk_file_line_edit.setStyleSheet( f"QLineEdit {{ background-color: {COLOURS['red']} }}" @@ -490,6 +651,208 @@ def plot_grid(self): self.plot_widget.canvas.draw() + def eq_select_geqdsk_file(self): + """Choose a "geqdsk" equilibrium file to open + + """ + + filename, _ = QFileDialog.getOpenFileName(self, "Open geqdsk file", ".") + + if (filename is None) or (filename == ""): + return # Cancelled + if not os.path.exists(filename): + self.write("Could not find " + filename) + self.geqdsk_file_line_edit.setStyleSheet( + f"QLineEdit {{ background-color: {COLOURS['red']} }}" + ) + return + + self.eq_geqdsk_lineedit.setText(filename) + self.eq_geqdsk_lineedit.setStyleSheet("") + + self.eq_read_geqdsk() + + def eq_read_geqdsk(self): + """Read an equilibrium file + + """ + + self.statusbar.showMessage("Reading geqdsk", 2000) + geqdsk_filename = self.eq_geqdsk_lineedit.text() + + if not os.path.exists(geqdsk_filename): + self.geqdsk_file_line_edit.setStyleSheet( + f"QLineEdit {{ background-color : {COLOURS['red']} }}" + ) + self.statusbar.showMessage( + f"Could not find equilibrium file '{geqdsk_filename}'" + ) + return + + try: + with open(geqdsk_filename, "rt") as f: + self.eq_data = tokamak.just_read_geqdsk(f) + except (ValueError, RuntimeError) as e: + error_message = QErrorMessage() + error_message.showMessage(str(e)) + error_message.exec_() + return + + self.R2D, self.Z2D = np.meshgrid( + self.eq_data["R1D"], self.eq_data["Z1D"], indexing="ij" + ) + self.opoints, self.xpoints = critical.find_critical( + self.R2D, self.Z2D, self.eq_data["psi2D"] + ) + self.eq_data["psi_axis"] = self.opoints[0][2] + self.eq_data["psi_sep"] = [x[2] for x in self.xpoints] + self.eq_data["Rmin"] = min(self.eq_data["R1D"]) + self.eq_data["Rmax"] = max(self.eq_data["R1D"]) + self.eq_data["Zmin"] = min(self.eq_data["Z1D"]) + self.eq_data["Zmax"] = max(self.eq_data["Z1D"]) + + min_psi = self.eq_data["psi2D"].min() + max_psi = self.eq_data["psi2D"].max() + + def setup_psi_widget(widget): + widget.setRange(min_psi, max_psi) + widget.setDecimals(4) + widget.setSingleStep(0.0001) + + def setup_psinorm_widget(widget): + widget.setDecimals(4) + widget.setSingleStep(0.01) + + for contour in self.psi_contours.values(): + setup_psi_widget(contour["unnorm_widget"]) + setup_psinorm_widget(contour["norm_widget"]) + + self.psinorm_coreDoubleSpinBox.setValue( + tokamak.TokamakEquilibrium.default_options["psinorm_core"] + ) + self.psinorm_solDoubleSpinBox.setValue( + tokamak.TokamakEquilibrium.default_options["psinorm_sol"] + ) + + self.plot_equilibrium() + + def deal_with_separate_psi_contour(self, state, name): + checked = state == Qt.Checked + + contour = self.psi_contours[name] + parent = self.psi_contours[contour["parent"]] + + self._remove_contour_from_plot(name, redraw=True) + + contour["norm_widget"].setEnabled(checked) + contour["unnorm_widget"].setEnabled(checked) + contour["enabled"] = checked + + if checked: + parent["norm_widget"].valueChanged.disconnect( + contour["norm_widget"].setValue + ) + parent["unnorm_widget"].valueChanged.disconnect( + contour["unnorm_widget"].setValue + ) + else: + parent["norm_widget"].valueChanged.connect(contour["norm_widget"].setValue) + parent["unnorm_widget"].valueChanged.connect( + contour["unnorm_widget"].setValue + ) + contour["norm_widget"].setValue(parent["norm_widget"].value()) + contour["unnorm_widget"].setValue(parent["unnorm_widget"].value()) + try: + del self.options["psi_" + name] + except KeyError: + pass + + def _remove_contour_from_plot(self, name, redraw=False): + """Remove named psi contour from equilibrium plot + + """ + try: + self.psi_contours[name]["plot"].collections[0].remove() + del self.psi_contours[name]["plot"] + if redraw: + self.equilibrium_plot_widget.canvas.draw() + except KeyError: + pass + + def plot_equilibrium(self): + """Plot preliminary-equilibrium and psi contours + """ + for contour in self.psi_contours: + self._remove_contour_from_plot(contour) + + self.equilibrium_plot_widget.clear() + + if not hasattr(self, "eq_data"): + return + self.statusbar.showMessage("plotting equilibrium", 2000) + self.equilibrium_plot_widget.axes.contour( + self.eq_data["R1D"], self.eq_data["Z1D"], self.eq_data["psi2D"].T, levels=40 + ) + for num, opoint in enumerate(self.opoints): + self.equilibrium_plot_widget.axes.plot( + opoint[0], opoint[1], "o", label=f"O-point {num}" + ) + for num, xpoint in enumerate(self.xpoints): + self.equilibrium_plot_widget.axes.plot( + xpoint[0], xpoint[1], "x", label=f"X-point {num}" + ) + self.equilibrium_plot_widget.canvas.draw() + + for contour in self.psi_contours: + self.plot_single_psi_contour(contour) + + def update_linked_psi(self, value, name, direction): + """Update the value of the normalised/unnormalised psi widget + + """ + contour = self.psi_contours[name] + widget = direction + "_widget" + slot = direction + "_slot" + + if direction == "norm": + conversion = psi_to_psinorm + elif direction == "unnorm": + conversion = psinorm_to_psi + else: + raise ValueError("Direction must be 'unnorm' or 'norm'") + + with disconnected(contour[widget].valueChanged, contour[slot]): + contour[widget].setValue( + conversion(value, self.eq_data["psi_axis"], self.eq_data["psi_sep"]) + ) + self.plot_single_psi_contour(name) + + def plot_single_psi_contour(self, name): + """Plot a single psi contour onto the equilibrium plot + """ + if not hasattr(self, "eq_data"): + return + + self._remove_contour_from_plot(name) + + # Nicer local name + contour = self.psi_contours[name] + + if not contour["enabled"]: + return + + self.psi_contours[name]["plot"] = self.equilibrium_plot_widget.axes.contour( + self.eq_data["R1D"], + self.eq_data["Z1D"], + self.eq_data["psi2D"].T, + levels=[contour["unnorm_widget"].value()], + colors="k", + linewidths=3, + linestyles=contour["style"], + ) + + self.equilibrium_plot_widget.canvas.draw() + class Preferences(QDialog, Ui_Preferences): """Dialog box for editing Hypnotoad preferences diff --git a/hypnotoad/gui/hypnotoad_mainWindow.py b/hypnotoad/gui/hypnotoad_mainWindow.py index 2d770ace..f3d3ea1e 100644 --- a/hypnotoad/gui/hypnotoad_mainWindow.py +++ b/hypnotoad/gui/hypnotoad_mainWindow.py @@ -113,22 +113,245 @@ def setupUi(self, Hypnotoad): self.action_Revert.setIcon(icon8) self.action_Preferences = QAction(Hypnotoad) self.action_Preferences.setObjectName(u"action_Preferences") - icon9 = QIcon(QIcon.fromTheme(u"document-properties")) + icon9 = QIcon() + iconThemeName = u"document-properties" + if QIcon.hasThemeIcon(iconThemeName): + icon9 = QIcon.fromTheme(iconThemeName) + else: + icon9.addFile(u".", QSize(), QIcon.Normal, QIcon.Off) + self.action_Preferences.setIcon(icon9) self.centralwidget = QWidget(Hypnotoad) self.centralwidget.setObjectName(u"centralwidget") - self.horizontalLayout_2 = QHBoxLayout(self.centralwidget) + self.verticalLayout = QVBoxLayout(self.centralwidget) + self.verticalLayout.setObjectName(u"verticalLayout") + self.tabWidget = QTabWidget(self.centralwidget) + self.tabWidget.setObjectName(u"tabWidget") + self.equilibrium_tab = QWidget() + self.equilibrium_tab.setObjectName(u"equilibrium_tab") + self.horizontalLayout_2 = QHBoxLayout(self.equilibrium_tab) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.verticalLayout_5 = QVBoxLayout() + self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.eq_geqdsk_label = QLabel(self.equilibrium_tab) + self.eq_geqdsk_label.setObjectName(u"eq_geqdsk_label") + + self.horizontalLayout_4.addWidget(self.eq_geqdsk_label) + + self.eq_geqdsk_lineedit = QLineEdit(self.equilibrium_tab) + self.eq_geqdsk_lineedit.setObjectName(u"eq_geqdsk_lineedit") + + self.horizontalLayout_4.addWidget(self.eq_geqdsk_lineedit) + + self.eq_geqdsk_browse = QPushButton(self.equilibrium_tab) + self.eq_geqdsk_browse.setObjectName(u"eq_geqdsk_browse") + + self.horizontalLayout_4.addWidget(self.eq_geqdsk_browse) + + + self.verticalLayout_5.addLayout(self.horizontalLayout_4) + + self.normalised_psi_box = QGroupBox(self.equilibrium_tab) + self.normalised_psi_box.setObjectName(u"normalised_psi_box") + self.formLayoutWidget = QWidget(self.normalised_psi_box) + self.formLayoutWidget.setObjectName(u"formLayoutWidget") + self.formLayoutWidget.setGeometry(QRect(10, 30, 361, 263)) + self.formLayout_2 = QFormLayout(self.formLayoutWidget) + self.formLayout_2.setObjectName(u"formLayout_2") + self.formLayout_2.setSizeConstraint(QLayout.SetDefaultConstraint) + self.formLayout_2.setContentsMargins(0, 0, 0, 0) + self.psinorm_coreLabel = QLabel(self.formLayoutWidget) + self.psinorm_coreLabel.setObjectName(u"psinorm_coreLabel") + + self.formLayout_2.setWidget(1, QFormLayout.LabelRole, self.psinorm_coreLabel) + + self.psinorm_coreDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_coreDoubleSpinBox.setObjectName(u"psinorm_coreDoubleSpinBox") + + self.formLayout_2.setWidget(1, QFormLayout.FieldRole, self.psinorm_coreDoubleSpinBox) + + self.psinorm_solLabel = QLabel(self.formLayoutWidget) + self.psinorm_solLabel.setObjectName(u"psinorm_solLabel") + + self.formLayout_2.setWidget(2, QFormLayout.LabelRole, self.psinorm_solLabel) + + self.psinorm_solDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_solDoubleSpinBox.setObjectName(u"psinorm_solDoubleSpinBox") + + self.formLayout_2.setWidget(2, QFormLayout.FieldRole, self.psinorm_solDoubleSpinBox) + + self.separateInnerSolLabel = QLabel(self.formLayoutWidget) + self.separateInnerSolLabel.setObjectName(u"separateInnerSolLabel") + + self.formLayout_2.setWidget(3, QFormLayout.LabelRole, self.separateInnerSolLabel) + + self.separateInnerSolCheckBox = QCheckBox(self.formLayoutWidget) + self.separateInnerSolCheckBox.setObjectName(u"separateInnerSolCheckBox") + + self.formLayout_2.setWidget(3, QFormLayout.FieldRole, self.separateInnerSolCheckBox) + + self.innerSolLabel = QLabel(self.formLayoutWidget) + self.innerSolLabel.setObjectName(u"innerSolLabel") + + self.formLayout_2.setWidget(4, QFormLayout.LabelRole, self.innerSolLabel) + + self.psinorm_sol_innerDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_sol_innerDoubleSpinBox.setObjectName(u"psinorm_sol_innerDoubleSpinBox") + + self.formLayout_2.setWidget(4, QFormLayout.FieldRole, self.psinorm_sol_innerDoubleSpinBox) + + self.separatePrivateFluxLabel = QLabel(self.formLayoutWidget) + self.separatePrivateFluxLabel.setObjectName(u"separatePrivateFluxLabel") + + self.formLayout_2.setWidget(5, QFormLayout.LabelRole, self.separatePrivateFluxLabel) + + self.separatePrivateFluxCheckBox = QCheckBox(self.formLayoutWidget) + self.separatePrivateFluxCheckBox.setObjectName(u"separatePrivateFluxCheckBox") + + self.formLayout_2.setWidget(5, QFormLayout.FieldRole, self.separatePrivateFluxCheckBox) + + self.psinorm_pfLabel = QLabel(self.formLayoutWidget) + self.psinorm_pfLabel.setObjectName(u"psinorm_pfLabel") + + self.formLayout_2.setWidget(6, QFormLayout.LabelRole, self.psinorm_pfLabel) + + self.psinorm_pfDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_pfDoubleSpinBox.setObjectName(u"psinorm_pfDoubleSpinBox") + + self.formLayout_2.setWidget(6, QFormLayout.FieldRole, self.psinorm_pfDoubleSpinBox) + + self.separateInnerPrivateFluxLabel = QLabel(self.formLayoutWidget) + self.separateInnerPrivateFluxLabel.setObjectName(u"separateInnerPrivateFluxLabel") + + self.formLayout_2.setWidget(7, QFormLayout.LabelRole, self.separateInnerPrivateFluxLabel) + + self.separate_upper_lowerPrivateFluxCheckBox = QCheckBox(self.formLayoutWidget) + self.separate_upper_lowerPrivateFluxCheckBox.setObjectName(u"separate_upper_lowerPrivateFluxCheckBox") + + self.formLayout_2.setWidget(7, QFormLayout.FieldRole, self.separate_upper_lowerPrivateFluxCheckBox) + + self.psinorm_pf_upperLabel = QLabel(self.formLayoutWidget) + self.psinorm_pf_upperLabel.setObjectName(u"psinorm_pf_upperLabel") + + self.formLayout_2.setWidget(8, QFormLayout.LabelRole, self.psinorm_pf_upperLabel) + + self.psinorm_pf_upperDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_pf_upperDoubleSpinBox.setObjectName(u"psinorm_pf_upperDoubleSpinBox") + + self.formLayout_2.setWidget(8, QFormLayout.FieldRole, self.psinorm_pf_upperDoubleSpinBox) + + self.psinorm_pf_lowerLabel = QLabel(self.formLayoutWidget) + self.psinorm_pf_lowerLabel.setObjectName(u"psinorm_pf_lowerLabel") + + self.formLayout_2.setWidget(9, QFormLayout.LabelRole, self.psinorm_pf_lowerLabel) + + self.psinorm_pf_lowerDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget) + self.psinorm_pf_lowerDoubleSpinBox.setObjectName(u"psinorm_pf_lowerDoubleSpinBox") + + self.formLayout_2.setWidget(9, QFormLayout.FieldRole, self.psinorm_pf_lowerDoubleSpinBox) + + + self.verticalLayout_5.addWidget(self.normalised_psi_box) + + self.unnormalised_psi_box = QGroupBox(self.equilibrium_tab) + self.unnormalised_psi_box.setObjectName(u"unnormalised_psi_box") + self.formLayoutWidget_2 = QWidget(self.unnormalised_psi_box) + self.formLayoutWidget_2.setObjectName(u"formLayoutWidget_2") + self.formLayoutWidget_2.setGeometry(QRect(10, 20, 361, 217)) + self.formLayout_4 = QFormLayout(self.formLayoutWidget_2) + self.formLayout_4.setObjectName(u"formLayout_4") + self.formLayout_4.setSizeConstraint(QLayout.SetMaximumSize) + self.formLayout_4.setContentsMargins(0, 0, 0, 0) + self.psi_coreLabel = QLabel(self.formLayoutWidget_2) + self.psi_coreLabel.setObjectName(u"psi_coreLabel") + + self.formLayout_4.setWidget(0, QFormLayout.LabelRole, self.psi_coreLabel) + + self.psi_coreDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_coreDoubleSpinBox.setObjectName(u"psi_coreDoubleSpinBox") + + self.formLayout_4.setWidget(0, QFormLayout.FieldRole, self.psi_coreDoubleSpinBox) + + self.psi_solLabel = QLabel(self.formLayoutWidget_2) + self.psi_solLabel.setObjectName(u"psi_solLabel") + + self.formLayout_4.setWidget(1, QFormLayout.LabelRole, self.psi_solLabel) + + self.psi_solDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_solDoubleSpinBox.setObjectName(u"psi_solDoubleSpinBox") + + self.formLayout_4.setWidget(1, QFormLayout.FieldRole, self.psi_solDoubleSpinBox) + + self.psi_sol_innerLabel = QLabel(self.formLayoutWidget_2) + self.psi_sol_innerLabel.setObjectName(u"psi_sol_innerLabel") + + self.formLayout_4.setWidget(2, QFormLayout.LabelRole, self.psi_sol_innerLabel) + + self.psi_sol_innerDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_sol_innerDoubleSpinBox.setObjectName(u"psi_sol_innerDoubleSpinBox") + + self.formLayout_4.setWidget(2, QFormLayout.FieldRole, self.psi_sol_innerDoubleSpinBox) + + self.psi_pf_upperLabel = QLabel(self.formLayoutWidget_2) + self.psi_pf_upperLabel.setObjectName(u"psi_pf_upperLabel") + + self.formLayout_4.setWidget(4, QFormLayout.LabelRole, self.psi_pf_upperLabel) + + self.psi_pf_upperDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_pf_upperDoubleSpinBox.setObjectName(u"psi_pf_upperDoubleSpinBox") + + self.formLayout_4.setWidget(4, QFormLayout.FieldRole, self.psi_pf_upperDoubleSpinBox) + + self.psi_pf_lowerLabel = QLabel(self.formLayoutWidget_2) + self.psi_pf_lowerLabel.setObjectName(u"psi_pf_lowerLabel") + + self.formLayout_4.setWidget(5, QFormLayout.LabelRole, self.psi_pf_lowerLabel) + + self.psi_pf_lowerDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_pf_lowerDoubleSpinBox.setObjectName(u"psi_pf_lowerDoubleSpinBox") + + self.formLayout_4.setWidget(5, QFormLayout.FieldRole, self.psi_pf_lowerDoubleSpinBox) + + self.psi_pfLabel = QLabel(self.formLayoutWidget_2) + self.psi_pfLabel.setObjectName(u"psi_pfLabel") + + self.formLayout_4.setWidget(3, QFormLayout.LabelRole, self.psi_pfLabel) + + self.psi_pfDoubleSpinBox = QDoubleSpinBox(self.formLayoutWidget_2) + self.psi_pfDoubleSpinBox.setObjectName(u"psi_pfDoubleSpinBox") + + self.formLayout_4.setWidget(3, QFormLayout.FieldRole, self.psi_pfDoubleSpinBox) + + + self.verticalLayout_5.addWidget(self.unnormalised_psi_box) + + + self.horizontalLayout_2.addLayout(self.verticalLayout_5) + + self.equilibrium_plotting_area = QFrame(self.equilibrium_tab) + self.equilibrium_plotting_area.setObjectName(u"equilibrium_plotting_area") + self.equilibrium_plotting_area.setFrameShape(QFrame.StyledPanel) + self.equilibrium_plotting_area.setFrameShadow(QFrame.Raised) + + self.horizontalLayout_2.addWidget(self.equilibrium_plotting_area) + + self.tabWidget.addTab(self.equilibrium_tab, "") + self.mesh_tab = QWidget() + self.mesh_tab.setObjectName(u"mesh_tab") + self.verticalLayout_3 = QVBoxLayout(self.mesh_tab) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setObjectName(u"horizontalLayout") self.verticalLayout_2 = QVBoxLayout() self.verticalLayout_2.setObjectName(u"verticalLayout_2") - self.search_bar = QLineEdit(self.centralwidget) + self.search_bar = QLineEdit(self.mesh_tab) self.search_bar.setObjectName(u"search_bar") self.verticalLayout_2.addWidget(self.search_bar) - self.options_form = QTableWidget(self.centralwidget) + self.options_form = QTableWidget(self.mesh_tab) if (self.options_form.columnCount() < 2): self.options_form.setColumnCount(2) __qtablewidgetitem = QTableWidgetItem() @@ -141,42 +364,42 @@ def setupUi(self, Hypnotoad): self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.options_file_label = QLabel(self.centralwidget) + self.options_file_label = QLabel(self.mesh_tab) self.options_file_label.setObjectName(u"options_file_label") self.horizontalLayout_3.addWidget(self.options_file_label) - self.options_file_line_edit = QLineEdit(self.centralwidget) + self.options_file_line_edit = QLineEdit(self.mesh_tab) self.options_file_line_edit.setObjectName(u"options_file_line_edit") self.horizontalLayout_3.addWidget(self.options_file_line_edit) - self.options_file_browse_button = QPushButton(self.centralwidget) + self.options_file_browse_button = QPushButton(self.mesh_tab) self.options_file_browse_button.setObjectName(u"options_file_browse_button") self.horizontalLayout_3.addWidget(self.options_file_browse_button) - self.geqdsk_file_label = QLabel(self.centralwidget) + self.geqdsk_file_label = QLabel(self.mesh_tab) self.geqdsk_file_label.setObjectName(u"geqdsk_file_label") self.horizontalLayout_3.addWidget(self.geqdsk_file_label) - self.geqdsk_file_line_edit = QLineEdit(self.centralwidget) + self.geqdsk_file_line_edit = QLineEdit(self.mesh_tab) self.geqdsk_file_line_edit.setObjectName(u"geqdsk_file_line_edit") self.horizontalLayout_3.addWidget(self.geqdsk_file_line_edit) - self.geqdsk_file_browse_button = QPushButton(self.centralwidget) + self.geqdsk_file_browse_button = QPushButton(self.mesh_tab) self.geqdsk_file_browse_button.setObjectName(u"geqdsk_file_browse_button") self.horizontalLayout_3.addWidget(self.geqdsk_file_browse_button) - self.run_button = QPushButton(self.centralwidget) + self.run_button = QPushButton(self.mesh_tab) self.run_button.setObjectName(u"run_button") self.horizontalLayout_3.addWidget(self.run_button) - self.write_grid_button = QPushButton(self.centralwidget) + self.write_grid_button = QPushButton(self.mesh_tab) self.write_grid_button.setObjectName(u"write_grid_button") self.horizontalLayout_3.addWidget(self.write_grid_button) @@ -187,13 +410,17 @@ def setupUi(self, Hypnotoad): self.horizontalLayout.addLayout(self.verticalLayout_2) - self.plottingArea = QWidget(self.centralwidget) + self.plottingArea = QFrame(self.mesh_tab) self.plottingArea.setObjectName(u"plottingArea") self.horizontalLayout.addWidget(self.plottingArea) - self.horizontalLayout_2.addLayout(self.horizontalLayout) + self.verticalLayout_3.addLayout(self.horizontalLayout) + + self.tabWidget.addTab(self.mesh_tab, "") + + self.verticalLayout.addWidget(self.tabWidget) Hypnotoad.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(Hypnotoad) @@ -236,6 +463,9 @@ def setupUi(self, Hypnotoad): self.retranslateUi(Hypnotoad) + self.tabWidget.setCurrentIndex(0) + + QMetaObject.connectSlotsByName(Hypnotoad) # setupUi @@ -275,6 +505,26 @@ def retranslateUi(self, Hypnotoad): #endif // QT_CONFIG(shortcut) self.action_Revert.setText(QCoreApplication.translate("Hypnotoad", u"&Revert", None)) self.action_Preferences.setText(QCoreApplication.translate("Hypnotoad", u"&Preferences...", None)) + self.eq_geqdsk_label.setText(QCoreApplication.translate("Hypnotoad", u"geqdsk file", None)) + self.eq_geqdsk_browse.setText(QCoreApplication.translate("Hypnotoad", u"Browse", None)) + self.normalised_psi_box.setTitle(QCoreApplication.translate("Hypnotoad", u"Normalised Psi", None)) + self.psinorm_coreLabel.setText(QCoreApplication.translate("Hypnotoad", u"Core", None)) + self.psinorm_solLabel.setText(QCoreApplication.translate("Hypnotoad", u"SOL", None)) + self.separateInnerSolLabel.setText(QCoreApplication.translate("Hypnotoad", u"Separate inner SOL", None)) + self.innerSolLabel.setText(QCoreApplication.translate("Hypnotoad", u"Inner SOL", None)) + self.separatePrivateFluxLabel.setText(QCoreApplication.translate("Hypnotoad", u"Separate private flux", None)) + self.psinorm_pfLabel.setText(QCoreApplication.translate("Hypnotoad", u"PF", None)) + self.separateInnerPrivateFluxLabel.setText(QCoreApplication.translate("Hypnotoad", u"Separate upper/lower private flux", None)) + self.psinorm_pf_upperLabel.setText(QCoreApplication.translate("Hypnotoad", u"Upper PF", None)) + self.psinorm_pf_lowerLabel.setText(QCoreApplication.translate("Hypnotoad", u"Lower PF", None)) + self.unnormalised_psi_box.setTitle(QCoreApplication.translate("Hypnotoad", u"Unormalised Psi", None)) + self.psi_coreLabel.setText(QCoreApplication.translate("Hypnotoad", u"Core", None)) + self.psi_solLabel.setText(QCoreApplication.translate("Hypnotoad", u"SOL", None)) + self.psi_sol_innerLabel.setText(QCoreApplication.translate("Hypnotoad", u"Inner sol", None)) + self.psi_pf_upperLabel.setText(QCoreApplication.translate("Hypnotoad", u"Lower PF", None)) + self.psi_pf_lowerLabel.setText(QCoreApplication.translate("Hypnotoad", u"Upper PF", None)) + self.psi_pfLabel.setText(QCoreApplication.translate("Hypnotoad", u"PF", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.equilibrium_tab), QCoreApplication.translate("Hypnotoad", u"&Equilibrium", None)) ___qtablewidgetitem = self.options_form.horizontalHeaderItem(0) ___qtablewidgetitem.setText(QCoreApplication.translate("Hypnotoad", u"Name", None)); ___qtablewidgetitem1 = self.options_form.horizontalHeaderItem(1) @@ -285,6 +535,7 @@ def retranslateUi(self, Hypnotoad): self.geqdsk_file_browse_button.setText(QCoreApplication.translate("Hypnotoad", u"Browse", None)) self.run_button.setText(QCoreApplication.translate("Hypnotoad", u"Run", None)) self.write_grid_button.setText(QCoreApplication.translate("Hypnotoad", u"Write Grid", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.mesh_tab), QCoreApplication.translate("Hypnotoad", u"&Mesh", None)) self.menu_File.setTitle(QCoreApplication.translate("Hypnotoad", u"&File", None)) self.menu_Help.setTitle(QCoreApplication.translate("Hypnotoad", u"&Help", None)) self.menu_Mesh.setTitle(QCoreApplication.translate("Hypnotoad", u"&Mesh", None)) diff --git a/hypnotoad/gui/hypnotoad_mainWindow.ui b/hypnotoad/gui/hypnotoad_mainWindow.ui index d3c6aa07..61c51cc3 100644 --- a/hypnotoad/gui/hypnotoad_mainWindow.ui +++ b/hypnotoad/gui/hypnotoad_mainWindow.ui @@ -14,86 +14,336 @@ MainWindow - + - - - + + + 0 + + + + &Equilibrium + + - - - - - - - Name - - - - - Value - - - - - - - - - - Options file - - - + - + + + + + geqdsk file + + + + + + + + + + Browse + + + + - - - Browse + + + Normalised Psi + + + + 10 + 30 + 361 + 263 + + + + + QLayout::SetDefaultConstraint + + + + + Core + + + + + + + + + + SOL + + + + + + + + + + Separate inner SOL + + + + + + + + + + Inner SOL + + + + + + + + + + Separate private flux + + + + + + + + + + PF + + + + + + + + + + Separate upper/lower private flux + + + + + + + + + + Upper PF + + + + + + + + + + Lower PF + + + + + + + + - - - geqdsk file + + + Unormalised Psi + + + + 10 + 20 + 361 + 217 + + + + + QLayout::SetMaximumSize + + + + + Core + + + + + + + + + + SOL + + + + + + + + + + Inner sol + + + + + + + + + + Upper PF + + + + + + + + + + Lower PF + + + + + + + + + + PF + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + &Mesh + + + + - - - - - - Browse - - - - - - - Run - - + + + + + + + + + Name + + + + + Value + + + + + + + + + + Options file + + + + + + + + + + Browse + + + + + + + geqdsk file + + + + + + + + + + Browse + + + + + + + Run + + + + + + + Write Grid + + + + + + - - - Write Grid - - + - - - - - + + @@ -261,7 +511,8 @@ - + + .. &Preferences...