Thursday, March 28, 2013

How Complex Numbers Make Things Simple


I need a robust test case that can validate both calculated velocities and lift coefficients. Luckily, a special case of the Joukowsky transformation on which zfoil is based provides an analytic solution of the flow over a flat plate. I’ll use this for my test case, as a means to convey to you the power of complex numbers, and as an excuse to play with Racket’s plot library.
I’ll be taking some baby steps here, both to refresh mysaelf on some of the fine points of complex functions and of fluid mechanics, and to make the workings of zfoil accessible to users whose comfort zone hasn’t pushed much beyond algebra.
Ok, what’s a complex number? It’s the sum of a real number, and an imaginary one. Imaginary numbers are multiples of \(\sqrt{-1}\), usually denoted by i or j. All the properties of complex numbers emerge from this definition, and many of the properties of real numbers extend to the complex.
\[z=x+iy\]
Why complex numbers? Well, from their very definition, they do something reals can’t – handle the square root of negative numbers. They can also handle a number of other situations where real numbers seem to faulter, either by increasing the space to look for solutions (from the number line to the complex plane), or by tweaking the definition of common real functions to accomodate complex numbers. As a result, we say that complex numbers are closed – any operation on a complex number (or its subset, the reals) yields a complex number.
That’s pretty cool, mathematically. Complex numbers clean up some of the messes of real numbers, and don’t create any new messes. But how they do this, introducing an "imaginary" second dimension, means that a complex number can represent a vector, and a complex function can map one vector into another. Since in fluid mechanics, we are often mapping positions to velocities, both vectors, wouldn’t it be convenient if we could use complex numbers to describe fluid flows?
Consider a generic complex function:
\[F(z)=u(x,y)+iv(x,y)\]
Very loosely speaking, if \(F(z)\) is well behaved, if you can write it much as you might write a real function, then \(F\) is said to be analytic, and its constituent real functions \(u\) and \(v\) satisfy the Cauchy-Riemann (CR) equations:
\[u_x - v_y = 0\] \[u_y + v_x = 0\]
The CR equations are easily derived by finding \({dF}\over{dz}\) via the traditional limit process, first by letting \(\Delta z=\Delta x\), and then by letting \(\Delta z=i\Delta y\), and then by supposing that the results ought to be identical.
Now consider a generic description of a velocity field:
\[V(z)=u(x,y)+iv(x,y)\]
Fluid flows are obviously subject to the laws of physics, such as conservation of mass, which for an incompressible fluid in steady motion can be expressed as:
\[u_x + v_y = 0\]
The above is a special case of the continuity equation, derived by converting a Lagrangian description of an infinitesimal fluid element’s mass to an Eulerian description of a density field – less scary than it sounds, maybe another article.
Conservation of momentum, aka Newton’s second law, also must be obeyed. In other words, you have to consider the forces acting on a fluid. The force of friction can be eliminated if:
\[u_y - v_x = 0\]
The above is a statement that the flow is "irrotational" (see fluid kinematics). If the above relation is used to generate second derivatives to substitute into Navier-Stokes (the complete fluid mechanics description of momentum), you will find that, if it holds, then friction is irrelevant.
So the physical laws that velocity must obey (given that the assumptions above hold, the flow is incompressible, and friction can be ignored) differ from the mathematical laws that analytic complex functions must obey only by the sign of the v term, the sign of the imaginary component. This means that the conjugate of the velocity is analytic!
\[\bar{V}=u(x,y)-v(x,y)\]
So any analytic complex function we can write represents the conjugate of a physically possible velocity field! That is very cool!
Let’s use Racket to plot a complex function, \(\bar{V}=1-{1\over{z^2}}\)(or rather, it’s conjugate, which will appeal to our sense of what a velocity field should look like).
> (require plot)
; Racket's plot library deals in 2D vectors, not complex
> (define (complex->vector z)(vector (real-part z)(imag-part z)))
> (define (vbar z)
    (- 1.0 (expt z -2.0)))
> (plot (vector-field
         ; Cast vbar into a form suitable for plot
         (λ(x y)(complex->vector
                 (if (> (+ (sqr x)(sqr y)) 1.0)
                     (conjugate
                      (vbar (make-rectangular x y)))
                     ; Suppress large velocities near origin
                     0.0)))
         -2.0 2.0 -2.0 2.0))
(object:2d-plot-snip% ...)


Hey, that almost looks like the flow about a cylinder! Guess what, it is the (incompressible inviscid) flow about a cylinder, simply because we can write it!
Earlier, I referred to Lagrangian & Eulerian descriptions. Most of you were exposed to the Lagrangian point of view in high school or college physics, where the "answer" was often the position of a particle as a function of time, and velocity and acceleration could be found by taking the derivative of that function, a derivative in time.
In fluid mechanics, we are not interested in the current position of a given particle, but the properties of whatever particle is at a point at a given time. This is the Eulerian viewpoint. When you investigate the mathematics that relate the two viewpoints, you find that the temporal derivative in the Lagrangian approach is expressed as a spatial derivative in the Eulerian.
So if the temporal integral of a particle’s velocity is its position, what do you get when you integrate the conjugate of velocity over space?
I’ll let Racket show you:
; Integrate vbar
> (define (Phi z)
    (+ z (expt z -1.0)))
; Values of contour lines
> (define cv '(-1.5 -1.2 -0.9 -0.6 -0.3 -0.01 0.01 0.3 0.6 0.9 1.2 1.5))
; Plot imaginary component of Phi
> (plot (list
         (contours
          (λ(x y)
            (let [(norm (+ (sqr x)(sqr y)))]
              (if (> norm 1.0)
                  (imag-part (Phi (make-rectangular x y)))
                  ; Suppress interior flow in hopefully aesthetic way
                  1.0)))
          -2.0 2.0 -2.0 2.0
          #:levels cv)))
(object:2d-plot-snip% ...)

The function \(\Phi\)(Phi) is called the complex potential, and its imaginary component is called the stream function. As you can see, contours of the stream function in the second plot correspond well with the direction of the velocity vectors in the first plot, and the cylindrical flow we’re investigating is clearly apparent.
Now the really nifty part – what happens if we take the derivative of \(\Phi\) with respect to a different spatial variable?
Let \(w=ze^{i\alpha}\), or \(z=we^{-i\alpha}\). "W" and "Z" space differ by a simple rotation. Our cylindrical flow shouldn’t change, as we’re just changing our viewpoint. As proper lazy programmers, we’ll phrase things to reuse as much code as possible by using composition and the chain rule:
\[\Phi_{1} = \Phi(z(w))\] \[{{d\Phi_1}\over{dw}}={{d\Phi}\over{dz}}{{dz}\over{dw}}\] \[{{d\Phi_1}\over{dw}}={\bar{V}(z(w))}{{dz}\over{dw}}\]
> (define alpha 0.3)
> (define (z w)(* w (exp (* 0-1i alpha))))
> (define dz/dw (exp (* 0-1i alpha)))
> (define (Phi1 w)
    (Phi (z w)))
> (plot
   (list (contours
          (λ(x y)
            (let [(norm (+ (sqr x)(sqr y)))]
              (if (> norm 1.0)
                  (imag-part (Phi1 (make-rectangular x y)))
                  1.0)))
          #:levels cv)
         (vector-field
          (λ(x y)(complex->vector
                  (if (> (+ (sqr x)(sqr y)) 1.0)
                      (conjugate
                       (* dz/dw (vbar (z (make-rectangular x y)))))
                      0.0)))))
   #:x-min -2.0
   #:x-max  2.0
   #:y-min -2.0
   #:y-max  2.0)
(object:2d-plot-snip% ...)

Notice that using composition with \(\Phi\) is somewhat easier than the chain rule with \(\bar{V}\). This tends to be true generally. It pays to manipulate \(\Phi\) and invoke its derivative only when neccessary.
Ok, in yet another display of foreknowledge, I’m going to add circulation to the picture.
\[\Phi_{2} = \Phi_{1} + {k}\cdot{ln(w)}\]
We want the strength of the circlation to be such that the point \(w = 1\) is a stagnation point.
\[{{d\Phi_{2}}\over{dw}}(1) = 0\] \[{{{d\Phi_1}\over{dw}}(1) + {k\over 1}}=0\] \[k = -{{d\Phi_1}\over{dw}}(1)\] \[k = -{{d\Phi}\over{dz}}(z(1))\cdot{{dz}\over{dw}}\] \[k = -{\bar{V}(z(1))}\cdot{e^{-i\alpha}}\] \[k = -(1-{1\over{z(1)^2}})\cdot{e^{-i\alpha}}\] \[k = -(1-{1\over{(e^{-i\alpha})^2}})\cdot{e^{-i\alpha}}\] \[k = -(e^{-i\alpha}-e^{-i\alpha}\cdot{e^{2i\alpha}})\] \[k = e^{i\alpha}-e^{-i\alpha}\] \[k = 2i sin \alpha\]
> (define (Phi2 w)
    (+ (Phi1 w)
       (* 0+2i
          (sin alpha)
          (log w))))
> (plot
   (contours
          (λ(x y)
            (let [(norm (+ (sqr x)(sqr y)))]
              (if (> norm 1.0)
                  (imag-part (Phi2 (make-rectangular x y)))
                  1.0)))
          -2.0 2.0 -2.0 2.0
          #:levels cv))
(object:2d-plot-snip% ...)


... which sets us up for my last great feat of foreknowledge. Let \(f = w + {1\over w}\), and \(\Phi_{3}=\Phi_{2}(w(f))\). Unlike the earlier simple rotation, the transformation between "F" and "W" space presents a little challenge. Note that as \(w \rightarrow \infty\), \(f \rightarrow w\). In other words, from far enough away, our flow field is unchanged by the transformation. However, in the near field, the flow is distorted in an interesting way. \[f = w + {1\over w}\] \[fw = w^2 +1\] \[0 = w^2 - fw + 1\] \[w ={{f \pm \sqrt{f^2 - 4}}\over{2}}\] Note that \(w(f)\) is double valued, but we are interested in the solutions lying outside our cylinder.
> (define (w f)
    (let* [(radical (sqrt (- (sqr f) 4)))
           (w1 (+ f radical))]
      (if (>= (magnitude w1) 2.0)
          (/ w1 2.0)
          (/ (- f radical) 2.0))))
> (define (Phi3 f)
    (Phi2 (w f)))
> (plot
   (contours
    (λ(x y)
      (imag-part (Phi3 (make-rectangular x y))))
    -3.0 3.0 -3.0 3.0
    #:levels cv))
(object:2d-plot-snip% ...)
Yes, the flow about our cylinder is mapped to the flow about a flat plate (hence "F" space). Racket’s plotting ability again provides backup to my mathematical meandering, and I have an exact description of the flow around a flat plate to match zfoil’s calculations against. The only step left is to confine z to \(e^{i\theta}\), which will result in f being confined to the x axis between -2 and 2... my test flat plate. This results in a neat real-valued function for \(\bar{V}\) (and since we’re dealing with a flat plate, also for \(V\)). \[V ={{sin(\theta - \alpha) + sin \alpha }\over{sin \theta}}\] \[f = 2 cos \theta\] By relating f and V to \(\theta\), we explicitly select whether we’re dealing with the upper or lower surface of our flat plate. Note that near the trailing edge, \(V \rightarrow {{\theta - \alpha + \alpha}\over{\theta}} \rightarrow 1\), but at the leading edge, \(V \rightarrow {{{sin(\pi - \alpha)}+sin {\alpha}}\over{sin{\pi}}}\rightarrow {{2 sin{\alpha}}\over 0} \rightarrow \infty\). Infinity sucks. It can be dealt with, symbolically, and the result is exactly what you’d expect for lift over a flat plate, but no numerical method can compare to its symbolic certainty, and so I do not quite have my perfect test case yet.
Next post, we'll approach the performance of a flat plate with a very thin airfoil with a blunt leading edge, which will keep velocities finite everywhere.

No comments:

Post a Comment