OOFEM 3.0
Loading...
Searching...
No Matches
grid.C
Go to the documentation of this file.
1#include "grid.h"
2#include "error.h"
3#include <stdlib.h>
4#include <stdio.h>
5#include <math.h>
6
7namespace oofem {
8
9// NB: When using matrix indices (i,j), they run from 1 to (m,n).
10// When using 1-dim array index 'ind', it runs from 0 to (m*n-1).
11
12// Index offsets of all neighbors (direct and diagonal ones)
14 -1, -1, -1, 0, 0, 1, 1, 1
15};
17 -1, 0, 1, -1, 1, -1, 0, 1
18};
19
20// Indicator which neighbors are diagonal ones
21bool is_diag[] = {
22 true, false, true, false, false, true, false, true
23};
24
25// Index offsets of direct neighbours
26int iOffsets[] = {
27 -1, 1, 0, 0
28};
29int jOffsets[] = {
30 0, 0, -1, 1
31};
32
33// Offsets used by calcTime
34// NB: Don't change the ordering without also changing the if-else
35// statements below that set xmin, ymin.
36int icalcOffsets[] = {
37 -2, -1, 1, 2, 0, 0, 0, 0
38};
39int jcalcOffsets[] = {
40 0, 0, 0, 0, -2, -1, 1, 2
41};
42
43
44Grid :: Grid(int N, int M) :
45 n(N), m(M),
46 T(m, n),
47 F(m, n)
48{
49 solutionAvailable = false;
50
51 // (calloc sets all bits of the allocated memory to zero, so the initial values are "false")
52 Frozen = ( bool * ) calloc( m * n, sizeof( bool ) );
53 narrowBand = new Heap(m *n);
54}
55
56Grid :: ~Grid()
57{
58 if ( Frozen ) {
59 free(Frozen);
60 }
61 delete narrowBand;
62}
63
64int
65Grid :: giveSize(int dir)
66{
67 switch ( dir ) {
68 case 1: return n;
69
70 case 2: return m;
71
72 default: return 0;
73 }
74}
75
76/*
77 * void
78 * Grid :: setPrescribedField(double *PF)
79 * {
80 * // For efficiency, F is passed by its pointer and from now on
81 * // will be taken care of (and deleted) by the Grid.
82 * if ( F ) {
83 * delete F;
84 * }
85 * F = PF;
86 * }
87 */
88
89void
90Grid :: unFreeze()
91{
92 for ( int ind = 0; ind < ( m * n ); ind++ ) {
93 Frozen [ ind ] = false;
94 }
95 narrowBand->setToEmpty(m * n);
96 solutionAvailable = false;
97}
98
99void
100Grid :: setZeroValues(FloatMatrix *gridCoords)
101{
102 // gridCoords contains coordinates of points at which the solution is set to zero.
103 // These coordinates must be expressed the grid coordinate system,
104 // with the first coordinate (j) from 1 to n and the second (i) from 1 to m.
105 // However, they do not need to be integers - the points do not need
106 // to coincide with grid nodes.
107
108 if ( gridCoords->giveNumberOfColumns() != 2 ) {
109 OOFEM_ERROR("Matrix gridCoords passed to Grid :: setZeroValues should have 2 columns.");
110 }
111 int nValues = gridCoords->giveNumberOfRows();
112 for ( int ival = 1; ival <= nValues; ival++ ) {
113 // get coordinates of the specified point
114 double CPx = gridCoords->at(ival, 1);
115 double CPy = gridCoords->at(ival, 2);
116 // find nearest grid node (i, j)
117 double j = ( int ) ceil(CPx - 0.5);
118 if ( j < 1 ) {
119 j = 1;
120 } else if ( j > n ) {
121 j = n;
122 }
123 double i = ( int ) ceil(CPy - 0.5);
124 if ( i < 1 ) {
125 i = 1;
126 } else if ( i > m ) {
127 i = m;
128 }
129 // determine the index of the nearest grid node
130 int CPInd = ij2ind((int)i, (int)j, (int)m);
131
132 // Calculate time-distance of nearest grid node and freeze the value
133 double Fij = F.at((size_t)i, (size_t)j);
134 T.at((size_t)i, (size_t)j) = sqrt( ( i - CPy ) * ( i - CPy ) + ( j - CPx ) * ( j - CPx ) ) / Fij;
135 Frozen [ CPInd ] = true;
136
137 // For four direct neighbors or all eight neighbours of the nearest grid node, do the same
138 // (depending on parameter initDiag)
139
140 if ( initDiag <= 0. ) { // initialize direct neighbors only
141 for ( int neigh = 0; neigh < 4; neigh++ ) {
142 int ni = (int)(i + iOffsets [ neigh ]);
143 int nj = (int)(j + jOffsets [ neigh ]);
144 if ( isInDomain(ni, nj, m, n) ) {
145 int nInd = ij2ind(ni, nj, m);
146 double time;
147 if ( centDiff > 0 ) { // use the average speed
148 time = sqrt( ( ni - CPy ) * ( ni - CPy ) + ( nj - CPx ) * ( nj - CPx ) ) / ( 0.5 * ( Fij + F.at(ni, nj) ) );
149 } else { // use the speed at the arrival point
150 time = sqrt( ( ni - CPy ) * ( ni - CPy ) + ( nj - CPx ) * ( nj - CPx ) ) / F.at(ni, nj);
151 }
152 if ( Frozen [ nInd ] ) {
153 T.at(ni, nj) = std::min( time, T.at(ni, nj) );
154 } else {
155 T.at(ni, nj) = time;
156 Frozen [ nInd ] = true;
157 }
158 }
159 }
160 } else { // initialize all neighbors
161 for ( int neigh = 0; neigh < 8; neigh++ ) {
162 int ni = (int) (i + iOffsets_full [ neigh ]);
163 int nj = (int) (j + jOffsets_full [ neigh ]);
164 if ( isInDomain(ni, nj, m, n) ) {
165 int nInd = ij2ind(ni, nj, m);
166 double time;
167 if ( centDiff > 0 ) { // use the average speed
168 time = sqrt( ( ni - CPy ) * ( ni - CPy ) + ( nj - CPx ) * ( nj - CPx ) ) / ( 0.5 * ( Fij + F.at(ni, nj) ) );
169 } else { // use the speed at the arrival point
170 time = sqrt( ( ni - CPy ) * ( ni - CPy ) + ( nj - CPx ) * ( nj - CPx ) ) / F.at(ni, nj);
171 }
172 // for diagonal neighbors, use initDiag as a scaling factor
173 if ( is_diag [ neigh ] ) {
174 time *= initDiag;
175 }
176 if ( Frozen [ nInd ] ) {
177 T.at(ni, nj) = std::min( time, T.at(ni, nj) );
178 } else {
179 T.at(ni, nj) = time;
180 Frozen [ nInd ] = true;
181 }
182 }
183 }
184 }
185 }
186 // after each external change, the solution becomes invalid
187 solutionAvailable = false;
188}
189
190void
191Grid :: setSolutionValueAt(int i, int j, double value)
192{
193 T.at(i, j) = value;
194 // after each external change, the solution becomes invalid
195 solutionAvailable = false;
196}
197
198double
199Grid :: giveSolutionValueAt(int i, int j)
200{
201 if ( !solutionAvailable ) {
202 int eFlag;
203 fastMarch(eFlag);
204 // TBD: one should check eFlag and send an error message if it is > 0
205 solutionAvailable = true;
206 }
207 return T.at(i, j);
208}
209
210void
211Grid :: printSolutionAsData()
212{
213 for ( int j = 1; j <= n; j++ ) {
214 printf("\n");
215 for ( int i = 1; i <= m; i++ ) {
216 printf( "%d %d %g\n", j, i, T.at(i, j) );
217 }
218 }
219}
220
222void
223Grid :: fastMarch(int &eFlag)
224{
225 eFlag = 0;
226
227 // Create the initial narrow band
228 if ( !narrowBand ) {
229 narrowBand = new Heap(m *n);
230 }
231
232 // Loop over all grid points (not efficient, but done only once)
233 for ( int ind = 0; ind < ( m * n ); ind++ ) {
234 if ( Frozen [ ind ] ) {
235 int i = ind2i(ind, m);
236 int j = ind2j(ind, m);
237
238 for ( int neigh = 0; neigh < 4; neigh++ ) {
239 int ni = i + iOffsets [ neigh ];
240 int nj = j + jOffsets [ neigh ];
241 int nInd = ij2ind(ni, nj, m);
242
243 if ( isInDomain(ni, nj, m, n) && !Frozen [ nInd ] ) {
244 if ( !narrowBand->isInHeap(nInd) ) {
245 int tmpFlag = 0;
246 double time = calcTime(ni, nj, F.at(ni, nj), order, tmpFlag);
247 narrowBand->insert(time, nInd);
248 if ( tmpFlag > eFlag ) {
249 eFlag = tmpFlag;
250 }
251 }
252 }
253 }
254 }
255 }
256
257 // Loop
258 int CPInd;
259 while ( narrowBand->nElems() > 0 ) {
260 // Get minimal time-distance and index of this narrow-band element
261 double time = narrowBand->getSmallest(& CPInd);
262 int i = ind2i(CPInd, m);
263 int j = ind2j(CPInd, m);
264
265 // Freeze and set time
266 Frozen [ CPInd ] = true;
267 T.at(i, j) = time;
268
269 // For all neighbours
270 for ( int neigh = 0; neigh < 4; neigh++ ) {
271 int ni = i + iOffsets [ neigh ];
272 int nj = j + jOffsets [ neigh ];
273 int nInd = ij2ind(ni, nj, m);
274
275 // If valid for consideration
276 if ( isInDomain(ni, nj, m, n) && !Frozen [ nInd ] ) {
277 int tmpFlag;
278 time = calcTime(ni, nj, F.at(ni, nj), order, tmpFlag);
279 // If T(ni,nj) has not been previously calculated
280 if ( !narrowBand->isInHeap(nInd) ) {
281 narrowBand->insert(time, nInd);
282 } else {
283 narrowBand->update(time, nInd);
284 }
285
286 if ( tmpFlag > eFlag ) {
287 eFlag = tmpFlag;
288 }
289 }
290 }
291 }
292 solutionAvailable = true;
293}
294
295// Time-dist calculation (for a grid with unit spacing)
296double
297Grid :: calcTime(int i, int j, double Fij, int ord, int &eFlag)
298{
299 // Get infinity.
300 // NB: Is this good practice? As in the matlab code, using Inf as
301 // a flag simplifies the code, so that's why I use this. Maybe this
302 // doesn't work with some compilers?
303 //double Inf = 1.0 / 0.0;
304 double Inf = std::numeric_limits<float>::infinity();
305
306 // Temporary error flag
307 int tmpFlag = 0;
308
309 // Frozen values at neighbors
310 double CrossVals [ 8 ];
311 //mj
312 double Fx = -1.;
313 double Fy = -1.;
314
315 // time calculated
316 double time;
317
318 // Variables used in the final formula
319 double xmin1 = 0., xmin2, ymin1 = 0., ymin2;
320
321 // Get values at surrounding nodes (at those that are frozen)
322 for ( int iter = 0; iter < 8; iter++ ) {
323 int ni = i + icalcOffsets [ iter ];
324 int nj = j + jcalcOffsets [ iter ];
325 int nInd = ij2ind(ni, nj, m);
326 if ( isInDomain(ni, nj, m, n) && Frozen [ nInd ] ) {
327 CrossVals [ iter ] = T.at(ni, nj);
328 } else {
329 CrossVals [ iter ] = Inf;
330 }
331 }
332
333 // If none of the immediate neighbors is frozen, we cannot proceed
334 if ( ( CrossVals [ 1 ] == Inf ) && ( CrossVals [ 2 ] == Inf ) &&
335 ( CrossVals [ 5 ] == Inf ) && ( CrossVals [ 6 ] == Inf ) ) {
336 eFlag = 0;
337 time = Inf;
338 return time;
339 }
340
341 // Calculate coefficients of quadratic equation
342 double a = 0.;
343 double b = 0.;
344 // mj double c = -1. / ( Fij * Fij );
345 double c = 0.;
346
347 switch ( ord ) {
348 // First-order algorithm
349 case 1:
350 // Contribution of y-direction
351 if ( !( ( CrossVals [ 1 ] == Inf ) && ( CrossVals [ 2 ] == Inf ) ) ) {
352 ymin1 = std::min(CrossVals [ 1 ], CrossVals [ 2 ]);
353 a += 1.;
354 b -= 2. * ymin1;
355 c += ymin1 * ymin1;
356 //mj
357 int ni, nj;
358 if ( CrossVals [ 1 ] < CrossVals [ 2 ] ) {
359 ni = i + icalcOffsets [ 1 ];
360 nj = j + jcalcOffsets [ 1 ];
361 } else {
362 ni = i + icalcOffsets [ 2 ];
363 nj = j + jcalcOffsets [ 2 ];
364 }
365 Fy = F.at(ni, nj);
366 //end mj
367 }
368
369 // Contribution of x-direction
370 if ( !( ( CrossVals [ 5 ] == Inf ) && ( CrossVals [ 6 ] == Inf ) ) ) {
371 xmin1 = std::min(CrossVals [ 5 ], CrossVals [ 6 ]);
372 a += 1.;
373 b -= 2. * xmin1;
374 c += xmin1 * xmin1;
375 //mj
376 int ni, nj;
377 if ( CrossVals [ 5 ] < CrossVals [ 6 ] ) {
378 ni = i + icalcOffsets [ 5 ];
379 nj = j + jcalcOffsets [ 5 ];
380 } else {
381 ni = i + icalcOffsets [ 6 ];
382 nj = j + jcalcOffsets [ 6 ];
383 }
384 Fx = F.at(ni, nj);
385 //end mj
386 }
387 break;
388
389 // Second-order algorithm
390 case 2:
391 default:
392 // Contribution of y-direction
393 if ( !( ( CrossVals [ 1 ] == Inf ) && ( CrossVals [ 2 ] == Inf ) ) ) {
394 if ( CrossVals [ 1 ] < CrossVals [ 2 ] ) {
395 ymin1 = CrossVals [ 1 ];
396 //mj
397 int ni = i + icalcOffsets [ 1 ];
398 int nj = j + jcalcOffsets [ 1 ];
399 Fy = F.at(ni, nj);
400 //end mj
401 if ( CrossVals [ 0 ] <= CrossVals [ 1 ] ) { //second-order formula can be applied
402 ymin2 = CrossVals [ 0 ];
403 a += 2.25;
404 b += -6. * ymin1 + 1.5 * ymin2;
405 c += 4.0 * ymin1 * ymin1 + 0.25 * ymin2 * ymin2 - 2.0 * ymin1 * ymin2;
406 } else { //first-order formula
407 a += 1.;
408 b -= 2. * ymin1;
409 c += ymin1 * ymin1;
410 }
411 } else {
412 ymin1 = CrossVals [ 2 ];
413 //mj
414 int ni = i + icalcOffsets [ 2 ];
415 int nj = j + jcalcOffsets [ 2 ];
416 Fy = F.at(ni, nj);
417 //end mj
418 if ( CrossVals [ 3 ] <= CrossVals [ 2 ] ) { //second-order formula can be applied
419 ymin2 = CrossVals [ 3 ];
420 a += 2.25;
421 b += -6. * ymin1 + 1.5 * ymin2;
422 c += 4.0 * ymin1 * ymin1 + 0.25 * ymin2 * ymin2 - 2.0 * ymin1 * ymin2;
423 } else { //first-order formula
424 a += 1.;
425 b -= 2. * ymin1;
426 c += ymin1 * ymin1;
427 }
428 }
429 }
430 // Contribution of x-direction
431 if ( !( ( CrossVals [ 5 ] == Inf ) && ( CrossVals [ 6 ] == Inf ) ) ) {
432 if ( CrossVals [ 5 ] < CrossVals [ 6 ] ) {
433 xmin1 = CrossVals [ 5 ];
434 //mj
435 int ni = i + icalcOffsets [ 5 ];
436 int nj = j + jcalcOffsets [ 5 ];
437 Fx = F.at(ni, nj);
438 //end mj
439 if ( CrossVals [ 4 ] <= CrossVals [ 5 ] ) { //second-order formula can be applied
440 xmin2 = CrossVals [ 4 ];
441 a += 2.25;
442 b += -6. * xmin1 + 1.5 * xmin2;
443 c += 4.0 * xmin1 * xmin1 + 0.25 * xmin2 * xmin2 - 2.0 * xmin1 * xmin2;
444 } else { //first-order formula
445 a += 1.;
446 b -= 2. * xmin1;
447 c += xmin1 * xmin1;
448 }
449 } else {
450 xmin1 = CrossVals [ 6 ];
451 //mj
452 int ni = i + icalcOffsets [ 6 ];
453 int nj = j + jcalcOffsets [ 6 ];
454 Fx = F.at(ni, nj);
455 //end mj
456 if ( CrossVals [ 7 ] <= CrossVals [ 6 ] ) { //second-order formula can be applied
457 xmin2 = CrossVals [ 7 ];
458 a += 2.25;
459 b += -6. * xmin1 + 1.5 * xmin2;
460 c += 4.0 * xmin1 * xmin1 + 0.25 * xmin2 * xmin2 - 2.0 * xmin1 * xmin2;
461 } else { //first-order formula
462 a += 1.;
463 b -= 2. * xmin1;
464 c += xmin1 * xmin1;
465 }
466 }
467 }
468 }
469
470 // for centDiff=2, the average speed is used, otherwise the speed at arrival point is used
471 double Faver;
472 if ( centDiff != 2 ) {
473 Faver = Fij;
474 } else if ( Fx >= 0. && Fy >= 0. ) {
475 Faver = 0.5 * Fij + 0.25 * ( Fx + Fy );
476 } else if ( Fx >= 0. ) {
477 Faver = 0.5 * ( Fij + Fx );
478 } else if ( Fy >= 0. ) {
479 Faver = 0.5 * ( Fij + Fy );
480 } else {
481 Faver = Fij;
482 }
483
484 c -= 1. / ( Faver * Faver );
485
486 // Solve quadratic equation
487 double d = ( b * b ) - ( 4.0 * a * c );
488
489 // Error treatment and appropriate time-dist calculation
490 if ( ( d < 0.0 ) && ( ord == 2 ) ) {
491 // if second-order method did not work, try first-order formula
492 time = calcTime(i, j, Fij, 1, tmpFlag);
493 eFlag = std::max(1, tmpFlag);
494 } else if ( ( d < 0.0 ) && ( ord == 1 ) ) {
495 if ( ( CrossVals [ 1 ] == Inf ) && ( CrossVals [ 2 ] == Inf ) ) {
496 ymin1 = Inf;
497 }
498 if ( ( CrossVals [ 5 ] == Inf ) && ( CrossVals [ 6 ] == Inf ) ) {
499 xmin1 = Inf;
500 }
501
502 eFlag = 2;
503 time = std::min(xmin1, ymin1) + 1.0 / Fij;
504 } else {
505 eFlag = 0;
506 time = ( -b + sqrt(d) ) / ( 2.0 * a );
507 }
508
509 return time;
510}
511}
#define N(a, b)
int giveNumberOfColumns() const
Returns number of columns of receiver.
int giveNumberOfRows() const
Returns number of rows of receiver.
double at(std::size_t i, std::size_t j) const
FloatMatrix T
Matrix storing the values of the unknown (computed) field (e.g., arrival times).
Definition grid.h:78
bool solutionAvailable
Flag indicating whether the solution has been computed.
Definition grid.h:75
int ij2ind(int _i, int _j, int _m)
Utility methods.
Definition grid.h:96
bool isInDomain(int i, int j, int _m, int _n)
Definition grid.h:99
int ind2i(int ind, int _m)
Definition grid.h:97
void fastMarch(int &eFlag)
Fast marching method, solving the eikonal equation.
Definition grid.C:223
Heap * narrowBand
Heap used for efficient sorting and detection of smallest candidate.
Definition grid.h:87
int centDiff
Definition grid.h:72
double initDiag
Definition grid.h:71
double calcTime(int i, int j, double Fij, int ord, int &eFlag)
Auxiliary function that evaluates the tentative value at one grid point, exploited by the fast marchi...
Definition grid.C:297
int ind2j(int ind, int _m)
Definition grid.h:98
int m
Definition grid.h:67
int n
Grid dimensions: number of grid nodes horizontally (n) and vertically (m).
Definition grid.h:67
int order
Algorithmic parameters.
Definition grid.h:70
bool * Frozen
Array storing indicators of frozen points (at which the solution is already known).
Definition grid.h:84
FloatMatrix F
Matrix storing the values of the prescribed field (e.g., front velocities).
Definition grid.h:81
#define OOFEM_ERROR(...)
Definition error.h:79
int iOffsets[]
Definition grid.C:26
int icalcOffsets[]
Definition grid.C:36
int iOffsets_full[]
Definition grid.C:13
int jOffsets[]
Definition grid.C:29
int jOffsets_full[]
Definition grid.C:16
bool is_diag[]
Definition grid.C:21
int jcalcOffsets[]
Definition grid.C:39

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011