MNE-CPP 0.1.9
A Framework for Electrophysiology
Loading...
Searching...
No Matches
fwd_eeg_sphere_model.cpp
Go to the documentation of this file.
1//=============================================================================================================
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
42
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
57using namespace Eigen;
58using namespace UTILSLIB;
59using 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
125static 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
142float **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
159double **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
176void mne_free_cmatrix_1 (float **m)
177{
178 if (m) {
179 free(*m);
180 free(m);
181 }
182}
183
184void 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
197static int terms = 0; /* These statistics may be useful */
198static int eval = 0;
199
200//=============================================================================================================
201// DEFINE MEMBER METHODS
202//=============================================================================================================
203
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
250FwdEegSphereModel* 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
290
291//=============================================================================================================
292
293FwdEegSphereModel* 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
315bad : {
316 delete eeg_models;
317 delete eeg_model;
318 return NULL;
319 }
320}
321
322//=============================================================================================================
323
324fitUser 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
448void 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
482void 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
511int 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{
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
638int 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
675bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int neeg, float **Vval_vec, void *client)
676{
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
787int 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
820int 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
860int 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{
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
977int 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
1006bool 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
1032Eigen::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
1043void 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
1050void fromDoubleEigenMatrix(const Eigen::MatrixXd& from_mat, double **to_mat)
1051{
1052 fromDoubleEigenMatrix(from_mat, to_mat, from_mat.rows(), from_mat.cols());
1053}
1054
1055void 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
1061void 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
1067static 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
1079static 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
1129namespace FWDLIB
1130{
1131
1132typedef 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
1140static int comp_pars(const void *p1,const void *p2)
1141/*
1142 * Comparison function for sorting layers
1143 */
1144{
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
1157static 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
1180static 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
1197static 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
1213void 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
1231double 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
1271double 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
1310bool 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 */
1434out : {
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}
int k
Definition fiff_tag.cpp:324
SimplexAlgorithm Template Implementation.
FwdEegSphereModelSet class declaration.
FwdEegSphereModel class declaration.
FwdCoil description.
Definition fwd_coil.h:89
float ** rmag
Definition fwd_coil.h:180
FwdCoilSet description.
FwdEegSphereLayer description.
Electric Current Dipole description.
QList< FwdEegSphereLayer > layers
static bool fwd_eeg_spherepot_vec(float *rd, float **el, int neeg, float **Vval_vec, void *client)
static int fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet *els, float **Vval_vec, void *client)
bool fwd_eeg_fit_berg_scherg(int nterms, int nfit, float &rv)
static int fwd_eeg_spherepot_coil(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client)
static int fwd_eeg_spherepot(float *rd, float *Q, float **el, int neeg, Eigen::VectorXf &Vval, void *client)
static FwdEegSphereModel * setup_eeg_sphere_model(const QString &eeg_model_file, QString eeg_model_name, float eeg_sphere_rad)
double fwd_eeg_get_multi_sphere_model_coeff(int n)
bool fwd_setup_eeg_sphere_model(float rad, bool fit_berg_scherg, int nfit)
Holds a set of Electric Current Dipoles.
FwdEegSphereModel * fwd_select_eeg_sphere_model(const QString &p_sName)
static FwdEegSphereModelSet * fwd_load_eeg_sphere_models(const QString &p_sFileName, FwdEegSphereModelSet *now)