diff --git a/data/weather/fetchAlcantara.py b/data/weather/fetchAlcantara.py index 2a65d561b..9d210d73a 100644 --- a/data/weather/fetchAlcantara.py +++ b/data/weather/fetchAlcantara.py @@ -2,55 +2,89 @@ c = cdsapi.Client() -years = ['2015', '2016', '2017', '2018'] +years = ["2015", "2016", "2017", "2018"] for year in years: print() print(year) print() c.retrieve( - 'reanalysis-era5-pressure-levels', + "reanalysis-era5-pressure-levels", { - 'product_type':'reanalysis', - 'format':'netcdf', - 'variable':[ - 'geopotential','temperature','u_component_of_wind', - 'v_component_of_wind' + "product_type": "reanalysis", + "format": "netcdf", + "variable": [ + "geopotential", + "temperature", + "u_component_of_wind", + "v_component_of_wind", ], - 'pressure_level':[ - '600', - '650','700','750', - '775','800','825', - '850','875','900', - '925','950','975', - '1000' + "pressure_level": [ + "600", + "650", + "700", + "750", + "775", + "800", + "825", + "850", + "875", + "900", + "925", + "950", + "975", + "1000", ], - 'year':[ - year + "year": [year], + "month": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", ], - 'month':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12' + "day": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", ], - 'day':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12', - '13','14','15', - '16','17','18', - '19','20','21', - '22','23','24', - '25','26','27', - '28','29','30', - '31' - ], - 'time':[ - '00:00','06:00','12:00', - '18:00' - ], - 'area': '1.5/314.25/-3.75/317.25' + "time": ["00:00", "06:00", "12:00", "18:00"], + "area": "1.5/314.25/-3.75/317.25", }, - 'Alcantara_' + year + '_ERA-5.nc') \ No newline at end of file + "Alcantara_" + year + "_ERA-5.nc", + ) diff --git a/data/weather/fetchCLBI.py b/data/weather/fetchCLBI.py index c69face49..dd27c4e8c 100644 --- a/data/weather/fetchCLBI.py +++ b/data/weather/fetchCLBI.py @@ -2,55 +2,89 @@ c = cdsapi.Client() -years = ['2015', '2016', '2017', '2018'] +years = ["2015", "2016", "2017", "2018"] for year in years: print() print(year) print() c.retrieve( - 'reanalysis-era5-pressure-levels', + "reanalysis-era5-pressure-levels", { - 'product_type':'reanalysis', - 'format':'netcdf', - 'variable':[ - 'geopotential','temperature','u_component_of_wind', - 'v_component_of_wind' + "product_type": "reanalysis", + "format": "netcdf", + "variable": [ + "geopotential", + "temperature", + "u_component_of_wind", + "v_component_of_wind", ], - 'pressure_level':[ - '600', - '650','700','750', - '775','800','825', - '850','875','900', - '925','950','975', - '1000' + "pressure_level": [ + "600", + "650", + "700", + "750", + "775", + "800", + "825", + "850", + "875", + "900", + "925", + "950", + "975", + "1000", ], - 'year':[ - year + "year": [year], + "month": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", ], - 'month':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12' + "day": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", ], - 'day':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12', - '13','14','15', - '16','17','18', - '19','20','21', - '22','23','24', - '25','26','27', - '28','29','30', - '31' - ], - 'time':[ - '00:00','06:00','12:00', - '18:00' - ], - 'area': '-4.5/324/-7.5/326.25' + "time": ["00:00", "06:00", "12:00", "18:00"], + "area": "-4.5/324/-7.5/326.25", }, - 'CLBI_' + year + '_ERA-5.nc') \ No newline at end of file + "CLBI_" + year + "_ERA-5.nc", + ) diff --git a/data/weather/fetchSpaceportAmerica.py b/data/weather/fetchSpaceportAmerica.py index b5e5f01dd..9823c2f25 100644 --- a/data/weather/fetchSpaceportAmerica.py +++ b/data/weather/fetchSpaceportAmerica.py @@ -2,55 +2,89 @@ c = cdsapi.Client() -years = ['2015', '2016', '2017', '2018'] +years = ["2015", "2016", "2017", "2018"] for year in years: print() print(year) print() c.retrieve( - 'reanalysis-era5-pressure-levels', + "reanalysis-era5-pressure-levels", { - 'product_type':'reanalysis', - 'format':'netcdf', - 'variable':[ - 'geopotential','temperature','u_component_of_wind', - 'v_component_of_wind' + "product_type": "reanalysis", + "format": "netcdf", + "variable": [ + "geopotential", + "temperature", + "u_component_of_wind", + "v_component_of_wind", ], - 'pressure_level':[ - '600', - '650','700','750', - '775','800','825', - '850','875','900', - '925','950','975', - '1000' + "pressure_level": [ + "600", + "650", + "700", + "750", + "775", + "800", + "825", + "850", + "875", + "900", + "925", + "950", + "975", + "1000", ], - 'year':[ - year + "year": [year], + "month": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", ], - 'month':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12' + "day": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", ], - 'day':[ - '01','02','03', - '04','05','06', - '07','08','09', - '10','11','12', - '13','14','15', - '16','17','18', - '19','20','21', - '22','23','24', - '25','26','27', - '28','29','30', - '31' - ], - 'time':[ - '00:00','06:00','12:00', - '18:00' - ], - 'area': '34.5/252/31.5/254.25' + "time": ["00:00", "06:00", "12:00", "18:00"], + "area": "34.5/252/31.5/254.25", }, - 'SpaceportAmerica_' + year + '_ERA-5.nc') \ No newline at end of file + "SpaceportAmerica_" + year + "_ERA-5.nc", + ) diff --git a/docs/conf.py b/docs/conf.py index 77d49886c..cf117478d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,49 +12,50 @@ # import os import sys -sys.path.insert(0, os.path.abspath('../')) + +sys.path.insert(0, os.path.abspath("../")) # -- Project information ----------------------------------------------------- -project = 'RocketPy' -copyright = '2020, Projeto Jupiter' -author = 'Giovani Hdalgo Ceotto' +project = "RocketPy" +copyright = "2020, Projeto Jupiter" +author = "Giovani Hdalgo Ceotto" # The full version, including alpha/beta/rc tags -release = '0.9.7' +release = "0.9.7" # -- General configuration --------------------------------------------------- -master_doc = 'index' +master_doc = "index" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'nbsphinx', - 'm2r2', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "nbsphinx", + "m2r2", ] # source_suffix = '.rst' # source_suffix = ['.rst', '.md'] # Don't run notebooks -nbsphinx_execute = 'never' +nbsphinx_execute = "never" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] napoleon_numpy_docstring = True -autodoc_member_order = 'bysource' -autoclass_content = 'both' +autodoc_member_order = "bysource" +autoclass_content = "both" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- @@ -62,31 +63,29 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'pydata_sphinx_theme' +html_theme = "pydata_sphinx_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['static'] +html_static_path = ["static"] html_logo = "static/RocketPy_Logo_Black.svg" -html_favicon = 'static/favicon.ico' +html_favicon = "static/favicon.ico" html_theme_options = { - "logo_link": "index", - "github_url": "https://github.com/Projeto-Jupiter/RocketPy", - "collapse_navigation": True, - "show_toc_level": 3, + "logo_link": "index", + "github_url": "https://github.com/Projeto-Jupiter/RocketPy", + "collapse_navigation": True, + "show_toc_level": 3, } html_sidebars = { "**": ["search-field.html", "sidebar-nav-bs.html", "sidebar-ethical-ads.html"] } -html_theme_options = { - "navbar_end": ["navbar-icon-links.html", "search-field.html"] -} +html_theme_options = {"navbar_end": ["navbar-icon-links.html", "search-field.html"]} html_use_modindex = True html_copy_source = False html_domain_indices = False -html_file_suffix = '.html' +html_file_suffix = ".html" -htmlhelp_basename = 'rocketpy' +htmlhelp_basename = "rocketpy" diff --git a/rocketpy/Rocket.py b/rocketpy/Rocket.py index ea88a0bc0..e51aeec96 100644 --- a/rocketpy/Rocket.py +++ b/rocketpy/Rocket.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -__author__ = "Giovani Hidalgo Ceotto, Franz Masatoshi Yuri" +__author__ = "Giovani Hidalgo Ceotto, Franz Masatoshi Yuri, Lucas Azevedo Pezente, Lucas Kierulff Balabram" __copyright__ = "Copyright 20XX, Projeto Jupiter" __license__ = "MIT" @@ -19,7 +19,7 @@ import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm -from numpy import genfromtxt +from numpy import genfromtxt, sqrt from .Function import Function @@ -165,8 +165,12 @@ def __init__( radius, distanceRocketNozzle, distanceRocketPropellant, - powerOffDrag, - powerOnDrag, + powerOffDrag=None, + powerOnDrag=None, + rocket_length=None, + speed_of_sound=340.29, + air_density=1.2250, + dynamic_viscosity=1.7893e-05, ): """Initializes Rocket class, process inertial, geometrical and aerodynamic parameters. @@ -207,11 +211,28 @@ def __init__( information. If int or float is given, it is assumed constant. If callable, string or array is given, it must be a function of Mach number only. - + rocket_length : int, float, optional + Rocket length used in the expressions for estimating drag coefficients + in meters (m). + speed_of_sound : int, float, optional + Speed of sound used for estimating drag coefficients, in + meters per second (m/s). The default value was obtained from the + Environment class at sea level and standard atmosphere. + air_density : int, float, optional + Air density used in the expressions for estimating drag coefficients + in kg/m³. + dynamic_viscosity : int, float, optional + Dynamic viscosity of the fluid used in the expressions for + estimating drag coefficients in Pa s. Returns ------- None """ + # Define relevant physical constants + self.speed_of_sound = speed_of_sound + self.air_density = air_density + self.dynamic_viscosity = dynamic_viscosity + # Define rocket inertia attributes in SI units self.mass = mass self.inertiaI = inertiaI @@ -221,6 +242,10 @@ def __init__( # Define rocket geometrical parameters in SI units self.radius = radius self.area = np.pi * self.radius ** 2 + self.rocket_length = rocket_length + self.nose_length = 0 + self.tail_length = 0 + self.tail_bottom_radius = 0 # Center of mass distance to points of interest self.distanceRocketNozzle = distanceRocketNozzle @@ -246,20 +271,24 @@ def __init__( ) # Define aerodynamic drag coefficients - self.powerOffDrag = Function( - powerOffDrag, - "Mach Number", - "Drag Coefficient with Power Off", - "spline", - "constant", - ) - self.powerOnDrag = Function( - powerOnDrag, - "Mach Number", - "Drag Coefficient with Power On", - "spline", - "constant", - ) + self.power_off_drag_as_input = powerOffDrag is not None + if self.power_off_drag_as_input: + self.powerOffDrag = Function( + powerOffDrag, + "Mach Number", + "Drag Coefficient with Power Off", + "spline", + "constant", + ) + self.power_on_drag_as_input = powerOnDrag is not None + if self.power_on_drag_as_input: + self.powerOnDrag = Function( + powerOnDrag, + "Mach Number", + "Drag Coefficient with Power On", + "spline", + "constant", + ) # Define motor to be used self.motor = motor @@ -278,8 +307,142 @@ def __init__( # Evaluate static margin (even though no aerodynamic surfaces are present yet) self.evaluateStaticMargin() + self.evaluateDragCoefficient() + return None + def evaluateDragCoefficient(self): + """Calculates and returns the drag coefficient as the sum of + the individual drag coefficients at zero angle of attack as + a function of the Mach number. The angle of attack is assumed + to be zero. + + Parameters + ---------- + None + + Returns + ------- + self.drag_coefficient_estimate : Function + Function of mach number expressing the estimate for the drag coefficient, + defined as in the literature. + """ + self.evaluateViscousFrictionCoefficient() + self.evaluateForebodyDragCoefficient() + + self.drag_coefficient_estimate = self.forebody_drag_coefficient + np.sum( + [ + self.aerodynamicSurfaces[i][2] + for i in range(len(self.aerodynamicSurfaces)) + ] + ) + + if self.drag_coefficient_estimate is not None: + self.drag_coefficient_estimate.setInputs("Mach Number") + self.drag_coefficient_estimate.setOutputs("Drag Coefficient") + + if not self.power_off_drag_as_input: + self.powerOffDrag = self.drag_coefficient_estimate + if not self.power_on_drag_as_input: + self.powerOnDrag = self.drag_coefficient_estimate + + return self.drag_coefficient_estimate + + def evaluateViscousFrictionCoefficient(self): + """Calculates and returns the viscous friction coefficient as + a function of the Mach number. The viscous friction coefficient + depends on the Reynolds number and given by a mathematical + expression. Some assumptions for calculating this coefficient + are that the angle of attack equals zero and the speed of sound + is constant and equal to the value passed in init. + + Parameters + ---------- + None + + Returns + ------- + self.viscous_friction_coefficient : Function + Function of mach number expressing the viscous friction coefficient, + defined as in the literature. + """ + + def calculations(mach): + reynolds = ( + self.air_density + * self.speed_of_sound + * self.rocket_length + * mach + / self.dynamic_viscosity + ) + reynolds_critical = 5e5 + + if reynolds <= 1e4: + return 1.328e-2 + elif reynolds <= reynolds_critical: + return 1.328 / (sqrt(reynolds)) + else: + B = reynolds_critical * ( + 0.074 / (reynolds ** (1 / 5)) - (1.328 / (sqrt(reynolds))) + ) + return 0.074 / (reynolds ** (1 / 5)) - (B / reynolds) + + self.viscous_friction_coefficient = Function( + calculations, "Mach Number", "Viscous Friction Coefficient" + ) + + return self.viscous_friction_coefficient + + def evaluateForebodyDragCoefficient(self): + """Calculates and returns the tail drag coefficient as a function + of the Mach number. The tail drag coefficient depends on geometric + parameters such as the total length of the rocket body, the length + of the boat tail, the maximum body diamater, and the diamater of the + rocket base. + + Parameters + ---------- + None + + Returns + ------- + self.forebody_drag_coefficient : Function + Function of mach number expressing the tail drag coefficient, + defined as in the literature. + """ + + # Return something for mock purposes + + def calculations(mach): + forebody_drag_coefficient = ( + ( + 1 + + 60 / ((self.rocket_length / (2 * self.radius)) ** 3) + + 0.00125 + * (self.rocket_length - self.nose_length - self.tail_length) + / self.radius + ) + * ( + 2.7 * self.nose_length / (2 * self.radius) + + 2 + * (self.rocket_length - self.nose_length - self.tail_length) + / self.radius + + 2 + * (1 - self.tail_bottom_radius / self.radius) + * self.tail_length + / (2 * self.radius) + ) + * self.viscous_friction_coefficient(mach) + ) + + return forebody_drag_coefficient + + self.forebody_drag_coefficient = Function( + calculations, "Mach Number", "Forebody Drag Coefficient" + ) + + return self.forebody_drag_coefficient + def evaluateReducedMass(self): """Calculates and returns the rocket's total reduced mass. The reduced mass is defined as the product of the propellant mass @@ -390,7 +553,9 @@ def evaluateStaticMargin(self): # Return self return self - def addTail(self, topRadius, bottomRadius, length, distanceToCM): + def addTail( + self, topRadius, bottomRadius, length, distanceToCM, drag_coefficient=None + ): """Create a new tail or rocket diameter change, storing its parameters as part of the aerodynamicSurfaces list. Its parameters are the axial position along the rocket and its @@ -433,19 +598,36 @@ def addTail(self, topRadius, bottomRadius, length, distanceToCM): else: cpz = distanceToCM + (length / 3) * (1 + (1 - r) / (1 - r ** 2)) + # Save tail length + self.tail_length = length + self.tail_bottom_radius = bottomRadius + # Calculate clalpha clalpha = -2 * (1 - r ** (-2)) * (topRadius / rref) ** 2 cldata = Function( lambda x: clalpha * x, "Alpha (rad)", "Cl", interpolation="linear" ) + # Calculate Cd + if drag_coefficient is None: + drag_coefficient = ( + 0.029 + * ((bottomRadius / self.radius) ** 3) + / (self.evaluateForebodyDragCoefficient() ** 0.5) + ) + drag_coefficient.setInputs("Mach Number") + drag_coefficient.setOutputs("Tail Drag Coefficient") + # Store values as new aerodynamic surface - tail = [(0, 0, cpz), cldata, "Tail"] + tail = [(0, 0, cpz), cldata, drag_coefficient, "Tail"] self.aerodynamicSurfaces.append(tail) # Refresh static margin calculation self.evaluateStaticMargin() + # Refresh Drag coefficient calculation + self.evaluateDragCoefficient() + # Return self return self.aerodynamicSurfaces[-1] @@ -493,6 +675,9 @@ def addNose(self, length, kind, distanceToCM): else: cpz = distanceToCM - k * length + # Save nose cone length + self.nose_length = length + # Calculate clalpha clalpha = 2 cldata = Function( @@ -504,17 +689,35 @@ def addNose(self, length, kind, distanceToCM): ) # Store values - nose = [(0, 0, cpz), cldata, "Nose Cone"] + # Nose cone drag coefficient is given as a constant and equals zero as it has already been accounted for in the forebody. + nose = [ + (0, 0, cpz), + cldata, + Function(lambda mach: 0, "Mach Number", "Nose Cone Drag Coefficient"), + "Nose Cone", + ] self.aerodynamicSurfaces.append(nose) # Refresh static margin calculation self.evaluateStaticMargin() + # Refresh Drag coefficient calculation + self.evaluateDragCoefficient() + # Return self return self.aerodynamicSurfaces[-1] def addFins( - self, n, span, rootChord, tipChord, distanceToCM, radius=0, airfoil=None + self, + n, + span, + rootChord, + tipChord, + distanceToCM, + drag_coefficient=None, + thickness=None, + radius=0, + airfoil=None, ): """Create a fin set, storing its parameters as part of the aerodynamicSurfaces list. Its parameters are the axial position @@ -593,16 +796,6 @@ def addFins( lambda x: clalpha * x, "Alpha (rad)", "Cl", interpolation="linear" ) - # Store values - fin = [(0, 0, cpz), cldata, "Fins"] - self.aerodynamicSurfaces.append(fin) - - # Refresh static margin calculation - self.evaluateStaticMargin() - - # Return self - return self.aerodynamicSurfaces[-1] - else: def cnalfa1(cn): @@ -639,7 +832,7 @@ def cnalfa1(cn): # Import the lift curve as a function of lift values by attack angle read = genfromtxt(airfoil, delimiter=",") - # Aplies number of fins to lift coefficient data + # Applies number of fins to lift coefficient data data = [[cl[0], (n / 2) * cnalfa1(cl[1])] for cl in read] cldata = Function( data, @@ -649,18 +842,35 @@ def cnalfa1(cn): extrapolation="natural", ) - # Takes an approximation to an angular coefficient - clalpha = cldata.differentiate(x=0, dx=1e-2) + # Calculate Cd + if drag_coefficient is None and thickness is not None: + exposed_area = (1 / 2) * (rootChord + tipChord) * span + total_area = exposed_area + self.radius * rootChord + + # In the formula, instead of span, it should be the distance between the + # midpoint of the root and the tip of the fin, but we are using + # span for now. + drag_coefficient = ( + 2 + * self.evaluateViscousFrictionCoefficient() + * (1 + 2 * (thickness / span)) + * (n * (2 * total_area - exposed_area) / (np.pi * self.radius ** 2)) + ) + drag_coefficient.setInputs("Mach Number") + drag_coefficient.setOutputs("Fins Drag Coefficient") - # Store values - fin = [(0, 0, cpz), cldata, "Fins"] - self.aerodynamicSurfaces.append(fin) + # Store values as new aerodynamic surface + fin = [(0, 0, cpz), cldata, drag_coefficient, "Fins"] + self.aerodynamicSurfaces.append(fin) - # Refresh static margin calculation - self.evaluateStaticMargin() + # Refresh static margin calculation + self.evaluateStaticMargin() + + # Refresh Drag coefficient calculation + self.evaluateDragCoefficient() - # Return self - return self.aerodynamicSurfaces[-1] + # Return self + return self.aerodynamicSurfaces[-1] def addParachute( self, name, CdS, trigger, samplingRate=100, lag=0, noise=(0, 0, 0) diff --git a/setup.py b/setup.py index b971b83fa..c4ecc1927 100644 --- a/setup.py +++ b/setup.py @@ -4,14 +4,9 @@ long_description = fh.read() setuptools.setup( - name="rocketpy", + name="rocketpy", version="0.9.8", - install_requires = [ - 'numpy>=1.0', - 'scipy>=1.0', - 'matplotlib>=3.0', - 'requests' - ], + install_requires=["numpy>=1.0", "scipy>=1.0", "matplotlib>=3.0", "requests"], maintainer="RocketPy Developers", author="Giovani Hidalgo Ceotto", author_email="ghceotto@gmail.com", @@ -25,5 +20,5 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], - python_requires='>=3.6', + python_requires=">=3.6", ) diff --git a/tests/test_solidmotor.py b/tests/test_solidmotor.py index f7ba68759..e5fba2175 100644 --- a/tests/test_solidmotor.py +++ b/tests/test_solidmotor.py @@ -16,6 +16,7 @@ nozzleRadius = 33 / 1000 throatRadius = 11 / 1000 + @patch("matplotlib.pyplot.show") def test_motor(mock_show): example_motor = SolidMotor( @@ -36,14 +37,18 @@ def test_motor(mock_show): def test_initilize_motor_asserts_dynamic_values(solid_motor): - grain_vol = grainInitialHeight * (np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2)) + grain_vol = grainInitialHeight * ( + np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2) + ) grain_mass = grain_vol * grainDensity assert solid_motor.maxThrust == 2200.0 assert solid_motor.maxThrustTime == 0.15 assert solid_motor.burnOutTime == burnOut assert solid_motor.totalImpulse == solid_motor.thrust.integral(0, burnOut) - assert solid_motor.averageThrust == solid_motor.thrust.integral(0, burnOut) / burnOut + assert ( + solid_motor.averageThrust == solid_motor.thrust.integral(0, burnOut) / burnOut + ) assert solid_motor.grainInitialVolume == grain_vol assert solid_motor.grainInitialMass == grain_mass assert solid_motor.propellantInitialMass == grainNumber * grain_mass @@ -67,7 +72,9 @@ def test_grain_geometry_progession_asserts_extreme_values(solid_motor): def test_mass_curve_asserts_extreme_values(solid_motor): - grain_vol = grainInitialHeight * (np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2)) + grain_vol = grainInitialHeight * ( + np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2) + ) grain_mass = grain_vol * grainDensity assert np.allclose(solid_motor.mass.getSource()[-1][-1], 0) @@ -100,11 +107,14 @@ def test_burn_area_asserts_extreme_values(solid_motor): def test_evaluate_inertia_I_asserts_extreme_values(solid_motor): - grain_vol = grainInitialHeight * (np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2)) + grain_vol = grainInitialHeight * ( + np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2) + ) grain_mass = grain_vol * grainDensity grainInertiaI_initial = grain_mass * ( - (1 / 4) * (grainOuterRadius ** 2 + grainInitialInnerRadius ** 2) + (1 / 12) * grainInitialHeight ** 2 + (1 / 4) * (grainOuterRadius ** 2 + grainInitialInnerRadius ** 2) + + (1 / 12) * grainInitialHeight ** 2 ) initialValue = (grainNumber - 1) / 2 @@ -120,10 +130,14 @@ def test_evaluate_inertia_I_asserts_extreme_values(solid_motor): def test_evaluate_inertia_Z_asserts_extreme_values(solid_motor): - grain_vol = grainInitialHeight * (np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2)) + grain_vol = grainInitialHeight * ( + np.pi * (grainOuterRadius ** 2 - grainInitialInnerRadius ** 2) + ) grain_mass = grain_vol * grainDensity - grainInertiaZ_initial = grain_mass * (1 / 2.0) * (grainInitialInnerRadius ** 2 + grainOuterRadius ** 2) + grainInertiaZ_initial = ( + grain_mass * (1 / 2.0) * (grainInitialInnerRadius ** 2 + grainOuterRadius ** 2) + ) assert np.allclose( solid_motor.inertiaZ.getSource()[0][-1], grainInertiaZ_initial, atol=0.01 @@ -174,7 +188,7 @@ def tests_export_eng_asserts_exported_values_correct(solid_motor): "0", "{:2.3}".format(grain_mass), "{:2.3}".format(grain_mass), - "RocketPy" + "RocketPy", ] assert dataPoints == [