{ "cells": [ { "cell_type": "markdown", "id": "11051f3d", "metadata": {}, "source": [ "# Soderberg 1986 composite preset demo\n", "\n", "This notebook demonstrates the `Soderberg1986Pipeline`, which keeps the regeneration + young-stand + mortality + valuation workflow from the Elfving preset but uses **Soderberg (1986)** for mature-tree DBH growth.\n", "\n", "It also reports `qmd_cm` and `hq_m` in each projection step." ] }, { "cell_type": "code", "execution_count": 1, "id": "f367db5a", "metadata": { "execution": { "iopub.execute_input": "2026-02-19T23:10:02.847240Z", "iopub.status.busy": "2026-02-19T23:10:02.846930Z", "iopub.status.idle": "2026-02-19T23:10:04.150246Z", "shell.execute_reply": "2026-02-19T23:10:04.148005Z" } }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "from pyforestry.base.helpers.primitives import SiteBase\n", "from pyforestry.sweden.simulation.presets import (\n", " Soderberg1986PipelineConfig,\n", " build_soderberg_1986_pipeline,\n", ")\n", "from pyforestry.sweden.site import Sweden, SwedishSite\n", "from pyforestry.sweden.siteindex.sis.generated_site_category_trees import (\n", " predict_site_categories_county_tree,\n", ")\n", "from pyforestry.sweden.siteindex.sis.hagglund_lundmark_1977 import Hagglund_Lundmark_1977_SIS\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "a982b8f8", "metadata": { "execution": { "iopub.execute_input": "2026-02-19T23:10:04.154174Z", "iopub.status.busy": "2026-02-19T23:10:04.153677Z", "iopub.status.idle": "2026-02-19T23:10:04.159894Z", "shell.execute_reply": "2026-02-19T23:10:04.157877Z" } }, "outputs": [], "source": [ "class SwedishSiteDemo(SwedishSite):\n", " \"\"\"Concrete wrapper for notebooks (implements SiteBase abstract method).\"\"\"\n", "\n", " def compute_attributes(self) -> None:\n", " SwedishSite.__post_init__(self)\n", "\n", " def __post_init__(self) -> None:\n", " SiteBase.__post_init__(self)" ] }, { "cell_type": "code", "execution_count": 3, "id": "77357b8e", "metadata": { "execution": { "iopub.execute_input": "2026-02-19T23:10:04.162764Z", "iopub.status.busy": "2026-02-19T23:10:04.162528Z", "iopub.status.idle": "2026-02-19T23:10:04.439849Z", "shell.execute_reply": "2026-02-19T23:10:04.437708Z" } }, "outputs": [ { "data": { "text/plain": [ "{'sis_closeness': {'requested_sis': 20.0,\n", " 'achieved_sis': 20.698654683125625,\n", " 'sis_abs_error': 0.6986546831256248,\n", " 'sis_rel_error_pct': 3.493273415628124},\n", " 'predicted_site_categories': {'field_layer': ,\n", " 'bottom_layer': ,\n", " 'soil_texture': ,\n", " 'soil_moisture': ,\n", " 'soil_depth': ,\n", " 'soil_water': ,\n", " 'ditched': False}}" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "requested_sis = 20.0\n", "site_category_species = \"Pinus sylvestris\"\n", "county = Sweden.County.KOPPARBERG_OVRIGA\n", "\n", "predicted_site_categories = predict_site_categories_county_tree(\n", " sis_hagglund_1979=requested_sis,\n", " species=site_category_species,\n", " Direktlan=county,\n", ")\n", "\n", "site = SwedishSiteDemo(\n", " latitude=60.5,\n", " longitude=15.0,\n", " altitude=150.0,\n", " field_layer=predicted_site_categories[\"field_layer\"],\n", " bottom_layer=predicted_site_categories[\"bottom_layer\"],\n", " soil_texture=predicted_site_categories[\"soil_texture\"],\n", " soil_moisture=predicted_site_categories[\"soil_moisture\"],\n", " soil_depth=predicted_site_categories[\"soil_depth\"],\n", " soil_water=predicted_site_categories[\"soil_water\"],\n", " ditched=predicted_site_categories[\"ditched\"],\n", ")\n", "\n", "achieved_sis = Hagglund_Lundmark_1977_SIS(\n", " species=site_category_species,\n", " latitude=site.latitude,\n", " altitude=site.altitude or 0.0,\n", " soil_moisture=site.soil_moisture,\n", " ground_layer=site.bottom_layer or Sweden.BottomLayer.FRESH_MOSS,\n", " vegetation=site.field_layer,\n", " soil_texture=site.soil_texture or Sweden.SoilTextureTill.SANDY,\n", " climate_code=site.climate_zone or Sweden.ClimateZone.K1,\n", " lateral_water=site.soil_water or Sweden.SoilWater.SELDOM_NEVER,\n", " soil_depth=site.soil_depth or Sweden.SoilDepth.DEEP,\n", " incline_percent=site.incline_percent or 0.0,\n", " aspect=site.aspect or 0.0,\n", " nfi_adjustments=True,\n", " dlan=site.county or county,\n", " ditched=bool(site.ditched),\n", " peat=False,\n", " gotland=False,\n", " coast=(site.distance_to_coast or 9999.0) < 50.0,\n", " limes_norrlandicus=bool(site.n_of_limes_norrlandicus),\n", ")\n", "\n", "sis_closeness = {\n", " \"requested_sis\": requested_sis,\n", " \"achieved_sis\": float(achieved_sis),\n", " \"sis_abs_error\": abs(float(achieved_sis) - requested_sis),\n", " \"sis_rel_error_pct\": 100.0 * abs(float(achieved_sis) - requested_sis) / requested_sis,\n", "}\n", "\n", "display({\"sis_closeness\": sis_closeness, \"predicted_site_categories\": predicted_site_categories})\n", "\n", "config = Soderberg1986PipelineConfig(\n", " sample_trees=120,\n", " random_seed=2026,\n", " deterministic=True,\n", " dt_years=5.0,\n", ")\n", "preset = build_soderberg_1986_pipeline(config)\n", "preset" ] }, { "cell_type": "code", "execution_count": null, "id": "e2152830", "metadata": {}, "outputs": [], "source": [ "results = preset.run_projection(site=site, n_steps=30)\n", "results" ] }, { "cell_type": "code", "execution_count": null, "id": "7995f3f9", "metadata": {}, "outputs": [], "source": [ "results[[\n", " \"step_index\",\n", " \"age_years\",\n", " \"stems_per_ha\",\n", " \"qmd_cm\",\n", " \"hq_m\",\n", " \"mean_dbh_cm\",\n", " \"mean_height_m\",\n", " \"basal_area_m2_ha\",\n", " \"standing_volume_m3sk_per_ha\",\n", " \"standing_value_sek_per_ha\",\n", "]]" ] }, { "cell_type": "code", "execution_count": null, "id": "a16462e2", "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(2, 2, figsize=(11, 7), sharex=True)\n", "axes = axes.flatten()\n", "\n", "axes[0].plot(results[\"age_years\"], results[\"qmd_cm\"], marker=\"o\")\n", "axes[0].set_ylabel(\"QMD (cm)\")\n", "\n", "axes[1].plot(results[\"age_years\"], results[\"hq_m\"], marker=\"o\")\n", "axes[1].set_ylabel(\"Hq (m)\")\n", "\n", "axes[2].plot(results[\"age_years\"], results[\"standing_volume_m3sk_per_ha\"], marker=\"o\")\n", "axes[2].set_ylabel(\"Standing volume (m3/ha)\")\n", "axes[2].set_xlabel(\"Stand age (years)\")\n", "\n", "axes[3].plot(results[\"age_years\"], results[\"standing_value_sek_per_ha\"], marker=\"o\")\n", "axes[3].set_ylabel(\"Standing value (SEK/ha)\")\n", "axes[3].set_xlabel(\"Stand age (years)\")\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "id": "e096252f", "metadata": {}, "source": [ "## Notes\n", "\n", "- Mature-tree growth in this preset comes from `Soderberg1986Model`.\n", "- Unlike Elfving 2010, there is no stand-level basal-area correction stage in the mature-growth model." ] } ], "metadata": { "kernelspec": { "display_name": "pyforestry", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.2" } }, "nbformat": 4, "nbformat_minor": 5 }