The AHA Model  Revision: 12809
Reference implementation 04 (HEDG02_04)
m_fileio.f90
Go to the documentation of this file.
1 !> @file m_fileio.f90
2 !! File objects for the AHA Model.
3 !! @author Sergey Budaev <sergey.budaev@uib.no>
4 !! @author Jarl Giske <jarl.giske@uib.no>
5 !! @date 2016-2017
6 
7 !-------------------------------------------------------------------------------
8 ! $Id: m_fileio.f90 6468 2017-11-24 13:26:50Z sbu062 $
9 !-------------------------------------------------------------------------------
10 
11 !-------------------------------------------------------------------------------
12 !> @brief Definition of high level file objects
13 !> @section file_io_module FILE_IO module
14 !> This module defines input and output to external files in various formats.
15 !! The main format for output files for numerical data is CSV. It is useful
16 !! for one-dimensional vectors and two-dimensional matrices. More complex
17 !! data structures can be output in other formats (to be implemented) such as
18 !! binary, XML or HDF. This module provides an unitary object-oriented
19 !! interface to all file types and objects.
20 !!
21 !! **Current status:** Only [CSV](http://ahamodel.uib.no/doc/ar01s08.html) and
22 !! plain text (TXT) files are implemented so far. And even here, only the file
23 !! object is implemented in the object oriented style, record (string) is used
24 !! exactly as in the [CSV_IO](http://ahamodel.uib.no/doc/ar01s08.html) in HEDTOOLS.
25 !!
26 !! @subsection file_io_module_csv CSV format
27 !! Notably, standard HEDTOOLS whole array procedure
28 !! [CSV_MATRIX_WRITE](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_matrix_write)
29 !! can save a single vector or 2D matrix into a separate CSV file.
30 !!
31 !! **Example:** @anchor file_handle_example_code
32 !! @code
33 !! ! File handle object identifies a specific file.
34 !! type(FILE_HANDLE) :: test_file
35 !! ! String variable that is used to build each record (row)
36 !! ! it must fit the whole record into its length;
37 !! character(len=:), allocatable :: record_string
38 !!
39 !! ! Open the file for writing
40 !! call test_file%open_write( "test_file.csv", FORMAT_CSV )
41 !!
42 !! ! Write optional header.
43 !! call test_file%header_write("Header is the first line of the file")
44 !!
45 !! ! The current record must be cleared before it is built. Note that
46 !! ! if the record string is allocatable, it must be whole blank.
47 !! record_string=repeat(" ", max_len)
48 !! ! Append values to the first record. The first record is
49 !! ! here the variable names.
50 !! call csv_record_append( record_string, ["No", "V1", "V2", "V3"] )
51 !! ! Write this record 'record_string' physically to the disk.
52 !! call test_file%record_write(record_string)
53 !!
54 !! ! Now write the data beyond the first row.
55 !! do i=1, 100
56 !! record_string=repeat(" ", max_len) ! clean each new record string
57 !! ! Append values of various types to the current
58 !! ! record string 'record_string'
59 !! call csv_record_append( record_string, i )
60 !! call csv_record_append( record_string, [ 1.1, 2.2 ] )
61 !! call csv_record_append( record_string, 3.3 )
62 !! ! Once the record is built, write it to the disk.
63 !! call test_file%record_write(record_string)
64 !! end do
65 !!
66 !! ! Close file at the end.
67 !! call test_file%close()
68 !! @endcode
69 !! See code of the the_population::population_save_data_all_agents_csv() for
70 !! another example of writing data to CSV file. But note that these codes use
71 !! the standard non-object-oriented procedures from HEDTOOLS, not these
72 !! wrappers.
73 !!
74 !! @subsection file_io_module_txt Plain text (TXT) format
75 !! The file handler object file_io::file_handle defined in this module can be
76 !! easily used to write arbitrary plain text files. Here is an example.
77 !!
78 !! **Example:**
79 !! @code
80 !! ! File handle object identifies a specific file.
81 !! type(FILE_HANDLE) :: test_file
82 !!
83 !! ! Open the file for writing
84 !! call test_file%open_write( "test_file.txt", FORMAT_TXT )
85 !!
86 !! ! Write an arbitrary row of data to the file, character text string:
87 !! call test_file%record_write("This is a test string to output")
88 !!
89 !! ! Standard Fortran intrinsic 'write' can be combined with the 'get_unit()'
90 !! ! accessor function for unit.
91 !! write( test_file%get_unit(), * ) "Raw string 1, success=", test_file%is_success()
92 !! write( test_file%get_unit(), * ) "Raw string 2, success=", test_file%is_success()
93 !! write( test_file%get_unit(), * ) "Raw string 3, success=", test_file%is_success()
94 !!
95 !! ! Close file at the end.
96 !! call test_file%close()
97 !! @endcode
98 !!
99 !! Thus, the wrappers implemented in this unit allow to use unitary file
100 !! handler object to work with specific files, even though they are not fully
101 !! object oriented. Using a single file handle is simpler and more
102 !! understandable than different Fortran file identifiers (file name, unit).
103 !!
104 !! The logger commondata::logger_init() is the standard normal method to
105 !! report everything in the model during the runtime. Therefore, using
106 !! separate plain text file(s) to output any reports should be very rare if
107 !! needed at all.
108 !> ### Accessibility of objects ###
109 !! By default, all objects in `FILE_IO` are *private*.
110 module file_io
111  use commondata
112  use csv_io
113  implicit none
114 
115  private ! All objects here are private except explicitly stated public.
116 
117  !> @name Public enumeration constants defining supported file types
118  !! @{
119  !> Define the file types that are supported by this module.
120  enum, bind(C)
122  end enum
123 
124  public :: undefined, format_csv, format_txt
125  !> @}
126 
127  !> `FILE_HANDLE` is the basic file handle object. It provides an
128  !! unitary object oriented interface for operations with any supported file
129  !! types.
130  !!
131  !! **Example:**
132  !! @code
133  !! type(FILE_HANDLE) :: data_file
134  !! @endcode
135  !> Only CSV format is supported so far.
136  !> @note It would be even more useful to add the CSV file record into the
137  !! type as an allocatable component and implement simple record_clean
138  !! record_append functions. However, allocatable components
139  !! are not supported in GNU gfortran 4.8, issues error:
140  !! `Error: Deferred-length character component 'record_string' at
141  !! (1) is not yet supported`. Therefore, raw operations with the CSV
142  !! file record are left here as in the non-OO versions. See
143  !! [extended example](http://ahamodel.uib.no/doc/ar01s08.html#_extended_example)
144  !! in HEDTOOLS and the code of this procedure:
145  !! the_population::population_save_data_all_agents_csv(). But note
146  !! that these codes use standard non-object-oriented procedures from
147  !! HEDTOOLS, not these object oriented wrappers.
148  type, public :: file_handle
149  !> The format of the file, i.e. the file type. The following file types
150  !! are implemented:
151  !! - `FORMAT_CSV` -- Comma Separated Values is a plain text;
152  !! - `FORMAT_TXT` -- Plain text file.
153  !! .
154  integer, private :: format
155  !> `file_object_csv` is the standard `CSV_FILE` type from CSV_IO module
156  !! HEDTOOLS. For details on this component type see
157  !! [CSV_FILE](http://ahamodel.uib.no/doc/ar01s08.html#_derived_type_csv_file)
158  !! `CSV_FILE` has the following sub-components:
159  !! - `character (len=MAX_FILENAME) :: name` -- file name;
160  !! - `integer :: unit` -- Fortran file unit;
161  !! - `logical :: status` -- Logical status of the last operation.
162  !! .
163  !! @note In the wrapper implementation routines, just substitute the
164  !! original CSV file handle object with this%file_object_csv.
165  type (csv_file), private :: file_object_csv
166  contains
167  ! All objects here are private except explicitly stated public.
168  private
169  !> Obtain the success (TRUE) or failure (FALSE) status of the latest
170  !! file operation.
171  !! See @ref file_handle_example_code "example code".
172  !!
173  !! **Example:**
174  !! @code
175  !! if ( data_file%is_success() ) then
176  !! @endcode
177  procedure, public :: is_success => file_operation_last_is_success
178  !> Get the file name associated with the file handle.
179  !! See @ref file_handle_example_code "example code".
180  !!
181  !! **Example:**
182  !! @code
183  !! print *, data_file%get_name()
184  !! @endcode
185  procedure, public :: get_name => file_hangle_get_name_string
186  !> Get the Fortran unit number associated with the
187  !! file handle object.
188  !! See @ref file_handle_example_code "example code".
189  !!
190  !! **Example:**
191  !! @code
192  !! print *, data_file%get_unit()
193  !! @endcode
194  procedure, public :: get_unit => file_object_get_associated_unit
195  !> Check if the file format is CSV.
196  !! See @ref file_handle_example_code "example code".
197  procedure, public :: is_csv => file_object_format_is_csv
198  !> Check if the file format is TXT.
199  !! See @ref file_handle_example_code "example code".
200  procedure, public :: is_txt => file_object_format_is_txt
201  !> Open file for physical writing on the disk.
202  !! See @ref file_handle_example_code "example code".
203  !!
204  !! **Example:**
205  !! @code
206  !! call data_file%open_write( "file_001.csv" )
207  !! @endcode
208  procedure, public :: open_write => csv_open_write_this
209  !> Physically write an information header (metadata) about the file.
210  !! See @ref file_handle_example_code "example code".
211  !!
212  !! **Example:**
213  !! @code
214  !! call data_file%header_write("Agent data at start of the simulation")
215  !! @endcode
216  procedure, public :: header_write => csv_header_line_write_this
217  !> Closes the file for reading or writing.
218  !! See @ref file_handle_example_code "example code".
219  !!
220  !! **Example:**
221  !! @code
222  !! call data_file%close()
223  !! @endcode
224  procedure, public :: close => csv_close_this
225  !> Physically write a single string CSV data record (row) to the file.
226  !! See @ref file_handle_example_code "example code".
227  !!
228  !! **Example:**
229  !! @code
230  !! call data_file%record_write( record_string )
231  !! @endcode
232  procedure, public :: record_write => csv_record_string_write_this
233 
234  end type
235 
236 contains ! ........ implementation of procedures for this level ................
237 
238  !-----------------------------------------------------------------------------
239  !> Get the success or error status of the latest file operation.
240  !! **Example:**
241  !! @code
242  !! if ( data_file%is_success() ) then
243  !! @endcode
244  function file_operation_last_is_success(this) result (is_success)
245  class(file_handle), intent(inout) :: this
246  !> @return TRUE if the latest file operation was successful,
247  !! FALSE otherwise.
248  logical :: is_success
249  is_success = this%file_object_csv%status
250  end function file_operation_last_is_success
251 
252  !-----------------------------------------------------------------------------
253  !> Get the file name associated with the file handle. If the file name is
254  !! (yet) undefined, the latest operation success flag
255  !! (see file_io::is_success()) is FALSE.
256  !! **Example:**
257  !! @code
258  !! print *, data_file%get_name()
259  !! @endcode
260  function file_hangle_get_name_string(this) result (name_file)
261  class(file_handle), intent(inout) :: this
262  !> @return the name of the file.
263  character(len=:), allocatable :: name_file
264  name_file = this%file_object_csv%name
265  if ( len_trim(name_file)==0 ) this%file_object_csv%status = .false.
266  end function file_hangle_get_name_string
267 
268  !-----------------------------------------------------------------------------
269  !> A Low level function to get the Fortran unit number associated with the
270  !! file handle object.
271  !! @note This function is useful only for diagnostics because Fortran unit is
272  !! treated automatically and transparently in all the routines of this
273  !! module. Of course, it could also be useful for low-level code.
274  !! **Example:**
275  !! @code
276  !! print *, data_file%get_unit()
277  !! @endcode
278  function file_object_get_associated_unit(this) result (unit_n)
279  class(file_handle), intent(inout) :: this
280  !> @return the Furtran file unit.
281  integer :: unit_n
282  unit_n = get_file_unit( this%file_object_csv%name, &
283  this%file_object_csv%status )
285 
286  !-----------------------------------------------------------------------------
287  !> Check if the file format is CSV.
288  function file_object_format_is_csv(this) result (is_type)
289  class(file_handle), intent(in) :: this
290  !> @return TRUE if the file format is CSV, FALSE otherwise.
291  logical :: is_type
292  if (this%format == format_csv) then
293  is_type = .true.
294  else
295  is_type = .false.
296  end if
297  end function file_object_format_is_csv
298 
299  !-----------------------------------------------------------------------------
300  !> Check if the file format is CSV.
301  function file_object_format_is_txt(this) result (is_type)
302  class(file_handle), intent(in) :: this
303  !> @return TRUE if the file format is TXT, FALSE otherwise.
304  logical :: is_type
305  if (this%format == format_txt) then
306  is_type = .true.
307  else
308  is_type = .false.
309  end if
310  end function file_object_format_is_txt
311 
312  !-----------------------------------------------------------------------------
313  !> This is an object oriented wrapper for `CSV_OPEN_WRITE()`. For details see
314  !! [CSV_OPEN_WRITE](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_header_write).
315  !! @note This procedure also automatically and transparently assigns the
316  !! Fortran unit.
317  !! **Example:**
318  !! @code
319  !! call data_file%open_write( "file_001.csv" )
320  !! @endcode
321  subroutine csv_open_write_this(this, name, format)
322  class(file_handle), intent(inout) :: this
323  !> @param[in] name the name of the file.
324  character(len=*), intent(in) :: name
325  !> @param[in] format optional data format type of the file:
326  !! - FORMAT_CSV (default)
327  !! - FORMAT_TXT
328  !! .
329  integer, optional, intent(in) :: format
330 
331  ! Local copies of optionals
332  integer :: format_def
333 
334  !> ### Implementation notes ###
335  !> Set name from the mandatory `name` argument.
336  this%file_object_csv%name = name
337 
338  !> Default format is `FORMAT_CSV`.
339  if (present(format)) then
340  format_def = format
341  else
342  format_def = format_csv
343  end if
344 
345  !> Providing a non-supported format results in FALSE status flag.
346  select case (format_def)
347  case (format_csv)
348  this%format = format_def
349  this%file_object_csv%status = .true.
350  case (format_txt)
351  this%format = format_def
352  this%file_object_csv%status = .true. ! Not implemented so far.
353  case default
354  this%format = undefined
355  this%file_object_csv%status = .false. ! Unknown format, error.
356  return
357  end select
358 
359  !> Open the file for writing physically.
360  call csv_open_write( this%file_object_csv )
361 
362  end subroutine csv_open_write_this
363 
364  !-----------------------------------------------------------------------------
365  !> This is an object oriented wrapper for `CSV_CLOSE()`. For details see
366  !! [CSV_CLOSE](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_close).
367  !! @note This procedure also automatically ans transparently assigns the
368  !! Fortran unit.
369  !! **Example:**
370  !! @code
371  !! call data_file%close()
372  !! @endcode
373  subroutine csv_close_this(this)
374  class(file_handle), intent(inout) :: this
375  call csv_close( this%file_object_csv )
376  end subroutine csv_close_this
377 
378  !-----------------------------------------------------------------------------
379  !> This is an object oriented wrapper for `CSV_HEADER_WRITE()`. See
380  !! [CSV_HEADER_WRITE](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_header_write)
381  !! for details.
382  !! @note File header is optional in CSV files and is not used in most cases.
383  !! **Example:**
384  !! @code
385  !! call data_file%header_write("Agent data at start of the simulation")
386  !! @endcode
387  subroutine csv_header_line_write_this(this, header)
388  class(file_handle), intent(inout) :: this
389  !> @param[in] header is the optional header line for the CSV file. If
390  !! header is absent, it is automatically generated from the
391  !! file name.
392  character(len=*), optional, intent(in) :: header
393  if (present(header)) then
394  call csv_header_write( header, this%file_object_csv )
395  else
396  call csv_header_write( "File " // this%file_object_csv%name, &
397  this%file_object_csv )
398  end if
399  end subroutine csv_header_line_write_this
400 
401  !-----------------------------------------------------------------------------
402  !> Physically write a single string CSV data record to the file. See
403  !! [CSV_RECORD_WRITE](http://ahamodel.uib.no/doc/ar01s08.html#_subroutine_csv_record_write)
404  !! **Example:**
405  !! @code
406  !! call data_file%record_write( record_string )
407  !! @endcode
408  subroutine csv_record_string_write_this(this, csv_record)
409  class(file_handle), intent(inout) :: this
410  !> @param[in] csv_record character string that keeps the whole record
411  !! (row) of the CSV data spreadsheet.
412  character(len=*), intent(in) :: csv_record
413  call csv_record_write( csv_record, this%file_object_csv )
414  end subroutine csv_record_string_write_this
415 
416 
417 end module file_io
COMMONDATA – definitions of global constants and procedures.
Definition: m_common.f90:1497
logical, parameter, public true
Safety parameter avoid errors in logical values, so we can now refer to standard Fortran ....
Definition: m_common.f90:1632
logical, parameter, public false
Definition: m_common.f90:1632
Definition of high level file objects.
Definition: m_fileio.f90:110
character(len=:) function, allocatable file_hangle_get_name_string(this)
Get the file name associated with the file handle. If the file name is (yet) undefined,...
Definition: m_fileio.f90:261
logical function file_object_format_is_csv(this)
Check if the file format is CSV.
Definition: m_fileio.f90:289
subroutine csv_close_this(this)
This is an object oriented wrapper for CSV_CLOSE(). For details see CSV_CLOSE.
Definition: m_fileio.f90:374
logical function file_object_format_is_txt(this)
Check if the file format is CSV.
Definition: m_fileio.f90:302
subroutine csv_open_write_this(this, name, format)
This is an object oriented wrapper for CSV_OPEN_WRITE(). For details see CSV_OPEN_WRITE.
Definition: m_fileio.f90:322
integer function file_object_get_associated_unit(this)
A Low level function to get the Fortran unit number associated with the file handle object.
Definition: m_fileio.f90:279
@, public format_txt
Definition: m_fileio.f90:121
@, public undefined
Definition: m_fileio.f90:121
subroutine csv_header_line_write_this(this, header)
This is an object oriented wrapper for CSV_HEADER_WRITE(). See CSV_HEADER_WRITE for details.
Definition: m_fileio.f90:388
@, public format_csv
Definition: m_fileio.f90:121
subroutine csv_record_string_write_this(this, csv_record)
Physically write a single string CSV data record to the file. See CSV_RECORD_WRITE Example:
Definition: m_fileio.f90:409
logical function file_operation_last_is_success(this)
Get the success or error status of the latest file operation. Example:
Definition: m_fileio.f90:245
FILE_HANDLE is the basic file handle object. It provides an unitary object oriented interface for ope...
Definition: m_fileio.f90:148