25 character(len=*),
intent(in) :: filename
26 double precision,
intent(out) :: snapshot_time,dx_km,dy_km
27 integer,
intent(out) :: nx,ny
28 double precision,
allocatable,
intent(out) :: bframe(:,:,:)
31 allocate(bframe(nx,ny,3))
37 use,
intrinsic :: ieee_arithmetic, only: ieee_is_finite
39 character(len=*),
intent(in) :: filename
40 double precision,
intent(out) :: snapshot_time,dx_km,dy_km
41 integer,
intent(out) :: nx,ny
44 integer(kind=8) :: file_size,expected_size
47 inquire(file=filename,exist=exists)
48 if(.not.exists)
call mpistop(
'missing data-driven boundary frame')
49 open(newunit=iu,file=trim(filename),status=
'old',access=
'stream', &
50 form=
'unformatted',action=
'read',iostat=ios)
51 if(ios/=0)
call mpistop(
'cannot open data-driven boundary frame')
52 read(iu,iostat=ios) snapshot_time
53 if(ios==0)
read(iu,iostat=ios) nx
54 if(ios==0)
read(iu,iostat=ios) ny
55 if(ios==0)
read(iu,iostat=ios) dx_km
56 if(ios==0)
read(iu,iostat=ios) dy_km
57 inquire(unit=iu,size=file_size)
59 if(ios/=0)
call mpistop(
'cannot read data-driven boundary header')
60 if(nx<=0 .or. ny<=0)
call mpistop(
'invalid nx/ny in data-driven boundary frame')
61 if(.not.ieee_is_finite(snapshot_time) .or. .not.ieee_is_finite(dx_km) .or. &
62 .not.ieee_is_finite(dy_km) .or. dx_km<=0.d0 .or. dy_km<=0.d0) &
63 call mpistop(
'invalid time or spacing in data-driven boundary frame')
64 expected_size=32_8+int(nx,kind=8)*int(ny,kind=8)*24_8
65 if(file_size/=expected_size) &
66 call mpistop(
'data-driven boundary frame size does not match its header')
73 character(len=*),
intent(in) :: filename
74 double precision,
intent(out) :: snapshot_time,dx_km,dy_km
75 integer,
intent(out) :: nx,ny
76 double precision,
intent(out) :: bframe(:,:,:)
81 if(
size(bframe,1)/=nx .or.
size(bframe,2)/=ny .or.
size(bframe,3)/=3) &
82 call mpistop(
'destination array does not match data-driven boundary frame')
83 open(newunit=iu,file=trim(filename),status=
'old',access=
'stream', &
84 form=
'unformatted',action=
'read',iostat=ios)
85 if(ios/=0)
call mpistop(
'cannot reopen data-driven boundary frame')
86 read(iu,iostat=ios) snapshot_time
87 if(ios==0)
read(iu,iostat=ios) nx
88 if(ios==0)
read(iu,iostat=ios) ny
89 if(ios==0)
read(iu,iostat=ios) dx_km
90 if(ios==0)
read(iu,iostat=ios) dy_km
91 if(ios==0)
read(iu,iostat=ios) bframe
93 if(ios/=0)
call mpistop(
'cannot read data-driven boundary payload')
99 character(len=*),
intent(in) :: directory
101 character(len=*),
intent(in),
optional :: prefix
102 integer,
intent(in),
optional :: expected_nframe
104 character(len=1024) :: filename
105 character(len=64) :: frame_prefix
106 double precision :: snapshot_time,dx_km,dy_km
107 integer :: iframe,nx,ny,left_slot,right_slot
111 if(
present(prefix)) frame_prefix=trim(prefix)
112 if(len_trim(directory)>len(series%directory)) &
113 call mpistop(
'data-driven boundary directory path is too long')
114 if(len_trim(frame_prefix)>len(series%prefix)) &
115 call mpistop(
'data-driven boundary prefix is too long')
116 series%directory=trim(directory)
117 series%prefix=trim(frame_prefix)
118 if(
present(expected_nframe))
then
119 if(expected_nframe<2)
call mpistop(
'expected boundary frame count must be at least two')
120 series%nframe=expected_nframe
121 do iframe=1,series%nframe
122 write(filename,
'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),iframe
123 inquire(file=trim(filename),exist=exists)
124 if(.not.exists)
call mpistop(
'missing frame in data-driven boundary series')
126 write(filename,
'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),series%nframe+1
127 inquire(file=trim(filename),exist=exists)
128 if(exists)
call mpistop(
'boundary series contains more frames than declared')
132 write(filename,
'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),iframe
133 inquire(file=trim(filename),exist=exists)
137 series%nframe=iframe-1
139 if(series%nframe<2)
call mpistop(
'data-driven boundary series requires at least two frames')
141 if(
allocated(series%times))
deallocate(series%times)
142 if(
allocated(series%values))
deallocate(series%values)
143 allocate(series%times(series%nframe))
145 do iframe=1,series%nframe
146 write(filename,
'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),iframe
154 if(nx/=series%nx .or. ny/=series%ny) &
155 call mpistop(
'inconsistent nx/ny in data-driven boundary series')
156 if(abs(dx_km-series%dx_km)>1.d-10*max(1.d0,abs(series%dx_km))) &
157 call mpistop(
'inconsistent dx in data-driven boundary series')
158 if(abs(dy_km-series%dy_km)>1.d-10*max(1.d0,abs(series%dy_km))) &
159 call mpistop(
'inconsistent dy in data-driven boundary series')
160 if(snapshot_time<=series%times(iframe-1)) &
161 call mpistop(
'data-driven boundary times must be strictly increasing')
163 series%times(iframe)=snapshot_time
165 if(abs(series%times(1))>1.d-10) &
166 call mpistop(
'first data-driven boundary snapshot_time must be zero')
167 allocate(series%values(series%nx,series%ny,3,2))
168 series%cache_indices=0
174 double precision,
intent(in) :: time_seconds
175 double precision,
intent(out) :: bframe(series%nx,series%ny,3)
176 integer,
intent(out),
optional :: left_index
177 double precision,
intent(out),
optional :: weight
179 integer :: lo,left_slot,right_slot
180 double precision :: alpha,denominator
182 if(time_seconds<=series%times(1))
then
185 else if(time_seconds>=series%times(series%nframe))
then
190 do while(lo<series%nframe-1 .and. time_seconds>=series%times(lo+1))
193 denominator=series%times(lo+1)-series%times(lo)
194 alpha=(time_seconds-series%times(lo))/denominator
197 bframe=(1.d0-alpha)*series%values(:,:,:,left_slot)+ &
198 alpha*series%values(:,:,:,right_slot)
199 if(
present(left_index)) left_index=lo
200 if(
present(weight)) weight=alpha
234 integer,
intent(in) :: iframe,slot
235 character(len=1024) :: filename
236 double precision :: snapshot_time,dx_km,dy_km
237 double precision,
parameter :: reltol=1.d-10
240 if(iframe<1 .or. iframe>series%nframe) &
241 call mpistop(
'data-driven boundary cache index is out of range')
242 write(filename,
'(a,"/",a,i4.4,".dat")') trim(series%directory), &
243 trim(series%prefix),iframe
245 dx_km,dy_km,series%values(:,:,:,slot))
246 if(nx/=series%nx .or. ny/=series%ny .or. &
247 abs(dx_km-series%dx_km)>reltol*max(1.d0,abs(series%dx_km)) .or. &
248 abs(dy_km-series%dy_km)>reltol*max(1.d0,abs(series%dy_km)) .or. &
249 abs(snapshot_time-series%times(iframe))>reltol*max(1.d0,abs(snapshot_time))) &
250 call mpistop(
'data-driven boundary frame changed after header scan')
251 series%cache_indices(slot)=iframe
255 driving_time_scale,bframe,left_index,weight)
261 double precision,
intent(in) :: time_code,unit_time_seconds,driving_time_scale
262 double precision,
intent(out) :: bframe(series%nx,series%ny,3)
263 integer,
intent(out),
optional :: left_index
264 double precision,
intent(out),
optional :: weight
265 double precision :: observation_time
267 if(unit_time_seconds<=0.d0)
call mpistop(
'unit_time_seconds must be positive')
268 if(driving_time_scale<=0.d0)
call mpistop(
'driving_time_scale must be positive')
269 observation_time=time_code*unit_time_seconds*driving_time_scale