home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 138_01 / oct84col.ddj < prev    next >
Text File  |  1985-11-14  |  18KB  |  581 lines

  1. .pl 63
  2. .po 0
  3. ..
  4. .. this article was prepared for the C/Unix Programmer's Notebook
  5. .. Copyright 1984 (c) Anthony Skjellum.  
  6. ..
  7. .. by A. Skjellum
  8. ..
  9. .he "C/Unix Programmer's Notebook"   for October, 1984 DDJ. 
  10.  
  11.  
  12.                                Introduction
  13.  
  14.     The  thrust of this entire column is to illustrate scientific  uses 
  15.  
  16. of  the C programming language by examples.  The aim is to present routines 
  17.  
  18. useful  in  conjunction  with scientific programming,  some  of  which  are 
  19.  
  20. general and others which implement specific numerical algorithms. There are 
  21.  
  22. specific audiences for which this column is intended.   The first  audience 
  23.  
  24. is  the  group of  die-hard programmers who refuse to change languages  and 
  25.  
  26. continue  to produce useful programs in outdated languages.   Not  only  is 
  27.  
  28. this  of greater expense to themselves,  but also cheats others by  locking 
  29.  
  30. them  into FORTRAN or other old-fashioned languages.   For them,  I want to 
  31.  
  32. illustrate the elegance and versatility of C.   The second audience  (which 
  33.  
  34. may  also  include  the first as a subset) are those  users  interested  in 
  35.  
  36. scientific applications but who may not have used C for this purpose.  This 
  37.  
  38. article  is intended to illustrate that C is completely acceptable for such 
  39.  
  40. purposes and the code shows how generally concepts can be presented.  (This 
  41.  
  42. article makes no attempt to survey scientific applications where C could be 
  43.  
  44. used but merely includes some non-trivial examples).   Finally,  for  those 
  45.  
  46. readers  who  don't  fit into the above  categories,  the  general  purpose 
  47.  
  48. routines will still prove interesting. 
  49.  
  50.     Three  programming  systems are presented.   The first is a set  of 
  51.  
  52. general  purpose subroutines which are designed to simplify the process  of 
  53.  
  54. user-program  interaction,  and  to provide a  straight-forward  means  for 
  55.  
  56. handling  erroneous  input.  The  input  mechanisms  are  not  particularly 
  57.  
  58. sophisticated, but emphasize structure in the user's program.  The routines 
  59.  
  60. make  range  checking  so automatic that the programmer has no  excuse  for 
  61.  
  62. omitting  such  checks,  regardless  of how 'quickly' a program  is  to  be 
  63.  
  64. completed.   Providing these routines to novice programmers in a  classroom 
  65.  
  66. environment  has  eliminated  a lot of frustration over using  the  scanf() 
  67.  
  68. function.
  69.  
  70.     The  second  and third programming systems  illustrate  Runge-Kutta 
  71.  
  72. integration.   The Runge-Kutta formalism is a standard numerical  technique 
  73.  
  74. for  handling the numerical integration of one or more first order ordinary 
  75.  
  76. differential  equations.   Interested readers may wish to consult the  book 
  77.  
  78. Numerical Analysis, by Richard L.  Burden et.  al. (Prindle, Weber, Schmidt 
  79.  
  80. publishers,  Second edition,  1981).  This is the source for the algorithms 
  81.  
  82. presented  in  the  code  and is also a  fine  reference  for  introductory 
  83.  
  84. numerical  methods.   The  choice of Runge-Kutta routines as  examples  was 
  85.  
  86. based on their widespread use in scientific work.
  87.  
  88.                              Acknowledgements
  89.  
  90.     The  general purpose library has received extensive use by  Caltech 
  91.  
  92. students during the past school year.   Thanks are due to Scott Lewicki who 
  93.  
  94. discovered a couple of minor details which caused major errors.
  95.  
  96.     The  Runge-Kutta  code  was developed by  Michael  J.  Roberts  and 
  97.  
  98. myself.   Mr.  Roberts  developed the major portion of the RKSYS  (multiple 
  99.  
  100. equations)  routines  as  a  project  for  Caltech's  Physics  20   course: 
  101.  
  102. Introduction to Computational Physics (Prof.  Geoffrey C. Fox, instructor).  
  103.  
  104. Approximately  sixty  man  hours  were spent  in  developing,  testing  and 
  105.  
  106. debugging this code.   Mr.  Roberts also wrote the original version of  the 
  107.  
  108. documentation for RKSYS included in modified form as Table III.
  109.  
  110.  
  111.                        GPR: General Purpose Routines
  112.  
  113.     The general purpose library consists of five subroutines.   Four of 
  114.  
  115. these  subroutines  deal with input.   The fifth is a simple  facility  for 
  116.  
  117. printing files to the console.  This latter routine is called display() and 
  118.  
  119. will be considered separately from the input functions.
  120.  
  121.     The  other routines are iinp(),  finp(),  sinp() and  cinq().   The 
  122.  
  123. first three provide integer, floating point, and string input respectively.  
  124.  
  125. The latter is a yes-no question processor.  The exact calling sequences for 
  126.  
  127. each of the routines is provided in Table I.
  128.  
  129. .pa
  130.                       ---------- Table I. ----------
  131.  
  132. 1.    int iinp(prompt,cflag,low,high);
  133.  
  134.     char *prompt: optional prompt string to be printed before input
  135.     char cflag:   checking flag: if non-zero, range checking is performed
  136.     int low
  137.     int high:     low, high are (inclusive) range checking values
  138.  
  139.     iinp() repeats input until a valid number is entered; the valid 
  140.     number is the function's return value.
  141.  
  142. 2.    double finp(prompt,cflag,low,high);
  143.  
  144.     char *prompt: optional prompt string to be printed before input
  145.     char cflag:   checking flag: if non-zero, range checking is performed
  146.     double low
  147.     double high:  low, high are (inclusive) range checking values
  148.  
  149.     finp() repeats input until a valid number is entered; the valid
  150.     number is the function's return value
  151.  
  152. 3.    len = sinp(prompt,string,length);
  153.  
  154.     int len:      length of entered string
  155.     char *prompt: optional prompt string to be printed before input
  156.     int length:   maximum length of input string
  157.  
  158.     sinp() ignores leading spaces.
  159.  
  160. 4.    retn = cinq(prompt);
  161.  
  162.     int retn:     1 --> 'Y' was typed, 0 --> 'N' was typed
  163.     char *prompt: optional prompt string to be printed before input
  164.  
  165. 5.    retn = display(fname);
  166.  
  167.     int retn:     0 --> success, -1 --> failure       
  168.     char *fname:  null-terminated name of file
  169.  
  170.     display() prints the specified file on the standard output device.
  171.  
  172.  
  173.                     ---------- End Table I. ----------
  174.  
  175. .pa
  176.  
  177.     Traditionally,  user  input consists of a sequence of lines such as 
  178.  
  179. the following sequence for inputting an integer: 
  180.  
  181.         #define MIN 10
  182.         #define MAX 100
  183.         ...
  184.  
  185.         int input;
  186.         ...
  187.  
  188.         while (1)    /* input loop */
  189.         {
  190.             printf("Enter input variable --> ");
  191.  
  192.             if (scanf("%u",&input) != 1)    /* get variable */
  193.             {
  194.                 drain();    /* drain spurious characters */
  195.                 continue;    /* skip range checks */
  196.             }
  197.  
  198.             /* do range checking */
  199.  
  200.             if ((input >= MIN) && (input <= MAX))
  201.                 break;        /* we are done */
  202.  
  203.             printf("\nNumber out of range\n");
  204.  
  205.         } /* keep looping until scanf() can read a variable */
  206.  
  207. Entering   this   sequence  repeatedly  can  be  rather  tedious   from   a 
  208.  
  209. programmatical  point of view,  so error checking is often  omitted.   This 
  210.  
  211. practice leads to programs which do not handle user mistakes intelligently.  
  212.  
  213. The  GPR routines allow the above sequence to be replaced by a single  line 
  214.  
  215. of code:
  216.  
  217.         input = iinp("Enter input variable -->",1,MIN,MAX);
  218.  
  219. Since  using  the GPR input functions is easier than  using  scanf(),  this 
  220.  
  221. variety  of  function should be well-received and can be used  in  lieu  of 
  222.  
  223. scanf() for most purposes.   A further advantage of these functions is that 
  224.  
  225. they unclutter the user's program.   More sophisticated checking will still 
  226.  
  227. need to be included explicitly: the input functions only do range checking.
  228.  
  229.     The  display() function was included to encourage users to  provide 
  230.  
  231. on-line help/documentation along with their programs.  This function allows 
  232.  
  233. users  to  print  out additional  text  whenever  appropriate.   With  this 
  234.  
  235. function, a trivial on-line help facility can be created; a help feature is 
  236.  
  237. almost always appropriate but usually is omitted.
  238.  
  239.     The  GPR routines may be found in Listing I.   The current list  is 
  240.  
  241. not  exhaustive  but  intended  only  to suggest  a  trend  for  additional 
  242.  
  243. routines.
  244.  
  245.                         RK4: Runge-Kutta Algorithm
  246.  
  247.     We  have investigated the general purpose library which  simplifies 
  248.  
  249. the  task  of  correct  input.   Now  we  want  to  consider  a  scientific 
  250.  
  251. application  of  C:   single  equation  Runge-Kutta  integration.    Before 
  252.  
  253. introducing the code, some background is required.
  254.  
  255.     We start with a differential equation in the canonical form
  256.  
  257.         y' = f(y,t)
  258.  
  259. where  y'  is the first derivative of y with respect to t and f(y,t)  is  a 
  260.  
  261. piece-wise  continuous  function  of its arguments.   In  addition  to  the 
  262.  
  263. differential equation, we are given an initial condition.  Namely,
  264.  
  265.         y(t=0) = y0
  266.  
  267. where y0 is some real constant (e.g.  35.1).  These two equations  uniquely 
  268.  
  269. specify the solution y(t) which is as yet unknown.   The need for numerical 
  270.  
  271. techniques   arise   when  the  differential  equation  cannot  be   solved 
  272.  
  273. analytically. 
  274.  
  275.     There  are  many possible numerical approaches to the  solution  of 
  276.  
  277. this  equation,  but  considering them is beyond the scope of  the  current 
  278.  
  279. discussion.   For  now,  it is sufficient to state that a technique  exists 
  280.  
  281. called  RK4  (Runge-Kutta  fourth  order) which  will  solve  the  equation 
  282.  
  283. numerically  with  known  error characteristics.   Solution  of  a  typical 
  284.  
  285. equation is presented in Listing II. for the equation
  286.  
  287.         y'(t)  = 1 + y
  288.  
  289. with the initial condition
  290.  
  291.         y(t=0) = 5.0
  292.  
  293. Since this equation can also be solved analytically,  a comparison is  made 
  294.  
  295. with  the  exact solution in order to demonstrate error characteristics  of 
  296.  
  297. the method.  The analytical solution turns out to be
  298.  
  299.         y(t) = t + 5.0*exp(t)
  300.  
  301. This latter result can be deduced by inspection.
  302.  
  303.     The  Runge-Kutta  algorithm and code is presented in  Listing  III.  
  304.  
  305. Readers interested in more information should consult the book by Burden et 
  306.  
  307. al.  Calling sequences are described in Table II.
  308.  
  309. .pa
  310.                       ---------- Table II. ----------
  311.  
  312.                         RK4: Runge-Kutta Integrator
  313.                              ----------------
  314.  
  315.                                Description:
  316.  
  317.  
  318. File:        RK4.C,        Subroutine Library: Listing III.
  319.  
  320.     The C language call is as follows:
  321.  
  322.         rk4(function,a,b,n,alpha,t,w);
  323.  
  324. where:
  325.     function    returns the right-hand side f(y,t) of the system.
  326.  
  327.         a        is the start of the interval of integration
  328.  
  329.     b        is the end   of the interval of integration
  330.  
  331.     n        is the number of integration steps
  332.  
  333.     alpha        is the initial value y(0) = alpha
  334.  
  335.     t        is the array where the times will be stored
  336.  
  337.     w        is where the approximations to y will be stored
  338.  
  339.  
  340.  
  341.  
  342.     The formal C definitions for the function and its parameters are:
  343.  
  344. 1.    rk4(): n step integrator
  345.  
  346.     rk4(function,a,b,n,alpha,t,w)
  347.     double (*function)();    /* function giving f(t,y) */
  348.     double a;        /* beginning of interval */
  349.     double b;        /* end of interval */
  350.     int n;            /* number of steps in interval */
  351.     double alpha;        /* initial condition for y */
  352.     double t[];        /* array for returning T[i] values */
  353.     double w[];        /* array for returning W[i] values */
  354.  
  355.  
  356. .cp 8
  357. 2.    rk4_1(): 1 step integrator
  358.  
  359.     rk4_1(function,h,time,yapprox)
  360.     double (*function)();        /* Pointer to function to integrate */
  361.     double h;            /* Step size */
  362.     double time;            /* Current time step */
  363.     double *yapprox;        /* Current approximation of function */
  364.     
  365.  
  366.  
  367.                     ---------- End Table II. ----------
  368. .pa
  369.  
  370.                  RKSYS: Systems of differential equations
  371.  
  372.     Imagine  now that we have a set (system) of N first-order  ordinary 
  373.  
  374. differential equations:
  375.  
  376.         y '(t) = f (t,y ,y ... y )          (i = 1,2,...N) 
  377.               i        i    1  2     N    
  378.  
  379. This problem is useful for solving other systems too,  since sets of linear 
  380.  
  381. differential   equations   involving  higher-order   derivatives   can   be 
  382.  
  383. transformed to larger systems of the above variety (see Burden).   Thus,  a 
  384.  
  385. program which can solve the above system has reasonably wide applicability.  
  386.  
  387.      Unfortunately,  the  problem is more complicated for N equations  than 
  388.  
  389. for  one equation as is evident from Table III.  in which the more  general 
  390.  
  391. Runge-Kutta software is described.  (Listing IV. contains the actual code.)  
  392.  
  393. Listings V.  and VI.  are example programs which use rk4n() to solve  small 
  394.  
  395. systems  of  equations.   The  Listing V.  implements the same  problem  as 
  396.  
  397. Listing II., but using rk4n() instead of rk4() to perform the integration.
  398.  
  399. .pa
  400.                      ---------- Table III. ----------
  401.  
  402.                              RK System Solver
  403.                                   (RKSYS)
  404.                              ----------------
  405.  
  406.  
  407. Files:        RKS.C,                  Subroutine library: Listing IV.
  408.                 RKST1.C,                Test program #1:    Listing V.
  409.                 RKST2.C                 Test program #2:    Listing VI.
  410.  
  411.  
  412.  
  413.     The format of the C language call is as follows:
  414.  
  415.         rk4n(function,wsource,wstore,m,a,b,n,alpha,t,kuttas);
  416.  
  417. where:
  418.  
  419.     function    is  a pointer to the function which will return the 
  420.                         first derivatives of each function in your  system.  
  421.                         It will be called as:
  422.     
  423.                 function(j,i,tval,rk_comp)
  424.  
  425.             where:
  426.                 j    is the current time step
  427.                 i    is the current function
  428.                     number (0, 1, ..., n-1).
  429.                 tval    is the current time value
  430.                 rk_comp    is a pointer to a function
  431.                     which your derivative function
  432.                     must call as:
  433.  
  434.                         rk_comp(n,j,i)
  435.                     where:
  436.                         n is the function
  437.                             number in the
  438.                             system (0,...n-1)
  439.                             of the function
  440.                             you wish to
  441.                             evaluate.
  442.                         j is the time step.
  443.                         i is the current
  444.                             function number.
  445.  
  446. .cp 8
  447.     wsource        is a pointer to your function which will return a W 
  448.                         value  (which  will have been stored by the  user's 
  449.                         WSTORE routine -- one need only worry about storing 
  450.                         and   returning  these  values,   not  the   values 
  451.                         themselves). It will be called as:
  452.  
  453.                 wsource(j,i)
  454.  
  455.             where:
  456.                 j    is the current time step.
  457.                 i    is the current function
  458.                     number.
  459.  
  460.     wstore        is  a  pointer to the user's  function  which  will             
  461.                         store values of W (see wsource above). It is called 
  462.                         as:
  463.  
  464.                 wstore(j,i,value)
  465.  
  466.             where:
  467.                 j    is the current time step.
  468.                 i    is the current function
  469.                     number.
  470.                 value    is the value to be stored
  471.                     for that location.
  472.  
  473.                 Note:   wsource(j,i)  should  be  equal  to 
  474.                         "value" after wstore(j,i,value).
  475.  
  476.     m        is the number of equations in your system.
  477.  
  478.     a        is the starting time value for the interval.
  479.  
  480.     b        is the ending time value for the interval.
  481.  
  482.     n        is  the number of points into which the interval is 
  483.                         to be broken.
  484.  
  485.     alpha        is  a one dimensional array of the initial  values.  
  486.                         The  value alpha[0] is the first function at time = 
  487.                         a, alpha[1] is the second, etc.
  488.  
  489.     t        is a one dimensional array where rk4n() will  store 
  490.                         the time values as it calculates them.   It must be 
  491.                         at least as large as n.
  492.  
  493.     kuttas        is  a  two dimensional array,  the  size  of  whose 
  494.                         second element is 4 (i.e., Kuttas[][4]). It will be 
  495.                         the  storage  area  for  "k"  values  as  they  are 
  496.                         calculated.
  497.  
  498.  
  499.     The formal C definitions for the function and its parameters are:
  500.  
  501.     double rk4n(function,wsource,wstore,m,a,b,n,alpha,t,kuttas)
  502.     double (*function)();
  503.     double (*wsource)();
  504.     double (*wstore)();
  505.     int m;
  506.     double a;
  507.     double b;
  508.     int n;
  509.     double alpha[];
  510.     double t[];
  511.     double kuttas[][4];
  512.  
  513. Comments on array sizes, (*wsource)(), (*wstore)():
  514.  
  515.     The  (*wstore)() function should trap out-of-bound storage requests 
  516. which  result as a natural part of the rk4n() algorithm.   A  more  elegant 
  517. solution is to make the array size used by (*wstore)() one greater than the 
  518. number of steps.     
  519.  
  520.                    ---------- End Table III. ----------
  521. .pa
  522.  
  523.                          How to use the integrator
  524.  
  525.     The   rk4n()   subroutine  will  solve  a  system  of   first-order 
  526.  
  527. differential  equations  as specified by the  calling  program,  using  the 
  528.  
  529. Runge-Kutta order four integrator described in Burden et. al, pages 239-240 
  530.  
  531. (refer also to page 205).
  532.  
  533.     The  calling  program  must  provide  information  for  the  rk4n() 
  534.  
  535. subroutine  in order for it to solve the system of differential  equations.  
  536.  
  537. This information must consist of:
  538.  
  539.     * The first derivatives of each of the functions in the system.
  540.     * Subroutines to store and retrieve values as the functions are
  541.       integrated
  542.     * Initial conditions for each of the functions
  543.     * The range over which the function will be integrated
  544.     * Storage areas for the time and intermediate "K" value arrays.
  545.  
  546. The  information must be provided in a specific order and format,  which is 
  547.  
  548. described in Table III.
  549.  
  550.     By  studying the Runge-Kutta routines,  the reader will notice  the 
  551.  
  552. central  importance  of pointers to functions in the  organization  of  the 
  553.  
  554. code.   Using this concept effectively allows the software to be completely 
  555.  
  556. divorced of the specifics of the user's program.  
  557.  
  558.                                 Conclusions
  559.  
  560.     In  this  article,  three sets of example routines were  presented.
  561.  
  562. This  code  was  included to demonstrate how  scientific  applications  and 
  563.  
  564. related  software  is actually implemented in  C.   Studying  the  examples 
  565.  
  566. should  help  the  reader  to gain insight into the use of  C  for  similar 
  567.  
  568. undertakings.
  569.  
  570.                              Copyright Notice 
  571.  
  572.     All  the code presented with this article is copyright  1983,  1984 
  573.  
  574. (c)  by  the California Institute of  Technology  (Caltech),  Pasadena,  CA 
  575.  
  576. 91125.  All rights reserved.  This code may be freely distributed, used for 
  577.  
  578. all non-commercial purposes, but may not be sold.  
  579.  
  580.  
  581.