From 164ba862f72da3deab6d7e1d5b37e6c9d6bc6e61 Mon Sep 17 00:00:00 2001 From: Antonio Leblanc Date: Wed, 11 Nov 2026 02:04:02 -0300 Subject: [PATCH] wasm: add Farsite to the browser demo model selector --- CHANGELOG.md | 2 ++ bindings/wasm/README.md | 8 ++++---- bindings/wasm/index.html | 3 +++ bindings/wasm/landscape.mjs | 21 ++++++++++++++------- bindings/wasm/worker.mjs | 11 +++++++++++ 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 062a38d5..f2389b40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ history, so they summarise each release rather than list every change. The ### Added +- The browser demo includes the `Farsite` propagation model in its model + selector. - `tests/mnh_coupled.bash` goes from a bare Linux machine to a coupled ForeFire + Meso-NH run: it downloads and builds Meso-NH 6.0.1, builds ForeFire with MPI, links them and runs `tests/mnh_real_nested`. With diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 7b98bb3a..80c528ef 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -94,10 +94,10 @@ map ForeFire writes natively, rebuilt from the perimeter of every frame. nodes on a tight curve do not wash it out. The models on offer are the ones that run on this fuel table with only slope -and wind: Rothermel, BalbiNov2011Curv, BalbiUnsteady, TroisPourcent and Iso. -Balbi2015, Balbi2020 and BalbiNov2011TMdMl also need moisture and temperature -layers and abort without them; BalbiNov2011 places the front far from the -ignition. +and wind: Rothermel, Farsite, BalbiNov2011Curv, BalbiUnsteady, TroisPourcent +and Iso. Balbi2015, Balbi2020 and BalbiNov2011TMdMl also need moisture and +temperature layers and abort without them; BalbiNov2011 places the front far +from the ignition. ### The landscape comes from map tiles diff --git a/bindings/wasm/index.html b/bindings/wasm/index.html index dccfee19..6c516f40 100644 --- a/bindings/wasm/index.html +++ b/bindings/wasm/index.html @@ -462,6 +462,7 @@

Run

// temperature layers abort here, and BalbiNov2011 misplaces the front. const MODELS = [ ["Rothermel", "Rothermel (1972)"], + ["Farsite", "FARSITE (Finney 1998)"], ["BalbiNov2011Curv", "Balbi 2011, curvature"], ["BalbiUnsteady", "Balbi, unsteady"], ["TroisPourcent", "3% rule"], @@ -765,6 +766,8 @@

Run

liveMoisture: value.liveMoisture, depth: { 5: value.depth5, 4: value.depth4 }, }), + deadMoisture: value.deadMoisture, + liveMoisture: value.liveMoisture, defaultFuel: FUELS[0].index, }, model: $("model").value, diff --git a/bindings/wasm/landscape.mjs b/bindings/wasm/landscape.mjs index 6915c848..4aaa4e34 100644 --- a/bindings/wasm/landscape.mjs +++ b/bindings/wasm/landscape.mjs @@ -20,15 +20,17 @@ const IMAGERY = const TERRAIN = "https://s3.amazonaws.com/elevation-tiles-prod/terrarium"; /*! The three fuel classes, as rows of tests/runff/fuels.csv — the repository's - * own Rothermel table, not invented numbers. Index 0 has a fuel bed depth of - * zero, which is what makes it a barrier. */ + * own Rothermel table — followed by the FARSITE columns from + * STDfarsiteFuelsTable (FM4 chaparral for shrub, FM1 short grass for grass, + * NB0 for bare ground). Index 0 has a fuel bed depth of zero, which is what + * makes it a barrier. */ const FUEL_ROWS = { - 5: "5;626.0;600.0;0.1;1.0;4325.0;5844.0;0.6;1.393;0.201;8.3;1.0;300;70000;18802000.0;18802000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3", - 4: "4;613.0;538.0;0.1;1.0;4357.0;6524.0;0.19;1.286;0.085;8.3;1.0;300;70000;18677000.0;18677000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3", - 0: "0;563.0;522.0;0.1;1.0;6099.0;7273.0;0;0.764;0.352;8.3;1.0;300;70000;18169000.0;18167000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3", + 5: "5;626.0;600.0;0.1;1.0;4325.0;5844.0;0.6;1.393;0.201;8.3;1.0;300;70000;18802000.0;18802000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3;5.01;4.01;2.0;0.0;5.01;0;2000;1800;1500;6.0;0.2;8000.0;8000.0", + 4: "4;613.0;538.0;0.1;1.0;4357.0;6524.0;0.19;1.286;0.085;8.3;1.0;300;70000;18677000.0;18677000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3;0.74;0.0;0.0;0.0;0.0;0;3500;1800;1500;1.0;0.12;8000.0;8000.0", + 0: "0;563.0;522.0;0.1;1.0;6099.0;7273.0;0;0.764;0.352;8.3;1.0;300;70000;18169000.0;18167000.0;1800;1000;600;0.3;2.5e-05;4.0;0.3;0.0;0.0;0.0;0.0;0.0;0;0;0;0;0.0;0.0;0.0;0.0", }; const FUEL_HEADER = - "Index;Rhod;Rhol;Md;Ml;sd;sl;e;Sigmad;Sigmal;stoch;RhoA;Ta;Tau0;Deltah;DeltaH;Cp;Cpa;Ti;X0;r00;Blai;me"; + "Index;Rhod;Rhol;Md;Ml;sd;sl;e;Sigmad;Sigmal;stoch;RhoA;Ta;Tau0;Deltah;DeltaH;Cp;Cpa;Ti;X0;r00;Blai;me;h1;h10;h100;lh;lw;dynamic;sav1;savlh;savlw;depth;xmext;heatd;heatl"; export const FUELS = [ { index: 5, label: "shrub / maquis", note: "depth 0.6 m", rgb: [46, 125, 50] }, @@ -52,7 +54,12 @@ export function fuelsTable({ deadMoisture, liveMoisture, depth }) { const cells = FUEL_ROWS[f.index].split(";"); cells[col("Md")] = String(deadMoisture); cells[col("Ml")] = String(liveMoisture); - if (depth[f.index] !== undefined) cells[col("e")] = String(depth[f.index]); + if (depth[f.index] !== undefined) { + cells[col("e")] = String(depth[f.index]); + cells[col("depth")] = String( + (+cells[col("depth")] * depth[f.index]) / FUEL_DEFAULTS.depth[f.index] + ); + } return cells.join(";"); }); return [FUEL_HEADER, ...rows].join("\n"); diff --git a/bindings/wasm/worker.mjs b/bindings/wasm/worker.mjs index cf4cb4c3..d8b71978 100644 --- a/bindings/wasm/worker.mjs +++ b/bindings/wasm/worker.mjs @@ -42,6 +42,17 @@ function build(land, model, solver) { ff.setString("fuelsTable", land.fuelsTable); ff.setDouble("defaultFuelType", land.defaultFuel); for (const [key, value] of Object.entries(solver)) ff.setDouble(key, value); + if (solver.windReductionFactor !== undefined) + ff.setDouble("Farsite.adjustementWind", solver.windReductionFactor); + if (land.deadMoisture !== undefined) { + ff.setDouble("moistures.ones", land.deadMoisture); + ff.setDouble("moistures.tens", land.deadMoisture); + ff.setDouble("moistures.hundreds", land.deadMoisture); + } + if (land.liveMoisture !== undefined) { + ff.setDouble("moistures.liveh", land.liveMoisture); + ff.setDouble("moistures.livew", land.liveMoisture); + } ff.setString("propagationModel", model); ff.execute(`FireDomain[sw=(0,0,0);ne=(${side},${side},0);t=0]`);