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: minenergy - Minimum stored energy necessary for reproduction minage - minimal age for reproduction user2: Physiological parameters of foraminifera: Va - amount of the stored energy gen - generation: 0 haploid, 1 diploid ~ 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; @include "foraminifera.inc" // -------------------------------- experiment begin -------------------------------- function onExpDefLoad() { // define genotype and creature groups GenePools.clear(); Populations.clear(); GenePools[0].name = "Unused"; SignalView.mode = 1; 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; //radius of the chamber ExpParams.rads = [1.2, 0.6]; //inital genotypes ExpParams.genh = createForamGenotype(ExpParams.rads[0]); ExpParams.gend = createForamGenotype(ExpParams.rads[1])+"\nn:p=0,d=\"S\""; ExpParams.creath = -0.99; //just above the bottom ExpParams.e_meta = 0.1; ExpParams.feedrate = 0.5; ExpParams.feede0 = 100; ExpParams.feedtrans = 3; ExpParams.nutrientsize = 0.1; ExpParams.nutrientgen = "//0\np:sh=2,sx="+ExpParams.nutrientsize+",sy="+ExpParams.nutrientsize+",sz="+ExpParams.nutrientsize+"\nn:d=T,p=0"; ExpParams.autorestart = 0; ExpParams.popsize = 10; //ExpParams.logging = 1; //uncomment to enable logging simulation parameters to log files //number of offspring ExpParams.energy0h = 40; ExpParams.energy0d = 25; //minial volume for reproduction ExpParams.minenerg = [300, 300]; //minimal age for reproduction ExpParams.minage = [100, 100]; //crossover probability ExpParams.crossprob = 0.4; //mutation probability ExpParams.mutationprob = 0.2; ExpParams.repro_time = 45; //grwoth speed in simulation steps ExpParams.growth_step = 50; ExpState.totaltestedcr = 0; ExpState.nutrient = ""; nutrientenergywaiting = ExpParams.feede0; reprocounter = 0; ExpParams.worldsize = 50; World.wrldwat = 50; World.wrldsiz = ExpParams.worldsize; World.wrldbnd = 1; } @include "standard_placement.inc" function onExpInit() { Populations[0].clear(); Populations[1].clear(); for (var i = 0; i < ExpParams.popsize; i++) { var cr = Populations[0].add(ExpParams.genh); cr.name = "Initial creature" + i; placeCreatureRandomly(cr, 0, 0); cr.energy0 = ExpParams.energy0h; cr.energy = cr.energy0; cr.user1 = {"minenergy" : ExpParams.minenerg[0], "minage": ExpParams.minage[0]}; cr.user2 = {"Va" : ExpParams.energy0h, "gen" : 0, "growth_step" : ExpParams.growth_step, "rsize" : ExpParams.rads[0], "vinit" : ExpParams.energy0h}; } ExpState.totaltestedcr = 0; nutrientenergywaiting = ExpParams.feede0; } 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 createForamGenotype(radius) { return "//0\np:sh=1,sx=" + radius + ",sy=" + radius + ",sz=" + radius + ", rz=3.14159265358979"; } 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 readyToRepro(cr) { cr.signals.add("repro"); cr.signals[0].power = 1; } function foramMove(cr) { //TODO moving inside sediment? cr.moveAbs(cr.pos_x, cr.pos_y, 0); //adjustment in z axis var p = cr.getMechPart(0); var n = cr.signals.receiveSet("nutrient", ExpParams.nutrient_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; } else { cr.localDrive = (0.1 * Math.rnd01, 0.1 * Math.rnd01, 0); } } function foramGrow(cr) { //TODO how size is related to the energy? cr.user2["rsize"] = ExpParams.rads[cr.user2["gen"]] * Math.min(Math.max(float(cr.user2["Va"] / cr.user2["vinit"]) * 0.5, 1.0), 2.5); var geno = createForamGenotype(cr.user2["rsize"]); if (cr.user2["gen"] == 1) { geno += "\nn:p=0,d=\"S\""; //TODO why initial genotypes are not used as defined in ExpParams? //TODO maybe it would be nice if they rotated so the "S" would show where they are going (direction/intention) } var cr2 = Populations[0].add(geno); cr2.energy0 = cr.energy; cr2.energy = cr2.energy0; setGenotype(cr2, cr.user1, cr.user2); cr2.moveAbs(cr.center_x - cr2.size_x / 2, cr.center_y - cr2.size_y / 2, cr.pos_z); Populations[0].delete(cr); } function onForamsStep(cr) { foramMove(cr); //energy costs depend on size if (cr.energy > 100) { //TODO energy costs dependent on size // cr.energy_m = cr.user2["Va"]/cr.user2["vinit"]; } //TODO lifespan should not be used, it is set to 0 with every growth_step if (cr.lifespan >= ExpParams.max_age) { //TODO what is max age value? should there be one //Populations[0].kill(cr); //return; } //foram growth if (cr.lifespan == cr.user2["growth_step"]) { foramGrow(cr); } else { var properSize = 0; if (cr.user2["gen"] == 0) { properSize = cr.user2["Va"] >= cr.user1["minenergy"]; } else { properSize = cr.user2["Va"] >= cr.user1[0]["minenergy"]; //TODO gene selection } //if creature has proper age and cytoplasm amount if ( properSize ) { //reproduce with probability repro_prob if (Math.rnd01 <= ExpParams.repro_prob) { readyToRepro(cr); } } if ( properSize && (cr.signals.receive("repro") > 0)) { readyToRepro(cr); } } } 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++; } function setGenotype(cr, new_user1, new_user2) { cr.user2 = {"Va" : new_user2["Va"], "gen" : new_user2["gen"], "growth_step" : new_user2["growth_step"], "rsize" : new_user2["rsize"], "vinit": new_user2["vinit"]}; if (cr.user2["gen"] == 0) { cr.user1 = {"minenergy" : new_user1["minenergy"], "minage": new_user1["minage"] }; } else if (cr.user2["gen"] == 1) { cr.user1 = [ {"minenergy" : new_user1[0]["minenergy"], "minage": new_user1[0]["minage"] }, {"minenergy" : new_user1[1]["minenergy"], "minage": new_user1[1]["minage"] }]; } } // --------------------------------foram end ------------------------------------- // -------------------------------- nutrient begin -------------------------------- function onNutrientsStep(cr) { cr.moveAbs(cr.pos_x % ExpParams.worldsize, cr.pos_y % ExpParams.worldsize, 0.5); } function addNutrient() { var cr = Populations[1].add(ExpParams.nutrientgen); cr.name = "Nutrients"; cr.idleen = 0; cr.energy0 = ExpParams.feede0; cr.energy = cr.energy0; cr.signals.add("nutrient"); cr.signals[0].value = cr.getMechPart(0); placeRandomlyNotColliding(cr); } function onNutrientsCollision() { if (Collision.Creature2.user2 != null) { var e = Collision.Part2.ing * ExpParams.feedtrans; //Simulator.print("transferring "+e+" from "+Collision.Creature1.name+" to "+Collision.Creature2.name+" ("+Collision.Creature2.energy+")"); Collision.Creature1.energy_m = Collision.Creature1.energy_m + e; Collision.Creature2.energy_p = Collision.Creature2.energy_p + e; Collision.Creature2.user2["Va"] = float(Collision.Creature2.user2["Va"]) + float(e); } } // -------------------------------- nutrient end -------------------------------- 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(); } @include "standard_events.inc" ~ prop: id:max_age name:Maximal age type:d 100 1000 500 group:Foraminifera prop: id:worldsize name:World size type:d 10 1000 20 prop: id:growth_step name:Growth step type:d -1 10000 1000 group:Foraminifera help:You can turn off growth by setting this param to -1 prop: id:rads name:Haploid and diploid radius type:x group:Foraminifera prop: id:minage name:Min reproduction age of forams type:x group:Foraminifera prop: id:minenerg name:Min reproduction energy of forams type:x group:Foraminifera prop: id:nutrient_range name:Range of nutrient smell type:d 0 10000 10000 prop: id:repro_time name:Time before reproduction type:d 0 1000 prop: id:popsize name:Initial forams population size type:d 1 1000 100 group:Foraminifera prop: id:energy0h name:Number of offspring from haploid forams type:d group:Foraminifera prop: id:energy0d name:Number of offspring from diploid forams type:d group:Foraminifera prop: id:repro_prob name:Probability of reproduction type:f 0 1 0.9 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:genh name:Initial genotype of haploid forams type:s 1 group:Foraminifera prop: id:gend name:Initial genotype of diploid forams type:s 1 group:Foraminifera 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~ 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:feede0 name:Nutrients's energy group:Energy type:f 0 1000 prop: id:feedtrans name:Ingestion multiplier group:Energy type:f 0 100 prop: id:nutrientgen name:Nutrients's genotype group:Energy type:s 1 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 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