1 | /* |
2 | * FiniteDifferences.hpp |
3 | * |
4 | * Created on: Sep 17, 2015 |
5 | * Author: i-bird |
6 | */ |
7 | |
8 | #ifndef OPENFPM_NUMERICS_SRC_FINITEDIFFERENCE_FDSCHEME_HPP_ |
9 | #define OPENFPM_NUMERICS_SRC_FINITEDIFFERENCE_FDSCHEME_HPP_ |
10 | |
11 | #include "../Matrix/SparseMatrix.hpp" |
12 | #include "Grid/grid_dist_id.hpp" |
13 | #include "Grid/Iterators/grid_dist_id_iterator_sub.hpp" |
14 | #include "eq.hpp" |
15 | #include "NN/CellList/CellDecomposer.hpp" |
16 | #include "Grid/staggered_dist_grid_util.hpp" |
17 | #include "Grid/grid_dist_id.hpp" |
18 | #include "Vector/Vector_util.hpp" |
19 | #include "Grid/staggered_dist_grid.hpp" |
20 | |
21 | /*! \brief Finite Differences |
22 | * |
23 | * This class is able to discretize on a Matrix any system of equations producing a linear system of type \f$Ax=b\f$. In order to create a consistent |
24 | * Matrix it is required that each processor must contain a contiguous range on grid points without |
25 | * holes. In order to ensure this, each processor produce a contiguous local labeling of its local |
26 | * points. Each processor also add an offset equal to the number of local |
27 | * points of the processors with id smaller than him, to produce a global and non overlapping |
28 | * labeling. An example is shown in the figures down, here we have |
29 | * a grid 8x6 divided across four processors each processor label locally its grid points |
30 | * |
31 | * \verbatim |
32 | * |
33 | +--------------------------+ |
34 | | 1 2 3 4| 1 2 3 4| |
35 | | | | |
36 | | 5 6 7 8| 5 6 7 8| |
37 | | | | |
38 | | 9 10 11 12| 9 10 11 12| |
39 | +--------------------------+ |
40 | |13 14 15| 13 14 15 16 17| |
41 | | | | |
42 | |16 17 18| 18 19 20 21 22| |
43 | | | | |
44 | |19 20 21| 23 24 25 26 27| |
45 | +--------------------------+ |
46 | |
47 | * |
48 | * |
49 | * \endverbatim |
50 | * |
51 | * To the local relabelling is added an offset to make the local id global and non overlapping |
52 | * |
53 | * |
54 | * \verbatim |
55 | * |
56 | +--------------------------+ |
57 | | 1 2 3 4|23 24 25 26| |
58 | | | | |
59 | | 5 6 7 8|27 28 29 30| |
60 | | | | |
61 | | 9 10 12 13|31 32 33 34| |
62 | +--------------------------+ |
63 | |14 15 16| 35 36 37 38 39| |
64 | | | | |
65 | |17 18 19| 40 41 42 43 44| |
66 | | | | |
67 | |20 21 22| 45 46 47 48 49| |
68 | +--------------------------+ |
69 | * |
70 | * |
71 | * \endverbatim |
72 | * |
73 | * \tparam Sys_eqs Definition of the system of equations |
74 | * |
75 | * # Examples |
76 | * |
77 | * ## Solve lid-driven cavity 2D for incompressible fluid (inertia=0 --> Re=0) |
78 | * |
79 | * In this case the system of equation to solve is |
80 | * |
81 | * \f$ |
82 | \left\{ |
83 | \begin{array}{c} |
84 | \eta\nabla v_x + \partial_x P = 0 \quad Eq1 \\ |
85 | \eta\nabla v_y + \partial_y P = 0 \quad Eq2 \\ |
86 | \partial_x v_x + \partial_y v_y = 0 \quad Eq3 |
87 | \end{array} |
88 | \right. \f$ |
89 | |
90 | and boundary conditions |
91 | |
92 | * \f$ |
93 | \left\{ |
94 | \begin{array}{c} |
95 | v_x = 0, v_y = 0 \quad x = 0 \quad B1\\ |
96 | v_x = 0, v_y = 1.0 \quad x = L \quad B2\\ |
97 | v_x = 0, v_y = 0 \quad y = 0 \quad B3\\ |
98 | v_x = 0, v_y = 0 \quad y = L \quad B4\\ |
99 | \end{array} |
100 | \right. \f$ |
101 | |
102 | * |
103 | * with \f$v_x\f$ and \f$v_y\f$ the velocity in x and y and \f$P\f$ Pressure |
104 | * |
105 | * In order to solve such system first we define the general properties of the system |
106 | * |
107 | * \snippet eq_unit_test.hpp Definition of the system |
108 | * |
109 | * ## Define the equations of the system |
110 | * |
111 | * \snippet eq_unit_test.hpp Definition of the equation of the system in the bulk and at the boundary |
112 | * |
113 | * ## Define the domain and impose the equations |
114 | * |
115 | * \snippet eq_unit_test.hpp lid-driven cavity 2D |
116 | * |
117 | * # 3D |
118 | * |
119 | * A 3D case is given in the examples |
120 | * |
121 | */ |
122 | |
123 | template<typename Sys_eqs> |
124 | class FDScheme |
125 | { |
126 | public: |
127 | |
128 | //! Distributed grid map |
129 | typedef grid_dist_id<Sys_eqs::dims,typename Sys_eqs::stype,aggregate<size_t>,typename Sys_eqs::b_grid::decomposition::extended_type> g_map_type; |
130 | |
131 | //! Type that specify the properties of the system of equations |
132 | typedef Sys_eqs Sys_eqs_typ; |
133 | |
134 | //! Encapsulation of the b term as constant |
135 | struct constant_b |
136 | { |
137 | //! scalar |
138 | typename Sys_eqs::stype scal; |
139 | |
140 | /*! \brief Constrictor from a scalar |
141 | * |
142 | * \param scal scalar |
143 | * |
144 | */ |
145 | constant_b(typename Sys_eqs::stype scal) |
146 | { |
147 | this->scal = scal; |
148 | } |
149 | |
150 | /*! \brief Get the b term on a grid point |
151 | * |
152 | * \note It does not matter the grid point it is a scalar |
153 | * |
154 | * \param key grid position (unused because it is a constant) |
155 | * |
156 | * \return the scalar |
157 | * |
158 | */ |
159 | typename Sys_eqs::stype get(grid_dist_key_dx<Sys_eqs::dims> & key) |
160 | { |
161 | return scal; |
162 | } |
163 | }; |
164 | |
165 | //! Encapsulation of the b term as grid |
166 | template<typename grid, unsigned int prp> |
167 | struct grid_b |
168 | { |
169 | //! b term fo the grid |
170 | grid & gr; |
171 | |
172 | /*! \brief gr grid that encapsulate |
173 | * |
174 | * \param gr grid |
175 | * |
176 | */ |
177 | grid_b(grid & gr) |
178 | :gr(gr) |
179 | {} |
180 | |
181 | /*! \brief Get the value of the b term on a grid point |
182 | * |
183 | * \param key grid point |
184 | * |
185 | * \return the value |
186 | * |
187 | */ |
188 | auto get(grid_dist_key_dx<Sys_eqs::dims> & key) -> decltype(gr.template get<prp>(key)) |
189 | { |
190 | return gr.template get<prp>(key); |
191 | } |
192 | }; |
193 | |
194 | private: |
195 | |
196 | //! Padding |
197 | Padding<Sys_eqs::dims> pd; |
198 | |
199 | //! Sparse matrix triplet type |
200 | typedef typename Sys_eqs::SparseMatrix_type::triplet_type triplet; |
201 | |
202 | //! Vector b |
203 | typename Sys_eqs::Vector_type b; |
204 | |
205 | //! Domain Grid informations |
206 | const grid_sm<Sys_eqs::dims,void> & gs; |
207 | |
208 | //! Get the grid spacing |
209 | typename Sys_eqs::stype spacing[Sys_eqs::dims]; |
210 | |
211 | //! mapping grid |
212 | g_map_type g_map; |
213 | |
214 | //! row of the matrix |
215 | size_t row; |
216 | |
217 | //! row on b |
218 | size_t row_b; |
219 | |
220 | //! Grid points that has each processor |
221 | openfpm::vector<size_t> pnt; |
222 | |
223 | //! Staggered position for each property |
224 | comb<Sys_eqs::dims> s_pos[Sys_eqs::nvar]; |
225 | |
226 | //! Each point in the grid has a global id, to decompose correctly the Matrix each processor contain a |
227 | //! contiguos range of global id, example processor 0 can have from 0 to 234 and processor 1 from 235 to 512 |
228 | //! no processors can have holes in the sequence, this number indicate where the sequence start for this |
229 | //! processor |
230 | size_t s_pnt; |
231 | |
232 | /*! \brief Equation id + space position |
233 | * |
234 | */ |
235 | struct key_and_eq |
236 | { |
237 | //! space position |
238 | grid_key_dx<Sys_eqs::dims> key; |
239 | |
240 | //! equation id |
241 | size_t eq; |
242 | }; |
243 | |
244 | /*! \brief From the row Matrix position to the spatial position |
245 | * |
246 | * \param row Matrix row |
247 | * |
248 | * \return spatial position + equation id |
249 | * |
250 | */ |
251 | inline key_and_eq from_row_to_key(size_t row) |
252 | { |
253 | key_and_eq ke; |
254 | auto it = g_map.getDomainIterator(); |
255 | |
256 | while (it.isNext()) |
257 | { |
258 | size_t row_low = g_map.template get<0>(it.get()); |
259 | |
260 | if (row >= row_low * Sys_eqs::nvar && row < row_low * Sys_eqs::nvar + Sys_eqs::nvar) |
261 | { |
262 | ke.eq = row - row_low * Sys_eqs::nvar; |
263 | ke.key = g_map.getGKey(it.get()); |
264 | return ke; |
265 | } |
266 | |
267 | ++it; |
268 | } |
269 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " the row does not map to any position" << "\n" ; |
270 | |
271 | return ke; |
272 | } |
273 | |
274 | /*! \brief calculate the mapping grid size with padding |
275 | * |
276 | * \param sz original grid size |
277 | * \param pd padding |
278 | * |
279 | * \return padded grid size |
280 | * |
281 | */ |
282 | inline const std::vector<size_t> padding( const size_t (& sz)[Sys_eqs::dims], Padding<Sys_eqs::dims> & pd) |
283 | { |
284 | std::vector<size_t> g_sz_pad(Sys_eqs::dims); |
285 | |
286 | for (size_t i = 0 ; i < Sys_eqs::dims ; i++) |
287 | g_sz_pad[i] = sz[i] + pd.getLow(i) + pd.getHigh(i); |
288 | |
289 | return g_sz_pad; |
290 | } |
291 | |
292 | /*! \brief Check if the Matrix is consistent |
293 | * |
294 | */ |
295 | void consistency() |
296 | { |
297 | openfpm::vector<triplet> & trpl = A.getMatrixTriplets(); |
298 | |
299 | // A and B must have the same rows |
300 | if (row != row_b) |
301 | std::cerr << "Error " << __FILE__ << ":" << __LINE__ << "the term B and the Matrix A for Ax=B must contain the same number of rows\n" ; |
302 | |
303 | // Indicate all the non zero rows |
304 | openfpm::vector<unsigned char> nz_rows; |
305 | nz_rows.resize(row_b); |
306 | |
307 | for (size_t i = 0 ; i < trpl.size() ; i++) |
308 | nz_rows.get(trpl.get(i).row() - s_pnt*Sys_eqs::nvar) = true; |
309 | |
310 | // Indicate all the non zero colums |
311 | // This check can be done only on single processor |
312 | |
313 | Vcluster<> & v_cl = create_vcluster(); |
314 | if (v_cl.getProcessingUnits() == 1) |
315 | { |
316 | openfpm::vector<unsigned> nz_cols; |
317 | nz_cols.resize(row_b); |
318 | |
319 | for (size_t i = 0 ; i < trpl.size() ; i++) |
320 | nz_cols.get(trpl.get(i).col()) = true; |
321 | |
322 | // all the rows must have a non zero element |
323 | for (size_t i = 0 ; i < nz_rows.size() ; i++) |
324 | { |
325 | if (nz_rows.get(i) == false) |
326 | { |
327 | key_and_eq ke = from_row_to_key(i); |
328 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " Ill posed matrix row " << i << " is not filled, position " << ke.key.to_string() << " equation: " << ke.eq << "\n" ; |
329 | } |
330 | } |
331 | |
332 | // all the colums must have a non zero element |
333 | for (size_t i = 0 ; i < nz_cols.size() ; i++) |
334 | { |
335 | if (nz_cols.get(i) == false) |
336 | std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " Ill posed matrix colum " << i << " is not filled\n" ; |
337 | } |
338 | } |
339 | } |
340 | |
341 | /*! \brief Copy a given solution vector in a staggered grid |
342 | * |
343 | * \tparam Vct Vector type |
344 | * \tparam Grid_dst target grid |
345 | * \tparam pos set of properties |
346 | * |
347 | * \param v Vector |
348 | * \param g_dst target staggered grid |
349 | * |
350 | */ |
351 | template<typename Vct, typename Grid_dst ,unsigned int ... pos> void copy_staggered(Vct & v, Grid_dst & g_dst) |
352 | { |
353 | // check that g_dst is staggered |
354 | if (g_dst.is_staggered() == false) |
355 | std::cerr << __FILE__ << ":" << __LINE__ << " The destination grid must be staggered " << std::endl; |
356 | |
357 | #ifdef SE_CLASS1 |
358 | |
359 | if (g_map.getLocalDomainSize() != g_dst.getLocalDomainSize()) |
360 | std::cerr << __FILE__ << ":" << __LINE__ << " The staggered and destination grid in size does not match " << std::endl; |
361 | #endif |
362 | |
363 | // sub-grid iterator over the grid map |
364 | auto g_map_it = g_map.getDomainIterator(); |
365 | |
366 | // Iterator over the destination grid |
367 | auto g_dst_it = g_dst.getDomainIterator(); |
368 | |
369 | while (g_map_it.isNext() == true) |
370 | { |
371 | typedef typename to_boost_vmpl<pos...>::type vid; |
372 | typedef boost::mpl::size<vid> v_size; |
373 | |
374 | auto key_src = g_map_it.get(); |
375 | size_t lin_id = g_map.template get<0>(key_src); |
376 | |
377 | // destination point |
378 | auto key_dst = g_dst_it.get(); |
379 | |
380 | // Transform this id into an id for the Eigen vector |
381 | |
382 | copy_ele<Sys_eqs_typ, Grid_dst,Vct> cp(key_dst,g_dst,v,lin_id,g_map.size()); |
383 | |
384 | boost::mpl::for_each_ref<boost::mpl::range_c<int,0,v_size::value>>(cp); |
385 | |
386 | ++g_map_it; |
387 | ++g_dst_it; |
388 | } |
389 | } |
390 | |
391 | /*! \brief Copy a given solution vector in a normal grid |
392 | * |
393 | * \tparam Vct Vector type |
394 | * \tparam Grid_dst target grid |
395 | * \tparam pos set of property |
396 | * |
397 | * \param v Vector |
398 | * \param g_dst target normal grid |
399 | * |
400 | */ |
401 | template<typename Vct, typename Grid_dst ,unsigned int ... pos> void copy_normal(Vct & v, Grid_dst & g_dst) |
402 | { |
403 | // check that g_dst is staggered |
404 | if (g_dst.is_staggered() == true) |
405 | std::cerr << __FILE__ << ":" << __LINE__ << " The destination grid must be normal " << std::endl; |
406 | |
407 | grid_key_dx<Grid_dst::dims> start; |
408 | grid_key_dx<Grid_dst::dims> stop; |
409 | |
410 | for (size_t i = 0 ; i < Grid_dst::dims ; i++) |
411 | { |
412 | start.set_d(i,pd.getLow(i)); |
413 | stop.set_d(i,g_map.size(i) - pd.getHigh(i)); |
414 | } |
415 | |
416 | // sub-grid iterator over the grid map |
417 | auto g_map_it = g_map.getSubDomainIterator(start,stop); |
418 | |
419 | // Iterator over the destination grid |
420 | auto g_dst_it = g_dst.getDomainIterator(); |
421 | |
422 | while (g_dst_it.isNext() == true) |
423 | { |
424 | typedef typename to_boost_vmpl<pos...>::type vid; |
425 | typedef boost::mpl::size<vid> v_size; |
426 | |
427 | auto key_src = g_map_it.get(); |
428 | size_t lin_id = g_map.template get<0>(key_src); |
429 | |
430 | // destination point |
431 | auto key_dst = g_dst_it.get(); |
432 | |
433 | // Transform this id into an id for the vector |
434 | |
435 | copy_ele<Sys_eqs_typ, Grid_dst,Vct> cp(key_dst,g_dst,v,lin_id,g_map.size()); |
436 | |
437 | boost::mpl::for_each_ref<boost::mpl::range_c<int,0,v_size::value>>(cp); |
438 | |
439 | ++g_map_it; |
440 | ++g_dst_it; |
441 | } |
442 | } |
443 | |
444 | /*! \brief Impose an operator |
445 | * |
446 | * This function the RHS no matrix is imposed. This |
447 | * function is usefull if the Matrix has been constructed and only |
448 | * the right hand side b must be changed |
449 | * |
450 | * Ax = b |
451 | * |
452 | * \param num right hand side of the term (b term) |
453 | * \param id Equation id in the system that we are imposing |
454 | * \param it_d iterator that define where you want to impose |
455 | * |
456 | */ |
457 | template<typename bop, typename iterator> |
458 | void impose_dit_b(bop num, |
459 | long int id , |
460 | const iterator & it_d) |
461 | { |
462 | |
463 | auto it = it_d; |
464 | grid_sm<Sys_eqs::dims,void> gs = g_map.getGridInfoVoid(); |
465 | |
466 | // iterate all the grid points |
467 | while (it.isNext()) |
468 | { |
469 | // get the position |
470 | auto key = it.get(); |
471 | |
472 | b(g_map.template get<0>(key)*Sys_eqs::nvar + id) = num.get(key); |
473 | |
474 | // if SE_CLASS1 is defined check the position |
475 | #ifdef SE_CLASS1 |
476 | // T::position(key,gs,s_pos); |
477 | #endif |
478 | |
479 | ++row_b; |
480 | ++it; |
481 | } |
482 | } |
483 | |
484 | /*! \brief Impose an operator |
485 | * |
486 | * This function impose an operator on a particular grid region to produce the system |
487 | * |
488 | * Ax = b |
489 | * |
490 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
491 | * \snippet eq_unit_test.hpp Copy the solution to grid |
492 | * |
493 | * \param op Operator to impose (A term) |
494 | * \param num right hand side of the term (b term) |
495 | * \param id Equation id in the system that we are imposing |
496 | * \param it_d iterator that define where you want to impose |
497 | * |
498 | */ |
499 | template<typename T, typename bop, typename iterator> void impose_dit(const T & op, |
500 | bop num, |
501 | long int id , |
502 | const iterator & it_d) |
503 | { |
504 | openfpm::vector<triplet> & trpl = A.getMatrixTriplets(); |
505 | |
506 | auto it = it_d; |
507 | grid_sm<Sys_eqs::dims,void> gs = g_map.getGridInfoVoid(); |
508 | |
509 | std::unordered_map<long int,typename Sys_eqs::stype> cols; |
510 | |
511 | // iterate all the grid points |
512 | while (it.isNext()) |
513 | { |
514 | // get the position |
515 | auto key = it.get(); |
516 | |
517 | // Add padding |
518 | for (size_t i = 0 ; i < Sys_eqs::dims ; i++) |
519 | key.getKeyRef().set_d(i,key.getKeyRef().get(i) + pd.getLow(i)); |
520 | |
521 | // Calculate the non-zero colums |
522 | T::value(g_map,key,gs,spacing,cols,1.0); |
523 | |
524 | // indicate if the diagonal has been set |
525 | bool is_diag = false; |
526 | |
527 | // create the triplet |
528 | for ( auto it = cols.begin(); it != cols.end(); ++it ) |
529 | { |
530 | trpl.add(); |
531 | trpl.last().row() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
532 | trpl.last().col() = it->first; |
533 | trpl.last().value() = it->second; |
534 | |
535 | if (trpl.last().row() == trpl.last().col()) |
536 | {is_diag = true;} |
537 | |
538 | } |
539 | |
540 | // If does not have a diagonal entry put it to zero |
541 | if (is_diag == false) |
542 | { |
543 | trpl.add(); |
544 | trpl.last().row() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
545 | trpl.last().col() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
546 | trpl.last().value() = 0.0; |
547 | } |
548 | |
549 | b(g_map.template get<0>(key)*Sys_eqs::nvar + id) = num.get(key); |
550 | |
551 | cols.clear(); |
552 | |
553 | // if SE_CLASS1 is defined check the position |
554 | #ifdef SE_CLASS1 |
555 | // T::position(key,gs,s_pos); |
556 | #endif |
557 | |
558 | ++row; |
559 | ++row_b; |
560 | ++it; |
561 | } |
562 | } |
563 | |
564 | |
565 | |
566 | /*! \brief Construct the gmap structure |
567 | * |
568 | */ |
569 | void construct_gmap() |
570 | { |
571 | Vcluster<> & v_cl = create_vcluster(); |
572 | |
573 | // Calculate the size of the local domain |
574 | size_t sz = g_map.getLocalDomainSize(); |
575 | |
576 | // Get the total size of the local grids on each processors |
577 | v_cl.allGather(sz,pnt); |
578 | v_cl.execute(); |
579 | s_pnt = 0; |
580 | |
581 | // calculate the starting point for this processor |
582 | for (size_t i = 0 ; i < v_cl.getProcessUnitID() ; i++) |
583 | s_pnt += pnt.get(i); |
584 | |
585 | // resize b if needed |
586 | b.resize(Sys_eqs::nvar * g_map.size(),Sys_eqs::nvar * sz); |
587 | |
588 | // Calculate the starting point |
589 | |
590 | // Counter |
591 | size_t cnt = 0; |
592 | |
593 | // Create the re-mapping grid |
594 | auto it = g_map.getDomainIterator(); |
595 | |
596 | while (it.isNext()) |
597 | { |
598 | auto key = it.get(); |
599 | |
600 | g_map.template get<0>(key) = cnt + s_pnt; |
601 | |
602 | ++cnt; |
603 | ++it; |
604 | } |
605 | |
606 | // sync the ghost |
607 | g_map.template ghost_get<0>(); |
608 | } |
609 | |
610 | /*! \initialize the object FDScheme |
611 | * |
612 | * \param domain simulation domain |
613 | * |
614 | */ |
615 | void Initialize(const Box<Sys_eqs::dims,typename Sys_eqs::stype> & domain) |
616 | { |
617 | construct_gmap(); |
618 | |
619 | // Create a CellDecomposer and calculate the spacing |
620 | |
621 | size_t sz_g[Sys_eqs::dims]; |
622 | for (size_t i = 0 ; i < Sys_eqs::dims ; i++) |
623 | { |
624 | if (Sys_eqs::boundary[i] == NON_PERIODIC) |
625 | sz_g[i] = gs.getSize()[i] - 1; |
626 | else |
627 | sz_g[i] = gs.getSize()[i]; |
628 | } |
629 | |
630 | CellDecomposer_sm<Sys_eqs::dims,typename Sys_eqs::stype> cd(domain,sz_g,0); |
631 | |
632 | for (size_t i = 0 ; i < Sys_eqs::dims ; i++) |
633 | spacing[i] = cd.getCellBox().getHigh(i); |
634 | } |
635 | |
636 | public: |
637 | |
638 | /*! \brief set the staggered position for each property |
639 | * |
640 | * \param sp vector containing the staggered position for each property |
641 | * |
642 | */ |
643 | void setStagPos(comb<Sys_eqs::dims> (& sp)[Sys_eqs::nvar]) |
644 | { |
645 | for (size_t i = 0 ; i < Sys_eqs::nvar ; i++) |
646 | s_pos[i] = sp[i]; |
647 | } |
648 | |
649 | /*! \brief compute the staggered position for each property |
650 | * |
651 | * This is compute from the value_type stored by Sys_eqs::b_grid::value_type |
652 | * the position of the staggered properties |
653 | * |
654 | * |
655 | */ |
656 | void computeStag() |
657 | { |
658 | typedef typename Sys_eqs::b_grid::value_type prp_type; |
659 | |
660 | openfpm::vector<comb<Sys_eqs::dims>> c_prp[prp_type::max_prop]; |
661 | |
662 | stag_set_position<Sys_eqs::dims,prp_type> ssp(c_prp); |
663 | |
664 | boost::mpl::for_each_ref< boost::mpl::range_c<int,0,prp_type::max_prop> >(ssp); |
665 | } |
666 | |
667 | /*! \brief Get the specified padding |
668 | * |
669 | * \return the padding specified |
670 | * |
671 | */ |
672 | const Padding<Sys_eqs::dims> & getPadding() |
673 | { |
674 | return pd; |
675 | } |
676 | |
677 | /*! \brief Return the map between the grid index position and the position in the distributed vector |
678 | * |
679 | * It is the map explained in the intro of the FDScheme |
680 | * |
681 | * \return the map |
682 | * |
683 | */ |
684 | const g_map_type & getMap() |
685 | { |
686 | return g_map; |
687 | } |
688 | |
689 | /*! \brief Constructor |
690 | * |
691 | * The periodicity is given by the grid b_g |
692 | * |
693 | * \param stencil maximum extension of the stencil on each directions |
694 | * \param domain the domain |
695 | * \param b_g object grid that will store the solution |
696 | * |
697 | */ |
698 | FDScheme(const Ghost<Sys_eqs::dims,long int> & stencil, |
699 | const Box<Sys_eqs::dims,typename Sys_eqs::stype> & domain, |
700 | const typename Sys_eqs::b_grid & b_g) |
701 | :pd({0,0,0},{0,0,0}),gs(b_g.getGridInfoVoid()),g_map(b_g,stencil,pd),row(0),row_b(0) |
702 | { |
703 | Initialize(domain); |
704 | } |
705 | |
706 | /*! \brief Constructor |
707 | * |
708 | * The periodicity is given by the grid b_g |
709 | * |
710 | * \param pd Padding, how many points out of boundary are present |
711 | * \param stencil maximum extension of the stencil on each directions |
712 | * \param domain the domain |
713 | * \param b_g object grid that will store the solution |
714 | * |
715 | */ |
716 | FDScheme(Padding<Sys_eqs::dims> & pd, |
717 | const Ghost<Sys_eqs::dims,long int> & stencil, |
718 | const Box<Sys_eqs::dims,typename Sys_eqs::stype> & domain, |
719 | const typename Sys_eqs::b_grid & b_g) |
720 | :pd(pd),gs(b_g.getGridInfoVoid()),g_map(b_g,stencil,pd),row(0),row_b(0) |
721 | { |
722 | Initialize(domain); |
723 | } |
724 | |
725 | /*! \brief Impose an operator |
726 | * |
727 | * This function impose an operator on a box region to produce the system |
728 | * |
729 | * Ax = b |
730 | * |
731 | * ## Stokes equation, lid driven cavity with one splipping wall |
732 | * \snippet eq_unit_test.hpp lid-driven cavity 2D |
733 | * |
734 | * \param op Operator to impose (A term) |
735 | * \param num right hand side of the term (b term) |
736 | * \param id Equation id in the system that we are imposing |
737 | * \param start starting point of the box |
738 | * \param stop stop point of the box |
739 | * \param skip_first skip the first point |
740 | * |
741 | */ |
742 | template<typename T> void impose(const T & op, |
743 | typename Sys_eqs::stype num, |
744 | long int id, |
745 | const long int (& start)[Sys_eqs::dims], |
746 | const long int (& stop)[Sys_eqs::dims], |
747 | bool skip_first = false) |
748 | { |
749 | grid_key_dx<Sys_eqs::dims> start_k; |
750 | grid_key_dx<Sys_eqs::dims> stop_k; |
751 | |
752 | bool increment = false; |
753 | if (skip_first == true) |
754 | { |
755 | start_k = grid_key_dx<Sys_eqs::dims>(start); |
756 | stop_k = grid_key_dx<Sys_eqs::dims>(start); |
757 | |
758 | auto it = g_map.getSubDomainIterator(start_k,stop_k); |
759 | |
760 | if (it.isNext() == true) |
761 | increment = true; |
762 | } |
763 | |
764 | // add padding to start and stop |
765 | start_k = grid_key_dx<Sys_eqs::dims>(start); |
766 | stop_k = grid_key_dx<Sys_eqs::dims>(stop); |
767 | |
768 | |
769 | auto it = g_map.getSubDomainIterator(start_k,stop_k); |
770 | |
771 | if (increment == true) |
772 | ++it; |
773 | |
774 | constant_b b(num); |
775 | |
776 | impose_git_gmap(op,b,id,it); |
777 | |
778 | } |
779 | |
780 | /*! \brief In case we want to impose a new b re-using FDScheme we have to call |
781 | * This function |
782 | */ |
783 | void new_b() |
784 | {row_b = 0;} |
785 | |
786 | /*! \brief In case we want to impose a new A re-using FDScheme we have to call |
787 | * This function |
788 | * |
789 | */ |
790 | void new_A() |
791 | {row = 0;} |
792 | |
793 | /*! \brief Impose an operator |
794 | * |
795 | * This function impose an operator on a particular grid region to produce the system |
796 | * |
797 | * Ax = b |
798 | * |
799 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
800 | * \snippet eq_unit_test.hpp Copy the solution to grid |
801 | * |
802 | * \param op Operator to impose (A term) |
803 | * \param num right hand side of the term (b term) |
804 | * \param id Equation id in the system that we are imposing |
805 | * \param it_d iterator that define where you want to impose |
806 | * |
807 | */ |
808 | template<unsigned int prp, typename b_term, typename iterator> |
809 | void impose_dit_b(b_term & b_t, |
810 | const iterator & it_d, |
811 | long int id = 0) |
812 | { |
813 | grid_b<b_term,prp> b(b_t); |
814 | |
815 | impose_dit_b(b,id,it_d); |
816 | } |
817 | |
818 | /*! \brief Impose an operator |
819 | * |
820 | * This function impose an operator on a particular grid region to produce the system |
821 | * |
822 | * Ax = b |
823 | * |
824 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
825 | * \snippet eq_unit_test.hpp Copy the solution to grid |
826 | * |
827 | * \param op Operator to impose (A term) |
828 | * \param num right hand side of the term (b term) |
829 | * \param id Equation id in the system that we are imposing |
830 | * \param it_d iterator that define where you want to impose |
831 | * |
832 | */ |
833 | template<typename T> void impose_dit(const T & op , |
834 | typename Sys_eqs::stype num, |
835 | long int id , |
836 | grid_dist_iterator_sub<Sys_eqs::dims,typename g_map_type::d_grid> it_d) |
837 | { |
838 | constant_b b(num); |
839 | |
840 | impose_dit(op,b,id,it_d); |
841 | } |
842 | |
843 | /*! \brief Impose an operator |
844 | * |
845 | * This function impose an operator on a particular grid region to produce the system |
846 | * |
847 | * Ax = b |
848 | * |
849 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
850 | * \snippet eq_unit_test.hpp Copy the solution to grid |
851 | * |
852 | * \param op Operator to impose (A term) |
853 | * \param num right hand side of the term (b term) |
854 | * \param id Equation id in the system that we are imposing |
855 | * \param it_d iterator that define where you want to impose |
856 | * |
857 | */ |
858 | template<typename T, typename bop, typename iterator> void impose_git_gmap(const T & op , |
859 | bop num, |
860 | long int id , |
861 | const iterator & it_d) |
862 | { |
863 | openfpm::vector<triplet> & trpl = A.getMatrixTriplets(); |
864 | |
865 | auto it = it_d; |
866 | grid_sm<Sys_eqs::dims,void> gs = g_map.getGridInfoVoid(); |
867 | |
868 | std::unordered_map<long int,float> cols; |
869 | |
870 | // iterate all the grid points |
871 | while (it.isNext()) |
872 | { |
873 | // get the position |
874 | auto key = it.get(); |
875 | |
876 | // Calculate the non-zero colums |
877 | T::value(g_map,key,gs,spacing,cols,1.0); |
878 | |
879 | // indicate if the diagonal has been set |
880 | bool is_diag = false; |
881 | |
882 | // create the triplet |
883 | for ( auto it = cols.begin(); it != cols.end(); ++it ) |
884 | { |
885 | trpl.add(); |
886 | trpl.last().row() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
887 | trpl.last().col() = it->first; |
888 | trpl.last().value() = it->second; |
889 | |
890 | if (trpl.last().row() == trpl.last().col()) |
891 | is_diag = true; |
892 | |
893 | // std::cout << "(" << trpl.last().row() << "," << trpl.last().col() << "," << trpl.last().value() << ")" << "\n"; |
894 | } |
895 | |
896 | // If does not have a diagonal entry put it to zero |
897 | if (is_diag == false) |
898 | { |
899 | trpl.add(); |
900 | trpl.last().row() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
901 | trpl.last().col() = g_map.template get<0>(key)*Sys_eqs::nvar + id; |
902 | trpl.last().value() = 0.0; |
903 | } |
904 | |
905 | b(g_map.template get<0>(key)*Sys_eqs::nvar + id) = num.get(key); |
906 | |
907 | cols.clear(); |
908 | |
909 | // if SE_CLASS1 is defined check the position |
910 | #ifdef SE_CLASS1 |
911 | // T::position(key,gs,s_pos); |
912 | #endif |
913 | |
914 | ++row; |
915 | ++row_b; |
916 | ++it; |
917 | } |
918 | } |
919 | |
920 | /*! \brief Impose an operator |
921 | * |
922 | * This function impose an operator on a particular grid region to produce the system |
923 | * |
924 | * Ax = b |
925 | * |
926 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
927 | * \snippet eq_unit_test.hpp Copy the solution to grid |
928 | * |
929 | * \param op Operator to impose (A term) |
930 | * \param num right hand side of the term (b term) |
931 | * \param id Equation id in the system that we are imposing |
932 | * \param it_d iterator that define where you want to impose |
933 | * |
934 | */ |
935 | template<typename T, typename bop, typename iterator> void impose_git(const T & op , |
936 | bop num, |
937 | long int id , |
938 | const iterator & it_d) |
939 | { |
940 | openfpm::vector<triplet> & trpl = A.getMatrixTriplets(); |
941 | |
942 | auto start = it_d.getStart(); |
943 | auto stop = it_d.getStop(); |
944 | |
945 | auto itg = g_map.getSubDomainIterator(start,stop); |
946 | |
947 | auto it = it_d; |
948 | grid_sm<Sys_eqs::dims,void> gs = g_map.getGridInfoVoid(); |
949 | |
950 | std::unordered_map<long int,float> cols; |
951 | |
952 | // iterate all the grid points |
953 | while (it.isNext()) |
954 | { |
955 | // get the position |
956 | auto key = it.get(); |
957 | auto keyg = itg.get(); |
958 | |
959 | // Calculate the non-zero colums |
960 | T::value(g_map,keyg,gs,spacing,cols,1.0); |
961 | |
962 | // indicate if the diagonal has been set |
963 | bool is_diag = false; |
964 | |
965 | // create the triplet |
966 | for ( auto it = cols.begin(); it != cols.end(); ++it ) |
967 | { |
968 | trpl.add(); |
969 | trpl.last().row() = g_map.template get<0>(keyg)*Sys_eqs::nvar + id; |
970 | trpl.last().col() = it->first; |
971 | trpl.last().value() = it->second; |
972 | |
973 | if (trpl.last().row() == trpl.last().col()) |
974 | is_diag = true; |
975 | |
976 | // std::cout << "(" << trpl.last().row() << "," << trpl.last().col() << "," << trpl.last().value() << ")" << "\n"; |
977 | } |
978 | |
979 | // If does not have a diagonal entry put it to zero |
980 | if (is_diag == false) |
981 | { |
982 | trpl.add(); |
983 | trpl.last().row() = g_map.template get<0>(keyg)*Sys_eqs::nvar + id; |
984 | trpl.last().col() = g_map.template get<0>(keyg)*Sys_eqs::nvar + id; |
985 | trpl.last().value() = 0.0; |
986 | } |
987 | |
988 | b(g_map.template get<0>(keyg)*Sys_eqs::nvar + id) = num.get(key); |
989 | |
990 | cols.clear(); |
991 | |
992 | // if SE_CLASS1 is defined check the position |
993 | #ifdef SE_CLASS1 |
994 | // T::position(key,gs,s_pos); |
995 | #endif |
996 | |
997 | ++row; |
998 | ++row_b; |
999 | ++it; |
1000 | ++itg; |
1001 | } |
1002 | } |
1003 | |
1004 | /*! \brief Impose an operator |
1005 | * |
1006 | * This function impose an operator on a particular grid region to produce the system |
1007 | * |
1008 | * Ax = b |
1009 | * |
1010 | * ## Stokes equation 2D, lid driven cavity with one splipping wall |
1011 | * \snippet eq_unit_test.hpp Copy the solution to grid |
1012 | * |
1013 | * \param op Operator to impose (A term) |
1014 | * \param num right hand side of the term (b term) |
1015 | * \param id Equation id in the system that we are imposing |
1016 | * \param it_d iterator that define where you want to impose |
1017 | * |
1018 | */ |
1019 | template<unsigned int prp, typename T, typename b_term, typename iterator> void impose_dit(const T & op , |
1020 | b_term & b_t, |
1021 | const iterator & it_d, |
1022 | long int id = 0) |
1023 | { |
1024 | grid_b<b_term,prp> b(b_t); |
1025 | |
1026 | impose_dit(op,b,id,it_d); |
1027 | } |
1028 | |
1029 | //! type of the sparse matrix |
1030 | typename Sys_eqs::SparseMatrix_type A; |
1031 | |
1032 | /*! \brief produce the Matrix |
1033 | * |
1034 | * \return the Sparse matrix produced |
1035 | * |
1036 | */ |
1037 | typename Sys_eqs::SparseMatrix_type & getA() |
1038 | { |
1039 | #ifdef SE_CLASS1 |
1040 | consistency(); |
1041 | #endif |
1042 | A.resize(g_map.size()*Sys_eqs::nvar,g_map.size()*Sys_eqs::nvar, |
1043 | g_map.getLocalDomainSize()*Sys_eqs::nvar, |
1044 | g_map.getLocalDomainSize()*Sys_eqs::nvar); |
1045 | |
1046 | return A; |
1047 | |
1048 | } |
1049 | |
1050 | /*! \brief produce the B vector |
1051 | * |
1052 | * \return the vector produced |
1053 | * |
1054 | */ |
1055 | typename Sys_eqs::Vector_type & getB() |
1056 | { |
1057 | #ifdef SE_CLASS1 |
1058 | consistency(); |
1059 | #endif |
1060 | |
1061 | return b; |
1062 | } |
1063 | |
1064 | /*! \brief Copy the vector into the grid |
1065 | * |
1066 | * ## Copy the solution into the grid |
1067 | * \snippet eq_unit_test.hpp Copy the solution to grid |
1068 | * |
1069 | * \tparam scheme Discretization scheme |
1070 | * \tparam Grid_dst type of the target grid |
1071 | * \tparam pos target properties |
1072 | * |
1073 | * \param v Vector that contain the solution of the system |
1074 | * \param start point |
1075 | * \param stop point |
1076 | * \param g_dst Destination grid |
1077 | * |
1078 | */ |
1079 | template<unsigned int ... pos, typename Vct, typename Grid_dst> void copy(Vct & v,const long int (& start)[Sys_eqs_typ::dims], const long int (& stop)[Sys_eqs_typ::dims], Grid_dst & g_dst) |
1080 | { |
1081 | if (is_grid_staggered<Sys_eqs>::value()) |
1082 | { |
1083 | if (g_dst.is_staggered() == true) |
1084 | copy_staggered<Vct,Grid_dst,pos...>(v,g_dst); |
1085 | else |
1086 | { |
1087 | // Create a temporal staggered grid and copy the data there |
1088 | auto & g_map = this->getMap(); |
1089 | |
1090 | // Convert the ghost in grid units |
1091 | |
1092 | Ghost<Grid_dst::dims,long int> g_int; |
1093 | for (size_t i = 0 ; i < Grid_dst::dims ; i++) |
1094 | { |
1095 | g_int.setLow(i,g_map.getDecomposition().getGhost().getLow(i) / g_map.spacing(i)); |
1096 | g_int.setHigh(i,g_map.getDecomposition().getGhost().getHigh(i) / g_map.spacing(i)); |
1097 | } |
1098 | |
1099 | staggered_grid_dist<Grid_dst::dims,typename Grid_dst::stype,typename Grid_dst::value_type,typename Grid_dst::decomposition::extended_type, typename Grid_dst::memory_type, typename Grid_dst::device_grid_type> stg(g_dst,g_int,this->getPadding()); |
1100 | stg.setDefaultStagPosition(); |
1101 | copy_staggered<Vct,decltype(stg),pos...>(v,stg); |
1102 | |
1103 | // sync the ghost and interpolate to the normal grid |
1104 | stg.template ghost_get<pos...>(); |
1105 | stg.template to_normal<Grid_dst,pos...>(g_dst,this->getPadding(),start,stop); |
1106 | } |
1107 | } |
1108 | else |
1109 | { |
1110 | copy_normal<Vct,Grid_dst,pos...>(v,g_dst); |
1111 | } |
1112 | } |
1113 | |
1114 | /*! \brief Copy the vector into the grid |
1115 | * |
1116 | * ## Copy the solution into the grid |
1117 | * \snippet eq_unit_test.hpp Copy the solution to grid |
1118 | * |
1119 | * \tparam scheme Discretization scheme |
1120 | * \tparam Grid_dst type of the target grid |
1121 | * \tparam pos target properties |
1122 | * |
1123 | * \param v Vector that contain the solution of the system |
1124 | * \param g_dst Destination grid |
1125 | * |
1126 | */ |
1127 | template<unsigned int ... pos, typename Vct, typename Grid_dst> void copy(Vct & v, Grid_dst & g_dst) |
1128 | { |
1129 | long int start[Sys_eqs_typ::dims]; |
1130 | long int stop[Sys_eqs_typ::dims]; |
1131 | |
1132 | for (size_t i = 0 ; i < Sys_eqs_typ::dims ; i++) |
1133 | { |
1134 | start[i] = 0; |
1135 | stop[i] = g_dst.size(i); |
1136 | } |
1137 | |
1138 | this->copy<pos...>(v,start,stop,g_dst); |
1139 | } |
1140 | }; |
1141 | |
1142 | #define EQS_FIELDS 0 |
1143 | #define EQS_SPACE 1 |
1144 | |
1145 | |
1146 | #endif /* OPENFPM_NUMERICS_SRC_FINITEDIFFERENCE_FDSCHEME_HPP_ */ |
1147 | |