#include #include #include #include #include #include #include // Simulates heat transmission through a plate represented by a matrix typedef struct { // Dimensions of the matrix size_t rows; size_t cols; // When temperature changes are lower than epsilon, simulation stops double epsilon; // Matrix of temperatures representing the plate double** matrix; // An auxiliary matrix to store the state of the plate after an update double** mirror; // The starting time of the simulation struct timespec start_time; // Current generation of the simulation size_t generation; // Pointer to the matrix with the current state of the simulation double** current; // Pointer to the matrix that will hold the next generation double** next; } heat_t; int read_matrix(heat_t* heat, const char* filename); void simulate(heat_t* heat); double update_generation(heat_t* heat); double update_cell(heat_t* heat, size_t row, size_t col); double elapsed(const struct timespec* start_time); void** create_matrix(const size_t rows, const size_t columns, const size_t cell_size); void** destroy_matrix(void** matrix, const size_t rows); int main(int argc, char* argv[]) { if ( argc != 3 ) return fprintf(stderr, "usage: heat matrix.bin epsilon\n"), 1; heat_t heat; memset(&heat, 0, sizeof(heat_t)); heat.epsilon = strtod(argv[2], NULL); if ( heat.epsilon <= 0.0 ) return fprintf(stderr, "error: invalid epsilon %lg\n", heat.epsilon), 2; int error = read_matrix(&heat, argv[1]); if ( error == EXIT_SUCCESS ) { clock_gettime(CLOCK_MONOTONIC, &heat.start_time); simulate(&heat); destroy_matrix((void**)heat.matrix, heat.rows); destroy_matrix((void**)heat.mirror, heat.rows); } return error; } int read_matrix(heat_t* heat, const char* filename) { int error = EXIT_SUCCESS; FILE* input = fopen(filename, "rb"); if ( input == NULL ) return fprintf(stderr, "error opening %s\n", filename), 11; if ( fread(&heat->rows, sizeof(size_t), 1, input) > 0 && fread(&heat->cols, sizeof(size_t), 1, input) > 0 && heat->rows >= 2 && heat->cols >= 2 ) { heat->matrix = (double**) create_matrix(heat->rows, heat->cols, sizeof(double)); heat->mirror = (double**) create_matrix(heat->rows, heat->cols, sizeof(double)); if ( heat->matrix && heat->mirror ) { for ( size_t row = 0; row < heat->rows; ++row ) { if ( fread( heat->matrix[row], sizeof(double), heat->cols, input ) > 0 ) memcpy( heat->mirror[row], heat->matrix[row], sizeof(double) * heat->cols ); else { fprintf(stderr, "error reading %zu row\n", row + 1); error = 14; break; } } } else { fprintf(stderr, "error allocating %zu x %zu matrixes\n", heat->rows, heat->cols); error = 13; } } else { fprintf(stderr, "error reading matrix dimensions\n"); error = 12; } if ( error ) { heat->matrix = (double**) destroy_matrix((void**)heat->matrix, heat->rows); heat->mirror = (double**) destroy_matrix((void**)heat->mirror, heat->rows); } else printf("%zu x %zu matrix loaded (%0.3lf GiB)\n", heat->rows, heat->cols , 2 * 8 * heat->rows * heat->cols / 1073741824.0); fclose(input); return error; } void simulate(heat_t* heat) { heat->current = heat->matrix; heat->next = heat->mirror; double delta = 0.0; while ( (delta = update_generation(heat)) > heat->epsilon ) printf("%.9lfs: generation %zu delta=%0.6lf\n", elapsed(&heat->start_time), heat->generation, delta); } double update_generation(heat_t* heat) { // The maximum temperature change detected in this generation double max_change = DBL_MIN; // Update all rows for ( size_t row = 1; row < heat->rows - 1; ++row ) { // Upate all columns for ( size_t col = 1; col < heat->cols - 1; ++col ) { // Update cell from current matrix to the next matrix double change = update_cell(heat, row, col); // If the cell change is greater than our maximum, update the maximum if ( change > max_change ) max_change = change; } } // The next matrix is enterely updated, it is now the current matrix // The current matrix will be used for the next generation. So swap them double** temp = heat->current; heat->current = heat->next; heat->next = temp; // A new generation was completed ++heat->generation; // Return the max temperature change detected in this generation return max_change; } double update_cell(heat_t* heat, size_t row, size_t col) { // The new value is the average of the neighbors in the previous generation heat->next[row][col] = ( heat->current[row - 1][col] + heat->current[row][col - 1] + heat->current[row][col + 1] + heat->current[row + 1][col] ) / 4.0; // Return the difference between the cell's new temperature and old one return fabs( heat->next[row][col] - heat->current[row][col] ); } double elapsed(const struct timespec* start_time) { struct timespec current_time; clock_gettime(CLOCK_MONOTONIC, ¤t_time); return current_time.tv_sec - start_time->tv_sec + 1e-9 * (current_time.tv_nsec - start_time->tv_nsec); } void** create_matrix(const size_t rows, const size_t columns, const size_t cell_size) { void** matrix = (void**) calloc(rows, sizeof(void*)); if ( matrix == NULL ) return NULL; for ( size_t row = 0; row < rows; ++row ) if ( ( matrix[row] = calloc( columns, cell_size ) ) == NULL ) return destroy_matrix(matrix, rows), NULL; return matrix; } void** destroy_matrix(void** matrix, const size_t rows) { if ( matrix ) for ( size_t row = 0; row < rows; ++row ) free( matrix[row] ); free( matrix ); return NULL; }