The AHA Model  Revision: 12809
Reference implementation 04 (HEDG02_04)
m_env.f90
Go to the documentation of this file.
1 !> @file m_env.f90
2 !! The environmental objects of 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_env.f90 8302 2019-05-16 14:47:53Z sbu062 $
9 !-------------------------------------------------------------------------------
10 
11 !-------------------------------------------------------------------------------
12 !> @brief Definition of environmental objects.
13 !> @section the_environment_module THE_ENVIRONMENT module
14 !> This module defines various environment objects and primitives, starting
15 !! from the basic primitive a spatial object the_environment::spatial. This
16 !! object represents a point in a three-dimensional space. The agent class
17 !! hierarchy starts from this spatial primitive as the agent is a spatial
18 !! object.
20 
21  use commondata
22 
23  implicit none
24 
25  character (len=*), parameter, private :: modname = "(THE_ENVIRONMENT)"
26 
27  !> Default dimensionality of the environment universe.
28  integer, parameter, private :: dimensionality_default = 3
29 
30  !> The number of corners for an environment object in the 2D *X*x*Y* plane
31  !! @warning Valid only in the simplistic box-like implementation of
32  !! environment objects and should be reimplemented if the
33  !! environment is set as an arbitrary polyhedron.
34  integer, parameter :: dim_environ_corners = 4
35 
36  !> Definition of a spatial object. Spatial object determines the position of
37  !! the agent, food items and other things in the simulated space. Here we
38  !! use continuous 3D environment (real type coordinates)
39  !! @note IMPORTANT: We will use **position** method to **set** location of
40  !! a spatial object and **location** method to **get** its location.
41  !! @note We use `position_v` method to **set** define spatial position using
42  !! raw 3D coordinates, x, y, z -- it lacks extensibility -- for
43  !! convenience only.
44  !! @warning Note that we **do not** set **ID** at the elementary `SPATIAL`
45  !! objects. This is done to make type constructor `SPATIAL(x, y, z)`
46  !! (that is used frequently in different places) shorter and easier
47  !! to use. We do not need to include an ID then. IDs are set at
48  !! higher levels in the object hierarchy, e.g. `FOOD_ITEM` has
49  !! `food_iid` integer data component.
50  type, public :: spatial
51  !> We define three-dimensional environment: x, y and depth.
52  real(srp) :: x, y, depth
53  contains
54  !> Create an empty spatial object.
55  !! @note `position` is the standard method for placing spatial object,
56  !! returns object type. `position_v` is non-extensible method
57  !! that returns raw 3D coordinates, for convenience only.
58  !! `position` and `position_v` are overriden in moving
59  !! objects below. Not sure if we really need `position_v`.
60  !! See `the_environment::spatial_create_empty()`
61  procedure, public :: create => spatial_create_empty
62  !> Place spatial object into a 3D space, define the object's current
63  !! coordinates. Object-based procedure.
64  !! See `the_environment::spatial_fix_position_3d_o()`
65  procedure, public :: position => spatial_fix_position_3d_o
66  !> Place spatial object into a 3D space, define the object's current
67  !! coordinates. Vector-based procedure.
68  !! See `the_environment::spatial_fix_position_3d_s()`
69  procedure, public :: position_v => spatial_fix_position_3d_s
70  !> Assign all `MISSING` coordinates to the `SPATIAL` object.
71  !! See `the_environment::spatial_make_missing()`
72  procedure, public :: missing => spatial_make_missing
73  !> Calculate the Euclidean distance between two spatial objects.
74  !! See `the_environment::spatial_distance_3d()`
75  procedure, public :: distance => spatial_distance_3d
76  !> Calculates the minimum distance from a the_environment::spatial class
77  !! object to a line segment delimited by two the_environment::spatial
78  !! endpoints in the 2D *XY* plane (the depth coordinate is ignored).
79  !! See `the_environment::geo_poly2d_dist_point_to_section()`.
80  procedure, public :: distance_segment2d => &
82  !> Calculates the minimum distance from a the_environment::spatial class
83  !! object to a line segment delimited by two the_environment::spatial
84  !! class endpoints in the 3D *XY* space.
85  !! See `the_environment::geo_poly3d_dist_point_to_section()`.
86  procedure, public :: distance_segment => &
88  !> Calculate the Euclidean distance between the current and previous
89  !! position of a single spatial object.
90  !! See `the_environment::spatial_self_distance_3d()`
91  procedure, public :: way => spatial_self_distance_3d
92  !> Function to check if this spatial object is located within an area
93  !! set by an environmental object
94  !! See `the_environment::spatial_check_located_within_3d()`
96  !> Identify in which environment from the input list this spatial
97  !! agent is currently in.
98  !! See `the_environment::spatial_get_environment_in_pos()`.
99  procedure, public :: find_environment => spatial_get_environment_in_pos
100  !> Logical function to check if the argument spatial object(s) is(are)
101  !! located **below** this spatial object.
102  !! See `the_environment::spatial_check_located_below()`
103  procedure, public :: is_below => spatial_check_located_below
104  !> Logical function to check if the argument spatial object(s) is(are)
105  !! located **above** this spatial object.
106  !! See `the_environment::spatial_check_located_above()`
107  procedure, public :: is_above => spatial_check_located_above
108  !> Determine the nearest spatial object to **this** spatial object among
109  !! an array of other spatial objects.
110  !! See `the_environment::spatial_get_nearest_object()`
111  procedure, public :: nearest => spatial_get_nearest_object
112  !> Determine the nearest spatial object to **this** spatial object among
113  !! an array of other spatial objects.
114  !! See `the_environment::spatial_get_nearest_id()`
115  procedure, public :: nearest_num => spatial_get_nearest_id
116  !> Calculate the distances between **this** spatial object and an array of
117  !! its neighbours. Optionally output the distances, sorting index vector
118  !! and rankings vector for each of these neighbours. Optionally do only
119  !! partial indexing, up to the order `rank_max` for computational speed.
120  !! See `the_environment::spatial_neighbours_distances()`
121  procedure, public :: neighbours => spatial_neighbours_distances
122  !> Get the current spatial position of a `SPATIAL` object. Object-based.
123  !! See `the_environment::spatial_get_current_pos_3d_o()`
124  procedure, public :: now_o => spatial_get_current_pos_3d_o
125  !> Get the current spatial position of a `SPATIAL` object. Vector-based.
126  !! See `the_environment::spatial_get_current_pos_3d_v()`
127  procedure, public :: now_v => spatial_get_current_pos_3d_v
128  !> Get the current spatial position of a `SPATIAL` object.
129  !! Generic interface/alias.
130  generic, public :: location => now_o, now_v
131  !> Get the current spatial position of a `SPATIAL` object.
132  !! Generic interface/alias.
133  generic, public :: now => now_o, now_v
134  !> Get the current `X` position of a `SPATIAL` object.
135  !! See `the_environment::spatial_get_current_pos_x_3d()`
136  procedure, public :: xpos => spatial_get_current_pos_x_3d
137  !> Get the current `Y` position of a `SPATIAL` object.
138  !! See `the_environment::spatial_get_current_pos_y_3d()`
139  procedure, public :: ypos => spatial_get_current_pos_y_3d
140  !> Get the current `Z` (depth) position of a `SPATIAL` object.
141  !! See `the_environment::spatial_get_current_pos_d_3d()`
142  procedure, public :: dpos => spatial_get_current_pos_d_3d
143  !> Calculate the illumination (background irradiance) at the depth of the
144  !! spatial object at an arbitrary time step of the model.
145  !! See `the_environment::spatial_calc_irradiance_at_depth()`
146  procedure, public :: illumination => spatial_calc_irradiance_at_depth
147  !> Calculate the visibility range of a spatial object. Wrapper to the
148  !! `visual_range` function. This function calculates the distance from
149  !! which this object can be seen by a visual object (e.g. predator or
150  !! prey).
151  !! See `the_environment::spatial_visibility_visual_range_cm()`.
152  !! @warning The function interface for the basic spatial type
153  !! the_environment::spatial is called `visibility_basic`
154  !! to distinguish it from similar `visibility` methods
155  !! defined for several extension classes, such as
156  !! the_environment::predator, the_environment::food_item and
157  !! the_body::condition because this function is unrelated to
158  !! them, otherwise it must have the same parameters as in
159  !! the class extensions.
160  procedure, public :: visibility => spatial_visibility_visual_range_cm
161  end type spatial
162 
163  !> Definition of a movable spatial object. It extends the
164  !! the_environment::spatial object, but also adds its previous position
165  !! history in stack-like arrays. The history is maintained with the
166  !! commondata::add_to_history() subroutine, adding a single
167  !! element on the top (end) of the history stack.
168  type, public, extends(spatial) :: spatial_moving
169  !> We define prior historical values of the `SPATIAL` positions.
170  !! @note Historical stack has the same the_environment::spatial type but
171  !! is an array of prior values (i.e. array of
172  !! the_environment::spatial objects). The
173  !! the_environment::spatial_moving::position() method overrides
174  !! the standard function defined for the_environment::spatial
175  !! (the_environment::spatial::position()), it not only sets the
176  !! current position, but also moves the previous position of the
177  !! object into the history stack.
178  type(spatial), dimension(HISTORY_SIZE_SPATIAL) :: history
179  contains
180  !> Create a new spatial moving object. Initially it has no position, all
181  !! coordinate values are commondata::missing or commondata::invalid for
182  !! real type coordinates.
183  !! See `the_environment::spatial_moving_create_3d()`
184  procedure, public :: create => spatial_moving_create_3d
185  !> Place spatial movable object into a 3D space, define the object's current
186  !! coordinates, but first save previous coordinates. Object-based.
187  !! See `the_environment::spatial_moving_fix_position_3d_o()`
188  procedure, public :: position => spatial_moving_fix_position_3d_o
189  !> Repeat/re-save the current position into the positional history stack.
190  !! See `the_environment::spatial_moving_repeat_position_history_3d()`.
191  procedure, public :: repeat_position => &
193  !> Place spatial movable object into a 3D space, define the object's current
194  !! coordinates, but first save previous coordinates. Vector-based.
195  !! See `the_environment::spatial_moving_fix_position_3d_v()`
196  procedure, public :: position_v => spatial_moving_fix_position_3d_v
197  !> Create a new empty history of positions for spatial moving object.
198  !! Assign all values to the commondata::missing value code.
199  !! See `the_environment::spatial_moving_clean_hstory_3d()`
200  procedure, public :: spatial_history_clean => spatial_moving_clean_hstory_3d
201  !> Calculate the Euclidean distance between the current and previous
202  !! position of a single spatial movable object. Optionally, it also
203  !! calculates the total distance traversed during the `from_history` points
204  !! from the history stack along with the distance from the current position
205  !! and the last historical value.
206  !! See `the_environment::spatial_moving_self_distance_3d()`.
207  procedure, public :: way => spatial_moving_self_distance_3d
208  !> The spatial moving object **ascends**, goes up the depth with specific
209  !! fixed step size. See `the_environment::spatial_moving_go_up()`.
210  procedure, public :: go_up => spatial_moving_go_up
211  !> The spatial moving object **decends**, goes down the depth with
212  !! specific fixed step size.
213  !! See `the_environment::spatial_moving_go_down()`.
214  procedure, public :: go_down => spatial_moving_go_down
215  !> Implements an optionally environment-restricted Gaussian random
216  !! walk in 3D.
217  !! See `the_environment::spatial_moving_randomwalk_gaussian_step_3d()`.
218  procedure, public :: rwalk3d => spatial_moving_randomwalk_gaussian_step_3d
219  !> Implements an optionally environment-restricted Gaussian random
220  !! walk in a "2.5 dimensions", i.e. 2D x y with separate walk
221  !! parameters for the third depth dimension.
222  !! See `the_environment::spatial_moving_randomwalk_gaussian_step_25d()`.
223  procedure, public :: rwalk25d=>spatial_moving_randomwalk_gaussian_step_25d
224  !> Implements an optionally environment-restricted Gaussian random
225  !! walk. Generic interface for 3D and 3.5D moves.
226  !! See `the_environment::spatial_moving_randomwalk_gaussian_step_3d()`
227  !! and `the_environment::spatial_moving_randomwalk_gaussian_step_25d()`.
228  generic, public :: rwalk => rwalk3d, rwalk25d
229  !> Implements an optionally environment-restricted **correlated
230  !! directional** Gaussian random walk in 3D towards (or away of)
231  !! an the_environment::spatial class `target` object.
232  !! See `the_environment::spatial_moving_corwalk_gaussian_step_3d()`.
233  procedure, public :: corwalk3d => spatial_moving_corwalk_gaussian_step_3d
234  !> Implements an optionally environment-restricted **correlated
235  !! directional** Gaussian random walk in 3D towards (or away of)
236  !! an the_environment::spatial class `target` object.
237  !! See `the_environment::spatial_moving_corwalk_gaussian_step_25d()`.
238  procedure, public :: corwalk25d =>spatial_moving_corwalk_gaussian_step_25d
239  !> Implements an optionally environment-restricted **correlated
240  !! directional** Gaussian random walk. `corwalk` is a generic
241  !! interface for 3D and "2.5"D moves. For details see the 3d version
242  !! and a version with separate *X,Y* and *depth* random parameters.
243  !! - `the_environment::spatial_moving_corwalk_gaussian_step_3d()`;
244  !! - `the_environment::spatial_moving_corwalk_gaussian_step_25d()`;
245  !! .
246  generic, public :: corwalk => corwalk3d, corwalk25d
247  !> Implements an optionally environment-restricted **directional**
248  !! Gaussian random walk in 3D towards a `target` the_environment::spatial
249  !! object.
250  !! See `the_environment::spatial_moving_dirwalk_gaussian_step_3d()`
251  !! @warning obsolete, will be removed!
252  procedure, public :: dirwalk3d => spatial_moving_dirwalk_gaussian_step_3d
253  !> Implements an optionally environment-restricted **directional**
254  !! Gaussian random walk in "2.5"D towards a `target` object. i.e.
255  !! 2D x y with separate walk parameters for the third depth
256  !! dimension.
257  !! See `the_environment::spatial_moving_dirwalk_gaussian_step_25d()`
258  !! @warning obsolete, will be removed!
259  procedure, public :: dirwalk25d=>spatial_moving_dirwalk_gaussian_step_25d
260  !> Implements an optionally environment-restricted **directional**
261  !! Gaussian random walk. Generic interface for 3D and "2.5"D moves.
262  !! @warning obsolete, will be removed!
263  generic, public :: dirwalk => dirwalk3d, dirwalk25d
264  end type spatial_moving
265 
266  !> Definition of the overall **environment**. Environment is a general
267  !! container for all habitats, patches and other similar objects where the
268  !! whole life of the agents takes place. Environment just provides
269  !! the *limits* for all these objects.
270  !! @warning In this version, the environment objects are the most simplistic
271  !! form: 3D "boxes". An arbitrary convex *polyhedron*-based environment
272  !! can be implemented but this requires a more complex computational
273  !! geometry backend.
274  !! @note Coordinate system should **always** use the `SPATIAL` type objects,
275  !! we don't define `_v`-type procedures (it is also unclear are `_v`
276  !! procedures really necessary).
277  type, public :: environment
278  !> Set shape and limits of the whole environment, by default a
279  !! rectangle with Cartesian coordinates based on ENVIRONMENT_WHOLE_SIZE.
280  !! The minimum and maximum coordinates are set through the `SPATIAL`
281  !! object.
282  type(spatial) :: coord_min, coord_max
283  contains
284  !> Create the highest level container environment. Vector-based.
285  !! See `the_environment::environment_whole_build_vector()`
286  procedure, public :: build_vector => environment_whole_build_vector
287  !> Create the highest level container environment. Object-based.
288  !! See `the_environment::environment_whole_build_object()`
289  procedure, public :: build_object => environment_whole_build_object
290  !> Build an **unlimited environment**, with the spatial coordinates limited
291  !! by the maximum machine supported values based on the intrinsic `huge`
292  !! function.
293  !! See `the_environment::environment_build_unlimited()`
294  procedure, public :: build_unlimited => environment_build_unlimited
295  !> Create the highest level container environment. Generic interface.
296  !! See `the_environment::environment_whole_build_vector()`,
297  !! `the_environment::environment_whole_build_object()` and
298  !! `the_environment::environment_build_unlimited()`
299  generic, public :: build => build_vector, build_object, build_unlimited
300  !> Return an environment object that is shrunk by a fixed value in the 2D
301  !! XxY plane.
302  !! See `the_environment::environment_shrink_xy_fixed()`.
303  procedure, public :: shrink2d => environment_shrink_xy_fixed
304  !> Function to get the **minimum** spatial limits (coordinates) of
305  !! the environment.
306  !! See `the_environment::environment_get_minimum_obj()`
307  procedure, public :: lim_min => environment_get_minimum_obj
308  !> Function to get the **maximum** spatial limits (coordinates) of
309  !! the environment.
310  !! See `the_environment::environment_get_maximum_obj()`
311  procedure, public :: lim_max => environment_get_maximum_obj
312  !> Get the **minimum depth** in this environment.
313  !! See `the_environment::environment_get_minimum_depth()`.
314  procedure, public :: depth_min => environment_get_minimum_depth
315  !> Get the **maximum depth** in this environment.
316  !! See `the_environment::environment_get_maximum_depth()`.
317  procedure, public :: depth_max => environment_get_maximum_depth
318  !> Check if a spatial object is actually within this environment.
319  !! See `the_environment::environment_check_located_within_3d()`
321  !> Get the corners of the environment in the 2D X Y plane.
322  !! See `the_environment::environment_get_corners_2dxy()`.
323  procedure, public :: corners2d => environment_get_corners_2dxy
324  !> Get the spatial point position within this environment that is
325  !! nearest to an arbitrary spatial object located outside of the this
326  !! environment. If the spatial object is actually located in this
327  !! environment,return its own spatial position.
328  !! See `the_environment::environment_get_nearest_point_in_outside_obj()`.
329  procedure, public :: nearest_target => &
331  !> Determine the centroid of the environment.
332  !! See `the_environment::environment_centre_coordinates_3d()`
333  procedure, public :: centre => environment_centre_coordinates_3d
334  !> Generate a random spatial object with the uniform distribution within
335  !! (i.e. bound to) **this** environment.
336  !! See `the_environment::environment_random_uniform_spatial_3d()`
337  procedure, public :: uniform_s => environment_random_uniform_spatial_3d
338  !> Generate a random spatial object with the uniform distribution within
339  !! (i.e. bound to) **this** environment, the third depth coordinate is
340  !! fixed.
341  !! See `the_environment::environment_random_uniform_spatial_2d()`
342  procedure, public :: uniform2_s => environment_random_uniform_spatial_2d
343  !> Generate a vector of random spatial objects with the uniform distribution
344  !! within (i.e. bound to) **this** environment. Full 3D procedure.
345  !! See `the_environment::environment_random_uniform_spatial_vec_3d()`
346  procedure, public :: uniform_v =>environment_random_uniform_spatial_vec_3d
347  !> Generate a vector of random spatial objects with the uniform distribution
348  !! within (i.e. bound to) **this** environment. The third, depth coordinate
349  !! is non-stochastic, and provided as an array parameter.
350  !! See `the_environment::environment_random_uniform_spatial_vec_2d()`
351  procedure, public :: uniform2_v=>environment_random_uniform_spatial_vec_2d
352  !> Generate a vector of random spatial objects with the uniform
353  !! distribution within (i.e. bound to) **this** environment. Generic
354  !! interface.
355  generic, public :: uniform => uniform_s,uniform2_s,uniform_v,uniform2_v
356  !> Generates a vector of random spatial object with Gaussian coordinates
357  !! within (i.e. bound to) **this** environment. Full 3D procedure.
358  !! See `the_environment::environment_random_gaussian_spatial_3d()`
359  procedure, public :: gaussian3d => environment_random_gaussian_spatial_3d
360  !> Generates a vector of random spatial object with Gaussian coordinates
361  !! within (i.e. bound to) **this** environment. The depth coordinate is
362  !! set separately and can be non-random (fixed for the whole output array)
363  !! or Gaussian with separate variance.
364  !! See `the_environment::environment_random_gaussian_spatial_2d()`
365  procedure, public :: gaussian2d => environment_random_gaussian_spatial_2d
366  end type environment
367 
368  !> Definition of a single food item. Food item is a spatial object that has
369  !! specific location in space. It can be "created" and eaten ("disappear").
370  !! Food item is an immobile SPATIAL object that has a position in 3D space.
371  type, public, extends(spatial_moving) :: food_item
372  !> Food item has a size (radius) that determines its visibility and
373  !! nutritional value for the predatory agent.
374  real(srp) :: size
375  !> Food item can be present or absent (eaten by the agent, =.TRUE.).
376  logical :: eaten
377  !> Unique ID of this food item. Needed in the resource array.
378  integer :: food_iid
379  contains
380  !> Create a single food item at an undefined position with default size.
381  !! See `the_environment::food_item_create()`
382  procedure, public :: create => food_item_create
383  !> Make a single food item, i.e. place it into a specific position
384  !! in the model environment space and set the size.
385  !! See `the_environment::food_item_make()`
386  procedure, public :: make => food_item_make
387  !> Stochastic outcome of **this** food item capture by an agent.
388  !! Returns TRUE if the food item is captured.
389  !! See `the_environment::food_item_capture_success_stochast()`
390  procedure, public :: capture_success => food_item_capture_success_stochast
391  !> Calculate the probability of capture of **this** food item by a predator
392  !! agent depending on the distance between the agent and this food item.
393  !! See `the_environment::food_item_capture_probability_calc()`
394  procedure, public :: capture_probability => food_item_capture_probability_calc
395  !> Calculate the visibility range of this food item. Wrapper to the
396  !! `visual_range` function. This function calculates the distance from
397  !! which this food item can be seen by a predator (i.e. the default
398  !! predator's visual range).
399  !! See `the_environment::food_item_visibility_visual_range()`
400  procedure, public :: visibility => food_item_visibility_visual_range
401  !> Make the food item "disappear" and take the "eaten" state, i.e.
402  !! impossible for consumption by the agents.
403  !! See `the_environment::food_item_disappear()`
404  procedure, public :: disappear => food_item_disappear
405  !> Logical check-indicator function for the food item being eaten and not
406  !! available.
407  !! See `the_environment::food_item_is_eaten_unavailable()`
408  procedure, public :: is_unavailable => food_item_is_eaten_unavailable
409  !> Logical check-indicator function for the food item being available.
410  !! @returns Logical indicator TRUE if the food item is present
411  !! in the environment and therefore available.
412  !! See `the_environment::food_item_is_available()`
413  procedure, public :: is_available => food_item_is_available
414  !> Get the size component of the food item object.
415  !! See `the_environment::food_item_get_size()`
416  procedure, public :: get_size => food_item_get_size
417  !> Calculate and get the mass of the food item.
418  !! See `the_environment::food_item_get_mass()`
419  procedure, public :: get_mass => food_item_get_mass
420  !> Get the unique id of the food item object.
421  !! See `the_environment::food_item_get_iid()`
422  procedure, public :: get_iid => food_item_get_iid
423  !> Set unique id for the food item object.
424  !! See `the_environment::food_item_set_iid()`
425  procedure, public :: set_iid => food_item_set_iid
426  !> Clone the properties of this food item to another food item.
427  !! See `the_environment::food_item_clone_assign()`
428  procedure, public :: clone => food_item_clone_assign
429  end type food_item
430 
431  !> Definition of the super-type FOOD resource type. This is a superclass,
432  !! several sub-classes can be defined for different kinds of food and prey
433  !! objects.
434  type, public :: food_resource
435  !> Food resource type label
436  character (len=LABEL_LENGTH) :: food_label
437  !> Availability of this kind of food, number of food objects that are
438  !! provided into the environment.
439  integer :: number_food_items
440  !> Food resource consists of an array of `FOOD_ITEM`'s
441  type(food_item), allocatable, dimension(:) :: food
442  contains
443  !> Make food resource object. This class standard constructor.
444  !! See `the_environment::food_resource_make()`
445  procedure, public :: make => food_resource_make
446  !> Replenish and restore food resource: the food resource is restored to
447  !! its initial state as set by the_environment::food_resource::make() or
448  !! to a **smaller** abundance.
449  !! See `the_environment::food_resource_replenish_food_items_all()`
450  procedure, public :: replenish => food_resource_replenish_food_items_all
451  !> Delete and deallocate food resource object. This class destructor.
452  !! See `the_environment::food_resource_destroy_deallocate()`
453  procedure, public :: destroy => food_resource_destroy_deallocate
454  !> Sort the food resource objects within the array by their sizes.
455  !! The two subroutines below are a variant of the recursive quick-sort
456  !! algorithm adapted for sorting real components of the the `FOOD_RESOURCE`
457  !! object.
458  !! See `the_environment::food_resource_sort_by_size()`
459  procedure, public :: sort => food_resource_sort_by_size
460  !> Reset individual iid for the food resource. Individual iids must normally
461  !! coincide with the array order index. If it is changed after sorting,
462  !! iids no longer reflect the correct index. So this subroutine resets iids
463  !! to be coinciding with each food item index.
464  !! See `the_environment::food_resource_reset_iid_all()`
465  procedure, public :: reindex => food_resource_reset_iid_all
466  !> Get the label of the this food resource.
467  !! See `the_environment::food_resource_get_label()`.
468  procedure, public :: get_label => food_resource_get_label
469  !> Get the number of food items in the food resource.
470  !! See `the_environment::food_resource_get_abundance()`.
471  procedure, public :: abundance => food_resource_get_abundance
472  !> Get the location object array (array of the_environment::spatial
473  !! objects) of a food resource object.
474  !! See `the_environment::food_resource_locate_3d()`
475  procedure, public :: location => food_resource_locate_3d
476  !> Calculate the average distance between food items within a resource.
477  !! See `the_environment::food_resource_calc_average_distance_items()`
478  procedure, public :: distance_average => &
480  !> Collapse several food resources into one. The collapsed resource can then
481  !! go into the perception system. The properties of the component resources
482  !! are retained in the collapsed resource.
483  !! See `the_environment::food_resources_collapse()`
484  procedure, public :: join => food_resources_collapse
485  !> Transfer back the resulting food resources into their original objects
486  !! out from a collapsed object from `food_resources_collapse`.
487  !! See `the_environment::food_resources_update_back()`
488  procedure, public :: unjoin => food_resources_update_back
489  !> Implement vertical migration of all the food items in the resource in
490  !! a sinusoidal pattern.
491  !! See `the_environment::food_resource_migrate_move_items()`.
492  procedure, public :: migrate_vertical => food_resource_migrate_move_items
493  !> Perform a random walk step for all food items within the food
494  !! resource with default parameters.
495  !! See `the_environment::food_resource_rwalk_items_default()`.
496  procedure, public :: rwalk => food_resource_rwalk_items_default
497  !> Save characteristics of food items in the resource into a CSV file.
498  !! See `the_environment::food_resource_save_foods_csv()`.
499  procedure, public :: save_csv => food_resource_save_foods_csv
500  end type food_resource
501 
502  !> Definition of the `PREDATOR` objects. **Predator** is a moving agent that
503  !! hunts on the evolving AHA agents but its internal structure is very
504  !! simplistic (although we can in principle doit as a full AHA complexity
505  !! with genome, GOS etc...).
506  type, public, extends(spatial_moving) :: predator
507  !> The label of the predator.
508  character (len=LABEL_LENGTH) :: label
509  !> Individual body size of the predator, can be stochastic or not. Can
510  !! affect attack rate (e.g. larger predators more dangerous).
511  real(srp) :: body_size
512  !> The attack rate of the predator, i.e. the baseline probability of
513  !! attacking catching the prey agent if the latter is found in proximity
514  !! (within the visual range).
515  real(srp) :: attack_rate
516  contains
517  !> Initialise a predator object.
518  !! See `the_environment::predator_make_init()`
519  procedure, public :: make => predator_make_init
520  !> Set label for the predator, if not provided, set it random.
521  !! See `the_environment::predator_label_set()`
522  procedure, public :: label_set => predator_label_set
523  !> Accessor function for the predator body size (length).
524  !! See `the_environment::predator_get_body_size()`
525  procedure, public :: get_size => predator_get_body_size
526  !> Accessor function for the predator attack rate .
527  !! See `the_environment::predator_get_capture_efficiency()`
528  procedure, public :: get_attack_rate => predator_get_attack_rate
529  !> Calculates the risk of capture of the `prey_spatial` idealised spatial
530  !! object with the body length `prey_length`. This is a backend function.
531  !! See `the_environment::predator_capture_risk_calculate_fish()`.
532  procedure, public :: risk_fish => predator_capture_risk_calculate_fish
533  !> Calculates the risk of capture by a specific predator of an
534  !! array of the fish agents with the spatial locations array
535  !! defined by `prey_spatial` and the body length array
536  !! `prey_length`. This subroutine takes account of both the predator
537  !! dilution and confusion effects and risk adjusted by the distance
538  !! towards the predator.
539  !! See `the_environment::predator_capture_risk_calculate_fish_group() `.
540  procedure, public :: risk_fish_group => &
542  !> Calculate the visibility range of this predator. Wrapper to the
543  !! `visual_range` function. This function calculates the distance from
544  !! which this predator can be seen by a visual object (e.g. prey).
545  !! See `the_environment::predator_visibility_visual_range()`.
546  procedure, public :: visibility => predator_visibility_visual_range
547  end type predator
548 
549  !> Definition of the **environment habitat** `HABITAT` object.
550  !! There can potentially be of several types of habitats (patches etc.), so
551  !! the superclass HABITAT defines the most general properties and procedures.
552  !! More specific procedures are defined in sub-objects. Such procedures can
553  !! be overriden from super-object to sub-objects providing for procedure
554  !! polymorphism.
555  type, public, extends(environment) :: habitat
556  !> The name of the habitat
557  character (len=LABEL_LENGTH) :: habitat_name
558  !> Other agent mortality risks
559  real(srp) :: risk_mortality
560  !> Egg mortality risk
561  real(srp) :: risk_egg_mortality
562  !> Number of predators that dwell in the habitat.
563  !... Habitat-specific predation ............................................
564  integer :: predators_number
565  !> Habitat has an array of predators (i.e. `PREDATOR` objects).
566  !! @note The implementation of predators is very simplistic here, just
567  !! a single type of predators integrated into the `HABITAT` object,
568  !! without a separate predator container. This is, for example,
569  !! different from the food resources made as a FOOD_RESOURCE
570  !! container (below) that allows several types of food.
571  !! A more advanced version should implement a specific container
572  !! like `FOOD_RESOURCE` and, ultimately, a full implementation
573  !! of an AHA predator (with the genome, neurobiology etc.). Do we
574  !! need several types of predators or predation bound functions?
575  type(predator), allocatable, dimension(:) :: predators
576  !... Habitat-specific food resources .......................................
577  !> Habitat has a food resource (i.e. FOOD_RESOURCE` object) which is
578  !! an array of `FOOD_ITEM`s.
579  !! @note A container object `FOOD_RESOURCE`is used for the food resource
580  !! rather than just raw number of food items and array of food items
581  !! (as done with predation) to allow implementation of several
582  !! different food resources more easily.
583  type(food_resource) :: food
584  contains
585  !> Make an instance of the habitat object.
586  !! See `the_environment::habitat_make_init()`
587  procedure, public :: make => habitat_make_init
588  !> Return the name (label) of the habitat.
589  !! See `the_environment::habitat_name_get()`.
590  procedure, public :: get_label => habitat_name_get
591  !> Get the mortality risk associated with this habitat.
592  !! See `the_environment::habitat_get_risk_mortality()`.
593  procedure, public :: get_mortality => habitat_get_risk_mortality
594  !> Get the egg mortality risk associated with this habitat.
595  !! See `the_environment::habitat_get_risk_mortality_egg()`.
596  procedure, public :: get_egg_mort => habitat_get_risk_mortality_egg
597  !> Save the predators with their characteristics into a CSV file.
598  !! See `the_environment::habitat_save_predators_csv()`.
599  procedure, public :: save_predators_csv => habitat_save_predators_csv
600 
601  end type habitat
602 
603  !> A list (array) of all the the_environment::habitat objects available
604  !! to the agents. This single array should encompass all the locations that
605  !! the agent can potentially be in (e.g. migrate from one to another).
606  !!
607  !! It is then very important that the separate habitat objects that are
608  !! defined in the model are actually different data entities than the global
609  !! array. If any change is made to the habitat objects after the global
610  !! array was assembled, these must be synchronised with the array and vice
611  !! versa.
612  !!
613  !! To determine where the agent (or any other spatial object) is currently
614  !! located within use the the_environment::spatial::find_environment() method.
615  !! The simplest form of assembling the global array is
616  !! @code
617  !! allocate(Global_Habitats_Available(2))
618  !! Global_Habitats_Available = [habitat_safe, habitat_dangerous]
619  !! @endcode
620  !! A more powerful alternative is using the the_environment::assemble()
621  !! procedure:
622  !! @code
623  !! call assemble(habitat_safe, habitat_dangerous, reindex=.TRUE.)
624  !! @endcode
625  !! See the_environment::assemble() and the_environment::disassemble()
626  !! procedures for more information on creating the global array of habitat
627  !! objects and disassembling individual habitat objects back (updating the
628  !! internal data components and arrays for each of the individual habitats.
629  !!
630  !! Here is an example of the steps necessary to use joined food resource
631  !! from several assembled habitats:
632  !! @code
633  !! ! 1. Assemble the global array of habitat objects
634  !! ! Global_Habitats_Available.
635  !! call assemble( habitat_test1, habitat_test2, &
636  !! habitat_test3, habitat_test4 )
637  !!
638  !! ! 2. Join returns a single food resource object out of those in the
639  !! ! global array Global_Habitats_Available
640  !! joined_food_res2 = join( reindex=.TRUE. )
641  !!
642  !! ! 3. Modify the joined single food resource object in some way.
643  !! ! Here it just resets the sizes of the food items for a part
644  !! ! of the data.
645  !! joined_food_res2%food( 1:size(habitat_test1%food%food) )%size = 100.0
646  !!
647  !! ! 4. Unjoin updates the food resources from the single global object
648  !! ! back to the global array Global_Habitats_Available.
649  !! call unjoin( joined_food_res2, reindex=.TRUE. )
650  !!
651  !! ! 5. To complete unjoin, the updated food habitat and resource data
652  !! ! should be transferred back to the original separate habitat objects
653  !! ! usint `disassemble`
654  !! call disassemble( habitat_test1, habitat_test2, &
655  !! habitat_test3, habitat_test4 )
656  !! @endcode
657  !! @note Determining the environment object the agent is currently in
658  !! can be done by the_environment::spatial::find_environment()
659  !! method in this way:
660  !! @verbatim
661  !! ...
662  !! environment_limits = Global_Habitats_Available( &
663  !! this_agent%find_environment( &
664  !! Global_Habitats_Available) )
665  !! ...
666  !! @endverbatim
667  !! @note Using a list of the_environment::habitat's rather than
668  !! the_environment::environment's because the agent dwells in a
669  !! habitat object and extended properties (e.g. habitat name) are
670  !! easily available in such a case.
671  !! @warning It is not possible to define this global variable in the
672  !! commondata module because all environmental objects are defined
673  !! in a higher level hierarchy module the_environment.
674  !! @warning This array must be initialised immediately after creating the
675  !! environmental objects / habitats:
676  !! the_evolution::init_environment_objects().
677  !! @warning This global array definition cannot be moved to the start of
678  !! the module (for convenience), this results in the "object used
679  !! before it is defined" compiler error.
680  type(habitat), dimension(:), allocatable, public :: global_habitats_available
681 
682  !> Calculate *surface light* intensity (that is subject to diel variation)
683  !! for specific time step of the model. Irradiance can be *stochastic* if
684  !! an optionallogical `stochastic` flag is set to `TRUE`.
685  !! @details Light (`surlig`) is calculated from a sine function. Light
686  !! intensity just beneath the surface is modelled by assuming a
687  !! 50 % loss by scattering at the surface:
688  !! @f[ L_{t} = L_{max} 0.5 sin(\pi dt / \Omega ) . @f]
689  !! **Usage:**
690  !! - deterministic:
691  !! @code
692  !! surface_light(1)
693  !! @endcode
694  !! - stochastic:
695  !! @code
696  !! surface_light(1,YES)
697  !! @endcode
698  !>
699  !! @note Note that it is impossible to do a simple single whole-elemental
700  !! implementation for this function as `random_number` is *never
701  !! pure* but elemental can only work with all pure functions.
702  interface light_surface
703  module procedure light_surface_deterministic
704  module procedure light_surface_stochastic_scalar
705  module procedure light_surface_stochastic_vector
706  end interface light_surface
707 
708  !> Calculate *underwater background irradiance* at specific depth
709  !! @details Underwater light is attenuated following Beer’s law,
710  !! @f[ E_{b}(z,t) = L_{t} e^{-K z} , @f] where @f$ E_{b}(z,t) @f@
711  !! is background irradiance at depth z at time t and K is the
712  !! attenuation coefficient for downwelling irradiance.
713  !! @note The generic interface includes two elemental functions for integer
714  !! and real depth.
715  interface light_depth
716  module procedure light_depth_integer
717  module procedure light_depth_real
718  end interface light_depth
719 
720  !> Calculate visual range of predator using Dag Aksnes's procedures
721  !! `srgetr()`, `easyr()` and `deriv()`.
722  !! @note This is a non-pure/elemental version with **debugging log output**.
723  !! @warning The main interface name is `visual_range()`, it is this name
724  !! which is used throughout the code.
725  !! @note It is possible to use either the "debug" (this) or "fast" (next)
726  !! generic interface for `visual_range()` by tweaking the interface
727  !! name, e.g. to switch to the debug version rename
728  !! `visual_range_debug()` to `visual_range()` and the next version to
729  !! `visual_range_disable()`.
730  !!
731  !! ### Specific implementations ###
732  !! See specific implementations:
733  !! - the_environment::visual_range_scalar() for scalar argument
734  !! - the_environment::visual_range_vector() for vector argument
735  !! - the_environment::visual_range_fast() elemental (parallel-safe) version
736  !! lacking sanity checks and extended debugging.
737  !! .
738  interface visual_range
739  module procedure visual_range_scalar
740  module procedure visual_range_vector
741  end interface visual_range
742 
743  !> Calculate visual range of predator using Dag Aksnes's procedures
744  !! `srgetr()`, `easyr()` and `deriv()`.
745  !! @note This is a pure/elemental version with **no** debugging log output.
746  !! @warning The main interface name is `visual_range`, it is this name
747  !! which is used throughout the code.
748  !! @warning The parameter `prey_contrast` to the **vector**-based function
749  !! call must be an **scalar**. Otherwise a segmentation fault
750  !! runtime error results. Vector-based call is analogous to calling
751  !! `visual_range_vector()` with `prey_contrast_vect` parameter.
753  module procedure visual_range_fast
754  end interface visual_range_new
755 
756  !> Internal distance calculation backend engine.
757  interface dist
758  module procedure dist_scalar
759  module procedure dist_vector_nd
760  end interface dist
761 
762  !> An alias for the the_environment::food_resources_collapse_global_object()
763  !! method for joining food resources into a single global food resource out
764  !! of the global array the_environment::global_habitats_available.
765  !! See the_environment::unjoin() for how to unjoin an array of food resources
766  !! back into an array.
767  interface join
769  end interface join
770 
771  !> An alias to the_environment::food_resources_update_back_global_object()
772  !! method to transfer (having been modified) food resource objects out from
773  !! the single united object back to the global array
774  !! the_environment::global_habitats_available.
775  !! See the_environment::join() for how to join an array of food resources
776  !! into a single global object.
777  !! @warning Note that complete restoring the food resources back to each of
778  !! the individual habitat objects out of the global array must be
779  !! done using the the_environment::disassemble() procedure.
780  interface unjoin
782  end interface
783 
784  !> Interface to the procedure to **assemble** the global array of habitat
785  !! objects the_environment::global_habitats_available from a list of separate
786  !! habitat object components.
787  !! This call
788  !! @code
789  !! assemble(hab_a, hab_b, hab_c)
790  !! @endcode
791  !! is equivalent to
792  !! @code
793  !! Global_Habitats_Available = [ hab_a, hab_b, hab_c ]
794  !! @endcode
795  !! See the_environment::global_habitats_assemble() for the backend
796  !! implementation.
797  interface assemble
798  module procedure global_habitats_assemble
799  end interface
800 
801  !> Interface to the procedure to **disassemble** the global habitats objects
802  !! array the_environment::global_habitats_available back into separate
803  !! habitat object components.
804  !! See the_environment::global_habitats_disassemble() for the backend
805  !! implementation.
806  interface disassemble
807  module procedure global_habitats_disassemble
808  end interface disassemble
809 
810  !> Interface operator to concatenate two arrays of the spatial
811  !! the_environment::spatial or spatial moving the_environment::spatial_moving
812  !! objects.
813  !! @code
814  !! object1%location() .cat. object2%location()
815  !! @endcode
816  !! See the_environment::spatial_stack2arrays() and
817  !! the_environment::spatial_moving_stack2arrays() for backend implementation.
818  !> @warning This operator works with fixed **types** rather than class. All
819  !! input and output parameters are defined as **type**, so this
820  !! is not class-safe.
821  interface operator (.cat.)
822  procedure spatial_stack2arrays
823  procedure spatial_moving_stack2arrays
824  end interface operator (.cat.)
825 
826  !> Interface operator to concatenate the **location** components of two
827  !! arrays ofthe_environment::spatial **class** objects.
828  !! @code
829  !! all_objects%position%( object1 .catloc. object2 )
830  !! @endcode
831  !> @note Unlike the .cat. operator implemented using the
832  !! the_environment::spatial_stack2arrays() and
833  !! the_environment::spatial_moving_stack2arrays() methods, this
834  !! procedure is class-safe and can be used with any class upwards,
835  !! but it concatenates **only** the location data (returns **type**
836  !! the_environment::spatial).
837  interface operator (.catloc.)
838  procedure spatial_class_stack2arrays_locs
839  end interface operator (.catloc.)
840 
841  !> Interface operators `.within.` for testing whether a spatial object (first
842  !! argument lies within an environment (second argument). Usage:
843  !! @code
844  !! if ( object .within. environment ) then
845  !! @endcode
846  !! See `the_environment::spatial_check_located_within_3d()`.
847  interface operator (.within.)
848  procedure spatial_check_located_within_3d
849  end interface operator (.within.)
850 
851  !> Interface operators `.contains.` for testing whether an environment
852  !! object (first argument) contains a `SPATIAL` object (second argument).
853  !! Usage:
854  !! @code
855  !! if ( environment .contains. object ) then
856  !! @endcode
857  !! See `the_environment::environment_check_located_within_3d()`.
858  interface operator (.contains.)
859  procedure environment_check_located_within_3d
860  end interface operator (.contains.)
861 
862  !> Interface operators .above. for spatial objects. Usage:
863  !! @code
864  !! object1 .above. object2
865  !! @endcode
866  !! Tests the condition of `object1` is above `object2`
867  !! The operator can be used in two ways:
868  !! - as an expression, with both scalar and array values:
869  !! @code
870  !! parents%ind(i) .above. parents%ind(i)%perceive_food%foods_seen
871  !! @endcode
872  !! - in if blocks, only **scalars**:
873  !! @code
874  !! if ( parents%ind(i) .above. parents%ind(i)%perceive_food%foods_seen(1) )
875  !! @endcode
876  !! .
877  !! @note Note that the operator `.above.` refers to the "below" procedure
878  !! `the_environment::spatial_check_located_below` as the dummy
879  !! parameters have reverse order in this implementation procedure.
880  interface operator (.above.)
881  procedure spatial_check_located_below
882  end interface operator (.above.)
883 
884  !> Interface operators .below. for spatial objects. Usage:
885  !! @code
886  !! object1 .below. object2
887  !! @endcode
888  !! Tests the condition of `object1` is below `object2`
889  !! The operator can be used in two ways:
890  !! - as an expression, with both scalar and array values:
891  !! @code
892  !! parents%ind(i) .below. parents%ind(i)%perceive_food%foods_seen
893  !! @endcode
894  !! - in if blocks, only **scalars**:
895  !! @code
896  !! if ( parents%ind(i) .below. parents%ind(i)%perceive_food%foods_seen(1) )
897  !! @endcode
898  !! .
899  !! @note Note that the operator `.below.` refers to the "above" procedure
900  !! `the_environment::spatial_check_located_above` as the dummy
901  !! parameters have reverse order in this implementation procedure.
902  interface operator (.below.)
903  procedure spatial_check_located_above
904  end interface operator (.below.)
905 
906  !> Interface operator "-" for the the_environment::environment spatial
907  !! container objects. Return an environment object that is shrunk by a
908  !! fixed value in the 2D XxY plane.
909  !! See `the_environment::environment_shrink_xy_fixed()`.
910  !! The operator can be used as follows:
911  !! @code
912  !! temp_hab = habitat_safe - 0.5_SRP
913  !! @endcode
914  interface operator (-)
915  procedure environment_shrink_xy_fixed
916  end interface operator (-)
917 
918  !-----------------------------------------------------------------------------
919 
920  !> These are public access functions, but probably we don't need to allow
921  !! public access to functions inside generic interfaces
923  !> We do not need specific functions outside of this module, always use
924  !! generic functions.
925  private :: light_surface_deterministic, &
932  srgetr, easyr, deriv
933 
934 contains ! ........ implementation of procedures for this level ................
935 
936  !-----------------------------------------------------------------------------
937  !> Create an empty spatial object. The object's starting coordinates get
938  !! all `MISSING` values.
939  elemental subroutine spatial_create_empty(this)
940  class(spatial), intent(inout) :: this
941 
942  call this%missing()
943 
944  end subroutine spatial_create_empty
945 
946  !-----------------------------------------------------------------------------
947  !> Create the highest level container environment.
948  !! Set the size of the 3D environment container as two coordinate vectors
949  !! setting the minimum and maximum coordinate limits:
950  !! `min_coord(1)` for *x*, `min_coord(2)` for *y*, `min_coord(3)` for *z*
951  !! The size of the environment should be set from parameter vectors
952  !! in `COMMONDATA`.
953  !! @param min_coord Minimum coordinate bound for the environment.
954  !! @param max_coord Maximum coordinate bound for the environment.
955  !! @note This version accepts simple *arrays* as the environment coordinates.
956  !! @warning Not-extensible version. TODO: Do we need it? Deprecate?
957  !! There is a generic function `build` that should normally be used.
958  subroutine environment_whole_build_vector(this, min_coord, max_coord)
959  class(environment), intent(inout) :: this
960  ! Set the size of the 3D environment container as two coordinate vectors
961  ! setting the minimum and maximum coordinate limits:
962  ! `min_coord(1)` for *x*, `min_coord(2)` for *y*, `min_coord(3)` for *z*
963  ! The size of the environment should be set from parameter vectors
964  ! in `COMMONDATA`.
965  real(SRP), dimension(3), intent(in) :: min_coord, max_coord
966 
967  ! Set the environment limits from the parameter vectors.
968  ! @note We use standard type-bound function `position` with
969  ! type constructor for `SPATIAL` here.
970  call this%coord_min%position( spatial( &
971  min_coord(1), min_coord(2), min_coord(3)) )
972  call this%coord_max%position( spatial( &
973  max_coord(1), max_coord(2), max_coord(3)) )
974 
975  end subroutine environment_whole_build_vector
976 
977  !-----------------------------------------------------------------------------
978  !> Create the highest level container environment.
979  !! Set the size of the 3D environment container as two coordinate vectors
980  !! setting the minimum and maximum coordinate limits. The parameters
981  !! `min_coord` and `max_coord` are SPATIAL objects.
982  !! @param min_coord Minimum coordinate bound for the environment,
983  !! `SPATIAL` object.
984  !! @param max_coord Maximum coordinate bound for the environment,
985  !! `SPATIAL` object.
986  !! @note This version accepts `SPATIAL` *objects* as the environment
987  !! coordinates.
988  subroutine environment_whole_build_object(this, min_coord, max_coord)
989  class(environment), intent(inout) :: this
990  ! Set the size of the 3D environment container as two coordinate vectors
991  ! setting the minimum and maximum coordinate limits. The parameters
992  ! `min_coord` and `max_coord` are SPATIAL objects.
993  type(spatial), intent(in) :: min_coord, max_coord
994 
995  ! Set the environment limits from the parameter SPATIAL container objects.
996  this%coord_min = min_coord
997  this%coord_max = max_coord
998 
999  end subroutine environment_whole_build_object
1000 
1001  !-----------------------------------------------------------------------------
1002  !> Build an **unlimited environment**, with the spatial coordinates limited
1003  !! by the maximum machine supported values based on the intrinsic `huge`
1004  !! function.
1006  class(environment), intent(inout) :: this
1007 
1008  ! Local constant parameter setting the largest coordinate possible for the
1009  ! unlimited environment. This is actually the biggest real type constant
1010  ! that can be represented by the machione CPU. Also the min. coordinates.
1011  real(SRP), parameter :: MAX_COORD=huge(0.0)
1012  real(SRP), parameter :: MIN_COORD=-1_srp*huge(0.0)
1013 
1014  call this%build( spatial(min_coord, min_coord, min_coord), &
1015  spatial(max_coord, max_coord, max_coord) )
1016 
1017  end subroutine environment_build_unlimited
1018 
1019  !-----------------------------------------------------------------------------
1020  !> Return an environment object that is shrunk by a fixed value in the 2D
1021  !! XxY plane.
1022  !!
1023  !> Here is an illustration of the function. The outer box is the
1024  !! input environment, the inner box is the shrunken environment that is
1025  !! returned. The shrinkage value is fixed, defined by the second function
1026  !! parameter. The depth is ignored in this function working with the
1027  !! simplistic box-like environment objects.
1028  !> @verbatim
1029  !! min_coord is obtained +---------------------+
1030  !! by coordinate addition; | + |
1031  !! | +-------------+ |
1032  !! | | | |
1033  !! |-->| |<--|
1034  !! | | | |
1035  !! | | | |
1036  !! | +-------------+ |
1037  !! | - | max_coord is obtained by
1038  !! +---------------------+ coordinate subtraction.
1039  !! @endverbatim
1040  !> There is a user defined operator `-` (minus), that can be used as follows:
1041  !! @code
1042  !! temp_hab = habitat_safe - 0.5_SRP
1043  !! @endcode
1044  !! @warning Valid only for the simplistic box-like environments;
1045  !! should be reimplemented if the environment is implemented as an
1046  !! arbitrary polyhedron.
1047  function environment_shrink_xy_fixed(this, shrink_value) result (shrunken)
1048  class(environment), intent(in) :: this
1049  real(srp), intent(in) :: shrink_value
1050  type(environment) :: shrunken
1051 
1052  call shrunken%build( [ this%coord_min%x + shrink_value, &
1053  this%coord_min%y + shrink_value, &
1054  this%coord_min%depth ], &
1055  [ this%coord_max%x - shrink_value, &
1056  this%coord_max%y - shrink_value, &
1057  this%coord_max%depth ] )
1058 
1059  end function environment_shrink_xy_fixed
1060 
1061  !-----------------------------------------------------------------------------
1062  !> Function to get the **minimum** spatial limits (coordinates) of
1063  !! the environment.
1064  !! @returns The minimum spatial bound of the environment, as
1065  !! a `SPATIAL` object.
1066  function environment_get_minimum_obj(this) result (posout)
1067  class(environment), intent(in) :: this
1068 
1069  ! @returns The minimum spatial bound of the environment, as
1070  ! a `SPATIAL` object.
1071  type(spatial) :: posout
1072 
1073  posout = this%coord_min%location()
1074 
1075  end function environment_get_minimum_obj
1076 
1077  !-----------------------------------------------------------------------------
1078  !> Function to get the **maximum** spatial limits (coordinates) of
1079  !! the environment.
1080  !! @returns The maximum spatial bound of the environment, as
1081  !! a `SPATIAL` object.
1082  function environment_get_maximum_obj(this) result (posout)
1083  class(environment), intent(in) :: this
1084 
1085  ! @returns The maximum spatial bound of the environment, as
1086  ! a `SPATIAL` object.
1087  type(spatial) :: posout
1088 
1089  posout = this%coord_max%location()
1090 
1091  end function environment_get_maximum_obj
1092 
1093  !-----------------------------------------------------------------------------
1094  !> Get the **minimum depth** in this environment.
1095  elemental function environment_get_minimum_depth(this) result (mindepth)
1096  class(environment), intent(in) :: this
1097  !> @return The maximum depth of this environment
1098  real(srp) :: mindepth
1099 
1100  mindepth = min( this%coord_min%dpos(), this%coord_max%dpos() )
1101 
1102  end function environment_get_minimum_depth
1103 
1104  !-----------------------------------------------------------------------------
1105  !> Get the **maximum depth** in this environment.
1106  elemental function environment_get_maximum_depth(this) result (maxdepth)
1107  class(environment), intent(in) :: this
1108  !> @return The maximum depth of this environment
1109  real(srp) :: maxdepth
1110 
1111  maxdepth = max( this%coord_min%dpos(), this%coord_max%dpos() )
1112 
1113  end function environment_get_maximum_depth
1114 
1115  !-----------------------------------------------------------------------------
1116  !> Get the corners of the environment in the 2D X Y plane. This is a very
1117  !! simplistic procedure that works only with the box environmental objects.
1118  !! @warning Should be reimplemented if the environment is implemented as an
1119  !! arbitrary polyhedron.
1120  pure function environment_get_corners_2dxy (this, ref_depth, offset) &
1121  result(corners)
1122  class(environment), intent(in) :: this
1123  !> @param[in] ref_depth optional parameter setting the fixed depth for the
1124  !! returned corner objects. If not provided, a fixed default
1125  !! value equal to the local parameter `REF_DEPTH_DEF=0.0` is
1126  !! used.
1127  real(srp), optional, intent(in) :: ref_depth
1128  !> @param[in] offset The offset that can be set to make the borders and
1129  !! corners of the environment inside at a fixed value. If
1130  !! offset is absent, the actual corners of the environment
1131  !! are returned. The offset parameter works as the `shrink2d`
1132  !! function (the_environment::environment::shrink2d()`.
1133  !> @verbatim
1134  !! .---------------------.
1135  !! | + |
1136  !! | *-------------* |
1137  !! | | | |
1138  !! | | |<--| offset shrinks corners
1139  !! | | | |
1140  !! | | | |
1141  !! | *-------------* |
1142  !! | - |
1143  !! .---------------------.
1144  !! @endverbatim
1145  real(srp), optional, intent(in) :: offset
1146  !> @return Returns an array of the environment corners in the
1147  !! 2D *X* x *Y* plane. The number of corners for the environment
1148  !! object in the 2D *X* x *Y* plane is defined by the parameter
1149  !! constant the_environment::dim_environ_corners.
1150  type(spatial), dimension(DIM_ENVIRON_CORNERS) :: corners
1151 
1152  ! Default reference depth, i.e. the depth coordinate of the
1153  ! returned corners objects.
1154  real(srp), parameter :: ref_depth_def = 0.0_srp
1155 
1156  ! Local copies of optionals
1157  real(srp) :: ref_depth_loc, offset_loc
1158 
1159  ! Check optional parameter and est default.
1160  if (present(ref_depth)) then
1161  ref_depth_loc = ref_depth
1162  else
1163  ref_depth_loc = ref_depth_def
1164  end if
1165  if (present(offset)) then
1166  offset_loc = offset
1167  else
1168  offset_loc = 0.0_srp
1169  end if
1170 
1171  !> ### Implementation details ###
1172  !> The corners 1,2,3,4 of the simplistic box-like environmental object are
1173  !! defined as follows:
1174  !! @verbatim
1175  !! (1:minX,minY) ----- (2:maxX,minY)
1176  !! | |
1177  !! (4:minX,maxY) ----- (3:maxX,maxY)
1178  !! @endverbatim
1179  !! These four points are returned as the the_environment::spatial
1180  !! class objects.
1181  !! @warning The order of the points is important because it is also
1182  !! used in the `nearest_target` procedure
1183  !! the_environment::environment_get_nearest_point_in_outside_obj().
1184  corners(1) = spatial( this%coord_min%x + offset_loc, &
1185  this%coord_min%y + offset_loc, ref_depth_loc)
1186 
1187  corners(2) = spatial( this%coord_max%x - offset_loc, &
1188  this%coord_min%y + offset_loc, ref_depth_loc)
1189 
1190  corners(3) = spatial( this%coord_max%x - offset_loc, &
1191  this%coord_max%y - offset_loc, ref_depth_loc)
1192 
1193  corners(4) = spatial( this%coord_min%x + offset_loc, &
1194  this%coord_max%y - offset_loc, ref_depth_loc)
1195 
1196  end function environment_get_corners_2dxy
1197 
1198  !-----------------------------------------------------------------------------
1199  !> Check if a spatial object is actually within this environment.
1200  !! @returns TRUE if the spatial object is located within the environment.
1201  !! @param check_object A spatial object (`SPATIAL` or any **extension**)
1202  !! to check.
1203  !> There is a user-defined operator:
1204  !! @code
1205  !! if ( environment .contains. object ) then
1206  !! @endcode
1207  elemental function environment_check_located_within_3d(this, check_object) &
1208  result(is_within)
1209  class(environment), intent(in) :: this
1210 
1211  ! @returns TRUE if the spatial object is located within the environment.
1212  logical :: is_within
1213 
1214  ! @param check_object A spatial object (`SPATIAL` or any **extension**)
1215  ! to check.
1216  class(spatial), intent(in) :: check_object
1217 
1218  ! Here we just compare the 3D coordinates of the two objects.
1219  ! @note Check if we can use extensible within object specific functions
1220  ! instead of raw object components for 3D `SPATIAL` environment.
1221  if (check_object%x >= this%coord_min%x .and. &
1222  check_object%y >= this%coord_min%y .and. &
1223  check_object%depth >= this%coord_min%depth .and. &
1224  check_object%x <= this%coord_max%x .and. &
1225  check_object%y <= this%coord_max%y .and. &
1226  check_object%depth <= this%coord_max%depth ) then
1227  is_within = .true.
1228  else
1229  is_within = .false.
1230  end if
1231 
1233 
1234  !-----------------------------------------------------------------------------
1235  !> Generate a random spatial object with the uniform distribution within
1236  !! (i.e. bound to) **this** environment.
1237  !! @returns uniformly distributed random `SPATIAL` object bound to
1238  !! `this` environment.
1239  function environment_random_uniform_spatial_3d(this) result (uniform)
1240  class(environment), intent(in) :: this
1241 
1242  ! @returns uniformly distributed random `SPATIAL` object bound to
1243  ! `this` environment.
1244  type(spatial) :: uniform
1245 
1246  ! @note Use type constructor here, but can use position_v type bound
1247  ! function instead. Didn't actually use it as try to survive
1248  ! without such `_v` non-object functions. If succeed, remove all
1249  ! of them.
1250  uniform = spatial( rand(this%coord_min%x, this%coord_max%x), &
1251  rand(this%coord_min%y, this%coord_max%y), &
1252  rand(this%coord_min%depth, this%coord_max%depth) )
1253 
1255 
1256  !-----------------------------------------------------------------------------
1257  !> Generate a random spatial object with the uniform distribution within
1258  !! (i.e. bound to) **this** environment, the third depth coordinate is fixed.
1259  !! @returns uniformly distributed random `SPATIAL` object bound to
1260  !! `this` environment.
1261  function environment_random_uniform_spatial_2d(this, fixdepth) result(uniform)
1262  class(environment), intent(in) :: this
1263 
1264  real(srp), intent(in) :: fixdepth
1265 
1266  ! @returns uniformly distributed random `SPATIAL` object bound to
1267  ! `this` environment.
1268  type(spatial) :: uniform
1269 
1270  ! @note Use type constructor here, but can use position_v type bound
1271  ! function instead. Didn't actually use it as try to survive
1272  ! without such `_v` non-object functions. If succeed, remove all
1273  ! of them.
1274  uniform = spatial( rand(this%coord_min%x, this%coord_max%x), &
1275  rand(this%coord_min%y, this%coord_max%y), &
1276  fixdepth )
1277 
1279 
1280  !-----------------------------------------------------------------------------
1281  !> Generate a vector of random spatial objects with the uniform distribution
1282  !! within (i.e. bound to) **this** environment.
1283  !! @param the dimension(size) of the vector to generate
1284  !! @returns uniformly distributed random `SPATIAL` object bound to
1285  !! `this` environment.
1286  !! @warning **Intel Fortran porting note**. This whole array function **does
1287  !! not work under Intel Fortran 13**, issues *stack overflow*
1288  !! runtime error, although compiles without issues:
1289  !! `loc_food_here = this%uniform(this%food%number_food_items)`.
1290  function environment_random_uniform_spatial_vec_3d(this, num) result (uniform)
1291  class(environment), intent(in) :: this
1292 
1293  ! @param the dimension(size) of the vector to generate
1294  integer, intent(in) :: num
1295 
1296  ! @returns uniformly distributed random `SPATIAL` object bound to
1297  ! `this` environment.
1298  type(spatial), dimension(num) :: uniform
1299 
1300  ! Local counter.
1301  integer :: i
1302 
1303  ! We call the type bound `uniform_s=>environment_random_uniform_spatial_3d`
1304  ! to get a vector of uniformly-distributed spatial objects.
1305  do i=1, num
1306  ! @warning **Intel Fortran porting note**. Setting objects directly
1307  ! without using type-bound function:
1308  ! `uniform(i) = this%uniform_s()`
1309  ! does **not** avoid intel fortran stack overflow runtime
1310  ! error. Still **have** to use scalar rather than vector
1311  ! function `uniform` for scattering lots of objects.
1312  !uniform(i) = SPATIAL( RAND(this%coord_min%x, this%coord_max%x), &
1313  ! RAND(this%coord_min%y, this%coord_max%y), &
1314  ! RAND(this%coord_min%depth, this%coord_max%depth) )
1315  ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1316  ! @note Intel Fortran 17 should now work well, restored call to
1317  ! the type-bound function.
1318  uniform(i) = this%uniform_s()
1319 
1320  end do
1321 
1323 
1324  !-----------------------------------------------------------------------------
1325  !> Generate a vector of random spatial objects with the uniform distribution
1326  !! within (i.e. bound to) **this** environment. The third, depth coordinate
1327  !! is non-stochastic, and provided as an array parameter.
1328  !! @param the dimension(size) of the vector to generate
1329  !! @returns uniformly distributed random `SPATIAL` object bound to
1330  !! `this` environment.
1331  function environment_random_uniform_spatial_vec_2d(this, fixdep_array) &
1332  result(uniform)
1333  class(environment), intent(in) :: this
1334 
1335  ! @param the dimension(size) of the vector to generate
1336  real(srp), dimension(:), intent(in) :: fixdep_array
1337 
1338  ! @returns uniformly distributed random `SPATIAL` object bound to
1339  ! `this` environment.
1340  type(spatial), dimension(size(fixdep_array)) :: uniform
1341 
1342  ! Local counter.
1343  integer :: i
1344 
1345  !> ### Implementation details ###
1346  !> We call the type bound `uniform_s=>environment_random_uniform_spatial_3d`
1347  !! to get a vector of uniformly-distributed spatial objects.
1348  do i=1, size(fixdep_array)
1349  ! @warning **Intel Fortran porting note**. Setting objects directly
1350  ! without using type-bound function:
1351  ! `uniform(i) = this%uniform_s()`
1352  ! does **not** avoid intel fortran stack overflow runtime
1353  ! error. Still **have** to use scalar rather than vector
1354  ! function `uniform` for scattering lots of objects.
1355  uniform(i) = spatial( rand(this%coord_min%x, this%coord_max%x), &
1356  rand(this%coord_min%y, this%coord_max%y), &
1357  fixdep_array(i) )
1358  end do
1359 
1361 
1362  !-----------------------------------------------------------------------------
1363  !> Generates a vector of random spatial object with Gaussian coordinates
1364  !! within (i.e. bound to) **this** environment.
1365  !! @param num the dimension(size) of the vector to generate
1366  !! @param centroid Optional centroid of the Gaussian scatter. If not
1367  !! provided, will select random uniformly distributed point.
1368  !! @param variance Gaussian variance parameter.
1369  !! @returns Gaussian ranrom SPATIAL object bound to `this` environment.
1371  num, centroid, variance) result (gaussian)
1372  class(environment), intent(in) :: this
1373 
1374  ! @param num the dimension(size) of the vector to generate
1375  integer, intent(in) :: num
1376 
1377  ! @param centroid Optional centroid of the Gaussian scatter. If not
1378  ! provided, will select random uniformly distributed point.
1379  class(spatial), optional, intent(in) :: centroid
1380 
1381  ! @param variance Gaussian variance parameter.
1382  real(srp), optional, intent(in) :: variance
1383 
1384  ! @returns Gaussian ranrom SPATIAL object bound to `this` environment.
1385  type(spatial), dimension(num) :: gaussian
1386 
1387  ! Local copy of the centroid
1388  type(spatial) :: centroid_here
1389 
1390  ! Local random coordinates
1391  real(srp) :: x_coord, y_coord, d_coord, variance_here
1392 
1393  ! Local counter
1394  integer :: i
1395 
1396  !> ### Implementation details ###
1397  !> First, check optional centroid and set local centroid.
1398  if (present(centroid)) then
1399  if ( this%is_within(centroid) ) then
1400  !> The centroid that is provided is accepted only if it is within
1401  !! **this** environment.
1402  call centroid_here%position( centroid%location() )
1403  else
1404  !> If a centroid is provided but is outside of the environment,
1405  !! reset it to random uniform.
1406  call centroid_here%position( this%uniform() )
1407  end if
1408  else
1409  call centroid_here%position( this%uniform() )
1410  end if
1411 
1412  ! Check if variance parameter is provided.
1413  if (present(variance)) then
1414  variance_here = variance
1415  else
1416  variance_here = 1.0_srp
1417  end if
1418 
1419  !> Now, generate Gaussian spatial objects and fill the output array.
1420  do i=1, num
1421  !> First, generate random Gaussian coordinanes for a temporary spatial
1422  !! object.
1423  x_coord = rnorm( centroid_here%x, variance_here )
1424  y_coord = rnorm( centroid_here%y, variance_here )
1425  d_coord = rnorm( centroid_here%depth, variance_here )
1426  !> Make sure this spatial object is within the bounding environment.
1427  do while ( this%is_within( spatial(x_coord, y_coord, d_coord) ) )
1428  x_coord = rnorm( centroid_here%x, variance_here )
1429  y_coord = rnorm( centroid_here%y, variance_here )
1430  d_coord = rnorm( centroid_here%depth, variance_here )
1431  end do
1432  !> Finally, set assign the output array component to this object.
1433  gaussian(i) = spatial(x_coord, y_coord, d_coord)
1434  end do
1435 
1437 
1438  !-----------------------------------------------------------------------------
1439  !> Generates a vector of random spatial object with Gaussian coordinates
1440  !! within (i.e. bound to) **this** environment. The depth coordinate is
1441  !! set separately and can be non-random (fixed for the whole output array)
1442  !! or Gaussian with separate variance.
1443  !! @param num the dimension(size) of the vector to generate
1444  !! @param centroid Optional centroid of the Gaussian scatter. If not
1445  !! provided, will select random uniformly distributed point.
1446  !! @param fixdepth Optional fixed depth for the generated Gaussian objects.
1447  !! @param variance Gaussian variance parameter.
1448  !! @param variance_depth Gaussian variance parameter for the fixed depth.
1449  !! @returns Gaussian random SPATIAL object bound to `this` environment.
1451  num, centroid, fixdepth, variance, variance_depth) &
1452  result(gaussian)
1453  class(environment), intent(in) :: this
1454 
1455  ! @param num the dimension(size) of the vector to generate
1456  integer, intent(in) :: num
1457 
1458  ! @param centroid Optional centroid of the Gaussian scatter. If not
1459  ! provided, will select random uniformly distributed point.
1460  class(spatial), optional, intent(in) :: centroid
1461 
1462  ! @param fixdepth Optional fixed depth for the generated Gaussian objects.
1463  real(srp), optional, intent(in) :: fixdepth
1464 
1465  ! @param variance Gaussian variance parameter.
1466  real(srp), optional, intent(in) :: variance
1467 
1468  ! @param variance_depth Gaussian variance parameter for the fixed depth.
1469  real(srp), optional, intent(in) :: variance_depth
1470 
1471  ! @returns Gaussian random SPATIAL object bound to `this` environment.
1472  type(spatial), dimension(num) :: gaussian
1473 
1474  ! Local copy of the centroid
1475  type(spatial) :: centroid_here
1476 
1477  ! Local random coordinates
1478  real(srp) :: x_coord, y_coord, d_coord, variance_here
1479 
1480  ! Local counter
1481  integer :: i
1482 
1483  !> ### Implementation details ###
1484  !> First, check optional centroid parameter and set local centroid.
1485  check_centroid: if (present(centroid)) then
1486  check_depth1: if (present(fixdepth)) then
1487  !> Make sure fixdepth is within the allowed environmental range.
1488  if ( fixdepth >= this%coord_min%dpos() .and. &
1489  fixdepth <= this%coord_max%dpos() ) then
1490  !> If fixed depth is provided, we use the centroid but take the depth
1491  !! from the `fixdepth` parameter.
1492  call centroid_here%position( &
1493  spatial( centroid%xpos(), centroid%ypos(), fixdepth ) )
1494  !> And check if the resulting centroid is within this environment.
1495  !! If not, make it random uniform making use of the fixed depth.
1496  if (.not. this%is_within( centroid_here ) ) then
1497  centroid_here = centroid_urandom( fixdepth )
1498  end if
1499  else
1500  !> If the fixdepth is not conformant with this environment, use
1501  !! centroid provided and discard the fixdepth parameter
1502  call centroid_here%position( centroid%location() )
1503  !> And check if the resulting centroid is within this environment,
1504  !! if not, discard both the centroid and the fixed depth parameters
1505  !! and use random uniform centroid as the last resort.
1506  if (.not. this%is_within( centroid_here ) ) then
1507  centroid_here = centroid_urandom( )
1508  end if
1509  end if
1510  else check_depth1
1511  !> If fixed depth is not provided, however, we use the fixed depth
1512  !! from the centroid
1513  if ( this%is_within(centroid) ) then
1514  !> The centroid that is provided is accepted only if it is within
1515  !! **this** environment.
1516  call centroid_here%position( centroid%location() )
1517  else
1518  !> If a centroid is provided but is outside of the environment,
1519  !! reset it to random uniform.
1520  centroid_here = centroid_urandom( centroid%dpos() )
1521  !call centroid_here%position( this%uniform( centroid%dpos() ) )
1522  ! But make sure the random centroid is still within this environment.
1523  !do while ( this%is_within(centroid_here) )
1524  ! call centroid_here%position( this%uniform( centroid%dpos() ) )
1525  !end do
1526  end if
1527  end if check_depth1
1528  else check_centroid
1529  check_depth2: if (present(fixdepth)) then
1530  if ( fixdepth >= this%coord_min%dpos() .and. &
1531  fixdepth <= this%coord_max%dpos() ) then
1532  !> Check if it's conformant with the environment. Use if if okay.
1533  call centroid_here%position( this%uniform( fixdepth ) )
1534  else
1535  !> If the depth provided is not conformant, discard and use random.
1536  call centroid_here%position( this%uniform( ) )
1537  end if
1538  else check_depth2
1539  !> Finally, if neither centroid nor depth is provided, use random
1540  !! uniform.
1541  call centroid_here%position( this%uniform( ) )
1542  end if check_depth2
1543  end if check_centroid
1544 
1545  ! Check if variance parameter is provided.
1546  if (present(variance)) then
1547  variance_here = variance
1548  else
1549  variance_here = 1.0_srp
1550  end if
1551 
1552  !> Check if separate variance parameter for the depth is provided. This
1553  !! sets if **stochastic depth** is to be generated.
1554  depth_stochastic: if (present(variance_depth)) then
1555  !> If separate depth variance parameter is provided, depth is stochastic
1556  !! Gaussian. Generate Gaussian spatial objects and fill the output array.
1557  do i=1, num
1558  !> First, generate random Gaussian coordinanes for a temporary spatial
1559  !! object.
1560  x_coord = rnorm( centroid_here%x, variance_here )
1561  y_coord = rnorm( centroid_here%y, variance_here )
1562  d_coord = rnorm( centroid_here%depth, variance_depth )
1563  ! And make sure this spatial object is within the bounding environment.
1564  do while ( .not. this%is_within( spatial(x_coord, y_coord, d_coord) ) )
1565  x_coord = rnorm( centroid_here%x, variance_here )
1566  y_coord = rnorm( centroid_here%y, variance_here )
1567  d_coord = rnorm( centroid_here%depth, variance_depth )
1568  end do
1569  !> Finally, set assign the output array component to this object.
1570  gaussian(i) = spatial(x_coord, y_coord, d_coord)
1571  end do
1572  else depth_stochastic
1573  !> If there is no separate variance parameter for the depth,
1574  !! **depth fixed** deterministic, identical in for the whole array.
1575  !! Set the fixed depth.
1576  d_coord = centroid_here%dpos()
1577  !> Now, generate Gaussian spatial objects and fill the output array.
1578  do i=1, num
1579  !> First, generate random Gaussian coordinanes for a temporary spatial
1580  !! object.
1581  x_coord = rnorm( centroid_here%x, variance_here )
1582  y_coord = rnorm( centroid_here%y, variance_here )
1583  ! And make sure this spatial object is within the bounding environment.
1584  do while ( .not. this%is_within( spatial(x_coord, y_coord, d_coord) ) )
1585  x_coord = rnorm( centroid_here%x, variance_here )
1586  y_coord = rnorm( centroid_here%y, variance_here )
1587  end do
1588  !> Finally, set assign the output array component to this object.
1589  gaussian(i) = spatial(x_coord, y_coord, d_coord)
1590  end do
1591  end if depth_stochastic
1592 
1593  contains
1594  !> Make a random centroid with fixed depth bound within **this**
1595  !! environment.
1596  function centroid_urandom( fixed_depth ) result ( centroid_out )
1597  type(spatial) :: centroid_out
1598  real(srp), optional :: fixed_depth
1599 
1600  if (present(fixed_depth)) then
1601  ! Make a random uniform centroid.
1602  centroid_out = this%uniform( fixed_depth )
1603  ! Making sure it is within the bounding environment.
1604  do while ( .not. this%is_within(centroid_out) )
1605  centroid_out = this%uniform( fixed_depth )
1606  end do
1607  else
1608  ! Make a random uniform centroid.
1609  centroid_out = this%uniform( )
1610  ! Making sure it is within the bounding environment.
1611  do while ( .not. this%is_within(centroid_out) )
1612  centroid_out = this%uniform( )
1613  end do
1614  end if
1615 
1616  end function centroid_urandom
1617 
1619 
1620  !-----------------------------------------------------------------------------
1621  !> Get the spatial point position within this environment that is nearest to
1622  !! an arbitrary spatial object located outside of the this environment. If
1623  !! the spatial object is actually located in this environment, return its own
1624  !! position.
1625  !! @note This function is necessary for the implementation of migration
1626  !! behaviour across two or several environments or habitats: it
1627  !! allows to set the (nearest) target point in the desired target
1628  !! environment.
1629  !! @warning Valid only for the simplistic box-like environments;
1630  !! should be reimplemented if the environment is implemented as an
1631  !! arbitrary polyhedron.
1632  subroutine environment_get_nearest_point_in_outside_obj(this, outside_object,&
1633  offset_into, point_spatial, point_dist)
1634  class(environment), intent(in) :: this
1635  !> @param[in] outside_object is the outside object, the minimum distances
1636  !! to the environment and the closest point are evaluated for
1637  !! this object.
1638  class(spatial), intent(in) :: outside_object
1639  !> @param[in] offset_into optional offset guaranteeing that the nearest
1640  !! point is located more or less deeply within the this target
1641  !! environment rather than just at the border of the
1642  !! environment. This parameter is useful if the nearest point
1643  !! in the environment actually sets the target point, to which
1644  !! the agent represented by the `outside_object` spatial is
1645  !! relocating. In such a case, the agent is therefore moving
1646  !! into the environment, at a distance `offset_into` rather
1647  !! than just to the edge of this target environment.
1648  !! This is illustrated by the below:
1649  !> @verbatim
1650  !! .----------------------. this environment
1651  !! | + |
1652  !! | *--------------* |
1653  !! | | target point | | outside_object
1654  !! | | is inside (*)<------------<*=><
1655  !! | | | |
1656  !! | | >|---|<-------- offset_into
1657  !! | *--------------* |
1658  !! | - |
1659  !! .------------------ ---.
1660  !! @endverbatim
1661  real(SRP), optional, intent(in) :: offset_into
1662 
1663  !> @return The point within the this environment that is the nearest to the
1664  !! `outside_object` the_environment::spatial class target object.
1665  type(spatial), optional, intent(out) :: point_spatial
1666 
1667  real(SRP), optional, intent(out) :: point_dist
1668 
1669  ! Local counter
1670  integer :: i
1671 
1672  ! Local variable representing the four corners of the this environment
1673  type(spatial), dimension(DIM_ENVIRON_CORNERS) :: corners
1674 
1675  ! Arrays of the nearest objects between the outside point and all the
1676  ! outer segments of the this environment.
1677  type(spatial), dimension(DIM_ENVIRON_CORNERS) :: segment_nearest_obj
1678  real(SRP), dimension(DIM_ENVIRON_CORNERS) :: segment_distance
1679 
1680  !> ### Implementation notes ###
1681  !> First, check if the outside object is actually located within the target
1682  !! environment.
1683  !! - If so, the distance between the object and the environment
1684  !! is zero. This is a degenerate case that is treated separately: the
1685  !! nearest point within the environment coincides with the location of
1686  !! the outside object itself.
1687  !! .
1688  if ( this%is_within(outside_object) ) then
1689  if (present(point_spatial)) &
1690  call point_spatial%position( outside_object%location() )
1691  if (present(point_dist)) point_dist = 0.0_srp
1692  return
1693  end if
1694 
1695  !> If the object is indeed outside, determine the four corners of the
1696  !! `this` environment. These corners are delimiting the outside of the
1697  !! this environment. The reference depth is set to the depth of the
1698  !! outside object.
1699  if (present(offset_into)) then
1700  !> @nolte Note that if the `offset_into` offset parameter is set, the
1701  !! corners are adjusted to this offset value, so that they are
1702  !! actually inside the this environment object.
1703  corners = this%corners2d( ref_depth = outside_object%depth, &
1704  offset=offset_into )
1705  else
1706  corners = this%corners2d( ref_depth = outside_object%depth )
1707  end if
1708 
1709  !> Calculate the distances and the nearest points between the
1710  !! outside object and the four outer segments (1,2), (2,3), (3,4), (4,1).
1711  !! @warning This fast but **unsafe** implementation requires
1712  !! **the same order of points** to be set also in the
1713  !! the_environment::environment_get_corners_2dxy().
1714  !! For arbitrary order of points, a full cross loop is needed,
1715  !! with the sizes of `the segment_nearest_obj` `segment_distance`
1716  !! arrays set to the square
1717  !! `DIM_ENVIRON_CORNERS * DIM_ENVIRON_CORNERS`.
1718  !! @verbatim
1719  !! 1, 2 1------------2
1720  !! 2, 3 | |
1721  !! 3, 4 | |
1722  !! 4, 1 4------------3
1723  !! @endverbatim
1724  do i=1, size(corners)-1
1725  call outside_object%distance_segment2d( &
1726  sectp1=corners( i ), &
1727  sectp2=corners( i+1 ), &
1728  min_dist=segment_distance( i ), &
1729  point_segment=segment_nearest_obj( i ))
1730  end do
1731  call outside_object%distance_segment2d( &
1732  sectp1=corners( size(corners) ), &
1733  sectp2=corners( 1 ), &
1734  min_dist=segment_distance( size(corners) ), &
1735  point_segment=segment_nearest_obj( size(corners) ))
1736 
1737 
1738  !> Finally, the returned nearest point is the point where the distance
1739  !! between the outside point and any of the outer segments of the
1740  !! environment reaches the minimum value.
1741  if (present(point_spatial)) &
1742  point_spatial = segment_nearest_obj( minloc(segment_distance,1) )
1743 
1744  if (present(point_dist)) point_dist = minval(segment_distance)
1745 
1747 
1748  !-----------------------------------------------------------------------------
1749  !> Place spatial object into a 3D space, define the object's current
1750  !! coordinates
1751  !! @param coordinates The spatial objects will now get these coordinates.
1752  !! @note This is actually the **constructor** for the `SPATIAL` object
1753  !! giving it its value and existence.
1754  !! @note This version takes *scalar coordinates* as the argument.
1755  !! @warning This implementation is **not extensible**.
1756  subroutine spatial_fix_position_3d_s(this, x, y, depth)
1757  class(spatial), intent(inout) :: this
1758 
1759  ! @param coordinates The spatial objects will now get these coordinates.
1760  real(SRP), intent(in) :: x,y,depth
1761 
1762  this%x = x
1763  this%y = y
1764  this%depth = depth
1765 
1766  end subroutine spatial_fix_position_3d_s
1767 
1768  !-----------------------------------------------------------------------------
1769  !> Place spatial object into a 3D space, define the object's current
1770  !! coordinates.
1771  !! @param location SPATIAL location that will be assigned to the current
1772  !! spatial object.
1773  !! @note This is actually the **constructor** for the `SPATIAL` object giving
1774  !! it its value and existence.
1775  !! @note This version takes a `SPATIAL` object as argument.
1776  elemental subroutine spatial_fix_position_3d_o(this, location)
1777  class(spatial), intent(inout) :: this
1778 
1779  ! @param location SPATIAL location that will be assigned to the current
1780  ! spatial object.
1781  type(spatial), intent(in) :: location
1782 
1783  this%x = location%x
1784  this%y = location%y
1785  this%depth = location%depth
1786 
1787  end subroutine spatial_fix_position_3d_o
1788 
1789  !-----------------------------------------------------------------------------
1790  !> Assign all commondata::missing` coordinates to the_environment::spatial
1791  !! object.
1792  elemental subroutine spatial_make_missing(this)
1793  class(spatial), intent(inout) :: this
1794 
1795  ! Use standard object function to set it missing, use type constructor
1796  ! to set specific values.
1797  call this%position( spatial(missing,missing,missing) )
1798 
1799  end subroutine spatial_make_missing
1800 
1801  !-----------------------------------------------------------------------------
1802  !> Get the current spatial position of a `SPATIAL` object.
1803  !! @return Current spatial coordinates as a `SPATIAL` object.
1804  !! @note This function returns **SPATIAL** type object (3D coordinates).
1805  !! @note This is a standard extensible version.
1806  elemental function spatial_get_current_pos_3d_o(this) result(coordinates)
1807  class(spatial), intent(in) :: this
1808  ! @return Current spatial coordinates as a `SPATIAL` object.
1809  type(spatial) :: coordinates
1810 
1811  coordinates%x = this%x
1812  coordinates%y = this%y
1813  coordinates%depth = this%depth
1814 
1815  end function spatial_get_current_pos_3d_o
1816 
1817  !-----------------------------------------------------------------------------
1818  !> Get the current spatial position of a `SPATIAL` object.
1819  !! @param vector flag indicating if we return a 3D vector rather than
1820  !! `SPATIAL` type object. **Note:** We need this logical
1821  !! parameter to avoid ambiguity in calling the generic
1822  !! function: `Error: 'spatial_get_current_pos_3d' and
1823  !! 'spatial_get_current_pos_3d_v' for GENERIC 'now' at (1) are
1824  !! ambiguous`. The parameter itself is **not used**.
1825  !! So, for vector-output must always be TRUE.
1826  !! @return Current spatial coordinates as a 3-dimensional array.
1827  !! @note This function returns **array** of 3D coordinates.
1828  !! @warning This function is **non extensible**.
1829  !! @note The *vector form* of the `location` function is particularly
1830  !! convenient for data output into the `LOGGER` module that does
1831  !! not accept object data, but does accept simple array data, e.g.:
1832  !! @code
1833  !! call LOG_DBG ("location=" // &
1834  !! TOSTR(proto_parents%individual(ind)%location(.TRUE.)))
1835  !! @endcode
1836  pure function spatial_get_current_pos_3d_v(this, vector) result(coordinates)
1837  class(spatial), intent(in) :: this
1838  ! @param vector flag indicating if we return a 3D vector rather than
1839  !! `SPATIAL` type object.
1840  !! @note We need this logical parameter to avoid ambiguity in calling
1841  !! the generic function: `Error: 'spatial_get_current_pos_3d' and
1842  !! 'spatial_get_current_pos_3d_v' for GENERIC 'now' at (1) are
1843  !! ambiguous`. The parameter itself is **not used**.
1844  !! So, for vector-output **must always be TRUE**!
1845  logical, intent(in) :: vector ! Not used in calculations
1846  ! @return Current spatial coordinates as a 3-dimensional array.
1847  real(srp), dimension(DIMENSIONALITY_DEFAULT) :: coordinates
1848 
1849  coordinates(1) = this%x
1850  coordinates(2) = this%y
1851  coordinates(3) = this%depth
1852 
1853  end function spatial_get_current_pos_3d_v
1854 
1855  !-----------------------------------------------------------------------------
1856  !> Get the current `X` position of a `SPATIAL` object.
1857  !! @returns x_pos current X coordinate of the `SPATIAL` object.
1858  !! @note Not sure if really much needed.
1859  elemental function spatial_get_current_pos_x_3d(this) result (x_pos)
1860  class(spatial), intent(in) :: this
1861  ! @returns x_pos current X coordinate of the `SPATIAL` object
1862  real(srp) :: x_pos
1863 
1864  x_pos = this%x
1865 
1866  end function spatial_get_current_pos_x_3d
1867 
1868  !-----------------------------------------------------------------------------
1869  !> Get the current `Y` position of a `SPATIAL` object.
1870  !! @returns x_pos current X coordinate of the `SPATIAL` object.
1871  !! @note Not sure if really much needed.
1872  elemental function spatial_get_current_pos_y_3d(this) result (y_pos)
1873  class(spatial), intent(in) :: this
1874  ! @returns x_pos current X coordinate of the `SPATIAL` object
1875  real(srp) :: y_pos
1876 
1877  y_pos = this%y
1878 
1879  end function spatial_get_current_pos_y_3d
1880 
1881  !-----------------------------------------------------------------------------
1882  !> Get the current `DEPTH` position of a `SPATIAL` object.
1883  !! @returns x_pos current X coordinate of the `SPATIAL` object.
1884  !! @note Not sure if really much needed.
1885  elemental function spatial_get_current_pos_d_3d(this) result (depth_pos)
1886  class(spatial), intent(in) :: this
1887  ! @returns x_pos current X coordinate of the `SPATIAL` object.
1888  real(srp) :: depth_pos
1889 
1890  depth_pos = this%depth
1891 
1892  end function spatial_get_current_pos_d_3d
1893 
1894  !-----------------------------------------------------------------------------
1895  !> Calculate the illumination (background irradiance) at the depth of the
1896  !! spatial object at an arbitrary time step of the model.
1897  !! @warning Cannot implement a generic function accepting also vectors of
1898  !! this objects as only elemental object-bound array functions are
1899  !! allowed by the standard. This function cannot be elemental, so
1900  !! passed-object dummy argument must always be scalar.
1901  function spatial_calc_irradiance_at_depth(this, time_step_model) &
1902  result(irradiance_at_depth)
1903  class(spatial), intent(in) :: this
1904  integer, optional, intent(in) :: time_step_model
1905  real(srp) :: irradiance_at_depth
1906 
1907  ! Local copies of optionals
1908  integer :: time_step_model_here
1909 
1910  !> ### Implementation details ###
1911  !> Check optional time step parameter. If unset, use global
1912  !! `commondata::global_time_step_model_current`.
1913  if (present(time_step_model)) then
1914  time_step_model_here = time_step_model
1915  else
1916  time_step_model_here = global_time_step_model_current
1917  end if
1918 
1919  !> Calculate ambient illumination / irradiance at the depth of
1920  !! this food item at the given time step.
1921  irradiance_at_depth = &
1922  light_depth( depth=this%dpos(), &
1923  surface_light = light_surface( &
1924  tstep=time_step_model_here, &
1925  is_stochastic=daylight_stochastic) )
1926 
1928 
1929  !-----------------------------------------------------------------------------
1930  !> Calculate the visibility range of a spatial object. Wrapper to the
1931  !! the_environment::visual_range() function. This function calculates
1932  !! the distance from which this object can be seen by a visual object
1933  !! (e.g. predator or prey).
1934  !! @warning Cannot implement a generic function accepting also vectors of
1935  !! this objects as only elemental object-bound array functions are
1936  !! allowed by the standard. This function cannot be elemental, so
1937  !! passed-object dummy argument must always be scalar.
1938  function spatial_visibility_visual_range_cm(this, object_area, contrast, &
1939  time_step_model) result (visrange)
1940  class(spatial), intent(in) :: this
1941  !> @param[in] object_area is the **mandatory** area of the spatial object
1942  !! (m).
1943  !! @note object_area has optional attribute here with the base
1944  !! the_environemnt::spatial class object can be only optional because
1945  !! it is optional in all extension classes
1946  !! (the_environment::food_item, the_environment::predator,
1947  !! the_body::condition,the_neurobio::spatialobj_percept_comp).
1948  !! However, it is actually **mandatory** here and not providing
1949  !! object size results in wrong calculations. Such a case is logged
1950  !! with the commondata::ltag_error logger tag.
1951  real(srp), optional, intent(in) :: object_area
1952  !> @param[in] contrast is optional inherent contrast of this spatial
1953  !! object. the default contrast of all objects is defined
1954  !! by the commondata::preycontrast_default parameter.
1955  real(srp), optional, intent(in) :: contrast
1956  !> @param[in] optional time step of the model, if absent gets the current
1957  !! time step as defined by the value of
1958  !! `commondata::global_time_step_model_current`.
1959  integer, optional, intent(in) :: time_step_model
1960  !> @return The maximum distance (m) from which this object can be seen.
1961  real(srp) :: visrange
1962 
1963  ! Local copies of optionals
1964  integer :: time_step_model_here
1965  real(srp) :: object_area_here, contrast_here
1966 
1967  ! Local variables
1968  real(srp) :: irradiance_agent_depth
1969 
1970  !> PROCNAME is the procedure name for logging and debugging
1971  character(len=*), parameter :: procname = &
1972  "(spatial_visibility_visual_range_m)"
1973 
1974  !> ### Implementation details ###
1975  !> Check if optional object area parameter is present. For a base
1976  !! the_environment::spatial object, providing explicit value is
1977  !! **mandatory**. If object area is absent, commondata::missing value
1978  !! is used as a default, which results in **wrong calculations**.
1979  if (present(object_area)) then
1980  object_area_here = object_area
1981  else
1982  object_area_here = missing
1983  !> - Such a case is logged with the commondata::ltag_error logger tag.
1984  !! (check logger errors with `grep ERROR: *log`).
1985  !! .
1986  call log_msg(ltag_error // "Object area ('object_area') parameter is" //&
1987  " not provided for a base SPATIAL class object in" // &
1988  procname // ": MISSING value is used for area.")
1989  end if
1990  !> Check optional `contrast` parameter. If unset, use global
1991  !! `commondata::preycontrast_default`.
1992  if (present(contrast)) then
1993  contrast_here = contrast
1994  else
1995  contrast_here = preycontrast_default
1996  end if
1997 
1998  !> Check optional time step parameter. If unset, use global
1999  !! `commondata::global_time_step_model_current`.
2000  if (present(time_step_model)) then
2001  time_step_model_here = time_step_model
2002  else
2003  time_step_model_here = global_time_step_model_current
2004  end if
2005 
2006  !> Calculate ambient illumination / irradiance at the depth of
2007  !! this object at the given time step.
2008  irradiance_agent_depth = this%illumination(time_step_model_here)
2009 
2010  !> Return visual range to see this spatial object: its visibility range.
2011  visrange = m2cm( visual_range( irradiance = irradiance_agent_depth, &
2012  prey_area = object_area_here, &
2013  prey_contrast = contrast_here ) )
2014 
2016 
2017  !-----------------------------------------------------------------------------
2018  !> Identify in which environment from the input list this spatial agent is
2019  !! currently in.
2020  !! Example call:
2021  !! @code
2022  !! ienv = object%find_environment( [habitat_safe, habitat_dangerous] )
2023  !! @endcode
2024  !! @note Because the habitat object is an extension of the environment, this
2025  !! method also works with the habitats.
2026  !!
2027  !> Determining the environment object the agent is currently in
2028  !! can be done by the_environment::spatial::find_environment()
2029  !! method in this way:
2030  !! @verbatim
2031  !! ...
2032  !! environment_limits = Global_Habitats_Available( &
2033  !! this_agent%find_environment(Global_Habitats_Available) )
2034  !! ...
2035  !! @endverbatim
2036  !! This uses the the_environment::global_habitats_available global array
2037  !! containing all available environments, initialised in
2038  !! the_evolution::init_environment_objects().
2039  pure function spatial_get_environment_in_pos(this, environments_array) &
2040  result(environ_list_number)
2041  class(spatial), intent(in) :: this
2042  !> @param[in] environments_array An array of environment objects, where the
2043  !! `this` object can be. If this parameter is omitted, the
2044  !! environment objects are obtained from the default global
2045  !! array the_environment::global_habitats_available.
2046  !! @warning The environment objects within the array must be
2047  !! non-overlapping, otherwise, the results are
2048  !! undefined due to parallel algorithm.
2049  class(environment), optional, dimension(:), intent(in) :: environments_array
2050  !> @return Returns the number of the environment from the input array,
2051  !! where this spatial object is currently in.
2052  integer :: environ_list_number
2053 
2054  ! Local number of the environments in the input array
2055  integer :: number_environments
2056 
2057  ! Local counters
2058  integer :: i
2059 
2060  array_provided: if (present(environments_array)) then
2061 
2062  !> ### Implementation details ###
2063  !> First, determine the size of the input array of the environments among
2064  !! which the check is done.
2065  number_environments = size(environments_array)
2066 
2067  !> Also, initialise the return value to zero.
2068  environ_list_number = 0
2069 
2070  !> Then cycle over all the input environments whether the this spatial
2071  !! object is within it.
2072  do concurrent(i=1:number_environments)
2073  if (this .within. environments_array(i) ) then
2074  !> The `.within.` operator is used for checking (defined by
2075  !! `the_environment::environment_check_located_within_3d()`).
2076  !! Then, return the number of the environment within the input array
2077  !! and exit.
2078  environ_list_number = i
2079  ! exit ! do concurrent does not accept premature exit.
2080  end if
2081  end do
2082 
2083  else array_provided
2084 
2085  !> If the `environments_array` array is not provided, default habitats
2086  !! are obtained from the the_environment::global_habitats_available
2087  !! global array .
2088  ! @note The code is repeated from the `environments_array` "provided"
2089  ! part above to work around the requirement to conversion between
2090  ! objects the_environment::environment and
2091  ! the_environment::habitat because the input `environments_array`
2092  ! parameter is `class`.
2093  number_environments = size(global_habitats_available)
2094 
2095  ! Also, initialise the return value to zero.
2096  environ_list_number = 0
2097 
2098  ! Then cycle over all the input environments whether the this spatial
2099  ! object is within it.
2100  do concurrent(i=1:number_environments)
2101  if (this .within. global_habitats_available(i) ) then
2102  ! The `.within.` operator is used for checking (defined by
2103  ! `the_environment::environment_check_located_within_3d()`).
2104  ! Then, return the number of the environment within the input array
2105  ! and exit.
2106  environ_list_number = i
2107  ! exit ! do concurrent does not accept premature exit.
2108  end if
2109  end do
2110 
2111  end if array_provided
2112 
2113  end function spatial_get_environment_in_pos
2114 
2115  !-----------------------------------------------------------------------------
2116  !> Place spatial movable object into a 3D space, define the object's current
2117  !! coordinates, but first save previous coordinates.
2118  !! @param x The spatial objects will now get these coordinates.
2119  !! @param y The spatial objects will now get these coordinates.
2120  !! @param depth The spatial objects will now get these coordinates.
2121  subroutine spatial_moving_fix_position_3d_v(this, x, y, depth)
2122  class(spatial_moving), intent(inout) :: this
2123 
2124  ! The spatial objects will now get these coordinates
2125  real(SRP), intent(in) :: x,y,depth
2126 
2127  !> ### Implementation details ###
2128  !> Save previous coordinates into the history stacks.
2129  call add_to_history(this%history%x, this%x)
2130  call add_to_history(this%history%y, this%y)
2131  call add_to_history(this%history%depth, this%depth)
2132 
2133  !> Finally, position the object now with the coordinates provided.
2134  this%x = x
2135  this%y = y
2136  this%depth = depth
2137 
2138  end subroutine spatial_moving_fix_position_3d_v
2139 
2140  !-----------------------------------------------------------------------------
2141  !> Place spatial movable object into a 3D space, define the object's current
2142  !! coordinates, but first save previous coordinates.
2143  !! @param location The spatial objects will now get these coordinates.
2144  elemental subroutine spatial_moving_fix_position_3d_o(this, location)
2145  class(spatial_moving), intent(inout) :: this
2146 
2147  ! The spatial objects will now get these coordinates
2148  type(spatial), intent(in) :: location
2149 
2150  !> ### Implementation details ###
2151  !> Save previous coordinates into the history stacks.
2152  call add_to_history(this%history%x, this%x)
2153  call add_to_history(this%history%y, this%y)
2154  call add_to_history(this%history%depth, this%depth)
2155 
2156  !> Finally, position the object now with the coordinates provided.
2157  this%x = location%x
2158  this%y = location%y
2159  this%depth = location%depth
2160 
2161  end subroutine spatial_moving_fix_position_3d_o
2162 
2163  !-----------------------------------------------------------------------------
2164  !> Repeat (re-save) the current position into the positional history stack.
2165  !! @note Re-saving the current position is necessary to keep the
2166  !! full positional history even for the the_behavior::behaviour's that
2167  !! do not involve spatial displacement (movement).
2169  class(spatial_moving), intent(inout) :: this
2170 
2171  ! Re-save current coordinates into the history stacks.
2172  call add_to_history( this%history%x, this%x )
2173  call add_to_history( this%history%y, this%y )
2174  call add_to_history( this%history%depth, this%depth )
2175 
2177 
2178  !-----------------------------------------------------------------------------
2179  !> Calculate the Euclidean distance between two spatial objects. This is a
2180  !! type-bound function.
2181  !! @returns distance Euclidean distance between two spatial objects.
2182  !! @param other another spatial object that we measure distance between.
2183  !! @note Note that this version uses the vector-based backend
2184  !! for calculation. The vector-based backend is equivalent to the
2185  !! scalar-based, but might be more general as it easily works with
2186  !! other dimensionality (e.g. 2D or 4D). The negative side is that
2187  !! the vector-based backend cannot be used to make an
2188  !! **elemental** function.
2189  !! **Example:** `x_dist = object%distance(other_object)`
2190  elemental function spatial_distance_3d (this, other) &
2191  result(distance_euclidean)
2192  class(spatial), intent(in) :: this
2193  ! @returns distance Euclidean distance between two spatial objects.
2194  real(srp) :: distance_euclidean
2195  ! @param other another spatial object that we measure distance between.
2196  class(spatial), intent(in) :: other
2197 
2198  !> ### Implementation details ###
2199  !> Calculate the distance between these two spatial objects.
2200  !! @note Note that this version uses the vector-based backend
2201  !! for calculation. The vector-based backend is equivalent to the
2202  !! scalar-based, but might be more general as it easily works with
2203  !! other dimensionality (e.g. 2D or 4D). The negative side is that
2204  !! the vector-based backend cannot be used to make an
2205  !! **elemental** function.
2206  distance_euclidean = dist( [this%x, this%y, this%depth], &
2207  [other%x, other%y, other%depth] )
2208 
2209  end function spatial_distance_3d
2210 
2211  !-----------------------------------------------------------------------------
2212  !> Concatenate two arrays of the_environment::spatial objects `a` and `b`.
2213  !! This procedure uses array slices which would be faster in most cases than
2214  !! the intrinsic `[a,b]` method.
2215  !! @note There is a user defined operator `.cat.` making use of this
2216  !! procedure that can be used like this:
2217  !! @code
2218  !! object1%location() .cat. object2%location()
2219  !! @endcode
2220  !> @warning This is the the_environment::spatial **type** version. All input
2221  !! and output parameters are defined as **type**, so this is not
2222  !! class-safe.
2223  pure function spatial_stack2arrays(a, b) result (c)
2224  !> @param[in] a first array
2225  type(spatial), intent(in), dimension(:) :: a
2226  !> @param[in] b second array
2227  type(spatial), intent(in), dimension(:) :: b
2228  !> @return an array [a, b]
2229  type(spatial), dimension(:), allocatable :: c
2230 
2231  allocate(c(size(a)+size(b)))
2232  c(1:size(a)) = a
2233  c(size(a)+1:size(a)+size(b)) = b
2234  ! The easier method using intrinsic array joining [a, b] is probably slower.
2235 
2236  end function spatial_stack2arrays
2237 
2238  !-----------------------------------------------------------------------------
2239  !> Concatenate two arrays of the_environment::spatial_moving objects `a` and
2240  !! `b`. This procedure uses array slices which would be faster in most cases
2241  !! than the intrinsic `[a,b]` method.
2242  !! @note There is a user defined operator `.cat.` making use of this
2243  !! procedure that can be used like this:
2244  !! @code
2245  !! object1%location() .cat. object2%location()
2246  !! @endcode
2247  !> @warning This is the the_environment::spatial_moving **type** version.
2248  !! All input and output parameters are defined as **type**, so this
2249  !! is not class-safe.
2250  pure function spatial_moving_stack2arrays(a, b) result (c)
2251  !> @param[in] a first array
2252  type(spatial_moving), intent(in), dimension(:) :: a
2253  !> @param[in] b second array
2254  type(spatial_moving), intent(in), dimension(:) :: b
2255  !> @return an array [a, b]
2256  type(spatial_moving), dimension(:), allocatable :: c
2257 
2258  allocate(c(size(a)+size(b)))
2259  c(1:size(a)) = a
2260  c(size(a)+1:size(a)+size(b)) = b
2261  ! The easier method using intrinsic array joining [a, b] is probably slower.
2262 
2263  end function spatial_moving_stack2arrays
2264 
2265  !-----------------------------------------------------------------------------
2266  !> Concatenate the **location** components of two arrays of
2267  !! the_environment::spatial class objects a and b. This procedure uses
2268  !! array slices which would be faster in most cases than the intrinsic
2269  !! `[a,b]` method.
2270  !! @note There is a user defined operator `.cat.` making use of this
2271  !! procedure that can be used like this:
2272  !! @code
2273  !! all_objects%position%( object1 .catloc. object2 )
2274  !! @endcode
2275  !> @warning Unlike the the_environment::spatial_stack2arrays() and
2276  !! the_environment::spatial_moving_stack2arrays() methods, this
2277  !! procedure is class-safe and can be used with any class upwards,
2278  !! but it concatenates **only** the location data (returns **type**
2279  !! the_environment::spatial).
2280  pure function spatial_class_stack2arrays_locs(a, b) result (c)
2281  !> @param[in] a first array
2282  class(spatial), intent(in), dimension(:) :: a
2283  !> @param[in] b second array
2284  class(spatial), intent(in), dimension(:) :: b
2285  !> @return an array [a, b]
2286  type(spatial), dimension(:), allocatable :: c
2287 
2288  allocate(c(size(a)+size(b)))
2289  call c(1:size(a))%position( a%location() )
2290  call c(size(a)+1:size(a)+size(b))%position( b%location() )
2291 
2292  end function spatial_class_stack2arrays_locs
2293 
2294  !-----------------------------------------------------------------------------
2295  !> This is a **non-type-bound** version of the distance calculation function.
2296  !! @note Note that this is an **elemental** function that can also accept
2297  !! arrays (but these must be conforming).
2298  !! Example:
2299  !! @code
2300  !! x_dist = dist3d(object, other_object) # scalar variant
2301  !! @endcode
2302  !! @code
2303  !! D=dist3d( habitat_safe%food%food(1:3), &
2304  !! habitat_safe%food%food(4:6) ) # vector variant
2305  !! @endcode
2306  elemental function dist3d (this, other) result (distance_euclidean)
2307  class(spatial), intent(in) :: this
2308  !> @returns distance Euclidean distance between two spatial objects.
2309  real(srp) :: distance_euclidean
2310  !> @param other another spatial object that we measure distance between.
2311  class(spatial), intent(in) :: other
2312 
2313  !> ### Implementation details ###
2314  !> Calculate the distance between these two spatial objects.
2315  !! @note Note that this version uses the scalar-based backend
2316  !! for calculation. The scalar-based backend is equivalent to the
2317  !! vector-based one, but is linked with the 3D space only (has to be
2318  !! re-implemented in case of 2D or 4D space). But its positive side
2319  !! is that it is an **elemental** function that can be used to make
2320  !! further elemental functions.
2321  distance_euclidean = dist(this%x, other%x, &
2322  this%y, other%y, &
2323  this%depth, other%depth)
2324 
2325  end function dist3d
2326 
2327  !-----------------------------------------------------------------------------
2328  !> Calculate the Euclidean distance between the current and previous
2329  !! position of a single spatial object.
2330  !! @param from_history We have to calculate total distance from this point
2331  !! in the spatial stack history (< HISTORY_SIZE_SPATIAL).
2332  !! Not used here, always 0.0.
2333  !! @returns distance Euclidean distance between two spatial objects.
2334  elemental function spatial_self_distance_3d (this, from_history) &
2335  result(distance_euclidean)
2336  class(spatial), intent(in) :: this ! NOT used here, always results in 0.0
2337  ! @param from_history We have to calculate total distance from this point
2338  ! in the spatial stack history (< HISTORY_SIZE_SPATIAL)
2339  integer, optional, intent(in) :: from_history ! NOT used here, always 0.0.
2340  ! @returns distance Euclidean distance between two spatial objects
2341  real(srp) :: distance_euclidean
2342 
2343  !> ### Implementation details ###
2344  !> The distance between two positions of an immobile object is zero
2345  !! in all cases. It is a fixed value in this procedure.
2346  distance_euclidean=0.0_srp
2347 
2348  end function spatial_self_distance_3d
2349 
2350  !-----------------------------------------------------------------------------
2351  !> Calculate the Euclidean distance between the current and previous
2352  !! position of a single spatial movable object. Optionally, it also
2353  !! calculates the total distance traversed during the `from_history` points
2354  !! from the history stack along with the distance from the current position
2355  !! and the last historical value. For example, to calculate the **average**
2356  !! distance throughout the whole history (`HISTORY_SIZE_SPATIAL`
2357  !! points) do this:
2358  !! @code
2359  !! object_name%way(HISTORY_SIZE_SPATIAL - 1) / HISTORY_SIZE_SPATIAL
2360  !! @endcode
2361  !! This is because for `N` points in history we can calculate `N-1`
2362  !! distances, but the sample size is `N`, that is `N-1` plus an additional
2363  !! distance between the latest historical point and the current position.
2364  !! @param from_history We have to calculate total distance from this point
2365  !! in the spatial stack history (< `HISTORY_SIZE_SPATIAL`)
2366  !! @returns distance Euclidean distance between two spatial objects.
2367  elemental function spatial_moving_self_distance_3d (this, from_history) &
2368  result(distance_euclidean)
2369  class(spatial_moving), intent(in) :: this
2370  ! @param from_history We have to calculate total distance from this point
2371  ! in the spatial stack history (< `HISTORY_SIZE_SPATIAL`)
2372  integer, optional, intent(in) :: from_history
2373  ! @returns distance Euclidean distance between two spatial objects
2374  real(srp) :: distance_euclidean
2375 
2376  ! Local copy of optional
2377  integer :: from_history_here
2378 
2379  ! Local variable to keep the size of the history array
2380  integer :: history_size
2381 
2382  ! Local counter
2383  integer :: i
2384 
2385  !> ### Implementation details ###
2386  !> We get the history size from the history stack size (`history`),
2387  !! alternatively, can get from the `HISTORY_SIZE_SPATIAL` parameter
2388  !! directly.
2389  history_size = size(this%history)
2390 
2391  !> Check if we are asked to calculate distance traversed using the history
2392  !! stack. If the number provided exceed the limit, set maximum. If no
2393  !! history requested (parameter not present or 0) calculate the distance
2394  !! between the current point and the latest historical point.
2395  !! @note If we have history stack of size **N**, we can calculate maximum
2396  !! **N-1** historical distances between **N** successive points.
2397  if (present(from_history)) then
2398  if (from_history <= history_size_spatial-1) then
2399  from_history_here = from_history
2400  else
2401  from_history_here = history_size_spatial-1
2402  end if
2403  else
2404  from_history_here = 0
2405  end if
2406 
2407  !> Calculate the distance between the current position and
2408  !> the last historical stack record.
2409  distance_euclidean=dist( this%x, this%history(history_size)%x, &
2410  this%y, this%history(history_size)%y, &
2411  this%depth, this%history(history_size)%depth )
2412 
2413  !> Now cycle backwards through the `from_history` steps of the history
2414  !! stack and calculate the sum = total distance traversed.
2415  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
2416  ! implemented in all compilers. Use normal `do` in such a case.
2417  ! @warning Note that this `do concurrent` uses negative step -1.
2418  ! TODO: Parallel processing needs testing for correctness and
2419  ! speed benefit! So far **disabled** `do concurrent`.
2420  !do concurrent ( i = history_size:history_size-from_history_here+1:-1 )
2421  do i = history_size, history_size-from_history_here+1, -1
2422  distance_euclidean = distance_euclidean + &
2423  dist( this%history(i)%x, this%history(i-1)%x, &
2424  this%history(i)%y, this%history(i-1)%y, &
2425  this%history(i)%depth, this%history(i-1)%depth )
2426  end do
2427 
2428  end function spatial_moving_self_distance_3d
2429 
2430  !-----------------------------------------------------------------------------
2431  !> Create a new spatial moving object. Initially it has no position, all
2432  !! coordinate values are `MISSING` or `INVALID` for real type coordinates.
2433  !! @note This is the **constructor** for `SPATIAL_MOVING` object type that
2434  !! makes it into existence and gives it its value.
2435  elemental subroutine spatial_moving_create_3d(this)
2436  class(spatial_moving), intent(inout) :: this
2437 
2438  !> ### Implementation details ###
2439  !> Assign `MISSING` values to the new object, use interface function
2440  !! and type constructor.
2441  !call this%position( SPATIAL(MISSING,MISSING,MISSING) )
2442  call this%missing()
2443 
2444  !> This also cleanups the history stack, i.e. fills it with `MISSING`
2445  !! values.
2446  call this%spatial_history_clean()
2447 
2448  end subroutine spatial_moving_create_3d
2449 
2450  !-----------------------------------------------------------------------------
2451  !> Create a new empty history of positions for spatial moving object.
2452  !! Assign all values to the MISSING value code.
2453  elemental subroutine spatial_moving_clean_hstory_3d(this)
2454  class(spatial_moving), intent(inout) :: this
2455 
2456  !> ### Implementation details ###
2457  !> Set all historical stack **arrays** to `MISSING`.
2458  this%history%x = missing
2459  this%history%y = missing
2460  this%history%depth = missing
2461 
2462  end subroutine spatial_moving_clean_hstory_3d
2463 
2464  !-----------------------------------------------------------------------------
2465  !> The spatial moving object **ascends**, goes up the depth with specific
2466  !! fixed step size.
2467  elemental subroutine spatial_moving_go_up(this, step)
2468  class(spatial_moving), intent(inout) :: this
2469  !> @param[in] step is the step size for the upwards walk. If it is too
2470  !! large (the object would extend beyond its current
2471  !! environment), it is adjusted autiomatically.
2472  real(srp), optional, intent(in) :: step
2473 
2474  ! Local minimum depth
2475  real(srp) :: min_depth
2476 
2477  ! Local copy of the step
2478  real(srp) :: step_here
2479 
2480  step_here = step
2481 
2482  !> ### Implementation details ###
2483  !> Calculate the minimum depth in the environment currently
2484  !! occupied by the spatial object.
2485  !! - Use a combination of the_environment::spatial::find_environment()
2486  !! and the_environment::environment::depth_min().
2487  !! .
2488  min_depth = global_habitats_available( &
2489  this%find_environment( &
2491  )%depth_min()
2492 
2493  !> Check if the target depth is likely to go beyond the environment
2494  !! depth limits and reduce the upwnward walk step size
2495  !! accordingly. Namely, if the depth coordinate of the object
2496  !! minus the depth step exceeds the minimum depth, the step is reduced
2497  !! to be within the available environment:
2498  !! @f$ d_{o} - D_{min} - \varepsilon @f$, where @f$ D_{min} @f$ is the
2499  !! maximum depth, @f$ d_{o} @f$ is the current depth of the object and
2500  !! @f$ \varepsilon @f$ is a very small constant defined by the parameter
2501  !! commondata::zero to guarantee the object remains within the current
2502  !! environment.
2503  if (this%dpos() - step_here <= min_depth ) &
2504  step_here = max( 0.0_srp, this%dpos() - min_depth - zero )
2505 
2506 
2507  !> Relocate the object so that its X and Y coordinates remain intact
2508  !! but the depth reduces by the step size.
2509  !! - Here, if the object is a the_environment::food_item class object,
2510  !! also check if it is the_environment::food_item::is_available() and
2511  !! move the object only if yes.
2512  !! .
2513  select type(this)
2514  class is (food_item)
2515  if (this%is_available()) &
2516  call this%position( spatial( x = this%x, &
2517  y = this%y, &
2518  depth = this%depth - step_here ) )
2519  class default
2520  call this%position( spatial( x = this%x, &
2521  y = this%y, &
2522  depth = this%depth - step_here ) )
2523  end select
2524 
2525  end subroutine spatial_moving_go_up
2526 
2527  !-----------------------------------------------------------------------------
2528  !> The spatial moving object **decends**, goes down the depth with specific
2529  !! fixed step size.
2530  elemental subroutine spatial_moving_go_down(this, step)
2531  class(spatial_moving), intent(inout) :: this
2532  !> @param[in] step is the step size for the upwards walk. If it is too
2533  !! large (the object would extend beyond its current
2534  !! environment), it is adjusted autiomatically.
2535  real(srp), optional, intent(in) :: step
2536 
2537  ! Local minimum depth
2538  real(srp) :: max_depth
2539 
2540  ! Local copy of the step
2541  real(srp) :: step_here
2542 
2543  step_here = step
2544 
2545  !> ### Implementation details ###
2546  !> Calculate the maximum depth in the environment currently
2547  !! occupied by the spatial object.
2548  !! - Use a combination of the_environment::spatial::find_environment()
2549  !! and the_environment::environment::depth_max().
2550  !! .
2551  max_depth = global_habitats_available( &
2552  this%find_environment( &
2554  )%depth_max()
2555 
2556  !> Check if the target depth is likely to go beyond the environment
2557  !! depth limits and reduce the downward walk step size
2558  !! accordingly. Namely, if the depth coordinate of the object
2559  !! plus the depth step exceeds the maximum depth, the step is reduced
2560  !! to be within the available environment:
2561  !! @f$ D_{max} - d_{o} - \varepsilon @f$, where @f$ D_{max} @f$ is the
2562  !! maximum depth, @f$ d_{o} @f$ is the current depth of the object and
2563  !! @f$ \varepsilon @f$ is a very small constant defined by the parameter
2564  !! commondata::zero to guarantee the object remains within the current
2565  !! environment.
2566  if (this%dpos() + step_here >= max_depth ) &
2567  step_here = max( 0.0_srp, max_depth - this%dpos() - zero )
2568 
2569  !> Relocate the object so that its X and Y coordinates remain intact
2570  !! but the depth reduces by the step size.
2571  !! - Here, if the object is a the_environment::food_item class object,
2572  !! also check if it is the_environment::food_item::is_available() and
2573  !! move the object only if yes.
2574  !! .
2575  select type(this)
2576  class is (food_item)
2577  if (this%is_available()) &
2578  call this%position( spatial( x = this%x, &
2579  y = this%y, &
2580  depth = this%depth + step_here ) )
2581  class default
2582  call this%position( spatial( x = this%x, &
2583  y = this%y, &
2584  depth = this%depth + step_here ) )
2585  end select
2586 
2587  end subroutine spatial_moving_go_down
2588 
2589  !-----------------------------------------------------------------------------
2590  !> @brief Implements an optionally environment-restricted Gaussian random
2591  !! walk in 3D.
2592  !! @param meanshift the mean shift along any of the three dimensions.
2593  !! @param cv_shift the coefficient of variation for a single elementary
2594  !! shift in any of the three dimensions.
2595  !! @param environment_limits Limits of the environment area available for
2596  !! the random walk. The moving object cannot get beyond this limit.
2597  !! @details The moving object walks in three dimensions. The process is
2598  !! simple, first shift along the x axis for some random Gaussian
2599  !! length (with the mean `meanshift` and the variance/CV `cv_shift`)
2600  !! then walk another Gaussian length the y axis. The, walk in the
2601  !! same manner along the z axis. The optional restriction is that
2602  !! the whole walk must not exceed specific spatial location set by
2603  !! the environment parameter.
2604  subroutine spatial_moving_randomwalk_gaussian_step_3d(this, meanshift, &
2605  cv_shift, environment_limits)
2606  class(spatial_moving), intent(inout) :: this
2607 
2608  ! @param meanshift the mean shift along any of the three dimensions.
2609  real(SRP), intent(in) :: meanshift
2610  ! @param cv_shift the coefficient of variation for a single elementary
2611  ! shift in any of the three dimensions.
2612  real(SRP), intent(in) :: cv_shift
2613  ! @param environment_limits Limits of the environment area available for
2614  ! the random walk. The moving object cannot get beyond this limit.
2615  class(environment), intent(in), optional :: environment_limits
2616 
2617  ! Local test object that we test, should lay within the
2618  ! `environment_limits` environmental object.
2619  type(spatial) :: current_pos, test_object
2620 
2621  !...........................................................................
2622 
2623  !> ### Implementation details ###
2624  !> First, we get the current coordinates of the spatial object.
2625  current_pos = this%now()
2626 
2627  !> And set a temporary spatial test object with the new coordinates,
2628  !! advancing/adding our random walk step. This is done via the
2629  !! `.radd.` operator and calling RNORM (see HEDTOOLS).
2630  call test_object%position( spatial( &
2631  x=current_pos%x .radd. &
2632  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2633  y=current_pos%y .radd. &
2634  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2635  depth=current_pos%depth .radd. &
2636  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ))
2637 
2638  !> Environment restriction part of the random walk, if `environment_limits`
2639  !! parameter object is provided. Here the object is not allowed to go beyond
2640  !! its bounding environment: get new position while outside of the target
2641  !! environment.
2642  environ_restrict: if (present(environment_limits)) then
2643  !> - Loop while this new test spatial object is outside
2644  !! of our target environment. It must be **strictly** within.
2645  !! .
2646  do while (.NOT. test_object%is_within(environment_limits))
2647  ! (If the `test_object` is outside the environment, we create new
2648  ! randomly updated coordinates.)
2649  call test_object%position( spatial( &
2650  x=current_pos%x .radd. &
2651  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2652  y=current_pos%y .radd. &
2653  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2654  depth=current_pos%depth .radd. &
2655  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ))
2656  end do
2657  end if environ_restrict
2658 
2659  !> Finally, change the current position of the `this` object to the
2660  !! position defined by the `test_object`. The standard function `position`
2661  !! for the `SPATIAL_MOVING`is used, that keeps the movement history.
2662  call this%position(test_object)
2663 
2665 
2666  !-----------------------------------------------------------------------------
2667  !> @brief Implements an optionally environment-restricted Gaussian random
2668  !! walk in a "2.5 dimensions", i.e. 2D x y with separate walk
2669  !! parameters for the third depth dimension.
2670  !! @param meanshift the mean shift along any of the three dimensions.
2671  !! @param cv_shift the coefficient of variation for a single elementary
2672  !! shift in any of the three dimensions.
2673  !! @param environment_limits Limits of the environment area available for
2674  !! the random walk. The moving object cannot get beyond this limit.
2675  !! @details The moving object walks in three dimensions. The process is
2676  !! simple, first shift along the x axis for some random Gaussian
2677  !! length (with the mean `meanshift` and the variance/CV `cv_shift`)
2678  !! then walk another Gaussian length the y axis. The, walk in the
2679  !! same manner along the z axis. But here z axis has separate walk
2680  !! parameters (`meanshift` and `cv_shift`) that are much smaller
2681  !! than the x and y parameters. The optional restriction is that
2682  !! the whole walk must not exceed specific spatial location set by
2683  !! the environment parameter.
2684  !! @note The mean and CV is different for the 2D x y movement and the
2685  !! third dimension *depth* movement.
2687  meanshift_xy, cv_shift_xy, &
2688  meanshift_depth, cv_shift_depth, &
2689  environment_limits)
2690  class(spatial_moving), intent(inout) :: this
2691 
2692  ! @param meanshift the mean shift along any of the three dimensions.
2693  real(SRP), intent(in) :: meanshift_xy, meanshift_depth
2694  ! @param cv_shift the coefficient of variation for a single elementary
2695  ! shift in any of the three dimensions.
2696  real(SRP), intent(in) :: cv_shift_xy, cv_shift_depth
2697  ! @param environment_limits Limits of the environment area available for
2698  ! the random walk. The moving object cannot get beyond this limit.
2699  class(environment), intent(in), optional :: environment_limits
2700 
2701  ! Local test object that we test, should lay within the
2702  ! `environment_limits` environmental object.
2703  type(spatial) :: current_pos, test_object
2704 
2705  !...........................................................................
2706 
2707  !> ### Implementation details ###
2708  !> First, we get the current coordinates of the spatial object.
2709  current_pos = this%now()
2710 
2711  !> And set a temporary spatial test object with the new coordinates,
2712  !! advancing/adding our random walk step. This is done via the
2713  !! `.radd.` operator and calling the `RNORM` function (see HEDTOOLS).
2714  call test_object%position( spatial( &
2715  x=current_pos%x .radd. &
2716  rnorm( meanshift_xy, &
2717  cv2variance(cv_shift_xy, meanshift_xy) ), &
2718  y=current_pos%y .radd. &
2719  rnorm( meanshift_xy, &
2720  cv2variance(cv_shift_xy, meanshift_xy) ), &
2721  depth=current_pos%depth .radd. &
2722  rnorm( meanshift_depth, &
2723  cv2variance(cv_shift_depth, meanshift_depth) ) ) )
2724 
2725  !> Environment restriction part of the random walk, if `environment_limits`
2726  !! parameter object is provided. Here the object is not allowed to go beyond
2727  !! its bounding environment: get new position while outside of the target
2728  !! environment.
2729  environ_restrict: if (present(environment_limits)) then
2730  !> - Loop while this new test spatial object is outside
2731  !! of our target environment. It must be **strictly** within.
2732  !! .
2733  do while (.NOT. test_object%is_within(environment_limits))
2734  ! (if the `test_object` is outside the environment, we create new
2735  ! randomly updated coordinates.)
2736  call test_object%position( spatial( &
2737  x=current_pos%x .radd. &
2738  rnorm( meanshift_xy, &
2739  cv2variance(cv_shift_xy, meanshift_xy) ), &
2740  y=current_pos%y .radd. &
2741  rnorm( meanshift_xy, &
2742  cv2variance(cv_shift_xy, meanshift_xy) ), &
2743  depth=current_pos%depth .radd. &
2744  rnorm( meanshift_depth, &
2745  cv2variance(cv_shift_depth, meanshift_depth) ) ) )
2746  end do
2747  end if environ_restrict
2748 
2749  !> Finally, change the current position of the `this` object to the
2750  !! position defined by the `test_object`. The standard function `position`
2751  !! for the `SPATIAL_MOVING`is used, that keeps the movement history.
2752  call this%position(test_object)
2753 
2755 
2756  !-----------------------------------------------------------------------------
2757  !> @brief Implements an optionally environment-restricted **correlated
2758  !! directional** Gaussian random walk in 3D towards (or away of)
2759  !! an the_environment::spatial class `target` object.
2760  !! @details The moving object walks in three dimensions towards (or away
2761  !! of) a the_environment::spatial class target.
2762  !! Here is an example of walks:
2763  !! @image html img_doxygen_corwalk_3d.svg "Correlated Gaussian random walk in 3D"
2764  !! @image latex img_doxygen_corwalk_3d.eps "Correlated Gaussian random walk in 3D" width=14cm
2765  subroutine spatial_moving_corwalk_gaussian_step_3d(this, target, &
2766  meanshift, &
2767  cv_shift, &
2768  is_away, &
2769  ci_lim, &
2770  environment_limits,&
2771  is_converged, &
2772  debug_reps )
2773  class(spatial_moving), intent(inout) :: this
2774  !> @param[in] target The target of the random walk.
2775  class(spatial), intent(in) :: target
2776  !> @param meanshift the mean shift along any of the three dimensions.
2777  real(SRP), intent(in) :: meanshift
2778  !> @param cv_shift the coefficient of variation for a single elementary
2779  !> shift in any of the three dimensions.
2780  real(SRP), intent(in) :: cv_shift
2781  !> @param[in] is_away optional logical flag, if set to TRUE, the walk
2782  !! is actually directed out of the target, so that the object
2783  !! is going to maximise rather than minimise the distance to
2784  !! the target.
2785  logical, optional, intent(in) :: is_away
2786  !> @param[in] ci_lim This parameter sets the convergence criterion; it is
2787  !! the confidence interval limit for the distance between the
2788  !! object and the target. The walk is considered "converged" if
2789  !! the distance between the object and the target is smaller
2790  !! than `ci_lim` std. deviations of the average step distance
2791  !! (set by `meanshift`). The default value is the 95% confidence
2792  !! interval (1.95996).
2793  ! | Conf. interval | ci_lim |
2794  ! | -------------: | :------: |
2795  ! | 25.0% | 0.31860 |
2796  ! | 50.0% | 0.67449 |
2797  ! | 75.0% | 1.15035 |
2798  ! | 90.0% | 1.64485 |
2799  ! | 95.0% | 1.95996 |
2800  ! | 97.0% | 2.17009 |
2801  ! | 99.0% | 2.57583 |
2802  ! | 99.9% | 3.29053 |
2803  real(SRP), optional, intent(in) :: ci_lim
2804  !> @param environment_limits optional limits of the environment area
2805  !! available for the random walk. The moving object cannot get
2806  !! beyond this limit.
2807  class(environment), intent(in), optional :: environment_limits
2808  !> @param[out] is_converged optional logical flag for the "converged"
2809  !! state, i.e. the distance between the object and the
2810  !! target is smaller than `ci_lim` standard deviations of
2811  !! the average step distance.
2812  logical, optional, intent(out) :: is_converged
2813  !> @param[out] debug_reps optional internal counter for repeated samplings
2814  !! of random positions for prospective spatial advancement
2815  !! of the object. If the counter reaches a too high value,
2816  !! a "no convergence" state is reached. In case of the avoiding
2817  !! environmentally limited walk, when the object maximises its
2818  !! distance from the target, such a condition may indicate that
2819  !! there is no further space to avoid the target in the
2820  !! available environment.
2821  integer, optional, intent(out) :: debug_reps
2822 
2823  ! Local test object that we test, should lay within the
2824  ! `environment_limits` environmental object.
2825  type(spatial) :: current_pos, test_object
2826 
2827  ! Local copy of is_away, if TRUE the object is maximising the distance
2828  ! to the target, i.e. tries to avoid target.
2829  logical :: move_out
2830 
2831  ! Local copy of optional ci_lim
2832  real(SRP) :: perc_ci
2833 
2834  ! Default 95% confidence limit.
2835  real(SRP), parameter :: PERC_CI_DEF = 1.95996
2836 
2837  ! Maximum number of iterations, convergence limit.
2838  integer, parameter :: CONVERG = 100
2839 
2840  ! Counter for convergence repetitions.
2841  integer :: erep
2842 
2843  !...........................................................................
2844 
2845  ! Check optional parameter
2846  if (present(is_away)) then
2847  move_out = is_away
2848  else
2849  move_out = .false. ! default is to approach target.
2850  end if
2851  if(present(ci_lim)) then
2852  perc_ci = ci_lim
2853  else
2854  perc_ci = perc_ci_def
2855  end if
2856 
2857  erep = 0
2858 
2859  !> ### Implementation details ###
2860  !> First, check if the convergence criterion is reached. The walk is
2861  !! considered "converged" if the distance between the object and the
2862  !! target is smaller than `ci_lim` std. deviations of the average step
2863  !! distance (which is set by `meanshift`).
2864  if ( .not. move_out .and. &
2865  this%distance(target) < perc_ci * meanshift * cv_shift ) then
2866  if (present(is_converged)) then
2867  is_converged = .true.
2868  end if
2869  if(present(debug_reps)) then
2870  debug_reps = erep
2871  end if
2872  !> If the convergence condition is met, the object *does not* change its
2873  !! position any more, no further walks are performed.
2874  return
2875  end if
2876 
2877  !> If the convergence condition is not yet met, the current coordinates of
2878  !! the spatial object are recorded.
2879  current_pos = this%now()
2880 
2881  !> Then a temporary spatial test object (`test_object`) is set the new
2882  !! coordinates for the spatial moving object, advancing to a random walk
2883  !! step. This is done by randomly adding or subtracting a Gaussian step
2884  !! size with the mean `meanshift` and variance defined by `cv_shift`
2885  !! along all three spatial coordinates.
2886  call test_object%position( spatial( &
2887  x=current_pos%x .radd. &
2888  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2889  y=current_pos%y .radd. &
2890  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2891  depth=current_pos%depth .radd. &
2892  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ) )
2893 
2894  !> A series of conditions is then checked, the main is that the new
2895  !! position defined by the temporary spatial object should be such that
2896  !! the distance from the target must be smaller (if the object is intended
2897  !! to moved towards the target) or larger (if the object is intended to
2898  !! move away of the target).
2899  !!
2900  !! Environment restriction is also applied if `environment_limits`
2901  !! parameter is provided: the object is not allowed to go beyond
2902  !! its bounding environment in such a case.
2903  !!
2904  !! There is also a safeguard against poor convergence: If the number of
2905  !! iterations exceeds a fixed value (`CONVERG` local parameter), force to
2906  !! exit from the condition loops without changing the object position (i.e.
2907  !! no walk is done).
2908  away: if (move_out) then
2909  environ_restrict1: if (present(environment_limits)) then
2910  ! Loop while this new test spatial object is outside
2911  ! our target environment. It must be **strictly** within.
2912  do while ( test_object%distance(target) < this%distance(target) .or. &
2913  .NOT. test_object%is_within(environment_limits) )
2914  erep = erep + 1
2915  if (erep > converg) exit
2916  ! if the `test_object` is outside the environment or not advancing
2917  ! to the correct direction, we create new randomly updated
2918  ! coordinates and iterate while this wrong condition still holds.
2919  call test_object%position( spatial( &
2920  x=current_pos%x .radd. &
2921  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2922  y=current_pos%y .radd. &
2923  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2924  depth=current_pos%depth .radd. &
2925  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ) )
2926  end do
2927  else environ_restrict1
2928  do while ( test_object%distance(target) < this%distance(target) )
2929  erep = erep + 1
2930  if (erep > converg) exit
2931  ! if the `test_object` is outside the environment or not advancing
2932  ! to the correct direction, we create new randomly updated
2933  ! coordinates and iterate while this wrong condition still holds.
2934  call test_object%position( spatial( &
2935  x=current_pos%x .radd. &
2936  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2937  y=current_pos%y .radd. &
2938  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2939  depth=current_pos%depth .radd. &
2940  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ) )
2941  end do
2942  end if environ_restrict1
2943  else away
2944  environ_restrict2: if (present(environment_limits)) then
2945  ! Loop while this new test spatial object is outside
2946  ! our target environment. It must be **strictly** within.
2947  do while ( test_object%distance(target) > this%distance(target) .or. &
2948  .NOT. test_object%is_within(environment_limits) )
2949  erep = erep + 1
2950  if (erep > converg) exit
2951  ! if the `test_object` is outside the environment or not advancing
2952  ! to the correct direction, we create new randomly updated
2953  ! coordinates and iterate while this wrong condition still holds.
2954  call test_object%position( spatial( &
2955  x=current_pos%x .radd. &
2956  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2957  y=current_pos%y .radd. &
2958  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2959  depth=current_pos%depth .radd. &
2960  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ) )
2961  end do
2962  else environ_restrict2
2963  do while ( test_object%distance(target) > this%distance(target) )
2964  erep = erep + 1
2965  if (erep > converg) exit
2966  ! if the `test_object` is outside the environment or not advancing
2967  ! to the correct direction, we create new randomly updated
2968  ! coordinates and iterate while this wrong condition still holds.
2969  call test_object%position( spatial( &
2970  x=current_pos%x .radd. &
2971  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2972  y=current_pos%y .radd. &
2973  rnorm( meanshift, cv2variance(cv_shift, meanshift) ), &
2974  depth=current_pos%depth .radd. &
2975  rnorm( meanshift, cv2variance(cv_shift, meanshift) ) ) )
2976  end do
2977  end if environ_restrict2
2978  end if away
2979  !> Finally, change the current position of the `this` object to the
2980  !! position defined by the `test_object`. Such a change is done only if
2981  !! the non-convergence condition is not detected.
2982  if (erep < converg) call this%position(test_object)
2983 
2984  !> At the end, check and return the optional intent[out] parameters
2985  !! `is_converged` and `debug_reps`.
2986  if ( .not. move_out .and. &
2987  this%distance(target) < perc_ci * meanshift * cv_shift ) then
2988  if (present(is_converged)) then
2989  is_converged = .true.
2990  end if
2991  end if
2992  ! return debug erep counter.
2993  if(present(debug_reps)) then
2994  debug_reps = erep
2995  end if
2996 
2998 
2999  !-----------------------------------------------------------------------------
3000  !> @brief Implements an optionally environment-restricted **correlated
3001  !! directional** Gaussian random walk in 3D towards (or away of)
3002  !! an the_environment::spatial class `target` object.
3003  !! @details The moving object walks in three dimensions towards (or away
3004  !! of) a the_environment::spatial class target.
3005  !! @image html img_doxygen_corwalk_3d.svg "Correlated Gaussian random walk in 3D"
3006  !! @image latex img_doxygen_corwalk_3d.eps "Correlated Gaussian random walk in 3D" width=14cm
3007  subroutine spatial_moving_corwalk_gaussian_step_25d(this, target, &
3008  meanshift_xy, &
3009  cv_shift_xy, &
3010  meanshift_depth, &
3011  cv_shift_depth, &
3012  is_away, &
3013  ci_lim, &
3014  environment_limits,&
3015  is_converged, &
3016  debug_reps )
3017  class(spatial_moving), intent(inout) :: this
3018  !> @param[in] target The target of the random walk.
3019  class(spatial), intent(in) :: target
3020  !> @param meanshift_xy the mean shift along any of the three dimensions.
3021  real(SRP), intent(in) :: meanshift_xy
3022  !> @param cv_shift_xy the coefficient of variation for a single elementary
3023  !> shift in any of the three dimensions.
3024  real(SRP), intent(in) :: cv_shift_xy
3025  !> @param meanshift_depth the mean shift along any of the three dimensions.
3026  real(SRP), intent(in) :: meanshift_depth
3027  !> @param cv_shift_depth the coefficient of variation for a single
3028  !> elementary shift in any of the three dimensions.
3029  real(SRP), intent(in) :: cv_shift_depth
3030  !> @param[in] is_away optional logical flag, if set to TRUE, the walk
3031  !! is actually directed out of the target, so that the object
3032  !! is going to maximise rather than minimise the distance to
3033  !! the target.
3034  logical, optional, intent(in) :: is_away
3035  !> @param[in] ci_lim This parameter sets the convergence criterion; it is
3036  !! the confidence interval limit for the distance between the
3037  !! object and the target. The walk is considered "converged" if
3038  !! the distance between the object and the target is smaller
3039  !! than `ci_lim` std. deviations of the average step distance
3040  !! (set by `meanshift`). The default value is the 95% confidence
3041  !! interval (1.95996).
3042  ! | Conf. interval | ci_lim |
3043  ! | -------------: | :------: |
3044  ! | 25.0% | 0.31860 |
3045  ! | 50.0% | 0.67449 |
3046  ! | 75.0% | 1.15035 |
3047  ! | 90.0% | 1.64485 |
3048  ! | 95.0% | 1.95996 |
3049  ! | 97.0% | 2.17009 |
3050  ! | 99.0% | 2.57583 |
3051  ! | 99.9% | 3.29053 |
3052  real(SRP), optional, intent(in) :: ci_lim
3053  !> @param environment_limits optional limits of the environment area
3054  !! available for the random walk. The moving object cannot get
3055  !! beyond this limit.
3056  class(environment), intent(in), optional :: environment_limits
3057  !> @param[out] is_converged optional logical flag for the "converged"
3058  !! state, i.e. the distance between the object and the
3059  !! target is smaller than `ci_lim` standard deviations of
3060  !! the average step distance.
3061  logical, optional, intent(out) :: is_converged
3062  !> @param[out] debug_reps optional internal counter for repeated samplings
3063  !! of random positions for prospective spatial advancement
3064  !! of the object. If the counter reaches a too high value,
3065  !! a "no convergence" state is reached. In case of the avoiding
3066  !! environmentally limited walk, when the object maximises its
3067  !! distance from the target, such a condition may indicate that
3068  !! there is no further space to avoid the target in the
3069  !! available environment.
3070  integer, optional, intent(out) :: debug_reps
3071 
3072  ! Local test object that we test, should lay within the
3073  ! `environment_limits` environmental object.
3074  type(spatial) :: current_pos, test_object
3075 
3076  ! Local copy of is_away, if TRUE the object is maximising the distance
3077  ! to the target, i.e. tries to avoid target.
3078  logical :: move_out
3079 
3080  ! Local copy of optional ci_lim
3081  real(SRP) :: perc_ci
3082 
3083  ! Default 95% confidence limit.
3084  real(SRP), parameter :: PERC_CI_DEF = 1.95996
3085 
3086  ! Maximum number of iterations, convergence limit.
3087  integer, parameter :: CONVERG = 100
3088 
3089  ! Counter for convergence repetitions.
3090  integer :: erep
3091 
3092  !...........................................................................
3093 
3094  ! Check optional parameter
3095  if (present(is_away)) then
3096  move_out = is_away
3097  else
3098  move_out = .false. ! default is to approach target.
3099  end if
3100  if(present(ci_lim)) then
3101  perc_ci = ci_lim
3102  else
3103  perc_ci = perc_ci_def
3104  end if
3105 
3106  erep = 0
3107 
3108  !> ### Implementation details ###
3109  !> First, check if the convergence criterion is reached. The walk is
3110  !! considered "converged" if the distance between the object and the
3111  !! target is smaller than `ci_lim` std. deviations of the average step
3112  !! distance (which is set by `meanshift`).
3113  if ( .not. move_out .and. &
3114  this%distance(target) < perc_ci * meanshift_xy * cv_shift_xy ) then
3115  if (present(is_converged)) then
3116  is_converged = .true.
3117  end if
3118  if(present(debug_reps)) then
3119  debug_reps = erep
3120  end if
3121  !> If the convergence condition is met, the object *does not* change its
3122  !! position any more, no further walks are performed.
3123  return
3124  end if
3125 
3126  !> If the convergence condition is not yet met, the current coordinates of
3127  !! the spatial object are recorded.
3128  current_pos = this%now()
3129 
3130  !> Then a temporary spatial test object (`test_object`) is set the new
3131  !! coordinates for the spatial moving object, advancing to a random walk
3132  !! step. This is done by randomly adding or subtracting a Gaussian step
3133  !! size with the mean `meanshift` and variance defined by `cv_shift`
3134  !! along all three spatial coordinates.
3135  call test_object%position( spatial( &
3136  x=current_pos%x .radd. &
3137  rnorm( meanshift_xy, &
3138  cv2variance(cv_shift_xy, meanshift_xy) ), &
3139  y=current_pos%y .radd. &
3140  rnorm( meanshift_xy, &
3141  cv2variance(cv_shift_xy, meanshift_xy) ), &
3142  depth=current_pos%depth .radd. &
3143  rnorm( meanshift_depth, &
3144  cv2variance(cv_shift_depth, meanshift_depth) ) ) )
3145 
3146  !> A series of conditions is then checked, the main is that the new
3147  !! position defined by the temporary spatial object should be such that
3148  !! the distance from the target must be smaller (if the object is intended
3149  !! to moved towards the target) or larger (if the object is intended to
3150  !! move away of the target).
3151  !!
3152  !! Environment restriction is also applied if `environment_limits`
3153  !! parameter is provided: the object is not allowed to go beyond
3154  !! its bounding environment in such a case.
3155  !!
3156  !! There is also a safeguard against poor convergence: If the number of
3157  !! iterations exceeds a fixed value (`CONVERG` local parameter), force to
3158  !! exit from the condition loops without changing the object position (i.e.
3159  !! no walk is done).
3160  away: if (move_out) then
3161  environ_restrict1: if (present(environment_limits)) then
3162  ! Loop while this new test spatial object is outside
3163  ! our target environment. It must be **strictly** within.
3164  do while ( test_object%distance(target) < this%distance(target) .or. &
3165  .NOT. test_object%is_within(environment_limits) )
3166  erep = erep + 1
3167  if (erep > converg) exit
3168  ! if the `test_object` is outside the environment or not advancing
3169  ! to the correct direction, we create new randomly updated
3170  ! coordinates and iterate while this wrong condition still holds.
3171  call test_object%position( spatial( &
3172  x=current_pos%x .radd. &
3173  rnorm( meanshift_xy, &
3174  cv2variance(cv_shift_xy, meanshift_xy) ), &
3175  y=current_pos%y .radd. &
3176  rnorm( meanshift_xy, &
3177  cv2variance(cv_shift_xy, meanshift_xy) ), &
3178  depth=current_pos%depth .radd. &
3179  rnorm( meanshift_depth, &
3180  cv2variance(cv_shift_depth, meanshift_depth) ) ))
3181  end do
3182  else environ_restrict1
3183  do while ( test_object%distance(target) < this%distance(target) )
3184  erep = erep + 1
3185  if (erep > converg) exit
3186  ! if the `test_object` is outside the environment or not advancing
3187  ! to the correct direction, we create new randomly updated
3188  ! coordinates and iterate while this wrong condition still holds.
3189  call test_object%position( spatial( &
3190  x=current_pos%x .radd. &
3191  rnorm( meanshift_xy, &
3192  cv2variance(cv_shift_xy, meanshift_xy) ), &
3193  y=current_pos%y .radd. &
3194  rnorm( meanshift_xy, &
3195  cv2variance(cv_shift_xy, meanshift_xy) ), &
3196  depth=current_pos%depth .radd. &
3197  rnorm( meanshift_depth, &
3198  cv2variance(cv_shift_depth, meanshift_depth) ) ))
3199  end do
3200  end if environ_restrict1
3201  else away
3202  environ_restrict2: if (present(environment_limits)) then
3203  ! Loop while this new test spatial object is outside
3204  ! our target environment. It must be **strictly** within.
3205  do while ( test_object%distance(target) > this%distance(target) .or. &
3206  .NOT. test_object%is_within(environment_limits) )
3207  erep = erep + 1
3208  if (erep > converg) exit
3209  ! if the `test_object` is outside the environment or not advancing
3210  ! to the correct direction, we create new randomly updated
3211  ! coordinates and iterate while this wrong condition still holds.
3212  call test_object%position( spatial( &
3213  x=current_pos%x .radd. &
3214  rnorm( meanshift_xy, &
3215  cv2variance(cv_shift_xy, meanshift_xy) ), &
3216  y=current_pos%y .radd. &
3217  rnorm( meanshift_xy, &
3218  cv2variance(cv_shift_xy, meanshift_xy) ), &
3219  depth=current_pos%depth .radd. &
3220  rnorm( meanshift_depth, &
3221  cv2variance(cv_shift_depth, meanshift_depth) ) ))
3222  end do
3223  else environ_restrict2
3224  do while ( test_object%distance(target) > this%distance(target) )
3225  erep = erep + 1
3226  if (erep > converg) exit
3227  ! if the `test_object` is outside the environment or not advancing
3228  ! to the correct direction, we create new randomly updated
3229  ! coordinates and iterate while this wrong condition still holds.
3230  call test_object%position( spatial( &
3231  x=current_pos%x .radd. &
3232  rnorm( meanshift_xy, &
3233  cv2variance(cv_shift_xy, meanshift_xy) ), &
3234  y=current_pos%y .radd. &
3235  rnorm( meanshift_xy, &
3236  cv2variance(cv_shift_xy, meanshift_xy) ), &
3237  depth=current_pos%depth .radd. &
3238  rnorm( meanshift_depth, &
3239  cv2variance(cv_shift_depth, meanshift_depth) ) ))
3240  end do
3241  end if environ_restrict2
3242  end if away
3243  !> Finally, change the current position of the `this` object to the
3244  !! position defined by the `test_object`. Such a change is done only if
3245  !! the non-convergence condition is not detected.
3246  if (erep < converg) call this%position(test_object)
3247 
3248  !> At the end, check and return the optional intent[out] parameters
3249  !! `is_converged` and `debug_reps`.
3250  if ( .not. move_out .and. &
3251  this%distance(target) < perc_ci * meanshift_xy * cv_shift_xy ) then
3252  if (present(is_converged)) then
3253  is_converged = .true.
3254  end if
3255  end if
3256  ! return debug erep counter.
3257  if(present(debug_reps)) then
3258  debug_reps = erep
3259  end if
3260 
3262 
3263  !-----------------------------------------------------------------------------
3264  !> @brief Implements an optionally environment-restricted **directional**
3265  !! Gaussian random walk in 3D towards a `target`
3266  !! the_environment::spatial class object.
3267  !! @details The moving object walks in three dimensions towards a target.
3268  !! The process is simple, first shift along the x axis for some
3269  !! random Gaussian length (with the mean `meanshift` and the
3270  !! variance/CV `cv_shift`) in the direction that minimises the
3271  !! coordinate-bound distance from the target. If the target is
3272  !! located at a distance not exceeding the `meanshift` we have
3273  !! got towards the target. Then the process is repeated for the
3274  !! y and z axes. The optional restriction is that the whole walk
3275  !! must not exceed specific spatial location set by the environment
3276  !! parameter.
3277  !! @note This `dirwalk` is a obsolete suboptimal implementation
3278  !! See `the_environment::spatial_moving_corwalk_gaussian_step_3d()`
3279  !! and `the_environment::spatial_moving_corwalk_gaussian_step_25d()`
3280  !! for a better alternative.
3281  !! @param[in] target The target of the random walk, the walk should converge
3282  !! to the target within finite number of steps.
3283  !! @param[in] meanshift the mean shift along any of the three dimensions.
3284  !! @param[in] cv_shift the coefficient of variation for a single elementary
3285  !! shift in any of the three dimensions.
3286  !! @param[in] environment_limits Limits of the environment area available for
3287  !! the random walk. The moving object cannot get beyond this limit.
3288  subroutine spatial_moving_dirwalk_gaussian_step_3d(this, target, &
3289  meanshift, &
3290  cv_shift, &
3291  environment_limits)
3292  class(spatial_moving), intent(inout) :: this
3293  ! @param target The target of the random walk, the walk should converge
3294  ! to the target within finite number of steps.
3295  class(spatial), intent(in) :: target
3296  ! @param meanshift the mean shift along any of the three dimensions.
3297  real(SRP), intent(in) :: meanshift
3298  ! @param cv_shift the coefficient of variation for a single elementary
3299  ! shift in any of the three dimensions.
3300  real(SRP), intent(in) :: cv_shift
3301  ! @param environment_limits Limits of the environment area available for
3302  ! the random walk. The moving object cannot get beyond this limit.
3303  class(environment), intent(in), optional :: environment_limits
3304 
3305  ! Local test object that we test, should lay within the
3306  ! `environment_limits` environmental object.
3307  type(spatial) :: current_pos, test_object
3308 
3309  !...........................................................................
3310 
3311  !> ### Implementation details ###
3312  !> First, we get the current coordinates of the spatial object.
3313  current_pos = this%now()
3314 
3315  !> And set a temporary spatial test object with the new coordinates,
3316  !! advancing/adding our random walk step. This is done via the
3317  !! `updated_position` sub-function.
3318  call test_object%position( spatial( &
3319  updated_position(target%x, current_pos%x), &
3320  updated_position(target%y, current_pos%y), &
3321  updated_position(target%depth, current_pos%depth) ) )
3322 
3323  !> Environment restriction part of the random walk, if `environment_limits`
3324  !! parameter object is provided. Here the object is not allowed to go beyond
3325  !! its bounding environment.
3326  environ_restrict: if (present(environment_limits)) then
3327  !> Loop while this new test spatial object is outside
3328  !! our target environment. It must be **strictly** within.
3329  do while (.NOT. test_object%is_within(environment_limits))
3330  !> (if the `test_object` is outside the environment, we create new
3331  !! randomly updated coordinates.)
3332  call test_object%position( spatial( &
3333  updated_position(target%x, current_pos%x), &
3334  updated_position(target%y, current_pos%y), &
3335  updated_position(target%depth, current_pos%depth) ) )
3336  end do
3337  end if environ_restrict
3338 
3339  !> Finally, change the current position of the `this` object to the
3340  !! position defined by the `test_object`. The standard function `position`
3341  !! for the `SPATIAL_MOVING`is used, that keeps the movement history.
3342  call this%position(test_object)
3343 
3344  contains
3345  !> Calculate a Gaussian random updated coordinate for multidimensional
3346  !! Gaussian *targeted* random walk along any of the dimensions. The dalta
3347  !! shift has value and variance but may be either forward or backward
3348  !! so as to *minimise the distance from the target*.
3349  function updated_position(coord_target, coord_object) result (coord_new)
3350  !> @return Updated coordinate.
3351  real(srp) :: coord_new
3352  !> @param coord_target axis-bound coordinate of the target.
3353  !> @param coord_object actual axis-bound coordinate of the moving
3354  !! spatial object.
3355  real(srp), intent(in) :: coord_target, coord_object
3356  ! Local random Gaussian coordinate shift.
3357  real(srp) :: delta
3358  ! We first check if the axis-bound distance between the object and the
3359  ! target is less then the mean shift....
3360  if ( abs(coord_target-coord_object) < meanshift ) then
3361  ! is so, we have now reached the target successfully.
3362  coord_new = coord_target
3363  else
3364  ! We first calculate a Gaussian random shift along this coordinate,
3365  ! the absolute value.
3366  delta = rnorm( meanshift, cv2variance(cv_shift, meanshift) )
3367  ! Then we should make sure the object is actually approaching
3368  ! the target. So we choose the direction that minimises the new
3369  ! coordinate-bound distance between the object and the target.
3370  if ( abs(coord_target-(coord_object-delta)) < &
3371  abs(coord_target-(coord_object+delta)) ) then
3372  coord_new = coord_object-delta
3373  else
3374  coord_new = coord_object+delta
3375  end if
3376  end if
3377  end function updated_position
3378 
3380 
3381  !-----------------------------------------------------------------------------
3382  !> @brief Implements an optionally environment-restricted **directional**
3383  !! Gaussian random walk in "2.5"-D towards a `target`
3384  !! the_environment::spatial class object. i.e. 2D x y with
3385  !! separate walk parameters for the third depth dimension.
3386  !! @details The moving object walks in three dimensions towards a target.
3387  !! The process is simple, first shift along the x axis for some
3388  !! random Gaussian length (with the mean `meanshift` and the
3389  !! variance/CV `cv_shift`) in the direction that minimises the
3390  !! coordinate-bound distance from the target. If the target is
3391  !! located at a distance not exceeding the `meanshift` we have
3392  !! got towards the target. Then the process is repeated for the
3393  !! y and z axes. The optional restriction is that the whole walk
3394  !! must not exceed specific spatial location set by the environment
3395  !! parameter.
3396  !! @note This `dirwalk` is a obsolete suboptimal implementation
3397  !! See `the_environment::spatial_moving_corwalk_gaussian_step_3d()`
3398  !! and `the_environment::spatial_moving_corwalk_gaussian_step_25d()`
3399  !! for a better alternative.
3400  !! @param[in] target The target of the random walk, the walk should converge
3401  !! to the target within finite number of steps.
3402  !! @param[in] meanshift_xy the mean shift along the X and Y dimensions.
3403  !! @param[in] cv_shift_xy the coefficient of variation for a single
3404  !! elementary shift the X and Ydimensions.
3405  !! @param[in] meanshift_depth the mean shift along the depth dimension.
3406  !! @param[in] cv_shift_depth the coefficient of variation for a single
3407  !! elementary shift in the depth dimension.
3408  !! @param[in] environment_limits Limits of the environment area available for
3409  !! the random walk. The moving object cannot get beyond this limit.
3410  subroutine spatial_moving_dirwalk_gaussian_step_25d(this, target, &
3411  meanshift_xy, &
3412  cv_shift_xy, &
3413  meanshift_depth, &
3414  cv_shift_depth, &
3415  environment_limits)
3416  class(spatial_moving), intent(inout) :: this
3417  ! @param target The target of the random walk, the walk should converge
3418  ! to the target within finite number of steps.
3419  class(spatial), intent(in) :: target
3420  ! @param meanshift the mean shift along any of the three dimensions.
3421  real(SRP), intent(in) :: meanshift_xy, meanshift_depth
3422  ! @param cv_shift the coefficient of variation for a single elementary
3423  ! shift in any of the three dimensions.
3424  real(SRP), intent(in) :: cv_shift_xy, cv_shift_depth
3425  ! @param environment_limits Limits of the environment area available for
3426  ! the random walk. The moving object cannot get beyond this limit.
3427  class(environment), intent(in), optional :: environment_limits
3428 
3429  ! Local test object that we test, should lay within the
3430  ! `environment_limits` environmental object.
3431  type(spatial) :: current_pos, test_object
3432 
3433  !...........................................................................
3434 
3435  !> ### Implementation details ###
3436  !> First, we get the current coordinates of the spatial object.
3437  current_pos = this%now()
3438 
3439  !> And set a temporary spatial test object with the new coordinates,
3440  !! advancing/adding our random walk step. This is done via the
3441  !! `updated_position` sub-function.
3442  call test_object%position( spatial( &
3443  updated_position(target%x, current_pos%x, &
3444  meanshift_xy, cv_shift_xy), &
3445  updated_position(target%y, current_pos%y, &
3446  meanshift_xy, cv_shift_xy), &
3447  updated_position(target%depth, current_pos%depth, &
3448  meanshift_depth, cv_shift_depth) ) )
3449 
3450  !> Environment restriction part of the random walk, if `environment_limits`
3451  !! parameter object is provided. Here the object is not allowed to go beyond
3452  !! its bounding environment.
3453  environ_restrict: if (present(environment_limits)) then
3454  !> Loop while this new test spatial object is outside
3455  !! our target environment. It must be **strictly** within.
3456  do while (.NOT. test_object%is_within(environment_limits))
3457  !> (if the `test_object` is outside the environment, we create new
3458  !! randomly updated coordinates.)
3459  call test_object%position( spatial( &
3460  updated_position(target%x, current_pos%x, &
3461  meanshift_xy, cv_shift_xy), &
3462  updated_position(target%y, current_pos%y, &
3463  meanshift_xy, cv_shift_xy), &
3464  updated_position(target%depth, current_pos%depth, &
3465  meanshift_depth, cv_shift_depth) ) )
3466  end do
3467  end if environ_restrict
3468 
3469  !> Finally, change the current position of the `this` object to the
3470  !! position defined by the `test_object`. The standard function `position`
3471  !! for the `SPATIAL_MOVING`is used, that keeps the movement history.
3472  call this%position(test_object)
3473 
3474  contains
3475  !> Calculate a Gaussian random updated coordinate for multidimensional
3476  !! Gaussian *targeted* random walk along any of the dimensions. The dalta
3477  !! shift has value and variance but may be either forward or backward
3478  !! so as to *minimise the distance from the target*.
3479  function updated_position(coord_target, coord_object, meanshift, cv_shift) &
3480  result(coord_new)
3481  !> @return Updated coordinate.
3482  real(srp) :: coord_new
3483  !> @param coord_target axis-bound coordinate of the target.
3484  !> @param coord_object actual axis-bound coordinate of the moving
3485  !! spatial object.
3486  real(srp) :: coord_target, coord_object
3487  !! @param[in] meanshift Gaussian distribution parameters for the step size:
3488  !! mean positional shift.
3489  !! @param[in] cv_shift Gaussian distribution parameters for the step size:
3490  !! coefficient of variation.
3491  real(srp) :: meanshift, cv_shift
3492  ! Local random Gaussian coordinate shift.
3493  real(srp) :: delta
3494  !> ### Implementation details ###
3495  !> We first check if the axis-bound distance between the object and the
3496  !! target is less then the mean shift...
3497  if ( abs(coord_target-coord_object) < meanshift ) then
3498  !> If so, we have now reached the target successfully.
3499  coord_new = coord_target
3500  else
3501  !> If not, first calculate a Gaussian random shift along this
3502  !! coordinate, the absolute value.
3503  delta = rnorm( meanshift, cv2variance(cv_shift, meanshift) )
3504  !> Then we should make sure the object is actually approaching
3505  !! the target. So we choose the direction that minimises the new
3506  !! coordinate-bound distance between the object and the target.
3507  if ( abs(coord_target-(coord_object-delta)) < &
3508  abs(coord_target-(coord_object+delta)) ) then
3509  coord_new = coord_object-delta
3510  else
3511  coord_new = coord_object+delta
3512  end if
3513  end if
3514  end function updated_position
3515 
3517 
3518  !-----------------------------------------------------------------------------
3519  !> Perform one or several steps of random walk by an array of
3520  !! the_environment::spatial_moving class objects. This is a 3D version
3521  !! with the same walk parameters for the horizontal *XxY* plane and *depth*.
3522  subroutine rwalk3d_array( this, dist_array, cv_array, &
3523  dist_all, cv_all, &
3524  environment_limits, n_walks )
3525  !> @param[in] this is an array of the_environment::spatial class objects.
3526  class(spatial_moving), dimension(:), intent(inout) :: this
3527  !> @param[in] step_size_array an array of step sizes for each object.
3528  real(SRP), optional, dimension(:), intent(in) :: dist_array
3529  !> @param[in]cv_array Coefficients of variation for the walk.
3530  real(SRP), optional, dimension(:), intent(in) :: cv_array
3531  !> @param[in] dist_all the value of the walk step size that is identical in
3532  !! all objects in the array.
3533  real(SRP), optional, intent(in) :: dist_all
3534  !> @param[in] cv_all the value of the walk coefficient of variation that is
3535  !! identical in all objects in the array.
3536  real(SRP), optional, intent(in) :: cv_all
3537  !> @param environment_limits Limits of the environment area available for
3538  !! the random walk. The moving object cannot get beyond this limit.
3539  !! If this parameter is not provided, the environmental limits are
3540  !! obtained automatically from the global array
3541  !! the_environment::global_habitats_available.
3542  class(environment), intent(in), optional :: environment_limits
3543  !> @param[in] n_walk optional number of walk steps that should be
3544  !! performed, default just one.
3545  integer, optional, intent(in) :: n_walks
3546 
3547  ! Local variables, copies of optionals.
3548  real(SRP), dimension(size(this)) :: dist_array_here, cv_array_here
3549  integer :: n_walks_here
3550 
3551  ! Local params.
3552  real(SRP), dimension(size(this)) :: step_size_walk
3553  integer :: j, i, ind, pop_n
3554  integer, dimension(size(this)) :: pop_permutation
3555 
3556  ! Default step size
3557  real(SRP), parameter :: STEP_DEFAULT = 1.0_srp
3558 
3559  ! Default walk step CV.
3560  real(SRP), parameter :: CV_DEFAULT = 0.5_srp
3561 
3562  !> ### Implementation details ###
3563  !> - Calculate the distance array size.
3564  pop_n = size(this)
3565 
3566  if (present(dist_array)) then
3567  dist_array_here = dist_array
3568  else
3569  dist_array_here = step_default
3570  end if
3571 
3572  if (present(cv_array)) then
3573  cv_array_here = cv_array
3574  else
3575  cv_array_here = cv_default
3576  end if
3577 
3578  if (present(dist_all)) then
3579  dist_array_here = dist_all
3580  else
3581  dist_array_here = step_default
3582  end if
3583 
3584  if (present(cv_all)) then
3585  cv_array_here = cv_all
3586  else
3587  cv_array_here = cv_default
3588  end if
3589 
3590  if (present(n_walks)) then
3591  n_walks_here = n_walks
3592  else
3593  n_walks_here = 1
3594  end if
3595 
3596  !> - calculate the step size along the axes from the distance array.
3597  step_size_walk = dist2step(dist_array_here)
3598 
3599  !> - Calculate the random permutation of individual indices.
3600  !! @warning Random order here is a prototype for testing for use in
3601  !! behaviour selection by population members.
3602  pop_permutation = permute_random(pop_n)
3603 
3604  !> - Perform Gaussian random walks for each of the individuals in a random
3605  !! order that is set by the `pop_permutation` array.
3606  select type (this)
3607  !> - if the input objects array is of the_environment::food_item,
3608  !! also check if it is available (not eaten) using the
3609  !! the_environment::food_item::is_available() method.
3610  !! .
3611  class is (food_item)
3612  environ_restrict_food: if (present(environment_limits)) then
3613  do j=1, n_walks_here
3614  do i=1, pop_n
3615  ind = pop_permutation(i)
3616  if (this(ind)%is_available()) &
3617  call this(ind)%rwalk( step_size_walk(ind), &
3618  cv_array_here(ind), &
3619  environment_limits )
3620  end do
3621  end do
3622  else environ_restrict_food
3623  do j=1, n_walks_here
3624  do i=1, pop_n
3625  ind = pop_permutation(i)
3626  if (this(ind)%is_available()) &
3627  call this(ind)%rwalk( step_size_walk(ind), &
3628  cv_array_here(ind), &
3630  this(ind)%find_environment( &
3632  end do
3633  end do
3634  end if environ_restrict_food
3635  !> - in the default class case, no such check is made.
3636  !! .
3637  !! .
3638  class default
3639  environ_restrict_def: if (present(environment_limits)) then
3640  do j=1, n_walks_here
3641  do i=1, pop_n
3642  ind = pop_permutation(i)
3643  call this(ind)%rwalk( step_size_walk(ind), &
3644  cv_array_here(ind), &
3645  environment_limits )
3646  end do
3647  end do
3648  else environ_restrict_def
3649  do j=1, n_walks_here
3650  do i=1, pop_n
3651  ind = pop_permutation(i)
3652  call this(ind)%rwalk( step_size_walk(ind), &
3653  cv_array_here(ind), &
3655  this(ind)%find_environment( &
3657  end do
3658  end do
3659  end if environ_restrict_def
3660  end select
3661  ! @note Note that the `select type` construct is placed out of the loops;
3662  ! this results in code duplication but avoids multiple calling of
3663  ! `select type` construct within the loops, which would increase
3664  ! speed.
3665 
3666  end subroutine rwalk3d_array
3667 
3668  !-----------------------------------------------------------------------------
3669  !> Perform one or several steps of random walk by an array of
3670  !! the_environment::spatial_moving class objects. This is a 2.5D version
3671  !! with separate walk parameters for the horizontal *XxY* plane and *depth*.
3672  subroutine rwalk25d_array ( this, dist_array_xy, cv_array_xy, &
3673  dist_array_depth, cv_array_depth, &
3674  dist_all_xy, cv_all_xy, &
3675  dist_all_depth, cv_all_depth, &
3676  environment_limits, n_walks )
3677  !> @param[in] this is an array of the_environment::spatial class objects.
3678  class(spatial_moving), dimension(:), intent(inout) :: this
3679  !> @param[in] dist_array_xy an array of step sizes for each object.
3680  real(SRP), optional, dimension(:), intent(in) :: dist_array_xy
3681  !> @param[in]cv_array_xy Coefficients of variation for the walk.
3682  real(SRP), optional, dimension(:), intent(in) :: cv_array_xy
3683  !> @param[in] dist_array_depth an array of step sizes for each object.
3684  real(SRP), optional, dimension(:), intent(in) :: dist_array_depth
3685  !> @param[in]cv_array_depth Coefficients of variation for the walk.
3686  real(SRP), optional, dimension(:), intent(in) :: cv_array_depth
3687  !> @param[in] dist_all_xy the value of the walk step size for horizontal
3688  !! plane that is identical in all objects in the array.
3689  real(SRP), optional, intent(in) :: dist_all_xy
3690  !> @param[in] cv_all_xy the value of the walk coefficient of variation in
3691  !! the horizontal plane that is identical in all objects within
3692  !! the array.
3693  real(SRP), optional, intent(in) :: cv_all_xy
3694  !> @param[in] dist_all_depth the value of the walk step size for the depth
3695  !! plane that is identical in all objects in the array.
3696  real(SRP), optional, intent(in) :: dist_all_depth
3697  !> @param[in] cv_all_depth the value of the walk coefficient of variation
3698  !! in the depth plane that is identical in all objects in the
3699  !! array.
3700  real(SRP), optional, intent(in) :: cv_all_depth
3701  !> @param environment_limits Limits of the environment area available for
3702  !! the random walk. The moving object cannot get beyond this limit.
3703  !! If this parameter is not provided, the environmental limits are
3704  !! obtained automatically from the global array
3705  !! the_environment::global_habitats_available.
3706  class(environment), intent(in), optional :: environment_limits
3707  !> @param[in] n_walk optional number of walk steps that should be
3708  !! performed, default just one.
3709  integer, optional, intent(in) :: n_walks
3710 
3711  ! Local variables, copies of optionals.
3712  real(SRP), dimension(size(this)) :: dist_array_xy_here, cv_array_xy_here
3713  real(SRP), dimension(size(this)) :: &
3714  dist_array_depth_here, cv_array_depth_here
3715  integer :: n_walks_here
3716 
3717  ! Local params.
3718  real(SRP), dimension(size(this)) :: step_size_walk_xy, step_size_walk_depth
3719  integer :: j, i, ind, pop_n
3720  integer, dimension(size(this)) :: pop_permutation
3721 
3722  ! Default step size
3723  real(SRP), parameter :: STEP_DEFAULT = 1.0_srp
3724 
3725  ! Default walk step CV.
3726  real(SRP), parameter :: CV_DEFAULT = 0.5_srp
3727 
3728  !> ### Implementation details ###
3729  !> - Calculate the distance array size.
3730  pop_n = size(this)
3731 
3732  if (present(dist_array_xy)) then
3733  dist_array_xy_here = dist_array_xy
3734  else
3735  dist_array_xy_here = step_default
3736  end if
3737 
3738  if (present(cv_array_xy)) then
3739  cv_array_xy_here = cv_array_xy
3740  else
3741  cv_array_xy_here = cv_default
3742  end if
3743 
3744  !> - If the depth walk step distance is not provided as a parameter,
3745  !! 1/2 of the default step size is used as the default value. Thus,
3746  !! it is assumed that the extent of random movements of the agents
3747  !! in the horizontal plane is greater than vertical movements.
3748  if (present(dist_array_depth)) then
3749  dist_array_depth_here = dist_array_depth
3750  else
3751  dist_array_depth_here = step_default / 2.0_srp
3752  end if
3753 
3754  if (present(cv_array_depth)) then
3755  cv_array_depth_here = cv_array_depth
3756  else
3757  cv_array_depth_here = cv_default
3758  end if
3759 
3760  if (present(dist_all_xy)) then
3761  dist_array_xy_here = dist_all_xy
3762  else
3763  dist_array_xy_here = step_default
3764  end if
3765 
3766  if (present(cv_all_xy)) then
3767  cv_array_xy_here = cv_all_xy
3768  else
3769  cv_array_xy_here = cv_default
3770  end if
3771 
3772  if (present(dist_all_depth)) then
3773  dist_array_depth_here = dist_all_depth
3774  else
3775  dist_array_depth_here = step_default / 2.0_srp
3776  end if
3777 
3778  if (present(cv_all_depth)) then
3779  cv_array_depth_here = cv_all_depth
3780  else
3781  cv_array_depth_here = cv_default
3782  end if
3783 
3784  if (present(n_walks)) then
3785  n_walks_here = n_walks
3786  else
3787  n_walks_here = 1
3788  end if
3789 
3790  !> - Calculate the step size along the axes from the distance array.
3791  step_size_walk_xy = dist2step(dist_array_xy_here)
3792  step_size_walk_depth = dist2step(dist_array_depth_here)
3793 
3794  !> - Calculate the random permutation of individual indices.
3795  !! @warning Random order here is a prototype for testing for use in
3796  !! behaviour selection by population members.
3797  pop_permutation = permute_random(pop_n)
3798 
3799  !> - Perform Gaussian random walks for each of the objects in a random
3800  !! order that is set by the `pop_permutation` array.
3801  !! .
3802  select type (this)
3803  !> - if the input objects array is of the_environment::food_item,
3804  !! also check if it is available (not eaten) using the
3805  !! the_environment::food_item::is_available() method.
3806  !! .
3807  class is (food_item)
3808  environ_restrict_food: if (present(environment_limits)) then
3809  do j=1, n_walks_here
3810  do i=1, pop_n
3811  ind = pop_permutation(i)
3812  if(this(ind)%is_available()) &
3813  call this(ind)%rwalk25d &
3814  ( meanshift_xy = step_size_walk_xy(ind), &
3815  cv_shift_xy = cv_array_xy_here(ind), &
3816  meanshift_depth = step_size_walk_depth(ind), &
3817  cv_shift_depth = cv_array_depth_here(ind), &
3818  environment_limits = environment_limits )
3819  end do
3820  end do
3821  else environ_restrict_food
3822  do j=1, n_walks_here
3823  do i=1, pop_n
3824  ind = pop_permutation(i)
3825  if(this(ind)%is_available()) &
3826  call this(ind)%rwalk25d &
3827  ( meanshift_xy = step_size_walk_xy(ind), &
3828  cv_shift_xy = cv_array_xy_here(ind), &
3829  meanshift_depth = step_size_walk_depth(ind), &
3830  cv_shift_depth = cv_array_depth_here(ind), &
3831  environment_limits=global_habitats_available( &
3832  this(ind)%find_environment( &
3834  end do
3835  end do
3836  end if environ_restrict_food
3837  !> - in the default class case, no such check is made.
3838  !! .
3839  !! .
3840  class default
3841  environ_restrict_def: if (present(environment_limits)) then
3842  do j=1, n_walks_here
3843  do i=1, pop_n
3844  ind = pop_permutation(i)
3845  call this(ind)%rwalk25d &
3846  ( meanshift_xy = step_size_walk_xy(ind), &
3847  cv_shift_xy = cv_array_xy_here(ind), &
3848  meanshift_depth = step_size_walk_depth(ind), &
3849  cv_shift_depth = cv_array_depth_here(ind), &
3850  environment_limits = environment_limits )
3851  end do
3852  end do
3853  else environ_restrict_def
3854  do j=1, n_walks_here
3855  do i=1, pop_n
3856  ind = pop_permutation(i)
3857  call this(ind)%rwalk25d &
3858  ( meanshift_xy = step_size_walk_xy(ind), &
3859  cv_shift_xy = cv_array_xy_here(ind), &
3860  meanshift_depth = step_size_walk_depth(ind), &
3861  cv_shift_depth = cv_array_depth_here(ind), &
3862  environment_limits=global_habitats_available( &
3863  this(ind)%find_environment( &
3865  end do
3866  end do
3867  end if environ_restrict_def
3868  end select
3869  ! @note Note that the `select type` construct is placed out of the loops;
3870  ! this results in code duplication but avoids multiple calling of
3871  ! `select type` construct within the loops, which would increase
3872  ! speed.
3873 
3874  end subroutine rwalk25d_array
3875 
3876  !-----------------------------------------------------------------------------
3877  !> Function to check if this spatial object is located within an area
3878  !! set by an environmental object (parameter). This should be similar
3879  !! to an analogous function defined for the environment object.
3880  !! @param environment_limits `the_environment::environment` object that
3881  !! sets the limits that we check the current spatial object to
3882  !! be within.
3883  !! @returns Logical flag TRUE if the the_environment::spatial object is
3884  !! within the `environment_limits` environment.
3885  !! @note Can be used as a user-defined operator:
3886  !! @code
3887  !! if ( object .within. environment ) then
3888  !! @endcode
3889  !! @note We need it to implement environment-restricted Gaussian
3890  !! random walk.
3891  elemental function spatial_check_located_within_3d(this, environment_limits)&
3892  result(is_within)
3893  class(spatial), intent(in) :: this
3894 
3895  ! @param environment_limits `the_environment::environment` object that sets
3896  ! the limits that we check the current spatial object to be within.
3897  class(environment), intent(in) :: environment_limits
3898  ! @returns Logical flag TRUE if the the_environment::spatial object is
3899  ! within the `environment_limits` environment.
3900  logical :: is_within
3901 
3902  is_within = environment_limits%is_within(this)
3903 
3904  end function spatial_check_located_within_3d
3905 
3906  !-----------------------------------------------------------------------------
3907  !> Logical function to check if the **argument** spatial object(s)
3908  !! (`check_object`) is (are) located **below** the **this** reference
3909  !! spatial object. Elemental function that also works with arrays. Use as:
3910  !! @code
3911  !! reference_object%is_below(check_object)
3912  !! @endcode
3913  !! See also the user-defined operator `.below.`.
3914  !! The `.below.` operator can be used in two ways:
3915  !! - as an expression, with both scalar and array values:
3916  !! @code
3917  !! parents%ind(i) .below. parents%ind(i)%perceive_food%foods_seen
3918  !! @endcode
3919  !! - in if blocks, only **scalars**:
3920  !! @code
3921  !! if ( parents%ind(i) .below. parents%ind(i)%perceive_food%foods_seen(1) )
3922  !! @endcode
3923  !! .
3924  elemental function spatial_check_located_below(this, check_object) &
3925  result(are_below)
3926  class(spatial), intent(in) :: this
3927  class(spatial), intent(in) :: check_object
3928  logical :: are_below
3929 
3930  if ( check_object%dpos() > this%dpos() ) then
3931  are_below = .true.
3932  else
3933  are_below = .false.
3934  end if
3935 
3936  end function spatial_check_located_below
3937 
3938  !-----------------------------------------------------------------------------
3939  !> Logical function to check if the **argument** spatial object(s)
3940  !! (`check_object`) is (are) located **above** the **this** reference
3941  !! spatial object. Elemental function that also works with arrays. Use as:
3942  !! @code
3943  !! reference_object%is_above(check_object)
3944  !! @endcode
3945  !! See also the user-defined operator `.above.`.
3946  !! The `.above.` operator can be used in two ways:
3947  !! - as an expression, with both scalar and array values:
3948  !! @code
3949  !! parents%ind(i) .above. parents%ind(i)%perceive_food%foods_seen
3950  !! @endcode
3951  !! - in if blocks, only **scalars**:
3952  !! @code
3953  !! if ( parents%ind(i) .above. parents%ind(i)%perceive_food%foods_seen(1) )
3954  !! @endcode
3955  !! .
3956  elemental function spatial_check_located_above(this, check_object) &
3957  result(are_above)
3958  class(spatial), intent(in) :: this
3959  class(spatial), intent(in) :: check_object
3960  logical :: are_above
3961 
3962  if ( check_object%dpos() < this%dpos() ) then
3963  are_above = .true.
3964  else
3965  are_above = .false.
3966  end if
3967 
3968  end function spatial_check_located_above
3969 
3970  !-----------------------------------------------------------------------------
3971  !> Determine the nearest spatial object to **this** spatial object among
3972  !! an array of other spatial objects.
3973  !! @note These two functions closely related, they return the nearest *object*
3974  !! and its *id*
3975  !! - the_environment::spatial_get_nearest_object()
3976  !! - the_environment::spatial_get_nearest_id()
3977  !! .
3978  !! However, each of them can also return the other output parameter
3979  !! as an intent(out) optional argument (in the subroutine style).
3980  !! For example the_environment::spatial_get_nearest_object() returns
3981  !! the nearest *object* but can also provide its *id* as an
3982  !! intent(out) dummy parameter. This is done for convenience of the
3983  !! function use.
3984  !! @param neighbours the array of spatial objects that we search for the
3985  !! nearest one.
3986  !! @param number Optional number of the nearest object within the
3987  !! neighbours array.
3988  !! @returns Returns the nearest spatial object among the array.
3989  !! @note This function returns the nearest spatial **object itself** with
3990  !! optionally its number. See also the next function
3991  !! `spatial_get_nearest_id`.
3992  function spatial_get_nearest_object (this, neighbours, number) result (object)
3993  class(spatial), intent(in) :: this
3994 
3995  ! @param neighbours the array of spatial objects that we search for the
3996  ! nearest one.
3997  class(spatial), dimension(:), intent(in) :: neighbours
3998 
3999  ! @param number Optional number of the nearest object within the
4000  ! neighbours array.
4001  integer, optional, intent(out) :: number
4002 
4003  ! @returns Returns the nearest spatial object among the array.
4004  type(spatial) :: object
4005 
4006  ! Local counter and the copy of the optional output number of the
4007  ! nearest neighbour.
4008  integer :: i, number_here
4009 
4010  ! Local array of distances between this spatial object and its
4011  ! neighbours (the array))
4012  real(srp), dimension(size(neighbours)) :: distance
4013 
4014  ! Initialise the number of the closest neighbour.
4015  number_here = unknown
4016 
4017  !> ### Implementation details ###
4018  !> Calculate an array of distances between this object and the
4019  !! neighbouring objects using the_environment::spatial::distance().
4020  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
4021  ! implemented in all compilers. Use normal `do` in such a case.
4022  ! Using `do concurrent` seems cleaner than this one-liner:
4023  ! `distance = spatial_distance_3d( this, neighbours )`.
4024  do concurrent( i = 1:size(neighbours) )
4025  distance(i) = this%distance(neighbours(i))
4026  end do
4027 
4028  !> Locate the index of the minimum distance.
4029  number_here = minloc(distance,1)
4030 
4031  !> And return the the_environment::spatial::location() function result for
4032  !! this neighbour as the **resultant object**.
4033  object = neighbours(number_here)%location()
4034 
4035  !> Also return the optional nearest neighbour index number if requested.
4036  if(present(number)) number = number_here
4037 
4038  end function spatial_get_nearest_object
4039 
4040  !-----------------------------------------------------------------------------
4041  !> Determine the nearest spatial object to **this** spatial object among
4042  !! an array of other spatial objects.
4043  !! @note These two functions closely related, they return the nearest *object*
4044  !! and its *id*
4045  !! - the_environment::spatial_get_nearest_object()
4046  !! - the_environment::spatial_get_nearest_id()
4047  !! .
4048  !! However, each of them can also return the other output parameter
4049  !! as an intent(out) optional argument (in the subroutine style).
4050  !! For example the_environment::spatial_get_nearest_object() returns
4051  !! the nearest *object* but can also provide its *id* as an
4052  !! intent(out) dummy parameter. This is done for convenience of the
4053  !! function use.
4054  !! @param neighbours the array of spatial objects that we search for the
4055  !! nearest one.
4056  !! @param object optional spatial object that is the nearest neighbour
4057  !! to **this** object.
4058  !! @returns The id number of the nearest object within the
4059  !! neighbours array
4060  !! @note This function returns the **id number** of the nearest spatial
4061  !! object and optionally the object itself. See also the previous
4062  !! function `spatial_get_nearest_object`.
4063  function spatial_get_nearest_id (this, neighbours, object) result (id)
4064  class(spatial), intent(in) :: this
4065 
4066  ! @param neighbours the array of spatial objects that we search for the
4067  ! nearest one.
4068  class(spatial), dimension(:), intent(in) :: neighbours
4069 
4070  ! @param object optional spatial object that is the nearest neighbour
4071  ! to **this** object.
4072  type(spatial), optional, intent(out) :: object
4073 
4074  ! @returns The id number of the nearest object within the
4075  ! neighbours array
4076  integer :: id
4077 
4078  ! Local counter.
4079  integer :: i
4080 
4081  ! Local array of distances between this spatial object and its
4082  ! neighbours (the array))
4083  real(srp), dimension(size(neighbours)) :: distance
4084 
4085  ! Initialise the optional nearest object as MISSING.
4086  if (present(object)) call object%missing()
4087 
4088  !> ### Implementation details ###
4089  !> Calculate an array of distances between this object and the
4090  !! neighbouring objects. Note that we cannot use a whole-array
4091  !! single-liner as `distance` is a type bound *function*.
4092  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
4093  ! implemented in all compilers. Use normal `do` in such a case.
4094  ! Using `do concurrent` seems cleaner than this one-liner:
4095  ! `distance = spatial_distance_3d( this, neighbours )`.
4096  do concurrent( i = 1:size(neighbours) )
4097  distance(i) = this%distance(neighbours(i))
4098  end do
4099 
4100  !> Return the index of the nearest neighbour.
4101  id = minloc(distance,1)
4102 
4103  !> Also return the optional nearest neighbour object itself if requested.
4104  if(present(object)) call object%position( neighbours(id)%location() )
4105 
4106  end function spatial_get_nearest_id
4107 
4108  !-----------------------------------------------------------------------------
4109  !> @brief Make an instance of the habitat object (an environment superset).
4110  !! @details Make / build habitat object and set parameters or defaults.
4111  !! @warning This subroutine seems to become quite *long* and difficult to
4112  !! understand. It also combines *several tasks*, making habitat
4113  !! limits, then array of predators then food resource. On the
4114  !! other hand, this init procedure is normally called only once.
4115  !! TODO: Consider splitting to a few shorter task-specific pieces.
4116  subroutine habitat_make_init(this, coord_min, coord_max, label, &
4117  otherrisks, eggmortality, &
4118  predators_number, loc_predators, &
4119  food_abundance, loc_food, sizes_food )
4120  class(habitat), intent(inout) :: this
4121 
4122  ! Parameters of the habitat, only label is mandatory, other can be
4123  ! obtained from defaults.
4124  type(spatial), intent(in) :: coord_min,coord_max ! coordinate limits;
4125  character (len=*),optional, intent(in) :: label ! name of the habitat;
4126  real(SRP), optional, intent(in) :: otherrisks ! other mortality risk;
4127  real(SRP), optional, intent(in) :: eggmortality ! predation risk.
4128 
4129  ! @param predators_number **mandatory** number of predators in the habitat.
4130  integer :: predators_number
4131 
4132  ! @param loc_predators An array of the spatial locations of each of
4133  ! the above predators. If not provided, will be uniformly
4134  ! distributed within this habitat.
4135  type(spatial), dimension(:), optional :: loc_predators
4136 
4137  ! @param food_abundance **mandatory** food abundance, the number of food
4138  ! items within the habitat.
4139  integer :: food_abundance
4140 
4141  ! @param loc_food an optional array of the locations of the
4142  ! food items within (bounded) this habitat. If not provided,
4143  ! will be uniformly distributrd within this habitat.
4144  type(spatial), dimension(:), optional :: loc_food
4145 
4146  ! @param sizes_food an optional array of the food sizes. If not provided,
4147  ! will be Gaussian stochastic.
4148  real(SRP), dimension(:), optional :: sizes_food
4149 
4150  ! Copies of optional parameters.
4151  real(SRP) :: predation_here ! predation risk;
4152  real(SRP) :: otherrisks_here ! other mortality risk;
4153  real(SRP) :: eggmortality_here ! predation risk.
4154 
4155  ! Local array of the locations of the predators.
4156  type(spatial), allocatable, dimension(:) :: loc_pred_here
4157 
4158  ! Local array of locations of food items.
4159  type(spatial), allocatable, dimension(:) :: loc_food_here
4160 
4161  ! Local copy of the food size array
4162  real(SRP), allocatable, dimension(:) :: sizes_food_here
4163 
4164  integer :: i ! Local counter.
4165 
4166  !...........................................................................
4167 
4168  !> ### Implementation details ###
4169  !> #### A. Build the **general properties** of the habitat ####
4170 
4171  !> Build the physical spatial **environment** for this **habitat**,
4172  !! set the habitat coordinate limits.
4173  call this%build(coord_min, coord_max)
4174 
4175  !> Set label if provided or default random label otherwise.
4176  if (present(label)) then
4177  this%habitat_name = label
4178  else
4179  this%habitat_name = "HAB_" // rand_string( label_length - len("HAB_"), &
4181  end if
4182 
4183  ! Set the level of other mortality risks.
4184  if (present(otherrisks)) then
4185  otherrisks_here = otherrisks
4186  else
4187  otherrisks_here = other_risks_def
4188  end if
4189  this%risk_mortality = otherrisks_here
4190 
4191  ! Set egg mortality risk.
4192  if (present(eggmortality)) then
4193  eggmortality_here = eggmortality
4194  else
4195  eggmortality_here = eggmortality_def
4196  end if
4197  this%risk_egg_mortality = eggmortality_here
4198 
4199  !...........................................................................
4200  !> #### B. Build the **population of predators** in the habitat ####
4201  !> Set the number of predators.
4202  this%predators_number = predators_number
4203 
4204  !> Allocate the local array of predator locations.
4205  if (.not. allocated(loc_pred_here)) &
4206  allocate(loc_pred_here(this%predators_number))
4207 
4208  !> If we are provided with the array of spatial locations of each of
4209  !! the predators, we set the local array `loc_pred_here` from it.
4210  if (present(loc_predators)) then
4211  loc_pred_here = loc_predators
4212  else
4213  !> If the array of locations is *not* present, construct *uniform*
4214  !! random distriibution of the predators locations within the present
4215  !! environment (bounded uniformly distributed locations). Now use
4216  !! the type bound function `uniform`.
4217  loc_pred_here = this%uniform(this%predators_number)
4218  end if
4219 
4220  !> Now we can **allocate** the **array of predators** in the habitat.
4221  !! @note Note that we have to allocate predators here, unlike the food
4222  !! resource part below as predators are just a raw array in this
4223  !! type definition. In contrast, food resource is an object itself
4224  !! with its own `make` procedure (that does allocation).
4225  if (.not. allocated(this%predators)) &
4226  allocate(this%predators(this%predators_number))
4227 
4228  !> Make each of the predators using the call parameters of the
4229  !! object bound init function `make (attack_rate, position, label)`.
4230  !! @note Note that the attack rate is assumed to be Gaussian among
4231  !! the predators. Also, we do two separate loops for speed (avoid
4232  !! multiple repeated *if*'s within cycles)
4234  !> If we happen to get zero variance, do deterministic predators.
4235  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
4236  ! implemented in all compilers. Use normal `do` in such a case.
4237  do concurrent( i = 1:this%predators_number )
4238  call this%predators(i)%make( &
4239  body_size=predator_body_size, &
4240  attack_rate=predator_attack_rate_default, &
4241  position=loc_pred_here(i), &
4242  label="PRED_" // tostr(i,this%predators_number) )
4243  end do
4244  else
4245  !> Generate Gaussian stochastic predators with the object bound `make`
4246  !! procedure.
4247  ! @warning Cannot use `do concurrent` here as `RNORM` is not **pure**.
4248  do i = 1, this%predators_number
4249  call this%predators(i)%make( &
4250  body_size=predator_body_size, &
4251  attack_rate=rnorm(predator_attack_rate_default, &
4254  position=loc_pred_here(i), &
4255  label="PRED_" // tostr(i,this%predators_number) )
4256  end do
4257  end if
4258 
4259  !> Deallocate the local array of predator locations, we do not need them
4260  !! any more further.
4261  if (allocated(loc_pred_here)) deallocate (loc_pred_here)
4262 
4263  !...........................................................................
4264  !> #### C. Build the **food resource(s)** of the habitat ####
4265  !> Set the number of food items in the food resource within the habitat.
4266  this%food%number_food_items = food_abundance
4267 
4268  !> Allocate the local array of food item locations.
4269  if (.not. allocated(loc_food_here)) &
4270  allocate(loc_food_here(this%food%number_food_items))
4271 
4272  ! Check if we are provided with the locations of the food items.
4273  ! @note We use the class-safe type-bound function and loop rather
4274  ! than array assignment.
4275  if (present(loc_food)) then
4276  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
4277  ! implemented in all compilers. Use normal `do` in such a case.
4278  !do concurrent ( i=1:this%food%number_food_items )
4279  ! call loc_food_here(i)%position(loc_food(i))
4280  !end do
4281  ! @note Now do not use loop, use whole array with elemental function
4282  ! as it (seems) working well in Intel Fortran 17.
4283  call loc_food_here%position(loc_food)
4284  else
4285  !> If the array of locations is *not* present, construct *uniform*
4286  !! random distribution of the predators locations within the present
4287  !! environment (bounded uniformly distributed locations). Now use
4288  !! the type bound function `uniform`.
4289  ! @warning **Intel Fortran porting note**. This whole array function
4290  ! **does not work under Intel Fortran 13**, issues *stack
4291  ! overflow* runtime error, although compiles without issues:
4292  ! `loc_food_here = this%uniform(this%food%number_food_items)`.
4293  !do i=1, this%food%number_food_items
4294  ! loc_food_here(i)=this%uniform()
4295  !end do
4296  ! @note Now do not use loop, use whole array with elemental function
4297  ! as it (seems) working well in Intel Fortran 17.
4298  loc_food_here = this%uniform(this%food%number_food_items)
4299  end if
4300 
4301  !> And also allocate the local array of food sizes
4302  if (.not. allocated(sizes_food_here)) &
4303  allocate(sizes_food_here(this%food%number_food_items))
4304 
4305  !> If the food size array `sizes_food` is provided, use it, if not,
4306  !! make a Gaussian stochastic food with parameters from `COMMONDATA`.
4307  if (present(sizes_food)) then
4308  sizes_food_here = sizes_food
4309  else
4311  !> if we happened to get zero variance, do deterministic food resource.
4312  sizes_food_here = food_item_mean_size
4313  else
4314  !> Otherwise, generate random Gaussian array of food sizes.
4315  call rnorm_array( sizes_food_here, &
4319  end if
4320  end if
4321 
4322  !> **Make the food resource** now using the standard object-bound `make`
4323  !! function.
4324  !! @note Note that the food resource label is composed of `FOOD_` and
4325  !! the remaining part of the habitat label.
4326  !! @note Note that we do **not** allocate the **food resource object**
4327  !! in this procedure as it is allocated automatically by the
4328  !! food-resource-bound subroutine `make`.
4329  call this%food%make( "FOOD_" // this%habitat_name, &
4330  this%food%number_food_items, &
4331  loc_food_here, &
4332  sizes_food_here )
4333 
4334  !> Deallocate temporary array at the end.
4335  if (allocated(loc_food_here)) deallocate(loc_food_here)
4336  if (allocated(sizes_food_here)) deallocate(sizes_food_here)
4337 
4338  end subroutine habitat_make_init
4339 
4340  !-----------------------------------------------------------------------------
4341  !> Return the name of the habitat.
4342  function habitat_name_get(this) result(habitat_name)
4343  class(habitat), intent(in) :: this
4344  !> @return The habitat name (text string label).
4345  character(len=LABEL_LENGTH) :: habitat_name
4346 
4347  habitat_name = this%habitat_name
4348 
4349  end function habitat_name_get
4350 
4351  !-----------------------------------------------------------------------------
4352  !> Get the mortality risk associated with this habitat.
4353  function habitat_get_risk_mortality(this) result (value_out)
4354  class(habitat), intent(in) :: this
4355  !> @return The mortality risk in this population that is not linked
4356  !! with explicit predation.
4357  real(srp) :: value_out
4358 
4359  value_out = this%risk_mortality
4360 
4361  end function habitat_get_risk_mortality
4362 
4363  !-----------------------------------------------------------------------------
4364  !> Get the egg mortality risk associated with this habitat.
4365  function habitat_get_risk_mortality_egg(this) result (value_out)
4366  class(habitat), intent(in) :: this
4367  !> @return The mortality risk in this population that is not linked
4368  !! with explicit predation.
4369  real(srp) :: value_out
4370 
4371  value_out = this%risk_egg_mortality
4372 
4373  end function habitat_get_risk_mortality_egg
4374 
4375  !-----------------------------------------------------------------------------
4376  !> Save the predators with their characteristics into a CSV file.
4377  subroutine habitat_save_predators_csv(this, csv_file_name, is_success)
4378  class(habitat), intent(inout) :: this
4379  !> @param[in] csv_file_name optional file name to save the predators.
4380  !! Generated automatically if not provided.
4381  character(len=*), optional, intent(in) :: csv_file_name
4382 
4383  logical, optional, intent(out) :: is_success
4384 
4385  ! Local copies of optionals.
4386  character(len=FILENAME_LENGTH) :: csv_file_name_here
4387  logical :: is_success_write
4388 
4389  ! Counter
4390  integer :: i
4391 
4392  ! These are two character arrays to convert the numerical data into,
4393  ! due to the character label reshaped into the same array file.
4394  character(len=LABEL_LENGTH), dimension(size(this%predators%label)) :: &
4395  body_size_str, attack_rate_str
4396 
4397  !> ### Implementation notes ###
4398  !! First, check if the optional CSV file name is provided, if not,
4399  !! generate it automatically.
4400  if (present(csv_file_name)) then
4401  csv_file_name_here = csv_file_name
4402  else
4403  csv_file_name_here = "predators_" // trim(this%habitat_name) // "_" // &
4404  model_name // "_" // mmdd // "_gen_" // &
4406  generations) // csv
4407  end if
4408 
4409  !> Second, save the predators data using the
4410  !! [CSV_MATRIX_WRITE()](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_matrix_write)
4411  !! from [HEDTOOLS](http://ahamodel.uib.no/doc/).
4412  ! @note Note that all numerical data are converted to strings before
4413  ! reshape, this is because label is a string type and all data
4414  ! must be the same type in reshape.
4415  do concurrent(i=1:this%predators_number)
4416  body_size_str(i) = tostr(this%predators(i)%body_size)
4417  attack_rate_str(i) = tostr(this%predators(i)%attack_rate)
4418  end do
4419  is_success_write = .false.
4420  call csv_matrix_write( reshape( &
4421  [ body_size_str, &
4422  attack_rate_str, &
4423  this%predators%label ], &
4424  [ this%predators_number, 3 ] ), &
4425  ! ^ N. vars in reshape.
4426  csv_file_name_here, &
4427  [ character(len=LABEL_LENGTH) :: &
4428  "BODY_SIZE", "ATTACK_RATE", "LABEL" ], &
4429  is_success_write )
4430 
4431  if (present(is_success)) is_success = is_success_write
4432 
4433  !> The CSV output data file can be optionally compressed with the
4434  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
4435  !! to TRUE.
4436  if ( is_zip_outputs ) then
4437  call call_external(command=cmd_zip_output // " " // csv_file_name_here, &
4438  suppress_output=.true., &
4439  is_background_task=zip_outputs_background )
4440  end if
4441 
4442  end subroutine habitat_save_predators_csv
4443 
4444  !-----------------------------------------------------------------------------
4445  !> Save diagnostics data that shows the dynamics of the light and the
4446  !! average depth of the food items, light at the average depth of the food
4447  !! items etc at each time step of the model.
4448  !!
4449  !! Code to generate plots from these data with gnuplot. Only 1 to 100s rows
4450  !! are plotted, pattern is sinusoidal.
4451  !! @code
4452  !! set datafile separator ","
4453  !! set xlabel "Time steps of the model"
4454  !! set ylabel "SURFACE_LIGHT"
4455  !! plot "init_dynamics.csv" every ::1::100 using 1:2 with lines, \
4456  !! "init_dynamics.csv" every ::1::100 using 1:3 with lines, \
4457  !! "init_dynamics.csv" every ::1::100 using 1:4 with lines, \
4458  !! "init_dynamics.csv" every ::1::100 using 1:5 with lines
4459  !! @endcode
4460  subroutine save_dynamics(maxdepth, csv_file_name, is_success)
4461  use file_io
4462  use csv_io
4463  !> @param[in] maxdepth is an optional maximum depth, if absent, is set to
4464  !! the global maximum depth (autodetected) across all habitats.
4465  real(SRP), optional, intent(in) :: maxdepth
4466  !> @param[in] csv_file_name is the file name to save the data. The
4467  !! file format is CSV.
4468  character(len=*), intent(in) :: csv_file_name
4469  !> @param[out] is_success Flag showing that data save was successful
4470  !! (if TRUE).
4471  logical, optional, intent(out) :: is_success
4472 
4473  ! Local counter
4474  integer :: i
4475 
4476  ! File handle object and the string record that keeps each row of data.
4477  type(file_handle) :: out_file
4478  character(len=:), allocatable :: record_string
4479 
4480  ! The maximum depth
4481  real(SRP) :: maxdepth_loc
4482 
4483  ! A standard spatial object to calculate visibility
4484  type(spatial) :: object_std
4485  ! Visibility of a standard food item of mean size.
4486  real(SRP) :: visibility_food
4487 
4488  ! Column labels for the output file.
4489  !> The following data are saved in the CSV file:
4490  !! 1. `TIMESTEP` -- the time step of the model
4491  !! 2. `SURFACE_LIGHT` -- light at the surface
4492  !! 3. `LIGHT_DEP_10` -- light at 1/10 of the maximum depth
4493  !! 4. `LIGHT_DEP_HLF` -- light at 1/2 of the maximum depth
4494  !! 5. `LIGHT_DEP_MAX` -- light at the maximum depth
4495  !! 6. `MEAN_DEPTH` -- the mean depth of the food items (target depth)
4496  !! 7. `LIGHT_MDEPTH` -- light at the mean depth of the food items.
4497  !! 8. `FOOD_VIS_SURF` -- visibility range of a standard food item at the
4498  !! surface (zero depth)
4499  !! 9. `FOOD_VIS_10` -- visibility range of a standard food item at 1/10
4500  !! of maximum depth
4501  !! 10. `FOOD_VIS_HLF` -- visibility range of a standard food item at a half
4502  !! of the maximum depth.
4503  !! 11. `FOOD_VIS_DPMAX` -- visibility range of a standard food item at the
4504  !! maximum depth
4505  !! 12. `FOOD_VIS_MDEPT` -- visibility range of a standard food item at the
4506  !! target mean depth `MEAN_DEPTH`
4507  !! 13. `DEP_VR_UND_200` -- depth at which the visibility of the standard
4508  !! average food item falls below 100 cm
4509  !! 14. `DEP_VR_UND_100` -- depth at which the visibility of the standard
4510  !! average food item falls below 100 cm
4511  !! 15. `DEP_VR_UND_020` -- depth at which the visibility of the standard
4512  !! average food item falls below 20 cm
4513  !! 16. `DEP_VR_UND_005` -- depth at which the visibility of the standard
4514  !! average food item falls below 5 cm
4515  !! .
4516  character(len=LABEL_LENGTH), dimension(*), parameter :: COLNAMES = &
4517  [ character(len=label_length) :: &
4518  "TIMESTEP", "SURFACE_LIGHT", "LIGHT_DEP_10", "LIGHT_DEP_HLF", &
4519  "LIGHT_DEP_MAX", "MEAN_DEPTH", "LIGHT_MDEPTH", "FOOD_VIS_SURF", &
4520  "FOOD_VIS_10", "FOOD_VIS_HLF", "FOOD_VIS_DPMAX", "FOOD_VIS_MDEPT",&
4521  "DEP_VR_UND_400", "DEP_VR_UND_200", "DEP_VR_UND_100", &
4522  "DEP_VR_UND_020" ]
4523 
4524  ! The data (columns) that are calculated and saved:
4525  real(SRP) :: surface_light ! - surface light
4526  real(SRP) :: target_depth ! - the average depth of the food items
4527  real(SRP) :: target_depth_light ! - light at the target depth
4528  real(SRP) :: depth_minimum_fall ! - depth at which visibility falls below
4529  ! specific values.
4530 
4531  if (present(maxdepth)) then
4532  maxdepth_loc = maxdepth
4533  else
4534  ! Maxdepth is set the the global maximum.
4535  maxdepth_loc = maxval( global_habitats_available%depth_max() )
4536  end if
4537 
4538  ! Open CSV file for writing
4539  call out_file%open_write( trim(csv_file_name), format_csv )
4540  if ( .not. out_file%is_success() ) then
4541  if (present(is_success)) is_success = .false.
4542  call out_file%close()
4543  return
4544  end if
4545 
4546  ! Make the first record the column names from the `COLNAMES` string array.
4547  record_string = repeat(" ", label_length*size(colnames)+size(colnames)*4)
4548  call csv_record_append( record_string, colnames )
4549  call out_file%record_write(record_string)
4550  if ( .not. out_file%is_success() ) then
4551  if (present(is_success)) is_success = .false.
4552  call out_file%close()
4553  return
4554  end if
4555 
4556  ! Save other records one by one; one record is a single time step
4557  ! of the model.
4558  records_ls: do i = 1, lifespan
4559 
4560  record_string = repeat(" ", label_length*size(colnames)+size(colnames)*4)
4561 
4562  ! TIMESTEP
4563  call csv_record_append( record_string, i )
4564  ! SURFACE_LIGHT
4565  surface_light = light_surface( i )
4566  call csv_record_append( record_string, surface_light )
4567  ! LIGHT_DEP_10
4568  target_depth_light = light_depth( depth = maxdepth_loc / 10.0_srp, &
4569  surface_light = surface_light, &
4570  is_stochastic=.false. )
4571  call csv_record_append( record_string, target_depth_light )
4572  ! LIGHT_DEP_HLF
4573  target_depth_light = light_depth( depth = maxdepth_loc / 2.0_srp, &
4574  surface_light = surface_light, &
4575  is_stochastic=.false. )
4576  call csv_record_append( record_string, target_depth_light )
4577  ! LIGHT_DEP_MAX
4578  target_depth_light = light_depth( depth = maxdepth_loc, &
4579  surface_light = surface_light, &
4580  is_stochastic=.false. )
4581  call csv_record_append( record_string, target_depth_light )
4582  ! MEAN_DEPTH
4583  target_depth = center_depth_sinusoidal( i, maxdepth_loc )
4584  call csv_record_append( record_string, target_depth )
4585  ! LIGHT_MDEPTH
4586  target_depth_light = light_depth( depth = target_depth, &
4587  surface_light = surface_light, &
4588  is_stochastic=.false. )
4589  call csv_record_append( record_string, target_depth_light )
4590  ! FOOD_VIS_SURF
4591  call object_std%position_v( 1.0_srp, 2.0_srp, 0.0 )
4592  visibility_food = object_std%visibility( &
4593  object_area = &
4595  contrast = preycontrast_default, &
4596  time_step_model = i )
4597  call csv_record_append( record_string, visibility_food )
4598  ! FOOD_VIS_10
4599  call object_std%position_v( 1.0_srp, 2.0_srp, maxdepth_loc / 10.0_srp )
4600  visibility_food = object_std%visibility( &
4601  object_area = &
4603  contrast = preycontrast_default, &
4604  time_step_model = i )
4605  call csv_record_append( record_string, visibility_food )
4606  ! FOOD_VIS_HLF
4607  call object_std%position_v( 1.0_srp, 2.0_srp, maxdepth_loc / 2.0_srp )
4608  visibility_food = object_std%visibility( &
4609  object_area = &
4611  contrast = preycontrast_default, &
4612  time_step_model = i )
4613  call csv_record_append( record_string, visibility_food )
4614  ! FOOD_VIS_DPMAX
4615  call object_std%position_v( 1.0_srp, 2.0_srp, maxdepth_loc )
4616  visibility_food = object_std%visibility( &
4617  object_area = &
4619  contrast = preycontrast_default, &
4620  time_step_model = i )
4621  call csv_record_append( record_string, visibility_food )
4622  ! FOOD_VIS_MDEPT
4623  call object_std%position_v( 1.0_srp, 2.0_srp, target_depth )
4624  visibility_food = object_std%visibility( &
4625  object_area = &
4627  contrast = preycontrast_default, &
4628  time_step_model = i )
4629  call csv_record_append( record_string, visibility_food )
4630  ! DEP_VR_UND_400
4631  depth_minimum_fall = minimum_depth_visibility( &
4632  400.0_srp, &
4633  object_area = carea( cm2m(food_item_mean_size) ), &
4634  time_step_model = i )
4635  call csv_record_append( record_string, depth_minimum_fall )
4636  ! DEP_VR_UND_200
4637  depth_minimum_fall = minimum_depth_visibility( &
4638  200.0_srp, &
4639  object_area = carea( cm2m(food_item_mean_size) ), &
4640  time_step_model = i )
4641  call csv_record_append( record_string, depth_minimum_fall )
4642  ! DEP_VR_UND_100
4643  depth_minimum_fall = minimum_depth_visibility( &
4644  100.0_srp, &
4645  object_area = carea( cm2m(food_item_mean_size) ), &
4646  time_step_model = i )
4647  call csv_record_append( record_string, depth_minimum_fall )
4648  ! DEP_VR_UND_020
4649  depth_minimum_fall = minimum_depth_visibility( &
4650  20.0_srp, &
4651  object_area = carea( cm2m(food_item_mean_size) ), &
4652  time_step_model = i )
4653  call csv_record_append( record_string, depth_minimum_fall )
4654 
4655  call out_file%record_write(record_string)
4656  if ( .not. out_file%is_success() ) then
4657  if (present(is_success)) is_success = .false.
4658  call out_file%close()
4659  return
4660  end if
4661 
4662  end do records_ls
4663 
4664  call out_file%close()
4665  if ( out_file%is_success() ) then
4666  if (present(is_success)) is_success = .true.
4667  else
4668  if (present(is_success)) is_success = .false.
4669  end if
4670 
4671  end subroutine save_dynamics
4672 
4673  !-----------------------------------------------------------------------------
4674  !> Determine the centroid of the environment.
4675  !! @returns habitat centre coordinates, spatial object type
4676  !! @param nodepth Logical flag indicating that **depth** should not change.
4677  function environment_centre_coordinates_3d(this, nodepth) &
4678  result(habitat_centre)
4679  class(environment), intent(in) :: this
4680  ! @returns habitat centre coordinates, spatial object type
4681  type(spatial) :: habitat_centre
4682 
4683  ! @param nodepth Logical flag indicating that **depth** should not change.
4684  logical, optional, intent(in) :: nodepth
4685 
4686  ! Calculate centre of the habitat as the averages of all coordinates.
4687  habitat_centre%x = (this%coord_max%x - this%coord_min%x)/2.0_srp
4688  habitat_centre%y = (this%coord_max%y - this%coord_min%y)/2.0_srp
4689 
4690  ! Check optional `nodepth` flag for leaving the depth level untouched.
4691  if (present(nodepth)) then
4692  if (nodepth .eqv. .false.) then
4693  ! nodepth is FALSE, so **do** calculate centroid depth.
4694  habitat_centre%depth = (this%coord_max%depth - &
4695  this%coord_min%depth)/2.0_srp
4696  end if
4697  else
4698  ! nodepth is not provided (absent), so **do** calculate centroid depth.
4699  habitat_centre%depth = (this%coord_max%depth - &
4700  this%coord_min%depth)/2.0_srp
4701  end if
4702 
4704 
4705  !-----------------------------------------------------------------------------
4706  !> Wrapper for calculating *visual range of a fish predator* using
4707  !! the Dag Aksnes's procedures `srgetr()`, `easyr()` and `deriv()`.
4708  !! See `srgetr()` for computational details.
4709  !! @note Note that this is a **scalar** version. The measurement unit here
4710  !! is meter, might need conversion if other units are used.
4711  !!
4712  !! @param[in] irradiance background irradiance at specific depth
4713  !! @param[in] prey_area prey area, m^2
4714  !! @param[in] prey_contrast optional prey inherent contrast or default
4715  !! parameter if not present.
4716  !! @return Returns visual range of the fish predator.
4717  !!
4718  !> Example call:
4719  !! @code
4720  !! visual_range( light_depth( 30., light_surface(100,.TRUE.) ) )
4721  !! @endcode
4722  !!
4723  !! ### Specific implementations ###
4724  !! See specific implementations:
4725  !! - the_environment::visual_range_scalar() for scalar argument
4726  !! - the_environment::visual_range_vector() for vector argument
4727  !! - the_environment::visual_range_fast() elemental (parallel-safe) version
4728  !! lacking sanity checks and extended debugging.
4729  !! .
4730  function visual_range_scalar(irradiance, prey_area, prey_contrast) &
4731  result(visual_range_calculate)
4732 
4733  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
4734  character(len=*), parameter :: procname = "(visual_range_scalar)"
4735 
4736  ! @param irradiance background irradiance at specific depth
4737  real(srp), intent(in) :: irradiance
4738 
4739  ! @param prey_area prey area, m^2
4740  real(srp), optional, intent(in) :: prey_area
4741 
4742  ! @param prey_contrast optional prey inherent contrast or default
4743  ! parameter if not present.
4744  real(srp), optional, intent(in) :: prey_contrast
4745 
4746  ! @return Returns visual range of the fish predator
4747  real(srp) :: visual_range_calculate
4748 
4749  ! Local **high precision** value of `visual_range_calculate`, explicitly
4750  ! converted to `SRP` at the end.
4751  real(hrp) :: visual_range_hrp_here
4752 
4753  !> ### Notable parameters ###
4754  !> #### VISUAL_RANGE_MAX_OVERFLOW ####
4755  !! `VISUAL_RANGE_MAX_OVERFLOW = 1300.0_HRP`
4756  !! The maximum ceiling value of visual range (cm) calculable using
4757  !! `srgetr()` under the `commondata::q_prec_128` numerical
4758  !! precision model (referred as commondata::hrp). This value is set to
4759  !! the visual range in the cases of numerical overflow.
4760  !! @note The real 128 bit limit (commondata::q_prec_128) is sufficient to
4761  !! calculate visual range up to the fish length of approximately
4762  !! 700 cm (area 153.9 cm^2). At this level, the maximum visual
4763  !! range is 1343.34 cm. This would be sufficient for non-whales.
4764  real(hrp), parameter :: visual_range_max_overflow = 1300.0_hrp
4765 
4766  ! Local error flag
4767  integer :: error_flag
4768 
4769  !> #### error_msg ####
4770  !> Local error message, character array:
4771  !! - 1 = NO_CONVERGENCE
4772  !! - 2 = DIVISION_ZERO
4773  !! - 3 = NEGATIVE_RANGE
4774  !! .
4775  character(len=LABEL_LENGTH), parameter, dimension(3) :: &
4776  error_msg = ["NO_CONVERGENCE", "DIVISION_ZERO ", "NEGATIVE_RANGE"]
4777 
4778  ! Local copies of optional parameters
4779  real(srp) :: prey_area_here, prey_contrast_here
4780 
4781  !> ### Implementation notes ###
4782  !> The computational backend for the visual range computation
4783  !! `srgetr()`, `easyr()` and `deriv()` now uses the
4784  !! `commondata::q_prec_128` 128 bit numerical precision model. This
4785  !! precision is sufficient to calculate visual range up to the the
4786  !! object radius of approximately 700 cm (area 153.9 cm^2) without x86 FPU
4787  !! overflow errors. At this level, the maximum visual range is 1343.34 cm.
4788  !> #### Visual range plots ####
4789  !> The visual range plots below are generated by
4790  !! `HEDTOOLS/tools/visrange_plot.f90`.
4791  !! @image html img_doxy_visrange-005.svg
4792  !! @image latex img_doxy_visrange-005.eps "Visual range, 5.0" width=14cm
4793  !! @image html img_doxy_visrange-250.svg
4794  !! @image latex img_doxy_visrange-250.eps "Visual range, 250" width=14cm
4795  !! @image html img_doxy_visrange-500.svg
4796  !! @image latex img_doxy_visrange-500.eps "Visual range, 500" width=14cm
4797  !> #### Calculation details ####
4798  !! First, check if background irradiance is below commondata::zero and
4799  !! just return zero visual range.
4800  if(irradiance < zero) then
4801  visual_range_calculate = 0.0_srp
4802  return
4803  end if
4804 
4805  !> Check if prey area dummy parameter is provided, if not, use the
4806  !! default value `commondata::preyarea_default`.
4807  if (present(prey_area)) then
4808  prey_area_here=prey_area
4809  else
4810  prey_area_here=preyarea_default
4811  end if
4812 
4813  ! Initialise the error flag.
4814  error_flag = 0
4815 
4816  !> Check if prey contrast dummy parameter is provided, if not, use the
4817  !! default value `commondata::preycontrast_default`.
4818  if (present(prey_contrast)) then
4819  prey_contrast_here=prey_contrast
4820  else
4821  prey_contrast_here=preycontrast_default
4822  end if
4823 
4824  !> Call the main computational backend `the_environment::srgetr()`
4825  !! Note that `the_environment::srgetr()` and the whole computational
4826  !! backend are in `commondata::hrp` precision to reduce numerical
4827  !! rounding and avoid overflow errors.
4828  call srgetr( visual_range_hrp_here, &
4829  real(beamatt,hrp), real(prey_contrast_here,HRP), &
4830  real(prey_area_here,HRP), real(VISCAP,HRP), &
4831  real(EYESAT,HRP), real(irradiance,HRP), error_flag )
4832 
4833  !> The visual range calculation backend `srgetr()` seems
4834  !! computationally suboptimal and with large object size
4835  !! leads to numerical overflow, so the visual range is -Infinity.
4836  !! This is corrected in two steps. (1) in `deriv()`, a problematic
4837  !! part of the computation now checks proactively for potential
4838  !! overflow (comparing to `log(huge())` for the current FPU
4839  !! precision level), so no NaNs or Infinity are produced and
4840  !! no FPU invalid arithmetic errors occur. This is reported to the logger.
4841  if (error_flag /= 0) then
4842  call log_msg("ERROR: In " // procname // &
4843  ": (srgetr) issued error code " // &
4844  tostr(error_flag) // " :: " // error_msg(error_flag) // &
4845  ". Object area (prey_area)=" // tostr(prey_area) // &
4846  ", object contrast (prey_contrast)=" // &
4847  tostr(prey_contrast) // &
4848  ". Visual range calculated as " // &
4849  tostr(visual_range_hrp_here) // " m" )
4850  !> If the visual range value returned by `srgetr` is negative, we reset
4851  !! it to the maximum overflow ceiling value `VISUAL_RANGE_MAX_OVERFLOW`.
4852  !! It has previously be set to `easyr()` approximation, but that was
4853  !! grossly wrong (hugely overestimating). It is safer to just limit
4854  !! visual range for detecting bigger objects to such a fixed value.
4855  if (visual_range_hrp_here < zero ) then
4856  visual_range_hrp_here = visual_range_max_overflow
4857  call log_msg("ERROR: In " // procname // ": Visual range " // &
4858  "recalculated using `VISUAL_RANGE_MAX_OVERFLOW` ceiling: " &
4859  // tostr(cm2m(visual_range_hrp_here)) // " m for HRP " // &
4860  "real kind=" // tostr(hrp) // " precision model." )
4861  end if
4862  end if
4863 
4864  !> Finally, do explicit conversion of the final return value from
4865  !! the high `commondata::hrp` to the standard precision `commondata::srp`.
4866  visual_range_calculate = real(visual_range_hrp_here, srp)
4867 
4868  end function visual_range_scalar
4869 
4870  !-----------------------------------------------------------------------------
4871  !> Wrapper for calculating *visual range of a fish predator* using
4872  !! the Dag Aksnes's procedures `srgetr()`, `easyr()` and `deriv()`.
4873  !! See `srgetr()` for computational details.
4874  !! @note This is a **vector** version, `prey_area` is mandatory and also
4875  !! defines the vector size for all other vector parameters including
4876  !! the returned function value vector. This is useful for selecting
4877  !! among a swarm of prey with different sizes when vector is processed.
4878  !! The measurement unit here is meter. Might need conversion if other
4879  !! units are used.
4880  !! @param[in] irradiance background irradiance at specific depth
4881  !! @param[in] prey_area prey area, m^2; Mandatory parameter.
4882  !! @param[in] prey_contrast_vect optional prey inherent contrast or default
4883  !! parameter if not present. This parameter sets **individual vector**
4884  !! prey contrast, so can be used for providing stochastic contrast
4885  !! data for each object.
4886  !! @param[in] prey_contrast optional prey inherent contrast or default
4887  !! parameter if not present. This parameter sets **common scalar**
4888  !! prey contrast for the whole vector.
4889  !! @return Returns visual range of the fish predator.
4890  !!
4891  !! ### Specific implementations ###
4892  !! See specific implementations:
4893  !! - the_environment::visual_range_scalar() for scalar argument
4894  !! - the_environment::visual_range_vector() for vector argument
4895  !! - the_environment::visual_range_fast() elemental (parallel-safe) version
4896  !! lacking sanity checks and extended debugging.
4897  !! .
4898  function visual_range_vector(irradiance, prey_area, prey_contrast_vect, &
4899  prey_contrast) result(visual_range_calculate)
4900 
4901  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
4902  character(len=*), parameter :: procname = "(visual_range_vector)"
4903 
4904  ! @param irradiance background irradiance at specific depth
4905  real(srp), intent(in) :: irradiance
4906 
4907  ! @param prey_area prey area, m^2
4908  ! @note Mandagtory parameter.
4909  real(srp), dimension(:), intent(in) :: prey_area
4910 
4911  ! @param prey_contrast_vect optional prey inherent contrast or default
4912  ! parameter if not present.
4913  ! @note This parameter sets **individual vector** prey contrast, so can
4914  ! be used for providing stochastic contrast data for each object.
4915  real(srp), optional, dimension(size(prey_area)), intent(in) :: &
4916  prey_contrast_vect
4917 
4918  ! @param prey_contrast optional prey inherent contrast or default
4919  ! parameter if not present.
4920  ! @note This parameter sets **common scalar** prey contrast for the whole
4921  ! vector.
4922  real(srp), optional, intent(in) :: prey_contrast
4923 
4924  ! @return Returns visual range of the fish predator
4925  real(srp), dimension(size(prey_area)) :: visual_range_calculate
4926 
4927  ! Local copies of optional parameters
4928  real(srp), dimension(size(prey_area)) :: prey_contrast_here
4929 
4930  ! Local counter
4931  integer :: i
4932 
4933  !> ### Implementation notes ###
4934  !> The computational backend for the visual range computation
4935  !! `srgetr()`, `easyr()` and `deriv()` now uses the
4936  !! `commondata::q_prec_128` 128 bit numerical precision model. This precision
4937  !! is sufficient to calculate visual range up to the the object radius of
4938  !! approximately 700 cm (area 153.9 cm^2) without FPU overflow errors. At
4939  !! this level, the maximum visual range is 1343.34 cm.
4940  !> #### Visual range plots ####
4941  !> The visual range plots below are generated by
4942  !! `HEDTOOLS/tools/visrange_plot.f90`.
4943  !! @image html img_doxy_visrange-005.svg
4944  !! @image latex img_doxy_visrange-005.eps "Visual range, 0.5" width=14cm
4945  !! @image html img_doxy_visrange-250.svg
4946  !! @image latex img_doxy_visrange-250.eps "Visual range, 250" width=14cm
4947  !! @image html img_doxy_visrange-500.svg
4948  !! @image latex img_doxy_visrange-500.eps "Visual range, 500" width=14cm
4949  !> Check if prey contrast dummy parameter is provided, if not, use the
4950  !! default value `commondata::preycontrast_default`.
4951  if (present(prey_contrast_vect)) then
4952  prey_contrast_here=prey_contrast_vect
4953  else
4954  prey_contrast_here=preycontrast_default
4955  end if
4956 
4957  !> Note that the function can use either a (possibly stochastic) vector or
4958  !! a scalar value (same for all) parameters for prey contrast as optional
4959  !! arguments. Scalar value takes precedence if both are provided.
4960  if (present(prey_contrast)) then
4961  prey_contrast_here = prey_contrast
4962  end if
4963 
4964  !> #### Calculation of the visual range ####
4965  !> The main body of calculation is actually a loop calling the
4966  !! scalar-based function `the_environment::visual_range_scalar()`.
4967  ! @warning Converting this loop to `do concurrent` requires purifying
4968  ! `visual_range_scalar()`: deleting calls to LOG_MSG and probably
4969  ! adding optional output variables for error reporting out of
4970  ! the procedure call (not in-place logging).
4971  ! @note The backend engine is now elemental and parallel-safe. There
4972  ! is also a non-debug and no log **elemental** `visual_range()`
4973  ! procedure `visual_range_fast`().
4974  do i=1, size(visual_range_calculate)
4975  visual_range_calculate(i) = visual_range_scalar(irradiance, &
4976  prey_area(i), prey_contrast_here(i))
4977  end do
4978 
4979  end function visual_range_vector
4980 
4981  !-----------------------------------------------------------------------------
4982  !> Wrapper for calculating *visual range of a fish predator* using
4983  !! the Dag Aksnes's procedures `srgetr()`, `easyr()` and `deriv()`.
4984  !! This is a new **elemental** and parallel-ready visual range function
4985  !! wrapper making use the elemental-procedures based computational backend.
4986  !! See notes on `visual_range_scalar()` and `srgetr()` for computational
4987  !! details.
4988  !! @param[in] irradiance background irradiance at specific depth
4989  !! @param[in] prey_area prey area, m^2
4990  !! @param[in] prey_contrast optional prey inherent contrast or default
4991  !! parameter if not present.
4992  !! @return Returns visual range of the fish predator
4993  !! @warning It is simplified, e.g. **no error reporting** is done.
4994  !! Nonetheless, debugging the old code has shown that it works
4995  !! okay up to the `MAX_LOG` non-whale size limit. Use the
4996  !! non-elemental version whenever debugging or logging is required!
4997  !! The parameter `prey_contrast` to the **vector**-based function
4998  !! call must be an **scalar**. Otherwise a segmentation fault
4999  !! runtime error results. Vector-based call is analogous to calling
5000  !! `visual_range_vector` with `prey_contrast_vect` parameter.
5001  !!
5002  !! ### Specific implementations ###
5003  !! See specific implementations:
5004  !! - the_environment::visual_range_scalar() for scalar argument
5005  !! - the_environment::visual_range_vector() for vector argument
5006  !! - the_environment::visual_range_fast() elemental (parallel-safe) version
5007  !! lacking sanity checks and extended debugging.
5008  !! .
5009  elemental function visual_range_fast(irradiance, prey_area, prey_contrast) &
5010  result(visual_range_calculate)
5011 
5012  ! @param irradiance background irradiance at specific depth
5013  real(srp), intent(in) :: irradiance
5014 
5015  ! @param prey_area prey area, m^2
5016  real(srp), optional, intent(in) :: prey_area
5017 
5018  ! @param prey_contrast optional prey inherent contrast or default
5019  ! parameter if not present.
5020  real(srp), optional, intent(in) :: prey_contrast
5021 
5022  ! @param error_string optional error description, if no error was produced
5023  ! returns "SUCCESS " (note trailing spaces, use `trim`
5024  ! while testing!)
5025  ! @warning Cannot be used for **output** as in pure functions arguments
5026  ! must have intent **in** only.
5027  !character(len=LABEL_LENGTH), optional, intent(out) :: error_string
5028 
5029  ! @return Returns visual range of the fish predator
5030  real(srp) :: visual_range_calculate
5031 
5032  ! Local **high precision** value of `visual_range_calculate`, explicitly
5033  ! converted to `SRP` at the end.
5034  real(hrp) :: visual_range_hrp_here
5035 
5036  ! Local copies of optional parameters
5037  real(srp) :: prey_area_here, prey_contrast_here
5038 
5039  ! Local error flag
5040  integer :: error_flag
5041 
5042  ! Local error message.
5043  ! @warning Must have all the same length.
5044  ! @note `error_msg` and `error_string` are not used for normal non-debug
5045  ! operation and are disabled in the code here.
5046  !character(len=*), parameter, dimension(0:3) :: error_msg = &
5047  ! ["SUCCESS ", "NO_CONVERGENCE", "DIVISION_ZERO ", "NEGATIVE_RANGE"]
5048 
5049  ! Final error description (array), cannot be output externally as an
5050  ! intent(out) parameter but can be used for internal debugging.
5051  ! @note `error_string` is not used for normal non-debug operation and is
5052  ! disabled in the code here.
5053  ! character(len=LABEL_LENGTH) :: error_string
5054 
5055  if (present(prey_area)) then
5056  prey_area_here=prey_area
5057  else
5058  prey_area_here=preyarea_default
5059  end if
5060 
5061  if (present(prey_contrast)) then
5062  prey_contrast_here=prey_contrast
5063  else
5064  prey_contrast_here=preycontrast_default
5065  end if
5066 
5067  ! Initialise the error flag. Normally, no error reporting is done here
5068  ! to keep the intent requirements of the pure function.
5069  error_flag = 0
5070 
5071  !> ### Implementation details ###
5072  !> This version of the visual_range procedure does not call the error
5073  !! correction code and does not report errors into the logger. However,
5074  !! this allows declaring it as **elemental**.
5075  !! @note Note that `srgetr()` and the whole computational backend are now
5076  !! in `commondata::hrp` precision to avoid numerical overflow errors.
5077  call srgetr( visual_range_hrp_here, &
5078  real(beamatt,hrp), real(prey_contrast_here,HRP), &
5079  real(prey_area_here,HRP), real(VISCAP,HRP), &
5080  real(EYESAT,HRP), real(irradiance,HRP), error_flag )
5081 
5082 
5083  ! Optionally produce an error description array. Can be used only for
5084  ! internal debugging, not for output in this pure function.
5085  ! @note `error_string` is not used for normal non-debug operation and is
5086  ! disabled in the code here.
5087  !if (present(error_string)) error_string = error_msg(error_flag)
5088  !error_string = error_msg(error_flag)
5089 
5090  !> Finally, do explicit conversion from `commondata::hrp` to
5091  !! `commondata::srp`.
5092  visual_range_calculate = real(visual_range_hrp_here, srp)
5093 
5094  end function visual_range_fast
5095 
5096  !=============================================================================
5097  !> @name Visual range calculation backend.
5098  !! The subroutines the_environment::srgetr(), the_environment::easyr() and
5099  !! the_environment::deriv() should be better isolated into a separate
5100  !! module or form a submodule, but it is not used here as submodules are a
5101  !! F2008 feature not supported by all compiler systems. Anyway, submodule
5102  !! is not essential here.
5103  !! @note Note that all these backend procedures are now **pure** and
5104  !! therefore parallel-safe.
5105  !! @{
5106  ! submodule AKVISRANGE ! Dag AKsnes VISual RANGEe utilities.
5107  ! @warning The commondata::hrp 128 precision model should be used, anything
5108  ! smaller can produce FPU overflows! Precision is defined in the
5109  ! @ref commondata module.
5110  ! @code
5111  ! integer, parameter, public :: Q_PREC_128 = selected_real_kind(33, 4931)
5112  ! integer, parameter, public :: HRP = Q_PREC_128
5113  ! @endcode
5114  !-----------------------------------------------------------------------------
5115  !> Obtain visual range by solving the non-linear equation
5116  !! by means of Newton-Raphson iteration and derivation in
5117  !! subroutine the_environment::deriv(). Initial value is calculated in
5118  !! the_environment::easyr(). The calculation is based on the model described
5119  !! in Aksnes & Utne (1997) Sarsia 83:137-147.
5120  !! @note Programmed and tested 29 January 2001 Dag L Aksnes.
5121  !! @note This subroutine is left almost intact with only the most crucial
5122  !! changes.
5123  !! (a) added commondata::hrp precision specifier (128 bit precision
5124  !! model) to real type specifiers and `_HRP` for literal constants;
5125  !! (b) restored diagnostic `IER` output from archival Hed11.f90.
5126  !! (c) added explicit `intent` and declared the procedures as
5127  !! `pure` that is required for being parallel-friendly.
5128  ! **Input parameters:**
5129  !> @param[in] RST start value of r calculated by the_environment::easyr();
5130  !! @param[in] c beam attenuation coefficient `(m-1)`;
5131  !! @param[in] C0 prey inherent contrast;
5132  !! @param[in] Ap prey area `(m^2)`;
5133  !! @param[in] Vc parameter characterising visual capacity (d.l.)
5134  !! @param[in] this parameter is denoted E' in Aksnes & Utne;
5135  !! @param[in] Ke saturation parameter `(uE m-2 s-1)`;
5136  !! @param[in] Eb background irradiance at depth `DEPTH`;
5137  ! **Output parameters:**
5138  !> @param[out] r the predator's visual range (when `F1=0`);
5139  !! @param[out] IER **1** = No convergence after IEND steps, error return;
5140  !! **2** = Return in case of zero divisor;
5141  !! **3** = r out of allowed range (negative);
5142  !! **0** = valid r returned.
5143  !!
5144  !> **Notable variables:**
5145  !> - **F1** function value of equation in `deriv`;
5146  !! - **FDER** the derivative of the function.
5147  !! .
5148  elemental subroutine srgetr(r, c, C0, Ap, Vc, Ke, Eb, IER)
5149  ! Input parameters
5150  ! RST : start value of r calculated by `easyr`
5151  ! c : beam attenuation coefficient (m-1)
5152  ! C0 : prey inherent contrast
5153  ! Ap : prey area (m^2)
5154  ! Vc : parameter characterising visual capacity (d.l.)
5155  ! this parameter is denoted E' in Aksnes & Utne
5156  ! Ke : saturation parameter (uE m-2 s-1)
5157  ! Eb : background irradiance at depth DEPTH
5158  ! Output parameters
5159  ! F1 : function value of equation in `deriv`
5160  ! FDER : the derivative of the function
5161  ! r : the predator's visual range (when F1=0)
5162  ! IER : = 1, No convergence after IEND steps. Error return.
5163  ! = 2, Return in case of zero divisor.
5164  ! = 3, r out of allowed range (negative)
5165  ! = 0, valid r returned
5166  real(hrp), intent(in) :: c, c0, ap, vc, ke, eb
5167  real(hrp), intent(out) :: r
5168  integer, optional, intent(out) :: ier
5169 
5170  real(hrp) :: as, eps, rst, tol, tolf, f1, fder, dx
5171  integer :: iend, i
5172 
5173  !.............................................................................
5174 
5175  ! Initial guess of visual range (RST)
5176  call easyr(rst,c0,ap,vc,ke,eb)
5177 
5178  ! Upper boundary of allowed error of visual range.
5180  ! Maximum number of iteration steps
5181  iend = 100
5182 
5183  ! Prepare iteration
5184  r = rst
5185  tol = r
5186 
5187  call deriv(r,f1,fder,c,c0,ap,vc,ke,eb)
5188  tolf = 100.0_hrp * eps
5189 
5190  ! Start iteration expmt
5191  ! @warning Cannot probably be converted to `do concurrent` due to `exit`s
5192  !! from the loop.
5193  do 6 i = 1, iend
5194  if (f1 .feq. 0.0_hrp) goto 7
5195 
5196  ! Equation is not satisfied by r
5197  if (fder .feq. 0.0_hrp) goto 8
5198 
5199  ! Iteration is possible
5200  dx = f1/fder
5201  r = r-dx
5202 
5203  ! Test on allowed range
5204  if (r .LT. 0.0_hrp) goto 9
5205 
5206  tol = r
5207 
5208  call deriv(r,f1,fder,c,c0,ap,vc,ke,eb)
5209 
5210  ! Test on satisfactory accuracy
5211  tol = eps
5212  as = abs(r)
5213  if ((as-1.0_hrp) > 0.0_hrp) tol = tol*as
5214  tol = tol*as
5215  if ((abs(dx)-tol) > 0.0_hrp) goto 6
5216  if ((abs(f1)-tolf) .LE. 0.0_hrp) goto 7
5217  6 continue
5218 
5219  ! No convergence after IEND steps. Error return.
5220  if (present(ier)) ier = 1
5221  7 return
5222  ! Return in case of zero divisor
5223  8 if (present(ier)) ier = 2
5224  return
5225  ! r out of allowed range (negative)
5226  9 if (present(ier)) ier = 3
5227  return
5228 
5229  end subroutine srgetr
5230 
5231  !-----------------------------------------------------------------------------
5232  !> Obtain a first estimate of visual range by using a simplified
5233  !! expression of visual range. See `srgetr()` for more details.
5234  !! @note This subroutine is left almost intact, only (a) added
5235  !! commondata::hrp for real type (`HRP` is *high real precision*
5236  !! (128 bit) and is defined in @ref commondata).
5237  elemental subroutine easyr(r, C0, Ap, Vc, Ke, Eb)
5238  real(hrp), intent(out) :: r
5239  real(hrp), intent(in) :: c0, ap, vc, ke, eb
5240  real(hrp) :: r2
5241  ! See the calling routine the_environment::srgetr() for explanation of
5242  ! parameters
5243  r2 = abs(c0)*ap*vc*eb/(ke+eb)
5244  r = sqrt(r2)
5245  return
5246  end subroutine easyr
5247 
5248  !-----------------------------------------------------------------------------
5249  !> Derivation of equation for visual range of a predator.
5250  !! See the_environment::srgetr() for more details.
5251  !! @note This is a high precision version. But higher precision alone is
5252  !! not sufficient to prevent numerical exponentiation overflow.
5253  !! @note This subroutine is left almost intact, only (a) added
5254  !! commondata::hrp for literal constants and real type (`HRP` is the
5255  !! *high real precision* (128 bit) and is defined in @ref commondata),
5256  !! (b) added numerical overflow safeguard code based on `MAX_LOG`
5257  !! exponentiability limit; added logging of overflow using `LOG_MSG`.
5258  ! **Output parameters:**
5259  !> @param[out] F1 function value of equation in
5260  !! the_environment::deriv();
5261  !! @param[out] FDER the derivative of the function;
5262  !! @param[inout] r the predator's visual range (when `F1=0`).
5263  !!
5264  !> **Input parameters:**
5265  !!
5266  !> See explanation in calling routine the_environment::srgetr().
5267  !!
5268  !> The function and the derivative is calculated on the basis of the
5269  !! log-transformed expression.
5270  elemental subroutine deriv(r, F1, FDER, c, C0, Ap, Vc, Ke, Eb)
5271  ! Input parameters
5272  ! See explanation in calling routine
5273  ! Output parameters
5274  ! F1 : function value of equation in `deriv`
5275  ! FDER : the derivative of the function
5276  ! r : the predator's visual range (when F1=0)
5277  !
5278  ! The function and the derivative is calculated on the basis of the
5279  ! log-transformed expression
5280  real(hrp), intent(inout) :: r
5281  real(hrp), intent(out) :: f1, fder
5282  real(hrp), intent(in) :: c, c0, ap, vc, ke, eb
5283 
5284  real(hrp) :: fr1, fr2
5285 
5286  !> ### Implementation notes ###
5287  !! `MAX_LOG` is a parameter determining the safe limit of `exp` function
5288  !! overflow in the current float point precision model, well below this.
5289  !! We cannot calculate precise exponent of a value exceeding this
5290  !! parameter. The maximum possible exponentiation-able value is set
5291  !! to the maximum **128-bit** real value kind `Q_PREC_128`, this bottom
5292  !! line value would set a safe limit for `HRP` calculations.
5293  !! A benefit of this approach is that it doesn't require IEEE exception
5294  !! handling that depends on not fully portable IEEE modules.
5295  !! @note The real 128 bit limit (Q_PREC_128) is sufficient to calculate
5296  !! visual range up to the fish length of approximately 700 cm
5297  !! (area 153.9 cm2). At this level, the maximum visual range is
5298  !! 1343.34 cm. This would be sufficient for non-whales.
5299  real(hrp), parameter :: huge_real = huge(0.0_hrp)
5300  real(hrp), parameter :: max_log = log(huge_real)
5301 
5302  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
5303  character(len=*), parameter :: procname = "(deriv)"
5304 
5305  fr2=log(abs(c0)*ap*vc)
5306 
5307  ! @note Here `exp(c*r)` can result in huge FPU overflow (Infinity),
5308  ! so we now check if the exponent is likely to be so high and
5309  ! **if yes**, just set the huge ceiling. In such a case one needs
5310  ! to *rescale* variables (e.g. from meters to tenth or hundreds
5311  ! of meters).
5312  ! @warning The resulting calculations are then most probably grossly
5313  ! wrong but we nonetheless avoid FPU runtime error: incorrect
5314  ! arithmetic calculations.
5315  if (c*r < max_log) then
5316  fr1=log(((ke+eb)/eb)*r*r*exp(c*r))
5317  else
5318  fr1=huge_real
5319  ! Subroutine `LOG_MSG` is defined in `LOGGER` module. It reports
5320  ! numerical overflow errors.
5321  ! @warning `LOG_MSG` is **disabled** here as it cannot be **pure** and
5322  ! hampers purifying `deriv` for parallel processing.
5323  ! A negative side is no error report logging. But the `HRP`
5324  ! 64 bit precision model seems to work okay. There is also
5325  ! a guarantee against overflow and gross errors with the
5326  ! `HUGE_REAL` parameter.
5327  !call LOG_MSG ( "ERROR in " // PROCNAME // ": FR1 overflow " // &
5328  ! "exceeding " // TOSTR(HUGE_REAL) // " limit :" // &
5329  ! " c*r =" // TOSTR(c*r) )
5330  end if
5331 
5332  f1 = fr1-fr2
5333  fder = c + 2.0_hrp/r
5334  return
5335 
5336  end subroutine deriv
5337 
5338  ! end submodule AKVISRANGE
5339  !> @}
5340  !=============================================================================
5341 
5342  !-----------------------------------------------------------------------------
5343  !> Calculate deterministic surface light at specific time step of the model.
5344  !! Light (`surlig`) is calculated from a sine function. Light intensity
5345  !! just beneath the surface is modelled by assuming a 50 % loss by scattering
5346  !! at the surface: @f[ L_{t} = L_{max} 0.5 sin(\pi dt / \Omega ) @f].
5347  !! @returns surface light intensity.
5348  !! @param tstep time step of the model, limited by maximum
5349  !! commondata::lifespan.
5350  !! @note This is a deterministic version.
5351  !! Code for wxMaxima for quickcalc:
5352  !! @code
5353  !! surlig(a, span) := 500*0.5*(1.01+sin(3.14*2.*50*a/(1.*span)));
5354  !! surlig(a, span) := 500*0.5*(1.01+sin(3.14*2.*50*a/span));
5355  !! wxplot2d(surlig(a, 14000), [a,0, 1400]);
5356  !! @endcode
5357  !! @note Note that this is an elemental function that accepts both scalar
5358  !! and array parameter.
5359  elemental function light_surface_deterministic(tstep) result (surlig)
5360  ! @returns surface light intensity
5361  real(srp) :: surlig
5362  ! @param tstep time step of the model, limited by maximum
5363  ! commondata::lifespan.
5364  integer, optional, intent(in) :: tstep
5365 
5366  ! Local copies of optionals.
5367  integer :: tstep_loc
5368 
5369  if (present(tstep)) then
5370  tstep_loc = tstep
5371  else
5372  tstep_loc = global_time_step_model_current
5373  end if
5374 
5375  ! TODO: what is 1.01 in old code?
5376  surlig = daylight * 0.5_srp * (1.01_srp + sin(pi * 2.0_srp * &
5377  dielcycles * real(tstep_loc,srp) / real(lifespan,srp)))
5378 
5379  end function light_surface_deterministic
5380 
5381  !-----------------------------------------------------------------------------
5382  !> Calculate stochastic surface light at specific time step of the model.
5383  !! Light (`surlig`) is calculated from a sine function. Light intensity
5384  !! just beneath the surface is modelled by assuming a 50 % loss by scattering
5385  !! at the surface: @f[ L_{t} = L_{max} 0.5 sin(\pi dt / \Omega ) @f].
5386  !! This deterministic value sets the *mean* for the stochastic final value,
5387  !! which is Gaussian with CV equal to `DAYLIGHT_CV`.
5388  !! @returns surface light intensity
5389  !! @param tstep time step of the model, limited by maximum
5390  !! commondata::lifespan.
5391  !! @param is_stochastic logical indicator for stochastic light intensity
5392  !! if TRUE, then Gaussian stochastic version is used, if FALSE,
5393  !! deterministic is used.
5394  !! Code for wxMaxima for quickcalc:
5395  !! @code
5396  !! surlig(a, span) := 500*0.5*(1.01+sin(3.14*2.*50*a/(1.*span)));
5397  !! wxplot2d(surlig(a, 14000), [a,0, 1400]);
5398  !! @endcode
5399  function light_surface_stochastic_scalar(tstep, is_stochastic) result (surlig)
5400  ! @returns surface light intensity
5401  real(srp) :: surlig
5402  ! @param tstep time step of the model, limited by maximum
5403  ! commondata::lifespan
5404  integer, optional, intent(in) :: tstep
5405  ! @param is_stochastic logical indicator for stochastic light intensity
5406  ! if TRUE, then Gaussian stochastic version is used, if FALSE,
5407  ! deterministic is used.
5408  logical, intent(in) :: is_stochastic
5409 
5410  ! Local variable, deterministic light intensity that sets a mean value.
5411  real(srp) :: surlig_deterministic
5412 
5413  ! Local copies of optionals.
5414  integer :: tstep_loc
5415 
5416  if (present(tstep)) then
5417  tstep_loc = tstep
5418  else
5419  tstep_loc = global_time_step_model_current
5420  end if
5421 
5422  surlig_deterministic = light_surface_deterministic(tstep_loc)
5423 
5424  if (is_stochastic) then
5425  ! If `is_stochastic` flag is TRUE, then the light intensity is a random
5426  ! Gaussian variate with the mean equal to the deterministic value and
5427  ! coefficient of variation DAYLIGHT_CV.
5428  surlig = rnorm(surlig_deterministic,(surlig_deterministic*daylight_cv)**2)
5429  else
5430  ! If `is_stochastic` flag is FALSE, deterministic *scalar* value is used.
5431  surlig = surlig_deterministic
5432  end if
5433 
5434  end function light_surface_stochastic_scalar
5435 
5436  !-----------------------------------------------------------------------------
5437  !> Calculate stochastic surface light at specific time step of the model.
5438  !! @param tstep time step of the model, limited by maximum
5439  !! commondata::lifespan.
5440  !! @returns surface light intensity.
5441  !! @param is_stochastic logical indicator for stochastic light intensity
5442  !! if TRUE, then Gaussian stochastic version is used, if FALSE,
5443  !! deterministic is used.
5444  !! @note This function accepts vector arguments.
5445  !! @warning Note that the `tstep` array parameter is *mandatory* here
5446  !! (otherwise the generic interface is ambiguous).
5447  function light_surface_stochastic_vector(tstep, is_stochastic) result (surlig)
5448  ! @param tstep time step of the model, limited by maximum
5449  ! commondata::lifespan.
5450  integer, intent(in), dimension(:) :: tstep
5451  ! @returns surface light intensity
5452  real(srp), dimension(size(tstep)) :: surlig
5453  ! @param is_stochastic logical indicator for stochastic light intensity
5454  ! if TRUE, then Gaussian stochastic version is used, if FALSE,
5455  ! deterministic is used.
5456  logical, intent(in) :: is_stochastic
5457 
5458  ! Local variable, deterministic light intensity that sets a mean value.
5459  real(srp), dimension(size(tstep)) :: surlig_deterministic
5460 
5461  ! Local counter
5462  integer :: i
5463 
5464  !> ### Implementation details ###
5465  surlig_deterministic = light_surface_deterministic(tstep)
5466 
5467  if (is_stochastic) then
5468  !> If `is_stochastic` flag is TRUE, then the light intensity is a random
5469  !> Gaussian vector with *each element* mean equal to the deterministic
5470  !> value and coefficient of variation `DAYLIGHT_CV`.
5471  do i=1, size(tstep)
5472  !surlig(i) = surface_light_stochastic_scalar(i,.TRUE.) ! small overhead
5473  surlig(i) = rnorm( surlig_deterministic(i), &
5474  (surlig_deterministic(i) * daylight_cv)**2 )
5475  end do
5476  else
5477  !> If `is_stochastic` flag is FALSE, deterministic *vector* value is used.
5478  surlig = surlig_deterministic
5479  end if
5480 
5481  end function light_surface_stochastic_vector
5482 
5483  !-----------------------------------------------------------------------------
5484  !> Calculate underwater light at specific depth given specific surface light.
5485  !! @details Underwater light is attenuated following Beer’s law,
5486  !! @f[ E_{b}(z,t) = L_{t} e^{-K z} , @f] where @f$ E_{b}(z,t) @f$
5487  !! is background irradiance at depth z at time t and K is the
5488  !! attenuation coefficient for downwelling irradiance. The value
5489  !! of K in the old code was set very high to allow the vertical
5490  !! dynamics to take place within 30 depth cells.
5491  !! @returns Eb background irradiance at specific depth.
5492  !! @param[in] depth The integer depth horizon where we get background
5493  !! @param[in] surface_light Irradiance at the surface, normally calculated
5494  !! at specific time point of the model with the
5495  !! the_environment::light_surface() function. If this parameter is
5496  !! absent, surface light at the current time step is obtained. The
5497  !! time step in such case is obtained from
5498  !! commondata::global_time_step_model_current.
5499  !! @param[in] is_stochastic stochastic indicator for the surface light in
5500  !! the_environment::light_surface() function. If this parameter
5501  !! is absent, the default commondata::daylight_stochastic
5502  !! parameter value is used.
5503  !! @note Note that this function accepts **integer** depth, a separate
5504  !! function should be used for physical real type depth.
5505  !! @note Note that it is an elemental function that accepts both scalar
5506  !! and array parameters.
5507  function light_depth_integer(depth, surface_light, is_stochastic) result(Eb)
5508  ! @returns Eb background irradiance at specific depth.
5509  real(srp) :: eb
5510  ! @param[in] depth The integer depth horizon where we get background
5511  ! irradiance.
5512  integer, intent(in) :: depth
5513  ! @param[in] surface_light Irradiance at the surface, normally calculated
5514  ! at specific time point of the model with `light_surface()`
5515  ! generic function.
5516  real(srp), optional, intent(in) :: surface_light
5517  ! @param[in] is_stochastic stochastic indicator for the surface light in
5518  ! the_environment::light_surface() function. If this parameter
5519  ! is absent, the default commondata::daylight_stochastic
5520  ! parameter value is used.
5521  logical, optional, intent(in) :: is_stochastic
5522 
5523  ! Local copies of optionals.
5524  real(srp) :: surface_light_loc
5525  logical :: is_stochastic_loc
5526 
5527  if (present(is_stochastic)) then
5528  is_stochastic_loc = is_stochastic
5529  else
5530  is_stochastic_loc = daylight_stochastic
5531  end if
5532 
5533  if (present(surface_light)) then
5534  surface_light_loc = surface_light
5535  else
5536  surface_light_loc = light_surface(is_stochastic=is_stochastic_loc)
5537  end if
5538 
5539  !> @note Note that the commondata::lightdecay parameter is in cm.
5540  ! @note Old code implementation can differ (HED24)!
5541  ! In *SRinitage*, *SRhabitat* and *SRgrowth* calculations use an
5542  ! iterative formula that does not exactly follow the Beer's law
5543  ! formula:
5544  ! @code
5545  ! Eb = autosurlig(age) ! Eb is background irradiance at a depth
5546  ! do dep = 1, depth
5547  ! Eb = Eb * exp(-lightdecay)
5548  ! end do
5549  ! @endcode
5550  ! Eb = surface_light * (exp(-LIGHTDECAY))**(depth)
5551  ! or in *SRdecision* it follows the Beer's law formula:
5552  ! @code
5553  ! comp1 = autosurlig(age)*exp(-lightdecay*(z))
5554  ! @endcode
5555  ! @note wxMaxima quick code for plotting (assuming surface light 500.0):
5556  ! @code
5557  ! wxplot2d( 500.0*exp(-0.002 * D), [D, 0., 3000.] );
5558  ! @endcode
5559  eb = surface_light_loc * exp(-lightdecay * real(depth,srp))
5560 
5561  end function light_depth_integer
5562 
5563  !-----------------------------------------------------------------------------
5564  !> Calculate underwater light at specific depth given specific surface light.
5565  !! @details Underwater light is attenuated following Beer’s law,
5566  !! @f[ E_{b}(z,t) = L_{t} e^{-K z} , @f] where @f$ E_{b}(z,t) @f$
5567  !! is background irradiance at depth z at time t and K is the
5568  !! attenuation coefficient for downwelling irradiance.
5569  !! @returns Eb background irradiance at specific depth.
5570  !! @param[in] depth The integer depth horizon where we get background.
5571  !! @param[in] surface_light Irradiance at the surface, normally calculated
5572  !! at specific time point of the model with the
5573  !! the_environment::light_surface() function. If this parameter is
5574  !! absent, surface light at the current time step is obtained. The
5575  !! time step in such case is obtained from
5576  !! commondata::global_time_step_model_current.
5577  !! @param[in] is_stochastic stochastic indicator for the surface light in
5578  !! the_environment::light_surface() function. If this parameter
5579  !! is absent, the default commondata::daylight_stochastic
5580  !! parameter value is used.
5581  !! @note Note that this function accepts **real** depth.
5582  function light_depth_real(depth, surface_light, is_stochastic) result(Eb)
5583  ! @returns Eb background irradiance at specific depth.
5584  real(srp) :: eb
5585  ! @param depth The integer depth horizon where we get background
5586  ! irradiance.
5587  real(srp), intent(in) :: depth
5588  ! @param surface_light Irradiance at the surface, normally calculated
5589  ! at specific time point of the model with
5590  ! the_environment::light_surface() function.
5591  real(srp), optional, intent(in) :: surface_light
5592  ! @param[in] is_stochastic stochastic indicator for the surface light in
5593  ! the_environment::light_surface() function. If this parameter
5594  ! is absent, the default commondata::daylight_stochastic
5595  ! parameter value is used.
5596  logical, optional, intent(in) :: is_stochastic
5597 
5598  ! Local copies of optionals.
5599  real(srp) :: surface_light_loc
5600  logical :: is_stochastic_loc
5601 
5602  if (present(is_stochastic)) then
5603  is_stochastic_loc = is_stochastic
5604  else
5605  is_stochastic_loc = daylight_stochastic
5606  end if
5607 
5608  if (present(surface_light)) then
5609  surface_light_loc = surface_light
5610  else
5611  surface_light_loc = light_surface(is_stochastic=is_stochastic_loc)
5612  end if
5613 
5614  !> @note Note that the commondata::lightdecay parameter is in cm.
5615  !> @note wxMaxima quick code for plotting (assuming surface light 500.0):
5616  !! @code
5617  !! wxplot2d( 500.0*exp(-0.002 * D), [D, 0., 3000.] );
5618  !! @endcode
5619  eb = surface_light_loc * exp(-lightdecay * depth)
5620 
5621  end function light_depth_real
5622 
5623  !-----------------------------------------------------------------------------
5624  !> Calculate distance between 3D or 2D points. This is a function engine
5625  !! for use within type bound procedures.
5626  !! **Example** (dist_scalar):
5627  !! @code
5628  !! dist(1.0,10.0, 2.0,20.0, 3.0,30.0 )
5629  !! @endcode
5630  !! @note This version accepts individual scalar coordinates.
5631  !! @note Note that it is an elemental function, accepting also arrays (should
5632  !! have equal size and shape for elemental operations).
5633  elemental function dist_scalar(x1, x2, y1, y2, z1, z2) result (distance)
5634  real(srp) :: distance
5635  real(srp), intent(in) :: x1, x2, y1, y2
5636  real(srp), intent(in), optional :: z1, z2
5637 
5638  if (present(z1)) then
5639  if (present(z2)) then
5640  !> @note 3D distance is calculated only if z1 and z2 are provided,
5641  !! otherwise an orphaned z coordinate is ignored.
5642  distance = sqrt( (x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2 )
5643  else
5644  distance = sqrt( (x1-x2)**2 + (y1-y2)**2 )
5645  end if
5646  else
5647  distance = sqrt( (x1-x2)**2 + (y1-y2)**2 )
5648  end if
5649 
5650  end function dist_scalar
5651 
5652  !-----------------------------------------------------------------------------
5653  !> Calculate distance between N-dimensional points. This is a function engine
5654  !! for use within other type bound procedures.
5655  !! @returns the distance between two N-dimensional vectors.
5656  !! @param cvector N-dimensional vectors for the two points we calculate
5657  !! the distance between. For a 3D case the vercors look like:
5658  !! x = cvector(1), y = cvector(2), z = cvector(3).
5659  !! **Example** dist_vector:
5660  !! @code
5661  !! dist( [1.0, 2.0, 3.0], [10.0, 20.0, 30.0] )
5662  !! @endcode
5663  !! @note This version accepts vectors of coordinates for each of
5664  !! the two objects.
5665  !! @warning The shapes and sizes of the two arrays must be equal
5666  pure function dist_vector_nd(cvector1, cvector2) result (distance)
5667  ! @returns the distance between two N-dimensional vectors.
5668  real(srp) :: distance
5669  ! @param cvector N-dimensional vectors for the two points we calculate
5670  ! the distance between. For a 3D case the vercors look like:
5671  ! x = cvector(1), y = cvector(2), z = cvector(3).
5672  real(srp), intent(in), dimension(:) :: cvector1
5673  real(srp), intent(in), dimension(:) :: cvector2
5674 
5675  distance = sqrt(sum( (cvector1-cvector2)**2 ))
5676 
5677  end function dist_vector_nd
5678 
5679  !-----------------------------------------------------------------------------
5680  !> Calculate the squared distance between two *N*-dimensional points.
5681  !! @note This function is useful in some cases when squared distances
5682  !! are used to save on calculation of square root.
5683  pure function dist2_vector(cvector1, cvector2) result (distance)
5684  ! @returns the squared distance between two N-dimensional vectors.
5685  real(srp) :: distance
5686  ! @param cvector N-dimensional vectors for the two points we calculate
5687  ! the distance between. For a 3D case the vercors look like:
5688  ! x = cvector(1), y = cvector(2), z = cvector(3).
5689  real(srp), intent(in), dimension(:) :: cvector1
5690  real(srp), intent(in), dimension(:) :: cvector2
5691 
5692  distance = abs( sum( (cvector1-cvector2)**2 ) )
5693 
5694  end function dist2_vector
5695 
5696  !-----------------------------------------------------------------------------
5697  !> Calculate the magnitude of an arbitrary *N*-dimensional vector. This is
5698  !! a raw vector backend.
5699  pure function vect_magnitude(vector) result (vlength)
5700  !> @param[in] vector a vector in *N* dimensions.
5701  real(srp), intent(in), dimension(:) :: vector
5702  !> @return The magnitude of the vector.
5703  real(srp) :: vlength
5704 
5705  !> Vector length is trivially calculated as the euclidean norm:
5706  !! @f[ \left \| x \right \| = \sum x_{i}^{2} . @f]
5707  vlength = sqrt( sum(vector**2) )
5708 
5709  end function vect_magnitude
5710 
5711  !-----------------------------------------------------------------------------
5712  !> Calculate the unit step along a single coordinate axis given the average
5713  !! distance between any two points in a N-dimensional Gaussian random walk.
5714  !! @return Unit step length along a single x,y,or z axis
5715  !! @param average_distance The average distance traversed by the moving
5716  !! point during a single step of the Gaussian random walk.
5717  !! @param dimensionality The dimensionality of the random walk
5718  !! **Example:** dist2step Generate a Gaussian random walk in 3D with discrete
5719  !! step size, i.e. the **true distance between the points** (rather
5720  !! than coordinate shifts) equal to 10.0:
5721  !! @code
5722  !! call moving_zzz%rwalk( dist2step(10.0), 0.5 )
5723  !! @endcode
5724  elemental function dist2step(average_distance, dimensionality) &
5725  result(unit_step)
5726  ! @return Unit step length along a single x,y,or z axis
5727  real(srp) :: unit_step
5728 
5729  ! @param average_distance The average distance traversed by the moving
5730  ! point during a single step of the Gaussian random walk.
5731  real(srp), intent(in) :: average_distance
5732 
5733  ! @param dimensionality The dimensionality of the random walk
5734  integer, optional, intent(in) :: dimensionality
5735 
5736  ! Local copy of the `dimensionality` parameter.
5737  integer :: dim_here
5738 
5739  if (present(dimensionality)) then
5740  dim_here = dimensionality
5741  else
5742  dim_here = dimensionality_default
5743  end if
5744 
5745  unit_step = sqrt( (average_distance**2) / dim_here )
5746 
5747  end function dist2step
5748 
5749  !-----------------------------------------------------------------------------
5750  !> Create a single food item at an undefined position with default size.
5751  elemental subroutine food_item_create(this)
5752  class(food_item), intent(inout) :: this
5753 
5754  ! We here just set an undefined location of the food object using
5755  !! standard interface function `missing`.
5756  call this%missing()
5757 
5758  !> This also cleanups the history stack, i.e. fills it with `MISSING`
5759  !! values.
5760  call this%spatial_history_clean()
5761 
5762  ! Then we set the food item size.
5763  this%size = food_item_size_default
5764 
5765  ! This food item is NOT eaten from creation, so set the status
5766  this%eaten = .false.
5767 
5768  ! Set `UNKNOWN` iid at create.
5769  ! Initially, set a random iid.
5770  ! @warning random id on create is now disabled to allow elemental function,
5771  ! because random are never pure. So care to set iid's elsewhere.
5772  ! `call this%set_iid()`
5773  this%food_iid = unknown
5774 
5775  end subroutine food_item_create
5776 
5777  !-----------------------------------------------------------------------------
5778  !> Make a single food item, i.e. place it into a specific position
5779  !! in the model environment space and set the size.
5780  !! @param Location of the food item as a `SPATIAL` type container
5781  !! @param size This is the optional size of the food item. If absent
5782  !! then default food size is used as defined in `COMMONDATA`
5783  !! @param iid id for the food item. Note: There are no random iids for
5784  !! food items, iid should agree with the item index within the
5785  !! food resource object.
5786  elemental subroutine food_item_make(this, location, size, iid)
5787  class(food_item), intent(inout) :: this
5788 
5789  ! @param Location of the food item as a `SPATIAL` type container
5790  type(spatial), intent(in) :: location
5791 
5792  ! @param size This is the optional size of the food item. If absent
5793  ! then default food size is used as defined in `COMMONDATA`
5794  real(srp), optional, intent(in) :: size
5795 
5796  ! @param iid id for the food item.
5797  ! @note There are no random iids for food items, iid should agree with
5798  ! the item index within the food resource.
5799  integer, intent(in) :: iid
5800 
5801  !> ### Implementation details ###
5802  !> We here just set the location of the food object using
5803  !! standard interface function `position`.
5804  call this%position(location)
5805 
5806  !> Also, clean up the history stack, i.e. fills it with `MISSING`
5807  !! values.
5808  call this%spatial_history_clean()
5809 
5810  !> Then we set the food item size. Check if optional size is provided and
5811  !! left untouched if not.
5812  !! @note Note that if the value provided is very small (e.g. zero or below),
5813  !! a minimum default value is used as a "floor".
5814  if (present(size)) then
5815  this%size = max(food_item_minimum_size, size)
5816  end if
5817 
5818  !> This food item is NOT eaten from creation, so set the status FALSE.
5819  this%eaten = .false.
5820 
5821  !> Set the individual id `iid`.
5822  call this%set_iid(iid)
5823 
5824  end subroutine food_item_make
5825 
5826  !-----------------------------------------------------------------------------
5827  !> @brief Stochastic outcome of **this** food item capture by an agent.
5828  !! Returns TRUE if the food item is captured.
5829  !! @details In this version, food item capture depends only on the **fixed
5830  !! probability** set by default as `FOOD_ITEM_CAPTURE_PROBABILITY`.
5831  !! Could also implement more complex patterns, e.g. dependent
5832  !! on the food item size (e.g. capture probability increases with
5833  !! food item size).
5834  !! @param[in] prob fixed probability of food item capture.
5835  !! return@ TRUE if capture success.
5836  !! @warning This function does not change the state of the food item object,
5837  !! only returns success.
5838  !! @warning This function cannot be made elemental / puredue to random
5839  !! number call.
5840  function food_item_capture_success_stochast(this, prob) result (success)
5841  class(food_item), intent(in) :: this ! This food object.
5842  ! @param[in] prob fixed probability of food item capture.
5843  real(srp), optional, intent(in) :: prob
5844  ! return@ TRUE if capture success.
5845  logical :: success
5846 
5847  ! Local copy of `prob` parameter.
5848  real(srp) :: prob_here
5849 
5850  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
5851  character(len=*), parameter :: procname="(food_item_capture_success_stochast)"
5852 
5853  success = .false. ! Init to FALSE.
5854 
5855  !> ### Implementation details ###
5856  !> Check if `prob` is present, if not, use default parameter
5857  !! `FOOD_ITEM_CAPTURE_PROBABILITY` value, assuming this food item is in
5858  !! proximity of the predator agent (see function `capture_probability`.
5859  if (present(prob)) then
5860  prob_here = prob
5861  else
5862  prob_here = food_item_capture_probability
5863  end if
5864 
5865  !> First check if this food item is available, if it is not, this may
5866  !! mean it was an error (e.g. only available food items should get into
5867  !! the agent perception object).
5868  if ( this%is_available() ) then
5869  !> @note Note that the probability of capture is fixed by the input
5870  !! value and does not currently depend on the properties of
5871  !! the food item itself. TODO: make option to depend on **this**
5872  !! food item properties, e.g. its size.
5873  if ( rand_r4() < prob_here ) success = .true.
5874  else
5875  call log_dbg( ltag_warn // procname // ", Cannot capture food item " // &
5876  "as it is not available (has been already eaten?). Check code.")
5877  end if
5878 
5880 
5881  !-----------------------------------------------------------------------------
5882  !> Calculate the probability of capture of **this** food item by a predator
5883  !! agent depending on the distance between the agent and this food item.
5884  !! @details Capture probability is determined on the basis of a non-parametric
5885  !! relationship (interpolation) between the distance between the
5886  !! predator agent and this food item. It is equal to the the
5887  !! baseline value set by `commondata::food_item_capture_probability`
5888  !! parameter at the distance 0.0 and approaches a nearly zero value
5889  !! set by the parameter
5890  !! `commondata::food_item_capture_probability_min = 0.1`, at the
5891  !! distance equal to the visual range of the agent.
5892  !! @param[in] distance optional distance to the food item.
5893  !! @param[in] time_step_model optional time step of the model, if absent,
5894  !! obtained from the global variable
5895  !! `commondata::global_time_step_model_current`.
5896  !! @returns The probability of capture of this food item.
5898  distance, time_step_model ) &
5899  result(capt_prob)
5900  class(food_item), intent(in) :: this
5901  ! @param[in] distance optional distance to the food item.
5902  real(srp), optional, intent(in) :: distance
5903  ! @param[in] time_step_model optional time step of the model, if absent,
5904  ! obtained from the global variable
5905  ! `commondata::global_time_step_model_current`.
5906  integer, optional, intent(in) :: time_step_model
5907  ! @returns The probability of capture of this food item.
5908  real(srp) :: capt_prob
5909 
5910  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
5911  character(len=*), parameter :: &
5912  procname="(food_item_capture_probability_calc)"
5913 
5914  ! Local copies of optional dummy parameters.
5915  real(srp) :: distance_here
5916  integer :: time_step_model_here
5917 
5918  ! Local variables.
5919  real(srp) :: visrange_predator
5920 
5921  ! Interpolation grid parameters.
5922  real(srp), dimension(3) :: interpol_abscissa, interpol_ordinate
5923 
5924  ! Check optional time step parameter. If unset, use global
5925  ! `commondata::global_time_step_model_current`.
5926  if (present(time_step_model)) then
5927  time_step_model_here = time_step_model
5928  else
5929  time_step_model_here = global_time_step_model_current
5930  end if
5931 
5932  !> ### Implementation details ###
5933  !> #### Visual range ####
5934  !> First, the visual range for a predator @f$ R_{v} @f$ to detect this
5935  !! food item is calculated using
5936  !! the_environment::food_item_visibility_visual_range().
5937  visrange_predator = this%visibility()
5938 
5939  !> The probability of capture of this food item by a predator is obtained
5940  !! by a nonparametric function that is based on nonlinear interpolation.
5941  !> #### Interpolation grid abscissa ####
5942  !> The distance for calculating the probability of capture of this food
5943  !! item by a predator is expressed in terms of the predator's visual
5944  !! range. The interpolation grid abscissa is set to the distance zero,
5945  !! half of the visual range (@f$ 1/2 R_{v} @f$) and full visual range
5946  !! (@f$ R_{v} @f$) of the predator agent.
5947  interpol_abscissa = [ 0.0_srp, &
5948  visrange_predator/2.0_srp, &
5949  visrange_predator ]
5950 
5951  !> #### Interpolation grid ordinate ####
5952  !> It is assumed that the probability of capture of this food item is
5953  !! equal to the `commondata::food_item_capture_probability` parameter at
5954  !! the distance zero, and reduces to 0.8 of this value at @f$ 1/2 R_{v} @f$,
5955  !! and further reduces to
5956  !! `commondata::food_item_capture_probability_min = 0.1` at
5957  !! the full visual range distance (@f$ R_{v} @f$).
5958  !! @image html img_doxygen_capture_prob.svg
5959  !! @image latex img_doxygen_capture_prob.eps "Capture probability and visual range" width=14cm
5960  !! @note Interpolation plot command:
5961  !! `htintrpl.exe [0.0 0.5 1.0] [0.85, 0.68, 0.1]` (0.68=0.85*0.8).
5962  interpol_ordinate = [ food_item_capture_probability, &
5965 
5966  !> #### Optional distance parameter ####
5967  !> Check optional distance dummy parameter and set local value or
5968  !! default (half a visual range) if the parameter is not provided.
5969  if (present(distance)) then
5970  if (distance > visrange_predator) then
5971  !> Also check if the distance provided is longer than the visual range
5972  !! so this food item cannot be detected by the agent. There should be
5973  !! normally no such cases, if this occurs, it may point to a bug.
5974  call log_dbg( ltag_warn // "distance to food item exceeds " // &
5975  "the visual range:" // tostr(distance) // ">" // &
5976  tostr(visrange_predator), procname, modname )
5977  !> If this is the case, set the capture probability to zero and exit.
5978  capt_prob = 0.0_srp
5979  return
5980  end if
5981  !> Finally, he distance provided should not be equal to the missing value
5982  !! commondata::missing. Return zero capture probability in such a case.
5983  if (distance .feq. missing) then
5984  capt_prob = 0.0_srp
5985  return
5986  end if
5987  distance_here = distance
5988  else
5989  distance_here = visrange_predator/2.0_srp
5990  end if
5991 
5992  !> #### Final calculations ####
5993  !> Finally, the probability of capture of this food item by a predator is
5994  !! obtained by **nonlinear** (`DDPINTERPOL`) interpolation of the distance
5995  !! value expressed in terms of the visual range over the grid set by
5996  !! `interpol_abscissa` and `interpol_ordinate`. There is an additional
5997  !! condition *0 < p < 1* that is enforced by commondata::within().
5998  ! @note Interpolation algorithm can be `LINTERPOL` (linear) or
5999  ! `DDPINTERPOL` (nonlinear) or any other supported by HEDTOOLS.
6000  capt_prob = within( ddpinterpol(interpol_abscissa, interpol_ordinate, &
6001  distance_here), &
6002  0.0_srp, 1.0_srp )
6003 
6004  !> #### Extended debugging outputs ####
6005  !> Log the capture probability visual range calculated, along with the
6006  !! distance and the visual range.
6007  call log_dbg("INFO: Calculated food item capture probability: " // &
6008  tostr(capt_prob) // ", Distance: " // tostr(distance_here) // &
6009  ", Visual range:" // tostr(visrange_predator), procname, modname)
6010 
6011  !> Interpolation plots can be saved in the @ref intro_debug_mode
6012  !! "debug mode" using this plotting command:
6013  !! commondata::debug_interpolate_plot_save().
6015  grid_xx=interpol_abscissa, grid_yy=interpol_ordinate, &
6016  ipol_value=distance_here, &
6017  algstr="DDPINTERPOL", & ! @warning Must be as in `capt_prob`!
6018  output_file="plot_debug_capture_probability_s_" // &
6019  tostr(global_time_step_model_current) // "_" // &
6020  tostr(this%get_iid()) // "_" // &
6021  rand_string(label_length, label_cst, label_cen) // ps )
6022 
6024 
6025  !-----------------------------------------------------------------------------
6026  !> Calculate the visibility range of this food item. Wrapper to the
6027  !! the_environment::visual_range() function. This function calculates
6028  !! the distance from which this food item can be seen by a predator
6029  !! (i.e. the default predator's visual range).
6030  !! @warning The `visual_range` procedures use meter for units, this
6031  !! auto-converts to cm.
6032  !! @warning Cannot implement a generic function accepting also vectors of
6033  !! this objects as only elemental object-bound array functions are
6034  !! allowed by the standard. This function cannot be elemental, so
6035  !! passed-object dummy argument must always be scalar.
6036  function food_item_visibility_visual_range(this, object_area, contrast, &
6037  time_step_model) result (visrange)
6038  class(food_item), intent(in) :: this
6039  !> @param[in] object_area is optional area of the food item (m). If not
6040  !! provided, obtained from the this object `size` attribute
6041  !! (the_environment::food_item::size).
6042  real(srp), optional, intent(in) :: object_area
6043  !> @param[in] contrast is optional inherent visual contrast of the food
6044  !! item. the default contrast of all objects is defined by the
6045  !! commondata::preycontrast_default parameter.
6046  real(srp), optional, intent(in) :: contrast
6047  !> @param[in] optional time step of the model, if absent gets the current
6048  !! time step as defined by the value of
6049  !! `commondata::global_time_step_model_current`.
6050  integer, optional, intent(in) :: time_step_model
6051  real(srp) :: visrange
6052 
6053  ! Local copies of optionals
6054  real(srp) :: object_area_here, contrast_here
6055 
6056  ! Local variables
6057  real(srp) :: irradiance_agent_depth
6058  integer :: time_step_model_here
6059 
6060  ! Check optional object area, the default value, if this parameter is
6061  ! absent, is the circle area of this food item converted to m.
6062  if (present(object_area)) then
6063  object_area_here = object_area
6064  else
6065  object_area_here = carea( cm2m( this%size ) )
6066  end if
6067 
6068  ! Check optional `contrast` parameter. If unset, use global
6069  ! `commondata::preycontrast_default`.
6070  if (present(contrast)) then
6071  contrast_here = contrast
6072  else
6073  contrast_here = preycontrast_default
6074  end if
6075 
6076  ! Check optional time step parameter. If unset, use global
6077  ! `commondata::global_time_step_model_current`.
6078  if (present(time_step_model)) then
6079  time_step_model_here = time_step_model
6080  else
6081  time_step_model_here = global_time_step_model_current
6082  end if
6083 
6084  !> ### Implementation details ###
6085  !> Calculate ambient illumination / irradiance at the depth of
6086  !! this food item at the given time step.
6087  irradiance_agent_depth = this%illumination(time_step_model_here)
6088 
6089  !> Return visual range of a predator to see this food item.
6090  visrange = m2cm( visual_range( irradiance = irradiance_agent_depth, &
6091  prey_area = object_area_here, &
6092  prey_contrast = contrast_here ) )
6093 
6095 
6096  !-----------------------------------------------------------------------------
6097  !> Find the depth at which the visibility of a spatial object becomes
6098  !! smaller than a specific distance value `target_range`.
6099  !! @note This is a diagnostic function.
6100  function minimum_depth_visibility( target_range, &
6101  depth_range_min, depth_range_max, &
6102  object_area, object_contrast, &
6103  time_step_model ) result (depth_out)
6104  !> @param[in] target_range This is the target visual range (visibility)
6105  !! value: this function calculates the depth at which visibility
6106  !! becomes smaller than this target range.
6107  real(srp), intent(in) :: target_range
6108 
6109  !> !> @param[in] depth_range_min sets the optimisation range for depth,
6110  !! this is the **minimum** depth where the search is done.
6111  !! This is an optional parameter, if absent, is set to the
6112  !! global minimum depth in the global habitats array
6113  !! commondata::global_habitats_available.
6114  real(srp), optional, intent(in) :: depth_range_min
6115  !> !> @param[in] depth_range_max sets the optimisation range for depth,
6116  !! this is the **maximum** depth where the search is done.
6117  !! This is an optional parameter, if absent, is set to the
6118  !! global maximum depth in the global habitats array
6119  !! commondata::global_habitats_available.
6120  real(srp), optional, intent(in) :: depth_range_max
6121 
6122  !> @param[in] object_area is the optional area of the spatial object (m) for
6123  !! which the depth is calculated. If absent, is calculated for
6124  !! the default average food item
6125  !! commondata::food_item_size_default.
6126  real(srp), optional, intent(in) :: object_area
6127  !> @param[in] object_contrast optional contrast of the spatial object for
6128  !! which the calculation is done; if absent is set from the
6129  !! default commondata::preycontrast_default.
6130  real(srp), optional, intent(in) :: object_contrast
6131 
6132  !> @param[in] time_step_model is the time step of the model. If absent, is
6133  !! obtained from the global variable
6134  !! commondata::global_time_step_model_current.
6135  integer, optional, intent(in) :: time_step_model
6136  real(srp) :: depth_out
6137 
6138  ! Local copies of optionals
6139  real(srp) :: depth_range_min_loc, depth_range_max_loc
6140  real(srp) :: object_area_here, contrast_here
6141  integer :: time_step_model_here
6142 
6143  ! Check optional parameters and calculate the default range of the depths.
6144  ! Note that if depth range values are not set as parameters explicitly,
6145  ! they are calculated from the global habitats array
6146  ! commondata::global_time_step_model_current.
6147  if (present(depth_range_min)) then
6148  depth_range_min_loc = depth_range_min
6149  else
6150  depth_range_min_loc = minval( global_habitats_available%depth_min() )
6151  end if
6152 
6153  if (present(depth_range_max)) then
6154  depth_range_max_loc = depth_range_max
6155  else
6156  depth_range_max_loc = maxval( global_habitats_available%depth_max() )
6157  end if
6158 
6159  ! Check optional object area, the default value, if this parameter is
6160  ! absent, is the circle area of the default food item converted to m,
6161  ! commondata::food_item_size_default.
6162  if (present(object_area)) then
6163  object_area_here = object_area
6164  else
6165  object_area_here = carea( cm2m( food_item_size_default ) )
6166  end if
6167 
6168  ! Check optional `contrast` parameter. If unset, use global
6169  ! `commondata::preycontrast_default`.
6170  if (present(object_contrast)) then
6171  contrast_here = object_contrast
6172  else
6173  contrast_here = preycontrast_default
6174  end if
6175 
6176  ! Check optional time step parameter. If unset, use global
6177  ! `commondata::global_time_step_model_current`.
6178  if (present(time_step_model)) then
6179  time_step_model_here = time_step_model
6180  else
6181  time_step_model_here = global_time_step_model_current
6182  end if
6183 
6184  !> ### Implementation details ###
6185  !! The depth when the visibility of the spatial object becomes smaller
6186  !! than the target distance `target_range` is calculated using the Brent's
6187  !! commondata::zeroin() optimisation algorithm, see Brent, R., (1973).
6188  !! Algorithms for Minimization Without Derivatives, Prentice-Hall, Inc.
6189  !!
6190  !! The function for calculating the visibility of this spatial object
6191  !! for optimisation by `zeroin` is calculated by ::visibility_loc().
6192  !! This function is local to this function and is further wrapped into
6193  !! also local ::visibility_loc_diff() (it is set as the parameter f to
6194  !! commondata::zeroin()).
6195  depth_out = zeroin( depth_range_min_loc, &
6196  depth_range_max_loc, &
6199 
6200  !> If the the depth cannot be calculated, further checks are done and
6201  !! an appropriate limiting value is set for this function return.
6202  if ( depth_out .feq. missing ) then
6203  if ( visibility_loc(depth_range_max_loc) > target_range ) then
6204  depth_out = depth_range_max_loc
6205  else if ( visibility_loc(depth_range_min_loc) < target_range ) then
6206  depth_out = depth_range_min_loc
6207  end if
6208  end if
6209 
6210  contains
6211  !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6212  !> This function calculates the visibility range of the spatial object
6213  !! at the depth given by the argument `depth`.
6214  !! This function is internal to
6215  !! the_environment::minimum_depth_visibility().
6216  function visibility_loc(depth) result (vis_out)
6217  real(srp), intent(in) :: depth
6218  real(srp) :: vis_out
6219 
6220  real(srp) :: irradiance_at_depth
6221 
6222  !> Calculate ambient illumination / irradiance at the depth of
6223  !! this food item at the given time step.
6224  irradiance_at_depth = &
6225  light_depth( depth=depth, &
6226  surface_light = light_surface( &
6227  tstep=time_step_model_here, &
6228  is_stochastic=.false.) )
6229 
6230  !> Return the visibility range for this spatial object.
6231  vis_out = m2cm( visual_range( irradiance = irradiance_at_depth, &
6232  prey_area = object_area_here, &
6233  prey_contrast = contrast_here ) )
6234 
6235  end function visibility_loc
6236 
6237  !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6238  !> This is a wrapper function that calculates the visibility range minus
6239  !! target minimum distance.
6240  !! This function is internal to
6241  !! the_environment::minimum_depth_visibility().
6242  real(srp) function visibility_loc_diff(depth)
6243  !> @param[in] depth the depth of the spatial object.
6244  real(srp), intent(in) :: depth
6245  visibility_loc_diff = visibility_loc(depth) - target_range
6246  end function visibility_loc_diff
6247 
6248  end function minimum_depth_visibility
6249 
6250  !-----------------------------------------------------------------------------
6251  !> Make the food item "disappear" and take the "eaten" state, i.e.
6252  !! impossible for consumption by the agents.
6253  elemental subroutine food_item_disappear(this)
6254  class(food_item), intent(inout) :: this
6255 
6256  this%eaten = .true.
6257 
6258  end subroutine food_item_disappear
6259 
6260  !-----------------------------------------------------------------------------
6261  !> Logical check-indicator function for the food item being eaten and not
6262  !! available.
6263  !! @returns Logical indicator TRUE if the food item is eaten and not
6264  !! available any more.
6265  elemental function food_item_is_eaten_unavailable(this) result(not_available)
6266  class(food_item), intent(in) :: this
6267 
6268  ! @returns Logical indicator TRUE if the food item is eaten and not
6269  ! available any more.
6270  logical :: not_available
6271 
6272  if (this%eaten) then
6273  not_available = .true.
6274  else
6275  not_available = .false.
6276  end if
6277 
6278  end function food_item_is_eaten_unavailable
6279 
6280  !-----------------------------------------------------------------------------
6281  !> Logical check-indicator function for the food item being available.
6282  !! @returns Logical indicator TRUE if the food item is present
6283  !! in the environment and therefore available.
6284  !! @note It is the opposite of the above function
6285  !! the_environment::food_item::is_unavailable().
6286  elemental function food_item_is_available(this) result(available)
6287  class(food_item), intent(in) :: this
6288 
6289  ! @returns Logical indicator TRUE if the food item is present
6290  ! in the environment and therefore available.
6291  logical :: available
6292 
6293  if (this%eaten) then
6294  available = .false.
6295  else
6296  available = .true.
6297  end if
6298 
6299  end function food_item_is_available
6300 
6301  !-----------------------------------------------------------------------------
6302  !> Get the size component of the food item object.
6303  !! @returns item_size The size of the food item.
6304  elemental function food_item_get_size(this) result (item_size)
6305  class(food_item), intent(in) :: this
6306 
6307  ! @returns item_size The size of the food item.
6308  real(srp) :: item_size
6309 
6310  item_size = this%size
6311 
6312  end function food_item_get_size
6313 
6314  !-----------------------------------------------------------------------------
6315  !> Calculate the mass of a food item, the non-OO backend.
6316  !! @details The food item mass depends on the density of the food and its
6317  !! volume @f[ \rho \cdot \frac{4}{3}\pi r^{3} , @f]
6318  !! where @f$ \rho @f$ is the food density and
6319  !! @f[ V = \frac{4}{3}\pi r^{3} @f] is the food item volume.
6320  !! @note Food item density is set by commondata::food_item_density
6321  !! parameter.
6322  elemental function size2mass_food(radius) result (this_mass)
6323  real(srp), intent(in) :: radius
6324  real(srp) :: this_mass
6325 
6326  this_mass = food_item_density * (4.0_srp/3.0_srp * pi * radius**3)
6327 
6328  end function size2mass_food
6329 
6330  !-----------------------------------------------------------------------------
6331  !> Calculate the size (radius) of a food item, a reverse function of
6332  !! the_environment::size2mass_food():
6333  !! @f[ = \sqrt[3]{ \frac{M}{\rho \cdot 4/3 \pi} } , @f]
6334  !! where @f$ M @f$ is the food item mass, @f$ \rho @f$ is its density.
6335  !! @note Food item density is set by commondata::food_item_density
6336  !! parameter.
6337  elemental function mass2size_food(mass) result (radius)
6338  real(srp), intent(in) :: mass
6339  real(srp) :: radius
6340 
6341  radius = ( mass / (food_item_density*4.0_srp/3.0_srp*pi) )**(1_srp/3_srp)
6342 
6343  end function mass2size_food
6344 
6345  !-----------------------------------------------------------------------------
6346  !> Calculate and get the mass of the food item.
6347  !! @returns item_mass The mass of the food item.
6348  !! @note This is an OO frontend/wrapper.
6349  elemental function food_item_get_mass(this) result (item_mass)
6350  class(food_item), intent(in) :: this
6351 
6352  ! @returns item_mass The mass of the food item.
6353  real(srp) :: item_mass
6354 
6355  !> ### Implementation details ###
6356  !> The food item mass depends on the density of the food and its volume
6357  !! @f[ \rho \cdot \frac{4}{3}\pi r^{3} , @f] where @f$ \rho @f$ is the food
6358  !! density and @f[ V = \frac{4}{3}\pi r^{3} @f] is the food item volume.
6359  ! @note Use the `size2mass_food` non-OO backend for calculation.
6360  item_mass = size2mass_food(this%size)
6361 
6362  end function food_item_get_mass
6363 
6364  !-----------------------------------------------------------------------------
6365  !> Set unique id for the food item object.
6366  !! @param iid individual id number for the food item.
6367  elemental subroutine food_item_set_iid(this, iid)
6368  class(food_item), intent(inout) :: this
6369 
6370  ! @param iid individual id number for the food item.
6371  integer, intent(in) :: iid
6372 
6373  ! Set the food item iid
6374  this%food_iid = iid
6375 
6376  end subroutine food_item_set_iid
6377 
6378  !-----------------------------------------------------------------------------
6379  !> Clone the properties of this food item to another food item.
6380  !! @note Note that the this food item serves as the source and the other,
6381  !! as the destination for cloning. So when used like this
6382  !! `food_item_source%clone( cloned_to_this_destination_item )`
6383  elemental subroutine food_item_clone_assign(this, the_other)
6384  class(food_item), intent(in) :: this
6385  !> param[inout] the_other Target food item to which the properties of
6386  !! the **this** item are cloned. It is declared as a
6387  !! polymorphic `class` so can get an extension type.
6388  class(food_item), intent(inout) :: the_other
6389 
6390  !> ### Implementation details ###
6391  !> Use make method to transfer all the properties from `this` to
6392  !! `the_other`.
6393  call the_other%make(this%location(), this%size, this%food_iid)
6394  the_other%eaten = this%eaten
6395 
6396  end subroutine food_item_clone_assign
6397 
6398  !-----------------------------------------------------------------------------
6399  !> Get the unique id of the food item object.
6400  !! @returns iid the individual id number of this food item.
6401  elemental function food_item_get_iid(this) result(iid)
6402  class(food_item), intent(in) :: this
6403 
6404  ! @returns iid the individual id number of this food item.
6405  integer :: iid
6406 
6407  iid = this%food_iid
6408 
6409  end function food_item_get_iid
6410 
6411  !-----------------------------------------------------------------------------
6412  !> Make food resource object. This class standard constructor.
6413  !! @param label Label for this food resource object.
6414  !! @param abundance the number of food items in the resource.
6415  !! @param locations An array of `SPATIAL` locations of each food item.
6416  !! @param sizes An array of sizes of each food item.
6417  pure subroutine food_resource_make(this, label, abundance, locations, sizes)
6418  class(food_resource), intent(inout) :: this
6419 
6420  ! @param label Label for this food resource
6421  character(len=*), intent(in) :: label
6422 
6423  ! @param abundance the number of food items in the resource
6424  integer, intent(in) :: abundance
6425 
6426  ! @param locations An array of `SPATIAL` locations of each food item
6427  type(spatial), dimension(:), intent(in) :: locations
6428 
6429  ! @param sizes An array of sizes of each food item.
6430  real(srp), dimension(:), intent(in) :: sizes
6431 
6432  ! Local counter
6433  integer :: i
6434 
6435  !> ### Implementation details ###
6436  !> First, we allocate the array of the food item objects with the size
6437  !! of `abundance` parameter.
6438  if (.not. allocated(this%food)) allocate(this%food(abundance))
6439 
6440  !> Set label from input data.
6441  this%food_label = label
6442 
6443  !> Set abundance from input data.
6444  this%number_food_items = abundance
6445 
6446  !> Create all food items in the resource,
6447  ! @note Note that `create` method is elemental function so we can
6448  ! create a whole array. This might be faster and can parallelizse.
6449  call this%food%create()
6450 
6451  !> Set locations and food item sizes from input data vectors.
6452  ! But first, initialise locations of all these food items by `create` to
6453  ! `MISSING` and sizes to the default `FOOD_ITEM_SIZE_DEFAULT`. This
6454  ! would guard against non-initialised objects if `locations` and `sizes`
6455  ! would guard happen to have different sizes.
6456  do concurrent( i=1:size(this%food) )
6457  !call this%food(i)%create() !> Non-elemental variant of `create`.
6458  call this%food(i)%make( locations(i), sizes(i), i )
6459  end do
6460 
6461  end subroutine food_resource_make
6462 
6463  !-----------------------------------------------------------------------------
6464  !> Get the number of food items in the food resource.
6465  elemental function food_resource_get_abundance(this) result (abundance_out)
6466  class(food_resource), intent(in) :: this
6467  !> @return The number of food items in this food resource.
6468  integer :: abundance_out
6469 
6470  abundance_out = this%number_food_items
6471 
6472  end function food_resource_get_abundance
6473 
6474  !-----------------------------------------------------------------------------
6475  !> Get the label of the this food resource.
6476  elemental function food_resource_get_label(this) result (label_out)
6477  class(food_resource), intent(in) :: this
6478  !> @return The label of this food resource.
6479  character(len=LABEL_LENGTH) :: label_out
6480 
6481  label_out = this%food_label
6482 
6483  end function food_resource_get_label
6484 
6485  !-----------------------------------------------------------------------------
6486  !> Delete and deallocate food resource object. This class standard destructor.
6487  pure subroutine food_resource_destroy_deallocate(this)
6488  class(food_resource), intent(inout) :: this
6489 
6490  ! Deallocate the array of the food item objects.
6491  if (allocated(this%food)) deallocate(this%food)
6492 
6493  ! Set undefined label from input data.
6494  this%food_label = "undefined"
6495 
6496  ! Set unknown abundance.
6497  this%number_food_items = unknown
6498 
6499  end subroutine food_resource_destroy_deallocate
6500 
6501  !-----------------------------------------------------------------------------
6502  !> Get the location object array (array of `SPATIAL` objects) of a
6503  !! food resource object.
6504  !! @returns locate_array an array of `SPATIAL` location objects for all
6505  !! food items within the resource.
6506  pure function food_resource_locate_3d(this) result (locate_array)
6507  class(food_resource), intent(in) :: this
6508 
6509  ! @returns locate_array an array of `SPATIAL` location objects for all
6510  ! food items within the resource.
6511  type(spatial), dimension(size(this%food)) :: locate_array
6512 
6513  ! Local counter
6514  integer :: i
6515 
6516  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
6517  ! implemented in all compilers. Use normal `do` in such a case.
6518  do concurrent( i=1:size(this%food) )
6519  locate_array(i) = this%food(i)%location()
6520  end do
6521 
6522  end function food_resource_locate_3d
6523 
6524  !-----------------------------------------------------------------------------
6525  !> Calculate the average distance between food items within a resource.
6526  !! e.g. to compare it with the agent's random walk step size.
6528  result(average_distance_food_items)
6529  class(food_resource), intent(in) :: this
6530  integer, optional, intent(in) :: n_sample
6531  real(srp) :: average_distance_food_items
6532 
6533  ! Local copies of optionals
6534  integer :: n_sample_here
6535 
6536  ! Default sample size if not provided
6537  integer, parameter :: n_sample_default = 100
6538 
6539  if (present(n_sample)) then
6540  n_sample_here = n_sample
6541  else
6542  n_sample_here = n_sample_default
6543  end if
6544 
6545  average_distance_food_items = distance_average(this%food, n_sample_here)
6546 
6548 
6549  !-----------------------------------------------------------------------------
6550  !> Replenish and restore food resource. The food resource is replenished
6551  !! by substituting randomly selected `replace` food items or all items if
6552  !! `replace` is omitted or exceeds the actual number of food
6553  !! items. Unlike the the_environment::food_resource::make() method, the
6554  !! sizes and the positions of the food items within the resource are reused
6555  !! from the previous positions (previously explicitly set set by the
6556  !! the_environment::food_resource::make() method).
6557  !! @warning This method is not used to build the food resource for the
6558  !! first time ("init"). Use the_environment::food_resource::make()
6559  !! to do this instead. This method is only for *modifying* the
6560  !! existing food resource object.
6561  subroutine food_resource_replenish_food_items_all(this, replace)
6562  class(food_resource), intent(inout) :: this
6563  !> @param[in] replace Optional number of food items that replace those
6564  !! those that have been eaten in the population, this replace
6565  !! number cannot make the food resource population size
6566  !! bigger than the previous value.
6567  integer, optional, intent(in) :: replace
6568 
6569  ! Local count of eaten food items
6570  integer :: n_eaten
6571 
6572  ! Local array of the indices of all eaten food items
6573  integer, dimension(:), allocatable :: idx_eaten_items
6574 
6575  ! Local counters
6576  integer :: i, j
6577 
6578  !> ### Implementation notes ###
6579  !> Implementation differs depending on full (`replase` absent) or partial
6580  !! replenishment (`replace` present).
6581  !> - If the `replace` parameter is set, a maximum of this number of eaten
6582  !! food items will be returned to the food resource, i.e. set the "not
6583  !! eaten" status.
6584  partial_full: if (present(replace)) then
6585  !> - First count food items that are eaten.
6586  n_eaten = count( this%food%eaten .eqv. .true. )
6587  !> - If the number of food items replaced (`replace`) exceeds this
6588  !! value `n_eaten` we reset all eaten food items as "not eaten."
6589  if ( replace >= n_eaten ) then
6590  where ( this%food%eaten ) this%food%eaten = .false.
6591  !> - Otherwise, the number of food items defined by `replace` will
6592  !! be replaced randomly (set not eaten) in the eaten. To do this:
6593  else
6594  !> - allocate a temporary array of the size equal
6595  !! to this number of eaten items, initialised to
6596  !! commondata::unknown.
6597  allocate( idx_eaten_items(n_eaten) ); idx_eaten_items = unknown
6598  !> - put indexes of all eaten food items into the
6599  !! `idx_eaten_items` array.
6600  j=0
6601  do concurrent(i=1:size(this%food))
6602  if (this%food(i)%eaten .eqv. .true.) then
6603  j = j + 1
6604  idx_eaten_items(j) = i
6605  end if
6606  end do
6607  !> - reorder `idx_eaten_items` in a random order
6608  idx_eaten_items = idx_eaten_items( permute_random(n_eaten) )
6609  !> - finally, reset the first `replace` food items as not eaten
6610  do concurrent(i=1:replace)
6611  this%food(idx_eaten_items(i))%eaten = .false.
6612  end do
6613  end if
6614 
6615  !> - If `replace` parameter is not present, just reset the
6616  !! the_environment::food_resource::food::eaten indicator of each food
6617  !! item to FALSE. Thus, the food resource is just reset to a fully
6618  !! available state.
6619  !! .
6620  !! @note Note that the food items within the food resource retain their
6621  !! sizes and positions unchanged. If fully stochastic resource
6622  !! is required or its size needs to be altered, use the
6623  !! the_environment::food_resource::make() method.
6624  else partial_full
6625  this%food%eaten = .false.
6626 
6627  end if partial_full
6628 
6630 
6631  !-----------------------------------------------------------------------------
6632  !> This subroutine implements the migration of all the food items in the
6633  !! resource according to the plankton migration pattern from the G1 model
6634  !! (HED18). Briefly, the movement of each of the food items has two
6635  !! components:
6636  !! - deterministic: regular vertical migration movement;
6637  !! - stochastic: small scale random Gaussian displacement.
6638  !! .
6639  !!
6640  !! #### Global habitats array ####
6641  !! If the habitats are assembled into the global array
6642  !! the_environment::global_habitats_available and the migration is done
6643  !! on the original habitat-bound food resource objects, these original
6644  !! habitat objects and the global array require constant synchronisation
6645  !! with the the_environment::assemble() and the_environment::disassemble()
6646  !! procedures. Here is an example:
6647  !! @code
6648  !! call disassemble( habitat_test1, habitat_test2 )
6649  !! call habitat_test1%food%migrate_vertical()
6650  !! call habitat_test2%food%migrate_vertical()
6651  !! call assemble ( habitat_test1, habitat_test2 )
6652  !! @endcode
6653  !!
6654  !! To avoid this, a separate procedure that works directly on an *array* of
6655  !! habitat objects is implemented: the_environment::migrate_food_vertical().
6656  !! Its use is more efficient:
6657  !! @code
6658  !! call migrate_food_vertical( Global_Habitats_Available )
6659  !! @endcode
6660  subroutine food_resource_migrate_move_items(this, max_depth, time_step_model)
6661  class(food_resource), intent(inout) :: this
6662  !> @param[in] max_depth optional maximum depth of deterministic vertical
6663  !! migration; if this parameter is absent, the maximum depth
6664  !! in the habitat container for this food resource is used
6665  real(SRP), optional, intent(in) :: max_depth
6666  !> @param[in] time_step_model Optional time step of the model. If absent,
6667  !! the time step is obtained from the global array
6668  !! commondata::global_habitats_available.
6669  integer, optional, intent(in) :: time_step_model
6670 
6671  ! Local copies of optionals
6672  real(SRP) :: depth_loc
6673  integer :: tstep_loc
6674 
6675  ! Fixed habitat number in the global array
6676  integer :: habitat_res
6677 
6678  ! Local counter for food items.
6679  integer ifood
6680 
6681  ! The new position for the food item that involves only vertical shift.
6682  type(spatial) :: position_new
6683 
6684  real(SRP) :: target_depth
6685 
6686  !> ### Implementation notes ###
6687  !> #### Checks and preparations ####
6688  !> First, the procedure checks if the maximum depth for the vertical
6689  !! migration of food items is provided. If not, the maximum depth is
6690  !! determined as the maximum depth in the habitat of a single randomly
6691  !! chosen food item in this resource (see
6692  !! the_environment::spatial::find_environment()). Because a single food
6693  !! resource is nested within a specific habitat, all items are located
6694  !! in this environment, so the maximum depth is the same for all items of
6695  !! the resource.
6696  ifood = rand_i(1, this%number_food_items)
6697  !> Thus, `habitat_res` keeps the number of the habitat in the global array
6698  !! the_environment::global_habitats_available
6699  habitat_res = this%food(ifood)%find_environment()
6700  if (present(max_depth)) then
6701  depth_loc = max_depth
6702  else
6703  depth_loc = global_habitats_available(habitat_res)%depth_max()
6704  end if
6705 
6706  !> Second, a check is done if the time step is provided. If no, the global
6707  !! time step from commondata::global_time_step_model_current is used.
6708  if (present(time_step_model)) then
6709  tstep_loc = time_step_model
6710  else
6711  tstep_loc = global_time_step_model_current
6712  end if
6713 
6714  !> #### Deterministic move ####
6715  !> The depth centre of the food item vertical distribution is determined
6716  !! using the code from the HED18 model with minimum adaptations.
6717  !! This code is isolated into the the_environment::center_depth_sinusoidal()
6718  !! function.
6719  target_depth = center_depth_sinusoidal(tstep_loc, depth_loc)
6720 
6721  !> Once the target depth for the food items is known, all the food
6722  !! resource is moved up or down to the target depth.
6723  do concurrent(ifood=1:this%number_food_items)
6724  position_new = spatial( this%food(ifood)%xpos(), &
6725  this%food(ifood)%ypos(), &
6726  target_depth )
6727  call this%food(ifood)%position( position_new )
6728  end do
6729 
6730  !> #### Stochastic walk ####
6731  !> The second phase of the food item migration involves a stochastic
6732  !! random walk of each of the food items. This stochastic movement is
6733  !! described by the following parameters of the
6734  !! the_environment::spatial_moving::rwalk():
6735  !! - commondata::food_item_migrate_xy_mean,
6736  !! - commondata::food_item_migrate_xy_cv ,
6737  !! - commondata::food_item_migrate_depth_mean,
6738  !! - commondata::food_item_migrate_depth_cv.
6739  !! - the limiting habitat for random walk is defined by the `habitat_res`
6740  !! element of the global array the_environment::global_habitats_available
6741  !! the same for all items as food resource is nested within the habitat.
6742  !! .
6743  !! @note Note that the stochastic random walk coincides with the default
6744  !! the_environment::food_resource::rwalk() method. The code is
6745  !! retained independently here because the parameters might not
6746  !! coincide with the default walk. Otherwise use call the default
6747  !! @code
6748  !! call this%rwalk()
6749  !! @endcode
6750  do ifood=1, this%number_food_items
6751  call this%food(ifood)%rwalk( meanshift_xy = food_item_migrate_xy_mean, &
6752  cv_shift_xy = food_item_migrate_xy_cv, &
6753  meanshift_depth = food_item_migrate_depth_mean, &
6754  cv_shift_depth = food_item_migrate_depth_cv, &
6755  environment_limits = &
6756  global_habitats_available(habitat_res) )
6757  end do
6758 
6759  end subroutine food_resource_migrate_move_items
6760 
6761  !-----------------------------------------------------------------------------
6762  !> Perform a random walk step for all food items within the food resource.
6763  !! The walk is performed with the default parameters:
6764  !! - commondata::food_item_migrate_xy_mean,
6765  !! - commondata::food_item_migrate_xy_cv ,
6766  !! - commondata::food_item_migrate_depth_mean,
6767  !! - commondata::food_item_migrate_depth_cv.
6768  !! .
6770  class(food_resource), intent(inout) :: this
6771 
6772  ! Fixed habitat number in the global array
6773  ! the_environment::global_habitats_available
6774  integer :: ifood, habitat_res
6775 
6776  !> ### Implementation notes ###
6777  !> First, the habitat number within the global habitats array
6778  !! the_environment::global_habitats_available is determined for a
6779  !! single randomly chosen food item within this resource. Because
6780  !! the food resource is bound to the habitat, this identifies the
6781  !! habitat.
6782  ifood = rand_i(1, this%number_food_items)
6783  habitat_res = this%food(ifood)%find_environment()
6784 
6785  !> Then, all food items within the resource are subjected to Gaussian
6786  !! random walk the_environment::spatial_moving::rwalk() with the
6787  !! following parameters:
6788  !! - commondata::food_item_migrate_xy_mean,
6789  !! - commondata::food_item_migrate_xy_cv ,
6790  !! - commondata::food_item_migrate_depth_mean,
6791  !! - commondata::food_item_migrate_depth_cv.
6792  !! - the limiting habitat for random walk is defined by the `habitat_res`
6793  !! element of the global array the_environment::global_habitats_available
6794  !! the same for all items as food resource is nested within the habitat.
6795  !! .
6796  do ifood=1, this%number_food_items
6797  call this%food(ifood)%rwalk( meanshift_xy = food_item_migrate_xy_mean, &
6798  cv_shift_xy = food_item_migrate_xy_cv, &
6799  meanshift_depth = food_item_migrate_depth_mean, &
6800  cv_shift_depth = food_item_migrate_depth_cv, &
6801  environment_limits = &
6802  global_habitats_available(habitat_res) )
6803  end do
6804 
6805  end subroutine food_resource_rwalk_items_default
6806 
6807  !-----------------------------------------------------------------------------
6808  !> Migrate food items in a whole array of food resources. The array is
6809  !! normally the the_environment::global_habitats_available.
6810  !! @code
6811  !! call migrate_food_vertical( Global_Habitats_Available )
6812  !! @endcode
6813  !! @note This is a not type-bound procedure.
6814  !!
6815  !! #### Global habitats array ####
6816  !! All the habitat objects are normally assembled into the global array
6817  !! the_environment::global_habitats_available. If so, this procedure makes
6818  !! it unnecessary to synchronise the habitat objects with the global array
6819  !! constantly by calling the_environment::habitat::disassemble() and
6820  !! the_environment::habitat::assemble() procedures whenever the habitat
6821  !! objects are changed (foods migrated). It makes the migration change
6822  !! directly on the *global array*.
6823  !!
6824  !! The the_environment::food_resource::migrate_vertical() method that
6825  !! operates directly on *food resource object* in such case would require
6826  !! constant synchronisation/update between the global array and each
6827  !! habitat-bound food resource object, e.g.:
6828  !! @code
6829  !! call disassemble( habitat_test1, habitat_test2 )
6830  !! call habitat_test1%food%migrate_vertical()
6831  !! call habitat_test2%food%migrate_vertical()
6832  !! call assemble ( habitat_test1, habitat_test2 )
6833  !! @endcode
6834  subroutine migrate_food_vertical(habitats, time_step_model)
6835  !> @param[inout] habitats is an array of habitats
6836  class(habitat), dimension(:), intent(inout) :: habitats
6837  !> @param[in] time_step_model Optional time step of the model. If absent,
6838  !! the time step is obtained from the global array
6839  !! commondata::global_time_step_model_current.
6840  integer, optional, intent(in) :: time_step_model
6841 
6842  ! Local copies of optionals
6843  integer :: tstep_loc
6844 
6845  integer :: i
6846 
6847  !> ### Implementation notes ###
6848  !> A check is done if the time step is provided. If no, the global
6849  !! time step from commondata::global_time_step_model_current is used.
6850  if (present(time_step_model)) then
6851  tstep_loc = time_step_model
6852  else
6853  tstep_loc = global_time_step_model_current
6854  end if
6855 
6856  !> Then food resources within each of the habitats within the `habitats`
6857  !! array is subjected to the
6858  !! the_environment::food_resource::migrate_vertical() method.
6859  do i=1, size(habitats)
6860  call habitats(i)%food%migrate_vertical(time_step_model = tstep_loc)
6861  end do
6862 
6863  end subroutine migrate_food_vertical
6864 
6865  !-----------------------------------------------------------------------------
6866  !> Perform a random walk of food items in a whole array of food resources.
6867  !! The array is normally the the_environment::global_habitats_available.
6868  !! This procedure is a wrapper for the_environment::food_resource::rwalk()
6869  !! to do a walk on a whole array of habitats and linked food resources.
6870  !! @code
6871  !! call rwalk_food_step( Global_Habitats_Available )
6872  !! @endcode
6873  !! @note This is a not type-bound procedure.
6874  subroutine rwalk_food_step(habitats)
6875  !> @param[inout] habitats is an array of habitats
6876  class(habitat), dimension(:), intent(inout) :: habitats
6877 
6878  integer :: i
6879 
6880  !> ### Implementation notes ###
6881  !> All the food resources within each of the habitats within the `habitats`
6882  !! array is subjected to the
6883  !! the_environment::food_resource::rwalk() method.
6884  do i=1, size(habitats)
6885  call habitats(i)%food%rwalk()
6886  end do
6887 
6888  end subroutine rwalk_food_step
6889 
6890  !-----------------------------------------------------------------------------
6891  !> This function calculates the target depth for the sinusoidal vertical
6892  !! migration pattern of the food items at each time step of the model.
6893  !! See the_environment::food_resource_migrate_move_items() for the calling
6894  !! procedure.
6895  !! @note The depth centre of the food item vertical distribution
6896  !! is determined using the code from the HED18 model with
6897  !! minimum adaptations. This function isolates this HED18 code.
6898  !! The pattern coincides with the COPDVM = 1 in the HED18 model.
6899  !! Different pattern(s) is easy to add in separate procedures.
6900  !! @note This procedure is isolated into a separate one, so
6901  !! diagnostic data/plots can be saved.
6902  function center_depth_sinusoidal(tstep, depth) result (copcenterdepth)
6903  !> @param[in] tstep time step of the model.
6904  integer, intent(in) :: tstep
6905  !> @param[in] depth sets the maximum target depth for vertical migration.
6906  real(srp), intent(in) :: depth
6907  !> @returns The target depth for the regular migration of the food
6908  !! items.
6909  real(srp) :: copcenterdepth
6910 
6911  ! Local variable.
6912  real(srp) :: copsindepth
6913 
6914  !> Verbatim code from HED18:
6915  !! @code
6916  !! do a = 1, flifespan
6917  !! !find center of copepod distribution
6918  !! copsindepth = sin(3.14*a*2.*dielcycles/(flifespan+1.))
6919  !! copcenterdepth = int(depth/2 + 0.33*depth*copsindepth)
6920  !! @endcode
6921  copsindepth = sin( pi * real(tstep,srp) * 2.0_srp * &
6922  real(dielcycles, srp)/( real(LIFESPAN, SRP) + 1.0_srp) )
6923  copcenterdepth = depth / 2.0_srp + 0.499_srp * depth*copsindepth
6924 
6925  end function center_depth_sinusoidal
6926 
6927  !-----------------------------------------------------------------------------
6928  !> Save characteristics of food items in the resource into a CSV file.
6929  subroutine food_resource_save_foods_csv(this, csv_file_name, is_success)
6930  class(food_resource), intent(in) :: this
6931  character(len=*), optional, intent(in) :: csv_file_name
6932  logical, optional, intent(out) :: is_success
6933 
6934  ! Local copies of optionals.
6935  character(len=FILENAME_LENGTH) :: csv_file_name_here
6936  logical :: is_success_write
6937 
6938  !> ### Implementation notes ###
6939  !! First, check if the optional CSV file name is provided, if not,
6940  !! generate it automatically.
6941  if (present(csv_file_name)) then
6942  csv_file_name_here = csv_file_name
6943  else
6944  csv_file_name_here = "food_items_" // trim(this%food_label) // "_" // &
6945  model_name // "_" // mmdd // "_gen_" // &
6946  tostr(global_generation_number_current, &
6947  generations) // csv
6948  end if
6949 
6950  !> Then, data for the food resource is saved using the
6951  !! [CSV_MATRIX_WRITE()](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_matrix_write)
6952  !! procedure from [HEDTOOLS](http://ahamodel.uib.no/doc/).
6953  is_success_write = .false.
6954  call csv_matrix_write ( reshape( &
6955  [ this%food%x, &
6956  this%food%y, &
6957  this%food%depth, &
6958  this%food%size, &
6959  size2mass_food(this%food%size), &
6960  conv_l2r(this%food%eaten), &
6961  real(this%food%food_iid,SRP) ], &
6962  [this%number_food_items, 7]), &
6963  csv_file_name_here, &
6964  [ character(len=LABEL_LENGTH) :: &
6965  "X","Y", "DPTH", "SIZE", &
6966  "MASS", "EATEN", "IID"], &
6967  is_success_write )
6968 
6969  if (present(is_success)) is_success = is_success_write
6970 
6971  !> The CSV output data file can be optionally compressed with the
6972  !! commondata::cmd_zip_output command if commondata::is_zip_outputs is set
6973  !! to TRUE.
6974  if ( is_zip_outputs ) then
6975  call call_external(command=cmd_zip_output // " " // csv_file_name_here, &
6976  suppress_output=.true., &
6977  is_background_task=zip_outputs_background )
6978  end if
6979 
6980  end subroutine food_resource_save_foods_csv
6981 
6982  !-----------------------------------------------------------------------------
6983  !> Sort the food resource objects within the array by their sizes.
6984  !! The two subroutines below are a variant of the recursive quick-sort
6985  !! algorithm adapted for sorting real components of the the `FOOD_RESOURCE`
6986  !! object.
6987  !! @param[in] reindex is a logical flag enabling re-indexing the
6988  !! food resource after it is sorted. The default is **NOT**
6989  !! to reindex.
6990  elemental subroutine food_resource_sort_by_size(this, reindex)
6991  class(food_resource), intent(inout) :: this
6992  ! @param[in] reindex is a logical flag enabling re-indexing the
6993  ! food resource after it is sorted. The default is **NOT**
6994  ! to reindex.
6995  logical, optional, intent(in) :: reindex
6996 
6997  call qsort(this%food) ! This is the array component we sort, work on array.
6998 
6999  ! @note Note that re-indexing of food resource after sorting is NOT
7000  ! enabled by default.
7001  if (present(reindex)) then
7002  if (reindex) call this%reindex()
7003  end if
7004 
7005  contains
7006 
7007  !...........................................................................
7008  !> `qsort` and `qs_partition_` are the two parts of the recursive sort
7009  !! algorithm `qsort` is the recursive frontend. Sorts an array of food
7010  !! items by size within the resource object.
7011  !! @param `A` has the same type as the individual component objects
7012  !! of the array-object that we are going to sort.
7013  recursive pure subroutine qsort(A)
7014 
7015  ! @param `A` has the same type as the individual component objects
7016  ! of the array-object that we are going to sort.
7017  type(food_item), intent(in out), dimension(:) :: a
7018  integer :: iq
7019 
7020  if(size(a) > 1) then
7021  call qs_partition_size(a, iq)
7022  call qsort(a(:iq-1))
7023  call qsort(a(iq:))
7024  endif
7025 
7026  end subroutine qsort
7027 
7028  !...........................................................................
7029  !> `qsort` and `qs_partition_` are the two parts of the recursive sort
7030  !! algorithm `qs_partition_size` is a pivot backend, here it sorts
7031  !! food items within the food resource object by real size components.
7032  pure subroutine qs_partition_size(A, marker)
7033 
7034  type(food_item), intent(inout), dimension(:) :: a
7035  integer, intent(out) :: marker
7036  integer :: i, j
7037  type(food_item) :: temp
7038  ! @note Pivot point `x`, has the same type **as
7039  ! the sorted object component**.
7040  real(srp) :: x
7041 
7042  ! we sort `FOOD_ITEM` objects within the `FOOD_RESOURCE by their
7043  ! `size` components (hardwired).
7044  x = a(1)%size
7045  i= 0
7046  j= size(a) + 1
7047 
7048  do
7049  j = j-1
7050  do
7051  if (a(j)%size <= x) exit
7052  j = j-1
7053  end do
7054  i = i+1
7055  do
7056  if (a(i)%size >= x) exit
7057  i = i+1
7058  end do
7059  if (i < j) then
7060  ! exchange A(i) and A(j)
7061  temp = a(i)
7062  a(i) = a(j)
7063  a(j) = temp
7064  elseif (i == j) then
7065  marker = i+1
7066  return
7067  else
7068  marker = i
7069  return
7070  endif
7071  end do
7072 
7073  end subroutine qs_partition_size
7074 
7075  end subroutine food_resource_sort_by_size
7076 
7077  !-----------------------------------------------------------------------------
7078  !> Reset individual iid for the food resource. Individual iids must normally
7079  !! coincide with the array order index. If it is changed after sorting,
7080  !! iids no longer reflect the correct index. So this subroutine resets iids
7081  !! to be coinciding with each food item index.
7082  !! @param start_iid is an optional parameters that sets the starting value
7083  !! for reindexing. For example, indexing from 1000 rather than 1.
7084  !! @warning Always reindex food resource after food items have been sorted!
7085  pure subroutine food_resource_reset_iid_all(this, start_iid)
7086  class(food_resource), intent(inout) :: this
7087  ! @param start_iid is an optional parameters that sets the starting value
7088  ! for reindexing. For example, indexing from 1000 rather than 1.
7089  integer, optional, intent(in) :: start_iid
7090 
7091  ! Local copies of optionals
7092  integer :: start_iid_loc
7093 
7094  ! Local variables
7095  integer :: i
7096 
7097  if (present(start_iid)) then
7098  start_iid_loc = start_iid
7099  else
7100  start_iid_loc = 1
7101  end if
7102 
7103  do concurrent(i=1:this%number_food_items)
7104  this%food(i)%food_iid = i + start_iid_loc - 1
7105  ! call this%food(i)%set_iid( i + start_iid_loc - 1 )
7106  end do
7107 
7108  end subroutine food_resource_reset_iid_all
7109 
7110  !-----------------------------------------------------------------------------
7111  !> Reset and reindex iids for an input list of several food resources. As
7112  !! the result of this subroutine all food items across all the resources
7113  !! within the whole list will have unique iids.
7114  !! @param[inout] resource_1, resource_2, ... a list of food resources to
7115  !! reindex
7116  !! @note The calculation does not use an array of food resources because
7117  !! this can create problems in setting input dummy parameters in
7118  !! the array constructor. It just accepts raw resource objects and
7119  !! does all the operations directly on them. The number of food
7120  !! resources is probably never big, so the hard-coded limit of
7121  !! 20 components would probably never be exceeded. But the object
7122  !! list implementation is rather wordy, code-duplicating and prone
7123  !! to editing bugs. **The main aim** of this wordy, code-dubbing and
7124  !! mistype-prone approach was to allow easy passage of the whole
7125  !! original resource objects back from the collapsed object
7126  !! retaining all the changes that were introduced (e.g. the `eaten`
7127  !! status) while the resource objects were processed as the joined
7128  !! super-object.
7129  !! TODO: Perhaps it could be reimplemented in a better style using
7130  !! an extension object type.
7131  !! @warning Note that this is not a type-bound subroutine. It should not
7132  !! be declared in the `FOOD_RESOURCE` type.
7133  !> @note See notes on `food_resources_collapse()`,
7134  !! `food_resources_update_back()` and `reindex_food_resources()`.
7135  subroutine reindex_food_resources( resource_1, &
7136  resource_2, &
7137  resource_3, &
7138  resource_4, &
7139  resource_5, &
7140  resource_6, &
7141  resource_7, &
7142  resource_8, &
7143  resource_9, &
7144  resource_10, &
7145  resource_11, &
7146  resource_12, &
7147  resource_13, &
7148  resource_14, &
7149  resource_15, &
7150  resource_16, &
7151  resource_17, &
7152  resource_18, &
7153  resource_19, &
7154  resource_20 )
7155 
7156  class(food_resource), optional, intent(inout) :: resource_1, &
7157  resource_2, &
7158  resource_3, &
7159  resource_4, &
7160  resource_5, &
7161  resource_6, &
7162  resource_7, &
7163  resource_8, &
7164  resource_9, &
7165  resource_10, &
7166  resource_11, &
7167  resource_12, &
7168  resource_13, &
7169  resource_14, &
7170  resource_15, &
7171  resource_16, &
7172  resource_17, &
7173  resource_18, &
7174  resource_19, &
7175  resource_20
7176 
7177  integer, parameter :: n_resources = 4
7178 
7179  integer :: start_value_resource
7180 
7181  start_value_resource = 1
7182 
7183  r1: if (present(resource_1)) then
7184  ! Reindex the i-th resorce using the `start_value_resource`
7185  call resource_1%reindex(start_value_resource)
7186  ! Update the starting index for the **next** resource component array.
7187  start_value_resource = resource_1%number_food_items + 1
7188  end if r1
7189 
7190  r2: if (present(resource_2)) then
7191  call resource_2%reindex(start_value_resource)
7192  start_value_resource = resource_2%number_food_items + 1
7193  end if r2
7194 
7195  r3: if (present(resource_3)) then
7196  call resource_3%reindex(start_value_resource)
7197  start_value_resource = resource_3%number_food_items + 1
7198  end if r3
7199 
7200  r4: if (present(resource_4)) then
7201  call resource_4%reindex(start_value_resource)
7202  start_value_resource = resource_4%number_food_items + 1
7203  end if r4
7204 
7205  r5: if (present(resource_5)) then
7206  call resource_5%reindex(start_value_resource)
7207  start_value_resource = resource_5%number_food_items + 1
7208  end if r5
7209 
7210  r6: if (present(resource_6)) then
7211  call resource_6%reindex(start_value_resource)
7212  start_value_resource = resource_6%number_food_items + 1
7213  end if r6
7214 
7215  r7: if (present(resource_7)) then
7216  call resource_7%reindex(start_value_resource)
7217  start_value_resource = resource_7%number_food_items + 1
7218  end if r7
7219 
7220  r8: if (present(resource_8)) then
7221  call resource_8%reindex(start_value_resource)
7222  start_value_resource = resource_8%number_food_items + 1
7223  end if r8
7224 
7225  r9: if (present(resource_9)) then
7226  call resource_9%reindex(start_value_resource)
7227  start_value_resource = resource_9%number_food_items + 1
7228  end if r9
7229 
7230  r10: if (present(resource_10)) then
7231  call resource_10%reindex(start_value_resource)
7232  start_value_resource = resource_10%number_food_items + 1
7233  end if r10
7234 
7235  r11: if (present(resource_11)) then
7236  call resource_11%reindex(start_value_resource)
7237  start_value_resource = resource_11%number_food_items + 1
7238  end if r11
7239 
7240  r12: if (present(resource_12)) then
7241  call resource_12%reindex(start_value_resource)
7242  start_value_resource = resource_12%number_food_items + 1
7243  end if r12
7244 
7245  r13: if (present(resource_13)) then
7246  call resource_13%reindex(start_value_resource)
7247  start_value_resource = resource_13%number_food_items + 1
7248  end if r13
7249 
7250  r14: if (present(resource_14)) then
7251  call resource_14%reindex(start_value_resource)
7252  start_value_resource = resource_14%number_food_items + 1
7253  end if r14
7254 
7255  r15: if (present(resource_15)) then
7256  call resource_15%reindex(start_value_resource)
7257  start_value_resource = resource_15%number_food_items + 1
7258  end if r15
7259 
7260  r16: if (present(resource_16)) then
7261  call resource_16%reindex(start_value_resource)
7262  start_value_resource = resource_16%number_food_items + 1
7263  end if r16
7264 
7265  r17: if (present(resource_17)) then
7266  call resource_17%reindex(start_value_resource)
7267  start_value_resource = resource_17%number_food_items + 1
7268  end if r17
7269 
7270  r18: if (present(resource_18)) then
7271  call resource_18%reindex(start_value_resource)
7272  start_value_resource = resource_18%number_food_items + 1
7273  end if r18
7274 
7275  r19: if (present(resource_19)) then
7276  call resource_19%reindex(start_value_resource)
7277  start_value_resource = resource_19%number_food_items + 1
7278  end if r19
7279 
7280  r20: if (present(resource_20)) then
7281  call resource_20%reindex(start_value_resource)
7282  start_value_resource = resource_20%number_food_items + 1
7283  end if r20
7284 
7285  end subroutine reindex_food_resources
7286 
7287  !-----------------------------------------------------------------------------
7288  !> Collapse several food resources into one. The collapsed resource can then
7289  !! go into the perception system. The properties of the component resources
7290  !! are retained in the collapsed resource.
7291  !! @param[out] food_resource_collapsed output resource object that is
7292  !! formed by joining the list of component resource objects.
7293  !! @param[in] resource_1, resource_2, ... a list of component resource
7294  !! objects.
7295  !! @param[in] reindex logical flag to reindex the joined resource (TRUE)
7296  !! upon joining. The default is **no** reindexing.
7297  !! @param[in] label Label for the joined food resource, if absent set to
7298  !! 'tmp_object'.
7299  !>
7300  !> ### Implementation notes ###
7301  !! The calculations in this procedure does not use an array of food
7302  !! resources because this can create problems in setting input dummy
7303  !! parameters in the array constructor. It just accepts raw resource
7304  !! objects and does all the operations directly on them. The number of
7305  !! food resources is probably never big, so the hard-coded limit of
7306  !! 20 components would probably never be exceeded. But the object
7307  !! list implementation is rather wordy, code-duplicating and prone
7308  !! to editing bugs. **The main aim** of this wordy, code-dubbing and
7309  !! mistype-prone approach was to allow easy passage of the whole
7310  !! original resource objects back from the collapsed object
7311  !! retaining all the changes that were introduced (e.g. the `eaten`
7312  !! status) while the resource objects were processed as the joined
7313  !! super-object. TODO: Perhaps it could be reimplemented in a better
7314  !! style using an extension object type.
7315  !> The joined food resource object retains the original component
7316  !! ID's. If common unique iids are needed, `reindex` method should
7317  !! be called upon joining! This is performed now using the optional
7318  !! 'reindex' parameter.
7319  !! If the food items are re-sorted within the joined food resource,
7320  !! their iid's no longer correspond to the original id's. Therefore,
7321  !! unjoining will result in totally new order and id's and the
7322  !! stacking of the original component resources is **broken**:
7323  !! correct unjoining is then impossible.
7324  !> ### Usage example ###
7325  !! This example shows how to use the `join` and `unjoin` methods
7326  !! to collapse and split back food resources.
7327  !! @code
7328  !! ! Join two resources into `joined_food_res_tmp`
7329  !! call joined_food_res_tmp%join( habitat_safe%food, &
7330  !! habitat_dangerous%food, &
7331  !! reindex=.TRUE. )
7332  !! ! We can then work with the collapsed resource, e.g. get
7333  !! ! the perception and use the "eat" method.
7334  !! call proto_parents%individual(ind)%see_food(joined_food_res_tmp)
7335  !! call proto_parents%individual(ind)%do_eat_food_item( &
7336  !! food_item_selected, joined_food_res_tmp)
7337  !! ! after this we use the "unjoin" method to get back the
7338  !! ! original component food resources, they are now in an updated
7339  !! ! state, e.g. the "eaten" flag is transferred from the collapsed
7340  !! ! food resource object back to the component objects.
7341  !! call joined_food_res_tmp%unjoin( habitat_safe%food, &
7342  !! habitat_dangerous%food, &
7343  !! reindex=.TRUE. )
7344  !! ! destroy the temporary collapsed resource object.
7345  !! call joined_food_res_tmp%destroy()
7346  !! @endcode
7347  !> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7348  !> @note See notes on `food_resources_collapse()`,
7349  !! `food_resources_update_back()` and `reindex_food_resources()`.
7350  subroutine food_resources_collapse ( food_resource_collapsed, &
7351  resource_1, &
7352  resource_2, &
7353  resource_3, &
7354  resource_4, &
7355  resource_5, &
7356  resource_6, &
7357  resource_7, &
7358  resource_8, &
7359  resource_9, &
7360  resource_10, &
7361  resource_11, &
7362  resource_12, &
7363  resource_13, &
7364  resource_14, &
7365  resource_15, &
7366  resource_16, &
7367  resource_17, &
7368  resource_18, &
7369  resource_19, &
7370  resource_20, &
7371  reindex, &
7372  label )
7373 
7374  ! @note Note that `food_resource_collapsed` is actually the `this` object
7375  ! in the type-bound procedure. It has been initially defined in
7376  ! a previous non-type-bound version as **type** to enable strict
7377  ! typing. But this would not work with type-bound, so had to
7378  ! declare as a polymorphic `class`. However, it should normally
7379  ! only accept the `FOOD_RESOURCE` type objects.
7380  ! TODO: check if food_resource_collapsed should be safer with intent inout.
7381  class(food_resource), intent(out) :: food_resource_collapsed
7382 
7383  class(food_resource), optional, intent(in) :: resource_1, &
7384  resource_2, &
7385  resource_3, &
7386  resource_4, &
7387  resource_5, &
7388  resource_6, &
7389  resource_7, &
7390  resource_8, &
7391  resource_9, &
7392  resource_10, &
7393  resource_11, &
7394  resource_12, &
7395  resource_13, &
7396  resource_14, &
7397  resource_15, &
7398  resource_16, &
7399  resource_17, &
7400  resource_18, &
7401  resource_19, &
7402  resource_20
7403 
7404  ! @param[in] reindex logical flag to reindex the joined resource (TRUE)
7405  ! upon joining. The default is **no** reindexing.
7406  logical, optional, intent(in) :: reindex
7407  ! @param[in] label Label for the joined food resource, if absent set to
7408  ! 'tmp_object'.
7409  character(len=*), optional, intent(in) :: label
7410 
7411  ! Local total abundance of the collapsed food resource object.
7412  integer :: abundance_total
7413 
7414  ! Local object parts/components for each of the component resources.
7415  type(spatial), dimension(:), allocatable :: &
7416  locations_res_1, locations_res_2, locations_res_3, &
7417  locations_res_4, locations_res_5, locations_res_6, &
7418  locations_res_7, locations_res_8, locations_res_9, &
7419  locations_res_10, locations_res_11, locations_res_12, &
7420  locations_res_13, locations_res_14, locations_res_15, &
7421  locations_res_16, locations_res_17, locations_res_18, &
7422  locations_res_19, locations_res_20
7423 
7424  real(SRP), dimension(:), allocatable :: &
7425  sizes_res_1, sizes_res_2, sizes_res_3, sizes_res_4, &
7426  sizes_res_5, sizes_res_6, sizes_res_7, sizes_res_8, &
7427  sizes_res_9, sizes_res_10, sizes_res_11, sizes_res_12, &
7428  sizes_res_13, sizes_res_14, sizes_res_15, sizes_res_16, &
7429  sizes_res_17, sizes_res_18, sizes_res_19, sizes_res_20
7430 
7431  logical, dimension(:), allocatable :: &
7432  eaten_res_1, eaten_res_2, eaten_res_3, eaten_res_4, &
7433  eaten_res_5, eaten_res_6, eaten_res_7, eaten_res_8, &
7434  eaten_res_9, eaten_res_10, eaten_res_11, eaten_res_12, &
7435  eaten_res_13, eaten_res_14, eaten_res_15, eaten_res_16, &
7436  eaten_res_17, eaten_res_18, eaten_res_19, eaten_res_20
7437 
7438  integer, dimension(:), allocatable :: &
7439  old_iid_res_1, old_iid_res_2, old_iid_res_3, old_iid_res_4, &
7440  old_iid_res_5, old_iid_res_6, old_iid_res_7, old_iid_res_8, &
7441  old_iid_res_9, old_iid_res_10, old_iid_res_11, old_iid_res_12, &
7442  old_iid_res_13, old_iid_res_14, old_iid_res_15, old_iid_res_16, &
7443  old_iid_res_17, old_iid_res_18, old_iid_res_19, old_iid_res_20
7444 
7445  ! Local copy of optional label
7446  character(len=LABEL_LENGTH) :: label_loc
7447 
7448  abundance_total = 0
7449 
7450  if (present(label)) then
7451  label_loc = label
7452  else
7453  label_loc = "tmp_object"
7454  end if
7455 
7456  !> ### Implementation details ###
7457  !> For each food resource that is provided in the list we take the
7458  !! temporary transfer arrays for parameter copying from the component
7459  !! food resource into the `food_resource_collapsed`.
7460  ! @warning The `associate` construct to hide `resource_XXX` and components
7461  ! would **not** work with allocatable, had to work without assign
7462  ! shortcuts.
7463  if (present(resource_1)) then
7464  abundance_total = abundance_total + resource_1%number_food_items
7465  allocate(locations_res_1(resource_1%number_food_items))
7466  locations_res_1 = resource_1%location()
7467  allocate(sizes_res_1(resource_1%number_food_items))
7468  sizes_res_1 = resource_1%food%size
7469  allocate(eaten_res_1(resource_1%number_food_items))
7470  eaten_res_1 = resource_1%food%eaten
7471  allocate(old_iid_res_1(resource_1%number_food_items))
7472  old_iid_res_1 = resource_1%food%food_iid
7473  else
7474  allocate(locations_res_1(0))
7475  allocate(sizes_res_1(0))
7476  allocate(eaten_res_1(0))
7477  allocate(old_iid_res_1(0))
7478  end if
7479 
7480  if (present(resource_2)) then
7481  abundance_total = abundance_total + resource_2%number_food_items
7482  allocate(locations_res_2(resource_2%number_food_items))
7483  locations_res_2 = resource_2%location()
7484  allocate(sizes_res_2(resource_2%number_food_items))
7485  sizes_res_2 = resource_2%food%size
7486  allocate(eaten_res_2(resource_2%number_food_items))
7487  eaten_res_2 = resource_2%food%eaten
7488  allocate(old_iid_res_2(resource_2%number_food_items))
7489  old_iid_res_2 = resource_2%food%food_iid
7490  else
7491  allocate(locations_res_2(0))
7492  allocate(sizes_res_2(0))
7493  allocate(eaten_res_2(0))
7494  allocate(old_iid_res_2(0))
7495  end if
7496 
7497  if (present(resource_3)) then
7498  abundance_total = abundance_total + resource_3%number_food_items
7499  allocate(locations_res_3(resource_3%number_food_items))
7500  locations_res_3 = resource_3%location()
7501  allocate(sizes_res_3(resource_3%number_food_items))
7502  sizes_res_3 = resource_3%food%size
7503  allocate(eaten_res_3(resource_3%number_food_items))
7504  eaten_res_3 = resource_3%food%eaten
7505  allocate(old_iid_res_3(resource_3%number_food_items))
7506  old_iid_res_3 = resource_3%food%food_iid
7507  else
7508  allocate(locations_res_3(0))
7509  allocate(sizes_res_3(0))
7510  allocate(eaten_res_3(0))
7511  allocate(old_iid_res_3(0))
7512  end if
7513 
7514  if (present(resource_4)) then
7515  abundance_total = abundance_total + resource_4%number_food_items
7516  allocate(locations_res_4(resource_4%number_food_items))
7517  locations_res_4 = resource_4%location()
7518  allocate(sizes_res_4(resource_4%number_food_items))
7519  sizes_res_4 = resource_4%food%size
7520  allocate(eaten_res_4(resource_4%number_food_items))
7521  eaten_res_4 = resource_4%food%eaten
7522  allocate(old_iid_res_4(resource_4%number_food_items))
7523  old_iid_res_4 = resource_4%food%food_iid
7524  else
7525  allocate(locations_res_4(0))
7526  allocate(sizes_res_4(0))
7527  allocate(eaten_res_4(0))
7528  allocate(old_iid_res_4(0))
7529  end if
7530 
7531  if (present(resource_5)) then
7532  abundance_total = abundance_total + resource_5%number_food_items
7533  allocate(locations_res_5(resource_5%number_food_items))
7534  locations_res_5 = resource_5%location()
7535  allocate(sizes_res_5(resource_5%number_food_items))
7536  sizes_res_5 = resource_5%food%size
7537  allocate(eaten_res_5(resource_5%number_food_items))
7538  eaten_res_5 = resource_5%food%eaten
7539  allocate(old_iid_res_5(resource_5%number_food_items))
7540  old_iid_res_5 = resource_5%food%food_iid
7541  else
7542  allocate(locations_res_5(0))
7543  allocate(sizes_res_5(0))
7544  allocate(eaten_res_5(0))
7545  allocate(old_iid_res_5(0))
7546  end if
7547 
7548  if (present(resource_6)) then
7549  abundance_total = abundance_total + resource_6%number_food_items
7550  allocate(locations_res_6(resource_6%number_food_items))
7551  locations_res_6 = resource_6%location()
7552  allocate(sizes_res_6(resource_6%number_food_items))
7553  sizes_res_6 = resource_6%food%size
7554  allocate(eaten_res_6(resource_6%number_food_items))
7555  eaten_res_6 = resource_6%food%eaten
7556  allocate(old_iid_res_6(resource_6%number_food_items))
7557  old_iid_res_6 = resource_6%food%food_iid
7558  else
7559  allocate(locations_res_6(0))
7560  allocate(sizes_res_6(0))
7561  allocate(eaten_res_6(0))
7562  allocate(old_iid_res_6(0))
7563  end if
7564 
7565  if (present(resource_7)) then
7566  abundance_total = abundance_total + resource_7%number_food_items
7567  allocate(locations_res_7(resource_7%number_food_items))
7568  locations_res_7 = resource_7%location()
7569  allocate(sizes_res_7(resource_7%number_food_items))
7570  sizes_res_7 = resource_7%food%size
7571  allocate(eaten_res_7(resource_7%number_food_items))
7572  eaten_res_7 = resource_7%food%eaten
7573  allocate(old_iid_res_7(resource_7%number_food_items))
7574  old_iid_res_7 = resource_7%food%food_iid
7575  else
7576  allocate(locations_res_7(0))
7577  allocate(sizes_res_7(0))
7578  allocate(eaten_res_7(0))
7579  allocate(old_iid_res_7(0))
7580  end if
7581 
7582  if (present(resource_8)) then
7583  abundance_total = abundance_total + resource_8%number_food_items
7584  allocate(locations_res_8(resource_8%number_food_items))
7585  locations_res_8 = resource_8%location()
7586  allocate(sizes_res_8(resource_8%number_food_items))
7587  sizes_res_8 = resource_8%food%size
7588  allocate(eaten_res_8(resource_8%number_food_items))
7589  eaten_res_8 = resource_8%food%eaten
7590  allocate(old_iid_res_8(resource_8%number_food_items))
7591  old_iid_res_8 = resource_8%food%food_iid
7592  else
7593  allocate(locations_res_8(0))
7594  allocate(sizes_res_8(0))
7595  allocate(eaten_res_8(0))
7596  allocate(old_iid_res_8(0))
7597  end if
7598 
7599  if (present(resource_9)) then
7600  abundance_total = abundance_total + resource_9%number_food_items
7601  allocate(locations_res_9(resource_9%number_food_items))
7602  locations_res_9 = resource_9%location()
7603  allocate(sizes_res_9(resource_9%number_food_items))
7604  sizes_res_9 = resource_9%food%size
7605  allocate(eaten_res_9(resource_9%number_food_items))
7606  eaten_res_9 = resource_9%food%eaten
7607  allocate(old_iid_res_9(resource_9%number_food_items))
7608  old_iid_res_9 = resource_9%food%food_iid
7609  else
7610  allocate(locations_res_9(0))
7611  allocate(sizes_res_9(0))
7612  allocate(eaten_res_9(0))
7613  allocate(old_iid_res_9(0))
7614  end if
7615 
7616  if (present(resource_10)) then
7617  abundance_total = abundance_total + resource_10%number_food_items
7618  allocate(locations_res_10(resource_10%number_food_items))
7619  locations_res_10 = resource_10%location()
7620  allocate(sizes_res_10(resource_10%number_food_items))
7621  sizes_res_10 = resource_10%food%size
7622  allocate(eaten_res_10(resource_10%number_food_items))
7623  eaten_res_10 = resource_10%food%eaten
7624  allocate(old_iid_res_10(resource_10%number_food_items))
7625  old_iid_res_10 = resource_10%food%food_iid
7626  else
7627  allocate(locations_res_10(0))
7628  allocate(sizes_res_10(0))
7629  allocate(eaten_res_10(0))
7630  allocate(old_iid_res_10(0))
7631  end if
7632 
7633  if (present(resource_11)) then
7634  abundance_total = abundance_total + resource_11%number_food_items
7635  allocate(locations_res_11(resource_11%number_food_items))
7636  locations_res_11 = resource_11%location()
7637  allocate(sizes_res_11(resource_11%number_food_items))
7638  sizes_res_11 = resource_11%food%size
7639  allocate(eaten_res_11(resource_11%number_food_items))
7640  eaten_res_11 = resource_11%food%eaten
7641  allocate(old_iid_res_11(resource_11%number_food_items))
7642  old_iid_res_11 = resource_11%food%food_iid
7643  else
7644  allocate(locations_res_11(0))
7645  allocate(sizes_res_11(0))
7646  allocate(eaten_res_11(0))
7647  allocate(old_iid_res_11(0))
7648  end if
7649 
7650  if (present(resource_12)) then
7651  abundance_total = abundance_total + resource_12%number_food_items
7652  allocate(locations_res_12(resource_12%number_food_items))
7653  locations_res_12 = resource_12%location()
7654  allocate(sizes_res_12(resource_12%number_food_items))
7655  sizes_res_12 = resource_12%food%size
7656  allocate(eaten_res_12(resource_12%number_food_items))
7657  eaten_res_12 = resource_12%food%eaten
7658  allocate(old_iid_res_12(resource_12%number_food_items))
7659  old_iid_res_12 = resource_12%food%food_iid
7660  else
7661  allocate(locations_res_12(0))
7662  allocate(sizes_res_12(0))
7663  allocate(eaten_res_12(0))
7664  allocate(old_iid_res_12(0))
7665  end if
7666 
7667  if (present(resource_13)) then
7668  abundance_total = abundance_total + resource_13%number_food_items
7669  allocate(locations_res_13(resource_13%number_food_items))
7670  locations_res_13 = resource_13%location()
7671  allocate(sizes_res_13(resource_13%number_food_items))
7672  sizes_res_13 = resource_13%food%size
7673  allocate(eaten_res_13(resource_13%number_food_items))
7674  eaten_res_13 = resource_13%food%eaten
7675  allocate(old_iid_res_13(resource_13%number_food_items))
7676  old_iid_res_13 = resource_13%food%food_iid
7677  else
7678  allocate(locations_res_13(0))
7679  allocate(sizes_res_13(0))
7680  allocate(eaten_res_13(0))
7681  allocate(old_iid_res_13(0))
7682  end if
7683 
7684  if (present(resource_14)) then
7685  abundance_total = abundance_total + resource_14%number_food_items
7686  allocate(locations_res_14(resource_14%number_food_items))
7687  locations_res_14 = resource_14%location()
7688  allocate(sizes_res_14(resource_14%number_food_items))
7689  sizes_res_14 = resource_14%food%size
7690  allocate(eaten_res_14(resource_14%number_food_items))
7691  eaten_res_14 = resource_14%food%eaten
7692  allocate(old_iid_res_14(resource_14%number_food_items))
7693  old_iid_res_14 = resource_14%food%food_iid
7694  else
7695  allocate(locations_res_14(0))
7696  allocate(sizes_res_14(0))
7697  allocate(eaten_res_14(0))
7698  allocate(old_iid_res_14(0))
7699  end if
7700 
7701  if (present(resource_15)) then
7702  abundance_total = abundance_total + resource_15%number_food_items
7703  allocate(locations_res_15(resource_15%number_food_items))
7704  locations_res_15 = resource_15%location()
7705  allocate(sizes_res_15(resource_15%number_food_items))
7706  sizes_res_15 = resource_15%food%size
7707  allocate(eaten_res_15(resource_15%number_food_items))
7708  eaten_res_15 = resource_15%food%eaten
7709  allocate(old_iid_res_15(resource_15%number_food_items))
7710  old_iid_res_15 = resource_15%food%food_iid
7711  else
7712  allocate(locations_res_15(0))
7713  allocate(sizes_res_15(0))
7714  allocate(eaten_res_15(0))
7715  allocate(old_iid_res_15(0))
7716  end if
7717 
7718  if (present(resource_16)) then
7719  abundance_total = abundance_total + resource_16%number_food_items
7720  allocate(locations_res_16(resource_16%number_food_items))
7721  locations_res_16 = resource_16%location()
7722  allocate(sizes_res_16(resource_16%number_food_items))
7723  sizes_res_16 = resource_16%food%size
7724  allocate(eaten_res_16(resource_16%number_food_items))
7725  eaten_res_16 = resource_16%food%eaten
7726  allocate(old_iid_res_16(resource_16%number_food_items))
7727  old_iid_res_16 = resource_16%food%food_iid
7728  else
7729  allocate(locations_res_16(0))
7730  allocate(sizes_res_16(0))
7731  allocate(eaten_res_16(0))
7732  allocate(old_iid_res_16(0))
7733  end if
7734 
7735  if (present(resource_17)) then
7736  abundance_total = abundance_total + resource_17%number_food_items
7737  allocate(locations_res_17(resource_17%number_food_items))
7738  locations_res_17 = resource_17%location()
7739  allocate(sizes_res_17(resource_17%number_food_items))
7740  sizes_res_17 = resource_17%food%size
7741  allocate(eaten_res_17(resource_17%number_food_items))
7742  eaten_res_17 = resource_17%food%eaten
7743  allocate(old_iid_res_17(resource_17%number_food_items))
7744  old_iid_res_17 = resource_17%food%food_iid
7745  else
7746  allocate(locations_res_17(0))
7747  allocate(sizes_res_17(0))
7748  allocate(eaten_res_17(0))
7749  allocate(old_iid_res_17(0))
7750  end if
7751 
7752  if (present(resource_18)) then
7753  abundance_total = abundance_total + resource_18%number_food_items
7754  allocate(locations_res_18(resource_18%number_food_items))
7755  locations_res_18 = resource_18%location()
7756  allocate(sizes_res_18(resource_18%number_food_items))
7757  sizes_res_18 = resource_18%food%size
7758  allocate(eaten_res_18(resource_18%number_food_items))
7759  eaten_res_18 = resource_18%food%eaten
7760  allocate(old_iid_res_18(resource_18%number_food_items))
7761  old_iid_res_18 = resource_18%food%food_iid
7762  else
7763  allocate(locations_res_18(0))
7764  allocate(sizes_res_18(0))
7765  allocate(eaten_res_18(0))
7766  allocate(old_iid_res_18(0))
7767  end if
7768 
7769  if (present(resource_19)) then
7770  abundance_total = abundance_total + resource_19%number_food_items
7771  allocate(locations_res_19(resource_19%number_food_items))
7772  locations_res_19 = resource_19%location()
7773  allocate(sizes_res_19(resource_19%number_food_items))
7774  sizes_res_19 = resource_19%food%size
7775  allocate(eaten_res_19(resource_19%number_food_items))
7776  eaten_res_19 = resource_19%food%eaten
7777  allocate(old_iid_res_19(resource_19%number_food_items))
7778  old_iid_res_19 = resource_19%food%food_iid
7779  else
7780  allocate(locations_res_19(0))
7781  allocate(sizes_res_19(0))
7782  allocate(eaten_res_19(0))
7783  allocate(old_iid_res_19(0))
7784  end if
7785 
7786  if (present(resource_20)) then
7787  abundance_total = abundance_total + resource_20%number_food_items
7788  allocate(locations_res_20(resource_20%number_food_items))
7789  locations_res_20 = resource_20%location()
7790  allocate(sizes_res_20(resource_20%number_food_items))
7791  sizes_res_20 = resource_20%food%size
7792  allocate(eaten_res_20(resource_20%number_food_items))
7793  eaten_res_20 = resource_20%food%eaten
7794  allocate(old_iid_res_20(resource_20%number_food_items))
7795  old_iid_res_20 = resource_20%food%food_iid
7796  else
7797  allocate(locations_res_20(0))
7798  allocate(sizes_res_20(0))
7799  allocate(eaten_res_20(0))
7800  allocate(old_iid_res_20(0))
7801  end if
7802 
7803  !> Make this food resource from the component objects that are provided.
7804  ! @note Normally, `make` method allocates objects automatically, so no
7805  ! no need to call `allocate`on `food_resource_collapsed%food`.
7806  call food_resource_collapsed%make( &
7807  label=label_loc, &
7808  abundance=abundance_total, &
7809  locations=[ locations_res_1, &
7810  locations_res_2, &
7811  locations_res_3, &
7812  locations_res_4, &
7813  locations_res_5, &
7814  locations_res_6, &
7815  locations_res_7, &
7816  locations_res_8, &
7817  locations_res_9, &
7818  locations_res_10, &
7819  locations_res_11, &
7820  locations_res_12, &
7821  locations_res_13, &
7822  locations_res_14, &
7823  locations_res_15, &
7824  locations_res_16, &
7825  locations_res_17, &
7826  locations_res_18, &
7827  locations_res_19, &
7828  locations_res_20 ], &
7829  sizes=[ sizes_res_1, &
7830  sizes_res_2, &
7831  sizes_res_3, &
7832  sizes_res_4, &
7833  sizes_res_5, &
7834  sizes_res_6, &
7835  sizes_res_7, &
7836  sizes_res_8, &
7837  sizes_res_9, &
7838  sizes_res_10, &
7839  sizes_res_11, &
7840  sizes_res_12, &
7841  sizes_res_13, &
7842  sizes_res_14, &
7843  sizes_res_15, &
7844  sizes_res_16, &
7845  sizes_res_17, &
7846  sizes_res_18, &
7847  sizes_res_19, &
7848  sizes_res_20 ] )
7849 
7850  !> And add extra object component arrays that do not get modified by `make`:
7851  !! the **eaten** status and the id number (**iid**).
7852  food_resource_collapsed%food%eaten = [ eaten_res_1, &
7853  eaten_res_2, &
7854  eaten_res_3, &
7855  eaten_res_4, &
7856  eaten_res_5, &
7857  eaten_res_6, &
7858  eaten_res_7, &
7859  eaten_res_8, &
7860  eaten_res_9, &
7861  eaten_res_10, &
7862  eaten_res_11, &
7863  eaten_res_12, &
7864  eaten_res_13, &
7865  eaten_res_14, &
7866  eaten_res_15, &
7867  eaten_res_16, &
7868  eaten_res_17, &
7869  eaten_res_18, &
7870  eaten_res_19, &
7871  eaten_res_20 ]
7872 
7873  food_resource_collapsed%food%food_iid = [ old_iid_res_1, &
7874  old_iid_res_2, &
7875  old_iid_res_3, &
7876  old_iid_res_4, &
7877  old_iid_res_5, &
7878  old_iid_res_6, &
7879  old_iid_res_7, &
7880  old_iid_res_8, &
7881  old_iid_res_9, &
7882  old_iid_res_10, &
7883  old_iid_res_11, &
7884  old_iid_res_12, &
7885  old_iid_res_13, &
7886  old_iid_res_14, &
7887  old_iid_res_15, &
7888  old_iid_res_16, &
7889  old_iid_res_17, &
7890  old_iid_res_18, &
7891  old_iid_res_19, &
7892  old_iid_res_20 ]
7893 
7894  !> If the `reindex` flag is present and is TRUE, do reindexing the new
7895  !! joined food resource.
7896  if (present(reindex)) then
7897  if (reindex) call food_resource_collapsed%reindex()
7898  end if
7899 
7900  end subroutine food_resources_collapse
7901 
7902  !-----------------------------------------------------------------------------
7903  !> Join food resources into a single global food resource out of the global
7904  !! array the_environment::global_habitats_available.
7905  !! See the_environment::unjoin() for how to unjoin an array of food resources
7906  !! back into an array.
7907  !! The joined resource can then go into the perception system. The
7908  !! properties of the component resources are retained in the collapsed
7909  !! resource.
7910  !! @note This procedure is intended to use a short interface name `join`,
7911  !! see the_environment::join().
7912  !! @note A similar procedure using a **list** of input component resources
7913  !! is implemented in the_environment::food_resources_collapse().
7914  function food_resources_collapse_global_object (reindex, label) &
7915  result(food_resource_collapsed)
7916  !> @param[in] reindex logical flag to reindex the joined resource (TRUE)
7917  !! upon joining. The default is **no** reindexing.
7918  logical, optional, intent(in) :: reindex
7919  !> @param[in] label Label for the joined food resource, if absent set to
7920  !! 'tmp_object'.
7921  character(len=*), optional, intent(in) :: label
7922  !> @return A collapsed food resource joining the input array.
7923  type(food_resource) :: food_resource_collapsed
7924 
7925  ! Local total number of food items in the output collapsed food resource.
7926  integer :: abundance_total
7927 
7928  ! Local copy of optional label
7929  character(len=LABEL_LENGTH) :: label_loc
7930 
7931  ! Local counters
7932  integer :: i, j, k, size_arr_step
7933 
7934  !> ### Implementation details ###
7935  !! This procedure builds the output `food_resource_collapsed` resource
7936  !! object from scratch fully substituting the normal `make`
7937  !! the_environment::food_resource::make() method. Check out the main method
7938  !! the_environment::food_resource::make() in case of reimplementation.
7939  !!
7940  !> - Determine the total number of food items in the collapsed food
7941  !! resource, it equals to the sum of the items across all components
7942  !! of the the_environment::global_habitats_available array.
7943  abundance_total = sum(global_habitats_available%food%number_food_items)
7944 
7945  !> - Make a label for the collapsed object, if not present as a dummy
7946  !! parameter, set to 'tmp_object'.
7947  if (present(label)) then
7948  label_loc = label
7949  else
7950  label_loc = "tmp_object"
7951  end if
7952  food_resource_collapsed%food_label = label_loc
7953 
7954  !> - Allocate the food items array for the collapsed food resource.
7955  if (.not. allocated( food_resource_collapsed%food )) &
7956  allocate( food_resource_collapsed%food(abundance_total) )
7957 
7958  !> - Set the abundance for the new output object equal to the sum across
7959  !! all the components.
7960  food_resource_collapsed%number_food_items = abundance_total
7961 
7962  !> - Create all the food items in the new object using the
7963  !! the_environment::food_item::create() method.
7964  call food_resource_collapsed%food%create()
7965 
7966  !> - Finally, copy individual food items from the component resources
7967  !! into the new joined resource looping over all resources and appending
7968  !! arrays.
7969  ! @note Implementation note: GNU gfortran accepts copying the array
7970  ! by slices in previous version. But Intel ifort resulted in
7971  ! segmentation faults. Therefore, instead of working with the
7972  ! array slices, an inner loop over `j` is implemented: it doesn't
7973  ! break ifort now.
7974  size_arr_step = 0
7975  copy_foods: do i=1, size(global_habitats_available)
7976  k=0
7977  do j = size_arr_step + 1, size_arr_step + &
7978  global_habitats_available(i)%food%number_food_items
7979  k = k + 1
7980  food_resource_collapsed%food(j) = &
7981  global_habitats_available(i)%food%food(k)
7982  end do
7983  size_arr_step = size_arr_step + &
7984  global_habitats_available(i)%food%number_food_items
7985  end do copy_foods
7986 
7987  !> - If the `reindex` flag is present and is TRUE, do reindexing the new
7988  !! joined food resource.
7989  !! .
7990  if (present(reindex)) then
7991  if (reindex) call food_resource_collapsed%reindex()
7992  end if
7993 
7995 
7996  !-----------------------------------------------------------------------------
7997  !> Transfer back the resulting food resources into their original objects
7998  !! out from a collapsed object from `food_resources_collapse`.
7999  !> @param[inout] resource_1, resource_2, ... a list of food resources to
8000  !! restore from the joined state.
8001  !> @param[in] reindex logical flag to reindex the joined resource (TRUE)
8002  !! upon joining. Default is **no** reindexing.
8003  !! @note The calculation does not use an array of food resources because
8004  !! this can create problems in setting input dummy parameters in
8005  !! the array constructor. It just accepts raw resource objects and
8006  !! does all the operations directly on them. The number of food
8007  !! resources is probably never big, so the hard-coded limit of
8008  !! 20 components would probably never be exceeded. But the object
8009  !! list implementation is rather wordy, code-duplicating and prone
8010  !! to editing bugs. **The main aim** of this wordy, code-dubbing and
8011  !! mistype-prone approach was to allow easy passage of the whole
8012  !! original resource objects back from the collapsed object
8013  !! retaining all the changes that were introduced (e.g. the `eaten`
8014  !! status) while the resource objects were processed as the joined
8015  !! super-object. TODO: Perhaps it could be reimplemented in a better
8016  !! style using an extension object type.
8017  !! @warning The un-joined food resource objects retain the joined object
8018  !! ID's. If individual id indexing are required, `reindex` method
8019  !! should be called for each of the unjoined food resource object
8020  !! upon joining!
8021  !> @note See notes on `food_resources_collapse()`,
8022  !! `food_resources_update_back()` and `reindex_food_resources()`.
8023  subroutine food_resources_update_back( food_resource_collapsed, &
8024  resource_1, &
8025  resource_2, &
8026  resource_3, &
8027  resource_4, &
8028  resource_5, &
8029  resource_6, &
8030  resource_7, &
8031  resource_8, &
8032  resource_9, &
8033  resource_10, &
8034  resource_11, &
8035  resource_12, &
8036  resource_13, &
8037  resource_14, &
8038  resource_15, &
8039  resource_16, &
8040  resource_17, &
8041  resource_18, &
8042  resource_19, &
8043  resource_20, &
8044  reindex )
8045 
8046  ! @note Note that `food_resource_collapsed` is actually the `this` object
8047  ! in the type-bound procedure. It has been initially defined in
8048  ! a previous non-type-bound version as **type** to enable strict
8049  ! typing. But this would not work with type-bound, so had to
8050  ! declare as a polymorphic `class`. However, it should normally
8051  ! only accept the `FOOD_RESOURCE` type objects.
8052  class(food_resource), intent(in) :: food_resource_collapsed
8053  ! @param[inout] resource_1, resource_2, ... a list of food resources to
8054  ! restore from the joined state.
8055  class(food_resource), optional, intent(inout) :: resource_1, &
8056  resource_2, &
8057  resource_3, &
8058  resource_4, &
8059  resource_5, &
8060  resource_6, &
8061  resource_7, &
8062  resource_8, &
8063  resource_9, &
8064  resource_10, &
8065  resource_11, &
8066  resource_12, &
8067  resource_13, &
8068  resource_14, &
8069  resource_15, &
8070  resource_16, &
8071  resource_17, &
8072  resource_18, &
8073  resource_19, &
8074  resource_20
8075 
8076  ! @param[in] reindex logical flag to reindex the joined resource (TRUE)
8077  ! upon joining. Default is **no** reindexing.
8078  logical, optional, intent(in) :: reindex
8079 
8080  ! Local counters, global (all component resource objects) and local (each
8081  ! of the resource).
8082  integer :: global_count_collapsed, i
8083 
8084  !> ### Implementation details ###
8085 
8086  global_count_collapsed = 0
8087 
8088  if (present(resource_1)) then
8089  associate( res => resource_1 )
8090  ! @warning We cannot use `do concurrent` construct here since global
8091  ! counter `global_count_collapsed` is updated sequentially
8092  ! and depends on the local counter `i`.
8093  do i=1, res%number_food_items
8094  global_count_collapsed = global_count_collapsed + 1
8095  !> For each food item within the resource we copy all the object
8096  !! component values from the **collapsed** object to the original
8097  !! component object.
8098  call res%food(i)%position( &
8099  food_resource_collapsed%food(global_count_collapsed)%location())
8100  res%food(i)%size = food_resource_collapsed%food( &
8101  global_count_collapsed)%size
8102  res%food(i)%eaten = food_resource_collapsed%food( &
8103  global_count_collapsed)%eaten
8104  res%food(i)%food_iid = food_resource_collapsed%food( &
8105  global_count_collapsed)%food_iid
8106  end do
8107  !> If `reindex` is explicitly set to TRUE, we reindex the component
8108  !! resources upon unjoining.
8109  if (present(reindex)) then
8110  if (reindex) call res%reindex()
8111  end if
8112  end associate
8113  end if
8114 
8115  if (present(resource_2)) then
8116  associate( res => resource_2 )
8117  ! @warning We cannot use `do concurrent` construct here since global
8118  ! counter `global_count_collapsed` is updated sequentially
8119  ! and depends on the local counter `i`.
8120  do i=1, res%number_food_items
8121  global_count_collapsed = global_count_collapsed + 1
8122  ! For each food item within the resource we copy all the object
8123  ! component values from the **collapsed** object to the original
8124  ! component object.
8125  call res%food(i)%position( &
8126  food_resource_collapsed%food(global_count_collapsed)%location())
8127  res%food(i)%size = food_resource_collapsed%food( &
8128  global_count_collapsed)%size
8129  res%food(i)%eaten = food_resource_collapsed%food( &
8130  global_count_collapsed)%eaten
8131  res%food(i)%food_iid = food_resource_collapsed%food( &
8132  global_count_collapsed)%food_iid
8133  end do
8134  ! If reindex is explicitly set to TRUE, we reindex the component
8135  ! resources upon unjoining.
8136  if (present(reindex)) then
8137  if (reindex) call res%reindex()
8138  end if
8139  end associate
8140  end if
8141 
8142  if (present(resource_3)) then
8143  associate( res => resource_3 )
8144  ! @warning We cannot use `do concurrent` construct here since global
8145  ! counter `global_count_collapsed` is updated sequentially
8146  ! and depends on the local counter `i`.
8147  do i=1, res%number_food_items
8148  global_count_collapsed = global_count_collapsed + 1
8149  ! For each food item within the resource we copy all the object
8150  ! component values from the **collapsed** object to the original
8151  ! component object.
8152  call res%food(i)%position( &
8153  food_resource_collapsed%food(global_count_collapsed)%location())
8154  res%food(i)%size = food_resource_collapsed%food( &
8155  global_count_collapsed)%size
8156  res%food(i)%eaten = food_resource_collapsed%food( &
8157  global_count_collapsed)%eaten
8158  res%food(i)%food_iid = food_resource_collapsed%food( &
8159  global_count_collapsed)%food_iid
8160  end do
8161  ! If reindex is explicitly set to TRUE, we reindex the component
8162  ! resources upon unjoining.
8163  if (present(reindex)) then
8164  if (reindex) call res%reindex()
8165  end if
8166  end associate
8167  end if
8168 
8169  if (present(resource_4)) then
8170  associate( res => resource_4 )
8171  ! @warning We cannot use `do concurrent` construct here since global
8172  ! counter `global_count_collapsed` is updated sequentially
8173  ! and depends on the local counter `i`.
8174  do i=1, res%number_food_items
8175  global_count_collapsed = global_count_collapsed + 1
8176  ! For each food item within the resource we copy all the object
8177  ! component values from the **collapsed** object to the original
8178  ! component object.
8179  call res%food(i)%position( &
8180  food_resource_collapsed%food(global_count_collapsed)%location())
8181  res%food(i)%size = food_resource_collapsed%food( &
8182  global_count_collapsed)%size
8183  res%food(i)%eaten = food_resource_collapsed%food( &
8184  global_count_collapsed)%eaten
8185  res%food(i)%food_iid = food_resource_collapsed%food( &
8186  global_count_collapsed)%food_iid
8187  end do
8188  ! If reindex is explicitly set to TRUE, we reindex the component
8189  ! resources upon unjoining.
8190  if (present(reindex)) then
8191  if (reindex) call res%reindex()
8192  end if
8193  end associate
8194  end if
8195 
8196  if (present(resource_5)) then
8197  associate( res => resource_5 )
8198  ! @warning We cannot use `do concurrent` construct here since global
8199  ! counter `global_count_collapsed` is updated sequentially
8200  ! and depends on the local counter `i`.
8201  do i=1, res%number_food_items
8202  global_count_collapsed = global_count_collapsed + 1
8203  ! For each food item within the resource we copy all the object
8204  ! component values from the **collapsed** object to the original
8205  ! component object.
8206  call res%food(i)%position( &
8207  food_resource_collapsed%food(global_count_collapsed)%location())
8208  res%food(i)%size = food_resource_collapsed%food( &
8209  global_count_collapsed)%size
8210  res%food(i)%eaten = food_resource_collapsed%food( &
8211  global_count_collapsed)%eaten
8212  res%food(i)%food_iid = food_resource_collapsed%food( &
8213  global_count_collapsed)%food_iid
8214  end do
8215  ! If reindex is explicitly set to TRUE, we reindex the component
8216  ! resources upon unjoining.
8217  if (present(reindex)) then
8218  if (reindex) call res%reindex()
8219  end if
8220  end associate
8221  end if
8222 
8223  if (present(resource_6)) then
8224  associate( res => resource_6 )
8225  ! @warning We cannot use `do concurrent` construct here since global
8226  ! counter `global_count_collapsed` is updated sequentially
8227  ! and depends on the local counter `i`.
8228  do i=1, res%number_food_items
8229  global_count_collapsed = global_count_collapsed + 1
8230  ! For each food item within the resource we copy all the object
8231  ! component values from the **collapsed** object to the original
8232  ! component object.
8233  call res%food(i)%position( &
8234  food_resource_collapsed%food(global_count_collapsed)%location())
8235  res%food(i)%size = food_resource_collapsed%food( &
8236  global_count_collapsed)%size
8237  res%food(i)%eaten = food_resource_collapsed%food( &
8238  global_count_collapsed)%eaten
8239  res%food(i)%food_iid = food_resource_collapsed%food( &
8240  global_count_collapsed)%food_iid
8241  end do
8242  ! If reindex is explicitly set to TRUE, we reindex the component
8243  ! resources upon unjoining.
8244  if (present(reindex)) then
8245  if (reindex) call res%reindex()
8246  end if
8247  end associate
8248  end if
8249 
8250  if (present(resource_7)) then
8251  associate( res => resource_7 )
8252  ! @warning We cannot use `do concurrent` construct here since global
8253  ! counter `global_count_collapsed` is updated sequentially
8254  ! and depends on the local counter `i`.
8255  do i=1, res%number_food_items
8256  global_count_collapsed = global_count_collapsed + 1
8257  ! For each food item within the resource we copy all the object
8258  ! component values from the **collapsed** object to the original
8259  ! component object.
8260  call res%food(i)%position( &
8261  food_resource_collapsed%food(global_count_collapsed)%location())
8262  res%food(i)%size = food_resource_collapsed%food( &
8263  global_count_collapsed)%size
8264  res%food(i)%eaten = food_resource_collapsed%food( &
8265  global_count_collapsed)%eaten
8266  res%food(i)%food_iid = food_resource_collapsed%food( &
8267  global_count_collapsed)%food_iid
8268  end do
8269  ! If reindex is explicitly set to TRUE, we reindex the component
8270  ! resources upon unjoining.
8271  if (present(reindex)) then
8272  if (reindex) call res%reindex()
8273  end if
8274  end associate
8275  end if
8276 
8277  if (present(resource_8)) then
8278  associate( res => resource_8 )
8279  ! @warning We cannot use `do concurrent` construct here since global
8280  ! counter `global_count_collapsed` is updated sequentially
8281  ! and depends on the local counter `i`.
8282  do i=1, res%number_food_items
8283  global_count_collapsed = global_count_collapsed + 1
8284  ! For each food item within the resource we copy all the object
8285  ! component values from the **collapsed** object to the original
8286  ! component object.
8287  call res%food(i)%position( &
8288  food_resource_collapsed%food(global_count_collapsed)%location())
8289  res%food(i)%size = food_resource_collapsed%food( &
8290  global_count_collapsed)%size
8291  res%food(i)%eaten = food_resource_collapsed%food( &
8292  global_count_collapsed)%eaten
8293  res%food(i)%food_iid = food_resource_collapsed%food( &
8294  global_count_collapsed)%food_iid
8295  end do
8296  ! If reindex is explicitly set to TRUE, we reindex the component
8297  ! resources upon unjoining.
8298  if (present(reindex)) then
8299  if (reindex) call res%reindex()
8300  end if
8301  end associate
8302  end if
8303 
8304  if (present(resource_9)) then
8305  associate( res => resource_9 )
8306  ! @warning We cannot use `do concurrent` construct here since global
8307  ! counter `global_count_collapsed` is updated sequentially
8308  ! and depends on the local counter `i`.
8309  do i=1, res%number_food_items
8310  global_count_collapsed = global_count_collapsed + 1
8311  ! For each food item within the resource we copy all the object
8312  ! component values from the **collapsed** object to the original
8313  ! component object.
8314  call res%food(i)%position( &
8315  food_resource_collapsed%food(global_count_collapsed)%location())
8316  res%food(i)%size = food_resource_collapsed%food( &
8317  global_count_collapsed)%size
8318  res%food(i)%eaten = food_resource_collapsed%food( &
8319  global_count_collapsed)%eaten
8320  res%food(i)%food_iid = food_resource_collapsed%food( &
8321  global_count_collapsed)%food_iid
8322  end do
8323  ! If reindex is explicitly set to TRUE, we reindex the component
8324  ! resources upon unjoining.
8325  if (present(reindex)) then
8326  if (reindex) call res%reindex()
8327  end if
8328  end associate
8329  end if
8330 
8331  if (present(resource_10)) then
8332  associate( res => resource_10 )
8333  ! @warning We cannot use `do concurrent` construct here since global
8334  ! counter `global_count_collapsed` is updated sequentially
8335  ! and depends on the local counter `i`.
8336  do i=1, res%number_food_items
8337  global_count_collapsed = global_count_collapsed + 1
8338  ! For each food item within the resource we copy all the object
8339  ! component values from the **collapsed** object to the original
8340  ! component object.
8341  call res%food(i)%position( &
8342  food_resource_collapsed%food(global_count_collapsed)%location())
8343  res%food(i)%size = food_resource_collapsed%food( &
8344  global_count_collapsed)%size
8345  res%food(i)%eaten = food_resource_collapsed%food( &
8346  global_count_collapsed)%eaten
8347  res%food(i)%food_iid = food_resource_collapsed%food( &
8348  global_count_collapsed)%food_iid
8349  end do
8350  ! If reindex is explicitly set to TRUE, we reindex the component
8351  ! resources upon unjoining.
8352  if (present(reindex)) then
8353  if (reindex) call res%reindex()
8354  end if
8355  end associate
8356  end if
8357 
8358  if (present(resource_11)) then
8359  associate( res => resource_11 )
8360  ! @warning We cannot use `do concurrent` construct here since global
8361  ! counter `global_count_collapsed` is updated sequentially
8362  ! and depends on the local counter `i`.
8363  do i=1, res%number_food_items
8364  global_count_collapsed = global_count_collapsed + 1
8365  ! For each food item within the resource we copy all the object
8366  ! component values from the **collapsed** object to the original
8367  ! component object.
8368  call res%food(i)%position( &
8369  food_resource_collapsed%food(global_count_collapsed)%location())
8370  res%food(i)%size = food_resource_collapsed%food( &
8371  global_count_collapsed)%size
8372  res%food(i)%eaten = food_resource_collapsed%food( &
8373  global_count_collapsed)%eaten
8374  res%food(i)%food_iid = food_resource_collapsed%food( &
8375  global_count_collapsed)%food_iid
8376  end do
8377  ! If reindex is explicitly set to TRUE, we reindex the component
8378  ! resources upon unjoining.
8379  if (present(reindex)) then
8380  if (reindex) call res%reindex()
8381  end if
8382  end associate
8383  end if
8384 
8385  if (present(resource_12)) then
8386  associate( res => resource_12 )
8387  ! @warning We cannot use `do concurrent` construct here since global
8388  ! counter `global_count_collapsed` is updated sequentially
8389  ! and depends on the local counter `i`.
8390  do i=1, res%number_food_items
8391  global_count_collapsed = global_count_collapsed + 1
8392  ! For each food item within the resource we copy all the object
8393  ! component values from the **collapsed** object to the original
8394  ! component object.
8395  call res%food(i)%position( &
8396  food_resource_collapsed%food(global_count_collapsed)%location())
8397  res%food(i)%size = food_resource_collapsed%food( &
8398  global_count_collapsed)%size
8399  res%food(i)%eaten = food_resource_collapsed%food( &
8400  global_count_collapsed)%eaten
8401  res%food(i)%food_iid = food_resource_collapsed%food( &
8402  global_count_collapsed)%food_iid
8403  end do
8404  ! If reindex is explicitly set to TRUE, we reindex the component
8405  ! resources upon unjoining.
8406  if (present(reindex)) then
8407  if (reindex) call res%reindex()
8408  end if
8409  end associate
8410  end if
8411 
8412  if (present(resource_13)) then
8413  associate( res => resource_13 )
8414  ! @warning We cannot use `do concurrent` construct here since global
8415  ! counter `global_count_collapsed` is updated sequentially
8416  ! and depends on the local counter `i`.
8417  do i=1, res%number_food_items
8418  global_count_collapsed = global_count_collapsed + 1
8419  ! For each food item within the resource we copy all the object
8420  ! component values from the **collapsed** object to the original
8421  ! component object.
8422  call res%food(i)%position( &
8423  food_resource_collapsed%food(global_count_collapsed)%location())
8424  res%food(i)%size = food_resource_collapsed%food( &
8425  global_count_collapsed)%size
8426  res%food(i)%eaten = food_resource_collapsed%food( &
8427  global_count_collapsed)%eaten
8428  res%food(i)%food_iid = food_resource_collapsed%food( &
8429  global_count_collapsed)%food_iid
8430  end do
8431  ! If reindex is explicitly set to TRUE, we reindex the component
8432  ! resources upon unjoining.
8433  if (present(reindex)) then
8434  if (reindex) call res%reindex()
8435  end if
8436  end associate
8437  end if
8438 
8439  if (present(resource_14)) then
8440  associate( res => resource_14 )
8441  ! @warning We cannot use `do concurrent` construct here since global
8442  ! counter `global_count_collapsed` is updated sequentially
8443  ! and depends on the local counter `i`.
8444  do i=1, res%number_food_items
8445  global_count_collapsed = global_count_collapsed + 1
8446  ! For each food item within the resource we copy all the object
8447  ! component values from the **collapsed** object to the original
8448  ! component object.
8449  call res%food(i)%position( &
8450  food_resource_collapsed%food(global_count_collapsed)%location())
8451  res%food(i)%size = food_resource_collapsed%food( &
8452  global_count_collapsed)%size
8453  res%food(i)%eaten = food_resource_collapsed%food( &
8454  global_count_collapsed)%eaten
8455  res%food(i)%food_iid = food_resource_collapsed%food( &
8456  global_count_collapsed)%food_iid
8457  end do
8458  ! If reindex is explicitly set to TRUE, we reindex the component
8459  ! resources upon unjoining.
8460  if (present(reindex)) then
8461  if (reindex) call res%reindex()
8462  end if
8463  end associate
8464  end if
8465 
8466  if (present(resource_15)) then
8467  associate( res => resource_15 )
8468  ! @warning We cannot use `do concurrent` construct here since global
8469  ! counter `global_count_collapsed` is updated sequentially
8470  ! and depends on the local counter `i`.
8471  do i=1, res%number_food_items
8472  global_count_collapsed = global_count_collapsed + 1
8473  ! For each food item within the resource we copy all the object
8474  ! component values from the **collapsed** object to the original
8475  ! component object.
8476  call res%food(i)%position( &
8477  food_resource_collapsed%food(global_count_collapsed)%location())
8478  res%food(i)%size = food_resource_collapsed%food( &
8479  global_count_collapsed)%size
8480  res%food(i)%eaten = food_resource_collapsed%food( &
8481  global_count_collapsed)%eaten
8482  res%food(i)%food_iid = food_resource_collapsed%food( &
8483  global_count_collapsed)%food_iid
8484  end do
8485  ! If reindex is explicitly set to TRUE, we reindex the component
8486  ! resources upon unjoining.
8487  if (present(reindex)) then
8488  if (reindex) call res%reindex()
8489  end if
8490  end associate
8491  end if
8492 
8493  if (present(resource_16)) then
8494  associate( res => resource_16 )
8495  ! @warning We cannot use `do concurrent` construct here since global
8496  ! counter `global_count_collapsed` is updated sequentially
8497  ! and depends on the local counter `i`.
8498  do i=1, res%number_food_items
8499  global_count_collapsed = global_count_collapsed + 1
8500  ! For each food item within the resource we copy all the object
8501  ! component values from the **collapsed** object to the original
8502  ! component object.
8503  call res%food(i)%position( &
8504  food_resource_collapsed%food(global_count_collapsed)%location())
8505  res%food(i)%size = food_resource_collapsed%food( &
8506  global_count_collapsed)%size
8507  res%food(i)%eaten = food_resource_collapsed%food( &
8508  global_count_collapsed)%eaten
8509  res%food(i)%food_iid = food_resource_collapsed%food( &
8510  global_count_collapsed)%food_iid
8511  end do
8512  ! If reindex is explicitly set to TRUE, we reindex the component
8513  ! resources upon unjoining.
8514  if (present(reindex)) then
8515  if (reindex) call res%reindex()
8516  end if
8517  end associate
8518  end if
8519 
8520  if (present(resource_17)) then
8521  associate( res => resource_17 )
8522  ! @warning We cannot use `do concurrent` construct here since global
8523  ! counter `global_count_collapsed` is updated sequentially
8524  ! and depends on the local counter `i`.
8525  do i=1, res%number_food_items
8526  global_count_collapsed = global_count_collapsed + 1
8527  ! For each food item within the resource we copy all the object
8528  ! component values from the **collapsed** object to the original
8529  ! component object.
8530  call res%food(i)%position( &
8531  food_resource_collapsed%food(global_count_collapsed)%location())
8532  res%food(i)%size = food_resource_collapsed%food( &
8533  global_count_collapsed)%size
8534  res%food(i)%eaten = food_resource_collapsed%food( &
8535  global_count_collapsed)%eaten
8536  res%food(i)%food_iid = food_resource_collapsed%food( &
8537  global_count_collapsed)%food_iid
8538  end do
8539  ! If reindex is explicitly set to TRUE, we reindex the component
8540  ! resources upon unjoining.
8541  if (present(reindex)) then
8542  if (reindex) call res%reindex()
8543  end if
8544  end associate
8545  end if
8546 
8547  if (present(resource_18)) then
8548  associate( res => resource_18 )
8549  ! @warning We cannot use `do concurrent` construct here since global
8550  ! counter `global_count_collapsed` is updated sequentially
8551  ! and depends on the local counter `i`.
8552  do i=1, res%number_food_items
8553  global_count_collapsed = global_count_collapsed + 1
8554  ! For each food item within the resource we copy all the object
8555  ! component values from the **collapsed** object to the original
8556  ! component object.
8557  call res%food(i)%position( &
8558  food_resource_collapsed%food(global_count_collapsed)%location())
8559  res%food(i)%size = food_resource_collapsed%food( &
8560  global_count_collapsed)%size
8561  res%food(i)%eaten = food_resource_collapsed%food( &
8562  global_count_collapsed)%eaten
8563  res%food(i)%food_iid = food_resource_collapsed%food( &
8564  global_count_collapsed)%food_iid
8565  end do
8566  ! If reindex is explicitly set to TRUE, we reindex the component
8567  ! resources upon unjoining.
8568  if (present(reindex)) then
8569  if (reindex) call res%reindex()
8570  end if
8571  end associate
8572  end if
8573 
8574  if (present(resource_19)) then
8575  associate( res => resource_19 )
8576  ! @warning We cannot use `do concurrent` construct here since global
8577  ! counter `global_count_collapsed` is updated sequentially
8578  ! and depends on the local counter `i`.
8579  do i=1, res%number_food_items
8580  global_count_collapsed = global_count_collapsed + 1
8581  ! For each food item within the resource we copy all the object
8582  ! component values from the **collapsed** object to the original
8583  ! component object.
8584  call res%food(i)%position( &
8585  food_resource_collapsed%food(global_count_collapsed)%location())
8586  res%food(i)%size = food_resource_collapsed%food( &
8587  global_count_collapsed)%size
8588  res%food(i)%eaten = food_resource_collapsed%food( &
8589  global_count_collapsed)%eaten
8590  res%food(i)%food_iid = food_resource_collapsed%food( &
8591  global_count_collapsed)%food_iid
8592  end do
8593  ! If reindex is explicitly set to TRUE, we reindex the component
8594  ! resources upon unjoining.
8595  if (present(reindex)) then
8596  if (reindex) call res%reindex()
8597  end if
8598  end associate
8599  end if
8600 
8601  if (present(resource_20)) then
8602  associate( res => resource_20 )
8603  ! @warning We cannot use `do concurrent` construct here since global
8604  ! counter `global_count_collapsed` is updated sequentially
8605  ! and depends on the local counter `i`.
8606  do i=1, res%number_food_items
8607  global_count_collapsed = global_count_collapsed + 1
8608  ! For each food item within the resource we copy all the object
8609  ! component values from the **collapsed** object to the original
8610  ! component object.
8611  call res%food(i)%position( &
8612  food_resource_collapsed%food(global_count_collapsed)%location())
8613  res%food(i)%size = food_resource_collapsed%food( &
8614  global_count_collapsed)%size
8615  res%food(i)%eaten = food_resource_collapsed%food( &
8616  global_count_collapsed)%eaten
8617  res%food(i)%food_iid = food_resource_collapsed%food( &
8618  global_count_collapsed)%food_iid
8619  end do
8620  ! If reindex is explicitly set to TRUE, we reindex the component
8621  ! resources upon unjoining.
8622  if (present(reindex)) then
8623  if (reindex) call res%reindex()
8624  end if
8625  end associate
8626  end if
8627 
8628  end subroutine food_resources_update_back
8629 
8630  !-----------------------------------------------------------------------------
8631  !> Transfer the (having been modified) food resource objects from the single
8632  !! united object `food_resource_collapsed` back to the global array
8633  !! the_environment::global_habitats_available array.
8634  !! See the_environment::join() for how to join an array of food resources
8635  !! into a single global object.
8636  subroutine food_resources_update_back_global_object(food_resource_collapsed,&
8637  reindex )
8638  !> @param[in] A collapsed food resource previously joining the input array.
8639  type(food_resource), intent(in) :: food_resource_collapsed
8640  !> @param[in] reindex logical flag to reindex the unjoined resource (TRUE)
8641  !! upon unjoining. The default is **no** reindexing.
8642  logical, optional, intent(in) :: reindex
8643 
8644  ! Local counters, global (all component resource objects) and local (each
8645  ! of the resource).
8646  integer :: global_count_collapsed, res_num, i, j
8647 
8648  !> ### Implementation details ###
8649  !! This procedure restores individual food resources into the global array
8650  !! the_environment::global_habitats_available array from the collapsed
8651  !! resource `food_resource_collapsed`.
8652  !!
8653  global_count_collapsed = 0
8654 
8655  do concurrent(res_num=1:size(global_habitats_available))
8656  associate( res => global_habitats_available(res_num)%food )
8657  do concurrent(i=1:res%number_food_items)
8658  !prev_res = 0
8659  !do j=1, res_num-1
8660  ! prev_res = prev_res + size( resources_in_loc(j)%food ) ! safer than + %number_food_items
8661  !end do
8662  global_count_collapsed = sum( &
8663  [( size(global_habitats_available(j)%food%food), &
8664  j=1, res_num-1 )] ) + i
8665  !> For each food item within the resource we copy all the object
8666  !! component values from the **collapsed** object to the original
8667  !! component object.
8668  call res%food(i)%position( &
8669  food_resource_collapsed%food(global_count_collapsed)%location())
8670  res%food(i)%size = food_resource_collapsed%food( &
8671  global_count_collapsed)%size
8672  res%food(i)%eaten = food_resource_collapsed%food( &
8673  global_count_collapsed)%eaten
8674  res%food(i)%food_iid = food_resource_collapsed%food( &
8675  global_count_collapsed)%food_iid
8676  end do
8677  !> If `reindex` is explicitly set to TRUE, we reindex the component
8678  !! resources upon unjoining.
8679  if (present(reindex)) then
8680  if (reindex) call res%reindex()
8681  end if
8682  end associate
8683  end do
8684 
8686 
8687  !-----------------------------------------------------------------------------
8688  !> Assemble the global habitats objects array
8689  !! the_environment::global_habitats_available from a list of separate
8690  !! habitat objects.
8691  !! This call
8692  !! @code
8693  !! assemble(hab_a, hab_b, hab_c)
8694  !! @endcode
8695  !! is equivalent to
8696  !! @code
8697  !! Global_Habitats_Available = [ hab_a, hab_b, hab_c ]
8698  !! @endcode
8699  !! @note But note that the `reindex` parameter allows automatic reindexing
8700  !! of the global array the_environment::global_habitats_available.
8701  subroutine global_habitats_assemble ( habitat_1, &
8702  habitat_2, &
8703  habitat_3, &
8704  habitat_4, &
8705  habitat_5, &
8706  habitat_6, &
8707  habitat_7, &
8708  habitat_8, &
8709  habitat_9, &
8710  habitat_10, &
8711  habitat_11, &
8712  habitat_12, &
8713  habitat_13, &
8714  habitat_14, &
8715  habitat_15, &
8716  habitat_16, &
8717  habitat_17, &
8718  habitat_18, &
8719  habitat_19, &
8720  habitat_20, &
8721  reindex )
8722 
8723  !> @param[inout] habitat_1, ... a list (up to 20) of food resources to
8724  !! restore from the joined state.
8725  !! @warning elementary habitats in the list are strictly
8726  !! **type**, extension (class) objects are not
8727  !! supported.
8728  type(habitat), optional, intent(in) :: habitat_1, &
8729  habitat_2, &
8730  habitat_3, &
8731  habitat_4, &
8732  habitat_5, &
8733  habitat_6, &
8734  habitat_7, &
8735  habitat_8, &
8736  habitat_9, &
8737  habitat_10, &
8738  habitat_11, &
8739  habitat_12, &
8740  habitat_13, &
8741  habitat_14, &
8742  habitat_15, &
8743  habitat_16, &
8744  habitat_17, &
8745  habitat_18, &
8746  habitat_19, &
8747  habitat_20
8748 
8749  !> @param[in] reindex logical flag to reindex the global joined food
8750  !! resource array (TRUE) linked to each of the habitats upon
8751  !! assemble. Default is **no** reindexing.
8752  logical, optional, intent(in) :: reindex
8753 
8754  ! Local counters, global (all component resource objects) and local (each
8755  ! of the resource).
8756  integer :: global_count_collapsed
8757 
8758  global_count_collapsed = 0
8759 
8760  !> ### Implementation notes ###
8761  !> - Stage 1: Calculate how many habitat objects are there in the
8762  !! input parameter list.
8763  if (present(habitat_1)) then
8764  global_count_collapsed = global_count_collapsed + 1
8765  end if
8766 
8767  if (present(habitat_2)) then
8768  global_count_collapsed = global_count_collapsed + 1
8769  end if
8770 
8771  if (present(habitat_3)) then
8772  global_count_collapsed = global_count_collapsed + 1
8773  end if
8774 
8775  if (present(habitat_4)) then
8776  global_count_collapsed = global_count_collapsed + 1
8777  end if
8778 
8779  if (present(habitat_5)) then
8780  global_count_collapsed = global_count_collapsed + 1
8781  end if
8782 
8783  if (present(habitat_6)) then
8784  global_count_collapsed = global_count_collapsed + 1
8785  end if
8786 
8787  if (present(habitat_7)) then
8788  global_count_collapsed = global_count_collapsed + 1
8789  end if
8790 
8791  if (present(habitat_8)) then
8792  global_count_collapsed = global_count_collapsed + 1
8793  end if
8794 
8795  if (present(habitat_9)) then
8796  global_count_collapsed = global_count_collapsed + 1
8797  end if
8798 
8799  if (present(habitat_10)) then
8800  global_count_collapsed = global_count_collapsed + 1
8801  end if
8802 
8803  if (present(habitat_11)) then
8804  global_count_collapsed = global_count_collapsed + 1
8805  end if
8806 
8807  if (present(habitat_10)) then
8808  global_count_collapsed = global_count_collapsed + 1
8809  end if
8810 
8811  if (present(habitat_13)) then
8812  global_count_collapsed = global_count_collapsed + 1
8813  end if
8814 
8815  if (present(habitat_14)) then
8816  global_count_collapsed = global_count_collapsed + 1
8817  end if
8818 
8819  if (present(habitat_15)) then
8820  global_count_collapsed = global_count_collapsed + 1
8821  end if
8822 
8823  if (present(habitat_16)) then
8824  global_count_collapsed = global_count_collapsed + 1
8825  end if
8826 
8827  if (present(habitat_17)) then
8828  global_count_collapsed = global_count_collapsed + 1
8829  end if
8830 
8831  if (present(habitat_18)) then
8832  global_count_collapsed = global_count_collapsed + 1
8833  end if
8834 
8835  if (present(habitat_19)) then
8836  global_count_collapsed = global_count_collapsed + 1
8837  end if
8838 
8839  if (present(habitat_20)) then
8840  global_count_collapsed = global_count_collapsed + 1
8841  end if
8842 
8843  !> - Stage 2: Allocate the the_environment::global_habitats_available
8844  !! global array of habitat objects the above number of elements.
8845  if (.not. allocated(global_habitats_available)) then
8846  allocate(global_habitats_available(global_count_collapsed))
8847  else
8848  deallocate(global_habitats_available)
8849  allocate(global_habitats_available(global_count_collapsed))
8850  end if
8851 
8852  !> - Stage 3: Build the global array of habitat objects one by one from
8853  !! the input list of individual habitat objects.
8854  global_count_collapsed = 0
8855 
8856  if (present(habitat_1)) then
8857  global_count_collapsed = global_count_collapsed + 1
8858  global_habitats_available(global_count_collapsed) = habitat_1
8859  end if
8860 
8861  if (present(habitat_2)) then
8862  global_count_collapsed = global_count_collapsed + 1
8863  global_habitats_available(global_count_collapsed) = habitat_2
8864  end if
8865 
8866  if (present(habitat_3)) then
8867  global_count_collapsed = global_count_collapsed + 1
8868  global_habitats_available(global_count_collapsed) = habitat_3
8869  end if
8870 
8871  if (present(habitat_4)) then
8872  global_count_collapsed = global_count_collapsed + 1
8873  global_habitats_available(global_count_collapsed) = habitat_4
8874  end if
8875 
8876  if (present(habitat_5)) then
8877  global_count_collapsed = global_count_collapsed + 1
8878  global_habitats_available(global_count_collapsed) = habitat_5
8879  end if
8880 
8881  if (present(habitat_6)) then
8882  global_count_collapsed = global_count_collapsed + 1
8883  global_habitats_available(global_count_collapsed) = habitat_6
8884  end if
8885 
8886  if (present(habitat_7)) then
8887  global_count_collapsed = global_count_collapsed + 1
8888  global_habitats_available(global_count_collapsed) = habitat_7
8889  end if
8890 
8891  if (present(habitat_8)) then
8892  global_count_collapsed = global_count_collapsed + 1
8893  global_habitats_available(global_count_collapsed) = habitat_8
8894  end if
8895 
8896  if (present(habitat_9)) then
8897  global_count_collapsed = global_count_collapsed + 1
8898  global_habitats_available(global_count_collapsed) = habitat_9
8899  end if
8900 
8901  if (present(habitat_10)) then
8902  global_count_collapsed = global_count_collapsed + 1
8903  global_habitats_available(global_count_collapsed) = habitat_10
8904  end if
8905 
8906  if (present(habitat_11)) then
8907  global_count_collapsed = global_count_collapsed + 1
8908  global_habitats_available(global_count_collapsed) = habitat_11
8909  end if
8910 
8911  if (present(habitat_12)) then
8912  global_count_collapsed = global_count_collapsed + 1
8913  global_habitats_available(global_count_collapsed) = habitat_12
8914  end if
8915 
8916  if (present(habitat_13)) then
8917  global_count_collapsed = global_count_collapsed + 1
8918  global_habitats_available(global_count_collapsed) = habitat_13
8919  end if
8920 
8921  if (present(habitat_14)) then
8922  global_count_collapsed = global_count_collapsed + 1
8923  global_habitats_available(global_count_collapsed) = habitat_14
8924  end if
8925 
8926  if (present(habitat_15)) then
8927  global_count_collapsed = global_count_collapsed + 1
8928  global_habitats_available(global_count_collapsed) = habitat_15
8929  end if
8930 
8931  if (present(habitat_16)) then
8932  global_count_collapsed = global_count_collapsed + 1
8933  global_habitats_available(global_count_collapsed) = habitat_16
8934  end if
8935 
8936  if (present(habitat_17)) then
8937  global_count_collapsed = global_count_collapsed + 1
8938  global_habitats_available(global_count_collapsed) = habitat_17
8939  end if
8940 
8941  if (present(habitat_18)) then
8942  global_count_collapsed = global_count_collapsed + 1
8943  global_habitats_available(global_count_collapsed) = habitat_18
8944  end if
8945 
8946  if (present(habitat_19)) then
8947  global_count_collapsed = global_count_collapsed + 1
8948  global_habitats_available(global_count_collapsed) = habitat_19
8949  end if
8950 
8951  if (present(habitat_20)) then
8952  global_count_collapsed = global_count_collapsed + 1
8953  global_habitats_available(global_count_collapsed) = habitat_20
8954  end if
8955 
8956  !> - Stage 4: Optionally reindex each element of the the global array
8957  !! the_environment::global_habitats_available.
8958  !! .
8959  if (present(reindex)) then
8960  if (reindex) then
8961  do concurrent(global_count_collapsed=1:size(global_habitats_available))
8962  call global_habitats_available(global_count_collapsed)%food%reindex()
8963  end do
8964  end if
8965  end if
8966 
8967  end subroutine global_habitats_assemble
8968 
8969  !-----------------------------------------------------------------------------
8970  !> Disassemble the global habitats objects array
8971  !! the_environment::global_habitats_available into separate habitat object.
8972  subroutine global_habitats_disassemble ( habitat_1, &
8973  habitat_2, &
8974  habitat_3, &
8975  habitat_4, &
8976  habitat_5, &
8977  habitat_6, &
8978  habitat_7, &
8979  habitat_8, &
8980  habitat_9, &
8981  habitat_10, &
8982  habitat_11, &
8983  habitat_12, &
8984  habitat_13, &
8985  habitat_14, &
8986  habitat_15, &
8987  habitat_16, &
8988  habitat_17, &
8989  habitat_18, &
8990  habitat_19, &
8991  habitat_20, &
8992  reindex )
8993 
8994  !> @param[inout] habitat_1, ... a list (from 2 to 20) of food resources to
8995  !! restore from the joined state.
8996  !! @warning elementary habitats in the list are strictly
8997  !! **type**, extension (class) objects are not
8998  !! supported.
8999  type(habitat), intent(out) :: habitat_1
9000 
9001  type(habitat), optional, intent(out) :: habitat_2, &
9002  habitat_3, &
9003  habitat_4, &
9004  habitat_5, &
9005  habitat_6, &
9006  habitat_7, &
9007  habitat_8, &
9008  habitat_9, &
9009  habitat_10, &
9010  habitat_11, &
9011  habitat_12, &
9012  habitat_13, &
9013  habitat_14, &
9014  habitat_15, &
9015  habitat_16, &
9016  habitat_17, &
9017  habitat_18, &
9018  habitat_19, &
9019  habitat_20
9020 
9021 
9022  !> @param[in] reindex logical flag to reindex the joined food resource
9023  !! (TRUE) linked to each of the habitats upon disassemble.
9024  !! Default is **no** reindexing.
9025  logical, optional, intent(in) :: reindex
9026 
9027  ! Local counters, global (all component resource objects) and local (each
9028  ! of the resource).
9029  integer :: global_count_collapsed
9030 
9031  global_count_collapsed = 0
9032 
9033  !if (present(habitat_1)) then
9034  global_count_collapsed = global_count_collapsed + 1
9035  habitat_1 = global_habitats_available( global_count_collapsed )
9036  if (present(reindex)) then
9037  if (reindex) call habitat_1%food%reindex()
9038  end if
9039  !end if
9040 
9041  if (present(habitat_2)) then
9042  global_count_collapsed = global_count_collapsed + 1
9043  habitat_2 = global_habitats_available( global_count_collapsed )
9044  if (present(reindex)) then
9045  if (reindex) call habitat_2%food%reindex()
9046  end if
9047  end if
9048 
9049  if (present(habitat_3)) then
9050  global_count_collapsed = global_count_collapsed + 1
9051  habitat_3 = global_habitats_available( global_count_collapsed )
9052  if (present(reindex)) then
9053  if (reindex) call habitat_3%food%reindex()
9054  end if
9055  end if
9056 
9057  if (present(habitat_4)) then
9058  global_count_collapsed = global_count_collapsed + 1
9059  habitat_4 = global_habitats_available( global_count_collapsed )
9060  if (present(reindex)) then
9061  if (reindex) call habitat_4%food%reindex()
9062  end if
9063  end if
9064 
9065  if (present(habitat_5)) then
9066  global_count_collapsed = global_count_collapsed + 1
9067  habitat_5 = global_habitats_available( global_count_collapsed )
9068  if (present(reindex)) then
9069  if (reindex) call habitat_5%food%reindex()
9070  end if
9071  end if
9072 
9073  if (present(habitat_6)) then
9074  global_count_collapsed = global_count_collapsed + 1
9075  habitat_6 = global_habitats_available( global_count_collapsed )
9076  if (present(reindex)) then
9077  if (reindex) call habitat_6%food%reindex()
9078  end if
9079  end if
9080 
9081  if (present(habitat_7)) then
9082  global_count_collapsed = global_count_collapsed + 1
9083  habitat_7 = global_habitats_available( global_count_collapsed )
9084  if (present(reindex)) then
9085  if (reindex) call habitat_7%food%reindex()
9086  end if
9087  end if
9088 
9089  if (present(habitat_8)) then
9090  global_count_collapsed = global_count_collapsed + 1
9091  habitat_8 = global_habitats_available( global_count_collapsed )
9092  if (present(reindex)) then
9093  if (reindex) call habitat_8%food%reindex()
9094  end if
9095  end if
9096 
9097  if (present(habitat_9)) then
9098  global_count_collapsed = global_count_collapsed + 1
9099  habitat_9 = global_habitats_available( global_count_collapsed )
9100  if (present(reindex)) then
9101  if (reindex) call habitat_9%food%reindex()
9102  end if
9103  end if
9104 
9105  if (present(habitat_10)) then
9106  global_count_collapsed = global_count_collapsed + 1
9107  habitat_10 = global_habitats_available( global_count_collapsed )
9108  if (present(reindex)) then
9109  if (reindex) call habitat_10%food%reindex()
9110  end if
9111  end if
9112 
9113  if (present(habitat_11)) then
9114  global_count_collapsed = global_count_collapsed + 1
9115  habitat_11 = global_habitats_available( global_count_collapsed )
9116  if (present(reindex)) then
9117  if (reindex) call habitat_11%food%reindex()
9118  end if
9119  end if
9120 
9121  if (present(habitat_12)) then
9122  global_count_collapsed = global_count_collapsed + 1
9123  habitat_12 = global_habitats_available( global_count_collapsed )
9124  if (present(reindex)) then
9125  if (reindex) call habitat_12%food%reindex()
9126  end if
9127  end if
9128 
9129  if (present(habitat_13)) then
9130  global_count_collapsed = global_count_collapsed + 1
9131  habitat_13 = global_habitats_available( global_count_collapsed )
9132  if (present(reindex)) then
9133  if (reindex) call habitat_13%food%reindex()
9134  end if
9135  end if
9136 
9137  if (present(habitat_14)) then
9138  global_count_collapsed = global_count_collapsed + 1
9139  habitat_14 = global_habitats_available( global_count_collapsed )
9140  if (present(reindex)) then
9141  if (reindex) call habitat_14%food%reindex()
9142  end if
9143  end if
9144 
9145  if (present(habitat_15)) then
9146  global_count_collapsed = global_count_collapsed + 1
9147  habitat_15 = global_habitats_available( global_count_collapsed )
9148  if (present(reindex)) then
9149  if (reindex) call habitat_15%food%reindex()
9150  end if
9151  end if
9152 
9153  if (present(habitat_16)) then
9154  global_count_collapsed = global_count_collapsed + 1
9155  habitat_16 = global_habitats_available( global_count_collapsed )
9156  if (present(reindex)) then
9157  if (reindex) call habitat_16%food%reindex()
9158  end if
9159  end if
9160 
9161  if (present(habitat_17)) then
9162  global_count_collapsed = global_count_collapsed + 1
9163  habitat_17 = global_habitats_available( global_count_collapsed )
9164  if (present(reindex)) then
9165  if (reindex) call habitat_17%food%reindex()
9166  end if
9167  end if
9168 
9169  if (present(habitat_18)) then
9170  global_count_collapsed = global_count_collapsed + 1
9171  habitat_18 = global_habitats_available( global_count_collapsed )
9172  if (present(reindex)) then
9173  if (reindex) call habitat_18%food%reindex()
9174  end if
9175  end if
9176 
9177  if (present(habitat_19)) then
9178  global_count_collapsed = global_count_collapsed + 1
9179  habitat_19 = global_habitats_available( global_count_collapsed )
9180  if (present(reindex)) then
9181  if (reindex) call habitat_19%food%reindex()
9182  end if
9183  end if
9184 
9185  if (present(habitat_20)) then
9186  global_count_collapsed = global_count_collapsed + 1
9187  habitat_20 = global_habitats_available( global_count_collapsed )
9188  if (present(reindex)) then
9189  if (reindex) call habitat_20%food%reindex()
9190  end if
9191  end if
9192 
9193  end subroutine global_habitats_disassemble
9194 
9195  !-----------------------------------------------------------------------------
9196  !> Calculate the distances between **this** spatial object and an array of
9197  !! its neighbours. Optionally output the distances, sorting index vector
9198  !! and rankings vector for each of these neighbours. Optionally do only
9199  !! partial indexing, up to the order `rank_max` for computational speed.
9200  !! Procedure `ARRAY_INDEX()` from HEDTOOLS is used as the computational
9201  !! backend for partial segmented indexing.
9202  !! @param[in] neighbours an array of spatial objects that we
9203  !! sort by distance from **this** target object.
9204  !! @param[out] dist optional vector of the distance between each of the
9205  !! neighbours and **this** spatial object.
9206  !! @param[out] index_vector a vector for sort order indexing of the
9207  !! neighbours. see documentation for `ARRAY_INDEX()` in `HEDTOOLS`
9208  !! for more details on sort order indexing.
9209  !! @param[out] ranks optional vector of rank ordering scores of each of
9210  !! the distances.
9211  !! @param[in] rank_max sets the maximum limit on the objects to rank/index
9212  !! we are interested in, i.e. for partial indexing (see manual
9213  !! for `ARRAY_INDEX`).
9214  !! @param[out] error_flag optional error flag, normally should be FALSE.
9215  !! @warning Cannot be made `pure` because of I/O calls.
9216  subroutine spatial_neighbours_distances(this, neighbours, dist, &
9217  index_vector, ranks, rank_max, &
9218  error_flag)
9219  class(spatial), intent(in) :: this
9220 
9221  ! @param neighbours an array of spatial objects that we
9222  ! sort by distance from **this** target object.
9223  class(spatial), dimension(:), intent(in) :: neighbours
9224 
9225  ! @param dist optional vector of the distance between each of the
9226  ! neighbours and **this** spatial object.
9227  real(SRP), dimension(:), optional, intent(out) :: dist
9228 
9229  ! @param index_vector a vector for sort order indexing of the neighbours.
9230  ! see documentation for `ARRAY_INDEX` in `HEDTOOLS` for more
9231  ! details on sort order indexing.
9232  integer, dimension(:), optional, intent(out) :: index_vector
9233 
9234  ! @param ranks optional vector of rank ordering scores of each of
9235  ! the distances.
9236  integer, dimension(:), optional, intent(out) :: ranks
9237 
9238  ! @param rank_max sets the maximum limit on the objects to rank/index
9239  ! we are interested in, i.e. for partial indexing (see manual
9240  ! for `ARRAY_INDEX`).
9241  integer, optional, intent(in) :: rank_max
9242 
9243  ! @param error_flag optional error flag, normally should be FALSE
9244  logical, optional, intent(out) :: error_flag
9245 
9246  ! Local copy of distances vector between **this** spatial object and
9247  ! each of the neighbours.
9248  real(SRP), dimension(size(neighbours)) :: dist_here
9249  integer, dimension(size(neighbours)) :: index_vec_here
9250 
9251  ! PROCNAME is for error reporting and debugging
9252  character (len=*), parameter :: PROCNAME = "(spatial_neighbours_distances)"
9253 
9254  ! Initialise `error_flag` if it is provided.
9255  if (present(error_flag)) error_flag=.false.
9256 
9257  !> ### Implementation details ###
9258  !> First, calculate the distances between this object ant all of its
9259  !! neighbours. This is done using the parallel `do concurrent` construct
9260  !! from F2008.
9261  !do concurrent ( i = 1:size(neighbours) )
9262  ! dist_here(i) = this%distance(neighbours(i))
9263  !end do
9264  dist_here = this%distance(neighbours)
9265 
9266  ! Check if we have to output these distances.
9267  if (present(dist)) dist = dist_here
9268 
9269  !> Iterative vector sorting and ranking indexes can be slow to calculate
9270  !! when there are many neighbours. So we need to know are they really
9271  !! necessary (parameters present). Also check that all vectors have the
9272  !! same sizes.
9273  if (present(index_vector)) then
9274  ! If we need index vector then calculate it.
9275  if ( size(neighbours) /= size(index_vector) ) then
9276  if (present(error_flag)) error_flag=.true.
9277  call log_msg( ltag_warn // procname // &
9278  ": INDEX_VECTOR mismatch neighbours vector!" )
9279  end if
9280  if (present(rank_max)) then
9281  !> Partial indexing is used if `rank_max` parameter is provided. This
9282  !! will avoid full indexing of all objects which may be much faster for
9283  !! big arrays.
9284  call array_index(dist_here, index_vector, rank_max)
9285  else
9286  ! Full indexing otherwise.
9287  call array_index(dist_here, index_vector)
9288  end if
9289  if (present(ranks)) then
9290  !> Then calculate ranks if we need them.
9291  if ( size(neighbours) /= size(ranks) ) then
9292  if (present(error_flag)) error_flag=.true.
9293  call log_msg( ltag_warn // procname // &
9294  ": RANKS mismatch neighbours vector!")
9295  end if
9296  call array_rank(index_vector, ranks)
9297  end if
9298  else
9299  !> If we need ranks, calculate both index vector and ranks
9300  if (present(ranks)) then
9301  if ( size(neighbours) /= size(ranks) ) then
9302  call log_msg( ltag_warn // procname // &
9303  ": RANKS mismatch neighbours vector!")
9304  end if
9305  if (present(rank_max)) then
9306  !> Use partial indexing if `rank_max` parameter is provided. This
9307  !! will avoid full indexing of all objects which may be much faster
9308  !! for big arrays.
9309  call array_index(dist_here, index_vec_here, rank_max)
9310  else
9311  !> Full indexing otherwise.
9312  call array_index(dist_here, index_vec_here)
9313  end if
9314  call array_rank(index_vec_here, ranks)
9315  index_vector = index_vec_here
9316  end if
9317  end if
9318 
9319  end subroutine spatial_neighbours_distances
9320 
9321  !-----------------------------------------------------------------------------
9322  !> Initialise a predator object.
9323  !! @param body_size the body size of the predator.
9324  !! @param attack_rate baseline attack rate of the predator.
9325  !! @param environment The environment within which the predator is located
9326  !! initially.
9327  !! @param label optional label for the predator.
9328  elemental subroutine predator_make_init(this, body_size, attack_rate, &
9329  position, label)
9330  class(predator), intent(inout) :: this
9331 
9332  ! @param body_size the body size of the predator.
9333  real(srp), intent(in) :: body_size
9334  ! @param attack_rate baseline attack rate of the predator.
9335  real(srp), intent(in) :: attack_rate
9336  ! @param environment The environment within which the predator is located
9337  !! initially
9338  type(spatial), intent(in), optional :: position
9339 
9340  ! @param label optional label for the predator
9341  character(len=*), optional, intent(in) :: label
9342 
9343  !> ### Implementation details ###
9344  !> We first create an empty spatial sub-object for the predator.
9345  call this%create()
9346 
9347  !> Set the body size parameter, with a limitation that it must exceed zero.
9348  this%body_size = max( zero, body_size )
9349 
9350  !> Set the capture attack rate parameter, limited to be within the range
9351  !! [0:1].
9352  this%attack_rate = within(attack_rate, 0.0_srp, 1.0_srp)
9353 
9354  !> Set the initial position if it is provided (will remain `MISSING` as
9355  !! initialised in `create` method above otherwise).
9356  if (present(position)) call this%position(position)
9357 
9358  !> Finally, set label for the predator if provided, empty if absent.
9359  if (present(label)) then
9360  this%label = label
9361  else
9362  this%label = ""
9363  end if
9364 
9365  end subroutine predator_make_init
9366 
9367  !-----------------------------------------------------------------------------
9368  !> Set label for the predator, if not provided, set it random.
9369  !! @param label optional label for the predator.
9370  subroutine predator_label_set(this, label)
9371  class(predator), intent(inout) :: this
9372  ! @param label optional label for the predator.
9373  character(len=*), optional :: label
9374 
9375  if (present(label)) then
9376  this%label = label
9377  else
9378  this%label = "PRED_" // rand_string( label_length-len("PRED_"), &
9379  label_cst, label_cen)
9380  end if
9381 
9382  end subroutine predator_label_set
9383 
9384  !-----------------------------------------------------------------------------
9385  !> Accessor function for the predator body size (length).
9386  elemental function predator_get_body_size(this) result (body_size_get)
9387  class(predator), intent(in) :: this
9388  real(srp) :: body_size_get
9389 
9390  body_size_get = this%body_size
9391 
9392  end function predator_get_body_size
9393 
9394  !-----------------------------------------------------------------------------
9395  !> Accessor function for the predator attack rate.
9396  elemental function predator_get_attack_rate(this) result (capt_get)
9397  class(predator), intent(in) :: this
9398  real(srp) :: capt_get
9399 
9400  capt_get = this%attack_rate
9401 
9402  end function predator_get_attack_rate
9403 
9404  !-----------------------------------------------------------------------------
9405  !> Calculates the risk of capture of the fish with the spatial location
9406  !! defined by `prey_spatial` and the body length equal to `prey_length`.
9407  !! This is a backend function bound to the predator rather than prey
9408  !! object.
9409  !! @note This procedure calculates the probability of capture from the the
9410  !! predator object side:
9411  !! @code
9412  !! predator%risk_fish( agent%location, agent%get_length() )
9413  !! @endcode
9414  !! It is not possible for the predator-object-bound function to
9415  !! determine the properties of the prey agent (that is the dummy
9416  !! parameter) as these parameters are defined in the_neurobio module
9417  !! later in the class hierarchy. For example, body mass of the agent
9418  !! is set in the_body::condition whereas the perception object
9419  !! bound function the_neurobio::perception::has_pred is defined in
9420  !! the the_neurobio::perception class. This is why the properties of
9421  !! the prey, the length and the distance, are set via mandatory dummy
9422  !! parameters. The capture probability should normally be calculated
9423  !! for the agent the other way round using the frontend function
9424  !! the_neurobio::perception::risk_pred():
9425  !! @code
9426  !! agent%risk_pred( predator )
9427  !! @endcode
9429  prey_spatial, prey_length, &
9430  prey_distance, is_freezing, &
9431  time_step_model, debug_plot_file) result (risk_out)
9432  class(predator), intent(in) :: this
9433  !> @param[in] prey_spatial the spatial position of a fish/agent prey.
9434  class(spatial), intent(in) :: prey_spatial
9435  !> @param[in] prey_length the length of the prey fish agent.
9436  real(srp), intent(in) :: prey_length
9437  !> @param[in] prey_distance optional distance between the `this` predator
9438  !! and the prey fish agent.
9439  real(srp), optional, intent(in) :: prey_distance
9440  !> @param[in] is_freezing Optional logical indicator that the prey fish
9441  !! agent is immobile.
9442  logical, optional, intent(in) :: is_freezing
9443  !> @param[in] time_step_model optional time step of the model.
9444  integer, optional, intent(in) :: time_step_model
9445  !> @param[in] debug_plot_file optional file name for the debug
9446  !! nonparametric function (interpolation) plot.
9447  character(len=*), optional, intent(in) :: debug_plot_file
9448 
9449  !> @return The probability of successful capture of the prey by the `this`
9450  !! predator
9451  real(srp) :: risk_out
9452 
9453  character(len=*), parameter :: procname = &
9454  "(predator_capture_risk_calculate_fish)"
9455 
9456  ! Local copies of optionals
9457  real(srp) :: prey_distance_here
9458  logical :: is_freezing_loc
9459  integer :: time_step_here
9460  character(FILENAME_LENGTH) :: debug_plot_file_here
9461 
9462  ! Local variables:
9463  ! Illumination at the prey agent depth.
9464  real(srp) :: irradiance_agent_depth
9465  ! The side area of the fish agent.
9466  real(srp) :: prey_fish_area_m
9467  ! The visibility of the prey fish agent, calculated using the
9468  ! visual range backend.
9469  real(srp) :: prey_visibility
9470 
9471  !> ### Check optional parameters ###
9472  if (present(is_freezing)) then
9473  is_freezing_loc = is_freezing
9474  else
9475  is_freezing_loc = .false.
9476  end if
9477 
9478  !> Check optional time step parameter. If unset, use global variable
9479  !! `commondata::global_time_step_model_current`.
9480  if (present(time_step_model)) then
9481  time_step_here = time_step_model
9482  else
9483  time_step_here = global_time_step_model_current
9484  end if
9485 
9486  !> Check the distance to the prey dummy parameter. If present, the dummy
9487  !! parameter for the distance is used.
9488  if (present(prey_distance)) then
9489  prey_distance_here = prey_distance
9490  else
9491  !> However, if the dummy parameter is not provided, the distance between
9492  !! the `this` predator and the prey spatial object is calculated using
9493  !! the spatial::distance() function.
9494  prey_distance_here = this%distance( prey_spatial )
9495  end if
9496 
9497  !> Check optional debug plot file name, if absent, a random name is
9498  !! generated based on the predator's iid. However, the prey agent's
9499  !! iid cannot yet be determined as it is defined in the following
9500  !! module layers. Threfore, it should be provided, if necessary, via
9501  !! the optional file name parameter.
9502  if (present(debug_plot_file)) then
9503  debug_plot_file_here = debug_plot_file
9504  else
9505  debug_plot_file_here = "plot_debug_predation_risk_" // &
9506  tostr(global_time_step_model_current) // "_" // &
9507  rand_string(label_length, label_cst,label_cen) // ps
9508  end if
9509 
9510  !> ### Implementation details ###
9511  !> #### Calculate the visibility of the prey fish object ####
9512  !> First, calculate the **illumination** (background irradiance) at the
9513  !! depth of the prey spatial object.
9514  irradiance_agent_depth = prey_spatial%illumination(time_step_here)
9515 
9516  !> Second, calculate the **fish prey area** (m) that will later go into
9517  !! the visual range calculation backend engine.
9518  prey_fish_area_m = length2sidearea_fish( cm2m( prey_length ) )
9519 
9520  !> Third, calculate the visual range for the predator for detecting the
9521  !! fish prey object defined by the `prey_spatial` spatial object.
9522  !! It is assumed here that the predator is much larger than the agent
9523  !! prey.
9524  !! @image html img_doxygen_predator_visrange.svg
9525  !! @image latex img_doxygen_predator_visrange.eps "Visual ranges for the agent and the predator" width=14cm
9526  !! At the figure above, @f$ VR_{p} @f$ is the visual range for the agent
9527  !! to detect the predator and @f$ VR_{a} @f$ is the visual range for the
9528  !! predator to see the agent prey.
9529  !> This is why he calculations of the capture probability are
9530  !! based on the latter, @f$ VR_{a} @f$, the visibility of the prey for the
9531  !! predator.
9532  prey_visibility = &
9533  m2cm( visual_range( &
9534  irradiance = irradiance_agent_depth, &
9535  prey_area = prey_fish_area_m, &
9536  prey_contrast = individual_visual_contrast_default &
9537  ) )
9538 
9539  !> #### Check prey agent visibility ####
9540  !> Check if the prey agent is visible to the predator. Predator can only
9541  !! attack a prey agent that it can see. Otherwise zero predation risk is
9542  !! returned
9543  if ( prey_visibility < prey_distance_here ) then
9544  call log_dbg(ltag_info // "Prey agent is invisible to the predator:" // &
9545  " visibility=" // tostr(prey_visibility) // " < " // &
9546  " distance=" // tostr(prey_distance_here), procname, modname)
9547  risk_out = 0.0_srp
9548  return
9549  end if
9550 
9551  !> #### Calculate the predation risk ####
9552  !> Calculation is conducted differently depending on whether the agent is
9553  !! **moving** or **immobile** (freezing). Because the dimensionality
9554  !! of the interpolation grid arrays can be different in these two cases
9555  !! (requiring different declarations) they are isolated into two Fortran
9556  !! named block constructs.
9557  moving_vs_freezing: if (is_freezing_loc) then
9558 
9559  !> ##### Freezing (immobile) agent #####
9560  !! Calculations of the predation risk for a freezing agent are
9561  !! conducted in the named block construct `FREEZING`.
9562  freezing: block
9563 
9564  ! Interpolation grid, the dimensionality can be different in
9565  ! moving and freezing agents:
9566  ! The dimensionality of the grid.
9567  integer, parameter :: interpol_dim = 4
9568  ! Interpolation grid abscissa and ordinate.
9569  real(srp), dimension(INTERPOL_DIM) :: interpol_abscissa, &
9570  interpol_ordinate
9571 
9572  !> - Calculate the interpolation grid that is then used to calculate
9573  !! the predation risk.
9574  !> - The interpolation **abscissa** is defined by the visual range:
9575  !! from zero, half, 0.75 of the visual range and a full visual range.
9576  !! It is assumed that tha ability of the predator to locate and
9577  !! attack an immobile (freezing) agent is much smaller than a moving
9578  !! agent.
9579  interpol_abscissa = [ 0.0_srp, &
9580  prey_visibility * 0.50_srp, &
9581  prey_visibility * 0.75_srp, &
9582  prey_visibility ]
9583 
9584  !> - The interpolation grid **ordinate** determines the nonparametric
9585  !! relationship between the distance between the predator and prey
9586  !! and the predation risk, i.e. the probability of successful capture
9587  !! of the prey fish agent. The probability of capture at the zero
9588  !! distance is fixed and equal to the predator's inherent **attack
9589  !! rate** (the the_environment::predator::attack_rate data component)
9590  !! that is always less than the theoretically possible probability
9591  !! 1.0, whereas at the end of the visual range, the probability of
9592  !! capture is zero. Therefore, the interpolation function is defined
9593  !! by the two middle points: 0.5 and 0.75 of the visual range:
9594  !! - commondata::predator_attack_capture_prob_frz_50
9595  !! - commondata::predator_attack_capture_prob_frz_75
9596  !! .
9597  interpol_ordinate = [ this%attack_rate, &
9598  this%attack_rate * &
9599  predator_attack_capture_prob_frz_50, &
9600  this%attack_rate * &
9601  predator_attack_capture_prob_frz_75, &
9602  0.0_srp ]
9603 
9604  !> - Finally, the probability of capture of the target prey fish agent is
9605  !! a nonparametric function of the distance between the
9606  !! predator and the prey calculated using the above grid arrays.
9607  !! There is an additional condition that this probability must be
9608  !! *0 < p < 1* that is enforced by commondata::within().
9609  !! @image html img_doxygen_predator_capt_frz.svg
9610  !! @image latex img_doxygen_predator_capt_frz.eps "Predator capture probability of a freezing agent" width=14cm
9611  !> Produce plot with:
9612  !! @verbatim
9613  !! htintrpl.exe [0 0.5 0.75 1] [1 0.1 0.01 0] [0.5] [nonlinear]
9614  !! @endverbatim
9615  risk_out = within( ddpinterpol( interpol_abscissa, &
9616  interpol_ordinate, &
9617  prey_distance_here ), 0.0_srp, 1.0_srp )
9618 
9619  !> - Interpolation plots can be saved in the @ref intro_debug_mode
9620  !! "debug mode" using this plotting command:
9621  !! commondata::debug_interpolate_plot_save().
9622  !! .
9623  call debug_interpolate_plot_save( &
9624  grid_xx=interpol_abscissa, grid_yy=interpol_ordinate, &
9625  ipol_value=prey_distance_here, &
9626  algstr="DDPINTERPOL", & ! Must be as in `risk_out`!
9627  output_file=trim(debug_plot_file_here) )
9628 
9629  end block freezing
9630 
9631  else moving_vs_freezing
9632 
9633  !> ##### Normal moving agent #####
9634  !! Calculations of the predation risk for the normal moving agent are
9635  !! conducted in the named block construct `NORMAL_MOVING`.
9636  normal_moving: block
9637 
9638  ! Interpolation grid, the dimensionality can be different in moving and
9639  ! freezing agents:
9640  ! The dimensionality of the grid.
9641  integer, parameter :: interpol_dim = 3
9642  ! Interpolation grid abscissa and ordinate.
9643  real(srp), dimension(INTERPOL_DIM) :: interpol_abscissa, &
9644  interpol_ordinate
9645 
9646  !> - Calculate the interpolation grid that is then used to calculate
9647  !! the predation risk.
9648  !> - The interpolation **abscissa** is defined by the visual range:
9649  !! zero, distance half of the visual range, full visual range.
9650  interpol_abscissa = [0.0_srp, prey_visibility/2.0_srp, prey_visibility]
9651 
9652  !> - The interpolation grid **ordinate** determines the nonparametric
9653  !! relationship between the distance between the predator and prey
9654  !! and the predation risk, i.e. the probability of successful capture
9655  !! of the prey fish agent. The probability of capture at the zero
9656  !! distance is fixed and equal to the predator's inherent **attack
9657  !! rate** (the the_environment::predator::attack_rate data component)
9658  !! that is always less than the theoretically possible probability
9659  !! 1.0, whereas at the end of the visual range, the probability of
9660  !! capture is much lower and is defined by the
9661  !! commondata::predator_attack_capture_probability_min parameter.
9662  !! Therefore, there is only one value that can be set independently:
9663  !! the probability of capture at the distance equal to 1/2 of the
9664  !! visual range: it is defined as the parameter constant
9665  !! commondata::predator_attack_capture_probability_half.
9666  interpol_ordinate = [ this%attack_rate, &
9667  this%attack_rate * &
9668  predator_attack_capture_probability_half, &
9669  this%attack_rate * &
9670  predator_attack_capture_probability_min ]
9671 
9672  !> - Finally, the probability of capture of the target prey fish agent
9673  !! is a nonparametric function of the distance between the
9674  !! predator and the prey calculated using the above grid arrays.
9675  !! There is an additional condition that this probability must be
9676  !! *0 < p < 1* that is enforced by commondata::within().
9677  !! @image html img_doxygen_predator_capt.svg
9678  !! @image latex img_doxygen_predator_capt.eps "Predator capture probability" width=14cm
9679  !> Produce plot with:
9680  !! @verbatim
9681  !! htintrpl.exe [0 0.5 0.75 1] [1 0.1 0.01 0] [0.5] [nonlinear]
9682  !! @endverbatim
9683  risk_out = within( ddpinterpol( interpol_abscissa, &
9684  interpol_ordinate, &
9685  prey_distance_here ), 0.0_srp, 1.0_srp )
9686 
9687  !> - Interpolation plots can be saved in the @ref intro_debug_mode
9688  !! "debug mode" using this plotting command:
9689  !! commondata::debug_interpolate_plot_save().
9690  !! .
9691  call debug_interpolate_plot_save( &
9692  grid_xx=interpol_abscissa, grid_yy=interpol_ordinate, &
9693  ipol_value=prey_distance_here, &
9694  algstr="DDPINTERPOL", & ! Must be as in `risk_out`!
9695  output_file=trim(debug_plot_file_here) )
9696 
9697  end block normal_moving
9698 
9699  end if moving_vs_freezing
9700 
9701  !> #### Extended debugging outputs ####
9702  !> Log the capture probability visual range calculated, along with the
9703  !! distance and the visual range.
9704  if ( is_freezing_loc ) call log_dbg(ltag_info // "Agent is freezing.", &
9705  procname, modname)
9706  call log_dbg(ltag_info // "Calculated predator's capture probability: " //&
9707  tostr(risk_out) // ", Distance: " // tostr(prey_distance_here) // &
9708  ", Visual range:" // tostr(prey_visibility), procname, modname)
9709 
9711 
9712  !-----------------------------------------------------------------------------
9713  !> Calculates the risk of capture by a specific predator of an
9714  !! array of the fish agents with the spatial locations array
9715  !! defined by `prey_spatial` and the body length array
9716  !! `prey_length`. This subroutine takes account of both the predator
9717  !! dilution and confusion effects and risk adjusted by the distance
9718  !! towards the predator.
9719  !! @note This procedure accepts prey agents as a series of separate
9720  !! component arrays: `prey_spatial`, `prey_length`, `is_freezing`
9721  !! rather than a sigle the_individual::individual_agent type/class.
9722  !! This is done because the agent class hierarchy is defined
9723  !! specifically in the upstream modules and is not yet accessible
9724  !! at this level. The procedure here has the bare minimum requirements
9725  !! for the prey agents: the class the_environment::spatial.
9727  prey_spatial, prey_length, is_freezing, &
9728  time_step_model, risk, risk_indexed, &
9729  index_dist)
9730  class(predator), intent(in) :: this
9731  !> @param[in] prey_spatial the spatial position of a fish/agent prey.
9732  class(spatial), dimension(:), intent(in) :: prey_spatial
9733  !> @param[in] prey_length the length of the prey fish agent.
9734  real(SRP), dimension(:), intent(in) :: prey_length
9735  !> @param[in] is_freezing Optional logical indicator that the prey fish
9736  !! agent is immobile.
9737  logical, optional, dimension(:), intent(in) :: is_freezing
9738  !> @param[in] time_step_model optional time step of the model.
9739  integer, optional, intent(in) :: time_step_model
9740  !> @param[out] risk is an optional array of predation risk estimates for
9741  !! each agent in the group. The values of the risk for all
9742  !! agents that are not visible to the predator get zero risk.
9743  !! Note that this array has the normal order of the objects
9744  !! as in the input arrays, i.e. as in the normal array of
9745  !! the agents.
9746  real(SRP), optional, dimension(:), intent(out) :: risk
9747  !> @param[out] risk_indexed is an optional array of predation risk estimates
9748  !! for each agent in the group. Risk for all agents that are
9749  !! not visible to the predator get zero risk. Note that this
9750  !! array is in the "raw" order, as the distances between the
9751  !! predator and the agents (not the normal order of the agents
9752  !! within the input group). Therefore, to translate this "raw"
9753  !! order it to the normal order, one requires the partial index
9754  !! array `index_dist`. The nature of the partial indexing
9755  !! dictates that only values with the index in the range from
9756  !! 1 to commondata::predator_risk_group_select_index_partial,
9757  !! i.e. the nearest neighbours of the predator make sense. All
9758  !! other index values are commondata::unknown.
9759  !!@warning The size of this array cannot be smaller than the partial
9760  !! indexing parameter
9761  !! commondata::predator_risk_group_select_index_partial
9762  real(SRP), optional, dimension(:), intent(out) :: risk_indexed
9763  !> @param[out] index_dist optional partial index array that shows the
9764  !! partial sorting of the agents with respect to their
9765  !! distance to the predator. Note that The nature of the
9766  !! partial indexing dictates that only values with the index
9767  !! in the range from
9768  !! 1 to commondata::predator_risk_group_select_index_partial,
9769  !! i.e. the nearest neighbours of the predator make sense. All
9770  !! other index values are commondata::unknown.
9771  !!@warning The size of this array cannot be smaller than the partial
9772  !! indexing parameter
9773  !! commondata::predator_risk_group_select_index_partial
9774  integer, optional, dimension(:), intent(out) :: index_dist
9775 
9776  ! PROCNAME is the procedure name for logging and debugging.
9777  character(len=*), parameter :: PROCNAME = &
9778  "(predator_capture_risk_calculate_fish_group)"
9779 
9780  ! Local copies of optionals
9781  integer :: time_step_model_here
9782  logical, dimension(size(prey_spatial)) :: is_freezing_here
9783 
9784  ! Temporary possible error status for sub-procedures
9785  logical :: err_flag
9786 
9787  !> ### Implementation details ###
9788  !> #### Alternative indexing schemes ####
9789  !! Two kinds of indexing of the output adjusted risk are possible to get:
9790  !! normal and "raw". The array in the normal order (`risk`) is provided
9791  !! with the same indexing as the input arrays (e.g. the array of the
9792  !! prey agents. The array in the "raw" order `risk_indexed` is arranged
9793  !! in the order of the distances between the predator and the prey agents
9794  !! (the first is the nearest). The latter "raw" array can be transformed
9795  !! to the normal ordering using the `index_dist` indexing array. See
9796  !! [partial array indexing](http://ahamodel.uib.no/doc/ar01s07.html#_subroutines_array_index_and_array_rank)
9797  !! procedure in HEDTOOLS.
9798  !! The table below presents an example of the two modes of ordering.
9799  !! "raw" | normal | Adjusted risk
9800  !! ------:|:------:|:-------------:
9801  !! 1 | 249 | 0.571031094
9802  !! 2 | 433 | 0.445715338
9803  !! 3 | 272 | 0.202977672
9804  !! 4 | 713 | 0.113853760
9805  !! 5 | 359 | 0.086924118
9806  !! 6 | 665 | 0.000000000
9807  !!
9808  !! An example of the code requesting the normal array of risks. This array
9809  !! is arranges in the same order as as the prey `prey_spatial`:
9810  !! @code
9811  !! call habitat_safe%predators(i)%risk_fish_group( &
9812  !! prey_spatial=proto_parents%individual, &
9813  !! prey_length=proto_parents%individual%body_length, &
9814  !! risk = group_risk_array )
9815  !! @endcode
9816  !!
9817  !! An example of the code requesting "raw" indexed array of risks along
9818  !! with the partial index array:
9819  !! @code
9820  !! call this_predator%risk_fish_group( &
9821  !! prey_spatial = this%individual%location(), &
9822  !! prey_length = this%individual%get_length(), &
9823  !! is_freezing = this%individual%freeze%is_executed(), &
9824  !! time_step_model = time_step_model_here, &
9825  !! risk_indexed = p_risk, &
9826  !! index_dist = prey_index )
9827  !! @endcode
9828  !!
9829  !> #### Notable local variables ####
9830  !> The calculations potentially involve looping over a huge array of
9831  !! potential prey agents, even though not all of them are at a close
9832  !! enough distance to the predator to have any risk. Only the agents that
9833  !! are within the visibility range (i.e. can be visible to the predator)
9834  !! count here out of the huge whole-population array.
9835  !! Finding the agents neighbouring to the predator benefits from
9836  !! partial indexing. Only a small small subarray within the input array
9837  !! of spatial prey agent objects is indexed and analysed. The maximum size
9838  !! of such a subarray is defined by the partial index size:
9839  !! commondata::predator_risk_group_select_index_partial (so all the work
9840  !! subarrays benefiting from partial indexing have such number of elements).
9841  !! - **dist_index** is the partial array sorting index, see `ARRAY_INDEX()`
9842  !! procedure from HEDTOOLS for more details. Note that This vector comes
9843  !! into the the_environment::neighbours() spatial indexing procedure and
9844  !! must have the full size of the spatial array being indexed. So its
9845  !! size here is equal to the `prey_spatial`array size.
9846  integer, dimension(size(prey_spatial)) :: dist_index
9847 
9848  !> - **risk_adjusted** is an array of predation risk estimates for each
9849  !! agent in the group. Risk for all agents that are not visible
9850  !! to the predator get null risk. This is the main array that is indexed
9851  !! exactly as the output.
9852  real(SRP), dimension(size(prey_spatial)) :: risk_adjusted
9853 
9854  !> - **risk_adjusted_indexed** is an array of predation risk estimates for
9855  !! each agent in the group. Risk for all agents that are not visible
9856  !! to the predator get null risk. This array that is indexed in the order
9857  !! of the distances between the predator and each of the nearest agents.
9858  !! Translation of the true index array order to this raw order requires
9859  !! the indexing array `dist_index`.
9860  real(SRP), dimension(PREDATOR_RISK_GROUP_SELECT_INDEX_PARTIAL) :: &
9861  risk_adjusted_indexed
9862 
9863  !! - **dist_neighbours** is the partial array of the distances of each of
9864  !! the agents from the predator. Note that this array contains valid
9865  !! values only for commondata::predator_risk_group_select_index_partial
9866  !! agents that are located in proximity of the predator.
9867  real(SRP), dimension(size(prey_spatial)) :: dist_neighbours
9868 
9869  !> - **risk_agent_is_visible** is a logical flag array indicating that
9870  !! the i-th prey agent in the visual field of the predator is visible
9871  !! (i.e. within the visual range). Array limited by the maximum index
9872  !! size commondata::predator_risk_group_select_index_partial.
9873  logical, dimension(PREDATOR_RISK_GROUP_SELECT_INDEX_PARTIAL) &
9874  :: risk_agent_is_visible
9875 
9876  !> - **risk_agent_visibility** is the visibility range of each prey agent
9877  !! in proximity of this predator. Array limited by the maximum index
9878  !! size commondata::predator_risk_group_select_index_partial.
9879  real(SRP), dimension(PREDATOR_RISK_GROUP_SELECT_INDEX_PARTIAL) &
9880  :: risk_agent_visibility
9881 
9882  !> - **risk_agent_rank** is the integer rank order of each agent in the
9883  !! visual field of the predator with respect to the distance from the
9884  !! predator. Only agents visible to the predator (distance < maximum
9885  !! visibility range) count. One potential caveat is that because the
9886  !! prey agents are stochastic, there can be cases when a tiny agent is
9887  !! the nearest to the predator, but its visibility range is very small,
9888  !! smaller than the distance to the predator. Such agent is not counted
9889  !! and has undefined (commondata::unknown) risk_agent_rank.
9890  integer, dimension(PREDATOR_RISK_GROUP_SELECT_INDEX_PARTIAL) &
9891  :: risk_agent_rank
9892 
9893  !> - **rank_visible** is the overall counter and the total number of
9894  !! "ranked" prey agents, i.e. those that are "visible".
9895  integer :: rank_visible
9896 
9897  !> - **risk_agent_baseline** is the baseline risk of predation for each
9898  !! of the prey agents in proximity of the predator. Array limited by
9899  !! the index size commondata::predator_risk_group_select_index_partial.
9900  real(SRP), dimension(PREDATOR_RISK_GROUP_SELECT_INDEX_PARTIAL) &
9901  :: risk_agent_baseline
9902 
9903  ! The weighting coefficient adjusting the baseline predation risk for
9904  ! predator confusion and predator dilution effects.
9905  real(SRP) :: dilution_weight
9906 
9907  ! Local counter and minimum of the dimensions
9908  integer :: i, min_dim
9909 
9910  ! File name for debug CSV data.
9911  character(len=:), allocatable :: tmp_debug_file
9912 
9913  !> #### Checks and preparations ####
9914  !> Initialise index and rank values. Uninitialised index arrays may result
9915  !! in invalid memory reference in `ARRAY_INDEX` (it is not safe by design,
9916  !! see notes on the HEDTOOLS
9917  !! [array indexing](http://ahamodel.uib.no/doc/ar01s07.html#_subroutines_array_index_and_array_rank)
9918  !! procedures.
9919  dist_neighbours = missing ; dist_index = unknown
9920  risk_agent_rank = unknown ; risk_agent_is_visible = .false.
9921  risk_agent_visibility = missing ; risk_agent_baseline = 0.0_srp
9922 
9923  risk_adjusted = 0.0_srp; risk_adjusted_indexed = 0.0_srp
9924 
9925  !> Check if the optional `is_freezing` parameter array is provided. If not,
9926  !! it is assumed that the prey agents are NOT freezing.
9927  if (present(is_freezing)) then
9928  is_freezing_here = is_freezing
9929  else
9930  is_freezing_here = .false.
9931  end if
9932 
9933  !> Check optional time step parameter. If unset, use global
9934  !! `commondata::global_time_step_model_current`.
9935  if (present(time_step_model)) then
9936  time_step_model_here = time_step_model
9937  else
9938  time_step_model_here = global_time_step_model_current
9939  end if
9940 
9941  !> Check the size of the input arrays of prey agents, prey length and
9942  !! freezing indicztors. The sizes of all three arrays must be equal.
9943  if ( .not. ( size(prey_spatial) == size(prey_length) .and. &
9944  size(prey_length) == size(is_freezing_here) ) ) then
9945  !> However, if they have different sizes, log warning, this may point to
9946  !! a potential bug.
9947  call log_msg( ltag_warn // "Unequal agent input arrays in " // procname &
9948  // ": " // tostr(size(prey_spatial)) // ", " // &
9949  tostr(size(prey_length)) // ", " // &
9950  tostr(size(is_freezing)) )
9951  end if
9952 
9953  !> #### Step 1 ####
9954  !> First, we get, up to the maximum order (fast *partial indexing*)
9955  !! of `commondata::predator_risk_group_select_index_partial`, neighbouring
9956  !! agents that are in proximity of this predator. Here we get
9957  !! **partial index** vector for the input array of objects: `dist_index`.
9958  call this%neighbours( neighbours = prey_spatial, &
9959  dist = dist_neighbours, &
9960  index_vector = dist_index, &
9961  rank_max = predator_risk_group_select_index_partial,&
9962  error_flag = err_flag )
9963 
9964  if (err_flag) call log_msg ( ltag_warn // procname // ": Got error flag" &
9965  // " from conspecific objects (neighbours) procedure.")
9966 
9967  !> If the `index_dist` optional parameter is present in the list of
9968  !! parameters, it returns the indexing vector `dist_index`.
9969  if (present(index_dist)) then
9970  index_dist = unknown
9971  min_dim = min(size(index_dist),size(dist_index))
9972  index_dist(1:min_dim) = dist_index(1:min_dim)
9973  end if
9974 
9975  !> #### Step 2 ####
9976  !> Get a vector of agents sorted by their distances from the predator and
9977  !! calculate the baseline risk of predation for each of the agents using
9978  !! the the_environment::predator::risk_fish() method. The size of the array
9979  !! is limited by the maximum partial rank index
9980  !! `commondata::predator_risk_group_select_index_partial`, so only this
9981  !! number of nearest agents is taken into account. The other are too far
9982  !! at this moment and have null risk anyway (probably).
9983  !!
9984  !! The boolean flag array
9985  !! `risk_agent_is_visible` indicating that this `i`-th agent is within the
9986  !! "neighbours" group is also set to TRUE.
9987  !!
9988  !! The baseline risk of predation is the risk not taking account of the
9989  !! predator dilution effect by the other agent's group members in
9990  !! proximity.
9991  rank_visible = 0
9992  ! @warning It is not possible to use do concurrent for this loop as
9993  ! impure procedures are called there, e.g. illumination (can be
9994  ! stochastic).
9995  do i = 1, predator_risk_group_select_index_partial
9996  !> - First, calculate the visibility (visual range) of each neighbouring
9997  !! prey agent.
9998  risk_agent_visibility(i) = &
9999  prey_spatial(dist_index(i))%visibility( &
10000  object_area = length2sidearea_fish(cm2m( &
10001  prey_length(dist_index(i)))), &
10002  time_step_model = time_step_model_here )
10003 
10004  !> - The risk of predation is non-zero only for those agents which are
10005  !! located from the predator at a distance smaller than their
10006  !! visibility distance, otherwise they fall outside of the visual
10007  !! range of the predator.
10008  if ( dist_neighbours(dist_index(i)) < risk_agent_visibility(i) ) then
10009  !> - Such agents are marked with the `risk_agent_is_visible` boolean
10010  !! vector value TRUE.
10011  risk_agent_is_visible(i) = .true.
10012  !> - Each of such visible agent is also assigned the consecutive rank,
10013  !! with the nearest agent having the rank 1 while the furtherst
10014  !! agent having the rank *N* (*N* is less than the maximum indexing
10015  !! rank commondata::predator_risk_group_select_index_partial).
10016  !! This is illustrated by the following plot.
10017  !! @image html img_doxygen_predator_grouprisk.svg "Calculation of predation risk in a group of prey agents"
10018  !! @image latex img_doxygen_predator_grouprisk.eps "Calculation of predation risk in a group of prey agents" width=14cm
10019  !! Here the prey agents P1, P3 and P4 are invisible to the
10020  !! predator (in the centre) because their visibility ranges are
10021  !! smaller that the distance towards the predator. P2 has the
10022  !! rank R=1 as it has the smallest distance to the predator among
10023  !! all the agents that are visible (P2, P5, P6). Notably, the
10024  !! agent P1 is invisible due to small body size that leads to
10025  !! very short visibility range (even though it is actually the
10026  !! closest to the predator!) and therefore zero baseline
10027  !! probability capture at the distance to the predator (a plot
10028  !! of the relationship between the distance and the baseline
10029  !! probability of capture is also overlaid at the agent P1).
10030  !! .
10031  rank_visible = rank_visible + 1
10032  risk_agent_rank(i) = rank_visible
10033  !> - For all visible agents, the baseline risk of predation is
10034  !! calculated using the the_environment::predator::risk_fish()
10035  !! method. The baseline risk of predation is the risk not taking
10036  !! account of the predator confusion and dilution effect by the
10037  !! other agent's group members in proximity.
10038  !> @note Note that the baseline risk is zero if the distance to
10039  !! the predator exceeds the visual range, it is done
10040  !! automatically in the_environment::predator::risk_fish().
10041  !!
10042  !> .
10043  risk_agent_baseline(i) = &
10044  this%risk_fish( prey_spatial = prey_spatial(dist_index(i)), &
10045  prey_length = prey_length(dist_index(i)), &
10046  is_freezing = is_freezing_here(dist_index(i)), &
10047  time_step_model = time_step_model_here )
10048  end if
10049  end do
10050 
10051  !> - The step 2 is finalised by checking if there were any prey agents
10052  !! visible to the predator. If none were visible, return from the
10053  !! procedure with the consequence that all adjusted risk values are
10054  !! equal to the initialisation value `risk_adjusted = 0.0`. This
10055  !! situation is also logged in the DEBUG mode.
10056  !! .
10057  if ( rank_visible==0 ) then
10058  call log_dbg(ltag_info // "This predator does not see any potential" // &
10059  " prey agents.", procname, modname)
10060  if (present(risk)) risk=risk_adjusted
10061  if (present(risk_indexed)) then
10062  risk_indexed = missing
10063  min_dim = min( size(risk_adjusted_indexed),size(risk_indexed) )
10064  risk_indexed(1:min_dim) = risk_adjusted_indexed(1:min_dim)
10065  end if
10066  return
10067  end if
10068 
10069  !> #### Step 3 ####
10070  !! Given that at the step 2 we have defined which of the prey agents in the
10071  !! group are actually visible to the predator, their rank order with
10072  !! respect to the distance, and the total number of such visible agents,
10073  !! this final step calculates the array of the adjusted risk values.
10074  !!
10075  !! This can be done in several ways:
10076  !! - ::adjust_risk_nonpar_noadjust() - no specific adjustment is made
10077  !! for predator confusion or dilution, adjusted risk equals the baseline.
10078  !! - ::adjust_risk_nonpar_fixed() - fixed predator confusion/dilution
10079  !! effect;
10080  !! - ::adjust_risk_dilute_nofirst() - all prey except the nearest have
10081  !! diluted risk, on average by *1/(N-1)*, the nearest agent has the
10082  !! baseline risk.
10083  !! - ::adjust_risk_dilute_all() - all prey except the closest have
10084  !! diluted risk, on average by *1/N*.
10085  !! .
10086  !! These procedires are implemented as subroutines within this main
10087  !! `the_environment::predator_capture_risk_calculate_fish_group()`.
10088  !call adjust_risk_nonpar_noadjust()
10090  !call adjust_risk_dilute_nofirst()
10091  !call adjust_risk_dilute_all()
10092 
10093  !> Calculate the raw indexed array of the risk that is intended to be
10094  !! returned (output) along with the partial index.
10095  do concurrent(i=1:predator_risk_group_select_index_partial)
10096  risk_adjusted_indexed(i) = risk_adjusted(dist_index(i))
10097  end do
10098 
10099  !> Finally, in the DEBUG mode also save debug data to CSV file.
10100  !! Below is an example data from one simulation using the fixed predation
10101  !! adjustment effect ::adjust_risk_nonpar_fixed().
10102  !!
10103  !! AGENT | VISIBLE | RANK | VISIBILITY | DISTANCE | RISK_BASE | RISK_ADJ
10104  !! -----:|:-------:|:-----:|:----------:|:---------:|:---------:|:--------:
10105  !! P1 | 1 | 1 | 297.934 | 148.458 | 0.597635 | 0.597635
10106  !! P2 | 1 | 2 | 310.591 | 238.575 | 0.375258 | 0.179290
10107  !! P3 | 0 | -9999 | *305.785* | *306.733* | 0 | 0
10108  !! P4 | 0 | -9999 | *296.327* | *309.332* | 0 | 0
10109  !! P5 | 1 | 3 | 338.934 | 316.221 | 0.192204 | 0.034169
10110  !! P6 | 0 | -9999 | *283.830* | *318.537* | 0 | 0
10111  !! P7 | 1 | 4 | 343.421 | 339.065 | 0 | 0
10112  !! P8 | 0 | -9999 | 311.020 | 366.969 | 0 | 0
10113  !! P9 | 0 | -9999 | 360.617 | 378.106 | 0 | 0
10114  !! P10 | 0 | -9999 | 312.113 | 395.021 | 0 | 0
10115  !! P11 | 0 | -9999 | 322.244 | 435.172 | 0 | 0
10116  !! P12 | 0 | -9999 | 335.271 | 442.236 | 0 | 0
10117  !! P13 | 0 | -9999 | 309.192 | 456.375 | 0 | 0
10118  !! P14 | 0 | -9999 | 269.335 | 477.448 | 0 | 0
10119  !! P15 | 0 | -9999 | 349.299 | 486.160 | 0 | 0
10120  !! P16 | 0 | -9999 | 273.446 | 501.956 | 0 | 0
10121  !! P17 | 0 | -9999 | 311.889 | 516.109 | 0 | 0
10122  !! P18 | 0 | -9999 | 361.142 | 533.081 | 0 | 0
10123  !! P19 | 0 | -9999 | 277.595 | 547.986 | 0 | 0
10124  !! P20 | 0 | -9999 | 267.683 | 561.175 | 0 | 0
10125  !!
10126  !> @note Note that the prey agents 3 and 4 are closer to the predator than
10127  !! 7, but are not visible due to small visibility range (small body
10128  !! sizes).
10129  if (is_debug) then
10130  tmp_debug_file = "debug_predator_dilution_" // &
10131  tostr(global_time_step_model_current) // "_" // &
10132  rand_string(label_length, label_cst,label_cen) // csv
10133 
10134  call csv_matrix_write ( reshape( &
10135  [conv_l2r(risk_agent_is_visible), & ! 1
10136  real(risk_agent_rank, kind=srp), & ! 2
10137  risk_agent_visibility, & ! 3
10138  dist_neighbours(dist_index( & ! 4
10139  1:predator_risk_group_select_index_partial)), &
10140  risk_agent_baseline, & ! 5
10141  risk_adjusted(dist_index( & ! 6
10142  1:predator_risk_group_select_index_partial))], &
10143  [predator_risk_group_select_index_partial, 6]), &
10144  tmp_debug_file, &
10145  ["IS_VISIBLE", "RANK ", "VISIBILITY", &
10146  "DIST ", "RISK_BASE ", "RISK_ADJ "] &
10147  )
10148  call log_dbg( ltag_info // "Saved debug predator dilution data" // &
10149  ", CSV file: " // tmp_debug_file, procname, modname )
10150  end if
10151 
10152  !> Finally, calculate the optional output arrays if the are requested as
10153  !! optional parameters: `risk` and `risk_indexed`
10154  if (present(risk)) risk=risk_adjusted
10155  if (present(risk_indexed)) then
10156  risk_indexed = 0.0_srp
10157  min_dim = min( size(risk_adjusted_indexed),size(risk_indexed))
10158  risk_indexed(1:min_dim) = risk_adjusted_indexed(1:min_dim)
10159  end if
10160 
10161  contains !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10162 
10163  !> Adjust the predation risk for confusion and dilution effects.
10164  !!
10165  !! In this version, the adjusted risk is equal to the baseline risk, i.e.
10166  !! there are no specific predator confusion or dilution effects.
10167  !!
10168  !! See the main container procedure
10169  !! `the_environment::predator_capture_risk_calculate_fish_group()`.
10171 
10172  !> ### Implementation details ###
10173  do concurrent(i = 1 : predator_risk_group_select_index_partial)
10174  if ( risk_agent_is_visible(i) ) then
10175  !> The adjusted risk of predation in this version is simplistic
10176  !! and just equals the baseline risk. So no adjustment is actually
10177  !! made.
10178  !!
10179  !> Note that the adjusted risk is calculated only for a small
10180  !! subarray within the potentially huge input array of spatial prey
10181  !! agents, commondata::predator_risk_group_select_index_partial
10182  !! maximum elements. All other values are nulls.
10183  risk_adjusted(dist_index(i))=risk_agent_baseline(i)
10184  end if
10185  end do
10186 
10187  end subroutine adjust_risk_nonpar_noadjust
10188 
10189  !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10190  !> Adjust the predation risk for confusion and dilution effects.
10191  !!
10192  !! This version is based on a fixed pattern linking the rank of the
10193  !! prey agent within the predator's visual field and the adjusted risk.
10194  !! However, the basic pattern scales independently on the specific
10195  !! number of prey agents that the predator sees.
10196  !!
10197  !! See the main container procedure
10198  !! `the_environment::predator_capture_risk_calculate_fish_group()`.
10200 
10201  !> PROCNAME is the procedure name for logging and debugging
10202  character(len=*), parameter :: PROCNAME = "(adjust_risk_nonpar_fixed)"
10203 
10204  !> ### Notable variables ###
10205  !> - **predator_risk_group_dilution_abscissa** is the abscissa for the
10206  !! non-parametric function that links the baseline unadjusted
10207  !! predation risk for any prey agent within a group and the risk
10208  !! estimate that takes account of predator confusion and predator
10209  !! dilution effects. The ordinate of the grid is defined by the
10210  !! parameter array commondata::predator_risk_group_dilution_ordinate.
10211  !! .
10212  real(SRP), dimension(size(PREDATOR_RISK_GROUP_DILUTION_ORDINATE)) &
10213  :: predator_risk_group_dilution_abscissa
10214 
10215  !> ### Implementation details ###
10216  !> The grid abscissa for the nonparametric function (see below)
10217  !! depends on the number visible prey agents within the visual field
10218  !! of the predator *N*. It is constructed as a 3-element array (the
10219  !! second point is a middle interval):
10220  !! [ 1.0, 1 + (*N-1*/2.0), *N* ].
10221  !! The ordinate of the grid is defined by the parameter array
10222  !! commondata::predator_risk_group_dilution_ordinate.
10223  predator_risk_group_dilution_abscissa = &
10224  [ 1.0_srp, &
10225  1.0_srp + (real(rank_visible - 1, srp)/2.0_srp), &
10226  real(rank_visible, SRP) ]
10227 
10228  do concurrent(i = 1 : predator_risk_group_select_index_partial)
10229  if ( risk_agent_is_visible(i) ) then
10230  !> - The estimate of the adjusted predation risk (@f$ R_{a} @f$)
10231  !! for each agent in proximity to this predator then depends
10232  !! on the rank order (@f$ r @f$) of the agent in within the
10233  !! visual field of the predator:
10234  !! @f[ R_{a} = R_{b} \cdot \varsigma(r) , @f] where
10235  !! @f$ R_{b} @f$ is unadjusted risk and @f$ \varsigma @f$ is a
10236  !! non-parametric weighting function that depends on the rank
10237  !! order @f$ r @f$ of the prey agent.
10238  !! The weighting function @f$ \varsigma(r) @f$ is in turn
10239  !! calculated as a nonlinear nonparametric function defined by
10240  !! the grid arrays:
10241  !! - `predator_risk_group_dilution_abscissa`
10242  !! - `commondata::predator_risk_group_dilution_ordinate`.
10243  !! .
10244  !!
10245  !! In this way, the first prey agent (closest distance) is
10246  !! subject to the predation risk equal to the baseline
10247  !! unadjusted risk, whereas the last prey agent (furtherest
10248  !! from the predator in the group) has fully diluted predation
10249  !! risk equal to zero. The agent with the middle rank in the
10250  !! group has the adjusted risk somewhere in between the
10251  !! unadjusted risk and null. Thus, the predator confusion and
10252  !! dilution effects are relative and depend on the agent's
10253  !! position with respect to the predator.
10254  !! @image html img_doxy_pred_risk_group_nonpar.svg "Nonparametric predator confusion/dilution factor"
10255  !! @image latex img_doxy_pred_risk_group_nonpar.eps "Nonparametric predator confusion/dilution factor" width=14cm
10256  !> Nonetheless, the pattern is independent on the specific
10257  !! number of prey agents in the group, e.g. it is the same for
10258  !! 3 and 9 agents.
10259  dilution_weight=ddpinterpol( predator_risk_group_dilution_abscissa,&
10260  predator_risk_group_dilution_ordinate, &
10261  real(risk_agent_rank(i), SRP) )
10262  !> Interpolation plots can be saved in the
10263  !! @ref intro_debug_mode "debug mode" using the
10264  !! command: `commondata::debug_interpolate_plot_save()`.
10265  !! @warning Involves **huge** number of plots, should
10266  !! normally be disabled.
10267  !! @warning This is **disabled** (commented out) here to allow
10268  !! parallel `do concurrent` construction. If debug
10269  !! plots are enabled, `do concurrent` has to be
10270  !! altered to normal `do`.
10271  !!
10272  !> .
10273  !> .
10274  !call debug_interpolate_plot_save( &
10275  ! grid_xx=predator_risk_group_dilution_abscissa, &
10276  ! grid_yy=PREDATOR_RISK_GROUP_DILUTION_ORDINATE, &
10277  ! ipol_value=real(rank_visible, SRP), algstr="DDPINTERPOL", &
10278  ! output_file="plot_debug_predator_dilution_" // &
10279  ! TOSTR(Global_Time_Step_Model_Current) // "_" // &
10280  ! RAND_STRING(LABEL_LENGTH, LABEL_CST,LABEL_CEN) &
10281  ! // PS )
10282 
10283  !> Note that the adjusted risk is calculated only for a small
10284  !! subarray within the potentially huge input array of spatial prey
10285  !! agents, commondata::predator_risk_group_select_index_partial
10286  !! maximum elements. All other values are nulls.
10287  risk_adjusted(dist_index(i))=risk_agent_baseline(i)*dilution_weight
10288  end if
10289  end do
10290 
10291  end subroutine adjust_risk_nonpar_fixed
10292 
10293  !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10294  !> Adjust the predation risk of a group of *N* prey agents for predator
10295  !! dilution effect.
10296  !!
10297  !! In this version, the nearest agent has a high risk equal to the
10298  !! baseline unadjusted risk whereas the other agents in the group have
10299  !! the risk diluted, on average, by their total number *N-1* (the
10300  !! nearest agent is excluded). The risks of the non-nearest agents
10301  !! still depend on the individual rank order. Whereas the average
10302  !! adjusted risk for all non-rank-1 agents is equal to *1/(N-1)*,
10303  !! each of the agents has specific risk. For the rank-2 agent it is
10304  !! @f$ R_{b} \cdot 2/(N-1) @f$ while for the furtherest agent the risk
10305  !! is zero: @f$ R_{b} \cdot 0.0 @f$.
10306  !!
10307  !! See the main container procedure
10308  !! `the_environment::predator_capture_risk_calculate_fish_group()`.
10310 
10311  !> ### Notable variables ###
10312  !> - **predator_risk_dilution** is the predator dilution weighting
10313  !! factor array. It is selected such that the average value over
10314  !! the whole array is *1/(N-1)* where *N* is the total number of
10315  !! prey agents in the group including the nearest agent.
10316  !! .
10317  real(SRP), allocatable, dimension(:) :: predator_risk_dilution
10318 
10319  !> ### Implementation details ###
10320  !> Calculate the array of dilution factor that is equally linearly
10321  !! spaced from 2/(N-1) to 0.0 (the furtherest agent). The average
10322  !! dilution factor for this group is therefore *1/(N-1)*.
10323  if (rank_visible == 1) then
10324  !> Nonetheless, if there is only one prey agent, its risk
10325  !! is calculated as the full baseline risk @f$ R_{b} @f$.
10326  risk_adjusted(dist_index(1)) = risk_agent_baseline(1)
10327  return
10328  elseif (rank_visible == 2) then
10329  !> If, on the other hand, only two prey agents are visible to
10330  !! the predator, the first (nearest, rank 1) gets the baseline
10331  !! risk @f$ R_{b} @f$ and the second @f$ R_{b} / 2.0 @f$.
10332  risk_adjusted(dist_index(1)) = risk_agent_baseline(1)
10333  risk_adjusted(dist_index(2)) = risk_agent_baseline(2) / 2.0_srp
10334  return
10335  else
10336  !> The `LINSPACE()` procedure for generating linearly equally
10337  !! spaced array from HEDTOOLS is used for calculation of the
10338  !! dilution factor array `predator_risk_dilution`.
10339  allocate(predator_risk_dilution(rank_visible-1))
10340  predator_risk_dilution = linspace( 2.0_srp/(rank_visible-1), &
10341  0.0_srp, &
10342  (rank_visible-1) )
10343  call log_dbg( ltag_info // "Predator dilution factor array: " // &
10344  tostr(predator_risk_dilution), procname, modname )
10345  end if
10346 
10347  do concurrent(i = 1 : predator_risk_group_select_index_partial)
10348  if ( risk_agent_is_visible(i) ) then
10349  if ( risk_agent_rank(i) == 1 ) then
10350  !> - The adjusted risk is equal to the baseline risk for the
10351  !! prey agent that has the rank one, i.e. the closest to the
10352  !! predator.
10353  risk_adjusted(dist_index(i)) = risk_agent_baseline(i)
10354  else
10355  !> - However, for all other agents in the group the adjusted
10356  !! risk is diluted by the remaining group size (i.e.
10357  !! excluding the nearest agent **N-1**).
10358  !! @image html img_doxy_pred_risk_group_dilute.svg "Adjustment of the predation risk by dilution factor"
10359  !! @image latex img_doxy_pred_risk_group_dilute.eps "Adjustment of the predation risk by dilution factor" width=14cm
10360  !! In the example plot above, the baseline risk for the
10361  !! nearest prey agent in a group of 6 is 0.7, it is unchanged.
10362  !! But the risk is diluted for all the remaining (rank>1)
10363  !! prey agents on average by *6-1=5*, even though it is still
10364  !! dependent on their rank by a linearly evenly spaced
10365  !! dilution factor `predator_risk_dilution` (the plot also
10366  !! for simplicity assumes the baseline risk is also
10367  !! @f$ R_{b}=0.7 @f$ and is identical for all agents; in
10368  !! real data, the linearly spaced factor is used as a weight
10369  !! for specific baseline risk values @f$ R_{b} @f$, so the
10370  !! pattern deviates from the perfect straight line).
10371  !! .
10372  risk_adjusted(dist_index(i)) = risk_agent_baseline(i) * &
10373  predator_risk_dilution(risk_agent_rank(i)-1)
10374 
10375  end if
10376  end if
10377  end do
10378 
10379  end subroutine adjust_risk_dilute_nofirst
10380 
10381  !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10382  !> Adjust the predation risk of a group of *N* prey agents for predator
10383  !! dilution effect.
10384  !!
10385  !! In this version, all prey agents in the group have
10386  !! the risk diluted, on average, by their total number *N*. The risks
10387  !! of the agents are not fixed and depend on the individual rank order.
10388  !! Whereas the average adjusted risk is equal to *1/(N)*,
10389  !! each of the agents has specific risk. For the rank-1 agent it is
10390  !! @f$ R_{b} \cdot 2/N @f$ while for the furtherest agent the risk
10391  !! is zero: @f$ R_{b} \cdot 0.0 @f$.
10392  !!
10393  !! See the main container procedure
10394  !! `the_environment::predator_capture_risk_calculate_fish_group()`.
10396 
10397  !> ### Notable variables ###
10398  !> - **predator_risk_dilution** is the predator dilution weighting
10399  !! factor array. It is selected such that the average value over
10400  !! the whole array is *1/N* where *N* is the total number of
10401  !! prey agents in the group including the nearest agent.
10402  !! .
10403  real(SRP), allocatable, dimension(:) :: predator_risk_dilution
10404 
10405  !> ### Implementation details ###
10406  !> Calculate the array of dilution factor that is equally linearly
10407  !! spaced from 2/N (nearest agent) to 0.0 (the furtherest agent).
10408  !! The average dilution factor for this group is therefore *1/N*.
10409  if (rank_visible == 1) then
10410  !> Nonetheless, if there is only one prey agent, its risk
10411  !! is calculated as the full baseline risk @f$ R_{b} @f$.
10412  risk_adjusted(dist_index(1)) = risk_agent_baseline(1)
10413  return
10414  elseif (rank_visible == 2) then
10415  !> If, on the other hand, only two prey agents are visible to
10416  !! the predator, they both get the same risk equal to a half of
10417  !! baseline @f$ R_{b} / 2.0 @f$.
10418  risk_adjusted(dist_index(1)) = risk_agent_baseline(1) / 2.0_srp
10419  risk_adjusted(dist_index(2)) = risk_agent_baseline(2) / 2.0_srp
10420  return
10421  else
10422  !> The `LINSPACE()` procedure for generating linearly equally
10423  !! spaced array from HEDTOOLS is used for calculation of the
10424  !! dilution factor array `predator_risk_dilution`.
10425  allocate(predator_risk_dilution(rank_visible))
10426  predator_risk_dilution = linspace( 2.0_srp/rank_visible, &
10427  0.0_srp, &
10428  rank_visible )
10429  call log_dbg( ltag_info // "Predator dilution factor array: " // &
10430  tostr(predator_risk_dilution), procname, modname )
10431  end if
10432 
10433  do concurrent(i = 1 : predator_risk_group_select_index_partial)
10434  if ( risk_agent_is_visible(i) ) then
10435  !> - The average adjusted risk is diluted by the group size *N*.
10436  !! However, individual prey agents have the adjusted risk
10437  !! values that are weighted by the linearly equally spaced
10438  !! dilution value `predator_risk_dilution`. Thus, even though
10439  !! individual values of the adjusted risk are ranked by the
10440  !! distance from the predator, their average values for the
10441  !! whole group is diluted by the group size *N*.
10442  !! .
10443  risk_adjusted(dist_index(i)) = risk_agent_baseline(i) * &
10444  predator_risk_dilution(risk_agent_rank(i))
10445  end if
10446  end do
10447 
10448  end subroutine adjust_risk_dilute_all
10449 
10451 
10452  !-----------------------------------------------------------------------------
10453  !> Calculate the visibility range of this predator. Wrapper to the
10454  !! the_environment::visual_range() function. This function calculates the
10455  !! distance from which this predator can be seen by a visual object
10456  !! (e.g. the agent).
10457  !! @warning The `visual_range` procedures use meter for units, this
10458  !! auto-converts to cm.
10459  !! @warning Cannot implement a generic function accepting also vectors of
10460  !! this objects as only elemental object-bound array functions are
10461  !! allowed by the standard. This function cannot be elemental, so
10462  !! passed-object dummy argument must always be scalar.
10463  function predator_visibility_visual_range(this, object_area, contrast, &
10464  time_step_model) result (visrange)
10465  class(predator), intent(in) :: this
10466  !> @param[in] object_area optional area of the spatial object, m.
10467  !! if not provided (normally), calculated from the
10468  !! the_environment::predator::body_size attribute of this
10469  !! predator object.
10470  real(srp), optional, intent(in) :: object_area
10471  !> @param[in] contrast is optional inherent visual contrast of the predator.
10472  !! the default contrast of all objects is defined by the
10473  !! commondata::preycontrast_default parameter.
10474  real(srp), optional, intent(in) :: contrast
10475  !> @param[in] optional time step of the model, if absent gets the current
10476  !! time step as defined by the value of
10477  !! `commondata::global_time_step_model_current`.
10478  integer, optional, intent(in) :: time_step_model
10479  !> @return The maximum distance from which this predator can be seen.
10480  real(srp) :: visrange
10481 
10482  ! Local copies of optionals
10483  real(srp) :: object_area_here, contrast_here
10484 
10485  ! Local variables
10486  real(srp) :: irradiance_agent_depth
10487  integer :: time_step_model_here
10488 
10489  !> ### Implementation details ###
10490  !! **Checks.** Check optional object area, the default value, if
10491  !! this parameter is absent, the area is calculated from the
10492  !! the_environment::predator::body_size attribute of the predator
10493  !! object with inline conversion to m. Note that the body side area
10494  !! of a fish object is calculated from the body length using the
10495  !! commondata::length2sidearea_fish() function.
10496  if (present(object_area)) then
10497  object_area_here = object_area
10498  else
10499  object_area_here = length2sidearea_fish( cm2m( this%body_size ) )
10500  end if
10501 
10502  !> Check optional `contrast` parameter. If unset, use global
10503  !! `commondata::preycontrast_default`.
10504  if (present(contrast)) then
10505  contrast_here = contrast
10506  else
10507  contrast_here = preycontrast_default
10508  end if
10509 
10510  !> Check optional time step parameter. If unset, use global
10511  !! `commondata::global_time_step_model_current`.
10512  if (present(time_step_model)) then
10513  time_step_model_here = time_step_model
10514  else
10515  time_step_model_here = global_time_step_model_current
10516  end if
10517 
10518  !> Calculate ambient illumination / irradiance at the depth of
10519  !! this predator object at the given time step using the
10520  !! the_environment::spatial::illumination() method.
10521  irradiance_agent_depth = this%illumination(time_step_model_here)
10522 
10523  !> Return visual range for (to detect) this predator using
10524  !! the_environment::visual_range() wrapper function.
10525  visrange = &
10526  m2cm( visual_range( irradiance = irradiance_agent_depth, &
10527  prey_area = object_area_here, &
10528  prey_contrast = contrast_here ) )
10529 
10531 
10532  !-----------------------------------------------------------------------------
10533  !> Calculates the average nearest neighbour distance amongst an array of
10534  !! spatial objects (class) by sampling `sample_size` of them. The sample size
10535  !! `sample_size` is optional, if not provided set to `SAMPLE_SIZE_DEFAULT=25`.
10536  !! @param spatial_objects An array of spatial class objects for which we
10537  !! calculate the average nearest neighbour distance.
10538  !! @param sample_size Optional sample size for the calculation.
10539  !! @returns Returns a (sample-based) estimate of the mean nearest neighbour
10540  !! distance among an array of the spatial objects.
10541  function distance_average(spatial_objects, sample_size) &
10542  result(mean_nndist)
10543 
10544  ! @param spatial_objects An array of spatial class objects for which we
10545  ! calculate the average nearest neighbour distance.
10546  class(spatial), dimension(:), intent(in) :: spatial_objects
10547 
10548  ! @param sample_size Optional sample size for the calculation.
10549  integer, optional, intent(in) :: sample_size
10550 
10551  ! @returns Returns a (sample-based) estimate of the mean nearest neighbour
10552  ! distance among an array of the spatial objects.
10553  real(srp) :: mean_nndist
10554 
10555  ! PROCNAME is the procedure name for logging and debugging (with MODNAME).
10556  character(len=*), parameter :: procname = "(distance_average)"
10557 
10558  !> ### Notable local variables ###
10559  !> - **SAMPLE_SIZE_DEFAULT** is the local default sample size
10560  !! - **SAMPLE_SIZE_WARN** is the minimum warning sample size that results
10561  !! in warning logging.
10562  integer, parameter :: sample_size_default=25, sample_size_warn=20
10563 
10564  !> - **MAX_ARRAY_DIMENSIONALITY**: the maximum dimensionality of the input
10565  !! array of
10566  !! spatial objects for doing full non-sampling based calculations.
10567  !! @details Maximum size of the input array of spatial objects for full
10568  !! element by element selection without random sampling. This
10569  !! maximum dimensionality can be obtained by solving this
10570  !! square equation: @f[ M = x^{2}+x , @f] where @f$ M @f$ is
10571  !! the maximum number of permutations. So, the maximum
10572  !! dimensionality of the input array of objects is
10573  !! @f[ \frac{\sqrt{4\cdot M + 1} + 1}{2} . @f] The value obtained
10574  !! is then rounded to the nearest whole integer using `nint`.
10575  !! @note Note that for the standard default `SAMPLE_SIZE_DEFAULT=25`,
10576  !! `MAX_ARRAY_DIMENSIONALITY` equals 6.
10577  !! .
10578  integer, parameter :: max_array_dimensionality = nint( &
10579  (sqrt(4.0_srp * sample_size_default + 1) + 1.0_srp) / 2.0_srp )
10580 
10581  ! Local variables.
10582  integer :: n ! Local sample size.
10583  integer :: i, j, k, perm ! Counters.
10584 
10585  ! Extra long local integers, needed to fit after exponentiation.
10586  integer(LONG) :: array_size ! Input spatial objects array size.
10587  integer(LONG) :: max_permutations ! Maximum number of permutations.
10588 
10589  ! Randomly sampled spatial object
10590  type(spatial) :: spatial_object_sampled
10591 
10592  ! Array of the original spatial objects excluding the above randomly
10593  ! sampled object
10594  type(spatial), dimension(size(spatial_objects)-1) :: spatial_all_other
10595 
10596  ! Nearest neighbour of the above randomly sampled object
10597  type(spatial) :: spatial_object_nearest_neighbour
10598 
10599  if (present(sample_size)) then
10600  n = sample_size
10601  else
10602  n = sample_size_default
10603  end if
10604 
10605  ! Set the array size from its dimension.
10606  array_size = size(spatial_objects)
10607 
10608  ! Maximum number of full cross pairwise permutations.
10609  ! @note Note that integers are declared as `kind=LONG` so they can fit
10610  ! the very big numbers resulting from the exponentiation if the
10611  ! number of objects is big.
10612  max_permutations = ((array_size**2)-array_size)
10613 
10614  !> ### Implementation details ###
10615  !> Check the array size. Big and small arrays are treated differently.
10616  array_size_treat: select case (array_size)
10617 
10618  !. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10619  !> If the array size is zero or one, there is no sense calculating
10620  !! average nearest neighbour distance, just return zero and log warning.
10621  case (0:1)
10622 
10623  call log_msg( ltag_warn // procname // ": too small array size " // &
10624  tostr(int(array_size)) // ", returned " // procname // &
10625  " value is ZERO." )
10626  mean_nndist = 0.0_srp
10627 
10628  !. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10629  !> If the array size is small enough, do full element by element
10630  !! selection and permutation. We do not then really use the
10631  !! `sample_size` (or `N`) parameter.
10632  case (2:max_array_dimensionality)
10633 
10634  call log_msg(ltag_info // procname // ": object array size is " // &
10635  "small enough for quick full object by object " // &
10636  "calculation; Random object sampling is NOT used." )
10637 
10638  ! Initialise the output mean distance with zero.
10639  mean_nndist = 0.0_srp
10640 
10641  !> Do full N x N permutations in such a case.
10642  !! @note Note that we here do full crossing rather than half cutting
10643  !! `j=i+1,array_size`. Nearest neighbour distance are **not**
10644  !! transitive.
10645  base_obj: do i = 1, array_size
10646 
10647  ! Get the first spatial object for which we calculate the distances
10648  call spatial_object_sampled%position( spatial_objects(i)%location() )
10649  ! Get an array of all other objects excluding the already sampled.
10650  k = 0
10651  do j = 1, array_size
10652  if (i /= j) then
10653  k = k + 1
10654  call spatial_all_other(k)%position(spatial_objects(j)%location())
10655  end if
10656  end do
10657 
10658  ! Then we find the nearest neighbour of the i-th object from the input
10659  ! array of spatial objects.
10660  spatial_object_nearest_neighbour = &
10661  spatial_object_sampled%nearest( spatial_all_other )
10662 
10663  ! Calculate the distance between the consecutive ith object
10664  ! and its nearest neighbour `spatial_object_nearest_neighbour` and
10665  ! update the overall sum.
10666  mean_nndist = mean_nndist + spatial_object_sampled%distance( &
10667  spatial_object_nearest_neighbour )
10668  end do base_obj
10669 
10670  ! Finally, calculate the mean distance over all these permutations.
10671  mean_nndist = mean_nndist / real(array_size, srp)
10672 
10673  !. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10674  !> In all other cases do random sampling from the input array of spatial
10675  !! objects and calculate sample-based average nearestneighbour distance.
10676  case default
10677 
10678  !> Set the maximum sample size not exceeding 1/2 of all possible
10679  !! permutations in case the input object array is relatively large.
10680  !! The default value 1/2 of all possible permutations may be quite big
10681  !! with a huge array, but the logic behind the decision is that the
10682  !! requested sample size was also big.
10683  !! @note Note that we have to **convert** `integer(LONG)` to the
10684  !! default `integer` using the intrinsic function `int` to make
10685  !! it acceptable for the `TOSTR` function from HEDTOOLS.
10686  !! Hopefully the final `max_permutations` would not be too huge.
10687  if (n > max_permutations / 2) then
10688  call log_msg( ltag_warn // procname // &
10689  ": requested sample size " // &
10690  tostr(n) // " exceeds 1/2 maximum number of permutations " // &
10691  tostr(int(max_permutations)) // ", use 1/2 of the latter.")
10692  n = max_permutations / 2
10693  end if
10694 
10695  !> If the requested sample size is very small, just do a warning. The
10696  !! logic behind NOT changing N to, e.g. `SAMPLE_SIZE_DEFAULT` is that
10697  !! if a small N was requested, this must have serious grounds, e.g.
10698  !! if the agent is basing its decision making process on an incomplete
10699  !! information. So the small value is left as small as requested.
10700  if (n < sample_size_warn) &
10701  call log_msg( ltag_warn // procname // ": requested sample size " // &
10702  tostr(n) // " is quite small, average value may be imprecise.")
10703 
10704 
10705  !> Initialise the output mean distance with zero.
10706  mean_nndist = 0.0_srp
10707 
10708  !> Do permutations, using `PERMUTE` named do-block:
10709  permute: do perm = 1, n
10710  !> - We sample a single random spatial objects from the available
10711  !! array.
10712  !! @note Note that we have to **convert** `integer(LONG)` to the
10713  !! default`integer` using the intrinsic function `int` to
10714  !! make it acceptable for the `RAND_I` function from HEDTOOLS.
10715  !! Hopefully the final `array_size` would not be too huge.
10716  i = rand_i(1,int(array_size))
10717  call spatial_object_sampled%position( spatial_objects(i)%location() )
10718 
10719  !> - Get an array of all other objects excluding the already sampled.
10720  !! @note There seems to be no class-safe way to do whole-array
10721  !! assignments of `SPATIAL` objects for high speed, e.g.
10722  !! using the index slice:
10723  !! `[ [(j, j=1,i-1)], [(j,j=i+1,array_size)] ]` as we use the
10724  !! type-bound function `location` and `position` to copy
10725  !! arays. So using the explicit old-fashioned do-loop here.
10726  k = 0
10727  do j=1, array_size
10728  if (i /= j) then
10729  k = k + 1
10730  call spatial_all_other(k)%position(spatial_objects(j)%location())
10731  end if
10732  end do
10733 
10734  !> - Then we find the nearest neighbour of the i-th object out
10735  !! from the input array of spatial objects.
10736  spatial_object_nearest_neighbour = &
10737  spatial_object_sampled%nearest( spatial_all_other )
10738 
10739  !> - Calculate the distance between the (randomly selected) ith object
10740  !! and its nearest neighbour `spatial_object_nearest_neighbour` and
10741  !! update the overall sum.
10742  !! .
10743  mean_nndist = mean_nndist + spatial_object_sampled%distance( &
10744  spatial_object_nearest_neighbour )
10745  end do permute
10746 
10747  !> Finally, calculate the mean distance over all these permutations.
10748  mean_nndist = mean_nndist / real(n, srp)
10749 
10750  end select array_size_treat
10751 
10752  end function distance_average
10753 
10754  !-----------------------------------------------------------------------------
10755  !-----------------------------------------------------------------------------
10756  !> @name Computational geometry backend: **polygon2D**
10757  !> Rudimentary *computational geometry* (geo_) procedures, based on
10758  !! *2D polygons* (poly2d) with fixed depth or depth ignored.
10759  !! @note Manually constructed using 2D *X* x *Y* vectors ignoring the *depth*
10760  !! dimension.
10761  !> @{
10762 
10763  !-----------------------------------------------------------------------------
10764  !> Calculates the minimum distance from a the_environment::spatial class
10765  !! object to a line segment delimited by two the_environment::spatial class
10766  !! endpoints in the 2D *XY* plane (the depth coordinate is ignored).
10767  !! (The algorithm is partially based on
10768  !! [this](https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment).)
10769  subroutine geo_poly2d_dist_point_to_section( point, sectp1, sectp2, &
10770  min_dist, point_segment )
10771  !> @param[in] point is the reference point to which the distance is
10772  !! calculated
10773  class(spatial), intent(in) :: point
10774  !> @param[in] sectp1 is the first end of the line segment.
10775  class(spatial), intent(in) :: sectp1
10776  !> @param[in] sectp2 is the second end point of the line segment.
10777  class(spatial), intent(in) :: sectp2
10778  !> @param[out] min_dist is the output minimum distance between the
10779  !! reference point and the line segment delimited by `sectp1`
10780  !! and `sectp2`.
10781  real(SRP), intent(out) :: min_dist
10782  !> @param[out] point_segment is the optional output coordinates of the
10783  !! nearest point **PN** on the **P1 P2** segment returned in the
10784  !! form of the_environment::spatial type object. But note that
10785  !! the third `depth` coordinate of this object is copied from
10786  !! the input `point` object.
10787  type(spatial), optional, intent(out) :: point_segment
10788 
10789  ! Local variables
10790  real(SRP) :: refval, dist2_p1_p2
10791 
10792  !> ### Implementation details ###
10793  !> A scheme of the calculation is presented on this figure:
10794  !! @image html img_doxygen_dist_segment.svg"Calculation of the distance from a spatial object to a line segment"
10795  !! @image latex img_doxygen_dist_segment.eps "Calculation of the distance from a spatial object to a line segment" width=14cm
10796  !! First, calculate the squared distance between the end points of
10797  !! the line segment **P1** and **P2** using the backend function
10798  !! the_environment::dist2_vector() that accepts vectors of arbitrary
10799  !! dimensionality: @f$ D^{2}(\mathbf{p1},\mathbf{p2}) @f$
10800  dist2_p1_p2 = dist2_vector( [sectp1%x, sectp1%y], [sectp2%x, sectp2%y] )
10801 
10802  !> - if the distance between the end points is zero, the line segment
10803  !! actually has zero length and the distance between the spatial object
10804  !! **P0** and the segment is trivial to determine:
10805  !! @f$ D_{min} = D(\mathbf{p_0},\mathbf{p_1}) = D(\mathbf{p_0},\mathbf{p_2}) @f$.
10806  !! .
10807  if ( dist2_p1_p2 < zero ) then
10808  min_dist = dist( [point%x, point%y], [sectp1%x, sectp1%y] )
10809  return
10810  end if
10811 
10812  !> Second, determine the reference value @f$ r @f$ (a normalised distance
10813  !! from **P1** to the closest point) that is calculated as follows:
10814  !! @f[ r = \frac{\left|\mathbf{p}_{0} - \mathbf{p}_{1}\right| \cdot \left|\mathbf{p}_{2} - \mathbf{p}_{1}\right|}{D^{2}(\mathbf{p_1},\mathbf{p_2})} , @f]
10815  !! where @f$ D^{2}(\mathbf{p_1},\mathbf{p_2}) @f$ is the distance between
10816  !! the end points **P1** and **P2**, and the numerator is the dot product
10817  !! of the vectors @f$ \left|\mathbf{p}_{0} - \mathbf{p}_{1}\right| @f$ and
10818  !! @f$ \left|\mathbf{p}_{2} - \mathbf{p}_{1}\right| @f$.
10819  refval = dot_product( [point%x,point%y]-[sectp1%x, sectp1%y], &
10820  [sectp2%x,sectp2%y]-[sectp1%x, sectp1%y] )/dist2_p1_p2
10821 
10822  !> - The @f$ r < 0 @f$ indicates that the projection of the spatial object
10823  !! **P0** onto the **P1 P2** line is located in front of the **P1**
10824  !! end, thus the minimum distance is calculated as
10825  !! @f[ D_{min} = D(\mathbf{p_0},\mathbf{p_1}) . @f] Also, the nearest
10826  !! point **PN** coordinates coincide with the *X* and *Y* coordinates of
10827  !! **P1**.
10828  if ( refval < 0.0_srp ) then
10829  min_dist = dist( [point%x, point%y], [sectp1%x, sectp1%y] )
10830  if (present(point_segment)) then
10831  point_segment = spatial( sectp1%x, sectp1%y, point%depth )
10832  end if
10833  !> - If @f$ r > 1 @f$, the projection of the spatial object **P0** on the
10834  !! line **P1 P2** is behind the **P2** end, so the minimum distance is
10835  !! @f[ D_{min} = D(\mathbf{p_0},\mathbf{p_2}) . @f] Also, the nearest
10836  !! point **PN** coordinates coincide with the *X* and *Y* coordinates of
10837  !! **P2**.
10838  else if ( refval > 1.0_srp ) then
10839  min_dist = dist( [point%x, point%y], [sectp2%x, sectp2%y] )
10840  if (present(point_segment)) then
10841  point_segment = spatial( sectp2%x, sectp2%y, point%depth )
10842  end if
10843  !> - In other cases, the spatial object **P0** projects to a specific point
10844  !! on the **P1 P2** line. The distance between the spatial object **P0**
10845  !! and this projection is calculated as:
10846  !! @f[ D_{min}= \frac{\left | (y_2-y_1)x_0-(x_2-x_2)y_0+x_2y_1-y_2x_1 \right |}{D^2(\mathbf{p_1},\mathbf{p_2})} , @f]
10847  !! where @f$ (x_0, y_0) @f$ are the coordinates of the spatial object
10848  !! **P0**, @f$ (x_1, y_1) @f$ are the coordinates of the point **P1**
10849  !! and @f$ (x_2, y_2) @f$ are the coordinates of the point **P2**.
10850  !! The nearest point **PN** coordinates are calculated as
10851  !! @f[ \left | \mathbf{p_1}+(\mathbf{p_2}-\mathbf{p_1}) r \right | . @f]
10852  !! .
10853  else
10854  min_dist = &
10855  abs( (sectp2%y-sectp1%y)*point%x - (sectp2%x-sectp1%x)*point%y + &
10856  sectp2%x*sectp1%y - sectp2%y*sectp1%x ) / sqrt( dist2_p1_p2 )
10857  if (present(point_segment)) then
10858  point_segment = spatial( sectp1%x + (sectp2%x-sectp1%x) * refval, &
10859  sectp1%y + (sectp2%y-sectp1%y) * refval, &
10860  point%depth )
10861  end if
10862  end if
10863 
10864  end subroutine geo_poly2d_dist_point_to_section
10865 
10866  !-----------------------------------------------------------------------------
10867  !> Calculates the minimum distance from a the_environment::spatial class
10868  !! object to a line segment delimited by two the_environment::spatial class
10869  !! endpoints in the 3D *XY* space.
10870  !! (The algorithm is partially based on
10871  !! [this](https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment).)
10872  subroutine geo_poly3d_dist_point_to_section( point, sectp1, sectp2, &
10873  min_dist, point_segment )
10874  !> @param[in] point is the reference point to which the distance is
10875  !! calculated
10876  class(spatial), intent(in) :: point
10877  !> @param[in] sectp1 is the first end of the line segment.
10878  class(spatial), intent(in) :: sectp1
10879  !> @param[in] sectp2 is the second end point of the line segment.
10880  class(spatial), intent(in) :: sectp2
10881  !> @param[out] min_dist is the output minimum distance between the
10882  !! reference point and the line segment delimited by `sectp1`
10883  !! and `sectp2`.
10884  real(SRP), intent(out) :: min_dist
10885  !> @param[out] point_segment is the optional output coordinates of the
10886  !! nearest point **PN** on the **P1 P2** segment returned in the
10887  !! form of the_environment::spatial type object.
10888  type(spatial), optional, intent(out) :: point_segment
10889 
10890  ! Local variables
10891  real(SRP) :: refval, dist2_p1_p2
10892 
10893  ! Local copy of optional
10894  type(spatial) :: point_segment_loc
10895 
10896  !> ### Implementation details ###
10897  !> A scheme of the calculation is presented on this figure:
10898  !! @image html img_doxygen_dist_segment.svg"Calculation of the distance from a spatial object to a line segment"
10899  !! @image latex img_doxygen_dist_segment.eps "Calculation of the distance from a spatial object to a line segment" width=14cm
10900  !! First, calculate the squared distance between the end points of
10901  !! the line segment **P1** and **P2** using the backend function
10902  !! the_environment::dist2_vector() that accepts vectors of arbitrary
10903  !! dimensionality: @f$ D^{2}(\mathbf{p1},\mathbf{p2}) @f$
10904  dist2_p1_p2 = dist2_vector( [sectp1%x, sectp1%y, sectp1%depth], &
10905  [sectp2%x, sectp2%y, sectp2%depth] )
10906 
10907  !> - if the distance between the end points is zero, the line segment
10908  !! actually has zero length and the distance between the spatial object
10909  !! **P0** and the segment is trivial to determine:
10910  !! @f$ D_{min} = D(\mathbf{p_0},\mathbf{p_1}) = D(\mathbf{p_0},\mathbf{p_2}) @f$.
10911  !! .
10912  if ( dist2_p1_p2 < zero ) then
10913  min_dist = dist( [point%x, point%y, point%depth], &
10914  [sectp1%x, sectp1%y, sectp1%depth] )
10915  return
10916  end if
10917 
10918  !> Second, determine the reference value @f$ r @f$ (a normalised distance
10919  !! from **P1** to the closest point) that is calculated as follows:
10920  !! @f[ r = \frac{\left|\mathbf{p}_{0} - \mathbf{p}_{1}\right| \cdot \left|\mathbf{p}_{2} - \mathbf{p}_{1}\right|}{D^{2}(\mathbf{p_1},\mathbf{p_2})} , @f]
10921  !! where @f$ D^{2}(\mathbf{p_1},\mathbf{p_2}) @f$ is the distance between
10922  !! the end points **P1** and **P2**, and the numerator is the dot product
10923  !! of the vectors @f$ \left|\mathbf{p}_{0} - \mathbf{p}_{1}\right| @f$ and
10924  !! @f$ \left|\mathbf{p}_{2} - \mathbf{p}_{1}\right| @f$.
10925  refval = dot_product( [point%x,point%y,point%depth]- &
10926  [sectp1%x,sectp1%y,sectp1%depth], &
10927  [sectp2%x,sectp2%y,sectp2%depth]- &
10928  [sectp1%x,sectp1%y,sectp1%depth] ) / &
10929  dist2_p1_p2
10930 
10931  !> - The @f$ r < 0 @f$ indicates that the projection of the spatial object
10932  !! **P0** onto the **P1 P2** line is located in front of the **P1**
10933  !! end, thus the minimum distance is calculated as
10934  !! @f[ D_{min} = D(\mathbf{p_0},\mathbf{p_1}) . @f]
10935  if ( refval < 0.0_srp ) then
10936  min_dist = dist( [point%x, point%y, point%depth], &
10937  [sectp1%x, sectp1%y, sectp1%depth] )
10938  if (present(point_segment)) then
10939  point_segment = spatial( sectp1%x, sectp1%y, sectp1%depth )
10940  end if
10941  !> - If @f$ r > 1 @f$, the projection of the spatial object **P0** on the
10942  !! line **P1 P2** is behind the **P2** end, so the minimum distance is
10943  !! @f[ D_{min} = D(\mathbf{p_0},\mathbf{p_2}) . @f]
10944  else if ( refval > 1.0_srp ) then
10945  min_dist = dist( [point%x, point%y, point%depth], &
10946  [sectp2%x, sectp2%y, sectp2%depth] )
10947  if (present(point_segment)) then
10948  point_segment = spatial( sectp2%x, sectp2%y, sectp2%depth )
10949  end if
10950  !> - In other cases, the spatial object **P0** projects to a specific point
10951  !! on the **P1 P2** line, that has these spatial coordinates:
10952  ! @f[
10953  ! \left\{\begin{matrix}
10954  ! x_1 + r (x_2 - x_1), \\
10955  ! y_1 + r (y_2 - y_1), \\
10956  ! z_1 + r (z_2 - z_1)
10957  ! \end{matrix}\right.
10958  ! @f]
10959  !> @image html img_doxygen_dist_point3d_formula_1.svg
10960  !! @image latex img_doxygen_dist_point3d_formula_1.eps "" width=14cm
10961  !! It is then trivial to calculate the distance between the spatial
10962  !! object **P0** and this projection point.
10963  !! .
10964  else
10965  point_segment_loc = &
10966  spatial( sectp1%x + (sectp2%x - sectp1%x) * refval, &
10967  sectp1%y + (sectp2%y - sectp1%y) * refval, &
10968  sectp1%depth + (sectp2%depth - sectp1%depth) * refval )
10969  min_dist = dist( [point%x, point%y, point%depth], &
10970  [point_segment_loc%x, point_segment_loc%y, &
10971  point_segment_loc%depth] )
10972  if (present(point_segment)) then
10973  point_segment = point_segment_loc
10974  end if
10975  end if
10976 
10977  end subroutine geo_poly3d_dist_point_to_section
10978 
10979  !-----------------------------------------------------------------------------
10980  !> Calculate a the_environment::spatial target with an offset.
10981  !!
10982  !! This function calculate the coordinates of a point **C** in between two
10983  !! objects, a reference object **A** and a target object **B**, but at a
10984  !! smaller distance with specific offset @f$ \Delta @f$ from the target
10985  !! **B**.
10986  !> @image html img_doxygen_dist_offset.svg
10987  !! @image latex img_doxygen_dist_offset.eps "" width=8cm
10988  function offset_dist(obj_a, obj_b, offset) result (obj_c)
10989  !> @param[in] obj_a reference spatial object (**A**)
10990  !> @param[in] obj_b target spatial object(**B**)
10991  class(spatial), intent(in) :: obj_a, obj_b
10992  !> @param[in] offset distance offset for the target object
10993  real(srp), intent(in) :: offset
10994  !> @return Returns the coordinates of the updated target object **C**
10995  !! that is located at a smaller distance from **A**, by the value
10996  !! of the offset parameter. If the distance between **A** and **B**
10997  !! is smaller that the `offset` value the returned error spatial
10998  !! object has commondata::missing coordinates. There is no error
10999  !! code variable with intent(out) to keep the function pure.
11000  type(spatial) :: obj_c
11001 
11002  ! distance between the **A** and **B**.
11003  real(srp) :: dist
11004 
11005  dist = obj_a%distance(obj_b)
11006 
11007  !> The coordinate @f$ x_c @f$ of the new target **C** are defined as:
11008  !! @f[ x_c = x_a + dist(A,B)-\Delta \cdot \frac {x_b-x_a} {dist(A,B)} , @f]
11009  !! where @f$ x_a, x_b @f$ are the *x* coordinates of the points **A**
11010  !! and **B** and @f$ dist(A,B) @f$ is the distance between **A** and **B**.
11011  !! The *y* and *depth* coordinates of the point **C** are defined in the
11012  !! same way.
11013  if (dist - offset > zero) then
11014  obj_c%x = &
11015  obj_a%x + (dist - offset) * (obj_b%x - obj_a%x) / dist
11016  obj_c%y = &
11017  obj_a%y + (dist - offset) * (obj_b%y - obj_a%y) / dist
11018  obj_c%depth = &
11019  obj_a%depth + (dist - offset) * (obj_b%depth - obj_a%depth) / dist
11020  else
11021  obj_c = spatial( missing, missing, missing )
11022  end if
11023 
11024  end function offset_dist
11025 
11026  !> @}
11027  ! end of computational geometry backend: **polygon2D**
11028 
11029 end module the_environment
Simple history stack function, add to the end of the stack. We need only to add components on top (en...
Definition: m_common.f90:5292
Convert cm to m.
Definition: m_common.f90:5306
Checks if a real number is near 0.0. Thus function can be used for comparing two real values like thi...
Definition: m_common.f90:5385
Logical function to check if a value is within a specific range, lower <= X <= upper.
Definition: m_common.f90:5362
Convert m to cm.
Definition: m_common.f90:5319
Force a value within the range set by the vmin and vmax dummy parameter values.
Definition: m_common.f90:5350
Interface to the procedure to assemble the global array of habitat objects the_environment::global_ha...
Definition: m_env.f90:797
Interface to the procedure to disassemble the global habitats objects array the_environment::global_h...
Definition: m_env.f90:806
Internal distance calculation backend engine.
Definition: m_env.f90:757
An alias for the the_environment::food_resources_collapse_global_object() method for joining food res...
Definition: m_env.f90:767
Calculate underwater background irradiance at specific depth.
Definition: m_env.f90:715
Calculate surface light intensity (that is subject to diel variation) for specific time step of the m...
Definition: m_env.f90:702
An alias to the_environment::food_resources_update_back_global_object() method to transfer (having be...
Definition: m_env.f90:780
Calculate visual range of predator using Dag Aksnes's procedures srgetr(), easyr() and deriv().
Definition: m_env.f90:752
Calculate visual range of predator using Dag Aksnes's procedures srgetr(), easyr() and deriv().
Definition: m_env.f90:738
subroutine adjust_risk_dilute_nofirst()
Adjust the predation risk of a group of N prey agents for predator dilution effect.
Definition: m_env.f90:10310
real(srp) function updated_position(coord_target, coord_object)
Calculate a Gaussian random updated coordinate for multidimensional Gaussian targeted random walk alo...
Definition: m_env.f90:3350
real(srp) function visibility_loc(depth)
This function calculates the visibility range of the spatial object at the depth given by the argumen...
Definition: m_env.f90:6217
subroutine adjust_risk_nonpar_fixed()
Adjust the predation risk for confusion and dilution effects.
Definition: m_env.f90:10200
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
real(srp) function visibility_loc_diff(depth)
This is a wrapper function that calculates the visibility range minus target minimum distance....
Definition: m_env.f90:6243
subroutine adjust_risk_dilute_all()
Adjust the predation risk of a group of N prey agents for predator dilution effect.
Definition: m_env.f90:10396
subroutine adjust_risk_nonpar_noadjust()
Adjust the predation risk for confusion and dilution effects.
Definition: m_env.f90:10171
type(spatial) function centroid_urandom(fixed_depth)
Make a random centroid with fixed depth bound within this environment.
Definition: m_env.f90:1597
pure subroutine qs_partition_size(A, marker)
qsort and qs_partition_ are the two parts of the recursive sort algorithm qs_partition_size is a pivo...
Definition: m_env.f90:7033
COMMONDATA – definitions of global constants and procedures.
Definition: m_common.f90:1497
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
real(srp), parameter, public food_item_size_default_cv
Coefficient of variation for Gaussian food items.
Definition: m_common.f90:2445
integer, parameter, public history_size_spatial
The size of the history for spatial moving objects, i.e. how many time steps positions to remember in...
Definition: m_common.f90:2264
character(len= *), parameter, public model_name
Model name for tags, file names etc. Must be very short. See Model descriptors.
Definition: m_common.f90:1938
character(len= *), parameter, public ps
Standard file extension for debug and other PostScript plots.
Definition: m_common.f90:1716
real(srp) function zeroin(ax, bx, f, tol)
This function calculates a zero of a function f(x) in the interval (ax,bx).
Definition: m_common.f90:6745
real(srp), parameter, public daylight_cv
Coefficient of variation for stochastic DAYLIGHT,.
Definition: m_common.f90:2517
integer, parameter, public srp
Definition of the standard real type precision (SRP).
Definition: m_common.f90:1551
real(srp), parameter, public pi
The PI number.
Definition: m_common.f90:1710
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
integer, parameter, public generations
Maximum number of generations in GA.
Definition: m_common.f90:2058
subroutine call_external(command, suppress_output, suppress_error, is_background_task, cmd_is_success, exit_code)
Call an external program using a command line. Wrapper to two alternative system shell calling intrin...
Definition: m_common.f90:7712
real(srp), parameter, public daylight
Maximum above-surface light intensity at midday, DAYLIGHT=500.0.
Definition: m_common.f90:2509
real(srp), parameter, public predator_attack_rate_cv
Coefficient of variation for a single predator attack among the whole population of stochastic predat...
Definition: m_common.f90:2364
integer, parameter, public label_length
The length of standard character string labels. We use labels for various objects,...
Definition: m_common.f90:1736
real(srp), parameter, public eggmortality_def
Default level of egg mortality in the habitat.
Definition: m_common.f90:2335
logical, public, protected is_zip_outputs
This parameter enables or disables post-processing compression of the data: if TRUE,...
Definition: m_common.f90:2014
real(srp), parameter, public food_item_capture_probability_min
The minimum probability of capture a food item, when the item is at a distance equal to the visual ra...
Definition: m_common.f90:2467
real(srp), parameter, public missing
Numerical code for missing and invalid real type values.
Definition: m_common.f90:1699
logical, parameter, public daylight_stochastic
Flag for stochastic daylight pattern (if TRUE) or deterministic sinusoidal (when FALSE)....
Definition: m_common.f90:2513
integer, parameter, public hrp
Definition of the high real precision (HRP). This real type kind is used in pieces where a higher lev...
Definition: m_common.f90:1556
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
real(hrp), parameter, public tolerance_high_def_hrp
Default value of high tolerance (low precision). This is the high commondata::hrp precision real....
Definition: m_common.f90:1690
real(srp), parameter, public predator_body_size
The body size of the predator. In this version all predators have the same body size set by this para...
Definition: m_common.f90:2357
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
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), parameter, public predator_attack_rate_default
Mean rate of a single predator attack.
Definition: m_common.f90:2360
real(srp), parameter, public zero
Some parameters should never be zero or below. In such cases they could be set to some smallest disti...
Definition: m_common.f90:1644
integer, parameter, public dielcycles
Number of days and nights in a lifespan, DIELCYCLES=500.
Definition: m_common.f90:2260
logical, parameter, public zip_outputs_background
This parameter defines if the output files are compressed in the background in the parallel mode or t...
Definition: m_common.f90:2019
integer, parameter, public lifespan
Number of time steps in the agent's maximum life length.
Definition: m_common.f90:2066
real(srp), parameter, public preycontrast_default
Inherent contrast of prey, CONTRAST =1.0.
Definition: m_common.f90:2527
elemental real(srp) function carea(R)
Calculate a circle area.
Definition: m_common.f90:5665
character(len= *), parameter, public ltag_warn
Definition: m_common.f90:1822
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
real(srp), parameter, public food_item_size_default
Default size of a single food item.
Definition: m_common.f90:2438
real(srp), parameter, public food_item_mean_size
The above is also the average size of a stochastic Gaussian food items.
Definition: m_common.f90:2442
real(srp), parameter, public beamatt
Beam attenuation coefficient of water (m-1),BEAMATT = 1.0.
Definition: m_common.f90:2522
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
character(len= *), parameter, private procname
PROCNAME is the procedure name for logging and debugging (with commondata::modname).
Definition: m_common.f90:1605
real(srp), parameter, public food_item_capture_probability
The baseline probability that the food item is captured. See the_neurobio::food_item_capture_probabil...
Definition: m_common.f90:2463
character(len= *), parameter, public csv
Standard data file extension for data output is now .csv.
Definition: m_common.f90:1713
real(srp), parameter, public preyarea_default
Area of prey (m2), PREYAREA = 3.E-6.
Definition: m_common.f90:2532
real(srp), parameter, public food_item_minimum_size
The minimum size of a food item. This is the "floor" in case the stochastically generated (e....
Definition: m_common.f90:2449
real(srp), parameter, public tolerance_high_def_srp
Default value of high tolerance (low precision). This is the standard commondata::srp precision real....
Definition: m_common.f90:1683
real(srp), parameter, public lightdecay
Vertical conservation of light, per depth (old code lightdecay=0.2).
Definition: m_common.f90:2554
character(len= *), parameter, public cmd_zip_output
This parameter defines the compression program that is executed to "zip" the data files if commondata...
Definition: m_common.f90:2029
real(srp), parameter, public other_risks_def
Default level of other mortality risks in the habitat.
Definition: m_common.f90:2324
logical, parameter, public false
Definition: m_common.f90:1632
Definition of high level file objects.
Definition: m_fileio.f90:110
@, public format_csv
Definition: m_fileio.f90:121
Definition of environmental objects.
Definition: m_env.f90:19
subroutine global_habitats_assemble(habitat_1, habitat_2, habitat_3, habitat_4, habitat_5, habitat_6, habitat_7, habitat_8, habitat_9, habitat_10, habitat_11, habitat_12, habitat_13, habitat_14, habitat_15, habitat_16, habitat_17, habitat_18, habitat_19, habitat_20, reindex)
Assemble the global habitats objects array the_environment::global_habitats_available from a list of ...
Definition: m_env.f90:8722
subroutine food_resource_migrate_move_items(this, max_depth, time_step_model)
This subroutine implements the migration of all the food items in the resource according to the plank...
Definition: m_env.f90:6661
elemental real(srp) function visual_range_fast(irradiance, prey_area, prey_contrast)
Wrapper for calculating visual range of a fish predator using the Dag Aksnes's procedures srgetr(),...
Definition: m_env.f90:5011
subroutine spatial_moving_randomwalk_gaussian_step_3d(this, meanshift, cv_shift, environment_limits)
Implements an optionally environment-restricted Gaussian random walk in 3D.
Definition: m_env.f90:2606
elemental subroutine food_item_set_iid(this, iid)
Set unique id for the food item object.
Definition: m_env.f90:6368
subroutine habitat_save_predators_csv(this, csv_file_name, is_success)
Save the predators with their characteristics into a CSV file.
Definition: m_env.f90:4378
integer, parameter dim_environ_corners
The number of corners for an environment object in the 2D X*x*Y plane.
Definition: m_env.f90:34
real(srp) function, private light_surface_stochastic_scalar(tstep, is_stochastic)
Calculate stochastic surface light at specific time step of the model. Light (surlig) is calculated f...
Definition: m_env.f90:5400
subroutine geo_poly3d_dist_point_to_section(point, sectp1, sectp2, min_dist, point_segment)
Calculates the minimum distance from a the_environment::spatial class object to a line segment delimi...
Definition: m_env.f90:10874
real(srp) function food_item_capture_probability_calc(this, distance, time_step_model)
Calculate the probability of capture of this food item by a predator agent depending on the distance ...
Definition: m_env.f90:5900
type(habitat), dimension(:), allocatable, public global_habitats_available
A list (array) of all the the_environment::habitat objects available to the agents....
Definition: m_env.f90:680
type(spatial) function environment_centre_coordinates_3d(this, nodepth)
Determine the centroid of the environment.
Definition: m_env.f90:4679
elemental subroutine, private deriv(r, F1, FDER, c, C0, Ap, Vc, Ke, Eb)
Derivation of equation for visual range of a predator. See the_environment::srgetr() for more details...
Definition: m_env.f90:5271
elemental logical function food_item_is_eaten_unavailable(this)
Logical check-indicator function for the food item being eaten and not available.
Definition: m_env.f90:6266
real(srp) function, private visual_range_scalar(irradiance, prey_area, prey_contrast)
Wrapper for calculating visual range of a fish predator using the Dag Aksnes's procedures srgetr(),...
Definition: m_env.f90:4732
real(srp) function, private light_depth_integer(depth, surface_light, is_stochastic)
Calculate underwater light at specific depth given specific surface light.
Definition: m_env.f90:5508
elemental real(srp) function spatial_get_current_pos_d_3d(this)
Get the current DEPTH position of a SPATIAL object.
Definition: m_env.f90:1886
elemental real(srp) function spatial_moving_self_distance_3d(this, from_history)
Calculate the Euclidean distance between the current and previous position of a single spatial movabl...
Definition: m_env.f90:2369
pure real(srp) function dist2_vector(cvector1, cvector2)
Calculate the squared distance between two N-dimensional points.
Definition: m_env.f90:5684
type(spatial) function environment_random_uniform_spatial_3d(this)
Generate a random spatial object with the uniform distribution within (i.e. bound to) this environmen...
Definition: m_env.f90:1240
elemental real(srp) function size2mass_food(radius)
Calculate the mass of a food item, the non-OO backend.
Definition: m_env.f90:6323
subroutine environment_whole_build_vector(this, min_coord, max_coord)
Create the highest level container environment. Set the size of the 3D environment container as two c...
Definition: m_env.f90:959
type(spatial) function, dimension(size(fixdep_array)) environment_random_uniform_spatial_vec_2d(this, fixdep_array)
Generate a vector of random spatial objects with the uniform distribution within (i....
Definition: m_env.f90:1333
type(spatial) function environment_random_uniform_spatial_2d(this, fixdepth)
Generate a random spatial object with the uniform distribution within (i.e. bound to) this environmen...
Definition: m_env.f90:1262
elemental integer function food_resource_get_abundance(this)
Get the number of food items in the food resource.
Definition: m_env.f90:6466
elemental type(spatial) function spatial_get_current_pos_3d_o(this)
Get the current spatial position of a SPATIAL object.
Definition: m_env.f90:1807
subroutine environment_get_nearest_point_in_outside_obj(this, outside_object, offset_into, point_spatial, point_dist)
Get the spatial point position within this environment that is nearest to an arbitrary spatial object...
Definition: m_env.f90:1634
pure type(spatial) function, dimension(:), allocatable spatial_stack2arrays(a, b)
Concatenate two arrays of the_environment::spatial objects a and b. This procedure uses array slices ...
Definition: m_env.f90:2224
real(srp) function, private light_depth_real(depth, surface_light, is_stochastic)
Calculate underwater light at specific depth given specific surface light.
Definition: m_env.f90:5583
subroutine food_resources_update_back_global_object(food_resource_collapsed, reindex)
Transfer the (having been modified) food resource objects from the single united object food_resource...
Definition: m_env.f90:8638
subroutine spatial_moving_randomwalk_gaussian_step_25d(this, meanshift_xy, cv_shift_xy, meanshift_depth, cv_shift_depth, environment_limits)
Implements an optionally environment-restricted Gaussian random walk in a "2.5 dimensions",...
Definition: m_env.f90:2690
type(spatial) function, dimension(num) environment_random_gaussian_spatial_2d(this, num, centroid, fixdepth, variance, variance_depth)
Generates a vector of random spatial object with Gaussian coordinates within (i.e....
Definition: m_env.f90:1453
elemental subroutine spatial_fix_position_3d_o(this, location)
Place spatial object into a 3D space, define the object's current coordinates.
Definition: m_env.f90:1777
elemental logical function spatial_check_located_within_3d(this, environment_limits)
Function to check if this spatial object is located within an area set by an environmental object (pa...
Definition: m_env.f90:3893
elemental real(srp) function mass2size_food(mass)
Calculate the size (radius) of a food item, a reverse function of the_environment::size2mass_food():
Definition: m_env.f90:6338
elemental real(srp) function environment_get_minimum_depth(this)
Get the minimum depth in this environment.
Definition: m_env.f90:1096
pure real(srp) function, dimension(dimensionality_default) spatial_get_current_pos_3d_v(this, vector)
Get the current spatial position of a SPATIAL object.
Definition: m_env.f90:1837
real(srp) function center_depth_sinusoidal(tstep, depth)
This function calculates the target depth for the sinusoidal vertical migration pattern of the food i...
Definition: m_env.f90:6903
elemental subroutine food_item_make(this, location, size, iid)
Make a single food item, i.e. place it into a specific position in the model environment space and se...
Definition: m_env.f90:5787
integer function spatial_get_nearest_id(this, neighbours, object)
Determine the nearest spatial object to this spatial object among an array of other spatial objects.
Definition: m_env.f90:4064
subroutine food_resource_replenish_food_items_all(this, replace)
Replenish and restore food resource. The food resource is replenished by substituting randomly select...
Definition: m_env.f90:6562
pure subroutine food_resource_make(this, label, abundance, locations, sizes)
Make food resource object. This class standard constructor.
Definition: m_env.f90:6418
elemental real(srp) function predator_get_body_size(this)
Accessor function for the predator body size (length).
Definition: m_env.f90:9387
elemental real(srp) function food_item_get_mass(this)
Calculate and get the mass of the food item.
Definition: m_env.f90:6350
subroutine spatial_neighbours_distances(this, neighbours, dist, index_vector, ranks, rank_max, error_flag)
Calculate the distances between this spatial object and an array of its neighbours....
Definition: m_env.f90:9219
real(srp) function, dimension(size(prey_area)), private visual_range_vector(irradiance, prey_area, prey_contrast_vect, prey_contrast)
Wrapper for calculating visual range of a fish predator using the Dag Aksnes's procedures srgetr(),...
Definition: m_env.f90:4900
subroutine predator_label_set(this, label)
Set label for the predator, if not provided, set it random.
Definition: m_env.f90:9371
subroutine food_resources_collapse(food_resource_collapsed, resource_1, resource_2, resource_3, resource_4, resource_5, resource_6, resource_7, resource_8, resource_9, resource_10, resource_11, resource_12, resource_13, resource_14, resource_15, resource_16, resource_17, resource_18, resource_19, resource_20, reindex, label)
Collapse several food resources into one. The collapsed resource can then go into the perception syst...
Definition: m_env.f90:7373
elemental subroutine, private srgetr(r, c, C0, Ap, Vc, Ke, Eb, IER)
Obtain visual range by solving the non-linear equation by means of Newton-Raphson iteration and deriv...
Definition: m_env.f90:5149
pure type(spatial) function, dimension(dim_environ_corners) environment_get_corners_2dxy(this, ref_depth, offset)
Get the corners of the environment in the 2D X Y plane. This is a very simplistic procedure that work...
Definition: m_env.f90:1122
elemental subroutine, private easyr(r, C0, Ap, Vc, Ke, Eb)
Obtain a first estimate of visual range by using a simplified expression of visual range....
Definition: m_env.f90:5238
elemental real(srp) function spatial_get_current_pos_y_3d(this)
Get the current Y position of a SPATIAL object.
Definition: m_env.f90:1873
pure real(srp) function dist_vector_nd(cvector1, cvector2)
Calculate distance between N-dimensional points. This is a function engine for use within other type ...
Definition: m_env.f90:5667
real(srp) function minimum_depth_visibility(target_range, depth_range_min, depth_range_max, object_area, object_contrast, time_step_model)
Find the depth at which the visibility of a spatial object becomes smaller than a specific distance v...
Definition: m_env.f90:6104
elemental real(srp) function dist_scalar(x1, x2, y1, y2, z1, z2)
Calculate distance between 3D or 2D points. This is a function engine for use within type bound proce...
Definition: m_env.f90:5634
elemental subroutine spatial_moving_clean_hstory_3d(this)
Create a new empty history of positions for spatial moving object. Assign all values to the MISSING v...
Definition: m_env.f90:2454
real(srp) function predator_capture_risk_calculate_fish(this, prey_spatial, prey_length, prey_distance, is_freezing, time_step_model, debug_plot_file)
Calculates the risk of capture of the fish with the spatial location defined by prey_spatial and the ...
Definition: m_env.f90:9432
type(spatial) function offset_dist(obj_a, obj_b, offset)
Calculate a the_environment::spatial target with an offset.
Definition: m_env.f90:10989
subroutine predator_capture_risk_calculate_fish_group(this, prey_spatial, prey_length, is_freezing, time_step_model, risk, risk_indexed, index_dist)
Calculates the risk of capture by a specific predator of an array of the fish agents with the spatial...
Definition: m_env.f90:9730
subroutine geo_poly2d_dist_point_to_section(point, sectp1, sectp2, min_dist, point_segment)
Calculates the minimum distance from a the_environment::spatial class object to a line segment delimi...
Definition: m_env.f90:10771
pure type(spatial) function, dimension(:), allocatable spatial_class_stack2arrays_locs(a, b)
Concatenate the location components of two arrays of the_environment::spatial class objects a and b....
Definition: m_env.f90:2281
elemental real(srp) function, private light_surface_deterministic(tstep)
Calculate deterministic surface light at specific time step of the model. Light (surlig) is calculate...
Definition: m_env.f90:5360
character(len=label_length) function habitat_name_get(this)
Return the name of the habitat.
Definition: m_env.f90:4343
subroutine global_habitats_disassemble(habitat_1, habitat_2, habitat_3, habitat_4, habitat_5, habitat_6, habitat_7, habitat_8, habitat_9, habitat_10, habitat_11, habitat_12, habitat_13, habitat_14, habitat_15, habitat_16, habitat_17, habitat_18, habitat_19, habitat_20, reindex)
Disassemble the global habitats objects array the_environment::global_habitats_available into separat...
Definition: m_env.f90:8993
type(spatial) function spatial_get_nearest_object(this, neighbours, number)
Determine the nearest spatial object to this spatial object among an array of other spatial objects.
Definition: m_env.f90:3993
elemental integer function food_item_get_iid(this)
Get the unique id of the food item object.
Definition: m_env.f90:6402
subroutine spatial_moving_corwalk_gaussian_step_25d(this, target, meanshift_xy, cv_shift_xy, meanshift_depth, cv_shift_depth, is_away, ci_lim, environment_limits, is_converged, debug_reps)
Implements an optionally environment-restricted correlated directional Gaussian random walk in 3D tow...
Definition: m_env.f90:3017
real(srp) function predator_visibility_visual_range(this, object_area, contrast, time_step_model)
Calculate the visibility range of this predator. Wrapper to the the_environment::visual_range() funct...
Definition: m_env.f90:10465
type(spatial) function environment_get_minimum_obj(this)
Function to get the minimum spatial limits (coordinates) of the environment.
Definition: m_env.f90:1067
subroutine migrate_food_vertical(habitats, time_step_model)
Migrate food items in a whole array of food resources. The array is normally the the_environment::glo...
Definition: m_env.f90:6835
type(spatial) function, dimension(num) environment_random_gaussian_spatial_3d(this, num, centroid, variance)
Generates a vector of random spatial object with Gaussian coordinates within (i.e....
Definition: m_env.f90:1372
elemental subroutine food_resource_sort_by_size(this, reindex)
Sort the food resource objects within the array by their sizes. The two subroutines below are a varia...
Definition: m_env.f90:6991
elemental real(srp) function spatial_self_distance_3d(this, from_history)
Calculate the Euclidean distance between the current and previous position of a single spatial object...
Definition: m_env.f90:2336
real(srp) function food_resource_calc_average_distance_items(this, n_sample)
Calculate the average distance between food items within a resource. e.g. to compare it with the agen...
Definition: m_env.f90:6529
subroutine reindex_food_resources(resource_1, resource_2, resource_3, resource_4, resource_5, resource_6, resource_7, resource_8, resource_9, resource_10, resource_11, resource_12, resource_13, resource_14, resource_15, resource_16, resource_17, resource_18, resource_19, resource_20)
Reset and reindex iids for an input list of several food resources. As the result of this subroutine ...
Definition: m_env.f90:7155
subroutine environment_build_unlimited(this)
Build an unlimited environment, with the spatial coordinates limited by the maximum machine supported...
Definition: m_env.f90:1006
pure real(srp) function vect_magnitude(vector)
Calculate the magnitude of an arbitrary N-dimensional vector. This is a raw vector backend.
Definition: m_env.f90:5700
type(environment) function environment_shrink_xy_fixed(this, shrink_value)
Return an environment object that is shrunk by a fixed value in the 2D XxY plane.
Definition: m_env.f90:1048
elemental logical function food_item_is_available(this)
Logical check-indicator function for the food item being available.
Definition: m_env.f90:6287
type(spatial) function environment_get_maximum_obj(this)
Function to get the maximum spatial limits (coordinates) of the environment.
Definition: m_env.f90:1083
pure integer function spatial_get_environment_in_pos(this, environments_array)
Identify in which environment from the input list this spatial agent is currently in....
Definition: m_env.f90:2041
elemental subroutine spatial_moving_repeat_position_history_3d(this)
Repeat (re-save) the current position into the positional history stack.
Definition: m_env.f90:2169
elemental subroutine spatial_moving_go_up(this, step)
The spatial moving object ascends, goes up the depth with specific fixed step size.
Definition: m_env.f90:2468
real(srp) function spatial_calc_irradiance_at_depth(this, time_step_model)
Calculate the illumination (background irradiance) at the depth of the spatial object at an arbitrary...
Definition: m_env.f90:1903
subroutine spatial_moving_corwalk_gaussian_step_3d(this, target, meanshift, cv_shift, is_away, ci_lim, environment_limits, is_converged, debug_reps)
Implements an optionally environment-restricted correlated directional Gaussian random walk in 3D tow...
Definition: m_env.f90:2773
integer, parameter, private dimensionality_default
Default dimensionality of the environment universe.
Definition: m_env.f90:28
elemental real(srp) function food_item_get_size(this)
Get the size component of the food item object.
Definition: m_env.f90:6305
elemental subroutine food_item_clone_assign(this, the_other)
Clone the properties of this food item to another food item.
Definition: m_env.f90:6384
subroutine rwalk3d_array(this, dist_array, cv_array, dist_all, cv_all, environment_limits, n_walks)
Perform one or several steps of random walk by an array of the_environment::spatial_moving class obje...
Definition: m_env.f90:3525
elemental subroutine spatial_make_missing(this)
Assign all commondata::missing` coordinates to the_environment::spatial object.
Definition: m_env.f90:1793
elemental real(srp) function dist3d(this, other)
This is a non-type-bound version of the distance calculation function.
Definition: m_env.f90:2307
real(srp) function spatial_visibility_visual_range_cm(this, object_area, contrast, time_step_model)
Calculate the visibility range of a spatial object. Wrapper to the the_environment::visual_range() fu...
Definition: m_env.f90:1940
pure subroutine food_resource_reset_iid_all(this, start_iid)
Reset individual iid for the food resource. Individual iids must normally coincide with the array ord...
Definition: m_env.f90:7086
subroutine spatial_moving_fix_position_3d_v(this, x, y, depth)
Place spatial movable object into a 3D space, define the object's current coordinates,...
Definition: m_env.f90:2122
real(srp) function distance_average(spatial_objects, sample_size)
Calculates the average nearest neighbour distance amongst an array of spatial objects (class) by samp...
Definition: m_env.f90:10543
elemental real(srp) function spatial_get_current_pos_x_3d(this)
Get the current X position of a SPATIAL object.
Definition: m_env.f90:1860
type(food_resource) function food_resources_collapse_global_object(reindex, label)
Join food resources into a single global food resource out of the global array the_environment::globa...
Definition: m_env.f90:7916
real(srp) function habitat_get_risk_mortality_egg(this)
Get the egg mortality risk associated with this habitat.
Definition: m_env.f90:4366
subroutine spatial_fix_position_3d_s(this, x, y, depth)
Place spatial object into a 3D space, define the object's current coordinates.
Definition: m_env.f90:1757
elemental subroutine predator_make_init(this, body_size, attack_rate, position, label)
Initialise a predator object.
Definition: m_env.f90:9330
pure type(spatial) function, dimension(size(this%food)) food_resource_locate_3d(this)
Get the location object array (array of SPATIAL objects) of a food resource object.
Definition: m_env.f90:6507
elemental logical function environment_check_located_within_3d(this, check_object)
Check if a spatial object is actually within this environment.
Definition: m_env.f90:1209
subroutine food_resources_update_back(food_resource_collapsed, resource_1, resource_2, resource_3, resource_4, resource_5, resource_6, resource_7, resource_8, resource_9, resource_10, resource_11, resource_12, resource_13, resource_14, resource_15, resource_16, resource_17, resource_18, resource_19, resource_20, reindex)
Transfer back the resulting food resources into their original objects out from a collapsed object fr...
Definition: m_env.f90:8045
elemental real(srp) function environment_get_maximum_depth(this)
Get the maximum depth in this environment.
Definition: m_env.f90:1107
elemental subroutine spatial_create_empty(this)
These are public access functions, but probably we don't need to allow public access to functions ins...
Definition: m_env.f90:940
elemental subroutine spatial_moving_create_3d(this)
Create a new spatial moving object. Initially it has no position, all coordinate values are MISSING o...
Definition: m_env.f90:2436
real(srp) function food_item_visibility_visual_range(this, object_area, contrast, time_step_model)
Calculate the visibility range of this food item. Wrapper to the the_environment::visual_range() func...
Definition: m_env.f90:6038
pure subroutine food_resource_destroy_deallocate(this)
Delete and deallocate food resource object. This class standard destructor.
Definition: m_env.f90:6488
elemental subroutine food_item_create(this)
Create a single food item at an undefined position with default size.
Definition: m_env.f90:5752
subroutine save_dynamics(maxdepth, csv_file_name, is_success)
Save diagnostics data that shows the dynamics of the light and the average depth of the food items,...
Definition: m_env.f90:4461
elemental character(len=label_length) function food_resource_get_label(this)
Get the label of the this food resource.
Definition: m_env.f90:6477
type(spatial) function, dimension(num) environment_random_uniform_spatial_vec_3d(this, num)
Generate a vector of random spatial objects with the uniform distribution within (i....
Definition: m_env.f90:1291
subroutine spatial_moving_dirwalk_gaussian_step_25d(this, target, meanshift_xy, cv_shift_xy, meanshift_depth, cv_shift_depth, environment_limits)
Implements an optionally environment-restricted directional Gaussian random walk in "2....
Definition: m_env.f90:3416
real(srp) function, dimension(size(tstep)), private light_surface_stochastic_vector(tstep, is_stochastic)
Calculate stochastic surface light at specific time step of the model.
Definition: m_env.f90:5448
subroutine habitat_make_init(this, coord_min, coord_max, label, otherrisks, eggmortality, predators_number, loc_predators, food_abundance, loc_food, sizes_food)
Make an instance of the habitat object (an environment superset).
Definition: m_env.f90:4120
elemental logical function spatial_check_located_below(this, check_object)
Logical function to check if the argument spatial object(s) (check_object) is (are) located below the...
Definition: m_env.f90:3926
pure type(spatial_moving) function, dimension(:), allocatable spatial_moving_stack2arrays(a, b)
Concatenate two arrays of the_environment::spatial_moving objects a and b. This procedure uses array ...
Definition: m_env.f90:2251
elemental real(srp) function spatial_distance_3d(this, other)
Calculate the Euclidean distance between two spatial objects. This is a type-bound function.
Definition: m_env.f90:2192
logical function food_item_capture_success_stochast(this, prob)
Stochastic outcome of this food item capture by an agent. Returns TRUE if the food item is captured.
Definition: m_env.f90:5841
subroutine rwalk_food_step(habitats)
Perform a random walk of food items in a whole array of food resources. The array is normally the the...
Definition: m_env.f90:6875
subroutine rwalk25d_array(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 an array of the_environment::spatial_moving class obje...
Definition: m_env.f90:3677
elemental subroutine food_item_disappear(this)
Make the food item "disappear" and take the "eaten" state, i.e. impossible for consumption by the age...
Definition: m_env.f90:6254
subroutine food_resource_save_foods_csv(this, csv_file_name, is_success)
Save characteristics of food items in the resource into a CSV file.
Definition: m_env.f90:6930
elemental real(srp) function dist2step(average_distance, dimensionality)
Calculate the unit step along a single coordinate axis given the average distance between any two poi...
Definition: m_env.f90:5726
elemental logical function spatial_check_located_above(this, check_object)
Logical function to check if the argument spatial object(s) (check_object) is (are) located above the...
Definition: m_env.f90:3958
character(len= *), parameter, private modname
Definition: m_env.f90:25
elemental subroutine spatial_moving_go_down(this, step)
The spatial moving object decends, goes down the depth with specific fixed step size.
Definition: m_env.f90:2531
real(srp) function habitat_get_risk_mortality(this)
Get the mortality risk associated with this habitat.
Definition: m_env.f90:4354
elemental real(srp) function predator_get_attack_rate(this)
Accessor function for the predator attack rate.
Definition: m_env.f90:9397
subroutine spatial_moving_dirwalk_gaussian_step_3d(this, target, meanshift, cv_shift, environment_limits)
Implements an optionally environment-restricted directional Gaussian random walk in 3D towards a targ...
Definition: m_env.f90:3292
elemental subroutine spatial_moving_fix_position_3d_o(this, location)
Place spatial movable object into a 3D space, define the object's current coordinates,...
Definition: m_env.f90:2145
subroutine food_resource_rwalk_items_default(this)
Perform a random walk step for all food items within the food resource. The walk is performed with th...
Definition: m_env.f90:6770
subroutine environment_whole_build_object(this, min_coord, max_coord)
Create the highest level container environment. Set the size of the 3D environment container as two c...
Definition: m_env.f90:989
FILE_HANDLE is the basic file handle object. It provides an unitary object oriented interface for ope...
Definition: m_fileio.f90:148
Definition of the overall environment. Environment is a general container for all habitats,...
Definition: m_env.f90:277
Definition of a single food item. Food item is a spatial object that has specific location in space....
Definition: m_env.f90:371
Definition of the super-type FOOD resource type. This is a superclass, several sub-classes can be def...
Definition: m_env.f90:434
Definition of the environment habitat HABITAT object. There can potentially be of several types of ha...
Definition: m_env.f90:555
Definition of the PREDATOR objects. Predator is a moving agent that hunts on the evolving AHA agents ...
Definition: m_env.f90:506
Definition of a movable spatial object. It extends the the_environment::spatial object,...
Definition: m_env.f90:168
Definition of a spatial object. Spatial object determines the position of the agent,...
Definition: m_env.f90:50