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 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
- dbh = [ERModel.declining_increment_growth(t, dbh_params, initial_dbh) for t in years]
134
- height = [ERModel.declining_increment_growth(t, height_params, initial_height) for t in years]
 
 
 
 
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.