v2.0.0
Loading...
Searching...
No Matches
fwd_bem_model.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "fwd_bem_model.h"
42#include "fwd_bem_solution.h"
44#include <mne/mne_surface.h>
45#include <mne/mne_triangle.h>
47
48#include "fwd_comp_data.h"
49#include "fwd_bem_model.h"
50
51#include "fwd_thread_arg.h"
52
53#include <fiff/fiff_stream.h>
55
56#include <QFile>
57#include <QList>
58#include <QThread>
59#include <QtConcurrent>
60
61#define _USE_MATH_DEFINES
62#include <math.h>
63
64#include <Eigen/Dense>
65
66static float Qx[] = {1.0,0.0,0.0};
67static float Qy[] = {0.0,1.0,0.0};
68static float Qz[] = {0.0,0.0,1.0};
69
70#ifndef TRUE
71#define TRUE 1
72#endif
73
74#ifndef FALSE
75#define FALSE 0
76#endif
77
78#ifndef FAIL
79#define FAIL -1
80#endif
81
82#ifndef OK
83#define OK 0
84#endif
85
86#define X_40 0
87#define Y_40 1
88#define Z_40 2
89
90#define VEC_DIFF_40(from,to,diff) {\
91 (diff)[X_40] = (to)[X_40] - (from)[X_40];\
92 (diff)[Y_40] = (to)[Y_40] - (from)[Y_40];\
93 (diff)[Z_40] = (to)[Z_40] - (from)[Z_40];\
94 }
95
96#define VEC_COPY_40(to,from) {\
97 (to)[X_40] = (from)[X_40];\
98 (to)[Y_40] = (from)[Y_40];\
99 (to)[Z_40] = (from)[Z_40];\
100 }
101
102#define VEC_DOT_40(x,y) ((x)[X_40]*(y)[X_40] + (x)[Y_40]*(y)[Y_40] + (x)[Z_40]*(y)[Z_40])
103
104#define VEC_LEN_40(x) sqrt(VEC_DOT_40(x,x))
105
106#define CROSS_PRODUCT_40(x,y,xy) {\
107 (xy)[X_40] = (x)[Y_40]*(y)[Z_40]-(y)[Y_40]*(x)[Z_40];\
108 (xy)[Y_40] = -((x)[X_40]*(y)[Z_40]-(y)[X_40]*(x)[Z_40]);\
109 (xy)[Z_40] = (x)[X_40]*(y)[Y_40]-(y)[X_40]*(x)[Y_40];\
110 }
111
112#define MALLOC_40(x,t) (t *)malloc((x)*sizeof(t))
113
114#define ALLOC_CMATRIX_40(x,y) mne_cmatrix_40((x),(y))
115
116#define FREE_40(x) if ((char *)(x) != NULL) free((char *)(x))
117
118#define FREE_CMATRIX_40(m) mne_free_cmatrix_40((m))
119
120void mne_free_cmatrix_40 (float **m)
121{
122 if (m) {
123 FREE_40(*m);
124 FREE_40(m);
125 }
126}
127
128static void matrix_error_40(int kind, int nr, int nc)
129
130{
131 if (kind == 1)
132 printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc);
133 else if (kind == 2)
134 printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc);
135 else
136 printf("Allocation error for a %d x %d matrix\n",nr,nc);
137 if (sizeof(void *) == 4) {
138 printf("This is probably because you seem to be using a computer with 32-bit architecture.\n");
139 printf("Please consider moving to a 64-bit platform.");
140 }
141 printf("Cannot continue. Sorry.\n");
142 exit(1);
143}
144
145float **mne_cmatrix_40 (int nr,int nc)
146
147{
148 int i;
149 float **m;
150 float *whole;
151
152 m = MALLOC_40(nr,float *);
153 if (!m) matrix_error_40(1,nr,nc);
154 whole = MALLOC_40(nr*nc,float);
155 if (!whole) matrix_error_40(2,nr,nc);
156
157 for(i=0;i<nr;i++)
158 m[i] = whole + i*nc;
159 return m;
160}
161
162//float
163Eigen::MatrixXf toFloatEigenMatrix_40(float **mat, const int m, const int n)
164{
165 Eigen::MatrixXf eigen_mat(m,n);
166
167 for ( int i = 0; i < m; ++i)
168 for ( int j = 0; j < n; ++j)
169 eigen_mat(i,j) = mat[i][j];
170
171 return eigen_mat;
172}
173
174void fromFloatEigenMatrix_40(const Eigen::MatrixXf& from_mat, float **& to_mat, const int m, const int n)
175{
176 for ( int i = 0; i < m; ++i)
177 for ( int j = 0; j < n; ++j)
178 to_mat[i][j] = from_mat(i,j);
179}
180
181void fromFloatEigenMatrix_40(const Eigen::MatrixXf& from_mat, float **& to_mat)
182{
183 fromFloatEigenMatrix_40(from_mat, to_mat, from_mat.rows(), from_mat.cols());
184}
185
186float **mne_lu_invert_40(float **mat,int dim)
187/*
188 * Invert a matrix using the LU decomposition from
189 * LAPACK
190 */
191{
192 Eigen::MatrixXf eigen_mat = toFloatEigenMatrix_40(mat, dim, dim);
193 Eigen::MatrixXf eigen_mat_inv = eigen_mat.inverse();
194 fromFloatEigenMatrix_40(eigen_mat_inv, mat);
195 return mat;
196}
197
198void mne_transpose_square_40(float **mat, int n)
199/*
200 * In-place transpose of a square matrix
201 */
202{
203 int j,k;
204 float val;
205
206 for (j = 1; j < n; j++)
207 for (k = 0; k < j; k++) {
208 val = mat[j][k];
209 mat[j][k] = mat[k][j];
210 mat[k][j] = val;
211 }
212 return;
213}
214
215float mne_dot_vectors_40(float *v1,
216 float *v2,
217 int nn)
218
219{
220#ifdef BLAS
221 int one = 1;
222 float res = sdot(&nn,v1,&one,v2,&one);
223 return res;
224#else
225 float res = 0.0;
226 int k;
227
228 for (k = 0; k < nn; k++)
229 res = res + v1[k]*v2[k];
230 return res;
231#endif
232}
233
234void mne_add_scaled_vector_to_40(float *v1,float scale, float *v2,int nn)
235
236{
237#ifdef BLAS
238 float fscale = scale;
239 int one = 1;
240 saxpy(&nn,&fscale,v1,&one,v2,&one);
241#else
242 int k;
243 for (k = 0; k < nn; k++)
244 v2[k] = v2[k] + scale*v1[k];
245#endif
246 return;
247}
248
249void mne_scale_vector_40(double scale,float *v,int nn)
250
251{
252#ifdef BLAS
253 float fscale = scale;
254 int one = 1;
255 sscal(&nn,&fscale,v,&one);
256#else
257 int k;
258 for (k = 0; k < nn; k++)
259 v[k] = v[k]*scale;
260#endif
261}
262
263#include <Eigen/Core>
264using namespace Eigen;
265
266float **mne_mat_mat_mult_40 (float **m1,
267 float **m2,
268 int d1,
269 int d2,
270 int d3)
271/* Matrix multiplication
272 * result(d1 x d3) = m1(d1 x d2) * m2(d2 x d3) */
273
274{
275#ifdef BLAS
276 float **result = ALLOC_CMATRIX_40(d1,d3);
277 char *transa = "N";
278 char *transb = "N";
279 float zero = 0.0;
280 float one = 1.0;
281 sgemm (transa,transb,&d3,&d1,&d2,
282 &one,m2[0],&d3,m1[0],&d2,&zero,result[0],&d3);
283 return (result);
284#else
285 float **result = ALLOC_CMATRIX_40(d1,d3);
286 int j,k,p;
287 float sum;
288
289 // TODO: Hack to include faster Eigen processing. This should eventually be replaced dwith pure Eigen logic.
290 MatrixXf a(d1,d2);
291 MatrixXf b(d2,d3);
292
293 // ToDo use Eigen::Map
294 for (j = 0; j < d1; j++) {
295 for (k = 0; k < d2; k++) {
296 a(j,k) = m1[j][k];
297 }
298 }
299
300 for (j = 0; j < d2; j++) {
301 for (k = 0; k < d3; k++) {
302 b(j,k) = m2[j][k];
303 }
304 }
305
306 MatrixXf resultMat = a * b;
307
308 for (j = 0; j < d1; j++) {
309 for (k = 0; k < d3; k++) {
310 result[j][k] = resultMat(j,k);
311 }
312 }
313
314// for (j = 0; j < d1; j++)
315// for (k = 0; k < d3; k++) {
316// sum = 0.0;
317// for (p = 0; p < d2; p++)
318// sum = sum + m1[j][p]*m2[p][k];
319// result[j][k] = sum;
320// }
321 return (result);
322#endif
323}
324
325namespace FWDLIB
326{
327
328static struct {
329 int kind;
330 const QString name;
331} surf_expl[] = { { FIFFV_BEM_SURF_ID_BRAIN , "inner skull" },
332{ FIFFV_BEM_SURF_ID_SKULL , "outer skull" },
333{ FIFFV_BEM_SURF_ID_HEAD , "scalp" },
334{ -1 , "unknown" } };
335
336static struct {
338 const QString name;
339} method_expl[] = { { FWD_BEM_CONSTANT_COLL , "constant collocation" },
340{ FWD_BEM_LINEAR_COLL , "linear collocation" },
341{ -1 , "unknown" } };
342
343}
344
345#define BEM_SUFFIX "-bem.fif"
346#define BEM_SOL_SUFFIX "-bem-sol.fif"
347
348//============================= misc_util.c =============================
349
350static QString strip_from(const QString& s, const QString& suffix)
351{
352 QString res;
353
354 if (s.endsWith(suffix)) {
355 res = s;
356 res.chop(suffix.size());
357 }
358 else
359 res = s;
360
361 return res;
362}
363
364//============================= mne_coord_transforms.c =============================
365
366namespace FWDLIB
367{
368
372typedef struct {
373 int frame;
374 const QString name;
376
377}
378
379const QString mne_coord_frame_name_40(int frame)
380
381{
382 static FWDLIB::frameNameRec_40 frames[] = {
383 {FIFFV_COORD_UNKNOWN,"unknown"},
384 {FIFFV_COORD_DEVICE,"MEG device"},
385 {FIFFV_COORD_ISOTRAK,"isotrak"},
386 {FIFFV_COORD_HPI,"hpi"},
387 {FIFFV_COORD_HEAD,"head"},
388 {FIFFV_COORD_MRI,"MRI (surface RAS)"},
389 {FIFFV_MNE_COORD_MRI_VOXEL, "MRI voxel"},
390 {FIFFV_COORD_MRI_SLICE,"MRI slice"},
391 {FIFFV_COORD_MRI_DISPLAY,"MRI display"},
392 {FIFFV_MNE_COORD_CTF_DEVICE,"CTF MEG device"},
393 {FIFFV_MNE_COORD_CTF_HEAD,"CTF/4D/KIT head"},
394 {FIFFV_MNE_COORD_RAS,"RAS (non-zero origin)"},
395 {FIFFV_MNE_COORD_MNI_TAL,"MNI Talairach"},
396 {FIFFV_MNE_COORD_FS_TAL_GTZ,"Talairach (MNI z > 0)"},
397 {FIFFV_MNE_COORD_FS_TAL_LTZ,"Talairach (MNI z < 0)"},
398 {-1,"unknown"}
399 };
400 int k;
401 for (k = 0; frames[k].frame != -1; k++) {
402 if (frame == frames[k].frame)
403 return frames[k].name;
404 }
405 return frames[k].name;
406}
407
408//=============================================================================================================
409// USED NAMESPACES
410//=============================================================================================================
411
412using namespace Eigen;
413using namespace FIFFLIB;
414using namespace MNELIB;
415using namespace FWDLIB;
416
417//=============================================================================================================
418// DEFINE MEMBER METHODS
419//=============================================================================================================
420
422:ntri (NULL)
423,np (NULL)
424,nsurf (0)
425,sigma (NULL)
426,gamma (NULL)
427,source_mult(NULL)
428,field_mult (NULL)
430,solution (NULL)
431,nsol (0)
432,head_mri_t ()
433,v0 (NULL)
434,use_ip_approach(false)
436{
437}
438
439//=============================================================================================================
440
442{
443 for (int k = 0; k < this->nsurf; k++)
444 delete this->surfs[k];
445 FREE_40(this->ntri);
446 FREE_40(this->np);
447 FREE_40(this->sigma);
448 FREE_40(this->source_mult);
449 FREE_40(this->field_mult);
450 FREE_CMATRIX_40(this->gamma);
451 this->fwd_bem_free_solution();
452}
453
454//=============================================================================================================
455
457{
458 FREE_CMATRIX_40(this->solution); this->solution = NULL;
459 this->sol_name.clear();
460 FREE_40(this->v0); this->v0 = NULL;
462 this->nsol = 0;
463
464 return;
465}
466
467//=============================================================================================================
468
470/*
471 * Make a standard BEM solution file name
472 */
473{
474 QString s1, s2;
475
476 s1 = strip_from(name,".fif");
477 s2 = strip_from(s1,"-sol");
478 s1 = strip_from(s2,"-bem");
479 s2 = QString("%1%2").arg(s1).arg(BEM_SOL_SUFFIX);
480 return s2;
481}
482
483//=============================================================================================================
484
486{
487 int k;
488
489 for (k = 0; surf_expl[k].kind >= 0; k++)
490 if (surf_expl[k].kind == kind)
491 return surf_expl[k].name;
492
493 return surf_expl[k].name;
494}
495
496//=============================================================================================================
497
499
500{
501 int k;
502
503 for (k = 0; method_expl[k].method >= 0; k++)
504 if (method_expl[k].method == method)
505 return method_expl[k].name;
506
507 return method_expl[k].name;
508}
509
510//=============================================================================================================
511
512int FwdBemModel::get_int(FiffStream::SPtr &stream, const FiffDirNode::SPtr &node, int what, int *res)
513/*
514 * Wrapper to get int's
515 */
516{
517 FiffTag::SPtr t_pTag;
518 if(node->find_tag(stream, what, t_pTag)) {
519 if (t_pTag->getType() != FIFFT_INT) {
520 printf("Expected an integer tag : %d (found data type %d instead)\n",what,t_pTag->getType() );
521 return FAIL;
522 }
523 *res = *t_pTag->toInt();
524 return OK;
525 }
526 return FAIL;
527}
528
529//=============================================================================================================
530
532{
533 // if (!model) {
534 // printf("No model specified for fwd_bem_find_surface");
535 // return NULL;
536 // }
537 for (int k = 0; k < this->nsurf; k++)
538 if (this->surfs[k]->id == kind)
539 return this->surfs[k];
540 printf("Desired surface (%d = %s) not found.", kind,fwd_bem_explain_surface(kind).toUtf8().constData());
541 return NULL;
542}
543
544//=============================================================================================================
545
546FwdBemModel *FwdBemModel::fwd_bem_load_surfaces(const QString &name, int *kinds, int nkind)
547/*
548 * Load a set of surfaces
549 */
550{
551 QList<MNESurface*> surfs;// = NULL;
552 float *sigma = NULL;
553 float *sigma1;
554 FwdBemModel *m = NULL;
555 int j,k;
556
557 if (nkind <= 0) {
558 qCritical("No surfaces specified to fwd_bem_load_surfaces");
559 return NULL;
560 }
561
562// surfs = MALLOC_40(nkind,MNESurface*);
563 sigma = MALLOC_40(nkind,float);
564// for (k = 0; k < nkind; k++)
565// surfs[k] = NULL;
566
567 for (k = 0; k < nkind; k++) {
569 if (surfs[k] == NULL)
570 goto bad;
571 if ((surfs[k] = MNESurface::read_bem_surface(name,kinds[k],TRUE,sigma+k)) == NULL)
572 goto bad;
573 if (sigma[k] < 0.0) {
574 qCritical("No conductivity available for surface %s",fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
575 goto bad;
576 }
577 if (surfs[k]->coord_frame != FIFFV_COORD_MRI) { /* We make our life much easier with this */
578 qCritical("Surface %s not specified in MRI coordinates.",fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
579 goto bad;
580 }
581 }
582 m = new FwdBemModel;
583
584 m->surf_name = name;
585 m->nsurf = nkind;
586 m->surfs = surfs;
587 m->sigma = sigma;
588 m->ntri = MALLOC_40(nkind,int);
589 m->np = MALLOC_40(nkind,int);
590 m->gamma = ALLOC_CMATRIX_40(nkind,nkind);
591 m->source_mult = MALLOC_40(nkind,float);
592 m->field_mult = MALLOC_40(nkind,float);
593 /*
594 * Dirty trick for the zero conductivity outside
595 */
596 sigma1 = MALLOC_40(nkind+1,float);
597 sigma1[0] = 0.0;
598 sigma = sigma1+1;
599 for (k = 0; k < m->nsurf; k++)
600 sigma[k] = m->sigma[k];
601 /*
602 * Gamma factors and multipliers
603 */
604 for (j = 0; j < m->nsurf; j++) {
605 m->ntri[j] = m->surfs[j]->ntri;
606 m->np[j] = m->surfs[j]->np;
607 m->source_mult[j] = 2.0/(sigma[j]+sigma[j-1]);
608 m->field_mult[j] = sigma[j]-sigma[j-1];
609 for (k = 0; k < m->nsurf; k++)
610 m->gamma[j][k] = (sigma[k]-sigma[k-1])/(sigma[j]+sigma[j-1]);
611 }
612 FREE_40(sigma1);
613
614 return m;
615
616bad : {
617 FREE_40(sigma);
618 for (k = 0; k < surfs.size(); k++)
619 delete surfs[k];
620// FREE_40(surfs);
621 surfs.clear();
622 return NULL;
623 }
624}
625
626//=============================================================================================================
627
629/*
630 * Load surfaces for the homogeneous model
631 */
632{
633 int kinds[] = { FIFFV_BEM_SURF_ID_BRAIN };
634 int nkind = 1;
635
636 return fwd_bem_load_surfaces(name,kinds,nkind);
637}
638
639//=============================================================================================================
640
642/*
643 * Load surfaces for three-layer model
644 */
645{
647 int nkind = 3;
648
649 return fwd_bem_load_surfaces(name,kinds,nkind);
650}
651
652//=============================================================================================================
653
655/*
656 * Load the potential solution matrix and attach it to the model:
657 *
658 * return values:
659 *
660 * TRUE found a suitable solution
661 * FALSE did not find a suitable solution
662 * FAIL error in reading the solution
663 *
664 */
665{
666 QFile file(name);
667 FiffStream::SPtr stream(new FiffStream(&file));
668
669 float **sol = NULL;
670 FiffDirNode::SPtr bem_node;
671 int method;
672 FiffTag::SPtr t_pTag;
673 int nsol;
674
675 if(!stream->open())
676 goto not_found;
677
678 /*
679 * Find the BEM data
680 */
681 {
682 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(FIFFB_BEM);
683
684 if (nodes.size() == 0) {
685 printf ("No BEM data in %s",name.toUtf8().constData());
686 goto not_found;
687 }
688 bem_node = nodes[0];
689 }
690 /*
691 * Approximation method
692 */
693 if (get_int(stream,bem_node,FIFF_BEM_APPROX,&method) != OK)
694 goto not_found;
699 else {
700 printf ("Cannot handle BEM approximation method : %d",method);
701 goto bad;
702 }
704 printf("Approximation method in file : %d desired : %d",method,bem_method);
705 goto not_found;
706 }
707 {
708 int dim,k;
709
710 if (!bem_node->find_tag(stream, FIFF_BEM_POT_SOLUTION, t_pTag))
711 goto bad;
712 qint32 ndim;
713 QVector<qint32> dims;
714 t_pTag->getMatrixDimensions(ndim, dims);
715
716 if (ndim != 2) {
717 printf("Expected a two-dimensional solution matrix instead of a %d dimensional one",ndim);
718 goto bad;
719 }
720 for (k = 0, dim = 0; k < m->nsurf; k++)
721 dim = dim + ((method == FWD_BEM_LINEAR_COLL) ? m->surfs[k]->np : m->surfs[k]->ntri);
722 if (dims[0] != dim || dims[1] != dim) {
723 printf("Expected a %d x %d solution matrix instead of a %d x %d one",dim,dim,dims[0],dims[1]);
724 goto not_found;
725 }
726
727 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
728 sol = ALLOC_CMATRIX_40(tmp_sol.rows(),tmp_sol.cols());
729 fromFloatEigenMatrix_40(tmp_sol, sol);
730 nsol = dims[1];
731 }
732 if(m)
734 m->sol_name = name;
735 m->solution = sol;
736 m->nsol = nsol;
737 m->bem_method = method;
738 stream->close();
739
740 return TRUE;
741
742bad : {
743 stream->close();
744 FREE_CMATRIX_40(sol);
745 return FAIL;
746 }
747
748not_found : {
749 stream->close();
750 FREE_CMATRIX_40(sol);
751 return FALSE;
752 }
753}
754
755//=============================================================================================================
756
758/*
759 * Set the coordinate transformation
760 */
761{
762 if (t.from == FIFFV_COORD_HEAD && t.to == FIFFV_COORD_MRI) {
763 m->head_mri_t = t;
764 return OK;
765 }
766 else if (t.from == FIFFV_COORD_MRI && t.to == FIFFV_COORD_HEAD) {
767 m->head_mri_t = t.inverted();
768 return OK;
769 }
770 else {
771 printf ("Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
772 return FAIL;
773 }
774}
775
776//=============================================================================================================
777
778MNESurface* FwdBemModel::make_guesses(MNESurface* guess_surf, float guessrad, float *guess_r0, float grid, float exclude, float mindist) /* Exclude points closer than this to
779 * the guess boundary surface */
780/*
781 * Make a guess space inside a sphere
782 */
783{
784 char *bemname = NULL;
785 MNESurface* sphere = NULL;
786 MNESurface* res = NULL;
787 int k;
788 float dist;
789 float r0[] = { 0.0, 0.0, 0.0 };
790
791 if (!guess_r0)
792 guess_r0 = r0;
793
794 if (!guess_surf) {
795 printf("Making a spherical guess space with radius %7.1f mm...\n",1000*guessrad);
796 //#ifdef USE_SHARE_PATH
797 // if ((bemname = mne_compose_mne_name("share/mne","icos.fif")) == NULL)
798 //#else
799 // if ((bemname = mne_compose_mne_name("setup/mne","icos.fif")) == NULL)
800 //#endif
801 // goto out;
802
803 // QFile bemFile("/usr/pubsw/packages/mne/stable/share/mne/icos.fif");
804
805 QFile bemFile(QString(QCoreApplication::applicationDirPath() + "/../resources/general/surf2bem/icos.fif"));
806 if ( !QCoreApplication::startingUp() )
807 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString("/../resources/general/surf2bem/icos.fif"));
808 else if (!bemFile.exists())
809 bemFile.setFileName("../resources/general/surf2bem/icos.fif");
810
811 if( !bemFile.exists () ){
812 qDebug() << bemFile.fileName() << "does not exists.";
813 goto out;
814 }
815
816 bemname = MALLOC_40(strlen(bemFile.fileName().toUtf8().data())+1,char);
817 strcpy(bemname,bemFile.fileName().toUtf8().data());
818
819 if ((sphere = MNESurface::read_bem_surface(bemname,9003,FALSE,NULL)) == NULL)
820 goto out;
821
822 for (k = 0; k < sphere->np; k++) {
823 dist = VEC_LEN_40(&sphere->rr(k,0));
824 sphere->rr(k,X_40) = guessrad*sphere->rr(k,X_40)/dist + guess_r0[X_40];
825 sphere->rr(k,Y_40) = guessrad*sphere->rr(k,Y_40)/dist + guess_r0[Y_40];
826 sphere->rr(k,Z_40) = guessrad*sphere->rr(k,Z_40)/dist + guess_r0[Z_40];
827 }
828 if (sphere->add_geometry_info(TRUE) == FAIL)
829 goto out;
830 guess_surf = sphere;
831 }
832 else {
833 printf("Guess surface (%d = %s) is in %s coordinates\n",
834 guess_surf->id,FwdBemModel::fwd_bem_explain_surface(guess_surf->id).toUtf8().constData(),
835 mne_coord_frame_name_40(guess_surf->coord_frame).toUtf8().constData());
836 }
837 printf("Filtering (grid = %6.f mm)...\n",1000*grid);
838 res = (MNESurface*)MNESourceSpace::make_volume_source_space(*guess_surf,grid,exclude,mindist);
839
840out : {
841 FREE_40(bemname);
842 if(sphere)
843 delete sphere;
844 return res;
845 }
846}
847
848//=============================================================================================================
849
850double FwdBemModel::calc_beta(double *rk, double *rk1)
851
852{
853 double rkk1[3];
854 double size;
855 double res;
856
857 VEC_DIFF_40(rk,rk1,rkk1);
858 size = VEC_LEN_40(rkk1);
859
860 res = log((VEC_LEN_40(rk)*size + VEC_DOT_40(rk,rkk1))/
861 (VEC_LEN_40(rk1)*size + VEC_DOT_40(rk1,rkk1)))/size;
862 return (res);
863}
864
865//=============================================================================================================
866
867void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* The final result */
868/*
869 * The linear potential matrix element computations
870 */
871{
872 double y1[3],y2[3],y3[3]; /* Corners with origin at from */
873 double *y[5];
874 double **yy;
875 double l1,l2,l3; /* Lengths of y1, y2, and y3 */
876 double solid; /* The standard solid angle */
877 double vec_omega[3]; /* The cross-product integral */
878 double cross[3]; /* y1 x y2 */
879 double triple; /* VEC_DOT_40(y1 x y2,y3) */
880 double ss;
881 double beta[3],bbeta[3];
882 int j,k;
883 double z[3];
884 double n2,area2;
885 double diff[3];
886 static const double solid_eps = 4.0*M_PI/1.0E6;
887 /*
888 * This circularity makes things easy for us...
889 */
890 y[0] = y3;
891 y[1] = y1;
892 y[2] = y2;
893 y[3] = y3;
894 y[4] = y1;
895 yy = y + 1; /* yy can have index -1! */
896 /*
897 * The standard solid angle computation
898 */
899 VEC_DIFF_40(from,to->r1,y1);
900 VEC_DIFF_40(from,to->r2,y2);
901 VEC_DIFF_40(from,to->r3,y3);
902
903 CROSS_PRODUCT_40(y1,y2,cross);
904 triple = VEC_DOT_40(cross,y3);
905
906 l1 = VEC_LEN_40(y1);
907 l2 = VEC_LEN_40(y2);
908 l3 = VEC_LEN_40(y3);
909 ss = (l1*l2*l3+VEC_DOT_40(y1,y2)*l3+VEC_DOT_40(y1,y3)*l2+VEC_DOT_40(y2,y3)*l1);
910 solid = 2.0*atan2(triple,ss);
911 if (std::fabs(solid) < solid_eps) {
912 for (k = 0; k < 3; k++)
913 omega[k] = 0.0;
914 }
915 else {
916 /*
917 * Calculate the magic vector vec_omega
918 */
919 for (j = 0; j < 3; j++)
920 beta[j] = calc_beta(yy[j],yy[j+1]);
921 bbeta[0] = beta[2] - beta[0];
922 bbeta[1] = beta[0] - beta[1];
923 bbeta[2] = beta[1] - beta[2];
924
925 for (j = 0; j < 3; j++)
926 vec_omega[j] = 0.0;
927 for (j = 0; j < 3; j++)
928 for (k = 0; k < 3; k++)
929 vec_omega[k] = vec_omega[k] + bbeta[j]*yy[j][k];
930 /*
931 * Put it all together...
932 */
933 area2 = 2.0*to->area;
934 n2 = 1.0/(area2*area2);
935 for (k = 0; k < 3; k++) {
936 CROSS_PRODUCT_40(yy[k+1],yy[k-1],z);
937 VEC_DIFF_40(yy[k+1],yy[k-1],diff);
938 omega[k] = n2*(-area2*VEC_DOT_40(z,to->nn)*solid +
939 triple*VEC_DOT_40(diff,vec_omega));
940 }
941 }
942#ifdef CHECK
943 /*
944 * Check it out!
945 *
946 * omega1 + omega2 + omega3 = solid
947 */
948 rel1 = (solid + omega[X_40]+omega[Y_40]+omega[Z_40])/solid;
949 /*
950 * The other way of evaluating...
951 */
952 for (j = 0; j < 3; j++)
953 check[j] = 0;
954 for (k = 0; k < 3; k++) {
955 CROSS_PRODUCT_40(to->nn[to],yy[k],z);
956 for (j = 0; j < 3; j++)
957 check[j] = check[j] + omega[k]*z[j];
958 }
959 for (j = 0; j < 3; j++)
960 check[j] = -area2*check[j]/triple;
961 fprintf (stderr,"(%g,%g,%g) =? (%g,%g,%g)\n",
962 check[X_40],check[Y_40],check[Z_40],
963 vec_omega[X_40],vec_omega[Y_40],vec_omega[Z_40]);
964 for (j = 0; j < 3; j++)
965 check[j] = check[j] - vec_omega[j];
966 rel2 = sqrt(VEC_DOT_40(check,check)/VEC_DOT_40(vec_omega,vec_omega));
967 fprintf (stderr,"err1 = %g, err2 = %g\n",100*rel1,100*rel2);
968#endif
969 return;
970}
971
972//=============================================================================================================
973
975/*
976 * Improve auto-element approximation...
977 */
978{
979 float *row;
980 float sum,miss;
981 int nnode = surf->np;
982 int ntri = surf->ntri;
983 int nmemb;
984 int j,k;
985 float pi2 = 2.0*M_PI;
986 MNETriangle* tri;
987
988#ifdef SIMPLE
989 for (j = 0; j < nnode; j++) {
990 row = mat[j];
991 sum = 0.0;
992 for (k = 0; k < nnode; k++)
993 sum = sum + row[k];
994 fprintf (stderr,"row %d sum = %g missing = %g\n",j+1,sum/pi2,
995 1.0-sum/pi2);
996 row[j] = pi2 - sum;
997 }
998#else
999 for (j = 0; j < nnode; j++) {
1000 /*
1001 * How much is missing?
1002 */
1003 row = mat[j];
1004 sum = 0.0;
1005 for (k = 0; k < nnode; k++)
1006 sum = sum + row[k];
1007 miss = pi2-sum;
1008 nmemb = surf->nneighbor_tri[j];
1009 /*
1010 * The node itself receives one half
1011 */
1012 row[j] = miss/2.0;
1013 /*
1014 * The rest is divided evenly among the member nodes...
1015 */
1016 miss = miss/(4.0*nmemb);
1017 for (k = 0,tri = surf->tris.data(); k < ntri; k++,tri++) {
1018 if (tri->vert[0] == j) {
1019 row[tri->vert[1]] = row[tri->vert[1]] + miss;
1020 row[tri->vert[2]] = row[tri->vert[2]] + miss;
1021 }
1022 else if (tri->vert[1] == j) {
1023 row[tri->vert[0]] = row[tri->vert[0]] + miss;
1024 row[tri->vert[2]] = row[tri->vert[2]] + miss;
1025 }
1026 else if (tri->vert[2] == j) {
1027 row[tri->vert[0]] = row[tri->vert[0]] + miss;
1028 row[tri->vert[1]] = row[tri->vert[1]] + miss;
1029 }
1030 }
1031 /*
1032 * Just check it it out...
1033 *
1034 for (k = 0, sum = 0; k < nnode; k++)
1035 sum = sum + row[k];
1036 fprintf (stderr,"row %d sum = %g\n",j+1,sum/pi2);
1037 */
1038 }
1039#endif
1040 return;
1041}
1042
1043//=============================================================================================================
1044
1045float **FwdBemModel::fwd_bem_lin_pot_coeff(const QList<MNESurface*>& surfs)
1046/*
1047 * Calculate the coefficients for linear collocation approach
1048 */
1049{
1050 float **mat = NULL;
1051 float **sub_mat = NULL;
1052 int np1,np2,ntri,np_tot,np_max;
1053 float **nodes_ptr;
1054 MNETriangle* tri;
1055 double omega[3];
1056 double *row = NULL;
1057 int j,k,p,q,c;
1058 int joff,koff;
1059 MNESurface* surf1;
1060 MNESurface* surf2;
1061
1062 for (p = 0, np_tot = np_max = 0; p < surfs.size(); p++) {
1063 np_tot += surfs[p]->np;
1064 if (surfs[p]->np > np_max)
1065 np_max = surfs[p]->np;
1066 }
1067
1068 mat = ALLOC_CMATRIX_40(np_tot,np_tot);
1069 for (j = 0; j < np_tot; j++)
1070 for (k = 0; k < np_tot; k++)
1071 mat[j][k] = 0.0;
1072 row = MALLOC_40(np_max,double);
1073 sub_mat = MALLOC_40(np_max,float *);
1074 for (p = 0, joff = 0; p < surfs.size(); p++, joff = joff + np1) {
1075 surf1 = surfs[p];
1076 np1 = surf1->np;
1077 nodes_ptr = NULL; // UNUSED: was float** alias
1078 for (q = 0, koff = 0; q < surfs.size(); q++, koff = koff + np2) {
1079 surf2 = surfs[q];
1080 np2 = surf2->np;
1081 ntri = surf2->ntri;
1082
1083 printf("\t\t%s (%d) -> %s (%d) ... ",
1084 fwd_bem_explain_surface(surf1->id).toUtf8().constData(),np1,
1085 fwd_bem_explain_surface(surf2->id).toUtf8().constData(),np2);
1086
1087 for (j = 0; j < np1; j++) {
1088 for (k = 0; k < np2; k++)
1089 row[k] = 0.0;
1090 for (k = 0, tri = surf2->tris.data(); k < ntri; k++,tri++) {
1091 /*
1092 * No contribution from a triangle that
1093 * this vertex belongs to
1094 */
1095 if (p == q && (tri->vert[0] == j || tri->vert[1] == j || tri->vert[2] == j))
1096 continue;
1097 /*
1098 * Otherwise do the hard job
1099 */
1100 lin_pot_coeff (&surf1->rr(j,0),tri,omega);
1101 for (c = 0; c < 3; c++)
1102 row[tri->vert[c]] = row[tri->vert[c]] - omega[c];
1103 }
1104 for (k = 0; k < np2; k++)
1105 mat[j+joff][k+koff] = row[k];
1106 }
1107 if (p == q) {
1108 for (j = 0; j < np1; j++)
1109 sub_mat[j] = mat[j+joff]+koff;
1110 correct_auto_elements (surf1,sub_mat);
1111 }
1112 printf("[done]\n");
1113 }
1114 }
1115 FREE_40(row);
1116 FREE_40(sub_mat);
1117 return(mat);
1118}
1119
1120//=============================================================================================================
1121
1123/*
1124 * Compute the linear collocation potential solution
1125 */
1126{
1127 float **coeff = NULL;
1128 float ip_mult;
1129 int k;
1130
1131 if(m)
1133
1134 printf("\nComputing the linear collocation solution...\n");
1135 fprintf (stderr,"\tMatrix coefficients...\n");
1136 if ((coeff = fwd_bem_lin_pot_coeff (m->surfs)) == NULL)
1137 goto bad;
1138
1139 for (k = 0, m->nsol = 0; k < m->nsurf; k++)
1140 m->nsol += m->surfs[k]->np;
1141
1142 fprintf (stderr,"\tInverting the coefficient matrix...\n");
1143 if ((m->solution = fwd_bem_multi_solution (coeff,m->gamma,m->nsurf,m->np)) == NULL)
1144 goto bad;
1145
1146 /*
1147 * IP approach?
1148 */
1149 if ((m->nsurf == 3) &&
1150 (ip_mult = m->sigma[m->nsurf-2]/m->sigma[m->nsurf-1]) <= m->ip_approach_limit) {
1151 float **ip_solution = NULL;
1152
1153 fprintf (stderr,"IP approach required...\n");
1154
1155 fprintf (stderr,"\tMatrix coefficients (homog)...\n");
1156 QList<MNESurface*> last_surfs;
1157 last_surfs << m->surfs.last();
1158 if ((coeff = fwd_bem_lin_pot_coeff(last_surfs))== NULL)//m->surfs+m->nsurf-1,1)) == NULL)
1159 goto bad;
1160
1161 fprintf (stderr,"\tInverting the coefficient matrix (homog)...\n");
1162 if ((ip_solution = fwd_bem_homog_solution (coeff,m->surfs[m->nsurf-1]->np)) == NULL)
1163 goto bad;
1164
1165 fprintf (stderr,"\tModify the original solution to incorporate IP approach...\n");
1166
1167 fwd_bem_ip_modify_solution(m->solution,ip_solution,ip_mult,m->nsurf,m->np);
1168 FREE_CMATRIX_40(ip_solution);
1169
1170 }
1172 printf("Solution ready.\n");
1173 return OK;
1174
1175bad : {
1176 if(m)
1178 FREE_CMATRIX_40(coeff);
1179 return FAIL;
1180 }
1181}
1182
1183//=============================================================================================================
1184
1185float **FwdBemModel::fwd_bem_multi_solution(float **solids, float **gamma, int nsurf, int *ntri) /* Number of triangles or nodes on each surface */
1186/*
1187 * Invert I - solids/(2*M_PI)
1188 * Take deflation into account
1189 * The matrix is destroyed after inversion
1190 * This is the general multilayer case
1191 */
1192{
1193 int j,k,p,q;
1194 float defl;
1195 float pi2 = 1.0/(2*M_PI);
1196 float mult;
1197 int joff,koff,jup,kup,ntot;
1198
1199 for (j = 0,ntot = 0; j < nsurf; j++)
1200 ntot += ntri[j];
1201 defl = 1.0/ntot;
1202 /*
1203 * Modify the matrix
1204 */
1205 for (p = 0, joff = 0; p < nsurf; p++) {
1206 jup = ntri[p] + joff;
1207 for (q = 0, koff = 0; q < nsurf; q++) {
1208 kup = ntri[q] + koff;
1209 mult = (gamma == NULL) ? pi2 : pi2*gamma[p][q];
1210 for (j = joff; j < jup; j++)
1211 for (k = koff; k < kup; k++)
1212 solids[j][k] = defl - solids[j][k]*mult;
1213 koff = kup;
1214 }
1215 joff = jup;
1216 }
1217 for (k = 0; k < ntot; k++)
1218 solids[k][k] = solids[k][k] + 1.0;
1219
1220 return (mne_lu_invert_40(solids,ntot));
1221}
1222
1223//=============================================================================================================
1224
1225float **FwdBemModel::fwd_bem_homog_solution(float **solids, int ntri)
1226/*
1227 * Invert I - solids/(2*M_PI)
1228 * Take deflation into account
1229 * The matrix is destroyed after inversion
1230 * This is the homogeneous model case
1231 */
1232{
1233 return fwd_bem_multi_solution (solids,NULL,1,&ntri);
1234}
1235
1236//=============================================================================================================
1237
1238void FwdBemModel::fwd_bem_ip_modify_solution(float **solution, float **ip_solution, float ip_mult, int nsurf, int *ntri) /* Number of triangles (nodes) on each surface */
1239/*
1240 * Modify the solution according to the IP approach
1241 */
1242{
1243 int s;
1244 int j,k,joff,koff,ntot,nlast;
1245 float mult;
1246 float *row = NULL;
1247 float **sub = NULL;
1248
1249 for (s = 0, koff = 0; s < nsurf-1; s++)
1250 koff = koff + ntri[s];
1251 nlast = ntri[nsurf-1];
1252 ntot = koff + nlast;
1253
1254 row = MALLOC_40(nlast,float);
1255 sub = MALLOC_40(ntot,float *);
1256 mult = (1.0 + ip_mult)/ip_mult;
1257
1258 printf("\t\tCombining...");
1259#ifndef OLD
1260 printf("t ");
1261 mne_transpose_square_40(ip_solution,nlast);
1262#endif
1263 for (s = 0, joff = 0; s < nsurf; s++) {
1264 printf("%d3 ",s+1);
1265 /*
1266 * Pick the correct submatrix
1267 */
1268 for (j = 0; j < ntri[s]; j++)
1269 sub[j] = solution[j+joff]+koff;
1270 /*
1271 * Multiply
1272 */
1273#ifdef OLD
1274 for (j = 0; j < ntri[s]; j++) {
1275 for (k = 0; k < nlast; k++) {
1276 res = mne_dot_vectors_skip_skip(sub[j],1,ip_solution[0]+k,nlast,nlast);
1277 row[k] = sub[j][k] - 2.0*res;
1278 }
1279 for (k = 0; k < nlast; k++)
1280 sub[j][k] = row[k];
1281 }
1282#else
1283 for (j = 0; j < ntri[s]; j++) {
1284 for (k = 0; k < nlast; k++)
1285 row[k] = mne_dot_vectors_40(sub[j],ip_solution[k],nlast);
1286 mne_add_scaled_vector_to_40(row,-2.0,sub[j],nlast);
1287 }
1288#endif
1289 joff = joff+ntri[s];
1290 }
1291#ifndef OLD
1292 printf("t ");
1293 mne_transpose_square_40(ip_solution,nlast);
1294#endif
1295 printf("33 ");
1296 /*
1297 * The lower right corner is a special case
1298 */
1299 for (j = 0; j < nlast; j++)
1300 for (k = 0; k < nlast; k++)
1301 sub[j][k] = sub[j][k] + mult*ip_solution[j][k];
1302 /*
1303 * Final scaling
1304 */
1305 printf("done.\n\t\tScaling...");
1306 mne_scale_vector_40(ip_mult,solution[0],ntot*ntot);
1307 printf("done.\n");
1308 FREE_40(row); FREE_40(sub);
1309 return;
1310}
1311
1312//=============================================================================================================
1313
1314int FwdBemModel::fwd_bem_check_solids(float **angles, int ntri1, int ntri2, float desired)
1315/*
1316 * Check the angle computations
1317 */
1318{
1319 float *sums = MALLOC_40(ntri1,float);
1320 float sum;
1321 int j,k;
1322 int res = 0;
1323
1324 for (j = 0; j < ntri1; j++) {
1325 sum = 0;
1326 for (k = 0; k < ntri2; k++)
1327 sum = sum + angles[j][k];
1328 sums[j] = sum/(2*M_PI);
1329 }
1330 for (j = 0; j < ntri1; j++)
1331 /*
1332 * Three cases:
1333 * same surface: sum = 2*pi
1334 * to outer: sum = 4*pi
1335 * to inner: sum = 0*pi;
1336 */
1337 if (std::fabs(sums[j]-desired) > 1e-4) {
1338 printf("solid angle matrix: rowsum[%d] = 2PI*%g",
1339 j+1,sums[j]);
1340 res = -1;
1341 break;
1342 }
1343 FREE_40(sums);
1344 return res;
1345}
1346
1347//=============================================================================================================
1348
1349float **FwdBemModel::fwd_bem_solid_angles(const QList<MNESurface*>& surfs)
1350/*
1351 * Compute the solid angle matrix
1352 */
1353{
1354 MNESurface* surf1;
1355 MNESurface* surf2;
1356 MNETriangle* tri;
1357 int ntri1,ntri2,ntri_tot;
1358 int j,k,p,q;
1359 int joff,koff;
1360 float **solids;
1361 float result;
1362 float **sub_solids = NULL;
1363 float desired;
1364
1365 for (p = 0,ntri_tot = 0; p < surfs.size(); p++)
1366 ntri_tot += surfs[p]->ntri;
1367
1368 sub_solids = MALLOC_40(ntri_tot,float *);
1369 solids = ALLOC_CMATRIX_40(ntri_tot,ntri_tot);
1370 for (p = 0, joff = 0; p < surfs.size(); p++, joff = joff + ntri1) {
1371 surf1 = surfs[p];
1372 ntri1 = surf1->ntri;
1373 for (q = 0, koff = 0; q < surfs.size(); q++, koff = koff + ntri2) {
1374 surf2 = surfs[q];
1375 ntri2 = surf2->ntri;
1376 printf("\t\t%s (%d) -> %s (%d) ... ",fwd_bem_explain_surface(surf1->id).toUtf8().constData(),ntri1,fwd_bem_explain_surface(surf2->id).toUtf8().constData(),ntri2);
1377 for (j = 0; j < ntri1; j++)
1378 for (k = 0, tri = surf2->tris.data(); k < ntri2; k++, tri++) {
1379 if (p == q && j == k)
1380 result = 0.0;
1381 else
1382 result = MNESurfaceOrVolume::solid_angle (surf1->tris[j].cent,*tri);
1383 solids[j+joff][k+koff] = result;
1384 }
1385 for (j = 0; j < ntri1; j++)
1386 sub_solids[j] = solids[j+joff]+koff;
1387 printf("[done]\n");
1388 if (p == q)
1389 desired = 1;
1390 else if (p < q)
1391 desired = 0;
1392 else
1393 desired = 2;
1394 if (fwd_bem_check_solids(sub_solids,ntri1,ntri2,desired) == FAIL) {
1395 FREE_CMATRIX_40(solids);
1396 FREE_40(sub_solids);
1397 return NULL;
1398 }
1399 }
1400 }
1401 FREE_40(sub_solids);
1402 return (solids);
1403}
1404
1405//=============================================================================================================
1406
1408/*
1409 * Compute the solution for the constant collocation approach
1410 */
1411{
1412 float **solids = NULL;
1413 int k;
1414 float ip_mult;
1415
1416 if(m)
1418
1419 printf("\nComputing the constant collocation solution...\n");
1420 printf("\tSolid angles...\n");
1421 if ((solids = fwd_bem_solid_angles(m->surfs)) == NULL)
1422 goto bad;
1423
1424 for (k = 0, m->nsol = 0; k < m->nsurf; k++)
1425 m->nsol += m->surfs[k]->ntri;
1426
1427 fprintf (stderr,"\tInverting the coefficient matrix...\n");
1428 if ((m->solution = fwd_bem_multi_solution (solids,m->gamma,m->nsurf,m->ntri)) == NULL)
1429 goto bad;
1430 /*
1431 * IP approach?
1432 */
1433 if ((m->nsurf == 3) &&
1434 (ip_mult = m->sigma[m->nsurf-2]/m->sigma[m->nsurf-1]) <= m->ip_approach_limit) {
1435 float **ip_solution = NULL;
1436
1437 fprintf (stderr,"IP approach required...\n");
1438
1439 fprintf (stderr,"\tSolid angles (homog)...\n");
1440 QList<MNESurface*> last_surfs;
1441 last_surfs << m->surfs.last();
1442 if ((solids = fwd_bem_solid_angles (last_surfs)) == NULL)//m->surfs+m->nsurf-1,1)) == NULL)
1443 goto bad;
1444
1445 fprintf (stderr,"\tInverting the coefficient matrix (homog)...\n");
1446 if ((ip_solution = fwd_bem_homog_solution (solids,m->surfs[m->nsurf-1]->ntri)) == NULL)
1447 goto bad;
1448
1449 fprintf (stderr,"\tModify the original solution to incorporate IP approach...\n");
1450 fwd_bem_ip_modify_solution(m->solution,ip_solution,ip_mult,m->nsurf,m->ntri);
1451 FREE_CMATRIX_40(ip_solution);
1452 }
1454 fprintf (stderr,"Solution ready.\n");
1455
1456 return OK;
1457
1458bad : {
1459 if(m)
1461 FREE_CMATRIX_40(solids);
1462 return FAIL;
1463 }
1464}
1465
1466//=============================================================================================================
1467
1469/*
1470 * Compute the solution
1471 */
1472{
1473 /*
1474 * Compute the solution
1475 */
1480
1481 if(m)
1483 printf ("Unknown BEM method: %d\n",bem_method);
1484 return FAIL;
1485}
1486
1487//=============================================================================================================
1488
1489int FwdBemModel::fwd_bem_load_recompute_solution(const QString& name, int bem_method, int force_recompute, FwdBemModel *m)
1490/*
1491 * Load or recompute the potential solution matrix
1492 */
1493{
1494 int solres;
1495
1496 if (!m) {
1497 printf ("No model specified for fwd_bem_load_recompute_solution");
1498 return FAIL;
1499 }
1500
1501 if (!force_recompute) {
1502 if(m)
1505 if (solres == TRUE) {
1506 printf("\nLoaded %s BEM solution from %s\n",fwd_bem_explain_method(m->bem_method).toUtf8().constData(),name.toUtf8().constData());
1507 return OK;
1508 }
1509 else if (solres == FAIL)
1510 return FAIL;
1511#ifdef DEBUG
1512 else
1513 printf("Desired BEM solution not available in %s (%s)\n",name,err_get_error());
1514#endif
1515 }
1519}
1520
1521//=============================================================================================================
1522
1523float FwdBemModel::fwd_bem_inf_field(float *rd, float *Q, float *rp, float *dir) /* Which field component */
1524/*
1525 * Infinite-medium magnetic field
1526 * (without \mu_0/4\pi)
1527 */
1528{
1529 float diff[3],diff2,cross[3];
1530
1531 VEC_DIFF_40(rd,rp,diff);
1532 diff2 = VEC_DOT_40(diff,diff);
1533 CROSS_PRODUCT_40(Q,diff,cross);
1534
1535 return (VEC_DOT_40(cross,dir)/(diff2*sqrt(diff2)));
1536}
1537
1538//=============================================================================================================
1539
1540float FwdBemModel::fwd_bem_inf_pot(float *rd, float *Q, float *rp) /* Potential point */
1541/*
1542 * The infinite medium potential
1543 */
1544{
1545 float diff[3];
1546 float diff2;
1547 VEC_DIFF_40(rd,rp,diff);
1548 diff2 = VEC_DOT_40(diff,diff);
1549 return (VEC_DOT_40(Q,diff)/(4.0*M_PI*diff2*sqrt(diff2)));
1550}
1551
1552//=============================================================================================================
1553
1555/*
1556 * Set up for computing the solution at a set of electrodes
1557 */
1558{
1559 FwdCoil* el;
1560 MNESurface* scalp;
1561 int k,p,q,v;
1562 float *one_sol,*pick_sol;
1563 float r[3],w[3],dist;
1564 int best;
1565 MNETriangle* tri;
1566 float x,y,z;
1567 FwdBemSolution* sol;
1568
1569 if (!m) {
1570 printf("Model missing in fwd_bem_specify_els");
1571 goto bad;
1572 }
1573 if (!m->solution) {
1574 printf("Solution not computed in fwd_bem_specify_els");
1575 goto bad;
1576 }
1577 if (!els || els->ncoil == 0)
1578 return OK;
1580 /*
1581 * Hard work follows
1582 */
1583 els->user_data = sol = new FwdBemSolution();
1585
1586 sol->ncoil = els->ncoil;
1587 sol->np = m->nsol;
1588 sol->solution = ALLOC_CMATRIX_40(sol->ncoil,sol->np);
1589 /*
1590 * Go through all coils
1591 */
1592 for (k = 0; k < els->ncoil; k++) {
1593 el = els->coils[k];
1594 one_sol = sol->solution[k];
1595 for (q = 0; q < m->nsol; q++)
1596 one_sol[q] = 0.0;
1597 scalp = m->surfs[0];
1598 /*
1599 * Go through all 'integration points'
1600 */
1601 for (p = 0; p < el->np; p++) {
1602 VEC_COPY_40(r,el->rmag[p]);
1603 if (!m->head_mri_t.isEmpty())
1605 best = scalp->project_to_surface(nullptr,Eigen::Map<const Eigen::Vector3f>(r),dist);
1606 if (best < 0) {
1607 printf("One of the electrodes could not be projected onto the scalp surface. How come?");
1608 goto bad;
1609 }
1611 /*
1612 * Simply pick the value at the triangle
1613 */
1614 pick_sol = m->solution[best];
1615 for (q = 0; q < m->nsol; q++)
1616 one_sol[q] += el->w[p]*pick_sol[q];
1617 }
1618 else if (m->bem_method == FWD_BEM_LINEAR_COLL) {
1619 /*
1620 * Calculate a linear interpolation between the vertex values
1621 */
1622 tri = &scalp->tris[best];
1623 scalp->triangle_coords(Eigen::Map<const Eigen::Vector3f>(r),best,x,y,z);
1624
1625 w[X_40] = el->w[p]*(1.0 - x - y);
1626 w[Y_40] = el->w[p]*x;
1627 w[Z_40] = el->w[p]*y;
1628 for (v = 0; v < 3; v++) {
1629 pick_sol = m->solution[tri->vert[v]];
1630 for (q = 0; q < m->nsol; q++)
1631 one_sol[q] += w[v]*pick_sol[q];
1632 }
1633 }
1634 else {
1635 printf("Unknown BEM approximation method : %d\n",m->bem_method);
1636 goto bad;
1637 }
1638 }
1639 }
1640 return OK;
1641
1642bad : {
1644 return FAIL;
1645 }
1646}
1647
1648//=============================================================================================================
1649
1650void FwdBemModel::fwd_bem_pot_grad_calc(float *rd, float *Q, FwdBemModel* m, FwdCoilSet* els, int all_surfs, float *xgrad, float *ygrad, float *zgrad)
1651/*
1652 * Compute the potentials due to a current dipole
1653 */
1654{
1655 MNETriangle* tri;
1656 int ntri;
1657 int s,k,p,nsol,pp;
1658 float mult;
1659 float *v0,ee[3];
1660 float **solution;
1661 float mri_rd[3],mri_Q[3];
1662
1663 float *grads[3];
1664 float *grad;
1665
1666 grads[0] = xgrad;
1667 grads[1] = ygrad;
1668 grads[2] = zgrad;
1669
1670 if (!m->v0)
1671 m->v0 = MALLOC_40(m->nsol,float);
1672 v0 = m->v0;
1673
1674 VEC_COPY_40(mri_rd,rd);
1675 VEC_COPY_40(mri_Q,Q);
1676 if (!m->head_mri_t.isEmpty()) {
1679 }
1680 for (pp = 0; pp < 3; pp++) {
1681 grad = grads[pp];
1682
1683 for (p = 0; p < 3; p++)
1684 ee[p] = p == pp ? 1.0 : 0.0;
1685 if (!m->head_mri_t.isEmpty())
1687
1688 for (s = 0, p = 0; s < m->nsurf; s++) {
1689 ntri = m->surfs[s]->ntri;
1690 tri = m->surfs[s]->tris.data();
1691 mult = m->source_mult[s];
1692 for (k = 0; k < ntri; k++, tri++)
1693 v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent.data(),ee);
1694 }
1695 if (els) {
1697 solution = sol->solution;
1698 nsol = sol->ncoil;
1699 }
1700 else {
1701 solution = m->solution;
1702 nsol = all_surfs ? m->nsol : m->surfs[0]->ntri;
1703 }
1704 for (k = 0; k < nsol; k++)
1705 grad[k] = mne_dot_vectors_40(solution[k],v0,m->nsol);
1706 }
1707 return;
1708}
1709
1710//=============================================================================================================
1711
1712void FwdBemModel::fwd_bem_lin_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot) /* Put the result here */
1713/*
1714 * Compute the potentials due to a current dipole
1715 * using the linear potential approximation
1716 */
1717{
1718 float *rr_row;
1719 int np;
1720 int s,k,p,nsol;
1721 float mult,mri_rd[3],mri_Q[3];
1722
1723 float *v0;
1724 float **solution;
1725
1726 if (!m->v0)
1727 m->v0 = MALLOC_40(m->nsol,float);
1728 v0 = m->v0;
1729
1730 VEC_COPY_40(mri_rd,rd);
1731 VEC_COPY_40(mri_Q,Q);
1732 if (!m->head_mri_t.isEmpty()) {
1735 }
1736 for (s = 0, p = 0; s < m->nsurf; s++) {
1737 np = m->surfs[s]->np;
1738 mult = m->source_mult[s];
1739 for (k = 0; k < np; k++)
1740 v0[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,&m->surfs[s]->rr(k,0));
1741 }
1742 if (els) {
1744 solution = sol->solution;
1745 nsol = sol->ncoil;
1746 }
1747 else {
1748 solution = m->solution;
1749 nsol = all_surfs ? m->nsol : m->surfs[0]->np;
1750 }
1751 for (k = 0; k < nsol; k++)
1752 pot[k] = mne_dot_vectors_40(solution[k],v0,m->nsol);
1753 return;
1754}
1755
1756//=============================================================================================================
1757
1758void FwdBemModel::fwd_bem_lin_pot_grad_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *xgrad, float *ygrad, float *zgrad)
1759/*
1760 * Compute the derivaties of potentials due to a current dipole with respect to the dipole position
1761 * using the linear potential approximation
1762 */
1763{
1764 int np;
1765 int s,k,p,nsol,pp;
1766 float mult,mri_rd[3],mri_Q[3];
1767
1768 float *v0,ee[3];
1769 float **solution;
1770
1771 float *grads[3];
1772 float *grad;
1773
1774 grads[0] = xgrad;
1775 grads[1] = ygrad;
1776 grads[2] = zgrad;
1777
1778 if (!m->v0)
1779 m->v0 = MALLOC_40(m->nsol,float);
1780 v0 = m->v0;
1781
1782 VEC_COPY_40(mri_rd,rd);
1783 VEC_COPY_40(mri_Q,Q);
1784 if (!m->head_mri_t.isEmpty()) {
1787 }
1788 for (pp = 0; pp < 3; pp++) {
1789 grad = grads[pp];
1790
1791 for (p = 0; p < 3; p++)
1792 ee[p] = p == pp ? 1.0 : 0.0;
1793 if (!m->head_mri_t.isEmpty())
1795
1796 for (s = 0, p = 0; s < m->nsurf; s++) {
1797 np = m->surfs[s]->np;
1798 mult = m->source_mult[s];
1799 for (k = 0; k < np; k++)
1800 v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,&m->surfs[s]->rr(k,0),ee);
1801 }
1802 if (els) {
1804 solution = sol->solution;
1805 nsol = sol->ncoil;
1806 }
1807 else {
1808 solution = m->solution;
1809 nsol = all_surfs ? m->nsol : m->surfs[0]->np;
1810 }
1811 for (k = 0; k < nsol; k++)
1812 grad[k] = mne_dot_vectors_40(solution[k],v0,m->nsol);
1813 }
1814 return;
1815}
1816
1817//=============================================================================================================
1818
1819void FwdBemModel::fwd_bem_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot)
1820/*
1821 * Compute the potentials due to a current dipole
1822 */
1823{
1824 MNETriangle* tri;
1825 int ntri;
1826 int s,k,p,nsol;
1827 float mult;
1828 float *v0;
1829 float **solution;
1830 float mri_rd[3],mri_Q[3];
1831
1832 if (!m->v0)
1833 m->v0 = MALLOC_40(m->nsol,float);
1834 v0 = m->v0;
1835
1836 VEC_COPY_40(mri_rd,rd);
1837 VEC_COPY_40(mri_Q,Q);
1838 if (!m->head_mri_t.isEmpty()) {
1841 }
1842 for (s = 0, p = 0; s < m->nsurf; s++) {
1843 ntri = m->surfs[s]->ntri;
1844 tri = m->surfs[s]->tris.data();
1845 mult = m->source_mult[s];
1846 for (k = 0; k < ntri; k++, tri++)
1847 v0[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,tri->cent.data());
1848 }
1849 if (els) {
1851 solution = sol->solution;
1852 nsol = sol->ncoil;
1853 }
1854 else {
1855 solution = m->solution;
1856 nsol = all_surfs ? m->nsol : m->surfs[0]->ntri;
1857 }
1858 for (k = 0; k < nsol; k++)
1859 pot[k] = mne_dot_vectors_40(solution[k],v0,m->nsol);
1860 return;
1861}
1862
1863//=============================================================================================================
1864
1865int FwdBemModel::fwd_bem_pot_els(float *rd, float *Q, FwdCoilSet *els, float *pot, void *client) /* The model */
1866/*
1867 * This version calculates the potential on all surfaces
1868 */
1869{
1870 FwdBemModel* m = (FwdBemModel*)client;
1872
1873 if (!m) {
1874 printf("No BEM model specified to fwd_bem_pot_els");
1875 return FAIL;
1876 }
1877 if (!m->solution) {
1878 printf("No solution available for fwd_bem_pot_els");
1879 return FAIL;
1880 }
1881 if (!sol || sol->ncoil != els->ncoil) {
1882 printf("No appropriate electrode-specific data available in fwd_bem_pot_coils");
1883 return FAIL;
1884 }
1886 fwd_bem_pot_calc(rd,Q,m,els,FALSE,pot);
1887 else if (m->bem_method == FWD_BEM_LINEAR_COLL)
1888 fwd_bem_lin_pot_calc(rd,Q,m,els,FALSE,pot);
1889 else {
1890 printf("Unknown BEM method : %d",m->bem_method);
1891 return FAIL;
1892 }
1893 return OK;
1894}
1895
1896//=============================================================================================================
1897
1898int FwdBemModel::fwd_bem_pot_grad_els(float *rd, float *Q, FwdCoilSet *els, float *pot, float *xgrad, float *ygrad, float *zgrad, void *client) /* The model */
1899/*
1900 * This version calculates the potential on all surfaces
1901 */
1902{
1903 FwdBemModel* m = (FwdBemModel*)client;
1905
1906 if (!m) {
1907 qCritical("No BEM model specified to fwd_bem_pot_els");
1908 return FAIL;
1909 }
1910 if (!m->solution) {
1911 qCritical("No solution available for fwd_bem_pot_els");
1912 return FAIL;
1913 }
1914 if (!sol || sol->ncoil != els->ncoil) {
1915 qCritical("No appropriate electrode-specific data available in fwd_bem_pot_coils");
1916 return FAIL;
1917 }
1919 if (pot)
1920 fwd_bem_pot_calc(rd,Q,m,els,FALSE,pot);
1921 fwd_bem_pot_grad_calc(rd,Q,m,els,FALSE,xgrad,ygrad,zgrad);
1922 }
1923 else if (m->bem_method == FWD_BEM_LINEAR_COLL) {
1924 if (pot)
1925 fwd_bem_lin_pot_calc(rd,Q,m,els,FALSE,pot);
1926 fwd_bem_lin_pot_grad_calc(rd,Q,m,els,FALSE,xgrad,ygrad,zgrad);
1927 }
1928 else {
1929 qCritical("Unknown BEM method : %d",m->bem_method);
1930 return FAIL;
1931 }
1932 return OK;
1933}
1934
1935//=============================================================================================================
1936
1937#define ARSINH(x) log((x) + sqrt(1.0+(x)*(x)))
1938
1939void FwdBemModel::calc_f(double *xx, double *yy, double *f0, double *fx, double *fy) /* The weights in the linear approximation */
1940{
1941 double det = -xx[Y_40]*yy[X_40] + xx[Z_40]*yy[X_40] +
1942 xx[X_40]*yy[Y_40] - xx[Z_40]*yy[Y_40] - xx[X_40]*yy[Z_40] + xx[Y_40]*yy[Z_40];
1943 int k;
1944
1945 f0[X_40] = -xx[Z_40]*yy[Y_40] + xx[Y_40]*yy[Z_40];
1946 f0[Y_40] = xx[Z_40]*yy[X_40] - xx[X_40]*yy[Z_40];
1947 f0[Z_40] = -xx[Y_40]*yy[X_40] + xx[X_40]*yy[Y_40];
1948
1949 fx[X_40] = yy[Y_40] - yy[Z_40];
1950 fx[Y_40] = -yy[X_40] + yy[Z_40];
1951 fx[Z_40] = yy[X_40] - yy[Y_40];
1952
1953 fy[X_40] = -xx[Y_40] + xx[Z_40];
1954 fy[Y_40] = xx[X_40] - xx[Z_40];
1955 fy[Z_40] = -xx[X_40] + xx[Y_40];
1956
1957 for (k = 0; k < 3; k++) {
1958 f0[k] = f0[k]/det;
1959 fx[k] = fx[k]/det;
1960 fy[k] = fy[k]/det;
1961 }
1962}
1963
1964//=============================================================================================================
1965
1966void FwdBemModel::calc_magic(double u, double z, double A, double B, double *beta, double *D)
1967/*
1968 * Calculate Urankar's magic numbers
1969 */
1970{
1971 double B2 = 1.0 + B*B;
1972 double ABu = A + B*u;
1973 *D = sqrt(u*u + z*z + ABu*ABu);
1974 beta[0] = ABu/sqrt(u*u + z*z);
1975 beta[1] = (A*B + B2*u)/sqrt(A*A + B2*z*z);
1976 beta[2] = (B*z*z - A*u)/(z*(*D));
1977}
1978
1979//=============================================================================================================
1980
1981void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, double *T, double *S1, double *S2, double *f0, double *fx, double *fy)
1982{
1983 double y1[3],y2[3],y3[3];
1984 double xx[4],yy[4];
1985 double A,B,z,dx;
1986 double beta[3],I1,Tx,Ty,Txx,Tyy,Sxx,mult;
1987 double S1x,S1y,S2x,S2y;
1988 double D1,B2;
1989 int k;
1990 /*
1991 * Preliminaries...
1992 *
1993 * 1. Move origin to viewpoint...
1994 *
1995 */
1996 VEC_DIFF_40(from,to->r1,y1);
1997 VEC_DIFF_40(from,to->r2,y2);
1998 VEC_DIFF_40(from,to->r3,y3);
1999 /*
2000 * 2. Calculate local xy coordinates...
2001 */
2002 xx[0] = VEC_DOT_40(y1,to->ex);
2003 xx[1] = VEC_DOT_40(y2,to->ex);
2004 xx[2] = VEC_DOT_40(y3,to->ex);
2005 xx[3] = xx[0];
2006
2007 yy[0] = VEC_DOT_40(y1,to->ey);
2008 yy[1] = VEC_DOT_40(y2,to->ey);
2009 yy[2] = VEC_DOT_40(y3,to->ey);
2010 yy[3] = yy[0];
2011
2012 calc_f (xx,yy,f0,fx,fy);
2013 /*
2014 * 3. Distance of the plane from origin...
2015 */
2016 z = VEC_DOT_40(y1,to->nn);
2017 /*
2018 * Put together the line integral...
2019 * We use the convention where the local y-axis
2020 * is parallel to the last side and, therefore, dx = 0
2021 * on that side. We can thus omit the last side from this
2022 * computation in some cases.
2023 */
2024 I1 = 0.0;
2025 Tx = 0.0;
2026 Ty = 0.0;
2027 S1x = 0.0;
2028 S1y = 0.0;
2029 S2x = 0.0;
2030 S2y = 0.0;
2031 for (k = 0; k < 2; k++) {
2032 dx = xx[k+1] - xx[k];
2033 A = (yy[k]*xx[k+1] - yy[k+1]*xx[k])/dx;
2034 B = (yy[k+1]-yy[k])/dx;
2035 B2 = (1.0 + B*B);
2036 /*
2037 * Upper limit
2038 */
2039 calc_magic (xx[k+1],z,A,B,beta,&D1);
2040 I1 = I1 - xx[k+1]*ARSINH(beta[0]) - (A/sqrt(1.0+B*B))*ARSINH(beta[1])
2041 - z*atan(beta[2]);
2042 Txx = ARSINH(beta[1])/sqrt(B2);
2043 Tx = Tx + Txx;
2044 Ty = Ty + B*Txx;
2045 Sxx = (D1 - A*B*Txx)/B2;
2046 S1x = S1x + Sxx;
2047 S1y = S1y + B*Sxx;
2048 Sxx = (B*D1 + A*Txx)/B2;
2049 S2x = S2x + Sxx;
2050 /*
2051 * Lower limit
2052 */
2053 calc_magic (xx[k],z,A,B,beta,&D1);
2054 I1 = I1 + xx[k]*ARSINH(beta[0]) + (A/sqrt(1.0+B*B))*ARSINH(beta[1])
2055 + z*atan(beta[2]);
2056 Txx = ARSINH(beta[1])/sqrt(B2);
2057 Tx = Tx - Txx;
2058 Ty = Ty - B*Txx;
2059 Sxx = (D1 - A*B*Txx)/B2;
2060 S1x = S1x - Sxx;
2061 S1y = S1y - B*Sxx;
2062 Sxx = (B*D1 + A*Txx)/B2;
2063 S2x = S2x - Sxx;
2064 }
2065 /*
2066 * Handle last side (dx = 0) in a special way;
2067 */
2068 mult = 1.0/sqrt(xx[k]*xx[k]+z*z);
2069 /*
2070 * Upper...
2071 */
2072 Tyy = ARSINH(mult*yy[k+1]);
2073 Ty = Ty + Tyy;
2074 S1y = S1y + xx[k]*Tyy;
2075 /*
2076 * Lower...
2077 */
2078 Tyy = ARSINH(mult*yy[k]);
2079 Ty = Ty - Tyy;
2080 S1y = S1y - xx[k]*Tyy;
2081 /*
2082 * Set return values
2083 */
2084 *I1p = I1;
2085 T[X_40] = Tx;
2086 T[Y_40] = Ty;
2087 S1[X_40] = S1x;
2088 S1[Y_40] = S1y;
2089 S2[X_40] = S2x;
2090 S2[Y_40] = -S1x;
2091 return;
2092}
2093
2094//=============================================================================================================
2095
2096double FwdBemModel::one_field_coeff(float *dest, float *normal, MNETriangle* tri)
2097/*
2098 * Compute the integral over one triangle.
2099 * This looks magical but it is not.
2100 */
2101{
2102 double *yy[4];
2103 double y1[3],y2[3],y3[3];
2104 double beta[3];
2105 double bbeta[3];
2106 double coeff[3];
2107 int j,k;
2108
2109 yy[0] = y1;
2110 yy[1] = y2;
2111 yy[2] = y3;
2112 yy[3] = y1;
2113 VEC_DIFF_40(dest,tri->r1,y1);
2114 VEC_DIFF_40(dest,tri->r2,y2);
2115 VEC_DIFF_40(dest,tri->r3,y3);
2116 for (j = 0; j < 3; j++)
2117 beta[j] = calc_beta(yy[j],yy[j+1]);
2118 bbeta[0] = beta[2] - beta[0];
2119 bbeta[1] = beta[0] - beta[1];
2120 bbeta[2] = beta[1] - beta[2];
2121
2122 for (j = 0; j < 3; j++)
2123 coeff[j] = 0.0;
2124 for (j = 0; j < 3; j++)
2125 for (k = 0; k < 3; k++)
2126 coeff[k] = coeff[k] + yy[j][k]*bbeta[j];
2127 return (VEC_DOT_40(coeff,normal));
2128}
2129
2130//=============================================================================================================
2131
2132float **FwdBemModel::fwd_bem_field_coeff(FwdBemModel *m, FwdCoilSet *coils) /* Gradiometer coil positions */
2133/*
2134 * Compute the weighting factors to obtain the magnetic field
2135 */
2136{
2137 MNESurface* surf;
2138 MNETriangle* tri;
2139 FwdCoil* coil;
2140 FwdCoilSet* tcoils = NULL;
2141 int ntri;
2142 float **coeff = NULL;
2143 int j,k,p,s,off;
2144 double res;
2145 double mult;
2146
2147 if (m->solution == NULL) {
2148 printf("Solution matrix missing in fwd_bem_field_coeff");
2149 return NULL;
2150 }
2152 printf("BEM method should be constant collocation for fwd_bem_field_coeff");
2153 return NULL;
2154 }
2155 if (coils->coord_frame != FIFFV_COORD_MRI) {
2156 if (coils->coord_frame == FIFFV_COORD_HEAD) {
2157 if (m->head_mri_t.isEmpty()) {
2158 printf("head -> mri coordinate transform missing in fwd_bem_field_coeff");
2159 return NULL;
2160 }
2161 else {
2162 if (!coils) {
2163 qWarning("No coils to duplicate");
2164 return NULL;
2165 }
2166 /*
2167 * Make a transformed duplicate
2168 */
2169 if ((tcoils = coils->dup_coil_set(m->head_mri_t)) == NULL)
2170 return NULL;
2171 coils = tcoils;
2172 }
2173 }
2174 else {
2175 printf("Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->coord_frame);
2176 return NULL;
2177 }
2178 }
2179 ntri = m->nsol;
2180 coeff = ALLOC_CMATRIX_40(coils->ncoil,ntri);
2181
2182 for (s = 0, off = 0; s < m->nsurf; s++) {
2183 surf = m->surfs[s];
2184 ntri = surf->ntri;
2185 tri = surf->tris.data();
2186 mult = m->field_mult[s];
2187
2188 for (k = 0; k < ntri; k++,tri++) {
2189 for (j = 0; j < coils->ncoil; j++) {
2190 coil = coils->coils[j];
2191 res = 0.0;
2192 for (p = 0; p < coil->np; p++)
2193 res = res + coil->w[p]*one_field_coeff(coil->rmag[p],coil->cosmag[p],tri);
2194 coeff[j][k+off] = mult*res;
2195 }
2196 }
2197 off = off + ntri;
2198 }
2199 delete tcoils;
2200 return coeff;
2201}
2202
2203//=============================================================================================================
2204
2205double FwdBemModel::calc_gamma(double *rk, double *rk1)
2206{
2207 double rkk1[3];
2208 double size;
2209 double res;
2210
2211 VEC_DIFF_40(rk,rk1,rkk1);
2212 size = VEC_LEN_40(rkk1);
2213
2214 res = log((VEC_LEN_40(rk1)*size + VEC_DOT_40(rk1,rkk1))/
2215 (VEC_LEN_40(rk)*size + VEC_DOT_40(rk,rkk1)))/size;
2216 return (res);
2217}
2218
2219//=============================================================================================================
2220
2221void FwdBemModel::fwd_bem_one_lin_field_coeff_ferg(float *dest, float *dir, MNETriangle* tri, double *res) /* The results */
2222{
2223 double c[3]; /* Component of dest vector normal to
2224 * the triangle plane */
2225 double A[3]; /* Projection of dest onto the triangle */
2226 double c1[3],c2[3],c3[3];
2227 double y1[3],y2[3],y3[3];
2228 double *yy[4],*cc[4];
2229 double rjk[3][3];
2230 double cross[3],triple,l1,l2,l3,solid,clen;
2231 double common,sum,beta,gamma;
2232 int k;
2233
2234 yy[0] = y1; cc[0] = c1;
2235 yy[1] = y2; cc[1] = c2;
2236 yy[2] = y3; cc[2] = c3;
2237 yy[3] = y1; cc[3] = c1;
2238
2239 VEC_DIFF_40(tri->r2,tri->r3,rjk[0]);
2240 VEC_DIFF_40(tri->r3,tri->r1,rjk[1]);
2241 VEC_DIFF_40(tri->r1,tri->r2,rjk[2]);
2242
2243 for (k = 0; k < 3; k++) {
2244 y1[k] = tri->r1[k] - dest[k];
2245 y2[k] = tri->r2[k] - dest[k];
2246 y3[k] = tri->r3[k] - dest[k];
2247 }
2248 clen = VEC_DOT_40(y1,tri->nn);
2249 for (k = 0; k < 3; k++) {
2250 c[k] = clen*tri->nn[k];
2251 A[k] = dest[k] + c[k];
2252 c1[k] = tri->r1[k] - A[k];
2253 c2[k] = tri->r2[k] - A[k];
2254 c3[k] = tri->r3[k] - A[k];
2255 }
2256 /*
2257 * beta and gamma...
2258 */
2259 for (sum = 0.0, k = 0; k < 3; k++) {
2260 CROSS_PRODUCT_40(cc[k],cc[k+1],cross);
2261 beta = VEC_DOT_40(cross,tri->nn);
2262 gamma = calc_gamma (yy[k],yy[k+1]);
2263 sum = sum + beta*gamma;
2264 }
2265 /*
2266 * Solid angle...
2267 */
2268 CROSS_PRODUCT_40(y1,y2,cross);
2269 triple = VEC_DOT_40(cross,y3);
2270
2271 l1 = VEC_LEN_40(y1);
2272 l2 = VEC_LEN_40(y2);
2273 l3 = VEC_LEN_40(y3);
2274 solid = 2.0*atan2(triple,
2275 (l1*l2*l3+
2276 VEC_DOT_40(y1,y2)*l3+
2277 VEC_DOT_40(y1,y3)*l2+
2278 VEC_DOT_40(y2,y3)*l1));
2279 /*
2280 * Now we are ready to assemble it all together
2281 */
2282 common = (sum-clen*solid)/(2.0*tri->area);
2283 for (k = 0; k < 3; k++)
2284 res[k] = -VEC_DOT_40(rjk[k],dir)*common;
2285 return;
2286}
2287
2288//=============================================================================================================
2289
2290void FwdBemModel::fwd_bem_one_lin_field_coeff_uran(float *dest, float *dir, MNETriangle* tri, double *res) /* The results */
2291{
2292 double I1,T[2],S1[2],S2[2];
2293 double f0[3],fx[3],fy[3];
2294 double res_x,res_y;
2295 double x_fac,y_fac;
2296 int k;
2297 double len;
2298 /*
2299 * Compute the component integrals
2300 */
2301 field_integrals (dest,tri,&I1,T,S1,S2,f0,fx,fy);
2302 /*
2303 * Compute the coefficient for each node...
2304 */
2305 len = VEC_LEN_40(dir);
2306 dir[X_40] = dir[X_40]/len;
2307 dir[Y_40] = dir[Y_40]/len;
2308 dir[Z_40] = dir[Z_40]/len;
2309
2310 x_fac = -VEC_DOT_40(dir,tri->ex);
2311 y_fac = -VEC_DOT_40(dir,tri->ey);
2312 for (k = 0; k < 3; k++) {
2313 res_x = f0[k]*T[X_40] + fx[k]*S1[X_40] + fy[k]*S2[X_40] + fy[k]*I1;
2314 res_y = f0[k]*T[Y_40] + fx[k]*S1[Y_40] + fy[k]*S2[Y_40] - fx[k]*I1;
2315 res[k] = x_fac*res_x + y_fac*res_y;
2316 }
2317 return;
2318}
2319
2320//=============================================================================================================
2321
2322void FwdBemModel::fwd_bem_one_lin_field_coeff_simple(float *dest, float *normal, MNETriangle* source, double *res) /* The result for each triangle node */
2323/*
2324 * Simple version...
2325 */
2326{
2327 float diff[3];
2328 float vec_result[3];
2329 float dl;
2330 int k;
2331 const float *rr[3];
2332
2333 rr[0] = source->r1;
2334 rr[1] = source->r2;
2335 rr[2] = source->r3;
2336
2337 for (k = 0; k < 3; k++) {
2338 VEC_DIFF_40(rr[k],dest,diff);
2339 dl = VEC_DOT_40(diff,diff);
2340 CROSS_PRODUCT_40(diff,source->nn,vec_result);
2341 res[k] = source->area*VEC_DOT_40(vec_result,normal)/(3.0*dl*sqrt(dl));
2342 }
2343 return;
2344}
2345
2346//=============================================================================================================
2347
2348float **FwdBemModel::fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, int method) /* Which integration formula to use */
2349/*
2350 * Compute the weighting factors to obtain the magnetic field
2351 * in the linear potential approximation
2352 */
2353{
2354 MNESurface* surf;
2355 MNETriangle* tri;
2356 FwdCoil* coil;
2357 FwdCoilSet* tcoils = NULL;
2358 int ntri;
2359 float **coeff = NULL;
2360 int j,k,p,pp,off,s;
2361 double res[3],one[3];
2362 float mult;
2363 linFieldIntFunc func;
2364
2365 if (m->solution == NULL) {
2366 printf("Solution matrix missing in fwd_bem_lin_field_coeff");
2367 return NULL;
2368 }
2369 if (m->bem_method != FWD_BEM_LINEAR_COLL) {
2370 printf("BEM method should be linear collocation for fwd_bem_lin_field_coeff");
2371 return NULL;
2372 }
2373 if (coils->coord_frame != FIFFV_COORD_MRI) {
2374 if (coils->coord_frame == FIFFV_COORD_HEAD) {
2375 if (m->head_mri_t.isEmpty()) {
2376 printf("head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
2377 return NULL;
2378 }
2379 else {
2380 if (!coils) {
2381 qWarning("No coils to duplicate");
2382 return NULL;
2383 }
2384 /*
2385 * Make a transformed duplicate
2386 */
2387 if ((tcoils = coils->dup_coil_set(m->head_mri_t)) == NULL)
2388 return NULL;
2389 coils = tcoils;
2390 }
2391 }
2392 else {
2393 printf("Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->coord_frame);
2394 return NULL;
2395 }
2396 }
2401 else
2403
2404 coeff = ALLOC_CMATRIX_40(coils->ncoil,m->nsol);
2405 for (k = 0; k < m->nsol; k++)
2406 for (j = 0; j < coils->ncoil; j++)
2407 coeff[j][k] = 0.0;
2408 /*
2409 * Process each of the surfaces
2410 */
2411 for (s = 0, off = 0; s < m->nsurf; s++) {
2412 surf = m->surfs[s];
2413 ntri = surf->ntri;
2414 tri = surf->tris.data();
2415 mult = m->field_mult[s];
2416
2417 for (k = 0; k < ntri; k++,tri++) {
2418 for (j = 0; j < coils->ncoil; j++) {
2419 coil = coils->coils[j];
2420 for (pp = 0; pp < 3; pp++)
2421 res[pp] = 0;
2422 /*
2423 * Accumulate the coefficients for each triangle node...
2424 */
2425 for (p = 0; p < coil->np; p++) {
2426 func(coil->rmag[p],coil->cosmag[p],tri,one);
2427 for (pp = 0; pp < 3; pp++)
2428 res[pp] = res[pp] + coil->w[p]*one[pp];
2429 }
2430 /*
2431 * Add these to the corresponding coefficient matrix
2432 * elements...
2433 */
2434 for (pp = 0; pp < 3; pp++)
2435 coeff[j][tri->vert[pp]+off] = coeff[j][tri->vert[pp]+off] + mult*res[pp];
2436 }
2437 }
2438 off = off + surf->np;
2439 }
2440 /*
2441 * Discard the duplicate
2442 */
2443 delete tcoils;
2444 return (coeff);
2445}
2446
2447//=============================================================================================================
2448
2450/*
2451 * Set up for computing the solution at a set of coils
2452 */
2453{
2454 float **sol = NULL;
2455 FwdBemSolution* csol;
2456
2457 if (!m) {
2458 printf("Model missing in fwd_bem_specify_coils");
2459 goto bad;
2460 }
2461 if (!m->solution) {
2462 printf("Solution not computed in fwd_bem_specify_coils");
2463 goto bad;
2464 }
2465 if(coils)
2467 if (!coils || coils->ncoil == 0)
2468 return OK;
2470 sol = fwd_bem_field_coeff(m,coils);
2471 else if (m->bem_method == FWD_BEM_LINEAR_COLL)
2473 else {
2474 printf("Unknown BEM method in fwd_bem_specify_coils : %d",m->bem_method);
2475 goto bad;
2476 }
2477 coils->user_data = csol = new FwdBemSolution();
2479
2480 csol->ncoil = coils->ncoil;
2481 csol->np = m->nsol;
2482 csol->solution = mne_mat_mat_mult_40(sol,
2483 m->solution,
2484 coils->ncoil,
2485 m->nsol,
2486 m->nsol);//TODO: Suspicion, that this is slow - use Eigen
2487
2488 FREE_CMATRIX_40(sol);
2489 return OK;
2490
2491bad : {
2492 FREE_CMATRIX_40(sol);
2493 return FAIL;
2494
2495 }
2496}
2497
2498//=============================================================================================================
2499
2500void FwdBemModel::fwd_bem_lin_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B)
2501/*
2502 * Calculate the magnetic field in a set of coils
2503 */
2504{
2505 float *v0;
2506 int s,k,p,np;
2507 FwdCoil* coil;
2508 float mult;
2509 float my_rd[3],my_Q[3];
2510 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2511 /*
2512 * Infinite-medium potentials
2513 */
2514 if (!m->v0)
2515 m->v0 = MALLOC_40(m->nsol,float);
2516 v0 = m->v0;
2517 /*
2518 * The dipole location and orientation must be transformed
2519 */
2520 VEC_COPY_40(my_rd,rd);
2521 VEC_COPY_40(my_Q,Q);
2522 if (!m->head_mri_t.isEmpty()) {
2525 }
2526 /*
2527 * Compute the inifinite-medium potentials at the vertices
2528 */
2529 for (s = 0, p = 0; s < m->nsurf; s++) {
2530 np = m->surfs[s]->np;
2531 mult = m->source_mult[s];
2532 for (k = 0; k < np; k++)
2533 v0[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,&m->surfs[s]->rr(k,0));
2534 }
2535 /*
2536 * Primary current contribution
2537 * (can be calculated in the coil/dipole coordinates)
2538 */
2539 for (k = 0; k < coils->ncoil; k++) {
2540 coil = coils->coils[k];
2541 B[k] = 0.0;
2542 for (p = 0; p < coil->np; p++)
2543 B[k] = B[k] + coil->w[p]*fwd_bem_inf_field(rd,Q,coil->rmag[p],coil->cosmag[p]);
2544 }
2545 /*
2546 * Volume current contribution
2547 */
2548 for (k = 0; k < coils->ncoil; k++)
2549 B[k] = B[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol);
2550 /*
2551 * Scale correctly
2552 */
2553 for (k = 0; k < coils->ncoil; k++)
2554 B[k] = MAG_FACTOR*B[k];
2555 return;
2556}
2557
2558//=============================================================================================================
2559
2560void FwdBemModel::fwd_bem_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B)
2561/*
2562 * Calculate the magnetic field in a set of coils
2563 */
2564{
2565 float *v0;
2566 int s,k,p,ntri;
2567 FwdCoil* coil;
2568 MNETriangle* tri;
2569 float mult;
2570 float my_rd[3],my_Q[3];
2571 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2572 /*
2573 * Infinite-medium potentials
2574 */
2575 if (!m->v0)
2576 m->v0 = MALLOC_40(m->nsol,float);
2577 v0 = m->v0;
2578 /*
2579 * The dipole location and orientation must be transformed
2580 */
2581 VEC_COPY_40(my_rd,rd);
2582 VEC_COPY_40(my_Q,Q);
2583 if (!m->head_mri_t.isEmpty()) {
2586 }
2587 /*
2588 * Compute the inifinite-medium potentials at the centers of the triangles
2589 */
2590 for (s = 0, p = 0; s < m->nsurf; s++) {
2591 ntri = m->surfs[s]->ntri;
2592 tri = m->surfs[s]->tris.data();
2593 mult = m->source_mult[s];
2594 for (k = 0; k < ntri; k++, tri++)
2595 v0[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,tri->cent.data());
2596 }
2597 /*
2598 * Primary current contribution
2599 * (can be calculated in the coil/dipole coordinates)
2600 */
2601 for (k = 0; k < coils->ncoil; k++) {
2602 coil = coils->coils[k];
2603 B[k] = 0.0;
2604 for (p = 0; p < coil->np; p++)
2605 B[k] = B[k] + coil->w[p]*fwd_bem_inf_field(rd,Q,coil->rmag[p],coil->cosmag[p]);
2606 }
2607 /*
2608 * Volume current contribution
2609 */
2610 for (k = 0; k < coils->ncoil; k++)
2611 B[k] = B[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol);
2612 /*
2613 * Scale correctly
2614 */
2615 for (k = 0; k < coils->ncoil; k++)
2616 B[k] = MAG_FACTOR*B[k];
2617 return;
2618}
2619
2620//=============================================================================================================
2621
2622void FwdBemModel::fwd_bem_field_grad_calc(float *rd, float *Q, FwdCoilSet* coils, FwdBemModel* m, float *xgrad, float *ygrad, float *zgrad)
2623/*
2624 * Calculate the magnetic field in a set of coils
2625 */
2626{
2627 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2628 float *v0;
2629 int s,k,p,ntri,pp;
2630 FwdCoil* coil;
2631 MNETriangle* tri;
2632 float mult;
2633 float *grads[3],ee[3],mri_ee[3],mri_rd[3],mri_Q[3];
2634 float *grad;
2635
2636 grads[0] = xgrad;
2637 grads[1] = ygrad;
2638 grads[2] = zgrad;
2639 /*
2640 * Infinite-medium potentials
2641 */
2642 if (!m->v0)
2643 m->v0 = MALLOC_40(m->nsol,float);
2644 v0 = m->v0;
2645 /*
2646 * The dipole location and orientation must be transformed
2647 */
2648 VEC_COPY_40(mri_rd,rd);
2649 VEC_COPY_40(mri_Q,Q);
2650 if (!m->head_mri_t.isEmpty()) {
2653 }
2654 for (pp = 0; pp < 3; pp++) {
2655 grad = grads[pp];
2656 /*
2657 * Select the correct gradient component
2658 */
2659 for (p = 0; p < 3; p++)
2660 ee[p] = p == pp ? 1.0 : 0.0;
2661 VEC_COPY_40(mri_ee,ee);
2662 if (!m->head_mri_t.isEmpty())
2664 /*
2665 * Compute the inifinite-medium potential derivatives at the centers of the triangles
2666 */
2667 for (s = 0, p = 0; s < m->nsurf; s++) {
2668 ntri = m->surfs[s]->ntri;
2669 tri = m->surfs[s]->tris.data();
2670 mult = m->source_mult[s];
2671 for (k = 0; k < ntri; k++, tri++) {
2672 v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent.data(),mri_ee);
2673 }
2674 }
2675 /*
2676 * Primary current contribution
2677 * (can be calculated in the coil/dipole coordinates)
2678 */
2679 for (k = 0; k < coils->ncoil; k++) {
2680 coil = coils->coils[k];
2681 grad[k] = 0.0;
2682 for (p = 0; p < coil->np; p++)
2683 grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der(rd,Q,coil->rmag[p],coil->cosmag[p],ee);
2684 }
2685 /*
2686 * Volume current contribution
2687 */
2688 for (k = 0; k < coils->ncoil; k++)
2689 grad[k] = grad[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol);
2690 /*
2691 * Scale correctly
2692 */
2693 for (k = 0; k < coils->ncoil; k++)
2694 grad[k] = MAG_FACTOR*grad[k];
2695 }
2696 return;
2697}
2698
2699//=============================================================================================================
2700
2701float FwdBemModel::fwd_bem_inf_field_der(float *rd, float *Q, float *rp, float *dir, float *comp) /* Which gradient component */
2702/*
2703 * Derivative of the infinite-medium magnetic field with respect to
2704 * one of the dipole position coordinates (without \mu_0/4\pi)
2705 */
2706{
2707 float diff[3],diff2,diff3,diff5,cross[3],crossn[3],res;
2708
2709 VEC_DIFF_40(rd,rp,diff);
2710 diff2 = VEC_DOT_40(diff,diff);
2711 diff3 = sqrt(diff2)*diff2;
2712 diff5 = diff3*diff2;
2713 CROSS_PRODUCT_40(Q,diff,cross);
2714 CROSS_PRODUCT_40(dir,Q,crossn);
2715
2716 res = 3*VEC_DOT_40(cross,dir)*VEC_DOT_40(comp,diff)/diff5 - VEC_DOT_40(comp,crossn)/diff3;
2717 return res;
2718}
2719
2720//=============================================================================================================
2721
2722float FwdBemModel::fwd_bem_inf_pot_der(float *rd, float *Q, float *rp, float *comp) /* Which gradient component */
2723/*
2724 * Derivative of the infinite-medium potential with respect to one of
2725 * the dipole position coordinates
2726 */
2727{
2728 float diff[3];
2729 float diff2,diff5,diff3;
2730 float res;
2731
2732 VEC_DIFF_40(rd,rp,diff);
2733 diff2 = VEC_DOT_40(diff,diff);
2734 diff3 = sqrt(diff2)*diff2;
2735 diff5 = diff3*diff2;
2736
2737 res = 3*VEC_DOT_40(Q,diff)*VEC_DOT_40(comp,diff)/diff5 - VEC_DOT_40(comp,Q)/diff3;
2738 return (res/(4.0*M_PI));
2739}
2740
2741//=============================================================================================================
2742
2743void FwdBemModel::fwd_bem_lin_field_grad_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *xgrad, float *ygrad, float *zgrad)
2744/*
2745 * Calculate the gradient with respect to dipole position coordinates in a set of coils
2746 */
2747{
2748 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2749
2750 float *v0;
2751 int s,k,p,np,pp;
2752 FwdCoil *coil;
2753 float mult;
2754 float ee[3],mri_ee[3],mri_rd[3],mri_Q[3];
2755 float *grads[3];
2756 float *grad;
2757
2758 grads[0] = xgrad;
2759 grads[1] = ygrad;
2760 grads[2] = zgrad;
2761 /*
2762 * Space for infinite-medium potentials
2763 */
2764 if (!m->v0)
2765 m->v0 = MALLOC_40(m->nsol,float);
2766 v0 = m->v0;
2767 /*
2768 * The dipole location and orientation must be transformed
2769 */
2770 VEC_COPY_40(mri_rd,rd);
2771 VEC_COPY_40(mri_Q,Q);
2772 if (!m->head_mri_t.isEmpty()) {
2775 }
2776 for (pp = 0; pp < 3; pp++) {
2777 grad = grads[pp];
2778 /*
2779 * Select the correct gradient component
2780 */
2781 for (p = 0; p < 3; p++)
2782 ee[p] = p == pp ? 1.0 : 0.0;
2783 VEC_COPY_40(mri_ee,ee);
2784 if (!m->head_mri_t.isEmpty())
2786 /*
2787 * Compute the inifinite-medium potentials at the vertices
2788 */
2789 for (s = 0, p = 0; s < m->nsurf; s++) {
2790 np = m->surfs[s]->np;
2791 mult = m->source_mult[s];
2792
2793 for (k = 0; k < np; k++)
2794 v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,&m->surfs[s]->rr(k,0),mri_ee);
2795 }
2796 /*
2797 * Primary current contribution
2798 * (can be calculated in the coil/dipole coordinates)
2799 */
2800 for (k = 0; k < coils->ncoil; k++) {
2801 coil = coils->coils[k];
2802 grad[k] = 0.0;
2803 for (p = 0; p < coil->np; p++)
2804 grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der(rd,Q,coil->rmag[p],coil->cosmag[p],ee);
2805 }
2806 /*
2807 * Volume current contribution
2808 */
2809 for (k = 0; k < coils->ncoil; k++)
2810 grad[k] = grad[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol);
2811 /*
2812 * Scale correctly
2813 */
2814 for (k = 0; k < coils->ncoil; k++)
2815 grad[k] = MAG_FACTOR*grad[k];
2816 }
2817 return;
2818}
2819
2820//=============================================================================================================
2821
2822int FwdBemModel::fwd_bem_field(float *rd, float *Q, FwdCoilSet *coils, float *B, void *client) /* The model */
2823/*
2824 * This version calculates the magnetic field in a set of coils
2825 * Call fwd_bem_specify_coils first to establish the coil-specific
2826 * solution matrix
2827 */
2828{
2829 FwdBemModel* m = (FwdBemModel*)client;
2830 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2831
2832 if (!m) {
2833 printf("No BEM model specified to fwd_bem_field");
2834 return FAIL;
2835 }
2836 if (!sol || !sol->solution || sol->ncoil != coils->ncoil) {
2837 printf("No appropriate coil-specific data available in fwd_bem_field");
2838 return FAIL;
2839 }
2841 fwd_bem_field_calc(rd,Q,coils,m,B);
2842 else if (m->bem_method == FWD_BEM_LINEAR_COLL)
2843 fwd_bem_lin_field_calc(rd,Q,coils,m,B);
2844 else {
2845 printf("Unknown BEM method : %d",m->bem_method);
2846 return FAIL;
2847 }
2848 return OK;
2849}
2850
2851//=============================================================================================================
2852
2854 float Q[],
2855 FwdCoilSet *coils,
2856 float Bval[],
2857 float xgrad[],
2858 float ygrad[],
2859 float zgrad[],
2860 void *client) /* Client data to be passed to some foward modelling routines */
2861{
2862 FwdBemModel* m = (FwdBemModel*)client;
2863 FwdBemSolution* sol = (FwdBemSolution*)coils->user_data;
2864
2865 if (!m) {
2866 qCritical("No BEM model specified to fwd_bem_field");
2867 return FAIL;
2868 }
2869
2870 if (!sol || !sol->solution || sol->ncoil != coils->ncoil) {
2871 qCritical("No appropriate coil-specific data available in fwd_bem_field");
2872 return FAIL;
2873 }
2874
2876 if (Bval) {
2878 Q,
2879 coils,
2880 m,
2881 Bval);
2882 }
2883
2885 Q,
2886 coils,
2887 m,
2888 xgrad,
2889 ygrad,
2890 zgrad);
2891 } else if (m->bem_method == FWD_BEM_LINEAR_COLL) {
2892 if (Bval) {
2894 Q,
2895 coils,
2896 m,
2897 Bval);
2898 }
2899
2901 Q,
2902 coils,
2903 m,
2904 xgrad,
2905 ygrad,
2906 zgrad);
2907 } else {
2908 qCritical("Unknown BEM method : %d",m->bem_method);
2909 return FAIL;
2910 }
2911
2912 return OK;
2913}
2914
2915//=============================================================================================================
2916
2918/*
2919 * Compute the MEG or EEG forward solution for one source space
2920 * and possibly for only one source component
2921 */
2922{
2923 FwdThreadArg* a = (FwdThreadArg*)arg;
2924 MNESourceSpace* s = a->s;
2925 int j,p,q;
2926 float *xyz[3];
2927
2928 p = a->off;
2929 q = 3*a->off;
2930 if (a->fixed_ori) { /* The normal source component only */
2931 if (a->field_pot_grad && a->res_grad) { /* Gradient requested? */
2932 for (j = 0; j < s->np; j++) {
2933 if (s->inuse[j]) {
2934 if (a->field_pot_grad(&s->rr(j,0),
2935 &s->nn(j,0),
2936 a->coils_els,
2937 a->res[p],
2938 a->res_grad[q],
2939 a->res_grad[q+1],
2940 a->res_grad[q+2],
2941 a->client) != OK) {
2942 goto bad;
2943 }
2944 q = q + 3;
2945 p++;
2946 }
2947 }
2948 } else {
2949 for (j = 0; j < s->np; j++)
2950 if (s->inuse[j])
2951 if (a->field_pot(&s->rr(j,0),
2952 &s->nn(j,0),
2953 a->coils_els,
2954 a->res[p++],
2955 a->client) != OK)
2956 goto bad;
2957 }
2958 }
2959 else { /* All source components */
2960 if (a->field_pot_grad && a->res_grad) { /* Gradient requested? */
2961 for (j = 0; j < s->np; j++) {
2962 if (s->inuse[j]) {
2963 if (a->comp < 0) { /* Compute all components */
2964 if (a->field_pot_grad(&s->rr(j,0),
2965 Qx,
2966 a->coils_els,
2967 a->res[p],
2968 a->res_grad[q],
2969 a->res_grad[q+1],
2970 a->res_grad[q+2],
2971 a->client) != OK)
2972 goto bad;
2973 q = q + 3; p++;
2974 if (a->field_pot_grad(&s->rr(j,0),
2975 Qy,
2976 a->coils_els,
2977 a->res[p],
2978 a->res_grad[q],
2979 a->res_grad[q+1],
2980 a->res_grad[q+2],
2981 a->client) != OK)
2982 goto bad;
2983 q = q + 3; p++;
2984 if (a->field_pot_grad(&s->rr(j,0),
2985 Qz,
2986 a->coils_els,
2987 a->res[p],
2988 a->res_grad[q],
2989 a->res_grad[q+1],
2990 a->res_grad[q+2],
2991 a->client) != OK)
2992 goto bad;
2993 q = q + 3; p++;
2994 }
2995 else if (a->comp == 0) { /* Compute x component */
2996 if (a->field_pot_grad(&s->rr(j,0),
2997 Qx,
2998 a->coils_els,
2999 a->res[p],
3000 a->res_grad[q],
3001 a->res_grad[q+1],
3002 a->res_grad[q+2],
3003 a->client) != OK)
3004 goto bad;
3005 q = q + 3; p++;
3006 q = q + 3; p++;
3007 q = q + 3; p++;
3008 }
3009 else if (a->comp == 1) { /* Compute y component */
3010 q = q + 3; p++;
3011 if (a->field_pot_grad(&s->rr(j,0),
3012 Qy,
3013 a->coils_els,
3014 a->res[p],
3015 a->res_grad[q],
3016 a->res_grad[q+1],
3017 a->res_grad[q+2],
3018 a->client) != OK)
3019 goto bad;
3020 q = q + 3; p++;
3021 q = q + 3; p++;
3022 }
3023 else if (a->comp == 2) { /* Compute z component */
3024 q = q + 3; p++;
3025 q = q + 3; p++;
3026 if (a->field_pot_grad(&s->rr(j,0),
3027 Qz,
3028 a->coils_els,
3029 a->res[p],
3030 a->res_grad[q],
3031 a->res_grad[q+1],
3032 a->res_grad[q+2],
3033 a->client) != OK)
3034 goto bad;
3035 q = q + 3; p++;
3036 }
3037 }
3038 }
3039 }
3040 else {
3041 for (j = 0; j < s->np; j++) {
3042 if (s->inuse[j]) {
3043 if (a->vec_field_pot) {
3044 xyz[0] = a->res[p++];
3045 xyz[1] = a->res[p++];
3046 xyz[2] = a->res[p++];
3047 if (a->vec_field_pot(&s->rr(j,0),a->coils_els,xyz,a->client) != OK)
3048 goto bad;
3049 }
3050 else {
3051 if (a->comp < 0) { /* Compute all components here */
3052 if (a->field_pot(&s->rr(j,0),Qx,a->coils_els,a->res[p++],a->client) != OK)
3053 goto bad;
3054 if (a->field_pot(&s->rr(j,0),Qy,a->coils_els,a->res[p++],a->client) != OK)
3055 goto bad;
3056 if (a->field_pot(&s->rr(j,0),Qz,a->coils_els,a->res[p++],a->client) != OK)
3057 goto bad;
3058 }
3059 else if (a->comp == 0) { /* Compute x component */
3060 if (a->field_pot(&s->rr(j,0),Qx,a->coils_els,a->res[p++],a->client) != OK)
3061 goto bad;
3062 p++; p++;
3063 }
3064 else if (a->comp == 1) { /* Compute y component */
3065 p++;
3066 if (a->field_pot(&s->rr(j,0),Qy,a->coils_els,a->res[p++],a->client) != OK)
3067 goto bad;
3068 p++;
3069 }
3070 else if (a->comp == 2) { /* Compute z component */
3071 p++; p++;
3072 if (a->field_pot(&s->rr(j,0),Qz,a->coils_els,a->res[p++],a->client) != OK)
3073 goto bad;
3074 }
3075 }
3076 }
3077 }
3078 }
3079 }
3080 a->stat = OK;
3081 return NULL;
3082
3083bad : {
3084 a->stat = FAIL;
3085 return NULL;
3086 }
3087}
3088
3089//=============================================================================================================
3090
3091int FwdBemModel::compute_forward_meg(std::vector<std::unique_ptr<MNESourceSpace>>& spaces,
3092 FwdCoilSet *coils,
3093 FwdCoilSet *comp_coils,
3094 MNECTFCompDataSet *comp_data,
3095 bool fixed_ori,
3096 FwdBemModel *bem_model,
3097 Vector3f *r0,
3098 bool use_threads,
3099 FiffNamedMatrix& resp,
3100 FiffNamedMatrix& resp_grad,
3101 bool bDoGrad)
3102/*
3103 * Compute the MEG forward solution
3104 * Use either the sphere model or BEM in the calculations
3105 */
3106{
3107 float **res = NULL; /* The forward solution matrix */
3108 float **res_grad = NULL; /* The gradient with respect to the dipole position */
3109 MatrixXd matRes;
3110 MatrixXd matResGrad;
3111 int nrow = 0;
3112 FwdCompData *comp = NULL;
3113 fwdFieldFunc field; /* Computes the field for one dipole orientation */
3114 fwdVecFieldFunc vec_field; /* Computes the field for all dipole orientations */
3115 fwdFieldGradFunc field_grad; /* Computes the field and gradient with respect to dipole position
3116 * for one dipole orientation */
3117 int nmeg = coils->ncoil;/* Number of channels */
3118 int nsource; /* Total number of sources */
3119 int nspace = static_cast<int>(spaces.size());
3120 int k,p,q,off;
3121 QStringList names; /* Channel names */
3122 void *client;
3123 FwdThreadArg* one_arg = NULL;
3124 int nproc = QThread::idealThreadCount();
3125 QStringList emptyList;
3126
3127 if (bem_model) {
3128 /*
3129 * Use the new compensated field computation
3130 * It works the same way independent of whether or not the compensation is in effect
3131 */
3132#ifdef TEST
3133 printf("Using differences.\n");
3134 comp = FwdCompData::fwd_make_comp_data(comp_data,
3135 coils,comp_coils,
3137 NULL,
3138 my_bem_field_grad,
3139 bem_model,
3140 NULL);
3141#else
3142 comp = FwdCompData::fwd_make_comp_data(comp_data,
3143 coils,
3144 comp_coils,
3146 NULL,
3148 bem_model,
3149 NULL);
3150#endif
3151 if (!comp)
3152 goto bad;
3153 /*
3154 * Field computation matrices...
3155 */
3156 qDebug() << "!!!TODO Speed the following with Eigen up!";
3157 printf("Composing the field computation matrix...");
3158 if (fwd_bem_specify_coils(bem_model,coils) == FAIL)
3159 goto bad;
3160 printf("[done]\n");
3161
3162 if (comp->set && comp->set->current) { /* Test just to specify confusing output */
3163 printf("Composing the field computation matrix (compensation coils)...");
3164 if (fwd_bem_specify_coils(bem_model,comp->comp_coils) == FAIL)
3165 goto bad;
3166 printf("[done]\n");
3167 }
3169 vec_field = NULL;
3171 client = comp;
3172 }
3173 else {
3174 /*
3175 * Use the new compensated field computation
3176 * It works the same way independent of whether or not the compensation is in effect
3177 */
3178#ifdef TEST
3179 printf("Using differences.\n");
3180 comp = FwdCompData::fwd_make_comp_data(comp_data,coils,comp_coils,
3183 my_sphere_field_grad,
3184 r0,NULL);
3185#else
3186 comp = FwdCompData::fwd_make_comp_data(comp_data,coils,comp_coils,
3190 r0,NULL);
3191#endif
3192 if (!comp)
3193 goto bad;
3197 client = comp;
3198 }
3199 /*
3200 * Count the sources
3201 */
3202 for (k = 0, nsource = 0; k < nspace; k++)
3203 nsource += spaces[k]->nuse;
3204 /*
3205 * Allocate space for the solution
3206 */
3207 if (fixed_ori)
3208 res = ALLOC_CMATRIX_40(nsource,nmeg);
3209 else
3210 res = ALLOC_CMATRIX_40(3*nsource,nmeg);
3211 if (bDoGrad) {
3212 if (fixed_ori)
3213 res_grad = ALLOC_CMATRIX_40(3*nsource,nmeg);
3214 else
3215 res_grad = ALLOC_CMATRIX_40(3*3*nsource,nmeg);
3216 }
3217 /*
3218 * Set up the argument for the field computation
3219 */
3220 one_arg = new FwdThreadArg();
3221 one_arg->res = res;
3222 one_arg->res_grad = res_grad;
3223 one_arg->off = 0;
3224 one_arg->coils_els = coils;
3225 one_arg->client = client;
3226 one_arg->s = NULL;
3227 one_arg->fixed_ori = fixed_ori;
3228 one_arg->field_pot = field;
3229 one_arg->vec_field_pot = vec_field;
3230 one_arg->field_pot_grad = field_grad;
3231
3232 if (nproc < 2)
3233 use_threads = false;
3234
3235 if (use_threads) {
3236 int nthread = (fixed_ori || vec_field || nproc < 6) ? nspace : 3*nspace;
3237 QList <FwdThreadArg*> args; //fwdThreadArg *args = MALLOC_40(nthread,fwdThreadArg);
3238 int stat;
3239 /*
3240 * We need copies to allocate separate workspace for each thread
3241 */
3242 if (fixed_ori || vec_field || nproc < 6) {
3243 for (k = 0, off = 0; k < nthread; k++) {
3244 FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,bem_model != NULL);
3245 t_arg->s = spaces[k].get();
3246 t_arg->off = off;
3247 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
3248 args.append(t_arg);
3249 }
3250 printf("%d processors. I will use one thread for each of the %d source spaces.\n",
3251 nproc,nspace);
3252 }
3253 else {
3254 for (k = 0, off = 0, q = 0; k < nspace; k++) {
3255 for (p = 0; p < 3; p++,q++) {
3256 FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,bem_model != NULL);
3257 t_arg->s = spaces[k].get();
3258 t_arg->off = off;
3259 t_arg->comp = p;
3260 args.append(t_arg);
3261 }
3262 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
3263 }
3264 printf("%d processors. I will use %d threads : %d source spaces x 3 source components.\n",
3265 nproc,nthread,nspace);
3266 }
3267 printf("Computing MEG at %d source locations (%s orientations)...",
3268 nsource,fixed_ori ? "fixed" : "free");
3269 /*
3270 * Ready to start the threads & Wait for them to complete
3271 */
3272 QtConcurrent::blockingMap(args, meg_eeg_fwd_one_source_space);
3273 /*
3274 * Check the results
3275 */
3276 for (k = 0, stat = OK; k < nthread; k++)
3277 if (args[k]->stat != OK) {
3278 stat = FAIL;
3279 break;
3280 }
3281 for (k = 0; k < args.size(); k++)//nthread == args.size()
3282 FwdThreadArg::free_meg_multi_thread_duplicate(args[k],bem_model != NULL);
3283 if (stat != OK)
3284 goto bad;
3285 }
3286 else {
3287 printf("Computing MEG at %d source locations (%s orientations, no threads)...",
3288 nsource,fixed_ori ? "fixed" : "free");
3289 for (k = 0, off = 0; k < nspace; k++) {
3290 one_arg->s = spaces[k].get();
3291 one_arg->off = off;
3293 if (one_arg->stat != OK)
3294 goto bad;
3295 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
3296 }
3297 }
3298 printf("done.\n");
3299 {
3300 QStringList orig_names;
3301 for (k = 0; k < nmeg; k++)
3302 orig_names.append(coils->coils[k]->chname);
3303 names = orig_names;
3304 }
3305 if(one_arg)
3306 delete one_arg;
3307 if(comp)
3308 delete comp;
3309
3310 // Store solution in fiff named matrix
3311 nrow = fixed_ori ? nsource : 3*nsource;
3312 matRes.conservativeResize(nrow,nmeg);
3313 for(int i = 0; i < nrow; i++) {
3314 for(int j = 0; j < nmeg; j++) {
3315 matRes(i,j) = res[i][j];
3316 }
3317 }
3318 resp.nrow = nrow;
3319 resp.ncol = nmeg;
3320 resp.row_names = emptyList;
3321 resp.col_names = names;
3322 resp.data = matRes;
3324
3325 if (bDoGrad && res_grad) {
3326 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
3327 matResGrad = MatrixXd::Zero(nrow,nmeg);
3328 for(int i = 0; i < nrow; i++) {
3329 for(int j = 0; j < nmeg; j++) {
3330 matResGrad(i,j) = res_grad[i][j];
3331 }
3332 }
3333 resp_grad.nrow = nrow;
3334 resp_grad.ncol = nmeg;
3335 resp_grad.row_names = emptyList;
3336 resp_grad.col_names = names;
3337 resp_grad.data = matResGrad;
3338 resp_grad.transpose_named_matrix();
3339 }
3340 return OK;
3341
3342bad : {
3343 if(one_arg)
3344 delete one_arg;
3345 if(comp)
3346 delete comp;
3347 FREE_CMATRIX_40(res);
3348 FREE_CMATRIX_40(res_grad);
3349 return FAIL;
3350 }
3351}
3352
3353//=============================================================================================================
3354
3355int FwdBemModel::compute_forward_eeg(std::vector<std::unique_ptr<MNESourceSpace>>& spaces,
3356 FwdCoilSet *els,
3357 bool fixed_ori,
3358 FwdBemModel *bem_model,
3360 bool use_threads,
3361 FiffNamedMatrix& resp,
3362 FiffNamedMatrix& resp_grad,
3363 bool bDoGrad)
3364/*
3365 * Compute the EEG forward solution
3366 * Use either the sphere model or BEM in the calculations
3367 */
3368{
3369 float **res = NULL; /* The forward solution matrix */
3370 float **res_grad = NULL; /* The gradient with respect to the dipole position */
3371 MatrixXd matRes;
3372 MatrixXd matResGrad;
3373 int nrow = 0;
3374 fwdFieldFunc pot; /* Computes the potentials for one dipole orientation */
3375 fwdVecFieldFunc vec_pot; /* Computes the potentials for all dipole orientations */
3376 fwdFieldGradFunc pot_grad; /* Computes the potential and gradient with respect to dipole position
3377 * for one dipole orientation */
3378 int nsource; /* Total number of sources */
3379 int nspace = static_cast<int>(spaces.size());
3380 int neeg = els->ncoil; /* Number of channels */
3381 int k,p,q,off;
3382 QStringList names; /* Channel names */
3383 void *client;
3384 FwdThreadArg* one_arg = NULL;
3385 int nproc = QThread::idealThreadCount();
3386 QStringList emptyList;
3387 /*
3388 * Count the sources
3389 */
3390 for (k = 0, nsource = 0; k < nspace; k++)
3391 nsource += spaces[k]->nuse;
3392
3393 if (bem_model) {
3394 if (fwd_bem_specify_els(bem_model,els) == FAIL)
3395 goto bad;
3396 client = bem_model;
3397 pot = fwd_bem_pot_els;
3398 vec_pot = NULL;
3399#ifdef TEST
3400 printf("Using differences.\n");
3401 pot_grad = my_bem_pot_grad;
3402#else
3403 pot_grad = fwd_bem_pot_grad_els;
3404#endif
3405 }
3406 else {
3407 if (m->nfit == 0) {
3408 printf("Using the standard series expansion for a multilayer sphere model for EEG\n");
3410 vec_pot = NULL;
3411 pot_grad = NULL;
3412 }
3413 else {
3414 printf("Using the equivalent source approach in the homogeneous sphere for EEG\n");
3418 }
3419 client = m;
3420 }
3421 /*
3422 * Allocate space for the solution
3423 */
3424 if (fixed_ori)
3425 res = ALLOC_CMATRIX_40(nsource,neeg);
3426 else
3427 res = ALLOC_CMATRIX_40(3*nsource,neeg);
3428 if (bDoGrad) {
3429 if (!pot_grad) {
3430 qCritical("EEG gradient calculation function not available");
3431 goto bad;
3432 }
3433 if (fixed_ori)
3434 res_grad = ALLOC_CMATRIX_40(3*nsource,neeg);
3435 else
3436 res_grad = ALLOC_CMATRIX_40(3*3*nsource,neeg);
3437 }
3438 /*
3439 * Set up the argument for the field computation
3440 */
3441 one_arg = new FwdThreadArg();
3442 one_arg->res = res;
3443 one_arg->res_grad = res_grad;
3444 one_arg->off = 0;
3445 one_arg->coils_els = els;
3446 one_arg->client = client;
3447 one_arg->s = NULL;
3448 one_arg->fixed_ori = fixed_ori;
3449 one_arg->field_pot = pot;
3450 one_arg->vec_field_pot = vec_pot;
3451 one_arg->field_pot_grad = pot_grad;
3452
3453 if (nproc < 2)
3454 use_threads = false;
3455
3456 if (use_threads) {
3457 int nthread = (fixed_ori || vec_pot || nproc < 6) ? nspace : 3*nspace;
3458 QList <FwdThreadArg*> args; //FwdThreadArg* *args = MALLOC_40(nthread,FwdThreadArg*);
3459 int stat;
3460 /*
3461 * We need copies to allocate separate workspace for each thread
3462 */
3463 if (fixed_ori || vec_pot || nproc < 6) {
3464 for (k = 0, off = 0; k < nthread; k++) {
3465 FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,bem_model != NULL);
3466 t_arg->s = spaces[k].get();
3467 t_arg->off = off;
3468 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
3469 args.append(t_arg);
3470 }
3471 printf("%d processors. I will use one thread for each of the %d source spaces.\n",nproc,nspace);
3472 }
3473 else {
3474 for (k = 0, off = 0, q = 0; k < nspace; k++) {
3475 for (p = 0; p < 3; p++,q++) {
3476 FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,bem_model != NULL);
3477 t_arg->s = spaces[k].get();
3478 t_arg->off = off;
3479 t_arg->comp = p;
3480 args.append(t_arg);
3481 }
3482 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
3483 }
3484 printf("%d processors. I will use %d threads : %d source spaces x 3 source components.\n",nproc,nthread,nspace);
3485 }
3486 printf("Computing EEG at %d source locations (%s orientations)...",
3487 nsource,fixed_ori ? "fixed" : "free");
3488 /*
3489 * Ready to start the threads & Wait for them to complete
3490 */
3491 QtConcurrent::blockingMap(args, meg_eeg_fwd_one_source_space);
3492 /*
3493 * Check the results
3494 */
3495 for (k = 0, stat = OK; k < nthread; k++)
3496 if (args[k]->stat != OK) {
3497 stat = FAIL;
3498 break;
3499 }
3500 for (k = 0; k < args.size(); k++)//nthread == args.size()
3501 FwdThreadArg::free_eeg_multi_thread_duplicate(args[k],bem_model != NULL);
3502 if (stat != OK)
3503 goto bad;
3504 }
3505 else {
3506 printf("Computing EEG at %d source locations (%s orientations, no threads)...",
3507 nsource,fixed_ori ? "fixed" : "free");
3508 for (k = 0, off = 0; k < nspace; k++) {
3509 one_arg->s = spaces[k].get();
3510 one_arg->off = off;
3512 if (one_arg->stat != OK)
3513 goto bad;
3514 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
3515 }
3516 }
3517 printf("done.\n");
3518 {
3519 QStringList orig_names;
3520 for (k = 0; k < neeg; k++)
3521 orig_names.append(els->coils[k]->chname);
3522 names = orig_names;
3523 }
3524 if(one_arg)
3525 delete one_arg;
3526
3527 // Store solution in fiff named matrix
3528 nrow = fixed_ori ? nsource : 3*nsource;
3529 matRes.conservativeResize(nrow,neeg);
3530 for(int i = 0; i < nrow; i++) {
3531 for(int j = 0; j < neeg; j++) {
3532 matRes(i,j) = res[i][j];
3533 }
3534 }
3535 resp.nrow = nrow;
3536 resp.ncol = neeg;
3537 resp.row_names = emptyList;
3538 resp.col_names = names;
3539 resp.data = matRes;
3541
3542 if (bDoGrad && res_grad) {
3543 matResGrad = MatrixXd::Zero(fixed_ori ? 3*nsource : 3*3*nsource,neeg);
3544 for(int i = 0; i < nrow; i++) {
3545 for(int j = 0; j < neeg; j++) {
3546 matResGrad(i,j) = res_grad[i][j];
3547 }
3548 }
3549 resp_grad.nrow = nrow;
3550 resp_grad.ncol = neeg;
3551 resp_grad.row_names = emptyList;
3552 resp_grad.col_names = names;
3553 resp_grad.data = matResGrad;
3554 resp_grad.transpose_named_matrix();
3555 }
3556 return OK;
3557
3558bad : {
3559 if(one_arg)
3560 delete one_arg;
3561 FREE_CMATRIX_40(res);
3562 FREE_CMATRIX_40(res_grad);
3563 return FAIL;
3564 }
3565}
3566
3567//=============================================================================================================
3568
3569#define EPS 1e-5 /* Points closer to origin than this many
3570 meters are considered to be at the
3571 origin */
3572#define CEPS 1e-5
3573
3574int FwdBemModel::fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */
3575{
3576 /* This version uses Jukka Sarvas' field computation
3577 for details, see
3578
3579 Jukka Sarvas:
3580
3581 Basic mathematical and electromagnetic concepts
3582 of the biomagnetic inverse problem,
3583
3584 Phys. Med. Biol. 1987, Vol. 32, 1, 11-22
3585
3586 The formulas have been manipulated for efficient computation
3587 by Matti Hamalainen, February 1990
3588
3589 */
3590 float *r0 = (float *)client; /* The sphere model origin */
3591 float v[3],a_vec[3];
3592 float a,a2,r,r2;
3593 float ar,ar0,rr0;
3594 float vr,ve,re,r0e;
3595 float F,g0,gr,result,sum;
3596 int j,k,p;
3597 FwdCoil* this_coil;
3598 float *this_pos,*this_dir; /* These point to the coil structure! */
3599 int np;
3600 float myrd[3];
3601 float pos[3];
3602 /*
3603 * Shift to the sphere model coordinates
3604 */
3605 for (p = 0; p < 3; p++)
3606 myrd[p] = rd[p] - r0[p];
3607 rd = myrd;
3608 /*
3609 * Check for a dipole at the origin
3610 */
3611 for (k = 0 ; k < coils->ncoil ; k++)
3612 if (FWD_IS_MEG_COIL(coils->coils[k]->coil_class))
3613 Bval[k] = 0.0;
3614 r = VEC_LEN_40(rd);
3615 if (r > EPS) { /* The hard job */
3616
3617 CROSS_PRODUCT_40(Q,rd,v);
3618
3619 for (k = 0; k < coils->ncoil; k++) {
3620 this_coil = coils->coils[k];
3621 if (FWD_IS_MEG_COIL(this_coil->type)) {
3622
3623 np = this_coil->np;
3624
3625 for (j = 0, sum = 0.0; j < np; j++) {
3626
3627 this_pos = this_coil->rmag[j];
3628 this_dir = this_coil->cosmag[j];
3629
3630 for (p = 0; p < 3; p++)
3631 pos[p] = this_pos[p] - r0[p];
3632 this_pos = pos;
3633 result = 0.0;
3634
3635 /* Vector from dipole to the field point */
3636
3637 VEC_DIFF_40(rd,this_pos,a_vec);
3638
3639 /* Compute the dot products needed */
3640
3641 a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2);
3642
3643 if (a > 0.0) {
3644 r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2);
3645 if (r > 0.0) {
3646 rr0 = VEC_DOT_40(this_pos,rd);
3647 ar = (r2-rr0);
3648 if (std::fabs(ar/(a*r)+1.0) > CEPS) { /* There is a problem on the negative 'z' axis if the dipole location
3649 * and the field point are on the same line */
3650 ar0 = ar/a;
3651
3652 ve = VEC_DOT_40(v,this_dir); vr = VEC_DOT_40(v,this_pos);
3653 re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir);
3654
3655 /* The main ingredients */
3656
3657 F = a*(r*a + ar);
3658 gr = a2/r + ar0 + 2.0*(a+r);
3659 g0 = a + 2*r + ar0;
3660
3661 /* Mix them together... */
3662
3663 sum = sum + this_coil->w[j]*(ve*F + vr*(g0*r0e - gr*re))/(F*F);
3664 }
3665 }
3666 }
3667 } /* All points done */
3668 Bval[k] = MAG_FACTOR*sum;
3669 }
3670 }
3671 }
3672 return OK; /* Happy conclusion: this works always */
3673}
3674
3675//=============================================================================================================
3676
3677int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */
3678{
3679 /* This version uses Jukka Sarvas' field computation
3680 for details, see
3681
3682 Jukka Sarvas:
3683
3684 Basic mathematical and electromagnetic concepts
3685 of the biomagnetic inverse problem,
3686
3687 Phys. Med. Biol. 1987, Vol. 32, 1, 11-22
3688
3689 The formulas have been manipulated for efficient computation
3690 by Matti Hamalainen, February 1990
3691
3692 The idea of matrix kernels is from
3693
3694 Mosher, Leahy, and Lewis: EEG and MEG: Forward Solutions for Inverse Methods
3695
3696 which has been simplified here using standard vector notation
3697
3698 */
3699 float *r0 = (float *)client; /* The sphere model origin */
3700 float a_vec[3],v1[3],v2[3];
3701 float a,a2,r,r2;
3702 float ar,ar0,rr0;
3703 float re,r0e;
3704 float F,g0,gr,g,sum[3];
3705 int j,k,p;
3706 FwdCoil* this_coil;
3707 float *this_pos,*this_dir; /* These point to the coil structure! */
3708 int np;
3709 float myrd[3];
3710 float pos[3];
3711 /*
3712 * Shift to the sphere model coordinates
3713 */
3714 for (p = 0; p < 3; p++)
3715 myrd[p] = rd[p] - r0[p];
3716 rd = myrd;
3717 /*
3718 * Check for a dipole at the origin
3719 */
3720 r = VEC_LEN_40(rd);
3721 for (k = 0; k < coils->ncoil; k++) {
3722 this_coil = coils->coils[k];
3723 if (FWD_IS_MEG_COIL(this_coil->coil_class)) {
3724 if (r < EPS) {
3725 Bval[0][k] = Bval[1][k] = Bval[2][k] = 0.0;
3726 }
3727 else { /* The hard job */
3728
3729 np = this_coil->np;
3730 sum[0] = sum[1] = sum[2] = 0.0;
3731
3732 for (j = 0; j < np; j++) {
3733
3734 this_pos = this_coil->rmag[j];
3735 this_dir = this_coil->cosmag[j];
3736
3737 for (p = 0; p < 3; p++)
3738 pos[p] = this_pos[p] - r0[p];
3739 this_pos = pos;
3740
3741 /* Vector from dipole to the field point */
3742
3743 VEC_DIFF_40(rd,this_pos,a_vec);
3744
3745 /* Compute the dot products needed */
3746
3747 a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2);
3748
3749 if (a > 0.0) {
3750 r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2);
3751 if (r > 0.0) {
3752 rr0 = VEC_DOT_40(this_pos,rd);
3753 ar = (r2-rr0);
3754 if (std::fabs(ar/(a*r)+1.0) > CEPS) { /* There is a problem on the negative 'z' axis if the dipole location
3755 * and the field point are on the same line */
3756
3757 /* The main ingredients */
3758
3759 ar0 = ar/a;
3760 F = a*(r*a + ar);
3761 gr = a2/r + ar0 + 2.0*(a+r);
3762 g0 = a + 2*r + ar0;
3763
3764 re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir);
3765 CROSS_PRODUCT_40(rd,this_dir,v1);
3766 CROSS_PRODUCT_40(rd,this_pos,v2);
3767
3768 g = (g0*r0e - gr*re)/(F*F);
3769 /*
3770 * Mix them together...
3771 */
3772 for (p = 0; p < 3; p++)
3773 sum[p] = sum[p] + this_coil->w[j]*(v1[p]/F + v2[p]*g);
3774 }
3775 }
3776 }
3777 } /* All points done */
3778 for (p = 0; p < 3; p++)
3779 Bval[p][k] = MAG_FACTOR*sum[p];
3780 }
3781 }
3782 }
3783 return OK; /* Happy conclusion: this works always */
3784}
3785
3786//=============================================================================================================
3787
3788int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, float Bval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */
3789/*
3790 * Compute the derivatives of the sphere model field with respect to
3791 * dipole coordinates
3792 */
3793{
3794 /* This version uses Jukka Sarvas' field computation
3795 for details, see
3796
3797 Jukka Sarvas:
3798
3799 Basic mathematical and electromagnetic concepts
3800 of the biomagnetic inverse problem,
3801
3802 Phys. Med. Biol. 1987, Vol. 32, 1, 11-22
3803
3804 The formulas have been manipulated for efficient computation
3805 by Matti Hamalainen, February 1990
3806
3807 */
3808
3809 float v[3],a_vec[3];
3810 float a,a2,r,r2;
3811 float ar,rr0;
3812 float vr,ve,re,r0e;
3813 float F,g0,gr,result,G,F2;
3814
3815 int j,k,p;
3816 float huu;
3817 float ggr[3],gg0[3]; /* Gradient of gr & g0 */
3818 float ga[3]; /* Grapdient of a */
3819 float gar[3]; /* Gradient of ar */
3820 float gFF[3]; /* Gradient of F divided by F */
3821 float gresult[3];
3822 float eQ[3],rQ[3]; /* e x Q and r x Q */
3823 int do_field = 0;
3824 FwdCoil* this_coil;
3825 float *this_pos,*this_dir;
3826 int np;
3827 float myrd[3];
3828 float pos[3];
3829 float *r0 = (float *)client; /* The sphere model origin */
3830 /*
3831 * Shift to the sphere model coordinates
3832 */
3833 for (p = 0; p < 3; p++)
3834 myrd[p] = rd[p] - r0[p];
3835 rd = myrd;
3836
3837 if (Bval)
3838 do_field = 1;
3839
3840 /* Check for a dipole at the origin */
3841
3842 r = VEC_LEN_40(rd);
3843 for (k = 0; k < coils->ncoil ; k++) {
3844 if (FWD_IS_MEG_COIL(coils->coils[k]->coil_class)) {
3845 if (do_field)
3846 Bval[k] = 0.0;
3847 xgrad[k] = 0.0;
3848 ygrad[k] = 0.0;
3849 zgrad[k] = 0.0;
3850 }
3851 }
3852 if (r > EPS) { /* The hard job */
3853
3854 v[X_40] = Q[Y_40]*rd[Z_40] - Q[Z_40]*rd[Y_40];
3855 v[Y_40] = -Q[X_40]*rd[Z_40] + Q[Z_40]*rd[X_40];
3856 v[Z_40] = Q[X_40]*rd[Y_40] - Q[Y_40]*rd[X_40];
3857
3858 for (k = 0 ; k < coils->ncoil ; k++) {
3859
3860 this_coil = coils->coils[k];
3861
3862 if (FWD_IS_MEG_COIL(this_coil->type)) {
3863
3864 np = this_coil->np;
3865
3866 for (j = 0; j < np; j++) {
3867
3868 this_pos = this_coil->rmag[j];
3869 /*
3870 * Shift to the sphere model coordinates
3871 */
3872 for (p = 0; p < 3; p++)
3873 pos[p] = this_pos[p] - r0[p];
3874 this_pos = pos;
3875
3876 this_dir = this_coil->cosmag[j];
3877
3878 /* Vector from dipole to the field point */
3879
3880 a_vec[X_40] = this_pos[X_40] - rd[X_40];
3881 a_vec[Y_40] = this_pos[Y_40] - rd[Y_40];
3882 a_vec[Z_40] = this_pos[Z_40] - rd[Z_40];
3883
3884 /* Compute the dot and cross products needed */
3885
3886 a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2);
3887 r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2);
3888 rr0 = VEC_DOT_40(this_pos,rd);
3889 ar = (r2 - rr0)/a;
3890
3891 ve = VEC_DOT_40(v,this_dir); vr = VEC_DOT_40(v,this_pos);
3892 re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir);
3893
3894 /* eQ = this_dir x Q */
3895
3896 eQ[X_40] = this_dir[Y_40]*Q[Z_40] - this_dir[Z_40]*Q[Y_40];
3897 eQ[Y_40] = -this_dir[X_40]*Q[Z_40] + this_dir[Z_40]*Q[X_40];
3898 eQ[Z_40] = this_dir[X_40]*Q[Y_40] - this_dir[Y_40]*Q[X_40];
3899
3900 /* rQ = this_pos x Q */
3901
3902 rQ[X_40] = this_pos[Y_40]*Q[Z_40] - this_pos[Z_40]*Q[Y_40];
3903 rQ[Y_40] = -this_pos[X_40]*Q[Z_40] + this_pos[Z_40]*Q[X_40];
3904 rQ[Z_40] = this_pos[X_40]*Q[Y_40] - this_pos[Y_40]*Q[X_40];
3905
3906 /* The main ingredients */
3907
3908 F = a*(r*a + r2 - rr0);
3909 F2 = F*F;
3910 gr = a2/r + ar + 2.0*(a+r);
3911 g0 = a + 2.0*r + ar;
3912 G = g0*r0e - gr*re;
3913
3914 /* Mix them together... */
3915
3916 result = (ve*F + vr*G)/F2;
3917
3918 /* The computation of the gradient... */
3919
3920 huu = 2.0 + 2.0*a/r;
3921 for (p = X_40; p <= Z_40; p++) {
3922 ga[p] = -a_vec[p]/a;
3923 gar[p] = -(ga[p]*ar + this_pos[p])/a;
3924 gg0[p] = ga[p] + gar[p];
3925 ggr[p] = huu*ga[p] + gar[p];
3926 gFF[p] = ga[p]/a - (r*a_vec[p] + a*this_pos[p])/F;
3927 gresult[p] = -2.0*result*gFF[p] + (eQ[p]+gFF[p]*ve)/F +
3928 (rQ[p]*G + vr*(gg0[p]*r0e + g0*this_dir[p] - ggr[p]*re))/F2;
3929 }
3930
3931 if (do_field)
3932 Bval[k] = Bval[k] + this_coil->w[j]*result;
3933 xgrad[k] = xgrad[k] + this_coil->w[j]*gresult[X_40];
3934 ygrad[k] = ygrad[k] + this_coil->w[j]*gresult[Y_40];
3935 zgrad[k] = zgrad[k] + this_coil->w[j]*gresult[Z_40];
3936 }
3937 if (do_field)
3938 Bval[k] = MAG_FACTOR*Bval[k];
3939 xgrad[k] = MAG_FACTOR*xgrad[k];
3940 ygrad[k] = MAG_FACTOR*ygrad[k];
3941 zgrad[k] = MAG_FACTOR*zgrad[k];
3942 }
3943 }
3944 }
3945 return OK; /* Happy conclusion: this works always */
3946}
3947
3948//=============================================================================================================
3949
3950int FwdBemModel::fwd_mag_dipole_field(float *rm, float M[], FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */
3951/*
3952 * This is for a specific dipole component
3953 */
3954{
3955 int j,k,np;
3956 FwdCoil* this_coil;
3957 float sum,diff[3],dist,dist2,dist5,*dir;
3958
3959 for (k = 0; k < coils->ncoil; k++) {
3960 this_coil = coils->coils[k];
3961 if (FWD_IS_MEG_COIL(this_coil->type)) {
3962 np = this_coil->np;
3963 /*
3964 * Go through all points
3965 */
3966 for (j = 0, sum = 0.0; j < np; j++) {
3967 dir = this_coil->cosmag[j];
3968 VEC_DIFF_40(rm,this_coil->rmag[j],diff);
3969 dist = VEC_LEN_40(diff);
3970 if (dist > EPS) {
3971 dist2 = dist*dist;
3972 dist5 = dist2*dist2*dist;
3973 sum = sum + this_coil->w[j]*(3*VEC_DOT_40(M,diff)*VEC_DOT_40(diff,dir) - dist2*VEC_DOT_40(M,dir))/dist5;
3974 }
3975 } /* All points done */
3976 Bval[k] = MAG_FACTOR*sum;
3977 }
3978 else if (this_coil->type == FWD_COILC_EEG)
3979 Bval[k] = 0.0;
3980 }
3981 return OK;
3982}
3983
3984//=============================================================================================================
3985
3986int FwdBemModel::fwd_mag_dipole_field_vec(float *rm, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */
3987/*
3988 * This is for all dipole components
3989 * For EEG this produces a zero result
3990 */
3991{
3992 int j,k,p,np;
3993 FwdCoil* this_coil;
3994 float sum[3],diff[3],dist,dist2,dist5,*dir;
3995
3996 for (k = 0; k < coils->ncoil; k++) {
3997 this_coil = coils->coils[k];
3998 if (FWD_IS_MEG_COIL(this_coil->type)) {
3999 np = this_coil->np;
4000 sum[0] = sum[1] = sum[2] = 0.0;
4001 /*
4002 * Go through all points
4003 */
4004 for (j = 0; j < np; j++) {
4005 dir = this_coil->cosmag[j];
4006 VEC_DIFF_40(rm,this_coil->rmag[j],diff);
4007 dist = VEC_LEN_40(diff);
4008 if (dist > EPS) {
4009 dist2 = dist*dist;
4010 dist5 = dist2*dist2*dist;
4011 for (p = 0; p < 3; p++)
4012 sum[p] = sum[p] + this_coil->w[j]*(3*diff[p]*VEC_DOT_40(diff,dir) - dist2*dir[p])/dist5;
4013 }
4014 } /* All points done */
4015 for (p = 0; p < 3; p++)
4016 Bval[p][k] = MAG_FACTOR*sum[p];
4017 }
4018 else if (this_coil->type == FWD_COILC_EEG) {
4019 for (p = 0; p < 3; p++)
4020 Bval[p][k] = 0.0;
4021 }
4022 }
4023 return OK;
4024}
#define M_PI
#define FIFFV_MNE_COORD_MNI_TAL
#define FIFFV_MNE_COORD_FS_TAL_LTZ
#define FIFFV_COORD_MRI_SLICE
#define FIFFV_MNE_COORD_CTF_DEVICE
#define FIFFV_COORD_DEVICE
#define FIFFV_COORD_MRI_DISPLAY
#define FIFFV_MNE_COORD_MRI_VOXEL
#define FIFFV_MNE_COORD_CTF_HEAD
#define FIFFV_COORD_HPI
#define FIFFV_NO_MOVE
#define FIFFV_COORD_HEAD
#define FIFFV_COORD_MRI
#define FIFFV_COORD_ISOTRAK
#define FIFFV_COORD_UNKNOWN
#define FIFFV_MNE_COORD_FS_TAL_GTZ
#define FIFFV_MOVE
#define FIFFV_MNE_COORD_RAS
FiffNamedMatrix class declaration.
FiffStream class declaration.
#define FIFF_BEM_APPROX
Definition fiff_file.h:742
#define FIFFV_BEM_APPROX_LINEAR
Definition fiff_file.h:779
#define FIFFT_INT
Definition fiff_file.h:231
#define FIFFV_BEM_SURF_ID_SKULL
Definition fiff_file.h:751
#define FIFFB_BEM
Definition fiff_file.h:403
#define FIFFV_BEM_APPROX_CONST
Definition fiff_file.h:778
#define FIFFV_BEM_SURF_ID_HEAD
Definition fiff_file.h:752
#define FIFFV_BEM_SURF_ID_BRAIN
Definition fiff_file.h:749
#define FIFF_BEM_POT_SOLUTION
Definition fiff_file.h:741
MNETriangle class declaration.
MNESurface class declaration.
MNESourceSpace class declaration.
Forward BEM Solution (FwdBemSolution) class declaration.
int(* fwdFieldFunc)(float *rd, float *Q, FWDLIB::FwdCoilSet *coils, float *res, void *client)
Definition fwd_types.h:20
int(* fwdVecFieldFunc)(float *rd, FWDLIB::FwdCoilSet *coils, float **res, void *client)
Definition fwd_types.h:21
int(* fwdFieldGradFunc)(float *rd, float *Q, FWDLIB::FwdCoilSet *coils, float *res, float *xgrad, float *ygrad, float *zgrad, void *client)
Definition fwd_types.h:22
FwdCompData class declaration.
#define TRUE
#define FALSE
#define OK
#define FAIL
#define FWD_COILC_EEG
Definition fwd_coil.h:63
#define FWD_IS_MEG_COIL(x)
Definition fwd_coil.h:73
#define X_40
void fromFloatEigenMatrix_40(const Eigen::MatrixXf &from_mat, float **&to_mat, const int m, const int n)
#define FREE_40(x)
#define Z_40
void mne_transpose_square_40(float **mat, int n)
void mne_add_scaled_vector_to_40(float *v1, float scale, float *v2, int nn)
float ** mne_lu_invert_40(float **mat, int dim)
#define VEC_COPY_40(to, from)
#define ALLOC_CMATRIX_40(x, y)
#define BEM_SOL_SUFFIX
#define VEC_LEN_40(x)
#define CEPS
#define EPS
Eigen::MatrixXf toFloatEigenMatrix_40(float **mat, const int m, const int n)
#define FREE_CMATRIX_40(m)
const QString mne_coord_frame_name_40(int frame)
#define Y_40
#define VEC_DOT_40(x, y)
float mne_dot_vectors_40(float *v1, float *v2, int nn)
float ** mne_mat_mat_mult_40(float **m1, float **m2, int d1, int d2, int d3)
#define ARSINH(x)
#define VEC_DIFF_40(from, to, diff)
void mne_scale_vector_40(double scale, float *v, int nn)
#define MALLOC_40(x, t)
#define CROSS_PRODUCT_40(x, y, xy)
float ** mne_cmatrix_40(int nr, int nc)
void mne_free_cmatrix_40(float **m)
FwdEegSphereModel class declaration.
Fwd Thread Argument (FwdThreadArg) class declaration.
FwdBemModel class declaration.
#define FWD_BEM_CONSTANT_COLL
#define FWD_BEM_UNKNOWN
#define FWD_BEM_LIN_FIELD_URANKAR
#define FWD_BEM_IP_APPROACH_LIMIT
#define FWD_BEM_LIN_FIELD_FERGUSON
#define FWD_BEM_LINEAR_COLL
#define MAG_FACTOR
#define FWD_BEM_LIN_FIELD_SIMPLE
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
Forward modelling (BEM, MEG/EEG lead fields).
Definition compute_fwd.h:95
const QString name
Coordinate transformation description.
FiffCoordTrans inverted() const
QSharedPointer< FiffDirNode > SPtr
FIFF File I/O routines.
QSharedPointer< FiffStream > SPtr
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155
Lookup record mapping a FIFF coordinate frame integer ID to its human-readable name.
static int fwd_mag_dipole_field_vec(float *rm, FwdCoilSet *coils, float **Bval, void *client)
static int compute_forward_eeg(std::vector< std::unique_ptr< MNELIB::MNESourceSpace > > &spaces, FwdCoilSet *els, bool fixed_ori, FwdBemModel *bem_model, FwdEegSphereModel *m, bool use_threads, FIFFLIB::FiffNamedMatrix &resp, FIFFLIB::FiffNamedMatrix &resp_grad, bool bDoGrad)
static int fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, float Bval[], float xgrad[], float ygrad[], float zgrad[], void *client)
static float ** fwd_bem_field_coeff(FwdBemModel *m, FwdCoilSet *coils)
static FwdBemModel * fwd_bem_load_homog_surface(const QString &name)
static int get_int(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &node, int what, int *res)
static int fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval, void *client)
static float ** fwd_bem_multi_solution(float **solids, float **gamma, int nsurf, int *ntri)
static double calc_beta(double *rk, double *rk1)
static void * meg_eeg_fwd_one_source_space(void *arg)
static void correct_auto_elements(MNELIB::MNESurface *surf, float **mat)
static void fwd_bem_field_grad_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *xgrad, float *ygrad, float *zgrad)
static int fwd_bem_set_head_mri_t(FwdBemModel *m, const FIFFLIB::FiffCoordTrans &t)
static int fwd_bem_constant_collocation_solution(FwdBemModel *m)
static void fwd_bem_one_lin_field_coeff_uran(float *dest, float *dir, MNELIB::MNETriangle *tri, double *res)
static int fwd_bem_field(float *rd, float *Q, FwdCoilSet *coils, float *B, void *client)
static void fwd_bem_ip_modify_solution(float **solution, float **ip_solution, float ip_mult, int nsurf, int *ntri)
static double one_field_coeff(float *dest, float *normal, MNELIB::MNETriangle *tri)
static int fwd_bem_check_solids(float **angles, int ntri1, int ntri2, float desired)
static int fwd_bem_pot_grad_els(float *rd, float *Q, FwdCoilSet *els, float *pot, float *xgrad, float *ygrad, float *zgrad, void *client)
static float ** fwd_bem_solid_angles(const QList< MNELIB::MNESurface * > &surfs)
static float ** fwd_bem_lin_pot_coeff(const QList< MNELIB::MNESurface * > &surfs)
static FwdBemModel * fwd_bem_load_surfaces(const QString &name, int *kinds, int nkind)
static int fwd_bem_load_solution(const QString &name, int bem_method, FwdBemModel *m)
static void fwd_bem_lin_field_grad_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *xgrad, float *ygrad, float *zgrad)
static void calc_f(double *xx, double *yy, double *f0, double *fx, double *fy)
static QString fwd_bem_make_bem_sol_name(const QString &name)
static int fwd_bem_load_recompute_solution(const QString &name, int bem_method, int force_recompute, FwdBemModel *m)
static float ** fwd_bem_homog_solution(float **solids, int ntri)
static double calc_gamma(double *rk, double *rk1)
static void fwd_bem_pot_grad_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *xgrad, float *ygrad, float *zgrad)
MNELIB::MNESurface * fwd_bem_find_surface(int kind)
static void field_integrals(float *from, MNELIB::MNETriangle *to, double *I1p, double *T, double *S1, double *S2, double *f0, double *fx, double *fy)
static float fwd_bem_inf_pot(float *rd, float *Q, float *rp)
static void lin_pot_coeff(float *from, MNELIB::MNETriangle *to, double omega[3])
static float fwd_bem_inf_field_der(float *rd, float *Q, float *rp, float *dir, float *comp)
static const QString & fwd_bem_explain_method(int method)
static float ** fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, int method)
static int compute_forward_meg(std::vector< std::unique_ptr< MNELIB::MNESourceSpace > > &spaces, FwdCoilSet *coils, FwdCoilSet *comp_coils, MNELIB::MNECTFCompDataSet *comp_data, bool fixed_ori, FwdBemModel *bem_model, Eigen::Vector3f *r0, bool use_threads, FIFFLIB::FiffNamedMatrix &resp, FIFFLIB::FiffNamedMatrix &resp_grad, bool bDoGRad)
static int fwd_bem_linear_collocation_solution(FwdBemModel *m)
static int fwd_bem_specify_coils(FwdBemModel *m, FwdCoilSet *coils)
static void fwd_bem_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B)
static void fwd_bem_lin_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B)
static int fwd_bem_field_grad(float *rd, float Q[], FwdCoilSet *coils, float Bval[], float xgrad[], float ygrad[], float zgrad[], void *client)
static const QString & fwd_bem_explain_surface(int kind)
static int fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float Bval[], void *client)
static void fwd_bem_one_lin_field_coeff_simple(float *dest, float *normal, MNELIB::MNETriangle *source, double *res)
static void fwd_bem_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot)
void(* linFieldIntFunc)(float *dest, float *dir, MNELIB::MNETriangle *tri, double *res)
QList< MNELIB::MNESurface * > surfs
static void fwd_bem_lin_pot_grad_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *xgrad, float *ygrad, float *zgrad)
static MNELIB::MNESurface * make_guesses(MNELIB::MNESurface *guess_surf, float guessrad, float *guess_r0, float grid, float exclude, float mindist)
static int fwd_bem_specify_els(FwdBemModel *m, FwdCoilSet *els)
static FwdBemModel * fwd_bem_load_three_layer_surfaces(const QString &name)
static void calc_magic(double u, double z, double A, double B, double *beta, double *D)
static int fwd_bem_pot_els(float *rd, float *Q, FwdCoilSet *els, float *pot, void *client)
static void fwd_bem_lin_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot)
static int fwd_mag_dipole_field(float *rm, float M[], FwdCoilSet *coils, float Bval[], void *client)
static int fwd_bem_compute_solution(FwdBemModel *m, int bem_method)
FIFFLIB::FiffCoordTrans head_mri_t
static float fwd_bem_inf_field(float *rd, float *Q, float *rp, float *dir)
static float fwd_bem_inf_pot_der(float *rd, float *Q, float *rp, float *comp)
static void fwd_bem_one_lin_field_coeff_ferg(float *dest, float *dir, MNELIB::MNETriangle *tri, double *res)
Mapping from infinite medium potentials to a particular set of coils or electrodes.
static void fwd_bem_free_coil_solution(void *user)
Single MEG or EEG sensor coil with integration points, weights, and coordinate frame.
Definition fwd_coil.h:89
float ** rmag
Definition fwd_coil.h:180
QString chname
Definition fwd_coil.h:167
float ** cosmag
Definition fwd_coil.h:181
Collection of FwdCoil objects representing a full MEG or EEG sensor array.
void fwd_free_coil_set_user_data()
FwdCoilSet * dup_coil_set(const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans()) const
fwdUserFreeFunc user_data_free
This structure is used in the compensated field calculations.
MNELIB::MNECTFCompDataSet * set
FwdCoilSet * comp_coils
static FwdCompData * fwd_make_comp_data(MNELIB::MNECTFCompDataSet *set, FwdCoilSet *coils, FwdCoilSet *comp_coils, fwdFieldFunc field, fwdVecFieldFunc vec_field, fwdFieldGradFunc field_grad, void *client, fwdUserFreeFunc client_free)
static int fwd_comp_field_grad(float *rd, float *Q, FwdCoilSet *coils, float *res, float *xgrad, float *ygrad, float *zgrad, void *client)
static int fwd_comp_field_vec(float *rd, FwdCoilSet *coils, float **res, void *client)
static int fwd_comp_field(float *rd, float *Q, FwdCoilSet *coils, float *res, void *client)
Multi-layer spherical head model for EEG forward computation.
static int fwd_eeg_multi_spherepot_coil1(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client)
static int fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet *els, float **Vval_vec, void *client)
static int fwd_eeg_spherepot_coil(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client)
static int fwd_eeg_spherepot_grad_coil(float *rd, float Q[], FwdCoilSet *coils, float Vval[], float xgrad[], float ygrad[], float zgrad[], void *client)
Thread-local arguments for parallel forward field computation (source range, coils,...
fwdFieldGradFunc field_pot_grad
static void free_meg_multi_thread_duplicate(FwdThreadArg *one, bool bem_model)
static FwdThreadArg * create_eeg_multi_thread_duplicate(FwdThreadArg *one, bool bem_model)
static void free_eeg_multi_thread_duplicate(FwdThreadArg *one, bool bem_model)
fwdVecFieldFunc vec_field_pot
static FwdThreadArg * create_meg_multi_thread_duplicate(FwdThreadArg *one, bool bem_model)
MNELIB::MNESourceSpace * s
Collection of CTF third-order gradient compensation operators.
std::unique_ptr< MNECTFCompData > current
This defines a source space.
static MNESourceSpace * make_volume_source_space(const MNESurface &surf, float grid, float exclude, float mindist)
This defines a surface.
Definition mne_surface.h:79
void triangle_coords(const Eigen::Vector3f &r, int tri, float &x, float &y, float &z) const
int project_to_surface(const MNEProjData *proj_data, const Eigen::Vector3f &r, float &distp) const
static MNESurface * read_bem_surface(const QString &name, int which, int add_geometry, float *sigmap)
int add_geometry_info(int do_normals, int check_too_many_neighbors)
static double solid_angle(const Eigen::Vector3f &from, const MNELIB::MNETriangle &tri)
std::vector< MNETriangle > tris
Per-triangle geometric data for a cortical or BEM surface.
Eigen::Vector3f nn
Eigen::Vector3f ex
Eigen::Vector3f ey
Eigen::Vector3f cent
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const