4.4. Using the ws3 optimization modelling funtions (basic)

The ws3 package provides functions for formulating and solving optmization problems. In this notebook we will use these functions to formulate and solve a maximum even-flow harvest volume problem.

4.4.1. Set up environment

Below we (optionally) install the ws3 package from the local source code in this repository using the -e flag to ensure that the package is installed in editable mode (i.e., any changes you make to the source code immediately affect ws3 behaviour the next time you run the notebook). This is is not necessary if you have installed ws3 using pip or another method.

Set auto-reload to reload modules when they are changed.

[1]:
%load_ext autoreload
%autoreload 2
[2]:
clobber_ws3 = True
if clobber_ws3:
    %pip uninstall -y ws3
    %pip install -e ..
Found existing installation: ws3 1.1.0.dev0
Uninstalling ws3-1.1.0.dev0:
  Successfully uninstalled ws3-1.1.0.dev0
Note: you may need to restart the kernel to use updated packages.
Obtaining file:///home/gep/projects/ws3
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Installing backend dependencies ... done
  Preparing editable metadata (pyproject.toml) ... done
Requirement already satisfied: dill in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (0.4.0)
Requirement already satisfied: fiona in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.10.1)
Requirement already satisfied: gurobipy in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (12.0.3)
Requirement already satisfied: highspy in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.11.0)
Requirement already satisfied: libcbm in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.8.1)
Requirement already satisfied: matplotlib in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (3.10.5)
Requirement already satisfied: numpy>=1.21 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.2.6)
Requirement already satisfied: pandas>=1.3 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.3.1)
Requirement already satisfied: profilehooks in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.13.0)
Requirement already satisfied: pulp in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (3.2.2)
Requirement already satisfied: rasterio in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.4.3)
Requirement already satisfied: scipy>=1.7 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.16.1)
Requirement already satisfied: python-dateutil>=2.8.2 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2025.2)
Requirement already satisfied: six>=1.5 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas>=1.3->ws3==1.1.0.dev0) (1.17.0)
Requirement already satisfied: attrs>=19.2.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (25.3.0)
Requirement already satisfied: certifi in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (2025.8.3)
Requirement already satisfied: click~=8.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (8.2.1)
Requirement already satisfied: click-plugins>=1.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (1.1.1.2)
Requirement already satisfied: cligj>=0.5 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (0.7.2)
Requirement already satisfied: numexpr>=2.8.7 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (2.11.0)
Requirement already satisfied: numba in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (0.61.2)
Requirement already satisfied: pyyaml in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (6.0.2)
Requirement already satisfied: mock in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (5.2.0)
Requirement already satisfied: openpyxl in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (3.1.5)
Requirement already satisfied: contourpy>=1.0.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (1.3.3)
Requirement already satisfied: cycler>=0.10 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (4.59.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (1.4.8)
Requirement already satisfied: packaging>=20.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (25.0)
Requirement already satisfied: pillow>=8 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (11.3.0)
Requirement already satisfied: pyparsing>=2.3.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (3.2.3)
Requirement already satisfied: llvmlite<0.45,>=0.44.0dev0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from numba->libcbm->ws3==1.1.0.dev0) (0.44.0)
Requirement already satisfied: et-xmlfile in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from openpyxl->libcbm->ws3==1.1.0.dev0) (2.0.0)
Requirement already satisfied: affine in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from rasterio->ws3==1.1.0.dev0) (2.4.0)
Building wheels for collected packages: ws3
  Building editable for ws3 (pyproject.toml) ... done
  Created wheel for ws3: filename=ws3-1.1.0.dev0-py3-none-any.whl size=4140 sha256=234ec7265bfd9d1fa6baff1a4a2893b98fdda92b53bd338be7799e265eeb0626
  Stored in directory: /tmp/pip-ephem-wheel-cache-_pvy0c7t/wheels/8a/d1/f0/2b533a60b366fa03a12ca91a1ad068761e66b9df68fa0cadb9
Successfully built ws3
Installing collected packages: ws3
Successfully installed ws3-1.1.0.dev0
Note: you may need to restart the kernel to use updated packages.

Use pip to install Python packages listed in requirements.txt (some extra packages needed for example notebooks to run correctly).

[3]:
%pip install -r requirements.txt
Requirement already satisfied: seaborn in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 1)) (0.13.2)
Requirement already satisfied: geopandas in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 2)) (1.1.1)
Requirement already satisfied: ipywidgets in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from -r requirements.txt (line 3)) (8.1.7)
Requirement already satisfied: numpy!=1.24.0,>=1.20 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from seaborn->-r requirements.txt (line 1)) (2.2.6)
Requirement already satisfied: pandas>=1.2 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from seaborn->-r requirements.txt (line 1)) (2.3.1)
Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from seaborn->-r requirements.txt (line 1)) (3.10.5)
Requirement already satisfied: pyogrio>=0.7.2 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from geopandas->-r requirements.txt (line 2)) (0.11.1)
Requirement already satisfied: packaging in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from geopandas->-r requirements.txt (line 2)) (25.0)
Requirement already satisfied: pyproj>=3.5.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from geopandas->-r requirements.txt (line 2)) (3.7.1)
Requirement already satisfied: shapely>=2.0.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from geopandas->-r requirements.txt (line 2)) (2.1.1)
Requirement already satisfied: comm>=0.1.3 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipywidgets->-r requirements.txt (line 3)) (0.2.3)
Requirement already satisfied: ipython>=6.1.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipywidgets->-r requirements.txt (line 3)) (9.4.0)
Requirement already satisfied: traitlets>=4.3.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipywidgets->-r requirements.txt (line 3)) (5.14.3)
Requirement already satisfied: widgetsnbextension~=4.0.14 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipywidgets->-r requirements.txt (line 3)) (4.0.14)
Requirement already satisfied: jupyterlab_widgets~=3.0.15 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipywidgets->-r requirements.txt (line 3)) (3.0.15)
Requirement already satisfied: decorator in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (5.2.1)
Requirement already satisfied: ipython-pygments-lexers in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (1.1.1)
Requirement already satisfied: jedi>=0.16 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.19.2)
Requirement already satisfied: matplotlib-inline in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.1.7)
Requirement already satisfied: pexpect>4.3 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (4.9.0)
Requirement already satisfied: prompt_toolkit<3.1.0,>=3.0.41 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (3.0.51)
Requirement already satisfied: pygments>=2.4.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (2.19.2)
Requirement already satisfied: stack_data in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.6.3)
Requirement already satisfied: wcwidth in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from prompt_toolkit<3.1.0,>=3.0.41->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.2.13)
Requirement already satisfied: parso<0.9.0,>=0.8.4 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.8.4)
Requirement already satisfied: contourpy>=1.0.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (1.3.3)
Requirement already satisfied: cycler>=0.10 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (4.59.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (1.4.8)
Requirement already satisfied: pillow>=8 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (11.3.0)
Requirement already satisfied: pyparsing>=2.3.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (3.2.3)
Requirement already satisfied: python-dateutil>=2.7 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.2->seaborn->-r requirements.txt (line 1)) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.2->seaborn->-r requirements.txt (line 1)) (2025.2)
Requirement already satisfied: ptyprocess>=0.5 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.7.0)
Requirement already satisfied: certifi in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from pyogrio>=0.7.2->geopandas->-r requirements.txt (line 2)) (2025.8.3)
Requirement already satisfied: six>=1.5 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn->-r requirements.txt (line 1)) (1.17.0)
Requirement already satisfied: executing>=1.2.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (2.2.0)
Requirement already satisfied: asttokens>=2.1.0 in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (3.0.0)
Requirement already satisfied: pure-eval in /home/gep/projects/ws3/.venv/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets->-r requirements.txt (line 3)) (0.2.3)
Note: you may need to restart the kernel to use updated packages.

Import the ws3 package, and import a function from the local util module to generate and run scenarios. We also import matplotlib, which is used for visualisation.

[4]:
import matplotlib.pyplot as plt
import ws3
import ws3.forest
from util import run_scenario

4.4.2. Set up ForestModel instance

Set some reasonable model parameters.

[5]:
base_year = 2020
horizon = 10
period_length = 10
max_age = 1000
n_steps = 100
tvy_name = "totvol"

Create a new ForestModel object and import a model dataset from the data directory.

[6]:
fm = ws3.forest.ForestModel(model_name="tsa24_clipped",
                            model_path="data/woodstock_model_files_tsa24_clipped",
                            base_year=base_year,
                            horizon=horizon,
                            period_length=period_length,
                            max_age=max_age)
fm.import_landscape_section()
fm.import_areas_section(convert_periods_to_years=period_length)
fm.import_yields_section(convert_periods_to_years=period_length)
fm.import_actions_section(convert_periods_to_years=period_length)
fm.import_transitions_section(convert_periods_to_years=period_length)
fm.initialize_areas()
fm.add_null_action()
fm.reset_actions()

fm.actions["harvest"].is_harvest = True # set harvest action to be a harvest action (needed for `cmp_c_z` to work correctly)

[7]:
problem = run_scenario(fm, scenario_name="base-cgen_gs", print_df=True, workers=1)
running base scenario plus growing stock constraints
   period        oha           ohv            ogs
0       1  92.742411  12945.008110  135863.963286
1       2  97.379532  13592.258515  132839.040537
2       3  88.105291  13592.258515  128034.433647
3       4  90.176925  12297.757704  125301.751940
4       5  90.366370  12297.757704  124421.651363
5       6  91.904290  12297.757704  124170.013825
6       7  91.433562  12297.757704  124057.295448
7       8  90.561107  12297.757704  123503.944077
8       9  89.863045  12297.757704  122048.775279
9      10  97.379532  12297.757704  120000.000000
../_images/examples_021_ws3_model_example-optimize-basic_15_1.png

4.4.3. Formulate and solve optimization models

Use the run_scenario function (defined in the local util module) to formulate and solve various flavours of pre-defined scenarios. If only provided with an fm instance as an argument, the function will run the base scenario, which maximizes even-flow harvest volume.

The default HiGHS solver bindings will be used unless otherwise specified. Solver output is turned off by default, but we can turn it on by passing verbose=True argument to the run_scenario function that gets passed to the ws3.opt.Problem.solve method. This will show what the standard console output from each solver looks like, and let us confirm that the LP problem definition (matrix shape and number of nonzero coefficients) is corectly sent to each solver backend, that the objective function value for the optimal solution is the same for all solver backends, and the time each solver takes to solve our problem (negligible, because this problem is very small).

The different solver backends have pros and cons.

The default HiGHS bindings work well, use a fully open-source codebase (so are free to use and free to distribute), and have built-in parallel dual simplex algorithms that can run on up to 8 cores. The highspy bindnings also allow ws3 to pass the LP problem definition to the solver backend in an efficient way that keeps problem-building time relatively low, and provide full access to advanced HiGHS features for maximum flexibility in tweaking solver behaviour.

The PuLP bindings run more slowly than the default HiGHS bindings (largely because PuLP is a middleware solution that provides a standardized interface to multiple solver backends, so ws3 must first pass the LP problem to PuLP, and PuLP then has to build its own internal representation of the problem before passing it to a solver backend, and then the backend needs to build its own internal represenation of the problem before solving it). The PuLP bindings are interesting because they provide a single interface that is not specific to a particular solver backend (so it can be used with any solver that PuLP supports). The PuLP bindings are also fully open source (so free to use and free to distribute). ws3 PuLP binding currently support two solver backends (the default CBC and HiGHS, which can be set via the solver_backend parameter. It is relatively easy to add support for more solvers, so eventually we plan to support more solvers through the PuLP bindings.

The Gurobi solver is one of the fastest and most powerful solvers available, so this might be a good choice for larger more difficult LP problems. Gurobi is closed source commercial software (so not free to use or distribute). The gurobipy Python package allows solving of small problems without requiring an active softwarwe licence to be installed and detected, which is how we can demonstrate Gurobi use here. Gurobi is quite generous with providing an unlimited number of full no-cost licenses for academic use, so if you are a researcher you should be able to use Gurobi for full-sized problems in ws3 (although licence acquisition and setup is a bit complicated and finicky).

[8]:
fig, df, p = run_scenario(fm, verbose=True)
running base scenario
Running HiGHS 1.11.0 (git hash: 364c83a): Copyright (c) 2025 HiGHS under MIT licence terms
LP   has 72 rows; 305 cols; 3425 nonzeros
Coefficient ranges:
  Matrix [3e-02, 4e+04]
  Cost   [2e+01, 4e+04]
  Bound  [1e+00, 1e+00]
  RHS    [1e+00, 1e+00]
Presolving model
60 rows, 297 cols, 3201 nonzeros  0s
Dependent equations search running on 24 equations with time limit of 1000.00s
Dependent equations search removed 0 rows and 0 nonzeros in 0.00s (limit = 1000.00s)
60 rows, 297 cols, 3201 nonzeros  0s
Presolve : Reductions: rows 60(-12); columns 297(-8); elements 3201(-224)
Solving the presolved LP
Using EKK parallel dual simplex solver - SIP with concurrency of 8
  Iteration        Objective     Infeasibilities num(sum)
          0     0.0000000000e+00 Ph1: 0(0) 0s
        115    -1.9380707345e+05 Pr: 0(0); Du: 0(1.02318e-12) 0s
Solving the original LP from the solution after postsolve
Model status        : Optimal
Simplex   iterations: 115
Objective value     : -1.9380707345e+05
P-D objective error :  1.5016869536e-16
HiGHS run time      :          0.01
../_images/examples_021_ws3_model_example-optimize-basic_18_1.png

Run the same scenario using the CBC solver by way of PuLP bindings. The results should be identical to the he previous scenario (run using default HiGHS solver).

[9]:
fig, df, p = run_scenario(fm, solver=ws3.opt.SOLVER_PULP, verbose=True)
running base scenario
Welcome to the CBC MILP Solver
Version: 2.10.3
Build Date: Dec 15 2019

command line - /home/gep/projects/ws3/.venv/lib/python3.12/site-packages/pulp/apis/../solverdir/cbc/linux/i64/cbc /tmp/583d20d6781646168ae2a94d98328e95-pulp.mps -max -threads 0 -timeMode elapsed -branch -printingOptions all -solution /tmp/583d20d6781646168ae2a94d98328e95-pulp.sol (default strategy 1)
At line 2 NAME          MODEL
At line 3 ROWS
At line 77 COLUMNS
At line 3776 RHS
At line 3849 BOUNDS
At line 4155 ENDATA
Problem MODEL has 72 rows, 305 columns and 3425 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Presolve 60 (-12) rows, 273 (-32) columns and 3177 (-248) elements
0  Obj -0 Dual inf 2157186.5 (273)
31  Obj 356474.73 Primal inf 656.98798 (31)
62  Obj 209236.84 Primal inf 115.74605 (25)
96  Obj 196675.93 Primal inf 32.967855 (17)
127  Obj 193916.47 Primal inf 1.388767 (7)
137  Obj 193807.07
Optimal - objective value 193807.07
After Postsolve, objective 193807.07, infeasibilities - dual 0 (0), primal 0 (0)
Optimal objective 193807.0735 - 137 iterations time 0.012, Presolve 0.00
Option for printingOptions changed from normal to all
Total time (CPU seconds):       0.01   (Wallclock seconds):       0.01

../_images/examples_021_ws3_model_example-optimize-basic_20_1.png

Run the same scenario using the Gurobi solver bindings. The results should be identical to the he previous scenario (run using default HiGHS solver).

[10]:
fig, df, p = run_scenario(fm, solver=ws3.opt.SOLVER_GUROBI, verbose=True)
running base scenario
Restricted license - for non-production use only - expires 2026-11-23
Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - "Ubuntu 24.04.3 LTS")

CPU model: Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 36 physical cores, 72 logical processors, using up to 32 threads

Optimize a model with 72 rows, 305 columns and 3425 nonzeros
Model fingerprint: 0x4d7c1bc0
Coefficient statistics:
  Matrix range     [3e-02, 4e+04]
  Objective range  [2e+01, 4e+04]
  Bounds range     [0e+00, 0e+00]
  RHS range        [1e+00, 1e+00]
Presolve removed 12 rows and 32 columns
Presolve time: 0.01s
Presolved: 60 rows, 273 columns, 3177 nonzeros

Iteration    Objective       Primal Inf.    Dual Inf.      Time
       0    2.2455285e+05   1.160851e+04   0.000000e+00      0s
      75    1.9380707e+05   0.000000e+00   0.000000e+00      0s

Solved in 75 iterations and 0.02 seconds (0.00 work units)
Optimal objective  1.938070735e+05
../_images/examples_021_ws3_model_example-optimize-basic_22_1.png

Run the base-cgen_ha scenario, which maximizes even-flow harvest volume plus adds a maximum (100 ha) harvest area constraint on period 1.

[11]:
fig, df, p = run_scenario(fm, scenario_name="base-cgen_ha", print_df=True)
running base scenario plus harvest area constraints
   period    oha           ohv            ogs
0       1  100.0  14796.128110  133686.175050
1       2  105.0  14104.788476  130186.412830
2       3  105.0  14056.321705  125559.065143
3       4  105.0  14056.321705  121504.669660
4       5  105.0  14521.334872  118497.889467
5       6  105.0  15535.934516  114355.356640
6       7  105.0  15535.934516  108958.499863
7       8   95.0  15535.934516  101915.256834
8       9  105.0  15535.934516   94001.947263
9      10  105.0  15535.934516   85148.810804
../_images/examples_021_ws3_model_example-optimize-basic_24_1.png

Next, we run the basease-cgen_hv scenario, which maximizes even-flow harvest volume plus adds a maximum harvest volume (10 000 \(m^3\) per 10-year period) constraint on period 1.

[12]:
problem = run_scenario(fm, scenario_name="base-cgen_hv", print_df=True)
running base scenario plus harvest volume constraints
   period        oha      ohv            ogs
0       1  78.730132  10000.0  139328.678709
1       2  74.793625  10500.0  140264.445013
2       3  77.290264  10500.0  139665.672672
3       4  81.534783  10500.0  138957.035504
4       5  81.409579  10500.0  139777.840122
5       6  78.426734  10500.0  140249.898543
6       7  81.699726  10500.0  139167.789849
7       8  77.691454  10500.0  137397.593486
8       9  82.666639  10500.0  136223.388524
9      10  82.666639  10500.0  133975.635313
../_images/examples_021_ws3_model_example-optimize-basic_26_1.png

Next, we run the basease-cgen_gs scenario, which maximizes even-flow harvest volume plus adds a minimum growning stock volume (120 000 \(m^3\)) constraint on period 10.

[13]:
problem = run_scenario(fm, scenario_name="base-cgen_gs", print_df=True)
running base scenario plus growing stock constraints
   period        oha           ohv            ogs
0       1  92.742411  12945.008110  135863.963286
1       2  97.379532  13592.258515  132839.040537
2       3  88.105291  13592.258515  128034.433647
3       4  90.176925  12297.757704  125301.751940
4       5  90.366370  12297.757704  124421.651363
5       6  91.904290  12297.757704  124170.013825
6       7  91.433562  12297.757704  124057.295448
7       8  90.561107  12297.757704  123503.944077
8       9  89.863045  12297.757704  122048.775279
9      10  97.379532  12297.757704  120000.000000
../_images/examples_021_ws3_model_example-optimize-basic_28_1.png

The ws3 optimization problem-building functions include a parallelized function that runs the model in parallel. This is useful for problems with many decision variables or large numbers of periods to solve. The parallelization is controlledd by the workers argument to the ForestModel.add_problem method, which gets passed down through to several subordinate functions that handle various parts of the model-buildling process.

ws3 is configured to run in serial (single-core) mode by default, so we need to activate parallel model generation by setting the workers argument. The run_scenario function we defined in the local util module has a workers argument that we can use to activate parallel model building mode. We run the same base-cgen_gs scenario in parallel by setting the workers argument to a number greater than one. Note that the model output is identical to the serial version of the model (which is what we would want and expect).

For a tiny model like the one we are using here for we will not see much of an advantage to running in parallel, but for larger models (with more development types, periods, or action options) we can expect to see a substantial speedup in the model-building step by running in parallel. The current implementation is tuned to run well for between 8 to 16 cores, but more cores could work well for larger models.

[14]:
problem = run_scenario(fm, scenario_name="base-cgen_gs", print_df=True, workers=4)
running base scenario plus growing stock constraints
   period        oha           ohv            ogs
0       1  92.742411  12945.008110  135863.963286
1       2  97.379532  13592.258515  132839.040537
2       3  88.105291  13592.258515  128034.433647
3       4  90.176925  12297.757704  125301.751940
4       5  90.366370  12297.757704  124421.651363
5       6  91.904290  12297.757704  124170.013825
6       7  91.433562  12297.757704  124057.295448
7       8  90.561107  12297.757704  123503.944077
8       9  89.863045  12297.757704  122048.775279
9      10  88.105291  12297.757704  120000.000000
../_images/examples_021_ws3_model_example-optimize-basic_30_1.png