slim.simulation.cage module

Cages contain almost all of the modelling logic in SLIM. They model single cages (aka pens) of salmon and lice. In contrast to literature we only assume eggs to be floating between cages.

This module is not meant for external use, and should be relatively self-contained.

class slim.simulation.cage.Cage(cage_id: int, cfg: Config, farm: Farm, initial_lice_pop: Optional[GrossLiceDistrib] = None)

Bases: slim.log.LoggableMixin

The class that contains fish and lice and deals with all their lifecycle logic.

Avoid instantiating this class directly. Usually a cage belongs to an instance of Farm which will deal with cage-to-farm lice movements.

In general, making a cage step resolves to calling the update() method every day.

  • cage_id – the label (id) of the cage within the farm

  • cfg – the farm configuration

  • farm – a Farm object

  • initial_lice_pop – if provided, overrides default generated lice population

property aggregation_rate

The aggregation rate is the number of lice over the total number of fish. Elsewhere, it is referred to as infection rate, but here “infection rate” only refers to host fish.


the aggregation rate


Average fish mass.

Params days

number of elapsed days


the average fish mass (in grams).

create_offspring(cur_time: datetime.datetime) GenoDistrib

Hatch the eggs from the event queue


cur_time – the current time


a delta egg genomics

property current_treatments

a list of current treatments

do_infection_events(days: int) int

Infect fish in this cage if the sea lice are in stage L2 and at least 1 day old

  • cur_date – current date of simulation

  • days – days the number of days elapsed


number of evolving lice, or equivalently the new number of infections

do_mating_events() Tuple[int, GenoDistrib]

Will generate two deltas: one to add to unavailable dams and subtract from available dams, one to add to eggs Assume males don’t become unavailable? in this case we don’t need a delta for sires


a pair (mating_dams, new_eggs)


Put the cage in a fallowing state.


  1. All the fish would be removed.

  2. L3/L4/L5 would therefore disappear as they are attached to fish.

  3. Dam waiting and treatment queues will be flushed.

generate_eggs_discrete_batch(sire_distrib: GenoDistrib, dam_distrib: GenoDistrib, number_eggs: int) GenoDistrib

Get number of eggs based on discrete genetic mechanism.

The algorithm emulates the following scenario: each sire s_i is sampled from sire_distrib and will have a given genomic \(g_{s_i}\) and similarly a dam \(d_i\) will have a genotype \(g_{d_i}\). Because the genomic of a lice can only be dominant, recessive or partial dominant then the mating will result in one of these happening depending on the usual Mendelevian mechanism. The algorithm terminates when all sires and dams have been mated. Here we emulate such sampling strategy via a O(1) algorithm as follows: we compute the probabilities of each combination arising and adopt a multinomial distribution in order to achieve a given number of eggs. The rationale for the formulae used here is the following:

  • to get an A (dominant) one needs a combination of dominant and dominant allele, or dominant and the right half of partially dominant genes;

  • to get an a (recessive) one does the same as above, but with fully recessive alleles instead;

  • to get a partially dominant one, we use the inclusion-exclusion principle and subtract the cases above from all the possible pairings.

Together with being fast, this approach naturally models statistical uncertainty compared to a perfect Mendelevian split. Unfortunately it does not naturally include mutation to non-existent strains (e.g. a mating between pure dominant lice distributions can never yield partial dominance or recessive genomics in the offspring).

  • sire_distrib – the genotype distribution of the sires

  • dam_distrib – the genotype distribution of the eggs

  • number_eggs – the total number of eggs


the newly sampled eggs as a GenoDistrib.

generate_eggs_maternal_batch(dams: GenoDistrib, number_eggs: int) GenoDistrib

Get number of eggs based on maternal genetic mechanism.

Maternal-only inheritance - all eggs have mother’s genotype.

  • dams – the genomics of the dams

  • number_eggs – the number of eggs produced


genomics distribution of eggs produced

get_arrivals(cur_date: datetime.datetime) GenoDistrib

Process the arrivals queue.


cur_date – Current date of simulation


Genotype distribution of eggs hatched in travel

get_background_lice_mortality() Dict[str, int]

Background death in a stage (remove entry) -> rate = number of individuals in stage*stage rate (nauplii 0.17/d, copepods 0.22/d, pre-adult female 0.05, pre-adult male … Stien et al 2005)


the current background mortality. The return value is genotype-agnostic

get_cleaner_fish_delta() int

Call this function before get_lice_treatment_mortality() !

get_dying_lice_from_dead_fish(num_dead_fish: int) Dict[str, int]

Get the number of lice that die when fish die.


num_dead_fish – the number of dead fish


a gross distribution

get_egg_batch(cur_date: datetime.datetime, egg_distrib: GenoDistrib) EggBatch

Get the expected arrival date of an egg batch

  • cur_date – the current time

  • egg_distrib – the egg distribution


EggBatch representing egg distribution and expected hatching date

get_fish_growth(days_since_start) Tuple[int, int]

Get the number of fish that get killed either naturally or by lice.


days_since_start: the number of days since the beginning.


a tuple (natural_deaths, lice_induced_deaths, lice_deaths)

get_fish_treatment_mortality(days_since_start: int, fish_lice_deaths: int, fish_background_deaths: int) int

Get fish mortality due to treatment. Mortality due to treatment is defined in terms of point percentage increases, thus we can only account for “excess deaths”. If multiple treatments are performed their side effects are additively combined. If no treatment is currently in action, no fish deaths are counted.

  • days_since_start – the number of days since the beginning of the simulation

  • fish_lice_deaths – the number of fish dead by lice

  • fish_background_deaths – the number of fish dead by natural reasons


number of fish death events

get_infecting_population(*args) int

Get the number of lice infecting a population.


*args – consider the given stages as infecting (default: CH to AM/AF)


gross number of infecting lice

get_infection_rates(days_since_start) Tuple[float, int]

Compute the number of lice that can infect and what their infection rate (number per fish) is.

Compared to Aldrin et al. we do not calculate a softmax across cage rates as this would cause multiprocessing issues. We still perform


days_since_start – days since the cage has opened


a pair (Einf, num_avail_lice)

get_lice_lifestage(cur_date: datetime.datetime) Tuple[int, int, int, int]

Move lice between lifecycle stages. See Section 2.1 of Aldrin et al. (2017)


cur_date – the current date


a tuple (new_l2, new_l4, new_l5f, new_l5m)

get_lice_treatment_mortality(cur_date) Tuple[Dict[str, GenoDistrib], float]

Calculate the number of lice in each stage killed by treatment.

Note: this method consumes the internal event queue


cur_date – the current date


a pair (distribution of dead lice, cost of treatment)

get_lice_treatment_mortality_rate(cur_date: datetime.datetime) Dict[str, GenoTreatmentValue]

Check if the cage is currently being treated. If yes, calculate the treatment rates. Note: this method consumes the internal treatment event queue.


cur_date – the current date


the mortality rates broken down by geno data.

get_mean_infected_fish(*args) int

Get the average number of infected fish.


*args – the stages to consider (optional, by default all stages from the third onward are taken into account)


the number of infected fish

get_num_eggs(mated_females) int

Get the number of new eggs


mated_females – the number of mated females that reproduce


the number of eggs produced

get_num_matings() int

Get the number of matings. Implement Cox (2017)’s approach assuming an unbiased sex distribution.

get_reservoir_lice(pressure: int, external_pressure_ratios: numpy.ndarray) Dict[str, GenoDistrib]

Get distribution of lice coming from the reservoir

  • pressure – External pressure

  • external_pressure_ratios – the external pressure ratios to sample from


Distribution of lice in L1 and L2

get_stage_ages_distrib(stage: str, temp_c: float = 10.0)

Create an age distribution (in days) for the sea lice within a lifecycle stage.

This distribution is computed by using a simplified version of formulae (4), (6), (8) in Aldrin et al.: we assume that all lice of a stage m-1 that evolve at stage m will be put at a stage-age of 0, and that this is going to be a constant/stable amount.

get_temperature(cur_date: datetime.datetime) float

Get the cage temperature at this farm and day


cur_date – the current day


the temperature (in °C)

static get_variance_infected_fish(num_fish: int, infecting_lice: int) float

Compute the variance of the lice infecting the fish.

Rationale: assuming that we generate \(N\) bins that sum to \(K\), we can model this as a multinomial distribution where all \(p_i\) are the same. Therefore, the variance of each bin is \(k \frac{n-1}{n^2}\)

Because we are considering the total variance of k events at the same time we need to multiply by \(k\), thus yielding \(k^2 \frac{n-1}{n^2}\) .

  • num_fish – the number of fish

  • infecting_lice – the number of lice attached to the fish


the variance

property is_fallowing

True if the cage is fallowing.

is_treated(treatment_type: Optional[Treatment] = None)

Check if a farm is treated.


treatment_type – if provided, check if there is a treatment of the given type


True if the cage is being treated

mutate(eggs: GenoDistrib, mutation_rate: float) GenoDistrib

Mutate the genotype distribution

  • eggs – the genotype distribution of the newly produced eggs

  • mutation_rate – the rate of mutation with respect to the number of eggs.

promote_population(prev_stage: Union[str, GenoDistrib], cur_stage: str, leaving_lice: int, entering_lice: Optional[int] = None)

Promote the population by stage and respect the genotypes

  • prev_stage – the lice stage from which cur_stage evolves

  • cur_stage – the lice stage that is about to evolve

  • leaving_lice – the number of lice in the _cur_stage=>next_stage_ progression

  • entering_lice – the number of lice in the _prev_stage=>cur_stage_ progression. If _prev_stage_ is a a string, _entering_lice_ must be an _int_

select_lice(distrib_lice_available: GenoDistrib, num_lice: int) GenoDistrib

From a geno distribution of eligible lice sample a given Genotype distribution

Note: this is very similar to GenoDistrib.normalise_to() but performs an explicit sampling.

TODO: should we integrate this into GenoDistrib class

  • distrib_lice_available – the starting dam genomic distribution

  • num_lice – the wished number of dams to sample


Create a JSON-serialisable dictionary version of a cage.

update(cur_date: datetime.datetime, pressure: int, ext_pressure_ratio: numpy.ndarray) Tuple[GenoDistrib, Optional[datetime.datetime], float]

Update the cage at the current time step.

  • cur_date – Current date of simulation

  • pressure – External pressure, planctonic lice coming from the reservoir

  • ext_pressure_ratio – The genotype ratio to use for the external pressure


Tuple (egg genotype distribution, hatching date, cost)

update_arrivals(arrivals_dict: GenoDistribByHatchDate, arrival_date: dt.datetime)

Update the arrivals queue

  • arrivals_dict – List of dictionaries of genotype distributions based on hatch date

  • arrival_date – Arrival date at this cage

update_deltas(dead_lice_dist: Dict[str, int], treatment_mortality: Dict[str, GenoDistrib], fish_deaths_natural: int, fish_deaths_from_lice: int, fish_deaths_from_treatment: int, new_L2: int, new_L4: int, new_females: int, new_males: int, new_infections: int, lice_from_reservoir: Dict[str, GenoDistrib], delta_dams_batch: int, new_offspring_distrib: GenoDistrib, hatched_arrivals_dist: GenoDistrib, cleaner_fish_delta: int = 0)

Update the number of fish and the lice in each life stage

  • dead_lice_dist – the number of dead lice due to background death (as a distribution)

  • treatment_mortality – the distribution of genotypes being affected by treatment

  • fish_deaths_natural – the number of natural fish death events

  • fish_deaths_from_lice – the number of lice-induced fish death events

  • fish_deaths_from_treatment – the number of treatment-induced fish death events

  • new_L2 – number of new L2 fish

  • new_L4 – number of new L4 fish

  • new_females – number of new adult females

  • new_males – number of new adult males

  • new_infections – the number of new infections (i.e. progressions from L2 to L3)

  • lice_from_reservoir – the number of lice taken from the reservoir

  • delta_dams_batch – the genotypes of now-unavailable females in batch events

  • new_offspring_distrib – the new offspring obtained from hatching and migrations

  • hatched_arrivals_dist – new offspring obtained from arrivals

  • cleaner_fish_delta – new cleaner fish