3.2.3. forest module

This module implements functions for building and running the wood supply simulation models.

The ForestModel and DevelopmentType classes constitute the core functional units of this module, and of the ws3 package in general.

class forest.Action(code, targetage=None, descr='', lockexempt=False, components=None, partial=None, is_harvest=0, is_sticky=0)[source]

Bases: object

Encapsulates data for an action.

class forest.DevelopmentType(key, parent)[source]

Bases: object

Encapsulates Forest development type data (curves, age, area), and provides methods to operate on the data.

The key is basically the fully expanded mask (expressed as a tuple of values). The parent is a reference to the ForestModel object in which self is embedded.

add_ycomp(ytype, yname, ycomp, first_match=True)[source]

Adds a yield component to the yield components.

Parameters:
  • ytype (str) – The type of yield component to add (‘c’ for complex).

  • yname (str) – The name of the yield component.

  • ycomp (str) – The yield component to add.

  • first_match (bool) – Flag indicating whether to only add the component if it doesn’t already exist. Defaults to True.

area(period, age=None, area=None, delta=True)[source]

If area not specified, returns area inventory for period (optionally age), else sets area for period and age. If delta switch active (default True), area value is interpreted as an increment on current inventory.

Parameters:
  • period (int) – The period for which the area is being retrieved or set.

  • age (int) – The age for which the area is being retrieved or set. If None, returns total area.

  • area (float) – The area value to set. If None, returns the area inventory.

  • delta (bool) – If True (default), interprets the area value as an increment on the current inventory. If False, sets the area value directly.

compile_action(acode, verbose=False)[source]

Compile action, given action code. This mostly involves resolving operability expression strings into lower and upper operability limits, defined as (alo, ahi) age pair for each period. Deletes action from self if not operable in any period.

Parameters:
  • acode (str) – The action code.

  • verbose (bool) – Verbosity flag. Defaults to False.

compile_actions(verbose=False)[source]

Compile all actions.

Parameters:

verbose (bool) – Verbosity flag. Defaults to False.

grow(start_period=1, cascade=True)[source]

Grow self (default starting period 1, and cascading to end of planning horizon).

Parameters:
  • start_period (int) – The starting period for growth (default is 1).

  • cascade (bool) – If True, growth cascades to the end of the planning horizon. Default is True.

initialize_areas()[source]

Copy initial inventory to period-1 inventory.

is_operable(acode, period, age=None, verbose=False)[source]

Test hypothetical operability. Does not imply that there is any operable area in current inventory.

Parameters:
  • acode (str) – The action code to test operability for.

  • period (int) – The period to test operability for.

  • age (int) – The age to test operability for. If None, only checks operability for the period.

  • verbose (bool) – If True, prints additional information for debugging purposes. Default is False.

operable_ages(acode, period)[source]

Finds list of ages at which self is operable, given an action code and period index.

operable_area(acode, period, age=None, cleanup=True)[source]

Returns 0 if inoperable or no current inventory, operable area given action code and period (and optionally age) index otherwise.

Parameters:
  • acode (str) – The action code to determine operability.

  • period (int) – The period to determine operability for.

  • age (int) – The age to determine operability for. If None, only checks operability for the period.

  • cleanup (bool) – If True (default), removes the age class from the inventory dict if operable area is less than self.parent.area_epsilon.

overwrite_initial_areas(period)[source]

Overwrites the initial areas for a specified period.

reset_areas(period=None)[source]

Reset areas dictionary.

resolve_condition(yname, lo, hi)[source]

Find lower and upper ages that correspond to lo and hi values of yname (interpreted as first occurence of yield value, reading curve from left and right, respectively).

ycomp(yname, silent_fail=True)[source]

Returns the yield components associated with the given yield name. Returns None if the yield name is not found and silent_fail is True.

Parameters:
  • yname (str) – The name of the yield to retrieve components for.

  • silent_fail (bool) – If True (default), returns None if the yield name is not found. If False, raises a KeyError that yield name is not found.

ycomps()[source]

Returns list of yield component keys.

class forest.ForestModel(model_name, model_path, base_year, horizon=30, period_length=10, max_age=1000, area_epsilon=0.01, curve_epsilon=0.01)[source]

Bases: object

This is the core class of the ws3 package. Includes methods import data from various sources, simulate growth and apply actions. The model can be used in either a (prescriptive) simulation-based approach or a (descriptive) optimization-based approach.

This class encapsulates all the information used to simulate scenarios from a given dataset (i.e., stratified intial inventory, growth and yield functions, action eligibility, transition matrix, action schedule, etc.), as well as a large collection of functions to import and export data, generate activity schedules, and simulate application of these schedules (i.e., run scenarios).

At the heart of the ForestModel class is a list of DevelopentType instances. Each DevelopmentType instance encapsulates information about one development type (i.e., a forest stratum, which is an aggregate of smaller stands that make up the raw forest inventory input data). The DevelopmentType class also stores a list of operable actions, maps state variable transitions to these actions, stores growth and yield functions, and knows how to grow itself when time is incremented during a simulation.

A typical use case starts with creating an instance of the ForestModel class. Then, we need to load data into this instance, define one or more scenarios (using a mix of heuristic and optimization approaches), run the scenarios, and export output data to a format suitable for analysis (or link to the next model in a larger modelling pipeline).

Initializes the ForestModel with the provided parameters.

Parameters:
  • model_name (str) – The name of model.

  • model_path (str) – The path to input data of model.

  • base_year (int) – The base year of teh model.

  • horizon (int) – The length (in number of periods) of the simulation horizon.

  • max_age (int) – The maximum age considered in the model.

  • area_epsilon (int)

  • curve_epsilon (int)

add_null_action(acode='null', minage=None, maxage=None)[source]

Adds a null action with the specified action code, minimum age (default is None), and maximum age (default is None).

add_problem(name, coeff_funcs, cflw_e=None, cgen_data=None, solver='highs', formulation=1, z_coeff_key='z', acodes=None, sense=-1, mask=None, workers=1, verbose=False)[source]

Add an optimization problem to the model.

Parameters:
  • name (str) – Used as key to store ws3.opt.Problem instances in a dict in the ws3.forest.ForestModel instanace, so make sure it is unique within a given model or you will overwrite dict values (assuming you want to stuff multiple problems, and their solutions, into your model at the same time).

  • coeff_funcs (dict) – Dict of function references, keyed on row name strings. These are the functions that generate the LP optimization problem matrix coefficients (for the objective function and constraint rows). This one gets complicated, and is a likely source of bugs. Make sure the row name key strings are all unique or you will make a mess. You can name the constraint rows anything you want, but the objective function row has to be named ‘z’. All coefficient functions must accept exactly two args, in this order: a ws3.forest.ForestModel instance and a path (a tuple of ws3.core.Node object instances). The ‘z’ coefficient function is special in that it must return a single float value. All other (i.e., constraint) coefficient functions just return a dict of floats, keyed on period ints (can be sparse, i.e., not necessary to include key:value pairs in output dict if value is 0.0). It is useful (but not necessary) to use functools.partial to specialize a smaller number of more general function definitions (with more args, that get “locked down” and hidden by partial) as we have done in the example in this notebook.

  • cflw_e (dict) –

    Dict of (dict, int) tuples, keyed on row name strings (must match row name key values used to define coefficient functions for flow constraints in coeff_func dict), where the int:float dict embedded in the tuple defines epsilon values keyed on periods (must include all periods, even if epsilon value is always the same). See example below.

    {'foo':({1:0.01, ..., 10:0.01}, 1), 'bar':({1:0.05, ..., 10:0.05}, 1)}

  • cgen_data (dict) –

    Dict of dict of dicts. The outer-level dict is keyed on row name strings (must match row names used in coeff_funcs. The middle second level of dicts always has keys ‘lb’ and ‘ub’, and the inner level of dicts specifies lower- and upper-bound general constraint RHS (float) values, keyed on period (int). See example below.

    {'foo':{'lb':{1:1., ..., 10:1.}, 'ub':{1:2., ..., 10:2.}}, 'bar':{{'lb':{1:1., ..., 10:1.}, 'ub':{1:2., ..., 10:4.}}}}

  • acodes (int) – List of strings. Action codes to be included in optimization problem formulation (actions must defined in the ForestModel instance, but can be only a subset).

  • sense (int) – Must be one of ws3.opt.SENSE_MAXIMIZE or ws3.opt.SENSE_MINIMIZE, or equivalent int values (just use the constants to keep code more legible).

  • mask (tuple) – Tuple of strings constituting a valid mask for your ForestModel instance. Can be None if you do not want to filter DevelopmentType instances.

  • workers (int) – Number of worker threads to use for parallel processing.

Returns:

ws3.opt.Problem. Reference to a new Problem instance that was created. Also stored in the ForestModel instance (problems attribute, keyed on problem name).

add_theme(name, basecodes=[], aggs={}, description='')[source]

Adds a theme to the model.

Parameters:
  • name (str) – The name of theme.

  • basecodes (list) – List of base codes for the theme.

  • aggs (dict) – Dictionary containing aggregated values for the theme.

  • description (str) – Description of the theme.

age_class_distribution(period, mask=None, omit_null=False)[source]

Returns age class distribution (dict of areas, keys on age).

Parameters:
  • period (int) – The period for which to retrieve the age class distribution.

  • mask (tuple) – A mask to filter development types. Default is None.

  • omit_null (bool) – If True, omits null areas from the distribution. Default is False.

Returns:

A dictionary where keys are ages and values are the corresponding area distributions.

apply_action(dtype_key, acode, period, age, area, override_operability=False, fuzzy_age=True, recourse_enabled=True, areaselector=None, compile_t_ycomps=False, compile_c_ycomps=False, verbose=False)[source]

Applies action, given action code, development type, period, age, area. Can optionally override operability limits, optionally use fuzzy age (i.e., attempt to apply action to proximal age class if specified age is not operable), optionally use default AreaSelector to patch missing area (if recourse enabled). Applying an action is a rather complex process, involving testing for operability (JIT-compiling operability expression as required), checking that valid transitions are defined, checking that area is available (possibly using fuzzy age and area selector functions to find missing area), generate list of target development types (from source development type and transition expressions [which may need to be JIT-compiled]), creating new development types (as needed), doing the area accounting correctly (without creating or destroying any area) and compiling the products from the action (which gets a bit complicated in the case of partial cuts…).

Parameters:
  • dtype_key (tuple) – The key identifying the development type.

  • acode (str) – The action code to apply.

  • period (int) – The period in which to apply the action.

  • age (int) – The age at which to apply the action.

  • area (float) – The area to apply the action on.

  • override_operability (bool) – If True, overrides operability limits. Default is False.

  • fuzzy_age (bool) – If True, attempts to apply action to proximal age class if specified age is not operable.

  • recourse_enabled (bool) – If True, uses default AreaSelector to patch missing area. Default is True.

  • areaselector (bool) – The AreaSelector object to use for patching missing area. Default is None.

  • compile_t_ycomps (bool) – If True, compiles time-indexed yield components. Default is False.

  • compile_c_ycomps (bool) – If True, compiles complex yield components. Default is False.

  • verbose (bool) – If True, prints additional information for debugging purposes. Default is False.

Returns (errorcode, missing_area, target_dt) triplet, where errorcode is an error code, missing_area is the missing area, and target_dt is a list of (dtk, tprop, targetage) triplets (one triplet per target development type).

Returns:

A tuple containing errorcode, missing_area, and target_dt.

Error codes: 1. invalid area argument 2. requested action not defined for development type 3. requested action defined, but never operable 4. action not operable 5. transitions not defined for action

apply_schedule(schedule, max_period=None, verbose=False, fail_on_missingarea=False, force_integral_area=False, override_operability=False, fuzzy_age=True, recourse_enabled=True, areaselector=None, compile_t_ycomps=False, compile_c_ycomps=False, rounding_bias=0.15, scale_area=None, reset=True)[source]

Assumes schedule in format returned by import_schedule_section(). That is: list of (dtype_key, age, area, acode, period, etype) tuples. Also assumes that actions in list are sorted by applied period.

Parameters:
  • schedule (list) – The schedule of actions to apply.

  • max_period (int) – The maximum period to apply actions for. If None, defaults to the horizon.

  • verbose (bool) – If True, prints additional information for debugging purposes. Default is False.

  • fail_on_missingarea (bool) – If True, raises an exception if missing area is encountered. Default is False.

  • force_integral_area (bool) – If True, forces the area to be integral. Default is False.

  • override_operability (bool) – If True, overrides operability limits. Default is False.

  • fuzzy_age (bool) – If True, attempts to apply action to proximal age class if specified age is not operable. Default is True.

  • recourse_enabled (bool) – If True, uses default AreaSelector to patch missing area. Default is True.

  • areaselector (object) – The AreaSelector object to use for patching missing area. Default is None.

  • compile_t_ycomps (bool) – If True, compiles time-indexed yield components. Default is False.

  • compile_c_ycomps (bool) – If True, compiles complex yield components. Default is False.

  • rounding_bias (float) – The rounding bias to use when forcing integral area. Default is 0.15.

  • scale_area – The scaling factor to apply to the area. Default is None.

  • reset (bool) – If True, resets the model before applying the schedule. Default is True.

Returns:

The missing area (float) after applying the schedule.

commit_actions(period=1, repair_future_actions=False, verbose=False)[source]

Commits applied actions (i.e., apply transitions and grow, default starting at period 1). By default, will attempt to repair broken (infeasible) future actions, attempting to replace infeasiblea operated area using default AreaSelector.

compile_actions(mask=None, verbose=False)[source]

Compile actions for the development types filtered by mask.

compile_product(period, expr, acode=None, dtype_keys=None, age=None, coeff=False, verbose=False)[source]

Compiles products from applied actions in given period. Parses string expression, which resolves to a single coefficient. Operated area can be filtered on action code, development type key list, and age. Result is product of sum of filtered area and coefficient.

compile_schedule(problem=None)[source]

Compiles the schedule of actions. If a problem is specified, compiles the schedule from the given problem data. Otherwise, compiles the schedule from the data in self.applied_actions.

create_dtype_fromkey(key)[source]

Creates a new development type, given a key (checks for existing, auto-assigns yield compompontents, auto-assign actions and transitions, checks for operability (filed under inoperable if applicable).

dt(dtype_key)[source]

Returns development type, given key (returns None on invalid key).

grow(start_period=1, cascade=True)[source]

Simulates growth (default startint at period 1 and cascading to the end of the planning horizon).

import_actions_section(filename_suffix='act', mask_func=None, nthemes=None, convert_periods_to_years=None)[source]

Imports ACTIONS section from a Forest model.

Note

  • This method parses the ACTIONS section from a Forest model file.

  • The section should contain information about actions, operability, aggregates, and partials.

  • Each action is represented by a code, description, and operability condition.

  • Operability conditions are defined for different masks.

  • Aggregates combine multiple actions.

  • Partials represent components of an action.

import_areas_section(model_path=None, model_name=None, filename_suffix='are', import_empty=False, convert_periods_to_years=None)[source]

Imports AREAS section from a Forest model.

Note

  • This method parses the AREAS section from a Forest model file.

  • Each line in the section represents an area for a specific development type and age class.

  • The section should contain information about development type keys, ages, and corresponding areas.

  • Empty areas (with values less than the area_epsilon) can be skipped if import_empty is False.

import_constants_section(filename_suffix='con')[source]

Imports CONSTANTS section from a Forest model.

Note

  • This method parses the CONSTANTS section from a Forest model file.

  • Each line in the section represents a constant and its value.

  • Constants are stored in a dictionary where the keys are the constant names and the values are their respective values.

  • The section should contain information about various constants used in the model.

import_control_section(filename_suffix='run')[source]

Imports CONTROL section from a Forest model.

Warning

Not implemented yet.

import_graphics_section(filename_suffix='gra')[source]

Imports GRAPHICS section from a Forest model.

Warning

Not implemented yet.

import_landscape_section(filename_suffix='lan', ti_offset=0)[source]

Imports LANDSCAPE section from a Forest model.

import_lifespan_section(filename_suffix='lif')[source]

Imports LIFESPAN section from a Forest model.

Warning

Not implemented yet.

import_optimize_section(filename_suffix='opt')[source]

Imports OPTIMIZE section from a Forest model.

Warning

Not implemented yet.

import_outputs_section(filename_suffix='out')[source]

Imports OUTPUTS section from a Forest model.

import_schedule_section(filename_suffix='seq', replace_commas=True, filename_prefix=None, convert_periods_to_years=None)[source]

Imports SCHEDULE section from a Forest model.

import_transitions_section(filename_suffix='trn', mask_func=None, nthemes=None, convert_periods_to_years=None)[source]

Imports TRANSITIONS section from a Forest model.

import_yields_section(filename_suffix='yld', mask_func=None, verbose=False, convert_periods_to_years=None)[source]

Imports YIELDS section from a Forest model.

initialize_areas(reset_areas=True)[source]

Copies areas from period 0 to period 1.

inventory(period, yname=None, age=None, mask=None, dtype_keys=None, verbose=0)[source]

Flexible method that compiles inventory at given period. Unit of return data defaults to area if yname not given, but takes on unit of yield component otherwise. Can be constrained by age and development type mask.

is_harvest(acode)[source]

Returns True if acode corresponds to a harvesting action.

match_mask(mask, key)[source]

Returns True if key matches mask.

nthemes()[source]

Returns number of themes

operable_area(acode, period, age=None, mask=None)[source]

Returns total operable area, given action code and period (and optionally age).

operable_dtypes(acode, period, mask=None)[source]

Returns dict (keyed on development type key, values are lists of operable ages).

operated_area(acode, period, dtype_key=None, age=None)[source]

Compiles operated area, given action code and period (and optionally list of development type keys or age).

overwrite_initial_areas(period)[source]

Overwrites the initial areas for all development types for the specified period.

piece_size(dtype_key, age)[source]

Returns piece size, given development type key and age.

register_curve(curve)[source]

Add curve to global curve hash map (uses result of Curve.points() to construct hash key).

repair_actions(period, areaselector=None, verbose=False)[source]

Attempts to repair the action schedule for given period, using an AreaSelector object (defaults to class-default areaselector, which is a simple greedy oldest-first selector).

reset()[source]

Resets the forest model by clearing applied actions and reinitializing areas.

reset_actions(period=None, acode=None, override_sticky=False)[source]

Resets actions. By default resets, all actions in all periods (except for sticky actions, unless overridden), unless period or acode specified.

reset_areas(period=None)[source]

Reset areas for all development types.

resolve_append(dtk, expr)[source]

This method has not been implemented yet.

resolve_condition(condition, dtype_key=None)[source]

Evaluate @AGE or @YLD condition. Returns list of ages.

resolve_replace(dtk, expr)[source]

Enables the creation of new development types by replacing an existing attribute code with a new value for a specific theme, instead of directly coding the attribute change in transition.

resolve_tappend(dt, tappend)[source]

This feature has not been implemented yet.

resolve_targetage(dtk, tyield, sage, tage, acode, verbose=False)[source]

This method determines the target age for a transition based on different parameters such as the development type key (dtk), yield information (tyield), stand age (sage), target age override (tage), and action code (acode).

resolve_tmask(dt, tmask, treplace, tappend)[source]

Returns new developement type key (tuple of values, one per theme), given developement type and (treplace, tappend) expressions.

resolve_treplace(dt, treplace)[source]
set_horizon(horizon)[source]

Sets the horizon of the model.

This method updates the horizon of the model to the specified value and adjusts the list of periods accordingly.

sylv_cred_formula(treatment_type, cover_type)[source]

Calculate Sylviculture Credits based on treatment type and cover type.

theme_basecodes(theme_index)[source]

Return list of base codes, given theme index.

to_cbm_sit(softwood_volume_yname, hardwood_volume_yname, admin_boundary, eco_boundary, disturbance_type_mapping, export_csv=False, sit_data_path='', default_last_pass_disturbance='fire', n_yield_vals=100, include_empty_dtypes=False)[source]

Exports model data in a CBM standard import tool (SIT) data exchange format. Returns sit_config (JSON-like dict namespace) and sit_tables (dict of pandas.DataFrame objects).

Parameters:
  • softwood_volume_yname (str) – The yield component name for softwood volume.

  • hardwood_volume_yname (str) – The yield component name for hardwood volume.

  • admin_boundary (str) – The administrative boundary for spatial units mapping.

  • eco_boundary (str) – The ecological boundary for spatial units mapping.

  • disturbance_type_mapping (dict) – A dictionary containing disturbance type mapping information.

  • export_csv (bool) – Flag indicating whether to export data to CSV files. Default is False.

  • sit_data_path (str) – The path to export CSV files. Default is empty string.

  • default_last_pass_disturbance (str) – The default last pass disturbance type. Default is ‘fire’.

  • n_yield_vals (int) – The number of yield values. Default is 100.

tree()
unmask(mask, verbose=0)[source]

Iteratively filter list of development type keys using mask values. Accepts Woodstock-style string masks to facilitate cut-and-paste testing.

class forest.GreedyAreaSelector(parent)[source]

Bases: object

Default AreaSelector implementation. Selects areas for treatment from oldest age classes.

operate(period, acode, target_area, mask=None, commit_actions=True, verbose=False)[source]

Greedily operate on oldest operable age classes. Returns missing area (i.e., difference between target and operated areas).

Parameters:
  • period (int) – The time period for the operation.

  • acode (str) – The action code to specify the action.

  • target_area (float) – The desired area to be achieved through operation.

  • mask (tuple) – Tuple of values for development types.

  • commit_actions (bool) – Flag indicating whether to commit actions. Defaults to True.

  • verbose (bool) – Verbosity flag. Defaults to False.

class forest.Output(parent, code=None, expression=None, factor=(1.0, 1), description='', theme_index=-1, is_basic=False, is_level=False)[source]

Bases: object

Encapsulates data and methods to operate on aggregate outputs from the model. Emulates behaviour of Forest outputs.

Warning

Behaviour of Forest outputs is quite complex. This class needs more work before it is used in a production setting (i.e., resolution of some complex output cases is buggy).