RMPCDMD
mpcd.f90
Go to the documentation of this file.
1 ! This file is part of RMPCDMD
2 ! Copyright (c) 2015-2017 Pierre de Buyl and contributors
3 ! License: BSD 3-clause (see file LICENSE)
4 
17 
18 module mpcd
19  use common
20  use particle_system
21  use cell_system
22  use threefry_module
23  implicit none
24 
25  private
26 
28  public :: wall_mpcd_step
29  public :: mpcd_stream_periodic
31  public :: compute_rho, compute_vx
32  public :: bulk_reaction
35  public :: rand_sphere
36  public :: rescale_cells
37 
38 contains
39 
43  function rand_sphere(state) result(n)
44  type(threefry_rng_t), intent(inout) :: state
45  double precision :: n(3)
46 
47  logical :: s_lt_one
48  double precision :: s, alpha
49 
50  s_lt_one = .false.
51  do while (.not. s_lt_one)
52  n(1) = threefry_double(state)
53  n(2) = threefry_double(state)
54  n(1:2) = 2*n(1:2) - 1
55  s = n(1)**2 + n(2)**2
56  if ( s<1.d0 ) s_lt_one = .true.
57  end do
58  alpha = 2.d0 * sqrt(1.d0 - s)
59  n(1) = n(1)*alpha
60  n(2) = n(2)*alpha
61  n(3) = 1.d0 - 2.d0*s
62  end function rand_sphere
63 
71  subroutine simple_mpcd_step(particles, cells, state, alpha, thermostat, T, hydro)
72  use omp_lib
73  class(particle_system_t), intent(inout) :: particles
74  class(cell_system_t), intent(in) :: cells
75  type(threefry_rng_t), intent(inout) :: state(:)
76  double precision, intent(in), optional :: alpha
77  logical, intent(in), optional :: thermostat
78  double precision, intent(in), optional :: T
79  logical, intent(in), optional :: hydro
80 
81  integer :: i, start, n
82  integer :: cell_idx, n_effective
83  double precision :: local_v(3), omega(3,3), vec(3)
84  double precision :: virtual_v(3)
85  double precision :: sin_alpha, cos_alpha
86  double precision :: t_factor
87  integer :: thread_id
88  logical :: do_thermostat, do_hydro
89 
90  if (present(thermostat)) then
91  do_thermostat = thermostat
92  if (present(t)) then
93  t_factor = sqrt(t)
94  else
95  stop 'no temperature given for thermostat in simple_mpcd_step'
96  end if
97  else
98  do_thermostat = .false.
99  end if
100 
101  if (present(hydro)) then
102  do_hydro = hydro
103  else
104  do_hydro = .true.
105  end if
106 
107  if ((.not. do_hydro) .and. (.not. thermostat)) then
108  stop .not.'requiring do_hydro without thermostat in simple_mpcd_step'
109  end if
110 
111  if (present(alpha)) then
112  sin_alpha = sin(alpha)
113  cos_alpha = cos(alpha)
114  else
115  sin_alpha = 1
116  cos_alpha = 0
117  end if
118 
119  call particles%time_step%tic()
120  !$omp parallel private(thread_id, t_factor)
121  thread_id = omp_get_thread_num() + 1
122  if (present(t)) then
123  t_factor = sqrt(t)
124  end if
125 
126  !$omp do private(start, n, local_v, i, vec, omega, n_effective, virtual_v)
127  do cell_idx = 1, cells% N
128  if (cells% cell_count(cell_idx) <= 1) cycle
129 
130  start = cells% cell_start(cell_idx)
131  n = cells% cell_count(cell_idx)
132 
133  n_effective = 0
134  local_v = 0
135  do i = start, start + n - 1
136  if (particles%species(i) > 0) then
137  local_v = local_v + particles% vel(:, i)
138  n_effective = n_effective + 1
139  end if
140  end do
141  if (n_effective <= 1) cycle
142  local_v = local_v / n_effective
143 
144  if (do_thermostat) then
145  virtual_v = 0
146  do i = start, start + n - 1
147  if (particles%species(i) <= 0) cycle
148  vec(1) = threefry_normal(state(thread_id))*t_factor
149  vec(2) = threefry_normal(state(thread_id))*t_factor
150  vec(3) = threefry_normal(state(thread_id))*t_factor
151  particles% vel(:, i) = vec
152  virtual_v = virtual_v + particles% vel(:, i)
153  end do
154  virtual_v = local_v - virtual_v / n_effective
155  if (do_hydro) then
156  do i = start, start + n - 1
157  if (particles%species(i) <= 0) cycle
158  particles% vel(:, i) = particles% vel(:, i) + virtual_v
159  end do
160  end if
161  else
162  vec = rand_sphere(state(thread_id))
163  omega = &
164  reshape( (/ &
165  cos_alpha+vec(1)**2*(1-cos_alpha), vec(1)*vec(2)*(1-cos_alpha) - vec(3)*sin_alpha, &
166  vec(1)*vec(3)*(1-cos_alpha) + vec(2)*sin_alpha ,&
167  vec(2)*vec(1)*(1-cos_alpha) + vec(3)*sin_alpha , cos_alpha+vec(2)**2*(1-cos_alpha) ,&
168  vec(2)*vec(3)*(1-cos_alpha) - vec(1)*sin_alpha,&
169  vec(3)*vec(1)*(1-cos_alpha) - vec(2)*sin_alpha, vec(3)*vec(2)*(1-cos_alpha) + vec(1)*sin_alpha,&
170  cos_alpha + vec(3)**2*(1-cos_alpha) &
171  /), (/3, 3/))
172 
173  do i = start, start + n - 1
174  particles% vel(:, i) = local_v + matmul(omega, (particles% vel(:, i)-local_v))
175  end do
176  end if
177 
178  end do
179  !$omp end do
180  !$omp end parallel
181  call particles%time_step%tac()
182 
183  end subroutine simple_mpcd_step
184 
185 
193  subroutine wall_mpcd_step(particles, cells, state, wall_temperature, wall_v, wall_n, &
194  thermostat, bulk_temperature, alpha, keep_cell_v)
196  use omp_lib
197  class(particle_system_t), intent(inout) :: particles
198  class(cell_system_t), intent(in) :: cells
199  type(threefry_rng_t), intent(inout) :: state(:)
200  double precision, optional, intent(in) :: wall_temperature(2)
201  double precision, optional, intent(in) :: wall_v(3,2)
202  integer, optional, intent(in) :: wall_n(2)
203  logical, intent(in), optional :: thermostat
204  double precision, intent(in), optional :: bulk_temperature
205  double precision, intent(in), optional :: alpha
206  logical, intent(in), optional :: keep_cell_v
207 
208  integer :: i, start, n
209  integer :: cell_idx
210  double precision :: local_v(3), omega(3,3), vec(3)
211  double precision :: sin_alpha, cos_alpha
212  integer :: n_virtual
213  integer :: cell(3)
214  integer :: wall_idx
215  double precision :: virtual_v(3), t_factor
216  logical :: all_present, all_absent
217  logical :: do_thermostat, do_cell_v
218  integer :: thread_id
219 
220  all_present = present(wall_temperature) .and. present(wall_v) .and. present(wall_n)
221  all_absent = .not. present(wall_temperature) .and. .not. present(wall_v) .and. .not. present(wall_n)
222  if ( .not. (all_present .or. all_absent) ) &
223  error stop 'wall parameters must be all present or all absent in wall_mpcd_step'
224 
225  if (present(thermostat)) then
226  do_thermostat = thermostat
227  if (do_thermostat) then
228  if (present(bulk_temperature)) then
229  t_factor = sqrt(bulk_temperature)
230  else
231  error stop 'thermostat requested but no temperature given in wall_mpcd_step'
232  end if
233  if (present(keep_cell_v)) then
234  do_cell_v = keep_cell_v
235  else
236  do_cell_v = .true.
237  end if
238  end if
239  else
240  do_thermostat = .false.
241  end if
242 
243  if (present(keep_cell_v)) then
244  if (.not. present(thermostat)) then
245  stop 'requiring keep_cell_v and not setting thermostat in wall_mpcd_step'
246  end if
247  if ((.not. keep_cell_v) .and. (.not. do_thermostat)) then
248  stop .not..not.'requiring keep_cell_v and thermostat in wall_mpcd_step'
249  end if
250  end if
251 
252  if (present(alpha)) then
253  sin_alpha = sin(alpha)
254  cos_alpha = cos(alpha)
255  else
256  sin_alpha = 1
257  cos_alpha = 0
258  end if
259 
260  call particles%time_step%tic()
261  !$omp parallel private(thread_id)
262  thread_id = omp_get_thread_num() + 1
263  !$omp do private(cell_idx, start, n, n_virtual, virtual_v, cell, wall_idx, local_v, i, vec, omega)
264  do cell_idx = 1, cells% N
265  if (cells% cell_count(cell_idx) <= 1) cycle
266 
267  start = cells% cell_start(cell_idx)
268  n = cells% cell_count(cell_idx)
269  n_virtual = 0
270 
271  ! Find whether we are in a wall cell
272  cell = compact_h_to_p(cell_idx - 1, cells% M) + 1
273  if (all_present .and. (cell(3) == 1)) then
274  wall_idx = 1
275  else if (all_present .and. (cell(3) == cells% L(3))) then
276  wall_idx = 2
277  else
278  wall_idx = -1
279  end if
280 
281  if (wall_idx > 0) then
282  if (n < wall_n(wall_idx)) then
283  n_virtual = wall_n(wall_idx) - n
284  virtual_v(1) = threefry_normal(state(thread_id))
285  virtual_v(2) = threefry_normal(state(thread_id))
286  virtual_v(3) = threefry_normal(state(thread_id))
287  virtual_v = virtual_v * sqrt(n_virtual*wall_temperature(wall_idx))
288  end if
289  end if
290 
291  local_v = 0
292  do i = start, start + n - 1
293  local_v = local_v + particles% vel(:, i)
294  end do
295  if (n_virtual > 0) then
296  local_v = (local_v + virtual_v) / wall_n(wall_idx)
297  else
298  local_v = local_v / n
299  end if
300 
301  if (do_thermostat) then
302  virtual_v = 0
303  do i = start, start + n - 1
304  vec(1) = threefry_normal(state(thread_id))
305  vec(2) = threefry_normal(state(thread_id))
306  vec(3) = threefry_normal(state(thread_id))
307  particles% vel(:, i) = vec*t_factor
308  virtual_v = virtual_v + particles% vel(:, i)
309  end do
310  if (do_cell_v) then
311  virtual_v = local_v - virtual_v / dble(n)
312  do i = start, start + n - 1
313  particles% vel(:, i) = particles% vel(:, i) + virtual_v
314  end do
315  end if
316  else
317  vec = rand_sphere(state(thread_id))
318  omega = &
319  reshape( (/ &
320  cos_alpha+vec(1)**2*(1-cos_alpha), vec(1)*vec(2)*(1-cos_alpha) - vec(3)*sin_alpha, &
321  vec(1)*vec(3)*(1-cos_alpha) + vec(2)*sin_alpha ,&
322  vec(2)*vec(1)*(1-cos_alpha) + vec(3)*sin_alpha , cos_alpha+vec(2)**2*(1-cos_alpha) ,&
323  vec(2)*vec(3)*(1-cos_alpha) - vec(1)*sin_alpha,&
324  vec(3)*vec(1)*(1-cos_alpha) - vec(2)*sin_alpha, vec(3)*vec(2)*(1-cos_alpha) + vec(1)*sin_alpha,&
325  cos_alpha + vec(3)**2*(1-cos_alpha) &
326  /), (/3, 3/))
327 
328  do i = start, start + n - 1
329  particles%vel(:,i) = local_v + matmul(omega, particles%vel(:,i)-local_v)
330  end do
331  end if
332  end do
333  !$omp end do
334  !$omp end parallel
335  call particles%time_step%tac()
336 
337  end subroutine wall_mpcd_step
338 
342  function compute_temperature(particles, cells, tz) result(te)
344  type(particle_system_t), intent(inout) :: particles
345  type(cell_system_t), intent(in) :: cells
346  type(profile_t), intent(inout), optional :: tz
347 
348  double precision :: te
349 
350  integer :: i, start, n, n_effective
351  integer :: cell_idx, count
352  integer :: cell(3)
353  double precision :: local_v(3), local_kin
354  logical :: do_tz
355 
356  if (present(tz)) then
357  do_tz = .true.
358  if (.not. (allocated(tz% data) .and. allocated(tz% count))) error stop 'profile_t: data not allocated'
359  else
360  do_tz = .false.
361  end if
362 
363  call particles%time_ct%tic()
364  cell_idx = 1
365  count = 0
366  te = 0
367  !$omp parallel do private(start, n, local_v, local_kin, i, cell, n_effective) &
368  !$omp& reduction(+:count) reduction(+:te)
369  do cell_idx = 1, cells% N
370  if (cells% cell_count(cell_idx) <= 1) cycle
371 
372  start = cells% cell_start(cell_idx)
373  n = cells% cell_count(cell_idx)
374  count = count + 1
375 
376  n_effective = 0
377  local_v = 0
378  local_kin = 0
379  do i = start, start + n - 1
380  if (particles%species(i) > 0) then
381  local_v = local_v + particles% vel(:, i)
382  n_effective = n_effective + 1
383  end if
384  end do
385 
386  if (n_effective <= 1) cycle
387 
388  local_v = local_v / n
389  do i = start, start + n - 1
390  if (particles%species(i) > 0) then
391  local_kin = local_kin + sum((particles% vel(:, i)-local_v)**2)
392  end if
393  end do
394  local_kin = local_kin / dble(3*(n_effective-1))
395  te = te + local_kin
396 
397  if (do_tz) then
398  cell = compact_h_to_p(cell_idx - 1, cells% M)
399  call tz% bin(cells% origin(3) + (cell(3)+0.5d0)*cells% a, local_kin)
400  end if
401 
402  end do
403  call particles%time_ct%tac()
404 
405  te = te / count
406 
407  end function compute_temperature
408 
410  subroutine compute_rho(particles, rhoz)
411  type(particle_system_t), intent(in) :: particles
412  type(histogram_t), intent(inout) :: rhoz
413 
414  integer :: i
415 
416  if (.not. allocated(rhoz% data)) error stop 'histogram_t: data not allocated'
417 
418  do i = 1, particles% Nmax
419  call rhoz% bin(particles% pos(3, i))
420  end do
421 
422  end subroutine compute_rho
423 
425  subroutine compute_vx(particles, vx)
426  type(particle_system_t), intent(in) :: particles
427  type(profile_t), intent(inout) :: vx
428 
429  integer :: i
430 
431  if (.not. allocated(vx% data)) error stop 'histogram_t: data not allocated'
432 
433  do i = 1, particles% Nmax
434  call vx% bin(particles% pos(3, i), particles% vel(1, i))
435  end do
436 
437  end subroutine compute_vx
438 
440  subroutine mpcd_stream_periodic(particles, cells, dt)
441  type(particle_system_t), intent(inout) :: particles
442  type(cell_system_t), intent(in) :: cells
443  double precision, intent(in) :: dt
444 
445  integer :: i, jump(3)
446  double precision :: edges(3)
447 
448  edges = cells% edges
449 
450  do i = 1, particles% Nmax
451  particles% pos(:,i) = particles% pos(:,i) + particles% vel(:,i)*dt
452  jump = floor(particles% pos(:,i) / edges)
453  particles% image(:,i) = particles% image(:,i) + jump
454  particles% pos(:,i) = particles% pos(:,i) - jump * edges
455  end do
456 
457  end subroutine mpcd_stream_periodic
458 
464  subroutine mpcd_stream_xforce_yzwall(particles, cells, dt, g)
465  type(particle_system_t), intent(inout) :: particles
466  type(cell_system_t), intent(in) :: cells
467  double precision, intent(in) :: dt
468  double precision, intent(in):: g
469 
470  integer :: i, bc(3), im(3)
471  double precision :: delta, L(3), gvec(3)
472  double precision, dimension(3) :: old_pos, old_vel
473  double precision, dimension(3) :: new_pos, new_vel
474  double precision :: t_c, t_b, t_ab
475  logical :: y_out, z_out
476 
477  l = cells%edges
478  bc = cells%bc
479  gvec = 0
480  gvec(1) = g
481 
482  call particles%time_stream%tic()
483  !$omp parallel do private(old_pos, old_vel, new_pos, new_vel, im, y_out, z_out)
484  do i = 1, particles% Nmax
485  old_pos = particles% pos(:,i)
486  old_vel = particles% vel(:,i)
487  new_pos = old_pos + old_vel*dt + gvec*dt**2/2
488  new_vel = old_vel
489  im = 0
490  y_out = ((new_pos(2)<0) .or. (new_pos(2)>l(2)))
491  z_out = ((new_pos(3)<0) .or. (new_pos(3)>l(3)))
492 
493  if ( &
494  ((bc(2)==periodic_bc) .and. z_out ) .or. &
495  ((bc(2)/=periodic_bc) .and. (y_out .or. z_out)) &
496  ) then
497  call yzwall_collision(old_pos, old_vel, new_pos, new_vel, im, l, dt, bc, g)
498  particles%flags(i) = ibset(particles%flags(i), wall_bit)
499  particles%vel(:,i) = new_vel
500  end if
501 
502  particles%pos(:,i) = new_pos
503  end do
504  call particles%time_stream%tac()
505 
506  end subroutine mpcd_stream_xforce_yzwall
507 
511  subroutine yzwall_collision(x0, v0, x, v, im, L, t, bc, g)
512  double precision, dimension(3), intent(inout) :: x0, v0, x, v
513  integer, dimension(3), intent(inout) :: im
514  double precision, intent(in) :: L(3), t
515  integer, intent(in) :: bc(3)
516  double precision, intent(in), optional :: g
517 
518  double precision, dimension(3) :: gvec
519  double precision :: t_collision, t_remainder, tt
520  integer :: i, coll_dim, actual_bc
521  integer :: jump(3)
522 
523  gvec = 0
524  if (present(g)) gvec(1) = g
525  im = 0
526 
527  coll_dim = 0
528  t_collision = huge(t_collision)
529  do i = 2, 3
530  if (v0(i) > 0) then
531  tt = (l(i)-x0(i))/v0(i)
532  if ( (tt>0) .and. (tt<t_collision) ) then
533  t_collision = tt
534  coll_dim = i
535  end if
536  else
537  tt = -x0(i)/v0(i)
538  if ( (tt>0) .and. (tt<t_collision) ) then
539  t_collision = tt
540  coll_dim = i
541  end if
542  end if
543  end do
544 
545  if (coll_dim==0) return
546 
547  t_remainder = t - t_collision
548 
549  x = x0 + v0*t_collision + gvec*t_collision**2 / 2
550  v = v0 + gvec*t_collision
551 
552  if (bc(coll_dim) == bounce_back_bc) then
553  v = -v
554  else if (bc(coll_dim) == specular_bc) then
555  v(coll_dim) = -v(coll_dim)
556  else if (bc(coll_dim) == periodic_bc) then
557  jump = floor(x/l)
558  x(coll_dim) = x(coll_dim) - jump(coll_dim)*l(coll_dim)
559  im(coll_dim) = im(coll_dim) + jump(coll_dim)
560  end if
561 
562  wall_loop: do while (.true.)
563  coll_dim = change_23(coll_dim)
564 
565  if (v(coll_dim)>0) then
566  t_collision = (l(coll_dim)-x(coll_dim))/v(coll_dim)
567  else
568  t_collision = -x(coll_dim)/v(coll_dim)
569  end if
570  if ((t_collision < 0) .or. (t_collision > t_remainder)) exit wall_loop
571  x = x + v*t_collision + gvec*t_collision**2 / 2
572  v = v + gvec*t_collision
573  t_remainder = t_remainder - t_collision
574  if (bc(coll_dim) == bounce_back_bc) then
575  v = -v
576  else if (bc(coll_dim) == specular_bc) then
577  v(coll_dim) = -v(coll_dim)
578  else if (bc(coll_dim) == periodic_bc) then
579  jump = floor(x/l)
580  x(coll_dim) = x(coll_dim) - jump(coll_dim)*l(coll_dim)
581  im(coll_dim) = im(coll_dim) + jump(coll_dim)
582  end if
583  end do wall_loop
584 
585  t_collision = t_remainder
586  x = x + v*t_collision + gvec*t_collision**2 / 2 + im*l
587  v = v + gvec*t_collision
588 
589  end subroutine yzwall_collision
590 
594  pure function change_23(i) result(r)
595  integer, intent(in) :: i
596  integer :: r
597  if (i==2) then
598  r = 3
599  else if (i==3) then
600  r = 2
601  end if
602  end function change_23
603 
605  subroutine bulk_reaction(p, c, from, to, rate, tau, state)
606  use omp_lib
607  type(particle_system_t), intent(inout) :: p
608  type(cell_system_t), intent(inout) :: c
609  integer, intent(in) :: from, to
610  double precision, intent(in) :: rate, tau
611  type(threefry_rng_t), intent(inout) :: state(:)
612 
613  integer :: cell_idx, start, n, i, pick, s
614  double precision :: local_rate
615  integer :: thread_id
616 
617  !$omp parallel private(thread_id)
618  thread_id = omp_get_thread_num() + 1
619  !$omp do private(cell_idx, start, n, local_rate, i, pick, s)
620  do cell_idx = 1, c%N
621  if ( (c%cell_count(cell_idx) <= 1) .or. .not. c%is_reac(cell_idx) ) cycle
622 
623  start = c%cell_start(cell_idx)
624  n = c%cell_count(cell_idx)
625 
626  local_rate = 0
627  do i = start, start + n - 1
628  s = p%species(i)
629  if (s==from) then
630  local_rate = local_rate + 1
631  pick = i
632  end if
633  end do
634 
635  local_rate = local_rate*rate
636  if (threefry_double(state(thread_id)) < (1 - exp(-local_rate*tau))) then
637  p%species(pick) = to
638  end if
639  end do
640  !$omp end do
641  !$omp end parallel
642 
643  end subroutine bulk_reaction
644 
646  subroutine bulk_reaction_endothermic(p, c, from, to, rate, tau, state, delta_u)
647  use omp_lib
648  type(particle_system_t), intent(inout) :: p
649  type(cell_system_t), intent(inout) :: c
650  integer, intent(in) :: from, to
651  double precision, intent(in) :: rate, tau, delta_u
652  type(threefry_rng_t), intent(inout) :: state(:)
653 
654  integer :: cell_idx, start, n, i, pick, s
655  double precision :: local_rate
656  integer :: thread_id
657  double precision :: kin, com_v(3), factor
658 
659  !$omp parallel private(thread_id)
660  thread_id = omp_get_thread_num() + 1
661  !$omp do private(cell_idx, start, n, local_rate, i, pick, s, kin, com_v, factor)
662  cell_loop: do cell_idx = 1, c%N
663  if ( (c%cell_count(cell_idx) <= 1) .or. .not. c%is_reac(cell_idx) ) cycle
664 
665  start = c%cell_start(cell_idx)
666  n = c%cell_count(cell_idx)
667 
668  local_rate = 0
669  com_v = 0
670  do i = start, start + n - 1
671  s = p%species(i)
672  if (s==from) then
673  local_rate = local_rate + 1
674  pick = i
675  end if
676  com_v = com_v + p%vel(:,i)
677  end do
678  com_v = com_v / n
679 
680  kin = 0
681  do i = start, start + n - 1
682  kin = kin + sum((p%vel(:,i)-com_v)**2)
683  end do
684  kin = kin / 2
685 
686  if ( (local_rate > 0) .and. (kin > delta_u) ) then
687  local_rate = local_rate*rate
688  if (threefry_double(state(thread_id)) < (1 - exp(-local_rate*tau))) then
689  p%species(pick) = to
690  factor = sqrt((kin - delta_u)/kin)
691  do i = start, start + n - 1
692  p%vel(:,i) = com_v + factor*(p%vel(:,i)-com_v)
693  end do
694  end if
695  end if
696  end do cell_loop
697  !$omp end do
698  !$omp end parallel
699 
700  end subroutine bulk_reaction_endothermic
701 
702  subroutine mpcd_stream_nogravity_zwall(particles, cells, dt)
703  type(particle_system_t), intent(inout) :: particles
704  type(cell_system_t), intent(in) :: cells
705  double precision, intent(in) :: dt
706 
707  integer :: i
708  double precision :: delta, L(3)
709  double precision, dimension(3) :: old_pos, old_vel
710  double precision, dimension(3) :: new_pos, new_vel
711  double precision :: t_c, t_b, t_ab
712  logical :: bounce
713 
714  l = cells%edges
715 
716  call particles%time_stream%tic()
717  !$omp parallel do private(i, old_pos, old_vel, new_pos, new_vel, bounce, t_c)
718  particles_loop: do i = 1, particles% Nmax
719  old_pos = particles%pos(:,i)
720  old_vel = particles%vel(:,i)
721  new_pos = old_pos + old_vel*dt + particles%force(:,i)*dt**2 / 2
722  bounce = .false.
723 
724  if (new_pos(3)<0) then
725  t_c = -old_pos(3)/old_vel(3)
726  bounce = .true.
727  else if (new_pos(3)>l(3)) then
728  t_c = (l(3)-old_pos(3))/old_vel(3)
729  bounce = .true.
730  else
731  new_vel = old_vel
732  end if
733 
734  if (bounce) then
735  new_pos = new_pos - old_vel*2*(dt-t_c)
736  new_vel = - old_vel
737  particles%flags(i) = ibset(particles%flags(i), wall_bit)
738  end if
739 
740  particles%pos(:,i) = new_pos
741  particles%vel(:,i) = new_vel
742  end do particles_loop
743  call particles%time_stream%tac()
744 
745  end subroutine mpcd_stream_nogravity_zwall
746 
748  subroutine rescale_cells(particles, cells, state, T)
749  use omp_lib
750  class(particle_system_t), intent(inout) :: particles
751  class(cell_system_t), intent(in) :: cells
752  type(threefry_rng_t), intent(inout) :: state(:)
753  double precision, intent(in) :: T
754 
755  integer :: i, start, n
756  integer :: cell_idx, n_effective
757  double precision :: local_v(3)
758  double precision :: t_factor, local_kin
759  integer :: thread_id
760 
761 
762  call particles%time_step%tic()
763  !$omp parallel private(thread_id)
764  thread_id = omp_get_thread_num() + 1
765  !$omp do private(start, n, local_v, i, n_effective, t_factor)
766  do cell_idx = 1, cells% N
767  if (cells% cell_count(cell_idx) <= 1) cycle
768 
769  start = cells% cell_start(cell_idx)
770  n = cells% cell_count(cell_idx)
771 
772  n_effective = 0
773  local_v = 0
774  do i = start, start + n - 1
775  if (particles%species(i) > 0) then
776  local_v = local_v + particles% vel(:, i)
777  n_effective = n_effective + 1
778  end if
779  end do
780  if (n_effective <= 1) cycle
781  local_v = local_v / n_effective
782 
783  local_kin = 0
784  do i = start, start + n - 1
785  if (particles%species(i) > 0) then
786  local_kin = local_kin + sum((particles%vel(:,i) - local_v)**2)
787  end if
788  end do
789  t_factor = sqrt(3*(n_effective-1)*t/local_kin)
790 
791  do i = start, start + n - 1
792  if (particles%species(i) > 0) &
793  particles% vel(:, i) = local_v + t_factor*(particles% vel(:, i)-local_v)
794  end do
795 
796  end do
797  !$omp end do
798  !$omp end parallel
799  call particles%time_step%tac()
800 
801  end subroutine rescale_cells
802 
803 
804 end module mpcd
pure integer function, dimension(dim), public compact_h_to_p(h, m)
Definition: hilbert.f90:274
subroutine, public wall_mpcd_step(particles, cells, state, wall_temperature, wall_v, wall_n, thermostat, bulk_temperature, alpha, keep_cell_v)
Collisions in partially filled cells at the walls use the rule of Lamura et al (2001) ...
Definition: mpcd.f90:195
subroutine, public bulk_reaction_endothermic(p, c, from, to, rate, tau, state, delta_u)
Apply a endothermic bulk unimolecular RMPCD reaction.
Definition: mpcd.f90:647
subroutine, public compute_rho(particles, rhoz)
Compute density profile along z.
Definition: mpcd.f90:411
integer, parameter, public periodic_bc
Definition: cell_system.f90:43
double precision function, public compute_temperature(particles, cells, tz)
Compute the temperature of a mpcd solvent.
Definition: mpcd.f90:343
subroutine, public mpcd_stream_periodic(particles, cells, dt)
Stream MPCD particles in a periodic system.
Definition: mpcd.f90:441
Data for particles.
subroutine, public simple_mpcd_step(particles, cells, state, alpha, thermostat, T, hydro)
Perform a collision.
Definition: mpcd.f90:72
subroutine, public bulk_reaction(p, c, from, to, rate, tau, state)
Apply a bulk unimolecular RMPCD reaction.
Definition: mpcd.f90:606
integer, parameter, public specular_bc
Definition: cell_system.f90:45
subroutine, public compute_vx(particles, vx)
Compute x-velocity profile along z.
Definition: mpcd.f90:426
integer, parameter, public wall_bit
Definition: common.f90:51
Compute compact Hilbert indices.
Definition: hilbert.f90:9
Utility routines.
Definition: common.f90:12
Container for a profile, e.g. v(x)
Definition: common.f90:66
subroutine, public mpcd_stream_nogravity_zwall(particles, cells, dt)
Definition: mpcd.f90:703
double precision function, dimension(3), public rand_sphere(state)
Return random point on the surface of a sphere.
Definition: mpcd.f90:44
Routines to perform MPCD dynamics.
Definition: mpcd.f90:18
subroutine, public rescale_cells(particles, cells, state, T)
Rescale the kinetic energy of all cells.
Definition: mpcd.f90:749
subroutine yzwall_collision(x0, v0, x, v, im, L, t, bc, g)
Collide a particle in y and z with constant acceleration in x.
Definition: mpcd.f90:512
Container for a histogram, e.g. p(x)
Definition: common.f90:86
Spatial cells.
Definition: cell_system.f90:11
pure integer function change_23(i)
Return 2 for 3 and 3 for 2.
Definition: mpcd.f90:595
subroutine, public mpcd_stream_xforce_yzwall(particles, cells, dt, g)
Stream MPCD particles with a force in the x-direction and specular or bounce-back conditions in y...
Definition: mpcd.f90:465
integer, parameter, public bounce_back_bc
Definition: cell_system.f90:44