MNE-CPP  0.1.9
A Framework for Electrophysiology
fwd_eeg_sphere_model.cpp
Go to the documentation of this file.
1 //=============================================================================================================
37 //=============================================================================================================
38 // INCLUDES
39 //=============================================================================================================
40 
42 
43 #include "fwd_eeg_sphere_model.h"
45 
46 #include <QtAlgorithms>
47 
48 #include <qmath.h>
49 
50 #include <Eigen/Core>
51 #include <Eigen/Dense>
52 
53 //=============================================================================================================
54 // USED NAMESPACES
55 //=============================================================================================================
56 
57 using namespace Eigen;
58 using namespace UTILSLIB;
59 using namespace FWDLIB;
60 
61 #define MIN_1(a,b) ((a) < (b) ? (a) : (b))
62 
63 //ToDo Remove after refactoring
64 #define X_1 0
65 #define Y_1 1
66 #define Z_1 2
67 /*
68  * Dot product and length
69  */
70 #define VEC_DOT_1(x,y) ((x)[X_1]*(y)[X_1] + (x)[Y_1]*(y)[Y_1] + (x)[Z_1]*(y)[Z_1])
71 #define VEC_LEN_1(x) sqrt(VEC_DOT_1(x,x))
72 /*
73  * Others...
74  */
75 #define VEC_DIFF_1(from,to,diff) {\
76  (diff)[X_1] = (to)[X_1] - (from)[X_1];\
77  (diff)[Y_1] = (to)[Y_1] - (from)[Y_1];\
78  (diff)[Z_1] = (to)[Z_1] - (from)[Z_1];\
79  }
80 
81 #define VEC_COPY_1(to,from) {\
82  (to)[X_1] = (from)[X_1];\
83  (to)[Y_1] = (from)[Y_1];\
84  (to)[Z_1] = (from)[Z_1];\
85  }
86 
87 #define CROSS_PRODUCT_1(x,y,xy) {\
88  (xy)[X_1] = (x)[Y_1]*(y)[Z_1]-(y)[Y_1]*(x)[Z_1];\
89  (xy)[Y_1] = -((x)[X_1]*(y)[Z_1]-(y)[X_1]*(x)[Z_1]);\
90  (xy)[Z_1] = (x)[X_1]*(y)[Y_1]-(y)[X_1]*(x)[Y_1];\
91  }
92 
93 #define MALLOC_1(x,t) (t *)malloc((x)*sizeof(t))
94 
95 #define REALLOC_1(x,y,t) (t *)((x == NULL) ? malloc((y)*sizeof(t)) : realloc((x),(y)*sizeof(t)))
96 
97 #define FREE(x) if ((char *)(x) != NULL) free((char *)(x))
98 
99 #define ALLOC_DCMATRIX_1(x,y) mne_dmatrix_1((x),(y))
100 #define FREE_DCMATRIX_1(m) mne_free_dcmatrix_1((m))
101 
102 #define ALLOC_CMATRIX_1(x,y) mne_cmatrix_1((x),(y))
103 
104 /*
105  * float matrices
106  */
107 #define FREE_CMATRIX_1(m) mne_free_cmatrix_1((m))
108 
109 #ifndef TRUE
110 #define TRUE 1
111 #endif
112 
113 #ifndef FALSE
114 #define FALSE 0
115 #endif
116 
117 #ifndef FAIL
118 #define FAIL -1
119 #endif
120 
121 #ifndef OK
122 #define OK 0
123 #endif
124 
125 static void matrix_error_1(int kind, int nr, int nc)
126 
127 {
128  if (kind == 1)
129  printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
130  else if (kind == 2)
131  printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
132  else
133  printf("Allocation error for a %d x %d matrix\n",nr,nc);
134  if (sizeof(void *) == 4) {
135  printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
136  printf("Please consider moving to a 64-bit platform.");
137  }
138  printf("Cannot continue. Sorry.\n");
139  exit(1);
140 }
141 
142 float **mne_cmatrix_1(int nr,int nc)
143 
144 {
145  int i;
146  float **m;
147  float *whole;
148 
149  m = MALLOC_1(nr,float *);
150  if (!m) matrix_error_1(1,nr,nc);
151  whole = MALLOC_1(nr*nc,float);
152  if (!whole) matrix_error_1(2,nr,nc);
153 
154  for(i=0;i<nr;i++)
155  m[i] = whole + i*nc;
156  return m;
157 }
158 
159 double **mne_dmatrix_1(int nr, int nc)
160 
161 {
162  int i;
163  double **m;
164  double *whole;
165 
166  m = MALLOC_1(nr,double *);
167  if (!m) matrix_error_1(1,nr,nc);
168  whole = MALLOC_1(nr*nc,double);
169  if (!whole) matrix_error_1(2,nr,nc);
170 
171  for(i=0;i<nr;i++)
172  m[i] = whole + i*nc;
173  return m;
174 }
175 
176 void mne_free_cmatrix_1 (float **m)
177 {
178  if (m) {
179  free(*m);
180  free(m);
181  }
182 }
183 
184 void mne_free_dcmatrix_1 (double **m)
185 
186 {
187  if (m) {
188  FREE(*m);
189  FREE(m);
190  }
191 }
192 
193 #define MAXTERMS 1000
194 #define EPS 1e-10
195 #define SIN_EPS 1e-3
196 
197 static int terms = 0; /* These statistics may be useful */
198 static int eval = 0;
199 
200 //=============================================================================================================
201 // DEFINE MEMBER METHODS
202 //=============================================================================================================
203 
204 FwdEegSphereModel::FwdEegSphereModel()
205 : fn(NULL)
206 , nterms (0)
207 , lambda (NULL)
208 , mu (NULL)
209 , nfit (0)
210 , scale_pos (0)
211 {
212  r0[0] = 0.0;
213  r0[1] = 0.0;
214  r0[2] = 0.0;
215 }
216 
217 //=============================================================================================================
218 
220 {
221  int k;
222 
223  if (!p_FwdEegSphereModel.name.isEmpty())
224  this->name = p_FwdEegSphereModel.name;
225  if (p_FwdEegSphereModel.nlayer() > 0) {
226  for (k = 0; k < p_FwdEegSphereModel.nlayer(); k++)
227  this->layers.append(p_FwdEegSphereModel.layers[k]);
228  }
229  VEC_COPY_1(this->r0,p_FwdEegSphereModel.r0);
230  if (p_FwdEegSphereModel.nterms > 0) {
231  this->fn = VectorXd(p_FwdEegSphereModel.nterms);
232  this->nterms = p_FwdEegSphereModel.nterms;
233  for (k = 0; k < p_FwdEegSphereModel.nterms; k++)
234  this->fn[k] = p_FwdEegSphereModel.fn[k];
235  }
236  if (p_FwdEegSphereModel.nfit > 0) {
237  this->mu = VectorXf(p_FwdEegSphereModel.nfit);
238  this->lambda = VectorXf(p_FwdEegSphereModel.nfit);
239  this->nfit = p_FwdEegSphereModel.nfit;
240  for (k = 0; k < p_FwdEegSphereModel.nfit; k++) {
241  this->mu[k] = p_FwdEegSphereModel.mu[k];
242  this->lambda[k] = p_FwdEegSphereModel.lambda[k];
243  }
244  }
245  this->scale_pos = p_FwdEegSphereModel.scale_pos;
246 }
247 
248 //=============================================================================================================
249 
250 FwdEegSphereModel* FwdEegSphereModel::fwd_create_eeg_sphere_model(const QString& name,
251  int nlayer,
252  const VectorXf& rads,
253  const VectorXf& sigmas)
254 /*
255  * Produce a new sphere model structure
256  */
257 {
258  FwdEegSphereModel* new_model = new FwdEegSphereModel();
259 
260  new_model->name = name;
261 
262  for (int k = 0; k < nlayer; k++) {
263  FwdEegSphereLayer layer;
264  layer.rad = layer.rel_rad = rads[k];
265  layer.sigma = sigmas[k];
266  new_model->layers.append(layer);
267  }
268  /*
269  * Sort...
270  */
271  std::sort(new_model->layers.begin(), new_model->layers.end(), FwdEegSphereLayer::comp_layers);
272 
273  /*
274  * Scale the radiuses
275  */
276  float R = new_model->layers[nlayer-1].rad;
277  float rR = new_model->layers[nlayer-1].rel_rad;
278  for (int k = 0; k < nlayer; k++) {
279  new_model->layers[k].rad = new_model->layers[k].rad/R;
280  new_model->layers[k].rel_rad = new_model->layers[k].rel_rad/rR;
281  }
282  return new_model;
283 }
284 
285 //=============================================================================================================
286 
288 {
289 }
290 
291 //=============================================================================================================
292 
293 FwdEegSphereModel* FwdEegSphereModel::setup_eeg_sphere_model(const QString& eeg_model_file, QString eeg_model_name, float eeg_sphere_rad)
294 {
295  FwdEegSphereModelSet* eeg_models = NULL;
296  FwdEegSphereModel* eeg_model = NULL;
297 
298  if (eeg_model_name.isEmpty())
299  eeg_model_name = QString("Default");
300 
301  eeg_models = FwdEegSphereModelSet::fwd_load_eeg_sphere_models(eeg_model_file,NULL);
302  eeg_models->fwd_list_eeg_sphere_models(stderr);
303 
304  if ((eeg_model = eeg_models->fwd_select_eeg_sphere_model(eeg_model_name)) == NULL)
305  goto bad;
306 
307  if (!eeg_model->fwd_setup_eeg_sphere_model(eeg_sphere_rad,true,3))
308  goto bad;
309  printf("Using EEG sphere model \"%s\" with scalp radius %7.1f mm\n",
310  eeg_model->name.toUtf8().constData(),1000*eeg_sphere_rad);
311  printf("\n");
312  delete eeg_models;
313  return eeg_model;
314 
315 bad : {
316  delete eeg_models;
317  delete eeg_model;
318  return NULL;
319  }
320 }
321 
322 //=============================================================================================================
323 
324 fitUser FwdEegSphereModel::new_fit_user(int nfit, int nterms)
325 
326 {
327  fitUser u = MALLOC_1(1,fitUserRec);
328  u->y = MALLOC_1(nterms-1,double);
329  u->resi = MALLOC_1(nterms-1,double);
330  u->M = ALLOC_DCMATRIX_1(nterms-1,nfit-1);
331  u->uu = ALLOC_DCMATRIX_1(nfit-1,nterms-1);
332  u->vv = ALLOC_DCMATRIX_1(nfit-1,nfit-1);
333  u->sing = MALLOC_1(nfit,double);
334  u->fn = MALLOC_1(nterms,double);
335  u->w = MALLOC_1(nterms,double);
336  u->nfit = nfit;
337  u->nterms = nterms;
338  return u;
339 }
340 
341 //=============================================================================================================
342 // fwd_multi_spherepot.c
344 {
345  MatrixXd M,Mn,help,Mm;
346  static MatrixXd mat1;
347  static MatrixXd mat2;
348  static MatrixXd mat3;
349  static VectorXd c1;
350  static VectorXd c2;
351  static VectorXd cr;
352  static VectorXd cr_mult;
353  double div,div_mult;
354  double n1;
355 #ifdef TEST
356  double rel1,rel2;
357  double b,c;
358 #endif
359  int k;
360 
361  if (this->nlayer() == 0 || this->nlayer() == 1)
362  return 1.0;
363  /*
364  * Now follows the tricky case
365  */
366 #ifdef TEST
367  if (this->nlayer() == 2) {
368  rel1 = layers[0].sigma/layers[1].sigma;
369  n1 = n + 1.0;
370  div_mult = 2.0*n + 1;
371  b = pow(this->layers[0].rel_rad,div_mult);
372  return div_mult/((n1 + n*rel1) + b*n1*(rel1-1.0));
373  }
374  else if (this->nlayer() == 3) {
375  rel1 = this->layers[0].sigma/this->layers[1].sigma;
376  rel2 = this->layers[1].sigma/this->layers[2].sigma;
377  n1 = n + 1.0;
378  div_mult = 2.0*n + 1.0;
379  b = pow(this->layers[0].rel_rad,div_mult);
380  c = pow(this->layers[1].rel_rad,div_mult);
381  div_mult = div_mult*div_mult;
382  div = (b*n*n1*(rel1-1.0)*(rel2-1.0) + c*(rel1*n + n1)*(rel2*n + n1))/c +
383  n1*(b*(rel1-1.0)*(rel2*n1 + n) + c*(rel1*n + n1)*(rel2-1.0));
384  return div_mult/div;
385  }
386 #endif
387  if (n == 1) {
388  /*
389  * Initialize the arrays
390  */
391  c1.resize(this->nlayer()-1);
392  c2.resize(this->nlayer()-1);
393  cr.resize(this->nlayer()-1);
394  cr_mult.resize(this->nlayer()-1);
395  for (k = 0; k < this->nlayer()-1; k++) {
396  c1[k] = this->layers[k].sigma/this->layers[k+1].sigma;
397  c2[k] = c1[k] - 1.0;
398  cr_mult[k] = this->layers[k].rel_rad;
399  cr[k] = cr_mult[k];
400  cr_mult[k] = cr_mult[k]*cr_mult[k];
401  }
402  if (mat1.cols() == 0)
403  mat1 = MatrixXd(2,2);
404  if (mat2.cols() == 0)
405  mat2 = MatrixXd(2,2);
406  if (mat3.cols() == 0)
407  mat3 = MatrixXd(2,2);
408  }
409  /*
410  * Increment the radius coefficients
411  */
412  for (k = 0; k < this->nlayer()-1; k++)
413  cr[k] = cr[k]*cr_mult[k];
414  /*
415  * Multiply the matrices
416  */
417  M = mat1;
418  Mn = mat2;
419  Mm = mat3;
420  M(0,0) = M(1,1) = 1.0;
421  M(0,1) = M(1,0) = 0.0;
422  div = 1.0;
423  div_mult = 2.0*n + 1.0;
424  n1 = n + 1.0;
425 
426  for (k = this->nlayer()-2; k >= 0; k--) {
427 
428  Mm(0,0) = (n + n1*c1[k]);
429  Mm(0,1) = n1*c2[k]/cr[k];
430  Mm(1,0) = n*c2[k]*cr[k];
431  Mm(1,1) = n1 + n*c1[k];
432 
433  Mn(0,0) = Mm(0,0)*M(0,0) + Mm(0,1)*M(1,0);
434  Mn(0,1) = Mm(0,0)*M(0,1) + Mm(0,1)*M(1,1);
435  Mn(1,0) = Mm(1,0)*M(0,0) + Mm(1,1)*M(1,0);
436  Mn(1,1) = Mm(1,0)*M(0,1) + Mm(1,1)*M(1,1);
437  help = M;
438  M = Mn;
439  Mn = help;
440  div = div*div_mult;
441 
442  }
443  return n*div/(n*M(1,1) + n1*M(1,0));
444 }
445 
446 //=============================================================================================================
447 // fwd_multi_spherepot.c
448 void FwdEegSphereModel::next_legen(int n, double x, double *p0, double *p01, double *p1, double *p11) /* Input: P1(n-2) Output: P1(n-1) */
449 /*
450  * Compute the next Legendre polynomials of the
451  * first and second kind using the recursion formulas.
452  *
453  * The routine initializes automatically with the known values
454  * when n = 1
455  */
456 {
457  double help0,help1;
458 
459  if (n > 1) {
460  help0 = *p0;
461  help1 = *p1;
462  *p0 = ((2*n-1)*x*help0 - (n-1)*(*p01))/n;
463  *p1 = ((2*n-1)*x*help1 - n*(*p11))/(n-1);
464  *p01 = help0;
465  *p11 = help1;
466  }
467  else if (n == 0) {
468  *p0 = 1.0;
469  *p1 = 0.0;
470  }
471  else if (n == 1) {
472  *p01 = 1.0;
473  *p0 = x;
474  *p11 = 0.0;
475  *p1 = sqrt(1.0-x*x);
476  }
477  return;
478 }
479 
480 //=============================================================================================================
481 
482 void FwdEegSphereModel::calc_pot_components(double beta, double cgamma, double *Vrp, double *Vtp, const Eigen::VectorXd& fn, int nterms)
483 {
484  double Vt = 0.0;
485  double Vr = 0.0;
486  double p0,p01,p1,p11;
487  double betan,multn;
488  int n;
489 
490  betan = 1.0;
491  p0 = p01 = p1 = p11 = 0.0;
492  for (n = 1; n <= nterms; n++) {
493  if (betan < EPS) {
494  terms = terms + n;
495  eval = eval + 1;
496  break;
497  }
498  next_legen (n,cgamma,&p0,&p01,&p1,&p11);
499  multn = betan*fn[n-1]; /* The 2*n + 1 factor is included in fn */
500  Vr = Vr + multn*p0;
501  Vt = Vt + multn*p1/n;
502  betan = beta*betan;
503  }
504  *Vrp = Vr;
505  *Vtp = Vt;
506  return;
507 }
508 
509 //=============================================================================================================
510 // fwd_multi_spherepot.c
511 int FwdEegSphereModel::fwd_eeg_multi_spherepot(float *rd, float *Q, float **el, int neeg, float *Vval, void *client) /* The model definition */
512 /*
513  * Compute the electric potentials in a set of electrodes in spherically
514  * Symmetric head model.
515  *
516  * The code is based on the formulas presented in
517  *
518  * Z. Zhang, A fast method to compute surface potentials
519  * generated by dipoles within multilayer anisotropic spheres,
520  * Phys. Med. Biol., 40, 335 - 349, 1995.
521  *
522  * and
523  *
524  * J.C. Moscher, R.M. Leahy, and P.S. Lewis, Matrix Kernels for
525  * Modeling of EEG and MEG Data, Los Alamos Technical Report,
526  * LA-UR-96-1993, 1996.
527  *
528  * This version does not use the acceleration with help of equivalent sources
529  * in the homogeneous model
530  *
531  */
532 {
533  FwdEegSphereModel* m = (FwdEegSphereModel*)client;
534  float my_rd[3],pos[3];
535  int k,p;
536  float pos2,rd_len,pos_len;
537  double beta,cos_gamma,Vr,Vt;
538  float vec1[3],vec2[3],v1,v2;
539  float cos_beta,Qr,Qt,Q2,c;
540  float pi4_inv = 0.25/M_PI;
541  float sigmaM_inv;
542  /*
543  * Precompute the coefficients
544  */
545  if (m->fn.size() == 0 || m->nterms != MAXTERMS) {
546  m->fn.resize(MAXTERMS);
547  m->nterms = MAXTERMS;
548  for (k = 0; k < MAXTERMS; k++)
549  m->fn[k] = (2*k+3)*m->fwd_eeg_get_multi_sphere_model_coeff(k+1);
550  }
551  /*
552  * Move to the sphere coordinates
553  */
554  for (p = 0; p < 3; p++)
555  my_rd[p] = rd[p] - m->r0[p];
556  rd = my_rd;
557  rd_len = VEC_LEN_1(rd);
558  Q2 = VEC_DOT_1(Q,Q);
559  /*
560  * Ignore dipoles outside the innermost sphere
561  */
562  if (rd_len >= m->layers[0].rad) {
563  for (k = 0; k < neeg; k++)
564  Vval[k] = 0.0;
565  return OK;
566  }
567  /*
568  * Special case: rd and Q are parallel
569  */
570  c = VEC_DOT_1(rd,Q)/(rd_len*sqrt(Q2));
571  if ((1.0-c*c) < SIN_EPS) { /* Almost parallel:
572  * Q is purely radial */
573  Qr = sqrt(Q2);
574  Qt = 0.0;
575  cos_beta = 0.0;
576  v1 = 0.0;
577  vec1[0] = vec1[1] = vec1[2] = 0.0;
578  }
579  else {
580  CROSS_PRODUCT_1(rd,Q,vec1);
581  v1 = VEC_LEN_1(vec1);
582  cos_beta = 0.0;
583  Qr = Qt = 0.0;
584  }
585  for (k = 0; k < neeg; k++) {
586  for (p = 0; p < 3; p++)
587  pos[p] = el[k][p] - m->r0[p];
588  /*
589  * Should the position be scaled or not?
590  */
591  if (m->scale_pos) {
592  pos_len = m->layers[m->nlayer()-1].rad/VEC_LEN_1(pos);
593 #ifdef DEBUG
594  printf("%10.4f %10.4f %10.4f %10.4f\n",pos_len,1000*pos[0],1000*pos[1],1000*pos[2]);
595 #endif
596  for (p = 0; p < 3; p++)
597  pos[p] = pos_len*pos[p];
598  }
599  pos2 = VEC_DOT_1(pos,pos);
600  pos_len = sqrt(pos2);
601  /*
602  * Calculate the two ingredients for the final result
603  */
604  cos_gamma = VEC_DOT_1(pos,rd)/(rd_len*pos_len);
605  beta = rd_len/pos_len;
606  calc_pot_components(beta,cos_gamma,&Vr,&Vt,m->fn,m->nterms);
607  /*
608  * Then compute the combined result
609  */
610  if (v1 > 0.0) {
611  CROSS_PRODUCT_1(rd,pos,vec2);
612  v2 = VEC_LEN_1(vec2);
613 
614  if (v2 > 0.0)
615  cos_beta = VEC_DOT_1(vec1,vec2)/(v1*v2);
616  else
617  cos_beta = 0.0;
618 
619  Qr = VEC_DOT_1(Q,rd)/rd_len;
620  Qt = sqrt(Q2 - Qr*Qr);
621  }
622  Vval[k] = pi4_inv*(Qr*Vr + Qt*cos_beta*Vt)/pos2;
623  }
624  /*
625  * Scale by the conductivity if we have the layers
626  * defined
627  */
628  if (m->nlayer() > 0) {
629  sigmaM_inv = 1.0/m->layers[m->nlayer()-1].sigma;
630  for (k = 0; k < neeg; k++)
631  Vval[k] = Vval[k]*sigmaM_inv;
632  }
633  return OK;
634 }
635 
636 //=============================================================================================================
637 // fwd_multi_spherepot.c
638 int FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client) /* Client data will be the sphere model definition */
639 /*
640  * Calculate the EEG in the sphere model using the fwdCoilSet structure
641  *
642  * This version does not use the acceleration with help of equivalent sources
643  * in the homogeneous model
644  *
645  */
646 {
647  float *vval_one = NULL,val;
648  int nvval = 0;
649  int k,c;
650  FwdCoil* el;
651 
652  for (k = 0; k < els->ncoil; k++, el++) {
653  el = els->coils[k];
654  if (el->coil_class == FWD_COILC_EEG) {
655  if (el->np > nvval) {
656  vval_one = REALLOC_1(vval_one,el->np,float);
657  nvval = el->np;
658  }
659  if (fwd_eeg_multi_spherepot(rd,Q,el->rmag,el->np,vval_one,client) != OK) {
660  FREE(vval_one);
661  return FAIL;
662  }
663  for (c = 0, val = 0.0; c < el->np; c++)
664  val += el->w[c]*vval_one[c];
665  *Vval = val;
666  }
667  Vval++;
668  }
669  FREE(vval_one);
670  return OK;
671 }
672 
673 //=============================================================================================================
674 // fwd_multi_spherepot.c
675 bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int neeg, float **Vval_vec, void *client)
676 {
677  FwdEegSphereModel* m = (FwdEegSphereModel*)client;
678  float fact = 0.25f/(float)M_PI;
679  float a_vec[3];
680  float a,a2,a3;
681  float rrd,rd2,rd2_inv,r,r2,ra,rda;
682  float F;
683  float c1,c2,m1,m2;
684  int k,p,eq;
685  float *this_pos;
686  float orig_rd[3],scaled_rd[3];
687  float pos[3],pos_len;
688  /*
689  * Shift to the sphere model coordinates
690  */
691  for (p = 0; p < 3; p++)
692  orig_rd[p] = rd[p] - m->r0[p];
693  rd = scaled_rd;
694  /*
695  * Initialize the arrays
696  */
697  for (k = 0 ; k < neeg ; k++) {
698  Vval_vec[X_1][k] = 0.0;
699  Vval_vec[Y_1][k] = 0.0;
700  Vval_vec[Z_1][k] = 0.0;
701  }
702  /*
703  * Ignore dipoles outside the innermost sphere
704  */
705  if (VEC_LEN_1(orig_rd) >= m->layers[0].rad)
706  return true;
707  /*
708  * Default to homogeneous model if no model was previously set
709  */
710 #ifdef FOO
711  if (nequiv == 0) /* what to do */
712  eeg_set_homog_sphere_model();
713 #endif
714  /*
715  * Make a weighted sum over the equivalence parameters
716  */
717  for (eq = 0; eq < m->nfit; eq++) {
718  /*
719  * Scale the dipole position
720  */
721  for (p = 0; p < 3; p++)
722  rd[p] = m->mu[eq]*orig_rd[p];
723 
724  rd2 = VEC_DOT_1(rd,rd);
725  rd2_inv = 1.0/rd2;
726 
727  /*
728  * Go over all electrodes
729  */
730  for (k = 0; k < neeg ; k++) {
731  this_pos = el[k];
732 
733  for (p = 0; p < 3; p++)
734  pos[p] = this_pos[p] - m->r0[p];
735  /*
736  * Scale location onto the surface of the sphere
737  */
738  if (m->scale_pos) {
739  pos_len = m->layers[m->nlayer()-1].rad/VEC_LEN_1(pos);
740  for (p = 0; p < 3; p++)
741  pos[p] = pos_len*pos[p];
742  }
743  this_pos = pos;
744 
745  /* Vector from dipole to the field point */
746 
747  VEC_DIFF_1 (rd,this_pos,a_vec);
748 
749  /* Compute the dot products needed */
750 
751  a2 = VEC_DOT_1(a_vec,a_vec); a = sqrt(a2);
752  a3 = 2.0/(a2*a);
753  r2 = VEC_DOT_1(this_pos,this_pos); r = sqrt(r2);
754  rrd = VEC_DOT_1(this_pos,rd);
755  ra = r2 - rrd;
756  rda = rrd - rd2;
757 
758  /* The main ingredients */
759 
760  F = a*(r*a + ra);
761  c1 = a3*rda + 1.0/a - 1.0/r;
762  c2 = a3 + (a+r)/(r*F);
763 
764  /* Mix them together and scale by lambda/(rd*rd) */
765 
766  m1 = (c1 - c2*rrd);
767  m2 = c2*rd2;
768 
769  Vval_vec[X_1][k] = Vval_vec[X_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[X_1] + m2*this_pos[X_1]);
770  Vval_vec[Y_1][k] = Vval_vec[Y_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[Y_1] + m2*this_pos[Y_1]);
771  Vval_vec[Z_1][k] = Vval_vec[Z_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[Z_1] + m2*this_pos[Z_1]);
772  } /* All electrodes done */
773  } /* All equivalent dipoles done */
774  /*
775  * Finish by scaling by 1/(4*M_PI);
776  */
777  for (k = 0; k < neeg; k++) {
778  Vval_vec[X_1][k] = fact*Vval_vec[X_1][k];
779  Vval_vec[Y_1][k] = fact*Vval_vec[Y_1][k];
780  Vval_vec[Z_1][k] = fact*Vval_vec[Z_1][k];
781  }
782  return true;
783 }
784 
785 //=============================================================================================================
786 // fwd_multi_spherepot.c
787 int FwdEegSphereModel::fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet* els, float **Vval_vec, void *client)
788 {
789  float **vval_one = NULL;
790  float val;
791  int nvval = 0;
792  int k,c,p;
793  FwdCoil* el;
794 
795  for (k = 0; k < els->ncoil; k++, el++) {
796  el = els->coils[k];
797  if (el->coil_class == FWD_COILC_EEG) {
798  if (el->np > nvval) {
799  FREE_CMATRIX_1(vval_one);
800  vval_one = ALLOC_CMATRIX_1(3,el->np);
801  nvval = el->np;
802  }
803  if (!fwd_eeg_spherepot_vec(rd,el->rmag,el->np,vval_one,client)) {
804  FREE_CMATRIX_1(vval_one);
805  return FAIL;
806  }
807  for (p = 0; p < 3; p++) {
808  for (c = 0, val = 0.0; c < el->np; c++)
809  val += el->w[c]*vval_one[p][c];
810  Vval_vec[p][k] = val;
811  }
812  }
813  }
814  FREE_CMATRIX_1(vval_one);
815  return OK;
816 }
817 
818 //=============================================================================================================
819 
820 int FwdEegSphereModel::fwd_eeg_spherepot_grad_coil(float *rd, float Q[], FwdCoilSet *coils, float Vval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */
821 /*
822  * Quick and dirty solution: use differences
823  *
824  * This routine uses the acceleration with help of equivalent sources
825  * in the homogeneous sphere.
826  *
827  */
828 {
829  float my_rd[3];
830  float step = 0.0005;
831  float step2 = 2*step;
832  float *grads[3];
833  int p,q;
834 
835  grads[0] = xgrad;
836  grads[1] = ygrad;
837  grads[2] = zgrad;
838 
839  for (p = 0; p < 3; p++) {
840  VEC_COPY_1(my_rd,rd);
841  my_rd[p] = my_rd[p] + step;
842  if (fwd_eeg_spherepot_coil(my_rd,Q,coils,grads[p],client) == FAIL)
843  return FAIL;
844  VEC_COPY_1(my_rd,rd);
845  my_rd[p] = my_rd[p] - step;
846  if (fwd_eeg_spherepot_coil(my_rd,Q,coils,Vval,client) == FAIL)
847  return FAIL;
848  for (q = 0; q < coils->ncoil; q++)
849  grads[p][q] = (grads[p][q]-Vval[q])/step2;
850  }
851  if (Vval) {
852  if (fwd_eeg_spherepot_coil(rd,Q,coils,Vval,client) == FAIL)
853  return FAIL;
854  }
855  return OK;
856 }
857 
858 //=============================================================================================================
859 // fwd_multi_spherepot.c
860 int FwdEegSphereModel::fwd_eeg_spherepot( float *rd, /* Dipole position */
861  float *Q, /* Dipole moment */
862  float **el, /* Electrode positions */
863  int neeg, /* Number of electrodes */
864  VectorXf& Vval, /* The potential values */
865  void *client)
866 /*
867  * This routine calculates the potentials for a specific dipole direction
868  *
869  * This routine uses the acceleration with help of equivalent sources
870  * in the homogeneous sphere.
871  */
872 {
873  FwdEegSphereModel* m = (FwdEegSphereModel*)client;
874  float fact = 0.25f/M_PI;
875  float a_vec[3];
876  float a,a2,a3;
877  float rrd,rd2,rd2_inv,r,r2,ra,rda;
878  float F;
879  float c1,c2,m1,m2,f1,f2;
880  int k,p,eq;
881  float *this_pos;
882  float orig_rd[3],scaled_rd[3];
883  float pos[3],pos_len;
884  /*
885  * Shift to the sphere model coordinates
886  */
887  for (p = 0; p < 3; p++)
888  orig_rd[p] = rd[p] - m->r0[p];
889  rd = scaled_rd;
890  /*
891  * Initialize the arrays
892  */
893  for (k = 0 ; k < neeg ; k++)
894  Vval[k] = 0.0;
895  /*
896  * Ignore dipoles outside the innermost sphere
897  */
898  if (VEC_LEN_1(orig_rd) >= m->layers[0].rad)
899  return true;
900  /*
901  * Default to homogeneous model if no model was previously set
902  */
903 #ifdef FOO
904  if (nequiv == 0) /* what to do */
905  eeg_set_homog_sphere_model();
906 #endif
907  /*
908  * Make a weighted sum over the equivalence parameters
909  */
910  for (eq = 0; eq < m->nfit; eq++) {
911  /*
912  * Scale the dipole position
913  */
914  for (p = 0; p < 3; p++)
915  rd[p] = m->mu[eq]*orig_rd[p];
916 
917  rd2 = VEC_DOT_1(rd,rd);
918  rd2_inv = 1.0/rd2;
919 
920  f1 = VEC_DOT_1(rd,Q);
921  /*
922  * Go over all electrodes
923  */
924  for (k = 0; k < neeg ; k++) {
925  this_pos = el[k];
926 
927  for (p = 0; p < 3; p++)
928  pos[p] = this_pos[p] - m->r0[p];
929  /*
930  * Scale location onto the surface of the sphere
931  */
932  if (m->scale_pos) {
933  pos_len = m->layers[m->nlayer()-1].rad/VEC_LEN_1(pos);
934  for (p = 0; p < 3; p++)
935  pos[p] = pos_len*pos[p];
936  }
937  this_pos = pos;
938 
939  /* Vector from dipole to the field point */
940 
941  VEC_DIFF_1 (rd,this_pos,a_vec);
942 
943  /* Compute the dot products needed */
944 
945  a2 = VEC_DOT_1(a_vec,a_vec); a = sqrt(a2);
946  a3 = 2.0/(a2*a);
947  r2 = VEC_DOT_1(this_pos,this_pos); r = sqrt(r2);
948  rrd = VEC_DOT_1(this_pos,rd);
949  ra = r2 - rrd;
950  rda = rrd - rd2;
951 
952  /* The main ingredients */
953 
954  F = a*(r*a + ra);
955  c1 = a3*rda + 1.0/a - 1.0/r;
956  c2 = a3 + (a+r)/(r*F);
957 
958  /* Mix them together and scale by lambda/(rd*rd) */
959 
960  m1 = (c1 - c2*rrd);
961  m2 = c2*rd2;
962 
963  f2 = VEC_DOT_1(this_pos,Q);
964  Vval[k] = Vval[k] + m->lambda[eq]*rd2_inv*(m1*f1 + m2*f2);
965  } /* All electrodes done */
966  } /* All equivalent dipoles done */
967  /*
968  * Finish by scaling by 1/(4*M_PI);
969  */
970  for (k = 0; k < neeg; k++)
971  Vval[k] = fact*Vval[k];
972  return OK;
973 }
974 
975 //=============================================================================================================
976 // fwd_multi_spherepot.c
977 int FwdEegSphereModel::fwd_eeg_spherepot_coil( float *rd, float *Q, FwdCoilSet* els, float *Vval, void *client)
978 {
979  VectorXf vval_one;
980  float val;
981  int nvval = 0;
982  int k,c;
983  FwdCoil* el;
984 
985  for (k = 0; k < els->ncoil; k++, el++) {
986  el = els->coils[k];
987  if (el->coil_class == FWD_COILC_EEG) {
988  if (el->np > nvval) {
989  vval_one.resize(el->np);
990  nvval = el->np;
991  }
992  if (fwd_eeg_spherepot(rd,Q,el->rmag,el->np,vval_one,client) != OK) {
993  return FAIL;
994  }
995  for (c = 0, val = 0.0; c < el->np; c++)
996  val += el->w[c]*vval_one[c];
997  *Vval = val;
998  }
999  Vval++;
1000  }
1001  return OK;
1002 }
1003 
1004 //=============================================================================================================
1005 // fwd_eeg_sphere_models.c
1006 bool FwdEegSphereModel::fwd_setup_eeg_sphere_model(float rad, bool fit_berg_scherg, int nfit)
1007 {
1008  int nterms = 200;
1009  float rv;
1010 
1011  /*
1012  * Scale the relative radiuses
1013  */
1014  for (int k = 0; k < this->nlayer(); k++)
1015  this->layers[k].rad = rad*this->layers[k].rel_rad;
1016 
1017  if (fit_berg_scherg) {
1018  if (this->fwd_eeg_fit_berg_scherg(nterms,nfit,rv)) {
1019  printf("Equiv. model fitting -> ");
1020  printf("RV = %g %%\n",100*rv);
1021  for (int k = 0; k < nfit; k++)
1022  printf("mu%d = %g\tlambda%d = %g\n", k+1,this->mu[k],k+1,this->layers[this->nlayer()-1].sigma*this->lambda[k]);
1023  }
1024  else
1025  return false;
1026  }
1027 
1028  printf("Defined EEG sphere model with rad = %7.2f mm\n", 1000.0*rad);
1029  return true;
1030 }
1031 
1032 Eigen::MatrixXd toDoubleEigenMatrix(double **mat, const int m, const int n)
1033 {
1034  Eigen::MatrixXd eigen_mat(m,n);
1035 
1036  for ( int i = 0; i < m; ++i)
1037  for ( int j = 0; j < n; ++j)
1038  eigen_mat(i,j) = mat[i][j];
1039 
1040  return eigen_mat;
1041 }
1042 
1043 void fromDoubleEigenMatrix(const Eigen::MatrixXd& from_mat, double **to_mat, const int m, const int n)
1044 {
1045  for ( int i = 0; i < m; ++i)
1046  for ( int j = 0; j < n; ++j)
1047  to_mat[i][j] = from_mat(i,j);
1048 }
1049 
1050 void fromDoubleEigenMatrix(const Eigen::MatrixXd& from_mat, double **to_mat)
1051 {
1052  fromDoubleEigenMatrix(from_mat, to_mat, from_mat.rows(), from_mat.cols());
1053 }
1054 
1055 void fromDoubleEigenVector(const Eigen::VectorXd& from_vec, double *to_vec, const int n)
1056 {
1057  for ( int i = 0; i < n; ++i)
1058  to_vec[i] = from_vec[i];
1059 }
1060 
1061 void fromDoubleEigenVector(const Eigen::VectorXd& from_vec, double *to_vec)
1062 {
1063  fromDoubleEigenVector(from_vec, to_vec, from_vec.size());
1064 }
1065 
1066 //============================= fwd_fit_berg_scherg.c
1067 static double dot_dvectors (double *v1,
1068  double *v2,
1069  int nn)
1070 {
1071  double result = 0.0;
1072  int k;
1073 
1074  for (k = 0; k < nn; k++)
1075  result = result + v1[k]*v2[k];
1076  return (result);
1077 }
1078 //============================= fwd_fit_berg_scherg.c
1079 static int c_dsvd(double **mat, /* The matrix */
1080  int m,int n, /* m rows n columns */
1081  double *sing, /* Singular values (must have size
1082  * MIN(m,n)+1 */
1083  double **uu, /* Left eigenvectors */
1084  double **vv) /* Right eigenvectors */
1085 /*
1086  * Compute the SVD of mat.
1087  * The singular vector calculations depend on whether
1088  * or not u and v are given.
1089  * The allocations should be done as follows
1090  *
1091  * mat = ALLOC_DCMATRIX(m,n);
1092  * vv = ALLOC_DCMATRIX(MIN(m,n),n);
1093  * uu = ALLOC_DCMATRIX(MIN(m,n),m);
1094  * sing = MALLOC(MIN(m,n),double);
1095  *
1096  * mat is modified by this operation
1097  *
1098  * This simply allocates the workspace and calls the
1099  * LAPACK Fortran routine
1100  */
1101 {
1102  int udim = MIN_1(m,n);
1103 
1104  Eigen::MatrixXd eigen_mat = toDoubleEigenMatrix(mat, m, n);
1105 
1106  //ToDo Optimize computation depending of whether uu or vv are defined
1107  Eigen::JacobiSVD< Eigen::MatrixXd > svd(eigen_mat,Eigen::ComputeFullU | Eigen::ComputeFullV);
1108 
1109  fromDoubleEigenVector(svd.singularValues(), sing, svd.singularValues().size());
1110 
1111  if ( uu != NULL )
1112  fromDoubleEigenMatrix(svd.matrixU().transpose(), uu, udim, m);
1113 
1114  if ( vv != NULL )
1115  fromDoubleEigenMatrix(svd.matrixV().transpose(), vv, n, n);
1116 
1117  return 0;
1118  // return info;
1119 }
1120 
1121 /*
1122  * Include the simplex and SVD code here.
1123  * It is not too much of a problem
1124  */
1125 
1126 //============================= fwd_fit_berg_scherg.c
1127 
1128 //============================= fwd_fit_berg_scherg.c
1129 namespace FWDLIB
1130 {
1131 
1132 typedef struct {
1133  double lambda; /* Magnitude for the apparent dipole */
1134  double mu; /* Distance multiplier for the apparent dipole */
1136 
1137 } // Namepsace
1138 
1139 //============================= fwd_fit_berg_scherg.c
1140 static int comp_pars(const void *p1,const void *p2)
1141 /*
1142  * Comparison function for sorting layers
1143  */
1144 {
1145  bergSchergPar v1 = (bergSchergPar)p1;
1146  bergSchergPar v2 = (bergSchergPar)p2;
1147 
1148  if (v1->mu > v2->mu)
1149  return -1;
1150  else if (v1->mu < v2->mu)
1151  return 1;
1152  else
1153  return 0;
1154 }
1155 
1156 //============================= fwd_fit_berg_scherg.c
1157 static void sort_parameters(VectorXd& mu,VectorXd& lambda,int nfit)
1158 /*
1159  * Sort the parameters so that largest mu comes first
1160  */
1161 {
1162  bergSchergPar pars = MALLOC_1(nfit,bergSchergParRec);
1163 
1164  for (int k = 0; k < nfit; k++) {
1165  pars[k].mu = mu[k];
1166  pars[k].lambda = lambda[k];
1167  }
1168 
1169  qsort (pars, nfit, sizeof(bergSchergParRec), comp_pars);
1170 
1171  for (int k = 0; k < nfit; k++) {
1172  mu[k] = pars[k].mu;
1173  lambda[k] = pars[k].lambda;
1174  }
1175 
1176  FREE(pars);
1177 }
1178 
1179 //============================= fwd_fit_berg_scherg.c
1180 static bool report_fit(int loop,
1181  const VectorXd &fitpar,
1182  double Smin)
1183 
1184 /*
1185  * Report our progress
1186  */
1187 {
1188 #ifdef LOG_FIT
1189  for (int k = 0; k < fitpar.size(); k++)
1190  printf("%g ",mu[k]);
1191  printf("%g\n",Smin);
1192 #endif
1193  return true;
1194 }
1195 
1196 //============================= fwd_fit_berg_scherg.c
1197 static MatrixXd get_initial_simplex(const VectorXd &pars,
1198  double simplex_size)
1199 
1200 {
1201  int npar = pars.size();
1202 
1203  MatrixXd simplex = MatrixXd::Zero(npar+1,npar);
1204 
1205  simplex.rowwise() += pars.transpose();
1206 
1207  for (int k = 1; k < npar+1; k++)
1208  simplex(k,k-1) += simplex_size;
1209 
1210  return simplex;
1211 }
1212 
1213 void FwdEegSphereModel::compose_linear_fitting_data(const VectorXd& mu,fitUser u)
1214 {
1215  double mu1n,k1;
1216  int k,p;
1217  /*
1218  * y is the data to be fitted (nterms-1 x 1)
1219  * M is the model matrix (nterms-1 x nfit-1)
1220  */
1221  for (k = 0; k < u->nterms-1; k++) {
1222  k1 = k + 1;
1223  mu1n = pow(mu[0],k1);
1224  u->y[k] = u->w[k]*(u->fn[k+1] - mu1n*u->fn[0]);
1225  for (p = 0; p < u->nfit-1; p++)
1226  u->M[k][p] = u->w[k]*(pow(mu[p+1],k1)-mu1n);
1227  }
1228 }
1229 
1230 // fwd_fit_berg_scherg.c
1231 double FwdEegSphereModel::compute_linear_parameters(const VectorXd& mu,
1232  VectorXd& lambda,
1233  fitUser u)
1234 /*
1235  * Compute the best-fitting linear parameters
1236  * Return the corresponding RV
1237  */
1238 {
1239  int k,p,q;
1240  VectorXd vec(u->nfit-1);
1241  double sum;
1242 
1243  compose_linear_fitting_data(mu,u);
1244 
1245  c_dsvd(u->M,u->nterms-1,u->nfit-1,u->sing,u->uu,u->vv);
1246  /*
1247  * Compute the residuals
1248  */
1249  for (k = 0; k < u->nterms-1; k++)
1250  u->resi[k] = u->y[k];
1251 
1252  for (p = 0; p < u->nfit-1; p++) {
1253  vec[p] = dot_dvectors(u->uu[p],u->y,u->nterms-1);
1254  for (k = 0; k < u->nterms-1; k++)
1255  u->resi[k] = u->resi[k] - u->uu[p][k]*vec[p];
1256  vec[p] = vec[p]/u->sing[p];
1257  }
1258 
1259  for (p = 0; p < u->nfit-1; p++) {
1260  for (q = 0, sum = 0.0; q < u->nfit-1; q++)
1261  sum += u->vv[q][p]*vec[q];
1262  lambda[p+1] = sum;
1263  }
1264  for (p = 1, sum = 0.0; p < u->nfit; p++)
1265  sum += lambda[p];
1266  lambda[0] = u->fn[0] - sum;
1267  return dot_dvectors(u->resi,u->resi,u->nterms-1)/dot_dvectors(u->y,u->y,u->nterms-1);
1268 }
1269 
1270 // fwd_fit_berg_scherg.c
1271 double FwdEegSphereModel::one_step (const VectorXd& mu, const void *user_data)
1272 /*
1273  * Evaluate the residual sum of squares fit for one set of
1274  * mu values
1275  */
1276 {
1277  int k,p;
1278  double dot;
1279  fitUser u = (fitUser)user_data;
1280 
1281  for (k = 0; k < u->nfit; k++) {
1282  if (std::fabs(mu[k]) > 1.0)
1283  return 1.0;
1284  }
1285  /*
1286  * Compose the data for the linear fitting
1287  */
1288  compose_linear_fitting_data(mu,u);
1289  /*
1290  * Compute SVD
1291  */
1292  c_dsvd(u->M,u->nterms-1,u->nfit-1,u->sing,u->uu,NULL);
1293  /*
1294  * Compute the residuals
1295  */
1296  for (k = 0; k < u->nterms-1; k++)
1297  u->resi[k] = u->y[k];
1298  for (p = 0; p < u->nfit-1; p++) {
1299  dot = dot_dvectors(u->uu[p],u->y,u->nterms-1);
1300  for (k = 0; k < u->nterms-1; k++)
1301  u->resi[k] = u->resi[k] - u->uu[p][k]*dot;
1302  }
1303  /*
1304  * Return their sum of squares
1305  */
1306  return dot_dvectors(u->resi,u->resi,u->nterms-1);
1307 }
1308 
1309 // fwd_fit_berg_scherg.c
1310 bool FwdEegSphereModel::fwd_eeg_fit_berg_scherg(int nterms, /* Number of terms to use in the series expansion
1311  * when fitting the parameters */
1312  int nfit, /* Number of equivalent dipoles to fit */
1313  float &rv)
1314 /*
1315  * This routine fits the Berg-Scherg equivalent spherical model
1316  * dipole parameters by minimizing the difference between the
1317  * actual and approximative series expansions
1318  */
1319 {
1320  bool res = false;
1321  int k;
1322  double rd,R,f;
1323  double simplex_size = 0.01;
1324  MatrixXd simplex;
1325  VectorXd func_val;
1326  double ftol = 1e-9;
1327  VectorXd lambda;
1328  VectorXd mu;
1329  int neval;
1330  int max_eval = 1000;
1331  int report = 1;
1332  fitUser u = new_fit_user(nfit,nterms);
1333 
1334  if (nfit < 2) {
1335  printf("fwd_fit_berg_scherg does not work with less than two equivalent sources.");
1336  return false;
1337  }
1338 
1339  /*
1340  * (1) Calculate the coefficients of the true expansion
1341  */
1342  for (k = 0; k < nterms; k++)
1343  u->fn[k] = this->fwd_eeg_get_multi_sphere_model_coeff(k+1);
1344 
1345  /*
1346  * (2) Calculate the weighting
1347  */
1348  rd = R = this->layers[0].rad;
1349  for (k = 1; k < this->nlayer(); k++) {
1350  if (this->layers[k].rad > R)
1351  R = this->layers[k].rad;
1352  if (this->layers[k].rad < rd)
1353  rd = this->layers[k].rad;
1354  }
1355  f = rd/R;
1356 
1357 #ifdef ZHANG
1358  /*
1359  * This is the Zhang weighting
1360  */
1361  for (k = 1; k < nterms; k++)
1362  u->w[k-1] = pow(f,k);
1363 #else
1364  /*
1365  * This is the correct weighting
1366  */
1367  for (k = 1; k < nterms; k++)
1368  u->w[k-1] = sqrt((2.0*k+1)*(3.0*k+1.0)/k)*pow(f,(k-1.0));
1369 #endif
1370 
1371  /*
1372  * (3) Prepare for simplex minimization
1373  */
1374  func_val = VectorXd(nfit+1);
1375  lambda = VectorXd(nfit);
1376  mu = VectorXd(nfit);
1377  /*
1378  * (4) Rather arbitrary initial guess
1379  */
1380  for (k = 0; k < nfit; k++) {
1381  /*
1382  mu[k] = (k+1)*0.1*f;
1383  */
1384  mu[k] = (rand() / (RAND_MAX + 1.0))*f;//replacement for: mu[k] = drand48()*f;
1385  }
1386 
1387  simplex = get_initial_simplex(mu,simplex_size);
1388  for (k = 0; k < nfit+1; k++)
1389  func_val[k] = one_step(static_cast<VectorXd>(simplex.row(k)),u);
1390 
1391  /*
1392  * (5) Do the nonlinear minimization
1393  */
1394  if (!(res = SimplexAlgorithm::simplex_minimize<double>( simplex,
1395  func_val,
1396  ftol,
1397  one_step,
1398  u,
1399  max_eval,
1400  neval,
1401  report,
1402  report_fit)))
1403  goto out;
1404 
1405  for (k = 0; k < nfit; k++)
1406  mu[k] = simplex(0,k);
1407 
1408  /*
1409  * (6) Do the final step: calculation of the linear parameters
1410  */
1411  rv = compute_linear_parameters(mu,lambda,u);
1412 
1413  sort_parameters(mu,lambda,nfit);
1414 #ifdef LOG_FIT
1415  printf("RV = %g %%\n",100*rv);
1416 #endif
1417  this->mu.resize(nfit);
1418  this->lambda.resize(nfit);
1419  this->nfit = nfit;
1420  for (k = 0; k < nfit; k++) {
1421  this->mu[k] = mu[k];
1422  /*
1423  * This division takes into account the actual conductivities
1424  */
1425  this->lambda[k] = lambda[k]/this->layers[this->nlayer()-1].sigma;
1426 #ifdef LOG_FIT
1427  printf("lambda%d = %g\tmu%d = %g\n",k+1,lambda[k],k+1,mu[k]);
1428 #endif
1429  }
1430 
1431  /*
1432  * This is the cleanup code
1433  */
1434 out : {
1435  if (u) {
1436  FREE(u->fn);
1437  FREE_DCMATRIX_1(u->M);
1438  FREE_DCMATRIX_1(u->uu);
1439  FREE_DCMATRIX_1(u->vv);
1440  FREE(u->y);
1441  FREE(u->w);
1442  FREE(u->resi);
1443  FREE(u->sing);
1444  }
1445  return res;
1446  }
1447 }
FWDLIB::FwdEegSphereModel::fwd_eeg_spherepot
static int fwd_eeg_spherepot(float *rd, float *Q, float **el, int neeg, Eigen::VectorXf &Vval, void *client)
Definition: fwd_eeg_sphere_model.cpp:860
FWDLIB::FwdEegSphereLayer::rad
float rad
Definition: fwd_eeg_sphere_layer.h:110
FWDLIB::FwdEegSphereLayer::rel_rad
float rel_rad
Definition: fwd_eeg_sphere_layer.h:111
FWDLIB::FwdCoil::np
int np
Definition: fwd_coil.h:179
FWDLIB::FwdEegSphereModelSet
Holds a set of Electric Current Dipoles.
Definition: fwd_eeg_sphere_model_set.h:80
UTILSLIB::b
Definition: ioutils.h:83
fwd_eeg_sphere_model.h
FwdEegSphereModel class declaration.
FWDLIB::bergSchergPar
Definition: fwd_eeg_sphere_model.cpp:1132
FWDLIB::FwdEegSphereModel::fwd_eeg_fit_berg_scherg
bool fwd_eeg_fit_berg_scherg(int nterms, int nfit, float &rv)
Definition: fwd_eeg_sphere_model.cpp:1310
FWDLIB::FwdEegSphereModel::fwd_eeg_spherepot_coil_vec
static int fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet *els, float **Vval_vec, void *client)
Definition: fwd_eeg_sphere_model.cpp:787
FWDLIB::FwdEegSphereModel
Electric Current Dipole description.
Definition: fwd_eeg_sphere_model.h:91
simplex_algorithm.h
SimplexAlgorithm Template Implementation.
FWDLIB::FwdEegSphereModelSet::fwd_list_eeg_sphere_models
void fwd_list_eeg_sphere_models(FILE *f)
Definition: fwd_eeg_sphere_model_set.cpp:260
FWDLIB::FwdEegSphereModel::fwd_setup_eeg_sphere_model
bool fwd_setup_eeg_sphere_model(float rad, bool fit_berg_scherg, int nfit)
Definition: fwd_eeg_sphere_model.cpp:1006
FWDLIB::FwdCoilSet
FwdCoilSet description.
Definition: fwd_coil_set.h:75
FWDLIB::FwdEegSphereModel::r0
Eigen::Vector3f r0
Definition: fwd_eeg_sphere_model.h:328
k
int k
Definition: fiff_tag.cpp:322
FWDLIB::FwdEegSphereModel::mu
Eigen::VectorXf mu
Definition: fwd_eeg_sphere_model.h:333
FWDLIB::FwdEegSphereModel::scale_pos
int scale_pos
Definition: fwd_eeg_sphere_model.h:336
FWDLIB::FwdEegSphereLayer::sigma
float sigma
Definition: fwd_eeg_sphere_layer.h:112
FWDLIB::FwdEegSphereModel::nterms
int nterms
Definition: fwd_eeg_sphere_model.h:331
FWDLIB::FwdEegSphereModelSet::fwd_load_eeg_sphere_models
static FwdEegSphereModelSet * fwd_load_eeg_sphere_models(const QString &p_sFileName, FwdEegSphereModelSet *now)
Definition: fwd_eeg_sphere_model_set.cpp:158
FWDLIB::FwdEegSphereModel::layers
QList< FwdEegSphereLayer > layers
Definition: fwd_eeg_sphere_model.h:327
FWDLIB::FwdCoil::w
float * w
Definition: fwd_coil.h:182
FWDLIB::FwdCoil::rmag
float ** rmag
Definition: fwd_coil.h:180
FWDLIB::FwdEegSphereModel::fwd_eeg_spherepot_coil
static int fwd_eeg_spherepot_coil(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client)
Definition: fwd_eeg_sphere_model.cpp:977
FWDLIB::FwdEegSphereModel::~FwdEegSphereModel
virtual ~FwdEegSphereModel()
Definition: fwd_eeg_sphere_model.cpp:287
FWDLIB::FwdEegSphereModel::setup_eeg_sphere_model
static FwdEegSphereModel * setup_eeg_sphere_model(const QString &eeg_model_file, QString eeg_model_name, float eeg_sphere_rad)
Definition: fwd_eeg_sphere_model.cpp:293
FWDLIB::FwdCoil::coil_class
int coil_class
Definition: fwd_coil.h:170
FWDLIB::FwdEegSphereLayer
FwdEegSphereLayer description.
Definition: fwd_eeg_sphere_layer.h:72
FWDLIB::FwdEegSphereModel::nfit
int nfit
Definition: fwd_eeg_sphere_model.h:335
FWDLIB::FwdEegSphereModel::name
QString name
Definition: fwd_eeg_sphere_model.h:326
FWDLIB::FwdEegSphereModel::fwd_eeg_get_multi_sphere_model_coeff
double fwd_eeg_get_multi_sphere_model_coeff(int n)
Definition: fwd_eeg_sphere_model.cpp:343
FWDLIB::fitUser
Definition: fwd_eeg_sphere_model.h:72
FWDLIB::FwdEegSphereModel::fn
Eigen::VectorXd fn
Definition: fwd_eeg_sphere_model.h:330
FWDLIB::FwdEegSphereModel::FwdEegSphereModel
FwdEegSphereModel()
Definition: fwd_eeg_sphere_model.cpp:204
FWDLIB::FwdEegSphereModelSet::fwd_select_eeg_sphere_model
FwdEegSphereModel * fwd_select_eeg_sphere_model(const QString &p_sName)
Definition: fwd_eeg_sphere_model_set.cpp:234
FWDLIB::FwdCoil
FwdCoil description.
Definition: fwd_coil.h:88
FWDLIB::FwdEegSphereModel::fwd_eeg_spherepot_vec
static bool fwd_eeg_spherepot_vec(float *rd, float **el, int neeg, float **Vval_vec, void *client)
Definition: fwd_eeg_sphere_model.cpp:675
fwd_eeg_sphere_model_set.h
FwdEegSphereModelSet class declaration.