EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MgridInterpolation.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MgridInterpolation.cxx
1 /* -------------------------------------------------------------------------- */
2 /* MgridDirection.cc */
3 /* */
4 /* Magnetic field map interpolation routines. */
5 /* */
6 /* A.Kisselev, PNPI, St.Petersburg, Russia. */
7 /* e-mail: kisselev@hermes.desy.de */
8 /* -------------------------------------------------------------------------- */
9 
10 #include <cstdio>
11 #include <cassert>
12 #include <cstring>
13 
14 #include <ayk.h>
15 #include <Mgrid.h>
16 
17 /* ========================================================================== */
18 /* Well, it is convenient to have an option to calculate the same point with */
19 /* different interpolation techniques; however switching should be fast; so */
20 /* it makes sense to cook few interpolation frames for a particular grid once */
21 /* and then simply switch pointers; */
22 
24 {
25  memset(this, 0x00, sizeof(MgridInterpolation));
26 
27  // Well, procedure makes no sense for a collection of grids;
28  assert (mgrid->__type != _MGRID_HEAP_);
29 
30  _mgrid = mgrid;
31 
32  // Per default only normal and edge cells used;
35 
36  // May move cube (at least away from grid border);
37  // more complicated positioning may be implemented later;
39 
40  // This is true for most of the modes;
42 } /* MgridInterpolation::MgridInterpolation */
43 
44 /* ========================================================================== */
45 /* Per default only _NORMAL_CELL_ cells used; may be changed by this routine;*/
46 
47 int MgridInterpolation::setAllowedCells(unsigned char _allowed_self_cells_mask,
48  unsigned char _allowed_neighbour_cells_mask)
49 {
50  // Only 4 lower bits should be present;
51  if (_allowed_self_cells_mask & 0xF0 || _allowed_neighbour_cells_mask & 0xF0)
52  return -1;
53 
54  // Of course no sense to use _DEAD_CELL_ as neighbours;
55  if (_allowed_neighbour_cells_mask & _DEAD_CELL_) return -1;
56 
57  // This is also clear; only 3dim orth polynomial modes may work
58  // with missing self cells;
60  _allowed_self_cells_mask & _DEAD_CELL_)
61  return -1;
62 
63  allowed_self_cells_mask = _allowed_self_cells_mask;
64  allowed_neighbour_cells_mask = _allowed_neighbour_cells_mask;
65 
66  return 0;
67 } /* MgridInterpolation::setAllowedCells */
68 
69 /* ========================================================================== */
70 
72 {
73  // Allocate cube of appropriate size for cell pointers;
74  cube_cell_num = 1;
75  for(int ik=0; ik<3; ik++)
76  cube_cell_num *= adim[ik];
77 
78  if (cube) delete[] cube;
79  cube = new MgridCell* [cube_cell_num];
80 
81  return 0;
82 } /* MgridInterpolation::allocateCellCubeMemory */
83 
84 /* ========================================================================== */
85 
87 {
88  for(int ik=0; ik<3; ik++)
89  reversed[sequence[ik]] = ik;
90 } /* MgridInterpolation::calculateReversedSequence */
91 
92 /* -------------------------------------------------------------------------- */
93 /* Probably not really best to the moment; just ascending order in adim[]; */
94 /* the organization is a bit strange (I mean inter->sequence[] array will be */
95 /* called once again) ...; */
96 
98 {
99  // Default initialization;
100  for(int ii=0; ii<3; ii++)
101  sequence[ii] = ii;
102 
103  // Trivial bubble sorting suffices;
104  for(int ii=0; ii<2; ii++)
105  for(int ik=0; ik<2; ik++)
106  if (adim[sequence[ik]] > adim[sequence[ik+1]])
107  {
108  int bff = sequence[ik];
109 
110  sequence[ik] = sequence[ik+1];
111  sequence[ik+1] = bff;
112  } /*if..for*/
113 
114  // And assign reversed mapping array as well;
116 
117  return 0;
118 } /* MgridInterpolation::assignBestSequence */
119 
120 /* -------------------------------------------------------------------------- */
121 
123 {
124  CoordSystem *system = &_mgrid->coord;
125 
126  //
127  // Sequential interpolation will alway proceed as a sequence
128  // in ALL 3 dimensions (even if say internal grid is 2dim only);
129  // I mean it is just 3 loops :-); for the missing dimensions
130  // 'adim' is set to 1; but they still need to be assigned;
131  //
132 
133  // This loop is along _sequence[] characters;
134  for(int ii=0; ii<3; ii++)
135  {
136  // Double counting check;
137  for(int ik=0; ik<ii; ik++)
138  if (_sequence[ii] == _sequence[ik])
139  return -1;
140 
141  t_coord_name *cname = find_coord_by_name(system->system_type, _sequence[ii]);
142  // Bad coordinate name;
143  if (!cname) return -1;
144 
145  sequence[ii] = cname->id;
146  } /*for*/
147 
148  // And assign reversed mapping array as well;
150 
151  return 0;
152 } /* MgridInterpolation::assignSuggestedSequence */
153 
154 /* ========================================================================== */
155 
156 int MgridInterpolation::precookSequentialHighCommon(int _adim[], char _sequence[])
157 {
158  // Check _sequence[] input;
159  if (_sequence)
160  {
161  if (assignSuggestedSequence(_sequence)) return -1;
162  }
163  else
165 
166  // Check _adim[] input;
167  for(int ik=0; ik<3; ik++)
168  {
169  if (_adim[ik] < 1 || _adim[ik] > _mgrid->dir[ik].dim) return -1;
170 
171  adim[ik] = _adim[ik];
172  } /*for*/
173 
174  if (allocateCellCubeMemory()) return -1;
175 
176  return 0;
177 } /* MgridInterpolation::precookSequentialHighCommon */
178 
179 /* -------------------------------------------------------------------------- */
180 
182 {
183  // And intialization, specific for this mode;
184  for(int ip=0; ip<3; ip++)
185  {
186  // Only one of the dimensions will not be equal to '1' (see below);
187  int bff[3] = {1, 1, 1}, true_coord = sequence[ip];
188 
189  bff[true_coord] = pdim[true_coord];
190 
191  // NB: hspace[] is in interpolation sequence;
192  ThreeDeePolySpace *space = hspace[ip] =
193  new ThreeDeePolySpace(bff, adim[true_coord]);
194 
195  for(int iq=0; iq<adim[true_coord]; iq++)
196  {
197  ThreeDeePolyPoint *point = space->points + iq;
198 
199  // Other 2 coordinates do not matter;
200  point->xx[true_coord] = _mgrid->cell_center_coord[true_coord][iq];
201  } /*for iq*/
202 
203  // Now when everything is prepared calculate basis;
205 
206  hfit[ip] = new ThreeDeePolynomial(space);
207  } /*for ip*/
208 
209  return 0;
210 } /* MgridInterpolation::postcookSequentialHighCommon */
211 
212 /* ========================================================================== */
213 /* Common part for multi-dim interpolation&fitting; adim[] here is assumed in */
214 /* a natural XYZ (RFZ) sequence /since sequence[] paramater is not used/; */
215 /* !! NB: all 3 elements in adim[] expected (so for RZ-grid to be fitted in */
216 /* 4x4 squares qdim[]= {4, 1, 4}); */
217 
219 {
220  // Check adim[] input;
221  for(int ik=0; ik<3; ik++)
222  {
223  if (_adim[ik] < 1 || _adim[ik] > _mgrid->dir[ik].dim) return -1;
224 
225  adim[ik] = _adim[ik];
226  } /*for ik*/
227 
228  if (allocateCellCubeMemory()) return -1;
229 
230  return 0;
231 } /* MgridInterpolation::preCookMultiDimCommon */
232 
233 /* -------------------------------------------------------------------------- */
234 /* Common part for multi-dim interpolation&fitting; */
235 
237 {
238  int i012[3];
239 
240  // It is easy to define coordinates to the leftmost corner, build basis and
241  // then only change function values and shift xx[] accordingly;
243 
244  for(i012[0]=0; i012[0]<adim[0]; i012[0]++)
245  for(i012[1]=0; i012[1]<adim[1]; i012[1]++)
246  for(i012[2]=0; i012[2]<adim[2]; i012[2]++)
247  {
249  i012[0]*adim[_Y_]*adim[_Z_] + i012[1]*adim[_Z_] + i012[2];
250 
251  // '3': will not hurt for missing dimensions as well;
252  for(int ik=0; ik<3; ik++)
253  point->xx[ik] = _mgrid->cell_center_coord[ik][i012[ik]];
254  } /*for..for*/
255 
256  // Now when everything is prepared calculate basis; indeed
257  // this is done only once assuming weight=1. in all points;
259 
260  // And allocate the same frame for a non-regular case; prefer a separate loop
261  // for clarity; 'weight=1' is now done in this constructor per default;
262  // cell coordinates are not known to this point => that's it;
264 
265  // Also want to intialize polynomial frames to be used for fitting;
266  // inter->gspace & inter->irregular have the same parameters => may
267  // have one set of fitting polynomials and use gspace for initialization;
269 
270  for(int ik=0; ik<3; ik++)
271  grad[ik] = new ThreeDeePolynomial(gspace);
272 
273  // (Almost) any collection of cells will work;
274  // and it is unreasonable to shift the cube (means
275  // it is better to use all cells which are most close
276  // to the self one;
278 
279  return 0;
280 } /* MgridInterpolation::postCookMultiDimCommon */
281 
282 /* ========================================================================== */
283 
284 int MgridInterpolation::fillOkArray(int left[3], int adim[3], int cmp,
285  MgridCombiCell *ok_arr)
286 {
287  int ok_counter = 0, bff[3], dim = adim[0]*adim[1]*adim[2];
288  MgridCombiCell qcell;
289 
290  // All cells bad per default;
291  memset(ok_arr, 0x00, dim*sizeof(MgridCombiCell));
292 
293  // Check all cells of suggested cube and set '1' for those which pass all tests;
294  for(int i0=0; i0<adim[0]; i0++)
295  {
296  bff[0] = left[0] + i0;
297 
298  for(int i1=0; i1<adim[1]; i1++)
299  {
300  bff[1] = left[1] + i1;
301 
302  for(int i2=0; i2<adim[2]; i2++)
303  {
304  bff[2] = left[2] + i2;
305 
306  if (_mgrid->multiAddrToCombiCell(bff, &qcell) ||
307  // Extra checks if weight usage is foreseen; for directional (1dim) errors it
308  // is sufficient to check that error along sweeping direction is non-zero;
310  !qcell.cell->B[_mgrid->cell_1derr_shift + cmp*_mgrid->coord.coord_num + 0]) ||
312  continue;
313 
314  if (qcell.property == _DEAD_CELL_) continue;
315  if (!(qcell.property & allowed_neighbour_cells_mask)) continue;
316 
317  ok_counter++;
318  ok_arr[i0*adim[1]*adim[2] + i1*adim[2] + i2] = qcell;
319  } /*for i2*/
320  } /*for i1*/
321  } /*for i0*/
322 
323  return ok_counter;
324 } /* MgridInterpolation::fillOkArray */
325 
326 /* -------------------------------------------------------------------------- */
327 /* Unify with th eoutput routine later, please; */
328 
330  int shift[3])
331 {
332  // 3D loop has adim[] indexing of course;
333  for(int i0=0; i0<adim[0]; i0++)
334  {
335  int i0s = i0 + shift[0];
336 
337  for(int i1=0; i1<adim[1]; i1++)
338  {
339  int i1s = i1 + shift[1];
340 
341  for(int i2=0; i2<adim[2]; i2++)
342  {
343  int i2s = i2 + shift[2];
344 
345  // Yes, ok_arr[][][] has gdim[] dimensions (and NOT necessarily adim[]!);
346  MgridCombiCell *qcell = ok_arr + i0s*gdim[1]*gdim[2] + i1s*gdim[2] + i2s;
347 
348  // Yes, if cell did not match the criteria, pointer is just NULL;
349  if (!qcell->cell) return -1;
350  } /*for i2*/
351  } /*for i1*/
352  } /*for i0*/
353 
354  return 0;
355 } /* MgridInterpolation::checkSmallCube */
356 
357 /* -------------------------------------------------------------------------- */
358 
360  int shift[3])
361 {
362  // 3D loop has adim[] indexing of course;
363  for(int i0=0; i0<adim[0]; i0++)
364  {
365  int i0s = i0 + shift[0];
366 
367  for(int i1=0; i1<adim[1]; i1++)
368  {
369  int i1s = i1 + shift[1];
370 
371  for(int i2=0; i2<adim[2]; i2++)
372  {
373  int i2s = i2 + shift[2];
374 
375  // Yes, ok_arr[][][] has gdim[] dimensions (and NOT necessarily adim[]!);
376  MgridCombiCell *qcell = ok_arr + i0s*gdim[1]*gdim[2] + i1s*gdim[2] + i2s;
377 
378  // Yes, if cell did not match the criteria, pointer is just NULL;
379  if (!qcell->cell) continue;
380 
381  // Record USED neighbour cell status; eventually these 4 bits may
382  // contain a mixture of SAFE/EDGE/EXTRA;
383  if (qcell->cell != _mgrid->self_qcell.cell)
384  _mgrid->last_field_status |= qcell->property << 4;
385 
386  // Put cell address into cube[] array and advance counter;
387  cube[cell_counter++] = qcell->cell;
388  } /*for i2*/
389  } /*for i1*/
390  } /*for i0*/
391 } /* MgridInterpolation::fillOutputCubeArray */
392 
393 /* -------------------------------------------------------------------------- */
394 /* In this case have to position a true KxMxN cube of cells around xx[]; first*/
395 /* figure out coordinates of the best possible 'leftmost' in all 3 directions */
396 /* cell; if all cells in this cube satisfy the conditions, take it; otherwise */
397 /* (and this was for whatever reason missing before May'2008) if ideal cube */
398 /* was required (say for sequential interpolation) try ALL possible cubes */
399 /* containing xx[] cell and choose the best one with full set of cells; */
400 /* NB: software stopped working correctly for non-potato cell area required */
401 /* for HERMES spectrometer magnet field map (septum plate gap!); only one */
402 /* fixed option of ideal cube was tried out and since direction towards septum*/
403 /* plate was available this cube hit DEAD cell area and failed; */
404 
406 {
407  // '0/1' depending on whether requested cube has even (0) or odd (1) number
408  // of cells along this XYZ (RFZ) side; natural XYZ (RFZ) sequence;
409  unsigned char oddity[3];
410 
411  cell_counter = 0;
412 
413  // Otherwise cube was selected already, just reuse;
414  if (!_mgrid->repetition_flag)
415  {
416  // Calculate oddity[] and left[] arrays; need true left corner of the cube;
417  for(int ik=0; ik<3; ik++)
418  if (_mgrid->coord.fake[ik])
419  {
420  // Save CPU time on fake directions;
421  oddity[ik] = 1;
422  left[ik] = 0;
423  }
424  else
425  {
426  MgridDirection *dir = _mgrid->dir + ik;
427 
428  oddity[ik] = adim[ik]%2;
429 
430  // Depends on whether 'size' was even or odd;
431  left[ik] = _mgrid->self[ik] - (adim[ik]-1)/2;
432 
433  // Even number of cells along this direction and xx[] is to the left
434  // of it's own cell center => move by 1 to the left;
435  if (!oddity[ik] && xx[ik] < _mgrid->cell_center_coord[ik][_mgrid->self[ik]])
436  left[ik]--;
437  } /*if..for ik*/
438 
439  for(int ik=0; ik<3; ik++)
440  {
441  if (_mgrid->coord.fake[ik]) continue;
442 
443  MgridDirection *dir = _mgrid->dir + ik;
444 
445  // Correct obvious screw up if possible (and allowed);
446  if (left[ik] < 0 )
447  {
449  if (cube_shift_allowed)
450  left[ik] = 0;
451  else
452  {
454  } /*if*/
455  } /*if*/
456  if (left[ik] + adim[ik] - 1 >= dir->dim)
457  {
459 
460  if (cube_shift_allowed)
461  {
462  left[ik] = dir->dim - adim[ik];
463  // Cube is too big?;
464  if (left[ik] < 0) _FAILURE_(_mgrid, _CUBE_FAILURE_)
465  }
466  else
467  {
469  } /*if*/
470  } /*if*/
471  } /*for*/
472  } /*if*/
473 
474  // Ok, so first try this suggested cube;
475  {
476  // Create 3D cube and stupidly check ALL it's cells;
477  MgridCombiCell ok[adim[0]][adim[1]][adim[2]];
478  int ok_counter = fillOkArray(left, adim, cmp, (MgridCombiCell*)ok);
479 
480  // If all cells fine or ideal cube is not required, go fill out
481  // output array based on this returned qcell[] array;
482  if (ok_counter == adim[0]*adim[1]*adim[2] ||
484  {
485  // Yes, no shifts, exact match;
486  int shift[3] = {0, 0, 0};
487 
488  fillOutputCubeArray(adim, (MgridCombiCell*)ok, shift);
489 
490  return 0;
491  }
492  else
493  {
494  // Ok, want to find ideal cube; clearly have to investigate
495  // at most 2n-1 shifts in all directions;
496  int gdim[3], gleft[3];
497 
498  for(int iq=0; iq<3; iq++)
499  {
500  gdim[iq] = adim[iq]*2 - 1;
501  gleft[iq] = _mgrid->self[iq] - adim[iq] + 1;
502  } /*for iq*/
503 
504  {
505  MgridCombiCell gok[gdim[0]][gdim[1]][gdim[2]];
506 
507  fillOkArray(gleft, gdim, cmp, (MgridCombiCell*)gok);
508 
509  // Loop through all small cubes of adim[] size
510  // inside this "big" cube; for now fall out as soon as the very first
511  // "complete" cube found; do it better later;
512  for(int i0=0; i0<adim[0]; i0++)
513  for(int i1=0; i1<adim[1]; i1++)
514  for(int i2=0; i2<adim[2]; i2++)
515  {
516  int gshift[3] = {i0, i1, i2};
517 
518  if (checkSmallCube(gdim, (MgridCombiCell*)gok, gshift))
519  continue;
520 
521  // Ok, "good" cube found; fill output array and return 0;
522  for(int iq=0; iq<3; iq++)
523  left[iq] = gleft[iq] + gshift[iq];
524 
525  fillOutputCubeArray(gdim, (MgridCombiCell*)gok, gshift);
526  return 0;
527  } /*for i0..i2*/
528  }
529 
530  // No luck --> return failure code;
532  }
533  }
534 
535  return 0;
536 } /* MgridInterpolation::checkNeighbouringCubicArea */
537 
538 /* -------------------------------------------------------------------------- */
539 /* This mode is used for multi-dim irregular case where inter->left[] is not */
540 /* needed any longer --> may destroy; NB: in principle this code may loop */
541 /* infinitely - fix this later; */
542 
544 {
545  int right[3], bff[3];
546  MgridCombiCell qcell;
547 
548  // Figure out right side of the cube (remember: cube failed!);
549  for(int ik=0; ik<3; ik++)
550  right[ik] = left[ik] + adim[ik] - 1;
551 
552  // Sequentially choose one of the 3 dimensions and loop through
553  // it's min/max values, always expanding by 1 (keeping other
554  // dimensions constant); this basically means that I always
555  // loop through possible cells on a 2-dim rectangle;
556  for(int xyz=_X_; ; xyz = (xyz+1)%3)
557  {
558  // If this is a fake direction, just skip it;
559  if (_mgrid->coord.fake[xyz] || extension_prohibited[xyz]) continue;
560 
561  MgridDirection *dir = _mgrid->dir + xyz;
562 
563  // 2 other directions;
564  int d1 = (xyz+1)%3, d2 = (xyz+2)%3;
565 
566  // If at the limit check whether it makes sense to deal with the other
567  // 2 directions; takes time but allows to avoid getting stuck;
568  if (left[xyz] <= 0 && right[xyz] >= dir->dim - 1)
569  {
570  // If extension in both other directions prohibited, I'm stuck;
572  return -1;
573 
574  // Think once again later whether this logic is Ok;
575 #if 1
576  // If extension in one of the other directions prohibited and
577  // 3-d direction is exhausted, same story;
578  if (extension_prohibited[d1] &&
579  left[d2] <= 0 && right[d2] >= _mgrid->dir[d2].dim - 1)
580  return -1;
581  if (extension_prohibited[d2] &&
582  left[d1] <= 0 && right[d1] >= _mgrid->dir[d1].dim - 1)
583  return -1;
584 
585  // Eventually if both other directions are exhausted, return -1 as well;
586  if (left[d1] <= 0 && right[d1] >= _mgrid->dir[d1].dim - 1 &&
587  left[d2] <= 0 && right[d2] >= _mgrid->dir[d2].dim - 1)
588  return -1;
589 #endif
590  } /*if*/
591 
592  for(int side=0; side<=1; side++)
593  {
594  if (!side)
595  {
596  // At the very left edge or beyond --> can not move further
597  // --> try other side (or other direction);
598  if (left[xyz] <= 0) continue;
599  bff[xyz] = --left[xyz];
600  }
601  else
602  {
603  // At the very right edge or beyond --> can not move further
604  // --> try other side (or other direction);
605  if (right[xyz] >= dir->dim - 1) continue;
606  bff[xyz] = ++right[xyz];
607  } /*if*/
608 
609  // 2-dim loop over other 2 directions;
610  for(bff[d1]=left[d1]; bff[d1]<=right[d1]; bff[d1]++)
611  for(bff[d2]=left[d2]; bff[d2]<=right[d2]; bff[d2]++)
612  {
613  // No such cell, no worries;
614  if (_mgrid->multiAddrToCombiCell(bff, &qcell)) continue;
615 
616  // Extra check if weight usage is foreseen;
617  if ((force_1dim_weight_usage &&
618  !qcell.cell->B[_mgrid->cell_1derr_shift + cmp*_mgrid->coord.coord_num + 0]) ||
620  continue;
621 
622  // Self cell was checked already -> skip this part;
623  // in fact this can not ever happen (?);
624  if (qcell.cell == _mgrid->self_qcell.cell ||
625  // This type of neighbour cells is not allowed;
627  continue;
628 
629  // Record USED neighbour cell status; eventually these 4 bits may
630  // contain a mixture of SAFE/EDGE/EXTRA;
631  _mgrid->last_field_status |= qcell.property << 4;
632 
633  // Put cell address into cube[] array and advance counter;
634  cube[cell_counter++] = qcell.cell;
635  // All needed cells allocated -> return;
636  if (cell_counter == cube_cell_num) return 0;
637  } /*for..for*/
638  } /*for*/
639  } /*for*/
640 } /* MgridInterpolation::appendArbitraryNeighbouringCells */
641 
642 /* -------------------------------------------------------------------------- */
643 
645 {
646  TVector3 xx;
647 
648  // Put in cell coordinates;
649  for(int ik=0; ik<cube_cell_num; ik++)
650  {
652  MgridCell *cell = cube[ik];
653 
654  if (_mgrid->cellPtrToCoord(cell, xx)) return -1;
655 
656  for(int iq=0; iq<3; iq++)
657  point->xx[iq] = xx[iq];
658 
659  // Please close direct access to this flag setting and check in the setting
660  // routine that respective bit is actually present in mgrid cell structure;
662  point->weight = 1./SQR(cell->B[_mgrid->cell_3derr_shift+cmp]);
663  } /*for*/
664 
665  // And rebuild basis polynomial vectors;
667 
668  return 0;
669 } /* MgridInterpolation::prepairIrregularCase */
670 
671 /* -------------------------------------------------------------------------- */
672 
673 int Mgrid::findSelfCell(TVector3 &xx)
674 {
675  // Find out 3dim xx[] indices in this grid;
676  // 'repetition_flag': 'self' cell was assigned by hand already;
677  if (!repetition_flag)
678  {
679  if (coordToMultiAddr(xx, self))
680  // Cell is out of grid at all --> definitely bad;
682 
683  // Figure out cell pointer; cell is out of grid at all
684  // (should not happen - see check above);
685  assert(!multiAddrToCombiCell(self, &self_qcell));
686  } /*if*/
687 
688  // Record self cell status;
690 
691  return 0;
692 } /* Mgrid::findSelfCell */
693 
694 /* ========================================================================== */