41#include <QtConcurrent>
45#ifndef _USE_MATH_DEFINES
46#define _USE_MATH_DEFINES
52static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
53static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
54static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
64constexpr int FAIL = -1;
66constexpr int LOADED = 1;
67constexpr int NOT_FOUND = 0;
70[[maybe_unused]]
constexpr auto BEM_SUFFIX =
"-bem.fif";
71constexpr auto BEM_SOL_SUFFIX =
"-bem-sol.fif";
72constexpr float EPS = 1e-5f;
73constexpr float CEPS = 1e-5f;
102static QString strip_from(
const QString& s,
const QString& suffix)
106 if (s.endsWith(suffix)) {
108 res.chop(suffix.size());
151 for (k = 0; frames[k].
frame != -1; k++) {
152 if (frame == frames[k].frame)
153 return frames[k].
name;
155 return frames[k].
name;
162using namespace Eigen;
209 s1 = strip_from(name,
".fif");
210 s2 = strip_from(s1,
"-sol");
211 s1 = strip_from(s2,
"-bem");
212 s2 = QString(
"%1%2").arg(s1).arg(BEM_SOL_SUFFIX);
222 for (k = 0; surf_expl[k].kind >= 0; k++)
223 if (surf_expl[k].kind == kind)
224 return surf_expl[k].name;
226 return surf_expl[k].name;
236 for (k = 0; method_expl[k].method >= 0; k++)
237 if (method_expl[k].method == method)
238 return method_expl[k].name;
240 return method_expl[k].name;
251 if (node->find_tag(stream, what, t_pTag)) {
253 qWarning(
"Expected an integer tag : %d (found data type %d instead)", what, t_pTag->getType());
256 *res = *t_pTag->toInt();
266 for (
int k = 0; k < this->
nsurf; k++)
267 if (this->
surfs[k]->
id == kind)
268 return this->
surfs[k].get();
280 std::vector<std::shared_ptr<MNESurface>>
surfs;
281 const int nkind =
static_cast<int>(kinds.size());
282 Eigen::VectorXf sigma_tmp(nkind);
286 qCritical(
"No surfaces specified to fwd_bem_load_surfaces");
290 for (k = 0; k < nkind; k++) {
300 qCritical(
"FsSurface %s not specified in MRI coordinates.",
fwd_bem_explain_surface(kinds[k]).toUtf8().constData());
304 surfs.push_back(std::move(s));
306 auto m = std::make_unique<FwdBemModel>();
310 m->surfs = std::move(
surfs);
311 m->sigma = sigma_tmp;
312 m->ntri.resize(nkind);
314 m->gamma.resize(nkind, nkind);
315 m->source_mult.resize(nkind);
316 m->field_mult.resize(nkind);
320 Eigen::VectorXf sigma1(nkind + 1);
322 sigma1.tail(nkind) = m->sigma;
327 for (j = 0; j < m->nsurf; j++) {
328 m->ntri[j] = m->surfs[j]->ntri;
329 m->np[j] = m->surfs[j]->np;
330 m->source_mult[j] = 2.0f / (sigma1[j + 1] + sigma1[j]);
331 m->field_mult[j] = sigma1[j + 1] - sigma1[j];
332 for (k = 0; k < m->nsurf; k++)
333 m->gamma(j, k) = (sigma1[k + 1] - sigma1[k]) / (sigma1[j + 1] + sigma1[j]);
374 if (!stream->open()) {
383 QList<FiffDirNode::SPtr> nodes = stream->dirtree()->dir_tree_find(
FIFFB_BEM);
385 if (nodes.size() == 0) {
386 qWarning(
"No BEM data in %s", name.toUtf8().constData());
404 qWarning(
"Cannot handle BEM approximation method : %d", method);
409 qWarning(
"Approximation method in file : %d desired : %d", method, bemMethod);
421 QVector<qint32> dims;
422 t_pTag->getMatrixDimensions(ndim, dims);
425 qWarning(
"Expected a two-dimensional solution matrix instead of a %d dimensional one", ndim);
429 for (k = 0, dim = 0; k <
nsurf; k++)
431 if (dims[0] != dim || dims[1] != dim) {
432 qWarning(
"Expected a %d x %d solution matrix instead of a %d x %d one", dim, dim, dims[0], dims[1]);
437 MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose();
438 nSolutions = dims[1];
442 solution = t_pTag->toFloatMatrix().transpose();
443 this->
nsol = nSolutions;
464 qWarning(
"Improper coordinate transform delivered to fwd_bem_set_head_mri_t");
480 qInfo(
"Making a spherical guess space with radius %7.1f mm...", 1000 * guessrad);
482 QFile bemFile(QString(QCoreApplication::applicationDirPath() +
"/../resources/general/surf2bem/icos.fif"));
483 if (!QCoreApplication::startingUp())
484 bemFile.setFileName(QCoreApplication::applicationDirPath() + QString(
"/../resources/general/surf2bem/icos.fif"));
485 else if (!bemFile.exists())
486 bemFile.setFileName(
"../resources/general/surf2bem/icos.fif");
488 if (!bemFile.exists()) {
489 qDebug() << bemFile.fileName() <<
"does not exists.";
493 bemname = bemFile.fileName();
499 for (k = 0; k < sphere_owner->np; k++) {
500 dist = sphere_owner->point(k).norm();
501 sphere_owner->rr.row(k) = (guessrad * sphere_owner->rr.row(k) / dist) + guess_r0.transpose();
503 if (sphere_owner->add_geometry_info(
true) ==
FAIL)
505 guess_surf = sphere_owner.get();
507 qInfo(
"Guess surface (%d = %s) is in %s coordinates",
511 qInfo(
"Filtering (grid = %6.f mm)...", 1000 * grid);
522 Eigen::Vector3d rkk1 = rk1 - rk;
523 double size = rkk1.norm();
525 return log((rk.norm() * size + rk.dot(rkk1)) /
526 (rk1.norm() * size + rk1.dot(rkk1))) /
537 Eigen::Vector3d y1, y2, y3;
540 Eigen::Vector3d vec_omega;
543 double beta[3], bbeta[3];
546 static const double solid_eps = 4.0 *
M_PI / 1.0E6;
550 y1 = (to.
r1 - from).cast<double>();
551 y2 = (to.
r2 - from).cast<double>();
552 y3 = (to.
r3 - from).cast<double>();
556 const Eigen::Vector3d* y_arr[5] = {&y3, &y1, &y2, &y3, &y1};
557 const Eigen::Vector3d** yy = y_arr + 1;
561 Eigen::Vector3d cross = y1.cross(y2);
562 triple = cross.dot(y3);
567 ss = (l1 * l2 * l3 + y1.dot(y2) * l3 + y1.dot(y3) * l2 + y2.dot(y3) * l1);
568 solid = 2.0 * atan2(triple, ss);
569 if (std::fabs(solid) < solid_eps) {
575 for (j = 0; j < 3; j++)
577 bbeta[0] = beta[2] - beta[0];
578 bbeta[1] = beta[0] - beta[1];
579 bbeta[2] = beta[1] - beta[2];
582 for (j = 0; j < 3; j++)
583 vec_omega += bbeta[j] * (*yy[j]);
587 area2 = 2.0 * to.
area;
588 n2 = 1.0 / (area2 * area2);
589 Eigen::Vector3d nn_d = to.
nn.cast<
double>();
590 for (k = 0; k < 3; k++) {
591 Eigen::Vector3d z = yy[k + 1]->cross(*yy[k - 1]);
592 Eigen::Vector3d diff = *yy[k - 1] - *yy[k + 1];
593 omega[k] = n2 * (-area2 * z.dot(nn_d) * solid + triple * diff.dot(vec_omega));
602 double rel1 = (solid + omega[0] + omega[1] + omega[2]) / solid;
606 Eigen::Vector3d check = Eigen::Vector3d::Zero();
607 Eigen::Vector3d nn_check = to.
nn.cast<
double>();
608 for (k = 0; k < 3; k++) {
609 Eigen::Vector3d z = nn_check.cross(*yy[k]);
610 check += omega[k] * z;
612 check *= -area2 / triple;
613 fprintf(stderr,
"(%g,%g,%g) =? (%g,%g,%g)\n",
614 check[0], check[1], check[2],
615 vec_omega[0], vec_omega[1], vec_omega[2]);
617 double rel2 = sqrt(check.dot(check) / vec_omega.dot(vec_omega));
618 fprintf(stderr,
"err1 = %g, err2 = %g\n", 100 * rel1, 100 * rel2);
635 float pi2 =
static_cast<float>(2.0 *
M_PI);
639 for (j = 0; j < nnode; j++) {
641 for (k = 0; k < nnode; k++)
642 sum = sum + mat(j, k);
643 fprintf(stderr,
"row %d sum = %g missing = %g\n", j + 1, sum / pi2,
645 mat(j, j) = pi2 - sum;
648 for (j = 0; j < nnode; j++) {
653 for (k = 0; k < nnode; k++)
654 sum = sum + mat(j, k);
660 mat(j, j) = miss / 2.0;
664 miss = miss / (4.0 * nmemb);
665 for (k = 0, tri = surf.
tris.data(); k <
ntri; k++, tri++) {
666 if (tri->
vert[0] == j) {
667 mat(j, tri->
vert[1]) = mat(j, tri->
vert[1]) + miss;
668 mat(j, tri->
vert[2]) = mat(j, tri->
vert[2]) + miss;
669 }
else if (tri->
vert[1] == j) {
670 mat(j, tri->
vert[0]) = mat(j, tri->
vert[0]) + miss;
671 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 < static_cast<int>(
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 < static_cast<int>(
surfs.size()); p++, joff = joff + np1) {
715 for (q = 0, koff = 0; q < static_cast<int>(
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)
772 rawSurfs.push_back(s.get());
774 qInfo(
"\nComputing the linear collocation solution...");
775 qInfo(
"\tMatrix coefficients...");
777 if (coeff.size() == 0) {
785 qInfo(
"\tInverting the coefficient matrix...");
797 Eigen::MatrixXf ip_solution;
799 qInfo(
"IP approach required...");
801 qInfo(
"\tMatrix coefficients (homog)...");
802 std::vector<MNESurface*> last_surfs = {
surfs.back().get()};
804 if (coeff.size() == 0) {
809 qInfo(
"\tInverting the coefficient matrix (homog)...");
811 if (ip_solution.size() == 0) {
816 qInfo(
"\tModify the original solution to incorporate IP approach...");
821 qInfo(
"Solution ready.");
837 float pi2 =
static_cast<float>(1.0 / (2 *
M_PI));
839 int joff, koff, jup, kup, ntot;
841 for (j = 0, ntot = 0; j <
nsurf; j++)
847 for (p = 0, joff = 0; p <
nsurf; p++) {
848 jup =
ntri[p] + joff;
849 for (q = 0, koff = 0; q <
nsurf; q++) {
850 kup =
ntri[q] + koff;
851 mult = (
gamma ==
nullptr) ? pi2 : pi2 * (*gamma)(p, q);
852 for (j = joff; j < jup; j++)
853 for (k = koff; k < kup; k++)
854 solids(j, k) = defl - solids(j, k) * mult;
859 for (k = 0; k < ntot; k++)
860 solids(k, k) = solids(k, k) + 1.0;
862 Eigen::MatrixXf result = solids.inverse();
887 int j, k, joff, koff, nlast;
890 for (s = 0, koff = 0; s <
nsurf - 1; s++)
891 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++) {
902 qInfo(
"%d3 ", s + 1);
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 < static_cast<int>(
surfs.size()); p++)
988 Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot);
989 for (p = 0, joff = 0; p < static_cast<int>(
surfs.size()); p++, joff = joff + ntri1) {
992 for (q = 0, koff = 0; q < static_cast<int>(
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)
1037 rawSurfs.push_back(s.get());
1039 qInfo(
"\nComputing the constant collocation solution...");
1040 qInfo(
"\tSolid angles...");
1042 if (solids.size() == 0) {
1050 qInfo(
"\tInverting the coefficient matrix...");
1061 Eigen::MatrixXf ip_solution;
1063 qInfo(
"IP approach required...");
1065 qInfo(
"\tSolid angles (homog)...");
1066 std::vector<MNESurface*> last_surfs = {
surfs.back().get()};
1068 if (solids.size() == 0) {
1073 qInfo(
"\tInverting the coefficient matrix (homog)...");
1075 if (ip_solution.size() == 0) {
1080 qInfo(
"\tModify the original solution to incorporate IP approach...");
1084 qInfo(
"Solution ready.");
1105 qWarning(
"Unknown BEM method: %d", bemMethod);
1118 if (!force_recompute) {
1121 if (solres == LOADED) {
1124 }
else if (solres ==
FAIL)
1128 qWarning(
"Desired BEM solution not available in %s (%s)", name, err_get_error());
1141 qWarning(
"[FwdBemModel::fwd_bem_save_model] No model to save");
1145 qWarning(
"[FwdBemModel::fwd_bem_save_model] Unknown BEM method : %d",
bem_method);
1154 int coordFrame =
surfs[0]->coord_frame;
1156 for (
int k = 0; k <
nsurf; ++k) {
1159 surfs[k]->writeToStream(stream.data());
1180 Eigen::Vector3f diff = rp - rd;
1181 float diff2 = diff.squaredNorm();
1182 Eigen::Vector3f cr = Q.cross(diff);
1184 return cr.dot(dir) / (diff2 * std::sqrt(diff2));
1194 Eigen::Vector3f diff = rp - rd;
1195 float diff2 = diff.squaredNorm();
1196 return Q.dot(diff) / (4.0 *
M_PI * diff2 * std::sqrt(diff2));
1209 float r[3], w[3], dist;
1216 qWarning(
"Solution not computed in fwd_bem_specify_els");
1219 if (!els || els->
ncoil() == 0)
1225 els->
user_data = std::make_unique<FwdBemSolution>();
1234 for (k = 0; k < els->
ncoil(); k++) {
1235 el = els->
coils[k].get();
1236 scalp =
surfs[0].get();
1240 for (p = 0; p < el->
np; p++) {
1241 r[0] = el->
rmag(p, 0);
1242 r[1] = el->
rmag(p, 1);
1243 r[2] = el->
rmag(p, 2);
1246 best = scalp->
project_to_surface(
nullptr, Eigen::Map<const Eigen::Vector3f>(r), dist);
1248 qWarning(
"One of the electrodes could not be projected onto the scalp surface. How come?");
1256 for (q = 0; q <
nsol; q++)
1262 tri = &scalp->
tris[best];
1263 scalp->
triangle_coords(Eigen::Map<const Eigen::Vector3f>(r), best, x, y, z);
1265 w[0] = el->
w[p] * (1.0 - x - y);
1266 w[1] = el->
w[p] * x;
1267 w[2] = el->
w[p] * y;
1268 for (v = 0; v < 3; v++) {
1269 for (q = 0; q <
nsol; q++)
1273 qWarning(
"Unknown BEM approximation method : %d",
bem_method);
1291 int s, k, p, nSolutions, pp;
1294 Eigen::Vector3f mri_rd = rd;
1295 Eigen::Vector3f mri_Q = Q;
1297 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1301 float* v0p =
v0.data();
1307 for (pp =
X; pp <=
Z; pp++) {
1308 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1310 ee = Eigen::Vector3f::Unit(pp);
1314 for (s = 0, p = 0; s <
nsurf; s++) {
1315 nTriangles =
surfs[s]->ntri;
1316 tri =
surfs[s]->tris.data();
1318 for (k = 0; k < nTriangles; k++, tri++)
1323 nSolutions = sol->
ncoil;
1324 for (k = 0; k < nSolutions; k++)
1327 nSolutions = all_surfs ? this->
nsol :
surfs[0]->ntri;
1328 for (k = 0; k < nSolutions; k++)
1344 int s, k, p, nSolutions;
1346 Eigen::Vector3f mri_rd = rd;
1347 Eigen::Vector3f mri_Q = Q;
1351 float* v0p =
v0.data();
1357 for (s = 0, p = 0; s <
nsurf; s++) {
1358 nPoints =
surfs[s]->np;
1360 for (k = 0; k < nPoints; k++)
1365 nSolutions = sol->
ncoil;
1366 for (k = 0; k < nSolutions; k++)
1369 nSolutions = all_surfs ? this->
nsol :
surfs[0]->np;
1370 for (k = 0; k < nSolutions; k++)
1385 int s, k, p, nSolutions, pp;
1387 Eigen::Vector3f mri_rd = rd;
1388 Eigen::Vector3f mri_Q = Q;
1391 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
1395 float* v0p =
v0.data();
1401 for (pp =
X; pp <=
Z; pp++) {
1402 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
1404 ee = Eigen::Vector3f::Unit(pp);
1408 for (s = 0, p = 0; s <
nsurf; s++) {
1409 nPoints =
surfs[s]->np;
1411 for (k = 0; k < nPoints; k++)
1416 nSolutions = sol->
ncoil;
1417 for (k = 0; k < nSolutions; k++)
1420 nSolutions = all_surfs ? this->
nsol :
surfs[0]->np;
1421 for (k = 0; k < nSolutions; k++)
1437 int s, k, p, nSolutions;
1439 Eigen::Vector3f mri_rd = rd;
1440 Eigen::Vector3f mri_Q = Q;
1444 float* v0p =
v0.data();
1450 for (s = 0, p = 0; s <
nsurf; s++) {
1451 nTriangles =
surfs[s]->ntri;
1452 tri =
surfs[s]->tris.data();
1454 for (k = 0; k < nTriangles; k++, tri++)
1459 nSolutions = sol->
ncoil;
1460 for (k = 0; k < nSolutions; k++)
1463 nSolutions = all_surfs ? this->
nsol :
surfs[0]->ntri;
1464 for (k = 0; k < nSolutions; k++)
1481 qWarning(
"No BEM model specified to fwd_bem_pot_els");
1484 if (m->solution.size() == 0) {
1485 qWarning(
"No solution available for fwd_bem_pot_els");
1489 qWarning(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1493 m->fwd_bem_pot_calc(rd, Q, &els,
false, pot);
1495 m->fwd_bem_lin_pot_calc(rd, Q, &els,
false, pot);
1497 qWarning(
"Unknown BEM method : %d", m->bem_method);
1505int 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)
1514 qCritical(
"No BEM model specified to fwd_bem_pot_els");
1517 if (m->solution.size() == 0) {
1518 qCritical(
"No solution available for fwd_bem_pot_els");
1522 qCritical(
"No appropriate electrode-specific data available in fwd_bem_pot_coils");
1526 m->fwd_bem_pot_calc(rd, Q, &els,
false, pot);
1527 m->fwd_bem_pot_grad_calc(rd, Q, &els,
false, xgrad, ygrad, zgrad);
1529 m->fwd_bem_lin_pot_calc(rd, Q, &els,
false, pot);
1530 m->fwd_bem_lin_pot_grad_calc(rd, Q, &els,
false, xgrad, ygrad, zgrad);
1532 qCritical(
"Unknown BEM method : %d", m->bem_method);
1542 return std::asinh(x);
1545void FwdBemModel::calc_f(
const Eigen::Vector3d& xx,
const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy)
1547 double det = -xx[1] * yy[0] + xx[2] * yy[0] +
1548 xx[0] * yy[1] - xx[2] * yy[1] - xx[0] * yy[2] + xx[1] * yy[2];
1550 f0[0] = -xx[2] * yy[1] + xx[1] * yy[2];
1551 f0[1] = xx[2] * yy[0] - xx[0] * yy[2];
1552 f0[2] = -xx[1] * yy[0] + xx[0] * yy[1];
1554 fx[0] = yy[1] - yy[2];
1555 fx[1] = -yy[0] + yy[2];
1556 fx[2] = yy[0] - yy[1];
1558 fy[0] = -xx[1] + xx[2];
1559 fy[1] = xx[0] - xx[2];
1560 fy[2] = -xx[0] + xx[1];
1571 double B2 = 1.0 + B * B;
1572 double ABu = A + B * u;
1573 D = sqrt(u * u + z * z + ABu * ABu);
1574 beta[0] = ABu / sqrt(u * u + z * z);
1575 beta[1] = (A * B + B2 * u) / sqrt(A * A + B2 * z * z);
1576 beta[2] = (B * z * z - A * u) / (z * D);
1581void 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)
1583 double xx[4], yy[4];
1585 Eigen::Vector3d beta;
1586 double I1, Tx, Ty, Txx, Tyy, Sxx, mult;
1587 double S1x, S1y, S2x;
1596 Eigen::Vector3d y1 = (to.
r1 - from).cast<double>();
1597 Eigen::Vector3d y2 = (to.
r2 - from).cast<double>();
1598 Eigen::Vector3d y3 = (to.
r3 - from).cast<double>();
1602 Eigen::Vector3d ex_d = to.
ex.cast<
double>();
1603 Eigen::Vector3d ey_d = to.
ey.cast<
double>();
1604 xx[0] = y1.dot(ex_d);
1605 xx[1] = y2.dot(ex_d);
1606 xx[2] = y3.dot(ex_d);
1609 yy[0] = y1.dot(ey_d);
1610 yy[1] = y2.dot(ey_d);
1611 yy[2] = y3.dot(ey_d);
1614 calc_f(Eigen::Map<const Eigen::Vector3d>(xx), Eigen::Map<const Eigen::Vector3d>(yy), f0, fx, fy);
1618 z = y1.dot(to.
nn.cast<
double>());
1632 for (k = 0; k < 2; k++) {
1633 dx = xx[k + 1] - xx[k];
1634 A = (yy[k] * xx[k + 1] - yy[k + 1] * xx[k]) / dx;
1635 B = (yy[k + 1] - yy[k]) / dx;
1641 I1 = I1 - xx[k + 1] *
arsinh(beta[0]) - (A / sqrt(1.0 + B * B)) *
arsinh(beta[1]) - z * atan(beta[2]);
1642 Txx =
arsinh(beta[1]) / sqrt(B2);
1645 Sxx = (D1 - A * B * Txx) / B2;
1647 S1y = S1y + B * Sxx;
1648 Sxx = (B * D1 + A * Txx) / B2;
1654 I1 = I1 + xx[k] *
arsinh(beta[0]) + (A / sqrt(1.0 + B * B)) *
arsinh(beta[1]) + z * atan(beta[2]);
1655 Txx =
arsinh(beta[1]) / sqrt(B2);
1658 Sxx = (D1 - A * B * Txx) / B2;
1660 S1y = S1y - B * Sxx;
1661 Sxx = (B * D1 + A * Txx) / B2;
1667 mult = 1.0 / sqrt(xx[k] * xx[k] + z * z);
1671 Tyy =
arsinh(mult * yy[k + 1]);
1673 S1y = S1y + xx[k] * Tyy;
1677 Tyy =
arsinh(mult * yy[k]);
1679 S1y = S1y - xx[k] * Tyy;
1700 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1701 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1702 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1704 const Eigen::Vector3d* yy[4] = {&y1, &y2, &y3, &y1};
1705 for (j = 0; j < 3; j++)
1706 beta[j] =
calc_beta(*yy[j], *yy[j + 1]);
1707 bbeta[0] = beta[2] - beta[0];
1708 bbeta[1] = beta[0] - beta[1];
1709 bbeta[2] = beta[1] - beta[2];
1711 Eigen::Vector3d coeff = Eigen::Vector3d::Zero();
1712 for (j = 0; j < 3; j++)
1713 coeff += bbeta[j] * (*yy[j]);
1714 return coeff.dot(normal.cast<
double>());
1729 int j, k, p, s, off;
1734 qWarning(
"Solution matrix missing in fwd_bem_field_coeff");
1735 return Eigen::MatrixXf();
1738 qWarning(
"BEM method should be constant collocation for fwd_bem_field_coeff");
1739 return Eigen::MatrixXf();
1744 qWarning(
"head -> mri coordinate transform missing in fwd_bem_field_coeff");
1745 return Eigen::MatrixXf();
1751 return Eigen::MatrixXf();
1752 coils = tcoils.get();
1755 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff", coils->
coord_frame);
1756 return Eigen::MatrixXf();
1760 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(), nTriangles);
1762 for (s = 0, off = 0; s <
nsurf; s++) {
1763 surf =
surfs[s].get();
1764 nTriangles = surf->
ntri;
1765 tri = surf->
tris.data();
1768 for (k = 0; k < nTriangles; k++, tri++) {
1769 for (j = 0; j < coils->
ncoil(); j++) {
1770 coil = coils->
coils[j].get();
1772 for (p = 0; p < coil->
np; p++)
1774 coeff(j, k + off) = mult * res;
1777 off = off + nTriangles;
1786 Eigen::Vector3d rkk1 = rk1 - rk;
1787 double size = rkk1.norm();
1789 return log((rk1.norm() * size + rk1.dot(rkk1)) /
1790 (rk.norm() * size + rk.dot(rkk1))) /
1798 double triple, l1, l2, l3, solid, clen;
1799 double common, sum, beta,
gamma;
1802 Eigen::Vector3d rjk[3];
1803 rjk[0] = (tri.
r3 - tri.
r2).cast<double>();
1804 rjk[1] = (tri.
r1 - tri.
r3).cast<double>();
1805 rjk[2] = (tri.
r2 - tri.
r1).cast<double>();
1807 Eigen::Vector3d y1 = (tri.
r1 - dest).cast<double>();
1808 Eigen::Vector3d y2 = (tri.
r2 - dest).cast<double>();
1809 Eigen::Vector3d y3 = (tri.
r3 - dest).cast<double>();
1811 const Eigen::Vector3d* yy[4] = {&y1, &y2, &y3, &y1};
1813 Eigen::Vector3d nn_d = tri.
nn.cast<
double>();
1814 clen = y1.dot(nn_d);
1815 Eigen::Vector3d c_vec = clen * nn_d;
1816 Eigen::Vector3d A_vec = dest.cast<
double>() + c_vec;
1818 Eigen::Vector3d c1 = tri.
r1.cast<
double>() - A_vec;
1819 Eigen::Vector3d c2 = tri.
r2.cast<
double>() - A_vec;
1820 Eigen::Vector3d c3 = tri.
r3.cast<
double>() - A_vec;
1822 const Eigen::Vector3d* cc[4] = {&c1, &c2, &c3, &c1};
1826 for (sum = 0.0, k = 0; k < 3; k++) {
1827 Eigen::Vector3d cross = cc[k]->cross(*cc[k + 1]);
1828 beta = cross.dot(nn_d);
1830 sum = sum + beta *
gamma;
1835 Eigen::Vector3d cross = y1.cross(y2);
1836 triple = cross.dot(y3);
1841 solid = 2.0 * atan2(triple, (l1 * l2 * l3 + y1.dot(y2) * l3 + y1.dot(y3) * l2 + y2.dot(y3) * l1));
1845 Eigen::Vector3d dir_d = dir.cast<
double>();
1846 common = (sum - clen * solid) / (2.0 * tri.
area);
1847 for (k = 0; k < 3; k++)
1848 res[k] = -rjk[k].dot(dir_d) * common;
1857 Eigen::Vector2d T, S1, S2;
1858 Eigen::Vector3d f0, fx, fy;
1859 double res_x, res_y;
1860 double x_fac, y_fac;
1869 Eigen::Vector3f dir = dir_in.normalized();
1871 x_fac = -dir.dot(tri.
ex);
1872 y_fac = -dir.dot(tri.
ey);
1873 for (k = 0; k < 3; k++) {
1874 res_x = f0[k] * T[0] + fx[k] * S1[0] + fy[k] * S2[0] + fy[k] * I1;
1875 res_y = f0[k] * T[1] + fx[k] * S1[1] + fy[k] * S2[1] - fx[k] * I1;
1876 res[k] = x_fac * res_x + y_fac * res_y;
1885 const Eigen::Vector3f* rr[3] = {&source.
r1, &source.
r2, &source.
r3};
1887 for (k = 0; k < 3; k++) {
1888 Eigen::Vector3f diff = dest - *rr[k];
1889 float dl = diff.squaredNorm();
1890 Eigen::Vector3f vec_result = diff.cross(source.
nn);
1891 res[k] = source.
area * vec_result.dot(normal) / (3.0 * dl * sqrt(dl));
1909 int j, k, p, pp, off, s;
1910 Eigen::Vector3d res, one;
1915 qWarning(
"Solution matrix missing in fwd_bem_lin_field_coeff");
1916 return Eigen::MatrixXf();
1919 qWarning(
"BEM method should be linear collocation for fwd_bem_lin_field_coeff");
1920 return Eigen::MatrixXf();
1925 qWarning(
"head -> mri coordinate transform missing in fwd_bem_lin_field_coeff");
1926 return Eigen::MatrixXf();
1932 return Eigen::MatrixXf();
1933 coils = tcoils.get();
1936 qWarning(
"Incompatible coil coordinate frame %d for fwd_bem_field_coeff", coils->
coord_frame);
1937 return Eigen::MatrixXf();
1947 Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->
ncoil(),
nsol);
1951 for (s = 0, off = 0; s <
nsurf; s++) {
1952 surf =
surfs[s].get();
1953 nTriangles = surf->
ntri;
1954 tri = surf->
tris.data();
1957 for (k = 0; k < nTriangles; k++, tri++) {
1958 for (j = 0; j < coils->
ncoil(); j++) {
1959 coil = coils->
coils[j].get();
1964 for (p = 0; p < coil->
np; p++) {
1965 func(coil->
pos(p), coil->
dir(p), *tri, one);
1966 res += coil->
w[p] * one;
1972 for (pp = 0; pp < 3; pp++)
1973 coeff(j, tri->
vert[pp] + off) = coeff(j, tri->
vert[pp] + off) + mult * res[pp];
1976 off = off + surf->
np;
1991 Eigen::MatrixXf sol;
1995 qWarning(
"Solution not computed in fwd_bem_specify_coils");
2000 if (!coils || coils->
ncoil() == 0)
2007 qWarning(
"Unknown BEM method in fwd_bem_specify_coils : %d",
bem_method);
2010 if (sol.size() == 0)
2012 coils->
user_data = std::make_unique<FwdBemSolution>();
2029 int s, k, p, nPoints;
2032 Eigen::Vector3f my_rd = rd;
2033 Eigen::Vector3f my_Q = Q;
2040 float* v0p =
v0.data();
2051 for (s = 0, p = 0; s <
nsurf; s++) {
2052 nPoints =
surfs[s]->np;
2054 for (k = 0; k < nPoints; k++)
2061 for (k = 0; k < coils.
ncoil(); k++) {
2062 coil = coils.
coils[k].get();
2064 for (p = 0; p < coil->
np; p++)
2070 for (k = 0; k < coils.
ncoil(); k++)
2075 for (k = 0; k < coils.
ncoil(); k++)
2087 int s, k, p, nTriangles;
2091 Eigen::Vector3f my_rd = rd;
2092 Eigen::Vector3f my_Q = Q;
2099 float* v0p =
v0.data();
2110 for (s = 0, p = 0; s <
nsurf; s++) {
2111 nTriangles =
surfs[s]->ntri;
2112 tri =
surfs[s]->tris.data();
2114 for (k = 0; k < nTriangles; k++, tri++)
2121 for (k = 0; k < coils.
ncoil(); k++) {
2122 coil = coils.
coils[k].get();
2124 for (p = 0; p < coil->
np; p++)
2130 for (k = 0; k < coils.
ncoil(); k++)
2135 for (k = 0; k < coils.
ncoil(); k++)
2148 int s, k, p, nTriangles, pp;
2152 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2153 Eigen::Vector3f ee, mri_ee;
2154 Eigen::Vector3f mri_rd = rd;
2155 Eigen::Vector3f mri_Q = Q;
2161 float* v0p =
v0.data();
2169 for (pp =
X; pp <=
Z; pp++) {
2170 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2174 ee = Eigen::Vector3f::Unit(pp);
2181 for (s = 0, p = 0; s <
nsurf; s++) {
2182 nTriangles =
surfs[s]->ntri;
2183 tri =
surfs[s]->tris.data();
2185 for (k = 0; k < nTriangles; k++, tri++) {
2193 for (k = 0; k < coils.
ncoil(); k++) {
2194 coil = coils.
coils[k].get();
2196 for (p = 0; p < coil->
np; p++)
2202 for (k = 0; k < coils.
ncoil(); k++)
2203 grad[k] = grad[k] + sol->
solution.row(k).dot(
v0);
2207 for (k = 0; k < coils.
ncoil(); k++)
2221 Eigen::Vector3f diff = rp - rd;
2222 float diff2 = diff.squaredNorm();
2223 float diff3 = std::sqrt(diff2) * diff2;
2224 float diff5 = diff3 * diff2;
2225 Eigen::Vector3f cr = Q.cross(diff);
2226 Eigen::Vector3f crn = dir.cross(Q);
2228 return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3;
2239 Eigen::Vector3f diff = rp - rd;
2240 float diff2 = diff.squaredNorm();
2241 float diff3 = std::sqrt(diff2) * diff2;
2242 float diff5 = diff3 * diff2;
2244 float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3;
2245 return res / (4.0 *
M_PI);
2257 int s, k, p, nPoints, pp;
2260 Eigen::Vector3f ee, mri_ee;
2261 Eigen::Vector3f mri_rd = rd;
2262 Eigen::Vector3f mri_Q = Q;
2263 Eigen::Ref<Eigen::VectorXf>* grads[] = {&xgrad, &ygrad, &zgrad};
2269 float* v0p =
v0.data();
2277 for (pp =
X; pp <=
Z; pp++) {
2278 Eigen::Ref<Eigen::VectorXf>& grad = *grads[pp];
2282 ee = Eigen::Vector3f::Unit(pp);
2289 for (s = 0, p = 0; s <
nsurf; s++) {
2290 nPoints =
surfs[s]->np;
2293 for (k = 0; k < nPoints; k++)
2300 for (k = 0; k < coils.
ncoil(); k++) {
2301 coil = coils.
coils[k].get();
2303 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);
2343 m->fwd_bem_lin_field_calc(rd, Q, coils, B);
2345 qWarning(
"Unknown BEM method : %d", m->bem_method);
2354 const Eigen::Vector3f& Q,
2356 Eigen::Ref<Eigen::VectorXf> Bval,
2357 Eigen::Ref<Eigen::VectorXf> xgrad,
2358 Eigen::Ref<Eigen::VectorXf> ygrad,
2359 Eigen::Ref<Eigen::VectorXf> zgrad,
2366 qCritical(
"No BEM model specified to fwd_bem_field");
2371 qCritical(
"No appropriate coil-specific data available in fwd_bem_field");
2376 m->fwd_bem_field_calc(rd, Q, coils, Bval);
2378 m->fwd_bem_field_grad_calc(rd, Q, coils, xgrad, ygrad, zgrad);
2380 m->fwd_bem_lin_field_calc(rd, Q, coils, Bval);
2382 m->fwd_bem_lin_field_grad_calc(rd, Q, coils, xgrad, ygrad, zgrad);
2384 qCritical(
"Unknown BEM method : %d", m->bem_method);
2403 Eigen::MatrixXf tmp_vec_res(3, ncoil);
2413 for (j = 0; j < s->
np; j++) {
2431 for (j = 0; j < s->
np; j++)
2446 for (j = 0; j < s->
np; j++) {
2473 }
else if (a->
comp == 0) {
2486 }
else if (a->
comp == 1) {
2499 }
else if (a->
comp == 2) {
2516 for (j = 0; j < s->
np; j++) {
2523 a->
res->col(p++) = tmp_vec_res.row(0).transpose();
2524 a->
res->col(p++) = tmp_vec_res.row(1).transpose();
2525 a->
res->col(p++) = tmp_vec_res.row(2).transpose();
2540 }
else if (a->
comp == 0) {
2547 }
else if (a->
comp == 1) {
2554 }
else if (a->
comp == 2) {
2589 const bool sphere =
nsurf == 0;
2590 Eigen::Vector3f sphere_r0 = r0;
2592 Eigen::MatrixXf res_mat;
2593 Eigen::MatrixXf res_grad_mat;
2595 MatrixXd matResGrad;
2602 int nmeg = coils->
ncoil();
2604 int nspace =
static_cast<int>(spaces.size());
2609 int nproc = QThread::idealThreadCount();
2610 QStringList emptyList;
2612 auto cleanup_fail = [&]() {
2631 return cleanup_fail();
2635 qInfo(
"Using differences.");
2652 return cleanup_fail();
2656 qInfo(
"Composing the field computation matrix...");
2658 return cleanup_fail();
2662 qInfo(
"Composing the field computation matrix (compensation coils)...");
2664 return cleanup_fail();
2667 vec_field =
nullptr;
2675 for (k = 0, nsource = 0; k < nspace; k++)
2676 nsource += spaces[k]->nuse;
2681 int ncols = fixed_ori ? nsource : 3 * nsource;
2682 res_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2685 int ncols = fixed_ori ? 3 * nsource : 3 * 3 * nsource;
2686 res_grad_mat = Eigen::MatrixXf::Zero(nmeg, ncols);
2691 one_arg = std::make_unique<FwdThreadArg>();
2692 one_arg->res = &res_mat;
2693 one_arg->res_grad = bDoGrad ? &res_grad_mat :
nullptr;
2695 one_arg->coils_els = coils;
2696 one_arg->client = client;
2697 one_arg->s =
nullptr;
2698 one_arg->fixed_ori = fixed_ori;
2699 one_arg->field_pot = field;
2700 one_arg->vec_field_pot = vec_field;
2701 one_arg->field_pot_grad = field_grad;
2704 use_threads =
false;
2707 int nthread = (fixed_ori || vec_field) ? nspace : 3 * nspace;
2708 std::vector<FwdThreadArg::UPtr> args;
2713 if (fixed_ori || vec_field) {
2714 for (k = 0, off = 0; k < nthread; k++) {
2716 t_arg->s = spaces[k].get();
2718 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2719 args.push_back(std::move(t_arg));
2721 qInfo(
"%d processors. I will use one thread for each of the %d source spaces.",
2724 for (k = 0, off = 0, q = 0; k < nspace; k++) {
2725 for (p = 0; p < 3; p++, q++) {
2727 t_arg->s = spaces[k].get();
2730 args.push_back(std::move(t_arg));
2732 off = fixed_ori ? off + spaces[k]->nuse : off + 3 * spaces[k]->nuse;
2734 qInfo(
"%d processors. I will use %d threads : %d source spaces x 3 source components.",
2735 nproc, nthread, nspace);
2737 qInfo(
"Computing MEG at %d source locations (%s orientations)...",
2738 nsource, fixed_ori ?
"fixed" :
"free");
2748 for (k = 0, stat =
OK; k < nthread; k++)
2749 if (args[k]->stat !=
OK) {
2754 return cleanup_fail();
2756 qInfo(
"Computing MEG at %d source locations (%s orientations, no threads)...",
2757 nsource, fixed_ori ?
"fixed" :
"free");
2758 for (k = 0, off = 0; k < nspace; k++) {
2759 one_arg->s = spaces[k].get();
2762 if (one_arg->stat !=
OK)
2763 return cleanup_fail();
2764 off = fixed_ori ? off + one_arg->s->nuse : off + 3 * one_arg->s->nuse;
2769 QStringList orig_names;
2770 for (k = 0; k < nmeg; k++)
2771 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++)
#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
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
#define FIFFV_BEM_APPROX_LINEAR
#define FIFFV_BEM_SURF_ID_SKULL
#define FIFF_BEM_COORD_FRAME
#define FIFFV_BEM_APPROX_CONST
#define FIFFV_BEM_SURF_ID_HEAD
#define FIFFV_BEM_SURF_ID_BRAIN
#define FIFF_BEM_POT_SOLUTION
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).
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.