EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Mgrid.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Mgrid.cxx
1 /* -------------------------------------------------------------------------- */
2 /* Mgrid.cc */
3 /* */
4 /* Magnetic field map handling routines. */
5 /* */
6 /* A.Kisselev, PNPI, St.Petersburg, Russia. */
7 /* e-mail: kisselev@hermes.desy.de */
8 /* -------------------------------------------------------------------------- */
9 
10 #include <cstdio>
11 #include <cmath>
12 #include <cassert>
13 #include <cstring>
14 #include <cstdlib>
15 #include <ctime>
16 
17 #include <ayk.h>
18 #include <Mgrid.h>
19 
20 /* ========================================================================== */
21 
22 //
23 // -> Constructors are private; user interface is done via external functions;
24 //
25 
26 Mgrid::Mgrid(char *_name, MgridType _type)
27 {
28  // Put magic sign and format ID in;
31  creation_time = time(0);
32 
33  __type = _type;
34 
36 
37  // Assign mgrid name;
38  snprintf(name, MGRID_NAME_LENGTH_MAX-1, "%s", _name);
39 
40  shift[0] = shift[1] = shift[2] = 0.0;
41  // Assign rotations in a way which means 'no rotation';
42  theta[0] = 90.; theta[1] = 90.; theta[2] = 0.;
43  // phi[2] is of no importance if theta[2]=0.;
44  phi [0] = 0.; phi [1] = 90.;
45 
46  position = NULL;
47 
48  repetition_flag = 0;
49 } /* Mgrid::Mgrid */
50 
51 /* -------------------------------------------------------------------------- */
52 
53 //
54 // -> error handling is needed;
55 // also handle shift[] and repetition_flag init?;
56 //
57 
58 Mgrid::Mgrid(FILE *fin)
59 {
60  memset(this, 0x00, sizeof(Mgrid));
61 
62  // Read in the header;
63  if (fread(this, sizeof(MgridHeader), 1, fin) != 1) return; // NULL;
64  // Check magic word;
66  return; //NULL;
67 
68  if (__type == _MGRID_HEAP_)
69  {
70  // Perform initialization first of all;
71  if (initializeAsHeap()) return; // NULL;
72 
73  // Loop through all the children; recursion;
74  // will this actually work in constructor?;
75  for(int ch=0; ch<child_num; ch++)
76  {
77  //children[ch] = import_mgrid(fin);
78  children[ch] = new Mgrid(fin);
79  //if (!children[ch]) return NULL;
80  } /*for*/
81  }
82  else
83  {
84  // Perform initialization first of all;
85  if (initializeAsSingleMgrid(0)) return; // NULL;
86 
87  // Read in mgrid cell data; NB: block read possible since
88  // initialize_mgrid(mgrid, 0) was called before (so that
89  // ram_cell_size=useful_cell_size);
91  fread(cells, useful_cell_size, cell_num, fin) != cell_num)
92  return; // NULL;
93 
95  {
96  int tail = suggestedPropertyArrayTail();
97 
98  if (fread(properties, sizeof(char), cell_num, fin) != cell_num ||
99  (tail && fread(properties, sizeof(char), tail, fin) != tail))
100  return; // NULL;
101  }
102  else
103  // Best guess values;
104  for(int ik=0; ik<cell_num; ik++)
105  properties[ik] = _SAFE_CELL_;
106  } /*if*/
107 } /* Mgrid::Mgrid */
108 
109 /* ========================================================================== */
110 /* calloc() call + initialize what is possible to default values; of course */
111 /* if this t_mgrid frame is used to store input grid, everything will be */
112 /* overwritten by fread(); */
113 
114 //
115 // -> header creation is different for mgrid heap and single mgrid;
116 //
117 
119  CoordSystem *field, MgridDirection *dir[3], unsigned cell_contents_bits)
120 {
121  Mgrid *mgrid = new Mgrid(name, _RECTANGULAR_MGRID_);
122 
123  // Copy over structures describing coordinates and field components;
124  mgrid->coord = *coord;
125  mgrid->field = *field;
126 
127  // Assign direction descriptors; NB: all 3 must be present
128  // in dir[3] on input, and in a natural XYZ (RFZ) sequence;
129  for(int ik=0; ik<3; ik++)
130  if (mgrid->coord.fake[ik])
131  {
132  // Force predefined situation in this case; check that provided pointer
133  // is really NULL (in order to avoid any ambiguities in input parameters);
134  if (dir[ik])
135  {
136  printf("[create_single_mgrid_header()]: dir[] pointer should be NULL"
137  " for fake coordinates\n");
138  return NULL;
139  } /*if*/
140 
141  mgrid->dir[ik] = MgridDirection(1, 0.0, 0.0);
142  }
143  else
144  {
145  // Full frame copy;
146  mgrid->dir[ik] = *dir[ik];
147 
148  // For non-fake dimensions step should be non-zero;
149  //if (!mgrid->dir[ik].step) return NULL;
150  if (!mgrid->dir[ik].getStep()) return NULL;
151  } /*if..for*/
152 
153  // 5 bits known for now; no problem to expand later;
154  if (cell_contents_bits >> _MAX_KNOWN_CELL_CONTENTS_BIT_) return NULL;
155  mgrid->cell_contents_bits = cell_contents_bits;
156 
157  return mgrid;
158 } /* create_single_mgrid_header */
159 
160 /* -------------------------------------------------------------------------- */
161 
162 Mgrid *create_mgrid_heap_header(char *name, int field_calculation_method)
163 {
164  Mgrid *mgrid = new Mgrid(name, _MGRID_HEAP_);
165 
166  mgrid->field_calculation_method = field_calculation_method;
167 
168  // Per default it is reasonable to assign _CARTESIAN_ initialization;
169  // just bacause if positioning is wanted, _CYLINDRICAL_ case would
170  // require additional transformation;
171  // I means these could be 2 additional parameters, but normally nobody
172  // should care about them; will be used in conversion routines;
173  //mgrid->coord.system_type = mgrid->field.system_type = _CARTESIAN_;
176 
177  return mgrid;
178 } /* create_mgrid_heap_header */
179 
180 /* ========================================================================== */
181 
183 {
184  // Position calculation is needed in both HEAP/SINGLE cases;
185  if (recalculatePosition()) return -1;
186 
187  if (child_num)
188  {
189  children = (Mgrid**)calloc(child_num, sizeof(Mgrid*));
190  if (!children) return -1;
191  } /*if*/
192 
193  scaling_factor = 1.;
194 
195  return 0;
196 } /* Mgrid::initializeAsHeap */
197 
198 /* -------------------------------------------------------------------------- */
199 
200 int Mgrid::initializeAsSingleMgrid(int _ram_cell_size)
201 {
202  // Position calculation is needed in both HEAP/SINGLE cases;
203  if (recalculatePosition()) return -1;
204 
205  // Initialize as a single grid; calculate useful cell size first;
208  else
209  // Well, I guess something useful must be stored?;
210  return -1;
211 
218 
219  // Calculate possible gradient[][] array shift in cell frame;
223 
224  // Calculate possible swerr[] shift in cell frame;
228 
232 
233  // Calculate possible xx[] shift in cell frame;
237 
238  // Change _CELL_DATA_DIM_ and recompile if needed;
240  useful_cell_size = cell_double_word_num*sizeof(double);
241 
242  // Now check suggested RAM-stored cell size;
243  if (!_ram_cell_size)
245  else
246  {
247  if (_ram_cell_size <= 0 || _ram_cell_size < useful_cell_size ||
248  _ram_cell_size > sizeof(MgridCell))
249  return -1;
250 
251  ram_cell_size = _ram_cell_size;
252  } /*if*/
253 
254  // Calculate coordinate/field mapping rules to convert
255  // possibly '<3'-dim coordinated and fields into 3dim space;
258 
259  cell_num = 1;
260  for(int ik=0; ik<3; ik++)
261  cell_num *= dir[ik].dim;
262  // Calculation of cell centers is needed even for the skewed case;
263  for(int ik=0; ik<3; ik++)
264  {
265  MgridDirection *pdir = dir + ik;
266 
267  // So numbering in this array matches internal grid
268  // numbering (not interpolation one!);
269  cell_center_coord[ik] = (double*)malloc(pdir->dim*sizeof(double));
270  if (!cell_center_coord[ik]) return -1;
271 
272  for(int ip=0; ip<pdir->dim; ip++)
273  cell_center_coord[ik][ip] = pdir->min + pdir->step*(ip + 0.5);
274  } /*for*/
275 
276  // Then do the actual allocation;
277  cells = (unsigned char*)calloc(cell_num, ram_cell_size);
278  properties = (unsigned char*)calloc(cell_num, sizeof(char));
279  if (!cells || !properties) return -1;
280  /* Do not mind to reset properties to _DEAD_CELL_ per default; */
281  for(int ik=0; ik<cell_num; ik++)
282  properties[ik] = _DEAD_CELL_;
283 
284  // And eventually attach default interpolation mode (wwc's 4x4x4
285  // weighted interpolation in all non-fake directions); NB: it is NOT
286  // the default HRC interpolation mode (which is qudratic interpolation
287  // along Y + weighted qudratic interpolation along X + quadratic
288  // interpolation along Z in a 4x3x3 cube;
289  {
290  int adim[3];
291 
292  for(int ik=0; ik<3; ik++)
293  if (coord.fake[ik])
294  adim[ik] = 1;
295  else
296  {
297  adim[ik] = coord.fake[ik] ? 1 : 4;
298  if (adim[ik] > dir[ik].dim) adim[ik] = dir[ik].dim;
299  } /*if..for*/
301  return -1;
302  }
303 
304  scaling_factor = 1.;
305 
306  return 0;
307 } /* Mgrid::initializeAsSingleMgrid */
308 
309 /* ========================================================================== */
310 /* User-level gateway functions; it is assumed that users at most want to */
311 /* change default interpolation mode once, without any extra complications; */
312 /* all the rest requires inclusion of mgsystem.h and usage of system-level */
313 /* routines like cook_interpolation() directly; 2 separate routines for */
314 /* interpolation and fitting look redundant, but will probably help users to */
315 /* control better what they are doing; */
316 
317 int Mgrid::setModeWrapper(unsigned mode, int adim[], int pdim[], char sequence[])
318 {
319  MgridInterpolation *inter;
320 
321  if (__type == _MGRID_HEAP_) return -1;
322 
323  // Create and assign this mode for all field components;
324  for(int ik=0; ik<field.coord_num; ik++)
325  {
326  inter = cookInterpolation(mode, adim, pdim, sequence);
327  if (!inter) return -1;
328 
329  // No sense to call switch_interpolation(), just do it;
331  interpolation [ik] = inter;
332  } /*for*/
333 
334  return 0;
335 } /* setModeWrapper */
336 
337 /* -------------------------------------------------------------------------- */
338 
339 int Mgrid::setInterpolationMode(unsigned mode, int adim[])
340 {
341  if (__type == _MGRID_HEAP_) return -1;
342 
343  if (mode != _SEQUENTIAL_FAST_INTERPOLATION_ &&
346  return -1;
347 
348  return setModeWrapper(mode, adim, NULL, NULL);
349 } /* Mgrid::setInterpolationMode */
350 
351 /* -------------------------------------------------------------------------- */
352 
354 {
355  // NB: adim[] is per definition in a natural XYZ sequence;
356  int adim[3] = {4, 3, 3};
357  char sequence[3] = {'Y', 'X', 'Z'};
358 
359  if (__type == _MGRID_HEAP_) return -1;
360 
361  return setModeWrapper(_SEQUENTIAL_FAST_INTERPOLATION_, adim, NULL, sequence);
362 } /* Mgrid::setHrcInterpolationMode */
363 
364 /* -------------------------------------------------------------------------- */
365 
367 {
368  if ( !htci) return -1;
369 
370  switch (htci->mode)
371  {
372  case _MODE_OFF_:
373  return turnInterpolationOff();
374  case _MODE_HRC_:
375  return setHrcInterpolationMode();
376  case _MODE_ADIM_:
377  //return setInterpolationMode(_SEQUENTIAL_HIGH_INTERPOLATION_, htci->adim);
379  default:
380  return -1;
381  } /*switch*/
382 } /* Mgrid::setHtcInterpolationMode */
383 
384 /* -------------------------------------------------------------------------- */
385 
386 int Mgrid::setFittingMode(unsigned mode, int adim[], int pdim[])
387 {
388  if (__type == _MGRID_HEAP_) return -1;
389 
390  if (mode != _SEQUENTIAL_HIGH_FITTING_ && mode != _MULTI_DIM_FITTING_)
391  return -1;
392 
393  return setModeWrapper(mode, adim, pdim, NULL);
394 } /* Mgrid::setFittingMode */
395 
396 /* -------------------------------------------------------------------------- */
397 
399 {
400  if (__type == _MGRID_HEAP_) return -1;
401 
402  return setModeWrapper(_NO_INTERPOLATION_, NULL, NULL, NULL);
403 } /* Mgrid::turnInterpolationOff */
404 
405 /* ========================================================================== */
406 
407 int Mgrid::switchInterpolation(char field_component_name, MgridInterpolation *inter)
408 {
409  t_coord_name *cname = find_coord_by_name(field.system_type, field_component_name);
410  int true_id = 0;
411 
412  if (!cname) return -1;
413 
414  // Check that this interpolation was really created for this mgrid;
415  if (this != inter->_mgrid) return -1;
416 
417  // Figure out 'compressed' id;
418  for(int ik=0; ik<3; ik++)
419  {
420  if (field.fake[ik]) continue;
421 
422  if (ik == cname->id) break;
423 
424  true_id++;
425  } /*for*/
426 
427  saved_interpolation[true_id] = interpolation[true_id];
428  interpolation [true_id] = inter;
429 
430  return 0;
431 } /* Mgrid::switchInterpolation */
432 
433 /* -------------------------------------------------------------------------- */
434 /* No stack, just one-time 'pop' mode; */
435 
437 {
438  for(int ik=0; ik<field.coord_num; ik++)
439  {
440  if (!saved_interpolation[ik]) return -1;
441 
443  saved_interpolation[ik] = NULL;
444  } /*for*/
445 
446  return 0;
447 } /* Mgrid::restoreInterpolations */
448 
449 /* ========================================================================== */
450 
452 {
453  MgridInterpolation *inter = new MgridInterpolation(this);
454 
455  inter->mode = _NO_INTERPOLATION_;
456 
457  return inter;
458 } /* Mgrid::cookInterpolationOff */
459 
460 /* ========================================================================== */
461 
462 #define quad1(x,xarr,yarr) \
463  (d3 = ((x)-(xarr)[2])/((xarr)[1]-(xarr)[0]), \
464  d1 = ((x)-(xarr)[1])/((xarr)[2]-(xarr)[0]), \
465  d2 = ((x)-(xarr)[0])/((xarr)[2]-(xarr)[1]), \
466  ( (yarr)[2]*d1*d2 + d3*((yarr)[0]*d1-(yarr)[1]*d2) ))
467 
468 #define quad2(x,xarr,yarr) \
469  (d6 = ((x)-(xarr)[2])/((xarr)[1]-(xarr)[0]), \
470  d4 = ((x)-(xarr)[1])/((xarr)[2]-(xarr)[0]), \
471  d5 = ((x)-(xarr)[0])/((xarr)[2]-(xarr)[1]), \
472  ( (yarr)[2]*d4*d5 + d6*((yarr)[0]*d4-(yarr)[1]*d5) ))
473 
474 static double quad3( double x, double xa[4], double ya[4])
475 {
476  double w1,w2;
477  double d1,d2,d3,d4,d5,d6; /* for quad macros */
478  if( xa[1] < xa[2] ) {
479  w1 = x - xa[1];
480 
481  if( w1 < 0 )
482  return quad1( x, xa, ya);
483 
484  w2 = xa[2] - x;
485  if( w2 < 0 )
486  return quad1( x, xa+1, ya+1);
487 
488  return (quad1( x, xa, ya) * w2 + quad2( x, xa+1, ya+1) * w1 )/(w1+w2);
489  } else {
490  w1 = xa[1] - x;
491  if( w1 < 0 )
492  return quad1( x, xa, ya);
493 
494  w2 = x - xa[2];
495  if( w2 < 0 )
496  return quad1( x, xa+1, ya+1);
497 
498  return (quad1( x, xa, ya) * w2 + quad2( x, xa+1, ya+1) * w1 )/(w1+w2);
499  }
500 } /* quad3 */
501 
502 /* -------------------------------------------------------------------------- */
503 /* Fake interpolation routine; used in order to allow 3 loops during field */
504 /* calculation even if the actual grid coordinate system has <3 dimensions; */
505 
506 static double trivial_assignment(double xx, double coord[1], double value[1])
507 {
508  return value[0];
509 } /* trivial_assignment */
510 
511 /* -------------------------------------------------------------------------- */
512 /* No check on coord[0] != coord[1] (should be done in the calling routine); */
513 
514 static double linear_interpolation(double xx, double coord[2], double value[2])
515 {
516  double slope = (value[1] - value[0])/(coord[1] - coord[0]);
517 
518  return value[0] + slope*(xx - coord[0]);
519 } /* linear_interpolation */
520 
521 /* -------------------------------------------------------------------------- */
522 /* A wrapper for wwc's quad1 macro; */
523 
524 static double quad_interpolation(double xx, double coord[3], double value[3])
525 {
526  double d1, d2, d3;
527 
528  return quad1(xx, coord, value);
529 } /* quad_interpolation */
530 
531 /* -------------------------------------------------------------------------- */
532 /* sequence[] and adim[] arrays here and below: all 3 elements expected; */
533 /* adim[]/pdim[] are always in a natural sequence; */
534 
536  char _sequence[])
537 {
538  MgridInterpolation *inter = new MgridInterpolation(this);
539 
541 
542  // Check sequence[] input and assign inter->sequence[];
543  if (_sequence)
544  {
545  if (inter->assignSuggestedSequence(_sequence)) return NULL;
546  }
547  else
548  inter->assignBestSequence();
549 
550  // Check adim[] input; loop is in a natural sequence;
551  for(int ik=0; ik<3; ik++)
552  {
553  if (_adim[ik] < 1 || _adim[ik] > 4 ||
554  // Well, mgrid->dir[ik].dim may be <4 :-)
555  _adim[ik] > this->dir[ik].dim)
556  return NULL;
557 
558  // Remap right here (adim[] array is in a natural XYZ sequence);
559  inter->adim[ik] = _adim[ik];
560  } /*for*/
561 
562  if (inter->allocateCellCubeMemory()) return NULL;
563 
564  // And intialization, specific for this mode; loop is along interpolation
565  // sequence!!!; NB: fun[] is also in interpolation sequence;
566  for(int ik=0; ik<3; ik++)
567  switch(inter->adim[inter->sequence[ik]])
568  {
569  case 1: inter->fun[ik] = trivial_assignment;
570  break;
571  case 2: inter->fun[ik] = linear_interpolation;
572  break;
573  case 3: inter->fun[ik] = quad_interpolation;
574  break;
575  case 4: inter->fun[ik] = quad3;
576  break;
577  } /*for ik..switch*/
578 
579  for(int ik=0; ik<3; ik++)
580  {
581  //if (inter->v[ik]) free(inter->v[ik]);
582  if (inter->v[ik]) delete[] inter->v[ik];
583 
584  // NB: v[] is also in interpolation sequence!;
585  inter->v[ik] =
586  //(double*)malloc(inter->adim[inter->sequence[ik]]*sizeof(double));
587  new double [inter->adim[inter->sequence[ik]]];
588  //if (!inter->v[ik]) return NULL;
589  } /*for ik*/
590 
591  return inter;
592 } /* Mgrid::cookSequentialFastInterpolation */
593 
594 /* ========================================================================== */
595 
597  char _sequence[])
598 {
599  MgridInterpolation *inter = new MgridInterpolation(this);
600 
602 
603  if (inter->precookSequentialHighCommon(_adim, _sequence)) return NULL;
604 
605  for(int ik=0; ik<3; ik++)
606  inter->pdim[ik] = inter->adim[ik];
607 
608  if (inter->postcookSequentialHighCommon()) return NULL;
609 
610  return inter;
611 } /* Mgrid::cookSequentialHighInterpolation */
612 
613 /* ========================================================================== */
614 
616  char _sequence[])
617 {
618  MgridInterpolation *inter = new MgridInterpolation(this);
619 
621 
622  if (inter->precookSequentialHighCommon(_adim, _sequence)) return NULL;
623 
624  // Check pdim[] input;
625  for(int ik=0; ik<3; ik++)
626  {
627  if (_pdim[ik] < 1 || _pdim[ik] > dir[ik].dim ||
628  (!coord.fake[ik] && _pdim[ik] >= _adim[ik]))
629  return NULL;
630 
631  inter->pdim[ik] = _pdim[ik];
632  } /*for*/
633 
634  if (inter->postcookSequentialHighCommon()) return NULL;
635 
636  return inter;
637 } /* Mgrid::cookSequentialHighFitting */
638 
639 /* ========================================================================== */
640 /* This mode does not need any special sequence => drop this complication */
641 /* and assume that both adim[] and pdim[] have natural grid indexing; */
642 
644 {
645  MgridInterpolation *inter = new MgridInterpolation(this);
646 
648 
649  if (inter->preCookMultiDimCommon(adim)) return NULL;
650 
651  // This is per definition for the interpolation mode;
652  for(int ik=0; ik<3; ik++)
653  inter->pdim[ik] = inter->adim[ik];
654 
655  if (inter->postCookMultiDimCommon()) return NULL;
656 
657  return inter;
658 } /* Mgrid::cookMultiDimInterpolation */
659 
660 /* ========================================================================== */
661 /* Of course pdim[] is expected in a natural XYZ (RFZ) sequence; */
662 /* !! NB: all 3 elements in adim[]/pdim[] expected; */
663 
665 {
666  MgridInterpolation *inter = new MgridInterpolation(this);
667 
668  inter->mode = _MULTI_DIM_FITTING_;
669 
670  if (inter->preCookMultiDimCommon(adim)) return NULL;
671 
672  for(int ik=0; ik<3; ik++)
673  {
674  // '>=': hmm, in fact may want '>' (part of directions
675  // fitting and part of directions interpolation); yes!;
676  if (pdim[ik] < 1 || pdim[ik] >/*=*/ inter->adim[ik]) return NULL;
677 
678  // pdim[] array is in interpolation sequence -> no remapping;
679  inter->pdim[ik] = pdim[ik];
680  } /*for*/
681 
682  if (inter->postCookMultiDimCommon()) return NULL;
683 
684  return inter;
685 } /* Mgrid::cookMultiDimFitting */
686 
687 /* ========================================================================== */
688 /* Common gateways to interpolation creation routines; */
689 
690 MgridInterpolation *Mgrid::cookInterpolation(unsigned mode, int adim[],
691  int pdim[], char sequence[])
692 {
693  switch (mode)
694  {
695  case _NO_INTERPOLATION_:
696  /* Here and below: unused parameters must be strictly NULL, */
697  /* in order to avoid misinterpretation; */
698  if (adim || pdim || sequence) return NULL;
699  return cookInterpolationOff();
701  if (pdim) return NULL;
702  return cookSequentialFastInterpolation(adim, sequence);
704  if (pdim) return NULL;
705  return cookSequentialHighInterpolation(adim, sequence);
707  return cookSequentialHighFitting(adim, pdim, sequence);
709  if (pdim || sequence) return NULL;
710  return cookMultiDimInterpolation(adim);
711  case _MULTI_DIM_FITTING_:
712  if (sequence) return NULL;
713  return cookMultiDimFitting(adim, pdim);
714  default: return NULL;
715  } /*switch*/
716 } /* Mgrid::cookInterpolation */
717 
718 /* ========================================================================== */
719 /* Calculate linear address in a grid cell array and return pointer; */
720 
722 {
723  // Sanity check on the limits first;
724  for(int ik=0; ik<3; ik++)
725  if (id[ik] < 0 || id[ik] >= dir[ik].dim) return -1;
726 
727  return id[_X_]*dir[_Y_].dim*dir[_Z_].dim + id[_Y_]*dir[_Z_].dim + id[_Z_];
728 } /* Mgrid::multiAddrToLinear */
729 
730 /* -------------------------------------------------------------------------- */
731 /* May use 'mgrid->dir[]' here because component->dir[] have the same 'dim' */
732 /* entries; */
733 
734 int Mgrid::linearAddrToMulti(int linear, int id[3])
735 {
736  // Sanity check;
737  if (linear < 0 || linear >= cell_num) return -1;
738 
739  // This way it will work for 2 and 3 dim case;
740  for(int ik=_Z_; ik>=_X_; ik--)
741  {
742  MgridDirection *pdir = dir + ik;
743 
744  id[ik] = linear % pdir->dim;
745  linear /= pdir->dim;
746  } /*for*/
747 
748  return 0;
749 } /* Mgrid::linearAddrToMulti */
750 
751 /* -------------------------------------------------------------------------- */
752 
754 {
755  if (linear < 0 || linear >= cell_num) return NULL;
756 
757  return (MgridCell *)(cells + linear*ram_cell_size);
758 } /* Mgrid::linearAddrToCellPtr */
759 
760 /* -------------------------------------------------------------------------- */
761 
763 {
764  int linear = multiAddrToLinear(id);
765 
766  if (linear == -1) return NULL;
767 
768  return (MgridCell *)(cells + linear*ram_cell_size);
769 } /* Mgrid::multiAddrToCellPtr */
770 
771 /* -------------------------------------------------------------------------- */
772 
773 int Mgrid::multiAddrToCoord(int id[], TVector3 &xx)
774 {
775  for(int ik=0; ik<3; ik++)
776  {
777  MgridDirection *pdir = dir + ik;
778 
779  // Sanity check, please; in fact it is not always needed; ignore;
780  if (id[ik] < 0 || id[ik] >= pdir->dim) return -1;
781 
782  xx[ik] = pdir->min + (id[ik] + 0.5)*pdir->step;
783  } /*for*/
784 
785  return 0;
786 } /* Mgrid::multiAddrToCoord */
787 
788 /* -------------------------------------------------------------------------- */
789 
790 int Mgrid::linearAddrToCoord(int linear, TVector3 &xx)
791 {
792  int id[3];
793 
794  if (linear < 0 || linear >= cell_num) return -1;
795 
796  // Now convert to 3dim indices;
797  if (linearAddrToMulti(linear, id)) return -1;
798 
799  // Not too much efficient, but suffices;
800  return multiAddrToCoord(id, xx);
801 } /* Mgrid::linearAddrToCoord */
802 
803 /* -------------------------------------------------------------------------- */
804 
805 int Mgrid::cellPtrToCoord(MgridCell *cell, TVector3 &xx)
806 {
807  int linear, offset = (char *)cell - (char*)cells;
808 
809  // Sanity check;
810  if (offset % ram_cell_size) return -1;
811  linear = offset / ram_cell_size;
812 
813  if (linear < 0 || linear >= cell_num) return -1;
814 
815  return linearAddrToCoord(linear, xx);
816 } /* Mgrid::cellPtrToCoord */
817 
818 /* -------------------------------------------------------------------------- */
819 
820 int Mgrid::coordToMultiAddr(TVector3 &xx, int id[])
821 {
822  int ret = 0;
823 
824  // Well, floor() can easily fail with a floating point exception
825  // in case if integer is out of range --> introduce a safe_floor() later;
826  //printf("%d: %f %f %f\n", (unsigned)mgrid, xx[0], xx[1], xx[2]); fflush(stdout);
827 
828  for(int ik=0; ik<3; ik++)
829  if (coord.fake[ik])
830  id[ik] = 0;
831  else
832  {
833  MgridDirection *pdir = dir + ik;
834 
835  // Well, introduce some safety against fpe, endlich;
836  if (xx[ik] < pdir->min)
837  // Yes, set to something "wrong";
838  id[ik] = -1;
839  else
840  {
841  if (xx[ik] > pdir->max)
842  // Also set to something "wrong";
843  id[ik] = pdir->dim;
844  else
845  id[ik] = (int)floor((xx[ik] - pdir->min)/pdir->step);
846  } /*if*/
847 
848  // Do not return immediately, since id[] array
849  // makes sense even in this case;
850  if (id[ik] < 0 || id[ik] >= pdir->dim) ret = -1;
851  } /*for*/
852 
853  return ret;
854 } /* Mgrid::coordToMultiAddr */
855 
856 /* -------------------------------------------------------------------------- */
857 
859 {
860  int id[3];
861 
862  // First calculate milti-dim indices;
863  if (coordToMultiAddr(xx, id)) return NULL;
864 
865  // And then return cell pointer (or NULL);
866  return multiAddrToCellPtr(id);
867 } /* Mgrid::coordToCellPtr */
868 
869 /* -------------------------------------------------------------------------- */
870 
872 {
873  int linear = multiAddrToLinear(id);
874 
875  if (linear == -1) return -1;
876 
877  // Then assign cell pointer and property;
878  qcell->cell = (MgridCell *)(cells + linear*ram_cell_size);
879  qcell->property = properties[linear];
880 
881  return 0;
882 } /* Mgrid::multiAddrToCombiCell */
883 
884 /* -------------------------------------------------------------------------- */
885 
886 int Mgrid::coordToCombiCell(TVector3 &xx, MgridCombiCell *qcell)
887 {
888  int id[3];
889 
890  /* First calculate milti-dim indices; */
891  if (coordToMultiAddr(xx, id)) return -1;
892 
893  return multiAddrToCombiCell(id, qcell);
894 } /* Mgrid::coordToCombiCell */
895 
896 /* -------------------------------------------------------------------------- */
897 
899 {
900  int id[3];
901  TVector3 xv(xx[0], xx[1], xx[2]);
902 
903  // First calculate milti-dim indices;
904  //if (coordToMultiAddr(xx, id)) return NULL;
905  if (coordToMultiAddr(xv, id)) return NULL;
906 
907  // And then return cell pointer (or NULL);
908  return multiAddrToCellPtr(id);
909 } /* Mgrid::coordToCellPtr */
910 
911 /* -------------------------------------------------------------------------- */
912 
913 int Mgrid::cellPtrToLinear(const MgridCell *cell) const
914 {
915  // This is BS of course (assume cell points to array member); FIXME: do better later;
916  //return cell - (MgridCell *)cells;
917  //const unsigned char *from = cells, *to = (const unsigned char*)cell;
918  //return (to - from)/ram_cell_size;
919  return ((const unsigned char*)cell - cells)/ram_cell_size;
920 } // Mgrid::cellPtrToLinear()
921 
922 /* -------------------------------------------------------------------------- */
923 /* Expects that fake coordinates are omitted in xx[] (but proper sequence */
924 /* is preserved); */
925 
927 {
928  // Well, need to adapt to TVector3 and debug at some point;
929  assert(0);
930 #ifdef _APR2014_
931  int id[3];
932  double bff[3], *ptr;
933 
934  // First expand to 3-dim coordinates; this routine is rarely used
935  // and should not be too efficient;
936  if (cexp)
937  {
938  assert(0);
939  //@@@ expand_to_global_coordinates(coord.coord_num, xx, bff, cexp);
940  ptr = bff;
941  }
942  else
943  ptr = xx;
944 
945  // Calculate milti-dim indices;
946  if (coordToMultiAddr(ptr, id)) return -1;
947 
948  // And then return linear address (or -1);
949  return multiAddrToLinear(id);
950 #endif
951 } /* Mgrid::compressedCoordToLinearAddr */
952 
953 /* ========================================================================== */
954 
955 int Mgrid::exportCore(FILE *fout, unsigned char contents)
956 {
957  int tail = suggestedPropertyArrayTail();
958  unsigned char *addr = cells;
959 
960  if (__type == _MGRID_HEAP_)
961  {
962  // Loop through all the children; recursion;
963  for(int ch=0; ch<child_num; ch++)
964  if (children[ch]->exportCore(fout, contents))
965  return -1;
966  }
967  else
968  {
969  // If more bits wanted, this should be changed;
970  if (contents >> 2) return -1;
971  // Store contents bits in the header;
972  object_contents_bits = contents;
973 
974  // Write out single mgrid header and cell data;
975  //if (fwrite(this, current_mgrid_header_length(), 1, fout) != 1) return -1;
976  if (fwrite(this, sizeof(MgridHeader), 1, fout) != 1) return -1;
977 
978  if (contents & _CELL_DATA_)
979  {
981  {
982  // What is stored in 'cells' is all useful --> dump as a whole;
983  if (fwrite(cells, ram_cell_size, cell_num, fout) != cell_num)
984  return -1;
985  }
986  else
987  // Cut out 'useless' part of cells stored in 'cells';
988  for(int ik=0; ik<cell_num; ik++)
989  {
990  if (fwrite(addr, useful_cell_size, 1, fout) != 1)
991  return -1;
992 
993  addr += ram_cell_size;
994  } /*for..if*/
995  } /*if*/
996 
997  // And eventually dump 'properties' as a whole;
998  if (contents & _CELL_PROPERTIES_)
999  {
1000  if (fwrite(properties, sizeof(char), cell_num, fout) != cell_num)
1001  return -1;
1002 
1003  // Perhaps few bytes more?;
1004  if (tail && fwrite(properties, sizeof(char), tail, fout) != tail)
1005  return -1;
1006  } /*if*/
1007  } /*if*/
1008 
1009  return 0;
1010 } /* Mgrid::exportCore */
1011 
1012 /* -------------------------------------------------------------------------- */
1013 
1014 int Mgrid::exportFieldMap(char *file_name, unsigned char contents)
1015 {
1016  // Check sizes of basic types;
1017  if (!basic_types_match()) return -1;
1018 
1019  FILE *fout = fopen(file_name, "w");
1020  if (!fout) return -1;
1021 
1022  // All the rest is done recursively;
1023  int ret = exportCore(fout, contents);
1024 
1025  fclose(fout);
1026 
1027  return ret;
1028 } /* Mgrid::exportFieldMap */
1029 
1030 /* ========================================================================== */
1031 /* Well, want property[] to occupy 8byte-even chunk of memory; this is of */
1032 /* interest at least for the shared memory application; */
1033 
1035 {
1036  int rest = cell_num % 8;
1037 
1038  if (!rest)
1039  return 0;
1040  else
1041  return 8 - rest;
1042 } /* Mgrid::suggestedPropertyArrayTail */
1043 
1044 /* ========================================================================== */
1045 /* Well, perhaps later put shift/theta/phi as parameters; for now mgrid */
1046 /* will be located in heap with it's present position parameters; */
1047 
1049 {
1050  if (heap->__type != _MGRID_HEAP_) return -1;
1051 
1052  heap->child_num++;
1053  heap->children = (Mgrid**)realloc(heap->children, heap->child_num*sizeof(Mgrid*));
1054  if (!heap->children) return -1;
1055 
1056  heap->children[heap->child_num-1] = this;
1057 
1058  return 0;
1059 } /* Mgrid::attachToHeap */
1060 
1061 /* ========================================================================== */
1062 
1064  {'X', _X_, _CARTESIAN_},
1065  {'Y', _Y_, _CARTESIAN_},
1066  {'Z', _Z_, _CARTESIAN_|_CYLINDRICAL_},
1067  {'R', _R_, _CYLINDRICAL_},
1068  {'F', _F_, _CYLINDRICAL_}};
1069 #define COORD_NAMES_NUM (sizeof(coord_names)/sizeof(coord_names[0]))
1070 
1071 t_coord_name *find_coord_by_name(unsigned char system_type, char name)
1072 {
1073  for(int ik=0; ik<COORD_NAMES_NUM; ik++)
1074  {
1075  t_coord_name *cname = coord_names + ik;
1076 
1077  if (name == cname->name)
1078  {
1079  // Check that this coordinate is allowed for this coordinate system
1080  // at all; if not, return -1;
1081  if (cname->allowed_systems & system_type)
1082  return cname;
1083  else
1084  return NULL;
1085  } /*if*/
1086  } /*for*/
1087 
1088  // No such coordinate name known;
1089  return NULL;
1090 } /* find_coord_by_name */
1091 
1092 /* ========================================================================== */
1093 /* Well, since t_mgrid contains only 64-bit words, cell data are doubles and */
1094 /* property array is characters with it's tail aligned at 8-byte boundary, */
1095 /* the only thing I have to check is that sizeof(double)=8; or probably */
1096 /* should I also check that sizeof(long)<=8?; */
1097 
1099 {
1100  if (sizeof(double) != 8) return 0;
1101 
1102  return 1;
1103 } /* basic_types_match */
1104 
1105 /* ========================================================================== */
1106 /* Recalculates shift/rotation flags and direct/reversed rotation matrices; */
1107 
1109 {
1110  int shift_flag = 0, rotation_flag = 0;
1111  TVector3 basis[3], buffer;
1112  double projection;
1113 
1114  // First check that shift/rotation are needed;
1115  for(int ik=0; ik<3; ik++)
1116  if (shift[ik])
1117  {
1118  shift_flag = 1;
1119  break;
1120  } /*if..for*/
1121 
1122  if (theta[0] != 90. || theta[1] != 90. || theta[2] != 0. ||
1123  /* phi[2] is of no importance if theta[2]=0.; */
1124  phi [0] != 0. || phi [1] != 90.)
1125  rotation_flag = 1;
1126 
1127  if (!shift_flag && !rotation_flag && position)
1128  {
1129  delete position;
1130  position = NULL;
1131  }
1132  else
1133  {
1134  if (!position)
1135  {
1136  position = new MgridPosition();
1137  memset(position, 0x00, sizeof(MgridPosition));
1138  } /*if*/
1139 
1140  if (shift_flag) position->shift_flag = 1;
1141 
1142  if (rotation_flag)
1143  {
1144  position->rotation_flag = 1;
1145 
1146  /* Calculate new basis vectors; */
1147  for(int ik=0; ik<3; ik++)
1148  {
1149  basis[ik][_X_] = sin(deg2rad(theta[ik]))*
1150  cos(deg2rad(phi[ik]));
1151  basis[ik][_Y_] = sin(deg2rad(theta[ik]))*
1152  sin(deg2rad(phi[ik]));
1153  basis[ik][_Z_] = cos(deg2rad(theta[ik]));
1154  } /*for*/
1155 
1156  // Check that these vectors are orthogonal and define
1157  // right-handed coordinate system; fix if needed;
1158  // '1': basis[0] is normalized (see creation);
1159  for(int ii=1; ii<3; ii++)
1160  {
1161  for(int ik=0; ik<ii; ik++)
1162  {
1163  projection = basis[ii].Dot(basis[ik]);//smul_v_v(basis[ii], basis[ik]);
1164 
1165  //mul_s_v(buffer, -projection, basis[ik]);
1166  //add_v_v(basis[ii], basis[ii], buffer);
1167  basis[ii] -= projection * basis[ik];
1168  } /*for*/
1169 
1170  basis[ii].SetMag(1.0);//normalize_v(basis[ii]);
1171  } /*for*/
1172 
1173  buffer = basis[0].Cross(basis[1]);//vmul_v_v(buffer, basis[0], basis[1]);
1174  // Well, this is a patology;
1175  //if (smul_v_v(buffer, basis[2]) < 0.)
1176  if (buffer.Dot(basis[2]) < 0.)//smul_v_v(buffer, basis[2]) < 0.)
1177  {
1178  printf("Transformation to a left-handed system!\n");
1179  return -1;
1180  } /*if*/
1181 
1182  // Need to incorporate TRotation::MakeBasis() later;
1183  assert(0);
1184 #if 0
1185  // And eventually calculate direct/reversed matrices;
1186  for(int ii=0; ii<3; ii++)
1187  for(int ik=0; ik<3; ik++)
1188  position->direct[ii][ik] =
1189  position->reversed[ik][ii] =
1190  basis[ii][ik];
1191 #endif
1192  } /*if*/
1193  } /*if*/
1194 
1195  return 0;
1196 } /* Mgrid::recalculatePosition */
1197 
1198 /* -------------------------------------------------------------------------- */
1199 /* Changes position of this object with respect to it's father; (just */
1200 /* assigns new values; if pointer is NULL, no change to this array; */
1201 
1202 int Mgrid::changePosition(double _shift[3], double _theta[3], double _phi[3])
1203 {
1204  if (_shift)
1205  for(int ik=0; ik<3; ik++)
1206  shift[ik] = _shift[ik];
1207 
1208  if (_theta)
1209  for(int ik=0; ik<3; ik++)
1210  theta[ik] = _theta[ik];
1211 
1212  if (_phi)
1213  for(int ik=0; ik<3; ik++)
1214  phi [ik] = _phi [ik];
1215 
1216  return recalculatePosition();
1217 } /* Mgrid::changePosition */
1218 
1219 /* ========================================================================== */
1220 /* The sequence is: */
1221 /* - find cell area around xx[] which will be used for interpolation */
1222 /* (algorithm depends on the interpolation mode); */
1223 /* - calculate interpolated (fitted) field value in a local grid system; */
1224 /* NB: this routine is expected to preserve xx[]; */
1225 
1226 int Mgrid::directFieldValue(TVector3 &xx, TVector3 &B)
1227 {
1228  int i012[3], seq[3], rev[3];
1229  // xxx[] is needed as intermediate array because this routine
1230  // is not allowed to change xx[];
1231  double xxx[3];
1232 
1233  // Will need to find self[] array anyway (cell to
1234  // which this xx[] belongs) --> do it now;
1235  if (findSelfCell(xx)) return -1;
1236 
1237  // The rest depends on the mode; loop through the field components;
1238  for(int cmp=0; cmp<field.coord_num; cmp++)
1239  {
1240  MgridInterpolation *inter = interpolation[cmp];
1241 
1242  // If interpolation is not associated with this mgrid, return immediately;
1243  if (!inter) _FAILURE_(this, _ZERO_INTERPOLATION_POINTER_);
1244 
1245  // Check that self cell may be used at all;
1248 
1249  if (inter->mode == _NO_INTERPOLATION_)
1250  {
1251  // inter->self_cell can not be _DEAD_CELL_ because
1252  // this bit is masked out for this mode => go ahead;
1253  B[cmp] = self_qcell.cell->B[cmp];
1254  }
1255  else
1256  if (inter->mode == _SEQUENTIAL_FAST_INTERPOLATION_ ||
1258  inter->mode == _SEQUENTIAL_HIGH_FITTING_ )
1259  {
1260  //printf("Here!\n"); exit(0);
1261  // These modes require strict cubic areas;
1262  // 'repetition_flag=1' means inter->left[] array was
1263  // assigned by hand already;
1264  if (inter->checkNeighbouringCubicArea(xx, cmp))
1265  return -1;
1266 
1267  for(int ik=0; ik<3; ik++)
1268  {
1269  seq[ik] = inter->sequence[ik];
1270  rev[ik] = inter->reversed[ik];
1271 
1272  // Move point to the leftmost position (expansion is per
1273  // definition done with respect to the leftmost corner);
1274  // another option would be to keep xx[], but choose a
1275  // proper starting point in coord[] array;
1276  xxx[ik] = xx[ik];
1277  if (inter->left[ik]) xxx[ik] -= inter->left[ik]*dir[ik].step;
1278  } /*for*/
1279 
1280  if (inter->mode == _SEQUENTIAL_FAST_INTERPOLATION_)
1281  {
1282  for(i012[2]=0; i012[2]<inter->adim[seq[2]]; i012[2]++)
1283  {
1284  for(i012[1]=0; i012[1]<inter->adim[seq[1]]; i012[1]++)
1285  {
1286  // Fill out array of values along inter->sequence[0];
1287  for(i012[0]=0; i012[0]<inter->adim[seq[0]]; i012[0]++)
1288  inter->v[0][i012[0]] = inter->cube[i012[rev[_X_]]*inter->adim[_Y_]*inter->adim[_Z_] +
1289  i012[rev[_Y_]]*inter->adim[_Z_] + i012[rev[_Z_]]]->B[cmp];
1290  // Interpolation along inter->sequence[0];
1291  inter->v[1][i012[1]] = inter->fun[0](xxx[seq[0]], cell_center_coord[seq[0]],
1292  inter->v[0]);
1293  } /*for*/
1294 
1295  // Interpolation along inter->sequence[1];
1296  inter->v[2][i012[2]] = inter->fun[1](xxx[seq[1]], cell_center_coord[seq[1]],
1297  inter->v[1]);
1298  } /*for*/
1299 
1300  // Interpolation along inter->sequence[2];
1301  B[cmp] = inter->fun[2](xxx[seq[2]], cell_center_coord[seq[2]],
1302  inter->v[2]);
1303  }
1304  else
1305  {
1306  // Yes, for now just hardcode this check; perhaps this has something
1307  // to do with weight usage; if weights are of no interest, it seems
1308  // one can remove this restiction; think later;
1309 #if _BACK_
1310  assert(coord.coord_num == 3 && field.coord_num == 1);
1311 #endif
1312 
1313  ThreeDeePolySpace *space0 = inter->hspace[0];
1314  ThreeDeePolySpace *space1 = inter->hspace[1];
1315  ThreeDeePolySpace *space2 = inter->hspace[2];
1316 
1317  for(i012[2]=0; i012[2]<inter->adim[seq[2]]; i012[2]++)
1318  {
1319  ThreeDeePolyPoint *point2 = space2->points + i012[2];
1320 
1321  for(i012[1]=0; i012[1]<inter->adim[seq[1]]; i012[1]++)
1322  {
1323  ThreeDeePolyPoint *point1 = space1->points + i012[1];
1324 
1325  // Fill out array of values along inter->sequence[0];
1326  for(i012[0]=0; i012[0]<inter->adim[seq[0]]; i012[0]++)
1327  {
1328  MgridCell *cell = inter->cube[i012[rev[_X_]]*inter->adim[_Y_]*inter->adim[_Z_] +
1329  i012[rev[_Y_]]*inter->adim[_Z_] + i012[rev[_Z_]]];
1330  ThreeDeePolyPoint *point0 = space0->points + i012[0];
1331 
1332  point0->f = cell->B[cmp];
1333 
1334  // If point weights should be taken into account, do it now;
1335  if (inter->force_1dim_weight_usage)
1336  point0->weight = 1./SQR(cell->B[cell_1derr_shift + cmp*coord.coord_num + 0]);
1337  }
1338 
1339  // Rebuild basis polynomial vectors (since weights changed);
1340  space0->buildOrthogonalPolynomials();
1341  // Interpolation along inter->sequence[0];
1342  space0->calculateFittingPolynomial(inter->hfit[0]);
1343 
1344  point1->f = inter->hfit[0]->value(xxx);
1345 
1346  if (inter->force_1dim_weight_usage)
1347  point1->weight = 1./(SQR(space0->getNaivePolyFitError(xxx)) +
1348  // Use self cell for now; may want to do interpolation later;
1350  } /*for*/
1351 
1352  // Rebuild basis polynomial vectors (since weights changed);
1353  space1->buildOrthogonalPolynomials();
1354  // Interpolation along inter->sequence[1];
1355  space1->calculateFittingPolynomial(inter->hfit[1]);
1356 
1357  point2->f = inter->hfit[1]->value(xxx);
1358 
1359  if (inter->force_1dim_weight_usage)
1360  point2->weight = 1./(SQR(space1->getNaivePolyFitError(xxx)) +
1361  // Use self cell for now; may want to do interpolation later;
1363  } /*for*/
1364 
1365  // Rebuild basis polynomial vectors (since weights changed);
1366  space2->buildOrthogonalPolynomials();
1367  // Interpolation along inter->sequence[2];
1368  space2->calculateFittingPolynomial(inter->hfit[2]);
1369  //B[cmp] = poly_value(space2, inter->hfit[2], xxx);
1370  B[cmp] = inter->hfit[2]->value(xxx);
1371 
1372  if (inter->fit_error_wanted)
1373  inter->ferr = space2->getNaivePolyFitError(xxx);
1374  } /*if*/
1375  }
1376  else
1377  if (inter->mode == _MULTI_DIM_INTERPOLATION_ ||
1378  inter->mode == _MULTI_DIM_FITTING_)
1379  {
1380  // For now repetition mode works for simple cases only;
1381  assert(!repetition_flag);
1382 
1383  // Prefer cubic area (faster!), but may work with (almost)
1384  // any collection of points;
1385  inter->checkNeighbouringCubicArea(xx, cmp);
1386 
1387  // This is indeed a redundant reassignment for the non-regular case;
1388  // however this allows to unify few more lines of code (see after
1389  // the below if{} statement; and anyway irregular case is so
1390  // CPU-intensive that few reassignments do not contribute much;
1391  for(int ik=0; ik<3; ik++)
1392  xxx[ik] = xx[ik];
1393 
1394  if (inter->cell_counter == inter->cube_cell_num && !inter->force_irregular_case)
1395  {
1396  // Need to shift xx[] to the leftmost corner of the grid because
1397  // polynomial basis was built in post_cook_multi_dim_common() for
1398  // the cube shifted to this corner;
1399  for(int ik=0; ik<3; ik++)
1400  if (inter->left[ik])
1401  xxx[ik] -= inter->left[ik]*dir[ik].step;
1402 
1403  inter->actual = inter->gspace;
1404  }
1405  else
1406  {
1407  // Ok, no luck with the ideal cube --> append as many more neighboring
1408  // points as needed;
1410 
1411  if (inter->appendArbitraryNeighbouringCells(cmp)) return -1;
1412 
1413  // Unfortunately need to fill in all the coordinates of all cells
1414  // and (even worse) to rebuild basis;
1415  if (inter->prepairIrregularCase(cmp)) return -1;
1416 
1417  inter->actual = inter->irregular;
1418  } /*if*/
1419 
1420  // To this point inter->actual is the pointer to the actually used
1421  // polynomial frame; now may produce 'best polynomial' and take
1422  // it's value at xxx[]; first assign function values;
1423  for(int ip=0; ip<inter->cube_cell_num; ip++)
1424  inter->actual->points[ip].f = inter->cube[ip]->B[cmp];
1425 
1426  // This place was not checked after conversion to C++;
1427  inter->actual->calculateFittingPolynomial(inter->gfit);
1428  B[cmp] = inter->gfit->value(xxx);
1429 
1430  if (inter->gradient_wanted)
1431  {
1432  inter->gfit->calculateGradient(inter->grad);
1433  for(int ik=0; ik<3; ik++)
1434  inter->gvalue[ik] = inter->grad[ik]->value(xxx);
1435  } /*if*/
1436  } /*if*/
1437  } /*for*/
1438 
1439  //printf("@W@ %f %f %f\n", B[0], B[1], B[2]);
1440 
1441 
1442  // Now B[] contains fit result, but in the (probably even 2-dim)
1443  // local coordinate system of the grid; need to convert it to
1444  // the 3-dim value;
1445  if (fexp) {
1446  assert(0);
1447  //@@@ expand_to_global_coordinates(field.coord_num, B, B, fexp);
1448  } //if
1449 
1450  return 0;
1451 } /* Mgrid::directFieldValue */
1452 
1453 /* -------------------------------------------------------------------------- */
1454 /* * - accept input 3dim coordinates; */
1455 /* * - shift/rotate input 3dim coordinates appropriately and do coordinate */
1456 /* system type transformation if needed; */
1457 /* * - if single mgrid, call mgrid_field_value(), otherwise propagate one */
1458 /* level down recursively; */
1459 /* * - rescale if needed; */
1460 /* * - expand this value to 3dim; */
1461 /* * - rotate field value back and apply coordinate system type */
1462 /* transformation if needed; */
1463 
1465 {
1466  int ret = -1;
1467  // '=' per default just reuse 'X' pointer (save CPU time);
1468  t_3d_cs_vector XL, *XP = X;
1469  // Save for further usage;
1470  unsigned char wanted_field_coord_system_type = D->system_type;
1471 
1472  last_field_status = 0;
1473 
1475 
1476  // Check _CYLINDRICAL_/_CARTESIAN_ match, fix if needed; apply shift/rotation
1477  // if needed; needed for both HEAP/SINGLE cases; 'XL': preserve 'X' if it
1478  // should be modified;
1479  if (position || X->system_type != coord.system_type)
1480  {
1481  // Well, X should be modified => create a local
1482  // (modified) copy, and switch pointer;
1483  XL = convertInputCoordinates(X);
1484  XP = &XL;
1485  } /*if*/
1486 
1487  // Ok, now X is transformed to the object native coordinates =>
1488  // proceed according to whether it's a collection of grids or
1489  // a single grid;
1490  if (__type == _MGRID_HEAP_)
1491  {
1492  switch (field_calculation_method)
1493  {
1494  case _SUPERPOSITION_:
1495  {
1496  // Well, in fact in case of HEAP field.system_type
1497  // does not make too much sense (as well as coord.system_type);
1498  // so one could put D->system_type in DL directly; but code becomes
1499  // even less readable in this case => sacrifice few CPU cycles for the
1500  // case when D->system_type != field.system_type (HEAP);
1502 
1503  for(int ik=0; ik<3; ik++)
1504  D->xx[ik] = 0.;
1505 
1506  for(int ch=0; ch<child_num; ch++)
1507  {
1508  Mgrid *child = children[ch];
1509 
1510  // Call the same routine recursively;
1511  ret = child->getFieldValue(XP, &DL);
1512 
1513  // Whatever happens, HEAP status for this method
1514  // will be a logical OR of member mgrids status words;
1516 
1517  if (ret) break;
1518 
1519  for(int ik=0; ik<3; ik++)
1520  D->xx[ik] += DL.xx[ik];
1521  } /*for*/
1522  }
1523  break;
1524  case _FIRST_MATCH_:
1525  for(int ch=0; ch<child_num; ch++)
1526  {
1527  Mgrid *child = children[ch];
1528 
1529  // Call the same routine recursively;
1530  // this strange assignment is done because in all cases
1531  // I want D->xx[] to contain field in mgrid field
1532  // coordinate system; this is faster than using DL[] and
1533  // copy DL --> D in a separate loop;
1535  ret = child->getFieldValue(XP, D);
1536  // For this method (_FIRST_MATCH_) HEAP status word
1537  // is the (success) word of the first match or (if
1538  // no successful mgrid found) a logical OR of all
1539  // failed children mgrids;
1540  if (ret)
1542  else
1543  {
1545  break;
1546  } /*if*/
1547  } /*for*/
1548  break;
1549  default:
1550  ret = -1;
1551  } /*switch*/
1552  }
1553  else
1554  // Calculate single grid interpolated field value;
1555  // NB: convert_input_coordinates() already happened in
1556  // the very beginning of get_field_value() routine;
1557  ret = directFieldValue(XP->xx, D->xx);
1558 
1559  // In case of success few more actions;
1560  if (!ret)
1561  {
1562  // Well, perhaps need to scale; NB: all 3 components!;
1563  // for a single few-component mgrid it would be more CPU-saving to
1564  // do rescaling before expanding to global coordinates, but prefer
1565  // to unify HEAP/SINGLE codes as much as possible;
1566  if (scaling_factor != 1.)
1567  for(int ik=0; ik<3; ik++)
1568  D->xx[ik] *= scaling_factor;
1569 
1570  // At this point field is already 3dim and rescaled in all
1571  // cases; in all cases it is mgrid field coordinate system -->
1572  // still coordinate type transformation may be needed; and
1573  // probably rotation as well; in trivial cases (read: usually)
1574  // this is not needed;
1575  if (field.system_type != wanted_field_coord_system_type ||
1577  convertOutputField(XP, D, wanted_field_coord_system_type);
1578  }
1579  else
1580  // Reset field for clarity in case of ret=-1;
1581  for(int ik=0; ik<3; ik++)
1582  D->xx[ik] = 0.;
1583 
1584  return ret;
1585 } /* Mgrid::getFieldValue */
1586 
1587 /* -------------------------------------------------------------------------- */
1588 /* Be pedantic, since both cartesian/cylindrical cases wanted, make common */
1589 /* part separate; only system usage --> no 'system_type' check; */
1590 
1591 int Mgrid::getFixedTypeFieldValue(unsigned char system_type, TVector3 &xx, TVector3 &B)
1592 {
1593  t_3d_cs_vector X = {system_type, TVector3(xx[0], xx[1], xx[2])};
1594  t_3d_cs_vector D = {system_type};
1595 
1596  int ret = getFieldValue(&X, &D);
1597 
1598  for(int ik=0; ik<3; ik++)
1599  B[ik] = D.xx[ik];
1600 
1601  return ret;
1602 } /* Mgrid::getFixedTypeFieldValue */
1603 
1604 /* -------------------------------------------------------------------------- */
1605 /* The most popular case --> separate routine; CPU overhead?; */
1606 
1607 int Mgrid::getCartesianFieldValue(TVector3 &xx, TVector3 &B)
1608 {
1609  return getFixedTypeFieldValue(_CARTESIAN_, xx, B);
1610 } /* Mgrid::getCartesianFieldValue */
1611 
1612 /* -------------------------------------------------------------------------- */
1613 
1614 int Mgrid::getCylindricalFieldValue(TVector3 &xx, TVector3 &B)
1615 {
1616  return getFixedTypeFieldValue(_CYLINDRICAL_, xx, B);
1617 } /* Mgrid::getCylindricalFieldValue */
1618 
1619 /* ========================================================================== */
1620 /* Well, structure copy does not take too much compared to FP operations; */
1621 /* and there is no need to check for the source/destination pointer overhead; */
1622 /* I mean this function does not care about possible overlap :-) */
1623 
1624 //
1625 // -> Ok, do not mind to keep there 2 routines as external functions;
1626 //
1627 
1629  unsigned char desired_coord_system)
1630 {
1631  double angle_in_radians;
1632  t_3d_cs_vector XL;
1633 
1634  // Sanity check, perhaps there is nothing to do;
1635  if (X->system_type == desired_coord_system) return *X;
1636 
1637  // For now only 2 system types are in used, consider both cases;
1638  if (X->system_type == _CARTESIAN_ && desired_coord_system == _CYLINDRICAL_)
1639  {
1640  XL.xx[_R_] = sqrt(SQR(X->xx[_X_])+SQR(X->xx[_Y_]));
1641  XL.xx[_F_] = X->xx[_X_] ? rad2deg(atan(X->xx[_Y_]/X->xx[_X_])) : (X->xx[_Y_] > 0. ? 90. : -90.);
1642  if (X->xx[_X_] < 0.) XL.xx[_F_] += 180.;
1643  }
1644  else
1645  if (X->system_type == _CYLINDRICAL_ && desired_coord_system == _CARTESIAN_)
1646  {
1647  angle_in_radians = deg2rad(X->xx[_F_]);
1648  XL.xx[_X_] = X->xx[_R_]*cos(angle_in_radians);
1649  XL.xx[_Y_] = X->xx[_R_]*sin(angle_in_radians);
1650  }
1651  else
1652  // No more options known; terminate;
1653  assert(0);
1654 
1655  // Z-component remains the same;
1656  XL.xx[_Z_] = X->xx[_Z_];
1657 
1658  // And eventually change system type to a new one;
1659  XL.system_type = desired_coord_system;
1660 
1661  return XL;
1662 } /* transform_coord_type */
1663 
1664 /* -------------------------------------------------------------------------- */
1665 /* Is suitable only for CAR/CYL case; if other systems will be needed in the */
1666 /* future, will have to rework completely; */
1667 
1668 static void transform_field_type(double mtx[2][2], t_3d_cs_vector *D,
1669  unsigned char desired_coord_system)
1670 {
1671  TVector3 bff;
1672 
1673  // Sanity check;
1674  if (D->system_type == desired_coord_system) return;
1675 
1676  // '2': well, Z-component will stay the same;
1677  for(int ik=0; ik<2; ik++)
1678  {
1679  bff[ik] = D->xx[ik];
1680  D->xx[ik] = 0.;
1681  } /*for*/
1682 
1683  // For now only 2 system types are in use, consider both cases;
1684  if (D->system_type == _CARTESIAN_ && desired_coord_system == _CYLINDRICAL_)
1685  {
1686 
1687  }
1688  else
1689  if (D->system_type == _CYLINDRICAL_ && desired_coord_system == _CARTESIAN_)
1690  {
1691  mtx[1][0] = -mtx[1][0];
1692  mtx[0][1] = -mtx[0][1];
1693  }
1694  else
1695  // No more options known; terminate;
1696  assert(0);
1697 
1698  // Eventually apply 2dim rotation;
1699  for(int ii=0; ii<2; ii++)
1700  for(int ik=0; ik<2; ik++)
1701  D->xx[ii] += mtx[ii][ik]*bff[ik];
1702 
1703  // And eventually change system type to a new one;
1704  D->system_type = desired_coord_system;
1705 } /* transform_field_type */
1706 
1707 /* -------------------------------------------------------------------------- */
1708 /* Converts output B[] field to the coordinate system type of the calling */
1709 /* object; also applies reversed rotation if needed; */
1710 
1712  unsigned char wanted_field_coord_system_type)
1713 {
1714  // I'm not allowed to change X in this routine;
1715  t_3d_cs_vector BFF;
1716  double angle_in_radians;
1717  double mtx[2][2];
1718 
1719  // Will need to call transform_field_type() twice => calculate
1720  // rotation matrix once and pass as a parameter; if spherical case
1721  // is ever needed, this part should be reworked completely;
1722  if (X->system_type != _CYLINDRICAL_)
1723  {
1725  // And forget about original X;
1726  X = &BFF;
1727  } /*if*/
1728  // Calculate 2dim rotation matrix corresponding to the X'phi;
1729  angle_in_radians = deg2rad(X->xx[_F_]);
1730  mtx[0][0] = mtx[1][1] = cos(angle_in_radians);
1731  mtx[0][1] = sin(angle_in_radians);
1732  mtx[1][0] = -mtx[0][1];
1733 
1734  // Do this strange reassignment because transform_coord_type() expects
1735  // consistent xx[] and system_type in 'D'; in fact to this moment
1736  // D->system_type was REALLY field.system_type (since it has just
1737  // been out of mfile_field_value() in mgrid's coordinates);
1739 
1740  // Check coordinate type match; do transformation to the calling
1741  // object coordinate system if necessary;
1742  if (D->system_type == _CYLINDRICAL_ &&
1743  ((position && position->rotation_flag) ||
1744  wanted_field_coord_system_type == _CARTESIAN_))
1746 
1747  // Now if rotation needed, it's Ok, since
1748  // D->system_type is _CARTESIAN_ => rotate now if needed;
1750  // This has never been checked after conversion to TRotation;
1751  //mul_a_v(D->xx, position->reversed, D->xx);
1752  D->xx = position->reversed * D->xx;
1753 
1754  // If to this point there is a coordinate type mismatch, do one more
1755  // transformation;
1756  if (D->system_type != wanted_field_coord_system_type)
1757  transform_field_type(mtx, D, wanted_field_coord_system_type);
1758 } /* Mgrid::convertOutputField */
1759 
1760 /* -------------------------------------------------------------------------- */
1761 /* Converts input xx[] coordinates provided by caller to the native type and */
1762 /* also applies shift/rotation if needed; the code is not well optimised, but */
1763 /* just straightforward; */
1764 
1766 {
1767  t_3d_cs_vector XL;
1768 
1769  // Check coordinate type match; do transformation to the object
1770  // coordinate system if necessary;
1771  if (X->system_type == _CYLINDRICAL_ &&
1774  else
1775  XL = *X;
1776 
1777  // Now if shift/rotation needed, it's Ok, since
1778  // XL->system_type is _CARTESIAN_ => shift and rotate if needed;
1779  if (position)
1780  {
1781  if (position->shift_flag)
1782  // 'sub': reversed transformation from this object point of view!;
1783  for(int iqq=0; iqq<3; iqq++)
1784  XL.xx[iqq] -= shift[iqq];
1785 
1786  if (position->rotation_flag)
1787  // This has never been checked after conversion to TRotation;
1788  //mul_a_v(XL.xx, position->direct, XL.xx);
1789  XL.xx = position->direct * XL.xx;
1790  } /*if*/
1791 
1792  /* If to this point there is a coordinate type mismatch, do one more */
1793  /* transformation; so it can happen that CYL->CAR->CYL is done since */
1794  /* shift/rotation are of course defined for _CARTESIAN_ type; */
1795  if (XL.system_type != coord.system_type)
1796  return transform_coord_type(&XL, coord.system_type);
1797  else
1798  return XL;
1799 } /* Mgrid::convertInputCoordinates */
1800 
1801 /* ========================================================================== */