• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gyrokinetics / gs2 / 2078172070

03 Oct 2025 09:22AM UTC coverage: 10.835% (+0.03%) from 10.806%
2078172070

push

gitlab-ci

David Dickinson
Merged in experimental/user_controlled_output_base_name (pull request #1184)

5049 of 46599 relevant lines covered (10.83%)

119786.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.7
/src/gs2_save.fpp
1
#include "unused_dummy.inc"
2
!> FIXME : Add documentation
3
module gs2_save
4
  use constants, only: run_name_size
5

6
  implicit none
7

8
  private
9

10
  public :: gs2_restore, gs2_save_for_restart, finish_gs2_save
11
  public :: read_many, save_many, gs2_save_response, gs2_restore_response
12
  public :: init_gs2_save, init_dt, read_t0_from_restart_file, init_ant_amp
13
  public :: set_restart_file, include_explicit_source_in_restart, proc_to_save_fields
14
  public :: init_vnm, restart_writable, EigNetcdfID
15
  public :: init_eigenfunc_file, finish_eigenfunc_file, add_eigenpair_to_file
16
  public :: gs2_save_slice
17

18
  interface gs2_restore
19
     module procedure gs2_restore_many
20
  end interface
21

22
  !> A custom type to look after the netcdf ids for the eigenvalue file
23
  type EigNetcdfID
24
     integer :: ncid            !< File handle
25
     integer :: nconv_count     !< Current size of `conv` dimension
26
  end type EigNetcdfID
27

28
  !> Read and write single or multiple restart files.
29
  !> Effectively ignored if not built with parallel netcdf
30
  logical :: read_many = .false., save_many = .false.
31

32
  !> Do we want to include the explicit source terms and
33
  !> related timesteps when saving/restoring from the restart file.
34
  logical :: include_explicit_source_in_restart = .true.
35

36
  !> Which processor should save the potentials. If <0 (default) then
37
  !> all processors save their potentials. If >= nproc then will be
38
  !> set to mod(proc_to_save_fields, nproc)
39
  integer :: proc_to_save_fields
40

41
  !> Used to hold the base of the restart file name.
42
  character (run_name_size) :: restart_file
43

44
#ifdef NETCDF
45
  !> The NETCDF_PARALLEL directives include code for parallel
46
  !> netcdf using HDF5 to write the output to a single restart file
47
  !> The save_many/read_many flag allows the old style multiple file output
48
  !> Here we set a run time flag to enable us to handle some of the different
49
  !> paths through the code at run time rather than with ifdef mazes
50
#ifdef NETCDF_PARALLEL
51
  logical, parameter :: has_netcdf_parallel = .true.
52
#else
53
  logical, parameter :: has_netcdf_parallel = .false.
54
#endif
55
#endif
56

57
contains
58
#ifdef NETCDF
59
  integer function get_dim_size(ncid, dim_name) result(dim_size)
×
60
    use neasyf, only: neasyf_dim
61
    use netcdf, only: nf90_inquire_dimension
62
    implicit none
63
    integer, intent(in) :: ncid
64
    character(len = *), intent(in) :: dim_name
65
    integer :: id, istatus
66
    call neasyf_dim(ncid, dim_name, dimid = id)
×
67
    istatus = nf90_inquire_dimension(ncid, id, len=dim_size)
×
68
  end function get_dim_size
×
69

70
  subroutine verify_compatible_dim_sizes(ncid, dim_name, expected_size)
×
71
    use mp, only: mp_abort, iproc
72
    integer, intent(in) :: ncid, expected_size
73
    character(len = *), intent(in) :: dim_name
74
    integer :: actual_size
75
    actual_size = get_dim_size(ncid, dim_name)
×
76
    if (actual_size /= expected_size) then
×
77
       write(*,'("Restart error (",A,"): got/expected = ",I0,"/",I0," : ",I0)') &
78
            dim_name, actual_size, expected_size, iproc
×
79
       call mp_abort('Incompatible '//dim_name//' in restart', .true.)
×
80
    end if
81
  end subroutine verify_compatible_dim_sizes
×
82

83
  !> Returns the file corresponding to restart file in current setup
84
  function get_file_proc(is_one_file_per_processor, fileopt) result(file_proc)
×
85
    use mp, only: iproc
86
    use optionals, only: get_option_with_default
87
    implicit none
88
    logical, intent(in) :: is_one_file_per_processor
89
    character(len=*), intent(in), optional :: fileopt
90
    character(run_name_size) :: file_proc
91
    file_proc = trim(restart_file) // trim(adjustl(get_option_with_default(fileopt, '')))
×
92
    if (is_one_file_per_processor) write(file_proc, '(A,".",I0)') trim(file_proc), iproc
×
93
  end function get_file_proc
×
94

95
  logical function variable_exists(ncid, var_name)
×
96
    use netcdf, only: nf90_inq_varid, NF90_NOERR
97
    implicit none
98
    integer, intent(in) :: ncid
99
    character(len = *), intent(in) :: var_name
100
    integer :: id
101
    variable_exists = nf90_inq_varid(ncid, var_name, id) == NF90_NOERR
×
102
  end function variable_exists
×
103
#endif
104

105
  !> Create and fill the restart files
106
  subroutine gs2_save_for_restart &
×
107
       (g, t0, vnm, has_phi, has_apar, has_bpar, &
×
108
       code_dt, code_dt_prev1, code_dt_prev2, code_dt_max, &
109
       fileopt, &
110
       save_glo_info_and_grids, save_velocities, header)
111
#ifdef NETCDF
112
    use fields_arrays, only: phinew, aparnew, bparnew
113
    use dist_fn_arrays, only: gexp_1, gexp_2, gexp_3, kx_shift, theta0_shift
114
    use kt_grids, only: naky, ntheta0, jtwist, akx, aky
115
    use antenna_data, only: a_ant, b_ant, ant_on
116
    use gs2_metadata, only: create_metadata
117
    use netcdf, only: nf90_collective, nf90_independent
118
    use neasyf, only: neasyf_open, neasyf_dim, neasyf_write, neasyf_close
119
    use mp, only: iproc, barrier, proc0, mp_comm, mp_info
120
    use theta_grid, only: theta
121
    use gs2_layouts, only: proc_id,ik_idx,it_idx,is_idx,ie_idx,il_idx, layout
122
    use layouts_type, only: g_layout_type
123
    use le_grids, only: energy, al, negrid, nlambda
124
    use species, only: nspec
125
    use dist_fn_arrays, only: vpa, vperp2
126
    use unit_tests, only: debug_message
127
    use optionals, only: get_option_with_default
128
#else
129
    use file_utils, only: error_unit
130
    use mp, only: proc0
131
#endif
132
    use theta_grid, only: ntgrid
133
    use gs2_layouts, only: g_lo
134
    use standard_header, only : standard_header_type
135
    implicit none
136
    !> The distribution function \(g\)
137
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in) :: g
138
    !> Current simulation time
139
    real, intent (in) :: t0
140
    !> FIXME: Collisionality multiplier?
141
    real, dimension (2), intent (in) :: vnm
142
    !> The current time step, the two previous time steps and the
143
    !> maximum allowed time step.
144
    real, intent(in) :: code_dt, code_dt_prev1, code_dt_prev2, code_dt_max
145
    !> Do we include electrostatic potential, \(\phi\)
146
    logical, intent (in) :: has_phi
147
    !> Do we include vector potential, \(A_\parallel\)
148
    logical, intent (in) :: has_apar
149
    !> Do we include parallel magnetic field, \(B_\parallel\)
150
    logical, intent (in) :: has_bpar
151
    !> Optional extra filename infix
152
    character (len=*), intent (in), optional :: fileopt
153
    !> Control if layout and diagnostic information is saved
154
    logical, intent (in), optional :: save_glo_info_and_grids
155
    !> Control if \(v_\parallel, v_\perp\) information is saved
156
    logical, intent (in), optional :: save_velocities
157
    !> Header for files with build and run information
158
    type(standard_header_type), optional, intent(in) :: header
159
#ifdef NETCDF
160
    !> Names of dimensions for distribution-sized variables in netCDF files
161
    character(len=5), dimension(*), parameter :: g_dim_names = ["theta", "sign ", "glo  "]
162
    character(len=5), dimension(*), parameter :: field_dim_names = ["theta", "akx  ", "aky  "]
163
    !> Arrays holding information about the layout for plotting
164
    integer, dimension (:,:,:,:,:), allocatable :: procid_kykxsel, iglo_kykxsel
×
165
    character (run_name_size) :: file_proc
166
    real, dimension(:), allocatable :: atmp
×
167
    real, dimension(naky) :: stmp
×
168
    integer, dimension(5) :: glo_counts
169
    integer, dimension(3) :: start_pos, counts, field_counts
170
    integer, dimension(1) :: scalar_count, exb_count
171
    logical :: is_one_file_per_processor, this_proc_saves_fields, has_gexp_1
172
    logical :: save_glo_info_and_grids_local, save_velocities_info
173
    logical :: should_define_single_entry_variables
174
    integer, parameter :: verb = 3
175
    integer :: ncid, iglo, n_elements
176
    ! Whether to use collective (parallel) or independent access to netCDF variables
177
    integer :: par_access
178
    type(standard_header_type) :: local_header
×
179

180
    if (present(header)) then
×
181
      local_header = header
×
182
    else
183
      local_header = standard_header_type()
×
184
    end if
185

186
    call debug_message(verb, 'gs2_save::gs2_save_for_restart initialising')
×
187

188
    ! Handle some optional inputs
189
    save_glo_info_and_grids_local = get_option_with_default(save_glo_info_and_grids, .false.)
×
190
    save_velocities_info = get_option_with_default(save_velocities, .false.)
×
191

192
    ! Decide if we want one file per processor or not
193
    is_one_file_per_processor = save_many .or. (.not. has_netcdf_parallel)
×
194

195
    ! Open the file and define dimensions and variables
196
    file_proc = get_file_proc(is_one_file_per_processor, fileopt)
×
197

198
    call debug_message(verb, 'gs2_save::gs2_save_for_restart opening file')
×
199
    call debug_message(verb, 'file proc is')
×
200
    call debug_message(verb, trim(adjustl(file_proc)))
×
201

202
    if (is_one_file_per_processor) then
×
203
       ncid = neasyf_open(file_proc, "w")
×
204
    else
205
       call barrier
×
206
       call debug_message(verb, &
207
            'gs2_save::gs2_save_for_restart called barrier before opening')
×
208

209
       ncid = neasyf_open(file_proc, "w", comm=mp_comm, info=mp_info)
×
210
    end if
211

212
    call create_metadata(ncid, "GS2 restart file", local_header)
×
213

214
    call debug_message(verb, 'gs2_save::gs2_save_for_restart defining dimensions')
×
215

216
    ! Work out the problem size
217
    if (is_one_file_per_processor) then
×
218
       n_elements = max(g_lo%ulim_proc-g_lo%llim_proc+1, 0)
×
219
    else
220
       n_elements = g_lo%ulim_world+1
×
221
    end if
222

223
    ! Note when processors have no local data we find n_elements = 0 so here we
224
    ! would be trying to create a zero size dimension. Netcdf interprets this as
225
    ! an unlimited dimension so neasyf requires unlimited = .true. in this case,
226
    ! hence the unlimited argument below. This will lead to empty gr/gi data being
227
    ! written on such processors.
228
    call neasyf_dim(ncid, "glo", dim_size=n_elements, unlimited = n_elements == 0)
×
229

230
    call neasyf_dim(ncid, "theta", theta)
×
231
    call neasyf_dim(ncid, "sign", dim_size=2)
×
232
    call neasyf_dim(ncid, "aky", aky)
×
233
    call neasyf_dim(ncid, "akx", akx)
×
234

235
    ! Decide if this processor should take part in defining items
236
    ! which should only appear once in outputs.
237
    should_define_single_entry_variables = proc0 .or. (.not. is_one_file_per_processor)
×
238

239
    !For saving distribution function
240
    if (save_glo_info_and_grids_local .and. should_define_single_entry_variables) then
×
241
       call neasyf_dim(ncid, "lambda", values=al, long_name="Energy/magnetic moment", units="1/(2 B_a)")
×
242
       call neasyf_dim(ncid, "species", dim_size=nspec)
×
243

244
       ! Energy coordinate is a function of species, but the dimension must be 1D
245
       call neasyf_dim(ncid, "egrid", dim_size=negrid, long_name="See 'energy' for energy grid values")
×
246
       call neasyf_write(ncid, "energy", energy, dim_names=["egrid  ", "species"], &
247
            long_name="Energy grid values")
×
248
    end if
249

250
    call debug_message(verb, 'gs2_save::gs2_save_for_restart defining variables')
×
251

252
    if (ant_on) call neasyf_dim(ncid, "nk_stir", dim_size=size(a_ant))
×
253

254
    call debug_message(verb, 'gs2_save::gs2_save_for_restart writing scalars')
×
255

256
    if (is_one_file_per_processor .or. proc0) then
×
257
      scalar_count = [1]
×
258
    else
259
      scalar_count = [0]
×
260
    end if
261

262
    call neasyf_write(ncid, "delt0", code_dt, count=scalar_count)
×
263
    call neasyf_write(ncid, "delt1", code_dt_prev1, count=scalar_count)
×
264
    call neasyf_write(ncid, "delt2", code_dt_prev2, count=scalar_count)
×
265
    call neasyf_write(ncid, "delt_max", code_dt_max, count=scalar_count)
×
266
    call neasyf_write(ncid, "t0", t0, count=scalar_count)
×
267
    call neasyf_write(ncid, "layout", layout, count=scalar_count)
×
268
    call neasyf_write(ncid, "ntheta0", ntheta0, count=scalar_count)
×
269
    call neasyf_write(ncid, "naky", naky, count=scalar_count)
×
270
    call neasyf_write(ncid, "nlambda", nlambda, count=scalar_count)
×
271
    call neasyf_write(ncid, "negrid", negrid, count=scalar_count)
×
272
    call neasyf_write(ncid, "nspec", nspec, count=scalar_count)
×
273
    call neasyf_write(ncid, "vnm1", vnm(1), count=scalar_count)
×
274
    call neasyf_write(ncid, "vnm2", vnm(2), count=scalar_count)
×
275

276
    if (ant_on) then
×
277
       atmp = real(a_ant)
×
278
       call neasyf_write(ncid, "a_ant_r", atmp, dim_names=["nk_stir"])
×
279
       atmp = aimag(a_ant)
×
280
       call neasyf_write(ncid, "a_ant_i", atmp, dim_names=["nk_stir"])
×
281

282
       atmp = real(b_ant)
×
283
       call neasyf_write(ncid, "b_ant_r", atmp, dim_names=["nk_stir"])
×
284
       atmp = aimag(b_ant)
×
285
       call neasyf_write(ncid, "b_ant_i", atmp, dim_names=["nk_stir"])
×
286
    end if
287

288
    call debug_message(verb, 'gs2_save::gs2_save_for_restart writing dist fn')
×
289

290
    if (is_one_file_per_processor) then
×
291
       start_pos = [1, 1, 1]
×
292
       par_access = nf90_independent
×
293
    else
294
       start_pos = [1, 1, g_lo%llim_proc+1]
×
295
       par_access = nf90_collective
×
296
    end if
297

298
    counts = shape(g)
×
299

300
    call neasyf_write(ncid, "gr", real(g), dim_names=g_dim_names, &
×
301
         start=start_pos, count=counts, par_access=par_access)
×
302
    call neasyf_write(ncid, "gi", aimag(g), dim_names=g_dim_names, &
×
303
         start=start_pos, count=counts, par_access=par_access)
×
304

305
    if (include_explicit_source_in_restart) then
×
306
       !Explicit source term gexp_1
307
#ifndef SHMEM
308
       has_gexp_1 = allocated(gexp_1)
×
309
#else
310
       has_gexp_1 = associated(gexp_1)
311
#endif
312

313
       if (has_gexp_1) then
×
314
          call neasyf_write(ncid, "gexp_1_r", real(gexp_1), dim_names=g_dim_names, &
×
315
                  start=start_pos, count=counts, par_access=par_access)
×
316
          call neasyf_write(ncid, "gexp_1_i", aimag(gexp_1), dim_names=g_dim_names, &
×
317
                  start=start_pos, count=counts, par_access=par_access)
×
318
       end if
319

320
       !Explicit source term gexp_2
321
       if(allocated(gexp_2)) then
×
322
          call neasyf_write(ncid, "gexp_2_r", real(gexp_2), dim_names=g_dim_names, &
×
323
                  start=start_pos, count=counts, par_access=par_access)
×
324
          call neasyf_write(ncid, "gexp_2_i", aimag(gexp_2), dim_names=g_dim_names, &
×
325
                  start=start_pos, count=counts, par_access=par_access)
×
326
       end if
327

328
       !Explicit source term gexp_3
329
       if(allocated(gexp_3)) then
×
330
          call neasyf_write(ncid, "gexp_3_r", real(gexp_3), dim_names=g_dim_names, &
×
331
                  start=start_pos, count=counts, par_access=par_access)
×
332
          call neasyf_write(ncid, "gexp_3_i", aimag(gexp_3), dim_names=g_dim_names, &
×
333
                  start=start_pos, count=counts, par_access=par_access)
×
334
       end if
335
    end if
336

337
    ! For saving distribution function
338
    if (save_glo_info_and_grids_local) then
×
339
       if (proc0 .or. .not. is_one_file_per_processor) then
×
340
          if(.not. allocated(iglo_kykxsel)) allocate(iglo_kykxsel(naky,ntheta0,nspec,negrid,nlambda))
×
341
          if(.not. allocated(procid_kykxsel)) allocate(procid_kykxsel(naky,ntheta0,nspec,negrid,nlambda))
×
342
       end if
343

344
       if (proc0) then
×
345
          do iglo=g_lo%llim_world,g_lo%ulim_world
×
346
             iglo_kykxsel(ik_idx(g_lo,iglo),it_idx(g_lo,iglo),is_idx(g_lo,iglo), &
×
347
                  ie_idx(g_lo,iglo),il_idx(g_lo,iglo)) = iglo
×
348
          end do
349

350
          do iglo=g_lo%llim_world,g_lo%ulim_world
×
351
             procid_kykxsel(ik_idx(g_lo,iglo),it_idx(g_lo,iglo),is_idx(g_lo,iglo), &
×
352
                  ie_idx(g_lo,iglo),il_idx(g_lo,iglo)) = proc_id(g_lo,iglo)
×
353
          end do
354

355
          glo_counts = shape(iglo_kykxsel)
×
356
       else
357
          glo_counts = [0, 0, 0, 0, 0]
×
358
       end if
359

360
       ! Either only write to proc0's serial file, or all processes need to call
361
       ! these functions in parallel
362
       if (proc0 .or. .not. is_one_file_per_processor) then
×
363
          call neasyf_write(ncid, "iglo_llim_proc", g_lo%llim_proc, count=scalar_count)
×
364

365
          ! Fill processor and layout information for plotting purposes
366
          call neasyf_write(ncid, "iglo_kykxsel", iglo_kykxsel, &
367
               dim_names=[character(len=7)::"aky", "akx", "species", "egrid", "lambda"], &
368
               count=glo_counts)
×
369
          call neasyf_write(ncid, "procid_kykxsel", procid_kykxsel, &
370
               dim_names=[character(len=7)::"aky", "akx", "species", "egrid", "lambda"], &
371
               count=glo_counts)
×
372

373
          call neasyf_write(ncid, "jtwist", jtwist, count=scalar_count)
×
374
       end if
375
    end if
376

377
    if (save_velocities_info) then
×
378
       call neasyf_write(ncid, "vpa", vpa, dim_names=["theta", "sign ", "glo  "], &
379
            start=start_pos, count=counts)
×
380
       call neasyf_write(ncid, "vperp2", vperp2, dim_names=["theta", "glo  "], &
381
            start=start_pos(1:3:2), count=counts(1:3:2))
×
382
    end if
383

384
    ! Decide if we are saving the fields
385
    this_proc_saves_fields = (iproc == proc_to_save_fields) .or. (proc_to_save_fields < 0)
×
386

387
    if (this_proc_saves_fields .or. .not. is_one_file_per_processor) then
×
388
       ! In parallel, all processes need to call `neasyf_write`, but
389
       ! only one should actually write any data
390
       if (this_proc_saves_fields) then
×
391
          field_counts = shape(phinew)
×
392
       else
393
          field_counts = [0, 0, 0]
×
394
       end if
395

396
       if (has_phi) then
×
397
          call neasyf_write(ncid, "phi_r", real(phinew), dim_names=field_dim_names, count=field_counts)
×
398
          call neasyf_write(ncid, "phi_i", aimag(phinew), dim_names=field_dim_names, count=field_counts)
×
399
       end if
400

401
       if (has_apar) then
×
402
          call neasyf_write(ncid, "apar_r", real(aparnew), dim_names=field_dim_names, count=field_counts)
×
403
          call neasyf_write(ncid, "apar_i", aimag(aparnew), dim_names=field_dim_names, count=field_counts)
×
404
       end if
405

406
       if (has_bpar) then
×
407
          call neasyf_write(ncid, "bpar_r", real(bparnew), dim_names=field_dim_names, count=field_counts)
×
408
          call neasyf_write(ncid, "bpar_i", aimag(bparnew), dim_names=field_dim_names, count=field_counts)
×
409
       end if
410
    end if
411

412
    ! Because we want to be able to restart using exb shear from a
413
    ! case which does not have exb shear we always add kx_shift and
414
    ! theta0_shift even if no exb shear present in simulation)
415
    if (is_one_file_per_processor .or. proc0) then
×
416
       exb_count = [naky]
×
417
    else
418
       exb_count = [0]
×
419
    end if
420

421
    if (allocated(kx_shift)) then
×
422
       stmp = kx_shift
×
423
    else
424
       stmp = 0.
×
425
    end if
426
    call neasyf_write(ncid, "kx_shift", stmp, dim_names=["aky"], count=exb_count)
×
427

428
    if (allocated(theta0_shift)) then
×
429
       stmp = theta0_shift
×
430
    else
431
       stmp = 0.
×
432
    end if
433
    call neasyf_write(ncid, "theta0_shift", stmp, dim_names=["aky"], count=exb_count)
×
434

435
    ! Close the file
436
    call neasyf_close(ncid)
×
437

438
    ! Deallocate local arrays
439
    if (allocated(iglo_kykxsel)) deallocate(iglo_kykxsel)
×
440
    if (allocated(procid_kykxsel)) deallocate(procid_kykxsel)
×
441

442
#else
443
    if (proc0) write (error_unit(),*) 'WARNING: gs2_save_for_restart is called without netcdf library'
444
    UNUSED_DUMMY(g); UNUSED_DUMMY(t0); UNUSED_DUMMY(vnm); UNUSED_DUMMY(has_phi)
445
    UNUSED_DUMMY(has_apar); UNUSED_DUMMY(has_bpar); UNUSED_DUMMY(code_dt)
446
    UNUSED_DUMMY(code_dt_prev1); UNUSED_DUMMY(code_dt_prev2); UNUSED_DUMMY(code_dt_max)
447
    UNUSED_DUMMY(fileopt); UNUSED_DUMMY(save_glo_info_and_grids)
448
    UNUSED_DUMMY(save_velocities); UNUSED_DUMMY(header)
449
#endif
450
  end subroutine gs2_save_for_restart
×
451

452
  !> Save g slices
453
  subroutine gs2_save_slice(g_in, it, ik, il, ie, is, time, nout, fileopt, header)
×
454
# ifdef NETCDF
455
    use neasyf, only: neasyf_open, neasyf_dim, neasyf_write, neasyf_close
456
    use gs2_metadata, only: create_metadata
457
    use unit_tests, only: debug_message
458
    use mp, only: iproc, proc0
459
    use optionals, only: get_option_with_default
460
    use convert, only: c2r
461
    use kt_grids, only: aky, akx, naky, ntheta0
462
    use le_grids, only: energy, al, negrid, nlambda
463
    use species, only: nspec
464
    use theta_grid, only: theta
465
    use gs2_layouts, only: proc_id, idx
466
# endif
467
    use theta_grid, only: ntgrid
468
    use gs2_layouts, only: g_lo
469
    use standard_header, only : standard_header_type
470
    implicit none
471
    !> The distribution function \(g\)
472
    complex, dimension (-ntgrid:, :, g_lo%llim_proc:), intent (in) :: g_in
473
    !> The indices of the slice to save
474
    integer, intent(in) :: it, ik, il, ie, is
475
    !> The current time
476
    real, intent(in) :: time
477
    !> The current time output index
478
    integer, intent(in), optional :: nout
479
    !> Optional extra filename infix
480
    character (len=*), intent (in), optional :: fileopt
481
    !> Header for files with build and run information
482
    type(standard_header_type), optional, intent(in) :: header
483
# ifdef NETCDF
484
    character (run_name_size) :: file_proc
485
    integer :: the_iglo, the_proc
486
    integer, parameter :: verb = 3
487
    type(standard_header_type) :: local_header
×
488
    character (len=*), parameter :: file_opt_base = '.slice'
489
    character(len=5), dimension(*), parameter :: g_slice_dim_names = &
490
         ["ri   ", "theta", "sign ", "t    "]
491
    logical :: appending, previous_file_exists
492
    real, dimension(:, :, :), allocatable :: ri_gslice
×
493
    integer, save :: nout_local = 1
494
    logical :: error_exit
495
    ! File handle
496
    integer :: ncid
497

498
    call debug_message(verb, 'gs2_save::gs2_save_slice initialising')
×
499

500
    ! Sanity check the inputs
501
    error_exit = .false.
×
502
    if (it < 1 .or. it > ntheta0) error_exit = .true.
×
503
    if (ik < 1 .or. ik > naky) error_exit = .true.
×
504
    if (il < 1 .or. il > nlambda) error_exit = .true.
×
505
    if (ie < 1 .or. ie > negrid) error_exit = .true.
×
506
    if (is < 1 .or. is > nspec) error_exit = .true.
×
507
    if (proc0 .and. error_exit) write(6,'("Error: Invalid index based to gs2_save_slice.")')
×
508
    if (error_exit) return
×
509

510
    the_iglo = idx(g_lo, ik, it, il, ie, is)
×
511
    the_proc = proc_id(g_lo, the_iglo)
×
512

513
    ! The proc which owns this slice is responsible for writing and
514
    ! does the rest of the work so let us return if that's not this
515
    ! proc
516
    if (iproc /= the_proc) return
×
517

518
    if (present(header)) then
×
519
      local_header = header
×
520
    else
521
      local_header = standard_header_type()
×
522
    end if
523

524
    ! Open the file and define dimensions and variables
525
    file_proc = get_file_proc(.false., file_opt_base // fileopt)
×
526

527
    call debug_message(verb, 'gs2_save::gs2_save_slice opening file')
×
528
    call debug_message(verb, 'file proc is')
×
529
    call debug_message(verb, trim(adjustl(file_proc)))
×
530

531
    nout_local = get_option_with_default(nout, nout_local)
×
532

533
    ! See if the output file already exists, so we know how to open
534
    ! the file.  This would be simpler if netCDF had a "open if it
535
    ! exists, create if it doesn't" mode, but it doesn't so we have
536
    ! to do this instead
537
    inquire(file=trim(file_proc), exist=previous_file_exists)
×
538
    appending = (previous_file_exists .and. nout_local > 1)
×
539

540
    if (appending) then
×
541
       ncid = neasyf_open(trim(file_proc), 'rw')
×
542
    else
543
       ncid = neasyf_open(trim(file_proc), 'w')
×
544
       call create_metadata(ncid, "GS2 slice file", local_header)
×
545

546
       call debug_message(verb, 'gs2_save::gs2_save_slice defining dimensions')
×
547
       call neasyf_dim(ncid, "ri", dim_size = 2, long_name = "real/imaginary components")
×
548
       call neasyf_dim(ncid, "theta", values = theta, long_name="Poloidal angle", units="rad")
×
549
       call neasyf_dim(ncid, "sign",  dim_size = 2, long_name = "sign of v||")
×
550
       call neasyf_dim(ncid, "t", unlimited = .true., long_name = "Time", units = "L/vt")
×
551

552
       call debug_message(verb, 'gs2_save::gs2_save_slice defining constant variables')
×
553
       call neasyf_write(ncid, "it", it)
×
554
       call neasyf_write(ncid, "ik", ik)
×
555
       call neasyf_write(ncid, "il", il)
×
556
       call neasyf_write(ncid, "ie", ie)
×
557
       call neasyf_write(ncid, "is", is)
×
558

559
       call neasyf_write(ncid, "iglo", the_iglo)
×
560
       call neasyf_write(ncid, "iproc", the_proc)
×
561

562
       call neasyf_write(ncid, "kx", akx(it))
×
563
       call neasyf_write(ncid, "ky", aky(ik))
×
564
       call neasyf_write(ncid, "lambda", al(il))
×
565
       call neasyf_write(ncid, "energy", energy(ie, is))
×
566
    end if
567

568
    call neasyf_write(ncid, "t", time, start = [nout_local], dim_names = ["t"])
×
569

570
    ! Convert from complex to real
571
    allocate(ri_gslice(2, size(theta), 2))
×
572
    call c2r(g_in(:, :, the_iglo), ri_gslice)
×
573
    call neasyf_write(ncid, "g_slice", ri_gslice, start = [1, 1, 1, nout_local], &
574
         dim_names = g_slice_dim_names)
×
575

576
    ! Close the file
577
    call neasyf_close(ncid)
×
578

579
    nout_local = nout_local + 1
×
580
# else
581
    UNUSED_DUMMY(g_in); UNUSED_DUMMY(it); UNUSED_DUMMY(ik) ; UNUSED_DUMMY(il)
582
    UNUSED_DUMMY(ie); UNUSED_DUMMY(is); UNUSED_DUMMY(time); UNUSED_DUMMY(nout)
583
    UNUSED_DUMMY(fileopt); UNUSED_DUMMY(header)
584
# endif
585
  end subroutine gs2_save_slice
×
586

587
  !> FIXME : Add documentation
588
  subroutine gs2_restore_many (g, scale, has_phi, has_apar, has_bpar, fileopt)
×
589
!MR, 2007: restore kx_shift array if already allocated
590
# ifdef NETCDF
591
    use array_utils, only: zero_array
592
    use mp, only: iproc, proc0, broadcast, mp_comm, mp_info
593
    use fields_arrays, only: phinew, aparnew, bparnew
594
    use fields_arrays, only: phi, apar, bpar
595
    use dist_fn_arrays, only: gexp_1, gexp_2, gexp_3, kx_shift, theta0_shift
596
    use kt_grids, only: naky, ntheta0
597
    use gs2_layouts, only: layout
598
    use neasyf, only: neasyf_open, neasyf_read, neasyf_close
599
    use netcdf_utils, only: check_netcdf_file_precision, kind_nf
600
# endif
601
    use mp, only: mp_abort
602
    use theta_grid, only: ntgrid
603
    use gs2_layouts, only: g_lo
604
    implicit none
605
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (out) :: g
606
    real, intent (in) :: scale
607
    logical, intent (in) :: has_phi, has_apar, has_bpar
608
    character (len=*), intent(in), optional :: fileopt
609
# ifdef NETCDF
610
    logical :: this_proc_reads_fields, need_to_broadcast_fields
611
    integer, dimension(3) :: counts, start_pos
612
    real, dimension(naky) :: stmp
×
613
    character (run_name_size) :: file_proc
614
    character (len = 5) :: restart_layout
615
    real :: fac
616
    logical :: has_explicit_source_terms, is_one_file_per_processor, has_gexp_1
617
    logical, save :: explicit_warning_given = .false.
618
    integer(kind_nf) :: ncid
619
    real, allocatable, dimension(:,:,:) :: tmpr, tmpi, ftmpr, ftmpi
×
620

621
    is_one_file_per_processor = read_many .or. (.not. has_netcdf_parallel)
×
622
    file_proc = get_file_proc(is_one_file_per_processor, fileopt)
×
623

624
    if (is_one_file_per_processor) then
×
625
       ncid = neasyf_open(file_proc, "r")
×
626
    else
627
       ncid = neasyf_open(file_proc, "r", comm=mp_comm, info=mp_info)
×
628
    end if
629

630
    call check_netcdf_file_precision (ncid)
×
631

632
    ! Check dimension sizes are compatible with the current simulation inputs
633
    call verify_compatible_dim_sizes(ncid, "theta", 2 * ntgrid + 1)
×
634
    call verify_compatible_dim_sizes(ncid, "sign", 2)
×
635
    call verify_compatible_dim_sizes(ncid, "aky", naky)
×
636
    call verify_compatible_dim_sizes(ncid, "akx", ntheta0)
×
637
    if (is_one_file_per_processor) then
×
638
       call verify_compatible_dim_sizes(ncid, "glo", g_lo%ulim_proc - g_lo%llim_proc + 1)
×
639
    else
640
       call verify_compatible_dim_sizes(ncid, "glo", g_lo%ulim_world + 1)
×
641
    end if
642

643
    if (include_explicit_source_in_restart) then
×
644
       has_explicit_source_terms = .true.
×
645

646
#ifndef SHMEM
647
       has_gexp_1 = allocated(gexp_1)
×
648
#else
649
       has_gexp_1 = associated(gexp_1)
650
#endif
651

652
       if (has_gexp_1) &
×
653
          has_explicit_source_terms = has_explicit_source_terms .and. &
654
               (variable_exists(ncid, "gexp_1_r") .and. variable_exists(ncid, "gexp_1_i"))
×
655

656
       if (allocated(gexp_2)) &
×
657
          has_explicit_source_terms = has_explicit_source_terms .and. &
658
               (variable_exists(ncid, "gexp_2_r") .and. variable_exists(ncid, "gexp_2_i"))
×
659

660
       if (allocated(gexp_3))  &
×
661
          has_explicit_source_terms = has_explicit_source_terms .and. &
662
               (variable_exists(ncid, "gexp_3_r") .and. variable_exists(ncid, "gexp_3_i"))
×
663

664
       if ((.not. explicit_warning_given).and. proc0 .and. .not. has_explicit_source_terms) then
×
665
          write(*, '("Warning: At least one explicit source term absent in restart file.")')
×
666
          write(*, '("  Will not load these terms. This is probably OK unless you expect the restart file to contain these terms.")')
×
667
          write(*, '("  This warning will not be repeated.")')
×
668

669
          ! Update this flag to ensure we don't give this warning again
670
          explicit_warning_given = .true.
×
671
       end if
672

673
    else
674
       ! Always skip trying to get the explicit source terms if we're not
675
       ! including them
676
       has_explicit_source_terms = .false.
×
677
    end if
678

679
    call neasyf_read(ncid, "layout", restart_layout)
×
680

681
    ! Abort if the layouts don't match
682
    if(restart_layout /= layout) then
×
683
       call mp_abort("Incompatible layouts in restart file ("//restart_layout//") and simulation ("//layout//")",.true.)
×
684
    end if
685

686
    allocate (tmpr(2*ntgrid+1,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
687
    allocate (tmpi(2*ntgrid+1,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
688

689
    call zero_array(tmpr); call zero_array(tmpi)
×
690

691
    if (is_one_file_per_processor) then
×
692
       start_pos = [1, 1, 1]
×
693
    else
694
       start_pos = [1, 1, g_lo%llim_proc+1]
×
695
    end if
696
    counts = shape(tmpr)
×
697

698
    call neasyf_read(ncid, "gr", tmpr, start=start_pos, count=counts)
×
699
    call neasyf_read(ncid, "gi", tmpi, start=start_pos, count=counts)
×
700
    g = cmplx(tmpr, tmpi)
×
701

702
    if (include_explicit_source_in_restart .and. has_explicit_source_terms) then
×
703
       ! Explicit source term
704
#ifndef SHMEM
705
       has_gexp_1 = allocated(gexp_1)
×
706
#else
707
       has_gexp_1 = associated(gexp_1)
708
#endif
709

710
       if (has_gexp_1) then
×
711
          call neasyf_read(ncid, "gexp_1_r", tmpr, start=start_pos, count=counts)
×
712
          call neasyf_read(ncid, "gexp_1_i", tmpi, start=start_pos, count=counts)
×
713
          gexp_1 = cmplx(tmpr, tmpi)
×
714
       endif
715

716
       ! Explicit source term
717
       if(allocated(gexp_2)) then
×
718
          call neasyf_read(ncid, "gexp_2_r", tmpr, start=start_pos, count=counts)
×
719
          call neasyf_read(ncid, "gexp_2_i", tmpi, start=start_pos, count=counts)
×
720
          gexp_2 = cmplx(tmpr, tmpi)
×
721
       endif
722

723
       ! Explicit source term
724
       if(allocated(gexp_3)) then
×
725
          call neasyf_read(ncid, "gexp_3_r", tmpr, start=start_pos, count=counts)
×
726
          call neasyf_read(ncid, "gexp_3_i", tmpi, start=start_pos, count=counts)
×
727
          gexp_3 = cmplx(tmpr, tmpi)
×
728
       endif
729
    end if
730

731
    deallocate(tmpr, tmpi)
×
732

733
    if (allocated(kx_shift)) then   ! MR begin
×
734
       call neasyf_read(ncid, "kx_shift", stmp)
×
735
       kx_shift = stmp
×
736
    endif   ! MR end
737

738
    if (allocated(theta0_shift)) then
×
739
       call neasyf_read(ncid, "theta0_shift", stmp)
×
740
       theta0_shift = stmp
×
741
    endif
742

743
    ! Decide if we are reading the fields
744
    need_to_broadcast_fields = proc_to_save_fields >= 0
×
745
    this_proc_reads_fields = (iproc == proc_to_save_fields) .or. (.not. need_to_broadcast_fields)
×
746

747
    if (this_proc_reads_fields) then
×
748
       allocate (ftmpr(2*ntgrid+1,ntheta0,naky))
×
749
       allocate (ftmpi(2*ntgrid+1,ntheta0,naky))
×
750

751
       if (has_phi) then
×
752
          call neasyf_read(ncid, "phi_r", ftmpr)
×
753
          call neasyf_read(ncid, "phi_i", ftmpi)
×
754
          phinew = cmplx(ftmpr, ftmpi)
×
755
       end if
756

757
       if (has_apar) then
×
758
          call neasyf_read(ncid, "apar_r", ftmpr)
×
759
          call neasyf_read(ncid, "apar_i", ftmpi)
×
760
          aparnew = cmplx(ftmpr, ftmpi)
×
761
       end if
762

763
       if (has_bpar) then
×
764
          call neasyf_read(ncid, "bpar_r", ftmpr)
×
765
          call neasyf_read(ncid, "bpar_i", ftmpi)
×
766
          bparnew = cmplx(ftmpr, ftmpi)
×
767
       end if
768

769
       deallocate(ftmpr, ftmpi)
×
770
    end if
771

772
    call neasyf_close(ncid)
×
773

774
    if (need_to_broadcast_fields) then
×
775
       if (has_phi) call broadcast(phinew, proc_to_save_fields)
×
776
       if (has_apar) call broadcast(aparnew, proc_to_save_fields)
×
777
       if (has_bpar) call broadcast(bparnew, proc_to_save_fields)
×
778
    end if
779

780
    if (has_phi) call zero_array(phi)
×
781
    if (has_apar) call zero_array(apar)
×
782
    if (has_bpar) call zero_array(bpar)
×
783

784
    if (scale > 0.) then
×
785
       g = g*scale
×
786
       phinew = phinew*scale
×
787
       aparnew = aparnew*scale
×
788
       bparnew = bparnew*scale
×
789
    else
790
       fac = - scale/(maxval(abs(phinew)))
×
791
       g = g*fac
×
792
       phinew = phinew*fac
×
793
       aparnew = aparnew*fac
×
794
       bparnew = bparnew*fac
×
795
    end if
796
    
797
# else
798
    call mp_abort("Cannot restore from restart file without netcdf.", .true.)
799
    UNUSED_DUMMY(scale); UNUSED_DUMMY(has_phi); UNUSED_DUMMY(has_apar)
800
    UNUSED_DUMMY(has_bpar); UNUSED_DUMMY(fileopt) ; g = 0.0
801
# endif
802
  end subroutine gs2_restore_many
×
803

804
  !> This routine writes a passed square complex array to a file
805
  !> with passed name
806
  subroutine gs2_save_response(resp, fname, code_dt, condition, header)
×
807
    use file_utils, only: error_unit
808
    use standard_header, only: standard_header_type
809
#ifdef NETCDF
810
    use optionals, only: get_option_with_default
811
    use convert, only: c2r
812
    use gs2_metadata, only: create_metadata
813
    use neasyf, only: neasyf_open, neasyf_dim, neasyf_write, neasyf_close
814
#endif
815
    implicit none
816
    complex,dimension(:,:), intent(in) :: resp
817
    character(len=*), intent(in) :: fname
818
    real, intent(in) :: code_dt
819
    real, intent(in), optional :: condition
820
    !> Header for files with build and run information
821
    type(standard_header_type), optional, intent(in) :: header
822
    integer :: sz
823

824
#ifdef NETCDF
825
    integer :: ncid
826
    real, dimension(:,:,:), allocatable :: ri_resp
×
827
    ! Actual value for optional `header` input
828
    type(standard_header_type) :: local_header
×
829
#else
830
    integer :: unit
831
#endif
832
    !Currently only support serial writing, but could be by any proc
833
    !so we have to make sure only one proc calls this routine
834

835
    !Verify we have a square array
836
    sz=size(resp(:,1))
×
837
    if(sz/=size(resp(1,:))) then
×
838
       write(error_unit(),'("Error: gs2_save_response expects a square array input.")')
×
839
       return
×
840
    endif
841

842
#ifdef NETCDF
843
    if (present(header)) then
×
844
      local_header = header
×
845
    else
846
      local_header = standard_header_type()
×
847
    end if
848

849
    ncid = neasyf_open(fname, "w")
×
850
    call create_metadata(ncid, "GS2 response matrix", local_header)
×
851

852
    call neasyf_dim(ncid, "ri", dim_size=2)
×
853
    call neasyf_dim(ncid, "ax1", dim_size=sz)
×
854
    call neasyf_dim(ncid, "ax2", dim_size=sz)
×
855

856
    call neasyf_write(ncid, "dt", code_dt)
×
857
    call neasyf_write(ncid, "condition", get_option_with_default(condition, -1.0))
×
858

859
    !/Convert complex to ri and write
860
    allocate(ri_resp(2,sz,sz))
×
861
    call c2r(resp,ri_resp)
×
862
    call neasyf_write(ncid, "response", ri_resp, dim_names=["ri ", "ax1", "ax2"])
×
863
    deallocate(ri_resp)
×
864

865
    !/Now close the file
866
    call neasyf_close(ncid)
×
867

868
#else
869
!Fall back on binary output if no NETCDF
870
    !Open file and write
871
    open(newunit=unit,file=fname,form="unformatted")
872
    write(unit) resp
873
    close(unit)
874

875
    UNUSED_DUMMY(code_dt); UNUSED_DUMMY(condition); UNUSED_DUMMY(header)
876
#endif
877
  end subroutine gs2_save_response
×
878

879
  !> This routine reads a square complex array from a file
880
  !! with passed name
881
  subroutine gs2_restore_response(resp, fname, code_dt, condition)
×
882
    use file_utils, only: error_unit
883
#ifdef NETCDF
884
    use neasyf, only: neasyf_open, neasyf_close, neasyf_read
885
    use convert, only: r2c
886
#endif
887
    implicit none
888
    complex, dimension(:,:), intent(out) :: resp
889
    character(len=*), intent(in) :: fname
890
    real, intent(out) :: code_dt
891
    real, intent(out), optional :: condition
892
    integer :: sz
893
#ifdef NETCDF
894
    integer :: ncid
895
    real, dimension(:,:,:), allocatable :: ri_resp
×
896
#else
897
    integer :: unit
898
#endif
899
    !Currently only support serial reading, but could be by any proc
900
    !so we have to make sure only one proc calls this routine
901

902
    !Verify we have a square array
903
    sz=size(resp(:,1))
×
904
    if(sz/=size(resp(1,:))) then
×
905
       write(error_unit(),'("Error: gs2_restore_response expects a square array output.")')
×
906
       return
×
907
    endif
908

909
#ifdef NETCDF
910
    ncid = neasyf_open(fname, "r")
×
911

912
    !/Read and convert ri to complex
913
    allocate(ri_resp(2,sz,sz))
×
914
    call neasyf_read(ncid, "response", ri_resp)
×
915
    call r2c(resp,ri_resp)
×
916
    deallocate(ri_resp)
×
917

918
    !/Get the time step
919
    call neasyf_read(ncid, "dt", code_dt)
×
920

921
    !/Get the condition number
922
    if (present(condition)) call neasyf_read(ncid, "condition", condition)
×
923

924
    !/Now close the file
925
    call neasyf_close(ncid)
×
926
#else
927
!Fall back on binary output if no NETCDF
928
    !Open file and write
929
    open(newunit=unit,file=fname,form="unformatted")
930
    read(unit) resp
931
    close(unit)
932

933
    !Set outputs to something invalid
934
    code_dt = -1 ; condition = 0.0
935
#endif
936
  end subroutine gs2_restore_response
×
937

938
  !> Initialises a file for saving output of eigensolver to netcdf
939
  subroutine init_eigenfunc_file(fname, IDs)
×
940
#ifdef NETCDF
941
    use theta_grid, only: theta
942
    use neasyf, only: neasyf_open, neasyf_dim
943
#endif
944
    implicit none
945
    character(len=*), intent(in) :: fname
946
    type(EigNetcdfID), intent(inout) :: IDs
947

948
    !Set nconv counter to 0
949
    IDs%nconv_count=0
×
950

951
#ifdef NETCDF
952
    IDs%ncid = neasyf_open(fname, "w")
×
953

954
    call neasyf_dim(IDs%ncid, "ri", dim_size=2)
×
955
    call neasyf_dim(IDs%ncid, "theta", theta)
×
956
    call neasyf_dim(IDs%ncid, "conv", unlimited=.true.)
×
957
#else
958
    UNUSED_DUMMY(fname)
959
#endif
960
  end subroutine init_eigenfunc_file
×
961

962
  !> Add an eigenpairs data to file
963
  subroutine add_eigenpair_to_file(eval, has_phi, has_apar, has_bpar, IDs, my_conv)
×
964
#ifdef NETCDF
965
    use fields_arrays, only: phinew, aparnew, bparnew
966
    use convert, only: c2r
967
    use theta_grid, only: ntgrid
968
    use neasyf, only: neasyf_write
969
    use optionals, only: get_option_with_default
970
#endif
971
    complex, intent(in) :: eval !Note just use fields to get eigenvectors
972
    real, intent(in), optional :: my_conv
973
    type(EigNetcdfID), intent(inout) :: IDs
974
    logical, intent(in) :: has_phi, has_apar, has_bpar
975
#ifdef NETCDF
976
    real, dimension(2) :: ri_omega
977
    real, dimension(:,:), allocatable :: ri_field
×
978
    integer, dimension(3) :: start_field, count_field
979
    real :: local_conv
980
    character(*), dimension(*), parameter :: eigen_field_dim_names = [character(len=5)::"ri", "theta", "conv"]
981

982
    !First increment counter
983
    IDs%nconv_count=IDs%nconv_count+1
×
984
    start_field = [1, 1, IDs%nconv_count]
×
985
    count_field = [2, 2*ntgrid+1, 1]
×
986

987
    local_conv = get_option_with_default(my_conv, real(IDs%nconv_count))
×
988

989
    call neasyf_write(IDs%ncid, "conv", local_conv, dim_names=["conv"], &
990
         start=[IDs%nconv_count])
×
991

992
    !/Omega
993
    ri_omega(1)=real(eval)
×
994
    ri_omega(2)=aimag(eval)
×
995
    call neasyf_write(IDs%ncid, "omega", ri_omega, dim_names=["ri  ", "conv"], &
996
         start=[1, IDs%nconv_count], count=[2,1])
×
997

998
    !/Fields
999
    allocate(ri_field(2,2*ntgrid+1))
×
1000
    if(has_phi)then
×
1001
       call c2r(phinew(:,1,1),ri_field)
×
1002
       call neasyf_write(IDs%ncid, "phi", ri_field, dim_names=eigen_field_dim_names, &
1003
            start=start_field, count=count_field)
×
1004
    endif
1005
    if(has_apar)then
×
1006
       call c2r(aparnew(:,1,1),ri_field)
×
1007
       call neasyf_write(IDs%ncid, "apar", ri_field, dim_names=eigen_field_dim_names, &
1008
            start=start_field, count=count_field)
×
1009
    endif
1010
    if(has_bpar)then
×
1011
       call c2r(bparnew(:,1,1),ri_field)
×
1012
       call neasyf_write(IDs%ncid, "bpar", ri_field, dim_names=eigen_field_dim_names, &
1013
            start=start_field, count=count_field)
×
1014
    endif
1015
    deallocate(ri_field)
×
1016
#else
1017
    UNUSED_DUMMY(eval); UNUSED_DUMMY(has_phi); UNUSED_DUMMY(has_apar); UNUSED_DUMMY(has_bpar)
1018
    UNUSED_DUMMY(IDs); UNUSED_DUMMY(my_conv)
1019
#endif
1020
  end subroutine add_eigenpair_to_file
×
1021

1022
  !> Close the eigenfunction file
1023
  subroutine finish_eigenfunc_file(IDs)
×
1024
#ifdef NETCDF
1025
    use neasyf, only: neasyf_close
1026
#endif
1027
    implicit none
1028
    type(EigNetcdfID), intent(inout) :: IDs
1029
#ifdef NETCDF
1030
    call neasyf_close(IDs%ncid)
×
1031
#else
1032
    UNUSED_DUMMY(IDs)
1033
#endif
1034
  end subroutine finish_eigenfunc_file
×
1035

1036
  !> This function checks to see if we can create a file with name
1037
  !! <restart_file>//<SomeSuffix> if not then our restarts are not
1038
  !! going to be possible and we return false. Can also be used to check
1039
  !! that we can read from the restart file (which assumes it exists).
1040
  function restart_writable(read_only, my_file, error_message)
×
1041
    use mp, only: proc0, broadcast
1042
    use optionals, only: get_option_with_default
1043
    implicit none
1044
    !> If present and true, only check that files can be read
1045
    logical, intent(in), optional :: read_only
1046
    !> An optional specific filename to check
1047
    character(len=*),intent(in),optional::my_file
1048
    !> Error message returned from `open` if there was a problem
1049
    character(len=:), allocatable, optional, intent(out) :: error_message
1050

1051
    character(len=*), parameter :: SuffixTmp = '.ThisIsATestFile'
1052
    character(9) :: open_mode
1053
    character(6) :: close_mode
1054
    character(run_name_size) :: local_file, filename
1055
    logical :: restart_writable
1056
    integer :: unit, ierr
1057
    character(len=200) :: io_err_msg
1058

1059
    ierr=-200
×
1060
    local_file = trim(get_option_with_default(my_file, restart_file))
×
1061

1062
    ! On proc0 try to open tmp file for writing
1063
    if (proc0)then
×
1064
       ! Default open and close modes
1065
       open_mode = 'readwrite'
×
1066
       close_mode = 'delete'
×
1067

1068
       ! If we want to test write capability then do it with an unusual
1069
       ! file name to prevent clobber
1070
       filename = trim(local_file) // SuffixTmp
×
1071

1072
       ! Set filemode to READ if read_only=T
1073
       if (get_option_with_default(read_only, .false.)) then
×
1074
          ! If we're only checking a file can be read, then don't
1075
          ! delete the file after we're done
1076
          open_mode = 'read'
×
1077
          close_mode = 'keep'
×
1078
          ! If checking readonly then we need to make sure we try to
1079
          ! read from an existing file
1080
          filename = local_file
×
1081
       end if
1082

1083
       open(newunit=unit, file=trim(filename), &
1084
            iostat=ierr, action=open_mode, &
1085
            iomsg=io_err_msg)
×
1086

1087
       ! If open was successful then we can close the file and delete it
1088
       if (ierr == 0) close(unit=unit, status=close_mode)
×
1089
    endif
1090

1091
    ! Now make sure everyone knows the answer
1092
    call broadcast(ierr)
×
1093
    restart_writable = (ierr == 0)
×
1094

1095
    if (.not. present(error_message)) return
×
1096

1097
    if (restart_writable) then
×
1098
      error_message = ""
×
1099
    else
1100
      error_message = trim(io_err_msg)
×
1101
    end if
1102
  end function restart_writable
×
1103

1104
  !> FIXME : Add documentation
1105
  subroutine init_gs2_save
28✔
1106
  end subroutine init_gs2_save
28✔
1107

1108
  !> Sets the base of the restart file to be used when
1109
  !> writing/reading files.
1110
  subroutine set_restart_file (file)
12✔
1111
    use mp, only: proc0
1112
    use file_utils, only: error_unit
1113
    implicit none
1114
    character(len=*), intent (in) :: file
1115
    if (proc0 .and. len_trim(file) > run_name_size) then
12✔
1116
       write( error_unit(), '("Argument to set_restart_file exceeds restart_file size so truncating")')
×
1117
    end if
1118
    restart_file = file(1:min(len_trim(file), run_name_size))
12✔
1119
  end subroutine set_restart_file
12✔
1120

1121
  !> FIXME : Add documentation  
1122
  subroutine finish_gs2_save
28✔
1123
  end subroutine finish_gs2_save
28✔
1124

1125
  !> FIXME : Add documentation
1126
  subroutine init_dt (delt0, delt1, delt2, delt_max, not_set_value)
×
1127
# ifdef NETCDF
1128
    use mp, only: proc0, broadcast
1129
    use optionals, only: get_option_with_default
1130
    use neasyf, only: neasyf_open, neasyf_read, neasyf_close
1131
    use netcdf_utils, only: kind_nf
1132
# else
1133
    use mp, only: mp_abort
1134
# endif
1135
    implicit none
1136
    real, intent (in out) :: delt0, delt1, delt2, delt_max
1137
    real, intent(in), optional :: not_set_value
1138
# ifdef NETCDF
1139
    character (run_name_size) :: file_proc
1140
    real :: not_set_value_to_use
1141
    logical :: is_one_file_per_processor
1142
    ! NetCDF handles
1143
    integer(kind_nf) :: ncid
1144
    real, dimension(0:3) :: time_values
1145
    if (proc0) then
×
1146
       is_one_file_per_processor = read_many .or. (.not. has_netcdf_parallel)
×
1147
       file_proc = get_file_proc(is_one_file_per_processor)
×
1148
       ncid = neasyf_open(file_proc, "r")
×
1149

1150
       not_set_value_to_use = get_option_with_default(not_set_value, -1.0)
×
1151
       time_values = not_set_value_to_use
×
1152
       ! Note unlike the explicit source terms all three time steps should always
1153
       ! be available in the restart file so we don't silence the error messages here.
1154
       ! The only situation we are likely to come across where the delt1 and delt2
1155
       ! values aren't available is where we are trying to read an old restart file.
1156
       ! This will then lead to the error/warning being displayed but the code should
1157
       ! carry on as intended and the missing steps will be set to a special value to
1158
       ! indicate they have not been set yet.
1159
       if (variable_exists(ncid, "delt0")) call neasyf_read(ncid, "delt0", time_values(0))
×
1160
       if (variable_exists(ncid, "delt1")) call neasyf_read(ncid, "delt1", time_values(1))
×
1161
       if (variable_exists(ncid, "delt2")) call neasyf_read(ncid, "delt2", time_values(2))
×
1162
       if (variable_exists(ncid, "delt_max")) call neasyf_read(ncid, "delt_max", time_values(3))              
×
1163
       call neasyf_close(ncid)
×
1164
    endif
1165

1166
    call broadcast(time_values)
×
1167
    delt0 = time_values(0) ; delt1 = time_values(1) ; delt2 = time_values(2)
×
1168
    delt_max = time_values(3)
×
1169
# else
1170
    call mp_abort("Cannot load initial dt from restart without netcdf.", .true.)
1171
    UNUSED_DUMMY(delt0); UNUSED_DUMMY(delt1); UNUSED_DUMMY(delt2); UNUSED_DUMMY(delt_max)
1172
    UNUSED_DUMMY(not_set_value)
1173
# endif
1174
  end subroutine init_dt
×
1175

1176
!> FIXME : Add documentation
1177
  subroutine init_vnm (vnm)
×
1178
# ifdef NETCDF
1179
    use mp, only: proc0, broadcast
1180
    use neasyf, only: neasyf_open, neasyf_read, neasyf_close
1181
    use netcdf_utils, only: kind_nf
1182
# else
1183
    use mp, only: mp_abort
1184
# endif
1185
    implicit none
1186
    real, dimension(2), intent (in out) :: vnm
1187
# ifdef NETCDF
1188
    character (run_name_size) :: file_proc
1189
    logical :: is_one_file_per_processor
1190
    ! NetCDF handles
1191
    integer(kind_nf) :: ncid
1192

1193
    if (proc0) then
×
1194
       is_one_file_per_processor = read_many .or. (.not. has_netcdf_parallel)
×
1195
       file_proc = get_file_proc(is_one_file_per_processor)
×
1196
       ncid = neasyf_open (file_proc, "r")
×
1197
       call neasyf_read(ncid, "vnm1", vnm(1))
×
1198
       call neasyf_read(ncid, "vnm2", vnm(2))
×
1199
       call neasyf_close(ncid)
×
1200
    endif
1201

1202
    call broadcast (vnm)
×
1203
# else
1204
    call mp_abort("Cannot load vnm from restart without netcdf.", .true.)
1205
    UNUSED_DUMMY(vnm)
1206
# endif
1207
  end subroutine init_vnm
×
1208

1209
  !> FIXME : Add documentation
1210
  !!
1211
  !! @note This routine gets a_ant and b_ant for proc 0 only!
1212
  subroutine init_ant_amp (a_ant, b_ant, nk_stir)
×
1213
# ifdef NETCDF
1214
    use constants, only: zi
1215
    use neasyf, only: neasyf_open, neasyf_read, neasyf_close
1216
    use mp, only: proc0, broadcast, mp_abort
1217
    use netcdf_utils, only: kind_nf
1218
# else
1219
    use mp, only: mp_abort
1220
# endif
1221
    implicit none
1222
    complex, dimension(:), intent (in out) :: a_ant, b_ant
1223
    integer, intent (in) :: nk_stir
1224
# ifdef NETCDF
1225
    character (run_name_size) :: file_proc
1226
    logical :: is_one_file_per_processor
1227
    ! NetCDF handles
1228
    integer(kind_nf) :: ncid
1229
    real, dimension(nk_stir) :: atmp
×
1230

1231
    if (proc0) then
×
1232
       is_one_file_per_processor = read_many .or. (.not. has_netcdf_parallel)
×
1233
       file_proc = get_file_proc(is_one_file_per_processor)
×
1234
       ncid = neasyf_open (file_proc, "r")
×
1235

1236
       call verify_compatible_dim_sizes(ncid, "nk_stir", nk_stir)
×
1237

1238
       atmp = 0.
×
1239
       call neasyf_read(ncid, "a_ant_r", atmp)
×
1240
       a_ant = atmp
×
1241

1242
       call neasyf_read(ncid, "a_ant_i", atmp)
×
1243
       a_ant = a_ant + zi * atmp
×
1244

1245
       call neasyf_read(ncid, "b_ant_r", atmp)
×
1246
       b_ant = atmp
×
1247

1248
       call neasyf_read(ncid, "b_ant_i", atmp)
×
1249
       b_ant = b_ant + zi * atmp
×
1250

1251
       call neasyf_close(ncid)
×
1252
    end if
1253
    call broadcast(a_ant)
×
1254
    call broadcast(b_ant)
×
1255
# else
1256
    call mp_abort("Cannot load antenna amplitudes from restart without netcdf.", .true.)
1257
    UNUSED_DUMMY(a_ant); UNUSED_DUMMY(b_ant); UNUSED_DUMMY(nk_stir)
1258
# endif
1259
  end subroutine init_ant_amp
×
1260

1261
  !> FIXME : Add documentation
1262
  subroutine read_t0_from_restart_file (tstart)
×
1263
# ifdef NETCDF
1264
    use mp, only: proc0, broadcast
1265
    use neasyf, only: neasyf_open, neasyf_read, neasyf_close
1266
    use netcdf_utils, only: kind_nf
1267
# else
1268
    use mp, only: mp_abort
1269
# endif
1270
    implicit none
1271
    real, intent (in out) :: tstart
1272
# ifdef NETCDF
1273
    character (run_name_size) :: file_proc
1274
    logical :: is_one_file_per_processor
1275
    ! NetCDF handles
1276
    integer(kind_nf) :: ncid
1277

1278
    if (proc0) then
×
1279
       is_one_file_per_processor = read_many .or. (.not. has_netcdf_parallel)
×
1280
       file_proc = get_file_proc(is_one_file_per_processor)
×
1281
       ncid = neasyf_open(file_proc, "r")
×
1282
       call neasyf_read(ncid, "t0", tstart)
×
1283
       call neasyf_close(ncid)
×
1284
    end if
1285

1286
    call broadcast (tstart)
×
1287
# else
1288
    call mp_abort("Cannot read t0 from restart file in build with no netcdf.", .true.)
1289
    UNUSED_DUMMY(tstart)
1290
# endif
1291
  end subroutine read_t0_from_restart_file
×
1292
end module gs2_save
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc