particleiterator.hpp
Go to the documentation of this file.
00001 00005 /* Copyright (c) 2005-2011 Taneli Kalvas. All rights reserved. 00006 * 00007 * You can redistribute this software and/or modify it under the terms 00008 * of the GNU General Public License as published by the Free Software 00009 * Foundation; either version 2 of the License, or (at your option) 00010 * any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, but 00013 * WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this library (file "COPYING" included in the package); 00019 * if not, write to the Free Software Foundation, Inc., 51 Franklin 00020 * Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 * 00022 * If you have questions about your rights to use or distribute this 00023 * software, please contact Berkeley Lab's Technology Transfer 00024 * Department at TTD@lbl.gov. Other questions, comments and bug 00025 * reports should be sent directly to the author via email at 00026 * taneli.kalvas@jyu.fi. 00027 * 00028 * NOTICE. This software was developed under partial funding from the 00029 * U.S. Department of Energy. As such, the U.S. Government has been 00030 * granted for itself and others acting on its behalf a paid-up, 00031 * nonexclusive, irrevocable, worldwide license in the Software to 00032 * reproduce, prepare derivative works, and perform publicly and 00033 * display publicly. Beginning five (5) years after the date 00034 * permission to assert copyright is obtained from the U.S. Department 00035 * of Energy, and subject to any subsequent five (5) year renewals, 00036 * the U.S. Government is granted for itself and others acting on its 00037 * behalf a paid-up, nonexclusive, irrevocable, worldwide license in 00038 * the Software to reproduce, prepare derivative works, distribute 00039 * copies to the public, perform publicly and display publicly, and to 00040 * permit others to do so. 00041 */ 00042 00043 #ifndef PARTICLEITERATOR_HPP 00044 #define PARTICLEITERATOR_HPP 1 00045 00046 00047 #include <vector> 00048 #include <iostream> 00049 #include <algorithm> 00050 #include <iomanip> 00051 #include <gsl/gsl_odeiv.h> 00052 #include <gsl/gsl_poly.h> 00053 #include "geometry.hpp" 00054 #include "trajectory.hpp" 00055 #include "particles.hpp" 00056 #include "vectorfield.hpp" 00057 #include "scalarfield.hpp" 00058 #include "scharge.hpp" 00059 #include "scheduler.hpp" 00060 #include "polysolver.hpp" 00061 #include "particledatabase.hpp" 00062 00063 00064 //#define DEBUG_PARTICLE_ITERATOR 1 00065 00066 00069 enum particle_iterator_type_e { 00070 PARTICLE_ITERATOR_ADAPTIVE = 0, 00071 PARTICLE_ITERATOR_FIXED_STEP_LEN 00072 }; 00073 00074 00083 template <class PP> class ColData { 00084 public: 00085 PP _x; 00086 int _dir; 00091 ColData( PP x, int dir ) : _x(x), _dir(dir) {} 00092 00097 bool operator<( const ColData &cd ) const { 00098 return( _x[0] < cd._x[0] ); 00099 } 00100 00109 static void build_coldata_linear( std::vector<ColData> &coldata, const Mesh &mesh, 00110 const PP &x1, const PP &x2 ) { 00111 00112 coldata.clear(); 00113 00114 for( size_t a = 0; a < PP::dim(); a++ ) { 00115 00116 int a1 = (int)floor( (x1[2*a+1]-mesh.origo(a))/mesh.h() ); 00117 int a2 = (int)floor( (x2[2*a+1]-mesh.origo(a))/mesh.h() ); 00118 if( a1 > a2 ) { 00119 int a = a2; 00120 a2 = a1; 00121 a1 = a; 00122 } 00123 00124 for( int b = a1+1; b <= a2; b++ ) { 00125 00126 // Save intersection coordinates 00127 double K = (b*mesh.h() + mesh.origo(a) - x1[2*a+1]) / 00128 (x2[2*a+1] - x1[2*a+1]); 00129 if( K < 0.0 ) K = 0.0; 00130 else if( K > 1.0 ) K = 1.0; 00131 //std::cout << "Found valid root: " << K << "\n"; 00132 00133 if( x2[2*a+1] > x1[2*a+1] ) 00134 coldata.push_back( ColData( x1 + (x2-x1)*K, a+1 ) ); 00135 else 00136 coldata.push_back( ColData( x1 + (x2-x1)*K, -a-1 ) ); 00137 } 00138 } 00139 00140 // Sort intersections in increasing time order 00141 sort( coldata.begin(), coldata.end() ); 00142 } 00143 00152 static void build_coldata_poly( std::vector<ColData> &coldata, const Mesh &mesh, 00153 const PP &x1, const PP &x2 ) { 00154 00155 #ifdef DEBUG_PARTICLE_ITERATOR 00156 std::cout << "Building coldata using polynomial interpolation\n"; 00157 #endif 00158 00159 coldata.clear(); 00160 00161 // Construct trajectory representation 00162 TrajectoryRep1D traj[PP::dim()]; 00163 for( size_t a = 0; a < PP::dim(); a++ ) { 00164 traj[a].construct( x2[0]-x1[0], 00165 x1[2*a+1], x1[2*a+2], 00166 x2[2*a+1], x2[2*a+2] ); 00167 } 00168 00169 // Solve trajectory intersections 00170 for( size_t a = 0; a < PP::dim(); a++ ) { 00171 00172 // Mesh number of x1 (start point) 00173 int i = (int)floor( (x1[2*a+1]-mesh.origo(a))/mesh.h() ); 00174 00175 // Search to negative (dj = -1) and positive (dj = +1) mesh directions 00176 for( int dj = -1; dj <= 1; dj += 2 ) { 00177 int j = i; 00178 if( dj == +1 ) 00179 j = i+1; 00180 int Kcount; // Solution counter 00181 double K[3]; // Solution array 00182 while( 1 ) { 00183 00184 // Intersection point 00185 double val = mesh.origo(a) + mesh.h() * j; 00186 if( val < mesh.origo(a) ) 00187 break; 00188 else if( val > mesh.max(a) ) 00189 break; 00190 00191 #ifdef DEBUG_PARTICLE_ITERATOR 00192 std::cout << " Searching intersections at coord(" << a << ") = " << val << "\n"; 00193 #endif 00194 Kcount = traj[a].solve( K, val ); 00195 if( Kcount == 0 ) 00196 break; // No valid roots 00197 00198 #ifdef DEBUG_PARTICLE_ITERATOR 00199 std::cout << " Found " << Kcount << " valid roots: "; 00200 for( int p = 0; p < Kcount; p++ ) 00201 std::cout << K[p] << " "; 00202 std::cout << "\n"; 00203 #endif 00204 00205 // Save roots to coldata 00206 for( int b = 0; b < Kcount; b++ ) { 00207 PP xcol; 00208 double x, v; 00209 xcol(0) = x1[0] + K[b]*(x2[0]-x1[0]); 00210 for( size_t c = 0; c < PP::dim(); c++ ) { 00211 traj[c].coord( x, v, K[b] ); 00212 if( a == c ) 00213 xcol[2*c+1] = val; // limit numerical inaccuracy 00214 else 00215 xcol[2*c+1] = x; 00216 xcol[2*c+2] = v; 00217 } 00218 if( mesh.geom_mode() == MODE_CYL ) 00219 xcol[5] = x1[5] + K[b]*(x2[5]-x1[5]); 00220 if( xcol[2*a+2] >= 0.0 ) 00221 coldata.push_back( ColData( xcol, a+1 ) ); 00222 else 00223 coldata.push_back( ColData( xcol, -a-1 ) ); 00224 } 00225 00226 j += dj; 00227 } 00228 } 00229 } 00230 00231 // Sort intersections in increasing time order 00232 sort( coldata.begin(), coldata.end() ); 00233 00234 #ifdef DEBUG_PARTICLE_ITERATOR 00235 std::cout << " Coldata built\n"; 00236 #endif 00237 } 00238 00239 }; 00240 00241 00249 template <class PP> class ParticleIterator { 00250 00251 gsl_odeiv_system _system; 00252 gsl_odeiv_step *_step; 00253 gsl_odeiv_control *_control; 00254 gsl_odeiv_evolve *_evolve; 00256 particle_iterator_type_e _type; 00258 bool _polyint; 00259 double _epsabs; 00260 double _epsrel; 00261 uint32_t _maxsteps; 00262 double _maxt; 00263 uint32_t _trajdiv; 00265 bool _mirror[6]; 00267 ParticleIteratorData _pidata; 00268 const TrajectoryHandlerCallback *_thand_cb; 00269 const TrajectoryEndCallback *_tend_cb; 00270 const TrajectoryEndCallback *_bsup_cb; 00271 ParticleDataBase *_pdb; 00272 pthread_mutex_t *_scharge_mutex; 00274 PP _xi; 00276 std::vector<PP> _traj; 00277 std::vector<ColData<PP> > _coldata; 00279 ParticleStatistics _stat; 00290 bool check_collision( Particle<PP> &particle, const PP &x1, const PP &x2, PP &status_x ) { 00291 00292 // If inside solid, bracket for collision point 00293 Vec3D v2 = x2.location(); 00294 int32_t bound = _pidata._geom->inside( v2 ); 00295 if( bound < 7 ) 00296 return( true ); // No collision happened. 00297 Vec3D vc; 00298 Vec3D v1 = x1.location(); 00299 double K = _pidata._geom->bracket_surface( bound, v2, v1, vc ); 00300 00301 // Calculate new PP 00302 for( size_t a = 0; a < PP::size(); a++ ) 00303 status_x[a] = x2[a] + K*(x1[a]-x2[a]); 00304 00305 // Remove all points from trajectory after time status_x[0]. 00306 for( size_t a = _traj.size()-1; a > 0; a-- ) { 00307 if( _traj[a][0] > status_x[0] ) 00308 _traj.pop_back(); 00309 else 00310 break; 00311 } 00312 00313 // Save last trajectory point and update status 00314 //_traj.push_back( status_x ); 00315 particle.set_status( PARTICLE_COLL ); 00316 00317 // Update collision statistics for boundary 00318 _stat.add_bound_collision( bound, particle.IQ() ); 00319 00320 return( false ); // Collision happened. 00321 } 00322 00323 00331 void handle_mirror( size_t c, int i[3], size_t a, int border, PP &x2 ) { 00332 00333 #ifdef DEBUG_PARTICLE_ITERATOR 00334 std::cout << " handle_mirror( c = " << c 00335 << ", i = (" << i[0] << ", " << i[1] << ", " << i[2] 00336 << "), a = " << a << ", border = " << border 00337 << ")\n"; 00338 #endif 00339 00340 double xmirror; 00341 if( border < 0 ) { 00342 xmirror = _pidata._geom->origo(a); 00343 i[a] = -i[a]-1; 00344 } else { 00345 xmirror = _pidata._geom->max(a); 00346 i[a] = 2*_pidata._geom->size(a)-i[a]-3; 00347 } 00348 00349 #ifdef DEBUG_PARTICLE_ITERATOR 00350 std::cout << " xmirror = " << xmirror << "\n"; 00351 std::cout << " i = (" << i[0] << ", " << i[1] << ", " << i[2] << ")\n"; 00352 std::cout << " xi = " << _xi << "\n"; 00353 #endif 00354 00355 // Check if found edge at first encounter 00356 bool caught_at_boundary = false; 00357 if( _coldata[c]._dir == border*((int)a+1) && 00358 ( i[a] == 0 || i[a] == (int)_pidata._geom->size(a)-2 ) ) { 00359 caught_at_boundary = true; 00360 #ifdef DEBUG_PARTICLE_ITERATOR 00361 std::cout << " caught_at_boundary\n"; 00362 #endif 00363 } 00364 00365 // Mirror traj back to _xi 00366 if( caught_at_boundary ) { 00367 _traj.push_back( _coldata[c]._x ); 00368 } else { 00369 for( int b = _traj.size()-1; b > 0; b-- ) { 00370 if( _traj[b][0] >= _xi[0] ) { 00371 00372 #ifdef DEBUG_PARTICLE_ITERATOR 00373 std::cout << " mirroring traj[" << b << "] = " << _traj[b] << "\n"; 00374 #endif 00375 _traj[b][2*a+1] = 2.0*xmirror - _traj[b][2*a+1]; 00376 _traj[b][2*a+2] *= -1.0; 00377 } else 00378 break; 00379 } 00380 } 00381 00382 // Mirror rest of the coldata 00383 for( size_t b = c; b < _coldata.size(); b++ ) { 00384 if( (size_t)abs(_coldata[b]._dir) == a+1 ) 00385 _coldata[b]._dir *= -1; 00386 _coldata[b]._x[2*a+1] = 2.0*xmirror - _coldata[b]._x[2*a+1]; 00387 _coldata[b]._x[2*a+2] *= -1.0; 00388 } 00389 00390 if( caught_at_boundary ) 00391 _traj.push_back( _coldata[c]._x ); 00392 00393 // Mirror calculation point 00394 x2[2*a+1] = 2.0*xmirror - x2[2*a+1]; 00395 x2[2*a+2] *= -1.0; 00396 00397 // Coordinates changed, reset integrator 00398 gsl_odeiv_step_reset( _step ); 00399 gsl_odeiv_evolve_reset( _evolve ); 00400 } 00401 00402 00403 void handle_collision( Particle<PP> &particle, uint32_t bound, size_t c, PP &status_x ) { 00404 00405 #ifdef DEBUG_PARTICLE_ITERATOR 00406 std::cout << " handle_collision()\n"; 00407 #endif 00408 00409 //_traj.push_back( _coldata[c]._x ); 00410 status_x = _coldata[c]._x; 00411 particle.set_status( PARTICLE_OUT ); 00412 _stat.add_bound_collision( bound, particle.IQ() ); 00413 } 00414 00415 00424 bool handle_trajectory_advance( Particle<PP> &particle, size_t c, int i[3], PP &x2 ) { 00425 00426 // Check for collisions with solids and advance coordinates i. 00427 if( PP::dim() == 2 ) { 00428 if( _coldata[c]._dir == -1 ) { 00429 if( ( abs(_pidata._geom->mesh(i[0], i[1] )) >= 7 || 00430 abs(_pidata._geom->mesh(i[0], i[1]+1)) >= 7 ) && 00431 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00432 return( false ); 00433 i[0]--; 00434 } else if( _coldata[c]._dir == +1 ) { 00435 if( ( abs(_pidata._geom->mesh(i[0]+1,i[1] )) >= 7 || 00436 abs(_pidata._geom->mesh(i[0]+1,i[1]+1)) >= 7 ) && 00437 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00438 return( false ); 00439 i[0]++; 00440 } else if( _coldata[c]._dir == -2 ) { 00441 if( ( abs(_pidata._geom->mesh(i[0], i[1] )) >= 7 || 00442 abs(_pidata._geom->mesh(i[0]+1,i[1] )) >= 7 ) && 00443 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00444 return( false ); 00445 i[1]--; 00446 } else { 00447 if( ( abs(_pidata._geom->mesh(i[0], i[1]+1)) >= 7 || 00448 abs(_pidata._geom->mesh(i[0]+1,i[1]+1)) >= 7 ) && 00449 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00450 return( false ); 00451 i[1]++; 00452 } 00453 } else if( PP::dim() == 3 ) { 00454 if( _coldata[c]._dir == -1 ) { 00455 if( ( abs(_pidata._geom->mesh(i[0], i[1], i[2] )) >= 7 || 00456 abs(_pidata._geom->mesh(i[0], i[1]+1,i[2] )) >= 7 || 00457 abs(_pidata._geom->mesh(i[0], i[1], i[2]+1)) >= 7 || 00458 abs(_pidata._geom->mesh(i[0], i[1]+1,i[2]+1)) >= 7 ) && 00459 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00460 return( false ); 00461 i[0]--; 00462 } else if( _coldata[c]._dir == +1 ) { 00463 if( ( abs(_pidata._geom->mesh(i[0]+1,i[1], i[2] )) >= 7 || 00464 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2] )) >= 7 || 00465 abs(_pidata._geom->mesh(i[0]+1,i[1], i[2]+1)) >= 7 || 00466 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2]+1)) >= 7 ) && 00467 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00468 return( false ); 00469 i[0]++; 00470 } else if( _coldata[c]._dir == -2 ) { 00471 if( ( abs(_pidata._geom->mesh(i[0], i[1],i[2] )) >= 7 || 00472 abs(_pidata._geom->mesh(i[0]+1,i[1],i[2] )) >= 7 || 00473 abs(_pidata._geom->mesh(i[0], i[1],i[2]+1)) >= 7 || 00474 abs(_pidata._geom->mesh(i[0]+1,i[1],i[2]+1)) >= 7 ) && 00475 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00476 return( false ); 00477 i[1]--; 00478 } else if( _coldata[c]._dir == +2 ) { 00479 if( ( abs(_pidata._geom->mesh(i[0], i[1]+1,i[2] )) >= 7 || 00480 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2] )) >= 7 || 00481 abs(_pidata._geom->mesh(i[0], i[1]+1,i[2]+1)) >= 7 || 00482 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2]+1)) >= 7 ) && 00483 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00484 return( false ); 00485 i[1]++; 00486 } else if( _coldata[c]._dir == -3 ) { 00487 if( ( abs(_pidata._geom->mesh(i[0], i[1], i[2] )) >= 7 || 00488 abs(_pidata._geom->mesh(i[0]+1,i[1], i[2] )) >= 7 || 00489 abs(_pidata._geom->mesh(i[0], i[1]+1,i[2])) >= 7 || 00490 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2])) >= 7 ) && 00491 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00492 return( false ); 00493 i[2]--; 00494 } else { 00495 if( ( abs(_pidata._geom->mesh(i[0], i[1], i[2]+1)) >= 7 || 00496 abs(_pidata._geom->mesh(i[0]+1,i[1], i[2]+1)) >= 7 || 00497 abs(_pidata._geom->mesh(i[0], i[1]+1,i[2]+1)) >= 7 || 00498 abs(_pidata._geom->mesh(i[0]+1,i[1]+1,i[2]+1)) >= 7 ) && 00499 !check_collision( particle, _xi, _coldata[c]._x, x2 ) ) 00500 return( false ); 00501 i[2]++; 00502 } 00503 } else { 00504 throw( Error( ERROR_LOCATION, "unsupported dimension number" ) ); 00505 } 00506 00507 // Check for collisions/mirroring with simulation boundary. Here 00508 // coordinates i are already advanced to next mesh. 00509 for( size_t a = 0; a < PP::dim(); a++ ) { 00510 00511 if( i[a] < 0 ) { 00512 if( _mirror[2*a] ) 00513 handle_mirror( c, i, a, -1, x2 ); 00514 else { 00515 handle_collision( particle, 1+2*a, c, x2 ); 00516 return( false ); 00517 } 00518 } else if( i[a] >= (_pidata._geom->size(a)-1) ) { 00519 if( _mirror[2*a+1] ) 00520 handle_mirror( c, i, a, +1, x2 ); 00521 else { 00522 handle_collision( particle, 2+2*a, c, x2 ); 00523 return( false ); 00524 } 00525 } 00526 } 00527 00528 return( true ); 00529 } 00530 00536 bool limit_trajectory_advance( const PP &x1, PP &x2 ) { 00537 00538 bool touched = false; 00539 00540 for( size_t a = 0; a < PP::dim(); a++ ) { 00541 00542 double lim1 = _pidata._geom->origo(a) - 00543 (_pidata._geom->size(a)-1)*_pidata._geom->h(); 00544 double lim2 = _pidata._geom->origo(a) + 00545 2*(_pidata._geom->size(a)-1)*_pidata._geom->h(); 00546 00547 if( x2[2*a+1] < lim1 ) { 00548 00549 double K = (lim1 - x1[2*a+1]) / (x2[2*a+1] - x1[2*a+1]); 00550 x2 = x1 + K*(x2-x1); 00551 touched = true; 00552 #ifdef DEBUG_PARTICLE_ITERATOR 00553 std::cout << "Limiting step to:\n"; 00554 std::cout << " x2: " << x2 << "\n"; 00555 #endif 00556 } else if(x2[2*a+1] > lim2 ) { 00557 00558 double K = (lim2 - x1[2*a+1]) / (x2[2*a+1] - x1[2*a+1]); 00559 x2 = x1 + K*(x2-x1); 00560 touched = true; 00561 #ifdef DEBUG_PARTICLE_ITERATOR 00562 std::cout << "Limiting step to:\n"; 00563 std::cout << " x2: " << x2 << "\n"; 00564 #endif 00565 } 00566 } 00567 00568 return( touched ); 00569 } 00570 00590 bool handle_trajectory( Particle<PP> &particle, const PP &x1, PP &x2, 00591 bool force_linear, bool first_step ) { 00592 00593 #ifdef DEBUG_PARTICLE_ITERATOR 00594 std::cout << "Handle trajectory from x1 to x2:\n"; 00595 std::cout << " x1: " << x1 << "\n"; 00596 std::cout << " x2: " << x2 << "\n"; 00597 #endif 00598 00599 // Limit trajectory advance to double the simulation box 00600 // If limitation done, force to linear interpolation 00601 if( limit_trajectory_advance( x1, x2 ) ) 00602 force_linear = true; 00603 00604 // Make coldata 00605 if( _polyint && !force_linear ) 00606 ColData<PP>::build_coldata_poly( _coldata, *_pidata._geom, x1, x2 ); 00607 else 00608 ColData<PP>::build_coldata_linear( _coldata, *_pidata._geom, x1, x2 ); 00609 00610 // TODO 00611 // Remove entrance to geometry if coming from outside or make 00612 // code to skip the collision detection for these particles 00613 00614 // No intersections, nothing to do 00615 if( _coldata.size() == 0 ) { 00616 #ifdef DEBUG_PARTICLE_ITERATOR 00617 std::cout << "No coldata\n"; 00618 #endif 00619 return( true ); 00620 } 00621 00622 // Starting mesh index 00623 int i[3] = {0, 0, 0}; 00624 for( size_t cdir = 0; cdir < PP::dim(); cdir++ ) 00625 i[cdir] = (int)floor( (x1[2*cdir+1]-_pidata._geom->origo(cdir))/_pidata._geom->h() ); 00626 00627 // Process intersection points 00628 #ifdef DEBUG_PARTICLE_ITERATOR 00629 std::cout << "Process coldata points:\n"; 00630 #endif 00631 for( size_t a = 0; a < _coldata.size(); a++ ) { 00632 00633 #ifdef DEBUG_PARTICLE_ITERATOR 00634 std::cout << " Coldata " << std::setw(4) << a << ": " 00635 << _coldata[a]._x << ", " 00636 << std::setw(3) << i[0] << " " 00637 << std::setw(3) << i[1] << " " 00638 << std::setw(3) << i[2] << " " 00639 << std::setw(3) << _coldata[a]._dir << "\n"; 00640 #endif 00641 00642 // Advance particle in mesh, check for possible collisions and 00643 // do mirroring. 00644 handle_trajectory_advance( particle, a, i, x2 ); 00645 00646 // Update space charge for one mesh. 00647 if( _pidata._scharge ) 00648 scharge_add_from_trajectory( *_pidata._scharge, _scharge_mutex, particle.IQ(), 00649 _xi, _coldata[a]._x ); 00650 00651 // Call trajectory handler callback 00652 if( _thand_cb ) 00653 (*_thand_cb)( &particle, &_coldata[a]._x, &x2 ); 00654 00655 #ifdef DEBUG_PARTICLE_ITERATOR 00656 if( particle.get_status() == PARTICLE_OUT ) { 00657 std::cout << " Particle out\n"; 00658 std::cout << " x = " << x2 << "\n"; 00659 } else if( particle.get_status() == PARTICLE_COLL ) { 00660 std::cout << " Particle collided\n"; 00661 std::cout << " x = " << x2 << "\n"; 00662 } 00663 #endif 00664 // Clear coldata and exit if particle collided. 00665 if( particle.get_status() != PARTICLE_OK ) { 00666 _traj.push_back( x2 ); 00667 _coldata.clear(); 00668 return( false ); 00669 } 00670 00671 // Update last intersection point xi. 00672 _xi = _coldata[a]._x; 00673 } 00674 00675 #ifdef DEBUG_PARTICLE_ITERATOR 00676 std::cout << "Coldata done\n"; 00677 #endif 00678 _coldata.clear(); 00679 return( true ); 00680 } 00681 00682 00685 bool axis_mirror_required( const PP &x2 ) { 00686 return( _pidata._geom->geom_mode() == MODE_CYL && 00687 x2[4] < 0.0 && 00688 x2[3] <= 0.01*_pidata._geom->h() && 00689 x2[3]*fabs(x2[5]) <= 1.0e-9*fabs(x2[4]) ); 00690 00691 } 00692 00693 00699 bool handle_axis_mirror_step( Particle<PP> &particle, const PP &x1, PP &x2 ) { 00700 00701 // Get acceleration at x2 00702 double dxdt[5]; 00703 PP::get_derivatives( x2[0], &x2[1], dxdt, (void *)&_pidata ); 00704 00705 // Calculate crossover point assuming zero acceleration in 00706 // r-direction and constant acceleration in x-direction 00707 double dt = -x2[3]/x2[4]; 00708 PP xc; 00709 xc[0] = x2[0]+dt; 00710 xc[1] = x2[1]+(x2[2]+0.5*dxdt[1]*dt)*dt; 00711 xc[2] = x2[2]; 00712 xc[3] = x2[3]+x2[4]*dt; 00713 xc[4] = x2[4]; 00714 xc[5] = x2[5]; 00715 00716 // Mirror x2 to x3 00717 PP x3 = 2*xc - x2; 00718 x3[3] *= -1.0; 00719 x3[4] *= -1.0; 00720 x3[5] *= -1.0; 00721 00722 #ifdef DEBUG_PARTICLE_ITERATOR 00723 std::cout << "Particle mirror:\n"; 00724 std::cout << " x1: " << x1 << "\n"; 00725 std::cout << " x2: " << x2 << "\n"; 00726 std::cout << " xc: " << xc << "\n"; 00727 std::cout << " x3: " << x3 << "\n"; 00728 #endif 00729 00730 // Handle step with linear interpolation to avoid going to r<=0 00731 if( !handle_trajectory( particle, x2, x3, true, false ) ) 00732 return( false ); // Particle done 00733 00734 // Save trajectory calculation points 00735 _traj.push_back( x2 ); 00736 _traj.push_back( xc ); 00737 xc[4] *= -1.0; 00738 xc[5] *= -1.0; 00739 _traj.push_back( xc ); 00740 00741 // Next step not a continuation of previous one, reset 00742 // integrator 00743 gsl_odeiv_step_reset( _step ); 00744 gsl_odeiv_evolve_reset( _evolve ); 00745 00746 // Continue iteration at mirrored point 00747 x2 = x3; 00748 return( true ); 00749 } 00750 00761 bool check_particle_definition( PP &x ) { 00762 00763 #ifdef DEBUG_PARTICLE_ITERATOR 00764 std::cout << "Particle defined at:\n"; 00765 std::cout << " x = " << x << "\n"; 00766 #endif 00767 00768 // Check if inside solids or outside simulation geometry box. 00769 if( _pidata._geom->inside( x.location() ) ) 00770 return( false ); 00771 00772 // Check if particle on simulation geometry border and directed outwards 00773 /* 00774 for( size_t a = 0; a < PP::dim(); a++ ) { 00775 if( x[2*a+1] == _pidata._geom->origo(a) && x[2*a+2] < 0.0 ) { 00776 if( _mirror[2*a] ) { 00777 x[2*a+2] *= -1.0; 00778 #ifdef DEBUG_PARTICLE_ITERATOR 00779 std::cout << "Mirroring to:\n"; 00780 std::cout << " x = " << x << "\n"; 00781 #endif 00782 } else { 00783 return( false ); 00784 } 00785 00786 } else if( x[2*a+1] == _pidata._geom->max(a) & x[2*a+2] > 0.0 ) { 00787 if( _mirror[2*a+1] ) { 00788 x[2*a+2] *= -1.0; 00789 #ifdef DEBUG_PARTICLE_ITERATOR 00790 std::cout << "Mirroring to:\n"; 00791 std::cout << " x = " << x << "\n"; 00792 #endif 00793 } else { 00794 return( false ); 00795 } 00796 } 00797 } 00798 00799 #ifdef DEBUG_PARTICLE_ITERATOR 00800 std::cout << "Definition ok\n"; 00801 #endif 00802 00803 */ 00804 return( true ); 00805 } 00806 00807 double calculate_dt( const PP &x, const double *dxdt ) { 00808 00809 double spd = 0.0, acc = 0.0; 00810 00811 for( size_t a = 0; a < (PP::size()-1)/2; a++ ) { 00812 //std::cout << "spd += " << dxdt[2*a]*dxdt[2*a] << "\n"; 00813 spd += dxdt[2*a]*dxdt[2*a]; 00814 //std::cout << "acc += " << dxdt[2*a+1]*dxdt[2*a+1] << "\n"; 00815 acc += dxdt[2*a+1]*dxdt[2*a+1]; 00816 } 00817 if( _pidata._geom->geom_mode() == MODE_CYL ) { 00818 //std::cout << "MODE_CYL\n"; 00819 //std::cout << "spd += " << x[3]*x[3]*x[5]*x[5] << "\n"; 00820 spd += x[3]*x[3]*x[5]*x[5]; 00821 //std::cout << "acc += " << x[3]*x[3]*dxdt[4]*dxdt[4] << "\n"; 00822 acc += x[3]*x[3]*dxdt[4]*dxdt[4]; 00823 } 00824 //std::cout << "spd = " << sqrt(spd) << "\n"; 00825 //std::cout << "acc = " << sqrt(acc) << "\n"; 00826 spd = _pidata._geom->h() / sqrt(spd); 00827 acc = sqrt( 2.0*_pidata._geom->h() / sqrt(acc) ); 00828 00829 return( spd < acc ? spd : acc ); 00830 } 00831 00832 public: 00833 00860 ParticleIterator( particle_iterator_type_e type, double epsabs, double epsrel, 00861 bool polyint, uint32_t maxsteps, double maxt, 00862 uint32_t trajdiv, bool mirror[6], ScalarField *scharge, 00863 pthread_mutex_t *scharge_mutex, 00864 const VectorField *efield, const VectorField *bfield, 00865 const Geometry *geom ) 00866 : _type(type), _polyint(polyint), _epsabs(epsabs), _epsrel(epsrel), _maxsteps(maxsteps), _maxt(maxt), 00867 _trajdiv(trajdiv), _pidata(scharge,efield,bfield,geom), 00868 _thand_cb(0), _tend_cb(0), _bsup_cb(0), _pdb(0), _scharge_mutex(scharge_mutex), 00869 _stat(geom->number_of_boundaries()) { 00870 00871 // Initialize mirroring 00872 _mirror[0] = mirror[0]; 00873 _mirror[1] = mirror[1]; 00874 _mirror[2] = mirror[2]; 00875 _mirror[3] = mirror[3]; 00876 _mirror[4] = mirror[4]; 00877 _mirror[5] = mirror[5]; 00878 00879 // Initialize system of ordinary differential equations (ODE) 00880 _system.jacobian = NULL; 00881 _system.params = (void *)&_pidata; 00882 _system.function = PP::get_derivatives; 00883 _system.dimension = PP::size()-1; // Time is not part of differential equation dimensions 00884 00885 // Make scale 00886 // 2D: x vx y vy 00887 // Cyl: x vx r vr omega 00888 // 3D: x vx y vy z vz 00889 double scale_abs[PP::size()-1]; 00890 for( uint32_t a = 0; a < (uint32_t)PP::size()-2; a+=2 ) { 00891 scale_abs[a+0] = 1.0; 00892 scale_abs[a+1] = 1.0e6; 00893 } 00894 if( _pidata._geom->geom_mode() == MODE_CYL ) 00895 scale_abs[4] = 1.0; 00896 00897 // Initialize ODE solver 00898 _step = gsl_odeiv_step_alloc( gsl_odeiv_step_rkck, _system.dimension ); 00899 //_control = gsl_odeiv_control_standard_new( _epsabs, _epsrel, 1.0, 1.0 ); 00900 _control = gsl_odeiv_control_scaled_new( _epsabs, _epsrel, 1.0, 1.0, scale_abs, PP::size()-1 ); 00901 _evolve = gsl_odeiv_evolve_alloc( _system.dimension ); 00902 } 00903 00904 00907 ~ParticleIterator() { 00908 gsl_odeiv_evolve_free( _evolve ); 00909 gsl_odeiv_control_free( _control ); 00910 gsl_odeiv_step_free( _step ); 00911 } 00912 00913 00916 void set_trajectory_handler_callback( const TrajectoryHandlerCallback *thand_cb ) { 00917 _thand_cb = thand_cb; 00918 } 00919 00920 00923 void set_trajectory_end_callback( const TrajectoryEndCallback *tend_cb, ParticleDataBase *pdb ) { 00924 _tend_cb = tend_cb; 00925 _pdb = pdb; 00926 } 00927 00928 00931 void set_bfield_suppression_callback( const CallbackFunctorD_V *bsup_cb ) { 00932 _pidata.set_bfield_suppression_callback( bsup_cb ); 00933 } 00934 00937 const ParticleStatistics &get_statistics( void ) const { 00938 return( _stat ); 00939 } 00940 00941 00950 void operator()( Particle<PP> *particle, uint32_t pi ) { 00951 00952 // Check particle status 00953 if( particle->get_status() != PARTICLE_OK ) 00954 return; 00955 00956 // Copy starting point to x and 00957 PP x = particle->x(); 00958 00959 // Check particle definition 00960 if( !check_particle_definition( x ) ) { 00961 particle->set_status( PARTICLE_BADDEF ); 00962 _stat.inc_end_baddef(); 00963 return; 00964 } 00965 particle->x() = x; 00966 00967 // Reset trajectory and save first trajectory point. 00968 _traj.clear(); 00969 _traj.push_back( x ); 00970 #ifdef DEBUG_PARTICLE_ITERATOR 00971 std::cout << x[0] << " " 00972 << x[1] << " " 00973 << x[2] << " " 00974 << x[3] << " " 00975 << x[4] << "\n"; 00976 #endif 00977 _pidata._qm = particle->qm(); 00978 _xi = x; 00979 00980 // Reset integrator 00981 gsl_odeiv_step_reset( _step ); 00982 gsl_odeiv_evolve_reset( _evolve ); 00983 00984 // Make initial guess for step size 00985 //std::cout << "Guess dt ------------------------------------------------\n"; 00986 double dxdt[PP::size()-1]; 00987 PP::get_derivatives( 0.0, &x[1], dxdt, (void *)&_pidata ); 00988 double dt = calculate_dt( x, dxdt ); 00989 00990 #ifdef DEBUG_PARTICLE_ITERATOR 00991 std::cout << "dxdt = "; 00992 for( size_t a = 0; a < PP::size()-1; a++ ) 00993 std::cout << dxdt[a] << " "; 00994 std::cout << "\n"; 00995 std::cout << "dt = " << dt << "\n"; 00996 std::cout << "*** Starting iteration\n"; 00997 #endif 00998 00999 // Iterate ODEs until maximum steps are done, time is used 01000 // or particle collides. 01001 PP x2; 01002 size_t nstp = 0; // Steps taken 01003 while( nstp < _maxsteps && x[0] < _maxt ) { 01004 01005 #ifdef DEBUG_PARTICLE_ITERATOR 01006 std::cout << "\n*** Step ***\n"; 01007 std::cout << " x = " << x2 << "\n"; 01008 std::cout << " dt = " << dt << " (proposed)\n"; 01009 #endif 01010 01011 // Take a step. 01012 x2 = x; 01013 01014 while( true ) { 01015 int retval = gsl_odeiv_evolve_apply( _evolve, _control, _step, &_system, 01016 &x2[0], _maxt, &dt, &x2[1] ); 01017 if( retval == IBSIMU_DERIV_ERROR ) { 01018 #ifdef DEBUG_PARTICLE_ITERATOR 01019 std::cout << "Step rejected\n"; 01020 std::cout << " x2 = " << x2 << "\n"; 01021 std::cout << " dt = " << dt << "\n"; 01022 #endif 01023 x2[0] = x[0]; // Reset time (this shouldn't be necessary - there 01024 // is a bug in GSL-1.12, report has been sent) 01025 dt *= 0.5; 01026 if( dt == 0.0 ) 01027 throw( Error( ERROR_LOCATION, "too small step size" ) ); 01028 //nstp++; 01029 continue; 01030 } else if( retval == GSL_SUCCESS ) { 01031 break; 01032 } else { 01033 throw( Error( ERROR_LOCATION, "gsl_odeiv_evolve_apply failed" ) ); 01034 } 01035 } 01036 01037 // Check step count number and step size validity 01038 if( nstp >= _maxsteps ) 01039 break; 01040 if( x2[0] == x[0] ) 01041 throw( Error( ERROR_LOCATION, "too small step size" ) ); 01042 01043 #ifdef DEBUG_PARTICLE_ITERATOR 01044 std::cout << "Step accepted from x1 to x2:\n"; 01045 std::cout << " dt = " << dt << " (taken)\n"; 01046 std::cout << " x1 = " << x << "\n"; 01047 std::cout << " x2 = " << x2 << "\n"; 01048 #endif 01049 01050 // Handle collisions and space charge of step. 01051 if( !handle_trajectory( *particle, x, x2, false, nstp == 0 ) ) { 01052 x = x2; 01053 break; // Particle done 01054 } 01055 01056 // Check if particle mirroring is required to avoid 01057 // singularity at symmetry axis. 01058 if( axis_mirror_required( x2 ) ) { 01059 if( !handle_axis_mirror_step( *particle, x, x2 ) ) 01060 break; // Particle done 01061 } 01062 01063 // Propagate coordinates 01064 x = x2; 01065 01066 // Save trajectory point 01067 _traj.push_back( x2 ); 01068 01069 // Increase step count. 01070 nstp++; 01071 } 01072 01073 #ifdef DEBUG_PARTICLE_ITERATOR 01074 std::cout << "\n*** Done stepping ***\n"; 01075 #endif 01076 01077 // Check if step count or time limited 01078 if( nstp == _maxsteps ) { 01079 particle->set_status( PARTICLE_NSTP ); 01080 _stat.inc_end_step(); 01081 } else if( x[0] >= _maxt ) { 01082 particle->set_status( PARTICLE_TIME ); 01083 _stat.inc_end_time(); 01084 } 01085 01086 // Save step count 01087 _stat.inc_sum_steps( nstp ); 01088 01089 // Save trajectory of current particle 01090 if( _trajdiv != 0 && pi % _trajdiv == 0 ) 01091 particle->copy_trajectory( _traj ); 01092 01093 // Save last particle location 01094 particle->x() = x; 01095 01096 // Call trajectory end callback 01097 if( _tend_cb ) 01098 (*_tend_cb)( particle, _pdb ); 01099 } 01100 01101 }; 01102 01103 01104 #endif