diff --git a/README.rst b/README.rst index 1ee6503..9833179 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,23 @@ -scikit-aero -=========== +.. image:: doc/source/_static/logo.png + :align: center + +.. image:: https://img.shields.io/maintenance/yes/2019.svg?style=for-the-badge + :target: https://github.com/AeroPython/scikit-aero + :alt: Maintenance + +.. image:: https://img.shields.io/pypi/l/scikit-aero.svg?style=for-the-badge + :target: https://github.com/AeroPython/scikit-aero/blob/master/COPYING + :alt: License + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest&style=for-the-badge + :target: https://aeropython.github.io/scikit-aero/ + :alt: Docs + +.. image:: https://img.shields.io/badge/mailing%20list-groups.io-8cbcd1.svg?style=for-the-badge + :target: aeropython@groups.io + :alt: Email + +| :Name: scikit-aero :Description: Aeronautical engineering calculations in Python @@ -7,6 +25,11 @@ scikit-aero :Author: AeroPython Team :Version: 0.2.dev0 +| + +Scikit-aero +----------- + scikit-aero is a Python package for various aeronautical engineering calculations. It is based on several existing Python packages on the field, but intends to provide pythonic syntax, use of SI units and full NumPy arrays @@ -41,23 +64,29 @@ Future Usage ===== -Atmosphere properties:: +Atmosphere properties: + +.. code-block:: python - >>> from skaero.atmosphere import coesa - >>> h, T, p, rho = coesa.table(1000) # Altitude by default, 1 km + from skaero.atmosphere import coesa + h, T, p, rho = coesa.table(1000) # Altitude by default, 1 km -Inverse computations allowed with density and pressure, which are monotonic:: +Inverse computations allowed with density and pressure, which are monotonic: - >>> h, T, p, rho = coesa.table(p=101325) # Pressure of 1 atm +.. code-block:: python -Gas dynamics calculations:: + h, T, p, rho = coesa.table(p=101325) # Pressure of 1 atm - >>> from skaero.gasdynamics import isentropic, shocks - >>> fl = isentropic.IsentropicFlow(gamma=1.4) - >>> p = 101325 * fl.p_p0(M=0.8) # Static pressure given total pressure of 1 atm - >>> ns = shocks.Shock(M_1=2.5, gamma=1.4) - >>> M_2 = ns.M_2 # Mach number behind a normal shock wave - >>> os = shocks.Shock(M_1=3.0, theta=np.radians(25), weak=True) +Gas dynamics calculations: + +.. code-block:: python + + from skaero.gasdynamics import isentropic, shocks + fl = isentropic.IsentropicFlow(gamma=1.4) + p = 101325 * fl.p_p0(M=0.8) # Static pressure given total pressure of 1 atm + ns = shocks.Shock(M_1=2.5, gamma=1.4) + M_2 = ns.M_2 # Mach number behind a normal shock wave + os = shocks.Shock(M_1=3.0, theta=np.radians(25), weak=True) Dependencies ============ @@ -87,9 +116,17 @@ version of IPython and its dependencies. Install ======= -This package uses distutils. To install, execute as usual:: +To install just execute: + +.. code-block:: bash + + $ pip install scikit-aero - $ python setup.py install +If you want to install the package in development mode, please execute: + +.. code-block:: bash + + $ pip install --editable /path_to_scikit-aero It is recommended that you **never ever use sudo** with distutils, pip, setuptools and friends in Linux because you might seriously break your @@ -109,13 +146,17 @@ Testing ======= scikit-aero recommends py.test for running the test suite. Running from the -top directory:: +top directory: + +.. code-block:: bash + + $ pytest - $ py.test +To test code coverage, make sure you install `py.test-cov`_ extension and run: -To test code coverage, make sure you install `py.test-cov`_ extension and run:: +.. code-block:: bash - $ py.test --cov skaero/ + $ pytest --cov skaero/ .. _`py.test-cov`: https://pypi.python.org/pypi/pytest-cov diff --git a/doc/source/_static/logo.png b/doc/source/_static/logo.png new file mode 100644 index 0000000..2b0551a Binary files /dev/null and b/doc/source/_static/logo.png differ diff --git a/doc/source/about_skaero.rst b/doc/source/about_skaero.rst new file mode 100644 index 0000000..5fbf0b1 --- /dev/null +++ b/doc/source/about_skaero.rst @@ -0,0 +1,50 @@ +About skaero +============ + +The package collects different algorithms that solve for classical aeronautical +or aerospace problems such us finding the isentropic relations for a given Mach +number, atmospheric properties for a given altitude and many others. + +History +------- + +.. _Juan Luis Cano: https://github.com/Juanlu001 +.. _Guilles Aouizerate: https://github.com/Gillu13 +.. _Javier J. Gutiérrez: https://github.com/javierj +.. _Jorge Martínez: https://github.com/jorgepiloto + +The project was started by `Juan Luis Cano`_. With the contribution of +`Guilles Aouizerate`_ and `Javier J. Gutiérrez`_ more implementations to the +package were done such us tests implementation and extension of the COESA model +up to 86km. + +After some time, the project was no longer mantained till `Jorge Martínez`_ +started working again on `skaero`: a Python package for aeronautical and +aerospace computations. + +Related Software +---------------- + +.. _AeroCalc: https://pypi.org/project/AeroCalc/0.11/ +.. _Matlab Aerospace Toolbox: www.mathworks.com/help/aerotbx/index.html +.. _PDAS: http://www.pdas.com/index.html + +Many different softwares that enable the user to make similar computations are +listed down: + +- `AeroCalc`_ +- `Matlab Aerospace Toolbox`_ +- `PDAS`_ + +In future versions +------------------ + +We are still working in this software and therefore issues and pull requests are +welcome. Some ideas for the moment are: + +- Improve project documentation +- Implement GOST russian atmosphere +- Include more examples on how to use the package + +Please, take a look to the official repository in GitHub if you want to make +a contribution to the project. diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index 6a676a0..0000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,22 +0,0 @@ -API Reference -============= - -Complete reference of the API. - -Isentropic ----------- - -.. automodule:: skaero.gasdynamics.isentropic - :members: - -Shocks ------- - -.. automodule:: skaero.gasdynamics.shocks - :members: - -Atmosphere ----------- - -.. automodule:: skaero.atmosphere.coesa - :members: diff --git a/doc/source/conf.py b/doc/source/conf.py index 606baae..65ebc24 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -27,11 +27,11 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', + 'sphinx.ext.napoleon', 'sphinx.ext.todo', - 'sphinx.ext.coverage', 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode'] + 'sphinx.ext.intersphinx', + 'nbsphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -97,7 +97,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'solar' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/examples/Convergent-divergent nozzle.ipynb b/doc/source/examples/Convergent-divergent nozzle.ipynb similarity index 100% rename from examples/Convergent-divergent nozzle.ipynb rename to doc/source/examples/Convergent-divergent nozzle.ipynb diff --git a/examples/Oblique shocks chart.ipynb b/doc/source/examples/Oblique shocks chart.ipynb similarity index 100% rename from examples/Oblique shocks chart.ipynb rename to doc/source/examples/Oblique shocks chart.ipynb diff --git a/examples/exercise_18.png b/doc/source/examples/exercise_18.png similarity index 100% rename from examples/exercise_18.png rename to doc/source/examples/exercise_18.png diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst new file mode 100644 index 0000000..9139e24 --- /dev/null +++ b/doc/source/examples/index.rst @@ -0,0 +1,12 @@ +Examples +======== + +Different examples on how to use `skaero` have been implemented in the following +Jupyter Notebook files. If you have more examples on how to use this package, +please start a pull request in the official repository. + +.. toctree:: + :maxdepth: 1 + + Convergent-divergent nozzle.ipynb + Oblique shocks chart.ipynb diff --git a/examples/oblique_shock_graph.png b/doc/source/examples/oblique_shock_graph.png similarity index 100% rename from examples/oblique_shock_graph.png rename to doc/source/examples/oblique_shock_graph.png diff --git a/doc/source/index.rst b/doc/source/index.rst index 62c363c..7abdde4 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,21 +1,23 @@ -scikit-aero -=========== +.. image:: _static/logo.png + :align: center + +| + +What is **scikit-aero**? +======================== **scikit-aero** is a Python package for various aeronautical engineering calculations. It is based on several existing Python packages on the field, but intends to provide pythonic syntax, use of SI units and full NumPy arrays support among other things. scikit-aero is licensed under the BSD license. + Contents: .. toctree:: :maxdepth: 2 - api - -Indices and tables -================== + about_skaero + examples/index + skaero/index -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/skaero/atmosphere/index.rst b/doc/source/skaero/atmosphere/index.rst new file mode 100644 index 0000000..6455365 --- /dev/null +++ b/doc/source/skaero/atmosphere/index.rst @@ -0,0 +1,13 @@ +Atmosphere module +================= + +The atmosphere modeled by **scikit-aero** follows the U.S. Committee on +Extension to the Standard Atmosphere (COESA), also known as U.S Standard +Atmosphere 1976. + +This atmosphere rotates with the Earth. The air is assumed to follow all the +laws for perfect gas and the hydrostatics equations. Therefore a realation +between temperature, pressure and density can be stablished with geopotential. + +.. automodule:: skaero.atmosphere.coesa + :members: diff --git a/doc/source/skaero/gasdynamics/index.rst b/doc/source/skaero/gasdynamics/index.rst new file mode 100644 index 0000000..57cbf31 --- /dev/null +++ b/doc/source/skaero/gasdynamics/index.rst @@ -0,0 +1,14 @@ +Gas dynamics +============ + +This module holds different submodules related to gas dynamics, in particular +with ideal flow computations. + +.. toctree:: + :maxdepth: 2 + + isentropic + nozzles + shocks + + diff --git a/doc/source/skaero/gasdynamics/isentropic.rst b/doc/source/skaero/gasdynamics/isentropic.rst new file mode 100644 index 0000000..f6f09df --- /dev/null +++ b/doc/source/skaero/gasdynamics/isentropic.rst @@ -0,0 +1,9 @@ +Isentropic +========== + +This submodule holds the class `IdealFlow` and `PrandtlMeyerExpansion`, since +both of them are considered under the study of ideal flow dynamics. + +.. automodule:: skaero.gasdynamics.isentropic + :members: + diff --git a/doc/source/skaero/gasdynamics/nozzles.rst b/doc/source/skaero/gasdynamics/nozzles.rst new file mode 100644 index 0000000..4ca479f --- /dev/null +++ b/doc/source/skaero/gasdynamics/nozzles.rst @@ -0,0 +1,5 @@ +Nozzles +======= + +.. automodule:: skaero.gasdynamics.nozzles + :members: diff --git a/doc/source/skaero/gasdynamics/shocks.rst b/doc/source/skaero/gasdynamics/shocks.rst new file mode 100644 index 0000000..6c8895b --- /dev/null +++ b/doc/source/skaero/gasdynamics/shocks.rst @@ -0,0 +1,5 @@ +Shocks +====== + +.. automodule:: skaero.gasdynamics.shocks + :members: diff --git a/doc/source/skaero/geometry/index.rst b/doc/source/skaero/geometry/index.rst new file mode 100644 index 0000000..5183bde --- /dev/null +++ b/doc/source/skaero/geometry/index.rst @@ -0,0 +1,5 @@ +Geometry Module +=============== + +.. automodule:: skaero.geometry.joukowsky + :members: diff --git a/doc/source/skaero/index.rst b/doc/source/skaero/index.rst new file mode 100644 index 0000000..9a96490 --- /dev/null +++ b/doc/source/skaero/index.rst @@ -0,0 +1,11 @@ +API skaero +========== + +All the modules that `skaero` contains are listed down. + +.. toctree:: + :maxdepth: 2 + + atmosphere/index + gasdynamics/index + geometry/index diff --git a/doc/source/solar/NEWS.txt b/doc/source/solar/NEWS.txt deleted file mode 100644 index d9743ee..0000000 --- a/doc/source/solar/NEWS.txt +++ /dev/null @@ -1,32 +0,0 @@ -News -==== - -1.3 ---- -* Release date: 2012-11-01. -* Source Code Pro is now used for code samples. -* Reduced font size of pre elements. -* Horizontal rule for header elements. -* HTML pre contents are now wrapped (no scrollbars). -* Changed permalink color from black to a lighter one. - -1.2 ---- -* Release date: 2012-10-03. -* Style additional admonition levels. -* Increase padding for navigation links (minor). -* Add shadow for admonition items (minor). - -1.1 ---- -* Release date: 2012-09-05. -* Add a new background. -* Revert font of headings to Open Sans Light. -* Darker color for h3 - h6. -* Removed dependency on solarized dark pygments style. -* Nice looking scrollbars for pre element. - -1.0 ---- -* Release date: 2012-08-24. -* Initial release. diff --git a/doc/source/solar/README.rst b/doc/source/solar/README.rst deleted file mode 100644 index caeedbd..0000000 --- a/doc/source/solar/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -Solar theme for Python Sphinx -============================= -Solar is an attempt to create a theme for Sphinx based on the `Solarized `_ color scheme. - -Preview -------- -http://vimalkumar.in/sphinx-themes/solar - -Download --------- -Released versions are available from http://github.com/vkvn/sphinx-themes/downloads - -Installation ------------- -#. Extract the archive. -#. Modify ``conf.py`` of an existing Sphinx project or create new project using ``sphinx-quickstart``. -#. Change the ``html_theme`` parameter to ``solar``. -#. Change the ``html_theme_path`` to the location containing the extracted archive. - -License -------- -`GNU General Public License `_. - -Credits -------- -Modified from the default Sphinx theme -- Sphinxdoc - -Background pattern from http://subtlepatterns.com. diff --git a/doc/source/solar/layout.html b/doc/source/solar/layout.html deleted file mode 100644 index 6c57110..0000000 --- a/doc/source/solar/layout.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "basic/layout.html" %} - -{%- block doctype -%} - -{%- endblock -%} - -{%- block extrahead -%} - - -{%- endblock -%} - -{# put the sidebar before the body #} -{% block sidebar1 %}{{ sidebar() }}{% endblock %} -{% block sidebar2 %}{% endblock %} - -{%- block footer %} - -{%- endblock %} diff --git a/doc/source/solar/static/solar.css b/doc/source/solar/static/solar.css deleted file mode 100644 index 6333339..0000000 --- a/doc/source/solar/static/solar.css +++ /dev/null @@ -1,344 +0,0 @@ -/* solar.css - * Modified from sphinxdoc.css of the sphinxdoc theme. -*/ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Open Sans', sans-serif; - font-size: 14px; - line-height: 150%; - text-align: center; - color: #002b36; - padding: 0; - margin: 0 auto; - width: 960px; - -moz-box-shadow: 0px 0px 10px #93a1a1; - -webkit-box-shadow: 0px 0px 10px #93a1a1; - box-shadow: 0px 0px 10px #93a1a1; - background: url("subtle_dots.png") repeat; - -} - -div.document { - background-color: #fcfcfc; - text-align: left; - background-repeat: repeat-x; -} - -div.bodywrapper { - margin: 0 240px 0 0; - border-right: 1px dotted #eee8d5; -} - -div.body { - background-color: white; - margin: 0; - padding: 0.5em 20px 20px 20px; -} - -div.related { - font-size: 1em; - background: #002b36; - color: #839496; - padding: 5px 0px; -} - -div.related ul { - height: 2em; - margin: 2px; -} - -div.related ul li { - margin: 0; - padding: 0; - height: 2em; - float: left; -} - -div.related ul li.right { - float: right; - margin-right: 5px; -} - -div.related ul li a { - margin: 0; - padding: 2px 5px; - line-height: 2em; - text-decoration: none; - color: #839496; -} - -div.related ul li a:hover { - background-color: #073642; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; -} - -div.sphinxsidebarwrapper { - padding: 0; -} - -div.sphinxsidebar { - margin: 0; - padding: 0.5em 15px 15px 0; - width: 210px; - float: right; - font-size: 0.9em; - text-align: left; -} - -div.sphinxsidebar h3, div.sphinxsidebar h4 { - margin: 1em 0 0.5em 0; - font-size: 1em; - padding: 0.7em; - background-color: #eeeff1; -} - -div.sphinxsidebar h3 a { - color: #2E3436; -} - -div.sphinxsidebar ul { - padding-left: 1.5em; - margin-top: 7px; - padding: 0; - line-height: 150%; - color: #586e75; -} - -div.sphinxsidebar ul ul { - margin-left: 20px; -} - -div.sphinxsidebar input { - border: 1px solid #eee8d5; -} - -div.footer { - background-color: #93a1a1; - color: #eee; - padding: 3px 8px 3px 0; - clear: both; - font-size: 0.8em; - text-align: right; -} - -div.footer a { - color: #eee; - text-decoration: none; -} - -/* -- body styles ----------------------------------------------------------- */ - -p { - margin: 0.8em 0 0.5em 0; -} - -div.body a, div.sphinxsidebarwrapper a { - color: #268bd2; - text-decoration: none; -} - -div.body a:hover, div.sphinxsidebarwrapper a:hover { - border-bottom: 1px solid #268bd2; -} - -h1, h2, h3, h4, h5, h6 { - font-family: "Open Sans", sans-serif; - font-weight: 300; -} - -h1 { - margin: 0; - padding: 0.7em 0 0.3em 0; - line-height: 1.2em; - color: #002b36; - text-shadow: #eee 0.1em 0.1em 0.1em; -} - -h2 { - margin: 1.3em 0 0.2em 0; - padding: 0 0 10px 0; - color: #073642; - border-bottom: 1px solid #eee; -} - -h3 { - margin: 1em 0 -0.3em 0; - padding-bottom: 5px; -} - -h3, h4, h5, h6 { - color: #073642; - border-bottom: 1px dotted #eee; -} - -div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { - color: #657B83!important; -} - -h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { - display: none; - margin: 0 0 0 0.3em; - padding: 0 0.2em 0 0.2em; - color: #aaa!important; -} - -h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, -h5:hover a.anchor, h6:hover a.anchor { - display: inline; -} - -h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, -h5 a.anchor:hover, h6 a.anchor:hover { - color: #777; - background-color: #eee; -} - -a.headerlink { - color: #c60f0f!important; - font-size: 1em; - margin-left: 6px; - padding: 0 4px 0 4px; - text-decoration: none!important; -} - -a.headerlink:hover { - background-color: #ccc; - color: white!important; -} - - -cite, code, tt { - font-family: 'Source Code Pro', monospace; - font-size: 0.9em; - letter-spacing: 0.01em; - background-color: #eeeff2; - font-style: normal; -} - -hr { - border: 1px solid #eee; - margin: 2em; -} - -.highlight { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; -} - -pre { - font-family: 'Source Code Pro', monospace; - font-style: normal; - font-size: 0.9em; - letter-spacing: 0.015em; - line-height: 120%; - padding: 0.7em; - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} - -pre a { - color: inherit; - text-decoration: underline; -} - -td.linenos pre { - padding: 0.5em 0; -} - -div.quotebar { - background-color: #f8f8f8; - max-width: 250px; - float: right; - padding: 2px 7px; - border: 1px solid #ccc; -} - -div.topic { - background-color: #f8f8f8; -} - -table { - border-collapse: collapse; - margin: 0 -0.5em 0 -0.5em; -} - -table td, table th { - padding: 0.2em 0.5em 0.2em 0.5em; -} - -div.admonition { - font-size: 0.9em; - margin: 1em 0 1em 0; - border: 1px solid #eee; - background-color: #f7f7f7; - padding: 0; - -moz-box-shadow: 0px 8px 6px -8px #93a1a1; - -webkit-box-shadow: 0px 8px 6px -8px #93a1a1; - box-shadow: 0px 8px 6px -8px #93a1a1; -} - -div.admonition p { - margin: 0.5em 1em 0.5em 1em; - padding: 0.2em; -} - -div.admonition pre { - margin: 0.4em 1em 0.4em 1em; -} - -div.admonition p.admonition-title -{ - margin: 0; - padding: 0.2em 0 0.2em 0.6em; - color: white; - border-bottom: 1px solid #eee8d5; - font-weight: bold; - background-color: #268bd2; -} - -div.warning p.admonition-title, -div.important p.admonition-title { - background-color: #cb4b16; -} - -div.hint p.admonition-title, -div.tip p.admonition-title { - background-color: #859900; -} - -div.caution p.admonition-title, -div.attention p.admonition-title, -div.danger p.admonition-title, -div.error p.admonition-title { - background-color: #dc322f; -} - -div.admonition ul, div.admonition ol { - margin: 0.1em 0.5em 0.5em 3em; - padding: 0; -} - -div.versioninfo { - margin: 1em 0 0 0; - border: 1px solid #eee; - background-color: #DDEAF0; - padding: 8px; - line-height: 1.3em; - font-size: 0.9em; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; -} diff --git a/doc/source/solar/static/solarized-dark.css b/doc/source/solar/static/solarized-dark.css deleted file mode 100644 index 6ebb945..0000000 --- a/doc/source/solar/static/solarized-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* solarized dark style for solar theme */ - -/*style pre scrollbar*/ -pre::-webkit-scrollbar, .highlight::-webkit-scrollbar { - height: 0.5em; - background: #073642; -} - -pre::-webkit-scrollbar-thumb { - border-radius: 1em; - background: #93a1a1; -} - -/* pygments style */ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #002B36!important; color: #93A1A1 } -.highlight .c { color: #586E75 } /* Comment */ -.highlight .err { color: #93A1A1 } /* Error */ -.highlight .g { color: #93A1A1 } /* Generic */ -.highlight .k { color: #859900 } /* Keyword */ -.highlight .l { color: #93A1A1 } /* Literal */ -.highlight .n { color: #93A1A1 } /* Name */ -.highlight .o { color: #859900 } /* Operator */ -.highlight .x { color: #CB4B16 } /* Other */ -.highlight .p { color: #93A1A1 } /* Punctuation */ -.highlight .cm { color: #586E75 } /* Comment.Multiline */ -.highlight .cp { color: #859900 } /* Comment.Preproc */ -.highlight .c1 { color: #586E75 } /* Comment.Single */ -.highlight .cs { color: #859900 } /* Comment.Special */ -.highlight .gd { color: #2AA198 } /* Generic.Deleted */ -.highlight .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #DC322F } /* Generic.Error */ -.highlight .gh { color: #CB4B16 } /* Generic.Heading */ -.highlight .gi { color: #859900 } /* Generic.Inserted */ -.highlight .go { color: #93A1A1 } /* Generic.Output */ -.highlight .gp { color: #93A1A1 } /* Generic.Prompt */ -.highlight .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #CB4B16 } /* Generic.Subheading */ -.highlight .gt { color: #93A1A1 } /* Generic.Traceback */ -.highlight .kc { color: #CB4B16 } /* Keyword.Constant */ -.highlight .kd { color: #268BD2 } /* Keyword.Declaration */ -.highlight .kn { color: #859900 } /* Keyword.Namespace */ -.highlight .kp { color: #859900 } /* Keyword.Pseudo */ -.highlight .kr { color: #268BD2 } /* Keyword.Reserved */ -.highlight .kt { color: #DC322F } /* Keyword.Type */ -.highlight .ld { color: #93A1A1 } /* Literal.Date */ -.highlight .m { color: #2AA198 } /* Literal.Number */ -.highlight .s { color: #2AA198 } /* Literal.String */ -.highlight .na { color: #93A1A1 } /* Name.Attribute */ -.highlight .nb { color: #B58900 } /* Name.Builtin */ -.highlight .nc { color: #268BD2 } /* Name.Class */ -.highlight .no { color: #CB4B16 } /* Name.Constant */ -.highlight .nd { color: #268BD2 } /* Name.Decorator */ -.highlight .ni { color: #CB4B16 } /* Name.Entity */ -.highlight .ne { color: #CB4B16 } /* Name.Exception */ -.highlight .nf { color: #268BD2 } /* Name.Function */ -.highlight .nl { color: #93A1A1 } /* Name.Label */ -.highlight .nn { color: #93A1A1 } /* Name.Namespace */ -.highlight .nx { color: #93A1A1 } /* Name.Other */ -.highlight .py { color: #93A1A1 } /* Name.Property */ -.highlight .nt { color: #268BD2 } /* Name.Tag */ -.highlight .nv { color: #268BD2 } /* Name.Variable */ -.highlight .ow { color: #859900 } /* Operator.Word */ -.highlight .w { color: #93A1A1 } /* Text.Whitespace */ -.highlight .mf { color: #2AA198 } /* Literal.Number.Float */ -.highlight .mh { color: #2AA198 } /* Literal.Number.Hex */ -.highlight .mi { color: #2AA198 } /* Literal.Number.Integer */ -.highlight .mo { color: #2AA198 } /* Literal.Number.Oct */ -.highlight .sb { color: #586E75 } /* Literal.String.Backtick */ -.highlight .sc { color: #2AA198 } /* Literal.String.Char */ -.highlight .sd { color: #93A1A1 } /* Literal.String.Doc */ -.highlight .s2 { color: #2AA198 } /* Literal.String.Double */ -.highlight .se { color: #CB4B16 } /* Literal.String.Escape */ -.highlight .sh { color: #93A1A1 } /* Literal.String.Heredoc */ -.highlight .si { color: #2AA198 } /* Literal.String.Interpol */ -.highlight .sx { color: #2AA198 } /* Literal.String.Other */ -.highlight .sr { color: #DC322F } /* Literal.String.Regex */ -.highlight .s1 { color: #2AA198 } /* Literal.String.Single */ -.highlight .ss { color: #2AA198 } /* Literal.String.Symbol */ -.highlight .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #268BD2 } /* Name.Variable.Class */ -.highlight .vg { color: #268BD2 } /* Name.Variable.Global */ -.highlight .vi { color: #268BD2 } /* Name.Variable.Instance */ -.highlight .il { color: #2AA198 } /* Literal.Number.Integer.Long */ diff --git a/doc/source/solar/static/subtle_dots.png b/doc/source/solar/static/subtle_dots.png deleted file mode 100644 index bb2d611..0000000 Binary files a/doc/source/solar/static/subtle_dots.png and /dev/null differ diff --git a/doc/source/solar/theme.conf b/doc/source/solar/theme.conf deleted file mode 100644 index d8fc2f3..0000000 --- a/doc/source/solar/theme.conf +++ /dev/null @@ -1,4 +0,0 @@ -[theme] -inherit = basic -stylesheet = solar.css -pygments_style = none diff --git a/skaero/atmosphere/coesa.py b/skaero/atmosphere/coesa.py index babb1ee..a5e9329 100644 --- a/skaero/atmosphere/coesa.py +++ b/skaero/atmosphere/coesa.py @@ -1,29 +1,9 @@ # coding: utf-8 """ -COESA model. +.. _U.S. 1976 Standard Atmosphere: http://ntrs.nasa.gov/search.jsp?R=19770009539 -Routines --------- - -table(h, kind='geopotential') -temperature(h, kind='geopotential') -pressure(h, kind='geopotential') -density(h, kind='geopotential') - -Examples --------- ->>> from skaero.atmosphere import coesa ->>> h, T0, p0, rho0 = coesa.table(1000) ->>> T1 = coesa.temperature(1000) ->>> p1 = coesa.pressure(1000) ->>> rho1 = coesa.density(1000) - -Notes ------ -Based on the U.S. 1976 Standard Atmosphere. - -.. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=19770009539 +COESA model, based on the `U.S. 1976 Standard Atmosphere`_. """ @@ -34,23 +14,23 @@ from skaero.atmosphere import util -# Constants and values : the following parameters are extracted from Notes -# reference (mainly Chap. 1.2.). Naming is consistent. WARNING : Some of these +# Constants and values : the following parameters are extracted from Notes +# reference (mainly Chap. 1.2.). Naming is consistent. WARNING : Some of these # values are not exactly consistent with the 2010 CODATA Recommended Values of -# the Fundamental Physical constants that you can find for example in the +# the Fundamental Physical constants that you can find for example in the # scipy.constants module # gas constant -Rs = 8.31432 # N m / (mol K), WARNING : different from the the 2010 CODATA +Rs = 8.31432 # N m / (mol K), WARNING : different from the the 2010 CODATA # Recommended Values of the Fundamental Physical Constants # set of geopotential heights from table 2 of Notes reference -H = np.array([0., 11.0, 20.0, 32., 47., 51., 71.00, 84.85205])*1e3 # m +H = np.array([0.0, 11.0, 20.0, 32.0, 47.0, 51.0, 71.00, 84.85205]) * 1e3 # m # set of molecular-scale temperature gradients from table 2 of Notes reference -LM = np.array([-6.5, 0., 1., 2.8, 0., -2.8, -2., 0.])*1e-3 # K / m +LM = np.array([-6.5, 0.0, 1.0, 2.8, 0.0, -2.8, -2.0, 0.0]) * 1e-3 # K / m -f_LM = interpolate.interp1d(H, LM, kind='zero') +f_LM = interpolate.interp1d(H, LM, kind="zero") # K, standard sea-level temperature T_0 = 288.15 # K @@ -58,40 +38,73 @@ # mean molecular-weight at sea-level M_0 = 28.9644e-3 # kg / mol -# set of geopotential heights from table 8 of Notes reference -H2 = np.array([0., 79005.7, 79493.3, 79980.8, 80468.2, 80955.7, 81443.0, - 81930.2, 82417.3, 82904.4, 83391.4, 83878.4, 84365.2, - 84852.05]) # m +# set of geopotential heights from table 8 of Notes reference +H2 = np.array( + [ + 0.0, + 79005.7, + 79493.3, + 79980.8, + 80468.2, + 80955.7, + 81443.0, + 81930.2, + 82417.3, + 82904.4, + 83391.4, + 83878.4, + 84365.2, + 84852.05, + ] +) # m # set of molecular weight ratios from table 8 of Notes reference -M_o_M0 = np.array([1., 1., 0.999996, 0.999989, 0.999971, 0.999941, 0.999909, - 0.999870, 0.999829, 0.999786, 0.999741, 0.999694, 0.999641, - 0.999579]) # - - +M_o_M0 = np.array( + [ + 1.0, + 1.0, + 0.999996, + 0.999989, + 0.999971, + 0.999941, + 0.999909, + 0.999870, + 0.999829, + 0.999786, + 0.999741, + 0.999694, + 0.999641, + 0.999579, + ] +) # - + f_M_o_M0 = interpolate.interp1d(H2, M_o_M0) # set of pressures and temperatures (initialization) P = np.array([constants.atm]) # Pa -TM = np.array([T_0]) # K +TM = np.array([T_0]) # K for k in range(1, len(H)): # from eq. [23] of Notes reference - TM = np.append(TM, TM[-1]+f_LM(H[k-1])*(H[k]-H[k-1])) - if f_LM(H[k-1]) == 0.: + TM = np.append(TM, TM[-1] + f_LM(H[k - 1]) * (H[k] - H[k - 1])) + if f_LM(H[k - 1]) == 0.0: # from eq. [33b] of Notes reference - P = np.append(P, P[-1]*np.exp(-constants.g*M_0*(H[k]-H[k-1])/ - (Rs*TM[-2]))) + P = np.append( + P, P[-1] * np.exp(-constants.g * M_0 * (H[k] - H[k - 1]) / (Rs * TM[-2])) + ) else: # from eq. [33a] of Notes reference - P = np.append(P, P[-1]*(TM[-2]/(TM[-1]))**(constants.g*M_0/ - (Rs*f_LM(H[k-1])))) + P = np.append( + P, + P[-1] * (TM[-2] / (TM[-1])) ** (constants.g * M_0 / (Rs * f_LM(H[k - 1]))), + ) -f_TM = interpolate.interp1d(H, TM, kind='zero') -f_P = interpolate.interp1d(H, P, kind='zero') -f_H = interpolate.interp1d(H, H, kind='zero') +f_TM = interpolate.interp1d(H, TM, kind="zero") +f_P = interpolate.interp1d(H, P, kind="zero") +f_H = interpolate.interp1d(H, H, kind="zero") -def table(x, kind='geopotential'): +def table(x, kind="geopotential"): """Computes table of COESA atmosphere properties. Returns temperature, pressure and density COESA values at the given @@ -102,8 +115,7 @@ def table(x, kind='geopotential'): x : array_like Geopotential or geometric altitude (depending on kind) given in meters. kind : str - Specifies the kind of interpolation as altitude x ('geopotential' or -'geometric'). Default is 'geopotential' + Specifies the kind of interpolation as altitude x ('geopotential' or 'geometric'). Default is 'geopotential' Returns ------- @@ -116,148 +128,151 @@ def table(x, kind='geopotential'): rho : array_like Density in kilograms per cubic meter. - Notes - ----- + Note + ---- Based on the U.S. 1976 Standard Atmosphere. - .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=19770009539 - """ - + # check the kind of altitude and raise an exception if necessary - if kind == 'geopotential': + if kind == "geopotential": alt = x - elif kind == 'geometric': + elif kind == "geometric": alt = util.geometric_to_geopotential(x) else: - raise ValueError("%s is unsupported: Use either geopotential or " - "geometric." % kind) - + raise ValueError( + "%s is unsupported: Use either geopotential or " "geometric." % kind + ) + h = np.asarray(alt) - + # check if altitude is out of bound and raise an exception if necessary - if (hH[-1]).any(): - raise ValueError("the given altitude x is out of bound, this module is " - "currently only valid for a geometric altitude between 0. and 86000. m") - - # K, molecule-scale temperature from eq. [23] of Notes reference - tm = f_TM(h) + f_LM(h)*(h-f_H(h)) + if (h < H[0]).any() or (h > H[-1]).any(): + raise ValueError( + "the given altitude x is out of bound, this module is " + "currently only valid for a geometric altitude between 0. and 86000. m" + ) + + # K, molecule-scale temperature from eq. [23] of Notes reference + tm = f_TM(h) + f_LM(h) * (h - f_H(h)) # K, absolute temperature from eq. [22] of Notes reference - T = tm*f_M_o_M0(h) - + T = tm * f_M_o_M0(h) + if h.shape: # if h is not a 0-d array (like a scalar) # Pa, intialization of the pressure vector p = np.zeros(len(h)) - # points of h for which the molecular-scale temperature gradient is zero - zero_gradient = (f_LM(h)==0.) + # points of h for which the molecular-scale temperature gradient is + # zero + zero_gradient = f_LM(h) == 0.0 - # points of h for which the molecular-scale temperature gradient is not + # points of h for which the molecular-scale temperature gradient is not # zero - not_zero_gradient = (f_LM(h)!=0.) + not_zero_gradient = f_LM(h) != 0.0 # Pa, pressure from eq. [33b] of Notes reference - p[zero_gradient] = f_P(h[zero_gradient])*np.exp(-constants.g*M_0* - (h[zero_gradient] - f_H(h[zero_gradient]))/(Rs*f_TM(h[zero_gradient]))) + p[zero_gradient] = f_P(h[zero_gradient]) * np.exp( + -constants.g + * M_0 + * (h[zero_gradient] - f_H(h[zero_gradient])) + / (Rs * f_TM(h[zero_gradient])) + ) # Pa, pressure from eq. [33a] of Notes reference - p[not_zero_gradient] = f_P(h[not_zero_gradient])*(f_TM(h[not_zero_gradient])/(f_TM(h[not_zero_gradient]) + f_LM(h[not_zero_gradient])*(h[not_zero_gradient]-f_H(h[not_zero_gradient]))))**(constants.g*M_0/(Rs*f_LM(h[not_zero_gradient]))) + p[not_zero_gradient] = f_P(h[not_zero_gradient]) * ( + f_TM(h[not_zero_gradient]) + / ( + f_TM(h[not_zero_gradient]) + + f_LM(h[not_zero_gradient]) + * (h[not_zero_gradient] - f_H(h[not_zero_gradient])) + ) + ) ** (constants.g * M_0 / (Rs * f_LM(h[not_zero_gradient]))) - else: - if f_LM(h)==0: + else: + if f_LM(h) == 0: # Pa, pressure from eq. [33b] of Notes reference - p = f_P(h)*np.exp(-constants.g*M_0*(h-f_H(h))/(Rs*f_TM(h))) + p = f_P(h) * np.exp(-constants.g * M_0 * (h - f_H(h)) / (Rs * f_TM(h))) else: # Pa, pressure from eq. [33a] of Notes reference - p = f_P(h)*(f_TM(h)/(f_TM(h)+f_LM(h)*(h-f_H(h))))**(constants.g*M_0/(Rs*f_LM(h))) - - rho = p*M_0/(Rs*tm) # kg / m^3, mass density - + p = f_P(h) * (f_TM(h) / (f_TM(h) + f_LM(h) * (h - f_H(h)))) ** ( + constants.g * M_0 / (Rs * f_LM(h)) + ) + + rho = p * M_0 / (Rs * tm) # kg / m^3, mass density + return alt, T, p, rho -def temperature(x, kind='geopotential'): - """Computes air temperature for a given altitude using the U.S. standard -atmosphere model +def temperature(x, kind="geopotential"): + """Computes air temperature for a given altitude using the U.S. standard atmosphere model Parameters ---------- x : array_like Geopotential or geometric altitude (depending on kind) given in meters. kind : str - Specifies the kind of interpolation as altitude x ('geopotential' or -'geometric'). Default is 'geopotential' + Specifies the kind of interpolation as altitude x ('geopotential' or 'geometric'). Default is 'geopotential' Returns ------- T : array_like Temperature in Kelvin. - Notes - ----- + Note + ---- Based on the U.S. 1976 Standard Atmosphere. - .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=19770009539 - """ - + T = table(x, kind)[1] return T -def pressure(x, kind='geopotential'): - """Computes absolute pressure for a given altitude using the U.S. standard -atmosphere model +def pressure(x, kind="geopotential"): + """Computes absolute pressure for a given altitude using the U.S. standard atmosphere model. Parameters ---------- x : array_like Geopotential or geometric altitude (depending on kind) given in meters. kind : str - Specifies the kind of interpolation as altitude x ('geopotential' or -'geometric'). Default is 'geopotential' + Specifies the kind of interpolation as altitude x ('geopotential' or 'geometric'). Default is 'geopotential' Returns ------- P : array_like Pressure in Pascal. - Notes - ----- + Note + ---- Based on the U.S. 1976 Standard Atmosphere. - .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=19770009539 - """ - + p = table(x, kind)[2] - return p + return p -def density(x, kind='geopotential'): - """Computes air mass density for a given altitude using the U.S. standard -atmosphere model +def density(x, kind="geopotential"): + """Computes air mass density for a given altitude using the U.S. standar atmosphere model. Parameters ---------- x : array_like Geopotential or geometric altitude (depending on kind) given in meters. kind : str - Specifies the kind of interpolation as altitude x ('geopotential' or -'geometric'). Default is 'geopotential' + Specifies the kind of interpolation as altitude x ('geopotential' or 'geometric'). Default is 'geopotential' Returns ------- rho : array_like Density in kilograms per cubic meter. - Notes - ----- + Note + ---- Based on the U.S. 1976 Standard Atmosphere. - .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=19770009539 - """ rho = table(x, kind)[3] diff --git a/skaero/atmosphere/util.py b/skaero/atmosphere/util.py index 77efd82..56345ff 100644 --- a/skaero/atmosphere/util.py +++ b/skaero/atmosphere/util.py @@ -12,6 +12,7 @@ # effective earth's radius R_Earth = 6356.7660e3 # m + def geometric_to_geopotential(z): """Returns geopotential altitude from geometric altitude. @@ -47,10 +48,9 @@ def geopotential_to_geometric(h): Notes ----- - Based on eq. 19 of the U.S. 1976 Standard Atmosphere. + Based on eq. 19 of the `U.S. 1976 Standard Atmosphere`_. - .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=1977\ -0009539 + .. _`U.S. 1976 Standard Atmosphere`: http://ntrs.nasa.gov/search.jsp?R=1977\0009539 """ diff --git a/skaero/gasdynamics/isentropic.py b/skaero/gasdynamics/isentropic.py index 8a59deb..d4de3d6 100644 --- a/skaero/gasdynamics/isentropic.py +++ b/skaero/gasdynamics/isentropic.py @@ -1,26 +1,4 @@ -# coding: utf-8 - -""" -Isentropic relations. - -Routines --------- -mach_angle(M) -prandtl_meyer_function(M, fl=None) -mach_from_area_ratio(fl, A_Astar) - -Classes -------- -IsentropicFlow(gamma) -PrandtlMeyerExpansion(M_1, nu, fl=None) - -Examples --------- ->>> from skaero.gasdynamics import isentropic ->>> fl = IsentropicFlow(gamma=1.4) ->>> _, M = isentropic.mach_from_area_ratio(2.5, fl) - -""" +""" Isentropic properties. """ from __future__ import division, absolute_import @@ -31,7 +9,11 @@ def mach_angle(M): - """Returns Mach angle given supersonic Mach number. + r"""Returns Mach angle given supersonic Mach number. + + .. math:: + + \mu = \arcsin{\left ( \frac{1}{M} \right )} Parameters ---------- @@ -97,7 +79,7 @@ def mach_from_area_ratio(A_Astar, fl=None): def mach_from_nu(nu, in_radians=True, gamma=1.4): - """Computes the Mach number given a Prandtl-Meyer angle, :math:`\nu`. + r"""Computes the Mach number given a Prandtl-Meyer angle, :math:`\nu`. Uses the relation between Mach number and Prandtl-Meyer angle for isentropic flow, to iteratively compute and return the Mach number. @@ -126,10 +108,11 @@ def mach_from_nu(nu, in_radians=True, gamma=1.4): if not in_radians: nu = np.radians(nu) - nu_max = np.pi / 2. * (np.sqrt((gamma + 1.) / (gamma - 1.)) - 1) - if(nu <= 0.0 or nu >= nu_max): - raise ValueError("Prandtl-Meyer angle must be between (0, %f) radians." - % nu_max) + nu_max = np.pi / 2.0 * (np.sqrt((gamma + 1.0) / (gamma - 1.0)) - 1) + if nu <= 0.0 or nu >= nu_max: + raise ValueError( + "Prandtl-Meyer angle must be between (0, %f) radians." % nu_max + ) eq = implicit(PrandtlMeyerExpansion.nu) M = sp.optimize.newton(eq, 2.0, args=(nu,)) @@ -138,9 +121,15 @@ def mach_from_nu(nu, in_radians=True, gamma=1.4): class IsentropicFlow(object): - """Class representing an isentropic flow. + """Class representing an isentropic gas flow. + + Isentropic flow is characterized by: + + * Viscous and heat conductivity effects are negligible. + * No chemical or radioactive heat production. """ + def __init__(self, gamma=1.4): """Constructor of IsentropicFlow. @@ -153,7 +142,10 @@ def __init__(self, gamma=1.4): self.gamma = gamma def p_p0(self, M): - """Pressure ratio from Mach number. + r"""Pressure ratio from Mach number. + + .. math:: + \left ( \frac{P}{P_{0}} \right ) = \left ( \frac{T}{T_{0}} \right )^{\frac{\gamma}{(\gamma - 1)}} Parameters ---------- @@ -166,12 +158,17 @@ def p_p0(self, M): Pressure ratio. """ + M = np.asanyarray(M) p_p0 = self.T_T0(M) ** (self.gamma / (self.gamma - 1)) + return p_p0 def rho_rho0(self, M): - """Density ratio from Mach number. + r"""Density ratio from Mach number. + + .. math:: + \left ( \frac{\rho}{\rho_{0}} \right ) = \left ( \frac{T}{T_{0}} \right )^{\frac{1}{(\gamma - 1)}} Parameters ---------- @@ -184,12 +181,16 @@ def rho_rho0(self, M): Density ratio. """ + M = np.asanyarray(M) rho_rho0 = self.T_T0(M) ** (1 / (self.gamma - 1)) return rho_rho0 def T_T0(self, M): - """Temperature ratio from Mach number. + r"""Temperature ratio from Mach number. + + .. math:: + \left ( \frac{T}{T_{0}} \right ) = \left (1 + \frac{\gamma - 1}{2}M^{2} \right )^{-1} Parameters ---------- @@ -225,11 +226,10 @@ def A_Astar(self, M): M = np.asanyarray(M) # If there is any zero entry, NumPy array division gives infinity, # which is correct. - with np.errstate(divide='ignore'): - A_Astar = ( - (2 / self.T_T0(M) / (self.gamma + 1)) ** - ((self.gamma + 1) / (2 * (self.gamma - 1))) / M - ) + with np.errstate(divide="ignore"): + A_Astar = (2 / self.T_T0(M) / (self.gamma + 1)) ** ( + (self.gamma + 1) / (2 * (self.gamma - 1)) + ) / M return A_Astar def a_a0(self, M): @@ -251,16 +251,21 @@ def a_a0(self, M): return a_a0 + class PrandtlMeyerExpansion(object): """Class representing a Prandtl-Meyer expansion fan. """ + @staticmethod def nu(M, gamma=1.4): - """Prandtl-Meyer angle for a given Mach number. + r"""Prandtl-Meyer angle for a given Mach number. The result is given by evaluating the Prandtl-Meyer function. + .. math:: + \nu = \sqrt{\frac{\gamma + 1}{\gamma - 1}} \tan^{-1}\left [ \sqrt{\frac{\gamma - 1}{\gamma + 1}(M^{2} - 1)} \right ] - \tan^{-1}(\sqrt{M^{2} - 1}) + Parameters ---------- M : float @@ -282,9 +287,9 @@ def nu(M, gamma=1.4): try: with np.errstate(invalid="raise"): sgpgm = np.sqrt((gamma + 1) / (gamma - 1)) - nu = ( - sgpgm * np.arctan(np.sqrt(M * M - 1) / sgpgm) - - np.arctan(np.sqrt(M * M - 1))) + nu = sgpgm * np.arctan(np.sqrt(M * M - 1) / sgpgm) - np.arctan( + np.sqrt(M * M - 1) + ) except FloatingPointError: raise ValueError("Mach number must be supersonic") return nu @@ -311,13 +316,15 @@ def __init__(self, M_1, theta, fl=None, gamma=1.4): """ if not fl: fl = IsentropicFlow(gamma=gamma) - nu_max = ( - PrandtlMeyerExpansion.nu(np.inf, fl.gamma) - - PrandtlMeyerExpansion.nu(M_1, fl.gamma)) + nu_max = PrandtlMeyerExpansion.nu(np.inf, fl.gamma) - PrandtlMeyerExpansion.nu( + M_1, fl.gamma + ) if theta > nu_max: raise ValueError( - "Deflection angle must be lower than maximum {:.2f}°" - .format(np.degrees(nu_max))) + "Deflection angle must be lower than maximum {:.2f}°".format( + np.degrees(nu_max) + ) + ) self.M_1 = M_1 self.theta = theta self.fl = fl diff --git a/skaero/gasdynamics/nozzles.py b/skaero/gasdynamics/nozzles.py index ff07615..87a0ed7 100644 --- a/skaero/gasdynamics/nozzles.py +++ b/skaero/gasdynamics/nozzles.py @@ -2,11 +2,6 @@ """ Utilities for working with nozzles. - -Classes -------- -Nozzle(x, A) - """ from __future__ import division, absolute_import @@ -20,6 +15,7 @@ class Nozzle(object): """Class representing a nozzle. """ + def __init__(self, x, A): self.x = x self.A = A @@ -75,8 +71,7 @@ def solve_flow(self, fl, p_0, T_0, p_B): """ p_ratio = p_B / p_0 if p_ratio > 1.0: - raise ValueError( - "Back pressure must be lower than reservoir pressure") + raise ValueError("Back pressure must be lower than reservoir pressure") # Exit and minimum area A_e = self.A[-1] diff --git a/skaero/gasdynamics/shocks.py b/skaero/gasdynamics/shocks.py index ea8cd43..53cc9f1 100644 --- a/skaero/gasdynamics/shocks.py +++ b/skaero/gasdynamics/shocks.py @@ -2,22 +2,6 @@ """ Shock waves. - -Routines --------- -max_deflection(M_1, gamma=1.4) -Shock(**kwargs) - -The important piece of the module is `Shock`, which returns a shock object -from a variety of combinations of parameters. For more information and -examples, see the docstring of `Shock`. - -Examples --------- ->>> from skaero.gasdynamics import shocks ->>> ns = shocks.Shock(M_1=2.0, gamma=1.4) # Normal shock by default ->>> shocks.Shock(M_1=3.0, theta=np.radians(25), weak=True) - """ from __future__ import division, absolute_import @@ -31,7 +15,8 @@ # Exceptions used in this module -class InvalidParametersError(Exception): pass +class InvalidParametersError(Exception): + pass def max_deflection(M_1, gamma=1.4): @@ -53,13 +38,13 @@ def max_deflection(M_1, gamma=1.4): Corresponding wave angle. """ + def eq(beta, M_1, gamma): os = _ShockClass(M_1, beta, gamma) return -os.theta mu = mach_angle(M_1) - beta_theta_max = optimize.fminbound( - eq, mu, np.pi / 2, args=(M_1, gamma), disp=0) + beta_theta_max = optimize.fminbound(eq, mu, np.pi / 2, args=(M_1, gamma), disp=0) os = _ShockClass(M_1, beta_theta_max, gamma) return os.theta, os.beta @@ -99,37 +84,32 @@ def _ShockFactory(**kwargs): * p2_p1, theta(, weak=True) """ - kwargs.setdefault('gamma', 1.4) + kwargs.setdefault("gamma", 1.4) try: # We want a view of the keys, but the syntax changed in Python 3 params = kwargs.viewkeys() except AttributeError: params = kwargs.keys() - if 'theta' not in params: - kwargs.setdefault('beta', np.pi / 2) + if "theta" not in params: + kwargs.setdefault("beta", np.pi / 2) # ['X', 'beta', 'gamma'] if len(params) != 3: raise InvalidParametersError("Invalid list of parameters") else: - if 'beta' in params: + if "beta" in params: raise InvalidParametersError("Invalid list of parameters") - kwargs.setdefault('weak', True) + kwargs.setdefault("weak", True) # ['X', 'theta', 'weak', 'gamma'] if len(params) != 4: raise InvalidParametersError("Invalid list of parameters") # Here is the list of available resolution methods - methods_list = [ - _from_deflection_angle - ] + methods_list = [_from_deflection_angle] # And we generate a dictionary from it, indexed by their call arguments - methods = { - frozenset(inspect.getargspec(f)[0]): f - for f in methods_list} + methods = {frozenset(inspect.getargspec(f)[0]): f for f in methods_list} # HACK, see http://stackoverflow.com/a/3999604/554319 - _k_class = ( - frozenset(inspect.getargspec(_ShockClass.__init__)[0]) - set(['self'])) + _k_class = frozenset(inspect.getargspec(_ShockClass.__init__)[0]) - set(["self"]) methods[_k_class] = _ShockClass try: call_sig = frozenset(params) @@ -146,6 +126,7 @@ def _from_deflection_angle(M_1, theta, weak, gamma): """Returns oblique shock given upstream Mach number and deflection angle. """ + def eq(beta, M_1, theta, gamma): os = _ShockClass(M_1, beta, gamma) return os.theta - theta @@ -156,11 +137,11 @@ def eq(beta, M_1, theta, gamma): else: if weak: mu = mach_angle(M_1) - beta = optimize.bisect( - eq, mu, beta_theta_max, args=(M_1, theta, gamma)) + beta = optimize.bisect(eq, mu, beta_theta_max, args=(M_1, theta, gamma)) else: beta = optimize.bisect( - eq, beta_theta_max, np.pi / 2, args=(M_1, theta, gamma)) + eq, beta_theta_max, np.pi / 2, args=(M_1, theta, gamma) + ) return _ShockClass(M_1, beta, gamma) @@ -169,12 +150,15 @@ class _ShockClass(object): """Class representing a shock. """ + def __init__(self, M_1, beta, gamma): mu = mach_angle(M_1) if beta < mu: raise ValueError( - "Shock wave angle must be higher than Mach angle {:.2f}°" - .format(np.degrees(mu))) + "Shock wave angle must be higher than Mach angle {:.2f}°".format( + np.degrees(mu) + ) + ) self.M_1 = M_1 self.M_1n = M_1 * np.sin(beta) if beta != 0.0 else 0.0 @@ -183,8 +167,9 @@ def __init__(self, M_1, beta, gamma): def __repr__(self): # FIXME: What if the object is returned from different parameters? - return ("Shock(M_1={0!r}, beta={1!r}, " - "gamma={2!r})".format(self.M_1, self.beta, self.gamma)) + return "Shock(M_1={0!r}, beta={1!r}, " "gamma={2!r})".format( + self.M_1, self.beta, self.gamma + ) @property def theta(self): @@ -195,9 +180,11 @@ def theta(self): theta = 0.0 else: theta = np.arctan( - 2 / np.tan(self.beta) * - (np.sin(self.beta) ** 2 - 1 / self.M_1 / self.M_1) / - (self.gamma + np.cos(2 * self.beta) + 2 / self.M_1 / self.M_1)) + 2 + / np.tan(self.beta) + * (np.sin(self.beta) ** 2 - 1 / self.M_1 / self.M_1) + / (self.gamma + np.cos(2 * self.beta) + 2 / self.M_1 / self.M_1) + ) return theta @property @@ -207,8 +194,9 @@ def M_2n(self): FIXME: Raises ZeroDivisionError if M_1n == 0.0. Consistent? """ M_2n = np.sqrt( - (1 / (self.M_1n * self.M_1n) + (self.gamma - 1) / 2) / - (self.gamma - (self.gamma - 1) / 2 / (self.M_1n * self.M_1n))) + (1 / (self.M_1n * self.M_1n) + (self.gamma - 1) / 2) + / (self.gamma - (self.gamma - 1) / 2 / (self.M_1n * self.M_1n)) + ) return M_2n @property @@ -224,9 +212,7 @@ def p2_p1(self): """Pressure ratio across the shock. """ - p2_p1 = ( - 1 + 2 * self.gamma * - (self.M_1n * self.M_1n - 1) / (self.gamma + 1)) + p2_p1 = 1 + 2 * self.gamma * (self.M_1n * self.M_1n - 1) / (self.gamma + 1) return p2_p1 @property @@ -234,9 +220,7 @@ def rho2_rho1(self): """Density ratio accross the shock. """ - rho2_rho1 = ( - (self.gamma + 1) / - (2 / (self.M_1n * self.M_1n) + self.gamma - 1)) + rho2_rho1 = (self.gamma + 1) / (2 / (self.M_1n * self.M_1n) + self.gamma - 1) return rho2_rho1 @property @@ -276,8 +260,7 @@ def rho02_rho01(self, fl=None): if fl is None: fl = IsentropicFlow(gamma=self.gamma) - rho02_rho01 = ( - fl.rho_rho0(self.M_1) / fl.rho_rho0(self.M_2) * self.rho2_rho1) + rho02_rho01 = fl.rho_rho0(self.M_1) / fl.rho_rho0(self.M_2) * self.rho2_rho1 return rho02_rho01 diff --git a/skaero/geometry/joukowsky.py b/skaero/geometry/joukowsky.py index 2a665cf..d795d2a 100644 --- a/skaero/geometry/joukowsky.py +++ b/skaero/geometry/joukowsky.py @@ -2,9 +2,9 @@ import matplotlib.pyplot as plt -def zeta_coord(radius, centre_chi=0., centre_eta=0.): +def zeta_coord(radius, centre_chi=0.0, centre_eta=0.0): """Transform a cicle into zeta coordinates.""" - angles = np.linspace(0, 2. * pl.pi, 100) + angles = np.linspace(0, 2.0 * pl.pi, 100) chi = radius * np.cos(angles) + centre_chi eta = radius * np.sin(angles) + centre_eta zeta = chi + 1j * eta @@ -16,7 +16,7 @@ def z_coord(zeta=None, radius=1.2, centre_chi=-0.02, centre_eta=0.01, plot=False if zeta is None: zeta = zeta_coord(radius, centre_chi, centre_eta) - z = zeta + 1. / zeta + z = zeta + 1.0 / zeta x = z.real y = z.imag if plot: diff --git a/skaero/util/decorators.py b/skaero/util/decorators.py index 9e5ceee..7e23569 100644 --- a/skaero/util/decorators.py +++ b/skaero/util/decorators.py @@ -20,7 +20,9 @@ def implicit(f): so the equation F(t; x) = 0 gives x = f(t). """ + @wraps(f) def _F(t, y): return f(t) - y + return _F diff --git a/skaero/version.py b/skaero/version.py index 506a493..8cfa142 100644 --- a/skaero/version.py +++ b/skaero/version.py @@ -1 +1 @@ -__version__ = '0.2.dev0' +__version__ = "0.2.dev0"