-
Notifications
You must be signed in to change notification settings - Fork 2
/
project2_Delaunoy_Crasset_IMPLICIT.c
709 lines (580 loc) · 22.5 KB
/
project2_Delaunoy_Crasset_IMPLICIT.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
#include <stdlib.h>
#include <stdio.h>
#include <mpi.h>
#include <math.h>
#include <assert.h>
#include <omp.h>
#include "project2_Delaunoy_Crasset_IMPLICIT.h"
#include "project2_Delaunoy_Crasset_SPARSE.h"
#include "project2_Delaunoy_Crasset_IO.h"
#define M_PI 3.14159265358979323846
/**
* Compute the indices of the A matrix this process is responsible for.
*
* Parameters:
* nbproc: The number of process
* myrank: The rank of this process
* size: The number of lines in A
* start: Pointer to an unsigned int that will be set to the starting index
* start: Pointer to an unsigned int that will be set to the ending index
*/
void get_indices(int nbproc, int myrank, unsigned int size, unsigned int* start, unsigned int* end){
// Compute number of indices a process is responsible for
int nbIndex = size/nbproc;
// Compute The remaining indices
int remaining = size%nbproc;
// Assign indices to processes
if(myrank < remaining){
*start = (nbIndex+1) * myrank;
*end = (nbIndex+1) * (myrank + 1) - 1;
}
else{
*start = nbIndex * myrank + remaining;
*end = nbIndex * (myrank + 1) - 1 + remaining;
}
}
/**
* Perform a linear system resolution using the conjugate gradient algorithm
*
* Parameters:
* A: A sparse matrix representing the variables coefficients in the equations
* the process calling the function is reponsible for
* b: An array representing the offsets in the equations
* size: The number of variables in the system
* rThresh: The threshold at which to stop the algorithm
* nbproc: The number of processors
* myrank: The rank of the processor running the function
* startIndex: The starting index in the A matrix the calling process in responsible for.
* endIndex: The ending index in the A matrix the calling process in responsible for.
* recvcounts: The number of elements received by each process on gather calls.
* dspls: The offsets at which to place the elemnts received by each process.
*
* Returns:
* A pointer to an allocated array containing the solution of the system
*/
double* MPISparseConjugateGradient(SparseMatrix* A, double* b, unsigned int size,
double rThresh, int nbproc, int myrank, unsigned int startIndex,
unsigned int endIndex, int* recvcounts, int* displs){
// Allocate memory
double* xCurr = malloc(size * sizeof(double));
if(!xCurr)
return NULL;
double* xNext = malloc(size * sizeof(double));
if(!xNext){
free(xCurr);
return NULL;
}
double* rCurr = malloc(size * sizeof(double));
if(!rCurr){
free(xCurr);
free(xNext);
return NULL;
}
double* rNext = malloc(size * sizeof(double));
if(!rNext){
free(xCurr);
free(xNext);
free(rCurr);
return NULL;
}
double* pCurr = malloc(size * sizeof(double));
if(!pCurr){
free(xCurr);
free(xNext);
free(rCurr);
free(rNext);
return NULL;
}
double* pNext = malloc(size * sizeof(double));
if(!pNext){
free(xCurr);
free(xNext);
free(rCurr);
free(rNext);
free(pCurr);
return NULL;
}
double* Ap = malloc(size * sizeof(double));
if(!Ap){
free(xCurr);
free(xNext);
free(rCurr);
free(rNext);
free(pCurr);
free(pNext);
return NULL;
}
double* tmpBuff = malloc((endIndex - startIndex + 1) *sizeof(double));
//double* tmpBuff = malloc(3 *sizeof(double));
if(!tmpBuff){
free(xCurr);
free(xNext);
free(rCurr);
free(rNext);
free(pCurr);
free(pNext);
free(Ap);
return NULL;
}
// Initialise x
for(unsigned int i = 0; i < size; i++){
xCurr[i] = 0;
}
// Initialise r
for(unsigned int i = 0; i < size; i++){
rCurr[i] = b[i]; // No need to add Ax_0 term as x_0 = 0
}
// Initialise p
for(unsigned int i = 0; i < size; i++){
pCurr[i] = rCurr[i];
}
// Compute r_0
double rBaseNorm = MPIDotProduct(rCurr, rCurr, size, startIndex, endIndex, myrank, nbproc);
double num, denum, alpha, beta, rNextNorm;
double rCurrNorm = rBaseNorm;
// Loop until convergence reached
while(rCurrNorm / rBaseNorm >= rThresh){
// Compute alpha
MPIMatVecMul(A, pCurr, tmpBuff, Ap, startIndex, endIndex, myrank, nbproc, recvcounts, displs);
alpha = rCurrNorm/MPIDotProduct(pCurr, Ap, size, startIndex, endIndex, myrank, nbproc);
// Compute xNext
#pragma omp parallel default(shared)
{
#pragma omp for schedule(static)
for(unsigned int i = 0; i < size; i++)
xNext[i] = xCurr[i] + alpha * pCurr[i];
// Compute rNext
#pragma omp for schedule(static)
for(unsigned int i = 0; i < size; i++)
rNext[i] = rCurr[i] - alpha * Ap[i];
}
// Compute next rNorm
rNextNorm = MPIDotProduct(rNext, rNext, size, startIndex, endIndex, myrank, nbproc);
// Compute beta
beta = rNextNorm/rCurrNorm;
// Compute pNext
#pragma omp parallel default(shared)
{
#pragma omp for schedule(static)
for(unsigned int i = 0; i < size; i++)
pNext[i] = rNext[i] + beta * pCurr[i];
}
// Go to next iteration
double* buf;
buf = xNext;
xNext = xCurr;
xCurr = buf;
buf = rNext;
rNext = rCurr;
rCurr = buf;
buf = pNext;
pNext = pCurr;
pCurr = buf;
rCurrNorm = rNextNorm;
}
// Free memory
free(tmpBuff);
free(xNext);
free(rCurr);
free(rNext);
free(pCurr);
free(pNext);
free(Ap);
return xCurr;
}
/**
* Transform 2d indices into the corresponding 1d index of a flattened array
*
* Parameters:
* x: The value of the first index
* y: The value of the second index
* ySize: The size of the second axis of the array
*
* Returns:
* The equivalent 1d index
*/
int get_1d(int x, int y, int ySize){
return x * ySize + y;
}
/**
* Transform 1d index into the corresponding 2d indices of a reshaped array
*
* Parameters:
* i: The 1d index
* ySize: The size of the second axis of the array.
* x: A pointer to the first index that will be set
* y: A pointer to the second index that will be set
*/
void get_2d(int i, int ySize, int* x, int* y){
*x = i/ySize;
*y = i%ySize;
}
/**
* Compute the equation matrix of the system
*
* Parameters:
* A: A pointer to a pointer of SparseMatrix that will be set with a SparseMatrix containing the
* elements of the equation matrix the calling process is responsible for
* b: A pointer to an array of double that will be set with the offsets of the equations
* result: The result of the previous iteration that will be used to fill the matrix A and vector b.
* start: The starting index the calling process is reponsible for
* end: The ending index the calling process is repsonsible for
* xSize: The x axis size of the discretization
* ySize: The y axis size of the discretization
* h: The filled h matrix with the depth at rest
* params: The parameters of the algorithm
* t: The current iteration
*/
void BuildSystemMatrix(SparseMatrix** A, double** b, double* result, unsigned int start, unsigned int end, int xSize, int ySize, double** h, Parameters* params, double t){
// Compute the indices of each part of the result vector
int uBegin = (xSize + 1) * (ySize + 1);
int vBegin = uBegin + (xSize + 2) * (ySize + 1);
int vEnd = vBegin + (xSize + 1) * (ySize + 2) - 1;
// Allocate memory
*A = createSparseMatrix(start, end, 5);
*b = malloc((vEnd + 1) * sizeof(double));
// Initialize the matrices
initAtb(*A, *b, result, start, end, xSize, ySize, h, params, t);
// Make those semi-definite positive
makeDefinitePositive(A, b, result, start, end, xSize, ySize, h, params, t);
}
/**
* Make the A matrix semi definite positive and update b accordingly.
* The elements pointed by At and b will be updated.
*
* Parameters:
* A: A pointer to a pointer of SparseMatrix containing the elements of the equation matrix
* the calling process is responsible for
* b: A pointer to an array of double containing the offsets of the equations
* result: The result of the previous iteration that will be used to fill the matrix A and vector b.
* start: The starting index the calling process is reponsible for
* end: The ending index the calling process is repsonsible for
* xSize: The x axis size of the discretization
* ySize: The y axis size of the discretization
* h: The filled h matrix with the depth at rest
* params: The parameters of the algorithm
* t: The current iteration
*/
void makeDefinitePositive(SparseMatrix** At, double** b, double* result, unsigned int start, unsigned int end, int xSize, int ySize, double** h, Parameters* params, double t){
// Allocate new A
SparseMatrix* AtA = createSparseMatrix(start, end, 25);
// Compute the indices of each part of the result vector
int uBegin = (xSize + 1) * (ySize + 1);
int vBegin = uBegin + (xSize + 2) * (ySize + 1);
int vEnd = vBegin + (xSize + 1) * (ySize + 2) - 1;
// Allocate new b
double* Atb = malloc((vEnd + 1) * sizeof(double));
// Construct full At and mult by this
#pragma omp parallel default(shared)
{
// Temporary variable used for computation
int x, y;
SparseVector* vec = createSparseVector(5);
// Loop on each line of A
#pragma omp for schedule(static)
for(int i = 0; i <= vEnd; i++){
// Set vec with the indices associated with the current line performed
//eta variable
if(i < uBegin){
get_2d(i, ySize + 1, &x, &y);
sparseVecInsertElement(vec, i, 1.0/params->deltaT);
if(x != 0)
sparseVecInsertElement(vec, uBegin + get_1d(x, y, ySize + 1), params->g/params->deltaX);
if(x != xSize)
sparseVecInsertElement(vec, uBegin + get_1d(x + 1, y, ySize + 1), -params->g/params->deltaX);
if(y != 0)
sparseVecInsertElement(vec, vBegin + get_1d(x, y, ySize + 2), params->g/params->deltaY);
if(y != ySize)
sparseVecInsertElement(vec, vBegin + get_1d(x, y + 1, ySize + 2), -params->g/params->deltaY);
}
//u variable
else if(i < vBegin){
get_2d(i - uBegin, ySize + 1, &x, &y);
if(x != 0)
sparseVecInsertElement(vec, get_1d(x - 1, y, ySize + 1), h[2*x-1][2*y]/params->deltaX);
if(x == 0)
sparseVecInsertElement(vec, get_1d(x, y, ySize + 1), -h[0][2*y]/params->deltaX);
else if(x != xSize + 1)
sparseVecInsertElement(vec, get_1d(x, y, ySize + 1), -h[2*x-1][2*y]/params->deltaX);
// At the border
if(x == 0 || x == xSize + 1){
sparseVecInsertElement(vec, i, 1.0);
}
else{
sparseVecInsertElement(vec, i, 1.0/params->deltaT + params->gamma);
}
}
//v variable
else{
get_2d(i - vBegin, ySize + 2, &x, &y);
if(y != 0)
sparseVecInsertElement(vec, get_1d(x, y - 1, ySize + 1), h[2*x][2*y-1]/params->deltaY);
if(y == 0)
sparseVecInsertElement(vec, get_1d(x, y, ySize + 1), -h[2*x][0]/params->deltaY);
else if (y != ySize + 1)
sparseVecInsertElement(vec, get_1d(x, y, ySize + 1), -h[2*x][2*y-1]/params->deltaY);
// At the border
if(y == 0 || y == ySize + 1){
sparseVecInsertElement(vec, i, 1.0);
}
else{
sparseVecInsertElement(vec, i, 1.0/params->deltaT + params->gamma);
}
}
// Mult A by this vec and set the corresponding elements of AtA
double result;
for(int j = start; j <= end; j++){
result = sparseMatVecDotProduct(*At, j, vec);
if(result != 0.0){
#pragma omp critical(fill_AtA)
sparseInsertElement(AtA, j, i, result);
}
}
// Mult b by this vec and set the corresponding element of Atb
Atb[i] = vecSparseDotProduct(vec, *b);
// Reset buffer vector
resetSparseVector(vec);
}
// Free space of tempary vector
freeSparseVector(vec);
}
// Free old matrices
freeSparseMatrix(*At);
free(*b);
// Replace old matrices with the new ones
*At = AtA;
*b = Atb;
}
/**
* Init the transposed equation coefficients matrix and offset matrix
*
* Parameters:
* A: A pointed to an allocated equation matrix which elements will be set
* b: A pointer to an allocated array of double which will be filled with offsets.
* result: The result of the previous iteration that will be used to fill the matrix A and vector b.
* start: The starting index the calling process is reponsible for
* end: The ending index the calling process is repsonsible for
* xSize: The x axis size of the discretization
* ySize: The y axis size of the discretization
* h: The filled h matrix with the depth at rest
* params: The parameters of the algorithm
* t: The current iteration
*/
void initAtb(SparseMatrix* A, double* b, double* result, unsigned int start, unsigned int end, int xSize, int ySize, double** h, Parameters* params, double t){
// Reset the matrix
resetSparseMatrix(A);
// Compute the indices of each part of the result vector
int uBegin = (xSize + 1) * (ySize + 1);
int vBegin = uBegin + (xSize + 2) * (ySize + 1);
int vEnd = vBegin + (xSize + 1) * (ySize + 2) - 1;
// Fill At
#pragma omp parallel default(shared)
{
int x, y;
// Loop on lines of At
#pragma omp for schedule(static)
for(int i = start; i <= end; i++){
//eta variable
if(i < uBegin){
get_2d(i, ySize + 1, &x, &y);
sparseInsertElement(A, i, i, 1.0/params->deltaT);
if(x != 0)
sparseInsertElement(A, i, uBegin + get_1d(x, y, ySize + 1), params->g/params->deltaX);
if(x != xSize)
sparseInsertElement(A, i, uBegin + get_1d(x + 1, y, ySize + 1), -params->g/params->deltaX);
if(y != 0)
sparseInsertElement(A, i, vBegin + get_1d(x, y, ySize + 2), params->g/params->deltaY);
if(y != ySize)
sparseInsertElement(A, i, vBegin + get_1d(x, y + 1, ySize + 2), -params->g/params->deltaY);
}
//u variable
else if(i < vBegin){
get_2d(i - uBegin, ySize + 1, &x, &y);
if(x != 0)
sparseInsertElement(A, i, get_1d(x - 1, y, ySize + 1), h[2*x-1][2*y]/params->deltaX);
if(x == 0)
sparseInsertElement(A, i, get_1d(x, y, ySize + 1), -h[0][2*y]/params->deltaX);
else if(x != xSize + 1)
sparseInsertElement(A, i, get_1d(x, y, ySize + 1), -h[2*x-1][2*y]/params->deltaX);
// At the border
if(x == 0 || x == xSize + 1){
sparseInsertElement(A, i, i, 1.0);
}
else{
sparseInsertElement(A, i, i, 1.0/params->deltaT + params->gamma);
}
}
//v variable
else{
get_2d(i - vBegin, ySize + 2, &x, &y);
if(y != 0)
sparseInsertElement(A, i, get_1d(x, y - 1, ySize + 1), h[2*x][2*y-1]/params->deltaY);
if(y == 0)
sparseInsertElement(A, i, get_1d(x, y, ySize + 1), -h[2*x][0]/params->deltaY);
else if (y != ySize + 1)
sparseInsertElement(A, i, get_1d(x, y, ySize + 1), -h[2*x][2*y-1]/params->deltaY);
// At the border
if(y == 0 || y == ySize + 1){
sparseInsertElement(A, i, i, 1.0);
}
else{
sparseInsertElement(A, i, i, 1.0/params->deltaT + params->gamma);
}
}
}
// Fill b
#pragma omp for schedule(static)
for(int i = 0; i <= vEnd; i++){
// eta constraint
if(i < uBegin){
b[i] = result[i]/params->deltaT;
}
// u constraint
else if(i < vBegin){
get_2d(i - uBegin, ySize + 1, &x, &y);
// At the border
if(x == 0 || x == xSize + 1){
b[i] = 0;
}
// In the middle
else{
b[i] = result[i]/params->deltaT;
}
}
// v constraint
else{
get_2d(i - vBegin, ySize + 2, &x, &y);
// At the bottom border
if(y == 0){
b[i] = 0;
}
// At the top border
else if(y == ySize + 1){
if(params->s == 0)
b[i] = params->A * sin(2 * M_PI * params->f * t * params->deltaT);
else
b[i] = params->A * sin(2 * M_PI * params->f * t * params->deltaT) * exp(- t * params->deltaT / 500);
}
// In the middle
else{
b[i] = result[i]/params->deltaT;
}
}
}
}
}
/**
* Save the results.
*
* Parameters:
* x: A pointer to the results
* xSize: The x axis size of the discretization
* ySize: The y axis size of the discretization
* iteration: The current iteration
* params: The parameters of the algorithm
* nbproc: The number of processes
* nbThread: The number of threads
*/
void saveImplicit(double* x, int xSize, int ySize, unsigned int iteration, Parameters* params, int nbproc, int nbThread){
// Compute the indices of each part of the result vector
int uBegin = (xSize + 1) * (ySize + 1);
int vBegin = uBegin + (xSize + 2) * (ySize + 1);
// Save results
saveToDisk(x, x + uBegin, x + vBegin, xSize, ySize, iteration, params, nbproc, nbThread);
}
/**
* Solve Navier-Stokes equations using the implicit Euler method
*
* Parameters:
* map: A pointer to a map structure
* params: The parameters with which to solve the problem
* eta: A pointer to an array of double that will be set with the final iteration results of eta
* u: A pointer to an array of double that will be set with the final iteration results of u
* v: A pointer to an array of double that will be set with the final iteration results of v
*
* Returns:
* A integer indicating whether the function executed successfully
*/
int eulerImplicitMPI(Map* map, Parameters* params, double** eta, double** u, double** v){
// Assert arugments
assert(map);
assert(params);
// Initialise the number of processes and process num
int nbproc, myrank ;
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
MPI_Comm_size(MPI_COMM_WORLD, &nbproc);
int openMP_nbthreads = omp_get_num_threads();
// Compute the size of the discretization
int xSize = (int)(map->a / params->deltaX);
int ySize = (int)(map->b / params->deltaY);
// Allocate memory
// x = [eta u v]
int inputSize = (xSize + 1) * (ySize + 1) + (xSize + 2) * (ySize + 1) + (xSize + 1) * (ySize + 2);
double* x = calloc(inputSize, sizeof(double));
if(!x)
return -1;
double** h = allocateDoubleMatrix(2 * xSize + 3, 2 * ySize + 3);
if(!h){
free(x);
return -1;
}
// Fill depth at rest matrix
for(int i = 0; i < 2 * xSize + 3; i++){
for(int j = 0; j < 2 * ySize + 3; j++){
h[i][j] = getGridValueAtDomainCoordinates(map, ((float)(i * xSize)/(xSize + 1)) * (params->deltaX / 2), ((float)(j * ySize)/(ySize + 1)) * (params->deltaY / 2));
}
}
// Compute the indices this process will be responsible for
unsigned int start, end;
get_indices(nbproc, myrank, inputSize, &start, &end);
// Loop over ietrations to perform
for(unsigned int t = 1; t <= params->TMax/params->deltaT; t++){
if(myrank == 0)
fprintf(stderr, "t = %d\n", t);
// Build the equation matrix
SparseMatrix* A;
double* b;
BuildSystemMatrix(&A, &b, x, start, end, xSize, ySize, h, params, t);
// Free no longer used results of previous iteration
free(x);
// Compute received counts and displacements
unsigned int tmpStart, tmpEnd;
int* recvcounts = malloc(nbproc * sizeof(int));
int* displs = malloc(nbproc * sizeof(int));
displs[0] = 0;
for(unsigned int k = 0; k < nbproc; k++){
get_indices(nbproc, k, inputSize, &tmpStart, &tmpEnd);
int tmpSize = tmpEnd - tmpStart + 1;
recvcounts[k] = tmpSize;
if(k < nbproc - 1)
displs[k+1] = displs[k] + tmpSize;
}
// Get threshold
double rThresh = params->r_threshold;
// Solve system using conjugate gradient method
x = MPISparseConjugateGradient(A, b, inputSize, rThresh, nbproc, myrank, start, end, recvcounts, displs);
// Process 0 saves arrays to disk
if(params->S != 0 && t % params->S == 0 && myrank == 0){
// Save to disk
saveImplicit(x, xSize, ySize, t, params, nbproc, openMP_nbthreads);
}
// Free memory unused
free(recvcounts);
free(displs);
freeSparseMatrix(A);
free(b);
}
// free depth at rest matrix
freeDoubleMatrix(h, 2*xSize+3);
// Save last iteration solution
if(params->S != 0 && ((int) (params->TMax/params->deltaT) % params->S) == 0 && myrank == 0){
saveImplicit(x, xSize, ySize, params->TMax/params->deltaT, params, nbproc, openMP_nbthreads);
}
// Set the solution pointers
int uBegin = (xSize + 1) * (ySize + 1);
int vBegin = uBegin + (xSize + 2) * (ySize + 1);
*eta = x;
*u = x + uBegin;
*v = x + vBegin;
return 0;
}