expdef: name:Reproduction of benthic foraminifera info:~ Genes and parameter values which control reproduction are stored in user1 and user2 fields. user1: genes which are not encoded in Ff genotype: min_repro_energy - Minimum energy necessary for reproduction hibernation - Defines foram behavior in the case of no nutrients user2: Physiological parameters of foraminifera: max_energy_level - maximum energy level reached so far gen - generation: 0 haploid, 1 diploid species - species: 0 not hibernating 1 hibernating hibernated - 0/1 foram isn't/is hibernated reproduce - 0/1 foram isn't/is ready for reproduction ~ code:~ /* changes 2015-06-24: - xxx.get(...) changed to xxx[...] - xxx.set(...,...) changed to xxx[...]=... - function placeRandomlyNotColliding(cr) explicitly called when adding a new creature, and removed collision checking from onForamsBorn() which is also called when "growing" (since "growing" is implemented as creating a new creature in place of the old one, so onForamsBorn() is also called) - use creature's center (not the bbox corner) when growing - added "S" receptor to visualize the difference between diplo and haplo generations - signal.value is a MechPart (a reference) so it does not have to be updated after changing the object location */ global nutrientenergywaiting; global reprocounter; global colors; global chambers; global o; global max_chamber_energ; @include "foraminifera.inc" // -------------------------------- experiment begin -------------------------------- function onExpDefLoad() { // define genotype and creature groups GenePools.clear(); Populations.clear(); GenePools[0].name = "Unused"; var pop = Populations[0]; pop.name = "Forams"; pop.en_assim = 0; pop.nnsim = 0; pop.enableperf = 1; pop.death = 1; pop.energy = 1; pop.selfmask = 0x10001; pop.othermask = 0x20001; //pop.selfmask = 0x20002; pop.othermask = 0x10002; pop.perfperiod = 25; pop = Populations.addGroup("Nutrients"); pop.nnsim = 0; pop.enableperf = 0; pop.death = 1; pop.energy = 1; pop.selfmask = 0x20002; pop.othermask = 0x10000; //pop.othermask = 0x10002; //world SignalView.mode = 1; ExpParams.world_size = 100; World.wrldwat = ExpParams.world_size; World.wrldsiz = ExpParams.world_size; World.wrldbnd = 1; ExpParams.stress = 1; ExpParams.creath = -0.99; //just above the bottom ExpParams.autorestart = 0; //ExpParams.logging = 1; //uncomment to enable logging simulation parameters to log files //reproduction ExpParams.foramPop = 3; ExpParams.crossprob = 0.4; ExpParams.mutationprob = 0.2; ExpParams.repro_time = 20; reprocounter = 0; //morphology init_chambers(); ExpParams.chamber_proculus_haplo = scale(60); ExpParams.chamber_difference_haplo = 0.0; ExpParams.chamber_proculus_diplo = scale(20); ExpParams.chamber_difference_diplo = 0.2; max_chamber_energ = [Vector.new(), Vector.new()]; for (var j = 0; j < 2; j++) { for (var i = 0; i < chambers[0].size; i++) { max_chamber_energ[j].add(((Math.pow(getProperty(j, "chamber_proculus"),3) + Math.pow(getProperty(j, "chamber_proculus") + (i) * getProperty(j, "chamber_difference"),3))*(i+1))/2); } } ExpParams.zone1_range = scale(250); ExpParams.zone2_range = scale(5000); //energetics ExpParams.min_repro_energ_haplo = max_chamber_energ[0][2]; ExpParams.min_repro_energ_diplo = max_chamber_energ[1][8]; ExpParams.e_meta = 0.00003 * max_chamber_energ[0][0]; ExpParams.energy_hib = 0.00003 * max_chamber_energ[0][0]; ExpParams.energy_move = 0.0003 * max_chamber_energ[0][0]; ExpParams.energies0_haplo = max_chamber_energ[0][0] - 0.1*max_chamber_energ[0][0]; ExpParams.energies0_diplo = max_chamber_energ[1][0] - 0.1*max_chamber_energ[1][0]; ExpParams.feedtrans = 0.05; ExpParams.e_repro_cost_haplo = 0.5; ExpParams.e_repro_cost_diplo = 0.3; //nutrients ExpParams.nutrientsize = scale(10); ExpParams.energy_nut = 100 * Math.pow(ExpParams.nutrientsize, 3); ExpParams.nutrientPop = 10; ExpParams.feedrate = 0.0025; nutrientenergywaiting = ExpParams.energy_nut; ExpState.totaltestedcr = 0; ExpState.nutrient = ""; } @include "standard_placement.inc" function scale(x) { return x*0.025; } function getProperty(gen, prop_id) { var ploid = "haplo"; if (gen == 1) ploid = "diplo"; return ExpParams.[prop_id + "_" + ploid]; } function addInitialForam(species, i) { var geno = createForamGenotype(0, species, 0); var cr = Populations[0].add(geno); cr.name = "Initial creature" + species + "_" + i; placeCreatureRandomly(cr, 0, 0); cr.energy0 = getProperty(0, "energies0"); cr.energy = cr.energy0; setGenotype({"opt" : 0, "cr" : cr, "species" : species}); } function onExpInit() { Populations[0].clear(); Populations[1].clear(); for (var i = 0; i < ExpParams.foramPop; i++) { addInitialForam(0, i); addInitialForam(1, i); } o = Populations[0][0].getMechPart(0).orient.clone(); ExpState.totaltestedcr = 0; nutrientenergywaiting = ExpParams.energy_nut; } function onExpLoad() { for (var pop in Populations) pop.clear(); Loader.addClass(sim_params.*); Loader.setBreakLabel(Loader.BeforeUnknown, "onExpLoad_Unknown"); Loader.run(); Simulator.print("Loaded " + Populations[0].size + " Forams and " + Populations[1].size + " nutrient objects"); } function onExpLoad_Unknown() { if (Loader.objectName == "org") // saved by the old expdef { var g = Genotype.newFromString(""); Loader.currentObject = g; Interface.makeFrom(g).setAllDefault(); Loader.loadObject(); var cr = Populations[0].add(g); if (cr != null) { //cr.rotate(0,0,Math.rnd01*Math.twopi); if ((typeof(g.user1) == "Vector") && (g.user1.size >= 3)) { // [x,y,energy] cr.move(g.user1[0] - cr.center_x, g.user1[1] - cr.center_y, 0); cr.energy = g.user1[2]; } else { cr.move(Math.rnd01 * World.wrldsiz - cr.center_x, Math.rnd01 * World.wrldsiz - cr.center_y, 0); } } } else if (Loader.objectName == "Creature") { Loader.currentObject = CreatureSnapshot.new(); Loader.loadObject(); Populations[0].add(Loader.currentObject); } } function onExpSave() { File.writeComment("saved by '%s.expdef'" % Simulator.expdef); var tmpvec = [], i; for(var cr in Populations[1]) tmpvec.add([cr.center_x, cr.center_y, cr.energy]); ExpState.nutrient = tmpvec; File.writeObject(sim_params.*); ExpState.nutrient = null; //vectors are only created for saving and then discarded for (var cr in Populations[0]) File.writeObject(cr); } // -------------------------------- experiment end -------------------------------- // -------------------------------- foram begin ----------------------------------- function onForamsBorn(cr) { cr.idleen = ExpParams.e_meta; } function placeRandomlyNotColliding(cr) { var retry = 100; //try 100 times while (retry--) { placeCreatureRandomly(cr, 0, 0); if (!cr.boundingBoxCollisions(0)) return cr; } Populations[0].delete(cr); } function foramGrow(cr, chamber_num) { var geno = createForamGenotype(cr.user2["gen"], cr.user2["species"], chamber_num); var cr2 = Populations[0].add(geno); cr2.energy0 = cr.energy; cr2.energy = cr2.energy0; setGenotype({"cr" : cr2, "parent_user1" : cr.user1, "parent_user2" : cr.user2, "opt" : 2}); cr2.moveAbs(cr.center_x - cr2.size_x / 2, cr.center_y - cr2.size_y / 2, cr.pos_z); Populations[0].delete(cr); } function stepToNearest(cr, range) { var p = cr.getMechPart(0); var n = cr.signals.receiveSet("nutrient", ExpParams.zone2_range); //if signals are received find the source of the nearest if (n.size > 0) { var i; var mp; var distvec = XYZ.new(0, 0, 0); var dist; var mindist = 100000000000; var mindistvec = null; for (i = 0; i < n.size; i++) { mp = n[i].value; distvec.set(mp.pos); distvec.sub(p.pos); dist = distvec.length; if (dist < mindist) { mindist = dist; mindistvec = distvec.clone(); } } mindistvec.normalize(); mindistvec.scale(-0.08); cr.localDrive = mindistvec; return 1; } else return 0; } function moveEnergyDec(cr) { if (cr.user2["hibernated"] == 1) { cr.energy_m += ExpParams.energy_hib; } else { cr.energy_m += ExpParams.energy_move; } } function foramMove(cr) { //TODO moving inside sediment? //adjustment in z axis cr.moveAbs(cr.pos_x, cr.pos_y, 0.5); //are there any nutrients in zone 1? if (cr.boundingBoxCollisions() == 2) { if (cr.user2["hibernated"] == 1) cr.user2["hibernated"] = 0; return; } else if (cr.user2["hibernated"] == 1) { moveEnergyDec(cr); return; } //are there any nutrients in zone 2? else { var moved = stepToNearest(ExpParams.zone2_range); //TODO weighted sum of distance and energy if (moved==1) { moveEnergyDec(cr); return; } } //no nutrients in zone 2 var hibernation = 0; if (cr.user2["gen"] == 0) hibernation = cr.user1["hibernation"]; else hibernation = cr.user1[0]["hibernation"]; //hibernation if (hibernation == 1) { moveEnergyDec(cr); cr.user2["hibernated"] = 1; cr.localDrive = XYZ.new(0,0,0); } //random move else { cr.localDrive = (0.1 * Math.rnd01, 0.1 * Math.rnd01, 0); moveEnergyDec(cr); } } function onForamsCollision() { if (Collision.Creature1.user2 != null) { Collision.Creature1.localDrive = XYZ.new(0,0,0); var e = Collision.Part2.ing * ExpParams.feedtrans; //TODO efficiency dependent on age //Simulator.print("transferring "+e+" to "+Collision.Creature1.name+" from "+Collision.Creature2.name+" ("+Collision.Creature2.energy+")"); Collision.Creature2.energy_m = Collision.Creature2.energy_m + e; Collision.Creature1.energy_p = Collision.Creature1.energy_p + e; Collision.Creature1.user2["hibernated"] = 0; } } function onForamsStep(cr) { cr.getMechPart(0).orient.set(o); if (deathConditions(cr) == 1) { Populations[0].kill(cr); return; } foramMove(cr); cr.user2["max_energy_level"] = Math.max(cr.energy, cr.user2["max_energy_level"]); if (cr.numparts <= chambers[0].size) { if ((cr.user2["max_energy_level"] >= max_chamber_energ[cr.user2["gen"]][cr.numparts-1])) { foramGrow(cr, cr.numparts); return; } } foramReproduce(cr); } function deathConditions(cr) { if ((cr.energy <= ExpParams.e_death_level) || (Math.rnd01 < ExpParams.hunted_prob)) return 1; else return 0; } function onForamsDied(cr) { //fossilization var geno = GenePools[0].add(cr.genotype); geno.user1 = cr.user1; geno.user2 = cr.user2; if (ExpParams.logging == 1) Simulator.print("\"" + cr.name + "\" died..."); ExpState.totaltestedcr++; } // --------------------------------foram end ------------------------------------- // -------------------------------- nutrient begin -------------------------------- function createNutrientGenotype(nutrientsize, zone1_range) { return "//0\np:sh=3,sx="+nutrientsize+",sy="+(zone1_range + nutrientsize)+",sz="+(zone1_range+nutrientsize)+",ry=1.5,vr=0.0,1.0,0.0"; } function onNutrientsStep(cr) { cr.moveAbs(cr.pos_x % ExpParams.world_size, cr.pos_y % ExpParams.world_size, -ExpParams.zone1_range+0.5); } function addNutrient() { var cr = Populations[1].add(createNutrientGenotype(ExpParams.nutrientsize, ExpParams.zone1_range)); cr.name = "Nutrients"; cr.idleen = 0; cr.energy0 = ExpParams.energy_nut; cr.energy = cr.energy0; cr.signals.add("nutrient"); cr.signals[0].value = cr.getMechPart(0); placeCreatureRandomly(cr, 0, 0); } function nutrientGrowth() { nutrientenergywaiting = nutrientenergywaiting + ExpParams.feedrate; if (nutrientenergywaiting > ExpParams.energy_nut) { for (var i = 0; i < ExpParams.nutrientPop; i++) { addNutrient(); } nutrientenergywaiting = 0.0; Simulator.checkpoint(); } } // -------------------------------- nutrient end -------------------------------- // -------------------------------- step begin -------------------------------- function onStep() { if (ExpParams.logging == 1) { createStatistics(); } nutrientGrowth(); //reproduction -------------------------------------------- reprocounter += 1; if (reprocounter > ExpParams.repro_time) { reprocounter = 0; reproduce_parents(0); reproduce_parents(1); } //check for extinction ----------------------------------------------- if (Populations[0].size == 0) { if (ExpParams.autorestart) { Simulator.print("no more creatures, restarting..."); onExpInit(); } else { Simulator.print("no more creatures, stopped."); Simulator.stop(); } } } function createStatistics() { var number_ploidy = [0, 0]; var number_species = [0, 0]; var e_inc = [0.0, 0.0]; var e_nut = 0.0; for (var i = 0; i < Populations[0].size; i++) { var cr = Populations[0].get(i); var gen = cr.user2["gen"]; number_ploidy[gen] = number_ploidy[gen] + 1; var species = cr.user2["species"]; number_species[species] = number_species[species] + 1; e_inc[gen] = e_inc[gen] + cr.energy; } for (var i = 0; i < Populations[1].size; i++) { var cr = Populations[1].get(i); e_nut += cr.energy; } var log_numbers = [number_ploidy[0], number_ploidy[1], Populations[1].size]; var log_species = [number_species[0], number_species[1]]; var log_energy = [e_inc[0], e_inc[1], e_nut]; log(log_numbers, "log_sizes.txt"); log(log_species, "log_species.txt"); log(log_energy, "log_energy.txt"); } function log(tolog, fname) { var f = File.appendDirect(fname, "forams data"); f.writeString("" + Simulator.stepNumber); for (var i = 0; i < tolog.size; i++) { f.writeString(";" + tolog[i]); } f.writeString("\n"); f.close(); } // -------------------------------- step end -------------------------------- @include "standard_events.inc" ~ prop: id:e_repro_cost_haplo name:Cost of reproduction type:f 0.1 0.9 0.5 group:Foraminifera prop: id:e_repro_cost_diplo name:Cost of reproduction type:f 0.1 0.9 0.3 group:Foraminifera prop: id:chamber_proculus_haplo name:Size of proculus type:f group:Foraminifera prop: id:chamber_proculus_diplo name:Size of proculus type:f group:Foraminifera prop: id:chamber_difference_haplo name:Difference in size between subsequent chambers type:f group:Foraminifera prop: id:chamber_difference_diplo name:Difference in size between subsequent chambers type:f group:Foraminifera prop: id:hunted_prob name:Probability of being hunted type:f 0 1 0 group:Forminifera prop: id:zone1_range name:Zone 1 range type:f 0 200 group:Foraminifera prop: id:zone2_range name:Zone 2 range type:f 0 3000 group:Foraminifera prop: id:colors name:Haploid and diploid colors type:x group:Foraminifera prop: id:min_repro_energ_haplo name:Min reproduction energy of forams type:f group:Foraminifera prop: id:min_repro_energ_diplo name:Min reproduction energy of forams type:f group:Foraminifera prop: id:repro_prob name:Probability of reproduction type:f 0 1 0.8 group:Foraminifera prop: id:energies0_haplo name:Energy of offspring from diploid forams type:f group:Foraminifera prop: id:energies0_diplo name:Energy of offspring from diploid forams type:f group:Foraminifera prop: id:energy_hib name:Energy used for hibernation during one step type:f 0 1 0.001 group:Foraminifera prop: id:energy_move name:Energy used for movement during one step type:f 0 20 0.001 group:Foraminifera prop: id:min_vol name:Minimal volume for reproduction type:f 100 900 100 group:Foraminifera prop: id:max_size name:Maximal size type:d 1 10 5 group:Foraminifera prop: id:foramPop name:Initial forams population size type:d 1 1000 100 group:Foraminifera prop: id:crossprob name:Crossover probability type:f 0 1 0 group:Foraminifera prop: id:mutationprob name:Mutation probability type:f 0 1 0 group:Foraminifera prop: id:e_death_level name:Minimal level of energy to sustain life type:f 0 20 0 group:Foraminifera prop: id:e_meta name:Idle metabolism type:f 0 1 group:Energy help:Each stick consumes this amount of energy in one time step prop: id:feedrate name:Feeding rate type:f 0 100 group:Energy help:How fast energy is created in the world prop: id:energy_nut name:Nutrients's energy type:f 0 1000 group:Energy prop: id:feedtrans name:Ingestion multiplier type:f 0 100 group:Energy prop: id:nutrientsize name:Nutrients's size type:f 0.1 0.9 0.1 group:Energy prop: id:e_meta name:Idle metabolism type:f 0 1 group:Energy help:Each stick consumes this amount of energy in one time step prop: id:feedrate name:Feeding rate type:f 0 100 group:Energy help:How fast energy is created in the world prop: id:feedtrans name:Ingestion multiplier group:Energy type:f 0 100 prop: id:nutrientsize name:Nutrients's size group:Energy type:f 0.1 prop: id:nutrientPop name:Nutrients population size group:Energy type:d 1 1000 10 prop: id:world_size name:World size type:d 10 1000 20 group:World prop: id:stress name:Environmental stress type:d 0 1 1 group:World prop: id:repro_trigger name:Reproduction trigger type:d 0 1 1 group:World prop: id:repro_time name:Time before reproduction type:d 0 1000 prop: id:creath name:Creation height type:f -1 50 help:~ Vertical position (above the surface) where new Forams are revived. Negative values are only used in the water area: 0 = at the surface -0.5 = half depth -1 = just above the bottom~ state: id:nutrient name:Nutrients locations help:vector of vectors [x,y,energy] type:x flags:32 prop: id:autorestart name:Restart after extinction help:Restart automatically this experiment after the last creature has died? type:d 0 1 state: id:notes name:Notes type:s 1 help:~ You can write anything here (it will be saved to the experiment file)~ state: id:totaltestedcr name:Evaluated Forams help:Total number of the Forams evaluated in the experiment type:d flags:16 prop: id:logging name:Log statistics to file type:d 0 1 0