/[ascend]/branches/jacob/models/johnpye/fprops/mixtures/init_mixfuncs.c
ViewVC logotype

Contents of /branches/jacob/models/johnpye/fprops/mixtures/init_mixfuncs.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3062 - (show annotations) (download) (as text)
Fri Aug 14 00:03:58 2015 UTC (3 years, 9 months ago) by jacob
File MIME type: text/x-csrc
File size: 19207 byte(s)
Extended dew and bubble-point functions further to check that mixture is not below triple-point conditions (temperature or pressure).

Corrected dew-temperature and bubble-temperature functions to work correctly (at first, these functions tended to wander into the critical region and fail).

Added component-specific property functions in API.

1 /* ASCEND modelling environment
2 Copyright (C) Carnegie Mellon University
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation --
17
18 Free Software Foundation, Inc.
19 59 Temple Place - Suite 330
20 Boston, MA 02111-1307, USA.
21 *//*
22 by Jacob Shealy, June 5-, 2015
23
24 Function definitions for initial model of ideal-solution mixing. Removed
25 these from the test files init_mix1, init_mix2, etc. to de-clutter them.
26 */
27
28 #include "mixture_generics.h"
29 #include "mixture_prepare.h"
30 #include "mixture_struct.h"
31 #include "init_mixfuncs.h"
32 #include "../helmholtz.h"
33 #include "../fluids.h"
34 #include "../fprops.h"
35 #include "../refstate.h"
36 #include "../sat.h"
37
38 #include <stdio.h>
39 #include <math.h>
40
41 /* Mixture-Preparation Functions */
42 #if 1
43 #define TT M->T
44 #define RHOS M->rhos
45 #define NPURE M->X->pures
46 #define PF M->X->PF
47 #define XS M->X->Xs
48 #endif
49
50 #if 0
51 /*
52 Calculate ideal-gas densities `rho_out' from the temperature and pressure
53 (used as starting densities for other routines)
54
55 Specifically, these densities may be useful if T > T_c (critical
56 temperature), and P < P_c (sub-critical pressure).
57 */
58 void ig_rhos(MixtureState *M, double P){
59 unsigned i; /* counter variable */
60
61 #if 1
62 #define TT M->T
63 #define RHOS M->rhos
64 #define NPURE M->X->pures
65 #define PF M->X->PF
66 #endif
67 for(i=0;i<NPURE;i++){
68 RHOS[i] = P / PF[i]->data->R / TT;
69 // printf("\n " TITLE "The ideal-gas mass density of %s is : %.4f kg/m3"
70 // , PF[i]->name, RHOS[i]);
71 MSG("Ideal-gas mass density of %s is : %.4f kg/m3", PF[i]->name, RHOS[i]);
72 } puts("");
73 #if 0
74 #undef PF
75 #undef NPURE
76 #undef RHOS
77 #undef TT
78 #endif
79 }
80
81 /*
82 Set initial densities for use in `pressure_rhos', given an initial
83 temperature and pressure. Considers whether a substance is in critical or
84 saturation regions.
85 */
86 void initial_rhos(MixtureState *M, double P, FpropsError *err){
87 unsigned i;
88 int Region;
89 /* enum Region_Enum {SUPERCRIT, GASEOUS, LIQUID, VAPOR, SAT_VLE} Region; */
90 #define SAT_VLE LIQUID+VAPOR
91 char *region_names[] = {
92 "super-critical",
93 "gaseous",
94 "vapor",
95 "liquid",
96 "solid",
97 "vapor-liquid equilibrium (saturation)"
98 };
99
100 #if 0
101 #define TT M->T
102 #define RHOS M->rhos
103 #define NPURE M->X->pures
104 #define PF M->X->PF
105 #endif
106 #define D PF[i]->data
107 for(i=0;i<NPURE;i++){
108 if(TT >= D->T_c){ /* temperature >= critical temperature */
109 if(P >= D->p_c){ /* pressure >= critical pressure */
110 RHOS[i] = D->rho_c; /* super-critical fluid */
111 Region = SUPERCRIT;
112 }else{ /* true gas (as opposed to sub-critical vapor) */
113 RHOS[i] = P / D->R / TT; /* density from ideal gas */
114 Region = GAS;
115 }
116 }else{
117 if(P >= D->p_c){ /* pressure >= critical pressure -- liquid */
118 RHOS[i] = fprops_rhof_T_rackett(TT, D); /* density from saturation liquid */
119 Region = LIQUID;
120 }else{ /* now we're getting into hard conditions */
121 double T_sat, /* find saturation temperature, densities */
122 rho_liq,
123 rho_vap;
124 fprops_sat_p(P, &T_sat, &rho_liq, &rho_vap, PF[i], err);
125
126 if(TT > T_sat){ /* this is the last condition that we can precisely find */
127 RHOS[i] = rho_vap; /* vapor phase */
128 Region = VAPOR;
129 }else{
130 RHOS[i] = (rho_liq + rho_vap) / 2; /* both liquid and vapor */
131 /*
132 We are in vapor-liquid equilibrium, and the density
133 cannot be determined without knowing how much of the
134 substance is in the vapor and how much in the liquid.
135 */
136 Region = SAT_VLE;
137 }
138 }
139 }
140 MSG("The substance %s was assigned a density of %.5f kg/m3;"
141 , PF[i]->name, RHOS[i]);
142 printf("\n\t it is in the %s region, since"
143 "\n\t\tCritical temperature T_c=%.2f K \tand current temperature T=%.2f K;"
144 "\n\t\tCritical pressure P_c=%.0f Pa\tand current pressure P=%.0f Pa."
145 , region_names[Region], D->T_c, TT, D->p_c, P);
146 }
147 #undef D
148 #if 0
149 #undef NPURE
150 #undef RHOS
151 #undef TT
152 #undef PF
153 #endif
154 }
155
156 /*
157 Structure to hold auxiliary data for function to find the error in pressure
158 at a given density
159 */
160 typedef struct PressureRhoData_Struct {
161 double T;
162 double P;
163 PureFluid *pfl;
164 FpropsError *err;
165 } PRData;
166
167 SecantSubjectFunction pressure_rho_error;
168 double pressure_rho_error(double rho, void *user_data){
169 PRData *prd = (PRData *)user_data;
170 FluidState fst = {prd->T, rho, prd->pfl};
171 MSG("Pressure while seeking P=%.0f Pa, trying rho=%.6g kg/m^3, here P=%.0f Pa"
172 ,prd->P, rho, fprops_p(fst, prd->err));
173
174 return fabs(prd->P - fprops_p(fst, prd->err)) / fabs(prd->P);
175 }
176
177 /*
178 Calculate realistic densities `rho_out' in ideal-solution, such that
179 densities are consistent with the given temperature and pressure
180 */
181 void pressure_rhos(MixtureState *M, double P, double tol, FpropsError *err){
182 /*
183 Find actual density by searching for the individual densities which
184 each satisfy the equation P_i(T, \rho_i) = P, starting from ideal-gas
185 density.
186
187 In production code, it might be advantageous to find several different
188 starting points for this search; e.g. if P > critical pressure, start
189 from critical density, etc. [NOTE: this is done in initial_rhos, above]
190
191 Compare with:
192 zeroin_solve in zeroin.c
193 helmholtz_sat in helmholtz.c
194 sat_p_resid, fprops_sat_p in sat.c (example of using zeroin_solve)
195 fprops_sat_hf in sat.c
196 fprops_solve_ph in solve_ph.c (uses fprops_sat_p from sat.c)
197 densities_to_mixture in init_mix.c or this file
198 densities_Ts_to_mixture in init_mix.c or this file
199 */
200 unsigned i1; /* counter variables */
201
202 for(i1=0;i1<NPURE;i1++){
203 PRData prd = {TT, P, PF[i1], err};
204 double rhos[] = {RHOS[i1], 1.01*RHOS[i1]};
205
206 secant_solve(&pressure_rho_error, &prd, rhos, tol);
207
208 RHOS[i1] = rhos[0];
209 }
210 }
211
212 /*
213 Structure to hold auxiliary data for function to find error in internal
214 energy at a given pressure.
215 */
216 typedef struct IntEnergyPressure_Struct {
217 double U;
218 double tol;
219 MixtureState *M;
220 FpropsError *err;
221 } IEData;
222
223 SecantSubjectFunction energy_p_error;
224 double energy_p_error(double P, void *user_data){
225 IEData *ied = (IEData *)user_data;
226 pressure_rhos(ied->M, P, ied->tol, ied->err);
227
228 return fabs(ied->U - mixture_u(ied->M, ied->err));
229 }
230
231 /*
232 Calculate realistic densities in ideal-solution, such that the internal
233 energy remains the same from pre-mixing to post-mixing conditions, starting
234 from a single temperature but disparate densities.
235
236 That is, if the component pure fluids for a solution are provided at
237 different/incompatible densities (which essentially means that they have
238 different pressures), find the set of solution densities that:
239 1. are `compatible' (result in the same pressure)
240 2. give the same overall internal energy as do the inconsistent
241 densities.
242
243 For now, I am using a secant[-like] method to find the pressure P that
244 satisfies the second condition above; densities are calculated using the
245 function pressure_rhos from fprops/mixtures/init_mixfuncs.c, and
246 averaged to see if the average density equals the original density.
247
248 The uniform-pressure condition (1) is satisfied automatically by using a
249 single pressure to find densities.
250 */
251 void densities_to_mixture(MixtureState *M, double tol, FpropsError *err){
252 #define XS M->X->Xs
253 unsigned i;
254 double u_avg = mixture_u(M, err); /* original average internal energy */
255 double h_avg = mixture_h(M, err); /* original average enthalpy */
256
257 double p[]={0.0, 0.0};
258
259 /*
260 Find average pressure in the solution, and set p1 equal to that; set p2
261 */
262 for(i=0;i<NPURE;i++){
263 p[0] += fprops_p((FluidState){TT,RHOS[i],PF[i]}, err);
264 }
265 p[0] /= NPURE;
266 p[1] = 1.1 * p[0];
267
268 IEData ied = {u_avg, tol, M, err};
269 secant_solve(&energy_p_error, &ied, p, tol);
270
271 /* confirm that when internal energy does not change, neither does enthalpy */
272 if(fabs(h_avg - mixture_h(M,err)) < 2*tol){
273 MSG("Average enthalpy remained constant at h=% .6g in mixing", h_avg);
274 }else{
275 MSG("Average enthalpy did not remain constant:"
276 "\n\tthe average from before mixing is h=% .6g,"
277 "\n\tthe average from after mixing is h=% .6g",
278 h_avg, mixture_h(M, err));
279 } puts("");
280 /*
281 The check using enthalpy indicates that enthalpy does change if total
282 volume is held constant (h_before = 1.09477e6, h_after=1.09714e6 J/kg
283 with starting densities 2, 3, 2.5, 1.7 kg/m3 for N2, NH3, CO2, CH4). I
284 therefore changed from using the average pre-mixing volume to using
285 pre-mixing internal energy. This will also be checked with enthalpy.
286
287 With the same densities as before, solving with internal energy, the
288 enthalpy still changes, although much less (h_before = 1.09477e6,
289 h_after = 1.09479e6 J/kg). I judge that both methods seem to give
290 similar results, although solving with internal energy seems to do
291 better.
292 */
293 }
294 #endif
295
296 /* Mixture-Property Functions */
297 /*
298 Calculate overall mass density of a mixture of components
299
300 @param M MixtureState with number of components, mass fractions, densities
301
302 @return mass density of mixture
303 */
304 double amixture_rho(MixtureState *M){
305 unsigned i;
306 double x_total=0.0; /* sum over all mass fractions -- to check consistency */
307 double vol_mix=0.0; /* volume per unit mass of the mixture, iteratively summed */
308
309 #if 0
310 #define XS M->X->Xs
311 #endif
312 for(i=0;i<NPURE;i++){
313 vol_mix += XS[i] / RHOS[i];
314 x_total += XS[i];
315 }
316 if(fabs(x_total - 1) > MIX_XTOL){
317 ERRMSG(MIX_XSUM_ERROR, x_total);
318 }
319 return 1 / vol_mix;
320 #if 0
321 #undef XS
322 #undef PF
323 #undef NPURE
324 #undef RHOS
325 #undef TT
326 #endif
327 }
328
329 /*
330 Calculate overall ideal-solution internal energy per unit mass in a mixture
331 of pure components.
332
333 @param M MixtureState with temperature, mass fractions, component densities, etc.
334 @param err error argument
335
336 @return ideal-solution internal energy
337 */
338 double amixture_u(MixtureState *M, FpropsError *err){
339 unsigned i;
340 double x_total=0.0; /* sum over all mass fractions -- to check consistency */
341 double u_mix=0.0; /* internal energy of the mixture, iteratively summed */
342
343 for(i=0;i<NPURE;i++){
344 u_mix += XS[i] * fprops_u((FluidState){TT,RHOS[i],PF[i]}, err);
345 x_total += XS[i];
346 }
347 if(fabs(x_total - 1) > MIX_XTOL){
348 ERRMSG(MIX_XSUM_ERROR, x_total);
349 }
350 return u_mix;
351 }
352
353 /*
354 Calculate overall ideal-solution enthalpy per unit mass in a mixture of pure
355 components.
356
357 @param M MixtureState with temperature, mass fractions, component densities, etc.
358 @param err error argument
359
360 @return ideal-solution enthalpy
361 */
362 double amixture_h(MixtureState *M, FpropsError *err){
363 unsigned i;
364 double x_total=0.0; /* sum over all mass fractions -- to check consistency */
365 double h_mix=0.0; /* enthalpy of mixture, iteratively summed */
366
367 for(i=0;i<NPURE;i++){
368 h_mix += XS[i] * fprops_h((FluidState){TT,RHOS[i],PF[i]}, err);
369 x_total += XS[i];
370 }
371 if(fabs(x_total - 1) > MIX_XTOL){
372 ERRMSG(MIX_XSUM_ERROR, x_total);
373 }
374 return h_mix;
375 }
376
377 /*
378 Calculate overall ideal-solution constant-pressure heat capacity (per unit
379 of mass), in a mixture of pure components
380
381 @param M MixtureState with temperature, mass fractions, component densities, etc.
382 @param err error argument
383
384 @return ideal-solution heat capacity (constant-pressure)
385 */
386 double amixture_cp(MixtureState *M, FpropsError *err){
387 unsigned i;
388 double x_total=0.0; /* sum over all mass fractions -- to check consistency */
389 double cp_mix=0.0; /* constant-pressure heat capacity of mixture, iteratively summed */
390
391 for(i=0;i<NPURE;i++){
392 cp_mix += XS[i] * fprops_cp((FluidState){TT,RHOS[i],PF[i]}, err);
393 x_total += XS[i];
394 }
395 if(fabs(x_total - 1) > MIX_XTOL){
396 ERRMSG(MIX_XSUM_ERROR, x_total);
397 }
398 return cp_mix;
399 }
400
401 /*
402 Calculate overall ideal-solution constant-volume heat capacity (per unit of
403 mass), in a mixture of pure components
404
405 @param M MixtureState with temperature, mass fractions, component densities, etc.
406 @param err error argument
407
408 @return ideal-solution heat capacity (constant-volume)
409 */
410 double amixture_cv(MixtureState *M, FpropsError *err){
411 unsigned i;
412 double x_total=0.0; /* sum over all mass fractions -- to check consistency */
413 double cv_mix=0.0; /* constant-volume heat capacity of mixture, iteratively summed */
414
415 for(i=0;i<NPURE;i++){
416 cv_mix += XS[i] * fprops_cv((FluidState){TT,RHOS[i],PF[i]}, err);
417 x_total += XS[i];
418 }
419 if(fabs(x_total - 1) > MIX_XTOL){
420 ERRMSG(MIX_XSUM_ERROR, x_total);
421 }
422 return cv_mix;
423 }
424
425 #if 0
426 /*
427 Calculate the value of the sum over all *mole* fractions x_i, of the mole
428 fraction times the natural logarithm of the mole fraction:
429 \sum\limits_i x_i \ln(x_i)
430
431 This quantity is used in calculating second-law mixture properties for ideal
432 solutions
433
434 @param nPure number of pure components
435 @param Xs array with mass fraction of each component
436 @param PFs array of pointers to PureFluid structures representing components
437
438 @return sum over all components of mole fraction times natural logarithm of
439 mole fraction.
440 */
441 double mixture_x_ln_x(unsigned nPure, double *x_mass, PureFluid **PFs){
442 unsigned i;
443 double x_total=0.0, /* sum over all mass fractions -- to check consistency */
444 x_mole, /* mole fraction of current component in the loop */
445 rM_avg=0.0, /* reciprocal average molar mass */
446 x_ln_x=0.0; /* sum of (x_i * ln(x_i)) over all `i' */
447
448 for(i=0;i<nPure;i++){ /* find the reciprocal average molar mass */
449 /* add mass fraction over molar mass */
450 rM_avg += x_mass[i] / PFs[i]->data->M;
451 x_total += x_mass[i];
452 }
453 /* return error if sum of mole fractions is not 1.00 */
454 if(fabs(x_total - 1) > MIX_XTOL){
455 ERRMSG(MIX_XSUM_ERROR, x_total);
456 }
457
458 for(i=0;i<nPure;i++){ /* Find the summation we came for */
459 x_mole = (x_mass[i] / PFs[i]->data->M) * rM_avg;
460 x_ln_x += x_mass[i] / PFs[i]->data->M * log(x_mole);
461 }
462 return x_ln_x;
463 }
464 #endif
465
466 /*
467 Calculate the overall ideal-solution entropy per unit mass in a mixture of
468 pure components
469
470 @param M MixtureState with temperature, mass fractions, component densities, etc.
471 @param err error argument
472 */
473 double amixture_s(MixtureState *M, FpropsError *err){
474 #define D PF[0]->data
475 unsigned i;
476 double x_total=0.0, /* sum over all mass fractions -- to check consistency */
477 s_mix=0.0; /* entropy of mixture, iteratively summed */
478 double R = D->R * D->M; /* ideal gas constant */
479
480 for(i=0;i<NPURE;i++){
481 s_mix += XS[i] * fprops_s((FluidState){TT,RHOS[i],PF[i]}, err);
482 x_total += XS[i];
483 }
484 if(fabs(x_total - 1) > MIX_XTOL){
485 ERRMSG(MIX_XSUM_ERROR, x_total);
486 }
487 return s_mix - (R * mixture_x_ln_x(NPURE,XS,PF));
488 }
489
490 /*
491 Calculate overall ideal-solution Gibbs energy per unit mass in a mixture
492
493 @param M MixtureState with temperature, mass fractions, component densities, etc.
494 @param err error argument
495 */
496 double amixture_g(MixtureState *M, FpropsError *err){
497 unsigned i;
498 double x_total=0.0, /* sum over all mass fractions -- to check consistency */
499 g_mix=0.0; /* entropy of mixture, iteratively summed */
500 double R = D->R * D->M; /* ideal gas constant */
501
502 for(i=0;i<NPURE;i++){
503 g_mix += XS[i] * fprops_g((FluidState){TT,RHOS[i],PF[i]}, err);
504 x_total += XS[i];
505 }
506 if(fabs(x_total - 1) > MIX_XTOL){
507 ERRMSG(MIX_XSUM_ERROR, x_total);
508 }
509 return g_mix + (R * TT * mixture_x_ln_x(NPURE,XS,PF));
510 }
511
512 /*
513 Calculate overall ideal-solution Helmholtz energy per unit mass in a mixture
514
515 @param M MixtureState with temperature, mass fractions, component densities, etc.
516 @param err error argument
517 */
518 double amixture_a(MixtureState *M, FpropsError *err){
519 unsigned i;
520 double x_total=0.0, /* sum over all mass fractions -- to check consistency */
521 a_mix=0.0; /* entropy of mixture, iteratively summed */
522 double R = D->R * D->M; /* ideal gas constant */
523
524 for(i=0;i<NPURE;i++){
525 a_mix += XS[i] * fprops_a((FluidState){TT,RHOS[i],PF[i]}, err);
526 x_total += XS[i];
527 }
528 if(fabs(x_total - 1) > MIX_XTOL){
529 ERRMSG(MIX_XSUM_ERROR, x_total);
530 }
531 return a_mix + (R * TT * mixture_x_ln_x(NPURE,XS,PF));
532 #if 1
533 #undef D
534 #undef XS
535 #undef PF
536 #undef NPURE
537 #undef RHOS
538 #undef TT
539 #endif
540 }
541
542 /* Mixture-Display Functions */
543 #if 0
544 /*
545 Print properties of a mixture, with correct formatting
546 */
547 void print_mixture_properties(char *how_calc, double rho, double u, double h, double cp, double cv, double s, double g, double a){
548 printf("\n %s %s"
549 "\n\t%s is\t\t: %.6f kg/m3"
550 "\n\t%s is\t: %g J/kg"
551 "\n\t%s is\t\t: %g J/kg"
552 "\n\t%s is\t: %g J/kg/K"
553 "\n\t%s is\t: %g J/kg/K"
554 "\n\t%s is\t\t: %g J/kg/K"
555 "\n\t%s is\t: %g J/kg"
556 "\n\t%s is\t: %g J/kg\n",
557 "For the mixture properties calculated", how_calc,
558 "The density of the mixture", rho,
559 "The internal energy of the mixture", u,
560 "The enthalpy of the mixture", h,
561 "The constant-pressure heat capacity", cp,
562 "The constant-volume heat capacity", cv,
563 "The entropy of the mixture", s,
564 "The Gibbs energy of the mixture", g,
565 "The Helmholtz energy of the mixture", a);
566 }
567
568 /*
569 Print table of properties for different substances
570 */
571 void print_substances_properties(const unsigned subst, char **headers, double *Xs, double *rhos, double *ps, double *us, double *hs, double *cps, double *cvs, double *ss, double *gs, double *as){
572 #define TBL_ROWS 11
573
574 unsigned i1,i2,i3;
575 unsigned col_width[20]={0};
576
577 double *vals[TBL_ROWS-1]={
578 Xs, rhos, ps, us, hs, cps, cvs, ss, gs, as
579 };
580
581 char *forms[TBL_ROWS-1]={
582 "% .6f", "% .6f", "%9.1f", "% .6g", "% .6g", "% .6g",
583 "% .6g", "% .6g", "% .6g", "% .6g"
584 };
585 char *sides[TBL_ROWS]={
586 "SUBSTANCES",
587 "MASS FRACTION",
588 "DENSITY (kg/m3)",
589 "PRESSURE (Pa)",
590 "INTERNAL ENERGY (J/kg)",
591 "ENTHALPY (J/kg)",
592 "C_P (J/kg/K)",
593 "C_V (J/kg/K)",
594 "ENTROPY (J/kg/K)",
595 "GIBBS ENERGY (J/kg)",
596 "HELMHOLTZ ENERGY (J/kg)"
597 };
598 char *cont[TBL_ROWS][subst+1];
599
600 PREPARE_TABLE(TBL_ROWS,subst+1,headers,sides,vals,forms,cont);
601 PRINT_STR_TABLE(TBL_ROWS,subst+1,col_width,cont);
602
603 #undef TBL_ROWS
604 }
605
606 /*
607 Print table of properties for different cases
608 */
609 void print_cases_properties(unsigned cases, char **headers, double *rhos, double *ps, double *us, double *hs, double *cps, double *cvs, double *ss, double *gs, double *as){
610 #define TBL_ROWS 10
611
612 unsigned i1,i2,i3;
613 unsigned col_width[20]={0};
614
615 double *vals[TBL_ROWS-1]={
616 rhos, ps, us, hs, cps, cvs, ss, gs, as
617 };
618
619 char *forms[TBL_ROWS-1]={
620 "% .6f", "%9.1f", "% .6g", "% .6g", "% .6g",
621 "% .6g", "% .6g", "% .6g", "% .6g"
622 };
623 char *sides[TBL_ROWS]={
624 "CASES",
625 "DENSITY (kg/m3)",
626 "PRESSURE (Pa)",
627 "INTERNAL ENERGY (J/kg)",
628 "ENTHALPY (J/kg)",
629 "C_P (J/kg/K)",
630 "C_V (J/kg/K)",
631 "ENTROPY (J/kg/K)",
632 "GIBBS ENERGY (J/kg)",
633 "HELMHOLTZ ENERGY (J/kg)"
634 };
635 char *cont[TBL_ROWS][cases+1];
636
637 PREPARE_TABLE(TBL_ROWS,cases+1,headers,sides,vals,forms,cont);
638 PRINT_STR_TABLE(TBL_ROWS,cases+1,col_width,cont);
639
640 #undef TBL_ROWS
641 }
642 #endif

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22