malcolmSQ commited on
Commit
0dba6f7
·
1 Parent(s): 22579eb

Update dashboard with improved metrics and parameter handling - Fixed per hectare calculations, added milestone comparisons, improved summary stats, updated README

Browse files
Files changed (2) hide show
  1. README.md +70 -99
  2. dashboard/app.py +201 -186
README.md CHANGED
@@ -4,66 +4,57 @@ A Python implementation of the Emissions Reduction (ER) model for Nigeria mangro
4
 
5
  ## Recent Updates and Improvements
6
 
7
- ### Growth Model Enhancements (2024)
8
- - **Updated Allometric Equations**: Implemented Zanvo et al. (2023) species-specific equations:
 
 
 
 
 
 
9
  - Rhizophora: Total = 1.938 × (DBH² H)^0.67628
10
  - Avicennia: Total = 1.486 × (DBH² H)^0.55864
11
 
12
- - **Chapman-Richards Implementation**:
13
- - Improved growth parameter estimation
14
- - Direct use of growth rate parameter (b) instead of deriving from Tm
15
- - Separate parameterization for DBH and height growth
16
- - Current parameters:
17
- * Rhizophora: max DBH 11.07cm, max height 12.0m
18
- * Avicennia: max DBH 17.0cm, max height 8.8m
19
-
20
- - **Growth Rate Parameters**:
21
- - b = 0.25 yr^-1 (independently estimated growth rate)
22
- - c = 2.1 (shape parameter)
23
- - Removed Tm-based calculation (previously b = ln(c)/Tm)
24
-
25
  ### Dashboard Improvements
26
- - Added height growth visualization
27
- - Improved number formatting with comma separators
28
- - Enhanced scenario comparison features
29
- - Added biomass per tree tracking
30
- - Updated parameter input ranges
31
-
32
- ### Configuration Updates
33
- - Restructured parameter files for clarity
34
- - Added detailed parameter documentation
35
- - Improved error handling for invalid inputs
36
- - Added support for scenario analysis
37
-
38
- ## Features
39
-
40
- - Species-specific growth modeling using Chapman-Richards equations
41
- - Biomass estimation using allometric equations
42
- - Carbon conversion and emissions reduction calculations
43
- - Interactive dashboard for parameter exploration using Gradio
44
- - Configurable planting schedules and mortality rates
45
- - Scenario comparison for different DBH ranges and growth rates
46
- - Per-species carbon tracking and cumulative hectare calculations
47
 
48
  ## Model Parameters
49
 
50
  ### Species Parameters
51
- - **Rhizophora**
52
- - Initial values: DBH = 1.0cm, Height = 0.5m
53
- - Chapman-Richards parameters: a = 11.07cm, b = 0.35, c = 2.1
 
54
  - Configurable planting density and mortality rates
55
 
56
- - **Avicennia**
57
- - Initial values: DBH = 1.0cm, Height = 0.5m
58
- - Chapman-Richards parameters: a = 17.0cm, b = 0.35, c = 2.1
 
59
  - Configurable planting density and mortality rates
60
 
61
- ### Carbon Parameters
62
- - Biomass to carbon conversion factor
63
- - Carbon to CO2 conversion factor
64
- - Buffer pool percentage
65
- - Leakage percentage
66
- - Baseline emissions rate
67
 
68
  ## Installation
69
 
@@ -80,74 +71,54 @@ A Python implementation of the Emissions Reduction (ER) model for Nigeria mangro
80
  pre-commit install
81
  ```
82
 
83
- ## Project Structure
84
-
85
- - `src/` - Core model implementation
86
- - `er_model.py` - Main ER calculation logic
87
- - `allometry.py` - Species-specific allometric equations
88
- - `metrics.py` - Carbon conversion and statistics
89
- - `scripts/` - Command-line tools
90
- - `run_pipeline.py` - Execute full ER calculation workflow
91
- - `analyze_results.py` - Generate reports and visualizations
92
- - `dashboard/` - Gradio web application for interactive exploration
93
- - `configs/` - Default parameter files
94
- - `outputs/` - Generated results and figures
95
- - `tests/` - Test suite
96
-
97
- ## Dashboard Features
98
-
99
- The interactive Gradio dashboard allows users to:
100
- 1. Adjust species-specific parameters:
101
- - Planting density (trees/ha)
102
- - Year-by-year mortality rates
103
- 2. Configure planting schedule:
104
- - Area planted per year (ha)
105
- - Up to 5-year planting schedule
106
- 3. Modify carbon parameters:
107
- - Buffer pool percentage
108
- 4. Explore scenarios:
109
- - Test different DBH ranges
110
- - Adjust growth rate factors
111
- - Compare results across 1000 ha scenarios
112
- 5. View results:
113
- - Carbon sequestration over time
114
- - Milestone year comparisons
115
- - Per-species carbon tracking
116
- - Cumulative hectare metrics
117
- - Scenario comparison tables
118
-
119
  ## Usage
120
 
121
  1. Configure parameters in `configs/params.yaml`
122
 
123
- 2. Run the full pipeline:
124
- ```bash
125
- python scripts/run_pipeline.py --config configs/params.yaml
126
- ```
127
-
128
- 3. Launch the dashboard:
129
  ```bash
130
- python dashboard/app.py
131
  ```
132
  This will start a Gradio server and open the dashboard in your default web browser.
 
 
 
 
 
 
 
 
 
 
 
133
 
134
- ## Development
135
 
136
- - Code style is enforced using `black` and `isort`
137
- - Type hints are required for all public functions
138
- - Tests are written using `pytest`
139
- - Pre-commit hooks ensure code quality
 
 
 
140
 
141
  ## Dependencies
142
 
143
  Key dependencies (specified in `environment.yml`):
144
- - numpy=1.24.3
145
- - pandas=2.0.3
146
- - gradio (latest version)
147
  - matplotlib
148
  - pyyaml
149
  - pytest (for development)
150
 
 
 
 
 
 
 
 
151
  ## License
152
 
153
  [License details to be added]
 
4
 
5
  ## Recent Updates and Improvements
6
 
7
+ ### Growth Model Enhancements (August 2024)
8
+ - **Updated Growth Parameters**: Back-solved from August 2024 field data (t=1.58 years):
9
+ - DBH growth rate (b): Changed from 0.1237 to 0.4736 yr⁻¹
10
+ - Height growth rate (b): Changed from 0.1237 to 0.1335 yr⁻¹
11
+ - Height shape parameter (c): Changed from 2.1 to 1.5
12
+ - Rhizophora height asymptote: Changed from 12.0m to 11.58m
13
+
14
+ - **Allometric Equations**: Using Zanvo et al. (2023) species-specific equations:
15
  - Rhizophora: Total = 1.938 × (DBH² H)^0.67628
16
  - Avicennia: Total = 1.486 × (DBH² H)^0.55864
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  ### Dashboard Improvements
19
+ - **Enhanced Parameter Controls**:
20
+ - Direct input of Chapman-Richards parameters
21
+ - Separate growth parameters for DBH and height
22
+ - Real-time model updates
23
+ - Improved validation and error handling
24
+
25
+ - **Improved Visualizations**:
26
+ - Carbon sequestration over time
27
+ - Annual emission reductions
28
+ - Per-tree biomass growth curves
29
+ - Enhanced data tables and summaries
30
+
31
+ - **Detailed Metrics**:
32
+ - Project overview with total area and buffer pool
33
+ - Comprehensive carbon sequestration metrics
34
+ - Milestone year comparisons (10, 20, 30 years)
35
+ - Per hectare metrics with annual rates
36
+ - Species-specific results
 
 
 
37
 
38
  ## Model Parameters
39
 
40
  ### Species Parameters
41
+ - **Rhizophora spp.**
42
+ - DBH: a = 11.07 cm, b = 0.4736 yr⁻¹, c = 2.1
43
+ - Height: a = 11.58 m, b = 0.1335 yr⁻¹, c = 1.5
44
+ - Initial values: DBH = 1.0 cm, Height = 0.5 m
45
  - Configurable planting density and mortality rates
46
 
47
+ - **Avicennia germinans**
48
+ - DBH: a = 11.07 cm, b = 0.4736 yr⁻¹, c = 2.1
49
+ - Height: a = 11.58 m, b = 0.1335 yr⁻¹, c = 1.5
50
+ - Initial values: DBH = 1.0 cm, Height = 0.5 m
51
  - Configurable planting density and mortality rates
52
 
53
+ ### Project Configuration
54
+ - Total area: 5000 ha (2500 ha each in years 1-2)
55
+ - Project duration: 30 years
56
+ - Buffer pool: 20%
57
+ - Species mix: Rhizophora spp. and Avicennia germinans
 
58
 
59
  ## Installation
60
 
 
71
  pre-commit install
72
  ```
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  ## Usage
75
 
76
  1. Configure parameters in `configs/params.yaml`
77
 
78
+ 2. Launch the dashboard:
 
 
 
 
 
79
  ```bash
80
+ python -m dashboard.app
81
  ```
82
  This will start a Gradio server and open the dashboard in your default web browser.
83
+ The dashboard will be accessible via a public URL for sharing.
84
+
85
+ 3. Using the Dashboard:
86
+ - Adjust species parameters (density, mortality, growth)
87
+ - Set planting schedule (up to 5 years)
88
+ - Configure buffer pool percentage
89
+ - View real-time updates to:
90
+ * Carbon sequestration curves
91
+ * Annual emission reductions
92
+ * Biomass per tree growth
93
+ * Detailed metrics and summaries
94
 
95
+ ## Project Structure
96
 
97
+ - `src/` - Core model implementation
98
+ - `er_model.py` - Main ER calculation logic
99
+ - `allometry.py` - Species-specific allometric equations
100
+ - `metrics.py` - Carbon conversion and statistics
101
+ - `dashboard/` - Gradio web application
102
+ - `configs/` - Parameter configuration files
103
+ - `tests/` - Test suite
104
 
105
  ## Dependencies
106
 
107
  Key dependencies (specified in `environment.yml`):
108
+ - numpy
109
+ - pandas
110
+ - gradio
111
  - matplotlib
112
  - pyyaml
113
  - pytest (for development)
114
 
115
+ ## Development
116
+
117
+ - Code style is enforced using `black` and `isort`
118
+ - Type hints are required for all public functions
119
+ - Tests are written using `pytest`
120
+ - Pre-commit hooks ensure code quality
121
+
122
  ## License
123
 
124
  [License details to be added]
dashboard/app.py CHANGED
@@ -9,6 +9,7 @@ import matplotlib.pyplot as plt
9
  import numpy as np
10
  import pandas as pd
11
  import yaml
 
12
 
13
  from src.er_model import ERModel
14
 
@@ -21,17 +22,23 @@ def load_config(config_path: Path = Path("configs/params.yaml")) -> dict:
21
 
22
  def run_model(config: dict) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
23
  """Run model with current parameters."""
24
- # Save temporary config
25
- temp_config = Path("configs/temp_config.yaml")
26
- with open(temp_config, "w") as f:
27
- yaml.dump(config, f)
28
 
29
- # Run model and cleanup
30
- model = ERModel(temp_config)
31
- results, species_results, scenario_results = model.run()
32
- temp_config.unlink()
33
-
34
- return results, species_results, scenario_results
 
 
 
 
 
 
 
 
35
 
36
 
37
  def format_number(x):
@@ -48,7 +55,13 @@ def format_number(x):
48
  def create_plots_and_tables(
49
  results: pd.DataFrame,
50
  species_results: pd.DataFrame,
51
- scenario_results: pd.DataFrame
 
 
 
 
 
 
52
  ) -> Tuple[plt.Figure, plt.Figure, plt.Figure, str, pd.DataFrame, pd.DataFrame]:
53
  """Create carbon sequestration plots, summary, and tables."""
54
  # Clear any existing plots
@@ -96,11 +109,6 @@ def create_plots_and_tables(
96
  fig3 = plt.figure(figsize=(10, 6))
97
  ax3 = fig3.add_subplot(111)
98
 
99
- # Load config to get growth parameters
100
- config = load_config()
101
- rhiz_params = config["species"][0]["chapman_richards"]
102
- avic_params = config["species"][1]["chapman_richards"]
103
-
104
  # Get initial values
105
  rhiz_init = config["species"][0]["initial_values"]
106
  avic_init = config["species"][1]["initial_values"]
@@ -114,12 +122,12 @@ def create_plots_and_tables(
114
  return initial + (a - initial) * (1 - np.exp(-b * t)) ** c
115
 
116
  # Calculate growth for Rhizophora
117
- rhiz_dbh = chapman_richards(years, rhiz_params["dbh"], rhiz_init["dbh"])
118
- rhiz_height = chapman_richards(years, rhiz_params["height"], rhiz_init["height"])
119
 
120
  # Calculate growth for Avicennia
121
- avic_dbh = chapman_richards(years, avic_params["dbh"], avic_init["dbh"])
122
- avic_height = chapman_richards(years, avic_params["height"], avic_init["height"])
123
 
124
  # Calculate biomass using Zanvo equations (convert kg to tonnes)
125
  rhiz_biomass = 1.938 * (rhiz_dbh**2 * rhiz_height)**0.67628 / 1000 # Convert kg to tonnes
@@ -147,7 +155,9 @@ def create_plots_and_tables(
147
  plt.draw()
148
 
149
  # Calculate total area from planting schedule
150
- total_area = sum(float(v) for k, v in results.iloc[-1].items() if k.startswith("area_year"))
 
 
151
 
152
  # Get final year carbon values
153
  final_gross = results["gross_carbon"].iloc[-1]
@@ -157,23 +167,54 @@ def create_plots_and_tables(
157
  if total_area > 0:
158
  gross_carbon_per_ha = final_gross / total_area
159
  net_carbon_per_ha = final_net / total_area
 
160
  else:
161
  gross_carbon_per_ha = 0
162
  net_carbon_per_ha = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  # Create summary text with formatted numbers
165
  summary = f"""
166
- Results Summary:
167
- ---------------
168
- Total Years: {len(results)}
169
  Total Area Planted: {format_number(total_area)} ha
170
- Total Gross Carbon: {format_number(final_gross)} tCO2
171
- Total Net Carbon: {format_number(final_net)} tCO2
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  Per Hectare Metrics (Year 30):
174
  -----------------------------
175
  Gross Carbon per ha: {format_number(gross_carbon_per_ha)} tCO2/ha
176
  Net Carbon per ha: {format_number(net_carbon_per_ha)} tCO2/ha
 
177
  """
178
 
179
  # Format species results table with comma separators
@@ -223,12 +264,24 @@ def update_model(
223
  year_4_area: float,
224
  year_5_area: float,
225
  buffer_percentage: float,
226
- scenario_area: float = 1000.0,
227
- dbh_range_min: float = 1.0,
228
- dbh_range_max: float = 25.0,
229
- height_range_min: float = 0.5,
230
- height_range_max: float = 15.0,
231
- growth_rate_factor: float = 1.0,
 
 
 
 
 
 
 
 
 
 
 
 
232
  ) -> Tuple[plt.Figure, plt.Figure, plt.Figure, str, pd.DataFrame, pd.DataFrame]:
233
  """Update model with new parameters and return plots and tables."""
234
  # Load base config
@@ -244,6 +297,22 @@ def update_model(
244
  "year_5": rhiz_mort_5
245
  })
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  # Update Avicennia parameters
248
  config["species"][1]["planting_density"] = avic_density
249
  config["species"][1]["mortality_rates"].update({
@@ -254,6 +323,22 @@ def update_model(
254
  "year_5": avic_mort_5
255
  })
256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  # Update planting schedule
258
  config["project"]["planting_schedule"].update({
259
  "year_1": year_1_area,
@@ -266,179 +351,109 @@ def update_model(
266
  # Update carbon parameters
267
  config["carbon"]["buffer_percentage"] = buffer_percentage
268
 
269
- # Add scenario parameters
270
- config["scenarios"] = {
271
- "area": scenario_area,
272
- "dbh_range": [dbh_range_min, dbh_range_max],
273
- "height_range": [height_range_min, height_range_max],
274
- "growth_rate_factor": growth_rate_factor
275
- }
276
-
277
  # Run model and create plots/tables
278
  results, species_results, scenario_results = run_model(config)
279
- return create_plots_and_tables(results, species_results, scenario_results)
280
 
281
 
282
  def main():
283
- """Launch the Gradio interface."""
284
- config = load_config()
285
-
286
- # Extract default values
287
- rhiz_defaults = config["species"][0]
288
- avic_defaults = config["species"][1]
289
- planting_defaults = config["project"]["planting_schedule"]
290
- carbon_defaults = config["carbon"]
291
- scenario_defaults = config.get("scenarios", {
292
- "area": 1000.0,
293
- "dbh_range": [1.0, 25.0],
294
- "height_range": [0.5, 15.0],
295
- "growth_rate_factor": 1.0
296
- })
297
-
298
- # Create interface
299
- with gr.Blocks(title="ER Model Dashboard", theme=gr.themes.Soft()) as interface:
300
  gr.Markdown("# ER Model Dashboard")
301
- gr.Markdown("Explore carbon sequestration results and test different parameters")
302
 
303
- with gr.Row():
304
- with gr.Column():
305
- # Rhizophora parameters
306
- gr.Markdown("### Rhizophora Parameters")
307
- rhiz_density = gr.Slider(
308
- minimum=100,
309
- maximum=10000,
310
- value=rhiz_defaults["planting_density"],
311
- label="Planting Density (trees/ha)"
312
- )
313
- rhiz_mort_1 = gr.Slider(0, 100, rhiz_defaults["mortality_rates"]["year_1"], label="Year 1 Mortality (%)")
314
- rhiz_mort_2 = gr.Slider(0, 100, rhiz_defaults["mortality_rates"]["year_2"], label="Year 2 Mortality (%)")
315
- rhiz_mort_3 = gr.Slider(0, 100, rhiz_defaults["mortality_rates"]["year_3"], label="Year 3 Mortality (%)")
316
- rhiz_mort_4 = gr.Slider(0, 100, rhiz_defaults["mortality_rates"]["year_4"], label="Year 4 Mortality (%)")
317
- rhiz_mort_5 = gr.Slider(0, 100, rhiz_defaults["mortality_rates"]["year_5"], label="Year 5 Mortality (%)")
318
-
319
- with gr.Column():
320
- # Avicennia parameters
321
- gr.Markdown("### Avicennia Parameters")
322
- avic_density = gr.Slider(
323
- minimum=100,
324
- maximum=10000,
325
- value=avic_defaults["planting_density"],
326
- label="Planting Density (trees/ha)"
327
- )
328
- avic_mort_1 = gr.Slider(0, 100, avic_defaults["mortality_rates"]["year_1"], label="Year 1 Mortality (%)")
329
- avic_mort_2 = gr.Slider(0, 100, avic_defaults["mortality_rates"]["year_2"], label="Year 2 Mortality (%)")
330
- avic_mort_3 = gr.Slider(0, 100, avic_defaults["mortality_rates"]["year_3"], label="Year 3 Mortality (%)")
331
- avic_mort_4 = gr.Slider(0, 100, avic_defaults["mortality_rates"]["year_4"], label="Year 4 Mortality (%)")
332
- avic_mort_5 = gr.Slider(0, 100, avic_defaults["mortality_rates"]["year_5"], label="Year 5 Mortality (%)")
333
-
334
- with gr.Row():
335
- with gr.Column():
336
- # Planting schedule
337
- gr.Markdown("### Planting Schedule")
338
- year_1_area = gr.Slider(0, 1000, planting_defaults["year_1"], label="Year 1 Area (ha)")
339
- year_2_area = gr.Slider(0, 1000, planting_defaults["year_2"], label="Year 2 Area (ha)")
340
- year_3_area = gr.Slider(0, 1000, planting_defaults["year_3"], label="Year 3 Area (ha)")
341
- year_4_area = gr.Slider(0, 1000, planting_defaults["year_4"], label="Year 4 Area (ha)")
342
- year_5_area = gr.Slider(0, 1000, planting_defaults["year_5"], label="Year 5 Area (ha)")
343
-
344
- with gr.Column():
345
- # Carbon parameters
346
- gr.Markdown("### Carbon Parameters")
347
- buffer_percentage = gr.Slider(
348
- minimum=0,
349
- maximum=30,
350
- value=carbon_defaults["buffer_percentage"],
351
- label="Buffer Percentage (%)"
352
- )
353
-
354
- # Scenario parameters
355
- gr.Markdown("### Scenario Parameters")
356
- scenario_area = gr.Slider(
357
- minimum=100,
358
- maximum=2000,
359
- value=scenario_defaults["area"],
360
- label="Scenario Area (ha)"
361
- )
362
- dbh_range_min = gr.Slider(
363
- minimum=0.5,
364
- maximum=10,
365
- value=scenario_defaults["dbh_range"][0],
366
- label="Min DBH (cm)"
367
- )
368
- dbh_range_max = gr.Slider(
369
- minimum=10,
370
- maximum=30,
371
- value=scenario_defaults["dbh_range"][1],
372
- label="Max DBH (cm)"
373
- )
374
- height_range_min = gr.Slider(
375
- minimum=0.5,
376
- maximum=5,
377
- value=scenario_defaults["height_range"][0],
378
- label="Min Height (m)"
379
- )
380
- height_range_max = gr.Slider(
381
- minimum=5,
382
- maximum=20,
383
- value=scenario_defaults["height_range"][1],
384
- label="Max Height (m)"
385
- )
386
- growth_rate_factor = gr.Slider(
387
- minimum=0.5,
388
- maximum=2.0,
389
- value=scenario_defaults["growth_rate_factor"],
390
- label="Growth Rate Factor"
391
- )
392
-
393
- # Outputs
394
- with gr.Row():
395
- plot1 = gr.Plot(label="Carbon Sequestration Over Time")
396
- plot2 = gr.Plot(label="Annual Emission Reductions")
397
 
398
  with gr.Row():
399
- plot3 = gr.Plot(label="Total Biomass per Tree")
400
-
401
- summary = gr.Textbox(label="Summary", lines=10)
402
 
403
- with gr.Row():
404
- species_table = gr.Dataframe(
405
- label="Annual Emission Reductions",
406
- headers=["Year", "Annual ERs (tCO2)", "Cumulative ERs (tCO2)", "Area Planted (ha)", "Cumulative Area (ha)"]
407
- )
408
- scenario_table = gr.Dataframe(
409
- label="Scenario Comparison",
410
- headers=["Scenario", "DBH Range", "Height Range", "Growth Rate", "Year 5 tCO2", "Year 10 tCO2", "Year 15 tCO2", "Year 30 tCO2"]
411
- )
 
 
412
 
413
- # Update function
414
  inputs = [
415
  rhiz_density, rhiz_mort_1, rhiz_mort_2, rhiz_mort_3, rhiz_mort_4, rhiz_mort_5,
416
  avic_density, avic_mort_1, avic_mort_2, avic_mort_3, avic_mort_4, avic_mort_5,
417
  year_1_area, year_2_area, year_3_area, year_4_area, year_5_area,
418
- buffer_percentage, scenario_area, dbh_range_min, dbh_range_max,
419
- height_range_min, height_range_max, growth_rate_factor
 
 
 
 
 
 
 
420
  ]
421
 
422
- outputs = [plot1, plot2, plot3, summary, species_table, scenario_table]
423
 
424
- # Initialize with default values
425
- interface.load(
426
- fn=update_model,
427
- inputs=inputs,
428
- outputs=outputs,
429
- )
430
 
431
- # Update on any change
432
- for inp in inputs:
433
- inp.change(
434
- fn=update_model,
435
- inputs=inputs,
436
- outputs=outputs
437
- )
438
-
439
- # Launch interface
440
- interface.launch(share=False)
441
 
442
 
443
  if __name__ == "__main__":
444
- main()
 
 
9
  import numpy as np
10
  import pandas as pd
11
  import yaml
12
+ import uuid
13
 
14
  from src.er_model import ERModel
15
 
 
22
 
23
  def run_model(config: dict) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
24
  """Run model with current parameters."""
25
+ # Create a temporary config file with a unique name
26
+ temp_config = Path(f"configs/temp_config_{uuid.uuid4()}.yaml")
 
 
27
 
28
+ try:
29
+ # Save temporary config
30
+ with open(temp_config, "w") as f:
31
+ yaml.dump(config, f)
32
+
33
+ # Run model
34
+ model = ERModel(temp_config)
35
+ results, species_results, scenario_results = model.run()
36
+
37
+ return results, species_results, scenario_results
38
+ finally:
39
+ # Clean up temp file
40
+ if temp_config.exists():
41
+ temp_config.unlink()
42
 
43
 
44
  def format_number(x):
 
55
  def create_plots_and_tables(
56
  results: pd.DataFrame,
57
  species_results: pd.DataFrame,
58
+ scenario_results: pd.DataFrame,
59
+ config: dict,
60
+ year_1_area: float,
61
+ year_2_area: float,
62
+ year_3_area: float,
63
+ year_4_area: float,
64
+ year_5_area: float
65
  ) -> Tuple[plt.Figure, plt.Figure, plt.Figure, str, pd.DataFrame, pd.DataFrame]:
66
  """Create carbon sequestration plots, summary, and tables."""
67
  # Clear any existing plots
 
109
  fig3 = plt.figure(figsize=(10, 6))
110
  ax3 = fig3.add_subplot(111)
111
 
 
 
 
 
 
112
  # Get initial values
113
  rhiz_init = config["species"][0]["initial_values"]
114
  avic_init = config["species"][1]["initial_values"]
 
122
  return initial + (a - initial) * (1 - np.exp(-b * t)) ** c
123
 
124
  # Calculate growth for Rhizophora
125
+ rhiz_dbh = chapman_richards(years, config["species"][0]["chapman_richards"]["dbh"], rhiz_init["dbh"])
126
+ rhiz_height = chapman_richards(years, config["species"][0]["chapman_richards"]["height"], rhiz_init["height"])
127
 
128
  # Calculate growth for Avicennia
129
+ avic_dbh = chapman_richards(years, config["species"][1]["chapman_richards"]["dbh"], avic_init["dbh"])
130
+ avic_height = chapman_richards(years, config["species"][1]["chapman_richards"]["height"], avic_init["height"])
131
 
132
  # Calculate biomass using Zanvo equations (convert kg to tonnes)
133
  rhiz_biomass = 1.938 * (rhiz_dbh**2 * rhiz_height)**0.67628 / 1000 # Convert kg to tonnes
 
155
  plt.draw()
156
 
157
  # Calculate total area from planting schedule
158
+ total_area = sum([
159
+ year_1_area, year_2_area, year_3_area, year_4_area, year_5_area
160
+ ])
161
 
162
  # Get final year carbon values
163
  final_gross = results["gross_carbon"].iloc[-1]
 
167
  if total_area > 0:
168
  gross_carbon_per_ha = final_gross / total_area
169
  net_carbon_per_ha = final_net / total_area
170
+ annual_net_per_ha = net_carbon_per_ha / len(results) # Divide by actual years
171
  else:
172
  gross_carbon_per_ha = 0
173
  net_carbon_per_ha = 0
174
+ annual_net_per_ha = 0
175
+
176
+ # Get year 10 and year 20 values for intermediate metrics
177
+ year_10_gross = results["gross_carbon"].iloc[9] if len(results) > 9 else 0
178
+ year_10_net = results["net_carbon"].iloc[9] if len(results) > 9 else 0
179
+ year_20_gross = results["gross_carbon"].iloc[19] if len(results) > 19 else 0
180
+ year_20_net = results["net_carbon"].iloc[19] if len(results) > 19 else 0
181
+
182
+ # Calculate annual averages
183
+ years_with_carbon = len(results[results["gross_carbon"] > 0])
184
+ if years_with_carbon > 0:
185
+ avg_annual_gross = final_gross / years_with_carbon
186
+ avg_annual_net = final_net / years_with_carbon
187
+ else:
188
+ avg_annual_gross = 0
189
+ avg_annual_net = 0
190
 
191
  # Create summary text with formatted numbers
192
  summary = f"""
193
+ Project Overview:
194
+ ----------------
195
+ Duration: {len(results)} years
196
  Total Area Planted: {format_number(total_area)} ha
197
+ Buffer Pool: {config["carbon"]["buffer_percentage"]}%
198
+
199
+ Carbon Sequestration:
200
+ -------------------
201
+ Total Gross Carbon (Year 30): {format_number(final_gross)} tCO2
202
+ Total Net Carbon (Year 30): {format_number(final_net)} tCO2
203
+ Average Annual Gross: {format_number(avg_annual_gross)} tCO2/yr
204
+ Average Annual Net: {format_number(avg_annual_net)} tCO2/yr
205
+
206
+ Milestone Years:
207
+ --------------
208
+ Year 10 Gross: {format_number(year_10_gross)} tCO2
209
+ Year 10 Net: {format_number(year_10_net)} tCO2
210
+ Year 20 Gross: {format_number(year_20_gross)} tCO2
211
+ Year 20 Net: {format_number(year_20_net)} tCO2
212
 
213
  Per Hectare Metrics (Year 30):
214
  -----------------------------
215
  Gross Carbon per ha: {format_number(gross_carbon_per_ha)} tCO2/ha
216
  Net Carbon per ha: {format_number(net_carbon_per_ha)} tCO2/ha
217
+ Average Annual Net per ha: {format_number(annual_net_per_ha)} tCO2/ha/yr
218
  """
219
 
220
  # Format species results table with comma separators
 
264
  year_4_area: float,
265
  year_5_area: float,
266
  buffer_percentage: float,
267
+ # Chapman-Richards parameters for Rhizophora
268
+ rhiz_dbh_a: float,
269
+ rhiz_dbh_b: float,
270
+ rhiz_dbh_c: float,
271
+ rhiz_height_a: float,
272
+ rhiz_height_b: float,
273
+ rhiz_height_c: float,
274
+ rhiz_initial_dbh: float,
275
+ rhiz_initial_height: float,
276
+ # Chapman-Richards parameters for Avicennia
277
+ avic_dbh_a: float,
278
+ avic_dbh_b: float,
279
+ avic_dbh_c: float,
280
+ avic_height_a: float,
281
+ avic_height_b: float,
282
+ avic_height_c: float,
283
+ avic_initial_dbh: float,
284
+ avic_initial_height: float,
285
  ) -> Tuple[plt.Figure, plt.Figure, plt.Figure, str, pd.DataFrame, pd.DataFrame]:
286
  """Update model with new parameters and return plots and tables."""
287
  # Load base config
 
297
  "year_5": rhiz_mort_5
298
  })
299
 
300
+ # Update Rhizophora Chapman-Richards parameters
301
+ config["species"][0]["chapman_richards"]["dbh"].update({
302
+ "a": rhiz_dbh_a,
303
+ "b": rhiz_dbh_b,
304
+ "c": rhiz_dbh_c
305
+ })
306
+ config["species"][0]["chapman_richards"]["height"].update({
307
+ "a": rhiz_height_a,
308
+ "b": rhiz_height_b,
309
+ "c": rhiz_height_c
310
+ })
311
+ config["species"][0]["initial_values"].update({
312
+ "dbh": rhiz_initial_dbh,
313
+ "height": rhiz_initial_height
314
+ })
315
+
316
  # Update Avicennia parameters
317
  config["species"][1]["planting_density"] = avic_density
318
  config["species"][1]["mortality_rates"].update({
 
323
  "year_5": avic_mort_5
324
  })
325
 
326
+ # Update Avicennia Chapman-Richards parameters
327
+ config["species"][1]["chapman_richards"]["dbh"].update({
328
+ "a": avic_dbh_a,
329
+ "b": avic_dbh_b,
330
+ "c": avic_dbh_c
331
+ })
332
+ config["species"][1]["chapman_richards"]["height"].update({
333
+ "a": avic_height_a,
334
+ "b": avic_height_b,
335
+ "c": avic_height_c
336
+ })
337
+ config["species"][1]["initial_values"].update({
338
+ "dbh": avic_initial_dbh,
339
+ "height": avic_initial_height
340
+ })
341
+
342
  # Update planting schedule
343
  config["project"]["planting_schedule"].update({
344
  "year_1": year_1_area,
 
351
  # Update carbon parameters
352
  config["carbon"]["buffer_percentage"] = buffer_percentage
353
 
 
 
 
 
 
 
 
 
354
  # Run model and create plots/tables
355
  results, species_results, scenario_results = run_model(config)
356
+ return create_plots_and_tables(results, species_results, scenario_results, config, year_1_area, year_2_area, year_3_area, year_4_area, year_5_area)
357
 
358
 
359
  def main():
360
+ """Run the Gradio interface."""
361
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  gr.Markdown("# ER Model Dashboard")
363
+ gr.Markdown("Explore carbon sequestration results with different growth parameters")
364
 
365
+ with gr.Tab("Model Parameters"):
366
+ with gr.Row():
367
+ with gr.Column():
368
+ gr.Markdown("### Rhizophora Parameters")
369
+ rhiz_density = gr.Number(value=2500, label="Planting Density (trees/ha)")
370
+ rhiz_mort_1 = gr.Number(value=20, label="Year 1 Mortality (%)")
371
+ rhiz_mort_2 = gr.Number(value=10, label="Year 2 Mortality (%)")
372
+ rhiz_mort_3 = gr.Number(value=5, label="Year 3 Mortality (%)")
373
+ rhiz_mort_4 = gr.Number(value=2, label="Year 4 Mortality (%)")
374
+ rhiz_mort_5 = gr.Number(value=1, label="Year 5 Mortality (%)")
375
+
376
+ gr.Markdown("### Rhizophora Growth Parameters")
377
+ rhiz_dbh_a = gr.Number(value=11.07, label="DBH Asymptote (cm)")
378
+ rhiz_dbh_b = gr.Number(value=0.4736, label="DBH Growth Rate (yr⁻¹)")
379
+ rhiz_dbh_c = gr.Number(value=2.1, label="DBH Shape Parameter")
380
+ rhiz_height_a = gr.Number(value=11.58, label="Height Asymptote (m)")
381
+ rhiz_height_b = gr.Number(value=0.1335, label="Height Growth Rate (yr⁻¹)")
382
+ rhiz_height_c = gr.Number(value=1.5, label="Height Shape Parameter")
383
+ rhiz_initial_dbh = gr.Number(value=1.0, label="Initial DBH (cm)")
384
+ rhiz_initial_height = gr.Number(value=0.5, label="Initial Height (m)")
385
+
386
+ with gr.Column():
387
+ gr.Markdown("### Avicennia Parameters")
388
+ avic_density = gr.Number(value=2500, label="Planting Density (trees/ha)")
389
+ avic_mort_1 = gr.Number(value=20, label="Year 1 Mortality (%)")
390
+ avic_mort_2 = gr.Number(value=10, label="Year 2 Mortality (%)")
391
+ avic_mort_3 = gr.Number(value=5, label="Year 3 Mortality (%)")
392
+ avic_mort_4 = gr.Number(value=2, label="Year 4 Mortality (%)")
393
+ avic_mort_5 = gr.Number(value=1, label="Year 5 Mortality (%)")
394
+
395
+ gr.Markdown("### Avicennia Growth Parameters")
396
+ avic_dbh_a = gr.Number(value=11.07, label="DBH Asymptote (cm)")
397
+ avic_dbh_b = gr.Number(value=0.4736, label="DBH Growth Rate (yr⁻¹)")
398
+ avic_dbh_c = gr.Number(value=2.1, label="DBH Shape Parameter")
399
+ avic_height_a = gr.Number(value=11.58, label="Height Asymptote (m)")
400
+ avic_height_b = gr.Number(value=0.1335, label="Height Growth Rate (yr⁻¹)")
401
+ avic_height_c = gr.Number(value=1.5, label="Height Shape Parameter")
402
+ avic_initial_dbh = gr.Number(value=1.0, label="Initial DBH (cm)")
403
+ avic_initial_height = gr.Number(value=0.5, label="Initial Height (m)")
404
+
405
+ with gr.Row():
406
+ with gr.Column():
407
+ gr.Markdown("### Project Parameters")
408
+ year_1_area = gr.Number(value=2500, label="Year 1 Planting Area (ha)")
409
+ year_2_area = gr.Number(value=2500, label="Year 2 Planting Area (ha)")
410
+ year_3_area = gr.Number(value=0, label="Year 3 Planting Area (ha)")
411
+ year_4_area = gr.Number(value=0, label="Year 4 Planting Area (ha)")
412
+ year_5_area = gr.Number(value=0, label="Year 5 Planting Area (ha)")
413
+ buffer_percentage = gr.Number(value=20, label="Buffer Pool (%)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
  with gr.Row():
416
+ update_btn = gr.Button("Update Model")
 
 
417
 
418
+ with gr.Tab("Results"):
419
+ with gr.Row():
420
+ carbon_plot = gr.Plot(label="Carbon Sequestration")
421
+ annual_plot = gr.Plot(label="Annual ERs")
422
+ biomass_plot = gr.Plot(label="Biomass per Tree")
423
+
424
+ summary_text = gr.Textbox(label="Summary", lines=10)
425
+
426
+ with gr.Row():
427
+ annual_table = gr.DataFrame(label="Annual Results")
428
+ species_table = gr.DataFrame(label="Species Results")
429
 
430
+ # Connect the update button to the model update function
431
  inputs = [
432
  rhiz_density, rhiz_mort_1, rhiz_mort_2, rhiz_mort_3, rhiz_mort_4, rhiz_mort_5,
433
  avic_density, avic_mort_1, avic_mort_2, avic_mort_3, avic_mort_4, avic_mort_5,
434
  year_1_area, year_2_area, year_3_area, year_4_area, year_5_area,
435
+ buffer_percentage,
436
+ # Chapman-Richards parameters for Rhizophora
437
+ rhiz_dbh_a, rhiz_dbh_b, rhiz_dbh_c,
438
+ rhiz_height_a, rhiz_height_b, rhiz_height_c,
439
+ rhiz_initial_dbh, rhiz_initial_height,
440
+ # Chapman-Richards parameters for Avicennia
441
+ avic_dbh_a, avic_dbh_b, avic_dbh_c,
442
+ avic_height_a, avic_height_b, avic_height_c,
443
+ avic_initial_dbh, avic_initial_height
444
  ]
445
 
446
+ outputs = [carbon_plot, annual_plot, biomass_plot, summary_text, annual_table, species_table]
447
 
448
+ # Connect button click event
449
+ update_btn.click(fn=update_model, inputs=inputs, outputs=outputs)
 
 
 
 
450
 
451
+ # Initialize the UI with default values
452
+ app.load(fn=update_model, inputs=inputs, outputs=outputs)
453
+
454
+ return app
 
 
 
 
 
 
455
 
456
 
457
  if __name__ == "__main__":
458
+ app = main()
459
+ app.launch(share=True)