41#include <QtConcurrent>
43#define _USE_MATH_DEFINES
48static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
49static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
50static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
60constexpr int FAIL = -1;
62constexpr int LOADED = 1;
63constexpr int NOT_FOUND = 0;
65constexpr auto BEM_SUFFIX =
"-bem.fif";
66constexpr auto BEM_SOL_SUFFIX =
"-bem-sol.fif";
67constexpr float EPS = 1e-5f;
68constexpr float CEPS = 1e-5f;
95static QString strip_from(
const QString& s,
const QString& suffix)
99 if (s.endsWith(suffix)) {
101 res.chop(suffix.size());
145 for (k = 0; frames[k].
frame != -1; k++) {
146 if (frame == frames[k].frame)
147 return frames[k].
name;
149 return frames[k].
name;
156using namespace Eigen;
203 s1 = strip_from(name,
".fif");
204 s2 = strip_from(s1,
"-sol");
205 s1 = strip_from(s2,
"-bem");
206 s2 = QString(
"%1%2").arg(s1).arg(BEM_SOL_SUFFIX);
216 for (k = 0; surf_expl[k].kind >= 0; k++)
217 if (surf_expl[k].kind == kind)
218 return surf_expl[k].name;
220 return surf_expl[k].name;
230 for (k = 0; method_expl[k].method >= 0; k++)
231 if (method_expl[k].method == method)
232 return method_expl[k].name;
234 return method_expl[k].name;
245 if(node->find_tag(stream, what, t_pTag)) {
247 qWarning(
"Expected an integer tag : %d (found data type %d instead)",what,t_pTag->getType() );
250 *res = *t_pTag->toInt();
260 for (
int k = 0; k < this->
nsurf; k++)
261 if (this->
surfs[k]->
id == kind)
262 return this->
surfs[k].get();
274 std::vector<std::shared_ptr<MNESurface>>
surfs;
275 const int nkind =
static_cast<int>(kinds.size());
276 Eigen::VectorXf sigma_tmp(nkind);
280 qCritical(
"No surfaces specified to fwd_bem_load_surfaces");
284 for (k = 0; k < nkind; k++) {
294 qCritical(
"FsSurface %s not specified in MRI coordinates.",
fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
298 surfs.push_back(std::move(s));
300 auto m = std::make_unique<FwdBemModel>();
304 m->surfs = std::move(
surfs);
305 m->sigma = sigma_tmp;
306 m->ntri.resize(nkind);
308 m->gamma.resize(nkind, nkind);
309 m->source_mult.resize(nkind);
310 m->field_mult.resize(nkind);
314 Eigen::VectorXf sigma1(nkind + 1);
316 sigma1.tail(nkind) = m->sigma;
321 for (j = 0; j < m->nsurf; j++) {
322 m->ntri[j] = m->surfs[j]->ntri;
323 m->np[j] = m->surfs[j]->np;
324 m->source_mult[j] = 2.0f / (sigma1[j+1] + sigma1[j]);
325 m->field_mult[j] = sigma1[j+1] - sigma1[j];
326 for (k = 0; k < m->nsurf; k++)
327 m->gamma(j, k) = (sigma1[k+1] - sigma1[k]) / (sigma1[j+1] + sigma1[j]);
368 if(!stream->open()) {
377 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(
FIFFB_BEM);
379 if (nodes.size() == 0) {
380 qWarning(
"No BEM data in %s",name.toUtf8().constData());
398 qWarning(
"Cannot handle BEM approximation method : %d",method);
403 qWarning(
"Approximation method in file : %d desired : %d",method,
bem_method);
415 QVector<qint32> dims;
416 t_pTag->getMatrixDimensions(ndim, dims);
419 qWarning(
"Expected a two-dimensional solution matrix instead of a %d dimensional one",ndim);
423 for (k = 0, dim = 0; k <
nsurf; k++)
425 if (dims[0] != dim || dims[1] != dim) {
426 qWarning(
"Expected a %d x %d solution matrix instead of a %d x %d one",dim,dim,dims[0],dims[1]);
431 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
436 solution = t_pTag->toFloatMatrix().transpose();
438 this->bem_method = method;
460 qWarning(
"Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
476 qInfo(
"Making a spherical guess space with radius %7.1f mm...",1000*guessrad);
478 QFile bemFile(QString(QCoreApplication::applicationDirPath() +
"/../resources/general/surf2bem/icos.fif"));
479 if ( !QCoreApplication::startingUp() )
480 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString(
"/../resources/general/surf2bem/icos.fif"));
481 else if (!bemFile.exists())
482 bemFile.setFileName(
"../resources/general/surf2bem/icos.fif");
484 if( !bemFile.exists () ){
485 qDebug() << bemFile.fileName() <<
"does not exists.";
489 bemname = bemFile.fileName();
495 for (k = 0; k < sphere_owner->np; k++) {
496 dist = sphere_owner->point(k).norm();
497 sphere_owner->rr.row(k) = (guessrad * sphere_owner->rr.row(k) / dist) + guess_r0.transpose();
499 if (sphere_owner->add_geometry_info(
true) ==
FAIL)
501 guess_surf = sphere_owner.get();
504 qInfo(
"Guess surface (%d = %s) is in %s coordinates",
508 qInfo(
"Filtering (grid = %6.f mm)...",1000*grid);
519 Eigen::Vector3d rkk1 = rk1 - rk;
520 double size = rkk1.norm();
522 return log((rk.norm() * size + rk.dot(rkk1)) /
523 (rk1.norm() * size + rk1.dot(rkk1))) / size;
533 Eigen::Vector3d y1, y2, y3;
536 Eigen::Vector3d vec_omega;
539 double beta[3],bbeta[3];
542 static const double solid_eps = 4.0*
M_PI/1.0E6;
546 y1 = (to.
r1 - from).cast<double>();
547 y2 = (to.
r2 - from).cast<double>();
548 y3 = (to.
r3 - from).cast<double>();
552 const Eigen::Vector3d* y_arr[5] = { &y3, &y1, &y2, &y3, &y1 };
553 const Eigen::Vector3d** yy = y_arr + 1;
557 Eigen::Vector3d cross = y1.cross(y2);
558 triple = cross.dot(y3);
563 ss = (l1*l2*l3+y1.dot(y2)*l3+y1.dot(y3)*l2+y2.dot(y3)*l1);
564 solid = 2.0*atan2(triple,ss);
565 if (std::fabs(solid) < solid_eps) {
572 for (j = 0; j < 3; j++)
574 bbeta[0] = beta[2] - beta[0];
575 bbeta[1] = beta[0] - beta[1];
576 bbeta[2] = beta[1] - beta[2];
579 for (j = 0; j < 3; j++)
580 vec_omega += bbeta[j] * (*yy[j]);
585 n2 = 1.0/(area2*area2);
586 Eigen::Vector3d nn_d = to.
nn.cast<
double>();
587 for (k = 0; k < 3; k++) {
588 Eigen::Vector3d z = yy[k+1]->cross(*yy[k-1]);
589 Eigen::Vector3d diff = *yy[k-1] - *yy[k+1];
590 omega[k] = n2*(-area2*z.dot(nn_d)*solid +
591 triple*diff.dot(vec_omega));
600 double rel1 = (solid + omega[0]+omega[1]+omega[2])/solid;
604 Eigen::Vector3d check = Eigen::Vector3d::Zero();
605 Eigen::Vector3d nn_check = to.
nn.cast<
double>();
606 for (k = 0; k < 3; k++) {
607 Eigen::Vector3d z = nn_check.cross(*yy[k]);
610 check *= -area2/triple;
611 fprintf (stderr,
"(%g,%g,%g) =? (%g,%g,%g)\n",
612 check[0],check[1],check[2],
613 vec_omega[0],vec_omega[1],vec_omega[2]);
615 double rel2 = sqrt(check.dot(check)/vec_omega.dot(vec_omega));
616 fprintf (stderr,
"err1 = %g, err2 = %g\n",100*rel1,100*rel2);
633 float pi2 = 2.0*
M_PI;
637 for (j = 0; j < nnode; j++) {
639 for (k = 0; k < nnode; k++)
640 sum = sum + mat(j,k);
641 fprintf (stderr,
"row %d sum = %g missing = %g\n",j+1,sum/pi2,
643 mat(j,j) = pi2 - sum;
646 for (j = 0; j < nnode; j++) {
651 for (k = 0; k < nnode; k++)
652 sum = sum + mat(j,k);
662 miss = miss/(4.0*nmemb);
663 for (k = 0,tri = surf.
tris.data(); k <
ntri; k++,tri++) {
664 if (tri->
vert[0] == j) {
665 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
666 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
668 else if (tri->
vert[1] == j) {
669 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
670 mat(j,tri->
vert[2]) = mat(j,tri->
vert[2]) + miss;
672 else if (tri->
vert[2] == j) {
673 mat(j,tri->
vert[0]) = mat(j,tri->
vert[0]) + miss;
674 mat(j,tri->
vert[1]) = mat(j,tri->
vert[1]) + miss;
696 int np1,np2,
ntri,np_tot,np_max;
698 Eigen::Vector3d omega;
704 for (p = 0, np_tot = np_max = 0; p <
surfs.size(); p++) {
705 np_tot +=
surfs[p]->np;
707 np_max =
surfs[p]->np;
710 Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(np_tot, np_tot);
711 Eigen::VectorXd row(np_max);
712 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + np1) {
715 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + np2) {
720 qInfo(
"\t\t%s (%d) -> %s (%d) ... ",
724 for (j = 0; j < np1; j++) {
725 for (k = 0; k < np2; k++)
727 for (k = 0, tri = surf2->
tris.data(); k <
ntri; k++,tri++) {
732 if (p == q && (tri->
vert[0] == j || tri->
vert[1] == j || tri->
vert[2] == j))
738 for (c = 0; c < 3; c++)
739 row[tri->
vert[c]] = row[tri->
vert[c]] - omega[c];
741 for (k = 0; k < np2; k++)
742 mat(j+joff,k+koff) = row[k];
745 Eigen::MatrixXf sub_mat = mat.block(joff, koff, np1, np1);
747 mat.block(joff, koff, np1, np1) = sub_mat;
762 Eigen::MatrixXf coeff;
769 std::vector<MNESurface*> rawSurfs;
770 rawSurfs.reserve(
nsurf);
771 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
773 qInfo(
"\nComputing the linear collocation solution...");
774 qInfo(
"\tMatrix coefficients...");
776 if (coeff.size() == 0) {
784 qInfo(
"\tInverting the coefficient matrix...");
796 Eigen::MatrixXf ip_solution;
798 qInfo(
"IP approach required...");
800 qInfo(
"\tMatrix coefficients (homog)...");
801 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
803 if (coeff.size() == 0) {
808 qInfo(
"\tInverting the coefficient matrix (homog)...");
810 if (ip_solution.size() == 0) {
815 qInfo(
"\tModify the original solution to incorporate IP approach...");
820 qInfo(
"Solution ready.");
836 float pi2 = 1.0/(2*
M_PI);
838 int joff,koff,jup,kup,ntot;
840 for (j = 0,ntot = 0; j <
nsurf; j++)
846 for (p = 0, joff = 0; p <
nsurf; p++) {
847 jup =
ntri[p] + joff;
848 for (q = 0, koff = 0; q <
nsurf; q++) {
849 kup =
ntri[q] + koff;
850 mult = (
gamma ==
nullptr) ? pi2 : pi2 * (*gamma)(p, q);
851 for (j = joff; j < jup; j++)
852 for (k = koff; k < kup; k++)
853 solids(j,k) = defl - solids(j,k)*mult;
858 for (k = 0; k < ntot; k++)
859 solids(k,k) = solids(k,k) + 1.0;
861 Eigen::MatrixXf result = solids.inverse();
886 int j,k,joff,koff,ntot,nlast;
889 for (s = 0, koff = 0; s <
nsurf-1; s++)
890 koff = koff +
ntri[s];
894 Eigen::VectorXf row(nlast);
895 mult = (1.0 + ip_mult)/ip_mult;
897 qInfo(
"\t\tCombining...");
899 ip_solution.transposeInPlace();
901 for (s = 0, joff = 0; s <
nsurf; s++) {
907 for (j = 0; j <
ntri[s]; j++) {
908 for (k = 0; k < nlast; k++) {
909 row[k] =
solution.row(j + joff).segment(koff, nlast).dot(ip_solution.row(k).head(nlast));
911 solution.row(j + joff).segment(koff, nlast) -= 2.0f * row.transpose();
913 joff = joff +
ntri[s];
917 ip_solution.transposeInPlace();
923 for (j = 0; j < nlast; j++)
924 for (k = 0; k < nlast; k++)
925 solution(j + koff, k + koff) += mult * ip_solution(j,k);
929 qInfo(
"done.\n\t\tScaling...");
946 Eigen::VectorXf sums(ntri1);
947 for (j = 0; j < ntri1; j++) {
949 for (k = 0; k < ntri2; k++)
950 sum = sum + angles(j,k);
951 sums[j] = sum/(2*
M_PI);
953 for (j = 0; j < ntri1; j++)
960 if (std::fabs(sums[j]-desired) > 1e-4) {
961 qWarning(
"solid angle matrix: rowsum[%d] = 2PI*%g",
979 int ntri1,ntri2,ntri_tot;
985 for (p = 0,ntri_tot = 0; p <
surfs.size(); p++)
988 Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot);
989 for (p = 0, joff = 0; p <
surfs.size(); p++, joff = joff + ntri1) {
992 for (q = 0, koff = 0; q <
surfs.size(); q++, koff = koff + ntri2) {
996 for (j = 0; j < ntri1; j++)
997 for (k = 0, tri = surf2->
tris.data(); k < ntri2; k++, tri++) {
998 if (p == q && j == k)
1002 solids(j+joff,k+koff) = result;
1011 Eigen::MatrixXf sub_block = solids.block(joff, koff, ntri1, ntri2);
1013 return Eigen::MatrixXf();
1027 Eigen::MatrixXf solids;
1034 std::vector<MNESurface*> rawSurfs;
1035 rawSurfs.reserve(
nsurf);
1036 for (
auto& s :
surfs) rawSurfs.push_back(s.get());
1038 qInfo(
"\nComputing the constant collocation solution...");
1039 qInfo(
"\tSolid angles...");
1041 if (solids.size() == 0) {
1049 qInfo(
"\tInverting the coefficient matrix...");
1060 Eigen::MatrixXf ip_solution;
1062 qInfo(
"IP approach required...");
1064 qInfo(
"\tSolid angles (homog)...");
1065 std::vector<MNESurface*> last_surfs = {
surfs.back().get() };
1067 if (solids.size() == 0) {
1072 qInfo(
"\tInverting the coefficient matrix (homog)...");
1074 if (ip_solution.size() == 0) {
1079 qInfo(
"\tModify the original solution to incorporate IP approach...");
1083 qInfo(
"Solution ready.");
1104 qWarning(
"Unknown BEM method: %d",
bem_method);
1117 if (!force_recompute) {
1120 if (solres == LOADED) {
1121 qInfo(
"\nLoaded %s BEM solution from %s",
fwd_bem_explain_method(this->bem_method).toUtf8().constData(),name.toUtf8().constData());
1124 else if (solres ==
FAIL)
1128 qWarning(
"Desired BEM solution not available in %s (%s)",name,err_get_error());
1144 Eigen::Vector3f diff = rp - rd;
1145 float diff2 = diff.squaredNorm();
1146 Eigen::Vector3f cr = Q.cross(diff);
1148 return cr.dot(dir) / (diff2 * std::sqrt(diff2));
1158 Eigen::Vector3f diff = rp - rd;
1159 float diff2 = diff.squaredNorm();
1160 return Q.dot(diff) / (4.0 *
M_PI * diff2 * std::sqrt(diff2));
1173 float r[3],w[3],dist;
1180 qWarning(
"Solution not computed in fwd_bem_specify_els");
1183 if (!els || els->
ncoil() == 0)
1189 els->
user_data = std::make_unique<FwdBemSolution>();
1198 for (k = 0; k < els->
ncoil(); k++) {
1199 el = els->
coils[k].get();
1200 scalp =
surfs[0].get();
1204 for (p = 0; p < el->
np; p++) {
1205 r[0] = el->
rmag(p, 0); r[1] = el->
rmag(p, 1); r[2] = el->
rmag(p, 2);
1210 qWarning(
"One of the electrodes could not be projected onto the scalp surface. How come?");
1218 for (q = 0; q <
nsol; q++)
1225 tri = &scalp->
tris[best];
1226 scalp->
triangle_coords(Eigen::Map<const Eigen::Vector3f>(r),best,x,y,z);
1228 w[0] = el->
w[p]*(1.0 - x - y);
1231 for (v = 0; v < 3; v++) {
1232 for (q = 0; q <
nsol; q++)
1237 qWarning(
"Unknown BEM approximation method : %d",
bem_method);
1258 Eigen::Vector3f mri_rd = rd;
1259 Eigen::Vector3f mri_Q = Q;
1261 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1264 v0.resize(this->nsol);
1265 float* v0p =
v0.data();
1271 for (pp =
X; pp <=
Z; pp++) {
1272 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1274 ee = Eigen::Vector3f::Unit(pp);
1278 for (s = 0, p = 0; s <
nsurf; s++) {
1280 tri =
surfs[s]->tris.data();
1282 for (k = 0; k <
ntri; k++, tri++)
1288 for (k = 0; k <
nsol; k++)
1292 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1293 for (k = 0; k <
nsol; k++)
1312 Eigen::Vector3f mri_rd = rd;
1313 Eigen::Vector3f mri_Q = Q;
1316 v0.resize(this->nsol);
1317 float* v0p =
v0.data();
1323 for (s = 0, p = 0; s <
nsurf; s++) {
1326 for (k = 0; k <
np; k++)
1332 for (k = 0; k <
nsol; k++)
1336 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1337 for (k = 0; k <
nsol; k++)
1354 Eigen::Vector3f mri_rd = rd;
1355 Eigen::Vector3f mri_Q = Q;
1358 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1361 v0.resize(this->nsol);
1362 float* v0p =
v0.data();
1368 for (pp =
X; pp <=
Z; pp++) {
1369 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1371 ee = Eigen::Vector3f::Unit(pp);
1375 for (s = 0, p = 0; s <
nsurf; s++) {
1378 for (k = 0; k <
np; k++)
1384 for (k = 0; k <
nsol; k++)
1388 nsol = all_surfs ? this->nsol :
surfs[0]->np;
1389 for (k = 0; k <
nsol; k++)
1407 Eigen::Vector3f mri_rd = rd;
1408 Eigen::Vector3f mri_Q = Q;
1411 v0.resize(this->nsol);
1412 float* v0p =
v0.data();
1418 for (s = 0, p = 0; s <
nsurf; s++) {
1420 tri =
surfs[s]->tris.data();
1422 for (k = 0; k <
ntri; k++, tri++)
1428 for (k = 0; k <
nsol; k++)
1432 nsol = all_surfs ? this->nsol :
surfs[0]->ntri;
1433 for (k = 0; k <
nsol; k++)
1450 qWarning(
"No BEM model specified to fwd_bem_pot_els");
1453 if (m->solution.size() == 0) {
1454 qWarning(
"No solution available for fwd_bem_pot_els");
1458 qWarning(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1462 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1465 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1468 qWarning(
"Unknown BEM method : %d",m->bem_method);
1476int 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)
1485 qCritical(
"No BEM model specified to fwd_bem_pot_els");
1488 if (m->solution.size() == 0) {
1489 qCritical(
"No solution available for fwd_bem_pot_els");
1493 qCritical(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1497 int n = els.
ncoil();
1498 m->fwd_bem_pot_calc(rd,Q,&els,
false,pot);
1499 m->fwd_bem_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1502 int n = els.
ncoil();
1503 m->fwd_bem_lin_pot_calc(rd,Q,&els,
false,pot);
1504 m->fwd_bem_lin_pot_grad_calc(rd,Q,&els,
false,xgrad,ygrad,zgrad);
1507 qCritical(
"Unknown BEM method : %d",m->bem_method);
1515inline double arsinh(
double x) {
return std::asinh(x); }
1517void FwdBemModel::calc_f(
const Eigen::Vector3d& xx,
const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1519 double det = -xx[1]*yy[0] + xx[2]*yy[0] +
1520 xx[0]*yy[1] - xx[2]*yy[1] - xx[0]*yy[2] + xx[1]*yy[2];
1522 f0[0] = -xx[2]*yy[1] + xx[1]*yy[2];
1523 f0[1] = xx[2]*yy[0] - xx[0]*yy[2];
1524 f0[2] = -xx[1]*yy[0] + xx[0]*yy[1];
1526 fx[0] = yy[1] - yy[2];
1527 fx[1] = -yy[0] + yy[2];
1528 fx[2] = yy[0] - yy[1];
1530 fy[0] = -xx[1] + xx[2];
1531 fy[1] = xx[0] - xx[2];
1532 fy[2] = -xx[0] + xx[1];
1543 double B2 = 1.0 + B*B;
1544 double ABu = A + B*u;
1545 D = sqrt(u*u + z*z + ABu*ABu);
1546 beta[0] = ABu/sqrt(u*u + z*z);
1547 beta[1] = (A*B + B2*u)/sqrt(A*A + B2*z*z);
1548 beta[2] = (B*z*z - A*u)/(z*D);
1553void 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)
1557 Eigen::Vector3d beta;
1558 double I1,Tx,Ty,Txx,Tyy,Sxx,mult;
1559 double S1x,S1y,S2x,S2y;
1568 Eigen::Vector3d y1 = (to.
r1 - from).cast<double>();
1569 Eigen::Vector3d y2 = (to.
r2 - from).cast<double>();
1570 Eigen::Vector3d y3 = (to.
r3 - from).cast<double>();
1574 Eigen::Vector3d ex_d = to.
ex.cast<
double>();
1575 Eigen::Vector3d ey_d = to.
ey.cast<
double>();
1576 xx[0] = y1.dot(ex_d);
1577 xx[1] = y2.dot(ex_d);
1578 xx[2] = y3.dot(ex_d);
1581 yy[0] = y1.dot(ey_d);
1582 yy[1] = y2.dot(ey_d);
1583 yy[2] = y3.dot(ey_d);
1586 calc_f(Eigen::Map<const Eigen::Vector3d>(xx), Eigen::Map<const Eigen::Vector3d>(yy), f0, fx, fy);
1590 z = y1.dot(to.
nn.cast<
double>());
1605 for (k = 0; k < 2; k++) {
1606 dx = xx[k+1] - xx[k];
1607 A = (yy[k]*xx[k+1] - yy[k+1]*xx[k])/dx;
1608 B = (yy[k+1]-yy[k])/dx;
1614 I1 = I1 - xx[k+1]*
arsinh(beta[0]) - (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1616 Txx =
arsinh(beta[1])/sqrt(B2);
1619 Sxx = (D1 - A*B*Txx)/B2;
1622 Sxx = (B*D1 + A*Txx)/B2;
1628 I1 = I1 + xx[k]*
arsinh(beta[0]) + (A/sqrt(1.0+B*B))*
arsinh(beta[1])
1630 Txx =
arsinh(beta[1])/sqrt(B2);
1633 Sxx = (D1 - A*B*Txx)/B2;
1636 Sxx = (B*D1 + A*Txx)/B2;
1642 mult = 1.0/sqrt(xx[k]*xx[k]+z*z);
1646 Tyy =
arsinh(mult*yy[k+1]);
1648 S1y = S1y + xx[k]*Tyy;
1652 Tyy =
arsinh(mult*yy[k]);
1654 S1y = S1y - xx[k]*Tyy;
1675 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1676 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1677 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1679 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1680 for (j = 0; j < 3; j++)
1682 bbeta[0] = beta[2] - beta[0];
1683 bbeta[1] = beta[0] - beta[1];
1684 bbeta[2] = beta[1] - beta[2];
1686 Eigen::Vector3d coeff = Eigen::Vector3d::Zero();
1687 for (j = 0; j < 3; j++)
1688 coeff += bbeta[j] * (*yy[j]);
1689 return coeff.dot(normal.cast<
double>());
1709 qWarning(
"Solution matrix missing in fwd_bem_field_coeff");
1710 return Eigen::MatrixXf();
1713 qWarning(
"BEM method should be constant collocation for fwd_bem_field_coeff");
1714 return Eigen::MatrixXf();
1719 qWarning(
"head -> mri coordinate transform missing in fwd_bem_field_coeff");
1720 return Eigen::MatrixXf();
1727 return Eigen::MatrixXf();
1728 coils = tcoils.get();
1732 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1733 return Eigen::MatrixXf();
1737 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
ntri);
1739 for (s = 0, off = 0; s <
nsurf; s++) {
1740 surf =
surfs[s].get();
1742 tri = surf->
tris.data();
1745 for (k = 0; k <
ntri; k++,tri++) {
1746 for (j = 0; j < coils->
ncoil(); j++) {
1747 coil = coils->
coils[j].get();
1749 for (p = 0; p < coil->
np; p++)
1751 coeff(j,k+off) = mult*res;
1763 Eigen::Vector3d rkk1 = rk1 - rk;
1764 double size = rkk1.norm();
1766 return log((rk1.norm() * size + rk1.dot(rkk1)) /
1767 (rk.norm() * size + rk.dot(rkk1))) / size;
1774 double triple,l1,l2,l3,solid,clen;
1775 double common,sum,beta,
gamma;
1778 Eigen::Vector3d rjk[3];
1779 rjk[0] = (tri.
r3 - tri.
r2).cast<double>();
1780 rjk[1] = (tri.
r1 - tri.
r3).cast<double>();
1781 rjk[2] = (tri.
r2 - tri.
r1).cast<double>();
1783 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1784 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1785 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1787 const Eigen::Vector3d* yy[4] = { &y1, &y2, &y3, &y1 };
1789 Eigen::Vector3d nn_d = tri.
nn.cast<
double>();
1790 clen = y1.dot(nn_d);
1791 Eigen::Vector3d c_vec = clen * nn_d;
1792 Eigen::Vector3d A_vec = dest.cast<
double>() + c_vec;
1794 Eigen::Vector3d c1 = tri.
r1.cast<
double>() - A_vec;
1795 Eigen::Vector3d c2 = tri.
r2.cast<
double>() - A_vec;
1796 Eigen::Vector3d c3 = tri.
r3.cast<
double>() - A_vec;
1798 const Eigen::Vector3d* cc[4] = { &c1, &c2, &c3, &c1 };
1802 for (sum = 0.0, k = 0; k < 3; k++) {
1803 Eigen::Vector3d cross = cc[k]->cross(*cc[k+1]);
1804 beta = cross.dot(nn_d);
1806 sum = sum + beta*
gamma;
1811 Eigen::Vector3d cross = y1.cross(y2);
1812 triple = cross.dot(y3);
1817 solid = 2.0*atan2(triple,
1825 Eigen::Vector3d dir_d = dir.cast<
double>();
1826 common = (sum-clen*solid)/(2.0*tri.
area);
1827 for (k = 0; k < 3; k++)
1828 res[k] = -rjk[k].dot(dir_d)*common;
1837 Eigen::Vector2d T, S1, S2;
1838 Eigen::Vector3d f0, fx, fy;
1849 Eigen::Vector3f dir = dir_in.normalized();
1851 x_fac = -dir.dot(tri.
ex);
1852 y_fac = -dir.dot(tri.
ey);
1853 for (k = 0; k < 3; k++) {
1854 res_x = f0[k]*T[0] + fx[k]*S1[0] + fy[k]*S2[0] + fy[k]*I1;
1855 res_y = f0[k]*T[1] + fx[k]*S1[1] + fy[k]*S2[1] - fx[k]*I1;
1856 res[k] = x_fac*res_x + y_fac*res_y;
1865 const Eigen::Vector3f* rr[3] = { &source.
r1, &source.
r2, &source.
r3 };
1867 for (k = 0; k < 3; k++) {
1868 Eigen::Vector3f diff = dest - *rr[k];
1869 float dl = diff.squaredNorm();
1870 Eigen::Vector3f vec_result = diff.cross(source.
nn);
1871 res[k] = source.
area*vec_result.dot(normal)/(3.0*dl*sqrt(dl));
1890 Eigen::Vector3d res, one;
1895 qWarning(
"Solution matrix missing in fwd_bem_lin_field_coeff");
1896 return Eigen::MatrixXf();
1899 qWarning(
"BEM method should be linear collocation for fwd_bem_lin_field_coeff");
1900 return Eigen::MatrixXf();
1905 qWarning(
"head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
1906 return Eigen::MatrixXf();
1913 return Eigen::MatrixXf();
1914 coils = tcoils.get();
1918 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->
coord_frame);
1919 return Eigen::MatrixXf();
1929 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
nsol);
1933 for (s = 0, off = 0; s <
nsurf; s++) {
1934 surf =
surfs[s].get();
1936 tri = surf->
tris.data();
1939 for (k = 0; k <
ntri; k++,tri++) {
1940 for (j = 0; j < coils->
ncoil(); j++) {
1941 coil = coils->
coils[j].get();
1946 for (p = 0; p < coil->
np; p++) {
1947 func(coil->
pos(p),coil->
dir(p),*tri,one);
1948 res += coil->
w[p] * one;
1954 for (pp = 0; pp < 3; pp++)
1955 coeff(j,tri->
vert[pp]+off) = coeff(j,tri->
vert[pp]+off) + mult*res[pp];
1958 off = off + surf->
np;
1973 Eigen::MatrixXf sol;
1977 qWarning(
"Solution not computed in fwd_bem_specify_coils");
1982 if (!coils || coils->
ncoil() == 0)
1989 qWarning(
"Unknown BEM method in fwd_bem_specify_coils : %d",
bem_method);
1992 if (sol.size() == 0)
1994 coils->
user_data = std::make_unique<FwdBemSolution>();
2014 Eigen::Vector3f my_rd = rd;
2015 Eigen::Vector3f my_Q = Q;
2022 float* v0p =
v0.data();
2033 for (s = 0, p = 0; s <
nsurf; s++) {
2036 for (k = 0; k <
np; k++)
2043 for (k = 0; k < coils.
ncoil(); k++) {
2044 coil = coils.
coils[k].get();
2046 for (p = 0; p < coil->
np; p++)
2056 for (k = 0; k < coils.
ncoil(); k++)
2061 for (k = 0; k < coils.
ncoil(); k++)
2077 Eigen::Vector3f my_rd = rd;
2078 Eigen::Vector3f my_Q = Q;
2085 float* v0p =
v0.data();
2096 for (s = 0, p = 0; s <
nsurf; s++) {
2098 tri =
surfs[s]->tris.data();
2100 for (k = 0; k <
ntri; k++, tri++)
2107 for (k = 0; k < coils.
ncoil(); k++) {
2108 coil = coils.
coils[k].get();
2110 for (p = 0; p < coil->
np; p++)
2120 for (k = 0; k < coils.
ncoil(); k++)
2125 for (k = 0; k < coils.
ncoil(); k++)
2142 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2143 Eigen::Vector3f ee, mri_ee;
2144 Eigen::Vector3f mri_rd = rd;
2145 Eigen::Vector3f mri_Q = Q;
2151 float* v0p =
v0.data();
2159 for (pp =
X; pp <=
Z; pp++) {
2160 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2164 ee = Eigen::Vector3f::Unit(pp);
2171 for (s = 0, p = 0; s <
nsurf; s++) {
2173 tri =
surfs[s]->tris.data();
2175 for (k = 0; k <
ntri; k++, tri++) {
2183 for (k = 0; k < coils.
ncoil(); k++) {
2184 coil = coils.
coils[k].get();
2186 for (p = 0; p < coil->
np; p++)
2197 for (k = 0; k < coils.
ncoil(); k++)
2198 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2202 for (k = 0; k < coils.
ncoil(); k++)
2216 Eigen::Vector3f diff = rp - rd;
2217 float diff2 = diff.squaredNorm();
2218 float diff3 = std::sqrt(diff2) * diff2;
2219 float diff5 = diff3 * diff2;
2220 Eigen::Vector3f cr = Q.cross(diff);
2221 Eigen::Vector3f crn = dir.cross(Q);
2223 return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3;
2234 Eigen::Vector3f diff = rp - rd;
2235 float diff2 = diff.squaredNorm();
2236 float diff3 = std::sqrt(diff2) * diff2;
2237 float diff5 = diff3 * diff2;
2239 float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3;
2240 return res / (4.0 *
M_PI);
2255 Eigen::Vector3f ee, mri_ee;
2256 Eigen::Vector3f mri_rd = rd;
2257 Eigen::Vector3f mri_Q = Q;
2258 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2264 float* v0p =
v0.data();
2272 for (pp =
X; pp <=
Z; pp++) {
2273 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2277 ee = Eigen::Vector3f::Unit(pp);
2284 for (s = 0, p = 0; s <
nsurf; s++) {
2288 for (k = 0; k <
np; k++)
2295 for (k = 0; k < coils.
ncoil(); k++) {
2296 coil = coils.
coils[k].get();
2298 for (p = 0; p < coil->
np; p++)
2309 for (k = 0; k < coils.
ncoil(); k++)
2310 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2314 for (k = 0; k < coils.
ncoil(); k++)
2333 qWarning(
"No BEM model specified to fwd_bem_field");
2337 qWarning(
"No appropriate coil-specific data available in fwd_bem_field");
2341 m->fwd_bem_field_calc(rd,Q,coils,B);
2344 m->fwd_bem_lin_field_calc(rd,Q,coils,B);
2347 qWarning(
"Unknown BEM method : %d",m->bem_method);
2356 const Eigen::Vector3f& Q,
2358 Eigen::Ref<Eigen::VectorXf> Bval,
2359 Eigen::Ref<Eigen::VectorXf> xgrad,
2360 Eigen::Ref<Eigen::VectorXf> ygrad,
2361 Eigen::Ref<Eigen::VectorXf> zgrad,
2368 qCritical(
"No BEM model specified to fwd_bem_field");
2373 qCritical(
"No appropriate coil-specific data available in fwd_bem_field");
2378 int n = coils.
ncoil();
2379 m->fwd_bem_field_calc(rd,Q,coils,Bval);
2381 m->fwd_bem_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2383 int n = coils.
ncoil();
2384 m->fwd_bem_lin_field_calc(rd,Q,coils,Bval);
2386 m->fwd_bem_lin_field_grad_calc(rd,Q,coils,xgrad,ygrad,zgrad);
2388 qCritical(
"Unknown BEM method : %d",m->bem_method);
2407 Eigen::MatrixXf tmp_vec_res(3, ncoil);
2409 auto fail = [&]() { a->
stat =
FAIL; };
2415 for (j = 0; j < s->
np; j++) {
2432 for (j = 0; j < s->
np; j++)
2447 for (j = 0; j < s->
np; j++) {
2469 else if (a->
comp == 0) {
2479 else if (a->
comp == 1) {
2489 else if (a->
comp == 2) {
2503 for (j = 0; j < s->
np; j++) {
2509 a->
res->col(p++) = tmp_vec_res.row(0).transpose();
2510 a->
res->col(p++) = tmp_vec_res.row(1).transpose();
2511 a->
res->col(p++) = tmp_vec_res.row(2).transpose();
2525 else if (a->
comp == 0) {
2531 else if (a->
comp == 1) {
2538 else if (a->
comp == 2) {
2570 Eigen::MatrixXf res_mat;
2571 Eigen::MatrixXf res_grad_mat;
2573 MatrixXd matResGrad;
2580 int nmeg = coils->
ncoil();
2582 int nspace =
static_cast<int>(spaces.size());
2587 int nproc = QThread::idealThreadCount();
2588 QStringList emptyList;
2590 auto cleanup_fail = [&]() { one_arg.reset();
delete comp;
return FAIL; };
2598 qInfo(
"Using differences.");
2615 return cleanup_fail();
2619 qInfo(
"Composing the field computation matrix...");
2621 return cleanup_fail();
2625 qInfo(
"Composing the field computation matrix (compensation coils)...");
2627 return cleanup_fail();
2631 vec_field =
nullptr;
2641 qInfo(
"Using differences.");
2645 my_sphere_field_grad,
2646 const_cast<Vector3f*
>(&r0));
2652 const_cast<Vector3f*
>(&r0));
2655 return cleanup_fail();
2664 for (k = 0, nsource = 0; k < nspace; k++)
2665 nsource += spaces[k]->nuse;
2670 int ncols = fixed_ori ? nsource : 3*nsource;
2671 res_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2674 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2675 res_grad_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2680 one_arg = std::make_unique<FwdThreadArg>();
2681 one_arg->res = &res_mat;
2682 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2684 one_arg->coils_els = coils;
2685 one_arg->client = client;
2686 one_arg->s =
nullptr;
2687 one_arg->fixed_ori = fixed_ori;
2688 one_arg->field_pot = field;
2689 one_arg->vec_field_pot = vec_field;
2690 one_arg->field_pot_grad = field_grad;
2693 use_threads =
false;
2696 int nthread = (fixed_ori || vec_field || nproc < 6) ? nspace : 3*nspace;
2697 std::vector<FwdThreadArg::UPtr> args;
2702 if (fixed_ori || vec_field || nproc < 6) {
2703 for (k = 0, off = 0; k < nthread; k++) {
2705 t_arg->s = spaces[k].get();
2707 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2708 args.push_back(std::move(t_arg));
2710 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",
2714 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2715 for (p = 0; p < 3; p++,q++) {
2717 t_arg->s = spaces[k].get();
2720 args.push_back(std::move(t_arg));
2722 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2724 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",
2725 nproc,nthread,nspace);
2727 qInfo(
"Computing MEG at %d source locations (%s orientations)...",
2728 nsource,fixed_ori ?
"fixed" :
"free");
2738 for (k = 0, stat =
OK; k < nthread; k++)
2739 if (args[k]->stat !=
OK) {
2744 return cleanup_fail();
2747 qInfo(
"Computing MEG at %d source locations (%s orientations, no threads)...",
2748 nsource,fixed_ori ?
"fixed" :
"free");
2749 for (k = 0, off = 0; k < nspace; k++) {
2750 one_arg->s = spaces[k].get();
2753 if (one_arg->stat !=
OK)
2754 return cleanup_fail();
2755 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2760 QStringList orig_names;
2761 for (k = 0; k < nmeg; k++)
2762 orig_names.append(coils->
coils[k]->chname);
2770 nrow = fixed_ori ? nsource : 3*nsource;
2775 resp.
data = res_mat.transpose().cast<
double>();
2778 if (bDoGrad && res_grad_mat.size() > 0) {
2779 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2780 resp_grad.
nrow = nrow;
2781 resp_grad.
ncol = nmeg;
2784 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
2805 Eigen::MatrixXf res_mat;
2806 Eigen::MatrixXf res_grad_mat;
2808 MatrixXd matResGrad;
2815 int nspace =
static_cast<int>(spaces.size());
2816 int neeg = els->
ncoil();
2821 int nproc = QThread::idealThreadCount();
2822 QStringList emptyList;
2826 for (k = 0, nsource = 0; k < nspace; k++)
2827 nsource += spaces[k]->nuse;
2836 qInfo(
"Using differences.");
2837 pot_grad = my_bem_pot_grad;
2843 if (eeg_model->
nfit == 0) {
2844 qInfo(
"Using the standard series expansion for a multilayer sphere model for EEG");
2850 qInfo(
"Using the equivalent source approach in the homogeneous sphere for EEG");
2861 int ncols = fixed_ori ? nsource : 3*nsource;
2862 res_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2866 qCritical(
"EEG gradient calculation function not available");
2869 int ncols = fixed_ori ? 3*nsource : 3*3*nsource;
2870 res_grad_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2875 one_arg = std::make_unique<FwdThreadArg>();
2876 one_arg->res = &res_mat;
2877 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2879 one_arg->coils_els = els;
2880 one_arg->client = client;
2881 one_arg->s =
nullptr;
2882 one_arg->fixed_ori = fixed_ori;
2883 one_arg->field_pot = pot;
2884 one_arg->vec_field_pot = vec_pot;
2885 one_arg->field_pot_grad = pot_grad;
2888 use_threads =
false;
2891 int nthread = (fixed_ori || vec_pot || nproc < 6) ? nspace : 3*nspace;
2892 std::vector<FwdThreadArg::UPtr> args;
2897 if (fixed_ori || vec_pot || nproc < 6) {
2898 for (k = 0, off = 0; k < nthread; k++) {
2900 t_arg->s = spaces[k].get();
2902 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2903 args.push_back(std::move(t_arg));
2905 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",nproc,nspace);
2908 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2909 for (p = 0; p < 3; p++,q++) {
2911 t_arg->s = spaces[k].get();
2914 args.push_back(std::move(t_arg));
2916 off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse;
2918 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",nproc,nthread,nspace);
2920 qInfo(
"Computing EEG at %d source locations (%s orientations)...",
2921 nsource,fixed_ori ?
"fixed" :
"free");
2931 for (k = 0, stat =
OK; k < nthread; k++)
2932 if (args[k]->stat !=
OK) {
2940 qInfo(
"Computing EEG at %d source locations (%s orientations, no threads)...",
2941 nsource,fixed_ori ?
"fixed" :
"free");
2942 for (k = 0, off = 0; k < nspace; k++) {
2943 one_arg->s = spaces[k].get();
2946 if (one_arg->stat !=
OK)
2948 off = fixed_ori ? off + one_arg->s->nuse : off + 3*one_arg->s->nuse;
2953 QStringList orig_names;
2954 for (k = 0; k < neeg; k++)
2955 orig_names.append(els->
coils[k]->chname);
2961 nrow = fixed_ori ? nsource : 3*nsource;
2966 resp.
data = res_mat.transpose().cast<
double>();
2969 if (bDoGrad && res_grad_mat.size() > 0) {
2970 nrow = fixed_ori ? 3*nsource : 3*3*nsource;
2971 resp_grad.
nrow = nrow;
2972 resp_grad.
ncol = neeg;
2975 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
2999 auto* r0 =
static_cast<float*
>(client);
3003 float F,g0,gr,result,sum;
3011 Eigen::Vector3f myrd = rd - Eigen::Map<const Eigen::Vector3f>(r0);
3015 for (k = 0 ; k < coils.
ncoil() ; k++)
3021 Eigen::Vector3f v = Q.cross(myrd);
3023 for (k = 0; k < coils.
ncoil(); k++) {
3024 this_coil = coils.
coils[k].get();
3029 for (j = 0, sum = 0.0; j <
np; j++) {
3031 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3032 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3034 Eigen::Vector3f pos = this_pos_raw - Eigen::Map<const Eigen::Vector3f>(r0);
3039 Eigen::Vector3f a_vec = pos - myrd;
3043 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3046 r2 = pos.squaredNorm(); r = sqrt(r2);
3048 rr0 = pos.dot(myrd);
3050 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3054 ve = v.dot(this_dir); vr = v.dot(pos);
3055 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3060 gr = a2/r + ar0 + 2.0*(a+r);
3065 sum = sum + this_coil->
w[j]*(ve*F + vr*(g0*r0e - gr*re))/(F*F);
3101 auto* r0 =
static_cast<float*
>(client);
3109 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3114 Eigen::Vector3f myrd = rd - r0_vec;
3119 for (k = 0; k < coils.
ncoil(); k++) {
3120 this_coil = coils.
coils[k].get();
3123 Bval(0,k) = Bval(1,k) = Bval(2,k) = 0.0;
3128 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3130 for (j = 0; j <
np; j++) {
3132 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3133 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3135 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3139 Eigen::Vector3f a_vec = pos - myrd;
3143 a2 = a_vec.squaredNorm(); a = sqrt(a2);
3146 r2 = pos.squaredNorm(); r = sqrt(r2);
3148 rr0 = pos.dot(myrd);
3150 if (std::fabs(ar/(a*r)+1.0) > CEPS) {
3157 gr = a2/r + ar0 + 2.0*(a+r);
3160 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3161 Eigen::Vector3f v1 = myrd.cross(this_dir);
3162 Eigen::Vector3f v2 = myrd.cross(pos);
3164 g = (g0*r0e - gr*re)/(F*F);
3168 sum += this_coil->
w[j]*(v1/F + v2*g);
3173 for (p = 0; p < 3; p++)
3183int 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)
3205 float F,g0,gr,result,G,F2;
3211 auto* r0 =
static_cast<float*
>(client);
3212 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3214 int ncoil = coils.
ncoil();
3218 Eigen::Vector3f myrd = rd - r0_vec;
3222 float r = myrd.norm();
3223 for (k = 0; k < ncoil ; k++) {
3233 Eigen::Vector3f v = Q.cross(myrd);
3235 for (k = 0 ; k < ncoil ; k++) {
3237 this_coil = coils.
coils[k].get();
3243 for (j = 0; j <
np; j++) {
3245 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3249 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3251 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3255 Eigen::Vector3f a_vec = pos - myrd;
3259 float a2 = a_vec.squaredNorm();
float a = sqrt(a2);
3260 float r2 = pos.squaredNorm(); r = sqrt(r2);
3261 float rr0 = pos.dot(myrd);
3262 float ar = (r2 - rr0)/a;
3264 ve = v.dot(this_dir); vr = v.dot(pos);
3265 re = pos.dot(this_dir); r0e = myrd.dot(this_dir);
3269 Eigen::Vector3f eQ = this_dir.cross(Q);
3273 Eigen::Vector3f rQ = pos.cross(Q);
3277 F = a*(r*a + r2 - rr0);
3279 gr = a2/r + ar + 2.0*(a+r);
3280 g0 = a + 2.0*r + ar;
3285 result = (ve*F + vr*G)/F2;
3289 huu = 2.0 + 2.0*a/r;
3290 Eigen::Vector3f ga = -a_vec/a;
3291 Eigen::Vector3f gar = -(ga*ar + pos)/a;
3292 Eigen::Vector3f gg0 = ga + gar;
3293 Eigen::Vector3f ggr = huu*ga + gar;
3294 Eigen::Vector3f gFF = ga/a - (r*a_vec + a*pos)/F;
3295 Eigen::Vector3f gresult = -2.0f*result*gFF + (eQ+gFF*ve)/F +
3296 (rQ*G + vr*(gg0*r0e + g0*this_dir - ggr*re))/F2;
3298 Bval[k] = Bval[k] + this_coil->
w[j]*result;
3299 xgrad[k] = xgrad[k] + this_coil->
w[j]*gresult[0];
3300 ygrad[k] = ygrad[k] + this_coil->
w[j]*gresult[1];
3301 zgrad[k] = zgrad[k] + this_coil->
w[j]*gresult[2];
3322 float sum,dist,dist2,dist5;
3325 for (k = 0; k < coils.
ncoil(); k++) {
3326 this_coil = coils.
coils[k].get();
3332 for (j = 0, sum = 0.0; j <
np; j++) {
3333 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3334 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3338 dist5 = dist2*dist2*dist;
3339 sum = sum + this_coil->
w[j]*(3*M.dot(diff)*diff.dot(dir) - dist2*M.dot(dir))/dist5;
3360 float dist,dist2,dist5;
3363 for (k = 0; k < coils.
ncoil(); k++) {
3364 this_coil = coils.
coils[k].get();
3367 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3371 for (j = 0; j <
np; j++) {
3372 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3373 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3377 dist5 = dist2*dist2*dist;
3378 for (p = 0; p < 3; p++)
3379 sum[p] = sum[p] + this_coil->
w[j]*(3*diff[p]*diff.dot(dir) - dist2*dir[p])/dist5;
3382 for (p = 0; p < 3; p++)
3386 for (p = 0; p < 3; p++)
const QString mne_coord_frame_name_40(int frame)
Per-thread work packet (dipole range, coil set, output column) consumed by the parallel forward-solut...
Software-gradiometer compensation wrapper that subtracts the reference-channel contribution from the ...
Per-sensor projection matrix that turns BEM node potentials into MEG coil readings or EEG electrode v...
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
Multi-shell spherical head model with Berg-Scherg equivalent-source approximation for fast EEG forwar...
Boundary Element Method (BEM) volume-conductor model — layered triangulated surfaces,...
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
Triangle descriptor with cached centroid, area and normal vectors.
#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
Matrix paired with row and column name lists, the on-disk form of FIFFB_PROJ_ITEM / FIFFB_MNE_NAMED_M...
#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
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Forward modelling — BEM solver, spherical models, sensor/coil definitions and the lead-field assembly...
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
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
FiffCoordTrans inverted() const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
FIFF named matrix: dense / sparse Eigen matrix plus row-name and column-name string lists.
void transpose_named_matrix()
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
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
Channel-specific projection that contracts a BEM node-potential vector down to one entry per MEG coil...
Single MEG sensor coil or EEG electrode — stores the coil-local frame and the (r_mag,...
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
Container of FwdCoil instances acting both as the in-memory image of the coil_def....
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
CTF / 4D software-gradiometer wrapper that re-evaluates the primary field callback on a separate refe...
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-shell concentric-sphere head model holding the Berg-Scherg equivalent-source parameters that ac...
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)
Per-thread work packet carrying the dipole-index range, coil set, field/grad callback and write-back ...
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)
Lightweight triangulated surface (vertices, triangles, normals).
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.