MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_data_driven_boundary.t
Go to the documentation of this file.
1!> Utilities for reading AMRVAC data-driven magnetic boundary frames.
2!>
3!> Python V1 writes each frame as a stream binary file with layout:
4!> snapshot_time, nx, ny, dx, dy, Bx, By, Bz.
5!>
6!> The spacing dx/dy is stored in km and magnetic-field components are stored
7!> in Gauss. The magnetic data are ordered as a Fortran array (nx, ny, 3).
9 implicit none
10
12 integer :: nframe=0,nx=0,ny=0
13 double precision :: dx_km=0.d0,dy_km=0.d0
14 character(len=1024) :: directory=''
15 character(len=64) :: prefix='B_'
16 double precision, allocatable :: times(:)
17 integer :: cache_indices(2)=0
18 ! Only the two frames bracketing the current observation time are cached.
19 double precision, allocatable :: values(:,:,:,:)
21
22contains
23
24 subroutine read_data_driven_boundary_frame(filename,snapshot_time,nx,ny,dx_km,dy_km,bframe)
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(:,:,:)
29
30 call read_data_driven_boundary_header(filename,snapshot_time,nx,ny,dx_km,dy_km)
31 allocate(bframe(nx,ny,3))
32 call read_data_driven_boundary_frame_into(filename,snapshot_time,nx,ny,dx_km,dy_km,bframe)
34
35 subroutine read_data_driven_boundary_header(filename,snapshot_time,nx,ny,dx_km,dy_km)
36 use mod_comm_lib, only: mpistop
37 use, intrinsic :: ieee_arithmetic, only: ieee_is_finite
38
39 character(len=*), intent(in) :: filename
40 double precision, intent(out) :: snapshot_time,dx_km,dy_km
41 integer, intent(out) :: nx,ny
42
43 integer :: iu,ios
44 integer(kind=8) :: file_size,expected_size
45 logical :: exists
46
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)
58 close(iu)
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')
68
69 subroutine read_data_driven_boundary_frame_into(filename,snapshot_time,nx,ny, &
70 dx_km,dy_km,bframe)
71 use mod_comm_lib, only: mpistop
72
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(:,:,:)
77
78 integer :: iu,ios
79
80 call read_data_driven_boundary_header(filename,snapshot_time,nx,ny,dx_km,dy_km)
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
92 close(iu)
93 if(ios/=0) call mpistop('cannot read data-driven boundary payload')
95
96 subroutine read_data_driven_boundary_series(directory,series,prefix,expected_nframe)
97 use mod_comm_lib, only: mpistop
98
99 character(len=*), intent(in) :: directory
100 type(data_driven_boundary_series), intent(inout) :: series
101 character(len=*), intent(in), optional :: prefix
102 integer, intent(in), optional :: expected_nframe
103
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
108 logical :: exists
109
110 frame_prefix='B_'
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')
125 end do
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')
129 else
130 iframe=1
131 do
132 write(filename,'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),iframe
133 inquire(file=trim(filename),exist=exists)
134 if(.not.exists) exit
135 iframe=iframe+1
136 end do
137 series%nframe=iframe-1
138 end if
139 if(series%nframe<2) call mpistop('data-driven boundary series requires at least two frames')
140
141 if(allocated(series%times)) deallocate(series%times)
142 if(allocated(series%values)) deallocate(series%values)
143 allocate(series%times(series%nframe))
144
145 do iframe=1,series%nframe
146 write(filename,'(a,"/",a,i4.4,".dat")') trim(directory),trim(frame_prefix),iframe
147 call read_data_driven_boundary_header(trim(filename),snapshot_time,nx,ny,dx_km,dy_km)
148 if(iframe==1) then
149 series%nx=nx
150 series%ny=ny
151 series%dx_km=dx_km
152 series%dy_km=dy_km
153 else
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')
162 end if
163 series%times(iframe)=snapshot_time
164 end do
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
169 call ensure_data_driven_boundary_pair(series,1,left_slot,right_slot)
171
172 subroutine interpolate_data_driven_boundary(series,time_seconds,bframe,left_index,weight)
173 type(data_driven_boundary_series), intent(inout) :: series
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
178
179 integer :: lo,left_slot,right_slot
180 double precision :: alpha,denominator
181
182 if(time_seconds<=series%times(1)) then
183 lo=1
184 alpha=0.d0
185 else if(time_seconds>=series%times(series%nframe)) then
186 lo=series%nframe-1
187 alpha=1.d0
188 else
189 lo=1
190 do while(lo<series%nframe-1 .and. time_seconds>=series%times(lo+1))
191 lo=lo+1
192 end do
193 denominator=series%times(lo+1)-series%times(lo)
194 alpha=(time_seconds-series%times(lo))/denominator
195 end if
196 call ensure_data_driven_boundary_pair(series,lo,left_slot,right_slot)
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
202
203 subroutine ensure_data_driven_boundary_pair(series,lo,left_slot,right_slot)
204 type(data_driven_boundary_series), intent(inout) :: series
205 integer, intent(in) :: lo
206 integer, intent(out) :: left_slot,right_slot
207 integer :: slot
208
209 left_slot=0
210 right_slot=0
211 do slot=1,2
212 if(series%cache_indices(slot)==lo) left_slot=slot
213 if(series%cache_indices(slot)==lo+1) right_slot=slot
214 end do
215 if(left_slot>0 .and. right_slot>0) return
216 if(left_slot>0) then
217 right_slot=3-left_slot
218 call load_data_driven_boundary_cache_slot(series,lo+1,right_slot)
219 else if(right_slot>0) then
220 left_slot=3-right_slot
221 call load_data_driven_boundary_cache_slot(series,lo,left_slot)
222 else
223 left_slot=1
224 right_slot=2
225 call load_data_driven_boundary_cache_slot(series,lo,left_slot)
226 call load_data_driven_boundary_cache_slot(series,lo+1,right_slot)
227 end if
229
230 subroutine load_data_driven_boundary_cache_slot(series,iframe,slot)
231 use mod_comm_lib, only: mpistop
232
233 type(data_driven_boundary_series), intent(inout) :: series
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
238 integer :: nx,ny
239
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
244 call read_data_driven_boundary_frame_into(trim(filename),snapshot_time,nx,ny, &
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
253
254 subroutine interpolate_data_driven_boundary_scaled(series,time_code,unit_time_seconds, &
255 driving_time_scale,bframe,left_index,weight)
256 !> Interpolate using AMRVAC code time. ``driving_time_scale`` is the
257 !> observational-time advance per simulated second; values greater than
258 !> one therefore accelerate the observed boundary evolution.
259 use mod_comm_lib, only: mpistop
260 type(data_driven_boundary_series), intent(inout) :: series
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
266
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
270 call interpolate_data_driven_boundary(series,observation_time,bframe,left_index,weight)
272
274 type(data_driven_boundary_series), intent(inout) :: series
275 if(allocated(series%times)) deallocate(series%times)
276 if(allocated(series%values)) deallocate(series%values)
277 series%nframe=0
278 series%nx=0
279 series%ny=0
280 series%directory=''
281 series%prefix='B_'
282 series%cache_indices=0
284
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Utilities for reading AMRVAC data-driven magnetic boundary frames.
subroutine read_data_driven_boundary_header(filename, snapshot_time, nx, ny, dx_km, dy_km)
subroutine interpolate_data_driven_boundary(series, time_seconds, bframe, left_index, weight)
subroutine read_data_driven_boundary_frame_into(filename, snapshot_time, nx, ny, dx_km, dy_km, bframe)
subroutine interpolate_data_driven_boundary_scaled(series, time_code, unit_time_seconds, driving_time_scale, bframe, left_index, weight)
subroutine read_data_driven_boundary_frame(filename, snapshot_time, nx, ny, dx_km, dy_km, bframe)
subroutine destroy_data_driven_boundary_series(series)
subroutine load_data_driven_boundary_cache_slot(series, iframe, slot)
subroutine read_data_driven_boundary_series(directory, series, prefix, expected_nframe)
subroutine ensure_data_driven_boundary_pair(series, lo, left_slot, right_slot)