MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
Obs.f90
Go to the documentation of this file.
1 !> @brief This module contains the derived type ObsType
2 !!
3 !! This module defines type ObsType, which is the highest-level
4 !! derived type for implementing observations. All objects derived from
5 !! NumericalModelType or BndType already contain an ObsType member.
6 !!
7 !! Examples:
8 !! NumericalModelType.obs
9 !! BndType.obs
10 !!
11 !! Similarly, an ObsType member could be added to, say,
12 !! NumericalExchangeType or any other type that has DF, AR, RP, AD, BD, and OT
13 !! routines.
14 !!
15 !! IMPLEMENTATION OF OBSERVATIONS IN A MODEL OR PACKAGE
16 !!
17 !! For simple boundary packages like RIV and DRN, only steps 1-6 are
18 !! needed. For models and advanced packages like MAW and SFR, additional
19 !! steps are needed.
20 !!
21 !! 1. (package only) Override BndType.bnd_obs_supported to return true.
22 !! bnd_obs_supported is called from various places in code.
23 !!
24 !! 2. (optional) Write a subroutine that implements abstract interface
25 !! ObserveModule.ProcessIdSub. (Not needed if IDstring, which identifies
26 !! location in model to be observed, is either a single node number or
27 !! a single {lay, row, col} set of indices).
28 !!
29 !! Examples:
30 !! gwf_process_head_drawdown_obs_id, gwf_process_intercell_obs_id
31 !!
32 !! A package can allow IDstring to be a boundary name.
33 !! Example: ObsModule.DefaultObsIdProcessor
34 !!
35 !! 3. Override BndType.bnd_df_obs() to define string(s) to be
36 !! recognized as observation type(s) and (optional) assign ProcessIdPtr
37 !! (not needed if IDstring is either a node number or a {lay, row, col}
38 !! set of indices).
39 !!
40 !! Examples: gwf_df_obs, drn_df_obs
41 !!
42 !! When boundary names are allowed and developer wants simulated value
43 !! to be cumulative (flow, for example) if user specifies multiple
44 !! boundaries with the same BOUNDNAME, in bnd_df_obs call to
45 !! ObsPackage.StoreObsType, provide cumulative argument as true.
46 !! Otherwise, simulated values are not cumulative.
47 !!
48 !! 4. In DF routine: Call bnd_df_obs
49 !!
50 !! 5. In AR routine: Call ObsType.obs_ar. This reads the OBS input
51 !! file.
52 !! Example (gwf_ar): call this%obs%obs_ar()
53 !! Example (lak_ar): call this%obs%obs_ar()
54 !!
55 !! 6. Override BndType.bnd_rp_obs for any package that needs to
56 !! check user input or process observation input in any special way.
57 !! If no special processing is needed, BndType.bnd_rp_obs can
58 !! be used. This routine also expands the ObserveType%indxbnds array for
59 !! each observation in a package. ObserveType%indxbnds is used to sum
60 !! simulated values from multiple boundaries when BOUNDNAMES is used.
61 !! Equivalent routine may or may not be needed for model observations.
62 !! If needed, call it from bottom of RP routine.
63 !!
64 !! Examples:
65 !! BndType.bnd_rp_obs, which is called from gwf_rp
66 !!
67 !! 7. In AD routine: Call ObsType.obs_ad
68 !! Example: gwf_ad
69 !!
70 !! 8. Write a *_bd_obs routine. This is the routine that actually
71 !! calculates the simulated value for each observation type supported
72 !! by the model/package. Call *_bd_obs from the bottom of the
73 !! _bd routine.
74 !! *_bd_obs needs to:
75 !! Call ObsType.obs_bd_clear
76 !! For each observation:
77 !! Calculate the simulated value
78 !! Call ObsType.SaveOneSimval
79 !! Examples: gwf_bd_obs, maw_bd_obs, lak_bd_obs
80 !!
81 !! 9. In BD routine:
82 !! Call BndType.bnd_bd_obs
83 !! Examples: BndType.bnd_bd calls bnd_bd_obs
84 !! GwfModelType.gwf_bd calls gwf_bd_obs
85 !! MawType.maw_bd calls maw_bd_obs
86 !! LakType.lak_bd calls lak_bd_obs
87 !!
88 !! 10. Ensure that ObsType.obs_ot is called. For packages, obs_ot is called
89 !! from the model _ot procedure. The model _ot procedure should also call
90 !! obs_ot for its own observations. Do not call obs_ot from a package _ot
91 !! procedure because the package _ot procedure may not be called, depending
92 !! on Output Control settings (ibudfl).
93 !!
94 !! Note: BndType.bnd_ot_obs calls:
95 !! ObsType.obs_ot
96 !!
97 !! Note: ObsType.obs_ot calls:
98 !! store_all_simvals
99 !! write_continuous_simvals
100 !! obsOutputList.WriteOutputLines
101 !!
102 !! BINARY OUTPUT:
103 !!
104 !! When observation-output files are written, the user has the option to have
105 !! output written to a binary file. Binary obs output files start with a
106 !! 100-byte header structured as follows:
107 !!
108 !! bytes 1-4 (ascii): Observation type contained in file; options are:
109 !! "cont" -- Continuous observations
110 !! byte 5: blank
111 !! bytes 6-11 (ascii): Precision of all floating-point values; options are:
112 !! "single" -- Single precision
113 !! "double" -- Double precision
114 !! bytes 12-15 (ascii): LENOBSNAME (integer; length of observation names,
115 !! in bytes)
116 !! bytes 16-100: blank
117 !!
118 !! IN A FILE OF CONTINUOUS OBSERVATIONS:
119 !!
120 !! The 100-byte header is followed by:
121 !! NOBS (4-byte integer) -- Number of observations.
122 !! NOBS repetitions of OBSNAME (ascii, LENOBSNAME bytes each).
123 !! Any number of repetitions of:
124 !! TIME SIMVAL-1 SIMVAL-2 ... SIMVAL-NOBS (floating point)
125 !!
126 !<
127 module obsmodule
128 
129  use kindmodule, only: dp, i4b
131  use basedismodule, only: disbasetype
137  tableft
138  use tablemodule, only: tabletype, table_cr
140  use listmodule, only: listtype
146  use obsoutputmodule, only: obsoutputtype
148  use openspecmodule, only: access, form
149  use simvariablesmodule, only: errmsg
151  use tdismodule, only: totim
152 
153  implicit none
154 
155  private
157 
158  type :: obstype
159  ! -- Public members
160  integer(I4B), public :: iout = 0 !< model list file unit
161  integer(I4B), public :: npakobs = 0 !< number of observations
162  integer(I4B), pointer, public :: inunitobs => null() !< observation input file unit
163  character(len=LINELENGTH), pointer, public :: inputfilename => null() !< observation input file name
164  character(len=2*LENPACKAGENAME + 4), public :: pkgname = '' !< package name
165  character(len=LENFTYPE), public :: filtyp = '' !< package file type
166  logical, pointer, public :: active => null() !> logical indicating if a observation is active
167  type(obscontainertype), dimension(:), pointer, public :: pakobs => null() !< package observations
168  type(obsdatatype), dimension(:), pointer, public :: obsdata => null() !< observation data
169  ! -- Private members
170  integer(I4B), private :: iprecision = 2 ! 2=double; 1=single
171  integer(I4B), private :: idigits = 0
172  character(len=LINELENGTH), private :: outputfilename = ''
173  character(len=LINELENGTH), private :: blocktypefound = ''
174  character(len=20), private :: obsfmtcont = ''
175  logical, private :: echo = .false.
176  logical, private :: more
177  type(listtype), private :: obslist
178  type(obsoutputlisttype), pointer, private :: obsoutputlist => null()
179  class(disbasetype), pointer, private :: dis => null()
180  type(blockparsertype), private :: parser
181  !
182  ! -- table object
183  type(tabletype), pointer :: obstab => null()
184  contains
185  ! -- Public procedures
186  procedure, public :: obs_df
187  procedure, public :: obs_ar
188  procedure, public :: obs_ad
189  procedure, public :: obs_bd_clear
190  procedure, public :: obs_ot
191  procedure, public :: obs_da
192  procedure, public :: saveonesimval
193  procedure, public :: storeobstype
194  procedure, public :: allocate_scalars
195  ! -- Private procedures
196  procedure, private :: build_headers
197  procedure, private :: define_fmts
198  procedure, private :: get_num
199  procedure, private :: get_obs
200  procedure, private :: get_obs_array
201  procedure, private :: get_obs_datum
202  procedure, private :: obs_ar1
203  procedure, private :: obs_ar2
204  procedure, private :: set_obs_array
205  procedure, private :: read_observations
206  procedure, private :: read_obs_blocks
207  procedure, private :: read_obs_options
208  procedure, private :: write_obs_simvals
209  end type obstype
210 
211 contains
212 
213  ! Non-type-bound procedures
214 
215  !> @ brief Create a new ObsType object
216  !!
217  !! Subroutine to create a new ObsType object. Soubroutine
218  !!
219  !! - creates object
220  !! - allocates pointer
221  !! - initializes values
222  !!
223  !<
224  subroutine obs_cr(obs, inobs)
225  ! -- dummy
226  type(obstype), pointer, intent(out) :: obs !< observation ObsType
227  integer(I4B), pointer, intent(in) :: inobs !< observation input file unit
228  !
229  allocate (obs)
230  call obs%allocate_scalars()
231  obs%inUnitObs => inobs
232  end subroutine obs_cr
233 
234  !> @ brief Process IDstring provided for each observation
235  !!
236  !! Subroutine to process the IDstring provided for each observation. The
237  !! IDstring identifies the location in the model of the node(s) or feature(s)
238  !! where the simulated value is to be extracted and recorded. Subroutine
239  !!
240  !! - interprets the IDstring
241  !! - stores the location of interest in the ObserveType object that
242  !! contains information about the observation
243  !!
244  !<
245  subroutine defaultobsidprocessor(obsrv, dis, inunitobs, iout)
246  ! -- dummy
247  type(observetype), intent(inout) :: obsrv !< observation ObserveType
248  class(disbasetype), intent(in) :: dis !< discretization object
249  integer(I4B), intent(in) :: inunitobs !< observation input file unit
250  integer(I4B), intent(in) :: iout !< model list file
251  ! -- local
252  integer(I4B) :: n
253  integer(I4B) :: icol, istart, istop
254  character(len=LINELENGTH) :: string
255  logical :: flag_string
256  !
257  ! -- Initialize variables
258  string = obsrv%IDstring
259  icol = 1
260  flag_string = .true. ! Allow string to contain a boundary name
261  !
262  n = dis%noder_from_string(icol, istart, istop, inunitobs, &
263  iout, string, flag_string)
264  !
265  if (n > 0) then
266  obsrv%NodeNumber = n
267  elseif (n == -2) then
268  ! Integer can't be read from string; it's presumed to be a boundary
269  ! name (already converted to uppercase)
270  obsrv%FeatureName = string(istart:istop)
271  ! -- Observation may require summing rates from multiple boundaries,
272  ! so assign NodeNumber as a value that indicates observation
273  ! is for a named boundary or group of boundaries.
274  obsrv%NodeNumber = namedboundflag
275  else
276  errmsg = 'Error reading data from ID string'
277  call store_error(errmsg)
278  call store_error_unit(inunitobs)
279  end if
280  end subroutine defaultobsidprocessor
281 
282  ! Type-bound public procedures
283 
284  !> @ brief Define some members of an ObsType object
285  !!
286  !! Subroutine to define some members of an ObsType object.
287  !!
288  !<
289  subroutine obs_df(this, iout, pkgname, filtyp, dis)
290  ! -- dummy
291  class(obstype), intent(inout) :: this
292  integer(I4B), intent(in) :: iout !< model list file unit
293  character(len=*), intent(in) :: pkgname !< package name
294  character(len=*), intent(in) :: filtyp !< package file type
295  class(disbasetype), pointer :: dis !< discretization object
296  !
297  this%iout = iout
298  this%pkgName = pkgname
299  this%filtyp = filtyp
300  this%dis => dis
301  !
302  ! -- Initialize block parser
303  call this%parser%Initialize(this%inUnitObs, this%iout)
304  end subroutine obs_df
305 
306  !> @ brief Allocate and read package observations
307  !!
308  !! Subroutine to allocate and read observations for a package. Subroutine
309  !!
310  !! - reads OPTIONS block of OBS input file
311  !! - reads CONTINUOUS blocks of OBS input file
312  !!
313  !<
314  subroutine obs_ar(this)
315  ! -- dummy
316  class(obstype) :: this
317  !
318  call this%obs_ar1(this%pkgName)
319  if (this%active) then
320  call this%obs_ar2(this%dis)
321  end if
322  end subroutine obs_ar
323 
324  !> @ brief Advance package observations
325  !!
326  !! Subroutine to advance each package observations by resetting the
327  !! "current" value.
328  !!
329  !<
330  subroutine obs_ad(this)
331  ! -- dummy
332  class(obstype) :: this
333  ! -- local
334  integer(I4B) :: i, n
335  class(observetype), pointer :: obsrv => null()
336  !
337  n = this%get_num()
338  do i = 1, n
339  obsrv => this%get_obs(i)
340  call obsrv%ResetCurrentValue()
341  end do
342  end subroutine obs_ad
343 
344  !> @ brief Clear observation output lines
345  !!
346  !! Subroutine to clear output lines in preparation for new rows of
347  !! continuous observations.
348  !!
349  !<
350  subroutine obs_bd_clear(this)
351  ! -- dummy
352  class(obstype), target :: this
353  !
354  call this%obsOutputList%ResetAllObsEmptyLines()
355  end subroutine obs_bd_clear
356 
357  !> @ brief Output observation data
358  !!
359  !! Subroutine to output observation data. Subroutine
360  !!
361  !! - stores each simulated value into its ObserveType object
362  !! - writes each simulated value to it ObsOutputList object
363  !! _ writes contents of ObsOutputList to output file
364  !!
365  !! This procedure should NOT be called from a package's _ot procedure
366  !! because the package _ot procedure may not be called every time step.
367  !!
368  !<
369  subroutine obs_ot(this)
370  ! -- dummy
371  class(obstype), intent(inout) :: this
372  !
373  if (this%npakobs > 0) then
374  call this%write_obs_simvals()
375  call this%obsOutputList%WriteAllObsLineReturns()
376  end if
377  end subroutine obs_ot
378 
379  !> @ brief Deallocate observation data
380  !!
381  !! Subroutine to deallocate observation data.
382  !!
383  !<
384  subroutine obs_da(this)
385  ! -- dummy
386  class(obstype), intent(inout) :: this
387  ! -- local
388  integer(I4B) :: i
389  class(observetype), pointer :: obsrv => null()
390  !
391  deallocate (this%active)
392  deallocate (this%inputFilename)
393  deallocate (this%obsData)
394  !
395  ! -- observation table object
396  if (associated(this%obstab)) then
397  call this%obstab%table_da()
398  deallocate (this%obstab)
399  nullify (this%obstab)
400  end if
401  !
402  ! -- deallocate pakobs components and pakobs
403  if (associated(this%pakobs)) then
404  do i = 1, this%npakobs
405  obsrv => this%pakobs(i)%obsrv
406  call obsrv%da()
407  deallocate (obsrv)
408  nullify (this%pakobs(i)%obsrv)
409  end do
410  deallocate (this%pakobs)
411  end if
412  !
413  ! -- deallocate obsOutputList
414  call this%obsOutputList%DeallocObsOutputList()
415  deallocate (this%obsOutputList)
416  !
417  ! -- deallocate obslist
418  call this%obslist%Clear()
419  !
420  ! -- nullify
421  nullify (this%inUnitObs)
422  end subroutine obs_da
423 
424  !> @ brief Save a simulated value
425  !!
426  !! Subroutine to save or accumulate a simulated value to its ObserveType
427  !! object.
428  !!
429  !<
430  subroutine saveonesimval(this, obsrv, simval)
431  ! -- dummy
432  class(obstype) :: this
433  class(observetype), intent(inout) :: obsrv !< observation ObserveType
434  real(DP), intent(in) :: simval !< simulated value
435  ! -- local
436  character(len=LENOBSTYPE) :: obsTypeID
437  type(obsdatatype), pointer :: obsDatum => null()
438  !
439  ! -- initialize variables
440  obstypeid = obsrv%ObsTypeId
441  obsdatum => this%get_obs_datum(obstypeid)
442  !
443  ! -- save current simulation time
444  obsrv%CurrentTimeStepEndTime = totim
445  !
446  ! -- assign or accumulate simulated value
447  if (obsdatum%Cumulative .and. simval /= dnodata) then
448  obsrv%CurrentTimeStepEndValue = obsrv%CurrentTimeStepEndValue + simval
449  else
450  obsrv%CurrentTimeStepEndValue = simval
451  end if
452  end subroutine saveonesimval
453 
454  !> @ brief Store observation type
455  !!
456  !! Subroutine to store type name and related information for an
457  !! observation type that belongs to a package or model in the
458  !! obsData array.
459  !!
460  !<
461  subroutine storeobstype(this, obsrvType, cumulative, indx)
462  ! -- dummy
463  class(obstype), intent(inout) :: this
464  character(len=*), intent(in) :: obsrvType !< observation type
465  ! cumulative: Accumulate simulated values for multiple boundaries
466  logical, intent(in) :: cumulative !< logical indicating if the observation should be accumulated
467  integer(I4B), intent(out) :: indx !< observation index
468  ! -- local
469  integer(I4B) :: i
470  character(len=LENOBSTYPE) :: obsTypeUpper
471  character(len=100) :: msg
472  !
473  ! -- Ensure that obsrvType is not blank
474  if (obsrvtype == '') then
475  msg = 'Programmer error: Invalid argument in store_obs_type.'
476  call store_error(msg, terminate=.true.)
477  end if
478  !
479  ! -- Find first unused element
480  indx = -1
481  do i = 1, maxobstypes
482  if (this%obsData(i)%ObsTypeID /= '') cycle
483  indx = i
484  exit
485  end do
486  !
487  ! -- Ensure that array size is not exceeded
488  if (indx == -1) then
489  msg = 'Size of obsData array is insufficient; ' &
490  //'need to increase MAXOBSTYPES.'
491  call store_error(msg)
492  call store_error_unit(this%inUnitObs)
493  end if
494  !
495  ! -- Convert character argument to upper case
496  obstypeupper = obsrvtype
497  call upcase(obstypeupper)
498  !
499  ! -- Assign members
500  this%obsData(indx)%ObsTypeID = obstypeupper
501  this%obsData(indx)%Cumulative = cumulative
502  end subroutine storeobstype
503 
504  ! Type-bound private procedures
505 
506  !> @ brief Allocate observation scalars
507  !!
508  !! Subroutine to allocate and initialize memory for non-allocatable
509  ! members (scalars).
510  !!
511  !<
512  subroutine allocate_scalars(this)
513  ! -- dummy
514  class(obstype) :: this
515  !
516  allocate (this%active)
517  allocate (this%inputFilename)
518  allocate (this%obsOutputList)
519  allocate (this%obsData(maxobstypes))
520  !
521  ! -- Initialize
522  this%active = .false.
523  this%inputFilename = ''
524  end subroutine allocate_scalars
525 
526  !> @ brief Read observation options and output formats
527  !!
528  !! Subroutine to read the options block in the observation input file and
529  !! define output formats.
530  !!
531  !<
532  subroutine obs_ar1(this, pkgname)
533  ! -- dummy
534  class(obstype), intent(inout) :: this
535  character(len=*), intent(in) :: pkgname !< package name
536  ! -- formats
537 10 format(/, 'The observation utility is active for "', a, '"')
538  !
539  if (this%inUnitObs > 0) then
540  this%active = .true.
541  !
542  ! -- Indicate that OBS is active
543  write (this%iout, 10) trim(pkgname)
544  !
545  ! -- Read Options block
546  call this%read_obs_options()
547  !
548  ! -- define output formats
549  call this%define_fmts()
550  end if
551  end subroutine obs_ar1
552 
553  !> @ brief Call procedure provided by package
554  !!
555  !! Subroutine to call procedure provided by package to interpret IDstring
556  !! and store required data.
557  !!
558  !<
559  subroutine obs_ar2(this, dis)
560  ! -- dummy
561  class(obstype), intent(inout) :: this
562  class(disbasetype) :: dis !< discretization object
563  ! -- local
564  integer(I4B) :: i
565  type(obsdatatype), pointer :: obsDat => null()
566  character(len=LENOBSTYPE) :: obsTypeID
567  class(observetype), pointer :: obsrv => null()
568  !
569  call this%read_observations()
570  ! -- allocate and set observation array
571  call this%get_obs_array(this%npakobs, this%pakobs)
572  !
573  do i = 1, this%npakobs
574  obsrv => this%pakobs(i)%obsrv
575  ! -- Call IDstring processor procedure provided by package
576  obstypeid = obsrv%ObsTypeId
577  obsdat => this%get_obs_datum(obstypeid)
578  if (associated(obsdat%ProcessIdPtr)) then
579  call obsdat%ProcessIdPtr(obsrv, dis, &
580  this%inUnitObs, this%iout)
581  else
582  call defaultobsidprocessor(obsrv, dis, &
583  this%inUnitObs, this%iout)
584  end if
585  end do
586  !
587  if (count_errors() > 0) then
588  call store_error_unit(this%inunitobs)
589  end if
590  end subroutine obs_ar2
591 
592  !> @ brief Read observation options block
593  !!
594  !! Subroutine to read the options block in the observation input file.
595  !!
596  !<
597  subroutine read_obs_options(this)
598  ! -- dummy
599  class(obstype) :: this
600  ! -- local
601  integer(I4B) :: iin
602  integer(I4B) :: ierr
603  integer(I4B) :: localprecision
604  integer(I4B) :: localdigits
605  character(len=40) :: keyword
606  character(len=LINELENGTH) :: fname
607  type(listtype), pointer :: lineList => null()
608  logical :: continueread, found, endOfBlock
609  ! -- formats
610 10 format('No options block found in OBS input. Defaults will be used.')
611 40 format('Text output number of digits of precision set to: ', i2)
612 50 format('Text output number of digits set to internal representation (G0).')
613 60 format(/, 'Processing observation options:',/)
614  !
615  localprecision = 0
616  localdigits = -1
617  linelist => null()
618  !
619  ! -- Find and store file name
620  iin = this%inUnitObs
621  inquire (unit=iin, name=fname)
622  this%inputFilename = fname
623  !
624  ! -- Read Options block
625  continueread = .false.
626  ierr = 0
627  !
628  ! -- get BEGIN line of OPTIONS block
629  call this%parser%GetBlock('OPTIONS', found, ierr, &
630  supportopenclose=.true., blockrequired=.false.)
631  if (ierr /= 0) then
632  ! end of file
633  errmsg = 'End-of-file encountered while searching for'// &
634  ' OPTIONS in OBS '// &
635  'input file "'//trim(this%inputFilename)//'"'
636  call store_error(errmsg)
637  call this%parser%StoreErrorUnit()
638  elseif (.not. found) then
639  this%blockTypeFound = ''
640  if (this%iout > 0) write (this%iout, 10)
641  end if
642  !
643  ! -- parse OPTIONS entries
644  if (found) then
645  write (this%iout, 60)
646  readblockoptions: do
647  call this%parser%GetNextLine(endofblock)
648  if (endofblock) exit
649  call this%parser%GetStringCaps(keyword)
650  select case (keyword)
651  case ('DIGITS')
652  !
653  ! -- error if digits already read
654  if (localdigits /= -1) then
655  errmsg = 'Error in OBS input: DIGITS has already been defined'
656  call store_error(errmsg)
657  exit readblockoptions
658  end if
659  !
660  ! -- Specifies number of significant digits used writing simulated
661  ! values to a text file. Default is stored digits.
662  !
663  ! -- Read integer value
664  localdigits = this%parser%GetInteger()
665  !
666  ! -- Set localdigits to valid value: 0, or 2 to 16
667  if (localdigits == 0) then
668  write (this%iout, 50)
669  else if (localdigits < 1) then
670  errmsg = 'Error in OBS input: Invalid value for DIGITS option'
671  call store_error(errmsg)
672  exit readblockoptions
673  else
674  if (localdigits < 2) localdigits = 2
675  if (localdigits > 16) localdigits = 16
676  write (this%iout, 40) localdigits
677  end if
678  case ('PRINT_INPUT')
679  this%echo = .true.
680  write (this%iout, '(a)') 'The PRINT_INPUT option has been specified.'
681  case default
682  errmsg = 'Error in OBS input: Unrecognized option: '// &
683  trim(keyword)
684  call store_error(errmsg)
685  exit readblockoptions
686  end select
687  end do readblockoptions
688  end if
689  !
690  if (count_errors() > 0) then
691  call this%parser%StoreErrorUnit()
692  end if
693  !
694  write (this%iout, '(1x)')
695  !
696  ! -- Assign type variables
697  if (localprecision > 0) this%iprecision = localprecision
698  if (localdigits >= 0) this%idigits = localdigits
699  end subroutine read_obs_options
700 
701  !> @ brief Define observation output formats
702  !!
703  !! Subroutine to define observation output formats.
704  !!
705  !<
706  subroutine define_fmts(this)
707  ! -- dummy
708  class(obstype) :: this
709  ! formats
710 50 format('(g', i2.2, '.', i2.2, ')')
711  !
712  if (this%idigits == 0) then
713  this%obsfmtcont = '(G0)'
714  else
715  write (this%obsfmtcont, 50) this%idigits + 7, this%idigits
716  end if
717  end subroutine define_fmts
718 
719  !> @ brief Read observations
720  !!
721  !! Subroutine to read the observations from the observation input file
722  !! and build headers for the observation output files.
723  !!
724  !<
725  subroutine read_observations(this)
726  ! -- dummy
727  class(obstype) :: this
728  ! -- local
729  !
730  ! -- Read CONTINUOUS blocks and store observations
731  call this%read_obs_blocks(this%outputFilename)
732  !
733  ! -- build headers
734  call this%build_headers()
735  end subroutine read_observations
736 
737  !> @ brief Get the number of observations
738  !!
739  !! Function to get the number of observationns in this ObsType object.
740  !!
741  !<
742  function get_num(this)
743  ! -- return
744  integer(I4B) :: get_num !< number of observations
745  ! -- dummy
746  class(obstype) :: this
747  !
748  get_num = this%obsList%Count()
749  end function get_num
750 
751  !> @ brief Build observation headers
752  !!
753  !! Subroutine to build headers for CSV-formatted and unformatted
754  !! continuous-observation output files and write them to those files.
755  !!
756  !! Each formatted header will have the form: "time,obsname-1,obsname-2, ..."
757  !!
758  !<
759  subroutine build_headers(this)
760  ! -- module
761  use iso_fortran_env, only: int32
762  ! -- dummy
763  class(obstype), target :: this
764  ! -- local
765  integer(I4B) :: i
766  integer(I4B) :: ii
767  integer(I4B) :: idx
768  integer(I4B) :: iu
769  integer(I4B) :: num
770  integer(int32) :: nobs
771  character(len=4) :: clenobsname
772  type(observetype), pointer :: obsrv => null()
773  type(obsoutputtype), pointer :: obsOutput => null()
774  !
775  ! -- Cycle through ObsOutputList to write headers
776  ! to formatted and unformatted file(s).
777  idx = 1
778  num = this%obsOutputList%Count()
779  all_obsfiles: do i = 1, num
780  obsoutput => this%obsOutputList%Get(i)
781  nobs = obsoutput%nobs
782  iu = obsoutput%nunit
783  !
784  ! -- write header information to the formatted file
785  if (obsoutput%FormattedOutput) then
786  write (iu, '(a)', advance='NO') 'time'
787  else
788  ! -- write header to unformatted file
789  ! First 11 bytes are obs type and precision
790  if (this%iprecision == 1) then
791  ! -- single precision output
792  write (iu) 'cont single'
793  else if (this%iprecision == 2) then
794  ! -- double precision output
795  write (iu) 'cont double'
796  end if
797  ! -- write LENOBSNAME to bytes 12-15
798  write (clenobsname, '(i4)') lenobsname
799  write (iu) clenobsname
800  ! -- write blanks to complete 100-byte header
801  do ii = 16, 100
802  write (iu) ' '
803  end do
804  ! -- write NOBS
805  write (iu) nobs
806  end if
807  !
808  ! -- write observation name
809  obsfile: do ii = 1, nobs
810  obsrv => this%get_obs(idx)
811  if (obsoutput%FormattedOutput) then
812  write (iu, '(a,a)', advance='NO') ',', trim(obsrv%Name)
813  !
814  ! -- terminate the line on the last observation in file
815  if (ii == nobs) then
816  write (iu, '(a)', advance='YES') ''
817  end if
818  else
819  write (iu) obsrv%Name
820  end if
821  idx = idx + 1
822  end do obsfile
823  end do all_obsfiles
824  end subroutine build_headers
825 
826  !> @ brief Get an array of observations
827  !!
828  !! Subroutine to get an array containing all observations in this
829  !! ObsType object.
830  !!
831  !<
832  subroutine get_obs_array(this, nObs, obsArray)
833  ! -- dummy
834  class(obstype), intent(inout) :: this
835  integer(I4B), intent(out) :: nObs !< number of observations
836  type(obscontainertype), dimension(:), pointer, intent(inout) :: obsArray !< observation array
837  !
838  nobs = this%get_num()
839  if (associated(obsarray)) deallocate (obsarray)
840  allocate (obsarray(nobs))
841  !
842  ! set observations in obsArray
843  if (nobs > 0) then
844  call this%set_obs_array(nobs, obsarray)
845  end if
846  end subroutine get_obs_array
847 
848  !> @ brief Get an ObsDataType object
849  !!
850  !! Function to get an ObsDataType object for the specified observation type.
851  !!
852  !<
853  function get_obs_datum(this, obsTypeID) result(obsDatum)
854  ! -- dummy
855  class(obstype) :: this
856  character(len=*), intent(in) :: obstypeid !< observation type
857  ! -- return
858  type(obsdatatype), pointer :: obsdatum !< observation ObsDataType
859  ! -- local
860  integer(I4B) :: i
861  !
862  obsdatum => null()
863  do i = 1, maxobstypes
864  if (this%obsData(i)%ObsTypeID == obstypeid) then
865  obsdatum => this%obsData(i)
866  exit
867  end if
868  end do
869  !
870  if (.not. associated(obsdatum)) then
871  errmsg = 'Observation type not found: '//trim(obstypeid)
872  call store_error(errmsg)
873  call store_error_unit(this%inUnitObs)
874  end if
875  end function get_obs_datum
876 
877  !> @ brief Set observation array values
878  !!
879  !! Subroutine to set values in an observation array.
880  !!
881  !<
882  subroutine set_obs_array(this, nObs, obsArray)
883  ! -- dummy
884  class(obstype), intent(inout) :: this
885  integer(I4B), intent(in) :: nObs !< number of observations
886  type(obscontainertype), dimension(nObs), intent(inout) :: obsArray !< observation array
887  !
888  ! -- local
889  integer(I4B) :: i
890  integer(I4B) :: n
891  type(observetype), pointer :: obsrv => null()
892  !
893  n = this%get_num()
894  do i = 1, n
895  obsrv => this%get_obs(i)
896  obsarray(i)%obsrv => obsrv
897  end do
898  end subroutine set_obs_array
899 
900  !> @ brief Get an ObserveType object
901  !!
902  !! Subroutine to get an ObserveType object from the list of observations
903  !! using an list index.
904  !!
905  !<
906  function get_obs(this, indx) result(obsrv)
907  ! -- dummy
908  class(obstype) :: this
909  integer(I4B), intent(in) :: indx !< observation list index
910  class(observetype), pointer :: obsrv !< observation ObserveType
911  !
912  obsrv => getobsfromlist(this%obsList, indx)
913  end function get_obs
914 
915  !> @ brief Read observation blocks
916  !!
917  !! Subroutine to read CONTIGUOUS block from the observation input file.
918  !!
919  !<
920  subroutine read_obs_blocks(this, fname)
921  ! -- dummy
922  class(obstype), intent(inout) :: this
923  character(len=*), intent(inout) :: fname
924  ! -- local
925  integer(I4B) :: ierr, indexobsout, numspec
926  logical :: fmtd, found, endOfBlock
927  character(len=LENBIGLINE) :: pnamein, fnamein
928  character(len=LENHUGELINE) :: line
929  character(len=LINELENGTH) :: btagfound, message, word
930  character(len=LINELENGTH) :: title
931  character(len=LINELENGTH) :: tag
932  character(len=20) :: accarg, bin, fmtarg
933  type(observetype), pointer :: obsrv => null()
934  type(obsoutputtype), pointer :: obsOutput => null()
935  integer(I4B) :: ntabrows
936  integer(I4B) :: ntabcols
937  !
938  ! -- initialize local variables
939  numspec = -1
940  errmsg = ''
941  !
942  inquire (unit=this%parser%iuactive, name=pnamein)
943  call getfilefrompath(pnamein, fnamein)
944  !
945  if (this%echo) then
946  !
947  ! -- create the observation table
948  ! -- table dimensions
949  ntabrows = 1
950  ntabcols = 5
951  !
952  ! -- initialize table and define columns
953  title = 'OBSERVATIONS READ FROM FILE "'//trim(fnamein)//'"'
954  call table_cr(this%obstab, fnamein, title)
955  call this%obstab%table_df(ntabrows, ntabcols, this%iout, &
956  finalize=.false.)
957  tag = 'NAME'
958  call this%obstab%initialize_column(tag, lenobsname, alignment=tableft)
959  tag = 'TYPE'
960  call this%obstab%initialize_column(tag, lenobstype + 12, alignment=tableft)
961  tag = 'TIME'
962  call this%obstab%initialize_column(tag, 12, alignment=tableft)
963  tag = 'LOCATION DATA'
964  call this%obstab%initialize_column(tag, lenboundname + 2, alignment=tableft)
965  tag = 'OUTPUT FILENAME'
966  call this%obstab%initialize_column(tag, 80, alignment=tableft)
967  end if
968  !
969  found = .true.
970  readblocks: do
971  if (.not. found) exit
972  !
973  call this%parser%GetBlock('*', found, ierr, .true., .false., btagfound)
974  if (.not. found) then
975  exit readblocks
976  end if
977  this%blockTypeFound = btagfound
978  !
979  ! Get keyword, which should be FILEOUT
980  call this%parser%GetStringCaps(word)
981  if (word /= 'FILEOUT') then
982  call store_error('CONTINUOUS keyword must be followed by '// &
983  '"FILEOUT" then by filename.')
984  cycle
985  end if
986  !
987  ! -- get name of output file
988  call this%parser%GetString(fname)
989  ! Fname is the output file name defined in the BEGIN line of the block.
990  if (fname == '') then
991  message = 'Error reading OBS input file, likely due to bad'// &
992  ' block or missing file name.'
993  call store_error(message)
994  cycle
995  else if (this%obsOutputList%ContainsFile(fname)) then
996  errmsg = 'OBS outfile "'//trim(fname)// &
997  '" is provided more than once.'
998  call store_error(errmsg)
999  cycle
1000  end if
1001  !
1002  ! -- look for BINARY option
1003  call this%parser%GetStringCaps(bin)
1004  if (bin == 'BINARY') then
1005  fmtarg = form
1006  accarg = access
1007  fmtd = .false.
1008  else
1009  fmtarg = 'FORMATTED'
1010  accarg = 'SEQUENTIAL'
1011  fmtd = .true.
1012  end if
1013  !
1014  ! -- open the output file
1015  numspec = 0
1016  call openfile(numspec, 0, fname, 'OBS OUTPUT', fmtarg, &
1017  accarg, 'REPLACE')
1018  !
1019  ! -- add output file to list of output files and assign its
1020  ! FormattedOutput member appropriately
1021  call this%obsOutputList%Add(fname, numspec)
1022  indexobsout = this%obsOutputList%Count()
1023  obsoutput => this%obsOutputList%Get(indexobsout)
1024  obsoutput%FormattedOutput = fmtd
1025  !
1026  ! -- process lines defining observations
1027  select case (btagfound)
1028  case ('CONTINUOUS')
1029  !
1030  ! -- construct a continuous observation from each line in the block
1031  readblockcontinuous: do
1032  call this%parser%GetNextLine(endofblock)
1033  if (endofblock) exit
1034  call this%parser%GetCurrentLine(line)
1035  call constructobservation(obsrv, line, numspec, fmtd, &
1036  indexobsout, this%obsData, &
1037  this%parser%iuactive)
1038  !
1039  ! -- increment number of observations
1040  ! to be written to this output file.
1041  obsoutput => this%obsOutputList%Get(indexobsout)
1042  obsoutput%nobs = obsoutput%nobs + 1
1043  call addobstolist(this%obsList, obsrv)
1044  !
1045  ! -- write line to the observation table
1046  if (this%echo) then
1047  call obsrv%WriteTo(this%obstab, btagfound, fname)
1048  end if
1049  end do readblockcontinuous
1050  case default
1051  errmsg = 'Error: Observation block type not recognized: '// &
1052  trim(btagfound)
1053  call store_error(errmsg)
1054  end select
1055  end do readblocks
1056  !
1057  ! -- finalize the observation table
1058  if (this%echo) then
1059  call this%obstab%finalize_table()
1060  end if
1061  !
1062  ! -- determine if error condition occurs
1063  if (count_errors() > 0) then
1064  call this%parser%StoreErrorUnit()
1065  end if
1066  end subroutine read_obs_blocks
1067 
1068  !> @ brief Write observation data
1069  !!
1070  !! Subroutine to write observation data for a time step for each observation
1071  !! to the observation output file.
1072  !!
1073  !<
1074  subroutine write_obs_simvals(this)
1075  ! -- dummy
1076  class(obstype), intent(inout) :: this
1077  ! -- local
1078  integer(I4B) :: i
1079  integer(I4B) :: iprec
1080  integer(I4B) :: numobs
1081  character(len=20) :: fmtc
1082  real(DP) :: simval
1083  class(observetype), pointer :: obsrv => null()
1084  !
1085  ! Write simulated values for observations
1086  iprec = this%iprecision
1087  fmtc = this%obsfmtcont
1088  ! -- iterate through all observations
1089  numobs = this%obsList%Count()
1090  do i = 1, numobs
1091  obsrv => this%get_obs(i)
1092  ! -- continuous observation
1093  simval = obsrv%CurrentTimeStepEndValue
1094  if (obsrv%FormattedOutput) then
1095  call write_fmtd_obs(fmtc, obsrv, this%obsOutputList, simval)
1096  else
1097  call write_unfmtd_obs(obsrv, iprec, this%obsOutputList, simval)
1098  end if
1099  end do
1100  end subroutine write_obs_simvals
1101 
1102 end module obsmodule
This module contains block parser methods.
Definition: BlockParser.f90:7
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
@ tableft
left justified table column
Definition: Constants.f90:171
integer(i4b), parameter lenpackagename
maximum length of the package name
Definition: Constants.f90:23
integer(i4b), parameter namedboundflag
named bound flag
Definition: Constants.f90:49
real(dp), parameter dnodata
real no data constant
Definition: Constants.f90:95
integer(i4b), parameter lenhugeline
maximum length of a huge line
Definition: Constants.f90:16
integer(i4b), parameter lenbigline
maximum length of a big line
Definition: Constants.f90:15
integer(i4b), parameter maxobstypes
maximum number of observation types
Definition: Constants.f90:48
integer(i4b), parameter lenftype
maximum length of a package type (DIS, WEL, OC, etc.)
Definition: Constants.f90:39
integer(i4b), parameter lenboundname
maximum length of a bound name
Definition: Constants.f90:36
integer(i4b), parameter lenobsname
maximum length of a observation name
Definition: Constants.f90:40
integer(i4b), parameter maxcharlen
maximum length of char string
Definition: Constants.f90:47
integer(i4b), parameter lenobstype
maximum length of a observation type (CONTINUOUS)
Definition: Constants.f90:41
subroutine, public getfilefrompath(pathname, filename)
integer(i4b) function, public getunit()
Get a free unit number.
subroutine, public upcase(word)
Convert to upper case.
subroutine, public openfile(iu, iout, fname, ftype, fmtarg_opt, accarg_opt, filstat_opt, mode_opt)
Open a file.
Definition: InputOutput.f90:30
This module defines variable data types.
Definition: kind.f90:8
This module contains the derived type ObsContainerType.
Definition: ObsContainer.f90:8
This module contains the derived types ObserveType and ObsDataType.
Definition: Observe.f90:15
subroutine, public constructobservation(newObservation, defLine, numunit, formatted, indx, obsData, inunit)
@ brief Construct a new ObserveType
Definition: Observe.f90:230
type(observetype) function, pointer, public getobsfromlist(list, idx)
@ brief Get an ObserveType from a list
Definition: Observe.f90:335
subroutine, public addobstolist(list, obs)
@ brief Add a ObserveType to a list
Definition: Observe.f90:319
This module contains the derived type ObsType.
Definition: Obs.f90:127
subroutine write_obs_simvals(this)
@ brief Write observation data
Definition: Obs.f90:1075
subroutine read_obs_blocks(this, fname)
@ brief Read observation blocks
Definition: Obs.f90:921
subroutine obs_da(this)
@ brief Deallocate observation data
Definition: Obs.f90:385
type(obsdatatype) function, pointer get_obs_datum(this, obsTypeID)
@ brief Get an ObsDataType object
Definition: Obs.f90:854
subroutine set_obs_array(this, nObs, obsArray)
@ brief Set observation array values
Definition: Obs.f90:883
subroutine obs_bd_clear(this)
@ brief Clear observation output lines
Definition: Obs.f90:351
subroutine obs_ar(this)
@ brief Allocate and read package observations
Definition: Obs.f90:315
subroutine obs_ar1(this, pkgname)
@ brief Read observation options and output formats
Definition: Obs.f90:533
subroutine read_observations(this)
@ brief Read observations
Definition: Obs.f90:726
subroutine obs_ad(this)
@ brief Advance package observations
Definition: Obs.f90:331
subroutine, public obs_cr(obs, inobs)
@ brief Create a new ObsType object
Definition: Obs.f90:225
subroutine get_obs_array(this, nObs, obsArray)
@ brief Get an array of observations
Definition: Obs.f90:833
subroutine read_obs_options(this)
@ brief Read observation options block
Definition: Obs.f90:598
subroutine, public defaultobsidprocessor(obsrv, dis, inunitobs, iout)
@ brief Process IDstring provided for each observation
Definition: Obs.f90:246
subroutine define_fmts(this)
@ brief Define observation output formats
Definition: Obs.f90:707
subroutine obs_df(this, iout, pkgname, filtyp, dis)
@ brief Define some members of an ObsType object
Definition: Obs.f90:290
integer(i4b) function get_num(this)
@ brief Get the number of observations
Definition: Obs.f90:743
subroutine storeobstype(this, obsrvType, cumulative, indx)
@ brief Store observation type
Definition: Obs.f90:462
subroutine build_headers(this)
@ brief Build observation headers
Definition: Obs.f90:760
subroutine saveonesimval(this, obsrv, simval)
@ brief Save a simulated value
Definition: Obs.f90:431
subroutine allocate_scalars(this)
@ brief Allocate observation scalars
Definition: Obs.f90:513
subroutine obs_ot(this)
@ brief Output observation data
Definition: Obs.f90:370
class(observetype) function, pointer get_obs(this, indx)
@ brief Get an ObserveType object
Definition: Obs.f90:907
subroutine obs_ar2(this, dis)
@ brief Call procedure provided by package
Definition: Obs.f90:560
This module defines the derived type ObsOutputListType.
This module defines the derived type ObsOutputType.
Definition: ObsOutput.f90:10
This module contains the ObsUtilityModule module.
Definition: ObsUtility.f90:9
subroutine, public write_fmtd_obs(fmtc, obsrv, obsOutputList, value)
@ brief Write formatted observation
Definition: ObsUtility.f90:34
subroutine, public write_unfmtd_obs(obsrv, iprec, obsOutputList, value)
@ brief Write unformatted observation
Definition: ObsUtility.f90:80
character(len=20) access
Definition: OpenSpec.f90:7
character(len=20) form
Definition: OpenSpec.f90:7
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
integer(i4b) function, public count_errors()
Return number of errors.
Definition: Sim.f90:59
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
Definition: Sim.f90:168
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=maxcharlen) errmsg
error message string
subroutine, public table_cr(this, name, title)
Definition: Table.f90:87
real(dp), pointer, public totim
time relative to start of simulation
Definition: tdis.f90:32
A generic heterogeneous doubly-linked list.
Definition: List.f90:14