build_fragment_from_indices Subroutine

public subroutine build_fragment_from_indices(sys_geom, monomer_indices, fragment, error, bonds)

Build a fragment on-the-fly from monomer indices with hydrogen capping for broken bonds

Extracts atoms from specified monomers and adds hydrogen caps where bonds are broken. Caps are always added at the end of the atom list. Supports both fixed-size (identical monomers) and variable-sized fragments.

Example: monomer_indices = [1, 3, 5] extracts waters 1, 3, and 5 If connectivity shows broken bonds, hydrogens are capped at positions of missing atoms

Arguments

Type IntentOptional Attributes Name
type(system_geometry_t), intent(in) :: sys_geom
integer, intent(in) :: monomer_indices(:)
type(physical_fragment_t), intent(out) :: fragment
type(error_t), intent(out) :: error
type(bond_t), intent(in), optional :: bonds(:)

Connectivity information for capping


Calls

proc~~build_fragment_from_indices~~CallsGraph proc~build_fragment_from_indices build_fragment_from_indices proc~add_hydrogen_caps add_hydrogen_caps proc~build_fragment_from_indices->proc~add_hydrogen_caps proc~calculate_monomer_distance calculate_monomer_distance proc~build_fragment_from_indices->proc~calculate_monomer_distance proc~check_duplicate_atoms check_duplicate_atoms proc~build_fragment_from_indices->proc~check_duplicate_atoms proc~count_hydrogen_caps count_hydrogen_caps proc~build_fragment_from_indices->proc~count_hydrogen_caps proc~error_add_context error_t%error_add_context proc~build_fragment_from_indices->proc~error_add_context proc~error_has_error error_t%error_has_error proc~build_fragment_from_indices->proc~error_has_error proc~fragment_compute_nelec physical_fragment_t%fragment_compute_nelec proc~build_fragment_from_indices->proc~fragment_compute_nelec proc~to_angstrom to_angstrom proc~calculate_monomer_distance->proc~to_angstrom error error proc~check_duplicate_atoms->error proc~element_number_to_symbol element_number_to_symbol proc~check_duplicate_atoms->proc~element_number_to_symbol proc~error_set error_t%error_set proc~check_duplicate_atoms->proc~error_set to_char to_char proc~check_duplicate_atoms->to_char

Called by

proc~~build_fragment_from_indices~~CalledByGraph proc~build_fragment_from_indices build_fragment_from_indices proc~compute_gmbe compute_gmbe proc~compute_gmbe->proc~build_fragment_from_indices proc~map_fragment_to_system_dipole_derivatives map_fragment_to_system_dipole_derivatives proc~map_fragment_to_system_dipole_derivatives->proc~build_fragment_from_indices proc~map_fragment_to_system_gradient map_fragment_to_system_gradient proc~map_fragment_to_system_gradient->proc~build_fragment_from_indices proc~map_fragment_to_system_hessian map_fragment_to_system_hessian proc~map_fragment_to_system_hessian->proc~build_fragment_from_indices proc~node_worker node_worker proc~node_worker->proc~build_fragment_from_indices proc~serial_fragment_processor serial_fragment_processor proc~serial_fragment_processor->proc~build_fragment_from_indices proc~compute_mbe compute_mbe proc~serial_fragment_processor->proc~compute_mbe interface~node_worker node_worker interface~node_worker->proc~node_worker interface~serial_fragment_processor serial_fragment_processor interface~serial_fragment_processor->proc~serial_fragment_processor proc~compute_mbe->proc~map_fragment_to_system_dipole_derivatives proc~compute_mbe->proc~map_fragment_to_system_gradient proc~compute_mbe->proc~map_fragment_to_system_hessian proc~compute_mbe_dipole_derivatives compute_mbe_dipole_derivatives proc~compute_mbe->proc~compute_mbe_dipole_derivatives proc~compute_mbe_gradient compute_mbe_gradient proc~compute_mbe->proc~compute_mbe_gradient proc~compute_mbe_hessian compute_mbe_hessian proc~compute_mbe->proc~compute_mbe_hessian proc~compute_mbe_dipole_derivatives->proc~map_fragment_to_system_dipole_derivatives proc~compute_mbe_gradient->proc~map_fragment_to_system_gradient proc~compute_mbe_hessian->proc~map_fragment_to_system_hessian proc~global_coordinator global_coordinator proc~global_coordinator->proc~compute_mbe proc~gmbe_run_distributed gmbe_context_t%gmbe_run_distributed proc~gmbe_run_distributed->interface~node_worker proc~mbe_run_distributed mbe_context_t%mbe_run_distributed proc~mbe_run_distributed->interface~node_worker interface~global_coordinator global_coordinator proc~mbe_run_distributed->interface~global_coordinator proc~mbe_run_serial mbe_context_t%mbe_run_serial proc~mbe_run_serial->interface~serial_fragment_processor interface~global_coordinator->proc~global_coordinator

Variables

Type Visibility Attributes Name Initial
integer, private :: atom_end
integer, private :: atom_global_idx
integer, private :: atom_i
integer, private :: atom_j
integer, private :: atom_start
integer, private, allocatable :: atoms_in_fragment(:)

List of all atom indices in this fragment

integer, private :: atoms_per_monomer
integer, private :: frag_atom_idx
integer, private :: i
integer, private :: iatom
integer, private :: j
integer, private :: mono_idx
integer, private :: n_atoms_no_caps
integer, private :: n_caps
integer, private :: n_monomers_in_frag
logical, private :: use_explicit_fragments

Source Code

   subroutine build_fragment_from_indices(sys_geom, monomer_indices, fragment, error, bonds)
      !! Build a fragment on-the-fly from monomer indices with hydrogen capping for broken bonds
      !!
      !! Extracts atoms from specified monomers and adds hydrogen caps where bonds are broken.
      !! Caps are always added at the end of the atom list.
      !! Supports both fixed-size (identical monomers) and variable-sized fragments.
      !!
      !! Example: monomer_indices = [1, 3, 5] extracts waters 1, 3, and 5
      !!          If connectivity shows broken bonds, hydrogens are capped at positions of missing atoms
      type(system_geometry_t), intent(in) :: sys_geom
      integer, intent(in) :: monomer_indices(:)
      type(physical_fragment_t), intent(out) :: fragment
      type(error_t), intent(out) :: error
      type(bond_t), intent(in), optional :: bonds(:)  !! Connectivity information for capping

      integer :: n_monomers_in_frag, atoms_per_monomer, n_atoms_no_caps
      integer :: i, j, mono_idx, atom_start, atom_end, frag_atom_idx
      integer :: atom_i, atom_j, n_caps
      integer, allocatable :: atoms_in_fragment(:)  !! List of all atom indices in this fragment
      integer :: iatom, atom_global_idx
      logical :: use_explicit_fragments

      n_monomers_in_frag = size(monomer_indices)

      ! Determine if we're using explicit fragment definitions or regular monomer-based
      use_explicit_fragments = allocated(sys_geom%fragment_atoms)

      if (use_explicit_fragments) then
         ! Variable-sized fragments: count total atoms from fragment definitions
         n_atoms_no_caps = 0
         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            n_atoms_no_caps = n_atoms_no_caps + sys_geom%fragment_sizes(mono_idx)
         end do

         ! Build list of atom indices (0-indexed) from explicit fragment definitions
         allocate (atoms_in_fragment(n_atoms_no_caps))
         iatom = 0
         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            do j = 1, sys_geom%fragment_sizes(mono_idx)
               iatom = iatom + 1
               atoms_in_fragment(iatom) = sys_geom%fragment_atoms(j, mono_idx)
            end do
         end do
      else
         ! Fixed-size monomers: use atoms_per_monomer
         atoms_per_monomer = sys_geom%atoms_per_monomer
         n_atoms_no_caps = n_monomers_in_frag*atoms_per_monomer

         ! Build list of atom indices in this fragment (0-indexed to match bond indices)
         allocate (atoms_in_fragment(n_atoms_no_caps))
         iatom = 0
         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            atom_start = (mono_idx - 1)*atoms_per_monomer
            do atom_i = 0, atoms_per_monomer - 1
               iatom = iatom + 1
               atoms_in_fragment(iatom) = atom_start + atom_i
            end do
         end do
      end if

      ! Count how many caps we need
      call count_hydrogen_caps(atoms_in_fragment, bonds, n_caps)

      ! Allocate arrays with space for original atoms + caps
      fragment%n_atoms = n_atoms_no_caps + n_caps
      fragment%n_caps = n_caps
      allocate (fragment%element_numbers(fragment%n_atoms))
      allocate (fragment%coordinates(3, fragment%n_atoms))
      if (n_caps > 0) allocate (fragment%cap_replaces_atom(n_caps))
      allocate (fragment%local_to_global(n_atoms_no_caps))  ! Only non-cap atoms

      ! Copy original atoms and build local→global mapping
      frag_atom_idx = 0
      if (use_explicit_fragments) then
         ! Variable-sized: copy atoms based on explicit fragment definitions
         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            do j = 1, sys_geom%fragment_sizes(mono_idx)
               frag_atom_idx = frag_atom_idx + 1
               ! fragment_atoms is 0-indexed, so +1 for Fortran arrays
               atom_global_idx = sys_geom%fragment_atoms(j, mono_idx) + 1
               fragment%element_numbers(frag_atom_idx) = sys_geom%element_numbers(atom_global_idx)
               fragment%coordinates(:, frag_atom_idx) = sys_geom%coordinates(:, atom_global_idx)
               fragment%local_to_global(frag_atom_idx) = atom_global_idx  ! Store 1-indexed global position
            end do
         end do
      else
         ! Fixed-size: use atoms_per_monomer
         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            atom_start = (mono_idx - 1)*atoms_per_monomer + 1
            atom_end = mono_idx*atoms_per_monomer

            ! Copy coordinates and elements
            do atom_i = atom_start, atom_end
               frag_atom_idx = frag_atom_idx + 1
               fragment%element_numbers(frag_atom_idx) = sys_geom%element_numbers(atom_i)
               fragment%coordinates(:, frag_atom_idx) = sys_geom%coordinates(:, atom_i)
               fragment%local_to_global(frag_atom_idx) = atom_i  ! Store 1-indexed global position
            end do
         end do
      end if

      ! Add hydrogen caps at end (if any)
      if (present(bonds) .and. n_caps > 0) then
         call add_hydrogen_caps(atoms_in_fragment, bonds, sys_geom, fragment, n_atoms_no_caps)
      end if

      ! Set electronic structure properties from system geometry
      if (use_explicit_fragments .and. allocated(sys_geom%fragment_charges) .and. &
          allocated(sys_geom%fragment_multiplicities)) then
         ! Explicit fragments: sum charges and multiplicities from constituent fragments
         fragment%charge = 0
         fragment%multiplicity = 1  ! Start with singlet assumption

         do i = 1, n_monomers_in_frag
            mono_idx = monomer_indices(i)
            fragment%charge = fragment%charge + sys_geom%fragment_charges(mono_idx)
         end do

         ! For single fragment, use its specific multiplicity
         if (n_monomers_in_frag == 1) then
            fragment%multiplicity = sys_geom%fragment_multiplicities(monomer_indices(1))
         else
            ! For multi-fragment composites, multiplicity needs careful treatment
            ! For now, default to system multiplicity (this may need refinement)
            fragment%multiplicity = sys_geom%multiplicity
         end if
      else
         ! Fixed-size monomers: use system defaults
         fragment%charge = sys_geom%charge
         fragment%multiplicity = sys_geom%multiplicity
      end if
      call fragment%compute_nelec()

      ! Validate: check for spatially overlapping atoms
      call check_duplicate_atoms(fragment, error)
      if (error%has_error()) then
         call error%add_context("mqc_physical_fragment:build_fragment_from_indices")
         return
      end if

      ! Calculate minimal distance between monomers in this fragment
      fragment%distance = calculate_monomer_distance(sys_geom, monomer_indices)

      deallocate (atoms_in_fragment)

   end subroutine build_fragment_from_indices