MODFLOW 6  version 6.9.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, 'e3)')
711  !
712  ! -- E3 keeps the E for any exponent; the width DIGITS + 8 holds the
713  ! longest value, a sign, "0.", the digits, and an exponent such as E-119
714  if (this%idigits == 0) then
715  this%obsfmtcont = '(G0)'
716  else
717  write (this%obsfmtcont, 50) this%idigits + 8, this%idigits
718  end if
719  end subroutine define_fmts
720 
721  !> @ brief Read observations
722  !!
723  !! Subroutine to read the observations from the observation input file
724  !! and build headers for the observation output files.
725  !!
726  !<
727  subroutine read_observations(this)
728  ! -- dummy
729  class(obstype) :: this
730  ! -- local
731  !
732  ! -- Read CONTINUOUS blocks and store observations
733  call this%read_obs_blocks(this%outputFilename)
734  !
735  ! -- build headers
736  call this%build_headers()
737  end subroutine read_observations
738 
739  !> @ brief Get the number of observations
740  !!
741  !! Function to get the number of observationns in this ObsType object.
742  !!
743  !<
744  function get_num(this)
745  ! -- return
746  integer(I4B) :: get_num !< number of observations
747  ! -- dummy
748  class(obstype) :: this
749  !
750  get_num = this%obsList%Count()
751  end function get_num
752 
753  !> @ brief Build observation headers
754  !!
755  !! Subroutine to build headers for CSV-formatted and unformatted
756  !! continuous-observation output files and write them to those files.
757  !!
758  !! Each formatted header will have the form: "time,obsname-1,obsname-2, ..."
759  !!
760  !<
761  subroutine build_headers(this)
762  ! -- module
763  use iso_fortran_env, only: int32
764  ! -- dummy
765  class(obstype), target :: this
766  ! -- local
767  integer(I4B) :: i
768  integer(I4B) :: ii
769  integer(I4B) :: idx
770  integer(I4B) :: iu
771  integer(I4B) :: num
772  integer(int32) :: nobs
773  character(len=4) :: clenobsname
774  type(observetype), pointer :: obsrv => null()
775  type(obsoutputtype), pointer :: obsOutput => null()
776  !
777  ! -- Cycle through ObsOutputList to write headers
778  ! to formatted and unformatted file(s).
779  idx = 1
780  num = this%obsOutputList%Count()
781  all_obsfiles: do i = 1, num
782  obsoutput => this%obsOutputList%Get(i)
783  nobs = obsoutput%nobs
784  iu = obsoutput%nunit
785  !
786  ! -- write header information to the formatted file
787  if (obsoutput%FormattedOutput) then
788  write (iu, '(a)', advance='NO') 'time'
789  else
790  ! -- write header to unformatted file
791  ! First 11 bytes are obs type and precision
792  if (this%iprecision == 1) then
793  ! -- single precision output
794  write (iu) 'cont single'
795  else if (this%iprecision == 2) then
796  ! -- double precision output
797  write (iu) 'cont double'
798  end if
799  ! -- write LENOBSNAME to bytes 12-15
800  write (clenobsname, '(i4)') lenobsname
801  write (iu) clenobsname
802  ! -- write blanks to complete 100-byte header
803  do ii = 16, 100
804  write (iu) ' '
805  end do
806  ! -- write NOBS
807  write (iu) nobs
808  end if
809  !
810  ! -- write observation name
811  obsfile: do ii = 1, nobs
812  obsrv => this%get_obs(idx)
813  if (obsoutput%FormattedOutput) then
814  write (iu, '(a,a)', advance='NO') ',', trim(obsrv%Name)
815  !
816  ! -- terminate the line on the last observation in file
817  if (ii == nobs) then
818  write (iu, '(a)', advance='YES') ''
819  end if
820  else
821  write (iu) obsrv%Name
822  end if
823  idx = idx + 1
824  end do obsfile
825  end do all_obsfiles
826  end subroutine build_headers
827 
828  !> @ brief Get an array of observations
829  !!
830  !! Subroutine to get an array containing all observations in this
831  !! ObsType object.
832  !!
833  !<
834  subroutine get_obs_array(this, nObs, obsArray)
835  ! -- dummy
836  class(obstype), intent(inout) :: this
837  integer(I4B), intent(out) :: nObs !< number of observations
838  type(obscontainertype), dimension(:), pointer, intent(inout) :: obsArray !< observation array
839  !
840  nobs = this%get_num()
841  if (associated(obsarray)) deallocate (obsarray)
842  allocate (obsarray(nobs))
843  !
844  ! set observations in obsArray
845  if (nobs > 0) then
846  call this%set_obs_array(nobs, obsarray)
847  end if
848  end subroutine get_obs_array
849 
850  !> @ brief Get an ObsDataType object
851  !!
852  !! Function to get an ObsDataType object for the specified observation type.
853  !!
854  !<
855  function get_obs_datum(this, obsTypeID) result(obsDatum)
856  ! -- dummy
857  class(obstype) :: this
858  character(len=*), intent(in) :: obstypeid !< observation type
859  ! -- return
860  type(obsdatatype), pointer :: obsdatum !< observation ObsDataType
861  ! -- local
862  integer(I4B) :: i
863  !
864  obsdatum => null()
865  do i = 1, maxobstypes
866  if (this%obsData(i)%ObsTypeID == obstypeid) then
867  obsdatum => this%obsData(i)
868  exit
869  end if
870  end do
871  !
872  if (.not. associated(obsdatum)) then
873  errmsg = 'Observation type not found: '//trim(obstypeid)
874  call store_error(errmsg)
875  call store_error_unit(this%inUnitObs)
876  end if
877  end function get_obs_datum
878 
879  !> @ brief Set observation array values
880  !!
881  !! Subroutine to set values in an observation array.
882  !!
883  !<
884  subroutine set_obs_array(this, nObs, obsArray)
885  ! -- dummy
886  class(obstype), intent(inout) :: this
887  integer(I4B), intent(in) :: nObs !< number of observations
888  type(obscontainertype), dimension(nObs), intent(inout) :: obsArray !< observation array
889  !
890  ! -- local
891  integer(I4B) :: i
892  integer(I4B) :: n
893  type(observetype), pointer :: obsrv => null()
894  !
895  n = this%get_num()
896  do i = 1, n
897  obsrv => this%get_obs(i)
898  obsarray(i)%obsrv => obsrv
899  end do
900  end subroutine set_obs_array
901 
902  !> @ brief Get an ObserveType object
903  !!
904  !! Subroutine to get an ObserveType object from the list of observations
905  !! using an list index.
906  !!
907  !<
908  function get_obs(this, indx) result(obsrv)
909  ! -- dummy
910  class(obstype) :: this
911  integer(I4B), intent(in) :: indx !< observation list index
912  class(observetype), pointer :: obsrv !< observation ObserveType
913  !
914  obsrv => getobsfromlist(this%obsList, indx)
915  end function get_obs
916 
917  !> @ brief Read observation blocks
918  !!
919  !! Subroutine to read CONTIGUOUS block from the observation input file.
920  !!
921  !<
922  subroutine read_obs_blocks(this, fname)
923  ! -- dummy
924  class(obstype), intent(inout) :: this
925  character(len=*), intent(inout) :: fname
926  ! -- local
927  integer(I4B) :: ierr, indexobsout, numspec
928  logical :: fmtd, found, endOfBlock
929  character(len=LENBIGLINE) :: pnamein, fnamein
930  character(len=LENHUGELINE) :: line
931  character(len=LINELENGTH) :: btagfound, message, word
932  character(len=LINELENGTH) :: title
933  character(len=LINELENGTH) :: tag
934  character(len=20) :: accarg, bin, fmtarg
935  type(observetype), pointer :: obsrv => null()
936  type(obsoutputtype), pointer :: obsOutput => null()
937  integer(I4B) :: ntabrows
938  integer(I4B) :: ntabcols
939  !
940  ! -- initialize local variables
941  numspec = -1
942  errmsg = ''
943  !
944  inquire (unit=this%parser%iuactive, name=pnamein)
945  call getfilefrompath(pnamein, fnamein)
946  !
947  if (this%echo) then
948  !
949  ! -- create the observation table
950  ! -- table dimensions
951  ntabrows = 1
952  ntabcols = 5
953  !
954  ! -- initialize table and define columns
955  title = 'OBSERVATIONS READ FROM FILE "'//trim(fnamein)//'"'
956  call table_cr(this%obstab, fnamein, title)
957  call this%obstab%table_df(ntabrows, ntabcols, this%iout, &
958  finalize=.false.)
959  tag = 'NAME'
960  call this%obstab%initialize_column(tag, lenobsname, alignment=tableft)
961  tag = 'TYPE'
962  call this%obstab%initialize_column(tag, lenobstype + 12, alignment=tableft)
963  tag = 'TIME'
964  call this%obstab%initialize_column(tag, 12, alignment=tableft)
965  tag = 'LOCATION DATA'
966  call this%obstab%initialize_column(tag, lenboundname + 2, alignment=tableft)
967  tag = 'OUTPUT FILENAME'
968  call this%obstab%initialize_column(tag, 80, alignment=tableft)
969  end if
970  !
971  found = .true.
972  readblocks: do
973  if (.not. found) exit
974  !
975  call this%parser%GetBlock('*', found, ierr, .true., .false., btagfound)
976  if (.not. found) then
977  exit readblocks
978  end if
979  this%blockTypeFound = btagfound
980  !
981  ! Get keyword, which should be FILEOUT
982  call this%parser%GetStringCaps(word)
983  if (word /= 'FILEOUT') then
984  call store_error('CONTINUOUS keyword must be followed by '// &
985  '"FILEOUT" then by filename.')
986  cycle
987  end if
988  !
989  ! -- get name of output file
990  call this%parser%GetString(fname)
991  ! Fname is the output file name defined in the BEGIN line of the block.
992  if (fname == '') then
993  message = 'Error reading OBS input file, likely due to bad'// &
994  ' block or missing file name.'
995  call store_error(message)
996  cycle
997  else if (this%obsOutputList%ContainsFile(fname)) then
998  errmsg = 'OBS outfile "'//trim(fname)// &
999  '" is provided more than once.'
1000  call store_error(errmsg)
1001  cycle
1002  end if
1003  !
1004  ! -- look for BINARY option
1005  call this%parser%GetStringCaps(bin)
1006  if (bin == 'BINARY') then
1007  fmtarg = form
1008  accarg = access
1009  fmtd = .false.
1010  else
1011  fmtarg = 'FORMATTED'
1012  accarg = 'SEQUENTIAL'
1013  fmtd = .true.
1014  end if
1015  !
1016  ! -- open the output file
1017  numspec = 0
1018  call openfile(numspec, 0, fname, 'OBS OUTPUT', fmtarg, &
1019  accarg, 'REPLACE')
1020  !
1021  ! -- add output file to list of output files and assign its
1022  ! FormattedOutput member appropriately
1023  call this%obsOutputList%Add(fname, numspec)
1024  indexobsout = this%obsOutputList%Count()
1025  obsoutput => this%obsOutputList%Get(indexobsout)
1026  obsoutput%FormattedOutput = fmtd
1027  !
1028  ! -- process lines defining observations
1029  select case (btagfound)
1030  case ('CONTINUOUS')
1031  !
1032  ! -- construct a continuous observation from each line in the block
1033  readblockcontinuous: do
1034  call this%parser%GetNextLine(endofblock)
1035  if (endofblock) exit
1036  call this%parser%GetCurrentLine(line)
1037  call constructobservation(obsrv, line, numspec, fmtd, &
1038  indexobsout, this%obsData, &
1039  this%parser%iuactive)
1040  !
1041  ! -- increment number of observations
1042  ! to be written to this output file.
1043  obsoutput => this%obsOutputList%Get(indexobsout)
1044  obsoutput%nobs = obsoutput%nobs + 1
1045  call addobstolist(this%obsList, obsrv)
1046  !
1047  ! -- write line to the observation table
1048  if (this%echo) then
1049  call obsrv%WriteTo(this%obstab, btagfound, fname)
1050  end if
1051  end do readblockcontinuous
1052  case default
1053  errmsg = 'Error: Observation block type not recognized: '// &
1054  trim(btagfound)
1055  call store_error(errmsg)
1056  end select
1057  end do readblocks
1058  !
1059  ! -- finalize the observation table
1060  if (this%echo) then
1061  call this%obstab%finalize_table()
1062  end if
1063  !
1064  ! -- determine if error condition occurs
1065  if (count_errors() > 0) then
1066  call this%parser%StoreErrorUnit()
1067  end if
1068  end subroutine read_obs_blocks
1069 
1070  !> @ brief Write observation data
1071  !!
1072  !! Subroutine to write observation data for a time step for each observation
1073  !! to the observation output file.
1074  !!
1075  !<
1076  subroutine write_obs_simvals(this)
1077  ! -- dummy
1078  class(obstype), intent(inout) :: this
1079  ! -- local
1080  integer(I4B) :: i
1081  integer(I4B) :: iprec
1082  integer(I4B) :: numobs
1083  character(len=20) :: fmtc
1084  real(DP) :: simval
1085  class(observetype), pointer :: obsrv => null()
1086  !
1087  ! Write simulated values for observations
1088  iprec = this%iprecision
1089  fmtc = this%obsfmtcont
1090  ! -- iterate through all observations
1091  numobs = this%obsList%Count()
1092  do i = 1, numobs
1093  obsrv => this%get_obs(i)
1094  ! -- continuous observation
1095  simval = obsrv%CurrentTimeStepEndValue
1096  if (obsrv%FormattedOutput) then
1097  call write_fmtd_obs(fmtc, obsrv, this%obsOutputList, simval)
1098  else
1099  call write_unfmtd_obs(obsrv, iprec, this%obsOutputList, simval)
1100  end if
1101  end do
1102  end subroutine write_obs_simvals
1103 
1104 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:172
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:1077
subroutine read_obs_blocks(this, fname)
@ brief Read observation blocks
Definition: Obs.f90:923
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:856
subroutine set_obs_array(this, nObs, obsArray)
@ brief Set observation array values
Definition: Obs.f90:885
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:728
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:835
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:745
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:762
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:909
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:169
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:35
A generic heterogeneous doubly-linked list.
Definition: List.f90:14