Skip to content

Crop livestock household

Bala's crop livestock model, a farming household that adds livestock rearing to its crop income.

  • Name: crop_livestock_household
  • Field: economics/resources
  • Formalism: discrete map
  • References: Bala et al. 2017

Select this system

from skcomplex.systems import create_simulation, create_system, describe

system = create_system('crop_livestock_household')
entry = describe('crop_livestock_household')
trajectory = create_simulation('crop_livestock_household').prepare().run().trajectory_

Factory

@register_system(
    field='economics/resources',
    tags=('agriculture', 'livestock', 'household', 'livelihood', 'stocks and flows', 'system dynamics', 'discrete map'),
    references=('Bala et al. 2017',),
    defaults={'until': 12},
    observables=lambda system: [
        ObservableSpec(name, function, element='household')
        for name, function in (
            ('crop_production', CropLivestockElement.get_crop_production),
            ('income_from_crop', CropLivestockElement.get_income_from_crop),
            ('income_from_livestock', CropLivestockElement.get_income_from_livestock),
            ('sale', CropLivestockElement.get_sale),
            ('purchase', CropLivestockElement.get_purchase),
        )
    ],
)
def crop_livestock_household(
    wealth: float = 10000.0,
    livestock: float = 4.0,
    *,
    crop_area: float = 0.5,
    yield_normal: float = 3.2,
    price: float = 50000.0,
    unit_price: float = 35000.0,
    purchase_price: float = 15000.0,
    household_consumption: float = 75000.0,
    max_livestock: float = 8.0,
    sale_fraction: float = 0.2,
) -> System:
    """Return Bala's crop livestock model, a farming household that adds livestock rearing to its crop income.

    A household with a fixed crop area and a grant or loan to start rearing livestock earns the crop production times
    the price and the sales of animals, and spends on its consumption and on buying animals. Every year it sells the
    integer part of a fraction of its herd and buys as many animals as its wealth affords when the herd has room for
    them, and the animals it buys join the herd after three years. The equations and the defaults are those of the
    STELLA model of Bala et al. (2017, Section 4.11.6), whose `DELAY(purchase_of_livestock, 3)` is a pipeline of
    three years and whose `INT` truncates, so the model is a yearly map. The effects of the cropping intensity and the
    regeneration on the yield are zero. Livestock raises the income of the household considerably, but only after the
    maturation delay.

    Args:
        wealth:
            The initial wealth of the household.

        livestock:
            The initial herd.

        crop_area:
            The crop area, in hectares.

        yield_normal:
            The normal crop yield, in tonnes per hectare.

        price:
            The price of a tonne of crop.

        unit_price:
            The price an animal sells for.

        purchase_price:
            The price of an animal bought.

        household_consumption:
            The consumption of the household per year.

        max_livestock:
            The largest herd the household can rear.

        sale_fraction:
            The fraction of the herd sold every year.

    Returns:
        system:
            The system, with the stocks `household.wealth`, `household.livestock`, and `household.yield_multiplier`,
            and the purchases of the previous year `household.purchases`, over years. The observables of the system are
            `household.crop_production`, `household.income_from_crop`, `household.income_from_livestock`,
            `household.sale`, and `household.purchase`.

    Examples:
        >>> from skcomplex.systems.economics.resources import crop_livestock_household
        >>> crop_livestock_household(wealth=20000.0).elements[0].wealth
        20000.0
    """
    sale = int(livestock * sale_fraction)
    affordable = int(wealth / purchase_price)
    household = CropLivestockElement(
        'household',
        wealth=wealth,
        livestock=livestock,
        purchases=float(affordable if affordable <= max_livestock + sale - livestock else 0),
        crop_area=crop_area,
        yield_normal=yield_normal,
        price=price,
        unit_price=unit_price,
        purchase_price=purchase_price,
        household_consumption=household_consumption,
        max_livestock=max_livestock,
        sale_fraction=sale_fraction,
    )
    return System(elements=[household])

Components

Elements:

  • CropLivestockElement, from Element
class CropLivestockElement(Element):
    """Farming household with crop land, wealth, and livestock, whose purchases join the herd after a maturation delay.

    Every year the household sells `INT(livestock sale_fraction)` animals and buys `INT(wealth / purchase_price)`
    animals when that number is at most `max_livestock + sale - livestock`, and none otherwise. Its wealth grows by the
    crop income, the area times the normal yield times the yield multiplier times the price, and the livestock income,
    and falls by the purchases and the household consumption. Purchased animals join the herd three years later, and
    the yield multiplier regenerates and degenerates at fractions of itself.

    Args:
        label:
            The label of the household.

        visual:
            How the element is drawn, or `None` for `default_visual_`.

        wealth:
            The wealth of the household.

        livestock:
            The animals of the herd.

        yield_multiplier:
            The multiplier of the normal crop yield.

        purchases:
            The animals bought in the previous year.

        crop_area:
            The crop area, in hectares.

        yield_normal:
            The normal crop yield, in tonnes per hectare.

        price:
            The price of a tonne of crop.

        unit_price:
            The price an animal sells for.

        purchase_price:
            The price of an animal bought.

        household_consumption:
            The consumption of the household per year.

        max_livestock:
            The largest herd the household can rear.

        sale_fraction:
            The fraction of the herd sold every year.

        yield_regeneration:
            The fraction by which the yield multiplier regenerates every year.

        cropping_intensity:
            The fraction by which the yield multiplier degenerates every year.

    Examples:
        >>> from skcomplex.systems.economics.resources import CropLivestockElement
        >>> CropLivestockElement('household', max_livestock=10.0).max_livestock
        10.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        wealth: Annotated[float, StateSpec()] = 10000.0,
        livestock: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 4.0,
        yield_multiplier: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
        purchases: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo), lags=2)] = 0.0,
        crop_area: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        yield_normal: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 3.2,
        price: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 50000.0,
        unit_price: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 35000.0,
        purchase_price: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 15000.0,
        household_consumption: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 75000.0,
        max_livestock: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 8.0,
        sale_fraction: Annotated[float, ParameterSpec(domain=RealSet.closed(0, 1))] = 0.2,
        yield_regeneration: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
        cropping_intensity: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.wealth = wealth
        self.livestock = livestock
        self.yield_multiplier = yield_multiplier
        self.purchases = purchases
        self.crop_area = crop_area
        self.yield_normal = yield_normal
        self.price = price
        self.unit_price = unit_price
        self.purchase_price = purchase_price
        self.household_consumption = household_consumption
        self.max_livestock = max_livestock
        self.sale_fraction = sale_fraction
        self.yield_regeneration = yield_regeneration
        self.cropping_intensity = cropping_intensity

    @staticmethod
    def get_sale(s: SimpleNamespace) -> Expression:
        """Return the animals sold in the year, `INT(livestock sale_fraction)`.

        Args:
            s:
                The namespace of the household.

        Returns:
            sale:
                The animals sold.
        """
        return floor(s.livestock * s.sale_fraction)

    @staticmethod
    def get_purchase(s: SimpleNamespace) -> Expression:
        """Return the animals bought in the year, the affordable ones when the herd has room for them.

        Args:
            s:
                The namespace of the household.

        Returns:
            purchase:
                The animals bought.
        """
        affordable = floor(s.wealth / s.purchase_price)
        room = s.max_livestock + CropLivestockElement.get_sale(s) - s.livestock
        return where(affordable <= room, affordable, 0)

    @staticmethod
    def get_crop_production(s: SimpleNamespace) -> Expression:
        """Return the crop production, the area times the normal yield times the yield multiplier.

        Args:
            s:
                The namespace of the household.

        Returns:
            production:
                The crop production, in tonnes per year.
        """
        return s.crop_area * s.yield_normal * s.yield_multiplier

    @staticmethod
    def get_income_from_crop(s: SimpleNamespace) -> Expression:
        """Return the income from the crop, the production times the price.

        Args:
            s:
                The namespace of the household.

        Returns:
            income:
                The income from the crop per year.
        """
        return CropLivestockElement.get_crop_production(s) * s.price

    @staticmethod
    def get_income_from_livestock(s: SimpleNamespace) -> Expression:
        """Return the income from the livestock, the animals sold times their price.

        Args:
            s:
                The namespace of the household.

        Returns:
            income:
                The income from the livestock per year.
        """
        return CropLivestockElement.get_sale(s) * s.unit_price

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the yearly steps of the stocks, with purchases that join the herd three years later.

        Args:
            s:
                The namespace of the symbols the laws can use.

        Returns:
            laws:
                The laws of the element.
        """
        sale, purchase = self.get_sale(s), self.get_purchase(s)
        income = self.get_income_from_crop(s) + self.get_income_from_livestock(s)
        expenditure = purchase * s.purchase_price + s.household_consumption
        multiplier = s.yield_multiplier * (1 + s.yield_regeneration - s.cropping_intensity)
        return [
            UpdateLaw(s.wealth, s.wealth + income - expenditure),
            UpdateLaw(s.livestock, s.livestock + lag(s.purchases, 2) - sale),
            UpdateLaw(s.yield_multiplier, multiplier),
            UpdateLaw(s.purchases, purchase),
        ]

Equations

household.wealth: wealth_household(t + 1) == crop_area_household*price_household*yield_normal_household*yield_multiplier_household(t) + unit_price_household*floor(sale_fraction_household*livestock_household(t)) - purchase_price_household*where(floor(wealth_household(t)/purchase_price_household) <= max_livestock_household + floor(sale_fraction_household*livestock_household(t)) - livestock_household(t), floor(wealth_household(t)/purchase_price_household), 0) - household_consumption_household + wealth_household(t)
household.livestock: livestock_household(t + 1) == -floor(sale_fraction_household*livestock_household(t)) + livestock_household(t) + purchases_household(t - 2)
household.yield_multiplier: yield_multiplier_household(t + 1) == -(cropping_intensity_household - yield_regeneration_household - 1)*yield_multiplier_household(t)
household.purchases: purchases_household(t + 1) == where(floor(wealth_household(t)/purchase_price_household) <= max_livestock_household + floor(sale_fraction_household*livestock_household(t)) - livestock_household(t), floor(wealth_household(t)/purchase_price_household), 0)