malcolmSQ commited on
Commit
43df71b
·
1 Parent(s): ce27302

UI improvements: move Key Metrics, update plot color, modularize cumulative soil carbon

Browse files
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
3
+ TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
4
+ cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
5
+ WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
6
+ ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
7
+ MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
8
+ h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
9
+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
10
+ A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
11
+ T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
12
+ B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
13
+ B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
14
+ KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
15
+ OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
16
+ jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
17
+ qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
18
+ rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
19
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
20
+ hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
21
+ ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
22
+ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
23
+ NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
24
+ ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
25
+ TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
26
+ jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
27
+ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
28
+ 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
29
+ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
30
+ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
31
+ -----END CERTIFICATE-----
dashboard/app.py CHANGED
@@ -16,7 +16,7 @@ import warnings
16
  import traceback
17
  from src.allometry import calculate_biomass
18
  from dashboard.plots import check_complex, create_growth_increment_plots, create_all_plots
19
- from dashboard.tables import create_survival_table, create_biomass_table
20
  import yaml
21
  import tempfile
22
 
@@ -65,12 +65,9 @@ def run_model_from_config(config):
65
  plots = create_all_plots(results, species_results, config)
66
  summary = create_summary(results, species_results, config, model)
67
  survival_table = create_survival_table(model)
68
- # Format tables
69
- results_fmt = results.copy()
70
  species_results_fmt = species_results.copy()
71
- for col in results_fmt.columns:
72
- if results_fmt[col].dtype in [float, int]:
73
- results_fmt[col] = results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
74
  for col in species_results_fmt.columns:
75
  if species_results_fmt[col].dtype in [float, int]:
76
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
@@ -84,12 +81,9 @@ def run_model(config_path):
84
  plots = create_all_plots(results, species_results, config)
85
  summary = create_summary(results, species_results, config, model)
86
  survival_table = create_survival_table(model)
87
- # Format tables
88
- results_fmt = results.copy()
89
  species_results_fmt = species_results.copy()
90
- for col in results_fmt.columns:
91
- if results_fmt[col].dtype in [float, int]:
92
- results_fmt[col] = results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
93
  for col in species_results_fmt.columns:
94
  if species_results_fmt[col].dtype in [float, int]:
95
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
@@ -98,32 +92,32 @@ def run_model(config_path):
98
  def create_summary(results, species_results, config, model):
99
  total_area = sum(config["project"]["planting_schedule"].values())
100
  final_gross = results["gross_carbon"].iloc[-1]
101
- final_net = results["net_carbon"].iloc[-1]
102
  # Sum soil carbon over all years
103
  total_soil_carbon = results["soil_carbon"].sum()
104
  final_gross_soil = final_gross + total_soil_carbon
105
- final_net_soil = final_net + total_soil_carbon
106
  years = len(results)
107
  gross_carbon_per_ha = final_gross / total_area if total_area > 0 else 0
108
- net_carbon_per_ha = final_net / total_area if total_area > 0 else 0
109
  gross_carbon_per_ha_soil = final_gross_soil / total_area if total_area > 0 else 0
110
- net_carbon_per_ha_soil = final_net_soil / total_area if total_area > 0 else 0
111
- annual_net_per_ha = net_carbon_per_ha / years if years > 0 else 0
112
- annual_net_per_ha_soil = net_carbon_per_ha_soil / years if years > 0 else 0
113
  soil_carbon_val = config["carbon"].get("soil_carbon_per_ha_per_year", 0)
114
  # Milestone years
115
  def get_val(col, idx):
116
  return results[col].iloc[idx] if len(results) > idx else 0
117
  year_5_gross = get_val("gross_carbon", 4)
118
- year_5_net = get_val("net_carbon", 4)
119
  year_10_gross = get_val("gross_carbon", 9)
120
- year_10_net = get_val("net_carbon", 9)
121
  year_20_gross = get_val("gross_carbon", 19)
122
- year_20_net = get_val("net_carbon", 19)
123
  avg_annual_gross = final_gross / years if years > 0 else 0
124
- avg_annual_net = final_net / years if years > 0 else 0
125
  avg_annual_gross_soil = final_gross_soil / years if years > 0 else 0
126
- avg_annual_net_soil = final_net_soil / years if years > 0 else 0
127
  # Surviving trees at milestones
128
  milestones = [5, 10, 20, 30]
129
  survival_lines = []
@@ -141,14 +135,14 @@ def create_summary(results, species_results, config, model):
141
  summary = (
142
  f"Project Overview:\n----------------\nDuration: {years} years\nTotal Area Planted: {total_area:,.0f} ha\nBuffer Pool: {config['carbon']['buffer_percentage']}%\n\n"
143
  f"Soil Carbon:\n-----------\nSoil carbon added: {soil_carbon_val} tCO2/ha/year\nTotal soil carbon added over project: {total_soil_carbon:,.0f} tCO2\n\n"
144
- f"Carbon Sequestration (Biomass Only):\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"
145
- f"Carbon Sequestration (With Soil Carbon):\n-------------------\nTotal Gross Carbon (Year {years}): {final_gross_soil:,.0f} tCO2\nTotal Net Carbon (Year {years}): {final_net_soil:,.0f} tCO2\nAverage Annual Gross: {avg_annual_gross_soil:,.0f} tCO2/yr\nAverage Annual Net: {avg_annual_net_soil:,.0f} tCO2/yr\n\n"
146
- f"Milestone Years (Biomass Only):\n--------------\nYear 5 Gross: {year_5_gross:,.0f} tCO2\nYear 5 Net: {year_5_net:,.0f} tCO2\nYear 10 Gross: {year_10_gross:,.0f} tCO2\nYear 10 Net: {year_10_net:,.0f} tCO2\nYear 20 Gross: {year_20_gross:,.0f} tCO2\nYear 20 Net: {year_20_net:,.0f} tCO2\n\n"
147
  "Surviving Trees (Milestones):\n----------------------------\n"
148
  + "\n".join(survival_lines)
149
  + "\n\nPer Hectare Surviving Trees (Final Year):\n----------------------------------------\n"
150
  + "\n".join(per_ha_lines)
151
- + f"\n\nPer Hectare Metrics (Year {years}):\n-----------------------------\nGross Carbon per ha (biomass only): {gross_carbon_per_ha:,.0f} tCO2/ha\nNet Carbon per ha (biomass only): {net_carbon_per_ha:,.0f} tCO2/ha\nGross Carbon per ha (with soil): {gross_carbon_per_ha_soil:,.0f} tCO2/ha\nNet Carbon per ha (with soil): {net_carbon_per_ha_soil:,.0f} tCO2/ha\nAverage Annual Net per ha (biomass only): {annual_net_per_ha:,.2f} tCO2/ha/yr\nAverage Annual Net per ha (with soil): {annual_net_per_ha_soil:,.2f} tCO2/ha/yr\n"
152
  )
153
  return summary
154
 
@@ -157,6 +151,7 @@ def hex_to_rgba(hex_color, alpha):
157
  r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
158
  return f'rgba({r},{g},{b},{alpha})'
159
 
 
160
  with gr.Blocks() as demo:
161
  gr.Markdown("# Mangrove ER Model Dashboard\nDeclining Increment Model Only")
162
  with gr.Tabs():
@@ -168,25 +163,33 @@ with gr.Blocks() as demo:
168
  else:
169
  gr.Markdown("**Mode:** Discrete declining increment growth (sum of annual increments)")
170
  gr.Markdown("## Planting Schedule (ha per year)")
171
- year_1 = gr.Number(value=2500, label="Year 1 Area (ha)")
172
- year_2 = gr.Number(value=2500, label="Year 2 Area (ha)")
173
- year_3 = gr.Number(value=0, label="Year 3 Area (ha)")
174
- year_4 = gr.Number(value=0, label="Year 4 Area (ha)")
175
- year_5 = gr.Number(value=0, label="Year 5 Area (ha)")
176
  update_btn = gr.Button("Update Results", variant="primary")
177
  with gr.Row():
178
  carbon_plot = gr.Plot()
179
  biomass_plot = gr.Plot()
180
  annual_plot = gr.Plot()
181
- # Add new growth/increment curves plot
182
  growth_plot = gr.Plot(label="Growth & Increment Curves (DBH/Height)")
183
  summary_box = gr.Textbox(label="Summary", lines=12)
184
- results_box = gr.Dataframe(label="Project Results (Annual)")
185
- species_box = gr.Dataframe(label="Species Results (Annual)")
186
- survival_box = gr.Dataframe(label="Surviving Trees Table")
187
- # Replace size_table with biomass debug table
188
- biomass_debug_table = gr.Dataframe(label="Biomass Table (inputs & outputs per year)")
189
-
 
 
 
 
 
 
 
 
 
190
  def update_declining_increment(y1, y2, y3, y4, y5):
191
  config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
192
  with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
@@ -199,24 +202,25 @@ with gr.Blocks() as demo:
199
  survival_table = create_survival_table(model)
200
  growth_fig = create_growth_increment_plots(config, model_type="declining_increment")
201
  biomass_debug_df = create_biomass_table(config)
202
- results_fmt = results.copy()
203
  species_results_fmt = species_results.copy()
204
- for col in results_fmt.columns:
205
- if results_fmt[col].dtype in [float, int]:
206
- results_fmt[col] = results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
207
  for col in species_results_fmt.columns:
208
  if species_results_fmt[col].dtype in [float, int]:
209
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
210
- return plots[0], plots[1], plots[2], growth_fig, summary, results_fmt, species_results_fmt, survival_table, biomass_debug_df
211
-
 
 
 
 
 
212
  update_btn.click(
213
  update_declining_increment,
214
  inputs=[year_1, year_2, year_3, year_4, year_5],
215
- outputs=[carbon_plot, biomass_plot, annual_plot, growth_plot, summary_box, results_box, species_box, survival_box, biomass_debug_table]
216
  )
217
-
218
  # Show initial results
219
- c, b, a, g, summary, r, s, surv, biomass_debug_df = update_declining_increment(2500, 2500, 0, 0, 0)
220
  carbon_plot.value = c
221
  biomass_plot.value = b
222
  annual_plot.value = a
@@ -226,4 +230,5 @@ with gr.Blocks() as demo:
226
  species_box.value = s
227
  survival_box.value = surv
228
  biomass_debug_table.value = biomass_debug_df
 
229
  demo.launch(share=True)
 
16
  import traceback
17
  from src.allometry import calculate_biomass
18
  from dashboard.plots import check_complex, create_growth_increment_plots, create_all_plots
19
+ from dashboard.tables import create_survival_table, create_biomass_table, create_project_results_table
20
  import yaml
21
  import tempfile
22
 
 
65
  plots = create_all_plots(results, species_results, config)
66
  summary = create_summary(results, species_results, config, model)
67
  survival_table = create_survival_table(model)
68
+ # Use modularized table formatting
69
+ results_fmt = create_project_results_table(results)
70
  species_results_fmt = species_results.copy()
 
 
 
71
  for col in species_results_fmt.columns:
72
  if species_results_fmt[col].dtype in [float, int]:
73
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
 
81
  plots = create_all_plots(results, species_results, config)
82
  summary = create_summary(results, species_results, config, model)
83
  survival_table = create_survival_table(model)
84
+ # Use modularized table formatting
85
+ results_fmt = create_project_results_table(results)
86
  species_results_fmt = species_results.copy()
 
 
 
87
  for col in species_results_fmt.columns:
88
  if species_results_fmt[col].dtype in [float, int]:
89
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
 
92
  def create_summary(results, species_results, config, model):
93
  total_area = sum(config["project"]["planting_schedule"].values())
94
  final_gross = results["gross_carbon"].iloc[-1]
95
+ final_buffer = results["buffer_carbon"].iloc[-1]
96
  # Sum soil carbon over all years
97
  total_soil_carbon = results["soil_carbon"].sum()
98
  final_gross_soil = final_gross + total_soil_carbon
99
+ final_buffer_soil = final_buffer + total_soil_carbon
100
  years = len(results)
101
  gross_carbon_per_ha = final_gross / total_area if total_area > 0 else 0
102
+ buffer_carbon_per_ha = final_buffer / total_area if total_area > 0 else 0
103
  gross_carbon_per_ha_soil = final_gross_soil / total_area if total_area > 0 else 0
104
+ buffer_carbon_per_ha_soil = final_buffer_soil / total_area if total_area > 0 else 0
105
+ annual_buffer_per_ha = buffer_carbon_per_ha / years if years > 0 else 0
106
+ annual_buffer_per_ha_soil = buffer_carbon_per_ha_soil / years if years > 0 else 0
107
  soil_carbon_val = config["carbon"].get("soil_carbon_per_ha_per_year", 0)
108
  # Milestone years
109
  def get_val(col, idx):
110
  return results[col].iloc[idx] if len(results) > idx else 0
111
  year_5_gross = get_val("gross_carbon", 4)
112
+ year_5_buffer = get_val("buffer_carbon", 4)
113
  year_10_gross = get_val("gross_carbon", 9)
114
+ year_10_buffer = get_val("buffer_carbon", 9)
115
  year_20_gross = get_val("gross_carbon", 19)
116
+ year_20_buffer = get_val("buffer_carbon", 19)
117
  avg_annual_gross = final_gross / years if years > 0 else 0
118
+ avg_annual_buffer = final_buffer / years if years > 0 else 0
119
  avg_annual_gross_soil = final_gross_soil / years if years > 0 else 0
120
+ avg_annual_buffer_soil = final_buffer_soil / years if years > 0 else 0
121
  # Surviving trees at milestones
122
  milestones = [5, 10, 20, 30]
123
  survival_lines = []
 
135
  summary = (
136
  f"Project Overview:\n----------------\nDuration: {years} years\nTotal Area Planted: {total_area:,.0f} ha\nBuffer Pool: {config['carbon']['buffer_percentage']}%\n\n"
137
  f"Soil Carbon:\n-----------\nSoil carbon added: {soil_carbon_val} tCO2/ha/year\nTotal soil carbon added over project: {total_soil_carbon:,.0f} tCO2\n\n"
138
+ f"Carbon Sequestration (Biomass Only):\n-------------------\nTotal Gross Carbon (Year {years}): {final_gross:,.0f} tCO2\nTotal Buffer Carbon (Year {years}): {final_buffer:,.0f} tCO2\nAverage Annual Gross: {avg_annual_gross:,.0f} tCO2/yr\nAverage Annual Buffer: {avg_annual_buffer:,.0f} tCO2/yr\n\n"
139
+ f"Carbon Sequestration (With Soil Carbon):\n-------------------\nTotal Gross Carbon (Year {years}): {final_gross_soil:,.0f} tCO2\nTotal Buffer Carbon (Year {years}): {final_buffer_soil:,.0f} tCO2\nAverage Annual Gross: {avg_annual_gross_soil:,.0f} tCO2/yr\nAverage Annual Buffer: {avg_annual_buffer_soil:,.0f} tCO2/yr\n\n"
140
+ f"Milestone Years (Biomass Only):\n--------------\nYear 5 Gross: {year_5_gross:,.0f} tCO2\nYear 5 Buffer: {year_5_buffer:,.0f} tCO2\nYear 10 Gross: {year_10_gross:,.0f} tCO2\nYear 10 Buffer: {year_10_buffer:,.0f} tCO2\nYear 20 Gross: {year_20_gross:,.0f} tCO2\nYear 20 Buffer: {year_20_buffer:,.0f} tCO2\n\n"
141
  "Surviving Trees (Milestones):\n----------------------------\n"
142
  + "\n".join(survival_lines)
143
  + "\n\nPer Hectare Surviving Trees (Final Year):\n----------------------------------------\n"
144
  + "\n".join(per_ha_lines)
145
+ + f"\n\nPer Hectare Metrics (Year {years}):\n-----------------------------\nGross Carbon per ha (biomass only): {gross_carbon_per_ha:,.0f} tCO2/ha\nBuffer Carbon per ha (biomass only): {buffer_carbon_per_ha:,.0f} tCO2/ha\nGross Carbon per ha (with soil): {gross_carbon_per_ha_soil:,.0f} tCO2/ha\nBuffer Carbon per ha (with soil): {buffer_carbon_per_ha_soil:,.0f} tCO2/ha\nAverage Annual Buffer per ha (biomass only): {annual_buffer_per_ha:,.2f} tCO2/ha/yr\nAverage Annual Buffer per ha (with soil): {annual_buffer_per_ha_soil:,.2f} tCO2/ha/yr\n"
146
  )
147
  return summary
148
 
 
151
  r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
152
  return f'rgba({r},{g},{b},{alpha})'
153
 
154
+ # Gradio UI code starts here
155
  with gr.Blocks() as demo:
156
  gr.Markdown("# Mangrove ER Model Dashboard\nDeclining Increment Model Only")
157
  with gr.Tabs():
 
163
  else:
164
  gr.Markdown("**Mode:** Discrete declining increment growth (sum of annual increments)")
165
  gr.Markdown("## Planting Schedule (ha per year)")
166
+ year_1 = gr.Number(value=2500, label="Year 1 Area (ha)", info="Enter the area to be planted in year 1 (ha)")
167
+ year_2 = gr.Number(value=2500, label="Year 2 Area (ha)", info="Enter the area to be planted in year 2 (ha)")
168
+ year_3 = gr.Number(value=0, label="Year 3 Area (ha)", info="Enter the area to be planted in year 3 (ha)")
169
+ year_4 = gr.Number(value=0, label="Year 4 Area (ha)", info="Enter the area to be planted in year 4 (ha)")
170
+ year_5 = gr.Number(value=0, label="Year 5 Area (ha)", info="Enter the area to be planted in year 5 (ha)")
171
  update_btn = gr.Button("Update Results", variant="primary")
172
  with gr.Row():
173
  carbon_plot = gr.Plot()
174
  biomass_plot = gr.Plot()
175
  annual_plot = gr.Plot()
 
176
  growth_plot = gr.Plot(label="Growth & Increment Curves (DBH/Height)")
177
  summary_box = gr.Textbox(label="Summary", lines=12)
178
+ key_metrics = gr.Markdown("""\
179
+ **Key Metrics**
180
+ - Total Area Planted: ... ha\n- Total Buffer Carbon (final year): ... tCO2\n- Average Annual Buffer Carbon: ... tCO2/yr\n- Buffer Pool: ... %
181
+ """)
182
+ # --- Tabbed tables section ---
183
+ with gr.Tabs():
184
+ with gr.Tab("Project Results (Annual)"):
185
+ results_box = gr.Dataframe(label="Project Results (Annual)")
186
+ with gr.Tab("Species Results (Annual)"):
187
+ species_box = gr.Dataframe(label="Species Results (Annual)")
188
+ with gr.Tab("Surviving Trees Table"):
189
+ survival_box = gr.Dataframe(label="Surviving Trees Table")
190
+ with gr.Tab("Biomass Table"):
191
+ biomass_debug_table = gr.Dataframe(label="Biomass Table (inputs & outputs per year)")
192
+ # --- End tabbed tables ---
193
  def update_declining_increment(y1, y2, y3, y4, y5):
194
  config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
195
  with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
 
202
  survival_table = create_survival_table(model)
203
  growth_fig = create_growth_increment_plots(config, model_type="declining_increment")
204
  biomass_debug_df = create_biomass_table(config)
205
+ results_fmt = create_project_results_table(results)
206
  species_results_fmt = species_results.copy()
 
 
 
207
  for col in species_results_fmt.columns:
208
  if species_results_fmt[col].dtype in [float, int]:
209
  species_results_fmt[col] = species_results_fmt[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
210
+ # Key metrics for summary card
211
+ total_area = sum(config["project"]["planting_schedule"].values())
212
+ final_buffer = results["buffer_carbon"].iloc[-1]
213
+ avg_annual_buffer = final_buffer / len(results) if len(results) > 0 else 0
214
+ buffer_pool = config["carbon"]["buffer_percentage"]
215
+ key_metrics_md = f"""**Key Metrics**\n- Total Area Planted: {total_area:,.0f} ha\n- Total Buffer Carbon (final year): {final_buffer:,.0f} tCO2\n- Average Annual Buffer Carbon: {avg_annual_buffer:,.0f} tCO2/yr\n- Buffer Pool: {buffer_pool}%"""
216
+ return (plots[0], plots[1], plots[2], growth_fig, summary, results_fmt, species_results_fmt, survival_table, biomass_debug_df, key_metrics_md)
217
  update_btn.click(
218
  update_declining_increment,
219
  inputs=[year_1, year_2, year_3, year_4, year_5],
220
+ outputs=[carbon_plot, biomass_plot, annual_plot, growth_plot, summary_box, results_box, species_box, survival_box, biomass_debug_table, key_metrics]
221
  )
 
222
  # Show initial results
223
+ c, b, a, g, summary, r, s, surv, biomass_debug_df, key_metrics_md = update_declining_increment(2500, 2500, 0, 0, 0)
224
  carbon_plot.value = c
225
  biomass_plot.value = b
226
  annual_plot.value = a
 
230
  species_box.value = s
231
  survival_box.value = surv
232
  biomass_debug_table.value = biomass_debug_df
233
+ key_metrics.value = key_metrics_md
234
  demo.launch(share=True)
dashboard/plots/__init__.py CHANGED
@@ -81,29 +81,29 @@ def create_growth_increment_plots(config, model_type=None):
81
 
82
  def create_all_plots(results, species_results, config):
83
  # Diagnostic: Check for complex numbers in results DataFrame columns used for plotting
84
- for col in ["gross_carbon", "net_carbon"]:
85
  arr = np.array(results[col])
86
  check_complex(arr, f"results['{col}']")
87
- # 1. Carbon curve (gross and net, with and without soil)
88
  fig1 = go.Figure()
89
  fig1.add_trace(go.Scatter(x=results["year"], y=results["gross_carbon"], mode="lines+markers", name="Gross Carbon", line=dict(width=2, color="blue")))
90
- fig1.add_trace(go.Scatter(x=results["year"], y=results["net_carbon"], mode="lines+markers", name="Net Carbon", line=dict(width=2, color="red")))
91
  if "gross_carbon_with_soil" in results.columns:
92
  # For backward compatibility, but we now sum soil carbon in summary, so recalc here for plot
93
  soil_cumsum = results["soil_carbon"].cumsum()
94
  gross_with_soil = results["gross_carbon"] + soil_cumsum
95
- net_with_soil = results["net_carbon"] + soil_cumsum
96
  fig1.add_trace(go.Scatter(x=results["year"], y=gross_with_soil, mode="lines+markers", name="Gross Carbon (with Soil)", line=dict(width=2, dash="dot", color="green")))
97
- fig1.add_trace(go.Scatter(x=results["year"], y=net_with_soil, mode="lines+markers", name="Net Carbon (with Soil)", line=dict(width=2, dash="dot", color="orange")))
98
  fig1.update_layout(title="Carbon Sequestration Over Time", xaxis_title="Year", yaxis_title="Carbon (tCO2)", hovermode="x unified", template="plotly_white")
99
 
100
- # 2. Annual carbon (ERs)
101
- annual_ers = results["net_carbon"].diff().fillna(results["net_carbon"].iloc[0])
102
  arr = np.array(annual_ers)
103
  check_complex(arr, "annual_ers")
104
  fig2 = go.Figure()
105
- fig2.add_trace(go.Bar(x=results["year"], y=annual_ers, name="Annual ERs", marker_color="#2ecc71", opacity=0.7))
106
- fig2.update_layout(title="Annual Emission Reductions", xaxis_title="Year", yaxis_title="Annual ERs (tCO2)", hovermode="x unified", template="plotly_white")
107
 
108
  # 3. Biomass per tree for all species (refactored to use model.species_metrics)
109
  years = results["year"]
 
81
 
82
  def create_all_plots(results, species_results, config):
83
  # Diagnostic: Check for complex numbers in results DataFrame columns used for plotting
84
+ for col in ["gross_carbon", "buffer_carbon"]:
85
  arr = np.array(results[col])
86
  check_complex(arr, f"results['{col}']")
87
+ # 1. Carbon curve (gross and buffer, with and without soil)
88
  fig1 = go.Figure()
89
  fig1.add_trace(go.Scatter(x=results["year"], y=results["gross_carbon"], mode="lines+markers", name="Gross Carbon", line=dict(width=2, color="blue")))
90
+ fig1.add_trace(go.Scatter(x=results["year"], y=results["buffer_carbon"], mode="lines+markers", name="Buffer Carbon", line=dict(width=2, color="red")))
91
  if "gross_carbon_with_soil" in results.columns:
92
  # For backward compatibility, but we now sum soil carbon in summary, so recalc here for plot
93
  soil_cumsum = results["soil_carbon"].cumsum()
94
  gross_with_soil = results["gross_carbon"] + soil_cumsum
95
+ buffer_with_soil = results["buffer_carbon"] + soil_cumsum
96
  fig1.add_trace(go.Scatter(x=results["year"], y=gross_with_soil, mode="lines+markers", name="Gross Carbon (with Soil)", line=dict(width=2, dash="dot", color="green")))
97
+ fig1.add_trace(go.Scatter(x=results["year"], y=buffer_with_soil, mode="lines+markers", name="Buffer Carbon (with Soil)", line=dict(width=2, dash="dot", color="#43c6ac")))
98
  fig1.update_layout(title="Carbon Sequestration Over Time", xaxis_title="Year", yaxis_title="Carbon (tCO2)", hovermode="x unified", template="plotly_white")
99
 
100
+ # 2. Annual buffer carbon (ERs)
101
+ annual_ers = results["buffer_carbon"].diff().fillna(results["buffer_carbon"].iloc[0])
102
  arr = np.array(annual_ers)
103
  check_complex(arr, "annual_ers")
104
  fig2 = go.Figure()
105
+ fig2.add_trace(go.Bar(x=results["year"], y=annual_ers, name="Annual Buffer ERs", marker_color="#2ecc71", opacity=0.7))
106
+ fig2.update_layout(title="Annual Emission Reductions (Buffer)", xaxis_title="Year", yaxis_title="Annual Buffer ERs (tCO2)", hovermode="x unified", template="plotly_white")
107
 
108
  # 3. Biomass per tree for all species (refactored to use model.species_metrics)
109
  years = results["year"]
dashboard/tables/__init__.py CHANGED
@@ -66,4 +66,14 @@ def create_survival_table(model):
66
  for col in surv.columns:
67
  surv[col] = surv[col].apply(lambda x: f"{x:,.0f}")
68
  surv = surv.reset_index()
69
- return surv
 
 
 
 
 
 
 
 
 
 
 
66
  for col in surv.columns:
67
  surv[col] = surv[col].apply(lambda x: f"{x:,.0f}")
68
  surv = surv.reset_index()
69
+ return surv
70
+
71
+ def create_project_results_table(results: pd.DataFrame) -> pd.DataFrame:
72
+ df = results.copy()
73
+ # Replace soil_carbon with its cumulative sum
74
+ df['soil_carbon'] = df['soil_carbon'].cumsum()
75
+ # Optionally format numbers for display
76
+ for col in df.columns:
77
+ if df[col].dtype in [float, int]:
78
+ df[col] = df[col].apply(lambda x: f"{x:,.2f}" if isinstance(x, float) else f"{x:,}")
79
+ return df
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=3.41.2
2
+ pandas>=1.5.3
3
+ numpy>=1.23.0
4
+ plotly>=5.0.0
5
+ pyyaml
src/er_model.py CHANGED
@@ -314,21 +314,21 @@ class ERModel:
314
  species_year_results["Cumulative ha"] = cumulative_area
315
  species_year_results["tCO2/ha"] = total_carbon / cumulative_area if cumulative_area > 0 else 0
316
  gross_carbon = total_carbon
317
- net_carbon = gross_carbon * (1 - self.carbon.buffer_percentage / 100)
318
- net_carbon -= self.carbon.leakage_percentage / 100 * gross_carbon
319
- net_carbon -= self.carbon.baseline_emissions * cumulative_area
320
- # Add soil carbon if present
321
  soil_carbon = 0
322
  if hasattr(self.carbon, 'soil_carbon_per_ha_per_year'):
323
  soil_carbon = self.carbon.soil_carbon_per_ha_per_year * cumulative_area
324
  gross_carbon_with_soil = gross_carbon + soil_carbon
325
- net_carbon_with_soil = net_carbon + soil_carbon
326
  year_results.update({
327
  "gross_carbon": gross_carbon,
328
- "net_carbon": net_carbon,
329
  "cumulative_area": cumulative_area,
330
  "gross_carbon_with_soil": gross_carbon_with_soil,
331
- "net_carbon_with_soil": net_carbon_with_soil,
332
  "soil_carbon": soil_carbon
333
  })
334
  results.append(year_results)
 
314
  species_year_results["Cumulative ha"] = cumulative_area
315
  species_year_results["tCO2/ha"] = total_carbon / cumulative_area if cumulative_area > 0 else 0
316
  gross_carbon = total_carbon
317
+ buffer_carbon = gross_carbon * (1 - self.carbon.buffer_percentage / 100)
318
+ buffer_carbon -= self.carbon.leakage_percentage / 100 * gross_carbon
319
+ buffer_carbon -= self.carbon.baseline_emissions * cumulative_area
320
+ # Cumulative soil carbon: add 1 t/ha for every hectare ever planted, each year
321
  soil_carbon = 0
322
  if hasattr(self.carbon, 'soil_carbon_per_ha_per_year'):
323
  soil_carbon = self.carbon.soil_carbon_per_ha_per_year * cumulative_area
324
  gross_carbon_with_soil = gross_carbon + soil_carbon
325
+ buffer_carbon_with_soil = buffer_carbon + soil_carbon
326
  year_results.update({
327
  "gross_carbon": gross_carbon,
328
+ "buffer_carbon": buffer_carbon,
329
  "cumulative_area": cumulative_area,
330
  "gross_carbon_with_soil": gross_carbon_with_soil,
331
+ "buffer_carbon_with_soil": buffer_carbon_with_soil,
332
  "soil_carbon": soil_carbon
333
  })
334
  results.append(year_results)