29 character(len=LENTIMESERIESNAME),
public :: name =
''
31 real(dp),
private :: sfac =
done
32 logical,
public :: autodeallocate = .true.
33 type(
listtype),
pointer,
private :: list => null()
53 procedure,
private :: da =>
ts_da
64 integer(I4B),
public :: inunit = 0
65 integer(I4B),
public :: iout = 0
66 integer(I4B),
public :: ntimeseries = 0
67 logical,
public :: finishedreading = .false.
68 character(len=LINELENGTH),
public :: datafile =
''
70 pointer,
contiguous,
public :: timeseries => null()
99 allocate (newtimeseriesfile)
100 allocate (newtimeseriesfile%parser)
107 class(*),
pointer,
intent(inout) :: obj
112 if (.not.
associated(obj))
return
124 class(*),
pointer,
intent(inout) :: obj
129 if (.not.
associated(obj))
return
141 type(
listtype),
intent(inout) :: list
144 class(*),
pointer :: obj => null()
154 type(
listtype),
intent(inout) :: list
155 integer(I4B),
intent(in) :: idx
159 class(*),
pointer :: obj => null()
161 obj => list%GetItem(idx)
164 if (.not.
associated(res))
then
182 n1 = ts1%list%Count()
183 n2 = ts2%list%Count()
190 tsr1 => ts1%GetNextTimeSeriesRecord()
191 tsr2 => ts2%GetNextTimeSeriesRecord()
192 if (tsr1%tsrTime /= tsr2%tsrTime)
return
193 if (tsr1%tsrValue /= tsr2%tsrValue)
return
209 function getvalue(this, time0, time1, extendToEndOfSimulation)
214 real(dp),
intent(in) :: time0
215 real(dp),
intent(in) :: time1
216 logical,
intent(in),
optional :: extendtoendofsimulation
220 if (
present(extendtoendofsimulation))
then
221 extend = extendtoendofsimulation
226 select case (this%iMethod)
228 getvalue = this%get_average_value(time0, time1, extend)
230 getvalue = this%get_value_at_time(time1, extend)
242 character(len=*),
intent(in) :: name
243 logical,
intent(in),
optional :: autoDeallocate
245 character(len=LENTIMESERIESNAME) :: tsNameTemp
248 this%tsfile => tsfile
252 this%Name = tsnametemp
256 if (
present(autodeallocate)) this%autoDeallocate = autodeallocate
262 if (this%Name ==
'')
then
263 errmsg =
'Name not specified for time series.'
273 real(DP),
intent(in) :: time
277 real(DP) :: time0, time1
283 class(*),
pointer :: obj => null()
285 tsrecearlier => null()
288 if (
associated(this%list%firstNode))
then
289 currnode => this%list%firstNode
295 if (
associated(currnode))
then
296 if (
associated(currnode%nextNode))
then
297 obj => currnode%nextNode%GetItem()
299 if (tsr%tsrTime < time .and. .not.
is_close(tsr%tsrTime, time))
then
300 currnode => currnode%nextNode
306 if (.not. this%read_next_record())
exit
313 if (
associated(currnode))
then
317 obj => tsnode0%GetItem()
319 time0 = tsrec0%tsrTime
320 do while (time0 > time)
321 if (
associated(tsnode0%prevNode))
then
322 tsnode0 => tsnode0%prevNode
323 obj => tsnode0%GetItem()
325 time0 = tsrec0%tsrTime
333 obj => tsnode1%GetItem()
335 time1 = tsrec1%tsrTime
336 do while (time1 < time .and. .not.
is_close(time1, time))
337 if (
associated(tsnode1%nextNode))
then
338 tsnode1 => tsnode1%nextNode
339 obj => tsnode1%GetItem()
341 time1 = tsrec1%tsrTime
344 if (.not. this%read_next_record())
then
353 if (time0 < time .or.
is_close(time0, time)) tsrecearlier => tsrec0
354 if (time1 > time .or.
is_close(time1, time)) tsreclater => tsrec1
365 real(DP),
intent(in) :: time
366 type(
listnodetype),
pointer,
intent(inout) :: nodeEarlier
369 real(DP) :: time0, time1
377 class(*),
pointer :: obj => null()
379 tsrecearlier => null()
381 nodeearlier => null()
384 if (
associated(this%list%firstNode))
then
385 currnode => this%list%firstNode
391 if (
associated(currnode))
then
392 if (
associated(currnode%nextNode))
then
393 obj => currnode%nextNode%GetItem()
395 if (tsr%tsrTime < time .and. .not.
is_close(tsr%tsrTime, time))
then
396 currnode => currnode%nextNode
408 if (
associated(currnode))
then
412 obj => tsnode0%GetItem()
414 time0 = tsrec0%tsrTime
415 do while (time0 > time)
416 if (
associated(tsnode0%prevNode))
then
417 tsnode0 => tsnode0%prevNode
418 obj => tsnode0%GetItem()
420 time0 = tsrec0%tsrTime
428 obj => tsnode1%GetItem()
430 time1 = tsrec1%tsrTime
431 do while (time1 < time .and. .not.
is_close(time1, time))
432 if (
associated(tsnode1%nextNode))
then
433 tsnode1 => tsnode1%nextNode
434 obj => tsnode1%GetItem()
436 time1 = tsrec1%tsrTime
444 if (time0 < time .or.
is_close(time0, time))
then
445 tsrecearlier => tsrec0
446 nodeearlier => tsnode0
448 if (time1 > time .or.
is_close(time1, time))
then
463 if (this%tsfile%finishedReading)
then
470 this%tsfile%finishedReading = .true.
483 real(dp),
intent(in) :: time
484 logical,
intent(in) :: extendtoendofsimulation
487 real(dp) :: ratio, time0, time1, timediff, timediffi, val0, val1, &
492 10
format(
'Error getting value at time ', g10.3,
' for time series "', a,
'"')
495 call this%get_surrounding_records(time, tsrearlier, tsrlater)
496 if (
associated(tsrearlier))
then
497 if (
associated(tsrlater))
then
505 time0 = tsrearlier%tsrTime
506 time1 = tsrlater%tsrtime
507 timediff = time1 - time0
508 timediffi = time - time0
509 if (timediff > 0)
then
510 ratio = timediffi / timediff
515 val0 = tsrearlier%tsrValue
516 val1 = tsrlater%tsrValue
517 valdiff = val1 - val0
523 if (extendtoendofsimulation .or.
is_close(tsrearlier%tsrTime, time))
then
536 if (
associated(tsrlater))
then
537 if (
is_close(tsrlater%tsrTime, time))
then
551 write (
errmsg, 10) time, trim(this%Name)
566 real(dp),
intent(in) :: time0
567 real(dp),
intent(in) :: time1
568 logical,
intent(in) :: extendtoendofsimulation
570 real(dp) :: area, currtime, nexttime, ratio0, ratio1, t0, t01, t1, &
571 timediff,
value, value0, value1, valuediff, currval, nextval
572 logical :: ldone, lprocess
573 type(
listnodetype),
pointer :: tslnodepreceding => null()
574 type(
listnodetype),
pointer :: currnode => null(), nextnode => null()
577 class(*),
pointer :: currobj => null(), nextobj => null()
579 10
format(
'Error encountered while performing integration', &
580 ' for time series "', a,
'" for time interval: ', g12.5,
' to ', g12.5)
585 call this%get_latest_preceding_node(time0, tslnodepreceding)
586 if (
associated(tslnodepreceding))
then
587 currnode => tslnodepreceding
588 do while (.not. ldone)
589 currobj => currnode%GetItem()
591 currtime = currrecord%tsrTime
595 elseif (currtime < time1)
then
596 if (.not.
associated(currnode%nextNode))
then
598 if (.not. this%read_next_record())
then
599 if (.not. extendtoendofsimulation)
then
600 write (
errmsg, 10) trim(this%Name), time0, time1
606 currval = currrecord%tsrValue
608 if (
associated(currnode%nextNode))
then
609 nextnode => currnode%nextNode
610 nextobj => nextnode%GetItem()
612 nexttime = nextrecord%tsrTime
613 nextval = nextrecord%tsrValue
615 elseif (extendtoendofsimulation)
then
625 if (currtime > time0 .or.
is_close(currtime, time0))
then
630 if (nexttime < time1 .or.
is_close(nexttime, time1))
then
637 select case (this%iMethod)
644 timediff = nexttime - currtime
645 ratio0 = (t0 - currtime) / timediff
646 ratio1 = (t1 - currtime) / timediff
647 valuediff = nextval - currval
648 value0 = currval + ratio0 * valuediff
649 value1 = currval + ratio1 * valuediff
650 if (this%iMethod ==
linear)
then
651 area = 0.5d0 * t01 * (value0 + value1)
669 if (.not.
associated(currnode%nextNode))
then
671 if (.not. this%read_next_record())
then
672 write (
errmsg, 10) trim(this%Name), time0, time1
675 elseif (
associated(currnode%nextNode))
then
676 currnode => currnode%nextNode
683 if (this%autoDeallocate)
then
684 if (
associated(tslnodepreceding))
then
685 if (
associated(tslnodepreceding%prevNode))
then
686 call this%list%DeallocateBackward(tslnodepreceding%prevNode)
702 real(dp),
intent(in) :: time0
703 real(dp),
intent(in) :: time1
704 logical,
intent(in) :: extendtoendofsimulation
706 real(dp) :: timediff,
value, valueintegrated
708 timediff = time1 - time0
709 if (timediff > 0)
then
710 valueintegrated = this%get_integrated_value(time0, time1, &
711 extendtoendofsimulation)
713 value = valueintegrated
715 value = valueintegrated / timediff
719 value = this%get_value_at_time(time0, extendtoendofsimulation)
732 real(DP),
intent(in) :: time
740 class(*),
pointer :: obj => null()
743 if (
associated(this%list%firstNode))
then
744 currnode => this%list%firstNode
747 &get_latest_preceding_node', &
754 if (
associated(currnode))
then
755 if (
associated(currnode%nextNode))
then
756 obj => currnode%nextNode%GetItem()
758 if (tsr%tsrTime < time .or.
is_close(tsr%tsrTime, time))
then
759 currnode => currnode%nextNode
765 if (.not. this%read_next_record())
exit
772 if (
associated(currnode))
then
776 obj => tsnode0%GetItem()
778 time0 = tsrec0%tsrTime
779 do while (time0 > time)
780 if (
associated(tsnode0%prevNode))
then
781 tsnode0 => tsnode0%prevNode
782 obj => tsnode0%GetItem()
784 time0 = tsrec0%tsrTime
791 if (time0 < time .or.
is_close(time0, time)) tslnode => tsnode0
800 if (
associated(this%list))
then
801 call this%list%Clear(.true.)
802 deallocate (this%list)
813 class(*),
pointer :: obj => null()
816 call this%list%Add(obj)
827 class(*),
pointer :: obj => null()
831 obj => this%list%GetItem()
832 if (
associated(obj))
then
845 class(*),
pointer :: obj => null()
849 obj => this%list%GetPreviousItem()
850 if (
associated(obj))
then
863 class(*),
pointer :: obj => null()
867 obj => this%list%GetNextItem()
868 if (
associated(obj))
then
878 double precision,
intent(in) :: time
879 double precision,
intent(in) :: epsi
885 call this%list%Reset()
888 tsr => this%GetNextTimeSeriesRecord()
889 if (
associated(tsr))
then
890 if (
is_close(tsr%tsrTime, time))
then
894 if (tsr%tsrTime > time)
exit
907 call this%list%Reset()
917 double precision :: badtime, time, time0, time1
920 class(*),
pointer :: obj => null()
926 call this%get_surrounding_nodes(time, nodeearlier, nodelater)
928 if (
associated(nodeearlier))
then
929 obj => nodeearlier%GetItem()
931 if (
associated(tsrearlier))
then
932 time0 = tsrearlier%tsrTime
936 if (
associated(nodelater))
then
937 obj => nodelater%GetItem()
939 if (
associated(tsrlater))
then
940 time1 = tsrlater%tsrTime
944 if (time0 > badtime)
then
946 if (time1 > badtime)
then
948 if (time > time0 .and. time < time1)
then
951 call this%list%InsertBefore(obj, nodelater)
955 if (time == time0 .and. tsrearlier%tsrValue ==
dnodata .and. &
957 tsrearlier%tsrValue = tsr%tsrValue
958 elseif (time == time1 .and. tsrlater%tsrValue ==
dnodata .and. &
960 tsrlater%tsrValue = tsr%tsrValue
965 call this%AddTimeSeriesRecord(tsr)
969 if (time1 > badtime)
then
971 if (time < time1)
then
974 call this%list%InsertBefore(obj, nodelater)
975 elseif (time == time1)
then
978 if (tsrlater%tsrValue ==
dnodata .and. tsr%tsrValue /=
dnodata)
then
979 tsrlater%tsrValue = tsr%tsrValue
984 call this%AddTimeSeriesRecord(tsr)
994 logical,
intent(in),
optional :: readtoend
998 class(*),
pointer :: obj => null()
1000 double precision :: endtime
1003 if (
present(readtoend))
then
1005 do while (this%read_next_record())
1010 nrecords = this%list%Count()
1011 obj => this%list%GetItem(nrecords)
1013 endtime = tsr%tsrTime
1021 logical,
optional,
intent(in) :: destroy
1023 call this%list%Clear(destroy)
1024 end subroutine clear
1032 integer(I4B) ::
count
1036 if (
associated(this%timeSeries))
then
1037 count =
size(this%timeSeries)
1048 integer(I4B),
intent(in) :: indx
1053 if (indx > 0 .and. indx <= this%nTimeSeries)
then
1054 res => this%timeSeries(indx)
1064 character(len=*),
intent(in) :: filename
1065 integer(I4B),
intent(in) :: iout
1066 logical,
optional,
intent(in) :: autoDeallocate
1068 integer(I4B) :: iMethod, istatus, j, nwords
1069 integer(I4B) :: ierr, inunit
1070 logical :: autoDeallocateLocal = .true.
1071 logical :: continueread, found, endOfBlock
1072 logical :: methodWasSet
1073 real(DP) :: sfaclocal
1074 character(len=40) :: keyword, keyvalue
1075 character(len=:),
allocatable :: line
1076 character(len=LENTIMESERIESNAME),
allocatable,
dimension(:) :: words
1079 if (
present(autodeallocate)) autodeallocatelocal = autodeallocate
1081 methodwasset = .false.
1085 this%datafile = filename
1089 inunit = this%inunit
1090 call openfile(inunit, 0, filename,
'TS6')
1093 call this%parser%Initialize(this%inunit, this%iout)
1096 continueread = .false.
1100 call this%parser%GetBlock(
'ATTRIBUTES', found, ierr, &
1101 supportopenclose=.true.)
1104 errmsg =
'End-of-file encountered while searching for'// &
1105 ' ATTRIBUTES in time-series '// &
1106 'input file "'//trim(this%datafile)//
'"'
1108 call this%parser%StoreErrorUnit()
1109 elseif (.not. found)
then
1110 errmsg =
'ATTRIBUTES block not found in time-series '// &
1111 'tsfile input file "'//trim(this%datafile)//
'"'
1113 call this%parser%StoreErrorUnit()
1119 call this%parser%GetNextLine(endofblock)
1120 if (endofblock)
exit
1123 call this%parser%GetStringCaps(keyword)
1126 if (keyword ==
'NAMES') keyword =
'NAME'
1128 if (keyword /=
'NAME' .and. keyword /=
'METHODS' .and. &
1129 keyword /=
'SFACS')
then
1131 call this%parser%GetStringCaps(keyvalue)
1134 select case (keyword)
1137 call this%parser%GetRemainingLine(line)
1138 call parseline(line, nwords, words, this%parser%iuactive)
1139 this%nTimeSeries = nwords
1142 allocate (this%timeSeries(this%nTimeSeries))
1143 do j = 1, this%nTimeSeries
1144 call this%timeSeries(j)%initialize_time_series(this, words(j), &
1145 autodeallocatelocal)
1148 methodwasset = .true.
1149 if (this%nTimeSeries == 0)
then
1150 errmsg =
'Error: NAME attribute not provided before METHOD in file: ' &
1153 call this%parser%StoreErrorUnit()
1155 select case (keyvalue)
1163 errmsg =
'Unknown interpolation method: "'//trim(keyvalue)//
'"'
1166 do j = 1, this%nTimeSeries
1167 this%timeSeries(j)%iMethod = imethod
1170 methodwasset = .true.
1171 if (this%nTimeSeries == 0)
then
1172 errmsg =
'Error: NAME attribute not provided before METHODS in file: ' &
1175 call this%parser%StoreErrorUnit()
1177 call this%parser%GetRemainingLine(line)
1178 call parseline(line, nwords, words, this%parser%iuactive)
1179 if (nwords < this%nTimeSeries)
then
1180 errmsg =
'METHODS attribute does not list a method for'// &
1183 call this%parser%StoreErrorUnit()
1185 do j = 1, this%nTimeSeries
1187 select case (words(j))
1195 errmsg =
'Unknown interpolation method: "'//trim(words(j))//
'"'
1198 this%timeSeries(j)%iMethod = imethod
1201 if (this%nTimeSeries == 0)
then
1202 errmsg =
'NAME attribute not provided before SFAC in file: ' &
1205 call this%parser%StoreErrorUnit()
1207 read (keyvalue, *, iostat=istatus) sfaclocal
1208 if (istatus /= 0)
then
1209 errmsg =
'Error reading numeric value from: "'//trim(keyvalue)//
'"'
1212 do j = 1, this%nTimeSeries
1213 this%timeSeries(j)%sfac = sfaclocal
1216 if (this%nTimeSeries == 0)
then
1217 errmsg =
'NAME attribute not provided before SFACS in file: ' &
1220 call this%parser%StoreErrorUnit()
1222 do j = 1, this%nTimeSeries
1223 sfaclocal = this%parser%GetDouble()
1224 this%timeSeries(j)%sfac = sfaclocal
1226 case (
'AUTODEALLOCATE')
1227 do j = 1, this%nTimeSeries
1228 this%timeSeries(j)%autoDeallocate = (keyvalue ==
'TRUE')
1231 errmsg =
'Unknown option found in ATTRIBUTES block: "'// &
1234 call this%parser%StoreErrorUnit()
1239 call this%parser%GetBlock(
'TIMESERIES', found, ierr, &
1240 supportopenclose=.true.)
1243 if (.not. this%read_tsfile_line())
then
1244 errmsg =
'Error: No time-series data contained in file: '// &
1250 if (.not. methodwasset)
then
1251 errmsg =
'Interpolation method was not set. METHOD or METHODS &
1252 &must be specified in the ATTRIBUTES block for this time series file.'
1257 if (
allocated(words))
deallocate (words)
1260 call this%parser%StoreErrorUnit()
1269 real(dp) :: tsrtime, tsrvalue
1271 logical :: endofblock
1278 call this%parser%GetNextLine(endofblock)
1281 if (endofblock)
then
1286 tsrtime = this%parser%GetDouble()
1289 tsloop:
do i = 1, this%nTimeSeries
1290 tsrvalue = this%parser%GetDouble()
1291 if (tsrvalue ==
dnodata) cycle tsloop
1293 tsrvalue = tsrvalue * this%timeSeries(i)%sfac
1311 ts => this%GetTimeSeries(i)
1312 if (
associated(ts))
then
1318 deallocate (this%timeSeries)
1319 deallocate (this%parser)
This module contains block parser methods.
This module contains simulation constants.
integer(i4b), parameter linelength
maximum length of a standard line
real(dp), parameter dnodata
real no data constant
integer(i4b), parameter lenhugeline
maximum length of a huge line
integer(i4b), parameter lentimeseriesname
maximum length of a time series name
@ linearend
linear end interpolation
@ undefined
undefined interpolation
@ linear
linear interpolation
@ stepwise
stepwise interpolation
real(dp), parameter dzero
real constant zero
real(dp), parameter done
real constant 1
This module defines variable data types.
pure logical function, public is_close(a, b, rtol, atol, symmetric)
Check if a real value is approximately equal to another.
This module contains simulation methods.
subroutine, public store_error(msg, terminate)
Store an error message.
integer(i4b) function, public count_errors()
Return number of errors.
subroutine, public store_error_unit(iunit, terminate)
Store the file unit number.
This module contains simulation variables.
character(len=maxcharlen) errmsg
error message string
type(timeseriestype) function, pointer gettimeseries(this, indx)
Get time series.
type(timeseriesrecordtype) function, pointer getcurrenttimeseriesrecord(this)
Get current ts record.
subroutine get_surrounding_nodes(this, time, nodeEarlier, nodeLater)
Get surrounding nodes.
subroutine inserttsr(this, tsr)
Insert a time series record.
integer(i4b) function count(this)
Count number of time series.
subroutine initialize_time_series(this, tsfile, name, autoDeallocate)
Initialize time series.
logical function read_next_record(this)
Read next record.
type(timeseriesfiletype) function, pointer, public gettimeseriesfilefromlist(list, idx)
Get time series from list.
subroutine, public constructtimeseriesfile(newTimeSeriesFile)
Construct time series file.
subroutine get_latest_preceding_node(this, time, tslNode)
Get latest preceding node.
subroutine reset(this)
Reset.
subroutine initializetsfile(this, filename, iout, autoDeallocate)
Open time-series tsfile file and read options and first record, which may contain data to define mult...
type(timeseriesrecordtype) function, pointer gettimeseriesrecord(this, time, epsi)
Get ts record.
subroutine, public addtimeseriesfiletolist(list, tsfile)
Add time series file to list.
real(dp) function getvalue(this, time0, time1, extendToEndOfSimulation)
Get time series value.
type(timeseriesfiletype) function, pointer castastimeseriesfiletype(obj)
Cast an unlimited polymorphic object as class(TimeSeriesFileType)
type(timeseriesfiletype) function, pointer, public castastimeseriesfileclass(obj)
Cast an unlimited polymorphic object as class(TimeSeriesFileType)
real(dp) function get_integrated_value(this, time0, time1, extendToEndOfSimulation)
Get integrated value.
real(dp) function get_value_at_time(this, time, extendToEndOfSimulation)
Get value for a time.
logical function, public sametimeseries(ts1, ts2)
Compare two time series; if they are identical, return true.
type(timeseriesrecordtype) function, pointer getnexttimeseriesrecord(this)
Get next ts record.
real(dp) function get_average_value(this, time0, time1, extendToEndOfSimulation)
Get average value.
subroutine clear(this, destroy)
Clear the list of time series records.
type(timeseriesrecordtype) function, pointer getprevioustimeseriesrecord(this)
Get previous ts record.
subroutine addtimeseriesrecord(this, tsr)
Add ts record.
subroutine ts_da(this)
Deallocate.
subroutine tsf_da(this)
Deallocate memory.
subroutine get_surrounding_records(this, time, tsrecEarlier, tsrecLater)
Get surrounding records.
logical function read_tsfile_line(this)
Read time series file line.
double precision function findlatesttime(this, readToEnd)
Find latest time.
subroutine, public addtimeseriesrecordtolist(list, tsrecord)
Add time series record to list.
subroutine, public constructtimeseriesrecord(newTsRecord, time, value)
Allocate and assign members of a new TimeSeriesRecordType object.
type(timeseriesrecordtype) function, pointer, public castastimeseriesrecordtype(obj)
Cast an unlimited polymorphic object as TimeSeriesRecordType.
A generic heterogeneous doubly-linked list.