module classes_boxes_particles_swap use, intrinsic :: iso_fortran_env, only: DP => REAL64 use procedures_random_number, only: random_integer use classes_hetero_couples, only: Abstract_Hetero_Couples use procedures_hetero_couples_factory, only: hetero_couples_destroy => destroy use classes_tower_sampler, only: Abstract_Tower_Sampler use procedures_tower_sampler_factory, only: tower_sampler_destroy => destroy use types_environment_wrapper, only: Environment_Wrapper use types_mixture_wrapper, only: Mixture_Wrapper use types_particle_wrapper, only: Concrete_Double_Particle use types_short_interactions_wrapper, only: Short_Interactions_Wrapper use types_dipolar_interactions_dynamic_wrapper, only: Dipolar_Interactions_Dynamic_Wrapper use types_dipolar_interactions_static_wrapper, only: Dipolar_Interactions_Static_Wrapper use procedures_dipoles_field_interaction, only: dipoles_field_visit_add => visit_add, & dipoles_field_visit_remove => visit_remove use types_observables_energies, only: Concrete_Double_Energies use procedures_observables_energies_factory, only: observables_energies_set => set use types_generating_observables_wrapper, only: Generating_Observables_Wrapper use classes_generating_algorithm, only: Abstract_Generating_Algorithm use procedures_selectors_resetters, only: selectors_reset => reset use procedures_transmutation_visitors, only: transmutation_visit_short => visit_short, & transmutation_visit_dipolar => visit_dipolar use procedures_metropolis_algorithm, only: metropolis_algorithm use procedures_transmutation_updaters, only: transmutation_update => update implicit none private type, extends(Abstract_Generating_Algorithm), public :: Boxes_Particles_Swap private type(Environment_Wrapper), pointer :: environment => null() type(Mixture_Wrapper), pointer :: mixture => null() type(Short_Interactions_Wrapper), pointer :: short_interactions => null() type(Dipolar_Interactions_Dynamic_Wrapper), pointer :: dipolar_interactions_dynamic(:) => & null() type(Dipolar_Interactions_Static_Wrapper), pointer :: dipolar_interactions_static(:) => & null() logical, allocatable :: can_translate(:, :) class(Abstract_Hetero_Couples), allocatable :: box_couples, component_couples(:) class(Abstract_Tower_Sampler), allocatable :: boxes_selector, components_selectors(:) contains procedure :: construct => Concrete_construct procedure :: destroy => Concrete_destroy procedure :: reset_selectors => Concrete_reset_selectors procedure :: get_num_choices => Concrete_get_num_choices procedure :: try => Concrete_try procedure, private :: metropolis_algorithm => Concrete_metropolis_algorithm procedure, private :: define_swap => Concrete_define_swap procedure, private :: acceptation_probability => Concrete_acceptation_probability procedure, private :: visit_walls => Concrete_visit_walls procedure, private :: visit_short => Concrete_visit_short procedure, private :: visit_fields => Concrete_visit_fields procedure, private :: visit_dipolar => Concrete_visit_dipolar procedure, private :: update_components => Concrete_update_components end type Boxes_Particles_Swap contains subroutine Concrete_construct(this, environment, mixture, short_interactions, & dipolar_interactions_dynamic, dipolar_interactions_static, can_translate, & box_couples, component_couples, boxes_selector, components_selectors) class(Boxes_Particles_Swap), intent(out) :: this type(Environment_Wrapper), target, intent(in) :: environment type(Mixture_Wrapper), target, intent(in) :: mixture type(Short_Interactions_Wrapper), target, intent(in) :: short_interactions type(Dipolar_Interactions_Dynamic_Wrapper), target, intent(in) :: & dipolar_interactions_dynamic(:) type(Dipolar_Interactions_Static_Wrapper), target, intent(in) :: & dipolar_interactions_static(:) logical, intent(in) :: can_translate(:, :) class(Abstract_Hetero_Couples), intent(in) :: box_couples, component_couples(:) class(Abstract_Tower_Sampler), intent(in) :: boxes_selector, components_selectors(:) this%environment => environment this%mixture => mixture this%short_interactions => short_interactions this%dipolar_interactions_dynamic => dipolar_interactions_dynamic this%dipolar_interactions_static => dipolar_interactions_static allocate(this%can_translate, source=can_translate) allocate(this%box_couples, source=box_couples) allocate(this%component_couples, source=component_couples) allocate(this%boxes_selector, source=boxes_selector) allocate(this%components_selectors, source=components_selectors) end subroutine Concrete_construct subroutine Concrete_destroy(this) class(Boxes_Particles_Swap), intent(inout) :: this call tower_sampler_destroy(this%components_selectors) call tower_sampler_destroy(this%boxes_selector) call hetero_couples_destroy(this%component_couples) call hetero_couples_destroy(this%box_couples) if (allocated(this%can_translate)) deallocate(this%can_translate) this%dipolar_interactions_static => null() this%dipolar_interactions_dynamic => null() this%short_interactions => null() this%mixture => null() this%environment => null() end subroutine Concrete_destroy subroutine Concrete_reset_selectors(this) class(Boxes_Particles_Swap), intent(inout) :: this call selectors_reset(this%components_selectors, this%box_couples, this%component_couples, & this%mixture%average_nums_particles, this%can_translate) end subroutine Concrete_reset_selectors pure integer function Concrete_get_num_choices(this) result(num_choices) class(Boxes_Particles_Swap), intent(in) :: this integer :: box_i_couple num_choices = 0 do box_i_couple = 1, this%box_couples%get_num() num_choices = num_choices + this%components_selectors(box_i_couple)%get_num_choices() end do end function Concrete_get_num_choices subroutine Concrete_try(this, observables) class(Boxes_Particles_Swap), intent(in) :: this type(Generating_Observables_Wrapper), intent(inout) :: observables logical :: success type(Concrete_Double_Energies) :: deltas(2) integer :: box_i_couple, ij_boxes(size(deltas)) integer :: i_partner, ij_components(size(deltas)) box_i_couple = this%boxes_selector%get() ij_boxes = this%box_couples%get(box_i_couple) ij_components = this%component_couples(box_i_couple)%& get(this%components_selectors(box_i_couple)%get()) observables%swaps_counters(ij_components(1), ij_components(2), ij_boxes(1), ij_boxes(2))%& num_hits = & observables%swaps_counters(ij_components(1), ij_components(2), ij_boxes(1), & ij_boxes(2))%num_hits + 1 do i_partner = 1, size(deltas) allocate(deltas(i_partner)%& short_energies(size(observables%energies(ij_boxes(i_partner))%short_energies), 2)) allocate(deltas(i_partner)%& dipolar_energies(size(observables%energies(ij_boxes(i_partner))%dipolar_energies), & 2)) end do call this%metropolis_algorithm(success, deltas, ij_boxes, ij_components) if (success) then do i_partner = 1, size(ij_boxes) observables%nums_particles(ij_components(1), ij_boxes(i_partner)) = this%mixture%& components(ij_components(1), ij_boxes(i_partner))%num_particles%get() observables%nums_particles(ij_components(2), ij_boxes(i_partner)) = this%mixture%& components(ij_components(2), ij_boxes(i_partner))%num_particles%get() call observables_energies_set(observables%energies(ij_boxes(i_partner)), & deltas(i_partner), cshift(ij_components, i_partner-1)) end do observables%swaps_counters(ij_components(1), ij_components(2), ij_boxes(1), & ij_boxes(2))%num_successes = & observables%swaps_counters(ij_components(1), ij_components(2), ij_boxes(1), & ij_boxes(2))%num_successes + 1 end if end subroutine Concrete_try subroutine Concrete_metropolis_algorithm(this, success, deltas, ij_boxes, ij_components) class(Boxes_Particles_Swap), intent(in) :: this logical, intent(out) :: success type(Concrete_Double_Energies), intent(inout) :: deltas(:) integer, intent(in) :: ij_boxes(:), ij_components(:) real(DP) :: delta_energies(size(deltas)) type(Concrete_Double_Particle) :: partners(size(deltas)) logical :: abort, overlap integer :: i_partner success = .false. call this%define_swap(abort, partners, ij_boxes, ij_components) if (abort) return call this%visit_walls(overlap, deltas, ij_boxes, ij_components, partners) if (overlap) return call this%visit_short(overlap, deltas, ij_boxes, ij_components, partners) if (overlap) return call this%visit_fields(deltas, ij_boxes, partners) call this%visit_dipolar(deltas, ij_boxes, ij_components, partners) do i_partner = 1, size(partners) delta_energies(i_partner) = & sum(deltas(i_partner)%walls_energies + deltas(i_partner)%field_energies) + & sum(deltas(i_partner)%short_energies + deltas(i_partner)%dipolar_energies) + & deltas(i_partner)%dipolar_shared_energy end do success = metropolis_algorithm(this%acceptation_probability(ij_boxes, ij_components, & sum(delta_energies))) if (success) call this%update_components(ij_boxes, ij_components, partners) end subroutine Concrete_metropolis_algorithm subroutine Concrete_define_swap(this, abort, partners, ij_boxes, ij_components) class(Boxes_Particles_Swap), intent(in) :: this logical, intent(out) :: abort type(Concrete_Double_Particle), intent(inout) :: partners(:) integer, intent(in) :: ij_boxes(:), ij_components(:) integer :: i_partner, i_swapped if (this%mixture%components(ij_components(1), ij_boxes(1))%num_particles%get() == 0 .or. & this%mixture%components(ij_components(2), ij_boxes(2))%num_particles%get() == 0) then abort = .true. return else abort = .false. end if do i_partner = 1, size(partners) partners(i_partner)%particles(1)%i = random_integer(this%mixture%& components(ij_components(i_partner), ij_boxes(i_partner))%num_particles%get()) partners(i_partner)%particles(1)%position = this%mixture%& components(ij_components(i_partner), ij_boxes(i_partner))%positions%& get(partners(i_partner)%particles(1)%i) partners(i_partner)%particles(1)%orientation = this%mixture%& components(ij_components(i_partner), ij_boxes(i_partner))%orientations%& get(partners(i_partner)%particles(1)%i) partners(i_partner)%particles(1)%dipole_moment = this%mixture%& components(ij_components(i_partner), ij_boxes(i_partner))%dipole_moments%& get(partners(i_partner)%particles(1)%i) end do do i_partner = 1, size(partners) i_swapped = size(partners) + 1 - i_partner partners(i_partner)%particles(2)%i = this%mixture%& components(ij_components(i_swapped), ij_boxes(i_partner))%num_particles%get() + 1 partners(i_partner)%particles(2)%position = partners(i_partner)%particles(1)%position partners(i_partner)%particles(2)%orientation = partners(i_swapped)%particles(1)%& orientation partners(i_partner)%particles(2)%dipole_moment = partners(i_swapped)%particles(1)%& dipole_moment end do end subroutine Concrete_define_swap !> \[ !> P[i_I^{\boldsymbol{I}} \to N_I^{\boldsymbol{J}} + 1, !> i_J^{\boldsymbol{J}} \to N_J^{\boldsymbol{I}} + 1] = \min \left(1, !> \frac{N_I^{\boldsymbol{I}}}{N_I^{\boldsymbol{J}} + 1} !> \frac{N_J^{\boldsymbol{J}}}{N_J^{\boldsymbol{I}} + 1} !> e^{-\beta \Delta U_{i_I^{\boldsymbol{I}} \to N_I^{\boldsymbol{J}} + 1, !> i_J^{\boldsymbol{J}} \to N_J^{\boldsymbol{I}} + 1}} \right) !> \] pure real(DP) function Concrete_acceptation_probability(this, ij_boxes, ij_components, & delta_energy) result(probability) class(Boxes_Particles_Swap), intent(in) :: this integer, intent(in) :: ij_boxes(:), ij_components(:) real(DP), intent(in) :: delta_energy probability = & real(this%mixture%components(ij_components(1), ij_boxes(1))%num_particles%get(), DP) / & real(this%mixture%components(ij_components(1), ij_boxes(2))%num_particles%get()+1, DP)*& real(this%mixture%components(ij_components(2), ij_boxes(2))%num_particles%get(), DP) / & real(this%mixture%components(ij_components(2), ij_boxes(1))%num_particles%get()+1, DP)*& exp(-delta_energy / this%environment%temperature%get()) probability = min(1._DP, probability) end function Concrete_acceptation_probability pure subroutine Concrete_visit_walls(this, overlap, deltas, ij_boxes, ij_components, partners) class(Boxes_Particles_Swap), intent(in) :: this logical, intent(out) :: overlap type(Concrete_Double_Energies), intent(inout) :: deltas(:) integer, intent(in) :: ij_boxes(:), ij_components(:) type(Concrete_Double_Particle), intent(in) :: partners(:) integer :: i_partner do i_partner = 1, size(partners) call transmutation_visit_short(overlap, deltas(i_partner)%walls_energies, this%& environment%visitable_walls(ij_boxes(i_partner)), this%short_interactions%& wall_pairs, cshift(ij_components, i_partner-1), partners(i_partner)%particles) if (overlap) return end do end subroutine Concrete_visit_walls subroutine Concrete_visit_short(this, overlap, deltas, ij_boxes, ij_components, partners) class(Boxes_Particles_Swap), intent(in) :: this logical, intent(out) :: overlap type(Concrete_Double_Energies), intent(inout) :: deltas(:) integer, intent(in) :: ij_boxes(:), ij_components(:) type(Concrete_Double_Particle), intent(in) :: partners(:) integer :: i_partner do i_partner = 1, size(partners) call transmutation_visit_short(overlap, deltas(i_partner)%short_energies, this%& short_interactions%cells(ij_boxes(i_partner)), cshift(ij_components, i_partner-1), & partners(i_partner)%particles) if (overlap) return end do end subroutine Concrete_visit_short pure subroutine Concrete_visit_fields(this, deltas, ij_boxes, partners) class(Boxes_Particles_Swap), intent(in) :: this type(Concrete_Double_Energies), intent(inout) :: deltas(:) integer, intent(in) :: ij_boxes(:) type(Concrete_Double_Particle), intent(in) :: partners(:) integer :: i_partner do i_partner = 1, size(partners) call transmutation_visit_dipolar(deltas(i_partner)%field_energies, this%& environment%external_fields(ij_boxes(i_partner)), partners(i_partner)%particles) end do end subroutine Concrete_visit_fields pure subroutine Concrete_visit_dipolar(this, deltas, ij_boxes, ij_components, partners) class(Boxes_Particles_Swap), intent(in) :: this type(Concrete_Double_Energies), intent(inout) :: deltas(:) integer, intent(in) :: ij_boxes(:), ij_components(:) type(Concrete_Double_Particle), intent(in) :: partners(:) integer :: i_partner do i_partner = 1, size(partners) call transmutation_visit_dipolar(deltas(i_partner)%dipolar_energies, & deltas(i_partner)%dipolar_shared_energy, this%& dipolar_interactions_dynamic(ij_boxes(i_partner)), & cshift(ij_components, i_partner-1), partners(i_partner)%particles) end do end subroutine Concrete_visit_dipolar subroutine Concrete_update_components(this, ij_boxes, ij_components, partners) class(Boxes_Particles_Swap), intent(in) :: this integer, intent(in) :: ij_boxes(:), ij_components(:) type(Concrete_Double_Particle), intent(in) :: partners(:) integer :: i_partner do i_partner = 1, size(partners) call transmutation_update(this%mixture%components(:, ij_boxes(i_partner)), this%& mixture%total_moments(ij_boxes(i_partner)), this%short_interactions%& cells(ij_boxes(i_partner)), this%dipolar_interactions_static(ij_boxes(i_partner)), & cshift(ij_components, i_partner-1), partners(i_partner)%particles) end do end subroutine Concrete_update_components end module classes_boxes_particles_swap