| 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 | |