Beispiel #1
0
    def do_fires(self):
        """Uses the various components of the fire model to draw and (possibly) spread one year's fires.
        """

        ##########################################################
        # 1) Find out how many ignitions there are this year,
        #      and their associated weather streams/forecasts
        ##########################################################
        weather_seed = seed_add(self.primary_random_seed, self.current_year)
        weather_seed = seed_add(weather_seed, 298238234)
        
        weathers, forecasts = self.WeatherModel.get_new_fires(random_seed=weather_seed)
        
        #if there are no fires this year, return nothing
        if len(weathers) == 0:
            return []

        #generate ignition locations
        def _rand_loc(i):
            random.seed(seed_add(weather_seed, i+253456))
            _x = random.uniform(0, self.size[0])
            _y = random.uniform(0, self.size[1])
            return (_x, _y)

        locations = [ _rand_loc(i) for i in range(len(weathers))  ]

        #set up fire record object list
        fire_records = [None] * len(weathers)

        ### FOR EACH IGNITION THIS YEAR:
        for i in range(len(weathers)):
            #we can't get a list of suppression decisions all at once, because each fire
            # may change the landscape in the vicinity of the NEXT fire, and that may in
            # turn change the policy decision. So we have to do one at a time and 
            # simulate the fire, before getting the next decision

            #check to make sure this weather stream has anything in it.
            if len(weathers[i]) == 0:
                continue

            ##########################################################
            # 2) Use the current suppression policy to make choices
            ##########################################################
            supr_decision = self.Policy.get_decision(self, locations[i], forecasts[i])

            ##########################################################
            # 3) Simulate each fire (and potential suppression)
            ##########################################################
            fire_records[i] = self.FireModel.simulate_fire(self, locations[i], weathers[i], supr_decision) 

        return fire_records
Beispiel #2
0
    def draw_wind(self, date, random_seed=None):
        """Random wind speed in kph

        Drawing from an exponential distribution with mean = 10
        TODO: what SHOULD it be?
        """
        wind_mean = 10
        random.seed(seed_add(random_seed,439201))
        return random.expovariate(1.0 / wind_mean)
Beispiel #3
0
    def draw_rain(self, date, random_seed=None):
        #from http://www.na.fs.fed.us/spfo/pubs/silvics_manual/Volume_1/pinus/ponderosa.htm
        # July, August, and September are dry; average rainfall is less than 25 mm
        #
        #that's ~ 0.8 mm per day. Lets draw from an exponential with mean = 0.8
        mean_summer_precip = 0.8 #mm
        random.seed(seed_add(random_seed, 792723))
        rainfall = random.expovariate(1.0 / mean_summer_precip)

        #cutting it off, so that many values are zero
        if rainfall < 0.8: rainfall = 0.0

        return rainfall
Beispiel #4
0
    def draw_RH(self, date, random_seed=None):
        #from the Western Regional Climate Center
        #http://www.wrcc.dri.edu/narratives/OREGON.htm
        #The afternoon average relative humidity ranges between 75 and 85 percent in January, 
        #while in July this drops to 25 or 30 percent east of the Cascades and slightly higher
        #  on the western side.  Relative humidities of 10 to 20 percent often occur under extreme
        # conditions during the summer and early fall.

        date_mean_humidity = math.cos( (2.0*(date)*math.pi) / (365.0) ) * (80-15) + 15
        
        #lets let actual humidity vary +/- 5%
        random.seed(seed_add(random_seed, 985727))
        rh = random.normalvariate(date_mean_humidity, 2.5)
        if rh < 0: rh = 0.0
        if rh > 100: rh = 100.0
        return rh
Beispiel #5
0
    def get_new_fires(self, random_seed=None):
        """Generates a new series of fires for a simulation year and returns any/all of them

        PARAMETERS
        ----------
        random_seed
            Any numeric value for seeding the weather/forecast generation process.
            Ensures replicability.


        RETURNS
        -------
        weather_streams, forecasts

        weather_streams
            A list of weather streams. Each weather stream is a 2-d list, indexed by day,
            of a particular fire/weather event.

        forecasts
            A list of weather forecasts. Each forecast is a a 2-d list, indexed by day, of
            weather forecast information for a particular fire/weather event.

        """
        #how many fires are there this year?
        ave_fire_count = 0.5
        random.seed(seed_add(random_seed, 471294932))
        fire_count = int(random.expovariate(1.0 / ave_fire_count))

        if fire_count == 0:
            return [[],[]]

        weather_streams = [None]*fire_count
        forecasts = [None]*fire_count

        for f in range(fire_count):
            #drawing fire days bewteen April-ish and October-ish
            streams = self.get_new_fire_weather_stream_pair( date=random.randint(100,310), random_seed=seed_add(random_seed,128439322))
            weather_streams[f] = streams[0]
            forecasts[f] = streams[1]

        return weather_streams, forecasts
Beispiel #6
0
    def draw_temp(self, date, random_seed=None):
        """Random temperature for the given date (as day of year)"""

        # from http://www.na.fs.fed.us/spfo/pubs/silvics_manual/Volume_1/pinus/ponderosa.htm
        # average annual temperatures are between 5 and 10 C (41 and 50 F), 
        # and average July-August temperatures are between 17 and 21 C
        # Annual extremes are from -40 to 43 
        #
        #Lets call average annual temperature 7.5C, with average summer temps of 19C
        # so the mean temp will rise from 7.5C in May to 19C in July, and back down to
        # 7.5 in October
        #
        #May starts on day 121
        date = date-121
        date_mean_temp = math.sin( (2.0*(date)*math.pi) / (365.0) ) * (19-7.5) + 7.5

        random.seed(seed_add(random_seed, 390752))
        #lets let daily actual temperatures vary around the mean by way of a normal distribution
        # where 95%-ish remain within +/- 20C from the mean (so one standard devation will be 10)

        return random.normalvariate(date_mean_temp, 10)
Beispiel #7
0
 def draw_wind_direction(self, date, random_seed=None):
     """A purely random wind direction, in degrees from North"""
     random.seed(seed_add(random_seed, 818127))
     return random.uniform(0,364)
Beispiel #8
0
    def get_new_fire_weather_stream_pair(self, date, random_seed=None):
        """Creates a new weather stream and forcast

        PARAMETERS
        ----------
        date
            the integer date of the year

        random_seed
            Any numeric value for seeding the weather/forecast generation process.
            Ensures replicability.

        RETURNS
        -------
        weather, forecast
        
        weather 
            A list of dictionaries of weather variables with the primary index being day.
        forcast
            The associated weather forcast, of identical form/shape as "weather"

        """

        #how many days long is this fire event?
        ave_fire_weather_days = 3
        random.seed(seed_add(random_seed, 3789284))
        day_count = int(random.expovariate(1.0/ave_fire_weather_days))

        #if the fire event is less than three days long, we'll need to add some extras to make sure
        #that at least the FORECAST is three days long
        extra_forecast_days = 0
        if day_count < 3:
            extra_forecast_days = 3 - day_count

        #add days for the lead-in time, which is needed to allow FWI index values to come 
        # to equilibrium
        buffer_days = 10
        day_count += buffer_days
        day_count += extra_forecast_days

        #get random weather variables for the days
        weather = [ self.draw_weather_variables(date, seed_add(random_seed, 9472843)) for i in range(day_count) ]
        forecast = weather[:]

        #now do a blending of the variables, walking forward from the first day, where
        # each day is a combination of (mostly) the previous day's value, and (somewhat)
        # it's own value
        w_blend = 0.8
        f_blend = self.forecast_accuracy * w_blend

        for i in range(1,day_count):
            weather[i]["Temperature"]    = w_blend * weather[i-1]["Temperature"]    + (1-w_blend) * weather[i]["Temperature"]
            weather[i]["RH"]             = w_blend * weather[i-1]["RH"]             + (1-w_blend) * weather[i]["RH"]
            weather[i]["Wind Speed"]     = w_blend * weather[i-1]["Wind Speed"]     + (1-w_blend) * weather[i]["Wind Speed"]
            weather[i]["Wind Direction"] = w_blend * weather[i-1]["Wind Direction"] + (1-w_blend) * weather[i]["Wind Direction"]
            weather[i]["Rainfall"]       = w_blend * weather[i-1]["Rainfall"]       + (1-w_blend) * weather[i]["Rainfall"]

            forecast[i]["Temperature"]    = f_blend * forecast[i-1]["Temperature"]    + (1-f_blend) * forecast[i]["Temperature"]
            forecast[i]["RH"]             = f_blend * forecast[i-1]["RH"]             + (1-f_blend) * forecast[i]["RH"]
            forecast[i]["Wind Speed"]     = f_blend * forecast[i-1]["Wind Speed"]     + (1-f_blend) * forecast[i]["Wind Speed"]
            forecast[i]["Wind Direction"] = f_blend * forecast[i-1]["Wind Direction"] + (1-f_blend) * forecast[i]["Wind Direction"]
            forecast[i]["Rainfall"]       = f_blend * forecast[i-1]["Rainfall"]       + (1-f_blend) * forecast[i]["Rainfall"]

        #now add the FWI variables
        #The first day will get a value of 50 for FFMC, DMC, and DC (moisture and drought codes)
        #After that, they will adjust themselves as they go
        weather[0]["FFMC"] = 50
        weather[0]["DMC"] = 50
        weather[0]["DC"] = 50
        forecast[0]["FFMC"] = 50
        forecast[0]["DMC"] = 50
        forecast[0]["DC"] = 50
        for i in range(1,day_count):
            #I'm holding the month to whatever it was when the fire started to avoid any 
            # discontinuities partway through a weather stream. Hense 'weather[0]["Date"]'
            MONTH = self.get_month(weather[0]["Date"]) 
            LAT = 45
            TEMP = weather[i]["Temperature"]
            RH = weather[i]["RH"]
            WIND = weather[i]["Wind Speed"]
            RAIN = weather[i]["Rainfall"]
            FFMCPrev = weather[i-1]["FFMC"]
            DMCPrev = weather[i-1]["DMC"]
            DCPrev = weather[i-1]["DC"]
            weather[i]["FFMC"] = FWI.FFMC(TEMP,RH,WIND,RAIN,FFMCPrev)
            weather[i]["DMC"] = FWI.DMC(TEMP,RH,RAIN,DMCPrev,LAT,MONTH)
            weather[i]["DC"] = FWI.DC(TEMP,RAIN,DMCPrev,LAT,MONTH)
            weather[i]["FWI"] = FWI.calcFWI(MONTH,TEMP,RH,WIND,RAIN,FFMCPrev,DMCPrev,DCPrev,LAT)

            TEMP = forecast[i]["Temperature"]
            RH = forecast[i]["RH"]
            WIND = forecast[i]["Wind Speed"]
            RAIN = forecast[i]["Rainfall"]
            FFMCPrev = forecast[i-1]["FFMC"]
            DMCPrev = forecast[i-1]["DMC"]
            DCPrev = forecast[i-1]["DC"]
            forecast[i]["FFMC"] = FWI.FFMC(TEMP,RH,WIND,RAIN,FFMCPrev)
            forecast[i]["DMC"] = FWI.DMC(TEMP,RH,RAIN,DMCPrev,LAT,MONTH)
            forecast[i]["DC"] = FWI.DC(TEMP,RAIN,DCPrev,LAT,MONTH)
            forecast[i]["FWI"] = FWI.calcFWI(MONTH,TEMP,RH,WIND,RAIN,FFMCPrev,DMCPrev,DCPrev,LAT)

        #the forecast will always be three days
        forecast = weather[buffer_days:buffer_days+3]

        #trim off the ten lead-in days
        weather = weather[buffer_days:]
        #trim off any extra forecast days
        if extra_forecast_days > 0:
            weather = weather[:-1*extra_forecast_days]


        return weather, forecast
Beispiel #9
0
    def __init__(self, landscape_size=(129,129), random_seed=None):

        #maintenance flags
        self.VERBOSE = True
        self.DEBUGGING = True


        #primary simulation models
        self.FireModel = FGFire.SpreadModel()
        self.HarvestModel = FGHarvest.HarvestModel()
        self.TreatmentModel = FGTreatments.TreatmentModel()
        self.GrowthModel = FGGrowth.GrowthModel()
        self.WeatherModel = FGWeather.WeatherModel()

        #fire suppression policy
        self.Policy = FGPolicy.SuppressionPolicy()

        #sanitize landscape_size
        #check if it's iterable, and of length 2
        if not hasattr(landscape_size,"__iter__"):
            #it's not iterable, so assume it's an int and correc that
            self.size = (landscape_size, landscape_size)
        else:
            #it's iterable, so save the first two values
            self.size = (landscape_size[0], landscape_size[1])


        #important pathway values
        self.year_history = [] # a list to hold all of this simulation's YearRecord objects
        self.current_year = 0
        self.primary_random_seed = random_seed


        #Primary data arrays, etc...
        #   site index: this remains constant
        self.site_index = DS.diamond_square(self.size, min_height=0, max_height=100, roughness=0.5, random_seed=seed_add(self.primary_random_seed, 0),AS_NP_ARRAY=True).astype(int)
        #   each cell's stand initiation year: each one started in the last 100 years or so
        self.stand_init_yr = DS.diamond_square(self.size, min_height=-100, max_height=0, roughness=0.95, random_seed=seed_add(self.primary_random_seed, 1), AS_NP_ARRAY=True).astype(int)
        #   the last year in which each cell experienced a stand-replacing fire, leaving dead trees standing
        self.stand_rplc_fire_yr = DS.diamond_square(self.size, min_height=-100, max_height=0, roughness=0.95, random_seed=seed_add(self.primary_random_seed, 2), AS_NP_ARRAY=True).astype(int)
        #   the last year in which there was a surface fire
        self.surf_fire_yr = DS.diamond_square(self.size, min_height=-100, max_height=0, roughness=0.95, random_seed=seed_add(self.primary_random_seed, 2), AS_NP_ARRAY=True).astype(int)


        #units
        #how many acres per cell?
        self.acres_per_cell = 25
Beispiel #10
0
 def _rand_loc(i):
     random.seed(seed_add(weather_seed, i+253456))
     _x = random.uniform(0, self.size[0])
     _y = random.uniform(0, self.size[1])
     return (_x, _y)