// CLUSTERS.C // Cluster trace functions // © 2021 Peter J. Meyer #include "iss.h" #define STACK_PTR_CHECK true // These are global. // int ct_spin_value, ct_new_spin_value; // short int ct_i, ct_j, ct_k, ct_l, ct_r; // short int nn_i, nn_j, nn_k, nn_l; // int ct_size_of_cluster; // short unsigned int ct_cluster_number; // int ct_cluster_number_overflow; // Variables local to this module. static int ct_cluster_type; static int ct_number_clusters; static int ct_stop_on_spanning_cluster; static int ct_get_cluster_size; static int ct_spanning_cluster_found; static int ct_num_spanning_clusters; static short int best_r; static short unsigned int ct_spin_distance; static void trace_single_cluster(void); static void clear_cluster_number_array(void); static void clear_path_status(void); static void update_path_status(void); static int is_spanning_cluster(int total_spanning_cluster); // Call with input data set up in *actd. /*---------------------------------------------------------*/ void trace_all_clusters(struct _all_cluster_trace_data *actd) { int i, j, k, l; ct_cluster_type = actd->cluster_type; ct_number_clusters = actd->number_clusters; ct_stop_on_spanning_cluster = actd->stop_on_spanning_cluster; ct_get_cluster_size = actd->get_cluster_size; if ( ct_stop_on_spanning_cluster ) { ct_number_clusters = false; ct_get_cluster_size = false; } ct_cluster_number = ct_num_spanning_clusters = ct_cluster_number_overflow = 0; ct_spanning_cluster_found = false; if ( ct_number_clusters ) clear_cluster_number_array(); mark_all_sites_unvisited_and_clear_path(); // S&B.C ct_i = ct_j = ct_k = ct_l = -1; // For 2d lattice ct_k and ct_l remain -1. // For 3d lattice ct_l remains -1. switch ( dimensionality ) { case 2: for ( i=0; i<size; i++ ) { for ( j=0; j<size; j++ ) { if ( ct_stop_on_spanning_cluster ) if ( !IS_BOUNDARY_SITE_2(i,j) ) continue; // A spanning cluster must include a boundary site // so to find a spanning cluster it is sufficient // to start traces only from boundary sites. if ( !( ct_spin_value = spin_at_site(i,j) ) ) // Returns 0 if site is not occupied // otherwise it returns the spin value. continue; // because site not occupied. if ( SITE_IS_VISITED_2(i,j) ) // ISM_DEF.C continue; // About to trace out a new cluster. if ( ++ct_cluster_number == 0 ) { ct_cluster_number_overflow++; ct_cluster_number = 1; // Start again from 1. } if ( ct_get_cluster_size ) ct_size_of_cluster = 0; // Now do cluster trace. clear_stack(); // STACK.C clear_path_status(); ct_i = i; ct_j = j; ct_spin_distance = 0; trace_single_cluster(); // recursive function // If counting cluster sizes (and not stopping on spanning cluster) // then size of cluster is now in size_of_cluster. // At this point stack_ptr should = stack and ct_spin_distance should = 0. #if STACK_POINTER_CHECK if ( !stack_empty() ) { printf("\nStack not empty (CLUSTERS.C)!\n"); exit(1); } if ( ct_spin_distance ) { printf("\nct_spin_distance not zero (CLUSTERS.C)!\n"); exit(1); } #endif if ( ct_spanning_cluster_found && ct_stop_on_spanning_cluster ) goto done; } } break; case 3: for ( i=0; i<size; i++ ) { for ( j=0; j<size; j++ ) { for ( k=0; k<size; k++ ) { ADD_CODE // 3d } } } break; case 4: for ( i=0; i<size; i++ ) { for ( j=0; j<size; j++ ) { for ( k=0; k<size; k++ ) { for ( l=0; l<size; l++ ) { ADD_CODE // 4d } } } } break; } done: actd->num_clusters_traced = ct_cluster_number + (unsigned int)65535*ct_cluster_number_overflow; actd->spanning_cluster_found = ct_spanning_cluster_found; actd->num_spanning_clusters = ct_num_spanning_clusters; actd->cluster_number_overflow = ct_cluster_number_overflow; spanning_cluster_list[ct_num_spanning_clusters] = 0; } // This function is called (recursively) once for each site or spin. // Only the return address is pushed onto the stack in an attempt // to minimize the danger of system stack overflow. // On entry the site is specified by ct_i, ct_j, ... // // Count number of bonds in cluster? /*----------------------------------*/ static void trace_single_cluster(void) { if ( ct_get_cluster_size ) ct_size_of_cluster++; switch ( dimensionality ) { case 2: MARK_SITE_AS_VISITED_2(ct_i,ct_j); // We may wish to know if this is a spanning cluster // even if we don't want to stop on a spanning cluster. update_path_status(); if ( is_spanning_cluster(false) ) // false = only 1 dimension to be covered { ct_spanning_cluster_found = true; if ( ct_number_clusters ) { if ( ct_num_spanning_clusters == MAX_NUM_SPANNING_CLUSTERS ) { printf("\nMore than %d spanning clusters found.\n", MAX_NUM_SPANNING_CLUSTERS); exit(1); } spanning_cluster_list[ct_num_spanning_clusters++] = ct_cluster_number; } if ( ct_stop_on_spanning_cluster ) { MARK_SITE_IN_SPANNING_CLUSTER_2(ct_i,ct_j); return; } } if ( ct_number_clusters ) { cluster_numbers2[ct_i][ct_j] = ct_cluster_number; #if RECORD_SPIN_DISTANCES spin_distances2[ct_i][ct_j] = ct_spin_distance; #endif } // Now visit each of the unvisited nn spins. loop { get_nn_sites(ct_i,ct_j); // Q&A.C // nn_sites[][] now holds the indices // of the nearest neighbour sites in all directions // (no. of directions = coord_num). // Not all of these are necessarily occupied. best_r = NON_EXISTENT; for ( ct_r=0; ct_r<coord_num; ct_r++ ) { if ( ( nn_i = nn_sites[ct_r][0] ) == NON_EXISTENT ) continue; // because this nn site does not exist (only with free boundary conditions) nn_j = nn_sites[ct_r][1]; if ( !SITE_IS_OCCUPIED_2(nn_i,nn_j) ) continue; // because site is not occupied if ( ct_cluster_type != CT_NN ) { // ct_cluster_type is one of CT_OPEN_BOND or CT_SPIN. if ( ! ( bonds2[ct_i][ct_j] & bit[ct_r] ) ) continue; // because no open bond exists in direction ct_r if ( ct_cluster_type == CT_SPIN ) { // ct_spin_value is set in trace_all_clusters(). if ( ct_spin_value != spin_at_site(nn_i,nn_j) ) continue; // because spin value is not same } } // Site exists and is occupied (or bond exists in direction ct_r). if ( !SITE_IS_VISITED_2(nn_i,nn_j) ) { // Found an unvisited nn site or spin. if ( ct_stop_on_spanning_cluster ) { // Find nn in best direction for spanning path. if ( best_r == NON_EXISTENT ) best_r = ct_r; else if ( directionality[ct_r] > directionality[best_r] ) best_r = ct_r; } else { best_r = ct_r; break; } } } if ( best_r == NON_EXISTENT ) return; // because site has no unvisited nn spins. // Have an unvisited occupied nn spin. if ( !push_onto_stack(ct_i,ct_j) ) return; // otherwise would loop forever ct_i = nn_sites[best_r][0]; ct_j = nn_sites[best_r][1]; ct_spin_distance++; trace_single_cluster(); // A recursive call, which marks this site as visited. if ( !pop_from_stack(&ct_i,&ct_j) ) { printf("\nFatal error: Stack underflow in trace_single_cluster()!\n"); // should never occur exit(1); } ct_spin_distance--; if ( ct_spanning_cluster_found && ct_stop_on_spanning_cluster ) { MARK_SITE_IN_SPANNING_CLUSTER_2(ct_i,ct_j); // This usually does not mark all the sites in the spanning cluster, // just those on the stack at the time that the spanning cluster is found. // The marked sites usually, but not always, form a spanning path. // In any case they show which cluster contains the spanning path. return; // back up through the recursive calls. } } break; case 3: ADD_CODE // 3d break; case 4: ADD_CODE // 4d break; } } /*----------------------------------------*/ static void clear_cluster_number_array(void) { switch ( dimensionality ) { case 2: memset(&cluster_numbers2[0][0],0,num_sites); break; case 3: memset(&cluster_numbers3[0][0][0],0,num_sites); break; case 4: memset(&cluster_numbers4[0][0][0][0],0,num_sites); break; } } /*-------------------------------*/ static void clear_path_status(void) { int d, i; for ( d=0; d<dimensionality; d++ ) for ( i=0; i<size; i++ ) edge[d][i] = false; } /*--------------------------------*/ static void update_path_status(void) { switch ( dimensionality ) { case 2: edge[0][ct_i] = edge[1][ct_j] = true; break; case 3: edge[0][ct_i] = edge[1][ct_j] = edge[2][ct_k] = true; break; case 4: edge[0][ct_i] = edge[1][ct_j] = edge[2][ct_k] = edge[3][ct_l]= true; break; } } /*------------------------------------------------------*/ static int is_spanning_cluster(int total_spanning_cluster) { int d, i; if ( total_spanning_cluster ) { for ( d=0; d<dimensionality; d++ ) { for ( i=0; i<size; i++ ) { if ( !edge[d][i] ) return ( false ); } } return ( true ); // because all dimensions are filled } else { for ( d=0; d<dimensionality; d++ ) { for ( i=0; i<size; i++ ) { if ( !edge[d][i] ) break; } if ( i== size ) return ( true ); // because a dimension is filled } return ( false ); } } // Assumes end of spanning cluster list marked by 0. // Returns true if cluster_num is in the spanning cluster list, false otherwise. /*--------------------------------------------------------*/ int in_spanning_cluster_list(unsigned short int cluster_num) { int i = 0; loop { if ( spanning_cluster_list[i] == 0 ) return ( false ); if ( spanning_cluster_list[i++] == cluster_num ) return ( true ); } }