FishMet: Fish feeding and appetite model, OPEN EDITION  0.1
FishMet: Fish feeding and appetite model, OPEN EDITION
m_env.f90 (16074)
Go to the documentation of this file.
1 !> @file m_env.f90
2 !! The FishMet Model: Definitions for the environment.
3 
4 ! ------------------------------------------------------------------------------
5 ! Copyright notice:
6 ! Copyright (c) 2015-2024 Sergey Budaev & Ivar Rønnestad
7 ! FishMet model code is distributed according to GNU AGPL v3, see LICENSE.md
8 ! ------------------------------------------------------------------------------
9 
10 
11 !> Module defining the environment
12 module environ
13 
14  use realcalc
15  use commondata
16 
17  implicit none
18 
19  !> Defines the food item
20  type, public :: food_item
21  !> The mass of a single food item
22  real(srp) :: mass
23  contains
24  !> Initialise a single food item.
25  !! See environ::food_item_init().
26  procedure, public :: init => food_item_init
27  !> Copy one food item characteristic(s) to another item.
28  !! See environ::food_item_copy_assign_to().
29  procedure, public :: copy => food_item_copy_assign_to
30  !> Schedule the provision of the food items.
31  !! See environ::food_item_schedule_feed().
32  procedure, public :: schedule => food_item_schedule_feed
33 
34  end type food_item
35 
36  contains
37 
38  !> Initialise the food item with its parameters
39  elemental subroutine food_item_init(this, mass)
40 
41  class(food_item), intent(inout) :: this
42  real(srp), optional, intent(in) :: mass
43 
44  if (present(mass)) then
45  this%mass = mass
46  else
47  this%mass = missing
48  end if
49 
50  end subroutine food_item_init
51 
52  !> Copy `what_to_copy` object to `this` (target) object.
53  !! @note The code is actually equivalent to
54  !! `this%mass = food_item_in%mass` , but if the base class
55  !! includes several components (e.g. energetic value), an explicit
56  !! `copy` method is beneficial.
57  !! @warning Note that making this an assignment overload (`assignment(=)`)
58  !! is dangerous as it will then apply to any higher-order
59  !! hierarchy objects. These will therefore not copy all data
60  !! components on normal `=` assignment, resulting in cryptic
61  !! errors.
62  elemental subroutine food_item_copy_assign_to(this, what_to_copy)
63  class(food_item), intent(inout) :: this
64  class(food_item), intent(in) :: what_to_copy
65 
66  this%mass = what_to_copy%mass
67 
68  end subroutine food_item_copy_assign_to
69 
70  !> Schedule the provision of food items depending on the time step.
71  !! It returns a pointer to the food item class object if food is given or
72  !! null pointer otherwise.
73  !! @note The function cannot be pure due to pointer assignment (C1283).
74  impure function food_item_schedule_feed( this, time_step, is_provided ) &
75  result( food_given )
76  !> Any derivative of the ::food_item object
77  class(food_item), intent(in), target :: this
78  !> Optional time step, if not provided the global
79  !! commondata::global_time_step is used
80  integer(LONG), optional, intent(in) :: time_step
81  !> Optional output indicator that shows if the food item is provided.
82  logical, optional, intent(out) :: is_provided
83 
84  class(food_item), pointer :: food_given
85 
86  integer(LONG) :: time_step_loc
87 
88  nullify(food_given)
89 
90  if (present(time_step)) then
91  time_step_loc = time_step
92  else
93  time_step_loc = global_time_step
94  end if
95 
96  !> TODO: The rule for scheduling the food items is here
97  food_intrvl: if ( global_interval_food_pattern(time_step_loc) ) then
98 
99  !! @note that each food item is given every `60/Global_Food_Input_Rate`
100  !! seconds.
101  rate: if ( mod(time_step_loc,rate2int(global_food_input_rate))==0 ) then
102  if(present(is_provided)) is_provided = .true.
103  food_given => this
104  else rate
105  if (present(is_provided)) is_provided = .false.
106  food_given => null()
107  end if rate
108 
109  else food_intrvl
110 
111  if (present(is_provided)) is_provided = .false.
112  food_given => null()
113 
114  end if food_intrvl
115 
116  end function food_item_schedule_feed
117 
118  !> Initialise and set up the global food provisioning pattern array
119  !! commondata::global_interval_food_pattern given the day:night pattern
120  !! commondata::global_hours_daytime_feeding. Note that feeding can occur
121  !! only during the daytime.
122  impure subroutine food_provisioning_pattern_init( max_steps )
123  integer(LONG), intent(in) :: max_steps
124 
125  integer(LONG) :: i
126 
127  logical, allocatable, dimension(:) :: is_day_now, is_offset
128 
129  if (.not. allocated(global_interval_food_pattern) ) then
130  allocate( global_interval_food_pattern(max_steps) )
131  else
132  deallocate (global_interval_food_pattern )
133  allocate( global_interval_food_pattern(max_steps) )
134  end if
135 
136  allocate(is_day_now(max_steps))
137  allocate(is_offset(max_steps))
138 
139  do i = 1, max_steps
142 
143  is_day_now(i) = is_day(i) ! is_day is calculated by ::is_day() function
144 
145  if ( is_day_now(i) .and. .not. is_offset(i) ) then
150  else
151  global_interval_food_pattern(i) = .false.
152  end if
153  end do
154 
155  end subroutine food_provisioning_pattern_init
156 
157  !> Read the food provisioning pattern from a CSV file. This allows to get
158  !! any arbitrary pattern of food input.
159  !! The input CSV file encodes data by *minute*. There, *1* or any other
160  !! non-zero value means that food is provided during the minute interval,
161  !! *0* means that food is not provided. The file can describe by-minute
162  !! pattern for 24 h or more if `is_single` is `TRUE`. The pattern is
163  !! then propagated for all 24 h periods described by the `max_steps`
164  !! parameter.
165  impure subroutine food_provisioning_get_file( max_steps, csv_file, column, &
166  is_single )
167  use csv_io , only: csv_matrix_read
168  !> Maximum number of the time steps of the model, normally
169  !! commondata::global_run_model_hours().
170  integer(LONG), intent(in) :: max_steps
171  !> Input file name
172  character(len=*), intent(in) :: csv_file
173  !> Optional column number in the CSV data file. Default is column 1.
174  integer, optional, intent(in) :: column
175  !> Optional parameter flag that determines if the feeding schedule
176  !! pattern from the data file `csv_file` is applied only once for the
177  !! first 24 h period (TRUE) or propagated to each 24 h period (FALSE).
178  !! The default value is FALSE
179  logical, optional, intent(in) :: is_single
180 
181  real(srp), allocatable, dimension(:,:) :: raw_data_csv, raw_data_csv_get
182  logical, allocatable, dimension(:) :: pattern_sec
183 
184  ! logical food provision pattern in minutes for 24 h data
185  ! Note: must be converted and propagated to seconds
186  logical, allocatable, dimension(:) :: pattern_min
187 
188  logical :: is_success, is_single_loc
189  integer :: column_loc
190 
191  integer(LONG) :: i, j, k, l, fill_n
192 
193  ! Number of seconds in a day, 24h x 60 min x 60 sec
194  integer(LONG), parameter :: day_sec = 24 * hour
195 
196  ! Number of minutes in hour = 60
197  integer, parameter :: minutes_hour = 60
198 
199  integer(LONG) :: get_rows, get_cols, max_rows
200 
201  if ( present(is_single) ) then
202  is_single_loc = is_single
203  else
204  is_single_loc = .false.
205  end if
206 
207  raw_data_csv_get = csv_matrix_read( csv_file, csv_file_status=is_success, &
208  missing_code=0.0_srp )
209 
210  get_rows = size(raw_data_csv_get,1)
211  get_cols = size(raw_data_csv_get,2)
212 
213  if (is_single_loc) then
214  max_rows = get_rows
215  else
216  max_rows = min(get_rows, day_sec)
217  end if
218 
219  if (present(column)) then
220  column_loc = min( column, size(raw_data_csv_get,2) )
221  else
222  column_loc = 1
223  end if
224 
225  ! An adjusted data matrix is obtained with the row length equal
226  ! to the maximum number of seconds per day. Then data from the raw
227  ! CSV input matrix is copied to the adjusted data matrix,
228  ! Note that all extra datain the adjusted matrix is commondata::missing
229  ! while the timing column is 0, i.e. no feed provided.
230  if (is_single_loc) then
231  allocate(raw_data_csv(get_rows, get_cols))
232  else
233  allocate(raw_data_csv(day_sec, get_cols))
234  end if
235 
236  raw_data_csv = missing
237  raw_data_csv(:,column_loc) = 0.0_srp
238  raw_data_csv(1:max_rows,:) = raw_data_csv_get(1:max_rows,:)
239  deallocate(raw_data_csv_get)
240 
241  if (.not. allocated(global_interval_food_pattern) ) then
242  allocate( global_interval_food_pattern(max_steps) )
243  else
244  deallocate (global_interval_food_pattern )
245  allocate( global_interval_food_pattern(max_steps) )
246  end if
247 
248  allocate(pattern_sec(max_steps))
249  allocate ( pattern_min( 24 * minutes_hour ) )
250 
252  pattern_sec = .false.
253  pattern_min = .false.
254 
255  fill_n = size(raw_data_csv,1)
256 
257  if (is_single_loc) then
258  l = 0
259  do i = 1, min(fill_n, max_steps/minute)
260  if (raw_data_csv(i,column_loc) /= 0.0_srp ) then
261  do k=1, minute
262  l = l + 1
263  pattern_sec(l) = .true.
264  end do
265  else
266  do k=1, minute
267  l = l + 1
268  end do
269  end if
270  end do
271  global_interval_food_pattern = pattern_sec
272  else
273  do i=1, min( size(raw_data_csv, 1), size(pattern_min) )
274  if ( raw_data_csv(i,column_loc) == 0.0_srp ) then
275  pattern_min(i) = .false.
276  else
277  pattern_min(i) = .true.
278  end if
279  end do
280  j = 1 ! sec to MINUTES_HOUR = 60
281  k = 1 ! min to size(pattern_min)
282  do i = 1, max_steps
283  global_interval_food_pattern(i) = pattern_min(k)
284  if ( j >= minutes_hour ) then
285  k = k + 1 ! next minute
286  j = 0
287  end if
288  if ( k > size(pattern_min) ) then
289  k = 1
290  end if
291  j = j + 1
292  end do
293  end if
294 
295  end subroutine food_provisioning_get_file
296 
297  !> Read the food provisioning pattern from a CSV file in raw format by s.
298  !! This allows to get any arbitrary pattern of food input. Unlike the
299  !! ::food_provisioning_get_file() subroutine, this procedure accepts the
300  !! food provisioning data in the raw format equal to the model time step
301  !! i.e. s (second). There, *1* or any other non-zero value means that food
302  !! is provided during the time step while *0* means that food is not
303  !! provided. The file can any duration within or exceeding 24 h period. Any
304  !! data over 24 h are accepted only if `is_single` is `TRUE`.
305  impure subroutine food_provisioning_get_raw( max_steps, csv_file, column, &
306  is_single )
307  use csv_io , only: csv_matrix_read
308  !> Maximum number of the time steps of the model, normally
309  !! commondata::global_run_model_hours().
310  integer(LONG), intent(in) :: max_steps
311  !> Input file name
312  character(len=*), intent(in) :: csv_file
313  !> Optional column number in the CSV data file. Default is column 1.
314  integer, optional, intent(in) :: column
315  !> Optional parameter flag that determines if the feeding schedule
316  !! pattern from the data file `csv_file` is applied only once for the
317  !! first 24 h period (TRUE) or propagated to each 24 h period (FALSE).
318  !! The default value is FALSE
319  logical, optional, intent(in) :: is_single
320 
321  real(srp), allocatable, dimension(:,:) :: raw_data_csv, raw_data_csv_get
322  logical, allocatable, dimension(:) :: pattern_sec
323 
324  logical :: is_success, is_single_loc
325  integer :: column_loc
326 
327  integer(LONG) :: i, j, fill_n
328 
329  ! Number of seconds in a day, 24h x 60 min x 60 sec
330  integer(LONG), parameter :: day_sec = 24 * hour
331 
332  integer(LONG) :: get_rows, get_cols, max_rows
333 
334  if ( present(is_single) ) then
335  is_single_loc = is_single
336  else
337  is_single_loc = .false.
338  end if
339 
340  raw_data_csv_get = csv_matrix_read( csv_file, csv_file_status=is_success, &
341  missing_code=0.0_srp )
342 
343  get_rows = size(raw_data_csv_get,1)
344  get_cols = size(raw_data_csv_get,2)
345 
346  if (is_single_loc) then
347  max_rows = get_rows
348  else
349  max_rows = min(get_rows, day_sec)
350  end if
351 
352  if (present(column)) then
353  column_loc = min( column, size(raw_data_csv_get,2) )
354  else
355  column_loc = 1
356  end if
357 
358  ! An adjusted data matrix is obtained with the row length equal
359  ! to the maximum number of seconds per day. Then data from the raw
360  ! CSV input matrix is copied to the adjusted data matrix,
361  ! Note that all extra datain the adjusted matrix is commondata::missing
362  ! while the timing column is 0, i.e. no feed provided.
363  if (is_single_loc) then
364  allocate(raw_data_csv(get_rows, get_cols))
365  else
366  allocate(raw_data_csv(day_sec, get_cols))
367  end if
368 
369  raw_data_csv = missing
370  raw_data_csv(:,column_loc) = 0.0_srp
371  raw_data_csv(1:max_rows,:) = raw_data_csv_get(1:max_rows,:)
372  deallocate(raw_data_csv_get)
373 
374  if (.not. allocated(global_interval_food_pattern) ) then
375  allocate( global_interval_food_pattern(max_steps) )
376  else
377  deallocate (global_interval_food_pattern )
378  allocate( global_interval_food_pattern(max_steps) )
379  end if
380 
381  allocate(pattern_sec(max_steps))
382 
384  pattern_sec = .false.
385 
386  fill_n = min(size(raw_data_csv,1), max_steps)
387 
388  if (is_single_loc) then
389  do i = 1, fill_n
390  if (raw_data_csv(i,column_loc) /= 0.0_srp ) then
391  pattern_sec(i) = .true.
392  end if
393  end do
394  else
395  j=0
396  do i=1, max_steps
397  j = j + 1
398  if (j > day_sec) j = 1
399  if (raw_data_csv(j,column_loc) /= 0.0_srp ) then
400  pattern_sec(i) = .true.
401  end if
402  end do
403  end if
404 
405  global_interval_food_pattern = pattern_sec
406 
407  end subroutine food_provisioning_get_raw
408 
409  !> Schedule two intervals N1 = food given, steps, N2 = gap, steps
410  !! @verbatim
411  !! N1 N2 N1 N2
412  !! +----+----------+----+----------+ ...
413  !! ^check_n
414  !! @endverbatim
415  elemental function schedule_2_int(check_n, int1, int2) result ( is_in_n1 )
416  !> Value to be checked, normally **raw time steps**
417  integer(LONG), intent(in) :: check_n
418  !> The length of the interval when the food is provided, **raw steps**
419  !> @warning If intervals are set in min, multiply argument by
420  !! commondata::minute
421  integer, intent(in) :: int1
422  !> The length of the interval when the food id not provided, **raw steps**
423  !> @warning If intervals are set in min, multiply argument by
424  !! commondata::minute
425  integer, intent(in) :: int2
426  !> Returns TRUE if `check_n` is within the `int1` interval,
427  !! otherwise FALSE.
428  logical :: is_in_n1
429 
430  integer :: mod_n
431 
432  mod_n = mod( check_n, (int1+int2) )
433 
434  if ( mod_n < int1 ) then
435  is_in_n1 = .true.
436  else
437  is_in_n1 = .false.
438  end if
439 
440  end function schedule_2_int
441 
442  !> Determine if the time step corresponds to day (returns true) or night
443  !! (returns false).
444  !! @note that it is a wrapper to ::schedule_2_int().
445  elemental function is_day(time_step) result (is_day_now)
446  !> Optional time step, if absent determine is it day or night for
447  !! commondata::global_time_step
448  integer(LONG), intent(in), optional :: time_step
449  logical :: is_day_now
450 
451  integer(LONG) :: time_step_loc
452 
453  if (present(time_step)) then
454  time_step_loc = time_step
455  else
456  time_step_loc = global_time_step
457  end if
458 
459  ! Adjustment for day_starts_hour:
460  !
461  ! @verbatim
462  !
463  ! H=5
464  ! +-----*----------+ +----------------+ +----
465  ! | D | N | D | N |
466  ! + +--------+ +--------+
467  !
468  ! H=5-day_starts_hour
469  ! +*---------------+ +----------------+
470  ! | D | N | D | N
471  ! ----+ +--------+ +--------+
472  ! ^
473  ! day_starts_hour
474  !
475  ! @endverbatim
476  if (time_step_loc <= global_day_starts_hour * hour ) then
477  is_day_now = .false.
478  else
479  is_day_now = schedule_2_int( time_step_loc - global_day_starts_hour * hour, &
482  end if
483 
484  end function is_day
485 
486 
487 end module environ
This module defines global parameters and general-level computational utilities.
Definition: m_common.f90:13
real(srp), public global_food_input_rate
Standard rate of food item input, per minute. Configuration file example:
Definition: m_common.f90:618
integer, dimension(2), public global_interval_food_param
Parameters of the food provisioning pattern:
Definition: m_common.f90:629
integer, public global_day_starts_hour
Default hour at which the "daytime" is normally started. This means, in particular,...
Definition: m_common.f90:204
integer, public global_run_model_feed_offset
The offset (delay) to start feeding at the beginning of the simulation, Note that the parameter file ...
Definition: m_common.f90:211
integer, parameter, public minute
Definition: m_common.f90:61
integer, parameter, public hour
Global time constants, number of sec in hour and min.
Definition: m_common.f90:61
integer(long), public global_time_step
Global variable that defines the current time step.
Definition: m_common.f90:86
logical, dimension(:), allocatable, public global_interval_food_pattern
Global food provisioning pattern, logical TRUE/FALSE for each time step.
Definition: m_common.f90:632
elemental integer(long) function rate2int(rate_min)
The function converts rate per min to interval in seconds.
Definition: m_common.f90:1363
integer, public global_hours_daytime_feeding
Default duration of the day time in hours, night duration is defined as 24 - ::global_hours_day_feedi...
Definition: m_common.f90:219
Module defining the environment.
Definition: m_env.f90:12
impure subroutine food_provisioning_pattern_init(max_steps)
Initialise and set up the global food provisioning pattern array commondata::global_interval_food_pat...
Definition: m_env.f90:123
elemental subroutine food_item_copy_assign_to(this, what_to_copy)
Copy what_to_copy object to this (target) object.
Definition: m_env.f90:63
impure subroutine food_provisioning_get_file(max_steps, csv_file, column, is_single)
Read the food provisioning pattern from a CSV file. This allows to get any arbitrary pattern of food ...
Definition: m_env.f90:167
elemental logical function is_day(time_step)
Determine if the time step corresponds to day (returns true) or night (returns false).
Definition: m_env.f90:446
elemental logical function schedule_2_int(check_n, int1, int2)
Schedule two intervals N1 = food given, steps, N2 = gap, steps.
Definition: m_env.f90:416
elemental subroutine food_item_init(this, mass)
Initialise the food item with its parameters.
Definition: m_env.f90:40
impure subroutine food_provisioning_get_raw(max_steps, csv_file, column, is_single)
Read the food provisioning pattern from a CSV file in raw format by s. This allows to get any arbitra...
Definition: m_env.f90:307
impure class(food_item) function, pointer food_item_schedule_feed(this, time_step, is_provided)
Schedule the provision of food items depending on the time step. It returns a pointer to the food ite...
Definition: m_env.f90:76
Defines the food item.
Definition: m_env.f90:20