41#include <QtConcurrent>
46#ifndef _USE_MATH_DEFINES
47#define _USE_MATH_DEFINES
53static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
54static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
55static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
65constexpr int FAIL = -1;
67constexpr int LOADED = 1;
68constexpr int NOT_FOUND = 0;
71[[maybe_unused]]
constexpr auto BEM_SUFFIX =
"-bem.fif";
72constexpr auto BEM_SOL_SUFFIX =
"-bem-sol.fif";
73constexpr float EPS = 1e-5f;
74constexpr float CEPS = 1e-5f;
103static QString strip_from(
const QString& s,
const QString& suffix)
107 if (s.endsWith(suffix)) {
109 res.chop(suffix.size());
152 for (k = 0; frames[k].
frame != -1; k++) {
153 if (frame == frames[k].frame)
154 return frames[k].
name;
156 return frames[k].
name;
163using namespace Eigen;
210 s1 = strip_from(name,
".fif");
211 s2 = strip_from(s1,
"-sol");
212 s1 = strip_from(s2,
"-bem");
213 s2 = QString(
"%1%2").arg(s1).arg(BEM_SOL_SUFFIX);
223 for (k = 0; surf_expl[k].kind >= 0; k++)
224 if (surf_expl[k].kind == kind)
225 return surf_expl[k].name;
227 return surf_expl[k].name;
237 for (k = 0; method_expl[k].method >= 0; k++)
238 if (method_expl[k].method == method)
239 return method_expl[k].name;
241 return method_expl[k].name;
252 if (node->find_tag(stream, what, t_pTag)) {
254 qWarning(
"Expected an integer tag : %d (found data type %d instead)", what, t_pTag->getType());
257 *res = *t_pTag->toInt();
267 for (
int k = 0; k < this->
nsurf; k++)
268 if (this->
surfs[k]->
id == kind)
269 return this->
surfs[k].get();
281 std::vector<std::shared_ptr<MNESurface>>
surfs;
282 const int nkind =
static_cast<int>(kinds.size());
283 Eigen::VectorXf sigma_tmp(nkind);
287 qCritical(
"No surfaces specified to fwd_bem_load_surfaces");
291 for (k = 0; k < nkind; k++) {
301 qCritical(
"FsSurface %s not specified in MRI coordinates.",
fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
305 surfs.push_back(std::move(s));
307 auto m = std::make_unique<FwdBemModel>();
311 m->surfs = std::move(
surfs);
312 m->sigma = sigma_tmp;
313 m->ntri.resize(nkind);
315 m->gamma.resize(nkind, nkind);
316 m->source_mult.resize(nkind);
317 m->field_mult.resize(nkind);
321 Eigen::VectorXf sigma1(nkind + 1);
323 sigma1.tail(nkind) = m->sigma;
328 for (j = 0; j < m->nsurf; j++) {
329 m->ntri[j] = m->surfs[j]->ntri;
330 m->np[j] = m->surfs[j]->np;
331 m->source_mult[j] = 2.0f / (sigma1[j + 1] + sigma1[j]);
332 m->field_mult[j] = sigma1[j + 1] - sigma1[j];
333 for (k = 0; k < m->nsurf; k++)
334 m->gamma(j, k) = (sigma1[k + 1] - sigma1[k]) / (sigma1[j + 1] + sigma1[j]);
375 if (!stream->open()) {
384 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(
FIFFB_BEM);
386 if (nodes.size() == 0) {
387 qWarning(
"No BEM data in %s", name.toUtf8().constData());
405 qWarning(
"Cannot handle BEM approximation method : %d", method);
410 qWarning(
"Approximation method in file : %d desired : %d", method, bemMethod);
422 QVector<qint32> dims;
423 t_pTag->getMatrixDimensions(ndim, dims);
426 qWarning(
"Expected a two-dimensional solution matrix instead of a %d dimensional one", ndim);
430 for (k = 0, dim = 0; k <
nsurf; k++)
432 if (dims[0] != dim || dims[1] != dim) {
433 qWarning(
"Expected a %d x %d solution matrix instead of a %d x %d one", dim, dim, dims[0], dims[1]);
438 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
439 nSolutions = dims[1];
443 solution = t_pTag->toFloatMatrix().transpose();
444 this->
nsol = nSolutions;
465 qWarning(
"Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
476 std::unique_ptr<MNESourceSpace> res;
481 qInfo(
"Making a spherical guess space with radius %7.1f mm...", 1000 * guessrad);
483 QFile bemFile(QString(QCoreApplication::applicationDirPath() +
"/../resources/general/surf2bem/icos.fif"));
484 if (!QCoreApplication::startingUp())
485 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString(
"/../resources/general/surf2bem/icos.fif"));
486 else if (!bemFile.exists())
487 bemFile.setFileName(
"../resources/general/surf2bem/icos.fif");
489 if (!bemFile.exists()) {
490 qDebug() << bemFile.fileName() <<
"does not exists.";
494 bemname = bemFile.fileName();
500 for (k = 0; k < sphere_owner->np; k++) {
501 dist = sphere_owner->point(k).norm();
502 sphere_owner->rr.row(k) = (guessrad * sphere_owner->rr.row(k) / dist) + guess_r0.transpose();
504 if (sphere_owner->add_geometry_info(
true) ==
FAIL)
506 guess_surf = sphere_owner.get();
508 qInfo(
"Guess surface (%d = %s) is in %s coordinates",
512 qInfo(
"Filtering (grid = %6.f mm)...", 1000 * grid);
523 Eigen::Vector3d rkk1 = rk1 - rk;
524 double size = rkk1.norm();
526 return log((rk.norm() * size + rk.dot(rkk1)) /
527 (rk1.norm() * size + rk1.dot(rkk1))) /
538 Eigen::Vector3d y1, y2, y3;
541 Eigen::Vector3d vec_omega;
544 double beta[3], bbeta[3];
547 static const double solid_eps = 4.0 * M_PI / 1.0E6;
551 y1 = (to.
r1 - from).cast<double>();
552 y2 = (to.
r2 - from).cast<double>();
553 y3 = (to.
r3 - from).cast<double>();
557 const Eigen::Vector3d* y_arr[5] = {&y3, &y1, &y2, &y3, &y1};
558 const Eigen::Vector3d** yy = y_arr + 1;
562 Eigen::Vector3d cross = y1.cross(y2);
563 triple = cross.dot(y3);
568 ss = (l1 * l2 * l3 + y1.dot(y2) * l3 + y1.dot(y3) * l2 + y2.dot(y3) * l1);
569 solid = 2.0 * atan2(triple, ss);
570 if (std::fabs(solid) < solid_eps) {
576 for (j = 0; j < 3; j++)
578 bbeta[0] = beta[2] - beta[0];
579 bbeta[1] = beta[0] - beta[1];
580 bbeta[2] = beta[1] - beta[2];
583 for (j = 0; j < 3; j++)
584 vec_omega += bbeta[j] * (*yy[j]);
588 area2 = 2.0 * to.
area;
589 n2 = 1.0 / (area2 * area2);
590 Eigen::Vector3d nn_d = to.
nn.cast<
double>();
591 for (k = 0; k < 3; k++) {
592 Eigen::Vector3d z = yy[k + 1]->cross(*yy[k - 1]);
593 Eigen::Vector3d diff = *yy[k - 1] - *yy[k + 1];
594 omega[k] = n2 * (-area2 * z.dot(nn_d) * solid + triple * diff.dot(vec_omega));
603 double rel1 = (solid + omega[0] + omega[1] + omega[2]) / solid;
607 Eigen::Vector3d check = Eigen::Vector3d::Zero();
608 Eigen::Vector3d nn_check = to.
nn.cast<
double>();
609 for (k = 0; k < 3; k++) {
610 Eigen::Vector3d z = nn_check.cross(*yy[k]);
611 check += omega[k] * z;
613 check *= -area2 / triple;
614 qInfo(
"(%g,%g,%g) =? (%g,%g,%g)",
615 check[0], check[1], check[2],
616 vec_omega[0], vec_omega[1], vec_omega[2]);
618 double rel2 = sqrt(check.dot(check) / vec_omega.dot(vec_omega));
619 qInfo(
"err1 = %g, err2 = %g", 100 * rel1, 100 * rel2);
636 float pi2 =
static_cast<float>(2.0 * M_PI);
640 for (j = 0; j < nnode; j++) {
642 for (k = 0; k < nnode; k++)
643 sum = sum + mat(j, k);
644 qInfo(
"row %d sum = %g missing = %g", j + 1, sum / pi2,
646 mat(j, j) = pi2 - sum;
649 for (j = 0; j < nnode; j++) {
654 for (k = 0; k < nnode; k++)
655 sum = sum + mat(j, k);
661 mat(j, j) = miss / 2.0;
665 miss = miss / (4.0 * nmemb);
666 for (k = 0, tri = surf.
tris.data(); k <
ntri; k++, tri++) {
667 if (tri->
vert[0] == j) {
668 mat(j, tri->
vert[1]) = mat(j, tri->
vert[1]) + miss;
669 mat(j, tri->
vert[2]) = mat(j, tri->
vert[2]) + miss;
670 }
else if (tri->
vert[1] == j) {
671 mat(j, tri->
vert[0]) = mat(j, tri->
vert[0]) + miss;
672 mat(j, tri->
vert[2]) = mat(j, tri->
vert[2]) + miss;
673 }
else if (tri->
vert[2] == j) {
674 mat(j, tri->
vert[0]) = mat(j, tri->
vert[0]) + miss;
675 mat(j, tri->
vert[1]) = mat(j, tri->
vert[1]) + miss;
697 int np1, np2,
ntri, np_tot, np_max;
699 Eigen::Vector3d omega;
705 for (p = 0, np_tot = np_max = 0; p < static_cast<int>(
surfs.size()); p++) {
706 np_tot +=
surfs[p]->np;
708 np_max =
surfs[p]->np;
711 Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(np_tot, np_tot);
712 Eigen::VectorXd row(np_max);
713 for (p = 0, joff = 0; p < static_cast<int>(
surfs.size()); p++, joff = joff + np1) {
716 for (q = 0, koff = 0; q < static_cast<int>(
surfs.size()); q++, koff = koff + np2) {
721 qInfo(
"\t\t%s (%d) -> %s (%d) ... ",
725 for (j = 0; j < np1; j++) {
726 for (k = 0; k < np2; k++)
728 for (k = 0, tri = surf2->
tris.data(); k <
ntri; k++, tri++) {
733 if (p == q && (tri->
vert[0] == j || tri->
vert[1] == j || tri->
vert[2] == j))
739 for (c = 0; c < 3; c++)
740 row[tri->
vert[c]] = row[tri->
vert[c]] - omega[c];
742 for (k = 0; k < np2; k++)
743 mat(j + joff, k + koff) = row[k];
746 Eigen::MatrixXf sub_mat = mat.block(joff, koff, np1, np1);
748 mat.block(joff, koff, np1, np1) = sub_mat;
763 Eigen::MatrixXf coeff;
770 std::vector<MNESurface*> rawSurfs;
771 rawSurfs.reserve(
nsurf);
772 for (
auto& s :
surfs)
773 rawSurfs.push_back(s.get());
775 qInfo(
"\nComputing the linear collocation solution...");
776 qInfo(
"\tMatrix coefficients...");
778 if (coeff.size() == 0) {
786 qInfo(
"\tInverting the coefficient matrix...");
798 Eigen::MatrixXf ip_solution;
800 qInfo(
"IP approach required...");
802 qInfo(
"\tMatrix coefficients (homog)...");
803 std::vector<MNESurface*> last_surfs = {
surfs.back().get()};
805 if (coeff.size() == 0) {
810 qInfo(
"\tInverting the coefficient matrix (homog)...");
812 if (ip_solution.size() == 0) {
817 qInfo(
"\tModify the original solution to incorporate IP approach...");
822 qInfo(
"Solution ready.");
838 float pi2 =
static_cast<float>(1.0 / (2 * M_PI));
840 int joff, koff, jup, kup, ntot;
842 for (j = 0, ntot = 0; j <
nsurf; j++)
848 for (p = 0, joff = 0; p <
nsurf; p++) {
849 jup =
ntri[p] + joff;
850 for (q = 0, koff = 0; q <
nsurf; q++) {
851 kup =
ntri[q] + koff;
852 mult = (
gamma ==
nullptr) ? pi2 : pi2 * (*gamma)(p, q);
853 for (j = joff; j < jup; j++)
854 for (k = koff; k < kup; k++)
855 solids(j, k) = defl - solids(j, k) * mult;
860 for (k = 0; k < ntot; k++)
861 solids(k, k) = solids(k, k) + 1.0;
863 Eigen::MatrixXf result = solids.inverse();
888 int j, k, joff, koff, nlast;
891 for (s = 0, koff = 0; s <
nsurf - 1; s++)
892 koff = koff +
ntri[s];
895 Eigen::VectorXf row(nlast);
896 mult = (1.0 + ip_mult) / ip_mult;
898 qInfo(
"\t\tCombining...");
900 ip_solution.transposeInPlace();
902 for (s = 0, joff = 0; s <
nsurf; s++) {
903 qInfo(
"%d3 ", s + 1);
908 for (j = 0; j <
ntri[s]; j++) {
909 for (k = 0; k < nlast; k++) {
910 row[k] =
solution.row(j + joff).segment(koff, nlast).dot(ip_solution.row(k).head(nlast));
912 solution.row(j + joff).segment(koff, nlast) -= 2.0f * row.transpose();
914 joff = joff +
ntri[s];
918 ip_solution.transposeInPlace();
924 for (j = 0; j < nlast; j++)
925 for (k = 0; k < nlast; k++)
926 solution(j + koff, k + koff) += mult * ip_solution(j, k);
930 qInfo(
"done.\n\t\tScaling...");
947 Eigen::VectorXf sums(ntri1);
948 for (j = 0; j < ntri1; j++) {
950 for (k = 0; k < ntri2; k++)
951 sum = sum + angles(j, k);
952 sums[j] = sum / (2 * M_PI);
954 for (j = 0; j < ntri1; j++)
961 if (std::fabs(sums[j] - desired) > 1e-4) {
962 qWarning(
"solid angle matrix: rowsum[%d] = 2PI*%g",
980 int ntri1, ntri2, ntri_tot;
986 for (p = 0, ntri_tot = 0; p < static_cast<int>(
surfs.size()); p++)
989 Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot);
990 for (p = 0, joff = 0; p < static_cast<int>(
surfs.size()); p++, joff = joff + ntri1) {
993 for (q = 0, koff = 0; q < static_cast<int>(
surfs.size()); q++, koff = koff + ntri2) {
997 for (j = 0; j < ntri1; j++)
998 for (k = 0, tri = surf2->
tris.data(); k < ntri2; k++, tri++) {
999 if (p == q && j == k)
1003 solids(j + joff, k + koff) = result;
1012 Eigen::MatrixXf sub_block = solids.block(joff, koff, ntri1, ntri2);
1014 return Eigen::MatrixXf();
1028 Eigen::MatrixXf solids;
1035 std::vector<MNESurface*> rawSurfs;
1036 rawSurfs.reserve(
nsurf);
1037 for (
auto& s :
surfs)
1038 rawSurfs.push_back(s.get());
1040 qInfo(
"\nComputing the constant collocation solution...");
1041 qInfo(
"\tSolid angles...");
1043 if (solids.size() == 0) {
1051 qInfo(
"\tInverting the coefficient matrix...");
1062 Eigen::MatrixXf ip_solution;
1064 qInfo(
"IP approach required...");
1066 qInfo(
"\tSolid angles (homog)...");
1067 std::vector<MNESurface*> last_surfs = {
surfs.back().get()};
1069 if (solids.size() == 0) {
1074 qInfo(
"\tInverting the coefficient matrix (homog)...");
1076 if (ip_solution.size() == 0) {
1081 qInfo(
"\tModify the original solution to incorporate IP approach...");
1085 qInfo(
"Solution ready.");
1106 qWarning(
"Unknown BEM method: %d", bemMethod);
1119 if (!force_recompute) {
1122 if (solres == LOADED) {
1125 }
else if (solres ==
FAIL)
1129 qWarning(
"Desired BEM solution not available in %s (%s)", name, err_get_error());
1142 qWarning(
"[FwdBemModel::fwd_bem_save_model] No model to save");
1146 qWarning(
"[FwdBemModel::fwd_bem_save_model] Unknown BEM method : %d",
bem_method);
1155 int coordFrame =
surfs[0]->coord_frame;
1157 for (
int k = 0; k <
nsurf; ++k) {
1160 surfs[k]->writeToStream(stream.data());
1181 Eigen::Vector3f diff = rp - rd;
1182 float diff2 = diff.squaredNorm();
1183 Eigen::Vector3f cr = Q.cross(diff);
1185 return cr.dot(dir) / (diff2 * std::sqrt(diff2));
1195 Eigen::Vector3f diff = rp - rd;
1196 float diff2 = diff.squaredNorm();
1197 return Q.dot(diff) / (4.0 * M_PI * diff2 * std::sqrt(diff2));
1210 float r[3], w[3], dist;
1217 qWarning(
"Solution not computed in fwd_bem_specify_els");
1220 if (!els || els->
ncoil() == 0)
1226 els->
user_data = std::make_unique<FwdBemSolution>();
1235 for (k = 0; k < els->
ncoil(); k++) {
1236 el = els->
coils[k].get();
1237 scalp =
surfs[0].get();
1241 for (p = 0; p < el->
np; p++) {
1242 r[0] = el->
rmag(p, 0);
1243 r[1] = el->
rmag(p, 1);
1244 r[2] = el->
rmag(p, 2);
1247 best = scalp->
project_to_surface(
nullptr, Eigen::Map<const Eigen::Vector3f>(r), dist);
1249 qWarning(
"One of the electrodes could not be projected onto the scalp surface. How come?");
1257 for (q = 0; q <
nsol; q++)
1263 tri = &scalp->
tris[best];
1264 scalp->
triangle_coords(Eigen::Map<const Eigen::Vector3f>(r), best, x, y, z);
1266 w[0] = el->
w[p] * (1.0 - x - y);
1267 w[1] = el->
w[p] * x;
1268 w[2] = el->
w[p] * y;
1269 for (v = 0; v < 3; v++) {
1270 for (q = 0; q <
nsol; q++)
1274 qWarning(
"Unknown BEM approximation method : %d",
bem_method);
1292 int s, k, p, nSolutions, pp;
1295 Eigen::Vector3f mri_rd = rd;
1296 Eigen::Vector3f mri_Q = Q;
1298 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1302 float* v0p =
v0.data();
1308 for (pp =
X; pp <=
Z; pp++) {
1309 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1311 ee = Eigen::Vector3f::Unit(pp);
1315 for (s = 0, p = 0; s <
nsurf; s++) {
1316 nTriangles =
surfs[s]->ntri;
1317 tri =
surfs[s]->tris.data();
1319 for (k = 0; k < nTriangles; k++, tri++)
1324 nSolutions = sol->
ncoil;
1325 for (k = 0; k < nSolutions; k++)
1328 nSolutions = all_surfs ? this->
nsol :
surfs[0]->ntri;
1329 for (k = 0; k < nSolutions; k++)
1345 int s, k, p, nSolutions;
1347 Eigen::Vector3f mri_rd = rd;
1348 Eigen::Vector3f mri_Q = Q;
1352 float* v0p =
v0.data();
1358 for (s = 0, p = 0; s <
nsurf; s++) {
1359 nPoints =
surfs[s]->np;
1361 for (k = 0; k < nPoints; k++)
1366 nSolutions = sol->
ncoil;
1367 for (k = 0; k < nSolutions; k++)
1370 nSolutions = all_surfs ? this->
nsol :
surfs[0]->np;
1371 for (k = 0; k < nSolutions; k++)
1386 int s, k, p, nSolutions, pp;
1388 Eigen::Vector3f mri_rd = rd;
1389 Eigen::Vector3f mri_Q = Q;
1392 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1396 float* v0p =
v0.data();
1402 for (pp =
X; pp <=
Z; pp++) {
1403 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1405 ee = Eigen::Vector3f::Unit(pp);
1409 for (s = 0, p = 0; s <
nsurf; s++) {
1410 nPoints =
surfs[s]->np;
1412 for (k = 0; k < nPoints; k++)
1417 nSolutions = sol->
ncoil;
1418 for (k = 0; k < nSolutions; k++)
1421 nSolutions = all_surfs ? this->
nsol :
surfs[0]->np;
1422 for (k = 0; k < nSolutions; k++)
1438 int s, k, p, nSolutions;
1440 Eigen::Vector3f mri_rd = rd;
1441 Eigen::Vector3f mri_Q = Q;
1445 float* v0p =
v0.data();
1451 for (s = 0, p = 0; s <
nsurf; s++) {
1452 nTriangles =
surfs[s]->ntri;
1453 tri =
surfs[s]->tris.data();
1455 for (k = 0; k < nTriangles; k++, tri++)
1460 nSolutions = sol->
ncoil;
1461 for (k = 0; k < nSolutions; k++)
1464 nSolutions = all_surfs ? this->
nsol :
surfs[0]->ntri;
1465 for (k = 0; k < nSolutions; k++)
1482 qWarning(
"No BEM model specified to fwd_bem_pot_els");
1485 if (m->solution.size() == 0) {
1486 qWarning(
"No solution available for fwd_bem_pot_els");
1490 qWarning(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1494 m->fwd_bem_pot_calc(rd, Q, &els,
false, pot);
1496 m->fwd_bem_lin_pot_calc(rd, Q, &els,
false, pot);
1498 qWarning(
"Unknown BEM method : %d", m->bem_method);
1506int 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)
1515 qCritical(
"No BEM model specified to fwd_bem_pot_els");
1518 if (m->solution.size() == 0) {
1519 qCritical(
"No solution available for fwd_bem_pot_els");
1523 qCritical(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1527 m->fwd_bem_pot_calc(rd, Q, &els,
false, pot);
1528 m->fwd_bem_pot_grad_calc(rd, Q, &els,
false, xgrad, ygrad, zgrad);
1530 m->fwd_bem_lin_pot_calc(rd, Q, &els,
false, pot);
1531 m->fwd_bem_lin_pot_grad_calc(rd, Q, &els,
false, xgrad, ygrad, zgrad);
1533 qCritical(
"Unknown BEM method : %d", m->bem_method);
1543 return std::asinh(x);
1546void FwdBemModel::calc_f(
const Eigen::Vector3d& xx,
const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1548 double det = -xx[1] * yy[0] + xx[2] * yy[0] +
1549 xx[0] * yy[1] - xx[2] * yy[1] - xx[0] * yy[2] + xx[1] * yy[2];
1551 f0[0] = -xx[2] * yy[1] + xx[1] * yy[2];
1552 f0[1] = xx[2] * yy[0] - xx[0] * yy[2];
1553 f0[2] = -xx[1] * yy[0] + xx[0] * yy[1];
1555 fx[0] = yy[1] - yy[2];
1556 fx[1] = -yy[0] + yy[2];
1557 fx[2] = yy[0] - yy[1];
1559 fy[0] = -xx[1] + xx[2];
1560 fy[1] = xx[0] - xx[2];
1561 fy[2] = -xx[0] + xx[1];
1572 double B2 = 1.0 + B * B;
1573 double ABu = A + B * u;
1574 D = sqrt(u * u + z * z + ABu * ABu);
1575 beta[0] = ABu / sqrt(u * u + z * z);
1576 beta[1] = (A * B + B2 * u) / sqrt(A * A + B2 * z * z);
1577 beta[2] = (B * z * z - A * u) / (z * D);
1582void 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)
1584 double xx[4], yy[4];
1586 Eigen::Vector3d beta;
1587 double I1, Tx, Ty, Txx, Tyy, Sxx, mult;
1588 double S1x, S1y, S2x;
1597 Eigen::Vector3d y1 = (to.
r1 - from).cast<double>();
1598 Eigen::Vector3d y2 = (to.
r2 - from).cast<double>();
1599 Eigen::Vector3d y3 = (to.
r3 - from).cast<double>();
1603 Eigen::Vector3d ex_d = to.
ex.cast<
double>();
1604 Eigen::Vector3d ey_d = to.
ey.cast<
double>();
1605 xx[0] = y1.dot(ex_d);
1606 xx[1] = y2.dot(ex_d);
1607 xx[2] = y3.dot(ex_d);
1610 yy[0] = y1.dot(ey_d);
1611 yy[1] = y2.dot(ey_d);
1612 yy[2] = y3.dot(ey_d);
1615 calc_f(Eigen::Map<const Eigen::Vector3d>(xx), Eigen::Map<const Eigen::Vector3d>(yy), f0, fx, fy);
1619 z = y1.dot(to.
nn.cast<
double>());
1633 for (k = 0; k < 2; k++) {
1634 dx = xx[k + 1] - xx[k];
1635 A = (yy[k] * xx[k + 1] - yy[k + 1] * xx[k]) / dx;
1636 B = (yy[k + 1] - yy[k]) / dx;
1642 I1 = I1 - xx[k + 1] *
arsinh(beta[0]) - (A / sqrt(1.0 + B * B)) *
arsinh(beta[1]) - z * atan(beta[2]);
1643 Txx =
arsinh(beta[1]) / sqrt(B2);
1646 Sxx = (D1 - A * B * Txx) / B2;
1648 S1y = S1y + B * Sxx;
1649 Sxx = (B * D1 + A * Txx) / B2;
1655 I1 = I1 + xx[k] *
arsinh(beta[0]) + (A / sqrt(1.0 + B * B)) *
arsinh(beta[1]) + z * atan(beta[2]);
1656 Txx =
arsinh(beta[1]) / sqrt(B2);
1659 Sxx = (D1 - A * B * Txx) / B2;
1661 S1y = S1y - B * Sxx;
1662 Sxx = (B * D1 + A * Txx) / B2;
1668 mult = 1.0 / sqrt(xx[k] * xx[k] + z * z);
1672 Tyy =
arsinh(mult * yy[k + 1]);
1674 S1y = S1y + xx[k] * Tyy;
1678 Tyy =
arsinh(mult * yy[k]);
1680 S1y = S1y - xx[k] * Tyy;
1701 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1702 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1703 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1705 const Eigen::Vector3d* yy[4] = {&y1, &y2, &y3, &y1};
1706 for (j = 0; j < 3; j++)
1707 beta[j] =
calc_beta(*yy[j], *yy[j + 1]);
1708 bbeta[0] = beta[2] - beta[0];
1709 bbeta[1] = beta[0] - beta[1];
1710 bbeta[2] = beta[1] - beta[2];
1712 Eigen::Vector3d coeff = Eigen::Vector3d::Zero();
1713 for (j = 0; j < 3; j++)
1714 coeff += bbeta[j] * (*yy[j]);
1715 return coeff.dot(normal.cast<
double>());
1730 int j, k, p, s, off;
1735 qWarning(
"Solution matrix missing in fwd_bem_field_coeff");
1736 return Eigen::MatrixXf();
1739 qWarning(
"BEM method should be constant collocation for fwd_bem_field_coeff");
1740 return Eigen::MatrixXf();
1745 qWarning(
"head -> mri coordinate transform missing in fwd_bem_field_coeff");
1746 return Eigen::MatrixXf();
1752 return Eigen::MatrixXf();
1753 coils = tcoils.get();
1756 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff", coils->
coord_frame);
1757 return Eigen::MatrixXf();
1761 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(), nTriangles);
1763 for (s = 0, off = 0; s <
nsurf; s++) {
1764 surf =
surfs[s].get();
1765 nTriangles = surf->
ntri;
1766 tri = surf->
tris.data();
1769 for (k = 0; k < nTriangles; k++, tri++) {
1770 for (j = 0; j < coils->
ncoil(); j++) {
1771 coil = coils->
coils[j].get();
1773 for (p = 0; p < coil->
np; p++)
1775 coeff(j, k + off) = mult * res;
1778 off = off + nTriangles;
1787 Eigen::Vector3d rkk1 = rk1 - rk;
1788 double size = rkk1.norm();
1790 return log((rk1.norm() * size + rk1.dot(rkk1)) /
1791 (rk.norm() * size + rk.dot(rkk1))) /
1799 double triple, l1, l2, l3, solid, clen;
1800 double common, sum, beta,
gamma;
1803 Eigen::Vector3d rjk[3];
1804 rjk[0] = (tri.
r3 - tri.
r2).cast<double>();
1805 rjk[1] = (tri.
r1 - tri.
r3).cast<double>();
1806 rjk[2] = (tri.
r2 - tri.
r1).cast<double>();
1808 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1809 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1810 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1812 const Eigen::Vector3d* yy[4] = {&y1, &y2, &y3, &y1};
1814 Eigen::Vector3d nn_d = tri.
nn.cast<
double>();
1815 clen = y1.dot(nn_d);
1816 Eigen::Vector3d c_vec = clen * nn_d;
1817 Eigen::Vector3d A_vec = dest.cast<
double>() + c_vec;
1819 Eigen::Vector3d c1 = tri.
r1.cast<
double>() - A_vec;
1820 Eigen::Vector3d c2 = tri.
r2.cast<
double>() - A_vec;
1821 Eigen::Vector3d c3 = tri.
r3.cast<
double>() - A_vec;
1823 const Eigen::Vector3d* cc[4] = {&c1, &c2, &c3, &c1};
1827 for (sum = 0.0, k = 0; k < 3; k++) {
1828 Eigen::Vector3d cross = cc[k]->cross(*cc[k + 1]);
1829 beta = cross.dot(nn_d);
1831 sum = sum + beta *
gamma;
1836 Eigen::Vector3d cross = y1.cross(y2);
1837 triple = cross.dot(y3);
1842 solid = 2.0 * atan2(triple, (l1 * l2 * l3 + y1.dot(y2) * l3 + y1.dot(y3) * l2 + y2.dot(y3) * l1));
1846 Eigen::Vector3d dir_d = dir.cast<
double>();
1847 common = (sum - clen * solid) / (2.0 * tri.
area);
1848 for (k = 0; k < 3; k++)
1849 res[k] = -rjk[k].dot(dir_d) * common;
1858 Eigen::Vector2d T, S1, S2;
1859 Eigen::Vector3d f0, fx, fy;
1860 double res_x, res_y;
1861 double x_fac, y_fac;
1870 Eigen::Vector3f dir = dir_in.normalized();
1872 x_fac = -dir.dot(tri.
ex);
1873 y_fac = -dir.dot(tri.
ey);
1874 for (k = 0; k < 3; k++) {
1875 res_x = f0[k] * T[0] + fx[k] * S1[0] + fy[k] * S2[0] + fy[k] * I1;
1876 res_y = f0[k] * T[1] + fx[k] * S1[1] + fy[k] * S2[1] - fx[k] * I1;
1877 res[k] = x_fac * res_x + y_fac * res_y;
1886 const Eigen::Vector3f* rr[3] = {&source.
r1, &source.
r2, &source.
r3};
1888 for (k = 0; k < 3; k++) {
1889 Eigen::Vector3f diff = dest - *rr[k];
1890 float dl = diff.squaredNorm();
1891 Eigen::Vector3f vec_result = diff.cross(source.
nn);
1892 res[k] = source.
area * vec_result.dot(normal) / (3.0 * dl * sqrt(dl));
1910 int j, k, p, pp, off, s;
1911 Eigen::Vector3d res, one;
1916 qWarning(
"Solution matrix missing in fwd_bem_lin_field_coeff");
1917 return Eigen::MatrixXf();
1920 qWarning(
"BEM method should be linear collocation for fwd_bem_lin_field_coeff");
1921 return Eigen::MatrixXf();
1926 qWarning(
"head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
1927 return Eigen::MatrixXf();
1933 return Eigen::MatrixXf();
1934 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();
1954 nTriangles = surf->
ntri;
1955 tri = surf->
tris.data();
1958 for (k = 0; k < nTriangles; 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>();
2030 int s, k, p, nPoints;
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++) {
2053 nPoints =
surfs[s]->np;
2055 for (k = 0; k < nPoints; k++)
2062 for (k = 0; k < coils.
ncoil(); k++) {
2063 coil = coils.
coils[k].get();
2065 for (p = 0; p < coil->
np; p++)
2071 for (k = 0; k < coils.
ncoil(); k++)
2076 for (k = 0; k < coils.
ncoil(); k++)
2088 int s, k, p, nTriangles;
2092 Eigen::Vector3f my_rd = rd;
2093 Eigen::Vector3f my_Q = Q;
2100 float* v0p =
v0.data();
2111 for (s = 0, p = 0; s <
nsurf; s++) {
2112 nTriangles =
surfs[s]->ntri;
2113 tri =
surfs[s]->tris.data();
2115 for (k = 0; k < nTriangles; k++, tri++)
2122 for (k = 0; k < coils.
ncoil(); k++) {
2123 coil = coils.
coils[k].get();
2125 for (p = 0; p < coil->
np; p++)
2131 for (k = 0; k < coils.
ncoil(); k++)
2136 for (k = 0; k < coils.
ncoil(); k++)
2149 int s, k, p, nTriangles, pp;
2153 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2154 Eigen::Vector3f ee, mri_ee;
2155 Eigen::Vector3f mri_rd = rd;
2156 Eigen::Vector3f mri_Q = Q;
2162 float* v0p =
v0.data();
2170 for (pp =
X; pp <=
Z; pp++) {
2171 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2175 ee = Eigen::Vector3f::Unit(pp);
2182 for (s = 0, p = 0; s <
nsurf; s++) {
2183 nTriangles =
surfs[s]->ntri;
2184 tri =
surfs[s]->tris.data();
2186 for (k = 0; k < nTriangles; k++, tri++) {
2194 for (k = 0; k < coils.
ncoil(); k++) {
2195 coil = coils.
coils[k].get();
2197 for (p = 0; p < coil->
np; p++)
2203 for (k = 0; k < coils.
ncoil(); k++)
2204 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2208 for (k = 0; k < coils.
ncoil(); k++)
2222 Eigen::Vector3f diff = rp - rd;
2223 float diff2 = diff.squaredNorm();
2224 float diff3 = std::sqrt(diff2) * diff2;
2225 float diff5 = diff3 * diff2;
2226 Eigen::Vector3f cr = Q.cross(diff);
2227 Eigen::Vector3f crn = dir.cross(Q);
2229 return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3;
2240 Eigen::Vector3f diff = rp - rd;
2241 float diff2 = diff.squaredNorm();
2242 float diff3 = std::sqrt(diff2) * diff2;
2243 float diff5 = diff3 * diff2;
2245 float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3;
2246 return res / (4.0 * M_PI);
2258 int s, k, p, nPoints, pp;
2261 Eigen::Vector3f ee, mri_ee;
2262 Eigen::Vector3f mri_rd = rd;
2263 Eigen::Vector3f mri_Q = Q;
2264 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2270 float* v0p =
v0.data();
2278 for (pp =
X; pp <=
Z; pp++) {
2279 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2283 ee = Eigen::Vector3f::Unit(pp);
2290 for (s = 0, p = 0; s <
nsurf; s++) {
2291 nPoints =
surfs[s]->np;
2294 for (k = 0; k < nPoints; k++)
2301 for (k = 0; k < coils.
ncoil(); k++) {
2302 coil = coils.
coils[k].get();
2304 for (p = 0; p < coil->
np; p++)
2310 for (k = 0; k < coils.
ncoil(); k++)
2311 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2315 for (k = 0; k < coils.
ncoil(); k++)
2334 qWarning(
"No BEM model specified to fwd_bem_field");
2338 qWarning(
"No appropriate coil-specific data available in fwd_bem_field");
2342 m->fwd_bem_field_calc(rd, Q, coils, B);
2344 m->fwd_bem_lin_field_calc(rd, Q, coils, B);
2346 qWarning(
"Unknown BEM method : %d", m->bem_method);
2355 const Eigen::Vector3f& Q,
2357 Eigen::Ref<Eigen::VectorXf> Bval,
2358 Eigen::Ref<Eigen::VectorXf> xgrad,
2359 Eigen::Ref<Eigen::VectorXf> ygrad,
2360 Eigen::Ref<Eigen::VectorXf> zgrad,
2367 qCritical(
"No BEM model specified to fwd_bem_field");
2372 qCritical(
"No appropriate coil-specific data available in fwd_bem_field");
2377 m->fwd_bem_field_calc(rd, Q, coils, Bval);
2379 m->fwd_bem_field_grad_calc(rd, Q, coils, xgrad, ygrad, zgrad);
2381 m->fwd_bem_lin_field_calc(rd, Q, coils, Bval);
2383 m->fwd_bem_lin_field_grad_calc(rd, Q, coils, xgrad, ygrad, zgrad);
2385 qCritical(
"Unknown BEM method : %d", m->bem_method);
2404 Eigen::MatrixXf tmp_vec_res(3, ncoil);
2414 for (j = 0; j < s->
np; j++) {
2432 for (j = 0; j < s->
np; j++)
2447 for (j = 0; j < s->
np; j++) {
2474 }
else if (a->
comp == 0) {
2487 }
else if (a->
comp == 1) {
2500 }
else if (a->
comp == 2) {
2517 for (j = 0; j < s->
np; j++) {
2524 a->
res->col(p++) = tmp_vec_res.row(0).transpose();
2525 a->
res->col(p++) = tmp_vec_res.row(1).transpose();
2526 a->
res->col(p++) = tmp_vec_res.row(2).transpose();
2541 }
else if (a->
comp == 0) {
2548 }
else if (a->
comp == 1) {
2555 }
else if (a->
comp == 2) {
2590 const bool sphere =
nsurf == 0;
2591 Eigen::Vector3f sphere_r0 = r0;
2593 Eigen::MatrixXf res_mat;
2594 Eigen::MatrixXf res_grad_mat;
2596 MatrixXd matResGrad;
2598 std::unique_ptr<FwdCompData> comp;
2603 int nmeg = coils->
ncoil();
2605 int nspace =
static_cast<int>(spaces.size());
2610 int nproc = QThread::idealThreadCount();
2611 QStringList emptyList;
2613 auto cleanup_fail = [&]() {
2632 return cleanup_fail();
2636 qInfo(
"Using differences.");
2653 return cleanup_fail();
2657 qInfo(
"Composing the field computation matrix...");
2659 return cleanup_fail();
2662 if (comp->set && comp->set->current) {
2663 qInfo(
"Composing the field computation matrix (compensation coils)...");
2665 return cleanup_fail();
2668 vec_field =
nullptr;
2672 client = comp.get();
2676 for (k = 0, nsource = 0; k < nspace; k++)
2677 nsource += spaces[k]->nuse;
2682 int ncols = fixed_ori ? nsource : 3 * nsource;
2683 res_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2686 int ncols = fixed_ori ? 3 * nsource : 3 * 3 * nsource;
2687 res_grad_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2692 one_arg = std::make_unique<FwdThreadArg>();
2693 one_arg->res = &res_mat;
2694 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2696 one_arg->coils_els = coils;
2697 one_arg->client = client;
2698 one_arg->s =
nullptr;
2699 one_arg->fixed_ori = fixed_ori;
2700 one_arg->field_pot = field;
2701 one_arg->vec_field_pot = vec_field;
2702 one_arg->field_pot_grad = field_grad;
2705 use_threads =
false;
2708 int nthread = (fixed_ori || vec_field) ? nspace : 3 * nspace;
2709 std::vector<FwdThreadArg::UPtr> args;
2714 if (fixed_ori || vec_field) {
2715 for (k = 0, off = 0; k < nthread; k++) {
2717 t_arg->s = spaces[k].get();
2719 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2720 args.push_back(std::move(t_arg));
2722 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",
2725 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2726 for (p = 0; p < 3; p++, q++) {
2728 t_arg->s = spaces[k].get();
2731 args.push_back(std::move(t_arg));
2733 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2735 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",
2736 nproc, nthread, nspace);
2738 qInfo(
"Computing MEG at %d source locations (%s orientations)...",
2739 nsource, fixed_ori ?
"fixed" :
"free");
2749 for (k = 0, stat =
OK; k < nthread; k++)
2750 if (args[k]->stat !=
OK) {
2755 return cleanup_fail();
2757 qInfo(
"Computing MEG at %d source locations (%s orientations, no threads)...",
2758 nsource, fixed_ori ?
"fixed" :
"free");
2759 for (k = 0, off = 0; k < nspace; k++) {
2760 one_arg->s = spaces[k].get();
2763 if (one_arg->stat !=
OK)
2764 return cleanup_fail();
2765 off = fixed_ori ? off + one_arg->s->nuse : off + 3 * one_arg->s->nuse;
2770 QStringList orig_names;
2771 for (k = 0; k < nmeg; k++)
2772 orig_names.append(coils->
coils[k]->chname);
2779 nrow = fixed_ori ? nsource : 3 * nsource;
2784 resp.
data = res_mat.transpose().cast<
double>();
2787 if (bDoGrad && res_grad_mat.size() > 0) {
2788 nrow = fixed_ori ? 3 * nsource : 3 * 3 * nsource;
2789 resp_grad.
nrow = nrow;
2790 resp_grad.
ncol = nmeg;
2793 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
2814 const bool sphere =
nsurf == 0;
2816 Eigen::MatrixXf res_mat;
2817 Eigen::MatrixXf res_grad_mat;
2819 MatrixXd matResGrad;
2826 int nspace =
static_cast<int>(spaces.size());
2827 int neeg = els->
ncoil();
2832 int nproc = QThread::idealThreadCount();
2833 QStringList emptyList;
2837 for (k = 0, nsource = 0; k < nspace; k++)
2838 nsource += spaces[k]->nuse;
2842 qCritical(
"EEG sphere model not defined.");
2845 if (eeg_model->
nfit == 0) {
2846 qInfo(
"Using the standard series expansion for a multilayer sphere model for EEG");
2851 qInfo(
"Using the equivalent source approach in the homogeneous sphere for EEG");
2864 qInfo(
"Using differences.");
2865 pot_grad = my_bem_pot_grad;
2874 int ncols = fixed_ori ? nsource : 3 * nsource;
2875 res_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2879 qCritical(
"EEG gradient calculation function not available");
2882 int ncols = fixed_ori ? 3 * nsource : 3 * 3 * nsource;
2883 res_grad_mat = Eigen::MatrixXf::Zero(neeg, ncols);
2888 one_arg = std::make_unique<FwdThreadArg>();
2889 one_arg->res = &res_mat;
2890 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2892 one_arg->coils_els = els;
2893 one_arg->client = client;
2894 one_arg->s =
nullptr;
2895 one_arg->fixed_ori = fixed_ori;
2896 one_arg->field_pot = pot;
2897 one_arg->vec_field_pot = vec_pot;
2898 one_arg->field_pot_grad = pot_grad;
2901 use_threads =
false;
2904 int nthread = (fixed_ori || vec_pot) ? nspace : 3 * nspace;
2905 std::vector<FwdThreadArg::UPtr> args;
2910 if (fixed_ori || vec_pot) {
2911 for (k = 0, off = 0; k < nthread; k++) {
2913 t_arg->s = spaces[k].get();
2915 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2916 args.push_back(std::move(t_arg));
2918 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.", nproc, nspace);
2920 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2921 for (p = 0; p < 3; p++, q++) {
2923 t_arg->s = spaces[k].get();
2926 args.push_back(std::move(t_arg));
2928 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2930 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.", nproc, nthread, nspace);
2932 qInfo(
"Computing EEG at %d source locations (%s orientations)...",
2933 nsource, fixed_ori ?
"fixed" :
"free");
2943 for (k = 0, stat =
OK; k < nthread; k++)
2944 if (args[k]->stat !=
OK) {
2951 qInfo(
"Computing EEG at %d source locations (%s orientations, no threads)...",
2952 nsource, fixed_ori ?
"fixed" :
"free");
2953 for (k = 0, off = 0; k < nspace; k++) {
2954 one_arg->s = spaces[k].get();
2957 if (one_arg->stat !=
OK)
2959 off = fixed_ori ? off + one_arg->s->nuse : off + 3 * one_arg->s->nuse;
2964 QStringList orig_names;
2965 for (k = 0; k < neeg; k++)
2966 orig_names.append(els->
coils[k]->chname);
2972 nrow = fixed_ori ? nsource : 3 * nsource;
2977 resp.
data = res_mat.transpose().cast<
double>();
2980 if (bDoGrad && res_grad_mat.size() > 0) {
2981 nrow = fixed_ori ? 3 * nsource : 3 * 3 * nsource;
2982 resp_grad.
nrow = nrow;
2983 resp_grad.
ncol = neeg;
2986 resp_grad.
data = res_grad_mat.transpose().cast<
double>();
3010 auto* r0 =
static_cast<float*
>(client);
3013 float vr, ve, re, r0e;
3014 float F, g0, gr, sum;
3022 Eigen::Vector3f myrd = rd - Eigen::Map<const Eigen::Vector3f>(r0);
3026 for (k = 0; k < coils.
ncoil(); k++)
3032 Eigen::Vector3f v = Q.cross(myrd);
3034 for (k = 0; k < coils.
ncoil(); k++) {
3035 this_coil = coils.
coils[k].get();
3039 for (j = 0, sum = 0.0; j <
np; j++) {
3040 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3041 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3043 Eigen::Vector3f pos = this_pos_raw - Eigen::Map<const Eigen::Vector3f>(r0);
3047 Eigen::Vector3f a_vec = pos - myrd;
3051 a2 = a_vec.squaredNorm();
3055 r2 = pos.squaredNorm();
3058 rr0 = pos.dot(myrd);
3060 if (std::fabs(ar / (a * r) + 1.0) > CEPS) {
3064 ve = v.dot(this_dir);
3066 re = pos.dot(this_dir);
3067 r0e = myrd.dot(this_dir);
3071 F = a * (r * a + ar);
3072 gr = a2 / r + ar0 + 2.0 * (a + r);
3073 g0 = a + 2 * r + ar0;
3077 sum = sum + this_coil->
w[j] * (ve * F + vr * (g0 * r0e - gr * re)) / (F * F);
3113 auto* r0 =
static_cast<float*
>(client);
3121 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3126 Eigen::Vector3f myrd = rd - r0_vec;
3131 for (k = 0; k < coils.
ncoil(); k++) {
3132 this_coil = coils.
coils[k].get();
3135 Bval(0, k) = Bval(1, k) = Bval(2, k) = 0.0;
3139 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3141 for (j = 0; j <
np; j++) {
3142 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3143 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3145 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3149 Eigen::Vector3f a_vec = pos - myrd;
3153 a2 = a_vec.squaredNorm();
3157 r2 = pos.squaredNorm();
3160 rr0 = pos.dot(myrd);
3162 if (std::fabs(ar / (a * r) + 1.0) > CEPS) {
3168 F = a * (r * a + ar);
3169 gr = a2 / r + ar0 + 2.0 * (a + r);
3170 g0 = a + 2 * r + ar0;
3172 re = pos.dot(this_dir);
3173 r0e = myrd.dot(this_dir);
3174 Eigen::Vector3f v1 = myrd.cross(this_dir);
3175 Eigen::Vector3f v2 = myrd.cross(pos);
3177 g = (g0 * r0e - gr * re) / (F * F);
3181 sum += this_coil->
w[j] * (v1 / F + v2 * g);
3186 for (p = 0; p < 3; p++)
3196int 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)
3217 float vr, ve, re, r0e;
3218 float F, g0, gr, result, G, F2;
3224 auto* r0 =
static_cast<float*
>(client);
3225 Eigen::Map<const Eigen::Vector3f> r0_vec(r0);
3227 int ncoil = coils.
ncoil();
3231 Eigen::Vector3f myrd = rd - r0_vec;
3235 float r = myrd.norm();
3236 for (k = 0; k < ncoil; k++) {
3246 Eigen::Vector3f v = Q.cross(myrd);
3248 for (k = 0; k < ncoil; k++) {
3249 this_coil = coils.
coils[k].get();
3254 for (j = 0; j <
np; j++) {
3255 Eigen::Map<const Eigen::Vector3f> this_pos_raw = this_coil->
pos(j);
3259 Eigen::Vector3f pos = this_pos_raw - r0_vec;
3261 Eigen::Map<const Eigen::Vector3f> this_dir = this_coil->
dir(j);
3265 Eigen::Vector3f a_vec = pos - myrd;
3269 float a2 = a_vec.squaredNorm();
3271 float r2 = pos.squaredNorm();
3273 float rr0 = pos.dot(myrd);
3274 float ar = (r2 - rr0) / a;
3276 ve = v.dot(this_dir);
3278 re = pos.dot(this_dir);
3279 r0e = myrd.dot(this_dir);
3283 Eigen::Vector3f eQ = this_dir.cross(Q);
3287 Eigen::Vector3f rQ = pos.cross(Q);
3291 F = a * (r * a + r2 - rr0);
3293 gr = a2 / r + ar + 2.0 * (a + r);
3294 g0 = a + 2.0 * r + ar;
3295 G = g0 * r0e - gr * re;
3299 result = (ve * F + vr * G) / F2;
3303 huu = 2.0 + 2.0 * a / r;
3304 Eigen::Vector3f ga = -a_vec / a;
3305 Eigen::Vector3f gar = -(ga * ar + pos) / a;
3306 Eigen::Vector3f gg0 = ga + gar;
3307 Eigen::Vector3f ggr = huu * ga + gar;
3308 Eigen::Vector3f gFF = ga / a - (r * a_vec + a * pos) / F;
3309 Eigen::Vector3f gresult = -2.0f * result * gFF + (eQ + gFF * ve) / F +
3310 (rQ * G + vr * (gg0 * r0e + g0 * this_dir - ggr * re)) / F2;
3312 Bval[k] = Bval[k] + this_coil->
w[j] * result;
3313 xgrad[k] = xgrad[k] + this_coil->
w[j] * gresult[0];
3314 ygrad[k] = ygrad[k] + this_coil->
w[j] * gresult[1];
3315 zgrad[k] = zgrad[k] + this_coil->
w[j] * gresult[2];
3336 float sum, dist, dist2, dist5;
3339 for (k = 0; k < coils.
ncoil(); k++) {
3340 this_coil = coils.
coils[k].get();
3346 for (j = 0, sum = 0.0; j <
np; j++) {
3347 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3348 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3351 dist2 = dist * dist;
3352 dist5 = dist2 * dist2 * dist;
3353 sum = sum + this_coil->
w[j] * (3 * M.dot(diff) * diff.dot(dir) - dist2 * M.dot(dir)) / dist5;
3373 float dist, dist2, dist5;
3376 for (k = 0; k < coils.
ncoil(); k++) {
3377 this_coil = coils.
coils[k].get();
3380 Eigen::Vector3f sum = Eigen::Vector3f::Zero();
3384 for (j = 0; j <
np; j++) {
3385 Eigen::Map<const Eigen::Vector3f> dir = this_coil->
dir(j);
3386 Eigen::Vector3f diff = this_coil->
pos(j) - rm;
3389 dist2 = dist * dist;
3390 dist5 = dist2 * dist2 * dist;
3391 for (p = 0; p < 3; p++)
3392 sum[p] = sum[p] + this_coil->
w[j] * (3 * diff[p] * diff.dot(dir) - dist2 * dir[p]) / dist5;
3395 for (p = 0; p < 3; p++)
3398 for (p = 0; p < 3; p++)
constexpr auto FIFFV_MNE_COORD_RAS
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFV_COORD_MRI_DISPLAY
constexpr auto FIFFV_MOVE
constexpr auto FIFFV_MNE_COORD_CTF_HEAD
constexpr auto FIFFV_MNE_COORD_CTF_DEVICE
constexpr auto FIFFV_COORD_MRI_SLICE
constexpr auto FIFFV_COORD_DEVICE
constexpr auto FIFFV_MNE_COORD_MRI_VOXEL
constexpr auto FIFFV_MNE_COORD_MNI_TAL
constexpr auto FIFFV_COORD_UNKNOWN
constexpr auto FIFFV_COORD_MRI
constexpr auto FIFFV_NO_MOVE
constexpr auto FIFFV_MNE_COORD_FS_TAL_GTZ
constexpr auto FIFFV_COORD_ISOTRAK
constexpr auto FIFFV_COORD_HPI
constexpr auto FIFFV_MNE_COORD_FS_TAL_LTZ
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
constexpr auto FIFFV_BEM_SURF_ID_HEAD
constexpr auto FIFFV_BEM_SURF_ID_SKULL
constexpr auto FIFFB_BEM_SURF
constexpr auto FIFFV_BEM_APPROX_LINEAR
constexpr auto FIFFV_BEM_APPROX_CONST
constexpr auto FIFFV_BEM_SURF_ID_BRAIN
constexpr auto FIFF_BEM_POT_SOLUTION
constexpr auto FIFF_BEM_COORD_FRAME
constexpr auto FIFF_BEM_APPROX
Matrix paired with row and column name lists, the on-disk form of FIFFB_PROJ_ITEM / FIFFB_MNE_NAMED_M...
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 ...
Multi-shell spherical head model with Berg-Scherg equivalent-source approximation for fast EEG forwar...
const QString mne_coord_frame_name_40(int frame)
Boundary Element Method (BEM) volume-conductor model — layered triangulated surfaces,...
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, void *client)> fwdFieldFunc
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
Per-sensor projection matrix that turns BEM node potentials into MEG coil readings or EEG electrode v...
Triangle descriptor with cached centroid, area and normal vectors.
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Single-hemisphere source space (cortical surface or volume grid) loaded from FIFF.
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 int FWD_BEM_LIN_FIELD_URANKAR
constexpr int FWD_COILC_EEG
constexpr float FWD_BEM_IP_APPROACH_LIMIT
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
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
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.
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.
static constexpr double MAG_FACTOR
int fwd_bem_specify_coils(FwdCoilSet *coils)
Precompute the coil-specific BEM solution for MEG.
int fwd_bem_load_solution(const QString &name, int bemMethod)
Load a pre-computed BEM solution from a FIFF file.
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.
int fwd_bem_load_recompute_solution(const QString &name, int bemMethod, int force_recompute)
Load a BEM solution from file, recomputing if necessary.
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.
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.
int fwd_bem_compute_solution(int bemMethod)
Compute the BEM solution matrix using the specified method.
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.
int fwd_bem_save_model(const QString &name) const
Save the surfaces, conductivities and potential solution (MNE-C fwd_bem_save_model).
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
static std::unique_ptr< MNELIB::MNESourceSpace > 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.
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
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)
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 std::unique_ptr< 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.
This defines a source space.
static std::unique_ptr< 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.