Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ def __init__(self, args):
motor = self.fileManager.getCurrentMotor()
simulationResult = motor.runSimulation()
for alert in simulationResult.alerts:
print('{} ({}, {}): {}'.format(motorlib.simResult.alertLevelNames[alert.level],
motorlib.simResult.alertTypeNames[alert.type],
alert.location,
alert.description))
print('{} ({}, {}): {}'.format(alert.level,
alert.type,
alert.location,
alert.description))
print()
if '-o' in args:
with open(args[args.index('-o') + 1], 'w') as outputFile:
Expand Down
Empty file added motorlib/enums/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions motorlib/enums/inhibitedEnds.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from enum import Enum


# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class InhibitedEnds(str, Enum):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should try to take this opportunity to make a migration that converts "top"->"forward" and "bottom"->"aft".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can see this would only be present on the .ric files. Is this assumption correct?
So that the necessary migration can be done I don't want to miss anything.

Any chance for a more complex .ric file to be made available so I can use it for testing too?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay! Here's a kind of silly example that uses all 4 inhibitor configurations:
example.ric.txt
and what it looks like on my end, so you can verify nothing changed:
image

NEITHER = 'Neither'
TOP = 'Top'
BOTTOM = 'Bottom'
BOTH = 'Both'
11 changes: 11 additions & 0 deletions motorlib/enums/multiValueChannels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from enum import Enum


# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class MultiValueChannels(str, Enum):
MASS = 'mass'
MASS_FLOW = 'massFlow'
MASS_FLUX = 'massFlux'
REGRESSION = 'regression'
WEB = 'web'
10 changes: 10 additions & 0 deletions motorlib/enums/simAlertLevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from enum import Enum


# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class SimAlertLevel(str, Enum):
"""Levels of severity for sim alerts"""
ERROR = 'Error'
WARNING = 'Warning'
MESSAGE = 'Message'
9 changes: 9 additions & 0 deletions motorlib/enums/simAlertType.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from enum import Enum

# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class SimAlertType(str, Enum):
"""Types of sim alerts"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Types of sim alerts"""
"""Types of simulation alerts"""

GEOMETRY = 'Geometry'
CONSTRAINT = 'Constraint'
VALUE = 'Value'
13 changes: 13 additions & 0 deletions motorlib/enums/singleValueChannels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from enum import Enum


# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class SingleValueChannels(str, Enum):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is recommended to use singular form for classes AFAIK

TIME = 'time'
KN = 'kn'
PRESSURE = 'pressure'
FORCE = 'force'
VOLUME_LOADING = 'volumeLoading'
EXIT_PRESSURE = 'exitPressure'
D_THROAT = 'dThroat'
83 changes: 83 additions & 0 deletions motorlib/enums/unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from enum import Enum


# Python 3.11 supports `StrEnum` that would make this a bit more concise to write
# https://docs.python.org/3/library/enum.html#enum.StrEnum
class Unit(str, Enum):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might make sense to create a base class Unit and add unit specific classes as well , like ImpulseUnit, LenghtUnit etc. Wil l help to be specific and more constistent

# Angle
DEGREES = 'deg'

# Burn Rate Coefficient
METER_PER_SECOND_PASCAL_TO_THE_POWER_OF_N = 'm/(s*Pa^n)'
INCH_PER_SECOND_POUND_PER_SQUARE_INCH_TO_THE_POWER_OF_N = 'in/(s*psi^n)'

# Density
KILOGRAM_PER_CUBIC_METER = 'kg/m^3'
POUND_PER_CUBIC_INCH = 'lb/in^3'
GRAM_PER_CUBIC_CENTIMETER = 'g/cm^3'

# Force
NEWTON = 'N'
POUND_FORCE = 'lbf'

# Impulse
NEWTON_SECOND = 'Ns'
POUND_FORCE_SECOND = 'lbfs'

# Length
METER = 'm'
CENTIMETER = 'cm'
MILLIMETER = 'mm'
INCH = 'in'
FOOT = 'ft'

# Mass Flow
KILOGRAM_PER_SECOND = 'kg/s'
POUND_PER_SECOND = 'lb/s'
GRAM_PER_SECOND = 'g/s'

# Mass Flux
KILOGRAM_PER_SQUARE_METER_PER_SECOND = 'kg/(m^2*s)'
POUND_PER_SQUARE_INCH_PER_SECOND = 'lb/(in^2*s)'

# Mass
KILOGRAM = 'kg'
GRAM = 'g'
POUND = 'lb'
OUNCE = 'oz'
GRAM_PER_MOLE = 'g/mol'

# Nozzle Erosion Coefficient
METER_PER_SECOND_PASCAL = 'm/(s*Pa)'
METER_PER_SECOND_MEGAPASCAL = 'm/(s*MPa)'
THOUSANDTH_INCH_PER_SECOND_POUND_PER_SQUARE_INCH = 'thou/(s*psi)'

# Nozzle Slag Coefficient
METER_PASCAL_PER_SECOND = '(m*Pa)/s'
METER_MEGAPASCAL_PER_SECOND = '(m*MPa)/s'
INCH_POUND_PER_SQUARE_INCH_PER_SECOND = '(in*psi)/s'

# Pressure
PASCAL = 'Pa'
MEGAPASCAL = 'MPa'
POUND_PER_SQUARE_INCH = 'psi'

# Temperature
KELVIN = 'K'

# Time
SECOND = 's'

# Velocity
METER_PER_SECOND = 'm/s'
CENTIMETER_PER_SECOND = 'cm/s'
MILLIMETER_PER_SECOND = 'mm/s'
FOOT_PER_SECOND = 'ft/s'
INCH_PER_SECOND = 'in/s'

# Volume
CUBIC_METER = 'm^3'
CUBIC_CENTIMETER = 'cm^3'
CUBIC_MILLIMETER = 'mm^3'
CUBIC_INCH = 'in^3'
CUBIC_FOOT = 'ft^3'
35 changes: 21 additions & 14 deletions motorlib/grain.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
from scipy import interpolate

from . import geometry
from .simResult import SimAlert, SimAlertLevel, SimAlertType
from .enums.inhibitedEnds import InhibitedEnds
from .enums.simAlertLevel import SimAlertLevel
from .enums.simAlertType import SimAlertType
from .enums.unit import Unit
from .simResult import SimAlert
from .properties import FloatProperty, EnumProperty, PropertyCollection

class Grain(PropertyCollection):
Expand All @@ -19,8 +23,8 @@ class Grain(PropertyCollection):
geomName = None
def __init__(self):
super().__init__()
self.props['diameter'] = FloatProperty('Diameter', 'm', 0, 1)
self.props['length'] = FloatProperty('Length', 'm', 0, 3)
self.props['diameter'] = FloatProperty('Diameter', Unit.METER, 0, 1)
self.props['length'] = FloatProperty('Length', Unit.METER, 0, 3)

def getVolumeSlice(self, regDist, dRegDist):
"""Returns the amount of propellant volume consumed as the grain regresses from a distance of 'regDist' to
Expand Down Expand Up @@ -67,9 +71,9 @@ def getRegressedLength(self, regDist):
endPos = self.getEndPositions(regDist)
return endPos[1] - endPos[0]

def getDetailsString(self, lengthUnit='m'):
def getDetailsString(self, Unit=Unit.METER):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is a good practice to keep args lowercase

"""Returns a short string describing the grain, formatted using the units that is passed in"""
return 'Length: {}'.format(self.props['length'].dispFormat(lengthUnit))
return 'Length: {}'.format(self.props['length'].dispFormat(Unit))

@abstractmethod
def simulationSetup(self, config):
Expand Down Expand Up @@ -103,17 +107,20 @@ class PerforatedGrain(Grain):
geomName = 'perfGrain'
def __init__(self):
super().__init__()
self.props['inhibitedEnds'] = EnumProperty('Inhibited ends', ['Neither', 'Top', 'Bottom', 'Both'])
self.props['inhibitedEnds'] = EnumProperty('Inhibited ends', [InhibitedEnds.NEITHER,
InhibitedEnds.TOP,
InhibitedEnds.BOTTOM,
InhibitedEnds.BOTH])
self.wallWeb = 0 # Max distance from the core to the wall

def getEndPositions(self, regDist):
if self.props['inhibitedEnds'].getValue() == 'Neither': # Neither
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.NEITHER:
return (regDist, self.props['length'].getValue() - regDist)
if self.props['inhibitedEnds'].getValue() == 'Top': # Top
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.TOP:
return (0, self.props['length'].getValue() - regDist)
if self.props['inhibitedEnds'].getValue() == 'Bottom': # Bottom
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.BOTTOM:
return (regDist, self.props['length'].getValue())
if self.props['inhibitedEnds'].getValue() == 'Both':
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.BOTH:
return (0, self.props['length'].getValue())
# The enum should prevent this from even being raised, but to cover the case where it somehow gets set wrong
raise ValueError('Invalid number of faces inhibited')
Expand All @@ -135,7 +142,7 @@ def getCoreSurfaceArea(self, regDist):

def getWebLeft(self, regDist):
wallLeft = self.wallWeb - regDist
if self.props['inhibitedEnds'].getValue() == 'Both':
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.BOTH:
return wallLeft
lengthLeft = self.getRegressedLength(regDist)
return min(lengthLeft, wallLeft)
Expand All @@ -145,9 +152,9 @@ def getSurfaceAreaAtRegression(self, regDist):
coreArea = self.getCoreSurfaceArea(regDist)

exposedFaces = 2
if self.props['inhibitedEnds'].getValue() == 'Top' or self.props['inhibitedEnds'].getValue() == 'Bottom':
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.TOP or self.props['inhibitedEnds'].getValue() == InhibitedEnds.BOTTOM:
exposedFaces = 1
if self.props['inhibitedEnds'].getValue() == 'Both':
if self.props['inhibitedEnds'].getValue() == InhibitedEnds.BOTH:
exposedFaces = 0

return coreArea + (exposedFaces * faceArea)
Expand All @@ -172,7 +179,7 @@ def getMassFlux(self, massIn, dTime, regDist, dRegDist, position, density):
# If a position in the grain is queried, the mass flow is the input mass, from the top face,
# and from the tube up to the point. The diameter is the core.
if position <= endPos[1]:
if self.props['inhibitedEnds'].getValue() in ('Top', 'Both'):
if self.props['inhibitedEnds'].getValue() in (InhibitedEnds.TOP, InhibitedEnds.BOTH):
top = 0
countedCoreLength = position
else:
Expand Down
13 changes: 8 additions & 5 deletions motorlib/grains/bates.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import skfmm
from skimage import measure

from ..enums.simAlertLevel import SimAlertLevel
from ..enums.simAlertType import SimAlertType
from ..enums.unit import Unit
from ..grain import PerforatedGrain
from .. import geometry
from ..simResult import SimAlert, SimAlertLevel, SimAlertType
from ..simResult import SimAlert
from ..properties import FloatProperty

class BatesGrain(PerforatedGrain):
Expand All @@ -15,7 +18,7 @@ class BatesGrain(PerforatedGrain):
geomName = "BATES"
def __init__(self):
super().__init__()
self.props['coreDiameter'] = FloatProperty('Core Diameter', 'm', 0, 1)
self.props['coreDiameter'] = FloatProperty('Core Diameter', Unit.METER, 0, 1)

def simulationSetup(self, config):
self.wallWeb = (self.props['diameter'].getValue() - self.props['coreDiameter'].getValue()) / 2
Expand All @@ -28,9 +31,9 @@ def getFaceArea(self, regDist):
inner = geometry.circleArea(self.props['coreDiameter'].getValue() + (2 * regDist))
return outer - inner

def getDetailsString(self, lengthUnit='m'):
return 'Length: {}, Core: {}'.format(self.props['length'].dispFormat(lengthUnit),
self.props['coreDiameter'].dispFormat(lengthUnit))
def getDetailsString(self, Unit=Unit.METER):
return 'Length: {}, Core: {}'.format(self.props['length'].dispFormat(Unit),
self.props['coreDiameter'].dispFormat(Unit))

def getGeometryErrors(self):
errors = super().getGeometryErrors()
Expand Down
13 changes: 8 additions & 5 deletions motorlib/grains/cGrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import numpy as np

from ..enums.simAlertLevel import SimAlertLevel
from ..enums.simAlertType import SimAlertType
from ..enums.unit import Unit
from ..grain import FmmGrain
from ..properties import FloatProperty
from ..simResult import SimAlert, SimAlertLevel, SimAlertType
from ..simResult import SimAlert

class CGrain(FmmGrain):
"""Defines a C grain, which is a cylindrical grain with a single slot taken out. The slot is a rectangular section
Expand All @@ -13,8 +16,8 @@ class CGrain(FmmGrain):
geomName = 'C Grain'
def __init__(self):
super().__init__()
self.props['slotWidth'] = FloatProperty('Slot width', 'm', 0, 1)
self.props['slotOffset'] = FloatProperty('Slot offset', 'm', -1, 1)
self.props['slotWidth'] = FloatProperty('Slot width', Unit.METER, 0, 1)
self.props['slotOffset'] = FloatProperty('Slot offset', Unit.METER, -1, 1)

self.props['slotOffset'].setValue(0)

Expand All @@ -24,8 +27,8 @@ def generateCoreMap(self):

self.coreMap[np.logical_and(np.abs(self.mapY) < slotWidth / 2, self.mapX > slotOffset)] = 0

def getDetailsString(self, lengthUnit='m'):
return 'Length: {}'.format(self.props['length'].dispFormat(lengthUnit))
def getDetailsString(self, Unit=Unit.METER):
return 'Length: {}'.format(self.props['length'].dispFormat(Unit))

def getGeometryErrors(self):
errors = super().getGeometryErrors()
Expand Down
Loading