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 type(propagator_data_t),public :: prop_data
85 contains
86 procedure :: do_algorithmic_operation => classical_particles_do_algorithmic_operation
87 procedure :: is_tolerance_reached => classical_particles_is_tolerance_reached
88 procedure :: copy_quantities_to_interaction => classical_particles_copy_quantities_to_interaction
89 procedure :: update_interactions_start => classical_particles_update_interactions_start
90 procedure :: update_interactions_finish => classical_particles_update_interactions_finish
91 procedure :: output_start => classical_particles_output_start
92 procedure :: output_write => classical_particles_output_write
93 procedure :: output_finish => classical_particles_output_finish
94 procedure :: restart_write_data => classical_particles_restart_write_data
95 procedure :: restart_read_data => classical_particles_restart_read_data
96 procedure :: update_kinetic_energy => classical_particles_update_kinetic_energy
97 procedure :: center_of_mass => classical_particles_center_of_mass
98 procedure :: center_of_mass_vel => classical_particles_center_of_mass_vel
99 procedure :: center => classical_particles_center
100 procedure :: axis_large => classical_particles_axis_large
101 procedure :: axis_inertia => classical_particles_axis_inertia
102 end type classical_particles_t
103
104
105contains
106
107 ! ---------------------------------------------------------
112 ! ---------------------------------------------------------
113 subroutine classical_particles_init(this, np)
114 class(classical_particles_t), intent(inout) :: this
115 integer, intent(in) :: np
116
119 this%np = np
120 safe_allocate(this%mass(1:np))
121 this%mass = m_zero
122 safe_allocate(this%pos(1:this%space%dim, 1:np))
123 this%pos = m_zero
124 safe_allocate(this%vel(1:this%space%dim, 1:np))
125 this%vel = m_zero
126 safe_allocate(this%tot_force(1:this%space%dim, 1:np))
127 this%tot_force = m_zero
128 safe_allocate(this%fixed(1:np))
129 this%fixed = .false. ! By default we let the particles move.
130 safe_allocate(this%lj_epsilon(1:np))
131 this%lj_epsilon = m_zero
132 safe_allocate(this%lj_sigma(1:np))
133 this%lj_sigma = m_zero
134
135 ! No interaction directly supported by the classical particles (but classes
136 ! that extend this one can add their own)
137 allocate(this%supported_interactions(0))
138 allocate(this%supported_interactions_as_partner(0))
139
140 ! Quantities updated by the algorithm
141 this%quantities(position)%required = .true.
142 this%quantities(velocity)%required = .true.
143 this%quantities(position)%updated_on_demand = .false.
144 this%quantities(velocity)%updated_on_demand = .false.
145
146 ! Other quantities
147 this%quantities(mass)%updated_on_demand = .false.
148 this%quantities(mass)%always_available = .true.
149
151 end subroutine classical_particles_init
152
153 ! ---------------------------------------------------------
154 subroutine classical_particles_copy(this, cp_in)
155 class(classical_particles_t), intent(out) :: this
156 class(classical_particles_t), intent(in) :: cp_in
157
159
160 this%np = cp_in%np
161 safe_allocate_source_a(this%mass, cp_in%mass)
162 safe_allocate_source_a(this%pos, cp_in%pos)
163 safe_allocate_source_a(this%vel, cp_in%vel)
164 safe_allocate_source_a(this%tot_force, cp_in%tot_force)
165 safe_allocate_source_a(this%fixed, cp_in%fixed)
166
167 this%kinetic_energy = cp_in%kinetic_energy
169 this%quantities = cp_in%quantities
170 this%supported_interactions = cp_in%supported_interactions
172 this%quantities(mass)%updated_on_demand = .false.
173 this%quantities(mass)%always_available = .true.
174 this%prop_data = cp_in%prop_data
178
179 ! ---------------------------------------------------------
180 subroutine classical_particles_init_interaction(this, interaction)
181 class(classical_particles_t), target, intent(inout) :: this
182 class(interaction_surrogate_t), intent(inout) :: interaction
186 select type (interaction)
187 class default
188 message(1) = "Trying to initialize an unsupported interaction by classical particles."
189 call messages_fatal(1, namespace=this%namespace)
190 end select
195 ! ---------------------------------------------------------
196 logical function classical_particles_do_algorithmic_operation(this, operation, updated_quantities) result(done)
197 class(classical_particles_t), intent(inout) :: this
198 class(algorithmic_operation_t), intent(in) :: operation
199 integer, allocatable, intent(out) :: updated_quantities(:)
200
201 integer :: ii, ip
202 real(real64), allocatable :: tmp_pos(:,:,:), tmp_vel(:,:,:)
203 real(real64) :: factor
204
206 call profiling_in(trim(this%namespace%get())//":"//trim(operation%id))
207
208 done = .true.
209 select type (prop => this%algo)
210 class is (propagator_t)
211
212 select case (operation%id)
214 this%prop_data%save_pos(:, 1:this%np) = this%pos(:, 1:this%np)
215 this%prop_data%save_vel(:, 1:this%np) = this%vel(:, 1:this%np)
216
217 case (verlet_start)
218 if (.not. this%prop_data%initialized) then
219 call this%prop_data%initialize(prop, this%space%dim, this%np)
220 do ip = 1, this%np
221 if (this%fixed(ip)) then
222 this%prop_data%acc(:, ip) = m_zero
223 else
224 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
225 end if
226 end do
227 end if
228
229 case (verlet_finish)
230 call this%prop_data%end()
231
232 case (beeman_finish)
233 call this%prop_data%end()
234
235 case (verlet_update_pos)
236 this%pos(:, 1:this%np) = this%pos(:, 1:this%np) + prop%dt * this%vel(:, 1:this%np) &
237 + m_half * prop%dt**2 * this%prop_data%acc(:, 1:this%np)
238 updated_quantities = [position]
239
241 do ii = size(this%prop_data%prev_acc, dim=3) - 1, 1, -1
242 this%prop_data%prev_acc(:, 1:this%np, ii + 1) = this%prop_data%prev_acc(:, 1:this%np, ii)
243 end do
244 do ip = 1, this%np
245 this%prop_data%prev_acc(:, ip, 1) = this%prop_data%acc(:, ip)
246 if (this%fixed(ip)) then
247 this%prop_data%acc(:, ip) = m_zero
248 else
249 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
250 end if
251 end do
252
253 case (verlet_compute_vel)
254 this%vel(:, 1:this%np) = this%vel(:, 1:this%np) &
255 + m_half * prop%dt * (this%prop_data%prev_acc(:, 1:this%np, 1) + this%prop_data%acc(:, 1:this%np))
256 updated_quantities = [velocity]
257
258 case (beeman_start)
259 if (.not. this%prop_data%initialized) then
260 call this%prop_data%initialize(prop, this%space%dim, this%np)
261 do ip = 1, this%np
262 if (this%fixed(ip)) then
263 this%prop_data%acc(:, ip) = m_zero
264 else
265 this%prop_data%acc(:, ip) = this%tot_force(:, ip) / this%mass(ip)
266 end if
267 this%prop_data%prev_acc(:, ip, 1) = this%prop_data%acc(:, ip)
268 end do
269 end if
270
271 case (beeman_predict_pos)
272 this%pos(:, 1:this%np) = this%pos(:, 1:this%np) + prop%dt * this%vel(:, 1:this%np) + &
273 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))
274
275 if (.not. prop%predictor_corrector) then
276 updated_quantities = [position]
277 end if
278
279 case (beeman_predict_vel)
280 this%vel(:, 1:this%np) = this%vel(:, 1:this%np) + &
281 m_one/6.0_real64 * prop%dt * ( m_two * this%prop_data%acc(:, 1:this%np) + &
282 5.0_real64 * this%prop_data%prev_acc(:, 1:this%np, 1) - this%prop_data%prev_acc(:, 1:this%np, 2))
283 updated_quantities = [velocity]
284
285 case (beeman_correct_pos)
286 this%pos(:, 1:this%np) = this%prop_data%save_pos(:, 1:this%np) &
287 + prop%dt * this%prop_data%save_vel(:, 1:this%np) &
288 + m_one/6.0_real64 * prop%dt**2 * (this%prop_data%acc(:, 1:this%np) &
289 + m_two * this%prop_data%prev_acc(:, 1:this%np, 1))
290 updated_quantities = [position]
291
292 case (beeman_correct_vel)
293 this%vel(:, 1:this%np) = this%prop_data%save_vel(:, 1:this%np) &
294 + m_half * prop%dt * (this%prop_data%acc(:, 1:this%np) + this%prop_data%prev_acc(:, 1:this%np, 1))
295 updated_quantities = [velocity]
296
297 case (expmid_2step_start)
298 if (.not. this%prop_data%initialized) then
299 call this%prop_data%initialize(prop, this%space%dim, this%np)
300 this%prop_data%prev_pos(:, 1:this%np, 1) = this%pos(:, 1:this%np)
301 this%prop_data%prev_vel(:, 1:this%np, 1) = this%vel(:, 1:this%np)
302 end if
303
305 call this%prop_data%end()
306
308 this%pos(:, 1:this%np) = 1.5_real64*this%prop_data%save_pos(:, 1:this%np) &
309 - m_half*this%prop_data%prev_pos(:, 1:this%np, 1)
310 this%vel(:, 1:this%np) = 1.5_real64*this%prop_data%save_vel(:, 1:this%np) &
311 - m_half*this%prop_data%prev_vel(:, 1:this%np, 1)
312 this%prop_data%prev_pos(:, 1:this%np, 1) = this%prop_data%save_pos(:, 1:this%np)
313 this%prop_data%prev_vel(:, 1:this%np, 1) = this%prop_data%save_vel(:, 1:this%np)
314 updated_quantities = [position, velocity]
315
316 case (update_hamiltonian)
317 do ip = 1, this%np
318 if (this%fixed(ip)) then
319 this%prop_data%hamiltonian_elements(:, ip) = m_zero
320 else
321 this%prop_data%hamiltonian_elements(:, ip) = this%tot_force(:, ip) / (this%mass(ip) * this%pos(:, ip) + m_epsilon)
322 end if
323 end do
324
326 safe_allocate(tmp_pos(1:this%space%dim, 1:this%np, 1:2))
327 safe_allocate(tmp_vel(1:this%space%dim, 1:this%np, 1:2))
328 ! apply exponential - at some point this could use the machinery of
329 ! exponential_apply (but this would require a lot of boilerplate code
330 ! like a Hamiltonian class etc)
331 ! prop_data%save_pos/vel contain the state at t - this is the state we want to
332 ! apply the Hamiltonian to
333 tmp_pos(:, 1:this%np, 1) = this%prop_data%save_pos(:, 1:this%np)
334 tmp_vel(:, 1:this%np, 1) = this%prop_data%save_vel(:, 1:this%np)
335 this%pos(:, 1:this%np) = this%prop_data%save_pos(:, 1:this%np)
336 this%vel(:, 1:this%np) = this%prop_data%save_vel(:, 1:this%np)
337 ! compute exponential with Taylor expansion
338 factor = m_one
339 do ii = 1, 4
340 factor = factor * prop%dt / ii
341 do ip = 1, this%np
342 ! apply hamiltonian
343 tmp_pos(:, ip, 2) = tmp_vel(:, ip, 1)
344 tmp_vel(:, ip, 2) = this%prop_data%hamiltonian_elements(:, ip) * tmp_pos(:, ip, 1)
345 ! swap temporary variables
346 tmp_pos(:, ip, 1) = tmp_pos(:, ip, 2)
347 tmp_vel(:, ip, 1) = tmp_vel(:, ip, 2)
348 ! accumulate components of Taylor expansion
349 this%pos(:, ip) = this%pos(:, ip) + factor * tmp_pos(:, ip, 1)
350 this%vel(:, ip) = this%vel(:, ip) + factor * tmp_vel(:, ip, 1)
351 end do
352 end do
353 safe_deallocate_a(tmp_pos)
354 safe_deallocate_a(tmp_vel)
355 updated_quantities = [position, velocity]
356
358 ! only correct for dt/2 if not converged yet
359 if (.not. this%is_tolerance_reached(prop%scf_tol)) then
360 this%pos(:, 1:this%np) = m_half*(this%pos(:, 1:this%np) + this%prop_data%save_pos(:, 1:this%np))
361 this%vel(:, 1:this%np) = m_half*(this%vel(:, 1:this%np) + this%prop_data%save_vel(:, 1:this%np))
362 updated_quantities = [position, velocity]
363 end if
364
365 case default
366 done = .false.
367 end select
368
369 end select
370
371 call profiling_out(trim(this%namespace%get())//":"//trim(operation%id))
374
375 ! ---------------------------------------------------------
376 logical function classical_particles_is_tolerance_reached(this, tol) result(converged)
377 class(classical_particles_t), intent(in) :: this
378 real(real64), intent(in) :: tol
379
380 integer :: ip
381 real(real64) :: change, max_change
382
384
385 ! Here we put the criterion that maximum acceleration change is below the tolerance
386 max_change = m_zero
387 do ip = 1, this%np
388 change = sum((this%prop_data%prev_tot_force(1:this%space%dim, ip) - this%tot_force(1:this%space%dim, ip))**2) / &
389 this%mass(ip)
390 if (change > max_change) then
391 max_change = change
392 end if
393 end do
394 converged = max_change < tol**2
395
396 if (debug%info) then
397 write(message(1), '(a, e13.6, a, e13.6)') "Debug: -- Maximum change in acceleration ", &
398 sqrt(max_change), " and tolerance ", tol
399 call messages_info(1, namespace=this%namespace)
400 end if
401
404
405 ! ---------------------------------------------------------
406 subroutine classical_particles_update_quantity(this, iq)
407 class(classical_particles_t), intent(inout) :: this
408 integer, intent(in) :: iq
409
411
412 ! We are only allowed to update quantities that can be updated on demand
413 assert(this%quantities(iq)%updated_on_demand)
414
415 select case (iq)
416 case default
417 message(1) = "Incompatible quantity."
418 call messages_fatal(1, namespace=this%namespace)
419 end select
420
423
424 ! ---------------------------------------------------------
425 subroutine classical_particles_init_interaction_as_partner(partner, interaction)
426 class(classical_particles_t), intent(in) :: partner
427 class(interaction_surrogate_t), intent(inout) :: interaction
428
430
431 select type (interaction)
432 class default
433 message(1) = "Unsupported interaction."
434 call messages_fatal(1, namespace=partner%namespace)
435 end select
436
439
440 ! ---------------------------------------------------------
441 subroutine classical_particles_copy_quantities_to_interaction(partner, interaction)
442 class(classical_particles_t), intent(inout) :: partner
443 class(interaction_surrogate_t), intent(inout) :: interaction
444
446
447 select type (interaction)
448 class default
449 message(1) = "Unsupported interaction."
450 call messages_fatal(1, namespace=partner%namespace)
451 end select
452
455
456 ! ---------------------------------------------------------
458 class(classical_particles_t), intent(inout) :: this
459
461
462 ! Store previous force, as it is used as SCF criterium
463 select type (prop => this%algo)
464 class is (propagator_t)
465 if (prop%predictor_corrector) then
466 this%prop_data%prev_tot_force(1:this%space%dim, 1:this%np) = this%tot_force(1:this%space%dim, 1:this%np)
467 end if
468 end select
472
473 ! ---------------------------------------------------------
475 class(classical_particles_t), intent(inout) :: this
476
477 type(interaction_iterator_t) :: iter
478
480
481 ! Compute the total force acting on the classical particles
482 this%tot_force(1:this%space%dim, 1:this%np) = m_zero
483 call iter%start(this%interactions)
484 do while (iter%has_next())
485 select type (interaction => iter%get_next())
486 class is (force_interaction_t)
487 this%tot_force(1:this%space%dim, 1:this%np) = this%tot_force(1:this%space%dim, 1:this%np) + &
488 interaction%force(1:this%space%dim, 1:this%np)
489 end select
490 end do
491
494
495 ! ---------------------------------------------------------
496 subroutine classical_particles_output_start(this)
497 class(classical_particles_t), intent(inout) :: this
498
499 integer :: iteration
500 real(real64) :: dt
501
503
504 select type (algo => this%algo)
505 class is (propagator_t)
506 dt = algo%dt
507 end select
508
509 iteration = this%iteration%counter()
510 if (iteration > 0) then
511 ! add one if restarting because the output routine is only called at the end of the timestep
512 iteration = iteration + 1
513 end if
514 ! Create output handle
515 call io_mkdir('td.general', this%namespace)
516 if (mpi_grp_is_root(this%grp)) then
517 call write_iter_init(this%output_handle(output_coordinates), iteration, dt, &
518 trim(io_workpath("td.general/coordinates", this%namespace)))
519 call write_iter_init(this%output_handle(output_energy), iteration, dt, &
520 trim(io_workpath("td.general/energy", this%namespace)))
521 end if
522
523 ! Output info for first iteration
524 if (iteration == 0) then
525 call this%output_write()
526 end if
527
530
531 ! ---------------------------------------------------------
532 subroutine classical_particles_output_finish(this)
533 class(classical_particles_t), intent(inout) :: this
536
537 if (mpi_grp_is_root(this%grp)) then
538 call write_iter_end(this%output_handle(output_coordinates))
539 call write_iter_end(this%output_handle(output_energy))
540 end if
541
544
545 ! ---------------------------------------------------------
546 subroutine classical_particles_output_write(this)
547 class(classical_particles_t), intent(inout) :: this
548
549 integer :: idir, ii, ip
550 character(len=50) :: aux
551 real(real64) :: tmp(this%space%dim)
552
553 if (.not. mpi_grp_is_root(this%grp)) return ! only first node outputs
554
556
557 if (this%iteration%counter() == 0) then
558 ! header
559 call write_iter_clear(this%output_handle(output_coordinates))
560 call write_iter_string(this%output_handle(output_coordinates), &
561 '############################################################################')
562 call write_iter_nl(this%output_handle(output_coordinates))
563 call write_iter_string(this%output_handle(output_coordinates),'# HEADER')
564 call write_iter_nl(this%output_handle(output_coordinates))
565
566 ! first line: column names
567 call write_iter_header_start(this%output_handle(output_coordinates))
568 do ip = 1, this%np
569 do idir = 1, this%space%dim
570 write(aux, '(a2,i3,a1,i3,a1)') 'x(', ip, ',', idir, ')'
571 call write_iter_header(this%output_handle(output_coordinates), aux)
572 end do
573 end do
574 do ip = 1, this%np
575 do idir = 1, this%space%dim
576 write(aux, '(a2,i3,a1,i3,a1)') 'v(', ip, ',', idir, ')'
577 call write_iter_header(this%output_handle(output_coordinates), aux)
578 end do
579 end do
580 do ip = 1, this%np
581 do idir = 1, this%space%dim
582 write(aux, '(a2,i3,a1,i3,a1)') 'f(', ip, ',', idir, ')'
583 call write_iter_header(this%output_handle(output_coordinates), aux)
584 end do
585 end do
586 call write_iter_nl(this%output_handle(output_coordinates))
587
588 ! second line: units
589 call write_iter_string(this%output_handle(output_coordinates), '#[Iter n.]')
590 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%time)) // ']')
591 do ip = 1, this%np
592 do idir = 1, this%space%dim
593 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%length)) // ']')
594 end do
595 end do
596 do ip = 1, this%np
597 do idir = 1, this%space%dim
598 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%velocity)) // ']')
599 end do
600 end do
601 do ip = 1, this%np
602 do idir = 1, this%space%dim
603 call write_iter_header(this%output_handle(output_coordinates), '[' // trim(units_abbrev(units_out%force)) // ']')
604 end do
605 end do
606 call write_iter_nl(this%output_handle(output_coordinates))
607
608 call write_iter_string(this%output_handle(output_coordinates), &
609 '############################################################################')
610 call write_iter_nl(this%output_handle(output_coordinates))
611 end if
612
613 call write_iter_start(this%output_handle(output_coordinates))
614
615 ! Position
616 do ip = 1, this%np
617 tmp(1:this%space%dim) = units_from_atomic(units_out%length, this%pos(1:this%space%dim, ip))
618 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
619 end do
620 ! Velocity
621 do ip = 1, this%np
622 tmp(1:this%space%dim) = units_from_atomic(units_out%velocity, this%vel(1:this%space%dim, ip))
623 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
624 end do
625 ! Force
626 do ip = 1, this%np
627 tmp(1:this%space%dim) = units_from_atomic(units_out%force, this%tot_force(1:this%space%dim, ip))
628 call write_iter_double(this%output_handle(output_coordinates), tmp, this%space%dim)
629 end do
630
631 call write_iter_nl(this%output_handle(output_coordinates))
632
633 ! Energies
634 if (this%iteration%counter() == 0) then
635 ! header
636 call write_iter_clear(this%output_handle(output_energy))
637 call write_iter_string(this%output_handle(output_energy), &
638 '############################################################################')
639 call write_iter_nl(this%output_handle(output_energy))
640 call write_iter_string(this%output_handle(output_energy),'# HEADER')
641 call write_iter_nl(this%output_handle(output_energy))
642
643 ! first line: column names
644 call write_iter_header_start(this%output_handle(output_energy))
645 call write_iter_header(this%output_handle(output_energy), 'Total')
646 call write_iter_header(this%output_handle(output_energy), 'Kinetic')
647 call write_iter_header(this%output_handle(output_energy), 'Potential')
648 call write_iter_header(this%output_handle(output_energy), 'Internal')
649 call write_iter_nl(this%output_handle(output_energy))
650
651 ! second line: units
652 call write_iter_string(this%output_handle(output_energy), '#[Iter n.]')
653 call write_iter_header(this%output_handle(output_energy), '[' // trim(units_abbrev(units_out%time)) // ']')
654 do ii = 1, 4
655 call write_iter_header(this%output_handle(output_energy), '[' // trim(units_abbrev(units_out%energy)) // ']')
656 end do
657 call write_iter_nl(this%output_handle(output_energy))
658
659 call write_iter_string(this%output_handle(output_energy), &
660 '############################################################################')
661 call write_iter_nl(this%output_handle(output_energy))
662 end if
663 call write_iter_start(this%output_handle(output_energy))
664
665 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%total_energy), 1)
666 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%kinetic_energy), 1)
667 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%potential_energy), 1)
668 call write_iter_double(this%output_handle(output_energy), units_from_atomic(units_out%energy, this%internal_energy), 1)
669 call write_iter_nl(this%output_handle(output_energy))
670
673
674 ! ---------------------------------------------------------
676 class(classical_particles_t), intent(inout) :: this
677
678 integer :: restart_file_unit
679
681
682 if (mpi_grp_is_root(this%grp)) then
683 call write_iter_flush(this%output_handle(output_coordinates))
684 call write_iter_flush(this%output_handle(output_energy))
685 end if
686
687 call profiling_in(trim(this%namespace%get())//":"//"RESTART_WRITE")
688
689 call io_mkdir('restart/'//td_dir, this%namespace, parents=.true.)
690 restart_file_unit = io_open('restart/'//td_dir// 'restart_classical_particles', this%namespace, action='write')
691 write(restart_file_unit, *) this%np
692 write(restart_file_unit, *) this%mass(:)
693 write(restart_file_unit, *) this%pos(:,:)
694 write(restart_file_unit, *) this%vel(:,:)
695 write(restart_file_unit, *) this%tot_force(:,:)
696 call io_close(restart_file_unit)
697
698 if (this%iteration%counter() > 0) then
699 ! only initialized after the first time step
700 call this%prop_data%restart_write(this%namespace, this%algo)
701 end if
702
703 message(1) = "Successfully wrote restart data for system "//trim(this%namespace%get())
704 call messages_info(1, namespace=this%namespace)
705
706 call profiling_out(trim(this%namespace%get())//":"//"RESTART_WRITE")
709
710 ! ---------------------------------------------------------
711 logical function classical_particles_restart_read_data(this)
712 class(classical_particles_t), intent(inout) :: this
713
714 integer :: restart_file_unit
715
717 call profiling_in(trim(this%namespace%get())//":"//"RESTART_READ")
718
719 call io_mkdir('restart/'//td_dir, this%namespace, parents=.true.)
720 restart_file_unit = io_open('restart/'//td_dir// 'restart_classical_particles', this%namespace, action='read', die=.false.)
721 if (restart_file_unit > 0) then
722 read(restart_file_unit, *) this%np
723 read(restart_file_unit, *) this%mass(:)
724 read(restart_file_unit, *) this%pos(:,:)
725 read(restart_file_unit, *) this%vel(:,:)
726 read(restart_file_unit, *) this%tot_force(:,:)
727 call io_close(restart_file_unit)
728 call this%prop_data%initialize(this%algo, this%space%dim, this%np)
729 if (this%iteration%counter() > 0) then
730 ! only initialized after the first time step
731 classical_particles_restart_read_data = this%prop_data%restart_read(this%namespace, this%algo)
732 else
734 end if
735 else
736 ! could not open file
738 end if
739
741 message(1) = "Successfully read restart data for system "//trim(this%namespace%get())
742 ! namespace should be added here at some point
743 call messages_info(1)
744 end if
745
746 call profiling_out(trim(this%namespace%get())//":"//"RESTART_READ")
749
750 ! ---------------------------------------------------------
752 class(classical_particles_t), intent(inout) :: this
753
754 integer :: ip
755
757
758 this%kinetic_energy = m_zero
759 do ip = 1, this%np
760 this%kinetic_energy = this%kinetic_energy + m_half * this%mass(ip) * sum(this%vel(:, ip)**2)
761 end do
762
765
766 ! ---------------------------------------------------------
767 function classical_particles_center_of_mass(this, mask, pseudo) result(pos)
768 class(classical_particles_t), intent(in) :: this
769 logical, optional, intent(in) :: mask(:)
770 logical, optional, intent(in) :: pseudo
771 real(real64) :: pos(this%space%dim)
772
773 real(real64) :: mass, total_mass
774 integer :: ip
775
777
778 assert(.not. this%space%is_periodic())
779
780 pos = m_zero
781 total_mass = m_zero
782 mass = m_one
783 do ip = 1, this%np
784 if (present(mask)) then
785 if (.not. mask(ip)) cycle
786 end if
787 if (.not. optional_default(pseudo, .false.)) then
788 mass = this%mass(ip)
789 end if
790 pos = pos + mass*this%pos(:, ip)
791 total_mass = total_mass + mass
792 end do
793 pos = pos/total_mass
794
797
798 ! ---------------------------------------------------------
799 function classical_particles_center_of_mass_vel(this) result(vel)
800 class(classical_particles_t), intent(in) :: this
801 real(real64) :: vel(this%space%dim)
802
803 real(real64) :: mass, total_mass
804 integer :: ip
805
807
808 vel = m_zero
809 total_mass = m_zero
810 do ip = 1, this%np
811 mass = this%mass(ip)
812 total_mass = total_mass + mass
813 vel = vel + mass*this%vel(:, ip)
814 end do
815 vel = vel/total_mass
816
819
820 ! ---------------------------------------------------------
821 function classical_particles_center(this) result(pos)
822 class(classical_particles_t), intent(in) :: this
823 real(real64) :: pos(this%space%dim)
824
825 real(real64) :: xmin(this%space%dim), xmax(this%space%dim)
826 integer :: ip, idir
827
829
830 xmin = m_huge
831 xmax = -m_huge
832 do ip = 1, this%np
833 do idir = 1, this%space%dim
834 if (this%pos(idir, ip) > xmax(idir)) xmax(idir) = this%pos(idir, ip)
835 if (this%pos(idir, ip) < xmin(idir)) xmin(idir) = this%pos(idir, ip)
836 end do
837 end do
838
839 pos = (xmax + xmin)/m_two
840
842 end function classical_particles_center
843
844 ! ---------------------------------------------------------
845 subroutine classical_particles_axis_large(this, x, x2)
846 class(classical_particles_t), intent(in) :: this
847 real(real64), intent(out) :: x(this%space%dim)
848 real(real64), intent(out) :: x2(this%space%dim)
849
850 integer :: ip, jp
851 real(real64) :: rmax, r, r2
852
854
855 assert(.not. this%space%is_periodic())
856
857 ! first get the further apart atoms
858 rmax = -m_huge
859 do ip = 1, this%np
860 do jp = 1, this%np/2 + 1
861 r = norm2(this%pos(:, ip) - this%pos(:, jp))
862 if (r > rmax) then
863 rmax = r
864 x = this%pos(:, ip) - this%pos(:, jp)
865 end if
866 end do
867 end do
868 x = x /norm2(x)
869
870 ! now let us find out what is the second most important axis
871 rmax = -m_huge
872 do ip = 1, this%np
873 r2 = sum(x * this%pos(:, ip))
874 r = norm2(this%pos(:, ip) - r2*x)
875 if (r > rmax) then
876 rmax = r
877 x2 = this%pos(:, ip) - r2*x
878 end if
879 end do
880
882 end subroutine classical_particles_axis_large
883
884 ! ---------------------------------------------------------
887 subroutine classical_particles_axis_inertia(this, x, x2, pseudo)
888 class(classical_particles_t), intent(in) :: this
889 real(real64), intent(out) :: x(this%space%dim)
890 real(real64), intent(out) :: x2(this%space%dim)
891 logical, intent(in) :: pseudo
893 real(real64) :: mass, tinertia(this%space%dim, this%space%dim), eigenvalues(this%space%dim)
894 integer :: ii, jj, ip
895 type(unit_t) :: unit
896
898
899 assert(.not. this%space%is_periodic())
900
901 ! first calculate the inertia tensor
902 tinertia = m_zero
903 mass = m_one
904 do ip = 1, this%np
905 if (.not. pseudo) mass = this%mass(ip)
906 do ii = 1, this%space%dim
907 tinertia(ii, :) = tinertia(ii, :) - mass*this%pos(ii, ip)*this%pos(:, ip)
908 tinertia(ii, ii) = tinertia(ii, ii) + mass*sum(this%pos(:, ip)**2)
909 end do
910 end do
911
912 unit = units_out%length**2
913 ! note: we always use amu for atomic masses, so no unit conversion to/from atomic is needed.
914 if (pseudo) then
915 write(message(1),'(a)') 'Moment of pseudo-inertia tensor [' // trim(units_abbrev(unit)) // ']'
916 else
917 write(message(1),'(a)') 'Moment of inertia tensor [amu*' // trim(units_abbrev(unit)) // ']'
918 end if
919 call messages_info(1, namespace=this%namespace)
920 call output_tensor(tinertia, this%space%dim, unit, write_average = .true., namespace=this%namespace)
921
922 call lalg_eigensolve(this%space%dim, tinertia, eigenvalues)
923
924 write(message(1),'(a,6f25.6)') 'Eigenvalues: ', units_from_atomic(unit, eigenvalues)
925 call messages_info(1, namespace=this%namespace)
926
927 ! make a choice to fix the sign of the axis.
928 do ii = 1, 2
929 jj = maxloc(abs(tinertia(:,ii)), dim = 1)
930 if (tinertia(jj,ii) < m_zero) tinertia(:,ii) = -tinertia(:,ii)
931 end do
932 x = tinertia(:,1)
933 x2 = tinertia(:,2)
934
937
938 ! ---------------------------------------------------------
939 subroutine classical_particles_end(this)
940 class(classical_particles_t), intent(inout) :: this
941
943
944 safe_deallocate_a(this%mass)
945 safe_deallocate_a(this%pos)
946 safe_deallocate_a(this%vel)
947 safe_deallocate_a(this%tot_force)
948 safe_deallocate_a(this%fixed)
949 safe_deallocate_a(this%lj_epsilon)
950 safe_deallocate_a(this%lj_sigma)
951
952 call system_end(this)
953
955 end subroutine classical_particles_end
956
958
959!! Local Variables:
960!! mode: f90
961!! coding: utf-8
962!! 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)
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, public classical_particles_update_quantity(this, iq)
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)
type(debug_t), save, public debug
Definition: debug.F90:154
real(real64), parameter, public m_two
Definition: global.F90:189
real(real64), parameter, public m_huge
Definition: global.F90:205
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_four
Definition: global.F90:191
real(real64), parameter, public m_epsilon
Definition: global.F90:203
character(len= *), parameter, public td_dir
Definition: global.F90:250
real(real64), parameter, public m_half
Definition: global.F90:193
real(real64), parameter, public m_one
Definition: global.F90:188
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:468
character(len=max_path_len) function, public io_workpath(path, namespace)
Definition: io.F90:313
subroutine, public io_mkdir(fname, namespace, parents)
Definition: io.F90:354
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:395
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
Definition: messages.F90:624
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:420
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
Definition: mpi.F90:430
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:167
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:137
integer, parameter, public velocity
Definition: quantity.F90:146
integer, parameter, public position
Definition: quantity.F90:146
integer, parameter, public mass
Definition: quantity.F90:146
This module implements the abstract system type.
Definition: system.F90:118
subroutine, public system_end(this)
Definition: system.F90:1116
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
Abstract class for systems.
Definition: system.F90:172
int true(void)