Skip to content

Commit

Permalink
On with the shocks refactor
Browse files Browse the repository at this point in the history
The API is the same, the implementation is much better now. Also updated
examples from README.rst and charts example.
  • Loading branch information
astrojuanlu committed Mar 1, 2013
1 parent 49d1379 commit 6a8a7ad
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 24 deletions.
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ Gas dynamics calculations::
>>> 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.NormalShock(M_1=2.5, gamma=1.4)
>>> ns = shocks.Shock(M_1=2.5, gamma=1.4)
>>> M_2 = ns.M_2 # Mach number behind a normal shock wave
>>> os = shocks.from_deflection_angle(3.0, np.radians(25), weak=True) # Known M and deflection angle
>>> os = shocks.Shock(M_1=3.0, theta=np.radians(25), weak=True)

Dependencies
============
Expand Down
6 changes: 3 additions & 3 deletions examples/Oblique shocks chart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
" beta = np.linspace(mu, np.pi / 2, N)\n",
" theta = np.zeros_like(beta)\n",
" for i in range(N):\n",
" theta[i] = shocks.ObliqueShock(M_1, beta[i], gamma).theta\n",
" theta[i] = shocks.Shock(M_1=M_1, beta=beta[i], gamma=gamma).theta\n",
" ax.plot(np.degrees(theta), np.degrees(beta), label=r\"$M_1 = \\,{}$\".format(M if np.isfinite(M) else \"\\infty\"), *args, **kwargs)"
],
"language": "python",
Expand Down Expand Up @@ -114,13 +114,13 @@
" theta_sonic = np.empty_like(M)\n",
" beta_sonic = np.empty_like(M)\n",
" def eq(beta, M_1, gamma):\n",
" os = shocks.ObliqueShock(M_1, beta, gamma)\n",
" os = shocks.Shock(M_1=M_1, beta=beta, gamma=gamma)\n",
" return os.M_2 - 1\n",
" for i in range(N):\n",
" mu = np.arcsin(1.0 / M[i])\n",
" __, beta_max = shocks.max_deflection(M[i], gamma)\n",
" beta_sonic[i] = sp.optimize.bisect(eq, mu, beta_max, args=(M[i], gamma))\n",
" theta_sonic[i] = shocks.ObliqueShock(M[i], beta_sonic[i], gamma).theta\n",
" theta_sonic[i] = shocks.Shock(M_1=M[i], beta=beta_sonic[i], gamma=gamma).theta\n",
" ax.plot(np.degrees(theta_sonic), np.degrees(beta_sonic), *args, **kwargs)"
],
"language": "python",
Expand Down
82 changes: 63 additions & 19 deletions skaero/gasdynamics/shocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,25 @@
Routines
--------
from_deflection_angle(M_1, theta, weak=True, gamma=1.4)
max_deflection(M_1, gamma=1.4)
Shock(**kwargs)
Classes
-------
NormalShock(M_1, gamma)
ObliqueShock(M_1, beta, gamma)
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.NormalShock(2.0, gamma=1.4) # Normal shock with M_1 = 2.0
>>> shocks.from_deflection_angle(3.0, np.radians(25), weak=True)
>>> 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

import inspect

import numpy as np
import scipy as sp
import scipy.optimize
Expand Down Expand Up @@ -67,6 +68,11 @@ def eq(beta, M_1, gamma):
def _ShockFactory(**kwargs):
"""Returns an object representing a shock wave.
Parameters
----------
gamma : float, optional
Specific heat ratio, default 7 / 5.
Examples
--------
>>> ss1 = Shock(M_1=1.5) # Given upstream Mach number (default beta = 90°)
Expand All @@ -80,26 +86,63 @@ def _ShockFactory(**kwargs):
>>> ss2.beta # Notice it is an oblique shock
0.6590997534071927
TODO
----
* This is a list of possible cases
* M_1(, beta=np.pi / 2) -> _ShockClass(M_1, beta) (only direct case)
* M_2(, beta=np.pi / 2)
* p2_p1(, beta=np.pi / 2)
* ...
* M_1, theta(, weak=True)
* M_2, theta(, weak=True)
* p2_p1, theta(, weak=True)
"""
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 not 'theta' 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:
raise InvalidParametersError("Invalid list of parameters")
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
]

# And we generate a dictionary from it, indexed by their call arguments
methods = {
frozenset(['M_1']): _ShockClass,
frozenset(['M_1', 'theta']): _from_deflection_angle,
frozenset(['M_1', 'theta', 'weak']): _from_deflection_angle
}
beta = kwargs.pop('beta', np.pi / 2)
gamma = kwargs.pop('gamma', 1.4)
params = frozenset(kwargs.keys())
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']))
methods[_k_class] = _ShockClass
try:
shock = methods[params](beta=beta, gamma=gamma, **kwargs)
call_sig = frozenset(params)
shock = methods[call_sig](**kwargs)
except KeyError:
raise InvalidParametersError("Invalid list of parameters")
raise NotImplementedError
return shock


Shock = _ShockFactory


def _from_deflection_angle(M_1, theta, weak=True, gamma=1.4, **kwargs):
def _from_deflection_angle(M_1, theta, weak, gamma):
"""Returns oblique shock given upstream Mach number and deflection angle.
"""
Expand All @@ -126,7 +169,7 @@ class _ShockClass(object):
"""Class representing a shock.
"""
def __init__(self, M_1, beta, gamma=1.4):
def __init__(self, M_1, beta, gamma):
mu = mach_angle(M_1)
if beta < mu:
raise ValueError(
Expand All @@ -148,7 +191,7 @@ def theta(self):
"""Deflection angle of the shock.
"""
if self.beta == 0.0 or self.beta == np.pi / 2:
if self.beta == mach_angle(self.M_1) or self.beta == np.pi / 2:
theta = 0.0
else:
theta = np.arctan(
Expand All @@ -161,6 +204,7 @@ def theta(self):
def M_2n(self):
"""Normal Mach number behind the shock.
FIXME: Raises ZeroDivisionError if M_1n == 0.0. Consistent?
"""
M_2n = np.sqrt(
(1 / (self.M_1n * self.M_1n) + (self.gamma - 1) / 2) /
Expand Down

0 comments on commit 6a8a7ad

Please sign in to comment.