Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions configs/hill.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# propagation_model: RothermelAndrews2018
# nn_ros_model_path:
propagation_model: ANNPropagationModel
nn_ros_model_path: /home/ai4geo/Documents/nn_ros_models/nn_abs_256_RothermelAndrews2018/saved_model.ffann
fuel_type: [6, 7]
domain_width: 1000
domain_height: 1000
horizontal_wind: 200.0
vertical_wind: 0.0
nb_steps: 20
step_size: 10
fire_front: [[0, 550], [50, 500], [0, 450]]

spatial_increment: 0.5
perimeter_resolution: 2 # max distance between two nodes
minimal_propagative_front_depth: 5 # resolution of arrival time matrix
min_speed: 0.0
max_speed: 100.0
relax: 1 # takes 0.2 of the new ROS and 0.8 of the old ROS, to set in function of spatial_increment!
propagation_speed_adjustment_factor: 1
burned_map_layer:
20 changes: 20 additions & 0 deletions configs/nn_rothermel_andrews_uniform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
propagation_model: ANNPropagationModel
nn_ros_model_path: /home/ai4geo/Documents/nn_ros_models/nn_abs_256_RothermelAndrews2018/saved_model.ffann
fuel_type: 6
domain_width: 2000
domain_height: 2000
horizontal_wind: 20.0
vertical_wind: 0.0
slope: 0
nb_steps: 10
step_size: 10
fire_front: [[950, 1050], [1000, 1000], [950, 950]]

spatial_increment: 0.5
perimeter_resolution: 2 # max distance between two nodes
minimal_propagative_front_depth: 5 # resolution of arrival time matrix
min_speed: 0.0
max_speed: 20.0
relax: 1 # takes 0.2 of the new ROS and 0.8 of the old ROS, to set in function of spatial_increment!
propagation_speed_adjustment_factor: 1
burned_map_layer:
7 changes: 4 additions & 3 deletions configs/rothermel_andrews_uniform.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
propagation_model: RothermelAndrews2018
nn_ros_model_path:
fuel_type: 6
domain_width: 2000
domain_height: 2000
horizontal_wind: 20.0
horizontal_wind: 200.0
vertical_wind: 0.0
slope: 0
nb_steps: 10
Expand All @@ -13,7 +14,7 @@ spatial_increment: 0.5
perimeter_resolution: 2 # max distance between two nodes
minimal_propagative_front_depth: 5 # resolution of arrival time matrix
min_speed: 0.0
max_speed: 20.0
relax: 0.2 # takes 0.2 of the new ROS and 0.8 of the old ROS, to set in function of spatial_increment!
max_speed: 100.0
relax: 1 # takes 0.2 of the new ROS and 0.8 of the old ROS, to set in function of spatial_increment!
propagation_speed_adjustment_factor: 1
burned_map_layer:
1 change: 1 addition & 0 deletions configs/rothermel_uniform.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
propagation_model: Rothermel
nn_ros_model_path:
fuel_type: 141
domain_width: 2000
domain_height: 2000
Expand Down
Binary file modified test/__pycache__/forefire_helper.cpython-38.pyc
Binary file not shown.
3 changes: 3 additions & 0 deletions test/forefire_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ def get_fuels_table(propagation_model):
return RothermelAndrews2018FuelTable
elif propagation_model == 'Rothermel':
return standardRothermelFuelTable
elif propagation_model == 'ANNPropagationModel':
#TODO: fix for a more general use
return RothermelAndrews2018FuelTable
else:
raise NotImplementedError

Expand Down
123 changes: 123 additions & 0 deletions test/hill_simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from forefire_helper import *
from simulation import UniformWindForeFireSimulation
import argparse
import yaml
import numpy as np
import matplotlib.pyplot as plt
import pdb


def hill(x, mean, cov, height=100):
N = len(mean)
den = (2*np.pi)**(N/2) * np.linalg.det(cov)**0.5
exp = np.exp(-0.5 * np.einsum('...k,kl,...l->...', x-mean, np.linalg.inv(cov), x-mean))
gaussian = exp / den
altitude = height * (gaussian - gaussian.min()) / (gaussian.max() - gaussian.min())
return altitude


def hill_simulation(config):
propagation_model = config['propagation_model']
nn_ros_model_path = config['nn_ros_model_path']

if 'fuels_table' in config:
fuels_table = fuels_table
else:
fuels_table = get_fuels_table(propagation_model)
fuel_type = config['fuel_type']
domain_width = config['domain_width']
domain_height = config['domain_height']
domain = (0, 0, domain_width, domain_height)

# Create hill with a 2D gaussian
mean = np.array([domain_height // 2, domain_width //2])
cov = np.array([
[1e5, 0],
[0, 1e5]])

map_x, map_y = np.meshgrid(
np.arange(domain_height),
np.arange(domain_width)
)

map = np.empty(map_x.shape + (2,))
map[:, :, 0] = map_x
map[:, :, 1] = map_y

altitude_map = hill(map, mean, cov)
# plt.imshow(altitude_map); plt.show()

fuel_map = np.ones_like(altitude_map)
fuel_map[:, :domain_width // 2] = fuel_type[0]
fuel_map[:, :domain_width // 2] = fuel_type[1]

horizontal_wind = config['horizontal_wind']
vertical_wind = config['vertical_wind']

fire_front = config['fire_front']
spatial_increment = config['spatial_increment']
minimal_propagative_front_depth = config['minimal_propagative_front_depth']
perimeter_resolution = config['perimeter_resolution']
relax = config['relax']
min_speed = config['min_speed']
burned_map_layer = config['burned_map_layer']

simulation = UniformWindForeFireSimulation(
propagation_model,
fuels_table,
horizontal_wind,
vertical_wind,
fuel_map,
altitude_map,
fire_front,
nn_ros_model_path,
spatial_increment,
minimal_propagative_front_depth,
perimeter_resolution,
relax,
min_speed,
burned_map_layer,
)

nb_steps = config['nb_steps']
step_size = config['step_size']

# Run simulation
pathes = simulation(nb_steps, step_size)

##-----------------------------------------------------------------------------
## Plot the simulated Fronts
##-----------------------------------------------------------------------------
plotExtents = (
float(simulation.ff["SWx"]),
float(simulation.ff["SWx"]) + float(simulation.ff["Lx"]),
float(simulation.ff["SWy"]),
float(simulation.ff["SWy"]) + float(simulation.ff["Ly"]))
plot_simulation(pathes, simulation.fuel_map[0, 0], simulation.altitude_map[0, 0], plotExtents, None)

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--config', type=str,
help='Path to the config file (.yaml) of the simulation.')
parser.add_argument('--propagation_model', type=str, default='RothermelAndrews2018',
help='Rate of Spread model (default: RothermelAndrews2018)')
parser.add_argument('--fuel_type', type=int, default=6,
help='Index of the fuel type to use (default: 6)')
parser.add_argument('--domain_width', type=int)
parser.add_argument('--domain_height', type=int)
parser.add_argument('--horizontal_wind', type=float)
parser.add_argument('--vertical_wind', type=float)
parser.add_argument('--slope', type=float)
parser.add_argument('--nb_steps', type=int,
help='Number of simulation steps')
parser.add_argument('--step_size', type=float,
help='Duration (in seconds) between each step.')
args = parser.parse_args()

if args.config:
with open(args.config, 'r') as stream:
config = yaml.safe_load(stream)
else:
config = vars(args)

hill_simulation(config)
92 changes: 89 additions & 3 deletions test/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pyforefire as forefire
from forefire_helper import *

import pdb

logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logger = logging.getLogger()
Expand All @@ -18,7 +19,6 @@ class ForeFireSimulation:
def __init__(
self,
propagation_model: str,
domain: Tuple[int],
fuels_table: callable,
spatial_increment: Optional[float] = None,
minimal_propagative_front_depth: Optional[float] = None,
Expand All @@ -45,7 +45,7 @@ def __init__(
# Initialize pyforefire module
ff = forefire.ForeFire()
ff["propagationModel"] = propagation_model
ff["SWx"], ff["SWy"], ff["Lx"], ff["Ly"] = domain
# ff["SWx"], ff["SWy"], ff["Lx"], ff["Ly"] = domain
ff["fuelsTable"] = fuels_table()

if spatial_increment:
Expand Down Expand Up @@ -118,6 +118,7 @@ def __init__(
fuel_type: float,
slope: float,
fire_front: List[List[float]],
nn_ros_model_path: Optional[str] = None,
spatial_increment: Optional[float] = None,
minimal_propagative_front_depth: Optional[float] = None,
perimeter_resolution: Optional[float] = None,
Expand All @@ -128,7 +129,6 @@ def __init__(
):
super().__init__(
propagation_model,
domain,
fuels_table,
spatial_increment,
minimal_propagative_front_depth,
Expand All @@ -140,12 +140,15 @@ def __init__(

# Instantiate domain
domain_height, domain_width = domain[-1], domain[-2]
self.ff["SWx"], self.ff["SWy"], self.ff["Lx"], self.ff["Ly"] = domain
domain_string = \
f'FireDomain[sw=({self.ff["SWx"]},{self.ff["SWy"]},0);ne=({self.ff["SWx"] + self.ff["Lx"]},{self.ff["SWy"] + self.ff["Ly"]},0);t=0]'
logger.info(domain_string)
self.ff.execute(domain_string)

# Propagation model layer
if nn_ros_model_path:
self.ff["FFANNPropagationModelPath"] = nn_ros_model_path
self.ff.addLayer(
"propagation",
self.ff["propagationModel"],
Expand Down Expand Up @@ -173,6 +176,89 @@ def __init__(
self.altitude_map[:, :, :] = np.linspace(0, domain_width, domain_width // data_resolution) * math.tan(slope)
self.ff.addScalarLayer("table", "altitude", 0, 0, 0, domain_width, domain_height, 0, self.altitude_map)

# Instantiate fire front (front orentation is clockwise!!)
self.ff.execute(f"\tFireFront[id=2;domain=0;t=0]")
for (xp, yp) in fire_front:
self.ff.execute(f"\t\tFireNode[domain=0;loc=({xp},{yp},0);vel=(0,0,0);t=0;state=init;frontId=2]")
logger.info(f'Initial fire front: {fire_front}')


class UniformWindForeFireSimulation(ForeFireSimulation):
"""
Class for a ForeFire simulation with uniform wind, uniform fuel (only one fuel type)
and uniform slope.
"""
def __init__(
self,
propagation_model: str,
fuels_table: callable,
horizontal_wind: float,
vertical_wind: float,
fuel_map: np.ndarray,
altitude_map: np.ndarray,
fire_front: List[List[float]],
nn_ros_model_path: Optional[str] = None,
spatial_increment: Optional[float] = None,
minimal_propagative_front_depth: Optional[float] = None,
perimeter_resolution: Optional[float] = None,
relax: Optional[float] = None,
min_speed: Optional[float] = None,
burned_map_layer: Optional[int] = None,
):
super().__init__(
propagation_model,
# domain,
fuels_table,
spatial_increment,
minimal_propagative_front_depth,
perimeter_resolution,
relax,
min_speed,
burned_map_layer,
)

# Instantiate domain
domain_height, domain_width = fuel_map.shape
self.ff["SWx"] = 0
self.ff["SWy"] = 0
self.ff["Lx"] = domain_width
self.ff["Ly"] = domain_height
domain_string = \
f'FireDomain[sw=({self.ff["SWx"]},{self.ff["SWy"]},0);ne=({self.ff["SWx"] + self.ff["Lx"]},{self.ff["SWy"] + self.ff["Ly"]},0);t=0]'
logger.info(domain_string)
self.ff.execute(domain_string)

# Propagation model layer
if nn_ros_model_path:
self.ff["FFANNPropagationModelPath"] = nn_ros_model_path
self.ff.addLayer(
"propagation",
self.ff["propagationModel"],
"propagationModel")
logger.info(f'ROS model: {propagation_model}')

# Wind layers
self.ff["windU"] = horizontal_wind
self.ff["windV"] = vertical_wind
self.ff.addLayer("data","windU","windU")
self.ff.addLayer("data","windV","windV")
logger.info(f'Uniform wind conditions: horizontal wind: {horizontal_wind} m/s | vertical wind: {vertical_wind} m/s')

# Fuel layer
self.fuel_map = fuel_map.reshape(1, 1, domain_height, domain_width)
self.ff.addIndexLayer(
"table",
"fuel", float(self.ff["SWx"]), float(self.ff["SWy"]), 0, float(self.ff["Lx"]), float(self.ff["Ly"]), 0,
self.fuel_map)
logger.info(f'Fuel map types: {list(np.unique(self.fuel_map))}')

# Altitude layer
self.altitude_map = altitude_map.reshape(1, 1, domain_height, domain_width)
self.ff.addIndexLayer(
"data",
"altitude", float(self.ff["SWx"]), float(self.ff["SWy"]), 0, float(self.ff["Lx"]), float(self.ff["Ly"]), 0,
self.altitude_map)

# Instantiate fire front (front orentation is clockwise!!)
self.ff.execute(f"\tFireFront[id=2;domain=0;t=0]")
for (xp, yp) in fire_front:
Expand Down
28 changes: 15 additions & 13 deletions test/tf_to_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,27 @@
tf_model_path = sys.argv[1]
bin_model_path = os.path.join(tf_model_path, 'saved_model.ffann')

rothermel_andrews_2018_input_names = [
"fuel.fl1h_tac",
"fuel.fd_ft",
"fuel.Dme_pc",
"fuel.SAVcar_ftinv",
"fuel.H_BTUlb",
"fuel.totMineral_r",
"fuel.effectMineral_r",
"fuel.fuelDens_lbft3",
"fuel.mdOnDry1h_r",
"normalWind",
"slope"
]

if not os.path.exists(bin_model_path):
model = tf.keras.saving.load_model(tf_model_path)
import pdb; pdb.set_trace()
save_model_structure(
model,
bin_model_path,
input_names=[
"normalWind", # Normal wind
"slope", # Slope
"fuel.Md", # Fuel particle moisture content
"fuel.DeltaH", # Fuel particle low heat content
"fuel.sd", # Fuel Particle surface area to volume ratio (1/ft)
"fuel.e", # Fuel depth (ft)
"fuel.Rhod", # Ovendry particle density
"fuel.me", # Moisture content of extinction
"fuel.Sigmad" # Ovendry fuel loading
],
input_names=rothermel_andrews_2018_input_names,
output_names=['ROSx']
)
# ['wind', 'slope', 'mdOnDry1h', 'H', 'SAVcar', 'fd', 'fuelDens', 'Dme', 'fl1h']

model, input_names, output_names = load_model_structure(bin_model_path)
Loading