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++) {
313 qCritical(
"FsSurface %s not specified in MRI coordinates.",
fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
317 surfs.push_back(std::move(s));
319 auto m = std::make_unique<FwdBemModel>();
323 m->surfs = std::move(
surfs);
324 m->sigma = sigma_tmp;
325 m->ntri.resize(nkind);
327 m->gamma.resize(nkind, nkind);
328 m->source_mult.resize(nkind);
329 m->field_mult.resize(nkind);
333 Eigen::VectorXf sigma1(nkind + 1);
335 sigma1.tail(nkind) = m->sigma;
340 for (j = 0; j < m->nsurf; j++) {
341 m->ntri[j] = m->surfs[j]->ntri;
342 m->np[j] = m->surfs[j]->np;
343 m->source_mult[j] = 2.0f / (sigma1[j+1] + sigma1[j]);
344 m->field_mult[j] = sigma1[j+1] - sigma1[j];
345 for (k = 0; k < m->nsurf; k++)
346 m->gamma(j, k) = (sigma1[k+1] - sigma1[k]) / (sigma1[j+1] + sigma1[j]);
387 if(!stream->open()) {
396 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(
FIFFB_BEM);
398 if (nodes.size() == 0) {
399 qWarning(
"No BEM data in %s",name.toUtf8().constData());
417 qWarning(
"Cannot handle BEM approximation method : %d",method);
422 qWarning(
"Approximation method in file : %d desired : %d",method,
bem_method);
434 QVector<qint32> dims;
435 t_pTag->getMatrixDimensions(ndim, dims);
438 qWarning(
"Expected a two-dimensional solution matrix instead of a %d dimensional one",ndim);
442 for (k = 0, dim = 0; k <
nsurf; k++)
444 if (dims[0] != dim || dims[1] != dim) {
445 qWarning(
"Expected a %d x %d solution matrix instead of a %d x %d one",dim,dim,dims[0],dims[1]);
450 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
455 solution = t_pTag->toFloatMatrix().transpose();
457 this->bem_method = method;
479 qWarning(
"Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
495 qInfo(
"Making a spherical guess space with radius %7.1f mm...",1000*guessrad);
497 QFile bemFile(QString(QCoreApplication::applicationDirPath() +
"/../resources/general/surf2bem/icos.fif"));
498 if ( !QCoreApplication::startingUp() )
499 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString(
"/../resources/general/surf2bem/icos.fif"));
500 else if (!bemFile.exists())
501 bemFile.setFileName(
"../resources/general/surf2bem/icos.fif");
503 if( !bemFile.exists () ){
504 qDebug() << bemFile.fileName() <<
"does not exists.";
508 bemname = bemFile.fileName();
514 for (k = 0; k < sphere_owner->np; k++) {
515 dist = sphere_owner->point(k).norm();
516 sphere_owner->rr.row(k) = (guessrad * sphere_owner->rr.row(k) / dist) + guess_r0.transpose();
518 if (sphere_owner->add_geometry_info(
true) ==
FAIL)
520 guess_surf = sphere_owner.get();
523 qInfo(
"Guess surface (%d = %s) is in %s coordinates",
527 qInfo(
"Filtering (grid = %6.f mm)...",1000*grid);
538 Eigen::Vector3d rkk1 = rk1 - rk;
539 double size = rkk1.norm();
541 return log((rk.norm() * size + rk.dot(rkk1)) /
542 (rk1.norm() * size + rk1.dot(rkk1))) / size;
552 Eigen::Vector3d y1, y2, y3;
555 Eigen::Vector3d vec_omega;
558 double beta[3],bbeta[3];
561 static const double solid_eps = 4.0*
M_PI/1.0E6;
565 y1 = (to.
r1 - from).cast<double>();
566 y2 = (to.
r2 - from).cast<double>();
567 y3 = (to.
r3 - from).cast<double>();
571 const Eigen::Vector3d* y_arr[5] = { &y3, &y1, &y2, &y3, &y1 };
572 const Eigen::Vector3d** yy = y_arr + 1;
576 Eigen::Vector3d cross = y1.cross(y2);
577 triple = cross.dot(y3);
582 ss = (l1*l2*l3+y1.dot(y2)*l3+y1.dot(y3)*l2+y2.dot(y3)*l1);
583 solid = 2.0*atan2(triple,ss);
584 if (std::fabs(solid) < solid_eps) {
591 for (j = 0; j < 3; j++)
593 bbeta[0] = beta[2] - beta[0];
594 bbeta[1] = beta[0] - beta[1];
595 bbeta[2] = beta[1] - beta[2];
598 for (j = 0; j < 3; j++)
599 vec_omega += bbeta[j] * (*yy[j]);
604 n2 = 1.0/(area2*area2);
605 Eigen::Vector3d nn_d = to.
nn.cast<
double>();
606 for (k = 0; k < 3; k++) {
607 Eigen::Vector3d z = yy[k+1]->cross(*yy[k-1]);
608 Eigen::Vector3d diff = *yy[k-1] - *yy[k+1];
609 omega[k] = n2*(-area2*z.dot(nn_d)*solid +
610 triple*diff.dot(vec_omega));
619 double rel1 = (solid + omega[0]+omega[1]+omega[2])/solid;
623 Eigen::Vector3d check = Eigen::Vector3d::Zero();
624 Eigen::Vector3d nn_check = to.
nn.cast<
double>();
625 for (k = 0; k < 3; k++) {
626 Eigen::Vector3d z = nn_check.cross(*yy[k]);
629 check *= -area2/triple;
630 fprintf (stderr,
"(%g,%g,%g) =? (%g,%g,%g)\n",
631 check[0],check[1],check[2],
632 vec_omega[0],vec_omega[1],vec_omega[2]);
634 double rel2 = sqrt(check.dot(check)/vec_omega.dot(vec_omega));
635 fprintf (stderr,
"err1 = %g, err2 = %g\n",100*rel1,100*rel2);
652 float pi2 = 2.0*
M_PI;
656 for (j = 0; j < nnode; j++) {
658 for (k = 0; k < nnode; k++)
659 sum = sum + mat(j,k);
660 fprintf (stderr,
"row %d sum = %g missing = %g\n",j+1,sum/pi2,
662 mat(j,j) = pi2 - sum;
665 for (j = 0; j < nnode; j++) {
670 for (k = 0; k < nnode; k++)
671 sum = sum + mat(j,k);
681 miss = miss/(4.0*nmemb);
682 for (k = 0,tri = surf.
tris.data(); k <
ntri; k++,tri++) {
683 if (tri->
vert[0] == j) {
684 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
685 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
687 else if (tri->
vert[1] == j) {
688 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
689 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
691 else if (tri->
vert[2] == j) {
692 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
693 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
715 int np1,np2,
ntri,np_tot,np_max;
717 Eigen::Vector3d omega;
723 for (p = 0, np_tot = np_max = 0; p <
surfs.size(); p++) {
724 np_tot +=
surfs[p]->np;
726 np_max =
surfs[p]->np;
729 Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(np_tot, np_tot);
730 Eigen::VectorXd row(np_max);
731 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + np1) {
734 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + np2) {
739 qInfo(
"\t\t%s (%d) -> %s (%d) ... ",
743 for (j = 0; j < np1; j++) {
744 for (k = 0; k < np2; k++)
746 for (k = 0, tri = surf2->
tris.data(); k <
ntri; k++,tri++) {
751 if (p == q && (tri->
vert[0] == j || tri->
vert[1] == j || tri->
vert[2] == j))
757 for (c = 0; c < 3; c++)
758 row[tri->
vert[c]] = row[tri->
vert[c]] - omega[c];
760 for (k = 0; k < np2; k++)
761 mat(j+joff,k+koff) = row[k];
764 Eigen::MatrixXf sub_mat = mat.block(joff, koff, np1, np1);
766 mat.block(joff, koff, np1, np1) = sub_mat;
781 Eigen::MatrixXf coeff;
788 std::vector<MNESurface*> rawSurfs;
789 rawSurfs.reserve(
nsurf);
790 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
792 qInfo(
"\nComputing the linear collocation solution...");
793 qInfo(
"\tMatrix coefficients...");
795 if (coeff.size() == 0) {
803 qInfo(
"\tInverting the coefficient matrix...");
815 Eigen::MatrixXf ip_solution;
817 qInfo(
"IP approach required...");
819 qInfo(
"\tMatrix coefficients (homog)...");
820 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
822 if (coeff.size() == 0) {
827 qInfo(
"\tInverting the coefficient matrix (homog)...");
829 if (ip_solution.size() == 0) {
834 qInfo(
"\tModify the original solution to incorporate IP approach...");
839 qInfo(
"Solution ready.");
855 float pi2 = 1.0/(2*
M_PI);
857 int joff,koff,jup,kup,ntot;
859 for (j = 0,ntot = 0; j <
nsurf; j++)
865 for (p = 0, joff = 0; p <
nsurf; p++) {
866 jup =
ntri[p] + joff;
867 for (q = 0, koff = 0; q <
nsurf; q++) {
868 kup =
ntri[q] + koff;
869 mult = (
gamma ==
nullptr) ? pi2 : pi2 * (*gamma)(p, q);
870 for (j = joff; j < jup; j++)
871 for (k = koff; k < kup; k++)
872 solids(j,k) = defl - solids(j,k)*mult;
877 for (k = 0; k < ntot; k++)
878 solids(k,k) = solids(k,k) + 1.0;
880 Eigen::MatrixXf result = solids.inverse();
905 int j,k,joff,koff,ntot,nlast;
908 for (s = 0, koff = 0; s <
nsurf-1; s++)
909 koff = koff +
ntri[s];
913 Eigen::VectorXf row(nlast);
914 mult = (1.0 + ip_mult)/ip_mult;
916 qInfo(
"\t\tCombining...");
918 ip_solution.transposeInPlace();
920 for (s = 0, joff = 0; s <
nsurf; s++) {
926 for (j = 0; j <
ntri[s]; j++) {
927 for (k = 0; k < nlast; k++) {
928 row[k] =
solution.row(j + joff).segment(koff, nlast).dot(ip_solution.row(k).head(nlast));
930 solution.row(j + joff).segment(koff, nlast) -= 2.0f * row.transpose();
932 joff = joff +
ntri[s];
936 ip_solution.transposeInPlace();
942 for (j = 0; j < nlast; j++)
943 for (k = 0; k < nlast; k++)
944 solution(j + koff, k + koff) += mult * ip_solution(j,k);
948 qInfo(
"done.\n\t\tScaling...");
965 Eigen::VectorXf sums(ntri1);
966 for (j = 0; j < ntri1; j++) {
968 for (k = 0; k < ntri2; k++)
969 sum = sum + angles(j,k);
970 sums[j] = sum/(2*
M_PI);
972 for (j = 0; j < ntri1; j++)
979 if (std::fabs(sums[j]-desired) > 1e-4) {
980 qWarning(
"solid angle matrix: rowsum[%d] = 2PI*%g",
998 int ntri1,ntri2,ntri_tot;
1004 for (p = 0,ntri_tot = 0; p <
surfs.size(); p++)
1007 Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot);
1008 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + ntri1) {
1010 ntri1 = surf1->
ntri;
1011 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + ntri2) {
1013 ntri2 = surf2->
ntri;
1015 for (j = 0; j < ntri1; j++)
1016 for (k = 0, tri = surf2->
tris.data(); k < ntri2; k++, tri++) {
1017 if (p == q && j == k)
1021 solids(j+joff,k+koff) = result;
1030 Eigen::MatrixXf sub_block = solids.block(joff, koff, ntri1, ntri2);
1032 return Eigen::MatrixXf();
1046 Eigen::MatrixXf solids;
1053 std::vector<MNESurface*> rawSurfs;
1054 rawSurfs.reserve(
nsurf);
1055 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
1057 qInfo(
"\nComputing the constant collocation solution...");
1058 qInfo(
"\tSolid angles...");
1060 if (solids.size() == 0) {
1068 qInfo(
"\tInverting the coefficient matrix...");
1079 Eigen::MatrixXf ip_solution;
1081 qInfo(
"IP approach required...");
1083 qInfo(
"\tSolid angles (homog)...");
1084 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
1086 if (solids.size() == 0) {
1091 qInfo(
"\tInverting the coefficient matrix (homog)...");
1093 if (ip_solution.size() == 0) {
1098 qInfo(
"\tModify the original solution to incorporate IP approach...");
1102 qInfo(
"Solution ready.");
1123 qWarning(
"Unknown BEM method: %d",
bem_method);
1136 if (!force_recompute) {
1139 if (solres == LOADED) {
1140 qInfo(
"\nLoaded %s BEM solution from %s",
fwd_bem_explain_method(this->bem_method).toUtf8().constData(),name.toUtf8().constData());
1143 else if (solres ==
FAIL)
1147 qWarning(
"Desired BEM solution not available in %s (%s)",name,err_get_error());
1163 Eigen::Vector3f diff = rp - rd;
1164 float diff2 = diff.squaredNorm();
1165 Eigen::Vector3f cr = Q.cross(diff);
1167 return cr.dot(dir) / (diff2 * std::sqrt(diff2));
1177 Eigen::Vector3f diff = rp - rd;
1178 float diff2 = diff.squaredNorm();
1179 return Q.dot(diff) / (4.0 *
M_PI * diff2 * std::sqrt(diff2));
1192 float r[3],w[3],dist;
1199 qWarning(
"Solution not computed in fwd_bem_specify_els");
1202 if (!els || els->
ncoil() == 0)
1208 els->
user_data = std::make_unique<FwdBemSolution>();
1217 for (k = 0; k < els->
ncoil(); k++) {
1218 el = els->
coils[k].get();
1219 scalp =
surfs[0].get();
1223 for (p = 0; p < el->
np; p++) {
1224 r[0] = el->
rmag(p, 0); r[1] = el->
rmag(p, 1); r[2] = el->
rmag(p, 2);
1229 qWarning(
"One of the electrodes could not be projected onto the scalp surface. How come?");
1237 for (q = 0; q <
nsol; q++)
1244 tri = &scalp->
tris[best];
1245 scalp->
triangle_coords(Eigen::Map<const Eigen::Vector3f>(r),best,x,y,z);
1247 w[0] = el->
w[p]*(1.0 - x - y);
1250 for (v = 0; v < 3; v++) {
1251 for (q = 0; q <
nsol; q++)
1256 qWarning(
"Unknown BEM approximation method : %d",
bem_method);
1277 Eigen::Vector3f mri_rd = rd;
1278 Eigen::Vector3f mri_Q = Q;
1280 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1283 v0.resize(this->nsol);
1284 float* v0p =
v0.data();
1290 for (pp =
X; pp <=
Z; pp++) {
1291 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1293 ee = Eigen::Vector3f::Unit(pp);
1297 for (s = 0, p = 0; s <
nsurf; s++) {
1299 tri =
surfs[s]->tris.data();
1301 for (k = 0; k <
ntri; k++, tri++)
1307 for (k = 0; k <
nsol; k++)
1311 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1312 for (k = 0; k <
nsol; k++)
1331 Eigen::Vector3f mri_rd = rd;
1332 Eigen::Vector3f mri_Q = Q;
1335 v0.resize(this->nsol);
1336 float* v0p =
v0.data();
1342 for (s = 0, p = 0; s <
nsurf; s++) {
1345 for (k = 0; k <
np; k++)
1351 for (k = 0; k <
nsol; k++)
1355 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1356 for (k = 0; k <
nsol; k++)
1373 Eigen::Vector3f mri_rd = rd;
1374 Eigen::Vector3f mri_Q = Q;
1377 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1380 v0.resize(this->nsol);
1381 float* v0p =
v0.data();
1387 for (pp =
X; pp <=
Z; pp++) {
1388 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1390 ee = Eigen::Vector3f::Unit(pp);
1394 for (s = 0, p = 0; s <
nsurf; s++) {
1397 for (k = 0; k <
np; k++)
1403 for (k = 0; k <
nsol; k++)
1407 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1408 for (k = 0; k <
nsol; k++)
1426 Eigen::Vector3f mri_rd = rd;
1427 Eigen::Vector3f mri_Q = Q;
1430 v0.resize(this->nsol);
1431 float* v0p =
v0.data();
1437 for (s = 0, p = 0; s <
nsurf; s++) {
1439 tri =
surfs[s]->tris.data();
1441 for (k = 0; k <
ntri; k++, tri++)
1447 for (k = 0; k <
nsol; k++)
1451 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1452 for (k = 0; k <
nsol; k++)
1469 qWarning(
"No BEM model specified to fwd_bem_pot_els");
1472 if (m->solution.size() == 0) {
1473 qWarning(
"No solution available for fwd_bem_pot_els");
1477 qWarning(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1481 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1484 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1487 qWarning(
"Unknown BEM method : %d",m->bem_method);
1495int 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)
1504 qCritical(
"No BEM model specified to fwd_bem_pot_els");
1507 if (m->solution.size() == 0) {
1508 qCritical(
"No solution available for fwd_bem_pot_els");
1512 qCritical(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1516 int n = els.
ncoil();
1517 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1518 m->fwd_bem_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1521 int n = els.
ncoil();
1522 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1523 m->fwd_bem_lin_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1526 qCritical(
"Unknown BEM method : %d",m->bem_method);
1534inline double arsinh(
double x) {
return std::asinh(x); }
1536void FwdBemModel::calc_f(
const Eigen::Vector3d& xx,
const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1538 double det = -xx[1]*yy[0] + xx[2]*yy[0] +
1539 xx[0]*yy[1] - xx[2]*yy[1] - xx[0]*yy[2] + xx[1]*yy[2];
1541 f0[0] = -xx[2]*yy[1] + xx[1]*yy[2];
1542 f0[1] = xx[2]*yy[0] - xx[0]*yy[2];
1543 f0[2] = -xx[1]*yy[0] + xx[0]*yy[1];
1545 fx[0] = yy[1] - yy[2];
1546 fx[1] = -yy[0] + yy[2];
1547 fx[2] = yy[0] - yy[1];
1549 fy[0] = -xx[1] + xx[2];
1550 fy[1] = xx[0] - xx[2];
1551 fy[2] = -xx[0] + xx[1];
1562 double B2 = 1.0 + B*B;
1563 double ABu = A + B*u;
1564 D = sqrt(u*u + z*z + ABu*ABu);
1565 beta[0] = ABu/sqrt(u*u + z*z);
1566 beta[1] = (A*B + B2*u)/sqrt(A*A + B2*z*z);
1567 beta[2] = (B*z*z - A*u)/(z*D);
1572void 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)
1576 Eigen::Vector3d beta;
1577 double I1,Tx,Ty,Txx,Tyy,Sxx,mult;
1578 double S1x,S1y,S2x,S2y;
1587 Eigen::Vector3d y1 = (to.
r1 - from).cast<double>();
1588 Eigen::Vector3d y2 = (to.
r2 - from).cast<double>();
1589 Eigen::Vector3d y3 = (to.
r3 - from).cast<double>();
1593 Eigen::Vector3d ex_d = to.
ex.cast<
double>();
1594 Eigen::Vector3d ey_d = to.
ey.cast<
double>();
1595 xx[0] = y1.dot(ex_d);
1596 xx[1] = y2.dot(ex_d);
1597 xx[2] = y3.dot(ex_d);
1600 yy[0] = y1.dot(ey_d);
1601 yy[1] = y2.dot(ey_d);
1602 yy[2] = y3.dot(ey_d);
1605 calc_f(Eigen::Map<const Eigen::Vector3d>(xx), Eigen::Map<const Eigen::Vector3d>(yy), f0, fx, fy);
1609 z = y1.dot(to.
nn.cast<
double>());
1624 for (k = 0; k < 2; k++) {
1625 dx = xx[k+1] - xx[k];
1626 A = (yy[k]*xx[k+1] - yy[k+1]*xx[k])/dx;
1627 B = (yy[k+1]-yy[k])/dx;
1633 I1 = I1 - xx[k+1]*
arsinh(beta[0]) - (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1635 Txx =
arsinh(beta[1])/sqrt(B2);
1638 Sxx = (D1 - A*B*Txx)/B2;
1641 Sxx = (B*D1 + A*Txx)/B2;
1647 I1 = I1 + xx[k]*
arsinh(beta[0]) + (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1649 Txx =
arsinh(beta[1])/sqrt(B2);
1652 Sxx = (D1 - A*B*Txx)/B2;
1655 Sxx = (B*D1 + A*Txx)/B2;
1661 mult = 1.0/sqrt(xx[k]*xx[k]+z*z);
1665 Tyy =
arsinh(mult*yy[k+1]);
1667 S1y = S1y + xx[k]*Tyy;
1671 Tyy =
arsinh(mult*yy[k]);
1673 S1y = S1y - xx[k]*Tyy;
1694 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1695 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1696 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1698 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1699 for (j = 0; j < 3; j++)
1701 bbeta[0] = beta[2] - beta[0];
1702 bbeta[1] = beta[0] - beta[1];
1703 bbeta[2] = beta[1] - beta[2];
1705 Eigen::Vector3d coeff = Eigen::Vector3d::Zero();
1706 for (j = 0; j < 3; j++)
1707 coeff += bbeta[j] * (*yy[j]);
1708 return coeff.dot(normal.cast<
double>());
1728 qWarning(
"Solution matrix missing in fwd_bem_field_coeff");
1729 return Eigen::MatrixXf();
1732 qWarning(
"BEM method should be constant collocation for fwd_bem_field_coeff");
1733 return Eigen::MatrixXf();
1738 qWarning(
"head -> mri coordinate transform missing in fwd_bem_field_coeff");
1739 return Eigen::MatrixXf();
1746 return Eigen::MatrixXf();
1747 coils = tcoils.get();
1751 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1752 return Eigen::MatrixXf();
1756 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
ntri);
1758 for (s = 0, off = 0; s <
nsurf; s++) {
1759 surf =
surfs[s].get();
1761 tri = surf->
tris.data();
1764 for (k = 0; k <
ntri; k++,tri++) {
1765 for (j = 0; j < coils->
ncoil(); j++) {
1766 coil = coils->
coils[j].get();
1768 for (p = 0; p < coil->
np; p++)
1770 coeff(j,k+off) = mult*res;
1782 Eigen::Vector3d rkk1 = rk1 - rk;
1783 double size = rkk1.norm();
1785 return log((rk1.norm() * size + rk1.dot(rkk1)) /
1786 (rk.norm() * size + rk.dot(rkk1))) / size;
1793 double triple,l1,l2,l3,solid,clen;
1794 double common,sum,beta,
gamma;
1797 Eigen::Vector3d rjk[3];
1798 rjk[0] = (tri.
r3 - tri.
r2).cast<double>();
1799 rjk[1] = (tri.
r1 - tri.
r3).cast<double>();
1800 rjk[2] = (tri.
r2 - tri.
r1).cast<double>();
1802 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1803 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1804 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1806 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1808 Eigen::Vector3d nn_d = tri.
nn.cast<
double>();
1809 clen = y1.dot(nn_d);
1810 Eigen::Vector3d c_vec = clen * nn_d;
1811 Eigen::Vector3d A_vec = dest.cast<
double>() + c_vec;
1813 Eigen::Vector3d c1 = tri.
r1.cast<
double>() - A_vec;
1814 Eigen::Vector3d c2 = tri.
r2.cast<
double>() - A_vec;
1815 Eigen::Vector3d c3 = tri.
r3.cast<
double>() - A_vec;
1817 const Eigen::Vector3d* cc[4] = { &c1, &c2, &c3, &c1 };
1821 for (sum = 0.0, k = 0; k < 3; k++) {
1822 Eigen::Vector3d cross = cc[k]->cross(*cc[k+1]);
1823 beta = cross.dot(nn_d);
1825 sum = sum + beta*
gamma;
1830 Eigen::Vector3d cross = y1.cross(y2);
1831 triple = cross.dot(y3);
1836 solid = 2.0*atan2(triple,
1844 Eigen::Vector3d dir_d = dir.cast<
double>();
1845 common = (sum-clen*solid)/(2.0*tri.
area);
1846 for (k = 0; k < 3; k++)
1847 res[k] = -rjk[k].dot(dir_d)*common;
1856 Eigen::Vector2d T, S1, S2;
1857 Eigen::Vector3d f0, fx, fy;
1868 Eigen::Vector3f dir = dir_in.normalized();
1870 x_fac = -dir.dot(tri.
ex);
1871 y_fac = -dir.dot(tri.
ey);
1872 for (k = 0; k < 3; k++) {
1873 res_x = f0[k]*T[0] + fx[k]*S1[0] + fy[k]*S2[0] + fy[k]*I1;
1874 res_y = f0[k]*T[1] + fx[k]*S1[1] + fy[k]*S2[1] - fx[k]*I1;
1875 res[k] = x_fac*res_x + y_fac*res_y;
1884 const Eigen::Vector3f* rr[3] = { &source.
r1, &source.
r2, &source.
r3 };
1886 for (k = 0; k < 3; k++) {
1887 Eigen::Vector3f diff = dest - *rr[k];
1888 float dl = diff.squaredNorm();
1889 Eigen::Vector3f vec_result = diff.cross(source.
nn);
1890 res[k] = source.
area*vec_result.dot(normal)/(3.0*dl*sqrt(dl));
1909 Eigen::Vector3d res, one;
1914 qWarning(
"Solution matrix missing in fwd_bem_lin_field_coeff");
1915 return Eigen::MatrixXf();
1918 qWarning(
"BEM method should be linear collocation for fwd_bem_lin_field_coeff");
1919 return Eigen::MatrixXf();
1924 qWarning(
"head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
1925 return Eigen::MatrixXf();
1932 return Eigen::MatrixXf();
1933 coils = tcoils.get();
1937 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1938 return Eigen::MatrixXf();
1948 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
nsol);
1952 for (s = 0, off = 0; s <
nsurf; s++) {
1953 surf =
surfs[s].get();
1955 tri = surf->
tris.data();
1958 for (k = 0; k <
ntri; k++,tri++) {
1959 for (j = 0; j < coils->
ncoil(); j++) {
1960 coil = coils->
coils[j].get();
1965 for (p = 0; p < coil->
np; p++) {
1966 func(coil->
pos(p),coil->
dir(p),*tri,one);
1967 res += coil->
w[p] * one;
1973 for (pp = 0; pp < 3; pp++)
1974 coeff(j,tri->
vert[pp]+off) = coeff(j,tri->
vert[pp]+off) + mult*res[pp];
1977 off = off + surf->
np;
1992 Eigen::MatrixXf sol;
1996 qWarning(
"Solution not computed in fwd_bem_specify_coils");
2001 if (!coils || coils->
ncoil() == 0)
2008 qWarning(
"Unknown BEM method in fwd_bem_specify_coils : %d",
bem_method);
2011 if (sol.size() == 0)
2013 coils->
user_data = std::make_unique<FwdBemSolution>();
2033 Eigen::Vector3f my_rd = rd;
2034 Eigen::Vector3f my_Q = Q;
2041 float* v0p =
v0.data();
2052 for (s = 0, p = 0; s <
nsurf; s++) {
2055 for (k = 0; k <
np; k++)
2062 for (k = 0; k < coils.
ncoil(); k++) {
2063 coil = coils.
coils[k].get();
2065 for (p = 0; p < coil->
np; p++)
2075 for (k = 0; k < coils.
ncoil(); k++)
2080 for (k = 0; k < coils.
ncoil(); k++)
2096 Eigen::Vector3f my_rd = rd;
2097 Eigen::Vector3f my_Q = Q;
2104 float* v0p =
v0.data();
2115 for (s = 0, p = 0; s <
nsurf; s++) {
2117 tri =
surfs[s]->tris.data();
2119 for (k = 0; k <
ntri; k++, tri++)
2126 for (k = 0; k < coils.
ncoil(); k++) {
2127 coil = coils.
coils[k].get();
2129 for (p = 0; p < coil->
np; p++)
2139 for (k = 0; k < coils.
ncoil(); k++)
2144 for (k = 0; k < coils.
ncoil(); k++)
2161 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2162 Eigen::Vector3f ee, mri_ee;
2163 Eigen::Vector3f mri_rd = rd;
2164 Eigen::Vector3f mri_Q = Q;
2170 float* v0p =
v0.data();
2178 for (pp =
X; pp <=
Z; pp++) {
2179 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2183 ee = Eigen::Vector3f::Unit(pp);
2190 for (s = 0, p = 0; s <
nsurf; s++) {
2192 tri =
surfs[s]->tris.data();
2194 for (k = 0; k <
ntri; k++, tri++) {
2202 for (k = 0; k < coils.
ncoil(); k++) {
2203 coil = coils.
coils[k].get();
2205 for (p = 0; p < coil->
np; p++)
2216 for (k = 0; k < coils.
ncoil(); k++)
2217 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2221 for (k = 0; k < coils.
ncoil(); k++)
2235 Eigen::Vector3f diff = rp - rd;
2236 float diff2 = diff.squaredNorm();
2237 float diff3 = std::sqrt(diff2) * diff2;
2238 float diff5 = diff3 * diff2;
2239 Eigen::Vector3f cr = Q.cross(diff);
2240 Eigen::Vector3f crn = dir.cross(Q);
2242 return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3;
2253 Eigen::Vector3f diff = rp - rd;
2254 float diff2 = diff.squaredNorm();
2255 float diff3 = std::sqrt(diff2) * diff2;
2256 float diff5 = diff3 * diff2;
2258 float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3;
2259 return res / (4.0 *
M_PI);
2274 Eigen::Vector3f ee, mri_ee;
2275 Eigen::Vector3f mri_rd = rd;
2276 Eigen::Vector3f mri_Q = Q;
2277 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2283 float* v0p =
v0.data();
2291 for (pp =
X; pp <=
Z; pp++) {
2292 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2296 ee = Eigen::Vector3f::Unit(pp);
2303 for (s = 0, p = 0; s <
nsurf; s++) {
2307 for (k = 0; k <
np; k++)
2314 for (k = 0; k < coils.
ncoil(); k++) {
2315 coil = coils.
coils[k].get();
2317 for (p = 0; p < coil->
np; p++)
2328 for (k = 0; k < coils.
ncoil(); k++)
2329 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2333 for (k = 0; k < coils.
ncoil(); k++)
2352 qWarning(
"No BEM model specified to fwd_bem_field");
2356 qWarning(
"No appropriate coil-specific data available in fwd_bem_field");
2360 m->fwd_bem_field_calc(rd,Q,coils,B);
2363 m->fwd_bem_lin_field_calc(rd,Q,coils,B);
2366 qWarning(
"Unknown BEM method : %d",m->bem_method);
2375 const Eigen::Vector3f& Q,
2377 Eigen::Ref<Eigen::VectorXf> Bval,
2378 Eigen::Ref<Eigen::VectorXf> xgrad,
2379 Eigen::Ref<Eigen::VectorXf> ygrad,
2380 Eigen::Ref<Eigen::VectorXf> zgrad,
2387 qCritical(
"No BEM model specified to fwd_bem_field");
2392 qCritical(
"No appropriate coil-specific data available in fwd_bem_field");
2397 int n = coils.
ncoil();
2398 m->fwd_bem_field_calc(rd,Q,coils,Bval);
2400 m->fwd_bem_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2402 int n = coils.
ncoil();
2403 m->fwd_bem_lin_field_calc(rd,Q,coils,Bval);
2405 m->fwd_bem_lin_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2407 qCritical(
"Unknown BEM method : %d",m->bem_method);
2426 Eigen::MatrixXf tmp_vec_res(3, ncoil);
2428 auto fail = [&]() { a->
stat =
FAIL; };
2434 for (j = 0; j < s->
np; j++) {
2451 for (j = 0; j < s->
np; j++)
2466 for (j = 0; j < s->
np; j++) {
2488 else if (a->
comp == 0) {
2498 else if (a->
comp == 1) {
2508 else if (a->
comp == 2) {
2522 for (j = 0; j < s->
np; j++) {
2528 a->
res->col(p++) = tmp_vec_res.row(0).transpose();
2529 a->
res->col(p++) = tmp_vec_res.row(1).transpose();
2530 a->
res->col(p++) = tmp_vec_res.row(2).transpose();
2544 else if (a->
comp == 0) {
2550 else if (a->
comp == 1) {
2557 else if (a->
comp == 2) {
2589 Eigen::MatrixXf res_mat;
2590 Eigen::MatrixXf res_grad_mat;
2592 MatrixXd matResGrad;
2599 int nmeg = coils->
ncoil();
2601 int nspace =
static_cast<int>(spaces.size());
2606 int nproc = QThread::idealThreadCount();
2607 QStringList emptyList;
2609 auto cleanup_fail = [&]() { one_arg.reset();
delete comp;
return FAIL; };
2617 qInfo(
"Using differences.");
2634 return cleanup_fail();
2638 qInfo(
"Composing the field computation matrix...");
2640 return cleanup_fail();
2644 qInfo(
"Composing the field computation matrix (compensation coils)...");
2646 return cleanup_fail();
2650 vec_field =
nullptr;
2660 qInfo(
"Using differences.");
2664 my_sphere_field_grad,
2665 const_cast<Vector3f*
>(&r0));
2671 const_cast<Vector3f*
>(&r0));
2674 return cleanup_fail();
2683 for (k = 0, nsource = 0; k < nspace; k++)
2684 nsource += spaces[k]->nuse;
2689 int ncols = fixed_ori ? nsource : 3*nsource;
2690 res_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2693 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2694 res_grad_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2699 one_arg = std::make_unique<FwdThreadArg>();
2700 one_arg->res = &res_mat;
2701 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2703 one_arg->coils_els = coils;
2704 one_arg->client = client;
2705 one_arg->s =
nullptr;
2706 one_arg->fixed_ori = fixed_ori;
2707 one_arg->field_pot = field;
2708 one_arg->vec_field_pot = vec_field;
2709 one_arg->field_pot_grad = field_grad;
2712 use_threads =
false;
2715 int nthread = (fixed_ori || vec_field || nproc < 6) ? nspace : 3*nspace;
2716 std::vector<FwdThreadArg::UPtr> args;
2721 if (fixed_ori || vec_field || nproc < 6) {
2722 for (k = 0, off = 0; k < nthread; k++) {
2724 t_arg->s = spaces[k].get();
2726 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2727 args.push_back(std::move(t_arg));
2729 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",
2733 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2734 for (p = 0; p < 3; p++,q++) {
2736 t_arg->s = spaces[k].get();
2739 args.push_back(std::move(t_arg));
2741 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2743 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",
2744 nproc,nthread,nspace);
2746 qInfo(
"Computing MEG at %d source locations (%s orientations)...",
2747 nsource,fixed_ori ?
"fixed" :
"free");
2757 for (k = 0, stat =
OK; k < nthread; k++)
2758 if (args[k]->stat !=
OK) {
2763 return cleanup_fail();
2766 qInfo(
"Computing MEG at %d source locations (%s orientations, no threads)...",
2767 nsource,fixed_ori ?
"fixed" :
"free");
2768 for (k = 0, off = 0; k < nspace; k++) {
2769 one_arg->s = spaces[k].get();
2772 if (one_arg->stat !=
OK)
2773 return cleanup_fail();
2774 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2779 QStringList orig_names;
2780 for (k = 0; k < nmeg; k++)
2781 orig_names.append(coils->
coils[k]->chname);
2789 nrow = fixed_ori ? nsource : 3*nsource;
2794 resp.
data = res_mat.transpose().cast<
double>();
2797 if (bDoGrad && res_grad_mat.size() > 0) {
2798 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2799 resp_grad.
nrow = nrow;
2800 resp_grad.
ncol = nmeg;
2803 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
2824 Eigen::MatrixXf res_mat;
2825 Eigen::MatrixXf res_grad_mat;
2827 MatrixXd matResGrad;
2834 int nspace =
static_cast<int>(spaces.size());
2835 int neeg = els->
ncoil();
2840 int nproc = QThread::idealThreadCount();
2841 QStringList emptyList;
2845 for (k = 0, nsource = 0; k < nspace; k++)
2846 nsource += spaces[k]->nuse;
2855 qInfo(
"Using differences.");
2856 pot_grad = my_bem_pot_grad;
2862 if (eeg_model->
nfit == 0) {
2863 qInfo(
"Using the standard series expansion for a multilayer sphere model for EEG");
2869 qInfo(
"Using the equivalent source approach in the homogeneous sphere for EEG");
2880 int ncols = fixed_ori ? nsource : 3*nsource;
2881 res_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2885 qCritical(
"EEG gradient calculation function not available");
2888 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2889 res_grad_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2894 one_arg = std::make_unique<FwdThreadArg>();
2895 one_arg->res = &res_mat;
2896 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2898 one_arg->coils_els = els;
2899 one_arg->client = client;
2900 one_arg->s =
nullptr;
2901 one_arg->fixed_ori = fixed_ori;
2902 one_arg->field_pot = pot;
2903 one_arg->vec_field_pot = vec_pot;
2904 one_arg->field_pot_grad = pot_grad;
2907 use_threads =
false;
2910 int nthread = (fixed_ori || vec_pot || nproc < 6) ? nspace : 3*nspace;
2911 std::vector<FwdThreadArg::UPtr> args;
2916 if (fixed_ori || vec_pot || nproc < 6) {
2917 for (k = 0, off = 0; k < nthread; k++) {
2919 t_arg->s = spaces[k].get();
2921 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2922 args.push_back(std::move(t_arg));
2924 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",nproc,nspace);
2927 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2928 for (p = 0; p < 3; p++,q++) {
2930 t_arg->s = spaces[k].get();
2933 args.push_back(std::move(t_arg));
2935 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2937 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",nproc,nthread,nspace);
2939 qInfo(
"Computing EEG at %d source locations (%s orientations)...",
2940 nsource,fixed_ori ?
"fixed" :
"free");
2950 for (k = 0, stat =
OK; k < nthread; k++)
2951 if (args[k]->stat !=
OK) {
2959 qInfo(
"Computing EEG at %d source locations (%s orientations, no threads)...",
2960 nsource,fixed_ori ?
"fixed" :
"free");
2961 for (k = 0, off = 0; k < nspace; k++) {
2962 one_arg->s = spaces[k].get();
2965 if (one_arg->stat !=
OK)
2967 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2972 QStringList orig_names;
2973 for (k = 0; k < neeg; k++)
2974 orig_names.append(els->
coils[k]->chname);
2980 nrow = fixed_ori ? nsource : 3*nsource;
2985 resp.
data = res_mat.transpose().cast<
double>();
2988 if (bDoGrad && res_grad_mat.size() > 0) {
2989 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2990 resp_grad.
nrow = nrow;
2991 resp_grad.
ncol = neeg;
2994 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
3018 auto* r0 =
static_cast<float*
>(client);
3022 float F,g0,gr,result,sum;
3030 Eigen::Vector3f myrd = rd - Eigen::Map<const Eigen::Vector3f>(r0);
3034 for (k = 0 ; k < coils.
ncoil() ; k++)
3040 Eigen::Vector3f v = Q.cross(myrd);
3042 for (k = 0; k < coils.
ncoil(); k++) {
3043 this_coil = coils.
coils[k].get();
3048 for (j = 0, sum = 0.0; j <
np; j++) {
3050 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3051 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3053 Eigen::Vector3f pos = this_pos_raw - Eigen::Map<const Eigen::Vector3f>(r0);
3058 Eigen::Vector3f a_vec = pos - myrd;
3062 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3065 r2 = pos.squaredNorm(); r = sqrt(r2);
3067 rr0 = pos.dot(myrd);
3069 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3073 ve = v.dot(this_dir); vr = v.dot(pos);
3074 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3079 gr = a2/r + ar0 + 2.0*(a+r);
3084 sum = sum + this_coil->
w[j]*(ve*F + vr*(g0*r0e - gr*re))/(F*F);
3120 auto* r0 =
static_cast<float*
>(client);
3128 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3133 Eigen::Vector3f myrd = rd - r0_vec;
3138 for (k = 0; k < coils.
ncoil(); k++) {
3139 this_coil = coils.
coils[k].get();
3142 Bval(0,k) = Bval(1,k) = Bval(2,k) = 0.0;
3147 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3149 for (j = 0; j <
np; j++) {
3151 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3152 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3154 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3158 Eigen::Vector3f a_vec = pos - myrd;
3162 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3165 r2 = pos.squaredNorm(); r = sqrt(r2);
3167 rr0 = pos.dot(myrd);
3169 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3176 gr = a2/r + ar0 + 2.0*(a+r);
3179 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3180 Eigen::Vector3f v1 = myrd.cross(this_dir);
3181 Eigen::Vector3f v2 = myrd.cross(pos);
3183 g = (g0*r0e - gr*re)/(F*F);
3187 sum += this_coil->
w[j]*(v1/F + v2*g);
3192 for (p = 0; p < 3; p++)
3202int 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)
3224 float F,g0,gr,result,G,F2;
3230 auto* r0 =
static_cast<float*
>(client);
3231 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3233 int ncoil = coils.
ncoil();
3237 Eigen::Vector3f myrd = rd - r0_vec;
3241 float r = myrd.norm();
3242 for (k = 0; k < ncoil ; k++) {
3252 Eigen::Vector3f v = Q.cross(myrd);
3254 for (k = 0 ; k < ncoil ; k++) {
3256 this_coil = coils.
coils[k].get();
3262 for (j = 0; j <
np; j++) {
3264 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3268 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3270 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3274 Eigen::Vector3f a_vec = pos - myrd;
3278 float a2 = a_vec.squaredNorm();
float a = sqrt(a2);
3279 float r2 = pos.squaredNorm(); r = sqrt(r2);
3280 float rr0 = pos.dot(myrd);
3281 float ar = (r2 - rr0)/a;
3283 ve = v.dot(this_dir); vr = v.dot(pos);
3284 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3288 Eigen::Vector3f eQ = this_dir.cross(Q);
3292 Eigen::Vector3f rQ = pos.cross(Q);
3296 F = a*(r*a + r2 - rr0);
3298 gr = a2/r + ar + 2.0*(a+r);
3299 g0 = a + 2.0*r + ar;
3304 result = (ve*F + vr*G)/F2;
3308 huu = 2.0 + 2.0*a/r;
3309 Eigen::Vector3f ga = -a_vec/a;
3310 Eigen::Vector3f gar = -(ga*ar + pos)/a;
3311 Eigen::Vector3f gg0 = ga + gar;
3312 Eigen::Vector3f ggr = huu*ga + gar;
3313 Eigen::Vector3f gFF = ga/a - (r*a_vec + a*pos)/F;
3314 Eigen::Vector3f gresult = -2.0f*result*gFF + (eQ+gFF*ve)/F +
3315 (rQ*G + vr*(gg0*r0e + g0*this_dir - ggr*re))/F2;
3317 Bval[k] = Bval[k] + this_coil->
w[j]*result;
3318 xgrad[k] = xgrad[k] + this_coil->
w[j]*gresult[0];
3319 ygrad[k] = ygrad[k] + this_coil->
w[j]*gresult[1];
3320 zgrad[k] = zgrad[k] + this_coil->
w[j]*gresult[2];
3341 float sum,dist,dist2,dist5;
3344 for (k = 0; k < coils.
ncoil(); k++) {
3345 this_coil = coils.
coils[k].get();
3351 for (j = 0, sum = 0.0; j <
np; j++) {
3352 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3353 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3357 dist5 = dist2*dist2*dist;
3358 sum = sum + this_coil->
w[j]*(3*M.dot(diff)*diff.dot(dir) - dist2*M.dot(dir))/dist5;
3379 float dist,dist2,dist5;
3382 for (k = 0; k < coils.
ncoil(); k++) {
3383 this_coil = coils.
coils[k].get();
3386 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3390 for (j = 0; j <
np; j++) {
3391 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3392 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3396 dist5 = dist2*dist2*dist;
3397 for (p = 0; p < 3; p++)
3398 sum[p] = sum[p] + this_coil->
w[j]*(3*diff[p]*diff.dot(dir) - dist2*dir[p])/dist5;
3401 for (p = 0; p < 3; p++)
3405 for (p = 0; p < 3; p++)
FwdBemSolution class declaration.
FwdThreadArg class declaration.
FwdBemModel class declaration.
FwdCompData 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
FwdEegSphereModel class declaration.
const QString mne_coord_frame_name_40(int frame)
FiffNamedMatrix 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
FiffStream class declaration.
#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
MNESourceSpace class declaration.
MNETriangle class declaration.
MNESurface 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
static std::unique_ptr< MNESurface > read_bem_surface(const QString &name, int which, bool add_geometry)
int project_to_surface(const MNEProjData *proj_data, const Eigen::Vector3f &r, float &distp) const
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