malcolmSQ commited on
Commit ·
560f9fd
1
Parent(s): 801d73b
Add support for continuous declining increment growth; use display names for species in dashboard
Browse files- configs/declining_increment.yaml +1 -0
- dashboard/app.py +24 -7
- src/er_model.py +7 -0
configs/declining_increment.yaml
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
growth_model: declining_increment
|
|
|
|
| 2 |
|
| 3 |
species:
|
| 4 |
- name: "species_A" # Rhizophora spp.
|
|
|
|
| 1 |
growth_model: declining_increment
|
| 2 |
+
continuous_growth: true
|
| 3 |
|
| 4 |
species:
|
| 5 |
- name: "species_A" # Rhizophora spp.
|
dashboard/app.py
CHANGED
|
@@ -25,6 +25,12 @@ MODEL_CONFIGS = {
|
|
| 25 |
"Declining Increment": "configs/declining_increment.yaml"
|
| 26 |
}
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
# Helper to update planting schedule in a config file
|
| 29 |
def update_planting_schedule(config_path, year_areas):
|
| 30 |
with open(config_path) as f:
|
|
@@ -39,7 +45,7 @@ def create_survival_table(model):
|
|
| 39 |
"""
|
| 40 |
years = range(1, model.project.duration_years + 1)
|
| 41 |
data = {"Year": []}
|
| 42 |
-
species_names = [s.name for s in model.species]
|
| 43 |
for name in species_names:
|
| 44 |
data[name] = []
|
| 45 |
data["Total Surviving Trees"] = []
|
|
@@ -113,8 +119,9 @@ def create_growth_increment_plots(config, model_type=None):
|
|
| 113 |
years = np.arange(1, config["project"]["duration_years"] + 1)
|
| 114 |
fig = make_subplots(rows=2, cols=2, subplot_titles=("DBH Growth", "HEIGHT Growth", "DBH Annual Increment", "HEIGHT Annual Increment"))
|
| 115 |
from src.er_model import ERModel
|
|
|
|
| 116 |
for sp in config["species"]:
|
| 117 |
-
name = sp["name"]
|
| 118 |
initial_dbh = sp["initial_values"]["dbh"]
|
| 119 |
initial_height = sp["initial_values"]["height"]
|
| 120 |
if model_type == "linear":
|
|
@@ -130,8 +137,12 @@ def create_growth_increment_plots(config, model_type=None):
|
|
| 130 |
elif model_type == "declining_increment":
|
| 131 |
dbh_params = sp["declining_increment"]["dbh"]
|
| 132 |
height_params = sp["declining_increment"]["height"]
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
else: # Default to Chapman-Richards
|
| 136 |
dbh_params = sp["chapman_richards"]["dbh"]
|
| 137 |
height_params = sp["chapman_richards"]["height"]
|
|
@@ -188,7 +199,7 @@ def create_all_plots(results, species_results, config):
|
|
| 188 |
from src.er_model import ERModel
|
| 189 |
growth_model = config.get('growth_model', 'chapman_richards')
|
| 190 |
for sp in config["species"]:
|
| 191 |
-
name = sp["name"]
|
| 192 |
initial_dbh = sp["initial_values"]["dbh"]
|
| 193 |
initial_height = sp["initial_values"]["height"]
|
| 194 |
if growth_model == "linear":
|
|
@@ -251,14 +262,14 @@ def create_summary(results, species_results, config, model):
|
|
| 251 |
for m in milestones:
|
| 252 |
if m <= years:
|
| 253 |
surv = model.calculate_total_surviving_trees(m)
|
| 254 |
-
surv_str = ", ".join([f"{k}: {v:,.0f}" for k, v in surv.items()])
|
| 255 |
survival_lines.append(f"Year {m} surviving trees: {surv_str}")
|
| 256 |
# Per hectare (final year)
|
| 257 |
surv_30 = model.calculate_total_surviving_trees(years)
|
| 258 |
per_ha_lines = []
|
| 259 |
for k, v in surv_30.items():
|
| 260 |
per_ha = v / total_area if total_area > 0 else 0
|
| 261 |
-
per_ha_lines.append(f"{k}: {per_ha:,.0f} trees/ha")
|
| 262 |
summary = (
|
| 263 |
f"Project Overview:\n----------------\nDuration: {years} years\nTotal Area Planted: {total_area:,.0f} ha\nBuffer Pool: {config['carbon']['buffer_percentage']}%\n\n"
|
| 264 |
f"Carbon Sequestration:\n-------------------\nTotal Gross Carbon (Year {years}): {final_gross:,.0f} tCO2\nTotal Net Carbon (Year {years}): {final_net:,.0f} tCO2\nAverage Annual Gross: {avg_annual_gross:,.0f} tCO2/yr\nAverage Annual Net: {avg_annual_net:,.0f} tCO2/yr\n\n"
|
|
@@ -275,6 +286,12 @@ with gr.Blocks() as demo:
|
|
| 275 |
gr.Markdown("# Mangrove ER Model Dashboard\nDeclining Increment Model Only")
|
| 276 |
with gr.Tabs():
|
| 277 |
with gr.Tab("Declining Increment"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
gr.Markdown("## Planting Schedule (ha per year)")
|
| 279 |
year_1 = gr.Number(value=2500, label="Year 1 Area (ha)")
|
| 280 |
year_2 = gr.Number(value=2500, label="Year 2 Area (ha)")
|
|
|
|
| 25 |
"Declining Increment": "configs/declining_increment.yaml"
|
| 26 |
}
|
| 27 |
|
| 28 |
+
# Add a mapping for display names
|
| 29 |
+
SPECIES_DISPLAY_NAMES = {
|
| 30 |
+
'species_A': 'Rhizophora spp.',
|
| 31 |
+
'species_B': 'Avicennia germinans'
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
# Helper to update planting schedule in a config file
|
| 35 |
def update_planting_schedule(config_path, year_areas):
|
| 36 |
with open(config_path) as f:
|
|
|
|
| 45 |
"""
|
| 46 |
years = range(1, model.project.duration_years + 1)
|
| 47 |
data = {"Year": []}
|
| 48 |
+
species_names = [SPECIES_DISPLAY_NAMES.get(s.name, s.name) for s in model.species]
|
| 49 |
for name in species_names:
|
| 50 |
data[name] = []
|
| 51 |
data["Total Surviving Trees"] = []
|
|
|
|
| 119 |
years = np.arange(1, config["project"]["duration_years"] + 1)
|
| 120 |
fig = make_subplots(rows=2, cols=2, subplot_titles=("DBH Growth", "HEIGHT Growth", "DBH Annual Increment", "HEIGHT Annual Increment"))
|
| 121 |
from src.er_model import ERModel
|
| 122 |
+
use_continuous = config.get('continuous_growth', False)
|
| 123 |
for sp in config["species"]:
|
| 124 |
+
name = SPECIES_DISPLAY_NAMES.get(sp["name"], sp["name"])
|
| 125 |
initial_dbh = sp["initial_values"]["dbh"]
|
| 126 |
initial_height = sp["initial_values"]["height"]
|
| 127 |
if model_type == "linear":
|
|
|
|
| 137 |
elif model_type == "declining_increment":
|
| 138 |
dbh_params = sp["declining_increment"]["dbh"]
|
| 139 |
height_params = sp["declining_increment"]["height"]
|
| 140 |
+
if use_continuous:
|
| 141 |
+
dbh = [ERModel.continuous_declining_increment_growth(t, dbh_params, initial_dbh) for t in years]
|
| 142 |
+
height = [ERModel.continuous_declining_increment_growth(t, height_params, initial_height) for t in years]
|
| 143 |
+
else:
|
| 144 |
+
dbh = [ERModel.declining_increment_growth(t, dbh_params, initial_dbh) for t in years]
|
| 145 |
+
height = [ERModel.declining_increment_growth(t, height_params, initial_height) for t in years]
|
| 146 |
else: # Default to Chapman-Richards
|
| 147 |
dbh_params = sp["chapman_richards"]["dbh"]
|
| 148 |
height_params = sp["chapman_richards"]["height"]
|
|
|
|
| 199 |
from src.er_model import ERModel
|
| 200 |
growth_model = config.get('growth_model', 'chapman_richards')
|
| 201 |
for sp in config["species"]:
|
| 202 |
+
name = SPECIES_DISPLAY_NAMES.get(sp["name"], sp["name"])
|
| 203 |
initial_dbh = sp["initial_values"]["dbh"]
|
| 204 |
initial_height = sp["initial_values"]["height"]
|
| 205 |
if growth_model == "linear":
|
|
|
|
| 262 |
for m in milestones:
|
| 263 |
if m <= years:
|
| 264 |
surv = model.calculate_total_surviving_trees(m)
|
| 265 |
+
surv_str = ", ".join([f"{SPECIES_DISPLAY_NAMES.get(k, k)}: {v:,.0f}" for k, v in surv.items()])
|
| 266 |
survival_lines.append(f"Year {m} surviving trees: {surv_str}")
|
| 267 |
# Per hectare (final year)
|
| 268 |
surv_30 = model.calculate_total_surviving_trees(years)
|
| 269 |
per_ha_lines = []
|
| 270 |
for k, v in surv_30.items():
|
| 271 |
per_ha = v / total_area if total_area > 0 else 0
|
| 272 |
+
per_ha_lines.append(f"{SPECIES_DISPLAY_NAMES.get(k, k)}: {per_ha:,.0f} trees/ha")
|
| 273 |
summary = (
|
| 274 |
f"Project Overview:\n----------------\nDuration: {years} years\nTotal Area Planted: {total_area:,.0f} ha\nBuffer Pool: {config['carbon']['buffer_percentage']}%\n\n"
|
| 275 |
f"Carbon Sequestration:\n-------------------\nTotal Gross Carbon (Year {years}): {final_gross:,.0f} tCO2\nTotal Net Carbon (Year {years}): {final_net:,.0f} tCO2\nAverage Annual Gross: {avg_annual_gross:,.0f} tCO2/yr\nAverage Annual Net: {avg_annual_net:,.0f} tCO2/yr\n\n"
|
|
|
|
| 286 |
gr.Markdown("# Mangrove ER Model Dashboard\nDeclining Increment Model Only")
|
| 287 |
with gr.Tabs():
|
| 288 |
with gr.Tab("Declining Increment"):
|
| 289 |
+
config = yaml.safe_load(open(MODEL_CONFIGS["Declining Increment"]))
|
| 290 |
+
use_continuous = config.get('continuous_growth', False)
|
| 291 |
+
if use_continuous:
|
| 292 |
+
gr.Markdown("**Mode:** Continuous declining increment growth (analytical formula)")
|
| 293 |
+
else:
|
| 294 |
+
gr.Markdown("**Mode:** Discrete declining increment growth (sum of annual increments)")
|
| 295 |
gr.Markdown("## Planting Schedule (ha per year)")
|
| 296 |
year_1 = gr.Number(value=2500, label="Year 1 Area (ha)")
|
| 297 |
year_2 = gr.Number(value=2500, label="Year 2 Area (ha)")
|
src/er_model.py
CHANGED
|
@@ -203,6 +203,13 @@ class ERModel:
|
|
| 203 |
total += frac * increment
|
| 204 |
return total
|
| 205 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
def calculate_carbon_for_species(self, species: Species, age: int, area: float) -> float:
|
| 207 |
"""
|
| 208 |
Calculate carbon sequestration for a single species and age.
|
|
|
|
| 203 |
total += frac * increment
|
| 204 |
return total
|
| 205 |
|
| 206 |
+
@staticmethod
|
| 207 |
+
def continuous_declining_increment_growth(age: float, params: Dict[str, float], initial_value: float) -> float:
|
| 208 |
+
r0 = params["r0"]
|
| 209 |
+
T_m = params["T_m"]
|
| 210 |
+
# Continuous formula: initial + r0 * (age - age^2/(2*Tm))
|
| 211 |
+
return initial_value + r0 * (age - age**2 / (2 * T_m))
|
| 212 |
+
|
| 213 |
def calculate_carbon_for_species(self, species: Species, age: int, area: float) -> float:
|
| 214 |
"""
|
| 215 |
Calculate carbon sequestration for a single species and age.
|