The AHA Model  Revision: 12809
Reference implementation 04 (HEDG02_04)
m_popul.f90
Go to the documentation of this file.
1 !> @file m_popul.f90
2 !! The Population objects for the AHA Model.
3 !! @author Sergey Budaev <sergey.budaev@uib.no>
4 !! @author Jarl Giske <jarl.giske@uib.no>
5 !! @date 2016-2017
6 
7 !-------------------------------------------------------------------------------
8 ! $Id: m_popul.f90 11210 2021-06-03 09:47:06Z sbu062 $
9 !-------------------------------------------------------------------------------
10 
11 !-------------------------------------------------------------------------------
12 !> @brief Define the population of agents object, its properties and
13 !! functions.
14 !> @section the_population_module THE_POPULATION module
15 !! @details The population is in the simplest case an array of individual
16 !! agent objects. Individual properties of the population members can
17 !! be referred as, e.g. `proto_parents%individual(i)%fitness` .
19 
20  use commondata ! Global definitions of the model objects
21  use the_individual ! The individual - properties of the individuals
22 
23  implicit none
24 
25  character (len=*), parameter, private :: modname = "(THE_POPULATION)"
26 
27  !> @brief Definition of individual member of a population.
28  !! @details Add an additional object, a member of the population.
29  !! It is the`INDIVIDUAL_AGENT` adding an (unique) integer
30  !! ID: `person_number`. Here we also have two functions for
31  !! setting and retrieving the IDs. We do not init members
32  !! of the population using a single init function, Instead,
33  !! init normally the INDIVIDUAL_AGENT object and for the
34  !! MEMBER_POPULATION just set_id. This is because we cannot
35  !! set id for isolated subject, only as a member of population.
36  type, public, extends(individual_agent) :: member_population
37  integer :: person_number
38  contains
39  private
40  !> Set integer ID number to individual member of the population object.
41  !! See `the_population::set_individual_id()`.
42  procedure, private :: set_id => set_individual_id
43  !> Get integer ID number to individual member of the population object.
44  !! See `the_population::get_individual_id()`.
45  procedure, public :: get_id => get_individual_id
46  !> Places the individual agent, a member of the population, within a specific
47  !! environment at random with a **uniform** distribution.
48  !! See `the_population::individ_posit_in_environ_uniform()`.
49  procedure, public :: place_uniform => individ_posit_in_environ_uniform
50  !> Set the individual to be **dead**.
51  !! See `the_population::genome_individual_set_dead_non_pure()`.
52  procedure, public :: dies_debug => genome_individual_set_dead_non_pure
53  end type member_population
54 
55  !> @brief Definition of the population object.
56  !! @details This is basically an array of individuals (of the type
57  !! `MEMBER_POPULATION`) plus population or generation descriptors.
58  type, public :: population
59  !> The size of the population.
60  integer :: population_size
61  !> @brief `POPULATION` is represented by an array of objects
62  !! of the type `MEMBER_POPULATION`
63  !! @details `POPULATION` is an array of objects of the type
64  !! `MEMBER_POPULATION`. It is represented as the \%individual
65  !! component of the type. The `i`-th individual of the population
66  !! `this` is accessed as `this%individual(i)`. the population also
67  !! has two descriptors: integer `pop_number` and string `pop_name`.
68  type(member_population), allocatable, dimension(:) :: individual
69  !> The numeric ID of the population (if we have several populations))
70  integer :: pop_number
71  !> The descriptive name of the population
72  character (len=LABEL_LENGTH) :: pop_name
73  contains
74  private
75  !> Initialise the population object.
76  !! See `the_population::init_population_random()`.
77  procedure, public :: init => init_population_random
78  !> Impose selective mortality at birth of the agents.
79  !! See `the_population::population_birth_mortality_init()`.
80  procedure, public :: mortality_birth => population_birth_mortality_init
81  !> Destroys this population and deallocates the array of individual
82  !! member objects.
83  !! See `the_population::population_destroy_deallocate_objects()`.
84  procedure, public :: destroy => population_destroy_deallocate_objects
85  !> Get the size of this population.
86  !! See `the_population::population_get_popsize()`.
87  procedure, public :: get_size => population_get_popsize
88  !> Get the population number ID.
89  !! See `the_population::population_get_pop_number()`.
90  procedure, public :: get_num_id => population_get_pop_number
91  !> Get the population character label ID.
92  !! See `the_population::population_get_pop_name()`.
93  procedure, public :: get_name => population_get_pop_name
94  !> Reset individual IDs of the population members.
95  !! See `the_population::reset_population_id_random()`.
96  procedure, public :: reset_id => reset_population_id_random
97  !> Determine the sex for each member of the population.
98  !! See `the_population::sex_initialise_from_genome()`.
99  procedure, public :: sex => sex_initialise_from_genome
100  !> Position each member of the population randomly within a
101  !! bounding environment with the **uniform** distribution.
102  !! See `the_population::position_individuals_uniform()`.
103  procedure, public :: scatter_uniform => position_individuals_uniform
104  !> Perform a single step of random walk by all agents, in 3D.
105  !! See `the_population::population_rwalk3d_all_agents_step()`.
106  procedure, public :: rwalk3d => population_rwalk3d_all_agents_step
107  !> Perform a single step of random walk by all agents, in 2.5D.
108  !! See `the_population::population_rwalk25d_all_agents_step()`.
109  procedure, public :: rwalk25d => population_rwalk25d_all_agents_step
110  !> This subroutine sorts the population `individual` object by
111  !! their \%fitness components.
112  !! See `the_population::sort_population_by_fitness()`.
113  procedure, public :: sort_by_fitness => sort_population_by_fitness
114  !> Save data for all agents within the population into a csv file.
115  !! See `the_population::population_save_data_all_agents_csv()`.
116  procedure, public :: save_csv => population_save_data_all_agents_csv
117  !> Save the genome data of all agents in this population to a CSV file.
118  !! See `the_population::population_save_data_all_genomes()`.
119  procedure, public :: save_genomes_csv => population_save_data_all_genomes
120  !> Load agent genome data in this population from a CSV file.
121  !! See `the_population::population_load_data_all_genomes()`.
122  procedure, public :: load_genomes_csv => population_load_data_all_genomes
123  !> Save the perceptual and emotional memory stack data of all agents
124  !! in this population to a CSV file.
125  !! See `the_population::population_save_data_memory()`.
126  procedure, public :: save_memory_csv => population_save_data_memory
127  !> Save the latest movement history of all agents.
128  !! See `the_population::population_save_data_movements()`.
129  procedure, public :: save_movements_csv => population_save_data_movements
130  !> Save the behaviours history the_neurobio::behaviour::history_behave
131  !! for all agents.
132  !! See `the_population::population_save_data_behaviours()`.
133  procedure, public :: save_behaviour_csv => population_save_data_behaviours
134  !> Subject the population to an attack by a specific predator.
135  !! See `the_population::population_subject_predator_attack()`.
136  procedure, public :: attacked => population_subject_predator_attack
137  !> Subject the population to mortality caused by habitat-specific
138  !! mortality risk. Each agent is affected by the risk associated with
139  !! the habitat it is currently in.
140  !! See `the_population::population_subject_other_risks()`.
141  procedure, public :: mortality_habitat => population_subject_other_risks
142  !> Subject all members of this population to their individual mortality
143  !! risks.
144  !! See `the_population::population_subject_individual_risk_mortality()`.
145  procedure, public :: mortality_individ => &
147  !> Calculate fitness for the pre-evolution phase of the genetic algorithm.
148  !! **Pre-evolution** is based on selection for a simple criterion without
149  !! explicit reproduction etc. The criterion for selection at this phase
150  !! is set by the integer the_individual::individual_agent::fitness
151  !! component. This procedure provides a whole-population wrapper for the
152  !! the_individual::fitness_calc() function.
153  !! See `the_population::population_preevol_fitness_calc()`.
154  procedure, public :: fitness_calc => population_preevol_fitness_calc
155  !> Determine the number of parents that have fitness higher than the
156  !! minimum acceptable value.
157  !! See `the_population::population_ga_reproduce_max()`.
158  procedure, public :: ga_reproduce_max => population_ga_reproduce_max
159  !> This function implements adaptive mutation rate that increases
160  !! as the population size reduces.
161  !! See `the_population::population_ga_mutation_rate_adaptive()`.
162  procedure, public :: ga_mutat_adaptive => &
164  !> Perform a single step of the life cycle of the population.
165  !! See `the_population::population_lifecycle_step_preevol()`.
166  procedure, public :: lifecycle_step => population_lifecycle_step_preevol
167  !> Perform a single step of the life cycle of the population. This
168  !! version includes only optimal food selection and eating without
169  !! the full fledged behaviour selection cascade of procedures
170  !! ::do_behave().
171  !! See `the_population::population_lifecycle_step_eatonly_preevol()`.
172  procedure, public :: lifecycle_eatonly => &
174 
175  end type population
176 
177  !> Global indicator variable that keeps the number of agents that have died
178  !! as a consequence of predatory attacks. All other dies are therefore caused
179  !! by starvation.
180  !! @note Note that this variable is initialised to zero at the start of each
181  !! generation in `GENERATIONS_PREEVOL` named loop in
182  !! the_evolution::generations_loop_ga().
184 
185 contains ! ........ implementation of procedures for this level ................
186 
187  !-----------------------------------------------------------------------------
188  !> Set integer ID number to individual member of the population object.
189  !! @note Note that this subroutine is private as setting individual IDs
190  !! makes sense only during the initialisation phase of the population.
191  subroutine set_individual_id (this, idnumber)
192  !> @param class, member of population.
193  class(member_population), intent(inout) :: this
194  !> @param id, integer id number assigned.
195  integer, intent(in) :: idnumber
196 
197  this%person_number = idnumber !> `person_number` is assigned the idnumber
198 
199  end subroutine set_individual_id
200 
201  !-----------------------------------------------------------------------------
202  !> Get integer ID number to individual member of the population object.
203  !! @note Note that this is public, so we can retrieve individual IDs but not
204  !! set them (id's are always set at initialisation)
205  function get_individual_id (this) result (idnumber)
206  !> @param class, member of population.
207  class(member_population), intent(in) :: this
208  !> @param id, integer id number retreived.
209  integer :: idnumber
210 
211  idnumber = this%person_number !> `person_number` is assigned the idnumber
212 
213  end function get_individual_id
214 
215  !-----------------------------------------------------------------------------
216  !> Places the individual agent, a member of the population, within a specific
217  !! environment at random with a **uniform** distribution.
218  !! The agents can be positioned with respect to their initial depth
219  !! - fixed depth, see commondata::init_agents_depth_is_fixed
220  !! - Gaussian depth, see commondata::init_agents_depth_is_gauss
221  !! - fully uniform distribution.
222  !! .
223  subroutine individ_posit_in_environ_uniform(this, environ)
224  class(member_population), intent(inout) :: this
225  !> @param environ sets the environment object where the agent is
226  !! placed randomly
227  class(environment), intent(in) :: environ
228 
230  call this%position( environ%uniform( within( init_agents_depth, &
231  environ%depth_min(), &
232  environ%depth_max() ) ) )
233  else if (init_agents_depth_is_gauss) then
234  call this%position( &
235  environ%uniform( within( rnorm( init_agents_depth, &
237  init_agents_depth)), &
238  environ%depth_min(), &
239  environ%depth_max() ) ) )
240  else
241  call this%position( environ%uniform() )
242  end if
243 
244  end subroutine individ_posit_in_environ_uniform
245 
246  !-----------------------------------------------------------------------------
247  !> Set the individual to be **dead**. Note that this function does not
248  !! deallocate the individual agent object, this may be a separate destructor
249  !! function.
250  !! @note This is a non-pure function logging some extended diagnostics
251  !! about the dying agent. See `dies` procedure from @ref the_genome
252  !! and all its overrides:
253  !! - the_genome::individual_genome::dies();
254  !! - the_neurobio::appraisal::dies();
255  !! - the_neurobio::gos_global::dies();
256  !! - the_individual::individual_agent::dies().
257  !! .
258  !! @warning This function should be normally used only while **debugging**.
259  !! In normal runs use the pure subroutine `dies` from
260  !! `ÌNDIVIDUAL_GENOME`.
261  subroutine genome_individual_set_dead_non_pure(this, non_debug_log)
262  class(member_population), intent(inout) :: this
263  logical, optional, intent(in) :: non_debug_log
264 
265  !> Local flag to do show log.
266  logical :: do_show_log
267 
268  !> Local parameter showing how many latest memory values to log out.
269  integer, parameter :: HIST_N=10
270 
271  do_show_log = .false. !> Don't show log by default.
272 
273  call this%dies() !> This is a pure function from `THE_GENOME` level.
274 
275  !> Turn on output logging only if in the DEBUG mode or if `non_debug_log`
276  !! is explicitly set to TRUE.
277  if (is_debug) then
278  do_show_log = .true.
279  else
280  if (present(non_debug_log)) then
281  if (non_debug_log) do_show_log = .true.
282  end if
283  end if
284 
285  if (.not. do_show_log) return !> Just exit back if no logging.
286 
287  call log_msg( "Agent # " // tostr(this%get_id()) // " with name " // &
288  trim(this%individ_label()) // " dies." )
289  call log_msg( "Agent properties: " )
290  call log_msg( " body mass: " // tostr(this%get_mass()) // &
291  ", body mass at birth: " // tostr(this%body_mass_birth) // &
292  ", max body mass: " // tostr(this%body_mass_maximum) // &
293  ", body length: " // tostr(this%get_length()) // &
294  ", energy reserves: " // tostr(this%get_energy()) // &
295  ", SMR: " // tostr(this%get_smr()) // &
296  ", stomach content: " // tostr(this%get_stom_content()) )
297  call log_msg( " Latest body mass (" // tostr(hist_n) // ") history: " // &
298  tostr(this%body_mass_history( &
300  call log_msg( " Latest body length (" // tostr(hist_n) // ") history: " &
301  // tostr(this%body_length_history( &
303  call log_msg( "Agent's GOS is " // this%gos_label() // &
304  ", arousal: " // tostr(this%arousal()) // "." )
305  !> @note Note that `TOSTR` accepts arrays including (concatenated)
306  !! character arrays.
307  call log_msg( " Latest GOS states: " // &
308  tostr(this%memory_motivations%gos_main( &
310 
312 
313  !-----------------------------------------------------------------------------
314  !> @brief Initialise the population object.
315  !! @details Initialise the population object, init it with random individuals
316  !! (function init on individuals), and assign sequential
317  !! person_number`s.
318  subroutine init_population_random(this, pop_size, pop_number_here, &
319  pop_name_here)
320  ! Parameters for this subroutine:
321  !> @param class, This -- member of population class (this)).
322  class(population), intent(inout) :: this
323  !> @param pop_size, The size of the population.
324  integer, intent(in) :: pop_size
325  !> @param id, Optional numeric population ID.
326  integer, optional, intent(in) :: pop_number_here
327  !> @param descriptor, Optional population string descriptor.
328  character (len=*), optional, intent(in) :: pop_name_here
329 
330  ! Local variables:
331  integer :: i !> local variable `i` is counter
332 
333  ! PROCNAME is the procedure name for logging and debugging
334  character(len=*), parameter :: PROCNAME = "(init_population_random)"
335 
336  !> Set population size from input parameter.
337  this%population_size = pop_size
338 
339  !> Allocate the population
340  if (.not. allocated(this%individual)) &
341  allocate(this%individual(this%population_size))
342 
343  !> Initialise all individuals of the population
344  do i=1, this%population_size
345  !> first, call `init to object individual(i)
346  call this%individual(i)%init()
347  !! second, ! call `set_id(i)` to identify agents within the population.
348  call this%individual(i)%set_id(i)
349  end do
350 
351  !> Set optional descriptors for the whole population. Numeric ID and
352  !! a short text description.
353  if (present(pop_number_here)) then
354  this%pop_number = pop_number_here
355  else
356  !> If optional ID is absent, ID is set to a random integer value from 1
357  !! to the maximum integer allowed for the pop_number type (minus 1).
358  this%pop_number = rand_i(1,huge(this%pop_number-1))
359  end if
360  if (present(pop_name_here)) then
361  this%pop_name = pop_name_here
362  else
363  !> If optional text description string of the population is absent,
364  !! it is set to the string representation of its numeric ID.
365  this%pop_name = tostr(this%pop_number)
366  end if
367 
368  call log_msg( ltag_info // "Initialised population " // this%pop_name // &
369  " # " // tostr(this%pop_number) // " with " // &
370  tostr(this%population_size) // " agents." )
371 
372  !> Log the initial location of the agents.
373  !! @warning The logic of the logger constructs here must coincide with
374  !! that in the population::individ_posit_in_environ_uniform().
376  call log_msg( ltag_info // "Initial location is FIXED at " // &
377  tostr(init_agents_depth) )
378  else if (init_agents_depth_is_gauss) then
379  call log_msg( ltag_info // "Initial location is GAUSSIAN at " // &
380  tostr(init_agents_depth) // ", with CV " // &
381  tostr(init_agents_depth_cv) )
382  else
383  call log_msg( ltag_info // "Initial location is fully uniform." )
384  end if
385 
386  end subroutine init_population_random
387 
388  !-----------------------------------------------------------------------------
389  !> Destroys this population and deallocates the array of individual member
390  !! objects.
392  class(population), intent(inout) :: this
393 
394  this%population_size = 0
395  this%pop_number = unknown
396  this%pop_name = ""
397  if (allocated(this%individual)) deallocate(this%individual)
398 
400 
401  !-----------------------------------------------------------------------------
402  !> Impose selective mortality at birth on the agents. Selective mortality
403  !! sets a fixed limit on uncontrolled evolution of the *energy reserves*
404  !! in newborn agents. If some newborn has too high energy at birth
405  !! (genetically fixed), such a deviating agent is killed at once.
406  !!
407  !! The values of the risk are chosen such that it is zero at the
408  !! fixed population mean (agent does not deviate) and reaches 1.0 if
409  !! the agent's energy reserves are 3.0 standard deviations higher than
410  !! the fixed mean value (agent strongly deviates).
411  subroutine population_birth_mortality_init(this, energy_mean, energy_sd)
412  class(population), intent(inout) :: this
413  !> @param[in] energy_mean optional mean energy at birth, if absent, is
414  !! calculated from the population data.
415  real(SRP), optional, intent(in) :: energy_mean
416  !> @param[in] energy_sd optional mean energy at birth, if absent, is
417  !! calculated from the population data.
418  real(SRP), optional, intent(in) :: energy_sd
419 
420  ! Local copies of optionals
421  real(SRP) :: energy_mean_loc, energy_sd_loc
422 
423  real(SRP) :: mortality
424  integer :: ind
425 
426  !> - `MORTALITY_BIRTH_INIT_ENERG_ABSCISSA` is the baseline abscissa for the
427  !! nonparametric interpolation function grid, in units of the standard
428  !! deviation (sigma) of the energy reserves in the newborn population,
429  !! i.e. the_body::condition::energy_birth.
430  ! htintrpl.exe [1.4 4 5] [0 0.5 1]
431  ! htintrpl.exe [0.7 1 2 3] [0 0.005 0.1 1]
432  real(SRP), parameter, dimension(*) :: MORTALITY_BIRTH_INIT_ENERG_ABSCISSA &
433  = [ 0.7_srp, 1.0_srp, 1.5_srp, 2.0_srp, 3.0_srp ]
434 
435  !> - `MORTALITY_BIRTH_INIT_ENERG_ORDINATE` is the ordinate for the
436  !! nonparametric interpolation function grid: sets the probability of
437  !! the agent's death given its energy reserves deviate from the fixed
438  !! mean by the number of standard deviations set by
439  !! `MORTALITY_BIRTH_INIT_ENERG_ABSCISSA`.
440  !! .
441  real(SRP), parameter, dimension(*) :: MORTALITY_BIRTH_INIT_ENERG_ORDINATE &
442  = [ 0.0_srp, 0.002_srp, 0.01_srp, 0.1_srp, 1.0_srp ]
443 
444  if (present(energy_mean)) then
445  energy_mean_loc = energy_mean
446  else
447  energy_mean_loc = average( this%individual%get_energ_birth() )
448  end if
449 
450  if (present(energy_sd)) then
451  energy_sd_loc = energy_sd
452  else
453  energy_sd_loc = std_dev( this%individual%get_energ_birth() )
454  end if
455 
456  inds: do ind=1, this%population_size
457 
458  !> Selective mortality sets a fixed limit on uncontrolled evolution of
459  !! the energy reserves in newborn agents. All newborn agents that have
460  !! the energy reserves exceeding some point may die with the probability
461  !! determined by the grid arrays:
462  !! - `MORTALITY_BIRTH_INIT_ENERG_ABSCISSA`;
463  !! - `MORTALITY_BIRTH_INIT_ENERG_ORDINATE`.
464  !! .
465  !!
466  !! The probability of death (mortality risk) is determined by the
467  !! nonparametric nonlinear function defined by `DDPINTERPOL`, where
468  !! the actual grid abscissa is the energy reserve of the agent in
469  !! and grid ordinate is the mortality risk.
470  !!
471  !! The values of the risk are chosen such that it is zero at the
472  !! fixed population mean (agent does not deviate) and reaches 1.0 if
473  !! the agent's energy reserves are 3.0 standard deviations higher than
474  !! the mean value.
475  mortality = within(ddpinterpol(energy_mean_loc + &
476  mortality_birth_init_energ_abscissa * &
477  energy_sd_loc, &
478  mortality_birth_init_energ_ordinate, &
479  this%individual(ind)%get_energ_birth()), &
480  0.0_srp, 1.0_srp )
481 
482  !> Interpolation plots can be saved in the @ref intro_debug_mode
483  !! "debug mode" using this plotting command:
484  !! `commondata::debug_interpolate_plot_save()`.
485  !! @warning Involves **huge** number of plots, should normally be
486  !! disabled.
488  grid_xx=energy_mean_loc + mortality_birth_init_energ_abscissa * &
489  energy_sd_loc, &
490  grid_yy=mortality_birth_init_energ_ordinate, &
491  ipol_value=this%individual(ind)%get_energ_birth(), &
492  algstr="DDPINTERPOL", &
493  output_file="plot_debug_mortality_birth_" // &
494  "gen_" // tostr(global_generation_number_current) // &
495  "_step_"// tostr(global_time_step_model_current) // &
496  mmdd // "_a_"// &
497  trim(this%individual(ind)%individ_label()) // &
498  "_" // rand_string(label_length, label_cst,label_cen) &
499  // ps )
500 
501  if ( rand_r4() < mortality ) then
502  call this%individual(ind)%dies()
503  end if
504  end do inds
505 
506  end subroutine population_birth_mortality_init
507 
508  !-----------------------------------------------------------------------------
509  !> Accessor get-function for the size of this population.
510  function population_get_popsize(this) result (pop_size_output)
511  class(population), intent(in) :: this
512  integer :: pop_size_output
513 
514  pop_size_output = this%population_size
515 
516  end function population_get_popsize
517 
518  !-----------------------------------------------------------------------------
519  !> Accessor get-function for the population number ID.
520  function population_get_pop_number(this) result(pop_number_output)
521  class(population), intent(in) :: this
522  integer :: pop_number_output
523 
524  pop_number_output = this%pop_number
525 
526  end function population_get_pop_number
527 
528  !-----------------------------------------------------------------------------
529  !> Accessor get-function for the population character label ID.
530  function population_get_pop_name(this) result (pop_name_string_out)
531  class(population), intent(in) :: this
532  character(len=LABEL_LENGTH) :: pop_name_string_out
533 
534  pop_name_string_out = this%pop_name
535 
536  end function population_get_pop_name
537 
538  !-----------------------------------------------------------------------------
539  !> @brief Reset individual IDs of the population members.
540  !! @details Makes new random individual IDs for the population members.
541  subroutine reset_population_id_random(this, pop_number_here, pop_name_here)
542  ! Parameters for this subroutine:
543  !> @param class, This -- member of population class (this)).
544  class(population), intent(inout) :: this
545  !> @param id, Optional numeric population ID.
546  integer, optional, intent(in) :: pop_number_here
547  !> @param descriptor, Optional population string descriptor.
548  character (len=*), optional, intent(in) :: pop_name_here
549 
550  ! Local variables:
551  integer :: i !> local variable `i` is counter
552 
553  !> Reset all individual IDs of the population members
554  do i=1, this%population_size
555  call this%individual(i)%set_id(i) ! call set_id(i) to the same object
556  end do
557 
558  !> Set optional descriptors for the whole population. Numeric ID and
559  !! a short text description.
560  if (present(pop_number_here)) then
561  this%pop_number = pop_number_here
562  else
563  !> If optional ID is absent, ID is set to a random integer value from 1
564  !! to the maximum integer allowed for the pop_number type (minus 1).
565  this%pop_number = rand_i(1,huge(this%pop_number-1))
566  end if
567  if (present(pop_name_here)) then
568  this%pop_name = pop_name_here
569  else
570  !> If optional text description string of the population is absent,
571  !! it is set to the string representation of its numeric ID.
572  this%pop_name = tostr(this%pop_number)
573  end if
574 
575  end subroutine reset_population_id_random
576 
577  !-----------------------------------------------------------------------------
578  !> @brief Determine the sex for each member of the population.
579  subroutine sex_initialise_from_genome(this)
580  class(population), intent(inout) :: this
581 
582  integer :: i
583 
584  do i=1, this%population_size
585  call this%individual(i)%sex_init()
586  end do
587 
588  end subroutine sex_initialise_from_genome
589 
590  !-----------------------------------------------------------------------------
591  !> @brief Position each member of the population randomly within a
592  !! bounding environment.
593  !! @note Moved the positioning procedure into a separate procedure as
594  !! initialising population may involve different spatial positioning,
595  !! e.g. uniform within the bounding environment or gaussian localised.
596  subroutine position_individuals_uniform(this, environ)
597  class(population), intent(inout) :: this
598 
599  !> @param environ the environment where we place the population
600  !! @warning Even though this parameter is optional, the bounding
601  !! environment should in most cases (almost?) be fixed, and
602  !! provided as a parameter. In most cases, unlimited environment
603  !! is useful for debugging only.
604  !! TODO: convert to class
605  class(environment), optional, intent(in) :: environ
606 
607  !> Local object representing the bounding environment for this population
608  type(environment) :: environ_here
609 
610  !> Local counter
611  integer :: i
612 
613  !> Check if the bounding environment is provided, if not, place agents
614  !! without limits
615  !! @warning The bounding environment should in most cases be fixed, and
616  !! provided as a parameter.
617  if (present(environ)) then
618  call environ_here%build( environ%lim_min(), environ%lim_max() )
619  else
620  call environ_here%build_unlimited()
621  end if
622 
623  !> Position agents randomly (uniform distribution) within the
624  !! bounding environment.
625  do i=1, this%population_size
626  call this%individual(i)%place_uniform(environ_here)
627  end do
628 
629  end subroutine position_individuals_uniform
630 
631  !-----------------------------------------------------------------------------
632  !> @brief This subroutine sorts the population `individual` object by
633  !! their \%fitness components.
634  !! @details The two subroutines `qsort` and `qs_partition_fitness` are a
635  !! variant of the recursive quick sort algorithm adapted for
636  !! `MEMBER_POPULATION` integer fitness component
637  elemental subroutine sort_population_by_fitness(this)
638  !> @param class, This -- member of population class (this)).
639  class(population), intent(inout) :: this
640 
641  call qsort(this%individual) !> This is the array component we sort.
642 
643  contains
644 
645  !...........................................................................
646  !> `qsort` is a recursive frontend for `MEMBER_POPULATION` objects
647  ! Based on:
648  ! - Juli Rew, SCD Consulting (juliana@ucar.edu), 9/03
649  ! - Based on algorithm from Cormen et al., Introduction to Algorithms, 1997
650  ! - Made F conformant by Walt Brainerd
651  ! - Source: http://www.fortran.com/qsort_c.f95
652  recursive pure subroutine qsort(A, is_reverse)
653 
654  !> @param `A` has the same type as the individual component objects
655  !! of the array-object that we are going to sort.
656  type(member_population), intent(in out), dimension(:) :: a
657  !> Logical flag for reverse (descending) sorting.
658  logical, optional, intent(in) :: is_reverse
659  integer :: iq
660 
661  if(size(a) > 1) then
662  call qs_partition_fitness(a, iq) ! partition
663  call qsort(a(:iq-1))
664  call qsort(a(iq:))
665  endif
666 
667  ! Check if reverse sorted array is requested
668  if (present(is_reverse)) then
669  if (is_reverse) a = a( size(a):1:-1 )
670  end if
671 
672  end subroutine qsort
673 
674  !...........................................................................
675  !> partition is a pivot backend for `fitness`
676  pure subroutine qs_partition_fitness(A, marker)
677 
678  type(member_population), intent(in out), dimension(:) :: a
679  integer, intent(out) :: marker
680  integer :: i, j
681  type(member_population) :: temp
682  !> @note Pivot point `x`, has the same type **as
683  !! the sorted object component**.
684  integer :: x
685 
686  !> Fitness is hardwired in this partition subroutine, but it can be used
687  !! as a model for similar othert sorting functions. Note that here integer
688  !! array is sorted.
689  x = a(1)%fitness
690  i= 0
691  j= size(a) + 1
692 
693  do
694  j = j-1
695  do
696  if (a(j)%fitness <= x) exit
697  j = j-1
698  end do
699  i = i+1
700  do
701  if (a(i)%fitness >= x) exit
702  i = i+1
703  end do
704  if (i < j) then
705  ! exchange A(i) and A(j)
706  temp = a(i)
707  a(i) = a(j)
708  a(j) = temp
709  elseif (i == j) then
710  marker = i+1
711  return
712  else
713  marker = i
714  return
715  endif
716  end do
717 
718  end subroutine qs_partition_fitness
719 
720  end subroutine sort_population_by_fitness
721 
722  !-----------------------------------------------------------------------------
723  !> Perform one or several steps of random walk by all agents.
724  !! @note This procedure was used for debugging.
725  subroutine population_rwalk3d_all_agents_step( this, dist_array, cv_array, &
726  dist_all, cv_all, &
727  environment_limits, n_walks )
728  class(population), intent(inout) :: this
729 
730  !> @param[in] step_size_array an array of step sizes for each individual.
731  real(SRP), optional, dimension(:), intent(in) :: dist_array
732  !> @param[in]cv_array Coefficients of variation for the walk.
733  real(SRP), optional, dimension(:), intent(in) :: cv_array
734  !> @param[in] dist_all the value of the walk step size that is identical in
735  !! all agents within the population.
736  real(SRP), optional, intent(in) :: dist_all
737  !> @param[in] cv_all the value of the walk coefficient of variation that is
738  !! identical in all agents within the population.
739  real(SRP), optional, intent(in) :: cv_all
740  !> @param environment_limits Limits of the environment area available for
741  !! the random walk. The moving object cannot get beyond this limit.
742  !! If this parameter is not provided, the environmental limits are
743  !! obtained automatically from the global array
744  !! the_environment::global_habitats_available.
745  class(environment), intent(in), optional :: environment_limits
746 
747  !> @param[in] n_walk optional number of walk steps that should be
748  !! performed, default just one.
749  integer, optional, intent(in) :: n_walks
750 
751  ! Local variables, copies of optionals.
752  real(SRP), dimension(this%population_size) :: dist_array_here, cv_array_here
753  integer :: n_walks_here
754 
755  ! Local params.
756  real(SRP), dimension(this%population_size) :: step_size_walk
757  integer :: j, i, ind, pop_n
758  integer, dimension(this%population_size) :: pop_permutation
759 
760  ! Default walk step CV.
761  real(SRP), parameter :: CV_DEFAULT = 0.5_srp
762 
763  !> ### Implementation details ###
764  !> - Calculate the distance array size.
765  pop_n = this%population_size
766 
767  if (present(dist_array)) then
768  dist_array_here = dist_array
769  else
770  dist_array_here = this%individual%get_length()
771  end if
772 
773  if (present(cv_array)) then
774  cv_array_here = cv_array
775  else
776  cv_array_here = cv_default
777  end if
778 
779  if (present(dist_all)) then
780  dist_array_here = dist_all
781  else
782  dist_array_here = this%individual%get_length()
783  end if
784 
785  if (present(cv_all)) then
786  cv_array_here = cv_all
787  else
788  cv_array_here = cv_default
789  end if
790 
791  if (present(n_walks)) then
792  n_walks_here = n_walks
793  else
794  n_walks_here = 1
795  end if
796 
797  !> - calculate the step size along the axes from the distance array.
798  step_size_walk = dist2step(dist_array_here)
799 
800  !> - Calculate the random permutation of individual indices.
801  !! @warning Random order here is a prototype for testing for use in
802  !! behaviour selection by population members.
803  pop_permutation = permute_random(pop_n)
804 
805  !> - Perform Gaussian random walks for each of the individuals in a random
806  !! order that is set by the `pop_permutation` array.
807  !! .
808  environ_restrict: if (present(environment_limits)) then
809  do j=1, n_walks_here
810  do i=1, pop_n
811  ind = pop_permutation(i)
812  if (this%individual(ind)%is_alive()) &
813  call this%individual(ind)%rwalk( step_size_walk(ind), &
814  cv_array_here(ind), &
815  environment_limits )
816  end do
817  end do
818  else environ_restrict
819  do j=1, n_walks_here
820  do i=1, pop_n
821  ind = pop_permutation(i)
822  if (this%individual(ind)%is_alive()) &
823  call this%individual(ind)%rwalk( &
824  step_size_walk(ind), &
825  cv_array_here(ind), &
826  global_habitats_available( &
827  this%individual(ind)%find_environment( &
828  global_habitats_available) ) )
829  end do
830  end do
831  end if environ_restrict
832 
834 
835  !-----------------------------------------------------------------------------
836  !> Perform one or several steps of random walk by all agents.
837  !! @note This procedure was used for debugging.
839  dist_array_xy, cv_array_xy, &
840  dist_array_depth, cv_array_depth, &
841  dist_all_xy, cv_all_xy, &
842  dist_all_depth, cv_all_depth, &
843  environment_limits, n_walks )
844  class(population), intent(inout) :: this
845 
846  !> @param[in] dist_array_xy an array of step sizes for each individual.
847  real(SRP), optional, dimension(:), intent(in) :: dist_array_xy
848  !> @param[in]cv_array_xy Coefficients of variation for the walk.
849  real(SRP), optional, dimension(:), intent(in) :: cv_array_xy
850 
851  !> @param[in] dist_array_depth an array of step sizes for each individual.
852  real(SRP), optional, dimension(:), intent(in) :: dist_array_depth
853  !> @param[in]cv_array_depth Coefficients of variation for the walk.
854  real(SRP), optional, dimension(:), intent(in) :: cv_array_depth
855 
856  !> @param[in] dist_all_xy the value of the walk step size for horizontal
857  !! plane that is identical in all agents within the population.
858  real(SRP), optional, intent(in) :: dist_all_xy
859  !> @param[in] cv_all_xy the value of the walk coefficient of variation in
860  !! the horizontal plane that is identical in all agents within
861  !! the population.
862  real(SRP), optional, intent(in) :: cv_all_xy
863 
864  !> @param[in] dist_all_depth the value of the walk step size for the depth
865  !! plane that is identical in all agents within the population.
866  real(SRP), optional, intent(in) :: dist_all_depth
867  !> @param[in] cv_all_depth the value of the walk coefficient of variation
868  !! in the depth plane that is identical in all agents within
869  !! the population.
870  real(SRP), optional, intent(in) :: cv_all_depth
871  !> @param environment_limits Limits of the environment area available for
872  !! the random walk. The moving object cannot get beyond this limit.
873  !! If this parameter is not provided, the environmental limits are
874  !! obtained automatically from the global array
875  !! the_environment::global_habitats_available.
876  class(environment), intent(in), optional :: environment_limits
877  !> @param[in] n_walk optional number of walk steps that should be
878  !! performed, default just one.
879  integer, optional, intent(in) :: n_walks
880 
881  ! Local variables, copies of optionals.
882  real(SRP), dimension(this%population_size) :: &
883  dist_array_xy_here, cv_array_xy_here
884  real(SRP), dimension(this%population_size) :: &
885  dist_array_depth_here, cv_array_depth_here
886  integer :: n_walks_here
887 
888  ! Local params.
889  real(SRP), dimension(this%population_size) :: step_size_walk_xy, &
890  step_size_walk_depth
891  integer :: j, i, ind, pop_n
892  integer, dimension(this%population_size) :: pop_permutation
893 
894  ! Default walk step CV.
895  real(SRP), parameter :: CV_DEFAULT = 0.5_srp
896 
897  !> ### Implementation details ###
898  !> - Calculate the distance array size.
899  pop_n = this%population_size
900 
901  if (present(dist_array_xy)) then
902  dist_array_xy_here = dist_array_xy
903  else
904  dist_array_xy_here = this%individual%get_length()
905  end if
906 
907  if (present(cv_array_xy)) then
908  cv_array_xy_here = cv_array_xy
909  else
910  cv_array_xy_here = cv_default
911  end if
912 
913  !> - If the depth walk step distance is not provided as a parameter,
914  !! 1/2 of the agent body size is used as the default value. Thus,
915  !! it is assumed that the extent of random movements of the agents
916  !! in the horizontal plane is greater than vertical movements.
917  if (present(dist_array_depth)) then
918  dist_array_depth_here = dist_array_depth
919  else
920  dist_array_depth_here = this%individual%get_length() / 2.0_srp
921  end if
922 
923  if (present(cv_array_depth)) then
924  cv_array_depth_here = cv_array_depth
925  else
926  cv_array_depth_here = cv_default
927  end if
928 
929  if (present(dist_all_xy)) then
930  dist_array_xy_here = dist_all_xy
931  else
932  dist_array_xy_here = this%individual%get_length()
933  end if
934 
935  if (present(cv_all_xy)) then
936  cv_array_xy_here = cv_all_xy
937  else
938  cv_array_xy_here = cv_default
939  end if
940 
941  if (present(dist_all_depth)) then
942  dist_array_depth_here = dist_all_depth
943  else
944  dist_array_depth_here = this%individual%get_length() / 2.0_srp
945  end if
946 
947  if (present(cv_all_depth)) then
948  cv_array_depth_here = cv_all_depth
949  else
950  cv_array_depth_here = cv_default
951  end if
952 
953  if (present(n_walks)) then
954  n_walks_here = n_walks
955  else
956  n_walks_here = 1
957  end if
958 
959  !> - Calculate the step size along the axes from the distance array.
960  step_size_walk_xy = dist2step(dist_array_xy_here)
961  step_size_walk_depth = dist2step(dist_array_depth_here)
962 
963  !> - Calculate the random permutation of individual indices.
964  !! @warning Random order here is a prototype for testing for use in
965  !! behaviour selection by population members.
966  pop_permutation = permute_random(pop_n)
967 
968  !> - Perform Gaussian random walks for each of the individuals in a random
969  !! order that is set by the `pop_permutation` array.
970  !! .
971  environ_restrict: if (present(environment_limits)) then
972  do j=1, n_walks_here
973  do i=1, pop_n
974  ind = pop_permutation(i)
975  if (this%individual(ind)%is_alive()) &
976  call this%individual(ind)%rwalk25d &
977  ( meanshift_xy = step_size_walk_xy(ind), &
978  cv_shift_xy = cv_array_xy_here(ind), &
979  meanshift_depth = step_size_walk_depth(ind), &
980  cv_shift_depth = cv_array_depth_here(ind), &
981  environment_limits = environment_limits )
982  end do
983  end do
984  else environ_restrict
985  do j=1, n_walks_here
986  do i=1, pop_n
987  ind = pop_permutation(i)
988  if (this%individual(ind)%is_alive()) &
989  call this%individual(ind)%rwalk25d &
990  ( meanshift_xy = step_size_walk_xy(ind), &
991  cv_shift_xy = cv_array_xy_here(ind), &
992  meanshift_depth = step_size_walk_depth(ind), &
993  cv_shift_depth = cv_array_depth_here(ind), &
994  environment_limits=global_habitats_available( &
995  this%individual(ind)%find_environment( &
996  global_habitats_available) ) )
997  end do
998  end do
999  end if environ_restrict
1000 
1002 
1003  !-----------------------------------------------------------------------------
1004  !> Subject the population to an attack by a specific predator. The predator
1005  !! acts on agents in its proximity and takes account of the predation
1006  !! confusion and dilution effects (see
1007  !! the_environment::predator::risk_fish_group()).
1008  subroutine population_subject_predator_attack(this, this_predator, &
1009  time_step_model)
1010  class(population), intent(inout) :: this
1011  class(predator), intent(in) :: this_predator
1012  integer, optional, intent(in) :: time_step_model
1013 
1014  ! Local copie sof optionals
1015  integer :: time_step_model_here
1016 
1017  !> ### Notable variables ###
1018  !> - p_risk is an array with the size equal to the agent population size,
1019  !! that keeps all the attack probabilities calculated by the
1020  !! the_environment::predator::risk_fish_group() function.
1021  real(SRP), dimension(size(this%individual)) :: p_risk
1022 
1023  !> - prey_index is the partial index of the prey agents that are in proximity of
1024  !! this predator.
1025  !! .
1026  integer, dimension(size(this%individual)) :: prey_index
1027 
1028  ! This is a temporary array of the the_environment::spatial type to keep
1029  ! the location of the prey agent data.
1030  ! COMPILER BUG REPORT:
1031  ! @warning It is necessary as a work around the bug in GNU gfortran
1032  ! where the array is passed into the (neighbours) subroutine
1033  ! and suddenly loses the standard Fortran array bounds starting
1034  ! from 1 and gets C-specific bounds starting from 0, keeping
1035  ! the overall array size intact.
1036  type(spatial), dimension(size(this%individual)) :: tmp_location
1037 
1038  ! Local counter
1039  integer :: i
1040 
1041  ! PROCNAME is the procedure name for logging and debugging
1042  character(len=*), parameter :: PROCNAME = &
1043  "(population_subject_predator_attack)"
1044 
1045  !> ### Implementation notes ###
1046  !> **Preparations:** Check optional time step parameter. If not provided,
1047  !! use global commondata::global_time_step_model_current parameter value.
1048  if (present(time_step_model)) then
1049  time_step_model_here = time_step_model
1050  else
1051  time_step_model_here = global_time_step_model_current
1052  end if
1053 
1054  !> **First,** calculate the risk of predation from this specific predator
1055  !! to each of the agents in the population using the
1056  !! the_environment::predator::risk_fish_group() method. The "raw" indexed
1057  !! output scheme is used here to avoid multiple cycling over the whole
1058  !! large population of agents, only the agents that are closest to the
1059  !! predator are processed and attacked (maximum number is equal to the
1060  !! index limit commondata::predator_risk_group_select_index_partial).
1061  tmp_location = this%individual%location()
1062  call this_predator%risk_fish_group( &
1063  prey_spatial = tmp_location, &
1064  prey_length = this%individual%get_length(), &
1065  is_freezing = this%individual%freeze%is_executed(), &
1066  time_step_model = time_step_model_here, &
1067  risk_indexed = p_risk, &
1068  index_dist = prey_index )
1069 
1070  !> **Second,** cycle through all the agents in close proximity of the
1071  !! predator, up to the maximum size of partial indexing parameter
1072  !! commondata::predator_risk_group_select_index_partial. The predator then
1073  !! stochastically attacks each of these agents with the probability equal
1074  !! to the risk of predation. If the attack is successful, the agent
1075  !! the_genome::individual_genome::dies() and loop is exited because the
1076  !! predator is assumed to catch only one agent at a time.
1078  if ( this%individual(prey_index(i))%is_alive() ) then
1079  if ( rand_r4() < p_risk(i) ) then
1080  ! Increment global counter of agents that are eaten by predators
1082  call this%individual(prey_index(i))%dies()
1083  call log_dbg( ltag_info // "The agent # " // tostr(prey_index(i)) //&
1084  " (" // tostr(i) // ")" // &
1085  " is caught by the predator and dies.", &
1086  procname, modname)
1087  call log_dbg( ltag_info // "Agent status is: " // &
1088  tostr(this%individual(prey_index(i))%is_alive()) )
1089  exit ! the predator catches only one agent at a time
1090  end if
1091  end if
1092  end do
1093 
1094  end subroutine population_subject_predator_attack
1095 
1096  !-----------------------------------------------------------------------------
1097  !> Subject the population to mortality caused by habitat-specific
1098  !! mortality risk. Each agent is affected by the risk associated with
1099  !! the habitat it is currently in.
1100  !> @note Note that there is no such a function for a single agent as it does
1101  !! not seem to be necessary.
1103  class(population), intent(inout) :: this
1104 
1105  ! Local variable that sets the habitat (number) the agent is currently in
1106  ! within the global array the_environment::global_habitats_available.
1107  integer :: agent_in
1108 
1109  ! Local counter.
1110  integer :: i
1111 
1112  ! PROCNAME is the procedure name for logging and debugging
1113  character(len=*), parameter :: PROCNAME = "(population_subject_other_risks)"
1114 
1115  !> ### Implementation notes ###
1116  !> All agents in the population are randomly subjected to the mortality
1117  !! risk the_environment::habitat::risk_mortality that is linked to the
1118  !! habitat object the agent is currently in in a loop.
1119  do i=1, this%population_size
1120  agent_in = this%individual(i)%find_environment()
1121  if (rand_r4() < global_habitats_available(agent_in)%get_mortality()) then
1122  !> If the agent is unhappy and is subjected to mortality event, it
1123  !! immediately the_genome::individual_genome::dies().
1124  call this%individual(i)%dies()
1125  call log_dbg( ltag_info // "Agent " // &
1126  tostr(this%individual(i)%get_id()) // " dies due to " // &
1127  "habitat linked mortality risk " // &
1128  tostr(global_habitats_available(agent_in)%get_mortality()), &
1129  procname, modname )
1130  end if
1131  end do
1132 
1133  end subroutine population_subject_other_risks
1134 
1135  !-----------------------------------------------------------------------------
1136  !> Subject all members of this population to their individual mortality
1137  !! risks.
1139  class(population), intent(inout) :: this
1140 
1141  ! Local counter.
1142  integer :: i
1143 
1144  !> ### Implementation notes ###
1145  !> The procedure is simple, loop over all individual agents in the
1146  !! population and stochastically call the_genome::individual_genome::dies()
1147  !! method with probability equal to individual mortality of the agent.
1148  do i=1, this%population_size
1149  if ( rand_r4() < this%individual(i)%get_mortality() ) &
1150  call this%individual(i)%dies()
1151  end do
1152 
1154 
1155  !-----------------------------------------------------------------------------
1156  !> This procedure performs a **single step** of the life cycle of the whole
1157  !! population, the agents for the step are selected in a random order.
1159  class(population), intent(inout) :: this
1160 
1161  !> ### Notable variables ###
1162  !> - `inds_order` is an array that sets the order in which the agents are
1163  !! being drawn out of the population to perform the step.
1164  integer, dimension(this%population_size) :: inds_order
1165 
1166  !> - `ind_seq` is the sequential number of the agent as drawn from the
1167  !! population; it is the loop control counter variable.
1168  integer :: ind_seq
1169 
1170  !> - `ind_real` is the real sequential number of the agent in the
1171  !! population; this real id is obtained from the order array
1172  !! `inds_order`.
1173  !! .
1174  integer :: ind_real
1175 
1176  ! Local variable that sets the habitat (number) the agent is currently in
1177  ! within the global array the_environment::global_habitats_available.
1178  integer :: agent_in
1179 
1180  ! PROCNAME is the procedure name for logging and debugging
1181  character(len=*), parameter :: PROCNAME = &
1182  "(population_lifecycle_step_preevol)"
1183 
1184  !> ### Implementation notes ###
1185  !> First, an ordering array `inds_order` is calculated that sets the order
1186  !! in which the agents are drawn from the population. In the simplest case,
1187  !! the order of the agents is random, so this array is actually an array of
1188  !! random integers. The procedure
1189  !! [PERMUTE_RANDOM](http://ahamodel.uib.no/doc/ar01s09.html#_random_permutation_permute_random_function)
1190  !! from HEDTOOLS is used here then.
1191  inds_order = permute_random(this%population_size)
1192 
1193  !> The agents can also be processed in any **non-random** order. This would
1194  !! require invoking an array indexing procedure
1195  !! [ARRAY_INDEX](http://ahamodel.uib.no/doc/ar01s07.html#_subroutines_array_index_and_array_rank)
1196  !! instead of `PERMUTE_RANDOM`.
1197  !!
1198  !! For example, to process the agents in the order of the body mass,
1199  !! the ordering array can be obtained obtained as below:
1200  !! @code
1201  !! call ARRAY_INDEX(this%individual%get_mass(), inds_order)
1202  !! @endcode
1203  !> But this code would rank the agents in an *increasing* order of their
1204  !! body mass. If this is not what is expected, e.g. if the
1205  !! non-random selection is used to mimic a competitive advantage for bigger
1206  !! and heavier agents, a reverse of the ordering is obtained like this:
1207  !! @code
1208  !! inds_order = inds_order(this%population_size:1:-1)
1209  !! @endcode
1210 
1211  !> Second, the agents are drawn from the population, one by one, in the
1212  !! named loop construct `INDIVIDUALS`.
1213  individuals: do ind_seq = 1, popsize
1214 
1215  !> - One individual is then drawn from the `inds_order` ordering array.
1216  ind_real = inds_order(ind_seq)
1217 
1218  associate( agent => this%individual(ind_real) )
1219 
1220  !> #### Initialisations ####
1221  !> - First, a check is done if the agent is dead or starved to death;
1222  !! if yes, no further processing is done. Also, in the former case
1223  !! the individual_genome::dies() method is called.
1224  if (agent%is_dead()) then
1225  cycle individuals
1226  end if
1227  if (agent%starved_death()) then
1228  call agent%dies()
1229  cycle individuals
1230  end if
1231 
1232  !> - `agent_in` index calculates the habitat number where the selected
1233  !! agent is currently in, calling the
1234  !! the_environment::spatial::find_environment() method.
1235  agent_in = agent%find_environment()
1236  if (agent_in<1 .or. agent_in>size(global_habitats_available) ) then
1237  call log_msg( ltag_error // "agent_in zero in " // procname // &
1238  tostr([agent%xpos(),agent%ypos(),agent%dpos()]) )
1239  call log_msg( ltag_error // "Agent: " // tostr(ind_real) // &
1240  " in " // tostr(ind_seq) )
1241  end if
1242 
1243  !> #### Get perceptions ####
1244  !! - Inner perceptions are obtained from the agent's "organism":
1245  !! stomach contents, bodymass, energy, age, reproductive factor:
1246  !! the_neurobio::perception::perceptions_inner().
1247  call agent%perceptions_inner()
1248 
1249  !> - Simple environmental perceptions are obtained: light, depth:
1250  !! the_neurobio::perception::perceptions_environ().
1251  call agent%perceptions_environ()
1252 
1253  !> - Spatial perceptions are obtained for food items, conspecifics and
1254  !! predators in proximity of this agent:
1255  !! - the_neurobio::perception::see_food()
1256  !! - the_neurobio::perception::see_consp()
1257  !! - the_neurobio::perception::see_pred()
1258  !! .
1259  call agent%see_food( global_habitats_available( agent_in )%food )
1260  call agent%see_consp( this%individual )
1261  call agent%see_pred( global_habitats_available( agent_in )%predators )
1262 
1263  !> - The above perceptions are added into the memory stack calling
1264  !! the_neurobio::perception::perception_to_memory().
1265  !! .
1266  call agent%perception_to_memory()
1267 
1268  !> #### Appraisal: Produce motivations ####
1269  !> - Perception components are calculated for each of the motivational
1270  !! states of the agent via the neuronal response function(s):
1271  !! the_neurobio::appraisal::motivations_percept_components().
1272  call agent%motivations_percept_components()
1273  !> - Then, primary motivation values are calculated from the perception
1274  !! components: the_neurobio::appraisal::motivations_primary_calc().
1275  call agent%motivations_primary_calc()
1276  !> - The values of the primary motivations are subjected to modulation
1277  !! the_neurobio::appraisal::modulation().
1278  call agent%modulation()
1279 
1280  !> - Final motivations of the agent are saved into the emotional
1281  !! memory stack: the_neurobio::appraisal::motivations_to_memory().
1282  !! .
1283  call agent%motivations_to_memory()
1284 
1285  !> #### Determine the Global Organismic State ####
1286  !> - The Global Organismic State of the agent is calculated using the
1287  !! the_neurobio::gos_global::gos_find() method.
1288  call agent%gos_find()
1289  call log_dbg( ltag_info // "Agent " // tostr(agent%get_id()) // &
1290  ", GOS is: " // agent%gos_label() // &
1291  ", GOS arousal :" // tostr(agent%arousal()), &
1292  procname, modname )
1293 
1294  !> - The population-wise maximum motivation value that is used for
1295  !! rescaling is calculated now.
1297  maxval( this%individual%motivations%max_perception() )
1298 
1299  !> #### Determine and execute the optimal behaviour ####
1300  !> - Once the GOS is determined for this agent, it selects the
1301  !! individually optimal behaviour that minimises its expected
1302  !! arousal; this behaviour is then executed. Both steps are
1303  !! implemented in the the_neurobio::behaviour::do_behave()
1304  !! method.
1305  !! .
1306  call agent%do_behave( &
1307  rescale_max_motivation=global_rescale_maximum_motivation )
1308  call log_dbg( ltag_info // "Agent " // tostr(agent%get_id()) // &
1309  " executed behaviour: " // agent%behaviour_is() // &
1310  " (at global time_step " // &
1311  tostr(global_time_step_model_current) // &
1312  ")", procname, modname )
1313 
1314  !> #### Update characteristics of the agent ####
1315  !> - Finally, characteristics of this agent are updated depending on
1316  !! the consequences of the behaviour. For example, hormone levels
1317  !! are updated the_body::condition::sex_steroids_update() and the
1318  !! living cost of the agent is subtracted
1319  !! (the_body::condition::subtract_living_cost()).
1320  !! Digestion also occurs by emptying the stomach by a fixed
1321  !! fraction (the_body::condition::stomach_empify(). Also, the
1322  !! energy reserves of the agent are updated based on the current
1323  !! mass and length (the_body::condition::energy_update().
1324  !! Note that the agent characteristics that directly follow from
1325  !! the behaviour unit that has been executed (e.g. food gain if the
1326  !! agent decided to eat a food item or travel cost if the agent
1327  !! migrated) are processed and updated in the respective behaviour
1328  !! execution method.
1329  call agent%sex_steroids_update()
1330  call agent%subtract_living_cost()
1331  call agent%stomach_empify()
1332  call agent%energy_update()
1333 
1334  !> - Finally, the age of the agent is incremented to one time step.
1335  call agent%age_increment()
1336 
1337  !> - Finally, another check is done if the agent is starved to death,
1338  !! if yes, the agent individual_genome::dies().
1339  !! .
1340  if (agent%starved_death()) then
1341  call agent%dies()
1342  end if
1343 
1344  end associate
1345 
1346  end do individuals
1347 
1348  end subroutine population_lifecycle_step_preevol
1349 
1350  !-----------------------------------------------------------------------------
1351  !> This procedure performs a **single step** of the life cycle of the whole
1352  !! population, the agents for the step are selected in a random order.
1353  !! @warning This version of the life cycle step includes only optimal food
1354  !! selection and eating and does not include the full fledged
1355  !! behaviour selection cascade of procedures ::do_behave().
1357  class(population), intent(inout) :: this
1358 
1359  !> ### Notable variables ###
1360  !> - `inds_order` is an array that sets the order in which the agents are
1361  !! being drawn out of the population to perform the step.
1362  integer, dimension(this%population_size) :: inds_order
1363 
1364  !> - `ind_seq` is the sequential number of the agent as drawn from the
1365  !! population; it is the loop control counter variable.
1366  integer :: ind_seq
1367 
1368  !> - `ind_real` is the real sequential number of the agent in the
1369  !! population; this real id is obtained from the order array
1370  !! `inds_order`.
1371  !! .
1372  integer :: ind_real
1373 
1374  ! Local variable that sets the habitat (number) the agent is currently in
1375  ! within the global array the_environment::global_habitats_available.
1376  integer :: agent_in
1377 
1378  ! Local variable that keeps the optimal food item
1379  integer :: food_item_selected
1380 
1381  ! PROCNAME is the procedure name for logging and debugging
1382  character(len=*), parameter :: PROCNAME = &
1383  "(population_lifecycle_step_eatonly_preevol)"
1384 
1385  !> ### Implementation notes ###
1386  !> First, an ordering array `inds_order` is calculated that sets the order
1387  !! in which the agents are drawn from the population. In the simplest case,
1388  !! the order of the agents is random, so this array is actually an array of
1389  !! random integers. The procedure
1390  !! [PERMUTE_RANDOM](http://ahamodel.uib.no/doc/ar01s09.html#_random_permutation_permute_random_function)
1391  !! from HEDTOOLS is used here then.
1392  inds_order = permute_random(this%population_size)
1393 
1394  !> The agents can also be processed in any **non-random** order. This would
1395  !! require invoking an array indexing procedure
1396  !! [ARRAY_INDEX](http://ahamodel.uib.no/doc/ar01s07.html#_subroutines_array_index_and_array_rank)
1397  !! instead of `PERMUTE_RANDOM`.
1398  !!
1399  !! For example, to process the agents in the order of the body mass,
1400  !! the ordering array can be obtained obtained as below:
1401  !! @code
1402  !! call ARRAY_INDEX(this%individual%get_mass(), inds_order)
1403  !! @endcode
1404  !> But this code would rank the agents in an *increasing *order of their
1405  !! of their body mass. If this is not what is expected, e.g. if the
1406  !! non-random selection is used to mimic a competitive advantage for bigger
1407  !! and heavier agents, a reverse of the ordering is obtained like this:
1408  !! @code
1409  !! inds_order = inds_order(this%population_size:1:-1)
1410  !! @endcode
1411 
1412  !> Second, the agents are drawn from the population, one by one, in the
1413  !! named loop construct `INDIVIDUALS`.
1414  individuals: do ind_seq = 1, popsize
1415 
1416  !> - One individual is then drawn from the `inds_order` ordering array.
1417  ind_real = inds_order(ind_seq)
1418 
1419  associate( agent => this%individual(ind_real) )
1420 
1421  !> #### Initialisations ####
1422  !> - First, a check is done if the agent is dead or starved to death;
1423  !! if yes, no further processing is done. Also, in the former case
1424  !! the individual_genome::dies() method is called.
1425  if (agent%is_dead()) then
1426  cycle individuals
1427  end if
1428  if (agent%starved_death()) then
1429  call agent%dies()
1430  cycle individuals
1431  end if
1432 
1433  !> - `agent_in` index calculates the habitat number where the selected
1434  !! agent is currently in, calling the
1435  !! the_environment::spatial::find_environment() method.
1436  agent_in = agent%find_environment()
1437  if (agent_in<1 .or. agent_in>size(global_habitats_available) ) then
1438  call log_msg( ltag_error // "agent_in zero in " // procname // &
1439  tostr([agent%xpos(),agent%ypos(),agent%dpos()]) )
1440  call log_msg( ltag_error // "Agent: " // tostr(ind_real) // &
1441  " in " // tostr(ind_seq) )
1442  end if
1443 
1444  !> #### Get perceptions ####
1445  !! - Inner perceptions are obtained from the agent's "organism":
1446  !! stomach contents, bodymass, energy, age, reproductive factor:
1447  !! the_neurobio::perception::perceptions_inner().
1448  call agent%perceptions_inner()
1449 
1450  !> - Simple environmental perceptions are obtained: light, depth:
1451  !! the_neurobio::perception::perceptions_environ().
1452  call agent%perceptions_environ()
1453 
1454  !> - Spatial perceptions are obtained for food items, conspecifics and
1455  !! predators in proximity of this agent:
1456  !! - the_neurobio::perception::see_food()
1457  !! - the_neurobio::perception::see_consp()
1458  !! - the_neurobio::perception::see_pred()
1459  !! .
1460  call agent%see_food( global_habitats_available( agent_in )%food )
1461  call agent%see_consp( this%individual )
1462  call agent%see_pred( global_habitats_available( agent_in )%predators )
1463 
1464  !> - The above perceptions are added into the memory stack calling
1465  !! the_neurobio::perception::perception_to_memory().
1466  !! .
1467  call agent%perception_to_memory()
1468 
1469  !> #### Appraisal: Produce motivations ####
1470  !> - Perception components are calculated for each of the motivational
1471  !! states of the agent via the neuronal response function(s):
1472  !! the_neurobio::appraisal::motivations_percept_components().
1473  call agent%motivations_percept_components()
1474  !> - Then, primary motivation values are calculated from the perception
1475  !! components: the_neurobio::appraisal::motivations_primary_calc().
1476  call agent%motivations_primary_calc()
1477  !> - The values of the primary motivations are subjected to modulation
1478  !! the_neurobio::appraisal::modulation().
1479  call agent%modulation()
1480 
1481  !> - Final motivations of the agent are saved into the emotional
1482  !! memory stack: the_neurobio::appraisal::motivations_to_memory().
1483  !! .
1484  call agent%motivations_to_memory()
1485 
1486  !> #### Determine the Global Organismic State ####
1487  !> - The Global Organismic State of the agent is calculated using the
1488  !! the_neurobio::gos_global::gos_find() method.
1489  call agent%gos_find()
1490  call log_dbg( ltag_info // "Agent " // tostr(agent%get_id()) // &
1491  ", GOS is: " // agent%gos_label() // &
1492  ", GOS arousal :" // tostr(agent%arousal()), &
1493  procname, modname )
1494 
1495  !> - The population-wise maximum motivation value that is used for
1496  !! rescaling is calculated now.
1497  global_rescale_maximum_motivation = &
1498  maxval( this%individual%motivations%max_perception() )
1499 
1500  !> #### Determine and execute the optimal behaviour ####
1501  !> - Once the GOS is determined for this agent, it selects the optimal
1502  !! food item that minimises its expected arousal and then tries to
1503  !! eat this food item. However, if the agent has no food in its
1504  !! perception a default random walk is executed.
1505  !! .
1506  do_behave: if ( agent%has_food() ) then
1507  food_item_selected = agent%food_item_select( &
1508  rescale_max_motivation=global_rescale_maximum_motivation)
1509  ! ++++ EAT FOOD ITEM
1510  call agent%do_eat_food_item( &
1511  food_item_selected, global_habitats_available( agent_in )%food )
1512  else do_behave
1513  call agent%do_walk()
1514  end if do_behave
1515 
1516  call log_dbg( ltag_info // "Agent " // tostr(agent%get_id()) // &
1517  " executed behaviour: " // agent%behaviour_is() // &
1518  " (at global time_step " // &
1519  tostr(global_time_step_model_current) // &
1520  ")", procname, modname )
1521 
1522  !> #### Update characteristics of the agent ####
1523  !> - Finally, characteristics of this agent are updated depending on
1524  !! the consequences of the behaviour. For example, hormone levels
1525  !! are updated and the living cost of the agent is subtracted.
1526  !! Note that the agent characteristics that directly follow from
1527  !! the behaviour unit that has been executed (e.g. food gain if the
1528  !! agent decided to eat a food item or travel cost if the agent
1529  !! migrated) are processed and updated in the respective behaviour
1530  !! execution method.
1531  call agent%sex_steroids_update()
1532  call agent%subtract_living_cost()
1533  call agent%energy_update()
1534 
1535  !> - Finally, the age of the agent is incremented to one time step.
1536  call agent%age_increment()
1537 
1538  !> - Finally, another check is done if the agent is starved to death,
1539  !! if yes, the agent individual_genome::dies().
1540  !! .
1541  if (agent%starved_death()) then
1542  call agent%dies()
1543  end if
1544 
1545  end associate
1546 
1547  end do individuals
1548 
1550 
1551  !-----------------------------------------------------------------------------
1552  !> Save data for all agents within the population into a CSV file.
1553  !> @note Note that this procedure is not using the @ref file_io wrappers.
1554  subroutine population_save_data_all_agents_csv(this, csv_file_name, &
1555  save_header, is_logging, is_success)
1556  use csv_io
1557  class(population), intent(in) :: this
1558  !> @param[in] csv_file_name is the name of the CSV output file.
1559  character(len=*), intent(in) :: csv_file_name
1560  !> @param[in] save_header turn ON/OFF of the descriptive file header.
1561  !! Header is saved into the first row of the CSV output file
1562  !! If not present, default is FALSE.
1563  logical, optional, intent(in) :: save_header
1564  !> @param[in] is_logging turn ON/OFF writing the file name and data into
1565  !! the logger. If not present, default is TRUE if it is the
1566  !! debug mode.
1567  logical, optional, intent(in) :: is_logging
1568  !> @param[out] is_success Flag showing that data save was successful
1569  !! (if TRUE).
1570  logical, optional, intent(out) :: is_success
1571 
1572  ! Local copies of optionals.
1573  logical :: logging_enabled
1574 
1575  ! Counter
1576  integer :: ind
1577 
1578  !> ### Implementation notes ###
1579  !> #### Local variables for CSV backend ####
1580  !> - `handle_csv` is the CSV file handle object defining the file name,
1581  !! Fortran unit and error descriptor, see HEDTOOLS manual for details.
1582  type(csv_file) :: handle_csv
1583  !> - `csv_record_tmp` is the temporary character string that keeps the
1584  !! whole record of the file, i.e. the whole row of the spreadsheet table.
1585  character(len=:), allocatable :: csv_record_tmp
1586  !> - `COLUMNS` is a parameter array that keeps all column headers; its
1587  !! size is equal to the total number of variables (columns) in the data
1588  !! spreadsheet file.
1589  !! .
1590  character(len=LABEL_LENGTH), dimension(*), &
1591  parameter :: COLUMNS = [ character(len=label_length) :: & ! COLS
1592  "ID_NUM ", & ! 1
1593  "PERS_NAME", & ! 2
1594  "ALIVE ", & ! 3
1595  "SEX_MALE ", & ! 4
1596  "BODY_LEN ", & ! 5
1597  "BIRTH_LEN", & ! 6
1598  "CTRL_RND ", & ! 7
1599  "BODY_MASS", & ! 8
1600  "BIRTHMASS", & ! 9
1601  "ENERGY ", & ! 10
1602  "BIRT_ENER", & ! 11
1603  "STOMACH ", & ! 12
1604  "MAXSTOMCP", & ! 13
1605  "SMR ", & ! 14
1606  "S_COST_SW", & ! 15
1607  "LIV_COST ", & ! 16
1608  "MORTALITY", & ! 17
1609  "HORM_GROW", & ! 18
1610  "HORM_THYR", & ! 19
1611  "HORM_ADRE", & ! 20
1612  "HORM_CORT", & ! 21
1613  "HORM_TEST", & ! 22
1614  "HORM_ESTR", & ! 23
1615  "HTEST_BAS", & ! 24
1616  "HESTR_BAS", & ! 25
1617  "REPR_FAC ", & ! 26
1618  "P_REPROD ", & ! 27
1619  "N_REPROD ", & ! 28
1620  "N_OFFSPNG", & ! 29
1621  "AGE ", & ! 30
1622  "GOS_MAIN ", & ! 31
1623  "GOS_ARUSL", & ! 32
1624  "GOS_REPET", & ! 33
1625  "EAT_ATMPT", & ! 34
1626  "N_EATEN ", & ! 35
1627  "MASSEATEN", & ! 36
1628  "PERC_FOOD", & ! 37
1629  "PERC_CONS", & ! 38
1630  "PERC_PRED", & ! 39
1631  "POS_X ", & ! 40
1632  "POS_Y ", & ! 41
1633  "POS_DEPTH", & ! 42
1634  "HABITAT ", & ! 43
1635  "FITNNESS " ] ! 44
1636 
1637  ! PROCNAME is the procedure name for logging and debugging
1638  character(len=*), parameter :: &
1639  PROCNAME = "(population_save_data_all_agents_csv)"
1640 
1641  ! Local, the name of the habitat the agent is currently in, out of the
1642  ! commondata::global_habitats_available array.
1643  character(len=LABEL_LENGTH) :: habitat_in
1644 
1645  ! Local timer object for file write.
1646  type(timer_cpu) :: file_write_timing
1647 
1648  if (present(is_logging)) then
1649  logging_enabled = is_logging
1650  else
1651  logging_enabled = is_debug
1652  end if
1653 
1654  if (logging_enabled) then
1655  call log_msg (ltag_info // "Saving all individuals in population # " // &
1656  tostr(this%pop_number) // " (name '" // trim(this%pop_name) // &
1657  "'), " // &
1658  "generation # " // tostr(global_generation_number_current) // &
1659  ", time step " // tostr(global_time_step_model_current) // &
1660  " to file: " // csv_file_name )
1661  call file_write_timing%start( &
1662  "Writing population data for population '" // &
1663  trim(this%pop_name) // "' " // tostr(size(this%individual)) &
1664  // " individuals" )
1665  end if
1666 
1667  !> #### Save data in CSV file ####
1668  !> @note Note that this subroutine does not use the object oriented
1669  !! wrappers from the @ref file_io module.
1670  !!
1671  !> - Define the file name \%name component of the CSV file handle. The
1672  !! file handle object `handle_csv` is now used as the file identifier.
1673  handle_csv%name = csv_file_name
1674  !> - Open the output file defined by the `handle_csv` handle object for
1675  !! writing.
1676  call csv_open_write( handle_csv )
1677 
1678  !> - Possible error status of the latest file operation is obtained by the
1679  !! \%status component of the file handle. Check if there were any errors
1680  !! opening the file and report in the logger with the error tag.
1681  if ( .not. handle_csv%status ) then
1682  call log_msg( ltag_error // "Opening output CSV file FAILED: " // &
1683  csv_file_name // ", in " // procname )
1684  call log_msg( ltag_error // "Data file " // csv_file_name // &
1685  " is not written in " // procname )
1686  if (present(is_success)) is_success = handle_csv%status
1687  return
1688  end if
1689 
1690  !> - If the `save_header` flag is set to TRUE, save the CSV file header.
1691  if (present(save_header)) then
1692  if (save_header) then
1693  call csv_header_write( "Population: " // this%pop_name, handle_csv )
1694  if ( .not. handle_csv%status ) then ! treat possible write error.
1695  if (present(is_success)) is_success = .false.
1696  call csv_close( handle_csv )
1697  return
1698  end if
1699  end if
1700  end if
1701 
1702  !> - Prepare the character string variable `csv_record_tmp` that keeps the
1703  !! whole record (row) of data in the output CSV data file. The length of
1704  !! this string should be enough to fit all the record data, otherwise
1705  !! the record is truncated.
1706  csv_record_tmp = repeat(" ", size(columns) * len(columns(1)) )
1707 
1708  !> - Produce the first record containing the column headers (variable
1709  !! names). Note that
1710  !! [CSV_RECORD_APPEND()](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_record_append)
1711  !! accepts both arrays and scalar values for appending. Also, write the
1712  !! first record physically to the file.
1713  call csv_record_append( csv_record_tmp, columns )
1714  call csv_record_write ( csv_record_tmp, handle_csv )
1715  if ( .not. handle_csv%status ) then ! treat possible write error.
1716  if (present(is_success)) is_success = .false.
1717  call csv_close( handle_csv )
1718  return
1719  end if
1720 
1721 
1722  !> - The actual data are written to the CSV file in a loop over all the
1723  !! individual members of the population. One record (row) of the data
1724  !! file then represents a single individual.
1725  do ind = 1, size(this%individual)
1726  !> - the `csv_record_tmp` character string variable is produced such
1727  !! that it can fit the whole record;
1728  csv_record_tmp = repeat(" ", &
1729  max( csv_guess_record_length(size(columns) + 1,0.0_srp),&
1730  len(this%individual(ind)%genome_label) ) )
1731 
1732  !> - the actual data for the individual is appended to the current
1733  !! record one by one. Note that logical values are converted to
1734  !! integers using commondata::conv_l2r() function.
1735  associate( agent => this%individual(ind) ) !COLS
1736  call csv_record_append(csv_record_tmp,agent%person_number ) ! 1
1737  call csv_record_append(csv_record_tmp,agent%genome_label ) ! 2
1738  call csv_record_append(csv_record_tmp,conv_l2r(agent%alive) ) ! 3*
1739  call csv_record_append(csv_record_tmp,conv_l2r(agent%sex_is_male)) ! 4*
1740  call csv_record_append(csv_record_tmp,agent%body_length ) ! 5
1741  call csv_record_append(csv_record_tmp,agent%body_length_birth ) ! 6
1742  call csv_record_append(csv_record_tmp,agent%control_unselected ) ! 7
1743  call csv_record_append(csv_record_tmp,agent%body_mass ) ! 8
1744  call csv_record_append(csv_record_tmp,agent%body_mass_birth ) ! 9
1745  call csv_record_append(csv_record_tmp,agent%energy_current ) ! 10
1746  call csv_record_append(csv_record_tmp,agent%energy_birth ) ! 11
1747  call csv_record_append(csv_record_tmp,agent%stomach_content_mass ) ! 12
1748  call csv_record_append(csv_record_tmp,agent%maxstomcap ) ! 13
1749  call csv_record_append(csv_record_tmp,agent%smr ) ! 14
1750  call csv_record_append(csv_record_tmp,agent%cost_swim_std() ) ! 15
1751  call csv_record_append(csv_record_tmp,agent%living_cost() ) ! 16
1752  call csv_record_append(csv_record_tmp,agent%ind_mortality ) ! 17
1753  call csv_record_append(csv_record_tmp,agent%growhorm_level ) ! 18
1754  call csv_record_append(csv_record_tmp,agent%thyroid_level ) ! 19
1755  call csv_record_append(csv_record_tmp,agent%adrenaline_level ) ! 20
1756  call csv_record_append(csv_record_tmp,agent%cortisol_level ) ! 21
1757  call csv_record_append(csv_record_tmp,agent%testosterone_level ) ! 22
1758  call csv_record_append(csv_record_tmp,agent%estrogen_level ) ! 23
1759  call csv_record_append(csv_record_tmp,agent%testosterone_baseline) ! 24
1760  call csv_record_append(csv_record_tmp,agent%estrogen_baseline ) ! 25
1761  if ( agent%is_male() ) then ! repr. factor in males and females
1762  call csv_record_append(csv_record_tmp,agent%testosterone_level )
1763  else
1764  call csv_record_append(csv_record_tmp,agent%estrogen_baseline ) ! 26
1765  endif
1766  call csv_record_append(csv_record_tmp, &
1767  agent%probability_reproduction() ) ! 27
1768  call csv_record_append(csv_record_tmp,agent%n_reproductions ) ! 28
1769  call csv_record_append(csv_record_tmp,agent%n_offspring ) ! 29
1770  call csv_record_append(csv_record_tmp,agent%age ) ! 30
1771  call csv_record_append(csv_record_tmp,agent%gos_main ) ! 31
1772  call csv_record_append(csv_record_tmp,agent%gos_arousal ) ! 32
1773  call csv_record_append(csv_record_tmp,agent%gos_repeated ) ! 33
1774  call csv_record_append(csv_record_tmp,agent%n_eats_all_indicator ) ! 34
1775  call csv_record_append(csv_record_tmp,agent%n_eaten_indicator ) ! 35
1776  call csv_record_append(csv_record_tmp,agent%mass_eaten_indicator ) ! 36
1777  call csv_record_append(csv_record_tmp, &
1778  agent%memory_stack%get_food_mean_n() ) ! 37
1779  call csv_record_append(csv_record_tmp, &
1780  agent%memory_stack%get_consp_mean_n() ) ! 38
1781  call csv_record_append(csv_record_tmp, &
1782  agent%memory_stack%get_pred_mean() ) ! 39
1783  call csv_record_append(csv_record_tmp,agent%x ) ! 40
1784  call csv_record_append(csv_record_tmp,agent%y ) ! 41
1785  call csv_record_append(csv_record_tmp,agent%depth ) ! 42
1786  if ( agent%is_alive() ) then
1787  habitat_in = global_habitats_available( &
1788  agent%find_environment())%get_label()
1789  else
1790  ! If the agent is dead and nullified, its habitat cannot be
1791  ! determined.
1792  habitat_in = "agent_dead"
1793  end if
1794  call csv_record_append(csv_record_tmp,habitat_in ) ! 43
1795  call csv_record_append(csv_record_tmp,agent%fitness ) ! 44
1796  end associate
1797 
1798  !> - after all data are appended to the record, this record is
1799  !! physically written to the disk using
1800  !! [CSV_RECORD_WRITE()](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_record_write).
1801  !! .
1802  call csv_record_write( csv_record_tmp, handle_csv )
1803  if ( .not. handle_csv%status ) then ! treat possible write error.
1804  if (present(is_success)) is_success = .false.
1805  call csv_close( handle_csv )
1806  return
1807  end if
1808 
1809  end do
1810 
1811  !> - When all the records are saved, the CSV file is closed with
1812  !! [CSV_CLOSE()](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_close).
1813  call csv_close( handle_csv )
1814 
1815  !> - This is finally sent to the logger (if `logging_enabled` is TRUE).
1816  !! .
1817  if (logging_enabled) then
1818  call log_msg (ltag_info // "Individual data saved, population size " // &
1819  tostr(this%population_size) // &
1820  ", number of columns " // tostr(size(columns)) )
1821  if ( .not. handle_csv%status ) call log_msg( ltag_error // &
1822  "File write operation FAILED." )
1823  call log_msg( file_write_timing%log() )
1824  end if
1825 
1826  if (present(is_success)) is_success = handle_csv%status
1827 
1828  !> The CSV output data file can be optionally compressed with the
1829  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
1830  !! to TRUE.
1831  if ( is_zip_outputs ) then
1832  call call_external(command=cmd_zip_output // " " // csv_file_name, &
1833  suppress_output=.true., &
1834  is_background_task=zip_outputs_background )
1835  end if
1836 
1838 
1839  !-----------------------------------------------------------------------------
1840  !> Save the genome data of all agents in this population to a CSV file.
1841  subroutine population_save_data_all_genomes(this, csv_file_name, is_success)
1842  use file_io
1843  use csv_io
1844  class(population), intent(in) :: this
1845  !> @param[in] csv_file_name is the name of the CSV output file.
1846  character(len=*), intent(in) :: csv_file_name
1847  !> @param[out] is_success Flag showing that data save was successful
1848  !! (if TRUE).
1849  logical, optional, intent(out) :: is_success
1850 
1851  ! Local counters.
1852  integer :: i, j, k, l, m
1853 
1854  ! File handle object.
1855  type(file_handle) :: genome_file
1856 
1857  ! **Column names**
1858  ! The column names are built from the components below with the
1859  ! consecutive digits indicating the order of each component.
1860  character(len=*), parameter :: TAG_CRO = "CRO_" ! Chromosome
1861  character(len=*), parameter :: TAG_GAP = "_" ! a single gap
1862  character(len=*), parameter :: TAG_ALE = "_ALE_" ! Allele
1863  character(len=*), parameter :: TAG_ACO = "_AC_" ! Allele component
1864 
1865  ! The length of the digit part of the column name, assumed two for the
1866  ! normal range 1:99
1867  integer, parameter :: DIG_LEN = 2
1868 
1869  ! Column length hard limit
1870  integer, parameter :: COL_LEN = len(tag_cro) + dig_len + &
1871  len(tag_gap) + dig_len + &
1872  len(tag_ale) + dig_len + &
1873  len(tag_aco) + dig_len
1874 
1875  ! Column (variable) headers.
1876  character(len=COL_LEN), allocatable, dimension(:) :: colname
1877 
1878  ! The number of columns in the output data file.
1879  integer :: n_columns
1880 
1881  ! *Record* that keeps the whole row of data. See @ref file_io.
1882  character(len=:), allocatable :: record_csv
1883 
1884  ! Separate variables were necessary to work around ifort 17 compiler bug:
1885  ! it crashed when everything was placed inline into the CSV_RECORD_APPEND():
1886  ! HEDG2_04.f90(45689): internal error: Please visit
1887  ! 'http://www.intel.com/software/products/support' for assistance.
1888  ! record_csv = repeat(" ", len(TOSTR(ALLELERANGE_MAX)) * n_columns +
1889  ! ^
1890  ! [ Aborting due to internal error. ]
1891  integer :: record_csv_max_length
1892  integer :: allele_val_append
1893 
1894  !> ### Implementation notes ###
1895  !> #### Build column names ####
1896  !> First, determine the number of columns in the data file. The number of
1897  !! columns is calculated by unwinding the whole data structure
1898  !! - `chromosome(j,k)%allele(l)%allele_value(m)`
1899  !! .
1900  !! See @ref the_genome for more details on the data structure.
1901  n_columns=0
1902  do j=1, n_chromosomes
1903  do k=1, chromosome_ploidy
1904  do l=1, len_chromosomes(j)
1905  do m=1, additive_comps
1906  n_columns = n_columns + 1
1907  end do
1908  end do
1909  end do
1910  end do
1911 
1912  !> The array of the column names is then allocated to the above number.
1913  allocate(colname(n_columns))
1914 
1915  !> The column names are built again by unwinding the whole genome data
1916  !! structure into a linear sequence. The column names are like this:
1917  !!
1918  !! `CRO_1_1_ALE_01_AC_1, CRO_1_1_ALE_01_AC_2, CRO_1_1_ALE_01_AC_3, ... `
1919  i=1
1920  do j=1, n_chromosomes
1921  do k=1, chromosome_ploidy
1922  do l=1, len_chromosomes(j)
1923  do m=1, additive_comps
1924  ! CRO_1_1_ALE_01_AC_1, CRO_1_1_ALE_01_AC_2, CRO_1_1_ALE_01_AC_3, ...
1925  colname(i) = tag_cro // tostr(j) // &
1926  tag_gap // tostr(k) // &
1927  tag_ale // tostr(l,10) // &
1928  tag_aco // tostr(m)
1929  i=i+1
1930  end do
1931  end do
1932  end do
1933  end do
1934 
1935  !> #### Write data to the file ####
1936  !> First, the file is opened for writing.
1937  call genome_file%open_write( csv_file_name, format_csv )
1938  if ( .not. genome_file%is_success() ) then
1939  if (present(is_success)) is_success = .false.
1940  call genome_file%close()
1941  return
1942  end if
1943 
1944  !> The first record of the data that contains the column names is then
1945  !! "appended" into the complete record and written to the file.
1946  !! The length of this record is calculated based on the length of the
1947  !! columns and their number.
1948  record_csv = repeat( " ", label_length * 2 + 2 * 3 + &
1949  col_len * n_columns + n_columns * 3 )
1950  !! - The first two columns contain the identifiers for each agent:
1951  !! - numeric ID of the agent
1952  !! - test string label ("name") of the agent
1953  !! .
1954  call csv_record_append( record_csv, &
1955  [ character(len=LABEL_LENGTH) :: "ID_NUM", "AGENT_NAME"] )
1956 
1957  !> - The remaining columns contain the chromosome and gene labels
1958  !! (see above);
1959  !! .
1960  call csv_record_append( record_csv, colname )
1961 
1962  !> This first line consisting of column names is then written to the
1963  !! output file.
1964  call genome_file%record_write( record_csv )
1965  if ( .not. genome_file%is_success() ) then
1966  if (present(is_success)) is_success = .false.
1967  call genome_file%close()
1968  return
1969  end if
1970 
1971  !> The maximum length of the data record is calculated as the maximum
1972  !! string length of a single data value multiplied by the number of
1973  !! columns. Because the record also adds separators, the number of columns
1974  !! multiplied by three is added to this value.
1975  record_csv_max_length = label_length * 2 + 2 * 3 + &
1976  len(tostr(allelerange_max)) * n_columns + &
1977  n_columns * 3
1978 
1979  !> Finally, cycle over all individuals in this population and
1980  !! save the genome data. The first two columns are
1981  !! the_genome::individual_genome::person_number and
1982  !! the_genome::individual_genome::genome_label. The other columns
1983  !! "unwind" the genome data structure over the inner loops for
1984  !! chromosomes, homologues, alleles and allele components.
1985  !! - - `chromosome(j,k)%allele(l)%allele_value(m)`
1986  !! .
1987  !! See @ref the_genome for more details on the data structure.
1988  inds: do i = 1, this%population_size
1989 
1990  record_csv = repeat(" ", record_csv_max_length )
1991 
1992  call csv_record_append( record_csv, this%individual(i)%person_number )
1993  call csv_record_append( record_csv, this%individual(i)%genome_label )
1994 
1995  genome: do j=1, n_chromosomes
1996  do k=1, chromosome_ploidy
1997  do l=1, len_chromosomes(j)
1998  do m=1, additive_comps
1999  allele_val_append = this%individual(i)% &
2000  chromosome(j,k)%allele(l)%allele_value(m)
2001  call csv_record_append( record_csv, allele_val_append )
2002  end do
2003  end do
2004  end do
2005  end do genome
2006  call genome_file%record_write( record_csv )
2007  if ( .not. genome_file%is_success() ) then
2008  if (present(is_success)) is_success = .false.
2009  call genome_file%close()
2010  return
2011  end if
2012 
2013  end do inds
2014 
2015  !> Once all individuals are saved, the file is closed.
2016  call genome_file%close()
2017  if ( .not. genome_file%is_success() ) then
2018  if (present(is_success)) is_success = .false.
2019  else
2020  if (present(is_success)) is_success = .true.
2021  end if
2022 
2023  !> The CSV output data file can be optionally compressed with the
2024  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
2025  !! to TRUE.
2026  if ( is_zip_outputs ) then
2027  call call_external(command=cmd_zip_output // " " // csv_file_name, &
2028  suppress_output=.true., &
2029  is_background_task=zip_outputs_background )
2030  end if
2031 
2032  end subroutine population_save_data_all_genomes
2033 
2034  !-----------------------------------------------------------------------------
2035  !> Load the genome data of all agents in this population from a CSV file.
2036  !! Note that the procedure implements several error correcting measures,
2037  !! e.g. checks for minimum number of rows in the file and minimum row length.
2038  !! The input CSV file therefore can include short text notes that are then
2039  !! ignored when reading data.
2040  subroutine population_load_data_all_genomes( this, pop_size, &
2041  pop_number_here, pop_name_here, csv_file_name, missing_random, is_success )
2042 
2043  use csv_io
2044  use base_strings, only : value, parse, split, compact, is_numeric, delall
2045 
2046  class(population), intent(inout) :: this
2047  !> @param pop_size, The size of the population.
2048  integer, intent(in) :: pop_size
2049  !> @param id, Optional numeric population ID.
2050  integer, optional, intent(in) :: pop_number_here
2051  !> @param descriptor, Optional population string descriptor.
2052  character (len=*), optional, intent(in) :: pop_name_here
2053  !> @param[in] csv_file_name is the name of the CSV output file.
2054  character(len=*), intent(in) :: csv_file_name
2055  !> @param[in] missing_random Flag dictating to generate random genomes
2056  !! if the commondata::popsize parameter exceeds the population
2057  !! size in the CSV genome data size. TRUE = generate random
2058  !! genome, FALSE = leave empty data commondata::unknown
2059  logical, optional, intent(in) :: missing_random
2060  !> @param[out] is_success Flag showing that data save was successful
2061  !! (if TRUE).
2062  logical, optional, intent(out) :: is_success
2063 
2064  ! Local copies of optionals
2065  integer :: pop_number_here_loc
2066  character (len=:), allocatable :: pop_name_here_loc
2067  logical :: missing_random_loc
2068 
2069  ! PROCNAME is the procedure name for logging and debugging
2070  character(len=*), parameter :: &
2071  PROCNAME = "(population_load_data_all_genomes)"
2072 
2073  !> - `handle_csv` is the CSV file handle object defining the file name,
2074  !! Fortran unit and error descriptor, see HEDTOOLS manual for details.
2075  type(csv_file) :: handle_csv
2076 
2077  !> - `line_data_buff` is the input data buffer that is being read
2078  !! via the `CSV_IO` procedure `READLINE()`
2079  character(len=:), allocatable :: line_data_buff
2080 
2081  !> - `n_rows_in` is the number of rows in the input CSV file excluding
2082  !! the first row containing the variable names.
2083  integer :: n_rows_in
2084 
2085  !> - `N_ROWS_MIN` is the minimum number of readable rows in the CSV
2086  !! genome data file, if there are less in the file, data are considered
2087  !! invalid.
2088  integer, parameter :: N_ROWS_MIN = 10
2089 
2090  !> - `MIN_FILE_INP_LEN` is the minimum length of the data row to be
2091  !! included in the read genome.
2092  integer, parameter :: MIN_FILE_INP_LEN = label_length + &
2093  n_chromosomes * chromosome_ploidy * 2 * additive_comps * 2
2094 
2095  ! Local logical flags
2096  logical :: success_flag, is_new_population, is_pop_id_reset
2097 
2098  ! Delimiters for data fields:
2099  character, parameter :: TAB =achar(9)
2100  character, parameter :: DQT =achar(34) ! double quote "
2101  character, parameter :: SQT =achar(39) ! single quote '
2102  character(len=*), parameter :: SDELIM = " ," // tab
2103 
2104  ! Length of a single field within record. Must be big enough to fit
2105  ! ID label and all numeric data fields.
2106  integer, parameter :: LEN_CSV_FIELD = label_length * 2
2107  ! Minimum length of a data field.
2108  integer, parameter :: MIN_FIELD = 1
2109 
2110  !> - `line_data_substrings` - an array of row string data values after
2111  !! parding the whole input raw data.
2112  character(len=LEN_CSV_FIELD), dimension(:), allocatable :: &
2113  line_data_substrings
2114 
2115  ! Local counters
2116  integer :: icase, nvars, jfield, ivalue, error_iflag, &
2117  line_data_buff_length, line_data_nflds, n_ignored_lns
2118 
2119  ! Local counters for loops
2120  integer :: i, j, k, l, m
2121 
2122  !> - `matrix_row` -- array representing the genome data for a single row
2123  !! after parsing the input data file row.
2124  integer, allocatable, dimension(:) :: matrix_row
2125 
2126  !> ### Implementation notes ###
2127  !! Genome data are obtained from the CSV data file provided by the
2128  !! `intent(in)` parameter `csv_file_name`.
2129  call log_delimiter(log_level_chapter)
2130  call log_msg( ltag_stage // "Reading genome data from file " // &
2131  csv_file_name // ", file rows less than " // &
2132  tostr(min_file_inp_len) // " ignored." )
2133 
2134  missing_random_loc = .false.
2135  if (present(missing_random)) then
2136  if (missing_random .eqv. .true. ) then
2137  missing_random_loc = .true.
2138  end if
2139  end if
2140 
2141  !> - First, calculate the number of records in the input CSV file and check
2142  !! that this number is greater than the minimum value set by the
2143  !! local parameter `N_ROWS_MIN`. Also check if the file can be read
2144  !! at all. If either of these conditions hold, exit from the procedure
2145  !! with
2146  n_rows_in = csv_file_lines_count( csv_file_name, numeric_only=.false., &
2147  csv_file_status=success_flag) - 1
2148  if ( n_rows_in < n_rows_min .or. (success_flag .eqv. .false.) ) then
2149  call log_write_error("Input CSV file has too few rows: "// &
2150  tostr(n_rows_in) // " or file access error, " // &
2151  " success flag is " // tostr(success_flag) )
2152  call log_msg(ltag_warn // "Check N_ROWS_MIN (=" // tostr(n_rows_min) // &
2153  ") in " // procname )
2154  return
2155  end if
2156  call log_msg( ltag_info // "Genome data " // csv_file_name // " contain " &
2157  // tostr(n_rows_in) // " rows" )
2158  if ( n_rows_in < pop_size ) call log_msg( ltag_warn // &
2159  "Number of rows in genome data " // csv_file_name // "," // &
2160  tostr(n_rows_in) // " is less than input pop_size " // &
2161  tostr(pop_size) )
2162 
2163  !> - Second, open the input data file for reading
2164  !> - Define the file name \%name component of the CSV file handle. The
2165  !! file handle object `handle_csv` is now used as the file identifier.
2166  handle_csv%name = csv_file_name
2167  !> - Open the output file defined by the `handle_csv` handle object for
2168  !! reading.
2169  call csv_open_read( handle_csv )
2170 
2171  !> - Possible error status of the latest file operation is obtained by
2172  !! the \%status component of the file handle. Check if there were
2173  !! any errors opening the file and report in the logger with the
2174  !! error tag.
2175  if ( .not. handle_csv%status ) then
2176  call log_write_error("Opening input CSV file FAILED")
2177  return
2178  end if
2179 
2180  !> - Third, read the variable names line of the CSV file. Note that
2181  !! they are not used here.
2182  if ( readline(handle_csv%unit, line_data_buff, .true.) .eqv. .false. ) then
2183  call log_write_error("Reading input CSV file FAILED")
2184  call csv_close( handle_csv )
2185  return
2186  end if
2187 
2188  !> - **HAVE_POP** check if the population is allocated and the population
2189  !! size is set, if not, allocate and set to commondata::popsize.
2190  have_pop: if (.not. allocated(this%individual)) then
2191  is_new_population = .true.
2192  !> - Check and reset population IDs if present
2193  if (present(pop_number_here)) then
2194  pop_number_here_loc = pop_number_here
2195  else
2196  pop_number_here_loc = rand_i(1,huge(this%pop_number-1))
2197  end if
2198  if (present(pop_name_here)) then
2199  pop_name_here_loc = pop_name_here
2200  else
2201  pop_name_here_loc = tostr(pop_number_here_loc)
2202  end if
2203  !> - Initialise population with the size from input parameter.
2204  call this%init(pop_size,pop_number_here_loc,pop_name_here_loc)
2205  !> - Note that if the population is not allocated, all individuals
2206  !! that are not obtained from the data file are initialised random.
2207  missing_random_loc = .true.
2208  call log_msg( ltag_info // "Initialised population to POPSIZE " // &
2209  tostr(this%population_size) // ", new population: " // &
2210  tostr(is_new_population) )
2211  else
2212  is_new_population = .false.
2213  call log_msg( ltag_info // "Population is already allocated POPSIZE=" &
2214  // tostr(this%population_size) )
2215  !> - Check and reset population IDs if present
2216  is_pop_id_reset = .false.
2217  if (present(pop_number_here)) then
2218  this%pop_number = pop_number_here
2219  is_pop_id_reset = .true.
2220  end if
2221  if (present(pop_name_here)) then
2222  this%pop_name = pop_name_here
2223  is_pop_id_reset = .true.
2224  end if
2225  if (is_pop_id_reset) &
2226  call log_msg( ltag_info // "Reset population identifiers " // &
2227  "from parameters: " // tostr(this%pop_number) // &
2228  ", " // trim(this%pop_name) // ", new population: " // &
2229  tostr(is_new_population) )
2230  end if have_pop
2231 
2232  call log_msg(ltag_info // "Start reading genome data for "// &
2233  tostr(this%pop_number) // ", " // trim(this%pop_name) // &
2234  ", population array size " // tostr(size(this%individual)) )
2235 
2236  !> - **MAIN_READ:** Cycle through the data lines using the non-advancing
2237  !! `READLINE` function and get the numerical genome data.
2238  icase = 0
2239  n_ignored_lns = 0
2240  main_read: do while ( readline(handle_csv%unit, line_data_buff, .true.) &
2241  .and. icase < this%population_size &
2242  .and. icase < pop_size )
2243 
2244  !> - All input file lines that contain data shorter than
2245  !! `MIN_FILE_INP_LEN` characters are ignored.
2246  if ( len_trim(line_data_buff) < min_file_inp_len ) then
2247  n_ignored_lns = n_ignored_lns + 1
2248  call log_dbg( ltag_info // "Ignored line " // tostr(icase) // ": " // &
2249  trim(line_data_buff), procname, modname )
2250  cycle main_read
2251  end if
2252 
2253  icase = icase + 1
2254 
2255  !> - Stripe away single and double quotes if any.
2256  call delall(line_data_buff, sqt)
2257  call delall(line_data_buff, dqt)
2258 
2259  line_data_buff_length = len_trim(line_data_buff)
2260 
2261  !> - An approximate upper bound for the number of data fields in the
2262  !! current record is `len_trim(line_data_buff)/MIN_FIELD`), so we
2263  !! can now allocate the temporary array of substrings. This is
2264  !! also the total number of variables including the first two
2265  !! non-data records.
2266  if ( .not. allocated(line_data_substrings) ) &
2267  allocate( line_data_substrings(line_data_buff_length/min_field) )
2268 
2269  !> - Split the input string buffer `line_data_buff` into an array of
2270  !! individual strings representing each file data field. The number
2271  !! of such non-empty strings (fields) is given by `line_data_nflds`.
2272  call parse(line_data_buff, sdelim, line_data_substrings, line_data_nflds)
2273 
2274  !> - **N_GCOLS:** Check if `line_data_nflds` obtained from CSV data
2275  !! coincides with the model (`this` data structure) as defined by
2276  !! the number of chromosomes, ploidy, chromosome length and N of
2277  !! additive components:
2278  !! - commondata::n_chromosomes,
2279  !! - commondata::chromosome_ploidy
2280  !! - commondata::len_chromosomes
2281  !! - commondata::additive_comps
2282  !! .
2283  nvars = 0
2284  n_gcols: do j=1, n_chromosomes
2285  do k=1, chromosome_ploidy
2286  do l=1, len_chromosomes(j)
2287  do m=1, additive_comps
2288  nvars = nvars + 1
2289  end do
2290  end do
2291  end do
2292  end do n_gcols
2293  if ( line_data_nflds - 2 /= nvars ) then
2294  call log_write_error( ltag_error // "Number of fields in data file " &
2295  // tostr(line_data_nflds) // &
2296  " is unequal to N of model data: " // tostr(nvars) // &
2297  " for case (row) " // tostr(icase) )
2298  if (present(is_success)) is_success = .false.
2299  return
2300  end if
2301 
2302  !> - We have to allocate the array of row values and initialise
2303  !! it for correct processing by the `VALUE` subroutine.
2304  if (.not. allocated(matrix_row)) allocate(matrix_row(line_data_nflds-2))
2305  matrix_row = unknown
2306 
2307  !> - first value from the file row is `ID_NUM`
2308  if (is_numeric(line_data_substrings(1))) then
2309  call value( trim(line_data_substrings(1)), ivalue, error_iflag)
2310  else
2311  call log_write_error("Error reading ID_NUM field, row " // tostr(icase))
2312  ivalue = unknown
2313  end if
2314  this%individual(icase)%person_number = ivalue
2315 
2316  !> - second value from the file row is `AGENT_NAME`.
2317  this%individual(icase)%genome_label = trim(line_data_substrings(2))
2318 
2319  !> - now read a single row of the genome data for this (`icase`)
2320  !! individual agent, note that if any data in the file are
2321  !! abridged, random values are generated within the appropriate
2322  !! genome limits (commondata::allelerange_min,
2323  !! commondata::allelerange_max).
2324  do jfield=3, line_data_nflds
2325  if ( trim(line_data_substrings(jfield))=="" ) then
2326  !matrix_row(jfield-2:line_data_nflds-2) = UNKNOWN
2327  call rand_array( matrix_row(jfield-2:line_data_nflds-2), &
2328  allelerange_min, allelerange_max )
2329  call log_msg( ltag_warn // "Incomplete data in field " // &
2330  tostr(jfield) // " , row " // tostr(icase) // &
2331  "; set random array: " // &
2332  tostr(matrix_row(jfield-2:line_data_nflds-2)) )
2333  cycle
2334  end if
2335  if (is_numeric(line_data_substrings(jfield))) then
2336  call value(trim(line_data_substrings(jfield)), matrix_row(jfield-2),&
2337  error_iflag)
2338  else
2339  !matrix_row(jfield-2) = UNKNOWN
2340  matrix_row(jfield-2) = rand_i(allelerange_min, allelerange_max)
2341  call log_msg( ltag_warn // "Wrong non-numeric data in row " // &
2342  tostr(jfield) // " , row " // tostr(icase) // &
2343  ": set random " // tostr(matrix_row(jfield-2)) )
2344  end if
2345  end do
2346 
2347  !> - Finally, split (parse) the single line array for the `icase`-s
2348  !! individual into the genome data structure.
2349  jfield = 0
2350  genome: do j=1, n_chromosomes
2351  do k=1, chromosome_ploidy
2352  do l=1, len_chromosomes(j)
2353  do m=1, additive_comps
2354  jfield = jfield + 1
2355  this%individual(icase)%chromosome(j,k)%allele(l)%allele_value(m) = matrix_row(jfield)
2356  end do
2357  end do
2358  end do
2359  end do genome
2360  call log_dbg( ltag_info // "Read genome data case " // tostr(icase) )
2361 
2362  deallocate( line_data_substrings )
2363 
2364  end do main_read
2365 
2366  call log_msg( ltag_info // "All " // tostr(icase) // &
2367  " rows read from data file " // handle_csv%name )
2368 
2369  !> - **FILL_EXTRA:** If the number of rows in the data file read is smaller
2370  !! than the population size commondata::popsize, the remaining data may
2371  !! need to be filled with some default values. The flag `missing_random`
2372  !! determines to initialise all the remaining individuals in the
2373  !! population as random.
2374  fill_extra: if ( icase < this%population_size .and. &
2375  (is_new_population .eqv. .false.) ) then
2376 
2377  call log_msg( ltag_info // "Processing extra data" )
2378  if ( missing_random_loc .eqv. .true. ) then
2379  !> Initialise all individuals of the population
2380  do i=icase+1, this%population_size
2381  !> - first, call `init to object individual(i)
2382  call this%individual(i)%init()
2383  !! - second, ! call `set_id(i)` to identify agents
2384  !! within the population.
2385  !! .
2386  call this%individual(i)%set_id(i)
2387  end do
2388  call log_msg( ltag_info // "Initialised random cases from " // &
2389  tostr(icase+1) // " to " // tostr(this%population_size) )
2390  else
2391  call log_msg(ltag_info // "The extra data are left intact" )
2392  end if
2393 
2394  end if fill_extra
2395 
2396  call csv_close( handle_csv )
2397  if (present(is_success)) is_success = .true.
2398  call log_msg( ltag_info // "Read genome data from " // &
2399  trim(handle_csv%name) // " completed, file closed. " // &
2400  tostr(n_ignored_lns) // " rows ignored." )
2401  call log_delimiter(log_level_chapter)
2402 
2403  contains ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2404  !> This subroutine writes error message to the main log file.
2405  subroutine log_write_error(message)
2406  character(len=*), intent(in) :: message
2407 
2408  call log_msg( ltag_error // message // ": " // &
2409  csv_file_name // ", in " // procname )
2410  call log_msg( ltag_error // "Data file " // csv_file_name // &
2411  " cannot be read in " // procname )
2412  if (present(is_success)) is_success = .false.
2413  end subroutine log_write_error
2414 
2415  end subroutine population_load_data_all_genomes
2416 
2417  !-----------------------------------------------------------------------------
2418  !> Save the perceptual and emotional memory stack data of all agents in this
2419  !! population to a CSV file.
2420  subroutine population_save_data_memory(this, csv_file_name, is_success)
2421  use file_io
2422  use csv_io
2423  class(population), intent(in) :: this
2424  !> @param[in] csv_file_name is the name of the CSV output file.
2425  character(len=*), intent(in) :: csv_file_name
2426  !> @param[out] is_success Flag showing that data save was successful
2427  !! (if TRUE).
2428  logical, optional, intent(out) :: is_success
2429 
2430  ! Local counters.
2431  integer :: i, j, agent
2432 
2433  ! File handle object.
2434  type(file_handle) :: memory_file
2435 
2436  ! *Record* that keeps the whole row of data. See @ref file_io.
2437  character(len=:), allocatable :: record_csv
2438 
2439  ! The length of a single record of data.
2440  integer :: record_csv_max_length
2441 
2442  !> ### Implementation details ###
2443  !> #### Notable variables ####
2444  !> - **COLUMNS_PERC** defines the column names for all components of the
2445  !! perceptual memory stack. They must agree with the components of
2446  !! perceptual memory: the_neurobio::memory_perceptual
2447  character(len=LABEL_LENGTH), dimension(*), parameter :: &
2448  COLUMNS_PERC = [ character(len=label_length) :: &
2449  ! Perception memory:
2450  "PERC_LIGHT", & ! 1
2451  "PERC_DEPTH", & ! 2
2452  "PERC_FOOD", & ! 3
2453  "PERC_FOODSIZ", & ! 4
2454  "PERC_FOODIST", & ! 5
2455  "PERC_CONSP", & ! 6
2456  "PERC_PRED", & ! 7
2457  "PERC_STOM", & ! 8
2458  "PERC_BDMASS", & ! 9
2459  "PERC_ENERG", & ! 10
2460  "PERC_REPRFAC" ] ! 11
2461 
2462  !> - **COLUMNS_EMOT** defines the column name for all components of the
2463  !! emotional memory stack. They must agree with the components of
2464  !! emotional memory: the_neurobio::memory_emotional.
2465  ! .
2466  character(len=LABEL_LENGTH), dimension(*), parameter :: &
2467  COLUMNS_EMOT = [ character(len=label_length) :: &
2468  ! Emotional memory:
2469  "MOTIV_HUNGER", & ! 1
2470  "MOTIV_AVOIDACT", & ! 2
2471  "MOTIV_REPROD", & ! 3
2472  ! GOS memory:
2473  "GOS_MAIN", & ! 4
2474  "GOS_AROUSAL", & ! 5
2475  "GOS_REPEATED" ] ! 6
2476 
2477  !> #### Preliminary ####
2478  !> First, the file `csv_file_name` is opened for writing.
2479  call memory_file%open_write( csv_file_name, format_csv )
2480  if ( .not. memory_file%is_success() ) then
2481  if (present(is_success)) is_success = .false.
2482  call memory_file%close()
2483  return
2484  end if
2485 
2486  !> #### Build column names ####
2487  !> The maximum length of the data record is calculated from three components
2488  !! - individual IDs: numeric ID and label;
2489  !! - perceptual memory components from `COLUMNS_PERC` array for
2490  !! commondata::history_size_perception steps;
2491  !! - emotional memory components from `COLUMNS_EMOT` array for
2492  !! commondata::history_size_motivation steps.
2493  !! .
2494  !! Note that, because the record also adds separators, such as comma and
2495  !! possibly double quotes, the number of columns multiplied by three is
2496  !! added to this value.
2497  record_csv_max_length = 50 + &
2498  label_length * 2 + 2 * 6 + &
2499  (label_length * size(columns_perc) + size(columns_perc) * 6) * &
2500  history_size_perception + &
2501  (label_length * size(columns_emot) + size(columns_emot) * 6) * &
2502  history_size_motivation
2503 
2504  !> The first record of the data that contains the column names is now being
2505  !! "appended" into the complete record and written to the file.
2506  !! The maximum length of this record is calculated above.
2507  record_csv = repeat( " ", record_csv_max_length )
2508 
2509  !! The first two columns contain the identifiers for each agent:
2510  !! - numeric ID of the agent
2511  !! - test string label ("name") of the agent
2512  !! .
2513  call csv_record_append( record_csv, &
2514  [ character(len=LABEL_LENGTH) :: "ID_NUM", "AGENT_NAME"] )
2515 
2516  !> The next portion is composed of the perceptual memory columns from
2517  !! `COLUMNS_PERC` array for each of the commondata::history_size_perception
2518  !! steps in the memory.
2519  !! @note Note that the order of data is: (1) perception component,
2520  !! (2) history steps:
2521  !! `PERC_LIGHT01, PERC_LIGHT02, PERC_LIGHT03, ...
2522  !! PERC_DEPTH01, PERC_DEPTH02, PERC_DEPTH03, ...`
2523  do j = 1, size(columns_perc)
2524  do i = 1, history_size_perception
2525  call csv_record_append( record_csv, &
2526  trim(columns_perc(j)) // &
2527  tostr(i, history_size_perception) )
2528  end do
2529  end do
2530 
2531  !> The third portion is composed of the emotional memory columns from
2532  !! `COLUMNS_EMOT` array for each of the commondata::history_size_motivation
2533  !! steps in the memory.
2534  !! @note Note that the order of data is: (1) motivation component,
2535  !! (2) history steps:
2536  !! `MOTIV_HUNGER01, MOTIV_HUNGER02, MOTIV_HUNGER03, ...
2537  !! MOTIV_AVOIDPAS01, MOTIV_AVOIDPAS02, MOTIV_AVOIDPAS03, ...`.
2538  do j = 1, size(columns_emot)
2539  do i = 1, history_size_motivation
2540  call csv_record_append( record_csv, &
2541  trim(columns_emot(j)) // &
2542  tostr(i, history_size_motivation) )
2543  end do
2544  end do
2545 
2546  !> After this step, the first record of column names is ready to be
2547  !! written to the file.
2548  call memory_file%record_write( record_csv )
2549  if ( .not. memory_file%is_success() ) then
2550  if (present(is_success)) is_success = .false.
2551  call memory_file%close()
2552  return
2553  end if
2554 
2555  !> #### Write the numerical data ####
2556  !> The actual data are written in the same order as above, looping over
2557  !! all individual agents in this population.
2558  !!
2559  !! The maximum record length `record_csv_max_length` is here the same as
2560  !! for writing the column headers, it is assumed that any numeric value
2561  !! in the data matrix occupies less than commondata::label_length
2562  !! characters.
2563  !!
2564  !! So, for each agent, the following data are written with full history:
2565  inds: do agent=1, this%population_size
2566  record_csv = repeat(" ", record_csv_max_length )
2567  associate( agent => this%individual(agent) )
2568  !> - ID data: numeric ID and the string label;
2569  call csv_record_append(record_csv,agent%person_number)
2570  call csv_record_append(record_csv,agent%genome_label )
2571  !> - Perceptual memory components;
2572  call csv_record_append(record_csv,agent%memory_stack%memory_light ) !1
2573  call csv_record_append(record_csv,agent%memory_stack%memory_depth ) !2
2574  call csv_record_append(record_csv,agent%memory_stack%memory_food ) !3
2575  call csv_record_append(record_csv,agent%memory_stack%memory_foodsiz) !4
2576  call csv_record_append(record_csv,agent%memory_stack%memory_foodist) !5
2577  call csv_record_append(record_csv,agent%memory_stack%memory_consp ) !6
2578  call csv_record_append(record_csv,agent%memory_stack%memory_pred ) !7
2579  call csv_record_append(record_csv,agent%memory_stack%memory_stom ) !8
2580  call csv_record_append(record_csv,agent%memory_stack%memory_bdmass ) !9
2581  call csv_record_append(record_csv,agent%memory_stack%memory_energ ) !10
2582  call csv_record_append(record_csv,agent%memory_stack%memory_reprfac) !11
2583  !> - Emotional memory components;
2584  call csv_record_append(record_csv, & !1
2585  agent%memory_motivations%hunger)
2586  call csv_record_append(record_csv, & !2
2587  agent%memory_motivations%defence_fear)
2588  call csv_record_append(record_csv, & !3
2589  agent%memory_motivations%reproduction)
2590  !> - GOS memory components, note here that `gos_main` is a text value
2591  !! that is undefined (empty string) at the initialisation stage;
2592  !! .
2593  call csv_record_append(record_csv, & !4
2594  agent%memory_motivations%gos_main)
2595  call csv_record_append(record_csv, & !5
2596  agent%memory_motivations%gos_arousal)
2597  call csv_record_append(record_csv, & !6
2598  agent%memory_motivations%gos_repeated)
2599  end associate
2600 
2601  !> Each complete record is written to the file as it is built.
2602  call memory_file%record_write( record_csv )
2603  if ( .not. memory_file%is_success() ) then
2604  if (present(is_success)) is_success = .false.
2605  call memory_file%close()
2606  return
2607  end if
2608 
2609  end do inds
2610 
2611  !> Once all individuals are saved, the file is closed.
2612  call memory_file%close()
2613  if ( .not. memory_file%is_success() ) then
2614  if (present(is_success)) is_success = .false.
2615  else
2616  if (present(is_success)) is_success = .true.
2617  end if
2618 
2619  !> The CSV output data file can be optionally compressed with the
2620  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
2621  !! to TRUE.
2622  if ( is_zip_outputs ) then
2623  call call_external(command=cmd_zip_output // " " // csv_file_name, &
2624  suppress_output=.true., &
2625  is_background_task=zip_outputs_background )
2626  end if
2627 
2628  end subroutine population_save_data_memory
2629 
2630  !-----------------------------------------------------------------------------
2631  !> Save the latest movement history of all agents.
2632  !! This method makes use of the the_environment::spatial_moving::history
2633  !! structure that saves latest movements of each agent.
2634  subroutine population_save_data_movements(this, csv_file_name, is_success)
2635  use file_io
2636  use csv_io
2637  class(population), intent(in) :: this
2638  !> @param[in] csv_file_name is the name of the CSV output file.
2639  character(len=*), intent(in) :: csv_file_name
2640  !> @param[out] is_success Flag showing that data save was successful
2641  !! (if TRUE).
2642  logical, optional, intent(out) :: is_success
2643 
2644  ! Local counters.
2645  integer :: i, agent
2646 
2647  ! File handle object.
2648  type(file_handle) :: history_file
2649 
2650  ! *Record* that keeps the whole row of data. See @ref file_io.
2651  character(len=:), allocatable :: record_csv
2652 
2653  ! The length of a single record of data.
2654  integer :: record_csv_max_length
2655 
2656  !> ### Implementation notes ###
2657  !> The maximum length of the data record is calculated from three
2658  !! components: (1) commondata::history_size_spatial * 3 columns of X, Y,
2659  !! and depth coordinates plus (2) the same number of separators for these
2660  !! data columns (assuming 3 characters) plus (3) two additional columns
2661  !! that contain the numeric ID of the agent and its string label.
2662  record_csv_max_length = history_size_spatial * label_length * 3 + &
2663  history_size_spatial * 3 * 3 + &
2664  label_length * 2 + 2 * 3
2665 
2666  !> First, the file `csv_file_name` is opened for writing.
2667  call history_file%open_write( csv_file_name, format_csv )
2668  if ( .not. history_file%is_success() ) then
2669  if (present(is_success)) is_success = .false.
2670  call history_file%close()
2671  return
2672  end if
2673 
2674  !> #### Build column names ####
2675  !> The first record of the data that contains the column names is now being
2676  !! built. The maximum length of this record is calculated above.
2677  !! First thing to do is to cleanup the record string.
2678  record_csv = repeat( " ", record_csv_max_length )
2679 
2680  !> After this, the first record is built by appending components.
2681  !!
2682  !! The first two columns contain the identifiers for each agent:
2683  !! - numeric ID of the agent
2684  !! - test string label ("name") of the agent
2685  !! .
2686  call csv_record_append( record_csv, &
2687  [ character(len=LABEL_LENGTH) :: "ID_NUM", "AGENT_NAME"] )
2688 
2689  !> All remaining columns are built in a loop, by triplets: "X", "Y",
2690  !! "Depth" for each step of the history, up to
2691  !! commondata::history_size_spatial triplets.
2692  do i=1, history_size_spatial
2693  call csv_record_append( record_csv, &
2694  "X_" // tostr(i, history_size_spatial), &
2695  "Y_" // tostr(i, history_size_spatial), &
2696  "D_" // tostr(i, history_size_spatial) )
2697  end do
2698 
2699  !> Now the first record of column names is ready to be written to the file.
2700  call history_file%record_write( record_csv )
2701  if ( .not. history_file%is_success() ) then
2702  if (present(is_success)) is_success = .false.
2703  call history_file%close()
2704  return
2705  end if
2706 
2707  !> #### Write the numerical data ####
2708  !> The actual data are written in the same order as above, looping over
2709  !! all individual agents in this population.
2710  inds: do agent=1, this%population_size
2711 
2712  !> - The record string is cleaned;
2713  record_csv = repeat(" ", record_csv_max_length )
2714 
2715  associate( agent => this%individual(agent) )
2716  !> - ID data appended: numeric ID and the string label;
2717  call csv_record_append( record_csv, agent%person_number )
2718  call csv_record_append( record_csv, agent%genome_label )
2719  !> - Movement history triplets (x, y, depth) for all
2720  !! commondata::history_size_spatial are appended to the
2721  !! record string in a loop.
2722  do i = 1, history_size_spatial
2723  call csv_record_append( record_csv, agent%history(i)%x , &
2724  agent%history(i)%y , &
2725  agent%history(i)%depth )
2726  end do
2727  end associate
2728 
2729  !> Each complete record is written to the file as it is built.
2730  call history_file%record_write( record_csv )
2731  if ( .not. history_file%is_success() ) then
2732  if (present(is_success)) is_success = .false.
2733  call history_file%close()
2734  return
2735  end if
2736 
2737  end do inds
2738 
2739  !> Once all individuals are saved, the file is closed.
2740  call history_file%close()
2741  if ( .not. history_file%is_success() ) then
2742  if (present(is_success)) is_success = .false.
2743  else
2744  if (present(is_success)) is_success = .true.
2745  end if
2746 
2747  !> The CSV output data file can be optionally compressed with the
2748  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
2749  !! to TRUE.
2750  if ( is_zip_outputs ) then
2751  call call_external(command=cmd_zip_output // " " // csv_file_name, &
2752  suppress_output=.true., &
2753  is_background_task=zip_outputs_background )
2754  end if
2755 
2756  end subroutine population_save_data_movements
2757 
2758  !-----------------------------------------------------------------------------
2759  !> Save the behaviours history stack the_neurobio::behaviour::history_behave
2760  !! for all agents.
2761  subroutine population_save_data_behaviours(this, csv_file_name, is_success)
2762  use file_io
2763  use csv_io
2764  class(population), intent(in) :: this
2765  !> @param[in] csv_file_name is the name of the CSV output file.
2766  character(len=*), intent(in) :: csv_file_name
2767  !> @param[out] is_success Flag showing that data save was successful
2768  !! (if TRUE).
2769  logical, optional, intent(out) :: is_success
2770 
2771  ! Local counters.
2772  integer :: i, agent
2773 
2774  ! File handle object.
2775  type(file_handle) :: history_file
2776 
2777  ! *Record* that keeps the whole row of data. See @ref file_io.
2778  character(len=:), allocatable :: record_csv
2779 
2780  ! The length of a single record of data.
2781  integer :: record_csv_max_length
2782 
2783  !> ### Implementation notes ###
2784  !> The maximum length of the data record is calculated from three
2785  !! components: (1) commondata::history_size_behaviours labels of behaviours
2786  !! plus (2) the same number of separators for these data columns (assuming
2787  !! 3 characters) plus (3) two additional columns that contain the numeric
2788  !! ID of the agent and its string label.
2789  record_csv_max_length = history_size_behaviours * label_length + &
2790  history_size_behaviours * 3 + &
2791  label_length * 2 + 2 * 3
2792 
2793  !> First, the file `csv_file_name` is opened for writing.
2794  call history_file%open_write( csv_file_name, format_csv )
2795  if ( .not. history_file%is_success() ) then
2796  if (present(is_success)) is_success = .false.
2797  call history_file%close()
2798  return
2799  end if
2800 
2801  !> #### Build column names ####
2802  !> The first record of the data that contains the column names is now being
2803  !! built. The maximum length of this record is calculated above.
2804  !! First thing to do is to cleanup the record string.
2805  record_csv = repeat( " ", record_csv_max_length )
2806 
2807  !> After this, the first record is built by appending components.
2808  !!
2809  !! The first two columns contain the identifiers for each agent:
2810  !! - numeric ID of the agent
2811  !! - test string label ("name") of the agent
2812  !! .
2813  call csv_record_append( record_csv, &
2814  [ character(len=LABEL_LENGTH) :: "ID_NUM", "AGENT_NAME"] )
2815 
2816  !> All remaining columns are built in a loop for each step of the history,
2817  !! up to commondata::history_size_behaviours steps back in history.
2818  do i=1, history_size_behaviours
2819  call csv_record_append( record_csv, &
2820  "BEHAV_" // tostr(i, history_size_behaviours) )
2821  end do
2822 
2823  !> Now the first record of column names is ready to be written to the file.
2824  call history_file%record_write( record_csv )
2825  if ( .not. history_file%is_success() ) then
2826  if (present(is_success)) is_success = .false.
2827  call history_file%close()
2828  return
2829  end if
2830 
2831  !> #### Write the numerical data ####
2832  !> The actual data are written in the same order as above, looping over
2833  !! all individual agents in this population.
2834  inds: do agent=1, this%population_size
2835 
2836  !> - The record string is cleaned;
2837  record_csv = repeat(" ", record_csv_max_length )
2838 
2839  associate( agent => this%individual(agent) )
2840  !> - ID data appended: numeric ID and the string label;
2841  call csv_record_append( record_csv, agent%person_number )
2842  call csv_record_append( record_csv, agent%genome_label )
2843  !> - Behaviour history for all commondata::history_size_behaviours
2844  !! steps is appended to the record string in a loop.
2845  do i = 1, history_size_behaviours
2846  call csv_record_append( record_csv, agent%history_behave(i) )
2847  end do
2848  end associate
2849 
2850  !> Each complete record is written to the file as it is built.
2851  call history_file%record_write( record_csv )
2852  if ( .not. history_file%is_success() ) then
2853  if (present(is_success)) is_success = .false.
2854  call history_file%close()
2855  return
2856  end if
2857 
2858  end do inds
2859 
2860  !> Once all individuals are saved, the file is closed.
2861  call history_file%close()
2862  if ( .not. history_file%is_success() ) then
2863  if (present(is_success)) is_success = .false.
2864  else
2865  if (present(is_success)) is_success = .true.
2866  end if
2867 
2868  !> The CSV output data file can be optionally compressed with the
2869  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
2870  !! to TRUE.
2871  if ( is_zip_outputs ) then
2872  call call_external(command=cmd_zip_output // " " // csv_file_name, &
2873  suppress_output=.true., &
2874  is_background_task=zip_outputs_background )
2875  end if
2876 
2877  end subroutine population_save_data_behaviours
2878 
2879  !-----------------------------------------------------------------------------
2880  !> Calculate fitness for the pre-evolution phase of the genetic algorithm.
2881  !! **Pre-evolution** is based on selection for a simple criterion without
2882  !! explicit reproduction etc. The criterion for selection at this phase
2883  !! is set by the integer the_individual::individual_agent::fitness component.
2884  !! This procedure provides a whole-population wrapper for the
2885  !! the_individual::individual_agent::fitness_calc() function.
2886  !! @warning Note that fitness here is actually an inverse of the fitness: the
2887  !! higher its value, the worse fitting is the agent.
2888  pure subroutine population_preevol_fitness_calc(this)
2889  class(population), intent(inout) :: this
2890 
2891  call this%individual%fitness_calc()
2892 
2893  end subroutine population_preevol_fitness_calc
2894 
2895  !-----------------------------------------------------------------------------
2896  !> Determine the number of parents that have fitness higher than the
2897  !! minimum acceptable value. Also, only alive agents are included into
2898  !! the reproducing number.
2899  !! @note This procedure is used in the fixed explicit fitness genetic
2900  !! algorithm, see the_evolution::generations_loop_ga().
2901  pure function population_ga_reproduce_max (this) result (val_out)
2902  class(population), intent(in) :: this
2903  integer :: val_out
2904 
2905  !> - `MIN_FITNESS` is the normal limit of the fitness value for inclusion
2906  !! into the reproducing elite group. See also
2907  !! the_individual::individual_agent::individual_preevol_fitness_calc().
2908  integer, parameter :: min_fitness = ga_fitness_select
2909 
2910  !> - `MIN_GA_REPRODUCE` is the minimum GA_REPRODUCE, the final value
2911  !! cannot be smaller than. It is set as the minimum proportion
2912  !! commondata::ga_reproduce_min_prop of the commondata::popsize.
2913  !! However, it cannot be smaller than the absolute minimum
2914  !! commondata::ga_reproduce_n_min.
2915  !! .
2916  integer, parameter :: min_ga_reproduce = &
2917  max( ga_reproduce_n_min, nint(ga_reproduce_min_prop*popsize) )
2918 
2919  ! - n_alive is the number of agents alive.
2920  integer :: n_alive
2921 
2922  val_out = within( count( this%individual%fitness < min_fitness &
2923  .and. this%individual%fitness >= 0 ), &
2924  min_ga_reproduce, ga_reproduce_n )
2925 
2926  ! Only alive agents are reproducing, so the output val_out is limited
2927  ! by the number of alive agents.
2928  n_alive = count(this%individual%alive)
2929  if ( val_out > n_alive ) val_out = max( min_ga_reproduce, n_alive )
2930 
2931  end function population_ga_reproduce_max
2932 
2933  !-----------------------------------------------------------------------------
2934  !> This function implements adaptive mutation rate that *increases*
2935  !! as the population size *reduces*.
2936  function population_ga_mutation_rate_adaptive(this, baseline, maxvalue) &
2937  result(mutat_rate_out)
2938  class(population), intent(in) :: this
2939  !> @param[in] baseline baseline mutation rate
2940  real(srp), intent(in) :: baseline
2941  !> @param[in] maxvalue maximum mutation rate
2942  real(srp), optional, intent(in) :: maxvalue
2943  !> @return The adjusted adaptive mutation rate.
2944  real(srp) :: mutat_rate_out
2945 
2946  !> ### Implementation notes ###
2947  !> #### Notable variables and parameters ####
2948  !> - `mutationrate_max` -- the maximum limit to the mutation rate. Can be
2949  !! obtained from the optional parameter `maxvalue`. The function
2950  !! returns its value at the lowest population size.
2951  !! @remark It is actually local copy of optional `maxvalue` parameter.
2952  real(srp) :: mutationrate_max
2953 
2954  !> - `MUTATIONRATE_MAX_DEF` -- the default maximum limit to the mutation
2955  !! rate `mutationrate_max` if `maxvalue` is not provided.
2956  real(srp), parameter :: mutationrate_max_def = 0.4_srp
2957 
2958  real(srp) :: step
2959 
2960  !> - `n_base_point` -- this is the base value for calculation of the
2961  !! adaptive mutation rate. It can be the number of agents alive or
2962  !! the number of agents that have grown.
2963  integer :: n_base_point
2964 
2965  !> - `mutation_grid_abscissa` and `mutation_grid_ordinate` are the arrays
2966  !! that define the interpolation grid for the adaptive mutation rate.
2967  real(srp), dimension(3) :: mutation_grid_abscissa, mutation_grid_ordinate
2968 
2969  !> - `MIN_GROWING` a parameter setting the minimum number of growing
2970  !! agents in the population. If their actual number is smaller,
2971  !! the mutation rate further incremented by a factor set by the
2972  !! parameter
2973  integer, parameter :: min_growing = 4
2974 
2975  !> - `NON_GROW_INCREMENT` is an increment factor for the mutation rate
2976  !! in case the number of growing agents is below the lower limit
2977  !! `MIN_GROWING`.
2978  !! .
2979  real(srp), parameter :: non_grow_increment = 1.3_srp
2980 
2981  if (present(maxvalue)) then
2982  mutationrate_max = maxvalue
2983  else
2984  mutationrate_max = mutationrate_max_def
2985  end if
2986 
2987  !> #### Procedure ####
2988  !> First, calculate the base point `n_base_point`. This value is the
2989  !! base for the calculation of the adaptive mutation rate.
2990  !! - If this number reduces, mutation rate increases up to
2991  !! `mutationrate_max`
2992  !! .
2993  !! The `n_base_point` can be:
2994  !! - Number of agents that are **alive**:
2995  !! n_base_point = count( this%individual%is_alive() )
2996  !! - Number of agents that have **grown**:
2997  !! n_base_point = count( this%individual%get_mass() > &
2998  !! this%individual%get_mass_birth() )
2999  !! .
3000  n_base_point = count( this%individual%is_alive() )
3001  !n_base_point = count( this%individual%get_mass() > &
3002  ! this%individual%get_mass_birth() )
3003 
3004  step = ( mutationrate_max - baseline ) / 4.0_srp
3005 
3006  !> - If the mutation rate is based on the number of alive agents, the grid
3007  !! abscissa `mutation_grid_abscissa` is an array with three
3008  !! elements:
3009  !! - minimum full population size
3010  !! - 1/2 of the full population size commondata::popsize
3011  !! - full commondata::popsize
3012  !! .
3013  mutation_grid_abscissa = [ 0.0_srp, popsize/2.0_srp, real(popsize, srp) ]
3014 
3015  !> - If, on the other hand, adaptive mutation rate is based on the number of
3016  !! agents that have grown, abscissa is set to specific arbitrary numbers
3017  !! that seem more or less optimal for the model performance.
3018  !! .
3019  !mutation_grid_abscissa = [ 0.0_SRP, 50.0_SRP, 100.0_SRP ]
3020 
3021  !> The grid ordinate is defined as
3022  !! - the maximum mutation rate limit `mutationrate_max`
3023  !! - a small middle value that is calculated as 1/4 of the range between
3024  !! the maximum (`MUTATIOlNRATE_MAX`) and the minimum (`baseline`)
3025  !! mutation values; the latter value increments the minimum `baseline`.
3026  !! - the lowest baseline value that is set by the `baseline` dummy
3027  !! parameter.
3028  !! .
3029  mutation_grid_ordinate = [ mutationrate_max, baseline + step, baseline ]
3030 
3031  !> Adaptive mutation rate is calculated based on the DDPINTERPOL()
3032  !! procedure with the grid array set by `mutation_grid_abscissa` and
3033  !! `mutation_grid_ordinate` and the interpolation value set by the
3034  !! number of agents that are the_genome::individual_genome::is_alive()
3035  !! in the population.
3036  !! An example pattern of the adaptive mutation rate function is plotted
3037  !! below. Here @f$ P_{max} @f$ is the maximum mutation rate defined by
3038  !! `mutationrate_max` and @f$ P_{b} @f$ is the baseline (normal, low)
3039  !! mutation rate defined by the `baseline` parameter.
3040  !! @image html img_doxygen_adapt_mutation.svg "Adaptive mutation rate"
3041  !! @image latex img_doxygen_adapt_mutation.eps "Adaptive mutation rate" width=14cm
3042  ! plotting commands:
3043  ! htintrpl.exe [0 500 1000] [0.5 0.275 0.2]
3044  ! htintrpl.exe [0 500 1000] [0.5 0.2 0.1]
3045  mutat_rate_out = within( ddpinterpol( &
3046  mutation_grid_abscissa, &
3047  mutation_grid_ordinate, &
3048  real(n_base_point, srp) ), &
3049  baseline, &
3050  mutationrate_max )
3051 
3052  !> If the number of agents that had grown from their birth mass is less
3053  !! than the minimum number (`MIN_GROWING`), mutation rate is incremented
3054  !! by a fixed factor `NON_GROW_INCREMENT`. However, it is still forced to
3055  !! be within the range `[ baseline, mutationrate_max ]`.
3056  if ( count( this%individual%get_mass() > &
3057  this%individual%get_mass_birth() ) < min_growing ) then
3058  mutat_rate_out = within( mutat_rate_out * non_grow_increment, &
3059  baseline, mutationrate_max )
3060  end if
3061 
3062  !> Interpolation plots can be saved in the @ref intro_debug_mode
3063  !! "debug mode" using this plotting command:
3064  !! commondata::debug_interpolate_plot_save().
3065  !! @warning Involves **huge** number of plots, should normally be
3066  !! disabled.
3067  call debug_interpolate_plot_save( &
3068  grid_xx=mutation_grid_abscissa, grid_yy=mutation_grid_ordinate, &
3069  ipol_value=real(n_base_point, srp), algstr="DDPINTERPOL", &
3070  output_file="plot_debug_adaptive_mutation_rate_" // mmdd // "_g_" &
3071  // tostr(global_generation_number_current) // ps )
3072 
3074 
3075 end module the_population
Calculate an average of an array excluding missing code values.
Definition: m_common.f90:5491
Force a value within the range set by the vmin and vmax dummy parameter values.
Definition: m_common.f90:5350
recursive pure subroutine qsort(A)
qsort and qs_partition_ are the two parts of the recursive sort algorithm qsort is the recursive fron...
Definition: m_env.f90:7014
pure subroutine qs_partition_fitness(A, marker)
partition is a pivot backend for fitness
Definition: m_popul.f90:677
subroutine log_write_error(message)
This subroutine writes error message to the main log file.
Definition: m_popul.f90:2406
COMMONDATA – definitions of global constants and procedures.
Definition: m_common.f90:1497
logical, public, protected is_debug
Sets the model in the debug mode if TRUE. The Debug mode generates huge additional outputs and logs....
Definition: m_common.f90:1981
character(len= *), parameter, private modname
MODNAME always refers to the name of the current module for use by the LOGGER function LOG_DBG....
Definition: m_common.f90:1591
integer, public global_generation_number_current
The current global generation number. This is a global non fixed-parameter variable that is updated i...
Definition: m_common.f90:2063
character(len= *), parameter, public ltag_error
Definition: m_common.f90:1823
logical, parameter, public init_agents_depth_is_fixed
This parameter determines if the agents are initialised at a fixed depth at the initialisation....
Definition: m_common.f90:2139
character(len= *), parameter, public ps
Standard file extension for debug and other PostScript plots.
Definition: m_common.f90:1716
logical, parameter, public init_agents_depth_is_gauss
This parameter determines if the agents are initialised at a fixed depth at the initialisation....
Definition: m_common.f90:2154
elemental real(srp) function cv2variance(cv, mean)
Calculate the variance from the coefficient of variation.
Definition: m_common.f90:6956
integer, parameter, public unknown
Numerical code for invalid or missing integer counts.
Definition: m_common.f90:1704
real(srp), parameter, public init_agents_depth_cv
This parameter sets the Coefficient of Variation for the Gaussian depth initialisation of the agents ...
Definition: m_common.f90:2169
integer, parameter, public label_length
The length of standard character string labels. We use labels for various objects,...
Definition: m_common.f90:1736
integer, parameter, public popsize
Maximum population size.
Definition: m_common.f90:2055
subroutine log_dbg(message_string, procname, modname)
LOG_DBG: debug message to the log. The message goes to the logger only when running in the DEBUG mode...
Definition: m_common.f90:9171
integer, parameter, public predator_risk_group_select_index_partial
Sets the limit for partial indexing and ranking of prey agents in the visual range of the predator....
Definition: m_common.f90:2402
real(srp), parameter, public init_agents_depth
The fixed depth at which the agents are initialised at the start of the simulation....
Definition: m_common.f90:2163
integer, parameter, public history_size_agent_prop
History stack size for the agent's basic properties, such as body length and body mass....
Definition: m_common.f90:3101
logical, parameter, public true
Safety parameter avoid errors in logical values, so we can now refer to standard Fortran ....
Definition: m_common.f90:1632
integer, parameter, public label_cen
Definition: m_common.f90:1743
real(srp), public global_rescale_maximum_motivation
Global maximum sensory information that is updated for the whole population of agents.
Definition: m_common.f90:4888
character(len=:), allocatable, public, protected mmdd
MMDD tag, year, month and day, used in file names and outputs. The value of the tag should be obtaine...
Definition: m_common.f90:2052
real(srp) function std_dev(array_in, missing_code, undef_ret_null)
Calculate standard deviation using trivial formula:
Definition: m_common.f90:6089
subroutine debug_interpolate_plot_save(grid_xx, grid_yy, ipol_value, algstr, output_file, enable_non_debug)
Produce a debug plot of the interpolation data using an external program htinterp from the HEDTOOLS t...
Definition: m_common.f90:8231
integer, parameter, public label_cst
This parameter defines the range of characters that is used for generating random labels,...
Definition: m_common.f90:1743
integer, public global_time_step_model_current
The current global time step of the model. This is a global non fixed-parameter variable that is upda...
Definition: m_common.f90:2095
integer, parameter, public history_size_motivation
Sets the size of the emotional state memory stack.
Definition: m_common.f90:3386
logical, parameter, public false
Definition: m_common.f90:1632
character(len= *), parameter, public ltag_info
Definition: m_common.f90:1821
Definition of high level file objects.
Definition: m_fileio.f90:110
@, public format_csv
Definition: m_fileio.f90:121
An umbrella module that collects all the components of the individual agent.
Definition: m_indiv.f90:16
Define the population of agents object, its properties and functions.
Definition: m_popul.f90:18
subroutine population_lifecycle_step_eatonly_preevol(this)
This procedure performs a single step of the life cycle of the whole population, the agents for the s...
Definition: m_popul.f90:1357
character(len=label_length) function population_get_pop_name(this)
Accessor get-function for the population character label ID.
Definition: m_popul.f90:531
subroutine population_save_data_movements(this, csv_file_name, is_success)
Save the latest movement history of all agents. This method makes use of the the_environment::spatial...
Definition: m_popul.f90:2635
subroutine population_destroy_deallocate_objects(this)
Destroys this population and deallocates the array of individual member objects.
Definition: m_popul.f90:392
integer function population_get_popsize(this)
Accessor get-function for the size of this population.
Definition: m_popul.f90:511
elemental subroutine sort_population_by_fitness(this)
This subroutine sorts the population individual object by their %fitness components.
Definition: m_popul.f90:638
subroutine population_save_data_all_genomes(this, csv_file_name, is_success)
Save the genome data of all agents in this population to a CSV file.
Definition: m_popul.f90:1842
real(srp) function population_ga_mutation_rate_adaptive(this, baseline, maxvalue)
This function implements adaptive mutation rate that increases as the population size reduces.
Definition: m_popul.f90:2938
integer function get_individual_id(this)
Get integer ID number to individual member of the population object.
Definition: m_popul.f90:206
subroutine population_rwalk3d_all_agents_step(this, dist_array, cv_array, dist_all, cv_all, environment_limits, n_walks)
Perform one or several steps of random walk by all agents.
Definition: m_popul.f90:728
subroutine population_birth_mortality_init(this, energy_mean, energy_sd)
Impose selective mortality at birth on the agents. Selective mortality sets a fixed limit on uncontro...
Definition: m_popul.f90:412
integer function population_get_pop_number(this)
Accessor get-function for the population number ID.
Definition: m_popul.f90:521
subroutine population_rwalk25d_all_agents_step(this, dist_array_xy, cv_array_xy, dist_array_depth, cv_array_depth, dist_all_xy, cv_all_xy, dist_all_depth, cv_all_depth, environment_limits, n_walks)
Perform one or several steps of random walk by all agents.
Definition: m_popul.f90:844
pure subroutine population_preevol_fitness_calc(this)
Calculate fitness for the pre-evolution phase of the genetic algorithm. Pre-evolution is based on sel...
Definition: m_popul.f90:2889
pure integer function population_ga_reproduce_max(this)
Determine the number of parents that have fitness higher than the minimum acceptable value....
Definition: m_popul.f90:2902
subroutine position_individuals_uniform(this, environ)
Position each member of the population randomly within a bounding environment.
Definition: m_popul.f90:597
subroutine population_subject_individual_risk_mortality(this)
Subject all members of this population to their individual mortality risks.
Definition: m_popul.f90:1139
subroutine population_subject_predator_attack(this, this_predator, time_step_model)
Subject the population to an attack by a specific predator. The predator acts on agents in its proxim...
Definition: m_popul.f90:1010
subroutine population_save_data_behaviours(this, csv_file_name, is_success)
Save the behaviours history stack the_neurobio::behaviour::history_behave for all agents.
Definition: m_popul.f90:2762
integer, public global_ind_n_eaten_by_predators
Global indicator variable that keeps the number of agents that have died as a consequence of predator...
Definition: m_popul.f90:183
subroutine population_save_data_all_agents_csv(this, csv_file_name, save_header, is_logging, is_success)
Save data for all agents within the population into a CSV file.
Definition: m_popul.f90:1556
subroutine population_subject_other_risks(this)
Subject the population to mortality caused by habitat-specific mortality risk. Each agent is affected...
Definition: m_popul.f90:1103
subroutine set_individual_id(this, idnumber)
Set integer ID number to individual member of the population object.
Definition: m_popul.f90:192
subroutine individ_posit_in_environ_uniform(this, environ)
Places the individual agent, a member of the population, within a specific environment at random with...
Definition: m_popul.f90:224
subroutine population_lifecycle_step_preevol(this)
This procedure performs a single step of the life cycle of the whole population, the agents for the s...
Definition: m_popul.f90:1159
subroutine population_save_data_memory(this, csv_file_name, is_success)
Save the perceptual and emotional memory stack data of all agents in this population to a CSV file.
Definition: m_popul.f90:2421
subroutine init_population_random(this, pop_size, pop_number_here, pop_name_here)
Initialise the population object.
Definition: m_popul.f90:320
character(len= *), parameter, private modname
Definition: m_popul.f90:25
subroutine genome_individual_set_dead_non_pure(this, non_debug_log)
Set the individual to be dead. Note that this function does not deallocate the individual agent objec...
Definition: m_popul.f90:262
subroutine sex_initialise_from_genome(this)
Determine the sex for each member of the population.
Definition: m_popul.f90:580
subroutine reset_population_id_random(this, pop_number_here, pop_name_here)
Reset individual IDs of the population members.
Definition: m_popul.f90:542
subroutine population_load_data_all_genomes(this, pop_size, pop_number_here, pop_name_here, csv_file_name, missing_random, is_success)
Load the genome data of all agents in this population from a CSV file. Note that the procedure implem...
Definition: m_popul.f90:2042
FILE_HANDLE is the basic file handle object. It provides an unitary object oriented interface for ope...
Definition: m_fileio.f90:148
This type describes parameters of the individual agent.
Definition: m_indiv.f90:37
Definition of individual member of a population.
Definition: m_popul.f90:36
Definition of the population object.
Definition: m_popul.f90:58