source: experiments/frams/foraminifera/data/scripts/foraminifera.expdef @ 847

Last change on this file since 847 was 847, checked in by Maciej Komosinski, 5 years ago

Code formatting

File size: 32.7 KB
RevLine 
[380]1expdef:
[406]2name:Reproduction of benthic foraminifera
[380]3info:~
[434]4Basic information about this simulation:
5www.framsticks.com/foraminifera
6
7Technical information:
[476]8Genes and parameter values which control reproduction are stored in data->genes and data->lifeparams fields.
[380]9
[476]10genes:
[402]11genes which are not encoded in Ff genotype:
[422]12min_repro_energy - Minimum energy necessary for reproduction
13hibernation - Defines foram behavior in the case of no nutrients
[380]14
[476]15lifeparams:
[380]16Physiological parameters of foraminifera:
[422]17max_energy_level - maximum energy level reached so far
[380]18gen - generation: 0 haploid, 1 diploid
[432]19species - species: 0 not hibernating 1 hibernating
20hibernated - 0/1 foram is/isn't hibernated
[422]21reproduce - 0/1 foram isn't/is ready for reproduction
[380]22~
23code:~
24
[422]25global colors;
[493]26global curColor;
[479]27global max_chamber_volume;
[474]28global movePerStep;
[479]29global reprocounter;
[481]30global changePeriod;
31global phase;
[493]32global nutrientSqrCm;
[567]33global species_genes;
34global max_chambers_def;
35global foram_uid; //introduced because each replacement of a creature (while growing) would generate a new Creature.uid
36global chamber_vis_denominator;
37global curRadius;
[590]38global nutrient_num_counter;
39global wrldsizSquareMeters;
[380]40
[406]41@include "foraminifera.inc"
[401]42
[380]43// -------------------------------- experiment begin --------------------------------
44
45function onExpDefLoad()
46{
47        // define genotype and creature groups
48        GenePools.clear();
49        Populations.clear();
50        GenePools[0].name = "Unused";
51
52        var pop = Populations[0];
[421]53        pop.name = "Forams";
[380]54        pop.en_assim = 0;
[404]55        pop.nnsim = 0;
[380]56        pop.enableperf = 1;
57        pop.death = 1;
58        pop.energy = 1;
[487]59        pop.selfmask = 0;
60        pop.othermask = 0;
[401]61        //pop.selfmask = 0x20002; pop.othermask = 0x10002;
[847]62        pop.perfperiod = 25;
[487]63        pop.bodysim = 0;
[380]64
[421]65        pop = Populations.addGroup("Nutrients");
[380]66        pop.nnsim = 0;
67        pop.enableperf = 0;
68        pop.death = 1;
69        pop.energy = 1;
[487]70        pop.selfmask = 0;
71        pop.othermask = 0;
[422]72        //pop.othermask = 0x10002;
[487]73        pop.bodysim = 0;
[380]74
[444]75        pop = Populations.addGroup("ReticulopodiaNutrients");
[430]76        pop.nnsim = 0;
77        pop.enableperf = 0;
78        pop.death = 0;
79        pop.energy = 0;
[487]80        pop.selfmask = 0;
81        pop.othermask = 0;
82        pop.bodysim = 0;
[444]83
[422]84        //world
85        SignalView.mode = 1;
[496]86        World.wrldwat = 200;
[493]87        World.wrldsiz = micronsToFrams(100000);
[847]88        wrldsizSquareMeters = Math.pow(framsToMicrons(World.wrldsiz) * 0.000001, 2);
[422]89        World.wrldbnd = 1;
90
[486]91        //ExpProperties.visualize = 1; //uncomment to visualize reticulopodia and indicate nutrients positions
[444]92
[847]93        //ExpProperties.logging = 1; //uncomment to enable logging simulation parameters to log files
[493]94        ExpProperties.logPref = "";
[401]95
[554]96        //morphology
[486]97        ExpProperties.zone1_range = micronsToFrams(1000);
98        ExpProperties.zone2_range = micronsToFrams(3000);
[567]99        ExpProperties.chamber_proculus_haplo = micronsToFrams(20);
100        ExpProperties.chamber_proculus_diplo = micronsToFrams(10);
[847]101        colors = ["1.0,1.0,0.3", "1.0,0.7,0.0"]; //yellow and orange
[579]102        curColor = colors[0];
[567]103        curRadius = ExpProperties.zone1_range;
[479]104
[422]105        //nutrients
[486]106        ExpProperties.nutrientradius = micronsToFrams(10);
[847]107        ExpProperties.energy_nut = 200 * energyFromVolume(ExpProperties.nutrientradius, 1);
[554]108        nutrientSqrCm = 10;
[847]109        ExpProperties.nutrient_pop = Math.pow(framsToMicrons(World.wrldsiz) * 0.0001, 2) / nutrientSqrCm;
[554]110
[380]111        ExpState.totaltestedcr = 0;
[421]112        ExpState.nutrient = "";
[567]113
114        max_chambers_def = 35;
[569]115        chamber_vis_denominator = 12;
[493]116        //addSpecies({"min_repro_energies" : [4,6]});
117        //addSpecies({"min_repro_energies" : [4,8]});
[847]118
[561]119        //Simulator.print(create_genotype(0.2, 30, "1.0,1.0,0.0", 0.6)); //sample call
120        //Simulator.print(create_genotype(0.1, 40, "1.0,0.5,0.0", 0.1)); //sample call
[380]121}
122
123@include "standard_placement.inc"
124
[567]125function onExpInit()
[475]126{
[568]127        species_genes = [];
[567]128        foram_uid = 0;
[568]129        movePerStep = getMovePerStep();
[475]130
[567]131        Populations[0].clear();
132        Populations[1].clear();
133        Populations[2].clear(); //reticulopodia and nutrients
134
135        if (ExpProperties.max_chamber_num_haplo == max_chambers_def && ExpProperties.max_chamber_num_diplo == max_chambers_def)
[479]136        {
[847]137                max_chamber_volume = [[30403.5869594578, 52812.2546633948, 79578.5148482541, 113588.815134453, 154620.677376218, 205094.322220826, 262572.712174265, 326078.453295303, 402342.518962956, 498133.985678766, 615066.864740109, 759500.497626816, 937064.025544282, 1155915.25563075, 1429139.14079748, 1762487.92940157, 2176286.62046069, 2685795.63187845, 3316190.12127043, 4096436.04462706, 5051343.25226193, 6231980.1061213, 7687880.79524734, 9485307.02904958, 11716968.9852569, 14459866.4934433, 17836388.9853663, 22004935.7247348, 27138607.2546045, 33482425.1582986, 41336775.1280297, 50997910.7842793, 62888631.7871037, 77563060.9243464, 95659468.591964]
138                                      , [3430.07716920763, 6159.93090353532, 9322.94192815286, 13462.9896597283, 18399.8550832969, 24558.9218126892, 31468.8148639192, 39189.4977865513, 48404.4292075836, 60185.8639086061, 74490.6048472854, 92117.8178412275, 113852.779747083, 140714.366929552, 174450.937643841, 215250.242147183, 266323.295274072, 328858.042352538, 406552.379957238, 503526.321155323, 621060.781025019, 767240.824049468, 947210.683224091, 1169506.19906112, 1447211.61255879, 1787155.29073739, 2205627.64766244, 2723413.2837305, 3360233.53738709, 4147771.02835393, 5126445.06973928, 6328060.3331703, 7805693.278958, 9631924.72156452, 11884287.1596814]];
[479]139        }
140
[567]141        else
[479]142        {
[567]143                max_chamber_volume = [Vector.new(), Vector.new()];
144                var density = 100;
[847]145                for (var ploid = 0; ploid < 2; ploid++)
[567]146                {
147                        var rad = getPloidRadius(ploid);
[847]148                        for (var cham_num = 0; cham_num < getProperty(ploid, "max_chamber_num"); cham_num++)
[567]149                        {
[847]150                                max_chamber_volume[ploid].add(volumeFromGeno(ploid, rad, cham_num + 1, density));
151                        }
[567]152                }
[479]153        }
[422]154
[487]155        if (species_genes.size == 0)
[380]156        {
[487]157                addSpecies({}); //default
[380]158        }
[487]159
160        for (var spec = 0; spec < species_genes.size; spec++)
161        {
162                for (var i = 0; i < ExpProperties.foramPop; i++)
163                {
[847]164                        addInitialForam(spec, i);
[487]165                }
166        }
[380]167        ExpState.totaltestedcr = 0;
[554]168
169        reprocounter = 0;
[590]170        nutrient_num_counter = 0;
[554]171        changePeriod = 0;
172        phase = "low";
[380]173}
174
175function onExpLoad()
176{
177        for (var pop in Populations)
178                pop.clear();
179
180        Loader.addClass(sim_params.*);
181        Loader.setBreakLabel(Loader.BeforeUnknown, "onExpLoad_Unknown");
182        Loader.run();
183
[421]184        Simulator.print("Loaded " + Populations[0].size + " Forams and " + Populations[1].size + " nutrient objects");
[380]185}
186
187function onExpLoad_Unknown()
188{
189        if (Loader.objectName == "org") // saved by the old expdef
190        {
191                var g = Genotype.newFromString("");
192                Loader.currentObject = g;
193                Interface.makeFrom(g).setAllDefault();
194                Loader.loadObject();
195                var cr = Populations[0].add(g);
196                if (cr != null)
197                {
[401]198                        //cr.rotate(0,0,Math.rnd01*Math.twopi);
[476]199                        if ((typeof(g.data->genes) == "Vector") && (g.data->genes.size >= 3))
[401]200                        {
201                                // [x,y,energy]
[638]202                                cr.move(g.data->genes[0] - cr.bboxCenter.x, g.data->genes[1] - cr.bboxCenter.y, 0);
[476]203                                cr.energy = g.data->genes[2];
[380]204                        }
205                        else
206                        {
[638]207                                cr.move(Math.rnd01 * World.wrldsiz - cr.bboxCenter.x, Math.rnd01 * World.wrldsiz - cr.bboxCenter.y, 0);
[380]208                        }
209                }
210        }
211        else if (Loader.objectName == "Creature")
212        {
213                Loader.currentObject = CreatureSnapshot.new();
214                Loader.loadObject();
215                Populations[0].add(Loader.currentObject);
216        }
217}
218
219function onExpSave()
220{
221        File.writeComment("saved by '%s.expdef'" % Simulator.expdef);
222
[401]223        var tmpvec = [], i;
[380]224
[401]225        for(var cr in Populations[1])
[638]226                tmpvec.add([cr.bboxCenter.x, cr.bboxCenter.y, cr.energy]);
[380]227
[421]228        ExpState.nutrient = tmpvec;
[380]229        File.writeObject(sim_params.*);
[432]230        ExpState.nutrient = null; //vectors are only created for saving and then discarded
[380]231
232        for (var cr in Populations[0])
233                File.writeObject(cr);
234}
235
236// -------------------------------- experiment end --------------------------------
237
[567]238function volumeFromGeno(morphotype, rad, chamber_num, density)
239{
240        var geno = create_genotype(rad, chamber_num, colors[morphotype], 1);
[847]241        var m = Model.newFromString(geno);
242        var mg = ModelGeometry.forModel(m);
243        mg.geom_density = density;
[567]244        var volumeInFrams = mg.volume();
245
[847]246        return volumeInFrams / Math.pow(ExpProperties.scalingFactor, 3);
[567]247}
248
[847]249function secToSimSteps(value_in_sec)
250{
251        return value_in_sec / ExpProperties.secPerStep;
[567]252}
253
254function volumeInMicrons(radiusInFrams)
255{
[847]256        return 4.0 / 3.0 * Math.pi * Math.pow(framsToMicrons(radiusInFrams), 3);
[567]257}
258
259function energyFromVolume(base, isRadiusInFrams)
260{
261        if (isRadiusInFrams == 1) //radius in frams
262        {
[847]263                return ExpProperties.picoCarbonPerMikro * volumeInMicrons(base);
[567]264        }
265        else //volume in microns
266        {
267                return ExpProperties.picoCarbonPerMikro * base;
268        }
269}
270
271function getMovePerStep()
272{
[847]273        return micronsToFrams((ExpProperties.foramSpeedMmPerMin / 60) * 1000) * ExpProperties.secPerStep;
[567]274}
275
276function micronsToFrams(micrometers)
277{
[847]278        return micrometers * ExpProperties.scalingFactor;
[567]279}
280
281function framsToMicrons(framsworldunits)
282{
[847]283        return framsworldunits / ExpProperties.scalingFactor;
[567]284}
285
286function getProperty(gen, prop_id)
287{
288        var ploid = "haplo";
289        if (gen == 1) ploid = "diplo";
290        return ExpProperties.[prop_id + "_" + ploid];
291}
292
293function getGene(cr, gen_id, gen_set)
294{
[847]295        if (cr.data->lifeparams->gen == 0)
[567]296                return cr.data->genes[gen_id];
[847]297        else
[567]298                return cr.data->genes[gen_set][gen_id];
299}
300
301function getPloidRadius(ploid)
302{
303        var radius = ExpProperties.chamber_proculus_haplo;
[847]304        if (ploid == 1)
305        {
306                radius = ExpProperties.chamber_proculus_diplo;
307        }
[567]308        return radius;
309}
310
311function chamberNumFromEnergy(energy, ploid)
312{
313        var chamber_num = max_chamber_volume[ploid].size;
314        for (var i = 0; i < chamber_num; i++)
315        {
[847]316                if (energy < energyFromVolume(max_chamber_volume[ploid][i], 0))
[567]317                {
[847]318                        chamber_num = i + 1;
[567]319                        break;
[847]320                }
321        }
322
[567]323        return chamber_num;
324}
325
[595]326function createAndRotate(geno, rotate_min, rotate_max, pop_num)
327{
328        var cr = Populations[pop_num].add(geno);
[847]329        cr.rotate(0, 0, Math.rndUni(rotate_min, rotate_max));
330        return cr;
[595]331}
332
333//TODO unifiy addForam, foramGrow and createOffspring
[567]334function addForam(species, iter, start_energy, ploid)
335{
336        var chambernum =  chamberNumFromEnergy(start_energy, ploid);
337        var radius = getPloidRadius(ploid);
338        var geno = create_genotype(radius, chambernum, colors[ploid], 1);
[579]339        curColor = colors[ploid];
[847]340        var cr = createAndRotate(geno, 0, 2 * Math.pi, 0);
[567]341        cr.name = "Initial creature" + species + "_" + iter;
[588]342        placeRandomlyNotColliding(cr);
[567]343        cr.energy = start_energy;
344        setGenotype({"opt" : "birth", "cr" : cr, "gen" : ploid, "species" : species, "energy0" : cr.energy, "genes" : species_genes[species], "parentsuids" : ["c0"]});
345        if (ploid == 1)
346        {
347                cr.data->genes = [cr.data->genes, cr.data->genes]; //TODO two different genes sets
348        }
349        moveReticulopodia(cr);
350}
351
352function addInitialForam(species, iter)
353{
354        var ploid = 0;
355        if (Math.rnd01 > 0.5)
356        {
357                ploid = 1;
[847]358        }
[567]359        //add new foram with random energy bewtween starting energy and reproduction threshold
360        var repro_thr = species_genes[species]->min_repro_energies[ploid];
[847]361        var start_energy = Math.rndUni(energyFromVolume(getPloidRadius(ploid), 1), repro_thr - 0.25 * repro_thr);
362        addForam(species, iter, start_energy, ploid);
[567]363}
364
365//new species can be added as a dictionary with parameter values that are different than default values
366function addSpecies(new_genes)
367{
[847]368        species_genes.add({"min_repro_energies" : [ExpProperties.min_repro_energ_haplo, ExpProperties.min_repro_energ_diplo], "energies0" : [ExpProperties.energies0_haplo, ExpProperties.energies0_diplo], "hibernation" : 0, "morphotype" : 0});
[567]369        for (var i = 0; i < new_genes.size; i++)
370        {
371                var key = new_genes.getKey(i);
[847]372                species_genes[species_genes.size - 1][key] = new_genes[key];
373        }
[567]374}
375
[421]376// -------------------------------- foram begin -----------------------------------
[380]377
[479]378function setForamMeta(cr)
[430]379{
[479]380        //percent of current energy
[847]381        cr.idleen = (ExpProperties.e_meta * cr.energy) * ExpProperties.secPerStep;
[430]382}
383
384function lastChamberNum(cr)
385{
[567]386        return cr.numparts;
[430]387}
388
[479]389function getZoneRange(cr, zone_num)
390{
[847]391        return ExpProperties.["zone" + zone_num + "_range"];
[479]392}
393
[567]394function addReticulopodia(cr, radius)
395{
[847]396        if (reticulopodiaExists(cr))
397        {
398                Populations[2].delete(cr.data->reticulopodiacreature);
399        }
400        var ret = Populations[2].add("//0\nm:Vstyle=reticulopodia\np:sh=1,sx=0.001,sy=0.001,sz=0.001\np:sh=3,sx=0.01,sy=" + radius + ",sz=" + radius + ",ry=1.57079633,vr=" + curColor + "\nj:0, 1, sh=1");
401        cr.data->reticulopodiacreature = ret;
402        ret.getMechPart(0).orient.set(cr.getMechPart(0).orient);
403        ret.locationSetBboxLow(cr.bboxCenter.x - radius, cr.bboxCenter.y - radius, cr.bboxCenter.z - radius);
[567]404}
405
[421]406function onForamsBorn(cr)
407{
[479]408        setForamMeta(cr);
[486]409        if (ExpProperties.visualize == 1)
[430]410        {
[567]411                addReticulopodia(cr, curRadius);
[583]412                moveReticulopodia(cr);
[430]413        }
[380]414}
415
[401]416function placeRandomlyNotColliding(cr)
417{
418        var retry = 100; //try 100 times
419        while (retry--)
420        {
421                placeCreatureRandomly(cr, 0, 0);
422                if (!cr.boundingBoxCollisions(0))
[588]423                {
[847]424                        cr.locationSetBboxLow(cr.bboxLow.x, cr.bboxLow.y, -cr.getPart(cr.numparts - 1).sx); //place slightly under the bottom surface ("z" value depends on the size of the last=largest chamber)
[401]425                        return cr;
[588]426                }
[401]427        }
428
429        Populations[0].delete(cr);
430}
431
[567]432function reticulopodiaExists(cr)
[444]433{
434        var has_ret = 0;
435
[476]436        if (cr.data->reticulopodiacreature != null)
[444]437        {
[476]438                if (Populations[2].findUID(cr.data->reticulopodiacreature.uid) != null)
[444]439                {
440                        has_ret = 1;
441                }
442        }
443
444        return has_ret;
445}
446
[567]447function visualization(cr)
[380]448{
[567]449        return reticulopodiaExists(cr);
450}
451
452function foramGrow(cr, chamber_num, lastchambergrowth)
453{
[847]454        if ((chamber_num + 1) <= max_chamber_volume[cr.data->lifeparams->gen].size)
[474]455        {
[579]456                curColor = colors[cr.data->lifeparams->gen];
[567]457                var ploid = cr.data->lifeparams->gen;
[847]458                var geno = create_genotype(getPloidRadius(ploid), chamber_num + 1, colors[ploid], lastchambergrowth);
[595]459                var cr2 = createAndRotate(geno, 0, 0, 0);
[422]460
[595]461                cr2.orient.set(cr.orient);
[474]462                cr2.energy0 = cr.energy;
463                cr2.energy = cr2.energy0;
[422]464
[554]465                setGenotype({"cr" : cr2, "parent_genes" : cr.data->genes, "parent_lifeparams" : cr.data->lifeparams, "opt" : "growth", "energy0" : cr.energy0});
[847]466                cr2.locationSetBboxLow(cr.bboxLow.x, cr.bboxLow.y, cr.bboxLow.z);
[479]467                setForamMeta(cr2);
[422]468
[567]469                if (reticulopodiaExists(cr))
[474]470                {
[476]471                        Populations[2].delete(cr.data->reticulopodiacreature);
[474]472                }
473                Populations[0].delete(cr);
[567]474                return cr2;
[430]475        }
[567]476        return cr;
[380]477}
478
[583]479function visualizeChamberGrowth(cr, chamber_time)
480{
481        var total_time = secToSimSteps(ExpProperties.chamberGrowthSec);
[847]482        var ret_unit = total_time / chamber_vis_denominator;
483        var chamber_unit = total_time - ret_unit;
[583]484
485        if (chamber_time < ret_unit || chamber_time >= chamber_unit)
486        {
[847]487                var new_rad = Math.min(Math.max((chamber_time % ret_unit) / ret_unit * getZoneRange(cr, 1), 0.01), getZoneRange(cr, 1));
[583]488
489                if(chamber_time < ret_unit)
490                {
[847]491                        new_rad = getZoneRange(cr, 1) - new_rad;
[583]492                }
493
494                curColor = colors[cr.data->lifeparams->gen];
[847]495                addReticulopodia(cr, new_rad);
[583]496
497                if (chamber_time == 0)//checking for end of chamber growth process
498                {
[847]499                        cr.data->lifeparams->chamber_growth = -1;
[583]500                }
501        }
502        else
503        {
[847]504                var new_rad = 1 - Math.min(Math.max((chamber_time - ret_unit) / chamber_unit, 0.01), 1);
[583]505                curRadius = cr.data->reticulopodiacreature.getPart(1).sy;
[847]506
[583]507                if (chamber_time == ret_unit)
508                {
509                        new_rad = 1;
510                }
511
[847]512                var new_cr = foramGrow(cr, chamberNumFromEnergy(cr.data->lifeparams->max_energy_level, cr.data->lifeparams->gen) - 1, new_rad);
513                curRadius = getZoneRange(new_cr, 1);
[583]514        }
515}
516
[435]517function stepToNearest(cr)
[380]518{
[638]519        var p = XYZ.new(cr.bboxCenter.x, cr.bboxCenter.y, cr.bboxCenter.z);
[847]520        var n = cr.signals.receiveSet("nutrient", getZoneRange(cr, 2));
[380]521
[401]522        //if signals are received find the source of the nearest
[380]523        if (n.size > 0)
524        {
525                var i;
526                var mp;
527                var distvec = XYZ.new(0, 0, 0);
528                var dist;
[470]529                var mindist = 100000000000.0;
[380]530                var mindistvec = null;
[430]531                var eating = 0;
[380]532
[401]533                for (i = 0; i < n.size; i++)
[380]534                {
[638]535                        mp = XYZ.new(n[i].value.bboxCenter.x, n[i].value.bboxCenter.y, n[i].value.bboxCenter.z);
[595]536                        distvec.set(mp);
537                        distvec.sub(p);
[401]538                        dist = distvec.length;
[847]539                        if (dist < getZoneRange(cr, 1))
[380]540                        {
[430]541                                if (n[i].value != null)
542                                {
543                                        energyTransfer(cr, n[i].value);
544                                        eating = 1;
545                                }
546                        }
[476]547                        else if (eating == 0 && cr.data->lifeparams->hibernated == 0 && dist < mindist)
[430]548                        {
[401]549                                mindist = dist;
550                                mindistvec = distvec.clone();
[380]551                        }
552                }
553
[476]554                if (!eating && cr.data->lifeparams->hibernated == 0)
[430]555                {
[588]556                        mindistvec.z = 0;
[430]557                        mindistvec.normalize();
[595]558                        mindistvec.scale(movePerStep);
559                        cr.drive = mindistvec;
[430]560                        moveEnergyDec(cr);
561                }
562
[422]563                return 1;
[380]564        }
[847]565
[422]566        else
[474]567        {
[422]568                return 0;
[474]569        }
[422]570}
[401]571
[422]572function moveEnergyDec(cr)
573{
[476]574        if (cr.data->lifeparams->hibernated == 0)
[422]575        {
[479]576                //percent of maximal energy
[847]577                cr.energy -= (ExpProperties.energy_move * cr.data->lifeparams->max_energy_level) * ExpProperties.secPerStep;
[422]578        }
[421]579}
[380]580
[584]581function fence(center, zone)
[487]582{
[847]583        return Math.min(Math.max(0 + zone, center), World.wrldsiz - zone); //add and subtract zone from the world size to prevent reticulopodia from crossing the fence
[487]584}
585
[422]586function foramMove(cr)
[421]587{
[430]588        //are there any nutrients in zone 1 or 2?
[401]589        {
[435]590                var moved = stepToNearest(cr); //TODO weighted sum of distance and energy
[847]591                if (moved == 1)
592                {
[502]593                        moveReticulopodia(cr);
[422]594                        return;
595                }
[401]596        }
597
[847]598        //Prevents forams from crossing the world border. In the case of touching the border with the reticulopodia direction of the movement should be changed.
[584]599        var change_direction = 0;
[638]600        var new_x = fence(cr.bboxCenter.x, getZoneRange(cr, 1));
601        var new_y = fence(cr.bboxCenter.y, getZoneRange(cr, 1));
[584]602
[638]603        if ((new_x != cr.bboxCenter.x) || (new_y != cr.bboxCenter.y) || (cr.data->lifeparams->dir_counter >= int(secToSimSteps(ExpProperties.dir_change_sec))))
[584]604        {
605                change_direction = 1;
[847]606                cr.locationSetBboxLow(new_x - cr.bboxSize.x / 2, new_y - cr.bboxSize.y / 2, -cr.getPart(cr.numparts - 1).sx); //place slightly under the bottom surface ("z" value depends on the size of the last=largest chamber)
[584]607        }
608
[422]609        //no nutrients in zone 2
[847]610        if (getGene(cr, "hibernation", 0) == 1)
[401]611        {
[430]612                reverseHib(cr);
[847]613                cr.drive = XYZ.new(0, 0, 0);
[380]614        }
[422]615        //random move
[554]616        else if (change_direction == 1)
[380]617        {
[476]618                cr.data->lifeparams->dir = randomDir();
[552]619                cr.data->lifeparams->dir_counter = 0;
[595]620                cr.drive = cr.data->lifeparams->dir;
[422]621                moveEnergyDec(cr);
622        }
[474]623        else
624        {
[595]625                cr.drive = cr.data->lifeparams->dir;
[474]626        }
[502]627        moveReticulopodia(cr);
[422]628}
[401]629
[502]630function moveReticulopodia(cr)
631{
632        if (visualization(cr))
633        {
[847]634                cr.data->reticulopodiacreature.locationSetBboxLow(cr.bboxCenter.x - getZoneRange(cr, 1), cr.bboxCenter.y - getZoneRange(cr, 1), cr.bboxCenter.z - getZoneRange(cr, 1));
[595]635                cr.data->reticulopodiacreature.drive = cr.drive;
[502]636        }
637}
638
[474]639function randomDir()
640{
[847]641        var dir = (Math.rndUni(-ExpProperties.zone2_range, ExpProperties.zone2_range), Math.rndUni(-ExpProperties.zone2_range, ExpProperties.zone2_range), 0);
[474]642        dir.normalize();
[847]643        dir.scale(-1 * movePerStep);
[474]644        return dir;
645}
646
[430]647function energyTransfer(cr1, cr2)
[422]648{
[847]649        cr1.drive = XYZ.new(0, 0, 0);
650        var e =  ExpProperties.feedtrans * cr1.energy * ExpProperties.secPerStep; //TODO efficiency dependent on age
[502]651        //Simulator.print("transferring "+e +"("+e*ExpProperties.ingestion+")"+" to "+cr1.name +" ("+ cr1.energy+") " +" from "+cr2.uid+" ("+cr2.energy+") "+ e/ExpProperties.secPerStep+ " per sec");
[510]652        var transferred = cr2.transferEnergyTo(cr1, e);
[847]653        cr1.energy -= transferred * (1 - ExpProperties.ingestion);
[476]654        if (cr1.data->lifeparams->hibernated == 1)
[422]655        {
[430]656                reverseHib(cr1);
[422]657        }
658}
[401]659
[430]660function reverseHib(cr)
661{
[476]662        if (cr.data->lifeparams->hibernated == 1)
[430]663        {
[479]664                setForamMeta(cr); //unhibernate
[847]665        }
[430]666        else
667        {
[847]668                cr.idleen = (ExpProperties.energy_hib * cr.energy) * ExpProperties.secPerStep; //hibernate
[430]669        }
[476]670        cr.data->lifeparams->hibernated = 1 - cr.data->lifeparams->hibernated;
[430]671}
672
[422]673function onForamsStep(cr)
674{
[479]675        //checking for gametogenesis process
676        if (cr.data->lifeparams->division_time > 0)
[444]677        {
[847]678                cr.data->lifeparams->division_time = Math.max(cr.data->lifeparams->division_time - 1, 0);
[444]679        }
[479]680        //checking for end of gametogenesis
681        else if (cr.data->lifeparams->division_time == 0)
[422]682        {
[479]683                //waiting for gamets fusion
[422]684        }
[479]685        //checking for chamber growth process
686        else if (cr.data->lifeparams->chamber_growth > 0)
687        {
[847]688                var chamber_time = Math.max(cr.data->lifeparams->chamber_growth - 1, 0);
[567]689                cr.data->lifeparams->chamber_growth = chamber_time;
[504]690                cr.energy -= ExpProperties.chamberCostPerSec * cr.energy * ExpProperties.secPerStep;
[422]691
[567]692                if (visualization(cr))
693                {
[583]694                        visualizeChamberGrowth(cr, chamber_time);
[567]695                }
[479]696        }
[583]697        //checking for end of the chamber growth process
[847]698        else if (cr.data->lifeparams->chamber_growth == 0 && visualization(cr) == 0)
699        {
700                foramGrow(cr, lastChamberNum(cr), 1);
[479]701                cr.data->lifeparams->chamber_growth = -1;
702                //Simulator.print("chamber "+ (lastChamberNum(cr) + 1) +" complete");
[430]703        }
[479]704        else
[422]705        {
[479]706                //update of metabolism rate
707                if (cr.data->lifeparams->hibernated == 0)
[401]708                {
[479]709                        setForamMeta(cr);
710                }
711
712                if (deathConditions(cr) == 1)
713                {
[493]714                        if (ExpProperties.logging == 1)
715                        {
[847]716                                log(createLogVector(cr, cr.data->lifeparams->max_energy_level), ExpProperties.logPref + "fossil_log.txt");
717                                log(createLogVector(cr, cr.lifespan), ExpProperties.logPref + "lifespan_log.txt");
718                        }
[479]719                        Populations[0].kill(cr);
720                        return;
721                }
722
[552]723                //update direction change counter
724                cr.data->lifeparams->dir_counter += 1;
725
[479]726                foramMove(cr);
727
728                var repro = foramReproduce(cr);
729                if (repro == 1)
730                {
731                        return;
732                }
733
734                cr.data->lifeparams->max_energy_level = Math.max(cr.energy, cr.data->lifeparams->max_energy_level);
735
736                //cheking conditions of chamber growth process start
[567]737                if  (lastChamberNum(cr) < max_chamber_volume[cr.data->lifeparams->gen].size)
[479]738                {
[847]739                        if ((cr.data->lifeparams->max_energy_level >= energyFromVolume(max_chamber_volume[cr.data->lifeparams->gen][lastChamberNum(cr) - 1], 0)))
[479]740                        {
[554]741                                cr.data->lifeparams->chamber_growth = int(secToSimSteps(ExpProperties.chamberGrowthSec));
[847]742                        }
[479]743                }
[847]744        }
[380]745}
746
[422]747function deathConditions(cr)
748{
[847]749        if ((cr.energy <= getProperty(cr.data->lifeparams->gen, "e_death_level")*cr.data->lifeparams->max_energy_level) || (Math.rnd01 < ExpProperties.hunted_prob))
[479]750        {
[422]751                return 1;
[479]752        }
[422]753        else
754                return 0;
755}
756
[421]757function onForamsDied(cr)
[380]758{
[444]759        if (visualization(cr))
[430]760        {
[476]761                Populations[2].delete(cr.data->reticulopodiacreature);
[430]762        }
[380]763        //fossilization
764        var geno = GenePools[0].add(cr.genotype);
[476]765        geno.data->genes = cr.data->genes;
766        geno.data->lifeparams = cr.data->lifeparams;
[486]767        if (ExpProperties.logging == 1) Simulator.print("\"" + cr.name + "\" died...");
[380]768        ExpState.totaltestedcr++;
769}
770
[421]771// --------------------------------foram end -------------------------------------
[380]772
[421]773// -------------------------------- nutrient begin --------------------------------
[380]774
[479]775function createNutrientGenotype(nutrientradius)
[422]776{
[847]777        return "//0\nm:Vstyle=nutrient\np:sh=3,sx=" + nutrientradius + ",sy=" + nutrientradius + ",sz=" + nutrientradius + ",ry=1.57,vr=0.0,1.0,0.0";
[422]778}
779
[421]780function onNutrientsStep(cr)
[380]781{
[638]782        cr.locationSetBboxLow(cr.bboxLow.x % World.wrldsiz, cr.bboxLow.y % World.wrldsiz, 0.5);
[380]783}
784
[421]785function addNutrient()
[380]786{
[486]787        var cr = Populations[1].add(createNutrientGenotype(ExpProperties.nutrientradius));
[380]788
[421]789        cr.name = "Nutrients";
[380]790        cr.idleen = 0;
[486]791        cr.energy0 = ExpProperties.energy_nut;
[416]792        cr.energy = cr.energy0;
[421]793        cr.signals.add("nutrient");
[380]794
[430]795        cr.signals[0].value = cr;
[380]796
[422]797        placeCreatureRandomly(cr, 0, 0);
[486]798        if (ExpProperties.visualize == 1)
[444]799        {
[847]800                var nutsize = ExpProperties.nutrientradius * 10;
801                var nut = Populations[2].add("//0\nm:Vstyle=nutrient_visual\np:sh=2,sx=" + nutsize + ",sy=" + nutsize + ",sz=" + nutsize + ",ry=1.5,vr=0.0,1.0,0.0");
[476]802                cr.data->reticulopodiacreature = nut;
[847]803                nut.locationSetBboxLow( cr.bboxLow.x + cr.bboxSize.x / 2 - nut.bboxSize.x / 2,  cr.bboxLow.y + cr.bboxSize.y / 2 - nut.bboxSize.y / 2, -nutsize);
[444]804        }
[380]805}
806
[444]807function onNutrientsDied(cr)
808{
809        if (visualization(cr))
810        {
[476]811                Populations[2].delete(cr.data->reticulopodiacreature);
[444]812        }
813}
814
[592]815function getNumberCounter(counter, increase, unitsize) //increase counter and then deduct and return an integer (=discrete) number of "full" units
[590]816{
817        counter += increase;
[847]818        var unitcount = int(counter / unitsize);
819        counter -= unitcount * unitsize;
[592]820        return {"counter" : counter, "number" : unitcount};
[590]821}
822
[422]823function nutrientGrowth()
[380]824{
[590]825        if (ExpProperties.foodfluxChange > 0)
[481]826        {
[847]827                changePeriod += 1;
828                if (phase == "low" && changePeriod >= secToSimSteps(23328000)) //9 months
829                {
830                        ExpProperties.foodflux = ExpProperties.foodflux / ExpProperties.foodfluxChange;
831                        phase = "high";
832                        changePeriod = 0;
833                }
834
835                else if (phase == "high" && changePeriod >= secToSimSteps(7776000)) //3 months
836                {
837                        ExpProperties.foodflux = ExpProperties.foodflux * ExpProperties.foodfluxChange;
838                        phase = "low";
839                        changePeriod = 0;
840                }
[481]841        }
[422]842
[847]843        var nutrientNum = getNumberCounter(nutrient_num_counter, ExpProperties.foodflux * wrldsizSquareMeters * ExpProperties.secPerStep, ExpProperties.energy_nut * 0.000000000001);
[493]844
[590]845        nutrient_num_counter = nutrientNum["counter"];
846
847        for (var i = 0; i < nutrientNum["number"]; i++)
[847]848        {
[590]849                addNutrient();
[380]850        }
[493]851
[590]852        if (ExpProperties.logging == 1 && nutrientNum["number"] > 0)
853        {
[847]854                log([nutrientNum["number"]], ExpProperties.logPref + "nutrients_log.txt");
[590]855        }
856
[380]857}
858
[421]859// -------------------------------- nutrient end --------------------------------
[380]860
[422]861// -------------------------------- step begin --------------------------------
[380]862
[422]863function onStep()
864{
[432]865
866        nutrientGrowth();
[486]867        if (ExpProperties.logging == 1)
[422]868        {
869                createStatistics();
870        }
871
872        //reproduction --------------------------------------------
873        reprocounter += 1;
[554]874        if (reprocounter > secToSimSteps(ExpProperties.reproTimeSec))
[422]875        {
876                reprocounter = 0;
[567]877                for (var s = 0; s < species_genes.size; s++)
878                {
879                        reproduce_parents(s);
880                }
[847]881
[422]882        }
883
884        //check for extinction -----------------------------------------------
885        if (Populations[0].size == 0)
886        {
[486]887                if (ExpProperties.autorestart)
[422]888                {
889                        Simulator.print("no more creatures, restarting...");
890                        onExpInit();
891                }
892                else
893                {
894                        Simulator.print("no more creatures, stopped.");
895                        Simulator.stop();
896                }
897        }
[486]898        if (ExpProperties.maxSteps > 0)
[432]899        {
[486]900                if (Simulator.stepNumber >= ExpProperties.maxSteps)
[432]901                        Simulator.stop();
902        }
[422]903}
904
905function createStatistics()
[847]906{
[487]907        var number = [];
908        var e_inc = [];
[422]909        var e_nut = 0.0;
910
[487]911        for (var s = 0; s < species_genes.size; s++)
912        {
[847]913                number.add([0, 0]); // [haplo][diplo]
914                e_inc.add([0, 0]);
[487]915        }
916
[422]917        for (var i = 0; i < Populations[0].size; i++)
918        {
919                var cr = Populations[0].get(i);
[476]920                var gen = cr.data->lifeparams->gen;
921                var species = cr.data->lifeparams->species;
[430]922
[847]923                number[species][gen] = number[species][gen] + 1;
[430]924                e_inc[species][gen] = e_inc[species][gen] + cr.energy;
[422]925        }
926
927        for (var i = 0; i < Populations[1].size; i++)
928        {
929                var cr = Populations[1].get(i);
930                e_nut += cr.energy;
931        }
932
[487]933        var log_numbers = [];
934        var log_energies = [];
[422]935
[487]936        for (var s = 0; s < species_genes.size; s++)
937        {
938                for (var p = 0; p < 2; p++)
939                {
940                        log_numbers.add(number[s][p]);
941                        log_energies.add(e_inc[s][p]);
942                }
943        }
[847]944
[487]945        log_numbers.add(Populations[1].size);
946        log_energies.add(e_nut);
947
[847]948        log(log_numbers, ExpProperties.logPref + "forams_log.txt");
949        log(log_energies,  ExpProperties.logPref + "energies_log.txt");
[422]950}
951
[380]952function log(tolog, fname)
953{
[847]954        var f = File.appendDirect(fname, "forams data");
[380]955        f.writeString("" + Simulator.stepNumber);
[401]956        for (var  i = 0; i < tolog.size; i++)
[380]957        {
958                f.writeString(";" + tolog[i]);
959        }
960        f.writeString("\n");
961        f.close();
962}
963
[567]964function createLogVector(cr, value)
965{
966        var vec = Vector.new();
967        for (var i = 0; i < species_genes.size; i++)
968        {
969                for (var j = 0; j < 2; j++)
970                {
971                        vec.add(0);
972                }
973                if (cr.data->lifeparams->species == i)
974                {
[847]975                        vec[i * 2 + cr.data->lifeparams->gen] = value;
[567]976                }
977        }
978        return vec;
979}
980
981
[422]982// -------------------------------- step end --------------------------------
[479]983//TODO default params values in frams instead of microns/seconds
[380]984
[401]985@include "standard_events.inc"
[380]986
[401]987~
[380]988
[486]989property:
[444]990id:visualize
991name:Show reticulopodia and nutrients
[430]992type:d 0 1 0
[554]993group:
[430]994
[486]995property:
[434]996id:maxSteps
[554]997name:Maximum number of steps
[552]998type:d 0 10000000 0
[554]999group:
[432]1000
[486]1001property:
[567]1002id:scalingFactor
1003name:Scaling factor for micrometers
1004type:f 0 -1 0.01
1005group:
1006
1007property:
[554]1008id:logging
1009name:Log statistics to file
1010type:d 0 1 0
1011group:
1012
1013property:
[493]1014id:logPref
1015name:Log prefix
1016type:s
1017
1018property:
[554]1019id:secPerStep
1020name:Seconds per simulation step
[568]1021help:~
[570]1022Number of seconds of foraminifera time per simulation step.
1023Lower values mean smoother animation.~
[554]1024type:f 1 480 300
1025flags: 16
1026group:
1027
1028property:
[474]1029id:foramSpeedMmPerMin
1030name:Speed of foraminfera in mm/min
[554]1031type:f 0.01 0.1 0.05
[474]1032flags: 16
1033group:Foraminifera
1034
[486]1035property:
[554]1036id:dir_change_sec
1037name:Number of seconds before direction change
1038type:d 300 300000 6000
[479]1039group:Foraminifera
1040
[486]1041property:
[554]1042id:foramPop
1043name:Initial forams population size
1044type:d 1 1000 20
[552]1045group:Foraminifera
1046
1047property:
[554]1048id:gametoPeriodSec
[479]1049name:Time of gametogenesis
[554]1050type:f 300 300000 21600
1051group:Reproduction
[479]1052
[486]1053property:
[554]1054id:gametSuccessRate
1055name:Ratio of successful gamets
1056type:f 0.0001 0.01 0.001
1057group:Reproduction
[479]1058
[486]1059property:
[554]1060id:divisionCost
1061name:Cost of division in pG
1062type:f 15 25 20
1063group:Reproduction
[474]1064
[486]1065property:
[554]1066id:min_repro_energ_haplo
[567]1067name:Min reproduction energy of haploid in pg
1068type:f 0 -1 350000
[554]1069group:Energy
[380]1070
[486]1071property:
[554]1072id:min_repro_energ_diplo
[567]1073name:Min reproduction energy of diploid in pg
1074type:f 0 -1 600000
[554]1075group:Energy
[479]1076
[486]1077property:
[554]1078id:repro_prob
1079name:Probability of reproduction
1080type:f 0 1 0.8
1081group:Reproduction
[380]1082
[486]1083property:
[554]1084id:energies0_haplo
1085name:Energy of offspring from diploid forams
1086type:f 0 -1 20
1087group:Energy
[479]1088
[486]1089property:
[554]1090id:energies0_diplo
1091name:Energy of offspring from diploid forams
1092type:f 0 -1 1.25
1093group:Energy
1094
1095property:
[567]1096id:max_chamber_num_haplo
1097name:Maximum number of haploid chambers
1098type:f 1 50 35
1099group:Energy
1100
1101property:
1102id:max_chamber_num_diplo
1103name:Maximum number of diploid chambers
1104type:f 1 50 35
1105group:Energy
1106
1107property:
[554]1108id:crossprob
1109name:Crossover probability
1110type:f 0 1 0
1111group:Reproduction
1112
1113property:
1114id:mutationprob
1115name:Mutation probability
1116type:f 0 1 0
1117group:Reproduction
1118
1119property:
1120id:reproTimeSec
1121name:Time before reproduction
1122type:d 0 10000 720
1123group:Reproduction
1124
1125property:
1126id:chamberGrowthSec
1127name:Time of the chamber growth in seconds
[567]1128type:f 720 43200 43200
[479]1129group:Foraminifera
1130
[486]1131property:
[422]1132id:chamber_proculus_haplo
[423]1133name:Size of proculus
[422]1134type:f
[421]1135group:Foraminifera
[380]1136
[486]1137property:
[422]1138id:chamber_proculus_diplo
[423]1139name:Size of proculus
[422]1140type:f
[380]1141group:Foraminifera
1142
[486]1143property:
[422]1144id:hunted_prob
1145name:Probability of being hunted
1146type:f 0 1 0
[554]1147group:Foraminifera
[380]1148
[486]1149property:
[557]1150id:zone1_range
[556]1151name:Zone 1 range in frams units
[554]1152type:f 0 200 10
[422]1153group:Foraminifera
[380]1154
[486]1155property:
[422]1156id:zone2_range
[556]1157name:Zone 2 range in frams units
[554]1158type:f 0 3000 30
[421]1159group:Foraminifera
[380]1160
[486]1161property:
[554]1162id:chamberCostPerSec
1163name:Cost of growning chamber per second
1164type:f 0 1 0.000001
1165group:Energy
[380]1166
[486]1167property:
[475]1168id:e_death_level_haplo
1169name:Minimal level of energy to sustain life of haploid
[554]1170type:f 0 1 0.5
1171group:Energy
[475]1172
[486]1173property:
[475]1174id:e_death_level_diplo
1175name:Minimal level of energy to sustain life of diploid
[554]1176type:f 0 1 0.5
1177group:Energy
[475]1178
[486]1179property:
[422]1180id:energy_hib
1181name:Energy used for hibernation during one step
[554]1182type:f 0 1 0.0000001
1183group:Energy
[422]1184
[486]1185property:
[422]1186id:energy_move
1187name:Energy used for movement during one step
[554]1188type:f 0 1 0.0000005
1189group:Energy
[422]1190
[486]1191property:
[380]1192id:e_meta
1193name:Idle metabolism
[554]1194type:f 0 1 0.0000005
[380]1195group:Energy
[556]1196help:Foraminifera consumes this proportion of its energy in one time step
[380]1197
[486]1198property:
[554]1199id:ingestion
1200name:Ingestion rate
1201type:f 0 -1 0.25
1202group:Energy
1203
1204property:
[493]1205id:nutrient_pop
[554]1206name:Nutrient population
[474]1207type:f 0 1000000
[380]1208group:Energy
1209help:How fast energy is created in the world
1210
[486]1211property:
[554]1212id:energy_nut
1213name:Nutrient energy
1214type:f 0 10000000
[481]1215group:Energy
1216
[486]1217property:
[554]1218id:nutrientradius
1219name:Nutrient size
1220type:f 0.001 0.9 0.1
[479]1221group:Energy
1222
[486]1223property:
[554]1224id:picoCarbonPerMikro
[556]1225name:Picograms of carbon in cubic micrometer
[554]1226type:f 0 -1 0.13
[380]1227group:Energy
1228
[486]1229property:
[380]1230id:feedtrans
[479]1231name:Energy transfer per second
[554]1232type:f 0 1 0.001
[380]1233group:Energy
[422]1234
[486]1235property:
[590]1236id:foodflux
1237name:POM flux in grams per second per square meter
1238type:f 0 1 0.0000000075631
[422]1239group:Energy
1240
[486]1241property:
[590]1242id:foodfluxChange
[554]1243name:Set variable feed rate
1244type:f 0 -1 0
[421]1245group:Energy
1246
[486]1247property:
[422]1248id:stress
1249name:Environmental stress
1250type:d 0 1 1
[554]1251group:
[422]1252
[486]1253property:
[422]1254id:repro_trigger
1255name:Reproduction trigger
1256type:d 0 1 1
[554]1257group:Reproduction
[422]1258
[486]1259property:
[422]1260id:creath
1261name:Creation height
[554]1262type:f -1 50 -0.99
[422]1263help:~
1264Vertical position (above the surface) where new Forams are revived.
1265Negative values are only used in the water area:
1266  0   = at the surface
1267-0.5 = half depth
1268-1   = just above the bottom~
1269
[554]1270property:
1271id:autorestart
1272name:Restart after extinction
1273help:Restart automatically this experiment after the last creature has died?
1274type:d 0 1 0
1275
[421]1276state:
1277id:nutrient
[428]1278name:Nutrient locations
[421]1279help:vector of vectors [x,y,energy]
1280type:x
1281flags:32
1282
[380]1283state:
1284id:notes
1285name:Notes
1286type:s 1
1287help:~
1288You can write anything here
1289(it will be saved to the experiment file)~
1290
1291state:
1292id:totaltestedcr
[421]1293name:Evaluated Forams
1294help:Total number of the Forams evaluated in the experiment
[380]1295type:d
1296flags:16
Note: See TracBrowser for help on using the repository browser.