self_organizing_map_utilities.f90 Source File

This module defines a class for simple self_organizing_map (one kohonen layer)


This file depends on

sourcefile~~self_organizing_map_utilities.f90~~EfferentGraph sourcefile~self_organizing_map_utilities.f90 self_organizing_map_utilities.f90 sourcefile~constants_utilities.f90 constants_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~constants_utilities.f90 sourcefile~distance_base_utilities.f90 distance_base_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~factory_distance_utilities.f90 factory_distance_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~factory_distance_utilities.f90 sourcefile~kohonen_layer_parameters_utilities.f90 kohonen_layer_parameters_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~kohonen_layer_parameters_utilities.f90 sourcefile~kohonen_map_base_utilities.f90 kohonen_map_base_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~kohonen_map_base_utilities.f90 sourcefile~kohonen_pattern_utilities.f90 kohonen_pattern_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~kohonen_pattern_utilities.f90 sourcefile~kohonen_prototype_utilities.f90 kohonen_prototype_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~kohonen_prototype_utilities.f90 sourcefile~precision_utilities.f90 precision_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~quicksort_utilities.f90 quicksort_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~quicksort_utilities.f90 sourcefile~random_generator_base_utilities.f90 random_generator_base_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~random_generator_base_utilities.f90 sourcefile~rkiss05_generator_utilities.f90 rkiss05_generator_utilities.f90 sourcefile~self_organizing_map_utilities.f90->sourcefile~rkiss05_generator_utilities.f90 sourcefile~constants_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~distance_base_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~constants_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~correlation_distance_utilities.f90 correlation_distance_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~correlation_distance_utilities.f90 sourcefile~direction_cosine_distance_utilities.f90 direction_cosine_distance_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~direction_cosine_distance_utilities.f90 sourcefile~euclidean_distance_utilities.f90 euclidean_distance_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~euclidean_distance_utilities.f90 sourcefile~manhattan_distance_utilities.f90 manhattan_distance_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~manhattan_distance_utilities.f90 sourcefile~max_distance_utilities.f90 max_distance_utilities.f90 sourcefile~factory_distance_utilities.f90->sourcefile~max_distance_utilities.f90 sourcefile~kohonen_layer_parameters_utilities.f90->sourcefile~constants_utilities.f90 sourcefile~kohonen_layer_parameters_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~kohonen_map_base_utilities.f90->sourcefile~kohonen_layer_parameters_utilities.f90 sourcefile~kohonen_map_base_utilities.f90->sourcefile~kohonen_pattern_utilities.f90 sourcefile~kohonen_map_base_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~kohonen_pattern_utilities.f90->sourcefile~constants_utilities.f90 sourcefile~kohonen_pattern_utilities.f90->sourcefile~kohonen_prototype_utilities.f90 sourcefile~kohonen_pattern_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~kohonen_prototype_utilities.f90->sourcefile~constants_utilities.f90 sourcefile~kohonen_prototype_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~kohonen_prototype_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~quicksort_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~sort_base_utilities.f90 sort_base_utilities.f90 sourcefile~quicksort_utilities.f90->sourcefile~sort_base_utilities.f90 sourcefile~random_generator_base_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~rkiss05_generator_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~rkiss05_generator_utilities.f90->sourcefile~random_generator_base_utilities.f90 sourcefile~correlation_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~correlation_distance_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~general_utilities.f90 general_utilities.f90 sourcefile~correlation_distance_utilities.f90->sourcefile~general_utilities.f90 sourcefile~direction_cosine_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~direction_cosine_distance_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~euclidean_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~euclidean_distance_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~manhattan_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~manhattan_distance_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~max_distance_utilities.f90->sourcefile~distance_base_utilities.f90 sourcefile~max_distance_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~sort_base_utilities.f90->sourcefile~precision_utilities.f90 sourcefile~general_utilities.f90->sourcefile~precision_utilities.f90

Files dependent on this one

sourcefile~~self_organizing_map_utilities.f90~~AfferentGraph sourcefile~self_organizing_map_utilities.f90 self_organizing_map_utilities.f90 sourcefile~som_predict_variables.f90 som_predict_variables.f90 sourcefile~som_predict_variables.f90->sourcefile~self_organizing_map_utilities.f90 sourcefile~som_train_variables.f90 som_train_variables.f90 sourcefile~som_train_variables.f90->sourcefile~self_organizing_map_utilities.f90

Source Code

!! author: Oscar Garcia-Cabrejo
!! date: 12/04/2024
!! version: 0.1
!!  This module defines a class for simple self_organizing_map (one kohonen layer) 
module self_organizing_map_utilities
!!  This module defines a class for simple self_organizing_map (one kohonen layer)
!$  use omp_lib     
use error_handling, only: error_t,error_stop;
use precision_utilities, only: wp;
use constants_utilities, only: NUMCHAR;
use random_generator_base_utilities, only: random_generator_base;
use rkiss05_generator_utilities, only: rkiss05_generator;
use kohonen_layer_parameters_utilities, only: kohonen_layer_parameters;
use kohonen_map_base_utilities, only: kohonen_map_base;
use kohonen_prototype_utilities, only: kohonen_prototype;
use kohonen_pattern_utilities, only: kohonen_pattern;
use distance_base_utilities, only: distance_base;
use factory_distance_utilities, only: factory_distance;
!use influence_function_utilities;
use quicksort_utilities, only: quicksort;
!
implicit none;
!
type,extends(kohonen_map_base) :: self_organizing_map
!!   Class to represent a self_organizing_map
    private
        character(len=NUMCHAR) :: class_name='self_organizing_map';
        type(kohonen_prototype),allocatable :: grid(:,:,:)
        integer,allocatable :: number_patterns(:,:,:),cells_index(:,:)
        real(kind=wp),allocatable :: u_matrix(:,:,:),distance(:,:)
        real(kind=wp),allocatable :: cells_distances(:,:),coordinates(:,:)
        type(kohonen_layer_parameters) :: parameters
        type(factory_distance) :: factory
        class(distance_base),allocatable :: distance_function
        real(kind=wp),allocatable :: distortion(:)
        type(rkiss05_generator) :: rnumber_grator
        integer :: seed  
        integer,allocatable :: grid_pattern_index(:,:,:),list_node_grid(:,:,:,:)
    contains
        procedure,public :: create => create_som
        procedure,public :: destroy => destroy_som
        procedure,private :: create_random_sample
        procedure,private :: train_som_data
        procedure,public :: train => train_som_data 
        procedure,public :: predict => predict_som
        procedure,public :: print => print_som
        procedure,public :: read => read_som
        procedure,public :: get_count => get_count_som
        procedure,public :: query => query_som
        procedure,public :: get_prototypes
        !procedure,public :: get_index => get_index_som
        procedure,public :: get_u_matrix => get_u_matrix_som
        procedure,private :: find_best_match_unit
        procedure,private :: update_weights
        !procedure,private :: update_weights1
        procedure,private :: find_bmu_grid
        procedure,private :: calculate_u_matrix
        procedure,private :: calculate_u_matrix_hexagonal
        procedure,private :: calculate_u_matrix_rectangular
        procedure,private :: calculate_sigma
        procedure,nopass,private :: position2index
        procedure,nopass,private :: index2position
        procedure,nopass,private :: calculate_distance_matrix
        procedure,nopass,private :: calculate_coordinates
        procedure,private :: calculate_distance_between_prototypes
        procedure,nopass,public :: external_train_map
        procedure,nopass,public :: external_predict_map
!
end type self_organizing_map
!
contains
!========================================================================================
    subroutine create_som(kohonen_map,training_parameters)
!========================================================================================
!!   Constructor for self_organizing_map 
        character(len=NUMCHAR),parameter :: fname = 'create_som'
!! A character variable with the name of the function
        class(self_organizing_map) :: kohonen_map
!! A  `self_organizing_map` object
        type(kohonen_layer_parameters),dimension(:) :: training_parameters
!! A `kohonen_layer_parameters` object
        integer :: ierr,nx,ny,nz,ix,iy,iz,nvar1,nvar2,seed,current_index,nepoch
        integer :: i,j
        real(kind=wp),allocatable :: input(:,:)
        character(len=NUMCHAR) :: base_message,message
!
        base_message=trim(kohonen_map%class_name)//'_'//trim(fname)//'_ERROR';
!
        kohonen_map%parameters=training_parameters(1);
        nx=training_parameters(1)%number_nodes_nx;
        ny=training_parameters(1)%number_nodes_ny;
        nz=training_parameters(1)%number_nodes_nz;
        nvar1=training_parameters(1)%number_variables1;
        nvar2=training_parameters(1)%number_variables2;
        nepoch=training_parameters(1)%number_epochs;
        write(*,*) 'Create= ',nx,ny,nz,nvar1,nvar2,nepoch;
        allocate(kohonen_map%grid(nx,ny,nz),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for grid array';
            call error_stop(message);
        endif
!
        allocate(kohonen_map%grid_pattern_index(nx,ny,nz),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for grid_pattern_index array';
            call error_stop(message);
        endif
!
        allocate(input(nvar1,nvar2),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for input array';
            call error_stop(message);
        endif
!
        allocate(kohonen_map%number_patterns(nx,ny,nz),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for number_patterns array';
            call error_stop(message);
        endif
!
        allocate(kohonen_map%cells_index(training_parameters(1)%number_patterns,3),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for cell_index array';
            call error_stop(message);
        endif
!
        kohonen_map%number_patterns=0;
        kohonen_map%cells_index=0;
        allocate(kohonen_map%u_matrix(2*nx-1,2*ny-1,2*nz-1),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for u_matrix array';
            call error_stop(message);
        endif
        kohonen_map%u_matrix=0.0_wp;
!
        allocate(kohonen_map%distance(nx*ny,nx*ny),stat=ierr);
        if(ierr /= 0) then
            message = trim(base_message)//'_allocating memory for distance array';
            call error_stop(message);
        endif
        kohonen_map%distance=0.0_wp;
!
        allocate(kohonen_map%cells_distances(nx*ny*nz,nx*ny*nz),stat=ierr);
        kohonen_map%cells_distances=0.0d0;
        allocate(kohonen_map%coordinates(nx*ny*nz,3),stat=ierr);
        kohonen_map%coordinates=0.0d0;
        allocate(kohonen_map%distortion(nepoch),stat=ierr);
        kohonen_map%distortion=0.0d0;
!
        call kohonen_map%factory%create_distance(training_parameters(1)%distance_type,&
            kohonen_map%distance_function);
!
        kohonen_map%seed=training_parameters(1)%random_seed_(1);
        call kohonen_map%rnumber_grator%create(kohonen_map%seed);
        ! do i=1,nvar1;
        !     do j=1,nvar2;
        !         input(i,j)=kohonen_map%rnumber_grator%generate();
        !         !write(*,*) 'input= ',input(i,j);
        !     enddo
        ! enddo
!   
    write(*,*) 'SOM: Initializing grid...',kohonen_map%seed;
        do iz=1,nz;
            do iy=1,ny;
                do ix=1,nx;
                    !write(*,*) 'creating ',ix,iy,iz
                    call kohonen_map%create_random_sample(input);
                    call kohonen_map%grid(ix,iy,iz)%create(input); 
                        current_index=position2index(ix,iy,iz,nx,ny);
                    call calculate_coordinates(current_index,ix,iy,iz,nx,ny,nz,&
                        kohonen_map%coordinates,training_parameters(1)%node_type);
                enddo!ix
            enddo !iy
         enddo !iz
         deallocate(input);
   !
         call calculate_distance_matrix(kohonen_map%coordinates,kohonen_map%cells_distances,&
             training_parameters(1)%node_type,training_parameters(1)%toroidal_grid);
    write(*,*) 'SOM: Initializing grid...OK';
!
    end subroutine create_som
!========================================================================================
    subroutine destroy_som(kohonen_map)
!========================================================================================
!!   Destructor for self_organizing_map 
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
   
        integer :: ix,iy,iz
!
!       write(*,*) 'SOM: Releasing memory...'
        if(allocated(kohonen_map%grid)) then
            do iz=1,size(kohonen_map%grid,3)
                do iy=1,size(kohonen_map%grid,2)
                    do ix=1,size(kohonen_map%grid,1);
                        call kohonen_map%grid(ix,iy,iz)%destroy();
                    enddo
                enddo
            enddo
            deallocate(kohonen_map%grid);
         endif
!
         if(allocated(kohonen_map%number_patterns)) then
             deallocate(kohonen_map%number_patterns);
         endif
!
         if(allocated(kohonen_map%cells_index)) then
             deallocate(kohonen_map%cells_index);
         endif
!
         if(allocated(kohonen_map%u_matrix)) then
             deallocate(kohonen_map%u_matrix);
         endif
!
         if(allocated(kohonen_map%distance_function)) then
             deallocate(kohonen_map%distance_function);
         endif
!
         if(allocated(kohonen_map%distance)) then
             deallocate(kohonen_map%distance);
         endif
!
         if(allocated(kohonen_map%cells_distances)) then
             deallocate(kohonen_map%cells_distances);
         endif
!
         if(allocated(kohonen_map%coordinates)) then
             deallocate(kohonen_map%coordinates);
         endif
!
         if(allocated(kohonen_map%distortion)) then
             deallocate(kohonen_map%distortion)
         endif
   !
         if(allocated(kohonen_map%grid_pattern_index)) then
             deallocate(kohonen_map%grid_pattern_index);
         endif
   !
         if(allocated(kohonen_map%list_node_grid)) then
             deallocate(kohonen_map%list_node_grid);
         endif
         call kohonen_map%rnumber_grator%destroy();
!
!        write(*,*) 'SOM: Releasing memory...OK!'
!
    end subroutine destroy_som
!========================================================================================
    subroutine create_random_sample(kohonen_map,input)
!========================================================================================
!! Subroutine to generate random values that serve as inputs to the SOM
    class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
    real(kind=wp),dimension(:,:),intent(out) :: input 
!! A real array with the initial values of the prototypes
    integer :: nvar1,nvar2,i,j
!
    nvar1=size(input,1);
    nvar2=size(input,2);
    do i=1,nvar1;
        do j=1,nvar2;
            input(i,j)=kohonen_map%rnumber_grator%generate();
        end do
    end do
!    
    end subroutine create_random_sample
!========================================================================================
   subroutine train_som_data(kohonen_map,input_data)
!========================================================================================
!!   Training function for self_organizing_map 
      class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
      type(kohonen_pattern),dimension(:),intent(inout) :: input_data
!! A `kohonen_pattern` array with the input data
      integer :: iteration,iepoch,ipattern,ix,iy,iz,jhit,ihit,khit,ineigh,jneigh
      integer :: kneigh,idbg,number_variables,idisto !neff,
      integer :: cx,cy,cz,i,j,k,number_nodes,debug_option,ix1,iy1,iz1,pos,pos1,max_pattern
      integer :: ierr,nx,ny,nz,ipos
      integer :: current_pos,ic,itemp
      real(kind=wp) :: distortion,dist,dist_hit,maximum_radius,minimum_radius
      real(kind=wp) :: current_radius,alpha,u_temp
      type(kohonen_prototype) :: current_prototype
      real(kind=wp),dimension(kohonen_map%parameters%number_variables1,&
      kohonen_map%parameters%number_variables2) :: current_values
      integer,allocatable :: pattern_index(:,:,:,:),positions(:)
!
! 
!
      nx=kohonen_map%parameters%number_nodes_nx;
      ny=kohonen_map%parameters%number_nodes_ny;
      nz=kohonen_map%parameters%number_nodes_nz;
      allocate(positions(nx*ny*nz),stat=ierr);
      idbg=kohonen_map%parameters%idbg;
      idisto=kohonen_map%parameters%idisto;
      debug_option=kohonen_map%parameters%debug_level;
      if(debug_option > 0) then
      open(idbg,file=trim(kohonen_map%parameters%debug_file),status='unknown');
      endif
      iteration = 0;
      distortion = 0.0_wp;
      number_variables=kohonen_map%parameters%number_variables1*kohonen_map%parameters%number_variables2;
      maximum_radius=dble(max(kohonen_map%parameters%number_nodes_nx,kohonen_map%parameters%number_nodes_ny));
      minimum_radius=1.0_wp;
      write(*,*) 'SOM: Training starting...'
      do iepoch = 1,kohonen_map%parameters%number_epochs;
         kohonen_map%distortion(iepoch)=distortion;
         write(6,*) ' Starting epoch -- distortion',iepoch,' -- ',distortion;
        if(iepoch > 1) write(idisto,*) iepoch,distortion
         distortion = 0.0_wp;
         do ipattern = 1, kohonen_map%parameters%number_patterns;
            iteration = iteration + 1;
            ihit = 0;
            jhit = 0;
            khit = 0;
            dist_hit = 100000.0_wp;
            call input_data(ipattern)%get(current_prototype);
            call current_prototype%get_prototype(current_values);
            call kohonen_map%find_best_match_unit(current_prototype,ihit,jhit,khit,dist_hit);
            !write(*,*) 'Test= ',ipattern,ihit,jhit,khit,dist_hit
            if(debug_option > 0) then
               write(idbg,*) 'Epoch,Current Pattern',iepoch,ipattern;
               call current_prototype%print(idbg);
            endif            
            distortion = distortion + dist_hit;
            if(debug_option > 0) then
               write(idbg,*) 'Neighborhood,alpha= ',alpha;
            endif
            call kohonen_map%update_weights(current_values,ihit,jhit,khit,maximum_radius,iteration);
      !   
         enddo !ipattern
      enddo!iepoch
      !       write(*,*) 'SOM: Training finished'
      !       write(*,*) 'Total number of iterations= ',iteration
      !     print prototypes
      ! if(kohonen_map%parameters%train_option < 3) then
      ! do iz=1,size(kohonen_map%grid,3)
      !    !write(kohonen_map%parameters%iprot,'(A,I4)') 'Layer ',iz
      !    do iy=1,size(kohonen_map%grid,2);
      !       do ix=1,size(kohonen_map%grid,1);
      !          !write(kohonen_map%parameters%iprot,'(A6,1X,3I4)') 'node= ',ix,iy,iz            
      !          call kohonen_map%grid(ix,iy,iz)%print(kohonen_map%parameters%iprot);
      !       enddo
      !    enddo
      ! enddo!ix
      ! endif
      !     calculate and print distance matrix
      call kohonen_map%calculate_distance_between_prototypes();
      !     final best match
      !      call kohonen_map%find_bmu_grid(input_data);
      max_pattern=0;         
      do ipattern = 1, kohonen_map%parameters%number_patterns
         ihit = 0;
         jhit = 0;
         khit = 0;
         dist_hit = 100000.0_wp;
         call input_data(ipattern)%get(current_prototype);
         !call current_prototype%get_prototype(current_values);
         call kohonen_map%find_best_match_unit(current_prototype,ihit,jhit,khit,dist_hit);
         kohonen_map%number_patterns(ihit,jhit,khit)=kohonen_map%number_patterns(ihit,jhit,khit)+1;
         if(kohonen_map%number_patterns(ihit,jhit,khit) > max_pattern) then 
               max_pattern=kohonen_map%number_patterns(ihit,jhit,khit);
         endif
         kohonen_map%cells_index(ipattern,1)=ihit;
         kohonen_map%cells_index(ipattern,2)=jhit;
         kohonen_map%cells_index(ipattern,3)=khit;
         if(debug_option > 0) then
            write(idbg,*) ipattern,ihit,jhit,khit;
         endif
         !if(kohonen_map%parameters%train_option < 3) then
         !   write(kohonen_map%parameters%iindex,*) ipattern,ihit,jhit,khit
         !endif
         !         write(*,*) 'BMU= ',ipattern,ihit,jhit,khit,dist_hit
         !
      enddo !ipattern
      !
      allocate(pattern_index(size(kohonen_map%grid,1),&
         size(kohonen_map%grid,2),size(kohonen_map%grid,3),&
         max_pattern),stat=ierr);
      pattern_index=-1;         
      do ipattern=1,kohonen_map%parameters%number_patterns
         ix=kohonen_map%cells_index(ipattern,1);
         iy=kohonen_map%cells_index(ipattern,2);
         iz=kohonen_map%cells_index(ipattern,3);
         do i=1,max_pattern;
            if(pattern_index(ix,iy,iz,i) < 0) then
               pattern_index(ix,iy,iz,i)=ipattern;
               exit;
            endif
         enddo
      enddo!ipattern
      if(kohonen_map%parameters%train_option < 3) then
         do iz1=1,size(kohonen_map%grid,3);
            do iy1=1,size(kohonen_map%grid,2);
               do ix1=1,size(kohonen_map%grid,1);
                  write(kohonen_map%parameters%isam,'(A,3I4)') 'Node= ',ix1,iy1,iz1
                  if(kohonen_map%number_patterns(ix1,iy1,iz1) > 0) then
                     write(kohonen_map%parameters%isam,'(A,10000I5)') 'Sample ID= ',&
                     pattern_index(ix1,iy1,iz1,1:kohonen_map%number_patterns(ix1,iy1,iz1));
                  else
                     write(kohonen_map%parameters%isam,'(A,I4)') 'Sample ID= ',0
                  endif
               enddo
            enddo
         enddo
         deallocate(pattern_index);
      endif
      !
        if(debug_option .gt. 0) then 
            close(idbg);
        endif
        close(idisto);
    
      !     print hit counter
      if(kohonen_map%parameters%train_option < 3) then
         do iz=1,size(kohonen_map%grid,3)
            do ix=1,size(kohonen_map%grid,1);
               write(kohonen_map%parameters%ihit,'(100I5)') (kohonen_map%number_patterns(ix,iy,iz),&
                  iy=1,size(kohonen_map%grid,2));
            enddo!ix
         enddo
      endif
      call kohonen_map%calculate_u_matrix();
!
   end subroutine train_som_data
!========================================================================================
    subroutine predict_som(kohonen_map,input_data,map_output)
!========================================================================================
!! Function for Prediction of a self_organizing_map 
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
        type(kohonen_pattern),dimension(:),intent(inout) :: input_data
!! A `kohonen_pattern` array with the input data        
        integer,dimension(:,:),intent(out) :: map_output
!! An integer array with the map output
        integer :: ipattern,ihit,jhit,khit,ix,iy,iz,number_variables,i,j,k
        real(kind=wp) :: dist_hit,dist
        type(kohonen_prototype) :: current_prototype
        real(kind=wp),dimension(kohonen_map%parameters%number_variables1,&
        kohonen_map%parameters%number_variables2) :: current_values
!
        number_variables=kohonen_map%parameters%number_variables1*&
                        kohonen_map%parameters%number_variables2;
!
!       write(*,*) 'SOM: Prediction starting...';
!       write(*,*) number_variables
        do ipattern = 1, size(input_data,1)
            ihit = 0;
            jhit = 0;
            dist_hit = 100000.0_wp;
            call input_data(ipattern)%get(current_prototype);
            !call current_prototype%print();
            !write(*,*) ihit,jhit,dist_hit
            !call current_prototype%get_prototype(current_values);
            !$OMP parallel do         
            do iz=1,size(kohonen_map%grid,3)
                do iy = 1, size(kohonen_map%grid,2);  
                    do ix = 1, size(kohonen_map%grid,1);
                        dist = 0.0_wp;
                        !write(*,*) ix,iy,dist
                        !call kohonen_map%grid(ix,iy)%print();
                        dist=kohonen_map%grid(ix,iy,iz)%distance(current_prototype,&
                            kohonen_map%distance_function);
                        dist = dist/float(number_variables);
                        if (dist < dist_hit) then
                            dist_hit = dist;
                            ihit = ix;
                            jhit = iy;
                            khit = iz;
                        endif
                     enddo
                enddo
            enddo
            !         
            !$OMP end parallel do
            !         
            call kohonen_map%grid(ihit,jhit,khit)%get_prototype(current_values);
            ! if(size(current_values,2) .eq. 1) then 
            !   write(kohonen_map%parameters%iout,*) (current_values(i,1),&
            !         i=1,size(current_values,1));
            ! else
            !   do i=1,size(current_values,1)
            !      write(kohonen_map%parameters%iout,*) (current_values(i,j),j=1,&
            !            size(current_values,2))
            !   enddo
            ! endif
            !call map_output(ipattern)%create(current_values);
            map_output(ipattern,1)=ihit;
            map_output(ipattern,2)=jhit;
            map_output(ipattern,3)=khit;
            !size(current_values,1),size(current_values,2)
            !write(*,*) current_values
        enddo !ipattern
!       write(*,*) 'SOM: Prediction finished';
!
    end subroutine predict_som
!========================================================================================
    subroutine print_som(kohonen_map,unit_)
!========================================================================================
!!   Print function for self_organizing_map 
        class(self_organizing_map) :: kohonen_map
!!
        integer,intent(inout),optional :: unit_
!!
        integer :: ix,iy,iz,unit1
!
        if(.not. present(unit_)) then 
            unit1=6;
        else
            unit1=unit_;
        endif
        write(unit1,*) 'SOM: Results';
        write(unit1,*)
        call kohonen_map%parameters%print(unit1);
        ! write(unit1,*) 'After'
        write(unit1,*)
        write(unit1,*) 'SOM: Grid nodes';
        write(unit1,*)
        do iz=1,size(kohonen_map%grid,3)
            do iy=1,size(kohonen_map%grid,2);
                do ix=1,size(kohonen_map%grid,1);
                    call kohonen_map%grid(ix,iy,iz)%print(unit1);
                enddo
            enddo!iy
        enddo!ix
        write(unit1,*)
        write(unit1,*) 'SOM: Hit count';
        write(unit1,*)
        write(unit1,*) 'Pattern Numbers';
        do iz=1,size(kohonen_map%number_patterns,3);
            do ix=1,size(kohonen_map%number_patterns,1);
                write(unit1,'(100I5)') (kohonen_map%number_patterns(ix,iy,iz),iy=1,&
                   size(kohonen_map%number_patterns,2));
            enddo
        enddo
        write(unit1,*)
        write(*,*) 'SOM: Pattern index'
        write(unit1,*)
        write(unit1,*)
        write(unit1,*) 'Pattern #, ix   ,iy';

        do ix=1,size(kohonen_map%cells_index,1);
            write(unit1,'(100I5)') ix, (kohonen_map%cells_index(ix,iy),&
                iy=1,size(kohonen_map%cells_index,2));
        enddo
!
    end subroutine print_som
!========================================================================================
    subroutine get_count_som(kohonen_map,count_)
!========================================================================================
!!   Function to get count matrix for self_organizing_map 
        class(self_organizing_map) :: kohonen_map
!!
        integer,dimension(:,:,:),intent(inout) :: count_
!!
        count_=kohonen_map%number_patterns;
!   
    end subroutine get_count_som
!========================================================================================
    subroutine query_som(kohonen_map,input_pattern,sample_index) !,output_patterns)
!========================================================================================
!!   Function to find the input samples associated with specific vector 
        class(self_organizing_map) :: kohonen_map
!!
        real(kind=wp),dimension(:,:),intent(inout) :: input_pattern
!!
        integer,allocatable :: sample_index(:)
!!
        integer :: ix,iy,iz,ihit,jhit,khit,ivar1,ivar2,nvar1,nvar2,number_patterns,ipat,ierr
        integer :: number_selected,i,pos
        real(kind=wp),dimension(kohonen_map%parameters%number_variables1,&
        kohonen_map%parameters%number_variables2) ::current_values
        real(kind=wp) :: dist,dist_min
        integer,dimension(size(kohonen_map%cells_index,1)) :: position,real_position
!
!(real_position(ix)=ix,ix=1,size(real_position))
        do ix=1,size(real_position)
            real_position(ix)=ix;
        enddo
        nvar1=kohonen_map%parameters%number_variables1;
        nvar2=kohonen_map%parameters%number_variables2;
        dist_min=1.0d10;
        !$OMP parallel do   
        do iz=1,size(kohonen_map%grid,3);
             do iy=1,size(kohonen_map%grid,2);
                 do ix=1,size(kohonen_map%grid,1);
                     dist=0.0_wp;
                     call kohonen_map%grid(ix,iy,iz)%get_prototype(current_values);
                     do ivar1=1,nvar1;
                         do ivar2=1,nvar2;
                             if(input_pattern(ivar1,ivar2) > 0.0_wp) then
                                 dist=dist+(input_pattern(ivar1,ivar2)-current_values(ivar1,ivar2))**2;
                             endif
                         enddo
                     enddo
                     if(dist < dist_min) then
                         dist_min=dist;
                         ihit=ix;jhit=iy;khit=iz;               
                     endif
                 enddo
             enddo
         enddo
         !$OMP end parallel do
!         write(*,*) 'BMU'
!         write(*,*) ihit,jhit,khit,dist_min
!
         position=0;
         number_patterns=kohonen_map%number_patterns(ihit,jhit,khit);
         if(number_patterns > 0) then
             where(kohonen_map%cells_index(:,1) == ihit .and. &
                 kohonen_map%cells_index(:,2) == jhit .and. &
                 kohonen_map%cells_index(:,3) == khit )
                 position=1;!real_position;
             end where
             number_selected=sum(position);
             pos=0
             if(number_selected > 0) then
                 allocate(sample_index(number_selected),stat=ierr);
                 do i=1,size(real_position)
                     if(position(i) == 1) then
                         pos=pos+1;
                         sample_index(pos)=real_position(i);
                         !write(*,*) 'Inside= ',i,real_position(i)
                     endif
                 enddo
             endif
             !write(*,*) kohonen_map%cells_index(118,1:3)
         else 
             write(*,*) 'WARNING: Query has returned an empty result'
            return
         endif
!
    end subroutine query_som
!========================================================================================
    subroutine read_som(kohonen_map,som_fl)
!========================================================================================
!! Subroutine to read the prototypes to define a self_organizing_map 
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object      
        character(len=*) :: som_fl
!! A character variable with the name of the file
        logical :: testfl,toroidal_grid
        integer :: isom,nx,ny,nz,nvar1,nvar2,ierr,ix,iy,iz,ivar,current_index
        character(len=40) :: current_line,node_type
        real(kind=wp),allocatable :: Prototype_value(:,:)
!
        isom=20;
        inquire(file=trim(som_fl),exist=testfl);
        if(.not. testfl) then
            write(*,*) 'ERROR: the som file does not exist'
            stop
        endif
!
        write(*,*)
        write(*,*) 'SOM: Reading SOM Prototypes...'
        write(*,*)
        open(isom,file=trim(som_fl),status='unknown',action='read',access='sequential');
        read(isom,'(A)') current_line
        write(*,*) trim(current_line)
        read(isom,'(A17,1X,3I6)') current_line,nx,ny,nz
        write(*,*) current_line,nx,ny,nz
        read(isom,'(A21,1X,2I6)') current_line,nvar1,nvar2
        write(*,*) current_line,nvar1,nvar2
        read(isom,'(A25,1X,A11,1X,L4)') current_line,node_type,toroidal_grid
        write(*,*) current_line,node_type,toroidal_grid
        allocate(Prototype_value(nvar1*nvar2,1),stat=ierr);
   !
        if(allocated(kohonen_map%grid)) then
            do iz=1,nz
                do iy=1,ny
                    do ix=1,nx
                        call kohonen_map%grid(ix,iy,iz)%destroy();
                    enddo
                enddo
            enddo
            deallocate(kohonen_map%grid);
        endif
        if(allocated(kohonen_map%coordinates)) then
            deallocate(kohonen_map%coordinates);
        endif
        allocate(kohonen_map%grid(nx,ny,nz),stat=ierr);
        allocate(kohonen_map%coordinates(nx*ny*nz,3),stat=ierr);
        allocate(kohonen_map%cells_distances(nx*ny*nz,nx*ny*nz),stat=ierr);
        do iz=1,nz
            read(isom,'(A)') current_line;
            write(*,*) 'Reading ',trim(current_line);
            do iy=1,ny
                do ix=1,nx;
                    read(isom,'(A)') current_line;
!                   write(*,*) current_line
                    read(isom,'(A)') current_line;
!                   write(*,*) current_line
                    read(isom,*) (Prototype_value(ivar,1),ivar=1,nvar1*nvar2);
                    !write(*,*) ix,iy,(Prototype_value(ivar,1),ivar=1,nvar1*nvar2)
                    call kohonen_map%grid(ix,iy,iz)%set_prototype(Prototype_value);
                    current_index=position2index(ix,iy,iz,nx,ny);
                    call calculate_coordinates(current_index,ix,iy,iz,nx,ny,nz,&
                           kohonen_map%coordinates,node_type);
                enddo
            enddo
         enddo
         close(isom)
         !write(*,*) 'Reading done'
         !
         call calculate_distance_matrix(kohonen_map%coordinates,kohonen_map%cells_distances,&
               node_type,toroidal_grid);   
   !
         write(*,*)
         write(*,*) 'SOM: Reading SOM Prototypes...finished'
         write(*,*)
!
     end subroutine read_som
!========================================================================================
    function position2index(ix,iy,iz,nx,ny) result(index_)
!========================================================================================
!! Function to calculate the index inside a rectangular grid from position ix,iy,iz
        integer,intent(in) :: ix,iy,iz,nx,ny
!! Integer variables
        integer ::index_
!! Integer variable with the required index
        index_=ix+(iy-1)*nx+(iz-1)*nx*ny;
!
    end function position2index
!========================================================================================
    subroutine index2position(index_,nx,ny,nz,cx,cy,cz)
!========================================================================================
!! Subroutine to calculate the position ix,iy,iz inside a rectangular grid from index
        integer,intent(in) :: index_
!! Integer variable representing the index
        integer,intent(in) :: nx,ny,nz
!! Integer variables representing the dimensions of the kohonen map
        integer,intent(inout) :: cx,cy,cz
!! Integer variables representing the position of the node
!  write(*,*) index_,nx,ny,1+int((index_-1)/(nx*ny))
        cz=min(1+int((index_-1)/(nx*ny)),nz);
        cy=min(1+int((index_-1-(cz-1)*nx*ny)/nx),ny);
        cx=min(index_-(cz-1)*nx*ny-(cy-1)*nx,nx);
!
    end subroutine index2position
!========================================================================================
    subroutine calculate_distance_matrix(coordinates,distance_matrix,grid_type,toroidal)
!========================================================================================
!! Subroutine to calculate the distance between the units inside a kohonen layer 
        real(kind=wp),dimension(:,:),intent(inout) :: coordinates
!! Real array with the coordinates
        real(kind=wp),dimension(:,:),intent(inout) :: distance_matrix
!! Real array with the distance_matrix
        character(len=*) :: grid_type
!! Character variable with the grid type
        logical :: toroidal
!! Logical variable for toroidal grid
        integer :: i,j
        real(kind=wp) :: maxdiffx,maxdiffy,maxdiffz
        real(kind=wp),dimension(3) :: diffs
!
        maxdiffx=maxval(coordinates(:,1))/2.0_wp;
        maxdiffy=maxval(coordinates(:,2))/2.0_wp;
        maxdiffz=maxval(coordinates(:,3))/2.0_wp;
!
        distance_matrix=0.0d0;
!
        if(toroidal) then
            do i=1,size(distance_matrix,1);
                do j=i+1,size(distance_matrix,2);
                    diffs=dabs(coordinates(j,1:3) - coordinates(i,1:3));
                    if (diffs(1) > maxdiffx) diffs(1)=2.0_wp*maxdiffx - diffs(1);
                    if (diffs(2) > maxdiffy) diffs(2)=2.0_wp*maxdiffy - diffs(2);
                    !if (diffs(3) > maxdiffy) diffs(3)=2*maxdiffz - diffs(3);
                    if (trim(grid_type) == 'hexagonal') then
                        distance_matrix(i,j)=sum(diffs**2);
                    elseif(trim(grid_type) == 'rectangular') then!rectangular
                       distance_matrix(i,j)=maxval(diffs);
                    endif
                    !write(*,*) 'd= ',i,j,diffs(1:3),trim(grid_type)!distance_matrix(i,j)
                enddo
            enddo
        else
            do i=1,size(distance_matrix,1);
                do j=i+1,size(distance_matrix,2);
                   diffs=dabs(coordinates(j,1:3) - coordinates(i,1:3));
                   distance_matrix(i,j)=dsqrt(sum(diffs**2));
                enddo
            enddo
        endif
      !
        distance_matrix=distance_matrix + transpose(distance_matrix);
!
    end subroutine calculate_distance_matrix
!========================================================================================
    subroutine calculate_coordinates(current_index,ix,iy,iz,nx,ny,nz,coordinates,node_type)
!========================================================================================
!!  Subroutine to calculate the coordinates of the units inside a kohonen layer 
        integer,intent(in) :: current_index,ix,iy,iz,nx,ny,nz
!!
        real(kind=wp),dimension(:,:),intent(out) :: coordinates
!!
        character(len=*),intent(in) :: node_type
!!
        coordinates(current_index,1)=dble(ix);
        coordinates(current_index,2)=dble(iy);
        coordinates(current_index,3)=dble(iz);
        !write(*,*) coordinates(current_index,1:3);
        if(trim(node_type) == 'hexagonal') then
            coordinates(current_index,1)=coordinates(current_index,1)+&
                  .5_wp*(mod(coordinates(current_index,2),2.0_wp));
            coordinates(current_index,2)=(dsqrt(3.0_wp)/2.0_wp)*coordinates(current_index,2);
        endif
!
    end subroutine calculate_coordinates
!========================================================================================
    subroutine find_best_match_unit(kohonen_map,current_prototype,ihit,jhit,khit,dist_hit)
!========================================================================================
!! Subroutine to calculate the best match unit
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
        type(kohonen_prototype),intent(inout) :: current_prototype
!! A `kohonen_prototype` object
        integer,intent(out) :: ihit,jhit,khit
!! Integer variables for the coordinates of the BMU
        real(kind=wp),intent(out) :: dist_hit
!! Real variable with the distance to the BMU
        integer :: debug_option,idbg,ix,iy,iz,number_variables
        real(kind=wp) :: dist
!
        idbg=kohonen_map%parameters%idbg;
        debug_option=kohonen_map%parameters%debug_level;
        number_variables=kohonen_map%parameters%number_variables1*&
                       kohonen_map%parameters%number_variables2
        ihit = 0;
        jhit = 0;
        khit = 0;
        dist_hit = 1.0e7;
        !$OMP parallel do   
        do iz = 1, size(kohonen_map%grid,3)  
            do iy = 1, size(kohonen_map%grid,2)
                do ix = 1,size(kohonen_map%grid,1)
                    dist = 0.0_wp;
                    dist=kohonen_map%grid(ix,iy,iz)%distance(current_prototype,&
                        kohonen_map%distance_function);
                    !write(*,*) 'dist= ',dist
                    if(debug_option > 0) then
                        call kohonen_map%grid(ix,iy,iz)%print(idbg);
                        write(idbg,*) ix,iy,iz,dist;
                    endif
                    dist = dist/float(number_variables);
                    if (dist < dist_hit) then
                        dist_hit = dist;
                        ihit = ix;
                        jhit = iy;
                        khit = iz;
                    endif
                enddo!ix
            enddo!iy
         enddo!iz
         !$OMP end parallel do   
!
!        write(*,*) 'find= ',ihit,jhit,khit,dist_hit
      return
!
    end subroutine find_best_match_unit
!========================================================================================
    subroutine update_weights(kohonen_map,current_values,ihit,jhit,khit,&
        maximum_radius,iteration) 
!========================================================================================
!!    Subroutine to update the weights   
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
        real(kind=wp),dimension(:,:),intent(inout) :: current_values
!! A real array with the values of the current unit
        integer,intent(inout) :: ihit,jhit,khit,iteration
!! Integer variables with the coordinates of the unit (neuron) to be modified
        real(kind=wp),intent(inout) :: maximum_radius
!! Real variable with the maximum radius of the neighborhood 
        real(kind=wp),dimension(size(current_values,1),size(current_values,2)) :: prototype_values
        real(kind=wp),dimension(size(current_values,1),size(current_values,2)) :: winner_values,term1,term2
        integer :: nx,ny,nz,debug_option,ic,current_pos,ineigh,jneigh,kneigh,idbg
        real(kind=wp) :: time_factor,current_radius,alpha,sigma2,h_neighborhood,real_distance,term3
        real(kind=wp) :: distance_ratio,geometric_distance2,eps,current_distance,lambda
        !type(influence_function) :: influence_func
        real(kind=wp),dimension(size(current_values,1),size(current_values,2)) :: v_vector
        real(kind=wp) :: v_vector_norm,r,Psi
        character(len=NUMCHAR) :: m_estimator
!
        nx=kohonen_map%parameters%number_nodes_nx;
        ny=kohonen_map%parameters%number_nodes_ny;
        nz=kohonen_map%parameters%number_nodes_nz;
        debug_option=kohonen_map%parameters%debug_level;
        idbg=kohonen_map%parameters%idbg;
        lambda=2.0_wp*(1.0_wp/maximum_radius);
        time_factor=1.0_wp-dble(iteration)/&
                 dble(kohonen_map%parameters%number_epochs*kohonen_map%parameters%number_patterns);
        !current_radius = max(maximum_radius*real(1001-iteration)/1000.0 + 0.9999999999,4.0d0);
        current_radius = max(maximum_radius*time_factor,4.0_wp);
        !alpha = max(kohonen_map%parameters%learning_rate*(1.0d0-real(iteration)/1000.0),0.01d0);
        alpha = max(kohonen_map%parameters%learning_rate*time_factor,0.01_wp);
        sigma2=current_radius**2;
        !
        m_estimator=trim(kohonen_map%parameters%m_estimator);  
!
        do ic=1,size(kohonen_map%coordinates,1)
            current_pos=position2index(ihit,jhit,khit,nx,ny);
            current_distance=kohonen_map%cells_distances(current_pos,ic)
            if(current_distance < current_radius) then
                geometric_distance2=current_distance**2;
                call index2position(ic,nx,ny,nz,ineigh,jneigh,kneigh);
                !write(*,*) ic,ineigh,jneigh,kneigh,ihit,jhit,khit
                select case(trim(kohonen_map%parameters%neighborhood_type))
                    case('gaussian')
                        h_neighborhood=alpha*dexp(-0.5_wp*geometric_distance2/sigma2);
                    case('bubble')
                        h_neighborhood=alpha;
                end select
                if(debug_option > 0) then
                    write(idbg,*) ihit,jhit,khit,ineigh,jneigh,kneigh
                endif
                select case(trim(kohonen_map%parameters%som_type))
                    case('normal_som')                      
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%get_prototype(prototype_values);
                        prototype_values=prototype_values+h_neighborhood*(current_values-prototype_values);
                        !v_vector=(current_values-prototype_values);
                        !v_vector_norm=dsqrt(sum(v_vector**2));
                        !r=v_vector_norm/sigma;
                        !Psi=influence_func%calculate(m_estimator,r);
                        !prototype_values=prototype_values+sigma*h_neighborhood*Psi*v_vector/v_vector_norm;
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%set_prototype(prototype_values);
                    case('visom')
                        !write(*,*) trim(kohonen_map%parameters%som_type)
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%get_prototype(prototype_values);
                        call kohonen_map%grid(ihit,jhit,khit)%get_prototype(winner_values);
                        real_distance=sum((winner_values-prototype_values)**2);
                        if( (ineigh == ihit) .and. (jneigh == jhit) .and. (kneigh == khit) ) then                           
                             prototype_values=prototype_values+h_neighborhood*(current_values-prototype_values);
                        else
                             distance_ratio=dsqrt(real_distance)/(dsqrt(geometric_distance2)*lambda);
                             term1=(current_values-winner_values);
                             term2=(winner_values-prototype_values);
                             eps=max(1.0_wp*time_factor,0.0_wp);
                             term3=1.0_wp;!((1.0d0-eps)+eps)
                             prototype_values=prototype_values+h_neighborhood*(term1+term2*&
                                         (distance_ratio-1.0_wp)*term3);
                        endif
                        !write(*,*) iteration,dsqrt(real_distance),dsqrt(geometric_distance2)*lambda,distance_ratio
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%set_prototype(prototype_values); 
                    case('robust_som')
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%get_prototype(prototype_values);
                        prototype_values=prototype_values+h_neighborhood*(current_values-prototype_values);
                        ! v_vector=(current_values-prototype_values);
                        ! v_vector_norm=dsqrt(sum(v_vector**2));
                        ! r=v_vector_norm/sigma;
                        ! Psi=influence_func%calculate(m_estimator,r);
                        ! prototype_values=prototype_values+sigma*h_neighborhood*Psi*v_vector/v_vector_norm;
                        call kohonen_map%grid(ineigh,jneigh,kneigh)%set_prototype(prototype_values);
                end select
            endif
        enddo!ic
!
    end subroutine update_weights
!========================================================================================
    subroutine calculate_distance_between_prototypes(kohonen_map)
!========================================================================================
!! Subroutine to calculate the distance between the prototypes
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
        integer :: nx,ny,ix,iy,iz,ix1,iy1,iz1,pos,pos1
!
        type(kohonen_prototype) :: current_prototype,current_prototype1
!!
        nx=kohonen_map%parameters%number_nodes_nx;
        ny=kohonen_map%parameters%number_nodes_ny;
        !$OMP parallel do  
        do iz=1,size(kohonen_map%grid,3)
            do iy=1,size(kohonen_map%grid,2);
                do ix=1,size(kohonen_map%grid,1);
                    current_prototype=kohonen_map%grid(ix,iy,iz);
                    pos=position2index(ix,iy,iz,nx,ny);
                    do iz1=1,size(kohonen_map%grid,3);
                        do iy1=1,size(kohonen_map%grid,2);
                            do ix1=1,size(kohonen_map%grid,1);
                                pos1=position2index(ix1,iy1,iz1,nx,ny)
                                current_prototype1=kohonen_map%grid(ix1,iy1,iz1);
                                kohonen_map%distance(pos,pos1)=current_prototype1%distance(current_prototype,&
                                      kohonen_map%distance_function);
                            enddo!ix1
                        enddo!iy1  
                    enddo!iz1
                enddo!ix
            enddo!iy         
        enddo!iz
        !$OMP end parallel do  
!
        if(kohonen_map%parameters%train_option < 3) then
            do ix=1,size(kohonen_map%distance,1)
                write(kohonen_map%parameters%idist,*) (kohonen_map%distance(ix,iy),iy=1,size(kohonen_map%distance,2));
            enddo!ix
        endif
! 
    end subroutine calculate_distance_between_prototypes
!========================================================================================
   subroutine find_bmu_grid(kohonen_map,input_data)
!========================================================================================
!! Subroutine to calculate the best match unit over the grid  
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object  
        type(kohonen_pattern),dimension(:),intent(inout) :: input_data
!! A `kohonen_pattern` array with the input data
        integer :: nx,ny,nz,ix,iy,iz,ihit,jhit,khit,idat,pat_hit
        type(kohonen_prototype) :: current_prototype
        real(kind=wp) :: dist,dist_min
!
        do idat=1,size(input_data)
            dist_min=1.0e7;ihit=0;jhit=0;khit=0;
            call input_data(idat)%get(current_prototype);
            !$OMP parallel do    
            do iz=1,size(kohonen_map%grid,3)
                do iy=1,size(kohonen_map%grid,2)
                    do ix=1,size(kohonen_map%grid,1)
                        dist=kohonen_map%grid(ix,iy,iz)%distance(current_prototype,kohonen_map%distance_function);
                        dist = dist/float(kohonen_map%parameters%number_variables)
                        if(dist < dist_min) then
                            dist_min=dist;
                            ihit=ix;jhit=iy;khit=iz;pat_hit=idat;
                        endif
                    enddo
                enddo
            enddo
            !$OMP end parallel do    
            kohonen_map%grid_pattern_index(ihit,jhit,khit)=pat_hit;
            kohonen_map%cells_index(idat,1)=ihit;
            kohonen_map%cells_index(idat,2)=jhit;
            kohonen_map%cells_index(idat,3)=khit;
            !    write(*,*) 'BMU= ',idat,ihit,jhit,khit,dist_min
        enddo
!
!
    end subroutine find_bmu_grid
!========================================================================================
    subroutine calculate_u_matrix(kohonen_map)
!========================================================================================
!! Subroutine to calculate  the u_matrix
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
        character(len=NUMCHAR) :: type_
        integer :: nx,ny,nz,nt,ierr,ix,iy,iz,cx,cy,cz,nxu,nyu,nzu
        real(kind=wp) :: dist,u_temp
!
        type_=trim(kohonen_map%parameters%node_type);
        nx=kohonen_map%parameters%number_nodes_nx;
        ny=kohonen_map%parameters%number_nodes_ny;
        nz=kohonen_map%parameters%number_nodes_nz;
!
        nxu=size(kohonen_map%u_matrix,1);
        nyu=size(kohonen_map%u_matrix,2);
        nzu=size(kohonen_map%u_matrix,3);
!
        select case(trim(type_))
! 
            case('rectangular')
                !call kohonen_map%calculate_u_matrix_rectangular();
!
                 do iz=1,size(kohonen_map%grid,3);
                     do iy=1,size(kohonen_map%grid,2);
                         do ix=1,size(kohonen_map%grid,1);
                             ! horizontal
                             if(ix<nx) then
                                 cx=ix+1;cy=iy;cz=iz;               
                                 dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                             kohonen_map%distance_function);
                                 kohonen_map%u_matrix(2*ix,2*iy-1,2*iz-1)=dist;
                             endif
                             !vertical
                             if(iy<ny) then
                                 cx=ix;cy=iy+1;cz=iz;               
                                 dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                                 kohonen_map%distance_function);
                                 kohonen_map%u_matrix(2*ix-1,2*iy,2*iz-1)=dist;              
                             endif
                             !
                             if(iz<nz) then
                                 cx=ix;cy=iy;cz=iz+1;               
                                 dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                                 kohonen_map%distance_function);
                                kohonen_map%u_matrix(2*ix-1,2*iy-1,2*iz-1)=dist;         
                             endif
                             ! Diagonal
                             if(ix < nx .and. iy < ny) then
                                 cx=ix+1;cy=iy+1;cz=iz;
                                 dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                               kohonen_map%distance_function);
                                 cx=ix+1;cy=iy+1;cz=iz;
                                 dist=dist+kohonen_map%grid(ix,cy,iz)%distance(kohonen_map%grid(cx,iy,cz),&
                                               kohonen_map%distance_function);
                                 kohonen_map%u_matrix(2*ix,2*iy,2*iz-1)=dist;         
                             endif
                         enddo
                     enddo
                 enddo
!
                 do iz=1,size(kohonen_map%u_matrix,3),2
                     do iy=1,size(kohonen_map%u_matrix,2),2
                         do ix=1,size(kohonen_map%u_matrix,1),2
                             u_temp=0.0d0;
                             if(ix>1 .and. ix<size(kohonen_map%u_matrix,1) .and. & 
                                iy>1 .and. iy<size(kohonen_map%u_matrix,2)) then
                                  u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix+1,iy,iz)+&
                                     kohonen_map%u_matrix(ix,iy-1,iz)+kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=4;
                             elseif(iy==1 .and. ix>1 .and. ix<size(kohonen_map%u_matrix,1)) then
                                  u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix+1,iy,iz)+&
                                     kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=3;
                             elseif(iy==size(kohonen_map%u_matrix,2) .and. ix>1 .and.&
                                ix<size(kohonen_map%u_matrix,1)) then
                                  u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix+1,iy,iz)+&
                                      kohonen_map%u_matrix(ix,iy-1,iz);
                                  nt=3;
                             elseif(ix==1 .and. iy>1 .and. iy<size(kohonen_map%u_matrix,2)) then
                                  u_temp = kohonen_map%u_matrix(ix+1,iy,iz)+&
                                  kohonen_map%u_matrix(ix,iy-1,iz)+kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=3;
                             elseif(ix==size(kohonen_map%u_matrix,1) .and. iy>1 .and. iy<size(kohonen_map%u_matrix,2)) then
                                  u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+&
                                  kohonen_map%u_matrix(ix,iy-1,iz)+kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=3;
                             elseif(ix==1 .and. iy==1) then
                                  u_temp = kohonen_map%u_matrix(ix+1,iy,iz)+kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=2;
                             elseif( ix==size(kohonen_map%u_matrix,1) .and. iy==1) then
                                  u_temp=kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix,iy+1,iz);
                                  nt=2;
                             elseif(ix==1 .and. iy==size(kohonen_map%u_matrix,2)) then
                                  u_temp=kohonen_map%u_matrix(ix+1,iy,iz)+kohonen_map%u_matrix(ix,iy-1,iz);
                                  nt=2;
                             elseif( ix==size(kohonen_map%u_matrix,1) .and. iy==size(kohonen_map%u_matrix,2)) then
                                  u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix,iy-1,iz);
                                  nt=2;
                             else
                                  u_temp = 0.0_wp;
                             endif
                             kohonen_map%u_matrix(ix,iy,iz)=u_temp/dble(nt);
                         enddo
                     enddo
                 enddo
!
            case('hexagonal')
                !call kohonen_map%calculate_u_matrix_hexagonal();
         !
                do iz=1,size(kohonen_map%grid,3);
                    do iy=1,size(kohonen_map%grid,2);
                        do ix=1,size(kohonen_map%grid,1);
                            if(ix < nx) then !horizontal
                                cx=ix+1;cy=iy;cz=iz;               
                                dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                                kohonen_map%distance_function);
                                kohonen_map%u_matrix(2*ix,2*iy-1,2*iz-1)=dist;
                            endif
                        !
                            if(iy < ny) then !diagonals
                                cx=ix;cy=iy+1;cz=iz;               
                                dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                              kohonen_map%distance_function);
                                kohonen_map%u_matrix(2*ix-1,2*iy,2*iz-1)=dist;
                                if(mod(iy,2)==0 .and. ix < nx) then
                                    cx=ix+1;cy=iy+1;cz=iz;
                                    dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                               kohonen_map%distance_function);
                                    kohonen_map%u_matrix(2*ix,2*iy,2*iz-1)=dist;               
                                elseif(mod(iy,2)==1 .and. ix>1) then
                                    cx=ix-1;cy=iy+1;cz=iz;
                                    dist=kohonen_map%grid(ix,iy,iz)%distance(kohonen_map%grid(cx,cy,cz),&
                                              kohonen_map%distance_function);
                                    kohonen_map%u_matrix(2*ix-2,2*iy,2*iz-1)=dist;
                                endif
                            endif
                        enddo
                    enddo
                enddo
      !
            do iz=1,nzu,2;
                do iy=1,nyu,2;
                    do ix=1,nxu,2;
                        u_temp=0.0d0;
                        if(ix>1 .and. iy>1 .and. ix<nxu .and. iy<nyu ) then !middle part of the map
                            u_temp = kohonen_map%u_matrix(ix-1,iy,iz) + kohonen_map%u_matrix(ix+1,iy,iz);
                            if (mod(iy-1,4)==0) then
                                u_temp = u_temp +  kohonen_map%u_matrix(ix-1,iy-1,iz) + kohonen_map%u_matrix(ix,iy-1,iz) + &
                                         kohonen_map%u_matrix(ix-1,iy+1,iz)+ kohonen_map%u_matrix(ix,iy+1,iz);                
                            else 
                                u_temp = u_temp+ kohonen_map%u_matrix(ix,iy-1,iz)+ kohonen_map%u_matrix(ix+1,iy-1,iz) +&
                                         kohonen_map%u_matrix(ix,iy+1,iz) +  kohonen_map%u_matrix(ix+1,iy+1,iz); 
                            endif
                            nt=6;
                        elseif(iy==1 .and. ix>1 .and. ix<nxu ) then ! upper edge
                            u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+kohonen_map%u_matrix(ix+1,iy,iz)+&
                                   kohonen_map%u_matrix(ix-1,iy+1,iz) + kohonen_map%u_matrix(ix,iy+1,iz);
                            nt=4;
                        elseif(iy==nyu .and. ix>1 .and. ix<nxu) then ! lower edge
                            u_temp = kohonen_map%u_matrix(ix-1,iy,iz)+ kohonen_map%u_matrix(ix+1,iy,iz);
                            if (mod(iy-1,4)==0) then
                                u_temp = u_temp + kohonen_map%u_matrix(ix-1,iy-1,iz) + kohonen_map%u_matrix(ix,iy-1,iz);
                            else 
                                u_temp = u_temp + kohonen_map%u_matrix(ix,iy-1,iz) + kohonen_map%u_matrix(ix+1,iy-1,iz); 
                            endif
                            nt=4;
                        elseif( ix==1 .and. iy>1 .and. iy<nyu) then ! left edge
                            u_temp = kohonen_map%u_matrix(ix+1,iy,iz);
                            if(mod(iy-1,4)==0) then
                                u_temp = u_temp + kohonen_map%u_matrix(ix,iy-1,iz)+ kohonen_map%u_matrix(ix,iy+1,iz);
                                nt=3;
                            else 
                                u_temp = u_temp + kohonen_map%u_matrix(ix,iy-1,iz) + kohonen_map%u_matrix(ix+1,iy-1,iz) +&
                                         kohonen_map%u_matrix(ix,iy+1,iz) + kohonen_map%u_matrix(ix+1,iy+1,iz); 
                                nt=5;
                            endif             
                        elseif(ix==nxu .and. iy>1 .and. iy<nyu) then ! right edge
                            u_temp = kohonen_map%u_matrix(ix-1,iy,iz);
                            if (mod(iy-1,4)==0) then
                                u_temp= u_temp + kohonen_map%u_matrix(ix,iy-1,iz) + kohonen_map%u_matrix(ix-1,iy-1,iz) +&
                                         kohonen_map%u_matrix(ix,iy+1,iz) + kohonen_map%u_matrix(ix-1,iy+1,iz);
                                nt=5;        
                            else 
                                u_temp = u_temp + kohonen_map%u_matrix(ix,iy-1,iz) + kohonen_map%u_matrix(ix,iy+1,iz);
                                nt=3;
                            endif
                        elseif(ix==1 .and. iy==1) then ! top left corner
                            u_temp = kohonen_map%u_matrix(ix+1,iy,iz) + kohonen_map%u_matrix(ix,iy+1,iz);
                            nt=2;
                        elseif(ix==nxu .and. iy==1) then ! top right corner
                            u_temp = kohonen_map%u_matrix(ix-1,iy,iz) +  kohonen_map%u_matrix(ix-1,iy+1,iz) +&
                                  kohonen_map%u_matrix(ix,iy+1,iz);
                            nt=3;
                        elseif(ix==1 .and. iy==nyu) then ! bottom left corner
                            if (mod(iy-1,4)==0) then
                                u_temp = kohonen_map%u_matrix(ix+1,iy,iz) + kohonen_map%u_matrix(ix,iy-1,iz);
                                nt=2;
                            else 
                                u_temp = kohonen_map%u_matrix(ix+1,iy,iz) + kohonen_map%u_matrix(ix,iy-1,iz) +&
                                         kohonen_map%u_matrix(ix+1,iy-1,iz); 
                                nt=3;
                            endif;
                        elseif(ix==nxu .and. iy==nyu) then ! bottom right corner
                            if (mod(iy-1,4)==0) then
                                u_temp = kohonen_map%u_matrix(ix-1,iy,iz) + kohonen_map%u_matrix(ix,iy-1,iz) +&
                                         kohonen_map%u_matrix(ix-1,iy-1,iz);
                                nt=3;
                            else 
                                u_temp = kohonen_map%u_matrix(ix-1,iy,iz) + kohonen_map%u_matrix(ix,iy-1,iz);
                                nt=2;
                            endif
                        endif
                        kohonen_map%u_matrix(ix,iy,iz)=u_temp/dble(nt);
                    enddo
                enddo
            enddo
         !
        end select
!
        if(kohonen_map%parameters%train_option < 3) then
            do iz=1,size(kohonen_map%u_matrix,3);
                write(kohonen_map%parameters%iumat,'(A,I4)') 'Layer ',iz 
                do ix=1,size(kohonen_map%u_matrix,1);
                    write(kohonen_map%parameters%iumat,'(100f10.5)') (kohonen_map%u_matrix(ix,iy,iz),&
                      iy=1,size(kohonen_map%u_matrix,2));
                enddo
            enddo
        endif
!
    end subroutine calculate_u_matrix
!========================================================================================
    subroutine calculate_u_matrix_hexagonal(kohonen_map)
!========================================================================================
!! Subroutine to calculate the u_matrix for an hexagonal grid
      class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object
    
    end subroutine calculate_u_matrix_hexagonal
!========================================================================================
    subroutine calculate_u_matrix_rectangular(kohonen_map)
!========================================================================================
!! Subroutine to calculate the u_matix for a rectangular grid
        class(self_organizing_map) :: kohonen_map 
!! A `self_organizing_map` object        
    end subroutine calculate_u_matrix_rectangular 
!========================================================================================
    subroutine get_u_matrix_som(kohonen_map,u_matrix)
!========================================================================================
!! Subroutine to get the u_matrix from a SOM
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object 
        real(kind=wp),dimension(:,:,:),intent(out) :: u_matrix
!! A real array to return the u_matrix
        u_matrix=kohonen_map%u_matrix;
!
    end subroutine get_u_matrix_som
!========================================================================================
    subroutine get_prototypes(kohonen_map,prototypes)
!========================================================================================
!! Subroutine to get SOM prototypes
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object 
        real(kind=wp),dimension(:,:),intent(out) :: prototypes
!! A real array to return the values of the SOM prototypes
        integer :: i,j,k,pos,nvar1,nvar2
        integer,dimension(1) :: nvar
        real(kind=wp),dimension(kohonen_map%parameters%number_variables1,&
                    kohonen_map%parameters%number_variables2) :: current_prototype
        real(kind=wp),dimension(kohonen_map%parameters%number_variables1*&
                    kohonen_map%parameters%number_variables2) :: current_prototype1
      !
        nvar1=kohonen_map%parameters%number_variables1;
        nvar2=kohonen_map%parameters%number_variables2;
        nvar(1)=nvar1*nvar2
        pos=0;
        do k=1,size(kohonen_map%grid,3)
            do j=1,size(kohonen_map%grid,2);
                do i=1,size(kohonen_map%grid,1);
                    pos=pos+1;
                    call kohonen_map%grid(i,j,k)%get_prototype(current_prototype);
                    current_prototype1(1:nvar1*nvar2)=reshape(current_prototype,nvar)
                    prototypes(pos,:)=current_prototype1;
                enddo
            enddo
        enddo
!
    end subroutine get_prototypes
!========================================================================================
    function calculate_sigma(kohonen_map,input_data,seed) result(sigma)
!========================================================================================
!!    Function to calculate the scaling factor sigma
        class(self_organizing_map) :: kohonen_map
!! A `self_organizing_map` object 
        real(kind=wp),dimension(:,:),intent(inout) :: input_data
!! A real array with the input data
        integer,intent(inout),optional :: seed
!! An integer with the random seed
        real(kind=wp) :: sigma
!! A real variable with the value of sigma
        integer :: ndat,nvar,seed1,nx,ny,nz,nxyz,ierr,i,j
        real(kind=wp),allocatable :: sample_pos(:),p_vector(:,:),sigma_table(:,:)
        real(kind=wp),allocatable :: current_sigma(:)
        integer,allocatable :: sample_index(:)
        type(quicksort) :: qsort
!
        if(.not. present(seed)) then 
            seed1=12345;
        else 
            seed1=seed;
        endif
        !
        ndat=size(input_data,1);
        nvar=size(input_data,2);
        !
        !kohonen_map%parameters=training_parameters(1);
        nx=kohonen_map%parameters%number_nodes_nx;
        ny=kohonen_map%parameters%number_nodes_ny;
        nz=kohonen_map%parameters%number_nodes_nz;
        nxyz=nx*ny*nz;
        allocate(sample_pos(ndat),stat=ierr);
        allocate(sample_index(ndat),stat=ierr);
        allocate(p_vector(nxyz,nvar),stat=ierr);
        allocate(sigma_table(ndat,nxyz),stat=ierr);
        allocate(current_sigma(ndat),stat=ierr);
        !call sgrnd(seed1);
        do i=1,size(sample_pos);
            sample_pos(i)=kohonen_map%rnumber_grator%generate();
        enddo
        !call grnd_array(sample_pos);
        do i=1,nxyz
            sample_index(i)=i;
        enddo
        !
        call qsort%sort(sample_pos,sample_index);
        !      
        !  define p vector (See Lopez-Rubio et al, 2015)
        !
        p_vector(1:nxyz,1:nvar)=input_data(sample_index(1:nxyz),1:nvar);
        !   
        !  Calculate the distance between the input data and the selected prototypes
        !
        do i=1,ndat
            do j=1,nxyz
                sigma_table(i,j)=sum((input_data(i,:)-p_vector(j,:))**2);
           enddo
        enddo
      !
        do j=1,nxyz
            current_sigma(1:ndat)=sigma_table(1:ndat,j);
            !(sample_index(i)=i,i=1,ndat)
            call qsort%sort(current_sigma,sample_index);
            !    if(current_sigma(1) > 1d-10) then
            !       current_sigma_value()
        enddo      
        !
        deallocate(sample_pos,sample_index,p_vector,sigma_table,current_sigma);   
!
    end function calculate_sigma 
!========================================================================================
    subroutine external_train_map(x,nvar,npat,nx,ny,nepoch,alpha,grid_type,&
       distance_type,neigh_type,toroidal,prot,distortion,&
       u_matrix,coords,number_patterns,node_index) bind(C, name="train_som")
!========================================================================================
!!    Subroutine to connect the self_organizing_map module to R o C
        use, intrinsic :: iso_c_binding, only : c_double, c_int, c_char
!! Import section
        real(kind=wp),parameter :: version=0.1_wp;
!! Parameter version
        character(len=*),parameter :: program_name="som_train";
!! Parameter name of the function
        integer(c_int), intent(in) :: nvar,npat
!! Integer variables to indicate the number of variables and patterns
        integer(c_int), intent(in) :: nx,ny
!! Integer variables to indicate the number of nodes of the SOM
        integer(c_int), intent(in) :: nepoch
!! Integer variables to indicate the number of epochs for training
        integer(c_int), intent(in) :: toroidal
!! Integer variable to indicate if a toroidal grid is used
        real(c_double),intent(out) :: prot(nx*ny,nvar)
!! Real array for the prototypes
        real(c_double),intent(out) :: distortion(nepoch)
!! Real array for the distortion measure (error during training)
        real(c_double),intent(out) :: u_matrix(2*nx-1,2*ny-1)
!! Real array for the u_matrix 
        real(c_double),intent(out) :: coords(nx*ny,3)
!! Real array for the grid coordinates of the SOM
        integer(c_int),intent(out) :: number_patterns(nx,ny)
!! Integer array with the number of hits for each neuron
        integer(c_int),intent(out) :: node_index(npat,3)
!! Integer array with the index node for all the neurons of the SOM       
        real(c_double),intent(in) :: x(npat,nvar)
!! Real array with the input patterns
        real(c_double),intent(in) :: alpha
!! Real value with the initial learning rate
        integer(c_int),intent(in) :: grid_type
!! Integer variable to indicate the type of grid  
        integer(c_int),intent(in) :: distance_type
!! Integer variable to indicate the distance type
        integer(c_int),intent(in) :: neigh_type
!! Integer variable to indicate the neighborhood type
        type(self_organizing_map) :: my_som
        type(kohonen_layer_parameters),dimension(1) :: parameters
        real(kind=wp),dimension(nvar,1) :: var
        integer :: i,j,k,ierr,pos,ihit,jhit,khit,nx1,ny1
        type(kohonen_pattern),allocatable :: input_patterns(:)
        real(kind=wp),dimension(nx*ny,nvar) :: prototypes
        real(kind=wp),dimension(nvar,1) :: temp
!
        parameters(1)%train_option=3;
        parameters(1)%number_nodes_nx=nx;
        parameters(1)%number_nodes_ny=ny;
        parameters(1)%number_nodes_nz=1;
        parameters(1)%number_variables1=nvar;
        parameters(1)%number_variables2=1;
        parameters(1)%number_variables=nvar;
        parameters(1)%number_patterns=npat;
        parameters(1)%number_epochs=nepoch;
        parameters(1)%learning_rate=alpha;
        parameters(1)%random_seed_=12345;
        if(grid_type == 0) then
             parameters(1)%node_type="rectangular"; !"hexagonal" !rectangular, hexagonal
         elseif(grid_type == 1) then
             parameters(1)%node_type="hexagonal";
         endif
         parameters(1)%debug_level=0;
         parameters(1)%debug_file="NOFILE";
         parameters(1)%pattern_file="NOFILE";
         parameters(1)%output_file="NOFILE";
         parameters(1)%distance_type="euclidean"; !"euclidean" !euclidean, manhattan, correlation, correlation2
         if(neigh_type == 0) then
             parameters(1)%neighborhood_type="bubble";
         elseif(neigh_type == 1) then
             parameters(1)%neighborhood_type="gaussian"; !gaussian,bubble
         endif
         parameters(1)%som_type="normal_som"!,visom
         if(toroidal == 1) then
             parameters(1)%toroidal_grid=.TRUE.;
         else
             parameters(1)%toroidal_grid=.FALSE.;
         endif
         !
         ! ADDED TO AVOID PRINTING UNIT INFO (THE CAUSE IS UNKNONW)
         ! write(*,*) ''
         ! write(*,'(A,A,f10.5)') trim(program_name),' version: ',version
         ! write(*,*) ''
         allocate(input_patterns(npat),stat=ierr);
         do i=1,npat
             var(1:nvar,1) = x(i,1:nvar)
             !write(*,*) i,var
             call input_patterns(i)%create(var);
             !    call input_patterns(i)%print();
         enddo
        ! Create SOM
        call my_som%create(parameters);
        ! Train SOM
        call my_som%train(input_patterns);
        ! Extract results
        pos=0
        k=1
        nx1=nx;ny1=ny;
        do j=1,ny
            do i=1,nx
                pos=position2index(i,j,k,nx1,ny1);
                !write(*,*) i,j,pos,i+(j-1)*nx
                call my_som%grid(i,j,k)%get_prototype(temp);
                !position2index()
                prototypes(pos,1:nvar)=temp(1:nvar,1);
            enddo
        enddo
        !
        ! Get the results in the arrays
        !
        distortion=my_som%distortion
        u_matrix(1:2*nx-1,1:2*ny-1)=my_som%u_matrix(:,:,1);
        !do i=1,size(my_som%coordinates,1);
        !write(*,*) my_som%coordinates(i,1:3);
        !   coords(i,1:3)=my_som%coordinates(i,1:3);
        !enddo
        coords=my_som%coordinates;
        !coords(1:nx*nx,1)=my_som%coordinates(:,1);
        !coords(1:nx*nx,2)=my_som%coordinates(:,2);
        !coords(1:nx*nx,3)=my_som%coordinates(:,3);
        number_patterns=my_som%number_patterns(:,:,1);
        node_index=my_som%cells_index
        prot=prototypes;
        ! 
        call my_som%destroy();
        !
        do i=1,npat
            call input_patterns(i)%destroy();
        enddo
        deallocate(input_patterns);
        !
        !
        ! write(*,*)
        ! write(*,'(A,A,f10.5,2X,A)') trim(program_name),' version: ',version,'Finished'
        ! write(*,*)
        !
    end subroutine external_train_map
!========================================================================================
    subroutine external_predict_map(prot,nx,ny,new_pat,npat,nvar,node_index) & 
        bind(C, name="predict_som")
!========================================================================================
!!    Subroutine to connect this module to R

        use, intrinsic :: iso_c_binding, only : c_double, c_int
        integer(c_int),intent(in) :: nx,ny,npat,nvar
        real(c_double),intent(in) :: prot(nx*ny,nvar),new_pat(npat,nvar)
        integer(c_int),intent(out) :: node_index(npat,3) 
!!
        type(self_organizing_map) :: my_som
        type(kohonen_layer_parameters),dimension(1) :: parameters
        integer :: ipat,inode,i_hit,nx1,ny1,nz1,cx,cy,cz,ix,iy,iz,pos,ierr
        real(kind=wp) :: dist,dist_hit
        real(kind=wp),dimension(nvar,1) :: temp
        type(kohonen_pattern),dimension(npat) :: input_data
!
        parameters(1)%train_option=3;
        parameters(1)%number_nodes_nx=nx;
        parameters(1)%number_nodes_ny=ny;
        parameters(1)%number_nodes_nz=1;
        parameters(1)%number_variables1=nvar;
        parameters(1)%number_variables2=1;
        parameters(1)%number_variables=nvar;
        parameters(1)%number_patterns=npat;
        parameters(1)%number_epochs=1;
        parameters(1)%learning_rate=0.0d0;
        parameters(1)%random_seed_=12345;
        parameters(1)%node_type="hexagonal"
        parameters(1)%debug_level=0;
        parameters(1)%debug_file="NOFILE"
        parameters(1)%pattern_file="NOFILE"
        parameters(1)%output_file="NOFILE"
        parameters(1)%distance_type="euclidean" !"euclidean" !euclidean, manhattan, correlation, correlation2
        parameters(1)%neighborhood_type="gaussian" !gaussian,bubble
        parameters(1)%som_type="normal_som"!,visom
        parameters(1)%toroidal_grid=.TRUE.
!
! call parameters(1)%print();
!
        call my_som%create(parameters);
!
        pos=0;
        iz=1; 
        do iy=1,ny
            do ix=1,nx
                pos=pos+1;
                temp(1:nvar,1)=prot(pos,1:nvar)
                call my_som%grid(ix,iy,iz)%set_prototype(temp)
            enddo
        enddo
        !
        do ipat=1,npat
            temp(1:nvar,1)=new_pat(ipat,1:nvar);
            call input_data(ipat)%create(temp);
        enddo
        !
        call my_som%predict(input_data,node_index);
        !
        call my_som%destroy();
        !
        do ipat=1,size(input_data);
            call input_data(ipat)%destroy();
        enddo
!
    end subroutine external_predict_map
! 
end module self_organizing_map_utilities