pyforestry.base.pricelist package#

Submodules#

pyforestry.base.pricelist.pricelist module#

Pricelist utilities and interfaces.

A price list is where a projection’s money comes from, so it carries a PricelistIdentity – a name, the currency its prices are quoted in, and who published it – alongside the prices themselves. That is what lets a run manifest state what its revenue figures are in and which list produced them, rather than leaving two runs’ money columns indistinguishable.

class pyforestry.base.pricelist.pricelist.DiameterRange(Min: float, Max: float)[source]#

Bases: object

The inclusive top-diameter span, in cm, over which an assortment is priced.

class pyforestry.base.pricelist.pricelist.LengthCorrections(corrections: Dict[int, Dict[int, int]] | None = None)[source]#

Bases: object

Holds logic for how the length modifies price (absolute or percent).

Now accepts a dictionary of corrections in the form:

{diameter: {length: correction_percentage, ...}, ...}
get_length_correction(diameter: int, log_part: int | None, length: int) → int[source]#

Returns the correction percentage for a given diameter and log length. Looks up the corrections dictionary for the closest available length (floored). Returns 0 if no correction applies.

class pyforestry.base.pricelist.pricelist.LengthRange(Min: float, Max: float)[source]#

Bases: object

The inclusive log-length span, in metres, an assortment accepts.

Metres because that is what every price list in this package states and what the only consumer reads: Nasberg_1985_BranchBound converts these to decimetres by multiplying by ten. This docstring used to say decimetres and the Pricelist defaults were written as decimetres – LengthRange(30, 50) – which the bucker then read as thirty to fifty metres, so no log a Swedish forest grows could satisfy them.

class pyforestry.base.pricelist.pricelist.Pricelist(*, identity: PricelistIdentity | None = None)[source]#

Bases: object

Holds the combined pulpwood, timber, etc. prices and constraints.

Alongside the prices it carries its PricelistIdentity: what the list is called, what money its prices are in, and who published it. Every monetary figure a projection reports comes from here, so a run that records what it earned records that too.

get_pulpwood_fuelwood_proportion(species: str | TreeName) → float[source]#

Proportion (0-1) of a pulpwood log downgraded to fuelwood.

Placeholder returning 0.0 (no downgrade); override per species as needed. See get_pulpwood_waste_proportion().

get_pulpwood_waste_proportion(species: str | TreeName) → float[source]#

Proportion (0-1) of a pulpwood log downgraded to waste/harvest residue.

Placeholder returning 0.0 (no downgrade), mirroring the timber-side TimberPricelist.get_timber_weight() hook. Consumed by the pulp branch of the Näsberg (1985) bucking optimiser when BuckingConfig.use_downgrading is set; override per species once real downgrade data is available.

load_from_dict(price_data: dict)[source]#

Loads and configures the entire pricelist from a dictionary.

class pyforestry.base.pricelist.pricelist.PricelistIdentity(name: str, currency: str, source: SourceReference)[source]#

Bases: object

Which price list a figure of money came from, and what money it is in.

A price list is what turns a volume into a sum, so every monetary figure a run reports is a figure in this list’s currency, against this list’s prices. Two runs’ revenues are comparable only under the same list, which is why a run that reports money records this: run_scenario() writes it into its manifest’s valuation block.

Parameters:
  • name – What the list is called, e.g. "Mellanskog 2013".

  • currency – The currency its prices are quoted in, e.g. "SEK". Per cubic metre; which volume basis is each timber table’s own business, declared in TimberPricelist.volume_type, so it is not repeated here.

  • source – Who published it, and when. A price list is regional market data with a publisher and a year, so this is a genuine citation rather than a stated choice – and a table assembled by whoever ran the model says so through the (none)/year-0 sentinel that UNATTRIBUTED_PRICELIST_IDENTITY carries.

Raises:

ValueError – If the name or the currency is blank. An unnamed list is a real state, and UNATTRIBUTED_PRICELIST_IDENTITY names itself as one; an empty string in a manifest names nothing.

property is_cited: bool#

Whether this list traces to a publisher rather than to whoever ran the model.

class pyforestry.base.pricelist.pricelist.PulpPricelist[source]#

Bases: object

Placeholder for pulp prices per species.

get_pulpwood_price(species: str | TreeName) → int[source]#

Try to find the price for a species by first looking for a full name match. If none is found, look for a match on just the genus. Returns a default price if no match is found.

class pyforestry.base.pricelist.pricelist.TimberPriceForDiameter(butt_price: float, middle_price: float, top_price: float)[source]#

Bases: object

Represents the set of prices for a given diameter, for each log part type. E.g. an entry might store: PriceButt, PriceMiddle, PriceTop, …

price_for_log_part(part_type: int) → float[source]#

Return the price (in e.g. SEK/m3) for the given part type index.

class pyforestry.base.pricelist.pricelist.TimberPricelist(min_diameter: int, max_diameter: int, volume_type: str = 'm3to')[source]#

Bases: object

Stores the entire set of timber prices by diameter class, etc.

class LogParts(*values)[source]#

Bases: IntEnum

Where along the stem a log came from, which is what it is priced by.

A butt log, a middle log and a top log of the same dimensions fetch different prices, so every price lookup is keyed by this alongside the diameter class. The integer values are the column order the price tables use.

Butt = 0#
Middle = 1#
Top = 2#
get_nearest_diameter_class(diameter_cm: float) → int[source]#

Returns the closest available diameter class (floored down to available class). If the requested diameter is smaller than min, returns min. If larger than max, returns max.

get_timber_weight(log_part: LogParts)[source]#

If you’re applying downgrading or certain proportions for pulp/fuel/cull, this returns an object with attributes like .PulpwoodPercentage, .FuelWoodPercentage and .LogCullPercentage. This is a placeholder.

price_for_log_part(log_part: LogParts, diameter_cm: float) → float[source]#

Get the price for a given log part (Butt, Middle, Top) at a given diameter (cm). Rounds or floors the diameter to the nearest available diameter class.

set_price_for_diameter(diameter: int, price_struct: TimberPriceForDiameter)[source]#

Store a price entry for a certain diameter class.

pyforestry.base.pricelist.pricelist.UNATTRIBUTED_PRICELIST_IDENTITY = PricelistIdentity(name='(unnamed price list)', currency='(unspecified)', source=SourceReference(author='(none)', year=0, title='Prices supplied by whoever configured the run', appendix='', note="No publisher named, so the figures this list produces are the caller's own. year=0 is a sentinel for 'not applicable', not a citation date."))#

What a price list carries when nobody has said whose it is – a hand-built table in a test, or a caller’s own figures. It is a real state rather than a missing one, so it is named: a run manifest reporting money against this says plainly that the prices are the analyst’s, which is what keeps it apart from a run priced against a published list. The (none)/year-0 form is the one the rest of the package uses for anything authored rather than published.

pyforestry.base.pricelist.pricelist.create_pricelist_from_data(price_data: dict, species_to_load: str | Sequence[str] | None = None, *, identity: PricelistIdentity | None = None) → Pricelist[source]#

Build a Pricelist from a dictionary.

Parameters:
  • price_data (dict) – The complete price dictionary (must contain the ‘Common’ block).

  • species_to_load (str | Sequence[str] | None, default None) –

    • None - load all species that have timber price tables.

    • str - load just that species.

    • iterable - load every species in the iterable; it is not an error if some of them have only pulp prices.

  • identity (PricelistIdentity | None, default None) – Which list this data is, what currency it is in, and who published it. A run manifest records it, so pass it whenever the figures will be reported: the shipped example data publishes its own alongside the prices (see MELLANSKOG_2013_IDENTITY). It is deliberately not read out of price_data, which is hashed as-is to validate a SolutionCube against the prices it was built from. Defaults to UNATTRIBUTED_PRICELIST_IDENTITY.

Returns:

A fully populated Pricelist instance.

Return type:

Pricelist

pyforestry.base.pricelist.solutioncube module#

Utilities for generating and querying precomputed bucking solutions.

class pyforestry.base.pricelist.solutioncube.SolutionCube(dataset: Dataset)[source]#

Bases: object

Container for precomputed bucking solutions.

classmethod generate(pricelist_data: ~typing.Dict[str, ~typing.Any], taper_model: ~typing.Type[~pyforestry.base.taper.taper.Taper], species_list: list[str], dbh_range: ~typing.Tuple[float, float], height_range: ~typing.Tuple[float, float], dbh_step: int = 2, height_step: float = 0.2, timber_class: ~typing.Type[~pyforestry.base.timber.timber_base.timber.Timber] = <class 'pyforestry.base.timber.timber_base.timber.Timber'>, workers: int = -1)[source]#

Generates the solution cube by running the optimizer in parallel.

classmethod load(path: str, pricelist_to_verify: Dict | None = None)[source]#

Loads a solution cube from a netCDF file.

lookup(species: str, dbh: float, height: float) → Tuple[float, list][source]#

Performs a fast lookup for a given tree’s properties. Uses nearest-neighbor interpolation.

lookup_timber_pricelist(species: str) → Tuple[float, list][source]#

Return an arbitrary timber value for species or warn if missing.

save(path: str)[source]#

Saves the dataset to a netCDF file.

Module contents#

Public API for pricelist utilities.