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 Define methods related to particle_type
10 : !> \par History
11 : !> 10.2014 Move routines out of particle_types.F [Ole Schuett]
12 : !> \author Ole Schuett
13 : ! **************************************************************************************************
14 : MODULE particle_methods
15 : USE atomic_kind_types, ONLY: get_atomic_kind
16 : USE basis_set_types, ONLY: get_gto_basis_set,&
17 : gto_basis_set_p_type
18 : USE cell_methods, ONLY: cell_create,&
19 : set_cell_param
20 : USE cell_types, ONLY: cell_clone,&
21 : cell_release,&
22 : cell_type,&
23 : get_cell,&
24 : pbc,&
25 : real_to_scaled
26 : USE cp2k_info, ONLY: compile_revision,&
27 : cp2k_version,&
28 : r_cwd,&
29 : r_host_name,&
30 : r_user_name
31 : USE cp_log_handling, ONLY: cp_get_default_logger,&
32 : cp_logger_get_default_io_unit,&
33 : cp_logger_type,&
34 : cp_to_string
35 : USE cp_output_handling, ONLY: cp_print_key_finished_output,&
36 : cp_print_key_generate_filename,&
37 : cp_print_key_unit_nr
38 : USE cp_units, ONLY: cp_unit_from_cp2k
39 : USE external_potential_types, ONLY: fist_potential_type,&
40 : get_potential
41 : USE input_constants, ONLY: dump_atomic,&
42 : dump_dcd,&
43 : dump_dcd_aligned_cell,&
44 : dump_extxyz,&
45 : dump_pdb,&
46 : dump_xmol
47 : USE input_cp2k_subsys, ONLY: create_cell_section
48 : USE input_enumeration_types, ONLY: enum_i2c,&
49 : enumeration_type
50 : USE input_keyword_types, ONLY: keyword_get,&
51 : keyword_type
52 : USE input_section_types, ONLY: section_get_keyword,&
53 : section_release,&
54 : section_type,&
55 : section_vals_get_subs_vals,&
56 : section_vals_type,&
57 : section_vals_val_get
58 : USE kinds, ONLY: default_path_length,&
59 : default_string_length,&
60 : dp,&
61 : sp
62 : USE machine, ONLY: m_timestamp,&
63 : timestamp_length
64 : USE mathconstants, ONLY: degree
65 : USE mathlib, ONLY: angle,&
66 : dihedral_angle,&
67 : gcd
68 : USE memory_utilities, ONLY: reallocate
69 : USE particle_types, ONLY: get_particle_pos_or_vel,&
70 : particle_type
71 : USE periodic_table, ONLY: nelem
72 : USE physcon, ONLY: massunit
73 : USE qmmm_ff_fist, ONLY: qmmm_ff_precond_only_qm
74 : USE qs_kind_types, ONLY: get_qs_kind,&
75 : qs_kind_type
76 : USE shell_potential_types, ONLY: get_shell,&
77 : shell_kind_type
78 : USE string_utilities, ONLY: uppercase
79 : USE util, ONLY: sort,&
80 : sort_unique
81 : #include "./base/base_uses.f90"
82 :
83 : IMPLICIT NONE
84 :
85 : PRIVATE
86 :
87 : ! Public subroutines
88 :
89 : PUBLIC :: write_fist_particle_coordinates, &
90 : write_qs_particle_coordinates, &
91 : write_particle_distances, &
92 : write_particle_coordinates, &
93 : write_structure_data, &
94 : get_particle_set, &
95 : write_particle_matrix, &
96 : write_final_cif
97 :
98 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'particle_methods'
99 :
100 : CONTAINS
101 :
102 : ! **************************************************************************************************
103 : !> \brief Get the components of a particle set.
104 : !> \param particle_set ...
105 : !> \param qs_kind_set ...
106 : !> \param first_sgf ...
107 : !> \param last_sgf ...
108 : !> \param nsgf ...
109 : !> \param nmao ...
110 : !> \param basis ...
111 : !> \date 14.01.2002
112 : !> \par History
113 : !> - particle type cleaned (13.10.2003,MK)
114 : !> - refactoring and add basis set option (17.08.2010,jhu)
115 : !> \author MK
116 : !> \version 1.0
117 : ! **************************************************************************************************
118 185006 : SUBROUTINE get_particle_set(particle_set, qs_kind_set, first_sgf, last_sgf, nsgf, &
119 185006 : nmao, basis)
120 :
121 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
122 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
123 : INTEGER, DIMENSION(:), INTENT(INOUT), OPTIONAL :: first_sgf, last_sgf, nsgf, nmao
124 : TYPE(gto_basis_set_p_type), DIMENSION(:), OPTIONAL :: basis
125 :
126 : INTEGER :: ikind, iparticle, isgf, nparticle, ns
127 :
128 185006 : CPASSERT(ASSOCIATED(particle_set))
129 :
130 185006 : nparticle = SIZE(particle_set)
131 185006 : IF (PRESENT(first_sgf)) THEN
132 38868 : CPASSERT(SIZE(first_sgf) >= nparticle)
133 : END IF
134 185006 : IF (PRESENT(last_sgf)) THEN
135 31651 : CPASSERT(SIZE(last_sgf) >= nparticle)
136 : END IF
137 185006 : IF (PRESENT(nsgf)) THEN
138 145682 : CPASSERT(SIZE(nsgf) >= nparticle)
139 : END IF
140 185006 : IF (PRESENT(nmao)) THEN
141 14 : CPASSERT(SIZE(nmao) >= nparticle)
142 : END IF
143 :
144 185006 : IF (PRESENT(first_sgf) .OR. PRESENT(last_sgf) .OR. PRESENT(nsgf)) THEN
145 : isgf = 0
146 1053775 : DO iparticle = 1, nparticle
147 869265 : CALL get_atomic_kind(particle_set(iparticle)%atomic_kind, kind_number=ikind)
148 869265 : IF (PRESENT(basis)) THEN
149 651519 : IF (ASSOCIATED(basis(ikind)%gto_basis_set)) THEN
150 651515 : CALL get_gto_basis_set(gto_basis_set=basis(ikind)%gto_basis_set, nsgf=ns)
151 : ELSE
152 4 : ns = 0
153 : END IF
154 : ELSE
155 217746 : CALL get_qs_kind(qs_kind_set(ikind), nsgf=ns)
156 : END IF
157 869265 : IF (PRESENT(nsgf)) nsgf(iparticle) = ns
158 869265 : IF (PRESENT(first_sgf)) first_sgf(iparticle) = isgf + 1
159 869265 : isgf = isgf + ns
160 1923536 : IF (PRESENT(last_sgf)) last_sgf(iparticle) = isgf
161 : END DO
162 : END IF
163 :
164 185006 : IF (PRESENT(first_sgf)) THEN
165 38868 : IF (SIZE(first_sgf) > nparticle) first_sgf(nparticle + 1) = isgf + 1
166 : END IF
167 :
168 185006 : IF (PRESENT(nmao)) THEN
169 86 : DO iparticle = 1, nparticle
170 72 : CALL get_atomic_kind(particle_set(iparticle)%atomic_kind, kind_number=ikind)
171 72 : CALL get_qs_kind(qs_kind_set(ikind), mao=ns)
172 86 : nmao(iparticle) = ns
173 : END DO
174 : END IF
175 :
176 185006 : END SUBROUTINE get_particle_set
177 :
178 : ! **************************************************************************************************
179 : !> \brief Should be able to write a few formats e.g. xmol, and some binary
180 : !> format (dcd) some format can be used for x,v,f
181 : !>
182 : !> FORMAT CONTENT UNITS x, v, f
183 : !> XMOL POS, VEL, FORCE, POS_VEL, POS_VEL_FORCE Angstrom, a.u., a.u.
184 : !>
185 : !> \param particle_set ...
186 : !> \param iunit ...
187 : !> \param output_format ...
188 : !> \param content ...
189 : !> \param title ...
190 : !> \param cell ...
191 : !> \param array ...
192 : !> \param unit_conv ...
193 : !> \param charge_occup ...
194 : !> \param charge_beta ...
195 : !> \param charge_extended ...
196 : !> \param print_kind ...
197 : !> \date 14.01.2002
198 : !> \author MK
199 : !> \version 1.0
200 : ! **************************************************************************************************
201 26826 : SUBROUTINE write_particle_coordinates(particle_set, iunit, output_format, &
202 26826 : content, title, cell, array, unit_conv, &
203 : charge_occup, charge_beta, &
204 : charge_extended, print_kind)
205 :
206 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
207 : INTEGER :: iunit, output_format
208 : CHARACTER(LEN=*) :: content, title
209 : TYPE(cell_type), OPTIONAL, POINTER :: cell
210 : REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: array
211 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: unit_conv
212 : LOGICAL, INTENT(IN), OPTIONAL :: charge_occup, charge_beta, &
213 : charge_extended, print_kind
214 :
215 : CHARACTER(len=*), PARAMETER :: routineN = 'write_particle_coordinates'
216 :
217 : CHARACTER(LEN=120) :: line
218 : CHARACTER(LEN=2) :: element_symbol
219 : CHARACTER(LEN=4) :: name
220 : CHARACTER(LEN=default_string_length) :: atm_name, my_format
221 : INTEGER :: handle, iatom, natom
222 : LOGICAL :: dummy, my_charge_beta, &
223 : my_charge_extended, my_charge_occup, &
224 : my_print_kind
225 : REAL(KIND=dp) :: angle_alpha, angle_beta, angle_gamma, &
226 : factor, qeff
227 26826 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: arr
228 : REAL(KIND=dp), DIMENSION(3) :: abc, angles, f, r, v
229 : REAL(KIND=dp), DIMENSION(3, 3) :: h
230 26826 : REAL(KIND=sp), ALLOCATABLE, DIMENSION(:) :: x4, y4, z4
231 : TYPE(cell_type), POINTER :: cell_dcd
232 : TYPE(fist_potential_type), POINTER :: fist_potential
233 : TYPE(shell_kind_type), POINTER :: shell
234 :
235 26826 : CALL timeset(routineN, handle)
236 :
237 26826 : natom = SIZE(particle_set)
238 26826 : IF (PRESENT(array)) THEN
239 1741 : SELECT CASE (TRIM(content))
240 : CASE ("POS_VEL", "POS_VEL_FORCE")
241 1741 : CPABORT("Illegal usage")
242 : END SELECT
243 : END IF
244 26826 : factor = 1.0_dp
245 26826 : IF (PRESENT(unit_conv)) THEN
246 26675 : factor = unit_conv
247 : END IF
248 53579 : SELECT CASE (output_format)
249 : CASE (dump_xmol, dump_extxyz)
250 26753 : my_print_kind = .FALSE.
251 26753 : IF (PRESENT(print_kind)) my_print_kind = print_kind
252 26753 : WRITE (iunit, "(I8)") natom
253 26753 : WRITE (iunit, "(A)") TRIM(title)
254 1369220 : DO iatom = 1, natom
255 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
256 1342467 : element_symbol=element_symbol)
257 1342467 : IF (LEN_TRIM(element_symbol) == 0 .OR. my_print_kind) THEN
258 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
259 24 : name=atm_name)
260 24 : dummy = qmmm_ff_precond_only_qm(id1=atm_name)
261 24 : my_format = "(A,"
262 24 : atm_name = TRIM(atm_name)
263 : ELSE
264 1342443 : my_format = "(T2,A2,"
265 1342443 : atm_name = TRIM(element_symbol)
266 : END IF
267 26753 : SELECT CASE (TRIM(content))
268 : CASE ("POS")
269 1227434 : IF (PRESENT(array)) THEN
270 47741 : r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
271 : ELSE
272 4718772 : r(:) = particle_set(iatom)%r(:)
273 : END IF
274 4909736 : WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), r(1:3)*factor
275 : CASE ("VEL")
276 85469 : IF (PRESENT(array)) THEN
277 0 : v(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
278 : ELSE
279 341876 : v(:) = particle_set(iatom)%v(:)
280 : END IF
281 341876 : WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), v(1:3)*factor
282 : CASE ("FORCE")
283 20955 : IF (PRESENT(array)) THEN
284 0 : f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
285 : ELSE
286 83820 : f(:) = particle_set(iatom)%f(:)
287 : END IF
288 83820 : WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), f(1:3)*factor
289 : CASE ("FORCE_MIXING_LABELS")
290 8609 : IF (PRESENT(array)) THEN
291 34436 : f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
292 : ELSE
293 0 : f(:) = particle_set(iatom)%f(:)
294 : END IF
295 1376903 : WRITE (iunit, TRIM(my_format)//"1X,3F20.10)") TRIM(atm_name), f(1:3)*factor
296 : END SELECT
297 : END DO
298 : CASE (dump_atomic)
299 170 : DO iatom = 1, natom
300 10 : SELECT CASE (TRIM(content))
301 : CASE ("POS")
302 160 : IF (PRESENT(array)) THEN
303 0 : r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
304 : ELSE
305 640 : r(:) = particle_set(iatom)%r(:)
306 : END IF
307 640 : WRITE (iunit, "(3F20.10)") r(1:3)*factor
308 : CASE ("VEL")
309 0 : IF (PRESENT(array)) THEN
310 0 : v(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
311 : ELSE
312 0 : v(:) = particle_set(iatom)%v(:)
313 : END IF
314 0 : WRITE (iunit, "(3F20.10)") v(1:3)*factor
315 : CASE ("FORCE")
316 0 : IF (PRESENT(array)) THEN
317 0 : f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
318 : ELSE
319 0 : f(:) = particle_set(iatom)%f(:)
320 : END IF
321 0 : WRITE (iunit, "(3F20.10)") f(1:3)*factor
322 : CASE ("FORCE_MIXING_LABELS")
323 0 : IF (PRESENT(array)) THEN
324 0 : f(:) = array((iatom - 1)*3 + 1:(iatom - 1)*3 + 3)
325 : ELSE
326 0 : f(:) = particle_set(iatom)%f(:)
327 : END IF
328 160 : WRITE (iunit, "(3F20.10)") f(1:3)*factor
329 : END SELECT
330 : END DO
331 : CASE (dump_dcd, dump_dcd_aligned_cell)
332 4 : IF (.NOT. (PRESENT(cell))) THEN
333 0 : CPABORT("Cell is not present! Report this bug!")
334 : END IF
335 : CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, &
336 4 : abc=abc)
337 4 : IF (.NOT. cell%orthorhombic .AND. (output_format == dump_dcd_aligned_cell)) THEN
338 : ! In the case of a non-orthorhombic cell adopt a common convention
339 : ! for the orientation of the cell with respect to the Cartesian axes:
340 : ! Cell vector a is aligned with the x axis and the cell vector b lies
341 : ! in the xy plane.
342 0 : NULLIFY (cell_dcd)
343 0 : CALL cell_create(cell_dcd)
344 0 : CALL cell_clone(cell, cell_dcd, tag="CELL_DCD")
345 0 : angles(1) = angle_alpha/degree
346 0 : angles(2) = angle_beta/degree
347 0 : angles(3) = angle_gamma/degree
348 : CALL set_cell_param(cell_dcd, abc, angles, &
349 0 : do_init_cell=.TRUE.)
350 0 : h(1:3, 1:3) = MATMUL(cell_dcd%hmat(1:3, 1:3), cell%h_inv(1:3, 1:3))
351 0 : CALL cell_release(cell_dcd)
352 : END IF
353 12 : ALLOCATE (arr(3, natom))
354 4 : IF (PRESENT(array)) THEN
355 0 : arr(1:3, 1:natom) = RESHAPE(array, [3, natom])
356 : ELSE
357 8 : SELECT CASE (TRIM(content))
358 : CASE ("POS")
359 1156 : DO iatom = 1, natom
360 4612 : arr(1:3, iatom) = particle_set(iatom)%r(1:3)
361 : END DO
362 : CASE ("VEL")
363 0 : DO iatom = 1, natom
364 0 : arr(1:3, iatom) = particle_set(iatom)%v(1:3)
365 : END DO
366 : CASE ("FORCE")
367 0 : DO iatom = 1, natom
368 0 : arr(1:3, iatom) = particle_set(iatom)%f(1:3)
369 : END DO
370 : CASE DEFAULT
371 4 : CPABORT("Illegal DCD dump type")
372 : END SELECT
373 : END IF
374 12 : ALLOCATE (x4(natom))
375 8 : ALLOCATE (y4(natom))
376 8 : ALLOCATE (z4(natom))
377 4 : IF (.NOT. cell%orthorhombic .AND. (output_format == dump_dcd_aligned_cell)) THEN
378 0 : x4(1:natom) = REAL(MATMUL(h(1, 1:3), arr(1:3, 1:natom)), KIND=sp)
379 0 : y4(1:natom) = REAL(MATMUL(h(2, 1:3), arr(1:3, 1:natom)), KIND=sp)
380 0 : z4(1:natom) = REAL(MATMUL(h(3, 1:3), arr(1:3, 1:natom)), KIND=sp)
381 : ELSE
382 1156 : x4(1:natom) = REAL(arr(1, 1:natom), KIND=sp)
383 1156 : y4(1:natom) = REAL(arr(2, 1:natom), KIND=sp)
384 1156 : z4(1:natom) = REAL(arr(3, 1:natom), KIND=sp)
385 : END IF
386 4 : WRITE (iunit) abc(1)*factor, angle_gamma, abc(2)*factor, &
387 8 : angle_beta, angle_alpha, abc(3)*factor
388 1156 : WRITE (iunit) x4*REAL(factor, KIND=sp)
389 1156 : WRITE (iunit) y4*REAL(factor, KIND=sp)
390 1156 : WRITE (iunit) z4*REAL(factor, KIND=sp)
391 : ! Release work storage
392 4 : DEALLOCATE (arr)
393 4 : DEALLOCATE (x4)
394 4 : DEALLOCATE (y4)
395 8 : DEALLOCATE (z4)
396 : CASE (dump_pdb)
397 59 : my_charge_occup = .FALSE.
398 59 : IF (PRESENT(charge_occup)) my_charge_occup = charge_occup
399 59 : my_charge_beta = .FALSE.
400 59 : IF (PRESENT(charge_beta)) my_charge_beta = charge_beta
401 59 : my_charge_extended = .FALSE.
402 59 : IF (PRESENT(charge_extended)) my_charge_extended = charge_extended
403 59 : IF (LEN_TRIM(title) > 0) THEN
404 : WRITE (UNIT=iunit, FMT="(A6,T11,A)") &
405 59 : "REMARK", TRIM(title)
406 : END IF
407 59 : CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, abc=abc)
408 : ! COLUMNS DATA TYPE CONTENTS
409 : ! --------------------------------------------------
410 : ! 1 - 6 Record name "CRYST1"
411 : ! 7 - 15 Real(9.3) a (Angstroms)
412 : ! 16 - 24 Real(9.3) b (Angstroms)
413 : ! 25 - 33 Real(9.3) c (Angstroms)
414 : ! 34 - 40 Real(7.2) alpha (degrees)
415 : ! 41 - 47 Real(7.2) beta (degrees)
416 : ! 48 - 54 Real(7.2) gamma (degrees)
417 : ! 56 - 66 LString Space group
418 : ! 67 - 70 Integer Z value
419 : WRITE (UNIT=iunit, FMT="(A6,3F9.3,3F7.2)") &
420 236 : "CRYST1", abc(1:3)*factor, angle_alpha, angle_beta, angle_gamma
421 59 : WRITE (UNIT=line(1:6), FMT="(A6)") "ATOM "
422 2999 : DO iatom = 1, natom
423 2940 : line = ""
424 : ! COLUMNS DATA TYPE CONTENTS
425 : ! 1 - 6 Record name "ATOM "
426 : ! 7 - 11 Integer Atom serial number
427 : ! 13 - 16 Atom Atom name
428 : ! 17 Character Alternate location indicator
429 : ! 18 - 20 Residue name Residue name
430 : ! 22 Character Chain identifier
431 : ! 23 - 26 Integer Residue sequence number
432 : ! 27 AChar Code for insertion of residues
433 : ! 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstrom
434 : ! 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstrom
435 : ! 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstrom
436 : ! 55 - 60 Real(6.2) Occupancy
437 : ! 61 - 66 Real(6.2) Temperature factor (Default = 0.0)
438 : ! 73 - 76 LString(4) Segment identifier, left-justified
439 : ! 77 - 78 LString(2) Element symbol, right-justified
440 : ! 79 - 80 LString(2) Charge on the atom
441 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
442 : element_symbol=element_symbol, name=atm_name, &
443 2940 : fist_potential=fist_potential, shell=shell)
444 2940 : IF (LEN_TRIM(element_symbol) == 0) THEN
445 0 : dummy = qmmm_ff_precond_only_qm(id1=atm_name)
446 : END IF
447 2940 : name = TRIM(atm_name)
448 2940 : IF (ASSOCIATED(fist_potential)) THEN
449 2940 : CALL get_potential(potential=fist_potential, qeff=qeff)
450 : ELSE
451 0 : qeff = 0.0_dp
452 : END IF
453 2940 : IF (ASSOCIATED(shell)) CALL get_shell(shell=shell, charge=qeff)
454 2940 : WRITE (UNIT=line(1:6), FMT="(A6)") "ATOM "
455 2940 : WRITE (UNIT=line(7:11), FMT="(I5)") MODULO(iatom, 100000)
456 2940 : WRITE (UNIT=line(13:16), FMT="(A4)") ADJUSTL(name)
457 : ! WRITE (UNIT=line(18:20),FMT="(A3)") TRIM(resname)
458 : ! WRITE (UNIT=line(23:26),FMT="(I4)") MODULO(idres,10000)
459 5880 : SELECT CASE (TRIM(content))
460 : CASE ("POS")
461 2940 : IF (PRESENT(array)) THEN
462 0 : r(1:3) = get_particle_pos_or_vel(iatom, particle_set, array)
463 : ELSE
464 11760 : r(:) = particle_set(iatom)%r(:)
465 : END IF
466 11760 : WRITE (UNIT=line(31:54), FMT="(3F8.3)") r(1:3)*factor
467 : CASE DEFAULT
468 2940 : CPABORT("PDB dump only for trajectory available")
469 : END SELECT
470 2940 : IF (my_charge_occup) THEN
471 2130 : WRITE (UNIT=line(55:60), FMT="(F6.2)") qeff
472 : ELSE
473 810 : WRITE (UNIT=line(55:60), FMT="(F6.2)") 0.0_dp
474 : END IF
475 2940 : IF (my_charge_beta) THEN
476 480 : WRITE (UNIT=line(61:66), FMT="(F6.2)") qeff
477 : ELSE
478 2460 : WRITE (UNIT=line(61:66), FMT="(F6.2)") 0.0_dp
479 : END IF
480 : ! WRITE (UNIT=line(73:76),FMT="(A4)") ADJUSTL(TRIM(molname))
481 2940 : WRITE (UNIT=line(77:78), FMT="(A2)") ADJUSTR(TRIM(element_symbol))
482 2940 : IF (my_charge_extended) THEN
483 330 : WRITE (UNIT=line(81:), FMT="(SP,F0.8)") qeff
484 : END IF
485 2999 : WRITE (UNIT=iunit, FMT="(A)") TRIM(line)
486 : END DO
487 59 : WRITE (UNIT=iunit, FMT="(A)") "END"
488 : CASE DEFAULT
489 26885 : CPABORT("Illegal dump type")
490 : END SELECT
491 :
492 26826 : CALL timestop(handle)
493 :
494 26826 : END SUBROUTINE write_particle_coordinates
495 :
496 : ! **************************************************************************************************
497 : !> \brief Write the atomic coordinates to the output unit.
498 : !> \param particle_set ...
499 : !> \param subsys_section ...
500 : !> \param charges ...
501 : !> \date 05.06.2000
502 : !> \author MK
503 : !> \version 1.0
504 : ! **************************************************************************************************
505 9701 : SUBROUTINE write_fist_particle_coordinates(particle_set, subsys_section, charges)
506 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
507 : TYPE(section_vals_type), POINTER :: subsys_section
508 : REAL(KIND=dp), DIMENSION(:), OPTIONAL :: charges
509 :
510 : CHARACTER(LEN=default_string_length) :: name, unit_str
511 : INTEGER :: iatom, ikind, iw, natom
512 : REAL(KIND=dp) :: conv, mass, qcore, qeff, qshell
513 : TYPE(cp_logger_type), POINTER :: logger
514 : TYPE(shell_kind_type), POINTER :: shell_kind
515 :
516 9701 : NULLIFY (logger)
517 9701 : NULLIFY (shell_kind)
518 :
519 9701 : logger => cp_get_default_logger()
520 : iw = cp_print_key_unit_nr(logger, subsys_section, &
521 9701 : "PRINT%ATOMIC_COORDINATES", extension=".coordLog")
522 :
523 9701 : CALL section_vals_val_get(subsys_section, "PRINT%ATOMIC_COORDINATES%UNIT", c_val=unit_str)
524 9701 : conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
525 9701 : CALL uppercase(unit_str)
526 9701 : IF (iw > 0) THEN
527 : WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
528 2505 : "MODULE FIST: ATOMIC COORDINATES IN "//TRIM(unit_str)
529 : WRITE (UNIT=iw, FMT="(/,T4,A,T30,A,T44,A,T58,A,T66,A,T77,A)") &
530 2505 : "Atom Kind Name", "X", "Y", "Z", "q(eff)", "Mass"
531 2505 : natom = SIZE(particle_set)
532 363107 : DO iatom = 1, natom
533 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
534 : kind_number=ikind, &
535 : name=name, &
536 : mass=mass, &
537 : qeff=qeff, &
538 360602 : shell=shell_kind)
539 360602 : IF (PRESENT(charges)) qeff = charges(iatom)
540 360602 : IF (ASSOCIATED(shell_kind)) THEN
541 : CALL get_shell(shell=shell_kind, &
542 : charge_core=qcore, &
543 3426 : charge_shell=qshell)
544 3426 : qeff = qcore + qshell
545 : END IF
546 : WRITE (UNIT=iw, FMT="(T2,I6,1X,I4,1X,A7,3(1X,F13.6),2(1X,F8.4))") &
547 1805515 : iatom, ikind, name, particle_set(iatom)%r(1:3)*conv, qeff, mass/massunit
548 : END DO
549 2505 : WRITE (iw, "(A)") ""
550 : END IF
551 :
552 : CALL cp_print_key_finished_output(iw, logger, subsys_section, &
553 9701 : "PRINT%ATOMIC_COORDINATES")
554 :
555 9701 : END SUBROUTINE write_fist_particle_coordinates
556 :
557 : ! **************************************************************************************************
558 : !> \brief Write the atomic coordinates to the output unit.
559 : !> \param particle_set ...
560 : !> \param qs_kind_set ...
561 : !> \param subsys_section ...
562 : !> \param label ...
563 : !> \date 05.06.2000
564 : !> \author MK
565 : !> \version 1.0
566 : ! **************************************************************************************************
567 17203 : SUBROUTINE write_qs_particle_coordinates(particle_set, qs_kind_set, subsys_section, label)
568 :
569 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
570 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
571 : TYPE(section_vals_type), POINTER :: subsys_section
572 : CHARACTER(LEN=*), INTENT(IN) :: label
573 :
574 : CHARACTER(len=*), PARAMETER :: routineN = 'write_qs_particle_coordinates'
575 :
576 : CHARACTER(LEN=2) :: element_symbol
577 : CHARACTER(LEN=default_string_length) :: unit_str
578 : INTEGER :: handle, iatom, ikind, iw, natom, z
579 : REAL(KIND=dp) :: conv, mass, zeff
580 : TYPE(cp_logger_type), POINTER :: logger
581 :
582 17203 : CALL timeset(routineN, handle)
583 :
584 17203 : NULLIFY (logger)
585 17203 : logger => cp_get_default_logger()
586 : iw = cp_print_key_unit_nr(logger, subsys_section, &
587 17203 : "PRINT%ATOMIC_COORDINATES", extension=".coordLog")
588 :
589 17203 : CALL section_vals_val_get(subsys_section, "PRINT%ATOMIC_COORDINATES%UNIT", c_val=unit_str)
590 17203 : conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
591 17203 : CALL uppercase(unit_str)
592 17203 : IF (iw > 0) THEN
593 : WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
594 3772 : "MODULE "//TRIM(label)//": ATOMIC COORDINATES IN "//TRIM(unit_str)
595 : WRITE (UNIT=iw, FMT="(/,T4,A,T30,A,T44,A,T58,A,T66,A,T77,A)") &
596 3772 : "Atom Kind Element", "X", "Y", "Z", "Z(eff)", "Mass"
597 3772 : natom = SIZE(particle_set)
598 23045 : DO iatom = 1, natom
599 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
600 : kind_number=ikind, &
601 : element_symbol=element_symbol, &
602 : mass=mass, &
603 19273 : z=z)
604 19273 : CALL get_qs_kind(qs_kind_set(ikind), zeff=zeff)
605 : WRITE (UNIT=iw, FMT="(T2,I6,1X,I4,1X,A2,1X,I4,3(1X,F13.6),2(1X,F8.4))") &
606 80864 : iatom, ikind, element_symbol, z, particle_set(iatom)%r(1:3)*conv, zeff, mass/massunit
607 : END DO
608 3772 : WRITE (iw, "(A)") ""
609 : END IF
610 :
611 : CALL cp_print_key_finished_output(iw, logger, subsys_section, &
612 17203 : "PRINT%ATOMIC_COORDINATES")
613 :
614 17203 : CALL timestop(handle)
615 :
616 17203 : END SUBROUTINE write_qs_particle_coordinates
617 :
618 : ! **************************************************************************************************
619 : !> \brief Write the matrix of the particle distances to the output unit.
620 : !> \param particle_set ...
621 : !> \param cell ...
622 : !> \param subsys_section ...
623 : !> \date 06.10.2000
624 : !> \author Matthias Krack
625 : !> \version 1.0
626 : ! **************************************************************************************************
627 10654 : SUBROUTINE write_particle_distances(particle_set, cell, subsys_section)
628 :
629 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
630 : TYPE(cell_type), POINTER :: cell
631 : TYPE(section_vals_type), POINTER :: subsys_section
632 :
633 : CHARACTER(len=*), PARAMETER :: routineN = 'write_particle_distances'
634 :
635 : CHARACTER(LEN=default_string_length) :: unit_str
636 : INTEGER :: handle, iatom, iw, jatom, natom
637 : INTEGER, DIMENSION(3) :: periodic
638 : LOGICAL :: explicit
639 : REAL(KIND=dp) :: conv, dab, dab_abort, dab_min, dab_warn
640 10654 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: distance_matrix
641 : REAL(KIND=dp), DIMENSION(3) :: rab
642 : TYPE(cp_logger_type), POINTER :: logger
643 :
644 10654 : CALL timeset(routineN, handle)
645 :
646 10654 : CPASSERT(ASSOCIATED(particle_set))
647 10654 : CPASSERT(ASSOCIATED(cell))
648 10654 : CPASSERT(ASSOCIATED(subsys_section))
649 :
650 10654 : NULLIFY (logger)
651 10654 : logger => cp_get_default_logger()
652 : iw = cp_print_key_unit_nr(logger, subsys_section, &
653 10654 : "PRINT%INTERATOMIC_DISTANCES", extension=".distLog")
654 :
655 10654 : CALL section_vals_val_get(subsys_section, "PRINT%INTERATOMIC_DISTANCES%UNIT", c_val=unit_str)
656 10654 : conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
657 : CALL section_vals_val_get(subsys_section, "PRINT%INTERATOMIC_DISTANCES%CHECK_INTERATOMIC_DISTANCES", &
658 10654 : r_val=dab_min, explicit=explicit)
659 :
660 10654 : dab_abort = 0.0_dp
661 10654 : dab_warn = 0.0_dp
662 10654 : natom = SIZE(particle_set)
663 :
664 : ! Compute interatomic distances only if their printout or check is explicitly requested
665 : ! Disable the default check for systems with more than 3000 atoms
666 10654 : IF (explicit .OR. (iw > 0) .OR. (natom <= 2000)) THEN
667 10612 : IF (dab_min > 0.0_dp) THEN
668 10608 : dab_warn = dab_min*conv
669 4 : ELSE IF (dab_min < 0.0_dp) THEN
670 0 : dab_abort = ABS(dab_min)*conv
671 : END IF
672 : END IF
673 :
674 10654 : IF ((iw > 0) .OR. (dab_abort > 0.0_dp) .OR. (dab_warn > 0.0_dp)) THEN
675 10608 : CALL get_cell(cell=cell, periodic=periodic)
676 10608 : IF (iw > 0) THEN
677 132 : ALLOCATE (distance_matrix(natom, natom))
678 72193 : distance_matrix(:, :) = 0.0_dp
679 : END IF
680 330257 : DO iatom = 1, natom
681 119889035 : DO jatom = iatom + 1, natom
682 : rab(:) = pbc(particle_set(iatom)%r(:), &
683 119558778 : particle_set(jatom)%r(:), cell)
684 119558778 : dab = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))*conv
685 119558778 : IF (dab_abort > 0.0_dp) THEN
686 : ! Stop the run for interatomic distances smaller than the requested threshold
687 0 : IF (dab < dab_abort) THEN
688 : CALL cp_abort(__LOCATION__, "The distance between the atoms "// &
689 : TRIM(ADJUSTL(cp_to_string(iatom, fmt="(I8)")))//" and "// &
690 : TRIM(ADJUSTL(cp_to_string(jatom, fmt="(I8)")))//" is only "// &
691 : TRIM(ADJUSTL(cp_to_string(dab, fmt="(F6.3)")))//" "// &
692 : TRIM(ADJUSTL(unit_str))//" and thus smaller than the requested threshold of "// &
693 : TRIM(ADJUSTL(cp_to_string(dab_abort, fmt="(F6.3)")))//" "// &
694 0 : TRIM(ADJUSTL(unit_str)))
695 : END IF
696 : END IF
697 119558778 : IF (dab < dab_warn) THEN
698 : ! Print warning for interatomic distances smaller than the requested threshold
699 : CALL cp_warn(__LOCATION__, "The distance between the atoms "// &
700 : TRIM(ADJUSTL(cp_to_string(iatom, fmt="(I8)")))//" and "// &
701 : TRIM(ADJUSTL(cp_to_string(jatom, fmt="(I8)")))//" is only "// &
702 : TRIM(ADJUSTL(cp_to_string(dab, fmt="(F6.3)")))//" "// &
703 : TRIM(ADJUSTL(unit_str))//" and thus smaller than the threshold of "// &
704 : TRIM(ADJUSTL(cp_to_string(dab_warn, fmt="(F6.3)")))//" "// &
705 908 : TRIM(ADJUSTL(unit_str)))
706 : END IF
707 119878427 : IF (iw > 0) THEN
708 35186 : distance_matrix(iatom, jatom) = dab
709 35186 : distance_matrix(jatom, iatom) = distance_matrix(iatom, jatom)
710 : END IF
711 : END DO
712 : END DO
713 10608 : IF (iw > 0) THEN
714 : ! Print the distance matrix
715 : WRITE (UNIT=iw, FMT="(/,/,T2,A)") &
716 33 : "INTERATOMIC DISTANCES IN "//TRIM(unit_str)
717 33 : CALL write_particle_matrix(distance_matrix, particle_set, iw)
718 33 : IF (ALLOCATED(distance_matrix)) DEALLOCATE (distance_matrix)
719 : END IF
720 : CALL cp_print_key_finished_output(iw, logger, subsys_section, &
721 10608 : "PRINT%INTERATOMIC_DISTANCES")
722 : END IF
723 :
724 10654 : CALL timestop(handle)
725 :
726 10654 : END SUBROUTINE write_particle_distances
727 :
728 : ! **************************************************************************************************
729 : !> \brief ...
730 : !> \param matrix ...
731 : !> \param particle_set ...
732 : !> \param iw ...
733 : !> \param el_per_part ...
734 : !> \param Ilist ...
735 : !> \param parts_per_line : number of particle columns to be printed in one line
736 : ! **************************************************************************************************
737 53 : SUBROUTINE write_particle_matrix(matrix, particle_set, iw, el_per_part, Ilist, parts_per_line)
738 : REAL(KIND=dp), DIMENSION(:, :) :: matrix
739 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
740 : INTEGER, INTENT(IN) :: iw
741 : INTEGER, INTENT(IN), OPTIONAL :: el_per_part
742 : INTEGER, DIMENSION(:), OPTIONAL, POINTER :: Ilist
743 : INTEGER, INTENT(IN), OPTIONAL :: parts_per_line
744 :
745 : CHARACTER(LEN=2) :: element_symbol
746 : CHARACTER(LEN=default_string_length) :: fmt_string1, fmt_string2
747 : INTEGER :: from, i, iatom, icol, jatom, katom, &
748 : my_el_per_part, my_parts_per_line, &
749 : natom, to
750 53 : INTEGER, DIMENSION(:), POINTER :: my_list
751 :
752 53 : my_el_per_part = 1
753 20 : IF (PRESENT(el_per_part)) my_el_per_part = el_per_part
754 53 : my_parts_per_line = 5
755 53 : IF (PRESENT(parts_per_line)) my_parts_per_line = MAX(parts_per_line, 1)
756 : WRITE (fmt_string1, FMT='(A,I0,A)') &
757 53 : "(/,T2,11X,", my_parts_per_line, "(4X,I5,4X))"
758 : WRITE (fmt_string2, FMT='(A,I0,A)') &
759 53 : "(T2,I5,2X,A2,2X,", my_parts_per_line, "(1X,F12.6))"
760 53 : IF (PRESENT(Ilist)) THEN
761 20 : natom = SIZE(Ilist)
762 : ELSE
763 33 : natom = SIZE(particle_set)
764 : END IF
765 159 : ALLOCATE (my_list(natom))
766 53 : IF (PRESENT(Ilist)) THEN
767 120 : my_list = Ilist
768 : ELSE
769 927 : DO i = 1, natom
770 927 : my_list(i) = i
771 : END DO
772 : END IF
773 53 : natom = natom*my_el_per_part
774 288 : DO jatom = 1, natom, my_parts_per_line
775 235 : from = jatom
776 235 : to = MIN(from + my_parts_per_line - 1, natom)
777 1279 : WRITE (UNIT=iw, FMT=TRIM(fmt_string1)) (icol, icol=from, to)
778 15446 : DO iatom = 1, natom
779 15158 : katom = iatom/my_el_per_part
780 15158 : IF (MOD(iatom, my_el_per_part) /= 0) katom = katom + 1
781 : CALL get_atomic_kind(atomic_kind=particle_set(my_list(katom))%atomic_kind, &
782 15158 : element_symbol=element_symbol)
783 : WRITE (UNIT=iw, FMT=TRIM(fmt_string2)) &
784 15158 : iatom, element_symbol, &
785 30551 : (matrix(iatom, icol), icol=from, to)
786 : END DO
787 : END DO
788 :
789 53 : DEALLOCATE (my_list)
790 :
791 53 : END SUBROUTINE write_particle_matrix
792 :
793 : ! **************************************************************************************************
794 : !> \brief Write structure data requested by a separate structure data input
795 : !> section to the output unit.
796 : !> input_section can be either motion_section or subsys_section.
797 : !>
798 : !> \param particle_set ...
799 : !> \param cell ...
800 : !> \param input_section ...
801 : !> \date 11.03.04
802 : !> \par History
803 : !> Recovered (23.03.06,MK)
804 : !> \author MK
805 : !> \version 1.0
806 : ! **************************************************************************************************
807 67624 : SUBROUTINE write_structure_data(particle_set, cell, input_section)
808 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
809 : TYPE(cell_type), POINTER :: cell
810 : TYPE(section_vals_type), POINTER :: input_section
811 :
812 : CHARACTER(LEN=*), PARAMETER :: routineN = 'write_structure_data'
813 :
814 : CHARACTER(LEN=default_string_length) :: string, unit_str
815 : INTEGER :: handle, i, i_rep, iw, n, n_rep, n_vals, &
816 : natom, new_size, old_size, wrk2(2), &
817 : wrk3(3), wrk4(4)
818 67624 : INTEGER, ALLOCATABLE, DIMENSION(:) :: work
819 67624 : INTEGER, DIMENSION(:), POINTER :: atomic_indices, index_list
820 : LOGICAL :: unique
821 : REAL(KIND=dp) :: conv, dab
822 : REAL(KIND=dp), DIMENSION(3) :: r, rab, rbc, rcd, s
823 : TYPE(cp_logger_type), POINTER :: logger
824 : TYPE(section_vals_type), POINTER :: section
825 :
826 67624 : CALL timeset(routineN, handle)
827 67624 : NULLIFY (atomic_indices)
828 67624 : NULLIFY (index_list)
829 67624 : NULLIFY (logger)
830 67624 : NULLIFY (section)
831 67624 : string = ""
832 :
833 67624 : logger => cp_get_default_logger()
834 : iw = cp_print_key_unit_nr(logger=logger, &
835 : basis_section=input_section, &
836 : print_key_path="PRINT%STRUCTURE_DATA", &
837 67624 : extension=".coordLog")
838 :
839 67624 : CALL section_vals_val_get(input_section, "PRINT%STRUCTURE_DATA%UNIT", c_val=unit_str)
840 67624 : conv = cp_unit_from_cp2k(1.0_dp, TRIM(unit_str))
841 67624 : CALL uppercase(unit_str)
842 67624 : IF (iw > 0) THEN
843 568 : natom = SIZE(particle_set)
844 : section => section_vals_get_subs_vals(section_vals=input_section, &
845 568 : subsection_name="PRINT%STRUCTURE_DATA")
846 :
847 568 : WRITE (UNIT=iw, FMT="(/,T2,A)") "REQUESTED STRUCTURE DATA"
848 : ! Print the requested atomic position vectors
849 : CALL section_vals_val_get(section_vals=section, &
850 : keyword_name="POSITION", &
851 568 : n_rep_val=n_rep)
852 568 : IF (n_rep > 0) THEN
853 : WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
854 145 : "Position vectors r(i) of the atoms i in "//TRIM(unit_str)
855 145 : old_size = 0
856 848 : DO i_rep = 1, n_rep
857 : CALL section_vals_val_get(section_vals=section, &
858 : keyword_name="POSITION", &
859 : i_rep_val=i_rep, &
860 703 : i_vals=atomic_indices)
861 703 : n_vals = SIZE(atomic_indices)
862 703 : new_size = old_size + n_vals
863 703 : CALL reallocate(index_list, 1, new_size)
864 2903 : index_list(old_size + 1:new_size) = atomic_indices(1:n_vals)
865 848 : old_size = new_size
866 : END DO
867 435 : ALLOCATE (work(new_size))
868 145 : CALL sort(index_list, new_size, work)
869 145 : DEALLOCATE (work)
870 1245 : DO i = 1, new_size
871 1100 : WRITE (UNIT=string, FMT="(A,I0,A)") "(", index_list(i), ")"
872 1100 : IF ((index_list(i) < 1) .OR. (index_list(i) > natom)) THEN
873 : WRITE (UNIT=iw, FMT="(T3,A)") &
874 30 : "Invalid atomic index "//TRIM(string)//" specified. Print request is ignored."
875 30 : CYCLE
876 : END IF
877 1070 : IF (i > 1) THEN
878 : ! Skip redundant indices
879 935 : IF (index_list(i) == index_list(i - 1)) CYCLE
880 : END IF
881 : WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6)") &
882 4425 : "r"//TRIM(string), "=", pbc(particle_set(index_list(i))%r(1:3), cell)*conv
883 : END DO
884 145 : DEALLOCATE (index_list)
885 : END IF
886 :
887 : ! Print the requested atomic position vectors in scaled coordinates
888 : CALL section_vals_val_get(section_vals=section, &
889 : keyword_name="POSITION_SCALED", &
890 568 : n_rep_val=n_rep)
891 568 : IF (n_rep > 0) THEN
892 : WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
893 27 : "Position vectors s(i) of the atoms i in scaled coordinates"
894 27 : old_size = 0
895 84 : DO i_rep = 1, n_rep
896 : CALL section_vals_val_get(section_vals=section, &
897 : keyword_name="POSITION_SCALED", &
898 : i_rep_val=i_rep, &
899 57 : i_vals=atomic_indices)
900 57 : n_vals = SIZE(atomic_indices)
901 57 : new_size = old_size + n_vals
902 57 : CALL reallocate(index_list, 1, new_size)
903 965 : index_list(old_size + 1:new_size) = atomic_indices(1:n_vals)
904 84 : old_size = new_size
905 : END DO
906 81 : ALLOCATE (work(new_size))
907 27 : CALL sort(index_list, new_size, work)
908 27 : DEALLOCATE (work)
909 481 : DO i = 1, new_size
910 454 : WRITE (UNIT=string, FMT="(A,I0,A)") "(", index_list(i), ")"
911 454 : IF ((index_list(i) < 1) .OR. (index_list(i) > natom)) THEN
912 : WRITE (UNIT=iw, FMT="(T3,A)") &
913 30 : "Invalid atomic index "//TRIM(string)//" specified. Print request is ignored."
914 30 : CYCLE
915 : END IF
916 424 : IF (i > 1) THEN
917 : ! Skip redundant indices
918 407 : IF (index_list(i) == index_list(i - 1)) CYCLE
919 : END IF
920 424 : r(1:3) = pbc(particle_set(index_list(i))%r(1:3), cell)
921 424 : CALL real_to_scaled(s, r, cell)
922 : WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6)") &
923 451 : "s"//TRIM(string), "=", s(1:3)
924 : END DO
925 27 : DEALLOCATE (index_list)
926 : END IF
927 :
928 : ! Print the requested distances
929 : CALL section_vals_val_get(section_vals=section, &
930 : keyword_name="DISTANCE", &
931 568 : n_rep_val=n)
932 568 : IF (n > 0) THEN
933 : WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
934 : "Distance vector r(i,j) between the atom i and j in "// &
935 129 : TRIM(unit_str)
936 355 : DO i = 1, n
937 : CALL section_vals_val_get(section_vals=section, &
938 : keyword_name="DISTANCE", &
939 : i_rep_val=i, &
940 226 : i_vals=atomic_indices)
941 226 : string = ""
942 : WRITE (UNIT=string, FMT="(A,2(I0,A))") &
943 226 : "(", atomic_indices(1), ",", atomic_indices(2), ")"
944 678 : wrk2 = atomic_indices
945 226 : CALL sort_unique(wrk2, unique)
946 355 : IF (((wrk2(1) >= 1) .AND. (wrk2(SIZE(wrk2)) <= natom)) .AND. unique) THEN
947 : rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
948 226 : particle_set(atomic_indices(2))%r(:), cell)
949 226 : dab = SQRT(rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3))
950 : WRITE (UNIT=iw, FMT="(T3,A,T20,A,3F13.6,3X,A,F13.6)") &
951 904 : "r"//TRIM(string), "=", rab(:)*conv, &
952 452 : "|r| =", dab*conv
953 : ELSE
954 : WRITE (UNIT=iw, FMT="(T3,A)") &
955 0 : "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
956 : END IF
957 : END DO
958 : END IF
959 :
960 : ! Print the requested angles
961 : CALL section_vals_val_get(section_vals=section, &
962 : keyword_name="ANGLE", &
963 568 : n_rep_val=n)
964 568 : IF (n > 0) THEN
965 : WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
966 : "Angle a(i,j,k) between the atomic distance vectors r(j,i) and "// &
967 67 : "r(j,k) in DEGREE"
968 139 : DO i = 1, n
969 : CALL section_vals_val_get(section_vals=section, &
970 : keyword_name="ANGLE", &
971 : i_rep_val=i, &
972 72 : i_vals=atomic_indices)
973 72 : string = ""
974 : WRITE (UNIT=string, FMT="(A,3(I0,A))") &
975 72 : "(", atomic_indices(1), ",", atomic_indices(2), ",", atomic_indices(3), ")"
976 288 : wrk3 = atomic_indices
977 72 : CALL sort_unique(wrk3, unique)
978 139 : IF (((wrk3(1) >= 1) .AND. (wrk3(SIZE(wrk3)) <= natom)) .AND. unique) THEN
979 : rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
980 67 : particle_set(atomic_indices(2))%r(:), cell)
981 : rbc(:) = pbc(particle_set(atomic_indices(2))%r(:), &
982 67 : particle_set(atomic_indices(3))%r(:), cell)
983 : WRITE (UNIT=iw, FMT="(T3,A,T26,A,F9.3)") &
984 268 : "a"//TRIM(string), "=", angle(-rab, rbc)*degree
985 : ELSE
986 : WRITE (UNIT=iw, FMT="(T3,A)") &
987 5 : "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
988 : END IF
989 : END DO
990 : END IF
991 :
992 : ! Print the requested dihedral angles
993 : CALL section_vals_val_get(section_vals=section, &
994 : keyword_name="DIHEDRAL_ANGLE", &
995 568 : n_rep_val=n)
996 568 : IF (n > 0) THEN
997 : WRITE (UNIT=iw, FMT="(/,T3,A,/)") &
998 : "Dihedral angle d(i,j,k,l) between the planes (i,j,k) and (j,k,l) "// &
999 5 : "in DEGREE"
1000 15 : DO i = 1, n
1001 : CALL section_vals_val_get(section_vals=section, &
1002 : keyword_name="DIHEDRAL_ANGLE", &
1003 : i_rep_val=i, &
1004 10 : i_vals=atomic_indices)
1005 10 : string = ""
1006 : WRITE (UNIT=string, FMT="(A,4(I0,A))") &
1007 10 : "(", atomic_indices(1), ",", atomic_indices(2), ",", &
1008 20 : atomic_indices(3), ",", atomic_indices(4), ")"
1009 50 : wrk4 = atomic_indices
1010 10 : CALL sort_unique(wrk4, unique)
1011 15 : IF (((wrk4(1) >= 1) .AND. (wrk4(SIZE(wrk4)) <= natom)) .AND. unique) THEN
1012 : rab(:) = pbc(particle_set(atomic_indices(1))%r(:), &
1013 0 : particle_set(atomic_indices(2))%r(:), cell)
1014 : rbc(:) = pbc(particle_set(atomic_indices(2))%r(:), &
1015 0 : particle_set(atomic_indices(3))%r(:), cell)
1016 : rcd(:) = pbc(particle_set(atomic_indices(3))%r(:), &
1017 0 : particle_set(atomic_indices(4))%r(:), cell)
1018 : WRITE (UNIT=iw, FMT="(T3,A,T26,A,F9.3)") &
1019 0 : "d"//TRIM(string), "=", dihedral_angle(rab, rbc, rcd)*degree
1020 : ELSE
1021 : WRITE (UNIT=iw, FMT="(T3,A)") &
1022 10 : "Invalid atomic indices "//TRIM(string)//" specified. Print request is ignored."
1023 : END IF
1024 : END DO
1025 : END IF
1026 : END IF
1027 : CALL cp_print_key_finished_output(iw, logger, input_section, &
1028 67624 : "PRINT%STRUCTURE_DATA")
1029 :
1030 67624 : CALL timestop(handle)
1031 :
1032 67624 : END SUBROUTINE write_structure_data
1033 :
1034 : ! **************************************************************************************************
1035 : !> \brief Write the final geometry and cell information to a CIF file
1036 : !> \param particle_set pointer to particles with atm_name, element_symbol and position
1037 : !> \param cell pointer to cell with abc, angle_alpha, angle_beta, angle_gamma and deth
1038 : !> \param input_section pointer to motion_section which has PRINT%FINAL_CIF
1039 : !> \param conv flag for whether convergence is achieved or not in optimization
1040 : !> \param keep_angles flag for whether cell optimization keeps initial angles
1041 : !> \param keep_symmetry flag for whether cell optimization keeps initial symmetry
1042 : !> \param keep_volume flag for whether cell optimization keeps initial volume
1043 : !> \param gopt_env_label the geometry optimization label "GEO_OPT", "CELL_OPT", ...
1044 : !> \param constraint_label label for directions with constraint in cell optimization
1045 : !> \par Intended to be invoked in gopt_f_methods:write_final_info.
1046 : !> This implementation does not consider higher space groups even if
1047 : !> one is detected, and the chemical formulae are neither written in
1048 : !> the sorted "Hill notation" nor expressed in groups of molecules.
1049 : !> Other potentially useful but yet to be written information includes:
1050 : !> the external pressure from CELL_OPT/EXTERNAL_POTENTIAL and the
1051 : !> stress tensor (virial) for CELL_OPT;
1052 : !> the fixed atoms from MOTION/CONSTRAINT/FIXED_ATOMS for all.
1053 : !>
1054 : !> History
1055 : !> 04.2026 - Created
1056 : !> \author HE Zilong
1057 : !> \version 1.0
1058 : ! **************************************************************************************************
1059 1085 : SUBROUTINE write_final_cif(particle_set, cell, input_section, conv, &
1060 : keep_angles, keep_symmetry, keep_volume, &
1061 : gopt_env_label, constraint_label)
1062 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1063 : TYPE(cell_type), INTENT(IN), POINTER :: cell
1064 : TYPE(section_vals_type), INTENT(IN), POINTER :: input_section
1065 : LOGICAL, INTENT(IN) :: conv, keep_angles, keep_symmetry, &
1066 : keep_volume
1067 : CHARACTER(LEN=default_string_length), INTENT(IN) :: gopt_env_label
1068 : CHARACTER(LEN=4), INTENT(IN) :: constraint_label
1069 :
1070 : CHARACTER(len=*), PARAMETER :: routineN = 'write_final_cif'
1071 :
1072 : CHARACTER(LEN=2) :: element_symbol
1073 1085 : CHARACTER(LEN=2), ALLOCATABLE :: element_list(:)
1074 : CHARACTER(LEN=4) :: perd_str
1075 1085 : CHARACTER(LEN=:), ALLOCATABLE :: formula_structural, formula_sum
1076 : CHARACTER(LEN=default_path_length) :: record
1077 : CHARACTER(LEN=default_string_length) :: atm_name, f_cif, f_label, f_type_symbol
1078 1085 : CHARACTER(LEN=default_string_length), ALLOCATABLE :: label(:), type_symbol(:)
1079 : CHARACTER(LEN=timestamp_length) :: timestamp
1080 : INTEGER :: elem_seen, file_unit, gcd_all, handle, &
1081 : i, iatom, ielem, natom, output_unit, &
1082 : symmetry_id, w_label, w_type_symbol
1083 1085 : INTEGER, ALLOCATABLE :: count_list(:)
1084 : INTEGER, DIMENSION(3) :: periodic
1085 : LOGICAL :: dummy, elem_in_list, orthorhombic
1086 : REAL(KIND=dp) :: angle_alpha, angle_beta, angle_gamma, &
1087 : deth
1088 : REAL(KIND=dp), DIMENSION(3) :: abc, r, s
1089 : REAL(KIND=dp), DIMENSION(3, 3) :: hmat
1090 : TYPE(cp_logger_type), POINTER :: logger
1091 : TYPE(enumeration_type), POINTER :: enum
1092 : TYPE(keyword_type), POINTER :: symmetry_keyword
1093 : TYPE(section_type), POINTER :: tmp_cell_section
1094 : TYPE(section_vals_type), POINTER :: print_key
1095 :
1096 1085 : CALL timeset(routineN, handle)
1097 :
1098 1085 : NULLIFY (enum, logger, symmetry_keyword, print_key, tmp_cell_section)
1099 1085 : logger => cp_get_default_logger()
1100 1085 : output_unit = cp_logger_get_default_io_unit(logger)
1101 1085 : print_key => section_vals_get_subs_vals(input_section, "PRINT%FINAL_CIF")
1102 :
1103 : ! Collect cell information
1104 1085 : perd_str = "NONE"
1105 : CALL get_cell(cell, alpha=angle_alpha, beta=angle_beta, gamma=angle_gamma, &
1106 : deth=deth, orthorhombic=orthorhombic, abc=abc, periodic=periodic, &
1107 1085 : h=hmat, symmetry_id=symmetry_id)
1108 4340 : IF (SUM(periodic(1:3)) /= 0) THEN
1109 693 : perd_str = ""
1110 693 : IF (periodic(1) == 1) perd_str = TRIM(perd_str)//"X"
1111 693 : IF (periodic(2) == 1) perd_str = TRIM(perd_str)//"Y"
1112 693 : IF (periodic(3) == 1) perd_str = TRIM(perd_str)//"Z"
1113 : END IF
1114 1085 : CALL create_cell_section(tmp_cell_section)
1115 1085 : symmetry_keyword => section_get_keyword(tmp_cell_section, "SYMMETRY")
1116 1085 : CALL keyword_get(symmetry_keyword, enum=enum)
1117 :
1118 : ! Collect atom information
1119 1085 : natom = SIZE(particle_set)
1120 1085 : ALLOCATE (element_list(nelem + 1), count_list(nelem + 1))
1121 130200 : count_list(:) = 0
1122 4340 : ALLOCATE (label(natom), type_symbol(natom))
1123 1085 : elem_seen = 0
1124 1085 : w_type_symbol = 0
1125 1085 : w_label = 0
1126 38205 : atom_loop: DO iatom = 1, natom
1127 37120 : elem_in_list = .FALSE.
1128 : CALL get_atomic_kind(atomic_kind=particle_set(iatom)%atomic_kind, &
1129 37120 : name=atm_name, element_symbol=element_symbol)
1130 37120 : type_symbol(iatom) = TRIM(atm_name)
1131 : ! From write_particle_coordinates above it seems possible
1132 : ! for some atoms to have empty element symbols; whatever
1133 : ! these are, do not count them in the chemical formula
1134 37120 : IF (LEN_TRIM(element_symbol) == 0) THEN
1135 0 : dummy = qmmm_ff_precond_only_qm(id1=atm_name)
1136 0 : label(iatom) = TRIM(atm_name)//TRIM(ADJUSTL(cp_to_string(iatom)))
1137 : ELSE
1138 37120 : label(iatom) = TRIM(element_symbol)//TRIM(ADJUSTL(cp_to_string(iatom)))
1139 311507 : elem_loop: DO ielem = 1, nelem + 1
1140 311507 : IF (element_list(ielem) == element_symbol) THEN
1141 35014 : elem_in_list = .TRUE.
1142 35014 : count_list(ielem) = count_list(ielem) + 1
1143 : EXIT elem_loop
1144 : END IF
1145 : END DO elem_loop
1146 : IF (.NOT. elem_in_list) THEN
1147 2106 : elem_seen = elem_seen + 1
1148 2106 : element_list(elem_seen) = element_symbol
1149 2106 : count_list(elem_seen) = 1
1150 : END IF
1151 : END IF
1152 37120 : IF (LEN_TRIM(type_symbol(iatom)) > w_type_symbol) &
1153 : w_type_symbol = LEN_TRIM(type_symbol(iatom))
1154 37120 : IF (LEN_TRIM(label(iatom)) > w_label) &
1155 1085 : w_label = LEN_TRIM(label(iatom))
1156 : END DO atom_loop
1157 :
1158 : ! Determine the format of each line in cif considering width of type_symbol and label
1159 : ! The fields are, in order:
1160 : ! _atom_site_type_symbol, _atom_site_label, _atom_site_symmetry_multiplicity,
1161 : ! _atom_site_fract_x, _atom_site_fract_y, _atom_site_fract_z, _atom_site_occupancy
1162 : ! in which:
1163 : ! _atom_site_type_symbol is taken as atm_name
1164 : ! _atom_site_label is taken as element_symbol//iatom
1165 : ! _atom_site_symmetry_multiplicity and _atom_site_occupancy are always 1
1166 1085 : f_type_symbol = "A"//TRIM(ADJUSTL(cp_to_string(w_type_symbol + 4)))
1167 1085 : f_label = "A"//TRIM(ADJUSTL(cp_to_string(w_label + 4)))
1168 1085 : f_cif = "(T3,"//TRIM(f_type_symbol)//","//TRIM(f_label)//",I4,3F14.8,F8.2)"
1169 :
1170 : ! Determine formula_sum
1171 1085 : CPASSERT(elem_seen > 0)
1172 1085 : CPASSERT(count_list(1) > 0)
1173 1085 : formula_sum = "'"
1174 3191 : DO ielem = 1, elem_seen
1175 2106 : formula_sum = formula_sum//TRIM(ADJUSTL(element_list(ielem)))
1176 2106 : formula_sum = formula_sum//TRIM(ADJUSTL(cp_to_string(count_list(ielem))))
1177 3191 : formula_sum = formula_sum//" "
1178 : END DO
1179 1085 : formula_sum = TRIM(ADJUSTL(formula_sum))//"'"
1180 :
1181 : ! Determine formula_structural and Z
1182 1085 : gcd_all = count_list(1)
1183 3191 : DO ielem = 1, elem_seen
1184 3191 : IF (count_list(ielem) /= 0) THEN
1185 2106 : gcd_all = gcd(gcd_all, count_list(ielem))
1186 : END IF
1187 : END DO
1188 65226 : IF (gcd_all > 1) count_list = count_list/gcd_all
1189 1085 : formula_structural = "'"
1190 3191 : DO ielem = 1, elem_seen
1191 2106 : formula_structural = formula_structural//TRIM(ADJUSTL(element_list(ielem)))
1192 2106 : formula_structural = formula_structural//TRIM(ADJUSTL(cp_to_string(count_list(ielem))))
1193 3191 : formula_structural = formula_structural//" "
1194 : END DO
1195 1085 : formula_structural = TRIM(ADJUSTL(formula_structural))//"'"
1196 :
1197 : ! Print a message to log
1198 : record = cp_print_key_generate_filename(logger, print_key, &
1199 : extension=".cif", &
1200 1085 : my_local=.FALSE.)
1201 1085 : IF (output_unit > 0) THEN
1202 672 : IF (conv) THEN
1203 : WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
1204 228 : routineN//": Optimization converged, writing CIF file gladly:"
1205 : ELSE
1206 : WRITE (UNIT=output_unit, FMT="(/,T2,A)") &
1207 444 : routineN//": Optimization not yet converged, writing CIF file anyway:"
1208 : END IF
1209 672 : WRITE (UNIT=output_unit, FMT="(T3,A)") TRIM(record)
1210 : END IF
1211 :
1212 : ! Make timestamp for the file
1213 1085 : CALL m_timestamp(timestamp)
1214 :
1215 : ! Prepare file unit and write to it
1216 : file_unit = cp_print_key_unit_nr(logger, input_section, "PRINT%FINAL_CIF", &
1217 1085 : file_status="REPLACE", extension=".cif")
1218 1085 : IF (file_unit > 0) THEN
1219 : ! Generic information
1220 : WRITE (UNIT=file_unit, FMT="(A)") &
1221 585 : "# CIF file created by CP2K "//TRIM(moduleN)//":"//TRIM(routineN)
1222 : WRITE (UNIT=file_unit, FMT="(A)") &
1223 585 : "data_"//TRIM(logger%iter_info%project_name)
1224 : WRITE (UNIT=file_unit, FMT="(A,T39,A)") &
1225 585 : "_audit_creation_date", timestamp(:10)
1226 : WRITE (UNIT=file_unit, FMT="(A,/,A,/,A)") &
1227 585 : "_audit_creation_method", ";", &
1228 1170 : TRIM(cp2k_version)//" (revision "//TRIM(compile_revision)//")"
1229 : WRITE (UNIT=file_unit, FMT="(A,/,A,/,A,/,A)") &
1230 585 : "Project name "//TRIM(logger%iter_info%project_name), &
1231 585 : "submitted by "//TRIM(r_user_name)//"@"//TRIM(r_host_name), &
1232 585 : "processed in "//TRIM(r_cwd), &
1233 1170 : "generated at "//TRIM(timestamp)
1234 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1235 585 : "- Optimization type: "//TRIM(gopt_env_label)
1236 585 : IF (conv) THEN
1237 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1238 228 : "- Optimization converged: TRUE"
1239 : ELSE
1240 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1241 357 : "- Optimization converged: FALSE"
1242 : END IF
1243 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1244 585 : "- Requested initial cell symmetry: "//TRIM(enum_i2c(enum, symmetry_id))
1245 585 : IF (orthorhombic) THEN
1246 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1247 463 : "- Cell is numerically orthorhombic: TRUE"
1248 : ELSE
1249 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1250 122 : "- Cell is numerically orthorhombic: FALSE"
1251 : END IF
1252 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1253 585 : "- Periodicity of cell: "//TRIM(perd_str)
1254 585 : IF (gopt_env_label == "CELL_OPT") THEN
1255 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1256 112 : "- Cell is subject to optimization: TRUE"
1257 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1258 112 : "- Cell has constraint on direction: "//TRIM(ADJUSTL(constraint_label))
1259 112 : IF (keep_angles) THEN
1260 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1261 15 : "- Keep angles between the cell vectors during optimization: TRUE"
1262 : ELSE
1263 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1264 97 : "- Keep angles between the cell vectors during optimization: FALSE"
1265 : END IF
1266 112 : IF (keep_symmetry) THEN
1267 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1268 21 : "- Keep initial cell symmetry during optimization: TRUE"
1269 : ELSE
1270 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1271 91 : "- Keep initial cell symmetry during optimization: FALSE"
1272 : END IF
1273 112 : IF (keep_volume) THEN
1274 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1275 3 : "- Keep initial cell volume during optimization: TRUE"
1276 : ELSE
1277 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1278 109 : "- Keep initial cell volume during optimization: FALSE"
1279 : END IF
1280 : ELSE
1281 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1282 473 : "- Cell is subject to optimization: FALSE"
1283 : END IF
1284 : WRITE (UNIT=file_unit, FMT="(T2,A)") &
1285 585 : "- Final cell vectors A, B, C by rows [angstrom]:"
1286 2340 : DO i = 1, 3
1287 : WRITE (UNIT=file_unit, FMT="(T3,3(1X,F19.10))") &
1288 1755 : cp_unit_from_cp2k(hmat(1, i), "angstrom"), &
1289 1755 : cp_unit_from_cp2k(hmat(2, i), "angstrom"), &
1290 4095 : cp_unit_from_cp2k(hmat(3, i), "angstrom")
1291 : END DO
1292 585 : WRITE (UNIT=file_unit, FMT="(A)") ";"
1293 : ! Data of cell and geometry
1294 : WRITE (UNIT=file_unit, FMT="(/,A,T44,A)") &
1295 585 : "_symmetry_space_group_name_H-M", "'P 1'"
1296 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1297 585 : "_cell_length_a", cp_unit_from_cp2k(abc(1), "angstrom")
1298 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1299 585 : "_cell_length_b", cp_unit_from_cp2k(abc(2), "angstrom")
1300 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1301 585 : "_cell_length_c", cp_unit_from_cp2k(abc(3), "angstrom")
1302 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1303 585 : "_cell_angle_alpha", angle_alpha
1304 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1305 585 : "_cell_angle_beta", angle_beta
1306 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1307 585 : "_cell_angle_gamma", angle_gamma
1308 : WRITE (UNIT=file_unit, FMT="(A,T48,A)") &
1309 585 : "_symmetry_Int_Tables_number", "1"
1310 : WRITE (UNIT=file_unit, FMT="(A,T36,A)") &
1311 585 : "_chemical_formula_structural", formula_structural
1312 : WRITE (UNIT=file_unit, FMT="(A,T36,A)") &
1313 585 : "_chemical_formula_sum", formula_sum
1314 : WRITE (UNIT=file_unit, FMT="(A,T31,F18.8)") &
1315 585 : "_cell_volume", cp_unit_from_cp2k(ABS(deth), "angstrom^3")
1316 : WRITE (UNIT=file_unit, FMT="(A,T41,I8)") &
1317 585 : "_cell_formula_units_Z", gcd_all
1318 : WRITE (UNIT=file_unit, FMT="(A,/,T2,A,/,T2,A,/,T3,A)") &
1319 585 : "loop_", "_symmetry_equiv_pos_site_id", &
1320 1170 : "_symmetry_equiv_pos_as_xyz", "1 'x, y, z'"
1321 : WRITE (UNIT=file_unit, FMT="(A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A,/,T2,A)") &
1322 585 : "loop_", "_atom_site_type_symbol", "_atom_site_label", &
1323 585 : "_atom_site_symmetry_multiplicity", "_atom_site_fract_x", &
1324 1170 : "_atom_site_fract_y", "_atom_site_fract_z", "_atom_site_occupancy"
1325 19780 : DO iatom = 1, natom
1326 19195 : r(1:3) = pbc(particle_set(iatom)%r(1:3), cell)
1327 19195 : CALL real_to_scaled(s, r, cell)
1328 76780 : DO i = 1, 3
1329 76780 : s(i) = MODULO(s(i), 1.0_dp)
1330 : END DO
1331 : WRITE (UNIT=file_unit, FMT=TRIM(f_cif)) &
1332 19780 : type_symbol(iatom), label(iatom), 1, s(1:3), 1.0_dp
1333 : END DO
1334 : END IF
1335 :
1336 : ! Finish
1337 1085 : DEALLOCATE (element_list, count_list, formula_structural, formula_sum, label, type_symbol)
1338 1085 : CALL section_release(tmp_cell_section)
1339 : CALL cp_print_key_finished_output(file_unit, logger, input_section, &
1340 1085 : "PRINT%FINAL_CIF")
1341 1085 : IF (output_unit > 0) &
1342 : WRITE (UNIT=output_unit, FMT='(/,T2,A)') &
1343 672 : routineN//": Done!"
1344 :
1345 1085 : CALL timestop(handle)
1346 :
1347 2170 : END SUBROUTINE write_final_cif
1348 :
1349 14806 : END MODULE particle_methods
|