From c6470013e9385fbf18618468aa8252079592097a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Ram=C3=ADrez?= Date: Sun, 17 Dec 2023 18:00:12 +0100 Subject: [PATCH] Updated documentation --- RepTate/gui/QApplicationManager.py | 17 +++- RepTate/gui/QApplicationWindow.py | 20 +++- .../theories/TheoryShanbhagMaxwellModes.py | 5 +- RepTate/theories/TheoryStickyReptation.py | 59 ++++------- RepTate/tools/ToolMaterialsDatabase.py | 98 +++++++++++-------- docs/source/developers/callgraphGUI.rst | 4 +- 6 files changed, 113 insertions(+), 90 deletions(-) diff --git a/RepTate/gui/QApplicationManager.py b/RepTate/gui/QApplicationManager.py index a26b5874..010275fa 100644 --- a/RepTate/gui/QApplicationManager.py +++ b/RepTate/gui/QApplicationManager.py @@ -112,9 +112,22 @@ def emit(self, record): self.widget.moveCursor(QTextCursor.End) -# class QApplicationManager(ApplicationManager, QMainWindow, Ui_MainWindow): class QApplicationManager(QMainWindow, Ui_MainWindow): - """Main Reptate window and application manager""" + """Main Reptate window and application manager. It contains the main toolbar from which the user can open the different applications. + + .. note:: This class inherits from PySide6 QMainWindow and Ui_MainWindow, which is the class generated by QtDesigner. + + .. graphviz:: + + digraph G { + "QApplicationWindow" [href="../developers/CodeCoreGUI.html#qapplicationwindow", target="_top", shape="box", style="rounded,filled"] + "QApplicationManager" [href="../developers/CodeCoreGUI.html#qapplicationmanager", target="_top", shape="box", style="rounded,filled"] + "QMainWindow" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QMainWindow.html", target="_top", style="filled"] + "QMainWindow" -> "QApplicationManager" [color=green]; + "QApplicationManager" -> "QApplicationWindow" [color=red]; + } + + """ html_help_file = "http://reptate.readthedocs.io/index.html" # COPIED FROM APPLICATIONMANAGER diff --git a/RepTate/gui/QApplicationWindow.py b/RepTate/gui/QApplicationWindow.py index f5dfa557..417c6402 100644 --- a/RepTate/gui/QApplicationWindow.py +++ b/RepTate/gui/QApplicationWindow.py @@ -39,6 +39,7 @@ import sys import os import io +import math import re import traceback from numpy import * @@ -353,7 +354,21 @@ def __init__(self, parent=None, fnames=None, factorsx=None, factorsy=None): # class QApplicationWindow(Application, QMainWindow, Ui_AppWindow): class QApplicationWindow(QMainWindow, Ui_AppWindow): - """Main abstract class that represents a GUI Application""" + """Main abstract class that represents a RepTate Application. + + .. note:: This class is not meant to be used directly. It is the base class for all RepTate GUI applications. This class inherits from PySide6 QMainWindow and Ui_MainWindow, which is the class generated by QtDesigner. + + .. graphviz:: + + digraph G { + "QApplicationWindow" [href="../developers/CodeCoreGUI.html#qapplicationwindow", target="_top", shape="box", style="rounded,filled"] + "QApplicationManager" [href="../developers/CodeCoreGUI.html#qapplicationmanager", target="_top", shape="box", style="rounded,filled"] + "QMainWindow" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QMainWindow.html", target="_top", style="filled"] + "QMainWindow" -> "QApplicationWindow" [color=green]; + "QApplicationManager" -> "QApplicationWindow" [color=red]; + } + + """ def __init__( self, name="Application Template", parent=None, nplot_max=4, ncols=2, **kwargs @@ -1857,12 +1872,13 @@ def clipboard_coordinates(self, artist): def new(self, line): """Create new empty dataset in the application""" + # TODO: Can this be removed? self.num_datasets += 1 if line == "": dsname = "Set%d" % self.num_datasets else: dsname = line - ds = DataSet(dsname, self) + ds = QDataSet(dsname, self) return ds, dsname def delete(self, ds_name): diff --git a/RepTate/theories/TheoryShanbhagMaxwellModes.py b/RepTate/theories/TheoryShanbhagMaxwellModes.py index 9554ebf3..fb46abe2 100644 --- a/RepTate/theories/TheoryShanbhagMaxwellModes.py +++ b/RepTate/theories/TheoryShanbhagMaxwellModes.py @@ -2036,9 +2036,8 @@ def getH(self, lam, Gexp, H, kernMat, *argv): return res_lsq.x def residualLM(self, H, lam, Gexp, kernMat): - """ - % - % HELPER FUNCTION: Gets Residuals r + """HELPER FUNCTION: Gets Residuals r + Input: H = guessed H, lambda = regularization parameter , Gexp = experimental data, diff --git a/RepTate/theories/TheoryStickyReptation.py b/RepTate/theories/TheoryStickyReptation.py index 80de0ac2..1d43ad9d 100644 --- a/RepTate/theories/TheoryStickyReptation.py +++ b/RepTate/theories/TheoryStickyReptation.py @@ -125,49 +125,32 @@ def g_descloizeaux(self, x, tol): return gx def calculate(self, f=None): - """ + """STICKY-REPTATION MODEL FOR LINEAR VISCOELASTICITY + + * **PARAMETERS:** + - Ge: elastic modulus + - tau_s: sticker dissociation time + - Zs: number of stickers per chain + - Ze: number of entanglements per chain + - alpha: magnitude of the contour-length fluctuations in the double-reptation model. This is principle a universal dimensionless number with a value around ~10. + + * **IMPORTANT:** - STICKY-REPTATION MODEL FOR LINEAR VISCOELASTICITY + - I. This sticky-reptation model assumes high Rouse frequencies not to affect the rheology at times of the order of the sticker time, due to which the rheology is independent of both the elementary (non-sticky) Rouse time, tau0, and the degree of polymerisation, N. See below. - PARAMETERS: - > Ge - elastic modulus - > tau_s - sticker dissociation time - > Zs - number of stickers per chain - > Ze - number of entanglements per chain - > alpha - magnitude of the contour-length fluctuations in the - double-reptation model. This is principle a universal - dimensionless number with a value around ~10. - IMPORTANT: - I. This sticky-reptation model assumes high Rouse frequencies - not to affect the rheology at times of the order of the - sticker time, due to which the rheology is independent - of both the elementary (non-sticky) Rouse time, tau0, - and the degree of polymerisation, N. See below. - II. The results may be affected by numerical approximations, - see below. + - II. The results may be affected by numerical approximations, see below. - I. MODEL APPROXIMATION: - 1: The reptation time and Rouse relaxation after sticker - dissociation are approximate. After sticker dissociation - a strand of length N/Zs relaxes (a factor of two, to - represent a strand of twice that length relaxes) is - ignored. The reptation time is taken tau_rep=tau_s Zs^2*Ze, - with the prefactor 3 ignored. - 2. The model assumes that the sticker dissociation time tau_s - is much larger than tau0*(N/Zs)^2. The shape of the sticker - plateau in G' and G'' is therefore not affected by the - early-time Rouse relaxation, and is independent of tau0 and - N: Including the high frequencies requires tau0 and N as - additional parameters. + * **I. MODEL APPROXIMATION:** - II. NUMERICAL SETTINGS: - Some numerical - 1. The infinite sums in the double-reptation model are - truncated using a numerical tolerance level. - 2. To transform G(t) to G'(w) and G''(w) a time range with - a finite number of samples is defined. The time range - and number of samples may affect the results.""" + - 1: The reptation time and Rouse relaxation after sticker dissociation are approximate. After sticker dissociation a strand of length N/Zs relaxes (a factor of two, to represent a strand of twice that length relaxes) is ignored. The reptation time is taken tau_rep=tau_s Zs^2*Ze, with the prefactor 3 ignored. + - 2. The model assumes that the sticker dissociation time tau_s is much larger than tau0*(N/Zs)^2. The shape of the sticker plateau in G' and G'' is therefore not affected by the early-time Rouse relaxation, and is independent of tau0 and N: Including the high frequencies requires tau0 and N as additional parameters. + + * **II. NUMERICAL SETTINGS:** + + - 1. The infinite sums in the double-reptation model are truncated using a numerical tolerance level. + - 2. To transform G(t) to G'(w) and G''(w) a time range with a finite number of samples is defined. The time range and number of samples may affect the results. + """ # --------------------------------------------- # FUNCTION INPUT ft = f.data_table diff --git a/RepTate/tools/ToolMaterialsDatabase.py b/RepTate/tools/ToolMaterialsDatabase.py index 8fee5129..9a861554 100644 --- a/RepTate/tools/ToolMaterialsDatabase.py +++ b/RepTate/tools/ToolMaterialsDatabase.py @@ -51,16 +51,16 @@ QGroupBox, QFormLayout, QInputDialog, - QTreeWidgetItemIterator + QTreeWidgetItemIterator, ) from PySide6.QtCore import QSize, QStandardPaths from PySide6.QtGui import QStandardItem, QFont, QIcon, QAction, QColor, QDoubleValidator from pathlib import Path import RepTate.tools.polymer_data as polymer_data -if getattr(sys, 'frozen', False): +if getattr(sys, "frozen", False): # If the application is run as a bundle, the PyInstaller bootloader - # extends the sys module by a flag frozen=True and sets the app + # extends the sys module by a flag frozen=True and sets the app # path into variable _MEIPASS'. PATH = sys._MEIPASS else: @@ -78,7 +78,9 @@ home_path = str(Path.home()) file_user_database_old = os.path.join(home_path, "user_database.npy") if os.path.exists(file_user_database_old): - materials_user_database_old = np.load(file_user_database_old, allow_pickle=True).item() + materials_user_database_old = np.load( + file_user_database_old, allow_pickle=True + ).item() else: materials_user_database_old = {} @@ -139,12 +141,12 @@ def createFormGroupBox(self, material, parameterdata): def check_chemistry(chem): - """Check if the file contains chemistry. If so, check if the chemistry appears in - the user or general materials database. - Arguments: - - chem {str} -- Chemistry - Returns: - - code {integer} -- -1 (not found) 0 (found in user's) 1 (found in general database) + """Check if the file contains chemistry. If so, check if the chemistry appears in the user or general materials database. + + :param chem: Chemistry + :type chem: str + :return: code -1 (not found) 0 (found in user's) 1 (found in general database) + :rtype: int """ if chem in materials_user_database.keys(): return 0 @@ -325,12 +327,12 @@ def __init__(self, name="", parent_app=None): # Search for the chemistry in the first file of the first dataset (OR CURRENT DATASET?) self.init_chem = None - if len(parent_app.datasets)>0: + if len(parent_app.datasets) > 0: ds = parent_app.datasets[list(parent_app.datasets)[0]] - if len(ds.files)>0: + if len(ds.files) > 0: f = ds.files[0] - if 'chem' in f.file_parameters: - self.init_chem=f.file_parameters['chem'] + if "chem" in f.file_parameters: + self.init_chem = f.file_parameters["chem"] self.update_parameter_table() # self.parent_application.update_all_ds_plots() @@ -436,7 +438,7 @@ def __init__(self, name="", parent_app=None): self.isofrictional.setChecked(True) self.shiftdata = self.tbMwT.addAction( QIcon(":/Icon8/Images/new_icons/icons8-vertical-shift-data.png"), - "Shift all Files in the current Application" + "Shift all Files in the current Application", ) self.verticalLayout.insertWidget(2, self.tbMwT) connection_id = self.isofrictional.triggered.connect(self.handle_vert_and_iso) @@ -444,7 +446,7 @@ def __init__(self, name="", parent_app=None): connection_id = self.shiftdata.triggered.connect(self.handle_shift_data) init_chem_index = self.cbmaterial.findText(self.init_chem) - if init_chem_index>-1: + if init_chem_index > -1: self.cbmaterial.setCurrentIndex(init_chem_index) self.change_material() @@ -454,7 +456,7 @@ def handle_vert_and_iso(self): def handle_shift_data(self): Tr = float(self.editT.text()) chem = self.cbmaterial.currentText() - msg = "Selected T=%g\nSelected material=%s\n"%(Tr, chem) + msg = "Selected T=%g\nSelected material=%s\n" % (Tr, chem) msg += "Do you want to shift all Tables in the current Dataset " msg += "to the chosen temperature using the WLF parameters for the chosen material?" ans = QMessageBox.question( @@ -465,8 +467,8 @@ def handle_shift_data(self): ) if ans != QMessageBox.Yes: return - - # Calculate shift factors + + # Calculate shift factors B1 = self.parameters["B1"].value B2 = self.parameters["B2"].value @@ -487,11 +489,11 @@ def handle_shift_data(self): Tf = f.file_parameters["T"] Mw = f.file_parameters["Mw"] if "iso" in f.file_parameters: - iso_file = (f.file_parameters["iso"].upper() == "TRUE") + iso_file = f.file_parameters["iso"].upper() == "TRUE" elif "isof" in f.file_parameters: - iso_file = (f.file_parameters["isof"].upper() == "TRUE") + iso_file = f.file_parameters["isof"].upper() == "TRUE" else: - iso_file=False + iso_file = False if iso and not iso_file: B2corrected = B2 + CTg / Mw # - 68.7 * dx12 @@ -500,7 +502,13 @@ def handle_shift_data(self): B2corrected = B2 Trcorrected = Tr - aT = np.power(10.0, -B1 * (Tf - Trcorrected) / (B2corrected + Trcorrected) / (B2corrected + Tf)) + aT = np.power( + 10.0, + -B1 + * (Tf - Trcorrected) + / (B2corrected + Trcorrected) + / (B2corrected + Tf), + ) if vert: bT = (1 + alpha * Tf) * (Tr + 273.15) / (1 + alpha * Tr) / (Tf + 273.15) else: @@ -508,27 +516,32 @@ def handle_shift_data(self): # Loop over file type columns for i, c in enumerate(f.file_type.col_names): - if c in ["t", "time"]: # Shift horizontally to the left - f.data_table.data[:, i] = f.data_table.data[:, i]/aT - elif c in ["w"]: # Shift horizontally to the right - f.data_table.data[:, i] = f.data_table.data[:, i]*aT - elif c in ["G'", "G''", "Gt", "sigma_xy", "N1", "sigma"]: # Shift vertically up - f.data_table.data[:, i] = f.data_table.data[:, i]*bT - + if c in ["t", "time"]: # Shift horizontally to the left + f.data_table.data[:, i] = f.data_table.data[:, i] / aT + elif c in ["w"]: # Shift horizontally to the right + f.data_table.data[:, i] = f.data_table.data[:, i] * aT + elif c in [ + "G'", + "G''", + "Gt", + "sigma_xy", + "N1", + "sigma", + ]: # Shift vertically up + f.data_table.data[:, i] = f.data_table.data[:, i] * bT + # Change file parameter T to target Temperature f.file_parameters["T"] = Tr it = QTreeWidgetItemIterator(ds.DataSettreeWidget) while it.value(): - if (it.value().text(0)==f.file_name_short): + if it.value().text(0) == f.file_name_short: for i in range(ds.DataSettreeWidget.columnCount()): if "T" == ds.DataSettreeWidget.headerItem().text(i): it.value().setText(i, str(f.file_parameters["T"])) - it+=1; + it += 1 self.do_plot() - - def change_material(self): selected_material_name = self.cbmaterial.currentText() @@ -624,7 +637,7 @@ def save_usermaterials(self): file_user_database = os.path.join(AppData_path, "user_database.npy") np.save(file_user_database, materials_user_database) msg = "Saved user database in '%s'" % file_user_database - QMessageBox.information(self, 'Saved', msg) + QMessageBox.information(self, "Saved", msg) def copy_material(self): # Dialog to ask for short name. Repeat until the name is not in the user's database or CANCEL @@ -753,13 +766,12 @@ def calculate_stuff(self, line="", file_parameters=[]): CC3 = -1.55 Z = Mw / Me tR = tau_e * Z * Z - tD = 3 * tau_e * Z ** 3 * (1 - 2 * CC1 / np.sqrt(Z) + CC2 / Z + CC3 / Z ** 1.5) + tD = 3 * tau_e * Z**3 * (1 - 2 * CC1 / np.sqrt(Z) + CC2 / Z + CC3 / Z**1.5) tab_data.append(["Z", "%g" % Z]) tab_data.append(["tau_R", "%g" % tR]) tab_data.append(["tau_D", "%g" % tD]) self.Qprint(tab_data) - def calculate(self, x, y, ax=None, color=None, file_parameters=[]): """Calculate some results related to the selected material or the file material""" self.calculate_stuff("", file_parameters) @@ -767,10 +779,10 @@ def calculate(self, x, y, ax=None, color=None, file_parameters=[]): def do_calculate_stuff(self, line=""): """Given the values of Mw (in kDa) and T (in °C), as well as a flag for isofrictional state and vertical shift, it returns some calculations for the current chemistry. -Example: - calculate_stuff 35.4 240 1 1 + Example: + calculate_stuff 35.4 240 1 1 - Mw=35.4 T=240 isofrictional=True verticalshift=True""" + Mw=35.4 T=240 isofrictional=True verticalshift=True""" items = line.split() if len(items) == 4: Mw = float(items[0]) @@ -823,8 +835,8 @@ def do_calculate_stuff(self, line=""): tD = ( 3 * tau_e - * Z ** 3 - * (1 - 2 * CC1 / np.sqrt(Z) + CC2 / Z + CC3 / Z ** 1.5) + * Z**3 + * (1 - 2 * CC1 / np.sqrt(Z) + CC2 / Z + CC3 / Z**1.5) ) self.Qprint("Z = %g" % Z) self.Qprint("tau_R = %g" % tR) @@ -834,7 +846,7 @@ def do_calculate_stuff(self, line=""): print(" Usage: calculate_stuff Mw T isofrictional verticalshift") def calculate_all(self, n, x, y, ax=None, color=None, file_parameters=[]): - """Calculate the tool for all views - In MatDB, only first view is needed """ + """Calculate the tool for all views - In MatDB, only first view is needed""" newxy = [] lenx = 1e9 for i in range(n): diff --git a/docs/source/developers/callgraphGUI.rst b/docs/source/developers/callgraphGUI.rst index 18908ce9..c445bd6e 100644 --- a/docs/source/developers/callgraphGUI.rst +++ b/docs/source/developers/callgraphGUI.rst @@ -28,8 +28,8 @@ The following scheme shows the class inheritance diagram of the most imporatant "QDataSet" [href="../developers/CodeCoreGUI.html#qdataset", target="_top", shape="box", style="rounded,filled"] "QTheory" [href="../developers/CodeCoreGUI.html#qtheory", target="_top", shape="box", style="rounded,filled"] "QTool" [href="../developers/CodeCoreGUI.html#qtool", target="_top", shape="box", style="rounded,filled"] - "QWidget" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qt-5/qwidget.html", target="_top", style="filled"] - "QMainWindow" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qt-5/qmainwindow.html", target="_top", style="filled"] + "QWidget" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html", target="_top", style="filled"] + "QMainWindow" [shape=box,fillcolor=palegreen,href="https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QMainWindow.html", target="_top", style="filled"] "QWidget" -> "QApplicationWindow" [color=green]; "QWidget" -> "QDataSet" [color=green]; "QWidget" -> "QTheory" [color=green];