60#include <QtConcurrent>
62#define _USE_MATH_DEFINES
67static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
68static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
69static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
79constexpr int FAIL = -1;
81constexpr int LOADED = 1;
82constexpr int NOT_FOUND = 0;
84constexpr auto BEM_SUFFIX =
"-bem.fif";
85constexpr auto BEM_SOL_SUFFIX =
"-bem-sol.fif";
86constexpr float EPS = 1e-5f;
87constexpr float CEPS = 1e-5f;
114static QString strip_from(
const QString& s,
const QString& suffix)
118 if (s.endsWith(suffix)) {
120 res.chop(suffix.size());
164 for (k = 0; frames[k].
frame != -1; k++) {
165 if (frame == frames[k].frame)
166 return frames[k].
name;
168 return frames[k].
name;
175using namespace Eigen;
222 s1 = strip_from(name,
".fif");
223 s2 = strip_from(s1,
"-sol");
224 s1 = strip_from(s2,
"-bem");
225 s2 = QString(
"%1%2").arg(s1).arg(BEM_SOL_SUFFIX);
235 for (k = 0; surf_expl[k].kind >= 0; k++)
236 if (surf_expl[k].kind == kind)
237 return surf_expl[k].name;
239 return surf_expl[k].name;
249 for (k = 0; method_expl[k].method >= 0; k++)
250 if (method_expl[k].method == method)
251 return method_expl[k].name;
253 return method_expl[k].name;
264 if(node->find_tag(stream, what, t_pTag)) {
266 qWarning(
"Expected an integer tag : %d (found data type %d instead)",what,t_pTag->getType() );
269 *res = *t_pTag->toInt();
279 for (
int k = 0; k < this->
nsurf; k++)
280 if (this->
surfs[k]->
id == kind)
281 return this->
surfs[k].get();
293 std::vector<std::shared_ptr<MNESurface>>
surfs;
294 const int nkind =
static_cast<int>(kinds.size());
295 Eigen::VectorXf sigma_tmp(nkind);
299 qCritical(
"No surfaces specified to fwd_bem_load_surfaces");
303 for (k = 0; k < nkind; k++) {
314 qCritical(
"FsSurface %s not specified in MRI coordinates.",
fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
319 surfs.push_back(std::shared_ptr<MNESurface>(s));
321 auto m = std::make_unique<FwdBemModel>();
325 m->surfs = std::move(
surfs);
326 m->sigma = sigma_tmp;
327 m->ntri.resize(nkind);
329 m->gamma.resize(nkind, nkind);
330 m->source_mult.resize(nkind);
331 m->field_mult.resize(nkind);
335 Eigen::VectorXf sigma1(nkind + 1);
337 sigma1.tail(nkind) = m->sigma;
342 for (j = 0; j < m->nsurf; j++) {
343 m->ntri[j] = m->surfs[j]->ntri;
344 m->np[j] = m->surfs[j]->np;
345 m->source_mult[j] = 2.0f / (sigma1[j+1] + sigma1[j]);
346 m->field_mult[j] = sigma1[j+1] - sigma1[j];
347 for (k = 0; k < m->nsurf; k++)
348 m->gamma(j, k) = (sigma1[k+1] - sigma1[k]) / (sigma1[j+1] + sigma1[j]);
389 if(!stream->open()) {
398 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(
FIFFB_BEM);
400 if (nodes.size() == 0) {
401 qWarning(
"No BEM data in %s",name.toUtf8().constData());
419 qWarning(
"Cannot handle BEM approximation method : %d",method);
424 qWarning(
"Approximation method in file : %d desired : %d",method,
bem_method);
436 QVector<qint32> dims;
437 t_pTag->getMatrixDimensions(ndim, dims);
440 qWarning(
"Expected a two-dimensional solution matrix instead of a %d dimensional one",ndim);
444 for (k = 0, dim = 0; k <
nsurf; k++)
446 if (dims[0] != dim || dims[1] != dim) {
447 qWarning(
"Expected a %d x %d solution matrix instead of a %d x %d one",dim,dim,dims[0],dims[1]);
452 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
457 solution = t_pTag->toFloatMatrix().transpose();
459 this->bem_method = method;
481 qWarning(
"Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
497 qInfo(
"Making a spherical guess space with radius %7.1f mm...",1000*guessrad);
499 QFile bemFile(QString(QCoreApplication::applicationDirPath() +
"/../resources/general/surf2bem/icos.fif"));
500 if ( !QCoreApplication::startingUp() )
501 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString(
"/../resources/general/surf2bem/icos.fif"));
502 else if (!bemFile.exists())
503 bemFile.setFileName(
"../resources/general/surf2bem/icos.fif");
505 if( !bemFile.exists () ){
506 qDebug() << bemFile.fileName() <<
"does not exists.";
510 bemname = bemFile.fileName();
515 sphere_owner.reset(sphere);
517 for (k = 0; k < sphere->
np; k++) {
518 dist = sphere->
point(k).norm();
519 sphere->
rr.row(k) = (guessrad * sphere->
rr.row(k) / dist) + guess_r0.transpose();
526 qInfo(
"Guess surface (%d = %s) is in %s coordinates",
530 qInfo(
"Filtering (grid = %6.f mm)...",1000*grid);
541 Eigen::Vector3d rkk1 = rk1 - rk;
542 double size = rkk1.norm();
544 return log((rk.norm() * size + rk.dot(rkk1)) /
545 (rk1.norm() * size + rk1.dot(rkk1))) / size;
555 Eigen::Vector3d y1, y2, y3;
558 Eigen::Vector3d vec_omega;
561 double beta[3],bbeta[3];
564 static const double solid_eps = 4.0*
M_PI/1.0E6;
568 y1 = (to.
r1 - from).cast<double>();
569 y2 = (to.
r2 - from).cast<double>();
570 y3 = (to.
r3 - from).cast<double>();
574 const Eigen::Vector3d* y_arr[5] = { &y3, &y1, &y2, &y3, &y1 };
575 const Eigen::Vector3d** yy = y_arr + 1;
579 Eigen::Vector3d cross = y1.cross(y2);
580 triple = cross.dot(y3);
585 ss = (l1*l2*l3+y1.dot(y2)*l3+y1.dot(y3)*l2+y2.dot(y3)*l1);
586 solid = 2.0*atan2(triple,ss);
587 if (std::fabs(solid) < solid_eps) {
594 for (j = 0; j < 3; j++)
596 bbeta[0] = beta[2] - beta[0];
597 bbeta[1] = beta[0] - beta[1];
598 bbeta[2] = beta[1] - beta[2];
601 for (j = 0; j < 3; j++)
602 vec_omega += bbeta[j] * (*yy[j]);
607 n2 = 1.0/(area2*area2);
608 Eigen::Vector3d nn_d = to.
nn.cast<
double>();
609 for (k = 0; k < 3; k++) {
610 Eigen::Vector3d z = yy[k+1]->cross(*yy[k-1]);
611 Eigen::Vector3d diff = *yy[k-1] - *yy[k+1];
612 omega[k] = n2*(-area2*z.dot(nn_d)*solid +
613 triple*diff.dot(vec_omega));
622 double rel1 = (solid + omega[0]+omega[1]+omega[2])/solid;
626 Eigen::Vector3d check = Eigen::Vector3d::Zero();
627 Eigen::Vector3d nn_check = to.
nn.cast<
double>();
628 for (k = 0; k < 3; k++) {
629 Eigen::Vector3d z = nn_check.cross(*yy[k]);
632 check *= -area2/triple;
633 fprintf (stderr,
"(%g,%g,%g) =? (%g,%g,%g)\n",
634 check[0],check[1],check[2],
635 vec_omega[0],vec_omega[1],vec_omega[2]);
637 double rel2 = sqrt(check.dot(check)/vec_omega.dot(vec_omega));
638 fprintf (stderr,
"err1 = %g, err2 = %g\n",100*rel1,100*rel2);
655 float pi2 = 2.0*
M_PI;
659 for (j = 0; j < nnode; j++) {
661 for (k = 0; k < nnode; k++)
662 sum = sum + mat(j,k);
663 fprintf (stderr,
"row %d sum = %g missing = %g\n",j+1,sum/pi2,
665 mat(j,j) = pi2 - sum;
668 for (j = 0; j < nnode; j++) {
673 for (k = 0; k < nnode; k++)
674 sum = sum + mat(j,k);
684 miss = miss/(4.0*nmemb);
685 for (k = 0,tri = surf.
tris.data(); k <
ntri; k++,tri++) {
686 if (tri->
vert[0] == j) {
687 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
688 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
690 else if (tri->
vert[1] == j) {
691 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
692 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
694 else if (tri->
vert[2] == j) {
695 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
696 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
718 int np1,np2,
ntri,np_tot,np_max;
720 Eigen::Vector3d omega;
726 for (p = 0, np_tot = np_max = 0; p <
surfs.size(); p++) {
727 np_tot +=
surfs[p]->np;
729 np_max =
surfs[p]->np;
732 Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(np_tot, np_tot);
733 Eigen::VectorXd row(np_max);
734 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + np1) {
737 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + np2) {
742 qInfo(
"\t\t%s (%d) -> %s (%d) ... ",
746 for (j = 0; j < np1; j++) {
747 for (k = 0; k < np2; k++)
749 for (k = 0, tri = surf2->
tris.data(); k <
ntri; k++,tri++) {
754 if (p == q && (tri->
vert[0] == j || tri->
vert[1] == j || tri->
vert[2] == j))
760 for (c = 0; c < 3; c++)
761 row[tri->
vert[c]] = row[tri->
vert[c]] - omega[c];
763 for (k = 0; k < np2; k++)
764 mat(j+joff,k+koff) = row[k];
767 Eigen::MatrixXf sub_mat = mat.block(joff, koff, np1, np1);
769 mat.block(joff, koff, np1, np1) = sub_mat;
784 Eigen::MatrixXf coeff;
791 std::vector<MNESurface*> rawSurfs;
792 rawSurfs.reserve(
nsurf);
793 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
795 qInfo(
"\nComputing the linear collocation solution...");
796 qInfo(
"\tMatrix coefficients...");
798 if (coeff.size() == 0) {
806 qInfo(
"\tInverting the coefficient matrix...");
818 Eigen::MatrixXf ip_solution;
820 qInfo(
"IP approach required...");
822 qInfo(
"\tMatrix coefficients (homog)...");
823 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
825 if (coeff.size() == 0) {
830 qInfo(
"\tInverting the coefficient matrix (homog)...");
832 if (ip_solution.size() == 0) {
837 qInfo(
"\tModify the original solution to incorporate IP approach...");
842 qInfo(
"Solution ready.");
858 float pi2 = 1.0/(2*
M_PI);
860 int joff,koff,jup,kup,ntot;
862 for (j = 0,ntot = 0; j <
nsurf; j++)
868 for (p = 0, joff = 0; p <
nsurf; p++) {
869 jup =
ntri[p] + joff;
870 for (q = 0, koff = 0; q <
nsurf; q++) {
871 kup =
ntri[q] + koff;
872 mult = (
gamma ==
nullptr) ? pi2 : pi2 * (*gamma)(p, q);
873 for (j = joff; j < jup; j++)
874 for (k = koff; k < kup; k++)
875 solids(j,k) = defl - solids(j,k)*mult;
880 for (k = 0; k < ntot; k++)
881 solids(k,k) = solids(k,k) + 1.0;
883 Eigen::MatrixXf result = solids.inverse();
908 int j,k,joff,koff,ntot,nlast;
911 for (s = 0, koff = 0; s <
nsurf-1; s++)
912 koff = koff +
ntri[s];
916 Eigen::VectorXf row(nlast);
917 mult = (1.0 + ip_mult)/ip_mult;
919 qInfo(
"\t\tCombining...");
921 ip_solution.transposeInPlace();
923 for (s = 0, joff = 0; s <
nsurf; s++) {
929 for (j = 0; j <
ntri[s]; j++) {
930 for (k = 0; k < nlast; k++) {
931 row[k] =
solution.row(j + joff).segment(koff, nlast).dot(ip_solution.row(k).head(nlast));
933 solution.row(j + joff).segment(koff, nlast) -= 2.0f * row.transpose();
935 joff = joff +
ntri[s];
939 ip_solution.transposeInPlace();
945 for (j = 0; j < nlast; j++)
946 for (k = 0; k < nlast; k++)
947 solution(j + koff, k + koff) += mult * ip_solution(j,k);
951 qInfo(
"done.\n\t\tScaling...");
968 Eigen::VectorXf sums(ntri1);
969 for (j = 0; j < ntri1; j++) {
971 for (k = 0; k < ntri2; k++)
972 sum = sum + angles(j,k);
973 sums[j] = sum/(2*
M_PI);
975 for (j = 0; j < ntri1; j++)
982 if (std::fabs(sums[j]-desired) > 1e-4) {
983 qWarning(
"solid angle matrix: rowsum[%d] = 2PI*%g",
1001 int ntri1,ntri2,ntri_tot;
1007 for (p = 0,ntri_tot = 0; p <
surfs.size(); p++)
1010 Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot);
1011 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + ntri1) {
1013 ntri1 = surf1->
ntri;
1014 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + ntri2) {
1016 ntri2 = surf2->
ntri;
1018 for (j = 0; j < ntri1; j++)
1019 for (k = 0, tri = surf2->
tris.data(); k < ntri2; k++, tri++) {
1020 if (p == q && j == k)
1024 solids(j+joff,k+koff) = result;
1033 Eigen::MatrixXf sub_block = solids.block(joff, koff, ntri1, ntri2);
1035 return Eigen::MatrixXf();
1049 Eigen::MatrixXf solids;
1056 std::vector<MNESurface*> rawSurfs;
1057 rawSurfs.reserve(
nsurf);
1058 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
1060 qInfo(
"\nComputing the constant collocation solution...");
1061 qInfo(
"\tSolid angles...");
1063 if (solids.size() == 0) {
1071 qInfo(
"\tInverting the coefficient matrix...");
1082 Eigen::MatrixXf ip_solution;
1084 qInfo(
"IP approach required...");
1086 qInfo(
"\tSolid angles (homog)...");
1087 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
1089 if (solids.size() == 0) {
1094 qInfo(
"\tInverting the coefficient matrix (homog)...");
1096 if (ip_solution.size() == 0) {
1101 qInfo(
"\tModify the original solution to incorporate IP approach...");
1105 qInfo(
"Solution ready.");
1126 qWarning(
"Unknown BEM method: %d",
bem_method);
1139 if (!force_recompute) {
1142 if (solres == LOADED) {
1143 qInfo(
"\nLoaded %s BEM solution from %s",
fwd_bem_explain_method(this->bem_method).toUtf8().constData(),name.toUtf8().constData());
1146 else if (solres ==
FAIL)
1150 qWarning(
"Desired BEM solution not available in %s (%s)",name,err_get_error());
1166 Eigen::Vector3f diff = rp - rd;
1167 float diff2 = diff.squaredNorm();
1168 Eigen::Vector3f cr = Q.cross(diff);
1170 return cr.dot(dir) / (diff2 * std::sqrt(diff2));
1180 Eigen::Vector3f diff = rp - rd;
1181 float diff2 = diff.squaredNorm();
1182 return Q.dot(diff) / (4.0 *
M_PI * diff2 * std::sqrt(diff2));
1195 float r[3],w[3],dist;
1202 qWarning(
"Solution not computed in fwd_bem_specify_els");
1205 if (!els || els->
ncoil() == 0)
1211 els->
user_data = std::make_unique<FwdBemSolution>();
1220 for (k = 0; k < els->
ncoil(); k++) {
1221 el = els->
coils[k].get();
1222 scalp =
surfs[0].get();
1226 for (p = 0; p < el->
np; p++) {
1227 r[0] = el->
rmag(p, 0); r[1] = el->
rmag(p, 1); r[2] = el->
rmag(p, 2);
1232 qWarning(
"One of the electrodes could not be projected onto the scalp surface. How come?");
1240 for (q = 0; q <
nsol; q++)
1247 tri = &scalp->
tris[best];
1248 scalp->
triangle_coords(Eigen::Map<const Eigen::Vector3f>(r),best,x,y,z);
1250 w[0] = el->
w[p]*(1.0 - x - y);
1253 for (v = 0; v < 3; v++) {
1254 for (q = 0; q <
nsol; q++)
1259 qWarning(
"Unknown BEM approximation method : %d",
bem_method);
1280 Eigen::Vector3f mri_rd = rd;
1281 Eigen::Vector3f mri_Q = Q;
1283 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1286 v0.resize(this->nsol);
1287 float* v0p =
v0.data();
1293 for (pp =
X; pp <=
Z; pp++) {
1294 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1296 ee = Eigen::Vector3f::Unit(pp);
1300 for (s = 0, p = 0; s <
nsurf; s++) {
1302 tri =
surfs[s]->tris.data();
1304 for (k = 0; k <
ntri; k++, tri++)
1310 for (k = 0; k <
nsol; k++)
1314 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1315 for (k = 0; k <
nsol; k++)
1334 Eigen::Vector3f mri_rd = rd;
1335 Eigen::Vector3f mri_Q = Q;
1338 v0.resize(this->nsol);
1339 float* v0p =
v0.data();
1345 for (s = 0, p = 0; s <
nsurf; s++) {
1348 for (k = 0; k <
np; k++)
1354 for (k = 0; k <
nsol; k++)
1358 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1359 for (k = 0; k <
nsol; k++)
1376 Eigen::Vector3f mri_rd = rd;
1377 Eigen::Vector3f mri_Q = Q;
1380 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1383 v0.resize(this->nsol);
1384 float* v0p =
v0.data();
1390 for (pp =
X; pp <=
Z; pp++) {
1391 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1393 ee = Eigen::Vector3f::Unit(pp);
1397 for (s = 0, p = 0; s <
nsurf; s++) {
1400 for (k = 0; k <
np; k++)
1406 for (k = 0; k <
nsol; k++)
1410 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1411 for (k = 0; k <
nsol; k++)
1429 Eigen::Vector3f mri_rd = rd;
1430 Eigen::Vector3f mri_Q = Q;
1433 v0.resize(this->nsol);
1434 float* v0p =
v0.data();
1440 for (s = 0, p = 0; s <
nsurf; s++) {
1442 tri =
surfs[s]->tris.data();
1444 for (k = 0; k <
ntri; k++, tri++)
1450 for (k = 0; k <
nsol; k++)
1454 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1455 for (k = 0; k <
nsol; k++)
1472 qWarning(
"No BEM model specified to fwd_bem_pot_els");
1475 if (m->solution.size() == 0) {
1476 qWarning(
"No solution available for fwd_bem_pot_els");
1480 qWarning(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1484 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1487 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1490 qWarning(
"Unknown BEM method : %d",m->bem_method);
1498int FwdBemModel::fwd_bem_pot_grad_els(
const Eigen::Vector3f& rd,
const Eigen::Vector3f& Q,
FwdCoilSet &els, Eigen::Ref<Eigen::VectorXf> pot, Eigen::Ref<Eigen::VectorXf> xgrad, Eigen::Ref<Eigen::VectorXf> ygrad, Eigen::Ref<Eigen::VectorXf> zgrad,
void *client)
1507 qCritical(
"No BEM model specified to fwd_bem_pot_els");
1510 if (m->solution.size() == 0) {
1511 qCritical(
"No solution available for fwd_bem_pot_els");
1515 qCritical(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1519 int n = els.
ncoil();
1520 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1521 m->fwd_bem_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1524 int n = els.
ncoil();
1525 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1526 m->fwd_bem_lin_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1529 qCritical(
"Unknown BEM method : %d",m->bem_method);
1537inline double arsinh(
double x) {
return std::asinh(x); }
1539void FwdBemModel::calc_f(
const Eigen::Vector3d& xx,
const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1541 double det = -xx[1]*yy[0] + xx[2]*yy[0] +
1542 xx[0]*yy[1] - xx[2]*yy[1] - xx[0]*yy[2] + xx[1]*yy[2];
1544 f0[0] = -xx[2]*yy[1] + xx[1]*yy[2];
1545 f0[1] = xx[2]*yy[0] - xx[0]*yy[2];
1546 f0[2] = -xx[1]*yy[0] + xx[0]*yy[1];
1548 fx[0] = yy[1] - yy[2];
1549 fx[1] = -yy[0] + yy[2];
1550 fx[2] = yy[0] - yy[1];
1552 fy[0] = -xx[1] + xx[2];
1553 fy[1] = xx[0] - xx[2];
1554 fy[2] = -xx[0] + xx[1];
1565 double B2 = 1.0 + B*B;
1566 double ABu = A + B*u;
1567 D = sqrt(u*u + z*z + ABu*ABu);
1568 beta[0] = ABu/sqrt(u*u + z*z);
1569 beta[1] = (A*B + B2*u)/sqrt(A*A + B2*z*z);
1570 beta[2] = (B*z*z - A*u)/(z*D);
1575void FwdBemModel::field_integrals(
const Eigen::Vector3f& from,
MNETriangle& to,
double& I1p, Eigen::Vector2d& T, Eigen::Vector2d& S1, Eigen::Vector2d& S2, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1579 Eigen::Vector3d beta;
1580 double I1,Tx,Ty,Txx,Tyy,Sxx,mult;
1581 double S1x,S1y,S2x,S2y;
1590 Eigen::Vector3d y1 = (to.
r1 - from).cast<double>();
1591 Eigen::Vector3d y2 = (to.
r2 - from).cast<double>();
1592 Eigen::Vector3d y3 = (to.
r3 - from).cast<double>();
1596 Eigen::Vector3d ex_d = to.
ex.cast<
double>();
1597 Eigen::Vector3d ey_d = to.
ey.cast<
double>();
1598 xx[0] = y1.dot(ex_d);
1599 xx[1] = y2.dot(ex_d);
1600 xx[2] = y3.dot(ex_d);
1603 yy[0] = y1.dot(ey_d);
1604 yy[1] = y2.dot(ey_d);
1605 yy[2] = y3.dot(ey_d);
1608 calc_f(Eigen::Map<const Eigen::Vector3d>(xx), Eigen::Map<const Eigen::Vector3d>(yy), f0, fx, fy);
1612 z = y1.dot(to.
nn.cast<
double>());
1627 for (k = 0; k < 2; k++) {
1628 dx = xx[k+1] - xx[k];
1629 A = (yy[k]*xx[k+1] - yy[k+1]*xx[k])/dx;
1630 B = (yy[k+1]-yy[k])/dx;
1636 I1 = I1 - xx[k+1]*
arsinh(beta[0]) - (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1638 Txx =
arsinh(beta[1])/sqrt(B2);
1641 Sxx = (D1 - A*B*Txx)/B2;
1644 Sxx = (B*D1 + A*Txx)/B2;
1650 I1 = I1 + xx[k]*
arsinh(beta[0]) + (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1652 Txx =
arsinh(beta[1])/sqrt(B2);
1655 Sxx = (D1 - A*B*Txx)/B2;
1658 Sxx = (B*D1 + A*Txx)/B2;
1664 mult = 1.0/sqrt(xx[k]*xx[k]+z*z);
1668 Tyy =
arsinh(mult*yy[k+1]);
1670 S1y = S1y + xx[k]*Tyy;
1674 Tyy =
arsinh(mult*yy[k]);
1676 S1y = S1y - xx[k]*Tyy;
1697 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1698 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1699 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1701 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1702 for (j = 0; j < 3; j++)
1704 bbeta[0] = beta[2] - beta[0];
1705 bbeta[1] = beta[0] - beta[1];
1706 bbeta[2] = beta[1] - beta[2];
1708 Eigen::Vector3d coeff = Eigen::Vector3d::Zero();
1709 for (j = 0; j < 3; j++)
1710 coeff += bbeta[j] * (*yy[j]);
1711 return coeff.dot(normal.cast<
double>());
1731 qWarning(
"Solution matrix missing in fwd_bem_field_coeff");
1732 return Eigen::MatrixXf();
1735 qWarning(
"BEM method should be constant collocation for fwd_bem_field_coeff");
1736 return Eigen::MatrixXf();
1741 qWarning(
"head -> mri coordinate transform missing in fwd_bem_field_coeff");
1742 return Eigen::MatrixXf();
1749 return Eigen::MatrixXf();
1750 coils = tcoils.get();
1754 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1755 return Eigen::MatrixXf();
1759 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
ntri);
1761 for (s = 0, off = 0; s <
nsurf; s++) {
1762 surf =
surfs[s].get();
1764 tri = surf->
tris.data();
1767 for (k = 0; k <
ntri; k++,tri++) {
1768 for (j = 0; j < coils->
ncoil(); j++) {
1769 coil = coils->
coils[j].get();
1771 for (p = 0; p < coil->
np; p++)
1773 coeff(j,k+off) = mult*res;
1785 Eigen::Vector3d rkk1 = rk1 - rk;
1786 double size = rkk1.norm();
1788 return log((rk1.norm() * size + rk1.dot(rkk1)) /
1789 (rk.norm() * size + rk.dot(rkk1))) / size;
1796 double triple,l1,l2,l3,solid,clen;
1797 double common,sum,beta,
gamma;
1800 Eigen::Vector3d rjk[3];
1801 rjk[0] = (tri.
r3 - tri.
r2).cast<double>();
1802 rjk[1] = (tri.
r1 - tri.
r3).cast<double>();
1803 rjk[2] = (tri.
r2 - tri.
r1).cast<double>();
1805 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1806 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1807 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1809 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1811 Eigen::Vector3d nn_d = tri.
nn.cast<
double>();
1812 clen = y1.dot(nn_d);
1813 Eigen::Vector3d c_vec = clen * nn_d;
1814 Eigen::Vector3d A_vec = dest.cast<
double>() + c_vec;
1816 Eigen::Vector3d c1 = tri.
r1.cast<
double>() - A_vec;
1817 Eigen::Vector3d c2 = tri.
r2.cast<
double>() - A_vec;
1818 Eigen::Vector3d c3 = tri.
r3.cast<
double>() - A_vec;
1820 const Eigen::Vector3d* cc[4] = { &c1, &c2, &c3, &c1 };
1824 for (sum = 0.0, k = 0; k < 3; k++) {
1825 Eigen::Vector3d cross = cc[k]->cross(*cc[k+1]);
1826 beta = cross.dot(nn_d);
1828 sum = sum + beta*
gamma;
1833 Eigen::Vector3d cross = y1.cross(y2);
1834 triple = cross.dot(y3);
1839 solid = 2.0*atan2(triple,
1847 Eigen::Vector3d dir_d = dir.cast<
double>();
1848 common = (sum-clen*solid)/(2.0*tri.
area);
1849 for (k = 0; k < 3; k++)
1850 res[k] = -rjk[k].dot(dir_d)*common;
1859 Eigen::Vector2d T, S1, S2;
1860 Eigen::Vector3d f0, fx, fy;
1871 Eigen::Vector3f dir = dir_in.normalized();
1873 x_fac = -dir.dot(tri.
ex);
1874 y_fac = -dir.dot(tri.
ey);
1875 for (k = 0; k < 3; k++) {
1876 res_x = f0[k]*T[0] + fx[k]*S1[0] + fy[k]*S2[0] + fy[k]*I1;
1877 res_y = f0[k]*T[1] + fx[k]*S1[1] + fy[k]*S2[1] - fx[k]*I1;
1878 res[k] = x_fac*res_x + y_fac*res_y;
1887 const Eigen::Vector3f* rr[3] = { &source.
r1, &source.
r2, &source.
r3 };
1889 for (k = 0; k < 3; k++) {
1890 Eigen::Vector3f diff = dest - *rr[k];
1891 float dl = diff.squaredNorm();
1892 Eigen::Vector3f vec_result = diff.cross(source.
nn);
1893 res[k] = source.
area*vec_result.dot(normal)/(3.0*dl*sqrt(dl));
1912 Eigen::Vector3d res, one;
1917 qWarning(
"Solution matrix missing in fwd_bem_lin_field_coeff");
1918 return Eigen::MatrixXf();
1921 qWarning(
"BEM method should be linear collocation for fwd_bem_lin_field_coeff");
1922 return Eigen::MatrixXf();
1927 qWarning(
"head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
1928 return Eigen::MatrixXf();
1935 return Eigen::MatrixXf();
1936 coils = tcoils.get();
1940 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1941 return Eigen::MatrixXf();
1951 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
nsol);
1955 for (s = 0, off = 0; s <
nsurf; s++) {
1956 surf =
surfs[s].get();
1958 tri = surf->
tris.data();
1961 for (k = 0; k <
ntri; k++,tri++) {
1962 for (j = 0; j < coils->
ncoil(); j++) {
1963 coil = coils->
coils[j].get();
1968 for (p = 0; p < coil->
np; p++) {
1969 func(coil->
pos(p),coil->
dir(p),*tri,one);
1970 res += coil->
w[p] * one;
1976 for (pp = 0; pp < 3; pp++)
1977 coeff(j,tri->
vert[pp]+off) = coeff(j,tri->
vert[pp]+off) + mult*res[pp];
1980 off = off + surf->
np;
1995 Eigen::MatrixXf sol;
1999 qWarning(
"Solution not computed in fwd_bem_specify_coils");
2004 if (!coils || coils->
ncoil() == 0)
2011 qWarning(
"Unknown BEM method in fwd_bem_specify_coils : %d",
bem_method);
2014 if (sol.size() == 0)
2016 coils->
user_data = std::make_unique<FwdBemSolution>();
2036 Eigen::Vector3f my_rd = rd;
2037 Eigen::Vector3f my_Q = Q;
2044 float* v0p =
v0.data();
2055 for (s = 0, p = 0; s <
nsurf; s++) {
2058 for (k = 0; k <
np; k++)
2065 for (k = 0; k < coils.
ncoil(); k++) {
2066 coil = coils.
coils[k].get();
2068 for (p = 0; p < coil->
np; p++)
2078 for (k = 0; k < coils.
ncoil(); k++)
2083 for (k = 0; k < coils.
ncoil(); k++)
2099 Eigen::Vector3f my_rd = rd;
2100 Eigen::Vector3f my_Q = Q;
2107 float* v0p =
v0.data();
2118 for (s = 0, p = 0; s <
nsurf; s++) {
2120 tri =
surfs[s]->tris.data();
2122 for (k = 0; k <
ntri; k++, tri++)
2129 for (k = 0; k < coils.
ncoil(); k++) {
2130 coil = coils.
coils[k].get();
2132 for (p = 0; p < coil->
np; p++)
2142 for (k = 0; k < coils.
ncoil(); k++)
2147 for (k = 0; k < coils.
ncoil(); k++)
2164 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2165 Eigen::Vector3f ee, mri_ee;
2166 Eigen::Vector3f mri_rd = rd;
2167 Eigen::Vector3f mri_Q = Q;
2173 float* v0p =
v0.data();
2181 for (pp =
X; pp <=
Z; pp++) {
2182 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2186 ee = Eigen::Vector3f::Unit(pp);
2193 for (s = 0, p = 0; s <
nsurf; s++) {
2195 tri =
surfs[s]->tris.data();
2197 for (k = 0; k <
ntri; k++, tri++) {
2205 for (k = 0; k < coils.
ncoil(); k++) {
2206 coil = coils.
coils[k].get();
2208 for (p = 0; p < coil->
np; p++)
2219 for (k = 0; k < coils.
ncoil(); k++)
2220 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2224 for (k = 0; k < coils.
ncoil(); k++)
2238 Eigen::Vector3f diff = rp - rd;
2239 float diff2 = diff.squaredNorm();
2240 float diff3 = std::sqrt(diff2) * diff2;
2241 float diff5 = diff3 * diff2;
2242 Eigen::Vector3f cr = Q.cross(diff);
2243 Eigen::Vector3f crn = dir.cross(Q);
2245 return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3;
2256 Eigen::Vector3f diff = rp - rd;
2257 float diff2 = diff.squaredNorm();
2258 float diff3 = std::sqrt(diff2) * diff2;
2259 float diff5 = diff3 * diff2;
2261 float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3;
2262 return res / (4.0 *
M_PI);
2277 Eigen::Vector3f ee, mri_ee;
2278 Eigen::Vector3f mri_rd = rd;
2279 Eigen::Vector3f mri_Q = Q;
2280 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2286 float* v0p =
v0.data();
2294 for (pp =
X; pp <=
Z; pp++) {
2295 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2299 ee = Eigen::Vector3f::Unit(pp);
2306 for (s = 0, p = 0; s <
nsurf; s++) {
2310 for (k = 0; k <
np; k++)
2317 for (k = 0; k < coils.
ncoil(); k++) {
2318 coil = coils.
coils[k].get();
2320 for (p = 0; p < coil->
np; p++)
2331 for (k = 0; k < coils.
ncoil(); k++)
2332 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2336 for (k = 0; k < coils.
ncoil(); k++)
2355 qWarning(
"No BEM model specified to fwd_bem_field");
2359 qWarning(
"No appropriate coil-specific data available in fwd_bem_field");
2363 m->fwd_bem_field_calc(rd,Q,coils,B);
2366 m->fwd_bem_lin_field_calc(rd,Q,coils,B);
2369 qWarning(
"Unknown BEM method : %d",m->bem_method);
2378 const Eigen::Vector3f& Q,
2380 Eigen::Ref<Eigen::VectorXf> Bval,
2381 Eigen::Ref<Eigen::VectorXf> xgrad,
2382 Eigen::Ref<Eigen::VectorXf> ygrad,
2383 Eigen::Ref<Eigen::VectorXf> zgrad,
2390 qCritical(
"No BEM model specified to fwd_bem_field");
2395 qCritical(
"No appropriate coil-specific data available in fwd_bem_field");
2400 int n = coils.
ncoil();
2401 m->fwd_bem_field_calc(rd,Q,coils,Bval);
2403 m->fwd_bem_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2405 int n = coils.
ncoil();
2406 m->fwd_bem_lin_field_calc(rd,Q,coils,Bval);
2408 m->fwd_bem_lin_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2410 qCritical(
"Unknown BEM method : %d",m->bem_method);
2429 Eigen::MatrixXf tmp_vec_res(3, ncoil);
2431 auto fail = [&]() { a->
stat =
FAIL; };
2437 for (j = 0; j < s->
np; j++) {
2454 for (j = 0; j < s->
np; j++)
2469 for (j = 0; j < s->
np; j++) {
2491 else if (a->
comp == 0) {
2501 else if (a->
comp == 1) {
2511 else if (a->
comp == 2) {
2525 for (j = 0; j < s->
np; j++) {
2531 a->
res->col(p++) = tmp_vec_res.row(0).transpose();
2532 a->
res->col(p++) = tmp_vec_res.row(1).transpose();
2533 a->
res->col(p++) = tmp_vec_res.row(2).transpose();
2547 else if (a->
comp == 0) {
2553 else if (a->
comp == 1) {
2560 else if (a->
comp == 2) {
2592 Eigen::MatrixXf res_mat;
2593 Eigen::MatrixXf res_grad_mat;
2595 MatrixXd matResGrad;
2602 int nmeg = coils->
ncoil();
2604 int nspace =
static_cast<int>(spaces.size());
2609 int nproc = QThread::idealThreadCount();
2610 QStringList emptyList;
2612 auto cleanup_fail = [&]() { one_arg.reset();
delete comp;
return FAIL; };
2620 qInfo(
"Using differences.");
2637 return cleanup_fail();
2641 qDebug() <<
"!!!TODO Speed the following with Eigen up!";
2642 qInfo(
"Composing the field computation matrix...");
2644 return cleanup_fail();
2648 qInfo(
"Composing the field computation matrix (compensation coils)...");
2650 return cleanup_fail();
2654 vec_field =
nullptr;
2664 qInfo(
"Using differences.");
2668 my_sphere_field_grad,
2669 const_cast<Vector3f*
>(&r0));
2675 const_cast<Vector3f*
>(&r0));
2678 return cleanup_fail();
2687 for (k = 0, nsource = 0; k < nspace; k++)
2688 nsource += spaces[k]->nuse;
2693 int ncols = fixed_ori ? nsource : 3*nsource;
2694 res_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2697 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2698 res_grad_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2703 one_arg = std::make_unique<FwdThreadArg>();
2704 one_arg->res = &res_mat;
2705 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2707 one_arg->coils_els = coils;
2708 one_arg->client = client;
2709 one_arg->s =
nullptr;
2710 one_arg->fixed_ori = fixed_ori;
2711 one_arg->field_pot = field;
2712 one_arg->vec_field_pot = vec_field;
2713 one_arg->field_pot_grad = field_grad;
2716 use_threads =
false;
2719 int nthread = (fixed_ori || vec_field || nproc < 6) ? nspace : 3*nspace;
2720 std::vector<FwdThreadArg::UPtr> args;
2725 if (fixed_ori || vec_field || nproc < 6) {
2726 for (k = 0, off = 0; k < nthread; k++) {
2728 t_arg->s = spaces[k].get();
2730 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2731 args.push_back(std::move(t_arg));
2733 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",
2737 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2738 for (p = 0; p < 3; p++,q++) {
2740 t_arg->s = spaces[k].get();
2743 args.push_back(std::move(t_arg));
2745 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2747 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",
2748 nproc,nthread,nspace);
2750 qInfo(
"Computing MEG at %d source locations (%s orientations)...",
2751 nsource,fixed_ori ?
"fixed" :
"free");
2761 for (k = 0, stat =
OK; k < nthread; k++)
2762 if (args[k]->stat !=
OK) {
2767 return cleanup_fail();
2770 qInfo(
"Computing MEG at %d source locations (%s orientations, no threads)...",
2771 nsource,fixed_ori ?
"fixed" :
"free");
2772 for (k = 0, off = 0; k < nspace; k++) {
2773 one_arg->s = spaces[k].get();
2776 if (one_arg->stat !=
OK)
2777 return cleanup_fail();
2778 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2783 QStringList orig_names;
2784 for (k = 0; k < nmeg; k++)
2785 orig_names.append(coils->
coils[k]->chname);
2793 nrow = fixed_ori ? nsource : 3*nsource;
2798 resp.
data = res_mat.transpose().cast<
double>();
2801 if (bDoGrad && res_grad_mat.size() > 0) {
2802 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2803 resp_grad.
nrow = nrow;
2804 resp_grad.
ncol = nmeg;
2807 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
2828 Eigen::MatrixXf res_mat;
2829 Eigen::MatrixXf res_grad_mat;
2831 MatrixXd matResGrad;
2838 int nspace =
static_cast<int>(spaces.size());
2839 int neeg = els->
ncoil();
2844 int nproc = QThread::idealThreadCount();
2845 QStringList emptyList;
2849 for (k = 0, nsource = 0; k < nspace; k++)
2850 nsource += spaces[k]->nuse;
2859 qInfo(
"Using differences.");
2860 pot_grad = my_bem_pot_grad;
2866 if (eeg_model->
nfit == 0) {
2867 qInfo(
"Using the standard series expansion for a multilayer sphere model for EEG");
2873 qInfo(
"Using the equivalent source approach in the homogeneous sphere for EEG");
2884 int ncols = fixed_ori ? nsource : 3*nsource;
2885 res_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2889 qCritical(
"EEG gradient calculation function not available");
2892 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2893 res_grad_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2898 one_arg = std::make_unique<FwdThreadArg>();
2899 one_arg->res = &res_mat;
2900 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2902 one_arg->coils_els = els;
2903 one_arg->client = client;
2904 one_arg->s =
nullptr;
2905 one_arg->fixed_ori = fixed_ori;
2906 one_arg->field_pot = pot;
2907 one_arg->vec_field_pot = vec_pot;
2908 one_arg->field_pot_grad = pot_grad;
2911 use_threads =
false;
2914 int nthread = (fixed_ori || vec_pot || nproc < 6) ? nspace : 3*nspace;
2915 std::vector<FwdThreadArg::UPtr> args;
2920 if (fixed_ori || vec_pot || nproc < 6) {
2921 for (k = 0, off = 0; k < nthread; k++) {
2923 t_arg->s = spaces[k].get();
2925 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2926 args.push_back(std::move(t_arg));
2928 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",nproc,nspace);
2931 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2932 for (p = 0; p < 3; p++,q++) {
2934 t_arg->s = spaces[k].get();
2937 args.push_back(std::move(t_arg));
2939 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2941 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",nproc,nthread,nspace);
2943 qInfo(
"Computing EEG at %d source locations (%s orientations)...",
2944 nsource,fixed_ori ?
"fixed" :
"free");
2954 for (k = 0, stat =
OK; k < nthread; k++)
2955 if (args[k]->stat !=
OK) {
2963 qInfo(
"Computing EEG at %d source locations (%s orientations, no threads)...",
2964 nsource,fixed_ori ?
"fixed" :
"free");
2965 for (k = 0, off = 0; k < nspace; k++) {
2966 one_arg->s = spaces[k].get();
2969 if (one_arg->stat !=
OK)
2971 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2976 QStringList orig_names;
2977 for (k = 0; k < neeg; k++)
2978 orig_names.append(els->
coils[k]->chname);
2984 nrow = fixed_ori ? nsource : 3*nsource;
2989 resp.
data = res_mat.transpose().cast<
double>();
2992 if (bDoGrad && res_grad_mat.size() > 0) {
2993 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2994 resp_grad.
nrow = nrow;
2995 resp_grad.
ncol = neeg;
2998 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
3022 auto* r0 =
static_cast<float*
>(client);
3026 float F,g0,gr,result,sum;
3034 Eigen::Vector3f myrd = rd - Eigen::Map<const Eigen::Vector3f>(r0);
3038 for (k = 0 ; k < coils.
ncoil() ; k++)
3044 Eigen::Vector3f v = Q.cross(myrd);
3046 for (k = 0; k < coils.
ncoil(); k++) {
3047 this_coil = coils.
coils[k].get();
3052 for (j = 0, sum = 0.0; j <
np; j++) {
3054 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3055 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3057 Eigen::Vector3f pos = this_pos_raw - Eigen::Map<const Eigen::Vector3f>(r0);
3062 Eigen::Vector3f a_vec = pos - myrd;
3066 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3069 r2 = pos.squaredNorm(); r = sqrt(r2);
3071 rr0 = pos.dot(myrd);
3073 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3077 ve = v.dot(this_dir); vr = v.dot(pos);
3078 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3083 gr = a2/r + ar0 + 2.0*(a+r);
3088 sum = sum + this_coil->
w[j]*(ve*F + vr*(g0*r0e - gr*re))/(F*F);
3124 auto* r0 =
static_cast<float*
>(client);
3132 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3137 Eigen::Vector3f myrd = rd - r0_vec;
3142 for (k = 0; k < coils.
ncoil(); k++) {
3143 this_coil = coils.
coils[k].get();
3146 Bval(0,k) = Bval(1,k) = Bval(2,k) = 0.0;
3151 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3153 for (j = 0; j <
np; j++) {
3155 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3156 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3158 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3162 Eigen::Vector3f a_vec = pos - myrd;
3166 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3169 r2 = pos.squaredNorm(); r = sqrt(r2);
3171 rr0 = pos.dot(myrd);
3173 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3180 gr = a2/r + ar0 + 2.0*(a+r);
3183 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3184 Eigen::Vector3f v1 = myrd.cross(this_dir);
3185 Eigen::Vector3f v2 = myrd.cross(pos);
3187 g = (g0*r0e - gr*re)/(F*F);
3191 sum += this_coil->
w[j]*(v1/F + v2*g);
3196 for (p = 0; p < 3; p++)
3206int FwdBemModel::fwd_sphere_field_grad(
const Eigen::Vector3f& rd,
const Eigen::Vector3f& Q,
FwdCoilSet &coils, Eigen::Ref<Eigen::VectorXf> Bval, Eigen::Ref<Eigen::VectorXf> xgrad, Eigen::Ref<Eigen::VectorXf> ygrad, Eigen::Ref<Eigen::VectorXf> zgrad,
void *client)
3228 float F,g0,gr,result,G,F2;
3234 auto* r0 =
static_cast<float*
>(client);
3235 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3237 int ncoil = coils.
ncoil();
3241 Eigen::Vector3f myrd = rd - r0_vec;
3245 float r = myrd.norm();
3246 for (k = 0; k < ncoil ; k++) {
3256 Eigen::Vector3f v = Q.cross(myrd);
3258 for (k = 0 ; k < ncoil ; k++) {
3260 this_coil = coils.
coils[k].get();
3266 for (j = 0; j <
np; j++) {
3268 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3272 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3274 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3278 Eigen::Vector3f a_vec = pos - myrd;
3282 float a2 = a_vec.squaredNorm();
float a = sqrt(a2);
3283 float r2 = pos.squaredNorm(); r = sqrt(r2);
3284 float rr0 = pos.dot(myrd);
3285 float ar = (r2 - rr0)/a;
3287 ve = v.dot(this_dir); vr = v.dot(pos);
3288 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3292 Eigen::Vector3f eQ = this_dir.cross(Q);
3296 Eigen::Vector3f rQ = pos.cross(Q);
3300 F = a*(r*a + r2 - rr0);
3302 gr = a2/r + ar + 2.0*(a+r);
3303 g0 = a + 2.0*r + ar;
3308 result = (ve*F + vr*G)/F2;
3312 huu = 2.0 + 2.0*a/r;
3313 Eigen::Vector3f ga = -a_vec/a;
3314 Eigen::Vector3f gar = -(ga*ar + pos)/a;
3315 Eigen::Vector3f gg0 = ga + gar;
3316 Eigen::Vector3f ggr = huu*ga + gar;
3317 Eigen::Vector3f gFF = ga/a - (r*a_vec + a*pos)/F;
3318 Eigen::Vector3f gresult = -2.0f*result*gFF + (eQ+gFF*ve)/F +
3319 (rQ*G + vr*(gg0*r0e + g0*this_dir - ggr*re))/F2;
3321 Bval[k] = Bval[k] + this_coil->
w[j]*result;
3322 xgrad[k] = xgrad[k] + this_coil->
w[j]*gresult[0];
3323 ygrad[k] = ygrad[k] + this_coil->
w[j]*gresult[1];
3324 zgrad[k] = zgrad[k] + this_coil->
w[j]*gresult[2];
3345 float sum,dist,dist2,dist5;
3348 for (k = 0; k < coils.
ncoil(); k++) {
3349 this_coil = coils.
coils[k].get();
3355 for (j = 0, sum = 0.0; j <
np; j++) {
3356 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3357 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3361 dist5 = dist2*dist2*dist;
3362 sum = sum + this_coil->
w[j]*(3*M.dot(diff)*diff.dot(dir) - dist2*M.dot(dir))/dist5;
3383 float dist,dist2,dist5;
3386 for (k = 0; k < coils.
ncoil(); k++) {
3387 this_coil = coils.
coils[k].get();
3390 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3394 for (j = 0; j <
np; j++) {
3395 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3396 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3400 dist5 = dist2*dist2*dist;
3401 for (p = 0; p < 3; p++)
3402 sum[p] = sum[p] + this_coil->
w[j]*(3*diff[p]*diff.dot(dir) - dist2*dir[p])/dist5;
3405 for (p = 0; p < 3; p++)
3409 for (p = 0; p < 3; p++)
#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_ISOTRAK
#define FIFFV_COORD_UNKNOWN
#define FIFFV_MNE_COORD_FS_TAL_GTZ
#define FIFFV_MNE_COORD_RAS
FiffNamedMatrix class declaration.
FiffStream class declaration.
#define FIFFV_BEM_APPROX_LINEAR
#define FIFFV_BEM_SURF_ID_SKULL
#define FIFFV_BEM_APPROX_CONST
#define FIFFV_BEM_SURF_ID_HEAD
#define FIFFV_BEM_SURF_ID_BRAIN
#define FIFF_BEM_POT_SOLUTION
MNETriangle class declaration.
MNESurface class declaration.
MNESourceSpace class declaration.
FwdBemSolution class declaration.
std::function< int(const Eigen::Vector3f &rd, FWDLIB::FwdCoilSet &coils, Eigen::Ref< Eigen::MatrixXf > res, void *client)> fwdVecFieldFunc
std::function< int(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FWDLIB::FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > res, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)> fwdFieldGradFunc
std::function< int(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FWDLIB::FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > res, void *client)> fwdFieldFunc
FwdCompData class declaration.
const QString mne_coord_frame_name_40(int frame)
FwdEegSphereModel class declaration.
FwdThreadArg class declaration.
FwdBemModel class declaration.
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).
constexpr int FWD_BEM_CONSTANT_COLL
constexpr double FWD_BEM_IP_APPROACH_LIMIT
constexpr int FWD_BEM_LIN_FIELD_URANKAR
constexpr int FWD_COILC_EEG
constexpr bool FWD_IS_MEG_COIL(int x)
constexpr int FWD_BEM_LINEAR_COLL
constexpr int FWD_BEM_LIN_FIELD_FERGUSON
constexpr int FWD_BEM_UNKNOWN
constexpr int FWD_BEM_LIN_FIELD_SIMPLE
Coordinate transformation description.
FiffCoordTrans inverted() const
QSharedPointer< FiffDirNode > SPtr
void transpose_named_matrix()
QSharedPointer< FiffStream > SPtr
std::unique_ptr< FiffTag > UPtr
Lookup record mapping a FIFF coordinate frame integer ID to its human-readable name.
static FwdBemModel::UPtr fwd_bem_load_three_layer_surfaces(const QString &name)
Load a three-layer BEM model (scalp, outer skull, inner skull) from a FIFF file.
static void field_integrals(const Eigen::Vector3f &from, MNELIB::MNETriangle &to, double &I1p, Eigen::Vector2d &T, Eigen::Vector2d &S1, Eigen::Vector2d &S2, Eigen::Vector3d &f0, Eigen::Vector3d &fx, Eigen::Vector3d &fy)
Compute the geometry integrals for the magnetic field from a triangle.
void fwd_bem_field_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > B)
Compute BEM magnetic fields at coils using constant collocation.
static double calc_gamma(const Eigen::Vector3d &rk, const Eigen::Vector3d &rk1)
Compute the gamma angle for the linear field integration (Ferguson).
void fwd_bem_pot_grad_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet *els, int all_surfs, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad)
Compute the gradient of BEM potentials with respect to dipole position (constant collocation).
Eigen::VectorXf source_mult
static double calc_beta(const Eigen::Vector3d &rk, const Eigen::Vector3d &rk1)
Compute the beta angle used in the linear collocation integration.
static int get_int(FIFFLIB::FiffStream::SPtr &stream, const FIFFLIB::FiffDirNode::SPtr &node, int what, int *res)
Read an integer tag from a FIFF node.
static void fwd_bem_one_lin_field_coeff_uran(const Eigen::Vector3f &dest, const Eigen::Vector3f &dir, MNELIB::MNETriangle &tri, Eigen::Vector3d &res)
Compute linear field coefficients using the Urankar method.
static Eigen::MatrixXf fwd_bem_multi_solution(Eigen::MatrixXf &solids, const Eigen::MatrixXf *gamma, int nsurf, const Eigen::VectorXi &ntri)
Compute the multi-surface BEM solution from solid-angle coefficients.
static FwdBemModel::UPtr fwd_bem_load_surfaces(const QString &name, const std::vector< int > &kinds)
Load BEM surfaces of specified kinds from a FIFF file.
static void correct_auto_elements(MNELIB::MNESurface &surf, Eigen::MatrixXf &mat)
Correct the auto (self-coupling) elements of the linear collocation matrix.
static int fwd_sphere_field_grad(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > Bval, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)
Callback: compute the spherical-model magnetic field and its position gradient at coils.
static int fwd_bem_check_solids(const Eigen::MatrixXf &angles, int ntri1, int ntri2, float desired)
Verify that solid-angle sums match the expected value.
int fwd_bem_load_solution(const QString &name, int bem_method)
Load a pre-computed BEM solution from a FIFF file.
static int fwd_mag_dipole_field_vec(const Eigen::Vector3f &rm, FwdCoilSet &coils, Eigen::Ref< Eigen::MatrixXf > Bval, void *client)
Callback: compute the vector magnetic field of a magnetic dipole at coils.
int fwd_bem_compute_solution(int bem_method)
Compute the BEM solution matrix using the specified method.
static constexpr double MAG_FACTOR
int fwd_bem_specify_coils(FwdCoilSet *coils)
Precompute the coil-specific BEM solution for MEG.
static Eigen::MatrixXf fwd_bem_solid_angles(const std::vector< MNELIB::MNESurface * > &surfs)
Compute the solid-angle matrix for all BEM surfaces.
int fwd_bem_specify_els(FwdCoilSet *els)
Precompute the electrode-specific BEM solution.
static void fwd_bem_one_lin_field_coeff_simple(const Eigen::Vector3f &dest, const Eigen::Vector3f &normal, MNELIB::MNETriangle &source, Eigen::Vector3d &res)
Compute linear field coefficients using the simple (direct) method.
std::vector< std::shared_ptr< MNELIB::MNESurface > > surfs
Eigen::VectorXf field_mult
std::unique_ptr< FwdBemModel > UPtr
virtual ~FwdBemModel()
Destroys the BEM model.
static void fwd_bem_one_lin_field_coeff_ferg(const Eigen::Vector3f &dest, const Eigen::Vector3f &dir, MNELIB::MNETriangle &tri, Eigen::Vector3d &res)
Compute linear field coefficients using the Ferguson method.
static QString fwd_bem_make_bem_sol_name(const QString &name)
Build a standard BEM solution file name from a model name.
static Eigen::MatrixXf fwd_bem_homog_solution(Eigen::MatrixXf &solids, int ntri)
Compute the homogeneous (single-layer) BEM solution.
void fwd_bem_lin_field_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > B)
Compute BEM magnetic fields at coils using linear collocation.
static int fwd_mag_dipole_field(const Eigen::Vector3f &rm, const Eigen::Vector3f &M, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > Bval, void *client)
Callback: compute the magnetic field of a magnetic dipole at coils.
MNELIB::MNESurface * fwd_bem_find_surface(int kind)
Find a surface of the given kind in this BEM model.
void fwd_bem_free_solution()
Release the potential solution matrix and associated workspace.
Eigen::MatrixXf fwd_bem_field_coeff(FwdCoilSet *coils)
Assemble the constant-collocation magnetic field coefficient matrix.
static int fwd_sphere_field(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > Bval, void *client)
Callback: compute the spherical-model magnetic field at coils.
static void lin_pot_coeff(const Eigen::Vector3f &from, MNELIB::MNETriangle &to, Eigen::Vector3d &omega)
Compute the linear potential coefficients for one source-destination pair.
void fwd_bem_field_grad_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad)
Compute the gradient of BEM magnetic fields with respect to dipole position (constant collocation).
static const QString & fwd_bem_explain_method(int method)
Return a human-readable label for a BEM method.
void fwd_bem_pot_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet *els, int all_surfs, Eigen::Ref< Eigen::VectorXf > pot)
Compute BEM potentials at electrodes using constant collocation.
void fwd_bem_lin_field_grad_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad)
Compute the gradient of BEM magnetic fields with respect to dipole position (linear collocation).
static void calc_f(const Eigen::Vector3d &xx, const Eigen::Vector3d &yy, Eigen::Vector3d &f0, Eigen::Vector3d &fx, Eigen::Vector3d &fy)
Compute the f0, fx, fy integration helper values from corner coordinates.
int fwd_bem_linear_collocation_solution()
Compute the linear-collocation BEM solution for this model.
int fwd_bem_load_recompute_solution(const QString &name, int bem_method, int force_recompute)
Load a BEM solution from file, recomputing if necessary.
static int fwd_bem_field(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > B, void *client)
Callback: compute BEM magnetic fields at coils for a dipole.
static int fwd_bem_field_grad(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > Bval, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)
Callback: compute BEM magnetic fields and position gradients at coils.
int compute_forward_eeg(std::vector< std::unique_ptr< MNELIB::MNESourceSpace > > &spaces, FwdCoilSet *els, bool fixed_ori, FwdEegSphereModel *eeg_model, bool use_threads, FIFFLIB::FiffNamedMatrix &resp, FIFFLIB::FiffNamedMatrix &resp_grad, bool bDoGrad)
Compute the EEG forward solution for one or more source spaces.
static float fwd_bem_inf_pot(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, const Eigen::Vector3f &rp)
Compute the infinite-medium electric potential at a single point.
static int fwd_bem_pot_els(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &els, Eigen::Ref< Eigen::VectorXf > pot, void *client)
Callback: compute BEM potentials at electrodes for a dipole.
static Eigen::MatrixXf fwd_bem_lin_pot_coeff(const std::vector< MNELIB::MNESurface * > &surfs)
Assemble the full linear-collocation potential coefficient matrix.
int fwd_bem_set_head_mri_t(const FIFFLIB::FiffCoordTrans &t)
Set the Head-to-MRI coordinate transform for this BEM model.
void(* linFieldIntFunc)(const Eigen::Vector3f &dest, const Eigen::Vector3f &dir, MNELIB::MNETriangle &tri, Eigen::Vector3d &res)
Function pointer type for linear field coefficient integration methods.
static float fwd_bem_inf_field_der(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, const Eigen::Vector3f &rp, const Eigen::Vector3f &dir, const Eigen::Vector3f &comp)
Compute the derivative of the infinite-medium magnetic field with respect to dipole position.
static FwdBemModel::UPtr fwd_bem_load_homog_surface(const QString &name)
Load a single-layer (homogeneous) BEM model from a FIFF file.
static float fwd_bem_inf_field(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, const Eigen::Vector3f &rp, const Eigen::Vector3f &dir)
Compute the infinite-medium magnetic field at a single point.
static float fwd_bem_inf_pot_der(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, const Eigen::Vector3f &rp, const Eigen::Vector3f &comp)
Compute the derivative of the infinite-medium electric potential with respect to dipole position.
static int fwd_sphere_field_vec(const Eigen::Vector3f &rd, FwdCoilSet &coils, Eigen::Ref< Eigen::MatrixXf > Bval, void *client)
Callback: compute the spherical-model vector magnetic field at coils.
void fwd_bem_lin_pot_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet *els, int all_surfs, Eigen::Ref< Eigen::VectorXf > pot)
Compute BEM potentials at electrodes using linear collocation.
FwdBemModel()
Constructs an empty BEM model.
static double one_field_coeff(const Eigen::Vector3f &dest, const Eigen::Vector3f &normal, MNELIB::MNETriangle &tri)
Compute the constant-collocation magnetic field coefficient for one triangle.
static const QString & fwd_bem_explain_surface(int kind)
Return a human-readable label for a BEM surface kind.
static void meg_eeg_fwd_one_source_space(FwdThreadArg *arg)
Thread worker: compute the forward solution for one source space.
int compute_forward_meg(std::vector< std::unique_ptr< MNELIB::MNESourceSpace > > &spaces, FwdCoilSet *coils, FwdCoilSet *comp_coils, MNELIB::MNECTFCompDataSet *comp_data, bool fixed_ori, const Eigen::Vector3f &r0, bool use_threads, FIFFLIB::FiffNamedMatrix &resp, FIFFLIB::FiffNamedMatrix &resp_grad, bool bDoGRad)
Compute the MEG forward solution for one or more source spaces.
static void fwd_bem_ip_modify_solution(Eigen::MatrixXf &solution, Eigen::MatrixXf &ip_solution, float ip_mult, int nsurf, const Eigen::VectorXi &ntri)
Modify the BEM solution with the isolated-problem (IP) approach.
static std::unique_ptr< MNELIB::MNESurface > make_guesses(MNELIB::MNESurface *guess_surf, float guessrad, const Eigen::Vector3f &guess_r0, float grid, float exclude, float mindist)
Generate a set of dipole guess locations inside a boundary surface.
int fwd_bem_constant_collocation_solution()
Compute the constant-collocation BEM solution for this model.
void fwd_bem_lin_pot_grad_calc(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet *els, int all_surfs, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad)
Compute the gradient of BEM potentials with respect to dipole position (linear collocation).
static int fwd_bem_pot_grad_els(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &els, Eigen::Ref< Eigen::VectorXf > pot, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)
Callback: compute BEM potentials and position gradients at electrodes.
Eigen::MatrixXf fwd_bem_lin_field_coeff(FwdCoilSet *coils, int method)
Assemble the linear-collocation magnetic field coefficient matrix.
static void calc_magic(double u, double z, double A, double B, Eigen::Vector3d &beta, double &D)
Compute the "magic" beta and D factors for the Urankar field integration.
FIFFLIB::FiffCoordTrans head_mri_t
Mapping from infinite medium potentials to a particular set of coils or electrodes.
Single MEG or EEG sensor coil with integration points, weights, and coordinate frame.
Eigen::Map< const Eigen::Vector3f > dir(int j) const
Eigen::Map< const Eigen::Vector3f > pos(int j) const
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > rmag
Collection of FwdCoil objects representing a full MEG or EEG sensor array.
std::unique_ptr< FwdBemSolution > user_data
std::unique_ptr< FwdCoilSet > UPtr
std::vector< FwdCoil::UPtr > coils
FwdCoilSet::UPtr dup_coil_set(const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans()) const
This structure is used in the compensated field calculations.
static int fwd_comp_field_grad(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > res, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)
MNELIB::MNECTFCompDataSet * set
static int fwd_comp_field_vec(const Eigen::Vector3f &rd, FwdCoilSet &coils, Eigen::Ref< Eigen::MatrixXf > res, void *client)
static int fwd_comp_field(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > res, void *client)
static FwdCompData * fwd_make_comp_data(MNELIB::MNECTFCompDataSet *set, FwdCoilSet *coils, FwdCoilSet *comp_coils, fwdFieldFunc field, fwdVecFieldFunc vec_field, fwdFieldGradFunc field_grad, void *client)
Multi-layer spherical head model for EEG forward computation.
static int fwd_eeg_multi_spherepot_coil1(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &els, Eigen::Ref< Eigen::VectorXf > Vval, void *client)
static int fwd_eeg_spherepot_grad_coil(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > Vval, Eigen::Ref< Eigen::VectorXf > xgrad, Eigen::Ref< Eigen::VectorXf > ygrad, Eigen::Ref< Eigen::VectorXf > zgrad, void *client)
static int fwd_eeg_spherepot_coil_vec(const Eigen::Vector3f &rd, FwdCoilSet &els, Eigen::Ref< Eigen::MatrixXf > Vval_vec, void *client)
static int fwd_eeg_spherepot_coil(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &els, Eigen::Ref< Eigen::VectorXf > Vval, void *client)
Thread-local arguments for parallel forward field computation (source range, coils,...
fwdFieldGradFunc field_pot_grad
std::unique_ptr< FwdThreadArg > UPtr
static FwdThreadArg::UPtr create_eeg_multi_thread_duplicate(FwdThreadArg &one, bool bem_model)
Eigen::MatrixXf * res_grad
fwdVecFieldFunc vec_field_pot
static FwdThreadArg::UPtr 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)
std::unique_ptr< MNESurface > UPtr
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)
Eigen::Map< const Eigen::Vector3f > normal(int k) const
Eigen::VectorXi nneighbor_tri
static double solid_angle(const Eigen::Vector3f &from, const MNELIB::MNETriangle &tri)
std::vector< MNETriangle > tris
Eigen::Map< const Eigen::Vector3f > point(int k) const
Per-triangle geometric data for a cortical or BEM surface.
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const