MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
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  !
233  ! -- return
234  return
235  end subroutine obs_cr
236 
237  !> @ brief Process IDstring provided for each observation
238  !!
239  !! Subroutine to process the IDstring provided for each observation. The
240  !! IDstring identifies the location in the model of the node(s) or feature(s)
241  !! where the simulated value is to be extracted and recorded. Subroutine
242  !!
243  !! - interprets the IDstring
244  !! - stores the location of interest in the ObserveType object that
245  !! contains information about the observation
246  !!
247  !<
248  subroutine defaultobsidprocessor(obsrv, dis, inunitobs, iout)
249  ! -- dummy
250  type(observetype), intent(inout) :: obsrv !< observation ObserveType
251  class(disbasetype), intent(in) :: dis !< discretization object
252  integer(I4B), intent(in) :: inunitobs !< observation input file unit
253  integer(I4B), intent(in) :: iout !< model list file
254  ! -- local
255  integer(I4B) :: n
256  integer(I4B) :: icol, istart, istop
257  character(len=LINELENGTH) :: string
258  logical :: flag_string
259  !
260  ! -- Initialize variables
261  string = obsrv%IDstring
262  icol = 1
263  flag_string = .true. ! Allow string to contain a boundary name
264  !
265  n = dis%noder_from_string(icol, istart, istop, inunitobs, &
266  iout, string, flag_string)
267  !
268  if (n > 0) then
269  obsrv%NodeNumber = n
270  elseif (n == -2) then
271  ! Integer can't be read from string; it's presumed to be a boundary
272  ! name (already converted to uppercase)
273  obsrv%FeatureName = string(istart:istop)
274  ! -- Observation may require summing rates from multiple boundaries,
275  ! so assign NodeNumber as a value that indicates observation
276  ! is for a named boundary or group of boundaries.
277  obsrv%NodeNumber = namedboundflag
278  else
279  errmsg = 'Error reading data from ID string'
280  call store_error(errmsg)
281  call store_error_unit(inunitobs)
282  end if
283  !
284  ! -- return
285  return
286  end subroutine defaultobsidprocessor
287 
288  ! Type-bound public procedures
289 
290  !> @ brief Define some members of an ObsType object
291  !!
292  !! Subroutine to define some members of an ObsType object.
293  !!
294  !<
295  subroutine obs_df(this, iout, pkgname, filtyp, dis)
296  ! -- dummy
297  class(obstype), intent(inout) :: this
298  integer(I4B), intent(in) :: iout !< model list file unit
299  character(len=*), intent(in) :: pkgname !< package name
300  character(len=*), intent(in) :: filtyp !< package file type
301  class(disbasetype), pointer :: dis !< discretization object
302  !
303  this%iout = iout
304  this%pkgName = pkgname
305  this%filtyp = filtyp
306  this%dis => dis
307  !
308  ! -- Initialize block parser
309  call this%parser%Initialize(this%inUnitObs, this%iout)
310  !
311  ! -- return
312  return
313  end subroutine obs_df
314 
315  !> @ brief Allocate and read package observations
316  !!
317  !! Subroutine to allocate and read observations for a package. Subroutine
318  !!
319  !! - reads OPTIONS block of OBS input file
320  !! - reads CONTINUOUS blocks of OBS input file
321  !!
322  !<
323  subroutine obs_ar(this)
324  ! -- dummy
325  class(obstype) :: this
326  !
327  call this%obs_ar1(this%pkgName)
328  if (this%active) then
329  call this%obs_ar2(this%dis)
330  end if
331  !
332  ! -- return
333  return
334  end subroutine obs_ar
335 
336  !> @ brief Advance package observations
337  !!
338  !! Subroutine to advance each package observations by resetting the
339  !! "current" value.
340  !!
341  !<
342  subroutine obs_ad(this)
343  ! -- dummy
344  class(obstype) :: this
345  ! -- local
346  integer(I4B) :: i, n
347  class(observetype), pointer :: obsrv => null()
348  !
349  n = this%get_num()
350  do i = 1, n
351  obsrv => this%get_obs(i)
352  call obsrv%ResetCurrentValue()
353  end do
354  !
355  ! -- return
356  return
357  end subroutine obs_ad
358 
359  !> @ brief Clear observation output lines
360  !!
361  !! Subroutine to clear output lines in preparation for new rows of
362  !! continuous observations.
363  !!
364  !<
365  subroutine obs_bd_clear(this)
366  ! -- dummy
367  class(obstype), target :: this
368  !
369  call this%obsOutputList%ResetAllObsEmptyLines()
370  !
371  ! -- return
372  return
373  end subroutine obs_bd_clear
374 
375  !> @ brief Output observation data
376  !!
377  !! Subroutine to output observation data. Subroutine
378  !!
379  !! - stores each simulated value into its ObserveType object
380  !! - writes each simulated value to it ObsOutputList object
381  !! _ writes contents of ObsOutputList to output file
382  !!
383  !! This procedure should NOT be called from a package's _ot procedure
384  !! because the package _ot procedure may not be called every time step.
385  !!
386  !<
387  subroutine obs_ot(this)
388  ! -- dummy
389  class(obstype), intent(inout) :: this
390  !
391  if (this%npakobs > 0) then
392  call this%write_obs_simvals()
393  call this%obsOutputList%WriteAllObsLineReturns()
394  end if
395  !
396  ! -- return
397  return
398  end subroutine obs_ot
399 
400  !> @ brief Deallocate observation data
401  !!
402  !! Subroutine to deallocate observation data.
403  !!
404  !<
405  subroutine obs_da(this)
406  ! -- dummy
407  class(obstype), intent(inout) :: this
408  ! -- local
409  integer(I4B) :: i
410  class(observetype), pointer :: obsrv => null()
411  !
412  deallocate (this%active)
413  deallocate (this%inputFilename)
414  deallocate (this%obsData)
415  !
416  ! -- observation table object
417  if (associated(this%obstab)) then
418  call this%obstab%table_da()
419  deallocate (this%obstab)
420  nullify (this%obstab)
421  end if
422  !
423  ! -- deallocate pakobs components and pakobs
424  if (associated(this%pakobs)) then
425  do i = 1, this%npakobs
426  obsrv => this%pakobs(i)%obsrv
427  call obsrv%da()
428  deallocate (obsrv)
429  nullify (this%pakobs(i)%obsrv)
430  end do
431  deallocate (this%pakobs)
432  end if
433  !
434  ! -- deallocate obsOutputList
435  call this%obsOutputList%DeallocObsOutputList()
436  deallocate (this%obsOutputList)
437  !
438  ! -- deallocate obslist
439  call this%obslist%Clear()
440  !
441  ! -- nullify
442  nullify (this%inUnitObs)
443  !
444  ! -- return
445  return
446  end subroutine obs_da
447 
448  !> @ brief Save a simulated value
449  !!
450  !! Subroutine to save or accumulate a simulated value to its ObserveType
451  !! object.
452  !!
453  !<
454  subroutine saveonesimval(this, obsrv, simval)
455  ! -- dummy
456  class(obstype) :: this
457  class(observetype), intent(inout) :: obsrv !< observation ObserveType
458  real(DP), intent(in) :: simval !< simulated value
459  ! -- local
460  character(len=LENOBSTYPE) :: obsTypeID
461  type(obsdatatype), pointer :: obsDatum => null()
462  !
463  ! -- initialize variables
464  obstypeid = obsrv%ObsTypeId
465  obsdatum => this%get_obs_datum(obstypeid)
466  !
467  ! -- save current simulation time
468  obsrv%CurrentTimeStepEndTime = totim
469  !
470  ! -- assign or accumulate simulated value
471  if (obsdatum%Cumulative .and. simval /= dnodata) then
472  obsrv%CurrentTimeStepEndValue = obsrv%CurrentTimeStepEndValue + simval
473  else
474  obsrv%CurrentTimeStepEndValue = simval
475  end if
476  !
477  ! -- return
478  return
479  end subroutine saveonesimval
480 
481  !> @ brief Store observation type
482  !!
483  !! Subroutine to store type name and related information for an
484  !! observation type that belongs to a package or model in the
485  !! obsData array.
486  !!
487  !<
488  subroutine storeobstype(this, obsrvType, cumulative, indx)
489  ! -- dummy
490  class(obstype), intent(inout) :: this
491  character(len=*), intent(in) :: obsrvType !< observation type
492  ! cumulative: Accumulate simulated values for multiple boundaries
493  logical, intent(in) :: cumulative !< logical indicating if the observation should be accumulated
494  integer(I4B), intent(out) :: indx !< observation index
495  ! -- local
496  integer(I4B) :: i
497  character(len=LENOBSTYPE) :: obsTypeUpper
498  character(len=100) :: msg
499  !
500  ! -- Ensure that obsrvType is not blank
501  if (obsrvtype == '') then
502  msg = 'Programmer error: Invalid argument in store_obs_type.'
503  call store_error(msg, terminate=.true.)
504  end if
505  !
506  ! -- Find first unused element
507  indx = -1
508  do i = 1, maxobstypes
509  if (this%obsData(i)%ObsTypeID /= '') cycle
510  indx = i
511  exit
512  end do
513  !
514  ! -- Ensure that array size is not exceeded
515  if (indx == -1) then
516  msg = 'Size of obsData array is insufficient; ' &
517  //'need to increase MAXOBSTYPES.'
518  call store_error(msg)
519  call store_error_unit(this%inUnitObs)
520  end if
521  !
522  ! -- Convert character argument to upper case
523  obstypeupper = obsrvtype
524  call upcase(obstypeupper)
525  !
526  ! -- Assign members
527  this%obsData(indx)%ObsTypeID = obstypeupper
528  this%obsData(indx)%Cumulative = cumulative
529  !
530  ! -- return
531  return
532  end subroutine storeobstype
533 
534  ! Type-bound private procedures
535 
536  !> @ brief Allocate observation scalars
537  !!
538  !! Subroutine to allocate and initialize memory for non-allocatable
539  ! members (scalars).
540  !!
541  !<
542  subroutine allocate_scalars(this)
543  ! -- dummy
544  class(obstype) :: this
545  !
546  allocate (this%active)
547  allocate (this%inputFilename)
548  allocate (this%obsOutputList)
549  allocate (this%obsData(maxobstypes))
550  !
551  ! -- Initialize
552  this%active = .false.
553  this%inputFilename = ''
554  !
555  ! -- return
556  return
557  end subroutine allocate_scalars
558 
559  !> @ brief Read observation options and output formats
560  !!
561  !! Subroutine to read the options block in the observation input file and
562  !! define output formats.
563  !!
564  !<
565  subroutine obs_ar1(this, pkgname)
566  ! -- dummy
567  class(obstype), intent(inout) :: this
568  character(len=*), intent(in) :: pkgname !< package name
569  ! -- formats
570 10 format(/, 'The observation utility is active for "', a, '"')
571  !
572  if (this%inUnitObs > 0) then
573  this%active = .true.
574  !
575  ! -- Indicate that OBS is active
576  write (this%iout, 10) trim(pkgname)
577  !
578  ! -- Read Options block
579  call this%read_obs_options()
580  !
581  ! -- define output formats
582  call this%define_fmts()
583  end if
584  !
585  ! -- return
586  return
587  end subroutine obs_ar1
588 
589  !> @ brief Call procedure provided by package
590  !!
591  !! Subroutine to call procedure provided by package to interpret IDstring
592  !! and store required data.
593  !!
594  !<
595  subroutine obs_ar2(this, dis)
596  ! -- dummy
597  class(obstype), intent(inout) :: this
598  class(disbasetype) :: dis !< discretization object
599  ! -- local
600  integer(I4B) :: i
601  type(obsdatatype), pointer :: obsDat => null()
602  character(len=LENOBSTYPE) :: obsTypeID
603  class(observetype), pointer :: obsrv => null()
604  !
605  call this%read_observations()
606  ! -- allocate and set observation array
607  call this%get_obs_array(this%npakobs, this%pakobs)
608  !
609  do i = 1, this%npakobs
610  obsrv => this%pakobs(i)%obsrv
611  ! -- Call IDstring processor procedure provided by package
612  obstypeid = obsrv%ObsTypeId
613  obsdat => this%get_obs_datum(obstypeid)
614  if (associated(obsdat%ProcessIdPtr)) then
615  call obsdat%ProcessIdPtr(obsrv, dis, &
616  this%inUnitObs, this%iout)
617  else
618  call defaultobsidprocessor(obsrv, dis, &
619  this%inUnitObs, this%iout)
620  end if
621  end do
622  !
623  if (count_errors() > 0) then
624  call store_error_unit(this%inunitobs)
625  end if
626  !
627  ! -- return
628  return
629  end subroutine obs_ar2
630 
631  !> @ brief Read observation options block
632  !!
633  !! Subroutine to read the options block in the observation input file.
634  !!
635  !<
636  subroutine read_obs_options(this)
637  ! -- dummy
638  class(obstype) :: this
639  ! -- local
640  integer(I4B) :: iin
641  integer(I4B) :: ierr
642  integer(I4B) :: localprecision
643  integer(I4B) :: localdigits
644  character(len=40) :: keyword
645  character(len=LINELENGTH) :: fname
646  type(listtype), pointer :: lineList => null()
647  logical :: continueread, found, endOfBlock
648  ! -- formats
649 10 format('No options block found in OBS input. Defaults will be used.')
650 40 format('Text output number of digits of precision set to: ', i2)
651 50 format('Text output number of digits set to internal representation (G0).')
652 60 format(/, 'Processing observation options:',/)
653  !
654  localprecision = 0
655  localdigits = -1
656  linelist => null()
657  !
658  ! -- Find and store file name
659  iin = this%inUnitObs
660  inquire (unit=iin, name=fname)
661  this%inputFilename = fname
662  !
663  ! -- Read Options block
664  continueread = .false.
665  ierr = 0
666  !
667  ! -- get BEGIN line of OPTIONS block
668  call this%parser%GetBlock('OPTIONS', found, ierr, &
669  supportopenclose=.true., blockrequired=.false.)
670  if (ierr /= 0) then
671  ! end of file
672  errmsg = 'End-of-file encountered while searching for'// &
673  ' OPTIONS in OBS '// &
674  'input file "'//trim(this%inputFilename)//'"'
675  call store_error(errmsg)
676  call this%parser%StoreErrorUnit()
677  elseif (.not. found) then
678  this%blockTypeFound = ''
679  if (this%iout > 0) write (this%iout, 10)
680  end if
681  !
682  ! -- parse OPTIONS entries
683  if (found) then
684  write (this%iout, 60)
685  readblockoptions: do
686  call this%parser%GetNextLine(endofblock)
687  if (endofblock) exit
688  call this%parser%GetStringCaps(keyword)
689  select case (keyword)
690  case ('DIGITS')
691  !
692  ! -- error if digits already read
693  if (localdigits /= -1) then
694  errmsg = 'Error in OBS input: DIGITS has already been defined'
695  call store_error(errmsg)
696  exit readblockoptions
697  end if
698  !
699  ! -- Specifies number of significant digits used writing simulated
700  ! values to a text file. Default is stored digits.
701  !
702  ! -- Read integer value
703  localdigits = this%parser%GetInteger()
704  !
705  ! -- Set localdigits to valid value: 0, or 2 to 16
706  if (localdigits == 0) then
707  write (this%iout, 50)
708  else if (localdigits < 1) then
709  errmsg = 'Error in OBS input: Invalid value for DIGITS option'
710  call store_error(errmsg)
711  exit readblockoptions
712  else
713  if (localdigits < 2) localdigits = 2
714  if (localdigits > 16) localdigits = 16
715  write (this%iout, 40) localdigits
716  end if
717  case ('PRINT_INPUT')
718  this%echo = .true.
719  write (this%iout, '(a)') 'The PRINT_INPUT option has been specified.'
720  case default
721  errmsg = 'Error in OBS input: Unrecognized option: '// &
722  trim(keyword)
723  call store_error(errmsg)
724  exit readblockoptions
725  end select
726  end do readblockoptions
727  end if
728  !
729  if (count_errors() > 0) then
730  call this%parser%StoreErrorUnit()
731  end if
732  !
733  write (this%iout, '(1x)')
734  !
735  ! -- Assign type variables
736  if (localprecision > 0) this%iprecision = localprecision
737  if (localdigits >= 0) this%idigits = localdigits
738  !
739  ! -- return
740  return
741  end subroutine read_obs_options
742 
743  !> @ brief Define observation output formats
744  !!
745  !! Subroutine to define observation output formats.
746  !!
747  !<
748  subroutine define_fmts(this)
749  ! -- dummy
750  class(obstype) :: this
751  ! formats
752 50 format('(g', i2.2, '.', i2.2, ')')
753  !
754  if (this%idigits == 0) then
755  this%obsfmtcont = '(G0)'
756  else
757  write (this%obsfmtcont, 50) this%idigits + 7, this%idigits
758  end if
759  !
760  ! -- return
761  return
762  end subroutine define_fmts
763 
764  !> @ brief Read observations
765  !!
766  !! Subroutine to read the observations from the observation input file
767  !! and build headers for the observation output files.
768  !!
769  !<
770  subroutine read_observations(this)
771  ! -- dummy
772  class(obstype) :: this
773  ! -- local
774  !
775  ! -- Read CONTINUOUS blocks and store observations
776  call this%read_obs_blocks(this%outputFilename)
777  !
778  ! -- build headers
779  call this%build_headers()
780  !
781  ! -- return
782  return
783  end subroutine read_observations
784 
785  !> @ brief Get the number of observations
786  !!
787  !! Function to get the number of observationns in this ObsType object.
788  !!
789  !<
790  function get_num(this)
791  ! -- return
792  integer(I4B) :: get_num !< number of observations
793  ! -- dummy
794  class(obstype) :: this
795  !
796  get_num = this%obsList%Count()
797  !
798  ! -- return
799  return
800  end function get_num
801 
802  !> @ brief Build observation headers
803  !!
804  !! Subroutine to build headers for CSV-formatted and unformatted
805  !! continuous-observation output files and write them to those files.
806  !!
807  !! Each formatted header will have the form: "time,obsname-1,obsname-2, ..."
808  !!
809  !<
810  subroutine build_headers(this)
811  ! -- module
812  use iso_fortran_env, only: int32
813  ! -- dummy
814  class(obstype), target :: this
815  ! -- local
816  integer(I4B) :: i
817  integer(I4B) :: ii
818  integer(I4B) :: idx
819  integer(I4B) :: iu
820  integer(I4B) :: num
821  integer(int32) :: nobs
822  character(len=4) :: clenobsname
823  type(observetype), pointer :: obsrv => null()
824  type(obsoutputtype), pointer :: obsOutput => null()
825  !
826  ! -- Cycle through ObsOutputList to write headers
827  ! to formatted and unformatted file(s).
828  idx = 1
829  num = this%obsOutputList%Count()
830  all_obsfiles: do i = 1, num
831  obsoutput => this%obsOutputList%Get(i)
832  nobs = obsoutput%nobs
833  iu = obsoutput%nunit
834  !
835  ! -- write header information to the formatted file
836  if (obsoutput%FormattedOutput) then
837  write (iu, '(a)', advance='NO') 'time'
838  else
839  ! -- write header to unformatted file
840  ! First 11 bytes are obs type and precision
841  if (this%iprecision == 1) then
842  ! -- single precision output
843  write (iu) 'cont single'
844  else if (this%iprecision == 2) then
845  ! -- double precision output
846  write (iu) 'cont double'
847  end if
848  ! -- write LENOBSNAME to bytes 12-15
849  write (clenobsname, '(i4)') lenobsname
850  write (iu) clenobsname
851  ! -- write blanks to complete 100-byte header
852  do ii = 16, 100
853  write (iu) ' '
854  end do
855  ! -- write NOBS
856  write (iu) nobs
857  end if
858  !
859  ! -- write observation name
860  obsfile: do ii = 1, nobs
861  obsrv => this%get_obs(idx)
862  if (obsoutput%FormattedOutput) then
863  write (iu, '(a,a)', advance='NO') ',', trim(obsrv%Name)
864  !
865  ! -- terminate the line on the last observation in file
866  if (ii == nobs) then
867  write (iu, '(a)', advance='YES') ''
868  end if
869  else
870  write (iu) obsrv%Name
871  end if
872  idx = idx + 1
873  end do obsfile
874  end do all_obsfiles
875  !
876  ! -- return
877  return
878  end subroutine build_headers
879 
880  !> @ brief Get an array of observations
881  !!
882  !! Subroutine to get an array containing all observations in this
883  !! ObsType object.
884  !!
885  !<
886  subroutine get_obs_array(this, nObs, obsArray)
887  ! -- dummy
888  class(obstype), intent(inout) :: this
889  integer(I4B), intent(out) :: nObs !< number of observations
890  type(obscontainertype), dimension(:), pointer, intent(inout) :: obsArray !< observation array
891  !
892  nobs = this%get_num()
893  if (associated(obsarray)) deallocate (obsarray)
894  allocate (obsarray(nobs))
895  !
896  ! set observations in obsArray
897  if (nobs > 0) then
898  call this%set_obs_array(nobs, obsarray)
899  end if
900  !
901  ! -- return
902  return
903  end subroutine get_obs_array
904 
905  !> @ brief Get an ObsDataType object
906  !!
907  !! Function to get an ObsDataType object for the specified observation type.
908  !!
909  !<
910  function get_obs_datum(this, obsTypeID) result(obsDatum)
911  ! -- dummy
912  class(obstype) :: this
913  character(len=*), intent(in) :: obstypeid !< observation type
914  ! -- return
915  type(obsdatatype), pointer :: obsdatum !< observation ObsDataType
916  ! -- local
917  integer(I4B) :: i
918  !
919  obsdatum => null()
920  do i = 1, maxobstypes
921  if (this%obsData(i)%ObsTypeID == obstypeid) then
922  obsdatum => this%obsData(i)
923  exit
924  end if
925  end do
926  !
927  if (.not. associated(obsdatum)) then
928  errmsg = 'Observation type not found: '//trim(obstypeid)
929  call store_error(errmsg)
930  call store_error_unit(this%inUnitObs)
931  end if
932  !
933  ! -- return
934  return
935  end function get_obs_datum
936 
937  !> @ brief Set observation array values
938  !!
939  !! Subroutine to set values in an observation array.
940  !!
941  !<
942  subroutine set_obs_array(this, nObs, obsArray)
943  ! -- dummy
944  class(obstype), intent(inout) :: this
945  integer(I4B), intent(in) :: nObs !< number of observations
946  type(obscontainertype), dimension(nObs), intent(inout) :: obsArray !< observation array
947  !
948  ! -- local
949  integer(I4B) :: i
950  integer(I4B) :: n
951  type(observetype), pointer :: obsrv => null()
952  !
953  n = this%get_num()
954  do i = 1, n
955  obsrv => this%get_obs(i)
956  obsarray(i)%obsrv => obsrv
957  end do
958  !
959  ! -- return
960  return
961  end subroutine set_obs_array
962 
963  !> @ brief Get an ObserveType object
964  !!
965  !! Subroutine to get an ObserveType object from the list of observations
966  !! using an list index.
967  !!
968  !<
969  function get_obs(this, indx) result(obsrv)
970  ! -- dummy
971  class(obstype) :: this
972  integer(I4B), intent(in) :: indx !< observation list index
973  class(observetype), pointer :: obsrv !< observation ObserveType
974  !
975  obsrv => getobsfromlist(this%obsList, indx)
976  !
977  ! -- return
978  return
979  end function get_obs
980 
981  !> @ brief Read observation blocks
982  !!
983  !! Subroutine to read CONTIGUOUS block from the observation input file.
984  !!
985  !<
986  subroutine read_obs_blocks(this, fname)
987  ! -- dummy
988  class(obstype), intent(inout) :: this
989  character(len=*), intent(inout) :: fname
990  ! -- local
991  integer(I4B) :: ierr, indexobsout, numspec
992  logical :: fmtd, found, endOfBlock
993  character(len=LENBIGLINE) :: pnamein, fnamein
994  character(len=LENHUGELINE) :: line
995  character(len=LINELENGTH) :: btagfound, message, word
996  character(len=LINELENGTH) :: title
997  character(len=LINELENGTH) :: tag
998  character(len=20) :: accarg, bin, fmtarg
999  type(observetype), pointer :: obsrv => null()
1000  type(obsoutputtype), pointer :: obsOutput => null()
1001  integer(I4B) :: ntabrows
1002  integer(I4B) :: ntabcols
1003  !
1004  ! -- initialize local variables
1005  numspec = -1
1006  errmsg = ''
1007  !
1008  inquire (unit=this%parser%iuactive, name=pnamein)
1009  call getfilefrompath(pnamein, fnamein)
1010  !
1011  if (this%echo) then
1012  !
1013  ! -- create the observation table
1014  ! -- table dimensions
1015  ntabrows = 1
1016  ntabcols = 5
1017  !
1018  ! -- initialize table and define columns
1019  title = 'OBSERVATIONS READ FROM FILE "'//trim(fnamein)//'"'
1020  call table_cr(this%obstab, fnamein, title)
1021  call this%obstab%table_df(ntabrows, ntabcols, this%iout, &
1022  finalize=.false.)
1023  tag = 'NAME'
1024  call this%obstab%initialize_column(tag, lenobsname, alignment=tableft)
1025  tag = 'TYPE'
1026  call this%obstab%initialize_column(tag, lenobstype + 12, alignment=tableft)
1027  tag = 'TIME'
1028  call this%obstab%initialize_column(tag, 12, alignment=tableft)
1029  tag = 'LOCATION DATA'
1030  call this%obstab%initialize_column(tag, lenboundname + 2, alignment=tableft)
1031  tag = 'OUTPUT FILENAME'
1032  call this%obstab%initialize_column(tag, 80, alignment=tableft)
1033  end if
1034  !
1035  found = .true.
1036  readblocks: do
1037  if (.not. found) exit
1038  !
1039  call this%parser%GetBlock('*', found, ierr, .true., .false., btagfound)
1040  if (.not. found) then
1041  exit readblocks
1042  end if
1043  this%blockTypeFound = btagfound
1044  !
1045  ! Get keyword, which should be FILEOUT
1046  call this%parser%GetStringCaps(word)
1047  if (word /= 'FILEOUT') then
1048  call store_error('CONTINUOUS keyword must be followed by '// &
1049  '"FILEOUT" then by filename.')
1050  cycle
1051  end if
1052  !
1053  ! -- get name of output file
1054  call this%parser%GetString(fname)
1055  ! Fname is the output file name defined in the BEGIN line of the block.
1056  if (fname == '') then
1057  message = 'Error reading OBS input file, likely due to bad'// &
1058  ' block or missing file name.'
1059  call store_error(message)
1060  cycle
1061  else if (this%obsOutputList%ContainsFile(fname)) then
1062  errmsg = 'OBS outfile "'//trim(fname)// &
1063  '" is provided more than once.'
1064  call store_error(errmsg)
1065  cycle
1066  end if
1067  !
1068  ! -- look for BINARY option
1069  call this%parser%GetStringCaps(bin)
1070  if (bin == 'BINARY') then
1071  fmtarg = form
1072  accarg = access
1073  fmtd = .false.
1074  else
1075  fmtarg = 'FORMATTED'
1076  accarg = 'SEQUENTIAL'
1077  fmtd = .true.
1078  end if
1079  !
1080  ! -- open the output file
1081  numspec = 0
1082  call openfile(numspec, 0, fname, 'OBS OUTPUT', fmtarg, &
1083  accarg, 'REPLACE')
1084  !
1085  ! -- add output file to list of output files and assign its
1086  ! FormattedOutput member appropriately
1087  call this%obsOutputList%Add(fname, numspec)
1088  indexobsout = this%obsOutputList%Count()
1089  obsoutput => this%obsOutputList%Get(indexobsout)
1090  obsoutput%FormattedOutput = fmtd
1091  !
1092  ! -- process lines defining observations
1093  select case (btagfound)
1094  case ('CONTINUOUS')
1095  !
1096  ! -- construct a continuous observation from each line in the block
1097  readblockcontinuous: do
1098  call this%parser%GetNextLine(endofblock)
1099  if (endofblock) exit
1100  call this%parser%GetCurrentLine(line)
1101  call constructobservation(obsrv, line, numspec, fmtd, &
1102  indexobsout, this%obsData, &
1103  this%parser%iuactive)
1104  !
1105  ! -- increment number of observations
1106  ! to be written to this output file.
1107  obsoutput => this%obsOutputList%Get(indexobsout)
1108  obsoutput%nobs = obsoutput%nobs + 1
1109  call addobstolist(this%obsList, obsrv)
1110  !
1111  ! -- write line to the observation table
1112  if (this%echo) then
1113  call obsrv%WriteTo(this%obstab, btagfound, fname)
1114  end if
1115  end do readblockcontinuous
1116  case default
1117  errmsg = 'Error: Observation block type not recognized: '// &
1118  trim(btagfound)
1119  call store_error(errmsg)
1120  end select
1121  end do readblocks
1122  !
1123  ! -- finalize the observation table
1124  if (this%echo) then
1125  call this%obstab%finalize_table()
1126  end if
1127  !
1128  ! -- determine if error condition occurs
1129  if (count_errors() > 0) then
1130  call this%parser%StoreErrorUnit()
1131  end if
1132  !
1133  ! -- return
1134  return
1135  end subroutine read_obs_blocks
1136 
1137  !> @ brief Write observation data
1138  !!
1139  !! Subroutine to write observation data for a time step for each observation
1140  !! to the observation output file.
1141  !!
1142  !<
1143  subroutine write_obs_simvals(this)
1144  ! -- dummy
1145  class(obstype), intent(inout) :: this
1146  ! -- local
1147  integer(I4B) :: i
1148  integer(I4B) :: iprec
1149  integer(I4B) :: numobs
1150  character(len=20) :: fmtc
1151  real(DP) :: simval
1152  class(observetype), pointer :: obsrv => null()
1153  !
1154  ! Write simulated values for observations
1155  iprec = this%iprecision
1156  fmtc = this%obsfmtcont
1157  ! -- iterate through all observations
1158  numobs = this%obsList%Count()
1159  do i = 1, numobs
1160  obsrv => this%get_obs(i)
1161  ! -- continuous observation
1162  simval = obsrv%CurrentTimeStepEndValue
1163  if (obsrv%FormattedOutput) then
1164  call write_fmtd_obs(fmtc, obsrv, this%obsOutputList, simval)
1165  else
1166  call write_unfmtd_obs(obsrv, iprec, this%obsOutputList, simval)
1167  end if
1168  end do
1169  !
1170  ! --return
1171  return
1172  end subroutine write_obs_simvals
1173 
1174 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:44
@ tableft
left justified table column
Definition: Constants.f90:170
integer(i4b), parameter lenpackagename
maximum length of the package name
Definition: Constants.f90:22
integer(i4b), parameter namedboundflag
named bound flag
Definition: Constants.f90:48
real(dp), parameter dnodata
real no data constant
Definition: Constants.f90:94
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:47
integer(i4b), parameter lenftype
maximum length of a package type (DIS, WEL, OC, etc.)
Definition: Constants.f90:38
integer(i4b), parameter lenboundname
maximum length of a bound name
Definition: Constants.f90:35
integer(i4b), parameter lenobsname
maximum length of a observation name
Definition: Constants.f90:39
integer(i4b), parameter maxcharlen
maximum length of char string
Definition: Constants.f90:46
integer(i4b), parameter lenobstype
maximum length of a observation type (CONTINUOUS)
Definition: Constants.f90:40
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:245
type(observetype) function, pointer, public getobsfromlist(list, idx)
@ brief Get an ObserveType from a list
Definition: Observe.f90:359
subroutine, public addobstolist(list, obs)
@ brief Add a ObserveType to a list
Definition: Observe.f90:340
This module contains the derived type ObsType.
Definition: Obs.f90:127
subroutine write_obs_simvals(this)
@ brief Write observation data
Definition: Obs.f90:1144
subroutine read_obs_blocks(this, fname)
@ brief Read observation blocks
Definition: Obs.f90:987
subroutine obs_da(this)
@ brief Deallocate observation data
Definition: Obs.f90:406
type(obsdatatype) function, pointer get_obs_datum(this, obsTypeID)
@ brief Get an ObsDataType object
Definition: Obs.f90:911
subroutine set_obs_array(this, nObs, obsArray)
@ brief Set observation array values
Definition: Obs.f90:943
subroutine obs_bd_clear(this)
@ brief Clear observation output lines
Definition: Obs.f90:366
subroutine obs_ar(this)
@ brief Allocate and read package observations
Definition: Obs.f90:324
subroutine obs_ar1(this, pkgname)
@ brief Read observation options and output formats
Definition: Obs.f90:566
subroutine read_observations(this)
@ brief Read observations
Definition: Obs.f90:771
subroutine obs_ad(this)
@ brief Advance package observations
Definition: Obs.f90:343
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:887
subroutine read_obs_options(this)
@ brief Read observation options block
Definition: Obs.f90:637
subroutine, public defaultobsidprocessor(obsrv, dis, inunitobs, iout)
@ brief Process IDstring provided for each observation
Definition: Obs.f90:249
subroutine define_fmts(this)
@ brief Define observation output formats
Definition: Obs.f90:749
subroutine obs_df(this, iout, pkgname, filtyp, dis)
@ brief Define some members of an ObsType object
Definition: Obs.f90:296
integer(i4b) function get_num(this)
@ brief Get the number of observations
Definition: Obs.f90:791
subroutine storeobstype(this, obsrvType, cumulative, indx)
@ brief Store observation type
Definition: Obs.f90:489
subroutine build_headers(this)
@ brief Build observation headers
Definition: Obs.f90:811
subroutine saveonesimval(this, obsrv, simval)
@ brief Save a simulated value
Definition: Obs.f90:455
subroutine allocate_scalars(this)
@ brief Allocate observation scalars
Definition: Obs.f90:543
subroutine obs_ot(this)
@ brief Output observation data
Definition: Obs.f90:388
class(observetype) function, pointer get_obs(this, indx)
@ brief Get an ObserveType object
Definition: Obs.f90:970
subroutine obs_ar2(this, dis)
@ brief Call procedure provided by package
Definition: Obs.f90:596
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:83
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:85
real(dp), pointer, public totim
time relative to start of simulation
Definition: tdis.f90:32
A generic heterogeneous doubly-linked list.
Definition: List.f90:10