diff --git a/motorlib/grain.py b/motorlib/grain.py index b7fe02b..8713aff 100644 --- a/motorlib/grain.py +++ b/motorlib/grain.py @@ -185,7 +185,7 @@ def getFaceImage(self, mapDim): """Returns an image of the grain's cross section, with resolution (mapDim, mapDim).""" @abstractmethod - def getRegressionData(self, mapDim, numContours=15): + def getRegressionData(self, mapDim, numContours=15, coreBlack=True): """Returns a tuple that includes a grain face image as described in 'getFaceImage', a regression map where color maps to regression depth, a list of contours (lists of (x,y) points in image space) of equal regression depth, and a list of corresponding contour lengths. The contours are equally spaced diff --git a/motorlib/motor.py b/motorlib/motor.py index 2d0fe27..66cd80c 100644 --- a/motorlib/motor.py +++ b/motorlib/motor.py @@ -1,7 +1,5 @@ """Conains the motor class and a supporting configuration property collection.""" -import numpy as np - from .grains import grainTypes from .nozzle import Nozzle from .propellant import Propellant @@ -120,7 +118,7 @@ def calcIdealThrustCoeff(self, chamberPres): def calcForce(self, chamberPres): """Calculates the force of the motor at a given regression depth per grain. Calculates pressure by default, but can also use a value passed in. This method uses a combination of the techniques described in these - resources to adjust the thrust coefficient: https://apps.dtic.mil/dtic/tr/fulltext/u2/a099791.pdf and + resources to adjust the thrust coefficient: https://apps.dtic.mil/dtic/tr/fulltext/u2/a099791.pdf and http://rasaero.com/dloads/Departures%20from%20Ideal%20Performance.pdf.""" thrustCoeffIdeal = self.calcIdealThrustCoeff(chamberPres) divLoss = self.nozzle.getDivergenceLosses() @@ -138,7 +136,6 @@ def runSimulation(self, callback=None): using the pressure to determine how the motor will regress in the given timestep at the current pressure. This process is repeated and regression tracked until all grains have burned out, when the results and any warnings are returned.""" - ambientPressure = self.config.getProperty('ambPressure') burnoutWebThres = self.config.getProperty('burnoutWebThres') burnoutThrustThres = self.config.getProperty('burnoutThrustThres') dTime = self.config.getProperty('timestep') @@ -199,8 +196,8 @@ def runSimulation(self, callback=None): desc = 'Initial port/throat ratio of ' + str(round(ratio, 3)) + ' was less than ' + str(minAllowed) simRes.addAlert(SimAlert(SimAlertLevel.WARNING, SimAlertType.CONSTRAINT, desc, 'N/A')) - # Perform timesteps, 0.01 converts the threshold to a % - while len(simRes.channels['time'].getData()) == 1 or simRes.channels['force'].getLast() > burnoutThrustThres * 0.01 * simRes.channels['force'].getMax(): + # Perform timesteps + while simRes.shouldContinueSim(burnoutThrustThres): # Calculate regression massFlow = 0 perGrainMass = [0 for grain in self.grains] diff --git a/motorlib/simResult.py b/motorlib/simResult.py index 0c278e0..ace802e 100644 --- a/motorlib/simResult.py +++ b/motorlib/simResult.py @@ -204,6 +204,14 @@ def getAlertsByLevel(self, level): out.append(alert) return out + def shouldContinueSim(self, thrustThres): + """Returns if the simulation should continue based on the thrust from the last timestep.""" + # With only one data point, there is nothing to compare + if len(self.channels['time'].getData()) == 1: + return True + # Otherwise perform the comparison. 0.01 converts the threshold to a % + return self.channels['force'].getLast() > thrustThres * 0.01 * self.channels['force'].getMax() + def getCSV(self, pref=None, exclude=[]): """Returns a string that contains a CSV of the simulated data. Preferences can be passed in to set units that the values will be converted to. All log channels are included unless their names are in the include diff --git a/motorlib/units.py b/motorlib/units.py index 0d47b87..c6ae923 100644 --- a/motorlib/units.py +++ b/motorlib/units.py @@ -74,7 +74,7 @@ def convertAll(quantities, originUnit, destUnit): convRate = getConversion(originUnit, destUnit) return [q * convRate for q in quantities] -def format(quantity, originUnit, destUnit, places=3): +def convFormat(quantity, originUnit, destUnit, places=3): """Takes in a quantity in originUnit, converts it to destUnit and outputs a rounded and formatted string that includes the unit appended to the end.""" num = round(convert(quantity, originUnit, destUnit), places) diff --git a/uilib/widgets/grainImageWidget.py b/uilib/widgets/grainImageWidget.py index 754f0e4..6a26317 100644 --- a/uilib/widgets/grainImageWidget.py +++ b/uilib/widgets/grainImageWidget.py @@ -1,20 +1,13 @@ -from PyQt5.QtWidgets import QMainWindow, QLabel, QSizePolicy, QApplication +from PyQt5.QtWidgets import QLabel from PyQt5.QtGui import QPixmap, QImage -from PyQt5.QtCore import Qt import numpy as np -import motorlib - class GrainImageWidget(QLabel): - def __init__(self): - super().__init__() - def showImage(self, image): np.ma.set_fill_value(image, 0) image = np.logical_not(image.filled()) image = image.astype(np.uint8) * 255 height, width = image.shape - bytesPerLine = width qImg = QImage(image.data, width, height, QImage.Format_Grayscale8) pixmap = QPixmap(qImg) self.setPixmap(pixmap) diff --git a/uilib/widgets/grainSelector.py b/uilib/widgets/grainSelector.py index 200af45..a3e325e 100644 --- a/uilib/widgets/grainSelector.py +++ b/uilib/widgets/grainSelector.py @@ -1,8 +1,6 @@ from PyQt5.QtWidgets import QGroupBox, QCheckBox, QRadioButton, QVBoxLayout from PyQt5.QtCore import pyqtSignal -import motorlib - class GrainSelector(QGroupBox): checksChanged = pyqtSignal() @@ -15,13 +13,13 @@ def __init__(self, parent): self.setTitle("Grains") def resetChecks(self): - for check in range(0, len(self.checks)): + for _ in range(0, len(self.checks)): self.layout().removeWidget(self.checks[-1]) self.checks[-1].deleteLater() del self.checks[-1] def setupChecks(self, simRes, multiselect): - for gid, grain in enumerate(simRes.motor.grains): + for gid, _ in enumerate(simRes.motor.grains): checkTitle = "Grain " + str(gid + 1) if multiselect: check = QCheckBox(checkTitle) diff --git a/uilib/widgets/graphWidget.py b/uilib/widgets/graphWidget.py index b4de8cc..3512b07 100644 --- a/uilib/widgets/graphWidget.py +++ b/uilib/widgets/graphWidget.py @@ -63,4 +63,4 @@ def resetPlot(self): self.draw() def saveImage(self, filename): - self.figure.savefig(filename) \ No newline at end of file + self.figure.savefig(filename) diff --git a/uilib/widgets/motorEditor.py b/uilib/widgets/motorEditor.py index 63f71c7..d6f75dc 100644 --- a/uilib/widgets/motorEditor.py +++ b/uilib/widgets/motorEditor.py @@ -1,4 +1,4 @@ -from PyQt5.QtWidgets import QLabel, QListWidget, QSizePolicy +from PyQt5.QtWidgets import QLabel import motorlib.grain import motorlib.nozzle @@ -24,8 +24,7 @@ def __init__(self, parent): self.nozzlePreview.hide() self.stats.addWidget(self.nozzlePreview) - self.nozzle = True - self.grainClass = None + self.objType = None def propertyUpdate(self): if issubclass(self.objType, motorlib.nozzle.Nozzle): diff --git a/uilib/widgets/resultsWidget.py b/uilib/widgets/resultsWidget.py index 048f71f..82269c5 100644 --- a/uilib/widgets/resultsWidget.py +++ b/uilib/widgets/resultsWidget.py @@ -1,9 +1,4 @@ -from threading import Thread - -from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.figure import Figure from PyQt5.QtWidgets import QWidget, QHeaderView, QLabel -from PyQt5.QtCore import pyqtSignal import motorlib @@ -96,14 +91,14 @@ def updateGrainTab(self): currentImpulse = self.simResult.getImpulse(index) remainingImpulse = self.simResult.getImpulse() - currentImpulse impUnit = self.preferences.getUnit('Ns') - self.ui.labelImpulseProgress.setText(motorlib.units.format(currentImpulse, 'Ns', impUnit)) - self.ui.labelImpulseRemaining.setText(motorlib.units.format(remainingImpulse, 'Ns', impUnit)) + self.ui.labelImpulseProgress.setText(motorlib.units.convFormat(currentImpulse, 'Ns', impUnit)) + self.ui.labelImpulseRemaining.setText(motorlib.units.convFormat(remainingImpulse, 'Ns', impUnit)) currentMass = self.simResult.getPropellantMass(index) remainingMass = self.simResult.getPropellantMass() - currentMass massUnit = self.preferences.getUnit('kg') - self.ui.labelMassProgress.setText(motorlib.units.format(remainingMass, 'kg', massUnit)) - self.ui.labelMassRemaining.setText(motorlib.units.format(currentMass, 'kg', massUnit)) + self.ui.labelMassProgress.setText(motorlib.units.convFormat(remainingMass, 'kg', massUnit)) + self.ui.labelMassRemaining.setText(motorlib.units.convFormat(currentMass, 'kg', massUnit)) currentISP = self.simResult.getISP(index) self.ui.labelISPProgress.setText(str(round(currentISP, 3)) + ' s')