Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Generate Gaussian cube files
10 : ! **************************************************************************************************
11 : MODULE realspace_grid_openpmd
12 :
13 : #ifdef __OPENPMD
14 : USE cp_files, ONLY: close_file, &
15 : open_file
16 : USE cp_log_handling, ONLY: cp_logger_get_default_io_unit
17 : USE cp_output_handling_openpmd, ONLY: cp_openpmd_get_value_unit_nr, &
18 : cp_openpmd_per_call_value_type
19 : USE kinds, ONLY: default_string_length, &
20 : dp
21 : USE message_passing, ONLY: &
22 : file_amode_rdonly, file_offset, mp_comm_type, mp_file_descriptor_type, mp_file_type, &
23 : mp_file_type_free, mp_file_type_hindexed_make_chv, mp_file_type_set_view_chv, &
24 : mpi_character_size
25 : USE physcon, ONLY: a_bohr, e_charge, seconds
26 : USE openpmd_api, ONLY: &
27 : openpmd_attributable_type, &
28 : openpmd_dynamic_memory_view_type_1d, &
29 : openpmd_dynamic_memory_view_type_3d, &
30 : openpmd_mesh_type, &
31 : openpmd_particle_species_type, &
32 : openpmd_record_component_type, &
33 : openpmd_record_type, openpmd_type_double, openpmd_type_int
34 : USE pw_grid_types, ONLY: PW_MODE_LOCAL
35 : USE pw_types, ONLY: pw_r3d_rs_type
36 : USE util, ONLY: sort_unique
37 :
38 : #else
39 :
40 : USE pw_types, ONLY: pw_r3d_rs_type
41 : USE kinds, ONLY: dp
42 :
43 : #endif
44 :
45 : #include "../base/base_uses.f90"
46 :
47 : IMPLICIT NONE
48 :
49 : PRIVATE
50 :
51 : PUBLIC :: pw_to_openpmd
52 :
53 : #ifdef __OPENPMD
54 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'realspace_grid_openpmd'
55 : LOGICAL, PARAMETER, PRIVATE :: debug_this_module = .FALSE.
56 :
57 : TYPE cp_openpmd_write_buffer_1d
58 : REAL(KIND=dp), DIMENSION(:), POINTER :: buffer => NULL()
59 : END TYPE cp_openpmd_write_buffer_1d
60 : #endif
61 :
62 : CONTAINS
63 :
64 : #ifdef __OPENPMD
65 : ! **************************************************************************************************
66 : !> \brief ...
67 : !> \param particles_z ...
68 : !> \param res_atom_types ...
69 : !> \param res_atom_counts ...
70 : !> \param res_len ...
71 : ! **************************************************************************************************
72 : SUBROUTINE pw_get_atom_types(particles_z, res_atom_types, res_atom_counts, res_len)
73 : INTEGER, DIMENSION(:), INTENT(IN) :: particles_z
74 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: res_atom_types, res_atom_counts
75 : INTEGER, INTENT(OUT) :: res_len
76 :
77 : INTEGER :: current_atom_number, i
78 : INTEGER, ALLOCATABLE, DIMENSION(:) :: particles_z_sorted
79 : LOGICAL :: unique
80 :
81 : ALLOCATE (particles_z_sorted(SIZE(particles_z)))
82 : particles_z_sorted(:) = particles_z(:)
83 : CALL sort_unique(particles_z_sorted, unique)
84 :
85 : ALLOCATE (res_atom_types(MIN(118, SIZE(particles_z))))
86 : ALLOCATE (res_atom_counts(MIN(118, SIZE(particles_z))))
87 : current_atom_number = -1
88 : res_len = 0
89 : DO i = 1, SIZE(particles_z_sorted)
90 : IF (particles_z_sorted(i) /= current_atom_number) THEN
91 : res_len = res_len + 1
92 : current_atom_number = particles_z_sorted(i)
93 : res_atom_types(res_len) = current_atom_number
94 : res_atom_counts(res_len) = 1
95 : ELSE
96 : res_atom_counts(res_len) = res_atom_counts(res_len) + 1
97 : END IF
98 : END DO
99 :
100 : END SUBROUTINE pw_get_atom_types
101 :
102 : ! **************************************************************************************************
103 : !> \brief ...
104 : !> \param particles_z ...
105 : !> \param particles_r ...
106 : !> \param particles_zeff ...
107 : !> \param atom_type ...
108 : !> \param atom_count ...
109 : !> \param openpmd_data ...
110 : !> \param do_write_data ...
111 : ! **************************************************************************************************
112 : SUBROUTINE pw_write_particle_species( &
113 : particles_z, &
114 : particles_r, &
115 : particles_zeff, &
116 : atom_type, &
117 : atom_count, &
118 : openpmd_data, &
119 : do_write_data &
120 : )
121 : INTEGER, DIMENSION(:), INTENT(IN) :: particles_z
122 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: particles_r
123 : REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: particles_zeff
124 : INTEGER, INTENT(IN) :: atom_type, atom_count
125 : TYPE(cp_openpmd_per_call_value_type) :: openpmd_data
126 : LOGICAL :: do_write_data
127 :
128 : CHARACTER(len=1), DIMENSION(3), PARAMETER :: dims = ["x", "y", "z"]
129 :
130 : CHARACTER(len=3) :: atom_type_as_string
131 : CHARACTER(len=default_string_length) :: species_name
132 : INTEGER :: i, j, k
133 : INTEGER, DIMENSION(1) :: global_extent, global_offset, &
134 : local_extent
135 : TYPE(cp_openpmd_write_buffer_1d) :: charge_write_buffer
136 : TYPE(cp_openpmd_write_buffer_1d), DIMENSION(3) :: write_buffers
137 : TYPE(openpmd_attributable_type) :: attr
138 : TYPE(openpmd_dynamic_memory_view_type_1d) :: unresolved_charge_write_buffer
139 : TYPE(openpmd_dynamic_memory_view_type_1d), &
140 : DIMENSION(3) :: unresolved_write_buffers
141 : TYPE(openpmd_particle_species_type) :: species
142 : TYPE(openpmd_record_component_type) :: charge_component, position_component, &
143 : position_offset_component
144 : TYPE(openpmd_record_type) :: charge, position, position_offset
145 :
146 : ! TODO: The charge is probably constant per species?
147 : ! If yes, we could use a constant component and save storage space
148 :
149 : global_extent(1) = atom_count
150 : IF (do_write_data) THEN
151 : global_offset(1) = 0
152 : local_extent(1) = atom_count
153 : ELSE
154 : global_offset(1) = 0
155 : local_extent(1) = 0
156 : END IF
157 :
158 : WRITE (atom_type_as_string, '(I3)') atom_type
159 : species_name = TRIM(openpmd_data%name_prefix)//"-"//ADJUSTL(atom_type_as_string)
160 :
161 : CALL openpmd_data%iteration%open()
162 : species = openpmd_data%iteration%get_particle_species(TRIM(species_name))
163 :
164 : position_offset = species%get_record("positionOffset")
165 : position = species%get_record("position")
166 : ! length
167 : CALL position%set_unit_dimension([1.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp])
168 : DO k = 1, SIZE(dims)
169 : position_offset_component = position_offset%get_component(dims(k))
170 : CALL position_offset_component%make_constant_zero(openpmd_type_int, global_extent)
171 : CALL position_offset_component%set_unit_SI(a_bohr) ! doesnt really matter as it is zero
172 : position_component = position%get_component(dims(k))
173 : CALL position_component%reset_dataset(openpmd_type_double, global_extent)
174 : CALL position_component%set_unit_SI(a_bohr)
175 : unresolved_write_buffers(k) = &
176 : position_component%store_chunk_span_1d_double(global_offset, local_extent)
177 : write_buffers(k)%buffer => unresolved_write_buffers(k)%resolve_double(DEALLOCATE=.FALSE.)
178 : END DO
179 :
180 : IF (PRESENT(particles_zeff)) THEN
181 : charge = species%get_record("charge")
182 : charge_component = charge%as_record_component()
183 : ! charge ~ time x current
184 : CALL charge%set_unit_dimension([0.0_dp, 0.0_dp, 1.0_dp, 1.0_dp, 0.0_dp, 0.0_dp, 0.0_dp])
185 : CALL charge_component%reset_dataset(openpmd_type_double, global_extent)
186 : CALL charge_component%set_unit_SI(e_charge)
187 : unresolved_charge_write_buffer = charge_component%store_chunk_span_1d_double(global_offset, local_extent)
188 : charge_write_buffer%buffer => unresolved_charge_write_buffer%resolve_double(DEALLOCATE=.FALSE.)
189 : END IF
190 :
191 : ! Resolve Spans for a second time to allow for internal reallocations in BP4 engine of ADIOS2
192 : DO k = 1, SIZE(dims)
193 : write_buffers(k)%buffer = unresolved_write_buffers(k)%resolve_double(DEALLOCATE=.TRUE.)
194 : END DO
195 : IF (PRESENT(particles_zeff)) THEN
196 : charge_write_buffer%buffer = unresolved_charge_write_buffer%resolve_double(DEALLOCATE=.TRUE.)
197 : END IF
198 : IF (do_write_data) THEN
199 : j = 1
200 : DO i = 1, SIZE(particles_z)
201 : IF (particles_z(i) == atom_type) THEN
202 : DO k = 1, 3
203 : write_buffers(k)%buffer(j) = particles_r(k, i)
204 : END DO
205 : IF (PRESENT(particles_zeff)) THEN
206 : charge_write_buffer%buffer(j) = particles_zeff(i)
207 : END IF
208 : j = j + 1
209 : END IF
210 : END DO
211 : END IF
212 : attr = openpmd_data%iteration%as_attributable()
213 : CALL attr%series_flush("hdf5.independent_stores = true")
214 : END SUBROUTINE pw_write_particle_species
215 :
216 : ! **************************************************************************************************
217 : !> \brief ...
218 : !> \param particles_z ...
219 : !> \param particles_r ...
220 : !> \param particles_zeff ...
221 : !> \param atom_types ...
222 : !> \param atom_counts ...
223 : !> \param num_atom_types ...
224 : !> \param openpmd_data ...
225 : !> \param gid ...
226 : ! **************************************************************************************************
227 : SUBROUTINE pw_write_particles( &
228 : particles_z, &
229 : particles_r, &
230 : particles_zeff, &
231 : atom_types, &
232 : atom_counts, &
233 : num_atom_types, &
234 : openpmd_data, &
235 : gid &
236 : )
237 : INTEGER, DIMENSION(:), INTENT(IN) :: particles_z
238 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: particles_r
239 : REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: particles_zeff
240 : INTEGER, DIMENSION(:), INTENT(IN) :: atom_types, atom_counts
241 : INTEGER, INTENT(IN), TARGET :: num_atom_types
242 : TYPE(cp_openpmd_per_call_value_type) :: openpmd_data
243 : TYPE(mp_comm_type), OPTIONAL :: gid
244 :
245 : INTEGER :: i, mpi_rank
246 : LOGICAL :: do_write_data
247 :
248 : IF (PRESENT(gid)) THEN
249 : CALL gid%get_rank(mpi_rank)
250 : do_write_data = mpi_rank == 0
251 : ELSE
252 : do_write_data = .TRUE.
253 : END IF
254 : DO i = 1, num_atom_types
255 : CALL pw_write_particle_species( &
256 : particles_z, &
257 : particles_r, &
258 : particles_zeff, &
259 : atom_types(i), &
260 : atom_counts(i), &
261 : openpmd_data, &
262 : do_write_data &
263 : )
264 : END DO
265 : END SUBROUTINE pw_write_particles
266 :
267 : ! **************************************************************************************************
268 : !> \brief ...
269 : !> \param pw ...
270 : !> \param unit_nr ...
271 : !> \param title ...
272 : !> \param particles_r ...
273 : !> \param particles_z ...
274 : !> \param particles_zeff ...
275 : !> \param stride ...
276 : !> \param zero_tails ...
277 : !> \param silent ...
278 : !> \param mpi_io ...
279 : ! **************************************************************************************************
280 : SUBROUTINE pw_to_openpmd( &
281 : pw, &
282 : unit_nr, &
283 : title, &
284 : particles_r, &
285 : particles_z, &
286 : particles_zeff, &
287 : stride, &
288 : zero_tails, &
289 : silent, &
290 : mpi_io &
291 : )
292 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
293 : INTEGER :: unit_nr
294 : CHARACTER(*), INTENT(IN), OPTIONAL :: title
295 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
296 : OPTIONAL :: particles_r
297 : INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: particles_z
298 : REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: particles_zeff
299 : INTEGER, DIMENSION(:), OPTIONAL, POINTER :: stride
300 : LOGICAL, INTENT(IN), OPTIONAL :: zero_tails, silent, mpi_io
301 :
302 : CHARACTER(len=*), PARAMETER :: routineN = 'pw_to_openpmd'
303 :
304 : CHARACTER(LEN=default_string_length) :: my_title
305 : INTEGER :: count1, count2, count3, handle, i, I1, &
306 : I2, I3, iat, L1, L2, L3, my_rank, &
307 : my_stride(3), np, num_atom_types, &
308 : num_pe, U1, U2, U3
309 : INTEGER, ALLOCATABLE, DIMENSION(:) :: atom_counts, atom_types
310 : INTEGER, DIMENSION(3) :: global_extent, local_extent, offset
311 : LOGICAL :: be_silent, my_zero_tails, parallel_write
312 : REAL(KIND=dp), DIMENSION(3) :: grid_spacing
313 : REAL(KIND=dp), POINTER :: write_buffer(:, :, :)
314 : TYPE(cp_openpmd_per_call_value_type) :: openpmd_data
315 : TYPE(mp_comm_type) :: gid
316 : TYPE(openpmd_attributable_type) :: attr
317 : TYPE(openpmd_dynamic_memory_view_type_3d) :: unresolved_write_buffer
318 : TYPE(openpmd_mesh_type) :: mesh
319 : TYPE(openpmd_record_component_type) :: scalar_mesh
320 :
321 : CALL timeset(routineN, handle)
322 :
323 : my_zero_tails = .FALSE.
324 : be_silent = .FALSE.
325 : parallel_write = .FALSE.
326 : gid = pw%pw_grid%para%group
327 : IF (PRESENT(title)) my_title = TRIM(title)
328 : IF (PRESENT(zero_tails)) my_zero_tails = zero_tails
329 : IF (PRESENT(silent)) be_silent = silent
330 : IF (PRESENT(mpi_io)) parallel_write = mpi_io
331 : my_stride = 1
332 : IF (PRESENT(stride)) THEN
333 : IF (SIZE(stride) /= 1 .AND. SIZE(stride) /= 3) THEN
334 : CALL cp_abort(__LOCATION__, "STRIDE keyword can accept only 1 "// &
335 : "(the same for X,Y,Z) or 3 values. Correct your input file.")
336 : END IF
337 : IF (SIZE(stride) == 1) THEN
338 : DO i = 1, 3
339 : my_stride(i) = stride(1)
340 : END DO
341 : ELSE
342 : my_stride = stride(1:3)
343 : END IF
344 : CPASSERT(my_stride(1) > 0)
345 : CPASSERT(my_stride(2) > 0)
346 : CPASSERT(my_stride(3) > 0)
347 : END IF
348 :
349 : openpmd_data = cp_openpmd_get_value_unit_nr(unit_nr)
350 :
351 : CPASSERT(PRESENT(particles_z) .EQV. PRESENT(particles_r))
352 : np = 0
353 : IF (PRESENT(particles_z)) THEN
354 : CALL pw_get_atom_types(particles_z, atom_types, atom_counts, num_atom_types)
355 : CPASSERT(SIZE(particles_z) == SIZE(particles_r, dim=2))
356 : np = SIZE(particles_z)
357 : END IF
358 :
359 : DO i = 1, 3
360 : ! Notes:
361 : ! 1. This loses information on the rotation of the mesh, the mesh is stored
362 : ! without reference to a global coordinate system
363 : ! 2. This assumes that the coordinate system is not sheared
364 : grid_spacing(i) = SQRT(SUM(pw%pw_grid%dh(:, i)**2))*REAL(my_stride(i), dp)
365 : END DO
366 :
367 : IF (PRESENT(particles_z)) THEN
368 : IF (parallel_write) THEN
369 : CALL pw_write_particles( &
370 : particles_z, &
371 : particles_r, &
372 : particles_zeff, &
373 : atom_types, &
374 : atom_counts, &
375 : num_atom_types, &
376 : openpmd_data, &
377 : gid &
378 : )
379 : ELSE
380 : CALL pw_write_particles( &
381 : particles_z, &
382 : particles_r, &
383 : particles_zeff, &
384 : atom_types, &
385 : atom_counts, &
386 : num_atom_types, &
387 : openpmd_data &
388 : )
389 : END IF
390 : END IF
391 :
392 : DO iat = 1, 3
393 : global_extent(iat) = (pw%pw_grid%npts(iat) + my_stride(iat) - 1)/my_stride(iat)
394 : ! '- 1' for upper gaussian bracket
395 : offset(iat) = ((pw%pw_grid%bounds_local(1, iat) - pw%pw_grid%bounds(1, iat) + my_stride(iat) - 1)/my_stride(iat))
396 : ! '+ 1' because upper end is inclusive, '- 1' for upper gaussian bracket
397 : ! refer local_extent to the global offset first in order to have consistent rounding
398 : local_extent(iat) = ((pw%pw_grid%bounds_local(2, iat) + 1 - pw%pw_grid%bounds(1, iat) + my_stride(iat) - 1)/my_stride(iat))
399 : END DO
400 : local_extent = local_extent - offset
401 :
402 : mesh = openpmd_data%iteration%get_mesh(TRIM(openpmd_data%name_prefix))
403 : CALL mesh%set_axis_labels(["x", "y", "z"])
404 : CALL mesh%set_position([0.5_dp, 0.5_dp, 0.5_dp])
405 : CALL mesh%set_grid_global_offset([ &
406 : pw%pw_grid%bounds(1, 1)*grid_spacing(1), &
407 : pw%pw_grid%bounds(1, 2)*grid_spacing(2), &
408 : pw%pw_grid%bounds(1, 3)*grid_spacing(3)])
409 : CALL mesh%set_grid_spacing(grid_spacing)
410 : CALL mesh%set_grid_unit_SI(a_bohr)
411 : CALL mesh%set_unit_dimension(openpmd_data%unit_dimension)
412 : scalar_mesh = mesh%as_record_component()
413 : CALL scalar_mesh%set_unit_SI(openpmd_data%unit_si)
414 : CALL scalar_mesh%reset_dataset(openpmd_type_double, global_extent)
415 :
416 : ! shortcut
417 : ! need to adjust L1/U1 for uneven distributions across MPI ranks
418 : ! (when working with a stride, we might have to skip the first n values)
419 : ! so keep this consistent with the offset and local_extent computed above
420 : ! L1 = pw%pw_grid%bounds_local(1, 1)
421 : L1 = pw%pw_grid%bounds(1, 1) + offset(1)*my_stride(1)
422 : L2 = pw%pw_grid%bounds_local(1, 2)
423 : L3 = pw%pw_grid%bounds_local(1, 3)
424 : ! offset + local_extent is the start index for the next rank already
425 : ! since the indexes are inclusive, subtract 1 from the boundary index
426 : U1 = pw%pw_grid%bounds(1, 1) + (offset(1) + local_extent(1) - 1)*my_stride(1)
427 : U2 = pw%pw_grid%bounds_local(2, 2)
428 : U3 = pw%pw_grid%bounds_local(2, 3)
429 :
430 : my_rank = pw%pw_grid%para%group%mepos
431 : num_pe = pw%pw_grid%para%group%num_pe
432 :
433 : IF (ALL(my_stride == 1)) THEN
434 : CALL scalar_mesh%store_chunk(pw%array(L1:U1, L2:U2, L3:U3), offset)
435 : ! Are there some conditions under which we can skip this flush?
436 : attr = openpmd_data%iteration%as_attributable()
437 : CALL attr%series_flush("hdf5.independent_stores = false")
438 : ELSE
439 : count3 = 0
440 : DO I3 = L3, U3, my_stride(3)
441 : ! maybe add an overload to provide `buf` here for HDF5, might have better performance
442 : ! for intermittent flushing
443 : ! or just call the buffer in the outer function if memory is no problem...
444 : unresolved_write_buffer = scalar_mesh%store_chunk_span_3d_double( &
445 : [offset(1), offset(2), offset(3) + count3], &
446 : [local_extent(1), local_extent(2), 1])
447 : write_buffer => unresolved_write_buffer%resolve_double(DEALLOCATE=.TRUE.)
448 :
449 : ! Sanity checks: ensure buffer is associated and matches expected shape
450 : CPASSERT(ASSOCIATED(write_buffer))
451 : CPASSERT(SIZE(write_buffer, 1) == local_extent(1))
452 : CPASSERT(SIZE(write_buffer, 2) == local_extent(2))
453 : CPASSERT(SIZE(write_buffer, 3) == 1)
454 :
455 : count2 = 0
456 : DO I2 = L2, U2, my_stride(2)
457 : ! This loop deals with ray (:, count2, count3) of the local subspace
458 : ! The write buffer itself has been allocated for slice (:, :, count3)
459 : count1 = 0
460 : DO I1 = L1, U1, my_stride(1)
461 : write_buffer(count1 + 1, count2 + 1, 1) = pw%array(I1, I2, I3)
462 : ! Debug: print the target indices in write_buffer to the command line
463 : ! WRITE(*,*) 'write_buffer index:', count1 + 1, ',', count2 + 1, ',', 1
464 : count1 = count1 + 1
465 : END DO
466 : count2 = count2 + 1
467 : END DO
468 : count3 = count3 + 1
469 : END DO
470 : END IF
471 :
472 : CALL timestop(handle)
473 :
474 : END SUBROUTINE pw_to_openpmd
475 :
476 : #else
477 :
478 : ! **************************************************************************************************
479 : !> \brief ...
480 : !> \param pw ...
481 : !> \param unit_nr ...
482 : !> \param title ...
483 : !> \param particles_r ...
484 : !> \param particles_z ...
485 : !> \param particles_zeff ...
486 : !> \param stride ...
487 : !> \param zero_tails ...
488 : !> \param silent ...
489 : !> \param mpi_io ...
490 : ! **************************************************************************************************
491 0 : SUBROUTINE pw_to_openpmd( &
492 : pw, &
493 : unit_nr, &
494 : title, &
495 0 : particles_r, &
496 0 : particles_z, &
497 0 : particles_zeff, &
498 : stride, &
499 : zero_tails, &
500 : silent, &
501 : mpi_io &
502 : )
503 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw
504 : INTEGER :: unit_nr
505 : CHARACTER(*), INTENT(IN), OPTIONAL :: title
506 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
507 : OPTIONAL :: particles_r
508 : INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: particles_z
509 : REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: particles_zeff
510 : INTEGER, DIMENSION(:), OPTIONAL, POINTER :: stride
511 : LOGICAL, INTENT(IN), OPTIONAL :: zero_tails, silent, mpi_io
512 :
513 : MARK_USED(pw)
514 : MARK_USED(unit_nr)
515 : MARK_USED(title)
516 : MARK_USED(particles_r)
517 : MARK_USED(particles_z)
518 : MARK_USED(particles_zeff)
519 : MARK_USED(stride)
520 : MARK_USED(zero_tails)
521 : MARK_USED(silent)
522 : MARK_USED(mpi_io)
523 0 : CPABORT("CP2K compiled without the openPMD-api")
524 :
525 0 : END SUBROUTINE pw_to_openpmd
526 :
527 : #endif
528 :
529 : END MODULE realspace_grid_openpmd
|