Octopus
classical_particles.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2019 N. Tancogne-Dejean
3!! Copyright (C) 2020 M. Oliveira
4!! Copyright (C) 2021 S. Ohlmann, I. Albar, A. Obzhirov, A. Geondzhian, M. Lawan
5!! Copyright (C) 2022 M. Oliveira
6!!
7!! This program is free software; you can redistribute it and/or modify
8!! it under the terms of the GNU General Public License as published by
9!! the Free Software Foundation; either version 2, or (at your option)
10!! any later version.
11!!
12!! This program is distributed in the hope that it will be useful,
13!! but WITHOUT ANY WARRANTY; without even the implied warranty of
14!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15!! GNU General Public License for more details.
16!!
17!! You should have received a copy of the GNU General Public License
18!! along with this program; if not, write to the Free Software
19!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20!! 02110-1301, USA.
21!!
22
23#include "global.h"
24
27 use debug_oct_m
29 use global_oct_m
32 use io_oct_m
33 use, intrinsic :: iso_fortran_env
36 use mpi_oct_m
39 use parser_oct_m
47 use space_oct_m
48 use system_oct_m
49 use unit_oct_m
51 use utils_oct_m
53
54 implicit none
55
56 private
57 public :: &
67
68 integer, parameter :: &
69 OUTPUT_COORDINATES = 1, &
71
72 type, extends(system_t), abstract :: classical_particles_t
73 private
74 type(c_ptr), public :: output_handle(2)
75 type(space_t), public :: space
76 integer, public :: np
77 real(real64), allocatable, public :: mass(:)
78 real(real64), allocatable, public :: pos(:,:)
79 real(real64), allocatable, public :: vel(:,:)
80 real(real64), allocatable, public :: tot_force(:,:)
81 real(real64), allocatable, public :: lj_epsilon(:)
82 real(real64), allocatable, public :: lj_sigma(:)
83 logical, allocatable, public :: fixed(:)
84 ! !! The default is to let the particles move.
85 type(propagator_data_t),public :: prop_data
86 contains
87 procedure :: do_algorithmic_operation => classical_particles_do_algorithmic_operation
88 procedure :: is_tolerance_reached => classical_particles_is_tolerance_reached
89 procedure :: copy_quantities_to_interaction => classical_particles_copy_quantities_to_interaction
90 procedure :: update_interactions_start => classical_particles_update_interactions_start
91 procedure :: update_interactions_finish => classical_particles_update_interactions_finish
92 procedure :: output_start => classical_particles_output_start
93 procedure :: output_write => classical_particles_output_write
94 procedure :: output_finish => classical_particles_output_finish
95 procedure :: restart_write_data => classical_particles_restart_write_data
96 procedure :: restart_read_data => classical_particles_restart_read_data
97 procedure :: update_kinetic_energy => classical_particles_update_kinetic_energy
98 procedure :: center_of_mass => classical_particles_center_of_mass
99 procedure :: center_of_mass_vel => classical_particles_center_of_mass_vel
100 procedure :: center => classical_particles_center
101 procedure :: axis_large => classical_particles_axis_large
102 procedure :: axis_inertia => classical_particles_axis_inertia
103 end type classical_particles_t
104
105
106contains
107
108 ! ---------------------------------------------------------
113 ! ---------------------------------------------------------
114 subroutine classical_particles_init(this, np)
115 class(classical_particles_t), intent(inout) :: this
116 integer, intent(in) :: np
117
119
120 this%np = np
121 safe_allocate(this%mass(1:np))
122 this%mass = m_zero
123 safe_allocate(this%pos(1:this%space%dim, 1:np))
124 this%pos = m_zero
125 safe_allocate(this%vel(1:this%space%dim, 1:np))
126 this%vel = m_zero
127 safe_allocate(this%tot_force(1:this%space%dim, 1:np))
128 this%tot_force = m_zero
129 safe_allocate(this%fixed(1:np))
130 this%fixed = .false. ! By default we let the particles move.
131 safe_allocate(this%lj_epsilon(1:np))
132 this%lj_epsilon = m_zero
133 safe_allocate(this%lj_sigma(1:np))
134 this%lj_sigma = m_zero
135
136 ! No interaction directly supported by the classical particles (but classes
137 ! that extend this one can add their own)
138 allocate(this%supported_interactions(0))
139 allocate(this%supported_interactions_as_partner(0))
140
141 ! Quantities updated by the algorithm
142 call this%quantities%add(quantity_t("position", updated_on_demand = .false.))
143 call this%quantities%add(quantity_t("velocity", updated_on_demand = .false.))
144
145 ! Other quantities
146 call this%quantities%add(quantity_t("mass", updated_on_demand = .false., always_available=.true.))
147
149 end subroutine classical_particles_init
150
151 ! ---------------------------------------------------------
152 subroutine classical_particles_copy(this, cp_in)
153 class(classical_particles_t), intent(out) :: this
154 class(classical_particles_t), intent(in) :: cp_in
155
157
158 this%np = cp_in%np
159 safe_allocate_source_a(this%mass, cp_in%mass)
160 safe_allocate_source_a(this%pos, cp_in%pos)
161 safe_allocate_source_a(this%vel, cp_in%vel)
162 safe_allocate_source_a(this%tot_force, cp_in%tot_force)
163 safe_allocate_source_a(this%fixed, cp_in%fixed)
164
165 this%kinetic_energy = cp_in%kinetic_energy
166
167 this%quantities = cp_in%quantities
168 this%supported_interactions = cp_in%supported_interactions
170 this%prop_data = cp_in%prop_data
175 ! ---------------------------------------------------------
176 subroutine classical_particles_init_interaction(this, interaction)
177 class(classical_particles_t), target, intent(inout) :: this
178 class(interaction_surrogate_t), intent(inout) :: interaction
179
182 select type (interaction)
183 class default
184 message(1) = "Trying to initialize an unsupported interaction by classical particles."
185 call messages_fatal(1, namespace=this%namespace)
186 end select
191 ! ---------------------------------------------------------
192 logical function classical_particles_do_algorithmic_operation(this, operation, updated_quantities) result(done)
193 class(classical_particles_t), intent(inout) :: this
194 class(algorithmic_operation_t), intent(in) :: operation
195 character(len=:), allocatable, intent(out) :: updated_quantities(:)
196
197 integer :: ii, ip
198 real(real64), allocatable :: tmp_pos(:,:,:), tmp_vel(:,:,:)
199 real(real64) :: factor
200
202 call profiling_in(trim(this%namespace%get())//":"//trim(operation%id))
203
204 done = .true.
205 select type (prop => this%algo)
206 class is (propagator_t)
208 select case (operation%id)
210 this%prop_data%save_pos(:, 1:this%np) = this%pos(:, 1:this%np)
211 this%prop_data%save_vel(:, 1:this%np) = this%vel(:, 1:this%np)
212
213 case (verlet_start)
214 if (.not. this%prop_data%initialized) then
215 call this%prop_data%initialize(prop, this%space%dim, this%np)
216 do ip = 1, this%np
217 if (this%fixed(ip)) then
218 this%prop_data%acc(:, ip) = m_zero
219 else
220 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
221 end if
222 end do
223 end if
224
225 case (verlet_finish)
226 call this%prop_data%end()
227
228 case (beeman_finish)
229 call this%prop_data%end()
230
231 case (verlet_update_pos)
232 this%pos(:, 1:this%np) = this%pos(:, 1:this%np) + prop%dt * this%vel(:, 1:this%np) &
233 + m_half * prop%dt**2 * this%prop_data%acc(:, 1:this%np)
234 updated_quantities = ["position"]
235
237 do ii = size(this%prop_data%prev_acc, dim=3) - 1, 1, -1
238 this%prop_data%prev_acc(:, 1:this%np, ii + 1) = this%prop_data%prev_acc(:, 1:this%np, ii)
239 end do
240 do ip = 1, this%np
241 this%prop_data%prev_acc(:, ip, 1) = this%prop_data%acc(:, ip)
242 if (this%fixed(ip)) then
243 this%prop_data%acc(:, ip) = m_zero
244 else
245 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
246 end if
247 end do
248
249 case (verlet_compute_vel)
250 this%vel(:, 1:this%np) = this%vel(:, 1:this%np) &
251 + m_half * prop%dt * (this%prop_data%prev_acc(:, 1:this%np, 1) + this%prop_data%acc(:, 1:this%np))
252 updated_quantities = ["velocity"]
253
254 case (beeman_start)
255 if (.not. this%prop_data%initialized) then
256 call this%prop_data%initialize(prop, this%space%dim, this%np)
257 do ip = 1, this%np
258 if (this%fixed(ip)) then
259 this%prop_data%acc(:, ip) = m_zero
260 else
261 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
262 end if
263 this%prop_data%prev_acc(:, ip, 1) = this%prop_data%acc(:, ip)
264 end do
265 end if
266
267 case (beeman_predict_pos)
268 this%pos(:, 1:this%np) = this%pos(:, 1:this%np) + prop%dt * this%vel(:, 1:this%np) + &
269 m_one/6.0_real64 * prop%dt**2 * (m_four*this%prop_data%acc(:, 1:this%np) - this%prop_data%prev_acc(:, 1:this%np, 1))
270
271 if (.not. prop%predictor_corrector) then
272 updated_quantities = ["position"]
273 end if
274
275 case (beeman_predict_vel)
276 this%vel(:, 1:this%np) = this%vel(:, 1:this%np) + &
277 m_one/6.0_real64 * prop%dt * ( m_two * this%prop_data%acc(:, 1:this%np) + &
278 5.0_real64 * this%prop_data%prev_acc(:, 1:this%np, 1) - this%prop_data%prev_acc(:, 1:this%np, 2))
279 updated_quantities = ["velocity"]
280
281 case (beeman_correct_pos)
282 this%pos(:, 1:this%np) = this%prop_data%save_pos(:, 1:this%np) &
283 + prop%dt * this%prop_data%save_vel(:, 1:this%np) &
284 + m_one/6.0_real64 * prop%dt**2 * (this%prop_data%acc(:, 1:this%np) &
285 + m_two * this%prop_data%prev_acc(:, 1:this%np, 1))
286 updated_quantities = ["position"]
287
288 case (beeman_correct_vel)
289 this%vel(:, 1:this%np) = this%prop_data%save_vel(:, 1:this%np) &
290 + m_half * prop%dt * (this%prop_data%acc(:, 1:this%np) + this%prop_data%prev_acc(:, 1:this%np, 1))
291 updated_quantities = ["velocity"]
292
293 case (expmid_2step_start)
294 if (.not. this%prop_data%initialized) then
295 call this%prop_data%initialize(prop, this%space%dim, this%np)
296 this%prop_data%prev_pos(:, 1:this%np, 1) = this%pos(:, 1:this%np)
297 this%prop_data%prev_vel(:, 1:this%np, 1) = this%vel(:, 1:this%np)
298 end if
299
301 call this%prop_data%end()
302
304 this%pos(:, 1:this%np) = 1.5_real64*this%prop_data%save_pos(:, 1:this%np) &
305 - m_half*this%prop_data%prev_pos(:, 1:this%np, 1)
306 this%vel(:, 1:this%np) = 1.5_real64*this%prop_data%save_vel(:, 1:this%np) &
307 - m_half*this%prop_data%prev_vel(:, 1:this%np, 1)
308 this%prop_data%prev_pos(:, 1:this%np, 1) = this%prop_data%save_pos(:, 1:this%np)
309 this%prop_data%prev_vel(:, 1:this%np, 1) = this%prop_data%save_vel(:, 1:this%np)
310 updated_quantities = ["position", "velocity"]
311
312 case (update_hamiltonian)
313 do ip = 1, this%np
314 if (this%fixed(ip)) then
315 this%prop_data%hamiltonian_elements(:, ip) = m_zero
316 else
317 this%prop_data%hamiltonian_elements(:, ip) = this%tot_force(:, ip) / (this%mass(ip) * this%pos(:, ip) + m_epsilon)
318 end if
319 end do
320
322 safe_allocate(tmp_pos(1:this%space%dim, 1:this%np, 1:2))
323 safe_allocate(tmp_vel(1:this%space%dim, 1:this%np, 1:2))
324 ! apply exponential - at some point this could use the machinery of
325 ! exponential_apply (but this would require a lot of boilerplate code
326 ! like a Hamiltonian class etc)
327 ! prop_data%save_pos/vel contain the state at t - this is the state we want to
328 ! apply the Hamiltonian to
329 tmp_pos(:, 1:this%np, 1) = this%prop_data%save_pos(:, 1:this%np)
330 tmp_vel(:, 1:this%np, 1) = this%prop_data%save_vel(:, 1:this%np)
331 this%pos(:, 1:this%np) = this%prop_data%save_pos(:, 1:this%np)
332 this%vel(:, 1:this%np) = this%prop_data%save_vel(:, 1:this%np)
333 ! compute exponential with Taylor expansion
334 factor = m_one
335 do ii = 1, 4
336 factor = factor * prop%dt / ii
337 do ip = 1, this%np
338 ! apply hamiltonian
339 tmp_pos(:, ip, 2) = tmp_vel(:, ip, 1)
340 tmp_vel(:, ip, 2) = this%prop_data%hamiltonian_elements(:, ip) * tmp_pos(:, ip, 1)
341 ! swap temporary variables
342 tmp_pos(:, ip, 1) = tmp_pos(:, ip, 2)
343 tmp_vel(:, ip, 1) = tmp_vel(:, ip, 2)
344 ! accumulate components of Taylor expansion
345 this%pos(:, ip) = this%pos(:, ip) + factor * tmp_pos(:, ip, 1)
346 this%vel(:, ip) = this%vel(:, ip) + factor * tmp_vel(:, ip, 1)
347 end do
348 end do
349 safe_deallocate_a(tmp_pos)
350 safe_deallocate_a(tmp_vel)
351 updated_quantities = ["position", "velocity"]
352
354 ! only correct for dt/2 if not converged yet
355 if (.not. this%is_tolerance_reached(prop%scf_tol)) then
356 this%pos(:, 1:this%np) = m_half*(this%pos(:, 1:this%np) + this%prop_data%save_pos(:, 1:this%np))
357 this%vel(:, 1:this%np) = m_half*(this%vel(:, 1:this%np) + this%prop_data%save_vel(:, 1:this%np))
358 updated_quantities = ["position", "velocity"]
359 end if
360
361 case default
362 done = .false.
363 end select
364
365 class default
366 done = .false.
367 end select
368
369 call profiling_out(trim(this%namespace%get())//":"//trim(operation%id))
372
373 ! ---------------------------------------------------------
374 logical function classical_particles_is_tolerance_reached(this, tol) result(converged)
375 class(classical_particles_t), intent(in) :: this
376 real(real64), intent(in) :: tol
377
378 integer :: ip
379 real(real64) :: change, max_change
380
382
383 ! Here we put the criterion that maximum acceleration change is below the tolerance
384 max_change = m_zero
385 do ip = 1, this%np
386 change = sum((this%prop_data%prev_tot_force(1:this%space%dim, ip) - this%tot_force(1:this%space%dim, ip))**2) / &
387 this%mass(ip)
388 if (change > max_change) then
389 max_change = change
390 end if
391 end do
392 converged = max_change < tol**2
393
394 write(message(1), '(a, e13.6, a, e13.6)') "Debug: -- Maximum change in acceleration ", &
395 sqrt(max_change), " and tolerance ", tol
396 call messages_info(1, namespace=this%namespace, debug_only=.true.)
397
400
401 ! ---------------------------------------------------------
402 subroutine classical_particles_update_quantity(this, label)
403 class(classical_particles_t), intent(inout) :: this
404 character(len=*), intent(in) :: label
405
407
408 select case (label)
409 case default
410 message(1) = "Incompatible quantity."
411 call messages_fatal(1, namespace=this%namespace)
412 end select
413
416
417 ! ---------------------------------------------------------
418 subroutine classical_particles_init_interaction_as_partner(partner, interaction)
419 class(classical_particles_t), intent(in) :: partner
420 class(interaction_surrogate_t), intent(inout) :: interaction
421
423
424 select type (interaction)
425 class default
426 message(1) = "Unsupported interaction."
427 call messages_fatal(1, namespace=partner%namespace)
428 end select
429
432
433 ! ---------------------------------------------------------
434 subroutine classical_particles_copy_quantities_to_interaction(partner, interaction)
435 class(classical_particles_t), intent(inout) :: partner
436 class(interaction_surrogate_t), intent(inout) :: interaction
437
439
440 select type (interaction)
441 class default
442 message(1) = "Unsupported interaction."
443 call messages_fatal(1, namespace=partner%namespace)
444 end select
445
448
449 ! ---------------------------------------------------------
451 class(classical_particles_t), intent(inout) :: this
452
454
455 ! Store previous force, as it is used as SCF criterium
456 select type (prop => this%algo)
457 class is (propagator_t)
458 if (prop%predictor_corrector) then
459 this%prop_data%prev_tot_force(1:this%space%dim, 1:this%np) = this%tot_force(1:this%space%dim, 1:this%np)
460 end if
461 end select
462
465
466 ! ---------------------------------------------------------
468 class(classical_particles_t), intent(inout) :: this
469
470 type(interaction_iterator_t) :: iter
471
473
474 ! Compute the total force acting on the classical particles
475 this%tot_force(1:this%space%dim, 1:this%np) = m_zero
476 call iter%start(this%interactions)
477 do while (iter%has_next())
478 select type (interaction => iter%get_next())
479 class is (force_interaction_t)
480 this%tot_force(1:this%space%dim, 1:this%np) = this%tot_force(1:this%space%dim, 1:this%np) + &
481 interaction%force(1:this%space%dim, 1:this%np)
482 end select
483 end do
484
487
488 ! ---------------------------------------------------------
489 subroutine classical_particles_output_start(this)
490 class(classical_particles_t), intent(inout) :: this
491
492 integer :: iteration
493 real(real64) :: dt
494
496
497 select type (algo => this%algo)
498 class is (propagator_t)
499 dt = algo%dt
500 end select
501
502 iteration = this%iteration%counter()
503 if (iteration > 0) then
504 ! add one if restarting because the output routine is only called at the end of the timestep
505 iteration = iteration + 1
506 end if
507 ! Create output handle
508 call io_mkdir('td.general', this%namespace)
509 if (mpi_grp_is_root(this%grp)) then
510 call write_iter_init(this%output_handle(output_coordinates), iteration, dt, &
511 trim(io_workpath("td.general/coordinates", this%namespace)))
512 call write_iter_init(this%output_handle(output_energy), iteration, dt, &
513 trim(io_workpath("td.general/energy", this%namespace)))
514 end if
515
516 ! Output info for first iteration
517 if (iteration == 0) then
518 call this%output_write()
519 end if
520
523
524 ! ---------------------------------------------------------
525 subroutine classical_particles_output_finish(this)
526 class(classical_particles_t), intent(inout) :: this
529
530 if (mpi_grp_is_root(this%grp)) then
531 call write_iter_end(this%output_handle(output_coordinates))
532 call write_iter_end(this%output_handle(output_energy))
533 end if
534
537
538 ! ---------------------------------------------------------
539 subroutine classical_particles_output_write(this)
540 class(classical_particles_t), intent(inout) :: this
541
542 integer :: idir, ii, ip
543 character(len=50) :: aux
544 real(real64) :: tmp(this%space%dim)
545
546 if (.not. mpi_grp_is_root(this%grp)) return ! only first node outputs
547
549
550 if (this%iteration%counter() == 0) then
551 ! header
552 call write_iter_clear(this%output_handle(output_coordinates))
553 call write_iter_string(this%output_handle(output_coordinates), &
554 '############################################################################')
555 call write_iter_nl(this%output_handle(output_coordinates))
556 call write_iter_string(this%output_handle(output_coordinates),'# HEADER')
557 call write_iter_nl(this%output_handle(output_coordinates))
558
559 ! first line: column names
560 call write_iter_header_start(this%output_handle(output_coordinates))
561 do ip = 1, this%np
562 do idir = 1, this%space%dim
563 write(aux, '(a2,i3,a1,i3,a1)') 'x(', ip, ',', idir, ')'
564 call write_iter_header(this%output_handle(output_coordinates), aux)
565 end do
566 end do
567 do ip = 1, this%np
568 do idir = 1, this%space%dim
569 write(aux, '(a2,i3,a1,i3,a1)') 'v(', ip, ',', idir, ')'
570 call write_iter_header(this%output_handle(output_coordinates), aux)
571 end do
572 end do
573 do ip = 1, this%np
574 do idir = 1, this%space%dim
575 write(aux, '(a2,i3,a1,i3,a1)') 'f(', ip, ',', idir, ')'
576 call write_iter_header(this%output_handle(output_coordinates), aux)
577 end do
578 end do
579 call write_iter_nl(this%output_handle(output_coordinates))
580
581 ! second line: units
582 call write_iter_string(this%output_handle(output_coordinates), '#[Iter n.]')
583 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%time)) // ']')
584 do ip = 1, this%np
585 do idir = 1, this%space%dim
586 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%length)) // ']')
587 end do
588 end do
589 do ip = 1, this%np
590 do idir = 1, this%space%dim
591 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%velocity)) // ']')
592 end do
593 end do
594 do ip = 1, this%np
595 do idir = 1, this%space%dim
596 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%force)) // ']')
597 end do
598 end do
599 call write_iter_nl(this%output_handle(output_coordinates))
600
601 call write_iter_string(this%output_handle(output_coordinates), &
602 '############################################################################')
603 call write_iter_nl(this%output_handle(output_coordinates))
604 end if
605
606 call write_iter_start(this%output_handle(output_coordinates))
607
608 ! Position
609 do ip = 1, this%np
610 tmp(1:this%space%dim) = units_from_atomic(units_out%length, this%pos(1:this%space%dim, ip))
611 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
612 end do
613 ! Velocity
614 do ip = 1, this%np
615 tmp(1:this%space%dim) = units_from_atomic(units_out%velocity, this%vel(1:this%space%dim, ip))
616 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
617 end do
618 ! Force
619 do ip = 1, this%np
620 tmp(1:this%space%dim) = units_from_atomic(units_out%force, this%tot_force(1:this%space%dim, ip))
621 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
622 end do
623
624 call write_iter_nl(this%output_handle(output_coordinates))
625
626 ! Energies
627 if (this%iteration%counter() == 0) then
628 ! header
629 call write_iter_clear(this%output_handle(output_energy))
630 call write_iter_string(this%output_handle(output_energy), &
631 '############################################################################')
632 call write_iter_nl(this%output_handle(output_energy))
633 call write_iter_string(this%output_handle(output_energy),'# HEADER')
634 call write_iter_nl(this%output_handle(output_energy))
635
636 ! first line: column names
637 call write_iter_header_start(this%output_handle(output_energy))
638 call write_iter_header(this%output_handle(output_energy), 'Total')
639 call write_iter_header(this%output_handle(output_energy), 'Kinetic')
640 call write_iter_header(this%output_handle(output_energy), 'Potential')
641 call write_iter_header(this%output_handle(output_energy), 'Internal')
642 call write_iter_nl(this%output_handle(output_energy))
643
644 ! second line: units
645 call write_iter_string(this%output_handle(output_energy), '#[Iter n.]')
646 call write_iter_header(this%output_handle(output_energy), '[' // trim(units_abbrev(units_out%time)) // ']')
647 do ii = 1, 4
648 call write_iter_header(this%output_handle(output_energy), '[' // trim(units_abbrev(units_out%energy)) // ']')
649 end do
650 call write_iter_nl(this%output_handle(output_energy))
651
652 call write_iter_string(this%output_handle(output_energy), &
653 '############################################################################')
654 call write_iter_nl(this%output_handle(output_energy))
655 end if
656 call write_iter_start(this%output_handle(output_energy))
657
658 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%total_energy), 1)
659 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%kinetic_energy), 1)
660 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%potential_energy), 1)
661 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%internal_energy), 1)
662 call write_iter_nl(this%output_handle(output_energy))
663
666
667 ! ---------------------------------------------------------
669 class(classical_particles_t), intent(inout) :: this
670
671 integer :: restart_file_unit
672
674
675 if (mpi_grp_is_root(this%grp)) then
676 call write_iter_flush(this%output_handle(output_coordinates))
677 call write_iter_flush(this%output_handle(output_energy))
678 end if
679
680 call profiling_in(trim(this%namespace%get())//":"//"RESTART_WRITE")
681
682 call io_mkdir('restart/'//td_dir, this%namespace, parents=.true.)
683 restart_file_unit = io_open('restart/'//td_dir// 'restart_classical_particles', this%namespace, action='write')
684 write(restart_file_unit, *) this%np
685 write(restart_file_unit, *) this%mass(:)
686 write(restart_file_unit, *) this%pos(:,:)
687 write(restart_file_unit, *) this%vel(:,:)
688 write(restart_file_unit, *) this%tot_force(:,:)
689 call io_close(restart_file_unit)
690
691 if (this%iteration%counter() > 0) then
692 ! only initialized after the first time step
693 call this%prop_data%restart_write(this%namespace, this%algo)
694 end if
695
696 message(1) = "Successfully wrote restart data for system "//trim(this%namespace%get())
697 call messages_info(1, namespace=this%namespace)
698
699 call profiling_out(trim(this%namespace%get())//":"//"RESTART_WRITE")
702
703 ! ---------------------------------------------------------
704 logical function classical_particles_restart_read_data(this)
705 class(classical_particles_t), intent(inout) :: this
706
707 integer :: restart_file_unit
708
710 call profiling_in(trim(this%namespace%get())//":"//"RESTART_READ")
711
712 call io_mkdir('restart/'//td_dir, this%namespace, parents=.true.)
713 restart_file_unit = io_open('restart/'//td_dir// 'restart_classical_particles', this%namespace, action='read', die=.false.)
714 if (restart_file_unit /= -1) then
715 read(restart_file_unit, *) this%np
716 read(restart_file_unit, *) this%mass(:)
717 read(restart_file_unit, *) this%pos(:,:)
718 read(restart_file_unit, *) this%vel(:,:)
719 read(restart_file_unit, *) this%tot_force(:,:)
720 call io_close(restart_file_unit)
721 call this%prop_data%initialize(this%algo, this%space%dim, this%np)
722 if (this%iteration%counter() > 0) then
723 ! only initialized after the first time step
724 classical_particles_restart_read_data = this%prop_data%restart_read(this%namespace, this%algo)
725 else
727 end if
728 else
729 ! could not open file
731 end if
732
734 message(1) = "Successfully read restart data for system "//trim(this%namespace%get())
735 ! namespace should be added here at some point
736 call messages_info(1)
737 else
738 message(1) = "Failed to read restart data for system "//trim(this%namespace%get())
739 ! namespace should be added here at some point
740 call messages_info(1)
741 end if
742
743 call profiling_out(trim(this%namespace%get())//":"//"RESTART_READ")
746
747 ! ---------------------------------------------------------
749 class(classical_particles_t), intent(inout) :: this
750
751 integer :: ip
752
754
755 this%kinetic_energy = m_zero
756 do ip = 1, this%np
757 this%kinetic_energy = this%kinetic_energy + m_half * this%mass(ip) * sum(this%vel(:, ip)**2)
758 end do
759
762
763 ! ---------------------------------------------------------
764 function classical_particles_center_of_mass(this, mask, pseudo) result(pos)
765 class(classical_particles_t), intent(in) :: this
766 logical, optional, intent(in) :: mask(:)
767 logical, optional, intent(in) :: pseudo
768 real(real64) :: pos(this%space%dim)
769
770 real(real64) :: mass, total_mass
771 integer :: ip
772
774
775 assert(.not. this%space%is_periodic())
776
777 pos = m_zero
778 total_mass = m_zero
779 mass = m_one
780 do ip = 1, this%np
781 if (present(mask)) then
782 if (.not. mask(ip)) cycle
783 end if
784 if (.not. optional_default(pseudo, .false.)) then
785 mass = this%mass(ip)
786 end if
787 pos = pos + mass*this%pos(:, ip)
788 total_mass = total_mass + mass
789 end do
790 pos = pos/total_mass
791
794
795 ! ---------------------------------------------------------
796 function classical_particles_center_of_mass_vel(this) result(vel)
797 class(classical_particles_t), intent(in) :: this
798 real(real64) :: vel(this%space%dim)
799
800 real(real64) :: mass, total_mass
801 integer :: ip
802
804
805 vel = m_zero
806 total_mass = m_zero
807 do ip = 1, this%np
808 mass = this%mass(ip)
809 total_mass = total_mass + mass
810 vel = vel + mass*this%vel(:, ip)
811 end do
812 vel = vel/total_mass
813
816
817 ! ---------------------------------------------------------
818 function classical_particles_center(this) result(pos)
819 class(classical_particles_t), intent(in) :: this
820 real(real64) :: pos(this%space%dim)
821
822 real(real64) :: xmin(this%space%dim), xmax(this%space%dim)
823 integer :: ip, idir
824
826
827 xmin = m_huge
828 xmax = -m_huge
829 do ip = 1, this%np
830 do idir = 1, this%space%dim
831 if (this%pos(idir, ip) > xmax(idir)) xmax(idir) = this%pos(idir, ip)
832 if (this%pos(idir, ip) < xmin(idir)) xmin(idir) = this%pos(idir, ip)
833 end do
834 end do
835
836 pos = (xmax + xmin)/m_two
837
839 end function classical_particles_center
840
841 ! ---------------------------------------------------------
842 subroutine classical_particles_axis_large(this, x, x2)
843 class(classical_particles_t), intent(in) :: this
844 real(real64), intent(out) :: x(this%space%dim)
845 real(real64), intent(out) :: x2(this%space%dim)
846
847 integer :: ip, jp
848 real(real64) :: rmax, r, r2
849
851
852 assert(.not. this%space%is_periodic())
853
854 ! first get the further apart atoms
855 rmax = -m_huge
856 do ip = 1, this%np
857 do jp = 1, this%np/2 + 1
858 r = norm2(this%pos(:, ip) - this%pos(:, jp))
859 if (r > rmax) then
860 rmax = r
861 x = this%pos(:, ip) - this%pos(:, jp)
862 end if
863 end do
864 end do
865 x = x /norm2(x)
866
867 ! now let us find out what is the second most important axis
868 rmax = -m_huge
869 do ip = 1, this%np
870 r2 = sum(x * this%pos(:, ip))
871 r = norm2(this%pos(:, ip) - r2*x)
872 if (r > rmax) then
873 rmax = r
874 x2 = this%pos(:, ip) - r2*x
875 end if
876 end do
877
879 end subroutine classical_particles_axis_large
880
881 ! ---------------------------------------------------------
884 subroutine classical_particles_axis_inertia(this, x, x2, pseudo)
885 class(classical_particles_t), intent(in) :: this
886 real(real64), intent(out) :: x(this%space%dim)
887 real(real64), intent(out) :: x2(this%space%dim)
888 logical, intent(in) :: pseudo
890 real(real64) :: mass, tinertia(this%space%dim, this%space%dim), eigenvalues(this%space%dim)
891 integer :: ii, jj, ip
892 type(unit_t) :: unit
893
895
896 assert(.not. this%space%is_periodic())
897
898 ! first calculate the inertia tensor
899 tinertia = m_zero
900 mass = m_one
901 do ip = 1, this%np
902 if (.not. pseudo) mass = this%mass(ip)
903 do ii = 1, this%space%dim
904 tinertia(ii, :) = tinertia(ii, :) - mass*this%pos(ii, ip)*this%pos(:, ip)
905 tinertia(ii, ii) = tinertia(ii, ii) + mass*sum(this%pos(:, ip)**2)
906 end do
907 end do
908
909 unit = units_out%length**2
910 ! note: we always use amu for atomic masses, so no unit conversion to/from atomic is needed.
911 if (pseudo) then
912 write(message(1),'(a)') 'Moment of pseudo-inertia tensor [' // trim(units_abbrev(unit)) // ']'
913 else
914 write(message(1),'(a)') 'Moment of inertia tensor [amu*' // trim(units_abbrev(unit)) // ']'
915 end if
916 call messages_info(1, namespace=this%namespace)
917 call output_tensor(tinertia, this%space%dim, unit, write_average = .true., namespace=this%namespace)
918
919 call lalg_eigensolve(this%space%dim, tinertia, eigenvalues)
920
921 write(message(1),'(a,6f25.6)') 'Eigenvalues: ', units_from_atomic(unit, eigenvalues)
922 call messages_info(1, namespace=this%namespace)
923
924 ! make a choice to fix the sign of the axis.
925 do ii = 1, 2
926 jj = maxloc(abs(tinertia(:,ii)), dim = 1)
927 if (tinertia(jj,ii) < m_zero) tinertia(:,ii) = -tinertia(:,ii)
928 end do
929 x = tinertia(:,1)
930 x2 = tinertia(:,2)
931
934
935 ! ---------------------------------------------------------
936 subroutine classical_particles_end(this)
937 class(classical_particles_t), intent(inout) :: this
938
940
941 safe_deallocate_a(this%mass)
942 safe_deallocate_a(this%pos)
943 safe_deallocate_a(this%vel)
944 safe_deallocate_a(this%tot_force)
945 safe_deallocate_a(this%fixed)
946 safe_deallocate_a(this%lj_epsilon)
947 safe_deallocate_a(this%lj_sigma)
948
949 call system_end(this)
950
952 end subroutine classical_particles_end
953
955
956!! Local Variables:
957!! mode: f90
958!! coding: utf-8
959!! End:
Writes to the corresponding file and adds one to the iteration. Must be called after write_iter_init(...
Definition: write_iter.F90:167
This module implements the basic elements defining algorithms.
Definition: algorithm.F90:141
subroutine classical_particles_output_finish(this)
subroutine classical_particles_axis_large(this, x, x2)
logical function classical_particles_is_tolerance_reached(this, tol)
subroutine classical_particles_output_write(this)
subroutine, public classical_particles_update_quantity(this, label)
logical function classical_particles_do_algorithmic_operation(this, operation, updated_quantities)
subroutine classical_particles_update_kinetic_energy(this)
subroutine classical_particles_axis_inertia(this, x, x2, pseudo)
This subroutine assumes that the origin of the coordinates is the center of mass of the system.
subroutine classical_particles_output_start(this)
logical function classical_particles_restart_read_data(this)
subroutine, public classical_particles_init_interaction_as_partner(partner, interaction)
subroutine, public classical_particles_end(this)
subroutine, public classical_particles_init(this, np)
The init routine is a module level procedure This has the advantage that different classes can have d...
subroutine, public classical_particles_copy(this, cp_in)
subroutine classical_particles_update_interactions_finish(this)
subroutine classical_particles_update_interactions_start(this)
subroutine, public classical_particles_copy_quantities_to_interaction(partner, interaction)
real(real64) function, dimension(this%space%dim) classical_particles_center_of_mass_vel(this)
subroutine, public classical_particles_init_interaction(this, interaction)
subroutine, public classical_particles_restart_write_data(this)
real(real64) function, dimension(this%space%dim) classical_particles_center_of_mass(this, mask, pseudo)
real(real64) function, dimension(this%space%dim) classical_particles_center(this)
real(real64), parameter, public m_two
Definition: global.F90:190
real(real64), parameter, public m_huge
Definition: global.F90:206
real(real64), parameter, public m_zero
Definition: global.F90:188
real(real64), parameter, public m_four
Definition: global.F90:192
real(real64), parameter, public m_epsilon
Definition: global.F90:204
character(len= *), parameter, public td_dir
Definition: global.F90:251
real(real64), parameter, public m_half
Definition: global.F90:194
real(real64), parameter, public m_one
Definition: global.F90:189
This module defines the abstract interaction_t class, and some auxiliary classes for interactions.
Definition: io.F90:114
subroutine, public io_close(iunit, grp)
Definition: io.F90:418
character(len=max_path_len) function, public io_workpath(path, namespace)
Definition: io.F90:270
subroutine, public io_mkdir(fname, namespace, parents)
Definition: io.F90:311
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:352
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:414
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:616
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
Definition: mpi.F90:434
This module implements the multisystem debug functionality.
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
character(len=algo_label_len), parameter, public beeman_start
character(len=algo_label_len), parameter, public beeman_finish
character(len=algo_label_len), parameter, public beeman_compute_acc
character(len=algo_label_len), parameter, public beeman_correct_pos
character(len=algo_label_len), parameter, public beeman_predict_pos
character(len=algo_label_len), parameter, public beeman_correct_vel
character(len=algo_label_len), parameter, public beeman_predict_vel
character(len=algo_label_len), parameter, public expmid_2step_predict_dt
character(len=algo_label_len), parameter, public expmid_2step_finish
character(len=algo_label_len), parameter, public expmid_2step_correct_dt_2
character(len=algo_label_len), parameter, public update_hamiltonian
character(len=algo_label_len), parameter, public expmid_2step_predict_dt_2
character(len=algo_label_len), parameter, public expmid_2step_start
This module implements the basic propagator framework.
Definition: propagator.F90:117
character(len=algo_label_len), parameter, public store_current_status
Definition: propagator.F90:169
character(len=30), parameter, public verlet_start
character(len=30), parameter, public verlet_compute_acc
character(len=30), parameter, public verlet_update_pos
character(len=30), parameter, public verlet_finish
character(len=30), parameter, public verlet_compute_vel
This module defines the quantity_t class and the IDs for quantities, which can be exposed by a system...
Definition: quantity.F90:138
This module implements the abstract system type.
Definition: system.F90:118
subroutine, public system_end(this)
Definition: system.F90:1147
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:223
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:118
subroutine, public output_tensor(tensor, ndim, unit, write_average, iunit, namespace)
Definition: utils.F90:242
Explicit interfaces to C functions, defined in write_iter_low.cc.
Definition: write_iter.F90:114
Descriptor of one algorithmic operation.
Definition: algorithm.F90:163
These class extend the list and list iterator to make an interaction list.
surrogate interaction class to avoid circular dependencies between modules.
Abstract class implementing propagators.
Definition: propagator.F90:138
Systems (system_t) can expose quantities that can be used to calculate interactions with other system...
Definition: quantity.F90:171
Abstract class for systems.
Definition: system.F90:172
int true(void)