diff --git a/configs/hill.yaml b/configs/hill.yaml new file mode 100644 index 0000000..8d45b08 --- /dev/null +++ b/configs/hill.yaml @@ -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: diff --git a/configs/nn_rothermel_andrews_uniform.yaml b/configs/nn_rothermel_andrews_uniform.yaml new file mode 100644 index 0000000..0b66112 --- /dev/null +++ b/configs/nn_rothermel_andrews_uniform.yaml @@ -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: diff --git a/configs/rothermel_andrews_uniform.yaml b/configs/rothermel_andrews_uniform.yaml index 493120c..a5b42c5 100644 --- a/configs/rothermel_andrews_uniform.yaml +++ b/configs/rothermel_andrews_uniform.yaml @@ -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 @@ -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: diff --git a/configs/rothermel_uniform.yaml b/configs/rothermel_uniform.yaml index d23b6f7..beff34d 100644 --- a/configs/rothermel_uniform.yaml +++ b/configs/rothermel_uniform.yaml @@ -1,4 +1,5 @@ propagation_model: Rothermel +nn_ros_model_path: fuel_type: 141 domain_width: 2000 domain_height: 2000 diff --git a/test/__pycache__/forefire_helper.cpython-38.pyc b/test/__pycache__/forefire_helper.cpython-38.pyc index b238b67..5457b4f 100644 Binary files a/test/__pycache__/forefire_helper.cpython-38.pyc and b/test/__pycache__/forefire_helper.cpython-38.pyc differ diff --git a/test/forefire_helper.py b/test/forefire_helper.py index d833675..aa16fcd 100644 --- a/test/forefire_helper.py +++ b/test/forefire_helper.py @@ -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 diff --git a/test/hill_simulation.py b/test/hill_simulation.py new file mode 100644 index 0000000..1f5fba2 --- /dev/null +++ b/test/hill_simulation.py @@ -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) \ No newline at end of file diff --git a/test/simulation.py b/test/simulation.py index 9575a6d..476b50c 100644 --- a/test/simulation.py +++ b/test/simulation.py @@ -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() @@ -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, @@ -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: @@ -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, @@ -128,7 +129,6 @@ def __init__( ): super().__init__( propagation_model, - domain, fuels_table, spatial_increment, minimal_propagative_front_depth, @@ -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"], @@ -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: diff --git a/test/tf_to_bin.py b/test/tf_to_bin.py index 20dc410..331c400 100644 --- a/test/tf_to_bin.py +++ b/test/tf_to_bin.py @@ -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) \ No newline at end of file diff --git a/test/uniform_simulation.py b/test/uniform_simulation.py index 3f4a199..8d93f51 100644 --- a/test/uniform_simulation.py +++ b/test/uniform_simulation.py @@ -6,6 +6,7 @@ def uniform_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 @@ -23,7 +24,6 @@ def uniform_simulation(config): minimal_propagative_front_depth = config['minimal_propagative_front_depth'] perimeter_resolution = config['perimeter_resolution'] relax = config['relax'] - smoothing = config['smoothing'] min_speed = config['min_speed'] burned_map_layer = config['burned_map_layer'] @@ -36,11 +36,11 @@ def uniform_simulation(config): fuel_type, slope, fire_front, + nn_ros_model_path, spatial_increment, minimal_propagative_front_depth, perimeter_resolution, relax, - smoothing, min_speed, burned_map_layer, )