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