int CSimulateVariableWind::CalculateFireSpreading(double fTimeLimit){ int x,y; int x2,y2; int i,j; bool bReturn = false; /* neighbor's address*/ /* N NE E SE S SW W NW */ static int nX[8] = { 0, 1, 1, 1, 0, -1, -1, -1}; static int nY[8] = { 1, 1, 0, -1, -1, -1, 0, 1}; double fDist; /* distance to neighbor */ double fAz; /* compass azimuth to neighbor (0=N) */ size_t modelNumber; /* fuel model number at current cell */ double moisture[6]; /* fuel moisture content at current cell */ double dSpreadRate; /* spread rate in direction of neighbor */ double dSpreadTime; /* time to spread from cell to neighbor */ double dIgnTime; /* time neighbor is ignited by current cell */ double dWindSpd; double dWindDir; int iBurntCells = 0; while (m_CentralPoints.Get_Count()!=0){ for (int iPt=0; iPt<m_CentralPoints.Get_Count();iPt++){ x = m_CentralPoints.Get_X(iPt); y = m_CentralPoints.Get_Y(iPt); if (!m_pDEM->is_NoData(x,y) && !m_pFuelGrid->is_NoData(x,y)){ modelNumber = (size_t) m_pFuelGrid->asInt(x, y); moisture[0] = m_pM1Grid->asDouble(x, y) / 100.; moisture[1] = m_pM10Grid->asDouble(x, y) / 100.; moisture[2] = m_pM100Grid->asDouble(x, y) / 100.; moisture[3] = m_pM100Grid->asDouble(x, y) / 100.; moisture[4] = m_pMHerbGrid->asDouble(x, y) / 100.; moisture[5] = m_pMWoodGrid->asDouble(x, y) / 100.; dWindSpd = getWindSpeed(x, y, m_pTimeGrid->asDouble(x,y)) * KMH2FTMIN; dWindDir = 360. - getWindDirection(x, y, m_pTimeGrid->asDouble(x,y)); Fire_SpreadNoWindNoSlope(m_Catalog, modelNumber, moisture); Fire_SpreadWindSlopeMax(m_Catalog, modelNumber, dWindSpd, dWindDir, tan(m_pSlopeGrid->asDouble(x,y)), m_pAspectGrid->asDouble(x,y)); for (i = -2; i < 3 ; i++){ for (j = -2; j < 3; j++){ if (i!= 0 || j!=0){ x2 = x + i; y2 = y + j; if (m_pTimeGrid->is_InGrid(x2,y2,false)){ fAz = getAzimuth(i,j); Fire_SpreadAtAzimuth(m_Catalog, modelNumber, fAz, FIRE_BYRAMS); dSpreadRate = Fuel_SpreadAny(m_Catalog, modelNumber); // in ft/min (awkward...) dSpreadRate *= FTMIN2MMIN; //a bit better... if (dSpreadRate > Smidgen){ fDist = sqrt(pow(i,2.) + pow(j,2.)) * m_pTimeGrid->Get_Cellsize(); dSpreadTime = fDist / dSpreadRate; dIgnTime = m_pTimeGrid->asDouble(x,y) + dSpreadTime; if (dIgnTime < fTimeLimit){ if (m_pTimeGrid->asDouble(x2,y2) == 0.0 || m_pTimeGrid->asDouble(x2, y2)>dIgnTime){ m_pTimeGrid->Set_Value(x2, y2, dIgnTime); m_AdjPoints.Add(x2,y2); Fire_FlameScorch(m_Catalog, modelNumber, FIRE_FLAME); m_pFlameGrid->Set_Value(x2, y2, Fuel_FlameLength(m_Catalog, modelNumber) * FT2M); m_pIntensityGrid->Set_Value(x2, y2, Fuel_ByramsIntensity(m_Catalog, modelNumber) * BTU2KCAL / FT2M ); m_pReactionIntensityGrid->Set_Value(x2, y2, Fuel_RxIntensity(m_Catalog, modelNumber)); m_pHeatPerUnitAreaGrid->Set_Value(x2, y2, Fuel_HeatPerUnitArea(m_Catalog, modelNumber)); m_pEffectiveWindGrid->Set_Value(x2,y2, Fuel_EffectiveWind(m_Catalog, modelNumber)); }//if }//if }//if }//if }//if }//for }//for }//if }//for m_CentralPoints.Clear(); for (int i=0; i<m_AdjPoints.Get_Count(); i++){ x= m_AdjPoints.Get_X(i); y = m_AdjPoints.Get_Y(i); m_CentralPoints.Add(x, y); iBurntCells++; }//for m_AdjPoints.Clear(); if (fTimeLimit == NO_TIME_LIMIT){ Process_Get_Okay(true); }//if }//while return iBurntCells; }//method
int CSimulate::CalculateFireSpreading(float fTimeLimit){ int x,y; int x2,y2; int n; bool bReturn = false; /* neighbor's address*/ /* N NE E SE S SW W NW */ static int nX[8] = { 0, 1, 1, 1, 0, -1, -1, -1}; static int nY[8] = { 1, 1, 0, -1, -1, -1, 0, 1}; double nDist[8]; /* distance to each neighbor */ double nAzm[8]; /* compass azimuth to each neighbor (0=N) */ size_t modelNumber; /* fuel model number at current cell */ double moisture[6]; /* fuel moisture content at current cell */ double dSpreadRate; /* spread rate in direction of neighbor */ double dSpreadTime; /* time to spread from cell to neighbor */ double dIgnTime; /* time neighbor is ignited by current cell */ double dWindSpd; int iBurntCells = 0; bool bUpdate = Parameters("UPDATEVIEW")->asBool(); for (n=0; n<8; n++){ nDist[n] = sqrt ( nX[n] * m_pDEM->Get_Cellsize() * nX[n] * m_pDEM->Get_Cellsize() + nY[n] * m_pDEM->Get_Cellsize() * nY[n] * m_pDEM->Get_Cellsize() ); nAzm[n] = n * 45.; }//for while (m_CentralPoints.Get_Count()!=0){ for (int iPt=0; iPt<m_CentralPoints.Get_Count();iPt++){ x = m_CentralPoints.Get_X(iPt); y = m_CentralPoints.Get_Y(iPt); if (!m_pDEM->is_NoData(x,y) && !m_pFuelGrid->is_NoData(x,y)){ modelNumber = (size_t) m_pFuelGrid->asInt(x, y); moisture[0] = m_pM1Grid->asFloat(x, y); moisture[1] = m_pM10Grid->asFloat(x, y); moisture[2] = m_pM100Grid->asFloat(x, y); moisture[3] = m_pM100Grid->asFloat(x, y); moisture[4] = m_pMHerbGrid->asFloat(x, y);; moisture[5] = m_pMWoodGrid->asFloat(x, y); dWindSpd = m_pWindSpdGrid->asFloat(x,y) * MS2FTMIN; Fire_SpreadNoWindNoSlope(m_Catalog, modelNumber, moisture); Fire_SpreadWindSlopeMax(m_Catalog, modelNumber, dWindSpd, m_pWindDirGrid->asFloat(x,y), tan(m_pSlopeGrid->asFloat(x,y)), m_pAspectGrid->asFloat(x,y)); for (n=0; n<8; n++){ x2 = x + nX[n]; y2 = y + nY[n]; if (m_pTimeGrid->is_InGrid(x2,y2,false)){ Fire_SpreadAtAzimuth(m_Catalog, modelNumber, nAzm[n], FIRE_BYRAMS); dSpreadRate = Fuel_SpreadAny(m_Catalog, modelNumber); // in ft/min (awkward...) dSpreadRate *= FTMIN2MMIN; //a bit better... if (dSpreadRate > Smidgen){ dSpreadTime = nDist[n] / dSpreadRate; if (fTimeLimit == NO_TIME_LIMIT){ dIgnTime = m_pTimeGrid->asDouble(x,y) + dSpreadTime; if (m_pTimeGrid->asDouble(x2,y2) == 0.0 || m_pTimeGrid->asDouble(x2, y2) > dIgnTime + THRESHOLD_FOR_DIFFERENCE ){ m_pTimeGrid->Set_Value(x2, y2, dIgnTime); m_AdjPoints.Add(x2,y2); Fire_FlameScorch(m_Catalog, modelNumber, FIRE_FLAME); m_pFlameGrid->Set_Value(x2, y2, Fuel_FlameLength(m_Catalog, modelNumber) * FT2M); m_pIntensityGrid->Set_Value(x2, y2, Fuel_ByramsIntensity(m_Catalog, modelNumber) * BTU2KCAL / FT2M ); }//if }//if }//if }//if }//for }//if }//for m_CentralPoints.Clear(); for (int i=0; i<m_AdjPoints.Get_Count(); i++){ x= m_AdjPoints.Get_X(i); y = m_AdjPoints.Get_Y(i); m_CentralPoints.Add(x, y); }//for m_AdjPoints.Clear(); if (fTimeLimit == NO_TIME_LIMIT){ Process_Get_Okay(true); }//if if (bUpdate){ DataObject_Update(m_pTimeGrid, true); } }//while return iBurntCells; }//method
int main ( int argc, char *argv[] ) { Rows = atoi(argv[1]); Cols = atoi(argv[1]); CellWd = 3.2808399*3000/Cols; /* Cell width (E-W) in feet. */ //1 m = 3.2808 ft CellHt = 3.2808399*3000/Cols; /* Cell height (N-S) in feet. */ //1 m = 3.2808 ft /* NOTE 2: Change these to set uniform burning conditions. */ size_t Model = 1; /* NFFL 1 */ double WindSpd = atof(argv[2]); /* m/s */ double WindDir = atof(argv[3]); /* degrees clockwise from north */ double M1 = atof(argv[4]); /* 1-hr dead fuel moisture */ double M10 = 0; /* 10-hr dead fuel moisture */ double M100 = 0; /* 100-hr dead fuel moisture */ double Mherb = 0; /* Live herbaceous fuel moisture */ double Mwood = 0; /* Live woody fuel moisture */ double slp_tmp, asp_tmp; //slope and aspect temporary values char buffer[100]; //buffer when usedm fgets skips lines /* neighbor's address*/ //N NE E SE S SW W NW a b c d e f g h static int nCol[16] = { 0, 1, 1, 1, 0, -1, -1, -1, -1, 1, -2, 2, -2, 2, -1, 1}; static int nRow[16] = { -1, -1, 0, 1, 1, 1, 0, -1, -2, -2, -1, -1, 1, 1, 2, 2}; static int nTimes = 0; /* counter for number of time steps */ FuelCatalogPtr catalog; /* fuel catalog handle */ double nDist[16]; /* distance to each neighbor */ double nAzm[16]; /* compass azimuth to each neighbor (0=N) */ double timeNow; /* current time (minutes) */ double timeNext; /* time of next cell ignition (minutes) */ int row, col, cell; /* row, col, and index of current cell */ int nrow, ncol, ncell; /* row, col, and index of neighbor cell */ int n, cells; /* neighbor index, total number of map cells */ size_t modelNumber; /* fuel model number at current cell */ double moisture[6]; /* fuel moisture content at current cell */ double fpm; /* spread rate in direction of neighbor */ double minutes; /* time to spread from cell to neighbor */ double ignTime; /* time neighbor is ignited by current cell */ int atEdge; /* flag indicating fire has reached edge */ size_t *fuelMap; /* ptr to fuel model map */ double *ignMap; /* ptr to ignition time map (minutes) */ double *flMap; /* ptr to flame length map (feet) */ double *slpMap; /* ptr to slope map (rise/reach) */ double *aspMap; /* ptr to aspect map (degrees from north) */ double *wspdMap; /* ptr to wind speed map (ft/min) */ double *wdirMap; /* ptr to wind direction map (deg from north) */ double *m1Map; /* ptr to 1-hr dead fuel moisture map */ double *m10Map; /* ptr to 10-hr dead fuel moisture map */ double *m100Map; /* ptr to 100-hr dead fuel moisture map */ double *mherbMap; /* ptr to live herbaceous fuel moisture map */ double *mwoodMap; /* ptr to live stem fuel moisture map */ FILE *slope_file, *aspect_file; printf("Running fireSim with Rows:%d, U:%lf, Dir:%lf\n", Rows, WindSpd, WindDir); /* NOTE 3: allocate all the maps. */ cells = Rows * Cols; if ( (ignMap = (double *) calloc(cells, sizeof(double))) == NULL || (flMap = (double *) calloc(cells, sizeof(double))) == NULL || (slpMap = (double *) calloc(cells, sizeof(double))) == NULL || (aspMap = (double *) calloc(cells, sizeof(double))) == NULL || (wspdMap = (double *) calloc(cells, sizeof(double))) == NULL || (wdirMap = (double *) calloc(cells, sizeof(double))) == NULL || (m1Map = (double *) calloc(cells, sizeof(double))) == NULL || (m10Map = (double *) calloc(cells, sizeof(double))) == NULL || (m100Map = (double *) calloc(cells, sizeof(double))) == NULL || (mherbMap = (double *) calloc(cells, sizeof(double))) == NULL || (mwoodMap = (double *) calloc(cells, sizeof(double))) == NULL || (fuelMap = (size_t *) calloc(cells, sizeof(size_t))) == NULL ) { fprintf(stderr, "Unable to allocate maps with %d cols and %d rows.\n", Cols, Rows); return (1); } /* NOTE 4: initialize all the maps -- modify them as you please. */ if ( (slope_file = fopen(argv[5],"r")) == NULL ){ printf("Unable to open output map \"%s\".\n", argv[5]); return (FIRE_STATUS_ERROR); } if ( (aspect_file = fopen(argv[6],"r")) == NULL ){ printf("Unable to open output map \"%s\".\n", argv[6]); return (FIRE_STATUS_ERROR); } /*for (n = 0; n < 6; n++){ fgets(buffer, 100, slope_file); fgets(buffer, 100, aspect_file); } */ for ( cell=0; cell<cells; cell++ ) { fscanf(aspect_file, "%lf", &asp_tmp); fscanf(slope_file, "%lf", &slp_tmp); slpMap[cell] = slp_tmp/100; //Slope in firelib is a fraction asp_tmp = (asp_tmp - 90 < 0) ? //while in Grass is percentage rise/reach. asp_tmp - 90 + 360 : asp_tmp - 90 ; //Aspect in firelib is N=0 and clockwise aspMap[cell] = 360 - asp_tmp; //while aspect in Grass is E=0 counter-clockwise fuelMap[cell] = Model; wspdMap[cell] = 196.850393701 * WindSpd; /* convert m/s into ft/min */ wdirMap[cell] = WindDir; m1Map[cell] = M1; m10Map[cell] = M10; m100Map[cell] = M100; mherbMap[cell] = Mherb; mwoodMap[cell] = Mwood; ignMap[cell] = INFINITY; flMap[cell] = 0.; } /* NOTE 5: set an ignition time & pattern (this ignites the middle cell). */ cell = floor(Cols/2) + Cols*floor(Rows/2); ignMap[cell] = 0.0; /* NOTE 6: create a standard fuel model catalog and a flame length table. */ //////////////////////////////// //Create fuel catalog //Create 13 + 0 (no fuel model) standard NFFL models and creates space for //aditional custom model catalog = Fire_FuelCatalogCreateStandard("Standard", 14); //Create aditional custom model based on NFFL1 //Only the PARTICLE LOAD is customized at the moment if ( Fire_FuelModelCreate ( catalog, //FuelCatalogData instance 14, //fuel model number "CUSTOM", //Name "Custom Fuel model", //longer description 0.197, //bed depth (ft) Fuel_Mext(catalog, 1), //moisture of extinction (dl) Fuel_SpreadAdjustment(catalog, 1), //spread adjustment factor (dl) 1) != FIRE_STATUS_OK ) //maximum number of particles { fprintf(stderr, "%s\n", FuelCat_Error(catalog)); Fire_FuelCatalogDestroy(catalog); return (NULL); } //Add a particle to the custom model nº 14 if ( Fire_FuelParticleAdd ( catalog, // FuelCatalogData instance pointer 14, //Custom fuel model id Fuel_Type(catalog,1,0), 0.23, // Custom particle load (lbs/ft2) 3500, // surface-area-to-volume ratio (ft2/ft3) Fuel_Density(catalog,1,0), //density (lbs/ft3) Fuel_Heat(catalog,1,0), //heat of combustion (btus/lb) Fuel_SiTotal(catalog,1,0), //total silica content (lb/lb) Fuel_SiEffective(catalog,1,0)) //effective silica content (lb/lb) != FIRE_STATUS_OK ) { fprintf(stderr, "%s\n", FuelCat_Error(catalog)); Fire_FuelCatalogDestroy(catalog); return (NULL); } Fire_FlameLengthTable(catalog, 500, 0.1); /* Calculate distance across cell to each neighbor and its azimuth. */ for ( n=0; n < 16; n++ ) { nDist[n] = sqrt ( nCol[n] * CellWd * nCol[n] * CellWd + nRow[n] * CellHt * nRow[n] * CellHt ); if (n < 8) nAzm[n] = n * 45.; else { nAzm[n] = atanf( (nCol[n] * CellWd) / (nRow[n] * CellHt) ); if ( nCol[n] > 0 && nRow[n] < 0) //1st quadrant nAzm[n] = RadToDeg( fabs( nAzm[n] ) ); if ( nCol[n] > 0 && nRow[n] > 0) //2st quadrant nAzm[n] = 180. - RadToDeg( nAzm[n] ) ; if ( nCol[n] < 0 && nRow[n] > 0) //3st quadrant nAzm[n] = RadToDeg( fabs( nAzm[n] ) )+ 180.; if ( nCol[n] < 0 && nRow[n] < 0) //4st quadrant nAzm[n] = 360. - RadToDeg( fabs( nAzm[n] )); } } /* NOTE 7: find the earliest (starting) ignition time. */ for ( timeNext=INFINITY, cell=0; cell<cells; cell++ ) { if ( ignMap[cell] < timeNext ) timeNext = ignMap[cell]; } /* NOTE 8: loop until no more cells can ignite or fire reaches an edge. */ atEdge = 0; //while ( timeNext < INFINITY && ! atEdge ) while ( timeNext < INFINITY) { timeNow = timeNext; timeNext = INFINITY; nTimes++; /* NOTE 9: examine each ignited cell in the fuel array. */ for ( cell=0, row=0; row<Rows; row++ ) { for ( col=0; col<Cols; col++, cell++ ) { /* Skip this cell if it has not ignited. */ if ( ignMap[cell] > timeNow ) { /* NOTE 12: first check if it is the next cell to ignite. */ if ( ignMap[cell] < timeNext ) timeNext = ignMap[cell]; continue; } /* NOTE 10: flag if the fire has reached the array edge. */ if ( row==0 || row==Rows-1 || col==0 || col==Cols-1 ) atEdge = 1; /* NOTE 11: determine basic fire behavior within this cell. */ modelNumber = fuelMap[cell]; moisture[0] = m1Map[cell]; moisture[1] = m10Map[cell]; moisture[2] = m100Map[cell]; moisture[3] = m100Map[cell]; moisture[4] = mherbMap[cell]; moisture[5] = mwoodMap[cell]; Fire_SpreadNoWindNoSlope(catalog, modelNumber, moisture); Fire_SpreadWindSlopeMax(catalog, modelNumber, wspdMap[cell], wdirMap[cell], slpMap[cell], aspMap[cell]); /* NOTE 12: examine each unignited neighbor. */ for ( n=0; n<16; n++ ) { /* First find the neighbor's location. */ nrow = row + nRow[n]; ncol = col + nCol[n]; if ( nrow<0 || nrow>=Rows || ncol<0 || ncol>=Cols ) continue; ncell = ncol + nrow*Cols; /* Skip this neighbor if it is already ignited. */ if ( ignMap[ncell] <= timeNow ) continue; /* Determine time to spread to this neighbor. */ Fire_SpreadAtAzimuth(catalog, modelNumber, nAzm[n], FIRE_NONE); if ( (fpm = Fuel_SpreadAny(catalog, modelNumber)) > Smidgen) { minutes = nDist[n] / fpm; /* Assign neighbor the earliest ignition time. */ if ( (ignTime = timeNow + minutes) < ignMap[ncell] ) { ignMap[ncell] = ignTime; Fire_FlameScorch(catalog, modelNumber, FIRE_FLAME); flMap[ncell] = Fuel_FlameLength(catalog,modelNumber); } /* Keep track of next cell ignition time. */ if ( ignTime < timeNext ) timeNext = ignTime; } } /* next neighbor n */ } /* next source col */ } /* next source row */ } /* next time */ printf("There were %d time steps ending at %3.2f minutes (%3.2f hours).\n", nTimes, timeNow, timeNow/60.); /* NOTE 13: save the ignition & flame length maps. */ PrintMap(aspMap,"aspect.Map"); PrintMap(slpMap,"slope.Map"); PrintMap(ignMap, "ign.Map"); PrintMap(flMap, "flame.Map"); return (0); }