1 /**********************************************************
2  * Version $Id: fireLib.c 911 2011-02-14 16:38:15Z reklov_w $
3  *********************************************************/
4 /*
5  *******************************************************************************
6  *
7  *  fireLib.c
8  *
9  *  Description
10  *      Library of BEHAVE (Andrews 1986) fire behavior algorithms
11  *      encapsulated and optimized for fire behavior simulation.
12  *
13  *  Legalities
14  *      Copyright (c) 1996 Collin D. Bevins.
15  *      See the file "license.txt" for information on usage and
16  *      redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17  *
18  *  Naming Conventions
19  *      All function names begin with "Fire_".
20  *      All fuel model and behavior parameter access macros begin with "Fuel_".
21  *      All fuel catalog parameter access macros begin with "FuelCat_".
22  *
23  *  Functions
24  *      There are 8 functions to create and destroy fuel models and catalogs:
25  *
26  *          Fire_FuelCatalogCreate(name, maxModels)
27  *              Creates a new fuel catalog capable of holding maxModels.
28  *
29  *          Fire_FuelCatalogCreateStandard(name, maxModels)
30  *              Creates a new fuel catalog capable of holding maxModels,
31  *              and fills models 0-13 with standard fire behavior models.
32  *
33  *          Fire_FuelModelCreate(catalog, model, name, desc, depth, mext,
34  *                  adjust, maxParticles)
35  *              Adds or replaces a fuel model in the catalog.  The model will
36  *              accept up to maxParticles particles.
37  *
38  *          Fire_FuelModelExists(catalog, model)
39  *              Returns 1 if model exists within the catalog.
40  *
41  *          Fire_FuelParticleAdd(catalog, model, live, load, savr, dens, heat,
42  *                  stot, seff)
43  *              Adds a fuel particle to a fuel model.
44  *
45  *          Fire_FlameLengthTable ( catalog, flameClasses, flameStep )
46  *              Creates a flame length look-up table containing flameClasses
47  *              number of classes, with each class spanning "flameStep"
48  *              feet of flame length.  Creating a flame length table can
49  *              significantly improve performance at the expense of user
50  *              specified precision.
51  *
52  *          Fire_FuelModelDestroy(catalog, model)
53  *              Destroys the model within the catalog.
54  *
55  *          Fire_FuelCatalogDestroy(catalog)
56  *              Destroys the catalog and all models within it.
57  *
58  *      There are 5 functions to process data within fuel models:
59  *
60  *          Fire_FuelCombustion(catalog, model)
61  *              Computes all the fuel-dependent model variables.
62  *              Called only once for each fuel model.
63  *              Called automatically by Fire_SpreadNoWindNoSlope().
64  *
65  *          Fire_SpreadNoWindNoSlope(catalog, model, moisture[6])
66  *              Determines reaction intensity, heat per unit area, and the
67  *              no-wind no-slope spread rate.
68  *
69  *          Fire_SpreadWindSlopeMax(catalog, model, windFpm, windDeg, slope,
70  *                  aspectDeg)
71  *              Determines maximum spread rate and azimuth of maximum spread
72  *              based upon input parameters and results of the most recent
73  *              call to Fire_SpreadNoWindNoSlope() for this model.
74  *
75  *          Fire_SpreadAtAzimuth(catalog, model, azimuth, doWhich)
76  *              Determines the spread rate in the specified azimuth based
77  *              upon the results of the most recent call to
78  *              Fire_SpreadWindSlopeMax() for this model.  The "doWhich"
79  *              parameter is the result of ORing the constants FIRE_BYRAMS,
80  *              FIRE_FLAME, and FIRE_SCORCH to request computation of the
81  *              associated fire variables.
82  *
83  *          Fire_FlameScorch(catalog, model, doWhich)
84  *              Determines the flame length and/or scorch height based upon
85  *              the most recent call to Fire_SpreadAtAzimuth().
86  *
87  *  History
88  *      1996/09/04  Version 1.0.0 release.
89  *      1999/03/05  Fixed NNFL07 live SAVR from 1500 to 1550.
90  *
91  *******************************************************************************
92  */
93 
94 #include "fireLib.h"
95 
96 #ifndef M_PI
97 #define M_PI 3.14159
98 #endif
99 
100 /*
101  *******************************************************************************
102  *
103  *  Fire_FuelCombustion()
104  *
105  *  Description
106  *      Calculates and stores all the fuel-dependent combustion variables.
107  *
108  *  Side Effects
109  *      All combustion varaiables are reclaculated for the model.
110  *      All behavior and environment variables are reset to zero.
111  *
112  *  Function Returns
113  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
114  *      Return status and error text are stored in the Fire Catalog's buffers.
115  *
116  *******************************************************************************
117  */
118 
119 int
Fire_FuelCombustion(FuelCatalogPtr catalog,size_t model)120 Fire_FuelCombustion (FuelCatalogPtr catalog, size_t model )
121     //FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
122     //size_t         model;       /* fuel model id number         [0-maxModels] */
123 {
124     size_t particle, size, life;
125 
126     double sizeClassAreaWtg[FIRE_LIFE_CATS][FIRE_SIZE_CLASSES];
127     double lifeLoad[FIRE_LIFE_CATS];
128     double lifeArea[FIRE_LIFE_CATS];
129     double lifeSavr[FIRE_LIFE_CATS];
130     double lifeHeat[FIRE_LIFE_CATS];
131     double lifeSeff[FIRE_LIFE_CATS];
132     double lifeEtaS[FIRE_LIFE_CATS];
133 
134     double totalArea;
135     double fineLive;
136     double beta;
137     double betaOpt;
138     double sigma;
139     double ratio;
140     double aa;
141     double sigma15;
142     double gammaMax;
143     double gamma;
144     double c;
145     double e;
146 
147     /* Validate catalog and fuel model existence. */
148     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
149     if ( ! Fire_FuelModelExists(catalog,model) )
150     {
151         sprintf(FuelCat_Error(catalog),
152             "Fire_FuelCombustion(): el modelo de combustible %d no existe en el cat�logo de combuestibles \"%s\".",
153             model, FuelCat_Name(catalog));
154         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
155     }
156 
157     /* Initialize the model's fuel particle dependent variables. */
158     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
159     {
160         Fuel_AreaWtg(catalog,model,particle)     = 0.;
161         Fuel_SizeAreaWtg(catalog,model,particle) = 0.;
162         Fuel_Moisture(catalog,model,particle)    = 0.;
163     }
164 
165     /* Initialize the model's fuel combustion variables. */
166     /* The following are calculated by this function. */
167     Fuel_FineDead(catalog,model)        = 0.0;
168     Fuel_LiveMextFactor(catalog,model)  = 0.0;
169     Fuel_BulkDensity(catalog,model)     = 0.0;
170     Fuel_ResidenceTime(catalog,model)   = 0.0;
171     Fuel_PropFlux(catalog,model)        = 0.0;
172     Fuel_SlopeK(catalog,model)          = 0.0;
173     Fuel_WindB(catalog,model)           = 0.0;
174     Fuel_WindE(catalog,model)           = 0.0;
175     Fuel_WindK(catalog,model)           = 0.0;
176 
177     for (life=0; life<FIRE_LIFE_CATS; life++)
178     {
179         Fuel_LifeAreaWtg(catalog,model,life) = 0.;
180         Fuel_LifeRxFactor(catalog,model,life) = 0.;
181         lifeLoad[life] = 0.;
182         lifeArea[life] = 0.;
183         lifeSavr[life] = 0.;
184         lifeHeat[life] = 0.;
185         lifeEtaS[life] = 0.;
186         lifeSeff[life] = 0.;
187         for ( size=0; size<FIRE_SIZE_CLASSES; size++ )
188             sizeClassAreaWtg[life][size] = 0.;
189     }
190 
191     /* Initialize the model's fire behavior variables. */
192     /* These are calculated by Fire_SpreadNoWindNoSlope(). */
193     Fuel_Spread0(catalog,model)         = 0.;
194     Fuel_RxIntensity(catalog,model)     = 0.;
195     Fuel_HeatPerUnitArea(catalog,model) = 0.;
196 
197     /* Initialize the model's fire behavior variables. */
198     /* These are calculated by Fire_SpreadWindSlopeMax(). */
199     Fuel_SpreadMax(catalog,model)       = 0.;
200     Fuel_AzimuthMax(catalog,model)      = 0.;
201     Fuel_EffectiveWind(catalog,model)   = 0.;
202     Fuel_PhiSlope(catalog,model)        = 0.;
203     Fuel_PhiWind(catalog,model)         = 0.;
204     Fuel_PhiEffWind(catalog,model)      = 0.;
205     Fuel_LwRatio(catalog,model)         = 1.;
206     Fuel_Eccentricity(catalog,model)    = 0.;
207     Fuel_WindLimit(catalog,model)       = 0;
208 
209     /* Initialize the model's fire behavior variables. */
210     /* These are calculated by Fire_SpreadAtAzimuth(). */
211     Fuel_SpreadAny(catalog,model)       = 0.;
212     Fuel_AzimuthAny(catalog,model)      = 0.;
213     Fuel_ByramsIntensity(catalog,model) = 0.;
214     Fuel_FlameLength(catalog,model)     = 0.;
215     Fuel_ScorchHeight(catalog,model)    = 0.;
216 
217     /* Initialize the model's environmental variables. */
218     Fuel_WindSpeed(catalog,model) = 0.;
219     Fuel_WindDir(catalog,model)   = 0.;
220     Fuel_Slope(catalog,model)     = 0.;
221     Fuel_Aspect(catalog,model)    = 0.;
222     for ( size=0; size<FIRE_MCLASSES; size++ )
223         Fuel_EnvMoisture(catalog,model,size) = 0.;
224 
225     /* Initialize the model's combustion flag. */
226     Fuel_CombustionFlag(catalog,model) = 1;
227 
228     /* If the model has no particles, we're all done. */
229     if ( Fuel_Particles(catalog,model) <= 0 )
230         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
231 
232     /* Initialize local fuel bed and combustion variables. */
233     beta = betaOpt = sigma = ratio = aa = sigma15 = 0.;
234     gamma = gammaMax = c = e = fineLive = totalArea = 0.;
235 
236     /* Accumulate surface areas by life category for the entire fuel bed. */
237     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
238     {
239         life = Fuel_Live(catalog,model,particle);
240         lifeArea[life] += Fuel_SurfaceArea(catalog,model,particle);
241         totalArea      += Fuel_SurfaceArea(catalog,model,particle);
242     }
243 
244     /* If no surface area, we're all done. */
245     if ( totalArea <= Smidgen )
246         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
247 
248     /* Surface area wtg factor for each particle within its life category */
249     /* and within its size class category (used to weight loading). */
250     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
251     {
252         life = Fuel_Live(catalog,model,particle);
253         if ( lifeArea[life] > Smidgen )
254         {
255             Fuel_AreaWtg(catalog,model,particle) =
256                 Fuel_SurfaceArea(catalog,model,particle) / lifeArea[life];
257 
258             size = Fuel_SizeClass(catalog,model,particle);
259             sizeClassAreaWtg[life][size] +=
260                 Fuel_AreaWtg(catalog,model,particle);
261         }
262     }
263 
264     /* Assign size class surface area weights to each particle. */
265     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
266     {
267         life = Fuel_Live(catalog,model,particle);
268         size = Fuel_SizeClass(catalog,model,particle);
269         Fuel_SizeAreaWtg(catalog,model,particle) = sizeClassAreaWtg[life][size];
270     }
271 
272     /* Derive life category surface area weighting factors. */
273     for ( life=0; life<FIRE_LIFE_CATS; life++ )
274         Fuel_LifeAreaWtg(catalog,model,life) = lifeArea[life] / totalArea;
275 
276     /* Accumulate life category weighted load, heat, savr, and seff. */
277     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
278     {
279         life = Fuel_Live(catalog,model,particle);
280 
281         lifeLoad[life] += Fuel_SizeAreaWtg(catalog,model,particle)
282                         * Fuel_Load(catalog,model,particle)
283                         * (1. - Fuel_SiTotal(catalog,model,particle));
284 
285         lifeSavr[life] += Fuel_AreaWtg(catalog,model,particle)
286                         * Fuel_Savr(catalog,model,particle);
287 
288         lifeHeat[life] += Fuel_AreaWtg(catalog,model,particle)
289                         * Fuel_Heat(catalog,model,particle);
290 
291         lifeSeff[life] += Fuel_AreaWtg(catalog,model,particle)
292                         * Fuel_SiEffective(catalog,model,particle);
293 
294         Fuel_BulkDensity(catalog,model) += Fuel_Load(catalog,model,particle);
295 
296         if ( Fuel_Density(catalog,model,particle) > Smidgen )
297             beta += Fuel_Load(catalog,model,particle)
298                   / Fuel_Density(catalog,model,particle);
299     }
300 
301     /* Accumulate life category contribution to reaction intensity. */
302     for ( life=0; life<FIRE_LIFE_CATS; life++ )
303     {
304         sigma += Fuel_LifeAreaWtg(catalog,model,life) * lifeSavr[life];
305 
306         lifeEtaS[life] = 1.;
307         if (lifeSeff[life] > 0.)
308         {
309             if ( (lifeEtaS[life] = 0.174 / pow(lifeSeff[life], 0.19)) > 1.0 )
310                 lifeEtaS[life] = 1.0;
311         }
312 
313         Fuel_LifeRxFactor(catalog,model,life) =
314             lifeLoad[life] * lifeHeat[life] * lifeEtaS[life];
315     }
316 
317     /* Fuel model residence time */
318     Fuel_ResidenceTime(catalog,model) = 384. / sigma;
319 
320     /* Fuel model bulk density */
321     if ( Fuel_Depth(catalog,model) > Smidgen )
322     {
323         Fuel_BulkDensity(catalog,model) /= Fuel_Depth(catalog,model);
324         beta /= Fuel_Depth(catalog,model);
325     }
326 
327     /* Propagating flux depends upon sigma and beta only. */
328     Fuel_PropFlux(catalog,model) =
329         exp((0.792 + 0.681*sqrt(sigma)) * (beta+0.1)) / (192.+0.2595*sigma);
330 
331     /* Gamma */
332     betaOpt   = 3.348 / (pow(sigma, 0.8189));
333     ratio     = beta / betaOpt;
334     aa        = 133. / (pow(sigma, 0.7913));
335     sigma15   = pow(sigma, 1.5);
336     gammaMax  = sigma15 / (495. + 0.0594*sigma15);
337     gamma     = gammaMax * pow(ratio, aa) * exp(aa * (1.-ratio));
338 
339     /* Factor gamma into life category reaction intensity contribution. */
340     for ( life=0; life<FIRE_LIFE_CATS; life++ )
341         Fuel_LifeRxFactor(catalog,model,life) *= gamma;
342 
343     /* Slope and wind intermediates constants for the fuel model. */
344     Fuel_SlopeK(catalog,model) = 5.275 * pow(beta, -0.3);
345     Fuel_WindB(catalog,model)  = 0.02526 * pow(sigma, 0.54);
346 
347     c = 7.47 * exp(-0.133 * pow(sigma, 0.55));
348     e = 0.715 * exp(-0.000359 * sigma);
349     Fuel_WindK(catalog,model) = c * pow(ratio, -e);
350     Fuel_WindE(catalog,model) = pow(ratio, e) / c;
351 
352     /* If no live fuel, we're done. */
353     if ( lifeLoad[FIRE_LIFE_LIVE] < Smidgen )
354         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
355 
356     /*  Fine dead fuel and fine live fuel factors. */
357     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
358     {
359         if ( Fuel_Live(catalog,model,particle) )
360             fineLive
361                   += Fuel_Load(catalog,model,particle)
362                    * exp(-500. / Fuel_Savr(catalog,model,particle));
363         else
364             Fuel_FineDead(catalog,model)
365                   += Fuel_Load(catalog,model,particle)
366                    * Fuel_SigmaFactor(catalog,model,particle);
367     }
368 
369     /* Live fuel extinction moisture factor. */
370     if ( fineLive > Smidgen )
371         Fuel_LiveMextFactor(catalog,model)
372             = 2.9 * Fuel_FineDead(catalog,model) / fineLive;
373 
374     /* That's all, folks!. */
375     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
376 }
377 
378 /*
379  *******************************************************************************
380  *
381  *  Fire_SpreadNoWindNoSlope()
382  *
383  *  Description
384  *      Calculates the fire reaction intensity and no-wind, no-slope spread
385  *      rate given the fuel model, combustion, and moisture regime inputs.
386  *
387  *  Side Effects
388  *      Updates the following fire behavior variables:
389  *          Fuel_RxIntensity(catalog,model).
390  *          Fuel_HeatPerUnitArea(catalog,model).
391  *          Fuel_Spread0(catalog,model).
392  *          Fuel_SpreadMax(catalog,model)  = Fuel_Spread0(catalog,model)
393  *          Fuel_SpreadAny(catalog,model)  = Fuel_Spread0(catalog,model)
394  *          Fuel_AzimuthAny(catalog,model) = 0.
395  *          Fuel_AzimuthMax(catalog,model) = 0.
396  *
397  *  Function Returns
398  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
399  *      Return status and error text are stored in the Fire Catalog's buffers.
400  *
401  *******************************************************************************
402  */
403 
404 int
Fire_SpreadNoWindNoSlope(FuelCatalogPtr catalog,size_t model,double moisture[FIRE_MCLASSES])405 Fire_SpreadNoWindNoSlope ( FuelCatalogPtr catalog, size_t model, double moisture[FIRE_MCLASSES] )
406    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
407    // size_t  model;              /* fuel model number            [0-maxModels] */
408    // double  moisture[FIRE_MCLASSES]; /* array of fuel moistures   (fractions) */
409 {
410     size_t mclass, particle, life, nlive;
411     double wfmd;
412     double rbQig;
413     double fdmois;
414     double qig;
415     double ratio;
416     double lifeMoisture[FIRE_LIFE_CATS];
417     double lifeEtaM[FIRE_LIFE_CATS];
418     double lifeMext[FIRE_LIFE_CATS];
419 
420     static size_t TimeLagClass[FIRE_SIZE_CLASSES] = {0, 0, 1, 1, 2, 2};
421 
422     /* Validate the catalog and fuel model. */
423     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
424     if ( ! Fire_FuelModelExists(catalog,model) )
425     {
426         sprintf(FuelCat_Error(catalog),
427             "Fire_SpreadNoWindNoSlope(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
428             model, FuelCat_Name(catalog));
429         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
430     }
431 
432     /*  Check if we must recalculate combustion intermediates. */
433     if ( ! Fuel_CombustionFlag(catalog,model) )
434     {
435         Fire_FuelCombustion(catalog,model);
436     }
437 
438     /* Otherwise check if the moisture environment has changed. */
439     else
440     {
441         for ( mclass=0; mclass<FIRE_MCLASSES; mclass++ )
442             if ( ! Equal(moisture[mclass],Fuel_EnvMoisture(catalog,model,mclass)) )
443                 break;
444 
445         /* If no change in fuel moisture, no more computation is needed. */
446         if ( mclass == FIRE_MCLASSES )
447             return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
448     }
449 
450     /* Store the new moistures in the fuel's environmental moisture array. */
451     for ( mclass=0; mclass<FIRE_MCLASSES; mclass++ )
452         Fuel_EnvMoisture(catalog,model,mclass) = moisture[mclass];
453 
454     /* Initialize the model's fire behavior variables. */
455     /* These are calculated by this function. */
456     Fuel_Spread0(catalog,model)         = 0.;
457     Fuel_RxIntensity(catalog,model)     = 0.;
458     Fuel_HeatPerUnitArea(catalog,model) = 0.;
459 
460     /* Initialize the model's fire behavior variables. */
461     /* These are calculated by Fire_SpreadWindSlopeMax(). */
462     Fuel_SpreadMax(catalog,model)       = 0.;
463     Fuel_AzimuthMax(catalog,model)      = 0.;
464 
465     /* Initialize the model's fire behavior variables. */
466     /* These are calculated by Fire_SpreadAtAzimuth(). */
467     Fuel_SpreadAny(catalog,model)       = 0.;
468     Fuel_AzimuthAny(catalog,model)      = 0.;
469     Fuel_ByramsIntensity(catalog,model) = 0.;
470     Fuel_FlameLength(catalog,model)     = 0.;
471     Fuel_ScorchHeight(catalog,model)    = 0.;
472 
473     /* If no fuel particles, return. */
474     if (Fuel_Particles(catalog,model) <= 0)
475         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
476 
477     /* Initialize local variables. */
478     wfmd = fdmois = rbQig = 0.;
479     for ( life=0; life<FIRE_LIFE_CATS; life++ )
480     {
481         lifeMoisture[life] = 0.;
482         lifeEtaM[life] = 0.;
483         lifeMext[life] = 0.;
484     }
485 
486     /* Assign particle moistures based upon their size class. */
487     nlive = 0;
488     for ( particle=0; particle<Fuel_Particles(catalog,model); particle++ )
489     {
490         /* if this is a dead fuel, accumulate its wtd fuel moisture. */
491         if ( Fuel_Live(catalog,model,particle) == FIRE_LIFE_DEAD )
492         {
493             mclass = TimeLagClass[Fuel_SizeClass(catalog,model,particle)];
494             wfmd += moisture[mclass]
495                   * Fuel_SigmaFactor(catalog,model,particle)
496                   * Fuel_Load(catalog,model,particle);
497         }
498         else
499         {
500             nlive++;
501             mclass = (Fuel_Type(catalog,model,particle) == FIRE_TYPE_HERB) ?
502                 FIRE_MCLASS_HERB : FIRE_MCLASS_WOOD;
503         }
504 
505         /* Assign this particle the fuel moisture for its size class. */
506         Fuel_Moisture(catalog,model,particle) = moisture[mclass];
507     }
508 
509     /* Compute live fuel extinction moisture. */
510     if ( nlive )
511     {
512         fdmois = ( Fuel_FineDead(catalog,model) > Smidgen ) ?
513             (wfmd / Fuel_FineDead(catalog,model)) : 0.;
514 
515         lifeMext[FIRE_LIFE_LIVE]
516             = ((Fuel_LiveMextFactor(catalog,model)
517             * (1.0 - fdmois/Fuel_Mext(catalog,model))) - 0.226);
518 
519         if ( lifeMext[FIRE_LIFE_LIVE] < Fuel_Mext(catalog,model) )
520             lifeMext[FIRE_LIFE_LIVE] = Fuel_Mext(catalog,model);
521     }
522 
523     /* Dead fuel extinction moisture is a fuel model input. */
524     lifeMext[FIRE_LIFE_DEAD] = Fuel_Mext(catalog,model);
525 
526     /* Compute category weighted moisture and accumulate the rbQig. */
527     for (particle=0; particle<Fuel_Particles(catalog,model); particle++)
528     {
529         qig = 250. + 1116. * Fuel_Moisture(catalog,model,particle);
530 
531         life = Fuel_Live(catalog,model,particle);
532 
533         lifeMoisture[life] += Fuel_AreaWtg(catalog,model,particle)
534                             * Fuel_Moisture(catalog,model,particle);
535 
536         rbQig += qig
537                * Fuel_AreaWtg(catalog,model,particle)
538                * Fuel_LifeAreaWtg(catalog,model,life)
539                * Fuel_SigmaFactor(catalog,model,particle);
540     }
541 
542     /* Complete the rbQig calculation. */
543     rbQig *= Fuel_BulkDensity(catalog,model);
544 
545     /*  Compute moisture damping coeff by life class, and combine with the */
546     /*  life class's rx factor to get the total reaction intensity. */
547     for (life=0; life<FIRE_LIFE_CATS; life++)
548     {
549         ratio = 0.;
550         if ( lifeMext[life] > Smidgen )
551         {
552             ratio = lifeMoisture[life] / lifeMext[life];
553             lifeEtaM[life] =
554                 1.0 - 2.59*ratio + 5.11*ratio*ratio - 3.52*ratio*ratio*ratio;
555         }
556 
557         /* If category moisture exceeds category extinction moisture, */
558         /* the damping coefficient is zero. */
559         if ( lifeMoisture[life] >= lifeMext[life] )
560             lifeEtaM[life] = 0.;
561 
562         /* Accumulate total reaction intensity. */
563         Fuel_RxIntensity(catalog,model)
564             += Fuel_LifeRxFactor(catalog,model,life)
565              * lifeEtaM[life];
566     }
567 
568     /* Calculate heat per unit area from rx intensity and residence time. */
569     Fuel_HeatPerUnitArea(catalog,model)
570         = Fuel_RxIntensity(catalog,model)
571         * Fuel_ResidenceTime(catalog,model);
572 
573     /* Calculate no-wind, no-slope spread rate. */
574     Fuel_Spread0(catalog,model)
575         = (rbQig > Smidgen)
576         ? Fuel_RxIntensity(catalog,model) * Fuel_PropFlux(catalog,model) / rbQig
577         : 0.;
578 
579     /* Re-initialize spread in maximum and any azimuth to no wind-no slope. */
580     Fuel_SpreadMax(catalog,model) = Fuel_Spread0(catalog,model);
581     Fuel_SpreadAny(catalog,model) = Fuel_Spread0(catalog,model);
582     Fuel_AzimuthMax(catalog,model) = Fuel_AzimuthAny(catalog,model) = 0.;
583 
584     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
585 }
586 
587 /*
588  *******************************************************************************
589  *
590  *  Fire_SpreadWindSlopeMax()
591  *
592  *  Description
593  *      Calculates maximum fire spread rate and direction under the given
594  *      wind-slope conditions.  Results depend only upon:
595  *      - no wind-slope spread rate
596  *      - wind speed and direction
597  *      - aspect and slope
598  *
599  *  Side Effects
600  *      Updates the following variables:
601  *          Fuel_Slope()                      = slope;
602  *          Fuel_PhiSlope().
603  *          Fuel_Wind()                       = windFpm;
604  *          Fuel_PhiWind().
605  *          Fuel_Aspect(catalog,model)        = aspect;
606  *          Fuel_WindDir(catalog,model)       = windDeg;
607  *          Fuel_PhiEffWind(catalog,model)    = phiEw;
608  *          Fuel_EffectiveWind(catalog,model) = effectiveWind;
609  *          Fuel_WindLimit(catalog,model)     = windLimit;
610  *          Fuel_SpreadMax(catalog,model)     = spreadMax;
611  *          Fuel_AzimuthMax(catalog,model)    = azimuthMax;
612  *          Fuel_LwRatio(catalog,model)       = lwRatio;
613  *          Fuel_Eccentricity(catalog,model)  = eccentricity;
614  *
615  *      Resets Fire_SpreadAtAzimuth() variables:
616  *          Fuel_SpreadAny(catalog,model)       = spreadMax;
617  *          Fuel_AzimuthAny(catalog,model)      = azimuthMax;
618  *          Fuel_ByramsIntensity(catalog,model) = 0.;
619  *          Fuel_FlameLength(catalog,model)     = 0.;
620  *          Fuel_ScorchHeight(catalog,model)    = 0.;
621  *
622  *      Previous Fire_SpreadAtAzimiuth() results become obsolete for this model.
623  *
624  *  Function Returns
625  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
626  *      Return status and error text are stored in the Fire Catalog's buffers.
627  *
628  *******************************************************************************
629  */
630 
631 int
Fire_SpreadWindSlopeMax(FuelCatalogPtr catalog,size_t model,double windFpm,double windDeg,double slope,double aspect)632 Fire_SpreadWindSlopeMax ( FuelCatalogPtr catalog, size_t model, double windFpm, double windDeg, double slope, double aspect )
633    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
634    // size_t  model;              /* fuel model number            [0-maxModels] */
635    // double  windFpm;            /* wind speed                        (ft/min) */
636    // double  windDeg;            /* wind bearing vector      (compass degrees) */
637    // double  slope;              /* slope                         (rise/reach) */
638    // double  aspect;             /* aspect (downslope)   azimuth (compass deg) */
639 {
640     double upslope, azimuthMax, phiEw;
641     double splitDeg, splitRad;
642     double slpRate, wndRate, rv, spreadMax;
643     double x, y, al, a;
644     double maxWind, effectiveWind, lwRatio, eccentricity;
645     size_t doEffectiveWind, checkWindLimit, windLimit;
646 
647     /* Validate the catalog and fuel model. */
648     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
649     if ( ! Fire_FuelModelExists(catalog,model) )
650     {
651         sprintf(FuelCat_Error(catalog),
652             "Fire_SpreadMax(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
653             model, FuelCat_Name(catalog));
654         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
655     }
656 
657     /* Recalculate slope factors ONLY if different from previous model input. */
658     if ( ! Equal(Fuel_Slope(catalog,model),slope) )
659     {
660         Fuel_PhiSlope(catalog,model) =
661             Fuel_SlopeK(catalog,model) * slope * slope;
662         Fuel_Slope(catalog,model) = slope;
663     }
664 
665     /* Recalculate wind factors ONLY if different from previous model input. */
666     if ( ! Equal(Fuel_WindSpeed(catalog,model),windFpm) )
667     {
668         Fuel_PhiWind(catalog,model) = (windFpm < Smidgen) ? 0. :
669             Fuel_WindK(catalog,model) * pow(windFpm, Fuel_WindB(catalog,model));
670         Fuel_WindSpeed(catalog,model) = windFpm;
671     }
672 
673     /* Combine wind and slope factors. */
674     phiEw = Fuel_PhiSlope(catalog,model) + Fuel_PhiWind(catalog,model);
675     windLimit = 0;
676     lwRatio = 1.;
677     eccentricity = 0.;
678     upslope = (aspect>=180.) ? aspect-180. : aspect+180.;
679 
680     /* Situation 1: no fire spread or reaction intensity. */
681     if ( Fuel_Spread0(catalog,model) < Smidgen )
682     {
683         spreadMax = 0.;
684         azimuthMax = 0;
685         /* There IS an effective wind even if there is no fire. */
686         doEffectiveWind = 1;
687         /* But since BEHAVE doesn't calculate effective wind when no spread. */
688         /* we wont either. */
689         effectiveWind = 0.;
690         doEffectiveWind = 0;
691         checkWindLimit = 0;
692     }
693 
694     /* Situation 2: no wind and no wind */
695     else if ( phiEw < Smidgen )
696     {
697         phiEw = 0.;
698         effectiveWind = 0.;
699         doEffectiveWind = 0;
700         spreadMax = Fuel_Spread0(catalog,model);
701         azimuthMax = 0;
702         checkWindLimit = 0;
703     }
704 
705     /* Situation 3: wind with no slope. */
706     else if ( slope < Smidgen )
707     {
708         effectiveWind = windFpm;
709         doEffectiveWind = 0;
710         spreadMax = Fuel_Spread0(catalog,model) * (1. + phiEw);
711         azimuthMax = windDeg;
712         checkWindLimit = 1;
713     }
714 
715     /* Situation 4: slope with no wind. */
716     else if ( windFpm < Smidgen )
717     {
718         doEffectiveWind = 1;
719         spreadMax = Fuel_Spread0(catalog,model) * (1. + phiEw);
720         azimuthMax = upslope;
721         checkWindLimit = 1;
722     }
723 
724     /* Situation 5: wind blows upslope. */
725     else if ( Equal(upslope,windDeg) )
726     {
727         doEffectiveWind = 1;
728         spreadMax = Fuel_Spread0(catalog,model) * (1. + phiEw);
729         azimuthMax = upslope;
730         checkWindLimit = 1;
731     }
732 
733     /* Situation 6: wind blows cross slope. */
734     else
735     {
736         /* Recalculate spread rate in the optimal direction. */
737         splitDeg = (upslope<=windDeg) ? windDeg-upslope : 360.-upslope+windDeg;
738         splitRad = DegreesToRadians(splitDeg);
739         slpRate  = Fuel_Spread0(catalog,model) * Fuel_PhiSlope(catalog,model);
740         wndRate  = Fuel_Spread0(catalog,model) * Fuel_PhiWind(catalog,model);
741         x        = slpRate + wndRate * cos(splitRad);
742         y        = wndRate * sin(splitRad);
743         rv       = sqrt(x*x + y*y);
744         spreadMax= Fuel_Spread0(catalog,model) + rv;
745 
746         /* Recalculate phiEw in the optimal direction. */
747         phiEw    = spreadMax / Fuel_Spread0(catalog,model) - 1.0;
748         doEffectiveWind = (phiEw > Smidgen) ? 1 : 0;
749         checkWindLimit = 1;
750 
751         /* Recalculate direction of maximum spread in azimuth degrees. */
752         al = asin(fabs(y) / rv);
753         if ( x >= 0. )
754             a = (y >= 0.) ? al          : M_PI + M_PI - al;
755         else
756             a = (y >= 0.) ? (M_PI - al) : (M_PI + al);
757 
758         splitDeg = RadiansToDegrees(a);
759         if ( (azimuthMax = upslope + splitDeg) > 360. )
760             azimuthMax -= 360.;
761     }
762 
763     /* Recalculate effective wind speed based upon phiEw. */
764     if ( doEffectiveWind )
765         effectiveWind = pow( (phiEw * Fuel_WindE(catalog,model)),
766                              (1. / Fuel_WindB(catalog,model)) );
767 
768     /* If effective wind exceeds maximum wind, scale back spread & phiEw. */
769     if ( checkWindLimit )
770     {
771         maxWind = 0.9 * Fuel_RxIntensity(catalog,model);
772         if ( effectiveWind > maxWind )
773         {
774             phiEw = (maxWind < Smidgen) ? 0. :
775                 Fuel_WindK(catalog,model) * pow(maxWind, Fuel_WindB(catalog,model));
776 
777             spreadMax = Fuel_Spread0(catalog,model) * (1. + phiEw);
778             effectiveWind = maxWind;
779             windLimit = 1;
780         }
781     }
782 
783     /* Determine fire ellipse parameters from the effective wind speed. */
784     /* = 1. + 0.25 * (Fuel_EffectiveWind(catalog,model) / 88.0); */
785     if ( effectiveWind > Smidgen )
786     {
787         lwRatio = 1. + 0.002840909 * effectiveWind;
788         eccentricity = sqrt(lwRatio * lwRatio - 1.0) / lwRatio;
789     }
790 
791     /* Store the results. */
792     Fuel_Aspect(catalog,model)       = aspect;
793     Fuel_WindDir(catalog,model)      = windDeg;
794     Fuel_PhiEffWind(catalog,model)   = phiEw;
795     Fuel_EffectiveWind(catalog,model)= effectiveWind;
796     Fuel_WindLimit(catalog,model)    = windLimit;
797     Fuel_SpreadMax(catalog,model)    = Fuel_SpreadAny(catalog,model)  = spreadMax;
798     Fuel_AzimuthMax(catalog,model)   = Fuel_AzimuthAny(catalog,model) = azimuthMax;
799     Fuel_LwRatio(catalog,model)      = lwRatio;
800     Fuel_Eccentricity(catalog,model) = eccentricity;
801 
802     /* Initialize behavior variables updated by Fire_SpreadAtAzimuth(). */
803     Fuel_ByramsIntensity(catalog,model) = 0.;
804     Fuel_FlameLength(catalog,model)     = 0.;
805     Fuel_ScorchHeight(catalog,model)    = 0.;
806 
807     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
808 }
809 
810 /*
811  *******************************************************************************
812  *
813  *  Fire_SpreadAtAzimuth()
814  *
815  *  Description
816  *      Calculates fire spread rate in a specified direction and optionally
817  *      calculates the fireline intensity, flame length, and scorch height
818  *      along the fire spread vector.
819  *
820  *  Side Effects
821  *      The following variables are updated:
822  *          Fuel_SpreadAny(catalog,model)
823  *          Fuel_AzimuthAny(catalog,model) == azimuth;
824  *          Fuel_ByramsIntensity(catalog,model) is updated if FIRE_BYRAMS.
825  *          Fuel_FlameLength(catalog,model) is updated if FIRE_FLAME.
826  *          Fuel_ScorchHeight(catalog,model) is updated if FIRE_SCORCH.
827  *  Notes
828  *      The calculations depend upon the most recent calls to
829  *      Fire_SpreadNoWindNoSlope() and Fire_SpreadWindSlopeMax() for this model.
830  *
831  *      The input azimuth is the degrees clockwise from north.
832  *
833  *  Function Returns
834  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
835  *      Return status and error text are stored in the Fire Catalog's buffers.
836  *
837  *******************************************************************************
838  */
839 
840 int
Fire_SpreadAtAzimuth(FuelCatalogPtr catalog,size_t model,double azimuth,size_t which)841 Fire_SpreadAtAzimuth ( FuelCatalogPtr catalog, size_t model, double azimuth, size_t which )
842    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
843    // size_t  model;              /* fuel model number            [0-maxModels] */
844    // double  azimuth;            /* fire spread azimuth     (deg from upslope) */
845    // size_t  which;      /* FIRE_NONE | FIRE_BYRAMS | FIRE_FLAME | FIRE_SCORCH */
846 {
847     double dir;
848     double radians;
849     double byrams;
850     double mph;
851     size_t lo, hi, mid, n;
852 
853     /* Validate catalog and the fuel model. */
854     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
855     if ( ! Fire_FuelModelExists(catalog,model) )
856     {
857         sprintf(FuelCat_Error(catalog),
858             "Fire_SpreadAtAzimuth(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
859             model, FuelCat_Name(catalog));
860         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
861     }
862 
863     /* Situation 1: no fire or reaction intensity, so no Byrams or flame. */
864     if ( Fuel_SpreadMax(catalog,model) < Smidgen )
865         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
866 
867     /* Situation 2: phiEw is zero OR azimuth is in the max spread direction */
868     if ( Fuel_PhiEffWind(catalog,model) < Smidgen
869       || Equal(Fuel_AzimuthMax(catalog,model),azimuth) )
870     {
871         Fuel_SpreadAny(catalog,model) = Fuel_SpreadMax(catalog,model);
872     }
873 
874     /* Situation 3: wind and/or slope and azimuth not in max spread direction */
875     else
876     {
877         /* Angle between maximum spread azimuth and requested azimuth. */
878         if ( (dir = fabs(Fuel_AzimuthMax(catalog,model) - azimuth)) > 180. )
879             dir = 360. - dir;
880         radians = DegreesToRadians(dir);
881 
882         /* Calculate the fire spread rate in this azimuth. */
883         Fuel_SpreadAny(catalog,model)
884             = Fuel_SpreadMax(catalog,model)
885             * (1. - Fuel_Eccentricity(catalog,model))
886             / (1. - Fuel_Eccentricity(catalog,model) * cos(radians));
887     }
888     Fuel_AzimuthAny(catalog,model) = azimuth;
889 
890     /* Additional fire behavior outputs. */
891     if ( which )
892     {
893         /* Must compute Byram's if any of the three are requested. */
894         byrams = Fuel_ResidenceTime(catalog,model)
895                * Fuel_SpreadAny(catalog,model)
896                * Fuel_RxIntensity(catalog,model)
897                / 60.;
898 
899         /* Byrams intensity is requested. */
900         if ( which & FIRE_BYRAMS )
901             Fuel_ByramsIntensity(catalog,model) = byrams;
902 
903         /* Flame length is requested. */
904         if ( (which & FIRE_FLAME) )
905         {
906             if ( byrams < Smidgen )
907             {
908                 Fuel_FlameLength(catalog,model) = 0.;
909             }
910             else
911             {
912                 /* Use lookup table if it exists & includes this intensity. */
913                 if ( (n = FuelCat_FlameClasses(catalog)) > 0
914                   && FuelCat_FlameArray(catalog)[n-1] > byrams )
915                 {
916                     hi = n-1;
917                     lo = 0;
918                     do {
919                         mid = lo + (hi-lo)/2;
920                         if ( FuelCat_FlameArray(catalog)[mid] > byrams )
921                             hi = mid;
922                         else
923                             lo = mid + 1;
924                     } while (lo != hi);
925                     Fuel_FlameLength(catalog,model) =
926                         FuelCat_FlameStep(catalog) * (lo+1);
927                 }
928                 /* otherwise compute flame length from scratch. */
929                 else
930                 {
931                     Fuel_FlameLength(catalog,model) = 0.45 * pow(byrams, 0.46);
932                 }
933             }
934         }
935 
936         /* Scorch height is requested. */
937         if ( (which & FIRE_SCORCH) )
938         {
939             if ( byrams < Smidgen )
940             {
941                 Fuel_ScorchHeight(catalog,model) = 0.;
942             }
943             else
944             {
945                 mph = Fuel_WindSpeed(catalog,model) / 88.;
946                 Fuel_ScorchHeight(catalog,model) =
947                     pow(byrams, 1.166667) / sqrt(byrams + (mph * mph * mph));
948             /*  Fuel_ScorchHeight(catalog,model) *= (63. / (140. - temp_f) ); */
949             }
950         }
951     }
952 
953     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
954 }
955 
956 /*
957  *******************************************************************************
958  *
959  *  Fire_FlameScorch()
960  *
961  *  Description
962  *      Calculates the flame length and/or scorch height for the current
963  *      Byram's intensity and azimuth (as determined by the most recent
964  *      call to Fire_SpreadAtAzimuth()).
965  *      Uses the Flame Length Table if it exists.
966  *      Offers a method of getting flame length if Fire_SpreadAtAzimuth()
967  *      is not calculating it.
968  *
969  *  Side Effects
970  *      The following variables are updated:
971  *          Fuel_FlameLength(catalog,model) is updated.
972  *          Fuel_ScorchHeight(catalog,model)
973  *  Notes
974  *      The calculations depend upon the most recent calls to
975  *      Fire_SpreadNoWindNoSlope(), Fire_SpreadWindSlopeMax(), and
976  *      Fire_SpreadAtAzimuth() for this model.
977  *
978  *  Function Returns
979  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
980  *      Return status and error text are stored in the Fire Catalog's buffers.
981  *
982  *******************************************************************************
983  */
984 
985 int
Fire_FlameScorch(FuelCatalogPtr catalog,size_t model,size_t which)986 Fire_FlameScorch ( FuelCatalogPtr catalog, size_t model, size_t which )
987    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
988    // size_t  model;              /* fuel model number            [0-maxModels] */
989    // size_t  which;      /* FIRE_NONE | FIRE_BYRAMS | FIRE_FLAME | FIRE_SCORCH */
990 {
991     double byrams;
992     double mph;
993     size_t lo, hi, mid, n;
994 
995     /* Validate catalog and the fuel model. */
996     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
997     if ( ! Fire_FuelModelExists(catalog,model) )
998     {
999         sprintf(FuelCat_Error(catalog),
1000             "Fire_FlameScorch(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
1001             model, FuelCat_Name(catalog));
1002         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1003     }
1004 
1005     byrams = Fuel_ResidenceTime(catalog,model)
1006            * Fuel_SpreadAny(catalog,model)
1007            * Fuel_RxIntensity(catalog,model)
1008            / 60.;
1009 
1010     /* Flame length is requested. */
1011     if ( (which & FIRE_FLAME) )
1012     {
1013         if ( byrams < Smidgen )
1014         {
1015             Fuel_FlameLength(catalog,model) = 0.;
1016         }
1017         else
1018         {
1019             /* Use lookup table if it exists & includes this intensity. */
1020             if ( (n = FuelCat_FlameClasses(catalog)) > 0
1021               && FuelCat_FlameArray(catalog)[n-1] > byrams )
1022             {
1023                 hi = n-1;
1024                 lo = 0;
1025                 do {
1026                     mid = lo + (hi-lo)/2;
1027                     if ( FuelCat_FlameArray(catalog)[mid] > byrams )
1028                         hi = mid;
1029                     else
1030                         lo = mid + 1;
1031                 } while (lo != hi);
1032                 Fuel_FlameLength(catalog,model) =
1033                     FuelCat_FlameStep(catalog) * (lo+1);
1034             }
1035             /* otherwise compute flame length from scratch. */
1036             else
1037             {
1038                 Fuel_FlameLength(catalog,model) = 0.45 * pow(byrams, 0.46);
1039             }
1040         }
1041     }
1042 
1043     /* Scorch height is requested. */
1044     if ( (which & FIRE_SCORCH) )
1045     {
1046         if ( byrams < Smidgen )
1047         {
1048             Fuel_ScorchHeight(catalog,model) = 0.;
1049         }
1050         else
1051         {
1052             mph = Fuel_WindSpeed(catalog,model) / 88.;
1053             Fuel_ScorchHeight(catalog,model) =
1054                 pow(byrams, 1.166667) / sqrt(byrams + (mph * mph * mph));
1055         /*  Fuel_ScorchHeight(catalog,model) *= (63. / (140. - temp_f) ); */
1056         }
1057     }
1058 
1059     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1060 }
1061 
1062 /*
1063  *******************************************************************************
1064  *
1065  *  Fire_FlameLengthTable()
1066  *
1067  *  Description
1068  *      Creates a flame length lookup table containing "flameClasses" classes
1069  *      with each class spanning "flameStep" feet.
1070  *
1071  *  Discussion
1072  *      Since flame length is strictly an output variable (e.g., it is never
1073  *      used as the basis for subsequent computations), we can usually afford
1074  *      to round it to some precision that makes sense to fire managers.
1075  *      Usually this will be in 1 foot or perhaps 6 inch increments.  The call
1076  *
1077  *
1078  *      creates a flame length table for flame lengths of 1 through 500 feet.
1079  *
1080  *      Fire_SpreadAtAzimuth() uses the flame table (if one is defined for the
1081  *      catalog) to avoid using the costly pow() function for highly iterative
1082  *      flame length calculations, saving a considerable amount of processing
1083  *      time.  Fire_SpreadAtAzimuth() will still use the pow() function to
1084  *      compute flame length if (1) a flame length table is not defined,
1085  *      (2) the fireline intensity exceeds the upper limit of the currently
1086  *      defined flame length table, or (3) the flame length table becomes
1087  *      undefined by a Fire_FlameLengthTable(catalog, 0, 0.) call.
1088  *
1089  *
1090  *  Examples
1091  *      Fire_FlameLengthTable(catalog, 200, 1.0);
1092  *          Creates a table for flame lengths of 1 through 200 feet in 1-foot
1093  *          intervals.  Any previously defined flame length table for this
1094  *          fuel catalog is destroyed.
1095  *
1096  *      Fire_FlameLengthTable(catalog, 500, 0.5);
1097  *          Creates a table for flame lengths of 0.5 through 250 feet in 6-inch
1098  *          intervals.  ANy previously defined flame length table for this
1099  *          fuel catalog is destroyed.
1100  *
1101  *      Fire_FlameLengthTable(catalog, 0, 0.);
1102  *          Destroys any existing flame length table for this catalog, and
1103  *          forces actual flame length computation using pow() function.
1104  *
1105  *  Side Effects
1106  *      If a flame length table currently exists, it is destroyed, and the
1107  *      FuelCat_FlameArray(), FuelCat_FlameClasses(), and
1108  *      FuelCat_FlameStep() are set to NULL, 0, and 0.0, respectively.
1109  *
1110  *      If fireClasses > 0, allocates a flame length table and fills it with
1111  *      the fireline intensity associated with the upper limit of each flame
1112  *      length class.  The FuelCat_FlameArray(), FuelCat_FlameClasses(), and
1113  *      FuelCat_FlameStep() are then updated.
1114  *
1115  *  Function Returns
1116  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
1117  *      Return status and error text are stored in the Fire Catalog's buffers.
1118  *
1119  *******************************************************************************
1120  */
1121 
1122 int
Fire_FlameLengthTable(FuelCatalogPtr catalog,size_t flameClasses,double flameStep)1123 Fire_FlameLengthTable ( FuelCatalogPtr catalog, size_t flameClasses, double flameStep )
1124    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
1125    // size_t  flameClasses;       /* number of flame length classes             */
1126    // double  flameStep;          /* flame length step value per class          */
1127 {
1128     double power, flame;
1129     size_t i;
1130 
1131     /* Validate the catalog. */
1132     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
1133 
1134     /* If a flame table already exists, destroy it. */
1135     if ( FuelCat_FlameArray(catalog) )
1136     {
1137         free(FuelCat_FlameArray(catalog));
1138         FuelCat_FlameArray(catalog)   = NULL;
1139         FuelCat_FlameClasses(catalog) = 0;
1140         FuelCat_FlameStep(catalog)    = 0.0;
1141     }
1142 
1143     /* If flameClasses is zero, simply return. */
1144     if ( flameClasses == 0 )
1145         return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1146 
1147     /* Otherwise create a new flame table. */
1148     if ( (FuelCat_FlameArray(catalog) = (double *)
1149         calloc(flameClasses, sizeof(double))) == NULL )
1150     {
1151         sprintf(FuelCat_Error(catalog),
1152             "Fire_FlameLengthTable(): imposible asignar tabla de longitud de llama con %d clases de %f pies.",
1153             flameClasses, flameStep);
1154         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1155     }
1156 
1157     /* Fill the array. */
1158     power = 1. / .46;
1159     for ( i=0; i<flameClasses; i++ )
1160     {
1161         flame = flameStep * (i+1);
1162         FuelCat_FlameArray(catalog)[i] = pow((flame / .45), power);
1163     }
1164     FuelCat_FlameClasses(catalog) = flameClasses;
1165     FuelCat_FlameStep(catalog)    = flameStep;
1166 
1167     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1168 }
1169 
1170 /*
1171  *******************************************************************************
1172  *
1173  *  Fire_FuelCatalogCreate()
1174  *
1175  *  Description
1176  *      Creates a new fuel model catalog capable of holding fuel models with
1177  *      id's in the range [0..maxModel].
1178  *      The catalog is filled by subsequent calls to Fire_FuelModelCreate().
1179  *
1180  *  Side Effects
1181  *      Allocates a new FuelCatalogData structure.
1182  *      Allocates an error text buffer for the catalog.
1183  *      Allocates a name for the catalog.
1184  *      Allocates an array of pointers to FuelData structures (the FuelData
1185  *      structures themselves are allocated by Fire_FuelModelCreate() and
1186  *      their pointers are stored here).
1187  *
1188  *  Notes
1189  *      The FuelCatalog contains a dynamically-allocated array of pointers
1190  *      to FuelData blocks.  These pointers are initially NULL and are
1191  *      subsequently assigned by Fire_FuelModelCreate().  The array provides
1192  *      the programmer with a means of directly accessing fuel models via
1193  *      their model number, which is handy when simulating fire growth.
1194  *
1195  *  Function Returns
1196  *      While most FireLib functions return a status code, this one returns
1197  *      a pointer to the new FuelCatalogData on success or NULL if unable
1198  *      to allocate any of the dynamic structures.
1199  *
1200  *******************************************************************************
1201  */
1202 
1203 FuelCatalogPtr
Fire_FuelCatalogCreate(char * name,size_t maxModels)1204 Fire_FuelCatalogCreate ( char *name, size_t maxModels )
1205    // char  *name;                /* FuelCatalogData instance name */
1206    // size_t maxModels;           /* maximum modelId allowed in this catalog */
1207 {
1208     FuelCatalogPtr catalog;
1209     static char *blank = {""};
1210 
1211     /* Catch a NULL name. */
1212     if ( name == NULL )
1213         name = blank;
1214 
1215     /* Allocate the FireCatalogData structure. */
1216     if ( (catalog = (FuelCatalogPtr) malloc(sizeof(FuelCatalogData))) == NULL )
1217     {
1218         fprintf(stderr,
1219             "Fire_FuelCatalogCreate(): imposible asignar el objeto \"%s\" del cat�logo de combustibles.\n",
1220             name);
1221         return (NULL);
1222     }
1223 
1224     /* Assign the magic cookie right away. */
1225     FuelCat_MagicCookie(catalog) = FIRE_CATALOG_MAGIC;
1226 
1227     /* Allocate and store the catalog instance name. */
1228     if ( (FuelCat_Name(catalog) = strdup(name)) == NULL )
1229     {
1230         fprintf(stderr,
1231             "Fire_FuelCatalogCreate(): imposible duplicar el nombre \"%s\" del cat�logo de combustibles.\n",
1232             name);
1233         free(catalog);
1234         return (NULL);
1235     }
1236 
1237     /* Allocate the FireCatalogData error message buffer. */
1238     if ( (FuelCat_Error(catalog) =
1239         (char *) calloc(FIRE_ERROR_BUFFER_SIZE, sizeof(char))) == NULL )
1240     {
1241         fprintf(stderr,
1242             "Fire_FuelCatalogCreate(): imposible asignar el bufer de error  \"%s\" del cat�logo de combustibles.\n",
1243             name);
1244         free(FuelCat_Name(catalog));
1245         free(catalog);
1246         return (NULL);
1247     }
1248     FuelCat_Status(catalog) = FIRE_STATUS_ERROR;
1249 
1250     /* Allocate a FuelModelPtr array to handle models [0..maxModels]. */
1251     maxModels++;
1252     FuelCat_MaxModels(catalog) = maxModels;
1253     if ( (FuelCat_ModelArray(catalog) = (FuelModelPtr *)
1254         calloc(FuelCat_MaxModels(catalog), sizeof(FuelModelPtr))) == NULL )
1255     {
1256         fprintf(stderr,
1257             "Fire_FuelCatalogCreate(): imposible asignar \"%s\" con %d modelos de combustible del cat�logo de combustibles.\n",
1258             name, maxModels);
1259         free(FuelCat_Error(catalog));
1260         free(FuelCat_Name(catalog));
1261         free(catalog);
1262         return (NULL);
1263     }
1264 
1265     /* Initialize variables and return ptr to this instance. */
1266     FuelCat_FlameArray(catalog)   = NULL;
1267     FuelCat_FlameClasses(catalog) = 0;
1268     FuelCat_FlameStep(catalog)    = 0.0;
1269     FuelCat_Status(catalog)       = FIRE_STATUS_OK;
1270     return (catalog);
1271 }
1272 
1273 /*
1274  *******************************************************************************
1275  *
1276  *  Fire_FuelCatalogCreateStandard()
1277  *
1278  *  Description
1279  *      Creates a new fuel model catalog capable of holding fuel models with
1280  *      id's in the range [0..maxModel].
1281  *      The catalog is then filled with the 13 standard fire behavior fuel
1282  *      models.  Other models may be added by subsequent calls to
1283  *      Fire_FuelModelCreate().
1284  *
1285  *  Side Effects
1286  *      Allocates a new FuelCatalogData structure.
1287  *      Fills the catalog with standard fuels models 0-13.
1288  *
1289  *  Function Returns
1290  *      While most FireLib functions return a status code, this one returns
1291  *      a pointer to the new FuelCatalogData on success, or NULL if unable
1292  *      to allocate any of the dynamic structures.
1293  *
1294  *******************************************************************************
1295  */
1296 
1297 FuelCatalogPtr
Fire_FuelCatalogCreateStandard(char * name,size_t maxModels)1298 Fire_FuelCatalogCreateStandard (char *name, size_t maxModels )
1299    // char  *name;                /* FuelCatalogData instance name */
1300    // size_t maxModels;           /* maximum modelId allowed in this catalog */
1301 {
1302     FuelCatalogPtr catalog;
1303     double stot, seff, heat, dens, adjust;
1304     size_t m, p;
1305 
1306     /* Fuel model definitions. */
1307     typedef struct {
1308         char *name; double depth; double mext; size_t maxParticles; char *desc;
1309     } StandardModels;
1310 
1311     StandardModels M[14] = {
1312         {"NoFuel", 0.1, 0.01, 0, "No Combustible Fuel" },
1313         {"NFFL01", 1.0, 0.12, 1, "Short Grass (1 ft)" },
1314         {"NFFL02", 1.0, 0.15, 4, "Timber (grass & understory)" },
1315         {"NFFL03", 2.5, 0.25, 1, "Tall Grass (2.5 ft)" },
1316         {"NFFL04", 6.0, 0.20, 4, "Chaparral (6 ft)" },
1317         {"NFFL05", 2.0, 0.20, 3, "Brush (2 ft)" },
1318         {"NFFL06", 2.5, 0.25, 3, "Dormant Brush & Hardwood Slash" },
1319         {"NFFL07", 2.5, 0.40, 4, "Southern Rough" },
1320         {"NFFL08", 0.2, 0.30, 3, "Closed Timber Litter" },
1321         {"NFFL09", 0.2, 0.25, 3, "Hardwood Litter" },
1322         {"NFFL10", 1.0, 0.25, 4, "Timber (litter & understory)" },
1323         {"NFFL11", 1.0, 0.15, 3, "Light Logging Slash" },
1324         {"NFFL12", 2.3, 0.20, 3, "Medium Logging Slash" },
1325         {"NFFL13", 3.0, 0.25, 3, "Heavy Logging Slash" }
1326     };
1327 
1328     /* Fuel particle definitions. */
1329     typedef struct {
1330         size_t model; size_t type; double load; double savr;
1331     } StandardParticle;
1332 
1333     static StandardParticle P[39] = {
1334         { 1, FIRE_TYPE_DEAD, 0.0340, 3500.},
1335         { 2, FIRE_TYPE_DEAD, 0.0920, 3000.},
1336         { 2, FIRE_TYPE_DEAD, 0.0460, 109.},
1337         { 2, FIRE_TYPE_DEAD, 0.0230, 30.},
1338         { 2, FIRE_TYPE_HERB, 0.0230, 1500.},
1339         { 3, FIRE_TYPE_DEAD, 0.1380, 1500.},
1340         { 4, FIRE_TYPE_DEAD, 0.2300, 2000.},
1341         { 4, FIRE_TYPE_DEAD, 0.1840, 109.},
1342         { 4, FIRE_TYPE_DEAD, 0.0920, 30.},
1343         { 4, FIRE_TYPE_WOOD, 0.2300, 1500.},
1344         { 5, FIRE_TYPE_DEAD, 0.0460, 2000.},
1345         { 5, FIRE_TYPE_DEAD, 0.0230, 109.},
1346         { 5, FIRE_TYPE_WOOD, 0.0920, 1500.},
1347         { 6, FIRE_TYPE_DEAD, 0.0690, 1750.},
1348         { 6, FIRE_TYPE_DEAD, 0.1150, 109.},
1349         { 6, FIRE_TYPE_DEAD, 0.0920, 30.},
1350         { 7, FIRE_TYPE_DEAD, 0.0520, 1750.},
1351         { 7, FIRE_TYPE_DEAD, 0.0860, 109.},
1352         { 7, FIRE_TYPE_DEAD, 0.0690, 30.},
1353         { 7, FIRE_TYPE_WOOD, 0.0170, 1550.},
1354         { 8, FIRE_TYPE_DEAD, 0.0690, 2000.},
1355         { 8, FIRE_TYPE_DEAD, 0.0460, 109.},
1356         { 8, FIRE_TYPE_DEAD, 0.1150, 30.},
1357         { 9, FIRE_TYPE_DEAD, 0.1340, 2500.},
1358         { 9, FIRE_TYPE_DEAD, 0.0190, 109.},
1359         { 9, FIRE_TYPE_DEAD, 0.0070, 30.},
1360         {10, FIRE_TYPE_DEAD, 0.1380, 2000.},
1361         {10, FIRE_TYPE_DEAD, 0.0920, 109.},
1362         {10, FIRE_TYPE_DEAD, 0.2300, 30.},
1363         {10, FIRE_TYPE_WOOD, 0.0920, 1500.},
1364         {11, FIRE_TYPE_DEAD, 0.0690, 1500.},
1365         {11, FIRE_TYPE_DEAD, 0.2070, 109.},
1366         {11, FIRE_TYPE_DEAD, 0.2530, 30.},
1367         {12, FIRE_TYPE_DEAD, 0.1840, 1500.},
1368         {12, FIRE_TYPE_DEAD, 0.6440, 109.},
1369         {12, FIRE_TYPE_DEAD, 0.7590, 30.},
1370         {13, FIRE_TYPE_DEAD, 0.3220, 1500.},
1371         {13, FIRE_TYPE_DEAD, 1.0580, 109.},
1372         {13, FIRE_TYPE_DEAD, 1.2880, 30.},
1373     };
1374 
1375     /* First, create the catalog. */
1376     if ( maxModels < 13 )
1377         maxModels = 13;
1378     if ( (catalog = Fire_FuelCatalogCreate(name, maxModels)) == NULL )
1379         return (NULL);
1380 
1381     /* Second, create all 14 models. */
1382     adjust = 1.0;
1383     for ( m=0; m<14; m++ )
1384     {
1385         if ( Fire_FuelModelCreate(catalog, m, M[m].name, M[m].desc, M[m].depth,
1386             M[m].mext, adjust, M[m].maxParticles) != FIRE_STATUS_OK )
1387         {
1388             fprintf(stderr, "%s\n", FuelCat_Error(catalog));
1389             Fire_FuelCatalogDestroy(catalog);
1390             return (NULL);
1391         }
1392     }
1393 
1394     /* Finally, add all the fuel particles. */
1395     stot   = 0.0555;
1396     seff   = 0.0100;
1397     heat   = 8000.0;
1398     dens   = 32.0;
1399     for ( p=0; p<39; p++ )
1400     {
1401         if ( Fire_FuelParticleAdd(catalog, P[p].model, P[p].type, P[p].load,
1402             P[p].savr, dens, heat, stot, seff) != FIRE_STATUS_OK )
1403         {
1404             fprintf(stderr, "%s\n", FuelCat_Error(catalog));
1405             Fire_FuelCatalogDestroy(catalog);
1406             return (NULL);
1407         }
1408     }
1409 
1410     return (catalog);
1411 }
1412 
1413 /*
1414  *******************************************************************************
1415  *
1416  *  Fire_FuelCatalogDestroy()
1417  *
1418  *  Description
1419  *      Destroys the fuel catalog and all its associated models and particles.
1420  *
1421  *  Side Effects
1422  *      Destroys all FuelData instances belonging to the catalog.
1423  *      Frees the array of pointers to FuelData structures.
1424  *      Frees the catalog name.
1425  *      Frees the catalog error text buffer.
1426  *      Frees the FuelCatalog instance.
1427  *
1428  *  Function Returns
1429  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
1430  *      Return status and error text are stored in the Fire Catalog's buffers.
1431  *
1432  *******************************************************************************
1433  */
1434 
1435 int
Fire_FuelCatalogDestroy(FuelCatalogPtr catalog)1436 Fire_FuelCatalogDestroy ( FuelCatalogPtr catalog )
1437    // FuelCatalogPtr catalog;     /* FuelCatalogData instance to destroy. */
1438 {
1439     size_t model;
1440 
1441     /* Validate the catalog. */
1442     assert(catalog!=NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
1443 
1444     /* First destroy all the fuel models in this catalog. */
1445     /* The free the catalog's array of FuelData pointers. */
1446     if ( FuelCat_ModelArray(catalog) )
1447     {
1448         for ( model=0; model <= FuelCat_MaxModels(catalog); model++ )
1449         {
1450             if ( FuelCat_ModelPtr(catalog,model) )
1451                 Fire_FuelModelDestroy(catalog, model);
1452         }
1453         free(FuelCat_ModelArray(catalog));
1454         FuelCat_ModelArray(catalog) = NULL;
1455     }
1456 
1457     /* Next destroy the flame length table. */
1458     if ( FuelCat_FlameArray(catalog) )
1459     {
1460         free(FuelCat_FlameArray(catalog));
1461         FuelCat_FlameArray(catalog)   = NULL;
1462         FuelCat_FlameClasses(catalog) = 0;
1463         FuelCat_FlameStep(catalog)    = 0.0;
1464     }
1465 
1466     /* Then free the name and error buffer for this FuelCatalogData instance. */
1467     if ( FuelCat_Error(catalog) )
1468     {
1469         free(FuelCat_Error(catalog));
1470         FuelCat_Error(catalog) = NULL;
1471     }
1472 
1473     if ( FuelCat_Name(catalog) )
1474     {
1475         free(FuelCat_Name(catalog));
1476         FuelCat_Name(catalog) = NULL;
1477     }
1478 
1479     /* Finally,free the FuelCatalogData instance and return. */
1480     free(catalog);
1481 
1482     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1483 }
1484 
1485 /*
1486  *******************************************************************************
1487  *
1488  *  Fire_FuelModelCreate()
1489  *
1490  *  Description
1491  *      Creates a new fuel model able to hold maxParticles fuel particles.
1492  *      Fuel particles are subsequently added by Fire_FuelParticleAdd().
1493  *
1494  *  Side Effects
1495  *      Any existing fuel model with modelId in the Fuel Catalog is destroyed.
1496  *      Allocates the fuel model's FuelData block.
1497  *      Allocates the fuel model's name string.
1498  *      Allocates the fuel model's description string.
1499  *      Allocates the fuel model's fuel particle pointer array of maxParticles
1500  *      (the FuelParticleData blocks are actually allocated within
1501  *      Fire_FuelparticleAdd() and thier pointers stored in this array).
1502  *      The fuel model's address is stored in the fuel catalog's pointer array.
1503  *
1504  *  Function Returns
1505  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
1506  *      Return status and error text are stored in the Fire Catalog's buffers.
1507  *
1508  *******************************************************************************
1509  */
1510 
1511 int
Fire_FuelModelCreate(FuelCatalogPtr catalog,size_t model,char * name,char * desc,double depth,double mext,double adjust,size_t maxParticles)1512 Fire_FuelModelCreate (FuelCatalogPtr catalog, size_t model, char *name, char *desc, double depth, double mext, double adjust, size_t maxParticles)
1513    // FuelCatalogPtr catalog;     /* FuelCatalogData instance */
1514    // size_t  model;              /* fuel model number            [0-maxModels] */
1515    // char   *name;               /* short name */
1516    // char   *desc;               /* longer description */
1517    // double  depth;              /* bed depth                             (ft) */
1518    // double  mext;               /* moisture of extinction                (dl) */
1519    // double  adjust;             /* spread adjustment factor              (dl) */
1520    // size_t  maxParticles;       /* maximum number of fuel model particles     */
1521 {
1522     static char *blank = {""};
1523     size_t particle;
1524 
1525     /* Validate the catalog. */
1526     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
1527 
1528     /* Make sure model id is within range. */
1529     if ( model > FuelCat_MaxModels(catalog) )
1530     {
1531         sprintf(FuelCat_Error(catalog),
1532             "Fire_FuelModelCreate(): fuel model \"%s\" number %d exceeds fuel catalog \"%s\" range [0..%d].",
1533             name, model, FuelCat_Name(catalog), FuelCat_MaxModels(catalog));
1534         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1535     }
1536 
1537     /* Validate depth and mext. */
1538     if ( depth < Smidgen )
1539     {
1540         sprintf(FuelCat_Error(catalog),
1541             "Fire_FuelModelCreate(): el modelo de combustible \"%s\" n�mero %d de ancho %5.4f es demasiado peque�o.",
1542             name, model, depth);
1543         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1544     }
1545 
1546     if ( mext < Smidgen )
1547     {
1548         sprintf(FuelCat_Error(catalog),
1549             "Fire_FuelModelCreate(): el modelo de combustible \"%s\" n�mero %d de humedad de extinci�n %5.4f es demasiado peque�o.",
1550             name, model, mext);
1551         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1552     }
1553 
1554     /* If this model already exists, delete it. */
1555     if ( FuelCat_ModelPtr(catalog,model) )
1556         Fire_FuelModelDestroy(catalog, model);
1557 
1558     /* Allocate the model's FuelData structure. */
1559     if ( maxParticles < 1 )
1560         maxParticles = 1;
1561     if ( (FuelCat_ModelPtr(catalog,model) =
1562                 (FuelModelPtr) calloc(1, sizeof(FuelModelData))) == NULL
1563       || (Fuel_ParticleArray(catalog,model) =
1564                 (PartPtr *) calloc(maxParticles, sizeof(PartPtr))) == NULL )
1565     {
1566         Fire_FuelModelDestroy(catalog, model);
1567         sprintf(FuelCat_Error(catalog),
1568             "Fire_FuelModelCreate(): imposible asignar el modelos de combustible \"%s\" n�mero %d para el cat�logo de combustibles \"%s\".",
1569             name, model, FuelCat_Name(catalog));
1570         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1571     }
1572 
1573     /* Catch NULL names and descriptions. */
1574     if ( name == NULL )
1575         name = blank;
1576     if ( desc == NULL )
1577         desc = blank;
1578 
1579     /* Store remaining attributes. */
1580     Fuel_Model(catalog,model)            = model;
1581     Fuel_Depth(catalog,model)            = depth;
1582     Fuel_Mext(catalog,model)             = mext;
1583     Fuel_SpreadAdjustment(catalog,model) = adjust;
1584     Fuel_Name(catalog,model)             = strdup(name);
1585     Fuel_Desc(catalog,model)             = strdup(desc);
1586     Fuel_CombustionFlag(catalog,model)   = 0;
1587     Fuel_MaxParticles(catalog,model)     = maxParticles;
1588     Fuel_Particles(catalog,model)        = 0;
1589     for ( particle=0; particle<Fuel_MaxParticles(catalog,model); particle++ )
1590         Fuel_ParticlePtr(catalog,model,particle) = NULL;
1591 
1592     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1593 }
1594 
1595 /*
1596  *******************************************************************************
1597  *
1598  *  Fire_FuelModelDestroy()
1599  *
1600  *  Description
1601  *      Deletes the specified fuel model.
1602  *      Note: this is one of only 3 functions that use the modelId instead
1603  *      of a FuelData pointer to identify the model.
1604  *
1605  *  Side Effects
1606  *      Free's all fuel particles added to the fuel model.
1607  *      Free's the fuel particle pointer array.
1608  *      Free's the fuel model's name.
1609  *      Free's the fuel model's description.
1610  *      Free's the fuel model's FuelData block.
1611  *      Sets the Fuel Catalog's pointer for this fuel model to NULL.
1612  *
1613  *  Function Returns
1614  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
1615  *      Return status and error text are stored in the Fire Catalog's buffers.
1616  *
1617  *******************************************************************************
1618  */
1619 
1620 int
Fire_FuelModelDestroy(FuelCatalogPtr catalog,size_t model)1621 Fire_FuelModelDestroy ( FuelCatalogPtr catalog, size_t model )
1622    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
1623    // size_t         model;       /* fuel model id number         [0-maxModels] */
1624 {
1625     size_t particle;
1626 
1627     /* Validate the catalog. */
1628     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
1629 
1630     /* Make sure model id is within range and exists. */
1631     if ( ! Fire_FuelModelExists(catalog,model) )
1632     {
1633         sprintf(FuelCat_Error(catalog),
1634             "Fire_FuelModelDestroy(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
1635             model, FuelCat_Name(catalog));
1636         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1637     }
1638 
1639     /* Free all the fuel model particles and their pointer array. */
1640     if ( Fuel_ParticleArray(catalog,model) )
1641     {
1642         for (particle=0; particle<Fuel_MaxParticles(catalog,model); particle++)
1643         {
1644             if ( Fuel_ParticlePtr(catalog,model,particle) )
1645             {
1646                 free(Fuel_ParticlePtr(catalog,model,particle));
1647                 Fuel_ParticlePtr(catalog,model,particle) = NULL;
1648             }
1649         }
1650         free(Fuel_ParticleArray(catalog,model));
1651         Fuel_ParticleArray(catalog,model) = NULL;
1652     }
1653 
1654     /* Free the fuel model name and description. */
1655     if ( Fuel_Name(catalog,model) )
1656     {
1657         free(Fuel_Name(catalog,model));
1658         Fuel_Name(catalog,model) = NULL;
1659     }
1660 
1661     if ( Fuel_Desc(catalog,model) )
1662     {
1663         free(Fuel_Desc(catalog,model));
1664         Fuel_Desc(catalog,model) = NULL;
1665     }
1666 
1667     /* Now free the FuelData instance and reset its catalog entry. */
1668     free(FuelCat_ModelPtr(catalog,model));
1669     FuelCat_ModelPtr(catalog,model) = NULL;
1670 
1671     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1672 }
1673 
1674 /*
1675  *******************************************************************************
1676  *
1677  *  Fire_FuelModelExists()
1678  *
1679  *  Description
1680  *      Performs a sanity check to make sure the catalog pointer is valid
1681  *      and the fuel model number is within range and exists.
1682  *
1683  *  Side Effects
1684  *      None.
1685  *
1686  *  Function Returns
1687  *      1 if "model" exists, 0 if it is undefined.
1688  *
1689  *******************************************************************************
1690  */
1691 
1692 int
Fire_FuelModelExists(FuelCatalogPtr catalog,size_t model)1693 Fire_FuelModelExists ( FuelCatalogPtr catalog, size_t model )
1694    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
1695    // size_t         model;       /* fuel model id number         [0-maxModels] */
1696 {
1697     /* Validate the model number. */
1698     if ( model > FuelCat_MaxModels(catalog)
1699       || ! FuelCat_ModelPtr(catalog,model) )
1700         return (int) 0;
1701 
1702     return (int) 1;
1703 }
1704 
1705 /*
1706  *******************************************************************************
1707  *
1708  *  Fire_FuelParticleAdd()
1709  *
1710  *  Description
1711  *      Adds a fuel particle to the specified fuel model.
1712  *
1713  *  Side Effects
1714  *      A FuelParticleData is allocated and appended to the model's array.
1715  *      The fuel model's particle counter is incremented.
1716  *      The fuel model's combustion flag set to 0.
1717  *
1718  *  Function Returns
1719  *      FIRE_STATUS_OK or FIRE_STATUS_ERROR.
1720  *      Return status and error text are stored in the Fire Catalog's buffers.
1721  *
1722  *******************************************************************************
1723  */
1724 
1725 int
Fire_FuelParticleAdd(FuelCatalogPtr catalog,size_t model,size_t type,double load,double savr,double dens,double heat,double stot,double seff)1726 Fire_FuelParticleAdd ( FuelCatalogPtr catalog, size_t model, size_t type, double load, double savr, double dens, double heat, double stot, double seff )
1727    // FuelCatalogPtr catalog;     /* FuelCatalogData instance pointer           */
1728    // size_t  model;              /* fuel model id number         [0-maxModels] */
1729    // size_t  type;               /* FIRE_TYPE_DEAD, _TYPE_HERB, or _TYPE_WOOD  */
1730    // double  load;               /* fuel load                        (lbs/ft2) */
1731    // double  savr;               /* surface-area-to-volume ratio     (ft2/ft3) */
1732    // double  dens;               /* density                          (lbs/ft3) */
1733    // double  heat;               /* heat of combustion               (btus/lb) */
1734    // double  stot;               /* total silica content               (lb/lb) */
1735    // double  seff;               /* effective silica content           (lb/lb) */
1736 {
1737     static double Size_boundary[FIRE_SIZE_CLASSES] =
1738         {1200., 192., 96., 48., 16., 0.};
1739     size_t particle, size;
1740 
1741     /* Validate the catalog. */
1742     assert(catalog!= NULL && FuelCat_MagicCookie(catalog)==FIRE_CATALOG_MAGIC);
1743 
1744     /* Validate the fuel model. */
1745     if ( ! Fire_FuelModelExists(catalog,model) )
1746     {
1747         sprintf(FuelCat_Error(catalog),
1748             "Fire_FuelParticleAdd(): el modelo de combustible %d no existe en el cat�logo de combustibles \"%s\".",
1749             model, FuelCat_Name(catalog));
1750         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1751     }
1752 
1753     /* Validate the "type" parameter. */
1754     if ( type != FIRE_TYPE_DEAD
1755       && type != FIRE_TYPE_HERB
1756       && type != FIRE_TYPE_WOOD )
1757     {
1758         sprintf(FuelCat_Error(catalog),
1759             "Fire_FuelParticleAdd(): el modelo de combustible %d de tipo de valor (arg #3) no es FIRE_TYPE_DEAD, FIRE_TYPE_HERB, o FIRE_TYPE_WOOD.",
1760             model);
1761         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1762     }
1763 
1764     /* Allocate a new FuelParticle */
1765     particle = Fuel_Particles(catalog,model);
1766     if ( (Fuel_ParticlePtr(catalog,model,particle) =
1767         (PartPtr) calloc(1, sizeof(FuelParticleData))) == NULL )
1768     {
1769         sprintf(FuelCat_Error(catalog),
1770             "Fire_FuelParticleAdd(): imposible asignar la part�cula de combustible al modelo de combustible \"%s\" n�mero %d en el cat�logo de combustibles \"%s\".",
1771             Fuel_Name(catalog,model), model, FuelCat_Name(catalog));
1772         return (FuelCat_Status(catalog) = FIRE_STATUS_ERROR);
1773     }
1774 
1775     /* Store the input particle attributes. */
1776     Fuel_Type(catalog,model,particle)       = type;
1777     Fuel_Load(catalog,model,particle)       = load;
1778     Fuel_Savr(catalog,model,particle)       = savr;
1779     Fuel_Density(catalog,model,particle)    = dens;
1780     Fuel_Heat(catalog,model,particle)       = heat;
1781     Fuel_SiTotal(catalog,model,particle)    = stot;
1782     Fuel_SiEffective(catalog,model,particle)= seff;
1783 
1784     /* Fuel life category. */
1785     Fuel_Live(catalog,model,particle) =
1786         (type==FIRE_TYPE_DEAD) ? FIRE_LIFE_DEAD : FIRE_LIFE_LIVE;
1787 
1788     /* Fuel particle surface area. */
1789     Fuel_SurfaceArea(catalog,model,particle) =
1790         (dens > Smidgen) ? load * savr / dens : 0.;
1791 
1792     /* Particle SAVR exponent factor. */
1793     Fuel_SigmaFactor(catalog,model,particle) =
1794         (savr > Smidgen) ? exp(-138. / savr) : 0.;
1795 
1796     /* Particle size class. */
1797     for ( size=0; savr < Size_boundary[size]; size++ )
1798         /* NOTHING */ ;
1799     Fuel_SizeClass(catalog,model,particle) = size;
1800 
1801     /* Initialize particle attributes that are bed & environ dependent. */
1802     Fuel_AreaWtg(catalog,model,particle)     = 0.;
1803     Fuel_SizeAreaWtg(catalog,model,particle) = 0.;
1804     Fuel_Moisture(catalog,model,particle)    = 0.;
1805 
1806     /* Increment the fuel model's particle counter and reset it flag. */
1807     Fuel_Particles(catalog,model)++;
1808     Fuel_CombustionFlag(catalog,model) = 0;
1809 
1810     return (FuelCat_Status(catalog) = FIRE_STATUS_OK);
1811 }
1812 
1813 /*
1814  *******************************************************************************
1815  * End of fireLib.c
1816  *******************************************************************************
1817  */
1818