aegis_sim.bioreactor

  1import numpy as np
  2import logging
  3
  4from aegis_sim import variables
  5from aegis_sim import submodels
  6from aegis_sim.constants import VALID_CAUSES_OF_DEATH
  7from aegis_sim.dataclasses.population import Population
  8from aegis_sim.recording import recordingmanager
  9from aegis_sim.parameterization import parametermanager
 10from aegis_sim.submodels.resources.starvation import starvation
 11from aegis_sim.submodels.resources.resources import resources
 12
 13
 14class Bioreactor:
 15    def __init__(self, population: Population):
 16        self.eggs: Population = None
 17        self.population: Population = population
 18
 19    ##############
 20    # MAIN LOGIC #
 21    ##############
 22
 23    def run_step(self):
 24        """Perform one step of simulation."""
 25
 26        # If extinct (no living individuals nor eggs left), do nothing
 27        if len(self) == 0:
 28            logging.debug("Population went extinct.")
 29            recordingmanager.summaryrecorder.extinct = True
 30        # Mortality sources
 31        self.mortalities()
 32        resources.replenish()
 33
 34        recordingmanager.popsizerecorder.write_before_reproduction(self.population)
 35        self.growth()  # size increase
 36        self.reproduction()  # reproduction
 37        self.age()  # age increment and potentially death
 38        self.hatch()
 39        submodels.architect.envdrift.evolve(step=variables.steps)
 40
 41        # Record data
 42        recordingmanager.popsizerecorder.write_after_reproduction(self.population)
 43        recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs)
 44        recordingmanager.envdriftmaprecorder.write(step=variables.steps)
 45        recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages)  # population census
 46        recordingmanager.picklerecorder.write(self.population)
 47        recordingmanager.featherrecorder.write(self.population)
 48        recordingmanager.guirecorder.record(self.population)
 49        recordingmanager.flushrecorder.flush()
 50        recordingmanager.popgenstatsrecorder.write(
 51            self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta")
 52        )  # TODO defers calculation of mutation rates; hacky
 53        recordingmanager.summaryrecorder.record_memuse()
 54        recordingmanager.terecorder.record(self.population.ages, "alive")
 55
 56    ###############
 57    # STEP LOGIC #
 58    ###############
 59
 60    def mortalities(self):
 61        for source in parametermanager.parameters.MORTALITY_ORDER:
 62            if source == "intrinsic":
 63                self.mortality_intrinsic()
 64            elif source == "abiotic":
 65                self.mortality_abiotic()
 66            elif source == "infection":
 67                self.mortality_infection()
 68            elif source == "predation":
 69                self.mortality_predation()
 70            elif source == "starvation":
 71                self.mortality_starvation()
 72            else:
 73                raise ValueError(f"Invalid source of mortality '{source}'")
 74
 75    def mortality_intrinsic(self):
 76        probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv")
 77        age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages)
 78        mask_kill = variables.rng.random(len(probs_surv)) < age_hazard
 79        self._kill(mask_kill=mask_kill, causeofdeath="intrinsic")
 80
 81    def mortality_abiotic(self):
 82        hazard = submodels.abiotic(variables.steps)
 83        age_hazard = submodels.frailty.modify(hazard=hazard, ages=self.population.ages)
 84        mask_kill = variables.rng.random(len(self.population)) < age_hazard
 85        self._kill(mask_kill=mask_kill, causeofdeath="abiotic")
 86
 87    def mortality_infection(self):
 88        submodels.infection(self.population)
 89        # TODO add age hazard
 90        mask_kill = self.population.infection == -1
 91        self._kill(mask_kill=mask_kill, causeofdeath="infection")
 92
 93    def mortality_predation(self):
 94        probs_kill = submodels.predation(len(self))
 95        # TODO add age hazard
 96        mask_kill = variables.rng.random(len(self)) < probs_kill
 97        self._kill(mask_kill=mask_kill, causeofdeath="predation")
 98
 99    def mortality_starvation(self):
100        recordingmanager.resourcerecorder.write_before_scavenging()
101        resources_scavenged = resources.scavenge(np.ones(len(self.population)))
102        recordingmanager.resourcerecorder.write_after_scavenging()
103        # mask_kill = starvation.get_mask_kill(
104        #     n=len(self.population),
105        #     resources_scavenged=resources_scavenged.sum(),
106        # )
107        mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum())
108        self._kill(mask_kill=mask_kill, causeofdeath="starvation")
109
110    def reproduction(self):
111        """Generate offspring of reproducing individuals.
112        Initial is set to 0.
113        """
114
115        # Check if fertile
116        mask_fertile = (
117            self.population.ages >= parametermanager.parameters.MATURATION_AGE
118        )  # Check if mature; mature if survived MATURATION_AGE full cycles
119        if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0:
120            mask_menopausal = (
121                self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT
122            )  # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles
123            mask_fertile = (mask_fertile) & (~mask_menopausal)
124
125        if not any(mask_fertile):
126            return
127
128        # Check if reproducing
129        probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile)
130
131        # Binomial calculation
132        n = parametermanager.parameters.MAX_OFFSPRING_NUMBER
133        p = probs_repr
134
135        assert np.all(p <= 1)
136        assert np.all(p >= 0)
137        num_repr = variables.rng.binomial(n=n, p=p)
138        mask_repr = num_repr > 0
139
140        if sum(num_repr) == 0:
141            return
142
143        # Indices of reproducing individuals
144        who = np.repeat(np.arange(len(self.population)), num_repr)
145
146        # Count ages at reproduction
147        ages_repr = self.population.ages[who]
148        recordingmanager.flushrecorder.collect("age_at_birth", ages_repr)
149
150        # Increase births statistics
151        self.population.births += num_repr
152
153        # Generate offspring genomes
154        parental_genomes = self.population.genomes.get(individuals=who)
155        parental_sexes = self.population.sexes[who]
156
157        muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[
158            mask_repr
159        ]
160        muta_prob = np.repeat(muta_prob, num_repr[mask_repr])
161
162        offspring_genomes = submodels.reproduction.generate_offspring_genomes(
163            genomes=parental_genomes,
164            muta_prob=muta_prob,
165            ages=ages_repr,
166            parental_sexes=parental_sexes,
167        )
168        offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes))
169
170        # Randomize order of newly laid egg attributes ..
171        # .. because the order will affect their probability to be removed because of limited carrying capacity
172        order = np.arange(len(offspring_sexes))
173        variables.rng.shuffle(order)
174        offspring_genomes = offspring_genomes[order]
175        offspring_sexes = offspring_sexes[order]
176
177        # Make eggs
178        eggs = Population.make_eggs(
179            offspring_genomes=offspring_genomes,
180            step=variables.steps,
181            offspring_sexes=offspring_sexes,
182            parental_generations=np.zeros(len(offspring_sexes)),  # TODO replace with working calculation
183        )
184        if self.eggs is None:
185            self.eggs = eggs
186        else:
187            self.eggs += eggs
188        if len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS:
189            indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :]
190            # TODO biased
191            self.eggs *= indices
192
193    def growth(self):
194        # TODO use already scavenged resources to determine growth
195        # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow")
196        # gathered_resources = submodels.resources.scavenge(max_growth_potential)
197        # self.population.sizes += gathered_resources
198        self.population.sizes += 1
199
200    def age(self):
201        """Increase age of all by one and kill those that surpass age limit.
202        Age denotes the number of full cycles that an individual survived and reproduced.
203        AGE_LIMIT is the maximum number of full cycles an individual can go through.
204        """
205        self.population.ages += 1
206        mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT
207        self._kill(mask_kill=mask_kill, causeofdeath="age_limit")
208
209    def hatch(self):
210        """Turn eggs into living individuals"""
211
212        # If nothing to hatch
213        if self.eggs is None or len(self.eggs) == 0:
214            return
215
216        # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE
217        if parametermanager.parameters.REPRODUCTION_REGULATION:
218            current_population_size = len(self.population)
219            remaining_capacity = resources.capacity - current_population_size
220            # If no remaining capacity, do not reproduce
221            if remaining_capacity < 1:
222                self.eggs = None
223                return
224            elif remaining_capacity < len(self.eggs):
225                indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False)
226                self.eggs *= indices
227
228        # If something to hatch
229        if (
230            (
231                parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0
232            )  # hatch when everyone dead
233            or (parametermanager.parameters.INCUBATION_PERIOD == 0)  # hatch immediately
234            or (
235                parametermanager.parameters.INCUBATION_PERIOD > 0
236                and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0
237            )  # hatch with delay
238        ):
239
240            self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes)
241            self.population += self.eggs
242            self.eggs = None
243
244    ################
245    # HELPER FUNCS #
246    ################
247
248    def _kill(self, mask_kill, causeofdeath):
249        """Kill individuals and record their data."""
250
251        assert causeofdeath in VALID_CAUSES_OF_DEATH
252
253        # Skip if no one to kill
254        if not any(mask_kill):
255            return
256
257        # Count ages at death
258        # if causeofdeath != "age_limit":
259        ages_death = self.population.ages[mask_kill]
260        recordingmanager.flushrecorder.collect(f"age_at_{causeofdeath}", ages_death)
261        recordingmanager.terecorder.record(ages_death, "dead")
262
263        # Retain survivors
264        self.population *= ~mask_kill
265
266    def __len__(self):
267        """Return the number of living individuals and saved eggs."""
268        return len(self.population) + len(self.eggs) if self.eggs is not None else len(self.population)
class Bioreactor:
 15class Bioreactor:
 16    def __init__(self, population: Population):
 17        self.eggs: Population = None
 18        self.population: Population = population
 19
 20    ##############
 21    # MAIN LOGIC #
 22    ##############
 23
 24    def run_step(self):
 25        """Perform one step of simulation."""
 26
 27        # If extinct (no living individuals nor eggs left), do nothing
 28        if len(self) == 0:
 29            logging.debug("Population went extinct.")
 30            recordingmanager.summaryrecorder.extinct = True
 31        # Mortality sources
 32        self.mortalities()
 33        resources.replenish()
 34
 35        recordingmanager.popsizerecorder.write_before_reproduction(self.population)
 36        self.growth()  # size increase
 37        self.reproduction()  # reproduction
 38        self.age()  # age increment and potentially death
 39        self.hatch()
 40        submodels.architect.envdrift.evolve(step=variables.steps)
 41
 42        # Record data
 43        recordingmanager.popsizerecorder.write_after_reproduction(self.population)
 44        recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs)
 45        recordingmanager.envdriftmaprecorder.write(step=variables.steps)
 46        recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages)  # population census
 47        recordingmanager.picklerecorder.write(self.population)
 48        recordingmanager.featherrecorder.write(self.population)
 49        recordingmanager.guirecorder.record(self.population)
 50        recordingmanager.flushrecorder.flush()
 51        recordingmanager.popgenstatsrecorder.write(
 52            self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta")
 53        )  # TODO defers calculation of mutation rates; hacky
 54        recordingmanager.summaryrecorder.record_memuse()
 55        recordingmanager.terecorder.record(self.population.ages, "alive")
 56
 57    ###############
 58    # STEP LOGIC #
 59    ###############
 60
 61    def mortalities(self):
 62        for source in parametermanager.parameters.MORTALITY_ORDER:
 63            if source == "intrinsic":
 64                self.mortality_intrinsic()
 65            elif source == "abiotic":
 66                self.mortality_abiotic()
 67            elif source == "infection":
 68                self.mortality_infection()
 69            elif source == "predation":
 70                self.mortality_predation()
 71            elif source == "starvation":
 72                self.mortality_starvation()
 73            else:
 74                raise ValueError(f"Invalid source of mortality '{source}'")
 75
 76    def mortality_intrinsic(self):
 77        probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv")
 78        age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages)
 79        mask_kill = variables.rng.random(len(probs_surv)) < age_hazard
 80        self._kill(mask_kill=mask_kill, causeofdeath="intrinsic")
 81
 82    def mortality_abiotic(self):
 83        hazard = submodels.abiotic(variables.steps)
 84        age_hazard = submodels.frailty.modify(hazard=hazard, ages=self.population.ages)
 85        mask_kill = variables.rng.random(len(self.population)) < age_hazard
 86        self._kill(mask_kill=mask_kill, causeofdeath="abiotic")
 87
 88    def mortality_infection(self):
 89        submodels.infection(self.population)
 90        # TODO add age hazard
 91        mask_kill = self.population.infection == -1
 92        self._kill(mask_kill=mask_kill, causeofdeath="infection")
 93
 94    def mortality_predation(self):
 95        probs_kill = submodels.predation(len(self))
 96        # TODO add age hazard
 97        mask_kill = variables.rng.random(len(self)) < probs_kill
 98        self._kill(mask_kill=mask_kill, causeofdeath="predation")
 99
100    def mortality_starvation(self):
101        recordingmanager.resourcerecorder.write_before_scavenging()
102        resources_scavenged = resources.scavenge(np.ones(len(self.population)))
103        recordingmanager.resourcerecorder.write_after_scavenging()
104        # mask_kill = starvation.get_mask_kill(
105        #     n=len(self.population),
106        #     resources_scavenged=resources_scavenged.sum(),
107        # )
108        mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum())
109        self._kill(mask_kill=mask_kill, causeofdeath="starvation")
110
111    def reproduction(self):
112        """Generate offspring of reproducing individuals.
113        Initial is set to 0.
114        """
115
116        # Check if fertile
117        mask_fertile = (
118            self.population.ages >= parametermanager.parameters.MATURATION_AGE
119        )  # Check if mature; mature if survived MATURATION_AGE full cycles
120        if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0:
121            mask_menopausal = (
122                self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT
123            )  # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles
124            mask_fertile = (mask_fertile) & (~mask_menopausal)
125
126        if not any(mask_fertile):
127            return
128
129        # Check if reproducing
130        probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile)
131
132        # Binomial calculation
133        n = parametermanager.parameters.MAX_OFFSPRING_NUMBER
134        p = probs_repr
135
136        assert np.all(p <= 1)
137        assert np.all(p >= 0)
138        num_repr = variables.rng.binomial(n=n, p=p)
139        mask_repr = num_repr > 0
140
141        if sum(num_repr) == 0:
142            return
143
144        # Indices of reproducing individuals
145        who = np.repeat(np.arange(len(self.population)), num_repr)
146
147        # Count ages at reproduction
148        ages_repr = self.population.ages[who]
149        recordingmanager.flushrecorder.collect("age_at_birth", ages_repr)
150
151        # Increase births statistics
152        self.population.births += num_repr
153
154        # Generate offspring genomes
155        parental_genomes = self.population.genomes.get(individuals=who)
156        parental_sexes = self.population.sexes[who]
157
158        muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[
159            mask_repr
160        ]
161        muta_prob = np.repeat(muta_prob, num_repr[mask_repr])
162
163        offspring_genomes = submodels.reproduction.generate_offspring_genomes(
164            genomes=parental_genomes,
165            muta_prob=muta_prob,
166            ages=ages_repr,
167            parental_sexes=parental_sexes,
168        )
169        offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes))
170
171        # Randomize order of newly laid egg attributes ..
172        # .. because the order will affect their probability to be removed because of limited carrying capacity
173        order = np.arange(len(offspring_sexes))
174        variables.rng.shuffle(order)
175        offspring_genomes = offspring_genomes[order]
176        offspring_sexes = offspring_sexes[order]
177
178        # Make eggs
179        eggs = Population.make_eggs(
180            offspring_genomes=offspring_genomes,
181            step=variables.steps,
182            offspring_sexes=offspring_sexes,
183            parental_generations=np.zeros(len(offspring_sexes)),  # TODO replace with working calculation
184        )
185        if self.eggs is None:
186            self.eggs = eggs
187        else:
188            self.eggs += eggs
189        if len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS:
190            indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :]
191            # TODO biased
192            self.eggs *= indices
193
194    def growth(self):
195        # TODO use already scavenged resources to determine growth
196        # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow")
197        # gathered_resources = submodels.resources.scavenge(max_growth_potential)
198        # self.population.sizes += gathered_resources
199        self.population.sizes += 1
200
201    def age(self):
202        """Increase age of all by one and kill those that surpass age limit.
203        Age denotes the number of full cycles that an individual survived and reproduced.
204        AGE_LIMIT is the maximum number of full cycles an individual can go through.
205        """
206        self.population.ages += 1
207        mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT
208        self._kill(mask_kill=mask_kill, causeofdeath="age_limit")
209
210    def hatch(self):
211        """Turn eggs into living individuals"""
212
213        # If nothing to hatch
214        if self.eggs is None or len(self.eggs) == 0:
215            return
216
217        # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE
218        if parametermanager.parameters.REPRODUCTION_REGULATION:
219            current_population_size = len(self.population)
220            remaining_capacity = resources.capacity - current_population_size
221            # If no remaining capacity, do not reproduce
222            if remaining_capacity < 1:
223                self.eggs = None
224                return
225            elif remaining_capacity < len(self.eggs):
226                indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False)
227                self.eggs *= indices
228
229        # If something to hatch
230        if (
231            (
232                parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0
233            )  # hatch when everyone dead
234            or (parametermanager.parameters.INCUBATION_PERIOD == 0)  # hatch immediately
235            or (
236                parametermanager.parameters.INCUBATION_PERIOD > 0
237                and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0
238            )  # hatch with delay
239        ):
240
241            self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes)
242            self.population += self.eggs
243            self.eggs = None
244
245    ################
246    # HELPER FUNCS #
247    ################
248
249    def _kill(self, mask_kill, causeofdeath):
250        """Kill individuals and record their data."""
251
252        assert causeofdeath in VALID_CAUSES_OF_DEATH
253
254        # Skip if no one to kill
255        if not any(mask_kill):
256            return
257
258        # Count ages at death
259        # if causeofdeath != "age_limit":
260        ages_death = self.population.ages[mask_kill]
261        recordingmanager.flushrecorder.collect(f"age_at_{causeofdeath}", ages_death)
262        recordingmanager.terecorder.record(ages_death, "dead")
263
264        # Retain survivors
265        self.population *= ~mask_kill
266
267    def __len__(self):
268        """Return the number of living individuals and saved eggs."""
269        return len(self.population) + len(self.eggs) if self.eggs is not None else len(self.population)
Bioreactor(population: aegis_sim.dataclasses.population.Population)
16    def __init__(self, population: Population):
17        self.eggs: Population = None
18        self.population: Population = population
def run_step(self):
24    def run_step(self):
25        """Perform one step of simulation."""
26
27        # If extinct (no living individuals nor eggs left), do nothing
28        if len(self) == 0:
29            logging.debug("Population went extinct.")
30            recordingmanager.summaryrecorder.extinct = True
31        # Mortality sources
32        self.mortalities()
33        resources.replenish()
34
35        recordingmanager.popsizerecorder.write_before_reproduction(self.population)
36        self.growth()  # size increase
37        self.reproduction()  # reproduction
38        self.age()  # age increment and potentially death
39        self.hatch()
40        submodels.architect.envdrift.evolve(step=variables.steps)
41
42        # Record data
43        recordingmanager.popsizerecorder.write_after_reproduction(self.population)
44        recordingmanager.popsizerecorder.write_egg_num_after_reproduction(self.eggs)
45        recordingmanager.envdriftmaprecorder.write(step=variables.steps)
46        recordingmanager.flushrecorder.collect("additive_age_structure", self.population.ages)  # population census
47        recordingmanager.picklerecorder.write(self.population)
48        recordingmanager.featherrecorder.write(self.population)
49        recordingmanager.guirecorder.record(self.population)
50        recordingmanager.flushrecorder.flush()
51        recordingmanager.popgenstatsrecorder.write(
52            self.population.genomes, self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta")
53        )  # TODO defers calculation of mutation rates; hacky
54        recordingmanager.summaryrecorder.record_memuse()
55        recordingmanager.terecorder.record(self.population.ages, "alive")

Perform one step of simulation.

def mortalities(self):
61    def mortalities(self):
62        for source in parametermanager.parameters.MORTALITY_ORDER:
63            if source == "intrinsic":
64                self.mortality_intrinsic()
65            elif source == "abiotic":
66                self.mortality_abiotic()
67            elif source == "infection":
68                self.mortality_infection()
69            elif source == "predation":
70                self.mortality_predation()
71            elif source == "starvation":
72                self.mortality_starvation()
73            else:
74                raise ValueError(f"Invalid source of mortality '{source}'")
def mortality_intrinsic(self):
76    def mortality_intrinsic(self):
77        probs_surv = self.population.phenotypes.extract(ages=self.population.ages, trait_name="surv")
78        age_hazard = submodels.frailty.modify(hazard=1 - probs_surv, ages=self.population.ages)
79        mask_kill = variables.rng.random(len(probs_surv)) < age_hazard
80        self._kill(mask_kill=mask_kill, causeofdeath="intrinsic")
def mortality_abiotic(self):
82    def mortality_abiotic(self):
83        hazard = submodels.abiotic(variables.steps)
84        age_hazard = submodels.frailty.modify(hazard=hazard, ages=self.population.ages)
85        mask_kill = variables.rng.random(len(self.population)) < age_hazard
86        self._kill(mask_kill=mask_kill, causeofdeath="abiotic")
def mortality_infection(self):
88    def mortality_infection(self):
89        submodels.infection(self.population)
90        # TODO add age hazard
91        mask_kill = self.population.infection == -1
92        self._kill(mask_kill=mask_kill, causeofdeath="infection")
def mortality_predation(self):
94    def mortality_predation(self):
95        probs_kill = submodels.predation(len(self))
96        # TODO add age hazard
97        mask_kill = variables.rng.random(len(self)) < probs_kill
98        self._kill(mask_kill=mask_kill, causeofdeath="predation")
def mortality_starvation(self):
100    def mortality_starvation(self):
101        recordingmanager.resourcerecorder.write_before_scavenging()
102        resources_scavenged = resources.scavenge(np.ones(len(self.population)))
103        recordingmanager.resourcerecorder.write_after_scavenging()
104        # mask_kill = starvation.get_mask_kill(
105        #     n=len(self.population),
106        #     resources_scavenged=resources_scavenged.sum(),
107        # )
108        mask_kill = starvation.get_mask_kill(ages=self.population.ages, resources_scavenged=resources_scavenged.sum())
109        self._kill(mask_kill=mask_kill, causeofdeath="starvation")
def reproduction(self):
111    def reproduction(self):
112        """Generate offspring of reproducing individuals.
113        Initial is set to 0.
114        """
115
116        # Check if fertile
117        mask_fertile = (
118            self.population.ages >= parametermanager.parameters.MATURATION_AGE
119        )  # Check if mature; mature if survived MATURATION_AGE full cycles
120        if parametermanager.parameters.REPRODUCTION_ENDPOINT > 0:
121            mask_menopausal = (
122                self.population.ages >= parametermanager.parameters.REPRODUCTION_ENDPOINT
123            )  # Check if menopausal; menopausal when lived through REPRODUCTION_ENDPOINT full cycles
124            mask_fertile = (mask_fertile) & (~mask_menopausal)
125
126        if not any(mask_fertile):
127            return
128
129        # Check if reproducing
130        probs_repr = self.population.phenotypes.extract(ages=self.population.ages, trait_name="repr", part=mask_fertile)
131
132        # Binomial calculation
133        n = parametermanager.parameters.MAX_OFFSPRING_NUMBER
134        p = probs_repr
135
136        assert np.all(p <= 1)
137        assert np.all(p >= 0)
138        num_repr = variables.rng.binomial(n=n, p=p)
139        mask_repr = num_repr > 0
140
141        if sum(num_repr) == 0:
142            return
143
144        # Indices of reproducing individuals
145        who = np.repeat(np.arange(len(self.population)), num_repr)
146
147        # Count ages at reproduction
148        ages_repr = self.population.ages[who]
149        recordingmanager.flushrecorder.collect("age_at_birth", ages_repr)
150
151        # Increase births statistics
152        self.population.births += num_repr
153
154        # Generate offspring genomes
155        parental_genomes = self.population.genomes.get(individuals=who)
156        parental_sexes = self.population.sexes[who]
157
158        muta_prob = self.population.phenotypes.extract(ages=self.population.ages, trait_name="muta", part=mask_repr)[
159            mask_repr
160        ]
161        muta_prob = np.repeat(muta_prob, num_repr[mask_repr])
162
163        offspring_genomes = submodels.reproduction.generate_offspring_genomes(
164            genomes=parental_genomes,
165            muta_prob=muta_prob,
166            ages=ages_repr,
167            parental_sexes=parental_sexes,
168        )
169        offspring_sexes = submodels.sexsystem.get_sex(len(offspring_genomes))
170
171        # Randomize order of newly laid egg attributes ..
172        # .. because the order will affect their probability to be removed because of limited carrying capacity
173        order = np.arange(len(offspring_sexes))
174        variables.rng.shuffle(order)
175        offspring_genomes = offspring_genomes[order]
176        offspring_sexes = offspring_sexes[order]
177
178        # Make eggs
179        eggs = Population.make_eggs(
180            offspring_genomes=offspring_genomes,
181            step=variables.steps,
182            offspring_sexes=offspring_sexes,
183            parental_generations=np.zeros(len(offspring_sexes)),  # TODO replace with working calculation
184        )
185        if self.eggs is None:
186            self.eggs = eggs
187        else:
188            self.eggs += eggs
189        if len(self.eggs) > parametermanager.parameters.CARRYING_CAPACITY_EGGS:
190            indices = np.arange(len(self.eggs))[-parametermanager.parameters.CARRYING_CAPACITY_EGGS :]
191            # TODO biased
192            self.eggs *= indices

Generate offspring of reproducing individuals. Initial is set to 0.

def growth(self):
194    def growth(self):
195        # TODO use already scavenged resources to determine growth
196        # max_growth_potential = self.population.phenotypes.extract(ages=self.population.ages, trait_name="grow")
197        # gathered_resources = submodels.resources.scavenge(max_growth_potential)
198        # self.population.sizes += gathered_resources
199        self.population.sizes += 1
def age(self):
201    def age(self):
202        """Increase age of all by one and kill those that surpass age limit.
203        Age denotes the number of full cycles that an individual survived and reproduced.
204        AGE_LIMIT is the maximum number of full cycles an individual can go through.
205        """
206        self.population.ages += 1
207        mask_kill = self.population.ages >= parametermanager.parameters.AGE_LIMIT
208        self._kill(mask_kill=mask_kill, causeofdeath="age_limit")

Increase age of all by one and kill those that surpass age limit. Age denotes the number of full cycles that an individual survived and reproduced. AGE_LIMIT is the maximum number of full cycles an individual can go through.

def hatch(self):
210    def hatch(self):
211        """Turn eggs into living individuals"""
212
213        # If nothing to hatch
214        if self.eggs is None or len(self.eggs) == 0:
215            return
216
217        # If REPRODUCTION_REGULATION is True, only reproduce until MAX_POPULATION_SIZE
218        if parametermanager.parameters.REPRODUCTION_REGULATION:
219            current_population_size = len(self.population)
220            remaining_capacity = resources.capacity - current_population_size
221            # If no remaining capacity, do not reproduce
222            if remaining_capacity < 1:
223                self.eggs = None
224                return
225            elif remaining_capacity < len(self.eggs):
226                indices = variables.rng.choice(len(self.eggs), size=int(remaining_capacity), replace=False)
227                self.eggs *= indices
228
229        # If something to hatch
230        if (
231            (
232                parametermanager.parameters.INCUBATION_PERIOD == -1 and len(self.population) == 0
233            )  # hatch when everyone dead
234            or (parametermanager.parameters.INCUBATION_PERIOD == 0)  # hatch immediately
235            or (
236                parametermanager.parameters.INCUBATION_PERIOD > 0
237                and variables.steps % parametermanager.parameters.INCUBATION_PERIOD == 0
238            )  # hatch with delay
239        ):
240
241            self.eggs.phenotypes = submodels.architect.__call__(self.eggs.genomes)
242            self.population += self.eggs
243            self.eggs = None

Turn eggs into living individuals