Source code for plots.drag

#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''Pychemqt, Chemical Engineering Process simulator
Copyright (C) 2009-2025, Juan José Gómez Romera <jjgomera@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.


.. include:: drag.rst


API Reference
-------------

The module include all related moody chart functionality
    * :class:`Drag`: Chart dialog
    * :class:`CalculateDialog`: Dialog to calculate a specified point and \
its configuration
    * :class:`Config`: Drag sphere chart configuration
'''


from numpy import logspace
from tools.qt import QtWidgets, translate

from lib import drag
from lib.config import Preferences
from lib.utilities import formatLine
from UI.widgets import Entrada_con_unidades, GridConfig, LineConfig

from plots.ui import Chart


[docs] class Config(QtWidgets.QWidget): """Drag sphere chart configuration""" TITLE = translate("Drag", "Drag Sphere chart") TITLECONFIG = translate("Drag", "Drag sphere diagram configuration")
[docs] def __init__(self, config=None, parent=None): super().__init__(parent) layout = QtWidgets.QGridLayout(self) layout.addWidget(QtWidgets.QLabel(self.tr("Method:")), 1, 1) self.metodos = QtWidgets.QComboBox() for f in drag.f_list: self.metodos.addItem(f.__name__) layout.addWidget(self.metodos, 1, 2) layout.addItem(QtWidgets.QSpacerItem( 10, 10, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed), 1, 3) self.lineconfig = LineConfig( "line", self.tr("Drag coefficient style line")) layout.addWidget(self.lineconfig, 4, 1, 1, 3) self.cruxconfig = LineConfig("crux", self.tr("Crux style line")) layout.addWidget(self.cruxconfig, 5, 1, 1, 3) self.gridconfig = GridConfig("grid", self.tr("Grid style line")) layout.addWidget(self.gridconfig, 6, 1, 1, 3) layout.addItem(QtWidgets.QSpacerItem( 10, 0, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding), 10, 1, 1, 3) if config and config.has_section("drag"): self.metodos.setCurrentIndex(config.getint("drag", 'method')) self.lineconfig.setConfig(config, "drag") self.cruxconfig.setConfig(config, "drag") self.gridconfig.setConfig(config, "drag")
[docs] def value(self, config): """Update ConfigParser instance with the config""" if not config.has_section("drag"): config.add_section("drag") config.set("drag", "method", str(self.metodos.currentIndex())) config = self.lineconfig.value(config, "drag") config = self.cruxconfig.value(config, "drag") config = self.gridconfig.value(config, "drag") return config
[docs] class Drag(Chart): """Drag sphere chart dialog""" title = translate("Drag", "Drag Sphere") widgetConfig = Config locLogo = (0.8, 0.85, 0.1, 0.1) note = None
[docs] def config(self): """Initialization action in plot don't neccesary to update in plot""" self.plt.fig.subplots_adjust( left=0.1, right=0.9, bottom=0.12, top=0.98) self.note = None
[docs] def click(self, event): """Update input and graph annotate when mouse click over chart""" Re = event.xdata Cd = event.ydata # Exit if click event if out of axis if Re is None: self.clearCrux() return Cd = None method = Preferences.getint("drag", "method") F = drag.f_list[method] Cd = F(Re) self.createCrux(Re, Cd)
[docs] @staticmethod def _txt(Re, Cd): txt = f"Re: {Re:0.4g}\n$C_d$: {Cd:0.4g}" return txt
[docs] def clearCrux(self): """Remove crux and note if exist""" self.plt.lx.set_ydata([0]) self.plt.ly.set_xdata([0]) if self.note: self.note.remove() self.note = None self.plt.draw()
[docs] def createCrux(self, Re, Cd): """Create a crux in selected point of plot and show data at bottom right corner""" self.clearCrux() txt = self._txt(Re, Cd) self.plt.lx.set_ydata([Cd]) self.plt.ly.set_xdata([Re]) self.note = self.plt.fig.text(0.92, 0.05, txt, size="8", va="top") self.plt.draw()
[docs] def plot(self): """Plot the drag chart using the indicate method """ method = Preferences.getint("drag", "method") f = drag.f_list[method] self.plt.ax.set_autoscale_on(False) self.plt.ax.clear() Re = logspace(-1, 6, 1000) Cd = [] for re in Re: try: v = f(re) except NotImplementedError: v = None Cd.append(v) kw = formatLine(Preferences, "drag", "line") self.plt.ax.plot(Re, Cd, **kw) kw = formatLine(Preferences, "drag", "crux") self.plt.lx = self.plt.ax.axhline(**kw) # the horiz line self.plt.ly = self.plt.ax.axvline(**kw) # the vert line xlabel = self.tr("Reynolds number") + ", " + r"$Re=\frac{V\rho D}{\mu}$" self.plt.ax.set_xlabel(xlabel, ha='center', size='10') self.plt.ax.set_ylabel("Drag coefficient, $C_d$, [-]", size='10') grid = Preferences.getboolean("drag", "grid") kw = formatLine(Preferences, "drag", "grid") del kw["marker"] if grid: self.plt.ax.grid(grid, **kw) else: self.plt.ax.grid(grid) self.plt.ax.set_xlim(0.1, 1e6) self.plt.ax.set_ylim(0.06, 300) self.plt.ax.set_xscale("log") self.plt.ax.set_yscale("log") self.plt.draw()
[docs] def calculate(self): dlg = CalculateDialog() if dlg.exec(): Re = dlg.Re.value Cd = dlg.Cd.value self.createCrux(Re, Cd)
[docs] class CalculateDialog(QtWidgets.QDialog): """Dialog to calculate a specified point"""
[docs] def __init__(self, parent=None): super().__init__(parent) title = self.tr("Calculate friction factor") self.setWindowTitle(title) layout = QtWidgets.QGridLayout(self) label = QtWidgets.QLabel(self.tr("Method:")) layout.addWidget(label, 1, 0) self.metodos = QtWidgets.QComboBox() for f in drag.f_list: self.metodos.addItem(f.__name__) self.metodos.currentIndexChanged.connect(self.calculate) layout.addWidget(self.metodos, 1, 1, 1, 2) layout.addWidget(QtWidgets.QLabel("Re"), 3, 1) self.Re = Entrada_con_unidades(float, tolerancia=4) self.Re.valueChanged.connect(self.calculate) layout.addWidget(self.Re, 3, 2) layout.addWidget(QtWidgets.QLabel("Cd"), 5, 1) self.Cd = Entrada_con_unidades(float, readOnly=True, decimales=8) layout.addWidget(self.Cd, 5, 2) layout.addItem(QtWidgets.QSpacerItem( 10, 10, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding), 9, 1, 1, 3) self.buttonBox = QtWidgets.QDialogButtonBox( QtWidgets.QDialogButtonBox.StandardButton.Ok | QtWidgets.QDialogButtonBox.StandardButton.Close) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) layout.addWidget(self.buttonBox, 10, 1, 1, 3)
[docs] def calculate(self): """Calculate point procedure""" Re = self.Re.value index = self.metodos.currentIndex() F = drag.f_list[index] if Re: Cd = F(Re) self.Cd.setValue(Cd)
if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) Dialog = Drag() Dialog.show() sys.exit(app.exec())