@@ -41,18 +41,20 @@ static PyMethodDef AtGPUMethods[] = {
4141 " reuse: if True, use previously cached description of the lattice.\n "
4242 " losses: if True, process losses\n "
4343 " gpu_pool: List of GPU id to use\n "
44- " tracking_starts: numpy array of indices of elements where tracking should start.\n "
45- " len(tracking_start) must divide the number of particle and it gives the stride size.\n "
46- " The i-th particle of rin starts at elem tracking_start[i/stride].\n "
47- " The behavior is similar to lattice.rotate(tracking_starts[i/stride]).\n "
48- " The stride size should be multiple of 64 for best performance.\n "
4944 " integrator: Type of integrator to use.\n "
5045 " 1: Euler 1st order, 1 drift/1 kick per step.\n "
5146 " 2: Mclachlan 2nd order, 2 drift/2 kicks per step.\n "
5247 " 3: Ruth 3rd order, 3 drifts/3 kicks per step.\n "
5348 " 4: Forest/Ruth 4th order, 4 drifts/3 kicks per step (Default).\n "
5449 " 5: Optimal 4th order from R. Mclachlan, 4 drifts/4 kicks per step.\n "
55- " 6: Yoshida 6th order, 8 drifts/7 kicks per step.\n\n "
50+ " 6: Yoshida 6th order, 8 drifts/7 kicks per step.\n "
51+ " start_elem: Start tracking at the specified element. The\n "
52+ " behavior is different from :py:func:`..lattice.rotate`, refpts are\n "
53+ " still indexed from the begininig of the ring and s_coord starts at\n "
54+ " the beginning of the ring. Default 0.\n "
55+ " end_elem: End tracking at the specified element (exclusive).\n "
56+ " When end_elem is different from the number of lattice elements,\n "
57+ " nturns=1 is required. Default number of elements.\n\n "
5658 " Returns:\n "
5759 " rout: 6 x n_particles x n_refpts x n_turns Fortran-ordered numpy array\n "
5860 " of particle coordinates\n\n "
@@ -138,7 +140,8 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
138140 " energy" , " particle" , " keep_counter" ,
139141 " reuse" ," losses" ,
140142 " bunch_spos" , " bunch_currents" , " gpu_pool" ,
141- " tracking_starts" ," integrator" ," verbose" ,
143+ " integrator" ," verbose" ,
144+ " start_elem" ," end_elem" ,
142145 nullptr };
143146
144147 NPY_TYPES floatType;
@@ -168,10 +171,12 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
168171 int counter=0 ;
169172 int losses=0 ;
170173 int integratorType=4 ;
174+ int startElem=0 ;
175+ int endElem=-1 ;
171176 double t0,t1;
172177
173178 // Get input args
174- if (!PyArg_ParseTupleAndKeywords (args, kwargs, " O!O!i|O!$iO!O!pppO!O!O!O!ip " , const_cast <char **>(kwlist),
179+ if (!PyArg_ParseTupleAndKeywords (args, kwargs, " O!O!i|O!$iO!O!pppO!O!O!ipii " , const_cast <char **>(kwlist),
175180 &PyList_Type, &lattice,
176181 &PyArray_Type, &rin,
177182 &num_turns,
@@ -185,9 +190,10 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
185190 &PyArray_Type, &bspos,
186191 &PyArray_Type, &bcurrents,
187192 &PyList_Type, &gpupool,
188- &PyArray_Type, &trackstarts,
189193 &integratorType,
190- &verbose
194+ &verbose,
195+ &startElem,
196+ &endElem
191197 )) {
192198 return nullptr ;
193199 }
@@ -292,6 +298,32 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
292298
293299 }
294300
301+ int num_elements = gpuLattice->getNbElement ();
302+
303+ // Check for partial turn tracking
304+ if (endElem==-1 ) endElem = num_elements;
305+ if (num_elements!=0 ) {
306+ if (startElem<0 || startElem>=num_elements) {
307+ char errMsg[64 ];
308+ sprintf (errMsg," start_elem out of range, %d not in [0..%d]" ,startElem,num_elements-1 );
309+ return PyErr_Format (PyExc_ValueError, errMsg);
310+ }
311+ if (endElem<0 || endElem>num_elements) {
312+ char errMsg[64 ];
313+ sprintf (errMsg," end_elem out of range, %d not in [0..%d]" ,endElem,num_elements);
314+ return PyErr_Format (PyExc_ValueError, errMsg);
315+ }
316+ if (startElem>endElem) {
317+ return PyErr_Format (PyExc_ValueError, " end_elem must be greater that start_elem" );
318+ }
319+ if ((endElem!=num_elements) && (num_turns!=1 )) {
320+ return PyErr_Format (PyExc_ValueError, " partial turn tracking not ending at the end of the ring requires nturns=1" );
321+ }
322+ } else {
323+ startElem = 0 ;
324+ endElem = 0 ;
325+ }
326+
295327 // Load lattice on the GPU
296328 try {
297329 uint64_t size = gpuLattice->fillGPUMemory ();
@@ -309,7 +341,8 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
309341 try {
310342
311343 if ( verbose )
312- cout << " Tracking " << num_particles << " particles for " << num_turns << " turns on " << gpuLattice->getGPUContext ()->name () << " #" << gpuId << endl;
344+ cout << " Tracking " << num_particles << " particles for " << num_turns << " turns on " <<
345+ gpuLattice->getGPUContext ()->name () << " #" << gpuId << " Integrator: #" << integratorType << endl;
313346
314347 npy_intp outdims[4 ] = {6 ,(npy_intp)(num_particles),num_refs,num_turns};
315348 PyObject *rout = PyArray_EMPTY (4 , outdims, floatType, 1 );
@@ -330,7 +363,7 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
330363 bool *xlostPtr = (bool *)PyArray_DATA ((PyArrayObject *)xlost);
331364 AT_FLOAT *xlostcoordPtr = (AT_FLOAT *)PyArray_DATA ((PyArrayObject *)xlostcoord);
332365
333- gpuLattice->run (num_turns,num_particles,drin,drout,num_refs,ref_pts,num_starts,track_starts ,
366+ gpuLattice->run (num_turns,num_particles,drin,drout,num_refs,ref_pts,startElem,endElem ,
334367 xnturnPtr,xnelemPtr,xlostcoordPtr,true );
335368
336369 // Format result for AT
@@ -355,7 +388,7 @@ static PyObject *at_gpupass(PyObject *self, PyObject *args, PyObject *kwargs) {
355388
356389 } else {
357390
358- gpuLattice->run (num_turns,num_particles,drin,drout,num_refs,ref_pts,num_starts,track_starts ,
391+ gpuLattice->run (num_turns,num_particles,drin,drout,num_refs,ref_pts,startElem,endElem ,
359392 nullptr ,nullptr ,nullptr ,true );
360393 return rout;
361394
0 commit comments