Google Answers Logo
View Question
 
Q: Matching "shock angle" results in C code to applet on NASA web page. ( Answered 5 out of 5 stars,   0 Comments )
Question  
Subject: Matching "shock angle" results in C code to applet on NASA web page.
Category: Science
Asked by: vincecate-ga
List Price: $20.00
Posted: 01 Jan 2003 17:47 PST
Expires: 31 Jan 2003 17:47 PST
Question ID: 136179
I am trying to write a C program that matches the output results for
"shock angle" in a Java applet on a NASA web page where you input the
Mach number and angle of your wedge (or cone?).  They give the formula
relating these 3 things.   My C code and NASA URLs are below.  It does not
match very well and I don't see any problem.  Can someone make the code 
match results?

#include<stdio.h>
#include<math.h>

/*
Computing the shock wave angle from mach number and angle.

http://www.grc.nasa.gov/WWW/K-12/airplane/shock.html
   applet you can put in mach number and angle

http://www.grc.nasa.gov/WWW/K-12/airplane/oblique.html
   first equation relates shock angle and wedge angle

http://www.grc.nasa.gov/WWW/K-12/airplane/oblique508.html
   equation1  in ascii text
*/

float NoseAngle, noseRad, Mach, partRad, partDeg;
float ShockAngle, shockRad, x, rad2deg, mSq, pi, M2, sinS, sinS2;
float cotA, minError, error, bestS, s, next;

main()
{

pi=3.141592;
rad2deg=180/pi;

while(1) {
    printf("\n Enter nose cone angle in degrees: ", NoseAngle);
    scanf("%f", &NoseAngle);

    printf("\n Enter the mach number: ");
    scanf("%f", &Mach);

    noseRad = NoseAngle / rad2deg;
    cotA = cos(noseRad)/sin(noseRad);   /* cotA = cot(noseRad); */
    M2 = Mach * Mach;
    minError = 1000;
    bestS=pi/2;
    for (s = noseRad; s<=(pi/2); s += pi/1000000) {
       sinS = sin(s);
       sinS2 = sinS * sinS;
       next = tan(s) * ((6 * M2)/(5 * (M2 * sinS2 - 1)) - 1);

       error = abs(cotA - next);
       if (error < minError) {
          minError = error;
          bestS = s;
       }
    }

    shockRad = bestS;
    printf("\n The shock angle is %f \n", shockRad * rad2deg);
  }
}

Request for Question Clarification by mathtalk-ga on 02 Jan 2003 09:29 PST
Hi, vincecate-ga:

Thanks for posting this interesting question.  I can make several
improvements to your program that I think you would find significant,
but I think a more fundamental problem is that that oblique shock
formula shown on those second two pages:

cot(a) = tan(s) [ (6M^2/(5(M^2sin^2(s) - 1) - 1) ]

is probably not the formula being implemented in the Java applet.  For
example, if we simply take the default values M = 2 and a = 10
provided on the applet, it returns s = 39.28.  However with these
values:

cot(a) = 5.67128...

tan(s) [ (6M^2/(5(M^2 sin^2(s) - 1))) - 1 ] = 5.68938...

The discrepancy is not blatant, but checking this and some other
values (for which the discrepance was even larger) motivated me to
check into the foundations of the underlying formula.

If you take a look here:

[Oblique Shocks: Eq. 13.101]
http://astron.berkeley.edu/~jrg/ay202/node179.html

you'll see that the formula cited at the NASA/Glenn Research Center
site is a special case of this one, in which the parameter "gamma"
(ratio of specific heats) appears (changing notation for consistency):

cot(a) = tan(s) [ ((gamma+1)M^2/(M^2 sin^2(s) - 1)) - 1 ]

which would agree with the formula above if gamma = 0.2.

One difficulty that I have with this is that gamma should be greater
than or equal to 1, from its definition as the ratio of specific heats
c_p/c_V.  So I think it would be helpful to have you clarify whether
the goal is to duplicate the results of the Java applet, per se, or to
provide a correct implementation that has a documentable basis?

The NASA site allows one to download the Java applet, for local use,
so I would anticipate using that approach if the goal is "faithful
imitation".  On the other hand, a "correct" implementation will also
require documenting the formulas and assumptions being used.

A couple of additional comments:

1) There's a bit of a glitch in the Java applet, in that without a
"calculate" button, it's not possible to just type changed values into
the input fields and immediately see updated output values (though
updating occurs if the "thumbs" are used to change input values).

2) There an unmatched parenthesis in the "text-only" version of the
shock angle relation.  However this seems a minor omission, by
comparision with the graphic version of the same formula (and other
references).

regards, mathtalk-ga

Clarification of Question by vincecate-ga on 02 Jan 2003 13:59 PST
Thanks for the work you have put into this.  If I knew how to pay
mathtalk-ga I would do so.  It seems you need to post as an answer.

I am just interested in getting the right result, but for use in some
other software.  Downloading the Java applet would be useful if they
had the source, but they don't seem to.  I think I am convinced that
the formula is wrong.  But I am not sure that the results of the Java
applet are wrong.  On the page:

http://www.grc.nasa.gov/WWW/wind/valid/wedge/wedge.html

I found a fortran program that matches the results of the applet:

http://www.grc.nasa.gov/WWW/wind/valid/wedge/oblshk.f

They have an example on the page that matches the applet, and when I
run it with the gamma input being 1.4 and tol (error tolerance) small
like 0.0001 I match the Java applet.

It is not obvious what exactly the equivalent formula from this
program.  How about if you post that in the form of an "answer" and
with the other work you did we call it done.

Thanks much,
  
    - Vince
Answer  
Subject: Re: Matching "shock angle" results in C code to applet on NASA web page.
Answered By: mathtalk-ga on 02 Jan 2003 21:09 PST
Rated:5 out of 5 stars
 
Hi, Vince:

Thanks for the kind words of encouragement.  I will go ahead and lay
out the formula being used in the Fortran program.  Sometimes you have
to laugh; it is somewhat cleverly done, but a bug was introduced
(which explains why one must set gamma = 1.4 there in order to get the
effect of gamma = 0.2 in the Java applet).

I'd love to also fix up the C program version, but it'll probably take
another day to code and test.  So first things first...

Recall the formula:

cot(a) = tan(s) [ ((gamma + 1) M^2/(M^2 sin^2(s) - 1)) - 1 ]

We can solve this formula for gamma + 1.  Note that 1/tan(s) = cot(s),
so:

cot(a) cot(s) + 1 = (gamma + 1)/(sin^2(s) - 1/M^2)

gamma + 1 = (cot(a) cot(s) + 1)(sin^2(s) - 1/M^2)

So any data point produced by the Java applet (or the Fortran program)
can be used to determine the underlying value of gamma + 1.  Here are
six values from the Java applet and corresponding computed values of
gamma + 1:

  M   a    s       gamma+1

  2   5  34.265    1.19099
  2  10  39.28     1.19666
  2  15  45.314    1.19855

  3   5  23.11     1.19315
  3  10  27.361    1.19732
  3  15  32.221    1.19866

As you can see, there is considerable unevenness in these derived
values of gamma + 1.  My guess is that the root-finding routine in the
Java applet is somehow "sloppy".  But the interesting thing is that
the sample output of the Fortran program shown on the Web page you
pointed out gives a very tight match:

  M   a    s       gamma+1
 2.5 15 36.94490  1.199999979

Here's how the Fortran program is solving for the oblique shock angle.
 It is called "beta" within the subroutine tbm( ), which does function
evaluations to service a "secant method" root finder in the routine
sckobl ("shock oblique").  But for the sake of consistency I will
stick to the notation we've already been using, with this set of
translations:

  Fortran
   mach1  - upstream Mach number (input); what we call M
   delta  - wedge angle (input); what we call a
   gamma  - ratio of specific heats (input)

   beta internal to tbm( )
   theta output from sckobl( )
   thet,thet1,thet2 internal to sckobl( )
          - various versions of the oblique shock angle, or s

Some attention should probably be given to analyzing the starting
values for the root-finding method in the Fortran program, as this can
have considerable impact on how quickly the method converges.  I will
delve more deeply into this in connection with the C program version.

But for our immediate purpose it will suffice to point out the lines
just before "20 continue" where a value for thet1 and a value for a
slightly larger thet2 = 1.02 * thet1 are assigned.

The root-finding code proper consists of the conditional loop
beginning with that continue statement and extending through the
if...endif block below it.  In between we have a call to function tbm(
) which returns delt2, an approximation of some quality to tan(delta)
(delta being the wedge angle a, see above).  The loop terminates when
the if condition fails, i.e. when the relative error of delt2 is
within tol of tan(delta).

The heart of the formula must therefore be decoded from function tbm(
).  Note that effectively its return value is being compared to
tan(a).  This suggests the following algebraic manipulation:

cot(a) = tan(s) [ ((gamma + 1)M^2/(M^2 sin^2(s) - 1)) - 1 ]

Obtain a common denominator on the right hand side and take the
reciprocal:

cot(a) = tan(s) ((gamma + 1)M^2 - M^2 sin^2(s) + 1)/(M^2 sin^2(s) - 1)

tan(a) = cot(s) (M^2 sin^2(s) - 1)/(M^2(gamma + 1 - sin^2(s)) + 1)

tan(a) = 2 cot(s) (M^2 sin^2(s) - 1)/(M^2(2gamma + 2 - 2sin^2(s)) + 2)

tan(a) = 2 cot(s) (M^2 sin^2(s) - 1)/(M^2(2gamma + 1 + cos(2s)) + 2)

In this form the right hand side is closely related to the value
computed by the function tbm( ).  Careful decoding of the expressions
there:

c1 = mach * mach * sin(beta) * sin(beta) - 1.0
c2 = mach * mach * ( gamma + cos(2.0*beta) ) + 2.0
tbm = 2.0 * (1.0/tan(beta)) * ( c1 / c2 )

yields this equivalent:

tan(a) = 2 cot(s) (M^2 sin^2(s) - 1)/(M^2(gamma + cos(2s)) + 2)

Therefore we see that there is actually a bug in this function, with
respect to how gamma is treated.  In the expression we derived above,
the subexpression:

2gamma + 1

in the denominator has been replaced simply by gamma.  Therefore, if
one wants to acheive the effect of solving with gamma = 0.2, it is
necessary to "compensate" in the Fortran program by setting gamma to
"2gamma + 1" or 1.4.

Sigh.  It is a bit of a twisted tale, but in the end all the loose
threads will be accounted for.  I look forward to working on the C
program after a pause for sleep!

Let me point out one advantage to inverting the expression, as was
done in the Fortran program.  In the original formula the right hand
side tends to positive infinity both at s = arcsin(1/M) and at s =
pi/2.  By taking reciprocals we turn those infinitudes into zeroes of
the corresponding expression, which is naturally easier to cope with
using numeric datatypes.

regards, mathtalk-ga

Clarification of Answer by mathtalk-ga on 09 Jan 2003 09:11 PST
Hi, Vince:

A couple of quick notes:

I've been working on a method to solve the wedge angle/shock angle
relation.  In the original form it can have either two solutions or no
solutions for certain values of "a", the wedge angle.

With a bit of rewriting one can express the relation as a cubic
polynomial, and in this form the cases of two solutions or no
solutions can be detected.  I'll present this work in the form of some
C code once I've tied up some loose ends, in particular the physical
meaning of the two solutions (or lack of solution).

Finally, Google Answers strongly prefers that there be no direct
contacts between researchers and clients.  So I will post my further
results here.

Thanks very much for the generous rating and tip!  It brought back
pleasant memories of a class in fluid mechanics.

regards, mathtalk-ga

Clarification of Answer by mathtalk-ga on 20 Jan 2003 19:37 PST
Hi, Vince:

I found a Web document (PDF file) that discusses the physical
interpretation of the two mathematical solutions for s (given a, M,
and gamma as previously discussed).  Scroll essentially to the end of
the document:

http://www.anu.edu.au/Physics/houwing/LectureNotes/Phys3034_Comp_Flows/C34_Flows_L18_OH.pdf

The conclusion is that the smaller solution is ordinarily the one
which is physically realized, in what is called a "weak shock".  Based
on this I will write some C code which solves for that root.

There is the related problem of checking whether a given wedge angle a
is "feasible" for an attached shock wave.  Essentially for given Mach
number M and gamma g, there is a maximum beyond which wedge angle a
results in a detached (curved) shock wave, rather than the oblique
shock wave contemplated in your question.

My thought is to provide an additional routine that will compute the
maximum wedge angle a for given M and g.  Since we are dealing with C
code, I would then use an "assert" within the root solver should the
value assigned to argument a turn out to be infeasible, i.e. it ought
to have been checked before calling for the solution of the shock
angle s.

regards, mathtalk-ga

Request for Answer Clarification by vincecate-ga on 23 Jan 2003 21:29 PST
Thanks again.

Is there any chance that the Berkeley paper is wrong and the other two are
right?

Here are my notes on this, and a few more URLs.

http://spacetethers.com/shockwave.html

Look forward to seeing the C code.  Is there any way I could tip you again?

  -- Vince

Clarification of Answer by mathtalk-ga on 24 Jan 2003 07:30 PST
Hi, Vince:

The Berkeley paper seems to have one statement that is perhaps
inconsistent about these strong vs. weak shock waves (or maybe open to
interpretation), but on the whole I think it is consistent with the
last URL discussed (from Frank Houwing's "Lecture notes" in physics).

The one confusing statement in "Berkeley" is where it says just before
the graph:

"The branches shown as continuous curves correspond to shock waves of
the strong family."

If this was meant to indicate that the solid portions of the curves
correspond to the "strong" shock wave solutions, then it has gotten
the facts backward as a look at the labelling beneath the graph or the
preceding explanatory text will show:

"Solid and dashed curves are the weak and strong families
respectively."

"The two shock waves determined by the shock polar for a [given]
deviation angle [a] are said to belong to the strong and weak
families. A shock wave of the strong family (segment PC)is strong:
p2/p1 is large, [makes] a large angle [s] with the direction of the
velocity , and converts the flow from supersonic to subsonic. A shock
wave of the weak family, the segment QC is weak, is inclined a smaller
angle to the stream, and almost always leaves the the flow
supersonic." [square brackets denote changes for clarity or
consistency with our notation: a = wedge angle, s = shock angle]

Compare this with the statement toward the end of Houwing's Lecture
Notes:

 2) For any given a < a_max there are two values of s predicted.
    The larger value of s is called the 'Strong shock solution'.
    The smaller value of s is called the 'Weak shock solution'.

where again I adapted the symbolic notation for clarity and
consistency.  He then adds this note on physical realization, an issue
that I didn't see addressed in the Berkeley paper:

"For steady flow over a wedge, the weak shock solution occurs in
reality (it is the stable solution).  Strong shock solutions occur in
special circumstances (will discuss later)."

The middle link you cite on that page appears to have been removed:

http://www2.eng.cam.ac.uk/~tph/3A3_S3.pdf

regards, mathtalk

Clarification of Answer by mathtalk-ga on 25 Jan 2003 20:55 PST
/*/  obliqshk.c (mathtalk-ga : 2003-01-25)

These routines are related to formation of attached shock waves
formed by steady supersonic flow past a wedge-shaped obstacle.

For the isentropic flow of an ideal gas, we have the equation:

cot(A) = tan(S) [ (g + 1)M^2/(M^2 sin^2(S) - 1) - 1 ]

where:

        A = angle of symmetric wedge surface wrt downstream

        S = angle of symmetric shock boundry wrt downstream

        M = Mach number ahead of shock wave (assumed > 1)

        g = gamma, ratio of specific heats in ideal gas law

This equation gives A more or less explicitly as a function of
M, g, and S.  Our general goal is to provide a means to solve
for S given M, g, and A.

The following should be considered:

1) Angle S is naturally constrained to between:

        arcsin(1/M) < S < pi/2

2) On this range the right hand tends to +oo at both endpoints,
   with one interior minimum.  Angle A is thus constrained:

        0 < A <= A_max

   with A_max being the inverse cotangent of the RHS minimum
   depending only on M and g.

3) For particular A and given M,g there may be no solution S
   (if A > A_max), one solution S (if A = A_max), or two (if
   A < A_max).  When two solutions are available, the smaller
   one is the stable solution and ordinarily the physically
   realized one.  The smaller angle corresponds to what is
   called a "weak" shock wave, the larger angle to what is
   called a "strong" shock wave.  The strong shock wave has
   a large increase in pressure and a correspondingly large
   drop in Mach speed to subsonic flow.  The weak shock wave
   has a smaller increase in pressure and in most cases the
   flow beyond the shock remains supersonic.

Taking these observations into account, the design of these
routines aims to provide for the smaller angle solution and
for determination of A_max, given M and g, so that existence
of a solution can be determined beforehand.

/*/

#include <math.h>
#include <assert.h>
#include <stdio.h>

/*/
    The expressions involving angle S can be reduced to a
    rational form involving u = cot(S), leading actually to
    a cubic equation to solve for u in terms of M, g, and
    tan(A).  However those routines below which directly
    take u as an argument are for "internal use" and are
    thus marked with the static keyword.

    tan(A) = u (R - u^2)/(T - R + (T+1)u^2)

    where:

          R = M^2 - 1

          T = M^2 (g + 1)

    Since this is C code, we prefer lowercase for variables.
    Hence Mach number becomes m instead of M, and the angles
    A and S are represented in code with a and s, resp.

    One of the cheesier aspects of the code below is the
    free overwriting of calling arugments, passed by value,
    on the way to quick computation of the return result.
/*/

static double amgu(double m, double g, double u)
{
    g = (g + 1.0) * (m *= m);  // overwrites g with T
    m -= 1.0;  // finish overwriting m with R

    return atan(u*(m - u*u)/(g - m + (g+1.0)*u*u));
}

double wedgeangle(double m, double g, double s)
// compute angle A from M, g, and S
{
    return amgu(m,g,1.0/tan(s)); // pass in u = cot(S)
}

/*/
    We note above that tan(A) can be expressed in terms
    of a rational function of u:

    f(u) = u (R - u^2)/(T + (T+1)u^2)

    which is reciprocal to the expression for cot(A) once
    u is identified with cot(S).  Now f(u) is bounded on
    [0,sqrt(R)] and odd as a function of u.

    So the function f(u) attains a maximum on this range
    where f'(u) vanishes (note that f(u) is zero at both
    endpoints and otherwise positive in the given interval).

    But now f'(u) is an even function of u, and hence can
    be expressed in terms of w = u^2.  In fact f'(u) = 0
    if and only if:

    (T+1)w^2 + (R(T+1) + 3(T-R))w - R(T-R) = 0

    a quadratic in w with exactly one positive root.
/*/

static double maxu(double m, double g)
// find u that maximizes expression f(u)
{
    double w;
    g = (g + 1.0) * (m *= m); //overwrite g with R
    m -= 1.0; // finish overwriting m with T
    w = m*(g + 1.0) + 3.0*(g - m);
    w += sqrt( w*w + 4.0*m*(g - m)*(g + 1.0));

    return sqrt(2.0*m*(g - m)/w);
}

double wedgemax(double m, double g)
// compute A_max from M & g
{
    double u;
    u = maxu(m,g);

    return amgu(m,g,u);
}

/*/
    Finally we turn to solving for u = cot(S) given the
    values of M, g, and A.  Letting v = cot(A) we see
    that the rational function f(u) = v gives a cubic:

        u^3 + v(T+1)u^2 - Ru + v(T-R) = 0

    which, by Descarte's rule of signs, must have two
    positive real roots or none.  We assume v > 0 and:

        0 < M^2 - 1 = R < M^2 <= T = (g+1)M^2

    The interesting point here is that in order to get
    the smaller angle S, we want to find the root u of
    this equation between maxu(m,g) and sqrt(R).  This
    is because cot(S) is a decreasing function, and so
    the larger root in terms of u gives the smaller
    solution for angle S.

    A number of root finding strategies or tactics are
    available to us.  At one extreme we might apply a
    variant of Cardano's formula; at the other one may
    try exhaustive search.

    I recently read some ideas on explicit solutions
    to cubic equations that I'd like to try, but for
    now Newton's method seems most attractive.  Note
    that the cubic polynomial is concave up when:

        0 <= u <= sqrt(R)

    and takes a positive value at u = sqrt(R) since:

        u^3 - Ru = 0

    there.  So u = sqrt(R) makes a suitable starting
    value, provided a positive root lies below it,
    i.e provided the cubic polynomial evaluated at
    maxu(m,g) is negative.

    To minimize dependencies I decided not to call
    maxu(m,g) to directly test this feasibility.  My
    alternative relies on the polynomial's convexity
    on [0,sqrt(R)]. Using u = sqrt(R) as an initial
    guess, we'd approach the larger of any positive
    root montonically from above.  Consequently the
    deriviatives would remain positive in this case.

    However if no positive real roots exist, we will
    eventually slip around the local minimum in such
    a manner that either u or the slope becomes non-
    positive.  Asserts are placed to guard against
    these possibilities.
/*/

static double umga(double m, double g, double a)
// find root for u using tailored Newton's method
{
    double u,v,p,q;
    g = 1.0 + g*(m *= m); // overwrite g with T-R
    v = tan(a);  // overwrite a with v = tan(A)

    // p = u^3 + v(T+1)u^2 - Ru + v(T-R)
    // q = dp/du = 3u^2 + 2v(T+1)u - R

    a = v*(g + m);  // overwrite a with v(T+1)
    m -= 1.0;  // finish overwriting m with R
    g *= v;  // and overwrite g with v(T-R)

    u = sqrt(m);

    do {
        p = ((u + a)*u - m)*u + g;
        q = (3.0*u + a + a)*u - m;
        assert(q > 0.0);
        u -= p/q;
        assert(u > 0.0);
    } while (p > 1.0e-6);  // Newton iteration

    return u;
}

double shockangle(double m, double g, double a)
// compute angle S from M, g, and A
{
    return atan(1.0/umga(m,g,a));  // u = cot(S)
}

/*/
    test data (angles in degrees):

      M     A      S     gamma
     2.5  15.0  36.94490  0.2

/*/
int main(int argc, char* argv[])
{
    double a,s;

    const double pi = 3.14159265358979323846;

    a = wedgemax(2.5,0.2)*180.0/pi;
    printf("Max. wedge if Mach 2.5, gamma 0.2 is %f degrees.\n", a);

    if (a > 15.0)
    {
        s = shockangle(2.5,0.2,pi/12.0); // pi/12 is 15 degrees
        printf("Shock angle for 15 degree wedge is %f degrees.\n",
            s*180.0/pi);

        // s = (36.9449/180.0)*pi;
        a = wedgeangle(2.5,0.2,s)*180.0/pi;
        printf("Corresponding wedge angle is %f degrees.\n", a);
    }
    else printf("Maximum wedge angle is below 15 degrees.\n");

    return 0;
}
vincecate-ga rated this answer:5 out of 5 stars and gave an additional tip of: $20.00
Thanks much.  If you find anything more please email to vince@cate.com.

Comments  
There are no comments at this time.

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy